michael@0: /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "BluetoothDevice.h" michael@0: #include "BluetoothReplyRunnable.h" michael@0: #include "BluetoothService.h" michael@0: #include "BluetoothUtils.h" michael@0: michael@0: #include "nsDOMClassInfo.h" michael@0: #include "nsTArrayHelpers.h" michael@0: michael@0: #include "mozilla/dom/bluetooth/BluetoothTypes.h" michael@0: #include "mozilla/dom/BluetoothDeviceBinding.h" michael@0: michael@0: USING_BLUETOOTH_NAMESPACE michael@0: michael@0: DOMCI_DATA(BluetoothDevice, BluetoothDevice) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothDevice) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(BluetoothDevice, michael@0: DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsUuids) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsServices) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothDevice, michael@0: DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothDevice, michael@0: DOMEventTargetHelper) michael@0: tmp->Unroot(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothDevice) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(BluetoothDevice, DOMEventTargetHelper) michael@0: NS_IMPL_RELEASE_INHERITED(BluetoothDevice, DOMEventTargetHelper) michael@0: michael@0: BluetoothDevice::BluetoothDevice(nsPIDOMWindow* aWindow, michael@0: const nsAString& aAdapterPath, michael@0: const BluetoothValue& aValue) michael@0: : DOMEventTargetHelper(aWindow) michael@0: , BluetoothPropertyContainer(BluetoothObjectType::TYPE_DEVICE) michael@0: , mJsUuids(nullptr) michael@0: , mJsServices(nullptr) michael@0: , mAdapterPath(aAdapterPath) michael@0: , mIsRooted(false) michael@0: { michael@0: MOZ_ASSERT(aWindow); michael@0: MOZ_ASSERT(IsDOMBinding()); michael@0: michael@0: const InfallibleTArray& values = michael@0: aValue.get_ArrayOfBluetoothNamedValue(); michael@0: for (uint32_t i = 0; i < values.Length(); ++i) { michael@0: SetPropertyByValue(values[i]); michael@0: } michael@0: michael@0: BluetoothService* bs = BluetoothService::Get(); michael@0: NS_ENSURE_TRUE_VOID(bs); michael@0: bs->RegisterBluetoothSignalHandler(mAddress, this); michael@0: } michael@0: michael@0: BluetoothDevice::~BluetoothDevice() michael@0: { michael@0: BluetoothService* bs = BluetoothService::Get(); michael@0: // bs can be null on shutdown, where destruction might happen. michael@0: NS_ENSURE_TRUE_VOID(bs); michael@0: bs->UnregisterBluetoothSignalHandler(mAddress, this); michael@0: Unroot(); michael@0: } michael@0: michael@0: void michael@0: BluetoothDevice::DisconnectFromOwner() michael@0: { michael@0: DOMEventTargetHelper::DisconnectFromOwner(); michael@0: michael@0: BluetoothService* bs = BluetoothService::Get(); michael@0: NS_ENSURE_TRUE_VOID(bs); michael@0: bs->UnregisterBluetoothSignalHandler(mAddress, this); michael@0: } michael@0: michael@0: void michael@0: BluetoothDevice::Root() michael@0: { michael@0: if (!mIsRooted) { michael@0: mozilla::HoldJSObjects(this); michael@0: mIsRooted = true; michael@0: } michael@0: } michael@0: michael@0: void michael@0: BluetoothDevice::Unroot() michael@0: { michael@0: if (mIsRooted) { michael@0: mJsUuids = nullptr; michael@0: mJsServices = nullptr; michael@0: mozilla::DropJSObjects(this); michael@0: mIsRooted = false; michael@0: } michael@0: } michael@0: michael@0: void michael@0: BluetoothDevice::SetPropertyByValue(const BluetoothNamedValue& aValue) michael@0: { michael@0: const nsString& name = aValue.name(); michael@0: const BluetoothValue& value = aValue.value(); michael@0: if (name.EqualsLiteral("Name")) { michael@0: mName = value.get_nsString(); michael@0: } else if (name.EqualsLiteral("Path")) { michael@0: MOZ_ASSERT(value.get_nsString().Length() > 0); michael@0: mPath = value.get_nsString(); michael@0: } else if (name.EqualsLiteral("Address")) { michael@0: mAddress = value.get_nsString(); michael@0: } else if (name.EqualsLiteral("Class")) { michael@0: mClass = value.get_uint32_t(); michael@0: } else if (name.EqualsLiteral("Icon")) { michael@0: mIcon = value.get_nsString(); michael@0: } else if (name.EqualsLiteral("Connected")) { michael@0: mConnected = value.get_bool(); michael@0: } else if (name.EqualsLiteral("Paired")) { michael@0: mPaired = value.get_bool(); michael@0: } else if (name.EqualsLiteral("UUIDs")) { michael@0: mUuids = value.get_ArrayOfnsString(); michael@0: nsresult rv; michael@0: nsIScriptContext* sc = GetContextForEventHandlers(&rv); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: NS_ENSURE_TRUE_VOID(sc); michael@0: michael@0: AutoPushJSContext cx(sc->GetNativeContext()); michael@0: michael@0: JS::Rooted uuids(cx); michael@0: if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, uuids.address()))) { michael@0: BT_WARNING("Cannot set JS UUIDs object!"); michael@0: return; michael@0: } michael@0: mJsUuids = uuids; michael@0: Root(); michael@0: } else if (name.EqualsLiteral("Services")) { michael@0: mServices = value.get_ArrayOfnsString(); michael@0: nsresult rv; michael@0: nsIScriptContext* sc = GetContextForEventHandlers(&rv); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: NS_ENSURE_TRUE_VOID(sc); michael@0: michael@0: AutoPushJSContext cx(sc->GetNativeContext()); michael@0: michael@0: JS::Rooted services(cx); michael@0: if (NS_FAILED(nsTArrayToJSArray(cx, mServices, services.address()))) { michael@0: BT_WARNING("Cannot set JS Services object!"); michael@0: return; michael@0: } michael@0: mJsServices = services; michael@0: Root(); michael@0: } else { michael@0: nsCString warningMsg; michael@0: warningMsg.AssignLiteral("Not handling device property: "); michael@0: warningMsg.Append(NS_ConvertUTF16toUTF8(name)); michael@0: BT_WARNING(warningMsg.get()); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: BluetoothDevice::Create(nsPIDOMWindow* aWindow, michael@0: const nsAString& aAdapterPath, michael@0: const BluetoothValue& aValue) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aWindow); michael@0: michael@0: nsRefPtr device = michael@0: new BluetoothDevice(aWindow, aAdapterPath, aValue); michael@0: return device.forget(); michael@0: } michael@0: michael@0: void michael@0: BluetoothDevice::Notify(const BluetoothSignal& aData) michael@0: { michael@0: BT_LOGD("[D] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get()); michael@0: michael@0: BluetoothValue v = aData.value(); michael@0: if (aData.name().EqualsLiteral("PropertyChanged")) { michael@0: MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue); michael@0: michael@0: const InfallibleTArray& arr = michael@0: v.get_ArrayOfBluetoothNamedValue(); michael@0: michael@0: for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) { michael@0: SetPropertyByValue(arr[i]); michael@0: } michael@0: } else { michael@0: #ifdef DEBUG michael@0: nsCString warningMsg; michael@0: warningMsg.AssignLiteral("Not handling device signal: "); michael@0: warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name())); michael@0: BT_WARNING(warningMsg.get()); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: void michael@0: BluetoothDevice::GetUuids(JSContext* aContext, michael@0: JS::MutableHandle aUuids, michael@0: ErrorResult& aRv) michael@0: { michael@0: if (!mJsUuids) { michael@0: BT_WARNING("UUIDs not yet set!"); michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return; michael@0: } michael@0: michael@0: JS::ExposeObjectToActiveJS(mJsUuids); michael@0: aUuids.setObject(*mJsUuids); michael@0: } michael@0: michael@0: void michael@0: BluetoothDevice::GetServices(JSContext* aCx, michael@0: JS::MutableHandle aServices, michael@0: ErrorResult& aRv) michael@0: { michael@0: if (!mJsServices) { michael@0: BT_WARNING("Services not yet set!"); michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return; michael@0: } michael@0: michael@0: JS::ExposeObjectToActiveJS(mJsServices); michael@0: aServices.setObject(*mJsServices); michael@0: } michael@0: michael@0: JSObject* michael@0: BluetoothDevice::WrapObject(JSContext* aContext) michael@0: { michael@0: return BluetoothDeviceBinding::Wrap(aContext, this); michael@0: }