toolkit/mozapps/downloads/DownloadLastDir.jsm

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:01241042495b
1 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /*
7 * The behavior implemented by gDownloadLastDir is documented here.
8 *
9 * In normal browsing sessions, gDownloadLastDir uses the browser.download.lastDir
10 * preference to store the last used download directory. The first time the user
11 * switches into the private browsing mode, the last download directory is
12 * preserved to the pref value, but if the user switches to another directory
13 * during the private browsing mode, that directory is not stored in the pref,
14 * and will be merely kept in memory. When leaving the private browsing mode,
15 * this in-memory value will be discarded, and the last download directory
16 * will be reverted to the pref value.
17 *
18 * Both the pref and the in-memory value will be cleared when clearing the
19 * browsing history. This effectively changes the last download directory
20 * to the default download directory on each platform.
21 *
22 * If passed a URI, the last used directory is also stored with that URI in the
23 * content preferences database. This can be disabled by setting the pref
24 * browser.download.lastDir.savePerSite to false.
25 */
26
27 const LAST_DIR_PREF = "browser.download.lastDir";
28 const SAVE_PER_SITE_PREF = LAST_DIR_PREF + ".savePerSite";
29 const nsIFile = Components.interfaces.nsIFile;
30
31 this.EXPORTED_SYMBOLS = [ "DownloadLastDir" ];
32
33 Components.utils.import("resource://gre/modules/Services.jsm");
34 Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
35
36 let observer = {
37 QueryInterface: function (aIID) {
38 if (aIID.equals(Components.interfaces.nsIObserver) ||
39 aIID.equals(Components.interfaces.nsISupports) ||
40 aIID.equals(Components.interfaces.nsISupportsWeakReference))
41 return this;
42 throw Components.results.NS_NOINTERFACE;
43 },
44 observe: function (aSubject, aTopic, aData) {
45 switch (aTopic) {
46 case "last-pb-context-exited":
47 gDownloadLastDirFile = null;
48 break;
49 case "browser:purge-session-history":
50 gDownloadLastDirFile = null;
51 if (Services.prefs.prefHasUserValue(LAST_DIR_PREF))
52 Services.prefs.clearUserPref(LAST_DIR_PREF);
53 // Ensure that purging session history causes both the session-only PB cache
54 // and persistent prefs to be cleared.
55 let cps2 = Components.classes["@mozilla.org/content-pref/service;1"].
56 getService(Components.interfaces.nsIContentPrefService2);
57
58 cps2.removeByName(LAST_DIR_PREF, {usePrivateBrowsing: false});
59 cps2.removeByName(LAST_DIR_PREF, {usePrivateBrowsing: true});
60 break;
61 }
62 }
63 };
64
65 let os = Components.classes["@mozilla.org/observer-service;1"]
66 .getService(Components.interfaces.nsIObserverService);
67 os.addObserver(observer, "last-pb-context-exited", true);
68 os.addObserver(observer, "browser:purge-session-history", true);
69
70 function readLastDirPref() {
71 try {
72 return Services.prefs.getComplexValue(LAST_DIR_PREF, nsIFile);
73 }
74 catch (e) {
75 return null;
76 }
77 }
78
79 function isContentPrefEnabled() {
80 try {
81 return Services.prefs.getBoolPref(SAVE_PER_SITE_PREF);
82 }
83 catch (e) {
84 return true;
85 }
86 }
87
88 let gDownloadLastDirFile = readLastDirPref();
89
90 this.DownloadLastDir = function DownloadLastDir(aWindow) {
91 this.window = aWindow;
92 }
93
94 DownloadLastDir.prototype = {
95 isPrivate: function DownloadLastDir_isPrivate() {
96 return PrivateBrowsingUtils.isWindowPrivate(this.window);
97 },
98 // compat shims
99 get file() this._getLastFile(),
100 set file(val) { this.setFile(null, val); },
101 cleanupPrivateFile: function () {
102 gDownloadLastDirFile = null;
103 },
104 // This function is now deprecated as it uses the sync nsIContentPrefService
105 // interface. New consumers should use the getFileAsync function.
106 getFile: function (aURI) {
107 let Deprecated = Components.utils.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated;
108 Deprecated.warning("DownloadLastDir.getFile is deprecated. Please use getFileAsync instead.",
109 "https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/DownloadLastDir.jsm",
110 Components.stack.caller);
111
112 if (aURI && isContentPrefEnabled()) {
113 let loadContext = this.window
114 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
115 .getInterface(Components.interfaces.nsIWebNavigation)
116 .QueryInterface(Components.interfaces.nsILoadContext);
117 let lastDir = Services.contentPrefs.getPref(aURI, LAST_DIR_PREF, loadContext);
118 if (lastDir) {
119 var lastDirFile = Components.classes["@mozilla.org/file/local;1"]
120 .createInstance(Components.interfaces.nsIFile);
121 lastDirFile.initWithPath(lastDir);
122 return lastDirFile;
123 }
124 }
125 return this._getLastFile();
126 },
127
128 _getLastFile: function () {
129 if (gDownloadLastDirFile && !gDownloadLastDirFile.exists())
130 gDownloadLastDirFile = null;
131
132 if (this.isPrivate()) {
133 if (!gDownloadLastDirFile)
134 gDownloadLastDirFile = readLastDirPref();
135 return gDownloadLastDirFile;
136 }
137 return readLastDirPref();
138 },
139
140 getFileAsync: function(aURI, aCallback) {
141 let plainPrefFile = this._getLastFile();
142 if (!aURI || !isContentPrefEnabled()) {
143 Services.tm.mainThread.dispatch(function() aCallback(plainPrefFile),
144 Components.interfaces.nsIThread.DISPATCH_NORMAL);
145 return;
146 }
147
148 let uri = aURI instanceof Components.interfaces.nsIURI ? aURI.spec : aURI;
149 let cps2 = Components.classes["@mozilla.org/content-pref/service;1"]
150 .getService(Components.interfaces.nsIContentPrefService2);
151 let loadContext = this.window
152 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
153 .getInterface(Components.interfaces.nsIWebNavigation)
154 .QueryInterface(Components.interfaces.nsILoadContext);
155 let result = null;
156 cps2.getByDomainAndName(uri, LAST_DIR_PREF, loadContext, {
157 handleResult: function(aResult) result = aResult,
158 handleCompletion: function(aReason) {
159 let file = plainPrefFile;
160 if (aReason == Components.interfaces.nsIContentPrefCallback2.COMPLETE_OK &&
161 result instanceof Components.interfaces.nsIContentPref) {
162 file = Components.classes["@mozilla.org/file/local;1"]
163 .createInstance(Components.interfaces.nsIFile);
164 file.initWithPath(result.value);
165 }
166 aCallback(file);
167 }
168 });
169 },
170
171 setFile: function (aURI, aFile) {
172 if (aURI && isContentPrefEnabled()) {
173 let uri = aURI instanceof Components.interfaces.nsIURI ? aURI.spec : aURI;
174 let cps2 = Components.classes["@mozilla.org/content-pref/service;1"]
175 .getService(Components.interfaces.nsIContentPrefService2);
176 let loadContext = this.window
177 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
178 .getInterface(Components.interfaces.nsIWebNavigation)
179 .QueryInterface(Components.interfaces.nsILoadContext);
180 if (aFile instanceof Components.interfaces.nsIFile)
181 cps2.set(uri, LAST_DIR_PREF, aFile.path, loadContext);
182 else
183 cps2.removeByDomainAndName(uri, LAST_DIR_PREF, loadContext);
184 }
185 if (this.isPrivate()) {
186 if (aFile instanceof Components.interfaces.nsIFile)
187 gDownloadLastDirFile = aFile.clone();
188 else
189 gDownloadLastDirFile = null;
190 } else {
191 if (aFile instanceof Components.interfaces.nsIFile)
192 Services.prefs.setComplexValue(LAST_DIR_PREF, nsIFile, aFile);
193 else if (Services.prefs.prefHasUserValue(LAST_DIR_PREF))
194 Services.prefs.clearUserPref(LAST_DIR_PREF);
195 }
196 }
197 };

mercurial