Bug 1242201 - Part 1: Move markup view into the inspector panel frame. r=bgrins
MozReview-Commit-ID: 2BGEN4zlgHq
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -104,17 +104,16 @@ function Inspector(toolbox) {
// telemetry counts in the Grid Inspector are not double counted on reload.
this.previousURL = this.target.url;
this.nodeMenuTriggerInfo = null;
this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
this._onContextMenu = this._onContextMenu.bind(this);
this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
- this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this);
this.onDetached = this.onDetached.bind(this);
this.onMarkupLoaded = this.onMarkupLoaded.bind(this);
this.onNewSelection = this.onNewSelection.bind(this);
this.onNewRoot = this.onNewRoot.bind(this);
this.onPaneToggleButtonClicked = this.onPaneToggleButtonClicked.bind(this);
this.onPanelWindowResize = this.onPanelWindowResize.bind(this);
@@ -267,17 +266,16 @@ Inspector.prototype = {
}
};
this.target.on("thread-paused", this.updateDebuggerPausedWarning);
this.target.on("thread-resumed", this.updateDebuggerPausedWarning);
this._toolbox.on("select", this.updateDebuggerPausedWarning);
this.updateDebuggerPausedWarning();
}
- this._initMarkup();
this.isReady = false;
return new Promise(resolve => {
this.once("markuploaded", () => {
this.isReady = true;
// All the components are initialized. Let's select a node.
if (defaultSelection) {
@@ -287,16 +285,18 @@ Inspector.prototype = {
// And setup the toolbar only now because it may depend on the document.
this.setupToolbar();
this.emit("ready");
resolve(this);
});
+ this._initMarkup();
+
this.setupSearchBox();
this.setupSidebar();
});
},
_onBeforeNavigate: function () {
this._defaultNode = null;
this.selection.setNodeFront(null);
@@ -772,18 +772,18 @@ Inspector.prototype = {
// Cancel this promise resolution as a new one had
// been queued up.
if (this._pendingSelection != onNodeSelected) {
return;
}
this._pendingSelection = null;
this.selection.setNodeFront(defaultNode, "navigateaway");
+ this.once("markuploaded", this.onMarkupLoaded);
this._initMarkup();
- this.once("markuploaded", this.onMarkupLoaded);
// Setup the toolbar again, since its content may depend on the current document.
this.setupToolbar();
};
this._pendingSelection = onNodeSelected;
this._getDefaultNodeForSelection()
.then(onNodeSelected, this._handleRejectionIfNotDestroyed);
},
@@ -1435,70 +1435,30 @@ Inspector.prototype = {
linkFollow.label = INSPECTOR_L10N.getFormatStr(
"inspector.menu.selectElement.label", popupNode.dataset.link);
}
return [linkFollow, linkCopy];
},
_initMarkup: function () {
- let doc = this.panelDoc;
-
- this._markupBox = doc.getElementById("markup-box");
-
- // create tool iframe
- this._markupFrame = doc.createElement("iframe");
- this._markupFrame.setAttribute("flex", "1");
- // This is needed to enable tooltips inside the iframe document.
- this._markupFrame.setAttribute("tooltip", "aHTMLTooltip");
- this._markupFrame.addEventListener("contextmenu", this._onContextMenu);
-
- this._markupBox.setAttribute("collapsed", true);
- this._markupBox.appendChild(this._markupFrame);
-
- this._markupFrame.addEventListener("load", this._onMarkupFrameLoad, true);
- this._markupFrame.setAttribute("src", "markup/markup.xhtml");
- this._markupFrame.setAttribute("aria-label",
- INSPECTOR_L10N.getStr("inspector.panelLabel.markupView"));
- },
-
- _onMarkupFrameLoad: function () {
- this._markupFrame.removeEventListener("load", this._onMarkupFrameLoad, true);
-
- this._markupFrame.contentWindow.focus();
-
- this._markupBox.removeAttribute("collapsed");
-
- this.markup = new MarkupView(this, this._markupFrame, this._toolbox.win);
-
+ this.markup = new MarkupView(this, this.panelDoc, this._toolbox.win);
this.emit("markuploaded");
},
_destroyMarkup: function () {
let destroyPromise;
- if (this._markupFrame) {
- this._markupFrame.removeEventListener("load", this._onMarkupFrameLoad, true);
- this._markupFrame.removeEventListener("contextmenu", this._onContextMenu);
- }
-
if (this.markup) {
destroyPromise = this.markup.destroy();
this.markup = null;
} else {
destroyPromise = promise.resolve();
}
- if (this._markupFrame) {
- this._markupFrame.remove();
- this._markupFrame = null;
- }
-
- this._markupBox = null;
-
return destroyPromise;
},
/**
* When the pane toggle button is clicked or pressed, toggle the pane, change the button
* state and tooltip.
*/
onPaneToggleButtonClicked: function (e) {
--- a/devtools/client/inspector/inspector.xhtml
+++ b/devtools/client/inspector/inspector.xhtml
@@ -18,19 +18,25 @@
<link rel="stylesheet" href="chrome://devtools/skin/animationinspector.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/sidebar-toggle.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabs.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabbar.css"/>
<link rel="stylesheet" href="resource://devtools/client/inspector/components/inspector-tab-panel.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/splitter/split-box.css"/>
<link rel="stylesheet" href="resource://devtools/client/inspector/layout/components/Accordion.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/reps/reps.css"/>
+ <link rel="stylesheet" href="chrome://devtools/skin/markup.css"/>
+ <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css"/>
+ <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css"/>
+ <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/mozilla.css"/>
<script type="application/javascript"
src="chrome://devtools/content/shared/theme-switching.js"></script>
+ <script type="application/javascript"
+ src="chrome://devtools/content/sourceeditor/codemirror/codemirror.bundle.js"></script>
<script type="text/javascript">
/* eslint-disable */
var isInChrome = window.location.href.includes("chrome:");
if (isInChrome) {
var exports = {};
var Cu = Components.utils;
var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
@@ -55,17 +61,21 @@
<input id="inspector-searchbox" class="devtools-searchinput"
type="search"
data-localization="placeholder=inspectorSearchHTML.label3"/>
<button id="inspector-searchinput-clear" class="devtools-searchinput-clear" tabindex="-1"></button>
</div>
<button id="inspector-eyedropper-toggle" class="devtools-button command-button-invertable"></button>
<div id="inspector-sidebar-toggle-box"></div>
</div>
- <div id="markup-box"></div>
+ <div id="markup-container" data-localization="aria-label=inspector.panelLabel.markupView" class="devtools-monospace">
+ <div class="markup-wrapper" role="presentation">
+ <div id="markup-root" class="markup-root" role="presentation"></div>
+ </div>
+ </div>
<div id="inspector-breadcrumbs-toolbar" class="devtools-toolbar">
<div id="inspector-breadcrumbs" class="breadcrumbs-widget-container"
role="group" data-localization="aria-label=inspector.breadcrumbs.label" tabindex="0"></div>
</div>
</div>
<!-- Splitter -->
<div xmlns="http://www.w3.org/1999/xhtml" id="inspector-splitter-box">
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -53,26 +53,28 @@ const ATTR_COLLAPSE_LENGTH_PREF = "devto
*/
/**
* The markup tree. Manages the mapping of nodes to MarkupContainers,
* 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.
+ * @param {Document} document
+ * The document that will contain the markup view.
+ * @param {DOMWindow} controllerWindow
+ * The window object of the toolbox document
*/
-function MarkupView(inspector, frame, controllerWindow) {
+function MarkupView(inspector, document, controllerWindow) {
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.doc = document;
+ this.win = this.doc.defaultView;
+ this._container = this.doc.getElementById("markup-container");
+ this._elt = this.doc.getElementById("markup-root");
this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize",
DEFAULT_MAX_CHILDREN);
this.collapseAttributes =
Services.prefs.getBoolPref(ATTR_COLLAPSE_ENABLED_PREF);
this.collapseAttributeLength =
Services.prefs.getIntPref(ATTR_COLLAPSE_LENGTH_PREF);
@@ -106,22 +108,23 @@ function MarkupView(inspector, frame, co
this._onCollapseAttributesPrefChange.bind(this);
this._isImagePreviewTarget = this._isImagePreviewTarget.bind(this);
this._onBlur = this._onBlur.bind(this);
EventEmitter.decorate(this);
// Listening to various events.
this._elt.addEventListener("click", this._onMouseClick);
+ this._elt.addEventListener("contextmenu", this.inspector._onContextMenu);
this._elt.addEventListener("mousemove", this._onMouseMove);
this._elt.addEventListener("mouseout", this._onMouseOut);
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);
+ this._elt.addEventListener("focus", this._onFocus);
+ this._elt.addEventListener("mouseup", this._onMouseUp);
+ this._elt.addEventListener("copy", this._onCopy);
this.walker.on("mutations", this._mutationObserver);
this.walker.on("display-change", this._onDisplayChange);
this.inspector.selection.on("new-node-front", this._onNewSelection);
this.toolbox.on("picker-canceled", this._onToolboxPickerCanceled);
this.toolbox.on("picker-node-hovered", this._onToolboxPickerHover);
this._onNewSelection();
this._initTooltips();
@@ -308,17 +311,17 @@ MarkupView.prototype = {
loop();
},
_onMouseClick: function (event) {
// From the target passed here, let's find the parent MarkupContainer
// and ask it if the tooltip should be shown
let parentNode = event.target;
let container;
- while (parentNode !== this.doc.body) {
+ while (parentNode !== this._container) {
if (parentNode.container) {
container = parentNode.container;
break;
}
parentNode = parentNode.parentNode;
}
if (container instanceof MarkupElementContainer) {
@@ -507,17 +510,17 @@ MarkupView.prototype = {
_isImagePreviewTarget: Task.async(function* (target) {
// From the target passed here, let's find the parent MarkupContainer
// and ask it if the tooltip should be shown
if (this.isDragging) {
return false;
}
let parent = target, container;
- while (parent !== this.doc.body) {
+ while (parent !== this._container) {
if (parent.container) {
container = parent.container;
break;
}
parent = parent.parentNode;
}
if (container instanceof MarkupElementContainer) {
@@ -662,16 +665,17 @@ MarkupView.prototype = {
},
/**
* Register all key shortcuts.
*/
_initShortcuts: function () {
let shortcuts = new KeyShortcuts({
window: this.win,
+ target: this._elt,
});
this._onShortcut = this._onShortcut.bind(this);
// Process localizable keys
["markupView.hide.key",
"markupView.edit.key",
"markupView.scrollInto.key"].forEach(name => {
@@ -1746,41 +1750,44 @@ MarkupView.prototype = {
this.undo.destroy();
this.undo = null;
this.popup.destroy();
this.popup = null;
this._elt.removeEventListener("click", this._onMouseClick);
+ this._elt.removeEventListener("contextmenu", this.inspector._onContextMenu);
this._elt.removeEventListener("mousemove", this._onMouseMove);
this._elt.removeEventListener("mouseout", this._onMouseOut);
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);
+ this._elt.removeEventListener("focus", this._onFocus);
+ this._elt.removeEventListener("mouseup", this._onMouseUp);
+ this._elt.removeEventListener("copy", this._onCopy);
this.walker.off("mutations", this._mutationObserver);
this.walker.off("display-change", this._onDisplayChange);
this.inspector.selection.off("new-node-front", this._onNewSelection);
- this.toolbox.off("picker-node-hovered",
- this._onToolboxPickerHover);
+ 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();
- this._elt = null;
-
for (let [, container] of this._containers) {
container.destroy();
}
this._containers = null;
+ this._elt.innerHTML = "";
+ this._elt = null;
+
+ this._container = null;
+
this.eventDetailsTooltip.destroy();
this.eventDetailsTooltip = null;
this.imagePreviewTooltip.destroy();
this.imagePreviewTooltip = null;
this.win = null;
this.doc = null;
deleted file mode 100644
--- a/devtools/client/inspector/markup/markup.xhtml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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/. -->
-<!DOCTYPE html>
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
- <link rel="stylesheet" href="chrome://devtools/skin/markup.css" type="text/css"/>
- <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css" type="text/css"/>
- <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css" type="text/css"/>
- <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/mozilla.css" type="text/css"/>
-
- <script type="application/javascript"
- src="chrome://devtools/content/shared/theme-switching.js"></script>
- <script type="application/javascript"
- src="chrome://devtools/content/sourceeditor/codemirror/codemirror.bundle.js"></script>
-</head>
-<body class="theme-body devtools-monospace" role="application">
- <div id="root-wrapper" role="presentation">
- <div id="root" role="presentation"></div>
- </div>
-</body>
-</html>
--- a/devtools/client/inspector/markup/views/html-editor.js
+++ b/devtools/client/inspector/markup/views/html-editor.js
@@ -27,17 +27,17 @@ function HTMLEditor(htmlDocument) {
this.doc = htmlDocument;
this.container = this.doc.createElement("div");
this.container.className = "html-editor theme-body";
this.container.style.display = "none";
this.editorInner = this.doc.createElement("div");
this.editorInner.className = "html-editor-inner";
this.container.appendChild(this.editorInner);
- this.doc.body.appendChild(this.container);
+ this.doc.getElementById("markup-container").appendChild(this.container);
this.hide = this.hide.bind(this);
this.refresh = this.refresh.bind(this);
EventEmitter.decorate(this);
this.doc.defaultView.addEventListener("resize",
this.refresh, true);
--- a/devtools/client/inspector/markup/views/markup-container.js
+++ b/devtools/client/inspector/markup/views/markup-container.js
@@ -42,19 +42,19 @@ MarkupContainer.prototype = {
* @param {String} type
* The type of container to build. This can be either 'textcontainer',
* 'readonlycontainer' or 'elementcontainer'.
*/
initialize: function (markupView, node, type) {
this.markup = markupView;
this.node = node;
this.undo = this.markup.undo;
- this.win = this.markup._frame.contentWindow;
+ this.win = this.markup.win;
this.id = "treeitem-" + markupContainerID++;
- this.htmlElt = this.win.document.documentElement;
+ this.htmlElt = this.markup.doc.getElementById("markup-container");
this.buildMarkup(type);
this.elt.container = this;
this._onMouseDown = this._onMouseDown.bind(this);
this._onToggle = this._onToggle.bind(this);
this._onMouseUp = this._onMouseUp.bind(this);
@@ -191,17 +191,17 @@ MarkupContainer.prototype = {
clearFocus: function () {
if (!this.canFocus) {
return;
}
this.canFocus = false;
let doc = this.markup.doc;
- if (!doc.activeElement || doc.activeElement === doc.body) {
+ if (!doc.activeElement || doc.activeElement === this.htmlElt) {
return;
}
let parent = doc.activeElement;
while (parent && parent !== this.elt) {
parent = parent.parentNode;
}
@@ -377,22 +377,20 @@ MarkupContainer.prototype = {
let rootElt = this.markup.getContainer(this.markup._rootNode).elt;
this._isDragging = isDragging;
this.markup.isDragging = isDragging;
this.tagLine.setAttribute("aria-grabbed", isDragging);
if (isDragging) {
this.htmlElt.classList.add("dragging");
this.elt.classList.add("dragging");
- this.markup.doc.body.classList.add("dragging");
rootElt.setAttribute("aria-dropeffect", "move");
} else {
this.htmlElt.classList.remove("dragging");
this.elt.classList.remove("dragging");
- this.markup.doc.body.classList.remove("dragging");
rootElt.setAttribute("aria-dropeffect", "none");
}
},
get isDragging() {
return this._isDragging;
},
@@ -569,18 +567,18 @@ MarkupContainer.prototype = {
if (this.isDragging) {
let x = 0;
let y = event.pageY - this.win.scrollY;
// Ensure we keep the dragged element within the markup view.
if (y < 0) {
y = 0;
- } else if (y >= this.markup.doc.body.offsetHeight - this.win.scrollY) {
- y = this.markup.doc.body.offsetHeight - this.win.scrollY - 1;
+ } else if (y >= this.htmlElt.offsetHeight - this.win.scrollY) {
+ y = this.htmlElt.offsetHeight - this.win.scrollY - 1;
}
let diff = y - this._dragStartY + this.win.scrollY;
this.elt.style.top = diff + "px";
let el = this.markup.doc.elementFromPoint(x, y);
this.markup.indicateDropTarget(el);
}
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -15,17 +15,16 @@ devtools.jar:
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/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)
content/sourceeditor/codemirror/codemirror.bundle.js (sourceeditor/codemirror/codemirror.bundle.js)
content/sourceeditor/codemirror/lib/codemirror.css (sourceeditor/codemirror/lib/codemirror.css)
--- a/devtools/client/themes/inspector.css
+++ b/devtools/client/themes/inspector.css
@@ -191,26 +191,8 @@ window {
/* "no results" warning message displayed in the ruleview and in the computed view */
#ruleview-no-results,
#computed-no-results {
color: var(--theme-body-color-inactive);
text-align: center;
margin: 5px;
}
-
-/* Markup Box */
-
-iframe {
- border: 0;
-}
-
-#markup-box {
- width: 100%;
- flex: 1;
- min-height: 0;
-}
-
-#markup-box > iframe {
- height: 100%;
- width: 100%;
-}
-
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -1,55 +1,56 @@
/* 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/. */
:root {
--markup-outline: var(--theme-splitter-color);
+ -moz-control-character-visibility: visible;
}
.theme-dark:root {
--markup-outline: var(--theme-highlight-pink);
}
-* {
- padding: 0;
- margin: 0;
-}
-
-:root {
- -moz-control-character-visibility: visible;
-}
-
-body {
- -moz-user-select: none;
+#markup-container {
+ width: 100%;
+ height: 100%;
+ flex: 1;
+ overflow: auto;
}
/* Force height and width (possibly overflowing) from inline elements.
* This allows long overflows of text or input fields to still be styled with
* the container, rather than the background disappearing when scrolling */
-#root {
+#markup-root {
float: left;
min-width: 100%;
}
/* Don't display a parent-child outline for the root elements */
-#root > ul > li > .children {
+#markup-root > ul > li > .children {
background: none;
}
-html.dragging {
+.markup-root * {
+ padding: 0;
+ margin: 0;
+ -moz-user-select: none;
+}
+
+.dragging {
overflow-x: hidden;
}
-body.dragging .tag-line {
+.dragging .tag-line {
cursor: grabbing;
}
-#root-wrapper:after {
+.markup-wrapper:after {
content: "";
display: block;
clear: both;
position:relative;
}
.html-editor {
display: none;