Bug 598600 - New tabs should be added to the last active group after restart [r=dolske,a=blocking2.0]
authorRaymond Lee <raymond@raysquare.com>
Thu, 14 Oct 2010 01:07:55 +0800
changeset 56457 e2124a8f65ed0ea2579af3d2bd2cf3b90d1bdf99
parent 56456 c6080277f39728452f6cb2b14bfc6200c73d9995
child 56458 c65228e510d61458d0d88b462b17c3a4b95321b5
push idunknown
push userunknown
push dateunknown
reviewersdolske, blocking2
bugs598600
milestone2.0b8pre
first release with
nightly win64
e2124a8f65ed / 4.0b8pre / 20101025161627 / files
nightly linux32
nightly linux64
nightly mac
nightly win32
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly win64
Bug 598600 - New tabs should be added to the last active group after restart [r=dolske,a=blocking2.0]
browser/base/content/tabview/groupitems.js
browser/base/content/tabview/tabitems.js
browser/base/content/test/tabview/Makefile.in
browser/base/content/test/tabview/browser_tabview_bug598600.js
browser/components/search/test/browser_addEngine.js
--- a/browser/base/content/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -65,16 +65,17 @@
 //   dontPush - true if this groupItem shouldn't push away on creation; default is false
 //   dontPush - true if this groupItem shouldn't push away or snap on creation; default is false
 //   immediately - true if we want all placement immediately, not with animation
 function GroupItem(listOfEls, options) {
   if (typeof options == 'undefined')
     options = {};
 
   this._inited = false;
+  this._uninited = false;
   this._children = []; // an array of Items
   this.defaultSize = new Point(TabItems.tabWidth * 1.5, TabItems.tabHeight * 1.5);
   this.isAGroupItem = true;
   this.id = options.id || GroupItems.getNextID();
   this._isStacked = false;
   this._stackAngles = [0];
   this.expanded = null;
   this.locked = (options.locked ? Utils.copy(options.locked) : {});
@@ -361,25 +362,33 @@ GroupItem.prototype = Utils.extend(new I
   isEmpty: function GroupItem_isEmpty() {
     return !this._children.length && !this.getTitle();
   },
 
   // ----------
   // Function: save
   // Saves this groupItem to persistent storage.
   save: function GroupItem_save() {
-    if (!this._inited) // too soon to save now
+    if (!this._inited || this._uninited) // too soon/late to save
       return;
 
     var data = this.getStorageData();
     if (GroupItems.groupItemStorageSanity(data))
       Storage.saveGroupItem(gWindow, data);
   },
 
   // ----------
+  // Function: deleteData
+  // Deletes the groupItem in the persistent storage.
+  deleteData: function GroupItem_deleteData() {
+    this._uninited = true;
+    Storage.deleteGroupItem(gWindow, this.id);
+  },
+
+  // ----------
   // Function: getTitle
   // Returns the title of this groupItem as a string.
   getTitle: function GroupItem_getTitle() {
     var value = (this.$title ? this.$title.val() : '');
     return (value == this.defaultName ? '' : value);
   },
 
   // ----------
@@ -556,17 +565,17 @@ GroupItem.prototype = Utils.extend(new I
     }, {
       duration: 170,
       complete: function() {
         iQ(this).remove();
         Items.unsquish();
       }
     });
 
-    Storage.deleteGroupItem(gWindow, this.id);
+    this.deleteData();
   },
 
   // ----------
   // Function: closeAll
   // Closes the groupItem and all of its children.
   closeAll: function GroupItem_closeAll() {
     let closeCenter = this.getBounds().center();
     if (this._children.length > 0) {
@@ -642,17 +651,17 @@ GroupItem.prototype = Utils.extend(new I
       self._sendToSubscribers("close");
       self.removeTrenches();
 
       iQ(self.container).remove();
       self.$undoContainer.remove();
       self.$undoContainer = null;
       Items.unsquish();
 
-      Storage.deleteGroupItem(gWindow, self.id);
+      self.deleteData();
     };
 
     this.$undoContainer.click(function(e) {
       // Only do this for clicks on this actual element.
       if (e.target.nodeName != self.$undoContainer[0].nodeName)
         return;
 
       self.$undoContainer.fadeOut(function() {
@@ -2067,13 +2076,13 @@ let GroupItems = {
     this.groupItems.forEach(function(groupItem) {
       if (groupItem.hidden) {
         let toClose = groupItem._children.concat();
         toClose.forEach(function(child) {
           child.removeSubscriber(groupItem, "close");
           child.close();
         });
 
-        Storage.deleteGroupItem(gWindow, groupItem.id);
+        groupItem.deleteData();
       }
     });
   }
 };
--- a/browser/base/content/tabview/tabitems.js
+++ b/browser/base/content/tabview/tabitems.js
@@ -197,21 +197,20 @@ function TabItem(tab, options) {
 
   iQ("<div>")
     .addClass('expander')
     .appendTo($div);
 
   this._updateDebugBounds();
 
   TabItems.register(this);
-  
-  if (!this.reconnected) {
+
+  if (!this.reconnected)
     GroupItems.newTab(this, options);
-  }
-  
+
   // tabs which were not reconnected at all or were not immediately added
   // to a group get the same treatment.
   if (!this.reconnected || (reconnected && !reconnected.addedToGroup) ) {
     this.setResizable(true, options.immediately);
     this.droppable(true);
   }
 };
 
@@ -799,17 +798,17 @@ let TabItems = {
         tabItem.favEl.src = iconUrl;
 
       // ___ URL
       let tabUrl = tab.linkedBrowser.currentURI.spec;
       if (tabUrl != tabItem.url) {
         let oldURL = tabItem.url;
         tabItem.url = tabUrl;
 
-        if (!tabItem.reconnected && (oldURL == 'about:blank' || !oldURL))
+        if (!tabItem.reconnected)
           this.reconnect(tabItem);
 
         tabItem.save();
       }
 
       // ___ label
       let label = tab.label;
       let $name = iQ(tabItem.nameEl);
@@ -1052,20 +1051,20 @@ let TabItems = {
               item.hideCachedData();
             }
           }, 15000);
         }
 
         item.reconnected = true;
         found = {addedToGroup: tabData.groupID};
       } else {
-        // if it's not a blank tab or it belongs to a group, it would mean 
-        // the item is reconnected.
-        item.reconnected = 
-          (item.tab.linkedBrowser.currentURI.spec != 'about:blank' || item.parent);
+        // We should never have any orphaned tabs. Therefore, item is not 
+        // connected if it has no parent and GroupItems.newTab() would handle 
+        // the group creation.
+        item.reconnected = (item.parent != null);
       }
       item.save();
 
       if (item.reconnected)
         item._sendToSubscribers("reconnected");
     } catch(e) {
       Utils.log(e);
     }
--- a/browser/base/content/test/tabview/Makefile.in
+++ b/browser/base/content/test/tabview/Makefile.in
@@ -51,16 +51,17 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug590606.js \
                  browser_tabview_bug591706.js \
                  browser_tabview_bug594176.js \
                  browser_tabview_bug595191.js \
                  browser_tabview_bug595518.js \
                  browser_tabview_bug595804.js \
                  browser_tabview_bug595930.js \
                  browser_tabview_bug595943.js \
+                 browser_tabview_bug598600.js \
                  browser_tabview_dragdrop.js \
                  browser_tabview_exit_button.js \
                  browser_tabview_group.js \
                  browser_tabview_launch.js \
                  browser_tabview_orphaned_tabs.js \
                  browser_tabview_privatebrowsing.js \
                  browser_tabview_search.js \
                  browser_tabview_snapping.js \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug598600.js
@@ -0,0 +1,115 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is tabview bug598600 test.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Raymond Lee <raymond@appcoast.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+let newWin;
+let prefService;
+
+function test() {
+  let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
+  prefService = 
+    Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).
+      getBranch("browser.panorama.");
+  // make sure we don't trigger the 'first run' behavior
+  prefService.setBoolPref("experienced_first_run", true);
+
+  waitForExplicitFinish();
+
+  // open a new window and setup the window state.
+  newWin = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no");
+  newWin.addEventListener("load", function(event) {
+    this.removeEventListener("load", arguments.callee, false);
+
+    let newState = {
+      windows: [{
+        tabs: [{
+          entries: [{ "url": "about:blank" }],
+          hidden: true,
+          attributes: {},
+          extData: {
+            "tabview-tab":
+              '{"bounds":{"left":20,"top":35,"width":280,"height":210},' +
+              '"userSize":null,"url":"about:blank","groupID":1,' + 
+              '"imageData":null,"title":null}'
+          }
+        },{
+          entries: [{ url: "about:blank" }],
+          index: 1,
+          hidden: false,
+          attributes: {},
+          extData: {
+            "tabview-tab": 
+              '{"bounds":{"left":375,"top":35,"width":280,"height":210},' + 
+              '"userSize":null,"url":"about:blank","groupID":2,' + 
+              '"imageData":null,"title":null}'
+          }
+        }],
+        selected:2,
+        _closedTabs: [],
+        extData: {
+          "tabview-groups": '{"nextID":3,"activeGroupId":2}',
+          "tabview-group": 
+            '{"1":{"bounds":{"left":15,"top":10,"width":320,"height":375},' + 
+            '"userSize":null,"locked":{},"title":"","id":1},' + 
+            '"2":{"bounds":{"left":380,"top":5,"width":320,"height":375},' + 
+            '"userSize":null,"locked":{},"title":"","id":2}}',
+          "tabview-ui": '{"pageBounds":{"left":0,"top":0,"width":875,"height":650}}'
+        }, sizemode:"normal"
+      }]
+    };
+    ss.setWindowState(newWin, JSON.stringify(newState), true);
+
+    // add a new tab.
+    newWin.gBrowser.addTab();
+    is(newWin.gBrowser.tabs.length, 3, "There are 3 browser tabs"); 
+
+    let onTabViewShow = function() {
+      newWin.removeEventListener("tabviewshown", onTabViewShow, false);
+
+      let contentWindow = newWin.document.getElementById("tab-view").contentWindow;
+
+      is(contentWindow.GroupItems.groupItems.length, 2, "Has two group items");
+      is(contentWindow.GroupItems.getOrphanedTabs().length, 0, "No orphan tabs");
+
+      // clean up and finish
+      prefService.setBoolPref("experienced_first_run", false);
+      newWin.close();
+
+      finish();
+    }
+    newWin.addEventListener("tabviewshown", onTabViewShow, false);
+    newWin.TabView.toggle();
+  }, false);
+}