summaryrefslogtreecommitdiff
path: root/components/downloads/src/DownloadLastDir.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'components/downloads/src/DownloadLastDir.jsm')
-rw-r--r--components/downloads/src/DownloadLastDir.jsm195
1 files changed, 195 insertions, 0 deletions
diff --git a/components/downloads/src/DownloadLastDir.jsm b/components/downloads/src/DownloadLastDir.jsm
new file mode 100644
index 000000000..552fd3ef1
--- /dev/null
+++ b/components/downloads/src/DownloadLastDir.jsm
@@ -0,0 +1,195 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+/*
+ * The behavior implemented by gDownloadLastDir is documented here.
+ *
+ * In normal browsing sessions, gDownloadLastDir uses the browser.download.lastDir
+ * preference to store the last used download directory. The first time the user
+ * switches into the private browsing mode, the last download directory is
+ * preserved to the pref value, but if the user switches to another directory
+ * during the private browsing mode, that directory is not stored in the pref,
+ * and will be merely kept in memory. When leaving the private browsing mode,
+ * this in-memory value will be discarded, and the last download directory
+ * will be reverted to the pref value.
+ *
+ * Both the pref and the in-memory value will be cleared when clearing the
+ * browsing history. This effectively changes the last download directory
+ * to the default download directory on each platform.
+ *
+ * If passed a URI, the last used directory is also stored with that URI in the
+ * content preferences database. This can be disabled by setting the pref
+ * browser.download.lastDir.savePerSite to false.
+ */
+
+const LAST_DIR_PREF = "browser.download.lastDir";
+const SAVE_PER_SITE_PREF = LAST_DIR_PREF + ".savePerSite";
+const nsIFile = Components.interfaces.nsIFile;
+
+this.EXPORTED_SYMBOLS = [ "DownloadLastDir" ];
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+var observer = {
+ QueryInterface: function (aIID) {
+ if (aIID.equals(Components.interfaces.nsIObserver) ||
+ aIID.equals(Components.interfaces.nsISupports) ||
+ aIID.equals(Components.interfaces.nsISupportsWeakReference))
+ return this;
+ throw Components.results.NS_NOINTERFACE;
+ },
+ observe: function (aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "last-pb-context-exited":
+ gDownloadLastDirFile = null;
+ break;
+ case "browser:purge-session-history":
+ gDownloadLastDirFile = null;
+ if (Services.prefs.prefHasUserValue(LAST_DIR_PREF))
+ Services.prefs.clearUserPref(LAST_DIR_PREF);
+ // Ensure that purging session history causes both the session-only PB cache
+ // and persistent prefs to be cleared.
+ let cps2 = Components.classes["@mozilla.org/content-pref/service;1"].
+ getService(Components.interfaces.nsIContentPrefService2);
+
+ cps2.removeByName(LAST_DIR_PREF, {usePrivateBrowsing: false});
+ cps2.removeByName(LAST_DIR_PREF, {usePrivateBrowsing: true});
+ break;
+ }
+ }
+};
+
+var os = Components.classes["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService);
+os.addObserver(observer, "last-pb-context-exited", true);
+os.addObserver(observer, "browser:purge-session-history", true);
+
+function readLastDirPref() {
+ try {
+ return Services.prefs.getComplexValue(LAST_DIR_PREF, nsIFile);
+ }
+ catch (e) {
+ return null;
+ }
+}
+
+function isContentPrefEnabled() {
+ try {
+ return Services.prefs.getBoolPref(SAVE_PER_SITE_PREF);
+ }
+ catch (e) {
+ return true;
+ }
+}
+
+var gDownloadLastDirFile = readLastDirPref();
+
+this.DownloadLastDir = function DownloadLastDir(aWindow) {
+ let loadContext = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIWebNavigation)
+ .QueryInterface(Components.interfaces.nsILoadContext);
+ // Need this in case the real thing has gone away by the time we need it.
+ // 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 = {
+ QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsILoadContext]),
+ usePrivateBrowsing: loadContext.usePrivateBrowsing,
+ originAttributes: {},
+ };
+}
+
+DownloadLastDir.prototype = {
+ isPrivate: function DownloadLastDir_isPrivate() {
+ return this.fakeContext.usePrivateBrowsing;
+ },
+ // compat shims
+ get file() { return this._getLastFile(); },
+ set file(val) { this.setFile(null, val); },
+ cleanupPrivateFile: function () {
+ gDownloadLastDirFile = null;
+ },
+ // This function is now deprecated as it uses the sync nsIContentPrefService
+ // interface. New consumers should use the getFileAsync function.
+ getFile: function (aURI) {
+ let Deprecated = Components.utils.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated;
+ Deprecated.warning("DownloadLastDir.getFile is deprecated. Please use getFileAsync instead.",
+ "https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/DownloadLastDir.jsm",
+ Components.stack.caller);
+
+ if (aURI && isContentPrefEnabled()) {
+ let lastDir = Services.contentPrefs.getPref(aURI, LAST_DIR_PREF, this.fakeContext);
+ if (lastDir) {
+ var lastDirFile = Components.classes["@mozilla.org/file/local;1"]
+ .createInstance(Components.interfaces.nsIFile);
+ lastDirFile.initWithPath(lastDir);
+ return lastDirFile;
+ }
+ }
+ return this._getLastFile();
+ },
+
+ _getLastFile: function () {
+ if (gDownloadLastDirFile && !gDownloadLastDirFile.exists())
+ gDownloadLastDirFile = null;
+
+ if (this.isPrivate()) {
+ if (!gDownloadLastDirFile)
+ gDownloadLastDirFile = readLastDirPref();
+ return gDownloadLastDirFile;
+ }
+ return readLastDirPref();
+ },
+
+ getFileAsync: function(aURI, aCallback) {
+ let plainPrefFile = this._getLastFile();
+ if (!aURI || !isContentPrefEnabled()) {
+ Services.tm.mainThread.dispatch(() => aCallback(plainPrefFile),
+ Components.interfaces.nsIThread.DISPATCH_NORMAL);
+ return;
+ }
+
+ let uri = aURI instanceof Components.interfaces.nsIURI ? aURI.spec : aURI;
+ let cps2 = Components.classes["@mozilla.org/content-pref/service;1"]
+ .getService(Components.interfaces.nsIContentPrefService2);
+ let result = null;
+ cps2.getByDomainAndName(uri, LAST_DIR_PREF, this.fakeContext, {
+ handleResult: aResult => result = aResult,
+ handleCompletion: function(aReason) {
+ let file = plainPrefFile;
+ if (aReason == Components.interfaces.nsIContentPrefCallback2.COMPLETE_OK &&
+ result instanceof Components.interfaces.nsIContentPref) {
+ file = Components.classes["@mozilla.org/file/local;1"]
+ .createInstance(Components.interfaces.nsIFile);
+ file.initWithPath(result.value);
+ }
+ aCallback(file);
+ }
+ });
+ },
+
+ setFile: function (aURI, aFile) {
+ if (aURI && isContentPrefEnabled()) {
+ let uri = aURI instanceof Components.interfaces.nsIURI ? aURI.spec : aURI;
+ let cps2 = Components.classes["@mozilla.org/content-pref/service;1"]
+ .getService(Components.interfaces.nsIContentPrefService2);
+ if (aFile instanceof Components.interfaces.nsIFile)
+ cps2.set(uri, LAST_DIR_PREF, aFile.path, this.fakeContext);
+ else
+ cps2.removeByDomainAndName(uri, LAST_DIR_PREF, this.fakeContext);
+ }
+ if (this.isPrivate()) {
+ if (aFile instanceof Components.interfaces.nsIFile)
+ gDownloadLastDirFile = aFile.clone();
+ else
+ gDownloadLastDirFile = null;
+ } else if (aFile instanceof Components.interfaces.nsIFile) {
+ Services.prefs.setComplexValue(LAST_DIR_PREF, nsIFile, aFile);
+ } else if (Services.prefs.prefHasUserValue(LAST_DIR_PREF)) {
+ Services.prefs.clearUserPref(LAST_DIR_PREF);
+ }
+ }
+};