123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- const Services = require("Services");
- const {Ci} = require("chrome");
- const {LocalizationHelper} = require("devtools/shared/l10n");
- const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
- const DevToolsUtils = require("devtools/shared/DevToolsUtils");
- const {Task} = require("devtools/shared/task");
- loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
- loader.lazyRequireGetter(this, "Hosts", "devtools/client/framework/toolbox-hosts", true);
- /**
- * Implement a wrapper on the chrome side to setup a Toolbox within Firefox UI.
- *
- * This component handles iframe creation within Firefox, in which we are loading
- * the toolbox document. Then both the chrome and the toolbox document communicate
- * via "message" events.
- *
- * Messages sent by the toolbox to the chrome:
- * - switch-host:
- * Order to display the toolbox in another host (side, bottom, window, or the
- * previously used one)
- * - toggle-minimize-mode:
- * When using the bottom host, the toolbox can be miximized to only display
- * the tool titles
- * - maximize-host:
- * When using the bottom host in minimized mode, revert back to regular mode
- * in order to see tool titles and the tools
- * - raise-host:
- * Focus the tools
- * - set-host-title:
- * When using the window host, update the window title
- *
- * Messages sent by the chrome to the toolbox:
- * - host-minimized:
- * The bottom host is done minimizing (after animation end)
- * - host-maximized:
- * The bottom host is done switching back to regular mode (after animation
- * end)
- * - switched-host:
- * The `switch-host` command sent by the toolbox is done
- */
- const LAST_HOST = "devtools.toolbox.host";
- const PREVIOUS_HOST = "devtools.toolbox.previousHost";
- let ID_COUNTER = 1;
- function ToolboxHostManager(target, hostType, hostOptions) {
- this.target = target;
- this.frameId = ID_COUNTER++;
- if (!hostType) {
- hostType = Services.prefs.getCharPref(LAST_HOST);
- }
- this.onHostMinimized = this.onHostMinimized.bind(this);
- this.onHostMaximized = this.onHostMaximized.bind(this);
- this.host = this.createHost(hostType, hostOptions);
- this.hostType = hostType;
- }
- ToolboxHostManager.prototype = {
- create: Task.async(function* (toolId) {
- yield this.host.create();
- this.host.frame.setAttribute("aria-label", L10N.getStr("toolbox.label"));
- this.host.frame.ownerDocument.defaultView.addEventListener("message", this);
- // We have to listen on capture as no event fires on bubble
- this.host.frame.addEventListener("unload", this, true);
- let toolbox = new Toolbox(this.target, toolId, this.host.type, this.host.frame.contentWindow, this.frameId);
- // Prevent reloading the toolbox when loading the tools in a tab (e.g. from about:debugging)
- if (!this.host.frame.contentWindow.location.href.startsWith("about:devtools-toolbox")) {
- this.host.frame.setAttribute("src", "about:devtools-toolbox");
- }
- return toolbox;
- }),
- handleEvent(event) {
- switch(event.type) {
- case "message":
- this.onMessage(event);
- break;
- case "unload":
- // On unload, host iframe already lost its contentWindow attribute, so
- // we can only compare against locations. Here we filter two very
- // different cases: preliminary about:blank document as well as iframes
- // like tool iframes.
- if (!event.target.location.href.startsWith("about:devtools-toolbox")) {
- break;
- }
- // Don't destroy the host during unload event (esp., don't remove the
- // iframe from DOM!). Otherwise the unload event for the toolbox
- // document doesn't fire within the toolbox *document*! This is
- // the unload event that fires on the toolbox *iframe*.
- DevToolsUtils.executeSoon(() => {
- this.destroy();
- });
- break;
- }
- },
- onMessage(event) {
- if (!event.data) {
- return;
- }
- // Toolbox document is still chrome and disallow identifying message
- // origin via event.source as it is null. So use a custom id.
- if (event.data.frameId != this.frameId) {
- return;
- }
- switch (event.data.name) {
- case "switch-host":
- this.switchHost(event.data.hostType);
- break;
- case "maximize-host":
- this.host.maximize();
- break;
- case "raise-host":
- this.host.raise();
- break;
- case "toggle-minimize-mode":
- this.host.toggleMinimizeMode(event.data.toolbarHeight);
- break;
- case "set-host-title":
- this.host.setTitle(event.data.title);
- break;
- }
- },
- postMessage(data) {
- let window = this.host.frame.contentWindow;
- window.postMessage(data, "*");
- },
- destroy() {
- this.destroyHost();
- this.host = null;
- this.hostType = null;
- this.target = null;
- },
- /**
- * Create a host object based on the given host type.
- *
- * Warning: bottom and sidebar hosts require that the toolbox target provides
- * a reference to the attached tab. Not all Targets have a tab property -
- * make sure you correctly mix and match hosts and targets.
- *
- * @param {string} hostType
- * The host type of the new host object
- *
- * @return {Host} host
- * The created host object
- */
- createHost(hostType, options) {
- if (!Hosts[hostType]) {
- throw new Error("Unknown hostType: " + hostType);
- }
- let newHost = new Hosts[hostType](this.target.tab, options);
- // Update the label and icon when the state changes.
- newHost.on("minimized", this.onHostMinimized);
- newHost.on("maximized", this.onHostMaximized);
- return newHost;
- },
- onHostMinimized() {
- this.postMessage({
- name: "host-minimized"
- });
- },
- onHostMaximized() {
- this.postMessage({
- name: "host-maximized"
- });
- },
- switchHost: Task.async(function* (hostType) {
- if (hostType == "previous") {
- // Switch to the last used host for the toolbox UI.
- // This is determined by the devtools.toolbox.previousHost pref.
- hostType = Services.prefs.getCharPref(PREVIOUS_HOST);
- // Handle the case where the previous host happens to match the current
- // host. If so, switch to bottom if it's not already used, and side if not.
- if (hostType === this.hostType) {
- if (hostType === Toolbox.HostType.BOTTOM) {
- hostType = Toolbox.HostType.SIDE;
- } else {
- hostType = Toolbox.HostType.BOTTOM;
- }
- }
- }
- let iframe = this.host.frame;
- let newHost = this.createHost(hostType);
- let newIframe = yield newHost.create();
- // change toolbox document's parent to the new host
- newIframe.swapFrameLoaders(iframe);
- this.destroyHost();
- if (this.hostType != Toolbox.HostType.CUSTOM) {
- Services.prefs.setCharPref(PREVIOUS_HOST, this.hostType);
- }
- this.host = newHost;
- this.hostType = hostType;
- this.host.setTitle(this.host.frame.contentWindow.document.title);
- this.host.frame.ownerDocument.defaultView.addEventListener("message", this);
- this.host.frame.addEventListener("unload", this, true);
- if (hostType != Toolbox.HostType.CUSTOM) {
- Services.prefs.setCharPref(LAST_HOST, hostType);
- }
- // Tell the toolbox the host changed
- this.postMessage({
- name: "switched-host",
- hostType
- });
- }),
- /**
- * Destroy the current host, and remove event listeners from its frame.
- *
- * @return {promise} to be resolved when the host is destroyed.
- */
- destroyHost() {
- // When Firefox toplevel is closed, the frame may already be detached and
- // the top level document gone
- if (this.host.frame.ownerDocument.defaultView) {
- this.host.frame.ownerDocument.defaultView.removeEventListener("message", this);
- }
- this.host.frame.removeEventListener("unload", this, true);
- this.host.off("minimized", this.onHostMinimized);
- this.host.off("maximized", this.onHostMaximized);
- return this.host.destroy();
- }
- };
- exports.ToolboxHostManager = ToolboxHostManager;
|