зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1186273 - Part 1. Move preferences and observers into dedicated threadsafe module. r=dhylands
This commit is contained in:
Родитель
34049e2d6c
Коммит
bc2720df67
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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',
|
||||
]
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -843,9 +843,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();
|
||||
|
|
|
@ -1459,9 +1459,6 @@ ContentParent::Init()
|
|||
unused << SendActivateA11y();
|
||||
}
|
||||
#endif
|
||||
|
||||
DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
|
||||
NS_ASSERTION(observer, "FileUpdateDispatcher is null");
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче