Bug 1249888 - try/catch SourceMapConsumer to avoid empty rule-view when source map is invalid; r=gl
MozReview-Commit-ID: BrheU7MMBMy
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -9,16 +9,18 @@ support-files =
doc_content_stylesheet_linked.css
doc_content_stylesheet_script.css
doc_copystyles.css
doc_copystyles.html
doc_cssom.html
doc_custom.html
doc_filter.html
doc_frame_script.js
+ doc_invalid_sourcemap.css
+ doc_invalid_sourcemap.html
doc_keyframeanimation.css
doc_keyframeanimation.html
doc_keyframeLineNumbers.html
doc_media_queries.html
doc_pseudoelement.html
doc_ruleLineNumbers.html
doc_sourcemaps.css
doc_sourcemaps.css.map
@@ -114,16 +116,17 @@ skip-if = os == "mac" # Bug 1245996 : cl
[browser_rules_filtereditor-appears-on-swatch-click.js]
[browser_rules_filtereditor-commit-on-ENTER.js]
[browser_rules_filtereditor-revert-on-ESC.js]
skip-if = (os == "win" && debug) # bug 963492: win.
[browser_rules_guessIndentation.js]
[browser_rules_inherited-properties_01.js]
[browser_rules_inherited-properties_02.js]
[browser_rules_inherited-properties_03.js]
+[browser_rules_invalid-source-map.js]
[browser_rules_keybindings.js]
[browser_rules_keyframes-rule_01.js]
[browser_rules_keyframes-rule_02.js]
[browser_rules_keyframeLineNumbers.js]
[browser_rules_lineNumbers.js]
[browser_rules_livepreview.js]
[browser_rules_mark_overridden_01.js]
[browser_rules_mark_overridden_02.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_invalid-source-map.js
@@ -0,0 +1,46 @@
+/* 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 that when a source map is missing/invalid, the rule view still loads
+// correctly.
+
+const TESTCASE_URI = URL_ROOT + "doc_invalid_sourcemap.html";
+const PREF = "devtools.styleeditor.source-maps-enabled";
+const CSS_LOC = "doc_invalid_sourcemap.css:1";
+
+add_task(function*() {
+ info("Setting the " + PREF + " pref to true");
+ Services.prefs.setBoolPref(PREF, true);
+
+ yield addTab(TESTCASE_URI);
+ let {toolbox, inspector, view} = yield openRuleView();
+
+ info("Selecting the test node");
+ yield selectNode("div", inspector);
+
+ let ruleEl = getRuleViewRule(view, "div");
+ ok(ruleEl, "The 'div' rule exists in the rule-view");
+
+ let prop = getRuleViewProperty(view, "div", "color");
+ ok(prop, "The 'color' property exists in this rule");
+
+ let value = getRuleViewPropertyValue(view, "div", "color");
+ is(value, "gold", "The 'color' property has the right value");
+
+ yield verifyLinkText(CSS_LOC, view);
+
+ info("Clearing the " + PREF + " pref");
+ Services.prefs.clearUserPref(PREF);
+});
+
+function verifyLinkText(text, view) {
+ info("Verifying that the rule-view stylesheet link is " + text);
+ let label = getRuleViewLinkByIndex(view, 1).querySelector("label");
+ return waitForSuccess(
+ () => label.getAttribute("value") == text,
+ "Link text changed to display correct location: " + text
+ );
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/doc_invalid_sourcemap.css
@@ -0,0 +1,3 @@
+div { color: gold; }
+
+/*# sourceMappingURL=this-source-map-does-not-exist.css.map */
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/doc_invalid_sourcemap.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Invalid source map</title>
+ <link rel="stylesheet" type="text/css" href="doc_invalid_sourcemap.css">
+</head>
+<body>
+ <div>invalid source map</div>
+</body>
+</html>
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -729,39 +729,48 @@ var StyleSheetActor = protocol.ActorClas
* Fetch the source map for this stylesheet.
*
* @return {Promise}
* A promise that resolves with a SourceMapConsumer, or null.
*/
_fetchSourceMap: function() {
let deferred = promise.defer();
- this._getText().then((content) => {
- let url = this._extractSourceMapUrl(content);
+ this._getText().then(sheetContent => {
+ let url = this._extractSourceMapUrl(sheetContent);
if (!url) {
// no source map for this stylesheet
deferred.resolve(null);
return;
- };
+ }
url = normalize(url, this.href);
let options = {
loadFromCache: false,
policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
window: this.window
};
- let map = fetch(url, options)
- .then(({content}) => {
- let map = new SourceMapConsumer(content);
- this._setSourceMapRoot(map, url, this.href);
- this._sourceMap = promise.resolve(map);
- deferred.resolve(map);
- return map;
- }, deferred.reject);
+ let map = fetch(url, options).then(({content}) => {
+ // Fetching the source map might have failed with a 404 or other. When
+ // this happens, SourceMapConsumer may fail with a JSON.parse error.
+ let consumer;
+ try {
+ consumer = new SourceMapConsumer(content);
+ } catch (e) {
+ deferred.reject(new Error(
+ `Source map at ${url} not found or invalid`));
+ return null;
+ }
+ this._setSourceMapRoot(consumer, url, this.href);
+ this._sourceMap = promise.resolve(consumer);
+
+ deferred.resolve(consumer);
+ return consumer;
+ }, deferred.reject);
this._sourceMap = map;
}, deferred.reject);
return deferred.promise;
},
/**