Backed out changeset ed586ca080c0 (bug 1053966) for the same Mnw permafails it was backed out for previously.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 16 Sep 2014 16:54:25 -0400
changeset 205596 52fce004492f36e068ae95fbbfd708a54bf7c5d9
parent 205595 051b1bd8b013604d6dbd4b6fddea0abb5887e363
child 205597 49ef7b18963dd0722004be46d4bb14ac12aa3c1c
child 205723 4a8aefdda5d4ab0bbc8277d58eb182b923b6fb8e
push id8780
push userkwierso@gmail.com
push dateWed, 17 Sep 2014 00:23:50 +0000
treeherderfx-team@f1473daa91ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1053966
milestone35.0a1
backs outed586ca080c06f266b92954fdbd2d614399f4660
Backed out changeset ed586ca080c0 (bug 1053966) for the same Mnw permafails it was backed out for previously.
b2g/chrome/content/payment.js
dom/audiochannel/AudioChannelService.cpp
dom/bindings/BindingUtils.h
dom/bluetooth/BluetoothService.cpp
dom/bluetooth/BluetoothService.h
dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.h
dom/bluetooth/bluez/BluetoothHfpManager.cpp
dom/bluetooth/bluez/BluetoothHfpManager.h
dom/bluetooth2/BluetoothService.cpp
dom/bluetooth2/BluetoothService.h
dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.h
dom/bluetooth2/bluez/BluetoothHfpManager.cpp
dom/bluetooth2/bluez/BluetoothHfpManager.h
dom/fmradio/FMRadioService.cpp
dom/geolocation/nsGeolocation.cpp
dom/geolocation/nsGeolocation.h
dom/settings/SettingsRequestManager.jsm
dom/system/NetworkGeolocationProvider.js
dom/system/gonk/AudioManager.cpp
dom/system/gonk/AutoMounterSetting.cpp
dom/system/gonk/NetworkManager.js
dom/system/gonk/RadioInterfaceLayer.js
dom/system/gonk/TimeZoneSettingObserver.cpp
dom/webidl/SettingChangeNotification.webidl
dom/webidl/moz.build
dom/wifi/WifiWorker.js
toolkit/devtools/discovery/discovery.js
--- a/b2g/chrome/content/payment.js
+++ b/b2g/chrome/content/payment.js
@@ -173,22 +173,23 @@ PaymentSettings.prototype = {
   },
 
   observe: function(aSubject, aTopic, aData) {
     if (aTopic != kMozSettingsChangedObserverTopic) {
       return;
     }
 
     try {
-      if (!aSubject.key ||
-          (aSubject.key !== kRilDefaultDataServiceId &&
-           aSubject.key !== kRilDefaultPaymentServiceId)) {
+      let setting = JSON.parse(aData);
+      if (!setting.key ||
+          (setting.key !== kRilDefaultDataServiceId &&
+           setting.key !== kRilDefaultPaymentServiceId)) {
         return;
       }
-      this.setServiceId(aSubject.key, aSubject.value);
+      this.setServiceId(setting.key, setting.value);
     } catch (e) {
       LOGE(e);
     }
   },
 
   cleanup: function() {
     Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
   }
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -15,17 +15,16 @@
 
 #include "mozilla/dom/ContentParent.h"
 
 #include "nsThreadUtils.h"
 #include "nsHashPropertyBag.h"
 #include "nsComponentManagerUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsJSUtils.h"
 #include "nsIAudioManager.h"
 #include "SpeakerManagerService.h"
 #define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
 #endif
 
@@ -797,43 +796,58 @@ AudioChannelService::Observe(nsISupports
     } else {
       NS_WARNING("ipc:content-shutdown message without childID property");
     }
   }
 #ifdef MOZ_WIDGET_GONK
   // To process the volume control on each audio channel according to
   // change of settings
   else if (!strcmp(aTopic, "mozsettings-changed")) {
-    AutoJSAPI jsapi;
-    jsapi.Init();
-    JSContext* cx = jsapi.cx();
-    RootedDictionary<SettingChangeNotification> setting(cx);
-    if (!WrappedJSToDictionary(cx, aSubject, setting)) {
+    AutoSafeJSContext cx;
+    nsDependentString dataStr(aData);
+    JS::Rooted<JS::Value> val(cx);
+    if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
+        !val.isObject()) {
+      return NS_OK;
+    }
+
+    JS::Rooted<JSObject*> obj(cx, &val.toObject());
+    JS::Rooted<JS::Value> key(cx);
+    if (!JS_GetProperty(cx, obj, "key", &key) ||
+        !key.isString()) {
       return NS_OK;
     }
-    if (!StringBeginsWith(setting.mKey, NS_LITERAL_STRING("audio.volume."))) {
+
+    JS::Rooted<JSString*> jsKey(cx, JS::ToString(cx, key));
+    if (!jsKey) {
       return NS_OK;
     }
-    if (!setting.mValue.isNumber()) {
+    nsAutoJSString keyStr;
+    if (!keyStr.init(cx, jsKey) || keyStr.Find("audio.volume.", 0, false)) {
       return NS_OK;
     }
-    
+
+    JS::Rooted<JS::Value> value(cx);
+    if (!JS_GetProperty(cx, obj, "value", &value) || !value.isInt32()) {
+      return NS_OK;
+    }
+
     nsCOMPtr<nsIAudioManager> audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID);
     NS_ENSURE_TRUE(audioManager, NS_OK);
 
-    int32_t index = setting.mValue.toNumber();
-    if (setting.mKey.EqualsLiteral("audio.volume.content")) {
+    int32_t index = value.toInt32();
+    if (keyStr.EqualsLiteral("audio.volume.content")) {
       audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Content, index);
-    } else if (setting.mKey.EqualsLiteral("audio.volume.notification")) {
+    } else if (keyStr.EqualsLiteral("audio.volume.notification")) {
       audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Notification, index);
-    } else if (setting.mKey.EqualsLiteral("audio.volume.alarm")) {
+    } else if (keyStr.EqualsLiteral("audio.volume.alarm")) {
       audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Alarm, index);
-    } else if (setting.mKey.EqualsLiteral("audio.volume.telephony")) {
+    } else if (keyStr.EqualsLiteral("audio.volume.telephony")) {
       audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Telephony, index);
-    } else if (!setting.mKey.EqualsLiteral("audio.volume.bt_sco")) {
+    } else if (!keyStr.EqualsLiteral("audio.volume.bt_sco")) {
       // bt_sco is not a valid audio channel so we manipulate it in
       // AudioManager.cpp. And the others should not be used.
       // We didn't use MOZ_CRASH or MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE here
       // because any web content who has permission of mozSettings can set any
       // names then it can be easy to crash the B2G.
       NS_WARNING("unexpected audio channel for volume control");
     }
   }
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2954,31 +2954,35 @@ CallerSubsumes(JS::Handle<JS::Value> aVa
   if (!aValue.isObject()) {
     return true;
   }
   return CallerSubsumes(&aValue.toObject());
 }
 
 template<class T>
 inline bool
