Bug 569552 - Fennec: onbeforeunload event is not handled properly [r=mfinkle]
authorVivien Nicolas <21@vingtetun.org>
Wed, 02 Mar 2011 15:13:25 +0100
changeset 67453 a29b816f3d261447380e45235e0b630b02def44f
parent 67452 ba63a2eca07d1fbc62eef7493e62ffdd0dece1b8
child 67454 26aaa9e8065f9efda7c9badd0815a723cd6a04f8
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle
bugs569552
Bug 569552 - Fennec: onbeforeunload event is not handled properly [r=mfinkle]
mobile/chrome/content/browser.js
mobile/chrome/content/content.js
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -358,16 +358,17 @@ var Browser = {
       }
       Services.prefs.clearUserPref("extensions.disabledAddons");
     }
 
     messageManager.addMessageListener("Browser:ViewportMetadata", this);
     messageManager.addMessageListener("Browser:FormSubmit", this);
     messageManager.addMessageListener("Browser:KeyPress", this);
     messageManager.addMessageListener("Browser:ZoomToPoint:Return", this);
+    messageManager.addMessageListener("Browser:CanUnload:Return", this);
     messageManager.addMessageListener("scroll", this);
     messageManager.addMessageListener("Browser:CertException", this);
 
     // broadcast a UIReady message so add-ons know we are finished with startup
     let event = document.createEvent("Events");
     event.initEvent("UIReady", true, false);
     window.dispatchEvent(event);
   },
