summaryrefslogtreecommitdiffstats
path: root/toolkit/components/urlformatter
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/urlformatter')
-rw-r--r--toolkit/components/urlformatter/URLFormatter.sys.mjs194
-rw-r--r--toolkit/components/urlformatter/components.conf16
-rw-r--r--toolkit/components/urlformatter/moz.build26
-rw-r--r--toolkit/components/urlformatter/nsIURLFormatter.idl50
-rw-r--r--toolkit/components/urlformatter/tests/unit/head_urlformatter.js13
-rw-r--r--toolkit/components/urlformatter/tests/unit/test_urlformatter.js100
-rw-r--r--toolkit/components/urlformatter/tests/unit/xpcshell.toml5
7 files changed, 404 insertions, 0 deletions
diff --git a/toolkit/components/urlformatter/URLFormatter.sys.mjs b/toolkit/components/urlformatter/URLFormatter.sys.mjs
new file mode 100644
index 0000000000..fe94e14b2e
--- /dev/null
+++ b/toolkit/components/urlformatter/URLFormatter.sys.mjs
@@ -0,0 +1,194 @@
+/* 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/. */
+
+/**
+ * @class nsURLFormatterService
+ *
+ * nsURLFormatterService exposes methods to substitute variables in URL formats.
+ *
+ * Mozilla Applications linking to Mozilla websites are strongly encouraged to use
+ * URLs of the following format:
+ *
+ * http[s]://%SERVICE%.mozilla.[com|org]/%LOCALE%/
+ */
+
+import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+
+const PREF_APP_DISTRIBUTION = "distribution.id";
+const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ Region: "resource://gre/modules/Region.sys.mjs",
+ UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
+});
+
+export function nsURLFormatterService() {
+ ChromeUtils.defineLazyGetter(this, "ABI", function UFS_ABI() {
+ let ABI = "default";
+ try {
+ ABI = Services.appinfo.XPCOMABI;
+ } catch (e) {}
+
+ return ABI;
+ });
+
+ ChromeUtils.defineLazyGetter(this, "OSVersion", function UFS_OSVersion() {
+ let OSVersion = "default";
+ let { sysinfo } = Services;
+ try {
+ OSVersion =
+ sysinfo.getProperty("name") + " " + sysinfo.getProperty("version");
+ OSVersion += ` (${sysinfo.getProperty("secondaryLibrary")})`;
+ } catch (e) {}
+
+ return encodeURIComponent(OSVersion);
+ });
+
+ ChromeUtils.defineLazyGetter(
+ this,
+ "distribution",
+ function UFS_distribution() {
+ let defaults = Services.prefs.getDefaultBranch(null);
+ let id = defaults.getCharPref(PREF_APP_DISTRIBUTION, "default");
+ let version = defaults.getCharPref(
+ PREF_APP_DISTRIBUTION_VERSION,
+ "default"
+ );
+
+ return { id, version };
+ }
+ );
+}
+
+nsURLFormatterService.prototype = {
+ classID: Components.ID("{e6156350-2be8-11db-a98b-0800200c9a66}"),
+ QueryInterface: ChromeUtils.generateQI(["nsIURLFormatter"]),
+
+ _defaults: {
+ LOCALE: () => Services.locale.appLocaleAsBCP47,
+ REGION() {
+ try {
+ // When the geoip lookup failed to identify the region, we fallback to
+ // the 'ZZ' region code to mean 'unknown'.
+ return lazy.Region.home || "ZZ";
+ } catch (e) {
+ return "ZZ";
+ }
+ },
+ VENDOR() {
+ return Services.appinfo.vendor;
+ },
+ NAME() {
+ return Services.appinfo.name;
+ },
+ ID() {
+ return Services.appinfo.ID;
+ },
+ VERSION() {
+ return Services.appinfo.version;
+ },
+ MAJOR_VERSION() {
+ return Services.appinfo.version.replace(
+ /^([^\.]+\.[0-9]+[a-z]*).*/gi,
+ "$1"
+ );
+ },
+ APPBUILDID() {
+ return Services.appinfo.appBuildID;
+ },
+ PLATFORMVERSION() {
+ return Services.appinfo.platformVersion;
+ },
+ PLATFORMBUILDID() {
+ return Services.appinfo.platformBuildID;
+ },
+ APP() {
+ return Services.appinfo.name.toLowerCase().replace(/ /, "");
+ },
+ OS() {
+ return Services.appinfo.OS;
+ },
+ XPCOMABI() {
+ return this.ABI;
+ },
+ BUILD_TARGET() {
+ return Services.appinfo.OS + "_" + this.ABI;
+ },
+ OS_VERSION() {
+ return this.OSVersion;
+ },
+ CHANNEL: () => lazy.UpdateUtils.UpdateChannel,
+ MOZILLA_API_KEY: () => AppConstants.MOZ_MOZILLA_API_KEY,
+ GOOGLE_LOCATION_SERVICE_API_KEY: () =>
+ AppConstants.MOZ_GOOGLE_LOCATION_SERVICE_API_KEY,
+ GOOGLE_SAFEBROWSING_API_KEY: () =>
+ AppConstants.MOZ_GOOGLE_SAFEBROWSING_API_KEY,
+ BING_API_CLIENTID: () => AppConstants.MOZ_BING_API_CLIENTID,
+ BING_API_KEY: () => AppConstants.MOZ_BING_API_KEY,
+ DISTRIBUTION() {
+ return this.distribution.id;
+ },
+ DISTRIBUTION_VERSION() {
+ return this.distribution.version;
+ },
+ },
+
+ formatURL: function uf_formatURL(aFormat) {
+ var _this = this;
+ var replacementCallback = function (aMatch, aKey) {
+ if (aKey in _this._defaults) {
+ return _this._defaults[aKey].call(_this);
+ }
+ console.error("formatURL: Couldn't find value for key: ", aKey);
+ return aMatch;
+ };
+ return aFormat.replace(/%([A-Z_]+)%/g, replacementCallback);
+ },
+
+ formatURLPref: function uf_formatURLPref(aPref) {
+ var format = null;
+
+ try {
+ format = Services.prefs.getStringPref(aPref);
+ } catch (ex) {
+ console.error("formatURLPref: Couldn't get pref: ", aPref);
+ return "about:blank";
+ }
+
+ if (
+ !Services.prefs.prefHasUserValue(aPref) &&
+ /^(data:text\/plain,.+=.+|chrome:\/\/.+\/locale\/.+\.properties)$/.test(
+ format
+ )
+ ) {
+ // This looks as if it might be a localised preference
+ try {
+ format = Services.prefs.getComplexValue(
+ aPref,
+ Ci.nsIPrefLocalizedString
+ ).data;
+ } catch (ex) {}
+ }
+
+ return this.formatURL(format);
+ },
+
+ trimSensitiveURLs: function uf_trimSensitiveURLs(aMsg) {
+ // Only the google API keys is sensitive for now.
+ aMsg = AppConstants.MOZ_GOOGLE_LOCATION_SERVICE_API_KEY
+ ? aMsg.replace(
+ RegExp(AppConstants.MOZ_GOOGLE_LOCATION_SERVICE_API_KEY, "g"),
+ "[trimmed-google-api-key]"
+ )
+ : aMsg;
+ return AppConstants.MOZ_GOOGLE_SAFEBROWSING_API_KEY
+ ? aMsg.replace(
+ RegExp(AppConstants.MOZ_GOOGLE_SAFEBROWSING_API_KEY, "g"),
+ "[trimmed-google-api-key]"
+ )
+ : aMsg;
+ },
+};
diff --git a/toolkit/components/urlformatter/components.conf b/toolkit/components/urlformatter/components.conf
new file mode 100644
index 0000000000..887072f553
--- /dev/null
+++ b/toolkit/components/urlformatter/components.conf
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+ {
+ 'js_name': 'urlFormatter',
+ 'cid': '{e6156350-2be8-11db-a98b-0800200c9a66}',
+ 'contract_ids': ['@mozilla.org/toolkit/URLFormatterService;1'],
+ 'interfaces': ['nsIURLFormatter'],
+ 'esModule': 'resource://gre/modules/URLFormatter.sys.mjs',
+ 'constructor': 'nsURLFormatterService',
+ },
+]
diff --git a/toolkit/components/urlformatter/moz.build b/toolkit/components/urlformatter/moz.build
new file mode 100644
index 0000000000..e9f1c92bf3
--- /dev/null
+++ b/toolkit/components/urlformatter/moz.build
@@ -0,0 +1,26 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Toolkit", "General")
+
+XPCSHELL_TESTS_MANIFESTS += ["tests/unit/xpcshell.toml"]
+
+XPIDL_SOURCES += [
+ "nsIURLFormatter.idl",
+]
+
+XPIDL_MODULE = "urlformatter"
+
+EXTRA_JS_MODULES += [
+ "URLFormatter.sys.mjs",
+]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+DEFINES["OBJDIR"] = OBJDIR
diff --git a/toolkit/components/urlformatter/nsIURLFormatter.idl b/toolkit/components/urlformatter/nsIURLFormatter.idl
new file mode 100644
index 0000000000..ee45e18ec1
--- /dev/null
+++ b/toolkit/components/urlformatter/nsIURLFormatter.idl
@@ -0,0 +1,50 @@
+/* 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/. */
+
+/**
+ * nsIURLFormatter
+ *
+ * nsIURLFormatter exposes methods to substitute variables in URL formats.
+ * Variable names can contain 'A-Z' letters and '_' characters.
+ *
+ * Mozilla Applications linking to Mozilla websites are strongly encouraged to use
+ * URLs of the following format:
+ *
+ * http[s]://%SERVICE%.mozilla.[com|org]/%LOCALE%/
+ */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(4ab31d30-372d-11db-a98b-0800200c9a66)]
+interface nsIURLFormatter: nsISupports
+{
+ /**
+ * formatURL - Formats a string URL
+ *
+ * The set of known variables is predefined.
+ * If a variable is unknown, it is left unchanged and a non-fatal error is reported.
+ *
+ * @param aFormat string Unformatted URL.
+ *
+ * @return The formatted URL.
+ */
+ AString formatURL(in AString aFormat);
+
+ /**
+ * formatURLPref - Formats a string URL stored in a preference
+ *
+ * If the preference value cannot be retrieved, a fatal error is reported
+ * and the "about:blank" URL is returned.
+ *
+ * @param aPref string Preference name.
+ *
+ * @return The formatted URL returned by formatURL(), or "about:blank".
+ */
+ AString formatURLPref(in AString aPref);
+
+ /**
+ * Remove all of the sensitive query parameter strings from URLs in |aMsg|.
+ */
+ AString trimSensitiveURLs(in AString aMsg);
+};
diff --git a/toolkit/components/urlformatter/tests/unit/head_urlformatter.js b/toolkit/components/urlformatter/tests/unit/head_urlformatter.js
new file mode 100644
index 0000000000..35d5b11b8c
--- /dev/null
+++ b/toolkit/components/urlformatter/tests/unit/head_urlformatter.js
@@ -0,0 +1,13 @@
+/* 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/. */
+const { getAppInfo, updateAppInfo } = ChromeUtils.importESModule(
+ "resource://testing-common/AppInfo.sys.mjs"
+);
+updateAppInfo({
+ name: "Url Formatter Test",
+ ID: "urlformattertest@test.mozilla.org",
+ version: "1",
+ platformVersion: "2.0",
+});
+var gAppInfo = getAppInfo();
diff --git a/toolkit/components/urlformatter/tests/unit/test_urlformatter.js b/toolkit/components/urlformatter/tests/unit/test_urlformatter.js
new file mode 100644
index 0000000000..c6a0c669c3
--- /dev/null
+++ b/toolkit/components/urlformatter/tests/unit/test_urlformatter.js
@@ -0,0 +1,100 @@
+/* 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/. */
+
+function run_test() {
+ var formatter = Services.urlFormatter;
+ var locale = Services.locale.appLocaleAsBCP47;
+ var OSVersion =
+ Services.sysinfo.getProperty("name") +
+ " " +
+ Services.sysinfo.getProperty("version");
+ try {
+ OSVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
+ } catch (e) {}
+ OSVersion = encodeURIComponent(OSVersion);
+ var abi = Services.appinfo.XPCOMABI;
+
+ let defaults = Services.prefs.getDefaultBranch(null);
+ let channel = defaults.getCharPref("app.update.channel", "default");
+
+ // Set distribution values.
+ defaults.setCharPref("distribution.id", "bacon");
+ defaults.setCharPref("distribution.version", "1.0");
+
+ var upperUrlRaw =
+ "http://%LOCALE%.%VENDOR%.foo/?name=%NAME%&id=%ID%&version=%VERSION%&platversion=%PLATFORMVERSION%&abid=%APPBUILDID%&pbid=%PLATFORMBUILDID%&app=%APP%&os=%OS%&abi=%XPCOMABI%";
+ var lowerUrlRaw =
+ "http://%locale%.%vendor%.foo/?name=%name%&id=%id%&version=%version%&platversion=%platformversion%&abid=%appbuildid%&pbid=%platformbuildid%&app=%app%&os=%os%&abi=%xpcomabi%";
+ // XXX %APP%'s RegExp is not global, so it only replaces the first space
+ var ulUrlRef =
+ "http://" +
+ locale +
+ ".Mozilla.foo/?name=Url Formatter Test&id=urlformattertest@test.mozilla.org&version=1&platversion=2.0&abid=" +
+ gAppInfo.appBuildID +
+ "&pbid=" +
+ gAppInfo.platformBuildID +
+ "&app=urlformatter test&os=XPCShell&abi=" +
+ abi;
+ var multiUrl = "http://%VENDOR%.%VENDOR%.%NAME%.%VENDOR%.%NAME%";
+ var multiUrlRef =
+ "http://Mozilla.Mozilla.Url Formatter Test.Mozilla.Url Formatter Test";
+ var encodedUrl =
+ "https://%LOCALE%.%VENDOR%.foo/?q=%E3%82%BF%E3%83%96&app=%NAME%&ver=%PLATFORMVERSION%";
+ var encodedUrlRef =
+ "https://" +
+ locale +
+ ".Mozilla.foo/?q=%E3%82%BF%E3%83%96&app=Url Formatter Test&ver=2.0";
+ var advancedUrl =
+ "http://test.mozilla.com/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/";
+ var advancedUrlRef =
+ "http://test.mozilla.com/Url Formatter Test/1/" +
+ gAppInfo.appBuildID +
+ "/XPCShell_" +
+ abi +
+ "/" +
+ locale +
+ "/" +
+ channel +
+ "/" +
+ OSVersion +
+ "/bacon/1.0/";
+
+ var pref = "xpcshell.urlformatter.test";
+ Services.prefs.setStringPref(pref, upperUrlRaw);
+
+ Assert.equal(formatter.formatURL(upperUrlRaw), ulUrlRef);
+ Assert.equal(formatter.formatURLPref(pref), ulUrlRef);
+ // Keys must be uppercase
+ Assert.notEqual(formatter.formatURL(lowerUrlRaw), ulUrlRef);
+ Assert.equal(formatter.formatURL(multiUrl), multiUrlRef);
+ // Encoded strings must be kept as is (Bug 427304)
+ Assert.equal(formatter.formatURL(encodedUrl), encodedUrlRef);
+
+ Assert.equal(formatter.formatURL(advancedUrl), advancedUrlRef);
+
+ for (let val of [
+ "MOZILLA_API_KEY",
+ "GOOGLE_LOCATION_SERVICE_API_KEY",
+ "GOOGLE_SAFEBROWSING_API_KEY",
+ "BING_API_CLIENTID",
+ "BING_API_KEY",
+ ]) {
+ let url = "http://test.mozilla.com/?val=%" + val + "%";
+ Assert.notEqual(formatter.formatURL(url), url);
+ }
+
+ let url_sb =
+ "http://test.mozilla.com/%GOOGLE_SAFEBROWSING_API_KEY%/?val=%GOOGLE_SAFEBROWSING_API_KEY%";
+ Assert.equal(
+ formatter.trimSensitiveURLs(formatter.formatURL(url_sb)),
+ "http://test.mozilla.com/[trimmed-google-api-key]/?val=[trimmed-google-api-key]"
+ );
+
+ let url_gls =
+ "http://test.mozilla.com/%GOOGLE_LOCATION_SERVICE_API_KEY%/?val=%GOOGLE_LOCATION_SERVICE_API_KEY%";
+ Assert.equal(
+ formatter.trimSensitiveURLs(formatter.formatURL(url_gls)),
+ "http://test.mozilla.com/[trimmed-google-api-key]/?val=[trimmed-google-api-key]"
+ );
+}
diff --git a/toolkit/components/urlformatter/tests/unit/xpcshell.toml b/toolkit/components/urlformatter/tests/unit/xpcshell.toml
new file mode 100644
index 0000000000..5c91088e7c
--- /dev/null
+++ b/toolkit/components/urlformatter/tests/unit/xpcshell.toml
@@ -0,0 +1,5 @@
+[DEFAULT]
+head = "head_urlformatter.js"
+skip-if = ["os == 'android'"]
+
+["test_urlformatter.js"]