Make plug-in loads of javascript: URIs execute synchronously. Bug 364028,r=biesi, sr=jst
authorbzbarsky@mit.edu
Wed, 13 Jun 2007 15:42:07 -0700
changeset 2364 a531162bf671f514cc9833c2700dde93a393cbd6
parent 2363 90fbe2c58f079102aab5210602c061e6c2e08ca2
child 2365 5c7f9597390f0daf34073b8e6f513cd338451e24
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi, jst
bugs364028
milestone1.9a6pre
Make plug-in loads of javascript: URIs execute synchronously. Bug 364028,r=biesi, sr=jst
dom/public/nsIScriptChannel.idl
dom/src/jsurl/nsJSProtocolHandler.cpp
modules/plugin/base/src/nsPluginHostImpl.cpp
--- a/dom/public/nsIScriptChannel.idl
+++ b/dom/public/nsIScriptChannel.idl
@@ -49,17 +49,17 @@
  *   executed at all, no matter what.  This is necessary because in this
  *   circumstance we have no way to tell whether script execution is allowed at
  *   all for the originating security context of this channel. 
  * - If the channel has an owner principal, how it is executed is controlled by
  *   this interface.  However if the owner principal does not subsume the
  *   principal of the environment in which the program is to be executed the
  *   execution will be forced to happen in a sandbox.
  */
-[scriptable, uuid(6dec981b-6fce-4f57-bf97-d518c6079c77)]
+[scriptable, uuid(33234b99-9588-4c7d-9da6-86b8b7cba565)]
 interface nsIScriptChannel : nsISupports
 {
   /**
    * Possible ways of executing the program.
    */
 
   /**
    * Don't execute at all.
@@ -80,9 +80,23 @@ interface nsIScriptChannel : nsISupports
   /**
    * Whether and how the program represented by this channel is to be executed.
    * The default value if this property has never been set on this channel MUST
    * be either EXECUTE_IN_SANDBOX or NO_EXECUTION.
    *
    * @throws NS_ERROR_INVALID_ARG when set to an unrecognized value.
    */
   attribute unsigned long executionPolicy;
+
+  /**
+   * Control whether the program should be executed synchronosly when
+   * the channel's AsyncOpen method is called or whether it should be
+   * executed asynchronously.  In both cases, any data that the
+   * channel returns will be returned asynchronously; the only thing
+   * this property affects is when the program executes.
+   *
+   * The default value of this property is TRUE.
+   *
+   * Setting this property after asyncOpen has been called on the
+   * channel has no effect.
+   */
+  attribute boolean executeAsync;   
 };
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -424,26 +424,28 @@ protected:
     nsresult mStatus; // Our status
 
     nsLoadFlags             mLoadFlags;
     nsLoadFlags             mActualLoadFlags; // See AsyncOpen
 
     nsRefPtr<nsJSThunk>     mIOThunk;
     PopupControlState       mPopupState;
     PRUint32                mExecutionPolicy;
+    PRPackedBool            mIsAsync;
     PRPackedBool            mIsActive;
     PRPackedBool            mOpenedStreamChannel;
 };
 
 nsJSChannel::nsJSChannel() :
     mStatus(NS_OK),
     mLoadFlags(LOAD_NORMAL),
     mActualLoadFlags(LOAD_NORMAL),
     mPopupState(openOverridden),
     mExecutionPolicy(EXECUTE_IN_SANDBOX),
+    mIsAsync(PR_TRUE),
     mIsActive(PR_FALSE),
     mOpenedStreamChannel(PR_FALSE)
 {
 }
 
 nsJSChannel::~nsJSChannel()
 {
 }
