--- a/addon-sdk/source/lib/sdk/util/object.js
+++ b/addon-sdk/source/lib/sdk/util/object.js
@@ -29,17 +29,17 @@ const { flatten } = require('./array');
* b.name // 'b'
*/
function merge(source) {
let descriptor = {};
// `Boolean` converts the first parameter to a boolean value. Any object is
// converted to `true` where `null` and `undefined` becames `false`. Therefore
// the `filter` method will keep only objects that are defined and not null.
- Array.slice(arguments, 1).filter(Boolean).forEach(function onEach(properties) {
+ [].slice.call(arguments, 1).filter(Boolean).forEach(function onEach(properties) {
getOwnPropertyIdentifiers(properties).forEach(function(name) {
descriptor[name] = Object.getOwnPropertyDescriptor(properties, name);
});
});
return Object.defineProperties(source, descriptor);
}
exports.merge = merge;
@@ -67,17 +67,17 @@ function each(obj, fn) {
exports.each = each;
/**
* Like `merge`, except no property descriptors are manipulated, for use
* with platform objects. Identical to underscore's `extend`. Useful for
* merging XPCOM objects
*/
function safeMerge(source) {
- Array.slice(arguments, 1).forEach(function onEach (obj) {
+ [].slice.call(arguments, 1).forEach(function onEach (obj) {
for (let prop in obj) source[prop] = obj[prop];
});
return source;
}
exports.safeMerge = safeMerge;
/*
* Returns a copy of the object without omitted properties
--- a/devtools/client/framework/devtools.js
+++ b/devtools/client/framework/devtools.js
@@ -23,17 +23,17 @@ const {Task} = require("devtools/shared/
const FORBIDDEN_IDS = new Set(["toolbox", ""]);
const MAX_ORDINAL = 99;
/**
* DevTools is a class that represents a set of developer tools, it holds a
* set of tools and keeps track of open toolboxes in the browser.
*/
-this.DevTools = function DevTools() {
+const DevTools = function DevTools() {
this._tools = new Map(); // Map<toolId, tool>
this._themes = new Map(); // Map<themeId, theme>
this._toolboxes = new Map(); // Map<target, toolbox>
// destroy() is an observer's handler so we need to preserve context.
this.destroy = this.destroy.bind(this);
// JSON Viewer for 'application/json' documents.
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/bin/dev-server.js
@@ -0,0 +1,65 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set 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 __dirname */
+
+"use strict";
+
+const toolbox = require("../node_modules/devtools-local-toolbox/index");
+const feature = require("devtools-config");
+const envConfig = require("../configs/development.json");
+
+const fs = require("fs");
+const path = require("path");
+
+feature.setConfig(envConfig);
+const webpackConfig = require("../webpack.config")(envConfig);
+
+let {app} = toolbox.startDevServer(envConfig, webpackConfig);
+
+function sendFile(res, src, encoding) {
+ const filePath = path.join(__dirname, src);
+ const file = encoding ? fs.readFileSync(filePath, encoding) : fs.readFileSync(filePath);
+ res.send(file);
+}
+
+function addFileRoute(from, to) {
+ app.get(from, function (req, res) {
+ sendFile(res, to, "utf-8");
+ });
+}
+
+// Routes
+addFileRoute("/", "../inspector.xhtml");
+addFileRoute("/markup/markup.xhtml", "../markup/markup.xhtml");
+
+app.get("/devtools/skin/images/:file.png", function (req, res) {
+ res.contentType("image/png");
+ sendFile(res, "../../themes/images/" + req.params.file + ".png");
+});
+
+app.get("/devtools/skin/images/:file.svg", function (req, res) {
+ res.contentType("image/svg+xml");
+ sendFile(res, "../../themes/images/" + req.params.file + ".svg", "utf-8");
+});
+
+app.get("/images/:file.svg", function (req, res) {
+ res.contentType("image/svg+xml");
+ sendFile(res, "../../themes/images/" + req.params.file + ".svg", "utf-8");
+});
+
+// Redirect chrome:devtools/skin/file.css to ../../themes/file.css
+app.get("/devtools/skin/:file.css", function (req, res) {
+ res.contentType("text/css; charset=utf-8");
+ sendFile(res, "../../themes/" + req.params.file + ".css", "utf-8");
+});
+
+// Redirect chrome:devtools/client/path/to/file.css to ../../path/to/file.css
+// and chrome:devtools/content/path/to/file.css to ../../path/to/file.css
+app.get(/^\/devtools\/(?:client|content)\/(.*)\.css$/, function (req, res) {
+ res.contentType("text/css; charset=utf-8");
+ sendFile(res, "../../" + req.params[0] + ".css");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/configs/development.json
@@ -0,0 +1,22 @@
+{
+ "title": "Inspector",
+ "environment": "development",
+ "baseWorkerURL": "public/build/",
+ "theme": "light",
+ "host": "",
+ "logging": {
+ "client": false,
+ "firefoxProxy": false
+ },
+ "features": {
+ },
+ "firefox": {
+ "proxyHost": "localhost:9000",
+ "webSocketConnection": false,
+ "webSocketHost": "localhost:6080"
+ },
+ "development": {
+ "serverPort": 8000,
+ "customIndex": true
+ }
+}
\ No newline at end of file
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -1,20 +1,18 @@
/* -*- 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 window */
+/* global window, BrowserLoader */
"use strict";
-var Cu = Components.utils;
-var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
var Services = require("Services");
var promise = require("promise");
var defer = require("devtools/shared/defer");
var EventEmitter = require("devtools/shared/event-emitter");
const {executeSoon} = require("devtools/shared/DevToolsUtils");
var {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
var {Task} = require("devtools/shared/task");
const {initCssProperties} = require("devtools/shared/fronts/css-properties");
@@ -1316,25 +1314,25 @@ Inspector.prototype = {
_initMarkup: function () {
let doc = this.panelDoc;
this._markupBox = doc.getElementById("markup-box");
// create tool iframe
this._markupFrame = doc.createElement("iframe");
this._markupFrame.setAttribute("flex", "1");
+ // This is needed to enable tooltips inside the iframe document.
this._markupFrame.setAttribute("tooltip", "aHTMLTooltip");
this._markupFrame.addEventListener("contextmenu", this._onContextMenu);
- // This is needed to enable tooltips inside the iframe document.
- this._markupFrame.addEventListener("load", this._onMarkupFrameLoad, true);
-
this._markupBox.setAttribute("collapsed", true);
this._markupBox.appendChild(this._markupFrame);
- this._markupFrame.setAttribute("src", "chrome://devtools/content/inspector/markup/markup.xhtml");
+
+ this._markupFrame.addEventListener("load", this._onMarkupFrameLoad, true);
+ this._markupFrame.setAttribute("src", "markup/markup.xhtml");
this._markupFrame.setAttribute("aria-label",
INSPECTOR_L10N.getStr("inspector.panelLabel.markupView"));
},
_onMarkupFrameLoad: function () {
this._markupFrame.removeEventListener("load", this._onMarkupFrameLoad, true);
this._markupFrame.contentWindow.focus();
@@ -1850,94 +1848,116 @@ Inspector.prototype = {
// When the inspector menu was setup on click (see _getNodeLinkMenuItems), we
// already checked that resolveRelativeURL existed.
this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
clipboardHelper.copyString(url);
}, console.error);
}
};
+/**
+ * Create a fake toolbox when running the inspector standalone, either in a chrome tab or
+ * in a content tab.
+ *
+ * @param {Target} target to debug
+ * @param {Function} createThreadClient
+ * When supported the thread client needs a reference to the toolbox.
+ * This callback will be called right after the toolbox object is created.
+ * @param {Object} dependencies
+ * - react
+ * - reactDOM
+ * - browserRequire
+ */
+const buildFakeToolbox = Task.async(function* (
+ target, createThreadClient, {
+ React,
+ ReactDOM,
+ browserRequire
+ }) {
+ const { InspectorFront } = require("devtools/shared/fronts/inspector");
+ const { Selection } = require("devtools/client/framework/selection");
+ const { getHighlighterUtils } = require("devtools/client/framework/toolbox-highlighter-utils");
+
+ let notImplemented = function () {
+ throw new Error("Not implemented in a tab");
+ };
+ let fakeToolbox = {
+ target,
+ hostType: "bottom",
+ doc: window.document,
+ win: window,
+ on() {}, emit() {}, off() {},
+ initInspector() {},
+ browserRequire,
+ React,
+ ReactDOM,
+ isToolRegistered() {
+ return false;
+ },
+ currentToolId: "inspector",
+ getCurrentPanel() {
+ return "inspector";
+ },
+ get textboxContextMenuPopup() {
+ notImplemented();
+ },
+ getPanel: notImplemented,
+ openSplitConsole: notImplemented,
+ viewCssSourceInStyleEditor: notImplemented,
+ viewJsSourceInDebugger: notImplemented,
+ viewSource: notImplemented,
+ viewSourceInDebugger: notImplemented,
+ viewSourceInStyleEditor: notImplemented,
+
+ // For attachThread:
+ highlightTool() {},
+ unhighlightTool() {},
+ selectTool() {},
+ raise() {},
+ getNotificationBox() {}
+ };
+
+ fakeToolbox.threadClient = yield createThreadClient(fakeToolbox);
+
+ let inspector = InspectorFront(target.client, target.form);
+ let showAllAnonymousContent =
+ Services.prefs.getBoolPref("devtools.inspector.showAllAnonymousContent");
+ let walker = yield inspector.getWalker({ showAllAnonymousContent });
+ let selection = new Selection(walker);
+ let highlighter = yield inspector.getHighlighter(false);
+ fakeToolbox.highlighterUtils = getHighlighterUtils(fakeToolbox);
+
+ fakeToolbox.inspector = inspector;
+ fakeToolbox.walker = walker;
+ fakeToolbox.selection = selection;
+ fakeToolbox.highlighter = highlighter;
+ return fakeToolbox;
+});
+
// URL constructor doesn't support chrome: scheme
let href = window.location.href.replace(/chrome:/, "http://");
let url = new window.URL(href);
-// Only use this method to attach the toolbox if some query parameters are given
-if (url.search.length > 1) {
+// If query parameters are given in a chrome tab, the inspector is running in standalone.
+if (window.location.protocol === "chrome:" && url.search.length > 1) {
const { targetFromURL } = require("devtools/client/framework/target-from-url");
const { attachThread } = require("devtools/client/framework/attach-thread");
- const { BrowserLoader } =
- Cu.import("resource://devtools/client/shared/browser-loader.js", {});
- const { Selection } = require("devtools/client/framework/selection");
- const { InspectorFront } = require("devtools/shared/fronts/inspector");
- const { getHighlighterUtils } = require("devtools/client/framework/toolbox-highlighter-utils");
+ const browserRequire = BrowserLoader({ window, useOnlyShared: true }).require;
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
Task.spawn(function* () {
let target = yield targetFromURL(url);
-
- let notImplemented = function () {
- throw new Error("Not implemented in a tab");
- };
- let fakeToolbox = {
+ let fakeToolbox = yield buildFakeToolbox(
target,
- hostType: "bottom",
- doc: window.document,
- win: window,
- on() {}, emit() {}, off() {},
- initInspector() {},
- browserRequire: BrowserLoader({
- window: window,
- useOnlyShared: true
- }).require,
- get React() {
- return this.browserRequire("devtools/client/shared/vendor/react");
- },
- get ReactDOM() {
- return this.browserRequire("devtools/client/shared/vendor/react-dom");
- },
- isToolRegistered() {
- return false;
- },
- currentToolId: "inspector",
- getCurrentPanel() {
- return "inspector";
- },
- get textboxContextMenuPopup() {
- notImplemented();
- },
- getPanel: notImplemented,
- openSplitConsole: notImplemented,
- viewCssSourceInStyleEditor: notImplemented,
- viewJsSourceInDebugger: notImplemented,
- viewSource: notImplemented,
- viewSourceInDebugger: notImplemented,
- viewSourceInStyleEditor: notImplemented,
-
- // For attachThread:
- highlightTool() {},
- unhighlightTool() {},
- selectTool() {},
- raise() {},
- getNotificationBox() {}
- };
-
- // attachThread also expect a toolbox as argument
- fakeToolbox.threadClient = yield attachThread(fakeToolbox);
-
- let inspector = InspectorFront(target.client, target.form);
- let showAllAnonymousContent =
- Services.prefs.getBoolPref("devtools.inspector.showAllAnonymousContent");
- let walker = yield inspector.getWalker({ showAllAnonymousContent });
- let selection = new Selection(walker);
- let highlighter = yield inspector.getHighlighter(false);
-
- fakeToolbox.inspector = inspector;
- fakeToolbox.walker = walker;
- fakeToolbox.selection = selection;
- fakeToolbox.highlighter = highlighter;
- fakeToolbox.highlighterUtils = getHighlighterUtils(fakeToolbox);
-
+ (toolbox) => attachThread(toolbox),
+ { React, ReactDOM, browserRequire }
+ );
let inspectorUI = new Inspector(fakeToolbox);
inspectorUI.init();
}).then(null, e => {
window.alert("Unable to start the inspector:" + e.message + "\n" + e.stack);
});
}
+
+exports.Inspector = Inspector;
+exports.buildFakeToolbox = buildFakeToolbox;
--- a/devtools/client/inspector/inspector.xhtml
+++ b/devtools/client/inspector/inspector.xhtml
@@ -21,16 +21,27 @@
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabs.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabbar.css"/>
<link rel="stylesheet" href="resource://devtools/client/inspector/components/inspector-tab-panel.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/splitter/split-box.css"/>
<link rel="stylesheet" href="resource://devtools/client/inspector/layout/components/Accordion.css"/>
<script type="application/javascript;version=1.8"
src="chrome://devtools/content/shared/theme-switching.js"></script>
+ <script type="text/javascript">
+ var isInChrome = window.location.href.includes("chrome:");
+ if (isInChrome) {
+ var exports = {};
+ var Cu = Components.utils;
+ var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+ var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
+ }
+ </script>
+
+ <!-- in content, inspector.js is mapped to the dynamically generated webpack bundle -->
<script type="application/javascript;version=1.8" src="inspector.js" defer="true"></script>
</head>
<body class="theme-body" role="application">
<div class="inspector-responsive-container theme-body inspector">
<!-- Main Panel Content -->
<div id="inspector-main-content" class="devtools-main-content">
<div id="inspector-toolbar" class="devtools-toolbar" nowindowdrag="true"
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/local-toolbox.js
@@ -0,0 +1,122 @@
+/* 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 window, document */
+
+"use strict";
+
+const React = require("devtools/client/shared/vendor/react");
+const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+
+const { buildFakeToolbox, Inspector } = require("./inspector");
+
+function onConnect(arg) {
+ if (!arg || !arg.client) {
+ return;
+ }
+
+ let client = arg.client;
+
+ const tabTarget = client.getTabTarget();
+ let threadClient = { paused: false };
+ buildFakeToolbox(
+ tabTarget,
+ () => threadClient,
+ { React, ReactDOM, browserRequire: () => {} }
+ ).then(function (fakeToolbox) {
+ let inspector = new Inspector(fakeToolbox);
+ inspector.init();
+ });
+}
+
+/**
+ * Stylesheet links in devtools xhtml files are using chrome or resource URLs.
+ * Rewrite the href attribute to remove the protocol. web-server.js contains redirects
+ * to map CSS urls to the proper file. Supports urls using:
+ * - devtools/client/
+ * - devtools/content/
+ * - skin/
+ * The css for the light-theme will additionally be loaded.
+ * Will also add mandatory classnames and attributes to be compatible with devtools theme
+ * stylesheet.
+ *
+ */
+function fixStylesheets(doc) {
+ let links = doc.head.querySelectorAll("link");
+ for (let link of links) {
+ link.href = link.href.replace(/(resource|chrome)\:\/\//, "/");
+ }
+
+ // Add the light theme stylesheet to compensate for the missing theme-switching.js
+ let themeLink = doc.createElement("link");
+ themeLink.setAttribute("rel", "stylesheet");
+ themeLink.setAttribute("href", "/devtools/skin/light-theme.css");
+
+ doc.head.appendChild(themeLink);
+ doc.documentElement.classList.add("theme-light");
+ doc.body.classList.add("theme-light");
+
+ let isMac = /Mac/.test(window.navigator.userAgent);
+ let isLinux = /(Linux)|(X11)/.test(window.navigator.userAgent);
+ if (isMac) {
+ doc.documentElement.setAttribute("platform", "mac");
+ } else if (isLinux) {
+ doc.documentElement.setAttribute("platform", "linux");
+ } else {
+ doc.documentElement.setAttribute("platform", "win");
+ }
+}
+
+/**
+ * Called each time a childList mutation is received on the main document.
+ * Check the iframes currently loaded in the document and call fixStylesheets if needed.
+ */
+function fixStylesheetsOnMutation() {
+ let frames = document.body.querySelectorAll("iframe");
+ for (let frame of frames) {
+ let doc = frame.contentDocument || frame.contentWindow.document;
+ if (doc.__fixStylesheetsFlag) {
+ continue;
+ }
+
+ // Mark the document as processed to avoid extra changes.
+ doc.__fixStylesheetsFlag = true;
+ if (doc.readyState !== "complete") {
+ // If the document is not loaded yet, wait for DOMContentLoaded.
+ frame.contentWindow.addEventListener("DOMContentLoaded", () => {
+ fixStylesheets(doc);
+ }, { once: true });
+ } else {
+ fixStylesheets(doc);
+ }
+ }
+}
+
+window.addEventListener("DOMContentLoaded", function onInspectorDOMLoaded() {
+ window.removeEventListener("DOMContentLoaded", onInspectorDOMLoaded);
+
+ // Add styling for the main document.
+ fixStylesheets(document);
+
+ // Add a mutation observer to check if new iframes have been loaded and need to have
+ // their stylesheet links updated.
+ new window.MutationObserver(mutations => {
+ fixStylesheetsOnMutation();
+ }).observe(document.body, { childList: true, subtree: true });
+
+ const hasFirefoxTabParam = window.location.href.indexOf("firefox-tab") != -1;
+ if (!hasFirefoxTabParam) {
+ const inspectorRoot = document.querySelector(".inspector");
+ // Remove the inspector specific markup and add the landing page root element.
+ inspectorRoot.remove();
+ let mount = document.createElement("div");
+ mount.setAttribute("id", "mount");
+ document.body.appendChild(mount);
+ }
+
+ // Toolbox tries to add a theme classname on the documentElement and should only be
+ // required after DOMContentLoaded.
+ const { bootstrap } = require("devtools-local-toolbox");
+ bootstrap(React, ReactDOM).then(onConnect);
+});
--- a/devtools/client/inspector/markup/markup.xhtml
+++ b/devtools/client/inspector/markup/markup.xhtml
@@ -11,17 +11,16 @@
<link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/mozilla.css" type="text/css"/>
<script type="application/javascript;version=1.8"
src="chrome://devtools/content/shared/theme-switching.js"></script>
<script type="application/javascript;version=1.8"
src="chrome://devtools/content/sourceeditor/codemirror/codemirror.bundle.js"></script>
-
</head>
<body class="theme-body devtools-monospace" role="application">
<!-- NOTE THAT WE MAKE EXTENSIVE USE OF HTML COMMENTS IN THIS FILE IN ORDER -->
<!-- TO MAKE SPANS READABLE WHILST AVOIDING SIGNIFICANT WHITESPACE -->
<div id="root-wrapper" role="presentation">
<div id="root" role="presentation"></div>
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "inspector.html",
+ "version": "0.0.1",
+ "description": "The Firefox Inspector",
+ "scripts": {
+ "start": "node bin/dev-server"
+ },
+ "author": "",
+ "dependencies": {
+ "devtools-local-toolbox": "0.0.10",
+ "devtools-modules": "0.0.9",
+ "devtools-sham-modules": "0.0.9",
+ "devtools-config": "0.0.9",
+ "raw-loader": "^0.5.1",
+ "json-loader": "^0.5.4"
+ }
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/webpack.config.js
@@ -0,0 +1,142 @@
+/* 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 __dirname */
+
+"use strict";
+
+const {toolboxConfig} = require("devtools-local-toolbox/index");
+
+const path = require("path");
+const webpack = require("webpack");
+
+module.exports = envConfig => {
+ let webpackConfig = {
+ bail: true,
+ entry: [
+ path.join(__dirname, "local-toolbox.js")
+ ],
+ output: {
+ path: path.join(__dirname, "assets/build"),
+ filename: "inspector.js",
+ publicPath: "/"
+ },
+ module: {
+ // Disable handling of unknown requires
+ unknownContextRegExp: /$^/,
+ unknownContextCritical: false,
+
+ // Disable handling of requires with a single expression
+ exprContextRegExp: /$^/,
+ exprContextCritical: false,
+
+ // Warn for every expression in require
+ wrappedContextCritical: true,
+
+ loaders: [
+ {
+ test: /event-emitter/,
+ exclude: /node_modules/,
+ loaders: [path.join(__dirname, "./webpack/rewrite-event-emitter")],
+ }, {
+ // Replace all references to this.browserRequire() by require() in
+ // client/inspector/*.js files
+ test: /client\/inspector\/.*\.js$/,
+ loaders: [path.join(__dirname, "./webpack/rewrite-browser-require")],
+ }
+ ]
+ },
+ resolveLoader: {
+ root: [
+ path.resolve("./node_modules"),
+ path.resolve("./webpack"),
+ ]
+ },
+ resolve: {
+ alias: {
+ "acorn/util/walk": path.join(__dirname, "../../shared/acorn/walk"),
+ "acorn": path.join(__dirname, "../../shared/acorn"),
+ "devtools/client/framework/about-devtools-toolbox":
+ path.join(__dirname, "./webpack/about-devtools-sham.js"),
+ "devtools/client/framework/attach-thread":
+ path.join(__dirname, "./webpack/attach-thread-sham.js"),
+ "devtools/client/framework/target-from-url":
+ path.join(__dirname, "./webpack/target-from-url-sham.js"),
+ "devtools/client/jsonview/main":
+ path.join(__dirname, "./webpack/jsonview-sham.js"),
+ "devtools/client/sourceeditor/editor":
+ path.join(__dirname, "./webpack/editor-sham.js"),
+ "devtools/client/locales": path.join(__dirname, "../locales/en-US"),
+ "devtools/shared/locales": path.join(__dirname, "../../shared/locales/en-US"),
+ "devtools/shared/platform": path.join(__dirname, "../../shared/platform/content"),
+ "devtools": path.join(__dirname, "../../"),
+ "gcli": path.join(__dirname, "../../shared/gcli/source/lib/gcli"),
+ "method": path.join(__dirname, "../../../addon-sdk/source/lib/method"),
+ "modules/libpref/init/all":
+ path.join(__dirname, "../../../modules/libpref/init/all.js"),
+ "sdk/system/unload": path.join(__dirname, "./webpack/system-unload-sham.js"),
+ "sdk": path.join(__dirname, "../../../addon-sdk/source/lib/sdk"),
+ "Services": path.join(__dirname, "../shared/shim/Services.js"),
+ "toolkit/locales":
+ path.join(__dirname, "../../../toolkit/locales/en-US/chrome/global"),
+ },
+ },
+
+ plugins: [
+ new webpack.DefinePlugin({
+ "isWorker": JSON.stringify(false),
+ "reportError": "console.error",
+ "AppConstants": "{ DEBUG: true, DEBUG_JS_MODULES: true }",
+ "loader": `{
+ lazyRequireGetter: () => {},
+ lazyGetter: () => {}
+ }`,
+ "dump": "console.log",
+ }),
+ ]
+ };
+
+ webpackConfig.externals = [
+ /codemirror\//,
+ {
+ "promise": "var Promise",
+ "devtools/server/main": "{}",
+
+ // Just trying to get build to work. These should be removed eventually:
+ "chrome": "{}",
+
+ // In case you end up in chrome-land you can use this to help track down issues.
+ // SDK for instance does a bunch of this so if you somehow end up importing an SDK
+ // dependency this might help for debugging:
+ // "chrome": `{
+ // Cc: {
+ // "@mozilla.org/uuid-generator;1": { getService: () => { return {} } },
+ // "@mozilla.org/observer-service;1": { getService: () => { return {} } },
+ // },
+ // Ci: {},
+ // Cr: {},
+ // Cm: {},
+ // components: { classesByID: () => {} , ID: () => {} }
+ // }`,
+
+ "resource://gre/modules/XPCOMUtils.jsm": "{}",
+ "resource://devtools/client/styleeditor/StyleEditorUI.jsm": "{}",
+ "resource://devtools/client/styleeditor/StyleEditorUtil.jsm": "{}",
+ "devtools/client/inspector/inspector-panel": "{}",
+ "devtools/server/actors/utils/audionodes.json": "{}",
+
+ "devtools/client/shared/developer-toolbar": "{}",
+
+ // From sdk.
+ "resource://gre/modules/Preferences.jsm": "{}",
+ "@loader/options": "{}",
+ "@loader/unload": "{}",
+ },
+ ];
+
+ // Exclude all files from devtools/ or addon-sdk/ or modules/ .
+ webpackConfig.babelExcludes = /(devtools\/|addon-sdk\/|modules\/)/;
+
+ return toolboxConfig(webpackConfig, envConfig);
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/webpack/about-devtools-sham.js
@@ -0,0 +1,12 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set 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";
+
+module.exports = {
+ register: () => {},
+ unregister: () => {},
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/webpack/attach-thread-sham.js
@@ -0,0 +1,11 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set 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";
+
+module.exports = {
+ attachThread: () => {},
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/webpack/editor-sham.js
@@ -0,0 +1,13 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set 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";
+
+Editor.modes = {};
+function Editor() {}
+Editor.prototype.appendToLocalElement = () => {};
+
+module.exports = Editor;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/webpack/jsonview-sham.js
@@ -0,0 +1,14 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set 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";
+
+module.exports = {
+ JsonView: {
+ initialize: () => {},
+ destroy: () => {},
+ }
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/webpack/prefs-loader.js
@@ -0,0 +1,58 @@
+/* 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/. */
+
+// Rewrite devtools.js or all.js, leaving just the relevant pref() calls.
+
+"use strict";
+
+const PREF_RX = new RegExp("^ *pref\\(\"devtools");
+
+module.exports = function (content) {
+ this.cacheable && this.cacheable();
+
+ // If we're reading devtools.js we have to do some reprocessing.
+ // If we're reading all.js we just assume we can dump all the
+ // conditionals.
+ let isDevtools = this.request.endsWith("/devtools.js");
+
+ // This maps the text of a "#if" to its truth value. This has to
+ // cover all uses of #if in devtools.js.
+ const ifMap = {
+ "#if MOZ_UPDATE_CHANNEL == beta": false,
+ "#if defined(NIGHTLY_BUILD)": false,
+ "#ifdef NIGHTLY_BUILD": false,
+ "#ifdef MOZ_DEV_EDITION": false,
+ "#ifdef RELEASE_OR_BETA": true,
+ "#ifdef RELEASE_BUILD": true,
+ };
+
+ let lines = content.split("\n");
+ let ignoring = false;
+ let newLines = [];
+ let continuation = false;
+ for (let line of lines) {
+ if (line.startsWith("sticky_pref")) {
+ line = line.slice(7);
+ }
+
+ if (isDevtools) {
+ if (line.startsWith("#if")) {
+ if (!(line in ifMap)) {
+ throw new Error("missing line in ifMap: " + line);
+ }
+ ignoring = !ifMap[line];
+ } else if (line.startsWith("#else")) {
+ ignoring = !ignoring;
+ }
+ }
+
+ if (continuation || (!ignoring && PREF_RX.test(line))) {
+ newLines.push(line);
+
+ // The call to pref(...); might span more than one line.
+ continuation = !/\);/.test(line);
+ }
+ }
+ return newLines.join("\n");
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/webpack/rewrite-browser-require.js
@@ -0,0 +1,12 @@
+/* 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/. */
+
+// Replace all occurrences of "this.browserRequire(" by "require(".
+
+"use strict";
+
+module.exports = function (content) {
+ this.cacheable && this.cacheable();
+ return content.replace(/this\.browserRequire\(/g, "require(");
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/webpack/rewrite-event-emitter.js
@@ -0,0 +1,26 @@
+/* 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/. */
+
+// Remove the header code from event-emitter.js. This code confuses
+// webpack.
+
+"use strict";
+
+module.exports = function (content) {
+ this.cacheable && this.cacheable();
+
+ let lines = content.split("\n");
+ let ignoring = false;
+ let newLines = [];
+ for (let line of lines) {
+ if (/function \(factory\)/.test(line)) {
+ ignoring = true;
+ } else if (/call\(this, function /.test(line)) {
+ ignoring = false;
+ } else if (!ignoring && line !== "});") {
+ newLines.push(line);
+ }
+ }
+ return newLines.join("\n");
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/webpack/system-unload-sham.js
@@ -0,0 +1,11 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set 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";
+
+module.exports = {
+ when: () => {},
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/webpack/target-from-url-sham.js
@@ -0,0 +1,11 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set 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";
+
+module.exports = {
+ targetFromURL: () => {},
+};
--- a/devtools/client/shared/shim/Services.js
+++ b/devtools/client/shared/shim/Services.js
@@ -601,16 +601,24 @@ const Services = {
// sufficient for our purposes.
return {
openUILinkIn: function (url) {
window.open(url, "_blank");
},
};
},
},
+
+ /**
+ * Shims for Services.obs.add/removeObserver.
+ */
+ obs: {
+ addObserver: () => {},
+ removeObserver: () => {},
+ },
};
/**
* Create a new preference. This is used during startup (see
* devtools/client/preferences/devtools.js) to install the
* default preferences.
*
* @param {String} name the name of the preference
--- a/devtools/client/shared/undo.js
+++ b/devtools/client/shared/undo.js
@@ -148,18 +148,24 @@ UndoStack.prototype = {
/**
* ViewController implementation for undo/redo.
*/
/**
* Install this object as a command controller.
*/
installController: function (controllerWindow) {
+ let controllers = controllerWindow.controllers;
+ // Only available when running in a Firefox panel.
+ if (!controllers || !controllers.appendController) {
+ return;
+ }
+
this._controllerWindow = controllerWindow;
- controllerWindow.controllers.appendController(this);
+ controllers.appendController(this);
},
/**
* Uninstall this object from the command controller.
*/
uninstallController: function () {
if (!this._controllerWindow) {
return;
--- a/devtools/client/shared/widgets/tooltip/TooltipToggle.js
+++ b/devtools/client/shared/widgets/tooltip/TooltipToggle.js
@@ -64,25 +64,28 @@ TooltipToggle.prototype = {
* - {Number} toggleDelay
* An optional delay (in ms) that will be observed before showing
* and before hiding the tooltip. Defaults to DEFAULT_TOGGLE_DELAY.
* - {Boolean} interactive
* If enabled, the tooltip is not hidden when mouse leaves the
* target element and enters the tooltip. Allows the tooltip
* content to be interactive.
*/
- start: function (baseNode, targetNodeCb,
- {toggleDelay = DEFAULT_TOGGLE_DELAY, interactive = false} = {}) {
+ start: function (baseNode, targetNodeCb, {toggleDelay, interactive = false} = {}) {
this.stop();
if (!baseNode) {
// Calling tool is in the process of being destroyed.
return;
}
+ if (typeof toggleDelay === "undefined") {
+ toggleDelay = DEFAULT_TOGGLE_DELAY;
+ }
+
this._baseNode = baseNode;
this._targetNodeCb = targetNodeCb || (() => true);
this._toggleDelay = toggleDelay;
this._interactive = interactive;
baseNode.addEventListener("mousemove", this._onMouseMove);
baseNode.addEventListener("mouseout", this._onMouseOut);
--- a/devtools/shared/DevToolsUtils.js
+++ b/devtools/shared/DevToolsUtils.js
@@ -21,17 +21,18 @@ const ThreadSafeDevToolsUtils = require(
for (let key of Object.keys(ThreadSafeDevToolsUtils)) {
exports[key] = ThreadSafeDevToolsUtils[key];
}
/**
* Waits for the next tick in the event loop to execute a callback.
*/
exports.executeSoon = function executeSoon(aFn) {
- if (isWorker) {
+ // XXX: Move setImmmediate chrome implementation to loader
+ if (typeof setImmediate !== "undefined") {
setImmediate(aFn);
} else {
let executor;
// Only enable async stack reporting when DEBUG_JS_MODULES is set
// (customized local builds) to avoid a performance penalty.
if (AppConstants.DEBUG_JS_MODULES || flags.testing) {
let stack = getStack();
executor = () => {