Bug 1452533 - JsonSchemaValidator converts URL types into URL objects instead of nsIURIs r?felipe draft
authorKanika Saini
Tue, 05 Jun 2018 19:20:40 +0530
changeset 808621 d7a4199c88779965bddf114ef32b32ecdf01063f
parent 808264 1e2c9151a09e43613a79daa8d4a94dc3e314020c
push id113442
push userbmo:kanika16047@iiitd.ac.in
push dateWed, 20 Jun 2018 06:53:16 +0000
reviewersfelipe
bugs1452533
milestone62.0a1
Bug 1452533 - JsonSchemaValidator converts URL types into URL objects instead of nsIURIs r?felipe MozReview-Commit-ID: 6b6d90sCeEa
browser/components/enterprisepolicies/Policies.jsm
browser/components/enterprisepolicies/helpers/BookmarksPolicies.jsm
browser/components/enterprisepolicies/helpers/ProxyPolicies.jsm
browser/components/enterprisepolicies/tests/browser/browser_policy_bookmarks.js
browser/components/enterprisepolicies/tests/browser/browser_policy_proxy.js
toolkit/components/utils/JsonSchemaValidator.jsm
toolkit/components/utils/test/browser/browser_JsonSchemaValidator.js
--- a/browser/components/enterprisepolicies/Policies.jsm
+++ b/browser/components/enterprisepolicies/Policies.jsm
@@ -122,20 +122,20 @@ var Policies = {
     }
   },
 
   "Cookies": {
     onBeforeUIStartup(manager, param) {
       addAllowDenyPermissions("cookie", param.Allow, param.Block);
 
       if (param.Block) {
-        const hosts = param.Block.map(uri => uri.host).sort().join("\n");
+        const hosts = param.Block.map(url => url.hostname).sort().join("\n");
         runOncePerModification("clearCookiesForBlockedHosts", hosts, () => {
           for (let blocked of param.Block) {
-            Services.cookies.removeCookiesWithOriginAttributes("{}", blocked.host);
+            Services.cookies.removeCookiesWithOriginAttributes("{}", blocked.hostname);
           }
         });
       }
 
       if (param.Default !== undefined ||
           param.AcceptThirdParty !== undefined ||
           param.Locked) {
         const ACCEPT_COOKIES = 0;
@@ -503,19 +503,19 @@ var Policies = {
     }
   },
 
   "Homepage": {
     onBeforeUIStartup(manager, param) {
       // |homepages| will be a string containing a pipe-separated ('|') list of
       // URLs because that is what the "Home page" section of about:preferences
       // (and therefore what the pref |browser.startup.homepage|) accepts.
-      let homepages = param.URL.spec;
+      let homepages = param.URL.href;
       if (param.Additional && param.Additional.length > 0) {
-        homepages += "|" + param.Additional.map(url => url.spec).join("|");
+        homepages += "|" + param.Additional.map(url => url.href).join("|");
       }
       if (param.Locked) {
         setAndLockPref("browser.startup.homepage", homepages);
         setAndLockPref("browser.startup.page", 1);
         setAndLockPref("pref.browser.homepage.disable_button.current_page", true);
         setAndLockPref("pref.browser.homepage.disable_button.bookmark_page", true);
         setAndLockPref("pref.browser.homepage.disable_button.restore_default", true);
       } else {
@@ -558,24 +558,24 @@ var Policies = {
   "OfferToSaveLogins": {
     onBeforeUIStartup(manager, param) {
       setAndLockPref("signon.rememberSignons", param);
     }
   },
 
   "OverrideFirstRunPage": {
     onProfileAfterChange(manager, param) {
-      let url = param ? param.spec : "";
+      let url = param ? param.href : "";
       setAndLockPref("startup.homepage_welcome_url", url);
     }
   },
 
   "OverridePostUpdatePage": {
     onProfileAfterChange(manager, param) {
-      let url = param ? param.spec : "";
+      let url = param ? param.href : "";
       setAndLockPref("startup.homepage_override_url", url);
       // The pref startup.homepage_override_url is only used
       // as a fallback when the update.xml file hasn't provided
       // a specific post-update URL.
       manager.disallowFeature("postUpdateCustomPage");
     }
   },
 
@@ -852,28 +852,28 @@ function setDefaultPermission(policyName
  *        The list of URLs to be set as DENY_ACTION for the chosen permission.
  */
 function addAllowDenyPermissions(permissionName, allowList, blockList) {
   allowList = allowList || [];
   blockList = blockList || [];
 
   for (let origin of allowList) {
     try {
-      Services.perms.add(origin,
+      Services.perms.add(Services.io.newURI(origin.href),
                          permissionName,
                          Ci.nsIPermissionManager.ALLOW_ACTION,
                          Ci.nsIPermissionManager.EXPIRE_POLICY);
     } catch (ex) {
       log.error(`Added by default for ${permissionName} permission in the permission
-      manager - ${origin.spec}`);
+      manager - ${origin.href}`);
     }
   }
 
   for (let origin of blockList) {
-    Services.perms.add(origin,
+    Services.perms.add(Services.io.newURI(origin.href),
                        permissionName,
                        Ci.nsIPermissionManager.DENY_ACTION,
                        Ci.nsIPermissionManager.EXPIRE_POLICY);
   }
 }
 
 /**
  * runOnce
--- a/browser/components/enterprisepolicies/helpers/BookmarksPolicies.jsm
+++ b/browser/components/enterprisepolicies/helpers/BookmarksPolicies.jsm
@@ -3,43 +3,43 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 /*
  * A Bookmark object received through the policy engine will be an
  * object with the following properties:
  *
- * - URL (nsIURI)
+ * - URL (URL)
  *   (required) The URL for this bookmark
  *
  * - Title (string)
  *   (required) The title for this bookmark
  *
  * - Placement (string)
  *   (optional) Either "toolbar" or "menu". If missing or invalid,
  *   "toolbar" will be used
  *
  * - Folder (string)
  *   (optional) The name of the folder to put this bookmark into.
  *   If present, a folder with this name will be created in the
  *   chosen placement above, and the bookmark will be created there.
  *   If missing, the bookmark will be created directly into the
  *   chosen placement.
  *
- * - Favicon (nsIURI)
+ * - Favicon (URL)
  *   (optional) An http:, https: or data: URL with the favicon.
  *   If possible, we recommend against using this property, in order
  *   to keep the json file small.
  *   If a favicon is not provided through the policy, it will be loaded
  *   naturally after the user first visits the bookmark.
  *
  *
  * Note: The Policy Engine automatically converts the strings given to
- * the URL and favicon properties into a nsIURI object.
+ * the URL and favicon properties into a URL object.
  *
  * The schema for this object is defined in policies-schema.json.
  */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 ChromeUtils.defineModuleGetter(this, "PlacesUtils",
@@ -104,17 +104,17 @@ async function calculateLists(specifiedB
   // --------- STEP 1 ---------
   // Build two Maps (one with the existing bookmarks, another with
   // the specified bookmarks), to make iteration quicker.
 
   // LIST A
   // MAP of url (string) -> bookmarks objects from the Policy Engine
   let specifiedBookmarksMap = new Map();
   for (let bookmark of specifiedBookmarks) {
-    specifiedBookmarksMap.set(bookmark.URL.spec, bookmark);
+    specifiedBookmarksMap.set(bookmark.URL.href, bookmark);
   }
 
   // LIST B
   // MAP of url (string) -> bookmarks objects from Places
   let existingBookmarksMap = new Map();
   await PlacesUtils.bookmarks.fetch(
     { guidPrefix: BookmarksPolicies.BOOKMARK_GUID_PREFIX },
     (bookmark) => existingBookmarksMap.set(bookmark.url.href, bookmark)
@@ -188,60 +188,60 @@ async function calculateLists(specifiedB
   };
 }
 
 async function insertBookmark(bookmark) {
   let parentGuid = await getParentGuid(bookmark.Placement,
                                        bookmark.Folder);
 
   await PlacesUtils.bookmarks.insert({
-    url: bookmark.URL,
+    url: Services.io.newURI(bookmark.URL.href),
     title: bookmark.Title,
     guid: generateGuidWithPrefix(BookmarksPolicies.BOOKMARK_GUID_PREFIX),
     parentGuid,
   });
 
   if (bookmark.Favicon) {
     await setFaviconForBookmark(bookmark).catch(
       () => log.error(`Error setting favicon for ${bookmark.Title}`));
   }
 }
 
 async function setFaviconForBookmark(bookmark) {
   let faviconURI;
   let nullPrincipal = Services.scriptSecurityManager.createNullPrincipal({});
 
-  switch (bookmark.Favicon.scheme) {
-    case "data":
+  switch (bookmark.Favicon.protocol) {
+    case "data:":
       // data urls must first call replaceFaviconDataFromDataURL, using a
       // fake URL. Later, it's needed to call setAndFetchFaviconForPage
       // with the same URL.
-      faviconURI = Services.io.newURI("fake-favicon-uri:" + bookmark.URL.spec);
+      faviconURI = Services.io.newURI("fake-favicon-uri:" + bookmark.URL.href);
 
       PlacesUtils.favicons.replaceFaviconDataFromDataURL(
         faviconURI,
-        bookmark.Favicon.spec,
+        bookmark.Favicon.href,
         0, /* max expiration length */
         nullPrincipal
       );
       break;
 
-    case "http":
-    case "https":
-      faviconURI = bookmark.Favicon;
+    case "http:":
+    case "https:":
+      faviconURI = Services.io.newURI(bookmark.Favicon.href);
       break;
 
     default:
       log.error(`Bad URL given for favicon on bookmark "${bookmark.Title}"`);
       return Promise.resolve();
   }
 
   return new Promise(resolve => {
     PlacesUtils.favicons.setAndFetchFaviconForPage(
-      bookmark.URL,
+      Services.io.newURI(bookmark.URL.href),
       faviconURI,
       false, /* forceReload */
       PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
       resolve,
       nullPrincipal
     );
   });
 }
--- a/browser/components/enterprisepolicies/helpers/ProxyPolicies.jsm
+++ b/browser/components/enterprisepolicies/helpers/ProxyPolicies.jsm
@@ -35,17 +35,17 @@ var EXPORTED_SYMBOLS = [ "ProxyPolicies"
 
 var ProxyPolicies = {
   configureProxySettings(param, setPref) {
     if (param.Mode) {
       setPref("network.proxy.type", PROXY_TYPES_MAP.get(param.Mode));
     }
 
     if (param.AutoConfigURL) {
-      setPref("network.proxy.autoconfig_url", param.AutoConfigURL.spec);
+      setPref("network.proxy.autoconfig_url", param.AutoConfigURL.href);
     }
 
     if (param.UseProxyForDNS !== undefined) {
       setPref("network.proxy.socks_remote_dns", param.UseProxyForDNS);
     }
 
     if (param.AutoLogin !== undefined) {
       setPref("signon.autologin.proxy", param.AutoLogin);
--- a/browser/components/enterprisepolicies/tests/browser/browser_policy_bookmarks.js
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_bookmarks.js
@@ -10,17 +10,17 @@ ChromeUtils.import("resource://gre/modul
 const FAVICON_DATA = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwMDAsTBZbkNwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABNElEQVQ4y8WSsU0DURBE3yyWIaAJaqAAN4DPSL6AlIACKIEOyJEgRsIgOOkiInJqgAKowNg7BHdn7MOksNl+zZ//dvbDf5cAiklp22BdVtXdeTEpDYDB9m1VzU6OJuVp2NdEQCaI96fH2YHG4+mDduKYNMYINTcjcGbXzQVDEAphG0k48zUsajIbnAiMIXThpW8EICE0RAK4dvoKg9NIcTiQ589otyHOZLnwqK5nLwBFUZ4igc3iM0d1ff8CMC6mZ6Ihiaqq3gi1aUAnArD00SW1fq5OLBg0ymYmSZsR2/t4e/rGyCLW0sbp3oq+yTYqVgytQWui2FS7XYF7GFprY921T4CNQt8zr47dNzCkIX7y/jBtH+v+RGMQrc828W8pApnZbmEVQp/Ae7BlOy2ttib81/UFc+WRWEbjckIAAAAASUVORK5CYII=";
 
 const { BookmarksPolicies } = ChromeUtils.import("resource:///modules/policies/BookmarksPolicies.jsm", {});
 
 let CURRENT_POLICY;
 
 const BASE_POLICY = {
   "policies": {
-    "display_bookmarks_toolbar": true,
+    "DisplayBookmarksToolbar": true,
     "Bookmarks": [
       {
         "Title": "Bookmark 1",
         "URL": "https://bookmark1.example.com/",
         "Favicon": FAVICON_DATA
       },
       {
         "Title": "Bookmark 2",
--- a/browser/components/enterprisepolicies/tests/browser/browser_policy_proxy.js
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_proxy.js
@@ -11,19 +11,19 @@ add_task(async function test_proxy_modes
   let { ProxyPolicies, PROXY_TYPES_MAP } = ChromeUtils.import("resource:///modules/policies/ProxyPolicies.jsm", {});
 
   for (let [mode, expectedValue] of PROXY_TYPES_MAP) {
     ProxyPolicies.configureProxySettings({Mode: mode}, (_, value) => {
       is(value, expectedValue, "Correct proxy mode");
     });
   }
 
-  let autoconfigURL = Services.io.newURI("data:text/plain,test");
+  let autoconfigURL = new URL("data:text/plain,test");
   ProxyPolicies.configureProxySettings({AutoConfigURL: autoconfigURL}, (_, value) => {
-    is(value, autoconfigURL.spec, "AutoconfigURL correctly set");
+    is(value, autoconfigURL.href, "AutoconfigURL correctly set");
   });
 });
 
 add_task(async function test_proxy_boolean_settings() {
   // Tests that both false and true values are correctly set and locked
   await setupPolicyEngineWithJson({
     "policies": {
       "Proxy": {
--- a/toolkit/components/utils/JsonSchemaValidator.jsm
+++ b/toolkit/components/utils/JsonSchemaValidator.jsm
@@ -12,16 +12,18 @@
  * - The boolean type accepts (and casts) 0 and 1 as valid values.
  */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
+
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
   return new ConsoleAPI({
     prefix: "JsonSchemaValidator.jsm",
     // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
     // messages during development. See LOG_LEVELS in Console.jsm for details.
     maxLogLevel: "error",
   });
@@ -151,19 +153,19 @@ function validateAndParseSimpleParam(par
       break;
 
     case "origin":
       if (typeof(param) != "string") {
         break;
       }
 
       try {
-        parsedParam = Services.io.newURI(param);
+        parsedParam = new URL(param);
 
-        let pathQueryRef = parsedParam.pathQueryRef;
+        let pathQueryRef = parsedParam.pathname + parsedParam.hash;
         // Make sure that "origin" types won't accept full URLs.
         if (pathQueryRef != "/" && pathQueryRef != "") {
           valid = false;
         } else {
           valid = true;
         }
       } catch (ex) {
         valid = false;
@@ -177,17 +179,17 @@ function validateAndParseSimpleParam(par
       }
 
       if (type == "URLorEmpty" && param === "") {
         valid = true;
         break;
       }
 
       try {
-        parsedParam = Services.io.newURI(param);
+        parsedParam = new URL(param);
         valid = true;
       } catch (ex) {
         valid = false;
       }
       break;
   }
 
   return [valid, parsedParam];
--- a/toolkit/components/utils/test/browser/browser_JsonSchemaValidator.js
+++ b/toolkit/components/utils/test/browser/browser_JsonSchemaValidator.js
@@ -85,38 +85,38 @@ add_task(async function test_string_valu
 add_task(async function test_URL_values() {
   let schema = {
     type: "URL"
   };
 
   let valid, parsed;
   [valid, parsed] = JsonSchemaValidator.validateAndParseParameters("https://www.example.com/foo#bar", schema);
   ok(valid, "URL is valid");
-  ok(parsed instanceof Ci.nsIURI, "parsed is a nsIURI");
-  is(parsed.prePath, "https://www.example.com", "prePath is correct");
-  is(parsed.pathQueryRef, "/foo#bar", "pathQueryRef is correct");
+  ok(parsed instanceof URL, "parsed is a URL");
+  is(parsed.origin, "https://www.example.com", "origin is correct");
+  is(parsed.pathname + parsed.hash, "/foo#bar", "pathname is correct");
 
   // Invalid values:
   ok(!JsonSchemaValidator.validateAndParseParameters("", schema)[0], "Empty string is not accepted for URL");
   ok(!JsonSchemaValidator.validateAndParseParameters("www.example.com", schema)[0], "Scheme is required for URL");
   ok(!JsonSchemaValidator.validateAndParseParameters("https://:!$%", schema)[0], "Invalid URL");
   ok(!JsonSchemaValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
 });
 
 add_task(async function test_URLorEmpty_values() {
   let schema = {
     type: "URLorEmpty"
   };
 
   let valid, parsed;
   [valid, parsed] = JsonSchemaValidator.validateAndParseParameters("https://www.example.com/foo#bar", schema);
   ok(valid, "URL is valid");
-  ok(parsed instanceof Ci.nsIURI, "parsed is a nsIURI");
-  is(parsed.prePath, "https://www.example.com", "prePath is correct");
-  is(parsed.pathQueryRef, "/foo#bar", "pathQueryRef is correct");
+  ok(parsed instanceof URL, "parsed is a nsIURI");
+  is(parsed.origin, "https://www.example.com", "origin is correct");
+  is(parsed.pathname + parsed.hash, "/foo#bar", "pathname is correct");
 
   // Test that this type also accept empty strings
   [valid, parsed] = JsonSchemaValidator.validateAndParseParameters("", schema);
   ok(valid, "URLorEmpty is valid");
   ok(!parsed, "parsed value is falsy");
   is(typeof(parsed), "string", "parsed is a string");
   is(parsed, "", "parsed is an empty string");
 
@@ -132,19 +132,19 @@ add_task(async function test_origin_valu
   // Origin is a URL that doesn't contain a path/query string (i.e., it's only scheme + host + port)
   let schema = {
     type: "origin"
   };
 
   let valid, parsed;
   [valid, parsed] = JsonSchemaValidator.validateAndParseParameters("https://www.example.com", schema);
   ok(valid, "Origin is valid");
-  ok(parsed instanceof Ci.nsIURI, "parsed is a nsIURI");
-  is(parsed.prePath, "https://www.example.com", "prePath is correct");
-  is(parsed.pathQueryRef, "/", "pathQueryRef is corect");
+  ok(parsed instanceof URL, "parsed is a nsIURI");
+  is(parsed.origin, "https://www.example.com", "origin is correct");
+  is(parsed.pathname + parsed.hash, "/", "pathname is corect");
 
   // Invalid values:
   ok(!JsonSchemaValidator.validateAndParseParameters("https://www.example.com/foobar", schema)[0], "Origin cannot contain a path part");
   ok(!JsonSchemaValidator.validateAndParseParameters("https://:!$%", schema)[0], "Invalid origin");
   ok(!JsonSchemaValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
 });
 
 add_task(async function test_array_values() {
@@ -193,18 +193,18 @@ add_task(async function test_object_valu
       url: "https://www.example.com/foo#bar",
       title: "Foo",
       alias: "Bar"
     },
     schema);
 
   ok(valid, "Object is valid");
   ok(typeof(parsed) == "object", "parsed in an object");
-  ok(parsed.url instanceof Ci.nsIURI, "types inside the object are also parsed");
-  is(parsed.url.spec, "https://www.example.com/foo#bar", "URL was correctly parsed");
+  ok(parsed.url instanceof URL, "types inside the object are also parsed");
+  is(parsed.url.href, "https://www.example.com/foo#bar", "URL was correctly parsed");
   is(parsed.title, "Foo", "title was correctly parsed");
   is(parsed.alias, undefined, "property not described in the schema is not present in the parsed object");
 
   // Invalid values:
   ok(!JsonSchemaValidator.validateAndParseParameters(
     {
       url: "https://www.example.com/foo#bar",
       title: 3,
@@ -248,18 +248,18 @@ add_task(async function test_array_of_ob
     }],
     schema);
 
   ok(valid, "Array is valid");
   is(parsed.length, 2, "Correct number of items");
 
   ok(typeof(parsed[0]) == "object" && typeof(parsed[1]) == "object", "Correct objects inside array");
 
-  is(parsed[0].url.spec, "https://www.example.com/bookmark1", "Correct URL for bookmark 1");
-  is(parsed[1].url.spec, "https://www.example.com/bookmark2", "Correct URL for bookmark 2");
+  is(parsed[0].url.href, "https://www.example.com/bookmark1", "Correct URL for bookmark 1");
+  is(parsed[1].url.href, "https://www.example.com/bookmark2", "Correct URL for bookmark 2");
 
   is(parsed[0].title, "Foo", "Correct title for bookmark 1");
   is(parsed[1].title, "Bar", "Correct title for bookmark 2");
 });
 
 add_task(async function test_missing_arrays_inside_objects() {
   let schema = {
     type: "object",