Bug 952127 - notify source map subscribers after pretty-printing; r=bgrins

Intercept the applySourceMap source map worker request, so that when a
source is pretty-printed, source map subscribers can be updated.

That this does not yet handle pretty-printing original sources.  This
isn't supported yet by the debugger, and since the plan is to handle it
by augmenting the existing source map, it should be easy to fix this
code when it is implemented.

The mochitest is included here for testing but I am going to land it
upstream as well.

MozReview-Commit-ID: 3Lp1ikO8IzZ

--HG--
extra : rebase_source : f2f02e9e963864567a9dbe3a7e050afcb5f4d3b6
This commit is contained in:
Tom Tromey 2017-09-15 07:54:56 -06:00
Родитель f0b3f3cf16
Коммит 19c7bf1424
4 изменённых файлов: 115 добавлений и 4 удалений

Просмотреть файл

@ -71,6 +71,7 @@ skip-if = os == "linux" # bug 1351952
skip-if = true # Bug 1393121
[browser_dbg-navigation.js]
[browser_dbg-pretty-print.js]
[browser_dbg-pretty-print-console.js]
[browser_dbg-pretty-print-paused.js]
[browser_dbg-scopes-mutations.js]
[browser_dbg-search-file.js]

Просмотреть файл

@ -0,0 +1,47 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that pretty-printing updates console messages.
async function waitFor(condition) {
await BrowserTestUtils.waitForCondition(condition, "waitFor", 10, 500);
return condition();
}
add_task(async function () {
const dbg = await initDebugger("doc-minified.html");
invokeInTab("arithmetic");
info("Switch to console and check message");
const toolbox = dbg.toolbox;
const console = await toolbox.selectTool("webconsole");
const hud = console.hud;
let node = await waitFor(() => hud.ui.outputNode.querySelector(".frame-link-source"));
const initialLocation = "math.min.js:3:65";
is(node.textContent, initialLocation, "location is correct in minified code");
info("Switch back to debugger and pretty-print");
await toolbox.selectTool("jsdebugger");
await selectSource(dbg, "math.min.js", 2);
clickElement(dbg, "prettyPrintButton");
await waitForSource(dbg, "math.min.js:formatted");
const ppSrc = findSource(dbg, "math.min.js:formatted");
ok(ppSrc, "Pretty-printed source exists");
info("Switch back to console and check message");
node = await waitFor(() => {
// Wait until the message updates.
const found = hud.ui.outputNode.querySelector(".frame-link-source");
if (found.textContent == initialLocation) {
return null;
}
return found;
});
is(node.textContent, "math.min.js:formatted:22", "location is correct in minified code");
});

Просмотреть файл

@ -8,9 +8,9 @@ const SOURCE_MAP_PREF = "devtools.source-map.client-service.enabled";
/**
* A simple service to track source actors and keep a mapping between
* original URLs and objects holding the source actor's ID (which is
* used as a cookie by the devtools-source-map service) and the source
* map URL.
* original URLs and objects holding the source or style actor's ID
* (which is used as a cookie by the devtools-source-map service) and
* the source map URL.
*
* @param {object} toolbox
* The toolbox.
@ -21,8 +21,16 @@ function SourceMapURLService(toolbox, sourceMapService) {
this._toolbox = toolbox;
this._target = toolbox.target;
this._sourceMapService = sourceMapService;
// Map from content URLs to descriptors. Descriptors are later
// passed to the source map worker.
this._urls = new Map();
// Map from (stringified) locations to callbacks that are called
// when the service decides a location should change (say, a source
// map is available or the user changes the pref).
this._subscriptions = new Map();
// A backward map from actor IDs to the original URL. This is used
// to support pretty-printing.
this._idMap = new Map();
this._onSourceUpdated = this._onSourceUpdated.bind(this);
this.reset = this.reset.bind(this);
@ -80,6 +88,7 @@ SourceMapURLService.prototype.reset = function () {
this._sourceMapService.clearSourceMaps();
this._urls.clear();
this._subscriptions.clear();
this._idMap.clear();
};
/**
@ -95,7 +104,7 @@ SourceMapURLService.prototype.destroy = function () {
this._stylesheetsFront.off("stylesheet-added", this._onNewStyleSheet);
}
Services.prefs.removeObserver(SOURCE_MAP_PREF, this._onPrefChanged);
this._target = this._urls = this._subscriptions = null;
this._target = this._urls = this._subscriptions = this._idMap = null;
};
/**
@ -114,6 +123,7 @@ SourceMapURLService.prototype._onSourceUpdated = function (_, sourceEvent) {
// source code by SpiderMonkey.
let seenUrl = generatedUrl || url;
this._urls.set(seenUrl, { id, url: seenUrl, sourceMapURL });
this._idMap.set(id, seenUrl);
};
/**
@ -130,6 +140,43 @@ SourceMapURLService.prototype._onNewStyleSheet = function (sheet) {
let {href: url, sourceMapURL, actor: id} = sheet._form;
this._urls.set(url, { id, url, sourceMapURL});
this._idMap.set(id, url);
};
/**
* A callback that is called from the lower-level source map service
* proxy (see toolbox.js) when some tool has installed a new source
* map. This happens when pretty-printing a source.
*
* @param {String} id
* The actor ID (used as a cookie here as elsewhere in this file)
* @param {String} newUrl
* The URL of the pretty-printed source
*/
SourceMapURLService.prototype.sourceMapChanged = function (id, newUrl) {
if (!this._urls) {
return;
}
let urlKey = this._idMap.get(id);
if (urlKey) {
// The source map URL here doesn't actually matter.
this._urls.set(urlKey, { id, url: newUrl, sourceMapURL: "" });
// Walk over all the location subscribers, looking for any that
// are subscribed to a location coming from |urlKey|. Then,
// re-notify any such subscriber by clearing the stored promise
// and forcing a re-evaluation.
for (let [, subscriptionEntry] of this._subscriptions) {
if (subscriptionEntry.url === urlKey) {
// Force an update.
subscriptionEntry.promise = null;
for (let callback of subscriptionEntry.callbacks) {
this._callOneCallback(subscriptionEntry, callback);
}
}
}
}
};
/**

Просмотреть файл

@ -603,6 +603,22 @@ Toolbox.prototype = {
});
};
case "applySourceMap":
return (generatedId, url, code, mappings) => {
return target.applySourceMap(generatedId, url, code, mappings)
.then(result => {
// If a tool has changed or introduced a source map
// (e.g, by pretty-printing a source), tell the
// source map URL service about the change, so that
// subscribers to that service can be updated as
// well.
if (this._sourceMapURLService) {
this._sourceMapURLService.sourceMapChanged(generatedId, url);
}
return result;
});
};
default:
return target[name];
}