Bug 1459640 - add CSP also via meta tag, r=bgrins
This also fixes all the tests to not rely on eval() anymore.
MozReview-Commit-ID: 8H8r497nQVK
--- a/devtools/client/jsonview/converter-child.js
+++ b/devtools/client/jsonview/converter-child.js
@@ -17,16 +17,18 @@ loader.lazyGetter(this, "debugJsModules"
return !!(AppConstants.DEBUG_JS_MODULES);
});
const BinaryInput = CC("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream", "setInputStream");
const BufferStream = CC("@mozilla.org/io/arraybuffer-input-stream;1",
"nsIArrayBufferInputStream", "setData");
+const kCSP = "default-src 'none' ; script-src resource:; ";
+
// Localization
loader.lazyGetter(this, "jsonViewStrings", () => {
return Services.strings.createBundle(
"chrome://devtools/locale/jsonview.properties");
});
/**
* This object detects 'application/vnd.mozilla.json.view' content type
@@ -79,18 +81,18 @@ Converter.prototype = {
request.QueryInterface(Ci.nsIChannel);
request.contentType = "text/html";
let headers = getHttpHeaders(request);
// Enforce strict CSP:
try {
request.QueryInterface(Ci.nsIHttpChannel);
- request.setResponseHeader("Content-Security-Policy",
- "default-src 'none' ; script-src resource:; ", false);
+ request.setResponseHeader("Content-Security-Policy", kCSP, false);
+ request.setResponseHeader("Content-Security-Policy-Report-Only", "", false);
} catch (ex) {
// If this is not an HTTP channel we can't and won't do anything.
}
// Don't honor the charset parameter and use UTF-8 (see bug 741776).
request.contentCharset = "UTF-8";
this.decoder = new TextDecoder("UTF-8");
@@ -243,16 +245,20 @@ function initialHTML(doc) {
return "<!DOCTYPE html>\n" +
element("html", {
"platform": os,
"class": "theme-" + Services.prefs.getCharPref("devtools.theme"),
"dir": Services.locale.isAppLocaleRTL ? "rtl" : "ltr"
}, [
element("head", {}, [
+ element("meta", {
+ "http-equiv": "Content-Security-Policy",
+ content: kCSP,
+ }),
element("link", {
rel: "stylesheet",
type: "text/css",
href: baseURI + "css/main.css",
}),
]),
element("body", {}, [
element("div", {"id": "content"}, [
--- a/devtools/client/jsonview/test/browser_jsonview_content_type.js
+++ b/devtools/client/jsonview/test/browser_jsonview_content_type.js
@@ -65,16 +65,18 @@ add_task(async function() {
SpecialPowers.setBoolPref("browser.download.useDownloadDir", useDownloadDir);
});
});
function testType(isValid, type, params = "") {
const TEST_JSON_URL = "data:" + type + params + ",[1,2,3]";
return addJsonViewTab(TEST_JSON_URL).then(async function() {
ok(isValid, "The JSON Viewer should only load for valid content types.");
- is(await evalInContent("document.contentType"), type, "Got the right content type");
+ await ContentTask.spawn(gBrowser.selectedBrowser, type, function(contentType) {
+ is(content.document.contentType, contentType, "Got the right content type");
+ });
let count = await getElementCount(".jsonPanelBox .treeTable .treeRow");
is(count, 3, "There must be expected number of rows");
}, function() {
ok(!isValid, "The JSON Viewer should only not load for invalid content types.");
});
}
--- a/devtools/client/jsonview/test/browser_jsonview_row_selection.js
+++ b/devtools/client/jsonview/test/browser_jsonview_row_selection.js
@@ -10,37 +10,47 @@ add_task(async function() {
// Create a tall JSON so that there is a scrollbar.
let numRows = 1e3;
let json = JSON.stringify(Array(numRows).fill().map((_, i) => i));
let tab = await addJsonViewTab("data:application/json," + json);
is(await getElementCount(".treeRow"), numRows, "Got the expected number of rows.");
await assertRowSelected(null);
- await evalInContent("var scroller = $('.jsonPanelBox .panelContent')");
- ok(await evalInContent("scroller.clientHeight < scroller.scrollHeight"),
- "There is a scrollbar.");
- is(await evalInContent("scroller.scrollTop"), 0, "Initially scrolled to the top.");
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+ let scroller = content.document.querySelector(".jsonPanelBox .panelContent");
+ ok(scroller.clientHeight < scroller.scrollHeight, "There is a scrollbar.");
+ is(scroller.scrollTop, 0, "Initially scrolled to the top.");
- // Click to select last row.
- await evalInContent("$('.treeRow:last-child').click()");
+ // Click to select last row.
+ content.document.querySelector(".treeRow:last-child").click();
+ });
await assertRowSelected(numRows);
- is(await evalInContent("scroller.scrollTop + scroller.clientHeight"),
- await evalInContent("scroller.scrollHeight"), "Scrolled to the bottom.");
- // Click to select 2nd row.
- await evalInContent("$('.treeRow:nth-child(2)').click()");
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+ let scroller = content.document.querySelector(".jsonPanelBox .panelContent");
+ is(scroller.scrollTop + scroller.clientHeight, scroller.scrollHeight,
+ "Scrolled to the bottom.");
+ // Click to select 2nd row.
+ content.document.querySelector(".treeRow:nth-child(2)").click();
+ });
await assertRowSelected(2);
- ok(await evalInContent("scroller.scrollTop > 0"), "Not scrolled to the top.");
- // Synthetize up arrow key to select first row.
- await evalInContent("$('.treeTable').focus()");
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+ let scroller = content.document.querySelector(".jsonPanelBox .panelContent");
+ ok(scroller.scrollTop > 0, "Not scrolled to the top.");
+ // Synthetize up arrow key to select first row.
+ content.document.querySelector(".treeTable").focus();
+ });
await BrowserTestUtils.synthesizeKey("VK_UP", {}, tab.linkedBrowser);
await assertRowSelected(1);
- is(await evalInContent("scroller.scrollTop"), 0, "Scrolled to the top.");
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+ let scroller = content.document.querySelector(".jsonPanelBox .panelContent");
+ is(scroller.scrollTop, 0, "Scrolled to the top.");
+ });
});
add_task(async function() {
info("Test 2 JSON row selection started");
let numRows = 4;
let tab = await addJsonViewTab("data:application/json,[0,1,2,3]");
@@ -66,52 +76,73 @@ add_task(async function() {
info("Test 3 JSON row selection started");
// Create a JSON with a row taller than the panel.
let json = JSON.stringify([0, "a ".repeat(1e4), 1]);
await addJsonViewTab("data:application/json," + encodeURI(json));
is(await getElementCount(".treeRow"), 3, "Got the expected number of rows.");
await assertRowSelected(null);
- await evalInContent("var scroller = $('.jsonPanelBox .panelContent')");
- await evalInContent("var row = $('.treeRow:nth-child(2)')");
- ok(await evalInContent("scroller.clientHeight < row.clientHeight"),
- "The row is taller than the scroller.");
- is(await evalInContent("scroller.scrollTop"), 0, "Initially scrolled to the top.");
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+ let scroller = content.document.querySelector(".jsonPanelBox .panelContent");
+ let row = content.document.querySelector(".treeRow:nth-child(2)");
+ ok(scroller.clientHeight < row.clientHeight, "The row is taller than the scroller.");
+ is(scroller.scrollTop, 0, "Initially scrolled to the top.");
- // Select the tall row.
- await evalInContent("row.click()");
+ // Select the tall row.
+ row.click();
+ });
await assertRowSelected(2);
- is(await evalInContent("scroller.scrollTop"), await evalInContent("row.offsetTop"),
- "Scrolled to the top of the row.");
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+ let scroller = content.document.querySelector(".jsonPanelBox .panelContent");
+ let row = content.document.querySelector(".treeRow:nth-child(2)");
+ is(scroller.scrollTop, row.offsetTop,
+ "Scrolled to the top of the row.");
+ });
// Select the last row.
- await evalInContent("$('.treeRow:last-child').click()");
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+ content.document.querySelector(".treeRow:last-child").click();
+ });
await assertRowSelected(3);
- is(await evalInContent("scroller.scrollTop + scroller.offsetHeight"),
- await evalInContent("scroller.scrollHeight"), "Scrolled to the bottom.");
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+ let scroller = content.document.querySelector(".jsonPanelBox .panelContent");
+ is(scroller.scrollTop + scroller.offsetHeight,
+ scroller.scrollHeight, "Scrolled to the bottom.");
- // Select the tall row.
- await evalInContent("row.click()");
- await assertRowSelected(2);
- is(await evalInContent("scroller.scrollTop + scroller.offsetHeight"),
- await evalInContent("row.offsetTop + row.offsetHeight"),
- "Scrolled to the bottom of the row.");
+ // Select the tall row.
+ let row = content.document.querySelector(".treeRow:nth-child(2)");
+ row.click();
+ });
- // Scroll up a bit, so that both the top and bottom of the row are not visible.
- let scroll = await evalInContent(
- "scroller.scrollTop = Math.ceil((scroller.scrollTop + row.offsetTop) / 2)");
- ok(await evalInContent("scroller.scrollTop > row.offsetTop"),
- "The top of the row is not visible.");
- ok(await evalInContent("scroller.scrollTop + scroller.offsetHeight")
- < await evalInContent("row.offsetTop + row.offsetHeight"),
- "The bottom of the row is not visible.");
+ await assertRowSelected(2);
+ let scroll = await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+ let scroller = content.document.querySelector(".jsonPanelBox .panelContent");
+ let row = content.document.querySelector(".treeRow:nth-child(2)");
+ is(scroller.scrollTop + scroller.offsetHeight, row.offsetTop + row.offsetHeight,
+ "Scrolled to the bottom of the row.");
- // Select the tall row.
- await evalInContent("row.click()");
+ // Scroll up a bit, so that both the top and bottom of the row are not visible.
+ let scrollPos =
+ scroller.scrollTop = Math.ceil((scroller.scrollTop + row.offsetTop) / 2);
+ ok(scroller.scrollTop > row.offsetTop,
+ "The top of the row is not visible.");
+ ok(scroller.scrollTop + scroller.offsetHeight < row.offsetTop + row.offsetHeight,
+ "The bottom of the row is not visible.");
+
+ // Select the tall row.
+ row.click();
+ return scrollPos;
+ });
await assertRowSelected(2);
- is(await evalInContent("scroller.scrollTop"), scroll, "Scroll did not change");
+ await ContentTask.spawn(gBrowser.selectedBrowser, scroll, function(scrollPos) {
+ let scroller = content.document.querySelector(".jsonPanelBox .panelContent");
+ is(scroller.scrollTop, scrollPos, "Scroll did not change");
+ });
});
async function assertRowSelected(rowNum) {
- let idx = evalInContent("[].indexOf.call($$('.treeRow'), $('.treeRow.selected'))");
- is(await idx + 1, +rowNum, `${rowNum ? "The row #" + rowNum : "No row"} is selected.`);
+ let idx = await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+ return [].indexOf.call(content.document.querySelectorAll(".treeRow"),
+ content.document.querySelector(".treeRow.selected"));
+ });
+ is(idx + 1, +rowNum, `${rowNum ? "The row #" + rowNum : "No row"} is selected.`);
}
--- a/devtools/client/jsonview/test/doc_frame_script.js
+++ b/devtools/client/jsonview/test/doc_frame_script.js
@@ -118,18 +118,8 @@ addMessageListener("Test:JsonView:WaitFo
break;
}
}
}
});
observer.observe(firstRow, { attributes: true });
});
-
-addMessageListener("Test:JsonView:Eval", function(msg) {
- let result = content.eval(msg.data.code);
- sendAsyncMessage(msg.name, {result});
-});
-
-Cu.exportFunction(content.document.querySelector.bind(content.document),
- content, {defineAs: "$"});
-Cu.exportFunction(content.document.querySelectorAll.bind(content.document),
- content, {defineAs: "$$"});
--- a/devtools/client/jsonview/test/head.js
+++ b/devtools/client/jsonview/test/head.js
@@ -203,13 +203,8 @@ function waitForTime(delay) {
function waitForFilter() {
return executeInContent("Test:JsonView:WaitForFilter");
}
function normalizeNewLines(value) {
return value.replace("(\r\n|\n)", "\n");
}
-
-function evalInContent(code) {
- return executeInContent("Test:JsonView:Eval", {code})
- .then(result => result.result);
-}