Bug 1480095 - Allow loading custom error pages via nsILoadURIDelegate r=esawin,bz,jchen
authorJames Willcox <snorp@snorp.net>
Fri, 03 Aug 2018 14:48:31 -0500
changeset 487930 6cd69a56e853b2f2f3ef42e1ef406030cfbc9c1f
parent 487929 971943621f6e52daa0e94e48c3d846567978ff26
child 487931 d49371d4563a373e0c701f385c59699f2f19dede
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersesawin, bz, jchen
bugs1480095
milestone63.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 1480095 - Allow loading custom error pages via nsILoadURIDelegate r=esawin,bz,jchen MozReview-Commit-ID: IhVC9nU60fy
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
xpcom/base/nsILoadURIDelegate.idl
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4277,17 +4277,17 @@ nsDocShell::LoadURIWithOptions(const cha
 
 NS_IMETHODIMP
 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
                              const char16_t* aURL,
                              nsIChannel* aFailedChannel,
                              bool* aDisplayedErrorPage)
 {
   *aDisplayedErrorPage = false;
-  // Get prompt and string bundle servcies
+  // Get prompt and string bundle services
   nsCOMPtr<nsIPrompt> prompter;
   nsCOMPtr<nsIStringBundle> stringBundle;
   GetPromptAndStringBundle(getter_AddRefs(prompter),
                            getter_AddRefs(stringBundle));
 
   NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
   NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
 
@@ -4298,29 +4298,33 @@ nsDocShell::DisplayLoadError(nsresult aE
   nsAutoString formatStrs[kMaxFormatStrArgs];
   uint32_t formatStrCount = 0;
   bool addHostPort = false;
   nsresult rv = NS_OK;
   nsAutoString messageStr;
   nsAutoCString cssClass;
   nsAutoCString errorPage;
 
-  errorPage.AssignLiteral("neterror");
-
   if (mLoadURIDelegate) {
-    bool loadErrorHandled = false;
+    nsCOMPtr<nsIURI> errorPageURI;
     rv = mLoadURIDelegate->HandleLoadError(aURI, aError,
                                            NS_ERROR_GET_MODULE(aError),
-                                           &loadErrorHandled);
-    if (NS_SUCCEEDED(rv) && loadErrorHandled) {
-      // The request has been handled, nothing to do here.
+                                           getter_AddRefs(errorPageURI));
+    if (NS_FAILED(rv)) {
       *aDisplayedErrorPage = false;
       return NS_OK;
     }
-  }
+
+    if (errorPageURI) {
+      *aDisplayedErrorPage = NS_SUCCEEDED(LoadErrorPage(errorPageURI, aURI, aFailedChannel));
+      return NS_OK;
+    }
+  }
+
+  errorPage.AssignLiteral("neterror");
 
   // Turn the error code into a human readable error message.
   if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
     NS_ENSURE_ARG_POINTER(aURI);
 
     // Extract the schemes into a comma delimited list.
     nsAutoCString scheme;
     aURI->GetScheme(scheme);
@@ -4752,26 +4756,16 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, 
 
     MOZ_LOG(gDocShellLog, LogLevel::Debug,
            ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this,
             aURI ? aURI->GetSpecOrDefault().get() : "",
             NS_ConvertUTF16toUTF8(aURL).get(),
             chanName.get()));
   }
 #endif
