Index: chrome_frame/chrome_frame_npapi.cc |
=================================================================== |
--- chrome_frame/chrome_frame_npapi.cc (revision 0) |
+++ chrome_frame/chrome_frame_npapi.cc (revision 0) |
@@ -0,0 +1,1462 @@ |
+// 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 "chrome_frame/chrome_frame_npapi.h" |
+ |
+#include "base/basictypes.h" |
+#include "base/logging.h" |
+#include "base/message_loop.h" |
+#include "base/string_util.h" |
+#include "base/win_util.h" |
+#include "chrome/test/automation/tab_proxy.h" |
+#include "chrome_frame/ff_privilege_check.h" |
+#include "chrome_frame/utils.h" |
+ |
+MessageLoop* ChromeFrameNPAPI::message_loop_ = NULL; |
+int ChromeFrameNPAPI::instance_count_ = 0; |
+ |
+static const char* kNpEventNames[] = { |
+ "focus", |
+ "blur", |
+}; |
+ |
+NPClass ChromeFrameNPAPI::plugin_class_ = { |
+ NP_CLASS_STRUCT_VERSION, |
+ ChromeFrameNPAPI::AllocateObject, |
+ ChromeFrameNPAPI::DeallocateObject, |
+ ChromeFrameNPAPI::Invalidate, |
+ ChromeFrameNPAPI::HasMethod, |
+ ChromeFrameNPAPI::Invoke, |
+ NULL, // invokeDefault |
+ ChromeFrameNPAPI::HasProperty, |
+ ChromeFrameNPAPI::GetProperty, |
+ ChromeFrameNPAPI::SetProperty, |
+ NULL, // remove property |
+ NULL, // enumeration |
+ NULL, // construct |
+}; |
+ |
+NPIdentifier |
+ ChromeFrameNPAPI::plugin_property_identifiers_[PLUGIN_PROPERTY_COUNT] |
+ = {0}; |
+ |
+const NPUTF8* ChromeFrameNPAPI::plugin_property_identifier_names_[] = { |
+ "version", |
+ "src", |
+ "onload", |
+ "onloaderror", |
+ "onmessage", |
+ "readystate", |
+ "onprivatemessage", |
+ "usechromenetwork", |
+}; |
+ |
+const NPUTF8* ChromeFrameNPAPI::plugin_method_identifier_names_[] = { |
+ "postMessage", |
+ "postPrivateMessage", |
+}; |
+ |
+ChromeFrameNPAPI::PluginMethod ChromeFrameNPAPI::plugin_methods_[] = { |
+ &ChromeFrameNPAPI::postMessage, |
+ &ChromeFrameNPAPI::postPrivateMessage, |
+}; |
+ |
+NPIdentifier |
+ ChromeFrameNPAPI::plugin_method_identifiers_[arraysize(plugin_methods_)] |
+ = {0}; |
+ |
+ |
+void ChromeFrameNPAPI::CompileAsserts() { |
+ NOTREACHED(); // This function should never be invoked. |
+ |
+ COMPILE_ASSERT(arraysize(plugin_method_identifier_names_) == |
+ arraysize(plugin_methods_), |
+ you_must_add_both_plugin_method_and_name); |
+ |
+ COMPILE_ASSERT(arraysize(plugin_property_identifier_names_) == |
+ arraysize(plugin_property_identifiers_), |
+ you_must_add_both_plugin_property_and_name); |
+} |
+ |
+static const int kMaxBytesForPluginConsumption = 0x7FFFFFFF; |
+ |
+static const char kPluginSrcAttribute[] = "src"; |
+static const char kPluginForceFullPageAttribute[] = "force_full_page"; |
+static const char kPluginOnloadAttribute[] = "onload"; |
+static const char kPluginOnErrorAttribute[] = "onloaderror"; |
+static const char kPluginOnMessageAttribute[] = "onmessage"; |
+static const char kPluginOnPrivateMessageAttribute[] = "onprivatemessage"; |
+// These properties can only be set in arguments at control instantiation. |
+// When the privileged_mode property is provided and set to true, the control |
+// will probe for whether its hosting document has the system principal, in |
+// which case privileged mode will be enabled. |
+static const char kPluginPrivilegedModeAttribute[] = "privileged_mode"; |
+// If privileged mode is enabled, the string value of this argument will |
+// be appended to the chrome.exe command line. |
+static const char kPluginChromeExtraArguments[] = "chrome_extra_arguments"; |
+// If privileged mode is enabled, the string value of this argument will |
+// be used as the profile name for our chrome.exe instance. |
+static const char kPluginChromeProfileName[] = "chrome_profile_name"; |
+// If chrome network stack is to be used |
+static const char kPluginUseChromeNetwork[] = "usechromenetwork"; |
+ |
+ |
+NPError NPP_New(NPMIMEType plugin_type, NPP instance, uint16 mode, int16 argc, |
+ char* argn[], char* argv[], NPSavedData* saved) { |
+ if (instance == NULL) |
+ return NPERR_INVALID_INSTANCE_ERROR; |
+ |
+ ChromeFrameNPAPI::ChromeFrameNPObject* chrome_frame_npapi_obj = |
+ reinterpret_cast<ChromeFrameNPAPI::ChromeFrameNPObject*>( |
+ npapi::CreateObject(instance, ChromeFrameNPAPI::PluginClass())); |
+ DCHECK(chrome_frame_npapi_obj != NULL); |
+ |
+ ChromeFrameNPAPI* plugin_instance = |
+ chrome_frame_npapi_obj->chrome_frame_plugin_instance; |
+ DCHECK(plugin_instance != NULL); |
+ |
+ // Note that we MUST set instance->pdata BEFORE calling Initialize. This is |
+ // because Initialize can call back into the NPAPI host which will need the |
+ // pdata field to be set. |
+ chrome_frame_npapi_obj->chrome_frame_plugin_instance = |
+ plugin_instance; |
+ instance->pdata = chrome_frame_npapi_obj; |
+ |
+ bool init = plugin_instance->Initialize(plugin_type, instance, |
+ mode, argc, argn, argv); |
+ DCHECK(init); |
+ |
+ return NPERR_NO_ERROR; |
+} |
+ |
+NPError NPP_Destroy(NPP instance, NPSavedData** save) { |
+ // Takes ownership and releases the object at the end of scope. |
+ ScopedNpObject<ChromeFrameNPAPI::ChromeFrameNPObject> chrome_frame_npapi_obj( |
+ reinterpret_cast<ChromeFrameNPAPI::ChromeFrameNPObject*>( |
+ instance->pdata)); |
+ |
+ if (chrome_frame_npapi_obj.get()) { |
+ ChromeFrameNPAPI* plugin_instance = |
+ ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance); |
+ |
+ plugin_instance->Uninitialize(); |
+ instance->pdata = NULL; |
+ } |
+ |
+ return NPERR_NO_ERROR; |
+} |
+ |
+NPError NPP_SetWindow(NPP instance, NPWindow* window_info) { |
+ if (window_info == NULL) { |
+ NOTREACHED(); |
+ return NPERR_GENERIC_ERROR; |
+ } |
+ |
+ ChromeFrameNPAPI* plugin_instance = |
+ ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance); |
+ |
+ if (plugin_instance == NULL) { |
+ return NPERR_INVALID_INSTANCE_ERROR; |
+ } |
+ |
+ plugin_instance->SetWindow(window_info); |
+ return NPERR_NO_ERROR; |
+} |
+ |
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, |
+ NPBool seekable, uint16* stream_type) { |
+ NPAPIUrlRequest* url_request = ChromeFrameNPAPI::ValidateRequest( |
+ instance, stream->notifyData); |
+ if (url_request) { |
+ if (!url_request->OnStreamCreated(type, stream)) |
+ return NPERR_GENERIC_ERROR; |
+ } |
+ |
+ // We need to return the requested stream mode if we are returning a success |
+ // code. If we don't do this it causes Opera to blow up. |
+ *stream_type = NP_NORMAL; |
+ return NPERR_NO_ERROR; |
+} |
+ |
+NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason) { |
+ NPAPIUrlRequest* url_request = ChromeFrameNPAPI::ValidateRequest( |
+ instance, stream->notifyData); |
+ if (url_request) { |
+ url_request->OnStreamDestroyed(reason); |
+ } |
+ |
+ return NPERR_NO_ERROR; |
+} |
+ |
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value) { |
+ if (variable == NPPVpluginScriptableNPObject) { |
+ void** plugin = reinterpret_cast<void**>(value); |
+ ChromeFrameNPAPI::ChromeFrameNPObject* chrome_frame_npapi_obj = |
+ reinterpret_cast<ChromeFrameNPAPI::ChromeFrameNPObject*>( |
+ instance->pdata); |
+ // Return value is expected to be retained |
+ npapi::RetainObject(reinterpret_cast<NPObject*>(chrome_frame_npapi_obj)); |
+ *plugin = chrome_frame_npapi_obj; |
+ return NPERR_NO_ERROR; |
+ } |
+ return NPERR_GENERIC_ERROR; |
+} |
+ |
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value) { |
+ return NPERR_GENERIC_ERROR; |
+} |
+ |
+int32 NPP_WriteReady(NPP instance, NPStream* stream) { |
+ NPAPIUrlRequest* url_request = ChromeFrameNPAPI::ValidateRequest( |
+ instance, stream->notifyData); |
+ if (url_request) { |
+ return url_request->OnWriteReady(); |
+ } |
+ |
+ return kMaxBytesForPluginConsumption; |
+} |
+ |
+int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, |
+ void* buffer) { |
+ NPAPIUrlRequest* url_request = ChromeFrameNPAPI::ValidateRequest( |
+ instance, stream->notifyData); |
+ if (url_request) { |
+ return url_request->OnWrite(buffer, len); |
+ } |
+ |
+ return len; |
+} |
+ |
+void NPP_URLNotify(NPP instance, const char* url, NPReason reason, |
+ void* notifyData) { |
+ ChromeFrameNPAPI* plugin_instance = |
+ ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance); |
+ if (plugin_instance) { |
+ plugin_instance->UrlNotify(url, reason, notifyData); |
+ } |
+} |
+ |
+void NPP_Print(NPP instance, NPPrint* print_info) { |
+ ChromeFrameNPAPI* plugin_instance = |
+ ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance); |
+ |
+ if (plugin_instance == NULL) { |
+ NOTREACHED(); |
+ return; |
+ } |
+ |
+ plugin_instance->Print(print_info); |
+} |
+ |
+// ChromeFrameNPAPI member defines. |
+ |
+// TODO(tommi): remove ignore_setfocus_ since that's not how focus is |
+// handled anymore. |
+ |
+ChromeFrameNPAPI::ChromeFrameNPAPI() |
+ : instance_(NULL), |
+ mode_(NP_EMBED), |
+ force_full_page_plugin_(false), |
+ ready_state_(READYSTATE_LOADING), |
+ enabled_popups_(false) { |
+} |
+ |
+ChromeFrameNPAPI::~ChromeFrameNPAPI() { |
+ if (IsWindow()) { |
+ if (!UnsubclassWindow()) { |
+ // TODO(tommi): Figure out why this can sometimes happen in the |
+ // WidgetModeFF_Resize unittest. |
+ DLOG(ERROR) << "Couldn't unsubclass safely!"; |
+ UnsubclassWindow(TRUE); |
+ } |
+ } |
+ m_hWnd = NULL; |
+ |
+ instance_count_--; |
+ if (instance_count_ <= 0) { |
+ delete message_loop_; |
+ message_loop_ = NULL; |
+ } |
+ |
+ Uninitialize(); |
+} |
+ |
+std::string GetLocation(NPP instance, NPObject* window) { |
+ if (!window) { |
+ // Can fail if the browser is closing (seen in Opera). |
+ return ""; |
+ } |
+ |
+ std::string result; |
+ ScopedNpVariant href; |
+ ScopedNpVariant location; |
+ |
+ bool ok = npapi::GetProperty(instance, window, |
+ npapi::GetStringIdentifier("location"), &location); |
+ DCHECK(ok); |
+ DCHECK(location.type == NPVariantType_Object); |
+ |
+ if (ok) { |
+ ok = npapi::GetProperty(instance, |
+ location.value.objectValue, |
+ npapi::GetStringIdentifier("href"), |
+ &href); |
+ DCHECK(ok); |
+ DCHECK(href.type == NPVariantType_String); |
+ if (ok) { |
+ result.assign(href.value.stringValue.UTF8Characters, |
+ href.value.stringValue.UTF8Length); |
+ } |
+ } |
+ |
+ return result; |
+} |
+ |
+std::string ChromeFrameNPAPI::GetLocation() { |
+ // Note that GetWindowObject() will cache the window object here. |
+ return ::GetLocation(instance_, GetWindowObject()); |
+} |
+ |
+bool ChromeFrameNPAPI::Initialize(NPMIMEType mime_type, NPP instance, |
+ uint16 mode, int16 argc, char* argn[], |
+ char* argv[]) { |
+ if (!Base::Initialize()) |
+ return false; |
+ |
+ instance_ = instance; |
+ mime_type_ = mime_type; |
+ mode_ = mode; |
+ document_url_ = GetLocation(); |
+ |
+ if (instance_count_ == 0) { |
+ DCHECK(message_loop_ == NULL); |
+ message_loop_ = new MessageLoop(); |
+ } |
+ |
+ instance_count_++; |
+ |
+ // Create our prefs service wrapper here. |
+ DCHECK(!pref_service_.get()); |
+ pref_service_ = CreatePrefService(); |
+ if (!pref_service_.get()) { |
+ NOTREACHED() << "new NpProxyService"; |
+ return false; |
+ } |
+ |
+ // Temporary variables for privileged only parameters |
+ const char* onprivatemessage_arg = NULL; |
+ const char* chrome_extra_arguments_arg = NULL; |
+ const char* chrome_profile_name_arg = NULL; |
+ bool chrome_network_arg_set = false; |
+ bool chrome_network_arg = false; |
+ bool wants_privileged = false; |
+ |
+ for (int i = 0; i < argc; ++i) { |
+ if (LowerCaseEqualsASCII(argn[i], kPluginSrcAttribute)) { |
+ src_ = ResolveURL(GetDocumentUrl(), argv[i]); |
+ } else if (LowerCaseEqualsASCII(argn[i], kPluginForceFullPageAttribute)) { |
+ force_full_page_plugin_ = atoi(argv[i]) ? true : false; |
+ } else if (LowerCaseEqualsASCII(argn[i], kPluginOnErrorAttribute)) { |
+ onerror_handler_ = JavascriptToNPObject(argv[i]); |
+ } else if (LowerCaseEqualsASCII(argn[i], kPluginOnMessageAttribute)) { |
+ onmessage_handler_ = JavascriptToNPObject(argv[i]); |
+ } else if (LowerCaseEqualsASCII(argn[i], |
+ kPluginPrivilegedModeAttribute)) { |
+ // Test for the FireFox privileged mode if the user requests it |
+ // in initialization parameters. |
+ wants_privileged = atoi(argv[i]) ? true : false; |
+ } else if (LowerCaseEqualsASCII(argn[i], |
+ kPluginOnPrivateMessageAttribute)) { |
+ onprivatemessage_arg = argv[i]; |
+ } else if (LowerCaseEqualsASCII(argn[i], kPluginChromeExtraArguments)) { |
+ chrome_extra_arguments_arg = argv[i]; |
+ } else if (LowerCaseEqualsASCII(argn[i], kPluginChromeProfileName)) { |
+ chrome_profile_name_arg = argv[i]; |
+ } else if (LowerCaseEqualsASCII(argn[i], kPluginUseChromeNetwork)) { |
+ chrome_network_arg_set = true; |
+ chrome_network_arg = atoi(argv[i]) ? true : false; |
+ } |
+ } |
+ |
+ // Is the privileged mode requested? |
+ if (wants_privileged) { |
+ is_privileged_ = IsFireFoxPrivilegedInvocation(instance); |
+ if (!is_privileged_) { |
+ DLOG(WARNING) << "Privileged mode requested in non-privileged context"; |
+ } |
+ } |
+ |
+ std::wstring extra_arguments; |
+ std::wstring profile_name(GetHostProcessName(false)); |
+ if (is_privileged_) { |
+ // Process any privileged mode-only arguments we were handed. |
+ if (onprivatemessage_arg) |
+ onprivatemessage_handler_ = JavascriptToNPObject(onprivatemessage_arg); |
+ |
+ if (chrome_extra_arguments_arg) |
+ extra_arguments = UTF8ToWide(chrome_extra_arguments_arg); |
+ |
+ if (chrome_profile_name_arg) |
+ profile_name = UTF8ToWide(chrome_profile_name_arg); |
+ |
+ if (chrome_network_arg_set) |
+ automation_client_->set_use_chrome_network(chrome_network_arg); |
+ |
+ } |
+ |
+ // TODO(joshia): Initialize navigation here and send proxy config as |
+ // part of LaunchSettings |
+ /* |
+ if (!src_.empty()) |
+ automation_client_->InitiateNavigation(src_); |
+ |
+ std::string proxy_settings; |
+ bool has_prefs = pref_service_->Initialize(instance_, |
+ automation_client_.get()); |
+ if (has_prefs && pref_service_->GetProxyValueJSONString(&proxy_settings)) { |
+ automation_client_->SetProxySettings(proxy_settings); |
+ } |
+ */ |
+ |
+ // We can't call SubscribeToFocusEvents here since |
+ // when Initialize gets called, Opera is in a state where |
+ // it can't handle calls back and the thread will hang. |
+ // Instead, we call SubscribeToFocusEvents when we initialize |
+ // our plugin window. |
+ |
+ // TODO(stoyan): Ask host for specific interface whether to honor |
+ // host's in-private mode. |
+ return InitializeAutomation(profile_name, extra_arguments, |
+ GetBrowserIncognitoMode()); |
+} |
+ |
+void ChromeFrameNPAPI::Uninitialize() { |
+ // Don't call SetReadyState as it will end up calling FireEvent. |
+ // We are in the context of NPP_DESTROY. |
+ ready_state_ = READYSTATE_UNINITIALIZED; |
+ |
+ UnsubscribeFromFocusEvents(); |
+ |
+ if (pref_service_) { |
+ pref_service_->UnInitialize(); |
+ pref_service_ = NULL; |
+ } |
+ |
+ window_object_.Free(); |
+ onerror_handler_.Free(); |
+ onmessage_handler_.Free(); |
+ onprivatemessage_handler_.Free(); |
+ |
+ Base::Uninitialize(); |
+} |
+ |
+void ChromeFrameNPAPI::OnFinalMessage(HWND window) { |
+ // The automation server should be gone by now. |
+ Uninitialize(); |
+} |
+ |
+void ChromeFrameNPAPI::SubscribeToFocusEvents() { |
+ DCHECK(focus_listener_.get() == NULL); |
+ |
+ focus_listener_ = new DomEventListener(this); |
+ if (!focus_listener_->Subscribe(instance_, kNpEventNames, |
+ arraysize(kNpEventNames))) { |
+ focus_listener_ = NULL; |
+ focus_listener_ = new NPObjectEventListener(this); |
+ if (!focus_listener_->Subscribe(instance_, kNpEventNames, |
+ arraysize(kNpEventNames))) { |
+ DLOG(ERROR) << "Failed to subscribe to focus events"; |
+ focus_listener_ = NULL; |
+ } |
+ } |
+} |
+ |
+void ChromeFrameNPAPI::UnsubscribeFromFocusEvents() { |
+ if (!focus_listener_.get()) |
+ return; |
+ |
+ bool ret = focus_listener_->Unsubscribe(instance_, kNpEventNames, |
+ arraysize(kNpEventNames)); |
+ DLOG_IF(WARNING, !ret) << "focus_listener_->Unsubscribe failed"; |
+ focus_listener_ = NULL; |
+} |
+ |
+bool ChromeFrameNPAPI::SetWindow(NPWindow* window_info) { |
+ if (!window_info || !automation_client_.get()) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ |
+ HWND window = reinterpret_cast<HWND>(window_info->window); |
+ if (!::IsWindow(window)) { |
+ // No window created yet. Ignore this call. |
+ return false; |
+ } |
+ |
+ if (IsWindow()) { |
+ // We've already subclassed, make sure that SetWindow doesn't get called |
+ // with an HWND other than the one we subclassed during our lifetime. |
+ DCHECK(window == m_hWnd); |
+ return true; |
+ } |
+ |
+ automation_client_->SetParentWindow(window); |
+ |
+ SubscribeToFocusEvents(); |
+ |
+ if (force_full_page_plugin_) { |
+ // By default full page mode is only enabled when the plugin is loaded off |
+ // a separate file, i.e. it is the primary content in the window. Even if |
+ // we specify the width/height attributes for the plugin as 100% each, FF |
+ // instantiates the plugin passing in a width/height of 100px each. To |
+ // workaround this we resize the plugin window passed in by FF to the size |
+ // of its parent. |
+ HWND plugin_parent_window = ::GetParent(window); |
+ RECT plugin_parent_rect = {0}; |
+ ::GetClientRect(plugin_parent_window, &plugin_parent_rect); |
+ ::SetWindowPos(window, NULL, plugin_parent_rect.left, |
+ plugin_parent_rect.top, |
+ plugin_parent_rect.right - plugin_parent_rect.left, |
+ plugin_parent_rect.bottom - plugin_parent_rect.top, 0); |
+ } |
+ |
+ // Subclass the browser's plugin window here. |
+ if (SubclassWindow(window)) { |
+ DWORD new_style_flags = WS_CLIPCHILDREN; |
+ ModifyStyle(0, new_style_flags, 0); |
+ |
+ if (ready_state_ < READYSTATE_INTERACTIVE) { |
+ SetReadyState(READYSTATE_INTERACTIVE); |
+ } |
+ } |
+ |
+ return true; |
+} |
+ |
+void ChromeFrameNPAPI::Print(NPPrint* print_info) { |
+ if (!print_info) { |
+ NOTREACHED(); |
+ return; |
+ } |
+ |
+ // We dont support full tab mode yet. |
+ if (print_info->mode != NP_EMBED) { |
+ NOTREACHED(); |
+ return; |
+ } |
+ |
+ NPWindow window = print_info->print.embedPrint.window; |
+ |
+ RECT print_bounds = {0}; |
+ print_bounds.left = window.x; |
+ print_bounds.top = window.y; |
+ print_bounds.right = window.x + window.width; |
+ print_bounds.bottom = window.x + window.height; |
+ |
+ automation_client_->Print( |
+ reinterpret_cast<HDC>(print_info->print.embedPrint.platformPrint), |
+ print_bounds); |
+} |
+ |
+void ChromeFrameNPAPI::UrlNotify(const char* url, NPReason reason, |
+ void* notify_data) { |
+ if (enabled_popups_) { |
+ // We have opened the URL so tell the browser to restore popup settings |
+ enabled_popups_ = false; |
+ npapi::PopPopupsEnabledState(instance_); |
+ } |
+ |
+ // It is now safe to release the additional reference on the request |
+ NPAPIUrlRequest* request = RequestFromNotifyData(notify_data); |
+ if (request) { |
+ request->Stop(); |
+ request->Release(); |
+ } |
+} |
+ |
+void ChromeFrameNPAPI::OnAcceleratorPressed(int tab_handle, |
+ const MSG& accel_message) { |
+ DLOG(INFO) << __FUNCTION__ << " msg:" |
+ << StringPrintf("0x%04X", accel_message.message) << " key:" |
+ << accel_message.wParam; |
+ |
+ // The host browser does call TranslateMessage on messages like WM_KEYDOWN |
+ // WM_KEYUP, etc, which will result in messages like WM_CHAR, WM_SYSCHAR, etc |
+ // being posted to the message queue. We don't post these messages here to |
+ // avoid these messages from getting handled twice. |
+ if (accel_message.message != WM_CHAR && |
+ accel_message.message != WM_DEADCHAR && |
+ accel_message.message != WM_SYSCHAR && |
+ accel_message.message != WM_SYSDEADCHAR) { |
+ // A very primitive way to handle keystrokes. |
+ // TODO(tommi): When we've implemented a way for chrome to |
+ // know when keystrokes are handled (deterministically) on that side, |
+ // then this function should get called and not otherwise. |
+ ::PostMessage(::GetParent(m_hWnd), accel_message.message, |
+ accel_message.wParam, accel_message.lParam); |
+ } |
+ |
+ if (automation_client_.get()) { |
+ TabProxy* tab = automation_client_->tab(); |
+ if (tab) { |
+ tab->ProcessUnhandledAccelerator(accel_message); |
+ } |
+ } |
+} |
+ |
+void ChromeFrameNPAPI::OnTabbedOut(int tab_handle, bool reverse) { |
+ DLOG(INFO) << __FUNCTION__; |
+ |
+ ignore_setfocus_ = true; |
+ HWND parent = ::GetParent(m_hWnd); |
+ ::SetFocus(parent); |
+ |
+ INPUT input = {0}; |
+ input.type = INPUT_KEYBOARD; |
+ input.ki.wVk = VK_TAB; |
+ SendInput(1, &input, sizeof(input)); |
+ input.ki.dwFlags = KEYEVENTF_KEYUP; |
+ SendInput(1, &input, sizeof(input)); |
+ |
+ ignore_setfocus_ = false; |
+} |
+ |
+void ChromeFrameNPAPI::OnOpenURL(int tab_handle, |
+ const GURL& url, int open_disposition) { |
+ std::string target; |
+ switch (open_disposition) { |
+ case NEW_FOREGROUND_TAB: |
+ target = "_blank"; |
+ break; |
+ case NEW_BACKGROUND_TAB: |
+ target = "_blank"; |
+ break; |
+ case NEW_WINDOW: |
+ target = "_new"; |
+ break; |
+ default: |
+ break; |
+ } |
+ |
+ // Tell the browser to temporarily allow popups |
+ enabled_popups_ = true; |
+ npapi::PushPopupsEnabledState(instance_, TRUE); |
+ npapi::GetURLNotify(instance_, url.spec().c_str(), target.c_str(), NULL); |
+} |
+ |
+void ChromeFrameNPAPI::OnRequestStart(int tab_handle, int request_id, |
+ const IPC::AutomationURLRequest& request) { |
+ scoped_refptr<NPAPIUrlRequest> new_request(new NPAPIUrlRequest(instance_)); |
+ DCHECK(new_request); |
+ if (new_request->Initialize(automation_client_.get(), tab_handle, |
+ request_id, request.url, request.method, |
+ request.referrer, request.extra_request_headers, |
+ request.upload_data.get(), true)) { |
+ if (new_request->Start()) { |
+ // Keep additional reference on request for NPSTREAM |
+ // This will be released in NPP_UrlNotify |
+ new_request->AddRef(); |
+ } |
+ } |
+} |
+ |
+void ChromeFrameNPAPI::OnRequestRead(int tab_handle, int request_id, |
+ int bytes_to_read) { |
+ automation_client_->ReadRequest(request_id, bytes_to_read); |
+} |
+ |
+void ChromeFrameNPAPI::OnRequestEnd(int tab_handle, int request_id, |
+ const URLRequestStatus& status) { |
+ automation_client_->RemoveRequest(request_id, status.status(), true); |
+} |
+ |
+void ChromeFrameNPAPI::OnSetCookieAsync(int tab_handle, const GURL& url, |
+ const std::string& cookie) { |
+ // Use the newer NPAPI way if available |
+ if (npapi::VersionMinor() >= NPVERS_HAS_URL_AND_AUTH_INFO) { |
+ npapi::SetValueForURL(instance_, NPNURLVCookie, url.spec().c_str(), |
+ cookie.c_str(), cookie.length()); |
+ } else if (url == GURL(document_url_)) { |
+ std::string script = "javascript:document.cookie="; |
+ script.append(cookie); |
+ script.append(1, ';'); |
+ ExecuteScript(script, NULL); |
+ } else { |
+ // Third party cookie, use nsICookieService to set the cookie. |
+ NOTREACHED(); |
+ } |
+} |
+ |
+bool ChromeFrameNPAPI::HasMethod(NPObject* obj, NPIdentifier name) { |
+ for (int i = 0; i < arraysize(plugin_methods_); ++i) { |
+ if (name == plugin_method_identifiers_[i]) |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+bool ChromeFrameNPAPI::Invoke(NPObject* header, NPIdentifier name, |
+ const NPVariant* args, uint32_t arg_count, |
+ NPVariant* result) { |
+ ChromeFrameNPAPI* plugin_instance = ChromeFrameInstanceFromNPObject(header); |
+ if (!plugin_instance && (plugin_instance->automation_client_.get())) |
+ return false; |
+ |
+ bool success = false; |
+ for (int i = 0; i < arraysize(plugin_methods_); ++i) { |
+ if (name == plugin_method_identifiers_[i]) { |
+ PluginMethod method = plugin_methods_[i]; |
+ success = (plugin_instance->*method)(header, args, arg_count, result); |
+ break; |
+ } |
+ } |
+ |
+ return success; |
+} |
+ |
+void ChromeFrameNPAPI::InitializeIdentifiers() { |
+ npapi::GetStringIdentifiers(plugin_method_identifier_names_, |
+ arraysize(plugin_methods_), |
+ plugin_method_identifiers_); |
+ |
+ npapi::GetStringIdentifiers(plugin_property_identifier_names_, |
+ PLUGIN_PROPERTY_COUNT, |
+ plugin_property_identifiers_); |
+} |
+ |
+NPObject* ChromeFrameNPAPI::AllocateObject(NPP instance, NPClass* class_name) { |
+ static bool identifiers_initialized = false; |
+ |
+ ChromeFrameNPObject* plugin_object = new ChromeFrameNPObject(); |
+ DCHECK(plugin_object != NULL); |
+ |
+ plugin_object->chrome_frame_plugin_instance = new ChromeFrameNPAPI(); |
+ DCHECK(plugin_object->chrome_frame_plugin_instance != NULL); |
+ |
+ plugin_object->npp = NULL; |
+ |
+ COMPILE_ASSERT(arraysize(plugin_method_identifiers_) == |
+ arraysize(plugin_method_identifier_names_), |
+ method_count_mismatch); |
+ |
+ COMPILE_ASSERT(arraysize(plugin_method_identifiers_) == |
+ arraysize(plugin_methods_), |
+ method_count_mismatch); |
+ |
+ if (!identifiers_initialized) { |
+ InitializeIdentifiers(); |
+ identifiers_initialized = true; |
+ } |
+ |
+ return reinterpret_cast<NPObject*>(plugin_object); |
+} |
+ |
+void ChromeFrameNPAPI::DeallocateObject(NPObject* header) { |
+ ChromeFrameNPObject* plugin_object = |
+ reinterpret_cast<ChromeFrameNPObject*>(header); |
+ DCHECK(plugin_object != NULL); |
+ |
+ if (plugin_object) { |
+ delete plugin_object->chrome_frame_plugin_instance; |
+ delete plugin_object; |
+ } |
+} |
+ |
+void ChromeFrameNPAPI::Invalidate(NPObject* header) { |
+ DCHECK(header); |
+ ChromeFrameNPObject* plugin_object = |
+ reinterpret_cast<ChromeFrameNPObject*>(header); |
+ if (plugin_object) { |
+ DCHECK(plugin_object->chrome_frame_plugin_instance); |
+ plugin_object->chrome_frame_plugin_instance->Uninitialize(); |
+ } |
+} |
+ |
+ChromeFrameNPAPI* ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance( |
+ NPP instance) { |
+ if ((instance == NULL) || (instance->pdata == NULL)) { |
+ NOTREACHED(); |
+ return NULL; |
+ } |
+ |
+ return ChromeFrameInstanceFromNPObject(instance->pdata); |
+} |
+ |
+ChromeFrameNPAPI* ChromeFrameNPAPI::ChromeFrameInstanceFromNPObject( |
+ void* object) { |
+ ChromeFrameNPObject* plugin_object = |
+ reinterpret_cast<ChromeFrameNPObject*>(object); |
+ if (!plugin_object) { |
+ NOTREACHED(); |
+ return NULL; |
+ } |
+ |
+ DCHECK(plugin_object->chrome_frame_plugin_instance); |
+ return plugin_object->chrome_frame_plugin_instance; |
+} |
+ |
+bool ChromeFrameNPAPI::HasProperty(NPObject* obj, NPIdentifier name) { |
+ for (int i = 0; i < PLUGIN_PROPERTY_COUNT; ++i) { |
+ if (name == plugin_property_identifiers_[i]) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool ChromeFrameNPAPI::GetProperty(NPIdentifier name, |
+ NPVariant* variant) { |
+ if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_ONERROR]) { |
+ if (onerror_handler_) { |
+ variant->type = NPVariantType_Object; |
+ variant->value.objectValue = onerror_handler_.Copy(); |
+ return true; |
+ } |
+ } else if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_ONMESSAGE]) { |
+ if (onmessage_handler_) { |
+ variant->type = NPVariantType_Object; |
+ variant->value.objectValue = onmessage_handler_.Copy(); |
+ return true; |
+ } |
+ } else if (name == |
+ plugin_property_identifiers_[PLUGIN_PROPERTY_ONPRIVATEMESSAGE]) { |
+ if (!is_privileged_) { |
+ DLOG(WARNING) << "Attempt to read onprivatemessage property while not " |
+ "privileged"; |
+ } else { |
+ if (onprivatemessage_handler_) { |
+ variant->type = NPVariantType_Object; |
+ variant->value.objectValue = |
+ onprivatemessage_handler_.Copy(); |
+ return true; |
+ } |
+ } |
+ } else if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_SRC]) { |
+ AllocateStringVariant(src_, variant); |
+ return true; |
+ } else if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_VERSION]) { |
+ const std::wstring version = |
+ automation_client_->GetVersion(); |
+ AllocateStringVariant(WideToUTF8(version), variant); |
+ return true; |
+ } else if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_READYSTATE]) { |
+ INT32_TO_NPVARIANT(ready_state_, *variant); |
+ return true; |
+ } else if (name == |
+ plugin_property_identifiers_[PLUGIN_PROPERTY_USECHROMENETWORK]) { |
+ BOOLEAN_TO_NPVARIANT(automation_client_->use_chrome_network(), *variant); |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+bool ChromeFrameNPAPI::GetProperty(NPObject* object, NPIdentifier name, |
+ NPVariant* variant) { |
+ if (!object || !variant) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ |
+ ChromeFrameNPAPI* plugin_instance = ChromeFrameInstanceFromNPObject(object); |
+ if (!plugin_instance) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ |
+ return plugin_instance->GetProperty(name, variant); |
+} |
+ |
+bool ChromeFrameNPAPI::SetProperty(NPIdentifier name, |
+ const NPVariant* variant) { |
+ if (NPVARIANT_IS_OBJECT(*variant)) { |
+ if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_ONERROR]) { |
+ onerror_handler_.Free(); |
+ onerror_handler_ = variant->value.objectValue; |
+ return true; |
+ } else if ( |
+ name == plugin_property_identifiers_[PLUGIN_PROPERTY_ONMESSAGE]) { |
+ onmessage_handler_.Free(); |
+ onmessage_handler_ = variant->value.objectValue; |
+ return true; |
+ } else if (name == |
+ plugin_property_identifiers_[PLUGIN_PROPERTY_ONPRIVATEMESSAGE]) { |
+ if (!is_privileged_) { |
+ DLOG(WARNING) << "Attempt to set onprivatemessage while not privileged"; |
+ } else { |
+ onprivatemessage_handler_.Free(); |
+ onprivatemessage_handler_ = variant->value.objectValue; |
+ return true; |
+ } |
+ } |
+ } else if (NPVARIANT_IS_STRING(*variant) || NPVARIANT_IS_NULL(*variant)) { |
+ if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_SRC]) { |
+ return NavigateToURL(variant, 1, NULL); |
+ } |
+ } else if (NPVARIANT_IS_BOOLEAN(*variant)) { |
+ if (name == |
+ plugin_property_identifiers_[PLUGIN_PROPERTY_USECHROMENETWORK]) { |
+ automation_client_->set_use_chrome_network( |
+ NPVARIANT_TO_BOOLEAN(*variant)); |
+ } |
+ } |
+ |
+ return false; |
+} |
+ |
+bool ChromeFrameNPAPI::SetProperty(NPObject* object, NPIdentifier name, |
+ const NPVariant* variant) { |
+ if (!object || !variant) { |
+ DLOG(ERROR) << "Cannot set property: " << npapi::StringFromIdentifier(name); |
+ return false; |
+ } |
+ |
+ ChromeFrameNPAPI* plugin_instance = ChromeFrameInstanceFromNPObject(object); |
+ if (!plugin_instance) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ |
+ return plugin_instance->SetProperty(name, variant); |
+} |
+ |
+void ChromeFrameNPAPI::OnFocus() { |
+ DLOG(INFO) << __FUNCTION__; |
+ PostMessage(WM_SETFOCUS, 0, 0); |
+} |
+ |
+void ChromeFrameNPAPI::OnEvent(const char* event_name) { |
+ DCHECK(event_name); |
+ DLOG(INFO) << event_name; |
+ |
+ if (lstrcmpiA(event_name, "focus") == 0) { |
+ OnFocus(); |
+ } else if (lstrcmpiA(event_name, "blur") == 0) { |
+ OnBlur(); |
+ } else { |
+ NOTREACHED() << event_name; |
+ } |
+} |
+ |
+LRESULT CALLBACK ChromeFrameNPAPI::DropKillFocusHook(int code, WPARAM wparam, |
+ LPARAM lparam) { |
+ LRESULT ret = 0; |
+ CWPSTRUCT* wp = reinterpret_cast<CWPSTRUCT*>(lparam); |
+ if ((code < 0) || (wp->message != WM_KILLFOCUS)) |
+ ret = ::CallNextHookEx(NULL, code, wparam, lparam); |
+ |
+ return ret; |
+} |
+ |
+LRESULT ChromeFrameNPAPI::OnSetFocus(UINT message, WPARAM wparam, |
+ LPARAM lparam, BOOL& handled) { // NO_LINT |
+ // Opera has a WH_CALLWNDPROC hook that handles WM_KILLFOCUS and |
+ // prevents us from setting the focus to the tab. |
+ // To work around that, we set a temporary hook here that does nothing |
+ // (not even call other hooks) when it sees WM_KILLFOCUS. |
+ HHOOK hook = NULL; |
+ hook = ::SetWindowsHookEx(WH_CALLWNDPROC, DropKillFocusHook, NULL, |
+ ::GetCurrentThreadId()); |
+ // Since we chain message maps, make sure we are not calling base class |
+ // twice for WM_SETFOCUS. |
+ BOOL handled_by_base = TRUE; |
+ LRESULT ret = Base::OnSetFocus(message, wparam, lparam, handled_by_base); |
+ if (hook) |
+ ::UnhookWindowsHookEx(hook); |
+ |
+ return ret; |
+} |
+ |
+void ChromeFrameNPAPI::OnBlur() { |
+ DLOG(INFO) << __FUNCTION__; |
+} |
+ |
+void ChromeFrameNPAPI::OnLoad(int, const GURL& gurl) { |
+ DLOG(INFO) << "Firing onload"; |
+ FireEvent("load", gurl.spec()); |
+} |
+ |
+void ChromeFrameNPAPI::OnLoadFailed(int error_code, const std::string& url) { |
+ FireEvent("loaderror", url); |
+ |
+ ScopedNpVariant result; |
+ InvokeDefault(onerror_handler_, url, &result); |
+} |
+ |
+void ChromeFrameNPAPI::OnMessageFromChromeFrame(int tab_handle, |
+ const std::string& message, |
+ const std::string& origin, |
+ const std::string& target) { |
+ bool private_message = false; |
+ if (target.compare("*") != 0) { |
+ if (is_privileged_) { |
+ private_message = true; |
+ } else { |
+ if (!HaveSameOrigin(target, document_url_)) { |
+ DLOG(WARNING) << "Dropping posted message since target doesn't match " |
+ "the current document's origin. target=" << target; |
+ return; |
+ } |
+ } |
+ } |
+ |
+ // Create a MessageEvent object that contains the message and origin |
+ // as well as supporting other MessageEvent (see the HTML5 spec) properties. |
+ // Then call the onmessage handler. |
+ ScopedNpObject<NPObject> event; |
+ bool ok = CreateMessageEvent(false, true, message, origin, event.Receive()); |
+ if (ok) { |
+ // Don't call FireEvent here (or we'll have an event wrapped by an event). |
+ DispatchEvent(event); |
+ |
+ ScopedNpVariant result; |
+ NPVariant params[2]; |
+ OBJECT_TO_NPVARIANT(event, params[0]); |
+ bool invoke = false; |
+ if (private_message) { |
+ DCHECK(is_privileged_); |
+ STRINGN_TO_NPVARIANT(target.c_str(), target.length(), params[1]); |
+ invoke = InvokeDefault(onprivatemessage_handler_, |
+ arraysize(params), |
+ params, |
+ &result); |
+ } else { |
+ invoke = InvokeDefault(onmessage_handler_, params[0], &result); |
+ } |
+ DLOG_IF(WARNING, !invoke) << "InvokeDefault failed"; |
+ } else { |
+ NOTREACHED() << "CreateMessageEvent"; |
+ } |
+} |
+ |
+void ChromeFrameNPAPI::OnAutomationServerReady() { |
+ Base::OnAutomationServerReady(); |
+ |
+ std::string proxy_settings; |
+ bool has_prefs = pref_service_->Initialize(instance_, |
+ automation_client_.get()); |
+ if (has_prefs && pref_service_->GetProxyValueJSONString(&proxy_settings)) { |
+ automation_client_->SetProxySettings(proxy_settings); |
+ } |
+ |
+ if (!src_.empty()) { |
+ if (!automation_client_->InitiateNavigation(src_)) { |
+ DLOG(ERROR) << "Failed to navigate to: " << src_; |
+ src_.clear(); |
+ } |
+ } |
+ |
+ SetReadyState(READYSTATE_COMPLETE); |
+} |
+ |
+void ChromeFrameNPAPI::OnAutomationServerLaunchFailed( |
+ AutomationLaunchResult reason, const std::string& server_version) { |
+ SetReadyState(READYSTATE_UNINITIALIZED); |
+ |
+ if (reason == AUTOMATION_VERSION_MISMATCH) { |
+ DisplayVersionMismatchWarning(m_hWnd, server_version); |
+ } |
+} |
+ |
+bool ChromeFrameNPAPI::InvokeDefault(NPObject* object, |
+ unsigned param_count, |
+ const NPVariant* params, |
+ NPVariant* result) { |
+ if (!object) |
+ return false; |
+ |
+ bool ret = npapi::InvokeDefault(instance_, object, params, param_count, |
+ result); |
+ // InvokeDefault can return false in FF even though we do see the call |
+ // go through. It's not clear to me what the circumstances are, so |
+ // we log it as a warning while tracking it down. |
+ DLOG_IF(WARNING, !ret) << "npapi::InvokeDefault failed"; |
+ return ret; |
+} |
+ |
+bool ChromeFrameNPAPI::InvokeDefault(NPObject* object, const std::string& param, |
+ NPVariant* result) { |
+ NPVariant arg; |
+ STRINGN_TO_NPVARIANT(param.c_str(), param.length(), arg); |
+ return InvokeDefault(object, arg, result); |
+} |
+ |
+bool ChromeFrameNPAPI::InvokeDefault(NPObject* object, const NPVariant& param, |
+ NPVariant* result) { |
+ return InvokeDefault(object, 1, ¶m, result); |
+} |
+ |
+bool ChromeFrameNPAPI::CreateEvent(const std::string& type, bool bubbles, |
+ bool cancelable, NPObject** basic_event) { |
+ DCHECK(basic_event); |
+ NPObject* window = GetWindowObject(); |
+ if (!window) { |
+ // Can fail if the browser is closing (seen in Opera). |
+ return false; |
+ } |
+ |
+ const char* identifier_names[] = { |
+ "document", |
+ "createEvent", |
+ "initEvent", |
+ }; |
+ |
+ NPIdentifier identifiers[arraysize(identifier_names)]; |
+ npapi::GetStringIdentifiers(identifier_names, arraysize(identifier_names), |
+ identifiers); |
+ |
+ // Fetch the document object from the window. |
+ ScopedNpVariant document; |
+ bool ok = npapi::GetProperty(instance_, window, identifiers[0], &document); |
+ if (!ok) { |
+ // This could happen if the page is being unloaded. |
+ DLOG(WARNING) << "Failed to fetch the document object"; |
+ return false; |
+ } |
+ |
+ bool success = false; |
+ if (ok && NPVARIANT_IS_OBJECT(document)) { |
+ // Call document.createEvent("Event") to create a basic event object. |
+ NPVariant event_type; |
+ STRINGN_TO_NPVARIANT("Event", sizeof("Event") - 1, event_type); |
+ ScopedNpVariant result; |
+ success = npapi::Invoke(instance_, NPVARIANT_TO_OBJECT(document), |
+ identifiers[1], &event_type, 1, &result); |
+ if (!NPVARIANT_IS_OBJECT(result)) { |
+ DLOG(WARNING) << "Failed to invoke createEvent"; |
+ success = false; |
+ } else { |
+ NPVariant init_args[3]; |
+ STRINGN_TO_NPVARIANT(type.c_str(), type.length(), init_args[0]); |
+ BOOLEAN_TO_NPVARIANT(bubbles, init_args[1]); |
+ BOOLEAN_TO_NPVARIANT(cancelable, init_args[2]); |
+ |
+ // Now initialize the event object by calling |
+ // event.initEvent(type, bubbles, cancelable); |
+ ScopedNpVariant init_results; |
+ ok = npapi::Invoke(instance_, NPVARIANT_TO_OBJECT(result), identifiers[2], |
+ init_args, arraysize(init_args), &init_results); |
+ if (ok) { |
+ success = true; |
+ // Finally, pass the ownership to the caller. |
+ *basic_event = NPVARIANT_TO_OBJECT(result); |
+ VOID_TO_NPVARIANT(result); // Prevent the object from being released. |
+ } else { |
+ DLOG(ERROR) << "initEvent failed"; |
+ success = false; |
+ } |
+ } |
+ } |
+ |
+ return success; |
+} |
+ |
+bool ChromeFrameNPAPI::CreateMessageEvent(bool bubbles, bool cancelable, |
+ const std::string& data, |
+ const std::string& origin, |
+ NPObject** message_event) { |
+ DCHECK(message_event); |
+ ScopedNpObject<NPObject> event; |
+ bool ok = CreateEvent("message", false, true, event.Receive()); |
+ if (ok) { |
+ typedef enum { |
+ DATA, |
+ ORIGIN, |
+ LAST_EVENT_ID, |
+ SOURCE, |
+ MESSAGE_PORT, |
+ IDENTIFIER_COUNT, // Must be last. |
+ } StringIdentifiers; |
+ |
+ static NPIdentifier identifiers[IDENTIFIER_COUNT] = {0}; |
+ if (!identifiers[0]) { |
+ const NPUTF8* identifier_names[] = { |
+ "data", |
+ "origin", |
+ "lastEventId", |
+ "source", |
+ "messagePort", |
+ }; |
+ COMPILE_ASSERT(arraysize(identifier_names) == arraysize(identifiers), |
+ mismatched_array_size); |
+ npapi::GetStringIdentifiers(identifier_names, IDENTIFIER_COUNT, |
+ identifiers); |
+ } |
+ |
+ NPVariant arg; |
+ STRINGN_TO_NPVARIANT(data.c_str(), data.length(), arg); |
+ npapi::SetProperty(instance_, event, identifiers[DATA], &arg); |
+ STRINGN_TO_NPVARIANT(origin.c_str(), origin.length(), arg); |
+ npapi::SetProperty(instance_, event, identifiers[ORIGIN], &arg); |
+ STRINGN_TO_NPVARIANT("", 0, arg); |
+ npapi::SetProperty(instance_, event, identifiers[LAST_EVENT_ID], &arg); |
+ NULL_TO_NPVARIANT(arg); |
+ npapi::SetProperty(instance_, event, identifiers[SOURCE], &arg); |
+ npapi::SetProperty(instance_, event, identifiers[MESSAGE_PORT], &arg); |
+ *message_event = event.Detach(); |
+ } |
+ |
+ return ok; |
+} |
+ |
+ |
+void ChromeFrameNPAPI::DispatchEvent(NPObject* event) { |
+ DCHECK(event != NULL); |
+ |
+ ScopedNpObject<NPObject> embed; |
+ npapi::GetValue(instance_, NPNVPluginElementNPObject, &embed); |
+ if (embed != NULL) { |
+ NPVariant param; |
+ OBJECT_TO_NPVARIANT(event, param); |
+ ScopedNpVariant result; |
+ bool invoke = npapi::Invoke(instance_, embed, |
+ npapi::GetStringIdentifier("dispatchEvent"), ¶m, 1, &result); |
+ DLOG_IF(WARNING, !invoke) << "dispatchEvent failed"; |
+ } else { |
+ NOTREACHED() << "NPNVPluginElementNPObject"; |
+ } |
+} |
+ |
+bool ChromeFrameNPAPI::ExecuteScript(const std::string& script, |
+ NPVariant* result) { |
+ NPObject* window = GetWindowObject(); |
+ if (!window) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ |
+ NPString script_for_execution; |
+ script_for_execution.UTF8Characters = script.c_str(); |
+ script_for_execution.UTF8Length = script.length(); |
+ |
+ return npapi::Evaluate(instance_, window, &script_for_execution, result); |
+} |
+ |
+NPObject* ChromeFrameNPAPI::JavascriptToNPObject(const std::string& script) { |
+ // Convert the passed in script to an invocable NPObject |
+ // To achieve this we save away the function in a dummy window property |
+ // which is then read to get the script object representing the function. |
+ |
+ std::string script_code = |
+ "javascript:window.__cf_get_function_object ="; |
+ |
+ // If we are able to look up the name in the javascript namespace, then it |
+ // means that the caller passed in a function name. Convert the function |
+ // name to a NPObject we can invoke on. |
+ if (IsValidJavascriptFunction(script)) { |
+ script_code += script; |
+ } else { |
+ script_code += "new Function(\""; |
+ script_code += script; |
+ script_code += "\");"; |
+ } |
+ |
+ NPVariant result; |
+ if (!ExecuteScript(script_code, &result)) { |
+ NOTREACHED(); |
+ return NULL; |
+ } |
+ |
+ DCHECK(result.type == NPVariantType_Object); |
+ DCHECK(result.value.objectValue != NULL); |
+ return result.value.objectValue; |
+} |
+ |
+bool ChromeFrameNPAPI::IsValidJavascriptFunction(const std::string& script) { |
+ std::string script_code = "javascript:window['"; |
+ script_code += script; |
+ script_code += "'];"; |
+ |
+ ScopedNpVariant result; |
+ if (!ExecuteScript(script_code, &result)) { |
+ NOTREACHED(); |
+ return NULL; |
+ } |
+ |
+ return result.type == NPVariantType_Object; |
+} |
+ |
+bool ChromeFrameNPAPI::NavigateToURL(const NPVariant* args, uint32_t arg_count, |
+ NPVariant* result) { |
+ // Note that 'result' might be NULL. |
+ if (arg_count != 1 || !(NPVARIANT_IS_STRING(args[0]) || |
+ NPVARIANT_IS_NULL(args[0]))) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ |
+ if (ready_state_ == READYSTATE_UNINITIALIZED) { |
+ // Error(L"Chrome Frame failed to initialize."); |
+ // TODO(tommi): call NPN_SetException |
+ DLOG(WARNING) << "NavigateToURL called after failed initialization"; |
+ return false; |
+ } |
+ |
+ std::string url("about:blank"); |
+ |
+ if (!NPVARIANT_IS_NULL(args[0])) { |
+ const NPString& str = args[0].value.stringValue; |
+ if (str.UTF8Length) { |
+ url.assign(std::string(str.UTF8Characters, str.UTF8Length)); |
+ } |
+ } |
+ DLOG(WARNING) << __FUNCTION__ << " " << url; |
+ std::string full_url = ResolveURL(GetDocumentUrl(), url); |
+ if (full_url.empty()) |
+ return false; |
+ |
+ src_ = full_url; |
+ // Navigate only if we completed initialization i.e. proxy is set etc. |
+ if (ready_state_ == READYSTATE_COMPLETE) { |
+ if (!automation_client_->InitiateNavigation(full_url)) { |
+ // TODO(tommi): call NPN_SetException. |
+ src_.clear(); |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+bool ChromeFrameNPAPI::postMessage(NPObject* npobject, const NPVariant* args, |
+ uint32_t arg_count, NPVariant* result) { |
+ if (arg_count < 1 || arg_count > 2 || !NPVARIANT_IS_STRING(args[0])) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ |
+ const NPString& str = args[0].value.stringValue; |
+ std::string message(str.UTF8Characters, str.UTF8Length); |
+ std::string target; |
+ if (arg_count == 2 && NPVARIANT_IS_STRING(args[1])) { |
+ const NPString& str = args[1].value.stringValue; |
+ target.assign(str.UTF8Characters, str.UTF8Length); |
+ if (target.compare("*") != 0) { |
+ GURL resolved(target); |
+ if (!resolved.is_valid()) { |
+ npapi::SetException(npobject, |
+ "Unable to parse the specified target URL."); |
+ return false; |
+ } |
+ target = resolved.spec(); |
+ } |
+ } else { |
+ target = "*"; |
+ } |
+ |
+ GURL url(GURL(document_url_).GetOrigin()); |
+ std::string origin(url.is_empty() ? "null" : url.spec()); |
+ |
+ automation_client_->ForwardMessageFromExternalHost(message, origin, target); |
+ |
+ return true; |
+} |
+ |
+bool ChromeFrameNPAPI::postPrivateMessage(NPObject* npobject, |
+ const NPVariant* args, |
+ uint32_t arg_count, |
+ NPVariant* result) { |
+ if (!is_privileged_) { |
+ DLOG(WARNING) << "postPrivateMessage invoked in non-privileged mode"; |
+ return false; |
+ } |
+ |
+ if (arg_count != 3 || !NPVARIANT_IS_STRING(args[0]) || |
+ !NPVARIANT_IS_STRING(args[1]) || !NPVARIANT_IS_STRING(args[2])) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ |
+ const NPString& message_str = args[0].value.stringValue; |
+ const NPString& origin_str = args[1].value.stringValue; |
+ const NPString& target_str = args[2].value.stringValue; |
+ std::string message(message_str.UTF8Characters, message_str.UTF8Length); |
+ std::string origin(origin_str.UTF8Characters, origin_str.UTF8Length); |
+ std::string target(target_str.UTF8Characters, target_str.UTF8Length); |
+ |
+ automation_client_->ForwardMessageFromExternalHost(message, origin, target); |
+ |
+ return true; |
+} |
+ |
+void ChromeFrameNPAPI::FireEvent(const std::string& event_type, |
+ const std::string& data) { |
+ NPVariant arg; |
+ STRINGN_TO_NPVARIANT(data.c_str(), data.length(), arg); |
+ FireEvent(event_type, arg); |
+} |
+ |
+void ChromeFrameNPAPI::FireEvent(const std::string& event_type, |
+ const NPVariant& data) { |
+ // Check that we're not bundling an event inside an event. |
+ // Right now we're only expecting simple types for the data argument. |
+ DCHECK(NPVARIANT_IS_OBJECT(data) == false); |
+ |
+ ScopedNpObject<NPObject> ev; |
+ CreateEvent(event_type, false, false, ev.Receive()); |
+ if (ev) { |
+ // Add the 'data' member to the event. |
+ bool set = npapi::SetProperty(instance_, ev, |
+ npapi::GetStringIdentifier("data"), const_cast<NPVariant*>(&data)); |
+ DCHECK(set); |
+ DispatchEvent(ev); |
+ } |
+} |
+ |
+NpProxyService* ChromeFrameNPAPI::CreatePrefService() { |
+ return new NpProxyService; |
+} |
+ |
+NPObject* ChromeFrameNPAPI::GetWindowObject() const { |
+ if (!window_object_.get()) { |
+ NPError ret = npapi::GetValue(instance_, NPNVWindowNPObject, |
+ window_object_.Receive()); |
+ DLOG_IF(ERROR, ret != NPERR_NO_ERROR) << "NPNVWindowNPObject failed"; |
+ } |
+ return window_object_; |
+} |
+ |
+bool ChromeFrameNPAPI::GetBrowserIncognitoMode() { |
+ bool incognito_mode = false; |
+ |
+ // Check disabled for Opera due to bug: http://b/issue?id=1815494 |
+ if (GetBrowserType() != BROWSER_OPERA) { |
+ // Check whether host browser is in private mode; |
+ NPBool private_mode = FALSE; |
+ NPError err = npapi::GetValue(instance_, |
+ NPNVprivateModeBool, |
+ &private_mode); |
+ if (err == NPERR_NO_ERROR && private_mode) { |
+ incognito_mode = true; |
+ } |
+ } else { |
+ DLOG(WARNING) << "Not checking for private mode in Opera"; |
+ } |
+ |
+ return incognito_mode; |
+} |
+ |
+NPAPIUrlRequest* ChromeFrameNPAPI::ValidateRequest( |
+ NPP instance, void* notify_data) { |
+ ChromeFrameNPAPI* plugin_instance = |
+ ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance); |
+ if (plugin_instance) { |
+ return plugin_instance->RequestFromNotifyData(notify_data); |
+ } |
+ |
+ return NULL; |
+} |
+ |
+NPAPIUrlRequest* ChromeFrameNPAPI::RequestFromNotifyData( |
+ void* notify_data) const { |
+ NPAPIUrlRequest* request = reinterpret_cast<NPAPIUrlRequest*>(notify_data); |
+ DCHECK(request ? automation_client_->IsValidRequest(request) : 1); |
+ return request; |
+} |
+ |
+bool ChromeFrameNPAPI::HandleContextMenuCommand(UINT cmd) { |
+ if (cmd == IDC_ABOUT_CHROME_FRAME) { |
+ // TODO: implement "About Chrome Frame" |
+ } |
+ return false; |
+} |