зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1202458 - part1: inline text nodes in markupview only if they are short enough;r=pbro
The markup view will now inline a textnode in its container if and only if: - the text node is the only child (pseudo elements included) - the text node length is smaller than a predefined limit If a container is expanded, its text nodes will now always be rendered in full, no longer as a short version with an ellipsis. When selecting or navigating on a textnode, the layout will no longer be modified on the fly. MozReview-Commit-ID: HcDMqjbOesN --HG-- extra : rebase_source : 908ba5c1cab86018116271402c90992c1fca6d62 extra : histedit_source : f0083b1e4c54cde0fdce12e1d139baa41e7d6cda
This commit is contained in:
Родитель
b33a96b184
Коммит
fc46d5012b
|
@ -162,9 +162,9 @@ Selection.prototype = {
|
|||
setNodeFront: function (value, reason = "unknown") {
|
||||
this.reason = reason;
|
||||
|
||||
// If a singleTextChild text node is being set, then set it's parent instead.
|
||||
// If an inlineTextChild text node is being set, then set it's parent instead.
|
||||
let parentNode = value && value.parentNode();
|
||||
if (value && parentNode && parentNode.singleTextChild === value) {
|
||||
if (value && parentNode && parentNode.inlineTextChild === value) {
|
||||
value = parentNode;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,8 +44,6 @@ const EventEmitter = require("devtools/shared/event-emitter");
|
|||
const Heritage = require("sdk/core/heritage");
|
||||
const {parseAttribute} =
|
||||
require("devtools/client/shared/node-attribute-parser");
|
||||
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis",
|
||||
Ci.nsIPrefLocalizedString).data;
|
||||
const {Task} = require("devtools/shared/task");
|
||||
const {scrollIntoViewIfNeeded} = require("devtools/shared/layout/utils");
|
||||
const {PrefObserver} = require("devtools/client/styleeditor/utils");
|
||||
|
@ -989,6 +987,10 @@ MarkupView.prototype = {
|
|||
// accessibility where necessary.
|
||||
this._updateChildren(container, {flash: true}).then(() =>
|
||||
container.updateLevel());
|
||||
} else if (type === "inlineTextChild") {
|
||||
container.childrenDirty = true;
|
||||
this._updateChildren(container, {flash: true});
|
||||
container.update();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1549,19 +1551,19 @@ MarkupView.prototype = {
|
|||
return promise.resolve(container);
|
||||
}
|
||||
|
||||
if (container.singleTextChild
|
||||
&& container.singleTextChild != container.node.singleTextChild) {
|
||||
if (container.inlineTextChild
|
||||
&& container.inlineTextChild != container.node.inlineTextChild) {
|
||||
// This container was doing double duty as a container for a single
|
||||
// text child, back that out.
|
||||
this._containers.delete(container.singleTextChild);
|
||||
container.clearSingleTextChild();
|
||||
this._containers.delete(container.inlineTextChild);
|
||||
container.clearInlineTextChild();
|
||||
|
||||
if (container.hasChildren && container.selected) {
|
||||
container.setExpanded(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (container.node.singleTextChild) {
|
||||
if (container.node.inlineTextChild) {
|
||||
container.setExpanded(false);
|
||||
// this container will do double duty as the container for the single
|
||||
// text child.
|
||||
|
@ -1569,9 +1571,9 @@ MarkupView.prototype = {
|
|||
container.children.removeChild(container.children.firstChild);
|
||||
}
|
||||
|
||||
container.setSingleTextChild(container.node.singleTextChild);
|
||||
container.setInlineTextChild(container.node.inlineTextChild);
|
||||
|
||||
this._containers.set(container.node.singleTextChild, container);
|
||||
this._containers.set(container.node.inlineTextChild, container);
|
||||
container.childrenDirty = false;
|
||||
return promise.resolve(container);
|
||||
}
|
||||
|
@ -2008,7 +2010,7 @@ MarkupContainer.prototype = {
|
|||
* True if the current node can be expanded.
|
||||
*/
|
||||
get canExpand() {
|
||||
return this._hasChildren && !this.node.singleTextChild;
|
||||
return this._hasChildren && !this.node.inlineTextChild;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2717,13 +2719,13 @@ MarkupElementContainer.prototype = Heritage.extend(MarkupContainer.prototype, {
|
|||
});
|
||||
},
|
||||
|
||||
setSingleTextChild: function (singleTextChild) {
|
||||
this.singleTextChild = singleTextChild;
|
||||
setInlineTextChild: function (inlineTextChild) {
|
||||
this.inlineTextChild = inlineTextChild;
|
||||
this.editor.updateTextEditor();
|
||||
},
|
||||
|
||||
clearSingleTextChild: function () {
|
||||
this.singleTextChild = undefined;
|
||||
clearInlineTextChild: function () {
|
||||
this.inlineTextChild = undefined;
|
||||
this.editor.updateTextEditor();
|
||||
},
|
||||
|
||||
|
@ -2905,25 +2907,14 @@ TextEditor.prototype = {
|
|||
},
|
||||
|
||||
update: function () {
|
||||
if (!this.selected || !this.node.incompleteValue) {
|
||||
let text = this.node.shortValue;
|
||||
if (this.node.incompleteValue) {
|
||||
text += ELLIPSIS;
|
||||
}
|
||||
this.value.textContent = text;
|
||||
} else {
|
||||
let longstr = null;
|
||||
this.node.getNodeValue().then(ret => {
|
||||
longstr = ret;
|
||||
return longstr.string();
|
||||
}).then(str => {
|
||||
longstr.release().then(null, console.error);
|
||||
if (this.selected) {
|
||||
this.value.textContent = str;
|
||||
this.markup.emit("text-expand");
|
||||
}
|
||||
}).then(null, console.error);
|
||||
}
|
||||
let longstr = null;
|
||||
this.node.getNodeValue().then(ret => {
|
||||
longstr = ret;
|
||||
return longstr.string();
|
||||
}).then(str => {
|
||||
longstr.release().then(null, console.error);
|
||||
this.value.textContent = str;
|
||||
}).then(null, console.error);
|
||||
},
|
||||
|
||||
destroy: function () {},
|
||||
|
@ -3114,7 +3105,7 @@ ElementEditor.prototype = {
|
|||
* Update the inline text editor in case of a single text child node.
|
||||
*/
|
||||
updateTextEditor: function () {
|
||||
let node = this.node.singleTextChild;
|
||||
let node = this.node.inlineTextChild;
|
||||
|
||||
if (this.textEditor && this.textEditor.node != node) {
|
||||
this.elt.removeChild(this.textEditor.elt);
|
||||
|
|
|
@ -132,6 +132,7 @@ skip-if = e10s # Bug 1036409 - The last selected node isn't reselected
|
|||
[browser_markup_tag_edit_11.js]
|
||||
[browser_markup_tag_edit_12.js]
|
||||
[browser_markup_tag_edit_13-other.js]
|
||||
[browser_markup_textcontent_display.js]
|
||||
[browser_markup_textcontent_edit_01.js]
|
||||
[browser_markup_textcontent_edit_02.js]
|
||||
[browser_markup_toggle_01.js]
|
||||
|
|
|
@ -89,7 +89,7 @@ const TEST_DATA = [
|
|||
},
|
||||
check: function* (inspector) {
|
||||
let container = yield getContainerForSelector("#node1", inspector);
|
||||
ok(container.singleTextChild, "Has single text child.");
|
||||
ok(container.inlineTextChild, "Has single text child.");
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -99,9 +99,9 @@ const TEST_DATA = [
|
|||
},
|
||||
check: function* (inspector) {
|
||||
let container = yield getContainerForSelector("#node1", inspector);
|
||||
ok(container.singleTextChild, "Has single text child.");
|
||||
ok(!container.canExpand, "Can't expand container with singleTextChild.");
|
||||
ok(!container.singleTextChild.canExpand, "Can't expand singleTextChild.");
|
||||
ok(container.inlineTextChild, "Has single text child.");
|
||||
ok(!container.canExpand, "Can't expand container with inlineTextChild.");
|
||||
ok(!container.inlineTextChild.canExpand, "Can't expand inlineTextChild.");
|
||||
is(container.editor.elt.querySelector(".text").textContent.trim(),
|
||||
"newtext", "Single text child editor updated.");
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ const TEST_DATA = [
|
|||
},
|
||||
check: function* (inspector) {
|
||||
let container = yield getContainerForSelector("#node1", inspector);
|
||||
ok(!container.singleTextChild, "Does not have single text child.");
|
||||
ok(!container.inlineTextChild, "Does not have single text child.");
|
||||
ok(container.canExpand, "Can expand container with child nodes.");
|
||||
ok(container.editor.elt.querySelector(".text") == null,
|
||||
"Single text child editor removed.");
|
||||
|
@ -130,9 +130,9 @@ const TEST_DATA = [
|
|||
},
|
||||
check: function* (inspector) {
|
||||
let container = yield getContainerForSelector("#node1", inspector);
|
||||
ok(container.singleTextChild, "Has single text child.");
|
||||
ok(!container.canExpand, "Can't expand container with singleTextChild.");
|
||||
ok(!container.singleTextChild.canExpand, "Can't expand singleTextChild.");
|
||||
ok(container.inlineTextChild, "Has single text child.");
|
||||
ok(!container.canExpand, "Can't expand container with inlineTextChild.");
|
||||
ok(!container.inlineTextChild.canExpand, "Can't expand inlineTextChild.");
|
||||
ok(container.editor.elt.querySelector(".text").textContent.trim(),
|
||||
"newtext", "Single text child editor updated.");
|
||||
},
|
||||
|
@ -144,7 +144,7 @@ const TEST_DATA = [
|
|||
},
|
||||
check: function* (inspector) {
|
||||
let container = yield getContainerForSelector("#node1", inspector);
|
||||
ok(!container.singleTextChild, "Does not have single text child.");
|
||||
ok(!container.inlineTextChild, "Does not have single text child.");
|
||||
ok(!container.canExpand, "Can't expand empty container.");
|
||||
ok(container.editor.elt.querySelector(".text") == null,
|
||||
"Single text child editor removed.");
|
||||
|
@ -157,9 +157,9 @@ const TEST_DATA = [
|
|||
},
|
||||
check: function* (inspector) {
|
||||
let container = yield getContainerForSelector("#node1", inspector);
|
||||
ok(container.singleTextChild, "Has single text child.");
|
||||
ok(!container.canExpand, "Can't expand container with singleTextChild.");
|
||||
ok(!container.singleTextChild.canExpand, "Can't expand singleTextChild.");
|
||||
ok(container.inlineTextChild, "Has single text child.");
|
||||
ok(!container.canExpand, "Can't expand container with inlineTextChild.");
|
||||
ok(!container.inlineTextChild.canExpand, "Can't expand inlineTextChild.");
|
||||
ok(container.editor.elt.querySelector(".text").textContent.trim(),
|
||||
"newtext", "Single text child editor updated.");
|
||||
},
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/* 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 the rendering of text nodes in the markup view.
|
||||
|
||||
const LONG_VALUE = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do " +
|
||||
"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.";
|
||||
const SCHEMA = "data:text/html;charset=UTF-8,";
|
||||
const TEST_URL = `${SCHEMA}<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div id="shorttext">Short text</div>
|
||||
<div id="longtext">${LONG_VALUE}</div>
|
||||
<div id="shortcomment"><!--Short comment--></div>
|
||||
<div id="longcomment"><!--${LONG_VALUE}--></div>
|
||||
<div id="shorttext-and-node">Short text<span>Other element</span></div>
|
||||
<div id="longtext-and-node">${LONG_VALUE}<span>Other element</span></div>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
const TEST_DATA = [{
|
||||
desc: "Test node containing a short text, short text nodes can be inlined.",
|
||||
selector: "#shorttext",
|
||||
inline: true,
|
||||
value: "Short text",
|
||||
}, {
|
||||
desc: "Test node containing a long text, long text nodes are not inlined.",
|
||||
selector: "#longtext",
|
||||
inline: false,
|
||||
value: LONG_VALUE,
|
||||
}, {
|
||||
desc: "Test node containing a short comment, comments are not inlined.",
|
||||
selector: "#shortcomment",
|
||||
inline: false,
|
||||
value: "Short comment",
|
||||
}, {
|
||||
desc: "Test node containing a long comment, comments are not inlined.",
|
||||
selector: "#longcomment",
|
||||
inline: false,
|
||||
value: LONG_VALUE,
|
||||
}, {
|
||||
desc: "Test node containing a short text and a span.",
|
||||
selector: "#shorttext-and-node",
|
||||
inline: false,
|
||||
value: "Short text",
|
||||
}, {
|
||||
desc: "Test node containing a long text and a span.",
|
||||
selector: "#longtext-and-node",
|
||||
inline: false,
|
||||
value: LONG_VALUE,
|
||||
}, ];
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
for (let data of TEST_DATA) {
|
||||
yield checkNode(inspector, testActor, data);
|
||||
}
|
||||
});
|
||||
|
||||
function* checkNode(inspector, testActor, {desc, selector, inline, value}) {
|
||||
info(desc);
|
||||
|
||||
let container = yield getContainerForSelector(selector, inspector);
|
||||
let nodeValue = yield getFirstChildNodeValue(selector, testActor);
|
||||
is(nodeValue, value, "The test node's text content is correct");
|
||||
|
||||
is(!!container.inlineTextChild, inline, "Container inlineTextChild is as expected");
|
||||
is(!container.canExpand, inline, "Container canExpand property is as expected");
|
||||
|
||||
let textContainer;
|
||||
if (inline) {
|
||||
textContainer = container.elt.querySelector("pre");
|
||||
ok(!!textContainer, "Text container is already rendered for inline text elements");
|
||||
} else {
|
||||
textContainer = container.elt.querySelector("pre");
|
||||
ok(!textContainer, "Text container is not rendered for collapsed text nodes");
|
||||
yield inspector.markup.expandNode(container.node);
|
||||
yield waitForMultipleChildrenUpdates(inspector);
|
||||
|
||||
textContainer = container.elt.querySelector("pre");
|
||||
ok(!!textContainer, "Text container is rendered after expanding the container");
|
||||
}
|
||||
|
||||
is(textContainer.textContent, value, "The complete text node is rendered.");
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
// Test editing a node's text content
|
||||
|
||||
const TEST_URL = URL_ROOT + "doc_markup_edit.html";
|
||||
const {DEFAULT_VALUE_SUMMARY_LENGTH} = require("devtools/server/actors/inspector");
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
@ -26,55 +27,38 @@ add_task(function* () {
|
|||
newValue: "LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT. " +
|
||||
"DONEC POSUERE PLACERAT MAGNA ET IMPERDIET.",
|
||||
oldValue: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
|
||||
"Donec posuere placerat magna et imperdiet.",
|
||||
shortValue: true
|
||||
"Donec posuere placerat magna et imperdiet."
|
||||
});
|
||||
|
||||
yield editContainer(inspector, testActor, {
|
||||
selector: "#node17",
|
||||
newValue: "New value",
|
||||
oldValue: "LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT. " +
|
||||
"DONEC POSUERE PLACERAT MAGNA ET IMPERDIET."
|
||||
});
|
||||
|
||||
yield editContainer(inspector, testActor, {
|
||||
selector: "#node17",
|
||||
newValue: "LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT. " +
|
||||
"DONEC POSUERE PLACERAT MAGNA ET IMPERDIET.",
|
||||
shortValue: true
|
||||
oldValue: "New value"
|
||||
});
|
||||
});
|
||||
|
||||
function* getNodeValue(selector, testActor) {
|
||||
let nodeValue = yield testActor.eval(`
|
||||
content.document.querySelector("${selector}").firstChild.nodeValue;
|
||||
`);
|
||||
return nodeValue;
|
||||
}
|
||||
|
||||
function* editContainer(inspector, testActor,
|
||||
{selector, newValue, oldValue, shortValue}) {
|
||||
let nodeValue = yield getNodeValue(selector, testActor);
|
||||
{selector, newValue, oldValue}) {
|
||||
let nodeValue = yield getFirstChildNodeValue(selector, testActor);
|
||||
is(nodeValue, oldValue, "The test node's text content is correct");
|
||||
|
||||
info("Changing the text content");
|
||||
let onMutated = inspector.once("markupmutation");
|
||||
let container = yield focusNode(selector, inspector);
|
||||
|
||||
let isOldValueInline = oldValue.length <= DEFAULT_VALUE_SUMMARY_LENGTH;
|
||||
is(!!container.inlineTextChild, isOldValueInline, "inlineTextChild is as expected");
|
||||
is(!container.canExpand, isOldValueInline, "canExpand property is as expected");
|
||||
|
||||
let field = container.elt.querySelector("pre");
|
||||
|
||||
if (shortValue) {
|
||||
is(oldValue.indexOf(
|
||||
field.textContent.substring(0, field.textContent.length - 1)),
|
||||
0,
|
||||
"The shortened value starts with the full value " + field.textContent);
|
||||
ok(oldValue.length > field.textContent.length,
|
||||
"The shortened value is short");
|
||||
} else {
|
||||
is(field.textContent, oldValue,
|
||||
"The text node has the correct original value");
|
||||
}
|
||||
|
||||
inspector.markup.markNodeAsSelected(container.node);
|
||||
|
||||
if (shortValue) {
|
||||
info("Waiting for the text to be updated");
|
||||
yield inspector.markup.once("text-expand");
|
||||
}
|
||||
|
||||
is(field.textContent, oldValue,
|
||||
"The text node has the correct original value after selecting");
|
||||
setEditableFieldValue(field, newValue, inspector);
|
||||
|
@ -82,9 +66,18 @@ function* editContainer(inspector, testActor,
|
|||
info("Listening to the markupmutation event");
|
||||
yield onMutated;
|
||||
|
||||
nodeValue = yield getNodeValue(selector, testActor);
|
||||
nodeValue = yield getFirstChildNodeValue(selector, testActor);
|
||||
is(nodeValue, newValue, "The test node's text content has changed");
|
||||
|
||||
let isNewValueInline = newValue.length <= DEFAULT_VALUE_SUMMARY_LENGTH;
|
||||
is(!!container.inlineTextChild, isNewValueInline, "inlineTextChild is as expected");
|
||||
is(!container.canExpand, isNewValueInline, "canExpand property is as expected");
|
||||
|
||||
if (isOldValueInline != isNewValueInline) {
|
||||
is(container.expanded, !isNewValueInline,
|
||||
"Container was automatically expanded/collapsed");
|
||||
}
|
||||
|
||||
info("Selecting the <body> to reset the selection");
|
||||
let bodyContainer = yield getContainerForSelector("body", inspector);
|
||||
inspector.markup.markNodeAsSelected(bodyContainer.node);
|
||||
|
|
|
@ -17,7 +17,7 @@ add_task(function* () {
|
|||
yield inspector.markup.expandAll();
|
||||
yield waitForMultipleChildrenUpdates(inspector);
|
||||
|
||||
let nodeValue = yield getNodeValue(SELECTOR, testActor);
|
||||
let nodeValue = yield getFirstChildNodeValue(SELECTOR, testActor);
|
||||
let expectedValue = "line6";
|
||||
is(nodeValue, expectedValue, "The test node's text content is correct");
|
||||
|
||||
|
@ -84,17 +84,10 @@ add_task(function* () {
|
|||
yield sendKey("VK_RETURN", {}, editor, inspector.panelWin);
|
||||
yield onMutated;
|
||||
|
||||
nodeValue = yield getNodeValue(SELECTOR, testActor);
|
||||
nodeValue = yield getFirstChildNodeValue(SELECTOR, testActor);
|
||||
is(nodeValue, expectedValue, "The test node's text content is correct");
|
||||
});
|
||||
|
||||
function* getNodeValue(selector, testActor) {
|
||||
let nodeValue = yield testActor.eval(`
|
||||
content.document.querySelector("${selector}").firstChild.nodeValue;
|
||||
`);
|
||||
return nodeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the editor selection is at the expected positions.
|
||||
*/
|
||||
|
|
|
@ -89,6 +89,20 @@ var getContainerForSelector = Task.async(function* (selector, inspector) {
|
|||
return container;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve the nodeValue for the firstChild of a provided selector on the content page.
|
||||
*
|
||||
* @param {String} selector
|
||||
* @param {TestActorFront} testActor The current TestActorFront instance.
|
||||
* @return {String} the nodeValue of the first
|
||||
*/
|
||||
function* getFirstChildNodeValue(selector, testActor) {
|
||||
let nodeValue = yield testActor.eval(`
|
||||
content.document.querySelector("${selector}").firstChild.nodeValue;
|
||||
`);
|
||||
return nodeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the markupview's _waitForChildren function, wait for all queued
|
||||
* children updates to be handled.
|
||||
|
|
|
@ -248,7 +248,7 @@ var NodeActor = exports.NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
|
|||
}
|
||||
|
||||
let parentNode = this.walker.parentNode(this);
|
||||
let singleTextChild = this.walker.singleTextChild(this);
|
||||
let inlineTextChild = this.walker.inlineTextChild(this);
|
||||
|
||||
let form = {
|
||||
actor: this.actorID,
|
||||
|
@ -257,9 +257,10 @@ var NodeActor = exports.NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
|
|||
nodeType: this.rawNode.nodeType,
|
||||
namespaceURI: this.rawNode.namespaceURI,
|
||||
nodeName: this.rawNode.nodeName,
|
||||
nodeValue: this.rawNode.nodeValue,
|
||||
displayName: getNodeDisplayName(this.rawNode),
|
||||
numChildren: this.numChildren,
|
||||
singleTextChild: singleTextChild ? singleTextChild.form() : undefined,
|
||||
inlineTextChild: inlineTextChild ? inlineTextChild.form() : undefined,
|
||||
|
||||
// doctype attributes
|
||||
name: this.rawNode.name,
|
||||
|
@ -285,18 +286,6 @@ var NodeActor = exports.NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
|
|||
form.isDocumentElement = true;
|
||||
}
|
||||
|
||||
if (this.rawNode.nodeValue) {
|
||||
// We only include a short version of the value if it's longer than
|
||||
// gValueSummaryLength
|
||||
if (this.rawNode.nodeValue.length > gValueSummaryLength) {
|
||||
form.shortValue = this.rawNode.nodeValue
|
||||
.substring(0, gValueSummaryLength);
|
||||
form.incompleteValue = true;
|
||||
} else {
|
||||
form.shortValue = this.rawNode.nodeValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add an extra API for custom properties added by other
|
||||
// modules/extensions.
|
||||
form.setFormProperty = (name, value) => {
|
||||
|
@ -330,6 +319,7 @@ var NodeActor = exports.NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
|
|||
nativeAnonymousChildList: true,
|
||||
attributes: true,
|
||||
characterData: true,
|
||||
characterDataOldValue: true,
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
|
@ -1147,12 +1137,12 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
|||
},
|
||||
|
||||
/**
|
||||
* If the given NodeActor only has a single text node as a child,
|
||||
* return that child's NodeActor.
|
||||
* If the given NodeActor only has a single text node as a child with a text
|
||||
* content small enough to be inlined, return that child's NodeActor.
|
||||
*
|
||||
* @param NodeActor node
|
||||
*/
|
||||
singleTextChild: function (node) {
|
||||
inlineTextChild: function (node) {
|
||||
// Quick checks to prevent creating a new walker if possible.
|
||||
if (node.isBeforePseudoElement ||
|
||||
node.isAfterPseudoElement ||
|
||||
|
@ -1164,11 +1154,15 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
|||
let docWalker = this.getDocumentWalker(node.rawNode);
|
||||
let firstChild = docWalker.firstChild();
|
||||
|
||||
// If the first child isn't a text node, or there are multiple children
|
||||
// then bail out
|
||||
// Bail out if:
|
||||
// - more than one child
|
||||
// - unique child is not a text node
|
||||
// - unique child is a text node, but is too long to be inlined
|
||||
if (!firstChild ||
|
||||
docWalker.nextSibling() ||
|
||||
firstChild.nodeType !== Ci.nsIDOMNode.TEXT_NODE ||
|
||||
docWalker.nextSibling()) {
|
||||
firstChild.nodeValue.length > gValueSummaryLength
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -2200,8 +2194,7 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
|||
* newValue: <string> - The new value of the attribute, if any.
|
||||
*
|
||||
* `characterData` type:
|
||||
* newValue: <string> - the new shortValue for the node
|
||||
* [incompleteValue: true] - True if the shortValue was truncated.
|
||||
* newValue: <string> - the new nodeValue for the node
|
||||
*
|
||||
* `childList` type is returned when the set of children for a node
|
||||
* has changed. Includes extra data, which can be used by the client to
|
||||
|
@ -2211,7 +2204,7 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
|||
* seen by the client* that were added to the target node.
|
||||
* removed: array of <domnode actor ID> The list of actors *previously
|
||||
* seen by the client* that were removed from the target node.
|
||||
* singleTextChild: If the node now has a single text child, it will
|
||||
* inlineTextChild: If the node now has a single text child, it will
|
||||
* be sent here.
|
||||
*
|
||||
* Actors that are included in a MutationRecord's `removed` but
|
||||
|
@ -2288,13 +2281,8 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
|||
targetNode.getAttribute(mutation.attributeName)
|
||||
: null;
|
||||
} else if (type === "characterData") {
|
||||
if (targetNode.nodeValue.length > gValueSummaryLength) {
|
||||
mutation.newValue = targetNode.nodeValue
|
||||
.substring(0, gValueSummaryLength);
|
||||
mutation.incompleteValue = true;
|
||||
} else {
|
||||
mutation.newValue = targetNode.nodeValue;
|
||||
}
|
||||
mutation.newValue = targetNode.nodeValue;
|
||||
this._maybeQueueInlineTextChildMutation(change, targetNode);
|
||||
} else if (type === "childList" || type === "nativeAnonymousChildList") {
|
||||
// Get the list of removed and added actors that the client has seen
|
||||
// so that it can keep its ownership tree up to date.
|
||||
|
@ -2330,15 +2318,50 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
|||
mutation.removed = removedActors;
|
||||
mutation.added = addedActors;
|
||||
|
||||
let singleTextChild = this.singleTextChild(targetActor);
|
||||
if (singleTextChild) {
|
||||
mutation.singleTextChild = singleTextChild.form();
|
||||
let inlineTextChild = this.inlineTextChild(targetActor);
|
||||
if (inlineTextChild) {
|
||||
mutation.inlineTextChild = inlineTextChild.form();
|
||||
}
|
||||
}
|
||||
this.queueMutation(mutation);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the provided mutation could change the way the target element is
|
||||
* inlined with its parent node. If it might, a custom mutation of type
|
||||
* "inlineTextChild" will be queued.
|
||||
*
|
||||
* @param {MutationRecord} mutation
|
||||
* A characterData type mutation
|
||||
*/
|
||||
_maybeQueueInlineTextChildMutation: function (mutation) {
|
||||
let {oldValue, target} = mutation;
|
||||
let newValue = target.nodeValue;
|
||||
let limit = gValueSummaryLength;
|
||||
|
||||
if ((oldValue.length <= limit && newValue.length <= limit) ||
|
||||
(oldValue.length > limit && newValue.length > limit)) {
|
||||
// Bail out if the new & old values are both below/above the size limit.
|
||||
return;
|
||||
}
|
||||
|
||||
let parentActor = this.getNode(target.parentNode);
|
||||
if (!parentActor || parentActor.rawNode.children.length > 0) {
|
||||
// If the parent node has other children, a character data mutation will
|
||||
// not change anything regarding inlining text nodes.
|
||||
return;
|
||||
}
|
||||
|
||||
let inlineTextChild = this.inlineTextChild(parentActor);
|
||||
this.queueMutation({
|
||||
type: "inlineTextChild",
|
||||
target: parentActor.actorID,
|
||||
inlineTextChild:
|
||||
inlineTextChild ? inlineTextChild.form() : undefined
|
||||
});
|
||||
},
|
||||
|
||||
onFrameLoad: function ({ window, isTopLevel }) {
|
||||
if (!this.rootDoc && isTopLevel) {
|
||||
this.rootDoc = window.document;
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const Services = require("Services");
|
||||
const { Ci } = require("chrome");
|
||||
require("devtools/shared/fronts/styles");
|
||||
require("devtools/shared/fronts/highlighters");
|
||||
|
@ -15,7 +14,6 @@ const {
|
|||
preEvent,
|
||||
types
|
||||
} = require("devtools/shared/protocol.js");
|
||||
const { makeInfallible } = require("devtools/shared/DevToolsUtils");
|
||||
const {
|
||||
inspectorSpec,
|
||||
nodeSpec,
|
||||
|
@ -71,15 +69,6 @@ const AttributeModificationList = Class({
|
|||
}
|
||||
});
|
||||
|
||||
// A resolve that hits the main loop first.
|
||||
function delayedResolve(value) {
|
||||
let deferred = promise.defer();
|
||||
Services.tm.mainThread.dispatch(makeInfallible(() => {
|
||||
deferred.resolve(value);
|
||||
}), 0);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client side of the node actor.
|
||||
*
|
||||
|
@ -122,6 +111,14 @@ const NodeFront = FrontClassWithSpec(nodeSpec, {
|
|||
this.actorID = form;
|
||||
return;
|
||||
}
|
||||
|
||||
// backward-compatibility: shortValue indicates we are connected to old server
|
||||
if (form.shortValue) {
|
||||
// If the value is not complete, set nodeValue to null, it will be fetched
|
||||
// when calling getNodeValue()
|
||||
form.nodeValue = form.incompleteValue ? null : form.shortValue;
|
||||
}
|
||||
|
||||
// Shallow copy of the form. We could just store a reference, but
|
||||
// eventually we'll want to update some of the data.
|
||||
this._form = object.merge(form);
|
||||
|
@ -135,11 +132,11 @@ const NodeFront = FrontClassWithSpec(nodeSpec, {
|
|||
this.reparent(parentNodeFront);
|
||||
}
|
||||
|
||||
if (form.singleTextChild) {
|
||||
this.singleTextChild =
|
||||
types.getType("domnode").read(form.singleTextChild, ctx);
|
||||
if (form.inlineTextChild) {
|
||||
this.inlineTextChild =
|
||||
types.getType("domnode").read(form.inlineTextChild, ctx);
|
||||
} else {
|
||||
this.singleTextChild = undefined;
|
||||
this.inlineTextChild = undefined;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -186,8 +183,7 @@ const NodeFront = FrontClassWithSpec(nodeSpec, {
|
|||
});
|
||||
}
|
||||
} else if (change.type === "characterData") {
|
||||
this._form.shortValue = change.newValue;
|
||||
this._form.incompleteValue = change.incompleteValue;
|
||||
this._form.nodeValue = change.newValue;
|
||||
} else if (change.type === "pseudoClassLock") {
|
||||
this._form.pseudoClassLocks = change.pseudoClassLocks;
|
||||
} else if (change.type === "events") {
|
||||
|
@ -259,12 +255,6 @@ const NodeFront = FrontClassWithSpec(nodeSpec, {
|
|||
get tagName() {
|
||||
return this.nodeType === Ci.nsIDOMNode.ELEMENT_NODE ? this.nodeName : null;
|
||||
},
|
||||
get shortValue() {
|
||||
return this._form.shortValue;
|
||||
},
|
||||
get incompleteValue() {
|
||||
return !!this._form.incompleteValue;
|
||||
},
|
||||
|
||||
get isDocumentElement() {
|
||||
return !!this._form.isDocumentElement;
|
||||
|
@ -324,11 +314,14 @@ const NodeFront = FrontClassWithSpec(nodeSpec, {
|
|||
},
|
||||
|
||||
getNodeValue: custom(function () {
|
||||
if (!this.incompleteValue) {
|
||||
return delayedResolve(new ShortLongString(this.shortValue));
|
||||
// backward-compatibility: if nodevalue is null and shortValue is defined, the actual
|
||||
// value of the node needs to be fetched on the server.
|
||||
if (this._form.nodeValue === null && this._form.shortValue) {
|
||||
return this._getNodeValue();
|
||||
}
|
||||
|
||||
return this._getNodeValue();
|
||||
let str = this._form.nodeValue || "";
|
||||
return promise.resolve(new ShortLongString(str));
|
||||
}, {
|
||||
impl: "_getNodeValue"
|
||||
}),
|
||||
|
@ -806,13 +799,6 @@ const WalkerFront = FrontClassWithSpec(walkerSpec, {
|
|||
addedFronts.push(addedFront);
|
||||
}
|
||||
|
||||
if (change.singleTextChild) {
|
||||
targetFront.singleTextChild =
|
||||
types.getType("domnode").read(change.singleTextChild, this);
|
||||
} else {
|
||||
targetFront.singleTextChild = undefined;
|
||||
}
|
||||
|
||||
// Before passing to users, replace the added and removed actor
|
||||
// ids with front in the mutation record.
|
||||
emittedMutation.added = addedFronts;
|
||||
|
@ -858,6 +844,19 @@ const WalkerFront = FrontClassWithSpec(walkerSpec, {
|
|||
targetFront.updateMutation(change);
|
||||
}
|
||||
|
||||
// Update the inlineTextChild property of the target for a selected list of
|
||||
// mutation types.
|
||||
if (change.type === "inlineTextChild" ||
|
||||
change.type === "childList" ||
|
||||
change.type === "nativeAnonymousChildList") {
|
||||
if (change.inlineTextChild) {
|
||||
targetFront.inlineTextChild =
|
||||
types.getType("domnode").read(change.inlineTextChild, this);
|
||||
} else {
|
||||
targetFront.inlineTextChild = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
emitMutations.push(emittedMutation);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче