Bug 1080055: When recreating a browser restore the isAppTab attribute on the docshell. r=mconley
☠☠ backed out by 92c87e95915e ☠ ☠
authorDave Townsend <dtownsend@oxymoronical.com>
Fri, 17 Oct 2014 10:00:40 -0700
changeset 210896 ba0bb4f26680441eb0fcc5c29daa4721c35e329f
parent 210895 c44f001ea6c196717b09dcdf7ec9e1279fe04209
child 210897 e8a01f5feb55744a9cb6c0d5b180f0cf71973524
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmconley
bugs1080055
milestone36.0a1
Bug 1080055: When recreating a browser restore the isAppTab attribute on the docshell. r=mconley
browser/base/content/tabbrowser.xml
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_restore_isAppTab.js
browser/base/content/test/general/head.js
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1480,20 +1480,22 @@
             // Tearing down the browser gives a new permanentKey but we want to
             // keep the old one. Re-set it explicitly after unbinding from DOM.
             aBrowser.permanentKey = permanentKey;
             parent.appendChild(aBrowser);
 
             // Restore the progress listener.
             aBrowser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
 
-            if (aShouldBeRemote)
+            if (aShouldBeRemote) {
               tab.setAttribute("remote", "true");
-            else
+            } else {
               tab.removeAttribute("remote");
+              aBrowser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned })
+            }
 
             if (wasActive)
               aBrowser.focus();
 
             return true;
           ]]>
         </body>
       </method>
@@ -3094,16 +3096,23 @@
             case "DOMWebNotificationClicked": {
               let tab = this.getTabForBrowser(browser);
               if (!tab)
                 return;
               this.selectedTab = tab;
               window.focus();
               break;
             }
+            case "Browser:Init": {
+              let tab = this.getTabForBrowser(browser);
+              if (!tab)
+                return;
+              browser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned })
+              break;
+            }
           }
         ]]></body>
       </method>
 
       <constructor>
         <![CDATA[
           let browserStack = document.getAnonymousElementByAttribute(this, "anonid", "browserStack");
           this.mCurrentBrowser = document.getAnonymousElementByAttribute(this, "anonid", "initialBrowser");
@@ -3151,16 +3160,17 @@
           let remote = window.QueryInterface(Ci.nsIInterfaceRequestor)
             .getInterface(Ci.nsIWebNavigation)
             .QueryInterface(Ci.nsILoadContext)
             .useRemoteTabs;
           if (remote) {
             messageManager.addMessageListener("DOMTitleChanged", this);
             messageManager.addMessageListener("DOMWindowClose", this);
             messageManager.addMessageListener("contextmenu", this);
+            messageManager.addMessageListener("Browser:Init", this);
 
             // If this window has remote tabs, switch to our tabpanels fork
             // which does asynchronous tab switching.
             this.mPanelContainer.classList.add("tabbrowser-tabpanels");
           }
           messageManager.addMessageListener("DOMWebNotificationClicked", this);
         ]]>
       </constructor>
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -386,16 +386,17 @@ skip-if = buildapp == 'mulet' || e10s # 
 [browser_private_browsing_window.js]
 skip-if = buildapp == 'mulet'
 [browser_private_no_prompt.js]
 skip-if = buildapp == 'mulet'
 [browser_relatedTabs.js]
 [browser_removeTabsToTheEnd.js]
 [browser_removeUnsafeProtocolsFromURLBarPaste.js]
 skip-if = e10s
