Home | History | Annotate | Download | only in chrome_frame
      1 // Copyright (c) 2011 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 #include "chrome_frame/bho_loader.h"
      6 
      7 #include <atlbase.h>
      8 #include <atlcomcli.h>
      9 #include <exdisp.h>
     10 
     11 #include "chrome_frame/chrome_frame_helper_util.h"
     12 #include "chrome_frame/chrome_tab.h"
     13 #include "chrome_frame/event_hooker.h"
     14 
     15 
     16 // Describes the window class we look for.
     17 const wchar_t kStatusBarWindowClass[] = L"msctls_statusbar32";
     18 
     19 // On IE9, the status bar is disabled by default, so we look for an
     20 // AsyncBoundaryLayer window instead.
     21 const wchar_t kAsyncBoundaryDnWindow[] = L"asynclayerboundarydn\0";
     22 
     23 BHOLoader::BHOLoader() : hooker_(new EventHooker()) {
     24 }
     25 
     26 BHOLoader::~BHOLoader() {
     27   if (hooker_) {
     28     delete hooker_;
     29     hooker_ = NULL;
     30   }
     31 }
     32 
     33 void BHOLoader::OnHookEvent(DWORD event, HWND window) {
     34   // Step 1: Make sure that we are in a process named iexplore.exe.
     35   if (IsNamedProcess(L"iexplore.exe")) {
     36     if (!IsWindowOfClass(window, kStatusBarWindowClass) &&
     37         !IsWindowOfClass(window, kAsyncBoundaryDnWindow)) {
     38       return;
     39     } else {
     40       // We have the right sort of window, check to make sure it was created
     41       // on the current thread.
     42       DWORD thread_id = GetWindowThreadProcessId(window, NULL);
     43       _ASSERTE(thread_id == GetCurrentThreadId());
     44     }
     45 
     46     // Step 2: Check to see if the window is of the right class.
     47     HWND browser_hwnd = NULL;
     48     if (IsWindowOfClass(window, kStatusBarWindowClass)) {
     49       // For IE8 and under, IE loads BHOs in the WM_CREATE handler of the tab
     50       // window approximately after it creates the status bar window. To be as
     51       // close to IE as possible in our simulation on BHO loading, we watch for
     52       // the status bar to be created and do our simulated BHO loading at that
     53       // time.
     54       browser_hwnd = GetParent(window);
     55     } else if (IsWindowOfClass(window, kAsyncBoundaryDnWindow)) {
     56       // For IE9, the status bar is disabled by default, so we look for an
     57       // AsyncBoundaryWindow to be created. When we find that, look for a
     58       // child window owned by the current thread named "tabwindowclass".
     59       // That will be our browser window.
     60       browser_hwnd = RecurseFindWindow(NULL, L"tabwindowclass", NULL,
     61                                        GetCurrentThreadId(),
     62                                        GetCurrentProcessId());
     63       _ASSERTE(NULL != browser_hwnd);
     64     }
     65 
     66     if (browser_hwnd != NULL) {
     67       // Step 3:
     68       // Parent window of status bar window is the web browser window. Try to
     69       // get its IWebBrowser2 interface
     70       CComPtr<IWebBrowser2> browser;
     71       UtilGetWebBrowserObjectFromWindow(browser_hwnd, __uuidof(browser),
     72                                         reinterpret_cast<void**>(&browser));
     73       if (browser) {
     74         if (IsSystemLevelChromeFrameInstalled()) {
     75           // We're in the right place, but a system-level installation has
     76           // appeared. We should leave now.
     77           return;
     78         }
     79 
     80         // Figure out if we're already in the property map.
     81         wchar_t bho_clsid_as_string[MAX_PATH] = {0};
     82         StringFromGUID2(CLSID_ChromeFrameBHO, bho_clsid_as_string,
     83                         ARRAYSIZE(bho_clsid_as_string));
     84         CComBSTR bho_clsid_as_string_bstr(bho_clsid_as_string);
     85 
     86         CComVariant existing_bho;
     87         HRESULT hr = browser->GetProperty(bho_clsid_as_string_bstr,
     88                                           &existing_bho);
     89 
     90         if (V_VT(&existing_bho) != VT_DISPATCH &&
     91             V_VT(&existing_bho) != VT_UNKNOWN) {
     92           // Step 4:
     93           // We have the IWebBrowser2 interface. Now create the BHO instance
     94           CComPtr<IObjectWithSite> bho_object;
     95           hr =  bho_object.CoCreateInstance(CLSID_ChromeFrameBHO,
     96                                             NULL,
     97                                             CLSCTX_INPROC_SERVER);
     98 
     99           _ASSERTE(bho_object);
    100           if (SUCCEEDED(hr) && bho_object) {
    101             // Step 5:
    102             // Initialize the BHO by calling SetSite and passing it IWebBrowser2
    103             hr = bho_object->SetSite(browser);
    104             _ASSERTE(bho_object);
    105             if (SUCCEEDED(hr)) {
    106               // Step 6:
    107               // Now add the BHO to the collection of automation objects. This
    108               // will ensure that BHO will be accessible from the web pages as
    109               // any other BHO. Importantly, it will make sure that our BHO
    110               // will be cleaned up at the right time along with other BHOs.
    111               CComVariant object_variant(bho_object);
    112               browser->PutProperty(bho_clsid_as_string_bstr, object_variant);
    113             }
    114           }
    115         }
    116       }
    117     }
    118   }
    119 }
    120 
    121 bool BHOLoader::StartHook() {
    122   return hooker_->StartHook();
    123 }
    124 
    125 void BHOLoader::StopHook() {
    126   hooker_->StopHook();
    127 }
    128 
    129 BHOLoader* BHOLoader::GetInstance() {
    130   static BHOLoader loader;
    131   return &loader;
    132 }
    133