Bug 1360868 - Properly formatted data URLs in source links r=pbro draft
authorSebastian Zartner <sebastianzartner@gmail.com>
Sat, 29 Jul 2017 11:31:13 +0200
changeset 618019 4e7ccf4cd103b88c9703b844fc9cfc2b77808f7a
parent 617496 16ffc1d05422a81099ce8b9b59de66dde4c8b2f0
child 639940 7014fb6bef0f45c815cd0e1e4322189249ae34e3
push id71190
push userbmo:sebastianzartner@gmail.com
push dateSat, 29 Jul 2017 10:03:29 +0000
reviewerspbro
bugs1360868
milestone56.0a1
Bug 1360868 - Properly formatted data URLs in source links r=pbro MozReview-Commit-ID: 7loVwUynHhw
devtools/client/inspector/rules/models/rule.js
devtools/client/inspector/rules/test/browser_rules_style-editor-link.js
devtools/shared/inspector/css-logic.js
--- a/devtools/client/inspector/rules/models/rule.js
+++ b/devtools/client/inspector/rules/models/rule.js
@@ -135,21 +135,32 @@ Rule.prototype = {
    *         Promise which resolves with location as an object containing
    *         both the full and short version of the source string.
    */
   getOriginalSourceStrings: function () {
     return this.domRule.getOriginalLocation().then(({href,
                                                      line, mediaText}) => {
       let mediaString = mediaText ? " @" + mediaText : "";
       let linePart = line > 0 ? (":" + line) : "";
+      let decodedHref = href;
+
+      if (decodedHref) {
+        try {
+          decodedHref = decodeURI(href);
+        } catch (e) {
+          decodedHref = href;
+        }
+
+        decodedHref = unescape(href);
+      }
 
       let sourceStrings = {
-        full: (href || CssLogic.l10n("rule.sourceInline")) + linePart +
+        full: (decodedHref || CssLogic.l10n("rule.sourceInline")) + linePart +
           mediaString,
-        short: CssLogic.shortSource({href: href}) + linePart + mediaString
+        short: CssLogic.shortSource({href: decodedHref}) + linePart + mediaString
       };
 
       return sourceStrings;
     });
   },
 
   /**
    * Returns true if the rule matches the creation options
--- a/devtools/client/inspector/rules/test/browser_rules_style-editor-link.js
+++ b/devtools/client/inspector/rules/test/browser_rules_style-editor-link.js
@@ -1,20 +1,22 @@
 /* vim: set ft=javascript 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 links from the rule-view to the styleeditor
 
-const STYLESHEET_URL = "data:text/css," + encodeURIComponent(
-  ["#first {",
-   "color: blue",
-   "}"].join("\n"));
+const STYLESHEET_DATA_URL_CONTENTS = ["#first {",
+                                      "color: blue",
+                                      "}"].join("\n");
+const STYLESHEET_DATA_URL =
+      `data:text/css,${encodeURIComponent(STYLESHEET_DATA_URL_CONTENTS)}`;
+const STYLESHEET_DECODED_DATA_URL = `data:text/css,${STYLESHEET_DATA_URL_CONTENTS}`;
 
 const EXTERNAL_STYLESHEET_FILE_NAME = "doc_style_editor_link.css";
 const EXTERNAL_STYLESHEET_URL = URL_ROOT + EXTERNAL_STYLESHEET_FILE_NAME;
 
 const DOCUMENT_URL = "data:text/html;charset=utf-8," + encodeURIComponent(`
   <html>
   <head>
   <title>Rule view style editor link test</title>
@@ -22,17 +24,17 @@ const DOCUMENT_URL = "data:text/html;cha
   html { color: #000000; }
   div { font-variant: small-caps; color: #000000; }
   .nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em;
   font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">
   </style>
   <style>
   div { font-weight: bold; }
   </style>
-  <link rel="stylesheet" type="text/css" href="${STYLESHEET_URL}">
+  <link rel="stylesheet" type="text/css" href="${STYLESHEET_DATA_URL}">
   <link rel="stylesheet" type="text/css" href="${EXTERNAL_STYLESHEET_URL}">
   </head>
   <body>
   <h1>Some header text</h1>
   <p id="salutation" style="font-size: 12pt">hi.</p>
   <p id="body" style="font-size: 12pt">I am a test-case. This text exists
   solely to provide some things to
   <span style="color: yellow" class="highlight">
@@ -169,25 +171,37 @@ function* testDisabledStyleEditor(view, 
   clickLinkByIndex(view, 1);
   yield onStyleEditorSelected;
   is(toolbox.currentToolId, "styleeditor", "Style Editor should be selected");
 
   Services.prefs.clearUserPref("devtools.styleeditor.enabled");
 }
 
 function testRuleViewLinkLabel(view) {
-  let link = getRuleViewLinkByIndex(view, 2);
+  // Check data URL link label
+  let link = getRuleViewLinkByIndex(view, 1);
   let labelElem = link.querySelector(".ruleview-rule-source-label");
   let value = labelElem.textContent;
   let tooltipText = labelElem.getAttribute("title");
 
-  is(value, EXTERNAL_STYLESHEET_FILE_NAME + ":1",
-    "rule view stylesheet display value matches filename and line number");
-  is(tooltipText, EXTERNAL_STYLESHEET_URL + ":1",
-    "rule view stylesheet tooltip text matches the full URI path");
+  is(value, `${STYLESHEET_DATA_URL_CONTENTS}:1`,
+    "rule view data URL stylesheet display value matches contents");
+  is(tooltipText, `${STYLESHEET_DECODED_DATA_URL}:1`,
+    "rule view data URL stylesheet tooltip text matches the full URI path");
+
+  // Check external link label
+  link = getRuleViewLinkByIndex(view, 2);
+  labelElem = link.querySelector(".ruleview-rule-source-label");
+  value = labelElem.textContent;
+  tooltipText = labelElem.getAttribute("title");
+
+  is(value, `${EXTERNAL_STYLESHEET_FILE_NAME}:1`,
+    "rule view external stylesheet display value matches filename and line number");
+  is(tooltipText, `${EXTERNAL_STYLESHEET_URL}:1`,
+    "rule view external stylesheet tooltip text matches the full URI path");
 }
 
 function testUnselectableRuleViewLink(view, index) {
   let link = getRuleViewLinkByIndex(view, index);
   let unselectable = link.hasAttribute("unselectable");
 
   ok(unselectable, "Rule view is unselectable");
 }
--- a/devtools/shared/inspector/css-logic.js
+++ b/devtools/shared/inspector/css-logic.js
@@ -4,16 +4,18 @@
  * 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 { getRootBindingParent } = require("devtools/shared/layout/utils");
 const { getTabPrefs } = require("devtools/shared/indentation");
 
+const CROP_STRING_LENGTH = 40;
+
 /*
  * About the objects defined in this file:
  * - CssLogic contains style information about a view context. It provides
  *   access to 2 sets of objects: Css[Sheet|Rule|Selector] provide access to
  *   information that does not change when the selected element changes while
  *   Css[Property|Selector]Info provide information that is dependent on the
  *   selected element.
  *   Its key methods are highlight(), getPropertyInfo() and forEachSheet(), etc
@@ -104,16 +106,22 @@ exports.isContentStylesheet = function (
  * @param {CSSStyleSheet} sheet the DOM object for the style sheet.
  */
 exports.shortSource = function (sheet) {
   // Use a string like "inline" if there is no source href
   if (!sheet || !sheet.href) {
     return exports.l10n("rule.sourceInline");
   }
 
+  let dataUrl = sheet.href.trim().match(/^data:.*?,((?:.|\r|\n)*)$/);
+  if (dataUrl) {
+    return dataUrl[1].length > CROP_STRING_LENGTH ?
+        dataUrl[1].substr(0, CROP_STRING_LENGTH - 1) + "…" : dataUrl[1];
+  }
+
   // We try, in turn, the filename, filePath, query string, whole thing
   let url = {};
   try {
     url = new URL(sheet.href);
   } catch (ex) {
     // Some UA-provided stylesheets are not valid URLs.
   }
 
@@ -124,18 +132,17 @@ exports.shortSource = function (sheet) {
     }
     return url.pathname;
   }
 
   if (url.query) {
     return url.query;
   }
 
-  let dataUrl = sheet.href.match(/^(data:[^,]*),/);
-  return dataUrl ? dataUrl[1] : sheet.href;
+  return sheet.href;
 };
 
 const TAB_CHARS = "\t";
 const SPACE_CHARS = " ";
 
 /**
  * Prettify minified CSS text.
  * This prettifies CSS code where there is no indentation in usual places while