Bug 1619796 - Don't fire onLoadRequest for embedder-initiated loads. r=smaug,droeh
authorAgi Sferro <agi@sferro.dev>
Fri, 13 Mar 2020 18:22:16 +0000
changeset 518673 c9cec62a163b782387fa6058a2a300f461bea89d
parent 518672 ef28db75f1422da15d4176a100f515406862c7cf
child 518674 480130371a87aac7d1d274e50daaa9bddb93f3ab
push id37213
push usershindli@mozilla.com
push dateFri, 13 Mar 2020 21:46:16 +0000
treeherdermozilla-central@8ef0a54d7715 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, droeh
bugs1619796
milestone76.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1619796 - Don't fire onLoadRequest for embedder-initiated loads. r=smaug,droeh Differential Revision: https://phabricator.services.mozilla.com/D66695
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsDocShellLoadState.cpp
docshell/base/nsIWebNavigation.idl
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
mobile/android/modules/geckoview/GeckoViewNavigation.jsm
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8188,16 +8188,20 @@ nsresult nsDocShell::MaybeHandleLoadDele
                                              bool* aDidHandleLoad) {
   MOZ_ASSERT(aLoadState);
   MOZ_ASSERT(aDidHandleLoad);
   MOZ_ASSERT(aWindowType == nsIBrowserDOMWindow::OPEN_NEWWINDOW ||
              aWindowType == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW);
 
   *aDidHandleLoad = false;
 
+  if (aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE)) {
+    return NS_OK;
+  }
+
   nsCOMPtr<nsILoadURIDelegate> loadURIDelegate = GetLoadURIDelegate();
   // If we don't have a delegate or we're trying to load the error page, we
   // shouldn't be trying to do sandbox loads.
   if (!loadURIDelegate || aLoadState->LoadType() == LOAD_ERROR_PAGE) {
     return NS_OK;
   }
 
   // Dispatch only load requests for the current or a new window to the
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -146,16 +146,19 @@ class nsDocShell final : public nsDocLoa
 
     INTERNAL_LOAD_FLAGS_NO_OPENER = 0x100,
 
     // Whether a top-level data URI navigation is allowed for that load
     INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI = 0x200,
 
     // Whether the load was triggered by user interaction.
     INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED = 0x1000,
