Bug 761511: Patch 3 - Update Manager/Adapter/Device objects to be BluetoothPropertyObjects, and add class specific setters; r=mrbkap
authorKyle Machulis <kyle@nonpolynomial.com>
Tue, 31 Jul 2012 21:53:04 -0700
changeset 101071 f94b5c54e94e5ed7c286b2bdfcbcd497bf7b2733
parent 101070 e05666048b33a8aeb87779cf7a7823ee346ff2ce
child 101072 bcfaabb6bac8b4fea120206de9ca70496b96b43a
child 101076 7a62a2433ba157ae96c2ba4ab79652222bb3c0df
push idunknown
push userunknown
push dateunknown
reviewersmrbkap
bugs761511
milestone17.0a1
Bug 761511: Patch 3 - Update Manager/Adapter/Device objects to be BluetoothPropertyObjects, and add class specific setters; r=mrbkap
dom/bluetooth/BluetoothAdapter.cpp
dom/bluetooth/BluetoothAdapter.h
dom/bluetooth/BluetoothDevice.cpp
dom/bluetooth/BluetoothDevice.h
dom/bluetooth/BluetoothDeviceEvent.cpp
dom/bluetooth/BluetoothManager.cpp
dom/bluetooth/BluetoothManager.h
dom/bluetooth/BluetoothPropertyContainer.h
dom/bluetooth/BluetoothUtils.cpp
dom/bluetooth/BluetoothUtils.h
dom/bluetooth/Makefile.in
dom/bluetooth/linux/BluetoothDBusService.cpp
dom/bluetooth/nsIDOMBluetoothAdapter.idl
dom/bluetooth/nsIDOMBluetoothDevice.idl
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -3,126 +3,171 @@
 /* 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 "base/basictypes.h"
 #include "BluetoothAdapter.h"
 #include "BluetoothDevice.h"
 #include "BluetoothDeviceEvent.h"
+#include "BluetoothPropertyEvent.h"
 #include "BluetoothService.h"
 #include "BluetoothTypes.h"
 #include "BluetoothReplyRunnable.h"
+#include "BluetoothUtils.h"
 
 #include "nsDOMClassInfo.h"
 #include "nsDOMEvent.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMCIDInternal.h"
 #include "nsIDOMDOMRequest.h"
+#include "nsContentUtils.h"
 
 #include "mozilla/LazyIdleThread.h"
 #include "mozilla/Util.h"
 
 using namespace mozilla;
 
 USING_BLUETOOTH_NAMESPACE
 
 DOMCI_DATA(BluetoothAdapter, BluetoothAdapter)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothAdapter)
 
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(BluetoothAdapter,
+                                               nsDOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsUuids)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsDeviceAddresses)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothAdapter, 
                                                   nsDOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(devicefound)
   NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(devicedisappeared)
   NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(propertychanged)  
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothAdapter, 
                                                 nsDOMEventTargetHelper)
+  tmp->Unroot();
   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(devicefound)
   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(devicedisappeared)
   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(propertychanged)  
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothAdapter)
   NS_INTERFACE_MAP_ENTRY(nsIDOMBluetoothAdapter)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BluetoothAdapter)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothAdapter, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothAdapter, nsDOMEventTargetHelper)
 
-class GetPropertiesTask : public BluetoothReplyRunnable
+BluetoothAdapter::BluetoothAdapter(nsPIDOMWindow* aOwner, const nsAString& aPath)
+    : BluetoothPropertyContainer(BluetoothObjectType::TYPE_ADAPTER)
+    , mJsUuids(nullptr)
+    , mJsDeviceAddresses(nullptr)
+    , mIsRooted(false)
 {
-public:
-  GetPropertiesTask(BluetoothAdapter* aAdapter, nsIDOMDOMRequest* aReq) :
-    BluetoothReplyRunnable(aReq),
-    mAdapterPtr(aAdapter)
-  {
-  }
-
-  bool
-  ParseSuccessfulReply(jsval* aValue)
-  {
-    const InfallibleTArray<BluetoothNamedValue>& values =
-      mReply->get_BluetoothReplySuccess().value().get_ArrayOfBluetoothNamedValue();
-    for (uint32_t i = 0; i < values.Length(); ++i) {
-      mAdapterPtr->SetPropertyByValue(values[i]);
-    }
-    *aValue = JSVAL_VOID;
-    return true;
-  }
-
-  void
-  ReleaseMembers()
-  {
-    BluetoothReplyRunnable::ReleaseMembers();
-    mAdapterPtr = nullptr;
-  }
-private:
-  nsRefPtr<BluetoothAdapter> mAdapterPtr;
-};
+  BindToOwner(aOwner);
+  mPath = aPath;
+}
 
 BluetoothAdapter::~BluetoothAdapter()
 {
   BluetoothService* bs = BluetoothService::Get();
   // We can be null on shutdown, where this might happen
   if (bs) {
     if (NS_FAILED(bs->UnregisterBluetoothSignalHandler(mPath, this))) {
       NS_WARNING("Failed to unregister object with observer!");
     }
   }
+  Unroot();
+}
+
+void
+BluetoothAdapter::Unroot()
+{
+  if (!mIsRooted) {
+    return;
+  }
+  NS_DROP_JS_OBJECTS(this, BluetoothAdapter);
+  mIsRooted = false;
+}
+
+void
+BluetoothAdapter::Root()
+{
+  if (mIsRooted) {
+    return;
+  }
+  NS_HOLD_JS_OBJECTS(this, BluetoothAdapter);
+  mIsRooted = true;
 }
 
 void
 BluetoothAdapter::SetPropertyByValue(const BluetoothNamedValue& aValue)
 {
   const nsString& name = aValue.name();
   const BluetoothValue& value = aValue.value();
   if (name.EqualsLiteral("Name")) {
     mName = value.get_nsString();
   } else if (name.EqualsLiteral("Address")) {
     mAddress = value.get_nsString();
+  } else if (name.EqualsLiteral("Path")) {
+    mPath = value.get_nsString();
   } else if (name.EqualsLiteral("Enabled")) {
     mEnabled = value.get_bool();
   } else if (name.EqualsLiteral("Discoverable")) {
     mDiscoverable = value.get_bool();
+  } else if (name.EqualsLiteral("Discovering")) {
+    mDiscovering = value.get_bool();
   } else if (name.EqualsLiteral("Pairable")) {
     mPairable = value.get_bool();
   } else if (name.EqualsLiteral("Powered")) {
     mPowered = value.get_bool();
   } else if (name.EqualsLiteral("PairableTimeout")) {
     mPairableTimeout = value.get_uint32_t();
   } else if (name.EqualsLiteral("DiscoverableTimeout")) {
     mDiscoverableTimeout = value.get_uint32_t();
   } else if (name.EqualsLiteral("Class")) {
     mClass = value.get_uint32_t();
   } else if (name.EqualsLiteral("UUIDs")) {
     mUuids = value.get_ArrayOfnsString();
+    nsresult rv;
+    nsIScriptContext* sc = GetContextForEventHandlers(&rv);
+    if (sc) {
+      rv =
+        StringArrayToJSArray(sc->GetNativeContext(),
+                             sc->GetNativeGlobal(), mUuids, &mJsUuids);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Cannot set JS UUIDs object!");
+        return;
+      }
+      Root();
+    } else {
+      NS_WARNING("Could not get context!");
+    }
+  } else if (name.EqualsLiteral("Devices")) {
+    mDeviceAddresses = value.get_ArrayOfnsString();
+    nsresult rv;
+    nsIScriptContext* sc = GetContextForEventHandlers(&rv);
+    if (sc) {
+      rv =
+        StringArrayToJSArray(sc->GetNativeContext(),
+                             sc->GetNativeGlobal(), mUuids, &mJsDeviceAddresses);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Cannot set JS Devices Addresses object!");
+        return;
+      }
+      Root();
+    } else {
+      NS_WARNING("Could not get context!");
+    }
   } else {
 #ifdef DEBUG
     nsCString warningMsg;
     warningMsg.AssignLiteral("Not handling adapter property: ");
     warningMsg.Append(NS_ConvertUTF16toUTF8(name));
     NS_WARNING(warningMsg.get());
 #endif
   }
@@ -131,52 +176,70 @@ BluetoothAdapter::SetPropertyByValue(con
 // static
 already_AddRefed<BluetoothAdapter>
 BluetoothAdapter::Create(nsPIDOMWindow* aOwner, const nsAString& aPath)
 {
   // Make sure we at least have a path
   NS_ASSERTION(!aPath.IsEmpty(), "Adapter created with empty path!");
     
   BluetoothService* bs = BluetoothService::Get();
-  MOZ_ASSERT(bs);
+  if (!bs) {
+    NS_WARNING("BluetoothService not available!");
+    return nullptr;
+  }
 
-  nsRefPtr<BluetoothAdapter> adapter = new BluetoothAdapter(aPath);
-  adapter->BindToOwner(aOwner);
-  if (NS_FAILED(bs->RegisterBluetoothSignalHandler(aPath, adapter))) {
+  nsRefPtr<BluetoothAdapter> adapter = new BluetoothAdapter(aOwner, aPath);
+  nsString path;
+  path = adapter->GetPath();
+  if (NS_FAILED(bs->RegisterBluetoothSignalHandler(path, adapter))) {
     NS_WARNING("Failed to register object with observer!");
     return nullptr;
   }
   return adapter.forget();
 }
 
 void
 BluetoothAdapter::Notify(const BluetoothSignal& aData)
 {
   if (aData.name().EqualsLiteral("DeviceFound")) {
-    nsRefPtr<BluetoothDevice> d = BluetoothDevice::Create(GetOwner(), aData);
+    nsRefPtr<BluetoothDevice> d = BluetoothDevice::Create(GetOwner(), mPath, aData.value());
     nsRefPtr<BluetoothDeviceEvent> e = BluetoothDeviceEvent::Create(d);
     e->Dispatch(ToIDOMEventTarget(), NS_LITERAL_STRING("devicefound"));
+  } else if (aData.name().EqualsLiteral("PropertyChanged")) {
+    // Get BluetoothNamedValue, make sure array length is 1
+    InfallibleTArray<BluetoothNamedValue> arr = aData.value().get_ArrayOfBluetoothNamedValue();
+    if (arr.Length() != 1) {
+      // This really should not happen
+      NS_ERROR("Got more than one property in a change message!");
+      return;
+    }
+    BluetoothNamedValue v = arr[0];
+    SetPropertyByValue(v);
+    nsRefPtr<BluetoothPropertyEvent> e = BluetoothPropertyEvent::Create(v.name());
+    e->Dispatch(ToIDOMEventTarget(), NS_LITERAL_STRING("propertychanged"));
   } else {
 #ifdef DEBUG
     nsCString warningMsg;
     warningMsg.AssignLiteral("Not handling manager signal: ");
     warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
     NS_WARNING(warningMsg.get());
 #endif
   }
 }
 
 nsresult
 BluetoothAdapter::StartStopDiscovery(bool aStart, nsIDOMDOMRequest** aRequest)
 {
   BluetoothService* bs = BluetoothService::Get();
-  MOZ_ASSERT(bs);
+  if (!bs) {
+    NS_WARNING("BluetoothService not available!");
+    return NS_ERROR_FAILURE;
+  }
 
-  nsCOMPtr<nsIDOMRequestService> rs = do_GetService("@mozilla.org/dom/dom-request-service;1");
-    
+  nsCOMPtr<nsIDOMRequestService> rs = do_GetService("@mozilla.org/dom/dom-request-service;1");    
   if (!rs) {
     NS_WARNING("No DOMRequest Service!");
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIDOMDOMRequest> req;
   nsresult rv = rs->CreateRequest(GetOwner(), getter_AddRefs(req));
   if (NS_FAILED(rv)) {
@@ -263,15 +326,71 @@ BluetoothAdapter::GetDiscoverableTimeout
 {
   *aDiscoverableTimeout = mDiscoverableTimeout;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BluetoothAdapter::GetDevices(JSContext* aCx, jsval* aDevices)
 {
-  NS_WARNING("GetDevices not yet implemented.");
+  if (mJsDeviceAddresses) {
+    aDevices->setObject(*mJsDeviceAddresses);
+  }
+  else {
+    NS_WARNING("UUIDs not yet set!\n");
+    return NS_ERROR_FAILURE;
+  }    
+  return NS_OK;
+}
+
+nsresult
+BluetoothAdapter::GetUuids(JSContext* aCx, jsval* aValue)
+{
+  if (mJsUuids) {
+    aValue->setObject(*mJsUuids);
+  }
+  else {
+    NS_WARNING("UUIDs not yet set!\n");
+    return NS_ERROR_FAILURE;
+  }    
   return NS_OK;
 }
 
+NS_IMETHODIMP
+BluetoothAdapter::SetName(const nsAString& aName,
+                          nsIDOMDOMRequest** aRequest)
+{
+  if (mName.Equals(aName)) {
+    return NS_OK;
+  }
+  nsString name(aName);
+  BluetoothValue value(name);
+  BluetoothNamedValue property(NS_LITERAL_STRING("Name"), value);
+  return SetProperty(GetOwner(), property, aRequest);
+}
+ 
+NS_IMETHODIMP
+BluetoothAdapter::SetDiscoverable(const bool aDiscoverable,
+                                  nsIDOMDOMRequest** aRequest)
+{
+  if (aDiscoverable == mDiscoverable) {
+    return NS_OK;
+  }
+  BluetoothValue value(aDiscoverable);
+  BluetoothNamedValue property(NS_LITERAL_STRING("Discoverable"), value);
+  return SetProperty(GetOwner(), property, aRequest);
+}
+ 
+NS_IMETHODIMP
+BluetoothAdapter::SetDiscoverableTimeout(const PRUint32 aDiscoverableTimeout,
+                                         nsIDOMDOMRequest** aRequest)
+{
+  if (aDiscoverableTimeout == mDiscoverableTimeout) {
+    return NS_OK;
+  }
+  BluetoothValue value(aDiscoverableTimeout);
+  BluetoothNamedValue property(NS_LITERAL_STRING("DiscoverableTimeout"), value);
+  return SetProperty(GetOwner(), property, aRequest);
+}
+
 NS_IMPL_EVENT_HANDLER(BluetoothAdapter, propertychanged)
 NS_IMPL_EVENT_HANDLER(BluetoothAdapter, devicefound)
 NS_IMPL_EVENT_HANDLER(BluetoothAdapter, devicedisappeared)
--- a/dom/bluetooth/BluetoothAdapter.h
+++ b/dom/bluetooth/BluetoothAdapter.h
@@ -3,40 +3,43 @@
 /* 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_bluetooth_bluetoothadapter_h__
 #define mozilla_dom_bluetooth_bluetoothadapter_h__
 
 #include "BluetoothCommon.h"
+#include "BluetoothPropertyContainer.h"
 #include "nsCOMPtr.h"
 #include "nsDOMEventTargetHelper.h"
 #include "nsIDOMBluetoothAdapter.h"
 
 class nsIEventTarget;
 class nsIDOMDOMRequest;
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSignal;
 class BluetoothNamedValue;
 
 class BluetoothAdapter : public nsDOMEventTargetHelper
                        , public nsIDOMBluetoothAdapter
                        , public BluetoothSignalObserver
+                       , public BluetoothPropertyContainer
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMBLUETOOTHADAPTER
 
   NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::)
 
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothAdapter,
-                                           nsDOMEventTargetHelper)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(BluetoothAdapter,
+                                                         nsDOMEventTargetHelper)
+
   static already_AddRefed<BluetoothAdapter>
   Create(nsPIDOMWindow* aOwner, const nsAString& name);
 
   void Notify(const BluetoothSignal& aParam);
 
   nsIDOMEventTarget*
   ToIDOMEventTarget() const
   {
@@ -45,44 +48,42 @@ public:
   }
 
   nsISupports*
   ToISupports() const
   {
     return ToIDOMEventTarget();
   }
 
-  nsresult GetProperties();
-  void SetPropertyByValue(const BluetoothNamedValue& aValue);  
+  void Unroot();
+  virtual void SetPropertyByValue(const BluetoothNamedValue& aValue);  
 private:
   
-  BluetoothAdapter(const nsAString& aPath) : mPath(aPath)
-  {
-  }
-
+  BluetoothAdapter(nsPIDOMWindow* aOwner, const nsAString& aPath);
   ~BluetoothAdapter();
 
-  nsresult SetProperty(const BluetoothNamedValue& aValue,
-                       nsIDOMDOMRequest** aRequest);
+  void Root();
   nsresult StartStopDiscovery(bool aStart, nsIDOMDOMRequest** aRequest);
   
   nsString mAddress;
   nsString mName;
-  nsString mPath;
   bool mEnabled;
   bool mDiscoverable;
   bool mDiscovering;
   bool mPairable;
   bool mPowered;
   PRUint32 mPairableTimeout;
   PRUint32 mDiscoverableTimeout;
   PRUint32 mClass;
   nsTArray<nsString> mDeviceAddresses;
   nsTArray<nsString> mUuids;
-
+  JSObject* mJsUuids;
+  JSObject* mJsDeviceAddresses;
+  bool mIsRooted;
+  
   NS_DECL_EVENT_HANDLER(propertychanged)
   NS_DECL_EVENT_HANDLER(devicefound)
   NS_DECL_EVENT_HANDLER(devicedisappeared)
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/BluetoothDevice.cpp
+++ b/dom/bluetooth/BluetoothDevice.cpp
@@ -1,89 +1,193 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=40: */
 /* 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 "base/basictypes.h"
 #include "BluetoothDevice.h"
+#include "BluetoothPropertyEvent.h"
 #include "BluetoothTypes.h"
+#include "BluetoothReplyRunnable.h"
+#include "BluetoothService.h"
+#include "BluetoothUtils.h"
 
+#include "nsIDOMDOMRequest.h"
 #include "nsDOMClassInfo.h"
+#include "nsContentUtils.h"
 
 USING_BLUETOOTH_NAMESPACE
 
 DOMCI_DATA(BluetoothDevice, BluetoothDevice)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothDevice)
 
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(BluetoothDevice,
+                                               nsDOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsUuids)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothDevice, 
                                                   nsDOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(propertychanged)  
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothDevice, 
                                                 nsDOMEventTargetHelper)
+  tmp->Unroot();
   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(propertychanged)  
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothDevice)
   NS_INTERFACE_MAP_ENTRY(nsIDOMBluetoothDevice)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BluetoothDevice)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothDevice, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothDevice, nsDOMEventTargetHelper)
 
-BluetoothDevice::BluetoothDevice(const BluetoothSignal& aSignal)
+BluetoothDevice::BluetoothDevice(nsPIDOMWindow* aOwner,
+                                 const nsAString& aAdapterPath,
+                                 const BluetoothValue& aValue) :
+  BluetoothPropertyContainer(BluetoothObjectType::TYPE_DEVICE),
+  mJsUuids(nullptr),
+  mAdapterPath(aAdapterPath),
+  mIsRooted(false)
 {
+  BindToOwner(aOwner);
   const InfallibleTArray<BluetoothNamedValue>& values =
-    aSignal.value().get_ArrayOfBluetoothNamedValue();
+    aValue.get_ArrayOfBluetoothNamedValue();
   for (uint32_t i = 0; i < values.Length(); ++i) {
     SetPropertyByValue(values[i]);
   }
 }
 
+BluetoothDevice::~BluetoothDevice()
+{
+  BluetoothService* bs = BluetoothService::Get();
+  // bs can be null on shutdown, where destruction might happen.
+  if (bs) {
+    if (NS_FAILED(bs->UnregisterBluetoothSignalHandler(mPath, this))) {
+      NS_WARNING("Failed to unregister object with observer!");
+    }
+  }
+  Unroot();
+}
+
+void
+BluetoothDevice::Root()
+{
+  if (!mIsRooted) {
+    NS_HOLD_JS_OBJECTS(this, BluetoothDevice);
+    mIsRooted = true;
+  }
+}
+
+void
+BluetoothDevice::Unroot()
+{
+  if (mIsRooted) {
+    NS_DROP_JS_OBJECTS(this, BluetoothDevice);
+    mIsRooted = false;
+  }
+}
+  
 void
 BluetoothDevice::SetPropertyByValue(const BluetoothNamedValue& aValue)
 {
   const nsString& name = aValue.name();
   const BluetoothValue& value = aValue.value();
   if (name.EqualsLiteral("Name")) {
     mName = value.get_nsString();
   } else if (name.EqualsLiteral("Address")) {
     mAddress = value.get_nsString();
+    BluetoothService* bs = BluetoothService::Get();
+    if (!bs) {
+      NS_WARNING("BluetoothService not available!");
+      return;
+    }
+    // We can't actually set up our path until we know our address
+    bs->GetDevicePath(mAdapterPath, mAddress, mPath);
   } else if (name.EqualsLiteral("Class")) {
     mClass = value.get_uint32_t();
   } else if (name.EqualsLiteral("Connected")) {
     mConnected = value.get_bool();
   } else if (name.EqualsLiteral("Paired")) {
     mPaired = value.get_bool();
   } else if (name.EqualsLiteral("UUIDs")) {
     mUuids = value.get_ArrayOfnsString();
+    nsresult rv;
+    nsIScriptContext* sc = GetContextForEventHandlers(&rv);
+    if (sc) {
+      rv =
+        StringArrayToJSArray(sc->GetNativeContext(),
+                             sc->GetNativeGlobal(), mUuids, &mJsUuids);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Cannot set JS UUIDs object!");
+        return;
+      }
+      Root();
+    } else {
+      NS_WARNING("Could not get context!");
+    }
+#ifdef DEBUG
   } else {
-#ifdef DEBUG
     nsCString warningMsg;
     warningMsg.AssignLiteral("Not handling device property: ");
     warningMsg.Append(NS_ConvertUTF16toUTF8(name));
     NS_WARNING(warningMsg.get());
 #endif
   }
 }
 
 // static
 already_AddRefed<BluetoothDevice>
-BluetoothDevice::Create(nsPIDOMWindow* aOwner, const BluetoothSignal& aSignal)
+BluetoothDevice::Create(nsPIDOMWindow* aOwner,
+                        const nsAString& aAdapterPath,
+                        const BluetoothValue& aValue)
 {
-  nsRefPtr<BluetoothDevice> device = new BluetoothDevice(aSignal);
-  device->BindToOwner(device);
+  // Make sure we at least have a service
+  BluetoothService* bs = BluetoothService::Get();
+  if (!bs) {
+    NS_WARNING("BluetoothService not available!");
+    return nullptr;
+  }
+
+  nsRefPtr<BluetoothDevice> device = new BluetoothDevice(aOwner, aAdapterPath,
+                                                         aValue);
+  if (NS_FAILED(bs->RegisterBluetoothSignalHandler(device->mPath, device))) {
+    NS_WARNING("Failed to register object with observer!");
+    return nullptr;
+  }
   return device.forget();
 }
 
+void
+BluetoothDevice::Notify(const BluetoothSignal& aData)
+{
+  if (aData.name().EqualsLiteral("PropertyChanged")) {
+    // Get BluetoothNamedValue, make sure array length is 1
+    BluetoothNamedValue v = aData.value().get_ArrayOfBluetoothNamedValue()[0];
+    nsString name = v.name();
+    SetPropertyByValue(v);
+    nsRefPtr<BluetoothPropertyEvent> e = BluetoothPropertyEvent::Create(name);
+    e->Dispatch(ToIDOMEventTarget(), NS_LITERAL_STRING("propertychanged"));
+  } else {
+#ifdef DEBUG
+    nsCString warningMsg;
+    warningMsg.AssignLiteral("Not handling device signal: ");
+    warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
+    NS_WARNING(warningMsg.get());
+#endif
+  }
+}
+
 NS_IMETHODIMP
 BluetoothDevice::GetAddress(nsAString& aAddress)
 {
   aAddress = mAddress;
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -109,9 +213,21 @@ BluetoothDevice::GetPaired(bool* aPaired
 
 NS_IMETHODIMP
 BluetoothDevice::GetConnected(bool* aConnected)
 {
   *aConnected = mConnected;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+BluetoothDevice::GetUuids(JSContext* aCx, jsval* aUuids)
+{
+  if (mJsUuids) {
+    aUuids->setObject(*mJsUuids);
+  } else {
+    NS_WARNING("UUIDs not yet set!\n");
+    return NS_ERROR_FAILURE;
+  }    
+  return NS_OK;
+}
+
 NS_IMPL_EVENT_HANDLER(BluetoothDevice, propertychanged)
--- a/dom/bluetooth/BluetoothDevice.h
+++ b/dom/bluetooth/BluetoothDevice.h
@@ -3,63 +3,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_bluetooth_bluetoothdevice_h__
 #define mozilla_dom_bluetooth_bluetoothdevice_h__
 
 #include "BluetoothCommon.h"
+#include "BluetoothPropertyContainer.h"
 #include "nsDOMEventTargetHelper.h"
 #include "nsIDOMBluetoothDevice.h"
 #include "nsString.h"
 
+class nsIDOMDOMRequest;
+
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothNamedValue;
+class BluetoothValue;
 class BluetoothSignal;
 
 class BluetoothDevice : public nsDOMEventTargetHelper
                       , public nsIDOMBluetoothDevice
+                      , public BluetoothSignalObserver
+                      , public BluetoothPropertyContainer
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMBLUETOOTHDEVICE
 
   NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::)
 
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothDevice,
-                                           nsDOMEventTargetHelper)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(BluetoothDevice,
+                                                         nsDOMEventTargetHelper)
 
   static already_AddRefed<BluetoothDevice>
-  Create(nsPIDOMWindow* aOwner, const BluetoothSignal& aSignal);
+  Create(nsPIDOMWindow* aOwner, const nsAString& aAdapterPath,
+         const BluetoothValue& aValue);
+
+  void Notify(const BluetoothSignal& aParam);
 
   nsIDOMEventTarget*
   ToIDOMEventTarget() const
   {
     return static_cast<nsDOMEventTargetHelper*>(
       const_cast<BluetoothDevice*>(this));
   }
 
   nsISupports*
   ToISupports() const
   {
     return ToIDOMEventTarget();
   }
-  
+
+  void SetPropertyByValue(const BluetoothNamedValue& aValue);
+
+  void Unroot();
 private:
-  BluetoothDevice(const BluetoothSignal& aSignal);
-  ~BluetoothDevice() {}  
-  void SetPropertyByValue(const BluetoothNamedValue& aValue);
+  BluetoothDevice(nsPIDOMWindow* aOwner, const nsAString& aAdapterPath,
+                  const BluetoothValue& aValue);
+  ~BluetoothDevice();
+  void Root();
   
+  JSObject* mJsUuids;
+
+  nsString mAdapterPath;
   nsString mAddress;
   nsString mName;
   PRUint32 mClass;
   bool mConnected;
   bool mPaired;
+  bool mIsRooted;
   nsTArray<nsString> mUuids;
 
   NS_DECL_EVENT_HANDLER(propertychanged)
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/BluetoothDeviceEvent.cpp
+++ b/dom/bluetooth/BluetoothDeviceEvent.cpp
@@ -3,16 +3,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 "base/basictypes.h"
 #include "BluetoothDeviceEvent.h"
 #include "BluetoothTypes.h"
 #include "BluetoothDevice.h"
+#include "nsIDOMDOMRequest.h"
 
 #include "nsDOMClassInfo.h"
 
 USING_BLUETOOTH_NAMESPACE
 
 // static
 already_AddRefed<BluetoothDeviceEvent>
 BluetoothDeviceEvent::Create(BluetoothDevice* aDevice)
--- a/dom/bluetooth/BluetoothManager.cpp
+++ b/dom/bluetooth/BluetoothManager.cpp
@@ -139,41 +139,56 @@ public:
   }
   
 private:
   nsRefPtr<BluetoothManager> mManagerPtr;
   bool mEnabled;
 };
 
 BluetoothManager::BluetoothManager(nsPIDOMWindow *aWindow) :
+  BluetoothPropertyContainer(BluetoothObjectType::TYPE_MANAGER),
   mEnabled(false)
 {
   BindToOwner(aWindow);
-  mName.AssignLiteral("/");
+  mPath.AssignLiteral("/");
 }
 
 BluetoothManager::~BluetoothManager()
 {
   BluetoothService* bs = BluetoothService::Get();
   // We can be null on shutdown, where this might happen
   if (bs) {
-    if (NS_FAILED(bs->UnregisterBluetoothSignalHandler(mName, this))) {
+    if (NS_FAILED(bs->UnregisterBluetoothSignalHandler(mPath, this))) {
       NS_WARNING("Failed to unregister object with observer!");
     }
   }
 }
 
+void
+BluetoothManager::SetPropertyByValue(const BluetoothNamedValue& aValue)
+{
+#ifdef DEBUG
+    const nsString& name = aValue.name();
+    nsCString warningMsg;
+    warningMsg.AssignLiteral("Not handling manager property: ");
+    warningMsg.Append(NS_ConvertUTF16toUTF8(name));
+    NS_WARNING(warningMsg.get());
+#endif
+}
+
 NS_IMETHODIMP
 BluetoothManager::SetEnabled(bool aEnabled, nsIDOMDOMRequest** aDomRequest)
 {
   BluetoothService* bs = BluetoothService::Get();
-  MOZ_ASSERT(bs);
+  if (!bs) {
+    NS_WARNING("BluetoothService not available!");
+    return NS_ERROR_FAILURE;
+  }
 
   nsCOMPtr<nsIDOMRequestService> rs = do_GetService("@mozilla.org/dom/dom-request-service;1");
-
   if (!rs) {
     NS_WARNING("No DOMRequest Service!");
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIDOMDOMRequest> request;
   nsresult rv = rs->CreateRequest(GetOwner(), getter_AddRefs(request));
   if (NS_FAILED(rv)) {
@@ -203,17 +218,20 @@ BluetoothManager::GetEnabled(bool* aEnab
   *aEnabled = mEnabled;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BluetoothManager::GetDefaultAdapter(nsIDOMDOMRequest** aAdapter)
 {
   BluetoothService* bs = BluetoothService::Get();
-  MOZ_ASSERT(bs);
+  if (!bs) {
+    NS_WARNING("BluetoothService not available!");
+    return NS_ERROR_FAILURE;
+  }
   
   nsCOMPtr<nsIDOMRequestService> rs = do_GetService("@mozilla.org/dom/dom-request-service;1");
 
   if (!rs) {
     NS_WARNING("No DOMRequest Service!");
     return NS_ERROR_FAILURE;
   }
 
@@ -234,17 +252,20 @@ BluetoothManager::GetDefaultAdapter(nsID
 }
 
 // static
 already_AddRefed<BluetoothManager>
 BluetoothManager::Create(nsPIDOMWindow* aWindow) {
 
   nsRefPtr<BluetoothManager> manager = new BluetoothManager(aWindow);
   BluetoothService* bs = BluetoothService::Get();
-  MOZ_ASSERT(bs);
+  if (!bs) {
+    NS_WARNING("BluetoothService not available!");
+    return nullptr;
+  }
   
   if (NS_FAILED(bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING("/"), manager))) {
     NS_ERROR("Failed to register object with observer!");
     return nullptr;
   }
   
   return manager.forget();
 }
--- a/dom/bluetooth/BluetoothManager.h
+++ b/dom/bluetooth/BluetoothManager.h
@@ -3,48 +3,51 @@
 /* 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_bluetooth_bluetoothmanager_h__
 #define mozilla_dom_bluetooth_bluetoothmanager_h__
 
 #include "BluetoothCommon.h"
+#include "BluetoothPropertyContainer.h"
 #include "nsDOMEventTargetHelper.h"
 #include "nsIDOMBluetoothManager.h"
 #include "mozilla/Observer.h"
 #include "nsIEventTarget.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
+class BluetoothNamedValue;
+
 class BluetoothManager : public nsDOMEventTargetHelper
                        , public nsIDOMBluetoothManager
                        , public BluetoothSignalObserver
+                       , public BluetoothPropertyContainer
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMBLUETOOTHMANAGER
 
   NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::)
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothManager,
                                            nsDOMEventTargetHelper)
 
 
   inline void SetEnabledInternal(bool aEnabled) {mEnabled = aEnabled;}
 
   static already_AddRefed<BluetoothManager>
   Create(nsPIDOMWindow* aWindow);
   void Notify(const BluetoothSignal& aData);
+  virtual void SetPropertyByValue(const BluetoothNamedValue& aValue);
 private:
-  BluetoothManager() {}
   BluetoothManager(nsPIDOMWindow* aWindow);
   ~BluetoothManager();
   bool mEnabled;
-  nsString mName;
 
   NS_DECL_EVENT_HANDLER(enabled)
 };
 
 END_BLUETOOTH_NAMESPACE
 
 nsresult NS_NewBluetoothManager(nsPIDOMWindow* aWindow,
                                 nsIDOMBluetoothManager** aBluetoothManager);
--- a/dom/bluetooth/BluetoothPropertyContainer.h
+++ b/dom/bluetooth/BluetoothPropertyContainer.h
@@ -4,17 +4,16 @@
  * 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_bluetooth_bluetoothpropertyobject_h__
 #define mozilla_dom_bluetooth_bluetoothpropertyobject_h__
 
 #include "BluetoothCommon.h"
 #include "BluetoothReplyRunnable.h"
-#include "mozilla/RefPtr.h"
 
 class nsIDOMDOMRequest;
 class nsIDOMWindow;
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothNamedValue;
 
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/BluetoothUtils.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=40: */
+/* 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 "BluetoothUtils.h"
+#include "jsapi.h"
+#include "nsTArray.h"
+#include "nsString.h"
+#include "mozilla/Scoped.h"
+
+nsresult
+mozilla::dom::bluetooth::StringArrayToJSArray(JSContext* aCx, JSObject* aGlobal,
+                                              const nsTArray<nsString>& aSourceArray,
+                                              JSObject** aResultArray)
+{
+  NS_ASSERTION(aCx, "Null context!");
+  NS_ASSERTION(aGlobal, "Null global!");
+
+  JSAutoRequest ar(aCx);
+  JSAutoEnterCompartment ac;
+  if (!ac.enter(aCx, aGlobal)) {
+    NS_WARNING("Failed to enter compartment!");
+    return NS_ERROR_FAILURE;
+  }
+
+  JSObject* arrayObj;
+
+  if (aSourceArray.IsEmpty()) {
+    arrayObj = JS_NewArrayObject(aCx, 0, nullptr);
+  } else {
+    uint32_t valLength = aSourceArray.Length();
+    mozilla::ScopedDeleteArray<jsval> valArray(new jsval[valLength]);
+    JS::AutoArrayRooter tvr(aCx, valLength, valArray);
+    for (PRUint32 index = 0; index < valLength; index++) {
+      JSString* s = JS_NewUCStringCopyN(aCx, aSourceArray[index].BeginReading(),
+                                        aSourceArray[index].Length());
+      if(!s) {
+        NS_WARNING("Memory allocation error!");
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+      valArray[index] = STRING_TO_JSVAL(s);
+    }
+    arrayObj = JS_NewArrayObject(aCx, valLength, valArray);
+  }
+
+  if (!arrayObj) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  // XXX This is not what Jonas wants. He wants it to be live.
+  // Followup at bug 717414
+  if (!JS_FreezeObject(aCx, arrayObj)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aResultArray = arrayObj;
+  return NS_OK;
+}
+
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/BluetoothUtils.h
@@ -0,0 +1,24 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=40: */
+/* 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_bluetooth_bluetoothutils_h__
+#define mozilla_dom_bluetooth_bluetoothutils_h__
+
+#include "BluetoothCommon.h"
+
+class JSContext;
+class JSObject;
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+nsresult
+StringArrayToJSArray(JSContext* aCx, JSObject* aGlobal,
+                     const nsTArray<nsString>& aSourceArray,
+                     JSObject** aResultArray);
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
--- a/dom/bluetooth/Makefile.in
+++ b/dom/bluetooth/Makefile.in
@@ -31,16 +31,17 @@ CPPSRCS += \
   BluetoothService.cpp \
   BluetoothManager.cpp \
   BluetoothAdapter.cpp \
   BluetoothDevice.cpp \
   BluetoothDeviceEvent.cpp \
   BluetoothPropertyEvent.cpp \
   BluetoothReplyRunnable.cpp \
   BluetoothPropertyContainer.cpp \
+  BluetoothUtils.cpp \
   $(NULL)
 
 XPIDLSRCS = \
   nsIDOMNavigatorBluetooth.idl \
   nsIDOMBluetoothManager.idl \
   nsIDOMBluetoothAdapter.idl \
   nsIDOMBluetoothDevice.idl \
   nsIDOMBluetoothDeviceEvent.idl \
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -135,17 +135,20 @@ public:
   {
   }
 
   NS_IMETHOD
   Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
     BluetoothService* bs = BluetoothService::Get();
-    MOZ_ASSERT(bs);
+    if (!bs) {
+      NS_WARNING("BluetoothService not available!");
+      return NS_ERROR_FAILURE;
+    }    
     return bs->DistributeSignal(mSignal);
   }  
 };
 
 bool
 IsDBusMessageError(DBusMessage* aMsg, nsAString& aError)
 {
   DBusError err;
--- a/dom/bluetooth/nsIDOMBluetoothAdapter.idl
+++ b/dom/bluetooth/nsIDOMBluetoothAdapter.idl
@@ -4,32 +4,38 @@
  * 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 nsIDOMDOMRequest;
 interface nsIDOMBluetoothDevice;
 
-[scriptable, builtinclass, uuid(48df7f05-2bbc-4ac8-aa88-9fecd4c24028)]
+[scriptable, builtinclass, uuid(86e9fe78-ce64-476e-a357-333f7d3c8980)]
 interface nsIDOMBluetoothAdapter : nsIDOMEventTarget
 {
   readonly attribute DOMString address;
   [binaryname(AdapterClass)] readonly attribute unsigned long class;
   readonly attribute bool enabled;
   readonly attribute bool discovering;
 
   [implicit_jscontext]
   readonly attribute jsval devices;
 
+  [implicit_jscontext]
+  readonly attribute jsval uuids;
+  
   readonly attribute DOMString name;
   readonly attribute bool discoverable;
   // Unit: sec
   readonly attribute unsigned long discoverableTimeout;
 
+  nsIDOMDOMRequest setName(in DOMString name);
+  nsIDOMDOMRequest setDiscoverable(in bool discoverable);
+  nsIDOMDOMRequest setDiscoverableTimeout(in unsigned long timeout);
   nsIDOMDOMRequest startDiscovery();
   nsIDOMDOMRequest stopDiscovery();
   // Fired when discoverying and any device is discovered.
   attribute nsIDOMEventListener ondevicefound;
   // Fired when any device is out of discoverable range.
   attribute nsIDOMEventListener ondevicedisappeared;
   // Fired when a property of the adapter is changed
   attribute nsIDOMEventListener onpropertychanged;
--- a/dom/bluetooth/nsIDOMBluetoothDevice.idl
+++ b/dom/bluetooth/nsIDOMBluetoothDevice.idl
@@ -1,19 +1,19 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=40: */
 /* 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"
 
-[scriptable, builtinclass, uuid(2c446123-b5dd-4631-80f6-eda91befd8c9)]
+[scriptable, builtinclass, uuid(24c64513-9587-46c6-b718-bb9b9a754b0d)]
 interface nsIDOMBluetoothDevice : nsIDOMEventTarget
 {
   readonly attribute DOMString address;
   readonly attribute DOMString name;
-
   [binaryname(DeviceClass)] readonly attribute unsigned long class;
+  [implicit_jscontext] readonly attribute jsval uuids;
   readonly attribute bool connected;
   readonly attribute bool paired;
   attribute nsIDOMEventListener onpropertychanged;
 };