Bug 1186273 - Part 1. Move preferences and observers into dedicated threadsafe module. r=dhylands

This commit is contained in:
Andrew Osmond 2015-08-18 07:42:12 -04:00
parent a9b0b47323
commit c67ed45fb3
9 changed files with 1023 additions and 616 deletions

View File

@ -9,7 +9,6 @@
#include "nsIFile.h"
#include "nsIPrincipal.h"
#include "nsIObserver.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h"
@ -25,6 +24,9 @@
class nsIInputStream;
class nsIOutputStream;
struct DeviceStorageFileDescriptor;
#ifdef MOZ_WIDGET_GONK
class nsIVolume;
#endif
namespace mozilla {
class EventListenerManager;
@ -130,36 +132,8 @@ private:
uint64_t* aTotalSoFar);
};
/*
The FileUpdateDispatcher converts file-watcher-notify
observer events to file-watcher-update events. This is
used to be able to broadcast events from one child to
another child in B2G. (f.e., if one child decides to add
a file, we want to be able to able to send a onchange
notifications to every other child watching that device
storage object).
We create this object (via GetSingleton) in two places:
* ContentParent::Init (for IPC)
* nsDOMDeviceStorage::Init (for non-ipc)
*/
class FileUpdateDispatcher final
: public nsIObserver
{
~FileUpdateDispatcher() {}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
static FileUpdateDispatcher* GetSingleton();
private:
static mozilla::StaticRefPtr<FileUpdateDispatcher> sSingleton;
};
class nsDOMDeviceStorage final
: public mozilla::DOMEventTargetHelper
, public nsIObserver
{
typedef mozilla::ErrorResult ErrorResult;
typedef mozilla::dom::DeviceStorageEnumerationParameters
@ -172,7 +146,6 @@ public:
typedef nsTArray<nsString> VolumeNameArray;
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIOBSERVER
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
void EventListenerWasAdded(const nsAString& aType,
@ -299,6 +272,15 @@ public:
static bool ParseFullPath(const nsAString& aFullPath,
nsAString& aOutStorageName,
nsAString& aOutStoragePath);
// DeviceStorageStatics callbacks
void OnFileWatcherUpdate(const nsCString& aData, DeviceStorageFile* aFile);
void OnDiskSpaceWatcher(bool aLowDiskSpace);
void OnWritableNameChanged();
#ifdef MOZ_WIDGET_GONK
void OnVolumeStateChanged(nsIVolume* aVolume);
#endif
private:
~nsDOMDeviceStorage();
@ -341,7 +323,6 @@ private:
bool mIsWatchingFile;
bool mAllowedToWatchFile;
bool mIsDefaultLocation;
void DispatchDefaultChangeEvent();
nsresult Notify(const char* aReason, class DeviceStorageFile* aFile);

View File

@ -0,0 +1,802 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DeviceStorageStatics.h"
#include "mozilla/Preferences.h"
#include "nsDeviceStorage.h"
#include "nsIObserverService.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceDefs.h"
#include "nsISupportsPrimitives.h"
#include "nsPrintfCString.h"
#ifdef MOZ_WIDGET_GONK
#include "nsIVolume.h"
#endif
#ifdef MOZ_WIDGET_ANDROID
#include "AndroidBridge.h"
#endif
namespace mozilla {
namespace dom {
namespace devicestorage {
static const char* kPrefOverrideRootDir = "device.storage.overrideRootDir";
static const char* kPrefTesting = "device.storage.testing";
static const char* kPrefPromptTesting = "device.storage.prompt.testing";
static const char* kPrefWritableName = "device.storage.writable.name";
static const char* kFileWatcherUpdate = "file-watcher-update";
static const char* kDiskSpaceWatcher = "disk-space-watcher";
static const char* kFileWatcherNotify = "file-watcher-notify";
static const char* kDownloadWatcherNotify = "download-watcher-notify";
StaticRefPtr<DeviceStorageStatics> DeviceStorageStatics::sInstance;
StaticMutex DeviceStorageStatics::sMutex;
NS_IMPL_ISUPPORTS(DeviceStorageStatics,
nsIObserver)
/* static */ void
DeviceStorageStatics::Initialize()
{
MOZ_ASSERT(!sInstance);
StaticMutexAutoLock lock(sMutex);
sInstance = new DeviceStorageStatics();
sInstance->Init();
}
/* static */ void
DeviceStorageStatics::InitializeDirs()
{
StaticMutexAutoLock lock(sMutex);
if (NS_WARN_IF(!sInstance)) {
return;
}
/* The actual initialization can only happen on the main thread. This will
either happen when device storage is first used on the main thread, or
(in the future) when a worker is created. */
if (!sInstance->mInitialized && NS_IsMainThread()) {
sInstance->InitDirs();
sInstance->mInitialized = true;
}
MOZ_ASSERT(sInstance->mInitialized);
}
DeviceStorageStatics::DeviceStorageStatics()
: mInitialized(false)
, mPromptTesting(false)
{
DS_LOG_INFO("");
}
DeviceStorageStatics::~DeviceStorageStatics()
{
DS_LOG_INFO("");
}
void
DeviceStorageStatics::Init()
{
MOZ_ASSERT(NS_IsMainThread());
sMutex.AssertCurrentThreadOwns();
DS_LOG_INFO("");
Preferences::AddStrongObserver(this, kPrefTesting);
Preferences::AddStrongObserver(this, kPrefPromptTesting);
Preferences::AddStrongObserver(this, kPrefWritableName);
mWritableName = Preferences::GetString(kPrefWritableName);
mPromptTesting = Preferences::GetBool(kPrefPromptTesting, false);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
obs->AddObserver(this, kFileWatcherNotify, false);
obs->AddObserver(this, kDownloadWatcherNotify, false);
}
DS_LOG_INFO("");
}
void
DeviceStorageStatics::InitDirs()
{
MOZ_ASSERT(NS_IsMainThread());
sMutex.AssertCurrentThreadOwns();
DS_LOG_INFO("");
nsCOMPtr<nsIProperties> dirService
= do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
MOZ_ASSERT(dirService);
#if !defined(MOZ_WIDGET_GONK)
// Keep MOZ_WIDGET_COCOA above XP_UNIX,
// because both are defined in Darwin builds.
#if defined (MOZ_WIDGET_COCOA)
dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(mDirs[TYPE_PICTURES]));
dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(mDirs[TYPE_VIDEOS]));
dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(mDirs[TYPE_MUSIC]));
// Keep MOZ_WIDGET_ANDROID above XP_UNIX,
// because both are defined in Android builds.
#elif defined (MOZ_WIDGET_ANDROID)
nsAutoString path;
if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
NS_LITERAL_STRING(DEVICESTORAGE_PICTURES), path))) {
NS_NewLocalFile(path, /* aFollowLinks */ true,
getter_AddRefs(mDirs[TYPE_PICTURES]));
}
if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS), path))) {
NS_NewLocalFile(path, /* aFollowLinks */ true,
getter_AddRefs(mDirs[TYPE_VIDEOS]));
}
if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
NS_LITERAL_STRING(DEVICESTORAGE_MUSIC), path))) {
NS_NewLocalFile(path, /* aFollowLinks */ true,
getter_AddRefs(mDirs[TYPE_MUSIC]));
}
if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), path))) {
NS_NewLocalFile(path, /* aFollowLinks */ true,
getter_AddRefs(mDirs[TYPE_SDCARD]));
}
#elif defined (XP_UNIX)
dirService->Get(NS_UNIX_XDG_PICTURES_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(mDirs[TYPE_PICTURES]));
dirService->Get(NS_UNIX_XDG_VIDEOS_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(mDirs[TYPE_VIDEOS]));
dirService->Get(NS_UNIX_XDG_MUSIC_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(mDirs[TYPE_MUSIC]));
#elif defined (XP_WIN)
dirService->Get(NS_WIN_PICTURES_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(mDirs[TYPE_PICTURES]));
dirService->Get(NS_WIN_VIDEOS_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(mDirs[TYPE_VIDEOS]));
dirService->Get(NS_WIN_MUSIC_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(mDirs[TYPE_MUSIC]));
#endif
#ifndef MOZ_WIDGET_ANDROID
// Eventually, on desktop, we want to do something smarter -- for example,
// detect when an sdcard is inserted, and use that instead of this.
dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(mDirs[TYPE_SDCARD]));
if (mDirs[TYPE_SDCARD]) {
mDirs[TYPE_SDCARD]->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
}
#endif // !MOZ_WIDGET_ANDROID
dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(mDirs[TYPE_APPS]));
if (mDirs[TYPE_APPS]) {
mDirs[TYPE_APPS]->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
}
#endif // !MOZ_WIDGET_GONK
#ifdef MOZ_WIDGET_GONK
NS_NewLocalFile(NS_LITERAL_STRING("/data"),
false,
getter_AddRefs(mDirs[TYPE_APPS]));
#endif
if (XRE_IsParentProcess()) {
NS_GetSpecialDirectory("UAppData", getter_AddRefs(mDirs[TYPE_CRASHES]));
if (mDirs[TYPE_CRASHES]) {
mDirs[TYPE_CRASHES]->Append(NS_LITERAL_STRING("Crash Reports"));
}
}
#ifdef MOZ_WIDGET_GONK
// NS_GetSpecialDirectory("UAppData") fails in content processes because
// gAppData from toolkit/xre/nsAppRunner.cpp is not initialized.
else {
NS_NewLocalFile(NS_LITERAL_STRING("/data/b2g/mozilla/Crash Reports"),
false,
getter_AddRefs(mDirs[TYPE_CRASHES]));
}
#endif
// Directories which don't depend on a volume should be calculated once
// here. Directories which depend on the root directory of a volume
// should be calculated in DeviceStorageFile::GetRootDirectoryForType.
Preferences::AddStrongObserver(this, kPrefOverrideRootDir);
ResetOverrideRootDir();
}
void
DeviceStorageStatics::DumpDirs()
{
#ifdef DS_LOGGING
sMutex.AssertCurrentThreadOwns();
static const char* storageTypes[] = {
"app",
"crashes",
"pictures",
"videos",
"music",
"sdcard",
"override",
nullptr
};
for (uint32_t i = 0; i < TYPE_COUNT; ++i) {
MOZ_ASSERT(storageTypes[i]);
nsString path;
if (mDirs[i]) {
mDirs[i]->GetPath(path);
}
DS_LOG_INFO("%s: '%s'",
storageTypes[i], NS_LossyConvertUTF16toASCII(path).get());
}
#endif
}
void
DeviceStorageStatics::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
sMutex.AssertCurrentThreadOwns();
DS_LOG_INFO("");
Preferences::RemoveObserver(this, kPrefOverrideRootDir);
Preferences::RemoveObserver(this, kPrefTesting);
Preferences::RemoveObserver(this, kPrefPromptTesting);
Preferences::RemoveObserver(this, kPrefWritableName);
}
/* static */ already_AddRefed<nsIFile>
DeviceStorageStatics::GetDir(DeviceStorageType aType)
{
MOZ_ASSERT(aType < TYPE_COUNT);
StaticMutexAutoLock lock(sMutex);
if (NS_WARN_IF(!sInstance)) {
return nullptr;
}
nsCOMPtr<nsIFile> file;
switch (aType) {
case TYPE_APPS:
case TYPE_CRASHES:
case TYPE_OVERRIDE:
file = sInstance->mDirs[aType];
return file.forget();
default:
break;
}
// In testing, we default all device storage types to a temp directory.
// This is only initialized if the preference device.storage.testing
// was set to true, or if device.storage.overrideRootDir is set.
file = sInstance->mDirs[TYPE_OVERRIDE];
if (!file) {
file = sInstance->mDirs[aType];
#ifdef MOZ_WIDGET_GONK
/* We should use volume mount points on B2G. */
MOZ_ASSERT(!file);
#endif
}
return file.forget();
}
/* static */ bool
DeviceStorageStatics::HasOverrideRootDir()
{
StaticMutexAutoLock lock(sMutex);
if (NS_WARN_IF(!sInstance)) {
return false;
}
return sInstance->mDirs[TYPE_OVERRIDE];
}
/* static */ already_AddRefed<nsIFile>
DeviceStorageStatics::GetAppsDir()
{
return GetDir(TYPE_APPS);
}
/* static */ already_AddRefed<nsIFile>
DeviceStorageStatics::GetCrashesDir()
{
return GetDir(TYPE_CRASHES);
}
/* static */ already_AddRefed<nsIFile>
DeviceStorageStatics::GetPicturesDir()
{
return GetDir(TYPE_PICTURES);
}
/* static */ already_AddRefed<nsIFile>
DeviceStorageStatics::GetVideosDir()
{
return GetDir(TYPE_VIDEOS);
}
/* static */ already_AddRefed<nsIFile>
DeviceStorageStatics::GetMusicDir()
{
return GetDir(TYPE_MUSIC);
}
/* static */ already_AddRefed<nsIFile>
DeviceStorageStatics::GetSdcardDir()
{
return GetDir(TYPE_SDCARD);
}
/* static */ bool
DeviceStorageStatics::IsPromptTesting()
{
StaticMutexAutoLock lock(sMutex);
if (NS_WARN_IF(!sInstance)) {
return false;
}
return sInstance->mPromptTesting;
}
/* static */ void
DeviceStorageStatics::GetWritableName(nsString& aName)
{
StaticMutexAutoLock lock(sMutex);
if (NS_WARN_IF(!sInstance)) {
aName.Truncate();
return;
}
aName = sInstance->mWritableName;
}
/* static */ void
DeviceStorageStatics::SetWritableName(const nsAString& aName)
{
StaticMutexAutoLock lock(sMutex);
if (!NS_WARN_IF(!sInstance)) {
// Update inline although it will be updated again in case
// another thread comes in checking it before the update takes
sInstance->mWritableName = aName;
}
nsString name;
name.Assign(aName);
NS_DispatchToMainThread(NS_NewRunnableFunction([name] () -> void {
Preferences::SetString(kPrefWritableName, name);
}));
}
/* static */ void
DeviceStorageStatics::AddListener(nsDOMDeviceStorage* aListener)
{
DS_LOG_DEBUG("%p", aListener);
StaticMutexAutoLock lock(sMutex);
if (NS_WARN_IF(!sInstance)) {
return;
}
MOZ_ASSERT(sInstance->mInitialized);
if (sInstance->mListeners.IsEmpty()) {
NS_DispatchToMainThread(
NS_NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Register));
}
nsRefPtr<ListenerWrapper> wrapper =
new ListenerWrapper(aListener);
sInstance->mListeners.AppendElement(wrapper.forget());
}
/* static */ void
DeviceStorageStatics::RemoveListener(nsDOMDeviceStorage* aListener)
{
DS_LOG_DEBUG("%p", aListener);
StaticMutexAutoLock lock(sMutex);
if (!sInstance) {
return;
}
bool removed = false;
uint32_t i = sInstance->mListeners.Length();
while (i > 0) {
--i;
if (sInstance->mListeners[i]->mDeviceStorage.get() == aListener) {
sInstance->mListeners.RemoveElementAt(i);
removed = true;
break;
}
}
MOZ_ASSERT(removed);
if (removed && sInstance->mListeners.IsEmpty()) {
NS_DispatchToMainThread(
NS_NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Deregister));
}
}
void
DeviceStorageStatics::Register()
{
MOZ_ASSERT(NS_IsMainThread());
DS_LOG_INFO("");
StaticMutexAutoLock lock(sMutex);
if (NS_WARN_IF(!sInstance)) {
return;
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(this, kFileWatcherUpdate, false);
obs->AddObserver(this, kDiskSpaceWatcher, false);
#ifdef MOZ_WIDGET_GONK
obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false);
#endif
}
}
void
DeviceStorageStatics::Deregister()
{
MOZ_ASSERT(NS_IsMainThread());
DS_LOG_INFO("");
StaticMutexAutoLock lock(sMutex);
if (!sInstance) {
return;
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(this, kFileWatcherUpdate);
obs->RemoveObserver(this, kDiskSpaceWatcher);
#ifdef MOZ_WIDGET_GONK
obs->RemoveObserver(this, NS_VOLUME_STATE_CHANGED);
#endif
}
}
void
DeviceStorageStatics::ResetOverrideRootDir()
{
MOZ_ASSERT(NS_IsMainThread());
sMutex.AssertCurrentThreadOwns();
nsCOMPtr<nsIFile> f;
DS_LOG_INFO("");
if (Preferences::GetBool(kPrefTesting, false)) {
DS_LOG_INFO("temp");
nsCOMPtr<nsIProperties> dirService
= do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
MOZ_ASSERT(dirService);
dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
if (f) {
f->AppendRelativeNativePath(
NS_LITERAL_CSTRING("device-storage-testing"));
}
} else {
// For users running on desktop, it's convenient to be able to override
// all of the directories to point to a single tree, much like what happens
// on a real device.
const nsAdoptingString& overrideRootDir =
mozilla::Preferences::GetString(kPrefOverrideRootDir);
if (overrideRootDir && !overrideRootDir.IsEmpty()) {
NS_NewLocalFile(overrideRootDir, false, getter_AddRefs(f));
}
}
if (f) {
if (XRE_IsParentProcess()) {
// Only the parent process can create directories. In testing, because
// the preference is updated after startup, its entirely possible that
// the preference updated notification will be received by a child
// prior to the parent.
nsresult rv = f->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
nsString path;
f->GetPath(path);
nsPrintfCString msg("DeviceStorage: Unable to create directory '%s'",
NS_LossyConvertUTF16toASCII(path).get());
NS_WARNING(msg.get());
}
}
f->Normalize();
}
mDirs[TYPE_OVERRIDE] = f.forget();
DumpDirs();
}
NS_IMETHODIMP
DeviceStorageStatics::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
MOZ_ASSERT(NS_IsMainThread());
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
MOZ_ASSERT(aData);
StaticMutexAutoLock lock(sMutex);
if (NS_WARN_IF(!sInstance)) {
return NS_OK;
}
nsDependentString name(aData);
if (name.EqualsASCII(kPrefTesting) ||
name.EqualsASCII(kPrefOverrideRootDir)) {
ResetOverrideRootDir();
} else if(name.EqualsASCII(kPrefPromptTesting)) {
mPromptTesting = Preferences::GetBool(kPrefPromptTesting, false);
DS_LOG_INFO("prompt testing %d", mPromptTesting);
} else if(name.EqualsASCII(kPrefWritableName)) {
mWritableName = Preferences::GetString(kPrefWritableName);
uint32_t i = mListeners.Length();
DS_LOG_INFO("writable name '%s' (%u)",
NS_LossyConvertUTF16toASCII(mWritableName).get(), i);
while (i > 0) {
--i;
mListeners[i]->OnWritableNameChanged();
}
}
return NS_OK;
}
#ifdef MOZ_WIDGET_GONK
if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
nsCOMPtr<nsIVolume> volume = do_QueryInterface(aSubject);
if (NS_WARN_IF(!volume)) {
return NS_OK;
}
StaticMutexAutoLock lock(sMutex);
if (NS_WARN_IF(!sInstance)) {
return NS_OK;
}
uint32_t i = mListeners.Length();
DS_LOG_INFO("volume updated (%u)", i);
while (i > 0) {
--i;
mListeners[i]->OnVolumeStateChanged(volume);
}
return NS_OK;
}
#endif
if (!strcmp(aTopic, kFileWatcherUpdate)) {
DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
if (NS_WARN_IF(!file)) {
return NS_OK;
}
StaticMutexAutoLock lock(sMutex);
if (NS_WARN_IF(!sInstance)) {
return NS_OK;
}
auto data = NS_ConvertUTF16toUTF8(aData);
uint32_t i = mListeners.Length();
DS_LOG_INFO("file updated (%u)", i);
while (i > 0) {
--i;
mListeners[i]->OnFileWatcherUpdate(data, file);
}
return NS_OK;
}
if (!strcmp(aTopic, kDiskSpaceWatcher)) {
// 'disk-space-watcher' notifications are sent when there is a modification
// of a file in a specific location while a low device storage situation
// exists or after recovery of a low storage situation. For Firefox OS,
// these notifications are specific for apps storage.
bool lowDiskSpace = false;
if (!NS_strcmp(aData, MOZ_UTF16("full"))) {
lowDiskSpace = true;
} else if (NS_strcmp(aData, MOZ_UTF16("free"))) {
return NS_OK;
}
StaticMutexAutoLock lock(sMutex);
if (NS_WARN_IF(!sInstance)) {
return NS_OK;
}
uint32_t i = mListeners.Length();
DS_LOG_INFO("disk space %d (%u)", lowDiskSpace, i);
while (i > 0) {
--i;
mListeners[i]->OnDiskSpaceWatcher(lowDiskSpace);
}
return NS_OK;
}
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
StaticMutexAutoLock lock(sMutex);
if (NS_WARN_IF(!sInstance)) {
return NS_OK;
}
Shutdown();
sInstance = nullptr;
return NS_OK;
}
/* Here we convert file-watcher-notify and download-watcher-notify observer
events to file-watcher-update events. This is used to be able to
broadcast events from one child to another child in B2G. (f.e., if one
child decides to add a file, we want to be able to able to send a onchange
notifications to every other child watching that device storage object).*/
nsRefPtr<DeviceStorageFile> dsf;
if (!strcmp(aTopic, kDownloadWatcherNotify)) {
// aSubject will be an nsISupportsString with the native path to the file
// in question.
nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(aSubject);
if (!supportsString) {
return NS_OK;
}
nsString path;
nsresult rv = supportsString->GetData(path);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
// The downloader uses the sdcard storage type.
nsString volName;
#ifdef MOZ_WIDGET_GONK
if (DeviceStorageTypeChecker::IsVolumeBased(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD))) {
nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
if (NS_WARN_IF(!vs)) {
return NS_OK;
}
nsCOMPtr<nsIVolume> vol;
rv = vs->GetVolumeByPath(path, getter_AddRefs(vol));
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
rv = vol->GetName(volName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
nsString mountPoint;
rv = vol->GetMountPoint(mountPoint);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
if (!Substring(path, 0, mountPoint.Length()).Equals(mountPoint)) {
return NS_OK;
}
path = Substring(path, mountPoint.Length() + 1);
}
#endif
dsf = new DeviceStorageFile(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), volName, path);
} else if (!strcmp(aTopic, kFileWatcherNotify)) {
dsf = static_cast<DeviceStorageFile*>(aSubject);
} else {
DS_LOG_WARN("unhandled topic '%s'", aTopic);
return NS_OK;
}
if (NS_WARN_IF(!dsf || !dsf->mFile)) {
return NS_OK;
}
if (!XRE_IsParentProcess()) {
// Child process. Forward the notification to the parent.
ContentChild::GetSingleton()
->SendFilePathUpdateNotify(dsf->mStorageType,
dsf->mStorageName,
dsf->mPath,
NS_ConvertUTF16toUTF8(aData));
return NS_OK;
}
// Multiple storage types may match the same files. So walk through each of
// the storage types, and if the extension matches, tell them about it.
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (DeviceStorageTypeChecker::IsSharedMediaRoot(dsf->mStorageType)) {
DeviceStorageTypeChecker* typeChecker
= DeviceStorageTypeChecker::CreateOrGet();
MOZ_ASSERT(typeChecker);
static const nsLiteralString kMediaTypes[] = {
NS_LITERAL_STRING(DEVICESTORAGE_SDCARD),
NS_LITERAL_STRING(DEVICESTORAGE_PICTURES),
NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS),
NS_LITERAL_STRING(DEVICESTORAGE_MUSIC),
};
for (size_t i = 0; i < MOZ_ARRAY_LENGTH(kMediaTypes); i++) {
nsRefPtr<DeviceStorageFile> dsf2;
if (typeChecker->Check(kMediaTypes[i], dsf->mPath)) {
if (dsf->mStorageType.Equals(kMediaTypes[i])) {
dsf2 = dsf;
} else {
dsf2 = new DeviceStorageFile(kMediaTypes[i],
dsf->mStorageName, dsf->mPath);
}
obs->NotifyObservers(dsf2, kFileWatcherUpdate, aData);
}
}
} else {
obs->NotifyObservers(dsf, kFileWatcherUpdate, aData);
}
return NS_OK;
}
DeviceStorageStatics::ListenerWrapper::ListenerWrapper(nsDOMDeviceStorage* aListener)
: mDeviceStorage(aListener)
{
}
DeviceStorageStatics::ListenerWrapper::~ListenerWrapper()
{
}
void
DeviceStorageStatics::ListenerWrapper::OnFileWatcherUpdate(const nsCString& aData,
DeviceStorageFile* aFile)
{
nsRefPtr<ListenerWrapper> self = this;
nsCString data = aData;
nsRefPtr<DeviceStorageFile> file = aFile;
NS_DispatchToMainThread(NS_NewRunnableFunction([self, data, file] () -> void {
self->mDeviceStorage->OnFileWatcherUpdate(data, file);
}));
}
void
DeviceStorageStatics::ListenerWrapper::OnDiskSpaceWatcher(bool aLowDiskSpace)
{
nsRefPtr<ListenerWrapper> self = this;
NS_DispatchToMainThread(NS_NewRunnableFunction([self, aLowDiskSpace] () -> void {
self->mDeviceStorage->OnDiskSpaceWatcher(aLowDiskSpace);
}));
}
void
DeviceStorageStatics::ListenerWrapper::OnWritableNameChanged()
{
nsRefPtr<ListenerWrapper> self = this;
NS_DispatchToMainThread(NS_NewRunnableFunction([self] () -> void {
self->mDeviceStorage->OnWritableNameChanged();
}));
}
#ifdef MOZ_WIDGET_GONK
void
DeviceStorageStatics::ListenerWrapper::OnVolumeStateChanged(nsIVolume* aVolume)
{
nsRefPtr<ListenerWrapper> self = this;
nsCOMPtr<nsIVolume> volume = aVolume;
NS_DispatchToMainThread(NS_NewRunnableFunction([self, volume] () -> void {
self->mDeviceStorage->OnVolumeStateChanged(volume);
}));
}
#endif
} // namespace devicestorage
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,103 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_devicestorage_DeviceStorageStatics_h
#define mozilla_dom_devicestorage_DeviceStorageStatics_h
#include "mozilla/Mutex.h"
class nsDOMDeviceStorage;
class DeviceStorageFile;
#ifdef MOZ_WIDGET_GONK
class nsIVolume;
#endif
namespace mozilla {
namespace dom {
namespace devicestorage {
class DeviceStorageStatics : public nsIObserver
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOBSERVER
static void Initialize();
static void InitializeDirs();
static void AddListener(nsDOMDeviceStorage* aListener);
static void RemoveListener(nsDOMDeviceStorage* aListener);
static bool IsPromptTesting();
static void GetWritableName(nsString& aName);
static void SetWritableName(const nsAString& aName);
static bool HasOverrideRootDir();
static already_AddRefed<nsIFile> GetAppsDir();
static already_AddRefed<nsIFile> GetCrashesDir();
static already_AddRefed<nsIFile> GetPicturesDir();
static already_AddRefed<nsIFile> GetVideosDir();
static already_AddRefed<nsIFile> GetMusicDir();
static already_AddRefed<nsIFile> GetSdcardDir();
private:
enum DeviceStorageType {
TYPE_APPS,
TYPE_CRASHES,
TYPE_PICTURES,
TYPE_VIDEOS,
TYPE_MUSIC,
TYPE_SDCARD,
TYPE_OVERRIDE,
TYPE_COUNT
};
static already_AddRefed<nsIFile> GetDir(DeviceStorageType aType);
DeviceStorageStatics();
virtual ~DeviceStorageStatics();
void Init();
void InitDirs();
void DumpDirs();
void Shutdown();
void Register();
void Deregister();
void ResetOverrideRootDir();
class ListenerWrapper final {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ListenerWrapper)
explicit ListenerWrapper(nsDOMDeviceStorage* aListener);
void OnFileWatcherUpdate(const nsCString& aData, DeviceStorageFile* aFile);
void OnDiskSpaceWatcher(bool aLowDiskSpace);
void OnWritableNameChanged();
#ifdef MOZ_WIDGET_GONK
void OnVolumeStateChanged(nsIVolume* aVolume);
#endif
nsRefPtr<nsDOMDeviceStorage> mDeviceStorage;
private:
virtual ~ListenerWrapper();
};
nsTArray<nsRefPtr<ListenerWrapper> > mListeners;
nsCOMPtr<nsIFile> mDirs[TYPE_COUNT];
bool mInitialized;
bool mPromptTesting;
nsString mWritableName;
static StaticRefPtr<DeviceStorageStatics> sInstance;
static StaticMutex sMutex;
};
} // namespace devicestorage
} // namespace dom
} // namespace mozilla
#endif