-WrappedJSToDictionary(JSContext* aCx, nsISupports* aObject, T& aDictionary)
+WrappedJSToDictionary(nsISupports* aObject, T& aDictionary)
 {
   nsCOMPtr<nsIXPConnectWrappedJS> wrappedObj = do_QueryInterface(aObject);
   if (!wrappedObj) {
     return false;
   }
 
-  JS::Rooted<JSObject*> obj(aCx, wrappedObj->GetJSObject());
+  AutoJSAPI jsapi;
+  jsapi.Init();
+
+  JSContext* cx = jsapi.cx();
+  JS::Rooted<JSObject*> obj(cx, wrappedObj->GetJSObject());
   if (!obj) {
     return false;
   }
 
-  JSAutoCompartment ac(aCx, obj);
-  JS::Rooted<JS::Value> v(aCx, JS::ObjectValue(*obj));
-  return aDictionary.Init(aCx, v);
+  JSAutoCompartment ac(cx, obj);
+  JS::Rooted<JS::Value> v(cx, OBJECT_TO_JSVAL(obj));
+  return aDictionary.Init(cx, v);
 }
 
 
 template<class T, class S>
 inline nsRefPtr<T>
 StrongOrRawPtr(already_AddRefed<S>&& aPtr)
 {
   return aPtr.template downcast<T>();
--- a/dom/bluetooth/BluetoothService.cpp
+++ b/dom/bluetooth/BluetoothService.cpp
@@ -31,17 +31,16 @@
 #include "mozilla/ipc/UnixSocket.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsITimer.h"
 #include "nsServiceManagerUtils.h"
 #include "nsXPCOM.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #if defined(MOZ_WIDGET_GONK)
 #include "cutils/properties.h"
 #endif
 
 #if defined(MOZ_B2G_BT)
 #if defined(MOZ_B2G_BT_BLUEZ)
 /**
@@ -558,54 +557,101 @@ BluetoothService::HandleStartup()
 nsresult
 BluetoothService::HandleStartupSettingsCheck(bool aEnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   return StartStopBluetooth(aEnable, true);
 }
 
 nsresult
-BluetoothService::HandleSettingsChanged(nsISupports* aSubject)
+BluetoothService::HandleSettingsChanged(const nsAString& aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // The string that we're interested in will be a JSON string that looks like:
   //  {"key":"bluetooth.enabled","value":true}
 
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  JSContext* cx = jsapi.cx();
-  RootedDictionary<SettingChangeNotification> setting(cx);
-  if (!WrappedJSToDictionary(cx, aSubject, setting)) {
+  AutoSafeJSContext cx;
+  if (!cx) {
+    return NS_OK;
+  }
+
+  JS::Rooted<JS::Value> val(cx);
+  if (!JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val)) {
+    return JS_ReportPendingException(cx) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (!val.isObject()) {
     return NS_OK;
   }
-  if (setting.mKey.EqualsASCII(BLUETOOTH_DEBUGGING_SETTING)) {
-    if (!setting.mValue.isBoolean()) {
+
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
+
+  JS::Rooted<JS::Value> key(cx);
+  if (!JS_GetProperty(cx, obj, "key", &key)) {
+    MOZ_ASSERT(!JS_IsExceptionPending(cx));
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (!key.isString()) {
+    return NS_OK;
+  }
+
+  // First, check if the string equals to BLUETOOTH_DEBUGGING_SETTING
+  bool match;
+  if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_DEBUGGING_SETTING, &match)) {
+    MOZ_ASSERT(!JS_IsExceptionPending(cx));
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (match) {
+    JS::Rooted<JS::Value> value(cx);
+    if (!JS_GetProperty(cx, obj, "value", &value)) {
+      MOZ_ASSERT(!JS_IsExceptionPending(cx));
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    if (!value.isBoolean()) {
       MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.debugging.enabled'!");
       return NS_ERROR_UNEXPECTED;
     }
-  
-    SWITCH_BT_DEBUG(setting.mValue.toBoolean());
+
+    SWITCH_BT_DEBUG(value.toBoolean());
 
     return NS_OK;
   }
 
   // Second, check if the string is BLUETOOTH_ENABLED_SETTING
-  if (!setting.mKey.EqualsASCII(BLUETOOTH_ENABLED_SETTING)) {
-    return NS_OK;
-  }
-  if (!setting.mValue.isBoolean()) {
-    MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.enabled'!");
-    return NS_ERROR_UNEXPECTED;
+  if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_ENABLED_SETTING, &match)) {
+    MOZ_ASSERT(!JS_IsExceptionPending(cx));
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  sToggleInProgress = true;
+  if (match) {
+    JS::Rooted<JS::Value> value(cx);
+    if (!JS_GetProperty(cx, obj, "value", &value)) {
+      MOZ_ASSERT(!JS_IsExceptionPending(cx));
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
 
-  nsresult rv = StartStopBluetooth(setting.mValue.toBoolean(), false);
-  NS_ENSURE_SUCCESS(rv, rv);
+    if (!value.isBoolean()) {
+      MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.enabled'!");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    if (sToggleInProgress || value.toBoolean() == IsEnabled()) {
+      // Nothing to do here.
+      return NS_OK;
+    }
+
+    sToggleInProgress = true;
+
+    nsresult rv = StartStopBluetooth(value.toBoolean(), false);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
   return NS_OK;
 }
 
 nsresult
 BluetoothService::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -706,17 +752,17 @@ BluetoothService::Observe(nsISupports* a
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!strcmp(aTopic, "profile-after-change")) {
     return HandleStartup();
   }
 
   if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
-    return HandleSettingsChanged(aSubject);
+    return HandleSettingsChanged(nsDependentString(aData));
   }
 
   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     return HandleShutdown();
   }
 
   MOZ_ASSERT(false, "BluetoothService got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
--- a/dom/bluetooth/BluetoothService.h
+++ b/dom/bluetooth/BluetoothService.h
@@ -372,17 +372,17 @@ protected:
    */
   nsresult
   HandleStartupSettingsCheck(bool aEnable);
 
   /**
    * Called when "mozsettings-changed" observer topic fires.
    */
   nsresult
-  HandleSettingsChanged(nsISupports* aSubject);
+  HandleSettingsChanged(const nsAString& aData);
 
   /**
    * Called when XPCOM is shutting down.
    */
   virtual nsresult
   HandleShutdown();
 
   // Called by ToggleBtAck.
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -22,18 +22,16 @@
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsITelephonyService.h"
 #include "nsRadioInterfaceLayer.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
-#include "mozilla/dom/BindingUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #define MOZSETTINGS_CHANGED_ID               "mozsettings-changed"
 #define AUDIO_VOLUME_BT_SCO_ID               "audio.volume.bt_sco"
 
 /**
  * Dispatch task with arguments to main thread.
  */
 using namespace mozilla;
@@ -459,17 +457,17 @@ BluetoothHfpManager::Get()
 }
 
 NS_IMETHODIMP
 BluetoothHfpManager::Observe(nsISupports* aSubject,
                              const char* aTopic,
                              const char16_t* aData)
 {
   if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
-    HandleVolumeChanged(aSubject);
+    HandleVolumeChanged(nsDependentString(aData));
   } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     HandleShutdown();
   } else {
     MOZ_ASSERT(false, "BluetoothHfpManager got unexpected topic!");
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
@@ -558,38 +556,49 @@ public:
   void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
     BT_WARNING("BluetoothHandsfreeInterface::VolumeControl failed: %d",
                (int)aStatus);
   }
 };
 
 void
