diff options
-rw-r--r-- | chrome_frame/chrome_frame_activex.cc | 23 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_activex.h | 5 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_activex_base.h | 72 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_automation.cc | 87 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_automation.h | 9 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_delegate.h | 9 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_npapi.cc | 85 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_npapi.h | 10 | ||||
-rw-r--r-- | chrome_frame/chrome_launcher.cc | 1 | ||||
-rw-r--r-- | chrome_frame/chrome_launcher_unittest.cc | 97 | ||||
-rw-r--r-- | chrome_frame/chrome_tab.idl | 17 | ||||
-rw-r--r-- | chrome_frame/test/data/privileged_apis_host.html | 50 |
12 files changed, 402 insertions, 63 deletions
diff --git a/chrome_frame/chrome_frame_activex.cc b/chrome_frame/chrome_frame_activex.cc index 9999fb7..78b09fa 100644 --- a/chrome_frame/chrome_frame_activex.cc +++ b/chrome_frame/chrome_frame_activex.cc @@ -43,6 +43,7 @@ ChromeFrameActivex::~ChromeFrameActivex() { DCHECK(onloaderror_.size() == 0); DCHECK(onload_.size() == 0); DCHECK(onreadystatechanged_.size() == 0); + DCHECK(onextensionready_.size() == 0); } LRESULT ChromeFrameActivex::OnCreate(UINT message, WPARAM wparam, LPARAM lparam, @@ -147,6 +148,14 @@ void ChromeFrameActivex::OnAutomationServerLaunchFailed( } } +void ChromeFrameActivex::OnExtensionInstalled( + const FilePath& path, + void* user_data, + AutomationMsg_ExtensionResponseValues response) { + ScopedBstr path_str(path.value().c_str()); + Fire_onextensionready(path_str, response); +} + HRESULT ChromeFrameActivex::InvokeScriptFunction(const VARIANT& script_object, const std::string& param) { ScopedVariant script_arg(UTF8ToWide(param.c_str()).c_str()); @@ -155,12 +164,23 @@ HRESULT ChromeFrameActivex::InvokeScriptFunction(const VARIANT& script_object, HRESULT ChromeFrameActivex::InvokeScriptFunction(const VARIANT& script_object, VARIANT* param) { + return InvokeScriptFunction(script_object, param, 1); +} + +HRESULT ChromeFrameActivex::InvokeScriptFunction(const VARIANT& script_object, + VARIANT* params, + int param_count) { + DCHECK(param_count >= 0); + DCHECK(params); + if (V_VT(&script_object) != VT_DISPATCH) { return S_FALSE; } CComPtr<IDispatch> script(script_object.pdispVal); - HRESULT hr = script.Invoke1(static_cast<DISPID>(DISPID_VALUE), param); + HRESULT hr = script.InvokeN(static_cast<DISPID>(DISPID_VALUE), + params, + param_count); // 0x80020101 == SCRIPT_E_REPORTED. // When the script we're invoking has an error, we get this error back. DLOG_IF(ERROR, FAILED(hr) && hr != 0x80020101) << "Failed to invoke script"; @@ -276,6 +296,7 @@ HRESULT ChromeFrameActivex::IOleObject_SetClientSite( &onloaderror_, &onload_, &onreadystatechanged_, + &onextensionready_, }; for (int i = 0; i < arraysize(handlers); ++i) diff --git a/chrome_frame/chrome_frame_activex.h b/chrome_frame/chrome_frame_activex.h index d60b14b..04169af 100644 --- a/chrome_frame/chrome_frame_activex.h +++ b/chrome_frame/chrome_frame_activex.h @@ -95,10 +95,15 @@ END_MSG_MAP() virtual void ChromeFrameActivex::OnAutomationServerLaunchFailed( AutomationLaunchResult reason, const std::string& server_version); virtual void OnLoadFailed(int error_code, const std::string& url); + virtual void OnExtensionInstalled(const FilePath& path, + void* user_data, AutomationMsg_ExtensionResponseValues response); // Helper function to execute a function on a script IDispatch interface. HRESULT InvokeScriptFunction(const VARIANT& script, const std::string& param); HRESULT InvokeScriptFunction(const VARIANT& script, VARIANT* param); + HRESULT InvokeScriptFunction(const VARIANT& script, + VARIANT* param, + int param_count); HRESULT GetContainingDocument(IHTMLDocument2** doc); HRESULT GetDocumentWindow(IHTMLWindow2** window); diff --git a/chrome_frame/chrome_frame_activex_base.h b/chrome_frame/chrome_frame_activex_base.h index b8106aa..651ecbf 100644 --- a/chrome_frame/chrome_frame_activex_base.h +++ b/chrome_frame/chrome_frame_activex_base.h @@ -115,6 +115,18 @@ class ATL_NO_VTABLE ProxyDIChromeFrameEvents args, arraysize(args)); } + + void Fire_onextensionready(BSTR path, long response) { + // Arguments in reverse order to the function declaration, because + // that's what DISPPARAMS requires. + VARIANT args[2] = { { VT_I4, }, { VT_BSTR, } }; + args[0].lVal = response; + args[1].bstrVal = path; + + FireMethodWithParams(CF_EVENT_DISPID_ONEXTENSIONREADY, + args, + arraysize(args)); + } }; extern bool g_first_launch_by_process_; @@ -749,12 +761,53 @@ END_MSG_MAP() return S_OK; } + STDMETHOD(installExtension)(BSTR crx_path) { + DCHECK(automation_client_.get()); + + if (NULL == crx_path) { + NOTREACHED(); + return E_INVALIDARG; + } + + if (!is_privileged_) { + DLOG(ERROR) << "Attempt to installExtension in non-privileged mode"; + return E_ACCESSDENIED; + } + + FilePath::StringType crx_path_str(crx_path); + FilePath crx_file_path(crx_path_str); + + automation_client_->InstallExtension(crx_file_path, NULL); + return S_OK; + } + + STDMETHOD(loadExtension)(BSTR path) { + DCHECK(automation_client_.get()); + + if (NULL == path) { + NOTREACHED(); + return E_INVALIDARG; + } + + if (!is_privileged_) { + DLOG(ERROR) << "Attempt to loadExtension in non-privileged mode"; + return E_ACCESSDENIED; + } + + FilePath::StringType path_str(path); + FilePath file_path(path_str); + + automation_client_->LoadExpandedExtension(file_path, NULL); + return S_OK; + } + // Returns the vector of event handlers for a given event (e.g. "load"). // If the event type isn't recognized, the function fills in a descriptive // error (IErrorInfo) and returns E_INVALIDARG. HRESULT GetHandlersForEvent(BSTR event_type, EventHandlers** handlers) { DCHECK(handlers != NULL); + // TODO(tommi): make these if() statements data-driven. HRESULT hr = S_OK; const wchar_t* event_type_end = event_type + ::SysStringLen(event_type); if (LowerCaseEqualsASCII(event_type, event_type_end, "message")) { @@ -767,13 +820,23 @@ END_MSG_MAP() "readystatechanged")) { *handlers = &onreadystatechanged_; } else if (LowerCaseEqualsASCII(event_type, event_type_end, - "privatemessage")) { + "privatemessage")) { // This event handler is only available in privileged mode. - if (!is_privileged_) { + if (is_privileged_) { + *handlers = &onprivatemessage_; + } else { Error("Event type 'privatemessage' is privileged"); - return E_ACCESSDENIED; + hr = E_ACCESSDENIED; + } + } else if (LowerCaseEqualsASCII(event_type, event_type_end, + "extensionready")) { + // This event handler is only available in privileged mode. + if (is_privileged_) { + *handlers = &onextensionready_; + } else { + Error("Event type 'extensionready' is privileged"); + hr = E_ACCESSDENIED; } - *handlers = &onprivatemessage_; } else { Error(StringPrintf("Event type '%ls' not found", event_type).c_str()); hr = E_INVALIDARG; @@ -896,6 +959,7 @@ END_MSG_MAP() EventHandlers onload_; EventHandlers onreadystatechanged_; EventHandlers onprivatemessage_; + EventHandlers onextensionready_; // The UrlmonUrlRequest instance instantiated for downloading the base URL. scoped_refptr<CComObject<UrlmonUrlRequest> > base_url_request_; diff --git a/chrome_frame/chrome_frame_automation.cc b/chrome_frame/chrome_frame_automation.cc index 56548de..17fb43d 100644 --- a/chrome_frame/chrome_frame_automation.cc +++ b/chrome_frame/chrome_frame_automation.cc @@ -78,6 +78,14 @@ class ChromeFrameAutomationProxyImpl::CFMsgDispatcher InvokeCallback<Tuple1<AutomationMsg_NavigationResponseValues> >(msg, origin); break; + case AutomationMsg_InstallExtension::ID: + InvokeCallback<Tuple1<AutomationMsg_ExtensionResponseValues> >(msg, + origin); + break; + case AutomationMsg_LoadExpandedExtension::ID: + InvokeCallback<Tuple1<AutomationMsg_ExtensionResponseValues> >(msg, + origin); + break; default: NOTREACHED(); } @@ -598,6 +606,85 @@ void ChromeFrameAutomationClient::FindInPage(const std::wstring& search_string, automation_server_->SendAsAsync(msg, NULL, this); } +// Class that maintains context during the async load/install extension +// operation. When done, InstallExtensionComplete is posted back to the UI +// thread so that the users of ChromeFrameAutomationClient can be notified. +class InstallExtensionContext { + public: + InstallExtensionContext(ChromeFrameAutomationClient* client, + const FilePath& crx_path, void* user_data) : client_(client), + crx_path_(crx_path), user_data_(user_data) { + } + + ~InstallExtensionContext() { + } + + void InstallExtensionComplete(AutomationMsg_ExtensionResponseValues res) { + client_->PostTask(FROM_HERE, NewRunnableMethod(client_, + &ChromeFrameAutomationClient::InstallExtensionComplete, crx_path_, + user_data_, res)); + delete this; + } + + private: + ChromeFrameAutomationClient* client_; + FilePath crx_path_; + void* user_data_; +}; + +void ChromeFrameAutomationClient::InstallExtension( + const FilePath& crx_path, + void* user_data) { + if (automation_server_ == NULL) { + InstallExtensionComplete(crx_path, + user_data, + AUTOMATION_MSG_EXTENSION_INSTALL_FAILED); + return; + } + + InstallExtensionContext* ctx = new InstallExtensionContext( + this, crx_path, user_data); + + IPC::SyncMessage* msg = + new AutomationMsg_InstallExtension(0, crx_path, NULL); + + // The context will delete itself after it is called. + automation_server_->SendAsAsync(msg, NewCallback(ctx, + &InstallExtensionContext::InstallExtensionComplete), this); +} + +void ChromeFrameAutomationClient::InstallExtensionComplete( + const FilePath& crx_path, + void* user_data, + AutomationMsg_ExtensionResponseValues res) { + DCHECK(PlatformThread::CurrentId() == ui_thread_id_); + + if (chrome_frame_delegate_) { + chrome_frame_delegate_->OnExtensionInstalled(crx_path, user_data, res); + } +} + +void ChromeFrameAutomationClient::LoadExpandedExtension( + const FilePath& path, + void* user_data) { + if (automation_server_ == NULL) { + InstallExtensionComplete(path, + user_data, + AUTOMATION_MSG_EXTENSION_INSTALL_FAILED); + return; + } + + InstallExtensionContext* ctx = new InstallExtensionContext( + this, path, user_data); + + IPC::SyncMessage* msg = + new AutomationMsg_LoadExpandedExtension(0, path, NULL); + + // The context will delete itself after it is called. + automation_server_->SendAsAsync(msg, NewCallback(ctx, + &InstallExtensionContext::InstallExtensionComplete), this); +} + void ChromeFrameAutomationClient::CreateExternalTab() { AutomationLaunchResult launch_result = AUTOMATION_SUCCESS; DCHECK(IsWindow()); diff --git a/chrome_frame/chrome_frame_automation.h b/chrome_frame/chrome_frame_automation.h index a1d6d55..e1a34d7 100644 --- a/chrome_frame/chrome_frame_automation.h +++ b/chrome_frame/chrome_frame_automation.h @@ -209,6 +209,15 @@ class ChromeFrameAutomationClient FindInPageCase match_case, bool find_next); + virtual void InstallExtension(const FilePath& crx_path, void* user_data); + + virtual void LoadExpandedExtension(const FilePath& path, void* user_data); + + virtual void InstallExtensionComplete( + const FilePath& path, + void* user_data, + AutomationMsg_ExtensionResponseValues res); + TabProxy* tab() const { return tab_.get(); } BEGIN_MSG_MAP(ChromeFrameAutomationClient) diff --git a/chrome_frame/chrome_frame_delegate.h b/chrome_frame/chrome_frame_delegate.h index a886229..e11f099 100644 --- a/chrome_frame/chrome_frame_delegate.h +++ b/chrome_frame/chrome_frame_delegate.h @@ -21,6 +21,10 @@ class ChromeFrameDelegate { virtual void OnAutomationServerReady() = 0; virtual void OnAutomationServerLaunchFailed( AutomationLaunchResult reason, const std::string& server_version) = 0; + virtual void OnExtensionInstalled( + const FilePath& path, + void* user_data, + AutomationMsg_ExtensionResponseValues response) = 0; virtual void OnMessageReceived(const IPC::Message& msg) = 0; // This remains in interface since we call it if Navigate() @@ -52,8 +56,13 @@ class ChromeFrameDelegateImpl : public ChromeFrameDelegate { virtual void OnAutomationServerReady() {} virtual void OnAutomationServerLaunchFailed( AutomationLaunchResult reason, const std::string& server_version) {} + virtual void OnExtensionInstalled( + const FilePath& path, + void* user_data, + AutomationMsg_ExtensionResponseValues response) {} virtual void OnLoadFailed(int error_code, const std::string& url) {} virtual void OnMessageReceived(const IPC::Message& msg); + static bool IsTabMessage(const IPC::Message& message, int* tab_handle); virtual bool IsValid() const { diff --git a/chrome_frame/chrome_frame_npapi.cc b/chrome_frame/chrome_frame_npapi.cc index e1c5548..6c9791f 100644 --- a/chrome_frame/chrome_frame_npapi.cc +++ b/chrome_frame/chrome_frame_npapi.cc @@ -55,11 +55,15 @@ const NPUTF8* ChromeFrameNPAPI::plugin_property_identifier_names_[] = { const NPUTF8* ChromeFrameNPAPI::plugin_method_identifier_names_[] = { "postMessage", "postPrivateMessage", + "installExtension", + "loadExtension", }; ChromeFrameNPAPI::PluginMethod ChromeFrameNPAPI::plugin_methods_[] = { &ChromeFrameNPAPI::postMessage, &ChromeFrameNPAPI::postPrivateMessage, + &ChromeFrameNPAPI::installExtension, + &ChromeFrameNPAPI::loadExtension, }; NPIdentifier @@ -1383,6 +1387,87 @@ bool ChromeFrameNPAPI::postPrivateMessage(NPObject* npobject, return true; } +bool ChromeFrameNPAPI::installExtension(NPObject* npobject, + const NPVariant* args, + uint32_t arg_count, + NPVariant* result) { + if (arg_count > 2 || !NPVARIANT_IS_STRING(args[0]) || + (arg_count == 2 && !NPVARIANT_IS_OBJECT(args[1]))) { + NOTREACHED(); + return false; + } + + if (!is_privileged_) { + DLOG(WARNING) << "installExtension invoked in non-privileged mode"; + return false; + } + + if (!automation_client_.get()) { + DLOG(WARNING) << "installExtension invoked with no automaton client"; + NOTREACHED(); + return false; + } + + const NPString& crx_path_str = args[0].value.stringValue; + std::string crx_path_a(crx_path_str.UTF8Characters, crx_path_str.UTF8Length); + FilePath::StringType crx_path_u(UTF8ToWide(crx_path_a)); + FilePath crx_path(crx_path_u); + NPObject* retained_function = npapi::RetainObject(args[1].value.objectValue); + + automation_client_->InstallExtension(crx_path, retained_function); + // The response to this command will be returned in the OnExtensionInstalled + // delegate callback function. + + return true; +} + +void ChromeFrameNPAPI::OnExtensionInstalled( + const FilePath& path, + void* user_data, + AutomationMsg_ExtensionResponseValues res) { + ScopedNpVariant result; + NPVariant param; + INT32_TO_NPVARIANT(res, param); + NPObject* func = reinterpret_cast<NPObject*>(user_data); + + InvokeDefault(func, param, &result); + npapi::ReleaseObject(func); +} + +bool ChromeFrameNPAPI::loadExtension(NPObject* npobject, + const NPVariant* args, + uint32_t arg_count, + NPVariant* result) { + if (arg_count > 2 || !NPVARIANT_IS_STRING(args[0]) || + (arg_count == 2 && !NPVARIANT_IS_OBJECT(args[1]))) { + NOTREACHED(); + return false; + } + + if (!is_privileged_) { + DLOG(WARNING) << "loadExtension invoked in non-privileged mode"; + return false; + } + + if (!automation_client_.get()) { + DLOG(WARNING) << "loadExtension invoked with no automaton client"; + NOTREACHED(); + return false; + } + + const NPString& path_str = args[0].value.stringValue; + std::string path_a(path_str.UTF8Characters, path_str.UTF8Length); + FilePath::StringType path_u(UTF8ToWide(path_a)); + FilePath path(path_u); + NPObject* retained_function = npapi::RetainObject(args[1].value.objectValue); + + automation_client_->LoadExpandedExtension(path, retained_function); + // The response to this command will be returned in the OnExtensionInstalled + // delegate callback function. + + return true; +} + void ChromeFrameNPAPI::FireEvent(const std::string& event_type, const std::string& data) { NPVariant arg; diff --git a/chrome_frame/chrome_frame_npapi.h b/chrome_frame/chrome_frame_npapi.h index 005d72f..df35ce3 100644 --- a/chrome_frame/chrome_frame_npapi.h +++ b/chrome_frame/chrome_frame_npapi.h @@ -151,6 +151,8 @@ END_MSG_MAP() virtual void OnAutomationServerReady(); virtual void OnAutomationServerLaunchFailed( AutomationLaunchResult reason, const std::string& server_version); + virtual void OnExtensionInstalled(const FilePath& path, + void* user_data, AutomationMsg_ExtensionResponseValues response); private: void SubscribeToFocusEvents(); @@ -196,6 +198,14 @@ END_MSG_MAP() bool postPrivateMessage(NPObject* npobject, const NPVariant* args, uint32_t arg_count, NPVariant* result); + // This method is only available when the control is in privileged mode. + bool installExtension(NPObject* npobject, const NPVariant* args, + uint32_t arg_count, NPVariant* result); + + // This method is only available when the control is in privileged mode. + bool loadExtension(NPObject* npobject, const NPVariant* args, + uint32_t arg_count, NPVariant* result); + // Pointers to method implementations. static PluginMethod plugin_methods_[]; diff --git a/chrome_frame/chrome_launcher.cc b/chrome_frame/chrome_launcher.cc index 3ac0cc5..d7cca9c 100644 --- a/chrome_frame/chrome_launcher.cc +++ b/chrome_frame/chrome_launcher.cc @@ -24,7 +24,6 @@ const char* kAllowedSwitches[] = { switches::kDisableMetrics, switches::kNoFirstRun, switches::kUserDataDir, - switches::kLoadExtension, switches::kEnableRendererAccessibility, switches::kNoErrorDialogs, }; diff --git a/chrome_frame/chrome_launcher_unittest.cc b/chrome_frame/chrome_launcher_unittest.cc index 268369d..ca140801 100644 --- a/chrome_frame/chrome_launcher_unittest.cc +++ b/chrome_frame/chrome_launcher_unittest.cc @@ -1,49 +1,48 @@ -// Copyright (c) 2009 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 "base/command_line.h"
-#include "base/logging.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome_frame/chrome_launcher.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-// Utility class to disable logging. Required to disable DCHECKs that some
-// of our tests would otherwise trigger.
-class LogDisabler {
- public:
- LogDisabler() {
- initial_log_level_ = logging::GetMinLogLevel();
- logging::SetMinLogLevel(logging::LOG_FATAL + 1);
- }
-
- ~LogDisabler() {
- logging::SetMinLogLevel(initial_log_level_);
- }
-
- private:
- int initial_log_level_;
-};
-
-} // namespace
-
-TEST(ChromeLauncher, SanitizeCommandLine) {
- CommandLine bad(L"dummy.exe");
- bad.AppendSwitch(switches::kDisableMetrics); // in whitelist
- bad.AppendSwitchWithValue(switches::kLoadExtension, L"foo"); // in whitelist
- bad.AppendSwitch("no-such-switch"); // does not exist
- bad.AppendSwitch(switches::kHomePage); // exists but not in whitelist
-
- LogDisabler no_dchecks;
-
- CommandLine sanitized(L"dumbo.exe");
- chrome_launcher::SanitizeCommandLine(bad, &sanitized);
- EXPECT_TRUE(sanitized.HasSwitch(switches::kDisableMetrics));
- EXPECT_TRUE(sanitized.HasSwitch(switches::kLoadExtension));
- EXPECT_FALSE(sanitized.HasSwitch("no-such-switch"));
- EXPECT_FALSE(sanitized.HasSwitch(switches::kHomePage));
-
- EXPECT_EQ(sanitized.GetSwitchValue(switches::kLoadExtension), L"foo");
-}
+// Copyright (c) 2009 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 "base/command_line.h" +#include "base/logging.h" +#include "chrome/common/chrome_switches.h" +#include "chrome_frame/chrome_launcher.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// Utility class to disable logging. Required to disable DCHECKs that some +// of our tests would otherwise trigger. +class LogDisabler { + public: + LogDisabler() { + initial_log_level_ = logging::GetMinLogLevel(); + logging::SetMinLogLevel(logging::LOG_FATAL + 1); + } + + ~LogDisabler() { + logging::SetMinLogLevel(initial_log_level_); + } + + private: + int initial_log_level_; +}; + +} // namespace + +TEST(ChromeLauncher, SanitizeCommandLine) { + CommandLine bad(L"dummy.exe"); + bad.AppendSwitch(switches::kDisableMetrics); // in whitelist + bad.AppendSwitchWithValue(switches::kLoadExtension, L"foo"); // in whitelist + bad.AppendSwitch("no-such-switch"); // does not exist + bad.AppendSwitch(switches::kHomePage); // exists but not in whitelist + + LogDisabler no_dchecks; + + CommandLine sanitized(L"dumbo.exe"); + chrome_launcher::SanitizeCommandLine(bad, &sanitized); + EXPECT_TRUE(sanitized.HasSwitch(switches::kDisableMetrics)); + EXPECT_FALSE(sanitized.HasSwitch(switches::kLoadExtension)); + EXPECT_FALSE(sanitized.HasSwitch("no-such-switch")); + EXPECT_FALSE(sanitized.HasSwitch(switches::kHomePage)); +} + diff --git a/chrome_frame/chrome_tab.idl b/chrome_frame/chrome_tab.idl index 6f8a2ac..afb364e 100644 --- a/chrome_frame/chrome_tab.idl +++ b/chrome_frame/chrome_tab.idl @@ -11,7 +11,7 @@ import "ocidl.idl"; [ object, - uuid(B9F5EA20-C450-4f46-B70F-BFD3CA9A20C5), + uuid(FB243E4B-8AC2-4840-95F2-91B9AF9CFF10), dual, nonextensible, helpstring("IChromeFrame Interface"), @@ -65,6 +65,14 @@ interface IChromeFrame : IDispatch { HRESULT useChromeNetwork([out, retval] VARIANT_BOOL* pVal); [propput, id(11)] HRESULT useChromeNetwork([in] VARIANT_BOOL newVal); + + [id(12), hidden] + // This method is available only when the control is in privileged mode. + HRESULT installExtension([in] BSTR crx_path); + + [id(13), hidden] + // This method is available only when the control is in privileged mode. + HRESULT loadExtension([in] BSTR extension_path); }; [ @@ -95,6 +103,7 @@ typedef enum { CF_EVENT_DISPID_ONLOADERROR, CF_EVENT_DISPID_ONMESSAGE, CF_EVENT_DISPID_ONPRIVATEMESSAGE, + CF_EVENT_DISPID_ONEXTENSIONREADY, CF_EVENT_DISPID_ONREADYSTATECHANGED = DISPID_READYSTATECHANGE, } ChromeFrameEventDispId; @@ -106,7 +115,7 @@ typedef enum { library ChromeTabLib { importlib("stdole2.tlb"); - [uuid(A96B8A02-DD11-4936-8C0F-B2520289FABB)] + [uuid(388B5D64-CE67-415b-9B0F-745C56E868E7)] dispinterface DIChromeFrameEvents { properties: // None. @@ -123,6 +132,10 @@ library ChromeTabLib { [id(CF_EVENT_DISPID_ONPRIVATEMESSAGE)] // This event is only fired when the control is in privileged mode. void onprivatemessage([in] IDispatch* event, [in] BSTR target); + [id(CF_EVENT_DISPID_ONEXTENSIONREADY)] + // This event is only fired when the control is in privileged mode. + // response is one of AutomationMsg_ExtensionResponseValues. + void onextensionready([in] BSTR path, [in] long response); }; [uuid(BB1176EE-20DD-41DC-9D1E-AC1335C7BBB0)] diff --git a/chrome_frame/test/data/privileged_apis_host.html b/chrome_frame/test/data/privileged_apis_host.html index 7261704..fde39e1 100644 --- a/chrome_frame/test/data/privileged_apis_host.html +++ b/chrome_frame/test/data/privileged_apis_host.html @@ -25,16 +25,15 @@ onFailure(testName, 1, 'exception in OnChromeFrameMessage'); } } - - function OnChromeFrameLoaded(url) { + + function tryPrivateMessage() { var cf = GetChromeFrame(); - + try { // Any message received by this listener is a failure. // This succeeds in FF, but throws an exception in IE. cf.addEventListener('onprivatemessage', OnPrivateMessage, false); } catch(e) { - cf.onprivatemessage = appendStatus('addEventListener onprivatemessage threw exception') } @@ -46,11 +45,50 @@ } catch(e) { } appendStatus('After postPrivateMessage') - // The frame reflects this twice, first to a bogus target - // and again to the default target '*'. We succeed if we + } + + function tryInstallExtension() { + var cf = GetChromeFrame(); + + try { + // Any message received by this listener is a failure. + // This succeeds in FF, but throws an exception in IE. + cf.installExtension('foo'); + onFailure(testName, 1, 'installExtension should throw'); + } catch(e) { + appendStatus('installExtension threw exception') + } + + appendStatus('After installExtension') + } + + function tryLoadExtension() { + var cf = GetChromeFrame(); + + try { + // Any message received by this listener is a failure. + // This succeeds in FF, but throws an exception in IE. + cf.loadExtension('foo'); + onFailure(testName, 1, 'loadExtension should throw'); + } catch(e) { + appendStatus('loadExtension threw exception') + } + + appendStatus('After loadExtension') + } + + function OnChromeFrameLoaded(url) { + tryPrivateMessage(); + tryInstallExtension(); + tryLoadExtension(); + + // The frame reflects this twice, first to a bogus target + // and again to the default target '*'. We succeed if we // get the reflected message to OnChromeFrameMessage and not to // OnPrivateMessage. + var cf = GetChromeFrame(); cf.postMessage('succeed'); + appendStatus('After cf.postMessage') } function GetChromeFrame() { |