Bug 1209591 - allow loadURI consumers to expose whether an error page was immediately loaded as result of an error, r=smaug,mak
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Thu, 19 Nov 2015 11:06:10 -0800
changeset 305569 5dc9c9025ad7f553472a66e1928975dfa7f6f824
parent 305568 97c16a9b54de2f0d85d51d8a424d3f2932c77f71
child 305570 0714d159a05cde77e7e88c5f57f4dc49173453ab
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, mak
bugs1209591
milestone44.0a2
Bug 1209591 - allow loadURI consumers to expose whether an error page was immediately loaded as result of an error, r=smaug,mak
browser/base/content/urlbarBindings.xml
browser/base/content/utilityOverlay.js
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
docshell/base/nsIWebNavigation.idl
js/xpconnect/src/xpc.msg
xpcom/base/ErrorList.h
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -392,25 +392,28 @@ file, You can obtain one at http://mozil
               // but don't let that interfere with the loading of the url.
               Cu.reportError(ex);
             }
 
             let loadCurrent = () => {
               try {
                 openUILinkIn(url, "current", {
                   allowThirdPartyFixup: true,
+                  indicateErrorPageLoad: true,
                   disallowInheritPrincipal: !mayInheritPrincipal,
                   allowPinnedTabHostChange: true,
                   postData: postData,
                   allowPopups: url.startsWith("javascript:"),
                 });
               } catch (ex) {
                 // This load can throw an exception in certain cases, which means
                 // we'll want to replace the URL with the loaded URL:
-                this.handleRevert();
+                if (ex.result != Cr.NS_ERROR_LOAD_SHOWED_ERRORPAGE) {
+                  this.handleRevert();
+                }
               }
 
               // Ensure the start of the URL is visible for UX reasons:
               this.selectionStart = this.selectionEnd = 0;
             };
 
             // Focus the content area before triggering loads, since if the load
             // occurs in a new tab, we want focus to be restored to the content
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -214,16 +214,17 @@ function openLinkIn(url, where, params) 
   var aDisallowInheritPrincipal = params.disallowInheritPrincipal;
   var aInitiatingDoc        = params.initiatingDoc;
   var aIsPrivate            = params.private;
   var aSkipTabAnimation     = params.skipTabAnimation;
   var aAllowPinnedTabHostChange = !!params.allowPinnedTabHostChange;
   var aNoReferrer           = params.noReferrer;
   var aAllowPopups          = !!params.allowPopups;
   var aUserContextId        = params.userContextId;
