Backed out changesets b386e97721cf, 386b9c750bd2, 3c86861912bb (bug 1241085) because the about:newtab URI is now kept across sessions
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Thu, 05 May 2016 17:45:58 +0100
changeset 363908 de5ab3fd7c7e5772cc323a6d998a3a94143c23b1
parent 363907 1164b33e991f1115dc7a7262d08a27a6bada7e27
child 363909 92eb1555b47c07af66e17c08cac4ed8bb4e67aa5
child 364223 dea0a728eec67a7c78ac57ce34e83f7d554136fa
push id17331
push userbmo:giles@thaumas.net
push dateThu, 05 May 2016 19:50:43 +0000
bugs1241085
milestone49.0a1
backs outb386e97721cfad7464829c88a1a0b8c42e75315c
386b9c750bd2ed458112acd29eb72e4e1371af9d
3c86861912bb0e2a8b7e09d01fb2243461839a58
Backed out changesets b386e97721cf, 386b9c750bd2, 3c86861912bb (bug 1241085) because the about:newtab URI is now kept across sessions MozReview-Commit-ID: EVv6M6x9F44
browser/base/content/browser.js
browser/base/content/tabbrowser.xml
browser/base/content/test/urlbar/browser.ini
browser/base/content/test/urlbar/browser_urlbarPrivateBrowsingWindowChange.js
browser/components/sessionstore/SessionStore.jsm
browser/components/sessionstore/TabState.jsm
browser/components/sessionstore/test/browser_522545.js
toolkit/content/browser-child.js
toolkit/content/widgets/browser.xml
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -811,16 +811,20 @@ function _loadURIWithFlags(browser, uri,
   }
   let flags = params.flags || 0;
   let referrer = params.referrerURI;
   let referrerPolicy = ('referrerPolicy' in params ? params.referrerPolicy :
                         Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT);
   let charset = params.charset;
   let postData = params.postData;
 
