зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1530675 - Enable WebNavigation transistion notifications for WebExtensions with QuantumBar. r=mak,rpl
Differential Revision: https://phabricator.services.mozilla.com/D21218 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
45e4b14a1a
Коммит
da3d75151a
|
@ -4,19 +4,14 @@
|
|||
|
||||
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "UrlbarTestUtils",
|
||||
"resource://testing-common/UrlbarTestUtils.jsm");
|
||||
|
||||
const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
|
||||
const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
|
||||
|
||||
async function promiseAutocompleteResultPopup(inputText) {
|
||||
gURLBar.focus();
|
||||
gURLBar.value = inputText;
|
||||
gURLBar.controller.startSearch(inputText);
|
||||
await promisePopupShown(gURLBar.popup);
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
return gURLBar.controller.searchStatus >=
|
||||
Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH;
|
||||
});
|
||||
function promiseAutocompleteResultPopup(inputText) {
|
||||
return UrlbarTestUtils.promiseAutocompleteResultPopup(window, inputText, waitForFocus);
|
||||
}
|
||||
|
||||
async function addBookmark(bookmark) {
|
||||
|
@ -60,10 +55,7 @@ async function prepareSearchEngine() {
|
|||
await Services.search.setDefault(oldDefaultEngine);
|
||||
|
||||
// Make sure the popup is closed for the next test.
|
||||
gURLBar.blur();
|
||||
gURLBar.popup.selectedIndex = -1;
|
||||
gURLBar.popup.hidePopup();
|
||||
ok(!gURLBar.popup.popupOpen, "popup should be closed");
|
||||
await UrlbarTestUtils.promisePopupClose(window);
|
||||
|
||||
// Clicking suggestions causes visits to search results pages, so clear that
|
||||
// history now.
|
||||
|
@ -109,7 +101,46 @@ add_task(async function test_webnavigation_urlbar_typed_transitions() {
|
|||
await extension.awaitFinish("webNavigation.from_address_bar.typed");
|
||||
|
||||
await extension.unload();
|
||||
info("extension unloaded");
|
||||
});
|
||||
|
||||
add_task(async function test_webnavigation_urlbar_typed_closed_popup_transitions() {
|
||||
function backgroundScript() {
|
||||
browser.webNavigation.onCommitted.addListener((msg) => {
|
||||
browser.test.assertEq("http://example.com/?q=typedClosed", msg.url,
|
||||
"Got the expected url");
|
||||
// assert from_address_bar transition qualifier
|
||||
browser.test.assertTrue(msg.transitionQualifiers &&
|
||||
msg.transitionQualifiers.includes("from_address_bar"),
|
||||
"Got the expected from_address_bar transitionQualifier");
|
||||
browser.test.assertEq("typed", msg.transitionType,
|
||||
"Got the expected transitionType");
|
||||
browser.test.notifyPass("webNavigation.from_address_bar.typed");
|
||||
});
|
||||
|
||||
browser.test.sendMessage("ready");
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
background: backgroundScript,
|
||||
manifest: {
|
||||
permissions: ["webNavigation"],
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
await SimpleTest.promiseFocus(window);
|
||||
|
||||
await extension.awaitMessage("ready");
|
||||
await UrlbarTestUtils.promiseAutocompleteResultPopup(window, "http://example.com/?q=typedClosed", waitForFocus);
|
||||
await UrlbarTestUtils.promiseSearchComplete(window);
|
||||
// Closing the popup forces a different code route that handles no results
|
||||
// being displayed.
|
||||
await UrlbarTestUtils.promisePopupClose(window);
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
|
||||
await extension.awaitFinish("webNavigation.from_address_bar.typed");
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function test_webnavigation_urlbar_bookmark_transitions() {
|
||||
|
@ -149,12 +180,11 @@ add_task(async function test_webnavigation_urlbar_bookmark_transitions() {
|
|||
|
||||
await promiseAutocompleteResultPopup("Bookmark To Click");
|
||||
|
||||
let item = gURLBar.popup.richlistbox.getItemAtIndex(1);
|
||||
item.click();
|
||||
let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
|
||||
EventUtils.synthesizeMouseAtCenter(result.element.row, {});
|
||||
await extension.awaitFinish("webNavigation.from_address_bar.auto_bookmark");
|
||||
|
||||
await extension.unload();
|
||||
info("extension unloaded");
|
||||
});
|
||||
|
||||
add_task(async function test_webnavigation_urlbar_keyword_transition() {
|
||||
|
@ -195,13 +225,12 @@ add_task(async function test_webnavigation_urlbar_keyword_transition() {
|
|||
|
||||
await promiseAutocompleteResultPopup("testkw search");
|
||||
|
||||
let item = gURLBar.popup.richlistbox.getItemAtIndex(0);
|
||||
item.click();
|
||||
let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
|
||||
EventUtils.synthesizeMouseAtCenter(result.element.row, {});
|
||||
|
||||
await extension.awaitFinish("webNavigation.from_address_bar.keyword");
|
||||
|
||||
await extension.unload();
|
||||
info("extension unloaded");
|
||||
});
|
||||
|
||||
add_task(async function test_webnavigation_urlbar_search_transitions() {
|
||||
|
@ -237,11 +266,10 @@ add_task(async function test_webnavigation_urlbar_search_transitions() {
|
|||
await prepareSearchEngine();
|
||||
await promiseAutocompleteResultPopup("foo");
|
||||
|
||||
let item = gURLBar.popup.richlistbox.getItemAtIndex(0);
|
||||
item.click();
|
||||
let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
|
||||
EventUtils.synthesizeMouseAtCenter(result.element.row, {});
|
||||
|
||||
await extension.awaitFinish("webNavigation.from_address_bar.generated");
|
||||
|
||||
await extension.unload();
|
||||
info("extension unloaded");
|
||||
});
|
||||
|
|
|
@ -345,7 +345,7 @@ class UrlbarInput {
|
|||
browser.lastLocationChange == lastLocationChange) {
|
||||
openParams.postData = data.postData;
|
||||
openParams.allowInheritPrincipal = data.mayInheritPrincipal;
|
||||
this._loadURL(data.url, where, openParams, browser);
|
||||
this._loadURL(data.url, where, openParams, null, browser);
|
||||
}
|
||||
});
|
||||
return;
|
||||
|
@ -445,7 +445,10 @@ class UrlbarInput {
|
|||
if (!url) {
|
||||
throw new Error(`Invalid url for result ${JSON.stringify(result)}`);
|
||||
}
|
||||
this._loadURL(url, where, openParams);
|
||||
this._loadURL(url, where, openParams, {
|
||||
source: result.source,
|
||||
type: result.type,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -986,9 +989,15 @@ class UrlbarInput {
|
|||
* The POST data associated with a search submission.
|
||||
* @param {boolean} [params.allowInheritPrincipal]
|
||||
* If the principal may be inherited
|
||||
* @param {object} [result]
|
||||
* Details of the selected result, if any
|
||||
* @param {UrlbarUtils.RESULT_TYPE} [result.type]
|
||||
* Details of the result type, if any.
|
||||
* @param {UrlbarUtils.RESULT_SOURCE} [result.source]
|
||||
* Details of the result source, if any.
|
||||
* @param {object} browser [optional] the browser to use for the load.
|
||||
*/
|
||||
_loadURL(url, openUILinkWhere, params,
|
||||
_loadURL(url, openUILinkWhere, params, result = {},
|
||||
browser = this.window.gBrowser.selectedBrowser) {
|
||||
this.value = url;
|
||||
browser.userTypedValue = url;
|
||||
|
@ -1032,6 +1041,9 @@ class UrlbarInput {
|
|||
this.handleRevert();
|
||||
}
|
||||
|
||||
// Notify about the start of navigation.
|
||||
this._notifyStartNavigation(result);
|
||||
|
||||
try {
|
||||
this.window.openTrustedLinkIn(url, openUILinkWhere, params);
|
||||
} catch (ex) {
|
||||
|
@ -1131,6 +1143,20 @@ class UrlbarInput {
|
|||
insertLocation.insertAdjacentElement("afterend", pasteAndGo);
|
||||
}
|
||||
|
||||
/**
|
||||
* This notifies observers that the user has entered or selected something in
|
||||
* the URL bar which will cause navigation.
|
||||
*
|
||||
* We use the observer service, so that we don't need to load extra facilities
|
||||
* if they aren't being used, e.g. WebNavigation.
|
||||
*
|
||||
* @param {UrlbarResult} [result]
|
||||
* The result that was selected, if any.
|
||||
*/
|
||||
_notifyStartNavigation(result) {
|
||||
Services.obs.notifyObservers({result}, "urlbar-user-start-navigation");
|
||||
}
|
||||
|
||||
// Event handlers below.
|
||||
|
||||
_on_blur(event) {
|
||||
|
|
|
@ -66,6 +66,8 @@ var UrlbarUtils = {
|
|||
},
|
||||
|
||||
// Defines UrlbarResult types.
|
||||
// If you add new result types, consider checking if consumers of
|
||||
// "urlbar-user-start-navigation" need update as well.
|
||||
RESULT_TYPE: {
|
||||
// An open tab.
|
||||
// Payload: { icon, url, userContextId }
|
||||
|
@ -91,6 +93,8 @@ var UrlbarUtils = {
|
|||
// can return results from more than one source. This is used by the
|
||||
// ProvidersManager to decide which providers must be queried and which
|
||||
// results can be returned.
|
||||
// If you add new source types, consider checking if consumers of
|
||||
// "urlbar-user-start-navigation" need update as well.
|
||||
RESULT_SOURCE: {
|
||||
BOOKMARKS: 1,
|
||||
HISTORY: 2,
|
||||
|
|
|
@ -12,6 +12,8 @@ ChromeUtils.defineModuleGetter(this, "BrowserWindowTracker",
|
|||
"resource:///modules/BrowserWindowTracker.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "UrlbarUtils",
|
||||
"resource:///modules/UrlbarUtils.jsm");
|
||||
|
||||
// Maximum amount of time that can be passed and still consider
|
||||
// the data recent (similar to how is done in nsNavHistory,
|
||||
|
@ -33,6 +35,7 @@ var Manager = {
|
|||
this.createdNavigationTargetByOuterWindowId = new Map();
|
||||
|
||||
Services.obs.addObserver(this, "autocomplete-did-enter-text", true);
|
||||
Services.obs.addObserver(this, "urlbar-user-start-navigation", true);
|
||||
|
||||
Services.obs.addObserver(this, "webNavigation-createdNavigationTarget");
|
||||
|
||||
|
@ -49,6 +52,7 @@ var Manager = {
|
|||
uninit() {
|
||||
// Stop collecting recent tab transition data and reset the WeakMap.
|
||||
Services.obs.removeObserver(this, "autocomplete-did-enter-text");
|
||||
Services.obs.removeObserver(this, "urlbar-user-start-navigation");
|
||||
Services.obs.removeObserver(this, "webNavigation-createdNavigationTarget");
|
||||
|
||||
Services.mm.removeMessageListener("Content:Click", this);
|
||||
|
@ -108,7 +112,11 @@ var Manager = {
|
|||
* @param {string|undefined} data
|
||||
*/
|
||||
observe: function(subject, topic, data) {
|
||||
if (topic == "autocomplete-did-enter-text") {
|
||||
if (topic == "urlbar-user-start-navigation") {
|
||||
this.onURLBarUserStartNavigation(subject.wrappedJSObject);
|
||||
} else if (topic == "autocomplete-did-enter-text") {
|
||||
// autocomplete-did-enter-text supports the legacy urlbar. Bug 1535379 will
|
||||
// clean this up.
|
||||
this.onURLBarAutoCompletion(subject);
|
||||
} else if (topic == "webNavigation-createdNavigationTarget") {
|
||||
// The observed notification is coming from privileged JavaScript components running
|
||||
|
@ -129,6 +137,65 @@ var Manager = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Recognize the type of urlbar user interaction (e.g. typing a new url,
|
||||
* clicking on an url generated from a searchengine or a keyword, or a
|
||||
* bookmark found by the urlbar autocompletion).
|
||||
*
|
||||
* @param {object} acData
|
||||
* The data for the autocompleted item.
|
||||
* @param {object} [acData.result]
|
||||
* The result information associated with the navigation action.
|
||||
* @param {UrlbarUtils.RESULT_TYPE} [acData.result.type]
|
||||
* The result type associated with the navigation action.
|
||||
* @param {UrlbarUtils.RESULT_SOURCE} [acData.result.source]
|
||||
* The result source associated with the navigation action.
|
||||
*/
|
||||
onURLBarUserStartNavigation(acData) {
|
||||
let tabTransitionData = {
|
||||
from_address_bar: true,
|
||||
};
|
||||
|
||||
if (!acData.result) {
|
||||
tabTransitionData.typed = true;
|
||||
} else {
|
||||
switch (acData.result.type) {
|
||||
case UrlbarUtils.RESULT_TYPE.KEYWORD:
|
||||
tabTransitionData.keyword = true;
|
||||
break;
|
||||
case UrlbarUtils.RESULT_TYPE.SEARCH:
|
||||
tabTransitionData.generated = true;
|
||||
break;
|
||||
case UrlbarUtils.RESULT_TYPE.URL:
|
||||
if (acData.result.source == UrlbarUtils.RESULT_SOURCE.BOOKMARKS) {
|
||||
tabTransitionData.auto_bookmark = true;
|
||||
} else {
|
||||
tabTransitionData.typed = true;
|
||||
}
|
||||
break;
|
||||
case UrlbarUtils.RESULT_TYPE.REMOTE_TAB:
|
||||
// Remote tab are autocomplete results related to
|
||||
// tab urls from a remote synchronized Firefox.
|
||||
tabTransitionData.typed = true;
|
||||
break;
|
||||
case UrlbarUtils.RESULT_TYPE.TAB_SWITCH:
|
||||
// This "switchtab" autocompletion should be ignored, because
|
||||
// it is not related to a navigation.
|
||||
// Fall through.
|
||||
case UrlbarUtils.RESULT_TYPE.OMNIBOX:
|
||||
// "Omnibox" should be ignored as the add-on may or may not initiate
|
||||
// a navigation on the item being selected.
|
||||
throw new Error(`Unexpectedly received notification for ${acData.result.type}`);
|
||||
default:
|
||||
Cu.reportError(`Received unexpected result type ${acData.result.type}, falling back to typed transition.`);
|
||||
// Fallback on "typed" if the type is unknown.
|
||||
tabTransitionData.typed = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.setRecentTabTransitionData(tabTransitionData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Recognize the type of urlbar user interaction (e.g. typing a new url,
|
||||
* clicking on an url generated from a searchengine or a keyword, or a
|
||||
|
@ -146,13 +213,13 @@ var Manager = {
|
|||
let controller = input.popup.view.QueryInterface(Ci.nsIAutoCompleteController);
|
||||
let idx = input.popup.selectedIndex;
|
||||
|
||||
let tabTransistionData = {
|
||||
let tabTransitionData = {
|
||||
from_address_bar: true,
|
||||
};
|
||||
|
||||
if (idx < 0 || idx >= controller.matchCount) {
|
||||
// Recognize when no valid autocomplete results has been selected.
|
||||
tabTransistionData.typed = true;
|
||||
tabTransitionData.typed = true;
|
||||
} else {
|
||||
let value = controller.getValueAt(idx);
|
||||
let action = input._parseActionUrl(value);
|
||||
|
@ -161,21 +228,21 @@ var Manager = {
|
|||
// Detect keyword and generated and more typed scenarios.
|
||||
switch (action.type) {
|
||||
case "keyword":
|
||||
tabTransistionData.keyword = true;
|
||||
tabTransitionData.keyword = true;
|
||||
break;
|
||||
case "searchengine":
|
||||
case "searchsuggestion":
|
||||
tabTransistionData.generated = true;
|
||||
tabTransitionData.generated = true;
|
||||
break;
|
||||
case "visiturl":
|
||||
// Visiturl are autocompletion results related to
|
||||
// history suggestions.
|
||||
tabTransistionData.typed = true;
|
||||
tabTransitionData.typed = true;
|
||||
break;
|
||||
case "remotetab":
|
||||
// Remote tab are autocomplete results related to
|
||||
// tab urls from a remote synchronized Firefox.
|
||||
tabTransistionData.typed = true;
|
||||
tabTransitionData.typed = true;
|
||||
break;
|
||||
case "switchtab":
|
||||
// This "switchtab" autocompletion should be ignored, because
|
||||
|
@ -183,7 +250,7 @@ var Manager = {
|
|||
return;
|
||||
default:
|
||||
// Fallback on "typed" if unable to detect a known moz-action type.
|
||||
tabTransistionData.typed = true;
|
||||
tabTransitionData.typed = true;
|
||||
}
|
||||
} else {
|
||||
// Special handling for bookmark urlbar autocompletion
|
||||
|
@ -191,16 +258,16 @@ var Manager = {
|
|||
let styles = new Set(controller.getStyleAt(idx).split(/\s+/));
|
||||
|
||||
if (styles.has("bookmark")) {
|
||||
tabTransistionData.auto_bookmark = true;
|
||||
tabTransitionData.auto_bookmark = true;
|
||||
} else {
|
||||
// Fallback on "typed" if unable to detect a specific actionType
|
||||
// (and when in the styles there are "autofill" or "history").
|
||||
tabTransistionData.typed = true;
|
||||
tabTransitionData.typed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setRecentTabTransitionData(tabTransistionData);
|
||||
this.setRecentTabTransitionData(tabTransitionData);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче