Bug 1582266 - Use actual source text when mismatches are found with contents fetched over the network, r=loganfsmyth.

Differential Revision: https://phabricator.services.mozilla.com/D55402

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Brian Hackett 2019-12-11 14:22:12 +00:00
Родитель 0beec6773c
Коммит ee87de9d1b
5 изменённых файлов: 108 добавлений и 10 удалений

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

@ -184,3 +184,4 @@ skip-if = debug
skip-if = asan # Bug 1591064
[browser_dbg-toolbox-workers.js]
skip-if = asan || !nightly_build # Bug 1591064, parent intercept mode is needed bug 1588154
[browser_dbg-wrong-fetch.js]

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

@ -0,0 +1,25 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/>. */
// Test that incorrect source contents are not shown if the server refetches
// different HTML when attaching to an open page.
add_task(async function() {
await addTab(EXAMPLE_URL + "different_html.sjs");
// This goop is here to manually clear the HTTP cache because setting response
// headers in different_html.sjs to not cache the response doesn't work.
const cacheStorageSrv = SpecialPowers.Cc[
"@mozilla.org/netwerk/cache-storage-service;1"
].getService(Ci.nsICacheStorageService);
cacheStorageSrv.clear();
const toolbox = await openToolboxForTab(gBrowser.selectedTab, "jsdebugger");
const dbg = createDebuggerContext(toolbox);
await selectSource(dbg, "different_html.sjs");
await waitForLoadedSource(dbg, "different_html.sjs");
const contents = findSourceContent(dbg, "different_html.sjs");
ok(contents.value.includes("Incorrect contents fetched"), "Error message is shown");
});

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

@ -0,0 +1,31 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 contents = `
<div>Hello COUNTER</div>
<script>
function f() {
console.log("First Inline Script " + COUNTER);
}
setInterval(f, 1000);
</script>
<script>
function f() {
console.log("Second Inline Script " + COUNTER);
}
setInterval(f, 1000);
</script>
`;
function handleRequest(request, response) {
response.setHeader("Cache-Control", "no-store");
response.setHeader("Content-Type", "text/html");
let counter = 1 + (+getState("counter") % 4);
setState("counter", "" + counter);
response.write(contents.replace(/COUNTER/g, counter));
}

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

@ -239,16 +239,7 @@ const SourceActor = ActorClassWithSpec(sourceSpec, {
(this._contentType.includes("javascript") ||
this._contentType === "text/wasm")
) {
// If the source doesn't start at line 1, line numbers in the client will
// not match up with those in the source. Pad the text with blank lines to
// fix this. This can show up for sources associated with inline scripts
// in HTML created via document.write() calls: the script's source line
// number is relative to the start of the written HTML, but we show the
// source's content by itself.
const padding = this._source.startLine
? "\n".repeat(this._source.startLine - 1)
: "";
return toResolvedContent(padding + this._source.text);
return toResolvedContent(this.actualText());
}
const result = await this.sources.urlContents(
@ -263,6 +254,37 @@ const SourceActor = ActorClassWithSpec(sourceSpec, {
return result;
},
// Get the actual text of this source, padded so that line numbers will match
// up with the source itself.
actualText() {
// If the source doesn't start at line 1, line numbers in the client will
// not match up with those in the source. Pad the text with blank lines to
// fix this. This can show up for sources associated with inline scripts
// in HTML created via document.write() calls: the script's source line
// number is relative to the start of the written HTML, but we show the
// source's content by itself.
const padding = this._source.startLine
? "\n".repeat(this._source.startLine - 1)
: "";
return padding + this._source.text;
},
// Return whether the specified fetched contents includes the actual text of
// this source in the expected position.
contentMatches(fileContents) {
const lineBreak = /\r\n?|\n|\u2028|\u2029/;
const contentLines = fileContents.content.split(lineBreak);
const sourceLines = this._source.text.split(lineBreak);
let line = this._source.startLine - 1;
for (const sourceLine of sourceLines) {
const contentLine = contentLines[line++] || "";
if (!contentLine.includes(sourceLine)) {
return false;
}
}
return true;
},
getBreakableLines: async function() {
const positions = await this.getBreakpointPositions();
const lines = new Set();

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

@ -587,6 +587,25 @@ TabSources.prototype = {
throw error;
}
// When we fetch the contents, there is a risk that the contents we get
// do not match up with the actual text of the sources these contents will
// be associated with. We want to always show contents that include that
// actual text (otherwise it will be very confusing or unusable for users),
// so replace the contents with the actual text if there is a mismatch.
const actors = [...this._sourceActors.values()].filter(
actor => actor.url == url
);
if (!actors.every(actor => actor.contentMatches(result))) {
if (actors.length > 1) {
// When there are multiple actors we won't be able to show the source
// for all of them. Ask the user to reload so that we don't have to do
// any fetching.
result.content = "Error: Incorrect contents fetched, please reload.";
} else {
result.content = actors[0].actualText();
}
}
this._urlContents.set(url, { ...result, complete: true });
return result;