+  var aIndicateErrorPageLoad = params.indicateErrorPageLoad;
 
   if (where == "save") {
     if (!aInitiatingDoc) {
       Components.utils.reportError("openUILink/openLinkIn was called with " +
         "where == 'save' but without initiatingDoc.  See bug 814264.");
       return;
     }
     // TODO(1073187): propagate referrerPolicy.
@@ -333,16 +334,19 @@ function openLinkIn(url, where, params) 
     // (see stripUnsafeProtocolOnPaste).
     if (aDisallowInheritPrincipal && !(uriObj && uriObj.schemeIs("javascript"))) {
       flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
     }
 
     if (aAllowPopups) {
       flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_POPUPS;
     }
+    if (aIndicateErrorPageLoad) {
+      flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ERROR_LOAD_CHANGES_RV;
+    }
 
     w.gBrowser.loadURIWithFlags(url, {
       flags: flags,
       referrerURI: aNoReferrer ? null : aReferrerURI,
       referrerPolicy: aReferrerPolicy,
       postData: aPostData,
     });
     break;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4651,17 +4651,20 @@ nsDocShell::LoadURIWithOptions(const cha
         serv->NotifyObservers(fixupInfo, "keyword-uri-fixup", aURI);
       }
     }
   }
   // else no fixup service so just use the URI we created and see
   // what happens
 
   if (NS_ERROR_MALFORMED_URI == rv) {
-    DisplayLoadError(rv, uri, aURI, nullptr);
+    if (DisplayLoadError(rv, uri, aURI, nullptr) &&
+        (aLoadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
+      return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
+    }
   }
 
   if (NS_FAILED(rv) || !uri) {
     return NS_ERROR_FAILURE;
   }
 
   PopupControlState popupState;
   if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) {
@@ -4716,18 +4719,20 @@ nsDocShell::LoadURIWithOptions(const cha
   mOriginalUriString = uriString;
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
                              const char16_t* aURL,
-                             nsIChannel* aFailedChannel)
-{
+                             nsIChannel* aFailedChannel,
+                             bool* aDisplayedErrorPage)
+{
+  *aDisplayedErrorPage = false;
   // Get prompt and string bundle servcies
   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);
@@ -5095,18 +5100,20 @@ nsDocShell::DisplayLoadError(nsresult aE
     if (NS_SUCCEEDED(rv) && isSecureURI) {
       // Maybe TLS intolerant. Treat this as an SSL error.
       error.AssignLiteral("nssFailure2");
     }
   }
 
   if (UseErrorPages()) {
     // Display an error page
-    LoadErrorPage(aURI, aURL, errorPage.get(), error.get(),
-                  messageStr.get(), cssClass.get(), aFailedChannel);
+    nsresult loadedPage = LoadErrorPage(aURI, aURL, errorPage.get(),
+                                        error.get(), messageStr.get(),
+                                        cssClass.get(), aFailedChannel);
+    *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
   } else {
     // The prompter reqires that our private window has a document (or it
     // asserts). Satisfy that assertion now since GetDoc will force
     // creation of one if it hasn't already been created.
     if (mScriptGlobal) {
       unused << mScriptGlobal->GetDoc();
     }
 
@@ -10271,17 +10278,20 @@ nsDocShell::InternalLoad(nsIURI* aURI,
                  (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0,
                  srcdoc, aBaseURI, contentType);
   if (req && aRequest) {
     NS_ADDREF(*aRequest = req);
   }
 
   if (NS_FAILED(rv)) {
     nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
-    DisplayLoadError(rv, aURI, nullptr, chan);
+    if (DisplayLoadError(rv, aURI, nullptr, chan) &&
+        (aFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
+      return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
+    }
   }
 
   return rv;
 }
 
 nsIPrincipal*
 nsDocShell::GetInheritedPrincipal(bool aConsiderCurrentDocument)
 {
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -723,16 +723,24 @@ protected:
   void ClearFrameHistory(nsISHEntry* aEntry);
 
   /**
    * Initializes mTiming if it isn't yet.
    * After calling this, mTiming is non-null.
    */
   void MaybeInitTiming();
 
+  bool DisplayLoadError(nsresult aError, nsIURI* aURI, const char16_t* aURL,
+                        nsIChannel* aFailedChannel)
+  {
+    bool didDisplayLoadError = false;
+    DisplayLoadError(aError, aURI, aURL, aFailedChannel, &didDisplayLoadError);
+    return didDisplayLoadError;
+  }
+
 public:
   // Event type dispatched by RestorePresentation
   class RestorePresentationEvent : public nsRunnable
   {
   public:
     NS_DECL_NSIRUNNABLE
     explicit RestorePresentationEvent(nsDocShell* aDs) : mDocShell(aDs) {}
     void Revoke() { mDocShell = nullptr; }
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -41,17 +41,17 @@ interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
 
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(539bce70-8261-462f-bbd0-40ee90b7d636)]
+[scriptable, builtinclass, uuid(44aca825-0080-49f1-8407-df62183e5ec1)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * 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 URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -458,21 +458,24 @@ interface nsIDocShell : nsIDocShellTreeI
    * Display a load error in a frame while keeping that frame's currentURI
    * pointing correctly to the page where the error ocurred, rather than to
    * the error document page. You must provide either the aURI or aURL parameter.
    *
    * @param  aError         The error code to be displayed
    * @param  aURI           nsIURI of the page where the error happened
    * @param  aURL           wstring of the page where the error happened
    * @param  aFailedChannel The channel related to this error
+   *
+   * Returns whether or not we displayed an error page (note: will always
+   * return false if in-content error pages are disabled!)
    */
-  void displayLoadError(in nsresult aError,
-                        in nsIURI aURI,
-                        in wstring aURL,
-                        [optional] in nsIChannel aFailedChannel);
+  boolean displayLoadError(in nsresult aError,
+                           in nsIURI aURI,
+                           in wstring aURL,
+                           [optional] in nsIChannel aFailedChannel);
 
   /**
    * The channel that failed to load and resulted in an error page.
    * May be null. Relevant only to error pages.
    */
   readonly attribute nsIChannel failedChannel;
 
   /**
--- a/docshell/base/nsIWebNavigation.idl
+++ b/docshell/base/nsIWebNavigation.idl
@@ -11,17 +11,17 @@ interface nsISHistory;
 interface nsIURI;
 
 /**
  * The nsIWebNavigation interface defines an interface for navigating the web.
  * It provides methods and attributes to direct an object to navigate to a new
  * location, stop or restart an in process load, or determine where the object
  * has previously gone.
  */
-[scriptable, uuid(0e92d522-53a5-4af6-9a24-4eccdcbf4f91)]
+[scriptable, uuid(3ade79d4-8cb9-4952-b18d-4f9b63ca0d31)]
 interface nsIWebNavigation : nsISupports
 {
   /**
    * Indicates if the object can go back.  If true this indicates that
    * there is back session history available for navigation.
    */
   readonly attribute boolean canGoBack;
 
@@ -180,29 +180,33 @@ interface nsIWebNavigation : nsISupports
   const unsigned long LOAD_FLAGS_FORCE_ALLOW_COOKIES = 0x20000;
 
   /**
    * Prevent the owner principal from being inherited for this load.
    */
   const unsigned long LOAD_FLAGS_DISALLOW_INHERIT_OWNER = 0x40000;
 
   /**
+   * Overwrite the returned error code with a specific result code
+   * when an error page is displayed.
+   */
+  const unsigned long LOAD_FLAGS_ERROR_LOAD_CHANGES_RV = 0x80000;
+
+  /**
    * This flag specifies that the URI may be submitted to a third-party
    * server for correction. This should only be applied to non-sensitive
    * URIs entered by users.  This flag must not be passed to Reload.
    */
   const unsigned long LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP = 0x100000;
 
   /**
    * This flag specifies that common scheme typos should be corrected.
    */
   const unsigned long LOAD_FLAGS_FIXUP_SCHEME_TYPOS = 0x200000;
 
-  /* Note that flag 0x80000 is available. */
-
   /**
    * 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,
--- a/js/xpconnect/src/xpc.msg
+++ b/js/xpconnect/src/xpc.msg
@@ -151,16 +151,17 @@ XPC_MSG_DEF(NS_ERROR_PORT_ACCESS_NOT_ALL
 XPC_MSG_DEF(NS_ERROR_NET_RESET                      , "The connection was established, but no data was ever received")
 XPC_MSG_DEF(NS_ERROR_NET_INTERRUPT                  , "The connection was established, but the data transfer was interrupted")
 XPC_MSG_DEF(NS_ERROR_NET_PARTIAL_TRANSFER           , "A transfer was only partially done when it completed")
 XPC_MSG_DEF(NS_ERROR_NOT_RESUMABLE                  , "This request is not resumable, but it was tried to resume it, or to request resume-specific data")
 XPC_MSG_DEF(NS_ERROR_ENTITY_CHANGED                 , "It was attempted to resume the request, but the entity has changed in the meantime")
 XPC_MSG_DEF(NS_ERROR_REDIRECT_LOOP                  , "The request failed as a result of a detected redirection loop")
 XPC_MSG_DEF(NS_ERROR_UNSAFE_CONTENT_TYPE            , "The request failed because the content type returned by the server was not a type expected by the channel")
 XPC_MSG_DEF(NS_ERROR_REMOTE_XUL                     , "Attempt to access remote XUL document that is not in website's whitelist")
+XPC_MSG_DEF(NS_ERROR_LOAD_SHOWED_ERRORPAGE          , "The load caused an error page to be displayed.")
 
 XPC_MSG_DEF(NS_ERROR_FTP_LOGIN                      , "FTP error while logging in")
 XPC_MSG_DEF(NS_ERROR_FTP_CWD                        , "FTP error while changing directory")
 XPC_MSG_DEF(NS_ERROR_FTP_PASV                       , "FTP error while changing to passive mode")
 XPC_MSG_DEF(NS_ERROR_FTP_PWD                        , "FTP error while retrieving current directory")
 XPC_MSG_DEF(NS_ERROR_FTP_LIST                       , "FTP error while retrieving a directory listing")
 XPC_MSG_DEF(NS_ERROR_UNKNOWN_HOST                   , "The lookup of the hostname failed")
 XPC_MSG_DEF(NS_ERROR_DNS_LOOKUP_QUEUE_FULL          , "The DNS lookup queue is full")
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -226,16 +226,18 @@
   ERROR(NS_ERROR_ENTITY_CHANGED,       FAILURE(32)),
   /* The request failed because the content type returned by the server was not
    * a type expected by the channel (for nested channels such as the JAR
    * channel). */
   ERROR(NS_ERROR_UNSAFE_CONTENT_TYPE,  FAILURE(74)),
   /* The request failed because the user tried to access to a remote XUL
    * document from a website that is not in its white-list. */
   ERROR(NS_ERROR_REMOTE_XUL,           FAILURE(75)),
+  /* The request resulted in an error page being displayed. */
+  ERROR(NS_ERROR_LOAD_SHOWED_ERRORPAGE, FAILURE(77)),
 
 
   /* FTP specific error codes: */
 
   ERROR(NS_ERROR_FTP_LOGIN,  FAILURE(21)),
   ERROR(NS_ERROR_FTP_CWD,    FAILURE(22)),
   ERROR(NS_ERROR_FTP_PASV,   FAILURE(23)),
   ERROR(NS_ERROR_FTP_PWD,    FAILURE(24)),