-BluetoothHfpManager::HandleVolumeChanged(nsISupports* aSubject)
+BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // The string that we're interested in will be a JSON string that looks like:
   //  {"key":"volumeup", "value":10}
   //  {"key":"volumedown", "value":2}
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  JSContext* cx = jsapi.cx();
-  RootedDictionary<dom::SettingChangeNotification> setting(cx);
-  if (!WrappedJSToDictionary(cx, aSubject, setting)) {
-    return;
-  }
-  if (!setting.mKey.EqualsASCII(AUDIO_VOLUME_BT_SCO_ID)) {
-    return;
-  }
-  if (!setting.mValue.isNumber()) {
+  JSContext* cx = nsContentUtils::GetSafeJSContext();
+  NS_ENSURE_TRUE_VOID(cx);
+
+  JS::Rooted<JS::Value> val(cx);
+  NS_ENSURE_TRUE_VOID(JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val));
+  NS_ENSURE_TRUE_VOID(val.isObject());
+
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
+  JS::Rooted<JS::Value> key(cx);
+  if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) {
     return;
   }
 
-  mCurrentVgs = setting.mValue.toNumber();
+  bool match;
+  if (!JS_StringEqualsAscii(cx, key.toString(), AUDIO_VOLUME_BT_SCO_ID, &match) ||
+      !match) {
+    return;
+  }
+
+  JS::Rooted<JS::Value> value(cx);
+  if (!JS_GetProperty(cx, obj, "value", &value) ||
+      !value.isNumber()) {
+    return;
+  }
+
+  mCurrentVgs = value.toNumber();
 
   // Adjust volume by headset and we don't have to send volume back to headset
   if (mReceiveVgsFlag) {
     mReceiveVgsFlag = false;
     return;
   }
 
   // Only send volume back when there's a connected headset
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.h
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.h
@@ -144,17 +144,17 @@ private:
   friend class RespondToBLDNTask;
   friend class MainThreadTask;
 
   BluetoothHfpManager();
   bool Init();
   void Cleanup();
 
   void HandleShutdown();
-  void HandleVolumeChanged(nsISupports* aSubject);
+  void HandleVolumeChanged(const nsAString& aData);
   void Notify(const hal::BatteryInformation& aBatteryInfo);
 
   void NotifyConnectionStateChanged(const nsAString& aType);
   void NotifyDialer(const nsAString& aCommand);
 
   PhoneType GetPhoneType(const nsAString& aType);
   void ResetCallArray();
   uint32_t FindFirstCall(uint16_t aState);
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -18,18 +18,16 @@
 #include "jsapi.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsServiceManagerUtils.h"
-#include "mozilla/dom/BindingUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #ifdef MOZ_B2G_RIL
 #include "nsIDOMIccInfo.h"
 #include "nsIIccProvider.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsITelephonyService.h"
@@ -202,17 +200,17 @@ NS_IMPL_ISUPPORTS(BluetoothHfpManager::G
                   nsISettingsServiceCallback);
 
 NS_IMETHODIMP
 BluetoothHfpManager::Observe(nsISupports* aSubject,
                              const char* aTopic,
                              const char16_t* aData)
 {
   if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
-    HandleVolumeChanged(aSubject);
+    HandleVolumeChanged(nsDependentString(aData));
   } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     HandleShutdown();
   } else {
     MOZ_ASSERT(false, "BluetoothHfpManager got unexpected topic!");
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
@@ -552,39 +550,50 @@ BluetoothHfpManager::NotifyDialer(const 
 
   if (!BroadcastSystemMessage(type, parameters)) {
     BT_WARNING("Failed to broadcast system message to dialer");
   }
 }
 #endif // MOZ_B2G_RIL
 
 void
-BluetoothHfpManager::HandleVolumeChanged(nsISupports* aSubject)
+BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // The string that we're interested in will be a JSON string that looks like:
   //  {"key":"volumeup", "value":10}
   //  {"key":"volumedown", "value":2}
 
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  JSContext* cx = jsapi.cx();
-  RootedDictionary<SettingChangeNotification> setting(cx);
-  if (!WrappedJSToDictionary(cx, aSubject, setting)) {
-    return;
-  }
-  if (!setting.mKey.EqualsASCII(AUDIO_VOLUME_BT_SCO_ID)) {
-    return;
-  }
-  if (!setting.mValue.isNumber()) {
+  JSContext* cx = nsContentUtils::GetSafeJSContext();
+  NS_ENSURE_TRUE_VOID(cx);
+
+  JS::Rooted<JS::Value> val(cx);
+  NS_ENSURE_TRUE_VOID(JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val));
+  NS_ENSURE_TRUE_VOID(val.isObject());
+
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
+  JS::Rooted<JS::Value> key(cx);
+  if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) {
     return;
   }
 
-  mCurrentVgs = setting.mValue.toNumber();
+  bool match;
+  if (!JS_StringEqualsAscii(cx, key.toString(), AUDIO_VOLUME_BT_SCO_ID, &match) ||
+      !match) {
+    return;
+  }
+
+  JS::Rooted<JS::Value> value(cx);
+  if (!JS_GetProperty(cx, obj, "value", &value)||
+      !value.isNumber()) {
+    return;
+  }
+
+  mCurrentVgs = value.toNumber();
 
   // Adjust volume by headset and we don't have to send volume back to headset
   if (mReceiveVgsFlag) {
     mReceiveVgsFlag = false;
     return;
   }
 
   // Only send volume back when there's a connected headset
--- a/dom/bluetooth/bluez/BluetoothHfpManager.h
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.h
@@ -144,17 +144,17 @@ private:
 #ifdef MOZ_B2G_RIL
   friend class RespondToBLDNTask;
   friend class SendRingIndicatorTask;
 #endif
   friend class BluetoothHfpManagerObserver;
 
   BluetoothHfpManager();
   void HandleShutdown();
-  void HandleVolumeChanged(nsISupports* aSubject);
+  void HandleVolumeChanged(const nsAString& aData);
 
   bool Init();
   void Notify(const hal::BatteryInformation& aBatteryInfo);
 #ifdef MOZ_B2G_RIL
   void ResetCallArray();
   uint32_t FindFirstCall(uint16_t aState);
   uint32_t GetNumberOfCalls(uint16_t aState);
   uint32_t GetNumberOfConCalls();
