diff options
author | amit@chromium.org <amit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-03 17:33:29 +0000 |
---|---|---|
committer | amit@chromium.org <amit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-03 17:33:29 +0000 |
commit | 2c41ad7b7986fdec304ffa3d42ab4bc162bb89e0 (patch) | |
tree | ff3b26607ba232878732ac1e597b254cad7ccefb | |
parent | 71a329c66080f1c095afc642eef5fe10160dc125 (diff) | |
download | chromium_src-2c41ad7b7986fdec304ffa3d42ab4bc162bb89e0.zip chromium_src-2c41ad7b7986fdec304ffa3d42ab4bc162bb89e0.tar.gz chromium_src-2c41ad7b7986fdec304ffa3d42ab4bc162bb89e0.tar.bz2 |
Fix browser hang due to plugin deadlock
This involves two plugin instances with second instance making
sync calls to the renderer while the first one is still servicing
an incoming sync request.
Our logic to unblock the renderer during the sync call fails
since the 'in_dispatch_' counter is maintained per plugin channel
(each plugin instance uses its own separate channel). Making
'in_dispatch_' counter static member of PluginChannelBase fixes this
deadlock.
Added a new NPAPI UI test for this scenario.
BUG=12624
TEST=MultipleInstancesSyncCalls
Review URL: http://codereview.chromium.org/119052
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17492 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/plugin/plugin_channel_base.cc | 3 | ||||
-rw-r--r-- | chrome/plugin/plugin_channel_base.h | 2 | ||||
-rw-r--r-- | chrome/test/data/npapi/multiple_instances_sync_calls.html | 38 | ||||
-rw-r--r-- | chrome/test/ui/npapi_uitest.cc | 13 | ||||
-rw-r--r-- | webkit/glue/plugins/test/plugin_client.cc | 3 | ||||
-rw-r--r-- | webkit/glue/plugins/test/plugin_windowless_test.cc | 76 | ||||
-rw-r--r-- | webkit/glue/plugins/test/plugin_windowless_test.h | 6 |
7 files changed, 111 insertions, 30 deletions
diff --git a/chrome/plugin/plugin_channel_base.cc b/chrome/plugin/plugin_channel_base.cc index 3a4a958..bdc04b7 100644 --- a/chrome/plugin/plugin_channel_base.cc +++ b/chrome/plugin/plugin_channel_base.cc @@ -47,13 +47,14 @@ PluginChannelBase::PluginChannelBase() peer_pid_(0), in_remove_route_(false), channel_valid_(false), - in_dispatch_(0), send_unblocking_only_during_dispatch_(false) { } PluginChannelBase::~PluginChannelBase() { } +int PluginChannelBase::in_dispatch_ = 0; + void PluginChannelBase::CleanupChannels() { // Make a copy of the references as we can't iterate the map since items will // be removed from it as we clean them up. diff --git a/chrome/plugin/plugin_channel_base.h b/chrome/plugin/plugin_channel_base.h index 70a9c30..c0f357c 100644 --- a/chrome/plugin/plugin_channel_base.h +++ b/chrome/plugin/plugin_channel_base.h @@ -110,7 +110,7 @@ class PluginChannelBase : public IPC::Channel::Listener, // Track whether we're within a dispatch; works like a refcount, 0 when we're // not. - int in_dispatch_; + static int in_dispatch_; // If true, sync messages will only be marked as unblocking if the channel is // in the middle of dispatching a message. diff --git a/chrome/test/data/npapi/multiple_instances_sync_calls.html b/chrome/test/data/npapi/multiple_instances_sync_calls.html new file mode 100644 index 0000000..5658a12 --- /dev/null +++ b/chrome/test/data/npapi/multiple_instances_sync_calls.html @@ -0,0 +1,38 @@ +<html> + <head> + <script src="npapi.js"></script> + <script> + function TestCallback() { + onSuccess("multiple_instances_sync_calls", 1); + } + </script> + </head> + <body> + <div id="statusPanel" style="border: 1px solid red; width: 100%"> + Test running.... + </div> + + NPObject Proxy Test<p> + + Tests the case that involves two plugin instances with second
+ instance making sync calls to the renderer while the first one + is still servicing an incoming sync request + + <DIV ID="plugin"> + <embed type="application/vnd.npapi-test" + src="foo" + name="multiple_instances_sync_calls" + id="1" + width="100" + height="100" + mode="np_embed"></embed> + <embed type="application/vnd.npapi-test" + src="foo" + name="multiple_instances_sync_calls" + id="2" + width="100" + height="100" + mode="np_embed"></embed> + </DIV> + </body> +</html> diff --git a/chrome/test/ui/npapi_uitest.cc b/chrome/test/ui/npapi_uitest.cc index 76176a9..83ea533 100644 --- a/chrome/test/ui/npapi_uitest.cc +++ b/chrome/test/ui/npapi_uitest.cc @@ -284,3 +284,16 @@ TEST_F(NPAPIIncognitoTester, PrivateEnabled) { WaitForFinish("private", "1", url, kTestCompleteCookie, kTestCompleteSuccess, kShortWaitTimeout); } + +// Test a browser hang due to special case of multiple +// plugin instances indulged in sync calls across renderer. +TEST_F(NPAPIVisiblePluginTester, MultipleInstancesSyncCalls) { + if (UITest::in_process_renderer()) + return; + + GURL url = GetTestUrl(L"npapi", L"multiple_instances_sync_calls.html"); + NavigateToURL(url); + WaitForFinish("multiple_instances_sync_calls", "1", url, kTestCompleteCookie, + kTestCompleteSuccess, kShortWaitTimeout); +} + diff --git a/webkit/glue/plugins/test/plugin_client.cc b/webkit/glue/plugins/test/plugin_client.cc index fe0a4c6..bcad2a8 100644 --- a/webkit/glue/plugins/test/plugin_client.cc +++ b/webkit/glue/plugins/test/plugin_client.cc @@ -109,7 +109,8 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, // TODO(port): plugin_windowless_test.*. } else if (test_name == "execute_script_delete_in_paint" || test_name == "execute_script_delete_in_mouse_move" || - test_name == "delete_frame_test") { + test_name == "delete_frame_test" || + test_name == "multiple_instances_sync_calls") { new_test = new NPAPIClient::WindowlessPluginTest(instance, NPAPIClient::PluginClient::HostFunctions(), test_name); windowless_plugin = true; diff --git a/webkit/glue/plugins/test/plugin_windowless_test.cc b/webkit/glue/plugins/test/plugin_windowless_test.cc index 74f422e..a42a69d 100644 --- a/webkit/glue/plugins/test/plugin_windowless_test.cc +++ b/webkit/glue/plugins/test/plugin_windowless_test.cc @@ -8,10 +8,15 @@ namespace NPAPIClient { +// Remember the first plugin instance for tests involving multiple instances +WindowlessPluginTest* g_other_instance = NULL; + WindowlessPluginTest::WindowlessPluginTest( NPP id, NPNetscapeFuncs *host_functions, const std::string& test_name) : PluginTest(id, host_functions), test_name_(test_name) { + if (!g_other_instance) + g_other_instance = this; } int16 WindowlessPluginTest::HandleEvent(void* event) { @@ -28,8 +33,7 @@ int16 WindowlessPluginTest::HandleEvent(void* event) { } NPEvent* np_event = reinterpret_cast<NPEvent*>(event); - if (WM_PAINT == np_event->event && - test_name_ == "execute_script_delete_in_paint") { + if (WM_PAINT == np_event->event) { HDC paint_dc = reinterpret_cast<HDC>(np_event->wParam); if (paint_dc == NULL) { SetError("Invalid Window DC passed to HandleEvent for WM_PAINT"); @@ -47,39 +51,57 @@ int16 WindowlessPluginTest::HandleEvent(void* event) { DeleteObject(clipping_region); - NPUTF8* urlString = "javascript:DeletePluginWithinScript()"; - NPUTF8* targetString = NULL; - browser->geturl(id(), urlString, targetString); - SignalTestCompleted(); + if (test_name_ == "execute_script_delete_in_paint") { + ExecuteScriptDeleteInPaint(browser); + } else if (test_name_ == "multiple_instances_sync_calls") { + MultipleInstanceSyncCalls(browser); + } + } else if (WM_MOUSEMOVE == np_event->event && test_name_ == "execute_script_delete_in_mouse_move") { - std::string script = "javascript:DeletePluginWithinScript()"; - NPString script_string; - script_string.UTF8Characters = script.c_str(); - script_string.UTF8Length = static_cast<unsigned int>(script.length()); - - NPObject *window_obj = NULL; - browser->getvalue(id(), NPNVWindowNPObject, &window_obj); - NPVariant result_var; - NPError result = browser->evaluate(id(), window_obj, - &script_string, &result_var); + ExecuteScript(browser, id(), "DeletePluginWithinScript();", NULL); SignalTestCompleted(); } else if (WM_LBUTTONUP == np_event->event && test_name_ == "delete_frame_test") { - std::string script = - "javascript:parent.document.getElementById('frame').outerHTML = ''"; - NPString script_string; - script_string.UTF8Characters = script.c_str(); - script_string.UTF8Length = static_cast<unsigned int>(script.length()); - - NPObject *window_obj = NULL; - browser->getvalue(id(), NPNVWindowNPObject, &window_obj); - NPVariant result_var; - NPError result = browser->evaluate(id(), window_obj, - &script_string, &result_var); + ExecuteScript( + browser, id(), + "parent.document.getElementById('frame').outerHTML = ''", NULL); } // If this test failed, then we'd have crashed by now. return PluginTest::HandleEvent(event); } +NPError WindowlessPluginTest::ExecuteScript(NPNetscapeFuncs* browser, NPP id, + const std::string& script, NPVariant* result) { + std::string script_url = "javascript:"; + script_url += script; + + NPString script_string = { script_url.c_str(), script_url.length() }; + NPObject *window_obj = NULL; + browser->getvalue(id, NPNVWindowNPObject, &window_obj); + + NPVariant unused_result; + if (!result) + result = &unused_result; + + return browser->evaluate(id, window_obj, &script_string, result); +} + +void WindowlessPluginTest::ExecuteScriptDeleteInPaint( + NPNetscapeFuncs* browser) { + NPUTF8* urlString = "javascript:DeletePluginWithinScript()"; + NPUTF8* targetString = NULL; + browser->geturl(id(), urlString, targetString); + SignalTestCompleted(); +} + +void WindowlessPluginTest::MultipleInstanceSyncCalls(NPNetscapeFuncs* browser) { + if (this == g_other_instance) + return; + + DCHECK(g_other_instance); + ExecuteScript(browser, g_other_instance->id(), "TestCallback();", NULL); + SignalTestCompleted(); +} + } // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_windowless_test.h b/webkit/glue/plugins/test/plugin_windowless_test.h index 93f7cfc..5135c4c 100644 --- a/webkit/glue/plugins/test/plugin_windowless_test.h +++ b/webkit/glue/plugins/test/plugin_windowless_test.h @@ -19,6 +19,12 @@ class WindowlessPluginTest : public PluginTest { // NPAPI HandleEvent handler virtual int16 HandleEvent(void* event); + protected: + NPError ExecuteScript(NPNetscapeFuncs* browser, NPP id, + const std::string& script, NPVariant* result); + void ExecuteScriptDeleteInPaint(NPNetscapeFuncs* browser); + void MultipleInstanceSyncCalls(NPNetscapeFuncs* browser); + private: std::string test_name_; }; |