| 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;
|
| +}
|
|
|