mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-09 09:18:42 +09:00
Move Add-on SDK source to toolkit/jetpack
This commit is contained in:
parent
0a03067ee3
commit
23b67db438
993 changed files with 484 additions and 100663 deletions
95
toolkit/jetpack/dev/debuggee.js
Normal file
95
toolkit/jetpack/dev/debuggee.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/* 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.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
const { Cu } = require("chrome");
|
||||
const { Class } = require("../sdk/core/heritage");
|
||||
const { MessagePort, MessageChannel } = require("../sdk/messaging");
|
||||
const { require: devtoolsRequire } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const { DebuggerServer } = devtoolsRequire("devtools/server/main");
|
||||
|
||||
const outputs = new WeakMap();
|
||||
const inputs = new WeakMap();
|
||||
const targets = new WeakMap();
|
||||
const transports = new WeakMap();
|
||||
|
||||
const inputFor = port => inputs.get(port);
|
||||
const outputFor = port => outputs.get(port);
|
||||
const transportFor = port => transports.get(port);
|
||||
|
||||
const fromTarget = target => {
|
||||
const debuggee = new Debuggee();
|
||||
const { port1, port2 } = new MessageChannel();
|
||||
inputs.set(debuggee, port1);
|
||||
outputs.set(debuggee, port2);
|
||||
targets.set(debuggee, target);
|
||||
|
||||
return debuggee;
|
||||
};
|
||||
exports.fromTarget = fromTarget;
|
||||
|
||||
const Debuggee = Class({
|
||||
extends: MessagePort.prototype,
|
||||
close: function() {
|
||||
const server = transportFor(this);
|
||||
if (server) {
|
||||
transports.delete(this);
|
||||
server.close();
|
||||
}
|
||||
outputFor(this).close();
|
||||
},
|
||||
start: function() {
|
||||
const target = targets.get(this);
|
||||
if (target.isLocalTab) {
|
||||
// Since a remote protocol connection will be made, let's start the
|
||||
// DebuggerServer here, once and for all tools.
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
}
|
||||
|
||||
transports.set(this, DebuggerServer.connectPipe());
|
||||
}
|
||||
// TODO: Implement support for remote connections (See Bug 980421)
|
||||
else {
|
||||
throw Error("Remote targets are not yet supported");
|
||||
}
|
||||
|
||||
// pipe messages send to the debuggee to an actual
|
||||
// server via remote debugging protocol transport.
|
||||
inputFor(this).addEventListener("message", ({data}) =>
|
||||
transportFor(this).send(data));
|
||||
|
||||
// pipe messages received from the remote debugging
|
||||
// server transport onto the this debuggee.
|
||||
transportFor(this).hooks = {
|
||||
onPacket: packet => inputFor(this).postMessage(packet),
|
||||
onClosed: () => inputFor(this).close()
|
||||
};
|
||||
|
||||
inputFor(this).start();
|
||||
outputFor(this).start();
|
||||
},
|
||||
postMessage: function(data) {
|
||||
return outputFor(this).postMessage(data);
|
||||
},
|
||||
get onmessage() {
|
||||
return outputFor(this).onmessage;
|
||||
},
|
||||
set onmessage(onmessage) {
|
||||
outputFor(this).onmessage = onmessage;
|
||||
},
|
||||
addEventListener: function(...args) {
|
||||
return outputFor(this).addEventListener(...args);
|
||||
},
|
||||
removeEventListener: function(...args) {
|
||||
return outputFor(this).removeEventListener(...args);
|
||||
}
|
||||
});
|
||||
exports.Debuggee = Debuggee;
|
||||
120
toolkit/jetpack/dev/frame-script.js
Normal file
120
toolkit/jetpack/dev/frame-script.js
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/* 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";
|
||||
(function({content, sendSyncMessage, addMessageListener, sendAsyncMessage}) {
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const observerService = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
|
||||
const channels = new Map();
|
||||
const handles = new WeakMap();
|
||||
|
||||
// Takes remote port handle and creates a local one.
|
||||
// also set's up a messaging channel between them.
|
||||
// This is temporary workaround until Bug 914974 is fixed
|
||||
// and port can be transfered through message manager.
|
||||
const demarshal = (handle) => {
|
||||
if (handle.type === "MessagePort") {
|
||||
if (!channels.has(handle.id)) {
|
||||
const channel = new content.MessageChannel();
|
||||
channels.set(handle.id, channel);
|
||||
handles.set(channel.port1, handle);
|
||||
channel.port1.onmessage = onOutPort;
|
||||
}
|
||||
return channels.get(handle.id).port2;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const onOutPort = event => {
|
||||
const handle = handles.get(event.target);
|
||||
sendAsyncMessage("sdk/port/message", {
|
||||
port: handle,
|
||||
message: event.data
|
||||
});
|
||||
};
|
||||
|
||||
const onInPort = ({data}) => {
|
||||
const channel = channels.get(data.port.id);
|
||||
if (channel)
|
||||
channel.port1.postMessage(data.message);
|
||||
};
|
||||
|
||||
const onOutEvent = event =>
|
||||
sendSyncMessage("sdk/event/" + event.type,
|
||||
{ type: event.type,
|
||||
data: event.data });
|
||||
|
||||
const onInMessage = (message) => {
|
||||
const {type, data, origin, bubbles, cancelable, ports} = message.data;
|
||||
|
||||
const event = new content.MessageEvent(type, {
|
||||
bubbles: bubbles,
|
||||
cancelable: cancelable,
|
||||
data: data,
|
||||
origin: origin,
|
||||
target: content,
|
||||
source: content,
|
||||
ports: ports.map(demarshal)
|
||||
});
|
||||
content.dispatchEvent(event);
|
||||
};
|
||||
|
||||
const onReady = event => {
|
||||
channels.clear();
|
||||
};
|
||||
|
||||
addMessageListener("sdk/event/message", onInMessage);
|
||||
addMessageListener("sdk/port/message", onInPort);
|
||||
|
||||
const observer = {
|
||||
handleEvent: ({target, type}) => {
|
||||
observer.observe(target, type);
|
||||
},
|
||||
observe: (document, topic, data) => {
|
||||
// When frame associated with message manager is removed from document `docShell`
|
||||
// is set to `null` but observer is still kept alive. At this point accesing
|
||||
// `content.document` throws "can't access dead object" exceptions. In order to
|
||||
// avoid leaking observer and logged errors observer is going to be removed when
|
||||
// `docShell` is set to `null`.
|
||||
if (!docShell) {
|
||||
observerService.removeObserver(observer, topic);
|
||||
}
|
||||
else if (document === content.document) {
|
||||
if (topic.endsWith("-document-interactive")) {
|
||||
sendAsyncMessage("sdk/event/ready", {
|
||||
type: "ready",
|
||||
readyState: document.readyState,
|
||||
uri: document.documentURI
|
||||
});
|
||||
}
|
||||
if (topic.endsWith("-document-loaded")) {
|
||||
sendAsyncMessage("sdk/event/load", {
|
||||
type: "load",
|
||||
readyState: document.readyState,
|
||||
uri: document.documentURI
|
||||
});
|
||||
}
|
||||
if (topic === "unload") {
|
||||
channels.clear();
|
||||
sendAsyncMessage("sdk/event/unload", {
|
||||
type: "unload",
|
||||
readyState: "uninitialized",
|
||||
uri: document.documentURI
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
observerService.addObserver(observer, "content-document-interactive", false);
|
||||
observerService.addObserver(observer, "content-document-loaded", false);
|
||||
observerService.addObserver(observer, "chrome-document-interactive", false);
|
||||
observerService.addObserver(observer, "chrome-document-loaded", false);
|
||||
addEventListener("unload", observer, false);
|
||||
|
||||
})(this);
|
||||
259
toolkit/jetpack/dev/panel.js
Normal file
259
toolkit/jetpack/dev/panel.js
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
/* 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.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
const { Cu } = require("chrome");
|
||||
const { Class } = require("../sdk/core/heritage");
|
||||
const { curry } = require("../sdk/lang/functional");
|
||||
const { EventTarget } = require("../sdk/event/target");
|
||||
const { Disposable, setup, dispose } = require("../sdk/core/disposable");
|
||||
const { emit, off, setListeners } = require("../sdk/event/core");
|
||||
const { when } = require("../sdk/event/utils");
|
||||
const { getFrameElement } = require("../sdk/window/utils");
|
||||
const { contract, validate } = require("../sdk/util/contract");
|
||||
const { data: { url: resolve }} = require("../sdk/self");
|
||||
const { identify } = require("../sdk/ui/id");
|
||||
const { isLocalURL, URL } = require("../sdk/url");
|
||||
const { encode } = require("../sdk/base64");
|
||||
const { marshal, demarshal } = require("./ports");
|
||||
const { fromTarget } = require("./debuggee");
|
||||
const { removed } = require("../sdk/dom/events");
|
||||
const { id: addonID } = require("../sdk/self");
|
||||
const { viewFor } = require("../sdk/view/core");
|
||||
const { createView } = require("./panel/view");
|
||||
|
||||
const OUTER_FRAME_URI = module.uri.replace(/\.js$/, ".html");
|
||||
const FRAME_SCRIPT = module.uri.replace("/panel.js", "/frame-script.js");
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
const makeID = name =>
|
||||
("dev-panel-" + addonID + "-" + name).
|
||||
split("/").join("-").
|
||||
split(".").join("-").
|
||||
split(" ").join("-").
|
||||
replace(/[^A-Za-z0-9_\-]/g, "");
|
||||
|
||||
|
||||
// Weak mapping between `Panel` instances and their frame's
|
||||
// `nsIMessageManager`.
|
||||
const managers = new WeakMap();
|
||||
// Return `nsIMessageManager` for the given `Panel` instance.
|
||||
const managerFor = x => managers.get(x);
|
||||
|
||||
// Weak mappinging between iframe's and their owner
|
||||
// `Panel` instances.
|
||||
const panels = new WeakMap();
|
||||
const panelFor = frame => panels.get(frame);
|
||||
|
||||
// Weak mapping between panels and debugees they're targeting.
|
||||
const debuggees = new WeakMap();
|
||||
const debuggeeFor = panel => debuggees.get(panel);
|
||||
|
||||
const frames = new WeakMap();
|
||||
const frameFor = panel => frames.get(panel);
|
||||
|
||||
const setAttributes = (node, attributes) => {
|
||||
for (var key in attributes)
|
||||
node.setAttribute(key, attributes[key]);
|
||||
};
|
||||
|
||||
const onStateChange = ({target, data}) => {
|
||||
const panel = panelFor(target);
|
||||
panel.readyState = data.readyState;
|
||||
emit(panel, data.type, { target: panel, type: data.type });
|
||||
};
|
||||
|
||||
// port event listener on the message manager that demarshalls
|
||||
// and forwards to the actual receiver. This is a workaround
|
||||
// until Bug 914974 is fixed.
|
||||
const onPortMessage = ({data, target}) => {
|
||||
const port = demarshal(target, data.port);
|
||||
if (port)
|
||||
port.postMessage(data.message);
|
||||
};
|
||||
|
||||
// When frame is removed from the toolbox destroy panel
|
||||
// associated with it to release all the resources.
|
||||
const onFrameRemove = frame => {
|
||||
panelFor(frame).destroy();
|
||||
};
|
||||
|
||||
const onFrameInited = frame => {
|
||||
frame.style.visibility = "visible";
|
||||
}
|
||||
|
||||
const inited = frame => new Promise(resolve => {
|
||||
const { messageManager } = frame.frameLoader;
|
||||
const listener = message => {
|
||||
messageManager.removeMessageListener("sdk/event/ready", listener);
|
||||
resolve(frame);
|
||||
};
|
||||
messageManager.addMessageListener("sdk/event/ready", listener);
|
||||
});
|
||||
|
||||
const getTarget = ({target}) => target;
|
||||
|
||||
const Panel = Class({
|
||||
extends: Disposable,
|
||||
implements: [EventTarget],
|
||||
get id() {
|
||||
return makeID(this.name || this.label);
|
||||
},
|
||||
readyState: "uninitialized",
|
||||
ready: function() {
|
||||
const { readyState } = this;
|
||||
const isReady = readyState === "complete" ||
|
||||
readyState === "interactive";
|
||||
return isReady ? Promise.resolve(this) :
|
||||
when(this, "ready").then(getTarget);
|
||||
},
|
||||
loaded: function() {
|
||||
const { readyState } = this;
|
||||
const isLoaded = readyState === "complete";
|
||||
return isLoaded ? Promise.resolve(this) :
|
||||
when(this, "load").then(getTarget);
|
||||
},
|
||||
unloaded: function() {
|
||||
const { readyState } = this;
|
||||
const isUninitialized = readyState === "uninitialized";
|
||||
return isUninitialized ? Promise.resolve(this) :
|
||||
when(this, "unload").then(getTarget);
|
||||
},
|
||||
postMessage: function(data, ports=[]) {
|
||||
const manager = managerFor(this);
|
||||
manager.sendAsyncMessage("sdk/event/message", {
|
||||
type: "message",
|
||||
bubbles: false,
|
||||
cancelable: false,
|
||||
data: data,
|
||||
origin: this.url,
|
||||
ports: ports.map(marshal(manager))
|
||||
});
|
||||
}
|
||||
});
|
||||
exports.Panel = Panel;
|
||||
|
||||
validate.define(Panel, contract({
|
||||
label: {
|
||||
is: ["string"],
|
||||
msg: "The `option.label` must be a provided"
|
||||
},
|
||||
tooltip: {
|
||||
is: ["string", "undefined"],
|
||||
msg: "The `option.tooltip` must be a string"
|
||||
},
|
||||
icon: {
|
||||
is: ["string"],
|
||||
map: x => x && resolve(x),
|
||||
ok: x => isLocalURL(x),
|
||||
msg: "The `options.icon` must be a valid local URI."
|
||||
},
|
||||
url: {
|
||||
map: x => resolve(x.toString()),
|
||||
is: ["string"],
|
||||
ok: x => isLocalURL(x),
|
||||
msg: "The `options.url` must be a valid local URI."
|
||||
},
|
||||
invertIconForLightTheme: {
|
||||
is: ["boolean", "undefined"],
|
||||
msg: "The `options.invertIconForLightTheme` must be a boolean."
|
||||
},
|
||||
invertIconForDarkTheme: {
|
||||
is: ["boolean", "undefined"],
|
||||
msg: "The `options.invertIconForDarkTheme` must be a boolean."
|
||||
}
|
||||
}));
|
||||
|
||||
setup.define(Panel, (panel, {window, toolbox, url}) => {
|
||||
// Hack: Given that iframe created by devtools API is no good for us,
|
||||
// we obtain original iframe and replace it with the one that has
|
||||
// desired configuration.
|
||||
const original = getFrameElement(window);
|
||||
const container = original.parentNode;
|
||||
original.remove();
|
||||
const frame = createView(panel, container.ownerDocument);
|
||||
|
||||
// Following modifications are a temporary workaround until Bug 1049188
|
||||
// is fixed.
|
||||
// Enforce certain iframe customizations regardless of users request.
|
||||
setAttributes(frame, {
|
||||
"id": original.id,
|
||||
"src": url,
|
||||
"flex": 1,
|
||||
"forceOwnRefreshDriver": "",
|
||||
"tooltip": "aHTMLTooltip"
|
||||
});
|
||||
frame.style.visibility = "hidden";
|
||||
frame.classList.add("toolbox-panel-iframe");
|
||||
// Inject iframe into designated node until add-on author decides
|
||||
// to inject it elsewhere instead.
|
||||
if (!frame.parentNode)
|
||||
container.appendChild(frame);
|
||||
|
||||
// associate view with a panel
|
||||
frames.set(panel, frame);
|
||||
|
||||
// associate panel model with a frame view.
|
||||
panels.set(frame, panel);
|
||||
|
||||
const debuggee = fromTarget(toolbox.target);
|
||||
// associate debuggee with a panel.
|
||||
debuggees.set(panel, debuggee);
|
||||
|
||||
|
||||
// Setup listeners for the frame message manager.
|
||||
const { messageManager } = frame.frameLoader;
|
||||
messageManager.addMessageListener("sdk/event/ready", onStateChange);
|
||||
messageManager.addMessageListener("sdk/event/load", onStateChange);
|
||||
messageManager.addMessageListener("sdk/event/unload", onStateChange);
|
||||
messageManager.addMessageListener("sdk/port/message", onPortMessage);
|
||||
messageManager.loadFrameScript(FRAME_SCRIPT, false);
|
||||
|
||||
managers.set(panel, messageManager);
|
||||
|
||||
// destroy panel if frame is removed.
|
||||
removed(frame).then(onFrameRemove);
|
||||
// show frame when it is initialized.
|
||||
inited(frame).then(onFrameInited);
|
||||
|
||||
|
||||
// set listeners if there are ones defined on the prototype.
|
||||
setListeners(panel, Object.getPrototypeOf(panel));
|
||||
|
||||
|
||||
panel.setup({ debuggee: debuggee });
|
||||
});
|
||||
|
||||
createView.define(Panel, (panel, document) => {
|
||||
const frame = document.createElement("iframe");
|
||||
setAttributes(frame, {
|
||||
"sandbox": "allow-scripts",
|
||||
// We end up using chrome iframe with forced message manager
|
||||
// as fixing a swapFrameLoader seemed like a giant task (see
|
||||
// Bug 1075490).
|
||||
"type": "chrome",
|
||||
"forcemessagemanager": true,
|
||||
"transparent": true,
|
||||
"seamless": "seamless",
|
||||
});
|
||||
return frame;
|
||||
});
|
||||
|
||||
dispose.define(Panel, function(panel) {
|
||||
debuggeeFor(panel).close();
|
||||
|
||||
debuggees.delete(panel);
|
||||
managers.delete(panel);
|
||||
frames.delete(panel);
|
||||
panel.readyState = "destroyed";
|
||||
panel.dispose();
|
||||
});
|
||||
|
||||
viewFor.define(Panel, frameFor);
|
||||
14
toolkit/jetpack/dev/panel/view.js
Normal file
14
toolkit/jetpack/dev/panel/view.js
Normal file
|
|
@ -0,0 +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";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
const { method } = require("method/core");
|
||||
|
||||
const createView = method("dev/panel/view#createView");
|
||||
exports.createView = createView;
|
||||
64
toolkit/jetpack/dev/ports.js
Normal file
64
toolkit/jetpack/dev/ports.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* 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.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
// This module provides `marshal` and `demarshal` functions
|
||||
// that can be used to send MessagePort's over `nsIFrameMessageManager`
|
||||
// until Bug 914974 is fixed.
|
||||
|
||||
const { add, iterator } = require("../sdk/lang/weak-set");
|
||||
const { curry } = require("../sdk/lang/functional");
|
||||
|
||||
var id = 0;
|
||||
const ports = new WeakMap();
|
||||
|
||||
// Takes `nsIFrameMessageManager` and `MessagePort` instances
|
||||
// and returns a handle representing given `port`. Messages
|
||||
// received on given `port` will be forwarded to a message
|
||||
// manager under `sdk/port/message` and messages like:
|
||||
// { port: { type: "MessagePort", id: 2}, data: data }
|
||||
// Where id is an identifier associated with a given `port`
|
||||
// and `data` is an `event.data` received on port.
|
||||
const marshal = curry((manager, port) => {
|
||||
if (!ports.has(port)) {
|
||||
id = id + 1;
|
||||
const handle = {type: "MessagePort", id: id};
|
||||
// Bind id to the given port
|
||||
ports.set(port, handle);
|
||||
|
||||
// Obtain a weak reference to a port.
|
||||
add(exports, port);
|
||||
|
||||
port.onmessage = event => {
|
||||
manager.sendAsyncMessage("sdk/port/message", {
|
||||
port: handle,
|
||||
message: event.data
|
||||
});
|
||||
};
|
||||
|
||||
return handle;
|
||||
}
|
||||
return ports.get(port);
|
||||
});
|
||||
exports.marshal = marshal;
|
||||
|
||||
// Takes `nsIFrameMessageManager` instance and a handle returned
|
||||
// `marshal(manager, port)` returning a `port` that was passed
|
||||
// to it. Note that `port` may be GC-ed in which case returned
|
||||
// value will be `null`.
|
||||
const demarshal = curry((manager, {type, id}) => {
|
||||
if (type === "MessagePort") {
|
||||
for (let port of iterator(exports)) {
|
||||
if (id === ports.get(port).id)
|
||||
return port;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
exports.demarshal = demarshal;
|
||||
135
toolkit/jetpack/dev/theme.js
Normal file
135
toolkit/jetpack/dev/theme.js
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/* 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.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
const { Class } = require("../sdk/core/heritage");
|
||||
const { EventTarget } = require("../sdk/event/target");
|
||||
const { Disposable, setup, dispose } = require("../sdk/core/disposable");
|
||||
const { contract, validate } = require("../sdk/util/contract");
|
||||
const { id: addonID } = require("../sdk/self");
|
||||
const { onEnable, onDisable } = require("dev/theme/hooks");
|
||||
const { isString, instanceOf, isFunction } = require("sdk/lang/type");
|
||||
const { add } = require("sdk/util/array");
|
||||
const { data } = require("../sdk/self");
|
||||
const { isLocalURL } = require("../sdk/url");
|
||||
|
||||
const makeID = name =>
|
||||
("dev-theme-" + addonID + (name ? "-" + name : "")).
|
||||
split(/[ . /]/).join("-").
|
||||
replace(/[^A-Za-z0-9_\-]/g, "");
|
||||
|
||||
const Theme = Class({
|
||||
extends: Disposable,
|
||||
implements: [EventTarget],
|
||||
|
||||
initialize: function(options) {
|
||||
this.name = options.name;
|
||||
this.label = options.label;
|
||||
this.styles = options.styles;
|
||||
|
||||
// Event handlers
|
||||
this.onEnable = options.onEnable;
|
||||
this.onDisable = options.onDisable;
|
||||
},
|
||||
get id() {
|
||||
return makeID(this.name || this.label);
|
||||
},
|
||||
setup: function() {
|
||||
// Any initialization steps done at the registration time.
|
||||
},
|
||||
getStyles: function() {
|
||||
if (!this.styles) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (isString(this.styles)) {
|
||||
if (isLocalURL(this.styles)) {
|
||||
return [data.url(this.styles)];
|
||||
}
|
||||
}
|
||||
|
||||
let result = [];
|
||||
for (let style of this.styles) {
|
||||
if (isString(style)) {
|
||||
if (isLocalURL(style)) {
|
||||
style = data.url(style);
|
||||
}
|
||||
add(result, style);
|
||||
} else if (instanceOf(style, Theme)) {
|
||||
result = result.concat(style.getStyles());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
getClassList: function() {
|
||||
let result = [];
|
||||
for (let style of this.styles) {
|
||||
if (instanceOf(style, Theme)) {
|
||||
result = result.concat(style.getClassList());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.name) {
|
||||
add(result, this.name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
exports.Theme = Theme;
|
||||
|
||||
// Initialization & dispose
|
||||
|
||||
setup.define(Theme, (theme) => {
|
||||
theme.classList = [];
|
||||
theme.setup();
|
||||
});
|
||||
|
||||
dispose.define(Theme, function(theme) {
|
||||
theme.dispose();
|
||||
});
|
||||
|
||||
// Validation
|
||||
|
||||
validate.define(Theme, contract({
|
||||
label: {
|
||||
is: ["string"],
|
||||
msg: "The `option.label` must be a provided"
|
||||
},
|
||||
}));
|
||||
|
||||
// Support theme events: apply and unapply the theme.
|
||||
|
||||
onEnable.define(Theme, (theme, {window, oldTheme}) => {
|
||||
if (isFunction(theme.onEnable)) {
|
||||
theme.onEnable(window, oldTheme);
|
||||
}
|
||||
});
|
||||
|
||||
onDisable.define(Theme, (theme, {window, newTheme}) => {
|
||||
if (isFunction(theme.onDisable)) {
|
||||
theme.onDisable(window, newTheme);
|
||||
}
|
||||
});
|
||||
|
||||
// Support for built-in themes
|
||||
|
||||
const LightTheme = Theme({
|
||||
name: "theme-light",
|
||||
styles: "chrome://devtools/skin/light-theme.css",
|
||||
});
|
||||
|
||||
const DarkTheme = Theme({
|
||||
name: "theme-dark",
|
||||
styles: "chrome://devtools/skin/dark-theme.css",
|
||||
});
|
||||
|
||||
exports.LightTheme = LightTheme;
|
||||
exports.DarkTheme = DarkTheme;
|
||||
17
toolkit/jetpack/dev/theme/hooks.js
Normal file
17
toolkit/jetpack/dev/theme/hooks.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/* 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.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
const { method } = require("method/core");
|
||||
|
||||
const onEnable = method("dev/theme/hooks#onEnable");
|
||||
const onDisable = method("dev/theme/hooks#onDisable");
|
||||
|
||||
exports.onEnable = onEnable;
|
||||
exports.onDisable = onDisable;
|
||||
107
toolkit/jetpack/dev/toolbox.js
Normal file
107
toolkit/jetpack/dev/toolbox.js
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/* 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.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
const { Cu, Cc, Ci } = require("chrome");
|
||||
const { Class } = require("../sdk/core/heritage");
|
||||
const { Disposable, setup } = require("../sdk/core/disposable");
|
||||
const { contract, validate } = require("../sdk/util/contract");
|
||||
const { each, pairs, values } = require("../sdk/util/sequence");
|
||||
const { onEnable, onDisable } = require("../dev/theme/hooks");
|
||||
|
||||
const { gDevTools } = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {});
|
||||
|
||||
// This is temporary workaround to allow loading of the developer tools client - volcan
|
||||
// into a toolbox panel, this hack won't be necessary as soon as devtools patch will be
|
||||
// shipped in nightly, after which it can be removed. Bug 1038517
|
||||
const registerSDKURI = () => {
|
||||
const ioService = Cc['@mozilla.org/network/io-service;1']
|
||||
.getService(Ci.nsIIOService);
|
||||
const resourceHandler = ioService.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
|
||||
const uri = module.uri.replace("dev/toolbox.js", "");
|
||||
resourceHandler.setSubstitution("sdk", ioService.newURI(uri, null, null));
|
||||
};
|
||||
|
||||
registerSDKURI();
|
||||
|
||||
const Tool = Class({
|
||||
extends: Disposable,
|
||||
setup: function(params={}) {
|
||||
const { panels } = validate(this, params);
|
||||
const { themes } = validate(this, params);
|
||||
|
||||
this.panels = panels;
|
||||
this.themes = themes;
|
||||
|
||||
each(([key, Panel]) => {
|
||||
const { url, label, tooltip, icon, invertIconForLightTheme,
|
||||
invertIconForDarkTheme } = validate(Panel.prototype);
|
||||
const { id } = Panel.prototype;
|
||||
|
||||
gDevTools.registerTool({
|
||||
id: id,
|
||||
url: "about:blank",
|
||||
label: label,
|
||||
tooltip: tooltip,
|
||||
icon: icon,
|
||||
invertIconForLightTheme: invertIconForLightTheme,
|
||||
invertIconForDarkTheme: invertIconForDarkTheme,
|
||||
isTargetSupported: target => target.isLocalTab,
|
||||
build: (window, toolbox) => {
|
||||
const panel = new Panel();
|
||||
setup(panel, { window: window,
|
||||
toolbox: toolbox,
|
||||
url: url });
|
||||
|
||||
return panel.ready();
|
||||
}
|
||||
});
|
||||
}, pairs(panels));
|
||||
|
||||
each(([key, theme]) => {
|
||||
validate(theme);
|
||||
setup(theme);
|
||||
|
||||
gDevTools.registerTheme({
|
||||
id: theme.id,
|
||||
label: theme.label,
|
||||
stylesheets: theme.getStyles(),
|
||||
classList: theme.getClassList(),
|
||||
onApply: (window, oldTheme) => {
|
||||
onEnable(theme, { window: window,
|
||||
oldTheme: oldTheme });
|
||||
},
|
||||
onUnapply: (window, newTheme) => {
|
||||
onDisable(theme, { window: window,
|
||||
newTheme: newTheme });
|
||||
}
|
||||
});
|
||||
}, pairs(themes));
|
||||
},
|
||||
dispose: function() {
|
||||
each(Panel => gDevTools.unregisterTool(Panel.prototype.id),
|
||||
values(this.panels));
|
||||
|
||||
each(Theme => gDevTools.unregisterTheme(Theme.prototype.id),
|
||||
values(this.themes));
|
||||
}
|
||||
});
|
||||
|
||||
validate.define(Tool, contract({
|
||||
panels: {
|
||||
is: ["object", "undefined"]
|
||||
},
|
||||
themes: {
|
||||
is: ["object", "undefined"]
|
||||
}
|
||||
}));
|
||||
|
||||
exports.Tool = Tool;
|
||||
40
toolkit/jetpack/dev/utils.js
Normal file
40
toolkit/jetpack/dev/utils.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* 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 { Cu } = require("chrome");
|
||||
const { gDevTools } = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {});
|
||||
const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
|
||||
const { getActiveTab } = require("../sdk/tabs/utils");
|
||||
const { getMostRecentBrowserWindow } = require("../sdk/window/utils");
|
||||
|
||||
const targetFor = target => {
|
||||
target = target || getActiveTab(getMostRecentBrowserWindow());
|
||||
return devtools.TargetFactory.forTab(target);
|
||||
};
|
||||
|
||||
const getId = id => ((id.prototype && id.prototype.id) || id.id || id);
|
||||
|
||||
const getCurrentPanel = toolbox => toolbox.getCurrentPanel();
|
||||
exports.getCurrentPanel = getCurrentPanel;
|
||||
|
||||
const openToolbox = (id, tab) => {
|
||||
id = getId(id);
|
||||
return gDevTools.showToolbox(targetFor(tab), id);
|
||||
};
|
||||
exports.openToolbox = openToolbox;
|
||||
|
||||
const closeToolbox = tab => gDevTools.closeToolbox(targetFor(tab));
|
||||
exports.closeToolbox = closeToolbox;
|
||||
|
||||
const getToolbox = tab => gDevTools.getToolbox(targetFor(tab));
|
||||
exports.getToolbox = getToolbox;
|
||||
|
||||
const openToolboxPanel = (id, tab) => {
|
||||
id = getId(id);
|
||||
return gDevTools.showToolbox(targetFor(tab), id).then(getCurrentPanel);
|
||||
};
|
||||
exports.openToolboxPanel = openToolboxPanel;
|
||||
3848
toolkit/jetpack/dev/volcan.js
Normal file
3848
toolkit/jetpack/dev/volcan.js
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue