author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Mon, 02 Dec 2013 12:46:24 +0100 | |
changeset 158260 | 2581b84e0ca168be129a1fadab08fbbd17c23ab4 |
parent 158206 | 8a5221ecaa6779cb15b9aa5719e3bcd7dc2632ef (current diff) |
parent 158259 | d884dfe02381890e96e13fc9343253d5c02818d1 (diff) |
child 158283 | 691b5f265c9a1cc3f398686173fca43295ee1386 |
child 158320 | 2be8f1640e213b315ea99d833e74b1f71bb1d702 |
child 158361 | 8601e55efa523d9b7e2b394121e186ea6555d5d7 |
push id | 25739 |
push user | cbook@mozilla.com |
push date | Mon, 02 Dec 2013 11:47:10 +0000 |
treeherder | mozilla-central@2581b84e0ca1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 28.0a1 |
first release with | nightly linux32
2581b84e0ca1
/
28.0a1
/
20131202092621
/
files
nightly linux64
2581b84e0ca1
/
28.0a1
/
20131202092621
/
files
nightly mac
2581b84e0ca1
/
28.0a1
/
20131202092621
/
files
nightly win32
2581b84e0ca1
/
28.0a1
/
20131202092621
/
files
nightly win64
2581b84e0ca1
/
28.0a1
/
20131202092621
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
28.0a1
/
20131202092621
/
pushlog to previous
nightly linux64
28.0a1
/
20131202092621
/
pushlog to previous
nightly mac
28.0a1
/
20131202092621
/
pushlog to previous
nightly win32
28.0a1
/
20131202092621
/
pushlog to previous
nightly win64
28.0a1
/
20131202092621
/
pushlog to previous
|
--- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "121f70034b7ef01836aa345f8ff37b61b96ac88e", + "revision": "cf23b263b4bbf1024210350f24c7e6eb5872a4be", "repo_path": "/integration/gaia-central" }
--- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -679,16 +679,17 @@ GK_ATOM(ondblclick, "ondblclick") GK_ATOM(ondeliverysuccess, "ondeliverysuccess") GK_ATOM(ondeliveryerror, "ondeliveryerror") GK_ATOM(ondevicefound, "ondevicefound") GK_ATOM(ondialing, "ondialing") GK_ATOM(ondisabled, "ondisabled") GK_ATOM(ondischargingtimechange, "ondischargingtimechange") GK_ATOM(ondisconnected, "ondisconnected") GK_ATOM(ondisconnecting, "ondisconnecting") +GK_ATOM(ondiscoverystatechanged, "ondiscoverystatechanged") GK_ATOM(ondownloading, "ondownloading") GK_ATOM(onDOMActivate, "onDOMActivate") GK_ATOM(onDOMAttrModified, "onDOMAttrModified") GK_ATOM(onDOMCharacterDataModified, "onDOMCharacterDataModified") GK_ATOM(onDOMFocusIn, "onDOMFocusIn") GK_ATOM(onDOMFocusOut, "onDOMFocusOut") GK_ATOM(onDOMMouseScroll, "onDOMMouseScroll") GK_ATOM(onDOMNodeInserted, "onDOMNodeInserted")
--- a/content/events/test/test_all_synthetic_events.html +++ b/content/events/test/test_all_synthetic_events.html @@ -43,16 +43,20 @@ const kEventConstructors = { BlobEvent: { create: function (aName, aProps) { return new BlobEvent(aName, aProps); }, }, BluetoothDeviceEvent: { create: function (aName, aProps) { return new BluetoothDeviceEvent(aName, aProps); }, }, + BluetoothDiscoveryStateChangedEvent: { create: function (aName, aProps) { + return new BluetoothDiscoveryStateChangedEvent(aName, aProps); + }, + }, BluetoothStatusChangedEvent: { create: function (aName, aProps) { return new BluetoothStatusChangedEvent(aName, aProps); }, }, CallEvent: { create: function (aName, aProps) { return new CallEvent(aName, aProps); }, },
--- a/dom/bluetooth/BluetoothAdapter.cpp +++ b/dom/bluetooth/BluetoothAdapter.cpp @@ -4,16 +4,17 @@ * 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 "base/basictypes.h" #include "GeneratedEvents.h" #include "nsCxPusher.h" #include "nsDOMClassInfo.h" #include "nsIDOMBluetoothDeviceEvent.h" +#include "nsIDOMBluetoothDiscoveryStateChangedEvent.h" #include "nsIDOMBluetoothStatusChangedEvent.h" #include "nsTArrayHelpers.h" #include "DOMRequest.h" #include "nsThreadUtils.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" #include "mozilla/dom/BluetoothAdapterBinding.h" #include "mozilla/dom/ContentChild.h" @@ -315,16 +316,29 @@ BluetoothAdapter::Notify(const Bluetooth DispatchTrustedEvent(event); } else if (aData.name().EqualsLiteral("PropertyChanged")) { MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue); const InfallibleTArray<BluetoothNamedValue>& arr = v.get_ArrayOfBluetoothNamedValue(); MOZ_ASSERT(arr.Length() == 1); SetPropertyByValue(arr[0]); + } else if (aData.name().EqualsLiteral(DISCOVERY_STATE_CHANGED_ID)) { + MOZ_ASSERT(v.type() == BluetoothValue::Tbool); + bool isDiscovering = v.get_bool(); + + nsCOMPtr<nsIDOMEvent> event; + NS_NewDOMBluetoothDiscoveryStateChangedEvent( + getter_AddRefs(event), this, nullptr, nullptr); + + nsCOMPtr<nsIDOMBluetoothDiscoveryStateChangedEvent> e = + do_QueryInterface(event); + e->InitBluetoothDiscoveryStateChangedEvent(aData.name(), false, false, + isDiscovering); + DispatchTrustedEvent(event); } else if (aData.name().EqualsLiteral(PAIRED_STATUS_CHANGED_ID) || aData.name().EqualsLiteral(HFP_STATUS_CHANGED_ID) || aData.name().EqualsLiteral(SCO_STATUS_CHANGED_ID) || aData.name().EqualsLiteral(A2DP_STATUS_CHANGED_ID)) { MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue); const InfallibleTArray<BluetoothNamedValue>& arr = v.get_ArrayOfBluetoothNamedValue();
--- a/dom/bluetooth/BluetoothAdapter.h +++ b/dom/bluetooth/BluetoothAdapter.h @@ -142,16 +142,17 @@ public: already_AddRefed<DOMRequest> ToggleCalls(ErrorResult& aRv); already_AddRefed<DOMRequest> SendMediaMetaData(const MediaMetaData& aMediaMetaData, ErrorResult& aRv); already_AddRefed<DOMRequest> SendMediaPlayStatus(const MediaPlayStatus& aMediaPlayStatus, ErrorResult& aRv); IMPL_EVENT_HANDLER(devicefound); + IMPL_EVENT_HANDLER(discoverystatechanged); IMPL_EVENT_HANDLER(a2dpstatuschanged); IMPL_EVENT_HANDLER(hfpstatuschanged); IMPL_EVENT_HANDLER(pairedstatuschanged); IMPL_EVENT_HANDLER(requestmediaplaystatus); IMPL_EVENT_HANDLER(scostatuschanged); nsPIDOMWindow* GetParentObject() const {
--- a/dom/bluetooth/BluetoothCommon.h +++ b/dom/bluetooth/BluetoothCommon.h @@ -84,16 +84,21 @@ extern bool gBluetoothDebugFlag; #define SCO_STATUS_CHANGED_ID "scostatuschanged" /** * When the pair status of a Bluetooth device is changed, we'll dispatch an * event. */ #define PAIRED_STATUS_CHANGED_ID "pairedstatuschanged" + /** + * This event would be fired when discovery procedure starts or stops. + */ +#define DISCOVERY_STATE_CHANGED_ID "discoverystatechanged" + /** * When receiving a query about current play status from remote device, we'll * dispatch an event. */ #define REQUEST_MEDIA_PLAYSTATUS_ID "requestmediaplaystatus" // Bluetooth address format: xx:xx:xx:xx:xx:xx (or xx_xx_xx_xx_xx_xx) #define BLUETOOTH_ADDRESS_LENGTH 17
--- a/dom/bluetooth/BluetoothProfileController.cpp +++ b/dom/bluetooth/BluetoothProfileController.cpp @@ -158,16 +158,17 @@ BluetoothProfileController::SetupProfile } void BluetoothProfileController::Start() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mDeviceAddress.IsEmpty()); MOZ_ASSERT(mProfilesIndex == -1); + NS_ENSURE_TRUE_VOID(mProfiles.Length() > 0); ++mProfilesIndex; BT_LOGR_PROFILE(mProfiles[mProfilesIndex], ""); if (mConnect) { mProfiles[mProfilesIndex]->Connect(mDeviceAddress, this); } else { mProfiles[mProfilesIndex]->Disconnect(this);
--- a/dom/bluetooth/BluetoothProfileController.h +++ b/dom/bluetooth/BluetoothProfileController.h @@ -35,18 +35,18 @@ BEGIN_BLUETOOTH_NAMESPACE #define GET_MINOR_DEVICE_CLASS(cod) ((cod & 0xfc) >> 2) // Bit 21: Major service class = 0x100, Audio #define HAS_AUDIO(cod) (cod & 0x200000) // Bit 18: Major service class = 0x20, Rendering #define HAS_RENDERING(cod) (cod & 0x40000) -// Major device class = 0xA, Peripheral -#define IS_PERIPHERAL(cod) (GET_MAJOR_DEVICE_CLASS(cod) == 0xa) +// Major device class = 0x5, Peripheral +#define IS_PERIPHERAL(cod) (GET_MAJOR_DEVICE_CLASS(cod) == 0x5) class BluetoothProfileManagerBase; class BluetoothReplyRunnable; typedef void (*BluetoothProfileControllerCallback)(); class BluetoothProfileController : public RefCounted<BluetoothProfileController> { public:
--- a/dom/bluetooth/bluedroid/gonk/BluetoothServiceBluedroid.cpp +++ b/dom/bluetooth/bluedroid/gonk/BluetoothServiceBluedroid.cpp @@ -44,17 +44,16 @@ static bool sAdapterDiscoverable = false static bool sIsBtEnabled = false; static nsString sAdapterBdAddress; static nsString sAdapterBdName; static uint32_t sAdapterDiscoverableTimeout; static InfallibleTArray<nsString> sAdapterBondedAddressArray; static InfallibleTArray<BluetoothNamedValue> sRemoteDevicesPack; static nsTArray<nsRefPtr<BluetoothProfileController> > sControllerArray; static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sBondingRunnableArray; -static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeDiscoveryRunnableArray; static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sGetDeviceRunnableArray; static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sSetPropertyRunnableArray; static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sUnbondingRunnableArray; static nsTArray<int> sRequestedDeviceCountArray; static StaticAutoPtr<Monitor> sToggleBtMonitor; /** * Classes only used in this file @@ -491,22 +490,26 @@ DeviceFoundCallback(int aNumProperties, } } static void DiscoveryStateChangedCallback(bt_discovery_state_t aState) { MOZ_ASSERT(!NS_IsMainThread()); - if (!sChangeDiscoveryRunnableArray.IsEmpty()) { - BluetoothValue values(true); - DispatchBluetoothReply(sChangeDiscoveryRunnableArray[0], - values, EmptyString()); + bool isDiscovering = (aState == BT_DISCOVERY_STARTED); - sChangeDiscoveryRunnableArray.RemoveElementAt(0); + BluetoothSignal signal(NS_LITERAL_STRING(DISCOVERY_STATE_CHANGED_ID), + NS_LITERAL_STRING(KEY_ADAPTER), + isDiscovering); + + nsRefPtr<DistributeBluetoothSignalTask> + t = new DistributeBluetoothSignalTask(signal); + if (NS_FAILED(NS_DispatchToMainThread(t))) { + BT_WARNING("Failed to dispatch to main thread!"); } } static void PinRequestCallback(bt_bdaddr_t* aRemoteBdAddress, bt_bdname_t* aRemoteBdName, uint32_t aRemoteClass) { MOZ_ASSERT(!NS_IsMainThread()); @@ -913,27 +916,27 @@ nsresult BluetoothServiceBluedroid::StartDiscoveryInternal( BluetoothReplyRunnable* aRunnable) { MOZ_ASSERT(NS_IsMainThread()); if (!IsReady()) { NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); - return NS_OK; } + int ret = sBtInterface->start_discovery(); if (ret != BT_STATUS_SUCCESS) { ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("StartDiscovery")); - - return NS_OK; + return NS_ERROR_FAILURE; } - sChangeDiscoveryRunnableArray.AppendElement(aRunnable); + DispatchBluetoothReply(aRunnable, true, EmptyString()); + return NS_OK; } nsresult BluetoothServiceBluedroid::StopDiscoveryInternal( BluetoothReplyRunnable* aRunnable) { MOZ_ASSERT(NS_IsMainThread()); @@ -942,20 +945,20 @@ BluetoothServiceBluedroid::StopDiscovery NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); return NS_OK; } int ret = sBtInterface->cancel_discovery(); if (ret != BT_STATUS_SUCCESS) { ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("StopDiscovery")); - return NS_OK; + return NS_ERROR_FAILURE; } - sChangeDiscoveryRunnableArray.AppendElement(aRunnable); + DispatchBluetoothReply(aRunnable, true, EmptyString()); return NS_OK; } nsresult BluetoothServiceBluedroid::GetDevicePropertiesInternal( const BluetoothSignal& aSignal) {
--- a/dom/bluetooth/bluez/linux/BluetoothDBusService.cpp +++ b/dom/bluetooth/bluez/linux/BluetoothDBusService.cpp @@ -1507,16 +1507,30 @@ EventFilter(DBusConnection* aConn, DBusM } } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "PropertyChanged")) { ParsePropertyChange(aMsg, v, errorStr, sAdapterProperties, ArrayLength(sAdapterProperties)); + + BluetoothNamedValue& property = v.get_ArrayOfBluetoothNamedValue()[0]; + if (property.name().EqualsLiteral("Discovering")) { + bool isDiscovering = property.value(); + BluetoothSignal signal(NS_LITERAL_STRING(DISCOVERY_STATE_CHANGED_ID), + NS_LITERAL_STRING(KEY_ADAPTER), + isDiscovering); + + nsRefPtr<DistributeBluetoothSignalTask> + t = new DistributeBluetoothSignalTask(signal); + if (NS_FAILED(NS_DispatchToMainThread(t))) { + BT_WARNING("Failed to dispatch to main thread!"); + } + } } else if (dbus_message_is_signal(aMsg, DBUS_DEVICE_IFACE, "PropertyChanged")) { ParsePropertyChange(aMsg, v, errorStr, sDeviceProperties, ArrayLength(sDeviceProperties));
--- a/dom/bluetooth/interfaces/moz.build +++ b/dom/bluetooth/interfaces/moz.build @@ -3,12 +3,13 @@ # 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/. if CONFIG['MOZ_B2G_BT']: XPIDL_SOURCES += [ 'nsIDOMBluetoothDevice.idl', 'nsIDOMBluetoothDeviceEvent.idl', + 'nsIDOMBluetoothDiscoveryStateChangedEvent.idl', 'nsIDOMBluetoothStatusChangedEvent.idl', ] XPIDL_MODULE = 'dom_bluetooth'
new file mode 100644 --- /dev/null +++ b/dom/bluetooth/interfaces/nsIDOMBluetoothDiscoveryStateChangedEvent.idl @@ -0,0 +1,22 @@ +/* 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 "nsIDOMEvent.idl" + +[scriptable, builtinclass, uuid(9de639cb-71c4-4144-8462-09763ec87c20)] +interface nsIDOMBluetoothDiscoveryStateChangedEvent : nsIDOMEvent +{ + readonly attribute boolean discovering; + + [noscript] + void initBluetoothDiscoveryStateChangedEvent(in DOMString aType, + in boolean aCanBubble, + in boolean aCancelable, + in boolean aDiscovering); +}; + +dictionary BluetoothDiscoveryStateChangedEventInit : EventInit +{ + bool discovering; +};
--- a/dom/devicestorage/DeviceStorage.h +++ b/dom/devicestorage/DeviceStorage.h @@ -90,16 +90,17 @@ public: void collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> >& aFiles, PRTime aSince, nsAString& aRootPath); void AccumDiskUsage(uint64_t* aPicturesSoFar, uint64_t* aVideosSoFar, uint64_t* aMusicSoFar, uint64_t* aTotalSoFar); void GetDiskFreeSpace(int64_t* aSoFar); void GetStatus(nsAString& aStatus); + void DoFormat(nsAString& aStatus); static void GetRootDirectoryForType(const nsAString& aStorageType, const nsAString& aStorageName, nsIFile** aFile); nsresult CalculateSizeAndModifiedDate(); nsresult CalculateMimeType(); private: @@ -232,16 +233,17 @@ public: } already_AddRefed<DOMCursor> EnumerateEditable(const nsAString& aPath, const EnumerationParameters& aOptions, ErrorResult& aRv); already_AddRefed<DOMRequest> FreeSpace(ErrorResult& aRv); already_AddRefed<DOMRequest> UsedSpace(ErrorResult& aRv); already_AddRefed<DOMRequest> Available(ErrorResult& aRv); + already_AddRefed<DOMRequest> Format(ErrorResult& aRv); bool Default(); // Uses XPCOM GetStorageName static void CreateDeviceStorageFor(nsPIDOMWindow* aWin, const nsAString& aType,
--- a/dom/devicestorage/DeviceStorageRequestChild.cpp +++ b/dom/devicestorage/DeviceStorageRequestChild.cpp @@ -98,16 +98,26 @@ DeviceStorageRequestChild:: AvailableStorageResponse r = aValue; AutoJSContext cx; JS::Rooted<JS::Value> result( cx, StringToJsval(mRequest->GetOwner(), r.mountState())); mRequest->FireSuccess(result); break; } + case DeviceStorageResponseValue::TFormatStorageResponse: + { + FormatStorageResponse r = aValue; + AutoJSContext cx; + JS::Rooted<JS::Value> result( + cx, StringToJsval(mRequest->GetOwner(), r.mountState())); + mRequest->FireSuccess(result); + break; + } + case DeviceStorageResponseValue::TEnumerationResponse: { EnumerationResponse r = aValue; nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get()); uint32_t count = r.paths().Length(); for (uint32_t i = 0; i < count; i++) {
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp +++ b/dom/devicestorage/DeviceStorageRequestParent.cpp @@ -126,16 +126,28 @@ DeviceStorageRequestParent::Dispatch() nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.storageName()); nsRefPtr<PostAvailableResultEvent> r = new PostAvailableResultEvent(this, dsf); NS_DispatchToMainThread(r); break; } + case DeviceStorageParams::TDeviceStorageFormatParams: + { + DeviceStorageFormatParams p = mParams; + + nsRefPtr<DeviceStorageFile> dsf = + new DeviceStorageFile(p.type(), p.storageName()); + nsRefPtr<PostFormatResultEvent> r + = new PostFormatResultEvent(this, dsf); + NS_DispatchToMainThread(r); + break; + } + case DeviceStorageParams::TDeviceStorageEnumerationParams: { DeviceStorageEnumerationParams p = mParams; nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.storageName(), p.rootdir(), NS_LITERAL_STRING("")); nsRefPtr<CancelableRunnable> r = new EnumerateFileEvent(this, dsf, p.since()); @@ -210,16 +222,24 @@ DeviceStorageRequestParent::EnsureRequir case DeviceStorageParams::TDeviceStorageAvailableParams: { DeviceStorageAvailableParams p = mParams; type = p.type(); requestType = DEVICE_STORAGE_REQUEST_AVAILABLE; break; } + case DeviceStorageParams::TDeviceStorageFormatParams: + { + DeviceStorageFormatParams p = mParams; + type = p.type(); + requestType = DEVICE_STORAGE_REQUEST_FORMAT; + break; + } + case DeviceStorageParams::TDeviceStorageEnumerationParams: { DeviceStorageEnumerationParams p = mParams; type = p.type(); requestType = DEVICE_STORAGE_REQUEST_READ; break; } @@ -721,11 +741,39 @@ DeviceStorageRequestParent::PostAvailabl mFile->GetStatus(state); } AvailableStorageResponse response(state); unused << mParent->Send__delete__(mParent, response); return NS_OK; } +DeviceStorageRequestParent::PostFormatResultEvent:: + PostFormatResultEvent(DeviceStorageRequestParent* aParent, + DeviceStorageFile* aFile) + : CancelableRunnable(aParent) + , mFile(aFile) +{ +} + +DeviceStorageRequestParent::PostFormatResultEvent:: + ~PostFormatResultEvent() +{ +} + +nsresult +DeviceStorageRequestParent::PostFormatResultEvent::CancelableRun() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + nsString state = NS_LITERAL_STRING("unavailable"); + if (mFile) { + mFile->DoFormat(state); + } + + FormatStorageResponse response(state); + unused << mParent->Send__delete__(mParent, response); + return NS_OK; +} + } // namespace devicestorage } // namespace dom } // namespace mozilla
--- a/dom/devicestorage/DeviceStorageRequestParent.h +++ b/dom/devicestorage/DeviceStorageRequestParent.h @@ -222,16 +222,26 @@ private: public: PostAvailableResultEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile); virtual ~PostAvailableResultEvent(); virtual nsresult CancelableRun(); private: nsRefPtr<DeviceStorageFile> mFile; }; + class PostFormatResultEvent : public CancelableRunnable + { + public: + PostFormatResultEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile); + virtual ~PostFormatResultEvent(); + virtual nsresult CancelableRun(); + private: + nsRefPtr<DeviceStorageFile> mFile; + }; + protected: bool AddRunnable(CancelableRunnable* aRunnable) { MutexAutoLock lock(mMutex); if (mActorDestoryed) return false; mRunnables.AppendElement(aRunnable); return true;
--- a/dom/devicestorage/PDeviceStorageRequest.ipdl +++ b/dom/devicestorage/PDeviceStorageRequest.ipdl @@ -48,25 +48,31 @@ struct UsedSpaceStorageResponse uint64_t usedBytes; }; struct AvailableStorageResponse { nsString mountState; }; +struct FormatStorageResponse +{ + nsString mountState; +}; + union DeviceStorageResponseValue { ErrorResponse; SuccessResponse; BlobResponse; EnumerationResponse; FreeSpaceStorageResponse; UsedSpaceStorageResponse; AvailableStorageResponse; + FormatStorageResponse; }; sync protocol PDeviceStorageRequest { manager PContent; child: __delete__(DeviceStorageResponseValue response); };
--- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -385,16 +385,17 @@ DeviceStorageTypeChecker::GetAccessForRe case DEVICE_STORAGE_REQUEST_WATCH: case DEVICE_STORAGE_REQUEST_FREE_SPACE: case DEVICE_STORAGE_REQUEST_USED_SPACE: case DEVICE_STORAGE_REQUEST_AVAILABLE: aAccessResult.AssignLiteral("read"); break; case DEVICE_STORAGE_REQUEST_WRITE: case DEVICE_STORAGE_REQUEST_DELETE: + case DEVICE_STORAGE_REQUEST_FORMAT: aAccessResult.AssignLiteral("write"); break; case DEVICE_STORAGE_REQUEST_CREATE: aAccessResult.AssignLiteral("create"); break; default: aAccessResult.AssignLiteral("undefined"); } @@ -1275,16 +1276,46 @@ bool DeviceStorageFile::IsAvailable() { nsString status; GetStatus(status); return status.EqualsLiteral("available"); } void +DeviceStorageFile::DoFormat(nsAString& aStatus) +{ + DeviceStorageTypeChecker* typeChecker + = DeviceStorageTypeChecker::CreateOrGet(); + if (!typeChecker) { + return; + } + if (!typeChecker->IsVolumeBased(mStorageType)) { + aStatus.AssignLiteral("notVolume"); + return; + } +#ifdef MOZ_WIDGET_GONK + nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); + NS_ENSURE_TRUE_VOID(vs); + + nsCOMPtr<nsIVolume> vol; + nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol)); + NS_ENSURE_SUCCESS_VOID(rv); + if (!vol) { + return; + } + + vol->Format(); + + aStatus.AssignLiteral("formatting"); +#endif + return; +} + +void DeviceStorageFile::GetStatus(nsAString& aStatus) { DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet(); if (!typeChecker) { return; } if (!typeChecker->IsVolumeBased(mStorageType)) { @@ -1311,16 +1342,23 @@ DeviceStorageFile::GetStatus(nsAString& } bool isSharing; rv = vol->GetIsSharing(&isSharing); NS_ENSURE_SUCCESS_VOID(rv); if (isSharing) { aStatus.AssignLiteral("shared"); return; } + bool isFormatting; + rv = vol->GetIsFormatting(&isFormatting); + NS_ENSURE_SUCCESS_VOID(rv); + if (isFormatting) { + aStatus.AssignLiteral("unavailable"); + return; + } int32_t volState; rv = vol->GetState(&volState); NS_ENSURE_SUCCESS_VOID(rv); if (volState == nsIVolume::STATE_MOUNTED) { aStatus.AssignLiteral("available"); } #endif } @@ -1843,16 +1881,49 @@ public: return NS_OK; } private: nsRefPtr<DeviceStorageFile> mFile; nsRefPtr<DOMRequest> mRequest; }; +class PostFormatResultEvent : public nsRunnable +{ +public: + PostFormatResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest) + : mFile(aFile) + , mRequest(aRequest) + { + } + + ~PostFormatResultEvent() {} + + NS_IMETHOD Run() + { + MOZ_ASSERT(NS_IsMainThread()); + + nsString state = NS_LITERAL_STRING("unavailable"); + if (mFile) { + mFile->DoFormat(state); + } + + AutoJSContext cx; + JS::Rooted<JS::Value> result(cx, + StringToJsval(mRequest->GetOwner(), state)); + mRequest->FireSuccess(result); + mRequest = nullptr; + return NS_OK; + } + +private: + nsRefPtr<DeviceStorageFile> mFile; + nsRefPtr<DOMRequest> mRequest; +}; + class PostResultEvent : public nsRunnable { public: PostResultEvent(already_AddRefed<DOMRequest> aRequest, DeviceStorageFile* aFile) : mFile(aFile) , mRequest(aRequest) {} @@ -2418,16 +2489,33 @@ public: return NS_OK; } case DEVICE_STORAGE_REQUEST_WATCH: { mDeviceStorage->mAllowedToWatchFile = true; return NS_OK; } + + case DEVICE_STORAGE_REQUEST_FORMAT: + { + if (XRE_GetProcessType() != GeckoProcessType_Default) { + PDeviceStorageRequestChild* child + = new DeviceStorageRequestChild(mRequest, mFile); + DeviceStorageFormatParams params(mFile->mStorageType, + mFile->mStorageName); + ContentChild::GetSingleton() + ->SendPDeviceStorageRequestConstructor(child, params); + return NS_OK; + } + r = new PostFormatResultEvent(mFile, mRequest); + NS_DispatchToMainThread(r); + return NS_OK; + } + } if (r) { nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); NS_ASSERTION(target, "Must have stream transport service"); target->Dispatch(r, NS_DISPATCH_NORMAL); } @@ -3078,16 +3166,36 @@ nsDOMDeviceStorage::Available(ErrorResul mStorageName); nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_AVAILABLE, win, mPrincipal, dsf, request); NS_DispatchToMainThread(r); return request.forget(); } +already_AddRefed<DOMRequest> +nsDOMDeviceStorage::Format(ErrorResult& aRv) +{ + nsCOMPtr<nsPIDOMWindow> win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + nsRefPtr<DOMRequest> request = new DOMRequest(win); + + nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, + mStorageName); + nsCOMPtr<nsIRunnable> r + = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FORMAT, + win, mPrincipal, dsf, request); + NS_DispatchToMainThread(r); + return request.forget(); +} + NS_IMETHODIMP nsDOMDeviceStorage::GetRootDirectoryForFile(const nsAString& aName, nsIFile** aRootDirectory) { nsRefPtr<nsDOMDeviceStorage> ds; if (IsFullPath(aName)) { nsString storagePath;
--- a/dom/devicestorage/nsDeviceStorage.h +++ b/dom/devicestorage/nsDeviceStorage.h @@ -46,17 +46,18 @@ class ErrorResult; enum DeviceStorageRequestType { DEVICE_STORAGE_REQUEST_READ, DEVICE_STORAGE_REQUEST_WRITE, DEVICE_STORAGE_REQUEST_CREATE, DEVICE_STORAGE_REQUEST_DELETE, DEVICE_STORAGE_REQUEST_WATCH, DEVICE_STORAGE_REQUEST_FREE_SPACE, DEVICE_STORAGE_REQUEST_USED_SPACE, - DEVICE_STORAGE_REQUEST_AVAILABLE + DEVICE_STORAGE_REQUEST_AVAILABLE, + DEVICE_STORAGE_REQUEST_FORMAT }; class DeviceStorageUsedSpaceCache MOZ_FINAL { public: static DeviceStorageUsedSpaceCache* CreateOrGet(); DeviceStorageUsedSpaceCache();
--- a/dom/icc/interfaces/SimToolKit.idl +++ b/dom/icc/interfaces/SimToolKit.idl @@ -111,16 +111,27 @@ dictionary MozStkMenu * Help information available or not. * * @see TS 11.14, clause 12.6, Command Qualifier, SET UP MENU, bit 8. * * true: help information available. * false: no help information available. */ boolean isHelpAvailable; + + /** + * List of Next Action Indicators. + * Each element should be one of nsIDOMMozIccManager.STK_CMD_* + * or nsIDOMMozIccManager.STK_NEXT_ACTION_* + * If it's STK_NEXT_ACTION_NULL, the terminal should ignore this action + * in corresponding item. + * + * @see TS 11.14, clause 12.24, Items Next Action Indicator. + */ + jsval nextActionList; // unsigned short [] }; dictionary MozStkInput { /** * Text for the ME to display in conjunction with asking the user to respond. */ DOMString text;
--- a/dom/icc/interfaces/nsIDOMIccManager.idl +++ b/dom/icc/interfaces/nsIDOMIccManager.idl @@ -1,17 +1,17 @@ /* 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 "nsIDOMEventTarget.idl" interface nsIDOMMozIcc; -[scriptable, builtinclass, uuid(23067d6f-e0cb-4f34-8648-77c2b25a11f5)] +[scriptable, builtinclass, uuid(67e40e8e-35ee-40a4-a5b8-414588675133)] interface nsIDOMMozIccManager : nsIDOMEventTarget { /** * STK menu presentation types. */ const unsigned short STK_MENU_TYPE_NOT_SPECIFIED = 0x00; const unsigned short STK_MENU_TYPE_DATA_VALUES = 0x01; const unsigned short STK_MENU_TYPE_NAVIGATION_OPTIONS = 0x03; @@ -42,20 +42,20 @@ interface nsIDOMMozIccManager : nsIDOMEv const unsigned short STK_CMD_DISPLAY_TEXT = 0x21; const unsigned short STK_CMD_GET_INKEY = 0x22; const unsigned short STK_CMD_GET_INPUT = 0x23; const unsigned short STK_CMD_SELECT_ITEM = 0x24; const unsigned short STK_CMD_SET_UP_MENU = 0x25; const unsigned short STK_CMD_PROVIDE_LOCAL_INFO = 0x26; const unsigned short STK_CMD_TIMER_MANAGEMENT = 0x27; const unsigned short STK_CMD_SET_UP_IDLE_MODE_TEXT = 0x28; - const unsigned short STK_CMD_OPEN_CHANNEL = 0x30; - const unsigned short STK_CMD_CLOSE_CHANNEL = 0x31; - const unsigned short STK_CMD_RECEIVE_DATA = 0x32; - const unsigned short STK_CMD_SEND_DATA = 0x33; + const unsigned short STK_CMD_OPEN_CHANNEL = 0x40; + const unsigned short STK_CMD_CLOSE_CHANNEL = 0x41; + const unsigned short STK_CMD_RECEIVE_DATA = 0x42; + const unsigned short STK_CMD_SEND_DATA = 0x43; /** * STK result code. * * @see TS 11.14, clause 12.12 * * Results '0X' and '1X' indicate that the command has been performed. */ @@ -218,16 +218,22 @@ interface nsIDOMMozIccManager : nsIDOMEv /** * Browser termination cause. */ const unsigned short STK_BROWSER_TERMINATION_CAUSE_USER = 0x00; const unsigned short STK_BROWSER_TERMINATION_CAUSE_ERROR = 0x01; /** + * Next Action Indicator. + */ + const unsigned short STK_NEXT_ACTION_NULL = 0x00; + const unsigned short STK_NEXT_ACTION_END_PROACTIVE_SESSION = 0x81; + + /** * Array of iccIds that are currently detected. */ readonly attribute jsval iccIds; // DOMString[] /** * Get ICC object by iccId. * * @param iccId
--- a/dom/icc/tests/marionette/manifest.ini +++ b/dom/icc/tests/marionette/manifest.ini @@ -17,12 +17,13 @@ qemu = true [test_stk_send_dtmf.js] [test_stk_launch_browser.js] [test_stk_display_text.js] [test_stk_get_inkey.js] [test_stk_get_input.js] [test_stk_select_item.js] [test_stk_setup_menu.js] [test_stk_setup_idle_mode_text.js] +[test_stk_bip_command.js] [test_icc_access_invalid_object.js] disabled = Bug 933654 [test_icc_detected_undetected_event.js] disabled = Bug 933654
new file mode 100644 --- /dev/null +++ b/dom/icc/tests/marionette/test_stk_bip_command.js @@ -0,0 +1,48 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_HEAD_JS = "stk_helper.js"; + +function testBipCommand(command, expect) { + log("STK CMD " + JSON.stringify(command)); + + is(command.typeOfCommand, expect.typeOfCommand, expect.name); + is(command.options.text, expect.text, expect.name); + + runNextTest(); +} + +let tests = [ + {command: "d04b81030140018202818205074f70656e204944350702030403041f0239020578470a065465737447700272730d08f4557365724c6f670d08f4557365725077643c0301ad9c3e052101010101", + func: testBipCommand, + expect: {name: "open_channel_1", + typeOfCommand: iccManager.STK_CMD_OPEN_CHANNEL, + text: "Open ID"}}, + {command: "d0448103014001820281820500350702030403041f0239020578470a065465737447700272730d08f4557365724c6f670d08f4557365725077643c0301ad9c3e052101010101", + func: testBipCommand, + expect: {name: "open_channel_2", + typeOfCommand: iccManager.STK_CMD_OPEN_CHANNEL, + text: ""}}, + {command: "d05381030140018202818205094f70656e2049442031350702030403041f0239020578470a065465737447700272730d08f4557365724c6f670d08f4557365725077643c0301ad9c3e052101010101d004000900b4", + func: testBipCommand, + expect: {name: "open_channel_3", + typeOfCommand: iccManager.STK_CMD_OPEN_CHANNEL, + text: "Open ID 1"}}, + {command: "d01b810301410082028121850a436c6f73652049442031d004000a00b4", + func: testBipCommand, + expect: {name: "close_channel_1", + typeOfCommand: iccManager.STK_CMD_CLOSE_CHANNEL, + text: "Close ID 1"}}, + {command: "d022810301420082028121850e5265636569766520446174612031b701c8d004000e00b4", + func: testBipCommand, + expect: {name: "receive_data_1", + typeOfCommand: iccManager.STK_CMD_RECEIVE_DATA, + text: "Receive Data 1"}}, + {command: "d026810301430182028121850b53656e6420446174612031b6080001020304050607d004000b00b4", + func: testBipCommand, + expect: {name: "send_data_1", + typeOfCommand: iccManager.STK_CMD_SEND_DATA, + text: "Send Data 1"}}, +]; + +runNextTest();
--- a/dom/icc/tests/marionette/test_stk_select_item.js +++ b/dom/icc/tests/marionette/test_stk_select_item.js @@ -7,16 +7,20 @@ function testSelectItem(command, expect) log("STK CMD " + JSON.stringify(command)); is(command.typeOfCommand, iccManager.STK_CMD_SELECT_ITEM, expect.name); is(command.commandQualifier, expect.commandQualifier, expect.name); is(command.options.title, expect.title, expect.name); for (let index in command.options.items) { is(command.options.items[index].identifier, expect.items[index].identifier, expect.name); is(command.options.items[index].text, expect.items[index].text, expect.name); } + let length = command.options.nextActionList ? command.options.nextActionList.length : 0; + for (let i = 0; i < length; i++) { + is(command.options.nextActionList[i], expect.nextActionList[i], expect.name); + } runNextTest(); } let tests = [ {command: "d03d810301240082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d20338f07044974656d2034", func: testSelectItem, expect: {name: "select_item_cmd_1", @@ -53,17 +57,18 @@ let tests = [ commandQualifier: 0x00, title: "0LargeMenu", items: [{identifier: 255, text: "1 Call Forward Unconditional"}, {identifier: 254, text: "2 Call Forward On User Busy"}, {identifier: 253, text: "3 Call Forward On No Reply"}, {identifier: 252, text: "4 Call Forward On User Not Reachable"}, {identifier: 251, text: "5 Barring Of All Outgoing Calls"}, {identifier: 250, text: "6 Barring Of All Outgoing Int Calls"}, {identifier: 249, text: "7 CLI Presentation"}]}}, {command: "d039810301240082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d20331803131026", func: testSelectItem, expect: {name: "select_item_cmd_7", commandQualifier: 0x00, title: "Toolkit Select", - items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, + items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}], + nextActionList: [iccManager.STK_CMD_SEND_SMS, iccManager.STK_CMD_SET_UP_CALL, iccManager.STK_CMD_PROVIDE_LOCAL_INFO]}}, {command: "d037810301240082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d2033900102", func: testSelectItem, expect: {name: "select_item_cmd_8", commandQualifier: 0x00, title: "Toolkit Select", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d034810301248082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d2033", func: testSelectItem, @@ -299,12 +304,19 @@ let tests = [ commandQualifier: 0x00, title: "81ル0", items: [{identifier: 1, text: "81ル1"}, {identifier: 2, text: "81ル2"}, {identifier: 3, text: "81ル3"}]}}, {command: "d0348103012400820281828508820430a03832cb308f0901820430a03832cb318f0902820430a03832cb328f0903820430a03832cb33", func: testSelectItem, expect: {name: "select_item_cmd_48", commandQualifier: 0x00, title: "82ル0", - items: [{identifier: 1, text: "82ル1"}, {identifier: 2, text: "82ル2"}, {identifier: 3, text: "82ル3"}]}} + items: [{identifier: 1, text: "82ル1"}, {identifier: 2, text: "82ル2"}, {identifier: 3, text: "82ル3"}]}}, + {command: "d039810301240082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d20331803000081", + func: testSelectItem, + expect: {name: "select_item_cmd_49", + commandQualifier: 0x00, + title: "Toolkit Select", + items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}], + nextActionList: [iccManager.STK_NEXT_ACTION_NULL, iccManager.STK_NEXT_ACTION_NULL, iccManager.STK_NEXT_ACTION_END_PROACTIVE_SESSION]}}, ]; runNextTest();
--- a/dom/icc/tests/marionette/test_stk_setup_menu.js +++ b/dom/icc/tests/marionette/test_stk_setup_menu.js @@ -7,16 +7,20 @@ function testSetupMenu(command, expect) log("STK CMD " + JSON.stringify(command)); is(command.typeOfCommand, iccManager.STK_CMD_SET_UP_MENU, expect.name); is(command.commandQualifier, expect.commandQualifier, expect.name); is(command.options.title, expect.title, expect.name); for (let index in command.options.items) { is(command.options.items[index].identifier, expect.items[index].identifier, expect.name); is(command.options.items[index].text, expect.items[index].text, expect.name); } + let length = command.options.nextActionList ? command.options.nextActionList.length : 0; + for (let i = 0; i < length; i++) { + is(command.options.nextActionList[i], expect.nextActionList[i], expect.name); + } runNextTest(); } function isFirstMenuItemNull(command) { return (command.options.items.length == 1 && !(command.options.items[0])); } @@ -72,17 +76,18 @@ let tests = [ commandQualifier: 0x80, title: "Toolkit Menu", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}]}}, {command: "d041810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20338f07044974656d2034180413101526", func: testSetupMenu, expect: {name: "setup_menu_cmd_7", commandQualifier: 0x00, title: "Toolkit Menu", - items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}]}}, + items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}], + nextActionList: [iccManager.STK_CMD_SEND_SMS, iccManager.STK_CMD_SET_UP_CALL, iccManager.STK_CMD_LAUNCH_BROWSER, iccManager.STK_CMD_PROVIDE_LOCAL_INFO]}}, {command: "d03c810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20339e0201019f0401050505", func: testSetupMenu, expect: {name: "setup_menu_cmd_8", commandQualifier: 0x00, title: "Toolkit Menu", items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}}, {command: "d03c810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20339e0200019f0400050505", func: testSetupMenu, @@ -217,16 +222,23 @@ let tests = [ title: "80ル0", items: [{identifier: 1, text: "80ル1"}, {identifier: 2, text: "80ル2"}, {identifier: 3, text: "80ル3"}, {identifier: 4, text: "80ル4"}]}}, {command: "d02c8103012500820281828509800038003030eb00308f0a11800038003030eb00358f0a12800038003030eb0036", func: testSetupMenu, expect: {name: "setup_menu_cmd_31", commandQualifier: 0x00, title: "80ル0", items: [{identifier: 17, text: "80ル5"}, {identifier: 18, text: "80ル6"}]}}, + {command: "d041810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20338f07044974656d2034180481000000", + func: testSetupMenu, + expect: {name: "setup_menu_cmd_32", + commandQualifier: 0x00, + title: "Toolkit Menu", + items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}], + nextActionList: [iccManager.STK_NEXT_ACTION_END_PROACTIVE_SESSION, iccManager.STK_NEXT_ACTION_NULL, iccManager.STK_NEXT_ACTION_NULL, iccManager.STK_NEXT_ACTION_NULL]}}, {command: "D00D81030125008202818285008F00", func: testRemoveSetupMenu}, {command:"D03B810301250082028182850C546F6F6C6B6974204D656E758F07014974656D20318F07024974656D20328F07034974656D20338F07044974656D2034", func: testInitialSetupMenu}, ]; runNextTest();
--- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -74,16 +74,22 @@ struct DeviceStorageUsedSpaceParams }; struct DeviceStorageAvailableParams { nsString type; nsString storageName; }; +struct DeviceStorageFormatParams +{ + nsString type; + nsString storageName; +}; + struct DeviceStorageAddParams { nsString type; nsString storageName; nsString relpath; PBlob blob; }; @@ -114,16 +120,17 @@ union DeviceStorageParams { DeviceStorageAddParams; DeviceStorageGetParams; DeviceStorageDeleteParams; DeviceStorageEnumerationParams; DeviceStorageFreeSpaceParams; DeviceStorageUsedSpaceParams; DeviceStorageAvailableParams; + DeviceStorageFormatParams; }; struct FMRadioRequestEnableParams { double frequency; }; struct FMRadioRequestDisableParams
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -549,21 +549,16 @@ TabChild::HandlePossibleViewportChange() CSSSize viewport(viewportInfo.GetSize()); // We're not being displayed in any way; don't bother doing anything because // that will just confuse future adjustments. if (!screenW || !screenH) { return; } - // Make sure the viewport height is not shorter than the window when the page - // is zoomed out to show its full width. Note that before we set the viewport - // width, the "full width" of the page isn't properly defined, so that's why - // we have to call SetCSSViewport twice - once to set the width, and the - // second time to figure out the height based on the layout at that width. float oldBrowserWidth = mOldViewportWidth; mLastMetrics.mViewport.SizeTo(viewport); if (!oldBrowserWidth) { oldBrowserWidth = kDefaultViewportSize.width; } SetCSSViewport(viewport); // If this page has not been painted yet, then this must be getting run @@ -601,23 +596,16 @@ TabChild::HandlePossibleViewportChange() // For non-HTML content (e.g. SVG), just assume page size == viewport size. pageSize = viewport; } if (!pageSize.width) { // Return early rather than divide by 0. return; } - CSSToScreenScale minScale(mInnerSize.width / pageSize.width); - minScale = clamped(minScale, viewportInfo.GetMinZoom(), viewportInfo.GetMaxZoom()); - NS_ENSURE_TRUE_VOID(minScale.scale); // (return early rather than divide by 0) - - viewport.height = std::max(viewport.height, screenH / minScale.scale); - SetCSSViewport(viewport); - float oldScreenWidth = mLastMetrics.mCompositionBounds.width; if (!oldScreenWidth) { oldScreenWidth = mInnerSize.width; } FrameMetrics metrics(mLastMetrics); metrics.mViewport = CSSRect(CSSPoint(), viewport); metrics.mScrollableRect = CSSRect(CSSPoint(), pageSize);
--- a/dom/media/bridge/MediaModule.cpp +++ b/dom/media/bridge/MediaModule.cpp @@ -11,32 +11,41 @@ #include "PeerConnectionImpl.h" #define PEERCONNECTION_CID \ {0xb93af7a1, 0x3411, 0x44a8, {0xbd, 0x0a, 0x8a, 0xf3, 0xdd, 0xe4, 0xd8, 0xd8}} #define PEERCONNECTION_CONTRACTID "@mozilla.org/peerconnection;1" +#include "stun_udp_socket_filter.h" + +NS_DEFINE_NAMED_CID(NS_STUN_UDP_SOCKET_FILTER_HANDLER_CID) + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsStunUDPSocketFilterHandler) + + namespace sipcc { // Factory defined in sipcc::, defines sipcc::PeerConnectionImplConstructor NS_GENERIC_FACTORY_CONSTRUCTOR(PeerConnectionImpl) } // Defines kPEERCONNECTION_CID NS_DEFINE_NAMED_CID(PEERCONNECTION_CID); static const mozilla::Module::CIDEntry kCIDs[] = { { &kPEERCONNECTION_CID, false, nullptr, sipcc::PeerConnectionImplConstructor }, + { &kNS_STUN_UDP_SOCKET_FILTER_HANDLER_CID, false, nullptr, nsStunUDPSocketFilterHandlerConstructor }, { nullptr } }; static const mozilla::Module::ContractIDEntry kContracts[] = { { PEERCONNECTION_CONTRACTID, &kPEERCONNECTION_CID }, + { NS_STUN_UDP_SOCKET_FILTER_HANDLER_CONTRACTID, &kNS_STUN_UDP_SOCKET_FILTER_HANDLER_CID }, { nullptr } }; static const mozilla::Module kModule = { mozilla::Module::kVersion, kCIDs, kContracts };
--- a/dom/network/interfaces/nsIUDPSocketChild.idl +++ b/dom/network/interfaces/nsIUDPSocketChild.idl @@ -17,16 +17,18 @@ union NetAddr; native NetAddr(mozilla::net::NetAddr); [ptr] native NetAddrPtr(mozilla::net::NetAddr); [scriptable, uuid(B47E5A0F-D384-48EF-8885-4259793D9CF0)] interface nsIUDPSocketChild : nsISupports { readonly attribute unsigned short localPort; readonly attribute AUTF8String localAddress; + attribute AUTF8String filterName; + // Tell the chrome process to bind the UDP socket to a given local host and port void bind(in nsIUDPSocketInternal socket, in AUTF8String host, in unsigned short port); // Tell the chrome process to perform equivalent operations to all following methods void send(in AUTF8String host, in unsigned short port, [const, array, size_is(byteLength)] in uint8_t bytes, in unsigned long byteLength); // Send without DNS query
--- a/dom/network/src/UDPSocketChild.cpp +++ b/dom/network/src/UDPSocketChild.cpp @@ -62,17 +62,17 @@ UDPSocketChild::Bind(nsIUDPSocketInterna const nsACString& aHost, uint16_t aPort) { NS_ENSURE_ARG(aSocket); mSocket = aSocket; AddIPDLReference(); - gNeckoChild->SendPUDPSocketConstructor(this, nsCString(aHost), aPort); + gNeckoChild->SendPUDPSocketConstructor(this, nsCString(aHost), aPort, mFilterName); return NS_OK; } NS_IMETHODIMP UDPSocketChild::Close() { SendClose(); @@ -144,16 +144,34 @@ UDPSocketChild::GetLocalPort(uint16_t *a NS_IMETHODIMP UDPSocketChild::GetLocalAddress(nsACString &aLocalAddress) { aLocalAddress = mLocalAddress; return NS_OK; } +NS_IMETHODIMP +UDPSocketChild::SetFilterName(const nsACString &aFilterName) +{ + if (!mFilterName.IsEmpty()) { + // filter name can only be set once. + return NS_ERROR_FAILURE; + } + mFilterName = aFilterName; + return NS_OK; +} + +NS_IMETHODIMP +UDPSocketChild::GetFilterName(nsACString &aFilterName) +{ + aFilterName = mFilterName; + return NS_OK; +} + // PUDPSocketChild Methods bool UDPSocketChild::RecvCallback(const nsCString &aType, const UDPCallbackData &aData, const nsCString &aState) { if (NS_FAILED(mSocket->UpdateReadyState(aState))) NS_ERROR("Shouldn't fail!");
--- a/dom/network/src/UDPSocketChild.h +++ b/dom/network/src/UDPSocketChild.h @@ -41,14 +41,15 @@ public: virtual ~UDPSocketChild(); virtual bool RecvCallback(const nsCString& aType, const UDPCallbackData& aData, const nsCString& aState) MOZ_OVERRIDE; private: uint16_t mLocalPort; nsCString mLocalAddress; + nsCString mFilterName; }; } // namespace dom } // namespace mozilla #endif // !defined(mozilla_dom_UDPSocketChild_h__)
--- a/dom/network/src/UDPSocketParent.cpp +++ b/dom/network/src/UDPSocketParent.cpp @@ -1,12 +1,15 @@ +/* -*- 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 "nsIServiceManager.h" #include "UDPSocketParent.h" #include "nsComponentManagerUtils.h" #include "nsIUDPSocket.h" #include "nsINetAddr.h" #include "mozilla/unused.h" #include "mozilla/net/DNS.h" namespace mozilla { @@ -60,16 +63,17 @@ UDPSocketParent::~UDPSocketParent() } // PUDPSocketParent methods bool UDPSocketParent::Init(const nsCString &aHost, const uint16_t aPort) { nsresult rv; + NS_ASSERTION(mFilter, "No packet filter"); nsCOMPtr<nsIUDPSocket> sock = do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv); if (NS_FAILED(rv)) { FireInternalError(this, __LINE__); return true; } @@ -119,37 +123,56 @@ UDPSocketParent::Init(const nsCString &a } bool UDPSocketParent::RecvData(const InfallibleTArray<uint8_t> &aData, const nsCString& aRemoteAddress, const uint16_t& aPort) { NS_ENSURE_TRUE(mSocket, true); + NS_ASSERTION(mFilter, "No packet filter"); + // TODO, Bug 933102, filter packets that are sent with hostname. + // Until then we simply throw away packets that are sent to a hostname. + return true; + +#if 0 + // Enable this once we have filtering working with hostname delivery. uint32_t count; nsresult rv = mSocket->Send(aRemoteAddress, aPort, aData.Elements(), aData.Length(), &count); mozilla::unused << PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("onsent"), UDPSendResult(rv), NS_LITERAL_CSTRING("connected")); NS_ENSURE_SUCCESS(rv, true); NS_ENSURE_TRUE(count > 0, true); return true; +#endif } bool UDPSocketParent::RecvDataWithAddress(const InfallibleTArray<uint8_t>& aData, const mozilla::net::NetAddr& aAddr) { NS_ENSURE_TRUE(mSocket, true); + NS_ASSERTION(mFilter, "No packet filter"); + uint32_t count; - nsresult rv = mSocket->SendWithAddress(&aAddr, aData.Elements(), - aData.Length(), &count); + nsresult rv; + bool allowed; + rv = mFilter->FilterPacket(&aAddr, aData.Elements(), + aData.Length(), nsIUDPSocketFilter::SF_OUTGOING, + &allowed); + // Sending unallowed data, kill content. + NS_ENSURE_SUCCESS(rv, false); + NS_ENSURE_TRUE(allowed, false); + + rv = mSocket->SendWithAddress(&aAddr, aData.Elements(), + aData.Length(), &count); mozilla::unused << PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("onsent"), UDPSendResult(rv), NS_LITERAL_CSTRING("connected")); NS_ENSURE_SUCCESS(rv, true); NS_ENSURE_TRUE(count > 0, true); return true; } @@ -186,30 +209,42 @@ UDPSocketParent::ActorDestroy(ActorDestr NS_IMETHODIMP UDPSocketParent::OnPacketReceived(nsIUDPSocket* aSocket, nsIUDPMessage* aMessage) { // receiving packet from remote host, forward the message content to child process if (!mIPCOpen) { return NS_OK; } + NS_ASSERTION(mFilter, "No packet filter"); uint16_t port; nsCString ip; nsCOMPtr<nsINetAddr> fromAddr; aMessage->GetFromAddr(getter_AddRefs(fromAddr)); fromAddr->GetPort(&port); fromAddr->GetAddress(ip); nsCString data; aMessage->GetData(data); const char* buffer = data.get(); uint32_t len = data.Length(); + bool allowed; + mozilla::net::NetAddr addr; + fromAddr->GetNetAddr(&addr); + nsresult rv = mFilter->FilterPacket(&addr, + (const uint8_t*)buffer, len, + nsIUDPSocketFilter::SF_INCOMING, + &allowed); + // Receiving unallowed data, drop. + NS_ENSURE_SUCCESS(rv, NS_OK); + NS_ENSURE_TRUE(allowed, NS_OK); + FallibleTArray<uint8_t> fallibleArray; if (!fallibleArray.InsertElementsAt(0, buffer, len)) { FireInternalError(this, __LINE__); return NS_ERROR_OUT_OF_MEMORY; } InfallibleTArray<uint8_t> infallibleArray; infallibleArray.SwapElements(fallibleArray);
--- a/dom/network/src/UDPSocketParent.h +++ b/dom/network/src/UDPSocketParent.h @@ -1,30 +1,36 @@ +/* -*- 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_UDPSocketParent_h__ #define mozilla_dom_UDPSocketParent_h__ #include "mozilla/net/PUDPSocketParent.h" #include "nsCOMPtr.h" #include "nsIUDPSocket.h" +#include "nsIUDPSocketFilter.h" namespace mozilla { namespace dom { class UDPSocketParent : public mozilla::net::PUDPSocketParent , public nsIUDPSocketListener { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIUDPSOCKETLISTENER - UDPSocketParent() : mIPCOpen(true) {} + UDPSocketParent(nsIUDPSocketFilter* filter) : + mIPCOpen(true), + mFilter(filter) {} + virtual ~UDPSocketParent(); bool Init(const nsCString& aHost, const uint16_t aPort); virtual bool RecvClose() MOZ_OVERRIDE; virtual bool RecvData(const InfallibleTArray<uint8_t>& aData, const nsCString& aRemoteAddress, const uint16_t& aPort) MOZ_OVERRIDE; @@ -32,14 +38,15 @@ public: const mozilla::net::NetAddr& addr); virtual bool RecvRequestDelete() MOZ_OVERRIDE; private: virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; bool mIPCOpen; nsCOMPtr<nsIUDPSocket> mSocket; + nsCOMPtr<nsIUDPSocketFilter> mFilter; }; } // namespace dom } // namespace mozilla #endif // !defined(mozilla_dom_UDPSocketParent_h__)
deleted file mode 100644 --- a/dom/network/tests/unit_ipc/test_udpsocket_ipc.js +++ /dev/null @@ -1,9 +0,0 @@ -Components.utils.import("resource://gre/modules/Services.jsm"); - -function run_test() { - Services.prefs.setBoolPref('media.peerconnection.ipc.enabled', true); - run_test_in_child("/udpsocket_child.js", function() { - Services.prefs.clearUserPref('media.peerconnection.ipc.enabled'); - do_test_finished(); - }); -}
deleted file mode 100644 --- a/dom/network/tests/unit_ipc/udpsocket_child.js +++ /dev/null @@ -1,164 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ -'use strict'; - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); -Cu.import('resource://gre/modules/Services.jsm'); - -const SERVER_PORT = 12345; -const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0]; - -function UDPSocketInternalImpl() { -} - -UDPSocketInternalImpl.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIUDPSocketInternal]), - callListenerError: function(type, message, filename, lineNumber, columnNumber) { - if (this.onerror) { - this.onerror(); - } else { - do_throw('Received unexpected error: ' + message + ' at ' + filename + - ':' + lineNumber + ':' + columnNumber); - } - }, - callListenerReceivedData: function(type, host, port, data, dataLength) { - do_print('*** recv data(' + dataLength + ')=' + data.join() + '\n'); - if (this.ondata) { - try { - this.ondata(data, dataLength); - } catch(ex) { - if (ex === Cr.NS_ERROR_ABORT) - throw ex; - do_print('Caught exception: ' + ex + '\n' + ex.stack); - do_throw('test is broken; bad ondata handler; see above'); - } - } else { - do_throw('Received ' + dataLength + ' bytes of unexpected data!'); - } - }, - callListenerVoid: function(type) { - switch (type) { - case 'onopen': - if (this.onopen) { - this.onopen(); - } - break; - case 'onclose': - if (this.onclose) { - this.onclose(); - } - break; - } - }, - callListenerSent: function(type, value) { - if (value != Cr.NS_OK) { - do_throw('Previous send was failed with cause: ' + value); - } - }, - updateReadyState: function(readyState) { - do_print('*** current state: ' + readyState + '\n'); - }, - onopen: function() {}, - onclose: function() {}, -}; - -function makeSuccessCase(name) { - return function() { - do_print('got expected: ' + name); - run_next_test(); - }; -} - -function makeJointSuccess(names) { - let funcs = {}, successCount = 0; - names.forEach(function(name) { - funcs[name] = function() { - do_print('got excepted: ' + name); - if (++successCount === names.length) - run_next_test(); - }; - }); - return funcs; -} - -function makeExpectedData(expectedData, callback) { - return function(receivedData, receivedDataLength) { - if (receivedDataLength != expectedData.length) { - do_throw('Received data size mismatched, expected ' + expectedData.length + - ' but got ' + receivedDataLength); - } - for (let i = 0; i < receivedDataLength; i++) { - if (receivedData[i] != expectedData[i]) { - do_throw('Received mismatched data at position ' + i); - } - } - if (callback) { - callback(); - } else { - run_next_test(); - } - }; -} - -function makeFailureCase(name) { - return function() { - let argstr; - if (arguments.length) { - argstr = '(args: ' + - Array.map(arguments, function(x) {return x.data + ""; }).join(" ") + ')'; - } else { - argstr = '(no arguments)'; - } - do_throw('got unexpected: ' + name + ' ' + argstr); - }; -} - -function createSocketChild() { - return Cc['@mozilla.org/udp-socket-child;1'] - .createInstance(Ci.nsIUDPSocketChild); -} - -var UDPSocket = createSocketChild(); -var callback = new UDPSocketInternalImpl(); - -function connectSock() { - UDPSocket.bind(callback, '127.0.0.1', SERVER_PORT); - callback.onopen = makeSuccessCase('open'); -} - -function sendData() { - UDPSocket.send('127.0.0.1', SERVER_PORT, DATA_ARRAY, DATA_ARRAY.length); - callback.ondata = makeExpectedData(DATA_ARRAY); -} - -function clientClose() { - UDPSocket.close(); - callback.ondata = makeFailureCase('data'); - callback.onclose = makeSuccessCase('close'); -} - -function connectError() { - UDPSocket = createSocketChild(); - UDPSocket.bind(callback, 'some non-IP string', SERVER_PORT); - callback.onerror = makeSuccessCase('error'); - callback.onopen = makeFailureCase('open'); -} - -function cleanup() { - UDPSocket = null; - run_next_test(); -} - -add_test(connectSock); -add_test(sendData); -add_test(clientClose); -add_test(connectError); -add_test(cleanup); - -function run_test() { - run_next_test(); -}
--- a/dom/network/tests/unit_ipc/xpcshell.ini +++ b/dom/network/tests/unit_ipc/xpcshell.ini @@ -1,10 +1,7 @@ [DEFAULT] head = tail = -support-files = - udpsocket_child.js [test_tcpsocket_ipc.js] [test_tcpserversocket_ipc.js] -[test_udpsocket_ipc.js] run-sequentially = Uses hardcoded port, bug 903830.
--- a/dom/system/gonk/AutoMounter.cpp +++ b/dom/system/gonk/AutoMounter.cpp @@ -258,16 +258,31 @@ public: return; } vol->SetSharingEnabled(aAllowSharing); DBG("Calling UpdateState due to volume %s shareing set to %d", vol->NameStr(), (int)aAllowSharing); UpdateState(); } + void FormatVolume(const nsACString& aVolumeName) + { + RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName); + if (!vol) { + return; + } + if (vol->IsFormatRequested()) { + return; + } + vol->SetFormatRequested(true); + DBG("Calling UpdateState due to volume %s formatting set to %d", + vol->NameStr(), (int)vol->IsFormatRequested()); + UpdateState(); + } + private: AutoVolumeEventObserver mVolumeEventObserver; AutoVolumeManagerStateObserver mVolumeManagerStateObserver; RefPtr<VolumeResponseCallback> mResponseCallback; int32_t mMode; }; @@ -423,50 +438,54 @@ AutoMounter::UpdateState() LOG("UpdateState: Volume %s is %s and %s", vol->NameStr(), vol->StateStr(), vol->MediaPresent() ? "inserted" : "missing"); } if (!vol->MediaPresent()) { // No media - nothing we can do continue; } - if (tryToShare && vol->IsSharingEnabled()) { + if ((tryToShare && vol->IsSharingEnabled()) || vol->IsFormatRequested()) { // We're going to try to unmount and share the volumes switch (volState) { case nsIVolume::STATE_MOUNTED: { if (vol->IsMountLocked()) { // The volume is currently locked, so leave it in the mounted // state. - LOGW("UpdateState: Mounted volume %s is locked, not sharing", + LOGW("UpdateState: Mounted volume %s is locked, not sharing or formatting", vol->NameStr()); break; } // Mark the volume as if we've started sharing. This will cause // apps which watch device storage notifications to see the volume // go into the shared state, and prompt them to close any open files // that they might have. - vol->SetIsSharing(true); + if (tryToShare && vol->IsSharingEnabled()) { + vol->SetIsSharing(true); + } else if (vol->IsFormatRequested()){ + vol->SetIsFormatting(true); + } // Check to see if there are any open files on the volume and // don't initiate the unmount while there are open files. OpenFileFinder::Info fileInfo; OpenFileFinder fileFinder(vol->MountPoint()); if (fileFinder.First(&fileInfo)) { LOGW("The following files are open under '%s'", vol->MountPoint().get()); do { LOGW(" PID: %d file: '%s' app: '%s' comm: '%s' exe: '%s'\n", fileInfo.mPid, fileInfo.mFileName.get(), fileInfo.mAppName.get(), fileInfo.mComm.get(), fileInfo.mExe.get()); } while (fileFinder.Next(&fileInfo)); - LOGW("UpdateState: Mounted volume %s has open files, not sharing", + LOGW("UpdateState: Mounted volume %s has open files, not sharing or formatting", vol->NameStr()); // Check again in a few seconds to see if the files are closed. // Since we're trying to share the volume, this implies that we're // plugged into the PC via USB and this in turn implies that the // battery is charging, so we don't need to be too concerned about // wasting battery here. // @@ -490,20 +509,33 @@ AutoMounter::UpdateState() // Volume is mounted, we need to unmount before // we can share. LOG("UpdateState: Unmounting %s", vol->NameStr()); vol->StartUnmount(mResponseCallback); return; // UpdateState will be called again when the Unmount command completes } case nsIVolume::STATE_IDLE: { - // Volume is unmounted. We can go ahead and share. - LOG("UpdateState: Sharing %s", vol->NameStr()); - vol->StartShare(mResponseCallback); - return; // UpdateState will be called again when the Share command completes + LOG("UpdateState: Volume %s is nsIVolume::STATE_IDLE", vol->NameStr()); + if (vol->IsFormatting() && !vol->IsFormatRequested()) { + vol->SetFormatRequested(false); + LOG("UpdateState: Mounting %s", vol->NameStr()); + vol->StartMount(mResponseCallback); + break; + } + if (tryToShare && vol->IsSharingEnabled()) { + // Volume is unmounted. We can go ahead and share. + LOG("UpdateState: Sharing %s", vol->NameStr()); + vol->StartShare(mResponseCallback); + } else if (vol->IsFormatRequested()){ + // Volume is unmounted. We can go ahead and format. + LOG("UpdateState: Formatting %s", vol->NameStr()); + vol->StartFormat(mResponseCallback); + } + return; // UpdateState will be called again when the Share/Format command completes } default: { // Not in a state that we can do anything about. break; } } } else { // We're going to try and unshare and remount the volumes @@ -574,16 +606,25 @@ SetAutoMounterSharingModeIOThread(const { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); MOZ_ASSERT(sAutoMounter); sAutoMounter->SetSharingMode(aVolumeName, aAllowSharing); } static void +AutoMounterFormatVolumeIOThread(const nsCString& aVolumeName) +{ + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); + MOZ_ASSERT(sAutoMounter); + + sAutoMounter->FormatVolume(aVolumeName); +} + +static void UsbCableEventIOThread() { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); if (!sAutoMounter) { return; } DBG("Calling UpdateState due to USBCableEvent"); @@ -727,21 +768,30 @@ SetAutoMounterMode(int32_t aMode) NewRunnableFunction(SetAutoMounterModeIOThread, aMode)); } void SetAutoMounterSharingMode(const nsCString& aVolumeName, bool aAllowSharing) { XRE_GetIOMessageLoop()->PostTask( FROM_HERE, - NewRunnableFunction(SetAutoMounterSharingModeIOThread, + NewRunnableFunction(SetAutoMounterSharingModeIOThread, aVolumeName, aAllowSharing)); } void +AutoMounterFormatVolume(const nsCString& aVolumeName) +{ + XRE_GetIOMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(AutoMounterFormatVolumeIOThread, + aVolumeName)); +} + +void ShutdownAutoMounter() { sAutoMounterSetting = nullptr; sUsbCableObserver = nullptr; XRE_GetIOMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(ShutdownAutoMounterIOThread));
--- a/dom/system/gonk/AutoMounter.h +++ b/dom/system/gonk/AutoMounter.h @@ -55,16 +55,25 @@ GetAutoMounterStatus(); * If a volume is enabled for sharing, and the autmounter * is in a state to share, then the volume will be shared * with the PC. */ void SetAutoMounterSharingMode(const nsCString& aVolumeName, bool aAllowSharing); /** + * Formats the volume with specified volume name. + * + * If the volume is ready to format, automounter + * will unmount it, format it and then mount it again. + */ +void +AutoMounterFormatVolume(const nsCString& aVolumeName); + +/** * Shuts down the automounter. * * This leaves the volumes in whatever state they're in. */ void ShutdownAutoMounter(); } // system
--- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -297,21 +297,22 @@ XPCOMUtils.defineLazyGetter(this, "gMess if (DEBUG) debug("Unregistered " + topic + " target: " + target); } }, _enqueueTargetMessage: function _enqueueTargetMessage(topic, message, options) { let msg = { topic : topic, message : message, options : options }; - // Remove previous queued message of same message type, only one message - // per message type is allowed in queue. + // Remove previous queued message with the same message type and client Id + // , only one message per (message type + client Id) is allowed in queue. let messageQueue = this.targetMessageQueue; for(let i = 0; i < messageQueue.length; i++) { - if (messageQueue[i].message === message) { + if (messageQueue[i].message === message && + messageQueue[i].options.clientId === options.clientId) { messageQueue.splice(i, 1); break; } } messageQueue.push(msg); },
--- a/dom/system/gonk/Volume.cpp +++ b/dom/system/gonk/Volume.cpp @@ -55,17 +55,19 @@ static int32_t sMountGeneration = 0; Volume::Volume(const nsCSubstring& aName) : mMediaPresent(true), mState(nsIVolume::STATE_INIT), mName(aName), mMountGeneration(-1), mMountLocked(true), // Needs to agree with nsVolume::nsVolume mSharingEnabled(false), mCanBeShared(true), - mIsSharing(false) + mIsSharing(false), + mFormatRequested(false), + mIsFormatting(false) { DBG("Volume %s: created", NameStr()); } void Volume::SetIsSharing(bool aIsSharing) { if (aIsSharing == mIsSharing) { @@ -75,16 +77,30 @@ Volume::SetIsSharing(bool aIsSharing) LOG("Volume %s: IsSharing set to %d state %s", NameStr(), (int)mIsSharing, StateStr(mState)); if (mIsSharing) { mEventObserverList.Broadcast(this); } } void +Volume::SetIsFormatting(bool aIsFormatting) +{ + if (aIsFormatting == mIsFormatting) { + return; + } + mIsFormatting = aIsFormatting; + LOG("Volume %s: IsFormatting set to %d state %s", + NameStr(), (int)mIsFormatting, StateStr(mState)); + if (mIsFormatting) { + mEventObserverList.Broadcast(this); + } +} + +void Volume::SetMediaPresent(bool aMediaPresent) { MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); // mMediaPresent is slightly redunant to the state, however // when media is removed (while Idle), we get the following: // 631 Volume sdcard /mnt/sdcard disk removed (179:0) @@ -122,21 +138,29 @@ Volume::SetSharingEnabled(bool aSharingE { mSharingEnabled = aSharingEnabled; LOG("SetSharingMode for volume %s to %d canBeShared = %d", NameStr(), (int)mSharingEnabled, (int)mCanBeShared); } void +Volume::SetFormatRequested(bool aFormatRequested) +{ + mFormatRequested = aFormatRequested; + + LOG("SetFormatRequested for volume %s to %d CanBeFormatted = %d", + NameStr(), (int)mFormatRequested, (int)CanBeFormatted()); +} + +void Volume::SetState(Volume::STATE aNewState) { MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); - if (aNewState == mState) { return; } if (aNewState == nsIVolume::STATE_MOUNTED) { mMountGeneration = ++sMountGeneration; LOG("Volume %s: changing state from %s to %s @ '%s' (%d observers) " "mountGeneration = %d, locked = %d", NameStr(), StateStr(mState), @@ -151,17 +175,22 @@ Volume::SetState(Volume::STATE aNewState switch (aNewState) { case nsIVolume::STATE_NOMEDIA: // Cover the startup case where we don't get insertion/removal events mMediaPresent = false; mIsSharing = false; break; case nsIVolume::STATE_MOUNTED: + mIsFormatting = false; + mIsSharing = false; + break; case nsIVolume::STATE_FORMATTING: + mFormatRequested = false; + mIsFormatting = true; mIsSharing = false; break; case nsIVolume::STATE_SHARED: case nsIVolume::STATE_SHAREDMNT: // Covers startup cases. Normally, mIsSharing would be set to true // when we issue the command to initiate the sharing process, but // it's conceivable that a volume could already be in a shared state @@ -203,16 +232,25 @@ Volume::StartUnmount(VolumeResponseCallb { MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); StartCommand(new VolumeActionCommand(this, "unmount", "force", aCallback)); } void +Volume::StartFormat(VolumeResponseCallback* aCallback) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); + + StartCommand(new VolumeActionCommand(this, "format", "", aCallback)); +} + +void Volume::StartShare(VolumeResponseCallback* aCallback) { MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); StartCommand(new VolumeActionCommand(this, "share", "ums", aCallback)); }
--- a/dom/system/gonk/Volume.h +++ b/dom/system/gonk/Volume.h @@ -41,20 +41,24 @@ public: // The mount point is the name of the directory where the volume is mounted. // (i.e. path that leads to the files stored on the volume). const nsCString& MountPoint() const { return mMountPoint; } int32_t MountGeneration() const { return mMountGeneration; } bool IsMountLocked() const { return mMountLocked; } bool MediaPresent() const { return mMediaPresent; } bool CanBeShared() const { return mCanBeShared; } + bool CanBeFormatted() const { return CanBeShared(); } bool IsSharingEnabled() const { return mCanBeShared && mSharingEnabled; } + bool IsFormatRequested() const { return CanBeFormatted() && mFormatRequested; } bool IsSharing() const { return mIsSharing; } + bool IsFormatting() const { return mIsFormatting; } void SetSharingEnabled(bool aSharingEnabled); + void SetFormatRequested(bool aFormatRequested); typedef mozilla::Observer<Volume *> EventObserver; typedef mozilla::ObserverList<Volume *> EventObserverList; // NOTE: that observers must live in the IOThread. static void RegisterObserver(EventObserver* aObserver); static void UnregisterObserver(EventObserver* aObserver); @@ -64,20 +68,22 @@ private: friend class VolumeManager; // Calls HandleVoldResponse friend class VolumeListCallback; // Calls SetMountPoint, SetState // The StartXxx functions will queue up a command to the VolumeManager. // You can queue up as many commands as you like, and aCallback will // be called as each one completes. void StartMount(VolumeResponseCallback* aCallback); void StartUnmount(VolumeResponseCallback* aCallback); + void StartFormat(VolumeResponseCallback* aCallback); void StartShare(VolumeResponseCallback* aCallback); void StartUnshare(VolumeResponseCallback* aCallback); void SetIsSharing(bool aIsSharing); + void SetIsFormatting(bool aIsFormatting); void SetState(STATE aNewState); void SetMediaPresent(bool aMediaPresent); void SetMountPoint(const nsCSubstring& aMountPoint); void StartCommand(VolumeCommand* aCommand); void HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer& aTokenizer); static void UpdateMountLock(const nsACString& aVolumeName, @@ -86,18 +92,20 @@ private: bool mMediaPresent; STATE mState; const nsCString mName; nsCString mMountPoint; int32_t mMountGeneration; bool mMountLocked; bool mSharingEnabled; + bool mFormatRequested; bool mCanBeShared; bool mIsSharing; + bool mIsFormatting; static EventObserverList mEventObserverList; }; } // system } // mozilla #endif // mozilla_system_volumemanager_h__
--- a/dom/system/gonk/VolumeServiceIOThread.h +++ b/dom/system/gonk/VolumeServiceIOThread.h @@ -32,13 +32,14 @@ private: virtual void Notify(const VolumeManager::StateChangedEvent& aEvent); virtual void Notify(Volume* const & aVolume); RefPtr<nsVolumeService> mVolumeService; }; void InitVolumeServiceIOThread(nsVolumeService* const & aVolumeService); void ShutdownVolumeServiceIOThread(); +void FormatVolume(const nsCString& aVolume); } // system } // mozilla #endif // mozilla_system_volumeserviceiothread_h__
--- a/dom/system/gonk/nsIVolume.idl +++ b/dom/system/gonk/nsIVolume.idl @@ -1,16 +1,16 @@ /* 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 "nsISupports.idl" #include "nsIVolumeStat.idl" -[scriptable, uuid(e476e7ea-5cde-4d5a-b00d-d60daad76398)] +[scriptable, uuid(8c163fe4-5577-11e3-b3d0-10bf48d707fb)] interface nsIVolume : nsISupports { // These MUST match the states from android's system/vold/Volume.h header const long STATE_INIT = -1; const long STATE_NOMEDIA = 0; const long STATE_IDLE = 1; const long STATE_PENDING = 2; const long STATE_CHECKING = 3; @@ -57,18 +57,27 @@ interface nsIVolume : nsISupports // Determines if the volume is currently being shared. This covers off // more than just state == STATE_SHARED. isSharing will return true from the // time that the volume leaves the mounted state, until it gets back to // mounted, nomedia, or formatting states. This attribute is to allow // device storage to suppress unwanted 'unavailable' status when // transitioning from mounted to sharing and back again. readonly attribute boolean isSharing; + // Determines if the volume is currently formatting. This sets true once + // mFormatRequest == true and mState == STATE_MOUNTED, and sets false + // once the volume has been formatted and mounted again. + readonly attribute boolean isFormatting; + nsIVolumeStat getStats(); + // Formats the volume in IO thread, if the volume is ready to be formatted. + // Automounter will unmount it, format it and then mount it again. + void format(); + // Whether this is a fake volume. readonly attribute boolean isFake; }; %{C++ // For use with the ObserverService #define NS_VOLUME_STATE_CHANGED "volume-state-changed"
--- a/dom/system/gonk/nsVolume.cpp +++ b/dom/system/gonk/nsVolume.cpp @@ -8,16 +8,18 @@ #include "nsIPowerManagerService.h" #include "nsISupportsUtils.h" #include "nsIVolume.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "nsVolumeStat.h" #include "nsXULAppAPI.h" #include "Volume.h" +#include "AutoMounter.h" +#include "VolumeManager.h" #define VOLUME_MANAGER_LOG_TAG "nsVolume" #include "VolumeManagerLog.h" namespace mozilla { namespace system { const char * @@ -48,17 +50,18 @@ NS_IMPL_ISUPPORTS1(nsVolume, nsIVolume) nsVolume::nsVolume(const Volume* aVolume) : mName(NS_ConvertUTF8toUTF16(aVolume->Name())), mMountPoint(NS_ConvertUTF8toUTF16(aVolume->MountPoint())), mState(aVolume->State()), mMountGeneration(aVolume->MountGeneration()), mMountLocked(aVolume->IsMountLocked()), mIsFake(false), mIsMediaPresent(aVolume->MediaPresent()), - mIsSharing(aVolume->IsSharing()) + mIsSharing(aVolume->IsSharing()), + mIsFormatting(aVolume->IsFormatting()) { } bool nsVolume::Equals(nsIVolume* aVolume) { nsString volName; aVolume->GetName(volName); if (!mName.Equals(volName)) { @@ -96,16 +99,22 @@ bool nsVolume::Equals(nsIVolume* aVolume } bool isSharing; aVolume->GetIsSharing(&isSharing); if (mIsSharing != isSharing) { return false; } + bool isFormatting; + aVolume->GetIsFormatting(&isFormatting); + if (mIsFormatting != isFormatting) { + return false; + } + return true; } NS_IMETHODIMP nsVolume::GetIsMediaPresent(bool *aIsMediaPresent) { *aIsMediaPresent = mIsMediaPresent; return NS_OK; } @@ -117,16 +126,22 @@ NS_IMETHODIMP nsVolume::GetIsMountLocked } NS_IMETHODIMP nsVolume::GetIsSharing(bool *aIsSharing) { *aIsSharing = mIsSharing; return NS_OK; } +NS_IMETHODIMP nsVolume::GetIsFormatting(bool *aIsFormatting) +{ + *aIsFormatting = mIsFormatting; + return NS_OK; +} + NS_IMETHODIMP nsVolume::GetName(nsAString& aName) { aName = mName; return NS_OK; } NS_IMETHODIMP nsVolume::GetMountGeneration(int32_t* aMountGeneration) { @@ -165,41 +180,63 @@ NS_IMETHODIMP nsVolume::GetStats(nsIVolu } NS_IMETHODIMP nsVolume::GetIsFake(bool *aIsFake) { *aIsFake = mIsFake; return NS_OK; } +NS_IMETHODIMP nsVolume::Format() +{ + XRE_GetIOMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(FormatVolumeIOThread, NameStr())); + + return NS_OK; +} + +/* static */ +void nsVolume::FormatVolumeIOThread(const nsCString& aVolume) +{ + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); + if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { + return; + } + + AutoMounterFormatVolume(aVolume); +} + void nsVolume::LogState() const { if (mState == nsIVolume::STATE_MOUNTED) { LOG("nsVolume: %s state %s @ '%s' gen %d locked %d fake %d " - "media %d sharing %d", + "media %d sharing %d formatting %d", NameStr().get(), StateStr(), MountPointStr().get(), MountGeneration(), (int)IsMountLocked(), (int)IsFake(), - (int)IsMediaPresent(), (int)IsSharing()); + (int)IsMediaPresent(), (int)IsSharing(), + (int)IsFormatting()); return; } LOG("nsVolume: %s state %s", NameStr().get(), StateStr()); } void nsVolume::Set(nsIVolume* aVolume) { MOZ_ASSERT(NS_IsMainThread()); aVolume->GetName(mName); aVolume->GetMountPoint(mMountPoint); aVolume->GetState(&mState); aVolume->GetIsFake(&mIsFake); aVolume->GetIsMediaPresent(&mIsMediaPresent); aVolume->GetIsSharing(&mIsSharing); + aVolume->GetIsFormatting(&mIsFormatting); int32_t volMountGeneration; aVolume->GetMountGeneration(&volMountGeneration); if (mState != nsIVolume::STATE_MOUNTED) { // Since we're not in the mounted state, we need to // forgot whatever mount generation we may have had. mMountGeneration = -1;
--- a/dom/system/gonk/nsVolume.h +++ b/dom/system/gonk/nsVolume.h @@ -32,30 +32,32 @@ public: const bool& aIsMediaPresent, const bool& aIsSharing) : mName(aName), mMountPoint(aMountPoint), mState(aState), mMountGeneration(aMountGeneration), mMountLocked(false), mIsFake(false), mIsMediaPresent(aIsMediaPresent), - mIsSharing(aIsSharing) + mIsSharing(aIsSharing), + mIsFormatting(false) { } // This constructor is used by nsVolumeService::FindAddVolumeByName, and // will be followed shortly by a Set call. nsVolume(const nsAString& aName) : mName(aName), mState(STATE_INIT), mMountGeneration(-1), mMountLocked(true), // Needs to agree with Volume::Volume mIsFake(false), mIsMediaPresent(false), - mIsSharing(false) + mIsSharing(false), + mIsFormatting(false) { } bool Equals(nsIVolume* aVolume); void Set(nsIVolume* aVolume); void LogState() const; @@ -69,35 +71,38 @@ public: nsCString MountPointStr() const { return NS_LossyConvertUTF16toASCII(mMountPoint); } int32_t State() const { return mState; } const char* StateStr() const { return NS_VolumeStateStr(mState); } bool IsFake() const { return mIsFake; } bool IsMediaPresent() const { return mIsMediaPresent; } bool IsSharing() const { return mIsSharing; } + bool IsFormatting() const { return mIsFormatting; } typedef nsTArray<nsRefPtr<nsVolume> > Array; private: ~nsVolume() {} friend class nsVolumeService; // Calls the following XxxMountLock functions void UpdateMountLock(const nsAString& aMountLockState); void UpdateMountLock(bool aMountLocked); void SetIsFake(bool aIsFake); void SetState(int32_t aState); + static void FormatVolumeIOThread(const nsCString& aVolume); nsString mName; nsString mMountPoint; int32_t mState; int32_t mMountGeneration; bool mMountLocked; bool mIsFake; bool mIsMediaPresent; bool mIsSharing; + bool mIsFormatting; }; } // system } // mozilla #endif // mozilla_system_nsvolume_h__
--- a/dom/system/gonk/nsVolumeService.cpp +++ b/dom/system/gonk/nsVolumeService.cpp @@ -426,38 +426,40 @@ public: { MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); } NS_IMETHOD Run() { MOZ_ASSERT(NS_IsMainThread()); DBG("UpdateVolumeRunnable::Run '%s' state %s gen %d locked %d " - "media %d sharing %d", + "media %d sharing %d formatting %d", mVolume->NameStr().get(), mVolume->StateStr(), mVolume->MountGeneration(), (int)mVolume->IsMountLocked(), - (int)mVolume->IsMediaPresent(), mVolume->IsSharing()); + (int)mVolume->IsMediaPresent(), mVolume->IsSharing(), + mVolume->IsFormatting()); mVolumeService->UpdateVolume(mVolume); mVolumeService = nullptr; mVolume = nullptr; return NS_OK; } private: nsRefPtr<nsVolumeService> mVolumeService; nsRefPtr<nsVolume> mVolume; }; void nsVolumeService::UpdateVolumeIOThread(const Volume* aVolume) { DBG("UpdateVolumeIOThread: Volume '%s' state %s mount '%s' gen %d locked %d " - "media %d sharing %d", + "media %d sharing %d formatting %d", aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPoint().get(), aVolume->MountGeneration(), (int)aVolume->IsMountLocked(), - (int)aVolume->MediaPresent(), (int)aVolume->IsSharing()); + (int)aVolume->MediaPresent(), (int)aVolume->IsSharing(), + (int)aVolume->IsFormatting()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); NS_DispatchToMainThread(new UpdateVolumeRunnable(this, aVolume)); } } // namespace system } // namespace mozilla
--- a/dom/system/gonk/ril_consts.js +++ b/dom/system/gonk/ril_consts.js @@ -704,16 +704,17 @@ this.COMPREHENSIONTLV_TAG_ITEM = 0x0f; this.COMPREHENSIONTLV_TAG_ITEM_ID = 0x10; this.COMPREHENSIONTLV_TAG_RESPONSE_LENGTH = 0x11; this.COMPREHENSIONTLV_TAG_FILE_LIST = 0x12; this.COMPREHENSIONTLV_TAG_LOCATION_INFO = 0x13; this.COMPREHENSIONTLV_TAG_IMEI = 0x14; this.COMPREHENSIONTLV_TAG_HELP_REQUEST = 0x15; this.COMPREHENSIONTLV_TAG_NMR = 0x16; this.COMPREHENSIONTLV_TAG_DEFAULT_TEXT = 0x17; +this.COMPREHENSIONTLV_TAG_NEXT_ACTION_IND = 0x18; this.COMPREHENSIONTLV_TAG_CAUSE = 0x1a; this.COMPREHENSIONTLV_TAG_LOCATION_STATUS = 0x1b; this.COMPREHENSIONTLV_TAG_TRANSACTION_ID = 0x1c; this.COMPREHENSIONTLV_TAG_EVENT_LIST = 0x19; this.COMPREHENSIONTLV_TAG_ICON_ID = 0x1e; this.COMPREHENSIONTLV_TAG_ICON_ID_LIST = 0x1f; this.COMPREHENSIONTLV_TAG_TIMER_IDENTIFIER = 0x24; this.COMPREHENSIONTLV_TAG_TIMER_VALUE = 0x25; @@ -754,29 +755,29 @@ this.STK_CMD_POLL_INTERVAL = 0x03; this.STK_CMD_POLL_OFF = 0x04; this.STK_CMD_SET_UP_EVENT_LIST = 0x05; this.STK_CMD_SET_UP_CALL = 0x10; this.STK_CMD_SEND_SS = 0x11; this.STK_CMD_SEND_USSD = 0x12; this.STK_CMD_SEND_SMS = 0x13; this.STK_CMD_SEND_DTMF = 0x14; this.STK_CMD_LAUNCH_BROWSER = 0x15; -this.STK_CMD_OPEN_CHANNEL = 0x16; -this.STK_CMD_CLOSE_CHANNEL = 0x17; -this.STK_CMD_RECEIVE_DATA = 0x18; -this.STK_CMD_SEND_DATA = 0x19; this.STK_CMD_PLAY_TONE = 0x20; this.STK_CMD_DISPLAY_TEXT = 0x21; this.STK_CMD_GET_INKEY = 0x22; this.STK_CMD_GET_INPUT = 0x23; this.STK_CMD_SELECT_ITEM = 0x24; this.STK_CMD_SET_UP_MENU = 0x25; this.STK_CMD_PROVIDE_LOCAL_INFO = 0x26; this.STK_CMD_TIMER_MANAGEMENT = 0x27; this.STK_CMD_SET_UP_IDLE_MODE_TEXT = 0x28; +this.STK_CMD_OPEN_CHANNEL = 0x40; +this.STK_CMD_CLOSE_CHANNEL = 0x41; +this.STK_CMD_RECEIVE_DATA = 0x42; +this.STK_CMD_SEND_DATA = 0x43; // STK Result code. // TS 11.14, clause 12.12 // Results '0X' and '1X' indicate that the command has been performed. // Command performed successfully. this.STK_RESULT_OK = 0x00; @@ -977,16 +978,20 @@ this.STK_LOCAL_INFO_NMR_FOR_MULTIPLE_ACC this.STK_TIMER_START = 0x00; this.STK_TIMER_DEACTIVATE = 0x01; this.STK_TMIER_GET_CURRENT_VALUE = 0x02; // Browser Termination Cause. this.STK_BROWSER_TERMINATION_CAUSE_USER = 0x00; this.STK_BROWSER_TERMINATION_CAUSE_ERROR = 0x01; +// Next Action Indicator. +this.STK_NEXT_ACTION_NULL = 0x00; +this.STK_NEXT_ACTION_END_PROACTIVE_SESSION = 0x81; + /** * Supported Terminal Facilities. * * value = 1, supported. * 0, not supported. */ this.STK_TERMINAL_SUPPORT_PROFILE_DOWNLOAD = 1; this.STK_TERMINAL_SUPPORT_SMS_PP_DOWNLOAD = 1;
--- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -9724,16 +9724,21 @@ let StkCommandParamsFactory = { // The 1st bit and 2nd bit determines the presentation type. menu.presentationType = cmdDetails.commandQualifier & 0x03; // Help information available. if (cmdDetails.commandQualifier & 0x80) { menu.isHelpAvailable = true; } + ctlv = StkProactiveCmdHelper.searchForTag(COMPREHENSIONTLV_TAG_NEXT_ACTION_IND, ctlvs); + if (ctlv) { + menu.nextActionList = ctlv.value; + } + return menu; }, processDisplayText: function processDisplayText(cmdDetails, ctlvs) { let textMsg = {}; let ctlv = StkProactiveCmdHelper.searchForTag(COMPREHENSIONTLV_TAG_TEXT_STRING, ctlvs); if (!ctlv) { @@ -10414,16 +10419,33 @@ let StkProactiveCmdHelper = { retrieveUrl: function retrieveUrl(length) { let s = ""; for (let i = 0; i < length; i++) { s += String.fromCharCode(GsmPDUHelper.readHexOctet()); } return {url: s}; }, + /** + * Next Action Indicator List. + * + * | Byte | Description | Length | + * | 1 | Next Action tag | 1 | + * | 1 | Length(X) | 1 | + * | 3~ | Next Action List | X | + * | 3+X-1 | | | + */ + retrieveNextActionList: function retrieveNextActionList(length) { + let nextActionList = []; + for (let i = 0; i < length; i++) { + nextActionList.push(GsmPDUHelper.readHexOctet()); + } + return nextActionList; + }, + searchForTag: function searchForTag(tag, ctlvs) { let iter = Iterator(ctlvs); return this.searchForNextTag(tag, iter); }, searchForNextTag: function searchForNextTag(tag, iter) { for (let [index, ctlv] in iter) { if ((ctlv.tag & ~COMPREHENSIONTLV_FLAG_CR) == tag) { @@ -10479,16 +10501,19 @@ StkProactiveCmdHelper[COMPREHENSIONTLV_T return this.retrieveTimerValue(length); }; StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_IMMEDIATE_RESPONSE] = function COMPREHENSIONTLV_TAG_IMMEDIATE_RESPONSE(length) { return this.retrieveImmediaResponse(length); }; StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_URL] = function COMPREHENSIONTLV_TAG_URL(length) { return this.retrieveUrl(length); }; +StkProactiveCmdHelper[COMPREHENSIONTLV_TAG_NEXT_ACTION_IND] = function COMPREHENSIONTLV_TAG_NEXT_ACTION_IND(length) { + return this.retrieveNextActionList(length); +}; let ComprehensionTlvHelper = { /** * Decode raw data to a Comprehension-TLV. */ decode: function decode() { let hlen = 0; // For header(tag field + length field) length. let temp = GsmPDUHelper.readHexOctet();
--- a/dom/system/gonk/tests/test_ril_worker_stk.js +++ b/dom/system/gonk/tests/test_ril_worker_stk.js @@ -653,16 +653,186 @@ add_test(function test_stk_proactive_com do_check_eq(tlv.value.commandNumber, 0x01); do_check_eq(tlv.value.typeOfCommand, STK_CMD_MORE_TIME); do_check_eq(tlv.value.commandQualifier, 0x00); run_next_test(); }); /** + * Verify Proactive Command : Select Item + */ +add_test(function test_stk_proactive_command_select_item() { + let worker = newUint8Worker(); + let pduHelper = worker.GsmPDUHelper; + let berHelper = worker.BerTlvHelper; + let stkHelper = worker.StkProactiveCmdHelper; + let stkFactory = worker.StkCommandParamsFactory; + + let select_item_1 = [ + 0xD0, + 0x33, + 0x81, 0x03, 0x01, 0x24, 0x00, + 0x82, 0x02, 0x81, 0x82, + 0x85, 0x05, 0x54, 0x69, 0x74, 0x6C, 0x65, + 0x8F, 0x07, 0x01, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x31, + 0x8F, 0x07, 0x02, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x32, + 0x8F, 0x07, 0x03, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x33, + 0x18, 0x03, 0x10, 0x15, 0x20, + 0x90, 0x01, 0x01 + ]; + + for(let i = 0 ; i < select_item_1.length; i++) { + pduHelper.writeHexOctet(select_item_1[i]); + } + + let berTlv = berHelper.decode(select_item_1.length); + let ctlvs = berTlv.value; + let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs); + do_check_eq(tlv.value.commandNumber, 0x01); + do_check_eq(tlv.value.typeOfCommand, STK_CMD_SELECT_ITEM); + do_check_eq(tlv.value.commandQualifier, 0x00); + + let menu = stkFactory.createParam(tlv.value, ctlvs); + do_check_eq(menu.title, "Title"); + do_check_eq(menu.items[0].identifier, 1); + do_check_eq(menu.items[0].text, "item 1"); + do_check_eq(menu.items[1].identifier, 2); + do_check_eq(menu.items[1].text, "item 2"); + do_check_eq(menu.items[2].identifier, 3); + do_check_eq(menu.items[2].text, "item 3"); + do_check_eq(menu.nextActionList[0], STK_CMD_SET_UP_CALL); + do_check_eq(menu.nextActionList[1], STK_CMD_LAUNCH_BROWSER); + do_check_eq(menu.nextActionList[2], STK_CMD_PLAY_TONE); + do_check_eq(menu.defaultItem, 0x00); + + let select_item_2 = [ + 0xD0, + 0x33, + 0x81, 0x03, 0x01, 0x24, 0x00, + 0x82, 0x02, 0x81, 0x82, + 0x85, 0x05, 0x54, 0x69, 0x74, 0x6C, 0x65, + 0x8F, 0x07, 0x01, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x31, + 0x8F, 0x07, 0x02, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x32, + 0x8F, 0x07, 0x03, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x33, + 0x18, 0x03, 0x00, 0x15, 0x81, + 0x90, 0x01, 0x03 + ]; + + for(let i = 0 ; i < select_item_2.length; i++) { + pduHelper.writeHexOctet(select_item_2[i]); + } + + berTlv = berHelper.decode(select_item_2.length); + ctlvs = berTlv.value; + tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs); + do_check_eq(tlv.value.commandNumber, 0x01); + do_check_eq(tlv.value.typeOfCommand, STK_CMD_SELECT_ITEM); + do_check_eq(tlv.value.commandQualifier, 0x00); + + menu = stkFactory.createParam(tlv.value, ctlvs); + do_check_eq(menu.title, "Title"); + do_check_eq(menu.items[0].identifier, 1); + do_check_eq(menu.items[0].text, "item 1"); + do_check_eq(menu.items[1].identifier, 2); + do_check_eq(menu.items[1].text, "item 2"); + do_check_eq(menu.items[2].identifier, 3); + do_check_eq(menu.items[2].text, "item 3"); + do_check_eq(menu.nextActionList[0], STK_NEXT_ACTION_NULL); + do_check_eq(menu.nextActionList[1], STK_CMD_LAUNCH_BROWSER); + do_check_eq(menu.nextActionList[2], STK_NEXT_ACTION_END_PROACTIVE_SESSION); + do_check_eq(menu.defaultItem, 0x02); + + run_next_test(); +}); + +/** + * Verify Proactive Command : Set Up Menu + */ +add_test(function test_stk_proactive_command_set_up_menu() { + let worker = newUint8Worker(); + let pduHelper = worker.GsmPDUHelper; + let berHelper = worker.BerTlvHelper; + let stkHelper = worker.StkProactiveCmdHelper; + let stkFactory = worker.StkCommandParamsFactory; + + let set_up_menu_1 = [ + 0xD0, + 0x30, + 0x81, 0x03, 0x01, 0x25, 0x00, + 0x82, 0x02, 0x81, 0x82, + 0x85, 0x05, 0x54, 0x69, 0x74, 0x6C, 0x65, + 0x8F, 0x07, 0x01, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x31, + 0x8F, 0x07, 0x02, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x32, + 0x8F, 0x07, 0x03, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x33, + 0x18, 0x03, 0x10, 0x15, 0x20 + ]; + + for(let i = 0 ; i < set_up_menu_1.length; i++) { + pduHelper.writeHexOctet(set_up_menu_1[i]); + } + + let berTlv = berHelper.decode(set_up_menu_1.length); + let ctlvs = berTlv.value; + let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs); + do_check_eq(tlv.value.commandNumber, 0x01); + do_check_eq(tlv.value.typeOfCommand, STK_CMD_SET_UP_MENU); + do_check_eq(tlv.value.commandQualifier, 0x00); + + let menu = stkFactory.createParam(tlv.value, ctlvs); + do_check_eq(menu.title, "Title"); + do_check_eq(menu.items[0].identifier, 1); + do_check_eq(menu.items[0].text, "item 1"); + do_check_eq(menu.items[1].identifier, 2); + do_check_eq(menu.items[1].text, "item 2"); + do_check_eq(menu.items[2].identifier, 3); + do_check_eq(menu.items[2].text, "item 3"); + do_check_eq(menu.nextActionList[0], STK_CMD_SET_UP_CALL); + do_check_eq(menu.nextActionList[1], STK_CMD_LAUNCH_BROWSER); + do_check_eq(menu.nextActionList[2], STK_CMD_PLAY_TONE); + + let set_up_menu_2 = [ + 0xD0, + 0x30, + 0x81, 0x03, 0x01, 0x25, 0x00, + 0x82, 0x02, 0x81, 0x82, + 0x85, 0x05, 0x54, 0x69, 0x74, 0x6C, 0x65, + 0x8F, 0x07, 0x01, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x31, + 0x8F, 0x07, 0x02, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x32, + 0x8F, 0x07, 0x03, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x33, + 0x18, 0x03, 0x81, 0x00, 0x00 + ]; + + for(let i = 0 ; i < set_up_menu_2.length; i++) { + pduHelper.writeHexOctet(set_up_menu_2[i]); + } + + berTlv = berHelper.decode(set_up_menu_2.length); + ctlvs = berTlv.value; + tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs); + do_check_eq(tlv.value.commandNumber, 0x01); + do_check_eq(tlv.value.typeOfCommand, STK_CMD_SET_UP_MENU); + do_check_eq(tlv.value.commandQualifier, 0x00); + + let menu = stkFactory.createParam(tlv.value, ctlvs); + do_check_eq(menu.title, "Title"); + do_check_eq(menu.items[0].identifier, 1); + do_check_eq(menu.items[0].text, "item 1"); + do_check_eq(menu.items[1].identifier, 2); + do_check_eq(menu.items[1].text, "item 2"); + do_check_eq(menu.items[2].identifier, 3); + do_check_eq(menu.items[2].text, "item 3"); + do_check_eq(menu.nextActionList[0], STK_NEXT_ACTION_END_PROACTIVE_SESSION); + do_check_eq(menu.nextActionList[1], STK_NEXT_ACTION_NULL); + do_check_eq(menu.nextActionList[2], STK_NEXT_ACTION_NULL); + + run_next_test(); +}); + +/** * Verify Proactive Command : Set Up Call */ add_test(function test_stk_proactive_command_set_up_call() { let worker = newUint8Worker(); let pduHelper = worker.GsmPDUHelper; let berHelper = worker.BerTlvHelper; let stkHelper = worker.StkProactiveCmdHelper; let cmdFactory = worker.StkCommandParamsFactory; @@ -812,17 +982,17 @@ add_test(function test_stk_proactive_com let pduHelper = worker.GsmPDUHelper; let berHelper = worker.BerTlvHelper; let stkHelper = worker.StkProactiveCmdHelper; // Open Channel let open_channel = [ 0xD0, 0x0F, - 0x81, 0x03, 0x01, 0x16, 0x00, + 0x81, 0x03, 0x01, 0x40, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x04, 0x4F, 0x70, 0x65, 0x6E //alpha id: "Open" ]; for (let i = 0; i < open_channel.length; i++) { pduHelper.writeHexOctet(open_channel[i]); } @@ -835,17 +1005,17 @@ add_test(function test_stk_proactive_com tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs); do_check_eq(tlv.value.identifier, "Open"); // Close Channel let close_channel = [ 0xD0, 0x10, - 0x81, 0x03, 0x01, 0x17, 0x00, + 0x81, 0x03, 0x01, 0x41, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x05, 0x43, 0x6C, 0x6F, 0x73, 0x65 //alpha id: "Close" ]; for (let i = 0; i < close_channel.length; i++) { pduHelper.writeHexOctet(close_channel[i]); } @@ -858,17 +1028,17 @@ add_test(function test_stk_proactive_com tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs); do_check_eq(tlv.value.identifier, "Close"); // Receive Data let receive_data = [ 0XD0, 0X12, - 0x81, 0x03, 0x01, 0x18, 0x00, + 0x81, 0x03, 0x01, 0x42, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x07, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65 //alpha id: "Receive" ]; for (let i = 0; i < receive_data.length; i++) { pduHelper.writeHexOctet(receive_data[i]); } @@ -881,17 +1051,17 @@ add_test(function test_stk_proactive_com tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs); do_check_eq(tlv.value.identifier, "Receive"); // Send Data let send_data = [ 0xD0, 0x0F, - 0x81, 0x03, 0x01, 0x19, 0x00, + 0x81, 0x03, 0x01, 0x43, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x04, 0x53, 0x65, 0x6E, 0x64 //alpha id: "Send" ]; for (let i = 0; i < send_data.length; i++) { pduHelper.writeHexOctet(send_data[i]); }
--- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -129,16 +129,17 @@ var interfaceNamesInGlobalScope = "BatteryManager", "BeforeUnloadEvent", "BiquadFilterNode", "Blob", "BlobEvent", {name: "BluetoothAdapter", b2g: true}, {name: "BluetoothDevice", b2g: true}, {name: "BluetoothDeviceEvent", b2g: true}, + {name: "BluetoothDiscoveryStateChangedEvent", b2g: true}, {name: "BluetoothManager", b2g: true}, {name: "BluetoothStatusChangedEvent", b2g: true}, {name: "BoxObject", xbl: true}, {name: "BrowserFeedWriter", desktop: true}, {name: "CallEvent", b2g: true, pref: "dom.telephony.enabled"}, {name: "CallGroupErrorEvent", b2g: true, pref: "dom.telephony.enabled"}, "CameraCapabilities", "CameraControl",
--- a/dom/webidl/BluetoothAdapter.webidl +++ b/dom/webidl/BluetoothAdapter.webidl @@ -44,16 +44,17 @@ interface BluetoothAdapter : EventTarget [GetterThrows] readonly attribute any devices; // array of type DOMString[] [GetterThrows] readonly attribute any uuids; attribute EventHandler ondevicefound; + attribute EventHandler ondiscoverystatechanged; // Fired when pairing process is completed attribute EventHandler onpairedstatuschanged; // Fired when a2dp connection status changed attribute EventHandler ona2dpstatuschanged; // Fired when handsfree connection status changed
new file mode 100644 --- /dev/null +++ b/dom/webidl/BluetoothDiscoveryStateChangedEvent.webidl @@ -0,0 +1,16 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +[Constructor(DOMString type, optional BluetoothDiscoveryStateChangedEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"] +interface BluetoothDiscoveryStateChangedEvent : Event +{ + readonly attribute boolean discovering; +}; + +dictionary BluetoothDiscoveryStateChangedEventInit : EventInit +{ + boolean discovering = false; +};
--- a/dom/webidl/DeviceStorage.webidl +++ b/dom/webidl/DeviceStorage.webidl @@ -34,16 +34,18 @@ interface DeviceStorage : EventTarget { optional DeviceStorageEnumerationParameters options); [Throws] DOMRequest freeSpace(); [Throws] DOMRequest usedSpace(); [Throws] DOMRequest available(); + [Throws] + DOMRequest format(); // Note that the storageName is just a name (like sdcard), and doesn't // include any path information. readonly attribute DOMString storageName; // Determines if this storage area is the one which will be used by default // for storing new files. readonly attribute boolean default;
--- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -498,16 +498,17 @@ WEBIDL_FILES += [ 'StyleSheetChangeEvent.webidl', ] if CONFIG['MOZ_B2G_BT']: WEBIDL_FILES += [ 'BluetoothAdapter.webidl', 'BluetoothDevice.webidl', 'BluetoothDeviceEvent.webidl', + 'BluetoothDiscoveryStateChangedEvent.webidl', 'BluetoothManager.webidl', 'BluetoothStatusChangedEvent.webidl', ] if CONFIG['MOZ_B2G_RIL']: WEBIDL_FILES += [ 'CFStateChangeEvent.webidl', 'DataErrorEvent.webidl',
--- a/js/xpconnect/src/event_impl_gen.conf.in +++ b/js/xpconnect/src/event_impl_gen.conf.in @@ -25,16 +25,17 @@ simple_events = [ 'StyleSheetApplicableStateChangeEvent', #ifdef MOZ_WIDGET_GONK 'MozWifiStatusChangeEvent', 'MozWifiConnectionInfoEvent', #endif #ifdef MOZ_B2G_BT 'BluetoothDeviceEvent', 'BluetoothStatusChangedEvent', + 'BluetoothDiscoveryStateChangedEvent', #endif #ifdef MOZ_B2G_RIL 'CFStateChangeEvent', 'DataErrorEvent', 'MozEmergencyCbModeEvent', 'MozOtaStatusEvent', 'MozCellBroadcastEvent', 'MozVoicemailEvent',
--- a/media/mtransport/build/moz.build +++ b/media/mtransport/build/moz.build @@ -10,16 +10,17 @@ EXPORTS.mtransport += [ '../nricectx.h', '../nricemediastream.h', '../nriceresolverfake.h', '../rlogringbuffer.h', '../runnable_utils.h', '../runnable_utils_generated.h', '../sigslot.h', '../simpletokenbucket.h', + '../stun_udp_socket_filter.h', '../transportflow.h', '../transportlayer.h', '../transportlayerdtls.h', '../transportlayerice.h', '../transportlayerlog.h', '../transportlayerloopback.h', '../transportlayerprsock.h', ]
--- a/media/mtransport/nr_socket_prsock.cpp +++ b/media/mtransport/nr_socket_prsock.cpp @@ -891,16 +891,18 @@ void NrSocketIpc::create_m(const nsACStr nsresult rv; socket_child_ = do_CreateInstance("@mozilla.org/udp-socket-child;1", &rv); if (NS_FAILED(rv)) { err_ = true; MOZ_ASSERT(false, "Failed to create UDPSocketChild"); } + socket_child_->SetFilterName(nsCString("stun")); + if (NS_FAILED(socket_child_->Bind(this, host, port))) { err_ = true; MOZ_ASSERT(false, "Failed to create UDP socket"); } } void NrSocketIpc::sendto_m(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf) { ASSERT_ON_THREAD(main_thread_);
--- a/media/mtransport/objs.mozbuild +++ b/media/mtransport/objs.mozbuild @@ -10,16 +10,17 @@ mtransport_lcppsrcs = [ 'nr_timer.cpp', 'nricectx.cpp', 'nricemediastream.cpp', 'nriceresolver.cpp', 'nriceresolverfake.cpp', 'nrinterfaceprioritizer.cpp', 'rlogringbuffer.cpp', 'simpletokenbucket.cpp', + 'stun_udp_socket_filter.cpp', 'transportflow.cpp', 'transportlayer.cpp', 'transportlayerdtls.cpp', 'transportlayerice.cpp', 'transportlayerlog.cpp', 'transportlayerloopback.cpp', 'transportlayerprsock.cpp', ]
new file mode 100644 --- /dev/null +++ b/media/mtransport/stun_udp_socket_filter.cpp @@ -0,0 +1,207 @@ +/* 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 <string> +#include <set> + +extern "C" { +#include "nr_api.h" +#include "transport_addr.h" +#include "stun.h" +} + +#include "mozilla/net/DNS.h" +#include "stun_udp_socket_filter.h" +#include "nr_socket_prsock.h" + +namespace { + +class NetAddressAdapter { + public: + NetAddressAdapter(const mozilla::net::NetAddr& netaddr) + : addr_(ntohl(netaddr.inet.ip)), + port_(ntohs(netaddr.inet.port)) { + MOZ_ASSERT(netaddr.raw.family == AF_INET); + } + + bool operator<(const NetAddressAdapter& rhs) const { + return addr_ != rhs.addr_ ? (addr_ < rhs.addr_) : (port_ < rhs.port_); + } + + bool operator!=(const NetAddressAdapter& rhs) const { + return (*this < rhs) || (rhs < *this); + } + + private: + const uint32_t addr_; + const uint16_t port_; +}; + +class PendingSTUNRequest { + public: + PendingSTUNRequest(const NetAddressAdapter& netaddr, const UINT12 &id) + : id_(id), + net_addr_(netaddr), + is_id_set_(true) {} + + PendingSTUNRequest(const NetAddressAdapter& netaddr) + : id_(), + net_addr_(netaddr), + is_id_set_(false) {} + + bool operator<(const PendingSTUNRequest& rhs) const { + if (net_addr_ != rhs.net_addr_) { + return net_addr_ < rhs.net_addr_; + } + + if (!is_id_set_ && !rhs.is_id_set_) { + // PendingSTUNRequest can be stored to set only when it has id, + // so comparing two PendingSTUNRequst without id is not going + // to happen. + MOZ_CRASH(); + } + + if (!(is_id_set_ && rhs.is_id_set_)) { + // one of operands doesn't have id, ignore the difference. + return false; + } + + return memcmp(id_.octet, rhs.id_.octet, sizeof(id_.octet)) < 0; + } + + private: + const UINT12 id_; + const NetAddressAdapter net_addr_; + const bool is_id_set_; +}; + +class STUNUDPSocketFilter : public nsIUDPSocketFilter { + public: + STUNUDPSocketFilter() + : white_list_(), + pending_requests_() {} + + virtual ~STUNUDPSocketFilter() {} + + NS_DECL_ISUPPORTS + NS_DECL_NSIUDPSOCKETFILTER + + private: + bool filter_incoming_packet(const mozilla::net::NetAddr *remote_addr, + const uint8_t *data, + uint32_t len); + + bool filter_outgoing_packet(const mozilla::net::NetAddr *remote_addr, + const uint8_t *data, + uint32_t len); + + std::set<NetAddressAdapter> white_list_; + std::set<PendingSTUNRequest> pending_requests_; + std::set<PendingSTUNRequest> response_allowed_; +}; + +NS_IMPL_ISUPPORTS1(STUNUDPSocketFilter, nsIUDPSocketFilter) + +NS_IMETHODIMP +STUNUDPSocketFilter::FilterPacket(const mozilla::net::NetAddr *remote_addr, + const uint8_t *data, + uint32_t len, + int32_t direction, + bool *result) { + // Allowing IPv4 address only. + if (remote_addr->raw.family != AF_INET) { + *result = false; + return NS_OK; + } + + switch (direction) { + case nsIUDPSocketFilter::SF_INCOMING: + *result = filter_incoming_packet(remote_addr, data, len); + break; + case nsIUDPSocketFilter::SF_OUTGOING: + *result = filter_outgoing_packet(remote_addr, data, len); + break; + default: + MOZ_CRASH("Unknown packet direction"); + } + return NS_OK; +} + +bool STUNUDPSocketFilter::filter_incoming_packet(const mozilla::net::NetAddr *remote_addr, + const uint8_t *data, uint32_t len) { + // Check white list + if (white_list_.find(*remote_addr) != white_list_.end()) { + return true; + } + + // Check if we had sent any stun request to this destination. If we had sent a request + // to this host, we check the transaction id, and we can add this address to whitelist. + std::set<PendingSTUNRequest>::iterator it = + pending_requests_.find(PendingSTUNRequest(*remote_addr)); + if (it != pending_requests_.end()) { + if (nr_is_stun_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) { + const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data); + // If it is a STUN response message and we can match its id with one of the pending + // requests, we can add this address into whitelist. + if (nr_is_stun_response_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) { + PendingSTUNRequest pending_req(*remote_addr, msg->id); + std::set<PendingSTUNRequest>::iterator it = pending_requests_.find(pending_req); + if (it != pending_requests_.end()) { + pending_requests_.erase(it); + response_allowed_.erase(pending_req); + white_list_.insert(*remote_addr); + } + } else { + // If it is a STUN message, but not a response message, we add it into response + // allowed list and allow outgoing filter to send a response back. + response_allowed_.insert(PendingSTUNRequest(*remote_addr, msg->id)); + } + } + return true; + } + + return false; +} + +bool STUNUDPSocketFilter::filter_outgoing_packet(const mozilla::net::NetAddr *remote_addr, + const uint8_t *data, uint32_t len) { + // Check white list + if (white_list_.find(*remote_addr) != white_list_.end()) { + return true; + } + + // Check if it is a stun packet. If yes, we put it into a pending list and wait for + // response packet. + if (nr_is_stun_request_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) { + const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data); + pending_requests_.insert(PendingSTUNRequest(*remote_addr, msg->id)); + return true; + } + + // If it is a stun response packet, and we had received the request before, we can + // allow it packet to pass filter. + if (nr_is_stun_response_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) { + const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data); + std::set<PendingSTUNRequest>::iterator it = + response_allowed_.find(PendingSTUNRequest(*remote_addr, msg->id)); + if (it != response_allowed_.end()) { + return true; + } + } + + return false; +} + +} // anonymous namespace + +NS_IMPL_ISUPPORTS1(nsStunUDPSocketFilterHandler, nsIUDPSocketFilterHandler) + +NS_IMETHODIMP nsStunUDPSocketFilterHandler::NewFilter(nsIUDPSocketFilter **result) +{ + nsIUDPSocketFilter *ret = new STUNUDPSocketFilter(); + if (!ret) { + return NS_ERROR_OUT_OF_MEMORY; + } + NS_ADDREF(*result = ret); + return NS_OK; +}
new file mode 100644 --- /dev/null +++ b/media/mtransport/stun_udp_socket_filter.h @@ -0,0 +1,21 @@ +/* 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 stun_udp_socket_filter_h__ +#define stun_udp_socket_filter_h__ + +#include "nsIUDPSocketFilter.h" + +#define NS_STUN_UDP_SOCKET_FILTER_HANDLER_CONTRACTID NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX "stun" +#define NS_STUN_UDP_SOCKET_FILTER_HANDLER_CID { 0x3e43ee93, 0x829e, 0x4ea6, \ + { 0xa3, 0x4e, 0x62, 0xd9, 0xe4, 0xc9, 0xf9, 0x93 } }; + +class nsStunUDPSocketFilterHandler : public nsIUDPSocketFilterHandler { +public: + virtual ~nsStunUDPSocketFilterHandler() {} + NS_DECL_ISUPPORTS + NS_DECL_NSIUDPSOCKETFILTERHANDLER +}; + + +#endif // stun_udp_socket_filter_h__
--- a/media/mtransport/test/ice_unittest.cpp +++ b/media/mtransport/test/ice_unittest.cpp @@ -32,16 +32,18 @@ #include "nrinterfaceprioritizer.h" #include "mtransport_test_utils.h" #include "rlogringbuffer.h" #include "runnable_utils.h" #include "stunserver.h" // TODO(bcampen@mozilla.com): Big fat hack since the build system doesn't give // us a clean way to add object files to a single executable. #include "stunserver.cpp" +#include "stun_udp_socket_filter.h" +#include "mozilla/net/DNS.h" #define GTEST_HAS_RTTI 0 #include "gtest/gtest.h" #include "gtest_utils.h" using namespace mozilla; MtransportTestUtils *test_utils; @@ -915,16 +917,62 @@ class PrioritizerTest : public ::testing ASSERT_EQ(0, r); ASSERT_LE(pref1, pref2); } private: nr_interface_prioritizer *prioritizer_; }; +class PacketFilterTest : public ::testing::Test { + public: + PacketFilterTest(): filter_(nullptr) {} + + void SetUp() { + nsCOMPtr<nsIUDPSocketFilterHandler> handler = + do_GetService(NS_STUN_UDP_SOCKET_FILTER_HANDLER_CONTRACTID); + handler->NewFilter(getter_AddRefs(filter_)); + } + + void TestIncoming(const uint8_t* data, uint32_t len, + uint8_t from_addr, int from_port, + bool expected_result) { + mozilla::net::NetAddr addr; + MakeNetAddr(&addr, from_addr, from_port); + bool result; + nsresult rv = filter_->FilterPacket(&addr, data, len, + nsIUDPSocketFilter::SF_INCOMING, + &result); + ASSERT_EQ(NS_OK, rv); + ASSERT_EQ(expected_result, result); + } + + void TestOutgoing(const uint8_t* data, uint32_t len, + uint8_t to_addr, int to_port, + bool expected_result) { + mozilla::net::NetAddr addr; + MakeNetAddr(&addr, to_addr, to_port); + bool result; + nsresult rv = filter_->FilterPacket(&addr, data, len, + nsIUDPSocketFilter::SF_OUTGOING, + &result); + ASSERT_EQ(NS_OK, rv); + ASSERT_EQ(expected_result, result); + } + + private: + void MakeNetAddr(mozilla::net::NetAddr* net_addr, + uint8_t last_digit, uint16_t port) { + net_addr->inet.family = AF_INET; + net_addr->inet.ip = 192 << 24 | 168 << 16 | 1 << 8 | last_digit; + net_addr->inet.port = port; + } + + nsCOMPtr<nsIUDPSocketFilter> filter_; +}; } // end namespace TEST_F(IceGatherTest, TestGatherFakeStunServerHostnameNoResolver) { peer_->SetStunServer(g_stun_server_hostname, kDefaultStunServerPort); Gather(); } TEST_F(IceGatherTest, TestGatherFakeStunServerIpAddress) { @@ -1318,16 +1366,127 @@ TEST_F(PrioritizerTest, TestPrioritizer) HasLowerPreference("9", "2"); HasLowerPreference("2", "6"); HasLowerPreference("6", "7"); HasLowerPreference("7", "1"); HasLowerPreference("1", "5"); HasLowerPreference("5", "4"); } +TEST_F(PacketFilterTest, TestSendNonStunPacket) { + const unsigned char data[] = "12345abcde"; + TestOutgoing(data, sizeof(data), 123, 45, false); +} + +TEST_F(PacketFilterTest, TestRecvNonStunPacket) { + const unsigned char data[] = "12345abcde"; + TestIncoming(data, sizeof(data), 123, 45, false); +} + +TEST_F(PacketFilterTest, TestSendStunPacket) { + nr_stun_message *msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg)); + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST_F(PacketFilterTest, TestRecvStunPacketWithoutAPendingId) { + nr_stun_message *msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg)); + + msg->header.id.octet[0] = 1; + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + + msg->header.id.octet[0] = 0; + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestIncoming(msg->buffer, msg->length, 123, 45, true); + + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST_F(PacketFilterTest, TestRecvStunPacketWithoutAPendingAddress) { + nr_stun_message *msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg)); + + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestIncoming(msg->buffer, msg->length, 123, 46, false); + TestIncoming(msg->buffer, msg->length, 124, 45, false); + + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST_F(PacketFilterTest, TestRecvStunPacketWithPendingIdAndAddress) { + nr_stun_message *msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg)); + + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestIncoming(msg->buffer, msg->length, 123, 45, true); + + // Test whitelist by filtering non-stun packets. + const unsigned char data[] = "12345abcde"; + + // 123:45 is white-listed. + TestOutgoing(data, sizeof(data), 123, 45, true); + TestIncoming(data, sizeof(data), 123, 45, true); + + // Indications pass as well. + msg->header.type = NR_STUN_MSG_BINDING_INDICATION; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + TestIncoming(msg->buffer, msg->length, 123, 45, true); + + // Packets from and to other address are still disallowed. + TestOutgoing(data, sizeof(data), 123, 46, false); + TestIncoming(data, sizeof(data), 123, 46, false); + TestOutgoing(data, sizeof(data), 124, 45, false); + TestIncoming(data, sizeof(data), 124, 45, false); + + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST_F(PacketFilterTest, TestSendNonRequestStunPacket) { + nr_stun_message *msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg)); + + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, false); + + // Send a packet so we allow the incoming request. + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + + // This packet makes us able to send a response. + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestIncoming(msg->buffer, msg->length, 123, 45, true); + + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + static std::string get_environment(const char *name) { char *value = getenv(name); if (!value) return ""; return value; }
--- a/netwerk/base/public/moz.build +++ b/netwerk/base/public/moz.build @@ -102,16 +102,17 @@ XPIDL_SOURCES += [ 'nsISyncStreamListener.idl', 'nsISystemProxySettings.idl', 'nsIThreadRetargetableRequest.idl', 'nsIThreadRetargetableStreamListener.idl', 'nsITimedChannel.idl', 'nsITraceableChannel.idl', 'nsITransport.idl', 'nsIUDPSocket.idl', + 'nsIUDPSocketFilter.idl', 'nsIUnicharStreamLoader.idl', 'nsIUploadChannel.idl', 'nsIUploadChannel2.idl', 'nsIURI.idl', 'nsIURIChecker.idl', 'nsIURIClassifier.idl', 'nsIURIWithPrincipal.idl', 'nsIURL.idl',
new file mode 100644 --- /dev/null +++ b/netwerk/base/public/nsIUDPSocketFilter.idl @@ -0,0 +1,45 @@ +/* -*- Mode: IDL; 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 "nsISupports.idl" +#include "nsINetAddr.idl" + +native NetAddr(mozilla::net::NetAddr); +[ptr] native NetAddrPtr(mozilla::net::NetAddr); + + +/** + * Filters are created and run on the parent, and filter all UDP packets, both + * ingoing and outgoing. The child must specify the name of a recognized filter + * in order to create a UDP socket. + */ +[uuid(24f20de4-09e9-42ab-947a-0d6a3d103d59)] +interface nsIUDPSocketFilter : nsISupports +{ + const long SF_INCOMING = 0; + const long SF_OUTGOING = 1; + + bool filterPacket([const]in NetAddrPtr remote_addr, + [const, array, size_is(len)]in uint8_t data, + in unsigned long len, + in long direction); +}; + +/** + * Factory of a specified filter. + */ +[uuid(81ee76c6-4753-4125-9c8c-290ed9ba62fb)] +interface nsIUDPSocketFilterHandler : nsISupports +{ + nsIUDPSocketFilter newFilter(); +}; + +%{C++ +/** + * Filter handlers are registered with XPCOM under the following CONTRACTID prefix: + */ +#define NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX "@mozilla.org/network/udp-filter-handler;1?name=" +%}
--- a/netwerk/ipc/NeckoChild.cpp +++ b/netwerk/ipc/NeckoChild.cpp @@ -209,17 +209,18 @@ NeckoChild::DeallocPTCPServerSocketChild { TCPServerSocketChild* p = static_cast<TCPServerSocketChild*>(child); p->ReleaseIPDLReference(); return true; } PUDPSocketChild* NeckoChild::AllocPUDPSocketChild(const nsCString& aHost, - const uint16_t& aPort) + const uint16_t& aPort, + const nsCString& aFilter) { NS_NOTREACHED("AllocPUDPSocket should not be called"); return nullptr; } bool NeckoChild::DeallocPUDPSocketChild(PUDPSocketChild* child) {
--- a/netwerk/ipc/NeckoChild.h +++ b/netwerk/ipc/NeckoChild.h @@ -43,17 +43,18 @@ protected: virtual bool DeallocPWebSocketChild(PWebSocketChild*); virtual PTCPSocketChild* AllocPTCPSocketChild(); virtual bool DeallocPTCPSocketChild(PTCPSocketChild*); virtual PTCPServerSocketChild* AllocPTCPServerSocketChild(const uint16_t& aLocalPort, const uint16_t& aBacklog, const nsString& aBinaryType); virtual bool DeallocPTCPServerSocketChild(PTCPServerSocketChild*); virtual PUDPSocketChild* AllocPUDPSocketChild(const nsCString& aHost, - const uint16_t& aPort); + const uint16_t& aPort, + const nsCString& aFilter); virtual bool DeallocPUDPSocketChild(PUDPSocketChild*); virtual PRemoteOpenFileChild* AllocPRemoteOpenFileChild(const URIParams&, const OptionalURIParams&); virtual bool DeallocPRemoteOpenFileChild(PRemoteOpenFileChild*); virtual PRtspControllerChild* AllocPRtspControllerChild(); virtual bool DeallocPRtspControllerChild(PRtspControllerChild*); };
--- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -23,16 +23,17 @@ #include "mozilla/dom/network/TCPServerSocketParent.h" #include "mozilla/dom/network/UDPSocketParent.h" #include "mozilla/ipc/URIUtils.h" #include "mozilla/LoadContext.h" #include "mozilla/AppProcessChecker.h" #include "nsPrintfCString.h" #include "nsHTMLDNSPrefetch.h" #include "nsIAppsService.h" +#include "nsIUDPSocketFilter.h" #include "nsEscape.h" #include "RemoteOpenFileParent.h" #include "SerializedLoadContext.h" using mozilla::dom::ContentParent; using mozilla::dom::TabParent; using mozilla::net::PTCPSocketParent; using mozilla::dom::TCPSocketParent; @@ -374,33 +375,52 @@ NeckoParent::DeallocPTCPServerSocketPare { TCPServerSocketParent* p = static_cast<TCPServerSocketParent*>(actor); p->ReleaseIPDLReference(); return true; } PUDPSocketParent* NeckoParent::AllocPUDPSocketParent(const nsCString& aHost, - const uint16_t& aPort) + const uint16_t& aPort, + const nsCString& aFilter) { - bool enabled = Preferences::GetBool("media.peerconnection.ipc.enabled", false); - if (!enabled) { - NS_WARNING("Not support UDP socket in content process, aborting subprocess"); - return nullptr; + UDPSocketParent* p; + + // Only allow socket if it specifies a valid packet filter. + nsAutoCString contractId(NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX); + contractId.Append(aFilter); + + if (!aFilter.IsEmpty()) { + nsCOMPtr<nsIUDPSocketFilterHandler> filterHandler = + do_GetService(contractId.get()); + if (filterHandler) { + nsCOMPtr<nsIUDPSocketFilter> filter; + nsresult rv = filterHandler->NewFilter(getter_AddRefs(filter)); + if (NS_SUCCEEDED(rv)) { + p = new UDPSocketParent(filter); + } else { + printf_stderr("Cannot create filter that content specified. " + "filter name: %s, error code: %d.", aFilter.get(), rv); + } + } else { + printf_stderr("Content doesn't have a valid filter. " + "filter name: %s.", aFilter.get()); + } } - UDPSocketParent* p = new UDPSocketParent(); - p->AddRef(); + + NS_IF_ADDREF(p); return p; - } bool NeckoParent::RecvPUDPSocketConstructor(PUDPSocketParent* aActor, const nsCString& aHost, - const uint16_t& aPort) + const uint16_t& aPort, + const nsCString& aFilter) { return static_cast<UDPSocketParent*>(aActor)->Init(aHost, aPort); } bool NeckoParent::DeallocPUDPSocketParent(PUDPSocketParent* actor) { UDPSocketParent* p = static_cast<UDPSocketParent*>(actor);
--- a/netwerk/ipc/NeckoParent.h +++ b/netwerk/ipc/NeckoParent.h @@ -112,20 +112,22 @@ protected: const uint16_t& aBacklog, const nsString& aBinaryType); virtual bool RecvPTCPServerSocketConstructor(PTCPServerSocketParent*, const uint16_t& aLocalPort, const uint16_t& aBacklog, const nsString& aBinaryType); virtual bool DeallocPTCPServerSocketParent(PTCPServerSocketParent*); virtual PUDPSocketParent* AllocPUDPSocketParent(const nsCString& aHost, - const uint16_t& aPort); + const uint16_t& aPort, + const nsCString& aFilter); virtual bool RecvPUDPSocketConstructor(PUDPSocketParent*, const nsCString& aHost, - const uint16_t& aPort); + const uint16_t& aPort, + const nsCString& aFilter); virtual bool DeallocPUDPSocketParent(PUDPSocketParent*); virtual bool RecvHTMLDNSPrefetch(const nsString& hostname, const uint16_t& flags); virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname, const uint16_t& flags, const nsresult& reason); virtual mozilla::ipc::IProtocol*
--- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -52,17 +52,17 @@ parent: SerializedLoadContext loadContext, HttpChannelCreationArgs args); PWyciwygChannel(); PFTPChannel(PBrowser browser, SerializedLoadContext loadContext, FTPChannelCreationArgs args); PWebSocket(PBrowser browser, SerializedLoadContext loadContext); PTCPServerSocket(uint16_t localPort, uint16_t backlog, nsString binaryType); - PUDPSocket(nsCString host, uint16_t port); + PUDPSocket(nsCString host, uint16_t port, nsCString filter); PRemoteOpenFile(URIParams fileuri, OptionalURIParams appuri); HTMLDNSPrefetch(nsString hostname, uint16_t flags); CancelHTMLDNSPrefetch(nsString hostname, uint16_t flags, nsresult reason); PRtspController(); both: