// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROME_FRAME_CHROME_FRAME_PLUGIN_H_ #define CHROME_FRAME_CHROME_FRAME_PLUGIN_H_ #include <string> #include <vector> #include "base/memory/ref_counted.h" #include "base/win/win_util.h" #include "chrome_frame/chrome_frame_automation.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths_internal.h" #include "chrome_frame/simple_resource_loader.h" #include "chrome_frame/navigation_constraints.h" #include "chrome_frame/utils.h" #include "grit/chromium_strings.h" #define IDC_ABOUT_CHROME_FRAME 40018 // Helper so that this file doesn't include the messages header. void ChromeFramePluginGetParamsCoordinates( const MiniContextMenuParams& params, int* x, int* y); // A class to implement common functionality for all types of // plugins: ActiveX and ActiveDoc template <typename T> class ChromeFramePlugin : public ChromeFrameDelegateImpl, public NavigationConstraintsImpl { public: ChromeFramePlugin() : ignore_setfocus_(false){ } ~ChromeFramePlugin() { Uninitialize(); } BEGIN_MSG_MAP(T) MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_PARENTNOTIFY, OnParentNotify) END_MSG_MAP() bool Initialize() { DVLOG(1) << __FUNCTION__; DCHECK(!automation_client_.get()); automation_client_ = CreateAutomationClient(); if (!automation_client_.get()) { NOTREACHED() << "new ChromeFrameAutomationClient"; return false; } return true; } void Uninitialize() { DVLOG(1) << __FUNCTION__; if (IsValid()) { automation_client_->Uninitialize(); automation_client_ = NULL; } } bool InitializeAutomation(const std::wstring& profile_name, bool incognito, bool is_widget_mode, const GURL& url, const GURL& referrer, bool route_all_top_level_navigations) { DCHECK(IsValid()); DCHECK(launch_params_ == NULL); // We don't want to do incognito when privileged, since we're // running in browser chrome or some other privileged context. bool incognito_mode = !is_privileged() && incognito; base::FilePath profile_path; GetProfilePath(profile_name, &profile_path); // The profile name could change based on the browser version. For e.g. for // IE6/7 the profile is created in a different folder whose last component // is Google Chrome Frame. base::FilePath actual_profile_name = profile_path.BaseName(); launch_params_ = new ChromeFrameLaunchParams(url, referrer, profile_path, actual_profile_name.value(), SimpleResourceLoader::GetLanguage(), incognito_mode, is_widget_mode, route_all_top_level_navigations); return automation_client_->Initialize(this, launch_params_); } // ChromeFrameDelegate implementation virtual WindowType GetWindow() const { return (static_cast<const T*>(this))->m_hWnd; } virtual void GetBounds(RECT* bounds) { if (bounds) { if (::IsWindow(GetWindow())) { (static_cast<T*>(this))->GetClientRect(bounds); } } } virtual std::string GetDocumentUrl() { return document_url_; } virtual void OnAutomationServerReady() { } virtual bool IsValid() const { return automation_client_.get() != NULL; } virtual void OnHostMoved() { if (IsValid()) automation_client_->OnChromeFrameHostMoved(); } protected: virtual void OnNavigationFailed(int error_code, const GURL& gurl) { OnLoadFailed(error_code, gurl.spec()); } virtual void OnHandleContextMenu(const ContextMenuModel& menu_model, int align_flags, const MiniContextMenuParams& params) { if (!automation_client_.get()) { NOTREACHED(); return; } HMENU menu = BuildContextMenu(menu_model); if (!menu) return; T* self = static_cast<T*>(this); if (self->PreProcessContextMenu(menu)) { // In order for the context menu to handle keyboard input, give the // ActiveX window focus. ignore_setfocus_ = true; SetFocus(GetWindow()); ignore_setfocus_ = false; UINT flags = align_flags | TPM_LEFTBUTTON | TPM_RETURNCMD | TPM_RECURSE; int x, y; ChromeFramePluginGetParamsCoordinates(params, &x, &y); UINT selected = TrackPopupMenuEx(menu, flags, x, y, GetWindow(), NULL); // Menu is over now give focus back to chrome GiveFocusToChrome(false); if (IsValid() && selected != 0 && !self->HandleContextMenuCommand(selected, params)) { automation_client_->SendContextMenuCommandToChromeFrame(selected); } } DestroyMenu(menu); } LRESULT OnSetFocus(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { // NO_LINT if (!ignore_setfocus_ && IsValid()) { // Pass false to |restore_focus_view|, because we do not want Chrome // to focus the first focusable element in the current view, only the // view itself. GiveFocusToChrome(false); } return 0; } LRESULT OnSize(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { // NO_LINT handled = FALSE; // When we get resized, we need to resize the external tab window too. if (IsValid()) automation_client_->Resize(LOWORD(lparam), HIWORD(lparam), SWP_NOACTIVATE | SWP_NOZORDER); return 0; } LRESULT OnParentNotify(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { // NO_LINT switch (LOWORD(wparam)) { case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_XBUTTONDOWN: { // If we got activated via mouse click on the external tab, // we need to update the state of this thread and tell the // browser that we now have the focus. HWND focus = ::GetFocus(); HWND plugin_window = GetWindow(); // The Chrome-Frame instance may have launched a popup which currently // has focus. Because experimental extension popups are top-level // windows, we have to check that the focus has shifted to a window // that does not share the same GA_ROOTOWNER as the plugin. if (focus != plugin_window && ::GetAncestor(plugin_window, GA_ROOTOWNER) != ::GetAncestor(focus, GA_ROOTOWNER)) { ignore_setfocus_ = true; SetFocus(plugin_window); ignore_setfocus_ = false; } break; } } return 0; } // Return true if context menu should be displayed. The menu could be // modified as well (enable/disable commands, add/remove items). // Override in most-derived class if needed. bool PreProcessContextMenu(HMENU menu) { // Add an "About" item. AppendMenu(menu, MF_STRING, IDC_ABOUT_CHROME_FRAME, SimpleResourceLoader::Get(IDS_CHROME_FRAME_MENU_ABOUT).c_str()); return true; } // Return true if menu command is processed, otherwise the command will be // passed to Chrome for execution. Override in most-derived class if needed. bool HandleContextMenuCommand(UINT cmd, const MiniContextMenuParams& params) { return false; } // Allow overriding the type of automation client used, for unit tests. virtual ChromeFrameAutomationClient* CreateAutomationClient() { return new ChromeFrameAutomationClient; } void GiveFocusToChrome(bool restore_focus_to_view) { if (IsValid()) { TabProxy* tab = automation_client_->tab(); HWND chrome_window = automation_client_->tab_window(); if (tab && ::IsWindow(chrome_window)) { DVLOG(1) << "Setting initial focus"; tab->SetInitialFocus(base::win::IsShiftPressed(), restore_focus_to_view); } } } virtual void GetProfilePath(const std::wstring& profile_name, base::FilePath* profile_path) { return GetChromeFrameProfilePath(profile_name, profile_path); } protected: // Our gateway to chrome land scoped_refptr<ChromeFrameAutomationClient> automation_client_; // How we launched Chrome. scoped_refptr<ChromeFrameLaunchParams> launch_params_; // Url of the containing document. std::string document_url_; // We set this flag when we're taking the focus ourselves // and notifying the host browser that we're doing so. // When the flag is not set, we transfer the focus to chrome. bool ignore_setfocus_; }; #endif // CHROME_FRAME_CHROME_FRAME_PLUGIN_H_