--- a/dom/bluetooth2/BluetoothService.cpp
+++ b/dom/bluetooth2/BluetoothService.cpp
@@ -30,17 +30,16 @@
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsITimer.h"
 #include "nsServiceManagerUtils.h"
 #include "nsXPCOM.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #if defined(MOZ_WIDGET_GONK)
 #include "cutils/properties.h"
 #endif
 
 #if defined(MOZ_B2G_BT)
 #if defined(MOZ_B2G_BT_BLUEZ)
 /**
@@ -520,39 +519,70 @@ BluetoothService::HandleStartup()
 nsresult
 BluetoothService::HandleStartupSettingsCheck(bool aEnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   return StartStopBluetooth(aEnable, true, nullptr);
 }
 
 nsresult
-BluetoothService::HandleSettingsChanged(nsISupports* aSubject)
+BluetoothService::HandleSettingsChanged(const nsAString& aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // The string that we're interested in will be a JSON string that looks like:
   //  {"key":"bluetooth.enabled","value":true}
 
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  JSContext* cx = jsapi.cx();
-  RootedDictionary<SettingChangeNotification> setting(cx);
-  if (!WrappedJSToDictionary(cx, aSubject, setting)) {
+  AutoSafeJSContext cx;
+  if (!cx) {
+    return NS_OK;
+  }
+
+  JS::Rooted<JS::Value> val(cx);
+  if (!JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val)) {
+    return JS_ReportPendingException(cx) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (!val.isObject()) {
     return NS_OK;
   }
-  if (!setting.mKey.EqualsASCII(BLUETOOTH_DEBUGGING_SETTING)) {
+
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
+
+  JS::Rooted<JS::Value> key(cx);
+  if (!JS_GetProperty(cx, obj, "key", &key)) {
+    MOZ_ASSERT(!JS_IsExceptionPending(cx));
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (!key.isString()) {
     return NS_OK;
   }
-  if (!setting.mValue.isBoolean()) {
-    MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.debugging.enabled'!");
-    return NS_ERROR_UNEXPECTED;
+
+  // Check whether the string is BLUETOOTH_DEBUGGING_SETTING
+  bool match;
+  if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_DEBUGGING_SETTING, &match)) {
+    MOZ_ASSERT(!JS_IsExceptionPending(cx));
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  SWITCH_BT_DEBUG(setting.mValue.toBoolean());
+  if (match) {
+    JS::Rooted<JS::Value> value(cx);
+    if (!JS_GetProperty(cx, obj, "value", &value)) {
+      MOZ_ASSERT(!JS_IsExceptionPending(cx));
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    if (!value.isBoolean()) {
+      MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.debugging.enabled'!");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    SWITCH_BT_DEBUG(value.toBoolean());
+  }
 
   return NS_OK;
 }
 
 nsresult
 BluetoothService::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -653,17 +683,17 @@ BluetoothService::Observe(nsISupports* a
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!strcmp(aTopic, "profile-after-change")) {
     return HandleStartup();
   }
 
   if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
-    return HandleSettingsChanged(aSubject);
+    return HandleSettingsChanged(nsDependentString(aData));
   }
 
   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     return HandleShutdown();
   }
 
   MOZ_ASSERT(false, "BluetoothService got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
--- a/dom/bluetooth2/BluetoothService.h
+++ b/dom/bluetooth2/BluetoothService.h
@@ -371,17 +371,17 @@ protected:
    */
   nsresult
   HandleStartupSettingsCheck(bool aEnable);
 
   /**
    * Called when "mozsettings-changed" observer topic fires.
    */
   nsresult
-  HandleSettingsChanged(nsISupports* aSubject);
+  HandleSettingsChanged(const nsAString& aData);
 
   /**
    * Called when XPCOM is shutting down.
    */
   virtual nsresult
   HandleShutdown();
 
   // Called by ToggleBtAck.
--- a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -22,17 +22,16 @@
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsITelephonyService.h"
 #include "nsRadioInterfaceLayer.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #define MOZSETTINGS_CHANGED_ID               "mozsettings-changed"
 #define AUDIO_VOLUME_BT_SCO_ID               "audio.volume.bt_sco"
 
 using namespace mozilla;
 using namespace mozilla::ipc;
 USING_BLUETOOTH_NAMESPACE
 
@@ -461,17 +460,17 @@ BluetoothHfpManager::Get()
 }
 
 NS_IMETHODIMP
 BluetoothHfpManager::Observe(nsISupports* aSubject,
                              const char* aTopic,
                              const char16_t* aData)
 {
   if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
-    HandleVolumeChanged(aSubject);
+    HandleVolumeChanged(nsDependentString(aData));
   } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     HandleShutdown();
   } else {
     MOZ_ASSERT(false, "BluetoothHfpManager got unexpected topic!");
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
@@ -560,39 +559,49 @@ public:
   void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
     BT_WARNING("BluetoothHandsfreeInterface::VolumeControl failed: %d",
                (int)aStatus);
   }
 };
 
 void
-BluetoothHfpManager::HandleVolumeChanged(nsISupports* aSubject)
+BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // The string that we're interested in will be a JSON string that looks like:
   //  {"key":"volumeup", "value":10}
   //  {"key":"volumedown", "value":2}
+  JSContext* cx = nsContentUtils::GetSafeJSContext();
+  NS_ENSURE_TRUE_VOID(cx);
 
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  JSContext* cx = jsapi.cx();
-  RootedDictionary<dom::SettingChangeNotification> setting(cx);
-  if (!WrappedJSToDictionary(cx, aSubject, setting)) {
-    return;
-  }
-  if (!setting.mKey.EqualsASCII(AUDIO_VOLUME_BT_SCO_ID)) {
-    return;
-  }
-  if (!setting.mValue.isNumber()) {
+  JS::Rooted<JS::Value> val(cx);
+  NS_ENSURE_TRUE_VOID(JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val));
+  NS_ENSURE_TRUE_VOID(val.isObject());
+
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
+  JS::Rooted<JS::Value> key(cx);
+  if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) {
     return;
   }
 
-  mCurrentVgs = setting.mValue.toNumber();
+  bool match;
+  if (!JS_StringEqualsAscii(cx, key.toString(), AUDIO_VOLUME_BT_SCO_ID, &match) ||
+      !match) {
+    return;
+  }
+
+  JS::Rooted<JS::Value> value(cx);
+  if (!JS_GetProperty(cx, obj, "value", &value) ||
+      !value.isNumber()) {
+    return;
+  }
+
+  mCurrentVgs = value.toNumber();
 
   // Adjust volume by headset and we don't have to send volume back to headset
   if (mReceiveVgsFlag) {
     mReceiveVgsFlag = false;
     return;
   }
 
   // Only send volume back when there's a connected headset
--- a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.h
+++ b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.h
@@ -142,17 +142,17 @@ private:
   friend class CloseScoTask;
   friend class RespondToBLDNTask;
   friend class MainThreadTask;
 
   BluetoothHfpManager();
   bool Init();
 
   void HandleShutdown();
-  void HandleVolumeChanged(nsISupports* aSubject);
+  void HandleVolumeChanged(const nsAString& aData);
   void Notify(const hal::BatteryInformation& aBatteryInfo);
 
   void NotifyConnectionStateChanged(const nsAString& aType);
   void NotifyDialer(const nsAString& aCommand);
 
   PhoneType GetPhoneType(const nsAString& aType);
   void ResetCallArray();
   uint32_t FindFirstCall(uint16_t aState);
--- a/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
@@ -18,17 +18,16 @@
 #include "jsapi.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsServiceManagerUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #ifdef MOZ_B2G_RIL
 #include "nsIDOMIccInfo.h"
 #include "nsIIccProvider.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsITelephonyService.h"
