|
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 }; |