Bug 1126694 - Impl of DeviceStorageAreaListener. r=bz, dhylands

This commit is contained in:
Kershaw Chang 2015-05-04 08:11:00 +02:00
parent ad1d14bdde
commit d9082bb833
16 changed files with 401 additions and 7 deletions

View File

@ -32,6 +32,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "BatteryManager.h"
#include "mozilla/dom/DeviceStorageAreaListener.h"
#include "mozilla/dom/PowerManager.h"
#include "mozilla/dom/WakeLock.h"
#include "mozilla/dom/power/PowerManagerService.h"
@ -210,6 +211,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
#ifdef MOZ_EME
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
#endif
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorageAreaListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@ -334,6 +336,10 @@ Navigator::Invalidate()
mMediaKeySystemAccessManager = nullptr;
}
#endif
if (mDeviceStorageAreaListener) {
mDeviceStorageAreaListener = nullptr;
}
}
//*****************************************************************************
@ -914,6 +920,20 @@ Navigator::RegisterProtocolHandler(const nsAString& aProtocol,
mWindow->GetOuterWindow());
}
DeviceStorageAreaListener*
Navigator::GetDeviceStorageAreaListener(ErrorResult& aRv)
{
if (!mDeviceStorageAreaListener) {
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
mDeviceStorageAreaListener = new DeviceStorageAreaListener(mWindow);
}
return mDeviceStorageAreaListener;
}
nsDOMDeviceStorage*
Navigator::GetDeviceStorage(const nsAString& aType, ErrorResult& aRv)
{
@ -949,6 +969,28 @@ Navigator::GetDeviceStorages(const nsAString& aType,
mDeviceStorageStores.AppendElements(aStores);
}
nsDOMDeviceStorage*
Navigator::GetDeviceStorageByNameAndType(const nsAString& aName,
const nsAString& aType,
ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<nsDOMDeviceStorage> storage;
nsDOMDeviceStorage::CreateDeviceStorageByNameAndType(mWindow, aName, aType,
getter_AddRefs(storage));
if (!storage) {
return nullptr;
}
mDeviceStorageStores.AppendElement(storage);
return storage;
}
Geolocation*
Navigator::GetGeolocation(ErrorResult& aRv)
{

View File

@ -94,6 +94,7 @@ class Telephony;
class Voicemail;
class TVManager;
class InputPortManager;
class DeviceStorageAreaListener;
namespace time {
class TimeManager;
@ -214,11 +215,15 @@ public:
void RemoveIdleObserver(MozIdleObserver& aObserver, ErrorResult& aRv);
already_AddRefed<WakeLock> RequestWakeLock(const nsAString &aTopic,
ErrorResult& aRv);
DeviceStorageAreaListener* GetDeviceStorageAreaListener(ErrorResult& aRv);
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);
DesktopNotificationCenter* GetMozNotification(ErrorResult& aRv);
CellBroadcast* GetMozCellBroadcast(ErrorResult& aRv);
IccManager* GetMozIccManager(ErrorResult& aRv);
@ -379,6 +384,7 @@ private:
nsRefPtr<time::TimeManager> mTimeManager;
nsRefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
nsCOMPtr<nsPIDOMWindow> mWindow;
nsRefPtr<DeviceStorageAreaListener> mDeviceStorageAreaListener;
// Hashtable for saving cached objects DoResolve created, so we don't create
// the object twice if asked for it twice, whether due to use of "delete" or

View File

@ -292,6 +292,12 @@ public:
const nsAString& aType,
nsTArray<nsRefPtr<nsDOMDeviceStorage> >& aStores);
static void
CreateDeviceStorageByNameAndType(nsPIDOMWindow* aWin,
const nsAString& aName,
const nsAString& aType,
nsDOMDeviceStorage** aStore);
void Shutdown();
static void GetOrderedVolumeNames(nsTArray<nsString>& aVolumeNames);
@ -334,6 +340,11 @@ private:
already_AddRefed<nsDOMDeviceStorage>
GetStorageByName(const nsAString &aStorageName);
static already_AddRefed<nsDOMDeviceStorage>
GetStorageByNameAndType(nsPIDOMWindow* aWin,
const nsAString& aStorageName,
const nsAString& aType);
nsCOMPtr<nsIPrincipal> mPrincipal;
bool mIsWatchingFile;

View File

@ -0,0 +1,149 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "mozilla/dom/DeviceStorageAreaListener.h"
#include "mozilla/dom/DeviceStorageAreaListenerBinding.h"
#include "mozilla/Attributes.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
#ifdef MOZ_WIDGET_GONK
#include "nsIVolume.h"
#include "nsIVolumeService.h"
#endif
namespace mozilla {
namespace dom {
class VolumeStateObserver final : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
explicit VolumeStateObserver(DeviceStorageAreaListener* aListener)
: mDeviceStorageAreaListener(aListener) {}
void ForgetListener() { mDeviceStorageAreaListener = nullptr; }
private:
~VolumeStateObserver() {};
// This reference is non-owning and it's cleared by
// DeviceStorageAreaListener's destructor.
DeviceStorageAreaListener* MOZ_NON_OWNING_REF mDeviceStorageAreaListener;
};
NS_IMPL_ISUPPORTS(VolumeStateObserver, nsIObserver)
NS_IMETHODIMP
VolumeStateObserver::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
{
if (!mDeviceStorageAreaListener) {
return NS_OK;
}
#ifdef MOZ_WIDGET_GONK
if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
MOZ_ASSERT(vol);
int32_t state;
nsresult rv = vol->GetState(&state);
NS_ENSURE_SUCCESS(rv, rv);
nsString volName;
vol->GetName(volName);
switch (state) {
case nsIVolume::STATE_MOUNTED:
mDeviceStorageAreaListener->DispatchStorageAreaChangedEvent(
volName,
DeviceStorageAreaChangedEventOperation::Added);
break;
default:
mDeviceStorageAreaListener->DispatchStorageAreaChangedEvent(
volName,
DeviceStorageAreaChangedEventOperation::Removed);
break;
}
}
#endif
return NS_OK;
}
NS_IMPL_ADDREF_INHERITED(DeviceStorageAreaListener, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(DeviceStorageAreaListener, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN(DeviceStorageAreaListener)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
DeviceStorageAreaListener::DeviceStorageAreaListener(nsPIDOMWindow* aWindow)
: DOMEventTargetHelper(aWindow)
{
MOZ_ASSERT(aWindow);
mVolumeStateObserver = new VolumeStateObserver(this);
#ifdef MOZ_WIDGET_GONK
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(mVolumeStateObserver, NS_VOLUME_STATE_CHANGED, false);
}
#endif
}
DeviceStorageAreaListener::~DeviceStorageAreaListener()
{
#ifdef MOZ_WIDGET_GONK
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(mVolumeStateObserver, NS_VOLUME_STATE_CHANGED);
}
#endif
mVolumeStateObserver->ForgetListener();
}
JSObject*
DeviceStorageAreaListener::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return DeviceStorageAreaListenerBinding::Wrap(aCx, this, aGivenProto);
}
void
DeviceStorageAreaListener::DispatchStorageAreaChangedEvent(
const nsString& aStorageName,
DeviceStorageAreaChangedEventOperation aOperation)
{
StateMapType::const_iterator iter = mStorageAreaStateMap.find(aStorageName);
if (iter == mStorageAreaStateMap.end() &&
aOperation != DeviceStorageAreaChangedEventOperation::Added) {
// The operation of the first event to dispatch should be "Added".
return;
}
if (iter != mStorageAreaStateMap.end() &&
iter->second == aOperation) {
// No need to disptach the event if the state is unchanged.
return;
}
DeviceStorageAreaChangedEventInit init;
init.mOperation = aOperation;
init.mStorageName = aStorageName;
nsRefPtr<DeviceStorageAreaChangedEvent> event =
DeviceStorageAreaChangedEvent::Constructor(this,
NS_LITERAL_STRING("storageareachanged"),
init);
event->SetTrusted(true);
bool ignore;
DOMEventTargetHelper::DispatchEvent(event, &ignore);
mStorageAreaStateMap[aStorageName] = aOperation;
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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_DeviceStorageAreaListener_h
#define mozilla_dom_DeviceStorageAreaListener_h
#include <map>
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/DeviceStorageAreaChangedEvent.h"
namespace mozilla {
namespace dom {
class VolumeStateObserver;
class DeviceStorageAreaListener final : public DOMEventTargetHelper
{
public:
NS_DECL_ISUPPORTS_INHERITED
IMPL_EVENT_HANDLER(storageareachanged)
explicit DeviceStorageAreaListener(nsPIDOMWindow* aWindow);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
private:
friend class VolumeStateObserver;
typedef std::map<nsString, DeviceStorageAreaChangedEventOperation> StateMapType;
StateMapType mStorageAreaStateMap;
nsRefPtr<VolumeStateObserver> mVolumeStateObserver;
~DeviceStorageAreaListener();
void DispatchStorageAreaChangedEvent(
const nsString& aStorageName,
DeviceStorageAreaChangedEventOperation aOperation);
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_DeviceStorageAreaListener_h

View File

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

View File

@ -3579,6 +3579,30 @@ nsDOMDeviceStorage::CreateDeviceStoragesFor(
}
}
// static
void
nsDOMDeviceStorage::CreateDeviceStorageByNameAndType(
nsPIDOMWindow* aWin,
const nsAString& aName,
const nsAString& aType,
nsDOMDeviceStorage** aStore)
{
if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
if (NS_FAILED(storage->Init(aWin, aType, EmptyString()))) {
*aStore = nullptr;
return;
}
NS_ADDREF(*aStore = storage.get());
return;
}
nsRefPtr<nsDOMDeviceStorage> storage = GetStorageByNameAndType(aWin,
aName,
aType);
NS_ADDREF(*aStore = storage.get());
}
// static
bool
nsDOMDeviceStorage::ParseFullPath(const nsAString& aFullPath,
@ -3638,14 +3662,28 @@ nsDOMDeviceStorage::GetStorageByName(const nsAString& aStorageName)
ds = this;
return ds.forget();
}
return GetStorageByNameAndType(GetOwner(), aStorageName, mStorageType);
}
// static
already_AddRefed<nsDOMDeviceStorage>
nsDOMDeviceStorage::GetStorageByNameAndType(nsPIDOMWindow* aWin,
const nsAString& aStorageName,
const nsAString& aType)
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<nsDOMDeviceStorage> ds;
VolumeNameArray volNames;
GetOrderedVolumeNames(volNames);
VolumeNameArray::size_type numVolumes = volNames.Length();
VolumeNameArray::index_type i;
for (i = 0; i < numVolumes; i++) {
if (volNames[i].Equals(aStorageName)) {
ds = new nsDOMDeviceStorage(GetOwner());
nsresult rv = ds->Init(GetOwner(), mStorageType, aStorageName);
ds = new nsDOMDeviceStorage(aWin);
nsresult rv = ds->Init(aWin, aType, aStorageName);
if (NS_FAILED(rv)) {
return nullptr;
}

View File

@ -2361,6 +2361,21 @@ ContentChild::RecvFileSystemUpdate(const nsString& aFsName,
return true;
}
bool
ContentChild::RecvVolumeRemoved(const nsString& aFsName)
{
#ifdef MOZ_WIDGET_GONK
nsRefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton();
if (vs) {
vs->RemoveVolumeByName(aFsName);
}
#else
// Remove warnings about unused arguments
unused << aFsName;
#endif
return true;
}
bool
ContentChild::RecvNotifyProcessPriorityChanged(
const hal::ProcessPriority& aPriority)

View File

@ -360,6 +360,7 @@ public:
const bool& aIsUnmounting,
const bool& aIsRemovable,
const bool& aIsHotSwappable) override;
virtual bool RecvVolumeRemoved(const nsString& aFsName) override;
virtual bool RecvNuwaFork() override;

View File

@ -654,6 +654,7 @@ static const char* sObserverTopics[] = {
"file-watcher-update",
#ifdef MOZ_WIDGET_GONK
NS_VOLUME_STATE_CHANGED,
NS_VOLUME_REMOVED,
"phone-state-changed",
#endif
#ifdef ACCESSIBILITY
@ -3166,6 +3167,15 @@ ContentParent::Observe(nsISupports* aSubject,
nsString state(aData);
unused << SendNotifyPhoneStateChange(state);
}
else if(!strcmp(aTopic, NS_VOLUME_REMOVED)) {
#ifdef MOZ_NUWA_PROCESS
if (!(IsNuwaReady() && IsNuwaProcess()))
#endif
{
nsString volName(aData);
unused << SendVolumeRemoved(volName);
}
}
#endif
#ifdef ACCESSIBILITY
// Make sure accessibility is running in content process when accessibility
@ -4561,6 +4571,22 @@ ContentParent::RecvSetFakeVolumeState(const nsString& fsName, const int32_t& fsS
#endif
}
bool
ContentParent::RecvRemoveFakeVolume(const nsString& fsName)
{
#ifdef MOZ_WIDGET_GONK
nsresult rv;
nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
if (vs) {
vs->RemoveFakeVolume(fsName);
}
return true;
#else
NS_WARNING("ContentParent::RecvRemoveFakeVolume shouldn't be called when MOZ_WIDGET_GONK is not defined");
return false;
#endif
}
bool
ContentParent::RecvKeywordToURI(const nsCString& aKeyword,
nsString* aProviderName,

View File

@ -787,6 +787,8 @@ private:
virtual bool RecvSetFakeVolumeState(const nsString& fsName, const int32_t& fsState) override;
virtual bool RecvRemoveFakeVolume(const nsString& fsName) override;
virtual bool RecvKeywordToURI(const nsCString& aKeyword,
nsString* aProviderName,
OptionalInputStreamParams* aPostData,

View File

@ -574,6 +574,9 @@ child:
bool isSharing, bool isFormatting, bool isFake,
bool isUnmounting, bool isRemovable, bool isHotSwappable);
// Notify volume is removed.
VolumeRemoved(nsString fsName);
// Ask the Nuwa process to create a new child process.
NuwaFork();
@ -898,6 +901,7 @@ parent:
// called by the child (test code only) to propagate volume changes to the parent
async CreateFakeVolume(nsString fsName, nsString mountPoint);
async SetFakeVolumeState(nsString fsName, int32_t fsState);
async RemoveFakeVolume(nsString fsName);
sync KeywordToURI(nsCString keyword)
returns (nsString providerName, OptionalInputStreamParams postData, OptionalURIParams uri);

View File

@ -101,6 +101,7 @@ interface nsIVolume : nsISupports
%{C++
// For use with the ObserverService
#define NS_VOLUME_STATE_CHANGED "volume-state-changed"
#define NS_VOLUME_REMOVED "volume-removed"
namespace mozilla {
namespace system {

View File

@ -8,7 +8,7 @@
interface nsIArray;
[scriptable, uuid(879874c6-5532-437a-bf76-703d0c2e7e77)]
[scriptable, uuid(cfbf9880-cba5-11e4-8830-0800200c9a66)]
interface nsIVolumeService : nsISupports
{
nsIVolume getVolumeByName(in DOMString volName);
@ -24,6 +24,9 @@ interface nsIVolumeService : nsISupports
/* for test case only to simulate sdcard insertion/removal */
void createFakeVolume(in DOMString name, in DOMString path);
void SetFakeVolumeState(in DOMString name, in long state);
/* for test case only to test removal of storage area */
void removeFakeVolume(in DOMString name);
};
%{C++

View File

@ -439,7 +439,7 @@ NS_IMETHODIMP
nsVolumeService::CreateFakeVolume(const nsAString& name, const nsAString& path)
{
if (XRE_GetProcessType() == GeckoProcessType_Default) {
nsRefPtr<nsVolume> vol = new nsVolume(name, path, nsIVolume::STATE_INIT,
nsRefPtr<nsVolume> vol = new nsVolume(name, path, nsIVolume::STATE_MOUNTED,
-1 /* mountGeneration */,
true /* isMediaPresent */,
false /* isSharing */,
@ -469,9 +469,15 @@ nsVolumeService::SetFakeVolumeState(const nsAString& name, int32_t state)
if (!vol || !vol->IsFake()) {
return NS_ERROR_NOT_AVAILABLE;
}
vol->SetState(state);
vol->LogState();
UpdateVolume(vol.get());
// UpdateVolume expects the volume passed in to NOT be the
// same pointer as what CreateOrFindVolumeByName would return,
// which is why we allocate a temporary volume here.
nsRefPtr<nsVolume> volume = new nsVolume(name);
volume->Set(vol);
volume->SetState(state);
volume->LogState();
UpdateVolume(volume.get());
return NS_OK;
}
@ -479,6 +485,41 @@ nsVolumeService::SetFakeVolumeState(const nsAString& name, int32_t state)
return NS_OK;
}
NS_IMETHODIMP
nsVolumeService::RemoveFakeVolume(const nsAString& name)
{
if (XRE_GetProcessType() == GeckoProcessType_Default) {
SetFakeVolumeState(name, nsIVolume::STATE_NOMEDIA);
RemoveVolumeByName(name);
return NS_OK;
}
ContentChild::GetSingleton()->SendRemoveFakeVolume(nsString(name));
return NS_OK;
}
void
nsVolumeService::RemoveVolumeByName(const nsAString& aName)
{
nsRefPtr<nsVolume> vol;
{
MonitorAutoLock autoLock(mArrayMonitor);
vol = FindVolumeByName(aName);
}
if (!vol) {
return;
}
mVolumeArray.RemoveElement(vol);
if (XRE_GetProcessType() == GeckoProcessType_Default) {
nsCOMPtr<nsIObserverService> obs = GetObserverService();
if (!obs) {
return;
}
obs->NotifyObservers(nullptr, NS_VOLUME_REMOVED, nsString(aName).get());
}
}
/***************************************************************************
* The UpdateVolumeRunnable creates an nsVolume and updates the main thread
* data structure while running on the main thread.

View File

@ -46,12 +46,15 @@ public:
void DumpNoLock(const char* aLabel);
// To use this function, you have to create a new volume and pass it in.
void UpdateVolume(nsIVolume* aVolume, bool aNotifyObservers = true);
void UpdateVolumeIOThread(const Volume* aVolume);
void RecvVolumesFromParent(const nsTArray<dom::VolumeInfo>& aVolumes);
void GetVolumesForIPC(nsTArray<dom::VolumeInfo>* aResult);
void RemoveVolumeByName(const nsAString& aName);
private:
~nsVolumeService();