Out-of-process plug-ins should support asynchronous initialization
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 6 Aug 2012 23:42:36 +0000 (23:42 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 6 Aug 2012 23:42:36 +0000 (23:42 +0000)
<rdar://problem/10598594> and https://bugs.webkit.org/show_bug.cgi?id=92919

Reviewed by Anders Carlsson.

Source/WebKit2:

If a plug-in has been deemed capable of asynchronous initialization when run out of process...
...then do that!

Add flags to communicate that this plugin create is meant to create a plug-in already requested asynchronously and
to include whether or not the initialize call should include an artificial delay (for testing):
* PluginProcess/PluginCreationParameters.cpp:
(WebKit::PluginCreationParameters::PluginCreationParameters):
(WebKit::PluginCreationParameters::encode):
(WebKit::PluginCreationParameters::decode):
* PluginProcess/PluginCreationParameters.h:
(PluginCreationParameters):

Add a flag for the UI Process to tell the PluginProcess that it supports asynchronous initialization:
* Shared/Plugins/PluginProcessCreationParameters.cpp:
(WebKit::PluginProcessCreationParameters::PluginProcessCreationParameters):
(WebKit::PluginProcessCreationParameters::encode):
(WebKit::PluginProcessCreationParameters::decode):
* Shared/Plugins/PluginProcessCreationParameters.h:
(PluginProcessCreationParameters):

Allow the UI Process to pass along whether the plug-on supports asynchronous initialization:
* UIProcess/Plugins/PluginProcessProxy.cpp:
(WebKit::PluginProcessProxy::pluginProcessCrashedOrFailedToLaunch):
(WebKit::PluginProcessProxy::didCreateWebProcessConnection):
* UIProcess/Plugins/PluginProcessProxy.h:
(PluginProcessProxy):
* UIProcess/Plugins/mac/PluginProcessProxyMac.mm:
(WebKit::PluginProcessProxy::platformInitializePluginProcess):
* UIProcess/WebProcessProxy.messages.in:

Allow the Plugin Process to pass whether or not it supports asynchronous initialization, originally determined
in the UI Process, along to the WebProcess:
* PluginProcess/PluginProcess.cpp:
(WebKit::PluginProcess::PluginProcess):
(WebKit::PluginProcess::initializePluginProcess):
(WebKit::PluginProcess::createWebProcessConnection):
* PluginProcess/PluginProcess.h:
(PluginProcess):
* UIProcess/Plugins/PluginProcessProxy.messages.in:

Add a flag so PluginProcessConnections remember whether or not they support asynchronous initialization:
* WebProcess/Plugins/PluginProcessConnection.cpp:
(WebKit::PluginProcessConnection::PluginProcessConnection):
(WebKit::PluginProcessConnection::setSupportsAsynchronousPluginInitialization):
(WebKit):
* WebProcess/Plugins/PluginProcessConnection.h:
(WebKit::PluginProcessConnection::create):
(WebKit::PluginProcessConnection::supportsAsynchronousPluginInitialization):
(PluginProcessConnection):

Create PluginProcessConnections with the flag passed down from the PluginProcess about whether or not they
support asynchronous initialization:
* WebProcess/Plugins/PluginProcessConnectionManager.cpp:
(WebKit::PluginProcessConnectionManager::getPluginProcessConnection):

Responding to messages from the WebProcess, most of the heavy decision making in asynchronous initialization is here:
* PluginProcess/WebProcessConnection.cpp:
(WebKit::asynchronousInstanceIDsToIgnore): A set of instance IDs to *not* create asynchronously later because we know
  we no longer need to.
(WebKit):
(WebKit::WebProcessConnection::didReceiveMessage):
(WebKit::WebProcessConnection::destroyPlugin): If the plug-in doesn't exist but is awaiting asynchronous creation, flag
  this instance ID in the "asynchronous ignore set".
(WebKit::WebProcessConnection::createPluginInternal): Renamed from createPlugin, actually does the plug-in creation.
(WebKit::WebProcessConnection::createPlugin): Adds the instance ID to the "asynchronous ignore set" then calls createPluginInternal.
(WebKit::WebProcessConnection::createPluginAsynchronously): If the instance ID is in the "asynchronous ignore set", remove it from the
  set and do nothing else. Otherwise, perform the initialization and then send the asynchronous result back to the WebProcess.
* PluginProcess/WebProcessConnection.h:
(WebProcessConnection):
* PluginProcess/WebProcessConnection.messages.in:

Add helpers for asynchronous initialization that all plug-in types must implement:
* WebProcess/Plugins/Plugin.h:
(Plugin):

Add helpers for asynchronous initialization that plug-in controllers can override:
* WebProcess/Plugins/PluginController.h:
(PluginController):
(WebKit::PluginController::asynchronousPluginInitializationEnabled):
(WebKit::PluginController::asynchronousPluginInitializationEnabledForAllPlugins):
(WebKit::PluginController::artificialPluginInitializationDelayEnabled):

Give PluginProxys the ability to initialize either asynchronously or synchronously, and also the ability to synchronously
wait for previously asynchronous initialization (in case their PluginScriptObject is required):
* WebProcess/Plugins/PluginProxy.cpp:
(WebKit::PluginProxy::PluginProxy):
(WebKit::PluginProxy::initialize): Store the plugin creation parameters as a member, and decide whether to try synchronous
  or asynchronous initialization.
(WebKit):
(WebKit::PluginProxy::canInitializeAsynchronously): Answer based on preferences and what the PluginProcessConnection says
  that it supports.
(WebKit::PluginProxy::waitForAsynchronousInitialization): Synchronously wait on initialization when asynchronous initialization
  was previously requested.
(WebKit::PluginProxy::initializeSynchronously):
(WebKit::PluginProxy::didCreatePlugin): Double-check that we're still expecting asynchronous initialization, then call
  through to didCreatePluginInternal.
(WebKit::PluginProxy::didCreatePluginInternal): Handle completion of initialization (both synchronously and asynchronously)
(WebKit::PluginProxy::didFailToCreatePlugin): Double-check that we're still expecting asynchronous initialization, then call
  through to didFailToCreatePluginInternal.
(WebKit::PluginProxy::didFailToCreatePluginInternal): Handle failure to initialize (both synchronously and asynchronously)
(WebKit::PluginProxy::destroy):
* WebProcess/Plugins/PluginProxy.h:
(WebKit):
(WebKit::PluginProxy::isInitializingAsynchronously):
(PluginProxy):
* WebProcess/Plugins/PluginProxy.messages.in:

* WebProcess/Plugins/PluginView.cpp:
(WebKit::PluginView::PluginView):
(WebKit::PluginView::~PluginView): Always destroy the plug-in even if it hasn't been initialized yet, as it might be initializing
  right now.
(WebKit::PluginView::initializePlugin): Don't handle the result of initialization immediately. Break that out in to two
  methods that will be called later.
(WebKit):
(WebKit::PluginView::didFailToInitializePlugin):
(WebKit::PluginView::didInitializePlugin):
(WebKit::PluginView::scriptObject): If we truly need the script object, then wait for a synchronous initialization of the plug-in.
(WebKit::PluginView::asynchronousPluginInitializationEnabled):
(WebKit::PluginView::asynchronousPluginInitializationEnabledForAllPlugins):
(WebKit::PluginView::artificialPluginInitializationDelayEnabled):
* WebProcess/Plugins/PluginView.h:
(PluginView):

These methods shouldn't be called in the PluginProcess, only in the WebProcess:
* PluginProcess/PluginControllerProxy.cpp:
(WebKit::PluginControllerProxy::didInitializePlugin):
(WebKit):
(WebKit::PluginControllerProxy::didFailToInitializePlugin):
* PluginProcess/PluginControllerProxy.h:
(PluginControllerProxy):

NetscapePlugin is for in-process plug-ins:
* WebProcess/Plugins/Netscape/NetscapePlugin.h:
(NetscapePlugin):
(WebKit::NetscapePlugin::waitForAsynchronousInitialization):
(WebKit::NetscapePlugin::isInitializingAsynchronously):

Built-in PDFView is currently only in-process:
* WebProcess/Plugins/PDF/BuiltInPDFView.h:
(BuiltInPDFView):
(WebKit::BuiltInPDFView::waitForAsynchronousInitialization):
(WebKit::BuiltInPDFView::isInitializingAsynchronously):

Tools:

Add a plug-in with an NPP_New that takes 550ms (a reasonable trade-off between a solid test and a slow running test)
for testing asynchronous plug-in initialization.

* DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj:
* DumpRenderTree/TestNetscapePlugIn/Tests/SlowNPPNew.cpp: Copied from Source/WebKit2/Shared/Plugins/PluginProcessCreationParameters.h.
(SlowNPPNew):
(SlowNPPNew::SlowNPPNew):
(SlowNPPNew::NPP_New):

LayoutTests:

Add tests to make sure a plug-in with a long running NPP_New does not block the web thread.

* platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-expected.txt: Added.
* platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-multiple-expected.txt: Added.
* platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-multiple.html: Added.
* platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization.html: Added.
* platform/mac-wk2/plugins/slow/resources/asynchronous-plugin-initialization-multiple-finish.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@124815 268f45cc-cd09-0410-ab3c-d52691b4dbfc

38 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-multiple-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-multiple.html [new file with mode: 0644]
LayoutTests/platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization.html [new file with mode: 0644]
LayoutTests/platform/mac-wk2/plugins/slow/resources/asynchronous-plugin-initialization-multiple-finish.html [new file with mode: 0644]
Source/WebKit2/ChangeLog
Source/WebKit2/PluginProcess/PluginControllerProxy.cpp
Source/WebKit2/PluginProcess/PluginControllerProxy.h
Source/WebKit2/PluginProcess/PluginCreationParameters.cpp
Source/WebKit2/PluginProcess/PluginCreationParameters.h
Source/WebKit2/PluginProcess/PluginProcess.cpp
Source/WebKit2/PluginProcess/PluginProcess.h
Source/WebKit2/PluginProcess/WebProcessConnection.cpp
Source/WebKit2/PluginProcess/WebProcessConnection.h
Source/WebKit2/PluginProcess/WebProcessConnection.messages.in
Source/WebKit2/Shared/Plugins/PluginProcessCreationParameters.cpp
Source/WebKit2/Shared/Plugins/PluginProcessCreationParameters.h
Source/WebKit2/UIProcess/Plugins/PluginProcessProxy.cpp
Source/WebKit2/UIProcess/Plugins/PluginProcessProxy.h
Source/WebKit2/UIProcess/Plugins/PluginProcessProxy.messages.in
Source/WebKit2/UIProcess/Plugins/mac/PluginProcessProxyMac.mm
Source/WebKit2/UIProcess/WebProcessProxy.messages.in
Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePlugin.h
Source/WebKit2/WebProcess/Plugins/PDF/BuiltInPDFView.h
Source/WebKit2/WebProcess/Plugins/Plugin.h
Source/WebKit2/WebProcess/Plugins/PluginController.h
Source/WebKit2/WebProcess/Plugins/PluginProcessConnection.cpp
Source/WebKit2/WebProcess/Plugins/PluginProcessConnection.h
Source/WebKit2/WebProcess/Plugins/PluginProcessConnectionManager.cpp
Source/WebKit2/WebProcess/Plugins/PluginProxy.cpp
Source/WebKit2/WebProcess/Plugins/PluginProxy.h
Source/WebKit2/WebProcess/Plugins/PluginProxy.messages.in
Source/WebKit2/WebProcess/Plugins/PluginView.cpp
Source/WebKit2/WebProcess/Plugins/PluginView.h
Tools/ChangeLog
Tools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj
Tools/DumpRenderTree/TestNetscapePlugIn/Tests/SlowNPPNew.cpp [new file with mode: 0644]

index 58cd00a..6d0efd8 100644 (file)
@@ -1,3 +1,18 @@
+2012-08-03  Brady Eidson  <beidson@apple.com>
+
+        Out-of-process plug-ins should support asynchronous initialization
+        <rdar://problem/10598594> and https://bugs.webkit.org/show_bug.cgi?id=92919
+
+        Reviewed by Anders Carlsson.
+
+        Add tests to make sure a plug-in with a long running NPP_New does not block the web thread.
+
+        * platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-expected.txt: Added.
+        * platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-multiple-expected.txt: Added.
+        * platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-multiple.html: Added.
+        * platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization.html: Added.
+        * platform/mac-wk2/plugins/slow/resources/asynchronous-plugin-initialization-multiple-finish.html: Added.
+
 2012-08-06  Pravin D  <pravind.2k4@gmai.com>
 
         Testcase LayoutTests/editing/input/editable-container-with-word-wrap-normal.html failing on Mac
diff --git a/LayoutTests/platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-expected.txt b/LayoutTests/platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-expected.txt
new file mode 100644 (file)
index 0000000..a811c55
--- /dev/null
@@ -0,0 +1,7 @@
+Test that loading a slow loading plug-in (550ms second sleep in NPP_new) doesn't block the WebProcess
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Appending plug-in element took under 500ms, plug-in initializing asynchronously.
+
diff --git a/LayoutTests/platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-multiple-expected.txt b/LayoutTests/platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-multiple-expected.txt
new file mode 100644 (file)
index 0000000..4096eef
--- /dev/null
@@ -0,0 +1,5 @@
+This finishes the test and see's how much time passed.
+Only one of the 20 plug-ins should have had it's NPP_New and NPP_Destroy run to completion, taking a bit over 550ms.
+The other 19 should never have been instantiated at all.
+PASS Appending plug-in elements then navigating away took between 550ms and 1100ms suggesting a single plug-in was created and then destroyed.
+
diff --git a/LayoutTests/platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-multiple.html b/LayoutTests/platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization-multiple.html
new file mode 100644 (file)
index 0000000..cf81ca1
--- /dev/null
@@ -0,0 +1,47 @@
+<head>
+<script src="../../../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body onload="runTest()">
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+function createTestPlugin(testName)
+{
+    var plugin = document.createElement("embed");
+    plugin.type = "application/x-webkit-test-netscape";
+    plugin.setAttribute('test', testName);
+
+    return plugin;
+}
+
+function runTest() 
+{
+    if (!window.testRunner) {
+        debug("This test can only run from within DumpRenderTree because it requires TestNetscapePlugin.\n");
+        return;
+    }
+
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+    testRunner.overridePreference("WebKit2AsynchronousPluginInitializationEnabled", "1");
+    testRunner.overridePreference("WebKit2AsynchronousPluginInitializationEnabledForAllPlugins", "1");
+
+    // Grab the time before we create and append 20 slow plug-ins.
+    sessionStorage.beforeDate = (new Date).valueOf();
+    
+    var plugins = new Array();
+    for (var i = 0; i < 20; ++i)
+        plugins[i] = createTestPlugin('slow-npp-new');
+    for (var i = 0; i < 20; ++i)
+        document.body.appendChild(plugins[i]);
+
+    // Try to force a layout so the plug-ins instantiate
+    var foo = document.body.offsetTop;
+    
+    setTimeout("location.href = 'resources/asynchronous-plugin-initialization-multiple-finish.html'", 0);
+}
+
+description("Tests that loading multiple slow initializing plug-ins and then navigating only has to wait for one of them to finish being destroyed.");
+
+</script>
diff --git a/LayoutTests/platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization.html b/LayoutTests/platform/mac-wk2/plugins/slow/asynchronous-plugin-initialization.html
new file mode 100644 (file)
index 0000000..46b85e8
--- /dev/null
@@ -0,0 +1,49 @@
+<head>
+<script src="../../../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body onload="runTest()">
+<p id="description"></p>
+<div id="console"></div>
+<script>
+
+function createTestPlugin(testName)
+{
+    var plugin = document.createElement("embed");
+    plugin.type = "application/x-webkit-test-netscape";
+    plugin.setAttribute('test', testName);
+
+    return plugin;
+}
+
+function runTest() 
+{
+    if (!window.testRunner) {
+        debug("This test can only run from within DumpRenderTree because it requires TestNetscapePlugin.\n");
+        return;
+    }
+
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+    testRunner.overridePreference("WebKit2AsynchronousPluginInitializationEnabled", "1");
+    testRunner.overridePreference("WebKit2AsynchronousPluginInitializationEnabledForAllPlugins", "1");
+
+    var beforeDate = new Date;
+    
+    plugin = createTestPlugin('slow-npp-new');
+    document.body.appendChild(plugin);
+    // Try to force a layout so the plug-in instantiates
+    var foo = document.body.offsetTop;
+    
+    var afterDate = new Date;
+    
+    if (afterDate - beforeDate > 500)
+        testFailed("Appending plug-in element took over 500ms (" + (afterDate - beforeDate) + "ms) so the plug-in was probably being initialized synchronously");
+    else
+        testPassed("Appending plug-in element took under 500ms, plug-in initializing asynchronously.");
+    
+    testRunner.notifyDone();
+}
+
+description("Test that loading a slow loading plug-in (550ms second sleep in NPP_new) doesn't block the WebProcess");
+
+</script>
diff --git a/LayoutTests/platform/mac-wk2/plugins/slow/resources/asynchronous-plugin-initialization-multiple-finish.html b/LayoutTests/platform/mac-wk2/plugins/slow/resources/asynchronous-plugin-initialization-multiple-finish.html
new file mode 100644 (file)
index 0000000..6afba18
--- /dev/null
@@ -0,0 +1,33 @@
+<head>
+<script src="../../../../../fast/js/resources/js-test-pre.js"></script>
+</head>
+
+<body onload="loaded();">
+This finishes the test and see's how much time passed.<br>
+Only one of the 20 plug-ins should have had it's NPP_New and NPP_Destroy run to completion, taking a bit over 550ms.<br>
+The other 19 should never have been instantiated at all.<br>
+<div id="console"></div>
+</body>
+<script>
+
+function loaded() {
+    if (!window.testRunner) {
+        alert("Can only be run using WebKitTestRunner");
+        return;
+    }
+    
+    var timeLapsed = (new Date).valueOf() - sessionStorage.beforeDate;
+
+    if (timeLapsed > 1100)
+        testFailed("Appending plug-in elements then navigating away took over 1100ms (" + timeLapsed + "ms) so more than one plug-in was probably being initialized before any were destroyed.");
+    if (timeLapsed < 550)
+        testFailed("Appending plug-in elements then navigating away took less than 550ms (" + timeLapsed + "ms).  We expected a single plug-in to be created and then destroyed and that didn't happen.");
+    else
+        testPassed("Appending plug-in elements then navigating away took between 550ms and 1100ms suggesting a single plug-in was created and then destroyed.");
+
+    testRunner.notifyDone();
+}
+
+</script>
+
+
index a312a1b..6e32c40 100644 (file)
@@ -1,3 +1,153 @@
+2012-08-03  Brady Eidson  <beidson@apple.com>
+
+        Out-of-process plug-ins should support asynchronous initialization
+        <rdar://problem/10598594> and https://bugs.webkit.org/show_bug.cgi?id=92919
+
+        Reviewed by Anders Carlsson.
+
+        If a plug-in has been deemed capable of asynchronous initialization when run out of process...
+        ...then do that!
+
+        Add flags to communicate that this plugin create is meant to create a plug-in already requested asynchronously and
+        to include whether or not the initialize call should include an artificial delay (for testing):
+        * PluginProcess/PluginCreationParameters.cpp:
+        (WebKit::PluginCreationParameters::PluginCreationParameters):
+        (WebKit::PluginCreationParameters::encode):
+        (WebKit::PluginCreationParameters::decode):
+        * PluginProcess/PluginCreationParameters.h:
+        (PluginCreationParameters):
+
+        Add a flag for the UI Process to tell the PluginProcess that it supports asynchronous initialization:
+        * Shared/Plugins/PluginProcessCreationParameters.cpp:
+        (WebKit::PluginProcessCreationParameters::PluginProcessCreationParameters):
+        (WebKit::PluginProcessCreationParameters::encode):
+        (WebKit::PluginProcessCreationParameters::decode):
+        * Shared/Plugins/PluginProcessCreationParameters.h:
+        (PluginProcessCreationParameters):
+
+        Allow the UI Process to pass along whether the plug-on supports asynchronous initialization:
+        * UIProcess/Plugins/PluginProcessProxy.cpp:
+        (WebKit::PluginProcessProxy::pluginProcessCrashedOrFailedToLaunch):
+        (WebKit::PluginProcessProxy::didCreateWebProcessConnection):
+        * UIProcess/Plugins/PluginProcessProxy.h:
+        (PluginProcessProxy):
+        * UIProcess/Plugins/mac/PluginProcessProxyMac.mm:
+        (WebKit::PluginProcessProxy::platformInitializePluginProcess):
+        * UIProcess/WebProcessProxy.messages.in:
+
+        Allow the Plugin Process to pass whether or not it supports asynchronous initialization, originally determined
+        in the UI Process, along to the WebProcess:
+        * PluginProcess/PluginProcess.cpp:
+        (WebKit::PluginProcess::PluginProcess):
+        (WebKit::PluginProcess::initializePluginProcess):
+        (WebKit::PluginProcess::createWebProcessConnection):
+        * PluginProcess/PluginProcess.h:
+        (PluginProcess):
+        * UIProcess/Plugins/PluginProcessProxy.messages.in:
+
+        Add a flag so PluginProcessConnections remember whether or not they support asynchronous initialization:
+        * WebProcess/Plugins/PluginProcessConnection.cpp:
+        (WebKit::PluginProcessConnection::PluginProcessConnection):
+        (WebKit::PluginProcessConnection::setSupportsAsynchronousPluginInitialization):
+        (WebKit):
+        * WebProcess/Plugins/PluginProcessConnection.h:
+        (WebKit::PluginProcessConnection::create):
+        (WebKit::PluginProcessConnection::supportsAsynchronousPluginInitialization):
+        (PluginProcessConnection):
+
+        Create PluginProcessConnections with the flag passed down from the PluginProcess about whether or not they
+        support asynchronous initialization:
+        * WebProcess/Plugins/PluginProcessConnectionManager.cpp:
+        (WebKit::PluginProcessConnectionManager::getPluginProcessConnection):
+
+        Responding to messages from the WebProcess, most of the heavy decision making in asynchronous initialization is here:
+        * PluginProcess/WebProcessConnection.cpp:
+        (WebKit::asynchronousInstanceIDsToIgnore): A set of instance IDs to *not* create asynchronously later because we know
+          we no longer need to.
+        (WebKit):
+        (WebKit::WebProcessConnection::didReceiveMessage):
+        (WebKit::WebProcessConnection::destroyPlugin): If the plug-in doesn't exist but is awaiting asynchronous creation, flag 
+          this instance ID in the "asynchronous ignore set".
+        (WebKit::WebProcessConnection::createPluginInternal): Renamed from createPlugin, actually does the plug-in creation.
+        (WebKit::WebProcessConnection::createPlugin): Adds the instance ID to the "asynchronous ignore set" then calls createPluginInternal.
+        (WebKit::WebProcessConnection::createPluginAsynchronously): If the instance ID is in the "asynchronous ignore set", remove it from the
+          set and do nothing else. Otherwise, perform the initialization and then send the asynchronous result back to the WebProcess.
+        * PluginProcess/WebProcessConnection.h:
+        (WebProcessConnection):
+        * PluginProcess/WebProcessConnection.messages.in:
+
+        Add helpers for asynchronous initialization that all plug-in types must implement:
+        * WebProcess/Plugins/Plugin.h:
+        (Plugin):
+
+        Add helpers for asynchronous initialization that plug-in controllers can override:
+        * WebProcess/Plugins/PluginController.h:
+        (PluginController):
+        (WebKit::PluginController::asynchronousPluginInitializationEnabled):
+        (WebKit::PluginController::asynchronousPluginInitializationEnabledForAllPlugins):
+        (WebKit::PluginController::artificialPluginInitializationDelayEnabled):
+
+        Give PluginProxys the ability to initialize either asynchronously or synchronously, and also the ability to synchronously
+        wait for previously asynchronous initialization (in case their PluginScriptObject is required):
+        * WebProcess/Plugins/PluginProxy.cpp:
+        (WebKit::PluginProxy::PluginProxy):
+        (WebKit::PluginProxy::initialize): Store the plugin creation parameters as a member, and decide whether to try synchronous
+          or asynchronous initialization.
+        (WebKit):
+        (WebKit::PluginProxy::canInitializeAsynchronously): Answer based on preferences and what the PluginProcessConnection says
+          that it supports.
+        (WebKit::PluginProxy::waitForAsynchronousInitialization): Synchronously wait on initialization when asynchronous initialization
+          was previously requested.
+        (WebKit::PluginProxy::initializeSynchronously):
+        (WebKit::PluginProxy::didCreatePlugin): Double-check that we're still expecting asynchronous initialization, then call
+          through to didCreatePluginInternal.
+        (WebKit::PluginProxy::didCreatePluginInternal): Handle completion of initialization (both synchronously and asynchronously)
+        (WebKit::PluginProxy::didFailToCreatePlugin): Double-check that we're still expecting asynchronous initialization, then call
+          through to didFailToCreatePluginInternal.
+        (WebKit::PluginProxy::didFailToCreatePluginInternal): Handle failure to initialize (both synchronously and asynchronously)
+        (WebKit::PluginProxy::destroy):
+        * WebProcess/Plugins/PluginProxy.h:
+        (WebKit):
+        (WebKit::PluginProxy::isInitializingAsynchronously):
+        (PluginProxy):
+        * WebProcess/Plugins/PluginProxy.messages.in:
+
+        * WebProcess/Plugins/PluginView.cpp:
+        (WebKit::PluginView::PluginView):
+        (WebKit::PluginView::~PluginView): Always destroy the plug-in even if it hasn't been initialized yet, as it might be initializing
+          right now.
+        (WebKit::PluginView::initializePlugin): Don't handle the result of initialization immediately. Break that out in to two
+          methods that will be called later.
+        (WebKit):
+        (WebKit::PluginView::didFailToInitializePlugin):
+        (WebKit::PluginView::didInitializePlugin):
+        (WebKit::PluginView::scriptObject): If we truly need the script object, then wait for a synchronous initialization of the plug-in.
+        (WebKit::PluginView::asynchronousPluginInitializationEnabled):
+        (WebKit::PluginView::asynchronousPluginInitializationEnabledForAllPlugins):
+        (WebKit::PluginView::artificialPluginInitializationDelayEnabled):
+        * WebProcess/Plugins/PluginView.h:
+        (PluginView):
+
+        These methods shouldn't be called in the PluginProcess, only in the WebProcess:
+        * PluginProcess/PluginControllerProxy.cpp:
+        (WebKit::PluginControllerProxy::didInitializePlugin):
+        (WebKit):
+        (WebKit::PluginControllerProxy::didFailToInitializePlugin):
+        * PluginProcess/PluginControllerProxy.h:
+        (PluginControllerProxy):
+
+        NetscapePlugin is for in-process plug-ins:
+        * WebProcess/Plugins/Netscape/NetscapePlugin.h:
+        (NetscapePlugin):
+        (WebKit::NetscapePlugin::waitForAsynchronousInitialization):
+        (WebKit::NetscapePlugin::isInitializingAsynchronously):
+
+        Built-in PDFView is currently only in-process:
+        * WebProcess/Plugins/PDF/BuiltInPDFView.h:
+        (BuiltInPDFView):
+        (WebKit::BuiltInPDFView::waitForAsynchronousInitialization):
+        (WebKit::BuiltInPDFView::isInitializingAsynchronously):
+
 2012-08-06  Luciano Wolf  <luciano.wolf@openbossa.org>
 
         [Qt] Default sizes for input-text and text-area are different when running DRT/WTR
index 24a8cee..ab60f94 100644 (file)
@@ -317,6 +317,18 @@ void PluginControllerProxy::willSendEventToPlugin()
     ASSERT_NOT_REACHED();
 }
 
