Bug 1265813 - replace nsIIOService with URL in devtools; r=ochameau draft
authorTom Tromey <tom@tromey.com>
Fri, 13 May 2016 14:27:28 -0600
changeset 370956 308b838351d0cfd872b1c465133ed1340615cfd5
parent 370950 df627da479f868c9704d7ac0b1c6aa76fb298150
child 521867 25175c9bf005987840e82778c5ac6a22a904fba0
push id19186
push userbmo:ttromey@mozilla.com
push dateWed, 25 May 2016 16:36:11 +0000
reviewersochameau
bugs1265813
milestone49.0a1
Bug 1265813 - replace nsIIOService with URL in devtools; r=ochameau MozReview-Commit-ID: GyoP6ApQXVc
devtools/.eslintrc
devtools/client/commandline/test/browser_cmd_inject.js
devtools/client/inspector/rules/views/text-property-editor.js
devtools/client/shared/components/reps/url.js
devtools/client/shared/output-parser.js
devtools/client/shared/source-utils.js
devtools/client/webconsole/console-output.js
devtools/server/actors/utils/TabSources.js
devtools/shared/builtin-modules.js
devtools/shared/gcli/commands/cookie.js
devtools/shared/gcli/source/lib/gcli/util/host.js
devtools/shared/path.js
devtools/shared/worker/loader.js
--- a/devtools/.eslintrc
+++ b/devtools/.eslintrc
@@ -13,16 +13,17 @@
     "exports": true,
     "isWorker": true,
     "loader": true,
     "module": true,
     "require": true,
     "setInterval": true,
     "setTimeout": true,
     "XMLHttpRequest": true,