@@ -201,17 +200,17 @@ NS_IMPL_ISUPPORTS(BluetoothHfpManager::G
                   nsISettingsServiceCallback);
 
 NS_IMETHODIMP
 BluetoothHfpManager::Observe(nsISupports* aSubject,
                              const char* aTopic,
                              const char16_t* aData)
 {
   if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
-    HandleVolumeChanged(aSubject);
+    HandleVolumeChanged(nsDependentString(aData));
   } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     HandleShutdown();
   } else {
     MOZ_ASSERT(false, "BluetoothHfpManager got unexpected topic!");
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
@@ -551,39 +550,50 @@ BluetoothHfpManager::NotifyDialer(const 
 
   if (!BroadcastSystemMessage(type, parameters)) {
     BT_WARNING("Failed to broadcast system message to dialer");
   }
 }
 #endif // MOZ_B2G_RIL
 
 void
-BluetoothHfpManager::HandleVolumeChanged(nsISupports* aSubject)
+BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // The string that we're interested in will be a JSON string that looks like:
   //  {"key":"volumeup", "value":10}
   //  {"key":"volumedown", "value":2}
 
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  JSContext* cx = jsapi.cx();
-  RootedDictionary<dom::SettingChangeNotification> setting(cx);
-  if (!WrappedJSToDictionary(cx, aSubject, setting)) {
-    return;
-  }
-  if (!setting.mKey.EqualsASCII(AUDIO_VOLUME_BT_SCO_ID)) {
-    return;
-  }
-  if (!setting.mValue.isNumber()) {
+  JSContext* cx = nsContentUtils::GetSafeJSContext();
+  NS_ENSURE_TRUE_VOID(cx);
+
+  JS::Rooted<JS::Value> val(cx);
+  NS_ENSURE_TRUE_VOID(JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val));
+  NS_ENSURE_TRUE_VOID(val.isObject());
+
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
+  JS::Rooted<JS::Value> key(cx);
+  if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) {
     return;
   }
 
-  mCurrentVgs = setting.mValue.toNumber();
+  bool match;
+  if (!JS_StringEqualsAscii(cx, key.toString(), AUDIO_VOLUME_BT_SCO_ID, &match) ||
+      !match) {
+    return;
+  }
+
+  JS::Rooted<JS::Value> value(cx);
+  if (!JS_GetProperty(cx, obj, "value", &value)||
+      !value.isNumber()) {
+    return;
+  }
+
+  mCurrentVgs = value.toNumber();
 
   // Adjust volume by headset and we don't have to send volume back to headset
   if (mReceiveVgsFlag) {
     mReceiveVgsFlag = false;
     return;
   }
 
   // Only send volume back when there's a connected headset
--- a/dom/bluetooth2/bluez/BluetoothHfpManager.h
+++ b/dom/bluetooth2/bluez/BluetoothHfpManager.h
@@ -144,17 +144,17 @@ private:
 #ifdef MOZ_B2G_RIL
   friend class RespondToBLDNTask;
   friend class SendRingIndicatorTask;
 #endif
   friend class BluetoothHfpManagerObserver;
 
   BluetoothHfpManager();
   void HandleShutdown();
-  void HandleVolumeChanged(nsISupports* aSubject);
+  void HandleVolumeChanged(const nsAString& aData);
 
   bool Init();
   void Notify(const hal::BatteryInformation& aBatteryInfo);
 #ifdef MOZ_B2G_RIL
   void ResetCallArray();
   uint32_t FindFirstCall(uint16_t aState);
   uint32_t GetNumberOfCalls(uint16_t aState);
   uint32_t GetNumberOfConCalls();
--- a/dom/fmradio/FMRadioService.cpp
+++ b/dom/fmradio/FMRadioService.cpp
@@ -11,18 +11,16 @@
 #include "AudioManager.h"
 #include "nsDOMClassInfo.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/FMRadioChild.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsJSUtils.h"
-#include "mozilla/dom/BindingUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #define BAND_87500_108000_kHz 1
 #define BAND_76000_108000_kHz 2
 #define BAND_76000_90000_kHz  3
 
 #define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
 #define SETTING_KEY_AIRPLANEMODE_ENABLED "airplaneMode.enabled"
 
@@ -675,49 +673,70 @@ FMRadioService::CancelSeek(FMRadioReplyR
   TransitionState(
     ErrorResponse(NS_LITERAL_STRING("Seek action is cancelled")), Enabled);
 
   aReplyRunnable->SetReply(SuccessResponse());
   NS_DispatchToMainThread(aReplyRunnable);
 }
 
 NS_IMETHODIMP
