1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ArrayUtils.h"
10 #include "nsDirectoryService.h"
11 #include "nsLocalFile.h"
13 #include "nsGkAtoms.h"
14 #include "nsEnumeratorUtils.h"
15 #include "nsThreadUtils.h"
17 #include "mozilla/SimpleEnumerator.h"
18 #include "nsICategoryManager.h"
19 #include "nsISimpleEnumerator.h"
26 #elif defined(XP_UNIX)
29 # include <sys/param.h>
31 # ifdef MOZ_WIDGET_COCOA
32 # include <CoreServices/CoreServices.h>
33 # include <Carbon/Carbon.h>
37 #include "SpecialSystemDirectory.h"
38 #include "nsAppFileLocationProvider.h"
39 #include "BinaryPath.h"
41 using namespace mozilla
;
43 //----------------------------------------------------------------------------------------
44 nsresult
nsDirectoryService::GetCurrentProcessDirectory(nsIFile
** aFile
)
45 //----------------------------------------------------------------------------------------
47 if (NS_WARN_IF(!aFile
)) {
48 return NS_ERROR_INVALID_ARG
;
52 // Set the component registry location:
54 return NS_ERROR_FAILURE
;
59 // Some callers relying on this fallback make assumptions that don't
60 // hold on Android for BinaryPath::GetFile, so use GRE_HOME instead.
61 const char* greHome
= getenv("GRE_HOME");
63 return NS_ERROR_FAILURE
;
65 nsresult rv
= NS_NewNativeLocalFile(nsDependentCString(greHome
),
66 getter_AddRefs(mXCurProcD
));
71 nsCOMPtr
<nsIFile
> file
;
72 if (NS_SUCCEEDED(BinaryPath::GetFile(getter_AddRefs(file
)))) {
73 nsresult rv
= file
->GetParent(getter_AddRefs(mXCurProcD
));
80 return mXCurProcD
->Clone(aFile
);
81 } // GetCurrentProcessDirectory()
83 StaticRefPtr
<nsDirectoryService
> nsDirectoryService::gService
;
85 nsDirectoryService::nsDirectoryService() : mHashtable(128) {}
87 nsresult
nsDirectoryService::Create(REFNSIID aIID
, void** aResult
) {
88 if (NS_WARN_IF(!aResult
)) {
89 return NS_ERROR_INVALID_ARG
;
93 return NS_ERROR_NOT_INITIALIZED
;
96 return gService
->QueryInterface(aIID
, aResult
);
100 nsDirectoryService::Init() {
101 MOZ_ASSERT_UNREACHABLE("nsDirectoryService::Init() for internal use only!");
105 void nsDirectoryService::RealInit() {
106 NS_ASSERTION(!gService
,
107 "nsDirectoryService::RealInit Mustn't initialize twice!");
109 gService
= new nsDirectoryService();
111 // Let the list hold the only reference to the provider.
112 nsAppFileLocationProvider
* defaultProvider
= new nsAppFileLocationProvider
;
113 gService
->mProviders
.AppendElement(defaultProvider
);
116 nsDirectoryService::~nsDirectoryService() = default;
118 NS_IMPL_ISUPPORTS(nsDirectoryService
, nsIProperties
, nsIDirectoryService
,
119 nsIDirectoryServiceProvider
, nsIDirectoryServiceProvider2
)
122 nsDirectoryService::Undefine(const char* aProp
) {
123 if (NS_WARN_IF(!aProp
)) {
124 return NS_ERROR_INVALID_ARG
;
127 nsDependentCString
key(aProp
);
128 return mHashtable
.Remove(key
) ? NS_OK
: NS_ERROR_FAILURE
;
132 nsDirectoryService::GetKeys(nsTArray
<nsCString
>& aKeys
) {
133 return NS_ERROR_NOT_IMPLEMENTED
;
136 struct MOZ_STACK_CLASS FileData
{
137 FileData(const char* aProperty
, const nsIID
& aUUID
)
138 : property(aProperty
), data(nullptr), persistent(true), uuid(aUUID
) {}
140 const char* property
;
141 nsCOMPtr
<nsISupports
> data
;
146 static bool FindProviderFile(nsIDirectoryServiceProvider
* aElement
,
149 if (aData
->uuid
.Equals(NS_GET_IID(nsISimpleEnumerator
))) {
150 // Not all providers implement this iface
151 nsCOMPtr
<nsIDirectoryServiceProvider2
> prov2
= do_QueryInterface(aElement
);
153 nsCOMPtr
<nsISimpleEnumerator
> newFiles
;
154 rv
= prov2
->GetFiles(aData
->property
, getter_AddRefs(newFiles
));
155 if (NS_SUCCEEDED(rv
) && newFiles
) {
157 nsCOMPtr
<nsISimpleEnumerator
> unionFiles
;
159 NS_NewUnionEnumerator(getter_AddRefs(unionFiles
),
160 (nsISimpleEnumerator
*)aData
->data
.get(),
164 unionFiles
.swap(*(nsISimpleEnumerator
**)&aData
->data
);
167 aData
->data
= newFiles
;
170 aData
->persistent
= false; // Enumerators can never be persistent
171 return rv
== NS_SUCCESS_AGGREGATE_RESULT
;
175 rv
= aElement
->GetFile(aData
->property
, &aData
->persistent
,
176 (nsIFile
**)&aData
->data
);
177 if (NS_SUCCEEDED(rv
) && aData
->data
) {
186 nsDirectoryService::Get(const char* aProp
, const nsIID
& aUuid
, void** aResult
) {
187 if (NS_WARN_IF(!aProp
)) {
188 return NS_ERROR_INVALID_ARG
;
191 MOZ_ASSERT(NS_IsMainThread(), "Do not call dirsvc::get on non-main threads!");
193 nsDependentCString
key(aProp
);
195 nsCOMPtr
<nsIFile
> cachedFile
= mHashtable
.Get(key
);
198 nsCOMPtr
<nsIFile
> cloneFile
;
199 cachedFile
->Clone(getter_AddRefs(cloneFile
));
200 return cloneFile
->QueryInterface(aUuid
, aResult
);
203 // it is not one of our defaults, lets check any providers
204 FileData
fileData(aProp
, aUuid
);
206 for (int32_t i
= mProviders
.Length() - 1; i
>= 0; i
--) {
207 if (!FindProviderFile(mProviders
[i
], &fileData
)) {
212 if (fileData
.persistent
) {
213 Set(aProp
, static_cast<nsIFile
*>(fileData
.data
.get()));
215 nsresult rv
= (fileData
.data
)->QueryInterface(aUuid
, aResult
);
216 fileData
.data
= nullptr; // AddRef occurs in FindProviderFile()
220 FindProviderFile(static_cast<nsIDirectoryServiceProvider
*>(this), &fileData
);
222 if (fileData
.persistent
) {
223 Set(aProp
, static_cast<nsIFile
*>(fileData
.data
.get()));
225 nsresult rv
= (fileData
.data
)->QueryInterface(aUuid
, aResult
);
226 fileData
.data
= nullptr; // AddRef occurs in FindProviderFile()
230 return NS_ERROR_FAILURE
;
234 nsDirectoryService::Set(const char* aProp
, nsISupports
* aValue
) {
235 if (NS_WARN_IF(!aProp
)) {
236 return NS_ERROR_INVALID_ARG
;
239 return NS_ERROR_FAILURE
;
242 const nsDependentCString
key(aProp
);
243 return mHashtable
.WithEntryHandle(key
, [&](auto&& entry
) {
245 nsCOMPtr
<nsIFile
> ourFile
= do_QueryInterface(aValue
);
247 nsCOMPtr
<nsIFile
> cloneFile
;
248 ourFile
->Clone(getter_AddRefs(cloneFile
));
249 entry
.Insert(std::move(cloneFile
));
253 return NS_ERROR_FAILURE
;
258 nsDirectoryService::Has(const char* aProp
, bool* aResult
) {
259 if (NS_WARN_IF(!aProp
)) {
260 return NS_ERROR_INVALID_ARG
;
264 nsCOMPtr
<nsIFile
> value
;
265 nsresult rv
= Get(aProp
, NS_GET_IID(nsIFile
), getter_AddRefs(value
));
278 nsDirectoryService::RegisterProvider(nsIDirectoryServiceProvider
* aProv
) {
280 return NS_ERROR_FAILURE
;
283 mProviders
.AppendElement(aProv
);
287 void nsDirectoryService::RegisterCategoryProviders() {
288 nsCOMPtr
<nsICategoryManager
> catman(
289 do_GetService(NS_CATEGORYMANAGER_CONTRACTID
));
294 nsCOMPtr
<nsISimpleEnumerator
> entries
;
295 catman
->EnumerateCategory(XPCOM_DIRECTORY_PROVIDER_CATEGORY
,
296 getter_AddRefs(entries
));
298 for (auto& categoryEntry
: SimpleEnumerator
<nsICategoryEntry
>(entries
)) {
299 nsAutoCString contractID
;
300 categoryEntry
->GetValue(contractID
);
302 if (nsCOMPtr
<nsIDirectoryServiceProvider
> provider
=
303 do_GetService(contractID
.get())) {
304 RegisterProvider(provider
);
310 nsDirectoryService::UnregisterProvider(nsIDirectoryServiceProvider
* aProv
) {
312 return NS_ERROR_FAILURE
;
315 mProviders
.RemoveElement(aProv
);
319 // DO NOT ADD ANY LOCATIONS TO THIS FUNCTION UNTIL YOU TALK TO:
320 // dougt@netscape.com. This is meant to be a place of xpcom or system specific
321 // file locations, not application specific locations. If you need the later,
322 // register a callback for your application.
325 nsDirectoryService::GetFile(const char* aProp
, bool* aPersistent
,
327 nsCOMPtr
<nsIFile
> localFile
;
328 nsresult rv
= NS_ERROR_FAILURE
;
333 RefPtr
<nsAtom
> inAtom
= NS_Atomize(aProp
);
335 // check to see if it is one of our defaults
337 if (inAtom
== nsGkAtoms::DirectoryService_CurrentProcess
||
338 inAtom
== nsGkAtoms::DirectoryService_OS_CurrentProcessDirectory
) {
339 rv
= GetCurrentProcessDirectory(getter_AddRefs(localFile
));
342 // Unless otherwise set, the core pieces of the GRE exist
343 // in the current process directory.
344 else if (inAtom
== nsGkAtoms::DirectoryService_GRE_Directory
||
345 inAtom
== nsGkAtoms::DirectoryService_GRE_BinDirectory
) {
346 rv
= GetCurrentProcessDirectory(getter_AddRefs(localFile
));
347 } else if (inAtom
== nsGkAtoms::DirectoryService_OS_TemporaryDirectory
) {
348 rv
= GetSpecialSystemDirectory(OS_TemporaryDirectory
,
349 getter_AddRefs(localFile
));
350 } else if (inAtom
== nsGkAtoms::DirectoryService_OS_CurrentWorkingDirectory
) {
351 rv
= GetSpecialSystemDirectory(OS_CurrentWorkingDirectory
,
352 getter_AddRefs(localFile
));
354 #if defined(MOZ_WIDGET_COCOA)
355 else if (inAtom
== nsGkAtoms::DirectoryService_SystemDirectory
) {
356 rv
= GetSpecialSystemDirectory(Mac_SystemDirectory
,
357 getter_AddRefs(localFile
));
358 } else if (inAtom
== nsGkAtoms::DirectoryService_UserLibDirectory
) {
359 rv
= GetSpecialSystemDirectory(Mac_UserLibDirectory
,
360 getter_AddRefs(localFile
));
361 } else if (inAtom
== nsGkAtoms::Home
) {
363 GetSpecialSystemDirectory(Mac_HomeDirectory
, getter_AddRefs(localFile
));
364 } else if (inAtom
== nsGkAtoms::DirectoryService_DefaultDownloadDirectory
) {
365 rv
= GetSpecialSystemDirectory(Mac_DefaultDownloadDirectory
,
366 getter_AddRefs(localFile
));
367 } else if (inAtom
== nsGkAtoms::DirectoryService_OS_DesktopDirectory
) {
368 rv
= GetSpecialSystemDirectory(Mac_UserDesktopDirectory
,
369 getter_AddRefs(localFile
));
370 } else if (inAtom
== nsGkAtoms::DirectoryService_OS_DocumentsDirectory
) {
371 rv
= GetSpecialSystemDirectory(Mac_UserDocumentsDirectory
,
372 getter_AddRefs(localFile
));
373 } else if (inAtom
== nsGkAtoms::DirectoryService_LocalApplicationsDirectory
) {
374 rv
= GetSpecialSystemDirectory(Mac_LocalApplicationsDirectory
,
375 getter_AddRefs(localFile
));
376 } else if (inAtom
== nsGkAtoms::DirectoryService_UserPreferencesDirectory
) {
377 rv
= GetSpecialSystemDirectory(Mac_UserPreferencesDirectory
,
378 getter_AddRefs(localFile
));
379 } else if (inAtom
== nsGkAtoms::DirectoryService_PictureDocumentsDirectory
) {
380 rv
= GetSpecialSystemDirectory(Mac_PictureDocumentsDirectory
,
381 getter_AddRefs(localFile
));
382 } else if (inAtom
== nsGkAtoms::DirectoryService_DefaultScreenshotDirectory
) {
383 rv
= GetSpecialSystemDirectory(Mac_DefaultScreenshotDirectory
,
384 getter_AddRefs(localFile
));
386 #elif defined(XP_WIN)
387 else if (inAtom
== nsGkAtoms::DirectoryService_SystemDirectory
) {
388 rv
= GetSpecialSystemDirectory(Win_SystemDirectory
,
389 getter_AddRefs(localFile
));
390 } else if (inAtom
== nsGkAtoms::DirectoryService_WindowsDirectory
) {
391 rv
= GetSpecialSystemDirectory(Win_WindowsDirectory
,
392 getter_AddRefs(localFile
));
393 } else if (inAtom
== nsGkAtoms::DirectoryService_WindowsProgramFiles
) {
394 rv
= GetSpecialSystemDirectory(Win_ProgramFiles
, getter_AddRefs(localFile
));
395 } else if (inAtom
== nsGkAtoms::Home
) {
397 GetSpecialSystemDirectory(Win_HomeDirectory
, getter_AddRefs(localFile
));
398 } else if (inAtom
== nsGkAtoms::DirectoryService_Programs
) {
399 rv
= GetSpecialSystemDirectory(Win_Programs
, getter_AddRefs(localFile
));
400 } else if (inAtom
== nsGkAtoms::DirectoryService_Favorites
) {
401 rv
= GetSpecialSystemDirectory(Win_Favorites
, getter_AddRefs(localFile
));
402 } else if (inAtom
== nsGkAtoms::DirectoryService_OS_DesktopDirectory
) {
403 rv
= GetSpecialSystemDirectory(Win_Desktopdirectory
,
404 getter_AddRefs(localFile
));
405 } else if (inAtom
== nsGkAtoms::DirectoryService_OS_DocumentsDirectory
) {
406 rv
= GetSpecialSystemDirectory(Win_Documents
, getter_AddRefs(localFile
));
407 } else if (inAtom
== nsGkAtoms::DirectoryService_Appdata
) {
408 rv
= GetSpecialSystemDirectory(Win_Appdata
, getter_AddRefs(localFile
));
409 } else if (inAtom
== nsGkAtoms::DirectoryService_LocalAppdata
) {
410 rv
= GetSpecialSystemDirectory(Win_LocalAppdata
, getter_AddRefs(localFile
));
411 } else if (inAtom
== nsGkAtoms::DirectoryService_WinCookiesDirectory
) {
412 rv
= GetSpecialSystemDirectory(Win_Cookies
, getter_AddRefs(localFile
));
413 } else if (inAtom
== nsGkAtoms::DirectoryService_DefaultDownloadDirectory
) {
414 rv
= GetSpecialSystemDirectory(Win_Downloads
, getter_AddRefs(localFile
));
416 #elif defined(XP_UNIX)
417 else if (inAtom
== nsGkAtoms::Home
) {
418 rv
= GetSpecialSystemDirectory(Unix_HomeDirectory
,
419 getter_AddRefs(localFile
));
420 } else if (inAtom
== nsGkAtoms::DirectoryService_OS_DesktopDirectory
) {
421 rv
= GetSpecialSystemDirectory(Unix_XDG_Desktop
, getter_AddRefs(localFile
));
422 *aPersistent
= false;
423 } else if (inAtom
== nsGkAtoms::DirectoryService_DefaultDownloadDirectory
) {
425 GetSpecialSystemDirectory(Unix_XDG_Download
, getter_AddRefs(localFile
));
426 *aPersistent
= false;
427 } else if (inAtom
== nsGkAtoms::DirectoryService_OS_SystemConfigDir
) {
428 rv
= GetSpecialSystemDirectory(Unix_SystemConfigDirectory
,
429 getter_AddRefs(localFile
));
430 } else if (inAtom
== nsGkAtoms::DirectoryService_OS_DocumentsDirectory
) {
431 rv
= GetSpecialSystemDirectory(Unix_XDG_Documents
,
432 getter_AddRefs(localFile
));
441 return NS_ERROR_FAILURE
;
444 localFile
.forget(aResult
);
449 nsDirectoryService::GetFiles(const char* aProp
, nsISimpleEnumerator
** aResult
) {
450 if (NS_WARN_IF(!aResult
)) {
451 return NS_ERROR_INVALID_ARG
;
455 return NS_ERROR_FAILURE
;