Merge b2g-inbound to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-08-18 10:41:52 -04:00
commit 7f2f3a93e8
32 changed files with 1293 additions and 756 deletions

View File

@ -12,10 +12,10 @@
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<project name="platform_build" path="build" remote="b2g" revision="aacd9b12da7fc3c8f4deaaa8eedfbb158f4fefe7">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -12,10 +12,10 @@
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<project name="platform_build" path="build" remote="b2g" revision="aacd9b12da7fc3c8f4deaaa8eedfbb158f4fefe7">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="bfdb6348725a33bdcdc4e17999cb500be6beedb5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a6f9a1245d98c51172c15afecb9ade1a6ca511e2"/>

View File

@ -12,10 +12,10 @@
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<project name="platform_build" path="build" remote="b2g" revision="aacd9b12da7fc3c8f4deaaa8eedfbb158f4fefe7">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -128,8 +128,8 @@
<default remote="caf" revision="refs/tags/android-4.4.2_r1" sync-j="4"/>
<!-- Emulator specific things -->
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="72ffdf71c68a96309212eb13d63560d66db14c9e"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="85f6a2e1e638dbc8b119896e61383e973e764ffd"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="4bebbe8d92368befc31e8b4a99da2d29cc26bfbc"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="dea95f1a539f037db1b00732048a8722532b4d47"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="ef1a0385c3286dc2403c72fdc8092258c20f70d4"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="f37bd545063039e30a92f2550ae78c0e6e4e2d08"/>
<project name="platform_external_wpa_supplicant_8" path="external/wpa_supplicant_8" remote="b2g" revision="0c6a6547cd1fd302fa2b0f6e375654df36bf0ec4"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd4fc430da93fad3123f0775791a919568aa0ca2"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>

View File

@ -12,10 +12,10 @@
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<project name="platform_build" path="build" remote="b2g" revision="aacd9b12da7fc3c8f4deaaa8eedfbb158f4fefe7">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "d9d99f32762975a370f1abd34a3512bd6fe29111",
"git_revision": "507ba38fb64b27f87d11f4104dfcc58448e12b1a",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "a1b18f9a9e869b1ae261a064a7dd174d59169fb9",
"revision": "8ca4fec22230c6b6d62301261a6fc76a6fa64814",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="bfdb6348725a33bdcdc4e17999cb500be6beedb5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a6f9a1245d98c51172c15afecb9ade1a6ca511e2"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -206,7 +206,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCameraManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagesManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorageStores)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimeManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
@ -328,7 +327,10 @@ Navigator::Invalidate()
uint32_t len = mDeviceStorageStores.Length();
for (uint32_t i = 0; i < len; ++i) {
mDeviceStorageStores[i]->Shutdown();
nsRefPtr<nsDOMDeviceStorage> ds = do_QueryReferent(mDeviceStorageStores[i]);
if (ds) {
ds->Shutdown();
}
}
mDeviceStorageStores.Clear();
@ -961,7 +963,26 @@ Navigator::GetDeviceStorageAreaListener(ErrorResult& aRv)
return mDeviceStorageAreaListener;
}
nsDOMDeviceStorage*
already_AddRefed<nsDOMDeviceStorage>
Navigator::FindDeviceStorage(const nsAString& aName, const nsAString& aType)
{
auto i = mDeviceStorageStores.Length();
while (i > 0) {
--i;
nsRefPtr<nsDOMDeviceStorage> storage =
do_QueryReferent(mDeviceStorageStores[i]);
if (storage) {
if (storage->Equals(mWindow, aName, aType)) {
return storage.forget();
}
} else {
mDeviceStorageStores.RemoveElementAt(i);
}
}
return nullptr;
}
already_AddRefed<nsDOMDeviceStorage>
Navigator::GetDeviceStorage(const nsAString& aType, ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
@ -969,7 +990,13 @@ Navigator::GetDeviceStorage(const nsAString& aType, ErrorResult& aRv)
return nullptr;
}
nsRefPtr<nsDOMDeviceStorage> storage;
nsString name;
nsDOMDeviceStorage::GetDefaultStorageName(aType, name);
nsRefPtr<nsDOMDeviceStorage> storage = FindDeviceStorage(name, aType);
if (storage) {
return storage.forget();
}
nsDOMDeviceStorage::CreateDeviceStorageFor(mWindow, aType,
getter_AddRefs(storage));
@ -977,8 +1004,9 @@ Navigator::GetDeviceStorage(const nsAString& aType, ErrorResult& aRv)
return nullptr;
}
mDeviceStorageStores.AppendElement(storage);
return storage;
mDeviceStorageStores.AppendElement(
do_GetWeakReference(static_cast<DOMEventTargetHelper*>(storage)));
return storage.forget();
}
void
@ -991,12 +1019,31 @@ Navigator::GetDeviceStorages(const nsAString& aType,
return;
}
nsDOMDeviceStorage::CreateDeviceStoragesFor(mWindow, aType, aStores);
nsDOMDeviceStorage::VolumeNameArray volumes;
nsDOMDeviceStorage::GetOrderedVolumeNames(aType, volumes);
if (volumes.IsEmpty()) {
nsRefPtr<nsDOMDeviceStorage> storage = GetDeviceStorage(aType, aRv);
if (storage) {
aStores.AppendElement(storage.forget());
}
} else {
uint32_t len = volumes.Length();
aStores.SetCapacity(len);
for (uint32_t i = 0; i < len; ++i) {
nsRefPtr<nsDOMDeviceStorage> storage =
GetDeviceStorageByNameAndType(volumes[i], aType, aRv);
if (aRv.Failed()) {
break;
}
mDeviceStorageStores.AppendElements(aStores);
if (storage) {
aStores.AppendElement(storage.forget());
}
}
}
}
nsDOMDeviceStorage*
already_AddRefed<nsDOMDeviceStorage>
Navigator::GetDeviceStorageByNameAndType(const nsAString& aName,
const nsAString& aType,
ErrorResult& aRv)
@ -1006,7 +1053,10 @@ Navigator::GetDeviceStorageByNameAndType(const nsAString& aName,
return nullptr;
}
nsRefPtr<nsDOMDeviceStorage> storage;
nsRefPtr<nsDOMDeviceStorage> storage = FindDeviceStorage(aName, aType);
if (storage) {
return storage.forget();
}
nsDOMDeviceStorage::CreateDeviceStorageByNameAndType(mWindow, aName, aType,
getter_AddRefs(storage));
@ -1014,8 +1064,9 @@ Navigator::GetDeviceStorageByNameAndType(const nsAString& aName,
return nullptr;
}
mDeviceStorageStores.AppendElement(storage);
return storage;
mDeviceStorageStores.AppendElement(
do_GetWeakReference(static_cast<DOMEventTargetHelper*>(storage)));
return storage.forget();
}
Geolocation*

