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 281533 4f5aafee00a612ecda353b28017c86de3ddc7cf3
parent 281532 f7e9ad668e37124bf15503f881f4868fb5df6a06
child 281534 636ae3d5c7f0981faf97a8b4fa411e53221c169a
push id29945
push usercbook@mozilla.com
push dateTue, 26 Jan 2016 10:47:04 +0000
treeherdermozilla-central@7dc87559627e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgrins
bugs1225063
milestone47.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 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);