-  mFailedChannel = aFailedChannel;
-  mFailedURI = aURI;
-  mFailedLoadType = mLoadType;
-
-  if (mLSHE) {
-    // Abandon mLSHE's BFCache entry and create a new one.  This way, if
-    // we go back or forward to another SHEntry with the same doc
-    // identifier, the error page won't persist.
-    mLSHE->AbandonBFCacheEntry();
-  }
 
   nsAutoCString url;
   if (aURI) {
     nsresult rv = aURI->GetSpec(url);
     NS_ENSURE_SUCCESS(rv, rv);
   } else if (aURL) {
     CopyUTF16toUTF8(MakeStringSpan(aURL), url);
   } else {
@@ -4828,17 +4822,34 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, 
   // end of the URL, so append it last.
   errorPageUrl.AppendLiteral("&d=");
   errorPageUrl.AppendASCII(escapedDescription.get());
 
   nsCOMPtr<nsIURI> errorPageURI;
   nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return InternalLoad(errorPageURI, nullptr, Nothing(), false, false, nullptr, RP_Unset,
+  return LoadErrorPage(errorPageURI, aURI, aFailedChannel);
+}
+
+nsresult
+nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI, nsIChannel* aFailedChannel)
+{
+  mFailedChannel = aFailedChannel;
+  mFailedURI = aFailedURI;
+  mFailedLoadType = mLoadType;
+
+  if (mLSHE) {
+    // Abandon mLSHE's BFCache entry and create a new one.  This way, if
+    // we go back or forward to another SHEntry with the same doc
+    // identifier, the error page won't persist.
+    mLSHE->AbandonBFCacheEntry();
+  }
+
+  return InternalLoad(aErrorURI, nullptr, Nothing(), false, false, nullptr, RP_Unset,
                       nsContentUtils::GetSystemPrincipal(), nullptr,
                       INTERNAL_LOAD_FLAGS_NONE, EmptyString(),
                       nullptr, VoidString(), nullptr, nullptr,
                       LOAD_ERROR_PAGE, nullptr, true, VoidString(), this,
                       nullptr, nullptr, nullptr);
 }
 
 NS_IMETHODIMP
@@ -9521,17 +9532,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
                                     : nullptr;
 
   const bool isDocumentAuxSandboxed = doc &&
     (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
 
   const bool checkLoadDelegates = !(aFlags & INTERNAL_LOAD_FLAGS_DELEGATES_CHECKED);
   aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_DELEGATES_CHECKED;
 
-  if (aURI && mLoadURIDelegate && checkLoadDelegates &&
+  if (aURI && mLoadURIDelegate && checkLoadDelegates && aLoadType != LOAD_ERROR_PAGE &&
       (!targetDocShell || targetDocShell == static_cast<nsIDocShell*>(this))) {
     // Dispatch only load requests for the current or a new window to the
     // delegate, e.g., to allow for GeckoView apps to handle the load event
     // outside of Gecko.
     const int where = (aWindowTarget.IsEmpty() || targetDocShell)
                       ? nsIBrowserDOMWindow::OPEN_CURRENTWINDOW
                       : nsIBrowserDOMWindow::OPEN_NEWWINDOW;
 
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -585,22 +585,28 @@ private: // member functions
 
   // Helper method that is called when a new document (including any
   // sub-documents - ie. frames) has been completely loaded.
   nsresult EndPageLoad(nsIWebProgress* aProgress,
                        nsIChannel* aChannel,
                        nsresult aResult);
 
 
+  // Builds an error page URI (e.g. about:neterror?etc) for the given aURI
+  // and displays it via the LoadErrorPage() overload below.
   nsresult LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
-                           const char* aErrorPage,
-                           const char* aErrorType,
-                           const char16_t* aDescription,
-                           const char* aCSSClass,
-                           nsIChannel* aFailedChannel);
+                         const char* aErrorPage,
+                         const char* aErrorType,
+                         const char16_t* aDescription,
+                         const char* aCSSClass,
+                         nsIChannel* aFailedChannel);
+
+  // This method directly loads aErrorURI as an error page. aFailedURI and aFailedChannel
+  // come from DisplayLoadError() or the LoadErrorPage() overload above.
+  nsresult LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI, nsIChannel* aFailedChannel);
 
   bool DisplayLoadError(nsresult aError, nsIURI* aURI, const char16_t* aURL,
                         nsIChannel* aFailedChannel)
   {
     bool didDisplayLoadError = false;
     DisplayLoadError(aError, aURI, aURL, aFailedChannel, &didDisplayLoadError);
     return didDisplayLoadError;
   }
--- a/xpcom/base/nsILoadURIDelegate.idl
+++ b/xpcom/base/nsILoadURIDelegate.idl
@@ -36,14 +36,14 @@ interface nsILoadURIDelegate : nsISuppor
 
   /**
    * Delegates page load error handling.
    *
    * @param aURI The URI that failed to load.
    * @param aError The error code.
    * @param aErrorModule The error module code.
 
-   * Returns whether the page load error has been successfully handled.
+   * Returns an error page URL to load, or null to show the default error page.
+   * No error page is shown at all if an error is thrown.
    */
-  boolean
+  nsIURI
   handleLoadError(in nsIURI aURI, in nsresult aError, in short aErrorModule);
-
 };