View File

@ -18,6 +18,7 @@
#include "nsInterfaceHashtable.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsWeakPtr.h"
#ifdef MOZ_EME
#include "mozilla/dom/MediaKeySystemAccessManager.h"
#endif
@ -220,14 +221,18 @@ public:
already_AddRefed<WakeLock> RequestWakeLock(const nsAString &aTopic,
ErrorResult& aRv);
DeviceStorageAreaListener* GetDeviceStorageAreaListener(ErrorResult& aRv);
nsDOMDeviceStorage* GetDeviceStorage(const nsAString& aType,
ErrorResult& aRv);
already_AddRefed<nsDOMDeviceStorage> GetDeviceStorage(const nsAString& aType,
ErrorResult& aRv);
void GetDeviceStorages(const nsAString& aType,
nsTArray<nsRefPtr<nsDOMDeviceStorage> >& aStores,
ErrorResult& aRv);
nsDOMDeviceStorage* GetDeviceStorageByNameAndType(const nsAString& aName,
const nsAString& aType,
ErrorResult& aRv);
already_AddRefed<nsDOMDeviceStorage>
GetDeviceStorageByNameAndType(const nsAString& aName, const nsAString& aType,
ErrorResult& aRv);
DesktopNotificationCenter* GetMozNotification(ErrorResult& aRv);
CellBroadcast* GetMozCellBroadcast(ErrorResult& aRv);
IccManager* GetMozIccManager(ErrorResult& aRv);
@ -357,6 +362,9 @@ private:
bool CheckPermission(const char* type);
static bool CheckPermission(nsPIDOMWindow* aWindow, const char* aType);
already_AddRefed<nsDOMDeviceStorage> FindDeviceStorage(const nsAString& aName,
const nsAString& aType);
nsRefPtr<nsMimeTypeArray> mMimeTypes;
nsRefPtr<nsPluginArray> mPlugins;
nsRefPtr<Permissions> mPermissions;
@ -387,7 +395,7 @@ private:
nsRefPtr<nsDOMCameraManager> mCameraManager;
nsRefPtr<MediaDevices> mMediaDevices;
nsCOMPtr<nsIDOMNavigatorSystemMessages> mMessagesManager;
nsTArray<nsRefPtr<nsDOMDeviceStorage> > mDeviceStorageStores;
nsTArray<nsWeakPtr> mDeviceStorageStores;
nsRefPtr<time::TimeManager> mTimeManager;
nsRefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
nsCOMPtr<nsPIDOMWindow> mWindow;

