Bug 1356244 - devtools-addon: create low-level DevTools shim for mozilla-central;r=ochameau

MozReview-Commit-ID: KdfomLHgdLY

--HG--
extra : rebase_source : 5fff97798bfe3eeac98c8be12cbcfe6e6177afa8
extra : source : 2d422bec07d3a5bab5d83b4c949f27c6e49be261
This commit is contained in:
Julian Descottes 2017-05-18 09:12:22 +02:00
Родитель 0ce286101c
Коммит 52acc63ebc
9 изменённых файлов: 413 добавлений и 0 удалений

Просмотреть файл

@ -622,6 +622,10 @@
@RESPATH@/browser/chrome/webide.manifest
@RESPATH@/browser/@PREF_DIR@/webide-prefs.js
; [DevTools Shim Files]
@RESPATH@/browser/chrome/devtools-shim@JAREXT@
@RESPATH@/browser/chrome/devtools-shim.manifest
; DevTools
@RESPATH@/browser/chrome/devtools@JAREXT@
@RESPATH@/browser/chrome/devtools.manifest

Просмотреть файл

@ -8,6 +8,7 @@ const {Cu} = require("chrome");
const Services = require("Services");
// Load gDevToolsBrowser toolbox lazily as they need gDevTools to be fully initialized
loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
loader.lazyRequireGetter(this, "ToolboxHostManager", "devtools/client/framework/toolbox-host-manager", true);
loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
@ -468,6 +469,18 @@ DevTools.prototype = {
return true;
}),
/**
* Wrapper on TargetFactory.forTab, constructs a Target for the provided tab.
*
* @param {XULTab} tab
* The tab to use in creating a new target.
*
* @return {TabTarget} A target object
*/
getTargetForTab: function (tab) {
return TargetFactory.forTab(tab);
},
/**
* Either the SDK Loader has been destroyed by the add-on contribution
* workflow, or firefox is shutting down.

Просмотреть файл

@ -15,6 +15,7 @@ if CONFIG['MOZ_DEVTOOLS'] == 'all':
DIRS += [
'server',
'shared',
'shim',
]
# /browser uses DIST_SUBDIR. We opt-in to this treatment when building

Просмотреть файл

@ -0,0 +1,170 @@
/* 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";
this.EXPORTED_SYMBOLS = [
"DevToolsShim",
];
function removeItem(array, callback) {
let index = array.findIndex(callback);
if (index >= 0) {
array.splice(index, 1);
}
}
/**
* The DevToolsShim is a part of the DevTools go faster project, which moves the Firefox
* DevTools outside of mozilla-central to an add-on. It aims to bridge the gap for
* existing mozilla-central code that still needs to interact with DevTools (such as
* web-extensions).
*
* DevToolsShim is a singleton that provides a set of helpers to interact with DevTools,
* that work whether the DevTools addon is installed or not. It can be used to start
* listening to events, register tools, themes. As soon as a DevTools addon is installed
* the DevToolsShim will forward all the requests received until then to the real DevTools
* instance.
*
* DevToolsShim.isInstalled() can also be used to know if DevTools are currently
* installed.
*/
this.DevToolsShim = {
gDevTools: null,
listeners: [],
tools: [],
themes: [],
/**
* Check if DevTools are currently installed and available.
*
* @return {Boolean} true if DevTools are installed.
*/
isInstalled: function () {
return !!this.gDevTools;
},
/**
* Register an instance of gDevTools. Should be called by DevTools during startup.
*
* @param {DevTools} a devtools instance (from client/framework/devtools)
*/
register: function (gDevTools) {
this.gDevTools = gDevTools;
this._onDevToolsRegistered();
},
/**
* Unregister the current instance of gDevTools. Should be called by DevTools during
* shutdown.
*/
unregister: function () {
this.gDevTools = null;
},
/**
* The following methods can be called before DevTools are installed:
* - on
* - off
* - registerTool
* - unregisterTool
* - registerTheme
* - unregisterTheme
*
* If DevTools are not installed when calling the method, DevToolsShim will call the
* appropriate method as soon as a gDevTools instance is registered.
*/
/**
* This method is used by browser/components/extensions/ext-devtools.js for the events:
* - toolbox-created
* - toolbox-destroyed
*/
on: function (event, listener) {
if (this.isInstalled()) {
this.gDevTools.on(event, listener);
} else {
this.listeners.push([event, listener]);
}
},
/**
* This method is currently only used by devtools code, but is kept here for consistency
* with on().
*/
off: function (event, listener) {
if (this.isInstalled()) {
this.gDevTools.off(event, listener);
} else {
removeItem(this.listeners, ([e, l]) => e === event && l === listener);
}
},
/**
* This method is only used by the addon-sdk and should be removed when Firefox 56 is
* no longer supported.
*/
registerTool: function (tool) {
if (this.isInstalled()) {
this.gDevTools.registerTool(tool);
} else {
this.tools.push(tool);
}
},
/**
* This method is only used by the addon-sdk and should be removed when Firefox 56 is
* no longer supported.
*/
unregisterTool: function (tool) {
if (this.isInstalled()) {
this.gDevTools.unregisterTool(tool);
} else {
removeItem(this.tools, t => t === tool);
}
},
/**
* This method is only used by the addon-sdk and should be removed when Firefox 56 is
* no longer supported.
*/
registerTheme: function (theme) {
if (this.isInstalled()) {
this.gDevTools.registerTheme(theme);
} else {
this.themes.push(theme);
}
},
/**
* This method is only used by the addon-sdk and should be removed when Firefox 56 is
* no longer supported.
*/
unregisterTheme: function (theme) {
if (this.isInstalled()) {
this.gDevTools.unregisterTheme(theme);
} else {
removeItem(this.themes, t => t === theme);
}
},
_onDevToolsRegistered: function () {
// Register all pending event listeners on the real gDevTools object.
for (let [event, listener] of this.listeners) {
this.gDevTools.on(event, listener);
}
for (let tool of this.tools) {
this.gDevTools.registerTool(tool);
}
for (let theme of this.themes) {
this.gDevTools.registerTheme(theme);
}
this.listeners = [];
this.tools = [];
this.themes = [];
},
};

