Bug 1387477 - report source map errors due to missing sources; r=jdescottes

MozReview-Commit-ID: 5UbkJH8fvLn

--HG--
extra : rebase_source : 3242c0bcfcb5d04343c1a724fe78904d5f52e567
This commit is contained in:
Tom Tromey 2017-08-04 13:14:11 -06:00
parent ef291aef27
commit ef9552735d
10 changed files with 262 additions and 43 deletions

View file

@ -174,6 +174,7 @@ devtools/client/debugger/test/mochitest/code_worker-source-map.js
devtools/client/framework/test/code_ugly*
devtools/client/inspector/markup/test/events_bundle.js
devtools/client/netmonitor/test/xhr_bundle.js
devtools/client/webconsole/new-console-output/test/mochitest/code_bundle_nosource.js
devtools/server/tests/unit/babel_and_browserify_script_with_source_map.js
devtools/server/tests/unit/setBreakpoint*
devtools/server/tests/unit/sourcemapped.js

View file

@ -549,17 +549,42 @@ Toolbox.prototype = {
// Provide a wrapper for the service that reports errors more nicely.
this._sourceMapService = new Proxy(service, {
get: (target, name) => {
if (name === "getOriginalURLs") {
return (urlInfo) => {
return target.getOriginalURLs(urlInfo)
.catch(text => {
let message = L10N.getFormatStr("toolbox.sourceMapFailure",
text, urlInfo.url, urlInfo.sourceMapURL);
this.target.logErrorInPage(message, "source map");
});
};
switch (name) {
case "getOriginalURLs":
return (urlInfo) => {
return target.getOriginalURLs(urlInfo)
.catch(text => {
let message = L10N.getFormatStr("toolbox.sourceMapFailure",
text, urlInfo.url,
urlInfo.sourceMapURL);
this.target.logErrorInPage(message, "source map");
// It's ok to swallow errors here, because a null
// result just means that no source map was found.
return null;
});
};
case "getOriginalSourceText":
return (originalSource) => {
return target.getOriginalSourceText(originalSource)
.catch(text => {
let message = L10N.getFormatStr("toolbox.sourceMapSourceFailure",
text, originalSource.url);
this.target.logErrorInPage(message, "source map");
// Also replace the result with the error text.
// Note that this result has to have the same form
// as whatever the upstream getOriginalSourceText
// returns.
return {
text: message,
contentType: "text/plain",
};
});
};
default:
return target[name];
}
return target[name];
},
});

View file

@ -184,3 +184,10 @@ toolbox.allToolsButton.tooltip=Select another tool
# The URL that caused DevTools to try to fetch a source map: %2$S
# The URL of the source map itself: %3$S
toolbox.sourceMapFailure=Source map error: %1$S\nResource URL: %2$S\nSource Map URL: %3$S
# LOCALIZATION NOTE (toolbox.sourceMapSourceFailure): This is shown in
# the web console when there is a failure to fetch or parse an
# original source that was mentioned in a source map.
# The text of the error: %1$S
# The URL of the source: %2$S
toolbox.sourceMapSourceFailure=Error while fetching an original source: %1$S\nSource URL: %2$S

View file

@ -2,6 +2,8 @@
tags = devtools
subsuite = devtools
support-files =
code_bundle_nosource.js
code_bundle_nosource.js.map
head.js
test-batching.html
test-console.html
@ -53,6 +55,7 @@ skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32
[browser_webconsole_observer_notifications.js]
[browser_webconsole_shows_reqs_in_netmonitor.js]
[browser_webconsole_sourcemap_error.js]
[browser_webconsole_sourcemap_nosource.js]
[browser_webconsole_stacktrace_location_debugger_link.js]
[browser_webconsole_stacktrace_location_scratchpad_link.js]
[browser_webconsole_string.js]

View file

@ -40,35 +40,3 @@ add_task(function* () {
yield toolbox.selectTool("webconsole");
yield testOpenInDebugger(hud, toolbox, "document.bar");
});
function* testOpenInDebugger(hud, toolbox, text) {
info(`Testing message with text "${text}"`);
let messageNode = yield waitFor(() => findMessage(hud, text));
let frameLinkNode = messageNode.querySelector(".message-location .frame-link");
ok(frameLinkNode, "The message does have a location link");
yield checkClickOnNode(hud, toolbox, frameLinkNode);
}
function* checkClickOnNode(hud, toolbox, frameLinkNode) {
info("checking click on node location");
let url = frameLinkNode.getAttribute("data-url");
ok(url, `source url found ("${url}")`);
let line = frameLinkNode.getAttribute("data-line");
ok(line, `source line found ("${line}")`);
let onSourceInDebuggerOpened = once(hud.ui, "source-in-debugger-opened");
EventUtils.sendMouseEvent({ type: "click" },
frameLinkNode.querySelector(".frame-link-filename"));
yield onSourceInDebuggerOpened;
let dbg = toolbox.getPanel("jsdebugger");
is(
dbg._selectors.getSelectedSource(dbg._getState()).get("url"),
url,
"expected source url"
);
}

View file

@ -0,0 +1,54 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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 a missing original source is reported.
const JS_URL = URL_ROOT + "code_bundle_nosource.js";
const PAGE_URL = `data:text/html,
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Empty test page to test source map with missing original source</title>
</head>
<body>
<script src="${JS_URL}"></script>
</body>
</html>`;
add_task(function* () {
// Force the new debugger UI, in case this gets uplifted with the old
// debugger still turned on
yield pushPref("devtools.debugger.new-debugger-frontend", true);
yield pushPref("devtools.source-map.client-service.enabled", true);
const hud = yield openNewTabAndConsole(PAGE_URL);
const toolbox = hud.ui.newConsoleOutput.toolbox;
info("Finding \"here\" message and waiting for source map to be applied");
yield waitFor(() => {
let node = findMessage(hud, "here");
if (!node) {
return false;
}
let frameLinkNode = node.querySelector(".message-location .frame-link");
let url = frameLinkNode.getAttribute("data-url");
return url.includes("nosuchfile");
});
yield testOpenInDebugger(hud, toolbox, "here");
info("Selecting the console again");
yield toolbox.selectTool("webconsole");
const node = yield waitFor(() => findMessage(hud, "original source"));
ok(node, "source map error is displayed in web console");
});

View file

@ -0,0 +1,93 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Original source code for the cross-domain source map test.
// The generated file was made with
// webpack --devtool nosources-source-map code_nosource.js code_bundle_nosource.js
// ... and then the bundle was edited to change the source name.
function f() {
console.log("here");
}
f();
// Avoid script GC.
window.f = f;
/***/ })
/******/ ]);
//# sourceMappingURL=code_bundle_nosource.js.map

View file

@ -0,0 +1 @@
{"version":3,"sources":["webpack:///webpack/bootstrap 5f603779212cf1264c9b","nosuchfile.js"],"names":[],"mappings":";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;AAEA;AACA;;;;;;;;AC7DA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA","file":"code_bundle_nosource.js","sourceRoot":""}

View file

@ -0,0 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Original source code for the cross-domain source map test.
// The generated file was made with
// webpack --devtool nosources-source-map code_nosource.js code_bundle_nosource.js
// ... and then the bundle was edited to change the source name.
"use strict";
function f() {
console.log("here");
}
f();
// Avoid script GC.
window.f = f;

View file

@ -4,7 +4,8 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from ../../../../framework/test/shared-head.js */
/* exported WCUL10n, openNewTabAndConsole, waitForMessages, waitFor, findMessage,
openContextMenu, hideContextMenu, loadDocument, waitForNodeMutation */
openContextMenu, hideContextMenu, loadDocument,
waitForNodeMutation, testOpenInDebugger, checkClickOnNode */
"use strict";
@ -203,3 +204,51 @@ function waitForNodeMutation(node, observeConfig = {}) {
observer.observe(node, observeConfig);
});
}
/**
* Search for a given message. When found, simulate a click on the
* message's location, checking to make sure that the debugger opens
* the corresponding URL.
*
* @param {Object} hud
* The webconsole
* @param {Object} toolbox
* The toolbox
* @param {String} text
* The text to search for. This should be contained in the
* message. The searching is done with @see findMessage.
*/
function* testOpenInDebugger(hud, toolbox, text) {
info(`Finding message for open-in-debugger test; text is "${text}"`);
let messageNode = yield waitFor(() => findMessage(hud, text));
let frameLinkNode = messageNode.querySelector(".message-location .frame-link");
ok(frameLinkNode, "The message does have a location link");
yield checkClickOnNode(hud, toolbox, frameLinkNode);
}
/**
* Helper function for testOpenInDebugger.
*/
function* checkClickOnNode(hud, toolbox, frameLinkNode) {
info("checking click on node location");
let url = frameLinkNode.getAttribute("data-url");
ok(url, `source url found ("${url}")`);
let line = frameLinkNode.getAttribute("data-line");
ok(line, `source line found ("${line}")`);
let onSourceInDebuggerOpened = once(hud.ui, "source-in-debugger-opened");
EventUtils.sendMouseEvent({ type: "click" },
frameLinkNode.querySelector(".frame-link-filename"));
yield onSourceInDebuggerOpened;
let dbg = toolbox.getPanel("jsdebugger");
is(
dbg._selectors.getSelectedSource(dbg._getState()).get("url"),
url,
"expected source url"
);
}