Bug 1225063 - add option (devtools.markup.collapseAttributes) to enable/disable attribute collapsing from settings panel;r=bgrins
authorHugo Arregui <hugo.arregui@gmail.com>
Mon, 25 Jan 2016 16:02:31 -0800
changeset 325608 4f5aafee00a612ecda353b28017c86de3ddc7cf3
parent 325607 f7e9ad668e37124bf15503f881f4868fb5df6a06
child 325609 636ae3d5c7f0981faf97a8b4fa411e53221c169a
push id10011
push usercmanchester@mozilla.com
push dateTue, 26 Jan 2016 02:20:25 +0000
reviewersbgrins
bugs1225063
milestone47.0a1
Bug 1225063 - add option (devtools.markup.collapseAttributes) to enable/disable attribute collapsing from settings panel;r=bgrins
devtools/client/framework/toolbox-options.xul
devtools/client/inspector/markup/markup.js
devtools/client/inspector/markup/markup.xhtml
devtools/client/inspector/markup/test/browser_markup_tag_edit_07.js
devtools/client/locales/en-US/toolbox.dtd
devtools/client/preferences/devtools.js
--- a/devtools/client/framework/toolbox-options.xul
+++ b/devtools/client/framework/toolbox-options.xul
@@ -42,16 +42,19 @@
                     tooltiptext="&options.enablePersistentLogs.tooltip;"
                     data-pref="devtools.webconsole.persistlog"/>
         </vbox>
         <label>&options.context.inspector;</label>
         <vbox id="inspector-options" class="options-groupbox">
           <checkbox label="&options.showUserAgentStyles.label;"
                     tooltiptext="&options.showUserAgentStyles.tooltip;"
                     data-pref="devtools.inspector.showUserAgentStyles"/>
+          <checkbox label="&options.collapseAttrs.label;"
+                    tooltiptext="&options.collapseAttrs.tooltip;"
+                    data-pref="devtools.markup.collapseAttributes"/>
           <description>
             <label control="defaultColorUnitMenuList"
                    accesskey="&options.defaultColorUnit.accesskey;"
             >&options.defaultColorUnit.label;</label>
             <hbox>
               <menulist id="defaultColorUnitMenuList"
                         data-pref="devtools.defaultColorUnit">
                 <menupopup>
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -15,16 +15,18 @@ const DEFAULT_MAX_CHILDREN = 100;
 const COLLAPSE_DATA_URL_REGEX = /^data.+base64/;
 const COLLAPSE_DATA_URL_LENGTH = 60;
 const NEW_SELECTION_HIGHLIGHTER_TIMER = 1000;
 const DRAG_DROP_AUTOSCROLL_EDGE_DISTANCE = 50;
 const DRAG_DROP_MIN_AUTOSCROLL_SPEED = 5;
 const DRAG_DROP_MAX_AUTOSCROLL_SPEED = 15;
 const DRAG_DROP_MIN_INITIAL_DISTANCE = 10;
 const AUTOCOMPLETE_POPUP_PANEL_ID = "markupview_autoCompletePopup";
+const ATTR_COLLAPSE_ENABLED_PREF = "devtools.markup.collapseAttributes";
+const ATTR_COLLAPSE_LENGTH_PREF = "devtools.markup.collapseAttributeLength";
 
 const {UndoStack} = require("devtools/client/shared/undo");
 const {editableField, InplaceEditor} =
       require("devtools/client/shared/inplace-editor");
 const {HTMLEditor} = require("devtools/client/inspector/markup/html-editor");
 const promise = require("promise");
 const {Tooltip} = require("devtools/client/shared/widgets/Tooltip");
 const EventEmitter = require("devtools/shared/event-emitter");
@@ -32,16 +34,17 @@ const Heritage = require("sdk/core/herit
 const {setTimeout, clearTimeout, setInterval, clearInterval} =
       require("sdk/timers");
 const {parseAttribute} =
       require("devtools/client/shared/node-attribute-parser");
 const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis",
       Ci.nsIPrefLocalizedString).data;
 const {Task} = require("resource://gre/modules/Task.jsm");
 const {scrollIntoViewIfNeeded} = require("devtools/shared/layout/utils");