@@ -521,17 +522,17 @@ var Browser = {
 
     if (hasLocal != useLocal) {
       let oldTab = this.selectedTab;
 
       // Add new tab before closing the old one, in case there is only one.
       Browser.addTab(aURI, true, oldTab, aParams);
       if (/^about:(blank|empty)$/.test(currentURI) && !browser.canGoBack && !browser.canGoForward) {
         oldTab.chromeTab.ignoreUndo = true;
-        this.closeTab(oldTab);
+        this.closeTab(oldTab, { forceClose: true });
         oldTab = null;
       }
     }
     else {
       let params = aParams || {};
       let flags = params.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
       browser.loadURIWithFlags(aURI, flags, params.referrerURI, params.charset, params.postData);
     }
@@ -658,62 +659,75 @@ var Browser = {
     let event = document.createEvent("UIEvents");
     event.initUIEvent("TabOpen", true, false, window, getAttention);
     newTab.chromeTab.dispatchEvent(event);
     newTab.browser.messageManager.sendAsyncMessage("Browser:TabOpen");
 
     return newTab;
   },
 
-  closeTab: function(aTab) {
-    let tab = aTab;
-    if (aTab instanceof XULElement)
-      tab = this.getTabFromChrome(aTab);
-
-    // checking the length is a workaround for bug 615404
-    if (!tab || this._tabs.length < 2)
+  closeTab: function closeTab(aTab, aOptions) {
+    let tab = aTab instanceof XULElement ? this.getTabFromChrome(aTab) : aTab;
+    if (!tab || !this._getNextTab(tab))
+      return;
+
+    if (aOptions && "forceClose" in aOptions && aOptions.forceClose) {
+      this._doCloseTab(aTab);
       return;
+    }
+
+    tab.browser.messageManager.sendAsyncMessage("Browser:CanUnload", {});
+  },
+
+  _doCloseTab: function _docloseTab(aTab) {
+    let nextTab = this._getNextTab(aTab);
+    if (!nextTab)
+       return;
 
     // Make sure we leave the toolbar in an unlocked state
-    if (tab == this._selectedTab && tab.isLoading())
+    if (aTab == this._selectedTab && aTab.isLoading())
       BrowserUI.unlockToolbar();
 
-    let tabIndex = this._tabs.indexOf(tab);
-    if (tabIndex == -1)
-      return;
-
-    let nextTab = this._selectedTab;
-    if (nextTab == tab) {
-      nextTab = tab.owner || this.getTabAtIndex(tabIndex + 1) || this.getTabAtIndex(tabIndex - 1);
-      if (!nextTab)
-        return;
-    }
-
     // Tabs owned by the closed tab are now orphaned.
-    this._tabs.forEach(function(aTab, aIndex, aArray) {
-      if (aTab.owner == tab)
-        aTab.owner = null;
+    this._tabs.forEach(function(item, index, array) {
+      if (item.owner == aTab)
+        item.owner = null;
     });
 
     let event = document.createEvent("Events");
     event.initEvent("TabClose", true, false);
-    tab.chromeTab.dispatchEvent(event);
-    tab.browser.messageManager.sendAsyncMessage("Browser:TabClose");
-
-    let container = tab.chromeTab.parentNode;
-    tab.destroy();
-    this._tabs.splice(tabIndex, 1);
+    aTab.chromeTab.dispatchEvent(event);
+    aTab.browser.messageManager.sendAsyncMessage("Browser:TabClose");
+
+    let container = aTab.chromeTab.parentNode;
+    aTab.destroy();
+    this._tabs.splice(this._tabs.indexOf(aTab), 1);
 
     this.selectedTab = nextTab;
 
     event = document.createEvent("Events");
     event.initEvent("TabRemove", true, false);
     container.dispatchEvent(event);
   },
 
+  _getNextTab: function _getNextTab(aTab) {
+    let tabIndex = this._tabs.indexOf(aTab);
+    if (tabIndex == -1)
+      return null;
+
+    let nextTab = this._selectedTab;
+    if (nextTab == aTab) {
+      nextTab = aTab.owner || this.getTabAtIndex(tabIndex + 1) || this.getTabAtIndex(tabIndex - 1);
+      if (!nextTab)
+        return null;
+    }
+
+    return nextTab;
+  },
+
   get selectedTab() {
     return this._selectedTab;
   },
 
   set selectedTab(tab) {
     if (tab instanceof XULElement)
       tab = this.getTabFromChrome(tab);
 
@@ -1062,16 +1076,29 @@ var Browser = {
         if (tab)
           tab.updateViewportMetadata(json);
         break;
 
       case "Browser:FormSubmit":
         browser.lastLocation = null;
         break;
 
+      case "Browser:CanUnload:Return": {
+        if (!json.permit)
+          return;
+
+        // Allow a little delay to not close the target tab while processing
+        // a message for this particular tab
+        setTimeout(function(self) {
+          let tab = self.getTabForBrowser(browser);
+          self._doCloseTab(tab);
+        }, 0, this);
+        break;
+      }
+
       case "Browser:KeyPress":
         let event = document.createEvent("KeyEvents");
         event.initKeyEvent("keypress", true, true, null,
                            json.ctrlKey, json.altKey, json.shiftKey, json.metaKey,
                            json.keyCode, json.charCode);
         document.getElementById("mainKeyset").dispatchEvent(event);
         break;
 
@@ -2256,17 +2283,17 @@ var ContentCrashObserver = {
         if (Browser.tabs.length == 1) {
           // Get the start page from the *default* pref branch, not the user's
           let fallbackURL = Browser.getHomePage({ useDefault: true });
           Browser.addTab(fallbackURL, false, null, { getAttention: false });
         }
 
         // Close this tab, it could be the reason we crashed. The undo-close-tab
         // system will pick it up.
-        Browser.closeTab(Browser.selectedTab);
+        Browser.closeTab(Browser.selectedTab, { forceClose: true });
       }
 
       // Submit the report, if we have one and the user wants to submit it
       if (submit.value && dumpID)
         self.CrashSubmit.submit(dumpID, Elements.stack, null, null);
     }, 0, this);
   }
 };
@@ -2309,17 +2336,17 @@ function importDialog(aParent, aSrc, aAr
   while (currentNode = nodeIterator.nextNode()) {
     let trimmed = currentNode.nodeValue.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
     if (!trimmed.length)
       currentNode.parentNode.removeChild(currentNode);
   }
 
   let doc = xhr.responseXML.documentElement;
 
-  var dialog  = null;
+  let dialog  = null;
 
   // we need to insert before menulist-container if we want it to show correctly
   // for prompt.select for instance
   let menulistContainer = document.getElementById("menulist-container");
   let parentNode = menulistContainer.parentNode;
 
   // emit DOMWillOpenModalDialog event
   let event = document.createEvent("Events");
--- a/mobile/chrome/content/content.js
+++ b/mobile/chrome/content/content.js
@@ -264,16 +264,17 @@ let Content = {
     addMessageListener("Browser:MouseDown", this);
     addMessageListener("Browser:MouseUp", this);
     addMessageListener("Browser:MouseCancel", this);
     addMessageListener("Browser:SaveAs", this);
     addMessageListener("Browser:ZoomToPoint", this);
     addMessageListener("Browser:MozApplicationCache:Fetch", this);
     addMessageListener("Browser:SetCharset", this);
     addMessageListener("Browser:ContextCommand", this);
+    addMessageListener("Browser:CanUnload", this);
 
     if (Util.isParentProcess())
       addEventListener("DOMActivate", this, true);
 
     addEventListener("MozApplicationManifest", this, false);
     addEventListener("command", this, false);
     addEventListener("pagehide", this, false);
     addEventListener("keypress", this, false, false);
@@ -390,16 +391,21 @@ let Content = {
         target.focus();
         break;
       }
 
       case "Browser:Blur":
         gFocusManager.clearFocus(content);
         break;
 
+      case "Browser:CanUnload":
+        let canUnload = docShell.contentViewer.permitUnload();
+        sendSyncMessage("Browser:CanUnload:Return", { permit: canUnload });
+        break;
+
       case "Browser:MouseOver": {
         let element = elementFromPoint(x, y);
         if (!element)
           return;
 
         // Sending a mousemove force the dispatching of mouseover/mouseout
         this._sendMouseEvent("mousemove", element, x, y);
         break;