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:
Julian Descottes 2016-06-02 10:41:49 +02:00
Родитель b33a96b184
Коммит fc46d5012b
10 изменённых файлов: 259 добавлений и 156 удалений

Просмотреть файл

@ -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);
}