author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Fri, 30 Sep 2016 12:00:25 +0200 | |
changeset 315806 | 59db3b0781908d9e2145477ff8dc5a85729e25bf |
parent 315796 | 6f31b67533799bd84fe3b207681bcef2c78dac47 (current diff) |
parent 315805 | 1723fbc9d9d88a5fbf1c57cdd1e226e36f04c522 (diff) |
child 315962 | 5ffed033557e5b6f9694123f1948f867f913ede3 |
push id | 30756 |
push user | cbook@mozilla.com |
push date | Fri, 30 Sep 2016 10:00:40 +0000 |
treeherder | mozilla-central@59db3b078190 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 52.0a1 |
first release with | nightly linux32
59db3b078190
/
52.0a1
/
20160930030315
/
files
nightly linux64
59db3b078190
/
52.0a1
/
20160930030315
/
files
nightly mac
59db3b078190
/
52.0a1
/
20160930030315
/
files
nightly win32
59db3b078190
/
52.0a1
/
20160930030315
/
files
nightly win64
59db3b078190
/
52.0a1
/
20160930030315
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
52.0a1
/
20160930030315
/
pushlog to previous
nightly linux64
52.0a1
/
20160930030315
/
pushlog to previous
nightly mac
52.0a1
/
20160930030315
/
pushlog to previous
nightly win32
52.0a1
/
20160930030315
/
pushlog to previous
nightly win64
52.0a1
/
20160930030315
/
pushlog to previous
|
devtools/client/inspector/inspector-panel.js | file | annotate | diff | comparison | revisions |
--- a/devtools/client/animationinspector/animation-controller.js +++ b/devtools/client/animationinspector/animation-controller.js @@ -137,17 +137,17 @@ var AnimationsController = { return; } this.initialized = promise.defer(); this.onPanelVisibilityChange = this.onPanelVisibilityChange.bind(this); this.onNewNodeFront = this.onNewNodeFront.bind(this); this.onAnimationMutations = this.onAnimationMutations.bind(this); - let target = gToolbox.target; + let target = gInspector.target; this.animationsFront = new AnimationsFront(target.client, target.form); // Expose actor capabilities. this.traits = yield getServerTraits(target); if (this.destroyed) { console.warn("Could not fully initialize the AnimationsController"); return;
--- a/devtools/client/definitions.js +++ b/devtools/client/definitions.js @@ -4,17 +4,17 @@ "use strict"; const Services = require("Services"); const osString = Services.appinfo.OS; // Panels loader.lazyGetter(this, "OptionsPanel", () => require("devtools/client/framework/toolbox-options").OptionsPanel); -loader.lazyGetter(this, "InspectorPanel", () => require("devtools/client/inspector/inspector-panel").InspectorPanel); +loader.lazyGetter(this, "InspectorPanel", () => require("devtools/client/inspector/panel").InspectorPanel); loader.lazyGetter(this, "WebConsolePanel", () => require("devtools/client/webconsole/panel").WebConsolePanel); loader.lazyGetter(this, "DebuggerPanel", () => require("devtools/client/debugger/panel").DebuggerPanel); loader.lazyGetter(this, "StyleEditorPanel", () => require("devtools/client/styleeditor/styleeditor-panel").StyleEditorPanel); loader.lazyGetter(this, "ShaderEditorPanel", () => require("devtools/client/shadereditor/panel").ShaderEditorPanel); loader.lazyGetter(this, "CanvasDebuggerPanel", () => require("devtools/client/canvasdebugger/panel").CanvasDebuggerPanel); loader.lazyGetter(this, "WebAudioEditorPanel", () => require("devtools/client/webaudioeditor/panel").WebAudioEditorPanel); loader.lazyGetter(this, "MemoryPanel", () => require("devtools/client/memory/panel").MemoryPanel); loader.lazyGetter(this, "PerformancePanel", () => require("devtools/client/performance/panel").PerformancePanel);
--- a/devtools/client/framework/test/browser_toolbox_textbox_context_menu.js +++ b/devtools/client/framework/test/browser_toolbox_textbox_context_menu.js @@ -2,17 +2,17 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ const URL = "data:text/html;charset=utf8,test for textbox context menu"; add_task(function* () { let toolbox = yield openNewTabAndToolbox(URL, "inspector"); - let textboxContextMenu = toolbox.textboxContextMenuPopup; + let textboxContextMenu = toolbox.textBoxContextMenuPopup; emptyClipboard(); // Make sure the focus is predictable. let inspector = toolbox.getPanel("inspector"); let onFocus = once(inspector.searchBox, "focus"); inspector.searchBox.focus(); yield onFocus;
--- a/devtools/client/framework/toolbox.js +++ b/devtools/client/framework/toolbox.js @@ -132,17 +132,17 @@ function Toolbox(target, selectedTool, h this.destroy = this.destroy.bind(this); this.highlighterUtils = getHighlighterUtils(this); this._highlighterReady = this._highlighterReady.bind(this); this._highlighterHidden = this._highlighterHidden.bind(this); this._prefChanged = this._prefChanged.bind(this); this._saveSplitConsoleHeight = this._saveSplitConsoleHeight.bind(this); this._onFocus = this._onFocus.bind(this); this._showDevEditionPromo = this._showDevEditionPromo.bind(this); - this._updateTextboxMenuItems = this._updateTextboxMenuItems.bind(this); + this._updateTextBoxMenuItems = this._updateTextBoxMenuItems.bind(this); this._onBottomHostMinimized = this._onBottomHostMinimized.bind(this); this._onBottomHostMaximized = this._onBottomHostMaximized.bind(this); this._onToolSelectWhileMinimized = this._onToolSelectWhileMinimized.bind(this); this._onPerformanceFrontEvent = this._onPerformanceFrontEvent.bind(this); this._onBottomHostWillChange = this._onBottomHostWillChange.bind(this); this._toggleMinimizeMode = this._toggleMinimizeMode.bind(this); this._onTabbarFocus = this._onTabbarFocus.bind(this); this._onTabbarArrowKeypress = this._onTabbarArrowKeypress.bind(this); @@ -397,20 +397,20 @@ Toolbox.prototype = { gDevTools.on("pref-changed", this._prefChanged); let framesMenu = this.doc.getElementById("command-button-frames"); framesMenu.addEventListener("click", this.showFramesMenu, false); let noautohideMenu = this.doc.getElementById("command-button-noautohide"); noautohideMenu.addEventListener("click", this._toggleAutohide, true); - this.textboxContextMenuPopup = + this.textBoxContextMenuPopup = this.doc.getElementById("toolbox-textbox-context-popup"); - this.textboxContextMenuPopup.addEventListener("popupshowing", - this._updateTextboxMenuItems, true); + this.textBoxContextMenuPopup.addEventListener("popupshowing", + this._updateTextBoxMenuItems, true); this.shortcuts = new KeyShortcuts({ window: this.doc.defaultView }); this._buildDockButtons(); this._buildOptions(); this._buildTabs(); this._applyCacheSettings(); @@ -2091,20 +2091,20 @@ Toolbox.prototype = { this.webconsolePanel.removeEventListener("resize", this._saveSplitConsoleHeight); this.webconsolePanel = null; } if (this.closeButton) { this.closeButton.removeEventListener("click", this.destroy, true); this.closeButton = null; } - if (this.textboxContextMenuPopup) { - this.textboxContextMenuPopup.removeEventListener("popupshowing", - this._updateTextboxMenuItems, true); - this.textboxContextMenuPopup = null; + if (this.textBoxContextMenuPopup) { + this.textBoxContextMenuPopup.removeEventListener("popupshowing", + this._updateTextBoxMenuItems, true); + this.textBoxContextMenuPopup = null; } if (this.tabbar) { this.tabbar.removeEventListener("focus", this._onTabbarFocus, true); this.tabbar.removeEventListener("click", this._onTabbarFocus, true); this.tabbar.removeEventListener("keypress", this._onTabbarArrowKeypress); this.tabbar = null; } @@ -2230,23 +2230,34 @@ Toolbox.prototype = { return; } showDoorhanger({ window: this.win, type: "deveditionpromo" }); }, /** * Enable / disable necessary textbox menu items using globalOverlay.js. */ - _updateTextboxMenuItems: function () { + _updateTextBoxMenuItems: function () { let window = this.win; ["cmd_undo", "cmd_delete", "cmd_cut", "cmd_copy", "cmd_paste", "cmd_selectAll"].forEach(window.goUpdateCommand); }, /** + * Open the textbox context menu at given coordinates. + * Panels in the toolbox can call this on contextmenu events with event.screenX/Y + * instead of having to implement their own copy/paste/selectAll menu. + * @param {Number} x + * @param {Number} y + */ + openTextBoxContextMenu: function (x, y) { + this.textBoxContextMenuPopup.openPopupAtScreen(x, y, true); + }, + + /** * Connects to the SPS profiler when the developer tools are open. This is * necessary because of the WebConsole's `profile` and `profileEnd` methods. */ initPerformance: Task.async(function* () { // If target does not have profiler actor (addons), do not // even register the shared performance connection. if (!this.target.hasActor("profiler")) { return promise.resolve();
--- a/devtools/client/inspector/components/box-model.js +++ b/devtools/client/inspector/components/box-model.js @@ -336,20 +336,20 @@ BoxModelView.prototype = { } }, /** * Start listening to reflows in the current tab. */ trackReflows: function () { if (!this.reflowFront) { - let toolbox = this.inspector.toolbox; - if (toolbox.target.form.reflowActor) { - this.reflowFront = ReflowFront(toolbox.target.client, - toolbox.target.form); + let { target } = this.inspector; + if (target.form.reflowActor) { + this.reflowFront = ReflowFront(target.client, + target.form); } else { return; } } this.reflowFront.on("reflows", this.update); this.reflowFront.start(); }, @@ -379,17 +379,16 @@ BoxModelView.prototype = { initial: initialValue, contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE, property: { name: dimension.property }, start: self => { self.elt.parentNode.classList.add("boxmodel-editing"); }, - change: value => { if (NUMERIC.test(value)) { value += "px"; } let properties = [ { name: property, value: value } ]; @@ -399,25 +398,25 @@ BoxModelView.prototype = { let style = session.getProperty(bprop); if (!style || style == "none" || style == "hidden") { properties.push({ name: bprop, value: "solid" }); } } session.setProperties(properties).catch(e => console.error(e)); }, - done: (value, commit) => { editor.elt.parentNode.classList.remove("boxmodel-editing"); if (!commit) { session.revert().then(() => { session.destroy(); }, e => console.error(e)); } }, + contextMenu: this.inspector.onTextBoxContextMenu, cssProperties: this._cssProperties }, event); }, /** * Is the BoxModelView visible in the sidebar. * @return {Boolean} */ @@ -463,17 +462,17 @@ BoxModelView.prototype = { if (this.inspector.markup) { this.inspector.markup.off("leave", this.onMarkupViewLeave); this.inspector.markup.off("node-hover", this.onMarkupViewNodeHover); } this.inspector.sidebar.off("computedview-selected", this.onNewNode); this.inspector.selection.off("new-node-front", this.onNewSelection); this.inspector.sidebar.off("select", this.onSidebarSelect); - this.inspector._target.off("will-navigate", this.onWillNavigate); + this.inspector.target.off("will-navigate", this.onWillNavigate); this.inspector.off("computed-view-filtered", this.onFilterComputedView); this.inspector = null; this.doc = null; this.wrapper = null; this.container = null; this.expander = null; this.sizeLabel = null; @@ -787,17 +786,17 @@ BoxModelView.prototype = { // Hide completely the geometry editor if the picker is clicked toolbox.on("picker-started", this.onPickerStarted); // Temporary hide the geometry editor this.inspector.markup.on("leave", this.onMarkupViewLeave); this.inspector.markup.on("node-hover", this.onMarkupViewNodeHover); // Release the actor on will-navigate event - this.inspector._target.once("will-navigate", this.onWillNavigate); + this.inspector.target.once("will-navigate", this.onWillNavigate); }); }, /** * Hide the geometry editor highlighter on the currently selected element * @param {Boolean} [updateButton=true] * Indicates if the Geometry Editor's button needs to be unchecked too */
--- a/devtools/client/inspector/computed/computed.js +++ b/devtools/client/inspector/computed/computed.js @@ -143,18 +143,16 @@ function CssComputedView(inspector, docu // Create bound methods. this.focusWindow = this.focusWindow.bind(this); this._onContextMenu = this._onContextMenu.bind(this); this._onClick = this._onClick.bind(this); this._onCopy = this._onCopy.bind(this); this._onFilterStyles = this._onFilterStyles.bind(this); this._onClearSearch = this._onClearSearch.bind(this); this._onIncludeBrowserStyles = this._onIncludeBrowserStyles.bind(this); - this._onFilterTextboxContextMenu = - this._onFilterTextboxContextMenu.bind(this); let doc = this.styleDocument; this.element = doc.getElementById("propertyContainer"); this.searchField = doc.getElementById("computedview-searchbox"); this.searchClearButton = doc.getElementById("computedview-searchinput-clear"); this.includeBrowserStylesCheckbox = doc.getElementById("browser-style-checkbox"); @@ -162,18 +160,17 @@ function CssComputedView(inspector, docu this._onShortcut = this._onShortcut.bind(this); this.shortcuts.on("CmdOrCtrl+F", this._onShortcut); this.shortcuts.on("Escape", this._onShortcut); this.styleDocument.addEventListener("mousedown", this.focusWindow); this.element.addEventListener("click", this._onClick); this.element.addEventListener("copy", this._onCopy); this.element.addEventListener("contextmenu", this._onContextMenu); this.searchField.addEventListener("input", this._onFilterStyles); - this.searchField.addEventListener("contextmenu", - this._onFilterTextboxContextMenu); + this.searchField.addEventListener("contextmenu", this.inspector.onTextBoxContextMenu); this.searchClearButton.addEventListener("click", this._onClearSearch); this.includeBrowserStylesCheckbox.addEventListener("input", this._onIncludeBrowserStyles); this.searchClearButton.hidden = true; // No results text. this.noResults = this.styleDocument.getElementById("computedview-no-results"); @@ -542,29 +539,16 @@ CssComputedView.prototype = { } this.refreshPanel(); this._filterChangeTimeout = null; }, filterTimeout); }, /** - * Context menu handler for filter style search box. - */ - _onFilterTextboxContextMenu: function (event) { - try { - this.styleDocument.defaultView.focus(); - let contextmenu = this.inspector.toolbox.textboxContextMenuPopup; - contextmenu.openPopupAtScreen(event.screenX, event.screenY, true); - } catch (e) { - console.error(e); - } - }, - - /** * Called when the user clicks on the clear button in the filter style search * box. Returns true if the search box is cleared and false otherwise. */ _onClearSearch: function () { if (this.searchField.value) { this.setFilterStyles(""); return true; } @@ -649,18 +633,17 @@ CssComputedView.prototype = { get matchedProperties() { return this._matchedProperties || new Set(); }, /** * Focus the window on mousedown. */ focusWindow: function () { - let win = this.styleDocument.defaultView; - win.focus(); + this.styleWindow.focus(); }, /** * Context menu handler. */ _onContextMenu: function (event) { this._contextmenu.show(event); }, @@ -687,17 +670,17 @@ CssComputedView.prototype = { event.preventDefault(); }, /** * Copy the current selection to the clipboard */ copySelection: function () { try { - let win = this.styleDocument.defaultView; + let win = this.styleWindow; let text = win.getSelection().toString().trim(); // Tidy up block headings by moving CSS property names and their // values onto the same line and inserting a colon between them. let textArray = text.split(/[\r\n]+/); let result = ""; // Parse text array to output string. @@ -753,17 +736,17 @@ CssComputedView.prototype = { // Remove bound listeners this.styleDocument.removeEventListener("mousedown", this.focusWindow); this.element.removeEventListener("click", this._onClick); this.element.removeEventListener("copy", this._onCopy); this.element.removeEventListener("contextmenu", this._onContextMenu); this.searchField.removeEventListener("input", this._onFilterStyles); this.searchField.removeEventListener("contextmenu", - this._onFilterTextboxContextMenu); + this.inspector.onTextBoxContextMenu); this.searchClearButton.removeEventListener("click", this._onClearSearch); this.includeBrowserStylesCheckbox.removeEventListener("input", this._onIncludeBrowserStyles); // Nodes used in templating this.element = null; this.panel = null; this.searchField = null;
--- a/devtools/client/inspector/computed/test/browser_computed_search-filter_context-menu.js +++ b/devtools/client/inspector/computed/test/browser_computed_search-filter_context-menu.js @@ -12,17 +12,17 @@ const TEST_URI = "<h1>test filter contex add_task(function* () { yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); let {toolbox, inspector, view} = yield openComputedView(); yield selectNode("h1", inspector); let win = view.styleWindow; let searchField = view.searchField; - let searchContextMenu = toolbox.textboxContextMenuPopup; + let searchContextMenu = toolbox.textBoxContextMenuPopup; ok(searchContextMenu, "The search filter context menu is loaded in the computed view"); let cmdUndo = searchContextMenu.querySelector("[command=cmd_undo]"); let cmdDelete = searchContextMenu.querySelector("[command=cmd_delete]"); let cmdSelectAll = searchContextMenu.querySelector("[command=cmd_selectAll]"); let cmdCut = searchContextMenu.querySelector("[command=cmd_cut]"); let cmdCopy = searchContextMenu.querySelector("[command=cmd_copy]");
--- a/devtools/client/inspector/fonts/fonts.js +++ b/devtools/client/inspector/fonts/fonts.js @@ -27,19 +27,20 @@ FontInspector.prototype = { this.onNewNode = this.onNewNode.bind(this); this.onThemeChanged = this.onThemeChanged.bind(this); this.inspector.selection.on("new-node-front", this.onNewNode); this.inspector.sidebar.on("fontinspector-selected", this.onNewNode); this.showAll = this.showAll.bind(this); this.showAllLink = this.chromeDoc.getElementById("font-showall"); this.showAllLink.addEventListener("click", this.showAll); this.previewTextChanged = this.previewTextChanged.bind(this); - this.previewInput = - this.chromeDoc.getElementById("font-preview-text-input"); + this.previewInput = this.chromeDoc.getElementById("font-preview-text-input"); this.previewInput.addEventListener("input", this.previewTextChanged); + this.previewInput.addEventListener("contextmenu", + this.inspector.onTextBoxContextMenu); // Listen for theme changes as the color of the previews depend on the theme gDevTools.on("theme-switched", this.onThemeChanged); this.update(); }, /** @@ -54,16 +55,18 @@ FontInspector.prototype = { * Remove listeners. */ destroy: function () { this.chromeDoc = null; this.inspector.sidebar.off("fontinspector-selected", this.onNewNode); this.inspector.selection.off("new-node-front", this.onNewNode); this.showAllLink.removeEventListener("click", this.showAll); this.previewInput.removeEventListener("input", this.previewTextChanged); + this.previewInput.removeEventListener("contextmenu", + this.inspector.onTextBoxContextMenu); gDevTools.off("theme-switched", this.onThemeChanged); if (this._previewUpdateTimeout) { clearTimeout(this._previewUpdateTimeout); } },
--- a/devtools/client/inspector/inspector-search.js +++ b/devtools/client/inspector/inspector-search.js @@ -37,22 +37,19 @@ function InspectorSearch(inspector, inpu this.searchClearButton = clearBtn; this._lastSearched = null; this.searchClearButton.hidden = true; this._onKeyDown = this._onKeyDown.bind(this); this._onInput = this._onInput.bind(this); this._onClearSearch = this._onClearSearch.bind(this); - this._onFilterTextboxContextMenu = - this._onFilterTextboxContextMenu.bind(this); this.searchBox.addEventListener("keydown", this._onKeyDown, true); this.searchBox.addEventListener("input", this._onInput, true); - this.searchBox.addEventListener("contextmenu", - this._onFilterTextboxContextMenu); + this.searchBox.addEventListener("contextmenu", this.inspector.onTextBoxContextMenu); this.searchClearButton.addEventListener("click", this._onClearSearch); // For testing, we need to be able to wait for the most recent node request // to finish. Tests can watch this promise for that. this._lastQuery = promise.resolve(null); this.autocompleter = new SelectorAutocompleter(inspector, input); EventEmitter.decorate(this); @@ -64,17 +61,17 @@ InspectorSearch.prototype = { get walker() { return this.inspector.walker; }, destroy: function () { this.searchBox.removeEventListener("keydown", this._onKeyDown, true); this.searchBox.removeEventListener("input", this._onInput, true); this.searchBox.removeEventListener("contextmenu", - this._onFilterTextboxContextMenu); + this.inspector.onTextBoxContextMenu); this.searchClearButton.removeEventListener("click", this._onClearSearch); this.searchBox = null; this.searchClearButton = null; this.autocompleter.destroy(); }, _onSearch: function (reverse = false) { this.doFullTextSearch(this.searchBox.value, reverse) @@ -131,28 +128,16 @@ InspectorSearch.prototype = { const modifierKey = Services.appinfo.OS === "Darwin" ? event.metaKey : event.ctrlKey; if (event.keyCode === KeyCodes.DOM_VK_G && modifierKey) { this._onSearch(event.shiftKey); event.preventDefault(); } }, - /** - * Context menu handler for filter search box. - */ - _onFilterTextboxContextMenu: function (event) { - try { - let contextmenu = this.inspector.toolbox.textboxContextMenuPopup; - contextmenu.openPopupAtScreen(event.screenX, event.screenY, true); - } catch (e) { - console.error(e); - } - }, - _onClearSearch: function () { this.searchBox.classList.remove("devtools-style-searchbox-no-match"); this.searchBox.value = ""; this.searchClearButton.hidden = true; this.emit("search-cleared"); } };
rename from devtools/client/inspector/inspector-panel.js rename to devtools/client/inspector/inspector.js --- a/devtools/client/inspector/inspector-panel.js +++ b/devtools/client/inspector/inspector.js @@ -1,16 +1,20 @@ /* -*- 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 */ + "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"); @@ -76,54 +80,53 @@ const PORTRAIT_MODE_WIDTH = 700; * - computed-view-filtered * Fired when the computed rules view is filtered * - rule-view-refreshed * Fired when the rule view updates to a new node * - rule-view-sourcelinks-updated * Fired when the stylesheet source links have been updated (when switching * to source-mapped files) */ -function InspectorPanel(iframeWindow, toolbox) { +function Inspector(toolbox) { this._toolbox = toolbox; this._target = toolbox.target; - this.panelDoc = iframeWindow.document; - this.panelWin = iframeWindow; + this.panelDoc = window.document; + this.panelWin = window; this.panelWin.inspector = this; this.telemetry = new Telemetry(); this.nodeMenuTriggerInfo = null; this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this); this._onBeforeNavigate = this._onBeforeNavigate.bind(this); this.onNewRoot = this.onNewRoot.bind(this); this._onContextMenu = this._onContextMenu.bind(this); + this.onTextBoxContextMenu = this.onTextBoxContextMenu.bind(this); this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this); this.onNewSelection = this.onNewSelection.bind(this); this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this); this.onDetached = this.onDetached.bind(this); this.onPaneToggleButtonClicked = this.onPaneToggleButtonClicked.bind(this); this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this); this.onPanelWindowResize = this.onPanelWindowResize.bind(this); this.onSidebarShown = this.onSidebarShown.bind(this); this.onSidebarHidden = this.onSidebarHidden.bind(this); this._target.on("will-navigate", this._onBeforeNavigate); this._detectingActorFeatures = this._detectActorFeatures(); EventEmitter.decorate(this); } -exports.InspectorPanel = InspectorPanel; - -InspectorPanel.prototype = { +Inspector.prototype = { /** * open is effectively an asynchronous constructor */ - open: Task.async(function* () { + init: Task.async(function* () { // Localize all the nodes containing a data-localization attribute. localizeMarkup(this.panelDoc); this._cssPropertiesLoaded = initCssProperties(this.toolbox); yield this._cssPropertiesLoaded; yield this.target.makeRemote(); yield this._getPageStyle(); let defaultSelection = yield this._getDefaultNodeForSelection(); @@ -141,16 +144,20 @@ InspectorPanel.prototype = { get walker() { return this._toolbox.walker; }, get selection() { return this._toolbox.selection; }, + get highlighter() { + return this._toolbox.highlighter; + }, + get isOuterHTMLEditable() { return this._target.client.traits.editOuterHTML; }, get hasUrlToImageDataResolver() { return this._target.client.traits.urlToImageDataResolver; }, @@ -270,17 +277,17 @@ InspectorPanel.prototype = { this._defaultNode = null; this.selection.setNodeFront(null); this._destroyMarkup(); this.isDirty = false; this._pendingSelection = null; }, _getPageStyle: function () { - return this._toolbox.inspector.getPageStyle().then(pageStyle => { + return this.inspector.getPageStyle().then(pageStyle => { this.pageStyle = pageStyle; }, this._handleRejectionIfNotDestroyed); }, /** * Return a promise that will resolve to the default node for selection. */ _getDefaultNodeForSelection: function () { @@ -436,18 +443,16 @@ InspectorPanel.prototype = { /** * Build Splitter located between the main and side area of * the Inspector panel. */ setupSplitter: function () { let SplitBox = this.React.createFactory(this.browserRequire( "devtools/client/shared/components/splitter/split-box")); - this.panelWin.addEventListener("resize", this.onPanelWindowResize, true); - let splitter = SplitBox({ className: "inspector-sidebar-splitter", initialWidth: INITIAL_SIDEBAR_SIZE, initialHeight: INITIAL_SIDEBAR_SIZE, minSize: MIN_SIDEBAR_SIZE, splitterSize: 1, endPanelControl: true, startPanel: this.InspectorTabPanel({ @@ -457,16 +462,18 @@ InspectorPanel.prototype = { id: "inspector-sidebar-container" }), vert: this.useLandscapeMode(), }); this._splitter = this.ReactDOM.render(splitter, this.panelDoc.getElementById("inspector-splitter-box")); + this.panelWin.addEventListener("resize", this.onPanelWindowResize, true); + // Persist splitter state in preferences. this.sidebar.on("show", this.onSidebarShown); this.sidebar.on("hide", this.onSidebarHidden); this.sidebar.on("destroy", this.onSidebarHidden); }, /** * Splitter clean up. @@ -597,17 +604,17 @@ InspectorPanel.prototype = { // Setup the add-node button. this.addNode = this.addNode.bind(this); this.addNodeButton = this.panelDoc.getElementById("inspector-element-add-button"); this.addNodeButton.addEventListener("click", this.addNode); // Setup the eye-dropper icon if we're in an HTML document and we have actor support. if (this.selection.nodeFront && this.selection.nodeFront.isInHTMLDocument) { - this.toolbox.target.actorHasMethod("inspector", "pickColorFromPage").then(value => { + this.target.actorHasMethod("inspector", "pickColorFromPage").then(value => { if (!value) { return; } this.onEyeDropperDone = this.onEyeDropperDone.bind(this); this.onEyeDropperButtonClicked = this.onEyeDropperButtonClicked.bind(this); this.eyeDropperButton = this.panelDoc .getElementById("inspector-eyedropper-toggle"); @@ -921,16 +928,27 @@ InspectorPanel.prototype = { e.preventDefault(); this._openMenu({ screenX: e.screenX, screenY: e.screenY, target: e.target, }); }, + /** + * This is meant to be called by all the search, filter, inplace text boxes in the + * inspector, and just calls through to the toolbox openTextBoxContextMenu helper. + * @param {DOMEvent} e + */ + onTextBoxContextMenu: function (e) { + e.stopPropagation(); + e.preventDefault(); + this.toolbox.openTextBoxContextMenu(e.screenX, e.screenY); + }, + _openMenu: function ({ target, screenX = 0, screenY = 0 } = { }) { let markupContainer = this.markup.getContainer(this.selection.nodeFront); this.contextMenuTarget = target; this.nodeMenuTriggerInfo = markupContainer && markupContainer.editor.getInfoAtNode(target); let isSelectionElement = this.selection.isElementNode() && @@ -1268,17 +1286,17 @@ InspectorPanel.prototype = { let doc = this.panelDoc; this._markupBox = doc.getElementById("markup-box"); // create tool iframe this._markupFrame = doc.createElement("iframe"); this._markupFrame.setAttribute("flex", "1"); this._markupFrame.setAttribute("tooltip", "aHTMLTooltip"); - this._markupFrame.addEventListener("contextmenu", this._onContextMenu, true); + 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.setAttribute("aria-label", @@ -1297,17 +1315,17 @@ InspectorPanel.prototype = { this.emit("markuploaded"); }, _destroyMarkup: function () { let destroyPromise; if (this._markupFrame) { this._markupFrame.removeEventListener("load", this._onMarkupFrameLoad, true); - this._markupFrame.removeEventListener("contextmenu", this._onContextMenu, true); + this._markupFrame.removeEventListener("contextmenu", this._onContextMenu); } if (this.markup) { destroyPromise = this.markup.destroy(); this.markup = null; } else { destroyPromise = promise.resolve(); } @@ -1811,8 +1829,95 @@ InspectorPanel.prototype = { copyAttributeLink: function (link) { // 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); } }; + +// 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) { + 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"); + + Task.spawn(function* () { + let target = yield targetFromURL(url); + + 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: 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); + + let inspectorUI = new Inspector(fakeToolbox); + inspectorUI.init(); + }).then(null, e => { + window.alert("Unable to start the inspector:" + e.message + "\n" + e.stack); + }); +}
--- a/devtools/client/inspector/inspector.xhtml +++ b/devtools/client/inspector/inspector.xhtml @@ -20,16 +20,17 @@ <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"></script> + <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" data-localization-bundle="devtools/locale/inspector.properties">
--- a/devtools/client/inspector/markup/markup.js +++ b/devtools/client/inspector/markup/markup.js @@ -79,18 +79,18 @@ const INSPECTOR_L10N = new LocalizationH * updating based on mutations, and the undo/redo bindings. * * @param {Inspector} inspector * The inspector we're watching. * @param {iframe} frame * An iframe in which the caller has kindly loaded markup.xhtml. */ function MarkupView(inspector, frame, controllerWindow) { - this._inspector = inspector; - this.walker = this._inspector.walker; + this.inspector = inspector; + this.walker = this.inspector.walker; this._frame = frame; this.win = this._frame.contentWindow; this.doc = this._frame.contentDocument; this._elt = this.doc.querySelector("#root"); this.htmlEditor = new HTMLEditor(this.doc); try { this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize"); @@ -104,17 +104,17 @@ function MarkupView(inspector, frame, co Services.prefs.getIntPref(ATTR_COLLAPSE_LENGTH_PREF); // Creating the popup to be used to show CSS suggestions. let options = { autoSelect: true, theme: "auto", }; - this.popup = new AutocompletePopup(inspector._toolbox, options); + this.popup = new AutocompletePopup(inspector.toolbox, options); this.undo = new UndoStack(); this.undo.installController(controllerWindow); this._containers = new Map(); // Binding functions that need to be called in scope. this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this); @@ -140,18 +140,18 @@ function MarkupView(inspector, frame, co this._elt.addEventListener("mousemove", this._onMouseMove, false); this._elt.addEventListener("mouseout", this._onMouseOut, false); this._elt.addEventListener("blur", this._onBlur, true); this.win.addEventListener("mouseup", this._onMouseUp); this.win.addEventListener("copy", this._onCopy); this._frame.addEventListener("focus", this._onFocus, false); this.walker.on("mutations", this._mutationObserver); this.walker.on("display-change", this._onDisplayChange); - this._inspector.selection.on("new-node-front", this._onNewSelection); - this._inspector.toolbox.on("picker-node-hovered", this._onToolboxPickerHover); + this.inspector.selection.on("new-node-front", this._onNewSelection); + this.toolbox.on("picker-node-hovered", this._onToolboxPickerHover); this._onNewSelection(); this._initTooltips(); this._prefObserver = new PrefObserver("devtools.markup"); this._prefObserver.on(ATTR_COLLAPSE_ENABLED_PREF, this._onCollapseAttributesPrefChange); this._prefObserver.on(ATTR_COLLAPSE_LENGTH_PREF, @@ -163,32 +163,36 @@ function MarkupView(inspector, frame, co MarkupView.prototype = { /** * How long does a node flash when it mutates (in ms). */ CONTAINER_FLASHING_DURATION: 500, _selectedContainer: null, + get toolbox() { + return this.inspector.toolbox; + }, + /** * Handle promise rejections for various asynchronous actions, and only log errors if * the markup view still exists. * This is useful to silence useless errors that happen when the markup view is * destroyed while still initializing (and making protocol requests). */ _handleRejectionIfNotDestroyed: function (e) { if (!this._destroyer) { console.error(e); } }, _initTooltips: function () { - this.eventDetailsTooltip = new HTMLTooltip(this._inspector.toolbox, + this.eventDetailsTooltip = new HTMLTooltip(this.toolbox, {type: "arrow"}); - this.imagePreviewTooltip = new HTMLTooltip(this._inspector.toolbox, + this.imagePreviewTooltip = new HTMLTooltip(this.toolbox, {type: "arrow", useXulWrapper: "true"}); this._enableImagePreviewTooltip(); }, _enableImagePreviewTooltip: function () { this.imagePreviewTooltip.startTogglingOnHover(this._elt, this._isImagePreviewTarget); }, @@ -418,31 +422,30 @@ MarkupView.prototype = { * * @param {NodeFront} nodeFront * The node to show the highlighter for * @return {Promise} Resolves when the highlighter for this nodeFront is * shown, taking into account that there could already be highlighter * requests queued up */ _showBoxModel: function (nodeFront) { - return this._inspector.toolbox.highlighterUtils - .highlightNodeFront(nodeFront); + return this.toolbox.highlighterUtils.highlightNodeFront(nodeFront); }, /** * Hide the box model highlighter on a given node front * * @param {Boolean} forceHide * See toolbox-highlighter-utils/unhighlight * @return {Promise} Resolves when the highlighter for this nodeFront is * hidden, taking into account that there could already be highlighter * requests queued up */ _hideBoxModel: function (forceHide) { - return this._inspector.toolbox.highlighterUtils.unhighlight(forceHide); + return this.toolbox.highlighterUtils.unhighlight(forceHide); }, _briefBoxModelTimer: null, _clearBriefBoxModelTimer: function () { if (this._briefBoxModelTimer) { clearTimeout(this._briefBoxModelTimer); this._briefBoxModelPromise.resolve(); @@ -546,47 +549,47 @@ MarkupView.prototype = { * - if it's "test" (this is a special case for mochitest. In tests, we often * need to select elements but don't necessarily want the highlighter to come * and go after a delay as this might break test scenarios) * We also do not want to start a brief highlight timeout if the node is * already being hovered over, since in that case it will already be * highlighted. */ _shouldNewSelectionBeHighlighted: function () { - let reason = this._inspector.selection.reason; + let reason = this.inspector.selection.reason; let unwantedReasons = [ "inspector-open", "navigateaway", "nodeselected", "test" ]; - let isHighlight = this._hoveredNode === this._inspector.selection.nodeFront; + let isHighlight = this._hoveredNode === this.inspector.selection.nodeFront; return !isHighlight && reason && unwantedReasons.indexOf(reason) === -1; }, /** * React to new-node-front selection events. * Highlights the node if needed, and make sure it is shown and selected in * the view. */ _onNewSelection: function () { - let selection = this._inspector.selection; + let selection = this.inspector.selection; this.htmlEditor.hide(); if (this._hoveredNode && this._hoveredNode !== selection.nodeFront) { this.getContainer(this._hoveredNode).hovered = false; this._hoveredNode = null; } if (!selection.isNode()) { this.unmarkSelectedNode(); return; } - let done = this._inspector.updating("markup-view"); + let done = this.inspector.updating("markup-view"); let onShowBoxModel, onShow; // Highlight the element briefly if needed. if (this._shouldNewSelectionBeHighlighted()) { onShowBoxModel = this._brieflyShowBoxModel(selection.nodeFront); } onShow = this.showNode(selection.nodeFront).then(() => { @@ -606,17 +609,17 @@ MarkupView.prototype = { promise.all([onShowBoxModel, onShow]).then(done); }, /** * Maybe make selected the current node selection's MarkupContainer depending * on why the current node got selected. */ maybeNavigateToNewSelection: function () { - let {reason, nodeFront} = this._inspector.selection; + let {reason, nodeFront} = this.inspector.selection; // The list of reasons that should lead to navigating to the node. let reasonsToNavigate = [ // If the user picked an element with the element picker. "picker-node-picked", // If the user selected an element with the browser context menu. "browser-context-menu", // If the user added a new node by clicking in the inspector toolbar. @@ -651,19 +654,19 @@ MarkupView.prototype = { }, _onCopy: function (evt) { // Ignore copy events from editors if (this._isInputOrTextarea(evt.target)) { return; } - let selection = this._inspector.selection; + let selection = this.inspector.selection; if (selection.isNode()) { - this._inspector.copyOuterHTML(); + this.inspector.copyOuterHTML(); } evt.stopPropagation(); evt.preventDefault(); }, /** * Register all key shortcuts. */ @@ -708,17 +711,17 @@ MarkupView.prototype = { break; } case "markupView.edit.key": { this.beginEditingOuterHTML(this._selectedContainer.node); break; } case "markupView.scrollInto.key": { let selection = this._selectedContainer.node; - this._inspector.scrollNodeIntoView(selection); + this.inspector.scrollNodeIntoView(selection); break; } // Generic keys case "Delete": { this.deleteNodeOrAttribute(); break; } case "Backspace": { @@ -960,34 +963,34 @@ MarkupView.prototype = { let container; let {nodeType, isPseudoElement} = node; if (node === this.walker.rootNode) { container = new RootContainer(this, node); this._elt.appendChild(container.elt); this._rootNode = node; } else if (nodeType == nodeConstants.ELEMENT_NODE && !isPseudoElement) { - container = new MarkupElementContainer(this, node, this._inspector); + container = new MarkupElementContainer(this, node, this.inspector); } else if (nodeType == nodeConstants.COMMENT_NODE || nodeType == nodeConstants.TEXT_NODE) { - container = new MarkupTextContainer(this, node, this._inspector); + container = new MarkupTextContainer(this, node, this.inspector); } else { - container = new MarkupReadOnlyContainer(this, node, this._inspector); + container = new MarkupReadOnlyContainer(this, node, this.inspector); } if (flashNode) { container.flashMutation(); } this._containers.set(node, container); container.childrenDirty = true; this._updateChildren(container); - this._inspector.emit("container-created", container); + this.inspector.emit("container-created", container); return container; }, /** * Mutation observer used for included nodes. */ _mutationObserver: function (mutations) { @@ -1034,17 +1037,17 @@ MarkupView.prototype = { this._waitForChildren().then(() => { if (this._destroyer) { // Could not fully update after markup mutations, the markup-view was destroyed // while waiting for children. Bail out silently. return; } this._flashMutatedNodes(mutations); - this._inspector.emit("markupmutation", mutations); + this.inspector.emit("markupmutation", mutations); // Since the htmlEditor is absolutely positioned, a mutation may change // the location in which it should be shown. this.htmlEditor.refresh(); }); }, /** @@ -1272,47 +1275,47 @@ MarkupView.prototype = { isNodeRemovalMutation = true; break; } } if (!isNodeRemovalMutation) { return; } - this._inspector.off("markupmutation", onMutations); + this.inspector.off("markupmutation", onMutations); this._removedNodeObserver = null; // Don't select the new node if the user has already changed the current // selection. - if (this._inspector.selection.nodeFront === parentContainer.node || - (this._inspector.selection.nodeFront === removedNode && isHTMLTag)) { + if (this.inspector.selection.nodeFront === parentContainer.node || + (this.inspector.selection.nodeFront === removedNode && isHTMLTag)) { let childContainers = parentContainer.getChildContainers(); if (childContainers && childContainers[childIndex]) { this.markNodeAsSelected(childContainers[childIndex].node, reason); if (childContainers[childIndex].hasChildren) { this.expandNode(childContainers[childIndex].node); } this.emit("reselectedonremoved"); } } }; // Start listening for mutations until we find a childList change that has // removedNode removed. - this._inspector.on("markupmutation", onMutations); + this.inspector.on("markupmutation", onMutations); }, /** * Make sure to stop listening for node removal markupmutations and not * reselect the corresponding node when that happens. * Useful when the outerHTML/tagname edition failed. */ cancelReselectOnRemoved: function () { if (this._removedNodeObserver) { - this._inspector.off("markupmutation", this._removedNodeObserver); + this.inspector.off("markupmutation", this._removedNodeObserver); this._removedNodeObserver = null; this.emit("canceledreselectonremoved"); } }, /** * Replace the outerHTML of any node displayed in the inspector with * some other HTML code @@ -1474,18 +1477,18 @@ MarkupView.prototype = { // Select the new container. this._selectedContainer = container; if (node) { this._selectedContainer.selected = true; } // Change the current selection if needed. - if (this._inspector.selection.nodeFront !== node) { - this._inspector.selection.setNodeFront(node, reason || "nodeselected"); + if (this.inspector.selection.nodeFront !== node) { + this.inspector.selection.setNodeFront(node, reason || "nodeselected"); } return true; }, /** * Make sure that every ancestor of the selection are updated * and included in the list of visible children. @@ -1521,17 +1524,17 @@ MarkupView.prototype = { * Check if the current selection is a descendent of the container. * if so, make sure it's among the visible set for the container, * and set the dirty flag if needed. * * @return The node that should be made visible, if any. */ _checkSelectionVisible: function (container) { let centered = null; - let node = this._inspector.selection.nodeFront; + let node = this.inspector.selection.nodeFront; while (node) { if (node.parentNode() === container.node) { centered = node; break; } node = node.parentNode(); } @@ -1736,18 +1739,18 @@ MarkupView.prototype = { this._elt.removeEventListener("mousemove", this._onMouseMove, false); this._elt.removeEventListener("mouseout", this._onMouseOut, false); this._elt.removeEventListener("blur", this._onBlur, true); this.win.removeEventListener("mouseup", this._onMouseUp); this.win.removeEventListener("copy", this._onCopy); this._frame.removeEventListener("focus", this._onFocus, false); this.walker.off("mutations", this._mutationObserver); this.walker.off("display-change", this._onDisplayChange); - this._inspector.selection.off("new-node-front", this._onNewSelection); - this._inspector.toolbox.off("picker-node-hovered", + this.inspector.selection.off("new-node-front", this._onNewSelection); + this.toolbox.off("picker-node-hovered", this._onToolboxPickerHover); this._prefObserver.off(ATTR_COLLAPSE_ENABLED_PREF, this._onCollapseAttributesPrefChange); this._prefObserver.off(ATTR_COLLAPSE_LENGTH_PREF, this._onCollapseAttributesPrefChange); this._prefObserver.destroy(); @@ -2333,17 +2336,17 @@ MarkupContainer.prototype = { } // Follow attribute links if middle or meta click. if (isMiddleClick || isMetaClick) { let link = target.dataset.link; let type = target.dataset.type; // Make container tabbable descendants not tabbable (by default). this.canFocus = false; - this.markup._inspector.followAttributeLink(type, link); + this.markup.inspector.followAttributeLink(type, link); return; } // Start node drag & drop (if the mouse moved, see _onMouseMove). if (isLeftClick && this.isDraggable()) { this._isPreDragging = true; this._dragStartY = event.pageY; } @@ -2641,17 +2644,17 @@ function MarkupElementContainer(markupVi MarkupElementContainer.prototype = Heritage.extend(MarkupContainer.prototype, { _buildEventTooltipContent: Task.async(function* (target, tooltip) { if (target.hasAttribute("data-event")) { yield tooltip.hide(); let listenerInfo = yield this.node.getEventListenerInfo(); - let toolbox = this.markup._inspector.toolbox; + let toolbox = this.markup.toolbox; setEventTooltip(tooltip, listenerInfo, toolbox); // Disable the image preview tooltip while we display the event details this.markup._disableImagePreviewTooltip(); tooltip.once("hidden", () => { // Enable the image preview tooltip after closing the event details this.markup._enableImagePreviewTooltip(); }); tooltip.show(target); @@ -2907,17 +2910,18 @@ function TextEditor(container, node, tem this.container.undo.do(() => { this.node.setNodeValue(val); }, () => { this.node.setNodeValue(oldValue); }); }); }); }, - cssProperties: getCssProperties(this.markup._inspector.toolbox) + cssProperties: getCssProperties(this.markup.toolbox), + contextMenu: this.markup.inspector.onTextBoxContextMenu }); this.update(); } TextEditor.prototype = { get selected() { return this._selected; @@ -2961,17 +2965,17 @@ TextEditor.prototype = { * The node being edited. */ function ElementEditor(container, node) { this.container = container; this.node = node; this.markup = this.container.markup; this.template = this.markup.template.bind(this.markup); this.doc = this.markup.doc; - this._cssProperties = getCssProperties(this.markup._inspector.toolbox); + this._cssProperties = getCssProperties(this.markup.toolbox); this.attrElements = new Map(); this.animationTimers = {}; // The templates will fill the following properties this.elt = null; this.tag = null; this.closeTag = null; @@ -2989,16 +2993,17 @@ function ElementEditor(container, node) this.tag.setAttribute("tabindex", "-1"); editableField({ element: this.tag, multiline: true, maxWidth: () => getAutocompleteMaxWidth(this.tag, this.container.elt), trigger: "dblclick", stopOnReturn: true, done: this.onTagEdit.bind(this), + contextMenu: this.markup.inspector.onTextBoxContextMenu, cssProperties: this._cssProperties }); } // Make the new attribute space editable. this.newAttr.editMode = editableField({ element: this.newAttr, multiline: true, @@ -3016,16 +3021,17 @@ function ElementEditor(container, node) let undoMods = this._startModifyingAttributes(); this._applyAttributes(val, null, doMods, undoMods); this.container.undo.do(() => { doMods.apply(); }, function () { undoMods.apply(); }); }, + contextMenu: this.markup.inspector.onTextBoxContextMenu, cssProperties: this._cssProperties }); let displayName = this.node.displayName; this.tag.textContent = displayName; this.closeTag.textContent = displayName; let isVoidElement = HTML_VOID_ELEMENTS.includes(displayName); @@ -3257,16 +3263,17 @@ ElementEditor.prototype = { doMods.removeAttribute(attribute.name); this._applyAttributes(newValue, attr, doMods, undoMods); this.container.undo.do(() => { doMods.apply(); }, () => { undoMods.apply(); }); }, + contextMenu: this.markup.inspector.onTextBoxContextMenu, cssProperties: this._cssProperties }); // Figure out where we should place the attribute. if (attribute.name == "id") { before = this.attrList.firstChild; } else if (attribute.name == "class") { let idNode = this.attrElements.get("id"); @@ -3357,18 +3364,17 @@ ElementEditor.prototype = { * Listen to mutations, and when the attribute list is regenerated * try to focus on the attribute after the one that's being edited now. * If the attribute order changes, go to the beginning of the attribute list. */ refocusOnEdit: function (attrName, attrNode, direction) { // Only allow one refocus on attribute change at a time, so when there's // more than 1 request in parallel, the last one wins. if (this._editedAttributeObserver) { - this.markup._inspector.off("markupmutation", - this._editedAttributeObserver); + this.markup.inspector.off("markupmutation", this._editedAttributeObserver); this._editedAttributeObserver = null; } let container = this.markup.getContainer(this.node); let activeAttrs = [...this.attrList.childNodes] .filter(el => el.style.display != "none"); let attributeIndex = activeAttrs.indexOf(attrNode); @@ -3443,17 +3449,17 @@ ElementEditor.prototype = { editable.focus(); } this.markup.emit("refocusedonedit"); }; // Start listening for mutations until we find an attributes change // that modifies this attribute. - this.markup._inspector.once("markupmutation", onMutations); + this.markup.inspector.once("markupmutation", onMutations); }, /** * Called when the tag name editor has is done editing. */ onTagEdit: function (newTagName, isCommit) { if (!isCommit || newTagName.toLowerCase() === this.node.tagName.toLowerCase() ||
--- a/devtools/client/inspector/markup/test/browser_markup_update-on-navigtion.js +++ b/devtools/client/inspector/markup/test/browser_markup_update-on-navigtion.js @@ -10,17 +10,17 @@ const URL_1 = SCHEMA + "<div id='one' st const URL_2 = SCHEMA + "<div id='two' style='color:green;'>TWO</div>"; add_task(function* () { let {inspector, testActor} = yield openInspectorForURL(URL_1); assertMarkupViewIsLoaded(); yield selectNode("#one", inspector); - let willNavigate = inspector.toolbox.target.once("will-navigate"); + let willNavigate = inspector.target.once("will-navigate"); yield testActor.eval(`content.location = "${URL_2}"`); info("Waiting for will-navigate"); yield willNavigate; info("Navigation to page 2 has started, the inspector should be empty"); assertMarkupViewIsEmpty();
--- a/devtools/client/inspector/moz.build +++ b/devtools/client/inspector/moz.build @@ -9,15 +9,15 @@ DIRS += [ 'markup', 'rules', 'shared' ] DevToolsModules( 'breadcrumbs.js', 'inspector-commands.js', - 'inspector-panel.js', 'inspector-search.js', 'inspector.xhtml', + 'panel.js', 'toolsidebar.js', ) BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/panel.js @@ -0,0 +1,19 @@ +/* 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 InspectorPanel(iframeWindow, toolbox) { + this._inspector = new iframeWindow.Inspector(toolbox); +} +InspectorPanel.prototype = { + open() { + return this._inspector.init(); + }, + + destroy() { + return this._inspector.destroy(); + } +}; +exports.InspectorPanel = InspectorPanel;
--- a/devtools/client/inspector/rules/rules.js +++ b/devtools/client/inspector/rules/rules.js @@ -107,18 +107,16 @@ function CssRuleView(inspector, document this._outputParser = new OutputParser(document, this.cssProperties); this._onAddRule = this._onAddRule.bind(this); this._onContextMenu = this._onContextMenu.bind(this); this._onCopy = this._onCopy.bind(this); this._onFilterStyles = this._onFilterStyles.bind(this); this._onClearSearch = this._onClearSearch.bind(this); - this._onFilterTextboxContextMenu = - this._onFilterTextboxContextMenu.bind(this); this._onTogglePseudoClassPanel = this._onTogglePseudoClassPanel.bind(this); this._onTogglePseudoClass = this._onTogglePseudoClass.bind(this); let doc = this.styleDocument; this.element = doc.getElementById("ruleview-container-focusable"); this.addRuleButton = doc.getElementById("ruleview-add-rule-button"); this.searchField = doc.getElementById("ruleview-searchbox"); this.searchClearButton = doc.getElementById("ruleview-searchinput-clear"); @@ -135,18 +133,17 @@ function CssRuleView(inspector, document this.shortcuts.on("Escape", this._onShortcut); this.shortcuts.on("Return", this._onShortcut); this.shortcuts.on("Space", this._onShortcut); this.shortcuts.on("CmdOrCtrl+F", this._onShortcut); this.element.addEventListener("copy", this._onCopy); this.element.addEventListener("contextmenu", this._onContextMenu); this.addRuleButton.addEventListener("click", this._onAddRule); this.searchField.addEventListener("input", this._onFilterStyles); - this.searchField.addEventListener("contextmenu", - this._onFilterTextboxContextMenu); + this.searchField.addEventListener("contextmenu", this.inspector.onTextBoxContextMenu); this.searchClearButton.addEventListener("click", this._onClearSearch); this.pseudoClassToggle.addEventListener("click", this._onTogglePseudoClassPanel); this.hoverCheckbox.addEventListener("click", this._onTogglePseudoClass); this.activeCheckbox.addEventListener("click", this._onTogglePseudoClass); this.focusCheckbox.addEventListener("click", this._onTogglePseudoClass); this._handlePrefChange = this._handlePrefChange.bind(this); @@ -465,17 +462,17 @@ CssRuleView.prototype = { }, /** * Add a new rule to the current element. */ _onAddRule: function () { let elementStyle = this._elementStyle; let element = elementStyle.element; - let client = this.inspector.toolbox.target.client; + let client = this.inspector.target.client; let pseudoClasses = element.pseudoClassLocks; if (!client.traits.addNewRule) { return; } if (!this.pageStyle.supportsAuthoredStyles) { // We're talking to an old server. @@ -640,29 +637,16 @@ CssRuleView.prototype = { this.inspector.emit("ruleview-filtered"); this._filterChangeTimeout = null; }, filterTimeout); }, /** - * Context menu handler for filter style search box. - */ - _onFilterTextboxContextMenu: function (event) { - try { - this.styleWindow.focus(); - let contextmenu = this.inspector.toolbox.textboxContextMenuPopup; - contextmenu.openPopupAtScreen(event.screenX, event.screenY, true); - } catch (e) { - console.error(e); - } - }, - - /** * Called when the user clicks on the clear button in the filter style search * box. Returns true if the search box is cleared and false otherwise. */ _onClearSearch: function () { if (this.searchField.value) { this.setFilterStyles(""); return true; } @@ -694,17 +678,17 @@ CssRuleView.prototype = { // Remove bound listeners this.shortcuts.destroy(); this.element.removeEventListener("copy", this._onCopy); this.element.removeEventListener("contextmenu", this._onContextMenu); this.addRuleButton.removeEventListener("click", this._onAddRule); this.searchField.removeEventListener("input", this._onFilterStyles); this.searchField.removeEventListener("contextmenu", - this._onFilterTextboxContextMenu); + this.inspector.onTextBoxContextMenu); this.searchClearButton.removeEventListener("click", this._onClearSearch); this.pseudoClassToggle.removeEventListener("click", this._onTogglePseudoClassPanel); this.hoverCheckbox.removeEventListener("click", this._onTogglePseudoClass); this.activeCheckbox.removeEventListener("click", this._onTogglePseudoClass); this.focusCheckbox.removeEventListener("click", this._onTogglePseudoClass); this.searchField = null;
--- a/devtools/client/inspector/rules/test/browser_rules_search-filter_context-menu.js +++ b/devtools/client/inspector/rules/test/browser_rules_search-filter_context-menu.js @@ -11,17 +11,17 @@ const TEST_URI = "<h1>test filter contex add_task(function* () { yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); let {toolbox, inspector, view} = yield openRuleView(); yield selectNode("h1", inspector); let win = view.styleWindow; let searchField = view.searchField; - let searchContextMenu = toolbox.textboxContextMenuPopup; + let searchContextMenu = toolbox.textBoxContextMenuPopup; ok(searchContextMenu, "The search filter context menu is loaded in the rule view"); let cmdUndo = searchContextMenu.querySelector("[command=cmd_undo]"); let cmdDelete = searchContextMenu.querySelector("[command=cmd_delete]"); let cmdSelectAll = searchContextMenu.querySelector("[command=cmd_selectAll]"); let cmdCut = searchContextMenu.querySelector("[command=cmd_cut]"); let cmdCopy = searchContextMenu.querySelector("[command=cmd_copy]");
--- a/devtools/client/inspector/rules/views/rule-editor.js +++ b/devtools/client/inspector/rules/views/rule-editor.js @@ -82,17 +82,17 @@ RuleEditor.prototype = { destroy: function () { this.rule.domRule.off("location-changed"); this.toolbox.off("tool-registered", this.updateSourceLink); this.toolbox.off("tool-unregistered", this.updateSourceLink); }, get isSelectorEditable() { let trait = this.isEditable && - this.toolbox.target.client.traits.selectorEditable && + this.ruleView.inspector.target.client.traits.selectorEditable && this.rule.domRule.type !== ELEMENT_STYLE && this.rule.domRule.type !== CSSRule.KEYFRAME_RULE; // Do not allow editing anonymousselectors until we can // detect mutations on pseudo elements in Bug 1034110. return trait && !this.rule.elementStyle.element.isAnonymous; }, @@ -140,17 +140,18 @@ RuleEditor.prototype = { this.selectorText.addEventListener("click", event => { // Clicks within the selector shouldn't propagate any further. event.stopPropagation(); }, false); editableField({ element: this.selectorText, done: this._onSelectorDone, - cssProperties: this.rule.cssProperties + cssProperties: this.rule.cssProperties, + contextMenu: this.ruleView.inspector.onTextBoxContextMenu }); } if (this.rule.domRule.type !== CSSRule.KEYFRAME_RULE) { let selector = this.rule.domRule.selectors ? this.rule.domRule.selectors.join(", ") : this.ruleView.inspector.selectionCssSelector; @@ -443,17 +444,18 @@ RuleEditor.prototype = { this.editor = new InplaceEditor({ element: this.newPropSpan, done: this._onNewProperty, destroy: this._newPropertyDestroy, advanceChars: ":", contentType: InplaceEditor.CONTENT_TYPES.CSS_PROPERTY, popup: this.ruleView.popup, - cssProperties: this.rule.cssProperties + cssProperties: this.rule.cssProperties, + contextMenu: this.ruleView.inspector.onTextBoxContextMenu }); // Auto-close the input if multiple rules get pasted into new property. this.editor.input.addEventListener("paste", blurOnMultipleProperties(this.rule.cssProperties), false); }, /**
--- a/devtools/client/inspector/rules/views/text-property-editor.js +++ b/devtools/client/inspector/rules/views/text-property-editor.js @@ -214,17 +214,18 @@ TextPropertyEditor.prototype = { editableField({ start: this._onStartEditing, element: this.nameSpan, done: this._onNameDone, destroy: this.updatePropertyState, advanceChars: ":", contentType: InplaceEditor.CONTENT_TYPES.CSS_PROPERTY, popup: this.popup, - cssProperties: this.cssProperties + cssProperties: this.cssProperties, + contextMenu: this.ruleView.inspector.onTextBoxContextMenu }); // Auto blur name field on multiple CSS rules get pasted in. this.nameContainer.addEventListener("paste", blurOnMultipleProperties(this.cssProperties), false); this.valueContainer.addEventListener("click", (event) => { // Clicks within the value shouldn't propagate any further. @@ -284,17 +285,18 @@ TextPropertyEditor.prototype = { destroy: this.update, validate: this._onValidate, advanceChars: advanceValidate, contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE, property: this.prop, popup: this.popup, multiline: true, maxWidth: () => this.container.getBoundingClientRect().width, - cssProperties: this.cssProperties + cssProperties: this.cssProperties, + contextMenu: this.ruleView.inspector.onTextBoxContextMenu }); } }, /** * Get the path from which to resolve requests for this * rule's stylesheet. *
--- a/devtools/client/inspector/test/browser.ini +++ b/devtools/client/inspector/test/browser.ini @@ -156,8 +156,9 @@ subsuite = clipboard [browser_inspector_search-reserved.js] [browser_inspector_search-selection.js] [browser_inspector_search-sidebar.js] [browser_inspector_select-docshell.js] [browser_inspector_select-last-selected.js] [browser_inspector_search-navigation.js] [browser_inspector_sidebarstate.js] [browser_inspector_switch-to-inspector-on-pick.js] +[browser_inspector_textbox-menu.js]
--- a/devtools/client/inspector/test/browser_inspector_highlighter-03.js +++ b/devtools/client/inspector/test/browser_inspector_highlighter-03.js @@ -29,17 +29,17 @@ const DOCUMENT_SRC = "<style>" + const TEST_URI = "data:text/html;charset=utf-8," + DOCUMENT_SRC; add_task(function* () { let { inspector, toolbox, testActor } = yield openInspectorForURL(TEST_URI); info("Waiting for box mode to show."); let body = yield getNodeFront("body", inspector); - yield toolbox.highlighter.showBoxModel(body); + yield inspector.highlighter.showBoxModel(body); info("Waiting for element picker to become active."); yield startPicker(toolbox); info("Moving mouse over iframe padding."); yield moveMouseOver("iframe", 1, 1); info("Performing checks");
--- a/devtools/client/inspector/test/browser_inspector_highlighter-04.js +++ b/devtools/client/inspector/test/browser_inspector_highlighter-04.js @@ -22,22 +22,22 @@ const ELEMENTS = ["box-model-root", "box-model-infobar-container", "box-model-infobar-tagname", "box-model-infobar-id", "box-model-infobar-classes", "box-model-infobar-pseudo-classes", "box-model-infobar-dimensions"]; add_task(function* () { - let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL); + let {inspector, testActor} = yield openInspectorForURL(TEST_URL); info("Show the box-model highlighter"); let divFront = yield getNodeFront("div", inspector); - yield toolbox.highlighter.showBoxModel(divFront); + yield inspector.highlighter.showBoxModel(divFront); for (let id of ELEMENTS) { let foundId = yield testActor.getHighlighterNodeAttribute(id, "id"); is(foundId, id, "Element " + id + " found"); } info("Hide the box-model highlighter"); - yield toolbox.highlighter.hideBoxModel(); + yield inspector.highlighter.hideBoxModel(); });
--- a/devtools/client/inspector/test/browser_inspector_highlighter-hover_02.js +++ b/devtools/client/inspector/test/browser_inspector_highlighter-hover_02.js @@ -6,30 +6,30 @@ // Test that when after an element is selected and highlighted on hover, if the // mouse leaves the markup-view and comes back again on the same element, that // the highlighter is shown again on the node const TEST_URL = "data:text/html;charset=utf-8,<p>Select me!</p>"; add_task(function* () { - let {toolbox, inspector, testActor} = yield openInspectorForURL(TEST_URL); + let {inspector, testActor} = yield openInspectorForURL(TEST_URL); info("hover over the <p> line in the markup-view so that it's the " + "currently hovered node"); yield hoverContainer("p", inspector); info("select the <p> markup-container line by clicking"); yield clickContainer("p", inspector); let isVisible = yield testActor.isHighlighting(); ok(isVisible, "the highlighter is shown"); info("listen to the highlighter's hidden event"); let onHidden = testActor.waitForHighlighterEvent("hidden", - toolbox.highlighter); + inspector.highlighter); info("mouse-leave the markup-view"); yield mouseLeaveMarkupView(inspector); yield onHidden; isVisible = yield testActor.isHighlighting(); ok(!isVisible, "the highlighter is hidden after mouseleave"); info("hover over the <p> line again, which is still selected"); yield hoverContainer("p", inspector);
--- a/devtools/client/inspector/test/browser_inspector_highlighter-keybinding_03.js +++ b/devtools/client/inspector/test/browser_inspector_highlighter-keybinding_03.js @@ -41,17 +41,17 @@ add_task(function* () { yield doKeyStop(shortcutOpts); is(inspector.selection.nodeFront.id, "another", "The #another DIV is still selected. Passed."); function doKeyPick(args) { info("Key pressed. Waiting for element to be picked"); testActor.synthesizeKey(args); return promise.all([ - toolbox.selection.once("new-node-front"), + inspector.selection.once("new-node-front"), inspector.once("inspector-updated") ]); } function doKeyStop(args) { info("Key pressed. Waiting for picker to be canceled"); testActor.synthesizeKey(args); return inspector.toolbox.once("picker-stopped");
--- a/devtools/client/inspector/test/browser_inspector_highlighter-options.js +++ b/devtools/client/inspector/test/browser_inspector_highlighter-options.js @@ -180,25 +180,25 @@ const TEST_DATA = [ is(faded, "true", "Region " + region + " is faded"); } } } } ]; add_task(function* () { - let {inspector, toolbox, testActor} = yield openInspectorForURL( + let {inspector, testActor} = yield openInspectorForURL( "data:text/html;charset=utf-8," + encodeURI(TEST_URL)); let divFront = yield getNodeFront("div", inspector); for (let {desc, options, checkHighlighter} of TEST_DATA) { info("Running test: " + desc); info("Show the box-model highlighter with options " + options); - yield toolbox.highlighter.showBoxModel(divFront, options); + yield inspector.highlighter.showBoxModel(divFront, options); yield checkHighlighter(testActor); info("Hide the box-model highlighter"); - yield toolbox.highlighter.hideBoxModel(); + yield inspector.highlighter.hideBoxModel(); } });
--- a/devtools/client/inspector/test/browser_inspector_highlighter-xbl.js +++ b/devtools/client/inspector/test/browser_inspector_highlighter-xbl.js @@ -17,17 +17,17 @@ add_task(function* () { yield doKeyPick({key: "VK_RETURN", options: {}}); is(inspector.selection.nodeFront.className, "scale-slider", "The .scale-slider inside the scale was selected"); function doKeyPick(msg) { info("Key pressed. Waiting for element to be picked"); testActor.synthesizeKey(msg); return promise.all([ - toolbox.selection.once("new-node-front"), + inspector.selection.once("new-node-front"), inspector.once("inspector-updated") ]); } function moveMouseOver(selector) { info("Waiting for element " + selector + " to be highlighted"); testActor.synthesizeMouse({ options: {type: "mousemove"},
--- a/devtools/client/inspector/test/browser_inspector_invalidate.js +++ b/devtools/client/inspector/test/browser_inspector_invalidate.js @@ -10,26 +10,26 @@ const TEST_URI = "data:text/html;charset "browser_inspector_invalidate.js\n" + "<div style=\"width: 100px; height: 100px; background:yellow;\"></div>"; add_task(function* () { let {inspector, testActor} = yield openInspectorForURL(TEST_URI); let divFront = yield getNodeFront("div", inspector); info("Waiting for highlighter to activate"); - yield inspector.toolbox.highlighter.showBoxModel(divFront); + yield inspector.highlighter.showBoxModel(divFront); let rect = yield testActor.getSimpleBorderRect(); is(rect.width, 100, "The highlighter has the right width."); info("Changing the test element's size and waiting for the highlighter " + "to update"); yield testActor.changeHighlightedNodeWaitForUpdate( "style", "width: 200px; height: 100px; background:yellow;" ); rect = yield testActor.getSimpleBorderRect(); is(rect.width, 200, "The highlighter has the right width after update"); info("Waiting for highlighter to hide"); - yield inspector.toolbox.highlighter.hideBoxModel(); + yield inspector.highlighter.hideBoxModel(); });
--- a/devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js +++ b/devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js @@ -84,19 +84,18 @@ function* testNavigate(inspector, testAc ok(!(yield testActor.hasPseudoClassLock("#div-1", PSEUDO)), "pseudo-class lock is removed after inspecting sibling node"); yield selectNode("#div-1", inspector); yield togglePseudoClass(inspector); } function* showPickerOn(selector, inspector) { - let highlighter = inspector.toolbox.highlighter; let nodeFront = yield getNodeFront(selector, inspector); - yield highlighter.showBoxModel(nodeFront); + yield inspector.highlighter.showBoxModel(nodeFront); } function* assertPseudoAddedToNode(inspector, testActor, ruleview) { info("Make sure the pseudoclass lock is applied to #div-1 and its ancestors"); let hasLock = yield testActor.hasPseudoClassLock("#div-1", PSEUDO); ok(hasLock, "pseudo-class lock has been applied"); hasLock = yield testActor.hasPseudoClassLock("#parent-div", PSEUDO); @@ -114,17 +113,17 @@ function* assertPseudoAddedToNode(inspec info("Show the highlighter on #div-1"); yield showPickerOn("#div-1", inspector); info("Check that the infobar selector contains the pseudo-class"); let value = yield testActor.getHighlighterNodeTextContent( "box-model-infobar-pseudo-classes"); is(value, PSEUDO, "pseudo-class in infobar selector"); - yield inspector.toolbox.highlighter.hideBoxModel(); + yield inspector.highlighter.hideBoxModel(); } function* assertPseudoRemovedFromNode(testActor) { info("Make sure the pseudoclass lock is removed from #div-1 and its " + "ancestors"); let hasLock = yield testActor.hasPseudoClassLock("#div-1", PSEUDO); ok(!hasLock, "pseudo-class lock has been removed"); @@ -140,10 +139,10 @@ function* assertPseudoRemovedFromView(in ".ruleview-rule.theme-separator"); is(rules.length, 2, "rule view is showing 2 rules after removing lock"); yield showPickerOn("#div-1", inspector); let value = yield testActor.getHighlighterNodeTextContent( "box-model-infobar-pseudo-classes"); is(value, "", "pseudo-class removed from infobar selector"); - yield inspector.toolbox.highlighter.hideBoxModel(); + yield inspector.highlighter.hideBoxModel(); }
--- a/devtools/client/inspector/test/browser_inspector_search-filter_context-menu.js +++ b/devtools/client/inspector/test/browser_inspector_search-filter_context-menu.js @@ -10,17 +10,17 @@ const TEST_URI = "<h1>test filter contex add_task(function* () { yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); let {toolbox, inspector} = yield openInspector(); let {searchBox} = inspector; yield selectNode("h1", inspector); let win = inspector.panelWin; - let searchContextMenu = toolbox.textboxContextMenuPopup; + let searchContextMenu = toolbox.textBoxContextMenuPopup; ok(searchContextMenu, "The search filter context menu is loaded in the inspector"); let cmdUndo = searchContextMenu.querySelector("[command=cmd_undo]"); let cmdDelete = searchContextMenu.querySelector("[command=cmd_delete]"); let cmdSelectAll = searchContextMenu.querySelector("[command=cmd_selectAll]"); let cmdCut = searchContextMenu.querySelector("[command=cmd_cut]"); let cmdCopy = searchContextMenu.querySelector("[command=cmd_copy]");
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_textbox-menu.js @@ -0,0 +1,90 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that when right-clicking on various text boxes throughout the inspector does use +// the toolbox's context menu (copy/cut/paste/selectAll/Undo). + +add_task(function* () { + yield addTab(`data:text/html;charset=utf-8, + <style>h1 { color: red; }</style> + <h1 id="title">textbox context menu test</h1>`); + let {toolbox, inspector} = yield openInspector(); + yield selectNode("h1", inspector); + + info("Testing the markup-view tagname"); + let container = yield focusNode("h1", inspector); + let tag = container.editor.tag; + tag.focus(); + EventUtils.sendKey("return", inspector.panelWin); + yield checkTextBox(inspector.markup.doc.activeElement, toolbox); + + info("Testing the markup-view attribute"); + EventUtils.sendKey("tab", inspector.panelWin); + yield checkTextBox(inspector.markup.doc.activeElement, toolbox); + + info("Testing the markup-view new attribute"); + // It takes 2 tabs to focus the newAttr field, the first one just moves the cursor to + // the end of the field. + EventUtils.sendKey("tab", inspector.panelWin); + EventUtils.sendKey("tab", inspector.panelWin); + yield checkTextBox(inspector.markup.doc.activeElement, toolbox); + + info("Testing the markup-view textcontent"); + EventUtils.sendKey("tab", inspector.panelWin); + yield checkTextBox(inspector.markup.doc.activeElement, toolbox); + // Blur this last markup-view field, since we're moving on to the rule-view next. + EventUtils.sendKey("escape", inspector.panelWin); + + info("Testing the rule-view selector"); + let ruleView = inspector.ruleview.view; + let cssRuleEditor = getRuleViewRuleEditor(ruleView, 1); + EventUtils.synthesizeMouse(cssRuleEditor.selectorText, 0, 0, {}, inspector.panelWin); + yield checkTextBox(inspector.panelDoc.activeElement, toolbox); + + info("Testing the rule-view property name"); + EventUtils.sendKey("tab", inspector.panelWin); + yield checkTextBox(inspector.panelDoc.activeElement, toolbox); + + info("Testing the rule-view property value"); + EventUtils.sendKey("tab", inspector.panelWin); + yield checkTextBox(inspector.panelDoc.activeElement, toolbox); + + info("Testing the rule-view new property"); + // Tabbing out of the value field triggers a ruleview-changed event that we need to wait + // for. + let onRuleViewChanged = once(ruleView, "ruleview-changed"); + EventUtils.sendKey("tab", inspector.panelWin); + yield onRuleViewChanged; + yield checkTextBox(inspector.panelDoc.activeElement, toolbox); + + info("Switching to the computed-view"); + let onComputedViewReady = inspector.once("boxmodel-view-updated"); + selectComputedView(inspector); + yield onComputedViewReady; + + info("Testing the box-model region"); + let margin = inspector.panelDoc.querySelector(".boxmodel-margin.boxmodel-top > span"); + EventUtils.synthesizeMouseAtCenter(margin, {}, inspector.panelWin); + yield checkTextBox(inspector.panelDoc.activeElement, toolbox); +}); + +function* checkTextBox(textBox, {textBoxContextMenuPopup}) { + is(textBoxContextMenuPopup.state, "closed", "The menu is closed"); + + info("Simulating context click on the textbox and expecting the menu to open"); + let onContextMenu = once(textBoxContextMenuPopup, "popupshown"); + EventUtils.synthesizeMouse(textBox, 2, 2, {type: "contextmenu", button: 2}, + textBox.ownerDocument.defaultView); + yield onContextMenu; + + is(textBoxContextMenuPopup.state, "open", "The menu is now visible"); + + info("Closing the menu"); + let onContextMenuHidden = once(textBoxContextMenuPopup, "popuphidden"); + textBoxContextMenuPopup.hidePopup(); + yield onContextMenuHidden; + + is(textBoxContextMenuPopup.state, "closed", "The menu is closed again"); +}
--- a/devtools/client/jar.mn +++ b/devtools/client/jar.mn @@ -20,16 +20,17 @@ devtools.jar: content/webconsole/webconsole.xul (webconsole/webconsole.xul) * content/scratchpad/scratchpad.xul (scratchpad/scratchpad.xul) content/scratchpad/scratchpad.js (scratchpad/scratchpad.js) content/shared/splitview.css (shared/splitview.css) content/shared/theme-switching.js (shared/theme-switching.js) content/shared/frame-script-utils.js (shared/frame-script-utils.js) content/styleeditor/styleeditor.xul (styleeditor/styleeditor.xul) content/storage/storage.xul (storage/storage.xul) + content/inspector/inspector.js (inspector/inspector.js) content/inspector/fonts/fonts.js (inspector/fonts/fonts.js) content/inspector/markup/markup.xhtml (inspector/markup/markup.xhtml) content/animationinspector/animation-controller.js (animationinspector/animation-controller.js) content/animationinspector/animation-panel.js (animationinspector/animation-panel.js) content/animationinspector/animation-inspector.xhtml (animationinspector/animation-inspector.xhtml) content/sourceeditor/codemirror/addon/dialog/dialog.css (sourceeditor/codemirror/addon/dialog/dialog.css) content/sourceeditor/codemirror/addon/hint/show-hint.js (sourceeditor/codemirror/addon/hint/show-hint.js) content/sourceeditor/codemirror/addon/tern/tern.js (sourceeditor/codemirror/addon/tern/tern.js)
--- a/devtools/client/shared/inplace-editor.js +++ b/devtools/client/shared/inplace-editor.js @@ -83,16 +83,18 @@ function isKeyIn(key, ...keys) { * Called when input is committed or blurred. Called with * current value, a boolean telling the caller whether to * commit the change, and the direction of the next element to be * selected. Direction may be one of Services.focus.MOVEFOCUS_FORWARD, * Services.focus.MOVEFOCUS_BACKWARD, or null (no movement). * This function is called before the editor has been torn down. * {Function} destroy: * Called when the editor is destroyed and has been torn down. + * {Function} contextMenu: + * Called when the user triggers a contextmenu event on the input. * {Object} advanceChars: * This can be either a string or a function. * If it is a string, then if any characters in it are typed, * focus will advance to the next element. * Otherwise, if it is a function, then the function will * be called with three arguments: a key code, the current text, * and the insertion point. If the function returns true, * then the focus advance takes place. If it returns false, @@ -217,16 +219,17 @@ exports.getInplaceEditorForSpan = getInp function InplaceEditor(options, event) { this.elt = options.element; let doc = this.elt.ownerDocument; this.doc = doc; this.elt.inplaceEditor = this; this.cssProperties = options.cssProperties; this.change = options.change; this.done = options.done; + this.contextMenu = options.contextMenu; this.destroy = options.destroy; this.initial = options.initial ? options.initial : this.elt.textContent; this.multiline = options.multiline || false; this.maxWidth = options.maxWidth; if (typeof this.maxWidth == "function") { this.maxWidth = this.maxWidth(); } @@ -244,16 +247,17 @@ function InplaceEditor(options, event) { : !!options.preserveTextStyles; this._onBlur = this._onBlur.bind(this); this._onWindowBlur = this._onWindowBlur.bind(this); this._onKeyPress = this._onKeyPress.bind(this); this._onInput = this._onInput.bind(this); this._onKeyup = this._onKeyup.bind(this); this._onAutocompletePopupClick = this._onAutocompletePopupClick.bind(this); + this._onContextMenu = this._onContextMenu.bind(this); this._createInput(); // Hide the provided element and add our editor. this.originalDisplay = this.elt.style.display; this.elt.style.display = "none"; this.elt.parentNode.insertBefore(this.input, this.elt); @@ -285,16 +289,17 @@ function InplaceEditor(options, event) { } this.input.addEventListener("blur", this._onBlur, false); this.input.addEventListener("keypress", this._onKeyPress, false); this.input.addEventListener("input", this._onInput, false); this.input.addEventListener("dblclick", this._stopEventPropagation, false); this.input.addEventListener("click", this._stopEventPropagation, false); this.input.addEventListener("mousedown", this._stopEventPropagation, false); + this.input.addEventListener("contextmenu", this._onContextMenu, false); this.doc.defaultView.addEventListener("blur", this._onWindowBlur, false); this.validate = options.validate; if (this.validate) { this.input.addEventListener("keyup", this._onKeyup, false); } @@ -344,21 +349,20 @@ InplaceEditor.prototype = { // Already cleared. return; } this.input.removeEventListener("blur", this._onBlur, false); this.input.removeEventListener("keypress", this._onKeyPress, false); this.input.removeEventListener("keyup", this._onKeyup, false); this.input.removeEventListener("input", this._onInput, false); - this.input.removeEventListener("dblclick", this._stopEventPropagation, - false); + this.input.removeEventListener("dblclick", this._stopEventPropagation, false); this.input.removeEventListener("click", this._stopEventPropagation, false); - this.input.removeEventListener("mousedown", this._stopEventPropagation, - false); + this.input.removeEventListener("mousedown", this._stopEventPropagation, false); + this.input.removeEventListener("contextmenu", this._onContextMenu, false); this.doc.defaultView.removeEventListener("blur", this._onWindowBlur, false); this._stopAutosize(); this.elt.style.display = this.originalDisplay; if (this.doc.activeElement == this.input) { this.elt.focus(); @@ -1159,16 +1163,22 @@ InplaceEditor.prototype = { prevent = !input.value; } if (prevent) { event.preventDefault(); } }, + _onContextMenu: function (event) { + if (this.contextMenu) { + this.contextMenu(event); + } + }, + /** * Open the autocomplete popup, adding a custom click handler and classname. * * @param {Number} offset * X-offset relative to the input starting edge. * @param {Number} selectedIndex * The index of the item that should be selected. Use -1 to have no * item selected.
--- a/devtools/client/shared/widgets/Tooltip.js +++ b/devtools/client/shared/widgets/Tooltip.js @@ -736,17 +736,17 @@ Heritage.extend(SwatchBasedEditorTooltip this._originalColor = this.currentSwatchColor.textContent; let color = this.activeSwatch.style.backgroundColor; this.spectrum.off("changed", this._onSpectrumColorChange); this.spectrum.rgb = this._colorToRgba(color); this.spectrum.on("changed", this._onSpectrumColorChange); this.spectrum.updateUI(); } - let {target} = this.inspector.toolbox; + let {target} = this.inspector; target.actorHasMethod("inspector", "pickColorFromPage").then(value => { let tooltipDoc = this.tooltip.doc; let eyeButton = tooltipDoc.querySelector("#eyedropper-button"); if (value && this.inspector.selection.nodeFront.isInHTMLDocument) { eyeButton.addEventListener("click", this._openEyeDropper); } else { eyeButton.style.display = "none"; }
--- a/devtools/client/themes/inspector.css +++ b/devtools/client/themes/inspector.css @@ -6,16 +6,24 @@ :root { --eyedropper-image: url(images/command-eyedropper.svg); } .theme-firebug { --eyedropper-image: url(images/firebug/command-eyedropper.svg); } +:root.theme-light { + --breadcrumbs-border-color: #f3f3f3; +} + +:root.theme-dark { + --breadcrumbs-border-color: #454d5d; +} + /* Make sure to hide scroll bars for the parent window */ window { overflow: hidden; } /* The main Inspector panel container. */ .inspector-responsive-container { height: 100vh; @@ -112,16 +120,20 @@ window { #inspector-sidebar-toggle-box { line-height: initial; } #inspector-breadcrumbs-toolbar { padding: 0px; border-bottom-width: 0px; border-top-width: 1px; + border-top-color: var(--breadcrumbs-border-color); + /* Bug 1262668 - Use the same background as the body so the breadcrumbs toolbar doesn't + get mistaken as a splitter */ + background-color: var(--theme-body-background); display: block; position: relative; } #inspector-breadcrumbs-toolbar, #inspector-breadcrumbs-toolbar * { box-sizing: border-box; }
--- a/devtools/client/themes/widgets.css +++ b/devtools/client/themes/widgets.css @@ -209,17 +209,17 @@ #breadcrumb-separator-before, #breadcrumb-separator-after:after { background: var(--theme-selection-background); } #breadcrumb-separator-after, #breadcrumb-separator-before:after { - background: var(--theme-toolbar-background); + background: var(--theme-body-background); } /* This chevron arrow cannot be replicated easily in CSS, so we are using * a background image for it (still keeping it in a separate element so * we can handle RTL support with a CSS transform). */ #breadcrumb-separator-normal { background: url(images/breadcrumbs-divider@2x.png) no-repeat center right;