+const {PrefObserver} = require("devtools/client/styleeditor/utils");
 
 Cu.import("resource://devtools/shared/gcli/Templater.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "CSS", "CSS");
 loader.lazyGetter(this, "DOMParser", () => {
   return Cc["@mozilla.org/xmlextras/domparser;1"]
@@ -82,18 +85,20 @@ function MarkupView(inspector, frame, co
   this.htmlEditor = new HTMLEditor(this.doc);
 
   try {
     this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize");
   } catch (ex) {
     this.maxChildren = DEFAULT_MAX_CHILDREN;
   }
 
+  this.collapseAttributes =
+    Services.prefs.getBoolPref(ATTR_COLLAPSE_ENABLED_PREF);
   this.collapseAttributeLength =
-    Services.prefs.getIntPref("devtools.markup.collapseAttributeLength");
+    Services.prefs.getIntPref(ATTR_COLLAPSE_LENGTH_PREF);
 
   // Creating the popup to be used to show CSS suggestions.
   let options = {
     autoSelect: true,
     theme: "auto",
     // panelId option prevents the markupView autocomplete popup from
     // sharing XUL elements with other views, such as ruleView (see Bug 1191093)
     panelId: AUTOCOMPLETE_POPUP_PANEL_ID
@@ -113,16 +118,18 @@ function MarkupView(inspector, frame, co
   this._onMouseUp = this._onMouseUp.bind(this);
   this._onNewSelection = this._onNewSelection.bind(this);
   this._onKeyDown = this._onKeyDown.bind(this);
   this._onCopy = this._onCopy.bind(this);
   this._onFocus = this._onFocus.bind(this);
   this._onMouseMove = this._onMouseMove.bind(this);
   this._onMouseLeave = this._onMouseLeave.bind(this);
   this._onToolboxPickerHover = this._onToolboxPickerHover.bind(this);
+  this._onCollapseAttributesPrefChange =
+    this._onCollapseAttributesPrefChange.bind(this);
 
   // Listening to various events.
   this._elt.addEventListener("click", this._onMouseClick, false);
   this._elt.addEventListener("mousemove", this._onMouseMove, false);
   this._elt.addEventListener("mouseleave", this._onMouseLeave, false);
   this.doc.body.addEventListener("mouseup", this._onMouseUp);
   this.win.addEventListener("keydown", this._onKeyDown, false);
   this.win.addEventListener("copy", this._onCopy);
@@ -130,16 +137,22 @@ function MarkupView(inspector, frame, co
   this.walker.on("mutations", this._mutationObserver);
   this.walker.on("display-change", this._onDisplayChange);
   this._inspector.selection.on("new-node-front", this._onNewSelection);
   this._inspector.toolbox.on("picker-node-hovered", this._onToolboxPickerHover);
 
   this._onNewSelection();
   this._initTooltips();
 
+  this._prefObserver = new PrefObserver("devtools.markup");
+  this._prefObserver.on(ATTR_COLLAPSE_ENABLED_PREF,
+                        this._onCollapseAttributesPrefChange);
+  this._prefObserver.on(ATTR_COLLAPSE_LENGTH_PREF,
+                        this._onCollapseAttributesPrefChange);
+
   EventEmitter.decorate(this);
 }
 
 MarkupView.prototype = {
   /**
    * How long does a node flash when it mutates (in ms).
    */
   CONTAINER_FLASHING_DURATION: 500,
@@ -262,16 +275,24 @@ MarkupView.prototype = {
   _onMouseUp: function() {
     this.indicateDropTarget(null);
     this.indicateDragTarget(null);
     if (this._autoScrollInterval) {
       clearInterval(this._autoScrollInterval);
     }
   },
 
+  _onCollapseAttributesPrefChange: function() {
+    this.collapseAttributes =
+      Services.prefs.getBoolPref(ATTR_COLLAPSE_ENABLED_PREF);
+    this.collapseAttributeLength =
+      Services.prefs.getIntPref(ATTR_COLLAPSE_LENGTH_PREF);
+    this.update();
+  },
+
   cancelDragging: function() {
     if (!this.isDragging) {
       return;
     }
 
     for (let [, container] of this._containers) {
       if (container.isDragging) {
         container.cancelDragging();
@@ -1587,16 +1608,22 @@ MarkupView.prototype = {
     this.win.removeEventListener("copy", this._onCopy);
     this._frame.removeEventListener("focus", this._onFocus, false);
     this.walker.off("mutations", this._mutationObserver);
     this.walker.off("display-change", this._onDisplayChange);
     this._inspector.selection.off("new-node-front", this._onNewSelection);
     this._inspector.toolbox.off("picker-node-hovered",
                                 this._onToolboxPickerHover);
 
+    this._prefObserver.off(ATTR_COLLAPSE_ENABLED_PREF,
+                           this._onCollapseAttributesPrefChange);
+    this._prefObserver.off(ATTR_COLLAPSE_LENGTH_PREF,
+                           this._onCollapseAttributesPrefChange);
+    this._prefObserver.destroy();
+
     this._elt = null;
 
     for (let [, container] of this._containers) {
       container.destroy();
     }
     this._containers = null;
 
     this.tooltip.destroy();
@@ -2682,17 +2709,17 @@ ElementEditor.prototype = {
       }
     }
 
     // Only loop through the current attributes on the node.  Missing
     // attributes have already been removed at this point.
     for (let attr of nodeAttributes) {
       let el = this.attrElements.get(attr.name);
       let valueChanged = el &&
-        el.querySelector(".attr-value").textContent !== attr.value;
+        el.dataset.value !== attr.value;
       let isEditing = el && el.querySelector(".editable").inplaceEditor;
       let canSimplyShowEditor = el && (!valueChanged || isEditing);
 
       if (canSimplyShowEditor) {
         // Element already exists and doesn't need to be recreated.
         // Just show it (it's hidden by default due to the template).
         el.style.removeProperty("display");
       } else {
@@ -2868,19 +2895,19 @@ ElementEditor.prototype = {
       this.node.tagName, attributes, attribute.name);
 
     // Create links in the attribute value, and collapse long attributes if
     // needed.
     let collapse = value => {
       if (value && value.match(COLLAPSE_DATA_URL_REGEX)) {
         return truncateString(value, COLLAPSE_DATA_URL_LENGTH);
       }
-      return this.markup.collapseAttributeLength < 0
-        ? value :
-        truncateString(value, this.markup.collapseAttributeLength);
+      return this.markup.collapseAttributes
+        ? truncateString(value, this.markup.collapseAttributeLength)
+        : value;
     };
 
     val.innerHTML = "";
     for (let token of parsedLinksData) {
       if (token.type === "string") {
         val.appendChild(this.doc.createTextNode(collapse(token.value)));
       } else {
         let link = this.doc.createElement("span");
--- a/devtools/client/inspector/markup/markup.xhtml
+++ b/devtools/client/inspector/markup/markup.xhtml
@@ -69,16 +69,17 @@
      -->&gt;<!--
    --></span><!--
      --><div save="${eventNode}" class="markupview-events" data-event="true">ev</div><!--
  --></span>
 
     <span id="template-attribute"
           save="${attr}"
           data-attr="${attrName}"
+          data-value="${attrValue}"
           class="attreditor"
           style="display:none"> <!--
    --><span class="editable" save="${inner}" tabindex="0"><!--
      --><span save="${name}" class="attr-name theme-fg-color2"></span><!--
      -->=&quot;<!--
      --><span save="${val}" class="attr-value theme-fg-color6"></span><!--
      -->&quot;<!--
    --></span><!--
--- a/devtools/client/inspector/markup/test/browser_markup_tag_edit_07.js
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_07.js
@@ -68,35 +68,58 @@ var TEST_DATA = [{
     "src": DATA_URL_ATTRIBUTE
   },
   validate: (element, container, inspector) => {
     let editor = container.editor;
     let visibleAttrText = editor.attrElements.get("src").querySelector(".attr-value").textContent;
     is (visibleAttrText, DATA_URL_ATTRIBUTE_COLLAPSED);
   }
 }, {
-  desc: "Try to add long attribute with collapseAttributeLength == -1" +
+  desc: "Try to add long attribute with collapseAttributes == false" +
   "to make sure it isn't collapsed in attribute editor.",
   text: 'data-long="' + LONG_ATTRIBUTE + '"',
   expectedAttributes: {
     "data-long": LONG_ATTRIBUTE
   },
   setUp: function(inspector) {
-    inspector.markup.collapseAttributeLength = -1;
+    Services.prefs.setBoolPref("devtools.markup.collapseAttributes", false);
   },
   validate: (element, container, inspector) => {
     let editor = container.editor;
     let visibleAttrText = editor.attrElements
       .get("data-long")
       .querySelector(".attr-value")
       .textContent;
     is(visibleAttrText, LONG_ATTRIBUTE);
   },
   tearDown: function(inspector) {
-    inspector.markup.collapseAttributeLength = 120;
+    Services.prefs.clearUserPref("devtools.markup.collapseAttributes");
+  }
+}, {
+  desc: "Try to collapse attributes with collapseAttributeLength == 5",
+  text: 'data-long="' + LONG_ATTRIBUTE + '"',
+  expectedAttributes: {
+    "data-long": LONG_ATTRIBUTE
+  },
+  setUp: function(inspector) {
+    Services.prefs.setIntPref("devtools.markup.collapseAttributeLength", 2);
+  },
+  validate: (element, container, inspector) => {
+    let firstChar = LONG_ATTRIBUTE[0];
+    let lastChar = LONG_ATTRIBUTE[LONG_ATTRIBUTE.length - 1];
+    let collapsed = firstChar + "\u2026" + lastChar;
+    let editor = container.editor;
+    let visibleAttrText = editor.attrElements
+      .get("data-long")
+      .querySelector(".attr-value")
+      .textContent;
+    is(visibleAttrText, collapsed);
+  },
+  tearDown: function(inspector) {
+    Services.prefs.clearUserPref("devtools.markup.collapseAttributeLength");
   }
 }];
 
 add_task(function*() {
   let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
   yield runAddAttributesTests(TEST_DATA, "div", inspector, testActor);
 });
 
--- a/devtools/client/locales/en-US/toolbox.dtd
+++ b/devtools/client/locales/en-US/toolbox.dtd
@@ -54,16 +54,22 @@ values from browser.dtd.  -->
 <!ENTITY options.context.inspector "Inspector">
 
 <!-- LOCALIZATION NOTE (options.showUserAgentStyles.label): This is the label
   -  for the checkbox option to show user agent styles in the Inspector
   -  panel. -->
 <!ENTITY options.showUserAgentStyles.label "Show Browser Styles">
 <!ENTITY options.showUserAgentStyles.tooltip "Turning this on will show default styles that are loaded by the browser.">
 
+<!-- LOCALIZATION NOTE (options.collapseAttrs.label): This is the label
+  -  for the checkbox option to enable collapse attributes in the Inspector
+  -  panel. -->
+<!ENTITY options.collapseAttrs.label "Truncate DOM attributes">
+<!ENTITY options.collapseAttrs.tooltip "Truncate long attributes in the inspector">
+
 <!-- LOCALIZATION NOTE (options.defaultColorUnit.label): This is the label for a
   -  dropdown list that controls the default color unit used in the inspector.
   -  This label is visible in the options panel. -->
 <!ENTITY options.defaultColorUnit.label "Default color unit">
 
 <!-- LOCALIZATION NOTE (options.defaultColorUnit.accesskey): This is the access
   -  key for a dropdown list that controls the default color unit used in the
   -  inspector. This is visible in the options panel. -->
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -61,18 +61,20 @@ pref("devtools.inspector.show_pseudo_ele
 pref("devtools.inspector.imagePreviewTooltipSize", 300);
 // Enable user agent style inspection in rule-view
 pref("devtools.inspector.showUserAgentStyles", false);
 // Show all native anonymous content (like controls in <video> tags)
 pref("devtools.inspector.showAllAnonymousContent", false);
 // Enable the MDN docs tooltip
 pref("devtools.inspector.mdnDocsTooltip.enabled", true);
 
-// Collapse attributes that are too long.
-// Use -1 to not collapse attributes at all.
+// Enable to collapse attributes that are too long.
+pref("devtools.markup.collapseAttributes", true);
+
+// Length to collapse attributes
 pref("devtools.markup.collapseAttributeLength", 120);
 
 // DevTools default color unit
 pref("devtools.defaultColorUnit", "authored");
 
 // Enable the Responsive UI tool
 pref("devtools.responsiveUI.no-reload-notification", false);