Backed out changeset f3bdf3ebaf4c (bug 1202458) for test failures in test_inspector-mutations-value.html - missed that changeset in the backout before, sorry
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 02 Jun 2016 16:32:05 +0200
changeset 341101 c26aac8cf086fae326a34b4ff809dbfc054bec7e
parent 341100 e75f2d129de069681d0a690915c3e9c8f2a671e4
child 341102 a47a4d3877eb3a732d7578d77ade1142b1b941e7
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1202458
milestone49.0a1
backs outf3bdf3ebaf4cbf3bb426d10cf95e702bc8e981f4
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset f3bdf3ebaf4c (bug 1202458) for test failures in test_inspector-mutations-value.html - missed that changeset in the backout before, sorry
devtools/client/framework/selection.js
devtools/client/inspector/markup/markup.js
devtools/client/inspector/markup/test/browser.ini
devtools/client/inspector/markup/test/browser_markup_mutation_01.js
devtools/client/inspector/markup/test/browser_markup_textcontent_display.js
devtools/client/inspector/markup/test/browser_markup_textcontent_edit_01.js
devtools/client/inspector/markup/test/browser_markup_textcontent_edit_02.js
devtools/client/inspector/markup/test/head.js
devtools/server/actors/inspector.js
devtools/shared/fronts/inspector.js
--- a/devtools/client/framework/selection.js
+++ b/devtools/client/framework/selection.js
@@ -157,19 +157,19 @@ Selection.prototype = {
       return this.node.ownerDocument;
     }
     return null;
   },
 
   setNodeFront: function (value, reason = "unknown") {
     this.reason = reason;
 
-    // If an inlineTextChild text node is being set, then set it's parent instead.
+    // If a singleTextChild text node is being set, then set it's parent instead.
     let parentNode = value && value.parentNode();
-    if (value && parentNode && parentNode.inlineTextChild === value) {
+    if (value && parentNode && parentNode.singleTextChild === value) {
       value = parentNode;
     }
 
     // We used to return here if the node had not changed but we now need to
     // set the node even if it is already set otherwise it is not possible to
     // e.g. highlight the same node twice.
     let rawValue = null;
     if (value && value.isLocalToBeDeprecated()) {
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -39,16 +39,18 @@ const {Tooltip} = require("devtools/clie
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 const {setImageTooltip, setBrokenImageTooltip} =
       require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
 
 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");
 const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
 const {template} = require("devtools/shared/gcli/templater");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
@@ -982,20 +984,16 @@ MarkupView.prototype = {
         container.update();
       } else if (type === "childList" || type === "nativeAnonymousChildList") {
         container.childrenDirty = true;
         // Update the children to take care of changes in the markup view DOM
         // and update container (and its subtree) DOM tree depth level for
         // 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();
       }
     }
 
     this._waitForChildren().then(() => {
       if (this._destroyer) {
         console.warn("Could not fully update after markup mutations, " +
           "the markup-view was destroyed while waiting for children.");
         return;
@@ -1546,39 +1544,39 @@ MarkupView.prototype = {
     if (this._queuedChildUpdates.has(container)) {
       return this._queuedChildUpdates.get(container);
     }
 
     if (!container.childrenDirty) {
       return promise.resolve(container);
     }
 
-    if (container.inlineTextChild
-        && container.inlineTextChild != container.node.inlineTextChild) {
+    if (container.singleTextChild
+        && container.singleTextChild != container.node.singleTextChild) {
       // This container was doing double duty as a container for a single
       // text child, back that out.
-      this._containers.delete(container.inlineTextChild);
-      container.clearInlineTextChild();
+      this._containers.delete(container.singleTextChild);
+      container.clearSingleTextChild();
 
       if (container.hasChildren && container.selected) {
         container.setExpanded(true);
       }
     }
 
-    if (container.node.inlineTextChild) {
+    if (container.node.singleTextChild) {
       container.setExpanded(false);
       // this container will do double duty as the container for the single
       // text child.
       while (container.children.firstChild) {
         container.children.removeChild(container.children.firstChild);
       }
 
-      container.setInlineTextChild(container.node.inlineTextChild);
-
-      this._containers.set(container.node.inlineTextChild, container);
+      container.setSingleTextChild(container.node.singleTextChild);
+
+      this._containers.set(container.node.singleTextChild, container);
       container.childrenDirty = false;
       return promise.resolve(container);
     }
 
     if (!container.hasChildren) {
       while (container.children.firstChild) {
         container.children.removeChild(container.children.firstChild);
       }
@@ -2005,17 +2003,17 @@ MarkupContainer.prototype = {
       doc.activeElement.blur();
     }
   },
 
   /**
    * True if the current node can be expanded.
    */
   get canExpand() {
-    return this._hasChildren && !this.node.inlineTextChild;
+    return this._hasChildren && !this.node.singleTextChild;
   },
 
   /**
    * True if this is the root <html> element and can't be collapsed.
    */
   get mustExpand() {
     return this.node._parent === this.markup.walker.rootNode;
   },
@@ -2714,23 +2712,23 @@ MarkupElementContainer.prototype = Herit
     // for the tooltip, because we want the full-size image
     this.node.getImageData().then(data => {
       data.data.string().then(str => {
         clipboardHelper.copyString(str);
       });
     });
   },
 
-  setInlineTextChild: function (inlineTextChild) {
-    this.inlineTextChild = inlineTextChild;
+  setSingleTextChild: function (singleTextChild) {
+    this.singleTextChild = singleTextChild;
     this.editor.updateTextEditor();
   },
 
-  clearInlineTextChild: function () {
-    this.inlineTextChild = undefined;
+  clearSingleTextChild: function () {
+    this.singleTextChild = undefined;
     this.editor.updateTextEditor();
   },
 
   /**
    * Trigger new attribute field for input.
    */
   addAttribute: function () {
     this.editor.newAttr.editMode();
@@ -2902,24 +2900,35 @@ TextEditor.prototype = {
     if (value === this._selected) {
       return;
     }
     this._selected = value;
     this.update();
   },
 
   update: function () {
-    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);
+    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);
+    }
   },
 
   destroy: function () {},
 
   /**
    * Stub method for consistency with ElementEditor.
    */
   getInfoAtNode: function () {
@@ -3100,17 +3109,17 @@ ElementEditor.prototype = {
 
     this.updateTextEditor();
   },
 
   /**
    * Update the inline text editor in case of a single text child node.
    */
   updateTextEditor: function () {
-    let node = this.node.inlineTextChild;
+    let node = this.node.singleTextChild;
 
     if (this.textEditor && this.textEditor.node != node) {
       this.elt.removeChild(this.textEditor.elt);
       this.textEditor = null;
     }
 
     if (node && !this.textEditor) {
       // Create a text editor added to this editor.
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -127,17 +127,16 @@ skip-if = e10s # Bug 1036409 - The last 
 [browser_markup_tag_edit_06.js]
 [browser_markup_tag_edit_07.js]
 [browser_markup_tag_edit_08.js]
 [browser_markup_tag_edit_09.js]
 [browser_markup_tag_edit_10.js]
 [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]
 [browser_markup_toggle_02.js]
 [browser_markup_toggle_03.js]
 [browser_markup_update-on-navigtion.js]
 [browser_markup_void_elements_html.js]
 [browser_markup_void_elements_xhtml.js]
--- a/devtools/client/inspector/markup/test/browser_markup_mutation_01.js
+++ b/devtools/client/inspector/markup/test/browser_markup_mutation_01.js
@@ -84,87 +84,87 @@ const TEST_DATA = [
     test: function* (testActor) {
       yield testActor.eval(`
         let node1 = content.document.querySelector("#node1");
         node1.classList.remove("pseudo");
       `);
     },
     check: function* (inspector) {
       let container = yield getContainerForSelector("#node1", inspector);
-      ok(container.inlineTextChild, "Has single text child.");
+      ok(container.singleTextChild, "Has single text child.");
     }
   },
   {
     desc: "Updating the text-content",
     test: function* (testActor) {
       yield testActor.setProperty("#node1", "textContent", "newtext");
     },
     check: function* (inspector) {
       let container = yield getContainerForSelector("#node1", inspector);
-      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.singleTextChild, "Has single text child.");
+      ok(!container.canExpand, "Can't expand container with singleTextChild.");
+      ok(!container.singleTextChild.canExpand, "Can't expand singleTextChild.");
       is(container.editor.elt.querySelector(".text").textContent.trim(),
          "newtext", "Single text child editor updated.");
     }
   },
   {
     desc: "Adding a second text child",
     test: function* (testActor) {
       yield testActor.eval(`
         let node1 = content.document.querySelector("#node1");
         let newText = node1.ownerDocument.createTextNode("more");
         node1.appendChild(newText);
       `);
     },
     check: function* (inspector) {
       let container = yield getContainerForSelector("#node1", inspector);
-      ok(!container.inlineTextChild, "Does not have single text child.");
+      ok(!container.singleTextChild, "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.");
     },
   },
   {
     desc: "Go from 2 to 1 text child",
     test: function* (testActor) {
       yield testActor.setProperty("#node1", "textContent", "newtext");
     },
     check: function* (inspector) {
       let container = yield getContainerForSelector("#node1", inspector);
-      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.singleTextChild, "Has single text child.");
+      ok(!container.canExpand, "Can't expand container with singleTextChild.");
+      ok(!container.singleTextChild.canExpand, "Can't expand singleTextChild.");
       ok(container.editor.elt.querySelector(".text").textContent.trim(),
          "newtext", "Single text child editor updated.");
     },
   },
   {
     desc: "Removing an only text child",
     test: function* (testActor) {
       yield testActor.setProperty("#node1", "innerHTML", "");
     },
     check: function* (inspector) {
       let container = yield getContainerForSelector("#node1", inspector);
-      ok(!container.inlineTextChild, "Does not have single text child.");
+      ok(!container.singleTextChild, "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.");
     },
   },
   {
     desc: "Go from 0 to 1 text child",
     test: function* (testActor) {
       yield testActor.setProperty("#node1", "textContent", "newtext");
     },
     check: function* (inspector) {
       let container = yield getContainerForSelector("#node1", inspector);
-      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.singleTextChild, "Has single text child.");
+      ok(!container.canExpand, "Can't expand container with singleTextChild.");
+      ok(!container.singleTextChild.canExpand, "Can't expand singleTextChild.");
       ok(container.editor.elt.querySelector(".text").textContent.trim(),
          "newtext", "Single text child editor updated.");
     },
   },
 
   {
     desc: "Updating the innerHTML",
     test: function* (testActor) {
deleted file mode 100644
--- a/devtools/client/inspector/markup/test/browser_markup_textcontent_display.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/* 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.");
-}
--- a/devtools/client/inspector/markup/test/browser_markup_textcontent_edit_01.js
+++ b/devtools/client/inspector/markup/test/browser_markup_textcontent_edit_01.js
@@ -2,17 +2,16 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // 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);
 
   info("Expanding all nodes");
   yield inspector.markup.expandAll();
   yield waitForMultipleChildrenUpdates(inspector);
 
@@ -22,63 +21,71 @@ add_task(function* () {
     oldValue: "line6"
   });
 
   yield editContainer(inspector, testActor, {
     selector: "#node17",
     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."
+              "Donec posuere placerat magna et imperdiet.",
+    shortValue: true
   });
 
   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.",
-    oldValue: "New value"
+    shortValue: true
   });
 });
 
+function* getNodeValue(selector, testActor) {
+  let nodeValue = yield testActor.eval(`
+    content.document.querySelector("${selector}").firstChild.nodeValue;
+  `);
+  return nodeValue;
+}
+
 function* editContainer(inspector, testActor,
-                        {selector, newValue, oldValue}) {
-  let nodeValue = yield getFirstChildNodeValue(selector, testActor);
+                        {selector, newValue, oldValue, shortValue}) {
+  let nodeValue = yield getNodeValue(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 field = container.elt.querySelector("pre");
 
-  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");
+  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");
+  }
 
-  let field = container.elt.querySelector("pre");
+  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);
 
   info("Listening to the markupmutation event");
   yield onMutated;
 
-  nodeValue = yield getFirstChildNodeValue(selector, testActor);
+  nodeValue = yield getNodeValue(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);
 }
--- a/devtools/client/inspector/markup/test/browser_markup_textcontent_edit_02.js
+++ b/devtools/client/inspector/markup/test/browser_markup_textcontent_edit_02.js
@@ -12,17 +12,17 @@ const SELECTOR = ".node6";
 
 add_task(function* () {
   let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
 
   info("Expanding all nodes");
   yield inspector.markup.expandAll();
   yield waitForMultipleChildrenUpdates(inspector);
 
-  let nodeValue = yield getFirstChildNodeValue(SELECTOR, testActor);
+  let nodeValue = yield getNodeValue(SELECTOR, testActor);
   let expectedValue = "line6";
   is(nodeValue, expectedValue, "The test node's text content is correct");
 
   info("Open editable field for .node6");
   let container = yield focusNode(SELECTOR, inspector);
   let field = container.elt.querySelector("pre");
   field.focus();
   EventUtils.sendKey("return", inspector.panelWin);
@@ -79,20 +79,27 @@ add_task(function* () {
   info("Caret should be back on the first line");
   checkSelectionPositions(editor, 1, 1);
 
   info("Commit the new value with RETURN, wait for the markupmutation event");
   let onMutated = inspector.once("markupmutation");
   yield sendKey("VK_RETURN", {}, editor, inspector.panelWin);
   yield onMutated;
 
-  nodeValue = yield getFirstChildNodeValue(SELECTOR, testActor);
+  nodeValue = yield getNodeValue(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.
  */
 function checkSelectionPositions(editor, expectedStart, expectedEnd) {
   is(editor.input.selectionStart, expectedStart,
     "Selection should start at " + expectedStart);
   is(editor.input.selectionEnd, expectedEnd,
     "Selection should end at " + expectedEnd);
--- a/devtools/client/inspector/markup/test/head.js
+++ b/devtools/client/inspector/markup/test/head.js
@@ -85,30 +85,16 @@ var getContainerForSelector = Task.async
   info("Getting the markup-container for node " + selector);
   let nodeFront = yield getNodeFront(selector, inspector);
   let container = getContainerForNodeFront(nodeFront, inspector);
   info("Found markup-container " + container);
   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.
  * @param {InspectorPanel} inspector The instance of InspectorPanel currently
  * loaded in the toolbox
  * @return a promise that resolves when all queued children updates have been
  * handled
  */
 function waitForChildrenUpdated({markup}) {
--- a/devtools/server/actors/inspector.js
+++ b/devtools/server/actors/inspector.js
@@ -243,29 +243,28 @@ var NodeActor = exports.NodeActor = prot
 
   // Returns the JSON representation of this object over the wire.
   form: function (detail) {
     if (detail === "actorid") {
       return this.actorID;
     }
 
     let parentNode = this.walker.parentNode(this);
-    let inlineTextChild = this.walker.inlineTextChild(this);
+    let singleTextChild = this.walker.singleTextChild(this);
 
     let form = {
       actor: this.actorID,
       baseURI: this.rawNode.baseURI,
       parent: parentNode ? parentNode.actorID : undefined,
       nodeType: this.rawNode.nodeType,
       namespaceURI: this.rawNode.namespaceURI,
       nodeName: this.rawNode.nodeName,
-      nodeValue: this.rawNode.nodeValue,
       displayName: getNodeDisplayName(this.rawNode),
       numChildren: this.numChildren,
-      inlineTextChild: inlineTextChild ? inlineTextChild.form() : undefined,
+      singleTextChild: singleTextChild ? singleTextChild.form() : undefined,
 
       // doctype attributes
       name: this.rawNode.name,
       publicId: this.rawNode.publicId,
       systemId: this.rawNode.systemId,
 
       attrs: this.writeAttrs(),
       isBeforePseudoElement: this.isBeforePseudoElement,
@@ -281,16 +280,28 @@ var NodeActor = exports.NodeActor = prot
         this.rawNode.ownerDocument.contentType === "text/html",
       hasEventListeners: this._hasEventListeners,
     };
 
     if (this.isDocumentElement()) {
       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) => {
       if (!form.props) {
         form.props = {};
       }
       form.props[name] = value;
     };
@@ -314,17 +325,16 @@ var NodeActor = exports.NodeActor = prot
     // Create the observer on the node's actor.  The node will make sure
     // the observer is cleaned up when the actor is released.
     let observer = new node.defaultView.MutationObserver(callback);
     observer.mergeAttributeRecords = true;
     observer.observe(node, {
       nativeAnonymousChildList: true,
       attributes: true,
       characterData: true,
-      characterDataOldValue: true,
       childList: true,
       subtree: true
     });
     this.mutationObserver = observer;
   },
 
   get isBeforePseudoElement() {
     return this.rawNode.nodeName === "_moz_generated_content_before";
@@ -1132,42 +1142,38 @@ var WalkerActor = protocol.ActorClassWit
     let parent = walker.parentNode();
     if (parent) {
       return this._ref(parent);
     }
     return null;
   },
 
   /**
-   * 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.
+   * If the given NodeActor only has a single text node as a child,
+   * return that child's NodeActor.
    *
    * @param NodeActor node
    */
-  inlineTextChild: function (node) {
+  singleTextChild: function (node) {
     // Quick checks to prevent creating a new walker if possible.
     if (node.isBeforePseudoElement ||
         node.isAfterPseudoElement ||
         node.rawNode.nodeType != Ci.nsIDOMNode.ELEMENT_NODE ||
         node.rawNode.children.length > 0) {
       return undefined;
     }
 
     let docWalker = this.getDocumentWalker(node.rawNode);
     let firstChild = docWalker.firstChild();
 
-    // 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 the first child isn't a text node, or there are multiple children
+    // then bail out
     if (!firstChild ||
-        docWalker.nextSibling() ||
         firstChild.nodeType !== Ci.nsIDOMNode.TEXT_NODE ||
-        firstChild.nodeValue.length > gValueSummaryLength
-        ) {
+        docWalker.nextSibling()) {
       return undefined;
     }
 
     return this._ref(firstChild);
   },
 
   /**
    * Mark a node as 'retained'.
@@ -2189,27 +2195,28 @@ var WalkerActor = protocol.ActorClassWit
    * And additional attributes based on the mutation type:
    *
    * `attributes` type:
    *   attributeName: <string> - the attribute that changed
    *   attributeNamespace: <string> - the attribute's namespace URI, if any.
    *   newValue: <string> - The new value of the attribute, if any.
    *
    * `characterData` type:
-   *   newValue: <string> - the new nodeValue for the node
+   *   newValue: <string> - the new shortValue for the node
+   *   [incompleteValue: true] - True if the shortValue was truncated.
    *
    * `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
    * maintain its ownership subtree.
    *
    *   added: array of <domnode actor ID> - The list of actors *previously
    *     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.
-   *   inlineTextChild: If the node now has a single text child, it will
+   *   singleTextChild: If the node now has a single text child, it will
    *     be sent here.
    *
    * Actors that are included in a MutationRecord's `removed` but
    * not in an `added` have been removed from the client's ownership
    * tree (either by being moved under a node the client has seen yet
    * or by being removed from the tree entirely), and is considered
    * 'orphaned'.
    *
@@ -2276,18 +2283,23 @@ var WalkerActor = protocol.ActorClassWit
 
       if (type === "attributes") {
         mutation.attributeName = change.attributeName;
         mutation.attributeNamespace = change.attributeNamespace || undefined;
         mutation.newValue = targetNode.hasAttribute(mutation.attributeName) ?
                             targetNode.getAttribute(mutation.attributeName)
                             : null;
       } else if (type === "characterData") {
-        mutation.newValue = targetNode.nodeValue;
-        this._maybeQueueInlineTextChildMutation(change, targetNode);
+        if (targetNode.nodeValue.length > gValueSummaryLength) {
+          mutation.newValue = targetNode.nodeValue
+            .substring(0, gValueSummaryLength);
+          mutation.incompleteValue = true;
+        } else {
+          mutation.newValue = targetNode.nodeValue;
+        }
       } 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.
         let removedActors = [];
         let addedActors = [];
         for (let removed of change.removedNodes) {
           let removedActor = this.getNode(removed);
           if (!removedActor) {
@@ -2313,60 +2325,25 @@ var WalkerActor = protocol.ActorClassWit
           this._orphaned.delete(addedActor);
           addedActors.push(addedActor.actorID);
         }
 
         mutation.numChildren = targetActor.numChildren;
         mutation.removed = removedActors;
         mutation.added = addedActors;
 
-        let inlineTextChild = this.inlineTextChild(targetActor);
-        if (inlineTextChild) {
-          mutation.inlineTextChild = inlineTextChild.form();
+        let singleTextChild = this.singleTextChild(targetActor);
+        if (singleTextChild) {
+          mutation.singleTextChild = singleTextChild.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;
       this.rootNode = this.document();
       this.queueMutation({
         type: "newRoot",
         target: this.rootNode.form()
       });
--- a/devtools/shared/fronts/inspector.js
+++ b/devtools/shared/fronts/inspector.js
@@ -1,24 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
+const Services = require("Services");
 const { Ci } = require("chrome");
 require("devtools/shared/fronts/styles");
 require("devtools/shared/fronts/highlighters");
 const { ShortLongString } = require("devtools/server/actors/string");
 const {
   Front,
   FrontClassWithSpec,
   custom,
   preEvent,
   types
 } = require("devtools/shared/protocol.js");
+const { makeInfallible } = require("devtools/shared/DevToolsUtils");
 const {
   inspectorSpec,
   nodeSpec,
   nodeListSpec,
   walkerSpec
 } = require("devtools/shared/specs/inspector");
 const promise = require("promise");
 const { Task } = require("devtools/shared/task");
@@ -64,16 +66,25 @@ const AttributeModificationList = Class(
     this.setAttributeNS(ns, name, undefined);
   },
 
   removeAttribute: function (name) {
     this.setAttributeNS(undefined, name, undefined);
   }
 });
 
+// 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.
  *
  * Node fronts are strored in a tree that mirrors the DOM tree on the
  * server, but with a few key differences:
  *  - Not all children will be necessary loaded for each node.
  *  - The order of children isn't guaranteed to be the same as the DOM.
  * Children are stored in a doubly-linked list, to make addition/removal
@@ -106,42 +117,34 @@ const NodeFront = FrontClassWithSpec(nod
   },
 
   // Update the object given a form representation off the wire.
   form: function (form, detail, ctx) {
     if (detail === "actorid") {
       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);
     this._form.attrs = this._form.attrs ? this._form.attrs.slice() : [];
 
     if (form.parent) {
       // Get the owner actor for this actor (the walker), and find the
       // parent node of this actor from it, creating a standin node if
       // necessary.
       let parentNodeFront = ctx.marshallPool().ensureParentFront(form.parent);
       this.reparent(parentNodeFront);
     }
 
-    if (form.inlineTextChild) {
-      this.inlineTextChild =
-        types.getType("domnode").read(form.inlineTextChild, ctx);
+    if (form.singleTextChild) {
+      this.singleTextChild =
+        types.getType("domnode").read(form.singleTextChild, ctx);
     } else {
-      this.inlineTextChild = undefined;
+      this.singleTextChild = undefined;
     }
   },
 
   /**
    * Returns the parent NodeFront for this NodeFront.
    */
   parentNode: function () {
     return this._parent;
@@ -178,17 +181,18 @@ const NodeFront = FrontClassWithSpec(nod
       if (!found && change.newValue !== null) {
         this.attributes.push({
           name: change.attributeName,
           namespace: change.attributeNamespace,
           value: change.newValue
         });
       }
     } else if (change.type === "characterData") {
-      this._form.nodeValue = change.newValue;
+      this._form.shortValue = change.newValue;
+      this._form.incompleteValue = change.incompleteValue;
     } else if (change.type === "pseudoClassLock") {
       this._form.pseudoClassLocks = change.pseudoClassLocks;
     } else if (change.type === "events") {
       this._form.hasEventListeners = change.hasEventListeners;
     }
   },
 
   // Some accessors to make NodeFront feel more like an nsIDOMNode
@@ -250,16 +254,22 @@ const NodeFront = FrontClassWithSpec(nod
     return this._form.isAnonymous;
   },
   get isInHTMLDocument() {
     return this._form.isInHTMLDocument;
   },
   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;
   },
 
   // doctype properties
   get name() {
     return this._form.name;
@@ -309,24 +319,21 @@ const NodeFront = FrontClassWithSpec(nod
         return false;
       }
       parent = parent.parentNode();
     }
     return true;
   },
 
   getNodeValue: custom(function () {
-    // 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();
+    if (!this.incompleteValue) {
+      return delayedResolve(new ShortLongString(this.shortValue));
     }
 
-    let str = this._form.nodeValue || "";
-    return promise.resolve(new ShortLongString(str));
+    return this._getNodeValue();
   }, {
     impl: "_getNodeValue"
   }),
 
   // Accessors for custom form properties.
 
   getFormProperty: function (name) {
     return this._form.props ? this._form.props[name] : null;
@@ -794,16 +801,23 @@ const WalkerFront = FrontClassWithSpec(w
             addedFront.reparent(targetFront);
 
             // The actor is reconnected to the ownership tree, unorphan
             // it.
             this._orphaned.delete(addedFront);
             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;
           emittedMutation.removed = removedFronts;
 
           // If this is coming from a DOM mutation, the actor's numChildren
           // was passed in. Otherwise, it is simulated from a frame load or
           // unload, so don't change the front's form.
@@ -839,29 +853,16 @@ const WalkerFront = FrontClassWithSpec(w
             let releasedFront = this.get(released);
             this._retainedOrphans.delete(released);
             this._releaseFront(releasedFront, true);
           }
         } else {
           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);
       }
 
       if (options.cleanup) {
         for (let node of this._orphaned) {
           // This will move retained nodes to this._retainedOrphans.
           this._releaseFront(node);
         }