Bug 626455 - modal dialog in onbeforeunload: browser freeze after removing last tab group in panorama; r=dietrich

This commit is contained in:
Tim Taubert 2011-07-28 06:45:36 +02:00
parent f40258527f
commit 60949f9a7a
15 changed files with 358 additions and 142 deletions

View File

@ -0,0 +1,46 @@
/* ***** 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 content.js.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Tim Taubert <ttaubert@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 ***** */
addEventListener("DOMWillOpenModalDialog", function (event) {
// (event.isTrusted == true) when the event is generated by a user action
// and does not originate from a script.
if (event.isTrusted) {
// we're intentionally sending a synchronous message to handle this event
// as quick as possible, switch the selected tab and hide the tabview
// before the modal dialog is shown
sendSyncMessage("Panorama:DOMWillOpenModalDialog");
}
}, true);

View File

@ -738,9 +738,13 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// ----------
// Function: _unhide
// Shows the hidden group.
_unhide: function GroupItem__unhide() {
let self = this;
//
// Parameters:
// options - various options (see below)
//
// Possible options:
// immediately - true when no animations should be used
_unhide: function GroupItem__unhide(options) {
this._cancelFadeAwayUndoButtonTimer();
this.hidden = false;
this.$undoContainer.remove();
@ -748,20 +752,31 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
this.droppable(true);
this.setTrenches(this.bounds);
iQ(this.container).show().animate({
"-moz-transform": "scale(1)",
"opacity": 1
}, {
duration: 170,
complete: function() {
self._children.forEach(function(child) {
iQ(child.container).show();
});
let self = this;
UI.setActive(self);
self._sendToSubscribers("groupShown", { groupItemId: self.id });
}
});
let finalize = function () {
self._children.forEach(function(child) {
iQ(child.container).show();
});
UI.setActive(self);
self._sendToSubscribers("groupShown", { groupItemId: self.id });
};
let $container = iQ(this.container).show();
if (!options || !options.immediately) {
$container.animate({
"-moz-transform": "scale(1)",
"opacity": 1
}, {
duration: 170,
complete: finalize
});
} else {
$container.css({"-moz-transform": "none", opacity: 1});
finalize();
}
GroupItems.updateGroupCloseButtons();
},
@ -779,15 +794,29 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
let remainingGroups = GroupItems.groupItems.filter(function (groupItem) {
return (groupItem != self && groupItem.getChildren().length);
});
let tab = null;
if (!gBrowser._numPinnedTabs && !remainingGroups.length) {
let emptyGroups = GroupItems.groupItems.filter(function (groupItem) {
return (groupItem != self && !groupItem.getChildren().length);
});
let group = (emptyGroups.length ? emptyGroups[0] : GroupItems.newGroup());
group.newTab(null, { closedLastTab: true });
tab = group.newTab(null, {dontZoomIn: true});
}
this.destroy();
let closed = this.destroy();
if (!tab)
return;
if (closed) {
// Let's make the new tab the selected tab.
UI.goToTab(tab);
} else {
// Remove the new tab and group, if this group is no longer closed.
tab._tabViewTabItem.parent.destroy({immediately: true});
}
},
// ----------
@ -800,6 +829,10 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
//
// Options:
// immediately - (bool) if true, no animation will be used
//
// Returns true if the groupItem has been closed, or false otherwise. A group
// could not have been closed due to a tab with an onUnload handler (that
// waits for user interaction).
destroy: function GroupItem_destroy(options) {
let self = this;
@ -808,14 +841,11 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// In other words, the group "close" event is fired before all browser
// tabs in the group are closed. The below code would fire the group "close"
// event only after all browser tabs in that group are closed.
let shouldRemoveTabItems = [];
let toClose = this._children.concat();
toClose.forEach(function(child) {
this._children.concat().forEach(function(child) {
child.removeSubscriber("close", self._onChildClose);
let removed = child.close(true);
if (removed) {
shouldRemoveTabItems.push(child);
if (child.close(true)) {
self.remove(child, { dontArrange: true });
} else {
// child.removeSubscriber() must be called before child.close(),
// therefore we call child.addSubscriber() if the tab is not removed.
@ -823,15 +853,14 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
}
});
if (shouldRemoveTabItems.length != toClose.length) {
// remove children without the assiciated tab and show the group item
shouldRemoveTabItems.forEach(function(child) {
self.remove(child, { dontArrange: true });
});
if (this._children.length) {
if (this.hidden)
this.$undoContainer.fadeOut(function() { self._unhide() });
this.$undoContainer.fadeOut(function() { self._unhide() });
return false;
} else {
this.close(options);
return true;
}
},
@ -1809,13 +1838,16 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// Parameters:
// url - the new tab should open this url as well
// options - the options object
// dontZoomIn - set to true to not zoom into the newly created tab
// closedLastTab - boolean indicates the last tab has just been closed
newTab: function GroupItem_newTab(url, options) {
if (options && options.closedLastTab)
UI.closedLastTabInTabView = true;
UI.setActive(this, { dontSetActiveTabInGroup: true });
gBrowser.loadOneTab(url || "about:blank", { inBackground: false });
let dontZoomIn = !!(options && options.dontZoomIn);
return gBrowser.loadOneTab(url || "about:blank", { inBackground: dontZoomIn });
},
// ----------

View File

@ -482,29 +482,22 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// closing tab doesn't belong to a group and no empty group, create a new
// one for the new tab.
if (!groupClose && gBrowser.tabs.length == 1) {
let group;
if (this.tab._tabViewTabItem.parent) {
group = this.tab._tabViewTabItem.parent;
} else {
let emptyGroups = GroupItems.groupItems.filter(function (groupItem) {
return (!groupItem.getChildren().length);
});
group = (emptyGroups.length ? emptyGroups[0] : GroupItems.newGroup());
}
let group = this.tab._tabViewTabItem.parent;
group.newTab(null, { closedLastTab: true });
}
// when "TabClose" event is fired, the browser tab is about to close and our
// item "close" is fired before the browser tab actually get closed.
// Therefore, we need "tabRemoved" event below.
gBrowser.removeTab(this.tab);
let tabNotClosed =
Array.some(gBrowser.tabs, function(tab) { return tab == this.tab; }, this);
if (!tabNotClosed)
let tabClosed = !this.tab;
if (tabClosed)
this._sendToSubscribers("tabRemoved");
// No need to explicitly delete the tab data, becasue sessionstore data
// associated with the tab will automatically go away
return !tabNotClosed;
return tabClosed;
},
// ----------

View File

@ -26,6 +26,7 @@
* Raymond Lee <raymond@appcoast.com>
* Sean Dunn <seanedunn@yahoo.com>
* Tim Taubert <tim.taubert@gmx.de>
* Mihai Sucan <mihai.sucan@gmail.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
@ -80,6 +81,10 @@ let UI = {
// If true, a closed tab has just been restored.
restoredClosedTab: false,
// Variable: _isChangingVisibility
// Tracks whether we're currently in the process of showing/hiding the tabview.
_isChangingVisibility: false,
// Variable: _reorderTabItemsOnShow
// Keeps track of the <GroupItem>s which their tab items' tabs have been moved
// and re-orders the tab items when switching to TabView.
@ -230,6 +235,15 @@ let UI = {
self.uninit();
});
// ___ setup DOMWillOpenModalDialog message handler
let mm = gWindow.messageManager;
let callback = this._onDOMWillOpenModalDialog.bind(this);
mm.addMessageListener("Panorama:DOMWillOpenModalDialog", callback);
this._cleanupFunctions.push(function () {
mm.removeMessageListener("Panorama:DOMWillOpenModalDialog", callback);
});
// ___ setup key handlers
this._setTabViewFrameKeyHandlers();
@ -272,6 +286,10 @@ let UI = {
self._save();
}, false);
// ___ load frame script
let frameScript = "chrome://browser/content/tabview-content.js";
gWindow.messageManager.loadFrameScript(frameScript, true);
// ___ Done
this._frameInitialized = true;
this._save();
@ -476,9 +494,11 @@ let UI = {
// Parameters:
// zoomOut - true for zoom out animation, false for nothing.
showTabView: function UI_showTabView(zoomOut) {
if (this.isTabViewVisible())
if (this.isTabViewVisible() || this._isChangingVisibility)
return;
this._isChangingVisibility = true;
// initialize the direction of the page
this._initPageDirection();
@ -521,6 +541,7 @@ let UI = {
self.setActive(item);
self._resize(true);
self._isChangingVisibility = false;
dispatchEvent(event);
// Flush pending updates
@ -530,6 +551,7 @@ let UI = {
});
} else {
self.clearActiveTab();
self._isChangingVisibility = false;
dispatchEvent(event);
// Flush pending updates
@ -546,9 +568,11 @@ let UI = {
// Function: hideTabView
// Hides TabView and shows the main browser UI.
hideTabView: function UI_hideTabView() {
if (!this.isTabViewVisible())
if (!this.isTabViewVisible() || this._isChangingVisibility)
return;
this._isChangingVisibility = true;
// another tab might be select if user decides to stay on a page when
// a onclose confirmation prompts.
GroupItems.removeHiddenGroups();
@ -575,6 +599,8 @@ let UI = {
#endif
Storage.saveVisibilityData(gWindow, "false");
this._isChangingVisibility = false;
let event = document.createEvent("Events");
event.initEvent("tabviewhidden", true, false);
dispatchEvent(event);
@ -748,21 +774,14 @@ let UI = {
groupItem._children.length == 1 &&
groupItem._children[0].tab == tab);
// 2) Take care of the case where you've closed the last tab in
// an un-named groupItem, which means that the groupItem is gone (null) and
// there are no visible tabs.
let closingUnnamedGroup = (groupItem == null &&
gBrowser.visibleTabs.length <= 1);
// 3) When a blank tab is active while restoring a closed tab the
// 2) When a blank tab is active while restoring a closed tab the
// blank tab gets removed. The active group is not closed as this is
// where the restored tab goes. So do not show the TabView.
let tabItem = tab && tab._tabViewTabItem;
let closingBlankTabAfterRestore =
(tabItem && tabItem.isRemovedAfterRestore);
if ((closingLastOfGroup || closingUnnamedGroup) &&
!closingBlankTabAfterRestore) {
if (closingLastOfGroup && !closingBlankTabAfterRestore) {
// for the tab focus event to pick up.
self._closedLastVisibleTab = true;
self.showTabView();
@ -882,8 +901,14 @@ let UI = {
// if TabView is visible but we didn't just close the last tab or
// selected tab, show chrome.
if (this.isTabViewVisible())
if (this.isTabViewVisible()) {
// Unhide the group of the tab the user is activating.
if (tab && tab._tabViewTabItem && tab._tabViewTabItem.parent &&
tab._tabViewTabItem.parent.hidden)
tab._tabViewTabItem.parent._unhide({immediately: true});
this.hideTabView();
}
// another tab might be selected when hideTabView() is invoked so a
// validation is needed.
@ -917,6 +942,27 @@ let UI = {
}
},
// ----------
// Function: _onDOMWillOpenModalDialog
// Called when a web page is about to show a modal dialog.
_onDOMWillOpenModalDialog: function UI__onDOMWillOpenModalDialog(cx) {
if (!this.isTabViewVisible())
return;
let index = gBrowser.browsers.indexOf(cx.target);
if (index == -1)
return;
let tab = gBrowser.tabs[index];
// When TabView is visible, we need to call onTabSelect to make sure that
// TabView is hidden and that the correct group is activated. When a modal
// dialog is shown for currently selected tab the onTabSelect event handler
// is not called, so we need to do it.
if (gBrowser.selectedTab == tab && this._currentTab == tab)
this.onTabSelect(tab);
},
// ----------
// Function: setReorderTabsOnHide
// Sets the groupItem which the tab items' tabs should be re-ordered when

View File

@ -111,6 +111,7 @@ _BROWSER_FILES = \
browser_tabview_bug625269.js \
browser_tabview_bug625424.js \
browser_tabview_bug626368.js \
browser_tabview_bug626455.js \
browser_tabview_bug626525.js \
browser_tabview_bug626791.js \
browser_tabview_bug627239.js \

View File

@ -10,7 +10,7 @@ function test() {
function part1(win) {
registerCleanupFunction(function() win.close());
let contentWindow = win.document.getElementById("tab-view").contentWindow;
let contentWindow = win.TabView.getContentWindow();
is(contentWindow.GroupItems.groupItems.length, 1, "Has only one group");
let originalTab = win.gBrowser.selectedTab;
@ -71,15 +71,12 @@ function part2(win) {
// switch the selected tab to new tab
win.gBrowser.selectedTab = newTab;
whenTabViewIsHidden(function () {
is(win.gBrowser.selectedTab, newTab, "The seleted tab should be the same as before (new tab)");
win.close();
finish();
});
// show tabview
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, win);
// hide tabview
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, win);
})
showTabView(function () {
hideTabView(function () {
is(win.gBrowser.selectedTab, newTab,
"The selected tab should be the same as before (new tab)");
waitForFocus(finish);
}, win);
}, win);
}, win);
}

View File

@ -21,9 +21,7 @@ function onTabViewShown() {
function testStayOnPage(contentWindow, groupItemOne, groupItemTwo) {
whenDialogOpened(function (dialog) {
groupItemTwo.addSubscriber("groupShown", function onShown() {
groupItemTwo.removeSubscriber("groupShown", onShown);
executeSoon(function () {
is(gBrowser.tabs.length, 2,
"The total number of tab is 2 when staying on the page");
is(contentWindow.TabItems.getItems().length, 2,

View File

@ -25,6 +25,7 @@ function test1() {
is(groupItems.length, 1, "there is one groupItem");
whenTabViewIsHidden(function() {
gBrowser.selectedTab = gBrowser.tabs[0];
is(groupItems.length, 2, "there are two groupItems");
closeGroupItem(groupItems[1], finish);
});

View File

@ -18,6 +18,6 @@ function test() {
cw.GroupItems.resumeArrange();
ok(groupItem.isStacked(), 'groupItem is now stacked');
closeGroupItem(groupItem, finish);
closeGroupItem(groupItem, function () hideTabView(finish));
});
}

View File

@ -34,7 +34,7 @@ function test() {
ok(!document.getElementById("context_openTabInWindow").disabled, "The 'Move to New Window' menu item is enabled");
let newTabTwo = gBrowser.selectedTab;
gBrowser.selected = originalTab;
gBrowser.selectedTab = originalTab;
gBrowser.removeTab(newTabOne);
gBrowser.removeTab(newTabTwo);

View File

@ -0,0 +1,127 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*
* Contributor(s):
* Mihai Sucan <mihai.sucan@gmail.com>
* Raymond Lee <raymond@appcoast.com>
*/
const TEST_URL = 'data:text/html,<script>window.onbeforeunload=' +
'function(e){e.returnValue="?"}</script><body ' +
'onunload="alert(\'onunload\')" onpagehide="' +
'alert(\'onpagehide\')"></body>';
let contentWindow;
let activeGroup;
function test() {
waitForExplicitFinish();
showTabView(function () {
contentWindow = TabView.getContentWindow();
activeGroup = contentWindow.GroupItems.getActiveGroupItem();
gBrowser.browsers[0].contentWindow.location =
"data:text/html,<p>test for bug 626455, tab1";
gBrowser.addTab(TEST_URL);
afterAllTabsLoaded(testStayOnPage);
});
}
function testStayOnPage() {
whenDialogOpened(function (dialog) {
// stay on page
dialog.cancelDialog();
executeSoon(function () {
showTabView(function () {
is(gBrowser.tabs.length, 1,
"The total number of tab is 1 when staying on the page");
let location = gBrowser.browsers[0].contentWindow.location.toString();
isnot(location.indexOf("onbeforeunload"), -1,
"The open tab is the expected one");
is(contentWindow.GroupItems.getActiveGroupItem(), activeGroup,
"Active group is still the same");
is(contentWindow.GroupItems.groupItems.length, 1,
"Only one group is open");
// start the next test
testLeavePage();
});
});
});
closeGroupItem(activeGroup);
}
function testLeavePage() {
let dialogsAccepted = 0;
whenDialogOpened(function onDialogOpened(dialog) {
if (++dialogsAccepted < 3)
whenDialogOpened(onDialogOpened);
// Leave page
dialog.acceptDialog();
});
whenGroupClosed(activeGroup, finishTest);
closeGroupItem(activeGroup);
}
function finishTest() {
is(gBrowser.tabs.length, 1,
"The total number of tab is 1 after leaving the page");
is(contentWindow.TabItems.getItems().length, 1,
"The total number of tab items is 1 after leaving the page");
let location = gBrowser.browsers[0].contentWindow.location.toString();
is(location, "about:blank", "The open tab is the expected one");
isnot(contentWindow.GroupItems.getActiveGroupItem(), activeGroup,
"Active group is no longer the same");
is(contentWindow.GroupItems.groupItems.length, 1,
"Only one group is open");
finish();
}
// ----------
function whenGroupClosed(group, callback) {
group.addSubscriber("close", function onClose() {
group.removeSubscriber("close", onClose);
callback();
});
}
// ----------
function whenDialogOpened(callback) {
let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
let listener = {
onCloseWindow: function () {},
onWindowTitleChange: function () {},
onOpenWindow: function (xulWin) {
let domWin = xulWin.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
whenWindowLoaded(domWin, function () {
let dialog = domWin.document.querySelector("dialog");
if (dialog) {
wm.removeListener(listener);
callback(dialog);
}
});
}
};
wm.addListener(listener);
}

View File

@ -67,7 +67,7 @@ function test() {
EventUtils.synthesizeMouse(target, 600, 5, {type: "mouseup"}, cw);
checkNumberOfGroupItems(2);
next();
closeGroupItem(cw.GroupItems.groupItems[1], next);
}, win);
}

View File

@ -3,49 +3,32 @@
function test() {
waitForExplicitFinish();
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
TabView.toggle();
showTabView(onTabViewShown);
}
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
function onTabViewShown() {
ok(TabView.isVisible(), "Tab View is visible");
let contentWindow = document.getElementById("tab-view").contentWindow;
let contentWindow = TabView.getContentWindow();
let [originalTab] = gBrowser.visibleTabs;
let createGroupItem = function (left, top, width, height) {
let box = new contentWindow.Rect(left, top, width, height);
let groupItem = new contentWindow.GroupItem([], {bounds: box, immediately: true});
contentWindow.UI.setActive(groupItem);
gBrowser.loadOneTab("about:blank", {inBackground: true});
return groupItem;
};
// create group one and two
let boxOne = new contentWindow.Rect(20, 20, 300, 300);
let groupOne = new contentWindow.GroupItem([], { bounds: boxOne });
ok(groupOne.isEmpty(), "This group is empty");
let groupOne = createGroupItem(20, 20, 300, 300);
let groupTwo = createGroupItem(20, 400, 300, 300);
let boxTwo = new contentWindow.Rect(20, 400, 300, 300);
let groupTwo = new contentWindow.GroupItem([], { bounds: boxTwo });
groupOne.addSubscriber("childAdded", function onChildAdded() {
groupOne.removeSubscriber("childAdded", onChildAdded);
groupTwo.newTab();
waitForFocus(function () {
addTest(contentWindow, groupOne.id, groupTwo.id, originalTab);
});
let count = 0;
let onTabViewShown = function() {
if (count == 2) {
window.removeEventListener("tabviewshown", onTabViewShown, false);
addTest(contentWindow, groupOne.id, groupTwo.id, originalTab);
}
};
let onTabViewHidden = function() {
TabView.toggle();
if (++count == 2)
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
};
window.addEventListener("tabviewshown", onTabViewShown, false);
window.addEventListener("tabviewhidden", onTabViewHidden, false);
// open tab in group
groupOne.newTab();
}
function addTest(contentWindow, groupOneId, groupTwoId, originalTab) {
@ -74,24 +57,13 @@ function addTest(contentWindow, groupOneId, groupTwoId, originalTab) {
"The number of children in group one is decreased by 1");
is(groupTwo.getChildren().length, ++groupTwoTabItemCount,
"The number of children in group two is increased by 1");
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
groupTwo.closeAll();
// close undo group
let closeButton = groupTwo.$undoContainer.find(".close");
EventUtils.sendMouseEvent(
{ type: "click" }, closeButton[0], contentWindow);
};
groupTwo.addSubscriber("close", function onClose() {
groupTwo.removeSubscriber("close", onClose);
finish();
closeGroupItem(groupOne, function () {
closeGroupItem(groupTwo, function () hideTabView(finish));
});
window.addEventListener("tabviewhidden", onTabViewHidden, false);
gBrowser.selectedTab = originalTab;
}
groupTwo.addSubscriber("childAdded", endGame);
simulateDragDrop(tabItem.container, offsetX, offsetY, contentWindow);
}

View File

@ -1,15 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let timerId;
let newWin;
// ----------
function test() {
waitForExplicitFinish();
// launch tab view for the first time
newWindowWithTabView(function() {}, function(win) {
let panelSelected = false;
registerCleanupFunction(function () ok(panelSelected, "panel is selected"));
let onLoad = function (win) {
registerCleanupFunction(function () win.close());
newWin = win;
let onSelect = function(event) {
@ -21,27 +24,26 @@ function test() {
return;
deck.removeEventListener("select", onSelect, true);
whenTabViewIsShown(function() {
executeSoon(function() {
testMethodToHideAndShowTabView(function() {
newWin.document.getElementById("menu_tabview").doCommand();
}, function() {
testMethodToHideAndShowTabView(function() {
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, newWin);
}, finish);
});
});
}, win);
panelSelected = true;
};
let deck = win.document.getElementById("tab-view-deck");
deck.addEventListener("select", onSelect, true);
});
};
registerCleanupFunction(function () {
newWin.close();
});
let onShow = function (win) {
executeSoon(function() {
testMethodToHideAndShowTabView(function() {
newWin.document.getElementById("menu_tabview").doCommand();
}, function() {
testMethodToHideAndShowTabView(function() {
EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, newWin);
}, finish);
});
});
};
newWindowWithTabView(onShow, onLoad);
}
function testMethodToHideAndShowTabView(executeFunc, callback) {

View File

@ -55,6 +55,7 @@ browser.jar:
content/browser/tabview.css (content/tabview/tabview.css)
* content/browser/tabview.js (content/tabview/tabview.js)
content/browser/tabview.html (content/tabview/tabview.html)
content/browser/tabview-content.js (content/tabview/content.js)
* content/browser/urlbarBindings.xml (content/urlbarBindings.xml)
* content/browser/utilityOverlay.js (content/utilityOverlay.js)
* content/browser/web-panels.js (content/web-panels.js)