OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome_frame/chrome_frame_npapi.h" |
| 6 |
| 7 #include "base/basictypes.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/message_loop.h" |
| 10 #include "base/string_util.h" |
| 11 #include "base/win_util.h" |
| 12 #include "chrome/test/automation/tab_proxy.h" |
| 13 #include "chrome_frame/ff_privilege_check.h" |
| 14 #include "chrome_frame/utils.h" |
| 15 |
| 16 MessageLoop* ChromeFrameNPAPI::message_loop_ = NULL; |
| 17 int ChromeFrameNPAPI::instance_count_ = 0; |
| 18 |
| 19 static const char* kNpEventNames[] = { |
| 20 "focus", |
| 21 "blur", |
| 22 }; |
| 23 |
| 24 NPClass ChromeFrameNPAPI::plugin_class_ = { |
| 25 NP_CLASS_STRUCT_VERSION, |
| 26 ChromeFrameNPAPI::AllocateObject, |
| 27 ChromeFrameNPAPI::DeallocateObject, |
| 28 ChromeFrameNPAPI::Invalidate, |
| 29 ChromeFrameNPAPI::HasMethod, |
| 30 ChromeFrameNPAPI::Invoke, |
| 31 NULL, // invokeDefault |
| 32 ChromeFrameNPAPI::HasProperty, |
| 33 ChromeFrameNPAPI::GetProperty, |
| 34 ChromeFrameNPAPI::SetProperty, |
| 35 NULL, // remove property |
| 36 NULL, // enumeration |
| 37 NULL, // construct |
| 38 }; |
| 39 |
| 40 NPIdentifier |
| 41 ChromeFrameNPAPI::plugin_property_identifiers_[PLUGIN_PROPERTY_COUNT] |
| 42 = {0}; |
| 43 |
| 44 const NPUTF8* ChromeFrameNPAPI::plugin_property_identifier_names_[] = { |
| 45 "version", |
| 46 "src", |
| 47 "onload", |
| 48 "onloaderror", |
| 49 "onmessage", |
| 50 "readystate", |
| 51 "onprivatemessage", |
| 52 "usechromenetwork", |
| 53 }; |
| 54 |
| 55 const NPUTF8* ChromeFrameNPAPI::plugin_method_identifier_names_[] = { |
| 56 "postMessage", |
| 57 "postPrivateMessage", |
| 58 }; |
| 59 |
| 60 ChromeFrameNPAPI::PluginMethod ChromeFrameNPAPI::plugin_methods_[] = { |
| 61 &ChromeFrameNPAPI::postMessage, |
| 62 &ChromeFrameNPAPI::postPrivateMessage, |
| 63 }; |
| 64 |
| 65 NPIdentifier |
| 66 ChromeFrameNPAPI::plugin_method_identifiers_[arraysize(plugin_methods_)] |
| 67 = {0}; |
| 68 |
| 69 |
| 70 void ChromeFrameNPAPI::CompileAsserts() { |
| 71 NOTREACHED(); // This function should never be invoked. |
| 72 |
| 73 COMPILE_ASSERT(arraysize(plugin_method_identifier_names_) == |
| 74 arraysize(plugin_methods_), |
| 75 you_must_add_both_plugin_method_and_name); |
| 76 |
| 77 COMPILE_ASSERT(arraysize(plugin_property_identifier_names_) == |
| 78 arraysize(plugin_property_identifiers_), |
| 79 you_must_add_both_plugin_property_and_name); |
| 80 } |
| 81 |
| 82 static const int kMaxBytesForPluginConsumption = 0x7FFFFFFF; |
| 83 |
| 84 static const char kPluginSrcAttribute[] = "src"; |
| 85 static const char kPluginForceFullPageAttribute[] = "force_full_page"; |
| 86 static const char kPluginOnloadAttribute[] = "onload"; |
| 87 static const char kPluginOnErrorAttribute[] = "onloaderror"; |
| 88 static const char kPluginOnMessageAttribute[] = "onmessage"; |
| 89 static const char kPluginOnPrivateMessageAttribute[] = "onprivatemessage"; |
| 90 // These properties can only be set in arguments at control instantiation. |
| 91 // When the privileged_mode property is provided and set to true, the control |
| 92 // will probe for whether its hosting document has the system principal, in |
| 93 // which case privileged mode will be enabled. |
| 94 static const char kPluginPrivilegedModeAttribute[] = "privileged_mode"; |
| 95 // If privileged mode is enabled, the string value of this argument will |
| 96 // be appended to the chrome.exe command line. |
| 97 static const char kPluginChromeExtraArguments[] = "chrome_extra_arguments"; |
| 98 // If privileged mode is enabled, the string value of this argument will |
| 99 // be used as the profile name for our chrome.exe instance. |
| 100 static const char kPluginChromeProfileName[] = "chrome_profile_name"; |
| 101 // If chrome network stack is to be used |
| 102 static const char kPluginUseChromeNetwork[] = "usechromenetwork"; |
| 103 |
| 104 |
| 105 NPError NPP_New(NPMIMEType plugin_type, NPP instance, uint16 mode, int16 argc, |
| 106 char* argn[], char* argv[], NPSavedData* saved) { |
| 107 if (instance == NULL) |
| 108 return NPERR_INVALID_INSTANCE_ERROR; |
| 109 |
| 110 ChromeFrameNPAPI::ChromeFrameNPObject* chrome_frame_npapi_obj = |
| 111 reinterpret_cast<ChromeFrameNPAPI::ChromeFrameNPObject*>( |
| 112 npapi::CreateObject(instance, ChromeFrameNPAPI::PluginClass())); |
| 113 DCHECK(chrome_frame_npapi_obj != NULL); |
| 114 |
| 115 ChromeFrameNPAPI* plugin_instance = |
| 116 chrome_frame_npapi_obj->chrome_frame_plugin_instance; |
| 117 DCHECK(plugin_instance != NULL); |
| 118 |
| 119 // Note that we MUST set instance->pdata BEFORE calling Initialize. This is |
| 120 // because Initialize can call back into the NPAPI host which will need the |
| 121 // pdata field to be set. |
| 122 chrome_frame_npapi_obj->chrome_frame_plugin_instance = |
| 123 plugin_instance; |
| 124 instance->pdata = chrome_frame_npapi_obj; |
| 125 |
| 126 bool init = plugin_instance->Initialize(plugin_type, instance, |
| 127 mode, argc, argn, argv); |
| 128 DCHECK(init); |
| 129 |
| 130 return NPERR_NO_ERROR; |
| 131 } |
| 132 |
| 133 NPError NPP_Destroy(NPP instance, NPSavedData** save) { |
| 134 // Takes ownership and releases the object at the end of scope. |
| 135 ScopedNpObject<ChromeFrameNPAPI::ChromeFrameNPObject> chrome_frame_npapi_obj( |
| 136 reinterpret_cast<ChromeFrameNPAPI::ChromeFrameNPObject*>( |
| 137 instance->pdata)); |
| 138 |
| 139 if (chrome_frame_npapi_obj.get()) { |
| 140 ChromeFrameNPAPI* plugin_instance = |
| 141 ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance); |
| 142 |
| 143 plugin_instance->Uninitialize(); |
| 144 instance->pdata = NULL; |
| 145 } |
| 146 |
| 147 return NPERR_NO_ERROR; |
| 148 } |
| 149 |
| 150 NPError NPP_SetWindow(NPP instance, NPWindow* window_info) { |
| 151 if (window_info == NULL) { |
| 152 NOTREACHED(); |
| 153 return NPERR_GENERIC_ERROR; |
| 154 } |
| 155 |
| 156 ChromeFrameNPAPI* plugin_instance = |
| 157 ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance); |
| 158 |
| 159 if (plugin_instance == NULL) { |
| 160 return NPERR_INVALID_INSTANCE_ERROR; |
| 161 } |
| 162 |
| 163 plugin_instance->SetWindow(window_info); |
| 164 return NPERR_NO_ERROR; |
| 165 } |
| 166 |
| 167 NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, |
| 168 NPBool seekable, uint16* stream_type) { |
| 169 NPAPIUrlRequest* url_request = ChromeFrameNPAPI::ValidateRequest( |
| 170 instance, stream->notifyData); |
| 171 if (url_request) { |
| 172 if (!url_request->OnStreamCreated(type, stream)) |
| 173 return NPERR_GENERIC_ERROR; |
| 174 } |
| 175 |
| 176 // We need to return the requested stream mode if we are returning a success |
| 177 // code. If we don't do this it causes Opera to blow up. |
| 178 *stream_type = NP_NORMAL; |
| 179 return NPERR_NO_ERROR; |
| 180 } |
| 181 |
| 182 NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason) { |
| 183 NPAPIUrlRequest* url_request = ChromeFrameNPAPI::ValidateRequest( |
| 184 instance, stream->notifyData); |
| 185 if (url_request) { |
| 186 url_request->OnStreamDestroyed(reason); |
| 187 } |
| 188 |
| 189 return NPERR_NO_ERROR; |
| 190 } |
| 191 |
| 192 NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value) { |
| 193 if (variable == NPPVpluginScriptableNPObject) { |
| 194 void** plugin = reinterpret_cast<void**>(value); |
| 195 ChromeFrameNPAPI::ChromeFrameNPObject* chrome_frame_npapi_obj = |
| 196 reinterpret_cast<ChromeFrameNPAPI::ChromeFrameNPObject*>( |
| 197 instance->pdata); |
| 198 // Return value is expected to be retained |
| 199 npapi::RetainObject(reinterpret_cast<NPObject*>(chrome_frame_npapi_obj)); |
| 200 *plugin = chrome_frame_npapi_obj; |
| 201 return NPERR_NO_ERROR; |
| 202 } |
| 203 return NPERR_GENERIC_ERROR; |
| 204 } |
| 205 |
| 206 NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value) { |
| 207 return NPERR_GENERIC_ERROR; |
| 208 } |
| 209 |
| 210 int32 NPP_WriteReady(NPP instance, NPStream* stream) { |
| 211 NPAPIUrlRequest* url_request = ChromeFrameNPAPI::ValidateRequest( |
| 212 instance, stream->notifyData); |
| 213 if (url_request) { |
| 214 return url_request->OnWriteReady(); |
| 215 } |
| 216 |
| 217 return kMaxBytesForPluginConsumption; |
| 218 } |
| 219 |
| 220 int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, |
| 221 void* buffer) { |
| 222 NPAPIUrlRequest* url_request = ChromeFrameNPAPI::ValidateRequest( |
| 223 instance, stream->notifyData); |
| 224 if (url_request) { |
| 225 return url_request->OnWrite(buffer, len); |
| 226 } |
| 227 |
| 228 return len; |
| 229 } |
| 230 |
| 231 void NPP_URLNotify(NPP instance, const char* url, NPReason reason, |
| 232 void* notifyData) { |
| 233 ChromeFrameNPAPI* plugin_instance = |
| 234 ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance); |
| 235 if (plugin_instance) { |
| 236 plugin_instance->UrlNotify(url, reason, notifyData); |
| 237 } |
| 238 } |
| 239 |
| 240 void NPP_Print(NPP instance, NPPrint* print_info) { |
| 241 ChromeFrameNPAPI* plugin_instance = |
| 242 ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance); |
| 243 |
| 244 if (plugin_instance == NULL) { |
| 245 NOTREACHED(); |
| 246 return; |
| 247 } |
| 248 |
| 249 plugin_instance->Print(print_info); |
| 250 } |
| 251 |
| 252 // ChromeFrameNPAPI member defines. |
| 253 |
| 254 // TODO(tommi): remove ignore_setfocus_ since that's not how focus is |
| 255 // handled anymore. |
| 256 |
| 257 ChromeFrameNPAPI::ChromeFrameNPAPI() |
| 258 : instance_(NULL), |
| 259 mode_(NP_EMBED), |
| 260 force_full_page_plugin_(false), |
| 261 ready_state_(READYSTATE_LOADING), |
| 262 enabled_popups_(false) { |
| 263 } |
| 264 |
| 265 ChromeFrameNPAPI::~ChromeFrameNPAPI() { |
| 266 if (IsWindow()) { |
| 267 if (!UnsubclassWindow()) { |
| 268 // TODO(tommi): Figure out why this can sometimes happen in the |
| 269 // WidgetModeFF_Resize unittest. |
| 270 DLOG(ERROR) << "Couldn't unsubclass safely!"; |
| 271 UnsubclassWindow(TRUE); |
| 272 } |
| 273 } |
| 274 m_hWnd = NULL; |
| 275 |
| 276 instance_count_--; |
| 277 if (instance_count_ <= 0) { |
| 278 delete message_loop_; |
| 279 message_loop_ = NULL; |
| 280 } |
| 281 |
| 282 Uninitialize(); |
| 283 } |
| 284 |
| 285 std::string GetLocation(NPP instance, NPObject* window) { |
| 286 if (!window) { |
| 287 // Can fail if the browser is closing (seen in Opera). |
| 288 return ""; |
| 289 } |
| 290 |
| 291 std::string result; |
| 292 ScopedNpVariant href; |
| 293 ScopedNpVariant location; |
| 294 |
| 295 bool ok = npapi::GetProperty(instance, window, |
| 296 npapi::GetStringIdentifier("location"), &location); |
| 297 DCHECK(ok); |
| 298 DCHECK(location.type == NPVariantType_Object); |
| 299 |
| 300 if (ok) { |
| 301 ok = npapi::GetProperty(instance, |
| 302 location.value.objectValue, |
| 303 npapi::GetStringIdentifier("href"), |
| 304 &href); |
| 305 DCHECK(ok); |
| 306 DCHECK(href.type == NPVariantType_String); |
| 307 if (ok) { |
| 308 result.assign(href.value.stringValue.UTF8Characters, |
| 309 href.value.stringValue.UTF8Length); |
| 310 } |
| 311 } |
| 312 |
| 313 return result; |
| 314 } |
| 315 |
| 316 std::string ChromeFrameNPAPI::GetLocation() { |
| 317 // Note that GetWindowObject() will cache the window object here. |
| 318 return ::GetLocation(instance_, GetWindowObject()); |
| 319 } |
| 320 |
| 321 bool ChromeFrameNPAPI::Initialize(NPMIMEType mime_type, NPP instance, |
| 322 uint16 mode, int16 argc, char* argn[], |
| 323 char* argv[]) { |
| 324 if (!Base::Initialize()) |
| 325 return false; |
| 326 |
| 327 instance_ = instance; |
| 328 mime_type_ = mime_type; |
| 329 mode_ = mode; |
| 330 document_url_ = GetLocation(); |
| 331 |
| 332 if (instance_count_ == 0) { |
| 333 DCHECK(message_loop_ == NULL); |
| 334 message_loop_ = new MessageLoop(); |
| 335 } |
| 336 |
| 337 instance_count_++; |
| 338 |
| 339 // Create our prefs service wrapper here. |
| 340 DCHECK(!pref_service_.get()); |
| 341 pref_service_ = CreatePrefService(); |
| 342 if (!pref_service_.get()) { |
| 343 NOTREACHED() << "new NpProxyService"; |
| 344 return false; |
| 345 } |
| 346 |
| 347 // Temporary variables for privileged only parameters |
| 348 const char* onprivatemessage_arg = NULL; |
| 349 const char* chrome_extra_arguments_arg = NULL; |
| 350 const char* chrome_profile_name_arg = NULL; |
| 351 bool chrome_network_arg_set = false; |
| 352 bool chrome_network_arg = false; |
| 353 bool wants_privileged = false; |
| 354 |
| 355 for (int i = 0; i < argc; ++i) { |
| 356 if (LowerCaseEqualsASCII(argn[i], kPluginSrcAttribute)) { |
| 357 src_ = ResolveURL(GetDocumentUrl(), argv[i]); |
| 358 } else if (LowerCaseEqualsASCII(argn[i], kPluginForceFullPageAttribute)) { |
| 359 force_full_page_plugin_ = atoi(argv[i]) ? true : false; |
| 360 } else if (LowerCaseEqualsASCII(argn[i], kPluginOnErrorAttribute)) { |
| 361 onerror_handler_ = JavascriptToNPObject(argv[i]); |
| 362 } else if (LowerCaseEqualsASCII(argn[i], kPluginOnMessageAttribute)) { |
| 363 onmessage_handler_ = JavascriptToNPObject(argv[i]); |
| 364 } else if (LowerCaseEqualsASCII(argn[i], |
| 365 kPluginPrivilegedModeAttribute)) { |
| 366 // Test for the FireFox privileged mode if the user requests it |
| 367 // in initialization parameters. |
| 368 wants_privileged = atoi(argv[i]) ? true : false; |
| 369 } else if (LowerCaseEqualsASCII(argn[i], |
| 370 kPluginOnPrivateMessageAttribute)) { |
| 371 onprivatemessage_arg = argv[i]; |
| 372 } else if (LowerCaseEqualsASCII(argn[i], kPluginChromeExtraArguments)) { |
| 373 chrome_extra_arguments_arg = argv[i]; |
| 374 } else if (LowerCaseEqualsASCII(argn[i], kPluginChromeProfileName)) { |
| 375 chrome_profile_name_arg = argv[i]; |
| 376 } else if (LowerCaseEqualsASCII(argn[i], kPluginUseChromeNetwork)) { |
| 377 chrome_network_arg_set = true; |
| 378 chrome_network_arg = atoi(argv[i]) ? true : false; |
| 379 } |
| 380 } |
| 381 |
| 382 // Is the privileged mode requested? |
| 383 if (wants_privileged) { |
| 384 is_privileged_ = IsFireFoxPrivilegedInvocation(instance); |
| 385 if (!is_privileged_) { |
| 386 DLOG(WARNING) << "Privileged mode requested in non-privileged context"; |
| 387 } |
| 388 } |
| 389 |
| 390 std::wstring extra_arguments; |
| 391 std::wstring profile_name(GetHostProcessName(false)); |
| 392 if (is_privileged_) { |
| 393 // Process any privileged mode-only arguments we were handed. |
| 394 if (onprivatemessage_arg) |
| 395 onprivatemessage_handler_ = JavascriptToNPObject(onprivatemessage_arg); |
| 396 |
| 397 if (chrome_extra_arguments_arg) |
| 398 extra_arguments = UTF8ToWide(chrome_extra_arguments_arg); |
| 399 |
| 400 if (chrome_profile_name_arg) |
| 401 profile_name = UTF8ToWide(chrome_profile_name_arg); |
| 402 |
| 403 if (chrome_network_arg_set) |
| 404 automation_client_->set_use_chrome_network(chrome_network_arg); |
| 405 |
| 406 } |
| 407 |
| 408 // TODO(joshia): Initialize navigation here and send proxy config as |
| 409 // part of LaunchSettings |
| 410 /* |
| 411 if (!src_.empty()) |
| 412 automation_client_->InitiateNavigation(src_); |
| 413 |
| 414 std::string proxy_settings; |
| 415 bool has_prefs = pref_service_->Initialize(instance_, |
| 416 automation_client_.get()); |
| 417 if (has_prefs && pref_service_->GetProxyValueJSONString(&proxy_settings)) { |
| 418 automation_client_->SetProxySettings(proxy_settings); |
| 419 } |
| 420 */ |
| 421 |
| 422 // We can't call SubscribeToFocusEvents here since |
| 423 // when Initialize gets called, Opera is in a state where |
| 424 // it can't handle calls back and the thread will hang. |
| 425 // Instead, we call SubscribeToFocusEvents when we initialize |
| 426 // our plugin window. |
| 427 |
| 428 // TODO(stoyan): Ask host for specific interface whether to honor |
| 429 // host's in-private mode. |
| 430 return InitializeAutomation(profile_name, extra_arguments, |
| 431 GetBrowserIncognitoMode()); |
| 432 } |
| 433 |
| 434 void ChromeFrameNPAPI::Uninitialize() { |
| 435 // Don't call SetReadyState as it will end up calling FireEvent. |
| 436 // We are in the context of NPP_DESTROY. |
| 437 ready_state_ = READYSTATE_UNINITIALIZED; |
| 438 |
| 439 UnsubscribeFromFocusEvents(); |
| 440 |
| 441 if (pref_service_) { |
| 442 pref_service_->UnInitialize(); |
| 443 pref_service_ = NULL; |
| 444 } |
| 445 |
| 446 window_object_.Free(); |
| 447 onerror_handler_.Free(); |
| 448 onmessage_handler_.Free(); |
| 449 onprivatemessage_handler_.Free(); |
| 450 |
| 451 Base::Uninitialize(); |
| 452 } |
| 453 |
| 454 void ChromeFrameNPAPI::OnFinalMessage(HWND window) { |
| 455 // The automation server should be gone by now. |
| 456 Uninitialize(); |
| 457 } |
| 458 |
| 459 void ChromeFrameNPAPI::SubscribeToFocusEvents() { |
| 460 DCHECK(focus_listener_.get() == NULL); |
| 461 |
| 462 focus_listener_ = new DomEventListener(this); |
| 463 if (!focus_listener_->Subscribe(instance_, kNpEventNames, |
| 464 arraysize(kNpEventNames))) { |
| 465 focus_listener_ = NULL; |
| 466 focus_listener_ = new NPObjectEventListener(this); |
| 467 if (!focus_listener_->Subscribe(instance_, kNpEventNames, |
| 468 arraysize(kNpEventNames))) { |
| 469 DLOG(ERROR) << "Failed to subscribe to focus events"; |
| 470 focus_listener_ = NULL; |
| 471 } |
| 472 } |
| 473 } |
| 474 |
| 475 void ChromeFrameNPAPI::UnsubscribeFromFocusEvents() { |
| 476 if (!focus_listener_.get()) |
| 477 return; |
| 478 |
| 479 bool ret = focus_listener_->Unsubscribe(instance_, kNpEventNames, |
| 480 arraysize(kNpEventNames)); |
| 481 DLOG_IF(WARNING, !ret) << "focus_listener_->Unsubscribe failed"; |
| 482 focus_listener_ = NULL; |
| 483 } |
| 484 |
| 485 bool ChromeFrameNPAPI::SetWindow(NPWindow* window_info) { |
| 486 if (!window_info || !automation_client_.get()) { |
| 487 NOTREACHED(); |
| 488 return false; |
| 489 } |
| 490 |
| 491 HWND window = reinterpret_cast<HWND>(window_info->window); |
| 492 if (!::IsWindow(window)) { |
| 493 // No window created yet. Ignore this call. |
| 494 return false; |
| 495 } |
| 496 |
| 497 if (IsWindow()) { |
| 498 // We've already subclassed, make sure that SetWindow doesn't get called |
| 499 // with an HWND other than the one we subclassed during our lifetime. |
| 500 DCHECK(window == m_hWnd); |
| 501 return true; |
| 502 } |
| 503 |
| 504 automation_client_->SetParentWindow(window); |
| 505 |
| 506 SubscribeToFocusEvents(); |
| 507 |
| 508 if (force_full_page_plugin_) { |
| 509 // By default full page mode is only enabled when the plugin is loaded off |
| 510 // a separate file, i.e. it is the primary content in the window. Even if |
| 511 // we specify the width/height attributes for the plugin as 100% each, FF |
| 512 // instantiates the plugin passing in a width/height of 100px each. To |
| 513 // workaround this we resize the plugin window passed in by FF to the size |
| 514 // of its parent. |
| 515 HWND plugin_parent_window = ::GetParent(window); |
| 516 RECT plugin_parent_rect = {0}; |
| 517 ::GetClientRect(plugin_parent_window, &plugin_parent_rect); |
| 518 ::SetWindowPos(window, NULL, plugin_parent_rect.left, |
| 519 plugin_parent_rect.top, |
| 520 plugin_parent_rect.right - plugin_parent_rect.left, |
| 521 plugin_parent_rect.bottom - plugin_parent_rect.top, 0); |
| 522 } |
| 523 |
| 524 // Subclass the browser's plugin window here. |
| 525 if (SubclassWindow(window)) { |
| 526 DWORD new_style_flags = WS_CLIPCHILDREN; |
| 527 ModifyStyle(0, new_style_flags, 0); |
| 528 |
| 529 if (ready_state_ < READYSTATE_INTERACTIVE) { |
| 530 SetReadyState(READYSTATE_INTERACTIVE); |
| 531 } |
| 532 } |
| 533 |
| 534 return true; |
| 535 } |
| 536 |
| 537 void ChromeFrameNPAPI::Print(NPPrint* print_info) { |
| 538 if (!print_info) { |
| 539 NOTREACHED(); |
| 540 return; |
| 541 } |
| 542 |
| 543 // We dont support full tab mode yet. |
| 544 if (print_info->mode != NP_EMBED) { |
| 545 NOTREACHED(); |
| 546 return; |
| 547 } |
| 548 |
| 549 NPWindow window = print_info->print.embedPrint.window; |
| 550 |
| 551 RECT print_bounds = {0}; |
| 552 print_bounds.left = window.x; |
| 553 print_bounds.top = window.y; |
| 554 print_bounds.right = window.x + window.width; |
| 555 print_bounds.bottom = window.x + window.height; |
| 556 |
| 557 automation_client_->Print( |
| 558 reinterpret_cast<HDC>(print_info->print.embedPrint.platformPrint), |
| 559 print_bounds); |
| 560 } |
| 561 |
| 562 void ChromeFrameNPAPI::UrlNotify(const char* url, NPReason reason, |
| 563 void* notify_data) { |
| 564 if (enabled_popups_) { |
| 565 // We have opened the URL so tell the browser to restore popup settings |
| 566 enabled_popups_ = false; |
| 567 npapi::PopPopupsEnabledState(instance_); |
| 568 } |
| 569 |
| 570 // It is now safe to release the additional reference on the request |
| 571 NPAPIUrlRequest* request = RequestFromNotifyData(notify_data); |
| 572 if (request) { |
| 573 request->Stop(); |
| 574 request->Release(); |
| 575 } |
| 576 } |
| 577 |
| 578 void ChromeFrameNPAPI::OnAcceleratorPressed(int tab_handle, |
| 579 const MSG& accel_message) { |
| 580 DLOG(INFO) << __FUNCTION__ << " msg:" |
| 581 << StringPrintf("0x%04X", accel_message.message) << " key:" |
| 582 << accel_message.wParam; |
| 583 |
| 584 // The host browser does call TranslateMessage on messages like WM_KEYDOWN |
| 585 // WM_KEYUP, etc, which will result in messages like WM_CHAR, WM_SYSCHAR, etc |
| 586 // being posted to the message queue. We don't post these messages here to |
| 587 // avoid these messages from getting handled twice. |
| 588 if (accel_message.message != WM_CHAR && |
| 589 accel_message.message != WM_DEADCHAR && |
| 590 accel_message.message != WM_SYSCHAR && |
| 591 accel_message.message != WM_SYSDEADCHAR) { |
| 592 // A very primitive way to handle keystrokes. |
| 593 // TODO(tommi): When we've implemented a way for chrome to |
| 594 // know when keystrokes are handled (deterministically) on that side, |
| 595 // then this function should get called and not otherwise. |
| 596 ::PostMessage(::GetParent(m_hWnd), accel_message.message, |
| 597 accel_message.wParam, accel_message.lParam); |
| 598 } |
| 599 |
| 600 if (automation_client_.get()) { |
| 601 TabProxy* tab = automation_client_->tab(); |
| 602 if (tab) { |
| 603 tab->ProcessUnhandledAccelerator(accel_message); |
| 604 } |
| 605 } |
| 606 } |
| 607 |
| 608 void ChromeFrameNPAPI::OnTabbedOut(int tab_handle, bool reverse) { |
| 609 DLOG(INFO) << __FUNCTION__; |
| 610 |
| 611 ignore_setfocus_ = true; |
| 612 HWND parent = ::GetParent(m_hWnd); |
| 613 ::SetFocus(parent); |
| 614 |
| 615 INPUT input = {0}; |
| 616 input.type = INPUT_KEYBOARD; |
| 617 input.ki.wVk = VK_TAB; |
| 618 SendInput(1, &input, sizeof(input)); |
| 619 input.ki.dwFlags = KEYEVENTF_KEYUP; |
| 620 SendInput(1, &input, sizeof(input)); |
| 621 |
| 622 ignore_setfocus_ = false; |
| 623 } |
| 624 |
| 625 void ChromeFrameNPAPI::OnOpenURL(int tab_handle, |
| 626 const GURL& url, int open_disposition) { |
| 627 std::string target; |
| 628 switch (open_disposition) { |
| 629 case NEW_FOREGROUND_TAB: |
| 630 target = "_blank"; |
| 631 break; |
| 632 case NEW_BACKGROUND_TAB: |
| 633 target = "_blank"; |
| 634 break; |
| 635 case NEW_WINDOW: |
| 636 target = "_new"; |
| 637 break; |
| 638 default: |
| 639 break; |
| 640 } |
| 641 |
| 642 // Tell the browser to temporarily allow popups |
| 643 enabled_popups_ = true; |
| 644 npapi::PushPopupsEnabledState(instance_, TRUE); |
| 645 npapi::GetURLNotify(instance_, url.spec().c_str(), target.c_str(), NULL); |
| 646 } |
| 647 |
| 648 void ChromeFrameNPAPI::OnRequestStart(int tab_handle, int request_id, |
| 649 const IPC::AutomationURLRequest& request) { |
| 650 scoped_refptr<NPAPIUrlRequest> new_request(new NPAPIUrlRequest(instance_)); |
| 651 DCHECK(new_request); |
| 652 if (new_request->Initialize(automation_client_.get(), tab_handle, |
| 653 request_id, request.url, request.method, |
| 654 request.referrer, request.extra_request_headers, |
| 655 request.upload_data.get(), true)) { |
| 656 if (new_request->Start()) { |
| 657 // Keep additional reference on request for NPSTREAM |
| 658 // This will be released in NPP_UrlNotify |
| 659 new_request->AddRef(); |
| 660 } |
| 661 } |
| 662 } |
| 663 |
| 664 void ChromeFrameNPAPI::OnRequestRead(int tab_handle, int request_id, |
| 665 int bytes_to_read) { |
| 666 automation_client_->ReadRequest(request_id, bytes_to_read); |
| 667 } |
| 668 |
| 669 void ChromeFrameNPAPI::OnRequestEnd(int tab_handle, int request_id, |
| 670 const URLRequestStatus& status) { |
| 671 automation_client_->RemoveRequest(request_id, status.status(), true); |
| 672 } |
| 673 |
| 674 void ChromeFrameNPAPI::OnSetCookieAsync(int tab_handle, const GURL& url, |
| 675 const std::string& cookie) { |
| 676 // Use the newer NPAPI way if available |
| 677 if (npapi::VersionMinor() >= NPVERS_HAS_URL_AND_AUTH_INFO) { |
| 678 npapi::SetValueForURL(instance_, NPNURLVCookie, url.spec().c_str(), |
| 679 cookie.c_str(), cookie.length()); |
| 680 } else if (url == GURL(document_url_)) { |
| 681 std::string script = "javascript:document.cookie="; |
| 682 script.append(cookie); |
| 683 script.append(1, ';'); |
| 684 ExecuteScript(script, NULL); |
| 685 } else { |
| 686 // Third party cookie, use nsICookieService to set the cookie. |
| 687 NOTREACHED(); |
| 688 } |
| 689 } |
| 690 |
| 691 bool ChromeFrameNPAPI::HasMethod(NPObject* obj, NPIdentifier name) { |
| 692 for (int i = 0; i < arraysize(plugin_methods_); ++i) { |
| 693 if (name == plugin_method_identifiers_[i]) |
| 694 return true; |
| 695 } |
| 696 |
| 697 return false; |
| 698 } |
| 699 |
| 700 bool ChromeFrameNPAPI::Invoke(NPObject* header, NPIdentifier name, |
| 701 const NPVariant* args, uint32_t arg_count, |
| 702 NPVariant* result) { |
| 703 ChromeFrameNPAPI* plugin_instance = ChromeFrameInstanceFromNPObject(header); |
| 704 if (!plugin_instance && (plugin_instance->automation_client_.get())) |
| 705 return false; |
| 706 |
| 707 bool success = false; |
| 708 for (int i = 0; i < arraysize(plugin_methods_); ++i) { |
| 709 if (name == plugin_method_identifiers_[i]) { |
| 710 PluginMethod method = plugin_methods_[i]; |
| 711 success = (plugin_instance->*method)(header, args, arg_count, result); |
| 712 break; |
| 713 } |
| 714 } |
| 715 |
| 716 return success; |
| 717 } |
| 718 |
| 719 void ChromeFrameNPAPI::InitializeIdentifiers() { |
| 720 npapi::GetStringIdentifiers(plugin_method_identifier_names_, |
| 721 arraysize(plugin_methods_), |
| 722 plugin_method_identifiers_); |
| 723 |
| 724 npapi::GetStringIdentifiers(plugin_property_identifier_names_, |
| 725 PLUGIN_PROPERTY_COUNT, |
| 726 plugin_property_identifiers_); |
| 727 } |
| 728 |
| 729 NPObject* ChromeFrameNPAPI::AllocateObject(NPP instance, NPClass* class_name) { |
| 730 static bool identifiers_initialized = false; |
| 731 |
| 732 ChromeFrameNPObject* plugin_object = new ChromeFrameNPObject(); |
| 733 DCHECK(plugin_object != NULL); |
| 734 |
| 735 plugin_object->chrome_frame_plugin_instance = new ChromeFrameNPAPI(); |
| 736 DCHECK(plugin_object->chrome_frame_plugin_instance != NULL); |
| 737 |
| 738 plugin_object->npp = NULL; |
| 739 |
| 740 COMPILE_ASSERT(arraysize(plugin_method_identifiers_) == |
| 741 arraysize(plugin_method_identifier_names_), |
| 742 method_count_mismatch); |
| 743 |
| 744 COMPILE_ASSERT(arraysize(plugin_method_identifiers_) == |
| 745 arraysize(plugin_methods_), |
| 746 method_count_mismatch); |
| 747 |
| 748 if (!identifiers_initialized) { |
| 749 InitializeIdentifiers(); |
| 750 identifiers_initialized = true; |
| 751 } |
| 752 |
| 753 return reinterpret_cast<NPObject*>(plugin_object); |
| 754 } |
| 755 |
| 756 void ChromeFrameNPAPI::DeallocateObject(NPObject* header) { |
| 757 ChromeFrameNPObject* plugin_object = |
| 758 reinterpret_cast<ChromeFrameNPObject*>(header); |
| 759 DCHECK(plugin_object != NULL); |
| 760 |
| 761 if (plugin_object) { |
| 762 delete plugin_object->chrome_frame_plugin_instance; |
| 763 delete plugin_object; |
| 764 } |
| 765 } |
| 766 |
| 767 void ChromeFrameNPAPI::Invalidate(NPObject* header) { |
| 768 DCHECK(header); |
| 769 ChromeFrameNPObject* plugin_object = |
| 770 reinterpret_cast<ChromeFrameNPObject*>(header); |
| 771 if (plugin_object) { |
| 772 DCHECK(plugin_object->chrome_frame_plugin_instance); |
| 773 plugin_object->chrome_frame_plugin_instance->Uninitialize(); |
| 774 } |
| 775 } |
| 776 |
| 777 ChromeFrameNPAPI* ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance( |
| 778 NPP instance) { |
| 779 if ((instance == NULL) || (instance->pdata == NULL)) { |
| 780 NOTREACHED(); |
| 781 return NULL; |
| 782 } |
| 783 |
| 784 return ChromeFrameInstanceFromNPObject(instance->pdata); |
| 785 } |
| 786 |
| 787 ChromeFrameNPAPI* ChromeFrameNPAPI::ChromeFrameInstanceFromNPObject( |
| 788 void* object) { |
| 789 ChromeFrameNPObject* plugin_object = |
| 790 reinterpret_cast<ChromeFrameNPObject*>(object); |
| 791 if (!plugin_object) { |
| 792 NOTREACHED(); |
| 793 return NULL; |
| 794 } |
| 795 |
| 796 DCHECK(plugin_object->chrome_frame_plugin_instance); |
| 797 return plugin_object->chrome_frame_plugin_instance; |
| 798 } |
| 799 |
| 800 bool ChromeFrameNPAPI::HasProperty(NPObject* obj, NPIdentifier name) { |
| 801 for (int i = 0; i < PLUGIN_PROPERTY_COUNT; ++i) { |
| 802 if (name == plugin_property_identifiers_[i]) |
| 803 return true; |
| 804 } |
| 805 return false; |
| 806 } |
| 807 |
| 808 bool ChromeFrameNPAPI::GetProperty(NPIdentifier name, |
| 809 NPVariant* variant) { |
| 810 if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_ONERROR]) { |
| 811 if (onerror_handler_) { |
| 812 variant->type = NPVariantType_Object; |
| 813 variant->value.objectValue = onerror_handler_.Copy(); |
| 814 return true; |
| 815 } |
| 816 } else if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_ONMESSAGE]) { |
| 817 if (onmessage_handler_) { |
| 818 variant->type = NPVariantType_Object; |
| 819 variant->value.objectValue = onmessage_handler_.Copy(); |
| 820 return true; |
| 821 } |
| 822 } else if (name == |
| 823 plugin_property_identifiers_[PLUGIN_PROPERTY_ONPRIVATEMESSAGE]) { |
| 824 if (!is_privileged_) { |
| 825 DLOG(WARNING) << "Attempt to read onprivatemessage property while not " |
| 826 "privileged"; |
| 827 } else { |
| 828 if (onprivatemessage_handler_) { |
| 829 variant->type = NPVariantType_Object; |
| 830 variant->value.objectValue = |
| 831 onprivatemessage_handler_.Copy(); |
| 832 return true; |
| 833 } |
| 834 } |
| 835 } else if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_SRC]) { |
| 836 AllocateStringVariant(src_, variant); |
| 837 return true; |
| 838 } else if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_VERSION]) { |
| 839 const std::wstring version = |
| 840 automation_client_->GetVersion(); |
| 841 AllocateStringVariant(WideToUTF8(version), variant); |
| 842 return true; |
| 843 } else if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_READYSTATE]) { |
| 844 INT32_TO_NPVARIANT(ready_state_, *variant); |
| 845 return true; |
| 846 } else if (name == |
| 847 plugin_property_identifiers_[PLUGIN_PROPERTY_USECHROMENETWORK]) { |
| 848 BOOLEAN_TO_NPVARIANT(automation_client_->use_chrome_network(), *variant); |
| 849 return true; |
| 850 } |
| 851 |
| 852 return false; |
| 853 } |
| 854 |
| 855 bool ChromeFrameNPAPI::GetProperty(NPObject* object, NPIdentifier name, |
| 856 NPVariant* variant) { |
| 857 if (!object || !variant) { |
| 858 NOTREACHED(); |
| 859 return false; |
| 860 } |
| 861 |
| 862 ChromeFrameNPAPI* plugin_instance = ChromeFrameInstanceFromNPObject(object); |
| 863 if (!plugin_instance) { |
| 864 NOTREACHED(); |
| 865 return false; |
| 866 } |
| 867 |
| 868 return plugin_instance->GetProperty(name, variant); |
| 869 } |
| 870 |
| 871 bool ChromeFrameNPAPI::SetProperty(NPIdentifier name, |
| 872 const NPVariant* variant) { |
| 873 if (NPVARIANT_IS_OBJECT(*variant)) { |
| 874 if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_ONERROR]) { |
| 875 onerror_handler_.Free(); |
| 876 onerror_handler_ = variant->value.objectValue; |
| 877 return true; |
| 878 } else if ( |
| 879 name == plugin_property_identifiers_[PLUGIN_PROPERTY_ONMESSAGE]) { |
| 880 onmessage_handler_.Free(); |
| 881 onmessage_handler_ = variant->value.objectValue; |
| 882 return true; |
| 883 } else if (name == |
| 884 plugin_property_identifiers_[PLUGIN_PROPERTY_ONPRIVATEMESSAGE]) { |
| 885 if (!is_privileged_) { |
| 886 DLOG(WARNING) << "Attempt to set onprivatemessage while not privileged"; |
| 887 } else { |
| 888 onprivatemessage_handler_.Free(); |
| 889 onprivatemessage_handler_ = variant->value.objectValue; |
| 890 return true; |
| 891 } |
| 892 } |
| 893 } else if (NPVARIANT_IS_STRING(*variant) || NPVARIANT_IS_NULL(*variant)) { |
| 894 if (name == plugin_property_identifiers_[PLUGIN_PROPERTY_SRC]) { |
| 895 return NavigateToURL(variant, 1, NULL); |
| 896 } |
| 897 } else if (NPVARIANT_IS_BOOLEAN(*variant)) { |
| 898 if (name == |
| 899 plugin_property_identifiers_[PLUGIN_PROPERTY_USECHROMENETWORK]) { |
| 900 automation_client_->set_use_chrome_network( |
| 901 NPVARIANT_TO_BOOLEAN(*variant)); |
| 902 } |
| 903 } |
| 904 |
| 905 return false; |
| 906 } |
| 907 |
| 908 bool ChromeFrameNPAPI::SetProperty(NPObject* object, NPIdentifier name, |
| 909 const NPVariant* variant) { |
| 910 if (!object || !variant) { |
| 911 DLOG(ERROR) << "Cannot set property: " << npapi::StringFromIdentifier(name); |
| 912 return false; |
| 913 } |
| 914 |
| 915 ChromeFrameNPAPI* plugin_instance = ChromeFrameInstanceFromNPObject(object); |
| 916 if (!plugin_instance) { |
| 917 NOTREACHED(); |
| 918 return false; |
| 919 } |
| 920 |
| 921 return plugin_instance->SetProperty(name, variant); |
| 922 } |
| 923 |
| 924 void ChromeFrameNPAPI::OnFocus() { |
| 925 DLOG(INFO) << __FUNCTION__; |
| 926 PostMessage(WM_SETFOCUS, 0, 0); |
| 927 } |
| 928 |
| 929 void ChromeFrameNPAPI::OnEvent(const char* event_name) { |
| 930 DCHECK(event_name); |
| 931 DLOG(INFO) << event_name; |
| 932 |
| 933 if (lstrcmpiA(event_name, "focus") == 0) { |
| 934 OnFocus(); |
| 935 } else if (lstrcmpiA(event_name, "blur") == 0) { |
| 936 OnBlur(); |
| 937 } else { |
| 938 NOTREACHED() << event_name; |
| 939 } |
| 940 } |
| 941 |
| 942 LRESULT CALLBACK ChromeFrameNPAPI::DropKillFocusHook(int code, WPARAM wparam, |
| 943 LPARAM lparam) { |
| 944 LRESULT ret = 0; |
| 945 CWPSTRUCT* wp = reinterpret_cast<CWPSTRUCT*>(lparam); |
| 946 if ((code < 0) || (wp->message != WM_KILLFOCUS)) |
| 947 ret = ::CallNextHookEx(NULL, code, wparam, lparam); |
| 948 |
| 949 return ret; |
| 950 } |
| 951 |
| 952 LRESULT ChromeFrameNPAPI::OnSetFocus(UINT message, WPARAM wparam, |
| 953 LPARAM lparam, BOOL& handled) { // NO_LINT |
| 954 // Opera has a WH_CALLWNDPROC hook that handles WM_KILLFOCUS and |
| 955 // prevents us from setting the focus to the tab. |
| 956 // To work around that, we set a temporary hook here that does nothing |
| 957 // (not even call other hooks) when it sees WM_KILLFOCUS. |
| 958 HHOOK hook = NULL; |
| 959 hook = ::SetWindowsHookEx(WH_CALLWNDPROC, DropKillFocusHook, NULL, |
| 960 ::GetCurrentThreadId()); |
| 961 // Since we chain message maps, make sure we are not calling base class |
| 962 // twice for WM_SETFOCUS. |
| 963 BOOL handled_by_base = TRUE; |
| 964 LRESULT ret = Base::OnSetFocus(message, wparam, lparam, handled_by_base); |
| 965 if (hook) |
| 966 ::UnhookWindowsHookEx(hook); |
| 967 |
| 968 return ret; |
| 969 } |
| 970 |
| 971 void ChromeFrameNPAPI::OnBlur() { |
| 972 DLOG(INFO) << __FUNCTION__; |
| 973 } |
| 974 |
| 975 void ChromeFrameNPAPI::OnLoad(int, const GURL& gurl) { |
| 976 DLOG(INFO) << "Firing onload"; |
| 977 FireEvent("load", gurl.spec()); |
| 978 } |
| 979 |
| 980 void ChromeFrameNPAPI::OnLoadFailed(int error_code, const std::string& url) { |
| 981 FireEvent("loaderror", url); |
| 982 |
| 983 ScopedNpVariant result; |
| 984 InvokeDefault(onerror_handler_, url, &result); |
| 985 } |
| 986 |
| 987 void ChromeFrameNPAPI::OnMessageFromChromeFrame(int tab_handle, |
| 988 const std::string& message, |
| 989 const std::string& origin, |
| 990 const std::string& target) { |
| 991 bool private_message = false; |
| 992 if (target.compare("*") != 0) { |
| 993 if (is_privileged_) { |
| 994 private_message = true; |
| 995 } else { |
| 996 if (!HaveSameOrigin(target, document_url_)) { |
| 997 DLOG(WARNING) << "Dropping posted message since target doesn't match " |
| 998 "the current document's origin. target=" << target; |
| 999 return; |
| 1000 } |
| 1001 } |
| 1002 } |
| 1003 |
| 1004 // Create a MessageEvent object that contains the message and origin |
| 1005 // as well as supporting other MessageEvent (see the HTML5 spec) properties. |
| 1006 // Then call the onmessage handler. |
| 1007 ScopedNpObject<NPObject> event; |
| 1008 bool ok = CreateMessageEvent(false, true, message, origin, event.Receive()); |
| 1009 if (ok) { |
| 1010 // Don't call FireEvent here (or we'll have an event wrapped by an event). |
| 1011 DispatchEvent(event); |
| 1012 |
| 1013 ScopedNpVariant result; |
| 1014 NPVariant params[2]; |
| 1015 OBJECT_TO_NPVARIANT(event, params[0]); |
| 1016 bool invoke = false; |
| 1017 if (private_message) { |
| 1018 DCHECK(is_privileged_); |
| 1019 STRINGN_TO_NPVARIANT(target.c_str(), target.length(), params[1]); |
| 1020 invoke = InvokeDefault(onprivatemessage_handler_, |
| 1021 arraysize(params), |
| 1022 params, |
| 1023 &result); |
| 1024 } else { |
| 1025 invoke = InvokeDefault(onmessage_handler_, params[0], &result); |
| 1026 } |
| 1027 DLOG_IF(WARNING, !invoke) << "InvokeDefault failed"; |
| 1028 } else { |
| 1029 NOTREACHED() << "CreateMessageEvent"; |
| 1030 } |
| 1031 } |
| 1032 |
| 1033 void ChromeFrameNPAPI::OnAutomationServerReady() { |
| 1034 Base::OnAutomationServerReady(); |
| 1035 |
| 1036 std::string proxy_settings; |
| 1037 bool has_prefs = pref_service_->Initialize(instance_, |
| 1038 automation_client_.get()); |
| 1039 if (has_prefs && pref_service_->GetProxyValueJSONString(&proxy_settings)) { |
| 1040 automation_client_->SetProxySettings(proxy_settings); |
| 1041 } |
| 1042 |
| 1043 if (!src_.empty()) { |
| 1044 if (!automation_client_->InitiateNavigation(src_)) { |
| 1045 DLOG(ERROR) << "Failed to navigate to: " << src_; |
| 1046 src_.clear(); |
| 1047 } |
| 1048 } |
| 1049 |
| 1050 SetReadyState(READYSTATE_COMPLETE); |
| 1051 } |
| 1052 |
| 1053 void ChromeFrameNPAPI::OnAutomationServerLaunchFailed( |
| 1054 AutomationLaunchResult reason, const std::string& server_version) { |
| 1055 SetReadyState(READYSTATE_UNINITIALIZED); |
| 1056 |
| 1057 if (reason == AUTOMATION_VERSION_MISMATCH) { |
| 1058 DisplayVersionMismatchWarning(m_hWnd, server_version); |
| 1059 } |
| 1060 } |
| 1061 |
| 1062 bool ChromeFrameNPAPI::InvokeDefault(NPObject* object, |
| 1063 unsigned param_count, |
| 1064 const NPVariant* params, |
| 1065 NPVariant* result) { |
| 1066 if (!object) |
| 1067 return false; |
| 1068 |
| 1069 bool ret = npapi::InvokeDefault(instance_, object, params, param_count, |
| 1070 result); |
| 1071 // InvokeDefault can return false in FF even though we do see the call |
| 1072 // go through. It's not clear to me what the circumstances are, so |
| 1073 // we log it as a warning while tracking it down. |
| 1074 DLOG_IF(WARNING, !ret) << "npapi::InvokeDefault failed"; |
| 1075 return ret; |
| 1076 } |
| 1077 |
| 1078 bool ChromeFrameNPAPI::InvokeDefault(NPObject* object, const std::string& param, |
| 1079 NPVariant* result) { |
| 1080 NPVariant arg; |
| 1081 STRINGN_TO_NPVARIANT(param.c_str(), param.length(), arg); |
| 1082 return InvokeDefault(object, arg, result); |
| 1083 } |
| 1084 |
| 1085 bool ChromeFrameNPAPI::InvokeDefault(NPObject* object, const NPVariant& param, |
| 1086 NPVariant* result) { |
| 1087 return InvokeDefault(object, 1, ¶m, result); |
| 1088 } |
| 1089 |
| 1090 bool ChromeFrameNPAPI::CreateEvent(const std::string& type, bool bubbles, |
| 1091 bool cancelable, NPObject** basic_event) { |
| 1092 DCHECK(basic_event); |
| 1093 NPObject* window = GetWindowObject(); |
| 1094 if (!window) { |
| 1095 // Can fail if the browser is closing (seen in Opera). |
| 1096 return false; |
| 1097 } |
| 1098 |
| 1099 const char* identifier_names[] = { |
| 1100 "document", |
| 1101 "createEvent", |
| 1102 "initEvent", |
| 1103 }; |
| 1104 |
| 1105 NPIdentifier identifiers[arraysize(identifier_names)]; |
| 1106 npapi::GetStringIdentifiers(identifier_names, arraysize(identifier_names), |
| 1107 identifiers); |
| 1108 |
| 1109 // Fetch the document object from the window. |
| 1110 ScopedNpVariant document; |
| 1111 bool ok = npapi::GetProperty(instance_, window, identifiers[0], &document); |
| 1112 if (!ok) { |
| 1113 // This could happen if the page is being unloaded. |
| 1114 DLOG(WARNING) << "Failed to fetch the document object"; |
| 1115 return false; |
| 1116 } |
| 1117 |
| 1118 bool success = false; |
| 1119 if (ok && NPVARIANT_IS_OBJECT(document)) { |
| 1120 // Call document.createEvent("Event") to create a basic event object. |
| 1121 NPVariant event_type; |
| 1122 STRINGN_TO_NPVARIANT("Event", sizeof("Event") - 1, event_type); |
| 1123 ScopedNpVariant result; |
| 1124 success = npapi::Invoke(instance_, NPVARIANT_TO_OBJECT(document), |
| 1125 identifiers[1], &event_type, 1, &result); |
| 1126 if (!NPVARIANT_IS_OBJECT(result)) { |
| 1127 DLOG(WARNING) << "Failed to invoke createEvent"; |
| 1128 success = false; |
| 1129 } else { |
| 1130 NPVariant init_args[3]; |
| 1131 STRINGN_TO_NPVARIANT(type.c_str(), type.length(), init_args[0]); |
| 1132 BOOLEAN_TO_NPVARIANT(bubbles, init_args[1]); |
| 1133 BOOLEAN_TO_NPVARIANT(cancelable, init_args[2]); |
| 1134 |
| 1135 // Now initialize the event object by calling |
| 1136 // event.initEvent(type, bubbles, cancelable); |
| 1137 ScopedNpVariant init_results; |
| 1138 ok = npapi::Invoke(instance_, NPVARIANT_TO_OBJECT(result), identifiers[2], |
| 1139 init_args, arraysize(init_args), &init_results); |
| 1140 if (ok) { |
| 1141 success = true; |
| 1142 // Finally, pass the ownership to the caller. |
| 1143 *basic_event = NPVARIANT_TO_OBJECT(result); |
| 1144 VOID_TO_NPVARIANT(result); // Prevent the object from being released. |
| 1145 } else { |
| 1146 DLOG(ERROR) << "initEvent failed"; |
| 1147 success = false; |
| 1148 } |
| 1149 } |
| 1150 } |
| 1151 |
| 1152 return success; |
| 1153 } |
| 1154 |
| 1155 bool ChromeFrameNPAPI::CreateMessageEvent(bool bubbles, bool cancelable, |
| 1156 const std::string& data, |
| 1157 const std::string& origin, |
| 1158 NPObject** message_event) { |
| 1159 DCHECK(message_event); |
| 1160 ScopedNpObject<NPObject> event; |
| 1161 bool ok = CreateEvent("message", false, true, event.Receive()); |
| 1162 if (ok) { |
| 1163 typedef enum { |
| 1164 DATA, |
| 1165 ORIGIN, |
| 1166 LAST_EVENT_ID, |
| 1167 SOURCE, |
| 1168 MESSAGE_PORT, |
| 1169 IDENTIFIER_COUNT, // Must be last. |
| 1170 } StringIdentifiers; |
| 1171 |
| 1172 static NPIdentifier identifiers[IDENTIFIER_COUNT] = {0}; |
| 1173 if (!identifiers[0]) { |
| 1174 const NPUTF8* identifier_names[] = { |
| 1175 "data", |
| 1176 "origin", |
| 1177 "lastEventId", |
| 1178 "source", |
| 1179 "messagePort", |
| 1180 }; |
| 1181 COMPILE_ASSERT(arraysize(identifier_names) == arraysize(identifiers), |
| 1182 mismatched_array_size); |
| 1183 npapi::GetStringIdentifiers(identifier_names, IDENTIFIER_COUNT, |
| 1184 identifiers); |
| 1185 } |
| 1186 |
| 1187 NPVariant arg; |
| 1188 STRINGN_TO_NPVARIANT(data.c_str(), data.length(), arg); |
| 1189 npapi::SetProperty(instance_, event, identifiers[DATA], &arg); |
| 1190 STRINGN_TO_NPVARIANT(origin.c_str(), origin.length(), arg); |
| 1191 npapi::SetProperty(instance_, event, identifiers[ORIGIN], &arg); |
| 1192 STRINGN_TO_NPVARIANT("", 0, arg); |
| 1193 npapi::SetProperty(instance_, event, identifiers[LAST_EVENT_ID], &arg); |
| 1194 NULL_TO_NPVARIANT(arg); |
| 1195 npapi::SetProperty(instance_, event, identifiers[SOURCE], &arg); |
| 1196 npapi::SetProperty(instance_, event, identifiers[MESSAGE_PORT], &arg); |
| 1197 *message_event = event.Detach(); |
| 1198 } |
| 1199 |
| 1200 return ok; |
| 1201 } |
| 1202 |
| 1203 |
| 1204 void ChromeFrameNPAPI::DispatchEvent(NPObject* event) { |
| 1205 DCHECK(event != NULL); |
| 1206 |
| 1207 ScopedNpObject<NPObject> embed; |
| 1208 npapi::GetValue(instance_, NPNVPluginElementNPObject, &embed); |
| 1209 if (embed != NULL) { |
| 1210 NPVariant param; |
| 1211 OBJECT_TO_NPVARIANT(event, param); |
| 1212 ScopedNpVariant result; |
| 1213 bool invoke = npapi::Invoke(instance_, embed, |
| 1214 npapi::GetStringIdentifier("dispatchEvent"), ¶m, 1, &result); |
| 1215 DLOG_IF(WARNING, !invoke) << "dispatchEvent failed"; |
| 1216 } else { |
| 1217 NOTREACHED() << "NPNVPluginElementNPObject"; |
| 1218 } |
| 1219 } |
| 1220 |
| 1221 bool ChromeFrameNPAPI::ExecuteScript(const std::string& script, |
| 1222 NPVariant* result) { |
| 1223 NPObject* window = GetWindowObject(); |
| 1224 if (!window) { |
| 1225 NOTREACHED(); |
| 1226 return false; |
| 1227 } |
| 1228 |
| 1229 NPString script_for_execution; |
| 1230 script_for_execution.UTF8Characters = script.c_str(); |
| 1231 script_for_execution.UTF8Length = script.length(); |
| 1232 |
| 1233 return npapi::Evaluate(instance_, window, &script_for_execution, result); |
| 1234 } |
| 1235 |
| 1236 NPObject* ChromeFrameNPAPI::JavascriptToNPObject(const std::string& script) { |
| 1237 // Convert the passed in script to an invocable NPObject |
| 1238 // To achieve this we save away the function in a dummy window property |
| 1239 // which is then read to get the script object representing the function. |
| 1240 |
| 1241 std::string script_code = |
| 1242 "javascript:window.__cf_get_function_object ="; |
| 1243 |
| 1244 // If we are able to look up the name in the javascript namespace, then it |
| 1245 // means that the caller passed in a function name. Convert the function |
| 1246 // name to a NPObject we can invoke on. |
| 1247 if (IsValidJavascriptFunction(script)) { |
| 1248 script_code += script; |
| 1249 } else { |
| 1250 script_code += "new Function(\""; |
| 1251 script_code += script; |
| 1252 script_code += "\");"; |
| 1253 } |
| 1254 |
| 1255 NPVariant result; |
| 1256 if (!ExecuteScript(script_code, &result)) { |
| 1257 NOTREACHED(); |
| 1258 return NULL; |
| 1259 } |
| 1260 |
| 1261 DCHECK(result.type == NPVariantType_Object); |
| 1262 DCHECK(result.value.objectValue != NULL); |
| 1263 return result.value.objectValue; |
| 1264 } |
| 1265 |
| 1266 bool ChromeFrameNPAPI::IsValidJavascriptFunction(const std::string& script) { |
| 1267 std::string script_code = "javascript:window['"; |
| 1268 script_code += script; |
| 1269 script_code += "'];"; |
| 1270 |
| 1271 ScopedNpVariant result; |
| 1272 if (!ExecuteScript(script_code, &result)) { |
| 1273 NOTREACHED(); |
| 1274 return NULL; |
| 1275 } |
| 1276 |
| 1277 return result.type == NPVariantType_Object; |
| 1278 } |
| 1279 |
| 1280 bool ChromeFrameNPAPI::NavigateToURL(const NPVariant* args, uint32_t arg_count, |
| 1281 NPVariant* result) { |
| 1282 // Note that 'result' might be NULL. |
| 1283 if (arg_count != 1 || !(NPVARIANT_IS_STRING(args[0]) || |
| 1284 NPVARIANT_IS_NULL(args[0]))) { |
| 1285 NOTREACHED(); |
| 1286 return false; |
| 1287 } |
| 1288 |
| 1289 if (ready_state_ == READYSTATE_UNINITIALIZED) { |
| 1290 // Error(L"Chrome Frame failed to initialize."); |
| 1291 // TODO(tommi): call NPN_SetException |
| 1292 DLOG(WARNING) << "NavigateToURL called after failed initialization"; |
| 1293 return false; |
| 1294 } |
| 1295 |
| 1296 std::string url("about:blank"); |
| 1297 |
| 1298 if (!NPVARIANT_IS_NULL(args[0])) { |
| 1299 const NPString& str = args[0].value.stringValue; |
| 1300 if (str.UTF8Length) { |
| 1301 url.assign(std::string(str.UTF8Characters, str.UTF8Length)); |
| 1302 } |
| 1303 } |
| 1304 DLOG(WARNING) << __FUNCTION__ << " " << url; |
| 1305 std::string full_url = ResolveURL(GetDocumentUrl(), url); |
| 1306 if (full_url.empty()) |
| 1307 return false; |
| 1308 |
| 1309 src_ = full_url; |
| 1310 // Navigate only if we completed initialization i.e. proxy is set etc. |
| 1311 if (ready_state_ == READYSTATE_COMPLETE) { |
| 1312 if (!automation_client_->InitiateNavigation(full_url)) { |
| 1313 // TODO(tommi): call NPN_SetException. |
| 1314 src_.clear(); |
| 1315 return false; |
| 1316 } |
| 1317 } |
| 1318 return true; |
| 1319 } |
| 1320 |
| 1321 bool ChromeFrameNPAPI::postMessage(NPObject* npobject, const NPVariant* args, |
| 1322 uint32_t arg_count, NPVariant* result) { |
| 1323 if (arg_count < 1 || arg_count > 2 || !NPVARIANT_IS_STRING(args[0])) { |
| 1324 NOTREACHED(); |
| 1325 return false; |
| 1326 } |
| 1327 |
| 1328 const NPString& str = args[0].value.stringValue; |
| 1329 std::string message(str.UTF8Characters, str.UTF8Length); |
| 1330 std::string target; |
| 1331 if (arg_count == 2 && NPVARIANT_IS_STRING(args[1])) { |
| 1332 const NPString& str = args[1].value.stringValue; |
| 1333 target.assign(str.UTF8Characters, str.UTF8Length); |
| 1334 if (target.compare("*") != 0) { |
| 1335 GURL resolved(target); |
| 1336 if (!resolved.is_valid()) { |
| 1337 npapi::SetException(npobject, |
| 1338 "Unable to parse the specified target URL."); |
| 1339 return false; |
| 1340 } |
| 1341 target = resolved.spec(); |
| 1342 } |
| 1343 } else { |
| 1344 target = "*"; |
| 1345 } |
| 1346 |
| 1347 GURL url(GURL(document_url_).GetOrigin()); |
| 1348 std::string origin(url.is_empty() ? "null" : url.spec()); |
| 1349 |
| 1350 automation_client_->ForwardMessageFromExternalHost(message, origin, target); |
| 1351 |
| 1352 return true; |
| 1353 } |
| 1354 |
| 1355 bool ChromeFrameNPAPI::postPrivateMessage(NPObject* npobject, |
| 1356 const NPVariant* args, |
| 1357 uint32_t arg_count, |
| 1358 NPVariant* result) { |
| 1359 if (!is_privileged_) { |
| 1360 DLOG(WARNING) << "postPrivateMessage invoked in non-privileged mode"; |
| 1361 return false; |
| 1362 } |
| 1363 |
| 1364 if (arg_count != 3 || !NPVARIANT_IS_STRING(args[0]) || |
| 1365 !NPVARIANT_IS_STRING(args[1]) || !NPVARIANT_IS_STRING(args[2])) { |
| 1366 NOTREACHED(); |
| 1367 return false; |
| 1368 } |
| 1369 |
| 1370 const NPString& message_str = args[0].value.stringValue; |
| 1371 const NPString& origin_str = args[1].value.stringValue; |
| 1372 const NPString& target_str = args[2].value.stringValue; |
| 1373 std::string message(message_str.UTF8Characters, message_str.UTF8Length); |
| 1374 std::string origin(origin_str.UTF8Characters, origin_str.UTF8Length); |
| 1375 std::string target(target_str.UTF8Characters, target_str.UTF8Length); |
| 1376 |
| 1377 automation_client_->ForwardMessageFromExternalHost(message, origin, target); |
| 1378 |
| 1379 return true; |
| 1380 } |
| 1381 |
| 1382 void ChromeFrameNPAPI::FireEvent(const std::string& event_type, |
| 1383 const std::string& data) { |
| 1384 NPVariant arg; |
| 1385 STRINGN_TO_NPVARIANT(data.c_str(), data.length(), arg); |
| 1386 FireEvent(event_type, arg); |
| 1387 } |
| 1388 |
| 1389 void ChromeFrameNPAPI::FireEvent(const std::string& event_type, |
| 1390 const NPVariant& data) { |
| 1391 // Check that we're not bundling an event inside an event. |
| 1392 // Right now we're only expecting simple types for the data argument. |
| 1393 DCHECK(NPVARIANT_IS_OBJECT(data) == false); |
| 1394 |
| 1395 ScopedNpObject<NPObject> ev; |
| 1396 CreateEvent(event_type, false, false, ev.Receive()); |
| 1397 if (ev) { |
| 1398 // Add the 'data' member to the event. |
| 1399 bool set = npapi::SetProperty(instance_, ev, |
| 1400 npapi::GetStringIdentifier("data"), const_cast<NPVariant*>(&data)); |
| 1401 DCHECK(set); |
| 1402 DispatchEvent(ev); |
| 1403 } |
| 1404 } |
| 1405 |
| 1406 NpProxyService* ChromeFrameNPAPI::CreatePrefService() { |
| 1407 return new NpProxyService; |
| 1408 } |
| 1409 |
| 1410 NPObject* ChromeFrameNPAPI::GetWindowObject() const { |
| 1411 if (!window_object_.get()) { |
| 1412 NPError ret = npapi::GetValue(instance_, NPNVWindowNPObject, |
| 1413 window_object_.Receive()); |
| 1414 DLOG_IF(ERROR, ret != NPERR_NO_ERROR) << "NPNVWindowNPObject failed"; |
| 1415 } |
| 1416 return window_object_; |
| 1417 } |
| 1418 |
| 1419 bool ChromeFrameNPAPI::GetBrowserIncognitoMode() { |
| 1420 bool incognito_mode = false; |
| 1421 |
| 1422 // Check disabled for Opera due to bug: http://b/issue?id=1815494 |
| 1423 if (GetBrowserType() != BROWSER_OPERA) { |
| 1424 // Check whether host browser is in private mode; |
| 1425 NPBool private_mode = FALSE; |
| 1426 NPError err = npapi::GetValue(instance_, |
| 1427 NPNVprivateModeBool, |
| 1428 &private_mode); |
| 1429 if (err == NPERR_NO_ERROR && private_mode) { |
| 1430 incognito_mode = true; |
| 1431 } |
| 1432 } else { |
| 1433 DLOG(WARNING) << "Not checking for private mode in Opera"; |
| 1434 } |
| 1435 |
| 1436 return incognito_mode; |
| 1437 } |
| 1438 |
| 1439 NPAPIUrlRequest* ChromeFrameNPAPI::ValidateRequest( |
| 1440 NPP instance, void* notify_data) { |
| 1441 ChromeFrameNPAPI* plugin_instance = |
| 1442 ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance); |
| 1443 if (plugin_instance) { |
| 1444 return plugin_instance->RequestFromNotifyData(notify_data); |
| 1445 } |
| 1446 |
| 1447 return NULL; |
| 1448 } |
| 1449 |
| 1450 NPAPIUrlRequest* ChromeFrameNPAPI::RequestFromNotifyData( |
| 1451 void* notify_data) const { |
| 1452 NPAPIUrlRequest* request = reinterpret_cast<NPAPIUrlRequest*>(notify_data); |
| 1453 DCHECK(request ? automation_client_->IsValidRequest(request) : 1); |
| 1454 return request; |
| 1455 } |
| 1456 |
| 1457 bool ChromeFrameNPAPI::HandleContextMenuCommand(UINT cmd) { |
| 1458 if (cmd == IDC_ABOUT_CHROME_FRAME) { |
| 1459 // TODO: implement "About Chrome Frame" |
| 1460 } |
| 1461 return false; |
| 1462 } |
OLD | NEW |