Bug 1438274 - Fix browser and page actions clearance when navigating r=mixedpuppy a=lizzard
authorOriol Brufau <oriol-bugzilla@hotmail.com>
Thu, 15 Feb 2018 02:57:53 +0100
changeset 454943 5709eb07da4ac8270ed98e1846766ecb48c35ab0
parent 454942 12759635b5dc0450dc32f43b345740363ac23fa1
child 454944 bc4dc448c43bf76d945aa26742400aebb7115113
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy, lizzard
bugs1438274
milestone59.0
Bug 1438274 - Fix browser and page actions clearance when navigating r=mixedpuppy a=lizzard MozReview-Commit-ID: Jb43H65LmFB
browser/components/extensions/ext-browser.js
browser/components/extensions/test/browser/browser_ext_browserAction_context.js
browser/components/extensions/test/browser/browser_ext_pageAction_context.js
browser/components/extensions/test/browser/browser_ext_pageAction_title.js
--- a/browser/components/extensions/ext-browser.js
+++ b/browser/components/extensions/ext-browser.js
@@ -109,17 +109,16 @@ global.makeWidgetId = id => {
 global.TabContext = class extends EventEmitter {
   constructor(getDefaults, extension) {
     super();
 
     this.extension = extension;
     this.getDefaults = getDefaults;
 
     this.tabData = new WeakMap();
-    this.lastLocation = new WeakMap();
 
     windowTracker.addListener("progress", this);
     windowTracker.addListener("TabSelect", this);
   }
 
   get(nativeTab) {
     if (!this.tabData.has(nativeTab)) {
       this.tabData.set(nativeTab, this.getDefaults(nativeTab));
@@ -135,34 +134,24 @@ global.TabContext = class extends EventE
   handleEvent(event) {
     if (event.type == "TabSelect") {
       let nativeTab = event.target;
       this.emit("tab-select", nativeTab);
       this.emit("location-change", nativeTab);
     }
   }
 
-  onStateChange(browser, webProgress, request, stateFlags, statusCode) {
-    let flags = Ci.nsIWebProgressListener;
-
-    if (!(~stateFlags & (flags.STATE_IS_WINDOW | flags.STATE_START) ||
-          this.lastLocation.has(browser))) {
-      this.lastLocation.set(browser, request.URI);
-    }
-  }
-
   onLocationChange(browser, webProgress, request, locationURI, flags) {
     let gBrowser = browser.ownerGlobal.gBrowser;
-    let lastLocation = this.lastLocation.get(browser);
-    if (browser === gBrowser.selectedBrowser &&
-        !(lastLocation && lastLocation.equalsExceptRef(browser.currentURI))) {
-      let nativeTab = gBrowser.getTabForBrowser(browser);
-      this.emit("location-change", nativeTab, true);
+    if (browser === gBrowser.selectedBrowser) {
+      let tab = gBrowser.getTabForBrowser(browser);
+      // fromBrowse will be false in case of e.g. a hash change or history.pushState
+      let fromBrowse = !(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
+      this.emit("location-change", tab, fromBrowse);
     }
-    this.lastLocation.set(browser, browser.currentURI);
   }
 
   shutdown() {
     windowTracker.removeListener("progress", this);
     windowTracker.removeListener("TabSelect", this);
   }
 };
 
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_context.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_context.js
@@ -221,16 +221,27 @@ add_task(async function testTabSwitchCon
         {"icon": browser.runtime.getURL("default-2.png"),
          "popup": browser.runtime.getURL("default-2.html"),
          "title": "Default Title 2",
          "badge": "d2",
          "badgeBackgroundColor": [0, 0xff, 0, 0xff],
          "disabled": false},
       ];
 
+      let promiseTabLoad = details => {
+        return new Promise(resolve => {
+          browser.tabs.onUpdated.addListener(function listener(tabId, changed) {
+            if (tabId == details.id && changed.url == details.url) {
+              browser.tabs.onUpdated.removeListener(listener);
+              resolve();
+            }
+          });
+        });
+      };
+
       return [
         async expect => {
           browser.test.log("Initial state, expect default properties.");
 
           await expectDefaults(details[0]);
           expect(details[0]);
         },
         async expect => {
@@ -240,16 +251,23 @@ add_task(async function testTabSwitchCon
           await expectDefaults(details[0]);
           expect(details[1]);
         },
         async expect => {
           browser.test.log("Create a new tab. Expect default properties.");
           let tab = await browser.tabs.create({active: true, url: "about:blank?0"});
           tabs.push(tab.id);
 
+          browser.test.log("Await tab load.");
+          let promise = promiseTabLoad({id: tabs[1], url: "about:blank?0"});
+          let {url} = await browser.tabs.get(tabs[1]);
+          if (url === "about:blank") {
+            await promise;
+          }
+
           await expectDefaults(details[0]);
           expect(details[0]);
         },
         async expect => {
           browser.test.log("Change properties. Expect new properties.");
           let tabId = tabs[1];
           browser.browserAction.setIcon({tabId, path: "2.png"});
           browser.browserAction.setPopup({tabId, popup: "2.html"});
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_context.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_context.js
@@ -95,18 +95,23 @@ add_task(async function testTabSwitchCon
           expect(details[1]);
         },
         async expect => {
           browser.test.log("Create a new tab. No icon visible.");
           let tab = await browser.tabs.create({active: true, url: "about:blank?0"});
           tabs.push(tab.id);
           expect(null);
         },
-        expect => {
+        async expect => {
           browser.test.log("Await tab load. No icon visible.");
+          let promise = promiseTabLoad({id: tabs[1], url: "about:blank?0"});
+          let {url} = await browser.tabs.get(tabs[1]);
+          if (url === "about:blank") {
+            await promise;
+          }
           expect(null);
         },
         async expect => {
           browser.test.log("Change properties. Expect new properties.");
           let tabId = tabs[1];
           await browser.pageAction.show(tabId);
 
           browser.pageAction.setIcon({tabId, path: "2.png"});
@@ -188,8 +193,101 @@ add_task(async function testTabSwitchCon
             "unable to set popup to about:addons");
 
           expect(null);
         },
       ];
     },
   });
 });
+
+add_task(async function testNavigationClearsData() {
+  let url = "http://example.com/";
+  let default_title = "Default title";
+  let tab_title = "Tab title";
+
+  let {Management: {global: {tabTracker}}} = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
+  let extension, tab, tabId, tabs = [];
+  async function addTab(...args) {
+    tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, ...args);
+    tabId = tabTracker.getId(tab);
+    tabs.push(tab);
+  }
+  async function locationChange(url, task) {
+    let locationChanged = BrowserTestUtils.waitForLocationChange(gBrowser, url);
+    await ContentTask.spawn(tab.linkedBrowser, url, task);
+    await locationChanged;
+  }
+  function setUrl(url) {
+    return locationChange(url, (url) => { content.location.href = url; });
+  }
+  function historyPushState(url) {
+    return locationChange(url, (url) => { content.history.pushState(null, null, url); });
+  }
+  async function sendMessage(method, param, expect, msg) {
+    extension.sendMessage({method, param, expect, msg});
+    await extension.awaitMessage("done");
+  }
+  async function expectTabSpecificData(msg) {
+    await sendMessage("isShown", {tabId}, true, msg);
+    await sendMessage("getTitle", {tabId}, tab_title, msg);
+  }
+  async function expectDefaultData(msg) {
+    await sendMessage("isShown", {tabId}, false, msg);
+    await sendMessage("getTitle", {tabId}, default_title, msg);
+  }
+  async function setTabSpecificData() {
+    await expectDefaultData("Expect default data before setting tab-specific data.");
+    await sendMessage("show", tabId);
+    await sendMessage("setTitle", {tabId, title: tab_title});
+    await expectTabSpecificData("Expect tab-specific data after setting it.");
+  }
+
+  info("Load a tab before installing the extension");
+  await addTab(url, true, true);
+
+  extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      page_action: {default_title},
+    },
+    background: function() {
+      browser.test.onMessage.addListener(async ({method, param, expect, msg}) => {
+        let result = await browser.pageAction[method](param);
+        if (expect !== undefined) {
+          browser.test.assertEq(expect, result, msg);
+        }
+        browser.test.sendMessage("done");
+      });
+    },
+  });
+  await extension.startup();
+
+  info("Set tab-specific data to the existing tab.");
+  await setTabSpecificData();
+
+  info("Add a hash. Does not cause navigation.");
+  await setUrl(url + "#hash");
+  await expectTabSpecificData("Adding a hash does not clear tab-specific data");
+
+  info("Remove the hash. Causes navigation.");
+  await setUrl(url);
+  await expectDefaultData("Removing hash clears tab-specific data");
+
+  info("Open a new tab, set tab-specific data to it.");
+  await addTab("about:newtab", false, false);
+  await setTabSpecificData();
+
+  info("Load a page in that tab.");
+  await setUrl(url);
+  await expectDefaultData("Loading a page clears tab-specific data.");
+
+  info("Set tab-specific data.");
+  await setTabSpecificData();
+
+  info("Push history state. Does not cause navigation.");
+  await historyPushState(url + "/path");
+  await expectTabSpecificData("history.pushState() does not clear tab-specific data");
+
+  for (let tab of tabs) {
+    await BrowserTestUtils.removeTab(tab);
+  }
+  await extension.unload();
+});
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_title.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_title.js
@@ -96,18 +96,23 @@ add_task(async function testTabSwitchCon
           expect(details[1]);
         },
         async expect => {
           browser.test.log("Create a new tab. No icon visible.");
           let tab = await browser.tabs.create({active: true, url: "about:blank?0"});
           tabs.push(tab.id);
           expect(null);
         },
-        expect => {
+        async expect => {
           browser.test.log("Await tab load. No icon visible.");
+          let promise = promiseTabLoad({id: tabs[1], url: "about:blank?0"});
+          let {url} = await browser.tabs.get(tabs[1]);
+          if (url === "about:blank") {
+            await promise;
+          }
           expect(null);
         },
         async expect => {
           browser.test.log("Change properties. Expect new properties.");
           let tabId = tabs[1];
 
           await browser.pageAction.show(tabId);
           browser.pageAction.setIcon({tabId, path: "2.png"});