summaryrefslogtreecommitdiffstats
path: root/ceee/ie/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'ceee/ie/plugin')
-rw-r--r--ceee/ie/plugin/bho/browser_helper_object.cc29
-rw-r--r--ceee/ie/plugin/bho/browser_helper_object.h11
-rw-r--r--ceee/ie/plugin/bho/browser_helper_object.rgs1
-rw-r--r--ceee/ie/plugin/bho/browser_helper_object_unittest.cc7
-rw-r--r--ceee/ie/plugin/bho/executor.cc233
-rw-r--r--ceee/ie/plugin/bho/executor.h107
-rw-r--r--ceee/ie/plugin/bho/executor_com_unittest.cc762
-rw-r--r--ceee/ie/plugin/bho/testing_invoke_executor.cc96
-rw-r--r--ceee/ie/plugin/toolband/resource.h2
-rw-r--r--ceee/ie/plugin/toolband/toolband.gyp30
-rw-r--r--ceee/ie/plugin/toolband/toolband.idl19
-rwxr-xr-xceee/ie/plugin/toolband/toolband.rc1
-rw-r--r--ceee/ie/plugin/toolband/toolband_module.cc36
-rw-r--r--ceee/ie/plugin/toolband/toolband_proxy.cc150
-rw-r--r--ceee/ie/plugin/toolband/toolband_proxy.h29
-rw-r--r--ceee/ie/plugin/toolband/toolband_proxy.rgs11
16 files changed, 1498 insertions, 26 deletions
diff --git a/ceee/ie/plugin/bho/browser_helper_object.cc b/ceee/ie/plugin/bho/browser_helper_object.cc
index e237442..d0f5cdc 100644
--- a/ceee/ie/plugin/bho/browser_helper_object.cc
+++ b/ceee/ie/plugin/bho/browser_helper_object.cc
@@ -27,6 +27,7 @@
#include "ceee/ie/plugin/bho/cookie_accountant.h"
#include "ceee/ie/plugin/bho/http_negotiate.h"
#include "ceee/ie/plugin/scripting/script_host.h"
+#include "ceee/ie/plugin/toolband/toolband_proxy.h"
#include "chrome/browser/automation/extension_automation_constants.h"
#include "chrome/browser/extensions/extension_tabs_module_constants.h"
#include "chrome/common/automation_constants.h"
@@ -151,14 +152,25 @@ STDMETHODIMP BrowserHelperObject::SetSite(IUnknown* site) {
// We're being initialized.
hr = Initialize(site);
- // Release the site in case of failure.
- if (FAILED(hr))
+ // Release the site, and tear down our own state in case of failure.
+ if (FAILED(hr)) {
+ TearDown();
SuperSite::SetSite(NULL);
+ }
}
return hr;
}
+HRESULT BrowserHelperObject::RegisterProxies() {
+ return RegisterProxyStubs(&proxy_stub_cookies_) ? S_OK : E_UNEXPECTED;
+}
+
+void BrowserHelperObject::UnregisterProxies() {
+ UnregisterProxyStubs(proxy_stub_cookies_);
+ proxy_stub_cookies_.clear();
+}
+
HRESULT BrowserHelperObject::GetParentBrowser(IWebBrowser2* browser,
IWebBrowser2** parent_browser) {
DCHECK(browser != NULL);
@@ -245,13 +257,18 @@ HRESULT BrowserHelperObject::Initialize(IUnknown* site) {
DCHECK(SUCCEEDED(hr)) << "InitializeChromeFrameHost failed " <<
com::LogHr(hr);
if (FAILED(hr)) {
- TearDown();
return hr;
}
// Initialize the extension port manager.
extension_port_manager_.Initialize(chrome_frame_host_);
+ // Register the proxy/stubs for the executor.
+ // Note the proxy registration function will have logged what occurred.
+ hr = RegisterProxies();
+ if (FAILED(hr))
+ return hr;
+
// Preemptively feed the broker with an executor in our thread.
hr = GetBrokerRegistrar(&broker_registrar_);
LOG_IF(ERROR, FAILED(hr)) << "Failed to create broker, hr=" <<
@@ -280,21 +297,18 @@ HRESULT BrowserHelperObject::Initialize(IUnknown* site) {
CComQIPtr<IServiceProvider> service_provider(site);
DCHECK(service_provider);
if (service_provider == NULL) {
- TearDown();
return E_FAIL;
}
hr = ConnectSinks(service_provider);
DCHECK(SUCCEEDED(hr)) << "ConnectSinks failed " << com::LogHr(hr);
if (FAILED(hr)) {
- TearDown();
return hr;
}
hr = GetTabWindow(service_provider);
DCHECK(SUCCEEDED(hr)) << "GetTabWindow failed " << com::LogHr(hr);
if (FAILED(hr)) {
- TearDown();
return hr;
}
@@ -363,6 +377,9 @@ HRESULT BrowserHelperObject::TearDown() {
}
chrome_frame_host_.Release();
+ // Unregister the proxy/stubs for the executor.
+ UnregisterProxies();
+
return S_OK;
}
diff --git a/ceee/ie/plugin/bho/browser_helper_object.h b/ceee/ie/plugin/bho/browser_helper_object.h
index 039b305..8f79453 100644
--- a/ceee/ie/plugin/bho/browser_helper_object.h
+++ b/ceee/ie/plugin/bho/browser_helper_object.h
@@ -163,6 +163,11 @@ class ATL_NO_VTABLE BrowserHelperObject
// @}
protected:
+ // Register proxy/stubs for executor interfaces.
+ virtual HRESULT RegisterProxies();
+ // Unregister proxy/stubs for executor interfaces.
+ virtual void UnregisterProxies();
+
typedef base::win::ScopedComPtr<IContentScriptNativeApi, &GUID_NULL>
ScopedContentScriptNativeApiPtr;
typedef base::win::ScopedComPtr<IDispatch> ScopedDispatchPtr;
@@ -220,6 +225,8 @@ class ATL_NO_VTABLE BrowserHelperObject
// Initializes the BHO to the given site.
// Called from SetSite.
+ // @note On failure the state of the BHO may be inconsistent,
+ // so a TearDown may be needed.
HRESULT Initialize(IUnknown* site);
// Tears down an initialized bho.
@@ -371,6 +378,10 @@ class ATL_NO_VTABLE BrowserHelperObject
BrokerRpcClient broker_rpc_;
private:
+ // The BHO registers proxy/stubs for the CEEE executor on initialization.
+ // These are the cookies to allow us to unregister then on teardown.
+ std::vector<DWORD> proxy_stub_cookies_;
+
// Used during initialization to get the tab information from Chrome and
// register ourselves with the broker.
HRESULT RegisterTabInfo();
diff --git a/ceee/ie/plugin/bho/browser_helper_object.rgs b/ceee/ie/plugin/bho/browser_helper_object.rgs
index f7331a6..fd794ab 100644
--- a/ceee/ie/plugin/bho/browser_helper_object.rgs
+++ b/ceee/ie/plugin/bho/browser_helper_object.rgs
@@ -4,7 +4,6 @@ NoRemove CLSID {
InprocServer32 = s '%MODULE%' {
val ThreadingModel = s 'Apartment'
}
- 'TypeLib' = s '{7C09079D-F9CB-4E9E-9293-D224B071D8BA}'
}
}
}
diff --git a/ceee/ie/plugin/bho/browser_helper_object_unittest.cc b/ceee/ie/plugin/bho/browser_helper_object_unittest.cc
index 8bbac5c..be970e8 100644
--- a/ceee/ie/plugin/bho/browser_helper_object_unittest.cc
+++ b/ceee/ie/plugin/bho/browser_helper_object_unittest.cc
@@ -133,6 +133,13 @@ class TestingBrowserHelperObject
MOCK_METHOD2(GetParentBrowser, HRESULT(IWebBrowser2*, IWebBrowser2**));
+ // Neuter the proxy registration/unregistration.
+ HRESULT RegisterProxies() {
+ return S_OK;
+ }
+ void UnregisterProxies() {
+ }
+
// Pulicize
using BrowserHelperObject::HandleNavigateComplete;
diff --git a/ceee/ie/plugin/bho/executor.cc b/ceee/ie/plugin/bho/executor.cc
index d32410f..a6e6f7b 100644
--- a/ceee/ie/plugin/bho/executor.cc
+++ b/ceee/ie/plugin/bho/executor.cc
@@ -36,7 +36,6 @@
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/values.h"
-#include "base/scoped_ptr.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "ceee/common/com_utils.h"
@@ -46,6 +45,7 @@
#include "ceee/ie/plugin/bho/frame_event_handler.h"
#include "ceee/ie/plugin/bho/infobar_manager.h"
#include "ceee/ie/plugin/bho/tab_window_manager.h"
+#include "ceee/ie/plugin/toolband/toolband_proxy.h"
#include "chrome_frame/utils.h"
#include "broker_lib.h" // NOLINT
@@ -146,6 +146,14 @@ LRESULT CeeeExecutorCreator::GetMsgProc(int code, WPARAM wparam,
return 0;
}
+ // Register the proxy/stubs for the executor.
+ // We don't make arrangements to unregister them here as we don't
+ // expect we'll ever unload from the process.
+ // TODO(siggi@chromium.org): Is it worth arranging for unregistration
+ // in this case?
+ if (!RegisterProxyStubs(NULL))
+ LOG(ERROR) << "Executor Creator failed to register proxy/stubs";
+
CComPtr<ICeeeWindowExecutor> executor;
HRESULT hr = executor.CoCreateInstance(CLSID_CeeeExecutor);
LOG_IF(ERROR, FAILED(hr)) << "Failed to create Executor, hr=" <<
@@ -179,6 +187,229 @@ LRESULT CeeeExecutorCreator::GetMsgProc(int code, WPARAM wparam,
return ::CallNextHookEx(NULL, code, wparam, lparam);
}
+AsyncTabCall::AsyncTabCall() : task_hr_(E_PENDING), task_(NULL) {
+ VLOG(1) << "AsyncTabCall " << this << " created";
+}
+
+AsyncTabCall::~AsyncTabCall() {
+ VLOG(1) << "AsyncTabCall " << this << " deleted";
+}
+
+HRESULT AsyncTabCall::CreateInitialized(ICeeeTabExecutor* executor,
+ IUnknown* outer,
+ IUnknown** async_call) {
+ CComPolyObject<AsyncTabCall>* async_tab_call = NULL;
+ HRESULT hr = CComPolyObject<AsyncTabCall>::CreateInstance(
+ outer, &async_tab_call);
+
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to create async tab call " << com::LogHr(hr);
+ return hr;
+ }
+ DCHECK(async_tab_call != NULL);
+
+ hr = async_tab_call->m_contained.Initialize(executor);
+ if (FAILED(hr)) {
+ delete async_tab_call;
+ LOG(ERROR) << "Async tab call initialization failed " << com::LogHr(hr);
+ }
+
+ return async_tab_call->QueryInterface(async_call);
+}
+
+HRESULT AsyncTabCall::Initialize(ICeeeTabExecutor* executor) {
+ DCHECK(executor != NULL);
+ executor_ = executor;
+ return S_OK;
+}
+
+STDMETHODIMP AsyncTabCall::Begin_Initialize(CeeeWindowHandle hwnd) {
+ DCHECK(task_hr_ == E_PENDING && task_ == NULL);
+ task_hr_ = executor_->Initialize(hwnd);
+ return Signal();
+}
+
+STDMETHODIMP AsyncTabCall::Finish_Initialize() {
+ return task_hr_;
+}
+
+// A noop function.
+static void Noop() {
+}
+
+STDMETHODIMP AsyncTabCall::Begin_GetTabInfo() {
+ DCHECK(task_hr_ == E_PENDING && task_ == NULL);
+
+ // We do all the work on Finish_GetTabInfo, so schedule only a noop
+ // invocation. Alternatively we could schedule NULL here, though that
+ // would make it more difficult to distinguish error cases.
+ return ScheduleTask(NewRunnableFunction(Noop));
+}
+
+STDMETHODIMP AsyncTabCall::Finish_GetTabInfo(CeeeTabInfo *tab_info) {
+ return executor_->GetTabInfo(tab_info);
+}
+
+STDMETHODIMP AsyncTabCall::Begin_Navigate(BSTR url, long flags, BSTR target) {
+ DCHECK(task_hr_ == E_PENDING && task_ == NULL);
+
+ if (!ScheduleTask(NewRunnableMethod(this,
+ &AsyncTabCall::NavigateImpl,
+ CComBSTR(url),
+ flags,
+ CComBSTR(target)))) {
+ LOG(ERROR) << "Failed to schedule navigation task";
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP AsyncTabCall::Finish_Navigate() {
+ return task_hr_;
+}
+
+STDMETHODIMP AsyncTabCall::Begin_InsertCode(BSTR code,
+ BSTR file,
+ BOOL all_frames,
+ CeeeTabCodeType type) {
+ DCHECK(task_hr_ == E_PENDING && task_ == NULL);
+
+ if (!ScheduleTask(NewRunnableMethod(this,
+ &AsyncTabCall::InsertCodeImpl,
+ CComBSTR(code),
+ CComBSTR(file),
+ all_frames,
+ type))) {
+ LOG(ERROR) << "Failed to schedule insert code task";
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP AsyncTabCall::Finish_InsertCode() {
+ return task_hr_;
+}
+
+void AsyncTabCall::NavigateImpl(const CComBSTR& url,
+ long flags,
+ const CComBSTR& target) {
+ task_hr_ = executor_->Navigate(url, flags, target);
+}
+
+void AsyncTabCall::InsertCodeImpl(const CComBSTR& code,
+ const CComBSTR& file,
+ BOOL all_frames,
+ CeeeTabCodeType type) {
+ task_hr_ = executor_->InsertCode(code, file, all_frames, type);
+}
+
+int AsyncTabCall::OnCreate(LPCREATESTRUCT lpCreateStruct) {
+ // Our window maintains a self-reference to our object.
+ GetUnknown()->AddRef();
+ return 1;
+}
+
+LRESULT AsyncTabCall::OnExecuteTask(
+ UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled) {
+ VLOG(1) << "OnExecuteTask for " << this;
+
+ if (task_.get() != NULL) {
+ task_->Run();
+ task_.reset();
+ }
+
+ // Signal the call done, this'll cause the Finish_ call to execute.
+ Signal();
+
+ DestroyWindow();
+ return 1;
+}
+
+void AsyncTabCall::OnFinalMessage(HWND window) {
+ // Release our window's self-reference.
+ GetUnknown()->Release();
+}
+
+bool AsyncTabCall::ScheduleTask(Task* task) {
+ DCHECK(task != NULL);
+ DCHECK(m_hWnd == NULL);
+
+ if (Create(HWND_MESSAGE) == NULL) {
+ LOG(ERROR) << "Failed to create window";
+
+ delete task;
+ return false;
+ }
+ DCHECK(m_hWnd != NULL);
+
+ // Schedule the task for later by posting a message.
+ task_.reset(task);
+ PostMessage(kExecuteTaskMessage);
+
+ return true;
+}
+
+HRESULT AsyncTabCall::Signal() {
+ // We need to explicitly query for ISynchronize, because we're
+ // most likely aggregated, and the implementation is on our
+ // controlling outer.
+ CComPtr<ISynchronize> sync;
+ HRESULT hr = GetUnknown()->QueryInterface(&sync);
+ if (sync == NULL) {
+ LOG(ERROR) << "Failed to retrieve ISynchronize " << com::LogHr(hr);
+ return hr;
+ }
+ DCHECK(sync != NULL);
+
+ hr = sync->Signal();
+ if (FAILED(hr))
+ LOG(ERROR) << "Failed to signal " << com::LogHr(hr);
+
+ return hr;
+}
+
+CeeeExecutor::CeeeExecutor() : hwnd_(NULL) {
+}
+
+CeeeExecutor::~CeeeExecutor() {
+}
+
+HRESULT CeeeExecutor::CreateTabCall(ICeeeTabExecutor* executor,
+ IUnknown *outer,
+ REFIID riid2,
+ IUnknown **out) {
+ CComPtr<IUnknown> tab_call;
+ HRESULT hr = AsyncTabCall::CreateInitialized(executor, outer, &tab_call);
+ if (SUCCEEDED(hr))
+ hr = tab_call->QueryInterface(riid2, reinterpret_cast<void**>(out));
+
+ if (FAILED(hr)) {
+ delete tab_call;
+ LOG(ERROR) << "Async executor initialization failed " << com::LogHr(hr);
+ }
+
+ return hr;
+}
+
+STDMETHODIMP CeeeExecutor::CreateCall(REFIID async_iid,
+ IUnknown *outer,
+ REFIID requested_iid,
+ IUnknown **out) {
+ DCHECK(outer != NULL);
+ DCHECK(out != NULL);
+ // Null for safety.
+ *out = NULL;
+
+ if (async_iid == IID_AsyncICeeeTabExecutor) {
+ return CreateTabCall(this, outer, requested_iid, out);
+ } else {
+ LOG(ERROR) << "Unexpected IID to CreateCall";
+ return E_NOINTERFACE;
+ }
+}
+
HRESULT CeeeExecutor::Initialize(CeeeWindowHandle hwnd) {
DCHECK(hwnd);
hwnd_ = reinterpret_cast<HWND>(hwnd);
diff --git a/ceee/ie/plugin/bho/executor.h b/ceee/ie/plugin/bho/executor.h
index 379ba32..eeea590 100644
--- a/ceee/ie/plugin/bho/executor.h
+++ b/ceee/ie/plugin/bho/executor.h
@@ -14,6 +14,7 @@
#include <string.h>
#include "base/scoped_ptr.h"
+#include "base/task.h"
#include "ceee/ie/plugin/bho/infobar_manager.h"
#include "ceee/ie/plugin/toolband/resource.h"
@@ -69,12 +70,98 @@ class ATL_NO_VTABLE CeeeExecutorCreator
long current_thread_id_;
};
+// Implements an asynchronous call object for ICeeeTabExecutor.
+// This is instantiated through the executor's ICallFactory::CreateCall method.
+// For asynchronous methods, it posts a window message and processes the
+// method completion when the message is dispatched. This is done to avoid
+// performing significant operations inside IE, reentrantly during an
+// outgoing out-of-apartment call. During those times, IE and the various
+// third-party addons performing such outcalls tend to be in a fragile state.
+// @note the COM marshaling machinery will instantiate a new one of these for
+// each call it handles, so these are not implemented to cope with reuse.
+class AsyncTabCall
+ : public CComObjectRootEx<CComSingleThreadModel>,
+ public CComCoClass<AsyncTabCall>,
+ public CWindowImpl<AsyncTabCall>,
+ public AsyncICeeeTabExecutor {
+ public:
+ DECLARE_GET_CONTROLLING_UNKNOWN()
+
+ BEGIN_COM_MAP(AsyncTabCall)
+ COM_INTERFACE_ENTRY(AsyncICeeeTabExecutor)
+ COM_INTERFACE_ENTRY_AUTOAGGREGATE(IID_ISynchronize,
+ synchronize_.p,
+ CLSID_ManualResetEvent)
+ END_COM_MAP()
+
+ // The window message we post to ourselves.
+ static const uint32 kExecuteTaskMessage = WM_USER;
+
+ BEGIN_MSG_MAP(AsyncTabCall)
+ MSG_WM_CREATE(OnCreate)
+ MESSAGE_HANDLER(kExecuteTaskMessage, OnExecuteTask)
+ END_MSG_MAP()
+
+ AsyncTabCall();
+ ~AsyncTabCall();
+
+ // Creates an initialized instance. Aggregated if outer is non-NULL.
+ // @param executor the executor instance we invoke on.
+ // @param outer the controlling outer or NULL.
+ // @param async_call on success, returns the new AsyncTabCall.
+ static HRESULT CreateInitialized(ICeeeTabExecutor* executor,
+ IUnknown* outer,
+ IUnknown** async_call);
+
+ static bool ImplementsThreadSafeReferenceCounting() {
+ return false;
+ }
+
+ // @name AsyncICeeeTabExecutor implementation.
+ // @{
+ STDMETHOD(Begin_Initialize)(CeeeWindowHandle hwnd);
+ STDMETHOD(Finish_Initialize)();
+ STDMETHOD(Begin_GetTabInfo)();
+ STDMETHOD(Finish_GetTabInfo)(CeeeTabInfo *tab_info);
+ STDMETHOD(Begin_Navigate)(BSTR url, long flags, BSTR target);
+ STDMETHOD(Finish_Navigate)();
+ STDMETHOD(Begin_InsertCode)(BSTR code, BSTR file, BOOL all_frames,
+ CeeeTabCodeType type);
+ STDMETHOD(Finish_InsertCode)();
+ // @}
+
+ // Initialize a newly constructed async tab call.
+ // This is public only for unittesting.
+ HRESULT Initialize(ICeeeTabExecutor* executor);
+
+ private:
+ void NavigateImpl(const CComBSTR& url, long flags, const CComBSTR& target);
+ void InsertCodeImpl(const CComBSTR& code,
+ const CComBSTR& file,
+ BOOL all_frames,
+ CeeeTabCodeType type);
+
+ int OnCreate(LPCREATESTRUCT lpCreateStruct);
+ LRESULT OnExecuteTask(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);
+ virtual void OnFinalMessage(HWND window);
+ bool ScheduleTask(Task* task);
+ HRESULT Signal();
+
+ CComPtr<IUnknown> synchronize_;
+ CComPtr<ICeeeTabExecutor> executor_;
+
+ // The task we've scheduled for execution.
+ scoped_ptr<Task> task_;
+ HRESULT task_hr_;
+};
+
// The executor object that is instantiated in the destination thread and
// then called to... execute stuff...
class ATL_NO_VTABLE CeeeExecutor
: public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CeeeExecutor, &CLSID_CeeeExecutor>,
public IObjectWithSiteImpl<CeeeExecutor>,
+ public ICallFactory,
public ICeeeWindowExecutor,
public ICeeeTabExecutor,
public ICeeeCookieExecutor,
@@ -85,6 +172,7 @@ class ATL_NO_VTABLE CeeeExecutor
DECLARE_NOT_AGGREGATABLE(CeeeExecutor)
BEGIN_COM_MAP(CeeeExecutor)
COM_INTERFACE_ENTRY(IObjectWithSite)
+ COM_INTERFACE_ENTRY(ICallFactory)
COM_INTERFACE_ENTRY(ICeeeWindowExecutor)
COM_INTERFACE_ENTRY(ICeeeTabExecutor)
COM_INTERFACE_ENTRY(ICeeeCookieExecutor)
@@ -93,6 +181,17 @@ class ATL_NO_VTABLE CeeeExecutor
DECLARE_PROTECT_FINAL_CONSTRUCT()
DECLARE_CLASSFACTORY()
+ CeeeExecutor();
+ ~CeeeExecutor();
+
+ // @name ICallFactory implementation.
+ // @{
+ STDMETHOD(CreateCall)(REFIID async_iid,
+ IUnknown *outer,
+ REFIID requested_iid,
+ IUnknown **out);
+ // @}
+
// @name ICeeeWindowExecutor implementation.
// @{
STDMETHOD(Initialize)(CeeeWindowHandle hwnd);
@@ -131,9 +230,13 @@ class ATL_NO_VTABLE CeeeExecutor
STDMETHOD(OnTopFrameBeforeNavigate)(BSTR url);
// @}
- CeeeExecutor() : hwnd_(NULL) {}
-
protected:
+ // Unittest seam.
+ virtual HRESULT CreateTabCall(ICeeeTabExecutor* executor,
+ IUnknown *outer,
+ REFIID riid2,
+ IUnknown **out);
+
// Get the IWebBrowser2 interface of the
// frame event host that was set as our site.
virtual HRESULT GetWebBrowser(IWebBrowser2** browser);
diff --git a/ceee/ie/plugin/bho/executor_com_unittest.cc b/ceee/ie/plugin/bho/executor_com_unittest.cc
new file mode 100644
index 0000000..c1379a8
--- /dev/null
+++ b/ceee/ie/plugin/bho/executor_com_unittest.cc
@@ -0,0 +1,762 @@
+// Copyright (c) 2010 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.
+//
+// Executor COM implementation unit tests.
+
+#include "ceee/ie/plugin/bho/executor.h"
+
+#include <shlobj.h>
+#include <atlbase.h>
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/thread.h"
+#include "base/win/registry.h"
+#include "base/win/windows_version.h"
+#include "ceee/common/initializing_coclass.h"
+#include "ceee/ie/plugin/toolband/toolband_proxy.h"
+#include "ceee/ie/testing/mock_broker_and_friends.h"
+#include "ceee/testing/utils/instance_count_mixin.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using testing::_;
+using testing::DoAll;
+using testing::Invoke;
+using testing::InSequence;
+using testing::IsNull;
+using testing::NotNull;
+using testing::Return;
+using testing::StrEq;
+using testing::StrictMock;
+using testing::InstanceCountMixin;
+using testing::InstanceCountMixinBase;
+
+class ISynchronizeMock: public ISynchronize {
+ public:
+ MOCK_METHOD2_WITH_CALLTYPE(__stdcall, Wait,
+ HRESULT(DWORD flags, DWORD milliseconds));
+ MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Signal, HRESULT());
+ MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Reset, HRESULT());
+};
+
+class TestISynchronize
+ : public CComObjectRootEx<CComMultiThreadModel>,
+ public InitializingCoClass<TestISynchronize>,
+ public InstanceCountMixin<TestISynchronize>,
+ public StrictMock<ISynchronizeMock> {
+ public:
+ DECLARE_GET_CONTROLLING_UNKNOWN()
+ BEGIN_COM_MAP(TestISynchronize)
+ COM_INTERFACE_ENTRY(ISynchronize)
+ COM_INTERFACE_ENTRY_AGGREGATE_BLIND(async_call_.p)
+ END_COM_MAP()
+
+ TestISynchronize() {
+ }
+
+ ~TestISynchronize() {
+ }
+
+ HRESULT Initialize(TestISynchronize** self) {
+ *self = this;
+ return S_OK;
+ }
+
+ void set_async_call(IUnknown* async_call) { async_call_ = async_call; }
+
+ private:
+ // This is the async call instance we aggregate.
+ CComPtr<IUnknown> async_call_;
+};
+
+class MockCeeeExecutor
+ : public CComObjectRootEx<CComSingleThreadModel>,
+ public InitializingCoClass<MockCeeeExecutor>,
+ public InstanceCountMixin<MockCeeeExecutor>,
+ public StrictMock<testing::MockCeeeWindowExecutorImpl>,
+ public StrictMock<testing::MockCeeeTabExecutorImpl>,
+ public StrictMock<testing::MockCeeeCookieExecutorImpl>,
+ public StrictMock<testing::MockCeeeInfobarExecutorImpl> {
+ public:
+ BEGIN_COM_MAP(MockCeeeExecutor)
+ COM_INTERFACE_ENTRY(ICeeeWindowExecutor)
+ COM_INTERFACE_ENTRY(ICeeeTabExecutor)
+ COM_INTERFACE_ENTRY(ICeeeCookieExecutor)
+ COM_INTERFACE_ENTRY(ICeeeInfobarExecutor)
+ END_COM_MAP()
+
+ HRESULT Initialize(MockCeeeExecutor** self) {
+ *self = this;
+ return S_OK;
+ }
+};
+
+class TestAsyncTabCall
+ : public AsyncTabCall,
+ public InitializingCoClass<TestAsyncTabCall>,
+ public InstanceCountMixin<TestAsyncTabCall> {
+ public:
+};
+
+// A test subclass of CeeeExecutor to allow testing
+// the async behavior of ICeeeTabExecutor.
+class TestCeeeExecutor
+ : public CeeeExecutor,
+ public InitializingCoClass<TestCeeeExecutor>,
+ public InstanceCountMixin<TestCeeeExecutor> {
+ public:
+ HRESULT Initialize(TestCeeeExecutor** self) {
+ *self = this;
+ return S_OK;
+ }
+
+ // Mock out the ICeeeTabExecutor interface implementation.
+ MOCK_METHOD1_WITH_CALLTYPE(__stdcall, Initialize,
+ HRESULT(CeeeWindowHandle hwnd));
+ MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetTabInfo,
+ HRESULT(CeeeTabInfo* tab_info));
+ MOCK_METHOD3_WITH_CALLTYPE(__stdcall, Navigate,
+ HRESULT(BSTR url, long flags, BSTR target));
+ MOCK_METHOD4_WITH_CALLTYPE(__stdcall, InsertCode,
+ HRESULT(BSTR code, BSTR file, BOOL all_frames, CeeeTabCodeType type));
+
+ MOCK_METHOD4(CreateTabCall, HRESULT(ICeeeTabExecutor* executor,
+ IUnknown *outer,
+ REFIID riid2,
+ IUnknown **out));
+};
+
+class RemoteObjectHost;
+DISABLE_RUNNABLE_METHOD_REFCOUNT(RemoteObjectHost);
+
+class RemoteObjectHost {
+ public:
+ RemoteObjectHost() : remote_thread_("RemoteObjectHost") {
+ }
+
+ ~RemoteObjectHost() {
+ remote_git_ptr_.Revoke();
+ remote_object_.Release();
+ }
+
+ void HostObject(IUnknown* object) {
+ // Spin up our thread and initialize it.
+ ASSERT_TRUE(remote_thread_.StartWithOptions(
+ base::Thread::Options(MessageLoop::TYPE_UI, 0)));
+
+ // Marshal the object in the thread.
+ RunSync(
+ NewRunnableMethod(this, &RemoteObjectHost::InitRemoteThread, object));
+
+ // Marshal the contained object back as an IUnknown.
+ ASSERT_HRESULT_SUCCEEDED(remote_git_ptr_.CopyTo(&remote_object_));
+ }
+
+ void RunSync(Task* task) {
+ ScopedHandle event(::CreateEvent(NULL, TRUE, FALSE, NULL));
+
+ remote_thread_.message_loop()->PostTask(FROM_HERE, task);
+ remote_thread_.message_loop()->PostTask(FROM_HERE,
+ NewRunnableFunction(::SetEvent, event.Get()));
+
+ ASSERT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(event.Get(), INFINITE));
+ }
+
+ void InitRemoteThread(IUnknown* object) {
+ EXPECT_HRESULT_SUCCEEDED(::CoInitialize(NULL));
+ EXPECT_HRESULT_SUCCEEDED(remote_git_ptr_.Attach(object));
+ }
+
+ public:
+ // The git pointer is created in the remote_thread_.
+ CComGITPtr<IUnknown> remote_git_ptr_;
+ // And this is a local proxy to the remote object.
+ CComPtr<IUnknown> remote_object_;
+ base::Thread remote_thread_;
+};
+
+ACTION_P(QuitMessageLoop, message_loop) {
+ message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+}
+
+static const HRESULT E_CAFEBABE = 0xCAFEBABE;
+
+class ExecutorComTest: public testing::Test {
+ public:
+ ExecutorComTest()
+ : executor_(NULL),
+ synchronize_(NULL),
+ loop_(MessageLoop::TYPE_UI) {
+ }
+
+ static bool ImplementsThreadSafeReferenceCounting() {
+ return true;
+ }
+
+ virtual void SetUp() {
+ ASSERT_HRESULT_SUCCEEDED(::CoInitialize(NULL));
+
+ ASSERT_HRESULT_SUCCEEDED(TestCeeeExecutor::CreateInitialized(
+ &executor_, &executor_keeper_));
+ ASSERT_HRESULT_SUCCEEDED(TestISynchronize::CreateInitialized(
+ &synchronize_, &synchronize_keeper_));
+ }
+
+ virtual void TearDown() {
+ executor_ = NULL;
+ executor_keeper_.Release();
+
+ synchronize_->set_async_call(NULL);
+ synchronize_ = NULL;
+ synchronize_keeper_.Release();
+
+ UnregisterProxies();
+
+ // We want everything unregistered before we CoUninitialize.
+ ::CoUninitialize();
+
+ EXPECT_EQ(0, InstanceCountMixinBase::all_instance_count());
+ }
+
+ void RegisterProxies() {
+ ASSERT_TRUE(RegisterProxyStubs(&proxy_cookies_));
+ }
+
+ void UnregisterProxies() {
+ if (!proxy_cookies_.empty()) {
+ UnregisterProxyStubs(proxy_cookies_);
+ proxy_cookies_.clear();
+ }
+ }
+
+ static void CreateTestTabCallImpl(ICeeeTabExecutor* executor,
+ IUnknown *outer,
+ REFIID riid2,
+ IUnknown **out) {
+ CComPolyObject<TestAsyncTabCall>* async_tab_call = NULL;
+ ASSERT_HRESULT_SUCCEEDED(
+ CComPolyObject<TestAsyncTabCall>::CreateInstance(
+ outer, &async_tab_call));
+ ASSERT_TRUE(async_tab_call != NULL);
+ ASSERT_HRESULT_SUCCEEDED(async_tab_call->m_contained.Initialize(executor));
+ ASSERT_HRESULT_SUCCEEDED(
+ async_tab_call->QueryInterface(riid2, reinterpret_cast<void**>(out)));
+ }
+
+ void ExpectCreateTabCall(TestCeeeExecutor* executor) {
+ EXPECT_CALL(*executor, CreateTabCall(executor, _, _, _))
+ .WillOnce(
+ DoAll(
+ Invoke(&ExecutorComTest::CreateTestTabCallImpl),
+ Return(S_OK)));
+ }
+
+ void CreateTabExecutor(AsyncICeeeTabExecutor** executor_call) {
+ // Retrieve the call factory.
+ CComPtr<ICallFactory> call_factory;
+ ASSERT_HRESULT_SUCCEEDED(executor_keeper_->QueryInterface(&call_factory));
+ ASSERT_TRUE(call_factory != NULL);
+
+ // Create the call object.
+ ASSERT_NO_FATAL_FAILURE(ExpectCreateTabCall(executor_));
+ CComPtr<IUnknown> async_unknown;
+ ASSERT_HRESULT_SUCCEEDED(
+ call_factory->CreateCall(IID_AsyncICeeeTabExecutor,
+ synchronize_->GetControllingUnknown(),
+ IID_IUnknown,
+ &async_unknown));
+ synchronize_->set_async_call(async_unknown);
+ async_unknown.Release();
+
+ ASSERT_HRESULT_SUCCEEDED(
+ synchronize_keeper_->QueryInterface(executor_call));
+ }
+
+ void RunShortMessageLoop() {
+ // Set up the expectations we have for invocations
+ // during the brief message loop.
+ EXPECT_CALL(*synchronize_, Signal())
+ .WillOnce(
+ DoAll(
+ QuitMessageLoop(&loop_),
+ Return(S_OK)));
+
+ // And run the loop.
+ loop_.Run();
+ }
+
+ protected:
+ // This test executor is created on setup.
+ TestCeeeExecutor* executor_;
+ CComPtr<ICeeeTabExecutor> executor_keeper_;
+
+ TestISynchronize* synchronize_;
+ CComPtr<ISynchronize> synchronize_keeper_;
+
+ MessageLoop loop_;
+
+ std::vector<DWORD> proxy_cookies_;
+};
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(ExecutorComTest);
+
+static const wchar_t* kUrl = L"http://www.google.com/";
+static long kFlags = 74565;
+static const wchar_t* kTarget = L"_blank";
+
+TEST_F(ExecutorComTest, MarshalingFailsWithNoProxy) {
+ RemoteObjectHost host;
+ ASSERT_NO_FATAL_FAILURE(host.HostObject(executor_keeper_));
+
+ // We expect failure, unmarshaling shouldn't be possible
+ // without the proxies registered in our (source) thread.
+ CComPtr<ICeeeTabExecutor> executor;
+ ASSERT_HRESULT_FAILED(host.remote_object_.QueryInterface(&executor));
+}
+
+TEST_F(ExecutorComTest, MarshalingFailsWithNoLocalProxy) {
+ RemoteObjectHost host;
+ ASSERT_NO_FATAL_FAILURE(host.HostObject(executor_keeper_));
+
+ std::vector<DWORD> invoker_cookies;
+ host.RunSync(NewRunnableFunction(RegisterProxyStubs, &invoker_cookies));
+
+ // We expect failure, unmarshaling shouldn't be possible
+ // without the proxies registered in our (source) thread.
+ CComPtr<ICeeeTabExecutor> executor;
+ ASSERT_HRESULT_FAILED(host.remote_object_.QueryInterface(&executor));
+}
+
+TEST_F(ExecutorComTest, MarshalingSucceedsWithProxiesRegistered) {
+ RemoteObjectHost host;
+ ASSERT_NO_FATAL_FAILURE(host.HostObject(executor_keeper_));
+
+ std::vector<DWORD> invoker_cookies;
+ host.RunSync(NewRunnableFunction(RegisterProxyStubs, &invoker_cookies));
+
+ // Register proxies in our thread.
+ RegisterProxies();
+
+ // We expect success as both home and away proxies
+ // are now registered.
+ CComPtr<ICeeeTabExecutor> executor;
+ ASSERT_HRESULT_SUCCEEDED(host.remote_object_.QueryInterface(&executor));
+}
+
+TEST_F(ExecutorComTest, AllProxiesAreRegistered) {
+ // Create the mock executor.
+ CComPtr<IUnknown> mock_keeper;
+ MockCeeeExecutor* mock_executor = NULL;
+ ASSERT_HRESULT_SUCCEEDED(
+ MockCeeeExecutor::CreateInitialized(&mock_executor, &mock_keeper));
+
+ RemoteObjectHost host;
+ ASSERT_NO_FATAL_FAILURE(host.HostObject(mock_keeper));
+
+ // Register proxies on both sides.
+ std::vector<DWORD> invoker_cookies;
+ host.RunSync(NewRunnableFunction(RegisterProxyStubs, &invoker_cookies));
+ RegisterProxies();
+
+ CComPtr<ICeeeWindowExecutor> window_executor;
+ EXPECT_HRESULT_SUCCEEDED(
+ host.remote_object_.QueryInterface(&window_executor));
+
+ CComPtr<ICeeeTabExecutor> tab_executor;
+ EXPECT_HRESULT_SUCCEEDED(
+ host.remote_object_.QueryInterface(&tab_executor));
+
+ CComPtr<ICeeeCookieExecutor> cookie_executor;
+ EXPECT_HRESULT_SUCCEEDED(
+ host.remote_object_.QueryInterface(&cookie_executor));
+
+ CComPtr<ICeeeInfobarExecutor> infobar_executor;
+ EXPECT_HRESULT_SUCCEEDED(
+ host.remote_object_.QueryInterface(&infobar_executor));
+}
+
+TEST_F(ExecutorComTest, UnregisterProxiesWorks) {
+ RemoteObjectHost host;
+ ASSERT_NO_FATAL_FAILURE(host.HostObject(executor_keeper_));
+
+ std::vector<DWORD> invoker_cookies;
+ host.RunSync(NewRunnableFunction(RegisterProxyStubs, &invoker_cookies));
+
+ // Register proxies in our thread.
+ RegisterProxies();
+
+ // We expect success as both home and away proxies
+ // are now registered.
+ CComPtr<ICeeeTabExecutor> executor;
+ ASSERT_HRESULT_SUCCEEDED(host.remote_object_.QueryInterface(&executor));
+ executor.Release();
+
+ // Now unregister the proxies on our side and try again.
+ // We need to release the local proxy, as it'll cache any interfaces
+ // we've already queried.
+ UnregisterProxies();
+ host.remote_object_.Release();
+ ASSERT_HRESULT_SUCCEEDED(host.remote_git_ptr_.CopyTo(&host.remote_object_));
+ ASSERT_HRESULT_FAILED(host.remote_object_.QueryInterface(&executor));
+}
+
+TEST_F(ExecutorComTest, ImplementsCallFactory) {
+ CComPtr<ICallFactory> call_factory;
+ ASSERT_HRESULT_SUCCEEDED(executor_keeper_->QueryInterface(&call_factory));
+ ASSERT_TRUE(call_factory != NULL);
+}
+
+TEST_F(ExecutorComTest, SyncInitialize) {
+ CComPtr<AsyncICeeeTabExecutor> executor_call;
+ ASSERT_NO_FATAL_FAILURE(CreateTabExecutor(&executor_call));
+
+ static const CeeeWindowHandle kWindow = 0xF1F2F3F4;
+ // We expect this to run synchronously at begin time.
+ EXPECT_CALL(*synchronize_, Signal());
+ EXPECT_CALL(*executor_, Initialize(kWindow))
+ .WillOnce(Return(E_CAFEBABE));
+
+ ASSERT_HRESULT_SUCCEEDED(executor_call->Begin_Initialize(kWindow));
+
+ // Verify that we return the exepected HRESULT.
+ ASSERT_EQ(E_CAFEBABE, executor_call->Finish_Initialize());
+}
+
+TEST_F(ExecutorComTest, AsyncGetTabInfo) {
+ CComPtr<AsyncICeeeTabExecutor> executor_call;
+ ASSERT_NO_FATAL_FAILURE(CreateTabExecutor(&executor_call));
+ ASSERT_HRESULT_SUCCEEDED(executor_call->Begin_GetTabInfo());
+
+ ASSERT_NO_FATAL_FAILURE(RunShortMessageLoop());
+
+ // We expect the call after the message loop, as there's an out
+ // param we have to cater to.
+ CeeeTabInfo tab_info = {};
+ EXPECT_CALL(*executor_, GetTabInfo(&tab_info))
+ .WillOnce(Return(E_CAFEBABE));
+
+ // Verify that we return the expected HRESULT.
+ ASSERT_EQ(E_CAFEBABE, executor_call->Finish_GetTabInfo(&tab_info));
+}
+
+TEST_F(ExecutorComTest, AsyncNavigate) {
+ CComPtr<AsyncICeeeTabExecutor> executor_call;
+ ASSERT_NO_FATAL_FAILURE(CreateTabExecutor(&executor_call));
+
+ ASSERT_HRESULT_SUCCEEDED(executor_call->Begin_Navigate(
+ CComBSTR(kUrl), kFlags, CComBSTR(kTarget)));
+
+ // We expect the call to from within the message loop.
+ EXPECT_CALL(*executor_, Navigate(StrEq(kUrl), kFlags, StrEq(kTarget)))
+ .WillOnce(Return(E_CAFEBABE));
+
+ ASSERT_NO_FATAL_FAILURE(RunShortMessageLoop());
+
+ // Verify that we return the exepected HRESULT.
+ ASSERT_EQ(E_CAFEBABE, executor_call->Finish_Navigate());
+}
+
+TEST_F(ExecutorComTest, AsyncInsertCode) {
+ CComPtr<AsyncICeeeTabExecutor> executor_call;
+ ASSERT_NO_FATAL_FAILURE(CreateTabExecutor(&executor_call));
+ const wchar_t* kCode = L"window.alert(\"You've been had.\")";
+ const wchar_t* kFile = L"CONSOLE";
+ ASSERT_HRESULT_SUCCEEDED(executor_call->Begin_InsertCode(
+ CComBSTR(kCode), CComBSTR(kFile), TRUE, kCeeeTabCodeTypeJs));
+
+ // Now set up the expectations we have for invocations
+ // during the brief message loop.
+ EXPECT_CALL(*executor_,
+ InsertCode(StrEq(kCode), StrEq(kFile), TRUE, kCeeeTabCodeTypeJs))
+ .WillOnce(Return(E_CAFEBABE));
+
+ ASSERT_NO_FATAL_FAILURE(RunShortMessageLoop());
+
+ // Verify that we return the expected HRESULT.
+ ASSERT_EQ(E_CAFEBABE, executor_call->Finish_InsertCode());
+}
+
+// This unittest fails due to a bug in COM, whereby asynchronous calls across
+// apartments in the same process cause an access violation on freeing an
+// invalid pointer.
+// This is true as of 15 Nov 2010, leaving this around in hopes Microsoft
+// comes through with a fix.
+TEST_F(ExecutorComTest, DISABLED_CrossApartmentCall) {
+ RemoteObjectHost host;
+ ASSERT_NO_FATAL_FAILURE(host.HostObject(executor_keeper_));
+
+ // Register home and away proxies.
+ std::vector<DWORD> invoker_cookies;
+ host.RunSync(NewRunnableFunction(RegisterProxyStubs, &invoker_cookies));
+ RegisterProxies();
+
+ CComPtr<ICeeeTabExecutor> executor;
+ ASSERT_HRESULT_SUCCEEDED(host.remote_object_.QueryInterface(&executor));
+
+ // A navigation call should go through with no trouble.
+ EXPECT_CALL(*executor_, Navigate(StrEq(kUrl), 0, StrEq(kTarget)))
+ .WillOnce(Return(S_OK));
+
+ EXPECT_HRESULT_SUCCEEDED(
+ executor->Navigate(CComBSTR(kUrl), 0, CComBSTR(kTarget)));
+}
+
+class TestClassFactory
+ : public CComObjectRootEx<CComSingleThreadModel>,
+ public InitializingCoClass<TestClassFactory>,
+ public IClassFactory {
+ public:
+ BEGIN_COM_MAP(TestClassFactory)
+ COM_INTERFACE_ENTRY(IClassFactory)
+ END_COM_MAP()
+
+ TestClassFactory() : executor_(NULL) {
+ }
+
+ HRESULT Initialize(TestCeeeExecutor* executor) {
+ executor_ = executor;
+ return S_OK;
+ }
+
+ STDMETHOD(CreateInstance)(IUnknown *outer, REFIID riid, void **out) {
+ if (outer != NULL) {
+ ADD_FAILURE() << "Attempted aggregation in CreateInstance";
+ return CLASS_E_NOAGGREGATION;
+ }
+
+ return executor_->QueryInterface(riid, out);
+ }
+
+ STDMETHOD(LockServer)(BOOL lock) {
+ ADD_FAILURE() << "Unexpected call to LockServer";
+ return E_NOTIMPL;
+ }
+
+ TestCeeeExecutor* executor_;
+};
+
+const wchar_t* kHKCUReplacement =
+ L"Software\\Google\\ExecutorComInvocationTest\\HKCU";
+
+// Registry script to register iid->async iid mapping.
+const wchar_t* kRegScript =
+ L"%ROOT% {\n"
+ L" Software {\n"
+ L" Classes {\n"
+ L" NoRemove 'Interface' {\n"
+ L" '%IID%' = s '%NAME%' {\n"
+ L" AsynchronousInterface = s '%ASYNC_IID%'\n"
+ L" }\n"
+ L" }\n"
+ L" }\n"
+ L" }\n"
+ L"}\n";
+
+
+
+// This fixture assists in launching a sub-process to invoke on our executor.
+// We can't do this from another thread in this process because of a bug
+// in the COM marshaling machinery that causes an access violation after
+// an asynchronous invocation across single-threaded apartments in the
+// same process.
+class ExecutorComInvocationTest: public ExecutorComTest {
+ public:
+ // We register in HKCU if the user is non-admin, otherwise in
+ // HKLM, because on UAC-enabled systems, HKCU is ignored for
+ // administrative users.
+ ExecutorComInvocationTest()
+ : root_key_(::IsUserAnAdmin() ? L"HKLM" : L"HKCU"),
+ clsid_(GUID_NULL),
+ cookie_(0),
+ invoker_(base::kNullProcessHandle) {
+ }
+
+ void UpdateRegistry(bool reg) {
+ // Register or unregister the sync->async IID mapping in our HKCR.
+ // Unfortunately there's no way to do this without changing machine-global
+ // state. The COM marshaling machinery does lookups in HKCR, which seems
+ // to be amalgamated from HKCU and HKLM in kernel-land, so
+ // RegOverridePredefKey doesn't help.
+ CRegObject reg_obj;
+ ASSERT_HRESULT_SUCCEEDED(reg_obj.FinalConstruct());
+ ASSERT_HRESULT_SUCCEEDED(
+ reg_obj.AddReplacement(L"IID", CComBSTR(IID_ICeeeTabExecutor)));
+ ASSERT_HRESULT_SUCCEEDED(
+ reg_obj.AddReplacement(L"ASYNC_IID",
+ CComBSTR(IID_AsyncICeeeTabExecutor)));
+ ASSERT_HRESULT_SUCCEEDED(
+ reg_obj.AddReplacement(L"NAME", L"ICeeeTabExecutor"));
+ ASSERT_HRESULT_SUCCEEDED(
+ reg_obj.AddReplacement(L"ROOT", root_key_));
+
+ if (reg) {
+ ASSERT_HRESULT_SUCCEEDED(reg_obj.StringRegister(kRegScript));
+ } else {
+ ASSERT_HRESULT_SUCCEEDED(reg_obj.StringUnregister(kRegScript));
+ }
+ }
+
+ virtual void SetUp() {
+ ASSERT_NO_FATAL_FAILURE(ExecutorComTest::SetUp());
+ ASSERT_NO_FATAL_FAILURE(UpdateRegistry(true));
+ ASSERT_NO_FATAL_FAILURE(RegisterProxies());
+
+ // Register a class factory with a new clsid.
+ ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&clsid_));
+ ASSERT_HRESULT_SUCCEEDED(
+ TestClassFactory::CreateInitialized(executor_, &class_factory_));
+ ASSERT_HRESULT_SUCCEEDED(::CoRegisterClassObject(clsid_,
+ class_factory_,
+ CLSCTX_LOCAL_SERVER,
+ REGCLS_MULTIPLEUSE,
+ &cookie_));
+ }
+
+ virtual void TearDown() {
+ // Wait for the invoker and assert it returned E_CAFEBABE.
+ if (invoker_ != base::kNullProcessHandle) {
+ int exit_code = -1;
+ EXPECT_TRUE(base::WaitForExitCode(invoker_, &exit_code));
+ EXPECT_EQ(E_CAFEBABE, exit_code);
+ }
+
+ EXPECT_HRESULT_SUCCEEDED(::CoRevokeClassObject(cookie_));
+ EXPECT_NO_FATAL_FAILURE(UpdateRegistry(false));
+ ExecutorComTest::TearDown();
+ }
+
+ template <size_t N>
+ void InvokeExecutor(const char* func, const char* (&args)[N]) {
+ FilePath testing_invoke_executor;
+ ASSERT_TRUE(PathService::Get(base::DIR_EXE, &testing_invoke_executor));
+ testing_invoke_executor =
+ testing_invoke_executor.Append(L"testing_invoke_executor.exe");
+
+ wchar_t clsid_str[40] = {};
+ ASSERT_NE(0, ::StringFromGUID2(clsid_, clsid_str, arraysize(clsid_str)));
+ CommandLine cmd_line(testing_invoke_executor);
+ cmd_line.AppendSwitchNative("class_id", clsid_str);
+ cmd_line.AppendSwitchASCII("func", func);
+
+ ASSERT_TRUE(N % 2 == 0);
+ for (size_t i = 0; i < N; i += 2) {
+ cmd_line.AppendSwitchASCII(args[i], args[i + 1]);
+ }
+
+ ASSERT_TRUE(base::LaunchApp(cmd_line, false, true, &invoker_));
+ }
+
+ protected:
+ GUID clsid_;
+ DWORD cookie_;
+ CComPtr<IClassFactory> class_factory_;
+ const wchar_t* root_key_;
+ base::ProcessHandle invoker_;
+};
+
+
+TEST_F(ExecutorComInvocationTest, SyncInitialize) {
+ const char* args[] = {
+ "hwnd", "1457",
+ };
+ ASSERT_NO_FATAL_FAILURE(InvokeExecutor("Initialize", args));
+
+ {
+ InSequence sequence;
+
+ // We expect the invocation to do a create tab call,
+ // followed by a navigate.
+ ExpectCreateTabCall(executor_);
+ EXPECT_CALL(*executor_, Initialize(static_cast<CeeeWindowHandle>(1457)))
+ .WillOnce(
+ DoAll(
+ QuitMessageLoop(&loop_),
+ Return(E_CAFEBABE)));
+ }
+
+ loop_.Run();
+}
+
+TEST_F(ExecutorComInvocationTest, AsyncGetTabInfo) {
+ const char* args[] = {
+ "ignore", "me",
+ };
+ ASSERT_NO_FATAL_FAILURE(InvokeExecutor("GetTabInfo", args));
+
+ {
+ InSequence sequence;
+
+ // We expect the invocation to do a create tab call,
+ // followed by a navigate.
+ ExpectCreateTabCall(executor_);
+ EXPECT_CALL(*executor_, GetTabInfo(NotNull()))
+ .WillOnce(
+ DoAll(
+ QuitMessageLoop(&loop_),
+ Return(E_CAFEBABE)));
+ }
+
+ loop_.Run();
+}
+
+TEST_F(ExecutorComInvocationTest, AsyncNavigate) {
+ const char* args[] = {
+ "url", "http://www.google.com/",
+ "flags", "74565",
+ "target", "_blank",
+ };
+ ASSERT_NO_FATAL_FAILURE(InvokeExecutor("Navigate", args));
+
+ {
+ InSequence sequence;
+
+ // We expect the invocation to do a create tab call,
+ // followed by a navigate.
+ ExpectCreateTabCall(executor_);
+ EXPECT_CALL(*executor_, Navigate(StrEq(L"http://www.google.com/"),
+ kFlags,
+ StrEq(kTarget)))
+ .WillOnce(
+ DoAll(
+ QuitMessageLoop(&loop_),
+ Return(E_CAFEBABE)));
+ }
+
+ loop_.Run();
+}
+
+TEST_F(ExecutorComInvocationTest, AsyncInsertCode) {
+ const char* args[] = {
+ "code", "alert('you\'ve been had')",
+ "file", "CONSOLE",
+ "all_frames", "true",
+ "type", "1", // kCeeeTabCodeTypeJs
+ };
+ ASSERT_NO_FATAL_FAILURE(InvokeExecutor("InsertCode", args));
+
+ {
+ InSequence sequence;
+
+ // We expect the invocation to do a create tab call,
+ // followed by a navigate.
+ ExpectCreateTabCall(executor_);
+ EXPECT_CALL(*executor_, InsertCode(StrEq(L"alert('you\'ve been had')"),
+ StrEq(L"CONSOLE"),
+ TRUE,
+ kCeeeTabCodeTypeJs))
+ .WillOnce(
+ DoAll(
+ QuitMessageLoop(&loop_),
+ Return(E_CAFEBABE)));
+ }
+
+ loop_.Run();
+}
+
+} // namespace
diff --git a/ceee/ie/plugin/bho/testing_invoke_executor.cc b/ceee/ie/plugin/bho/testing_invoke_executor.cc
new file mode 100644
index 0000000..f56de47
--- /dev/null
+++ b/ceee/ie/plugin/bho/testing_invoke_executor.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2010 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.
+//
+// Implements a command-line executable that assists in testing the executor
+// by invoking on it from across a process boundary.
+#include <atlbase.h>
+#include <string>
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "ceee/ie/plugin/toolband/toolband_proxy.h"
+
+#include "toolband.h" // NOLINT
+
+HRESULT DoInitialize(ICeeeTabExecutor* executor, CommandLine* cmd_line) {
+ CeeeWindowHandle hwnd = static_cast<CeeeWindowHandle>(
+ atoi(cmd_line->GetSwitchValueASCII("hwnd").c_str()));
+
+ return executor->Initialize(hwnd);
+}
+
+HRESULT DoGetTabInfo(ICeeeTabExecutor* executor, CommandLine* cmd_line) {
+ CeeeTabInfo info = {};
+ return executor->GetTabInfo(&info);
+}
+
+HRESULT DoNavigate(ICeeeTabExecutor* executor, CommandLine* cmd_line) {
+ std::wstring url = cmd_line->GetSwitchValueNative("url");
+ long flags = atoi(cmd_line->GetSwitchValueASCII("flags").c_str());
+ std::wstring target = cmd_line->GetSwitchValueNative("target");
+
+ return executor->Navigate(CComBSTR(url.c_str()),
+ flags,
+ CComBSTR(target.c_str()));
+}
+
+HRESULT DoInsertCode(ICeeeTabExecutor* executor, CommandLine* cmd_line) {
+ std::wstring code = cmd_line->GetSwitchValueNative("code");
+ std::wstring file = cmd_line->GetSwitchValueNative("file");
+ bool all_frames = cmd_line->GetSwitchValueASCII("all_frames") == "true";
+ CeeeTabCodeType type = static_cast<CeeeTabCodeType>(
+ atoi(cmd_line->GetSwitchValueASCII("type").c_str()));
+
+ return executor->InsertCode(CComBSTR(code.c_str()),
+ CComBSTR(file.c_str()),
+ all_frames,
+ type);
+}
+
+int main(int argc, char** argv) {
+ ::CoInitialize(NULL);
+
+ base::AtExitManager at_exit;
+ CommandLine::Init(argc, argv);
+
+ CommandLine* cmd_line = CommandLine::ForCurrentProcess();
+ std::wstring clsid = cmd_line->GetSwitchValueNative("class_id");
+
+ // Register the proxy/stubs for the exector interfaces.
+ RegisterProxyStubs(NULL);
+
+ CLSID executor_clsid = {};
+ HRESULT hr = ::CLSIDFromString(clsid.c_str(), &executor_clsid);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to read class_id argument";
+ exit(hr);
+ }
+
+ CComPtr<ICeeeTabExecutor> executor;
+ hr = executor.CoCreateInstance(executor_clsid);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to create tab executor.";
+ exit(hr);
+ }
+
+ std::string func = cmd_line->GetSwitchValueASCII("func");
+ if (func == "Initialize") {
+ hr = DoInitialize(executor, cmd_line);
+ } else if (func == "GetTabInfo") {
+ hr = DoGetTabInfo(executor, cmd_line);
+ } else if (func == "Navigate") {
+ hr = DoNavigate(executor, cmd_line);
+ } else if (func == "InsertCode") {
+ hr = DoInsertCode(executor, cmd_line);
+ } else {
+ hr = E_UNEXPECTED;
+ }
+
+ // We exit, because we don't want to trust the
+ // server to be around for teardown and goodbyes.
+ exit(hr);
+
+ NOTREACHED();
+ return hr;
+}
diff --git a/ceee/ie/plugin/toolband/resource.h b/ceee/ie/plugin/toolband/resource.h
index d090e40..1d27001 100644
--- a/ceee/ie/plugin/toolband/resource.h
+++ b/ceee/ie/plugin/toolband/resource.h
@@ -18,7 +18,7 @@
#define IDR_GREASEMONKEY_API_JS 105
#define IDR_EXECUTOR 106
#define IDR_EXECUTOR_CREATOR 107
-#define IDR_NO_EXTENSION 108
+#define IDR_TOOLBAND_PROXY 108
// Next default values for new objects
//
diff --git a/ceee/ie/plugin/toolband/toolband.gyp b/ceee/ie/plugin/toolband/toolband.gyp
index 7fb5529bfc..63686f1 100644
--- a/ceee/ie/plugin/toolband/toolband.gyp
+++ b/ceee/ie/plugin/toolband/toolband.gyp
@@ -41,6 +41,7 @@
'../../broker/broker.gyp:broker_rpc_lib',
'ceee_ie_lib',
'ie_toolband_common',
+ 'toolband_proxy_lib',
'toolband_idl',
'../bho/bho.gyp:bho',
'../scripting/scripting.gyp:scripting',
@@ -66,8 +67,8 @@
'../scripting/content_script_manager.rc',
],
'libraries': [
- 'oleacc.lib',
'iepmapi.lib',
+ 'oleacc.lib',
'rpcrt4.lib',
],
'include_dirs': [
@@ -120,5 +121,32 @@
'include_dirs': ['<(SHARED_INTERMEDIATE_DIR)'],
},
},
+ {
+ # This target builds a library out of the toolband proxy/stubs.
+ # TODO(siggi): Rename the IDL and move it to ie/plugin/common, as
+ # it's defining interfaces that are common across the the broker
+ # and bho/executor.
+ 'target_name': 'toolband_proxy_lib',
+ 'type': 'static_library',
+ 'sources': [
+ 'toolband_proxy.h',
+ 'toolband_proxy.cc',
+ 'toolband_proxy.rgs',
+ '<(SHARED_INTERMEDIATE_DIR)/toolband_p.c',
+ '<(SHARED_INTERMEDIATE_DIR)/toolband_dlldata.c',
+ ],
+ 'dependencies': [
+ '../../../../base/base.gyp:base',
+ 'toolband_idl',
+ ],
+ 'defines': [
+ # This define adds ToolbandProxy as a prefix to the generated
+ # proxy/stub entrypoint routine names.
+ 'ENTRY_PREFIX=ToolbandProxy',
+ # The proxy stub code defines _purecall, which conflicts with our
+ # CRT, so we neuter this here.
+ '_purecall=ToolbandPureCall',
+ ],
+ },
]
}
diff --git a/ceee/ie/plugin/toolband/toolband.idl b/ceee/ie/plugin/toolband/toolband.idl
index 4164945..f85fff9 100644
--- a/ceee/ie/plugin/toolband/toolband.idl
+++ b/ceee/ie/plugin/toolband/toolband.idl
@@ -4,6 +4,12 @@
//
// @file
// Interface and object declarations for CEEE IE toolband.
+//
+// Note: We hand-register the proxy/stubs for the marshalable interfaces
+// in this file in each COM apartment that needs to use them.
+// If you add new marshalable interfaces to this file be sure to update
+// the list of interface proxy/stubs in toolband_proxy.cc, and to
+// call RegisterProxyStubs and UnregisterProxyStubs appropriately.
import "oaidl.idl";
import "ocidl.idl";
@@ -102,11 +108,10 @@ typedef struct {
[
object,
- uuid(8DEEECC5-7B49-482d-99F8-2109EA5F2618),
+ uuid(66CDB7E2-B326-493e-B469-16234426C89B),
nonextensible,
helpstring("ICeeeWindowExecutor Interface"),
pointer_default(unique),
- oleautomation
]
// Object provided to the broker to execute code in a given window's thread.
interface ICeeeWindowExecutor : IUnknown {
@@ -176,11 +181,11 @@ interface ICeeeWindowExecutor : IUnknown {
[
object,
- uuid(C7FF41BA-72D5-4086-8B5A-2EF4FD12E0FE),
+ uuid(1F509E26-002A-461b-9083-ABD1002BD4E2),
+ async_uuid(EDF51257-5A41-4391-8186-44DB3E84FBE6),
nonextensible,
helpstring("ICeeeTabExecutor Interface"),
pointer_default(unique),
- oleautomation
]
// Object provided to the broker to execute code in a given window's thread.
interface ICeeeTabExecutor : IUnknown {
@@ -228,11 +233,10 @@ interface ICeeeTabExecutor : IUnknown {
[
object,
- uuid(07630967-D7FB-4745-992F-28614930D9A3),
+ uuid(875DB992-6ADA-4330-AD3F-28B3E3B9DB01),
nonextensible,
helpstring("ICeeeCookieExecutor Interface"),
pointer_default(unique),
- oleautomation
]
// Object provided to the broker to execute code in a given window's thread.
interface ICeeeCookieExecutor : IUnknown {
@@ -267,11 +271,10 @@ interface ICeeeCookieExecutor : IUnknown {
[
object,
- uuid(276D47E8-1692-4a21-907D-948D170E4330),
+ uuid(C79A1479-33F6-419f-970F-2FB3D1388922),
nonextensible,
helpstring("ICeeeInfobarExecutor Interface"),
pointer_default(unique),
- oleautomation
]
// Object provided to the broker to execute code in a given window's thread.
interface ICeeeInfobarExecutor : IUnknown {
diff --git a/ceee/ie/plugin/toolband/toolband.rc b/ceee/ie/plugin/toolband/toolband.rc
index 2f8c84a..d31f5b2 100755
--- a/ceee/ie/plugin/toolband/toolband.rc
+++ b/ceee/ie/plugin/toolband/toolband.rc
@@ -82,6 +82,7 @@ IDR_BROWSERHELPEROBJECT REGISTRY "ceee\\ie\\plugin\\bho\\browser_
IDR_EXECUTOR REGISTRY "ceee\\ie\\plugin\\bho\\executor.rgs"
IDR_EXECUTOR_CREATOR REGISTRY "ceee\\ie\\plugin\\bho\\executor_creator.rgs"
IDR_TOOL_BAND REGISTRY "tool_band.rgs"
+IDR_TOOLBAND_PROXY REGISTRY "toolband_proxy.rgs"
/////////////////////////////////////////////////////////////////////////////
//
diff --git a/ceee/ie/plugin/toolband/toolband_module.cc b/ceee/ie/plugin/toolband/toolband_module.cc
index 472bd30..6824542 100644
--- a/ceee/ie/plugin/toolband/toolband_module.cc
+++ b/ceee/ie/plugin/toolband/toolband_module.cc
@@ -16,6 +16,7 @@
#include "ceee/ie/plugin/bho/executor.h"
#include "ceee/ie/plugin/toolband/toolband_module_reporting.h"
#include "ceee/ie/plugin/toolband/tool_band.h"
+#include "ceee/ie/plugin/toolband/toolband_proxy.h"
#include "ceee/ie/plugin/scripting/script_host.h"
#include "ceee/common/windows_constants.h"
#include "chrome/common/url_constants.h"
@@ -45,6 +46,8 @@ OBJECT_ENTRY_AUTO(CLSID_CeeeExecutor, CeeeExecutor)
class ToolbandModule : public CAtlDllModuleT<ToolbandModule> {
public:
+ typedef CAtlDllModuleT<ToolbandModule> Super;
+
ToolbandModule();
~ToolbandModule();
@@ -59,8 +62,11 @@ class ToolbandModule : public CAtlDllModuleT<ToolbandModule> {
return module_initialized_;
}
- private:
+ // We override reg/unregserver to register proxy/stubs.
+ HRESULT DllRegisterServer();
+ HRESULT DllUnregisterServer();
+ private:
base::AtExitManager at_exit_;
bool module_initialized_;
bool crash_reporting_initialized_;
@@ -108,7 +114,7 @@ ToolbandModule::~ToolbandModule() {
}
HRESULT ToolbandModule::DllCanUnloadNow() {
- HRESULT hr = CAtlDllModuleT<ToolbandModule>::DllCanUnloadNow();
+ HRESULT hr = Super::DllCanUnloadNow();
if (hr == S_OK)
Term();
return hr;
@@ -117,7 +123,26 @@ HRESULT ToolbandModule::DllCanUnloadNow() {
HRESULT ToolbandModule::DllGetClassObject(REFCLSID clsid, REFIID iid,
void** object) {
Init();
- return CAtlDllModuleT<ToolbandModule>::DllGetClassObject(clsid, iid, object);
+ return Super::DllGetClassObject(clsid, iid, object);
+}
+
+HRESULT ToolbandModule::DllRegisterServer() {
+ // No typelib registration.
+ HRESULT hr = Super::DllRegisterServer();
+ if (SUCCEEDED(hr) && !RegisterAsyncProxies(true)) {
+ hr = SELFREG_E_CLASS;
+ }
+ return hr;
+}
+
+HRESULT ToolbandModule::DllUnregisterServer() {
+ // No typelib registration.
+ HRESULT hr = Super::DllUnregisterServer(FALSE);
+ if (!RegisterAsyncProxies(false) && SUCCEEDED(hr)) {
+ // Note the error.
+ hr = SELFREG_E_CLASS;
+ }
+ return hr;
}
void ToolbandModule::Init() {
@@ -162,8 +187,7 @@ LONG ceee_module_util::UnlockModule() {
return module.Unlock();
}
-
-// DLL Entry Point
+// DLL Entry Point.
extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason,
LPVOID reserved) {
// Prevent us from being loaded by older versions of the shell.
@@ -196,7 +220,7 @@ STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
// function, which keeps us safe from ever forgetting to check for
// the --enable-ceee flag.
STDAPI DllRegisterServerImpl(void) {
- // registers object, typelib and all interfaces in typelib
+ // Registers objects.
HRESULT hr = module.DllRegisterServer();
return hr;
}
diff --git a/ceee/ie/plugin/toolband/toolband_proxy.cc b/ceee/ie/plugin/toolband/toolband_proxy.cc
new file mode 100644
index 0000000..7c1d2f1
--- /dev/null
+++ b/ceee/ie/plugin/toolband/toolband_proxy.cc
@@ -0,0 +1,150 @@
+// Copyright (c) 2010 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 "ceee/ie/plugin/toolband/toolband_proxy.h"
+
+#include <atlbase.h>
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/win/registry.h"
+#include "ceee/common/com_utils.h"
+#include "ceee/ie/plugin/toolband/resource.h"
+
+#include "toolband.h" // NOLINT
+
+extern "C" {
+// Declare the entrypoint function(s) generated by MIDL for our proxy/stubs.
+HRESULT STDAPICALLTYPE ToolbandProxyDllGetClassObject(REFCLSID clsid,
+ REFIID iid,
+ void** ppv);
+} // extern "C"
+
+namespace {
+
+struct InterfaceInfo {
+ const wchar_t* name;
+ const IID* iid;
+ const IID* async_iid;
+};
+
+#define DECLARE_SYNC_INTERFACE(itf) \
+ { L#itf, &IID_##itf, NULL },
+#define DECLARE_ASYNC_INTERFACE(itf) \
+ { L#itf, &IID_##itf, &IID_Async##itf }, \
+ DECLARE_SYNC_INTERFACE(Async##itf)
+
+// If you add new executor interfaces to toolband.idl, make sure to add
+// their IIDs here, or you will not be able to marshal them.
+const InterfaceInfo kInterfaceInfo[] = {
+ DECLARE_SYNC_INTERFACE(ICeeeWindowExecutor)
+ DECLARE_ASYNC_INTERFACE(ICeeeTabExecutor)
+ DECLARE_SYNC_INTERFACE(ICeeeCookieExecutor)
+ DECLARE_SYNC_INTERFACE(ICeeeInfobarExecutor)
+};
+
+#ifndef NDEBUG
+void CheckAsyncIidRegistered(const IID& iid, const IID& async_iid) {
+ std::wstring iid_str;
+ bool success = com::GuidToString(iid, &iid_str);
+ DCHECK(success);
+ std::wstring key_name = base::StringPrintf(
+ L"Interface\\%ls\\AsynchronousInterface", iid_str);
+
+ base::win::RegKey key;
+ if (key.Open(HKEY_CLASSES_ROOT, key_name.c_str(), KEY_READ)) {
+ // It's registered, the rest of this block is debug checking that
+ // the correct IID is indeed registered for the async interface.
+ std::wstring async_iid_str;
+ DCHECK(key.ReadValue(NULL, &async_iid_str));
+ IID read_async_iid;
+ DCHECK(SUCCEEDED(::IIDFromString(async_iid_str.c_str(), &read_async_iid)));
+ DCHECK(read_async_iid == async_iid);
+ } else {
+ LOG(WARNING) << "Sync->Async IID not registered. Key=" << key_name;
+ }
+}
+#endif // NDEBUG
+
+} // namespace
+
+bool RegisterProxyStubs(std::vector<DWORD>* cookies) {
+ bool succeeded = true;
+
+ for (size_t i = 0; i < arraysize(kInterfaceInfo); ++i) {
+ CComPtr<IUnknown> factory;
+ const InterfaceInfo& info = kInterfaceInfo[i];
+ HRESULT hr = ToolbandProxyDllGetClassObject(*info.iid, IID_IUnknown,
+ reinterpret_cast<void**>(&factory));
+
+ DWORD cookie = 0;
+ if (SUCCEEDED(hr)) {
+ hr = ::CoRegisterClassObject(*info.iid, factory, CLSCTX_INPROC_SERVER,
+ REGCLS_MULTIPLEUSE, &cookie);
+ }
+ if (SUCCEEDED(hr)) {
+ // Proxy/stubs have their own IID as class id.
+ hr = ::CoRegisterPSClsid(*info.iid, *info.iid);
+ if (SUCCEEDED(hr) && cookies != NULL) {
+ cookies->push_back(cookie);
+ }
+ }
+
+#ifndef NDEBUG
+ // If there's a corresponding Async interface, check whether
+ // it's registered. This is a debugging aid only.
+ if (info.async_iid != NULL)
+ CheckAsyncIidRegistered(*info.iid, *info.async_iid);
+#endif // NDEBUG
+
+ if (FAILED(hr)) {
+ succeeded = false;
+ LOG(ERROR) << "Failed to register proxy " << com::LogHr(hr);
+ }
+ }
+
+ VLOG(1) << "Registered toolband proxy/stubs in thread "
+ << ::GetCurrentThreadId();
+
+ return succeeded;
+}
+
+void UnregisterProxyStubs(const std::vector<DWORD>& cookies) {
+ for (size_t i = 0; i < cookies.size(); ++i) {
+ HRESULT hr = ::CoRevokeClassObject(cookies[i]);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to revoke class object " << com::LogHr(hr);
+ }
+ }
+}
+
+bool RegisterAsyncProxies(bool reg) {
+ for (size_t i = 0; i < arraysize(kInterfaceInfo); ++i) {
+ // Register the iid->async iid mapping for async interfaces.
+ if (kInterfaceInfo[i].async_iid != NULL) {
+ const InterfaceInfo& info = kInterfaceInfo[i];
+ std::wstring iid_str;
+ bool success = com::GuidToString(*info.iid, &iid_str);
+ DCHECK(success) << "Failed to stringify GUID";
+ std::wstring async_iid_str;
+ success = com::GuidToString(*info.async_iid, &async_iid_str);
+ DCHECK(success) << "Failed to stringify GUID";
+
+ _ATL_REGMAP_ENTRY entries[] = {
+ { L"IID", iid_str.c_str() },
+ { L"ASYNC_IID", async_iid_str.c_str() },
+ { L"NAME", info.name },
+ { NULL, NULL },
+ };
+
+ HRESULT hr = _pAtlModule->UpdateRegistryFromResource(
+ MAKEINTRESOURCE(IDR_TOOLBAND_PROXY), reg, entries);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to register async interface for " <<
+ info.name << com::LogHr(hr);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/ceee/ie/plugin/toolband/toolband_proxy.h b/ceee/ie/plugin/toolband/toolband_proxy.h
new file mode 100644
index 0000000..bbc2314
--- /dev/null
+++ b/ceee/ie/plugin/toolband/toolband_proxy.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2010 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 CEEE_IE_PLUGIN_TOOLBAND_TOOLBAND_PROXY_H_
+#define CEEE_IE_PLUGIN_TOOLBAND_TOOLBAND_PROXY_H_
+
+#include <windows.h>
+#include <vector>
+
+// Registers all proxy/stubs in the current apartment.
+// @param cookies if not NULL, then on return contains the registration
+// cookies for all successful proxy/stub class object registrations.
+// @returns true on success, false on failure.
+// @note this function logs errors for all failures.
+// @note this function will also create registry entries for the sync/async
+// interface mapping for interfaces that have asynchronous variants.
+bool RegisterProxyStubs(std::vector<DWORD>* cookies);
+
+// Unregisters proxy/stub class objects from this apartment.
+// @param cookies the cookies returned from an earlier call to
+// RegisterProxyStubs in this apartment.
+void UnregisterProxyStubs(const std::vector<DWORD>& cookies);
+
+// Registers or unregisters the sync to async IID mapping in registry
+// for any asynchronous interfaces we implement.
+// @param reg true to register, false to unregister.
+bool RegisterAsyncProxies(bool reg);
+
+#endif // CEEE_IE_PLUGIN_TOOLBAND_TOOLBAND_PROXY_H_
diff --git a/ceee/ie/plugin/toolband/toolband_proxy.rgs b/ceee/ie/plugin/toolband/toolband_proxy.rgs
new file mode 100644
index 0000000..e6bbf04
--- /dev/null
+++ b/ceee/ie/plugin/toolband/toolband_proxy.rgs
@@ -0,0 +1,11 @@
+HKLM {
+ NoRemove Software {
+ NoRemove Classes {
+ NoRemove 'Interface' {
+ '%IID%' = s '%NAME%' {
+ AsynchronousInterface = s '%ASYNC_IID%'
+ }
+ }
+ }
+ }
+}