3
devtools/shim/jar.mn Normal file
Просмотреть файл

@ -0,0 +1,3 @@
devtools-shim.jar:
% content devtools-shim %content/
content/DevToolsShim.jsm (DevToolsShim.jsm)

3
devtools/shim/moz.build Normal file
Просмотреть файл

@ -0,0 +1,3 @@
JAR_MANIFESTS += ['jar.mn']
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']

Просмотреть файл

@ -0,0 +1,21 @@
// This file was copied from the .eslintrc.xpcshell.js
// This new xpcshell test folder should stay in mozilla-central while devtools move to a
// GitHub repository, hence the duplication.
module.exports = {
"extends": [
"plugin:mozilla/xpcshell-test"
],
"rules": {
// Allow non-camelcase so that run_test doesn't produce a warning.
"camelcase": "off",
// Allow using undefined variables so that tests can refer to functions
// and variables defined in head.js files, without having to maintain a
// list of globals in each .eslintrc file.
// Note that bug 1168340 will eventually help auto-registering globals
// from head.js files.
"no-undef": "off",
"block-scoped-var": "off",
// Tests can always import anything.
"mozilla/reject-some-requires": "off",
}
};

Просмотреть файл

@ -0,0 +1,192 @@
/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { DevToolsShim } =
Components.utils.import("chrome://devtools-shim/content/DevToolsShim.jsm", {});
// Test the DevToolsShim
/**
* Create a mocked version of DevTools that records all calls made to methods expected
* to be called by DevToolsShim.
*/
function createMockDevTools() {
let methods = [
"on",
"off",
"registerTool",
"registerTheme",
"unregisterTool",
"unregisterTheme",
];
let mock = {
callLog: {}
};
for (let method of methods) {
// Create a stub for method, that only pushes its arguments in the inner callLog
mock[method] = function (...args) {
mock.callLog[method].push(args);
};
mock.callLog[method] = [];
}
return mock;
}
/**
* Check if a given method was called an expected number of times, and finally check the
* arguments provided to the last call, if appropriate.
*/
function checkCalls(mock, method, length, lastArgs) {
ok(mock.callLog[method].length === length,
"Devtools.on was called the expected number of times");
// If we don't want to check the last call or if the method was never called, bail out.
if (!lastArgs || length === 0) {
return;
}
for (let i = 0; i < lastArgs.length; i++) {
let expectedArg = lastArgs[i];
ok(mock.callLog[method][length - 1][i] === expectedArg,
`Devtools.${method} was called with the expected argument (index ${i})`);
}
}
function test_register_unregister() {
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
DevToolsShim.register(createMockDevTools());
ok(DevToolsShim.isInstalled(), "DevTools are installed");
DevToolsShim.unregister();
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
}
function test_on_is_forwarded_to_devtools() {
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
function cb1() {}
function cb2() {}
let mock = createMockDevTools();
DevToolsShim.on("test_event", cb1);
DevToolsShim.register(mock);
checkCalls(mock, "on", 1, ["test_event", cb1]);
DevToolsShim.on("other_event", cb2);
checkCalls(mock, "on", 2, ["other_event", cb2]);
}
function test_off_called_before_registering_devtools() {
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
function cb1() {}
let mock = createMockDevTools();
DevToolsShim.on("test_event", cb1);
DevToolsShim.off("test_event", cb1);
DevToolsShim.register(mock);
checkCalls(mock, "on", 0);
}
function test_off_called_before_with_bad_callback() {
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
function cb1() {}
function cb2() {}
let mock = createMockDevTools();
DevToolsShim.on("test_event", cb1);
DevToolsShim.off("test_event", cb2);
DevToolsShim.register(mock);
// on should still be called
checkCalls(mock, "on", 1, ["test_event", cb1]);
// Calls to off should not be held and forwarded.
checkCalls(mock, "off", 0);
}
function test_registering_tool() {
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
let tool1 = {};
let tool2 = {};
let tool3 = {};
let mock = createMockDevTools();
// Pre-register tool1
DevToolsShim.registerTool(tool1);
// Pre-register tool3, but unregister right after
DevToolsShim.registerTool(tool3);
DevToolsShim.unregisterTool(tool3);
DevToolsShim.register(mock);
checkCalls(mock, "registerTool", 1, [tool1]);
DevToolsShim.registerTool(tool2);
checkCalls(mock, "registerTool", 2, [tool2]);
DevToolsShim.unregister();
// Create a new mock and check the tools are not added once again.
mock = createMockDevTools();
DevToolsShim.register(mock);
checkCalls(mock, "registerTool", 0);
}
function test_registering_theme() {
ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
let theme1 = {};
let theme2 = {};
let theme3 = {};
let mock = createMockDevTools();
// Pre-register theme1
DevToolsShim.registerTheme(theme1);
// Pre-register theme3, but unregister right after
DevToolsShim.registerTheme(theme3);
DevToolsShim.unregisterTheme(theme3);
DevToolsShim.register(mock);
checkCalls(mock, "registerTheme", 1, [theme1]);
DevToolsShim.registerTheme(theme2);
checkCalls(mock, "registerTheme", 2, [theme2]);
DevToolsShim.unregister();
// Create a new mock and check the themes are not added once again.
mock = createMockDevTools();
DevToolsShim.register(mock);
checkCalls(mock, "registerTheme", 0);
}
function run_test() {
test_register_unregister();
DevToolsShim.unregister();
test_on_is_forwarded_to_devtools();
DevToolsShim.unregister();
test_off_called_before_registering_devtools();
DevToolsShim.unregister();
test_off_called_before_with_bad_callback();
DevToolsShim.unregister();
test_registering_tool();
DevToolsShim.unregister();
test_registering_theme();
DevToolsShim.unregister();
}

Просмотреть файл

@ -0,0 +1,6 @@
[DEFAULT]
tags = devtools
firefox-appdir = browser
skip-if = toolkit == 'android'
[test_devtools_shim.js]