+  if (!(flags & browser.webNavigation.LOAD_FLAGS_FROM_EXTERNAL)) {
+    browser.userTypedClear++;
+  }
+
   let wasRemote = browser.isRemoteBrowser;
 
   let process = browser.isRemoteBrowser ? Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
                                         : Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
   let mustChangeProcess = gMultiProcessBrowser &&
                           !E10SUtils.canLoadURIInProcess(uri, process);
   if ((!wasRemote && !mustChangeProcess) ||
       (wasRemote && mustChangeProcess)) {
@@ -858,16 +862,19 @@ function _loadURIWithFlags(browser, uri,
     } else {
       throw e;
     }
   } finally {
     if ((!wasRemote && !mustChangeProcess) ||
         (wasRemote && mustChangeProcess)) {
       browser.inLoadURI = false;
     }
+    if (browser.userTypedClear) {
+      browser.userTypedClear--;
+    }
   }
 }
 
 // Starts a new load in the browser first switching the browser to the correct
 // process
 function LoadInOtherProcess(browser, loadOptions, historyIndex = -1) {
   let tab = gBrowser.getTabForBrowser(browser);
   SessionStore.navigateAndRestore(tab, loadOptions, historyIndex);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -613,22 +613,16 @@
             onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
               if (!aRequest)
                 return;
 
               var oldBlank = this.mBlank;
 
               const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
               const nsIChannel = Components.interfaces.nsIChannel;
-              let location, originalLocation;
-              try {
-                aRequest.QueryInterface(nsIChannel)
-                location = aRequest.URI;
-                originalLocation = aRequest.originalURI;
-              } catch (ex) {}
 
               if (aStateFlags & nsIWebProgressListener.STATE_START) {
                 this.mRequestCount++;
               }
               else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
                 const NS_ERROR_UNKNOWN_HOST = 2152398878;
                 if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) {
                   // to prevent bug 235825: wait for the request handled
@@ -637,31 +631,27 @@
                 }
                 // since we (try to) only handle STATE_STOP of the last request,
                 // the count of open requests should now be 0
                 this.mRequestCount = 0;
               }
 
               if (aStateFlags & nsIWebProgressListener.STATE_START &&
                   aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
+                // It's okay to clear what the user typed when we start
+                // loading a document. If the user types, this counter gets
+                // set to zero, if the document load ends without an
+                // onLocationChange, this counter gets decremented
+                // (so we keep it while switching tabs after failed loads)
+                // We need to add 2 because loadURIWithFlags may have
+                // cancelled a pending load which would have cleared
+                // its anchor scroll detection temporary increment.
                 if (aWebProgress.isTopLevel) {
-                  // Need to use originalLocation rather than location because things
-                  // like about:home and about:privatebrowsing arrive with nsIRequest
-                  // pointing to their resolved jar: or file: URIs.
-                  if (!(originalLocation && gInitialPages.includes(originalLocation.spec) &&
-                        originalLocation != "about:blank" &&
-                        this.mBrowser.currentURI && this.mBrowser.currentURI.spec == "about:blank")) {
-                    // This will trigger clearing the location bar. Don't do it if
-                    // we loaded off a blank browser and this is an initial page load
-                    // (e.g. about:privatebrowsing, about:newtab, etc.) so we avoid
-                    // clearing the location bar in case the user is typing in it.
-                    // loading about:blank shouldn't trigger this, either, because its
-                    // loads are "special".
-                    this.mBrowser.urlbarChangeTracker.startedLoad();
-                  }
+                  this.mBrowser.userTypedClear += 2;
+
                   // If the browser is loading it must not be crashed anymore
                   this.mTab.removeAttribute("crashed");
                 }
 
                 if (this._shouldShowProgress(aRequest)) {
                   if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
                     this.mTab.setAttribute("busy", "true");
 
@@ -681,39 +671,47 @@
                   this.mTab.removeAttribute("busy");
                   this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]);
                   if (!this.mTab.selected)
                     this.mTab.setAttribute("unread", "true");
                 }
                 this.mTab.removeAttribute("progress");
 
                 if (aWebProgress.isTopLevel) {
-                  let isSuccessful = Components.isSuccessCode(aStatus);
-                  if (!isSuccessful && !isTabEmpty(this.mTab)) {
+                  if (!Components.isSuccessCode(aStatus) &&
+                      !isTabEmpty(this.mTab)) {
                     // Restore the current document's location in case the
                     // request was stopped (possibly from a content script)
                     // before the location changed.
 
                     this.mBrowser.userTypedValue = null;
 
                     let inLoadURI = this.mBrowser.inLoadURI;
                     if (this.mTab.selected && gURLBar && !inLoadURI) {
                       URLBarSetURI();
                     }
-                  } else if (isSuccessful) {
-                    this.mBrowser.urlbarChangeTracker.finishedLoad();
+                  } else {
+                    // The document is done loading, we no longer want the
+                    // value cleared.
+
+                    if (this.mBrowser.userTypedClear > 1)
+                      this.mBrowser.userTypedClear -= 2;
+                    else if (this.mBrowser.userTypedClear > 0)
+                      this.mBrowser.userTypedClear--;
                   }
 
                   if (!this.mBrowser.mIconURL)
                     this.mTabBrowser.useDefaultIcon(this.mTab);
                 }
 
                 if (this.mBlank)
                   this.mBlank = false;
 
+                var location = aRequest.QueryInterface(nsIChannel).URI;
+
                 // For keyword URIs clear the user typed value since they will be changed into real URIs
                 if (location.scheme == "keyword")
                   this.mBrowser.userTypedValue = null;
 
                 if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.connecting"))
                   this.mTabBrowser.setTabTitle(this.mTab);
 
                 if (this.mTab.selected)
@@ -748,27 +746,28 @@
                                         aFlags) {
               // OnLocationChange is called for both the top-level content
               // and the subframes.
               let topLevel = aWebProgress.isTopLevel;
 
               if (topLevel) {
                 let isSameDocument =
                   !!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
-                // We need to clear the typed value
+                // If userTypedClear > 0, the document loaded correctly and we should be
+                // clearing the user typed value. We also need to clear the typed value
                 // if the document failed to load, to make sure the urlbar reflects the
                 // failed URI (particularly for SSL errors). However, don't clear the value
                 // if the error page's URI is about:blank, because that causes complete
                 // loss of urlbar contents for invalid URI errors (see bug 867957).
                 // Another reason to clear the userTypedValue is if this was an anchor
                 // navigation initiated by the user.
-                if (this.mBrowser.didStartLoadSinceLastUserTyping() ||
+                if (this.mBrowser.userTypedClear > 0 ||
                     ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
                      aLocation.spec != "about:blank") ||
-                    (isSameDocument && this.mBrowser.inLoadURI)) {
+                     (isSameDocument && this.mBrowser.inLoadURI)) {
                   this.mBrowser.userTypedValue = null;
                 }
 
                 // If the browser was playing audio, we should remove the playing state.
                 if (this.mTab.hasAttribute("soundplaying") && !isSameDocument) {
                   this.mTab.removeAttribute("soundplaying");
                   this.mTabBrowser._tabAttrModified(this.mTab, ["soundplaying"]);
                 }
@@ -1580,36 +1579,28 @@
             let filter = this._tabFilters.get(tab);
             let listener = this._tabListeners.get(tab);
             aBrowser.webProgress.removeProgressListener(filter);
             filter.removeProgressListener(listener);
 
             // We'll be creating a new listener, so destroy the old one.
             listener.destroy();
 
-            let oldUserTypedValue = aBrowser.userTypedValue;
-            let hadStartedLoad = aBrowser.didStartLoadSinceLastUserTyping();
-
             // Make sure the browser is destroyed so it unregisters from observer notifications
             aBrowser.destroy();
 
             // Make sure to restore the original droppedLinkHandler.
             let droppedLinkHandler = aBrowser.droppedLinkHandler;
 
             // Change the "remote" attribute.
             let parent = aBrowser.parentNode;
             parent.removeChild(aBrowser);
             aBrowser.setAttribute("remote", aShouldBeRemote ? "true" : "false");
             parent.appendChild(aBrowser);
 
-            aBrowser.userTypedValue = oldUserTypedValue;
-            if (hadStartedLoad) {
-              aBrowser.urlbarChangeTracker.startedLoad();
-            }
-
             aBrowser.droppedLinkHandler = droppedLinkHandler;
 
             // Switching a browser's remoteness will create a new frameLoader.
             // As frameLoaders start out with an active docShell we have to
             // deactivate it if this is not the selected tab's browser or the
             // browser window is minimized.
             aBrowser.docShellIsActive = (aBrowser == this.selectedBrowser &&
                                          window.windowState != window.STATE_MINIMIZED);
@@ -4161,16 +4152,20 @@
                   offset *= -1;
                 this.tabContainer.advanceSelectedTab(offset, true);
                 aEvent.preventDefault();
             }
           }
         ]]></body>
       </method>
 