+    "URL": true,
     "_Iterator": true,
   },
   "rules": {
     // These are the rules that have been configured so far to match the
     // devtools coding style.
 
     // Rules from the mozilla plugin
     "mozilla/mark-test-function-used": 1,
--- a/devtools/client/commandline/test/browser_cmd_inject.js
+++ b/devtools/client/commandline/test/browser_cmd_inject.js
@@ -49,17 +49,17 @@ function test() {
           markup: "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",
           hints:                                                                                           "",
           status: "VALID",
           args: {
             library: {
               value: function (library) {
                 is(library.type, "url", "inject type name");
                 is(library.url.origin, "http://example.com", "inject url hostname");
-                ok(library.url.path.indexOf("_inject.js") != -1, "inject url path");
+                ok(library.url.pathname.indexOf("_inject.js") != -1, "inject url path");
               },
               status: "VALID"
             }
           }
         },
         exec: {
           output: [ /http:\/\/example.com\/browser\/devtools\/client\/commandline\/test\/browser_cmd_inject.js loaded/ ]
         }
--- a/devtools/client/inspector/rules/views/text-property-editor.js
+++ b/devtools/client/inspector/rules/views/text-property-editor.js
@@ -1,33 +1,31 @@
 /* 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 {Cc, Ci} = require("chrome");
+const {Ci} = require("chrome");
 const {CssLogic} = require("devtools/shared/inspector/css-logic");
 const {InplaceEditor, editableField} =
       require("devtools/client/shared/inplace-editor");
 const {
   createChild,
   appendText,
   advanceValidate,
   blurOnMultipleProperties,
   throttle
 } = require("devtools/client/inspector/shared/utils");
 const {
   parseDeclarations,
   parseSingleValue,
 } = require("devtools/client/shared/css-parsing-utils");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
-const IOService = Cc["@mozilla.org/network/io-service;1"]
-                  .getService(Ci.nsIIOService);
 
 /**
  * TextPropertyEditor is responsible for the following:
  *   Owns a TextProperty object.
  *   Manages changes to the TextProperty.
  *   Can be expanded to display computed properties.
  *   Can mark a property disabled or enabled.
  *
@@ -249,48 +247,16 @@ TextPropertyEditor.prototype = {
     let domRule = this.rule.domRule;
     if (domRule) {
       return domRule.href || domRule.nodeHref;
     }
     return undefined;
   },
 
   /**
-   * Get the URI from which to resolve relative requests for
-   * this rule's stylesheet.
-   *
-   * @return {nsIURI} A URI based on the the stylesheet's href.
-   */
-  get sheetURI() {
-    if (this._sheetURI === undefined) {
-      if (this.sheetHref) {
-        this._sheetURI = IOService.newURI(this.sheetHref, null, null);
-      } else {
-        this._sheetURI = null;
-      }
-    }
-
-    return this._sheetURI;
-  },
-
-  /**
-   * Resolve a URI based on the rule stylesheet
-   *
-   * @param {String} relativePath
-   *        the path to resolve
-   * @return {String} the resolved path.
-   */
-  resolveURI: function (relativePath) {
-    if (this.sheetURI) {
-      relativePath = this.sheetURI.resolve(relativePath);
-    }
-    return relativePath;
-  },
-
-  /**
    * Populate the span based on changes to the TextProperty.
    */
   update: function () {
     if (this.ruleView.isDestroyed) {
       return;
     }
 
     if (this.prop.enabled) {
@@ -348,17 +314,17 @@ TextPropertyEditor.prototype = {
       bezierSwatchClass: sharedSwatchClass + bezierSwatchClass,
       bezierClass: "ruleview-bezier",
       filterSwatchClass: sharedSwatchClass + filterSwatchClass,
       filterClass: "ruleview-filter",
       angleSwatchClass: sharedSwatchClass + angleSwatchClass,
       angleClass: "ruleview-angle",
       defaultColorType: !propDirty,
       urlClass: "theme-link",
-      baseURI: this.sheetURI
+      baseURI: this.sheetHref
     };
     let frag = outputParser.parseCssProperty(name, val, parserOptions);
     this.valueSpan.innerHTML = "";
     this.valueSpan.appendChild(frag);
 
     // Attach the color picker tooltip to the color swatches
     this._colorSwatchSpans =
       this.valueSpan.querySelectorAll("." + colorSwatchClass);
@@ -473,17 +439,17 @@ TextPropertyEditor.prototype = {
       });
       appendText(li, ": ");
 
       let outputParser = this.ruleView._outputParser;
       let frag = outputParser.parseCssProperty(
         computed.name, computed.value, {
           colorSwatchClass: "ruleview-swatch ruleview-colorswatch",
           urlClass: "theme-link",
-          baseURI: this.sheetURI
+          baseURI: this.sheetHref
         }
       );
 
       // Store the computed property value that was parsed for output
       computed.parsedValue = frag.textContent;
 
       createChild(li, "span", {
         class: "ruleview-propertyvalue theme-fg-color1",
--- a/devtools/client/shared/components/reps/url.js
+++ b/devtools/client/shared/components/reps/url.js
@@ -1,14 +1,14 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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/. */
-/* global URLSearchParams, URL */
+/* global URLSearchParams */
 
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   function parseURLParams(url) {
     url = new URL(url);
     return parseURLEncodedText(url.searchParams);
--- a/devtools/client/shared/output-parser.js
+++ b/devtools/client/shared/output-parser.js
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Cc, Ci} = require("chrome");
 const {angleUtils} = require("devtools/client/shared/css-angle");
 const {colorUtils} = require("devtools/client/shared/css-color");
 const {getCSSLexer} = require("devtools/shared/css-lexer");
-const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 const BEZIER_KEYWORDS = ["linear", "ease-in-out", "ease-in", "ease-out",
                          "ease"];
 
 // Functions that accept a color argument.
@@ -525,17 +524,17 @@ OutputParser.prototype = {
       // seemed simpler on the whole.
       let [, leader, , body, trailer] =
         /^(url\([ \t\r\n\f]*(["']?))(.*?)(\2[ \t\r\n\f]*\))$/i.exec(match);
 
       this._appendTextNode(leader);
 
       let href = url;
       if (options.baseURI) {
-        href = options.baseURI.resolve(url);
+        href = new URL(url, options.baseURI).href;
       }
 
       this._appendNode("a", {
         target: "_blank",
         class: options.urlClass,
         href: href
       }, body);
 
@@ -643,17 +642,17 @@ OutputParser.prototype = {
    *           - bezierSwatchClass: ""  // The class to use for bezier swatches.
    *           - bezierClass: ""        // The class to use for the bezier value
    *                                    // that follows the swatch.
    *           - angleSwatchClass: ""   // The class to use for angle swatches.
    *           - angleClass: ""         // The class to use for the angle value
    *                                    // that follows the swatch.
    *           - supportsColor: false   // Does the CSS property support colors?
    *           - urlClass: ""           // The class to be used for url() links.
-   *           - baseURI: ""            // A string or nsIURI used to resolve
+   *           - baseURI: undefined     // A string used to resolve
    *                                    // relative links.
    *           - filterSwatch: false    // A special case for parsing a
    *                                    // "filter" property, causing the
    *                                    // parser to skip the call to
    *                                    // _wrapFilter.  Used only for
    *                                    // previewing with the filter swatch.
    * @return {Object}
    *         Overridden options object
@@ -664,24 +663,20 @@ OutputParser.prototype = {
       colorSwatchClass: "",
       colorClass: "",
       bezierSwatchClass: "",
       bezierClass: "",
       angleSwatchClass: "",
       angleClass: "",
       supportsColor: false,
       urlClass: "",
-      baseURI: "",
+      baseURI: undefined,
       filterSwatch: false
     };
 
-    if (typeof overrides.baseURI === "string") {
-      overrides.baseURI = Services.io.newURI(overrides.baseURI, null, null);
-    }
-
     for (let item in overrides) {
       defaults[item] = overrides[item];
     }
     return defaults;
   }
 };
 
 /**
--- a/devtools/client/shared/source-utils.js
+++ b/devtools/client/shared/source-utils.js
@@ -1,14 +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/. */
 "use strict";
 
-const { URL } = require("sdk/url");
 const { LocalizationHelper } = require("devtools/client/shared/l10n");
 
 const l10n = new LocalizationHelper("chrome://devtools/locale/components.properties");
 const UNKNOWN_SOURCE_STRING = l10n.getStr("frame.unknownSource");
 
 // Character codes used in various parsing helper functions.
 const CHAR_CODE_A = "a".charCodeAt(0);
 const CHAR_CODE_C = "c".charCodeAt(0);
@@ -25,67 +24,74 @@ const CHAR_CODE_P = "p".charCodeAt(0);
 const CHAR_CODE_R = "r".charCodeAt(0);
 const CHAR_CODE_S = "s".charCodeAt(0);
 const CHAR_CODE_T = "t".charCodeAt(0);
 const CHAR_CODE_U = "u".charCodeAt(0);
 const CHAR_CODE_COLON = ":".charCodeAt(0);
 const CHAR_CODE_SLASH = "/".charCodeAt(0);
 const CHAR_CODE_CAP_S = "S".charCodeAt(0);
 
-// The cache used in the `nsIURL` function.
+// The cache used in the `parseURL` function.
 const gURLStore = new Map();
 // The cache used in the `getSourceNames` function.
 const gSourceNamesStore = new Map();
 
 /**
  * Takes a string and returns an object containing all the properties
  * available on an URL instance, with additional properties (fileName),
  * Leverages caching.
  *
- * @TODO If loaded through Browser Loader, we can use the web API URL
- * directly, giving us the same interface without needing the SDK --
- * we still need to add `fileName` though.
- *
  * @param {String} location
  * @return {Object?} An object containing most properties available
  *                   in https://developer.mozilla.org/en-US/docs/Web/API/URL
  */
 
 function parseURL(location) {
   let url = gURLStore.get(location);
 
   if (url !== void 0) {
     return url;
   }
 
   try {
     url = new URL(location);
+    // The callers were generally written to expect a URL from
+    // sdk/url, which is subtly different.  So, work around some
+    // important differences here.
+    url = {
+      href: url.href,
+      protocol: url.protocol,
+      host: url.host,
+      hostname: url.hostname,
+      port: url.port || null,
+      pathname: url.pathname,
+      search: url.search,
+      hash: url.hash,
+      username: url.username,
+      password: url.password,
+      origin: url.origin,
+    };
+
     // Definitions:
     // Example: https://foo.com:8888/file.js
     // `hostname`: "foo.com"
     // `host`: "foo.com:8888"
-    //
-    // sdk/url does not match several definitions.: both `host` and `hostname`
-    // are actually the `hostname` (even though this is the `host` property on
-    // the original nsIURL, with `hostPort` representing the actual `host` name,
-    // AH!!!). So normalize all that garbage here.
     let isChrome = isChromeScheme(location);
-    let fileName = url.fileName || "/";
-    let hostname, host;
+
+    url.fileName = url.pathname ?
+      (url.pathname.slice(url.pathname.lastIndexOf("/") + 1) || "/") :
+      "/";
+
     if (isChrome) {
-      hostname = null;
-      host = null;
-    } else {
-      hostname = url.hostname;
-      host = url.port ? `${url.host}:${url.port}` : url.host;
+      url.hostname = null;
+      url.host = null;
     }
 
-    let parsed = Object.assign({}, url, { host, fileName, hostname });
-    gURLStore.set(location, parsed);
-    return parsed;
+    gURLStore.set(location, url);
+    return url;
   } catch (e) {
     gURLStore.set(location, null);
     return null;
   }
 }
 
 /**
  * Parse a source into a short and long name as well as a host name.
--- a/devtools/client/webconsole/console-output.js
+++ b/devtools/client/webconsole/console-output.js
@@ -1,31 +1,30 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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 {Cc, Ci, Cu} = require("chrome");
+const {Ci, Cu} = require("chrome");
 
 const Services = require("Services");
 
 loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
 loader.lazyImporter(this, "escapeHTML", "resource://devtools/client/shared/widgets/VariablesView.jsm");
 loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
 
 loader.lazyRequireGetter(this, "promise");
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "TableWidget", "devtools/client/shared/widgets/TableWidget", true);
 loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", true);
 
 const { extend } = require("sdk/core/heritage");
-const URI = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const STRINGS_URI = "chrome://devtools/locale/webconsole.properties";
 
 const WebConsoleUtils = require("devtools/shared/webconsole/utils").Utils;
 const { getSourceNames } = require("devtools/client/shared/source-utils");
 const {Task} = require("devtools/shared/task");
 const l10n = new WebConsoleUtils.L10n(STRINGS_URI);
@@ -2292,19 +2291,19 @@ Widgets.URLString.prototype = extend(Wid
    *
    * @param string token
    *        The token.
    * @return boolean
    *         Whenther the token is a URL.
    */
   _isURL: function (token) {
     try {
-      let uri = URI.newURI(token, null, null);
-      let url = uri.QueryInterface(Ci.nsIURL);
-      return true;
+      let url = new URL(token);
+      return ["http:", "https:", "ftp:", "data:", "javascript:",
+              "resource:", "chrome:"].includes(url.protocol);
     } catch (e) {
       return false;
     }
   },
 
   /**
    * Renders a string as a URL.
    *
--- a/devtools/server/actors/utils/TabSources.js
+++ b/devtools/server/actors/utils/TabSources.js
@@ -6,17 +6,16 @@
 
 const { Ci, Cu } = require("chrome");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { assert, fetch } = DevToolsUtils;
 const EventEmitter = require("devtools/shared/event-emitter");
 const { OriginalLocation, GeneratedLocation } = require("devtools/server/actors/common");
 const { resolve } = require("promise");
 const { joinURI } = require("devtools/shared/path");
-const URL = require("URL");
 
 loader.lazyRequireGetter(this, "SourceActor", "devtools/server/actors/source", true);
 loader.lazyRequireGetter(this, "isEvalSource", "devtools/server/actors/source", true);
 loader.lazyRequireGetter(this, "SourceMapConsumer", "source-map", true);
 loader.lazyRequireGetter(this, "SourceMapGenerator", "source-map", true);
 
 /**
  * Manages the sources for a thread. Handles source maps, locations in the
--- a/devtools/shared/builtin-modules.js
+++ b/devtools/shared/builtin-modules.js
@@ -217,23 +217,16 @@ defineLazyGetter(exports.modules, "index
 
 defineLazyGetter(exports.modules, "CSS", () => {
   let sandbox
     = Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(),
                  {wantGlobalProperties: ["CSS"]});
   return sandbox.CSS;
 });
 
-defineLazyGetter(exports.modules, "URL", () => {
-  let sandbox
-    = Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(),
-                 {wantGlobalProperties: ["URL"]});
-  return sandbox.URL;
-});
-
 // List of all custom globals exposed to devtools modules.
 // Changes here should be mirrored to devtools/.eslintrc.
 const globals = exports.globals = {
   isWorker: false,
   reportError: Cu.reportError,
   atob: atob,
   btoa: btoa,
   _Iterator: Iterator,
@@ -282,8 +275,14 @@ defineLazyGetter(globals, "setTimeout", 
   return Cu.import("resource://gre/modules/Timer.jsm", {}).setTimeout;
 });
 defineLazyGetter(globals, "clearInterval", () => {
   return Cu.import("resource://gre/modules/Timer.jsm", {}).clearInterval;
 });
 defineLazyGetter(globals, "setInterval", () => {
   return Cu.import("resource://gre/modules/Timer.jsm", {}).setInterval;
 });
+defineLazyGetter(globals, "URL", () => {
+  let sandbox
+    = Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(),
+                 {wantGlobalProperties: ["URL"]});
+  return sandbox.URL;
+});
--- a/devtools/shared/gcli/commands/cookie.js
+++ b/devtools/shared/gcli/commands/cookie.js
@@ -17,17 +17,16 @@
  * toolbar (the gcli command bar), and because this toolbar is only available on
  * a local Firefox desktop tab (not in webide or the browser toolbox), we can
  * make the commands run on the client.
  * This way, they'll always run in the parent process.
  */
 
 const { Ci, Cc } = require("chrome");
 const l10n = require("gcli/l10n");
-const URL = require("sdk/url").URL;
 
 XPCOMUtils.defineLazyGetter(this, "cookieMgr", function() {
   return Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
 });
 
 /**
  * Check host value and remove port part as it is not used
  * for storing cookies.
--- a/devtools/shared/gcli/source/lib/gcli/util/host.js
+++ b/devtools/shared/gcli/source/lib/gcli/util/host.js
@@ -13,17 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 'use strict';
 
 var Cc = require('chrome').Cc;
 var Ci = require('chrome').Ci;
-var URL = require('sdk/url').URL;
 
 var { Task } = require("devtools/shared/task");
 
 var util = require('./util');
 
 function Highlighter(document) {
   this._document = document;
   this._nodes = util.createEmptyNodeList(this._document);
@@ -63,17 +62,17 @@ exports.Highlighter = Highlighter;
 exports.exec = function(task) {
   return Task.spawn(task);
 };
 
 /**
  * The URL API is new enough that we need specific platform help
  */
 exports.createUrl = function(uristr, base) {
-  return URL(uristr, base);
+  return new URL(uristr, base);
 };
 
 /**
  * Load some HTML into the given document and return a DOM element.
  * This utility assumes that the html has a single root (other than whitespace)
  */
 exports.toDom = function(document, html) {
   var div = util.createElement(document, 'div');
--- a/devtools/shared/path.js
+++ b/devtools/shared/path.js
@@ -1,16 +1,14 @@
 /* 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 URL = require("URL");
-
 /*
  * Join all the arguments together and normalize the resulting URI.
  * The initial path must be an full URI with a protocol (i.e. http://).
  */
 exports.joinURI = (initialPath, ...paths) => {
   let url;
 
   try {
--- a/devtools/shared/worker/loader.js
+++ b/devtools/shared/worker/loader.js
@@ -485,24 +485,24 @@ var {
 this.worker = new WorkerDebuggerLoader({
   createSandbox: createSandbox,
   globals: {
     "isWorker": true,
     "dump": dump,
     "loader": loader,
     "reportError": reportError,
     "rpc": rpc,
-    "setImmediate": setImmediate
+    "setImmediate": setImmediate,
+    "URL": URL,
   },
   loadSubScript: loadSubScript,
   modules: {
     "Debugger": Debugger,
     "PromiseDebugging": PromiseDebugging,
     "Services": Object.create(null),
-    "URL": URL,
     "chrome": chrome,
     "xpcInspector": xpcInspector
   },
   paths: {
     // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
     "": "resource://gre/modules/commonjs/",
     // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
     "devtools": "resource://devtools",