@@ -625,31 +627,61 @@ nsJSChannel::AsyncOpen(nsIStreamListener
     // Add the javascript channel to its loadgroup so that we know if
     // network loads were canceled or not...
     nsCOMPtr<nsILoadGroup> loadGroup;
     mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
     if (loadGroup) {
         loadGroup->AddRequest(this, nsnull);
     }
 
-    // post an event to do the rest
-    nsCOMPtr<nsIRunnable> ev =
-        new nsRunnableMethod<nsJSChannel>(this, &nsJSChannel::EvaluateScript);
+    mPopupState = win->GetPopupControlState();
+
+    nsRunnableMethod<nsJSChannel>::Method method;
+    if (mIsAsync) {
+        // post an event to do the rest
+        method = &nsJSChannel::EvaluateScript;
+    } else {   
+        EvaluateScript();
+        if (mOpenedStreamChannel) {
+            // That will handle notifying things
+            return NS_OK;
+        }
+
+        NS_ASSERTION(NS_FAILED(mStatus), "We should have failed _somehow_");
+
+        // mStatus is going to be NS_ERROR_DOM_RETVAL_UNDEFINED if we didn't
+        // have any content resulting from the execution and NS_BINDING_ABORTED
+        // if something we did causes our own load to be stopped.  Return
+        // success in those cases, and error out in all others.
+        if (mStatus != NS_ERROR_DOM_RETVAL_UNDEFINED &&
+            mStatus != NS_BINDING_ABORTED) {
+            // Note that calling EvaluateScript() handled removing us from the
+            // loadgroup and marking us as not active anymore.
+            mListener = nsnull;
+            mContext = nsnull;
+            mOriginalInnerWindow = nsnull;
+            return mStatus;
+        }
+
+        // We're returning success from asyncOpen(), but we didn't open a
+        // stream channel.  We'll have to notify ourselves, but make sure to do
+        // it asynchronously.
+        method = &nsJSChannel::NotifyListener;            
+    }
+
+    nsCOMPtr<nsIRunnable> ev = new nsRunnableMethod<nsJSChannel>(this, method);
     nsresult rv = NS_DispatchToCurrentThread(ev);
 
     if (NS_FAILED(rv)) {
         loadGroup->RemoveRequest(this, nsnull, rv);
         mIsActive = PR_FALSE;
         mListener = nsnull;
         mContext = nsnull;
         mOriginalInnerWindow = nsnull;
     }
-
-    mPopupState = win->GetPopupControlState();
-
     return rv;
 }
 
 void
 nsJSChannel::EvaluateScript()
 {
     // Synchronously execute the script...
     // mIsActive is used to indicate the the request is 'busy' during the
@@ -682,17 +714,19 @@ nsJSChannel::EvaluateScript()
     // Reset load flags to their original value...
     mLoadFlags = mActualLoadFlags;
 
     // We're no longer active, it's now up to the stream channel to do
     // the loading, if needed.
     mIsActive = PR_FALSE;
 
     if (NS_FAILED(mStatus)) {
-        NotifyListener();
+        if (mIsAsync) {
+            NotifyListener();
+        }
         return;
     }
     
     // EvaluateScript() succeeded, and we were not canceled, that
     // means there's data to parse as a result of evaluating the
     // script.
 
     // Get the stream channels load flags (!= mLoadFlags).
@@ -724,27 +758,29 @@ nsJSChannel::EvaluateScript()
         }
 
         if (NS_SUCCEEDED(mStatus)) {
             mStatus = StopAll();
         }
     }
 
     if (NS_FAILED(mStatus)) {
-        NotifyListener();
+        if (mIsAsync) {
+            NotifyListener();
+        }
         return;
     }
     
     mStatus = mStreamChannel->AsyncOpen(this, mContext);
-    if (NS_FAILED(mStatus)) {
-        NotifyListener();
-    } else {
+    if (NS_SUCCEEDED(mStatus)) {
         // mStreamChannel will call OnStartRequest and OnStopRequest on
         // us, so we'll be sure to call them on our listener.
         mOpenedStreamChannel = PR_TRUE;
+    } else if (mIsAsync) {
+        NotifyListener();
     }
 
     return;
 }
 
 void
 nsJSChannel::NotifyListener()
 {
@@ -915,16 +951,35 @@ nsJSChannel::SetExecutionPolicy(PRUint32
 
 NS_IMETHODIMP
 nsJSChannel::GetExecutionPolicy(PRUint32* aPolicy)
 {
     *aPolicy = mExecutionPolicy;
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsJSChannel::SetExecuteAsync(PRBool aIsAsync)
+{
+    if (!mIsActive) {
+        mIsAsync = aIsAsync;
+    }
+    // else ignore this call
+    NS_WARN_IF_FALSE(!mIsActive, "Calling SetExecuteAsync on active channel?");
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsJSChannel::GetExecuteAsync(PRBool* aIsAsync)
+{
+    *aIsAsync = mIsAsync;
+    return NS_OK;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 nsJSProtocolHandler::nsJSProtocolHandler()
 {
 }
 
 nsresult
 nsJSProtocolHandler::Init()
--- a/modules/plugin/base/src/nsPluginHostImpl.cpp
+++ b/modules/plugin/base/src/nsPluginHostImpl.cpp
@@ -5804,16 +5804,18 @@ NS_IMETHODIMP nsPluginHostImpl::NewPlugi
         // Set the owner of channel to the document principal...
         channel->SetOwner(doc->NodePrincipal());
 
         // And if it's a script allow it to execute against the
         // document's script context.
         nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(channel));
         if (scriptChannel) {
           scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
+          // Plug-ins seem to depend on javascript: URIs running synchronously
+          scriptChannel->SetExecuteAsync(PR_FALSE);
         }
       }
 
       // deal with headers and post data
       nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
       if(httpChannel) {
         if (aPostData) {