+[browser_restore_isAppTab.js]
 [browser_sanitize-download-history.js]
 skip-if = true # bug 432425
 [browser_sanitize-passwordDisabledHosts.js]
 [browser_sanitize-sitepermissions.js]
 [browser_sanitize-timespans.js]
 skip-if = buildapp == 'mulet'
 [browser_sanitizeDialog.js]
 skip-if = buildapp == 'mulet'
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_restore_isAppTab.js
@@ -0,0 +1,150 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const DUMMY = "http://example.com/browser/browser/base/content/test/general/dummy_page.html";
+
+function getMinidumpDirectory() {
+  let dir = Services.dirsvc.get('ProfD', Ci.nsIFile);
+  dir.append("minidumps");
+  return dir;
+}
+
+// This observer is needed so we can clean up all evidence of the crash so
+// the testrunner thinks things are peachy.
+let CrashObserver = {
+  observe: function(subject, topic, data) {
+    is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
+    ok(subject instanceof Ci.nsIPropertyBag2,
+       'Subject implements nsIPropertyBag2.');
+    // we might see this called as the process terminates due to previous tests.
+    // We are only looking for "abnormal" exits...
+    if (!subject.hasKey("abnormal")) {
+      info("This is a normal termination and isn't the one we are looking for...");
+      return;
+    }
+
+    let dumpID;
+    if ('nsICrashReporter' in Ci) {
+      dumpID = subject.getPropertyAsAString('dumpID');
+      ok(dumpID, "dumpID is present and not an empty string");
+    }
+
+    if (dumpID) {
+      let minidumpDirectory = getMinidumpDirectory();
+      let file = minidumpDirectory.clone();
+      file.append(dumpID + '.dmp');
+      file.remove(true);
+      file = minidumpDirectory.clone();
+      file.append(dumpID + '.extra');
+      file.remove(true);
+    }
+  }
+}
+Services.obs.addObserver(CrashObserver, 'ipc:content-shutdown', false);
+
+registerCleanupFunction(() => {
+  Services.obs.removeObserver(CrashObserver, 'ipc:content-shutdown');
+});
+
+function frameScript() {
+  addMessageListener("Test:GetIsAppTab", function() {
+    sendAsyncMessage("Test:IsAppTab", { isAppTab: docShell.isAppTab });
+  });
+
+  addMessageListener("Test:Crash", function() {
+    privateNoteIntentionalCrash();
+    Components.utils.import("resource://gre/modules/ctypes.jsm");
+    let zero = new ctypes.intptr_t(8);
+    let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
+    badptr.contents
+  });
+}
+
+function loadFrameScript(browser) {
+  browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")();", true);
+}
+
+function isBrowserAppTab(browser) {
+  return new Promise(resolve => {
+    function listener({ data }) {
+      browser.messageManager.removeMessageListener("Test:IsAppTab", listener);
+      resolve(data.isAppTab);
+    }
+    browser.messageManager.addMessageListener("Test:IsAppTab", listener);
+    browser.messageManager.sendAsyncMessage("Test:GetIsAppTab");
+  });
+}
+
+// Restarts the child process by crashing it then reloading the tab
+let restart = Task.async(function*(browser) {
+  // If the tab isn't remote this would crash the main process so skip it
+  if (!browser.isRemoteBrowser)
+    return browser;
+
+  browser.messageManager.sendAsyncMessage("Test:Crash");
+  yield promiseWaitForEvent(browser, "AboutTabCrashedLoad", false, true);
+
+  TabCrashReporter.reloadCrashedTab(browser);
+
+  yield promiseTabLoaded(gBrowser.getTabForBrowser(browser));
+});
+
+add_task(function* navigate() {
+  let tab = gBrowser.addTab("about:robots");
+  let browser = tab.linkedBrowser;
+  gBrowser.selectedTab = tab;
+  yield waitForDocLoadComplete();
+  loadFrameScript(browser);
+  let isAppTab = yield isBrowserAppTab(browser);
+  ok(!isAppTab, "Docshell shouldn't think it is an app tab");
+
+  gBrowser.pinTab(tab);
+  isAppTab = yield isBrowserAppTab(browser);
+  ok(isAppTab, "Docshell should think it is an app tab");
+
+  gBrowser.loadURI(DUMMY);
+  yield waitForDocLoadComplete();
+  loadFrameScript(browser);
+  isAppTab = yield isBrowserAppTab(browser);
+  ok(isAppTab, "Docshell should think it is an app tab");
+
+  gBrowser.unpinTab(tab);
+  isAppTab = yield isBrowserAppTab(browser);
+  ok(!isAppTab, "Docshell shouldn't think it is an app tab");
+
+  gBrowser.pinTab(tab);
+  isAppTab = yield isBrowserAppTab(browser);
+  ok(isAppTab, "Docshell should think it is an app tab");
+
+  gBrowser.loadURI("about:robots");
+  yield waitForDocLoadComplete();
+  loadFrameScript(browser);
+  isAppTab = yield isBrowserAppTab(browser);
+  ok(isAppTab, "Docshell should think it is an app tab");
+
+  gBrowser.removeCurrentTab();
+});
+
+add_task(function* crash() {
+  if (!gMultiProcessBrowser || !TabCrashReporter)
+    return;
+
+  let tab = gBrowser.addTab(DUMMY);
+  let browser = tab.linkedBrowser;
+  gBrowser.selectedTab = tab;
+  yield waitForDocLoadComplete();
+  loadFrameScript(browser);
+  let isAppTab = yield isBrowserAppTab(browser);
+  ok(!isAppTab, "Docshell shouldn't think it is an app tab");
+
+  gBrowser.pinTab(tab);
+  isAppTab = yield isBrowserAppTab(browser);
+  ok(isAppTab, "Docshell should think it is an app tab");
+
+  yield restart(browser);
+  loadFrameScript(browser);
+  isAppTab = yield isBrowserAppTab(browser);
+  ok(isAppTab, "Docshell should think it is an app tab");
+
+  gBrowser.removeCurrentTab();
+});
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -103,26 +103,26 @@ function waitForCondition(condition, nex
 }
 
 function promiseWaitForCondition(aConditionFn) {
   let deferred = Promise.defer();
   waitForCondition(aConditionFn, deferred.resolve, "Condition didn't pass.");
   return deferred.promise;
 }
 
-function promiseWaitForEvent(object, eventName, capturing = false) {
+function promiseWaitForEvent(object, eventName, capturing = false, chrome = false) {
   return new Promise((resolve) => {
     function listener(event) {
       info("Saw " + eventName);
-      object.removeEventListener(eventName, listener, capturing);
+      object.removeEventListener(eventName, listener, capturing, chrome);
       resolve(event);
     }
 
     info("Waiting for " + eventName);
-    object.addEventListener(eventName, listener, capturing);
+    object.addEventListener(eventName, listener, capturing, chrome);
   });
 }
 
 function getTestPlugin(aName) {
   var pluginName = aName || "Test Plug-in";
   var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
   var tags = ph.getPluginTags();