Bug 893677 - Collapse data URL attribute values. r=miker
authorBrian Grinstead <briangrinstead@gmail.com>
Thu, 12 Sep 2013 17:25:08 -0500
changeset 147095 467edd1d0e1e6bfb6667a6b1b7ef348973cfa273
parent 147094 159e240fc628fea2921b51e139887f0cdd846e25
child 147096 933b9f822c2b30e736a1736584e818cd8dd64761
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmiker
bugs893677
milestone26.0a1
Bug 893677 - Collapse data URL attribute values. r=miker
browser/devtools/markupview/markup-view.js
browser/devtools/markupview/test/browser_inspector_markup_edit.html
browser/devtools/markupview/test/browser_inspector_markup_edit.js
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -5,16 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {Cc, Cu, Ci} = require("chrome");
 
 // Page size for pageup/pagedown
 const PAGE_SIZE = 10;
 const PREVIEW_AREA = 700;
 const DEFAULT_MAX_CHILDREN = 100;
+const COLLAPSE_ATTRIBUTE_LENGTH = 120;
+const COLLAPSE_DATA_URL_REGEX = /^data.+base64/;
+const COLLAPSE_DATA_URL_LENGTH = 60;
 
 const {UndoStack} = require("devtools/shared/undo");
 const {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
 const promise = require("sdk/core/promise");
 
 Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/Templater.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
@@ -1360,18 +1363,26 @@ ElementEditor.prototype = {
     // Remove the old version of this attribute from the DOM.
     let oldAttr = this.attrs[aAttr.name];
     if (oldAttr && oldAttr.parentNode) {
       oldAttr.parentNode.removeChild(oldAttr);
     }
 
     this.attrs[aAttr.name] = attr;
 
+    let collapsedValue;
+    if (aAttr.value.match(COLLAPSE_DATA_URL_REGEX)) {
+      collapsedValue = truncateString(aAttr.value, COLLAPSE_DATA_URL_LENGTH);
+    }
+    else {
+      collapsedValue = truncateString(aAttr.value, COLLAPSE_ATTRIBUTE_LENGTH);
+    }
+
     name.textContent = aAttr.name;
-    val.textContent = aAttr.value;
+    val.textContent = collapsedValue;
 
     return attr;
   },
 
   /**
    * Parse a user-entered attribute string and apply the resulting
    * attributes to the node.  This operation is undoable.
    *
@@ -1462,16 +1473,25 @@ ElementEditor.prototype = {
     }).then(null, console.error);
   }
 };
 
 function nodeDocument(node) {
   return node.ownerDocument || (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
 }
 
+function truncateString(str, maxLength) {
+  if (str.length <= maxLength) {
+    return str;
+  }
+
+  return str.substring(0, Math.ceil(maxLength / 2)) +
+         "…" +
+         str.substring(str.length - Math.floor(maxLength / 2));
+}
 /**
  * Parse attribute names and values from a string.
  *
  * @param  {String} attr
  *         The input string for which names/values are to be parsed.
  * @param  {HTMLDocument} doc
  *         A document that can be used to test valid attributes.
  * @return {Array}
--- a/browser/devtools/markupview/test/browser_inspector_markup_edit.html
+++ b/browser/devtools/markupview/test/browser_inspector_markup_edit.html
@@ -37,10 +37,12 @@
     <div id="node23"></div>
     <div id="node24"></div>
     <div id="retag-me">
       <div id="retag-me-2"></div>
     </div>
     <div id="node25"></div>
     <div id="node26" style='background-image: url("moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.org%2F");'></div>
     <div id="node27" class="Double &quot; and single &apos;"></div>
+    <img id="node-data-url" />
+    <div id="node-data-url-style"></div>
   </body>
 </html>
--- a/browser/devtools/markupview/test/browser_inspector_markup_edit.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_edit.js
@@ -29,16 +29,25 @@ function test() {
   waitForExplicitFinish();
 
   // Will hold the doc we're viewing
   let doc;
 
   // Holds the MarkupTool object we're testing.
   let markup;
 
+  let LONG_ATTRIBUTE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+  let LONG_ATTRIBUTE_COLLAPSED = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEF\u2026UVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+  let DATA_URL_INLINE_STYLE='color: red; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC");';
+  let DATA_URL_INLINE_STYLE_COLLAPSED='color: red; background: url("data:image/png;base64,iVBORw0KG\u2026NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC");';
+
+  let DATA_URL_ATTRIBUTE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC";
+  let DATA_URL_ATTRIBUTE_COLLAPSED = "data:image/png;base64,iVBORw0K\u20269/AFGGFyjOXZtQAAAAAElFTkSuQmCC";
+
   /**
    * Edit a given editableField
    */
   function editField(aField, aValue)
   {
     aField.focus();
     EventUtils.sendKey("return", inspector.panelWin);
     let input = inplaceEditor(aField).input;
@@ -200,16 +209,138 @@ function test() {
         assertAttributes(doc.querySelector("#node24"), {
           id: "node24",
           class: ""
         });
       }
     },
 
     {
+      desc: "Try to add long attribute to make sure it is collapsed in attribute editor.",
+      before: function() {
+        assertAttributes(doc.querySelector("#node24"), {
+          id: "node24",
+          class: ""
+        });
+      },
+      execute: function(after) {
+        let editor = getContainerForRawNode(markup, doc.querySelector("#node24")).editor;
+        let attr = editor.newAttr;
+        editField(attr, 'data-long="'+LONG_ATTRIBUTE+'"');
+        inspector.once("markupmutation", after);
+      },
+      after: function() {
+
+        let editor = getContainerForRawNode(markup, doc.querySelector("#node24")).editor;
+        let visibleAttrText = editor.attrs["data-long"].querySelector(".attr-value").textContent;
+        is (visibleAttrText, LONG_ATTRIBUTE_COLLAPSED)
+
+        assertAttributes(doc.querySelector("#node24"), {
+          id: "node24",
+          class: "",
+          'data-long':LONG_ATTRIBUTE
+        });
+      }
+    },
+
+    {
+      desc: "Try to modify the collapsed long attribute, making sure it expands.",
+      before: function() {
+        assertAttributes(doc.querySelector("#node24"), {
+          id: "node24",
+          class: "",
+          'data-long': LONG_ATTRIBUTE
+        });
+      },
+      execute: function(after) {
+        let editor = getContainerForRawNode(markup, doc.querySelector("#node24")).editor;
+        let attr = editor.attrs["data-long"].querySelector(".editable");
+
+        // Check to make sure it has expanded after focus
+        attr.focus();
+        EventUtils.sendKey("return", inspector.panelWin);
+        let input = inplaceEditor(attr).input;
+        is (input.value, 'data-long="'+LONG_ATTRIBUTE+'"');
+        EventUtils.sendKey("escape", inspector.panelWin);
+
+        editField(attr, input.value  + ' data-short="ABC"');
+        inspector.once("markupmutation", after);
+      },
+      after: function() {
+
+        let editor = getContainerForRawNode(markup, doc.querySelector("#node24")).editor;
+        let visibleAttrText = editor.attrs["data-long"].querySelector(".attr-value").textContent;
+        is (visibleAttrText, LONG_ATTRIBUTE_COLLAPSED)
+
+        assertAttributes(doc.querySelector("#node24"), {
+          id: "node24",
+          class: "",
+          'data-long': LONG_ATTRIBUTE,
+          "data-short": "ABC"
+        });
+      }
+    },
+
+    {
+      desc: "Try to add long data URL to make sure it is collapsed in attribute editor.",
+      before: function() {
+        assertAttributes(doc.querySelector("#node-data-url"), {
+          id: "node-data-url"
+        });
+      },
+      execute: function(after) {
+        let editor = getContainerForRawNode(markup, doc.querySelector("#node-data-url")).editor;
+        let attr = editor.newAttr;
+        editField(attr, 'src="'+DATA_URL_ATTRIBUTE+'"');
+        inspector.once("markupmutation", after);
+      },
+      after: function() {
+
+        let editor = getContainerForRawNode(markup, doc.querySelector("#node-data-url")).editor;
+        let visibleAttrText = editor.attrs["src"].querySelector(".attr-value").textContent;
+        is (visibleAttrText, DATA_URL_ATTRIBUTE_COLLAPSED);
+
+        let node = doc.querySelector("#node-data-url");
+        is (node.width, 16, "Image width has been set after data url src.");
+        is (node.height, 16, "Image height has been set after data url src.");
+
+        assertAttributes(node, {
+          id: "node-data-url",
+          "src": DATA_URL_ATTRIBUTE
+        });
+      }
+    },
+
+    {
+      desc: "Try to add long data URL to make sure it is collapsed in attribute editor.",
+      before: function() {
+        assertAttributes(doc.querySelector("#node-data-url-style"), {
+          id: "node-data-url-style"
+        });
+      },
+      execute: function(after) {
+        let editor = getContainerForRawNode(markup, doc.querySelector("#node-data-url-style")).editor;
+        let attr = editor.newAttr;
+        editField(attr, "style='"+DATA_URL_INLINE_STYLE+"'");
+        inspector.once("markupmutation", after);
+      },
+      after: function() {
+
+        let editor = getContainerForRawNode(markup, doc.querySelector("#node-data-url-style")).editor;
+        let visibleAttrText = editor.attrs["style"].querySelector(".attr-value").textContent;
+        is (visibleAttrText, DATA_URL_INLINE_STYLE_COLLAPSED)
+
+        assertAttributes(doc.querySelector("#node-data-url-style"), {
+          id: "node-data-url-style",
+          'style':DATA_URL_INLINE_STYLE
+        });
+      }
+    },
+
+    {
       desc: "Edit text",
       before: function() {
         let node = doc.querySelector('.node6').firstChild;
         is(node.nodeValue, "line6", "Text should be unchanged");
       },
       execute: function(after) {
         inspector.once("markupmutation", after);
         let node = doc.querySelector('.node6').firstChild;