Bug 1287915 - support webpack "raw!" requires in devtools loader; r?jryans draft
authorTom Tromey <tom@tromey.com>
Thu, 28 Jul 2016 12:11:42 -0600
changeset 395763 7ed8a766d753967e61990bcc82cd2062bd476417
parent 395061 3bc5415cc37d7c06e509cb49f5d4003481d49d15
child 527053 d666d7eecae12b854d9009dc57287a279099f9c5
push id24837
push userbmo:ttromey@mozilla.com
push dateTue, 02 Aug 2016 20:38:32 +0000
reviewersjryans
bugs1287915
milestone50.0a1
Bug 1287915 - support webpack "raw!" requires in devtools loader; r?jryans MozReview-Commit-ID: 1H6ogaMrZPV
.eslintignore
devtools/client/shared/browser-loader.js
devtools/client/shared/test/browser.ini
devtools/client/shared/test/browser_require_raw.js
devtools/client/shared/theme.js
devtools/shared/Loader.jsm
devtools/shared/loader-plugin-raw.jsm
devtools/shared/moz.build
devtools/shared/tests/unit/test_require_raw.js
devtools/shared/tests/unit/xpcshell.ini
--- a/.eslintignore
+++ b/.eslintignore
@@ -123,16 +123,17 @@ devtools/server/actors/**
 !devtools/server/actors/string.js
 !devtools/server/actors/csscoverage.js
 devtools/server/performance/**
 devtools/server/tests/**
 devtools/shared/*.js
 !devtools/shared/css-lexer.js
 !devtools/shared/defer.js
 !devtools/shared/event-emitter.js
+!devtools/shared/loader-plugin-raw.jsm
 !devtools/shared/task.js
 devtools/shared/*.jsm
 !devtools/shared/Loader.jsm
 devtools/shared/apps/**
 devtools/shared/client/**
 devtools/shared/discovery/**
 devtools/shared/gcli/**
 !devtools/shared/gcli/templater.js
--- a/devtools/client/shared/browser-loader.js
+++ b/devtools/client/shared/browser-loader.js
@@ -96,16 +96,22 @@ function BrowserLoaderBuilder({ baseURI,
 
   const opts = {
     id: "browser-loader",
     sharedGlobal: true,
     sandboxPrototype: window,
     paths: Object.assign({}, dynamicPaths, loaderOptions.paths),
     invisibleToDebugger: loaderOptions.invisibleToDebugger,
     requireHook: (id, require) => {
+      // If |id| requires special handling, simply defer to devtools
+      // immediately.
+      if (devtools.isLoaderPluginId(id)) {
+        return devtools.require(id);
+      }
+
       const uri = require.resolve(id);
       let isBrowserDir = BROWSER_BASED_DIRS.filter(dir => {
         return uri.startsWith(dir);
       }).length > 0;
 
       // If the URI doesn't match hardcoded paths try the regexp.
       if (!isBrowserDir) {
         isBrowserDir = uri.match(browserBasedDirsRegExp) != null;
--- a/devtools/client/shared/test/browser.ini
+++ b/devtools/client/shared/test/browser.ini
@@ -142,16 +142,17 @@ skip-if = e10s # Layouthelpers test shou
 [browser_mdn-docs-03.js]
 [browser_num-l10n.js]
 [browser_options-view-01.js]
 [browser_outputparser.js]
 skip-if = e10s # Test intermittently fails with e10s. Bug 1124162.
 [browser_poller.js]
 [browser_prefs-01.js]
 [browser_prefs-02.js]
+[browser_require_raw.js]
 [browser_spectrum.js]
 [browser_theme.js]
 [browser_tableWidget_basic.js]
 [browser_tableWidget_keyboard_interaction.js]
 [browser_tableWidget_mouse_interaction.js]
 [browser_telemetry_button_eyedropper.js]
 [browser_telemetry_button_paintflashing.js]
 skip-if = e10s # Bug 937167 - e10s paintflashing
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/test/browser_require_raw.js
@@ -0,0 +1,20 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
+
+const { require: browserRequire } = BrowserLoader({
+  baseURI: "resource://devtools/client/shared/",
+  window: this
+});
+
+const variableFileContents = browserRequire("raw!devtools/client/themes/variables.css");
+
+function test() {
+  ok(variableFileContents.length > 0, "raw browserRequire worked");
+  finish();
+}
--- a/devtools/client/shared/theme.js
+++ b/devtools/client/shared/theme.js
@@ -4,55 +4,31 @@
 
 "use strict";
 
 /**
  * Colors for themes taken from:
  * https://developer.mozilla.org/en-US/docs/Tools/DevToolsColors
  */
 
-const { Cu } = require("chrome");
-const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const Services = require("Services");
 const { gDevTools } = require("devtools/client/framework/devtools");
 
-const VARIABLES_URI = "chrome://devtools/skin/variables.css";
+const variableFileContents = require("raw!devtools/client/themes/variables.css");
+
 const THEME_SELECTOR_STRINGS = {
   light: ":root.theme-light {",
   dark: ":root.theme-dark {"
 };
 
