summaryrefslogtreecommitdiffstats
path: root/chrome_frame/bho_loader.cc
blob: d76898cf8f31813e6706bee88198b44548b3a664 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (c) 2011 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.

#include "chrome_frame/bho_loader.h"

#include <atlbase.h>
#include <atlcomcli.h>
#include <exdisp.h>

#include "chrome_frame/chrome_frame_helper_util.h"
#include "chrome_frame/chrome_tab.h"
#include "chrome_frame/event_hooker.h"


// Describes the window class we look for.
const wchar_t kStatusBarWindowClass[] = L"msctls_statusbar32";

// On IE9, the status bar is disabled by default, so we look for an
// AsyncBoundaryLayer window instead.
const wchar_t kAsyncBoundaryDnWindow[] = L"asynclayerboundarydn\0";

BHOLoader::BHOLoader() : hooker_(new EventHooker()) {
}

BHOLoader::~BHOLoader() {
  if (hooker_) {
    delete hooker_;
    hooker_ = NULL;
  }
}

void BHOLoader::OnHookEvent(DWORD event, HWND window) {
  // Step 1: Make sure that we are in a process named iexplore.exe.
  if (IsNamedProcess(L"iexplore.exe")) {
    if (!IsWindowOfClass(window, kStatusBarWindowClass) &&
        !IsWindowOfClass(window, kAsyncBoundaryDnWindow)) {
      return;
    } else {
      // We have the right sort of window, check to make sure it was created
      // on the current thread.
      DWORD thread_id = GetWindowThreadProcessId(window, NULL);
      _ASSERTE(thread_id == GetCurrentThreadId());
    }

    // Step 2: Check to see if the window is of the right class.
    HWND browser_hwnd = NULL;
    if (IsWindowOfClass(window, kStatusBarWindowClass)) {
      // For IE8 and under, IE loads BHOs in the WM_CREATE handler of the tab
      // window approximately after it creates the status bar window. To be as
      // close to IE as possible in our simulation on BHO loading, we watch for
      // the status bar to be created and do our simulated BHO loading at that
      // time.
      browser_hwnd = GetParent(window);
    } else if (IsWindowOfClass(window, kAsyncBoundaryDnWindow)) {
      // For IE9, the status bar is disabled by default, so we look for an
      // AsyncBoundaryWindow to be created. When we find that, look for a
      // child window owned by the current thread named "tabwindowclass".
      // That will be our browser window.
      browser_hwnd = RecurseFindWindow(NULL, L"tabwindowclass", NULL,
                                       GetCurrentThreadId(),
                                       GetCurrentProcessId());
      _ASSERTE(NULL != browser_hwnd);
    }

    if (browser_hwnd != NULL) {
      // Step 3:
      // Parent window of status bar window is the web browser window. Try to
      // get its IWebBrowser2 interface
      CComPtr<IWebBrowser2> browser;
      UtilGetWebBrowserObjectFromWindow(browser_hwnd, __uuidof(browser),
                                        reinterpret_cast<void**>(&browser));
      if (browser) {
        if (IsSystemLevelChromeFrameInstalled()) {
          // We're in the right place, but a system-level installation has
          // appeared. We should leave now.
          return;
        }

        // Figure out if we're already in the property map.
        wchar_t bho_clsid_as_string[MAX_PATH] = {0};
        StringFromGUID2(CLSID_ChromeFrameBHO, bho_clsid_as_string,
                        ARRAYSIZE(bho_clsid_as_string));
        CComBSTR bho_clsid_as_string_bstr(bho_clsid_as_string);

        CComVariant existing_bho;
        HRESULT hr = browser->GetProperty(bho_clsid_as_string_bstr,
                                          &existing_bho);

        if (V_VT(&existing_bho) != VT_DISPATCH &&
            V_VT(&existing_bho) != VT_UNKNOWN) {
          // Step 4:
          // We have the IWebBrowser2 interface. Now create the BHO instance
          CComPtr<IObjectWithSite> bho_object;
          hr =  bho_object.CoCreateInstance(CLSID_ChromeFrameBHO,
                                            NULL,
                                            CLSCTX_INPROC_SERVER);

          _ASSERTE(bho_object);
          if (SUCCEEDED(hr) && bho_object) {
            // Step 5:
            // Initialize the BHO by calling SetSite and passing it IWebBrowser2
            hr = bho_object->SetSite(browser);
            _ASSERTE(bho_object);
            if (SUCCEEDED(hr)) {
              // Step 6:
              // Now add the BHO to the collection of automation objects. This
              // will ensure that BHO will be accessible from the web pages as
              // any other BHO. Importantly, it will make sure that our BHO
              // will be cleaned up at the right time along with other BHOs.
              CComVariant object_variant(bho_object);
              browser->PutProperty(bho_clsid_as_string_bstr, object_variant);
            }
          }
        }
      }
    }
  }
}

bool BHOLoader::StartHook() {
  return hooker_->StartHook();
}

void BHOLoader::StopHook() {
  hooker_->StopHook();
}

BHOLoader* BHOLoader::GetInstance() {
  static BHOLoader loader;
  return &loader;
}