Bug 1478171 - [1.0] Forward channel redirect to nsILoadURIDelegate to allow external handling. r=smaug,snorp a=ritu GECKOVIEW_62_RELBRANCH
☠☠ backed out by 4cdeecd31350 ☠ ☠
authorDylan Roeh <droeh@mozilla.com>
Tue, 21 Aug 2018 12:52:39 -0500
branchGECKOVIEW_62_RELBRANCH
changeset 481112 99aa10a241f6
parent 481111 427eacc1a0bf
child 481113 31683bdf39d7
push id1772
push usernchen@mozilla.com
push dateFri, 31 Aug 2018 22:52:36 +0000
treeherdermozilla-release@31683bdf39d7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, snorp, ritu
bugs1478171
milestone62.0
Bug 1478171 - [1.0] Forward channel redirect to nsILoadURIDelegate to allow external handling. r=smaug,snorp a=ritu
netwerk/base/nsIChannelEventSink.idl
uriloader/base/nsDocLoader.cpp
--- a/netwerk/base/nsIChannelEventSink.idl
+++ b/netwerk/base/nsIChannelEventSink.idl
@@ -48,16 +48,23 @@ interface nsIChannelEventSink : nsISuppo
     /**
      * This is a special-cased redirect coming from hitting HSTS upgrade
      * redirect from http to https only.  In some cases this type of redirect
      * may be considered as safe despite not being the-same-origin redirect.
      */
     const unsigned long REDIRECT_STS_UPGRADE = 1 << 3;
 
     /**
+     * This redirect has already been presented to the nsILoadURIDelegate
+     * for possible handling; if this flag is set we may safely skip checking
+     * if the nsILoadURIDelegate will handle the redirect.
+     */
+    const unsigned long REDIRECT_DELEGATES_CHECKED = 1 << 4;
+
+    /**
      * Called when a redirect occurs. This may happen due to an HTTP 3xx status
      * code. The purpose of this method is to notify the sink that a redirect
      * is about to happen, but also to give the sink the right to veto the
      * redirect by throwing or passing a failure-code in the callback.
      *
      * Note that vetoing the redirect simply means that |newChannel| will not
      * be opened. It is important to understand that |oldChannel| will continue
      * loading as if it received a HTTP 200, which includes notifying observers
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nspr.h"
 #include "mozilla/Logging.h"
 #include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
 
 #include "nsDocLoader.h"
 #include "nsCURILoader.h"
 #include "nsNetUtil.h"
 #include "nsIHttpChannel.h"
 #include "nsIWebProgressListener2.h"
 
 #include "nsIServiceManager.h"
@@ -29,16 +31,18 @@
 #include "nsIScriptSecurityManager.h"
 
 #include "nsITransport.h"
 #include "nsISocketTransport.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsPresContext.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsILoadURIDelegate.h"
+#include "nsIBrowserDOMWindow.h"
 
 using mozilla::DebugOnly;
 using mozilla::LogLevel;
 
 //
 // Log module for nsIDocumentLoader logging...
 //
 // To enable logging (see mozilla/Logging.h for full details):
@@ -1415,21 +1419,116 @@ int64_t nsDocLoader::CalculateMaxProgres
     if (info->mMaxProgress < info->mCurrentProgress) {
       return int64_t(-1);
     }
     max += info->mMaxProgress;
   }
   return max;
 }
 
+class LoadURIDelegateRedirectHandler final : public mozilla::dom::PromiseNativeHandler
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(LoadURIDelegateRedirectHandler)
+
+  LoadURIDelegateRedirectHandler(nsDocLoader* aDocLoader,
+                                 nsIChannel* aOldChannel,
+                                 nsIChannel* aNewChannel,
+                                 uint32_t aFlags,
+                                 nsIAsyncVerifyRedirectCallback* aCallback)
+  : mDocLoader(aDocLoader)
+  , mOldChannel(aOldChannel)
+  , mNewChannel(aNewChannel)
+  , mFlags(aFlags)
+  , mCallback(aCallback)
+  {}
+
+  void
+  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    if (aValue.isBoolean() && aValue.toBoolean()) {
+      // The app handled the redirect, notify the callback
+      mCallback->OnRedirectVerifyCallback(NS_ERROR_ABORT);
+    } else {
+      UnhandledCallback();
+    }
+  }
+
+  void
+  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    UnhandledCallback();
+  }
+
+private:
+  ~LoadURIDelegateRedirectHandler()
+  {}
+
+  void UnhandledCallback()
+  {
+    // If the redirect wasn't handled by the nsILoadURIDelegate, let Gecko
+    // handle it.
+    mFlags |= nsIChannelEventSink::REDIRECT_DELEGATES_CHECKED;
+    mDocLoader->AsyncOnChannelRedirect(mOldChannel, mNewChannel, mFlags,
+                                       mCallback);
+  }
+
+  RefPtr<nsDocLoader> mDocLoader;
+  nsCOMPtr<nsIChannel> mOldChannel;
+  nsCOMPtr<nsIChannel> mNewChannel;
+  uint32_t mFlags;
+  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mCallback;
+};
+
+NS_IMPL_CYCLE_COLLECTION(LoadURIDelegateRedirectHandler, mDocLoader, 
+                         mOldChannel, mNewChannel, mCallback)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LoadURIDelegateRedirectHandler)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(LoadURIDelegateRedirectHandler)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(LoadURIDelegateRedirectHandler)
+
 NS_IMETHODIMP nsDocLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
                                                   nsIChannel *aNewChannel,
                                                   uint32_t aFlags,
                                                   nsIAsyncVerifyRedirectCallback *cb)
 {
+  if ((aFlags &
+      (nsIChannelEventSink::REDIRECT_TEMPORARY |
+       nsIChannelEventSink::REDIRECT_PERMANENT)) &&
+      !(aFlags & nsIChannelEventSink::REDIRECT_DELEGATES_CHECKED)) {
+    nsCOMPtr<nsIDocShell> docShell =
+      do_QueryInterface(static_cast<nsIRequestObserver*>(this));
+
+    nsCOMPtr<nsILoadURIDelegate> delegate;
+    docShell->GetLoadURIDelegate(getter_AddRefs(delegate));
+
+    nsCOMPtr<nsIURI> newURI;
+    aNewChannel->GetURI(getter_AddRefs(newURI));
+
+    if (newURI && delegate) {
+      RefPtr<mozilla::dom::Promise> promise;
+      const int where = nsIBrowserDOMWindow::OPEN_CURRENTWINDOW;
+      nsresult rv = delegate->LoadURI(newURI, where, /* flags */ 0,
+                                      /* triggering principal */ nullptr,
+                                      getter_AddRefs(promise));
+      if (NS_SUCCEEDED(rv) && promise) {
+        RefPtr<LoadURIDelegateRedirectHandler> handler =
+          new LoadURIDelegateRedirectHandler(this, aOldChannel, aNewChannel,
+                                             aFlags, cb);
+
+        promise->AppendNativeHandler(handler);
+        return NS_OK;
+      }
+    }
+  }
+
   if (aOldChannel)
   {
     nsLoadFlags loadFlags = 0;
     int32_t stateFlags = nsIWebProgressListener::STATE_REDIRECTING |
                          nsIWebProgressListener::STATE_IS_REQUEST;
 
     aOldChannel->GetLoadFlags(&loadFlags);
     // If the document channel is being redirected, then indicate that the