|
1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "base/basictypes.h" |
|
8 #include "BluetoothDevice.h" |
|
9 #include "BluetoothReplyRunnable.h" |
|
10 #include "BluetoothService.h" |
|
11 #include "BluetoothUtils.h" |
|
12 |
|
13 #include "nsDOMClassInfo.h" |
|
14 #include "nsTArrayHelpers.h" |
|
15 |
|
16 #include "mozilla/dom/bluetooth/BluetoothTypes.h" |
|
17 #include "mozilla/dom/BluetoothDeviceBinding.h" |
|
18 |
|
19 USING_BLUETOOTH_NAMESPACE |
|
20 |
|
21 DOMCI_DATA(BluetoothDevice, BluetoothDevice) |
|
22 |
|
23 NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothDevice) |
|
24 |
|
25 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(BluetoothDevice, |
|
26 DOMEventTargetHelper) |
|
27 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsUuids) |
|
28 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsServices) |
|
29 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
30 |
|
31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothDevice, |
|
32 DOMEventTargetHelper) |
|
33 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
34 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
35 |
|
36 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothDevice, |
|
37 DOMEventTargetHelper) |
|
38 tmp->Unroot(); |
|
39 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
40 |
|
41 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothDevice) |
|
42 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) |
|
43 |
|
44 NS_IMPL_ADDREF_INHERITED(BluetoothDevice, DOMEventTargetHelper) |
|
45 NS_IMPL_RELEASE_INHERITED(BluetoothDevice, DOMEventTargetHelper) |
|
46 |
|
47 BluetoothDevice::BluetoothDevice(nsPIDOMWindow* aWindow, |
|
48 const nsAString& aAdapterPath, |
|
49 const BluetoothValue& aValue) |
|
50 : DOMEventTargetHelper(aWindow) |
|
51 , BluetoothPropertyContainer(BluetoothObjectType::TYPE_DEVICE) |
|
52 , mJsUuids(nullptr) |
|
53 , mJsServices(nullptr) |
|
54 , mAdapterPath(aAdapterPath) |
|
55 , mIsRooted(false) |
|
56 { |
|
57 MOZ_ASSERT(aWindow); |
|
58 MOZ_ASSERT(IsDOMBinding()); |
|
59 |
|
60 const InfallibleTArray<BluetoothNamedValue>& values = |
|
61 aValue.get_ArrayOfBluetoothNamedValue(); |
|
62 for (uint32_t i = 0; i < values.Length(); ++i) { |
|
63 SetPropertyByValue(values[i]); |
|
64 } |
|
65 |
|
66 BluetoothService* bs = BluetoothService::Get(); |
|
67 NS_ENSURE_TRUE_VOID(bs); |
|
68 bs->RegisterBluetoothSignalHandler(mAddress, this); |
|
69 } |
|
70 |
|
71 BluetoothDevice::~BluetoothDevice() |
|
72 { |
|
73 BluetoothService* bs = BluetoothService::Get(); |
|
74 // bs can be null on shutdown, where destruction might happen. |
|
75 NS_ENSURE_TRUE_VOID(bs); |
|
76 bs->UnregisterBluetoothSignalHandler(mAddress, this); |
|
77 Unroot(); |
|
78 } |
|
79 |
|
80 void |
|
81 BluetoothDevice::DisconnectFromOwner() |
|
82 { |
|
83 DOMEventTargetHelper::DisconnectFromOwner(); |
|
84 |
|
85 BluetoothService* bs = BluetoothService::Get(); |
|
86 NS_ENSURE_TRUE_VOID(bs); |
|
87 bs->UnregisterBluetoothSignalHandler(mAddress, this); |
|
88 } |
|
89 |
|
90 void |
|
91 BluetoothDevice::Root() |
|
92 { |
|
93 if (!mIsRooted) { |
|
94 mozilla::HoldJSObjects(this); |
|
95 mIsRooted = true; |
|
96 } |
|
97 } |
|
98 |
|
99 void |
|
100 BluetoothDevice::Unroot() |
|
101 { |
|
102 if (mIsRooted) { |
|
103 mJsUuids = nullptr; |
|
104 mJsServices = nullptr; |
|
105 mozilla::DropJSObjects(this); |
|
106 mIsRooted = false; |
|
107 } |
|
108 } |
|
109 |
|
110 void |
|
111 BluetoothDevice::SetPropertyByValue(const BluetoothNamedValue& aValue) |
|
112 { |
|
113 const nsString& name = aValue.name(); |
|
114 const BluetoothValue& value = aValue.value(); |
|
115 if (name.EqualsLiteral("Name")) { |
|
116 mName = value.get_nsString(); |
|
117 } else if (name.EqualsLiteral("Path")) { |
|
118 MOZ_ASSERT(value.get_nsString().Length() > 0); |
|
119 mPath = value.get_nsString(); |
|
120 } else if (name.EqualsLiteral("Address")) { |
|
121 mAddress = value.get_nsString(); |
|
122 } else if (name.EqualsLiteral("Class")) { |
|
123 mClass = value.get_uint32_t(); |
|
124 } else if (name.EqualsLiteral("Icon")) { |
|
125 mIcon = value.get_nsString(); |
|
126 } else if (name.EqualsLiteral("Connected")) { |
|
127 mConnected = value.get_bool(); |
|
128 } else if (name.EqualsLiteral("Paired")) { |
|
129 mPaired = value.get_bool(); |
|
130 } else if (name.EqualsLiteral("UUIDs")) { |
|
131 mUuids = value.get_ArrayOfnsString(); |
|
132 nsresult rv; |
|
133 nsIScriptContext* sc = GetContextForEventHandlers(&rv); |
|
134 NS_ENSURE_SUCCESS_VOID(rv); |
|
135 NS_ENSURE_TRUE_VOID(sc); |
|
136 |
|
137 AutoPushJSContext cx(sc->GetNativeContext()); |
|
138 |
|
139 JS::Rooted<JSObject*> uuids(cx); |
|
140 if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, uuids.address()))) { |
|
141 BT_WARNING("Cannot set JS UUIDs object!"); |
|
142 return; |
|
143 } |
|
144 mJsUuids = uuids; |
|
145 Root(); |
|
146 } else if (name.EqualsLiteral("Services")) { |
|
147 mServices = value.get_ArrayOfnsString(); |
|
148 nsresult rv; |
|
149 nsIScriptContext* sc = GetContextForEventHandlers(&rv); |
|
150 NS_ENSURE_SUCCESS_VOID(rv); |
|
151 NS_ENSURE_TRUE_VOID(sc); |
|
152 |
|
153 AutoPushJSContext cx(sc->GetNativeContext()); |
|
154 |
|
155 JS::Rooted<JSObject*> services(cx); |
|
156 if (NS_FAILED(nsTArrayToJSArray(cx, mServices, services.address()))) { |
|
157 BT_WARNING("Cannot set JS Services object!"); |
|
158 return; |
|
159 } |
|
160 mJsServices = services; |
|
161 Root(); |
|
162 } else { |
|
163 nsCString warningMsg; |
|
164 warningMsg.AssignLiteral("Not handling device property: "); |
|
165 warningMsg.Append(NS_ConvertUTF16toUTF8(name)); |
|
166 BT_WARNING(warningMsg.get()); |
|
167 } |
|
168 } |
|
169 |
|
170 // static |
|
171 already_AddRefed<BluetoothDevice> |
|
172 BluetoothDevice::Create(nsPIDOMWindow* aWindow, |
|
173 const nsAString& aAdapterPath, |
|
174 const BluetoothValue& aValue) |
|
175 { |
|
176 MOZ_ASSERT(NS_IsMainThread()); |
|
177 MOZ_ASSERT(aWindow); |
|
178 |
|
179 nsRefPtr<BluetoothDevice> device = |
|
180 new BluetoothDevice(aWindow, aAdapterPath, aValue); |
|
181 return device.forget(); |
|
182 } |
|
183 |
|
184 void |
|
185 BluetoothDevice::Notify(const BluetoothSignal& aData) |
|
186 { |
|
187 BT_LOGD("[D] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get()); |
|
188 |
|
189 BluetoothValue v = aData.value(); |
|
190 if (aData.name().EqualsLiteral("PropertyChanged")) { |
|
191 MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue); |
|
192 |
|
193 const InfallibleTArray<BluetoothNamedValue>& arr = |
|
194 v.get_ArrayOfBluetoothNamedValue(); |
|
195 |
|
196 for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) { |
|
197 SetPropertyByValue(arr[i]); |
|
198 } |
|
199 } else { |
|
200 #ifdef DEBUG |
|
201 nsCString warningMsg; |
|
202 warningMsg.AssignLiteral("Not handling device signal: "); |
|
203 warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name())); |
|
204 BT_WARNING(warningMsg.get()); |
|
205 #endif |
|
206 } |
|
207 } |
|
208 |
|
209 void |
|
210 BluetoothDevice::GetUuids(JSContext* aContext, |
|
211 JS::MutableHandle<JS::Value> aUuids, |
|
212 ErrorResult& aRv) |
|
213 { |
|
214 if (!mJsUuids) { |
|
215 BT_WARNING("UUIDs not yet set!"); |
|
216 aRv.Throw(NS_ERROR_FAILURE); |
|
217 return; |
|
218 } |
|
219 |
|
220 JS::ExposeObjectToActiveJS(mJsUuids); |
|
221 aUuids.setObject(*mJsUuids); |
|
222 } |
|
223 |
|
224 void |
|
225 BluetoothDevice::GetServices(JSContext* aCx, |
|
226 JS::MutableHandle<JS::Value> aServices, |
|
227 ErrorResult& aRv) |
|
228 { |
|
229 if (!mJsServices) { |
|
230 BT_WARNING("Services not yet set!"); |
|
231 aRv.Throw(NS_ERROR_FAILURE); |
|
232 return; |
|
233 } |
|
234 |
|
235 JS::ExposeObjectToActiveJS(mJsServices); |
|
236 aServices.setObject(*mJsServices); |
|
237 } |
|
238 |
|
239 JSObject* |
|
240 BluetoothDevice::WrapObject(JSContext* aContext) |
|
241 { |
|
242 return BluetoothDeviceBinding::Wrap(aContext, this); |
|
243 } |