From b5b19652b20d2909e5497a0d246346ce1e7c3348 Mon Sep 17 00:00:00 2001 From: Nicolas Ouellet-Payeur Date: Fri, 21 Sep 2018 00:31:02 +0000 Subject: [PATCH] Bug 462674 - URLBar: Autocomplete "about:" URLs r=mak Pages that are whitelisted for displaying on about:about can be autocompleted in the URL bar. MozReview-Commit-ID: BYhWUImyiJH Differential Revision: https://phabricator.services.mozilla.com/D3072 --HG-- extra : moz-landing-system : lando --- toolkit/components/places/UnifiedComplete.js | 54 ++++++++++++++++- .../test_autofill_about_urls.js | 60 +++++++++++++++++++ .../tests/unifiedcomplete/test_visit_url.js | 4 +- .../places/tests/unifiedcomplete/xpcshell.ini | 1 + toolkit/content/aboutAbout.js | 34 ++--------- toolkit/modules/AboutPagesUtils.jsm | 39 ++++++++++++ toolkit/modules/moz.build | 1 + tools/lint/eslint/modules.json | 1 + 8 files changed, 162 insertions(+), 32 deletions(-) create mode 100644 toolkit/components/places/tests/unifiedcomplete/test_autofill_about_urls.js create mode 100644 toolkit/modules/AboutPagesUtils.jsm diff --git a/toolkit/components/places/UnifiedComplete.js b/toolkit/components/places/UnifiedComplete.js index 66ef6d3dd9b5..b556b13c2cbc 100644 --- a/toolkit/components/places/UnifiedComplete.js +++ b/toolkit/components/places/UnifiedComplete.js @@ -334,6 +334,7 @@ ChromeUtils.import("resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]); XPCOMUtils.defineLazyModuleGetters(this, { + AboutPagesUtils: "resource://gre/modules/AboutPagesUtils.jsm", BrowserUtils: "resource://gre/modules/BrowserUtils.jsm", ExtensionSearchHandler: "resource://gre/modules/ExtensionSearchHandler.jsm", OS: "resource://gre/modules/osfile.jsm", @@ -1020,6 +1021,8 @@ Search.prototype = { // may be really slow and we may end up showing old results for too long. this._cleanUpNonCurrentMatches(UrlbarUtils.MATCHTYPE.GENERAL); + this._matchAboutPages(); + // If we do not have enough results, and our match type is // MATCH_BOUNDARY_ANYWHERE, search again with MATCH_ANYWHERE to get more // results. @@ -1043,6 +1046,46 @@ Search.prototype = { await extensionsCompletePromise; }, + _shouldMatchAboutPages() { + // Only autocomplete input that starts with 'about:' and has at least 1 more + // character. + return (this._strippedPrefix == "about:" && + this._searchString); + }, + + _matchAboutPages() { + if (!this._shouldMatchAboutPages()) { + return; + } + for (const url of AboutPagesUtils.visibleAboutUrls) { + if (url.startsWith(`about:${this._searchString}`)) { + this._addMatch({ + value: url, + comment: url, + frecency: FRECENCY_DEFAULT, + }); + } + } + }, + + _matchAboutPageForAutofill() { + if (!this._shouldMatchAboutPages()) { + return false; + } + for (const url of AboutPagesUtils.visibleAboutUrls) { + if (url.startsWith(`about:${this._searchString}`)) { + this._addAutofillMatch( + url.replace(/^about:/, ""), + url, + Infinity, + [] + ); + return true; + } + } + return false; + }, + async _checkPreloadedSitesExpiry() { if (!UrlbarPrefs.get("usepreloadedtopurls.enabled")) return; @@ -1138,6 +1181,15 @@ Search.prototype = { } let shouldAutofill = this._shouldAutofill; + + if (this.pending && shouldAutofill) { + // It may also look like an about: link. + let matched = await this._matchAboutPageForAutofill(); + if (matched) { + return true; + } + } + if (this.pending && shouldAutofill) { // It may also look like a URL we know from the database. let matched = await this._matchKnownUrl(conn); @@ -2154,7 +2206,7 @@ Search.prototype = { if (this._searchTokens.length != 1) return false; - // autoFill can only cope with history or bookmarks entries. + // autoFill can only cope with history, bookmarks, and about: entries. if (!this.hasBehavior("history") && !this.hasBehavior("bookmark")) return false; diff --git a/toolkit/components/places/tests/unifiedcomplete/test_autofill_about_urls.js b/toolkit/components/places/tests/unifiedcomplete/test_autofill_about_urls.js new file mode 100644 index 000000000000..b1a190528b85 --- /dev/null +++ b/toolkit/components/places/tests/unifiedcomplete/test_autofill_about_urls.js @@ -0,0 +1,60 @@ +/* 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/. */ + +"use strict"; + +// "about:ab" should match "about:about" +add_task(async function aboutAb() { + await check_autocomplete({ + search: "about:ab", + autofilled: "about:ab", + completed: "about:ab", + matches: [{ + value: "about:about", + comment: "about:about", + style: ["autofill", "heuristic"], + }], + }); +}); + +// "about:about" should match "about:about" +add_task(async function aboutAbout() { + await check_autocomplete({ + search: "about:about", + autofilled: "about:about", + completed: "about:about", + matches: [{ + value: "about:about", + comment: "about:about", + style: ["autofill", "heuristic"], + }], + }); +}); + +// "about:a" should complete to "about:about" and also match "about:addons" +add_task(async function aboutAboutAndAboutAddons() { + await check_autocomplete({ + search: "about:a", + autofilled: "about:a", + completed: "about:a", + matches: [{ + value: "about:about", + comment: "about:about", + style: ["autofill", "heuristic"], + }, { + value: "about:addons", + comment: "about:addons", + }], + }); +}); + +// "about:" should *not* match anything +add_task(async function aboutColonHasNoMatch() { + await check_autocomplete({ + search: "about:", + autofilled: "about:", + completed: "about:", + matches: [], + }); +}); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_visit_url.js b/toolkit/components/places/tests/unifiedcomplete/test_visit_url.js index ab9c507bddf3..e945f9040719 100644 --- a/toolkit/components/places/tests/unifiedcomplete/test_visit_url.js +++ b/toolkit/components/places/tests/unifiedcomplete/test_visit_url.js @@ -49,9 +49,9 @@ add_task(async function() { info("visit url, about: protocol (no host)"); await check_autocomplete({ - search: "about:config", + search: "about:nonexistent", searchParam: "enable-actions", - matches: [ { uri: makeActionURI("visiturl", {url: "about:config", input: "about:config"}), title: "about:config", style: [ "action", "visiturl", "heuristic" ] } ], + matches: [ { uri: makeActionURI("visiturl", {url: "about:nonexistent", input: "about:nonexistent"}), title: "about:nonexistent", style: [ "action", "visiturl", "heuristic" ] } ], }); info("visit url, with non-standard whitespace"); diff --git a/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini b/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini index 0380d1a69187..0e90b0603393 100644 --- a/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini +++ b/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini @@ -19,6 +19,7 @@ support-files = [test_adaptive_limited.js] [test_autocomplete_functional.js] [test_autocomplete_stopSearch_no_throw.js] +[test_autofill_about_urls.js] [test_autofill_origins.js] [test_autofill_search_engines.js] [test_autofill_urls.js] diff --git a/toolkit/content/aboutAbout.js b/toolkit/content/aboutAbout.js index ff05d9d563ce..3f271b6e277c 100644 --- a/toolkit/content/aboutAbout.js +++ b/toolkit/content/aboutAbout.js @@ -2,44 +2,20 @@ * 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/. */ -ChromeUtils.import("resource://gre/modules/Services.jsm"); +ChromeUtils.import("resource://gre/modules/AboutPagesUtils.jsm"); -var gProtocols = []; var gContainer; window.onload = function() { gContainer = document.getElementById("abouts"); - findAbouts(); + AboutPagesUtils.visibleAboutUrls.forEach(createProtocolListing); }; -function findAbouts() { - for (var cid in Cc) { - var result = cid.match(/@mozilla.org\/network\/protocol\/about;1\?what\=(.*)$/); - if (result) { - var aboutType = result[1]; - var contract = "@mozilla.org/network/protocol/about;1?what=" + aboutType; - try { - var am = Cc[contract].getService(Ci.nsIAboutModule); - var uri = Services.io.newURI("about:" + aboutType); - var flags = am.getURIFlags(uri); - if (!(flags & Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT)) { - gProtocols.push(aboutType); - } - } catch (e) { - // getService might have thrown if the component doesn't actually - // implement nsIAboutModule - } - } - } - gProtocols.sort().forEach(createProtocolListing); -} - -function createProtocolListing(aProtocol) { - var uri = "about:" + aProtocol; +function createProtocolListing(aUrl) { var li = document.createElement("li"); var link = document.createElement("a"); - var text = document.createTextNode(uri); + var text = document.createTextNode(aUrl); - link.href = uri; + link.href = aUrl; link.appendChild(text); li.appendChild(link); gContainer.appendChild(li); diff --git a/toolkit/modules/AboutPagesUtils.jsm b/toolkit/modules/AboutPagesUtils.jsm new file mode 100644 index 000000000000..93f73a1931a8 --- /dev/null +++ b/toolkit/modules/AboutPagesUtils.jsm @@ -0,0 +1,39 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * 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/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ["AboutPagesUtils"]; + +ChromeUtils.import("resource://gre/modules/Services.jsm"); +ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); + +const AboutPagesUtils = {}; + +XPCOMUtils.defineLazyGetter(AboutPagesUtils, "visibleAboutUrls", () => { + const urls = []; + const rx = /@mozilla.org\/network\/protocol\/about;1\?what\=(.*)$/; + for (const cid in Cc) { + const result = cid.match(rx); + if (!result) { + continue; + } + const [, aboutType] = result; + try { + const am = Cc[cid].getService(Ci.nsIAboutModule); + const uri = Services.io.newURI(`about:${aboutType}`); + const flags = am.getURIFlags(uri); + if (!(flags & Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT)) { + urls.push(`about:${aboutType}`); + } + } catch (e) { + // getService() might have thrown if the component doesn't actually + // implement nsIAboutModule + } + } + urls.sort(); + return urls; +}); diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build index 64ef223c508e..5c5ce5334f9c 100644 --- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -166,6 +166,7 @@ with Files('docs/**'): SCHEDULES.exclusive = ['docs'] EXTRA_JS_MODULES += [ + 'AboutPagesUtils.jsm', 'ActorChild.jsm', 'ActorManagerChild.jsm', 'ActorManagerParent.jsm', diff --git a/tools/lint/eslint/modules.json b/tools/lint/eslint/modules.json index 8a702ab5820a..0e42fb431f72 100644 --- a/tools/lint/eslint/modules.json +++ b/tools/lint/eslint/modules.json @@ -1,4 +1,5 @@ { + "AboutPagesUtils.jsm": ["AboutPagesUtils"], "AddonManager.jsm": ["AddonManager", "AddonManagerPrivate"], "addons.js": ["AddonsEngine", "AddonValidator"], "addons.jsm": ["Addon", "STATE_ENABLED", "STATE_DISABLED"],