--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -705,17 +705,17 @@
completeselectedindex="true"
shrinkdelay="250"
tabscrolling="true"
showcommentcolumn="true"
showimagecolumn="true"
enablehistory="true"
maxrows="10"
newlines="stripsurroundingwhitespace"
- ontextentered="this.handleCommand(param);"
+ ontextentered="this.handleCommand(...args);"
ontextreverted="return this.handleRevert();"
pageproxystate="invalid"
onfocus="document.getElementById('identity-box').style.MozUserFocus= 'normal'"
onblur="setTimeout(() => { document.getElementById('identity-box').style.MozUserFocus = ''; }, 0);">
<box id="notification-popup-box" hidden="true" align="center">
<image id="default-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.defaultNotificationAnchor.label;"/>
<image id="geo-notification-icon" class="notification-anchor-icon geo-icon" role="button"
--- a/browser/base/content/test/urlbar/browser_autocomplete_edit_completed.js
+++ b/browser/base/content/test/urlbar/browser_autocomplete_edit_completed.js
@@ -26,20 +26,23 @@ add_task(function*() {
let nextValue = gURLBar.controller.getFinalCompleteValueAt(nextIndex);
is(list.selectedIndex, nextIndex, "The next item is selected.");
is(gURLBar.value, nextValue, "The selected URL is completed.");
info("Press backspace");
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
yield promiseSearchComplete();
- let editedValue = gURLBar.value;
+ let editedValue = gURLBar.textValue;
is(list.selectedIndex, initialIndex, "The initial index is selected again.");
isnot(editedValue, nextValue, "The URL has changed.");
+ let docLoad = waitForDocLoadAndStopIt("http://" + editedValue);
+
info("Press return to load edited URL.");
EventUtils.synthesizeKey("VK_RETURN", {});
yield Promise.all([
promisePopupHidden(gURLBar.popup),
- waitForDocLoadAndStopIt("http://" + editedValue)]);
+ docLoad,
+ ]);
gBrowser.removeTab(gBrowser.selectedTab);
});
--- a/browser/base/content/test/urlbar/browser_bug1070778.js
+++ b/browser/base/content/test/urlbar/browser_bug1070778.js
@@ -38,17 +38,17 @@ add_task(function*() {
is_selected(1);
// Re-select keyword item
EventUtils.synthesizeKey("VK_UP", {});
is_selected(0);
EventUtils.synthesizeKey("b", {});
yield promiseSearchComplete();
- is(gURLBar.value, "keyword ab", "urlbar should have expected input");
+ is(gURLBar.textValue, "keyword ab", "urlbar should have expected input");
let result = gURLBar.popup.richlistbox.firstChild;
isnot(result, null, "Should have first item");
let uri = NetUtil.newURI(result.getAttribute("url"));
is(uri.spec, makeActionURI("keyword", {url: "http://example.com/?q=ab", input: "keyword ab"}).spec, "Expect correct url");
EventUtils.synthesizeKey("VK_ESCAPE", {});
yield promisePopupHidden(gURLBar.popup);
--- a/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar_perwindowpb.js
+++ b/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar_perwindowpb.js
@@ -65,17 +65,17 @@ function* runTest(aSourceWindow, aDestWi
}
let awaitTabSwitch;
if (aExpectSwitch) {
awaitTabSwitch = BrowserTestUtils.removeTab(testTab, {dontRemove: true})
}
// Execute the selected action.
- controller.handleEnter(true);
+ controller.handleEnter(true, null);
info("sent Enter command to the controller");
if (aExpectSwitch) {
// If we expect a tab switch then the current tab
// will be closed and we switch to the other tab.
yield awaitTabSwitch;
} else {
// If we don't expect a tab switch then wait for the tab to load.
--- a/browser/base/content/test/urlbar/browser_urlbarAutoFillTrimURLs.js
+++ b/browser/base/content/test/urlbar/browser_urlbarAutoFillTrimURLs.js
@@ -39,18 +39,18 @@ function continue_test() {
info(`Testing with input: ${aTyped}`);
gURLBar.inputField.value = aTyped.substr(0, aTyped.length - 1);
gURLBar.focus();
gURLBar.selectionStart = aTyped.length - 1;
gURLBar.selectionEnd = aTyped.length - 1;
EventUtils.synthesizeKey(aTyped.substr(-1), {});
waitForSearchComplete(function () {
- info(`Got value: ${gURLBar.value}`);
- is(gURLBar.value, aExpected, "Autofilled value is as expected");
+ info(`Got value: ${gURLBar.textValue}`);
+ is(gURLBar.textValue, aExpected, "Autofilled value is as expected");
aCallback();
});
}
test_autoFill("http://", "http://", function () {
test_autoFill("http://au", "http://autofilltrimurl.com/", function () {
test_autoFill("http://www.autofilltrimurl.com", "http://www.autofilltrimurl.com/", function () {
// Now ensure selecting from the popup correctly trims.
--- a/browser/base/content/test/urlbar/browser_urlbarOneOffs.js
+++ b/browser/base/content/test/urlbar/browser_urlbarOneOffs.js
@@ -178,17 +178,17 @@ add_task(function* oneOffClick() {
let resultsPromise = promiseSearchResultsLoaded();
EventUtils.synthesizeMouseAtCenter(oneOffs[0], {});
yield resultsPromise;
gBrowser.removeTab(gBrowser.selectedTab);
});
// Presses the Return key when a one-off is selected.
-add_task(function* oneOffClick() {
+add_task(function* oneOffReturn() {
gBrowser.selectedTab = gBrowser.addTab();
let typedValue = "foo";
yield promiseAutocompleteResultPopup(typedValue, window, true);
assertState(0, -1, typedValue);
// Tab to select the first one-off.
--- a/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions.js
+++ b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions.js
@@ -20,17 +20,20 @@ add_task(function* prepare() {
Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
});
});
add_task(function* clickSuggestion() {
gBrowser.selectedTab = gBrowser.addTab();
gURLBar.focus();
yield promiseAutocompleteResultPopup("foo");
- let [idx, suggestion] = yield promiseFirstSuggestion();
+ let [idx, suggestion, engineName] = yield promiseFirstSuggestion();
+ Assert.equal(engineName,
+ "browser_searchSuggestionEngine%20searchSuggestionEngine.xml",
+ "Expected suggestion engine");
let item = gURLBar.popup.richlistbox.getItemAtIndex(idx);
let loadPromise = promiseTabLoaded(gBrowser.selectedTab);
item.click();
yield loadPromise;
let uri = Services.search.currentEngine.getSubmission(suggestion).uri;
Assert.ok(uri.equals(gBrowser.currentURI),
"The search results page should have loaded");
gBrowser.removeTab(gBrowser.selectedTab);
@@ -42,24 +45,24 @@ function getFirstSuggestion() {
let present = false;
for (let i = 0; i < matchCount; i++) {
let url = controller.getValueAt(i);
let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/);
if (mozActionMatch) {
let [, type, paramStr] = mozActionMatch;
let params = JSON.parse(paramStr);
if (type == "searchengine" && "searchSuggestion" in params) {
- return [i, params.searchSuggestion];
+ return [i, params.searchSuggestion, params.engineName];
}
}
}
return [-1, null];
}
function promiseFirstSuggestion() {
return new Promise(resolve => {
- let pair;
+ let tuple;
waitForCondition(() => {
- pair = getFirstSuggestion();
- return pair[0] >= 0;
- }, () => resolve(pair));
+ tuple = getFirstSuggestion();
+ return tuple[0] >= 0;
+ }, () => resolve(tuple));
});
}
--- a/browser/base/content/test/urlbar/browser_urlbarStop.js
+++ b/browser/base/content/test/urlbar/browser_urlbarStop.js
@@ -15,17 +15,16 @@ add_task(function* () {
gBrowser.selectedTab = gBrowser.addTab("about:blank");
is(gURLBar.textValue, "", "location bar is empty");
yield typeAndSubmitAndStop(badURL);
is(gURLBar.textValue, gURLBar.trimValue(badURL), "location bar reflects stopped page in an empty tab");
gBrowser.removeCurrentTab();
});
-function typeAndSubmitAndStop(url) {
- gBrowser.userTypedValue = url;
- URLBarSetURI();
+function* typeAndSubmitAndStop(url) {
+ yield promiseAutocompleteResultPopup(url, window, true);
is(gURLBar.textValue, gURLBar.trimValue(url), "location bar reflects loading page");
let promise = waitForDocLoadAndStopIt(url, gBrowser.selectedBrowser, false);
gURLBar.handleCommand();
- return promise;
+ yield promise;
}
--- a/browser/base/content/test/urlbar/browser_urlbar_autoFill_backspaced.js
+++ b/browser/base/content/test/urlbar/browser_urlbar_autoFill_backspaced.js
@@ -2,23 +2,23 @@
* confirm the remaining value.
*/
function* test_autocomplete(data) {
let {desc, typed, autofilled, modified, keys, action, onAutoFill} = data;
info(desc);
yield promiseAutocompleteResultPopup(typed);
- is(gURLBar.value, autofilled, "autofilled value is as expected");
+ is(gURLBar.textValue, autofilled, "autofilled value is as expected");
if (onAutoFill)
onAutoFill()
keys.forEach(key => EventUtils.synthesizeKey(key, {}));
- is(gURLBar.value, modified, "backspaced value is as expected");
+ is(gURLBar.textValue, modified, "backspaced value is as expected");
yield promiseSearchComplete();
ok(gURLBar.popup.richlistbox.children.length > 0, "Should get at least 1 result");
let result = gURLBar.popup.richlistbox.children[0];
let type = result.getAttribute("type");
let types = type.split(/\s+/);
ok(types.indexOf(action) >= 0, `The type attribute "${type}" includes the expected action "${action}"`);
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -330,195 +330,214 @@ file, You can obtain one at http://mozil
}
// tell widget to revert to last typed text only if the user
// was scrolling when they hit escape
return !isScrolling;
]]></body>
</method>
- <method name="handleCommand">
- <parameter name="aTriggeringEvent"/>
- <body><![CDATA[
- if (aTriggeringEvent instanceof MouseEvent && aTriggeringEvent.button == 2)
- return; // Do nothing for right clicks
-
- var url = this.value;
- var mayInheritPrincipal = false;
- var postData = null;
-
- let action = this._parseActionUrl(this._value);
- let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
+ <!--
+ This is ultimately called by the autocomplete controller as the result
+ of handleEnter when the Return key is pressed in the textbox. Since
+ onPopupClick also calls handleEnter, this is also called as a result in
+ that case.
- let matchLastLocationChange = true;
- if (action) {
- if (action.type == "switchtab") {
- url = action.params.url;
- if (this.hasAttribute("actiontype")) {
- this.handleRevert();
- let prevTab = gBrowser.selectedTab;
- if (switchToTabHavingURI(url) && isTabEmpty(prevTab)) {
- gBrowser.removeTab(prevTab);
- }
- return;
- }
- } else if (action.type == "remotetab") {
- url = action.params.url;
- } else if (action.type == "keyword") {
- url = action.params.url;
- } else if (action.type == "searchengine") {
- [url, postData] = this._parseAndRecordSearchEngineAction(action);
- } else if (action.type == "visiturl") {
- url = action.params.url;
- }
- continueOperation.call(this);
+ @param event
+ The event that triggered the command.
+ @param openUILinkWhere
+ Optional. The "where" to pass to openUILinkIn. This method
+ computes the appropriate "where" given the event, but you can
+ use this to override it.
+ @param openUILinkParams
+ Optional. The parameters to pass to openUILinkIn. As with
+ "where", this method computes the appropriate parameters, but
+ any parameters you supply here will override those.
+ -->
+ <method name="handleCommand">
+ <parameter name="event"/>
+ <parameter name="selectedPopupIndex"/>
+ <parameter name="openUILinkWhere"/>
+ <parameter name="openUILinkParams"/>
+ <body><![CDATA[
+ let isMouseEvent = event instanceof MouseEvent;
+ if (isMouseEvent && event.button == 2) {
+ // Do nothing for right clicks.
+ return;
}
- else {
- this._canonizeURL(aTriggeringEvent, response => {
- [url, postData, mayInheritPrincipal] = response;
- if (url) {
- matchLastLocationChange = (lastLocationChange ==
- gBrowser.selectedBrowser.lastLocationChange);
- continueOperation.call(this);
- }
- });
+
+ let where = "current";
+ // If the current tab is empty, ignore Alt+Enter (just reuse this tab)
+ let altEnter = !isMouseEvent &&
+ event &&
+ event.altKey &&
+ !isTabEmpty(gBrowser.selectedTab);
+ if (isMouseEvent || altEnter) {
+ // Use the standard UI link behaviors for clicks or Alt+Enter
+ where = isMouseEvent ? whereToOpenLink(event, false, false) : "tab";
}
- function continueOperation()
- {
- this.value = url;
- gBrowser.userTypedValue = url;
- if (gInitialPages.includes(url)) {
- gBrowser.selectedBrowser.initialPageLoadedFromURLBar = url;
- }
- try {
- addToUrlbarHistory(url);
- } catch (ex) {
- // Things may go wrong when adding url to session history,
- // but don't let that interfere with the loading of the url.
- Cu.reportError(ex);
- }
-
- let loadCurrent = () => {
- this._loadURL(aTriggeringEvent, url, "current", {
- disallowInheritPrincipal: !mayInheritPrincipal,
- postData: postData,
- });
- };
-
- // Focus the content area before triggering loads, since if the load
- // occurs in a new tab, we want focus to be restored to the content
- // area when the current tab is re-selected.
- gBrowser.selectedBrowser.focus();
-
- let isMouseEvent = aTriggeringEvent instanceof MouseEvent;
-
- // If the current tab is empty, ignore Alt+Enter (just reuse this tab)
- let altEnter = !isMouseEvent && aTriggeringEvent &&
- aTriggeringEvent.altKey && !isTabEmpty(gBrowser.selectedTab);
-
- if (isMouseEvent || altEnter) {
- // Use the standard UI link behaviors for clicks or Alt+Enter
- let where = "tab";
- if (isMouseEvent)
- where = whereToOpenLink(aTriggeringEvent, false, false);
-
- if (where == "current") {
- if (matchLastLocationChange) {
- loadCurrent();
- }
- } else {
- this._loadURL(aTriggeringEvent, url, where, {
- postData: postData,
- });
- }
- } else {
- if (matchLastLocationChange) {
- loadCurrent();
- }
+ // For the URL to load, prefer the url attribute of the selected
+ // result over the input's value. The reason is that if there is a
+ // selected result and it's a searchengine action, then its url will
+ // have been updated for the currently selected one-off search engine.
+ // Note that when this method is called, the popup may be closed,
+ // which is why this method has a selectedPopupIndex param.
+ let url;
+ if (typeof(selectedPopupIndex) != "number" ||
+ selectedPopupIndex < 0) {
+ selectedPopupIndex = this.popup.selectedIndex;
+ }
+ if (selectedPopupIndex >= 0) {
+ let item = this.popup.richlistbox.children[selectedPopupIndex];
+ if (item) {
+ url = item.getAttribute("url");
}
}
+ url = url || this.value;
+
+ let mayInheritPrincipal = false;
+ let postData = null;
+ let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
+ let matchLastLocationChange = true;
+
+let action = this._parseActionUrl(url);
+ if (action) {
+ switch (action.type) {
+ case "visiturl":
+ case "keyword":
+ case "remotetab":
+ url = action.params.url;
+ break;
+ case "switchtab":
+ url = action.params.url;
+ if (this.hasAttribute("actiontype")) {
+ this.handleRevert();
+ let prevTab = gBrowser.selectedTab;
+ if (switchToTabHavingURI(url) && isTabEmpty(prevTab)) {
+ gBrowser.removeTab(prevTab);
+ }
+ return;
+ }
+ break;
+ case "searchengine":
+ [url, postData] =
+ this._parseAndRecordSearchEngineAction(action, event, where,
+ openUILinkParams);
+ break;
+ }
+ this._loadURL(url, postData, where, openUILinkParams,
+ matchLastLocationChange, mayInheritPrincipal);
+ return;
+ }
+
+ this._canonizeURL(event, response => {
+ [url, postData, mayInheritPrincipal] = response;
+ if (url) {
+ matchLastLocationChange =
+ lastLocationChange ==
+ gBrowser.selectedBrowser.lastLocationChange;
+ this._loadURL(url, postData, where, openUILinkParams,
+ matchLastLocationChange, mayInheritPrincipal);
+ }
+ });
]]></body>
</method>
<method name="_loadURL">
- <parameter name="triggeringEvent"/>
<parameter name="url"/>
- <parameter name="where"/>
- <parameter name="overrideParams"/>
+ <parameter name="postData"/>
+ <parameter name="openUILinkWhere"/>
+ <parameter name="openUILinkParams"/>
+ <parameter name="matchLastLocationChange"/>
+ <parameter name="mayInheritPrincipal"/>
<body><![CDATA[
- let current = where == "current";
+ this.value = url;
+ gBrowser.userTypedValue = url;
+ if (gInitialPages.includes(url)) {
+ gBrowser.selectedBrowser.initialPageLoadedFromURLBar = url;
+ }
+ try {
+ addToUrlbarHistory(url);
+ } catch (ex) {
+ // Things may go wrong when adding url to session history,
+ // but don't let that interfere with the loading of the url.
+ Cu.reportError(ex);
+ }
+
+ let current = openUILinkWhere == "current";
let params;
if (current) {
params = {
+ postData: postData,
allowThirdPartyFixup: true,
indicateErrorPageLoad: true,
disallowInheritPrincipal: true,
allowPinnedTabHostChange: true,
+ disallowInheritPrincipal: !mayInheritPrincipal,
allowPopups: url.startsWith("javascript:"),
};
} else {
params = {
+ postData: postData,
allowThirdPartyFixup: true,
initiatingDoc: document,
};
}
- if (overrideParams) {
- for (let key in overrideParams) {
- params[key] = overrideParams[key];
+ if (openUILinkParams) {
+ for (let key in openUILinkParams) {
+ params[key] = openUILinkParams[key];
}
}
- let action = this._parseActionUrl(this.value);
+ // Focus the content area before triggering loads, since if the load
+ // occurs in a new tab, we want focus to be restored to the content
+ // area when the current tab is re-selected.
+ gBrowser.selectedBrowser.focus();
+
+ if (current && !matchLastLocationChange) {
+ return;
+ }
if (!current) {
this.handleRevert();
}
try {
- openUILinkIn(url, where, params);
+ openUILinkIn(url, openUILinkWhere, params);
} catch (ex) {
// This load can throw an exception in certain cases, which means
// we'll want to replace the URL with the loaded URL:
if (ex.result != Cr.NS_ERROR_LOAD_SHOWED_ERRORPAGE) {
this.handleRevert();
}
}
if (current) {
// Ensure the start of the URL is visible for UX reasons:
this.selectionStart = this.selectionEnd = 0;
}
-
- // Record one-off search telemetry if appropriate.
- if (action && action.type == "searchengine") {
- this.popup.oneOffSearchButtons.recordTelemetry(triggeringEvent,
- where, params);
- }
]]></body>
</method>
<method name="_parseAndRecordSearchEngineAction">
<parameter name="action"/>
+ <parameter name="event"/>
+ <parameter name="openUILinkWhere"/>
+ <parameter name="openUILinkParams"/>
<body><![CDATA[
let engine =
Services.search.getEngineByName(action.params.engineName);
+ BrowserSearch.recordSearchInTelemetry(engine, "urlbar");
+ this.popup.oneOffSearchButtons.recordTelemetry(event, openUILinkWhere,
+ openUILinkParams);
let query = action.params.searchSuggestion ||
action.params.searchQuery;
- return this._recordSearchEngineAction(engine, query);
- ]]></body>
- </method>
-
- <method name="_recordSearchEngineAction">
- <parameter name="engine"/>
- <parameter name="query"/>
- <body><![CDATA[
- BrowserSearch.recordSearchInTelemetry(engine, "urlbar");
let submission = engine.getSubmission(query, null, "keyword");
return [submission.uri.spec, submission.postData];
]]></body>
</method>
<method name="_canonizeURL">
<parameter name="aTriggeringEvent"/>
<parameter name="aCallback"/>
@@ -952,33 +971,34 @@ file, You can obtain one at http://mozil
this.gotResultForCurrentQuery = false;
this.mController.handleText();
}
this.resetActionType();
]]></body>
</method>
<method name="handleEnter">
+ <parameter name="event"/>
<body><![CDATA[
// We need to ensure we're using a selected autocomplete result.
// A result should automatically be selected by default,
// however autocomplete is async and therefore we may not
// have a result set relating to the current input yet. If that
// happens, we need to mark that when the first result does get added,
// it needs to be handled as if enter was pressed with that first
// result selected.
// If anything other than the default (first) result is selected, then
// it must have been manually selected by the human. We let this
// explicit choice be used, even if it may be related to a previous
// input.
// However, if the default result is automatically selected, we
// ensure that it corresponds to the current input.
if (this.popup.selectedIndex != 0 || this.gotResultForCurrentQuery) {
- return this.mController.handleEnter(false);
+ return this.mController.handleEnter(false, event);
}
this.handleEnterWhenGotResult = true;
return true;
]]></body>
</method>
@@ -1148,17 +1168,17 @@ file, You can obtain one at http://mozil
index: controller.selection.currentIndex,
kind: "mouse"
};
}
// Check for unmodified left-click, and use default behavior
if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey &&
!aEvent.altKey && !aEvent.metaKey) {
- controller.handleEnter(true);
+ controller.handleEnter(true, aEvent);
return;
}
// Check for middle-click or modified clicks on the search bar
if (popupForSearchBar) {
// Handle search bar popup clicks
var search = controller.getValueAt(this.selectedIndex);
@@ -1539,106 +1559,32 @@ file, You can obtain one at http://mozil
resolve();
};
this.addEventListener("transitionend", onTransitionEnd, true);
});
]]>
</body>
</method>
- <method name="onPopupClick">
- <parameter name="aEvent"/>
- <body>
- <![CDATA[
- // Ignore right-clicks
- if (aEvent.button == 2)
- return;
-
- var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController);
-
- // Check for unmodified left-click, and use default behavior
- if (aEvent.button == 0 && !aEvent.shiftKey && !aEvent.ctrlKey &&
- !aEvent.altKey && !aEvent.metaKey) {
- controller.handleEnter(true);
- return;
- }
-
- // Check for middle-click or modified clicks on the URL bar
- if (gURLBar && this.mInput == gURLBar) {
- var url = controller.getValueAt(this.selectedIndex);
- var options = {};
-
- // close the autocomplete popup and revert the entered address
- this.closePopup();
- controller.handleEscape();
-
- // Check if this is meant to be an action
- let action = this.mInput._parseActionUrl(url);
- if (action) {
- // TODO (bug 1054816): Centralise the implementation of actions
- // into a JS module.
- switch (action.type) {
- case "switchtab": // Fall through.
- case "keyword": // Fall through.
- case "visiturl": {
- url = action.params.url;
- break;
- }
- case "searchengine": {
- [url, options.postData] =
- this.input._parseAndRecordSearchEngineAction(action);
- break;
- }
- default: {
- return;
- }
- }
- }
-
- // respect the usual clicking subtleties
- openUILink(url, aEvent, options);
- }
- ]]>
- </body>
- </method>
-
<method name="_visuallySelectedOneOffChanged">
<body><![CDATA[
- let selectedNewURL;
- let oneOff = this.oneOffSearchButtons.visuallySelectedButton;
- let engine = (oneOff && oneOff.engine) ||
- Services.search.currentEngine;
-
- // Update the moz-actions of all searchengine results to use the
- // newly selected engine.
+ // Update all searchengine result items to use the newly selected
+ // engine.
for (let item of this.richlistbox.childNodes) {
if (item.collapsed) {
break;
}
let url = item.getAttribute("url");
if (url) {
let action = item._parseActionUrl(url);
if (action && action.type == "searchengine") {
- action.params.engineName = engine.name;
- let newURL =
- PlacesUtils.mozActionURI(action.type, action.params);
- item.setAttribute("url", newURL);
item._adjustAcItem();
- if (item == this.richlistbox.selectedItem) {
- selectedNewURL = newURL;
- }
}
}
}
-
- // The urlbar uses its _value property (via value) to determine where
- // to go when you press Return, so update it too.
- if (selectedNewURL) {
- this.input._value = selectedNewURL;
- }
]]></body>
</method>
<!-- This handles keypress changes to the selection among the one-off
search buttons and between the one-offs and the listbox. It returns
true if the keypress was consumed and false if not. -->
<method name="handleKeyPress">
<parameter name="aEvent"/>
@@ -1653,26 +1599,38 @@ file, You can obtain one at http://mozil
<!-- This is called when a one-off is clicked and when "search in new tab"
is selected from a one-off context menu. -->
<method name="handleOneOffSearch">
<parameter name="event"/>
<parameter name="engine"/>
<parameter name="where"/>
<parameter name="params"/>
<body><![CDATA[
- let query = this.input.textValue;
- let [url, postData] =
- this.input._recordSearchEngineAction(engine, query);
- params = params || {};
- params.postData = postData;
- gBrowser.selectedBrowser.focus();
- this.input._loadURL(event, url, where, params);
+ this.input.handleCommand(event, -1, where, params);
]]></body>
</method>
+ <!-- Result listitems call this to determine which search engine they
+ should show in their labels and include in their url attributes. -->
+ <property name="overrideSearchEngineName" readonly="true">
+ <getter><![CDATA[
+ // When building the popup, autocomplete reuses an item at index i if
+ // that item's url attribute matches the controller's value at index
+ // i, but only if overrideSearchEngineName matches the engine in the
+ // url attribute. To absolutely avoid reusing items that shouldn't be
+ // reused, always return a non-null name here by falling back to the
+ // current engine.
+ let engine =
+ (this.oneOffSearchButtons.visuallySelectedButton &&
+ this.oneOffSearchButtons.visuallySelectedButton.engine) ||
+ Services.search.currentEngine;
+ return engine ? engine.name : null;
+ ]]></getter>
+ </property>
+
<method name="createResultLabel">
<parameter name="item"/>
<parameter name="proposedLabel"/>
<body>
<![CDATA[
let parts = [proposedLabel];
let action = this.mInput._parseActionUrl(item.getAttribute("url"));
@@ -1723,17 +1681,17 @@ file, You can obtain one at http://mozil
this.selectedIndex = 0;
this.richlistbox.suppressMenuItemEvent = false;
this._ignoreNextSelect = false;
}
this.input.gotResultForCurrentQuery = true;
if (this.input.handleEnterWhenGotResult) {
this.input.handleEnterWhenGotResult = false;
- this.input.mController.handleEnter(false);
+ this.input.mController.handleEnter(false, null);
}
]]>
</body>
</method>
</implementation>
<handlers>
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -1159,16 +1159,22 @@
this._textbox = val;
}
]]></setter>
</property>
<field name="_textbox"><![CDATA[
null
]]></field>
+ <!-- Set this to a string that identifies your one-offs consumer. It'll
+ be appended to telemetry recorded with recordTelemetry(). -->
+ <field name="telemetryID"><![CDATA[
+ ""
+ ]]></field>
+
<!-- The query string currently shown in the one-offs. If the textbox
property is non-null, then this is automatically updated on
input. -->
<property name="query">
<getter><![CDATA[
return this._query;
]]></getter>
<setter><![CDATA[
@@ -1234,22 +1240,16 @@
<!-- The number of one-offs, including the add-engine button (if shown)
and the search-settings button. -->
<property name="numButtons" readonly="true">
<getter><![CDATA[
return this.getSelectableButtons(true).length;
]]></getter>
</property>
- <!-- Set this to a string that identifies your one-offs consumer. It'll
- be appended to telemetry recorded with recordTelemetry(). -->
- <field name="telemetryID"><![CDATA[
- ""
- ]]></field>
-
<property name="bundle" readonly="true">
<getter><![CDATA[
if (!this._bundle) {
const kBundleURI = "chrome://browser/locale/search.properties";
this._bundle = Services.strings.createBundle(kBundleURI);
}
return this._bundle;
]]></getter>
@@ -1818,16 +1818,20 @@
The "params" passed to openUILink.
@return True if telemetry was recorded and false if not.
-->
<method name="recordTelemetry">
<parameter name="aEvent"/>
<parameter name="aOpenUILinkWhere"/>
<parameter name="aOpenUILinkParams"/>
<body><![CDATA[
+ if (!aEvent) {
+ return;
+ }
+
let source = null;
let type = "unknown";
let engine = null;
let target = aEvent.originalTarget;
if (aEvent instanceof KeyboardEvent) {
type = "key";
if (this.selectedButton) {
@@ -1925,16 +1929,22 @@
// For some reason, if the context menu had been opened prior to the
// click, the suggestions popup won't be closed after loading the search
// in the current tab - so we hide it manually. Some focusing magic
// that happens when a search is loaded ensures that the popup is opened
// again if it needs to be, so we don't need to worry about which cases
// require manual hiding.
this.popup.hidePopup();
+ // Make sure the clicked button is selected. In practice this should
+ // always be the case since you have to mouse over the button to click
+ // it, and the mouseover handler selects the button. So mostly this is
+ // for tests.
+ this.selectedButton = button;
+
this.handleSearchCommand(event, engine);
]]></handler>
<handler event="command"><![CDATA[
let target = event.originalTarget;
if (target.classList.contains("addengine-item")) {
// On success, hide the panel and tell event listeners to reshow it to
// show the new engine.
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -284,40 +284,42 @@ nsAutoCompleteController::HandleText()
}
StartSearches();
return NS_OK;
}
NS_IMETHODIMP
-nsAutoCompleteController::HandleEnter(bool aIsPopupSelection, bool *_retval)
+nsAutoCompleteController::HandleEnter(bool aIsPopupSelection,
+ nsIDOMEvent *aEvent,
+ bool *_retval)
{
*_retval = false;
if (!mInput)
return NS_OK;
nsCOMPtr<nsIAutoCompleteInput> input(mInput);
+ int32_t selectedIndex = -1;
// allow the event through unless there is something selected in the popup
input->GetPopupOpen(_retval);
if (*_retval) {
nsCOMPtr<nsIAutoCompletePopup> popup;
input->GetPopup(getter_AddRefs(popup));
if (popup) {
- int32_t selectedIndex;
popup->GetSelectedIndex(&selectedIndex);
*_retval = selectedIndex >= 0;
}
}
// Stop the search, and handle the enter.
StopSearch();
- EnterMatch(aIsPopupSelection);
+ EnterMatch(aIsPopupSelection, aEvent, selectedIndex);
return NS_OK;
}
NS_IMETHODIMP
nsAutoCompleteController::HandleEscape(bool *_retval)
{
*_retval = false;
@@ -383,17 +385,17 @@ nsAutoCompleteController::HandleEndCompo
mCompositionState = eCompositionState_Committing;
return NS_OK;
}
NS_IMETHODIMP
nsAutoCompleteController::HandleTab()
{
bool cancel;
- return HandleEnter(false, &cancel);
+ return HandleEnter(false, nullptr, &cancel);
}
NS_IMETHODIMP
nsAutoCompleteController::HandleKeyNavigation(uint32_t aKey, bool *_retval)
{
// By default, don't cancel the event
*_retval = false;
@@ -1351,17 +1353,19 @@ nsAutoCompleteController::ClearSearchTim
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
return NS_OK;
}
nsresult
-nsAutoCompleteController::EnterMatch(bool aIsPopupSelection)
+nsAutoCompleteController::EnterMatch(bool aIsPopupSelection,
+ nsIDOMEvent *aEvent,
+ int32_t aSelectedPopupIndex)
{
nsCOMPtr<nsIAutoCompleteInput> input(mInput);
nsCOMPtr<nsIAutoCompletePopup> popup;
input->GetPopup(getter_AddRefs(popup));
NS_ENSURE_TRUE(popup != nullptr, NS_ERROR_FAILURE);
bool forceComplete;
input->GetForceComplete(&forceComplete);
@@ -1487,17 +1491,17 @@ nsAutoCompleteController::EnterMatch(boo
input->SelectTextRange(value.Length(), value.Length());
mSearchString = value;
}
obsSvc->NotifyObservers(input, "autocomplete-did-enter-text", nullptr);
ClosePopup();
bool cancel;
- input->OnTextEntered(&cancel);
+ input->OnTextEntered(aEvent, aSelectedPopupIndex, &cancel);
return NS_OK;
}
nsresult
nsAutoCompleteController::RevertTextValue()
{
// StopSearch() can call PostSearchCleanup() which might result
--- a/toolkit/components/autocomplete/nsAutoCompleteController.h
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.h
@@ -50,17 +50,19 @@ protected:
nsresult ClearSearchTimer();
void MaybeCompletePlaceholder();
void HandleSearchResult(nsIAutoCompleteSearch *aSearch,
nsIAutoCompleteResult *aResult);
nsresult ProcessResult(int32_t aSearchIndex, nsIAutoCompleteResult *aResult);
nsresult PostSearchCleanup();
- nsresult EnterMatch(bool aIsPopupSelection);
+ nsresult EnterMatch(bool aIsPopupSelection,
+ nsIDOMEvent *aEvent,
+ int32_t aSelectedPopupIndex);
nsresult RevertTextValue();
nsresult CompleteDefaultIndex(int32_t aResultIndex);
nsresult CompleteValue(nsString &aValue);
nsresult GetResultAt(int32_t aIndex, nsIAutoCompleteResult** aResult,
int32_t* aRowIndex);
nsresult GetResultValueAt(int32_t aIndex, bool aGetFinalValue,
--- a/toolkit/components/autocomplete/nsIAutoCompleteController.idl
+++ b/toolkit/components/autocomplete/nsIAutoCompleteController.idl
@@ -1,17 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIAutoCompleteInput;
+interface nsIDOMEvent;
-[scriptable, uuid(ff9f8465-204a-47a6-b3c9-0628b3856684)]
+[scriptable, uuid(c6dcd364-99a8-48a9-9611-77e6a18ac9f3)]
interface nsIAutoCompleteController : nsISupports
{
/*
* Possible values for the searchStatus attribute
*/
const unsigned short STATUS_NONE = 1;
const unsigned short STATUS_SEARCHING = 2;
const unsigned short STATUS_COMPLETE_NO_MATCH = 3;
@@ -58,19 +59,26 @@ interface nsIAutoCompleteController : ns
void handleText();
/*
* Notify the controller that the user wishes to enter the current text. If
* aIsPopupSelection is true, then a selection was made from the popup, so
* fill this value into the input field before continuing. If false, just
* use the current value of the input field.
*
+ * @param aIsPopupSelection
+ * Pass true if the selection was made from the popup.
+ * @param aEvent
+ * Optional. The event that triggered the enter, like a key event if
+ * the user pressed the Return key or a click event if the user clicked
+ * a popup item.
* @return True if the controller wishes to prevent event propagation and default event
*/
- boolean handleEnter(in boolean aIsPopupSelection);
+ boolean handleEnter(in boolean aIsPopupSelection,
+ in nsIDOMEvent aEvent);
/*
* Notify the controller that the user wishes to revert autocomplete
*
* @return True if the controller wishes to prevent event propagation and default event
*/
boolean handleEscape();
--- a/toolkit/components/autocomplete/nsIAutoCompleteInput.idl
+++ b/toolkit/components/autocomplete/nsIAutoCompleteInput.idl
@@ -2,17 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsIAutoCompleteController.idl"
interface nsIAutoCompletePopup;
-[scriptable, uuid(B068E70F-F82C-4C12-AD87-82E271C5C180)]
+[scriptable, uuid(d2f4b70b-e679-4295-9f91-a1ca57da7fca)]
interface nsIAutoCompleteInput : nsISupports
{
/*
* The result view that will be used to display results
*/
readonly attribute nsIAutoCompletePopup popup;
/*
@@ -119,19 +119,25 @@ interface nsIAutoCompleteInput : nsISupp
/*
* Notification that the search concluded successfully
*/
void onSearchComplete();
/*
* Notification that the user selected and entered a result item
*
+ * @param aEvent
+ * The event that triggered the enter. Will be null if it's unknown.
+ * @param aSelectedPopupIndex
+ * The index in the popup that was selected when the enter was made.
+ * -1 if unknown.
* @return True if the user wishes to prevent the enter
*/
- boolean onTextEntered();
+ boolean onTextEntered(in nsIDOMEvent aEvent,
+ in long aSelectedPopupIndex);
/*
* Notification that the user cancelled the autocomplete session
*
* @return True if the user wishes to prevent the revert
*/
boolean onTextReverted();
--- a/toolkit/components/autocomplete/tests/unit/test_completeDefaultIndex_casing.js
+++ b/toolkit/components/autocomplete/tests/unit/test_completeDefaultIndex_casing.js
@@ -25,17 +25,17 @@ add_test(function test_keyNavigation() {
aController.handleKeyNavigation(Ci.nsIDOMKeyEvent.DOM_VK_RIGHT);
do_check_eq(aController.input.textValue, "mozilla");
});
});
add_test(function test_handleEnter() {
doSearch("MOZ", "mozilla", function(aController) {
do_check_eq(aController.input.textValue, "MOZilla");
- aController.handleEnter(false);
+ aController.handleEnter(false, null);
do_check_eq(aController.input.textValue, "mozilla");
});
});
function doSearch(aSearchString, aResultValue, aOnCompleteCallback) {
let search = new AutoCompleteSearchBase("search",
new AutoCompleteResult([ "mozilla", "toolkit" ], 0));
registerAutoCompleteSearch(search);
--- a/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue.js
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue.js
@@ -11,17 +11,17 @@ function AutoCompleteInput(aSearches) {
AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
add_test(function test_handleEnter_mouse() {
doSearch("moz", "mozilla.com", "http://www.mozilla.com", function(aController) {
do_check_eq(aController.input.textValue, "moz");
do_check_eq(aController.getFinalCompleteValueAt(0), "http://www.mozilla.com");
// Keyboard interaction is tested by test_finalCompleteValueSelectedIndex.js
// so here just test popup selection.
- aController.handleEnter(true);
+ aController.handleEnter(true, null);
do_check_eq(aController.input.textValue, "http://www.mozilla.com");
});
});
function doSearch(aSearchString, aResultValue, aFinalCompleteValue, aOnCompleteCallback) {
let search = new AutoCompleteSearchBase(
"search",
new AutoCompleteResult([ aResultValue ], [ aFinalCompleteValue ])
--- a/toolkit/components/autocomplete/tests/unit/test_finalCompleteValueSelectedIndex.js
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValueSelectedIndex.js
@@ -36,17 +36,17 @@ add_test(function test_handleEnter() {
Assert.equal(aController.input.popup.selectedIndex, 0);
aController.handleKeyNavigation(Ci.nsIDOMKeyEvent.DOM_VK_DOWN);
Assert.equal(aController.input.popup.selectedIndex, 1);
// Simulate mouse interaction changing selectedIndex
// ie NOT keyboard interaction:
aController.input.popup.selectedIndex = 0;
- aController.handleEnter(false);
+ aController.handleEnter(false, null);
// Verify that the keyboard-selected thing got inserted,
// and not the mouse selection:
Assert.equal(aController.input.textValue, "http://www.mozilla.org");
});
// Then the case where we do not:
doSearch("moz", results, function(aController) {
Assert.equal(aController.input.textValue, "moz");
@@ -56,17 +56,17 @@ add_test(function test_handleEnter() {
Assert.equal(aController.input.popup.selectedIndex, 0);
aController.input.popupOpen = true;
// Simulate mouse interaction changing selectedIndex
// ie NOT keyboard interaction:
aController.input.popup.selectedIndex = 1;
Assert.equal(selectByWasCalled, false);
Assert.equal(aController.input.popup.selectedIndex, 1);
- aController.handleEnter(false);
+ aController.handleEnter(false, null);
// Verify that the input stayed the same, because no selection was made
// with the keyboard:
Assert.equal(aController.input.textValue, "moz");
});
});
function doSearch(aSearchString, aResults, aOnCompleteCallback) {
selectByWasCalled = false;
--- a/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_defaultIndex.js
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_defaultIndex.js
@@ -20,17 +20,17 @@ add_test(function test_handleEnter() {
];
doSearch("moz", results, controller => {
let input = controller.input;
Assert.equal(input.textValue, "mozilla.com");
Assert.equal(controller.getFinalCompleteValueAt(0), results[0][1]);
Assert.equal(controller.getFinalCompleteValueAt(1), results[1][1]);
Assert.equal(input.popup.selectedIndex, 0);
- controller.handleEnter(false);
+ controller.handleEnter(false, null);
// Verify that the keyboard-selected thing got inserted,
// and not the mouse selection:
Assert.equal(controller.input.textValue, "https://www.mozilla.com");
});
});
function doSearch(aSearchString, aResults, aOnCompleteCallback) {
let search = new AutoCompleteSearchBase(
--- a/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_forceComplete.js
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_forceComplete.js
@@ -19,56 +19,56 @@ function run_test() {
run_next_test();
}
add_test(function test_handleEnterWithDirectMatchCompleteSelectedIndex() {
doSearch("moz", "mozilla.com", "http://www.mozilla.com",
{ forceComplete: true, completeSelectedIndex: true }, function(aController) {
do_check_eq(aController.input.textValue, "moz");
do_check_eq(aController.getFinalCompleteValueAt(0), "http://www.mozilla.com");
- aController.handleEnter(false);
+ aController.handleEnter(false, null);
// After enter the final complete value should be shown in the input.
do_check_eq(aController.input.textValue, "http://www.mozilla.com");
});
});
add_test(function test_handleEnterWithDirectMatch() {
doSearch("mozilla", "mozilla.com", "http://www.mozilla.com",
{ forceComplete: true, completeDefaultIndex: true }, function(aController) {
// Should autocomplete the search string to a suggestion.
do_check_eq(aController.input.textValue, "mozilla.com");
do_check_eq(aController.getFinalCompleteValueAt(0), "http://www.mozilla.com");
- aController.handleEnter(false);
+ aController.handleEnter(false, null);
// After enter the final complete value should be shown in the input.
do_check_eq(aController.input.textValue, "http://www.mozilla.com");
});
});
add_test(function test_handleEnterWithNoMatch() {
doSearch("mozilla", "mozilla.com", "http://www.mozilla.com",
{ forceComplete: true, completeDefaultIndex: true }, function(aController) {
// Should autocomplete the search string to a suggestion.
do_check_eq(aController.input.textValue, "mozilla.com");
do_check_eq(aController.getFinalCompleteValueAt(0), "http://www.mozilla.com");
// Now input something that does not match...
aController.input.textValue = "mozillax";
// ... and confirm. We don't want one of the values from the previous
// results to be taken, since what's now in the input field doesn't match.
- aController.handleEnter(false);
+ aController.handleEnter(false, null);
do_check_eq(aController.input.textValue, "mozillax");
});
});
add_test(function test_handleEnterWithIndirectMatch() {
doSearch("com", "mozilla.com", "http://www.mozilla.com",
{ forceComplete: true, completeDefaultIndex: true }, function(aController) {
// Should autocomplete the search string to a suggestion.
do_check_eq(aController.input.textValue, "com >> mozilla.com");
do_check_eq(aController.getFinalCompleteValueAt(0), "http://www.mozilla.com");
- aController.handleEnter(false);
+ aController.handleEnter(false, null);
// After enter the final complete value from the suggestion should be shown
// in the input.
do_check_eq(aController.input.textValue, "http://www.mozilla.com");
});
});
function doSearch(aSearchString, aResultValue, aFinalCompleteValue,
aInputProps, aOnCompleteCallback) {
--- a/toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js
+++ b/toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js
@@ -26,17 +26,17 @@ add_test(function test_keyNavigation() {
aController.handleKeyNavigation(Ci.nsIDOMKeyEvent.DOM_VK_RIGHT);
do_check_eq(aController.input.textValue, "mozilla.com");
});
});
add_test(function test_handleEnter() {
doSearch("moz", "mozilla.com", "http://www.mozilla.com", function(aController) {
do_check_eq(aController.input.textValue, "mozilla.com");
- aController.handleEnter(false);
+ aController.handleEnter(false, null);
do_check_eq(aController.input.textValue, "http://www.mozilla.com");
});
});
function doSearch(aSearchString, aResultValue, aFinalCompleteValue, aOnCompleteCallback) {
let search = new AutoCompleteSearchBase(
"search",
new AutoCompleteResult([ aResultValue ], [ aFinalCompleteValue ])
--- a/toolkit/components/autocomplete/tests/unit/test_hiddenResult.js
+++ b/toolkit/components/autocomplete/tests/unit/test_hiddenResult.js
@@ -57,17 +57,17 @@ function run_test() {
input.onSearchComplete = function() {
// Hidden results should still be able to do inline autocomplete
do_check_eq(input.textValue, "mozillaTest1");
// Now, let's fill the textbox with the first result of the popup.
// The first search is marked as hidden, so we must always get the
// second search.
- controller.handleEnter(true);
+ controller.handleEnter(true, null);
do_check_eq(input.textValue, "mozillaTest2");
// Only one item in the popup.
do_check_eq(controller.matchCount, 1);
// Unregister searches
unregisterAutoCompleteSearch(searchNormal);
unregisterAutoCompleteSearch(searchTypeAhead);
--- a/toolkit/components/autocomplete/tests/unit/test_popupSelectionVsDefaultCompleteValue.js
+++ b/toolkit/components/autocomplete/tests/unit/test_popupSelectionVsDefaultCompleteValue.js
@@ -25,17 +25,17 @@ AutoCompleteInput.prototype = Object.cre
function run_test() {
run_next_test();
}
add_test(function test_handleEnter() {
doSearch("moz", function(aController) {
do_check_eq(aController.input.textValue, "mozilla.com");
- aController.handleEnter(true);
+ aController.handleEnter(true, null);
do_check_eq(aController.input.textValue, "mozilla.org");
});
});
function doSearch(aSearchString, aOnCompleteCallback) {
let typeAheadSearch = new AutoCompleteSearchBase(
"typeAheadSearch",
new AutoCompleteTypeAheadResult([ "mozilla.com" ], [ "http://www.mozilla.com" ])
--- a/toolkit/components/autocomplete/tests/unit/test_stopSearch.js
+++ b/toolkit/components/autocomplete/tests/unit/test_stopSearch.js
@@ -125,17 +125,17 @@ var gTests = [
controller.handleText();
},
function(controller) {
print("handleEscape");
controller.handleEscape();
},
function(controller) {
print("handleEnter");
- controller.handleEnter(false);
+ controller.handleEnter(false, null);
},
function(controller) {
print("handleTab");
controller.handleTab();
},
function(controller) {
print("handleKeyNavigation");
--- a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
+++ b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
@@ -250,17 +250,17 @@ function* check_autocomplete(test) {
if (test.autofilled) {
// Check the autoFilled result.
Assert.equal(input.textValue, test.autofilled,
"Autofilled value is correct");
// Now force completion and check correct casing of the result.
// This ensures the controller is able to do its magic case-preserving
// stuff and correct replacement of the user's casing with result's one.
- controller.handleEnter(false);
+ controller.handleEnter(false, null);
Assert.equal(input.textValue, test.completed,
"Completed value is correct");
}
}
var addBookmark = Task.async(function* (aBookmarkObj) {
Assert.ok(!!aBookmarkObj.uri, "Bookmark object contains an uri");
let parentId = aBookmarkObj.parentId ? aBookmarkObj.parentId
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -585,17 +585,17 @@ var AutoCompletePopup = {
this._input = null;
this._popupOpen = false;
addMessageListener("FormAutoComplete:HandleEnter", message => {
this.selectedIndex = message.data.selectedIndex;
let controller = Components.classes["@mozilla.org/autocomplete/controller;1"].
getService(Components.interfaces.nsIAutoCompleteController);
- controller.handleEnter(message.data.isPopupSelection);
+ controller.handleEnter(message.data.isPopupSelection, null);
});
addEventListener("unload", function() {
AutoCompletePopup.destroy();
});
},
destroy: function() {
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -39,17 +39,16 @@
<children includes="toolbarbutton"/>
</content>
<implementation implements="nsIAutoCompleteInput, nsIDOMXULMenuListElement">
<field name="mController">null</field>
<field name="mSearchNames">null</field>
<field name="mIgnoreInput">false</field>
- <field name="mEnterEvent">null</field>
<field name="_searchBeginHandler">null</field>
<field name="_searchCompleteHandler">null</field>
<field name="_textEnteredHandler">null</field>
<field name="_textRevertedHandler">null</field>
<constructor><![CDATA[
this.mController = Components.classes["@mozilla.org/autocomplete/controller;1"].
@@ -227,21 +226,23 @@
}
if (this._searchCompleteHandler)
this._searchCompleteHandler();
]]></body>
</method>
<method name="onTextEntered">
+ <parameter name="event"/>
+ <parameter name="selectedPopupIndex"/>
<body><![CDATA[
let rv = false;
- if (this._textEnteredHandler)
- rv = this._textEnteredHandler(this.mEnterEvent);
- this.mEnterEvent = null;
+ if (this._textEnteredHandler) {
+ rv = this._textEnteredHandler(event, selectedPopupIndex);
+ }
return rv;
]]></body>
</method>
<method name="onTextReverted">
<body><![CDATA[
if (this._textRevertedHandler)
return this._textRevertedHandler();
@@ -411,19 +412,23 @@
]]></body>
</method>
<!-- ::::::::::::: event dispatching ::::::::::::: -->
<method name="initEventHandler">
<parameter name="aEventType"/>
<body><![CDATA[
- let handlerString = this.getAttribute("on" + aEventType);
- if (handlerString) {
- return (new Function("eventType", "param", handlerString)).bind(this, aEventType);
+ let handlerCode = this.getAttribute("on" + aEventType);
+ if (handlerCode) {
+ return (...args) => {
+ let fn = (new Function("eventType", "args", handlerCode))
+ .bind(this, aEventType, args);
+ return fn();
+ };
}
return null;
]]></body>
</method>
<!-- ::::::::::::: key handling ::::::::::::: -->
<field name="_selectionDetails">null</field>
@@ -489,24 +494,23 @@
cancel = this.mController.handleEscape();
break;
case KeyEvent.DOM_VK_RETURN:
if (AppConstants.platform == "macosx") {
// Prevent the default action, since it will beep on Mac
if (aEvent.metaKey)
aEvent.preventDefault();
}
- this.mEnterEvent = aEvent;
if (this.mController.selection) {
this._selectionDetails = {
index: this.mController.selection.currentIndex,
kind: "key"
};
}
- cancel = this.handleEnter();
+ cancel = this.handleEnter(aEvent);
break;
case KeyEvent.DOM_VK_DELETE:
if (AppConstants.platform == "macosx" && !aEvent.shiftKey) {
break;
}
cancel = this.handleDelete();
break;
case KeyEvent.DOM_VK_BACK_SPACE:
@@ -531,18 +535,19 @@
aEvent.preventDefault();
}
return true;
]]></body>
</method>
<method name="handleEnter">
+ <parameter name="event"/>
<body><![CDATA[
- return this.mController.handleEnter(false);
+ return this.mController.handleEnter(false, event || null);
]]></body>
</method>
<method name="handleDelete">
<body><![CDATA[
return this.mController.handleDelete();
]]></body>
</method>
@@ -642,17 +647,17 @@
for (let i = 0; i < this.mController.matchCount; i++) {
let matchVal = this.mController.getFinalCompleteValueAt(i);
if (matchVal.toLowerCase() == filledVal) {
this.popup.selectedIndex = i;
break;
}
}
}
- this.mController.handleEnter(false);
+ this.mController.handleEnter(false, null);
}
if (!this.ignoreBlurWhileSearching)
this.detachController();
}
]]></handler>
</handlers>
</binding>
@@ -941,17 +946,17 @@ extends="chrome://global/content/binding
return aIndex;
]]></body>
</method>
<method name="onPopupClick">
<parameter name="aEvent"/>
<body><![CDATA[
var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController);
- controller.handleEnter(true);
+ controller.handleEnter(true, aEvent);
]]></body>
</method>
</implementation>
<handlers>
<handler event="popupshowing"><![CDATA[
// If normalMaxRows wasn't already set by the input, then set it here
// so that we restore the correct number when the popup is hidden.
@@ -1245,25 +1250,34 @@ extends="chrome://global/content/binding
// due to new results, but only when: the item is the same, *OR*
// we are about to replace the currently mouse-selected item, to
// avoid surprising the user.
let iface = Components.interfaces.nsIAutoCompletePopup;
if (item.getAttribute("text") == trimmedSearchString &&
invalidateReason == iface.INVALIDATE_REASON_NEW_RESULT &&
(item.getAttribute("url") == url ||
this.richlistbox.mouseSelectedIndex === this._currentIndex)) {
- item.collapsed = false;
- // Call adjustSiteIconStart only after setting collapsed=false.
- // The calculations it does may be wrong otherwise.
- item.adjustSiteIconStart(this._siteIconStart);
- // The popup may have changed size between now and the last time
- // the item was shown, so always handle over/underflow.
- item.handleOverUnderflow();
- this._currentIndex++;
- continue;
+ // Additionally, if the item is a searchengine action, then it
+ // should only be reused if the engine name is the same as the
+ // popup's override engine name, if any.
+ let action = item._parseActionUrl(url);
+ if (!action ||
+ action.type != "searchengine" ||
+ !this.overrideSearchEngineName ||
+ action.params.engineName == this.overrideSearchEngineName) {
+ item.collapsed = false;
+ // Call adjustSiteIconStart only after setting collapsed=
+ // false. The calculations it does may be wrong otherwise.
+ item.adjustSiteIconStart(this._siteIconStart);
+ // The popup may have changed size between now and the last
+ // time the item was shown, so always handle over/underflow.
+ item.handleOverUnderflow();
+ this._currentIndex++;
+ continue;
+ }
}
}
else {
// need to create a new item
item = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "richlistitem");
item.setAttribute("dir", this.style.direction);
}
@@ -1890,16 +1904,27 @@ extends="chrome://global/content/binding
emphasiseUrl = false;
// The order here is not localizable, we default to appending
// "- Search with Engine" to the search string, to be able to
// properly generate emphasis pairs. That said, no localization
// changed the order while it was possible, so doesn't look like
// there's a strong need for that.
let {engineName, searchSuggestion, searchQuery} = action.params;
+
+ // Override the engine name if the popup defines an override.
+ let override = popup.overrideSearchEngineName;
+ if (override && override != engineName) {
+ engineName = override;
+ action.params.engineName = override;
+ let newURL =
+ PlacesUtils.mozActionURI(action.type, action.params);
+ this.setAttribute("url", newURL);
+ }
+
let engineStr =
this._stringBundle.formatStringFromName("searchWithEngine",
[engineName], 1);
this._setUpDescription(this._actionText, engineStr, true);
// Make the title by generating an array of pairs and its
// corresponding interpolation string (e.g., "%1$S") to pass to
// _generateEmphasisPairs.
--- a/xpfe/components/autocomplete/resources/content/autocomplete.xml
+++ b/xpfe/components/autocomplete/resources/content/autocomplete.xml
@@ -1575,17 +1575,17 @@
<handlers>
<handler event="mouseout" action="this.popup.selectedIndex = -1;"/>
<handler event="mouseup"><![CDATA[
var rc = this.parentNode.treeBoxObject.getRowAt(event.clientX, event.clientY);
if (rc != -1) {
this.popup.selectedIndex = rc;
- this.popup.view.handleEnter(true);
+ this.popup.view.handleEnter(true, null);
}
]]></handler>
<handler event="mousemove"><![CDATA[
if (Date.now() - this.mLastMoveTime > 30) {
var rc = this.parentNode.treeBoxObject.getRowAt(event.clientX, event.clientY);
if (rc != -1 && rc != this.popup.selectedIndex)
this.popup.selectedIndex = rc;