+      <property name="userTypedClear"
+                onget="return this.mCurrentBrowser.userTypedClear;"
+                onset="return this.mCurrentBrowser.userTypedClear = val;"/>
+
       <property name="userTypedValue"
                 onget="return this.mCurrentBrowser.userTypedValue;"
                 onset="return this.mCurrentBrowser.userTypedValue = val;"/>
 
       <method name="createTooltip">
         <parameter name="event"/>
         <body><![CDATA[
           event.stopPropagation();
--- a/browser/base/content/test/urlbar/browser.ini
+++ b/browser/base/content/test/urlbar/browser.ini
@@ -48,17 +48,16 @@ support-files =
   authenticate.sjs
 [browser_urlbarDecode.js]
 [browser_urlbarDelete.js]
 [browser_urlbarEnter.js]
 [browser_urlbarEnterAfterMouseOver.js]
 skip-if = os == "linux" # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
 [browser_urlbarHashChangeProxyState.js]
 [browser_urlbarKeepStateAcrossTabSwitches.js]
-[browser_urlbarPrivateBrowsingWindowChange.js]
 [browser_urlbarRevert.js]
 [browser_urlbarSearchSingleWordNotification.js]
 [browser_urlbarSearchSuggestions.js]
 support-files =
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
 [browser_urlbarSearchSuggestionsNotification.js]
 support-files =
deleted file mode 100644
--- a/browser/base/content/test/urlbar/browser_urlbarPrivateBrowsingWindowChange.js
+++ /dev/null
@@ -1,43 +0,0 @@
-"use strict";
-
-/**
- * Test that when opening a private browsing window and typing in it before about:privatebrowsing
- * loads, we don't clear the URL bar.
- */
-add_task(function*() {
-  let urlbarTestValue = "Mary had a little lamb";
-  let win = OpenBrowserWindow({private: true});
-  let delayedStartupFinished = TestUtils.topicObserved("browser-delayed-startup-finished",
-                                                       subject => subject == win);
-  yield BrowserTestUtils.waitForEvent(win, "load");
-  let urlbar = win.document.getElementById("urlbar");
-  urlbar.value = urlbarTestValue;
-  // Need this so the autocomplete controller attaches:
-  let focusEv = new FocusEvent("focus", {});
-  urlbar.dispatchEvent(focusEv);
-  // And so we know input happened:
-  let inputEv = new InputEvent("input", {data: "", view: win, bubbles: true});
-  urlbar.onInput(inputEv);
-  // Check it worked:
-  is(urlbar.value, urlbarTestValue, "URL bar value should be there");
-  is(win.gBrowser.selectedBrowser.userTypedValue, urlbarTestValue, "browser object should know the url bar value");
-
-  let continueTest;
-  let continuePromise = new Promise(resolve => continueTest = resolve);
-  let wpl = {
-    onLocationChange(aWebProgress, aRequest, aLocation) {
-      if (aLocation && aLocation.spec == "about:privatebrowsing") {
-        continueTest();
-      }
-    },
-  };
-  win.gBrowser.addProgressListener(wpl);
-
-  yield continuePromise;
-  is(urlbar.value, urlbarTestValue,
-     "URL bar value should be the same once about:privatebrowsing has loaded");
-  is(win.gBrowser.selectedBrowser.userTypedValue, urlbarTestValue,
-     "browser object should still know url bar value once about:privatebrowsing has loaded");
-  win.gBrowser.removeProgressListener(wpl);
-  yield BrowserTestUtils.closeWindow(win);
-});
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -770,22 +770,20 @@ var SessionStoreInternal = {
           }
         }
         break;
       case "SessionStore:restoreHistoryComplete":
         // Notify the tabbrowser that the tab chrome has been restored.
         let tabData = TabState.collect(tab);
 
         // wall-paper fix for bug 439675: make sure that the URL to be loaded
-        // is always visible in the address bar if no other value is present
+        // is always visible in the address bar
         let activePageData = tabData.entries[tabData.index - 1] || null;
         let uri = activePageData ? activePageData.url || null : null;
-        if (!browser.userTypedValue) {
-          browser.userTypedValue = uri;
-        }
+        browser.userTypedValue = uri;
 
         // If the page has a title, set it.
         if (activePageData) {
           if (activePageData.title) {
             tab.label = activePageData.title;
             tab.crop = "end";
           } else if (activePageData.url != "about:blank") {
             tab.label = activePageData.url;
@@ -815,18 +813,21 @@ var SessionStoreInternal = {
           // previously pending tab. Mark the tab as no longer pending.
           this.markTabAsRestoring(tab);
         } else {
           // If the user was typing into the URL bar when we crashed, but hadn't hit
           // enter yet, then we just need to write that value to the URL bar without
           // loading anything. This must happen after the load, as the load will clear
           // userTypedValue.
           let tabData = TabState.collect(tab);
-          if (tabData.userTypedValue && !tabData.userTypedClear && !browser.userTypedValue) {
+          if (tabData.userTypedValue && !tabData.userTypedClear) {
             browser.userTypedValue = tabData.userTypedValue;
+            if (data.didStartLoad) {
+              browser.userTypedClear++;
+            }
             win.URLBarSetURI();
           }
 
           // Remove state we don't need any longer.
           TabStateCache.update(browser, {
             userTypedValue: null, userTypedClear: null
           });
         }
--- a/browser/components/sessionstore/TabState.jsm
+++ b/browser/components/sessionstore/TabState.jsm
@@ -124,27 +124,22 @@ var TabStateInternal = {
 
     // Store the tab icon.
     if (!("image" in tabData)) {
       let tabbrowser = tab.ownerDocument.defaultView.gBrowser;
       tabData.image = tabbrowser.getIcon(tab);
     }
 
     // If there is a userTypedValue set, then either the user has typed something
-    // in the URL bar, or a new tab was opened with a URI to load.
-    // If so, we also track whether we were still in the process of loading something.
+    // in the URL bar, or a new tab was opened with a URI to load. userTypedClear
+    // is used to indicate whether the tab was in some sort of loading state with
+    // userTypedValue.
     if (!("userTypedValue" in tabData) && browser.userTypedValue) {
       tabData.userTypedValue = browser.userTypedValue;
-      // We always used to keep track of the loading state as an integer, where
-      // '0' indicated the user had typed since the last load (or no load was
-      // ongoing), and any positive value indicated we had started a load since
-      // the last time the user typed in the URL bar. Mimic this to keep the
-      // session store representation in sync, even though we now represent this
-      // more explicitly:
-      tabData.userTypedClear = browser.didStartLoadSinceLastUserTyping() ? 1 : 0;
+      tabData.userTypedClear = browser.userTypedClear;
     }
 
     return tabData;
   },
 
   /**
    * Copy data for the given |browser| from the cache to |tabData|.
    *
--- a/browser/components/sessionstore/test/browser_522545.js
+++ b/browser/components/sessionstore/test/browser_522545.js
@@ -23,18 +23,18 @@ function test() {
     };
 
     waitForBrowserState(state, function() {
       let browser = gBrowser.selectedBrowser;
       is(browser.currentURI.spec, "about:blank",
          "No history entries still sets currentURI to about:blank");
       is(browser.userTypedValue, "example.com",
          "userTypedValue was correctly restored");
-      ok(!browser.didStartLoadSinceLastUserTyping(),
-         "We still know that no load is ongoing");
+      is(browser.userTypedClear, 0,
+         "userTypeClear restored as expected");
       is(gURLBar.value, "example.com",
          "Address bar's value correctly restored");
       // Change tabs to make sure address bar value gets updated
       gBrowser.selectedTab = gBrowser.tabContainer.getItemAtIndex(0);
       is(gURLBar.value, "about:mozilla",
          "Address bar's value correctly updated");
       runNextTest();
     });
@@ -55,18 +55,18 @@ function test() {
     };
 
     waitForBrowserState(state, function() {
       let browser = gBrowser.getBrowserAtIndex(1);
       is(browser.currentURI.spec, "about:blank",
          "No history entries still sets currentURI to about:blank");
       is(browser.userTypedValue, "example.org",
          "userTypedValue was correctly restored");
-      ok(!browser.didStartLoadSinceLastUserTyping(),
-         "We still know that no load is ongoing");
+      is(browser.userTypedClear, 0,
+         "userTypeClear restored as expected");
       is(gURLBar.value, "about:mozilla",
          "Address bar's value correctly restored");
       // Change tabs to make sure address bar value gets updated
       gBrowser.selectedTab = gBrowser.tabContainer.getItemAtIndex(1);
       is(gURLBar.value, "example.org",
          "Address bar's value correctly updated");
       runNextTest();
     });
@@ -88,18 +88,18 @@ function test() {
     };
 
     waitForBrowserState(state, function() {
       let browser = gBrowser.selectedBrowser;
       is(browser.currentURI.spec, "about:config",
          "browser.currentURI set to current entry in SH");
       is(browser.userTypedValue, "example.com",
          "userTypedValue was correctly restored");
-      ok(!browser.didStartLoadSinceLastUserTyping(),
-         "We still know that no load is ongoing");
+      is(browser.userTypedClear, 0,
+         "userTypeClear restored as expected");
       is(gURLBar.value, "example.com",
          "Address bar's value correctly restored to userTypedValue");
       runNextTest();
     });
   }
 
   // This tests the following use case:
   // User is in a tab with session history, presses back at some point, then
@@ -117,18 +117,18 @@ function test() {
     };
 
     waitForBrowserState(state, function() {
       let browser = gBrowser.selectedBrowser;
       is(browser.currentURI.spec, "about:mozilla",
          "browser.currentURI set to current entry in SH");
       is(browser.userTypedValue, "example.org",
          "userTypedValue was correctly restored");
-      ok(!browser.didStartLoadSinceLastUserTyping(),
-         "We still know that no load is ongoing");
+      is(browser.userTypedClear, 0,
+         "userTypeClear restored as expected");
       is(gURLBar.value, "example.org",
          "Address bar's value correctly restored to userTypedValue");
       runNextTest();
     });
   }
 
   // This test simulates lots of tabs opening at once and then quitting/crashing.
   function test_getBrowserState_lotsOfTabsOpening() {
@@ -181,29 +181,28 @@ function test() {
         tabs: [{ entries: [] }]
       }]
     };
 
     waitForBrowserState(state, function() {
       let browser = gBrowser.selectedBrowser;
       // Make sure this tab isn't loading and state is clear before we test.
       is(browser.userTypedValue, null, "userTypedValue is empty to start");
-      ok(!browser.didStartLoadSinceLastUserTyping(),
-         "Initially, no load should be ongoing");
+      is(browser.userTypedClear, 0, "userTypedClear is 0 to start");
 
       let inputText = "example.org";
       gURLBar.focus();
       gURLBar.value = inputText.slice(0, -1);
       EventUtils.synthesizeKey(inputText.slice(-1) , {});
 
       executeSoon(function () {
         is(browser.userTypedValue, "example.org",
            "userTypedValue was set when changing URLBar value");
-        ok(!browser.didStartLoadSinceLastUserTyping(),
-           "No load started since changing URLBar value");
+        is(browser.userTypedClear, 0,
+           "userTypedClear was not changed when changing URLBar value");
 
         // Now make sure ss gets these values too
         let newState = JSON.parse(ss.getBrowserState());
         is(newState.windows[0].tabs[0].userTypedValue, "example.org",
            "sessionstore got correct userTypedValue");
         is(newState.windows[0].tabs[0].userTypedClear, 0,
            "sessionstore got correct userTypedClear");
         runNextTest();
@@ -224,18 +223,18 @@ function test() {
     };
 
     waitForBrowserState(state, function() {
       let browser = gBrowser.selectedBrowser;
       is(browser.currentURI.spec, "http://example.com/",
          "userTypedClear=2 caused userTypedValue to be loaded");
       is(browser.userTypedValue, null,
          "userTypedValue was null after loading a URI");
-      ok(!browser.didStartLoadSinceLastUserTyping(),
-         "We should have reset the load state when the tab loaded");
+      is(browser.userTypedClear, 0,
+         "userTypeClear reset to 0");
       is(gURLBar.textValue, gURLBar.trimValue("http://example.com/"),
          "Address bar's value set after loading URI");
       runNextTest();
     });
   }
 
 
   let tests = [test_newTabFocused, test_newTabNotFocused,
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -272,40 +272,29 @@ var WebNavigation =  {
         this.reload(message.data.flags);
         break;
       case "WebNavigation:Stop":
         this.stop(message.data.flags);
         break;
     }
   },
 
-  _wrapURIChangeCall(fn) {
-    this._inLoadURI = true;
-    try {
-      fn();
-    } finally {
-      this._inLoadURI = false;
-      WebProgressListener.sendLoadCallResult();
-    }
-  },
-
   goBack: function() {
     if (this.webNavigation.canGoBack) {
-      this._wrapURIChangeCall(() => this.webNavigation.goBack());
+      this.webNavigation.goBack();
     }
   },
 
   goForward: function() {
-    if (this.webNavigation.canGoForward) {
-      this._wrapURIChangeCall(() => this.webNavigation.goForward());
-    }
+    if (this.webNavigation.canGoForward)
+      this.webNavigation.goForward();
   },
 
   gotoIndex: function(index) {
-    this._wrapURIChangeCall(() => this.webNavigation.gotoIndex(index));
+    this.webNavigation.gotoIndex(index);
   },
 
   loadURI: function(uri, flags, referrer, referrerPolicy, postData, headers, baseURI) {
     if (AppConstants.MOZ_CRASHREPORTER && CrashReporter.enabled) {
       let annotation = uri;
       try {
         let url = Services.io.newURI(uri, null, null);
         // If the current URI contains a username/password, remove it.
@@ -318,20 +307,24 @@ var WebNavigation =  {
     if (referrer)
       referrer = Services.io.newURI(referrer, null, null);
     if (postData)
       postData = makeInputStream(postData);
     if (headers)
       headers = makeInputStream(headers);
     if (baseURI)
       baseURI = Services.io.newURI(baseURI, null, null);
-    this._wrapURIChangeCall(() => {
-      return this.webNavigation.loadURIWithOptions(uri, flags, referrer, referrerPolicy,
-                                                   postData, headers, baseURI);
-    });
+    this._inLoadURI = true;
+    try {
+      this.webNavigation.loadURIWithOptions(uri, flags, referrer, referrerPolicy,
+                                            postData, headers, baseURI);
+    } finally {
+      this._inLoadURI = false;
+      WebProgressListener.sendLoadCallResult();
+    }
   },
 
   reload: function(flags) {
     this.webNavigation.reload(flags);
   },
 
   stop: function(flags) {
     this.webNavigation.stop(flags);
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -34,51 +34,46 @@
       <property name="canGoBack"
                 onget="return this.webNavigation.canGoBack;"
                 readonly="true"/>
 
       <property name="canGoForward"
                 onget="return this.webNavigation.canGoForward;"
                 readonly="true"/>
 
-      <method name="_wrapURIChangeCall">
-        <parameter name="fn"/>
-        <body>
-          <![CDATA[
-            if (!this.isRemoteBrowser) {
-              this.inLoadURI = true;
-              try {
-                fn();
-              } finally {
-                this.inLoadURI = false;
-              }
-            } else {
-              fn();
-            }
-          ]]>
-        </body>
-      </method>
-
-
       <method name="goBack">
         <body>
           <![CDATA[
             var webNavigation = this.webNavigation;
-            if (webNavigation.canGoBack)
-              this._wrapURIChangeCall(() => webNavigation.goBack());
+            if (webNavigation.canGoBack) {
+              try {
+                this.userTypedClear++;
+                webNavigation.goBack();
+              } finally {
+                if (this.userTypedClear)
+                  this.userTypedClear--;
+              }
+            }
           ]]>
         </body>
       </method>
 
       <method name="goForward">
         <body>
           <![CDATA[
             var webNavigation = this.webNavigation;
-            if (webNavigation.canGoForward)
-              this._wrapURIChangeCall(() => webNavigation.goForward());
+            if (webNavigation.canGoForward) {
+              try {
+                this.userTypedClear++;
+                webNavigation.goForward();
+              } finally {
+                if (this.userTypedClear)
+                  this.userTypedClear--;
+              }
+            }
           ]]>
         </body>
       </method>
 
       <method name="reload">
         <body>
           <![CDATA[
             const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
@@ -111,18 +106,17 @@
       <method name="loadURI">
         <parameter name="aURI"/>
         <parameter name="aReferrerURI"/>
         <parameter name="aCharset"/>
         <body>
           <![CDATA[
             const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
             const flags = nsIWebNavigation.LOAD_FLAGS_NONE;
-            this._wrapURIChangeCall(() =>
-              this.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset));
+            this.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset);
           ]]>
         </body>
       </method>
 
       <!-- throws exception for unknown schemes -->
       <method name="loadURIWithFlags">
         <parameter name="aURI"/>
         <parameter name="aFlags"/>
@@ -143,20 +137,27 @@
               aReferrerURI = params.referrerURI;
               if ('referrerPolicy' in params) {
                 aReferrerPolicy = params.referrerPolicy;
               }
               aCharset = params.charset;
               aPostData = params.postData;
             }
 
-            this._wrapURIChangeCall(() =>
+            if (!(aFlags & this.webNavigation.LOAD_FLAGS_FROM_EXTERNAL))
+              this.userTypedClear++;
+
+            try {
               this.webNavigation.loadURIWithOptions(
                   aURI, aFlags, aReferrerURI, aReferrerPolicy,
-                  aPostData, null, null));
+                  aPostData, null, null);
+            } finally {
+              if (this.userTypedClear)
+                this.userTypedClear--;
+            }
           ]]>
         </body>
       </method>
 
       <method name="goHome">
         <body>
           <![CDATA[
             try {
@@ -188,17 +189,23 @@
           ]]>
         </setter>
       </property>
 
       <method name="gotoIndex">
         <parameter name="aIndex"/>
         <body>
           <![CDATA[
-            this._wrapURIChangeCall(() => this.webNavigation.gotoIndex(aIndex));
+            try {
+              this.userTypedClear++;
+              this.webNavigation.gotoIndex(aIndex);
+            } finally {
+              if (this.userTypedClear)
+                this.userTypedClear--;
+            }
           ]]>
         </body>
       </method>
 
       <property name="currentURI" readonly="true">
        <getter><![CDATA[
           if (this.webNavigation) {
             return this.webNavigation.currentURI;
@@ -805,47 +812,67 @@
         <parameter name="priority"/>
         <body><![CDATA[
           let loadGroup = this.webNavigation.QueryInterface(Components.interfaces.nsIDocumentLoader)
                               .loadGroup.QueryInterface(Components.interfaces.nsISupportsPriority);
           loadGroup.priority = priority;
         ]]></body>
       </method>
 
-      <field name="urlbarChangeTracker">
-        ({
-          _startedLoadSinceLastUserTyping: false,
+      <!--
+        This field tracks the location bar state. The value that the user typed
+        in to the location bar may not be changed while this field is zero.
+        However invoking a load will temporarily increase this field to allow
+        the location bar to be updated to the new URL.
+
+        Case 1: Anchor scroll
+          The user appends the anchor to the URL. This sets the location bar
+          into typed state, and disables changes to the location bar. The user
+          then requests the scroll. loadURIWithFlags temporarily increases the
+          flag by 1 so that the anchor scroll's location change resets the
+          location bar state.
+
+        Case 2: Interrupted load
+          The user types in and submits the URL. This triggers an asynchronous
+          network load which increases the flag by 2. (The temporary increase
+          from loadURIWithFlags is not noticeable in this case.) When the load
+          is interrupted the flag returns to zero, and the location bar stays
+          in typed state.
 
-          startedLoad() {
-            this._startedLoadSinceLastUserTyping = true;
-          },
-          finishedLoad() {
-            this._startedLoadSinceLastUserTyping = false;
-          },
-          userTyped() {
-            this._startedLoadSinceLastUserTyping = false;
-          },
-        })
+        Case 3: New load
+          This works like case 2, but as the load is not interrupted the
+          location changes while the flag is still 2 thus resetting the
+          location bar state.
+
+        Case 4: Corrected load
+          This is a combination of case 2 and case 3, except that the original
+          load is interrupted by the new load. Normally cancelling and starting
+          a new load would reset the flag to 0 and then increase it to 2 again.
+          However both actions occur as a consequence of the loadURIWithFlags
+          invocation, which adds its temporary increase in to the mix. Since
+          the new URL would have been typed in the flag would have been reset
+          before loadURIWithFlags incremented it. The interruption resets the
+          flag to 0 and increases it to 2. Although loadURIWithFlags will
+          decrement the flag it remains at 1 thus allowing the location bar
+          state to be reset when the new load changes the location.
+          This case also applies when loading into a new browser, as this
+          interrupts the default load of about:blank.
+      -->
+      <field name="userTypedClear">
+        1
       </field>
 
-      <method name="didStartLoadSinceLastUserTyping">
-        <body><![CDATA[
-          return !this.inLoadURI &&
-                 this.urlbarChangeTracker._startedLoadSinceLastUserTyping;
-        ]]></body>
-      </method>
-
       <field name="_userTypedValue">
         null
       </field>
 
       <property name="userTypedValue"
                 onget="return this._userTypedValue;">
         <setter><![CDATA[
-          this.urlbarChangeTracker.userTyped();
+          this.userTypedClear = 0;
           this._userTypedValue = val;
           return val;
         ]]></setter>
       </property>
 
       <field name="mFormFillAttached">
         false
       </field>