diff --git a/devtools/client/dom/content/components/search-box.css b/devtools/client/dom/content/components/search-box.css
index f857fe5db3a8..168b5bbd0a07 100644
--- a/devtools/client/dom/content/components/search-box.css
+++ b/devtools/client/dom/content/components/search-box.css
@@ -7,4 +7,8 @@
/* Search Box */
.dom-searchbox {
float: right;
-}
\ No newline at end of file
+}
+
+.dom-searchbox:dir(rtl) {
+ float: left;
+}
diff --git a/devtools/client/dom/content/dom-view.css b/devtools/client/dom/content/dom-view.css
index c1293e934d7d..2d404325d6c2 100644
--- a/devtools/client/dom/content/dom-view.css
+++ b/devtools/client/dom/content/dom-view.css
@@ -32,7 +32,12 @@ body {
/* Space for read only properties icon */
.treeTable td.treeValueCell {
- padding-left: 16px;
+ padding-inline-start: 16px;
+}
+
+.treeTable .treeLabel,
+.treeTable td.treeValueCell .objectBox {
+ direction: ltr; /* Don't change the direction of english labels */
}
/* Read only properties have a padlock icon */
@@ -42,6 +47,10 @@ body {
background-size: 10px 10px;
}
+.treeTable tr:not(.writable) td.treeValueCell:dir(rtl) {
+ background-position-x: right 1px;
+}
+
/* Non-enumerable properties are grayed out */
.treeTable tr:not(.enumerable) td.treeValueCell {
opacity: 0.7;
@@ -87,14 +96,6 @@ body {
font-weight: bold;
}
-/******************************************************************************/
-/* Selection */
-
-.treeTable .treeRow:hover a,
-.treeTable .treeRow:hover span {
- color: var(--theme-selection-color) !important;
-}
-
/******************************************************************************/
/* Theme Dark */
diff --git a/devtools/client/dom/dom.html b/devtools/client/dom/dom.html
index b1a654f28b2b..62172590d2a9 100644
--- a/devtools/client/dom/dom.html
+++ b/devtools/client/dom/dom.html
@@ -3,7 +3,7 @@
- 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/. -->
-
+
diff --git a/devtools/client/inspector/markup/test/browser.ini b/devtools/client/inspector/markup/test/browser.ini
index d7be4448331e..3116e4beb173 100644
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -30,6 +30,7 @@ support-files =
doc_markup_tooltip.png
doc_markup_void_elements.html
doc_markup_void_elements.xhtml
+ doc_markup_whitespace.html
doc_markup_xul.xul
head.js
helper_attributes_test_runner.js
@@ -151,3 +152,4 @@ skip-if = e10s # Bug 1036409 - The last selected node isn't reselected
[browser_markup_update-on-navigtion.js]
[browser_markup_void_elements_html.js]
[browser_markup_void_elements_xhtml.js]
+[browser_markup_whitespace.js]
diff --git a/devtools/client/inspector/markup/test/browser_markup_navigation.js b/devtools/client/inspector/markup/test/browser_markup_navigation.js
index a64a448566b2..5bfd9719fbee 100644
--- a/devtools/client/inspector/markup/test/browser_markup_navigation.js
+++ b/devtools/client/inspector/markup/test/browser_markup_navigation.js
@@ -22,7 +22,9 @@ const TEST_DATA = [
["right", "node4"],
["down", "*text*"],
["down", "node5"],
+ ["down", "*text*"],
["down", "node6"],
+ ["down", "*text*"],
["down", "*comment*"],
["down", "node7"],
["right", "node7"],
@@ -33,9 +35,13 @@ const TEST_DATA = [
["right", "node7"],
["right", "*text*"],
["down", "node8"],
+ ["down", "*text*"],
["down", "node9"],
+ ["down", "*text*"],
["down", "node10"],
+ ["down", "*text*"],
["down", "node11"],
+ ["down", "*text*"],
["down", "node12"],
["right", "node12"],
["down", "*text*"],
@@ -53,13 +59,17 @@ const TEST_DATA = [
["home", "*doctype*"],
["pagedown", "*text*"],
["down", "node5"],
+ ["down", "*text*"],
["down", "node6"],
+ ["down", "*text*"],
["down", "*comment*"],
["down", "node7"],
["left", "node7"],
+ ["down", "*text*"],
["down", "node9"],
+ ["down", "*text*"],
["down", "node10"],
- ["pageup", "node2"],
+ ["pageup", "*text*"],
["pageup", "*doctype*"],
["down", "html"],
["left", "html"],
diff --git a/devtools/client/inspector/markup/test/browser_markup_whitespace.js b/devtools/client/inspector/markup/test/browser_markup_whitespace.js
new file mode 100644
index 000000000000..6de4e4dd5ac7
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_whitespace.js
@@ -0,0 +1,66 @@
+/* vim: set 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 whitespace text nodes do show up in the markup-view when needed.
+
+const TEST_URL = URL_ROOT + "doc_markup_whitespace.html";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+ let {markup} = inspector;
+
+ yield markup.expandAll();
+
+ info("Verify the number of child nodes and child elements in body");
+
+ // Body has 5 element children, but there are 6 text nodes in there too, they come from
+ // the HTML file formatting (spaces and carriage returns).
+ let {numNodes, numChildren} = yield testActor.getNodeInfo("body");
+ is(numNodes, 11, "The body node has 11 child nodes (includes text nodes)");
+ is(numChildren, 5, "The body node has 5 child elements (only element nodes)");
+
+ // In body, there are only block-level elements, so whitespace text nodes do not have
+ // layout, so they should be skipped in the markup-view.
+ info("Check that the body's whitespace text node children aren't shown");
+ let bodyContainer = markup.getContainer(inspector.selection.nodeFront);
+ let childContainers = bodyContainer.getChildContainers();
+ is(childContainers.length, 5,
+ "Only the element nodes are shown in the markup view");
+
+ // div#inline has 3 element children, but there are 4 text nodes in there too, like in
+ // body, they come from spaces and carriage returns in the HTML file.
+ info("Verify the number of child nodes and child elements in div#inline");
+ ({numNodes, numChildren} = yield testActor.getNodeInfo("#inline"));
+ is(numNodes, 7, "The div#inline node has 7 child nodes (includes text nodes)");
+ is(numChildren, 3, "The div#inline node has 3 child elements (only element nodes)");
+
+ // Within the inline formatting context in div#inline, the whitespace text nodes between
+ // the images have layout, so they should appear in the markup-view.
+ info("Check that the div#inline's whitespace text node children are shown");
+ yield selectNode("#inline", inspector);
+ let divContainer = markup.getContainer(inspector.selection.nodeFront);
+ childContainers = divContainer.getChildContainers();
+ is(childContainers.length, 5,
+ "Both the element nodes and some text nodes are shown in the markup view");
+
+ // div#pre has 2 element children, but there are 3 text nodes in there too, like in
+ // div#inline, they come from spaces and carriage returns in the HTML file.
+ info("Verify the number of child nodes and child elements in div#pre");
+ ({numNodes, numChildren} = yield testActor.getNodeInfo("#pre"));
+ is(numNodes, 5, "The div#pre node has 5 child nodes (includes text nodes)");
+ is(numChildren, 2, "The div#pre node has 2 child elements (only element nodes)");
+
+ // Within the inline formatting context in div#pre, the whitespace text nodes between
+ // the images have layout, so they should appear in the markup-view, but since
+ // white-space is set to pre, then the whitespace text nodes before and after the first
+ // and last image should also appear.
+ info("Check that the div#pre's whitespace text node children are shown");
+ yield selectNode("#pre", inspector);
+ divContainer = markup.getContainer(inspector.selection.nodeFront);
+ childContainers = divContainer.getChildContainers();
+ is(childContainers.length, 5,
+ "Both the element nodes and all text nodes are shown in the markup view");
+});
diff --git a/devtools/client/inspector/markup/test/doc_markup_dragdrop.html b/devtools/client/inspector/markup/test/doc_markup_dragdrop.html
index c6d9556df3ba..f45c26065348 100644
--- a/devtools/client/inspector/markup/test/doc_markup_dragdrop.html
+++ b/devtools/client/inspector/markup/test/doc_markup_dragdrop.html
@@ -16,16 +16,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=858038
Mozilla Bug 858038
-
-
-
- Before
-
- First
- Middle
- Last
-
- After
+ Before
+
FirstMiddleLast
After
diff --git a/devtools/client/inspector/markup/test/doc_markup_whitespace.html b/devtools/client/inspector/markup/test/doc_markup_whitespace.html
new file mode 100644
index 000000000000..9071c802d0a8
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_whitespace.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
div 1
+
div 2
+
div 3
+
+
+
+
+
+
+
+
+
+
+
diff --git a/devtools/client/inspector/markup/views/markup-container.js b/devtools/client/inspector/markup/views/markup-container.js
index e96e4a3eda86..22962e1d2a8d 100644
--- a/devtools/client/inspector/markup/views/markup-container.js
+++ b/devtools/client/inspector/markup/views/markup-container.js
@@ -259,7 +259,8 @@ MarkupContainer.prototype = {
return null;
}
- return [...this.children.children].map(node => node.container);
+ return [...this.children.children].filter(node => node.container)
+ .map(node => node.container);
},
/**
diff --git a/devtools/client/inspector/markup/views/root-container.js b/devtools/client/inspector/markup/views/root-container.js
index 0dceb803c9da..ccc918fca620 100644
--- a/devtools/client/inspector/markup/views/root-container.js
+++ b/devtools/client/inspector/markup/views/root-container.js
@@ -27,11 +27,12 @@ RootContainer.prototype = {
destroy: function () {},
/**
- * If the node has children, return the list of containers for all these
- * children.
+ * If the node has children, return the list of containers for all these children.
+ * @return {Array} An array of child containers or null.
*/
getChildContainers: function () {
- return [...this.children.children].map(node => node.container);
+ return [...this.children.children].filter(node => node.container)
+ .map(node => node.container);
},
/**
diff --git a/devtools/client/inspector/markup/views/text-editor.js b/devtools/client/inspector/markup/views/text-editor.js
index 76a7dd8f5633..d0466fcf0e56 100644
--- a/devtools/client/inspector/markup/views/text-editor.js
+++ b/devtools/client/inspector/markup/views/text-editor.js
@@ -7,6 +7,9 @@
const {getAutocompleteMaxWidth} = require("devtools/client/inspector/markup/utils");
const {editableField} = require("devtools/client/shared/inplace-editor");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
+const {LocalizationHelper} = require("devtools/shared/l10n");
+
+const INSPECTOR_L10N = new LocalizationHelper("devtools/locale/inspector.properties");
/**
* Creates a simple text editor node, used for TEXT and COMMENT
@@ -79,6 +82,16 @@ TextEditor.prototype = {
}).then(str => {
longstr.release().then(null, console.error);
this.value.textContent = str;
+
+ let isWhitespace = !/[^\s]/.exec(str);
+ this.value.classList.toggle("whitespace", isWhitespace);
+
+ let chars = str.replace(/\n/g, "⏎")
+ .replace(/\t/g, "⇥")
+ .replace(/ /g, "◦");
+ this.value.setAttribute("title", isWhitespace
+ ? INSPECTOR_L10N.getFormatStr("markupView.whitespaceOnly", chars)
+ : "");
}).then(null, console.error);
},
diff --git a/devtools/client/locales/en-US/inspector.properties b/devtools/client/locales/en-US/inspector.properties
index 0bb985e2b5a9..c9dde23f9f5e 100644
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -33,6 +33,10 @@ markupView.more.showing=Some nodes were hidden.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
markupView.more.showAll2=Show one more node;Show all #1 nodes
+# LOCALIZATION NOTE (markupView.whitespaceOnly)
+# Used in a tooltip that appears when the user hovers over whitespace-only text nodes in
+# the inspector.
+markupView.whitespaceOnly=Whitespace-only text node: %S
#LOCALIZATION NOTE: Used in the image preview tooltip when the image could not be loaded
previewTooltip.image.brokenImage=Could not load the image
diff --git a/devtools/client/shared/components/reps/function.js b/devtools/client/shared/components/reps/function.js
index 095d8e360d76..fd20dc3189c5 100644
--- a/devtools/client/shared/components/reps/function.js
+++ b/devtools/client/shared/components/reps/function.js
@@ -44,7 +44,9 @@ define(function (require, exports, module) {
let grip = this.props.object;
return (
- span({className: "objectBox objectBox-function"},
+ // Set dir="ltr" to prevent function parentheses from
+ // appearing in the wrong direction
+ span({dir: "ltr", className: "objectBox objectBox-function"},
this.getTitle(grip),
this.summarizeFunction(grip)
)
diff --git a/devtools/client/shared/components/tree/label-cell.js b/devtools/client/shared/components/tree/label-cell.js
index 7963e915fa6b..e14875b4d28a 100644
--- a/devtools/client/shared/components/tree/label-cell.js
+++ b/devtools/client/shared/components/tree/label-cell.js
@@ -33,15 +33,25 @@ define(function (require, exports, module) {
// Compute indentation dynamically. The deeper the item is
// inside the hierarchy, the bigger is the left padding.
let rowStyle = {
- "paddingLeft": (level * 16) + "px",
+ "paddingInlineStart": (level * 16) + "px",
};
+ let iconClassList = ["treeIcon"];
+ if (member.hasChildren && member.loading) {
+ iconClassList.push("devtools-throbber");
+ } else if (member.hasChildren) {
+ iconClassList.push("theme-twisty");
+ }
+ if (member.open) {
+ iconClassList.push("open");
+ }
+
return (
td({
className: "treeLabelCell",
key: "default",
style: rowStyle},
- span({ className: "treeIcon" }),
+ span({ className: iconClassList.join(" ") }),
span({
className: "treeLabel " + member.type + "Label",
"data-level": level
diff --git a/devtools/client/shared/components/tree/tree-view.css b/devtools/client/shared/components/tree/tree-view.css
index f0bc7284389a..850533872ef1 100644
--- a/devtools/client/shared/components/tree/tree-view.css
+++ b/devtools/client/shared/components/tree/tree-view.css
@@ -18,7 +18,7 @@
/* TreeView Table*/
.treeTable .treeLabelCell {
- padding: 2px 0 2px 0px;
+ padding: 2px 0;
vertical-align: top;
white-space: nowrap;
}
@@ -29,20 +29,21 @@
}
.treeTable .treeValueCell {
- padding: 2px 0 2px 5px;
+ padding: 2px 0;
+ padding-inline-start: 5px;
overflow: hidden;
}
.treeTable .treeLabel {
cursor: default;
overflow: hidden;
- padding-left: 4px;
+ padding-inline-start: 4px;
white-space: nowrap;
}
/* No paddding if there is actually no label */
.treeTable .treeLabel:empty {
- padding-left: 0;
+ padding-inline-start: 0;
}
.treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
@@ -60,11 +61,12 @@
/* Toggle Icon */
.treeTable .treeRow .treeIcon {
- height: 12px;
+ height: 14px;
width: 14px;
+ font-size: 10px; /* Set the size of loading spinner */
display: inline-block;
vertical-align: bottom;
- margin-left: 3px;
+ margin-inline-start: 3px;
padding-top: 1px;
}
@@ -75,14 +77,6 @@
background-repeat: no-repeat;
}
-/* Spinner (used for async fetch). Needs to have higher priority than
- theme toggle icons */
-.treeTable .treeRow.hasChildren.loading > .treeLabelCell > .treeIcon {
- background-image: url(chrome://devtools/skin/images/firebug/spinner.png) !important;
- background-position: 2px 1px !important;
- background-size: 9px 9px !important;
-}
-
/******************************************************************************/
/* Header */
@@ -106,7 +100,9 @@
}
.treeTable .treeHeaderCellBox {
- padding: 2px 14px 2px 10px;
+ padding: 2px 0;
+ padding-inline-start: 10px;
+ padding-inline-end: 14px;
}
.treeTable .treeHeaderRow > .treeHeaderCell:first-child > .treeHeaderCellBox {
@@ -137,44 +133,9 @@
/******************************************************************************/
/* Themes */
-/* Light, Firebug Theme: toggle icon */
-.theme-light .treeTable .treeRow.hasChildren > .treeLabelCell > .treeIcon,
-.theme-firebug .treeTable .treeRow.hasChildren > .treeLabelCell > .treeIcon {
- background-image: url(chrome://devtools/skin/images/controls.png);
- background-size: 56px 28px;
- background-position: 0 -14px;
-}
-
-.theme-light .treeTable .treeRow.hasChildren.opened > .treeLabelCell > .treeIcon,
-.theme-firebug .treeTable .treeRow.hasChildren.opened > .treeLabelCell > .treeIcon {
- background-image: url(chrome://devtools/skin/images/controls.png);
- background-size: 56px 28px;
- background-position: -14px -14px;
-}
-
-/* Dark Theme: toggle icon */
-.theme-dark .treeTable .treeRow.hasChildren > .treeLabelCell > .treeIcon {
- background-image: url(chrome://devtools/skin/images/controls.png);
- background-size: 56px 28px;
- background-position: -28px -14px;
-}
-
-.theme-dark .treeTable .treeRow.hasChildren.opened > .treeLabelCell > .treeIcon {
- background-image: url(chrome://devtools/skin/images/controls.png);
- background-size: 56px 28px;
- background-position: -42px -14px;
-}
-
-/* Support for retina displays */
-@media (min-resolution: 1.1dppx) {
- .treeTable .treeRow.hasChildren > .treeLabelCell > .treeIcon {
- background-image: url("chrome://devtools/skin/images/controls@2x.png") !important;
- }
-}
-
.theme-light .treeTable .treeRow:hover,
.theme-dark .treeTable .treeRow:hover {
- background-color: var(--theme-selection-background) !important;
+ background-color: var(--theme-selection-background-semitransparent) !important;
}
.theme-firebug .treeTable .treeRow:hover {
diff --git a/devtools/client/shared/test/test-actor.js b/devtools/client/shared/test/test-actor.js
index 6b57622f0beb..d8e2cf65eb0d 100644
--- a/devtools/client/shared/test/test-actor.js
+++ b/devtools/client/shared/test/test-actor.js
@@ -754,6 +754,7 @@ var TestActor = exports.TestActor = protocol.ActorClassWithSpec(testSpec, {
tagName: node.tagName,
namespaceURI: node.namespaceURI,
numChildren: node.children.length,
+ numNodes: node.childNodes.length,
attributes: [...node.attributes].map(({name, value, namespaceURI}) => {
return {name, value, namespaceURI};
}),
diff --git a/devtools/client/themes/common.css b/devtools/client/themes/common.css
index 6a570ea2d4a3..4c0a5653d734 100644
--- a/devtools/client/themes/common.css
+++ b/devtools/client/themes/common.css
@@ -699,6 +699,66 @@ checkbox:-moz-focusring {
-moz-image-region: rect(0, 32px, 16px, 16px);
}
+/* Twisty and checkbox controls */
+.theme-twisty, .theme-checkbox {
+ width: 14px;
+ height: 14px;
+ background-repeat: no-repeat;
+ background-image: url("chrome://devtools/skin/images/controls.png");
+ background-size: 56px 28px;
+}
+
+.theme-twisty {
+ cursor: pointer;
+ background-position: 0 -14px;
+}
+
+.theme-selected ~ .theme-twisty,
+.theme-dark .theme-twisty {
+ background-position: -28px -14px;
+}
+
+.theme-twisty:-moz-focusring {
+ outline-style: none;
+}
+
+.theme-twisty[open], .theme-twisty.open {
+ background-position: -14px -14px;
+}
+
+.theme-selected ~ .theme-twisty[open],
+.theme-dark .theme-twisty[open], .theme-dark .theme-twisty.open {
+ background-position: -42px -14px;
+}
+
+.theme-twisty[invisible] {
+ visibility: hidden;
+}
+
+/* Mirror the twisty for rtl direction */
+.theme-twisty:dir(rtl),
+.theme-twisty:-moz-locale-dir(rtl) {
+ transform: scaleX(-1);
+}
+
+.theme-checkbox {
+ display: inline-block;
+ border: 0;
+ padding: 0;
+ outline: none;
+ background-position: -28px 0;
+}
+
+.theme-checkbox[checked] {
+ background-position: -42px 0;
+}
+
+@media (min-resolution: 1.1dppx) {
+ .theme-twisty, .theme-checkbox {
+ background-image: url("chrome://devtools/skin/images/controls@2x.png");
+ }
+}
+
/* Throbbers */
.devtools-throbber::before {
content: "";
diff --git a/devtools/client/themes/dark-theme.css b/devtools/client/themes/dark-theme.css
index f9bdc05d1c9f..096b5694f056 100644
--- a/devtools/client/themes/dark-theme.css
+++ b/devtools/client/themes/dark-theme.css
@@ -256,56 +256,6 @@ div.CodeMirror span.eval-text {
min-height: 1.4em;
}
-/* Twisty and checkbox controls */
-.theme-twisty, .theme-checkbox {
- width: 14px;
- height: 14px;
- background-repeat: no-repeat;
- background-image: url("chrome://devtools/skin/images/controls.png");
- background-size: 56px 28px;
-}
-
-.theme-twisty {
- cursor: pointer;
- background-position: -28px -14px;
-}
-
-.theme-twisty:-moz-focusring {
- outline-style: none;
-}
-
-.theme-twisty[open], .theme-twisty.open {
- background-position: -42px -14px;
-}
-
-.theme-twisty[invisible] {
- visibility: hidden;
-}
-
-/* Mirror the twisty for rtl direction */
-.theme-twisty:dir(rtl),
-.theme-twisty:-moz-locale-dir(rtl) {
- transform: scaleX(-1);
-}
-
-.theme-checkbox {
- display: inline-block;
- border: 0;
- padding: 0;
- outline: none;
- background-position: -28px 0;
-}
-
-.theme-checkbox[checked] {
- background-position: -42px 0;
-}
-
-@media (min-resolution: 1.1dppx) {
- .theme-twisty, .theme-checkbox {
- background-image: url("chrome://devtools/skin/images/controls@2x.png");
- }
-}
-
/* XUL panel styling (see devtools/client/shared/widgets/tooltip/Tooltip.js) */
.theme-tooltip-panel .panel-arrowcontent {
diff --git a/devtools/client/themes/light-theme.css b/devtools/client/themes/light-theme.css
index a5f19edd1965..4d32b6ca0f52 100644
--- a/devtools/client/themes/light-theme.css
+++ b/devtools/client/themes/light-theme.css
@@ -258,66 +258,6 @@ div.CodeMirror span.eval-text {
min-height: 1.4em;
}
-/* Twisty and checkbox controls */
-
-.theme-twisty, .theme-checkbox {
- width: 14px;
- height: 14px;
- background-repeat: no-repeat;
- background-image: url("chrome://devtools/skin/images/controls.png");
- background-size: 56px 28px;
-}
-
-.theme-twisty {
- cursor: pointer;
- background-position: 0 -14px;
-}
-
-.theme-twisty:-moz-focusring {
- outline-style: none;
-}
-
-.theme-twisty[open], .theme-twisty.open {
- background-position: -14px -14px;
-}
-
-.theme-twisty[invisible] {
- visibility: hidden;
-}
-
-/* Use white twisty when next to a selected item in markup view */
-.theme-selected ~ .theme-twisty {
- background-position: -28px -14px;
-}
-
-.theme-selected ~ .theme-twisty[open] {
- background-position: -42px -14px;
-}
-
-/* Mirror the twisty for rtl direction */
-.theme-twisty:dir(rtl),
-.theme-twisty:-moz-locale-dir(rtl) {
- transform: scaleX(-1);
-}
-
-.theme-checkbox {
- display: inline-block;
- border: 0;
- padding: 0;
- outline: none;
- background-position: 0 0;
-}
-
-.theme-checkbox[checked] {
- background-position: -14px 0;
-}
-
-@media (min-resolution: 1.1dppx) {
- .theme-twisty, .theme-checkbox {
- background-image: url("chrome://devtools/skin/images/controls@2x.png");
- }
-}
-
/* XUL panel styling (see devtools/client/shared/widgets/tooltip/Tooltip.js) */
.theme-tooltip-panel .panel-arrowcontent {
diff --git a/devtools/client/themes/markup.css b/devtools/client/themes/markup.css
index c8a00ed73717..4b4cfd031f33 100644
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -247,6 +247,25 @@ ul.children + .tag-line::before {
font: inherit;
}
+/* Whitespace only text nodes are sometimes shown in the markup-view, and when they do
+ they get a greyed-out whitespace symbol so users know what they are */
+.editor.text .whitespace {
+ padding: 0 .5em;
+}
+
+.editor.text .whitespace::before {
+ content: "";
+ display: inline-block;
+ height: 4px;
+ width: 4px;
+ border: 1px solid var(--theme-body-color-inactive);
+ border-radius: 50%;
+}
+
+.tag-line[selected] .editor.text .whitespace::before {
+ border-color: white;
+}
+
.more-nodes {
padding-left: 16px;
}
diff --git a/devtools/client/webide/test/test_build.html b/devtools/client/webide/test/test_build.html
index b5acb58c67bc..ffb01998c6d0 100644
--- a/devtools/client/webide/test/test_build.html
+++ b/devtools/client/webide/test/test_build.html
@@ -40,8 +40,11 @@
let packagedAppLocation = getTestFilePath("build_app" + testSuffix + "1");
+ let onValidated = waitForUpdate(win, "project-validated");
+ let onDetails = waitForUpdate(win, "details");
yield winProject.projectList.importPackagedApp(packagedAppLocation);
- yield waitForUpdate(win, "details");
+ yield onValidated;
+ yield onDetails;
let project = win.AppManager.selectedProject;
@@ -77,8 +80,11 @@
// # Now test a full featured package.json
packagedAppLocation = getTestFilePath("build_app" + testSuffix + "2");
+ onValidated = waitForUpdate(win, "project-validated");
+ onDetails = waitForUpdate(win, "details");
yield winProject.projectList.importPackagedApp(packagedAppLocation);
- yield waitForUpdate(win, "project-validated");
+ yield onValidated;
+ yield onDetails;
project = win.AppManager.selectedProject;
@@ -96,8 +102,11 @@
is(loggedMessages[3], "succeed", "log messages are correct");
// Switch to the package dir in order to verify the generated webapp.manifest
+ onValidated = waitForUpdate(win, "project-validated");
+ onDetails = waitForUpdate(win, "details");
yield winProject.projectList.importPackagedApp(packageDir);
- yield waitForUpdate(win, "project-validated");
+ yield onValidated;
+ yield onDetails;
project = win.AppManager.selectedProject;
diff --git a/devtools/client/webide/test/test_duplicate_import.html b/devtools/client/webide/test/test_duplicate_import.html
index 456778aa78cc..ef01e23e448d 100644
--- a/devtools/client/webide/test/test_duplicate_import.html
+++ b/devtools/client/webide/test/test_duplicate_import.html
@@ -27,19 +27,21 @@
yield win.AppProjects.load();
is(win.AppProjects.projects.length, 0, "IDB is empty");
- info("to call importPackagedApp(" + packagedAppLocation + ")");
+ let onValidated = waitForUpdate(win, "project-validated");
+ let onDetails = waitForUpdate(win, "details");
yield winProject.projectList.importPackagedApp(packagedAppLocation);
- yield waitForUpdate(win, "project-validated");
- yield nextTick();
+ yield onValidated;
+ yield onDetails;
- info("to call importHostedApp(" + hostedAppManifest + ")");
yield winProject.projectList.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
yield nextTick();
- info("to call importPackagedApp(" + packagedAppLocation + ") again");
+ onValidated = waitForUpdate(win, "project-validated");
+ onDetails = waitForUpdate(win, "details");
yield winProject.projectList.importPackagedApp(packagedAppLocation);
- yield waitForUpdate(win, "project-validated");
+ yield onValidated;
+ yield onDetails;
let project = win.AppManager.selectedProject;
is(project.location, packagedAppLocation, "Correctly reselected existing packaged app.");
diff --git a/devtools/client/webide/test/test_runtime.html b/devtools/client/webide/test/test_runtime.html
index ccde517be3fe..496e8c8b5b96 100644
--- a/devtools/client/webide/test/test_runtime.html
+++ b/devtools/client/webide/test/test_runtime.html
@@ -90,8 +90,11 @@
let packagedAppLocation = getTestFilePath("app");
+ let onValidated = waitForUpdate(win, "project-validated");
+ let onDetails = waitForUpdate(win, "details");
yield winProject.projectList.importPackagedApp(packagedAppLocation);
- yield waitForUpdate(win, "project-validated");
+ yield onValidated;
+ yield onDetails;
let panelNode = docRuntime.querySelector("#runtime-panel");
let items = panelNode.querySelectorAll(".runtime-panel-item-usb");
diff --git a/devtools/client/webide/test/test_telemetry.html b/devtools/client/webide/test/test_telemetry.html
index 89a9761e6965..225ddb89b370 100644
--- a/devtools/client/webide/test/test_telemetry.html
+++ b/devtools/client/webide/test/test_telemetry.html
@@ -120,8 +120,11 @@
return Task.spawn(function*() {
let packagedAppLocation = getTestFilePath("../app");
let winProject = getProjectWindow(win);
+ let onValidated = waitForUpdate(win, "project-validated");
+ let onDetails = waitForUpdate(win, "details");
yield winProject.projectList.importPackagedApp(packagedAppLocation);
- yield waitForUpdate(win, "project-validated");
+ yield onValidated;
+ yield onDetails;
});
}
diff --git a/devtools/server/actors/inspector.js b/devtools/server/actors/inspector.js
index bf953134c02f..8e3631cd1a09 100644
--- a/devtools/server/actors/inspector.js
+++ b/devtools/server/actors/inspector.js
@@ -2958,10 +2958,11 @@ function standardTreeWalkerFilter(node) {
return nodeFilterConstants.FILTER_ACCEPT;
}
- // Ignore empty whitespace text nodes.
- if (node.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
- !/[^\s]/.exec(node.nodeValue)) {
- return nodeFilterConstants.FILTER_SKIP;
+ // Ignore empty whitespace text nodes that do not impact the layout.
+ if (isWhitespaceTextNode(node)) {
+ return nodeHasSize(node)
+ ? nodeFilterConstants.FILTER_ACCEPT
+ : nodeFilterConstants.FILTER_SKIP;
}
// Ignore all native and XBL anonymous content inside a non-XUL document
@@ -2982,14 +2983,38 @@ function standardTreeWalkerFilter(node) {
* it also includes all anonymous content (like internal form controls).
*/
function allAnonymousContentTreeWalkerFilter(node) {
- // Ignore empty whitespace text nodes.
- if (node.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
- !/[^\s]/.exec(node.nodeValue)) {
- return nodeFilterConstants.FILTER_SKIP;
+ // Ignore empty whitespace text nodes that do not impact the layout.
+ if (isWhitespaceTextNode(node)) {
+ return nodeHasSize(node)
+ ? nodeFilterConstants.FILTER_ACCEPT
+ : nodeFilterConstants.FILTER_SKIP;
}
return nodeFilterConstants.FILTER_ACCEPT;
}
+/**
+ * Is the given node a text node composed of whitespace only?
+ * @param {DOMNode} node
+ * @return {Boolean}
+ */
+function isWhitespaceTextNode(node) {
+ return node.nodeType == Ci.nsIDOMNode.TEXT_NODE && !/[^\s]/.exec(node.nodeValue);
+}
+
+/**
+ * Does the given node have non-0 width and height?
+ * @param {DOMNode} node
+ * @return {Boolean}
+ */
+function nodeHasSize(node) {
+ if (!node.getBoxQuads) {
+ return false;
+ }
+
+ let quads = node.getBoxQuads();
+ return quads.length && quads.some(quad => quad.bounds.width && quad.bounds.height);
+}
+
/**
* Returns a promise that is settled once the given HTMLImageElement has
* finished loading.