author | Alex Bardas <abardas@mozilla.com> |
Thu, 14 Aug 2014 15:48:00 -0400 | |
changeset 199839 | 9390b8124699a0ae49349a206ecd48ee1af8e3bb |
parent 199838 | f9a519ff098f331b83683366d17788fad0dc6fb0 |
child 199840 | abd2919b124b47afd188ffe709517835811150a6 |
push id | 47750 |
push user | ryanvm@gmail.com |
push date | Fri, 15 Aug 2014 21:04:12 +0000 |
treeherder | mozilla-inbound@baea646f5a80 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | Gijs, adw |
bugs | 1041678 |
milestone | 34.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -18,16 +18,20 @@ XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu", "resource://gre/modules/CharsetMenu.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils", "resource://gre/modules/ShortcutUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch", + "resource:///modules/ContentSearch.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "AboutHome", + "resource:///modules/AboutHome.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "gDNSService", "@mozilla.org/network/dns-service;1", "nsIDNSService"); const nsIWebNavigation = Ci.nsIWebNavigation; var gLastBrowserCharset = null; var gPrevCharset = null; @@ -3090,18 +3094,27 @@ const BrowserSearch = { win = window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", "about:blank"); Services.obs.addObserver(observer, "browser-delayed-startup-finished", false); } return; } #endif let openSearchPageIfFieldIsNotActive = function(aSearchBar) { - if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) + let doc = gBrowser.selectedBrowser.contentDocument; + let url = doc.documentURI.toLowerCase(); + let mm = gBrowser.selectedBrowser.messageManager; + + if (url === "about:home") { + AboutHome.focusInput(mm); + } else if (url === "about:newtab") { + ContentSearch.focusInput(mm); + } else if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) { openUILinkIn("about:home", "current"); + } }; let searchBar = this.searchBar; let placement = CustomizableUI.getPlacementOfWidget("search-container"); let focusSearchBar = () => { searchBar = this.searchBar; searchBar.select(); openSearchPageIfFieldIsNotActive(searchBar);
--- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -101,16 +101,19 @@ let AboutHomeListener = { } }, receiveMessage: function(aMessage) { switch (aMessage.name) { case "AboutHome:Update": this.onUpdate(aMessage.data); break; + case "AboutHome:FocusInput": + this.onFocusInput(); + break; } }, onUpdate: function(aData) { let doc = content.document; if (doc.documentURI.toLowerCase() != "about:home") return; @@ -133,16 +136,17 @@ let AboutHomeListener = { if (doc.documentURI.toLowerCase() != "about:home" || doc.documentElement.hasAttribute("hasBrowserHandlers")) { return; } doc.documentElement.setAttribute("hasBrowserHandlers", "true"); let self = this; addMessageListener("AboutHome:Update", self); + addMessageListener("AboutHome:FocusInput", self); addEventListener("click", this.onClick, true); addEventListener("pagehide", function onPageHide(event) { if (event.target.defaultView.frameElement) return; removeMessageListener("AboutHome:Update", self); removeEventListener("click", self.onClick, true); removeEventListener("pagehide", onPageHide, true); if (event.target.documentElement) @@ -207,16 +211,20 @@ let AboutHomeListener = { sendAsyncMessage("AboutHome:Sync"); break; case "settings": sendAsyncMessage("AboutHome:Settings"); break; } }, + + onFocusInput: function () { + content.document.getElementById("searchText").focus(); + }, }; AboutHomeListener.init(this); // An event listener for custom "WebChannelMessageToChrome" events on pages addEventListener("WebChannelMessageToChrome", function (e) { // if target is window then we want the document principal, otherwise fallback to target itself. let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal;
--- a/browser/base/content/newtab/search.js +++ b/browser/base/content/newtab/search.js @@ -71,16 +71,20 @@ let gSearch = { onCurrentEngine: function (engineName) { if (this._initialStateReceived) { this._nodes.panel.hidePopup(); this._setCurrentEngine(engineName); } }, + onFocusInput: function () { + this._nodes.text.focus(); + }, + _nodeIDSuffixes: [ "form", "logo", "manage", "panel", "text", ],
--- a/browser/base/content/test/general/browser_aboutHome.js +++ b/browser/base/content/test/general/browser_aboutHome.js @@ -414,16 +414,32 @@ let gTests = [ // Empty the search input, causing the suggestions to be hidden. EventUtils.synthesizeKey("a", { accelKey: true }); EventUtils.synthesizeKey("VK_DELETE", {}); ok(table.hidden, "Search suggestion table hidden"); }); } }, +{ + desc: "Cmd+k should focus the search bar element", + setup: function () {}, + run: Task.async(function* () { + let doc = gBrowser.selectedTab.linkedBrowser.contentDocument; + let logo = doc.getElementById("brandLogo"); + let searchInput = doc.getElementById("searchText"); + + EventUtils.synthesizeMouseAtCenter(logo, {}); + isnot(searchInput, doc.activeElement, "Search input should not be the active element."); + + EventUtils.synthesizeKey("k", { accelKey: true }); + yield promiseWaitForCondition(() => doc.activeElement === searchInput); + is(searchInput, doc.activeElement, "Search input should be the active element."); + }) +}, ]; function test() { waitForExplicitFinish(); requestLongerTimeout(2); ignoreAllUncaughtExceptions();
--- a/browser/base/content/test/general/head.js +++ b/browser/base/content/test/general/head.js @@ -77,16 +77,22 @@ function waitForCondition(condition, nex if (conditionPassed) { moveOn(); } tries++; }, 100); var moveOn = function() { clearInterval(interval); nextTest(); }; } +function promiseWaitForCondition(aConditionFn) { + let deferred = Promise.defer(); + waitForCondition(aConditionFn, deferred.resolve, "Condition didn't pass."); + return deferred.promise; +} + function getTestPlugin(aName) { var pluginName = aName || "Test Plug-in"; var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); var tags = ph.getPluginTags(); // Find the test plugin for (var i = 0; i < tags.length; i++) { if (tags[i].name == pluginName)
--- a/browser/base/content/test/newtab/browser_newtab_search.js +++ b/browser/base/content/test/newtab/browser_newtab_search.js @@ -181,16 +181,26 @@ function runTests() { yield undefined; yield suggestionsPromise.then(TestRunner.next); // Empty the search input, causing the suggestions to be hidden. EventUtils.synthesizeKey("a", { accelKey: true }); EventUtils.synthesizeKey("VK_DELETE", {}); ok(table.hidden, "Search suggestion table hidden"); + // Focus a different element than the search input. + let btn = getContentDocument().getElementById("newtab-customize-button"); + yield promiseClick(btn).then(TestRunner.next); + + isnot(input, getContentDocument().activeElement, "Search input should not be focused"); + // Test that Ctrl/Cmd + K will focus the input field. + EventUtils.synthesizeKey("k", { accelKey: true }); + yield promiseSearchEvents(["FocusInput"]).then(TestRunner.next); + is(input, getContentDocument().activeElement, "Search input should be focused"); + // Done. Revert the current engine and remove the new engines. Services.search.currentEngine = oldCurrentEngine; yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next); let events = []; for (let engine of gNewEngines) { Services.search.removeEngine(engine); events.push("CurrentState");
--- a/browser/modules/AboutHome.jsm +++ b/browser/modules/AboutHome.jsm @@ -246,9 +246,18 @@ let AboutHome = { } else { let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager); mm.broadcastAsyncMessage("AboutHome:Update", data); } }).then(null, function onError(x) { Cu.reportError("Error in AboutHome.sendAboutHomeData: " + x); }); }, + + /** + * Focuses the search input in the page with the given message manager. + * @param messageManager + * The MessageManager object of the selected browser. + */ + focusInput: function (messageManager) { + messageManager.sendAsyncMessage("AboutHome:FocusInput"); + } };
--- a/browser/modules/ContentSearch.jsm +++ b/browser/modules/ContentSearch.jsm @@ -89,16 +89,27 @@ this.ContentSearch = { init: function () { Cc["@mozilla.org/globalmessagemanager;1"]. getService(Ci.nsIMessageListenerManager). addMessageListener(INBOUND_MESSAGE, this); Services.obs.addObserver(this, "browser-search-engine-modified", false); }, + /** + * Focuses the search input in the page with the given message manager. + * @param messageManager + * The MessageManager object of the selected browser. + */ + focusInput: function (messageManager) { + messageManager.sendAsyncMessage(OUTBOUND_MESSAGE, { + type: "FocusInput" + }); + }, + receiveMessage: function (msg) { // Add a temporary event handler that exists only while the message is in // the event queue. If the message's source docshell changes browsers in // the meantime, then we need to update msg.target. event.detail will be // the docshell's new parent <xul:browser> element. msg.handleEvent = event => { let browserData = this._suggestionMap.get(msg.target); if (browserData) {