View File

@ -6,7 +6,6 @@
#include "base/basictypes.h"
#include "mozilla/Assertions.h"
#include "mozilla/unused.h"
#include "nsPrintfCString.h"
#include "nsIWeakReferenceUtils.h"
#include "CameraCommon.h"
#include "nsGlobalWindow.h"
@ -18,7 +17,7 @@ using namespace mozilla;
/* static */ StaticRefPtr<nsIThread> CameraControlImpl::sCameraThread;
CameraControlImpl::CameraControlImpl()
: mListenerLock(PR_NewRWLock(PR_RWLOCK_RANK_NONE, "CameraControlImpl.Listeners.Lock"))
: mListenerLock("mozilla::camera::CameraControlImpl.Listeners")
, mPreviewState(CameraControlListener::kPreviewStopped)
, mHardwareState(CameraControlListener::kHardwareUninitialized)
, mHardwareStateChangeReason(NS_OK)
@ -50,32 +49,11 @@ CameraControlImpl::CameraControlImpl()
mCameraThread->Dispatch(new Delegate(), NS_DISPATCH_NORMAL);
sCameraThread = mCameraThread;
}
// Care must be taken with the mListenerLock read-write lock to prevent
// deadlocks. Currently this is handled by ensuring that any attempts to
// acquire the lock for writing (as in Add/RemoveListener()) happen in a
// runnable dispatched to the Camera Thread--even if the method is being
// called from that thread. This ensures that if a registered listener
// (which is invoked with a read-lock) tries to call Add/RemoveListener(),
// the lock-for-writing attempt won't happen until the listener has
// completed.
//
// Multiple parallel listeners being invoked are not a problem because
// the read-write lock allows multiple simultaneous read-locks.
if (!mListenerLock) {
MOZ_CRASH("Out of memory getting new PRRWLock");
}
}
CameraControlImpl::~CameraControlImpl()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
MOZ_ASSERT(mListenerLock, "mListenerLock missing in ~CameraControlImpl()");
if (mListenerLock) {
PR_DestroyRWLock(mListenerLock);
mListenerLock = nullptr;
}
}
void
@ -85,7 +63,7 @@ CameraControlImpl::OnHardwareStateChange(CameraControlListener::HardwareState aN
// This callback can run on threads other than the Main Thread and
// the Camera Thread. On Gonk, it may be called from the camera's
// local binder thread, should the mediaserver process die.
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
if (aNewState == mHardwareState) {
DOM_CAMERA_LOGI("OnHardwareStateChange: state did not change from %d\n", mHardwareState);
@ -114,7 +92,7 @@ void
CameraControlImpl::OnConfigurationChange()
{
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
DOM_CAMERA_LOGI("OnConfigurationChange : %zu listeners\n", mListeners.Length());
@ -130,7 +108,7 @@ CameraControlImpl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
// This callback can run on threads other than the Main Thread and
// the Camera Thread. On Gonk, it is called from the camera
// library's auto focus thread.
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
@ -141,7 +119,7 @@ CameraControlImpl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
void
CameraControlImpl::OnAutoFocusMoving(bool aIsMoving)
{
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
@ -155,7 +133,7 @@ CameraControlImpl::OnFacesDetected(const nsTArray<Face>& aFaces)
// This callback can run on threads other than the Main Thread and
// the Camera Thread. On Gonk, it is called from the camera
// library's face detection thread.
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
@ -169,7 +147,7 @@ CameraControlImpl::OnTakePictureComplete(const uint8_t* aData, uint32_t aLength,
// This callback can run on threads other than the Main Thread and
// the Camera Thread. On Gonk, it is called from the camera
// library's snapshot thread.
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
@ -182,7 +160,7 @@ CameraControlImpl::OnPoster(dom::BlobImpl* aBlobImpl)
{
// This callback can run on threads other than the Main Thread and
// the Camera Thread.
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
@ -196,7 +174,7 @@ CameraControlImpl::OnShutter()
// This callback can run on threads other than the Main Thread and
// the Camera Thread. On Gonk, it is called from the camera driver's
// preview thread.
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
@ -211,7 +189,7 @@ CameraControlImpl::OnRecorderStateChange(CameraControlListener::RecorderState aS
// This callback can run on threads other than the Main Thread and
// the Camera Thread. On Gonk, it is called from the media encoder
// thread.
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
@ -225,7 +203,7 @@ CameraControlImpl::OnPreviewStateChange(CameraControlListener::PreviewState aNew
// This callback runs on the Main Thread and the Camera Thread, and
// may run on the local binder thread, should the mediaserver
// process die.
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
if (aNewState == mPreviewState) {
DOM_CAMERA_LOGI("OnPreviewStateChange: state did not change from %d\n", mPreviewState);
@ -252,7 +230,7 @@ void
CameraControlImpl::OnRateLimitPreview(bool aLimit)
{
// This function runs on neither the Main Thread nor the Camera Thread.
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
DOM_CAMERA_LOGI("OnRateLimitPreview: %d\n", aLimit);
@ -267,7 +245,7 @@ CameraControlImpl::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uin
{
// This function runs on neither the Main Thread nor the Camera Thread.
// On Gonk, it is called from the camera driver's preview thread.
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %zu preview frame listener(s)\n",
mListeners.Length());
@ -287,7 +265,7 @@ CameraControlImpl::OnUserError(CameraControlListener::UserContext aContext,
{
// This callback can run on threads other than the Main Thread and
// the Camera Thread.
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
const char* context[] = {
"StartCamera",
@ -328,7 +306,7 @@ CameraControlImpl::OnSystemError(CameraControlListener::SystemContext aContext,
{
// This callback can run on threads other than the Main Thread and
// the Camera Thread.
RwLockAutoEnterRead lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
const char* context[] = {
"Camera Service"
@ -746,7 +724,7 @@ protected:
void
CameraControlImpl::AddListenerImpl(already_AddRefed<CameraControlListener> aListener)
{
RwLockAutoEnterWrite lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
CameraControlListener* l = *mListeners.AppendElement() = aListener;
DOM_CAMERA_LOGI("Added camera control listener %p\n", l);
@ -784,7 +762,7 @@ CameraControlImpl::AddListener(CameraControlListener* aListener)
void
CameraControlImpl::RemoveListenerImpl(CameraControlListener* aListener)
{
RwLockAutoEnterWrite lock(mListenerLock);
MutexAutoLock lock(mListenerLock);
nsRefPtr<CameraControlListener> l(aListener);
mListeners.RemoveElement(l);

View File

@ -9,9 +9,9 @@
#include "nsWeakPtr.h"
#include "mozilla/Attributes.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/Mutex.h"
#include "nsIFile.h"
#include "nsProxyRelease.h"
#include "AutoRwLock.h"
#include "ICameraControl.h"
#include "CameraCommon.h"
#include "DeviceStorage.h"
@ -91,7 +91,7 @@ protected:
void AddListenerImpl(already_AddRefed<CameraControlListener> aListener);
void RemoveListenerImpl(CameraControlListener* aListener);
nsTArray<nsRefPtr<CameraControlListener> > mListeners;
PRRWLock* mListenerLock;
mutable Mutex mListenerLock;
class ControlMessage;
class ListenerMessage;

View File

@ -9,11 +9,11 @@
#include "nsIFile.h"
#include "nsIPrincipal.h"
#include "nsIObserver.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/DOMRequest.h"
#include "nsWeakReference.h"
#define DEVICESTORAGE_PICTURES "pictures"
#define DEVICESTORAGE_VIDEOS "videos"
@ -25,6 +25,9 @@
class nsIInputStream;
class nsIOutputStream;
struct DeviceStorageFileDescriptor;
#ifdef MOZ_WIDGET_GONK
class nsIVolume;
#endif
namespace mozilla {
class EventListenerManager;
@ -130,36 +133,13 @@ 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;
};
#define NS_DOM_DEVICE_STORAGE_CID \
{ 0xe4a9b969, 0x81fe, 0x44f1, \
{ 0xaa, 0x0c, 0x9e, 0x16, 0x64, 0x86, 0x2a, 0xd5 } }
class nsDOMDeviceStorage final
: public mozilla::DOMEventTargetHelper
, public nsIObserver
, public nsSupportsWeakReference
{
typedef mozilla::ErrorResult ErrorResult;
typedef mozilla::dom::DeviceStorageEnumerationParameters
@ -171,8 +151,8 @@ class nsDOMDeviceStorage final
public:
typedef nsTArray<nsString> VolumeNameArray;
NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_DEVICE_STORAGE_CID)
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIOBSERVER
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
void EventListenerWasAdded(const nsAString& aType,
@ -278,27 +258,38 @@ public:
const nsAString& aType,
nsDOMDeviceStorage** aStore);
static void
CreateDeviceStoragesFor(nsPIDOMWindow* aWin,
const nsAString& aType,
nsTArray<nsRefPtr<nsDOMDeviceStorage> >& aStores);
static void
CreateDeviceStorageByNameAndType(nsPIDOMWindow* aWin,
const nsAString& aName,
const nsAString& aType,
nsDOMDeviceStorage** aStore);
bool Equals(nsPIDOMWindow* aWin,
const nsAString& aName,
const nsAString& aType);
void Shutdown();
static void GetOrderedVolumeNames(const nsAString& aType,
nsTArray<nsString>& aVolumeNames);
static void GetOrderedVolumeNames(nsTArray<nsString>& aVolumeNames);
static void GetDefaultStorageName(const nsAString& aStorageType,
nsAString &aStorageName);
nsAString& aStorageName);
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 +332,6 @@ private:
bool mIsWatchingFile;
bool mAllowedToWatchFile;
bool mIsDefaultLocation;
void DispatchDefaultChangeEvent();
nsresult Notify(const char* aReason, class DeviceStorageFile* aFile);
@ -360,4 +350,6 @@ private:
nsRefPtr<DeviceStorageFileSystem> mFileSystem;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMDeviceStorage, NS_DOM_DEVICE_STORAGE_CID)
#endif

View File

@ -0,0 +1,833 @@
/* -*- 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]->Equals(aListener)) {
sInstance->mListeners.RemoveElementAt(i);
removed = true;
break;
}
}
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)
: mListener(do_GetWeakReference(static_cast<DOMEventTargetHelper*>(aListener)))
, mOwningThread(NS_GetCurrentThread())
{
}
DeviceStorageStatics::ListenerWrapper::~ListenerWrapper()
{
// Even weak pointers are not thread safe
NS_ProxyRelease(mOwningThread, mListener);
}
bool
DeviceStorageStatics::ListenerWrapper::Equals(nsDOMDeviceStorage* aListener)
{
bool current = false;
mOwningThread->IsOnCurrentThread(&current);
if (current) {
// It is only safe to acquire the reference on the owning thread
nsRefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(mListener);
return listener.get() == aListener;
}
return false;
}
void
DeviceStorageStatics::ListenerWrapper::OnFileWatcherUpdate(const nsCString& aData,
DeviceStorageFile* aFile)
{
nsRefPtr<ListenerWrapper> self = this;
nsCString data = aData;
nsRefPtr<DeviceStorageFile> file = aFile;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, data, file] () -> void {
nsRefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(self->mListener);
if (listener) {
listener->OnFileWatcherUpdate(data, file);
}
});
mOwningThread->Dispatch(r, NS_DISPATCH_NORMAL);
}
void
DeviceStorageStatics::ListenerWrapper::OnDiskSpaceWatcher(bool aLowDiskSpace)
{
nsRefPtr<ListenerWrapper> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aLowDiskSpace] () -> void {
nsRefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(self->mListener);
if (listener) {
listener->OnDiskSpaceWatcher(aLowDiskSpace);
}
});
mOwningThread->Dispatch(r, NS_DISPATCH_NORMAL);
}
void
DeviceStorageStatics::ListenerWrapper::OnWritableNameChanged()
{
nsRefPtr<ListenerWrapper> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void {
nsRefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(self->mListener);
if (listener) {
listener->OnWritableNameChanged();
}
});
mOwningThread->Dispatch(r, NS_DISPATCH_NORMAL);
}
#ifdef MOZ_WIDGET_GONK
void
DeviceStorageStatics::ListenerWrapper::OnVolumeStateChanged(nsIVolume* aVolume)
{
nsRefPtr<ListenerWrapper> self = this;
nsCOMPtr<nsIVolume> volume = aVolume;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, volume] () -> void {
nsRefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(self->mListener);
if (listener) {
listener->OnVolumeStateChanged(volume);
}
});
mOwningThread->Dispatch(r, NS_DISPATCH_NORMAL);
}
#endif
} // namespace devicestorage
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,105 @@
/* -*- 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 final : 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);
bool Equals(nsDOMDeviceStorage* aListener);
void OnFileWatcherUpdate(const nsCString& aData, DeviceStorageFile* aFile);
void OnDiskSpaceWatcher(bool aLowDiskSpace);
void OnWritableNameChanged();
#ifdef MOZ_WIDGET_GONK
void OnVolumeStateChanged(nsIVolume* aVolume);
#endif
private:
virtual ~ListenerWrapper();
nsWeakPtr mListener;
nsCOMPtr<nsIThread> mOwningThread;
};
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,12 @@ NS_IMPL_CYCLE_COLLECTION(DeviceStorageRequest,
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDeviceStorage)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
/* nsISupports is an ambiguous base of nsDOMDeviceStorage
so we have to work around that. */
if ( aIID.Equals(NS_GET_IID(nsDOMDeviceStorage)) )
foundInterface = static_cast<nsISupports*>(static_cast<void*>(this));
else
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper)
@ -3392,20 +2932,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)) {
@ -3467,6 +3003,7 @@ nsDOMDeviceStorage::~nsDOMDeviceStorage()
{
MOZ_ASSERT(NS_IsMainThread());
sInstanceCount--;
DeviceStorageStatics::RemoveListener(this);
}
void
@ -3479,14 +3016,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;
@ -3505,7 +3035,20 @@ void nsDOMDeviceStorage::InvalidateVolumeCaches()
// static
void
nsDOMDeviceStorage::GetOrderedVolumeNames(
nsDOMDeviceStorage::VolumeNameArray &aVolumeNames)
const nsAString& aType,
nsDOMDeviceStorage::VolumeNameArray& aVolumeNames)
{
if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
aVolumeNames.Clear();
return;
}
GetOrderedVolumeNames(aVolumeNames);
}
// static
void
nsDOMDeviceStorage::GetOrderedVolumeNames(
nsDOMDeviceStorage::VolumeNameArray& aVolumeNames)
{
MOZ_ASSERT(NS_IsMainThread());
@ -3554,12 +3097,7 @@ nsDOMDeviceStorage::CreateDeviceStorageFor(nsPIDOMWindow* aWin,
nsDOMDeviceStorage** aStore)
{
nsString storageName;
if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
// The storage name will be the empty string
storageName.Truncate();
} else {
GetDefaultStorageName(aType, storageName);
}
GetDefaultStorageName(aType, storageName);
nsRefPtr<nsDOMDeviceStorage> ds = new nsDOMDeviceStorage(aWin);
if (NS_FAILED(ds->Init(aWin, aType, storageName))) {
@ -3569,37 +3107,6 @@ nsDOMDeviceStorage::CreateDeviceStorageFor(nsPIDOMWindow* aWin,
ds.forget(aStore);
}
// static
void
nsDOMDeviceStorage::CreateDeviceStoragesFor(
nsPIDOMWindow* aWin,
const nsAString &aType,
nsTArray<nsRefPtr<nsDOMDeviceStorage> > &aStores)
{
nsresult rv;
if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
rv = storage->Init(aWin, aType, EmptyString());
if (NS_SUCCEEDED(rv)) {
aStores.AppendElement(storage);
}
return;
}
VolumeNameArray volNames;
GetOrderedVolumeNames(volNames);
VolumeNameArray::size_type numVolumeNames = volNames.Length();
for (VolumeNameArray::index_type i = 0; i < numVolumeNames; i++) {
nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
rv = storage->Init(aWin, aType, volNames[i]);
if (NS_FAILED(rv)) {
break;
}
aStores.AppendElement(storage);
}
}
// static
void
nsDOMDeviceStorage::CreateDeviceStorageByNameAndType(
@ -3628,6 +3135,19 @@ nsDOMDeviceStorage::CreateDeviceStorageByNameAndType(
NS_ADDREF(*aStore = storage.get());
}
bool
nsDOMDeviceStorage::Equals(nsPIDOMWindow* aWin,
const nsAString& aName,
const nsAString& aType)
{
MOZ_ASSERT(aWin);
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
return aWin && aWin == window &&
mStorageName.Equals(aName) &&
mStorageType.Equals(aType);
}
// static
bool
nsDOMDeviceStorage::ParseFullPath(const nsAString& aFullPath,
@ -3723,11 +3243,17 @@ void
nsDOMDeviceStorage::GetDefaultStorageName(const nsAString& aStorageType,
nsAString& aStorageName)
{
// See if the preferred volume is available.
nsAdoptingString prefStorageName =
mozilla::Preferences::GetString(PREF_STORAGE_WRITABLE_NAME);
if (!DeviceStorageTypeChecker::IsVolumeBased(aStorageType)) {
// The storage name will be the empty string
aStorageName.Truncate();
return;
}
if (prefStorageName) {
// See if the preferred volume is available.
nsString prefStorageName;
DeviceStorageStatics::GetWritableName(prefStorageName);
if (!prefStorageName.IsEmpty()) {
nsString status;
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(aStorageType,
prefStorageName);
@ -3746,7 +3272,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 +3859,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 +3870,7 @@ nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
}
void
nsDOMDeviceStorage::DispatchDefaultChangeEvent()
nsDOMDeviceStorage::OnWritableNameChanged()
{
nsAdoptingString DefaultLocation;
GetDefaultStorageName(mStorageType, DefaultLocation);
@ -4422,78 +3948,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

@ -16,6 +16,7 @@
#include "nsDeviceStorage.h"
#include "nsIFile.h"
#include "nsPIDOMWindow.h"
#include "nsGlobalWindow.h"
namespace mozilla {
namespace dom {
@ -23,7 +24,7 @@ namespace dom {
DeviceStorageFileSystem::DeviceStorageFileSystem(
const nsAString& aStorageType,
const nsAString& aStorageName)
: mDeviceStorage(nullptr)
: mWindowId(0)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
@ -76,14 +77,15 @@ DeviceStorageFileSystem::Init(nsDOMDeviceStorage* aDeviceStorage)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aDeviceStorage);
mDeviceStorage = aDeviceStorage;
nsCOMPtr<nsPIDOMWindow> window = aDeviceStorage->GetOwner();
MOZ_ASSERT(window->IsInnerWindow());
mWindowId = window->WindowID();
}
void
DeviceStorageFileSystem::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
mDeviceStorage = nullptr;
mShutdown = true;
}
@ -91,10 +93,9 @@ nsPIDOMWindow*
DeviceStorageFileSystem::GetWindow() const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (!mDeviceStorage) {
return nullptr;
}
return mDeviceStorage->GetOwner();
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
MOZ_ASSERT_IF(!mShutdown, window);
return window;
}
void

View File

@ -48,7 +48,7 @@ private:
nsString mStorageType;
nsString mStorageName;
nsDOMDeviceStorage* mDeviceStorage;
uint64_t mWindowId;
};
} // namespace dom

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

@ -71,7 +71,8 @@ DataCall.prototype = {
ifname: null,
addreses: null,
dnses: null,
gateways: null
gateways: null,
pcscf: null
};
function DataCallInterfaceService() {

View File

@ -960,7 +960,8 @@ function DataCall(aClientId, aApnSetting, aDataCallHandler) {
ifname: null,
addresses: [],
dnses: [],
gateways: []
gateways: [],
pcscf: []
};
this.state = NETWORK_STATE_UNKNOWN;
this.requestedNetworkIfaces = [];
@ -1091,6 +1092,7 @@ DataCall.prototype = {
this.linkInfo.addresses = aDataCall.addresses ? aDataCall.addresses.split(" ") : [];
this.linkInfo.gateways = aDataCall.gateways ? aDataCall.gateways.split(" ") : [];
this.linkInfo.dnses = aDataCall.dnses ? aDataCall.dnses.split(" ") : [];
this.linkInfo.pcscf = aDataCall.pcscf ? aDataCall.pcscf.split(" ") : [];
this.state = this._getGeckoDataCallState(aDataCall);
// Notify DataCallHandler about data call connected.
@ -1143,7 +1145,8 @@ DataCall.prototype = {
ifname: aUpdatedDataCall.ifname,
addresses: aUpdatedDataCall.addresses ? aUpdatedDataCall.addresses.split(" ") : [],
dnses: aUpdatedDataCall.dnses ? aUpdatedDataCall.dnses.split(" ") : [],
gateways: aUpdatedDataCall.gateways ? aUpdatedDataCall.gateways.split(" ") : []
gateways: aUpdatedDataCall.gateways ? aUpdatedDataCall.gateways.split(" ") : [],
pcscf: aUpdatedDataCall.pcscf ? aUpdatedDataCall.pcscf.split(" ") : []
};
switch (dataCallState) {
@ -1169,6 +1172,7 @@ DataCall.prototype = {
this.linkInfo.addresses = newLinkInfo.addresses.slice();
this.linkInfo.gateways = newLinkInfo.gateways.slice();
this.linkInfo.dnses = newLinkInfo.dnses.slice();
this.linkInfo.pcscf = newLinkInfo.pcscf.slice();
}
break;
case NETWORK_STATE_DISCONNECTED:
@ -1267,6 +1271,7 @@ DataCall.prototype = {
this.linkInfo.addresses = [];
this.linkInfo.dnses = [];
this.linkInfo.gateways = [];
this.linkInfo.pcscf = [];
},
reset: function() {
@ -1604,6 +1609,20 @@ RILNetworkInfo.prototype = {
// See http://www.iana.org/assignments/port-numbers
return this.getApnSetting().mmsport || -1;
},
getPcscf: function(aCount) {
if (this.type != NETWORK_TYPE_MOBILE_IMS) {
if (DEBUG) this.debug("Error! Only IMS network can get pcscf.");
throw Cr.NS_ERROR_UNEXPECTED;
}
let linkInfo = this.getDataCall().linkInfo;
if (aCount) {
aCount.value = linkInfo.pcscf.length;
}
return linkInfo.pcscf.slice();
},
};
function RILNetworkInterface(aDataCallHandler, aType, aApnSetting, aDataCall) {

View File

@ -392,7 +392,8 @@ DataCall.prototype = {
ifname: null,
addreses: null,
dnses: null,
gateways: null
gateways: null,
pcscf: null
};
function RadioInterfaceLayer() {

View File

@ -4,7 +4,7 @@
#include "nsISupports.idl"
[scriptable, uuid(d27ce247-9c7c-4582-826b-125e8275e9c2)]
[scriptable, uuid(88f18811-8f19-4902-a9b8-2a6430c71c94)]
interface nsIDataCall : nsISupports
{
/**
@ -55,6 +55,12 @@ interface nsIDataCall : nsISupports
* A space-delimited list of default gateway addresses.
*/
readonly attribute DOMString gateways;
/**
* A space-delimited list of Proxy Call State Control Function addresses for
* IMS client.
*/
readonly attribute DOMString pcscf;
};
[scriptable, uuid(e119c54b-9354-4ad6-a1ee-18608bde9320)]

View File

@ -5,7 +5,7 @@
#include "nsISupports.idl"
#include "nsINetworkInterface.idl"
[scriptable, uuid(501b7041-0754-4ddb-9174-946e2c2ebd83)]
[scriptable, uuid(b8bcd6aa-5b06-4362-a68c-317878429e51)]
interface nsIRilNetworkInfo : nsINetworkInfo
{
readonly attribute unsigned long serviceId;
@ -15,6 +15,17 @@ interface nsIRilNetworkInfo : nsINetworkInfo
readonly attribute DOMString mmsc; // Empty string if not set.
readonly attribute DOMString mmsProxy; // Empty string if not set.
readonly attribute long mmsPort; // -1 if not set.
/**
* Get the list of pcscf addresses, could be IPv4 or IPv6.
*
* @param count
* The length of the list of pcscf addresses.
*
* @returns the list of pcscf addresses.
*/
void getPcscf([optional] out unsigned long count,
[array, size_is(count), retval] out wstring pcscf);
};
[scriptable, function, uuid(cb2f0f5b-67f4-4c14-93e8-01e66b630464)]

View File

@ -4236,12 +4236,11 @@ RilObject.prototype[REQUEST_SETUP_DATA_CALL] = function REQUEST_SETUP_DATA_CALL(
}
let Buf = this.context.Buf;
// Skip version of data call.
Buf.readInt32();
let version = Buf.readInt32();
// Skip number of data calls.
Buf.readInt32();
this.readDataCall_v6(options);
this.readDataCall(options, version);
this.sendChromeMessage(options);
};
RilObject.prototype[REQUEST_SIM_IO] = function REQUEST_SIM_IO(length, options) {
@ -4512,9 +4511,13 @@ RilObject.prototype[REQUEST_LAST_DATA_CALL_FAIL_CAUSE] = null;
* length.
* # dnses - A space-delimited list of DNS server addresses.
* # gateways - A space-delimited list of default gateway addresses.
*
* V10:
* # pcscf - A space-delimited list of Proxy Call State Control Function
* addresses.
*/
RilObject.prototype.readDataCall_v6 = function(options) {
RilObject.prototype.readDataCall = function(options, version) {
if (!options) {
options = {};
}
@ -4529,6 +4532,10 @@ RilObject.prototype.readDataCall_v6 = function(options) {
options.dnses = Buf.readString();
options.gateways = Buf.readString();
if (version >= 10) {
options.pcscf = Buf.readString();
}
return options;
};
@ -4557,7 +4564,7 @@ RilObject.prototype[REQUEST_DATA_CALL_LIST] = function REQUEST_DATA_CALL_LIST(le
let datacalls = [];
for (let i = 0; i < num; i++) {
let datacall;
datacall = this.readDataCall_v6();
datacall = this.readDataCall({}, version);
datacalls.push(datacall);
}

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