Bug 612470 - Don't close the current Panorama group if there are still app tabs left. r=iangilman a=blocking2.0:betaN+

This commit is contained in:
Patrick Walton 2011-01-27 19:15:18 +01:00
parent afa835be2e
commit 971e48fc54
10 changed files with 219 additions and 52 deletions

View File

@ -233,9 +233,15 @@ let TabView = {
event.preventDefault();
self._initFrame(function() {
let tabItem = self._window.GroupItems.getNextGroupItemTab(event.shiftKey);
if (tabItem)
window.gBrowser.selectedTab = tabItem.tab;
let groupItems = self._window.GroupItems;
let tabItem = groupItems.getNextGroupItemTab(event.shiftKey);
if (!tabItem)
return;
// Switch to the new tab, and close the old group if it's now empty.
let oldGroupItem = groupItems.getActiveGroupItem();
window.gBrowser.selectedTab = tabItem.tab;
oldGroupItem.closeIfEmpty();
});
}
}, true);
@ -246,5 +252,12 @@ let TabView = {
prepareUndoCloseTab: function() {
if (this._window)
this._window.UI.restoredClosedTab = true;
},
// ----------
// Cleans up the tab view after undo close tab.
afterUndoCloseTab: function () {
if (this._window)
this._window.UI.restoredClosedTab = false;
}
};

View File

