Home | History | Annotate | Download | only in chrome_frame
      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