1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/mozapps/downloads/DownloadLastDir.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,197 @@ 1.4 +/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * The behavior implemented by gDownloadLastDir is documented here. 1.11 + * 1.12 + * In normal browsing sessions, gDownloadLastDir uses the browser.download.lastDir 1.13 + * preference to store the last used download directory. The first time the user 1.14 + * switches into the private browsing mode, the last download directory is 1.15 + * preserved to the pref value, but if the user switches to another directory 1.16 + * during the private browsing mode, that directory is not stored in the pref, 1.17 + * and will be merely kept in memory. When leaving the private browsing mode, 1.18 + * this in-memory value will be discarded, and the last download directory 1.19 + * will be reverted to the pref value. 1.20 + * 1.21 + * Both the pref and the in-memory value will be cleared when clearing the 1.22 + * browsing history. This effectively changes the last download directory 1.23 + * to the default download directory on each platform. 1.24 + * 1.25 + * If passed a URI, the last used directory is also stored with that URI in the 1.26 + * content preferences database. This can be disabled by setting the pref 1.27 + * browser.download.lastDir.savePerSite to false. 1.28 + */ 1.29 + 1.30 +const LAST_DIR_PREF = "browser.download.lastDir"; 1.31 +const SAVE_PER_SITE_PREF = LAST_DIR_PREF + ".savePerSite"; 1.32 +const nsIFile = Components.interfaces.nsIFile; 1.33 + 1.34 +this.EXPORTED_SYMBOLS = [ "DownloadLastDir" ]; 1.35 + 1.36 +Components.utils.import("resource://gre/modules/Services.jsm"); 1.37 +Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); 1.38 + 1.39 +let observer = { 1.40 + QueryInterface: function (aIID) { 1.41 + if (aIID.equals(Components.interfaces.nsIObserver) || 1.42 + aIID.equals(Components.interfaces.nsISupports) || 1.43 + aIID.equals(Components.interfaces.nsISupportsWeakReference)) 1.44 + return this; 1.45 + throw Components.results.NS_NOINTERFACE; 1.46 + }, 1.47 + observe: function (aSubject, aTopic, aData) { 1.48 + switch (aTopic) { 1.49 + case "last-pb-context-exited": 1.50 + gDownloadLastDirFile = null; 1.51 + break; 1.52 + case "browser:purge-session-history": 1.53 + gDownloadLastDirFile = null; 1.54 + if (Services.prefs.prefHasUserValue(LAST_DIR_PREF)) 1.55 + Services.prefs.clearUserPref(LAST_DIR_PREF); 1.56 + // Ensure that purging session history causes both the session-only PB cache 1.57 + // and persistent prefs to be cleared. 1.58 + let cps2 = Components.classes["@mozilla.org/content-pref/service;1"]. 1.59 + getService(Components.interfaces.nsIContentPrefService2); 1.60 + 1.61 + cps2.removeByName(LAST_DIR_PREF, {usePrivateBrowsing: false}); 1.62 + cps2.removeByName(LAST_DIR_PREF, {usePrivateBrowsing: true}); 1.63 + break; 1.64 + } 1.65 + } 1.66 +}; 1.67 + 1.68 +let os = Components.classes["@mozilla.org/observer-service;1"] 1.69 + .getService(Components.interfaces.nsIObserverService); 1.70 +os.addObserver(observer, "last-pb-context-exited", true); 1.71 +os.addObserver(observer, "browser:purge-session-history", true); 1.72 + 1.73 +function readLastDirPref() { 1.74 + try { 1.75 + return Services.prefs.getComplexValue(LAST_DIR_PREF, nsIFile); 1.76 + } 1.77 + catch (e) { 1.78 + return null; 1.79 + } 1.80 +} 1.81 + 1.82 +function isContentPrefEnabled() { 1.83 + try { 1.84 + return Services.prefs.getBoolPref(SAVE_PER_SITE_PREF); 1.85 + } 1.86 + catch (e) { 1.87 + return true; 1.88 + } 1.89 +} 1.90 + 1.91 +let gDownloadLastDirFile = readLastDirPref(); 1.92 + 1.93 +this.DownloadLastDir = function DownloadLastDir(aWindow) { 1.94 + this.window = aWindow; 1.95 +} 1.96 + 1.97 +DownloadLastDir.prototype = { 1.98 + isPrivate: function DownloadLastDir_isPrivate() { 1.99 + return PrivateBrowsingUtils.isWindowPrivate(this.window); 1.100 + }, 1.101 + // compat shims 1.102 + get file() this._getLastFile(), 1.103 + set file(val) { this.setFile(null, val); }, 1.104 + cleanupPrivateFile: function () { 1.105 + gDownloadLastDirFile = null; 1.106 + }, 1.107 + // This function is now deprecated as it uses the sync nsIContentPrefService 1.108 + // interface. New consumers should use the getFileAsync function. 1.109 + getFile: function (aURI) { 1.110 + let Deprecated = Components.utils.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated; 1.111 + Deprecated.warning("DownloadLastDir.getFile is deprecated. Please use getFileAsync instead.", 1.112 + "https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/DownloadLastDir.jsm", 1.113 + Components.stack.caller); 1.114 + 1.115 + if (aURI && isContentPrefEnabled()) { 1.116 + let loadContext = this.window 1.117 + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) 1.118 + .getInterface(Components.interfaces.nsIWebNavigation) 1.119 + .QueryInterface(Components.interfaces.nsILoadContext); 1.120 + let lastDir = Services.contentPrefs.getPref(aURI, LAST_DIR_PREF, loadContext); 1.121 + if (lastDir) { 1.122 + var lastDirFile = Components.classes["@mozilla.org/file/local;1"] 1.123 + .createInstance(Components.interfaces.nsIFile); 1.124 + lastDirFile.initWithPath(lastDir); 1.125 + return lastDirFile; 1.126 + } 1.127 + } 1.128 + return this._getLastFile(); 1.129 + }, 1.130 + 1.131 + _getLastFile: function () { 1.132 + if (gDownloadLastDirFile && !gDownloadLastDirFile.exists()) 1.133 + gDownloadLastDirFile = null; 1.134 + 1.135 + if (this.isPrivate()) { 1.136 + if (!gDownloadLastDirFile) 1.137 + gDownloadLastDirFile = readLastDirPref(); 1.138 + return gDownloadLastDirFile; 1.139 + } 1.140 + return readLastDirPref(); 1.141 + }, 1.142 + 1.143 + getFileAsync: function(aURI, aCallback) { 1.144 + let plainPrefFile = this._getLastFile(); 1.145 + if (!aURI || !isContentPrefEnabled()) { 1.146 + Services.tm.mainThread.dispatch(function() aCallback(plainPrefFile), 1.147 + Components.interfaces.nsIThread.DISPATCH_NORMAL); 1.148 + return; 1.149 + } 1.150 + 1.151 + let uri = aURI instanceof Components.interfaces.nsIURI ? aURI.spec : aURI; 1.152 + let cps2 = Components.classes["@mozilla.org/content-pref/service;1"] 1.153 + .getService(Components.interfaces.nsIContentPrefService2); 1.154 + let loadContext = this.window 1.155 + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) 1.156 + .getInterface(Components.interfaces.nsIWebNavigation) 1.157 + .QueryInterface(Components.interfaces.nsILoadContext); 1.158 + let result = null; 1.159 + cps2.getByDomainAndName(uri, LAST_DIR_PREF, loadContext, { 1.160 + handleResult: function(aResult) result = aResult, 1.161 + handleCompletion: function(aReason) { 1.162 + let file = plainPrefFile; 1.163 + if (aReason == Components.interfaces.nsIContentPrefCallback2.COMPLETE_OK && 1.164 + result instanceof Components.interfaces.nsIContentPref) { 1.165 + file = Components.classes["@mozilla.org/file/local;1"] 1.166 + .createInstance(Components.interfaces.nsIFile); 1.167 + file.initWithPath(result.value); 1.168 + } 1.169 + aCallback(file); 1.170 + } 1.171 + }); 1.172 + }, 1.173 + 1.174 + setFile: function (aURI, aFile) { 1.175 + if (aURI && isContentPrefEnabled()) { 1.176 + let uri = aURI instanceof Components.interfaces.nsIURI ? aURI.spec : aURI; 1.177 + let cps2 = Components.classes["@mozilla.org/content-pref/service;1"] 1.178 + .getService(Components.interfaces.nsIContentPrefService2); 1.179 + let loadContext = this.window 1.180 + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) 1.181 + .getInterface(Components.interfaces.nsIWebNavigation) 1.182 + .QueryInterface(Components.interfaces.nsILoadContext); 1.183 + if (aFile instanceof Components.interfaces.nsIFile) 1.184 + cps2.set(uri, LAST_DIR_PREF, aFile.path, loadContext); 1.185 + else 1.186 + cps2.removeByDomainAndName(uri, LAST_DIR_PREF, loadContext); 1.187 + } 1.188 + if (this.isPrivate()) { 1.189 + if (aFile instanceof Components.interfaces.nsIFile) 1.190 + gDownloadLastDirFile = aFile.clone(); 1.191 + else 1.192 + gDownloadLastDirFile = null; 1.193 + } else { 1.194 + if (aFile instanceof Components.interfaces.nsIFile) 1.195 + Services.prefs.setComplexValue(LAST_DIR_PREF, nsIFile, aFile); 1.196 + else if (Services.prefs.prefHasUserValue(LAST_DIR_PREF)) 1.197 + Services.prefs.clearUserPref(LAST_DIR_PREF); 1.198 + } 1.199 + } 1.200 +};