@ -7090,6 +7090,7 @@ function undoCloseTab(aIndex) {
if (ss.getClosedTabCount(window) > (aIndex || 0)) {
TabView.prepareUndoCloseTab();
tab = ss.undoCloseTab(window, aIndex || 0);
TabView.afterUndoCloseTab();
if (blankTabToRemove)
gBrowser.removeTab(blankTabToRemove);

View File

@ -538,36 +538,41 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// ----------
// Function: close
// Closes the groupItem, removing (but not closing) all of its children.
close: function GroupItem_close() {
//
// Parameters:
// options - An object with optional settings for this call.
//
// Options:
// immediately - (bool) if true, no animation will be used
close: function GroupItem_close(options) {
this.removeAll({dontClose: true});
GroupItems.unregister(this);
if (this.hidden) {
iQ(this.container).remove();
if (this.$undoContainer) {
this.$undoContainer.remove();
this.$undoContainer = null;
}
this.removeTrenches();
let self = this;
let destroyGroup = function () {
iQ(self.container).remove();
if (self.$undoContainer) {
self.$undoContainer.remove();
self.$undoContainer = null;
}
self.removeTrenches();
Items.unsquish();
this._sendToSubscribers("close");
self._sendToSubscribers("close");
GroupItems.updateGroupCloseButtons();
}
if (this.hidden || (options && options.immediately)) {
destroyGroup();
} else {
let self = this;
iQ(this.container).animate({
opacity: 0,
"-moz-transform": "scale(.3)",
}, {
duration: 170,
complete: function() {
iQ(this).remove();
self.removeTrenches();
Items.unsquish();
self._sendToSubscribers("close");
GroupItems.updateGroupCloseButtons();
}
complete: destroyGroup
});
}
this.deleteData();
},
@ -623,6 +628,21 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
}
},
// ----------
// Function: closeIfEmpty
// Closes the group if it's empty, unlocked, has no title, is closable, and
// autoclose is enabled (see pauseAutoclose()). Returns true if the close
// occurred and false otherwise.
closeIfEmpty: function() {
if (!this._children.length && !this.locked.close && !this.getTitle() &&
!GroupItems.getUnclosableGroupItemId() &&
!GroupItems._autoclosePaused) {
this.close();
return true;
}
return false;
},
// ----------
// Function: _unhide
// Shows the hidden group.
@ -870,7 +890,9 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
item.groupItemData = {};
item.addSubscriber(this, "close", function() {
self.remove(item);
let dontClose = !item.closedManually && gBrowser._numPinnedTabs > 0;
self.remove(item, { dontClose: dontClose });
if (self._children.length > 0 && self._activeTab) {
GroupItems.setActiveGroupItem(self);
UI.setActiveTab(self._activeTab);
@ -958,17 +980,11 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
if (typeof item.setResizable == 'function')
item.setResizable(true, options.immediately);
if (this._children.length == 0 && !this.locked.close && !this.getTitle() &&
!options.dontClose) {
if (!GroupItems.getUnclosableGroupItemId()) {
this.close();
this._makeClosestTabActive();
} else {
// this.close(); this line is causing the leak but the leak doesn't happen after re-enabling it
}
} else if (!options.dontArrange) {
let closed = options.dontClose ? false : this.closeIfEmpty();
if (closed)
this._makeClosestTabActive();
else if (!options.dontArrage)
this.arrange({animate: !options.immediately});
}
this._sendToSubscribers("childRemoved",{ groupItemId: this.id, item: item });
} catch(e) {
@ -1681,6 +1697,7 @@ let GroupItems = {
_arrangesPending: [],
_removingHiddenGroups: false,
_delayedModUpdates: [],
_autoclosePaused: false,
minGroupHeight: 110,
minGroupWidth: 125,
@ -1938,7 +1955,7 @@ let GroupItems = {
}
toClose.forEach(function(groupItem) {
groupItem.close();
groupItem.close({immediately: true});
});
}
@ -2436,5 +2453,21 @@ let GroupItems = {
return new Point(
Math.max(size.x, GroupItems.minGroupWidth),
Math.max(size.y, GroupItems.minGroupHeight));
},
// ----------
// Function: pauseAutoclose()
// Temporarily disable the behavior that closes groups when they become
// empty. This is used when entering private browsing, to avoid trashing the
// user's groups while private browsing is shuffling things around.
pauseAutoclose: function GroupItems_pauseAutoclose() {
this._autoclosePaused = true;
},
// ----------
// Function: unpauseAutoclose()
// Re-enables the auto-close behavior.
resumeAutoclose: function GroupItems_resumeAutoclose() {
this._autoclosePaused = false;
}
};

View File

@ -201,6 +201,7 @@ function TabItem(tab, options) {
// press close button or middle mouse click
if (iQ(e.target).hasClass("close") || Utils.isMiddleClick(e)) {
self.closedManually = true;
self.close();
} else {
if (!Items.item(this).isDragging)

View File

@ -479,6 +479,17 @@ let UI = {
let event = document.createEvent("Events");
event.initEvent("tabviewshown", true, false);
// Close the active group if it was empty. This will happen when the
// user returns to Panorama after looking at an app tab, having
// closed all other tabs. (If the user is looking at an orphan tab, then
// there is no active group for the purposes of this check.)
let activeGroupItem = null;
if (!GroupItems.getActiveOrphanTab()) {
activeGroupItem = GroupItems.getActiveGroupItem();
if (activeGroupItem && activeGroupItem.closeIfEmpty())
activeGroupItem = null;
}
if (zoomOut && currentTab && currentTab._tabViewTabItem) {
item = currentTab._tabViewTabItem;
// If there was a previous currentTab we want to animate
@ -492,11 +503,8 @@ let UI = {
self.setActiveTab(item);
if (item.parent) {
var activeGroupItem = GroupItems.getActiveGroupItem();
if (activeGroupItem)
activeGroupItem.setTopChild(item);
}
if (activeGroupItem && item.parent)
activeGroupItem.setTopChild(item);
self._resize(true);
dispatchEvent(event);
@ -591,8 +599,10 @@ let UI = {
// Pauses the storage activity that conflicts with sessionstore updates and
// private browsing mode switches. Calls can be nested.
storageBusy: function UI_storageBusy() {
if (!this._storageBusyCount)
if (!this._storageBusyCount) {
TabItems.pauseReconnecting();
GroupItems.pauseAutoclose();
}
this._storageBusyCount++;
},
@ -610,6 +620,7 @@ let UI = {
TabItems.resumeReconnecting();
GroupItems._updateTabBar();
GroupItems.resumeAutoclose();
}
},

View File

@ -80,6 +80,7 @@ _BROWSER_FILES = \
browser_tabview_bug608184.js \
browser_tabview_bug608158.js \
browser_tabview_bug610242.js \
browser_tabview_bug612470.js \
browser_tabview_bug613541.js \
browser_tabview_bug616729.js \
browser_tabview_bug616967.js \

View File

@ -122,6 +122,7 @@ function testGroups(groupItemOne, groupItemTwo, contentWindow) {
finish();
});
gBrowser.removeTab(groupItemTwo.getChild(0).tab);
groupItemTwo.close();
}
window.addEventListener("tabviewhidden", onTabViewHidden, false);
EventUtils.synthesizeKey("t", { accelKey: true });

View File

@ -81,7 +81,7 @@ function onTabViewWindowLoaded() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
ok(!TabView.isVisible(), "Tab View is hidden because we clicked on the app tab");
// Remove the tab we're looking at. Note: this will also close groupItem (verified below)
// Remove the tab we're looking at.
gBrowser.removeTab(normalXulTab);
// Make sure we haven't returned to TabView; this is the crux of this test
@ -93,9 +93,12 @@ function onTabViewWindowLoaded() {
gBrowser.unpinTab(appXulTab);
gBrowser.removeTab(appXulTab);
ok(groupItem.closeIfEmpty(), "the second group was empty");
// Verify ending state
is(gBrowser.tabs.length, 1, "we finish with one tab");
is(contentWindow.GroupItems.groupItems.length, 1, "we finish with one group");
is(contentWindow.GroupItems.groupItems.length, 1,
"we finish with one group");
ok(!TabView.isVisible(), "we finish with Tab View hidden");
finish();
@ -104,4 +107,4 @@ function onTabViewWindowLoaded() {
window.addEventListener("tabviewhidden", onTabViewHidden, false);
EventUtils.sendMouseEvent({ type: "mousedown" }, normalXulTab._tabViewTabItem.container, contentWindow);
EventUtils.sendMouseEvent({ type: "mouseup" }, normalXulTab._tabViewTabItem.container, contentWindow);
}
}

View File

@ -0,0 +1,110 @@
/* ***** 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 bug 612470 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):
* Patrick Walton <pcwalton@mozilla.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 ***** */
// Tests that groups behave properly when closing all tabs but app tabs.
let appTab, contentWindow;
let originalGroup, originalGroupTab, newGroup, newGroupTab;
function test() {
waitForExplicitFinish();
appTab = gBrowser.selectedTab;
gBrowser.pinTab(appTab);
originalGroupTab = gBrowser.addTab();
addEventListener("tabviewshown", createGroup, false);
TabView.toggle();
}
function createGroup() {
removeEventListener("tabviewshown", createGroup, false);
contentWindow = document.getElementById("tab-view").contentWindow;
is(contentWindow.GroupItems.groupItems.length, 1, "There's only one group");
originalGroup = contentWindow.GroupItems.groupItems[0];
// Create a new group.
let box = new contentWindow.Rect(20, 400, 300, 300);
newGroup = new contentWindow.GroupItem([], { bounds: box });
contentWindow.GroupItems.setActiveGroupItem(newGroup);
addEventListener("tabviewhidden", addTab, false);
TabView.toggle();
}
function addTab() {
removeEventListener("tabviewhidden", addTab, false);
newGroupTab = gBrowser.addTab();
is(newGroup.getChildren().length, 1, "One tab is in the new group");
executeSoon(removeTab);
}
function removeTab() {
is(gBrowser.visibleTabs.length, 2, "There are two tabs displayed");
gBrowser.removeTab(newGroupTab);
is(newGroup.getChildren().length, 0, "No tabs are in the new group");
is(gBrowser.visibleTabs.length, 1, "There is one tab displayed");
is(contentWindow.GroupItems.groupItems.length, 2,
"There are two groups still");
addEventListener("tabviewshown", checkForRemovedGroup, false);
TabView.toggle();
}
function checkForRemovedGroup() {
removeEventListener("tabviewshown", checkForRemovedGroup, false);
is(contentWindow.GroupItems.groupItems.length, 1,
"There is now only one group");
addEventListener("tabviewhidden", finishTest, false);
TabView.toggle();
}
function finishTest() {
removeEventListener("tabviewhidden", finishTest, false);
gBrowser.removeTab(originalGroupTab);
gBrowser.unpinTab(appTab);
finish();
}

View File

@ -49,14 +49,11 @@ let pb = Cc["@mozilla.org/privatebrowsing;1"].
function test() {
waitForExplicitFinish();
// Go into Tab View
window.addEventListener("tabviewshown", onTabViewLoadedAndShown, false);
TabView.toggle();
showTabView(onTabViewLoadedAndShown);
}
// -----------
function onTabViewLoadedAndShown() {
window.removeEventListener("tabviewshown", onTabViewLoadedAndShown, false);
ok(TabView.isVisible(), "Tab View is visible");
// Establish initial state
@ -111,18 +108,15 @@ function onTabViewLoadedAndShown() {
togglePBAndThen(function() {
ok(TabView.isVisible(), "Tab View is visible again");
verifyNormal();
// exit Tab View
window.addEventListener("tabviewhidden", onTabViewHidden, false);
TabView.toggle();
});
hideTabView(onTabViewHidden);
});
});
});
}
// -----------
function onTabViewHidden() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
ok(!TabView.isVisible(), "Tab View is not visible");
// go into private browsing and make sure Tab View remains hidden
@ -196,7 +190,6 @@ function togglePBAndThen(callback) {
return;
Services.obs.removeObserver(pbObserver, "private-browsing-transition-complete");
afterAllTabsLoaded(callback);
}