+void PluginControllerProxy::didInitializePlugin()
+{
+    // This should only be called on the plugin in the web process.
+    ASSERT_NOT_REACHED();
+}
+
+void PluginControllerProxy::didFailToInitializePlugin()
+{
+    // This should only be called on the plugin in the web process.
+    ASSERT_NOT_REACHED();
+}
+
 float PluginControllerProxy::contentsScaleFactor()
 {
     return m_contentsScaleFactor;
index a4af543..46da1a4 100644 (file)
@@ -90,6 +90,8 @@ private:
     virtual bool isAcceleratedCompositingEnabled();
     virtual void pluginProcessCrashed();
     virtual void willSendEventToPlugin();
+    virtual void didInitializePlugin() OVERRIDE;
+    virtual void didFailToInitializePlugin() OVERRIDE;
 
 #if PLATFORM(MAC)
     virtual void pluginFocusOrWindowFocusChanged(bool);
index e29e974..bc2e992 100644 (file)
@@ -37,6 +37,8 @@ PluginCreationParameters::PluginCreationParameters()
     , windowNPObjectID(0)
     , contentsScaleFactor(1)
     , isPrivateBrowsingEnabled(false)
+    , asynchronousCreationIncomplete(false)
+    , artificialPluginInitializationDelayEnabled(false)
 #if USE(ACCELERATED_COMPOSITING)
     , isAcceleratedCompositingEnabled(false)
 #endif
@@ -51,6 +53,8 @@ void PluginCreationParameters::encode(CoreIPC::ArgumentEncoder* encoder) const
     encoder->encode(userAgent);
     encoder->encode(contentsScaleFactor);
     encoder->encode(isPrivateBrowsingEnabled);
+    encoder->encode(asynchronousCreationIncomplete);
+    encoder->encode(artificialPluginInitializationDelayEnabled);
 
 #if USE(ACCELERATED_COMPOSITING)
     encoder->encode(isAcceleratedCompositingEnabled);
@@ -77,6 +81,12 @@ bool PluginCreationParameters::decode(CoreIPC::ArgumentDecoder* decoder, PluginC
     if (!decoder->decode(result.isPrivateBrowsingEnabled))
         return false;
 
+    if (!decoder->decode(result.asynchronousCreationIncomplete))
+        return false;
+
+    if (!decoder->decode(result.artificialPluginInitializationDelayEnabled))
+        return false;
+
 #if USE(ACCELERATED_COMPOSITING)
     if (!decoder->decode(result.isAcceleratedCompositingEnabled))
         return false;
index fcd5349..6f05bda 100644 (file)
@@ -60,6 +60,12 @@ struct PluginCreationParameters {
 
     // Whether private browsing is enabled at the time of instantiation.
     bool isPrivateBrowsingEnabled;
+    
+    // If requesting synchronous initialization, whether this plugin had previously been requested asynchronously
+    bool asynchronousCreationIncomplete;
+
+    // Simulated initialization delay test asynchronous plugin initialization
+    bool artificialPluginInitializationDelayEnabled;
 
 #if USE(ACCELERATED_COMPOSITING)
     // Whether accelerated compositing is enabled.
index bb1b3b8..4346133 100644 (file)
@@ -32,8 +32,9 @@
 #include "Attachment.h"
 #include "NetscapePlugin.h"
 #include "NetscapePluginModule.h"
-#include "PluginProcessProxyMessages.h"
+#include "PluginProcessConnectionMessages.h"
 #include "PluginProcessCreationParameters.h"
+#include "PluginProcessProxyMessages.h"
 #include "WebProcessConnection.h"
 #include <WebCore/NotImplemented.h>
 #include <WebCore/RunLoop.h>
@@ -74,6 +75,7 @@ PluginProcess& PluginProcess::shared()
 
 PluginProcess::PluginProcess()
     : ChildProcess(shutdownTimeout)
+    , m_supportsAsynchronousPluginInitialization(false)
 #if PLATFORM(MAC)
     , m_compositingRenderServerPort(MACH_PORT_NULL)
 #endif
@@ -159,6 +161,7 @@ void PluginProcess::initializePluginProcess(const PluginProcessCreationParameter
     ASSERT(!m_pluginModule);
 
     m_pluginPath = parameters.pluginPath;
+    m_supportsAsynchronousPluginInitialization = parameters.supportsAsynchronousPluginInitialization;
 
     platformInitialize(parameters);
 }
@@ -177,7 +180,7 @@ void PluginProcess::createWebProcessConnection()
     m_webProcessConnections.append(connection.release());
 
     CoreIPC::Attachment clientPort(listeningPort, MACH_MSG_TYPE_MAKE_SEND);
-    m_connection->send(Messages::PluginProcessProxy::DidCreateWebProcessConnection(clientPort), 0);
+    m_connection->send(Messages::PluginProcessProxy::DidCreateWebProcessConnection(clientPort, m_supportsAsynchronousPluginInitialization), 0);
 #elif USE(UNIX_DOMAIN_SOCKETS)
     int sockets[2];
     if (socketpair(AF_UNIX, SOCKET_TYPE, 0, sockets) == -1) {
@@ -209,7 +212,7 @@ void PluginProcess::createWebProcessConnection()
     m_webProcessConnections.append(connection.release());
 
     CoreIPC::Attachment clientSocket(sockets[0]);
-    m_connection->send(Messages::PluginProcessProxy::DidCreateWebProcessConnection(clientSocket), 0);
+    m_connection->send(Messages::PluginProcessProxy::DidCreateWebProcessConnection(clientSocket, m_supportsAsynchronousPluginInitialization), 0);
 #else
     notImplemented();
 #endif
index 633bb41..bcbd200 100644 (file)
@@ -99,6 +99,8 @@ private:
     // The plug-in module.
     RefPtr<NetscapePluginModule> m_pluginModule;
     
+    bool m_supportsAsynchronousPluginInitialization;
+    
 #if USE(ACCELERATED_COMPOSITING) && PLATFORM(MAC)
     // The Mach port used for accelerated compositing.
     mach_port_t m_compositingRenderServerPort;
index 5ae80b9..1621ca0 100644 (file)
@@ -34,7 +34,9 @@
 #include "PluginCreationParameters.h"
 #include "PluginProcess.h"
 #include "PluginProcessConnectionMessages.h"
+#include "PluginProxyMessages.h"
 #include <WebCore/RunLoop.h>
+#include <unistd.h>
 
 using namespace WebCore;
 
@@ -160,6 +162,11 @@ void WebProcessConnection::didReceiveMessage(CoreIPC::Connection* connection, Co
 {
     ConnectionStack::CurrentConnectionPusher currentConnection(connectionStack(), connection);
 
+    if (messageID.is<CoreIPC::MessageClassWebProcessConnection>()) {
+        didReceiveWebProcessConnectionMessage(connection, messageID, arguments);
+        return;
+    }
+
     if (!arguments->destinationID()) {
         ASSERT_NOT_REACHED();
         return;
@@ -208,11 +215,20 @@ void WebProcessConnection::didClose(CoreIPC::Connection*)
         destroyPluginControllerProxy(pluginControllers[i]);
 }
 
-void WebProcessConnection::destroyPlugin(uint64_t pluginInstanceID)
+void WebProcessConnection::destroyPlugin(uint64_t pluginInstanceID, bool asynchronousCreationIncomplete)
 {
     PluginControllerProxy* pluginControllerProxy = m_pluginControllers.get(pluginInstanceID);
-    ASSERT(pluginControllerProxy);
-
+    
+    // If there is no PluginControllerProxy then this plug-in doesn't exist yet and we probably have nothing to do.
+    if (!pluginControllerProxy) {
+        // If the plugin we're supposed to destroy was requested asynchronously and doesn't exist yet,
+        // we need to flag the instance ID so it is not created later.
+        if (asynchronousCreationIncomplete)
+            m_asynchronousInstanceIDsToIgnore.add(pluginInstanceID);
+        
+        return;
+    }
+    
     destroyPluginControllerProxy(pluginControllerProxy);
 }
 
@@ -225,7 +241,7 @@ void WebProcessConnection::syncMessageSendTimedOut(CoreIPC::Connection*)
 {
 }
 
-void WebProcessConnection::createPlugin(const PluginCreationParameters& creationParameters, bool& result, bool& wantsWheelEvents, uint32_t& remoteLayerClientID)
+void WebProcessConnection::createPluginInternal(const PluginCreationParameters& creationParameters, bool& result, bool& wantsWheelEvents, uint32_t& remoteLayerClientID)
 {
     OwnPtr<PluginControllerProxy> pluginControllerProxy = PluginControllerProxy::create(this, creationParameters);
 
@@ -247,6 +263,58 @@ void WebProcessConnection::createPlugin(const PluginCreationParameters& creation
 #endif
 }
 
+void WebProcessConnection::createPlugin(const PluginCreationParameters& creationParameters, bool& result, bool& wantsWheelEvents, uint32_t& remoteLayerClientID)
+{
+    PluginControllerProxy* pluginControllerProxy = m_pluginControllers.get(creationParameters.pluginInstanceID);
+
+    // The plug-in we're being asked to create synchronously might already exist if we just finished creating it asynchronously.
+    // In that case we need to not create it again (but also need to return the correct information about its creation).
+    if (pluginControllerProxy) {
+        result = true;
+        wantsWheelEvents = pluginControllerProxy->wantsWheelEvents();
+#if PLATFORM(MAC)
+        remoteLayerClientID = pluginControllerProxy->remoteLayerClientID();
+#endif
+        return;
+    }
+    
+    // The plugin we're supposed to create might have been requested asynchronously before.
+    // In that case we need to create it synchronously now but flag the instance ID so we don't recreate it asynchronously later.
+    if (creationParameters.asynchronousCreationIncomplete)
+        m_asynchronousInstanceIDsToIgnore.add(creationParameters.pluginInstanceID);
+    
+    createPluginInternal(creationParameters, result, wantsWheelEvents, remoteLayerClientID);
+}
+
+void WebProcessConnection::createPluginAsynchronously(const PluginCreationParameters& creationParameters)
+{
+    // In the time since this plugin was requested asynchronously we might have created it synchronously or destroyed it.
+    // In either of those cases we need to ignore this creation request.
+    if (m_asynchronousInstanceIDsToIgnore.contains(creationParameters.pluginInstanceID)) {
+        m_asynchronousInstanceIDsToIgnore.remove(creationParameters.pluginInstanceID);
+        return;
+    }
+    
+    // This version of CreatePlugin is only used by plug-ins that are known to behave when started asynchronously.
+    bool result = false;
+    uint32_t remoteLayerClientID = 0;
+    bool wantsWheelEvents = false;
+    
+    if (creationParameters.artificialPluginInitializationDelayEnabled) {
+        unsigned artificialPluginInitializationDelay = 5;
+        sleep(artificialPluginInitializationDelay);
+    }
+
+    createPluginInternal(creationParameters, result, wantsWheelEvents, remoteLayerClientID);
+
+    if (!result) {
+        m_connection->sendSync(Messages::PluginProxy::DidFailToCreatePlugin(), Messages::PluginProxy::DidFailToCreatePlugin::Reply(), creationParameters.pluginInstanceID);
+        return;
+    }
+
+    m_connection->sendSync(Messages::PluginProxy::DidCreatePlugin(wantsWheelEvents, remoteLayerClientID), Messages::PluginProxy::DidCreatePlugin::Reply(), creationParameters.pluginInstanceID);
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(PLUGIN_PROCESS)
index f384671..53ca1e4 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "Connection.h"
 #include "Plugin.h"
+#include <wtf/HashSet.h>
 #include <wtf/RefCounted.h>
 
 namespace WebKit {
@@ -67,14 +68,19 @@ private:
     virtual void syncMessageSendTimedOut(CoreIPC::Connection*);
 
     // Message handlers.
+    void didReceiveWebProcessConnectionMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*);
     void didReceiveSyncWebProcessConnectionMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*, OwnPtr<CoreIPC::ArgumentEncoder>&);
     void createPlugin(const PluginCreationParameters&, bool& result, bool& wantsWheelEvents, uint32_t& remoteLayerClientID);
-    void destroyPlugin(uint64_t pluginInstanceID);
+    void createPluginAsynchronously(const PluginCreationParameters&);
+    void destroyPlugin(uint64_t pluginInstanceID, bool asynchronousCreationIncomplete);
+    
+    void createPluginInternal(const PluginCreationParameters&, bool& result, bool& wantsWheelEvents, uint32_t& remoteLayerClientID);
 
     RefPtr<CoreIPC::Connection> m_connection;
 
     HashMap<uint64_t, PluginControllerProxy*> m_pluginControllers;
     RefPtr<NPRemoteObjectMap> m_npRemoteObjectMap;
+    HashSet<uint64_t> m_asynchronousInstanceIDsToIgnore;
 };
 
 } // namespace WebKit
index 07c3c27..f1a8359 100644 (file)
@@ -26,8 +26,11 @@ messages -> WebProcessConnection {
     # Creates a plug-in instance using the given creation parameters.
     CreatePlugin(WebKit::PluginCreationParameters pluginCreationParameters) -> (bool result, bool wantsWheelEvents, uint32_t remoteLayerClientID)
 
+    # Creates a plug-in instance asynchronously using the given creation parameters.
+    CreatePluginAsynchronously(WebKit::PluginCreationParameters pluginCreationParameters)
+
     # Destroys the plug-in instance with the given instance ID.
-    DestroyPlugin(uint64_t pluginInstanceID) -> ()
+    DestroyPlugin(uint64_t pluginInstanceID, bool asynchronousCreationIncomplete) -> ()
 }
 
 #endif
index c6c87a6..0f036bb 100644 (file)
 namespace WebKit {
 
 PluginProcessCreationParameters::PluginProcessCreationParameters()
+    : supportsAsynchronousPluginInitialization(false)
 {
 }
 
 void PluginProcessCreationParameters::encode(CoreIPC::ArgumentEncoder* encoder) const
 {
     encoder->encode(pluginPath);
+    encoder->encode(supportsAsynchronousPluginInitialization);
 
 #if PLATFORM(MAC)
     encoder->encode(parentProcessName);
@@ -50,6 +52,8 @@ bool PluginProcessCreationParameters::decode(CoreIPC::ArgumentDecoder* decoder,
 {
     if (!decoder->decode(result.pluginPath))
         return false;
+    if (!decoder->decode(result.supportsAsynchronousPluginInitialization))
+        return false;
 
 #if PLATFORM(MAC)
     if (!decoder->decode(result.parentProcessName))
index 62cf4bb..cfaedcd 100644 (file)
@@ -48,6 +48,7 @@ struct PluginProcessCreationParameters {
     static bool decode(CoreIPC::ArgumentDecoder*, PluginProcessCreationParameters&);
 
     String pluginPath;
+    bool supportsAsynchronousPluginInitialization;
 
 #if PLATFORM(MAC)
     String parentProcessName;
index 43fa2f4..c9e0133 100644 (file)
@@ -137,9 +137,9 @@ void PluginProcessProxy::pluginProcessCrashedOrFailedToLaunch()
         RefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply = m_pendingConnectionReplies.takeFirst();
 
 #if PLATFORM(MAC)
-        reply->send(CoreIPC::Attachment(0, MACH_MSG_TYPE_MOVE_SEND));
+        reply->send(CoreIPC::Attachment(0, MACH_MSG_TYPE_MOVE_SEND), false);
 #elif USE(UNIX_DOMAIN_SOCKETS)
-        reply->send(CoreIPC::Attachment());
+        reply->send(CoreIPC::Attachment(), false);
 #else
         notImplemented();
 #endif
@@ -228,7 +228,7 @@ void PluginProcessProxy::didFinishLaunching(ProcessLauncher*, CoreIPC::Connectio
     m_numPendingConnectionRequests = 0;
 }
 
-void PluginProcessProxy::didCreateWebProcessConnection(const CoreIPC::Attachment& connectionIdentifier)
+void PluginProcessProxy::didCreateWebProcessConnection(const CoreIPC::Attachment& connectionIdentifier, bool supportsAsynchronousPluginInitialization)
 {
     ASSERT(!m_pendingConnectionReplies.isEmpty());
 
@@ -236,9 +236,9 @@ void PluginProcessProxy::didCreateWebProcessConnection(const CoreIPC::Attachment
     RefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply = m_pendingConnectionReplies.takeFirst();
 
 #if PLATFORM(MAC)
-    reply->send(CoreIPC::Attachment(connectionIdentifier.port(), MACH_MSG_TYPE_MOVE_SEND));
+    reply->send(CoreIPC::Attachment(connectionIdentifier.port(), MACH_MSG_TYPE_MOVE_SEND), supportsAsynchronousPluginInitialization);
 #elif USE(UNIX_DOMAIN_SOCKETS)
-    reply->send(connectionIdentifier);
+    reply->send(connectionIdentifier, supportsAsynchronousPluginInitialization);
 #else
     notImplemented();
 #endif
index 0a51d9b..e0fe15f 100644 (file)
@@ -108,7 +108,7 @@ private:
 
     // Message handlers
     void didReceivePluginProcessProxyMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*);
-    void didCreateWebProcessConnection(const CoreIPC::Attachment&);
+    void didCreateWebProcessConnection(const CoreIPC::Attachment&, bool supportsAsynchronousPluginInitialization);
     void didGetSitesWithData(const Vector<String>& sites, uint64_t callbackID);
     void didClearSiteData(uint64_t callbackID);
 
index 7b59aef..183cb0c 100644 (file)
@@ -23,7 +23,7 @@
 #if ENABLE(PLUGIN_PROCESS)
 
 messages -> PluginProcessProxy {
-    DidCreateWebProcessConnection(CoreIPC::Attachment connectionIdentifier)
+    DidCreateWebProcessConnection(CoreIPC::Attachment connectionIdentifier, bool supportsAsynchronousPluginInitialization)
 
     DidGetSitesWithData(Vector<WTF::String> sites, uint64_t callbackID)
     DidClearSiteData(uint64_t callbackID)
index 3486de4..d4b4ba3 100644 (file)
@@ -118,6 +118,9 @@ bool PluginProcessProxy::createPropertyListFile(const PluginModuleInfo& plugin)
 
 void PluginProcessProxy::platformInitializePluginProcess(PluginProcessCreationParameters& parameters)
 {
+    // For know only Flash is known to behave with asynchronous plug-in initialization.
+    parameters.supportsAsynchronousPluginInitialization = m_pluginInfo.bundleIdentifier == "com.macromedia.Flash Player.plugin";
+
 #if USE(ACCELERATED_COMPOSITING) && HAVE(HOSTED_CORE_ANIMATION)
     parameters.parentProcessName = [[NSProcessInfo processInfo] processName];
 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
index 9e7b529..84805df 100644 (file)
@@ -34,7 +34,7 @@ messages -> WebProcessProxy {
     ShouldTerminate() -> (bool shouldTerminate)
 
 #if ENABLE(PLUGIN_PROCESS)
-    GetPluginProcessConnection(WTF::String pluginPath) -> (CoreIPC::Attachment connectionHandle) Delayed
+    GetPluginProcessConnection(WTF::String pluginPath) -> (CoreIPC::Attachment connectionHandle, bool supportsAsynchronousInitialization) Delayed
     PluginSyncMessageSendTimedOut(WTF::String pluginPath)
 #endif
 
index 1c49c60..8591892 100644 (file)
@@ -54,6 +54,10 @@ public:
 
     static PassRefPtr<NetscapePlugin> fromNPP(NPP);
 
+    // In-process NetscapePlugins don't support asynchronous initialization.
+    virtual void waitForAsynchronousInitialization() { }
+    virtual bool isBeingAsynchronouslyInitialized() const { return false; }
+
 #if PLATFORM(MAC)
     NPError setDrawingModel(NPDrawingModel);
     NPError setEventModel(NPEventModel);
index 6d11c85..8a7c28a 100644 (file)
@@ -50,6 +50,10 @@ public:
 
     static WebCore::PluginInfo pluginInfo();
 
+    // In-process PDFViews don't support asynchronous initialization.
+    virtual void waitForAsynchronousInitialization() { }
+    virtual bool isBeingAsynchronouslyInitialized() const { return false; }
+
 private:
     explicit BuiltInPDFView(WebFrame*);
 
index 5be84f4..b91174f 100644 (file)
@@ -83,6 +83,10 @@ public:
     // Sets the active plug-in controller and initializes the plug-in.
     bool initialize(PluginController*, const Parameters&);
 
+    // Forces synchronous initialization of a plugin previously initialized asynchronously.
+    virtual void waitForAsynchronousInitialization() = 0;
+    virtual bool isBeingAsynchronouslyInitialized() const = 0;
+
     // Destroys the plug-in.
     void destroyPlugin();
 
index 42c2959..6682ba0 100644 (file)
@@ -138,6 +138,15 @@ public:
 
     // Returns whether private browsing is enabled.
     virtual bool isPrivateBrowsingEnabled() = 0;
+    
+    // Returns whether or not asynchronous plugin initialization is enabled.
+    virtual bool asynchronousPluginInitializationEnabled() const { return false; }
+    
+    // Returns whether or not asynchronous plugin initialization should be attempted for all plugins.
+    virtual bool asynchronousPluginInitializationEnabledForAllPlugins() const { return false; }
+    
+    // Returns the articifical plugin delay to use for testing of asynchronous plugin initialization.
+    virtual bool artificialPluginInitializationDelayEnabled() const { return false; }
 
     // Increments a counter that prevents the plug-in from being destroyed.
     virtual void protectPluginFromDestruction() = 0;
@@ -151,6 +160,12 @@ public:
     virtual void windowedPluginGeometryDidChange(const WebCore::IntRect& frameRect, const WebCore::IntRect& clipRect, uint64_t windowID) = 0;
 #endif
 
+    // Called when the a plug-in instance is successfully initialized, either synchronously or asynchronously.
+    virtual void didInitializePlugin() = 0;
+    
+    // Called when the a plug-in instance fails to initialized, either synchronously or asynchronously.
+    virtual void didFailToInitializePlugin() = 0;
+
     // Helper class for delaying destruction of a plug-in.
     class PluginDestructionProtector {
     public:
index 8339ad4..cc313d2 100644 (file)
@@ -73,9 +73,10 @@ static double defaultSyncMessageTimeout(const String& pluginPath)
     return syncMessageTimeout;
 }
 
-PluginProcessConnection::PluginProcessConnection(PluginProcessConnectionManager* pluginProcessConnectionManager, const String& pluginPath, CoreIPC::Connection::Identifier connectionIdentifier)
+PluginProcessConnection::PluginProcessConnection(PluginProcessConnectionManager* pluginProcessConnectionManager, const String& pluginPath, CoreIPC::Connection::Identifier connectionIdentifier, bool supportsAsynchronousPluginInitialization)
     : m_pluginProcessConnectionManager(pluginProcessConnectionManager)
     , m_pluginPath(pluginPath)
+    , m_supportsAsynchronousPluginInitialization(supportsAsynchronousPluginInitialization)
 {
     m_connection = CoreIPC::Connection::createClientConnection(connectionIdentifier, this, WebProcess::shared().runLoop());
 
index 61c4ac2..bc866d1 100644 (file)
@@ -43,9 +43,9 @@ class PluginProxy;
     
 class PluginProcessConnection : public RefCounted<PluginProcessConnection>, CoreIPC::Connection::Client {
 public:
-    static PassRefPtr<PluginProcessConnection> create(PluginProcessConnectionManager* pluginProcessConnectionManager, const String& pluginPath, CoreIPC::Connection::Identifier connectionIdentifier)
+    static PassRefPtr<PluginProcessConnection> create(PluginProcessConnectionManager* pluginProcessConnectionManager, const String& pluginPath, CoreIPC::Connection::Identifier connectionIdentifier, bool supportsAsynchronousPluginInitialization)
     {
-        return adoptRef(new PluginProcessConnection(pluginProcessConnectionManager, pluginPath, connectionIdentifier));
+        return adoptRef(new PluginProcessConnection(pluginProcessConnectionManager, pluginPath, connectionIdentifier, supportsAsynchronousPluginInitialization));
     }
     ~PluginProcessConnection();
 
@@ -58,8 +58,10 @@ public:
 
     NPRemoteObjectMap* npRemoteObjectMap() const { return m_npRemoteObjectMap.get(); }
 
+    bool supportsAsynchronousPluginInitialization() const { return m_supportsAsynchronousPluginInitialization; }
+
 private:
-    PluginProcessConnection(PluginProcessConnectionManager* pluginProcessConnectionManager, const String& pluginPath, CoreIPC::Connection::Identifier connectionIdentifier);
+    PluginProcessConnection(PluginProcessConnectionManager*, const String& pluginPath, CoreIPC::Connection::Identifier connectionIdentifier, bool supportsAsynchronousInitialization);
 
     // CoreIPC::Connection::Client
     virtual void didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*);
@@ -82,6 +84,8 @@ private:
     HashMap<uint64_t, PluginProxy*> m_plugins;
 
     RefPtr<NPRemoteObjectMap> m_npRemoteObjectMap;
+    
+    bool m_supportsAsynchronousPluginInitialization;
 };
 
 } // namespace WebKit
index fd1906d..33d12cb 100644 (file)
@@ -57,8 +57,9 @@ PluginProcessConnection* PluginProcessConnectionManager::getPluginProcessConnect
     }
 
     CoreIPC::Attachment encodedConnectionIdentifier;
+    bool supportsAsynchronousInitialization;
     if (!WebProcess::shared().connection()->sendSync(Messages::WebProcessProxy::GetPluginProcessConnection(pluginPath),
-                                                     Messages::WebProcessProxy::GetPluginProcessConnection::Reply(encodedConnectionIdentifier), 0))
+                                                     Messages::WebProcessProxy::GetPluginProcessConnection::Reply(encodedConnectionIdentifier, supportsAsynchronousInitialization), 0))
         return 0;
 
 #if PLATFORM(MAC)
@@ -71,7 +72,7 @@ PluginProcessConnection* PluginProcessConnectionManager::getPluginProcessConnect
         return 0;
 #endif
 
-    RefPtr<PluginProcessConnection> pluginProcessConnection = PluginProcessConnection::create(this, pluginPath, connectionIdentifier);
+    RefPtr<PluginProcessConnection> pluginProcessConnection = PluginProcessConnection::create(this, pluginPath, connectionIdentifier, supportsAsynchronousInitialization);
     m_pluginProcessConnections.append(pluginProcessConnection);
 
     {
index d4a3549..d3af06b 100644 (file)
@@ -68,6 +68,7 @@ PluginProxy::PluginProxy(const String& pluginPath)
     , m_waitingForPaintInResponseToUpdate(false)
     , m_wantsWheelEvents(false)
     , m_remoteLayerClientID(0)
+    , m_waitingOnAsynchronousInitialization(false)
 {
 }
 
@@ -93,38 +94,109 @@ bool PluginProxy::initialize(const Parameters& parameters)
     m_connection->addPluginProxy(this);
 
     // Ask the plug-in process to create a plug-in.
-    PluginCreationParameters creationParameters;
-    creationParameters.pluginInstanceID = m_pluginInstanceID;
-    creationParameters.windowNPObjectID = windowNPObjectID();
-    creationParameters.parameters = parameters;
-    creationParameters.userAgent = controller()->userAgent();
-    creationParameters.contentsScaleFactor = contentsScaleFactor();
-    creationParameters.isPrivateBrowsingEnabled = controller()->isPrivateBrowsingEnabled();
+    m_pendingPluginCreationParameters = adoptPtr(new PluginCreationParameters);
+
+    m_pendingPluginCreationParameters->pluginInstanceID = m_pluginInstanceID;
+    m_pendingPluginCreationParameters->windowNPObjectID = windowNPObjectID();
+    m_pendingPluginCreationParameters->parameters = parameters;
+    m_pendingPluginCreationParameters->userAgent = controller()->userAgent();
+    m_pendingPluginCreationParameters->contentsScaleFactor = contentsScaleFactor();
+    m_pendingPluginCreationParameters->isPrivateBrowsingEnabled = controller()->isPrivateBrowsingEnabled();
+    m_pendingPluginCreationParameters->artificialPluginInitializationDelayEnabled = controller()->artificialPluginInitializationDelayEnabled();
+
 #if USE(ACCELERATED_COMPOSITING)
-    creationParameters.isAcceleratedCompositingEnabled = controller()->isAcceleratedCompositingEnabled();
+    m_pendingPluginCreationParameters->isAcceleratedCompositingEnabled = controller()->isAcceleratedCompositingEnabled();
 #endif
 
+    if (!canInitializeAsynchronously())
+        return initializeSynchronously();
+
+    // Remember that we tried to create this plug-in asynchronously in case we need to create it synchronously later.
+    m_waitingOnAsynchronousInitialization = true;
+    PluginCreationParameters creationParameters(*m_pendingPluginCreationParameters.get());
+    m_connection->connection()->send(Messages::WebProcessConnection::CreatePluginAsynchronously(creationParameters), m_pluginInstanceID);
+    return true;
+}
+
+bool PluginProxy::canInitializeAsynchronously() const
+{
+    return controller()->asynchronousPluginInitializationEnabled() && (m_connection->supportsAsynchronousPluginInitialization() || controller()->asynchronousPluginInitializationEnabledForAllPlugins());
+}
+
+void PluginProxy::waitForAsynchronousInitialization()
+{
+    ASSERT(!m_isStarted);
+    ASSERT(m_waitingOnAsynchronousInitialization);
+
+    initializeSynchronously();
+}
+
+bool PluginProxy::initializeSynchronously()
+{
+    ASSERT(m_pendingPluginCreationParameters);
+
+    m_pendingPluginCreationParameters->asynchronousCreationIncomplete = m_waitingOnAsynchronousInitialization;
     bool result = false;
     bool wantsWheelEvents = false;
     uint32_t remoteLayerClientID = 0;
+    
+    PluginCreationParameters parameters(*m_pendingPluginCreationParameters.get());
 
-    if (!m_connection->connection()->sendSync(Messages::WebProcessConnection::CreatePlugin(creationParameters), Messages::WebProcessConnection::CreatePlugin::Reply(result, wantsWheelEvents, remoteLayerClientID), 0) || !result) {
-        m_connection->removePluginProxy(this);
-        return false;
-    }
+    if (!m_connection->connection()->sendSync(Messages::WebProcessConnection::CreatePlugin(parameters), Messages::WebProcessConnection::CreatePlugin::Reply(result, wantsWheelEvents, remoteLayerClientID), 0) || !result)
+        didFailToCreatePluginInternal();
+    else
+        didCreatePluginInternal(wantsWheelEvents, remoteLayerClientID);
+    
+    return result;
+}
 
+void PluginProxy::didCreatePlugin(bool wantsWheelEvents, uint32_t remoteLayerClientID)
+{
+    // We might have tried to create the plug-in sychronously while waiting on the asynchronous reply,
+    // in which case we should ignore this message.
+    if (!m_waitingOnAsynchronousInitialization)
+        return;
+
+    didCreatePluginInternal(wantsWheelEvents, remoteLayerClientID);
+}
+
+void PluginProxy::didFailToCreatePlugin()
+{
+    // We might have tried to create the plug-in sychronously while waiting on the asynchronous reply,
+    // in which case we should ignore this message.
+    if (!m_waitingOnAsynchronousInitialization)
+        return;
+
+    didFailToCreatePluginInternal();
+}
+
+void PluginProxy::didCreatePluginInternal(bool wantsWheelEvents, uint32_t remoteLayerClientID)
+{
     m_wantsWheelEvents = wantsWheelEvents;
     m_remoteLayerClientID = remoteLayerClientID;
     m_isStarted = true;
+    controller()->didInitializePlugin();
 
-    return true;
+    // Whether synchronously or asynchronously, this plug-in was created and we shouldn't need to remember
+    // anything about how.
+    m_pendingPluginCreationParameters.clear();
+    m_waitingOnAsynchronousInitialization = false;
 }
 
-void PluginProxy::destroy()
+void PluginProxy::didFailToCreatePluginInternal()
 {
-    ASSERT(m_isStarted);
+    m_connection->removePluginProxy(this);
+    controller()->didFailToInitializePlugin();
 
-    m_connection->connection()->sendSync(Messages::WebProcessConnection::DestroyPlugin(m_pluginInstanceID), Messages::WebProcessConnection::DestroyPlugin::Reply(), 0);
+    // Whether synchronously or asynchronously, this plug-in failed to create and we shouldn't need to remember
+    // anything about how.
+    m_pendingPluginCreationParameters.clear();
+    m_waitingOnAsynchronousInitialization = false;
+}
+
+void PluginProxy::destroy()
+{
+    m_connection->connection()->sendSync(Messages::WebProcessConnection::DestroyPlugin(m_pluginInstanceID, m_waitingOnAsynchronousInitialization), Messages::WebProcessConnection::DestroyPlugin::Reply(), 0);
 
     m_isStarted = false;
 
index be07caa..c0ca116 100644 (file)
@@ -49,6 +49,8 @@ class ShareableBitmap;
 class NPVariantData;
 class PluginProcessConnection;
 
+struct PluginCreationParameters;
+
 class PluginProxy : public Plugin {
 public:
     static PassRefPtr<PluginProxy> create(const String& pluginPath);
@@ -60,11 +62,16 @@ public:
     void didReceivePluginProxyMessage(CoreIPC::Connection*, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments);
     void didReceiveSyncPluginProxyMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*, OwnPtr<CoreIPC::ArgumentEncoder>&);
 
+    bool isBeingAsynchronouslyInitialized() const { return m_waitingOnAsynchronousInitialization; }
+
 private:
     explicit PluginProxy(const String& pluginPath);
 
     // Plugin
     virtual bool initialize(const Parameters&);
+    bool initializeSynchronously();
+
+    virtual void waitForAsynchronousInitialization();
     virtual void destroy();
     virtual void paint(WebCore::GraphicsContext*, const WebCore::IntRect& dirtyRect);
     virtual PassRefPtr<ShareableBitmap> snapshot();
@@ -141,6 +148,14 @@ private:
     void windowedPluginGeometryDidChange(const WebCore::IntRect& frameRect, const WebCore::IntRect& clipRect, uint64_t windowID);
 #endif
 
+    bool canInitializeAsynchronously() const;
+
+    void didCreatePlugin(bool wantsWheelEvents, uint32_t remoteLayerClientID);
+    void didFailToCreatePlugin();
+    
+    void didCreatePluginInternal(bool wantsWheelEvents, uint32_t remoteLayerClientID);
+    void didFailToCreatePluginInternal();
+
     String m_pluginPath;
 
     RefPtr<PluginProcessConnection> m_connection;
@@ -174,6 +189,9 @@ private:
 
     // The client ID for the CA layer in the plug-in process. Will be 0 if the plug-in is not a CA plug-in.
     uint32_t m_remoteLayerClientID;
+    
+    OwnPtr<PluginCreationParameters> m_pendingPluginCreationParameters;
+    bool m_waitingOnAsynchronousInitialization;
 
 #if PLATFORM(MAC)
     RetainPtr<CALayer> m_pluginLayer;
index a2ef319..453c0f1 100644 (file)
@@ -74,6 +74,12 @@ messages -> PluginProxy {
     # Update geometry of windowed plugin widget
     WindowedPluginGeometryDidChange(WebCore::IntRect frameRect, WebCore::IntRect clipRect, uint64_t windowID)
 #endif
+
+    # Tells the WebProcess that the plug-in was successfully initialized asynchronously
+    DidCreatePlugin(bool wantsWheelEvents, uint32_t remoteLayerClientID) -> ()
+    
+    # Tells the WebProcess that the plug-in failed to initialize.
+    DidFailToCreatePlugin() -> ()
 }
 
 #endif
index 072a69c..d07f8b7 100644 (file)
@@ -260,6 +260,7 @@ PluginView::PluginView(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<P
     , m_webPage(webPage(m_pluginElement.get()))
     , m_parameters(parameters)
     , m_isInitialized(false)
+    , m_isWaitingForSynchronousInitialization(false)
     , m_isWaitingUntilMediaCanStart(false)
     , m_isBeingDestroyed(false)
     , m_pendingURLRequestsTimer(RunLoop::main(), this, &PluginView::pendingURLRequestsTimerFired)
@@ -285,7 +286,7 @@ PluginView::~PluginView()
     for (FrameLoadMap::iterator it = m_pendingFrameLoads.begin(), end = m_pendingFrameLoads.end(); it != end; ++it)
         it->first->setLoadListener(0);
 
-    if (m_plugin && m_isInitialized) {
+    if (m_plugin) {
         m_isBeingDestroyed = true;
         m_plugin->destroyPlugin();
         m_isBeingDestroyed = false;
@@ -487,15 +488,20 @@ void PluginView::initializePlugin()
             }
         }
     }
-    
-    if (!m_plugin->initialize(this, m_parameters)) {
-        // We failed to initialize the plug-in.
-        m_plugin = 0;
 
-        m_webPage->send(Messages::WebPageProxy::DidFailToInitializePlugin(m_parameters.mimeType));
-        return;
-    }
+    m_plugin->initialize(this, m_parameters);
     
+    // Plug-in initialization continued in didFailToInitializePlugin() or didInitializePlugin().
+}
+
+void PluginView::didFailToInitializePlugin()
+{
+    m_plugin = 0;
+    m_webPage->send(Messages::WebPageProxy::DidFailToInitializePlugin(m_parameters.mimeType));
+}
+
+void PluginView::didInitializePlugin()
+{
     m_isInitialized = true;
 
 #if PLATFORM(MAC)
@@ -539,6 +545,19 @@ PlatformLayer* PluginView::platformLayer() const
 
 JSObject* PluginView::scriptObject(JSGlobalObject* globalObject)
 {
+    // If we're already waiting for synchronous initialization of the plugin,
+    // calls to scriptObject() are from the plug-in itself and need to return 0;
+    if (m_isWaitingForSynchronousInitialization)
+        return 0;
+
+    // If the plug-in exists but is not initialized then we're still initializing asynchronously.
+    // We need to wait here until initialization has either succeeded or failed.
+    if (m_plugin->isBeingAsynchronouslyInitialized()) {
+        m_isWaitingForSynchronousInitialization = true;
+        m_plugin->waitForAsynchronousInitialization();
+        m_isWaitingForSynchronousInitialization = false;
+    }
+
     // The plug-in can be null here if it failed to initialize.
     if (!m_isInitialized || !m_plugin)
         return 0;
@@ -1250,6 +1269,21 @@ bool PluginView::isPrivateBrowsingEnabled()
     return settings->privateBrowsingEnabled();
 }
 
+bool PluginView::asynchronousPluginInitializationEnabled() const
+{
+    return m_webPage->asynchronousPluginInitializationEnabled();
+}
+
+bool PluginView::asynchronousPluginInitializationEnabledForAllPlugins() const
+{
+    return m_webPage->asynchronousPluginInitializationEnabledForAllPlugins();
+}
+
+bool PluginView::artificialPluginInitializationDelayEnabled() const
+{
+    return m_webPage->artificialPluginInitializationDelayEnabled();
+}
+
 void PluginView::protectPluginFromDestruction()
 {
     if (!m_isBeingDestroyed)
index f101b2f..a6a056c 100644 (file)
@@ -80,7 +80,6 @@ private:
     virtual ~PluginView();
 
     void initializePlugin();
-    void destroyPlugin();
 
     void viewGeometryDidChange();
     void viewVisibilityDidChange();
@@ -164,6 +163,9 @@ private:
     virtual void setCookiesForURL(const String& urlString, const String& cookieString);
     virtual bool getAuthenticationInfo(const WebCore::ProtectionSpace&, String& username, String& password);
     virtual bool isPrivateBrowsingEnabled();
+    virtual bool asynchronousPluginInitializationEnabled() const;
+    virtual bool asynchronousPluginInitializationEnabledForAllPlugins() const;
+    virtual bool artificialPluginInitializationDelayEnabled() const;
     virtual void protectPluginFromDestruction();
     virtual void unprotectPluginFromDestruction();
 #if PLUGIN_ARCHITECTURE(X11)
@@ -171,6 +173,9 @@ private:
     virtual void windowedPluginGeometryDidChange(const WebCore::IntRect& frameRect, const WebCore::IntRect& clipRect, uint64_t windowID);
 #endif
 
+    virtual void didInitializePlugin();
+    virtual void didFailToInitializePlugin();
+
     // WebFrame::LoadListener
     virtual void didFinishLoad(WebFrame*);
     virtual void didFailLoad(WebFrame*, bool wasCancelled);
@@ -181,6 +186,7 @@ private:
     Plugin::Parameters m_parameters;
     
     bool m_isInitialized;
+    bool m_isWaitingForSynchronousInitialization;
     bool m_isWaitingUntilMediaCanStart;
     bool m_isBeingDestroyed;
 
index 8c76672..9a0d302 100644 (file)
@@ -1,3 +1,19 @@
+2012-08-03  Brady Eidson  <beidson@apple.com>
+
+        Out-of-process plug-ins should support asynchronous initialization
+        <rdar://problem/10598594> and https://bugs.webkit.org/show_bug.cgi?id=92919
+
+        Reviewed by Anders Carlsson.
+
+        Add a plug-in with an NPP_New that takes 550ms (a reasonable trade-off between a solid test and a slow running test)
+        for testing asynchronous plug-in initialization.
+
+        * DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj:
+        * DumpRenderTree/TestNetscapePlugIn/Tests/SlowNPPNew.cpp: Copied from Source/WebKit2/Shared/Plugins/PluginProcessCreationParameters.h.
+        (SlowNPPNew):
+        (SlowNPPNew::SlowNPPNew):
+        (SlowNPPNew::NPP_New):
+
 2012-08-06  Xianzhu Wang  <wangxianzhu@chromium.org>
 
         [Chromium-Android] Virtual test suites fail
index 481064e..b7904f6 100644 (file)
@@ -67,6 +67,7 @@
                4437730E125CBC3600AAE02C /* WebArchiveDumpSupport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 44A997830FCDE86400580F10 /* WebArchiveDumpSupport.cpp */; };
                4437730F125CBC4D00AAE02C /* WebArchiveDumpSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 44A997820FCDE86400580F10 /* WebArchiveDumpSupport.h */; };
                4AD6A11413C8124000EA9737 /* FormValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4AD6A11313C8124000EA9737 /* FormValue.cpp */; };
+               5106803E15CC7B10001A8A23 /* SlowNPPNew.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5106803D15CC7B10001A8A23 /* SlowNPPNew.cpp */; };
                515F429C15C07872007C8F90 /* PluginScriptableObjectOverridesAllProperties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 515F429B15C07872007C8F90 /* PluginScriptableObjectOverridesAllProperties.cpp */; };
                5185F6B210714E07007AA393 /* HistoryDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5185F69F10714A57007AA393 /* HistoryDelegate.mm */; };
                5185F6B310714E12007AA393 /* HistoryDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5185F69E10714A57007AA393 /* HistoryDelegate.h */; };
                44A997820FCDE86400580F10 /* WebArchiveDumpSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebArchiveDumpSupport.h; path = cf/WebArchiveDumpSupport.h; sourceTree = "<group>"; };
                44A997830FCDE86400580F10 /* WebArchiveDumpSupport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebArchiveDumpSupport.cpp; path = cf/WebArchiveDumpSupport.cpp; sourceTree = "<group>"; };
                4AD6A11313C8124000EA9737 /* FormValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FormValue.cpp; sourceTree = "<group>"; };
+               5106803D15CC7B10001A8A23 /* SlowNPPNew.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SlowNPPNew.cpp; path = TestNetscapePlugIn/Tests/SlowNPPNew.cpp; sourceTree = SOURCE_ROOT; };
                515F429B15C07872007C8F90 /* PluginScriptableObjectOverridesAllProperties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PluginScriptableObjectOverridesAllProperties.cpp; sourceTree = "<group>"; };
                5185F69E10714A57007AA393 /* HistoryDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HistoryDelegate.h; path = mac/HistoryDelegate.h; sourceTree = "<group>"; };
                5185F69F10714A57007AA393 /* HistoryDelegate.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = HistoryDelegate.mm; path = mac/HistoryDelegate.mm; sourceTree = "<group>"; };
                                1AD9D2FD12028409001A70D1 /* PluginScriptableNPObjectInvokeDefault.cpp */,
                                515F429B15C07872007C8F90 /* PluginScriptableObjectOverridesAllProperties.cpp */,
                                1A1E4296141141C400388758 /* PrivateBrowsing.cpp */,
+                               5106803D15CC7B10001A8A23 /* SlowNPPNew.cpp */,
                        );
                        path = Tests;
                        sourceTree = "<group>";
                                1A1E4298141141C400388758 /* PrivateBrowsing.cpp in Sources */,
                                1A66C35114576A920099A115 /* ContentsScaleFactor.cpp in Sources */,
                                515F429C15C07872007C8F90 /* PluginScriptableObjectOverridesAllProperties.cpp in Sources */,
+                               5106803E15CC7B10001A8A23 /* SlowNPPNew.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
diff --git a/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/SlowNPPNew.cpp b/Tools/DumpRenderTree/TestNetscapePlugIn/Tests/SlowNPPNew.cpp
new file mode 100644 (file)
index 0000000..bd2a76a
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginTest.h"
+
+#include <string.h>
+
+using namespace std;
+
+class SlowNPPNew : public PluginTest {
+public:
+    SlowNPPNew(NPP npp, const string& identifier)
+        : PluginTest(npp, identifier)
+    {
+    }
+    
+private:
+    
+    virtual NPError NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData *saved)
+    {
+        usleep(550000);
+        return NPERR_NO_ERROR;
+    }
+};
+
+static PluginTest::Register<SlowNPPNew> slowNPPNew("slow-npp-new");