1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CHROME_FRAME_CHROME_FRAME_PLUGIN_H_ 6 #define CHROME_FRAME_CHROME_FRAME_PLUGIN_H_ 7 8 #include <string> 9 #include <vector> 10 11 #include "base/memory/ref_counted.h" 12 #include "base/win/win_util.h" 13 #include "chrome_frame/chrome_frame_automation.h" 14 #include "chrome/common/chrome_paths.h" 15 #include "chrome/common/chrome_paths_internal.h" 16 #include "chrome_frame/simple_resource_loader.h" 17 #include "chrome_frame/navigation_constraints.h" 18 #include "chrome_frame/utils.h" 19 #include "grit/chromium_strings.h" 20 21 #define IDC_ABOUT_CHROME_FRAME 40018 22 23 // Helper so that this file doesn't include the messages header. 24 void ChromeFramePluginGetParamsCoordinates( 25 const MiniContextMenuParams& params, 26 int* x, 27 int* y); 28 29 // A class to implement common functionality for all types of 30 // plugins: ActiveX and ActiveDoc 31 template <typename T> 32 class ChromeFramePlugin 33 : public ChromeFrameDelegateImpl, 34 public NavigationConstraintsImpl { 35 public: 36 ChromeFramePlugin() : ignore_setfocus_(false){ 37 } 38 ~ChromeFramePlugin() { 39 Uninitialize(); 40 } 41 42 BEGIN_MSG_MAP(T) 43 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) 44 MESSAGE_HANDLER(WM_SIZE, OnSize) 45 MESSAGE_HANDLER(WM_PARENTNOTIFY, OnParentNotify) 46 END_MSG_MAP() 47 48 bool Initialize() { 49 DVLOG(1) << __FUNCTION__; 50 DCHECK(!automation_client_.get()); 51 automation_client_ = CreateAutomationClient(); 52 if (!automation_client_.get()) { 53 NOTREACHED() << "new ChromeFrameAutomationClient"; 54 return false; 55 } 56 57 return true; 58 } 59 60 void Uninitialize() { 61 DVLOG(1) << __FUNCTION__; 62 if (IsValid()) { 63 automation_client_->Uninitialize(); 64 automation_client_ = NULL; 65 } 66 } 67 68 bool InitializeAutomation(const std::wstring& profile_name, 69 bool incognito, bool is_widget_mode, 70 const GURL& url, const GURL& referrer, 71 bool route_all_top_level_navigations) { 72 DCHECK(IsValid()); 73 DCHECK(launch_params_ == NULL); 74 // We don't want to do incognito when privileged, since we're 75 // running in browser chrome or some other privileged context. 76 bool incognito_mode = !is_privileged() && incognito; 77 base::FilePath profile_path; 78 GetProfilePath(profile_name, &profile_path); 79 // The profile name could change based on the browser version. For e.g. for 80 // IE6/7 the profile is created in a different folder whose last component 81 // is Google Chrome Frame. 82 base::FilePath actual_profile_name = profile_path.BaseName(); 83 launch_params_ = new ChromeFrameLaunchParams(url, referrer, profile_path, 84 actual_profile_name.value(), SimpleResourceLoader::GetLanguage(), 85 incognito_mode, is_widget_mode, route_all_top_level_navigations); 86 return automation_client_->Initialize(this, launch_params_); 87 } 88 89 // ChromeFrameDelegate implementation 90 virtual WindowType GetWindow() const { 91 return (static_cast<const T*>(this))->m_hWnd; 92 } 93 94 virtual void GetBounds(RECT* bounds) { 95 if (bounds) { 96 if (::IsWindow(GetWindow())) { 97 (static_cast<T*>(this))->GetClientRect(bounds); 98 } 99 } 100 } 101 virtual std::string GetDocumentUrl() { 102 return document_url_; 103 } 104 virtual void OnAutomationServerReady() { 105 } 106 107 virtual bool IsValid() const { 108 return automation_client_.get() != NULL; 109 } 110 111 virtual void OnHostMoved() { 112 if (IsValid()) 113 automation_client_->OnChromeFrameHostMoved(); 114 } 115 116 protected: 117 virtual void OnNavigationFailed(int error_code, const GURL& gurl) { 118 OnLoadFailed(error_code, gurl.spec()); 119 } 120 121 virtual void OnHandleContextMenu(const ContextMenuModel& menu_model, 122 int align_flags, 123 const MiniContextMenuParams& params) { 124 if (!automation_client_.get()) { 125 NOTREACHED(); 126 return; 127 } 128 129 HMENU menu = BuildContextMenu(menu_model); 130 if (!menu) 131 return; 132 133 T* self = static_cast<T*>(this); 134 if (self->PreProcessContextMenu(menu)) { 135 // In order for the context menu to handle keyboard input, give the 136 // ActiveX window focus. 137 ignore_setfocus_ = true; 138 SetFocus(GetWindow()); 139 ignore_setfocus_ = false; 140 UINT flags = align_flags | TPM_LEFTBUTTON | TPM_RETURNCMD | TPM_RECURSE; 141 int x, y; 142 ChromeFramePluginGetParamsCoordinates(params, &x, &y); 143 UINT selected = TrackPopupMenuEx(menu, flags, x, y, GetWindow(), NULL); 144 // Menu is over now give focus back to chrome 145 GiveFocusToChrome(false); 146 if (IsValid() && selected != 0 && 147 !self->HandleContextMenuCommand(selected, params)) { 148 automation_client_->SendContextMenuCommandToChromeFrame(selected); 149 } 150 } 151 152 DestroyMenu(menu); 153 } 154 155 LRESULT OnSetFocus(UINT message, WPARAM wparam, LPARAM lparam, 156 BOOL& handled) { // NO_LINT 157 if (!ignore_setfocus_ && IsValid()) { 158 // Pass false to |restore_focus_view|, because we do not want Chrome 159 // to focus the first focusable element in the current view, only the 160 // view itself. 161 GiveFocusToChrome(false); 162 } 163 return 0; 164 } 165 166 LRESULT OnSize(UINT message, WPARAM wparam, LPARAM lparam, 167 BOOL& handled) { // NO_LINT 168 handled = FALSE; 169 // When we get resized, we need to resize the external tab window too. 170 if (IsValid()) 171 automation_client_->Resize(LOWORD(lparam), HIWORD(lparam), 172 SWP_NOACTIVATE | SWP_NOZORDER); 173 return 0; 174 } 175 176 LRESULT OnParentNotify(UINT message, WPARAM wparam, LPARAM lparam, 177 BOOL& handled) { // NO_LINT 178 switch (LOWORD(wparam)) { 179 case WM_LBUTTONDOWN: 180 case WM_MBUTTONDOWN: 181 case WM_RBUTTONDOWN: 182 case WM_XBUTTONDOWN: { 183 // If we got activated via mouse click on the external tab, 184 // we need to update the state of this thread and tell the 185 // browser that we now have the focus. 186 HWND focus = ::GetFocus(); 187 HWND plugin_window = GetWindow(); 188 189 // The Chrome-Frame instance may have launched a popup which currently 190 // has focus. Because experimental extension popups are top-level 191 // windows, we have to check that the focus has shifted to a window 192 // that does not share the same GA_ROOTOWNER as the plugin. 193 if (focus != plugin_window && 194 ::GetAncestor(plugin_window, GA_ROOTOWNER) != 195 ::GetAncestor(focus, GA_ROOTOWNER)) { 196 ignore_setfocus_ = true; 197 SetFocus(plugin_window); 198 ignore_setfocus_ = false; 199 } 200 break; 201 } 202 } 203 204 return 0; 205 } 206 207 // Return true if context menu should be displayed. The menu could be 208 // modified as well (enable/disable commands, add/remove items). 209 // Override in most-derived class if needed. 210 bool PreProcessContextMenu(HMENU menu) { 211 // Add an "About" item. 212 AppendMenu(menu, MF_STRING, IDC_ABOUT_CHROME_FRAME, 213 SimpleResourceLoader::Get(IDS_CHROME_FRAME_MENU_ABOUT).c_str()); 214 return true; 215 } 216 217 // Return true if menu command is processed, otherwise the command will be 218 // passed to Chrome for execution. Override in most-derived class if needed. 219 bool HandleContextMenuCommand(UINT cmd, 220 const MiniContextMenuParams& params) { 221 return false; 222 } 223 224 // Allow overriding the type of automation client used, for unit tests. 225 virtual ChromeFrameAutomationClient* CreateAutomationClient() { 226 return new ChromeFrameAutomationClient; 227 } 228 229 void GiveFocusToChrome(bool restore_focus_to_view) { 230 if (IsValid()) { 231 TabProxy* tab = automation_client_->tab(); 232 HWND chrome_window = automation_client_->tab_window(); 233 if (tab && ::IsWindow(chrome_window)) { 234 DVLOG(1) << "Setting initial focus"; 235 tab->SetInitialFocus(base::win::IsShiftPressed(), restore_focus_to_view); 236 } 237 } 238 } 239 240 virtual void GetProfilePath(const std::wstring& profile_name, 241 base::FilePath* profile_path) { 242 return GetChromeFrameProfilePath(profile_name, profile_path); 243 } 244 245 protected: 246 // Our gateway to chrome land 247 scoped_refptr<ChromeFrameAutomationClient> automation_client_; 248 249 // How we launched Chrome. 250 scoped_refptr<ChromeFrameLaunchParams> launch_params_; 251 252 // Url of the containing document. 253 std::string document_url_; 254 255 // We set this flag when we're taking the focus ourselves 256 // and notifying the host browser that we're doing so. 257 // When the flag is not set, we transfer the focus to chrome. 258 bool ignore_setfocus_; 259 }; 260 261 #endif // CHROME_FRAME_CHROME_FRAME_PLUGIN_H_ 262