Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; 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/. */
6 #include "nsProfileDirServiceProvider.h"
7 #include "nsProfileStringTypes.h"
8 #include "nsProfileLock.h"
9 #include "nsIFile.h"
10 #include "nsDirectoryServiceDefs.h"
11 #include "nsAppDirectoryServiceDefs.h"
12 #include "nsISupportsUtils.h"
13 #include "nsISimpleEnumerator.h"
14 #include "nsIObserverService.h"
16 // File Name Defines
18 #define PREFS_FILE_50_NAME NS_LITERAL_CSTRING("prefs.js")
19 #define PREFS_FILE_METRO_50_NAME NS_LITERAL_CSTRING("metro-prefs.js")
20 #define USER_CHROME_DIR_50_NAME NS_LITERAL_CSTRING("chrome")
21 #define LOCAL_STORE_FILE_50_NAME NS_LITERAL_CSTRING("localstore.rdf")
22 #define PANELS_FILE_50_NAME NS_LITERAL_CSTRING("panels.rdf")
23 #define MIME_TYPES_FILE_50_NAME NS_LITERAL_CSTRING("mimeTypes.rdf")
24 #define BOOKMARKS_FILE_50_NAME NS_LITERAL_CSTRING("bookmarks.html")
25 #define DOWNLOADS_FILE_50_NAME NS_LITERAL_CSTRING("downloads.rdf")
26 #define SEARCH_FILE_50_NAME NS_LITERAL_CSTRING("search.rdf" )
28 //*****************************************************************************
29 // nsProfileDirServiceProvider::nsProfileDirServiceProvider
30 //*****************************************************************************
32 nsProfileDirServiceProvider::nsProfileDirServiceProvider(bool aNotifyObservers) :
33 #ifdef MOZ_PROFILELOCKING
34 mProfileDirLock(nullptr),
35 #endif
36 mNotifyObservers(aNotifyObservers),
37 mSharingEnabled(false)
38 {
39 }
42 nsProfileDirServiceProvider::~nsProfileDirServiceProvider()
43 {
44 #ifdef MOZ_PROFILELOCKING
45 delete mProfileDirLock;
46 #endif
47 }
49 nsresult
50 nsProfileDirServiceProvider::SetProfileDir(nsIFile* aProfileDir,
51 nsIFile* aLocalProfileDir)
52 {
53 if (!aLocalProfileDir)
54 aLocalProfileDir = aProfileDir;
55 if (mProfileDir) {
56 bool isEqual;
57 if (aProfileDir &&
58 NS_SUCCEEDED(aProfileDir->Equals(mProfileDir, &isEqual)) && isEqual) {
59 NS_WARNING("Setting profile dir to same as current");
60 return NS_OK;
61 }
62 #ifdef MOZ_PROFILELOCKING
63 mProfileDirLock->Unlock();
64 #endif
65 UndefineFileLocations();
66 }
67 mProfileDir = aProfileDir;
68 mLocalProfileDir = aLocalProfileDir;
69 if (!mProfileDir)
70 return NS_OK;
72 nsresult rv = InitProfileDir(mProfileDir);
73 if (NS_FAILED(rv))
74 return rv;
76 // Make sure that the local profile dir exists
77 // we just try to create it - if it exists already, that'll fail; ignore
78 // errors
79 mLocalProfileDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
81 #ifdef MOZ_PROFILELOCKING
82 // Lock the non-shared sub-dir if we are sharing,
83 // the whole profile dir if we are not.
84 nsCOMPtr<nsIFile> dirToLock;
85 if (mSharingEnabled)
86 dirToLock = mNonSharedProfileDir;
87 else
88 dirToLock = mProfileDir;
89 rv = mProfileDirLock->Lock(dirToLock, nullptr);
90 if (NS_FAILED(rv))
91 return rv;
92 #endif
94 if (mNotifyObservers) {
95 nsCOMPtr<nsIObserverService> observerService =
96 do_GetService("@mozilla.org/observer-service;1");
97 if (!observerService)
98 return NS_ERROR_FAILURE;
100 NS_NAMED_LITERAL_STRING(context, "startup");
101 // Notify observers that the profile has changed - Here they respond to new profile
102 observerService->NotifyObservers(nullptr, "profile-do-change", context.get());
103 // Now observers can respond to something another observer did on "profile-do-change"
104 observerService->NotifyObservers(nullptr, "profile-after-change", context.get());
105 }
107 return NS_OK;
108 }
110 nsresult
111 nsProfileDirServiceProvider::Register()
112 {
113 nsCOMPtr<nsIDirectoryService> directoryService =
114 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
115 if (!directoryService)
116 return NS_ERROR_FAILURE;
117 return directoryService->RegisterProvider(this);
118 }
120 nsresult
121 nsProfileDirServiceProvider::Shutdown()
122 {
123 if (!mNotifyObservers)
124 return NS_OK;
126 nsCOMPtr<nsIObserverService> observerService =
127 do_GetService("@mozilla.org/observer-service;1");
128 if (!observerService)
129 return NS_ERROR_FAILURE;
131 NS_NAMED_LITERAL_STRING(context, "shutdown-persist");
132 observerService->NotifyObservers(nullptr, "profile-before-change", context.get());
133 observerService->NotifyObservers(nullptr, "profile-before-change2", context.get());
134 return NS_OK;
135 }
137 //*****************************************************************************
138 // nsProfileDirServiceProvider::nsISupports
139 //*****************************************************************************
141 NS_IMPL_ISUPPORTS(nsProfileDirServiceProvider,
142 nsIDirectoryServiceProvider)
144 //*****************************************************************************
145 // nsProfileDirServiceProvider::nsIDirectoryServiceProvider
146 //*****************************************************************************
148 NS_IMETHODIMP
149 nsProfileDirServiceProvider::GetFile(const char *prop, bool *persistant, nsIFile **_retval)
150 {
151 NS_ENSURE_ARG(prop);
152 NS_ENSURE_ARG_POINTER(persistant);
153 NS_ENSURE_ARG_POINTER(_retval);
155 // Don't assert - we can be called many times before SetProfileDir() has been called.
156 if (!mProfileDir)
157 return NS_ERROR_FAILURE;
159 *persistant = true;
160 nsIFile* domainDir = mProfileDir;
162 nsCOMPtr<nsIFile> localFile;
163 nsresult rv = NS_ERROR_FAILURE;
165 if (strcmp(prop, NS_APP_PREFS_50_DIR) == 0) {
166 rv = domainDir->Clone(getter_AddRefs(localFile));
167 }
168 else if (strcmp(prop, NS_APP_PREFS_50_FILE) == 0) {
169 rv = domainDir->Clone(getter_AddRefs(localFile));
170 if (NS_SUCCEEDED(rv))
171 rv = localFile->AppendNative(PREFS_FILE_50_NAME);
172 }
173 else if (strcmp(prop, NS_METRO_APP_PREFS_50_FILE) == 0) {
174 rv = domainDir->Clone(getter_AddRefs(localFile));
175 if (NS_SUCCEEDED(rv))
176 rv = localFile->AppendNative(PREFS_FILE_METRO_50_NAME);
177 }
178 else if (strcmp(prop, NS_APP_USER_PROFILE_50_DIR) == 0) {
179 rv = domainDir->Clone(getter_AddRefs(localFile));
180 }
181 else if (strcmp(prop, NS_APP_USER_PROFILE_LOCAL_50_DIR) == 0) {
182 rv = mLocalProfileDir->Clone(getter_AddRefs(localFile));
183 }
184 else if (strcmp(prop, NS_APP_USER_CHROME_DIR) == 0) {
185 rv = domainDir->Clone(getter_AddRefs(localFile));
186 if (NS_SUCCEEDED(rv))
187 rv = localFile->AppendNative(USER_CHROME_DIR_50_NAME);
188 }
189 else if (strcmp(prop, NS_APP_LOCALSTORE_50_FILE) == 0) {
190 rv = domainDir->Clone(getter_AddRefs(localFile));
191 if (NS_SUCCEEDED(rv)) {
192 rv = localFile->AppendNative(LOCAL_STORE_FILE_50_NAME);
193 if (NS_SUCCEEDED(rv)) {
194 // it's OK if we can't copy the file... it will be created
195 // by client code.
196 (void) EnsureProfileFileExists(localFile, domainDir);
197 }
198 }
199 }
200 else if (strcmp(prop, NS_APP_USER_PANELS_50_FILE) == 0) {
201 rv = domainDir->Clone(getter_AddRefs(localFile));
202 if (NS_SUCCEEDED(rv)) {
203 rv = localFile->AppendNative(PANELS_FILE_50_NAME);
204 if (NS_SUCCEEDED(rv))
205 rv = EnsureProfileFileExists(localFile, domainDir);
206 }
207 }
208 else if (strcmp(prop, NS_APP_USER_MIMETYPES_50_FILE) == 0) {
209 rv = domainDir->Clone(getter_AddRefs(localFile));
210 if (NS_SUCCEEDED(rv)) {
211 rv = localFile->AppendNative(MIME_TYPES_FILE_50_NAME);
212 if (NS_SUCCEEDED(rv))
213 rv = EnsureProfileFileExists(localFile, domainDir);
214 }
215 }
216 else if (strcmp(prop, NS_APP_BOOKMARKS_50_FILE) == 0) {
217 rv = domainDir->Clone(getter_AddRefs(localFile));
218 if (NS_SUCCEEDED(rv))
219 rv = localFile->AppendNative(BOOKMARKS_FILE_50_NAME);
220 }
221 else if (strcmp(prop, NS_APP_DOWNLOADS_50_FILE) == 0) {
222 rv = domainDir->Clone(getter_AddRefs(localFile));
223 if (NS_SUCCEEDED(rv))
224 rv = localFile->AppendNative(DOWNLOADS_FILE_50_NAME);
225 }
226 else if (strcmp(prop, NS_APP_SEARCH_50_FILE) == 0) {
227 rv = domainDir->Clone(getter_AddRefs(localFile));
228 if (NS_SUCCEEDED(rv)) {
229 rv = localFile->AppendNative(SEARCH_FILE_50_NAME);
230 if (NS_SUCCEEDED(rv))
231 rv = EnsureProfileFileExists(localFile, domainDir);
232 }
233 }
235 if (NS_FAILED(rv)) {
236 return rv;
237 }
239 localFile.forget(_retval);
240 return NS_OK;
241 }
243 //*****************************************************************************
244 // Protected methods
245 //*****************************************************************************
247 nsresult
248 nsProfileDirServiceProvider::Initialize()
249 {
250 #ifdef MOZ_PROFILELOCKING
251 mProfileDirLock = new nsProfileLock;
252 if (!mProfileDirLock)
253 return NS_ERROR_OUT_OF_MEMORY;
254 #endif
256 return NS_OK;
257 }
259 nsresult
260 nsProfileDirServiceProvider::InitProfileDir(nsIFile *profileDir)
261 {
262 // Make sure our "Profile" folder exists.
263 // If it does not, copy the profile defaults to its location.
265 nsresult rv;
266 bool exists;
267 rv = profileDir->Exists(&exists);
268 if (NS_FAILED(rv))
269 return rv;
271 if (!exists) {
272 nsCOMPtr<nsIFile> profileDefaultsDir;
273 nsCOMPtr<nsIFile> profileDirParent;
274 nsAutoCString profileDirName;
276 (void)profileDir->GetParent(getter_AddRefs(profileDirParent));
277 if (!profileDirParent)
278 return NS_ERROR_FAILURE;
279 rv = profileDir->GetNativeLeafName(profileDirName);
280 if (NS_FAILED(rv))
281 return rv;
283 rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_50_DIR, getter_AddRefs(profileDefaultsDir));
284 if (NS_FAILED(rv)) {
285 rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR, getter_AddRefs(profileDefaultsDir));
286 if (NS_FAILED(rv))
287 return rv;
288 }
289 rv = profileDefaultsDir->CopyToNative(profileDirParent, profileDirName);
290 if (NS_FAILED(rv)) {
291 // if copying failed, lets just ensure that the profile directory exists.
292 profileDirParent->AppendNative(profileDirName);
293 rv = profileDirParent->Create(nsIFile::DIRECTORY_TYPE, 0700);
294 if (NS_FAILED(rv))
295 return rv;
296 }
298 rv = profileDir->SetPermissions(0700);
299 if (NS_FAILED(rv))
300 return rv;
301 }
302 else {
303 bool isDir;
304 rv = profileDir->IsDirectory(&isDir);
306 if (NS_FAILED(rv))
307 return rv;
308 if (!isDir)
309 return NS_ERROR_FILE_NOT_DIRECTORY;
310 }
312 if (mNonSharedDirName.Length())
313 rv = InitNonSharedProfileDir();
315 return rv;
316 }
318 nsresult
319 nsProfileDirServiceProvider::InitNonSharedProfileDir()
320 {
321 nsresult rv;
323 NS_ENSURE_STATE(mProfileDir);
324 NS_ENSURE_STATE(mNonSharedDirName.Length());
326 nsCOMPtr<nsIFile> localDir;
327 rv = mProfileDir->Clone(getter_AddRefs(localDir));
328 if (NS_SUCCEEDED(rv)) {
329 rv = localDir->Append(mNonSharedDirName);
330 if (NS_SUCCEEDED(rv)) {
331 bool exists;
332 rv = localDir->Exists(&exists);
333 if (NS_SUCCEEDED(rv)) {
334 if (!exists) {
335 rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
336 }
337 else {
338 bool isDir;
339 rv = localDir->IsDirectory(&isDir);
340 if (NS_SUCCEEDED(rv)) {
341 if (!isDir)
342 rv = NS_ERROR_FILE_NOT_DIRECTORY;
343 }
344 }
345 if (NS_SUCCEEDED(rv))
346 mNonSharedProfileDir = localDir;
347 }
348 }
349 }
350 return rv;
351 }
353 nsresult
354 nsProfileDirServiceProvider::EnsureProfileFileExists(nsIFile *aFile, nsIFile *destDir)
355 {
356 nsresult rv;
357 bool exists;
359 rv = aFile->Exists(&exists);
360 if (NS_FAILED(rv))
361 return rv;
362 if (exists)
363 return NS_OK;
365 nsCOMPtr<nsIFile> defaultsFile;
367 // Attempt first to get the localized subdir of the defaults
368 rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_50_DIR, getter_AddRefs(defaultsFile));
369 if (NS_FAILED(rv)) {
370 // If that has not been defined, use the top level of the defaults
371 rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR, getter_AddRefs(defaultsFile));
372 if (NS_FAILED(rv))
373 return rv;
374 }
376 nsAutoCString leafName;
377 rv = aFile->GetNativeLeafName(leafName);
378 if (NS_FAILED(rv))
379 return rv;
380 rv = defaultsFile->AppendNative(leafName);
381 if (NS_FAILED(rv))
382 return rv;
384 return defaultsFile->CopyTo(destDir, EmptyString());
385 }
387 nsresult
388 nsProfileDirServiceProvider::UndefineFileLocations()
389 {
390 nsresult rv;
392 nsCOMPtr<nsIProperties> directoryService =
393 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
394 NS_ENSURE_TRUE(directoryService, NS_ERROR_FAILURE);
396 (void) directoryService->Undefine(NS_APP_PREFS_50_DIR);
397 (void) directoryService->Undefine(NS_APP_PREFS_50_FILE);
398 (void) directoryService->Undefine(NS_APP_USER_PROFILE_50_DIR);
399 (void) directoryService->Undefine(NS_APP_USER_CHROME_DIR);
400 (void) directoryService->Undefine(NS_APP_LOCALSTORE_50_FILE);
401 (void) directoryService->Undefine(NS_APP_USER_PANELS_50_FILE);
402 (void) directoryService->Undefine(NS_APP_USER_MIMETYPES_50_FILE);
403 (void) directoryService->Undefine(NS_APP_BOOKMARKS_50_FILE);
404 (void) directoryService->Undefine(NS_APP_DOWNLOADS_50_FILE);
405 (void) directoryService->Undefine(NS_APP_SEARCH_50_FILE);
407 return NS_OK;
408 }
410 //*****************************************************************************
411 // Global creation function
412 //*****************************************************************************
414 nsresult NS_NewProfileDirServiceProvider(bool aNotifyObservers,
415 nsProfileDirServiceProvider** aProvider)
416 {
417 NS_ENSURE_ARG_POINTER(aProvider);
418 *aProvider = nullptr;
420 nsProfileDirServiceProvider *prov = new nsProfileDirServiceProvider(aNotifyObservers);
421 if (!prov)
422 return NS_ERROR_OUT_OF_MEMORY;
423 nsresult rv = prov->Initialize();
424 if (NS_FAILED(rv)) {
425 delete prov;
426 return rv;
427 }
428 NS_ADDREF(*aProvider = prov);
429 return NS_OK;
430 }