-FMRadioService::Observe(nsISupports* aSubject,
-                        const char* aTopic,
-                        const char16_t* aData)
+FMRadioService::Observe(nsISupports * aSubject,
+                        const char * aTopic,
+                        const char16_t * aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(sFMRadioService);
 
   if (strcmp(aTopic, MOZSETTINGS_CHANGED_ID) != 0) {
     return NS_OK;
   }
 
   // The string that we're interested in will be a JSON string looks like:
   //  {"key":"airplaneMode.enabled","value":true}
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  JSContext* cx = jsapi.cx();
-  RootedDictionary<dom::SettingChangeNotification> setting(cx);
-  if (!WrappedJSToDictionary(cx, aSubject, setting)) {
+  AutoSafeJSContext cx;
+  const nsDependentString dataStr(aData);
+  JS::Rooted<JS::Value> val(cx);
+  if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
+      !val.isObject()) {
+    NS_WARNING("Bad JSON string format.");
     return NS_OK;
   }
-  if (!setting.mKey.EqualsASCII(SETTING_KEY_AIRPLANEMODE_ENABLED)) {
-    return NS_OK;
-  }
-  if (!setting.mValue.isBoolean()) {
+
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
+  JS::Rooted<JS::Value> key(cx);
+  if (!JS_GetProperty(cx, obj, "key", &key) ||
+      !key.isString()) {
+    NS_WARNING("Failed to get string property `key`.");
     return NS_OK;
   }
 
-  mAirplaneModeEnabled = setting.mValue.toBoolean();
-  mHasReadAirplaneModeSetting = true;
+  JS::Rooted<JSString*> jsKey(cx, key.toString());
+  nsAutoJSString keyStr;
+  if (!keyStr.init(cx, jsKey)) {
+    return NS_OK;
+  }
 
-  // Disable the FM radio HW if Airplane mode is enabled.
-  if (mAirplaneModeEnabled) {
-    Disable(nullptr);
+  if (keyStr.EqualsLiteral(SETTING_KEY_AIRPLANEMODE_ENABLED)) {
+    JS::Rooted<JS::Value> value(cx);
+    if (!JS_GetProperty(cx, obj, "value", &value)) {
+      NS_WARNING("Failed to get property `value`.");
+      return NS_OK;
+    }
+
+    if (!value.isBoolean()) {
+      return NS_OK;
+    }
+
+    mAirplaneModeEnabled = value.toBoolean();
+    mHasReadAirplaneModeSetting = true;
+
+    // Disable the FM radio HW if Airplane mode is enabled.
+    if (mAirplaneModeEnabled) {
+      Disable(nullptr);
+    }
   }
 
   return NS_OK;
 }
 
 void
 FMRadioService::NotifyFMRadioEvent(FMRadioEventType aType)
 {
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -19,17 +19,16 @@
 #include "nsIObserverService.h"
 #include "nsPIDOMWindow.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 class nsIPrincipal;
 
 #ifdef MOZ_ENABLE_QT5GEOPOSITION
 #include "QTMLocationProvider.h"
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
@@ -708,36 +707,46 @@ nsresult nsGeolocationService::Init()
   return NS_OK;
 }
 
 nsGeolocationService::~nsGeolocationService()
 {
 }
 
 void
-nsGeolocationService::HandleMozsettingChanged(nsISupports* aSubject)
+nsGeolocationService::HandleMozsettingChanged(const char16_t* aData)
 {
     // The string that we're interested in will be a JSON string that looks like:
     //  {"key":"gelocation.enabled","value":true}
 
-    AutoJSAPI jsapi;
-    jsapi.Init();
-    JSContext* cx = jsapi.cx();
-    RootedDictionary<SettingChangeNotification> setting(cx);
-    if (!WrappedJSToDictionary(cx, aSubject, setting)) {
-      return;
-    }
-    if (!setting.mKey.EqualsASCII(GEO_SETINGS_ENABLED)) {
-      return;
-    }
-    if (!setting.mValue.isBoolean()) {
+    AutoSafeJSContext cx;
+
+    nsDependentString dataStr(aData);
+    JS::Rooted<JS::Value> val(cx);
+    if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) || !val.isObject()) {
       return;
     }
 
-    HandleMozsettingValue(setting.mValue.toBoolean());
+    JS::Rooted<JSObject*> obj(cx, &val.toObject());
+    JS::Rooted<JS::Value> key(cx);
+    if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) {
+      return;
+    }
+
+    bool match;
+    if (!JS_StringEqualsAscii(cx, key.toString(), GEO_SETINGS_ENABLED, &match) || !match) {
+      return;
+    }
+
+    JS::Rooted<JS::Value> value(cx);
+    if (!JS_GetProperty(cx, obj, "value", &value) || !value.isBoolean()) {
+      return;
+    }
+
+    HandleMozsettingValue(value.toBoolean());
 }
 
 void
 nsGeolocationService::HandleMozsettingValue(const bool aValue)
 {
     if (!aValue) {
       // turn things off
       StopDevice();
@@ -772,17 +781,17 @@ nsGeolocationService::Observe(nsISupport
       mGeolocators[i]->Shutdown();
     }
     StopDevice();
 
     return NS_OK;
   }
 
   if (!strcmp("mozsettings-changed", aTopic)) {
-    HandleMozsettingChanged(aSubject);
+    HandleMozsettingChanged(aData);
     return NS_OK;
   }
 
   if (!strcmp("timer-callback", aTopic)) {
     // decide if we can close down the service.
     for (uint32_t i = 0; i< mGeolocators.Length(); i++)
       if (mGeolocators[i]->HasActiveCallbacks()) {
         SetDisconnectTimer();
--- a/dom/geolocation/nsGeolocation.h
+++ b/dom/geolocation/nsGeolocation.h
@@ -65,17 +65,17 @@ public:
   NS_DECL_NSIOBSERVER
 
   nsGeolocationService() {
       mHigherAccuracy = false;
   }
 
   nsresult Init();
 
-  void HandleMozsettingChanged(nsISupports* aSubject);
+  void HandleMozsettingChanged(const char16_t* aData);
   void HandleMozsettingValue(const bool aValue);
 
   // Management of the Geolocation objects
   void AddLocator(mozilla::dom::Geolocation* locator);
   void RemoveLocator(mozilla::dom::Geolocation* locator);
 
   void SetCachedPosition(nsIDOMGeoPosition* aPosition);
   CachedPositionAndAccuracy GetCachedPosition();
--- a/dom/settings/SettingsRequestManager.jsm
+++ b/dom/settings/SettingsRequestManager.jsm
@@ -658,22 +658,22 @@ let SettingsRequestManager = {
         if (DEBUG) debug("Wrong observer topic: " + aTopic);
         break;
     }
   },
 
   sendSettingsChange: function(aKey, aValue, aIsServiceLock) {
     this.broadcastMessage("Settings:Change:Return:OK",
       { key: aKey, value: aValue });
-    var setting = {
-      key: aKey,
-      value: aValue,
-      isInternalChange: aIsServiceLock
-    };
-    Services.obs.notifyObservers(setting, kMozSettingsChangedObserverTopic, "");
+    Services.obs.notifyObservers(this, kMozSettingsChangedObserverTopic,
+      JSON.stringify({
+        key: aKey,
+        value: aValue,
+        isInternalChange: aIsServiceLock
+      }));
   },
 
   broadcastMessage: function broadcastMessage(aMsgName, aContent) {
     if (DEBUG) debug("Broadcast");
     this.children.forEach(function(msgMgr) {
       let principal = this.mmPrincipals.get(msgMgr);
       if (!principal) {
         if (DEBUG) debug("Cannot find principal for message manager to check permissions");
--- a/dom/system/NetworkGeolocationProvider.js
+++ b/dom/system/NetworkGeolocationProvider.js
@@ -259,20 +259,21 @@ WifiGeoPositionProvider.prototype = {
   listener: null,
 
   observe: function(aSubject, aTopic, aData) {
     if (aTopic != SETTINGS_CHANGED_TOPIC) {
       return;
     }
 
     try {
-      if (aSubject.key == SETTINGS_DEBUG_ENABLED) {
-        gLoggingEnabled = aSubject.value;
-      } else if (aSubject.key == SETTINGS_WIFI_ENABLED) {
-        gWifiScanningEnabled = aSubject.value;
+      let setting = JSON.parse(aData);
+      if (setting.key == SETTINGS_DEBUG_ENABLED) {
+        gLoggingEnabled = setting.value;
+      } else if (setting.key == SETTINGS_WIFI_ENABLED) {
+        gWifiScanningEnabled = setting.value;
       }
     } catch (e) {
     }
   },
 
   resetTimer: function() {
     if (this.timer) {
       this.timer.cancel();
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -36,18 +36,16 @@
 #include "BluetoothCommon.h"
 #include "BluetoothHfpManagerBase.h"
 
 #include "nsJSUtils.h"
 #include "nsThreadUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsXULAppAPI.h"
-#include "mozilla/dom/BindingUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 using namespace mozilla::dom::gonk;
 using namespace android;
 using namespace mozilla::hal;
 using namespace mozilla;
 using namespace mozilla::dom::bluetooth;
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "AudioManager" , ## args)
@@ -347,31 +345,46 @@ AudioManager::Observe(nsISupports* aSubj
   else if (!strcmp(aTopic, AUDIO_CHANNEL_PROCESS_CHANGED)) {
     HandleAudioChannelProcessChanged();
     return NS_OK;
   }
 
   // To process the volume control on each audio channel according to
   // change of settings
   else if (!strcmp(aTopic, MOZ_SETTINGS_CHANGE_ID)) {
-    AutoJSAPI jsapi;
-    jsapi.Init();
-    JSContext* cx = jsapi.cx();
-    RootedDictionary<dom::SettingChangeNotification> setting(cx);
-    if (!WrappedJSToDictionary(cx, aSubject, setting)) {
+    AutoSafeJSContext cx;
+    nsDependentString dataStr(aData);
+    JS::Rooted<JS::Value> val(cx);
+    if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
+        !val.isObject()) {
       return NS_OK;
     }
-    if (!setting.mKey.EqualsASCII("audio.volume.bt_sco")) {
-      return NS_OK;
-    }
-    if (!setting.mValue.isNumber()) {
+
+    JS::Rooted<JSObject*> obj(cx, &val.toObject());
+    JS::Rooted<JS::Value> key(cx);
+    if (!JS_GetProperty(cx, obj, "key", &key) ||
+        !key.isString()) {
       return NS_OK;
     }
 
-    int32_t index = setting.mValue.toNumber();
+    JS::Rooted<JSString*> jsKey(cx, JS::ToString(cx, key));
+    if (!jsKey) {
+      return NS_OK;
+    }
+    nsAutoJSString keyStr;
+    if (!keyStr.init(cx, jsKey) || !keyStr.EqualsLiteral("audio.volume.bt_sco")) {
+      return NS_OK;
+    }
+
+    JS::Rooted<JS::Value> value(cx);
+    if (!JS_GetProperty(cx, obj, "value", &value) || !value.isInt32()) {
+      return NS_OK;
+    }
+
+    int32_t index = value.toInt32();
     SetStreamVolumeIndex(AUDIO_STREAM_BLUETOOTH_SCO, index);
 
     return NS_OK;
   }
 
   NS_WARNING("Unexpected topic in AudioManager");
   return NS_ERROR_FAILURE;
 }
--- a/dom/system/gonk/AutoMounterSetting.cpp
+++ b/dom/system/gonk/AutoMounterSetting.cpp
@@ -15,31 +15,27 @@
 #include "nsJSUtils.h"
 #include "nsPrintfCString.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "xpcpublic.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/dom/BindingUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "AutoMounterSetting" , ## args)
 #define ERR(args...)  __android_log_print(ANDROID_LOG_ERROR, "AutoMounterSetting" , ## args)
 
 #define UMS_MODE                  "ums.mode"
 #define UMS_STATUS                "ums.status"
 #define UMS_VOLUME_ENABLED_PREFIX "ums.volume."
 #define UMS_VOLUME_ENABLED_SUFFIX ".enabled"
 #define MOZSETTINGS_CHANGED       "mozsettings-changed"
 
-using namespace mozilla::dom;
-
 namespace mozilla {
 namespace system {
 
 class SettingsServiceCallback MOZ_FINAL : public nsISettingsServiceCallback
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
@@ -234,45 +230,62 @@ AutoMounterSetting::Observe(nsISupports*
   }
 
   // Note that this function gets called for any and all settings changes,
   // so we need to carefully check if we have the one we're interested in.
   //
   // The string that we're interested in will be a JSON string that looks like:
   //  {"key":"ums.autoMount","value":true}
 
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  JSContext* cx = jsapi.cx();
-  RootedDictionary<SettingChangeNotification> setting(cx);
-  if (!WrappedJSToDictionary(cx, aSubject, setting)) {
+  mozilla::AutoSafeJSContext cx;
+  nsDependentString dataStr(aData);
+  JS::Rooted<JS::Value> val(cx);
+  if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
+      !val.isObject()) {
+    return NS_OK;
+  }
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
+  JS::Rooted<JS::Value> key(cx);
+  if (!JS_GetProperty(cx, obj, "key", &key) ||
+      !key.isString()) {
+    return NS_OK;
+  }
+
+  JSString *jsKey = JS::ToString(cx, key);
+  nsAutoJSString keyStr;
+  if (!keyStr.init(cx, jsKey)) {
+    return NS_OK;
+  }
+
+  JS::Rooted<JS::Value> value(cx);
+  if (!JS_GetProperty(cx, obj, "value", &value)) {
     return NS_OK;
   }
 
   // Check for ums.mode changes
-  if (setting.mKey.EqualsASCII(UMS_MODE)) {
-    if (!setting.mValue.isInt32()) {
+  if (keyStr.EqualsLiteral(UMS_MODE)) {
+    if (!value.isInt32()) {
       return NS_OK;
     }
-    int32_t mode = setting.mValue.toInt32();
+    int32_t mode = value.toInt32();
     SetAutoMounterMode(mode);
     return NS_OK;
   }
 
   // Check for ums.volume.NAME.enabled
-  if (StringBeginsWith(setting.mKey, NS_LITERAL_STRING(UMS_VOLUME_ENABLED_PREFIX)) &&
-      StringEndsWith(setting.mKey, NS_LITERAL_STRING(UMS_VOLUME_ENABLED_SUFFIX))) {
-    if (!setting.mValue.isBoolean()) {
+  if (StringBeginsWith(keyStr, NS_LITERAL_STRING(UMS_VOLUME_ENABLED_PREFIX)) &&
+      StringEndsWith(keyStr, NS_LITERAL_STRING(UMS_VOLUME_ENABLED_SUFFIX))) {
+    if (!value.isBoolean()) {
       return NS_OK;
     }
     const size_t prefixLen = sizeof(UMS_VOLUME_ENABLED_PREFIX) - 1;
     const size_t suffixLen = sizeof(UMS_VOLUME_ENABLED_SUFFIX) - 1;
     nsDependentSubstring volumeName =
-      Substring(setting.mKey, prefixLen, setting.mKey.Length() - prefixLen - suffixLen);
-    bool isSharingEnabled = setting.mValue.toBoolean();
+      Substring(keyStr, prefixLen, keyStr.Length() - prefixLen - suffixLen);
+    bool isSharingEnabled = value.toBoolean();
     SetAutoMounterSharingMode(NS_LossyConvertUTF16toASCII(volumeName), isSharingEnabled);
     return NS_OK;
   }
 
   return NS_OK;
 }
 
 }   // namespace system
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -215,17 +215,18 @@ NetworkManager.prototype = {
                                          Ci.nsIObserver,
                                          Ci.nsISettingsServiceCallback]),
 
   // nsIObserver
 
   observe: function(subject, topic, data) {
     switch (topic) {
       case TOPIC_MOZSETTINGS_CHANGED:
-        this.handle(subject.key, subject.value);
+        let setting = JSON.parse(data);
+        this.handle(setting.key, setting.value);
         break;
       case TOPIC_PREF_CHANGED:
         this._manageOfflineStatus =
           Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
         debug(PREF_MANAGE_OFFLINE_STATUS + " has changed to " + this._manageOfflineStatus);
         break;
       case TOPIC_XPCOM_SHUTDOWN:
         Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -955,17 +955,18 @@ XPCOMUtils.defineLazyGetter(this, "gData
     },
 
     /**
      * nsIObserver interface methods.
      */
     observe: function(subject, topic, data) {
       switch (topic) {
         case kMozSettingsChangedObserverTopic:
-          this.handle(subject.key, subject.value);
+          let setting = JSON.parse(data);
+          this.handle(setting.key, setting.value);
           break;
         case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
           this._shutdown();
           break;
       }
     },
   };
 });
@@ -3046,17 +3047,18 @@ RadioInterface.prototype = {
     gMessageManager.sendIccMessage("RIL:StkCommand", this.clientId, message);
   },
 
   // nsIObserver
 
   observe: function(subject, topic, data) {
     switch (topic) {
       case kMozSettingsChangedObserverTopic:
-        this.handleSettingsChange(subject.key, subject.value, subject.isInternalChange);
+        let setting = JSON.parse(data);
+        this.handleSettingsChange(setting.key, setting.value, setting.isInternalChange);
         break;
       case kSysClockChangeObserverTopic:
         let offset = parseInt(data, 10);
         if (this._lastNitzMessage) {
           this._lastNitzMessage.receiveTimeInMS += offset;
         }
         this._sntp.updateOffset(offset);
         break;
--- a/dom/system/gonk/TimeZoneSettingObserver.cpp
+++ b/dom/system/gonk/TimeZoneSettingObserver.cpp
@@ -17,28 +17,25 @@
 #include "nsISettingsService.h"
 #include "nsJSUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "TimeZoneSettingObserver.h"
 #include "xpcpublic.h"
 #include "nsContentUtils.h"
 #include "nsPrintfCString.h"
-#include "mozilla/dom/BindingUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Time Zone Setting" , ## args)
 #define ERR(args...)  __android_log_print(ANDROID_LOG_ERROR, "Time Zone Setting" , ## args)
 
 #define TIME_TIMEZONE       "time.timezone"
 #define MOZSETTINGS_CHANGED "mozsettings-changed"
 
 using namespace mozilla;
-using namespace mozilla::dom;
 
 namespace {
 
 class TimeZoneSettingObserver : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
@@ -182,44 +179,62 @@ TimeZoneSettingObserver::~TimeZoneSettin
     observerService->RemoveObserver(this, MOZSETTINGS_CHANGED);
   }
 }
 
 NS_IMPL_ISUPPORTS(TimeZoneSettingObserver, nsIObserver)
 
 NS_IMETHODIMP
 TimeZoneSettingObserver::Observe(nsISupports *aSubject,
-                                 const char *aTopic,
-                                 const char16_t *aData)
+                     const char *aTopic,
+                     const char16_t *aData)
 {
   if (strcmp(aTopic, MOZSETTINGS_CHANGED) != 0) {
     return NS_OK;
   }
 
   // Note that this function gets called for any and all settings changes,
   // so we need to carefully check if we have the one we're interested in.
   //
   // The string that we're interested in will be a JSON string that looks like:
   // {"key":"time.timezone","value":"America/Chicago"} or
   // {"key":"time.timezone","value":"UTC-05:00"}
 
   AutoSafeJSContext cx;
-  RootedDictionary<SettingChangeNotification> setting(cx);
-  if (!WrappedJSToDictionary(cx, aSubject, setting)) {
+
+  // Parse the JSON value.
+  nsDependentString dataStr(aData);
+  JS::Rooted<JS::Value> val(cx);
+  if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
+      !val.isObject()) {
     return NS_OK;
   }
-  if (!setting.mKey.EqualsASCII(TIME_TIMEZONE)) {
+
+  // Get the key, which should be the JS string "time.timezone".
+  JS::Rooted<JSObject*> obj(cx, &val.toObject());
+  JS::Rooted<JS::Value> key(cx);
+  if (!JS_GetProperty(cx, obj, "key", &key) ||
+      !key.isString()) {
     return NS_OK;
   }
-  if (!setting.mValue.isString()) {
+  bool match;
+  if (!JS_StringEqualsAscii(cx, key.toString(), TIME_TIMEZONE, &match) ||
+      !match) {
+    return NS_OK;
+  }
+
+  // Get the value, which should be a JS string like "America/Chicago".
+  JS::Rooted<JS::Value> value(cx);
+  if (!JS_GetProperty(cx, obj, "value", &value) ||
+      !value.isString()) {
     return NS_OK;
   }
 
   // Set the system timezone.
-  return SetTimeZone(setting.mValue, cx);
+  return SetTimeZone(value, cx);
 }
 
 } // anonymous namespace
 
 static mozilla::StaticRefPtr<TimeZoneSettingObserver> sTimeZoneSettingObserver;
 namespace mozilla {
 namespace system {
 void
deleted file mode 100644
--- a/dom/webidl/SettingChangeNotification.webidl
+++ /dev/null
@@ -1,12 +0,0 @@
-/* -*- 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/.
- */
-
-// Used internally by Gecko
-dictionary SettingChangeNotification {
-  DOMString key   = "";
-  any       value;
-  boolean   isInternalChange = false;
-};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -332,17 +332,16 @@ WEBIDL_FILES = [
     'Screen.webidl',
     'ScriptProcessorNode.webidl',
     'ScrollAreaEvent.webidl',
     'Selection.webidl',
     'ServiceWorker.webidl',
     'ServiceWorkerContainer.webidl',
     'ServiceWorkerGlobalScope.webidl',
     'ServiceWorkerRegistration.webidl',
-    'SettingChangeNotification.webidl',
     'SettingsManager.webidl',
     'ShadowRoot.webidl',
     'SharedWorker.webidl',
     'SharedWorkerGlobalScope.webidl',
     'SimpleGestureEvent.webidl',
     'SocketCommon.webidl',
     'SourceBuffer.webidl',
     'SourceBufferList.webidl',
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -3626,23 +3626,27 @@ WifiWorker.prototype = {
       }.bind(this));
     }
   },
 
   // nsIObserver implementation
   observe: function observe(subject, topic, data) {
     switch (topic) {
     case kMozSettingsChangedObserverTopic:
+      // The string we're interested in will be a JSON string that looks like:
+      // {"key":"wifi.enabled","value":"true"}.
+
+      let setting = JSON.parse(data);
       // To avoid WifiWorker setting the wifi again, don't need to deal with
       // the "mozsettings-changed" event fired from internal setting.
-      if (subject.isInternalChange) {
+      if (setting.isInternalChange) {
         return;
       }
 
-      this.handle(subject.key, subject.value);
+      this.handle(setting.key, setting.value);
       break;
 
     case "xpcom-shutdown":
       let wifiService = Cc["@mozilla.org/wifi/service;1"].getService(Ci.nsIWifiProxyService);
       wifiService.shutdown();
       let wifiCertService = Cc["@mozilla.org/wifi/certservice;1"].getService(Ci.nsIWifiCertService);
       wifiCertService.shutdown();
       break;
--- a/toolkit/devtools/discovery/discovery.js
+++ b/toolkit/devtools/discovery/discovery.js
@@ -200,20 +200,21 @@ LocalDevice.prototype = {
 
   /**
    * Observe any changes that might be made via the Settings app
    */
   observe: function(subject, topic, data) {
     if (topic !== "mozsettings-changed") {
       return;
     }
-    if (subject.key !== LocalDevice.SETTING) {
+    let setting = JSON.parse(data);
+    if (setting.key !== LocalDevice.SETTING) {
       return;
     }
-    this._name = subject.value;
+    this._name = setting.value;
     log("Device: " + this._name);
   },
 
   get name() {
     return this._name;
   },
 
   set name(name) {