Bug 1153022 - Add trimOutput option to inplace editor;r=pbrosset
authorBrian Grinstead <bgrinstead@mozilla.com>
Wed, 29 Apr 2015 17:27:19 -0700
changeset 273064 cd5b4e44e260a2f8bdbd7f1f4505443702e48ff9
parent 273063 fba992940a64dfc85f5f2ae61610129163f00cc2
child 273065 ef2bdaf2121ea22ed7b1ea13716444a957feaaa9
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbrosset
bugs1153022
milestone40.0a1
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
Bug 1153022 - Add trimOutput option to inplace editor;r=pbrosset
browser/devtools/markupview/markup-view.js
browser/devtools/shared/inplace-editor.js
browser/devtools/shared/test/browser.ini
browser/devtools/shared/test/browser_inplace-editor-01.js
browser/devtools/shared/test/browser_inplace-editor-02.js
browser/devtools/shared/test/browser_inplace-editor.js
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -2259,16 +2259,17 @@ function TextEditor(aContainer, aNode, a
 
   this.markup.template(aTemplate, this);
 
   editableField({
     element: this.value,
     stopOnReturn: true,
     trigger: "dblclick",
     multiline: true,
+    trimOutput: false,
     done: (aVal, aCommit) => {
       if (!aCommit) {
         return;
       }
       this.node.getNodeValue().then(longstr => {
         longstr.string().then(oldValue => {
           longstr.release().then(null, console.error);
 
--- a/browser/devtools/shared/inplace-editor.js
+++ b/browser/devtools/shared/inplace-editor.js
@@ -78,16 +78,20 @@ Cu.import("resource://gre/modules/devtoo
  *    {boolean} stopOnTab:
  *       If true, the tab key will not advance the editor to the next
  *       focusable element.
  *    {boolean} stopOnShiftTab:
  *       If true, shift tab will not advance the editor to the previous
  *       focusable element.
  *    {string} trigger: The DOM event that should trigger editing,
  *      defaults to "click"
+ *    {boolean} multiline: Should the editor be a multiline textarea?
+ *      defaults to false
+ *    {boolean} trimOutput: Should the returned string be trimmed?
+ *      defaults to true
  */
 function editableField(aOptions)
 {
   return editableItem(aOptions, function(aElement, aEvent) {
     if (!aOptions.element.inplaceEditor) {
       new InplaceEditor(aOptions, aEvent);
     }
   });
@@ -184,16 +188,17 @@ function InplaceEditor(aOptions, aEvent)
   this.doc = doc;
   this.elt.inplaceEditor = this;
 
   this.change = aOptions.change;
   this.done = aOptions.done;
   this.destroy = aOptions.destroy;
   this.initial = aOptions.initial ? aOptions.initial : this.elt.textContent;
   this.multiline = aOptions.multiline || false;
+  this.trimOutput = aOptions.trimOutput === undefined ? true : !!aOptions.trimOutput;
   this.stopOnShiftTab = !!aOptions.stopOnShiftTab;
   this.stopOnTab = !!aOptions.stopOnTab;
   this.stopOnReturn = !!aOptions.stopOnReturn;
   this.contentType = aOptions.contentType || CONTENT_TYPES.PLAIN_TEXT;
   this.property = aOptions.property;
   this.popup = aOptions.popup;
 
   this._onBlur = this._onBlur.bind(this);
@@ -249,16 +254,22 @@ function InplaceEditor(aOptions, aEvent)
   EventEmitter.decorate(this);
 }
 
 exports.InplaceEditor = InplaceEditor;
 
 InplaceEditor.CONTENT_TYPES = CONTENT_TYPES;
 
 InplaceEditor.prototype = {
+
+  get currentInputValue() {
+    let val = this.trimOutput ? this.input.value.trim() : this.input.value;
+    return val;
+  },
+
   _createInput: function InplaceEditor_createEditor()
   {
     this.input =
       this.doc.createElementNS(HTML_NS, this.multiline ? "textarea" : "input");
     this.input.inplaceEditor = this;
     this.input.classList.add("styleinspector-propertyeditor");
     this.input.value = this.initial;
 
@@ -393,17 +404,17 @@ InplaceEditor.prototype = {
     }
 
     this.input.value = newValue.value;
     this.input.setSelectionRange(newValue.start, newValue.end);
     this._doValidation();
 
     // Call the user's change handler if available.
     if (this.change) {
-      this.change(this.input.value.trim());
+      this.change(this.currentInputValue);
     }
 
     return true;
   },
 
   /**
    * Increment the property value based on the property type.
    *
@@ -785,18 +796,18 @@ InplaceEditor.prototype = {
   {
     if (this._applied) {
       return;
     }
 
     this._applied = true;
 
     if (this.done) {
-      let val = this.input.value.trim();
-      return this.done(this.cancelled ? this.initial : val, !this.cancelled, direction);
+      let val = this.cancelled ? this.initial : this.currentInputValue;
+      return this.done(val, !this.cancelled, direction);
     }
 
     return null;
   },
 
   /**
    * Handle loss of focus by calling done if it hasn't been called yet.
    */
@@ -1029,17 +1040,17 @@ InplaceEditor.prototype = {
 
     // Update size if we're autosizing.
     if (this._measurement) {
       this._updateSize();
     }
 
     // Call the user's change handler if available.
     if (this.change) {
-      this.change(this.input.value.trim());
+      this.change(this.currentInputValue);
     }
   },
 
   /**
    * Fire validation callback with current input
    */
   _doValidation: function()
   {
--- a/browser/devtools/shared/test/browser.ini
+++ b/browser/devtools/shared/test/browser.ini
@@ -65,17 +65,18 @@ support-files =
 [browser_graphs-09f.js]
 [browser_graphs-10a.js]
 [browser_graphs-10b.js]
 [browser_graphs-11a.js]
 [browser_graphs-11b.js]
 [browser_graphs-12.js]
 [browser_graphs-13.js]
 [browser_graphs-14.js]
-[browser_inplace-editor.js]
+[browser_inplace-editor-01.js]
+[browser_inplace-editor-02.js]
 [browser_layoutHelpers.js]
 skip-if = e10s # Layouthelpers test should not run in a content page.
 [browser_layoutHelpers-getBoxQuads.js]
 skip-if = e10s # Layouthelpers test should not run in a content page.
 [browser_mdn-docs-01.js]
 [browser_mdn-docs-02.js]
 [browser_num-l10n.js]
 [browser_observableobject.js]
rename from browser/devtools/shared/test/browser_inplace-editor.js
rename to browser/devtools/shared/test/browser_inplace-editor-01.js
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/test/browser_inplace-editor-02.js
@@ -0,0 +1,91 @@
+/* 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";
+
+let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
+let {editableField, getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
+
+// Test that the trimOutput option for the inplace editor works correctly.
+
+add_task(function*() {
+  yield promiseTab("data:text/html;charset=utf-8,inline editor tests");
+  let [host, win, doc] = yield createHost();
+
+  yield testNonTrimmed(doc);
+  yield testTrimmed(doc);
+
+  host.destroy();
+  gBrowser.removeCurrentTab();
+});
+
+function testNonTrimmed(doc) {
+  info("Testing the trimOutput=false option");
+  let def = promise.defer();
+
+  let initial = "\nMultiple\nLines\n";
+  let changed = " \nMultiple\nLines\n with more whitespace ";
+  createInplaceEditorAndClick({
+    trimOutput: false,
+    multiline: true,
+    initial: initial,
+    start: function(editor) {
+      is(editor.input.value, initial, "Explicit initial value should be used.");
+      editor.input.value = changed;
+      EventUtils.sendKey("return");
+    },
+    done: onDone(changed, true, def)
+  }, doc);
+
+  return def.promise;
+}
+
+function testTrimmed(doc) {
+  info("Testing the trimOutput=true option (default value)");
+  let def = promise.defer();
+
+  let initial = "\nMultiple\nLines\n";
+  let changed = " \nMultiple\nLines\n with more whitespace ";
+  createInplaceEditorAndClick({
+    initial: initial,
+    multiline: true,
+    start: function(editor) {
+      is(editor.input.value, initial, "Explicit initial value should be used.");
+      editor.input.value = changed;
+      EventUtils.sendKey("return");
+    },
+    done: onDone(changed.trim(), true, def)
+  }, doc);
+
+  return def.promise;
+}
+
+function onDone(value, isCommit, def) {
+  return function(actualValue, actualCommit) {
+    info("Inplace-editor's done callback executed, checking its state");
+    is(actualValue, value, "The value is correct");
+    is(actualCommit, isCommit, "The commit boolean is correct");
+    def.resolve();
+  }
+}
+
+function createInplaceEditorAndClick(options, doc) {
+  doc.body.innerHTML = "";
+  let span = options.element = createSpan(doc);
+
+  info("Creating an inplace-editor field");
+  editableField(options);
+
+  info("Clicking on the inplace-editor field to turn to edit mode");
+  span.click();
+}
+
+function createSpan(doc) {
+  info("Creating a new span element");
+  let span = doc.createElement("span");
+  span.setAttribute("tabindex", "0");
+  span.textContent = "Edit Me!";
+  doc.body.appendChild(span);
+  return span;
+}