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:
Mark Banner 2019-03-20 20:22:35 +00:00
Родитель 45e4b14a1a
Коммит da3d75151a
4 изменённых файлов: 162 добавлений и 37 удалений

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

@ -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);
}
},