+
+    // Whether the load should go through LoadURIDelegate.
+    INTERNAL_LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE = 0x2000,
   };
 
   // Event type dispatched by RestorePresentation
   class RestorePresentationEvent : public mozilla::Runnable {
    public:
     NS_DECL_NSIRUNNABLE
     explicit RestorePresentationEvent(nsDocShell* aDs)
         : mozilla::Runnable("nsDocShell::RestorePresentationEvent"),
--- a/docshell/base/nsDocShellLoadState.cpp
+++ b/docshell/base/nsDocShellLoadState.cpp
@@ -607,16 +607,20 @@ void nsDocShellLoadState::CalculateLoadU
   if (oldLoadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_CLASSIFIER) {
     mLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
   }
 
   if (oldLoadFlags & nsIWebNavigation::LOAD_FLAGS_FORCE_ALLOW_COOKIES) {
     mLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
   }
 
+  if (oldLoadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE) {
+    mLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE;
+  }
+
   if (!mSrcdocData.IsVoid()) {
     mLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_IS_SRCDOC;
   }
 
   if (mForceAllowDataURI) {
     mLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
   }
 
--- a/docshell/base/nsIWebNavigation.idl
+++ b/docshell/base/nsIWebNavigation.idl
@@ -228,16 +228,21 @@ interface nsIWebNavigation : nsISupports
 
   /**
    * These flags force TRR modes 1 or 3 for the load.
    */
   const unsigned long LOAD_FLAGS_DISABLE_TRR = 0x1000000;
   const unsigned long LOAD_FLAGS_FORCE_TRR = 0x2000000;
 
   /**
+   * This load should bypass the LoadURIDelegate.loadUri.
+   */
+  const unsigned long LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE = 0x4000000;
+
+  /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing this interface.  If it can't be loaded here
    * however, the URI dispatcher will go through its normal process of content
    * loading.
    *
    * @param aURI
    *        The URI string to load.  For HTTP and FTP URLs and possibly others,
    *        characters above U+007F will be converted to UTF-8 and then URL-
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -1623,28 +1623,70 @@ public class GeckoSession implements Par
      * @param uri the URI to load
      * @param referrer the referring GeckoSession, may be null
      * @param flags the load flags to use, an OR-ed value of {@link #LOAD_FLAGS_NONE LOAD_FLAGS_*}
      * @param additionalHeaders any additional request headers used with the load
      */
     @AnyThread
     public void loadUri(final @NonNull String uri, final @Nullable GeckoSession referrer,
                         final @LoadFlags int flags, final @Nullable Map<String, String> additionalHeaders) {
-        final GeckoBundle msg = new GeckoBundle();
-        msg.putString("uri", uri);
-        msg.putInt("flags", flags);
-
-        if (referrer != null) {
-            msg.putString("referrerSessionId", referrer.mId);
-        }
-
-        if (additionalHeaders != null) {
-            msg.putStringArray("headers", additionalHeadersToStringArray(additionalHeaders));
-        }
-        mEventDispatcher.dispatch("GeckoView:LoadUri", msg);
+        // For performance reasons we short-circuit the delegate here
+        // instead of making Gecko call it for direct loadUri calls.
+        final NavigationDelegate.LoadRequest request =
+                new NavigationDelegate.LoadRequest(
+                        uri,
+                        null,
+                        1, // OPEN_CURRENTWINDOW
+                        0, // No flags
+                        false);
+
+        shouldLoadUri(request).accept(allowOrDeny -> {
+            if (allowOrDeny == AllowOrDeny.DENY) {
+                return;
+            }
+
+            final GeckoBundle msg = new GeckoBundle();
+            msg.putString("uri", uri);
+            msg.putInt("flags", flags);
+
+            if (referrer != null) {
+                msg.putString("referrerSessionId", referrer.mId);
+            }
+
+            if (additionalHeaders != null) {
+                msg.putStringArray("headers", additionalHeadersToStringArray(additionalHeaders));
+            }
+
+            mEventDispatcher.dispatch("GeckoView:LoadUri", msg);
+        });
+    }
+
+    private GeckoResult<AllowOrDeny> shouldLoadUri(final NavigationDelegate.LoadRequest request) {
+        final NavigationDelegate delegate = mNavigationHandler.getDelegate();
+        if (delegate == null) {
+            return GeckoResult.fromValue(AllowOrDeny.ALLOW);
+        }
+
+        final GeckoResult<AllowOrDeny> result = new GeckoResult<>();
+
+        ThreadUtils.getUiHandler().post(() -> {
+            final GeckoResult<AllowOrDeny> delegateResult =
+                    delegate.onLoadRequest(this, request);
+
+            if (delegateResult == null) {
+                result.complete(AllowOrDeny.ALLOW);
+            } else {
+                delegateResult.accept(
+                    allowOrDeny -> result.complete(allowOrDeny),
+                    error -> result.completeExceptionally(error)
+                );
+            }
+        });
+
+        return result;
     }
 
     /**
      * Load the given URI.
      * @param uri The URI of the resource to load.
      */
     @AnyThread
     public void loadUri(final @NonNull Uri uri) {
--- a/mobile/android/modules/geckoview/GeckoViewNavigation.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewNavigation.jsm
@@ -34,20 +34,20 @@ const createReferrerInfo = aReferrer => 
   try {
     referrerUri = Services.io.newURI(aReferrer);
   } catch (ignored) {}
 
   return new ReferrerInfo(Ci.nsIReferrerInfo.EMPTY, true, referrerUri);
 };
 
 function convertFlags(aFlags) {
+  let navFlags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE;
   if (!aFlags) {
-    return Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+    return navFlags;
   }
-  let navFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
   // These need to match the values in GeckoSession.LOAD_FLAGS_*
   if (aFlags & (1 << 0)) {
     navFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
   }
   if (aFlags & (1 << 1)) {
     navFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY;
   }
   if (aFlags & (1 << 2)) {