зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1669566 - Add cookieStoreId functionality to browser.download.download, browser.download.search, and browser.download.erase; add unit tests. r=robwu,geckoview-reviewers,agi
Differential Revision: https://phabricator.services.mozilla.com/D119967
This commit is contained in:
Родитель
ba8f96b91d
Коммит
4d5eecd33f
|
@ -193,6 +193,11 @@ this.downloads = class extends ExtensionAPI {
|
|||
});
|
||||
}
|
||||
|
||||
if (options.cookieStoreId != null) {
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1721460
|
||||
throw new ExtensionError("Not implemented");
|
||||
}
|
||||
|
||||
if (options.headers) {
|
||||
for (const { name } of options.headers) {
|
||||
if (
|
||||
|
|
|
@ -2361,6 +2361,15 @@ DownloadCopySaver.prototype = {
|
|||
download.source.cookieJarSettings;
|
||||
}
|
||||
|
||||
if (download.source.userContextId) {
|
||||
// Getters and setters only exist on originAttributes,
|
||||
// so it has to be cloned, changed, and re-set
|
||||
channel.loadInfo.originAttributes = {
|
||||
...channel.loadInfo.originAttributes,
|
||||
userContextId: download.source.userContextId,
|
||||
};
|
||||
}
|
||||
|
||||
// This makes the channel be corretly throttled during page loads
|
||||
// and also prevents its caching.
|
||||
if (channel instanceof Ci.nsIHttpChannelInternal) {
|
||||
|
|
|
@ -34,6 +34,7 @@ const DOWNLOAD_ITEM_FIELDS = [
|
|||
"referrer",
|
||||
"filename",
|
||||
"incognito",
|
||||
"cookieStoreId",
|
||||
"danger",
|
||||
"mime",
|
||||
"startTime",
|
||||
|
@ -187,6 +188,15 @@ class DownloadItem {
|
|||
get incognito() {
|
||||
return this.download.source.isPrivate;
|
||||
}
|
||||
get cookieStoreId() {
|
||||
if (this.download.source.isPrivate) {
|
||||
return PRIVATE_STORE;
|
||||
}
|
||||
if (this.download.source.userContextId) {
|
||||
return getCookieStoreIdForContainer(this.download.source.userContextId);
|
||||
}
|
||||
return DEFAULT_STORE;
|
||||
}
|
||||
get danger() {
|
||||
return "safe";
|
||||
} // TODO
|
||||
|
@ -526,6 +536,7 @@ const downloadQuery = query => {
|
|||
"paused",
|
||||
"error",
|
||||
"incognito",
|
||||
"cookieStoreId",
|
||||
"bytesReceived",
|
||||
"totalBytes",
|
||||
"fileSize",
|
||||
|
@ -688,6 +699,15 @@ this.downloads = class extends ExtensionAPI {
|
|||
}
|
||||
}
|
||||
|
||||
let userContextId = null;
|
||||
if (options.cookieStoreId != null) {
|
||||
userContextId = getUserContextIdForCookieStoreId(
|
||||
extension,
|
||||
options.cookieStoreId,
|
||||
options.incognito
|
||||
);
|
||||
}
|
||||
|
||||
// Handle method, headers and body options.
|
||||
function adjustChannel(channel) {
|
||||
if (channel instanceof Ci.nsIHttpChannel) {
|
||||
|
@ -931,6 +951,10 @@ this.downloads = class extends ExtensionAPI {
|
|||
cookieJarSettings,
|
||||
};
|
||||
|
||||
if (userContextId) {
|
||||
source.userContextId = userContextId;
|
||||
}
|
||||
|
||||
// blob:-URLs can only be loaded by the principal with which they
|
||||
// are associated. This principal may have origin attributes.
|
||||
// `context.principal` does sometimes not have these attributes
|
||||
|
|
|
@ -106,6 +106,11 @@
|
|||
"description": "False if this download is recorded in the history, true if it is not recorded.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"cookieStoreId": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "The cookie store ID of the contextual identity."
|
||||
},
|
||||
"danger": {
|
||||
"$ref": "DangerType",
|
||||
"description": "Indication of whether this download is thought to be safe or known to be suspicious."
|
||||
|
@ -300,6 +305,11 @@
|
|||
"optional": true,
|
||||
"type": "string"
|
||||
},
|
||||
"cookieStoreId": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "The cookie store ID of the contextual identity."
|
||||
},
|
||||
"danger": {
|
||||
"$ref": "DangerType",
|
||||
"description": "Indication of whether this download is thought to be safe or known to be suspicious.",
|
||||
|
@ -383,6 +393,11 @@
|
|||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"cookieStoreId": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "The cookie store ID of the contextual identity; requires \"cookies\" permission."
|
||||
},
|
||||
"conflictAction": {
|
||||
"$ref": "FilenameConflictAction",
|
||||
"optional": true
|
||||
|
|
|
@ -0,0 +1,468 @@
|
|||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
function cookiesToMime(cookies) {
|
||||
return `dummy/${encodeURIComponent(cookies)}`.toLowerCase();
|
||||
}
|
||||
|
||||
function mimeToCookies(mime) {
|
||||
return decodeURIComponent(mime.replace("dummy/", ""));
|
||||
}
|
||||
|
||||
const server = createHttpServer({ hosts: ["example.net"] });
|
||||
|
||||
server.registerPathHandler("/download", (request, response) => {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
let cookies = request.hasHeader("Cookie") ? request.getHeader("Cookie") : "";
|
||||
// Assign the result through the MIME-type, to make it easier to read the
|
||||
// result via the downloads API.
|
||||
response.setHeader("Content-Type", cookiesToMime(cookies));
|
||||
// Response of length 7.
|
||||
response.write("1234567");
|
||||
});
|
||||
|
||||
const DOWNLOAD_URL = "http://example.net/download";
|
||||
|
||||
async function setUpCookies() {
|
||||
Services.cookies.removeAll();
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
incognitoOverride: "spanning",
|
||||
manifest: {
|
||||
permissions: ["cookies", "http://example.net/download"],
|
||||
},
|
||||
async background() {
|
||||
let url = "http://example.net/download";
|
||||
// Add default cookie
|
||||
await browser.cookies.set({
|
||||
url,
|
||||
name: "cookie_normal",
|
||||
value: "1",
|
||||
});
|
||||
|
||||
// Add private cookie
|
||||
await browser.cookies.set({
|
||||
url,
|
||||
storeId: "firefox-private",
|
||||
name: "cookie_private",
|
||||
value: "1",
|
||||
});
|
||||
|
||||
// Add container cookie
|
||||
await browser.cookies.set({
|
||||
url,
|
||||
storeId: "firefox-container-1",
|
||||
name: "cookie_container",
|
||||
value: "1",
|
||||
});
|
||||
browser.test.sendMessage("cookies set");
|
||||
},
|
||||
});
|
||||
await extension.startup();
|
||||
await extension.awaitMessage("cookies set");
|
||||
await extension.unload();
|
||||
}
|
||||
|
||||
function createDownloadTestExtension(extraPermissions = [], incognito = false) {
|
||||
let extensionOptions = {
|
||||
manifest: {
|
||||
permissions: ["downloads", ...extraPermissions],
|
||||
},
|
||||
background() {
|
||||
browser.test.onMessage.addListener(async (method, data) => {
|
||||
async function getDownload(data) {
|
||||
let donePromise = new Promise(resolve => {
|
||||
browser.downloads.onChanged.addListener(async delta => {
|
||||
if (delta.state?.current === "complete") {
|
||||
resolve(delta.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
let downloadId = await browser.downloads.download(data);
|
||||
browser.test.assertEq(await donePromise, downloadId, "got download");
|
||||
let [download] = await browser.downloads.search({ id: downloadId });
|
||||
browser.test.log(`Download results: ${JSON.stringify(download)}`);
|
||||
// Delete the file since we aren't interested in it.
|
||||
// TODO bug 1654819: On Windows the file may be recreated.
|
||||
await browser.downloads.removeFile(download.id);
|
||||
// Sanity check to verify that we got the result from /download.
|
||||
browser.test.assertEq(7, download.fileSize, "download succeeded");
|
||||
return download;
|
||||
}
|
||||
function checkDownloadError(data) {
|
||||
return browser.test.assertRejects(
|
||||
browser.downloads.download(data.downloadData),
|
||||
data.exceptionRe
|
||||
);
|
||||
}
|
||||
function search(data) {
|
||||
return browser.downloads.search(data);
|
||||
}
|
||||
function erase(data) {
|
||||
return browser.downloads.erase(data);
|
||||
}
|
||||
switch (method) {
|
||||
case "getDownload":
|
||||
return browser.test.sendMessage(method, await getDownload(data));
|
||||
case "checkDownloadError":
|
||||
return browser.test.sendMessage(
|
||||
method,
|
||||
await checkDownloadError(data)
|
||||
);
|
||||
case "search":
|
||||
return browser.test.sendMessage(method, await search(data));
|
||||
case "erase":
|
||||
return browser.test.sendMessage(method, await erase(data));
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
if (incognito) {
|
||||
extensionOptions.incognitoOverride = "spanning";
|
||||
}
|
||||
return ExtensionTestUtils.loadExtension(extensionOptions);
|
||||
}
|
||||
|
||||
function getResult(extension, method, data) {
|
||||
extension.sendMessage(method, data);
|
||||
return extension.awaitMessage(method);
|
||||
}
|
||||
|
||||
async function getCookies(extension, data) {
|
||||
let download = await getResult(extension, "getDownload", data);
|
||||
// The "/download" endpoint mirrors received cookies via Content-Type.
|
||||
let cookies = mimeToCookies(download.mime);
|
||||
return cookies;
|
||||
}
|
||||
|
||||
async function runTests(extension, containerDownloadAllowed, privateAllowed) {
|
||||
let forcedIncognitoException = null;
|
||||
if (!privateAllowed) {
|
||||
forcedIncognitoException = /private browsing access not allowed/;
|
||||
} else if (!containerDownloadAllowed) {
|
||||
forcedIncognitoException = /No permission for cookieStoreId/;
|
||||
}
|
||||
|
||||
// Test default container download
|
||||
if (containerDownloadAllowed) {
|
||||
equal(
|
||||
await getCookies(extension, {
|
||||
url: DOWNLOAD_URL,
|
||||
cookieStoreId: "firefox-default",
|
||||
}),
|
||||
"cookie_normal=1",
|
||||
"Default container cookies for downloads.download"
|
||||
);
|
||||
} else {
|
||||
await getResult(extension, "checkDownloadError", {
|
||||
exceptionRe: /No permission for cookieStoreId/,
|
||||
downloadData: {
|
||||
url: DOWNLOAD_URL,
|
||||
cookieStoreId: "firefox-default",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Test private container download
|
||||
if (privateAllowed && containerDownloadAllowed) {
|
||||
equal(
|
||||
await getCookies(extension, {
|
||||
url: DOWNLOAD_URL,
|
||||
cookieStoreId: "firefox-private",
|
||||
incognito: true,
|
||||
}),
|
||||
"cookie_private=1",
|
||||
"Private container cookies for downloads.download"
|
||||
);
|
||||
} else {
|
||||
await getResult(extension, "checkDownloadError", {
|
||||
exceptionRe: forcedIncognitoException,
|
||||
downloadData: {
|
||||
url: DOWNLOAD_URL,
|
||||
cookieStoreId: "firefox-private",
|
||||
incognito: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Test firefox-container-1 download
|
||||
if (containerDownloadAllowed) {
|
||||
equal(
|
||||
await getCookies(extension, {
|
||||
url: DOWNLOAD_URL,
|
||||
cookieStoreId: "firefox-container-1",
|
||||
}),
|
||||
"cookie_container=1",
|
||||
"firefox-container-1 cookies for downloads.download"
|
||||
);
|
||||
} else {
|
||||
await getResult(extension, "checkDownloadError", {
|
||||
exceptionRe: /No permission for cookieStoreId/,
|
||||
downloadData: {
|
||||
url: DOWNLOAD_URL,
|
||||
cookieStoreId: "firefox-container-1",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Test mismatched incognito and cookieStoreId download
|
||||
await getResult(extension, "checkDownloadError", {
|
||||
exceptionRe: forcedIncognitoException
|
||||
? forcedIncognitoException
|
||||
: /Illegal to set non-private cookieStoreId in a private window/,
|
||||
downloadData: {
|
||||
url: DOWNLOAD_URL,
|
||||
incognito: true,
|
||||
cookieStoreId: "firefox-container-1",
|
||||
},
|
||||
});
|
||||
await getResult(extension, "checkDownloadError", {
|
||||
exceptionRe: containerDownloadAllowed
|
||||
? /Illegal to set private cookieStoreId in a non-private window/
|
||||
: /No permission for cookieStoreId/,
|
||||
downloadData: {
|
||||
url: DOWNLOAD_URL,
|
||||
incognito: false,
|
||||
cookieStoreId: "firefox-private",
|
||||
},
|
||||
});
|
||||
|
||||
// Test invalid cookieStoreId download
|
||||
await getResult(extension, "checkDownloadError", {
|
||||
exceptionRe: containerDownloadAllowed
|
||||
? /Illegal cookieStoreId/
|
||||
: /No permission for cookieStoreId/,
|
||||
downloadData: {
|
||||
url: DOWNLOAD_URL,
|
||||
cookieStoreId: "invalid-invalid-invalid",
|
||||
},
|
||||
});
|
||||
|
||||
let searchRes, searchResDownload;
|
||||
// Test default container search
|
||||
searchRes = await getResult(extension, "search", {
|
||||
cookieStoreId: "firefox-default",
|
||||
});
|
||||
equal(
|
||||
searchRes.length,
|
||||
1,
|
||||
"Default container results length for downloads.search"
|
||||
);
|
||||
[searchResDownload] = searchRes;
|
||||
equal(
|
||||
mimeToCookies(searchResDownload.mime),
|
||||
"cookie_normal=1",
|
||||
"Default container cookies for downloads.search"
|
||||
);
|
||||
// Test default container search with mismatched container
|
||||
searchRes = await getResult(extension, "search", {
|
||||
mime: cookiesToMime("cookie_normal=1"),
|
||||
cookieStoreId: "firefox-container-1",
|
||||
});
|
||||
equal(
|
||||
searchRes.length,
|
||||
0,
|
||||
"Default container results length for downloads.search when container mismatched"
|
||||
);
|
||||
|
||||
// Test private container search
|
||||
searchRes = await getResult(extension, "search", {
|
||||
cookieStoreId: "firefox-private",
|
||||
});
|
||||
if (privateAllowed) {
|
||||
equal(
|
||||
searchRes.length,
|
||||
1,
|
||||
"Private container results length for downloads.search"
|
||||
);
|
||||
[searchResDownload] = searchRes;
|
||||
equal(
|
||||
mimeToCookies(searchResDownload.mime),
|
||||
"cookie_private=1",
|
||||
"Private container cookies for downloads.search"
|
||||
);
|
||||
// Test private container search with mismatched container
|
||||
searchRes = await getResult(extension, "search", {
|
||||
mime: cookiesToMime("cookie_private=1"),
|
||||
cookieStoreId: "firefox-container-1",
|
||||
});
|
||||
equal(
|
||||
searchRes.length,
|
||||
0,
|
||||
"Private container results length for downloads.search when container mismatched"
|
||||
);
|
||||
} else {
|
||||
equal(
|
||||
searchRes.length,
|
||||
0,
|
||||
"Private container results length for downloads.search when private disallowed"
|
||||
);
|
||||
}
|
||||
|
||||
// Test firefox-container-1 search
|
||||
searchRes = await getResult(extension, "search", {
|
||||
cookieStoreId: "firefox-container-1",
|
||||
});
|
||||
equal(
|
||||
searchRes.length,
|
||||
1,
|
||||
"firefox-container-1 results length for downloads.search"
|
||||
);
|
||||
[searchResDownload] = searchRes;
|
||||
equal(
|
||||
mimeToCookies(searchResDownload.mime),
|
||||
"cookie_container=1",
|
||||
"firefox-container-1 cookies for downloads.search"
|
||||
);
|
||||
// Test firefox-container-1 search with mismatched container
|
||||
searchRes = await getResult(extension, "search", {
|
||||
mime: cookiesToMime("cookie_container=1"),
|
||||
cookieStoreId: "firefox-default",
|
||||
});
|
||||
equal(
|
||||
searchRes.length,
|
||||
0,
|
||||
"firefox-container-1 container results length for downloads.search when container mismatched"
|
||||
);
|
||||
|
||||
// Test default container erase with mismatched container
|
||||
await getResult(extension, "erase", {
|
||||
mime: cookiesToMime("cookie_normal=1"),
|
||||
cookieStoreId: "firefox-container-1",
|
||||
});
|
||||
searchRes = await getResult(extension, "search", {
|
||||
mime: cookiesToMime("cookie_normal=1"),
|
||||
});
|
||||
equal(
|
||||
searchRes.length,
|
||||
1,
|
||||
"Default container results length for downloads.search after erase with mismatched container"
|
||||
);
|
||||
|
||||
// Test private container erase with mismatched container
|
||||
await getResult(extension, "erase", {
|
||||
mime: cookiesToMime("cookie_private=1"),
|
||||
cookieStoreId: "firefox-container-1",
|
||||
});
|
||||
searchRes = await getResult(extension, "search", {
|
||||
mime: cookiesToMime("cookie_private=1"),
|
||||
});
|
||||
equal(
|
||||
searchRes.length,
|
||||
privateAllowed ? 1 : 0,
|
||||
"Private container results length for downloads.search after erase with mismatched container"
|
||||
);
|
||||
|
||||
// Test firefox-container-1 erase with mismatched container
|
||||
await getResult(extension, "erase", {
|
||||
mime: cookiesToMime("cookie_container=1"),
|
||||
cookieStoreId: "firefox-default",
|
||||
});
|
||||
searchRes = await getResult(extension, "search", {
|
||||
mime: cookiesToMime("cookie_container=1"),
|
||||
});
|
||||
equal(
|
||||
searchRes.length,
|
||||
1,
|
||||
"firefox-container-1 results length for downloads.search after erase with mismatched container"
|
||||
);
|
||||
|
||||
// Test default container erase
|
||||
await getResult(extension, "erase", {
|
||||
cookieStoreId: "firefox-default",
|
||||
});
|
||||
searchRes = await getResult(extension, "search", {
|
||||
mime: cookiesToMime("cookie_normal=1"),
|
||||
});
|
||||
equal(
|
||||
searchRes.length,
|
||||
0,
|
||||
"Default container results length for downloads.search after erase"
|
||||
);
|
||||
|
||||
// Test private container erase
|
||||
await getResult(extension, "erase", {
|
||||
cookieStoreId: "firefox-private",
|
||||
});
|
||||
searchRes = await getResult(extension, "search", {
|
||||
mime: cookiesToMime("cookie_private=1"),
|
||||
});
|
||||
// The following will also pass when incognito disabled
|
||||
equal(
|
||||
searchRes.length,
|
||||
0,
|
||||
"Private container results length for downloads.search after erase"
|
||||
);
|
||||
|
||||
// Test firefox-container-1 erase
|
||||
await getResult(extension, "erase", {
|
||||
cookieStoreId: "firefox-container-1",
|
||||
});
|
||||
searchRes = await getResult(extension, "search", {
|
||||
mime: cookiesToMime("cookie_container=1"),
|
||||
});
|
||||
equal(
|
||||
searchRes.length,
|
||||
0,
|
||||
"firefox-container-1 results length for downloads.search after erase"
|
||||
);
|
||||
}
|
||||
|
||||
async function populateDownloads(extension) {
|
||||
await getResult(extension, "erase", {});
|
||||
await getResult(extension, "getDownload", {
|
||||
url: DOWNLOAD_URL,
|
||||
});
|
||||
await getResult(extension, "getDownload", {
|
||||
url: DOWNLOAD_URL,
|
||||
incognito: true,
|
||||
});
|
||||
await getResult(extension, "getDownload", {
|
||||
url: DOWNLOAD_URL,
|
||||
cookieStoreId: "firefox-container-1",
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
const nsIFile = Ci.nsIFile;
|
||||
const downloadDir = FileUtils.getDir("TmpD", ["downloads"]);
|
||||
downloadDir.createUnique(nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
Services.prefs.setIntPref("browser.download.folderList", 2);
|
||||
Services.prefs.setComplexValue("browser.download.dir", nsIFile, downloadDir);
|
||||
await setUpCookies();
|
||||
registerCleanupFunction(() => {
|
||||
Services.cookies.removeAll();
|
||||
Services.prefs.clearUserPref("browser.download.folderList");
|
||||
Services.prefs.clearUserPref("browser.download.dir");
|
||||
downloadDir.remove(false);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function download_cookieStoreId() {
|
||||
// Test extension with cookies permission and incognito enabled
|
||||
let extension = createDownloadTestExtension(["cookies"], true);
|
||||
await extension.startup();
|
||||
await runTests(extension, true, true);
|
||||
|
||||
// Test extension with incognito enabled and no cookies permission
|
||||
await populateDownloads(extension);
|
||||
let noCookiesExtension = createDownloadTestExtension([], true);
|
||||
await noCookiesExtension.startup();
|
||||
await runTests(noCookiesExtension, false, true);
|
||||
await noCookiesExtension.unload();
|
||||
|
||||
// Test extension with incognito disabled and no cookies permission
|
||||
await populateDownloads(extension);
|
||||
let noCookiesAndPrivateExtension = createDownloadTestExtension([], false);
|
||||
await noCookiesAndPrivateExtension.startup();
|
||||
await runTests(noCookiesAndPrivateExtension, false, false);
|
||||
await noCookiesAndPrivateExtension.unload();
|
||||
|
||||
// Verify that incognito disabled test did not delete private download
|
||||
let searchRes = await getResult(extension, "search", {
|
||||
mime: cookiesToMime("cookie_private=1"),
|
||||
});
|
||||
ok(searchRes.length, "Incognito disabled does not delete private download");
|
||||
|
||||
await extension.unload();
|
||||
});
|
|
@ -79,6 +79,8 @@ skip-if = socketprocess_networking || os == "android" # Android: Bug 1680132
|
|||
[test_ext_downloads.js]
|
||||
[test_ext_downloads_cookies.js]
|
||||
skip-if = os == "android" # downloads API needs to be implemented in GeckoView - bug 1538348
|
||||
[test_ext_downloads_cookieStoreId.js]
|
||||
skip-if = os == "android"
|
||||
[test_ext_downloads_download.js]
|
||||
skip-if = appname == "thunderbird" || os == "android" || tsan # tsan: bug 1612707
|
||||
[test_ext_downloads_misc.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче