Bug 515409 - closing a tab should go back to opening tab [r=mfinkle]
authorMatt Brubeck <mbrubeck@mozilla.com>
Tue, 15 Jun 2010 17:56:13 -0400
changeset 66287 6d64924f7851559b26d3a9d86aef1d968c673b4d
parent 66286 d20643c6f46343b89dbb8430605e804ba780e346
child 66288 a7214dfb3d07022d2b981f8bc464a07dbf50b8c1
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
bugs515409
Bug 515409 - closing a tab should go back to opening tab [r=mfinkle]
mobile/chrome/content/aboutHome.xhtml
mobile/chrome/content/browser-ui.js
mobile/chrome/content/browser.js
--- a/mobile/chrome/content/aboutHome.xhtml
+++ b/mobile/chrome/content/aboutHome.xhtml
@@ -135,18 +135,19 @@
       }
       catch (ex) { Cu.reportError(ex); }
   
       return null;
     }
 
     function openTabs(aURLs) {
       let BrowserUI = getChromeWin().BrowserUI;
-      for (let i=0; i< aURLs.length; i++) {
-        BrowserUI.newTab(aURLs[i]);
+      let owner = getChromeWin().Browser.selectedTab;
+      for (let i=0; i < aURLs.length; i++) {
+        BrowserUI.newTab(aURLs[i], owner);
       }
     }
 
     function initTabs() {
       let dirService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
       let session = dirService.get("ProfD", Ci.nsILocalFile);
       session.append("sessionstore.bak");
 
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -581,19 +581,19 @@ var BrowserUI = {
 
   updateStar : function() {
     if (PlacesUtils.getMostRecentBookmarkForURI(Browser.selectedBrowser.currentURI) != -1)
       this.starButton.setAttribute("starred", "true");
     else
       this.starButton.removeAttribute("starred");
   },
 
-  newTab : function newTab(aURI) {
+  newTab : function newTab(aURI, aOwner) {
     aURI = aURI || "about:blank";
-    let tab = Browser.addTab(aURI, true);
+    let tab = Browser.addTab(aURI, true, aOwner);
 
     this.hidePanel();
 
     if (aURI == "about:blank") {
       // Display awesomebar UI
       this.showToolbar(true);
     }
     else {
@@ -685,17 +685,23 @@ var BrowserUI = {
 
     // Check open panel
     if (this.isPanelVisible()) {
       this.hidePanel();
       return;
     }
 
     // Only if there are no dialogs, popups, or panels open
-    Browser.selectedBrowser.goBack();
+    let tab = Browser.selectedTab;
+    let browser = tab.browser;
+
+    if (browser.canGoBack)
+      browser.goBack();
+    else if (tab.owner)
+      this.closeTab(tab);
   },
 
   handleEvent: function handleEvent(aEvent) {
     switch (aEvent.type) {
       // Browser events
       case "DOMMetaAdded":
         this._metaAdded(aEvent);
         break;
@@ -2339,17 +2345,17 @@ var ContextHelper = {
     container.hidden = true;
 
     BrowserUI.popPopup();
   }
 };
 
 var ContextCommands = {
   openInNewTab: function cc_openInNewTab(aEvent) {
-    Browser.addTab(ContextHelper.linkURL, false);
+    Browser.addTab(ContextHelper.linkURL, false, Browser.selectedTab);
   },
 
   saveImage: function cc_saveImage(aEvent) {
     let doc = ContextHelper.popupNode.ownerDocument;
     saveImageURL(ContextHelper.mediaURL, null, "SaveImageTitle", false, false, doc.documentURIObject);
   }
 }
 
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -755,18 +755,19 @@ var Browser = {
   getTabFromChrome: function getTabFromChrome(chromeTab) {
     for (var t = 0; t < this._tabs.length; t++) {
       if (this._tabs[t].chromeTab == chromeTab)
         return this._tabs[t];
     }
     return null;
   },
 
-  addTab: function(uri, bringFront) {
+  addTab: function(uri, bringFront, aOwner) {
     let newTab = new Tab();
+    newTab.owner = aOwner || null;
     this._tabs.push(newTab);
 
     if (bringFront)
       this.selectedTab = newTab;
 
     newTab.load(uri);
 
     let event = document.createEvent("Events");
@@ -781,22 +782,28 @@ var Browser = {
       tab = this.getTabFromChrome(tab);
 
     if (!tab)
       return;
 
     let tabIndex = this._tabs.indexOf(tab);
 
     let nextTab = this._selectedTab;
-    if (this._selectedTab == tab) {
-      nextTab = this.getTabAtIndex(tabIndex + 1) || this.getTabAtIndex(tabIndex - 1);
+    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;
+    });
+
     let event = document.createEvent("Events");
     event.initEvent("TabClose", true, false);
     tab.chromeTab.dispatchEvent(event);
 
     this.selectedTab = nextTab;
 
     tab.destroy();
     this._tabs.splice(tabIndex, 1);
@@ -1673,21 +1680,20 @@ nsBrowserAccess.prototype = {
     }
 
     let browser;
     if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW) {
       let url = aURI ? aURI.spec : "about:blank";
       let newWindow = openDialog("chrome://browser/content/browser.xul", "_blank",
                                  "all,dialog=no", url, null, null, null);
       browser = newWindow.Browser.selectedBrowser;
-    } else {
-      if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB)
-        browser = Browser.addTab("about:blank", true).browser;
-      else // OPEN_CURRENTWINDOW and illegal values
-        browser = Browser.selectedBrowser;
+    } else if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
+      browser = Browser.addTab("about:blank", true, Browser.selectedTab).browser;
+    } else { // OPEN_CURRENTWINDOW and illegal values
+      browser = Browser.selectedBrowser;
     }
     
     try {
       let referrer;
       if (aURI) {
         if (aOpener) {
           location = aOpener.location;
           referrer = gIOService.newURI(location, null, null);
@@ -1948,17 +1954,17 @@ ContentCustomClicker.prototype = {
         Util.executeSoon(function() {
           self._dispatchMouseEvent(element, "mousedown", cX, cY);
           self._dispatchMouseEvent(element, "mouseup", cX, cY);
         });
       }
       else if (modifiers == Ci.nsIDOMNSEvent.CONTROL_MASK) {
         let uri = Util.getHrefForElement(element);
         if (uri)
-          Browser.addTab(uri, false);
+          Browser.addTab(uri, false, Browser.selectedTab);
       }
     },
 
     doubleClick: function doubleClick(cX1, cY1, cX2, cY2) {
       this._hideCanvas();
 
       const kDoubleClickRadius = 32;
 
@@ -2518,17 +2524,17 @@ const gPopupBlockerObserver = {
         // useful to the user, so we won't create a menu item for it.
         if (popupURIspec == "" || popupURIspec == "about:blank" ||
             popupURIspec == uri.spec)
           continue;
 
         let popupFeatures = pageReport[i].popupWindowFeatures;
         let popupName = pageReport[i].popupWindowName;
 
-        Browser.addTab(popupURIspec, false);
+        Browser.addTab(popupURIspec, false, Browser.selectedTab);
       }
     }
   }
 };
 
 const gXPInstallObserver = {
   observe: function xpi_observer(aSubject, aTopic, aData)
   {
@@ -2990,16 +2996,18 @@ function Tab() {
   this._browser = null;
   this._browserViewportState = null;
   this._state = null;
   this._listener = null;
   this._loading = false;
   this._chromeTab = null;
   this._resizeAndPaint = Util.bind(this._resizeAndPaint, this);
 
+  this.owner = null;
+
   // Set to 0 since new tabs that have not been viewed yet are good tabs to
   // toss if app needs more memory.
   this.lastSelected = 0;
 
   this.create();
 }
 
 Tab.prototype = {