// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/test/plugin/plugin_npobject_lifetime_test.h" namespace NPAPIClient { const int kNPObjectLifetimeTimer = 100; const int kNPObjectLifetimeTimerElapse = 2000; NPObject* NPObjectLifetimeTestInstance2::plugin_instance_object_ = NULL; NPObjectDeletePluginInNPN_Evaluate* NPObjectDeletePluginInNPN_Evaluate::g_npn_evaluate_test_instance_ = NULL; NPObjectLifetimeTest::NPObjectLifetimeTest(NPP id, NPNetscapeFuncs *host_functions) : PluginTest(id, host_functions), other_plugin_instance_object_(NULL), timer_id_(0) { } NPError NPObjectLifetimeTest::SetWindow(NPWindow* pNPWindow) { if (pNPWindow->window == NULL) return NPERR_NO_ERROR; HWND window_handle = reinterpret_cast(pNPWindow->window); if (!::GetProp(window_handle, L"Plugin_Instance")) { // TODO: this propery leaks. ::SetProp(window_handle, L"Plugin_Instance", this); // We attempt to retreive the NPObject for the plugin instance identified // by the NPObjectLifetimeTestInstance2 class as it may not have been // instantiated yet. timer_id_ = SetTimer(window_handle, kNPObjectLifetimeTimer, kNPObjectLifetimeTimerElapse, TimerProc); } return NPERR_NO_ERROR; } void CALLBACK NPObjectLifetimeTest::TimerProc( HWND window, UINT message, UINT_PTR timer_id, DWORD elapsed_milli_seconds) { NPObjectLifetimeTest* this_instance = reinterpret_cast (::GetProp(window, L"Plugin_Instance")); KillTimer(window, this_instance->timer_id_); ::RemoveProp(window, L"Plugin_Instance"); this_instance->timer_id_ = 0; this_instance->other_plugin_instance_object_ = NPObjectLifetimeTestInstance2::plugin_instance_object_; this_instance->HostFunctions()->retainobject( this_instance->other_plugin_instance_object_); NPString script; script.UTF8Characters = "javascript:DeleteSecondPluginInstance()"; script.UTF8Length = static_cast(strlen(script.UTF8Characters)); this_instance->HostFunctions()->geturlnotify( this_instance->id(), "javascript:DeleteSecondPluginInstance()", NULL, reinterpret_cast(1)); } void NPObjectLifetimeTest::URLNotify(const char* url, NPReason reason, void* data) { // Create a "location" identifier. NPIdentifier identifier = HostFunctions()->getstringidentifier("location"); // Declare a local variant value. NPVariant variantValue; // Get the location property from the window object (which is another object). HostFunctions()->getproperty(id(), other_plugin_instance_object_, identifier, &variantValue ); HostFunctions()->releaseobject(other_plugin_instance_object_); other_plugin_instance_object_ = NULL; // If this test failed, then we'd have crashed by now. SignalTestCompleted(); } NPObjectLifetimeTestInstance2::NPObjectLifetimeTestInstance2( NPP id, NPNetscapeFuncs *host_functions) : PluginTest(id, host_functions) { } NPObjectLifetimeTestInstance2::~NPObjectLifetimeTestInstance2() { if (plugin_instance_object_) { HostFunctions()->releaseobject(plugin_instance_object_); plugin_instance_object_ = NULL; } } NPError NPObjectLifetimeTestInstance2::SetWindow(NPWindow* pNPWindow) { if (pNPWindow->window == NULL) return NPERR_NO_ERROR; if (!plugin_instance_object_) { if (!HostFunctions()->getvalue(id(), NPNVWindowNPObject, &plugin_instance_object_)) { SetError("Failed to get NPObject for plugin instance2"); SignalTestCompleted(); return NPERR_GENERIC_ERROR; } } return NPERR_NO_ERROR; } NPObjectDeletePluginInNPN_Evaluate::NPObjectDeletePluginInNPN_Evaluate( NPP id, NPNetscapeFuncs *host_functions) : PluginTest(id, host_functions), plugin_instance_object_(NULL), timer_id_(0) { g_npn_evaluate_test_instance_ = this; } NPObjectDeletePluginInNPN_Evaluate::~NPObjectDeletePluginInNPN_Evaluate() { if (plugin_instance_object_) { HostFunctions()->releaseobject(plugin_instance_object_); plugin_instance_object_ = NULL; } } NPError NPObjectDeletePluginInNPN_Evaluate::SetWindow(NPWindow* np_window) { if (np_window->window == NULL) return NPERR_NO_ERROR; HWND window_handle = reinterpret_cast(np_window->window); // We setup a timerproc to invoke NPN_Evaluate to destroy this plugin // instance. This is to ensure that we don't destroy the plugin instance // while it is being used in webkit as this leads to crashes and is a // more accurate representation of the renderer crash as described in // http://b/issue?id=1134683. if (!timer_id_) { timer_id_ = SetTimer(window_handle, kNPObjectLifetimeTimer, kNPObjectLifetimeTimerElapse, TimerProc); } return NPERR_NO_ERROR; } void CALLBACK NPObjectDeletePluginInNPN_Evaluate::TimerProc( HWND window, UINT message, UINT_PTR timer_id, DWORD elapsed_milli_seconds) { KillTimer(window, g_npn_evaluate_test_instance_->timer_id_); g_npn_evaluate_test_instance_->timer_id_ = 0; NPObject *window_obj = NULL; g_npn_evaluate_test_instance_->HostFunctions()->getvalue( g_npn_evaluate_test_instance_->id(), NPNVWindowNPObject, &window_obj); if (!window_obj) { g_npn_evaluate_test_instance_->SetError( "Failed to get NPObject for plugin instance2"); g_npn_evaluate_test_instance_->SignalTestCompleted(); return; } std::string script = "javascript:DeletePluginWithinScript()"; NPString script_string; script_string.UTF8Characters = script.c_str(); script_string.UTF8Length = static_cast(script.length()); NPVariant result_var; g_npn_evaluate_test_instance_->HostFunctions()->evaluate( g_npn_evaluate_test_instance_->id(), window_obj, &script_string, &result_var); // If this test failed we would have crashed by now. } } // namespace NPAPIClient