From 6809e5a1350c8b3120efecc268914f38aa25e06a Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Fri, 7 Oct 2016 17:43:31 +0200 Subject: [PATCH] Backed out changeset 03250652b3a0 (bug 1266134) for failing browser_dbg_on-pause-raise.js on OSX and Windows. r=backout --- .../browser_dbg_breakpoints-other-tabs.js | 1 + .../test/mochitest/browser_dbg_host-layout.js | 4 +- .../mochitest/browser_dbg_worker-window.js | 13 +- .../client/debugger/test/mochitest/head.js | 3 - devtools/client/framework/devtools.js | 44 +-- devtools/client/framework/moz.build | 1 - .../framework/test/browser_devtools_api.js | 4 +- .../test/browser_toolbox_custom_host.js | 3 - .../framework/test/browser_toolbox_raise.js | 8 +- .../framework/test/browser_toolbox_toggle.js | 2 +- .../browser_toolbox_window_title_changes.js | 29 +- ...owser_toolbox_window_title_frame_select.js | 9 - devtools/client/framework/test/shared-head.js | 14 - .../client/framework/toolbox-host-manager.js | 258 ------------------ devtools/client/framework/toolbox.js | 216 ++++++++------- ...rowser_inspector_breadcrumbs_visibility.js | 2 +- .../test/browser_inspector_portrait_mode.js | 4 +- .../test/browser_styleeditor_sv_resize.js | 2 +- .../browser_webconsole_closure_inspection.js | 2 +- 19 files changed, 166 insertions(+), 453 deletions(-) delete mode 100644 devtools/client/framework/toolbox-host-manager.js diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-other-tabs.js b/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-other-tabs.js index 2763eee95dffb..11cde294f2f59 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-other-tabs.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-other-tabs.js @@ -37,5 +37,6 @@ var test = Task.async(function* () { is(packet.frame.where.line, 3, "Should have stopped at line 3 (debugger statement), not line 2 (other tab's breakpoint)"); + yield teardown(panel1); yield resumeDebuggerThenCloseAndFinish(panel2); }); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_host-layout.js b/devtools/client/debugger/test/mochitest/browser_dbg_host-layout.js index dc86d86493702..eae6a1f7acec2 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg_host-layout.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_host-layout.js @@ -84,7 +84,7 @@ function getHost(host) { function resizeToolboxWindow(panel, host) { let sizeOption = host.split(":")[1]; - let win = panel._toolbox.win.parent; + let win = panel._toolbox._host._window; // should be the same value as BREAKPOINT_SMALL_WINDOW_WIDTH in debugger-view.js let breakpoint = 850; @@ -101,7 +101,7 @@ function resizeToolboxWindow(panel, host) { function resizeAndWaitForLayoutChange(panel, width) { info("Updating toolbox window width to " + width); - let win = panel._toolbox.win.parent; + let win = panel._toolbox._host._window; let gDebugger = panel.panelWin; win.resizeTo(width, window.screen.availHeight); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js b/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js index 46198d31cd7f1..7cc7cfb5af7e5 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js @@ -34,17 +34,8 @@ add_task(function* () { "jsdebugger", Toolbox.HostType.WINDOW); - is(toolbox.hostType, "window", "correct host"); - - yield new Promise(done => { - toolbox.win.parent.addEventListener("message", function onmessage(event) { - if (event.data.name == "set-host-title") { - toolbox.win.parent.removeEventListener("message", onmessage); - done(); - } - }); - }); - ok(toolbox.win.parent.document.title.includes(WORKER_URL), + is(toolbox._host.type, "window", "correct host"); + ok(toolbox._host._window.document.title.includes(WORKER_URL), "worker URL in host title"); let toolTabs = toolbox.doc.querySelectorAll(".devtools-tab"); diff --git a/devtools/client/debugger/test/mochitest/head.js b/devtools/client/debugger/test/mochitest/head.js index 1f9d38b82d159..9ca0e0c8698c7 100644 --- a/devtools/client/debugger/test/mochitest/head.js +++ b/devtools/client/debugger/test/mochitest/head.js @@ -751,9 +751,6 @@ AddonDebugger.prototype = { }), _onMessage: function (event) { - if (typeof(event.data) !== "string") { - return; - } let json = JSON.parse(event.data); switch (json.name) { case "toolbox-title": diff --git a/devtools/client/framework/devtools.js b/devtools/client/framework/devtools.js index 58002e01a8a20..9753ddaef429d 100644 --- a/devtools/client/framework/devtools.js +++ b/devtools/client/framework/devtools.js @@ -10,7 +10,6 @@ const defer = require("devtools/shared/defer"); // Load gDevToolsBrowser toolbox lazily as they need gDevTools to be fully initialized 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); const {defaultTools: DefaultTools, defaultThemes: DefaultThemes} = @@ -19,7 +18,6 @@ const EventEmitter = require("devtools/shared/event-emitter"); const {JsonView} = require("devtools/client/jsonview/main"); const AboutDevTools = require("devtools/client/framework/about-devtools-toolbox"); const {when: unload} = require("sdk/system/unload"); -const {Task} = require("devtools/shared/task"); const FORBIDDEN_IDS = new Set(["toolbox", ""]); const MAX_ORDINAL = 99; @@ -399,27 +397,35 @@ DevTools.prototype = { * @return {Toolbox} toolbox * The toolbox that was opened */ - showToolbox: Task.async(function* (target, toolId, hostType, hostOptions) { + showToolbox: function (target, toolId, hostType, hostOptions) { + let deferred = defer(); + let toolbox = this._toolboxes.get(target); if (toolbox) { - if (hostType != null && toolbox.hostType != hostType) { - yield toolbox.switchHost(hostType); - } + let hostPromise = (hostType != null && toolbox.hostType != hostType) ? + toolbox.switchHost(hostType) : + promise.resolve(null); if (toolId != null && toolbox.currentToolId != toolId) { - yield toolbox.selectTool(toolId); + hostPromise = hostPromise.then(function () { + return toolbox.selectTool(toolId); + }); } - toolbox.raise(); - } else { - let manager = new ToolboxHostManager(target, hostType, hostOptions); - - toolbox = yield manager.create(toolId); - this._toolboxes.set(target, toolbox); + return hostPromise.then(function () { + toolbox.raise(); + return toolbox; + }); + } + else { + // No toolbox for target, create one + toolbox = new Toolbox(target, toolId, hostType, hostOptions); this.emit("toolbox-created", toolbox); + this._toolboxes.set(target, toolbox); + toolbox.once("destroy", () => { this.emit("toolbox-destroy", target); }); @@ -429,12 +435,16 @@ DevTools.prototype = { this.emit("toolbox-destroyed", target); }); - yield toolbox.open(); - this.emit("toolbox-ready", toolbox); + // If toolId was passed in, it will already be selected before the + // open promise resolves. + toolbox.open().then(() => { + deferred.resolve(toolbox); + this.emit("toolbox-ready", toolbox); + }); } - return toolbox; - }), + return deferred.promise; + }, /** * Return the toolbox for a given target. diff --git a/devtools/client/framework/moz.build b/devtools/client/framework/moz.build index 7b28b4b9e7491..13c7d27de3179 100644 --- a/devtools/client/framework/moz.build +++ b/devtools/client/framework/moz.build @@ -25,7 +25,6 @@ DevToolsModules( 'target-from-url.js', 'target.js', 'toolbox-highlighter-utils.js', - 'toolbox-host-manager.js', 'toolbox-hosts.js', 'toolbox-options.js', 'toolbox.js', diff --git a/devtools/client/framework/test/browser_devtools_api.js b/devtools/client/framework/test/browser_devtools_api.js index dc5a202e58cc9..f389df690e738 100644 --- a/devtools/client/framework/test/browser_devtools_api.js +++ b/devtools/client/framework/test/browser_devtools_api.js @@ -71,7 +71,7 @@ function runTests1(aTab) { gDevTools.showToolbox(target, toolId1).then(function (toolbox) { is(toolbox.target, target, "toolbox target is correct"); - is(toolbox.target.tab, gBrowser.selectedTab, "targeted tab is correct"); + is(toolbox._host.hostTab, gBrowser.selectedTab, "toolbox host is correct"); ok(events["init"], "init event fired"); ok(events["ready"], "ready event fired"); @@ -139,7 +139,7 @@ function runTests2() { gDevTools.showToolbox(target, toolId2).then(function (toolbox) { is(toolbox.target, target, "toolbox target is correct"); - is(toolbox.target.tab, gBrowser.selectedTab, "targeted tab is correct"); + is(toolbox._host.hostTab, gBrowser.selectedTab, "toolbox host is correct"); ok(events["init"], "init event fired"); ok(events["build"], "build event fired"); diff --git a/devtools/client/framework/test/browser_toolbox_custom_host.js b/devtools/client/framework/test/browser_toolbox_custom_host.js index 5d3aeed54ce07..8d5f2215d2b15 100644 --- a/devtools/client/framework/test/browser_toolbox_custom_host.js +++ b/devtools/client/framework/test/browser_toolbox_custom_host.js @@ -24,9 +24,6 @@ function test() { }); function onMessage(event) { - if (typeof(event.data) !== "string") { - return; - } info("onMessage: " + event.data); let json = JSON.parse(event.data); if (json.name == "toolbox-close") { diff --git a/devtools/client/framework/test/browser_toolbox_raise.js b/devtools/client/framework/test/browser_toolbox_raise.js index 0af1a457192c2..c1f26659fec03 100644 --- a/devtools/client/framework/test/browser_toolbox_raise.js +++ b/devtools/client/framework/test/browser_toolbox_raise.js @@ -42,12 +42,12 @@ function testWindowHost() { // Need to wait for focus as otherwise window.focus() is overridden by // toolbox window getting focused first on Linux and Mac. let onToolboxFocus = () => { - toolbox.win.parent.removeEventListener("focus", onToolboxFocus, true); + toolbox._host._window.removeEventListener("focus", onToolboxFocus, true); info("focusing main window."); window.focus(); }; // Need to wait for toolbox window to get focus. - toolbox.win.parent.addEventListener("focus", onToolboxFocus, true); + toolbox._host._window.addEventListener("focus", onToolboxFocus, true); } function onFocus() { @@ -56,11 +56,11 @@ function onFocus() { // Check if toolbox window got focus. let onToolboxFocusAgain = () => { - toolbox.win.parent.removeEventListener("focus", onToolboxFocusAgain, false); + toolbox._host._window.removeEventListener("focus", onToolboxFocusAgain, false); ok(true, "Toolbox window is the focused window after calling toolbox.raise()"); cleanup(); }; - toolbox.win.parent.addEventListener("focus", onToolboxFocusAgain, false); + toolbox._host._window.addEventListener("focus", onToolboxFocusAgain, false); // Now raise toolbox. toolbox.raise(); diff --git a/devtools/client/framework/test/browser_toolbox_toggle.js b/devtools/client/framework/test/browser_toolbox_toggle.js index d5b6d0e961d1d..c66aa002a3c76 100644 --- a/devtools/client/framework/test/browser_toolbox_toggle.js +++ b/devtools/client/framework/test/browser_toolbox_toggle.js @@ -82,7 +82,7 @@ function* testToggleDetachedToolbox(tab, key, modifiers) { info("Verify windowed toolbox is focused instead of closed when using " + "toggle key from the main window"); - let toolboxWindow = toolbox.win.top; + let toolboxWindow = toolbox._host._window; let onToolboxWindowFocus = once(toolboxWindow, "focus", true); EventUtils.synthesizeKey(key, modifiers); yield onToolboxWindowFocus; diff --git a/devtools/client/framework/test/browser_toolbox_window_title_changes.js b/devtools/client/framework/test/browser_toolbox_window_title_changes.js index 558c2094fa1ea..0ea14209d90bd 100644 --- a/devtools/client/framework/test/browser_toolbox_window_title_changes.js +++ b/devtools/client/framework/test/browser_toolbox_window_title_changes.js @@ -28,35 +28,28 @@ function test() { .then(() => toolbox.selectTool(TOOL_ID_1)) // undock toolbox and check title - .then(() => { - // We have to first switch the host in order to spawn the new top level window - // on which we are going to listen from title change event - return toolbox.switchHost(Toolbox.HostType.WINDOW) - .then(() => waitForTitleChange(toolbox)); - }) + .then(() => toolbox.switchHost(Toolbox.HostType.WINDOW)) .then(checkTitle.bind(null, NAME_1, URL_1, "toolbox undocked")) // switch to different tool and check title - .then(() => { - let onTitleChanged = waitForTitleChange(toolbox); - toolbox.selectTool(TOOL_ID_2); - return onTitleChanged; - }) + .then(() => toolbox.selectTool(TOOL_ID_2)) .then(checkTitle.bind(null, NAME_1, URL_1, "tool changed")) // navigate to different local url and check title .then(function () { - let onTitleChanged = waitForTitleChange(toolbox); + let deferred = defer(); + target.once("navigate", () => deferred.resolve()); gBrowser.loadURI(URL_2); - return onTitleChanged; + return deferred.promise; }) .then(checkTitle.bind(null, NAME_2, URL_2, "url changed")) // navigate to a real url and check title .then(() => { - let onTitleChanged = waitForTitleChange(toolbox); + let deferred = defer(); + target.once("navigate", () => deferred.resolve()); gBrowser.loadURI(URL_3); - return onTitleChanged; + return deferred.promise; }) .then(checkTitle.bind(null, NAME_3, URL_3, "url changed")) @@ -73,11 +66,7 @@ function test() { return gDevTools.showToolbox(target, null, Toolbox.HostType.WINDOW); }) .then(function (aToolbox) { toolbox = aToolbox; }) - .then(() => { - let onTitleChanged = waitForTitleChange(toolbox); - toolbox.selectTool(TOOL_ID_1); - return onTitleChanged; - }) + .then(() => toolbox.selectTool(TOOL_ID_1)) .then(checkTitle.bind(null, NAME_3, URL_3, "toolbox destroyed and recreated")) diff --git a/devtools/client/framework/test/browser_toolbox_window_title_frame_select.js b/devtools/client/framework/test/browser_toolbox_window_title_frame_select.js index 1e3d6664684ba..a674e0b44f4bb 100644 --- a/devtools/client/framework/test/browser_toolbox_window_title_frame_select.js +++ b/devtools/client/framework/test/browser_toolbox_window_title_frame_select.js @@ -25,14 +25,8 @@ add_task(function* () { let toolbox = yield gDevTools.showToolbox(target, null, Toolbox.HostType.BOTTOM); - let onTitleChanged = waitForTitleChange(toolbox); yield toolbox.selectTool("inspector"); - yield onTitleChanged; - yield toolbox.switchHost(Toolbox.HostType.WINDOW); - // Wait for title change event *after* switch host, in order to listen - // for the event on the WINDOW host window, which only exists after switchHost - yield waitForTitleChange(toolbox); is(getTitle(), `Developer Tools - Page title - ${URL}`, "Devtools title correct after switching to detached window host"); @@ -62,8 +56,6 @@ add_task(function* () { // Listen to will-navigate to check if the view is empty let willNavigate = toolbox.target.once("will-navigate"); - onTitleChanged = waitForTitleChange(toolbox); - // Only select the iframe after we are able to select an element from the top // level document. let newRoot = toolbox.getPanel("inspector").once("new-root"); @@ -72,7 +64,6 @@ add_task(function* () { yield willNavigate; yield newRoot; - yield onTitleChanged; info("Navigation to the iframe is done, the inspector should be back up"); is(getTitle(), `Developer Tools - Page title - ${URL}`, diff --git a/devtools/client/framework/test/shared-head.js b/devtools/client/framework/test/shared-head.js index a89c6d7525eca..eb69b40068dc7 100644 --- a/devtools/client/framework/test/shared-head.js +++ b/devtools/client/framework/test/shared-head.js @@ -580,17 +580,3 @@ function emptyClipboard() { function isWindows() { return Services.appinfo.OS === "WINNT"; } - -/** - * Wait for a given toolbox to get its title updated. - */ -function waitForTitleChange(toolbox) { - let deferred = defer(); - toolbox.win.parent.addEventListener("message", function onmessage(event) { - if (event.data.name == "set-host-title") { - toolbox.win.parent.removeEventListener("message", onmessage); - deferred.resolve(); - } - }); - return deferred.promise; -} diff --git a/devtools/client/framework/toolbox-host-manager.js b/devtools/client/framework/toolbox-host-manager.js deleted file mode 100644 index 7187f090927d3..0000000000000 --- a/devtools/client/framework/toolbox-host-manager.js +++ /dev/null @@ -1,258 +0,0 @@ -const Services = require("Services"); -const {Ci} = require("chrome"); -const {LocalizationHelper} = require("devtools/shared/l10n"); -const L10N = new LocalizationHelper("devtools/locale/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 or window) - * - switch-to-previous-host: - * Order to display the toolbox in the previously used host - * - 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 "switch-to-previous-host": - this.switchToPreviousHost(); - 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" - }); - }, - - /** - * Switch to the last used host for the toolbox UI. - * This is determined by the devtools.toolbox.previousHost pref. - */ - switchToPreviousHost() { - let 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; - } - } - - return this.switchHost(hostType); - }, - - switchHost: Task.async(function* (hostType) { - 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); - - // See bug 1022726, most probably because of swapFrameLoaders we need to - // first focus the window here, and then once again further from - // toolbox.js to make sure focus actually happens. - iframe.contentWindow.focus(); - - 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; diff --git a/devtools/client/framework/toolbox.js b/devtools/client/framework/toolbox.js index 5ce9017052aae..8d45cca6da99a 100644 --- a/devtools/client/framework/toolbox.js +++ b/devtools/client/framework/toolbox.js @@ -40,6 +40,8 @@ loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true); loader.lazyRequireGetter(this, "getHighlighterUtils", "devtools/client/framework/toolbox-highlighter-utils", true); +loader.lazyRequireGetter(this, "Hosts", + "devtools/client/framework/toolbox-hosts", true); loader.lazyRequireGetter(this, "Selection", "devtools/client/framework/selection", true); loader.lazyRequireGetter(this, "InspectorFront", @@ -101,17 +103,12 @@ const ToolboxButtons = exports.ToolboxButtons = [ * Tool to select initially * @param {Toolbox.HostType} hostType * Type of host that will host the toolbox (e.g. sidebar, window) - * @param {DOMWindow} contentWindow - * The window object of the toolbox document - * @param {string} frameId - * A unique identifier to differentiate toolbox documents from the - * chrome codebase when passing DOM messages + * @param {object} hostOptions + * Options for host specifically */ -function Toolbox(target, selectedTool, hostType, contentWindow, frameId) { +function Toolbox(target, selectedTool, hostType, hostOptions) { this._target = target; - this._win = contentWindow; - this.frameId = frameId; - + this._win = null; this._toolPanels = new Map(); this._telemetry = new Telemetry(); if (Services.prefs.getBoolPref("devtools.sourcemap.locations.enabled")) { @@ -139,7 +136,6 @@ function Toolbox(target, selectedTool, hostType, contentWindow, frameId) { this._prefChanged = this._prefChanged.bind(this); this._saveSplitConsoleHeight = this._saveSplitConsoleHeight.bind(this); this._onFocus = this._onFocus.bind(this); - this._onBrowserMessage = this._onBrowserMessage.bind(this); this._showDevEditionPromo = this._showDevEditionPromo.bind(this); this._updateTextBoxMenuItems = this._updateTextBoxMenuItems.bind(this); this._onBottomHostMinimized = this._onBottomHostMinimized.bind(this); @@ -153,12 +149,16 @@ function Toolbox(target, selectedTool, hostType, contentWindow, frameId) { this._target.on("close", this.destroy); + if (!hostType) { + hostType = Services.prefs.getCharPref(this._prefs.LAST_HOST); + } if (!selectedTool) { selectedTool = Services.prefs.getCharPref(this._prefs.LAST_TOOL); } this._defaultToolId = selectedTool; - this._hostType = hostType; + this._hostOptions = hostOptions; + this._host = this._createHost(hostType, hostOptions); EventEmitter.decorate(this); @@ -190,8 +190,10 @@ Toolbox.prototype = { _URL: "about:devtools-toolbox", _prefs: { + LAST_HOST: "devtools.toolbox.host", LAST_TOOL: "devtools.toolbox.selectedTool", SIDE_ENABLED: "devtools.toolbox.sideEnabled", + PREVIOUS_HOST: "devtools.toolbox.previousHost" }, currentToolId: null, @@ -268,7 +270,7 @@ Toolbox.prototype = { * tab. See HostType for more details. */ get hostType() { - return this._hostType; + return this._host.type; }, /** @@ -351,18 +353,27 @@ Toolbox.prototype = { */ open: function () { return Task.spawn(function* () { + let iframe = yield this._host.create(); + this._win = iframe.contentWindow; + + let domReady = defer(); + + // Prevent reloading the document when the toolbox is opened in a tab + let location = iframe.contentWindow.location.href; + if (!location.startsWith(this._URL)) { + iframe.setAttribute("src", this._URL); + } else { + // Update the URL so that onceDOMReady watch for the right url. + this._URL = location; + } + this.browserRequire = BrowserLoader({ window: this.doc.defaultView, useOnlyShared: true }).require; - if (this.win.location.href.startsWith(this._URL)) { - // Update the URL so that onceDOMReady watch for the right url. - this._URL = this.win.location.href; - } - - let domReady = defer(); - let domHelper = new DOMHelpers(this.win); + iframe.setAttribute("aria-label", L10N.getStr("toolbox.label")); + let domHelper = new DOMHelpers(iframe.contentWindow); domHelper.onceDOMReady(() => { domReady.resolve(); }, this._URL); @@ -615,7 +626,6 @@ Toolbox.prototype = { this.doc.addEventListener("keypress", this._splitConsoleOnKeypress, false); this.doc.addEventListener("focus", this._onFocus, true); this.win.addEventListener("unload", this.destroy); - this.win.addEventListener("message", this._onBrowserMessage, true); }, _removeHostListeners: function () { @@ -624,29 +634,6 @@ Toolbox.prototype = { this.doc.removeEventListener("keypress", this._splitConsoleOnKeypress, false); this.doc.removeEventListener("focus", this._onFocus, true); this.win.removeEventListener("unload", this.destroy); - this.win.removeEventListener("message", this._onBrowserMessage, true); - } - }, - - // Called whenever the chrome send a message - _onBrowserMessage: function (event) { - if (!event.data) { - return; - } - switch (event.data.name) { - case "switched-host": - this._onSwitchedHost(event.data); - break; - case "host-minimized": - if (this.hostType == Toolbox.HostType.BOTTOM) { - this._onBottomHostMinimized(); - } - break; - case "host-maximized": - if (this.hostType == Toolbox.HostType.BOTTOM) { - this._onBottomHostMaximized(); - } - break; } }, @@ -810,6 +797,9 @@ Toolbox.prototype = { // Show the button in its maximized state. this._onBottomHostMaximized(); + // Update the label and icon when the state changes. + this._host.on("minimized", this._onBottomHostMinimized); + this._host.on("maximized", this._onBottomHostMaximized); // Maximize again when a tool gets selected. this.on("before-select", this._onToolSelectWhileMinimized); // Maximize and stop listening before the host type changes. @@ -868,27 +858,14 @@ Toolbox.prototype = { }, _onToolSelectWhileMinimized: function () { - this.postMessage({ - name: "maximize-host" - }); - }, - - postMessage: function (msg) { - // We sometime try to send messages in middle of destroy(), where the - // toolbox iframe may already be detached and no longer have a parent. - if (this.win.parent) { - // Toolbox document is still chrome and disallow identifying message - // origin via event.source as it is null. So use a custom id. - msg.frameId = this.frameId; - this.win.parent.postMessage(msg, "*"); - } + this._host.maximize(); }, _onBottomHostWillChange: function () { - this.postMessage({ - name: "maximize-host" - }); + this._host.maximize(); + this._host.off("minimized", this._onBottomHostMinimized); + this._host.off("maximized", this._onBottomHostMaximized); this.off("before-select", this._onToolSelectWhileMinimized); }, @@ -901,10 +878,7 @@ Toolbox.prototype = { // tabbar is still visible. let toolbarHeight = this.tabbar.getBoxQuads({box: "content"})[0].bounds .height; - this.postMessage({ - name: "toggle-minimize-mode", - toolbarHeight - }); + this._host.toggleMinimizeMode(toolbarHeight); }, /** @@ -1637,9 +1611,7 @@ Toolbox.prototype = { * Raise the toolbox host. */ raise: function () { - this.postMessage({ - name: "raise-host" - }); + this._host.raise(); }, /** @@ -1653,10 +1625,7 @@ Toolbox.prototype = { } else { title = L10N.getFormatStr("toolbox.titleTemplate1", this.target.url); } - this.postMessage({ - name: "set-host-title", - title - }); + this._host.setTitle(title); }, // Returns an instance of the preference actor @@ -1834,14 +1803,48 @@ Toolbox.prototype = { } }, + /** + * Create a host object based on the given host type. + * + * Warning: some 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: function (hostType, options) { + if (!Hosts[hostType]) { + throw new Error("Unknown hostType: " + hostType); + } + + // clean up the toolbox if its window is closed + let newHost = new Hosts[hostType](this.target.tab, options); + newHost.on("window-closed", this.destroy); + return newHost; + }, + /** * Switch to the last used host for the toolbox UI. + * This is determined by the devtools.toolbox.previousHost pref. */ switchToPreviousHost: function () { - this.postMessage({ - name: "switch-to-previous-host" - }); - return this.once("host-changed"); + let hostType = Services.prefs.getCharPref(this._prefs.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; + } + } + + return this.switchHost(hostType); }, /** @@ -1864,27 +1867,33 @@ Toolbox.prototype = { // swapFrameLoaders() works around this issue. this.focusTool(this.currentToolId, false); - // Host code on the chrome side will send back a message once the host - // switched - this.postMessage({ - name: "switch-host", - hostType - }); + let newHost = this._createHost(hostType); + return newHost.create().then(iframe => { + // change toolbox document's parent to the new host + iframe.QueryInterface(Ci.nsIFrameLoaderOwner); + iframe.swapFrameLoaders(this._host.frame); - return this.once("host-changed"); - }, + this._host.off("window-closed", this.destroy); + this.destroyHost(); - _onSwitchedHost: function ({ hostType }) { - this._hostType = hostType; + let prevHostType = this._host.type; + this._host = newHost; - this._buildDockButtons(); - this._addKeysToWindow(); + if (this.hostType != Toolbox.HostType.CUSTOM) { + Services.prefs.setCharPref(this._prefs.LAST_HOST, this._host.type); + Services.prefs.setCharPref(this._prefs.PREVIOUS_HOST, prevHostType); + } - // Focus the tool to make sure keyboard shortcuts work straight away. - this.focusTool(this.currentToolId, true); + this._buildDockButtons(); + this._addKeysToWindow(); - this.emit("host-changed"); - this._telemetry.log(HOST_HISTOGRAM, this._getTelemetryHostId()); + // Focus the tool to make sure keyboard shortcuts work straight away. + this.focusTool(this.currentToolId, true); + + this.emit("host-changed"); + + this._telemetry.log(HOST_HISTOGRAM, this._getTelemetryHostId()); + }); }, /** @@ -2056,6 +2065,16 @@ Toolbox.prototype = { return this.notificationBox; }, + /** + * Destroy the current host, and remove event listeners from its frame. + * + * @return {promise} to be resolved when the host is destroyed. + */ + destroyHost: function () { + this._removeHostListeners(); + return this._host.destroy(); + }, + /** * Remove all UI elements, detach from target and clear up */ @@ -2163,19 +2182,11 @@ Toolbox.prototype = { // then destroying the host, successfully or not) before destroying the // target. deferred.resolve(settleAll(outstanding) + .catch(console.error) + .then(() => this.destroyHost()) .catch(console.error) .then(() => { - this._removeHostListeners(); - - // `location` may already be null if the toolbox document is already - // in process of destruction. Otherwise if it is still around, ensure - // releasing toolbox document and triggering cleanup thanks to unload - // event. We do that precisely here, before nullifying the target as - // various cleanup code depends on the target attribute to be still - // defined. - if (win.location) { - win.location.replace("about:blank"); - } + this._win = null; // Targets need to be notified that the toolbox is being torn down. // This is done after other destruction tasks since it may tear down @@ -2195,7 +2206,6 @@ Toolbox.prototype = { // Free _host after the call to destroyed in order to let a chance // to destroyed listeners to still query toolbox attributes this._host = null; - this._win = null; this._toolPanels.clear(); // Force GC to prevent long GC pauses when running tests and to free up diff --git a/devtools/client/inspector/test/browser_inspector_breadcrumbs_visibility.js b/devtools/client/inspector/test/browser_inspector_breadcrumbs_visibility.js index caee745c9b37b..5bfba98d317a8 100644 --- a/devtools/client/inspector/test/browser_inspector_breadcrumbs_visibility.js +++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs_visibility.js @@ -43,7 +43,7 @@ add_task(function* () { inspector.breadcrumbs.arrowScrollBox.scrollBehavior = "instant"; yield toolbox.switchHost(Toolbox.HostType.WINDOW); - let hostWindow = toolbox.win.parent; + let hostWindow = toolbox._host._window; let originalWidth = hostWindow.outerWidth; let originalHeight = hostWindow.outerHeight; hostWindow.resizeTo(640, 300); diff --git a/devtools/client/inspector/test/browser_inspector_portrait_mode.js b/devtools/client/inspector/test/browser_inspector_portrait_mode.js index 3340c1f7d5a48..2caa4990c0767 100644 --- a/devtools/client/inspector/test/browser_inspector_portrait_mode.js +++ b/devtools/client/inspector/test/browser_inspector_portrait_mode.js @@ -11,7 +11,7 @@ add_task(function* () { let { inspector, toolbox } = yield openInspectorForURL( "data:text/html;charset=utf-8,

foo

bar", "window"); - let hostWindow = toolbox.win.parent; + let hostWindow = toolbox._host._window; let originalWidth = hostWindow.outerWidth; let originalHeight = hostWindow.outerHeight; @@ -46,7 +46,7 @@ add_task(function* () { ok(splitter.classList.contains("horz"), "Splitter is in horizontal mode"); info("Restore original window size"); - toolbox.win.parent.resizeTo(originalWidth, originalHeight); + toolbox._host._window.resizeTo(originalWidth, originalHeight); }); /** diff --git a/devtools/client/styleeditor/test/browser_styleeditor_sv_resize.js b/devtools/client/styleeditor/test/browser_styleeditor_sv_resize.js index d684905fc707f..ff94bca1b7137 100644 --- a/devtools/client/styleeditor/test/browser_styleeditor_sv_resize.js +++ b/devtools/client/styleeditor/test/browser_styleeditor_sv_resize.js @@ -21,7 +21,7 @@ add_task(function* () { let editor = yield ui.editors[0].getSourceEditor(); let originalSourceEditor = editor.sourceEditor; - let hostWindow = toolbox.win.parent; + let hostWindow = toolbox._host._window; let originalWidth = hostWindow.outerWidth; let originalHeight = hostWindow.outerHeight; diff --git a/devtools/client/webconsole/test/browser_webconsole_closure_inspection.js b/devtools/client/webconsole/test/browser_webconsole_closure_inspection.js index 6a29d61aab6ca..007b1d3184dd0 100644 --- a/devtools/client/webconsole/test/browser_webconsole_closure_inspection.js +++ b/devtools/client/webconsole/test/browser_webconsole_closure_inspection.js @@ -96,5 +96,5 @@ function onExpandClosure(results) { gVariablesView.window.focus(); gJSTerm.once("sidebar-closed", finishTest); - EventUtils.synthesizeKey("VK_ESCAPE", {}, gVariablesView.window); + EventUtils.synthesizeKey("VK_ESCAPE", {}); }