зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1369705 - avoid starting the search service or calling the search-one-offs XBL constructor before first paint, r=adw.
This commit is contained in:
Родитель
84bf3ddef3
Коммит
dc628a8ae3
|
@ -47,22 +47,22 @@ const startupPhases = {
|
||||||
// For the following phases of startup we have only a black list for now
|
// For the following phases of startup we have only a black list for now
|
||||||
|
|
||||||
// We are at this phase after creating the first browser window (ie. after final-ui-startup).
|
// We are at this phase after creating the first browser window (ie. after final-ui-startup).
|
||||||
"before opening first browser window": {blacklist: {
|
"before opening first browser window": {},
|
||||||
|
|
||||||
|
// We reach this phase right after showing the first browser window.
|
||||||
|
// This means that anything already loaded at this point has been loaded
|
||||||
|
// before first paint and delayed it.
|
||||||
|
"before first paint": {blacklist: {
|
||||||
components: new Set([
|
components: new Set([
|
||||||
"nsSearchService.js",
|
"nsSearchService.js",
|
||||||
])
|
])
|
||||||
}},
|
}},
|
||||||
|
|
||||||
// We reach this phase right after showing the first browser window.
|
|
||||||
// This means that anything already loaded at this point has been loaded
|
|
||||||
// before first paint and delayed it.
|
|
||||||
"before first paint": {},
|
|
||||||
|
|
||||||
// We are at this phase once we are ready to handle user events.
|
// We are at this phase once we are ready to handle user events.
|
||||||
// Anything loaded at this phase or before gets in the way of the user
|
// Anything loaded at this phase or before gets in the way of the user
|
||||||
// interacting with the first browser window.
|
// interacting with the first browser window.
|
||||||
"before handling user events": {},
|
"before handling user events": {},
|
||||||
}
|
};
|
||||||
|
|
||||||
function test() {
|
function test() {
|
||||||
if (!AppConstants.NIGHTLY_BUILD && !AppConstants.DEBUG) {
|
if (!AppConstants.NIGHTLY_BUILD && !AppConstants.DEBUG) {
|
||||||
|
|
|
@ -121,7 +121,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
cxmenu.insertBefore(pasteAndGo, insertLocation.nextSibling);
|
cxmenu.insertBefore(pasteAndGo, insertLocation.nextSibling);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._enableOrDisableOneOffSearches();
|
this.popup.addEventListener("popupshowing", () => {
|
||||||
|
this._enableOrDisableOneOffSearches();
|
||||||
|
}, {capturing: true, once: true});
|
||||||
|
|
||||||
// The autocomplete controller uses heuristic on some internal caches
|
// The autocomplete controller uses heuristic on some internal caches
|
||||||
// to handle cases like backspace, autofill or repeated searches.
|
// to handle cases like backspace, autofill or repeated searches.
|
||||||
|
|
|
@ -70,6 +70,8 @@
|
||||||
|
|
||||||
<implementation implements="nsIObserver">
|
<implementation implements="nsIObserver">
|
||||||
<constructor><![CDATA[
|
<constructor><![CDATA[
|
||||||
|
this._textbox.placeholder = this._stringBundle.getString("searchPlaceholder");
|
||||||
|
|
||||||
if (this.parentNode.parentNode.localName == "toolbarpaletteitem")
|
if (this.parentNode.parentNode.localName == "toolbarpaletteitem")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -77,27 +79,36 @@
|
||||||
|
|
||||||
this._initialized = true;
|
this._initialized = true;
|
||||||
|
|
||||||
Services.search.init(aStatus => {
|
(window.delayedStartupPromise || Promise.resolve()).then(() => {
|
||||||
// Bail out if the binding's been destroyed
|
window.requestIdleCallback(() => {
|
||||||
if (!this._initialized)
|
Services.search.init(aStatus => {
|
||||||
return;
|
// Bail out if the binding's been destroyed
|
||||||
|
if (!this._initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
if (Components.isSuccessCode(aStatus)) {
|
if (Components.isSuccessCode(aStatus)) {
|
||||||
// Refresh the display (updating icon, etc)
|
// Refresh the display (updating icon, etc)
|
||||||
this.updateDisplay();
|
this.updateDisplay();
|
||||||
BrowserSearch.updateOpenSearchBadge();
|
BrowserSearch.updateOpenSearchBadge();
|
||||||
} else {
|
} else {
|
||||||
Components.utils.reportError("Cannot initialize search service, bailing out: " + aStatus);
|
Components.utils.reportError("Cannot initialize search service, bailing out: " + aStatus);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Some accessibility tests create their own <searchbar> that doesn't
|
// Wait until the popupshowing event to avoid forcing immediate
|
||||||
// use the popup binding below, so null-check oneOffButtons.
|
// attachment of the search-one-offs binding.
|
||||||
if (this.textbox.popup.oneOffButtons) {
|
this.textbox.popup.addEventListener("popupshowing", () => {
|
||||||
this.textbox.popup.oneOffButtons.telemetryOrigin = "searchbar";
|
let oneOffButtons = this.textbox.popup.oneOffButtons;
|
||||||
this.textbox.popup.oneOffButtons.popup = this.textbox.popup;
|
// Some accessibility tests create their own <searchbar> that doesn't
|
||||||
this.textbox.popup.oneOffButtons.textbox = this.textbox;
|
// use the popup binding below, so null-check oneOffButtons.
|
||||||
}
|
if (oneOffButtons) {
|
||||||
|
oneOffButtons.telemetryOrigin = "searchbar";
|
||||||
|
oneOffButtons.popup = this.textbox.popup;
|
||||||
|
oneOffButtons.textbox = this.textbox;
|
||||||
|
}
|
||||||
|
}, {capturing: true, once: true});
|
||||||
]]></constructor>
|
]]></constructor>
|
||||||
|
|
||||||
<destructor><![CDATA[
|
<destructor><![CDATA[
|
||||||
|
@ -286,8 +297,6 @@
|
||||||
|
|
||||||
var name = this.currentEngine.name;
|
var name = this.currentEngine.name;
|
||||||
var text = this._stringBundle.getFormattedString("searchtip", [name]);
|
var text = this._stringBundle.getFormattedString("searchtip", [name]);
|
||||||
|
|
||||||
this._textbox.placeholder = this._stringBundle.getString("searchPlaceholder");
|
|
||||||
this._textbox.label = text;
|
this._textbox.label = text;
|
||||||
this._textbox.tooltipText = text;
|
this._textbox.tooltipText = text;
|
||||||
]]></body>
|
]]></body>
|
||||||
|
@ -567,108 +576,115 @@
|
||||||
|
|
||||||
<binding id="searchbar-textbox"
|
<binding id="searchbar-textbox"
|
||||||
extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
|
extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
|
||||||
<implementation implements="nsIObserver">
|
<implementation>
|
||||||
<constructor><![CDATA[
|
<constructor><![CDATA[
|
||||||
const kXULNS =
|
|
||||||
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|
||||||
|
|
||||||
if (document.getBindingParent(this).parentNode.parentNode.localName ==
|
if (document.getBindingParent(this).parentNode.parentNode.localName ==
|
||||||
"toolbarpaletteitem")
|
"toolbarpaletteitem")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Initialize fields
|
|
||||||
this._stringBundle = document.getBindingParent(this)._stringBundle;
|
|
||||||
this._suggestEnabled =
|
|
||||||
Services.prefs.getBoolPref("browser.search.suggest.enabled");
|
|
||||||
|
|
||||||
if (Services.prefs.getBoolPref("browser.urlbar.clickSelectsAll"))
|
if (Services.prefs.getBoolPref("browser.urlbar.clickSelectsAll"))
|
||||||
this.setAttribute("clickSelectsAll", true);
|
this.setAttribute("clickSelectsAll", true);
|
||||||
|
|
||||||
// Add items to context menu and attach controller to handle them
|
|
||||||
var textBox = document.getAnonymousElementByAttribute(this,
|
var textBox = document.getAnonymousElementByAttribute(this,
|
||||||
"anonid", "textbox-input-box");
|
"anonid", "textbox-input-box");
|
||||||
var cxmenu = document.getAnonymousElementByAttribute(textBox,
|
var cxmenu = document.getAnonymousElementByAttribute(textBox,
|
||||||
"anonid", "input-box-contextmenu");
|
"anonid", "input-box-contextmenu");
|
||||||
var pasteAndSearch;
|
cxmenu.addEventListener("popupshowing",
|
||||||
cxmenu.addEventListener("popupshowing", function() {
|
() => { this.initContextMenu(cxmenu); },
|
||||||
BrowserSearch.searchBar._textbox.closePopup();
|
{capturing: true, once: true});
|
||||||
if (!pasteAndSearch)
|
|
||||||
return;
|
|
||||||
var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
|
|
||||||
var enabled = controller.isCommandEnabled("cmd_paste");
|
|
||||||
if (enabled)
|
|
||||||
pasteAndSearch.removeAttribute("disabled");
|
|
||||||
else
|
|
||||||
pasteAndSearch.setAttribute("disabled", "true");
|
|
||||||
});
|
|
||||||
|
|
||||||
var element, label, akey;
|
|
||||||
|
|
||||||
element = document.createElementNS(kXULNS, "menuseparator");
|
|
||||||
cxmenu.appendChild(element);
|
|
||||||
|
|
||||||
this.setAttribute("aria-owns", this.popup.id);
|
this.setAttribute("aria-owns", this.popup.id);
|
||||||
|
|
||||||
var insertLocation = cxmenu.firstChild;
|
|
||||||
while (insertLocation.nextSibling &&
|
|
||||||
insertLocation.getAttribute("cmd") != "cmd_paste")
|
|
||||||
insertLocation = insertLocation.nextSibling;
|
|
||||||
if (insertLocation) {
|
|
||||||
element = document.createElementNS(kXULNS, "menuitem");
|
|
||||||
label = this._stringBundle.getString("cmd_pasteAndSearch");
|
|
||||||
element.setAttribute("label", label);
|
|
||||||
element.setAttribute("anonid", "paste-and-search");
|
|
||||||
element.setAttribute("oncommand", "BrowserSearch.pasteAndSearch(event)");
|
|
||||||
cxmenu.insertBefore(element, insertLocation.nextSibling);
|
|
||||||
pasteAndSearch = element;
|
|
||||||
}
|
|
||||||
|
|
||||||
element = document.createElementNS(kXULNS, "menuitem");
|
|
||||||
label = this._stringBundle.getString("cmd_clearHistory");
|
|
||||||
akey = this._stringBundle.getString("cmd_clearHistory_accesskey");
|
|
||||||
element.setAttribute("label", label);
|
|
||||||
element.setAttribute("accesskey", akey);
|
|
||||||
element.setAttribute("cmd", "cmd_clearhistory");
|
|
||||||
cxmenu.appendChild(element);
|
|
||||||
|
|
||||||
element = document.createElementNS(kXULNS, "menuitem");
|
|
||||||
label = this._stringBundle.getString("cmd_showSuggestions");
|
|
||||||
akey = this._stringBundle.getString("cmd_showSuggestions_accesskey");
|
|
||||||
element.setAttribute("anonid", "toggle-suggest-item");
|
|
||||||
element.setAttribute("label", label);
|
|
||||||
element.setAttribute("accesskey", akey);
|
|
||||||
element.setAttribute("cmd", "cmd_togglesuggest");
|
|
||||||
element.setAttribute("type", "checkbox");
|
|
||||||
element.setAttribute("checked", this._suggestEnabled);
|
|
||||||
element.setAttribute("autocheck", "false");
|
|
||||||
this._suggestMenuItem = element;
|
|
||||||
cxmenu.appendChild(element);
|
|
||||||
|
|
||||||
this.addEventListener("keypress", aEvent => {
|
|
||||||
if (navigator.platform.startsWith("Mac") && aEvent.keyCode == KeyEvent.VK_F4)
|
|
||||||
this.openSearch()
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
this.controllers.appendController(this.searchbarController);
|
|
||||||
document.getBindingParent(this)._textboxInitialized = true;
|
document.getBindingParent(this)._textboxInitialized = true;
|
||||||
|
|
||||||
// Add observer for suggest preference
|
|
||||||
Services.prefs.addObserver("browser.search.suggest.enabled", this);
|
|
||||||
]]></constructor>
|
]]></constructor>
|
||||||
|
|
||||||
<destructor><![CDATA[
|
<destructor><![CDATA[
|
||||||
Services.prefs.removeObserver("browser.search.suggest.enabled", this);
|
// If the context menu has never been opened, there won't be anything
|
||||||
|
// to remove here.
|
||||||
// Because XBL and the customize toolbar code interacts poorly,
|
// Also, XBL and the customize toolbar code sometimes interact poorly.
|
||||||
// there may not be anything to remove here
|
|
||||||
try {
|
try {
|
||||||
this.controllers.removeController(this.searchbarController);
|
this.controllers.removeController(this.searchbarController);
|
||||||
} catch (ex) { }
|
} catch (ex) { }
|
||||||
]]></destructor>
|
]]></destructor>
|
||||||
|
|
||||||
<field name="_stringBundle"/>
|
// Add items to context menu and attach controller to handle them the
|
||||||
<field name="_suggestMenuItem"/>
|
// first time the context menu is opened.
|
||||||
<field name="_suggestEnabled"/>
|
<method name="initContextMenu">
|
||||||
|
<parameter name="aMenu"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
const kXULNS =
|
||||||
|
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||||
|
let stringBundle = document.getBindingParent(this)._stringBundle;
|
||||||
|
|
||||||
|
let pasteAndSearch, suggestMenuItem;
|
||||||
|
let element, label, akey;
|
||||||
|
|
||||||
|
element = document.createElementNS(kXULNS, "menuseparator");
|
||||||
|
aMenu.appendChild(element);
|
||||||
|
|
||||||
|
let insertLocation = aMenu.firstChild;
|
||||||
|
while (insertLocation.nextSibling &&
|
||||||
|
insertLocation.getAttribute("cmd") != "cmd_paste")
|
||||||
|
insertLocation = insertLocation.nextSibling;
|
||||||
|
if (insertLocation) {
|
||||||
|
element = document.createElementNS(kXULNS, "menuitem");
|
||||||
|
label = stringBundle.getString("cmd_pasteAndSearch");
|
||||||
|
element.setAttribute("label", label);
|
||||||
|
element.setAttribute("anonid", "paste-and-search");
|
||||||
|
element.setAttribute("oncommand", "BrowserSearch.pasteAndSearch(event)");
|
||||||
|
aMenu.insertBefore(element, insertLocation.nextSibling);
|
||||||
|
pasteAndSearch = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
element = document.createElementNS(kXULNS, "menuitem");
|
||||||
|
label = stringBundle.getString("cmd_clearHistory");
|
||||||
|
akey = stringBundle.getString("cmd_clearHistory_accesskey");
|
||||||
|
element.setAttribute("label", label);
|
||||||
|
element.setAttribute("accesskey", akey);
|
||||||
|
element.setAttribute("cmd", "cmd_clearhistory");
|
||||||
|
aMenu.appendChild(element);
|
||||||
|
|
||||||
|
element = document.createElementNS(kXULNS, "menuitem");
|
||||||
|
label = stringBundle.getString("cmd_showSuggestions");
|
||||||
|
akey = stringBundle.getString("cmd_showSuggestions_accesskey");
|
||||||
|
element.setAttribute("anonid", "toggle-suggest-item");
|
||||||
|
element.setAttribute("label", label);
|
||||||
|
element.setAttribute("accesskey", akey);
|
||||||
|
element.setAttribute("cmd", "cmd_togglesuggest");
|
||||||
|
element.setAttribute("type", "checkbox");
|
||||||
|
element.setAttribute("autocheck", "false");
|
||||||
|
suggestMenuItem = element;
|
||||||
|
aMenu.appendChild(element);
|
||||||
|
|
||||||
|
if (AppConstants.platform == "macosx") {
|
||||||
|
this.addEventListener("keypress", aEvent => {
|
||||||
|
if (aEvent.keyCode == KeyEvent.DOM_VK_F4)
|
||||||
|
this.openSearch()
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.controllers.appendController(this.searchbarController);
|
||||||
|
|
||||||
|
let onpopupshowing = function() {
|
||||||
|
BrowserSearch.searchBar._textbox.closePopup();
|
||||||
|
if (suggestMenuItem) {
|
||||||
|
let enabled =
|
||||||
|
Services.prefs.getBoolPref("browser.search.suggest.enabled");
|
||||||
|
suggestMenuItem.setAttribute("checked", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pasteAndSearch)
|
||||||
|
return;
|
||||||
|
let controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
|
||||||
|
let enabled = controller.isCommandEnabled("cmd_paste");
|
||||||
|
if (enabled)
|
||||||
|
pasteAndSearch.removeAttribute("disabled");
|
||||||
|
else
|
||||||
|
pasteAndSearch.setAttribute("disabled", "true");
|
||||||
|
};
|
||||||
|
aMenu.addEventListener("popupshowing", onpopupshowing);
|
||||||
|
onpopupshowing();
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
This overrides the searchParam property in autocomplete.xml. We're
|
This overrides the searchParam property in autocomplete.xml. We're
|
||||||
|
@ -749,19 +765,6 @@
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
<method name="observe">
|
|
||||||
<parameter name="aSubject"/>
|
|
||||||
<parameter name="aTopic"/>
|
|
||||||
<parameter name="aData"/>
|
|
||||||
<body><![CDATA[
|
|
||||||
if (aTopic == "nsPref:changed") {
|
|
||||||
this._suggestEnabled =
|
|
||||||
Services.prefs.getBoolPref("browser.search.suggest.enabled");
|
|
||||||
this._suggestMenuItem.setAttribute("checked", this._suggestEnabled);
|
|
||||||
}
|
|
||||||
]]></body>
|
|
||||||
</method>
|
|
||||||
|
|
||||||
<method name="openSearch">
|
<method name="openSearch">
|
||||||
<body>
|
<body>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
|
@ -863,10 +866,10 @@
|
||||||
this._self.value = "";
|
this._self.value = "";
|
||||||
break;
|
break;
|
||||||
case "cmd_togglesuggest":
|
case "cmd_togglesuggest":
|
||||||
// The pref observer will update _suggestEnabled and the menu
|
let enabled =
|
||||||
// checkmark.
|
Services.prefs.getBoolPref("browser.search.suggest.enabled");
|
||||||
Services.prefs.setBoolPref("browser.search.suggest.enabled",
|
Services.prefs.setBoolPref("browser.search.suggest.enabled",
|
||||||
!this._self._suggestEnabled);
|
!enabled);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// do nothing with unrecognized command
|
// do nothing with unrecognized command
|
||||||
|
|
|
@ -232,6 +232,14 @@ add_task(async function testAutocomplete() {
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(async function testClearHistory() {
|
add_task(async function testClearHistory() {
|
||||||
|
// Open the textbox context menu to trigger controller attachment.
|
||||||
|
let textbox = searchBar.textbox;
|
||||||
|
let popupShownPromise = BrowserTestUtils.waitForEvent(textbox, "popupshown");
|
||||||
|
EventUtils.synthesizeMouseAtCenter(textbox, { type: "contextmenu", button: 2 });
|
||||||
|
await popupShownPromise;
|
||||||
|
// Close the context menu.
|
||||||
|
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||||
|
|
||||||
let controller = searchBar.textbox.controllers.getControllerForCommand("cmd_clearhistory")
|
let controller = searchBar.textbox.controllers.getControllerForCommand("cmd_clearhistory")
|
||||||
ok(controller.isCommandEnabled("cmd_clearhistory"), "Clear history command enabled");
|
ok(controller.isCommandEnabled("cmd_clearhistory"), "Clear history command enabled");
|
||||||
controller.doCommand("cmd_clearhistory");
|
controller.doCommand("cmd_clearhistory");
|
||||||
|
|
|
@ -6,6 +6,12 @@ const {classes: Cc, utils: Cu, interfaces: Ci} = Components;
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||||
|
|
||||||
|
let firstPaintNotification = "widget-first-paint";
|
||||||
|
// widget-first-paint fires much later than expected on Linux.
|
||||||
|
if (AppConstants.platform == "linux")
|
||||||
|
firstPaintNotification = "xul-window-visible";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The startupRecorder component observes notifications at various stages of
|
* The startupRecorder component observes notifications at various stages of
|
||||||
|
@ -42,7 +48,7 @@ startupRecorder.prototype = {
|
||||||
let topics = [
|
let topics = [
|
||||||
"profile-do-change", // This catches stuff loaded during app-startup
|
"profile-do-change", // This catches stuff loaded during app-startup
|
||||||
"toplevel-window-ready", // Catches stuff from final-ui-startup
|
"toplevel-window-ready", // Catches stuff from final-ui-startup
|
||||||
"widget-first-paint",
|
firstPaintNotification,
|
||||||
"sessionstore-windows-restored",
|
"sessionstore-windows-restored",
|
||||||
];
|
];
|
||||||
for (let t of topics)
|
for (let t of topics)
|
||||||
|
@ -61,8 +67,8 @@ startupRecorder.prototype = {
|
||||||
const topicsToNames = {
|
const topicsToNames = {
|
||||||
"profile-do-change": "before profile selection",
|
"profile-do-change": "before profile selection",
|
||||||
"toplevel-window-ready": "before opening first browser window",
|
"toplevel-window-ready": "before opening first browser window",
|
||||||
"widget-first-paint": "before first paint",
|
|
||||||
};
|
};
|
||||||
|
topicsToNames[firstPaintNotification] = "before first paint";
|
||||||
this.record(topicsToNames[topic]);
|
this.record(topicsToNames[topic]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче