Bug 587276 - KeyEvent in TabCandy window affect browser [r=ian]

--HG--
extra : rebase_source : 9076dbf84083b547498e997fa3ceaa9d7fa04b7e
This commit is contained in:
Raymond Lee 2011-03-29 10:51:26 +08:00
Родитель ac8de9ed2a
Коммит e3d5ccff9c
13 изменённых файлов: 337 добавлений и 87 удалений

Просмотреть файл

@ -80,7 +80,7 @@
<command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
<command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
<command id="cmd_find"
oncommand="if (TabView.isVisible()) TabView.enableSearch(event); else gFindBar.onFindCommand();"
oncommand="gFindBar.onFindCommand();"
observes="isImage"/>
<command id="cmd_findAgain"
oncommand="gFindBar.onFindAgainCommand(false);"

Просмотреть файл

@ -262,12 +262,6 @@ let TabView = {
}
},
// ----------
enableSearch: function TabView_enableSearch(event) {
if (this._window)
this._window.UI.enableSearch(event);
},
// ----------
// Adds new key commands to the browser, for invoking the Tab Candy UI
// and for switching between groups of tabs when outside of the Tab Candy UI.

Просмотреть файл

@ -343,8 +343,7 @@ SearchEventHandlerClass.prototype = {
// else to be hidden, and to have everything have the appropriate
// event handlers;
init: function () {
var self = this;
iQ("#searchbox")[0].focus();
let self = this;
iQ("#search").hide();
iQ("#searchshade").hide().click(function(event) {
if ( event.target.id != "searchbox")
@ -390,6 +389,13 @@ SearchEventHandlerClass.prototype = {
if (event.target.nodeName == "INPUT")
return;
// / is used to activate the search feature so the key shouldn't be entered
// into the search box.
if (event.keyCode == KeyEvent.DOM_VK_SLASH) {
event.stopPropagation();
event.preventDefault();
}
this.switchToInMode();
this.initiatedBy = "keydown";
ensureSearchShown(true);
@ -526,7 +532,7 @@ function createSearchTabMacher() {
return new TabMatcher(iQ("#searchbox").val());
}
function hideSearch(event){
function hideSearch(event) {
iQ("#searchbox").val("");
iQ("#searchshade").hide();
iQ("#search").hide();
@ -540,7 +546,12 @@ function hideSearch(event){
performSearch();
SearchEventHandler.switchToBeforeMode();
if (event){
if (event) {
// when hiding the search mode, we need to prevent the keypress handler
// in UI__setTabViewFrameKeyHandlers to handle the key press again. e.g. Esc
// which is already handled by the key down in this class.
if (event.type == "keydown")
UI.ignoreKeypressForSearch = true;
event.preventDefault();
event.stopPropagation();
}

Просмотреть файл

@ -132,6 +132,14 @@ let UI = {
// windows is about to close.
isDOMWindowClosing: false,
// Variable: _browserKeys
// Used to keep track of allowed browser keys.
_browserKeys: null,
// Variable: ignoreKeypressForSearch
// Used to prevent keypress being handled after quitting search mode.
ignoreKeypressForSearch: false,
// ----------
// Function: toString
// Prints [UI] for debug use
@ -931,33 +939,116 @@ let UI = {
return cl;
},
// ----------
// Function: _setupBrowserKeys
// Sets up the allowed browser keys using key elements.
_setupBrowserKeys: function UI__setupKeyWhiteList() {
let keys = {};
[
#ifdef XP_UNIX
"quitApplication",
#endif
#ifdef XP_MACOSX
"preferencesCmdMac", "minimizeWindow",
#endif
"newNavigator", "newNavigatorTab", "find"
].forEach(function(key) {
let element = gWindow.document.getElementById("key_" + key);
keys[key] = element.getAttribute("key").toLocaleLowerCase().charCodeAt(0);
});
// for key combinations with shift key, the charCode of upper case letters
// are different to the lower case ones so need to handle them differently.
["closeWindow", "tabview", "undoCloseTab", "undoCloseWindow",
"privatebrowsing"].forEach(function(key) {
let element = gWindow.document.getElementById("key_" + key);
keys[key] = element.getAttribute("key").toLocaleUpperCase().charCodeAt(0);
});
delete this._browserKeys;
this._browserKeys = keys;
},
// ----------
// Function: _setTabViewFrameKeyHandlers
// Sets up the key handlers for navigating between tabs within the TabView UI.
_setTabViewFrameKeyHandlers: function UI__setTabViewFrameKeyHandlers() {
var self = this;
let self = this;
this._setupBrowserKeys();
iQ(window).keyup(function(event) {
if (!event.metaKey)
if (!event.metaKey)
Keys.meta = false;
});
iQ(window).keydown(function(event) {
if (event.metaKey)
iQ(window).keypress(function(event) {
if (event.metaKey)
Keys.meta = true;
if ((iQ(":focus").length > 0 && iQ(":focus")[0].nodeName == "INPUT") ||
isSearchEnabled())
function processBrowserKeys(evt) {
#ifdef XP_MACOSX
if (evt.metaKey) {
#else
if (evt.ctrlKey) {
#endif
let preventDefault = true;
if (evt.shiftKey) {
switch (evt.charCode) {
case self._browserKeys.privatebrowsing:
case self._browserKeys.undoCloseTab:
case self._browserKeys.undoCloseWindow:
case self._browserKeys.closeWindow:
preventDefault = false;
break;
case self._browserKeys.tabview:
self.exit();
break;
}
} else {
switch (evt.charCode) {
case self._browserKeys.find:
self.enableSearch();
break;
case self._browserKeys.newNavigator:
case self._browserKeys.newNavigatorTab:
preventDefault = false;
break;
#ifdef XP_UNIX
case self._browserKeys.quitApplication:
preventDefault = false;
break;
#endif
#ifdef XP_MACOSX
case self._browserKeys.preferencesCmdMac:
case self._browserKeys.minimizeWindow:
preventDefault = false;
break;
#endif
}
}
if (preventDefault) {
evt.stopPropagation();
evt.preventDefault();
}
}
}
if ((iQ(":focus").length > 0 && iQ(":focus")[0].nodeName == "INPUT") ||
isSearchEnabled() || self.ignoreKeypressForSearch) {
self.ignoreKeypressForSearch = false;
processBrowserKeys(event);
return;
}
function getClosestTabBy(norm) {
if (!self.getActiveTab())
return null;
var centers =
let centers =
[[item.bounds.center(), item]
for each(item in TabItems.getItems()) if (!item.parent || !item.parent.hidden)];
var myCenter = self.getActiveTab().bounds.center();
var matches = centers
let myCenter = self.getActiveTab().bounds.center();
let matches = centers
.filter(function(item){return norm(item[0], myCenter)})
.sort(function(a,b){
return myCenter.distance(a[0]) - myCenter.distance(b[0]);
@ -967,7 +1058,9 @@ let UI = {
return null;
}
var norm = null;
let preventDefault = true;
let activeTab;
let norm = null;
switch (event.keyCode) {
case KeyEvent.DOM_VK_RIGHT:
norm = function(a, me){return a.x > me.x};
@ -990,59 +1083,54 @@ let UI = {
nextTab = nextTab.parent.getChild(0);
self.setActiveTab(nextTab);
}
event.stopPropagation();
event.preventDefault();
} else if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) {
let activeGroupItem = GroupItems.getActiveGroupItem();
if (activeGroupItem && activeGroupItem.expanded)
activeGroupItem.collapse();
else
self.exit();
} else {
switch(event.keyCode) {
case KeyEvent.DOM_VK_ESCAPE:
let activeGroupItem = GroupItems.getActiveGroupItem();
if (activeGroupItem && activeGroupItem.expanded)
activeGroupItem.collapse();
else
self.exit();
break;
case KeyEvent.DOM_VK_RETURN:
case KeyEvent.DOM_VK_ENTER:
activeTab = self.getActiveTab();
if (activeTab)
activeTab.zoomIn();
break;
case KeyEvent.DOM_VK_TAB:
// tab/shift + tab to go to the next tab.
activeTab = self.getActiveTab();
if (activeTab) {
let tabItems = (activeTab.parent ? activeTab.parent.getChildren() :
[activeTab]);
let length = tabItems.length;
let currentIndex = tabItems.indexOf(activeTab);
event.stopPropagation();
event.preventDefault();
} else if (event.keyCode == KeyEvent.DOM_VK_RETURN ||
event.keyCode == KeyEvent.DOM_VK_ENTER) {
let activeTab = self.getActiveTab();
if (activeTab)
activeTab.zoomIn();
event.stopPropagation();
event.preventDefault();
} else if (event.keyCode == KeyEvent.DOM_VK_TAB) {
// tab/shift + tab to go to the next tab.
var activeTab = self.getActiveTab();
if (activeTab) {
var tabItems = (activeTab.parent ? activeTab.parent.getChildren() :
[activeTab]);
var length = tabItems.length;
var currentIndex = tabItems.indexOf(activeTab);
if (length > 1) {
if (event.shiftKey) {
if (currentIndex == 0)
newIndex = (length - 1);
else
newIndex = (currentIndex - 1);
} else {
if (currentIndex == (length - 1))
newIndex = 0;
else
newIndex = (currentIndex + 1);
if (length > 1) {
if (event.shiftKey) {
if (currentIndex == 0)
newIndex = (length - 1);
else
newIndex = (currentIndex - 1);
} else {
if (currentIndex == (length - 1))
newIndex = 0;
else
newIndex = (currentIndex + 1);
}
self.setActiveTab(tabItems[newIndex]);
}
}
self.setActiveTab(tabItems[newIndex]);
}
break;
default:
processBrowserKeys(event);
preventDefault = false;
}
if (preventDefault) {
event.stopPropagation();
event.preventDefault();
}
event.stopPropagation();
event.preventDefault();
} else if (event.keyCode == KeyEvent.DOM_VK_SLASH) {
// the / event handler for find bar is defined in the findbar.xml
// binding. To keep things in its own module, we handle our slash here.
self.enableSearch(event);
} else if (event.keyCode == KeyEvent.DOM_VK_BACK_SPACE) {
// prevent navigating backward in the selected tab's history
event.stopPropagation();
event.preventDefault();
}
});
},
@ -1050,17 +1138,10 @@ let UI = {
// ----------
// Function: enableSearch
// Enables the search feature.
// Parameters:
// event - the event triggers this action.
enableSearch: function UI_enableSearch(event) {
enableSearch: function UI_enableSearch() {
if (!isSearchEnabled()) {
ensureSearchShown();
SearchEventHandler.switchToInMode();
if (event) {
event.stopPropagation();
event.preventDefault();
}
}
},

Просмотреть файл

@ -50,6 +50,7 @@ _BROWSER_FILES = \
browser_tabview_bug586553.js \
browser_tabview_bug587043.js \
browser_tabview_bug587231.js \
browser_tabview_bug587276.js \
browser_tabview_bug587351.js \
browser_tabview_bug587503.js \
browser_tabview_bug587990.js \

Просмотреть файл

@ -0,0 +1,102 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let contentWindow;
function test() {
waitForExplicitFinish();
showTabView(test1);
}
function test1() {
ok(TabView.isVisible(), "Tab View is visible");
contentWindow = document.getElementById("tab-view").contentWindow;
whenTabViewIsHidden(function() {
ok(!TabView.isVisible(), "Tab View is not visible");
showTabView(test2);
});
EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, contentWindow);
}
function test2() {
ok(TabView.isVisible(), "Tab View is visible");
whenSearchIsEnabled(function() {
ok(contentWindow.isSearchEnabled(), "The search is enabled")
whenSearchIsDisabled(test3);
hideSearch();
});
EventUtils.synthesizeKey("f", { accelKey: true }, contentWindow);
}
function test3() {
ok(!contentWindow.isSearchEnabled(), "The search is disabled")
is(gBrowser.tabs.length, 1, "There is one tab before cmd/ctrl + t is pressed");
EventUtils.synthesizeKey("t", { accelKey: true }, contentWindow);
is(gBrowser.tabs.length, 2, "There are two tabs after cmd/ctrl + t is pressed");
gBrowser.tabs[0].linkedBrowser.loadURI("about:robots");
gBrowser.tabs[1].linkedBrowser.loadURI("http://example.com/");
afterAllTabsLoaded(function () {
showTabView(test4);
});
}
function test4() {
is(gBrowser.tabs.length, 2, "There are two tabs");
let onTabClose = function() {
gBrowser.tabContainer.removeEventListener("TabClose", onTabClose, true);
executeSoon(function() {
is(gBrowser.tabs.length, 1, "There is one tab after removing one");
EventUtils.synthesizeKey("T", { accelKey: true, shiftKey: true }, contentWindow);
is(gBrowser.tabs.length, 2, "There are two tabs after restoring one");
gBrowser.tabs[0].linkedBrowser.loadURI("about:blank");
gBrowser.removeTab(gBrowser.tabs[1]);
test8();
});
};
gBrowser.tabContainer.addEventListener("TabClose", onTabClose, true);
gBrowser.removeTab(gBrowser.tabs[1]);
}
// below key combination shouldn't trigger actions in tabview UI
function test8() {
let newTab = gBrowser.loadOneTab("about:blank", { inBackground: true });
is(gBrowser.tabs.length, 2, "There are two tabs before cmd/ctrl + w is pressed");
EventUtils.synthesizeKey("w", { accelKey: true }, contentWindow);
is(gBrowser.tabs.length, 2, "There are two tabs after cmd/ctrl + w is pressed");
gBrowser.removeTab(newTab);
test9();
}
function test9() {
let zoomLevel = ZoomManager.zoom;
EventUtils.synthesizeKey("+", { accelKey: true }, contentWindow);
is(ZoomManager.zoom, zoomLevel, "The zoom level remains unchanged after cmd/ctrl + + is pressed");
EventUtils.synthesizeKey("-", { accelKey: true }, contentWindow);
is(ZoomManager.zoom, zoomLevel, "The zoom level remains unchanged after cmd/ctrl + - is pressed");
test10();
}
function test10() {
is(gBrowser.tabs.length, 1, "There is one tab before cmd/ctrl + shift + a is pressed");
// it would open about:addons on a new tab if it passes through the white list.
EventUtils.synthesizeKey("A", { accelKey: true, shiftKey: true }, contentWindow);
executeSoon(function() {
is(gBrowser.tabs.length, 1, "There is still one tab after cmd/ctrl + shift + a is pressed");
hideTabView(finish);
})
}

Просмотреть файл

@ -57,5 +57,5 @@ function toggleTabViewTest(contentWindow) {
}
contentWindow.addEventListener("tabviewhidden", onTabViewHidden, false);
// Use keyboard shortcut to toggle back to browser view
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true });
EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true });
}

Просмотреть файл

@ -28,7 +28,7 @@ function onTabViewWindowLoaded() {
// verify that the keyboard combo works (this is the crux of bug 595518)
// Prepare the key combo
window.addEventListener("tabviewshown", onTabViewShown, false);
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, contentWindow);
EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, contentWindow);
}
let onTabViewShown = function() {

Просмотреть файл

@ -33,8 +33,8 @@ function testOne(contentWindow) {
onSearchEnabledAndDisabled(contentWindow, function() {
testTwo(contentWindow);
});
// execute a find command (i.e. press cmd/ctrl F)
document.getElementById("cmd_find").doCommand();
// press cmd/ctrl F
EventUtils.synthesizeKey("f", { accelKey: true });
}
function testTwo(contentWindow) {

Просмотреть файл

@ -41,7 +41,7 @@ function onTabViewWindowLoaded() {
// the appropriate group would get selected when the key
// combination is pressed
executeSoon(function() {
EventUtils.synthesizeKey("e", {accelKey : true, shiftKey: true}, contentWindow);
EventUtils.synthesizeKey("E", {accelKey : true, shiftKey: true}, contentWindow);
});
});

Просмотреть файл

@ -78,8 +78,8 @@ function part2(win) {
finish();
}, false);
// show tabview
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, win);
EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, win);
// hide tabview
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, win);
EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, win);
})
}

Просмотреть файл

@ -74,7 +74,7 @@ function onTabViewHidden() {
if (tabViewShownCount == 1) {
document.getElementById("menu_tabview").doCommand();
} else if (tabViewShownCount == 2) {
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true });
EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true });
} else if (tabViewShownCount == 3) {
window.removeEventListener("tabviewshown", onTabViewShown, false);
window.removeEventListener("tabviewhidden", onTabViewHidden, false);

Просмотреть файл

@ -172,6 +172,67 @@ function whenTabViewIsShown(callback, win) {
}, false);
}
// ----------
function showSearch(callback, win) {
win = win || window;
let contentWindow = win.document.getElementById("tab-view").contentWindow;
if (contentWindow.isSearchEnabled()) {
callback();
return;
}
whenSearchIsEnabled(callback, win);
contentWindow.performSearch();
}
// ----------
function hideSearch(callback, win) {
win = win || window;
let contentWindow = win.document.getElementById("tab-view").contentWindow;
if (!contentWindow.isSearchEnabled()) {
callback();
return;
}
whenSearchIsDisabled(callback, win);
contentWindow.hideSearch();
}
// ----------
function whenSearchIsEnabled(callback, win) {
win = win || window;
let contentWindow = win.document.getElementById("tab-view").contentWindow;
if (contentWindow.isSearchEnabled()) {
callback();
return;
}
contentWindow.addEventListener("tabviewsearchenabled", function () {
contentWindow.removeEventListener("tabviewsearchenabled", arguments.callee, false);
callback();
}, false);
}
// ----------
function whenSearchIsDisabled(callback, win) {
win = win || window;
let contentWindow = win.document.getElementById("tab-view").contentWindow;
if (!contentWindow.isSearchEnabled()) {
callback();
return;
}
contentWindow.addEventListener("tabviewsearchdisabled", function () {
contentWindow.removeEventListener("tabviewsearchdisabled", arguments.callee, false);
callback();
}, false);
}
// ----------
function hideGroupItem(groupItem, callback) {
if (groupItem.hidden) {