View File

@ -17,12 +17,14 @@ EXPORTS.mozilla.dom += [
EXPORTS.mozilla.dom.devicestorage += [
'DeviceStorageRequestChild.h',
'DeviceStorageRequestParent.h',
'DeviceStorageStatics.h',
]
UNIFIED_SOURCES += [
'DeviceStorageAreaListener.cpp',
'DeviceStorageRequestChild.cpp',
'DeviceStorageRequestParent.cpp',
'DeviceStorageStatics.cpp',
'nsDeviceStorage.cpp',
]

View File

@ -24,7 +24,6 @@
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/LazyIdleThread.h"
#include "mozilla/Preferences.h"
#include "mozilla/Scoped.h"
#include "mozilla/Services.h"
@ -34,8 +33,6 @@
#include "nsServiceManagerUtils.h"
#include "nsIFile.h"
#include "nsIDirectoryEnumerator.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceDefs.h"
#include "nsNetUtil.h"
#include "nsIOutputStream.h"
#include "nsCycleCollectionParticipant.h"
@ -45,6 +42,7 @@
#include "nsXULAppAPI.h"
#include "DeviceStorageFileDescriptor.h"
#include "DeviceStorageRequestChild.h"
#include "DeviceStorageStatics.h"
#include "nsCRT.h"
#include "nsIObserverService.h"
#include "nsIMIMEService.h"
@ -53,7 +51,6 @@
#include "nsIStringBundle.h"
#include "nsISupportsPrimitives.h"
#include "nsIDocument.h"
#include "nsPrintfCString.h"
#include <algorithm>
#include "private/pprio.h"
#include "nsContentPermissionHelper.h"
@ -63,10 +60,6 @@
// Microsoft's API Name hackery sucks
#undef CreateEvent
#ifdef MOZ_WIDGET_ANDROID
#include "AndroidBridge.h"
#endif
#ifdef MOZ_WIDGET_GONK
#include "nsIVolume.h"
#include "nsIVolumeService.h"
@ -75,8 +68,6 @@
#define DEVICESTORAGE_PROPERTIES \
"chrome://global/content/devicestorage.properties"
#define DEFAULT_THREAD_TIMEOUT_MS 30000
#define PREF_STORAGE_WRITABLE_NAME \
"device.storage.writable.name"
#define STORAGE_CHANGE_EVENT "change"
using namespace mozilla;
@ -84,12 +75,6 @@ using namespace mozilla::dom;
using namespace mozilla::dom::devicestorage;
using namespace mozilla::ipc;
#include "nsDirectoryServiceDefs.h"
const char* kFileWatcherUpdate = "file-watcher-update";
const char* kFileWatcherNotify = "file-watcher-notify";
const char *kDownloadWatcherNotify = "download-watcher-notify";
namespace mozilla {
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close);
} // namespace mozilla
@ -200,25 +185,6 @@ DeviceStorageUsedSpaceCache::SetUsedSizes(const nsAString& aStorageName,
cacheEntry->mDirty = false;
}
class GlobalDirs
{
private:
~GlobalDirs() {}
public:
NS_INLINE_DECL_REFCOUNTING(GlobalDirs)
#if !defined(MOZ_WIDGET_GONK)
nsCOMPtr<nsIFile> pictures;
nsCOMPtr<nsIFile> videos;
nsCOMPtr<nsIFile> music;
nsCOMPtr<nsIFile> sdcard;
#endif
nsCOMPtr<nsIFile> apps;
nsCOMPtr<nsIFile> crashes;
nsCOMPtr<nsIFile> overrideRootDir;
};
static StaticRefPtr<GlobalDirs> sDirs;
StaticAutoPtr<DeviceStorageTypeChecker>
DeviceStorageTypeChecker::sDeviceStorageTypeChecker;
@ -475,136 +441,10 @@ DeviceStorageTypeChecker::IsSharedMediaRoot(const nsAString& aType)
#else
// For desktop, if the directories have been overridden, then they share
// a common root.
return IsMediaType(aType) && sDirs->overrideRootDir;
return IsMediaType(aType) && DeviceStorageStatics::HasOverrideRootDir();
#endif
}
NS_IMPL_ISUPPORTS(FileUpdateDispatcher, nsIObserver)
mozilla::StaticRefPtr<FileUpdateDispatcher> FileUpdateDispatcher::sSingleton;
FileUpdateDispatcher*
FileUpdateDispatcher::GetSingleton()
{
if (sSingleton) {
return sSingleton;
}
sSingleton = new FileUpdateDispatcher();
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->AddObserver(sSingleton, kFileWatcherNotify, false);
obs->AddObserver(sSingleton, kDownloadWatcherNotify, false);
ClearOnShutdown(&sSingleton);
return sSingleton;
}
NS_IMETHODIMP
FileUpdateDispatcher::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
nsRefPtr<DeviceStorageFile> dsf;
if (!strcmp(aTopic, kDownloadWatcherNotify)) {
// aSubject will be an nsISupportsString with the native path to the file
// in question.
nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(aSubject);
if (!supportsString) {
return NS_OK;
}
nsString path;
nsresult rv = supportsString->GetData(path);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
// The downloader uses the sdcard storage type.
nsString volName;
#ifdef MOZ_WIDGET_GONK
if (DeviceStorageTypeChecker::IsVolumeBased(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD))) {
nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
if (NS_WARN_IF(!vs)) {
return NS_OK;
}
nsCOMPtr<nsIVolume> vol;
rv = vs->GetVolumeByPath(path, getter_AddRefs(vol));
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
rv = vol->GetName(volName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
nsString mountPoint;
rv = vol->GetMountPoint(mountPoint);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_OK;
}
if (!Substring(path, 0, mountPoint.Length()).Equals(mountPoint)) {
return NS_OK;
}
path = Substring(path, mountPoint.Length() + 1);
}
#endif
dsf = new DeviceStorageFile(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), volName, path);
} else if (!strcmp(aTopic, kFileWatcherNotify)) {
dsf = static_cast<DeviceStorageFile*>(aSubject);
} else {
NS_WARNING("FileUpdateDispatcher: Unrecognized topic");
return NS_OK;
}
if (!dsf || !dsf->mFile) {
NS_WARNING("FileUpdateDispatcher: Device storage file looks invalid!");
return NS_OK;
}
if (!XRE_IsParentProcess()) {
// Child process. Forward the notification to the parent.
ContentChild::GetSingleton()
->SendFilePathUpdateNotify(dsf->mStorageType,
dsf->mStorageName,
dsf->mPath,
NS_ConvertUTF16toUTF8(aData));
return NS_OK;
}
// Multiple storage types may match the same files. So walk through each of
// the storage types, and if the extension matches, tell them about it.
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (DeviceStorageTypeChecker::IsSharedMediaRoot(dsf->mStorageType)) {
DeviceStorageTypeChecker* typeChecker
= DeviceStorageTypeChecker::CreateOrGet();
MOZ_ASSERT(typeChecker);
static const nsLiteralString kMediaTypes[] = {
NS_LITERAL_STRING(DEVICESTORAGE_SDCARD),
NS_LITERAL_STRING(DEVICESTORAGE_PICTURES),
NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS),
NS_LITERAL_STRING(DEVICESTORAGE_MUSIC),
};
for (size_t i = 0; i < MOZ_ARRAY_LENGTH(kMediaTypes); i++) {
nsRefPtr<DeviceStorageFile> dsf2;
if (typeChecker->Check(kMediaTypes[i], dsf->mPath)) {
if (dsf->mStorageType.Equals(kMediaTypes[i])) {
dsf2 = dsf;
} else {
dsf2 = new DeviceStorageFile(kMediaTypes[i],
dsf->mStorageName, dsf->mPath);
}
obs->NotifyObservers(dsf2, kFileWatcherUpdate, aData);
}
}
} else {
obs->NotifyObservers(dsf, kFileWatcherUpdate, aData);
}
return NS_OK;
}
class IOEventComplete : public nsRunnable
{
public:
@ -623,7 +463,7 @@ public:
CopyASCIItoUTF16(mType, data);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->NotifyObservers(mFile, kFileWatcherNotify, data.get());
obs->NotifyObservers(mFile, "file-watcher-notify", data.get());
DeviceStorageUsedSpaceCache* usedSpaceCache
= DeviceStorageUsedSpaceCache::CreateOrGet();
@ -721,240 +561,6 @@ DeviceStorageFile::Init()
MOZ_ASSERT(typeChecker);
}
// The OverrideRootDir is needed to facilitate testing of the
// device.storage.overrideRootDir preference. The preference is normally
// only read once during initialization, but since the test environment has
// no convenient way to restart, we use a pref watcher instead.
class OverrideRootDir final : public nsIObserver
{
~OverrideRootDir();
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
static OverrideRootDir* GetSingleton();
void Init();
private:
static mozilla::StaticRefPtr<OverrideRootDir> sSingleton;
};
NS_IMPL_ISUPPORTS(OverrideRootDir, nsIObserver)
mozilla::StaticRefPtr<OverrideRootDir>
OverrideRootDir::sSingleton;
OverrideRootDir*
OverrideRootDir::GetSingleton()
{
if (sSingleton) {
return sSingleton;
}
// Preference changes are automatically forwarded from parent to child
// in ContentParent::Observe, so we'll see the change in both the parent
// and the child process.
sSingleton = new OverrideRootDir();
Preferences::AddStrongObserver(sSingleton, "device.storage.overrideRootDir");
Preferences::AddStrongObserver(sSingleton, "device.storage.testing");
ClearOnShutdown(&sSingleton);
return sSingleton;
}
OverrideRootDir::~OverrideRootDir()
{
Preferences::RemoveObserver(this, "device.storage.overrideRootDir");
Preferences::RemoveObserver(this, "device.storage.testing");
}
NS_IMETHODIMP
OverrideRootDir::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
{
MOZ_ASSERT(NS_IsMainThread());
if (sSingleton) {
sSingleton->Init();
}
return NS_OK;
}
void
OverrideRootDir::Init()
{
MOZ_ASSERT(NS_IsMainThread());
if (!sDirs) {
return;
}
if (mozilla::Preferences::GetBool("device.storage.testing", false)) {
nsCOMPtr<nsIProperties> dirService
= do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
MOZ_ASSERT(dirService);
dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->overrideRootDir));
if (sDirs->overrideRootDir) {
sDirs->overrideRootDir->AppendRelativeNativePath(
NS_LITERAL_CSTRING("device-storage-testing"));
}
} else {
// For users running on desktop, it's convenient to be able to override
// all of the directories to point to a single tree, much like what happens
// on a real device.
const nsAdoptingString& overrideRootDir =
mozilla::Preferences::GetString("device.storage.overrideRootDir");
if (overrideRootDir && overrideRootDir.Length() > 0) {
NS_NewLocalFile(overrideRootDir, false,
getter_AddRefs(sDirs->overrideRootDir));
} else {
sDirs->overrideRootDir = nullptr;
}
}
if (sDirs->overrideRootDir) {
if (XRE_IsParentProcess()) {
// Only the parent process can create directories. In testing, because
// the preference is updated after startup, its entirely possible that
// the preference updated notification will be received by a child
// prior to the parent.
nsresult rv
= sDirs->overrideRootDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
nsString path;
sDirs->overrideRootDir->GetPath(path);
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
nsPrintfCString msg("DeviceStorage: Unable to create directory '%s'",
NS_LossyConvertUTF16toASCII(path).get());
NS_WARNING(msg.get());
}
}
sDirs->overrideRootDir->Normalize();
}
}
// Directories which don't depend on a volume should be calculated once
// here. Directories which depend on the root directory of a volume
// should be calculated in DeviceStorageFile::GetRootDirectoryForType.
static void
InitDirs()
{
if (sDirs) {
return;
}
MOZ_ASSERT(NS_IsMainThread());
sDirs = new GlobalDirs;
ClearOnShutdown(&sDirs);
nsCOMPtr<nsIProperties> dirService
= do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
MOZ_ASSERT(dirService);
#if !defined(MOZ_WIDGET_GONK)
// Keep MOZ_WIDGET_COCOA above XP_UNIX,
// because both are defined in Darwin builds.
#if defined (MOZ_WIDGET_COCOA)
dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->pictures));
dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->videos));
dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->music));
// Keep MOZ_WIDGET_ANDROID above XP_UNIX,
// because both are defined in Android builds.
#elif defined (MOZ_WIDGET_ANDROID)
nsAutoString path;
if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
NS_LITERAL_STRING(DEVICESTORAGE_PICTURES), path))) {
NS_NewLocalFile(path, /* aFollowLinks */ true,
getter_AddRefs(sDirs->pictures));
}
if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS), path))) {
NS_NewLocalFile(path, /* aFollowLinks */ true,
getter_AddRefs(sDirs->videos));
}
if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
NS_LITERAL_STRING(DEVICESTORAGE_MUSIC), path))) {
NS_NewLocalFile(path, /* aFollowLinks */ true,
getter_AddRefs(sDirs->music));
}
if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), path))) {
NS_NewLocalFile(path, /* aFollowLinks */ true,
getter_AddRefs(sDirs->sdcard));
}
#elif defined (XP_UNIX)
dirService->Get(NS_UNIX_XDG_PICTURES_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->pictures));
dirService->Get(NS_UNIX_XDG_VIDEOS_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->videos));
dirService->Get(NS_UNIX_XDG_MUSIC_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->music));
#elif defined (XP_WIN)
dirService->Get(NS_WIN_PICTURES_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->pictures));
dirService->Get(NS_WIN_VIDEOS_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->videos));
dirService->Get(NS_WIN_MUSIC_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->music));
#endif
#ifndef MOZ_WIDGET_ANDROID
// Eventually, on desktop, we want to do something smarter -- for example,
// detect when an sdcard is inserted, and use that instead of this.
dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->sdcard));
if (sDirs->sdcard) {
sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
}
#endif // !MOZ_WIDGET_ANDROID
#endif // !MOZ_WIDGET_GONK
#ifdef MOZ_WIDGET_GONK
NS_NewLocalFile(NS_LITERAL_STRING("/data"),
false,
getter_AddRefs(sDirs->apps));
#else
dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->apps));
if (sDirs->apps) {
sDirs->apps->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
}
#endif
if (XRE_IsParentProcess()) {
NS_GetSpecialDirectory("UAppData", getter_AddRefs(sDirs->crashes));
if (sDirs->crashes) {
sDirs->crashes->Append(NS_LITERAL_STRING("Crash Reports"));
}
} else {
// NS_GetSpecialDirectory("UAppData") fails in content processes because
// gAppData from toolkit/xre/nsAppRunner.cpp is not initialized.
#ifdef MOZ_WIDGET_GONK
NS_NewLocalFile(NS_LITERAL_STRING("/data/b2g/mozilla/Crash Reports"),
false,
getter_AddRefs(sDirs->crashes));
#endif
}
OverrideRootDir::GetSingleton()->Init();
}
void
DeviceStorageFile::GetFullPath(nsAString &aFullPath)
{
@ -973,8 +579,8 @@ DeviceStorageFile::GetFullPath(nsAString &aFullPath)
// Directories which don't depend on a volume should be calculated once
// in InitDirs. Directories which depend on the root directory of a volume
// should be calculated in this method.
// in DeviceStorageStatics::Initialize. Directories which depend on the
// root directory of a volume should be calculated in this method.
void
DeviceStorageFile::GetRootDirectoryForType(const nsAString& aStorageType,
const nsAString& aStorageName,
@ -982,9 +588,8 @@ DeviceStorageFile::GetRootDirectoryForType(const nsAString& aStorageType,
{
nsCOMPtr<nsIFile> f;
*aFile = nullptr;
bool allowOverride = true;
InitDirs();
DeviceStorageStatics::InitializeDirs();
#ifdef MOZ_WIDGET_GONK
nsresult rv;
@ -994,7 +599,7 @@ DeviceStorageFile::GetRootDirectoryForType(const nsAString& aStorageType,
NS_ENSURE_TRUE_VOID(vs);
nsCOMPtr<nsIVolume> vol;
rv = vs->GetVolumeByName(aStorageName, getter_AddRefs(vol));
if(NS_FAILED(rv)) {
if (NS_FAILED(rv)) {
printf_stderr("##### DeviceStorage: GetVolumeByName('%s') failed\n",
NS_LossyConvertUTF16toASCII(aStorageName).get());
}
@ -1003,85 +608,36 @@ DeviceStorageFile::GetRootDirectoryForType(const nsAString& aStorageType,
}
#endif
// Picture directory
if (aStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
#ifdef MOZ_WIDGET_GONK
rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
if(NS_FAILED(rv)) {
printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
NS_LossyConvertUTF16toASCII(volMountPoint).get(),
NS_LossyConvertUTF16toASCII(aStorageType).get());
}
#else
f = sDirs->pictures;
#endif
}
// Video directory
else if (aStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
#ifdef MOZ_WIDGET_GONK
rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
if(NS_FAILED(rv)) {
printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
NS_LossyConvertUTF16toASCII(volMountPoint).get(),
NS_LossyConvertUTF16toASCII(aStorageType).get());
}
#else
f = sDirs->videos;
#endif
}
// Music directory
else if (aStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
#ifdef MOZ_WIDGET_GONK
rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
if(NS_FAILED(rv)) {
printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
NS_LossyConvertUTF16toASCII(volMountPoint).get(),
NS_LossyConvertUTF16toASCII(aStorageType).get());
}
#else
f = sDirs->music;
#endif
}
// Apps directory
else if (aStorageType.EqualsLiteral(DEVICESTORAGE_APPS)) {
f = sDirs->apps;
allowOverride = false;
}
// default SDCard
else if (aStorageType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
#ifdef MOZ_WIDGET_GONK
rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
if(NS_FAILED(rv)) {
printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
NS_LossyConvertUTF16toASCII(volMountPoint).get(),
NS_LossyConvertUTF16toASCII(aStorageType).get());
}
#else
f = sDirs->sdcard;
#endif
}
// crash reports directory.
else if (aStorageType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
f = sDirs->crashes;
allowOverride = false;
f = DeviceStorageStatics::GetPicturesDir();
} else if (aStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
f = DeviceStorageStatics::GetVideosDir();
} else if (aStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
f = DeviceStorageStatics::GetMusicDir();
} else if (aStorageType.EqualsLiteral(DEVICESTORAGE_APPS)) {
f = DeviceStorageStatics::GetAppsDir();
} else if (aStorageType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
f = DeviceStorageStatics::GetCrashesDir();
} else if (aStorageType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
f = DeviceStorageStatics::GetSdcardDir();
} else {
// Not a storage type that we recognize. Return null
printf_stderr("##### DeviceStorage: Unrecognized StorageType: '%s'\n",
NS_LossyConvertUTF16toASCII(aStorageType).get());
return;
}
// In testing, we default all device storage types to a temp directory.
// sDirs->overrideRootDir will only have been initialized (in InitDirs)
// if the preference device.storage.testing was set to true, or if
// device.storage.overrideRootDir is set. We can't test the preferences
// directly here, since we may not be on the main thread.
if (allowOverride && sDirs->overrideRootDir) {
f = sDirs->overrideRootDir;
#ifdef MOZ_WIDGET_GONK
/* For volume based storage types, we will only have a file already
if the override root directory option is in effect. */
if (!f && !volMountPoint.IsEmpty()) {
rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
if (NS_FAILED(rv)) {
printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
NS_LossyConvertUTF16toASCII(volMountPoint).get(),
NS_LossyConvertUTF16toASCII(aStorageType).get());
}
}
#endif
if (f) {
f->Clone(aFile);
@ -1841,24 +1397,6 @@ DeviceStorageFile::GetStorageStatus(nsAString& aStatus)
NS_IMPL_ISUPPORTS0(DeviceStorageFile)
static void
RegisterForSDCardChanges(nsIObserver* aObserver)
{
#ifdef MOZ_WIDGET_GONK
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->AddObserver(aObserver, NS_VOLUME_STATE_CHANGED, false);
#endif
}
static void
UnregisterForSDCardChanges(nsIObserver* aObserver)
{
#ifdef MOZ_WIDGET_GONK
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->RemoveObserver(aObserver, NS_VOLUME_STATE_CHANGED);
#endif
}
void
nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aStorageType,
const nsAString& aStorageName)
@ -1869,9 +1407,6 @@ nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aStorageType,
DeviceStorageFile::GetRootDirectoryForType(aStorageType,
aStorageName,
getter_AddRefs(f));
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->AddObserver(this, kFileWatcherUpdate, false);
obs->AddObserver(this, "disk-space-watcher", false);
mRootDirectory = f;
mStorageType = aStorageType;
mStorageName = aStorageName;
@ -2930,7 +2465,7 @@ public:
{
MOZ_ASSERT(NS_IsMainThread());
if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
if (DeviceStorageStatics::IsPromptTesting()) {
Allow(JS::UndefinedHandleValue);
return NS_OK;
}
@ -3363,7 +2898,6 @@ NS_IMPL_CYCLE_COLLECTION(DeviceStorageRequest,
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDeviceStorage)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper)
@ -3392,20 +2926,16 @@ nsresult
nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType,
const nsAString &aVolName)
{
DebugOnly<FileUpdateDispatcher*> observer
= FileUpdateDispatcher::GetSingleton();
MOZ_ASSERT(observer);
MOZ_ASSERT(aWindow);
SetRootDirectoryForType(aType, aVolName);
if (!mRootDirectory) {
return NS_ERROR_NOT_AVAILABLE;
}
DeviceStorageStatics::AddListener(this);
if (!mStorageName.IsEmpty()) {
Preferences::AddStrongObserver(this, PREF_STORAGE_WRITABLE_NAME);
mIsDefaultLocation = Default();
RegisterForSDCardChanges(this);
#ifdef MOZ_WIDGET_GONK
if (DeviceStorageTypeChecker::IsVolumeBased(mStorageType)) {
@ -3479,14 +3009,7 @@ nsDOMDeviceStorage::Shutdown()
mFileSystem = nullptr;
}
if (!mStorageName.IsEmpty()) {
Preferences::RemoveObserver(this, PREF_STORAGE_WRITABLE_NAME);
UnregisterForSDCardChanges(this);
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->RemoveObserver(this, kFileWatcherUpdate);
obs->RemoveObserver(this, "disk-space-watcher");
DeviceStorageStatics::RemoveListener(this);
}
StaticAutoPtr<nsTArray<nsString>> nsDOMDeviceStorage::sVolumeNameCache;
@ -3724,10 +3247,10 @@ nsDOMDeviceStorage::GetDefaultStorageName(const nsAString& aStorageType,
nsAString& aStorageName)
{
// See if the preferred volume is available.
nsAdoptingString prefStorageName =
mozilla::Preferences::GetString(PREF_STORAGE_WRITABLE_NAME);
nsString prefStorageName;
DeviceStorageStatics::GetWritableName(prefStorageName);
if (prefStorageName) {
if (!prefStorageName.IsEmpty()) {
nsString status;
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(aStorageType,
prefStorageName);
@ -3746,7 +3269,7 @@ nsDOMDeviceStorage::GetDefaultStorageName(const nsAString& aStorageType,
if (volNames.Length() > 0) {
aStorageName = volNames[0];
// overwrite the value of "device.storage.writable.name"
mozilla::Preferences::SetString(PREF_STORAGE_WRITABLE_NAME, aStorageName);
DeviceStorageStatics::SetWritableName(aStorageName);
return;
}
@ -4333,7 +3856,7 @@ nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
nsRefPtr<DeviceStorageCursorRequest> r
= new DeviceStorageCursorRequest(cursor);
if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
if (DeviceStorageStatics::IsPromptTesting()) {
r->Allow(JS::UndefinedHandleValue);
return cursor.forget();
}
@ -4344,7 +3867,7 @@ nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
}
void
nsDOMDeviceStorage::DispatchDefaultChangeEvent()
nsDOMDeviceStorage::OnWritableNameChanged()
{
nsAdoptingString DefaultLocation;
GetDefaultStorageName(mStorageType, DefaultLocation);
@ -4422,78 +3945,58 @@ nsDOMDeviceStorage::DispatchStorageStatusChangeEvent(nsAString& aStorageStatus)
}
#endif
NS_IMETHODIMP
nsDOMDeviceStorage::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
void
nsDOMDeviceStorage::OnFileWatcherUpdate(const nsCString& aData, DeviceStorageFile* aFile)
{
MOZ_ASSERT(NS_IsMainThread());
Notify(aData.get(), aFile);
}
if (!strcmp(aTopic, kFileWatcherUpdate)) {
DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
Notify(NS_ConvertUTF16toUTF8(aData).get(), file);
return NS_OK;
}
if (!strcmp(aTopic, "disk-space-watcher")) {
// 'disk-space-watcher' notifications are sent when there is a modification
// of a file in a specific location while a low device storage situation
// exists or after recovery of a low storage situation. For Firefox OS,
// these notifications are specific for apps storage.
nsRefPtr<DeviceStorageFile> file =
new DeviceStorageFile(mStorageType, mStorageName);
if (!NS_strcmp(aData, MOZ_UTF16("full"))) {
Notify("low-disk-space", file);
} else if (!NS_strcmp(aData, MOZ_UTF16("free"))) {
Notify("available-disk-space", file);
}
return NS_OK;
}
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) &&
aData &&
nsDependentString(aData).Equals(NS_LITERAL_STRING(PREF_STORAGE_WRITABLE_NAME)))
{
DispatchDefaultChangeEvent();
return NS_OK;
void
nsDOMDeviceStorage::OnDiskSpaceWatcher(bool aLowDiskSpace)
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<DeviceStorageFile> file =
new DeviceStorageFile(mStorageType, mStorageName);
if (aLowDiskSpace) {
Notify("low-disk-space", file);
} else {
Notify("available-disk-space", file);
}
}
#ifdef MOZ_WIDGET_GONK
else if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
// We invalidate the used space cache for the volume that actually changed
// state.
nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
if (!vol) {
return NS_OK;
}
nsString volName;
vol->GetName(volName);
void
nsDOMDeviceStorage::OnVolumeStateChanged(nsIVolume* aVolume) {
MOZ_ASSERT(NS_IsMainThread());
DeviceStorageUsedSpaceCache* usedSpaceCache
= DeviceStorageUsedSpaceCache::CreateOrGet();
MOZ_ASSERT(usedSpaceCache);
usedSpaceCache->Invalidate(volName);
// We invalidate the used space cache for the volume that actually changed
// state.
nsString volName;
aVolume->GetName(volName);
if (!volName.Equals(mStorageName)) {
// Not our volume - we can ignore.
return NS_OK;
}
DeviceStorageUsedSpaceCache* usedSpaceCache
= DeviceStorageUsedSpaceCache::CreateOrGet();
MOZ_ASSERT(usedSpaceCache);
usedSpaceCache->Invalidate(volName);
nsRefPtr<DeviceStorageFile> dsf(new DeviceStorageFile(mStorageType, mStorageName));
nsString status, storageStatus;
// Get Status (one of "available, unavailable, shared")
dsf->GetStatus(status);
DispatchStatusChangeEvent(status);
// Get real volume status (defined in dom/system/gonk/nsIVolume.idl)
dsf->GetStorageStatus(storageStatus);
DispatchStorageStatusChangeEvent(storageStatus);
return NS_OK;
if (!volName.Equals(mStorageName)) {
// Not our volume - we can ignore.
return;
}
#endif
return NS_OK;
nsRefPtr<DeviceStorageFile> dsf(new DeviceStorageFile(mStorageType, mStorageName));
nsString status, storageStatus;
// Get Status (one of "available, unavailable, shared")
dsf->GetStatus(status);
DispatchStatusChangeEvent(status);
// Get real volume status (defined in dom/system/gonk/nsIVolume.idl)
dsf->GetStorageStatus(storageStatus);
DispatchStorageStatusChangeEvent(storageStatus);
}
#endif
nsresult
nsDOMDeviceStorage::Notify(const char* aReason, DeviceStorageFile* aFile)

View File

@ -39,6 +39,25 @@ class Blob;
} // namespace dom
} // namespace mozilla
//#define DS_LOGGING 1
#ifdef DS_LOGGING
/* Polyfill __func__ on MSVC to pass to the log. */
#ifdef _MSC_VER
#define __func__ __FUNCTION__
#endif
#define DS_LOG_DEBUG(msg, ...) printf_stderr("[%s:%d] " msg "\n", __func__, __LINE__, ##__VA_ARGS__)
#define DS_LOG_INFO DS_LOG_DEBUG
#define DS_LOG_WARN DS_LOG_DEBUG
#define DS_LOG_ERROR DS_LOG_DEBUG
#else
#define DS_LOG_DEBUG(msg, ...)
#define DS_LOG_INFO(msg, ...)
#define DS_LOG_WARN(msg, ...)
#define DS_LOG_ERROR(msg, ...)
#endif
#define POST_ERROR_EVENT_FILE_EXISTS "NoModificationAllowedError"
#define POST_ERROR_EVENT_FILE_DOES_NOT_EXIST "NotFoundError"
#define POST_ERROR_EVENT_FILE_NOT_ENUMERABLE "TypeMismatchError"

View File

@ -842,9 +842,6 @@ ContentChild::InitXPCOM()
global->SetInitialProcessData(data);
}
DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
NS_ASSERTION(observer, "FileUpdateDispatcher is null");
// This object is held alive by the observer service.
nsRefPtr<SystemMessageHandledObserver> sysMsgObserver =
new SystemMessageHandledObserver();

View File

@ -1459,9 +1459,6 @@ ContentParent::Init()
unused << SendActivateA11y();
}
#endif
DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
NS_ASSERTION(observer, "FileUpdateDispatcher is null");
}
void

View File

@ -136,6 +136,7 @@ using namespace mozilla::system;
#include "TouchManager.h"
#include "MediaDecoder.h"
#include "mozilla/layers/CompositorLRU.h"
#include "mozilla/dom/devicestorage/DeviceStorageStatics.h"
using namespace mozilla;
using namespace mozilla::net;
@ -319,6 +320,8 @@ nsLayoutStatics::Initialize()
layers::CompositorLRU::Init();
mozilla::dom::devicestorage::DeviceStorageStatics::Initialize();
return NS_OK;
}