From 8f1e1f40c74f6e270e1caf609a2c8c72ff37b657 Mon Sep 17 00:00:00 2001
From: yami <34216515+kn-yami@users.noreply.github.com>
Date: Tue, 30 Jul 2019 15:27:59 +0200
Subject: [PATCH 1/5] Issue #1138 - Part 1: refactor the JSON Viewer stream
converter
Mozilla Bug 1367894
---
devtools/client/jsonview/converter-child.js | 354 +++++++++-----------
devtools/client/jsonview/css/general.css | 9 +-
devtools/client/jsonview/json-viewer.js | 4 +-
devtools/client/jsonview/utils.js | 2 +
4 files changed, 164 insertions(+), 205 deletions(-)
diff --git a/devtools/client/jsonview/converter-child.js b/devtools/client/jsonview/converter-child.js
index 61aa0c9a36..cf93161afa 100644
--- a/devtools/client/jsonview/converter-child.js
+++ b/devtools/client/jsonview/converter-child.js
@@ -23,10 +23,6 @@ const childProcessMessageManager =
Cc["@mozilla.org/childprocessmessagemanager;1"]
.getService(Ci.nsISyncMessageSender);
-// Amount of space that will be allocated for the stream's backing-store.
-// Must be power of 2. Used to copy the data stream in onStopRequest.
-const SEGMENT_SIZE = Math.pow(2, 17);
-
const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
const CONTRACT_ID = "@mozilla.org/streamconv;1?from=" +
JSON_VIEW_MIME_TYPE + "&to=*/*";
@@ -61,9 +57,9 @@ let Converter = Class({
* 1. asyncConvertData captures the listener
* 2. onStartRequest fires, initializes stuff, modifies the listener
* to match our output type
- * 3. onDataAvailable transcodes the data into a UTF-8 string
- * 4. onStopRequest gets the collected data and converts it,
- * spits it to the listener
+ * 3. onDataAvailable spits it back to the listener
+ * 4. onStopRequest spits it back to the listener and initializes
+ * the JSON Viewer
* 5. convert does nothing, it's just the synchronous version
* of asyncConvertData
*/
@@ -76,60 +72,52 @@ let Converter = Class({
},
onDataAvailable: function (request, context, inputStream, offset, count) {
- // From https://developer.mozilla.org/en/Reading_textual_data
- let is = Cc["@mozilla.org/intl/converter-input-stream;1"]
- .createInstance(Ci.nsIConverterInputStream);
- is.init(inputStream, this.charset, -1,
- Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
-
- // Seed it with something positive
- while (count) {
- let str = {};
- let bytesRead = is.readString(count, str);
- if (!bytesRead) {
- break;
- }
- count -= bytesRead;
- this.data += str.value;
- }
+ this.listener.onDataAvailable(...arguments);
},
onStartRequest: function (request, context) {
- this.data = "";
- this.uri = request.QueryInterface(Ci.nsIChannel).URI.spec;
-
- // Sets the charset if it is available. (For documents loaded from the
- // filesystem, this is not set.)
- this.charset =
- request.QueryInterface(Ci.nsIChannel).contentCharset || "UTF-8";
-
this.channel = request;
- this.channel.contentType = "text/html";
- this.channel.contentCharset = "UTF-8";
+
+ // Let "save as" save the original JSON, not the viewer.
+ // To save with the proper extension we need the original content type,
+ // which has been replaced by application/vnd.mozilla.json.view
+ let originalType;
+ if (request instanceof Ci.nsIHttpChannel) {
+ try {
+ let header = request.getResponseHeader("Content-Type");
+ originalType = header.split(";")[0];
+ } catch (err) {
+ // Handled below
+ }
+ } else {
+ let uri = request.QueryInterface(Ci.nsIChannel).URI.spec;
+ let match = uri.match(/^data:(.*?)[,;]/);
+ if (match) {
+ originalType = match[1];
+ }
+ }
+ const JSON_TYPES = ["application/json", "application/manifest+json"];
+ if (!JSON_TYPES.includes(originalType)) {
+ originalType = JSON_TYPES[0];
+ }
+ request.QueryInterface(Ci.nsIWritablePropertyBag);
+ request.setProperty("contentType", originalType);
+
+ // Parse source as JSON. This is like text/plain, but enforcing
+ // UTF-8 charset (see bug 741776).
+ request.QueryInterface(Ci.nsIChannel);
+ request.contentType = JSON_TYPES[0];
+ this.charset = request.contentCharset = "UTF-8";
+
// Because content might still have a reference to this window,
// force setting it to a null principal to avoid it being same-
// origin with (other) content.
- this.channel.loadInfo.resetPrincipalsToNullPrincipal();
+ request.loadInfo.resetPrincipalsToNullPrincipal();
- this.listener.onStartRequest(this.channel, context);
+ this.listener.onStartRequest(request, context);
},
- /**
- * This should go something like this:
- * 1. Make sure we have a unicode string.
- * 2. Convert it to a Javascript object.
- * 2.1 Removes the callback
- * 3. Convert that to HTML? Or XUL?
- * 4. Spit it back out at the listener
- */
onStopRequest: function (request, context, statusCode) {
- let headers = {
- response: [],
- request: []
- };
-
- let win = NetworkHelper.getWindowForRequest(request);
-
let Locale = {
$STR: key => {
try {
@@ -141,12 +129,10 @@ let Converter = Class({
}
};
- JsonViewUtils.exportIntoContentScope(win, Locale, "Locale");
-
- Events.once(win, "DOMContentLoaded", event => {
- win.addEventListener("contentMessage",
- this.onContentMessage.bind(this), false, true);
- });
+ let headers = {
+ response: [],
+ request: []
+ };
// The request doesn't have to be always nsIHttpChannel
// (e.g. in case of data: URLs)
@@ -156,7 +142,6 @@ let Converter = Class({
headers.response.push({name: name, value: value});
}
});
-
request.visitRequestHeaders({
visitHeader: function (name, value) {
headers.request.push({name: name, value: value});
@@ -164,156 +149,125 @@ let Converter = Class({
});
}
- let outputDoc = "";
+ let win = NetworkHelper.getWindowForRequest(request);
+ JsonViewUtils.exportIntoContentScope(win, Locale, "Locale");
+ JsonViewUtils.exportIntoContentScope(win, headers, "headers");
- try {
- headers = JSON.stringify(headers);
- outputDoc = this.toHTML(this.data, headers, this.uri);
- } catch (e) {
- console.error("JSON Viewer ERROR " + e);
- outputDoc = this.toErrorPage(e, this.data, this.uri);
- }
-
- let storage = Cc["@mozilla.org/storagestream;1"]
- .createInstance(Ci.nsIStorageStream);
-
- storage.init(SEGMENT_SIZE, 0xffffffff, null);
- let out = storage.getOutputStream(0);
-
- let binout = Cc["@mozilla.org/binaryoutputstream;1"]
- .createInstance(Ci.nsIBinaryOutputStream);
-
- binout.setOutputStream(out);
- binout.writeUtf8Z(outputDoc);
- binout.close();
-
- // We need to trim 4 bytes off the front (this could be underlying bug).
- let trunc = 4;
- let instream = storage.newInputStream(trunc);
-
- // Pass the data to the main content listener
- this.listener.onDataAvailable(this.channel, context, instream, 0,
- instream.available());
+ win.addEventListener("DOMContentLoaded", event => {
+ win.addEventListener("contentMessage",
+ onContentMessage.bind(this), false, true);
+ loadJsonViewer(win.document);
+ }, {once: true});
this.listener.onStopRequest(this.channel, context, statusCode);
-
this.listener = null;
- },
-
- htmlEncode: function (t) {
- return t !== null ? t.toString()
- .replace(/&/g, "&")
- .replace(/"/g, """)
- .replace(//g, ">") : "";
- },
-
- toHTML: function (json, headers, title) {
- let themeClassName = "theme-" + JsonViewUtils.getCurrentTheme();
- let clientBaseUrl = "resource://devtools/client/";
- let baseUrl = clientBaseUrl + "jsonview/";
- let themeVarsUrl = clientBaseUrl + "themes/variables.css";
- let commonUrl = clientBaseUrl + "themes/common.css";
- let toolbarsUrl = clientBaseUrl + "themes/toolbars.css";
-
- let os;
- let platform = Services.appinfo.OS;
- if (platform.startsWith("WINNT")) {
- os = "win";
- } else if (platform.startsWith("Darwin")) {
- os = "mac";
- } else {
- os = "linux";
- }
-
- return "\n" +
- "" +
- "
" + this.htmlEncode(title) + "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" + this.htmlEncode(json) + "
" +
- "" +
- "";
- },
-
- toErrorPage: function (error, data, uri) {
- // Escape unicode nulls
- data = data.replace("\u0000", "\uFFFD");
-
- let errorInfo = error + "";
-
- let output = "" + "error parsing";
- if (errorInfo.message) {
- output += "
" + errorInfo.message + "
";
- }
-
- output += "
" + this.highlightError(data,
- errorInfo.line, errorInfo.column) + "
";
-
- return "\n" +
- "" + this.htmlEncode(uri + " - Error") + "" +
- "" +
- "" +
- output +
- "";
- },
-
- // Chrome <-> Content communication
-
- onContentMessage: function (e) {
- // Do not handle events from different documents.
- let win = NetworkHelper.getWindowForRequest(this.channel);
- if (win != e.target) {
- return;
- }
-
- let value = e.detail.value;
- switch (e.detail.type) {
- case "copy":
- Clipboard.set(value, "text");
- break;
-
- case "copy-headers":
- this.copyHeaders(value);
- break;
-
- case "save":
- childProcessMessageManager.sendAsyncMessage(
- "devtools:jsonview:save", value);
- }
- },
-
- copyHeaders: function (headers) {
- let value = "";
- let eol = (Services.appinfo.OS !== "WINNT") ? "\n" : "\r\n";
-
- let responseHeaders = headers.response;
- for (let i = 0; i < responseHeaders.length; i++) {
- let header = responseHeaders[i];
- value += header.name + ": " + header.value + eol;
- }
-
- value += eol;
-
- let requestHeaders = headers.request;
- for (let i = 0; i < requestHeaders.length; i++) {
- let header = requestHeaders[i];
- value += header.name + ": " + header.value + eol;
- }
-
- Clipboard.set(value, "text");
}
});
+// Chrome <-> Content communication
+function onContentMessage(e) {
+ // Do not handle events from different documents.
+ let win = NetworkHelper.getWindowForRequest(this.channel);
+ if (win != e.target) {
+ return;
+ }
+
+ let value = e.detail.value;
+ switch (e.detail.type) {
+ case "copy":
+ copyString(win, value);
+ break;
+
+ case "copy-headers":
+ copyHeaders(win, value);
+ break;
+
+ case "save":
+ // The window ID is needed when the JSON Viewer is inside an iframe.
+ let windowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
+ childProcessMessageManager.sendAsyncMessage(
+ "devtools:jsonview:save", {url: value, windowID: windowID});
+ }
+}
+
+// Loads the JSON Viewer into a text/plain document
+function loadJsonViewer(doc) {
+ function addStyleSheet(url) {
+ let link = doc.createElement("link");
+ link.rel = "stylesheet";
+ link.type = "text/css";
+ link.href = url;
+ doc.head.appendChild(link);
+ }
+
+ let os;
+ let platform = Services.appinfo.OS;
+ if (platform.startsWith("WINNT")) {
+ os = "win";
+ } else if (platform.startsWith("Darwin")) {
+ os = "mac";
+ } else {
+ os = "linux";
+ }
+
+ doc.documentElement.setAttribute("platform", os);
+ doc.documentElement.dataset.contentType = doc.contentType;
+ doc.documentElement.classList.add("theme-" + JsonViewUtils.getCurrentTheme());
+ doc.documentElement.dir = Services.locale.isAppLocaleRTL ? "rtl" : "ltr";
+
+ let base = doc.createElement("base");
+ base.href = "resource://devtools/client/jsonview/";
+ doc.head.appendChild(base);
+
+ addStyleSheet("../themes/variables.css");
+ addStyleSheet("../themes/common.css");
+ addStyleSheet("../themes/toolbars.css");
+ addStyleSheet("css/main.css");
+
+ let json = doc.querySelector("pre");
+ json.id = "json";
+ let content = doc.createElement("div");
+ content.id = "content";
+ content.appendChild(json);
+ doc.body.appendChild(content);
+
+ let script = doc.createElement("script");
+ script.src = "lib/require.js";
+ script.dataset.main = "viewer-config";
+ doc.body.appendChild(script);
+}
+
+function copyHeaders(win, headers) {
+ let value = "";
+ let eol = (Services.appinfo.OS !== "WINNT") ? "\n" : "\r\n";
+
+ let responseHeaders = headers.response;
+ for (let i = 0; i < responseHeaders.length; i++) {
+ let header = responseHeaders[i];
+ value += header.name + ": " + header.value + eol;
+ }
+
+ value += eol;
+
+ let requestHeaders = headers.request;
+ for (let i = 0; i < requestHeaders.length; i++) {
+ let header = requestHeaders[i];
+ value += header.name + ": " + header.value + eol;
+ }
+
+ copyString(win, value);
+}
+
+function copyString(win, string) {
+ win.document.addEventListener("copy", event => {
+ event.clipboardData.setData("text/plain", string);
+ event.preventDefault();
+ }, {once: true});
+
+ win.document.execCommand("copy", false, null);
+}
+
// Stream converter component definition
let service = xpcom.Service({
id: components.ID(CLASS_ID),
diff --git a/devtools/client/jsonview/css/general.css b/devtools/client/jsonview/css/general.css
index 0c68d65e77..89b3b3c98e 100644
--- a/devtools/client/jsonview/css/general.css
+++ b/devtools/client/jsonview/css/general.css
@@ -28,8 +28,7 @@ pre {
font-family: var(--monospace-font-family);
}
-#json,
-#headers {
+#json {
display: none;
}
@@ -44,3 +43,9 @@ body.theme-dark {
.theme-dark pre {
background-color: var(--theme-body-background);
}
+
+/******************************************************************************/
+/* Fixes for quirks mode */
+table {
+ font: inherit;
+}
diff --git a/devtools/client/jsonview/json-viewer.js b/devtools/client/jsonview/json-viewer.js
index d96081da2f..bf86ebbea8 100644
--- a/devtools/client/jsonview/json-viewer.js
+++ b/devtools/client/jsonview/json-viewer.js
@@ -12,7 +12,6 @@ define(function (require, exports, module) {
const { MainTabbedArea } = createFactories(require("./components/main-tabbed-area"));
const json = document.getElementById("json");
- const headers = document.getElementById("headers");
let jsonData;
@@ -27,13 +26,12 @@ define(function (require, exports, module) {
jsonText: json.textContent,
jsonPretty: null,
json: jsonData,
- headers: JSON.parse(headers.textContent),
+ headers: window.headers,
tabActive: 0,
prettified: false
};
json.remove();
- headers.remove();
/**
* Application actions/commands. This list implements all commands
diff --git a/devtools/client/jsonview/utils.js b/devtools/client/jsonview/utils.js
index a70afdc68c..6ab697c892 100644
--- a/devtools/client/jsonview/utils.js
+++ b/devtools/client/jsonview/utils.js
@@ -96,6 +96,8 @@ exports.exportIntoContentScope = function (win, obj, defineAs) {
Cu.exportFunction(propValue, clone, {
defineAs: propName
});
+ } else {
+ clone[propName] = Cu.cloneInto(propValue, win);
}
}
};
From 32b3994a7b7fde810560676e87091f2c423a4a65 Mon Sep 17 00:00:00 2001
From: yami <34216515+kn-yami@users.noreply.github.com>
Date: Tue, 30 Jul 2019 15:42:58 +0200
Subject: [PATCH 2/5] Issue #1138 - Part 2: JSON Viewer should ignore BOM
Mozilla Bug 1395313
---
devtools/client/jsonview/json-viewer.js | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/devtools/client/jsonview/json-viewer.js b/devtools/client/jsonview/json-viewer.js
index bf86ebbea8..38cb6d7ecd 100644
--- a/devtools/client/jsonview/json-viewer.js
+++ b/devtools/client/jsonview/json-viewer.js
@@ -13,24 +13,26 @@ define(function (require, exports, module) {
const json = document.getElementById("json");
- let jsonData;
-
- try {
- jsonData = JSON.parse(json.textContent);
- } catch (err) {
- jsonData = err + "";
- }
-
// Application state object.
let input = {
jsonText: json.textContent,
jsonPretty: null,
- json: jsonData,
headers: window.headers,
tabActive: 0,
prettified: false
};
+ // Remove BOM, if present.
+ if (input.jsonText.startsWith("\ufeff")) {
+ input.jsonText = input.jsonText.slice(1);
+ }
+
+ try {
+ input.json = JSON.parse(input.jsonText);
+ } catch (err) {
+ input.json = err;
+ }
+
json.remove();
/**
@@ -59,7 +61,7 @@ define(function (require, exports, module) {
theApp.setState({jsonText: input.jsonText});
} else {
if (!input.jsonPretty) {
- input.jsonPretty = JSON.stringify(jsonData, null, " ");
+ input.jsonPretty = JSON.stringify(input.json, null, " ");
}
theApp.setState({jsonText: input.jsonPretty});
}
From 55d401707c14de0c497a5635ef280ab2c43ea884 Mon Sep 17 00:00:00 2001
From: yami <34216515+kn-yami@users.noreply.github.com>
Date: Tue, 30 Jul 2019 16:09:52 +0200
Subject: [PATCH 3/5] Issue #1138 - Part 3: avoid quirks mode in JSON Viewer
Mozilla Bug 1368899
---
devtools/client/jsonview/converter-child.js | 268 +++++++++++---------
devtools/client/jsonview/css/general.css | 7 +-
devtools/client/jsonview/css/main.css | 4 +-
3 files changed, 149 insertions(+), 130 deletions(-)
diff --git a/devtools/client/jsonview/converter-child.js b/devtools/client/jsonview/converter-child.js
index cf93161afa..1be3424740 100644
--- a/devtools/client/jsonview/converter-child.js
+++ b/devtools/client/jsonview/converter-child.js
@@ -58,8 +58,7 @@ let Converter = Class({
* 2. onStartRequest fires, initializes stuff, modifies the listener
* to match our output type
* 3. onDataAvailable spits it back to the listener
- * 4. onStopRequest spits it back to the listener and initializes
- * the JSON Viewer
+ * 4. onStopRequest spits it back to the listener
* 5. convert does nothing, it's just the synchronous version
* of asyncConvertData
*/
@@ -76,98 +75,168 @@ let Converter = Class({
},
onStartRequest: function (request, context) {
- this.channel = request;
-
- // Let "save as" save the original JSON, not the viewer.
- // To save with the proper extension we need the original content type,
- // which has been replaced by application/vnd.mozilla.json.view
- let originalType;
- if (request instanceof Ci.nsIHttpChannel) {
- try {
- let header = request.getResponseHeader("Content-Type");
- originalType = header.split(";")[0];
- } catch (err) {
- // Handled below
- }
- } else {
- let uri = request.QueryInterface(Ci.nsIChannel).URI.spec;
- let match = uri.match(/^data:(.*?)[,;]/);
- if (match) {
- originalType = match[1];
- }
- }
- const JSON_TYPES = ["application/json", "application/manifest+json"];
- if (!JSON_TYPES.includes(originalType)) {
- originalType = JSON_TYPES[0];
- }
- request.QueryInterface(Ci.nsIWritablePropertyBag);
- request.setProperty("contentType", originalType);
-
- // Parse source as JSON. This is like text/plain, but enforcing
- // UTF-8 charset (see bug 741776).
+ // Set the content type to HTML in order to parse the doctype, styles
+ // and scripts, but later a element will switch the tokenizer
+ // to the plaintext state in order to parse the JSON.
request.QueryInterface(Ci.nsIChannel);
- request.contentType = JSON_TYPES[0];
- this.charset = request.contentCharset = "UTF-8";
+ request.contentType = "text/html";
+
+ // JSON enforces UTF-8 charset (see bug 741776).
+ request.contentCharset = "UTF-8";
+
+ // Changing the content type breaks saving functionality. Fix it.
+ fixSave(request);
// Because content might still have a reference to this window,
// force setting it to a null principal to avoid it being same-
// origin with (other) content.
request.loadInfo.resetPrincipalsToNullPrincipal();
+ // Start the request.
this.listener.onStartRequest(request, context);
+
+ // Initialize stuff.
+ let win = NetworkHelper.getWindowForRequest(request);
+ exportData(win, request);
+ win.addEventListener("DOMContentLoaded", event => {
+ win.addEventListener("contentMessage", onContentMessage, false, true);
+ }, {once: true});
+
+ // Insert the initial HTML code.
+ let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "UTF-8";
+ let stream = converter.convertToInputStream(initialHTML(win.document));
+ this.listener.onDataAvailable(request, context, stream, 0, stream.available());
},
onStopRequest: function (request, context, statusCode) {
- let Locale = {
- $STR: key => {
- try {
- return jsonViewStrings.GetStringFromName(key);
- } catch (err) {
- console.error(err);
- return undefined;
- }
- }
- };
-
- let headers = {
- response: [],
- request: []
- };
-
- // The request doesn't have to be always nsIHttpChannel
- // (e.g. in case of data: URLs)
- if (request instanceof Ci.nsIHttpChannel) {
- request.visitResponseHeaders({
- visitHeader: function (name, value) {
- headers.response.push({name: name, value: value});
- }
- });
- request.visitRequestHeaders({
- visitHeader: function (name, value) {
- headers.request.push({name: name, value: value});
- }
- });
- }
-
- let win = NetworkHelper.getWindowForRequest(request);
- JsonViewUtils.exportIntoContentScope(win, Locale, "Locale");
- JsonViewUtils.exportIntoContentScope(win, headers, "headers");
-
- win.addEventListener("DOMContentLoaded", event => {
- win.addEventListener("contentMessage",
- onContentMessage.bind(this), false, true);
- loadJsonViewer(win.document);
- }, {once: true});
-
- this.listener.onStopRequest(this.channel, context, statusCode);
+ this.listener.onStopRequest(request, context, statusCode);
this.listener = null;
}
});
+// Lets "save as" save the original JSON, not the viewer.
+// To save with the proper extension we need the original content type,
+// which has been replaced by application/vnd.mozilla.json.view
+function fixSave(request) {
+ let originalType;
+ if (request instanceof Ci.nsIHttpChannel) {
+ try {
+ let header = request.getResponseHeader("Content-Type");
+ originalType = header.split(";")[0];
+ } catch (err) {
+ // Handled below
+ }
+ } else {
+ let uri = request.QueryInterface(Ci.nsIChannel).URI.spec;
+ let match = uri.match(/^data:(.*?)[,;]/);
+ if (match) {
+ originalType = match[1];
+ }
+ }
+ const JSON_TYPES = ["application/json", "application/manifest+json"];
+ if (!JSON_TYPES.includes(originalType)) {
+ originalType = JSON_TYPES[0];
+ }
+ request.QueryInterface(Ci.nsIWritablePropertyBag);
+ request.setProperty("contentType", originalType);
+}
+
+// Exports variables that will be accessed by the non-privileged scripts.
+function exportData(win, request) {
+ let Locale = {
+ $STR: key => {
+ try {
+ return jsonViewStrings.GetStringFromName(key);
+ } catch (err) {
+ console.error(err);
+ return undefined;
+ }
+ }
+ };
+ JsonViewUtils.exportIntoContentScope(win, Locale, "Locale");
+
+ let headers = {
+ response: [],
+ request: []
+ };
+ // The request doesn't have to be always nsIHttpChannel
+ // (e.g. in case of data: URLs)
+ if (request instanceof Ci.nsIHttpChannel) {
+ request.visitResponseHeaders({
+ visitHeader: function (name, value) {
+ headers.response.push({name: name, value: value});
+ }
+ });
+ request.visitRequestHeaders({
+ visitHeader: function (name, value) {
+ headers.request.push({name: name, value: value});
+ }
+ });
+ }
+ JsonViewUtils.exportIntoContentScope(win, headers, "headers");
+}
+
+// Serializes a qualifiedName and an optional set of attributes into an HTML
+// start tag. Be aware qualifiedName and attribute names are not validated.
+// Attribute values are escaped with escapingString algorithm in attribute mode
+// (https://html.spec.whatwg.org/multipage/syntax.html#escapingString).
+function startTag(qualifiedName, attributes = {}) {
+ return Object.entries(attributes).reduce(function (prev, [attr, value]) {
+ return prev + " " + attr + "=\"" +
+ value.replace(/&/g, "&")
+ .replace(/\u00a0/g, " ")
+ .replace(/"/g, """) +
+ "\"";
+ }, "<" + qualifiedName) + ">";
+}
+
+// Builds an HTML string that will be used to load stylesheets and scripts,
+// and switch the parser to plaintext state.
+function initialHTML(doc) {
+ let os;
+ let platform = Services.appinfo.OS;
+ if (platform.startsWith("WINNT")) {
+ os = "win";
+ } else if (platform.startsWith("Darwin")) {
+ os = "mac";
+ } else {
+ os = "linux";
+ }
+
+ let base = doc.createElement("base");
+ base.href = "resource://devtools/client/jsonview/";
+
+ let style = doc.createElement("link");
+ style.rel = "stylesheet";
+ style.type = "text/css";
+ style.href = "css/main.css";
+
+ let script = doc.createElement("script");
+ script.src = "lib/require.js";
+ script.dataset.main = "viewer-config";
+ script.defer = true;
+
+ let head = doc.createElement("head");
+ head.append(base, style, script);
+
+ return "\n" +
+ startTag("html", {
+ "platform": os,
+ "class": "theme-" + JsonViewUtils.getCurrentTheme(),
+ "dir": Services.locale.isAppLocaleRTL ? "rtl" : "ltr"
+ }) +
+ head.outerHTML +
+ startTag("body") +
+ startTag("div", {"id": "content"}) +
+ startTag("plaintext", {"id": "json"});
+}
+
// Chrome <-> Content communication
function onContentMessage(e) {
// Do not handle events from different documents.
- let win = NetworkHelper.getWindowForRequest(this.channel);
+ let win = this;
if (win != e.target) {
return;
}
@@ -191,53 +260,6 @@ function onContentMessage(e) {
}
}
-// Loads the JSON Viewer into a text/plain document
-function loadJsonViewer(doc) {
- function addStyleSheet(url) {
- let link = doc.createElement("link");
- link.rel = "stylesheet";
- link.type = "text/css";
- link.href = url;
- doc.head.appendChild(link);
- }
-
- let os;
- let platform = Services.appinfo.OS;
- if (platform.startsWith("WINNT")) {
- os = "win";
- } else if (platform.startsWith("Darwin")) {
- os = "mac";
- } else {
- os = "linux";
- }
-
- doc.documentElement.setAttribute("platform", os);
- doc.documentElement.dataset.contentType = doc.contentType;
- doc.documentElement.classList.add("theme-" + JsonViewUtils.getCurrentTheme());
- doc.documentElement.dir = Services.locale.isAppLocaleRTL ? "rtl" : "ltr";
-
- let base = doc.createElement("base");
- base.href = "resource://devtools/client/jsonview/";
- doc.head.appendChild(base);
-
- addStyleSheet("../themes/variables.css");
- addStyleSheet("../themes/common.css");
- addStyleSheet("../themes/toolbars.css");
- addStyleSheet("css/main.css");
-
- let json = doc.querySelector("pre");
- json.id = "json";
- let content = doc.createElement("div");
- content.id = "content";
- content.appendChild(json);
- doc.body.appendChild(content);
-
- let script = doc.createElement("script");
- script.src = "lib/require.js";
- script.dataset.main = "viewer-config";
- doc.body.appendChild(script);
-}
-
function copyHeaders(win, headers) {
let value = "";
let eol = (Services.appinfo.OS !== "WINNT") ? "\n" : "\r\n";
diff --git a/devtools/client/jsonview/css/general.css b/devtools/client/jsonview/css/general.css
index 89b3b3c98e..d80720f4fe 100644
--- a/devtools/client/jsonview/css/general.css
+++ b/devtools/client/jsonview/css/general.css
@@ -30,6 +30,7 @@ pre {
#json {
display: none;
+ white-space: pre-wrap;
}
/******************************************************************************/
@@ -43,9 +44,3 @@ body.theme-dark {
.theme-dark pre {
background-color: var(--theme-body-background);
}
-
-/******************************************************************************/
-/* Fixes for quirks mode */
-table {
- font: inherit;
-}
diff --git a/devtools/client/jsonview/css/main.css b/devtools/client/jsonview/css/main.css
index 04f3cb87ca..c75d7cea0c 100644
--- a/devtools/client/jsonview/css/main.css
+++ b/devtools/client/jsonview/css/main.css
@@ -3,7 +3,9 @@
* 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/. */
-@import "resource://devtools/client/shared/components/reps/reps.css";
+@import "resource://devtools/client/themes/variables.css";
+@import "resource://devtools/client/themes/common.css";
+@import "resource://devtools/client/themes/toolbars.css";
@import "resource://devtools/client/shared/components/tree/tree-view.css";
@import "resource://devtools/client/shared/components/tabs/tabs.css";
From 73cdb3106e2ea080e7f3937858ef7c71361f0846 Mon Sep 17 00:00:00 2001
From: yami <34216515+kn-yami@users.noreply.github.com>
Date: Thu, 1 Aug 2019 02:18:45 +0200
Subject: [PATCH 4/5] Issue #1138 - Part 4: fix JSON Viewer save functionality
Saving JSON from the viewer was broken, because the message passed to
the `saveToFile` function contained unneeded data. This bug was
introduced by 23e68227a2e3f3946fa4fd5589f338e6b36a6e56.
---
devtools/client/jsonview/converter-child.js | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/devtools/client/jsonview/converter-child.js b/devtools/client/jsonview/converter-child.js
index 1be3424740..65327c3953 100644
--- a/devtools/client/jsonview/converter-child.js
+++ b/devtools/client/jsonview/converter-child.js
@@ -252,11 +252,8 @@ function onContentMessage(e) {
break;
case "save":
- // The window ID is needed when the JSON Viewer is inside an iframe.
- let windowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
childProcessMessageManager.sendAsyncMessage(
- "devtools:jsonview:save", {url: value, windowID: windowID});
+ "devtools:jsonview:save", value);
}
}
From e6f376f5efc45928efc0b508c1a2ef5b2833dcf5 Mon Sep 17 00:00:00 2001
From: win7-7
Date: Thu, 8 Aug 2019 16:44:51 +0300
Subject: [PATCH 5/5] Convert dom/base/nsImageLoadingContent.cpp to use
AsyncOpen2 and followups along with it (1445670 and 1373780 part 2 and 3)
Convert dom/base/nsImageLoadingContent.cpp to use AsyncOpen2 and followups along with it (1445670 and 1373780 part 2 and 3)
---
dom/base/nsContentPolicy.cpp | 21 ++++
dom/base/nsContentUtils.cpp | 9 +-
dom/base/nsDocument.cpp | 22 ++--
dom/base/nsDocument.h | 6 +-
dom/base/nsIDocument.h | 10 +-
dom/base/nsIImageLoadingContent.idl | 9 ++
dom/base/nsImageLoadingContent.cpp | 101 +++++++++---------
dom/base/nsImageLoadingContent.h | 17 +--
dom/security/nsContentSecurityManager.cpp | 5 +-
image/imgLoader.cpp | 1 +
netwerk/protocol/http/nsCORSListenerProxy.cpp | 3 +
parser/html/nsHtml5TreeOpExecutor.cpp | 5 +-
12 files changed, 130 insertions(+), 79 deletions(-)
diff --git a/dom/base/nsContentPolicy.cpp b/dom/base/nsContentPolicy.cpp
index 5511b90864..534466103c 100644
--- a/dom/base/nsContentPolicy.cpp
+++ b/dom/base/nsContentPolicy.cpp
@@ -22,6 +22,7 @@
#include "nsIDOMWindow.h"
#include "nsITabChild.h"
#include "nsIContent.h"
+#include "nsIImageLoadingContent.h"
#include "nsILoadContext.h"
#include "nsCOMArray.h"
#include "nsContentUtils.h"
@@ -145,6 +146,16 @@ nsContentPolicy::CheckPolicy(CPMethod policyMethod,
decision);
if (NS_SUCCEEDED(rv) && NS_CP_REJECTED(*decision)) {
+ // If we are blocking an image, we have to let the
+ // ImageLoadingContent know that we blocked the load.
+ if (externalType == nsIContentPolicy::TYPE_IMAGE ||
+ externalType == nsIContentPolicy::TYPE_IMAGESET) {
+ nsCOMPtr img =
+ do_QueryInterface(requestingContext);
+ if (img) {
+ img->SetBlockedRequest(*decision);
+ }
+ }
/* policy says no, no point continuing to check */
return NS_OK;
}
@@ -193,6 +204,16 @@ nsContentPolicy::CheckPolicy(CPMethod policyMethod,
decision);
if (NS_SUCCEEDED(rv) && NS_CP_REJECTED(*decision)) {
+ // If we are blocking an image, we have to let the
+ // ImageLoadingContent know that we blocked the load.
+ if (externalType == nsIContentPolicy::TYPE_IMAGE ||
+ externalType == nsIContentPolicy::TYPE_IMAGESET) {
+ nsCOMPtr img =
+ do_QueryInterface(requestingContext);
+ if (img) {
+ img->SetBlockedRequest(*decision);
+ }
+ }
/* policy says no, no point continuing to check */
return NS_OK;
}
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 1f9c179472..800f40fa1e 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8478,12 +8478,9 @@ nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(nsContentPolicyType
bool
nsContentUtils::IsPreloadType(nsContentPolicyType aType)
{
- if (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
- aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
- aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD) {
- return true;
- }
- return false;
+ return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
+ aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
+ aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD);
}
nsresult
diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
index d8abc174fc..6b8e11db05 100644
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -9222,19 +9222,23 @@ already_AddRefed
nsDocument::ResolvePreloadImage(nsIURI *aBaseURI,
const nsAString& aSrcAttr,
const nsAString& aSrcsetAttr,
- const nsAString& aSizesAttr)
+ const nsAString& aSizesAttr,
+ bool *aIsImgSet)
{
nsString sourceURL;
+ bool isImgSet;
if (mPreloadPictureDepth == 1 && !mPreloadPictureFoundSource.IsVoid()) {
// We're in a element and found a URI from a source previous to
// this image, use it.
sourceURL = mPreloadPictureFoundSource;
+ isImgSet = true;
} else {
// Otherwise try to use this
as a source
HTMLImageElement::SelectSourceForTagWithAttrs(this, false, aSrcAttr,
aSrcsetAttr, aSizesAttr,
NullString(), NullString(),
sourceURL);
+ isImgSet = !aSrcsetAttr.IsEmpty();
}
// Empty sources are not loaded by
(i.e. not resolved to the baseURI)
@@ -9252,6 +9256,8 @@ nsDocument::ResolvePreloadImage(nsIURI *aBaseURI,
return nullptr;
}
+ *aIsImgSet = isImgSet;
+
// We don't clear mPreloadPictureFoundSource because subsequent
tags in
// this this share the same (though this is not valid per
// spec)
@@ -9260,16 +9266,12 @@ nsDocument::ResolvePreloadImage(nsIURI *aBaseURI,
void
nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
- ReferrerPolicy aReferrerPolicy)
+ ReferrerPolicy aReferrerPolicy, bool aIsImgSet)
{
// Early exit if the img is already present in the img-cache
// which indicates that the "real" load has already started and
// that we shouldn't preload it.
- int16_t blockingStatus;
- if (nsContentUtils::IsImageInCache(uri, static_cast(this)) ||
- !nsContentUtils::CanLoadImage(uri, static_cast(this),
- this, NodePrincipal(), &blockingStatus,
- nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD)) {
+ if (nsContentUtils::IsImageInCache(uri, static_cast(this))) {
return;
}
@@ -9288,6 +9290,10 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
MOZ_CRASH("Unknown CORS mode!");
}
+ nsContentPolicyType policyType =
+ aIsImgSet ? nsIContentPolicy::TYPE_IMAGESET :
+ nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD;
+
// Image not in cache - trigger preload
RefPtr request;
nsresult rv =
@@ -9301,7 +9307,7 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
loadFlags,
NS_LITERAL_STRING("img"),
getter_AddRefs(request),
- nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD);
+ policyType);
// Pin image-reference to avoid evicting it from the img-cache before
// the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h
index 95fd57545b..2b29b98fa5 100644
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -948,11 +948,13 @@ public:
ResolvePreloadImage(nsIURI *aBaseURI,
const nsAString& aSrcAttr,
const nsAString& aSrcsetAttr,
- const nsAString& aSizesAttr) override;
+ const nsAString& aSizesAttr,
+ bool *aIsImgSet) override;
virtual void MaybePreLoadImage(nsIURI* uri,
const nsAString &aCrossOriginAttr,
- ReferrerPolicy aReferrerPolicy) override;
+ ReferrerPolicy aReferrerPolicy,
+ bool aIsImgSet) override;
virtual void ForgetImagePreload(nsIURI* aURI) override;
virtual void MaybePreconnect(nsIURI* uri,
diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h
index e5d12ab8f6..d76a12d713 100644
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2260,21 +2260,27 @@ public:
* nesting and possible sources, which are used to inform URL selection
* responsive or
images. Unset attributes are expected
* to be marked void.
+ * If this image is for or
, aIsImgSet will be set to
+ * true, false otherwise.
*/
virtual already_AddRefed
ResolvePreloadImage(nsIURI *aBaseURI,
const nsAString& aSrcAttr,
const nsAString& aSrcsetAttr,
- const nsAString& aSizesAttr) = 0;
+ const nsAString& aSizesAttr,
+ bool *aIsImgSet) = 0;
/**
* Called by nsParser to preload images. Can be removed and code moved
* to nsPreloadURIs::PreloadURIs() in file nsParser.cpp whenever the
* parser-module is linked with gklayout-module. aCrossOriginAttr should
* be a void string if the attr is not present.
+ * aIsImgSet is the value got from calling ResolvePreloadImage, it is true
+ * when this image is for loading or
images.
*/
virtual void MaybePreLoadImage(nsIURI* uri,
const nsAString& aCrossOriginAttr,
- ReferrerPolicyEnum aReferrerPolicy) = 0;
+ ReferrerPolicyEnum aReferrerPolicy,
+ bool aIsImgSet) = 0;
/**
* Called by images to forget an image preload when they start doing
diff --git a/dom/base/nsIImageLoadingContent.idl b/dom/base/nsIImageLoadingContent.idl
index fea261a34c..eacc4ac3a9 100644
--- a/dom/base/nsIImageLoadingContent.idl
+++ b/dom/base/nsIImageLoadingContent.idl
@@ -103,6 +103,15 @@ interface nsIImageLoadingContent : imgINotificationObserver
*/
imgIRequest getRequest(in long aRequestType);
+ /**
+ * Call this function when the request was blocked by any of the
+ * security policies enforced.
+ *
+ * @param aContentDecision the decision returned from nsIContentPolicy
+ * (any of the types REJECT_*)
+ */
+ void setBlockedRequest(in int16_t aContentDecision);
+
/**
* @return true if the current request's size is available.
*/
diff --git a/dom/base/nsImageLoadingContent.cpp b/dom/base/nsImageLoadingContent.cpp
index 0c6c37b443..4aad55941c 100644
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -44,6 +44,7 @@
#include "mozAutoDocUpdate.h"
#include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/AutoRestore.h"
#include "mozilla/EventStates.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ImageTracker.h"
@@ -94,7 +95,8 @@ nsImageLoadingContent::nsImageLoadingContent()
mNewRequestsWillNeedAnimationReset(false),
mStateChangerDepth(0),
mCurrentRequestRegistered(false),
- mPendingRequestRegistered(false)
+ mPendingRequestRegistered(false),
+ mIsStartingImageLoad(false)
{
if (!nsContentUtils::GetImgLoaderForChannel(nullptr, nullptr)) {
mLoadingEnabled = false;
@@ -785,6 +787,11 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
nsIDocument* aDocument,
nsLoadFlags aLoadFlags)
{
+ MOZ_ASSERT(!mIsStartingImageLoad, "some evil code is reentering LoadImage.");
+ if (mIsStartingImageLoad) {
+ return NS_OK;
+ }
+
// Pending load/error events need to be canceled in some situations. This
// is not documented in the spec, but can cause site compat problems if not
// done. See bug 1309461 and https://github.com/whatwg/html/issues/1872.
@@ -814,6 +821,21 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
}
}
+ AutoRestore guard(mIsStartingImageLoad);
+ mIsStartingImageLoad = true;
+
+ // Data documents, or documents from DOMParser shouldn't perform image loading.
+ if (aDocument->IsLoadedAsData()) {
+ // This is the only codepath on which we can reach SetBlockedRequest while
+ // our pending request exists. Just clear it out here if we do have one.
+ ClearPendingRequest(NS_BINDING_ABORTED,
+ Some(OnNonvisible::DISCARD_IMAGES));
+ SetBlockedRequest(nsIContentPolicy::REJECT_REQUEST);
+ FireEvent(NS_LITERAL_STRING("error"));
+ FireEvent(NS_LITERAL_STRING("loadend"));
+ return NS_OK;
+ }
+
// URI equality check.
//
// We skip the equality check if our current image was blocked, since in that
@@ -844,23 +866,8 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
"Principal mismatch?");
#endif
- // Are we blocked?
- int16_t cpDecision = nsIContentPolicy::REJECT_REQUEST;
nsContentPolicyType policyType = PolicyTypeForLoad(aImageLoadType);
- nsContentUtils::CanLoadImage(aNewURI,
- static_cast(this),
- aDocument,
- aDocument->NodePrincipal(),
- &cpDecision,
- policyType);
- if (!NS_CP_ACCEPTED(cpDecision)) {
- FireEvent(NS_LITERAL_STRING("error"));
- FireEvent(NS_LITERAL_STRING("loadend"));
- SetBlockedRequest(aNewURI, cpDecision);
- return NS_OK;
- }
-
nsLoadFlags loadFlags = aLoadFlags;
int32_t corsmode = GetCORSMode();
if (corsmode == CORS_ANONYMOUS) {
@@ -878,7 +885,6 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
referrerPolicy = imgReferrerPolicy;
}
- // Not blocked. Do the load.
RefPtr& req = PrepareNextRequest(aImageLoadType);
nsCOMPtr content =
do_QueryInterface(static_cast(this));
@@ -932,7 +938,6 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
FireEvent(NS_LITERAL_STRING("error"));
FireEvent(NS_LITERAL_STRING("loadend"));
- return NS_OK;
}
return NS_OK;
@@ -1212,46 +1217,42 @@ nsImageLoadingContent::PrepareNextRequest(ImageLoadType aImageLoadType)
mMostRecentRequestChange = now;
}
- // If we don't have a usable current request, get rid of any half-baked
- // request that might be sitting there and make this one current.
- if (!HaveSize(mCurrentRequest))
- return PrepareCurrentRequest(aImageLoadType);
- // Otherwise, make it pending.
- return PreparePendingRequest(aImageLoadType);
+ // We only want to cancel the existing current request if size is not
+ // available. bz says the web depends on this behavior.
+ // Otherwise, we get rid of any half-baked request that might be sitting there
+ // and make this one current.
+ // TODO: Bug 583491
+ // Investigate/Cleanup NS_ERROR_IMAGE_SRC_CHANGED use in nsImageFrame.cpp
+ return HaveSize(mCurrentRequest) ?
+ PreparePendingRequest(aImageLoadType) :
+ PrepareCurrentRequest(aImageLoadType);
}
-void
-nsImageLoadingContent::SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision)
+nsresult
+nsImageLoadingContent::SetBlockedRequest(int16_t aContentDecision)
{
+ // If this is not calling from LoadImage, for example, from ServiceWorker,
+ // bail out.
+ if (!mIsStartingImageLoad) {
+ return NS_OK;
+ }
+
// Sanity
MOZ_ASSERT(!NS_CP_ACCEPTED(aContentDecision), "Blocked but not?");
- // We do some slightly illogical stuff here to maintain consistency with
- // old behavior that people probably depend on. Even in the case where the
- // new image is blocked, the old one should really be canceled with the
- // reason "image source changed". However, apparently there's some abuse
- // over in nsImageFrame where the displaying of the "broken" icon for the
- // next image depends on the cancel reason of the previous image. ugh.
- // XXX(seth): So shouldn't we fix nsImageFrame?!
- ClearPendingRequest(NS_ERROR_IMAGE_BLOCKED,
- Some(OnNonvisible::DISCARD_IMAGES));
-
- // For the blocked case, we only want to cancel the existing current request
- // if size is not available. bz says the web depends on this behavior.
- if (!HaveSize(mCurrentRequest)) {
+ // We should never have a pending request after we got blocked.
+ MOZ_ASSERT(!mPendingRequest, "mPendingRequest should be null.");
+ if (HaveSize(mCurrentRequest)) {
+ // PreparePendingRequest set mPendingRequestFlags, now since we've decided
+ // to block it, we reset it back to 0.
+ mPendingRequestFlags = 0;
+ } else {
mImageBlockingStatus = aContentDecision;
- uint32_t keepFlags = mCurrentRequestFlags & REQUEST_IS_IMAGESET;
- ClearCurrentRequest(NS_ERROR_IMAGE_BLOCKED,
- Some(OnNonvisible::DISCARD_IMAGES));
-
- // We still want to remember what URI we were and if it was an imageset,
- // despite not having an actual request. These are both cleared as part of
- // ClearCurrentRequest() before a new request is started.
- mCurrentURI = aURI;
- mCurrentRequestFlags = keepFlags;
}
+
+ return NS_OK;
}
RefPtr&
@@ -1262,7 +1263,7 @@ nsImageLoadingContent::PrepareCurrentRequest(ImageLoadType aImageLoadType)
mImageBlockingStatus = nsIContentPolicy::ACCEPT;
// Get rid of anything that was there previously.
- ClearCurrentRequest(NS_ERROR_IMAGE_SRC_CHANGED,
+ ClearCurrentRequest(NS_BINDING_ABORTED,
Some(OnNonvisible::DISCARD_IMAGES));
if (mNewRequestsWillNeedAnimationReset) {
@@ -1281,7 +1282,7 @@ RefPtr&
nsImageLoadingContent::PreparePendingRequest(ImageLoadType aImageLoadType)
{
// Get rid of anything that was there previously.
- ClearPendingRequest(NS_ERROR_IMAGE_SRC_CHANGED,
+ ClearPendingRequest(NS_BINDING_ABORTED,
Some(OnNonvisible::DISCARD_IMAGES));
if (mNewRequestsWillNeedAnimationReset) {
diff --git a/dom/base/nsImageLoadingContent.h b/dom/base/nsImageLoadingContent.h
index 5f7daff72f..cfb2a62072 100644
--- a/dom/base/nsImageLoadingContent.h
+++ b/dom/base/nsImageLoadingContent.h
@@ -302,18 +302,11 @@ protected:
*/
RefPtr& PrepareNextRequest(ImageLoadType aImageLoadType);
- /**
- * Called when we would normally call PrepareNextRequest(), but the request was
- * blocked.
- */
- void SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision);
-
/**
* Returns a COMPtr reference to the current/pending image requests, cleaning
* up and canceling anything that was there before. Note that if you just want
* to get rid of one of the requests, you should call
- * Clear*Request(NS_BINDING_ABORTED) instead, since it passes a more appropriate
- * aReason than Prepare*Request() does (NS_ERROR_IMAGE_SRC_CHANGED).
+ * Clear*Request(NS_BINDING_ABORTED) instead.
*
* @param aImageLoadType The ImageLoadType for this request
*/
@@ -459,6 +452,14 @@ private:
// registered with the refresh driver.
bool mCurrentRequestRegistered;
bool mPendingRequestRegistered;
+
+ // This member is used in SetBlockedRequest, if it's true, then this call is
+ // triggered from LoadImage.
+ // If this is false, it means this call is from other places like
+ // ServiceWorker, then we will ignore call to SetBlockedRequest for now.
+ //
+ // Also we use this variable to check if some evil code is reentering LoadImage.
+ bool mIsStartingImageLoad;
};
#endif // nsImageLoadingContent_h__
diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp
index 08fd9afd91..5c6701992f 100644
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -10,6 +10,7 @@
#include "nsIStreamListener.h"
#include "nsCDefaultURIFixup.h"
#include "nsIURIFixup.h"
+#include "nsIImageLoadingContent.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/TabChild.h"
@@ -123,7 +124,7 @@ nsContentSecurityManager::CheckFTPSubresourceLoad(nsIChannel* aChannel)
nsCOMPtr uri;
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
- NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_SUCCESS(rv, rv);
if (!uri) {
return NS_OK;
}
@@ -801,6 +802,8 @@ nsContentSecurityManager::CheckChannel(nsIChannel* aChannel)
// within nsCorsListenerProxy
rv = DoCheckLoadURIChecks(uri, loadInfo);
NS_ENSURE_SUCCESS(rv, rv);
+ // TODO: Bug 1371237
+ // consider calling SetBlockedRequest in nsContentSecurityManager::CheckChannel
}
return NS_OK;
diff --git a/image/imgLoader.cpp b/image/imgLoader.cpp
index f7fb657bd1..996ca01cf1 100644
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -1684,6 +1684,7 @@ imgLoader::ValidateRequestWithNewChannel(imgRequest* request,
rv = newChannel->AsyncOpen2(listener);
if (NS_WARN_IF(NS_FAILED(rv))) {
+ req->CancelAndForgetObserver(rv);
return false;
}
diff --git a/netwerk/protocol/http/nsCORSListenerProxy.cpp b/netwerk/protocol/http/nsCORSListenerProxy.cpp
index b9355c82bf..70035be28a 100644
--- a/netwerk/protocol/http/nsCORSListenerProxy.cpp
+++ b/netwerk/protocol/http/nsCORSListenerProxy.cpp
@@ -907,6 +907,9 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
NS_ENSURE_SUCCESS(rv, rv);
}
+ // TODO: Bug 1353683
+ // consider calling SetBlockedRequest in nsCORSListenerProxy::UpdateChannel
+ //
// Check that the uri is ok to load
rv = nsContentUtils::GetSecurityManager()->
CheckLoadURIWithPrincipal(mRequestingPrincipal, uri,
diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp
index 5c3f32d6fb..95f177376d 100644
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -954,8 +954,9 @@ nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL,
const nsAString& aImageReferrerPolicy)
{
nsCOMPtr baseURI = BaseURIForPreload();
+ bool isImgSet = false;
nsCOMPtr uri = mDocument->ResolvePreloadImage(baseURI, aURL, aSrcset,
- aSizes);
+ aSizes, &isImgSet);
if (uri && ShouldPreloadURI(uri)) {
// use document wide referrer policy
mozilla::net::ReferrerPolicy referrerPolicy = mSpeculationReferrerPolicy;
@@ -969,7 +970,7 @@ nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL,
}
}
- mDocument->MaybePreLoadImage(uri, aCrossOrigin, referrerPolicy);
+ mDocument->MaybePreLoadImage(uri, aCrossOrigin, referrerPolicy, isImgSet);
}
}