-let variableFileContents;
-
-/**
- * Returns a string of the file found at URI
- */
-function readURI(uri) {
-  let stream = NetUtil.newChannel({
-    uri: NetUtil.newURI(uri, "UTF-8"),
-    loadUsingSystemPrincipal: true}
-  ).open2();
-
-  let count = stream.available();
-  let data = NetUtil.readInputStreamToString(stream, count, {
-    charset: "UTF-8"
-  });
-  stream.close();
-  return data;
-}
-
 /**
  * Takes a theme name and returns the contents of its variable rule block.
  * The first time this runs fetches the variables CSS file and caches it.
  */
 function getThemeFile(name) {
-  if (!variableFileContents) {
-    variableFileContents = readURI(VARIABLES_URI);
-  }
-
   // If there's no theme expected for this name, use `light` as default.
   let selector = THEME_SELECTOR_STRINGS[name] ||
                  THEME_SELECTOR_STRINGS.light;
 
   // This is a pretty naive way to find the contents between:
   // selector {
   //   name: val;
   // }
--- a/devtools/shared/Loader.jsm
+++ b/devtools/shared/Loader.jsm
@@ -6,16 +6,17 @@
 
 /**
  * Manages the addon-sdk loader instance used to load the developer tools.
  */
 
 var { utils: Cu } = Components;
 var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 var { Loader, descriptor, resolveURI } = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
+var { requireRawId } = Cu.import("resource://devtools/shared/loader-plugin-raw.jsm", {});
 
 this.EXPORTED_SYMBOLS = ["DevToolsLoader", "devtools", "BuiltinProvider",
                          "require", "loader"];
 
 /**
  * Providers are different strategies for loading the devtools.
  */
 
@@ -54,16 +55,22 @@ BuiltinProvider.prototype = {
       paths.promise = "resource://gre/modules/Promise-backend.js";
     }
     this.loader = new Loader.Loader({
       id: "fx-devtools",
       paths,
       invisibleToDebugger: this.invisibleToDebugger,
       sharedGlobal: true,
       sharedGlobalBlocklist,
+      requireHook: (id, require) => {
+        if (id.startsWith("raw!")) {
+          return requireRawId(id, require);
+        }
+        return require(id);
+      },
     });
   },
 
   unload: function (reason) {
     Loader.unload(this.loader, reason);
     delete this.loader;
   },
 };
@@ -116,16 +123,24 @@ DevToolsLoader.prototype = {
   require: function () {
     if (!this._provider) {
       this._loadProvider();
     }
     return this.require.apply(this, arguments);
   },
 
   /**
+   * Return true if |id| refers to something requiring help from a
+   * loader plugin.
+   */
+  isLoaderPluginId: function (id) {
+    return id.startsWith("raw!");
+  },
+
+  /**
    * Override the provider used to load the tools.
    */
   setProvider: function (provider) {
     if (provider === this._provider) {
       return;
     }
 
     if (this._provider) {
new file mode 100644
--- /dev/null
+++ b/devtools/shared/loader-plugin-raw.jsm
@@ -0,0 +1,41 @@
+/* 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 { utils: Cu } = Components;
+const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
+
+/**
+ * A function that can be used as part of a require hook for a
+ * loader.js Loader.  This function only handles webpack-style "raw!"
+ * requires; other requires should not be passed to this.  See
+ * https://github.com/webpack/raw-loader.
+ */
+function requireRawId(id, require) {
+  let uri = require.resolve(id.slice(4));
+  // If the original string did not end with ".js", then
+  // require.resolve might have added the suffix.  We don't want to
+  // add a suffix for a raw load (if needed the caller can specify it
+  // manually), so remove it here.
+  if (!id.endsWith(".js") && uri.endsWith(".js")) {
+    uri = uri.slice(0, -3);
+  }
+  let stream = NetUtil.newChannel({
+    uri: NetUtil.newURI(uri, "UTF-8"),
+    loadUsingSystemPrincipal: true
+  }).open2();
+
+  let count = stream.available();
+  let data = NetUtil.readInputStreamToString(stream, count, {
+    charset: "UTF-8"
+  });
+  stream.close();
+
+  // For the time being it doesn't seem worthwhile to cache the
+  // result here.
+  return data;
+}
+
+this.EXPORTED_SYMBOLS = ["requireRawId"];
--- a/devtools/shared/moz.build
+++ b/devtools/shared/moz.build
@@ -49,16 +49,17 @@ DevToolsModules(
     'css-properties-db.js',
     'defer.js',
     'deprecated-sync-thenables.js',
     'DevToolsUtils.js',
     'dom-node-constants.js',
     'dom-node-filter-constants.js',
     'event-emitter.js',
     'indentation.js',
+    'loader-plugin-raw.jsm',
     'Loader.jsm',
     'Parser.jsm',
     'path.js',
     'protocol.js',
     'system.js',
     'task.js',
     'ThreadSafeDevToolsUtils.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/shared/tests/unit/test_require_raw.js
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test require using "raw!".
+
+function run_test() {
+  let loader = new DevToolsLoader();
+  let require = loader.require;
+
+  let variableFileContents = require("raw!devtools/client/themes/variables.css");
+  ok(variableFileContents.length > 0, "raw browserRequire worked");
+}
--- a/devtools/shared/tests/unit/xpcshell.ini
+++ b/devtools/shared/tests/unit/xpcshell.ini
@@ -20,12 +20,13 @@ support-files =
 [test_invisible_loader.js]
 [test_isSet.js]
 [test_safeErrorString.js]
 [test_defineLazyPrototypeGetter.js]
 [test_async-utils.js]
 [test_console_filtering.js]
 [test_prettifyCSS.js]
 [test_require_lazy.js]
+[test_require_raw.js]
 [test_require.js]
 [test_stack.js]
 [test_defer.js]
 [test_executeSoon.js]