mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
Bug 1824117 - Use a custom content prefs group parser in DownloadLastDir. r=Gijs
Make all file: URIs point to the same destination folder. Make data: URIs point to a destination folder per mime-type. If a mime-type is not provided it defaults to text/plain, per the data URI spec. Introduce a migration path in content prefs to remove existing data and file URIs, users will have to set them again though current ones are not particularly useful. Make getFileAsync an async function to simplify the code. Differential Revision: https://phabricator.services.mozilla.com/D173472
This commit is contained in:
parent
908a9b68fd
commit
b5fd4292c5
@ -37,11 +37,7 @@ function test_deleted_iframe(perSitePref, windowOptions = {}) {
|
||||
|
||||
let someDir = "blah";
|
||||
try {
|
||||
someDir = await new Promise((resolve, reject) => {
|
||||
gDownloadLastDir.getFileAsync("http://www.mozilla.org/", function(dir) {
|
||||
resolve(dir);
|
||||
});
|
||||
});
|
||||
someDir = await gDownloadLastDir.getFileAsync("http://www.mozilla.org/");
|
||||
} catch (ex) {
|
||||
ok(
|
||||
false,
|
||||
|
@ -0,0 +1,140 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Basic test for setting and retrieving a download last dir.
|
||||
// More complex tests can be found in browser/components/privatebrowsing/.
|
||||
|
||||
const SAVE_PER_SITE_PREF_BRANCH = "browser.download.lastDir";
|
||||
const SAVE_PER_SITE_PREF = SAVE_PER_SITE_PREF_BRANCH + ".savePerSite";
|
||||
|
||||
let { FileUtils } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/FileUtils.sys.mjs"
|
||||
);
|
||||
let { DownloadLastDir } = ChromeUtils.import(
|
||||
"resource://gre/modules/DownloadLastDir.jsm"
|
||||
);
|
||||
|
||||
add_task(
|
||||
{
|
||||
pref_set: [[SAVE_PER_SITE_PREF, true]],
|
||||
},
|
||||
async function test() {
|
||||
let downloadLastDir = new DownloadLastDir(null);
|
||||
|
||||
let unknownUri = Services.io.newURI("https://unknown.org/");
|
||||
Assert.deepEqual(
|
||||
await downloadLastDir.getFileAsync(unknownUri),
|
||||
null,
|
||||
"Untracked URI, no pref set"
|
||||
);
|
||||
|
||||
let dir1 = FileUtils.getDir("TmpD", ["dir1"], true);
|
||||
let uri1 = Services.io.newURI("https://test1.moz.org");
|
||||
downloadLastDir.setFile(uri1, dir1);
|
||||
let dir2 = FileUtils.getDir("TmpD", ["dir2"], true);
|
||||
let uri2 = Services.io.newURI("https://test2.moz.org");
|
||||
downloadLastDir.setFile(uri2, dir2);
|
||||
let dir3 = FileUtils.getDir("TmpD", ["dir3"], true);
|
||||
downloadLastDir.setFile(null, dir3);
|
||||
Assert.equal(
|
||||
(await downloadLastDir.getFileAsync(uri1)).path,
|
||||
dir1.path,
|
||||
"Check common URI"
|
||||
);
|
||||
Assert.equal(
|
||||
(await downloadLastDir.getFileAsync(uri2)).path,
|
||||
dir2.path,
|
||||
"Check common URI"
|
||||
);
|
||||
Assert.equal(downloadLastDir.file.path, dir3.path, "No URI");
|
||||
Assert.equal(
|
||||
(await downloadLastDir.getFileAsync(unknownUri)).path,
|
||||
dir3.path,
|
||||
"Untracked URI, pref set"
|
||||
);
|
||||
|
||||
info("Check clearHistory removes all data");
|
||||
let subject = {};
|
||||
Services.obs.notifyObservers(subject, "browser:purge-session-history");
|
||||
await subject.promise;
|
||||
Assert.deepEqual(
|
||||
await downloadLastDir.getFileAsync(uri1),
|
||||
null,
|
||||
"Check common URI after clear history returns null"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
await downloadLastDir.getFileAsync(uri2),
|
||||
null,
|
||||
"Check common URI after clear history returns null"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
await downloadLastDir.getFileAsync(unknownUri),
|
||||
null,
|
||||
"Check untracked URI after clear history returns null"
|
||||
);
|
||||
|
||||
// file: URIs should all point to the same folder.
|
||||
let fileUri1 = Services.io.newURI("file:///c:/test.txt");
|
||||
downloadLastDir.setFile(uri1, dir3);
|
||||
let dir4 = FileUtils.getDir("TmpD", ["dir4"], true);
|
||||
let fileUri2 = Services.io.newURI("file:///d:/test.png");
|
||||
downloadLastDir.setFile(uri1, dir4);
|
||||
Assert.equal(
|
||||
(await downloadLastDir.getFileAsync(fileUri1)).path,
|
||||
dir4.path,
|
||||
"Check file URI"
|
||||
);
|
||||
Assert.equal(
|
||||
(await downloadLastDir.getFileAsync(fileUri2)).path,
|
||||
dir4.path,
|
||||
"Check file URI"
|
||||
);
|
||||
let unknownFileUri = Services.io.newURI("file:///e:/test.mkv");
|
||||
Assert.equal(
|
||||
(await downloadLastDir.getFileAsync(unknownFileUri)).path,
|
||||
dir4.path,
|
||||
"Untracked File URI, pref set"
|
||||
);
|
||||
|
||||
// data: URIs should point to a folder per mime-type.
|
||||
// Unspecified mime-type is handled as text/plain.
|
||||
let dataUri1 = Services.io.newURI("data:text/plain;charset=UTF-8,1234");
|
||||
downloadLastDir.setFile(dataUri1, dir1);
|
||||
let dataUri2 = Services.io.newURI("data:image/png;base64,1234");
|
||||
Assert.equal(
|
||||
(await downloadLastDir.getFileAsync(dataUri2)).path,
|
||||
dir1.path,
|
||||
"Check data URI"
|
||||
);
|
||||
let dataUri3 = Services.io.newURI("data:image/png,5678");
|
||||
downloadLastDir.setFile(dataUri3, dir2);
|
||||
Assert.equal(
|
||||
(await downloadLastDir.getFileAsync(dataUri2)).path,
|
||||
dir2.path,
|
||||
"Data URI was changed, same mime-type"
|
||||
);
|
||||
Assert.equal(
|
||||
(await downloadLastDir.getFileAsync(dataUri1)).path,
|
||||
dir1.path,
|
||||
"Data URI was not changed, different mime-type"
|
||||
);
|
||||
let dataUri4 = Services.io.newURI("data:,");
|
||||
Assert.equal(
|
||||
(await downloadLastDir.getFileAsync(dataUri4)).path,
|
||||
dir1.path,
|
||||
"Data URI defaults to text/plain"
|
||||
);
|
||||
downloadLastDir.setFile(null, dir4);
|
||||
let unknownDataUri = Services.io.newURI("data:application/zip,");
|
||||
Assert.deepEqual(
|
||||
(await downloadLastDir.getFileAsync(unknownDataUri)).path,
|
||||
dir4.path,
|
||||
"Untracked data URI"
|
||||
);
|
||||
Assert.equal(
|
||||
(await downloadLastDir.getFileAsync(dataUri4)).path,
|
||||
dir1.path,
|
||||
"Data URI didn't change"
|
||||
);
|
||||
}
|
||||
);
|
@ -3,7 +3,7 @@ head = head.js
|
||||
firefox-appdir = browser
|
||||
skip-if = toolkit == 'android' # bug 1730213
|
||||
|
||||
|
||||
[test_DownloadLastDir_basics.js]
|
||||
[test_DownloadsCommon_getMimeInfo.js]
|
||||
[test_DownloadsCommon_isFileOfType.js]
|
||||
[test_DownloadsViewableInternally.js]
|
||||
|
@ -21,7 +21,7 @@ function createWindow(aOptions) {
|
||||
}
|
||||
|
||||
function getFile(downloadLastDir, aURI) {
|
||||
return new Promise(resolve => downloadLastDir.getFileAsync(aURI, resolve));
|
||||
return downloadLastDir.getFileAsync(aURI);
|
||||
}
|
||||
|
||||
function setFile(downloadLastDir, aURI, aValue) {
|
||||
|
@ -231,11 +231,7 @@ function promiseTargetFile(aFpP, win) {
|
||||
|
||||
// We must prompt for the file name explicitly.
|
||||
// If we must prompt because we were asked to...
|
||||
let file = await new Promise(resolve => {
|
||||
downloadLastDir.getFileAsync(null, function getFileAsyncCB(aFile) {
|
||||
resolve(aFile);
|
||||
});
|
||||
});
|
||||
let file = await downloadLastDir.getFileAsync(null);
|
||||
if (file && (await IOUtils.exists(file.path))) {
|
||||
dir = file;
|
||||
dirExists = true;
|
||||
|
@ -1115,7 +1115,7 @@ ContentPrefService2.prototype = {
|
||||
|
||||
// Database Creation & Access
|
||||
|
||||
_dbVersion: 4,
|
||||
_dbVersion: 5,
|
||||
|
||||
_dbSchema: {
|
||||
tables: {
|
||||
@ -1350,6 +1350,29 @@ ContentPrefService2.prototype = {
|
||||
await this._createIndex(aConn, name);
|
||||
}
|
||||
},
|
||||
|
||||
async _dbMigrate4To5(conn) {
|
||||
// This is a data migration for browser.download.lastDir. While it may not
|
||||
// affect all consumers, it's simpler and safer to do it here than elsewhere.
|
||||
await conn.execute(`
|
||||
DELETE FROM prefs
|
||||
WHERE id IN (
|
||||
SELECT p.id FROM prefs p
|
||||
JOIN groups g ON g.id = p.groupID
|
||||
JOIN settings s ON s.id = p.settingID
|
||||
WHERE s.name = 'browser.download.lastDir'
|
||||
AND (
|
||||
(g.name BETWEEN 'data:' AND 'data:' || X'FFFF') OR
|
||||
(g.name BETWEEN 'file:' AND 'file:' || X'FFFF')
|
||||
)
|
||||
)
|
||||
`);
|
||||
await conn.execute(`
|
||||
DELETE FROM groups WHERE NOT EXISTS (
|
||||
SELECT 1 FROM prefs WHERE groupId = groups.id
|
||||
)
|
||||
`);
|
||||
},
|
||||
};
|
||||
|
||||
function checkGroupArg(group) {
|
||||
|
@ -5,6 +5,8 @@
|
||||
let loadContext = Cu.createLoadContext();
|
||||
let privateLoadContext = Cu.createPrivateLoadContext();
|
||||
|
||||
const CURRENT_DB_VERSION = 5;
|
||||
|
||||
// There has to be a profile directory before the CPS service is gotten.
|
||||
do_get_profile();
|
||||
let cps = Cc["@mozilla.org/content-pref/service;1"].getService(
|
||||
|
@ -49,7 +49,7 @@ add_task(async function resetBeforeTests() {
|
||||
// and so migration will be run only once.
|
||||
add_task(async function testMigration() {
|
||||
// Test migrated db content.
|
||||
await schemaVersionIs(4);
|
||||
await schemaVersionIs(CURRENT_DB_VERSION);
|
||||
let dbExpectedState = [
|
||||
[null, "zoom-setting", 0.1],
|
||||
["bar.com", "zoom-setting", 0.3],
|
||||
|
@ -0,0 +1,60 @@
|
||||
/* 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/. */
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
|
||||
});
|
||||
|
||||
// Dump of version we migrate from
|
||||
const schema_queries = [
|
||||
"PRAGMA foreign_keys=OFF",
|
||||
"CREATE TABLE groups (id INTEGER PRIMARY KEY, name TEXT NOT NULL)",
|
||||
`INSERT INTO groups VALUES (1,'foo.com'),
|
||||
(2,'bar.com'),
|
||||
(3,'data:image/png;base64,1234'),
|
||||
(4,'file:///d/test.file')`,
|
||||
"CREATE TABLE settings (id INTEGER PRIMARY KEY, name TEXT NOT NULL)",
|
||||
`INSERT INTO settings VALUES (1,'zoom-setting'),
|
||||
(2,'browser.download.lastDir')`,
|
||||
`CREATE TABLE prefs (id INTEGER PRIMARY KEY,
|
||||
groupID INTEGER REFERENCES groups(id),
|
||||
settingID INTEGER NOT NULL REFERENCES settings(id),
|
||||
value BLOB,
|
||||
timestamp INTEGER NOT NULL DEFAULT 0)`,
|
||||
`INSERT INTO prefs VALUES (1,1,1,0.5,0),
|
||||
(2,1,2,'/download/dir',0),
|
||||
(3,2,1,0.3,0),
|
||||
(4,NULL,1,0.1,0),
|
||||
(5,3,2,'/download/dir',0),
|
||||
(6,4,2,'/download/dir',0)`,
|
||||
"CREATE INDEX groups_idx ON groups(name)",
|
||||
"CREATE INDEX settings_idx ON settings(name)",
|
||||
"CREATE INDEX prefs_idx ON prefs(timestamp, groupID, settingID)",
|
||||
];
|
||||
|
||||
add_setup(async function() {
|
||||
let conn = await Sqlite.openConnection({
|
||||
path: PathUtils.join(PathUtils.profileDir, "content-prefs.sqlite"),
|
||||
});
|
||||
Assert.equal(await conn.getSchemaVersion(), 0);
|
||||
await conn.executeTransaction(async () => {
|
||||
for (let query of schema_queries) {
|
||||
await conn.execute(query);
|
||||
}
|
||||
});
|
||||
await conn.setSchemaVersion(4);
|
||||
await conn.close();
|
||||
});
|
||||
|
||||
add_task(async function test() {
|
||||
// Test migrated db content.
|
||||
await schemaVersionIs(CURRENT_DB_VERSION);
|
||||
let dbExpectedState = [
|
||||
[null, "zoom-setting", 0.1],
|
||||
["bar.com", "zoom-setting", 0.3],
|
||||
["foo.com", "zoom-setting", 0.5],
|
||||
["foo.com", "browser.download.lastDir", "/download/dir"],
|
||||
];
|
||||
await dbOK(dbExpectedState);
|
||||
});
|
@ -14,4 +14,5 @@ support-files = AsyncRunner.sys.mjs
|
||||
[test_observers.js]
|
||||
[test_extractDomain.js]
|
||||
[test_migrationToSchema4.js]
|
||||
[test_migrationToSchema5.js]
|
||||
[test_removeAllDomainsSince.js]
|
||||
|
@ -880,11 +880,7 @@ this.downloads = class extends ExtensionAPIPersistent {
|
||||
);
|
||||
|
||||
async function getLastDirectory() {
|
||||
return new Promise(resolve => {
|
||||
downloadLastDir.getFileAsync(extension.baseURI, file => {
|
||||
resolve(file);
|
||||
});
|
||||
});
|
||||
return downloadLastDir.getFileAsync(extension.baseURI);
|
||||
}
|
||||
|
||||
function appendFilterForFileExtension(picker, ext) {
|
||||
|
@ -674,20 +674,10 @@ function promiseTargetFile(
|
||||
|
||||
// We must prompt for the file name explicitly.
|
||||
// If we must prompt because we were asked to...
|
||||
let file = await new Promise(resolve => {
|
||||
if (useDownloadDir) {
|
||||
// Keep async behavior in both branches
|
||||
Services.tm.dispatchToMainThread(function() {
|
||||
resolve(null);
|
||||
});
|
||||
} else {
|
||||
downloadLastDir.getFileAsync(aRelatedURI, function getFileAsyncCB(
|
||||
aFile
|
||||
) {
|
||||
resolve(aFile);
|
||||
});
|
||||
}
|
||||
});
|
||||
let file = null;
|
||||
if (!useDownloadDir) {
|
||||
file = await downloadLastDir.getFileAsync(aRelatedURI);
|
||||
}
|
||||
if (file && (await IOUtils.exists(file.path))) {
|
||||
dir = file;
|
||||
dirExists = true;
|
||||
|
@ -754,7 +754,7 @@ ConnectionData.prototype = Object.freeze({
|
||||
let caller = new Error().stack
|
||||
.split("\n", 3)
|
||||
.pop()
|
||||
.match(/^([^@]+@).*\/([^\/:]+)[:0-9]*$/);
|
||||
.match(/^([^@]*@).*\/([^\/:]+)[:0-9]*$/);
|
||||
caller = caller[1] + caller[2];
|
||||
this._log.debug(`Transaction (type ${type}) requested by: ${caller}`);
|
||||
|
||||
|
@ -29,6 +29,15 @@ const SAVE_PER_SITE_PREF = LAST_DIR_PREF + ".savePerSite";
|
||||
const nsIFile = Ci.nsIFile;
|
||||
|
||||
import { PrivateBrowsingUtils } from "resource://gre/modules/PrivateBrowsingUtils.sys.mjs";
|
||||
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
lazy,
|
||||
"cps2",
|
||||
"@mozilla.org/content-pref/service;1",
|
||||
"nsIContentPrefService2"
|
||||
);
|
||||
|
||||
let nonPrivateLoadContext = Cu.createLoadContext();
|
||||
let privateLoadContext = Cu.createPrivateLoadContext();
|
||||
@ -51,12 +60,22 @@ var observer = {
|
||||
}
|
||||
// Ensure that purging session history causes both the session-only PB cache
|
||||
// and persistent prefs to be cleared.
|
||||
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
|
||||
Ci.nsIContentPrefService2
|
||||
);
|
||||
|
||||
cps2.removeByName(LAST_DIR_PREF, nonPrivateLoadContext);
|
||||
cps2.removeByName(LAST_DIR_PREF, privateLoadContext);
|
||||
let promises = [
|
||||
new Promise(resolve =>
|
||||
lazy.cps2.removeByName(LAST_DIR_PREF, nonPrivateLoadContext, {
|
||||
handleCompletion: resolve,
|
||||
})
|
||||
),
|
||||
new Promise(resolve =>
|
||||
lazy.cps2.removeByName(LAST_DIR_PREF, privateLoadContext, {
|
||||
handleCompletion: resolve,
|
||||
})
|
||||
),
|
||||
];
|
||||
// This is for testing purposes.
|
||||
if (aSubject && typeof subject == "object") {
|
||||
aSubject.promise = Promise.all(promises);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -73,50 +92,52 @@ function readLastDirPref() {
|
||||
}
|
||||
}
|
||||
|
||||
function isContentPrefEnabled() {
|
||||
try {
|
||||
return Services.prefs.getBoolPref(SAVE_PER_SITE_PREF);
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
lazy,
|
||||
"isContentPrefEnabled",
|
||||
SAVE_PER_SITE_PREF,
|
||||
true
|
||||
);
|
||||
|
||||
var gDownloadLastDirFile = readLastDirPref();
|
||||
|
||||
// aForcePrivate is only used when aWindow is null.
|
||||
export function DownloadLastDir(aWindow, aForcePrivate) {
|
||||
let isPrivate = false;
|
||||
if (aWindow === null) {
|
||||
isPrivate = aForcePrivate || PrivateBrowsingUtils.permanentPrivateBrowsing;
|
||||
} else {
|
||||
let loadContext = aWindow.docShell.QueryInterface(Ci.nsILoadContext);
|
||||
isPrivate = loadContext.usePrivateBrowsing;
|
||||
export class DownloadLastDir {
|
||||
// aForcePrivate is only used when aWindow is null.
|
||||
constructor(aWindow, aForcePrivate) {
|
||||
let isPrivate = false;
|
||||
if (aWindow === null) {
|
||||
isPrivate =
|
||||
aForcePrivate || PrivateBrowsingUtils.permanentPrivateBrowsing;
|
||||
} else {
|
||||
let loadContext = aWindow.docShell.QueryInterface(Ci.nsILoadContext);
|
||||
isPrivate = loadContext.usePrivateBrowsing;
|
||||
}
|
||||
|
||||
// We always use a fake load context because we may not have one (i.e.,
|
||||
// in the aWindow == null case) and because the load context associated
|
||||
// with aWindow may disappear by the time we need it. This approach is
|
||||
// safe because we only care about the private browsing state. All the
|
||||
// rest of the load context isn't of interest to the content pref service.
|
||||
this.fakeContext = isPrivate ? privateLoadContext : nonPrivateLoadContext;
|
||||
}
|
||||
|
||||
// We always use a fake load context because we may not have one (i.e.,
|
||||
// in the aWindow == null case) and because the load context associated
|
||||
// with aWindow may disappear by the time we need it. This approach is
|
||||
// safe because we only care about the private browsing state. All the
|
||||
// rest of the load context isn't of interest to the content pref service.
|
||||
this.fakeContext = isPrivate ? privateLoadContext : nonPrivateLoadContext;
|
||||
}
|
||||
|
||||
DownloadLastDir.prototype = {
|
||||
isPrivate: function DownloadLastDir_isPrivate() {
|
||||
isPrivate() {
|
||||
return this.fakeContext.usePrivateBrowsing;
|
||||
},
|
||||
}
|
||||
|
||||
// compat shims
|
||||
get file() {
|
||||
return this._getLastFile();
|
||||
},
|
||||
return this.#getLastFile();
|
||||
}
|
||||
set file(val) {
|
||||
this.setFile(null, val);
|
||||
},
|
||||
}
|
||||
|
||||
cleanupPrivateFile() {
|
||||
gDownloadLastDirFile = null;
|
||||
},
|
||||
}
|
||||
|
||||
_getLastFile() {
|
||||
#getLastFile() {
|
||||
if (gDownloadLastDirFile && !gDownloadLastDirFile.exists()) {
|
||||
gDownloadLastDirFile = null;
|
||||
}
|
||||
@ -128,50 +149,61 @@ DownloadLastDir.prototype = {
|
||||
return gDownloadLastDirFile;
|
||||
}
|
||||
return readLastDirPref();
|
||||
},
|
||||
}
|
||||
|
||||
getFileAsync(aURI, aCallback) {
|
||||
let plainPrefFile = this._getLastFile();
|
||||
if (!aURI || !isContentPrefEnabled()) {
|
||||
Services.tm.dispatchToMainThread(() => aCallback(plainPrefFile));
|
||||
return;
|
||||
async getFileAsync(aURI) {
|
||||
let plainPrefFile = this.#getLastFile();
|
||||
if (!aURI || !lazy.isContentPrefEnabled) {
|
||||
return plainPrefFile;
|
||||
}
|
||||
|
||||
let uri = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
|
||||
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
|
||||
Ci.nsIContentPrefService2
|
||||
);
|
||||
let result = null;
|
||||
cps2.getByDomainAndName(uri, LAST_DIR_PREF, this.fakeContext, {
|
||||
handleResult: aResult => (result = aResult),
|
||||
handleCompletion(aReason) {
|
||||
let file = plainPrefFile;
|
||||
if (
|
||||
aReason == Ci.nsIContentPrefCallback2.COMPLETE_OK &&
|
||||
result instanceof Ci.nsIContentPref
|
||||
) {
|
||||
try {
|
||||
file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
file.initWithPath(result.value);
|
||||
} catch (e) {
|
||||
file = plainPrefFile;
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
lazy.cps2.getByDomainAndName(
|
||||
this.#cpsGroupFromURL(aURI),
|
||||
LAST_DIR_PREF,
|
||||
this.fakeContext,
|
||||
{
|
||||
_result: null,
|
||||
handleResult(aResult) {
|
||||
this._result = aResult;
|
||||
},
|
||||
handleCompletion(aReason) {
|
||||
let file = plainPrefFile;
|
||||
if (
|
||||
aReason == Ci.nsIContentPrefCallback2.COMPLETE_OK &&
|
||||
this._result instanceof Ci.nsIContentPref
|
||||
) {
|
||||
try {
|
||||
file = Cc["@mozilla.org/file/local;1"].createInstance(
|
||||
Ci.nsIFile
|
||||
);
|
||||
file.initWithPath(this._result.value);
|
||||
} catch (e) {
|
||||
file = plainPrefFile;
|
||||
}
|
||||
}
|
||||
resolve(file);
|
||||
},
|
||||
}
|
||||
aCallback(file);
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
setFile(aURI, aFile) {
|
||||
if (aURI && isContentPrefEnabled()) {
|
||||
let uri = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
|
||||
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
|
||||
Ci.nsIContentPrefService2
|
||||
);
|
||||
if (aURI && lazy.isContentPrefEnabled) {
|
||||
if (aFile instanceof Ci.nsIFile) {
|
||||
cps2.set(uri, LAST_DIR_PREF, aFile.path, this.fakeContext);
|
||||
lazy.cps2.set(
|
||||
this.#cpsGroupFromURL(aURI),
|
||||
LAST_DIR_PREF,
|
||||
aFile.path,
|
||||
this.fakeContext
|
||||
);
|
||||
} else {
|
||||
cps2.removeByDomainAndName(uri, LAST_DIR_PREF, this.fakeContext);
|
||||
lazy.cps2.removeByDomainAndName(
|
||||
this.#cpsGroupFromURL(aURI),
|
||||
LAST_DIR_PREF,
|
||||
this.fakeContext
|
||||
);
|
||||
}
|
||||
}
|
||||
if (this.isPrivate()) {
|
||||
@ -185,5 +217,36 @@ DownloadLastDir.prototype = {
|
||||
} else if (Services.prefs.prefHasUserValue(LAST_DIR_PREF)) {
|
||||
Services.prefs.clearUserPref(LAST_DIR_PREF);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-processor to extract a domain name to be used with the content-prefs
|
||||
* service. This specially handles data and file URIs so that the download
|
||||
* dirs are recalled in a more consistent way:
|
||||
* - all file:/// URIs share the same folder
|
||||
* - data: URIs share a folder per mime-type. If a mime-type is not
|
||||
* specified text/plain is assumed.
|
||||
* In any other case the original URL is returned as a string and ContentPrefs
|
||||
* will do its usual parsing.
|
||||
*
|
||||
* @param {string|nsIURI|URL} url The URL to parse
|
||||
* @returns {string} the domain name to use, or the original url.
|
||||
*/
|
||||
#cpsGroupFromURL(url) {
|
||||
if (typeof url == "string") {
|
||||
url = new URL(url);
|
||||
} else if (url instanceof Ci.nsIURI) {
|
||||
url = URL.fromURI(url);
|
||||
}
|
||||
if (!URL.isInstance(url)) {
|
||||
return url;
|
||||
}
|
||||
if (url.protocol == "data:") {
|
||||
return url.href.match(/^data:[^;,]*/i)[0].replace(/:$/, ":text/plain");
|
||||
}
|
||||
if (url.protocol == "file:") {
|
||||
return "file:///";
|
||||
}
|
||||
return url.href;
|
||||
}
|
||||
}
|
||||
|
@ -341,7 +341,7 @@ nsUnknownContentTypeDialog.prototype = {
|
||||
let preferredDir = await Downloads.getPreferredDownloadsDirectory();
|
||||
picker.displayDirectory = new FileUtils.File(preferredDir);
|
||||
|
||||
gDownloadLastDir.getFileAsync(aLauncher.source, lastDir => {
|
||||
gDownloadLastDir.getFileAsync(aLauncher.source).then(lastDir => {
|
||||
if (lastDir && isUsableDirectory(lastDir)) {
|
||||
picker.displayDirectory = lastDir;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user