Merge m-c to fx-team a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 27 Mar 2015 17:19:41 -0700
changeset 265079 9511efa9a8ebab205ba24fa467179aaa3849f770
parent 265078 5fde3c10ff0128f0e872373198f613c1a3bb22c8 (current diff)
parent 265035 ad587ca628cfc317941aa2c61b447226fbbd214a (diff)
child 265080 f0b932c8f3daec493c690083a38bf1af86c89b3a
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team a=merge
--- a/accessible/atk/nsMaiInterfaceDocument.cpp
+++ b/accessible/atk/nsMaiInterfaceDocument.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InterfaceInitFuncs.h"
 
 #include "Accessible-inl.h"
 #include "AccessibleWrap.h"
 #include "DocAccessible.h"
 #include "nsMai.h"
+#include "ProxyAccessible.h"
 #include "mozilla/Likely.h"
 
 using namespace mozilla::a11y;
 
 static const char* const kDocTypeName = "W3C-doctype";
 static const char* const kDocUrlName = "DocURL";
 static const char* const kMimeTypeName = "MimeType";
 
@@ -41,22 +42,24 @@ documentInterfaceInitCB(AtkDocumentIface
     aIface->get_document_attributes = getDocumentAttributesCB;
     aIface->get_document_attribute_value = getDocumentAttributeValueCB;
     aIface->get_document_locale = getDocumentLocaleCB;
 }
 
 const gchar *
 getDocumentLocaleCB(AtkDocument *aDocument)
 {
+  nsAutoString locale;
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
-  if (!accWrap)
-    return nullptr;
+  if (accWrap) {
+    accWrap->Language(locale);
+  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aDocument))) {
+    proxy->Language(locale);
+  }
 
-  nsAutoString locale;
-  accWrap->Language(locale);
   return locale.IsEmpty() ? nullptr : AccessibleWrap::ReturnString(locale);
 }
 
 static inline GSList *
 prependToList(GSList *aList, const char *const aName, const nsAutoString &aValue)
 {
   if (aValue.IsEmpty())
     return aList;
@@ -66,52 +69,82 @@ prependToList(GSList *aList, const char 
     atkAttr->name = g_strdup(aName);
     atkAttr->value = g_strdup(NS_ConvertUTF16toUTF8(aValue).get());
     return g_slist_prepend(aList, atkAttr);
 }
 
 AtkAttributeSet *
 getDocumentAttributesCB(AtkDocument *aDocument)
 {
+  nsAutoString url;
+  nsAutoString w3cDocType;
+  nsAutoString mimeType;
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
-  if (!accWrap || !accWrap->IsDoc())
+  if (accWrap) {
+    if (!accWrap->IsDoc()) {
+      return nullptr;
+    }
+
+    DocAccessible* document = accWrap->AsDoc();
+    document->URL(url);
+    document->DocType(w3cDocType);
+    document->MimeType(mimeType);
+  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aDocument))) {
+    proxy->URLDocTypeMimeType(url, w3cDocType, mimeType);
+  } else {
     return nullptr;
+  }
 
   // according to atkobject.h, AtkAttributeSet is a GSList
   GSList* attributes = nullptr;
-  DocAccessible* document = accWrap->AsDoc();
-  nsAutoString aURL;
-  document->URL(aURL);
-  attributes = prependToList(attributes, kDocUrlName, aURL);
-
-  nsAutoString aW3CDocType;
-  document->DocType(aW3CDocType);
-  attributes = prependToList(attributes, kDocTypeName, aW3CDocType);
-
-  nsAutoString aMimeType;
-  document->MimeType(aMimeType);
-  attributes = prependToList(attributes, kMimeTypeName, aMimeType);
+  attributes = prependToList(attributes, kDocUrlName, url);
+  attributes = prependToList(attributes, kDocTypeName, w3cDocType);
+  attributes = prependToList(attributes, kMimeTypeName, mimeType);
 
   return attributes;
 }
 
 const gchar *
 getDocumentAttributeValueCB(AtkDocument *aDocument,
                             const gchar *aAttrName)
 {
+  ProxyAccessible* proxy = nullptr;
+  DocAccessible* document = nullptr;
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
-  if (!accWrap || !accWrap->IsDoc())
-    return nullptr;
+  if (accWrap) {
+    if (!accWrap->IsDoc()) {
+      return nullptr;
+    }
 
-  DocAccessible* document = accWrap->AsDoc();
+    document = accWrap->AsDoc();
+  } else {
+    proxy = GetProxy(ATK_OBJECT(aDocument));
+    if (!proxy) {
+      return nullptr;
+    }
+  }
+
   nsAutoString attrValue;
-  if (!strcasecmp(aAttrName, kDocTypeName))
-    document->DocType(attrValue);
-  else if (!strcasecmp(aAttrName, kDocUrlName))
-    document->URL(attrValue);
-  else if (!strcasecmp(aAttrName, kMimeTypeName))
-    document->MimeType(attrValue);
-  else
+  if (!strcasecmp(aAttrName, kDocTypeName)) {
+    if (document) {
+      document->DocType(attrValue);
+    } else {
+      proxy->DocType(attrValue);
+    }
+  } else if (!strcasecmp(aAttrName, kDocUrlName)) {
+    if (document) {
+      document->URL(attrValue);
+    } else {
+      proxy->URL(attrValue);
+    }
+  } else if (!strcasecmp(aAttrName, kMimeTypeName)) {
+    if (document) {
+      document->MimeType(attrValue);
+    } else {
+      proxy->MimeType(attrValue);
+    }
+  } else {
     return nullptr;
+  }
 
   return attrValue.IsEmpty() ? nullptr : AccessibleWrap::ReturnString(attrValue);
 }
 }
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/DocAccessibleChild.cpp
@@ -1626,10 +1626,75 @@ DocAccessibleChild::RecvBounds(const uin
   Accessible* acc = IdToAccessible(aID);
   if (acc && !acc->IsDefunct()) {
     *aRect = acc->Bounds();
   }
 
   return false;
 }
 
+bool
+DocAccessibleChild::RecvLanguage(const uint64_t& aID,
+                                 nsString* aLocale)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (acc) {
+    acc->Language(*aLocale);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvDocType(const uint64_t& aID,
+                                nsString* aType)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (acc && acc->IsDoc()) {
+    acc->AsDoc()->DocType(*aType);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvURL(const uint64_t& aID,
+                            nsString* aURL)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (acc && acc->IsDoc()) {
+    acc->AsDoc()->URL(*aURL);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvMimeType(const uint64_t& aID,
+                                 nsString* aMime)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (acc && acc->IsDoc()) {
+    acc->AsDoc()->MimeType(*aMime);
+  }
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvURLDocTypeMimeType(const uint64_t& aID,
+                                           nsString* aURL,
+                                           nsString* aDocType,
+                                           nsString* aMimeType)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (acc && acc->IsDoc()) {
+    DocAccessible* doc = acc->AsDoc();
+    doc->URL(*aURL);
+    doc->DocType(*aDocType);
+    doc->MimeType(*aMimeType);
+  }
+
+  return true;
+}
+
 }
 }
--- a/accessible/ipc/DocAccessibleChild.h
+++ b/accessible/ipc/DocAccessibleChild.h
@@ -396,16 +396,25 @@ public:
   virtual bool RecvChildAtPoint(const uint64_t& aID,
                                 const int32_t& aX,
                                 const int32_t& aY,
                                 const uint32_t& aWhich,
                                 uint64_t* aChild,
                                 bool* aOk) override;
 
   virtual bool RecvBounds(const uint64_t& aID, nsIntRect* aRect) override;
+
+  virtual bool RecvLanguage(const uint64_t& aID, nsString* aLocale) override;
+  virtual bool RecvDocType(const uint64_t& aID, nsString* aType) override;
+  virtual bool RecvURL(const uint64_t& aID, nsString* aURL) override;
+  virtual bool RecvMimeType(const uint64_t& aID, nsString* aMime) override;
+  virtual bool RecvURLDocTypeMimeType(const uint64_t& aID,
+                                      nsString* aURL,
+                                      nsString* aDocType,
+                                      nsString* aMimeType) override;
 private:
 
   Accessible* IdToAccessible(const uint64_t& aID) const;
   Accessible* IdToAccessibleLink(const uint64_t& aID) const;
   Accessible* IdToAccessibleSelect(const uint64_t& aID) const;
   HyperTextAccessible* IdToHyperTextAccessible(const uint64_t& aID) const;
   ImageAccessible* IdToImageAccessible(const uint64_t& aID) const;
   TableCellAccessible* IdToTableCellAccessible(const uint64_t& aID) const;
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -204,12 +204,18 @@ child:
   prio(high) sync MinValue(uint64_t aID) returns(double aValue);
   prio(high) sync MaxValue(uint64_t aID) returns(double aValue);
   prio(high) sync Step(uint64_t aID) returns(double aStep);
 
   prio(high) sync TakeFocus(uint64_t aID);
   prio(high) sync ChildAtPoint(uint64_t aID, int32_t aX, int32_t aY, uint32_t aWhich)
     returns(uint64_t aChild, bool aOk);
   prio(high) sync Bounds(uint64_t aID) returns(nsIntRect aRect);
+
+  prio(high) sync Language(uint64_t aID) returns(nsString aLocale);
+  prio(high) sync DocType(uint64_t aID) returns(nsString aType);
+  prio(high) sync URL(uint64_t aID) returns(nsString aURL);
+  prio(high) sync MimeType(uint64_t aID) returns(nsString aMime);
+  prio(high) sync URLDocTypeMimeType(uint64_t aID) returns(nsString aURL, nsString aDocType, nsString aMimeType);
 };
 
 }
 }
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/ProxyAccessible.cpp
@@ -906,10 +906,41 @@ ProxyAccessible::ChildAtPoint(int32_t aX
 nsIntRect
 ProxyAccessible::Bounds()
 {
   nsIntRect rect;
   unused << mDoc->SendBounds(mID, &rect);
   return rect;
 }
 
+void
+ProxyAccessible::Language(nsString& aLocale)
+{
+  unused << mDoc->SendLanguage(mID, &aLocale);
+}
+
+void
+ProxyAccessible::DocType(nsString& aType)
+{
+  unused << mDoc->SendDocType(mID, &aType);
+}
+
+void
+ProxyAccessible::URL(nsString& aURL)
+{
+  unused << mDoc->SendURL(mID, &aURL);
+}
+
+void
+ProxyAccessible::MimeType(nsString aMime)
+{
+  unused << mDoc->SendMimeType(mID, &aMime);
+}
+
+void
+ProxyAccessible::URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
+                                    nsString& aMimeType)
+{
+  unused << mDoc->SendURLDocTypeMimeType(mID, &aURL, &aDocType, &aMimeType);
+}
+
 }
 }
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -271,16 +271,23 @@ public:
   double MaxValue();
   double Step();
 
   void TakeFocus();
   ProxyAccessible* ChildAtPoint(int32_t aX, int32_t aY,
                                 Accessible::EWhichChildAtPoint aWhichChild);
   nsIntRect Bounds();
 
+  void Language(nsString& aLocale);
+  void DocType(nsString& aType);
+  void URL(nsString& aURL);
+  void MimeType(nsString aMime);
+  void URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
+                          nsString& aMimeType);
+
   /**
    * Allow the platform to store a pointers worth of data on us.
    */
   uintptr_t GetWrapper() const { return mWrapper; }
   void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
 
   /*
    * Return the ID of the accessible being proxied.
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -535,18 +535,17 @@ struct RoleDescrComparator
 }
 
 - (id)value
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   nsAutoString value;
   mGeckoAccessible->Value(value);
-  return value.IsEmpty() ? nil : [NSString stringWithCharacters:reinterpret_cast<const unichar*>(value.BeginReading())
-                                                         length:value.Length()];
+  return nsCocoaUtils::ToNSString(value);
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (void)valueDidChange
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
@@ -580,18 +579,17 @@ struct RoleDescrComparator
 }
 
 - (NSString*)help
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   nsAutoString helpText;
   mGeckoAccessible->Help(helpText);
-  return helpText.IsEmpty() ? nil : [NSString stringWithCharacters:reinterpret_cast<const unichar*>(helpText.BeginReading())
-                                                            length:helpText.Length()];
+  return nsCocoaUtils::ToNSString(helpText);
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 // objc-style description (from NSObject); not to be confused with the accessible description above.
 - (NSString*)description
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
--- a/b2g/components/PaymentProviderStrategy.js
+++ b/b2g/components/PaymentProviderStrategy.js
@@ -6,19 +6,19 @@
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 const PREF_DEBUG = "dom.payment.debug";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyServiceGetter(this, "iccProvider",
-                                   "@mozilla.org/ril/content-helper;1",
-                                   "nsIIccProvider");
+XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
+                                   "@mozilla.org/icc/iccservice;1",
+                                   "nsIIccService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gRil",
                                    "@mozilla.org/ril;1",
                                    "nsIRadioInterfaceLayer");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
                                    "@mozilla.org/settingsService;1",
                                    "nsISettingsService");
@@ -143,17 +143,18 @@ PaymentProviderStrategy.prototype = {
   set paymentServiceId(aServiceId) {
     this._settings.paymentServiceId = aServiceId;
   },
 
   get iccInfo() {
     if (!this._iccInfo) {
       this._iccInfo = [];
       for (let i = 0; i < gRil.numRadioInterfaces; i++) {
-        let info = iccProvider.getIccInfo(i);
+        let icc = gIccService.getIccByServiceId(i);
+        let info = icc && icc.iccInfo;
         if (!info) {
           LOGE("Tried to get the ICC info for an invalid service ID " + i);
           continue;
         }
 
         this._iccInfo.push({
           iccId: info.iccid,
           mcc: info.mcc,
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0e7c8ade48129b3e03c5de8ae0452fd1f756535c"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0e7c8ade48129b3e03c5de8ae0452fd1f756535c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="527d1c939ee57deb7192166e56e2a3fffa8cb087"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0e7c8ade48129b3e03c5de8ae0452fd1f756535c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0e7c8ade48129b3e03c5de8ae0452fd1f756535c"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0e7c8ade48129b3e03c5de8ae0452fd1f756535c"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0e7c8ade48129b3e03c5de8ae0452fd1f756535c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="527d1c939ee57deb7192166e56e2a3fffa8cb087"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0e7c8ade48129b3e03c5de8ae0452fd1f756535c"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0e7c8ade48129b3e03c5de8ae0452fd1f756535c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "9cc496cecc37d7a29f9279827cdf6e4891211f67", 
+        "git_revision": "0e7c8ade48129b3e03c5de8ae0452fd1f756535c", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "9e79307fd6bcade07847b92d42948a6a6a334f79", 
+    "revision": "97a01eae94361363300254b54e53e2ac0f0b9d38", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0e7c8ade48129b3e03c5de8ae0452fd1f756535c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0e7c8ade48129b3e03c5de8ae0452fd1f756535c"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -173,17 +173,16 @@
 @RESPATH@/components/dom_audiochannel.xpt
 @RESPATH@/components/dom_base.xpt
 @RESPATH@/components/dom_system.xpt
 #ifdef MOZ_WIDGET_GONK
 @RESPATH@/components/dom_wifi.xpt
 @RESPATH@/components/dom_system_gonk.xpt
 #endif
 #ifdef MOZ_B2G_RIL
-@RESPATH@/components/dom_icc.xpt
 @RESPATH@/components/dom_wappush.xpt
 @RESPATH@/components/dom_mobileconnection.xpt
 #endif
 #ifdef MOZ_B2G_BT
 @RESPATH@/components/dom_bluetooth.xpt
 #endif
 #ifdef MOZ_B2G_CAMERA
 @BINPATH@/components/dom_camera.xpt
@@ -213,16 +212,17 @@
 @RESPATH@/components/dom_power.xpt
 @RESPATH@/components/dom_quota.xpt
 @RESPATH@/components/dom_range.xpt
 @RESPATH@/components/dom_security.xpt
 @RESPATH@/components/dom_settings.xpt
 @RESPATH@/components/dom_permissionsettings.xpt
 @RESPATH@/components/dom_sidebar.xpt
 @RESPATH@/components/dom_cellbroadcast.xpt
+@RESPATH@/components/dom_icc.xpt
 @RESPATH@/components/dom_mobilemessage.xpt
 @RESPATH@/components/dom_storage.xpt
 @RESPATH@/components/dom_stylesheets.xpt
 @RESPATH@/components/dom_telephony.xpt
 @RESPATH@/components/dom_threads.xpt
 @RESPATH@/components/dom_traversal.xpt
 @RESPATH@/components/dom_tv.xpt
 @RESPATH@/components/dom_views.xpt
@@ -478,16 +478,18 @@
 @RESPATH@/components/ResourceStatsManager.js
 @RESPATH@/components/ResourceStatsManager.manifest
 #endif // MOZ_WIDGET_GONK
 
 ; RIL
 #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
 @RESPATH@/components/CellBroadcastService.js
 @RESPATH@/components/CellBroadcastService.manifest
+@BINPATH@/components/IccService.js
+@BINPATH@/components/IccService.manifest
 @RESPATH@/components/MmsService.js
 @RESPATH@/components/MmsService.manifest
 @RESPATH@/components/MobileMessageDatabaseService.js
 @RESPATH@/components/MobileMessageDatabaseService.manifest
 #ifndef DISABLE_MOZ_RIL_GEOLOC
 @RESPATH@/components/MobileConnectionService.js
 @RESPATH@/components/MobileConnectionService.manifest
 @RESPATH@/components/RadioInterfaceLayer.js
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -656,17 +656,17 @@
 
       <tabs id="tabbrowser-tabs"
             class="tabbrowser-tabs"
             tabbrowser="content"
             flex="1"
             setfocus="false"
             tooltip="tabbrowser-tab-tooltip"
             stopwatchid="FX_TAB_CLICK_MS">
-        <tab class="tabbrowser-tab" selected="true" fadein="true"/>
+        <tab class="tabbrowser-tab" selected="true" visuallyselected="true" fadein="true"/>
       </tabs>
 
       <toolbarbutton id="new-tab-button"
                      class="toolbarbutton-1 chromeclass-toolbar-additional"
                      label="&tabCmd.label;"
                      command="cmd_newNavigatorTab"
                      onclick="checkForMiddleClick(this, event);"
                      tooltip="dynamic-shortcut-tooltip"
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1198,28 +1198,36 @@
                   findBar._findField.getAttribute("focused") == "true");
               }
 
               // If focus is in the tab bar, retain it there.
               if (document.activeElement == oldTab) {
                 // We need to explicitly focus the new tab, because
                 // tabbox.xml does this only in some cases.
                 this.mCurrentTab.focus();
-              } else if (gMultiProcessBrowser) {
+              } else if (gMultiProcessBrowser && document.activeElement !== newBrowser) {
                 // Clear focus so that _adjustFocusAfterTabSwitch can detect if
                 // some element has been focused and respect that.
                 document.activeElement.blur();
               }
 
               if (!gMultiProcessBrowser)
                 this._adjustFocusAfterTabSwitch(this.mCurrentTab);
             }
 
             this.tabContainer._setPositionalAttributes();
 
+            if (!gMultiProcessBrowser) {
+              let event = new CustomEvent("TabSwitchDone", {
+                bubbles: true,
+                cancelable: true
+              });
+              this.dispatchEvent(event);
+            }
+
             if (!aForceUpdate)
               TelemetryStopwatch.finish("FX_TAB_SWITCH_UPDATE_MS");
           ]]>
         </body>
       </method>
 
       <method name="_adjustFocusAfterTabSwitch">
         <parameter name="newTab"/>
@@ -2755,31 +2763,34 @@
           this._lastRelatedTab = null;
 
           this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(aTab._tPos, 1)[0]);
           this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(aTab._tPos, 1)[0]);
 
           let wasFocused = (document.activeElement == this.mCurrentTab);
 
           aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
-          this.mCurrentTab._selected = false;
+          this.mCurrentTab._logicallySelected = false;
+          this.mCurrentTab._visuallySelected = false;
 
           // invalidate caches
           this._browsers = null;
           this._visibleTabs = null;
 
           // use .item() instead of [] because dragging to the end of the strip goes out of
           // bounds: .item() returns null (so it acts like appendChild), but [] throws
           this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));
 
           for (let i = 0; i < this.tabs.length; i++) {
             this.tabs[i]._tPos = i;
-            this.tabs[i]._selected = false;
+            this.tabs[i]._logicallySelected = false;
+            this.tabs[i]._visuallySelected = false;
           }
-          this.mCurrentTab._selected = true;
+          this.mCurrentTab._logicallySelected = true;
+          this.mCurrentTab._visuallySelected = true;
 
           if (wasFocused)
             this.mCurrentTab.focus();
 
           this.tabContainer._handleTabSelect(false);
 
           if (aTab.pinned)
             this.tabContainer._positionPinnedTabs();
@@ -2861,16 +2872,528 @@
         <parameter name="aTab"/><!-- can be from a different window as well -->
         <body>
           <![CDATA[
             return SessionStore.duplicateTab(window, aTab);
           ]]>
         </body>
       </method>
 
+      <!--
+        The tab switcher is responsible for asynchronously switching
+        tabs in e10s. It waits until the new tab is ready (i.e., the
+        layer tree is available) before switching to it. Then it
+        unloads the layer tree for the old tab.
+
+        The tab switcher is a state machine. For each tab, it
+        maintains state about whether the layer tree for the tab is
+        available, being loaded, being unloaded, or unavailable. It
+        also keeps track of the tab currently being displayed, the tab
+        it's trying to load, and the tab the user has asked to switch
+        to. The switcher object is created upon tab switch. It is
+        released when there are no pending tabs to load or unload.
+
+        The following general principles have guided the design:
+
+        1. We only request one layer tree at a time. If the user
+        switches to a different tab while waiting, we don't request
+        the new layer tree until the old tab has loaded or timed out.
+
+        2. If loading the layers for a tab times out, we show the
+        spinner and possibly request the layer tree for another tab if
+        the user has requested one.
+
+        3. We discard layer trees on a delay. This way, if the user is
+        switching among the same tabs frequently, we don't continually
+        load the same tabs.
+
+        It's important that we always show either the spinner or a tab
+        whose layers are available. Otherwise the compositor will draw
+        an entirely black frame, which is very jarring. To ensure this
+        never happens, we do the following:
+
+        1. When switching away from a tab, we assume the old tab might
+        still be drawn until a MozAfterPaint event occurs. Because
+        layout and compositing happen asynchronously, we don't have
+        any other way of knowing when the switch actually takes
+        place. Therefore, we don't unload the old tab until the next
+        MozAfterPaint event.
+
+        2. Suppose that the user switches from tab A to B and then
+        back to A. Suppose that we ask for tab A's layers to be
+        unloaded via message M1 after the first switch and then
+        request them again via message M2 once the second switch
+        happens. Both loading and unloading of layers happens
+        asynchronously, and this can cause problems. It's possible
+        that the content process publishes one last layer tree before
+        M1 is received. The parent process doesn't know that this
+        layer tree was published before M1 and not after M2, so it
+        will display the tab. However, once M1 arrives, the content
+        process will destroy the layer tree for A and now we will
+        display black for it.
+
+        To counter this problem, we keep tab A in a separate
+        "unloading" state until the layer tree is actually dropped in
+        the compositor thread. While the tab is in the "unloading"
+        state, we're not allowed to request layers for it. Once the
+        layers are dropped in the compositor, an event will fire and
+        we will transition the tab to the "unloaded" state. Then we
+        are free to request the tab's layers again.
+      -->
+      <field name="_switcher">null</field>
+      <method name="_getSwitcher">
+        <body><![CDATA[
+          if (this._switcher) {
+            return this._switcher;
+          }
+
+          let switcher = {
+            // How long to wait for a tab's layers to load. After this
+            // time elapses, we're free to put up the spinner and start
+            // trying to load a different tab.
+            TAB_SWITCH_TIMEOUT: 300 /* ms */,
+
+            // When the user hasn't switched tabs for this long, we unload
+            // layers for all tabs that aren't in use.
+            UNLOAD_DELAY: 300 /* ms */,
+
+            // The next three tabs form the principal state variables.
+            // See the assertions in postActions for their invariants.
+
+            // Tab the user requested most recently.
+            requestedTab: this.selectedTab,
+
+            // Tab we're currently trying to load.
+            loadingTab: null,
+
+            // We show this tab in case the requestedTab hasn't loaded yet.
+            lastVisibleTab: this.selectedTab,
+
+            // Auxilliary state variables:
+
+            visibleTab: this.selectedTab,   // Tab that's on screen.
+            spinnerTab: null,               // Tab showing a spinner.
+            originalTab: this.selectedTab,  // Tab that we started on.
+
+            tabbrowser: this,  // Reference to gBrowser.
+            loadTimer: null,   // TAB_SWITCH_TIMEOUT timer.
+            unloadTimer: null, // UNLOAD_DELAY timer.
+
+            // Map from tabs to STATE_* (below).
+            tabState: new Map(),
+
+            // Set of tabs that might be visible right now. We maintain
+            // this set because we can't be sure when a tab is actually
+            // drawn. A tab is added to this set when we ask to make it
+            // visible. All tabs but the most recently shown tab are
+            // removed from the set upon MozAfterPaint.
+            maybeVisibleTabs: new Set([this.selectedTab]),
+
+            STATE_UNLOADED: 0,
+            STATE_LOADING: 1,
+            STATE_LOADED: 2,
+            STATE_UNLOADING: 3,
+
+            logging: false,
+
+            getTabState: function(tab) {
+              let state = this.tabState.get(tab);
+              if (state === undefined) {
+                return this.STATE_UNLOADED;
+              }
+              return state;
+            },
+
+            setTabState: function(tab, state) {
+              if (state == this.STATE_UNLOADED) {
+                this.tabState.delete(tab);
+              } else {
+                this.tabState.set(tab, state);
+              }
+
+              let browser = tab.linkedBrowser;
+              let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
+              if (state == this.STATE_LOADING) {
+                // Ask for a MozLayerTreeReady event.
+                fl.requestNotifyLayerTreeReady();
+                browser.docShellIsActive = true;
+              } else if (state == this.STATE_UNLOADING) {
+                // Ask for MozLayerTreeCleared event.
+                fl.requestNotifyLayerTreeCleared();
+                browser.docShellIsActive = false;
+              }
+            },
+
+            init: function() {
+              this.log("START");
+
+              window.addEventListener("MozAfterPaint", this);
+              window.addEventListener("MozLayerTreeReady", this);
+              window.addEventListener("MozLayerTreeCleared", this);
+              window.addEventListener("TabRemotenessChange", this);
+              this.setTabState(this.requestedTab, this.STATE_LOADED);
+            },
+
+            destroy: function() {
+              clearTimeout(this.unloadTimer);
+              clearTimeout(this.loadTimer);
+
+              window.removeEventListener("MozAfterPaint", this);
+              window.removeEventListener("MozLayerTreeReady", this);
+              window.removeEventListener("MozLayerTreeCleared", this);
+              window.removeEventListener("TabRemotenessChange", this);
+
+              this.tabbrowser._switcher = null;
+            },
+
+            finish: function() {
+              this.log("FINISH");
+
+              this.assert(this.tabbrowser._switcher);
+              this.assert(this.tabbrowser._switcher === this);
+              this.assert(!this.spinnerTab);
+              this.assert(!this.loadTimer);
+              this.assert(!this.loadingTab);
+              this.assert(this.lastVisibleTab === this.requestedTab);
+              this.assert(this.getTabState(this.requestedTab) == this.STATE_LOADED);
+
+              this.destroy();
+
+              let toBrowser = this.requestedTab.linkedBrowser;
+              toBrowser.setAttribute("type", "content-primary");
+
+              this.tabbrowser._adjustFocusAfterTabSwitch(this.requestedTab);
+
+              let fromBrowser = this.originalTab.linkedBrowser;
+              // It's possible that the tab we're switching from closed
+              // before we were able to finalize, in which case, fromBrowser
+              // doesn't exist.
+              if (fromBrowser) {
+                fromBrowser.setAttribute("type", "content-targetable");
+              }
+
+              let event = new CustomEvent("TabSwitchDone", {
+                bubbles: true,
+                cancelable: true
+              });
+              this.tabbrowser.dispatchEvent(event);
+            },
+
+            // This function is called after all the main state changes to
+            // make sure we display the right tab.
+            updateDisplay: function() {
+              // Figure out which tab we actually want visible right now.
+              let showTab = null;
+              if (this.getTabState(this.requestedTab) != this.STATE_LOADED &&
+                  this.lastVisibleTab && this.loadTimer) {
+                // If we can't show the requestedTab, and lastVisibleTab is
+                // available, show it.
+                showTab = this.lastVisibleTab;
+              } else {
+                // Show the requested tab. If it's not available, we'll show the spinner.
+                showTab = this.requestedTab;
+              }
+
+              // Show or hide the spinner as needed.
+              let needSpinner = this.getTabState(showTab) != this.STATE_LOADED;
+              if (!needSpinner && this.spinnerTab) {
+                this.tabbrowser.removeAttribute("pendingpaint");
+                this.spinnerTab.linkedBrowser.removeAttribute("pendingpaint");
+                this.spinnerTab = null;
+              } else if (needSpinner && this.spinnerTab !== showTab) {
+                if (this.spinnerTab) {
+                  this.spinnerTab.linkedBrowser.removeAttribute("pendingpaint");
+                }
+                this.spinnerTab = showTab;
+                this.tabbrowser.setAttribute("pendingpaint", "true");
+                this.spinnerTab.linkedBrowser.setAttribute("pendingpaint", "true");
+              }
+
+              // Switch to the tab we've decided to make visible.
+              if (this.visibleTab !== showTab) {
+                this.visibleTab = showTab;
+
+                this.maybeVisibleTabs.add(showTab);
+
+                let tabs = this.tabbrowser.mTabBox.tabs;
+                let tabPanel = this.tabbrowser.mPanelContainer;
+                let showPanel = tabs.getRelatedElement(showTab);
+                let index = Array.indexOf(tabPanel.childNodes, showPanel);
+                if (index != -1) {
+                  this.log(`Switch to tab ${index} - ${this.tinfo(showTab)}`);
+                  tabPanel.setAttribute("selectedIndex", index);
+                  if (showTab === this.requestedTab) {
+                    this.tabbrowser._adjustFocusAfterTabSwitch(showTab);
+                  }
+                }
+
+                // This doesn't necessarily exist if we're a new window and haven't switched tabs yet
+                if (this.lastVisibleTab)
+                  this.lastVisibleTab._visuallySelected = false;
+
+                this.visibleTab._visuallySelected = true;
+              }
+
+              this.lastVisibleTab = this.visibleTab;
+
+            },
+
+            assert: function(cond) {
+              if (!cond) {
+                dump("Assertion failure\n" + Error().stack);
+                throw new Error("Assertion failure");
+              }
+            },
+
+            // We've decided to try to load requestedTab.
+            loadRequestedTab: function() {
+              this.assert(!this.loadTimer);
+
+              // loadingTab can be non-null here if we timed out loading the current tab.
+              // In that case we just overwrite it with a different tab; it's had its chance.
+              this.loadingTab = this.requestedTab;
+              this.log("Loading tab " + this.tinfo(this.loadingTab));
+
+              this.setTabState(this.requestedTab, this.STATE_LOADING);
+              this.loadTimer = setTimeout(() => this.onLoadTimeout(), this.TAB_SWITCH_TIMEOUT);
+            },
+
+            // This function runs before every event. It fixes up the state
+            // to account for closed tabs.
+            preActions: function() {
+              this.assert(this.tabbrowser._switcher);
+              this.assert(this.tabbrowser._switcher === this);
+
+              for (let [tab, state] of this.tabState) {
+                if (!tab.linkedBrowser) {
+                  this.tabState.delete(tab);
+                }
+              }
+
+              if (this.lastVisibleTab && !this.lastVisibleTab.linkedBrowser) {
+                this.lastVisibleTab = null;
+              }
+              if (this.spinnerTab && !this.spinnerTab.linkedBrowser) {
+                this.spinnerTab = null;
+              }
+              if (this.loadingTab && !this.loadingTab.linkedBrowser) {
+                this.loadingTab = null;
+                clearTimeout(this.loadTimer);
+                this.loadTimer = null;
+              }
+            },
+
+            // This code runs after we've responded to an event or requested a new
+            // tab. It's expected that we've already updated all the principal
+            // state variables. This function takes care of updating any auxilliary
+            // state.
+            postActions: function() {
+              // Once we finish loading loadingTab, we null it out. So the state should
+              // always be LOADING.
+              this.assert(!this.loadingTab ||
+                          this.getTabState(this.loadingTab) == this.STATE_LOADING);
+
+              // We guarantee that loadingTab is non-null iff loadTimer is non-null. So
+              // the timer is set only when we're loading something.
+              this.assert(!this.loadTimer || this.loadingTab);
+              this.assert(!this.loadingTab || this.loadTimer);
+
+              // If we're not loading anything, try loading the requested tab.
+              if (!this.loadTimer && this.getTabState(this.requestedTab) == this.STATE_UNLOADED) {
+                this.loadRequestedTab();
+              }
+
+              // See how many tabs still have work to do.
+              let numPending = 0;
+              for (let [tab, state] of this.tabState) {
+                if (state == this.STATE_LOADED && tab !== this.requestedTab) {
+                  numPending++;
+                }
+                if (state == this.STATE_LOADING || state == this.STATE_UNLOADING) {
+                  numPending++;
+                }
+              }
+
+              this.updateDisplay();
+
+              // It's possible for updateDisplay to trigger one of our own event
+              // handlers, which might cause finish() to already have been called.
+              // Check for that before calling finish() again.
+              if (!this.tabbrowser._switcher) {
+                return;
+              }
+
+              if (numPending == 0) {
+                this.finish();
+              }
+
+              this.logState("done");
+            },
+
+            // Fires when we're ready to unload unused tabs.
+            onUnloadTimeout: function() {
+              this.logState("onUnloadTimeout");
+              this.preActions();
+
+              let numPending = 0;
+
+              // Unload any tabs that can be unloaded.
+              for (let [tab, state] of this.tabState) {
+                if (state == this.STATE_LOADED &&
+                    !this.maybeVisibleTabs.has(tab) &&
+                    tab !== this.lastVisibleTab &&
+                    tab !== this.loadingTab &&
+                    tab !== this.requestedTab)
+                {
+                  this.setTabState(tab, this.STATE_UNLOADING);
+                }
+
+                if (state != this.STATE_UNLOADED && tab !== this.requestedTab) {
+                  numPending++;
+                }
+              }
+
+              if (numPending) {
+                // Keep the timer going since there may be more tabs to unload.
+                this.unloadTimer = setTimeout(() => this.onUnloadTimeout(), this.UNLOAD_DELAY);
+              }
+
+              this.postActions();
+            },
+
+            // Fires when an ongoing load has taken too long.
+            onLoadTimeout: function() {
+              this.logState("onLoadTimeout");
+              this.preActions();
+              this.loadTimer = null;
+              this.loadingTab = null;
+              this.postActions();
+            },
+
+            // Fires when the layers become available for a tab.
+            onLayersReady: function(browser) {
+              this.logState("onLayersReady");
+
+              let tab = this.tabbrowser.getTabForBrowser(browser);
+              this.setTabState(tab, this.STATE_LOADED);
+
+              if (this.loadingTab === tab) {
+                clearTimeout(this.loadTimer);
+                this.loadTimer = null;
+                this.loadingTab = null;
+              }
+            },
+
+            // Fires when we paint the screen. Any tab switches we initiated
+            // previously are done, so there's no need to keep the old layers
+            // around.
+            onPaint: function() {
+              this.maybeVisibleTabs.clear();
+            },
+
+            // Called when we're done clearing the layers for a tab.
+            onLayersCleared: function(browser) {
+              this.logState("onLayersCleared");
+
+              let tab = this.tabbrowser.getTabForBrowser(browser);
+              if (tab) {
+                this.setTabState(tab, this.STATE_UNLOADED);
+              }
+            },
+
+            // Called when a tab switches from remote to non-remote. In this case
+            // a MozLayerTreeReady notification that we requested may never fire,
+            // so we need to simulate it.
+            onRemotenessChange: function(tab) {
+              this.logState("onRemotenessChange");
+              if (!tab.linkedBrowser.isRemoteBrowser) {
+                if (this.getTabState(tab) == this.STATE_LOADING) {
+                  this.onLayersReady(tab.linkedBrowser);
+                } else if (this.getTabState(tab) == this.STATE_UNLOADING) {
+                  this.onLayersCleared(tab.linkedBrowser);
+                }
+              }
+            },
+
+            // Called when the user asks to switch to a given tab.
+            requestTab: function(tab) {
+              if (tab === this.requestedTab) {
+                return;
+              }
+
+              this.logState("requestTab " + this.tinfo(tab));
+
+              this.requestedTab = tab;
+
+              this.preActions();
+
+              clearTimeout(this.unloadTimer);
+              this.unloadTimer = setTimeout(() => this.onUnloadTimeout(), this.UNLOAD_DELAY);
+
+              this.postActions();
+            },
+
+            handleEvent: function(event) {
+              this.preActions();
+
+              if (event.type == "MozLayerTreeReady") {
+                this.onLayersReady(event.originalTarget);
+              } if (event.type == "MozAfterPaint") {
+                this.onPaint();
+              } else if (event.type == "MozLayerTreeCleared") {
+                this.onLayersCleared(event.originalTarget);
+              } else if (event.type == "TabRemotenessChange") {
+                this.onRemotenessChange(event.target);
+              }
+
+              this.postActions();
+            },
+
+            tinfo: function(tab) {
+              if (tab) {
+                return tab._tPos + "(" + tab.linkedBrowser.currentURI.spec + ")";
+              } else {
+                return "null";
+              }
+            },
+
+            log: function(s) {
+              if (!this.logging)
+                return;
+              dump(s + "\n");
+            },
+
+            logState: function(prefix) {
+              if (!this.logging)
+                return;
+
+              dump(prefix + " ");
+              for (let i = 0; i < this.tabbrowser.tabs.length; i++) {
+                let tab = this.tabbrowser.tabs[i];
+                let state = this.getTabState(tab);
+
+                dump(i + ":");
+                if (tab === this.lastVisibleTab) dump("V");
+                if (tab === this.loadingTab) dump("L");
+                if (tab === this.requestedTab) dump("R");
+                if (state == this.STATE_LOADED) dump("(+)");
+                if (state == this.STATE_LOADING) dump("(+?)");
+                if (state == this.STATE_UNLOADED) dump("(-)");
+                if (state == this.STATE_UNLOADING) dump("(-?)");
+                dump(" ");
+              }
+              dump("\n");
+            },
+          };
+          this._switcher = switcher;
+          switcher.init();
+          return switcher;
+        ]]></body>
+      </method>
+
       <!-- BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
            MAKE SURE TO ADD IT HERE AS WELL. -->
       <property name="canGoBack"
                 onget="return this.mCurrentBrowser.canGoBack;"
                 readonly="true"/>
 
       <property name="canGoForward"
                 onget="return this.mCurrentBrowser.canGoForward;"
@@ -3375,16 +3898,20 @@
                               .getService(nsIEventListenerService);
           els.removeSystemEventListener(document, "keydown", this, false);
           els.removeSystemEventListener(document, "keypress", this, false);
           window.removeEventListener("sizemodechange", this, false);
 
           if (gMultiProcessBrowser) {
             messageManager.removeMessageListener("DOMTitleChanged", this);
             messageManager.removeMessageListener("contextmenu", this);
+
+            if (this._switcher) {
+              this._switcher.destroy();
+            }
           }
         ]]>
       </destructor>
 
       <!-- Deprecated stuff, implemented for backwards compatibility. -->
       <method name="enterTabbedMode">
         <body>
           Application.console.log("enterTabbedMode is an obsolete method and " +
@@ -3399,164 +3926,16 @@
         </body>
       </method>
       <method name="getStripVisibility">
         <body>
           return this.tabContainer.visible;
         </body>
       </method>
 
-      <method name="_showBusySpinnerRemoteBrowser">
-        <parameter name="aBrowser"/>
-        <body><![CDATA[
-          aBrowser.setAttribute("pendingpaint", "true");
-          if (this._contentWaitingCount <= 0) {
-            // We are not currently spinning
-            this.setAttribute("pendingpaint", "true");
-            this._contentWaitingCount = 1;
-          } else {
-            this._contentWaitingCount++;
-          }
-        ]]></body>
-      </method>
-
-      <method name="_hideBusySpinnerRemoteBrowser">
-        <parameter name="aBrowser"/>
-        <body><![CDATA[
-          aBrowser.removeAttribute("pendingpaint");
-          this._contentWaitingCount--;
-          if (this._contentWaitingCount <= 0) {
-            this.removeAttribute("pendingpaint");
-          }
-        ]]></body>
-      </method>
-
-      <method name="_prepareForTabSwitch">
-        <parameter name="toTab"/>
-        <parameter name="fromTab"/>
-        <body><![CDATA[
-          const kTabSwitchTimeout = 300;
-          let toBrowser = this.getBrowserForTab(toTab);
-          let fromBrowser = fromTab ? this.getBrowserForTab(fromTab)
-                                    : null;
-
-          // We only want to wait for the MozAfterRemotePaint event if
-          // the tab we're switching to is a remote tab, and if the tab
-          // we're switching to isn't the one we've already got. The latter
-          // case can occur when closing tabs before the currently selected
-          // one.
-          let shouldWait = toBrowser.getAttribute("remote") == "true" &&
-                           toBrowser != fromBrowser;
-
-          let switchPromise;
-
-          if (shouldWait) {
-            let timeoutId;
-            let panels = this.mPanelContainer;
-
-            // Both the timeout and MozAfterPaint promises use this same
-            // logic to determine whether they should carry out the tab
-            // switch, or reject it outright.
-            let attemptTabSwitch = (aResolve, aReject) => {
-              if (this.selectedBrowser == toBrowser) {
-                aResolve();
-              } else {
-                // We switched away or closed the browser before we timed
-                // out. We reject, which will cancel the tab switch.
-                aReject();
-              }
-            };
-
-            let timeoutPromise = new Promise((aResolve, aReject) => {
-              timeoutId = setTimeout(() => {
-                if (toBrowser.isRemoteBrowser) {
-                  // The browser's remoteness could have changed since we
-                  // setTimeout so only show the spinner if it's still remote.
-                  this._showBusySpinnerRemoteBrowser(toBrowser);
-                }
-                attemptTabSwitch(aResolve, aReject);
-              }, kTabSwitchTimeout);
-            });
-
-            let paintPromise = new Promise((aResolve, aReject) => {
-              let onRemotePaint = () => {
-                toBrowser.removeEventListener("MozAfterRemotePaint", onRemotePaint);
-                this._hideBusySpinnerRemoteBrowser(toBrowser);
-                clearTimeout(timeoutId);
-                attemptTabSwitch(aResolve, aReject);
-              };
-              toBrowser.addEventListener("MozAfterRemotePaint", onRemotePaint);
-              toBrowser.QueryInterface(Ci.nsIFrameLoaderOwner)
-                       .frameLoader
-                       .requestNotifyAfterRemotePaint();
-               // We need to activate the docShell on the tab we're switching
-               // to - otherwise, we won't initiate a remote paint request and
-               // therefore we won't get the MozAfterRemotePaint event that we're
-               // waiting for.
-               // Note that this happens, as we require, even if the timeout in the
-               // timeoutPromise triggers before the paintPromise even runs.
-               toBrowser.docShellIsActive = true;
-            });
-
-            switchPromise = Promise.race([paintPromise, timeoutPromise]);
-          } else {
-            // Activate the docShell on the tab we're switching to.
-            toBrowser.docShellIsActive = true;
-
-            // No need to wait - just resolve immediately to do the switch ASAP.
-            switchPromise = Promise.resolve();
-          }
-
-          return switchPromise;
-        ]]></body>
-      </method>
-
-      <method name="_deactivateContent">
-        <parameter name="tab"/>
-        <body><![CDATA[
-          // It's unlikely, yet possible, that while we were waiting
-          // to deactivate this tab, that something closed it and wiped
-          // out the browser. For example, during a tab switch, while waiting
-          // for the MozAfterRemotePaint event to fire, something closes the
-          // original tab that the user had selected. If that's the case, then
-          // there's nothing to deactivate.
-          let browser = this.getBrowserForTab(tab);
-          if (browser && this.selectedBrowser != browser) {
-            browser.docShellIsActive = false;
-          }
-        ]]></body>
-      </method>
-
-      <method name="_finalizeTabSwitch">
-        <parameter name="toTab"/>
-        <parameter name="fromTab"/>
-        <body><![CDATA[
-          this._adjustFocusAfterTabSwitch(toTab);
-          this._deactivateContent(fromTab);
-
-          let toBrowser = this.getBrowserForTab(toTab);
-          toBrowser.setAttribute("type", "content-primary");
-
-          let fromBrowser = this.getBrowserForTab(fromTab);
-          // It's possible that the tab we're switching from closed
-          // before we were able to finalize, in which case, fromBrowser
-          // doesn't exist.
-          if (fromBrowser) {
-            fromBrowser.setAttribute("type", "content-targetable");
-          }
-        ]]></body>
-      </method>
-
-      <method name="_cancelTabSwitch">
-        <parameter name="toTab"/>
-        <body><![CDATA[
-          this._deactivateContent(toTab);
-        ]]></body>
-      </method>
-
       <property name="mContextTab" readonly="true"
                 onget="return TabContextMenu.contextTab;"/>
       <property name="mPrefs" readonly="true"
                 onget="return Services.prefs;"/>
       <property name="mTabContainer" readonly="true"
                 onget="return this.tabContainer;"/>
       <property name="mTabs" readonly="true"
                 onget="return this.tabs;"/>
@@ -5037,52 +5416,71 @@
   <binding id="tabbrowser-tab" display="xul:hbox"
            extends="chrome://global/content/bindings/tabbox.xml#tab">
     <resources>
       <stylesheet src="chrome://browser/content/tabbrowser.css"/>
     </resources>
 
     <content context="tabContextMenu" closetabtext="&closeTab.label;">
       <xul:stack class="tab-stack" flex="1">
-        <xul:hbox xbl:inherits="pinned,selected,titlechanged,fadein"
+        <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged,fadein"
                   class="tab-background">
-          <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+          <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
                     class="tab-background-start"/>
-          <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+          <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
                     class="tab-background-middle"/>
-          <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+          <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
                     class="tab-background-end"/>
         </xul:hbox>
-        <xul:hbox xbl:inherits="pinned,selected,titlechanged"
+        <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
                   class="tab-content" align="center">
-          <xul:image xbl:inherits="fadein,pinned,busy,progress,selected"
+          <xul:image xbl:inherits="fadein,pinned,busy,progress,selected,visuallyselected"
                      class="tab-throbber"
                      role="presentation"
                      layer="true" />
-          <xul:image xbl:inherits="src=image,fadein,pinned,selected,busy,crashed"
+          <xul:image xbl:inherits="src=image,fadein,pinned,selected,visuallyselected,busy,crashed"
                      anonid="tab-icon-image"
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
           <xul:image xbl:inherits="crashed,busy"
                      class="tab-icon-overlay"
                      role="presentation"/>
           <xul:label flex="1"
                      anonid="tab-label"
-                     xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected"
+                     xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected,visuallyselected"
                      class="tab-text tab-label"
                      role="presentation"/>
           <xul:toolbarbutton anonid="close-button"
-                             xbl:inherits="fadein,pinned,selected"
+                             xbl:inherits="fadein,pinned,selected,visuallyselected"
                              class="tab-close-button close-icon"/>
         </xul:hbox>
       </xul:stack>
     </content>
 
     <implementation>
+
+      <property name="_selected">
+        <setter>
+          <![CDATA[
+          // in e10s we want to only pseudo-select a tab before its rendering is done, so that
+          // the rest of the system knows that the tab is selected, but we don't want to update its
+          // visual status to selected until after we receive confirmation that its content has painted.
+          this._logicallySelected = val;
+
+          // If we're non-e10s we should update the visual selection as well at the same time
+          if (!gMultiProcessBrowser) {
+            this._visuallySelected = val;
+          }
+
+          return val;
+        ]]>
+        </setter>
+      </property>
+
       <property name="label">
         <getter>
           return this.getAttribute("label");
         </getter>
         <setter>
           this.setAttribute("label", val);
           let event = new CustomEvent("TabLabelModified", {
             bubbles: true,
@@ -5509,47 +5907,27 @@
         <![CDATA[
           if (val < 0 || val >= this.childNodes.length)
             return val;
 
           let toTab = this.getRelatedElement(this.childNodes[val]);
           let fromTab = this._selectedPanel ? this.getRelatedElement(this._selectedPanel)
                                             : null;
 
-          let switchPromise = gBrowser._prepareForTabSwitch(toTab, fromTab);
+          gBrowser._getSwitcher().requestTab(toTab);
 
           var panel = this._selectedPanel;
           var newPanel = this.childNodes[val];
           this._selectedPanel = newPanel;
           if (this._selectedPanel != panel) {
             var event = document.createEvent("Events");
             event.initEvent("select", true, true);
             this.dispatchEvent(event);
 
             this._selectedIndex = val;
-
-            switchPromise.then(() => {
-              // If we cannot find the tabpanel that we were trying to switch to, then
-              // it must have been removed before our Promise could be resolved. In
-              // that case, we just cancel the tab switch.
-              var updatedTabIndex = Array.indexOf(this.childNodes, newPanel);
-              if (updatedTabIndex == -1) {
-                gBrowser._cancelTabSwitch(toTab);
-              } else {
-                this.setAttribute("selectedIndex", updatedTabIndex);
-                gBrowser._finalizeTabSwitch(toTab, fromTab);
-              }
-            }, () => {
-              // If the promise rejected, that means we don't want to actually
-              // flip the deck, so we cancel the tab switch.
-              // We need to nullcheck the method we're about to call because
-              // the binding might be dead at this point.
-              if (gBrowser._cancelTabSwitch)
-                gBrowser._cancelTabSwitch(toTab);
-            });
           }
 
           return val;
         ]]>
         </setter>
       </property>
     </implementation>
   </binding>
--- a/browser/base/content/test/general/browser_selectTabAtIndex.js
+++ b/browser/base/content/test/general/browser_selectTabAtIndex.js
@@ -1,14 +1,17 @@
 function test() {
   for (let i = 0; i < 9; i++)
     gBrowser.addTab();
 
   var isLinux = navigator.platform.indexOf("Linux") == 0;
   for (let i = 9; i >= 1; i--) {
+    // Make sure the keystroke goes to chrome.
+    document.activeElement.blur();
+
     EventUtils.synthesizeKey(i.toString(), { altKey: isLinux, accelKey: !isLinux });
 
     is(gBrowser.tabContainer.selectedIndex, (i == 9 ? gBrowser.tabs.length : i) - 1,
        (isLinux ? "Alt" : "Accel") + "+" + i + " selects expected tab");
   }
 
   gBrowser.selectTabAtIndex(-3);
   is(gBrowser.tabContainer.selectedIndex, gBrowser.tabs.length - 3,
--- a/browser/base/content/test/general/browser_tabfocus.js
+++ b/browser/base/content/test/general/browser_tabfocus.js
@@ -211,37 +211,36 @@ add_task(function*() {
   // When focus is in the tab bar, it should be retained there
   yield expectFocusShift(function () gBrowser.selectedTab.focus(),
                          "main-window", "tab2", true,
                          "focusing tab element");
   yield expectFocusShift(function () gBrowser.selectedTab = tab1,
                          "main-window", "tab1", true,
                          "tab change when selected tab element was focused");
 
-  let paintWaiter;
+  let switchWaiter;
   if (gMultiProcessBrowser) {
-    paintWaiter = new Promise((resolve, reject) => {
-      browser2.addEventListener("MozAfterRemotePaint", function paintListener() {
-        browser2.removeEventListener("MozAfterRemotePaint", paintListener, false);
+    switchWaiter = new Promise((resolve, reject) => {
+      gBrowser.addEventListener("TabSwitchDone", function listener() {
+        gBrowser.removeEventListener("TabSwitchDone", listener);
         executeSoon(resolve);
-      }, false);
-      browser2.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.requestNotifyAfterRemotePaint();
+      });
     });
   }
 
   yield expectFocusShift(function () gBrowser.selectedTab = tab2,
                          "main-window", "tab2", true,
                          "another tab change when selected tab element was focused");
 
   // When this a remote browser, wait for the paint on the second browser so that
   // any post tab-switching stuff has time to complete before blurring the tab.
   // Otherwise, the _adjustFocusAfterTabSwitch in tabbrowser gets confused and
   // isn't sure what tab is really focused.
   if (gMultiProcessBrowser) {
-    yield paintWaiter;
+    yield switchWaiter;
   }
 
   yield expectFocusShift(function () gBrowser.selectedTab.blur(),
                          "main-window", null, true,
                          "blurring tab element");
 
   // focusing the url field should switch active focus away from the browser but
   // not clear what would be the focus in the browser
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -210,16 +210,17 @@
 @RESPATH@/components/dom_css.xpt
 @RESPATH@/components/dom_devicestorage.xpt
 @RESPATH@/components/dom_events.xpt
 @RESPATH@/components/dom_geolocation.xpt
 @RESPATH@/components/dom_media.xpt
 @RESPATH@/components/dom_network.xpt
 @RESPATH@/components/dom_notification.xpt
 @RESPATH@/components/dom_html.xpt
+@RESPATH@/components/dom_icc.xpt
 @RESPATH@/components/dom_offline.xpt
 @RESPATH@/components/dom_json.xpt
 @RESPATH@/components/dom_power.xpt
 @RESPATH@/components/dom_quota.xpt
 @RESPATH@/components/dom_range.xpt
 @RESPATH@/components/dom_security.xpt
 @RESPATH@/components/dom_settings.xpt
 @RESPATH@/components/dom_permissionsettings.xpt
--- a/browser/themes/linux/browser-lightweightTheme.css
+++ b/browser/themes/linux/browser-lightweightTheme.css
@@ -6,26 +6,26 @@
 %filter substitution
 
 /*
  * LightweightThemeListener will append the current lightweight theme's header
  * image to the background-image for each of the following rulesets.
  */
 
 /* Lightweight theme on tabs */
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[visuallyselected=true]:-moz-lwtheme::before,
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[visuallyselected=true]:-moz-lwtheme::before {
   background-attachment: scroll, fixed;
   background-color: transparent;
   background-image: @fgTabTextureLWT@;/*, lwtHeader;*/
   background-position: 0 0, right top;
   background-repeat: repeat-x, no-repeat;
 }
 
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[visuallyselected=true]:-moz-lwtheme {
   background-attachment: scroll, scroll, fixed;
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTextureLWT@;/*,
                     lwtHeader;*/
   background-position: 0 0, 0 0, right top;
   background-repeat: repeat-x, repeat-x, no-repeat;
 }
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1836,25 +1836,25 @@ richlistitem[type~="action"][actiontype=
 /* Tab drag and drop */
 .tab-drop-indicator {
   list-style-image: url(chrome://browser/skin/tabbrowser/tabDragIndicator.png);
   margin-bottom: -9px;
   z-index: 3;
 }
 
 /* Tab close button */
-.tab-close-button:not([selected]):not(:hover) {
+.tab-close-button:not([visuallyselected]):not(:hover) {
   background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 64, 16, 48);
 }
 
-#TabsToolbar[brighttext] .tab-close-button:not([selected]):not(:hover) {
+#TabsToolbar[brighttext] .tab-close-button:not([visuallyselected]):not(:hover) {
   background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 80, 16, 64);
 }
 
-.tab-close-button:not([selected]):not(:hover):-moz-lwtheme-darktext {
+.tab-close-button:not([visuallyselected]):not(:hover):-moz-lwtheme-darktext {
   background-image: -moz-image-rect(url("chrome://global/skin/icons/close.svg"), 0, 96, 16, 80);
 }
 
 /* Tabstrip new tab button */
 .tabs-newtab-button,
 #TabsToolbar > #new-tab-button ,
 #TabsToolbar > #wrapper-new-tab-button > #new-tab-button {
   list-style-image: url("moz-icon://stock/gtk-add?size=menu");
@@ -1919,17 +1919,17 @@ richlistitem[type~="action"][actiontype=
   display: none;
 }
 
 /* All tabs menupopup */
 .alltabs-item > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
-.alltabs-item[selected="true"] {
+.alltabs-item[visuallyselected="true"] {
   font-weight: bold;
 }
 
 .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
 }
 
 .alltabs-item[tabIsVisible] {
--- a/browser/themes/osx/browser-lightweightTheme.css
+++ b/browser/themes/osx/browser-lightweightTheme.css
@@ -5,34 +5,34 @@
 %include shared.inc
 
 /*
  * LightweightThemeListener will append the current lightweight theme's header
  * image to the background-image for each of the following rulesets.
  */
 
 /* Lightweight theme on tabs */
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[visuallyselected=true]:-moz-lwtheme::before,
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[visuallyselected=true]:-moz-lwtheme::before {
   background-attachment: scroll, fixed;
   background-color: transparent;
   background-image: @fgTabTextureLWT@;/*, lwtHeader;*/
   background-position: 0 0, right top;
   background-repeat: repeat-x, no-repeat;
 }
 
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[visuallyselected=true]:-moz-lwtheme {
   background-attachment: scroll, scroll, fixed;
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTextureLWT@;/*,
                     lwtHeader;*/
   background-position: 0 0, 0 0, right top;
   background-repeat: repeat-x, repeat-x, no-repeat;
 }
 
 @media (min-resolution: 2dppx) {
-  #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
+  #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[visuallyselected=true]:-moz-lwtheme {
     background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
                       @fgTabTextureLWT@;/*,
                       lwtHeader;*/
   }
 }
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3040,36 +3040,36 @@ toolbarbutton.chevron > .toolbarbutton-m
   /* image preloading hack from shared/tabs.inc.css */
   #tabbrowser-tabs::before {
     background-image:
       url(chrome://browser/skin/tabbrowser/tab-background-end@2x.png),
       url(chrome://browser/skin/tabbrowser/tab-background-middle@2x.png),
       url(chrome://browser/skin/tabbrowser/tab-background-start@2x.png);
   }
 
-  .tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]),
+  .tabbrowser-tab:hover > .tab-stack > .tab-background:not([visuallyselected=true]),
   .tabs-newtab-button:hover {
     background-image: url(chrome://browser/skin/tabbrowser/tab-background-start@2x.png),
                       url(chrome://browser/skin/tabbrowser/tab-background-middle@2x.png),
                       url(chrome://browser/skin/tabbrowser/tab-background-end@2x.png);
   }
 
-  .tab-background-middle[selected=true] {
+  .tab-background-middle[visuallyselected=true] {
     background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
                       @fgTabTexture@,
                       none;
   }
 
-  .tab-background-start[selected=true]:-moz-locale-dir(ltr)::after,
-  .tab-background-end[selected=true]:-moz-locale-dir(rtl)::after {
+  .tab-background-start[visuallyselected=true]:-moz-locale-dir(ltr)::after,
+  .tab-background-end[visuallyselected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-start@2x.png);
   }
 
-  .tab-background-end[selected=true]:-moz-locale-dir(ltr)::after,
-  .tab-background-start[selected=true]:-moz-locale-dir(rtl)::after {
+  .tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr)::after,
+  .tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-end@2x.png);
   }
 
   .tab-icon-image {
     list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
   }
 
   .tab-throbber[busy] {
@@ -3077,50 +3077,50 @@ toolbarbutton.chevron > .toolbarbutton-m
   }
 
   .tab-throbber[progress] {
     list-style-image: url("chrome://browser/skin/tabbrowser/loading@2x.png");
   }
 
   /* Background tab separators */
   #tabbrowser-tabs[movingtab] > .tabbrowser-tab[beforeselected]:not([last-visible-tab])::after,
-  .tabbrowser-tab:not([selected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
-  #tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([selected]):not([beforehovered]):not(:hover)::after {
+  .tabbrowser-tab:not([visuallyselected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
+  #tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([visuallyselected]):not([beforehovered]):not(:hover)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-separator@2x.png);
   }
 }
 
-.tabbrowser-tab:not(:hover) > .tab-stack > .tab-content > .tab-icon-image:not([selected="true"]) {
+.tabbrowser-tab:not(:hover) > .tab-stack > .tab-content > .tab-icon-image:not([visuallyselected="true"]) {
   opacity: .9;
 }
 
 /*
  * Force the overlay to create a new stacking context so it always appears on
  * top of the icon.
  */
 .tab-icon-overlay {
   opacity: 0.9999;
 }
 
-.tab-label:not([selected="true"]) {
+.tab-label:not([visuallyselected="true"]) {
   opacity: .7;
 }
 
 .tabbrowser-tab,
 .tabs-newtab-button {
   font: message-box;
   border: none;
 }
 
-.tabbrowser-tab[selected=true]:not(:-moz-lwtheme) {
+.tabbrowser-tab[visuallyselected=true]:not(:-moz-lwtheme) {
   /* overriding tabbox.css */
   color: inherit;
 }
 
-.tabbrowser-tab[selected=true] {
+.tabbrowser-tab[visuallyselected=true] {
   /* overriding tabbox.css */
   text-shadow: inherit;
 }
 
 .tabs-newtab-button > .toolbarbutton-icon {
   -moz-box-align: center;
   border: solid transparent;
   border-width: 0 11px;
@@ -3160,17 +3160,17 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 
 /* Background tabs:
  *
  * Decrease the height of the hoverable region of background tabs whenever the tabs are at the top
  * of the window (e.g. no menubar, tabs in titlebar, etc.) to make it easier to drag the window by
  * the titlebar. We don't need this in fullscreen since window dragging is not an issue there.
  */
-#main-window[tabsintitlebar]:not([inFullscreen]) .tab-background-middle:not([selected=true]) {
+#main-window[tabsintitlebar]:not([inFullscreen]) .tab-background-middle:not([visuallyselected=true]) {
   clip-path: url(chrome://browser/content/browser.xul#tab-hover-clip-path);
 }
 
 /**
  * Tab Drag and Drop
  */
 
 .tab-drop-indicator {
@@ -3196,22 +3196,22 @@ toolbarbutton.chevron > .toolbarbutton-m
 
 .tab-close-button {
   -moz-appearance: none;
   border: none !important;
   background: none;
   cursor: default;
 }
 
-#TabsToolbar[brighttext] .tab-close-button.close-icon:not([selected=true]):not(:hover) {
+#TabsToolbar[brighttext] .tab-close-button.close-icon:not([visuallyselected=true]):not(:hover) {
   -moz-image-region: rect(0, 64px, 16px, 48px);
 }
 
 @media (min-resolution: 2dppx) {
-  #TabsToolbar[brighttext] .tab-close-button.close-icon:not([selected=true]):not(:hover) {
+  #TabsToolbar[brighttext] .tab-close-button.close-icon:not([visuallyselected=true]):not(:hover) {
     -moz-image-region: rect(0, 128px, 32px, 96px);
   }
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-image-region: rect(0, 13px, 20px, 0);
   margin: 0 0 var(--tab-toolbar-navbar-overlap);
--- a/browser/themes/osx/devedition.css
+++ b/browser/themes/osx/devedition.css
@@ -80,16 +80,16 @@
 
 /* Don't use the default background for tabs toolbar */
 #TabsToolbar {
   -moz-appearance: none !important;
 }
 
 /* Tab styling - make sure to use an inverted icon for the selected tab
    (brighttext only covers the unselected tabs) */
-.tab-close-button[selected=true]:not(:hover) {
+.tab-close-button[visuallyselected=true]:not(:hover) {
   -moz-image-region: rect(0, 64px, 16px, 48px);
 }
 @media (min-resolution: 2dppx) {
-  .tab-close-button[selected=true]:not(:hover) {
+  .tab-close-button[visuallyselected=true]:not(:hover) {
     -moz-image-region: rect(0, 128px, 32px, 96px);
   }
 }
--- a/browser/themes/shared/devedition.inc.css
+++ b/browser/themes/shared/devedition.inc.css
@@ -136,27 +136,27 @@
   -moz-margin-start: 0;
 }
 
 .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox {
   -moz-padding-end: 0;
   -moz-padding-start: 0;
 }
 
-.tab-background-start[selected=true]::after,
-.tab-background-start[selected=true]::before,
+.tab-background-start[visuallyselected=true]::after,
+.tab-background-start[visuallyselected=true]::before,
 .tab-background-start,
 .tab-background-end,
-.tab-background-end[selected=true]::after,
-.tab-background-end[selected=true]::before {
+.tab-background-end[visuallyselected=true]::after,
+.tab-background-end[visuallyselected=true]::before {
   width: 0;
 }
 
-.tab-background-start[selected=true]::after,
-.tab-background-end[selected=true]::after {
+.tab-background-start[visuallyselected=true]::after,
+.tab-background-end[visuallyselected=true]::after {
   -moz-margin-start: 0;
 }
 /* End override @tabCurveHalfWidth@ and @tabCurveWidth@ */
 
 #urlbar ::-moz-selection,
 #navigator-toolbox .searchbar-textbox ::-moz-selection,
 .browserContainer > findbar ::-moz-selection {
   background-color: var(--chrome-selection-background-color);
@@ -265,29 +265,29 @@ searchbar:not([oneoffui]) .search-go-but
 }
 
 .tab-background {
   visibility: hidden;
 }
 
 /* Make the tab splitter 1px wide with a solid background. */
 #tabbrowser-tabs[movingtab] > .tabbrowser-tab[beforeselected]:not([last-visible-tab])::after,
-.tabbrowser-tab:not([selected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
-#tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([selected]):not([beforehovered]):not(:hover)::after {
+.tabbrowser-tab:not([visuallyselected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
+#tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([visuallyselected]):not([beforehovered]):not(:hover)::after {
   background: var(--tab-separator-color);
   width: 1px;
   -moz-margin-start: 0;
   -moz-margin-end: -1px;
 }
 
 /* For the last tab separator, use margin-start of -1px to prevent jittering
    due to the ::after element causing the width of the tab to extend, which
    causes an overflow and makes it disappear, which removes the overflow and
    causes it to reappear, etc, etc. */
-#tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([selected]):not([beforehovered]):not(:hover)::after {
+#tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([visuallyselected]):not([beforehovered]):not(:hover)::after {
   -moz-margin-start: -1px;
   -moz-margin-end: 0;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down,
 .tabbrowser-arrowscrollbox > .scrollbutton-up {
   background-color: var(--tab-background-color);
   border-color: transparent;
@@ -299,29 +299,29 @@ searchbar:not([oneoffui]) .search-go-but
 }
 
 .tabbrowser-tab {
   /* We normally rely on other tab elements for pointer events, but this
      theme hides those so we need it set here instead */
   pointer-events: auto;
 }
 
-.tabbrowser-tab[pinned][titlechanged]:not([selected="true"]) > .tab-stack > .tab-content {
+.tabbrowser-tab[pinned][titlechanged]:not([visuallyselected="true"]) > .tab-stack > .tab-content {
   background-image: var(--pinned-tab-glow);
   background-position: center;
   background-size: 100%;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover,
 .tabbrowser-tab:hover {
   background-color: var(--tab-hover-background-color);
 }
 
-.tabbrowser-tab[selected] {
+.tabbrowser-tab[visuallyselected] {
   color: var(--tab-selection-color) !important; /* Override color: inherit */
   background-color: var(--tab-selection-background-color);
   box-shadow: var(--tab-selection-box-shadow);
 }
 
 /* Don't need space for the tab curves (66px - 30px) */
 .tabs-newtab-button {
   width: 36px;
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -42,17 +42,17 @@
   -moz-box-align: stretch;
 }
 
 .tabbrowser-tab[remote] {
   text-decoration: underline;
 }
 
 /* The selected tab should appear above adjacent tabs, .tabs-newtab-button and the highlight of #nav-bar */
-.tabbrowser-tab[selected=true] {
+.tabbrowser-tab[visuallyselected=true] {
   position: relative;
   z-index: 2;
 }
 
 .tab-background-middle {
   -moz-box-flex: 1;
   background-clip: padding-box;
   border-left: @tabCurveHalfWidth@ solid transparent;
@@ -162,132 +162,132 @@
   opacity: 0;
 }
 
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator,
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator {
   transition: opacity 150ms ease;
 }
 
-.tab-background-start[selected=true]::after,
-.tab-background-start[selected=true]::before,
+.tab-background-start[visuallyselected=true]::after,
+.tab-background-start[visuallyselected=true]::before,
 .tab-background-start,
 .tab-background-end,
-.tab-background-end[selected=true]::after,
-.tab-background-end[selected=true]::before {
+.tab-background-end[visuallyselected=true]::after,
+.tab-background-end[visuallyselected=true]::before {
   min-height: var(--tab-min-height);
   width: @tabCurveWidth@;
 }
 
-.tabbrowser-tab:not([selected=true]),
+.tabbrowser-tab:not([visuallyselected=true]),
 .tabbrowser-tab:-moz-lwtheme {
   color: inherit;
 }
 
 /* Selected tab */
 
 /*
  Tab background pseudo-elements which are positioned above .tab-background-start/end:
    - ::before - provides the fill of the tab curve and is clipped to the tab shape. This is where
                 pointer events go for the curve.
    - ::after  - provides the border/stroke of the tab curve and is overlayed above ::before.  Pointer
                 events go through to ::before to get the proper shape.
  */
 
 
-.tab-background-start[selected=true]::after,
-.tab-background-end[selected=true]::after {
+.tab-background-start[visuallyselected=true]::after,
+.tab-background-end[visuallyselected=true]::after {
   /* position ::after on top of its parent */
   -moz-margin-start: -@tabCurveWidth@;
   background-size: 100% 100%;
   content: "";
   display: -moz-box;
   position: relative;
 }
 
-.tab-background-start[selected=true]::before,
-.tab-background-end[selected=true]::before {
+.tab-background-start[visuallyselected=true]::before,
+.tab-background-end[visuallyselected=true]::before {
   /* all ::before pseudo elements */
   content: "";
   display: -moz-box;
 }
 
-.tab-background-start[selected=true]:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
-.tab-background-end[selected=true]:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
+.tab-background-start[visuallyselected=true]:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
+.tab-background-end[visuallyselected=true]:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
   background-image: url(chrome://browser/skin/tabbrowser/tab-selected-start.svg);
   background-size: 100% 100%;
 }
 
-.tab-background-end[selected=true]:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
-.tab-background-start[selected=true]:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
+.tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
+.tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
   background-image: url(chrome://browser/skin/tabbrowser/tab-selected-end.svg);
   background-size: 100% 100%;
 }
 
 /* For lightweight themes, clip the header image on start, middle, and end. */
-.tab-background-start[selected=true]:-moz-locale-dir(ltr):-moz-lwtheme::before,
-.tab-background-end[selected=true]:-moz-locale-dir(rtl):-moz-lwtheme::before {
+.tab-background-start[visuallyselected=true]:-moz-locale-dir(ltr):-moz-lwtheme::before,
+.tab-background-end[visuallyselected=true]:-moz-locale-dir(rtl):-moz-lwtheme::before {
   clip-path: url(chrome://browser/content/browser.xul#tab-curve-clip-path-start);
 }
 
-.tab-background-end[selected=true]:-moz-locale-dir(ltr):-moz-lwtheme::before,
-.tab-background-start[selected=true]:-moz-locale-dir(rtl):-moz-lwtheme::before {
+.tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr):-moz-lwtheme::before,
+.tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl):-moz-lwtheme::before {
   clip-path: url(chrome://browser/content/browser.xul#tab-curve-clip-path-end);
 }
 
-.tab-background-start[selected=true]:-moz-locale-dir(ltr)::after,
-.tab-background-end[selected=true]:-moz-locale-dir(rtl)::after {
+.tab-background-start[visuallyselected=true]:-moz-locale-dir(ltr)::after,
+.tab-background-end[visuallyselected=true]:-moz-locale-dir(rtl)::after {
   background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-start.png);
 }
 
-.tab-background-end[selected=true]:-moz-locale-dir(ltr)::after,
-.tab-background-start[selected=true]:-moz-locale-dir(rtl)::after {
+.tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr)::after,
+.tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl)::after {
   background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-end.png);
 }
 
-.tab-background-middle[selected=true] {
+.tab-background-middle[visuallyselected=true] {
   background-clip: padding-box, padding-box, content-box;
   background-color: @fgTabBackgroundColor@;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTexture@,
                     none;
   background-repeat: repeat-x;
   background-size: auto 100%;
   /* The padding-top combined with background-clip: content-box (the bottom-most) ensure the
      background-color doesn't extend above the top border. */
   padding-top: 2px;
 }
 
 /* Selected tab lightweight theme styles.
    See browser-lightweightTheme.css for information about run-time changes to LWT styles. */
-.tab-background-middle[selected=true]:-moz-lwtheme {
+.tab-background-middle[visuallyselected=true]:-moz-lwtheme {
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTextureLWT@;/*,
                     lwtHeader;*/
   /* Don't stretch the LWT header images */
   background-size: auto 100%, auto 100%, auto auto;
 }
 
 /* These LWT styles are normally overridden by browser-lightweightTheme.css */
-.tab-background-start[selected=true]:-moz-lwtheme::before,
-.tab-background-end[selected=true]:-moz-lwtheme::before {
+.tab-background-start[visuallyselected=true]:-moz-lwtheme::before,
+.tab-background-end[visuallyselected=true]:-moz-lwtheme::before {
   background-image: @fgTabTextureLWT@;
 }
 
-.tab-background-start[selected=true]:-moz-lwtheme::before,
-.tab-background-end[selected=true]:-moz-lwtheme::before,
-.tab-background-middle[selected=true]:-moz-lwtheme {
+.tab-background-start[visuallyselected=true]:-moz-lwtheme::before,
+.tab-background-end[visuallyselected=true]:-moz-lwtheme::before,
+.tab-background-middle[visuallyselected=true]:-moz-lwtheme {
   background-color: transparent;
 }
 
 /* End selected tab */
 
 /* new tab button border and gradient on hover */
-.tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]),
+.tabbrowser-tab:hover > .tab-stack > .tab-background:not([visuallyselected=true]),
 .tabs-newtab-button:hover {
   background-image: url(chrome://browser/skin/tabbrowser/tab-background-start.png),
                     url(chrome://browser/skin/tabbrowser/tab-background-middle.png),
                     url(chrome://browser/skin/tabbrowser/tab-background-end.png);
   background-position: left bottom, @tabCurveWidth@ bottom, right bottom;
   background-repeat: no-repeat;
   background-size: @tabCurveWidth@ 100%, calc(100% - (2 * @tabCurveWidth@)) 100%, @tabCurveWidth@ 100%;
 }
@@ -306,28 +306,28 @@
 /* Pinned tabs */
 
 /* Pinned tab separators need position: absolute when positioned (during overflow). */
 #tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned]::before {
   height: 100%;
   position: absolute;
 }
 
-.tabbrowser-tab[pinned][titlechanged]:not([selected="true"]) > .tab-stack > .tab-content {
+.tabbrowser-tab[pinned][titlechanged]:not([visuallyselected="true"]) > .tab-stack > .tab-content {
   background-image: radial-gradient(farthest-corner at center bottom, rgb(255,255,255) 3%, rgba(186,221,251,0.75) 20%, rgba(127,179,255,0.25) 40%, transparent 70%);
   background-position: center bottom var(--tab-toolbar-navbar-overlap);
   background-repeat: no-repeat;
   background-size: 85% 100%;
 }
 
 /* Background tab separators (3px wide).
    Also show separators beside the selected tab when dragging it. */
 #tabbrowser-tabs[movingtab] > .tabbrowser-tab[beforeselected]:not([last-visible-tab])::after,
-.tabbrowser-tab:not([selected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
-#tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([selected]):not([beforehovered]):not(:hover)::after {
+.tabbrowser-tab:not([visuallyselected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
+#tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([visuallyselected]):not([beforehovered]):not(:hover)::after {
   -moz-margin-start: -1.5px;
   -moz-margin-end: -1.5px;
   background-image: url(chrome://browser/skin/tabbrowser/tab-separator.png);
   background-position: left bottom var(--tab-toolbar-navbar-overlap);
   background-repeat: no-repeat;
   background-size: 3px 100%;
   content: "";
   display: -moz-box;
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -78,17 +78,17 @@
   @media (-moz-os-version: windows-vista),
          (-moz-os-version: windows-win7) {
     #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(:-moz-lwtheme),
     #browser-bottombox:not(:-moz-lwtheme),
     .browserContainer > findbar {
       background-color: @customToolbarColor@;
     }
 
-    .tab-background-middle[selected=true]:not(:-moz-lwtheme) {
+    .tab-background-middle[visuallyselected=true]:not(:-moz-lwtheme) {
       background-color: @customToolbarColor@;
     }
 
     #navigator-toolbox:not(:-moz-lwtheme)::after {
       background-color: #aabccf;
     }
 
     #urlbar:not(:-moz-lwtheme):not([focused]):hover,
--- a/browser/themes/windows/browser-lightweightTheme.css
+++ b/browser/themes/windows/browser-lightweightTheme.css
@@ -6,34 +6,34 @@
 %filter substitution
 
 /*
  * LightweightThemeListener will append the current lightweight theme's header
  * image to the background-image for each of the following rulesets.
  */
 
 /* Lightweight theme on tabs */
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[visuallyselected=true]:-moz-lwtheme::before,
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[visuallyselected=true]:-moz-lwtheme::before {
   background-attachment: scroll, fixed;
   background-color: transparent;
   background-image: @fgTabTextureLWT@;/*, lwtHeader;*/
   background-position: 0 0, right top;
   background-repeat: repeat-x, no-repeat;
 }
 
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[visuallyselected=true]:-moz-lwtheme {
   background-attachment: scroll, scroll, fixed;
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTextureLWT@;/*,
                     lwtHeader;*/
   background-position: 0 0, 0 0, right top;
   background-repeat: repeat-x, repeat-x, no-repeat;
 }
 
 @media (min-resolution: 1.25dppx) {
-  #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
+  #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[visuallyselected=true]:-moz-lwtheme {
     background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
                       @fgTabTextureLWT@;/*,
                       lwtHeader;*/
   }
 }
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1823,67 +1823,67 @@ toolbarbutton[type="socialmark"] > .tool
   /* image preloading hack from shared/tabs.inc.css */
   #tabbrowser-tabs::before {
     background-image:
       url(chrome://browser/skin/tabbrowser/tab-background-end@2x.png),
       url(chrome://browser/skin/tabbrowser/tab-background-middle@2x.png),
       url(chrome://browser/skin/tabbrowser/tab-background-start@2x.png);
   }
 
-  .tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]),
+  .tabbrowser-tab:hover > .tab-stack > .tab-background:not([visuallyselected=true]),
   .tabs-newtab-button:hover {
     background-image: url(chrome://browser/skin/tabbrowser/tab-background-start@2x.png),
                       url(chrome://browser/skin/tabbrowser/tab-background-middle@2x.png),
                       url(chrome://browser/skin/tabbrowser/tab-background-end@2x.png);
   }
 
-  .tab-background-middle[selected=true] {
+  .tab-background-middle[visuallyselected=true] {
     background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
                       @fgTabTexture@,
                       none;
   }
 
-  .tab-background-start[selected=true]:-moz-locale-dir(ltr)::after,
-  .tab-background-end[selected=true]:-moz-locale-dir(rtl)::after {
+  .tab-background-start[visuallyselected=true]:-moz-locale-dir(ltr)::after,
+  .tab-background-end[visuallyselected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-start@2x.png);
   }
 
-  .tab-background-end[selected=true]:-moz-locale-dir(ltr)::after,
-  .tab-background-start[selected=true]:-moz-locale-dir(rtl)::after {
+  .tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr)::after,
+  .tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-end@2x.png);
   }
 }
 
 %ifndef WINDOWS_AERO
 /* Use lighter colors of buttons and text in the titlebar on luna-blue */
 @media (-moz-windows-theme: luna-blue) {
   #tabbrowser-tabs[movingtab] > .tabbrowser-tab[beforeselected]:not([last-visible-tab])::after,
-  .tabbrowser-tab:not([selected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
-  #tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([selected]):not([beforehovered]):not(:hover)::after {
+  .tabbrowser-tab:not([visuallyselected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
+  #tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([visuallyselected]):not([beforehovered]):not(:hover)::after {
     background-image: url("chrome://browser/skin/tabbrowser/tab-separator-luna-blue.png");
   }
 }
 %endif
 
-#TabsToolbar[brighttext] .tab-close-button:not(:hover):not([selected="true"]) {
+#TabsToolbar[brighttext] .tab-close-button:not(:hover):not([visuallyselected="true"]) {
   -moz-image-region: rect(0, 64px, 16px, 48px) !important;
 }
 
 /* tabbrowser-tab focus ring */
 .tabbrowser-tab:focus > .tab-stack > .tab-content > .tab-label {
   outline: 1px dotted;
 }
 
 /* Background tabs:
  *
  * Decrease the height of the hoverable region of background tabs whenever the tabs are at the top
  * of the window (e.g. no menubar, tabs in titlebar, etc.) to make it easier to drag the window by
  * the titlebar. We don't need this in fullscreen since window dragging is not an issue there.
  */
-#main-window[tabsintitlebar][sizemode=normal] #toolbar-menubar[autohide="true"][inactive] + #TabsToolbar .tab-background-middle:not([selected=true]) {
+#main-window[tabsintitlebar][sizemode=normal] #toolbar-menubar[autohide="true"][inactive] + #TabsToolbar .tab-background-middle:not([visuallyselected=true]) {
   clip-path: url(chrome://browser/content/browser.xul#tab-hover-clip-path);
 }
 
 /* Tab DnD indicator */
 .tab-drop-indicator {
   list-style-image: url(chrome://browser/skin/tabbrowser/tabDragIndicator.png);
   margin-bottom: -9px;
   z-index: 3;
--- a/browser/themes/windows/devedition.css
+++ b/browser/themes/windows/devedition.css
@@ -153,11 +153,11 @@
   /* Reset image-region from the windows theme */
   -moz-image-region: auto !important;
   /* Add margin otherwise it looks weird */
   -moz-margin-start: 2px;
 }
 
 /* Tab styling - make sure to use an inverted icon for the selected tab
    (brighttext only covers the unselected tabs) */
-.tab-close-button[selected=true]:not(:hover) {
+.tab-close-button[visuallyselected=true]:not(:hover) {
   -moz-image-region: rect(0, 64px, 16px, 48px);
 }
--- a/build/autoconf/nspr.m4
+++ b/build/autoconf/nspr.m4
@@ -59,27 +59,35 @@ AC_ARG_WITH(nspr-exec-prefix,
 		NSPR_LIBS="${NO_NSPR_CONFIG_SYSTEM_LDFLAGS}"
 		NSPR_VERSION_STRING="$NO_NSPR_CONFIG_SYSTEM_VERSION"
 	else
 	    no_nspr="yes"
 	fi
 
 	if test -z "$no_nspr"; then
 		nspr_config_major_version=`echo $NSPR_VERSION_STRING | \
-			sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+			sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\1/'`
 		nspr_config_minor_version=`echo $NSPR_VERSION_STRING | \
-			sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+			sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\2/'`
 		nspr_config_micro_version=`echo $NSPR_VERSION_STRING | \
-			sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+			sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\4/'`
+		if test -z "$nspr_config_micro_version"; then
+			nspr_config_micro_version="0"
+		fi
+
 		min_nspr_major_version=`echo $min_nspr_version | \
-			sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+			sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\1/'`
 		min_nspr_minor_version=`echo $min_nspr_version | \
-			sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+			sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\2/'`
 		min_nspr_micro_version=`echo $min_nspr_version | \
-			sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+			sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\4/'`
+		if test -z "$min_nspr_micro_version"; then
+			min_nspr_micro_version="0"
+		fi
+
 		if test "$nspr_config_major_version" -ne "$min_nspr_major_version"; then
 			no_nspr="yes"
 		elif test "$nspr_config_major_version" -eq "$min_nspr_major_version" &&
 		     test "$nspr_config_minor_version" -lt "$min_nspr_minor_version"; then
 			no_nspr="yes"
 		elif test "$nspr_config_major_version" -eq "$min_nspr_major_version" &&
 		     test "$nspr_config_minor_version" -eq "$min_nspr_minor_version" &&
 		     test "$nspr_config_micro_version" -lt "$min_nspr_micro_version"; then
--- a/build/autoconf/nss.m4
+++ b/build/autoconf/nss.m4
@@ -40,27 +40,35 @@ AC_ARG_WITH(nss-exec-prefix,
 	no_nss=""
 	if test "$NSS_CONFIG" = "no"; then
 		no_nss="yes"
 	else
 		NSS_CFLAGS=`$NSS_CONFIG $nss_config_args --cflags`
 		NSS_LIBS=`$NSS_CONFIG $nss_config_args --libs`
 
 		nss_config_major_version=`$NSS_CONFIG $nss_config_args --version | \
-			sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+			sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\1/'`
 		nss_config_minor_version=`$NSS_CONFIG $nss_config_args --version | \
-			sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+			sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\2/'`
 		nss_config_micro_version=`$NSS_CONFIG $nss_config_args --version | \
-			sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+			sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\4/'`
+		if test -z "$nss_config_micro_version"; then
+			nss_config_micro_version="0"
+		fi
+
 		min_nss_major_version=`echo $min_nss_version | \
-			sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+			sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\1/'`
 		min_nss_minor_version=`echo $min_nss_version | \
-			sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+			sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\2/'`
 		min_nss_micro_version=`echo $min_nss_version | \
-			sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+			sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\4/'`
+		if test -z "$min_nss_micro_version"; then
+			min_nss_micro_version="0"
+		fi
+
 		if test "$nss_config_major_version" -lt "$min_nss_major_version"; then
 			no_nss="yes"
 		elif test "$nss_config_major_version" -eq "$min_nss_major_version" &&
 		     test "$nss_config_minor_version" -lt "$min_nss_minor_version"; then
 			no_nss="yes"
 		elif test "$nss_config_major_version" -eq "$min_nss_major_version" &&
 		     test "$nss_config_minor_version" -eq "$min_nss_minor_version" &&
 		     test "$nss_config_micro_version" -lt "$min_nss_micro_version"; then
--- a/docshell/test/browser/frame-head.js
+++ b/docshell/test/browser/frame-head.js
@@ -3,16 +3,18 @@
 
 // Functions that are automatically loaded as frame scripts for
 // timeline tests.
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise } = Cu.import('resource://gre/modules/Promise.jsm', {});
 
+Cu.import("resource://gre/modules/Timer.jsm");
+
 // Functions that look like mochitest functions but forward to the
 // browser process.
 
 this.ok = function(value, message) {
   sendAsyncMessage("browser:test:ok", {
     value: !!value,
     message: message});
 }
@@ -76,30 +78,32 @@ this.timelineContentTest = function(test
     info("Stop recording");
     docShell.recordProfileTimelineMarkers = false;
     finish();
   });
 }
 
 function timelineWaitForMarkers(docshell, searchFor) {
   if (typeof(searchFor) == "string") {
+    let searchForString = searchFor;
     let f = function (markers) {
-      return markers.some(m => m.name == searchFor);
+      return markers.some(m => m.name == searchForString);
     };
     searchFor = f;
   }
 
   return new Promise(function(resolve, reject) {
     let waitIterationCount = 0;
     let maxWaitIterationCount = 10; // Wait for 2sec maximum
     let markers = [];
 
-    let interval = content.setInterval(() => {
+    setTimeout(function timeoutHandler() {
       let newMarkers = docshell.popProfileTimelineMarkers();
       markers = [...markers, ...newMarkers];
       if (searchFor(markers) || waitIterationCount > maxWaitIterationCount) {
-        content.clearInterval(interval);
         resolve(markers);
+      } else {
+        setTimeout(timeoutHandler, 200);
+        waitIterationCount++;
       }
-      waitIterationCount++;
     }, 200);
   });
 }
--- a/docshell/test/browser/head.js
+++ b/docshell/test/browser/head.js
@@ -34,20 +34,29 @@ function makeTimelineTest(frameScriptNam
       finish();
       gBrowser.removeCurrentTab();
     });
   });
 }
 
 /* Open a URL for a timeline test.  */
 function timelineTestOpenUrl(url) {
-  return new Promise(function(resolve, reject) {
-    window.focus();
+  window.focus();
 
+  let tabSwitchPromise = new Promise((resolve, reject) => {
+    window.gBrowser.addEventListener("TabSwitchDone", function listener() {
+      window.gBrowser.removeEventListener("TabSwitchDone", listener);
+      resolve();
+    });
+  });
+
+  let loadPromise = new Promise(function(resolve, reject) {
     let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
     let linkedBrowser = tab.linkedBrowser;
 
     linkedBrowser.addEventListener("load", function onload() {
       linkedBrowser.removeEventListener("load", onload, true);
       resolve(tab);
     }, true);
   });
+
+  return Promise.all([tabSwitchPromise, loadPromise]).then(([_, tab]) => tab);
 }
--- a/dom/apps/OperatorApps.jsm
+++ b/dom/apps/OperatorApps.jsm
@@ -16,19 +16,19 @@ Cu.import("resource://gre/modules/Webapp
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 let Path = OS.Path;
 
 #ifdef MOZ_B2G_RIL
-XPCOMUtils.defineLazyServiceGetter(this, "iccProvider",
-                                   "@mozilla.org/ril/content-helper;1",
-                                   "nsIIccProvider");
+XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
+                                   "@mozilla.org/icc/iccservice;1",
+                                   "nsIIccService");
 #endif
 
 function debug(aMsg) {
   //dump("-*-*- OperatorApps.jsm : " + aMsg + "\n");
 }
 
 // Single Variant source dir will be set in PREF_SINGLE_VARIANT_DIR
 // preference.
@@ -64,27 +64,28 @@ let iccListener = {
   notifyStkCommand: function() {},
 
   notifyStkSessionEnd: function() {},
 
   notifyCardStateChanged: function() {},
 
   notifyIccInfoChanged: function() {
     // TODO: Bug 927709 - OperatorApps for multi-sim
-    // In Multi-sim, there is more than one client in iccProvider. Each
-    // client represents a icc service. To maintain the backward compatibility
+    // In Multi-sim, there is more than one client in IccService. Each
+    // client represents a icc handle. To maintain the backward compatibility
     // with single sim, we always use client 0 for now. Adding support for
     // multiple sim will be addressed in bug 927709, if needed.
     let clientId = 0;
-    let iccInfo = iccProvider.getIccInfo(clientId);
+    let icc = gIccService.getIccByServiceId(clientId);
+    let iccInfo = icc && icc.iccInfo;
     if (iccInfo && iccInfo.mcc && iccInfo.mnc) {
       let mcc = iccInfo.mcc;
       let mnc = iccInfo.mnc;
       debug("******* iccListener cardIccInfo MCC-MNC: " + mcc + "-" + mnc);
-      iccProvider.unregisterIccMsg(clientId, this);
+      icc.unregisterListener(this);
       OperatorAppsRegistry._installOperatorApps(mcc, mnc);
 
       debug("Broadcast message first-run-with-sim");
       let messenger = Cc["@mozilla.org/system-message-internal;1"]
                         .getService(Ci.nsISystemMessagesInternal);
       messenger.broadcastMessage("first-run-with-sim", { mcc: mcc,
                                                          mnc: mnc });
     }
@@ -100,40 +101,41 @@ this.OperatorAppsRegistry = {
     debug("init");
 #ifdef MOZ_B2G_RIL
     if (isFirstRunWithSIM()) {
       debug("First Run with SIM");
       Task.spawn(function() {
         try {
           yield this._initializeSourceDir();
           // TODO: Bug 927709 - OperatorApps for multi-sim
-          // In Multi-sim, there is more than one client in iccProvider. Each
-          // client represents a icc service. To maintain the backward
+          // In Multi-sim, there is more than one client in IccService. Each
+          // client represents a icc handle. To maintain the backward
           // compatibility with single sim, we always use client 0 for now.
           // Adding support for multiple sim will be addressed in bug 927709, if
           // needed.
           let clientId = 0;
-          let iccInfo = iccProvider.getIccInfo(clientId);
+          let icc = gIccService.getIccByServiceId(clientId);
+          let iccInfo = icc && icc.iccInfo;
           let mcc = 0;
           let mnc = 0;
           if (iccInfo && iccInfo.mcc) {
             mcc = iccInfo.mcc;
           }
           if (iccInfo && iccInfo.mnc) {
             mnc = iccInfo.mnc;
           }
           if (mcc && mnc) {
             this._installOperatorApps(mcc, mnc);
             let messenger = Cc["@mozilla.org/system-message-internal;1"]
                               .getService(Ci.nsISystemMessagesInternal);
             messenger.broadcastMessage("first-run-with-sim", { mcc: mcc,
                                                                mnc: mnc });
 
           } else {
-            iccProvider.registerIccMsg(clientId, iccListener);
+            icc.registerListener(iccListener);
           }
         } catch (e) {
           debug("Error Initializing OperatorApps. " + e);
         }
       }.bind(this));
     } else {
       debug("No First Run with SIM");
     }
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -31,16 +31,17 @@
 #include "nsUnicharUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "BatteryManager.h"
 #include "mozilla/dom/PowerManager.h"
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/CellBroadcast.h"
+#include "mozilla/dom/IccManager.h"
 #include "mozilla/dom/MobileMessageManager.h"
 #include "mozilla/dom/ServiceWorkerContainer.h"
 #include "mozilla/dom/Telephony.h"
 #include "mozilla/dom/Voicemail.h"
 #include "mozilla/dom/TVManager.h"
 #include "mozilla/dom/VRDevice.h"
 #include "mozilla/Hal.h"
 #include "nsISiteSpecificUserAgent.h"
@@ -48,17 +49,16 @@
 #include "mozilla/StaticPtr.h"
 #include "Connection.h"
 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
 #include "nsGlobalWindow.h"
 #ifdef MOZ_B2G
 #include "nsIMobileIdentityService.h"
 #endif
 #ifdef MOZ_B2G_RIL
-#include "mozilla/dom/IccManager.h"
 #include "mozilla/dom/MobileConnectionArray.h"
 #endif
 #include "nsIIdleObserver.h"
 #include "nsIPermissionManager.h"
 #include "nsMimeTypes.h"
 #include "nsNetUtil.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
@@ -172,24 +172,24 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlugins)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMimeTypes)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGeolocation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotification)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPowerManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCellBroadcast)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIccManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMobileMessageManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTelephony)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVoicemail)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTVManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
 #ifdef MOZ_B2G_RIL
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMobileConnections)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIccManager)
 #endif
 #ifdef MOZ_B2G_BT
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBluetooth)
 #endif
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelManager)
 #endif
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCameraManager)
@@ -245,16 +245,21 @@ Navigator::Invalidate()
     mPowerManager->Shutdown();
     mPowerManager = nullptr;
   }
 
   if (mCellBroadcast) {
     mCellBroadcast = nullptr;
   }
 
+  if (mIccManager) {
+    mIccManager->Shutdown();
+    mIccManager = nullptr;
+  }
+
   if (mMobileMessageManager) {
     mMobileMessageManager->Shutdown();
     mMobileMessageManager = nullptr;
   }
 
   if (mTelephony) {
     mTelephony = nullptr;
   }
@@ -272,21 +277,16 @@ Navigator::Invalidate()
     mConnection->Shutdown();
     mConnection = nullptr;
   }
 
 #ifdef MOZ_B2G_RIL
   if (mMobileConnections) {
     mMobileConnections = nullptr;
   }
-
-  if (mIccManager) {
-    mIccManager->Shutdown();
-    mIccManager = nullptr;
-  }
 #endif
 
 #ifdef MOZ_B2G_BT
   if (mBluetooth) {
     mBluetooth = nullptr;
   }
 #endif
 
@@ -1702,34 +1702,31 @@ Navigator::GetMozVoicemail(ErrorResult& 
     }
 
     mVoicemail = Voicemail::Create(mWindow, aRv);
   }
 
   return mVoicemail;
 }
 
-#ifdef MOZ_B2G_RIL
-
 IccManager*
 Navigator::GetMozIccManager(ErrorResult& aRv)
 {
   if (!mIccManager) {
     if (!mWindow) {
       aRv.Throw(NS_ERROR_UNEXPECTED);
       return nullptr;
     }
     NS_ENSURE_TRUE(mWindow->GetDocShell(), nullptr);
 
     mIccManager = new IccManager(mWindow);
   }
 
   return mIccManager;
 }
-#endif // MOZ_B2G_RIL
 
 #ifdef MOZ_GAMEPAD
 void
 Navigator::GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads,
                        ErrorResult& aRv)
 {
   if (!mWindow) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -39,20 +39,16 @@ class MediaDevices;
 struct MediaStreamConstraints;
 class WakeLock;
 class ArrayBufferViewOrBlobOrStringOrFormData;
 struct MobileIdOptions;
 class ServiceWorkerContainer;
 }
 }
 
-#ifdef MOZ_B2G_RIL
-class nsIDOMMozIccManager;
-#endif // MOZ_B2G_RIL
-
 //*****************************************************************************
 // Navigator: Script "navigator" object
 //*****************************************************************************
 
 namespace mozilla {
 namespace dom {
 
 namespace battery {
@@ -83,22 +79,22 @@ class Connection;
 
 #ifdef MOZ_B2G_BT
 namespace bluetooth {
 class BluetoothManager;
 } // namespace bluetooth
 #endif // MOZ_B2G_BT
 
 #ifdef MOZ_B2G_RIL
-class IccManager;
 class MobileConnectionArray;
 #endif
 
 class PowerManager;
 class CellBroadcast;
+class IccManager;
 class Telephony;
 class Voicemail;
 class TVManager;
 
 namespace time {
 class TimeManager;
 } // namespace time
 
@@ -219,16 +215,17 @@ public:
                                              ErrorResult& aRv);
   nsDOMDeviceStorage* GetDeviceStorage(const nsAString& aType,
                                        ErrorResult& aRv);
   void GetDeviceStorages(const nsAString& aType,
                          nsTArray<nsRefPtr<nsDOMDeviceStorage> >& aStores,
                          ErrorResult& aRv);
   DesktopNotificationCenter* GetMozNotification(ErrorResult& aRv);
   CellBroadcast* GetMozCellBroadcast(ErrorResult& aRv);
+  IccManager* GetMozIccManager(ErrorResult& aRv);
   MobileMessageManager* GetMozMobileMessage();
   Telephony* GetMozTelephony(ErrorResult& aRv);
   Voicemail* GetMozVoicemail(ErrorResult& aRv);
   TVManager* GetTv();
   network::Connection* GetConnection(ErrorResult& aRv);
   nsDOMCameraManager* GetMozCameras(ErrorResult& aRv);
   MediaDevices* GetMediaDevices(ErrorResult& aRv);
   void MozSetMessageHandler(const nsAString& aType,
@@ -238,17 +235,16 @@ public:
   void MozSetMessageHandlerPromise(Promise& aPromise, ErrorResult& aRv);
 
 #ifdef MOZ_B2G
   already_AddRefed<Promise> GetMobileIdAssertion(const MobileIdOptions& options,
                                                  ErrorResult& aRv);
 #endif
 #ifdef MOZ_B2G_RIL
   MobileConnectionArray* GetMozMobileConnections(ErrorResult& aRv);
-  IccManager* GetMozIccManager(ErrorResult& aRv);
 #endif // MOZ_B2G_RIL
 #ifdef MOZ_GAMEPAD
   void GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
 #endif // MOZ_GAMEPAD
   already_AddRefed<Promise> GetVRDevices(ErrorResult& aRv);
 #ifdef MOZ_B2G_FM
   FMRadio* GetMozFMRadio(ErrorResult& aRv);
 #endif
@@ -348,24 +344,24 @@ private:
   nsRefPtr<Geolocation> mGeolocation;
   nsRefPtr<DesktopNotificationCenter> mNotification;
   nsRefPtr<battery::BatteryManager> mBatteryManager;
 #ifdef MOZ_B2G_FM
   nsRefPtr<FMRadio> mFMRadio;
 #endif
   nsRefPtr<PowerManager> mPowerManager;
   nsRefPtr<CellBroadcast> mCellBroadcast;
+  nsRefPtr<IccManager> mIccManager;
   nsRefPtr<MobileMessageManager> mMobileMessageManager;
   nsRefPtr<Telephony> mTelephony;
   nsRefPtr<Voicemail> mVoicemail;
   nsRefPtr<TVManager> mTVManager;
   nsRefPtr<network::Connection> mConnection;
 #ifdef MOZ_B2G_RIL
   nsRefPtr<MobileConnectionArray> mMobileConnections;
-  nsRefPtr<IccManager> mIccManager;
 #endif
 #ifdef MOZ_B2G_BT
   nsRefPtr<bluetooth::BluetoothManager> mBluetooth;
 #endif
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   nsRefPtr<system::AudioChannelManager> mAudioChannelManager;
 #endif
   nsRefPtr<nsDOMCameraManager> mCameraManager;
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2774,32 +2774,40 @@ nsFrameLoader::RequestNotifyAfterRemoteP
 
 NS_IMETHODIMP
 nsFrameLoader::RequestNotifyLayerTreeReady()
 {
   if (mRemoteBrowser) {
     return mRemoteBrowser->RequestNotifyLayerTreeReady() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
   }
 
+  if (!mOwnerContent) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   nsRefPtr<AsyncEventDispatcher> event =
     new AsyncEventDispatcher(mOwnerContent,
                              NS_LITERAL_STRING("MozLayerTreeReady"),
                              true, false);
   event->PostDOMEvent();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameLoader::RequestNotifyLayerTreeCleared()
 {
   if (mRemoteBrowser) {
     return mRemoteBrowser->RequestNotifyLayerTreeCleared() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
   }
 
+  if (!mOwnerContent) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   nsRefPtr<AsyncEventDispatcher> event =
     new AsyncEventDispatcher(mOwnerContent,
                              NS_LITERAL_STRING("MozLayerTreeCleared"),
                              true, false);
   event->PostDOMEvent();
 
   return NS_OK;
 }
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -1234,16 +1234,17 @@ GK_ATOM(videocontrols, "videocontrols")
 GK_ATOM(viewport, "viewport")
 GK_ATOM(viewport_height, "viewport-height")
 GK_ATOM(viewport_initial_scale, "viewport-initial-scale")
 GK_ATOM(viewport_maximum_scale, "viewport-maximum-scale")
 GK_ATOM(viewport_minimum_scale, "viewport-minimum-scale")
 GK_ATOM(viewport_user_scalable, "viewport-user-scalable")
 GK_ATOM(viewport_width, "viewport-width")
 GK_ATOM(visibility, "visibility")
+GK_ATOM(visuallyselected, "visuallyselected")
 GK_ATOM(vlink, "vlink")
 GK_ATOM(vspace, "vspace")
 GK_ATOM(wbr, "wbr")
 GK_ATOM(when, "when")
 GK_ATOM(where, "where")
 GK_ATOM(widget, "widget")
 GK_ATOM(width, "width")
 GK_ATOM(window, "window")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -638,19 +638,18 @@ public:
                             const override;
   virtual bool has(JSContext *cx, JS::Handle<JSObject*> proxy,
                    JS::Handle<jsid> id, bool *bp) const override;
   virtual bool get(JSContext *cx, JS::Handle<JSObject*> proxy,
                    JS::Handle<JSObject*> receiver,
                    JS::Handle<jsid> id,
                    JS::MutableHandle<JS::Value> vp) const override;
   virtual bool set(JSContext *cx, JS::Handle<JSObject*> proxy,
-                   JS::Handle<JSObject*> receiver,
-                   JS::Handle<jsid> id,
-                   JS::MutableHandle<JS::Value> vp,
+                   JS::Handle<jsid> id, JS::Handle<JS::Value> v,
+                   JS::Handle<JS::Value> receiver,
                    JS::ObjectOpResult &result) const override;
 
   // SpiderMonkey extensions
   virtual bool getPropertyDescriptor(JSContext* cx,
                                      JS::Handle<JSObject*> proxy,
                                      JS::Handle<jsid> id,
                                      JS::MutableHandle<JSPropertyDescriptor> desc)
                                      const override;
@@ -904,29 +903,29 @@ nsOuterWindowProxy::get(JSContext *cx, J
   }
   // Else fall through to js::Wrapper
 
   return js::Wrapper::get(cx, proxy, receiver, id, vp);
 }
 
 bool
 nsOuterWindowProxy::set(JSContext *cx, JS::Handle<JSObject*> proxy,
-                        JS::Handle<JSObject*> receiver,
                         JS::Handle<jsid> id,
-                        JS::MutableHandle<JS::Value> vp,
+                        JS::Handle<JS::Value> v,
+                        JS::Handle<JS::Value> receiver,
                         JS::ObjectOpResult &result) const
 {
   int32_t index = GetArrayIndexFromId(cx, id);
   if (IsArrayIndex(index)) {
     // Reject the set.  It's up to the caller to decide whether to throw a
     // TypeError.  If the caller is strict mode JS code, it'll throw.
     return result.failReadOnly();
   }
 
-  return js::Wrapper::set(cx, proxy, receiver, id, vp, result);
+  return js::Wrapper::set(cx, proxy, id, v, receiver, result);
 }
 
 bool
 nsOuterWindowProxy::getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> proxy,
                                                  JS::AutoIdVector &props) const
 {
   // BaseProxyHandler::keys seems to do what we want here: call
   // ownPropertyKeys and then filter out the non-enumerable properties.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -9802,17 +9802,17 @@ class CGProxySpecialOperation(CGPerSigna
     foundVar: For getters and deleters, the generated code can also set a bool
     variable, declared by the caller, if the given indexed or named property
     already existed. If the caller wants this, it should pass the name of the
     bool variable as the foundVar keyword argument to the constructor. The
     caller is responsible for declaring the variable and initializing it to
     false.
     """
     def __init__(self, descriptor, operation, checkFound=True,
-                 argumentMutableValue=None, resultVar=None, foundVar=None):
+                 argumentHandleValue=None, resultVar=None, foundVar=None):
         self.checkFound = checkFound
         self.foundVar = foundVar or "found"
 
         nativeName = MakeNativeName(descriptor.binaryNameFor(operation))
         operation = descriptor.operations[operation]
         assert len(operation.signatures()) == 1
         signature = operation.signatures()[0]
 
@@ -9827,22 +9827,22 @@ class CGProxySpecialOperation(CGPerSigna
         if operation.isSetter() or operation.isCreator():
             # arguments[0] is the index or name of the item that we're setting.
             argument = arguments[1]
             info = getJSToNativeConversionInfo(
                 argument.type, descriptor,
                 treatNullAs=argument.treatNullAs,
                 sourceDescription=("value being assigned to %s setter" %
                                    descriptor.interface.identifier.name))
-            if argumentMutableValue is None:
-                argumentMutableValue = "desc.value()"
+            if argumentHandleValue is None:
+                argumentHandleValue = "desc.value()"
             templateValues = {
                 "declName": argument.identifier.name,
                 "holderName": argument.identifier.name + "_holder",
-                "val": argumentMutableValue,
+                "val": argumentHandleValue,
                 "obj": "obj",
                 "passedToJSImpl": "false"
             }
             self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
         elif operation.isGetter() or operation.isDeleter():
             if foundVar is None:
                 self.cgRoot.prepend(CGGeneric("bool found = false;\n"))
 
@@ -9877,20 +9877,20 @@ class CGProxyIndexedOperation(CGProxySpe
     If checkFound is False, will just assert that the prop is found instead of
     checking that it is before wrapping the value.
 
     resultVar: See the docstring for CGCallGenerator.
 
     foundVar: See the docstring for CGProxySpecialOperation.
     """
     def __init__(self, descriptor, name, doUnwrap=True, checkFound=True,
-                 argumentMutableValue=None, resultVar=None, foundVar=None):
+                 argumentHandleValue=None, resultVar=None, foundVar=None):
         self.doUnwrap = doUnwrap
         CGProxySpecialOperation.__init__(self, descriptor, name, checkFound,
-                                         argumentMutableValue=argumentMutableValue,
+                                         argumentHandleValue=argumentHandleValue,
                                          resultVar=resultVar,
                                          foundVar=foundVar)
 
     def define(self):
         # Our first argument is the id we're getting.
         argName = self.arguments[0].identifier.name
         if argName == "index":
             # We already have our index in a variable with that name
@@ -9938,19 +9938,19 @@ class CGProxyIndexedPresenceChecker(CGPr
         CGProxyIndexedGetter.__init__(self, descriptor, foundVar=foundVar)
         self.cgRoot.append(CGGeneric("(void)result;\n"))
 
 
 class CGProxyIndexedSetter(CGProxyIndexedOperation):
     """
     Class to generate a call to an indexed setter.
     """
-    def __init__(self, descriptor, argumentMutableValue=None):
+    def __init__(self, descriptor, argumentHandleValue=None):
         CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedSetter',
-                                         argumentMutableValue=argumentMutableValue)
+                                         argumentHandleValue=argumentHandleValue)
 
 
 class CGProxyIndexedDeleter(CGProxyIndexedOperation):
     """
     Class to generate a call to an indexed deleter.
 
     resultVar: See the docstring for CGCallGenerator.
 
@@ -9968,20 +9968,20 @@ class CGProxyNamedOperation(CGProxySpeci
 
     'value' is the jsval to use for the name; None indicates that it should be
     gotten from the property id.
 
     resultVar: See the docstring for CGCallGenerator.
 
     foundVar: See the docstring for CGProxySpecialOperation.
     """
-    def __init__(self, descriptor, name, value=None, argumentMutableValue=None,
+    def __init__(self, descriptor, name, value=None, argumentHandleValue=None,
                  resultVar=None, foundVar=None):
         CGProxySpecialOperation.__init__(self, descriptor, name,
-                                         argumentMutableValue=argumentMutableValue,
+                                         argumentHandleValue=argumentHandleValue,
                                          resultVar=resultVar,
                                          foundVar=foundVar)
         self.value = value
 
     def define(self):
         # Our first argument is the id we're getting.
         argName = self.arguments[0].identifier.name
         if argName == "id":
@@ -10069,19 +10069,19 @@ class CGProxyNamedPresenceChecker(CGProx
         CGProxyNamedGetter.__init__(self, descriptor, foundVar=foundVar)
         self.cgRoot.append(CGGeneric("(void)result;\n"))
 
 
 class CGProxyNamedSetter(CGProxyNamedOperation):
     """
     Class to generate a call to a named setter.
     """
-    def __init__(self, descriptor, argumentMutableValue=None):
+    def __init__(self, descriptor, argumentHandleValue=None):
         CGProxyNamedOperation.__init__(self, descriptor, 'NamedSetter',
-                                       argumentMutableValue=argumentMutableValue)
+                                       argumentHandleValue=argumentHandleValue)
 
 
 class CGProxyNamedDeleter(CGProxyNamedOperation):
     """
     Class to generate a call to a named deleter.
 
     resultVar: See the docstring for CGCallGenerator.
 
@@ -10688,17 +10688,17 @@ class CGDOMJSProxyHandler_get(ClassMetho
             named=getNamed)
 
 
 class CGDOMJSProxyHandler_setCustom(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'proxy'),
                 Argument('JS::Handle<jsid>', 'id'),
-                Argument('JS::MutableHandle<JS::Value>', 'vp'),
+                Argument('JS::Handle<JS::Value>', 'v'),
                 Argument('bool*', 'done')]
         ClassMethod.__init__(self, "setCustom", "bool", args, virtual=True, override=True, const=True)
         self.descriptor = descriptor
 
     def getBody(self):
         assertion = ("MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n"
                      '           "Should not have a XrayWrapper here");\n')
 
@@ -10713,17 +10713,17 @@ class CGDOMJSProxyHandler_setCustom(Clas
                                  "Can't cope with [OverrideBuiltins] and an indexed getter")
             if self.descriptor.operations['NamedCreator'] is not namedSetter:
                 raise ValueError("In interface " + self.descriptor.name + ": " +
                                  "Can't cope with named setter that is not also a named creator")
             if self.descriptor.hasUnforgeableMembers:
                 raise ValueError("In interface " + self.descriptor.name + ": " +
                                  "Can't cope with [OverrideBuiltins] and unforgeable members")
 
-            callSetter = CGProxyNamedSetter(self.descriptor, argumentMutableValue="vp")
+            callSetter = CGProxyNamedSetter(self.descriptor, argumentHandleValue="v")
             return (assertion +
                     callSetter.define() +
                     "*done = true;\n"
                     "return true;\n")
 
         # As an optimization, if we are going to call an IndexedSetter, go
         # ahead and call it and have done.
         indexedSetter = self.descriptor.operations['IndexedSetter']
@@ -10738,17 +10738,17 @@ class CGDOMJSProxyHandler_setCustom(Clas
                 if (IsArrayIndex(index)) {
                   $*{callSetter}
                   *done = true;
                   return true;
                 }
 
                 """,
                 callSetter=CGProxyIndexedSetter(self.descriptor,
-                                                argumentMutableValue="vp").define())
+                                                argumentHandleValue="v").define())
         else:
             setIndexed = ""
 
         return (assertion +
                 setIndexed +
                 "*done = false;\n"
                 "return true;\n")
 
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -214,23 +214,24 @@ DOMProxyHandler::defineProperty(JSContex
   if (!js::DefineOwnProperty(cx, expando, id, desc, result)) {
     return false;
   }
   *defined = true;
   return true;
 }
 
 bool
-DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<JSObject*> receiver,
-                     Handle<jsid> id, MutableHandle<JS::Value> vp, ObjectOpResult &result) const
+DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<jsid> id,
+                     Handle<JS::Value> v, Handle<JS::Value> receiver,
+                     ObjectOpResult &result) const
 {
   MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
              "Should not have a XrayWrapper here");
   bool done;
-  if (!setCustom(cx, proxy, id, vp, &done)) {
+  if (!setCustom(cx, proxy, id, v, &done)) {
     return false;
   }
   if (done) {
     return result.succeed();
   }
 
   // Make sure to ignore our named properties when checking for own
   // property descriptors for a set.
@@ -247,17 +248,17 @@ DOMProxyHandler::set(JSContext *cx, Hand
     if (!js::GetObjectProto(cx, proxy, &proto)) {
       return false;
     }
     if (proto && !JS_GetPropertyDescriptorById(cx, proto, id, &desc)) {
       return false;
     }
   }
 
-  return js::SetPropertyIgnoringNamedGetter(cx, proxy, id, vp, receiver, desc, result);
+  return js::SetPropertyIgnoringNamedGetter(cx, proxy, id, v, receiver, desc, result);
 }
 
 bool
 DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
                          JS::Handle<jsid> id, JS::ObjectOpResult &result) const
 {
   JS::Rooted<JSObject*> expando(cx);
   if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
@@ -345,17 +346,17 @@ IdToInt32(JSContext* cx, JS::Handle<jsid
     return -1;
   }
 
   return i;
 }
 
 bool
 DOMProxyHandler::setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
-                           JS::MutableHandle<JS::Value> vp, bool *done) const
+                           JS::Handle<JS::Value> v, bool *done) const
 {
   *done = false;
   return true;
 }
 
 //static
 JSObject *
 DOMProxyHandler::GetExpandoObject(JSObject *obj)
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -117,27 +117,27 @@ public:
   bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                JS::ObjectOpResult &result) const override;
   bool preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy,
                          JS::ObjectOpResult& result) const override;
   bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
                     const override;
   bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
            bool* bp) const override;
-  bool set(JSContext *cx, JS::Handle<JSObject*> proxy, JS::Handle<JSObject*> receiver,
-           JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp, JS::ObjectOpResult &result)
+  bool set(JSContext *cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+           JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver, JS::ObjectOpResult &result)
            const override;
 
   /*
    * If assigning to proxy[id] hits a named setter with OverrideBuiltins or
    * an indexed setter, call it and set *done to true on success. Otherwise, set
    * *done to false.
    */
   virtual bool setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
-                         JS::MutableHandle<JS::Value> vp, bool *done) const;
+                         JS::Handle<JS::Value> v, bool *done) const;
 
   static JSObject* GetExpandoObject(JSObject* obj);
 
   /* GetAndClearExpandoObject does not DROP or clear the preserving wrapper flag. */
   static JSObject* GetAndClearExpandoObject(JSObject* obj);
   static JSObject* EnsureExpandoObject(JSContext* cx,
                                        JS::Handle<JSObject*> obj);
 
--- a/dom/bluetooth/BluetoothRilListener.cpp
+++ b/dom/bluetooth/BluetoothRilListener.cpp
@@ -2,22 +2,21 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BluetoothRilListener.h"
 
 #include "BluetoothHfpManager.h"
-#include "nsIIccProvider.h"
+#include "nsIIccService.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsITelephonyCallInfo.h"
 #include "nsITelephonyService.h"
-#include "nsRadioInterfaceLayer.h" // For NS_RILCONTENTHELPER_CONTRACTID.
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 
 USING_BLUETOOTH_NAMESPACE
 
 /**
  *  IccListener
  */
@@ -55,25 +54,29 @@ IccListener::NotifyCardStateChanged()
   return NS_OK;
 }
 
 bool
 IccListener::Listen(bool aStart)
 {
   NS_ENSURE_TRUE(mOwner, false);
 
-  nsCOMPtr<nsIIccProvider> provider =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
-  NS_ENSURE_TRUE(provider, false);
+  nsCOMPtr<nsIIccService> service =
+    do_GetService(ICC_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(service, false);
+
+  nsCOMPtr<nsIIcc> icc;
+  service->GetIccByServiceId(mOwner->mClientId, getter_AddRefs(icc));
+  NS_ENSURE_TRUE(icc, false);
 
   nsresult rv;
   if (aStart) {
-    rv = provider->RegisterIccMsg(mOwner->mClientId, this);
+    rv = icc->RegisterListener(this);
   } else {
-    rv = provider->UnregisterIccMsg(mOwner->mClientId, this);
+    rv = icc->UnregisterListener(this);
   }
 
   return NS_SUCCEEDED(rv);
 }
 
 void
 IccListener::SetOwner(BluetoothRilListener *aOwner)
 {
--- a/dom/bluetooth/BluetoothRilListener.h
+++ b/dom/bluetooth/BluetoothRilListener.h
@@ -6,17 +6,17 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothrillistener_h__
 #define mozilla_dom_bluetooth_bluetoothrillistener_h__
 
 #include "BluetoothCommon.h"
 
 #include "nsAutoPtr.h"
 
-#include "nsIIccProvider.h"
+#include "nsIIccService.h"
 #include "nsIMobileConnectionService.h"
 #include "nsITelephonyCallInfo.h"
 #include "nsITelephonyService.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothRilListener;
 
--- a/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
@@ -21,16 +21,18 @@
 
 using namespace mozilla;
 USING_BLUETOOTH_NAMESPACE
 // AVRC_ID op code follows bluedroid avrc_defs.h
 #define AVRC_ID_REWIND  0x48
 #define AVRC_ID_FAST_FOR 0x49
 #define AVRC_KEY_PRESS_STATE  1
 #define AVRC_KEY_RELEASE_STATE  0
+// bluedroid bt_rc.h
+#define AVRC_MAX_ATTR_STR_LEN 255
 
 namespace {
   StaticRefPtr<BluetoothA2dpManager> sBluetoothA2dpManager;
   bool sInShutdown = false;
   static BluetoothA2dpInterface* sBtA2dpInterface;
   static BluetoothAvrcpInterface* sBtAvrcpInterface;
 } // anonymous namespace
 
@@ -42,22 +44,37 @@ ConvertAttributeString(BluetoothAvrcpMed
                        nsAString& aAttrStr)
 {
   BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
   NS_ENSURE_TRUE_VOID(a2dp);
 
   switch (aAttrId) {
     case AVRCP_MEDIA_ATTRIBUTE_TITLE:
       a2dp->GetTitle(aAttrStr);
+      /*
+       * bluedroid can only send string length AVRC_MAX_ATTR_STR_LEN - 1
+       */
+      if (aAttrStr.Length() >= AVRC_MAX_ATTR_STR_LEN) {
+        aAttrStr.Truncate(AVRC_MAX_ATTR_STR_LEN - 1);
+        BT_WARNING("Truncate media item attribute title, length is over 255");
+      }
       break;
     case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
       a2dp->GetArtist(aAttrStr);
+      if (aAttrStr.Length() >= AVRC_MAX_ATTR_STR_LEN) {
+        aAttrStr.Truncate(AVRC_MAX_ATTR_STR_LEN - 1);
+        BT_WARNING("Truncate media item attribute artist, length is over 255");
+      }
       break;
     case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
       a2dp->GetAlbum(aAttrStr);
+      if (aAttrStr.Length() >= AVRC_MAX_ATTR_STR_LEN) {
+        aAttrStr.Truncate(AVRC_MAX_ATTR_STR_LEN - 1);
+        BT_WARNING("Truncate media item attribute album, length is over 255");
+      }
       break;
     case AVRCP_MEDIA_ATTRIBUTE_TRACK_NUM:
       aAttrStr.AppendInt(a2dp->GetMediaNumber());
       break;
     case AVRCP_MEDIA_ATTRIBUTE_NUM_TRACKS:
       aAttrStr.AppendInt(a2dp->GetTotalMediaNumber());
       break;
     case AVRCP_MEDIA_ATTRIBUTE_GENRE:
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -12,24 +12,23 @@
 
 #include "jsapi.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsContentUtils.h"
 #include "nsIAudioManager.h"
 #include "nsIIccInfo.h"
-#include "nsIIccProvider.h"
+#include "nsIIccService.h"
 #include "nsIMobileConnectionInfo.h"
 #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"
 
@@ -721,22 +720,26 @@ BluetoothHfpManager::HandleVoiceConnecti
     BT_WARNING("The operator name was longer than 16 characters. We cut it.");
     mOperatorName.Left(mOperatorName, 16);
   }
 }
 
 void
 BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
 {
-  nsCOMPtr<nsIIccProvider> icc =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+  nsCOMPtr<nsIIccService> service =
+    do_GetService(ICC_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE_VOID(service);
+
+  nsCOMPtr<nsIIcc> icc;
+  service->GetIccByServiceId(aClientId, getter_AddRefs(icc));
   NS_ENSURE_TRUE_VOID(icc);
 
   nsCOMPtr<nsIIccInfo> iccInfo;
-  icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
+  icc->GetIccInfo(getter_AddRefs(iccInfo));
   NS_ENSURE_TRUE_VOID(iccInfo);
 
   nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
   NS_ENSURE_TRUE_VOID(gsmIccInfo);
   gsmIccInfo->GetMsisdn(mMsisdn);
 }
 
 void
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -23,22 +23,21 @@
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #ifdef MOZ_B2G_RIL
 #include "nsIIccInfo.h"
-#include "nsIIccProvider.h"
+#include "nsIIccService.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsITelephonyService.h"
-#include "nsRadioInterfaceLayer.h"
 #endif
 
 /**
  * BRSF bitmask of AG supported features. See 4.34.1 "Bluetooth Defined AT
  * Capabilities" in Bluetooth hands-free profile 1.6
  */
 #define BRSF_BIT_THREE_WAY_CALLING         1
 #define BSRF_BIT_EC_NR_FUNCTION            (1 << 1)
@@ -659,22 +658,26 @@ BluetoothHfpManager::HandleVoiceConnecti
     BT_WARNING("The operator name was longer than 16 characters. We cut it.");
     mOperatorName.Left(mOperatorName, 16);
   }
 }
 
 void
 BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
 {
-  nsCOMPtr<nsIIccProvider> icc =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+  nsCOMPtr<nsIIccService> service =
+    do_GetService(ICC_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE_VOID(service);
+
+  nsCOMPtr<nsIIcc> icc;
+  service->GetIccByServiceId(aClientId, getter_AddRefs(icc));
   NS_ENSURE_TRUE_VOID(icc);
 
   nsCOMPtr<nsIIccInfo> iccInfo;
-  icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
+  icc->GetIccInfo(getter_AddRefs(iccInfo));
   NS_ENSURE_TRUE_VOID(iccInfo);
 
   nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
   NS_ENSURE_TRUE_VOID(gsmIccInfo);
   gsmIccInfo->GetMsisdn(mMsisdn);
 }
 #endif // MOZ_B2G_RIL
 
--- a/dom/bluetooth2/BluetoothRilListener.cpp
+++ b/dom/bluetooth2/BluetoothRilListener.cpp
@@ -2,21 +2,20 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BluetoothRilListener.h"
 
 #include "BluetoothHfpManager.h"
-#include "nsIIccProvider.h"
+#include "nsIIccService.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsITelephonyService.h"
-#include "nsRadioInterfaceLayer.h" // For NS_RILCONTENTHELPER_CONTRACTID.
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 
 USING_BLUETOOTH_NAMESPACE
 
 /**
  *  IccListener
  */
@@ -54,25 +53,29 @@ IccListener::NotifyCardStateChanged()
   return NS_OK;
 }
 
 bool
 IccListener::Listen(bool aStart)
 {
   NS_ENSURE_TRUE(mOwner, false);
 
-  nsCOMPtr<nsIIccProvider> provider =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
-  NS_ENSURE_TRUE(provider, false);
+  nsCOMPtr<nsIIccService> service =
+    do_GetService(ICC_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(service, false);
+
+  nsCOMPtr<nsIIcc> icc;
+  service->GetIccByServiceId(mOwner->mClientId, getter_AddRefs(icc));
+  NS_ENSURE_TRUE(icc, false);
 
   nsresult rv;
   if (aStart) {
-    rv = provider->RegisterIccMsg(mOwner->mClientId, this);
+    rv = icc->RegisterListener(this);
   } else {
-    rv = provider->UnregisterIccMsg(mOwner->mClientId, this);
+    rv = icc->UnregisterListener(this);
   }
 
   return NS_SUCCEEDED(rv);
 }
 
 void
 IccListener::SetOwner(BluetoothRilListener *aOwner)
 {
--- a/dom/bluetooth2/BluetoothRilListener.h
+++ b/dom/bluetooth2/BluetoothRilListener.h
@@ -6,17 +6,17 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothrillistener_h__
 #define mozilla_dom_bluetooth_bluetoothrillistener_h__
 
 #include "BluetoothCommon.h"
 
 #include "nsAutoPtr.h"
 
-#include "nsIIccProvider.h"
+#include "nsIIccService.h"
 #include "nsIMobileConnectionService.h"
 #include "nsITelephonyCallInfo.h"
 #include "nsITelephonyService.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothRilListener;
 
--- a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -12,24 +12,23 @@
 
 #include "jsapi.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsContentUtils.h"
 #include "nsIAudioManager.h"
 #include "nsIIccInfo.h"
-#include "nsIIccProvider.h"
+#include "nsIIccService.h"
 #include "nsIMobileConnectionInfo.h"
 #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"
 
@@ -705,22 +704,26 @@ BluetoothHfpManager::HandleVoiceConnecti
     BT_WARNING("The operator name was longer than 16 characters. We cut it.");
     mOperatorName.Left(mOperatorName, 16);
   }
 }
 
 void
 BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
 {
-  nsCOMPtr<nsIIccProvider> icc =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+  nsCOMPtr<nsIIccService> service =
+    do_GetService(ICC_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE_VOID(service);
+
+  nsCOMPtr<nsIIcc> icc;
+  service->GetIccByServiceId(aClientId, getter_AddRefs(icc));
   NS_ENSURE_TRUE_VOID(icc);
 
   nsCOMPtr<nsIIccInfo> iccInfo;
-  icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
+  icc->GetIccInfo(getter_AddRefs(iccInfo));
   NS_ENSURE_TRUE_VOID(iccInfo);
 
   nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
   NS_ENSURE_TRUE_VOID(gsmIccInfo);
   gsmIccInfo->GetMsisdn(mMsisdn);
 }
 
 void
--- a/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
@@ -23,22 +23,21 @@
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #ifdef MOZ_B2G_RIL
 #include "nsIIccInfo.h"
-#include "nsIIccProvider.h"
+#include "nsIIccService.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsITelephonyService.h"
-#include "nsRadioInterfaceLayer.h"
 #endif
 
 /**
  * BRSF bitmask of AG supported features. See 4.34.1 "Bluetooth Defined AT
  * Capabilities" in Bluetooth hands-free profile 1.6
  */
 #define BRSF_BIT_THREE_WAY_CALLING         1
 #define BSRF_BIT_EC_NR_FUNCTION            (1 << 1)
@@ -659,22 +658,26 @@ BluetoothHfpManager::HandleVoiceConnecti
     BT_WARNING("The operator name was longer than 16 characters. We cut it.");
     mOperatorName.Left(mOperatorName, 16);
   }
 }
 
 void
 BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
 {
-  nsCOMPtr<nsIIccProvider> icc =
-    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+  nsCOMPtr<nsIIccService> service =
+    do_GetService(ICC_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE_VOID(service);
+
+  nsCOMPtr<nsIIcc> icc;
+  service->GetIccByServiceId(aClientId, getter_AddRefs(icc));
   NS_ENSURE_TRUE_VOID(icc);
 
   nsCOMPtr<nsIIccInfo> iccInfo;
-  icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
+  icc->GetIccInfo(getter_AddRefs(iccInfo));
   NS_ENSURE_TRUE_VOID(iccInfo);
 
   nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
   NS_ENSURE_TRUE_VOID(gsmIccInfo);
   gsmIccInfo->GetMsisdn(mMsisdn);
 }
 #endif // MOZ_B2G_RIL
 
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/dom/FetchDriver.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/URLSearchParams.h"
+#include "mozilla/Telemetry.h"
 
 #include "InternalRequest.h"
 #include "InternalResponse.h"
 
 #include "nsFormData.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
@@ -226,29 +227,33 @@ FetchRequest(nsIGlobalObject* aGlobal, c
     }
 
     nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
     if (!doc) {
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
 
+    Telemetry::Accumulate(Telemetry::FETCH_IS_MAINTHREAD, 1);
+
     nsRefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(p);
     nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
     nsRefPtr<FetchDriver> fetch =
       new FetchDriver(r, doc->NodePrincipal(), loadGroup);
     fetch->SetDocument(doc);
     aRv = fetch->Fetch(resolver);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
   } else {
     WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(worker);
 
+    Telemetry::Accumulate(Telemetry::FETCH_IS_MAINTHREAD, 0);
+
     if (worker->IsServiceWorker()) {
       r->SetSkipServiceWorker();
     }
 
     nsRefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(worker, p, r);
     if (NS_FAILED(NS_DispatchToMainThread(run))) {
       NS_WARNING("MainThreadFetchRunnable dispatch failed!");
     }
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -55,16 +55,19 @@ FetchDriver::~FetchDriver()
 }
 
 nsresult
 FetchDriver::Fetch(FetchDriverObserver* aObserver)
 {
   workers::AssertIsOnMainThread();
   mObserver = aObserver;
 
+  Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REQUEST_PASSTHROUGH,
+                        mRequest->WasCreatedByFetchEvent());
+
   return Fetch(false /* CORS flag */);
 }
 
 nsresult
 FetchDriver::Fetch(bool aCORSFlag)
 {
   // We do not currently implement parts of the spec that lead to recursion.
   MOZ_ASSERT(mFetchRecursionCount == 0);
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -36,16 +36,17 @@ InternalRequest::GetRequestConstructorCo
   copy->mSameOriginDataURL = true;
   copy->mPreserveContentCodings = true;
   // The default referrer is already about:client.
 
   copy->mContentPolicyType = nsIContentPolicy::TYPE_FETCH;
   copy->mMode = mMode;
   copy->mCredentialsMode = mCredentialsMode;
   copy->mCacheMode = mCacheMode;
+  copy->mCreatedByFetchEvent = mCreatedByFetchEvent;
   return copy.forget();
 }
 
 already_AddRefed<InternalRequest>
 InternalRequest::Clone()
 {
   nsRefPtr<InternalRequest> clone = new InternalRequest(*this);
 
@@ -82,16 +83,17 @@ InternalRequest::InternalRequest(const I
   , mForceOriginHeader(aOther.mForceOriginHeader)
   , mPreserveContentCodings(aOther.mPreserveContentCodings)
   , mSameOriginDataURL(aOther.mSameOriginDataURL)
   , mSandboxedStorageAreaURLs(aOther.mSandboxedStorageAreaURLs)
   , mSkipServiceWorker(aOther.mSkipServiceWorker)
   , mSynchronous(aOther.mSynchronous)
   , mUnsafeRequest(aOther.mUnsafeRequest)
   , mUseURLCredentials(aOther.mUseURLCredentials)
+  , mCreatedByFetchEvent(aOther.mCreatedByFetchEvent)
 {
   // NOTE: does not copy body stream... use the fallible Clone() for that
 }
 
 InternalRequest::~InternalRequest()
 {
 }
 
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -278,16 +278,34 @@ public:
     nsCOMPtr<nsIInputStream> s = mBodyStream;
     s.forget(aStream);
   }
 
   // The global is used as the client for the new object.
   already_AddRefed<InternalRequest>
   GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const;
 
+  bool
+  WasCreatedByFetchEvent() const
+  {
+    return mCreatedByFetchEvent;
+  }
+
+  void
+  SetCreatedByFetchEvent()
+  {
+    mCreatedByFetchEvent = true;
+  }
+
+  void
+  ClearCreatedByFetchEvent()
+  {
+    mCreatedByFetchEvent = false;
+  }
+
 private:
   // Does not copy mBodyStream.  Use fallible Clone() for complete copy.
   explicit InternalRequest(const InternalRequest& aOther);
 
   ~InternalRequest();
 
   nsCString mMethod;
   nsCString mURL;
@@ -312,14 +330,18 @@ private:
   bool mForceOriginHeader;
   bool mPreserveContentCodings;
   bool mSameOriginDataURL;
   bool mSandboxedStorageAreaURLs;
   bool mSkipServiceWorker;
   bool mSynchronous;
   bool mUnsafeRequest;
   bool mUseURLCredentials;
+  // This is only set when a Request object is created by a fetch event.  We
+  // use it to check if Service Workers are simply fetching intercepted Request
+  // objects without modifying them.
+  bool mCreatedByFetchEvent = false;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_InternalRequest_h
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -142,26 +142,29 @@ Request::Constructor(const GlobalObject&
     return nullptr;
   }
   RequestMode mode = aInit.mMode.WasPassed() ? aInit.mMode.Value() : fallbackMode;
   RequestCredentials credentials =
     aInit.mCredentials.WasPassed() ? aInit.mCredentials.Value()
                                    : fallbackCredentials;
 
   if (mode != RequestMode::EndGuard_) {
+    request->ClearCreatedByFetchEvent();
     request->SetMode(mode);
   }
 
   if (credentials != RequestCredentials::EndGuard_) {
+    request->ClearCreatedByFetchEvent();
     request->SetCredentialsMode(credentials);
   }
 
   RequestCache cache = aInit.mCache.WasPassed() ?
                        aInit.mCache.Value() : fallbackCache;
   if (cache != RequestCache::EndGuard_) {
+    request->ClearCreatedByFetchEvent();
     request->SetCacheMode(cache);
   }
 
   // Request constructor step 14.
   if (aInit.mMethod.WasPassed()) {
     nsAutoCString method(aInit.mMethod.Value());
     nsAutoCString upperCaseMethod = method;
     ToUpperCase(upperCaseMethod);
@@ -180,30 +183,33 @@ Request::Constructor(const GlobalObject&
 
     // Step 14.2
     if (upperCaseMethod.EqualsLiteral("DELETE") ||
         upperCaseMethod.EqualsLiteral("GET") ||
         upperCaseMethod.EqualsLiteral("HEAD") ||
         upperCaseMethod.EqualsLiteral("POST") ||
         upperCaseMethod.EqualsLiteral("PUT") ||
         upperCaseMethod.EqualsLiteral("OPTIONS")) {
+      request->ClearCreatedByFetchEvent();
       request->SetMethod(upperCaseMethod);
     } else {
+      request->ClearCreatedByFetchEvent();
       request->SetMethod(method);
     }
   }
 
   nsRefPtr<InternalHeaders> requestHeaders = request->Headers();
 
   nsRefPtr<InternalHeaders> headers;
   if (aInit.mHeaders.WasPassed()) {
     nsRefPtr<Headers> h = Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
+    request->ClearCreatedByFetchEvent();
     headers = h->GetInternalHeaders();
   } else {
     headers = new InternalHeaders(*requestHeaders);
   }
 
   requestHeaders->Clear();
 
   if (request->Mode() == RequestMode::No_cors) {
@@ -239,16 +245,17 @@ Request::Constructor(const GlobalObject&
     const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit = aInit.mBody.Value();
     nsCOMPtr<nsIInputStream> stream;
     nsCString contentType;
     aRv = ExtractByteStreamFromBody(bodyInit,
                                     getter_AddRefs(stream), contentType);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
+    request->ClearCreatedByFetchEvent();
     request->SetBody(stream);
 
     if (!contentType.IsVoid() &&
         !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
       requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
                              contentType, aRv);
     }
 
--- a/dom/icc/Assertions.cpp
+++ b/dom/icc/Assertions.cpp
@@ -1,22 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/MozIccBinding.h"
-#include "nsIIccProvider.h"
+#include "nsIIccService.h"
 
 namespace mozilla {
 namespace dom {
 namespace icc {
 
 #define ASSERT_EQUALITY(webidlType, webidlState, xpidlState) \
-  static_assert(static_cast<uint32_t>(webidlType::webidlState) == nsIIccProvider::xpidlState, \
-  #webidlType "::" #webidlState " should equal to nsIIccProvider::" #xpidlState)
+  static_assert(static_cast<uint32_t>(webidlType::webidlState) == nsIIcc::xpidlState, \
+  #webidlType "::" #webidlState " should equal to nsIIccService::" #xpidlState)
 
 /**
  * Enum IccCardState
  */
 #define ASSERT_ICC_CARD_STATE_EQUALITY(webidlState, xpidlState) \
   ASSERT_EQUALITY(IccCardState, webidlState, xpidlState)
 
 ASSERT_ICC_CARD_STATE_EQUALITY(Unknown, CARD_STATE_UNKNOWN);
--- a/dom/icc/Icc.cpp
+++ b/dom/icc/Icc.cpp
@@ -1,25 +1,29 @@
 /* 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 "mozilla/dom/Icc.h"
 
+#include "IccCallback.h"
 #include "mozilla/dom/DOMRequest.h"
 #include "mozilla/dom/IccInfo.h"
 #include "mozilla/dom/MozStkCommandEvent.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsIIccInfo.h"
 #include "nsIIccProvider.h"
+#include "nsIIccService.h"
 #include "nsJSON.h"
 #include "nsRadioInterfaceLayer.h"
 #include "nsServiceManagerUtils.h"
 
+using mozilla::dom::icc::IccCallback;
+
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 bool
 IsPukCardLockType(IccLockType aLockType)
 {
@@ -45,19 +49,20 @@ IsPukCardLockType(IccLockType aLockType)
 NS_IMPL_CYCLE_COLLECTION_INHERITED(Icc, DOMEventTargetHelper, mIccInfo)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Icc)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(Icc, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(Icc, DOMEventTargetHelper)
 
-Icc::Icc(nsPIDOMWindow* aWindow, long aClientId, nsIIccInfo* aIccInfo)
+Icc::Icc(nsPIDOMWindow* aWindow, long aClientId, nsIIcc* aHandler, nsIIccInfo* aIccInfo)
   : mLive(true)
   , mClientId(aClientId)
+  , mHandler(aHandler)
 {
   BindToOwner(aWindow);
 
   mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
 
   if (aIccInfo) {
     aIccInfo->GetIccid(mIccId);
     UpdateIccInfo(aIccInfo);
@@ -74,16 +79,17 @@ Icc::~Icc()
 {
 }
 
 void
 Icc::Shutdown()
 {
   mIccInfo.SetNull();
   mProvider = nullptr;
+  mHandler = nullptr;
   mLive = false;
 }
 
 nsresult
 Icc::NotifyEvent(const nsAString& aName)
 {
   return DispatchTrustedEvent(aName);
 }
@@ -165,20 +171,20 @@ Icc::GetIccInfo(Nullable<OwningMozIccInf
   aIccInfo = mIccInfo;
 }
 
 Nullable<IccCardState>
 Icc::GetCardState() const
 {
   Nullable<IccCardState> result;
 
-  uint32_t cardState = nsIIccProvider::CARD_STATE_UNDETECTED;
-  if (mProvider &&
-      NS_SUCCEEDED(mProvider->GetCardState(mClientId, &cardState)) &&
-      cardState != nsIIccProvider::CARD_STATE_UNDETECTED) {
+  uint32_t cardState = nsIIcc::CARD_STATE_UNDETECTED;
+  if (mHandler &&
+      NS_SUCCEEDED(mHandler->GetCardState(&cardState)) &&
+      cardState != nsIIcc::CARD_STATE_UNDETECTED) {
     MOZ_ASSERT(cardState < static_cast<uint32_t>(IccCardState::EndGuard_));
     result.SetValue(static_cast<IccCardState>(cardState));
   }
 
   return result;
 }
 
 void
@@ -244,110 +250,117 @@ Icc::SendStkEventDownload(const JSContex
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
   }
 }
 
 already_AddRefed<DOMRequest>
 Icc::GetCardLock(IccLockType aLockType, ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mHandler) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsRefPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->GetCardLockEnabled(mClientId, GetOwner(),
-                                              static_cast<uint32_t>(aLockType),
-                                              getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  // TODO: Bug 1125018 - Simplify The Result of GetCardLock and
+  // getCardLockRetryCount in MozIcc.webidl without a wrapper object.
+  nsRefPtr<IccCallback> requestCallback =
+    new IccCallback(GetOwner(), request, true);
+  nsresult rv = mHandler->GetCardLockEnabled(static_cast<uint32_t>(aLockType),
+                                             requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 Icc::UnlockCardLock(const IccUnlockCardLockOptions& aOptions, ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mHandler) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsRefPtr<nsIDOMDOMRequest> request;
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<IccCallback> requestCallback =
+    new IccCallback(GetOwner(), request);
   const nsString& password = IsPukCardLockType(aOptions.mLockType)
                            ? aOptions.mPuk : aOptions.mPin;
-  nsresult rv = mProvider->UnlockCardLock(mClientId, GetOwner(),
-                                          static_cast<uint32_t>(aOptions.mLockType),
-                                          password, aOptions.mNewPin,
-                                          getter_AddRefs(request));
+  nsresult rv =
+    mHandler->UnlockCardLock(static_cast<uint32_t>(aOptions.mLockType),
+                             password, aOptions.mNewPin, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 Icc::SetCardLock(const IccSetCardLockOptions& aOptions, ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mHandler) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsresult rv;
-  nsRefPtr<nsIDOMDOMRequest> request;
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<IccCallback> requestCallback =
+    new IccCallback(GetOwner(), request);
 
   if (aOptions.mEnabled.WasPassed()) {
     // Enable card lock.
     const nsString& password = (aOptions.mLockType == IccLockType::Fdn) ?
                                aOptions.mPin2 : aOptions.mPin;
 
-    rv = mProvider->SetCardLockEnabled(mClientId, GetOwner(),
-                                       static_cast<uint32_t>(aOptions.mLockType),
-                                       password, aOptions.mEnabled.Value(),
-                                       getter_AddRefs(request));
+    rv =
+      mHandler->SetCardLockEnabled(static_cast<uint32_t>(aOptions.mLockType),
+                                   password, aOptions.mEnabled.Value(),
+                                   requestCallback);
   } else {
     // Change card lock password.
-    rv = mProvider->ChangeCardLockPassword(mClientId, GetOwner(),
-                                           static_cast<uint32_t>(aOptions.mLockType),
-                                           aOptions.mPin, aOptions.mNewPin,
-                                           getter_AddRefs(request));
+    rv =
+      mHandler->ChangeCardLockPassword(static_cast<uint32_t>(aOptions.mLockType),
+                                       aOptions.mPin, aOptions.mNewPin,
+                                       requestCallback);
   }
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 Icc::GetCardLockRetryCount(IccLockType aLockType, ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mHandler) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsRefPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->GetCardLockRetryCount(mClientId, GetOwner(),
-                                                 static_cast<uint32_t>(aLockType),
-                                                 getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<IccCallback> requestCallback =
+    new IccCallback(GetOwner(), request);
+  nsresult rv = mHandler->GetCardLockRetryCount(static_cast<uint32_t>(aLockType),
+                                                requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 Icc::ReadContacts(IccContactType aContactType, ErrorResult& aRv)
 {
   if (!mProvider) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -387,48 +400,61 @@ Icc::UpdateContact(const JSContext* aCx,
 
   return request.forget().downcast<DOMRequest>();
 }
 
 already_AddRefed<DOMRequest>
 Icc::MatchMvno(IccMvnoType aMvnoType, const nsAString& aMvnoData,
                ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mHandler) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsRefPtr<nsIDOMDOMRequest> request;
-  nsresult rv = mProvider->MatchMvno(mClientId, GetOwner(),
-                                     static_cast<uint32_t>(aMvnoType),
-                                     aMvnoData, getter_AddRefs(request));
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsRefPtr<IccCallback> requestCallback =
+    new IccCallback(GetOwner(), request);
+  nsresult rv = mHandler->MatchMvno(static_cast<uint32_t>(aMvnoType),
+                                    aMvnoData, requestCallback);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  return request.forget().downcast<DOMRequest>();
+  return request.forget();
 }
 
 already_AddRefed<Promise>
 Icc::GetServiceState(IccService aService, ErrorResult& aRv)
 {
-  if (!mProvider) {
+  if (!mHandler) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsCOMPtr<nsISupports> supports;
-  nsresult rv = mProvider->GetServiceState(mClientId, GetOwner(),
-                                           static_cast<uint32_t>(aService),
-                                           getter_AddRefs(supports));
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
+  if (!global) {
+    return nullptr;
+  }
+
+  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (aRv.Failed()) {
     return nullptr;
   }
 
-  nsCOMPtr<Promise> promise = do_QueryInterface(supports);
+  nsRefPtr<IccCallback> requestCallback =
+    new IccCallback(GetOwner(), promise);
+
+  nsresult rv =
+    mHandler->GetServiceStateEnabled(static_cast<uint32_t>(aService),
+                                     requestCallback);
+
+  if (NS_FAILED(rv)) {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    // fall-through to return promise.
+  }
+
   return promise.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/icc/Icc.h
+++ b/dom/icc/Icc.h
@@ -3,16 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Icc_h
 #define mozilla_dom_Icc_h
 
 #include "mozilla/dom/MozIccBinding.h"
 #include "mozilla/DOMEventTargetHelper.h"
 
+class nsIIcc;
 class nsIIccInfo;
 class nsIIccProvider;
 
 namespace mozilla {
 namespace dom {
 
 class DOMRequest;
 class OwningMozIccInfoOrMozGsmIccInfoOrMozCdmaIccInfo;
@@ -20,17 +21,18 @@ class Promise;
 
 class Icc final : public DOMEventTargetHelper
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Icc, DOMEventTargetHelper)
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
 
-  Icc(nsPIDOMWindow* aWindow, long aClientId, nsIIccInfo* aIccInfo);
+  Icc(nsPIDOMWindow* aWindow, long aClientId,
+      nsIIcc* aHandler, nsIIccInfo* aIccInfo);
 
   void
   Shutdown();
 
   nsresult
   NotifyEvent(const nsAString& aName);
 
   nsresult
@@ -106,23 +108,29 @@ public:
   GetServiceState(IccService aService, ErrorResult& aRv);
 
   IMPL_EVENT_HANDLER(iccinfochange)
   IMPL_EVENT_HANDLER(cardstatechange)
   IMPL_EVENT_HANDLER(stkcommand)
   IMPL_EVENT_HANDLER(stksessionend)
 
 private:
+  // Put definition of the destructor in Icc.cpp to ensure forward declaration
+  // of nsIIccProvider, nsIIcc for the auto-generated .cpp file (i.e.,
+  // MozIccManagerBinding.cpp) that includes this header.
   ~Icc();
 
   bool mLive;
   uint32_t mClientId;
   nsString mIccId;
-  // mProvider is a xpcom service and will be released at shutdown, so it
+  // mProvider is a xpcom service and will be released at Shutdown(), so it
   // doesn't need to be cycle collected.
   nsCOMPtr<nsIIccProvider> mProvider;
+  // mHandler will be released at Shutdown(), so there is no need to join cycle
+  // collection.
+  nsCOMPtr<nsIIcc> mHandler;
   Nullable<OwningMozIccInfoOrMozGsmIccInfoOrMozCdmaIccInfo> mIccInfo;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_icc_Icc_h
new file mode 100644
--- /dev/null
+++ b/dom/icc/IccCallback.cpp
@@ -0,0 +1,135 @@
+/* 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 "IccCallback.h"
+
+#include "mozilla/dom/IccCardLockError.h"
+#include "mozilla/dom/MozIccBinding.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "nsJSUtils.h"
+#include "nsServiceManagerUtils.h"
+
+namespace mozilla {
+namespace dom {
+namespace icc {
+
+NS_IMPL_ISUPPORTS(IccCallback, nsIIccCallback)
+
+IccCallback::IccCallback(nsPIDOMWindow* aWindow, DOMRequest* aRequest,
+                         bool aIsCardLockEnabled)
+  : mWindow(aWindow)
+  , mRequest(aRequest)
+  , mIsCardLockEnabled(aIsCardLockEnabled)
+{
+}
+
+IccCallback::IccCallback(nsPIDOMWindow* aWindow, Promise* aPromise)
+  : mWindow(aWindow)
+  , mPromise(aPromise)
+{
+}
+
+nsresult
+IccCallback::NotifySuccess(JS::Handle<JS::Value> aResult)
+{
+  nsCOMPtr<nsIDOMRequestService> rs =
+    do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
+
+  return rs->FireSuccessAsync(mRequest, aResult);
+}
+
+nsresult
+IccCallback::NotifyGetCardLockEnabled(bool aResult)
+{
+  IccCardLockStatus result;
+  result.mEnabled.Construct(aResult);
+
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.Init(mWindow))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSContext* cx = jsapi.cx();
+  JS::Rooted<JS::Value> jsResult(cx);
+  if (!ToJSValue(cx, result, &jsResult)) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return NotifySuccess(jsResult);
+}
+
+NS_IMETHODIMP
+IccCallback::NotifySuccess()
+{
+  return NotifySuccess(JS::UndefinedHandleValue);
+}
+
+NS_IMETHODIMP
+IccCallback::NotifySuccessWithBoolean(bool aResult)
+{
+  if (mPromise) {
+    mPromise->MaybeResolve(aResult ? JS::TrueHandleValue : JS::FalseHandleValue);
+    return NS_OK;
+  }
+
+  return mIsCardLockEnabled
+    ? NotifyGetCardLockEnabled(aResult)
+    : NotifySuccess(aResult ? JS::TrueHandleValue : JS::FalseHandleValue);
+}
+
+NS_IMETHODIMP
+IccCallback::NotifyGetCardLockRetryCount(int32_t aCount)
+{
+  // TODO: Bug 1125018 - Simplify The Result of GetCardLock and
+  // getCardLockRetryCount in MozIcc.webidl without a wrapper object.
+  IccCardLockRetryCount result;
+  result.mRetryCount.Construct(aCount);
+
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.Init(mWindow))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSContext* cx = jsapi.cx();
+  JS::Rooted<JS::Value> jsResult(cx);
+  if (!ToJSValue(cx, result, &jsResult)) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return NotifySuccess(jsResult);
+}
+
+NS_IMETHODIMP
+IccCallback::NotifyError(const nsAString & aErrorMsg)
+{
+  if (mPromise) {
+    mPromise->MaybeRejectBrokenly(aErrorMsg);
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDOMRequestService> rs =
+    do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
+
+  return rs->FireErrorAsync(mRequest, aErrorMsg);
+}
+
+NS_IMETHODIMP
+IccCallback::NotifyCardLockError(const nsAString & aErrorMsg,
+                                 int32_t aRetryCount)
+{
+  nsRefPtr<IccCardLockError> error =
+    new IccCardLockError(mWindow, aErrorMsg, aRetryCount);
+  mRequest->FireDetailedError(error);
+
+  return NS_OK;
+}
+
+} // namespace icc
+} // namespace dom
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/icc/IccCallback.h
@@ -0,0 +1,64 @@
+/* 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_icc_IccCallback_h
+#define mozilla_dom_icc_IccCallback_h
+
+#include "nsCOMPtr.h"
+#include "nsIIccService.h"
+
+namespace mozilla {
+namespace dom {
+
+class DOMRequest;
+class Promise;
+
+namespace icc {
+
+/**
+ * A callback object for handling asynchronous request/response. This object is
+ * created when an asynchronous request is made and should be destroyed after
+ * Notify*Success/Error is called.
+ * The modules hold the reference of IccCallback in OOP mode and non-OOP mode
+ * are different.
+ * - OOP mode: IccRequestChild
+ * - non-OOP mode: IccService
+ * The reference should be released after Notify*Success/Error is called.
+ */
+class IccCallback final : public nsIIccCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIICCCALLBACK
+
+  // TODO: Bug 1125018 - Simplify The Result of GetCardLock and
+  // getCardLockRetryCount in MozIcc.webidl without a wrapper object.
+  IccCallback(nsPIDOMWindow* aWindow, DOMRequest* aRequest,
+              bool aIsCardLockEnabled = false);
+  IccCallback(nsPIDOMWindow* aWindow, Promise* aPromise);
+
+private:
+  ~IccCallback() {}
+
+  nsresult
+  NotifySuccess(JS::Handle<JS::Value> aResult);
+
+  // TODO: Bug 1125018 - Simplify The Result of GetCardLock and
+  // getCardLockRetryCount in MozIcc.webidl without a wrapper object.
+  nsresult
+  NotifyGetCardLockEnabled(bool aResult);
+
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsRefPtr<DOMRequest> mRequest;
+  nsRefPtr<Promise> mPromise;
+  // TODO: Bug 1125018 - Simplify The Result of GetCardLock and
+  // getCardLockRetryCount in MozIcc.webidl without a wrapper object.
+  bool mIsCardLockEnabled;
+};
+
+} // namespace icc
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_icc_IccCallback_h
--- a/dom/icc/IccInfo.cpp
+++ b/dom/icc/IccInfo.cpp
@@ -1,233 +1,295 @@
 /* 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 "mozilla/dom/IccInfo.h"
 
+#include "mozilla/dom/icc/PIccTypes.h"
 #include "nsPIDOMWindow.h"
 
 #define CONVERT_STRING_TO_NULLABLE_ENUM(_string, _enumType, _enum)      \
 {                                                                       \
   uint32_t i = 0;                                                       \
   for (const EnumEntry* entry = _enumType##Values::strings;             \
        entry->value;                                                    \
        ++entry, ++i) {                                                  \
     if (_string.EqualsASCII(entry->value)) {                            \
       _enum.SetValue(static_cast<_enumType>(i));                        \
     }                                                                   \
   }                                                                     \
 }
 
 using namespace mozilla::dom;
 
+using mozilla::dom::icc::IccInfoData;
+
 // IccInfo
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(IccInfo, mWindow, mIccInfo)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(IccInfo, mWindow)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IccInfo)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IccInfo)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IccInfo)
+  NS_INTERFACE_MAP_ENTRY(nsIIccInfo)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 IccInfo::IccInfo(nsPIDOMWindow* aWindow)
   : mWindow(aWindow)
 {
+  mIccType.SetIsVoid(true);
+  mIccid.SetIsVoid(true);
+  mMcc.SetIsVoid(true);
+  mMnc.SetIsVoid(true);
+  mSpn.SetIsVoid(true);
+}
+
+IccInfo::IccInfo(const IccInfoData& aData)
+{
+  mIccType = aData.iccType();
+  mIccid = aData.iccid();
+  mMcc = aData.mcc();
+  mMnc = aData.mnc();
+  mSpn = aData.spn();
+  mIsDisplayNetworkNameRequired = aData.isDisplayNetworkNameRequired();
+  mIsDisplaySpnRequired = aData.isDisplaySpnRequired();
+}
+
+// nsIIccInfo
+
+NS_IMETHODIMP
+IccInfo::GetIccType(nsAString & aIccType)
+{
+  aIccType = mIccType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+IccInfo::GetIccid(nsAString & aIccid)
+{
+  aIccid = mIccid;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+IccInfo::GetMcc(nsAString & aMcc)
+{
+  aMcc = mMcc;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+IccInfo::GetMnc(nsAString & aMnc)
+{
+  aMnc = mMnc;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+IccInfo::GetSpn(nsAString & aSpn)
+{
+  aSpn = mSpn;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+IccInfo::GetIsDisplayNetworkNameRequired(bool *aIsDisplayNetworkNameRequired)
+{
+  *aIsDisplayNetworkNameRequired = mIsDisplayNetworkNameRequired;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+IccInfo::GetIsDisplaySpnRequired(bool *aIsDisplaySpnRequired)
+{
+  *aIsDisplaySpnRequired = mIsDisplaySpnRequired;
+  return NS_OK;
 }
 
 void
 IccInfo::Update(nsIIccInfo* aInfo)
 {
-  mIccInfo = aInfo;
+  NS_ASSERTION(aInfo, "aInfo is null");
+
+  aInfo->GetIccType(mIccType);
+  aInfo->GetIccid(mIccid);
+  aInfo->GetMcc(mMcc);
+  aInfo->GetMnc(mMnc);
+  aInfo->GetSpn(mSpn);
+  aInfo->GetIsDisplayNetworkNameRequired(
+    &mIsDisplayNetworkNameRequired);
+  aInfo->GetIsDisplaySpnRequired(
+    &mIsDisplaySpnRequired);
 }
 
+// WebIDL implementation
+
 JSObject*
 IccInfo::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MozIccInfoBinding::Wrap(aCx, this, aGivenProto);
 }
 
 Nullable<IccType>
 IccInfo::GetIccType() const
 {
-  if (!mIccInfo) {
-    return Nullable<IccType>();
-  }
-
-  nsAutoString type;
   Nullable<IccType> iccType;
 
-  mIccInfo->GetIccType(type);
-  CONVERT_STRING_TO_NULLABLE_ENUM(type, IccType, iccType);
+  CONVERT_STRING_TO_NULLABLE_ENUM(mIccType, IccType, iccType);
 
   return iccType;
 }
 
 void
 IccInfo::GetIccid(nsAString& aIccId) const
 {
-  if (!mIccInfo) {
-    aIccId.SetIsVoid(true);
-    return;
-  }
-
-  mIccInfo->GetIccid(aIccId);
+  aIccId = mIccid;
 }
 
 void
 IccInfo::GetMcc(nsAString& aMcc) const
 {
-  if (!mIccInfo) {
-    aMcc.SetIsVoid(true);
-    return;
-  }
-
-  mIccInfo->GetMcc(aMcc);
+  aMcc = mMcc;
 }
 
 void
 IccInfo::GetMnc(nsAString& aMnc) const
 {
-  if (!mIccInfo) {
-    aMnc.SetIsVoid(true);
-    return;
-  }
-
-  mIccInfo->GetMnc(aMnc);
+  aMnc = mMnc;
 }
 
 void
 IccInfo::GetSpn(nsAString& aSpn) const
 {
-  if (!mIccInfo) {
-    aSpn.SetIsVoid(true);
-    return;
-  }
-
-  mIccInfo->GetSpn(aSpn);
+  aSpn = mSpn;
 }
 
 bool
 IccInfo::IsDisplayNetworkNameRequired() const
 {
-  if (!mIccInfo) {
-    return false;
-  }
-
-  bool isDisplayNetworkNameRequired;
-  mIccInfo->GetIsDisplayNetworkNameRequired(&isDisplayNetworkNameRequired);
-
-  return isDisplayNetworkNameRequired;
+  return mIsDisplayNetworkNameRequired;
 }
 
 bool
 IccInfo::IsDisplaySpnRequired() const
 {
-  if (!mIccInfo) {
-    return false;
-  }
-
-  bool isDisplaySpnRequired;
-  mIccInfo->GetIsDisplaySpnRequired(&isDisplaySpnRequired);
-
-  return isDisplaySpnRequired;
+  return mIsDisplaySpnRequired;
 }
 
 // GsmIccInfo
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(GsmIccInfo, IccInfo, mGsmIccInfo)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GsmIccInfo)
-NS_INTERFACE_MAP_END_INHERITING(IccInfo)
-
-NS_IMPL_ADDREF_INHERITED(GsmIccInfo, IccInfo)
-NS_IMPL_RELEASE_INHERITED(GsmIccInfo, IccInfo)
+NS_IMPL_ISUPPORTS_INHERITED(GsmIccInfo, IccInfo, nsIGsmIccInfo)
 
 GsmIccInfo::GsmIccInfo(nsPIDOMWindow* aWindow)
   : IccInfo(aWindow)
 {
+  mPhoneNumber.SetIsVoid(true);
 }
 
+GsmIccInfo::GsmIccInfo(const IccInfoData& aData)
+  : IccInfo(aData)
+{
+  mPhoneNumber = aData.phoneNumber();
+}
+
+// nsIGsmIccInfo
+
+NS_IMETHODIMP
+GsmIccInfo::GetMsisdn(nsAString & aMsisdn)
+{
+  aMsisdn = mPhoneNumber;
+  return NS_OK;
+}
+
+// WebIDL implementation
+
 void
 GsmIccInfo::Update(nsIGsmIccInfo* aInfo)
 {
+  MOZ_ASSERT(aInfo);
   nsCOMPtr<nsIIccInfo> iccInfo = do_QueryInterface(aInfo);
   MOZ_ASSERT(iccInfo);
 
   IccInfo::Update(iccInfo);
-  mGsmIccInfo = aInfo;
+
+  aInfo->GetMsisdn(mPhoneNumber);
 }
 
 JSObject*
 GsmIccInfo::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MozGsmIccInfoBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 GsmIccInfo::GetMsisdn(nsAString& aMsisdn) const
 {
-  if (!mGsmIccInfo) {
-    aMsisdn.SetIsVoid(true);
-    return;
-  }
-
-  mGsmIccInfo->GetMsisdn(aMsisdn);
+  aMsisdn = mPhoneNumber;
 }
 
 // CdmaIccInfo
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(CdmaIccInfo, IccInfo, mCdmaIccInfo)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CdmaIccInfo)
-NS_INTERFACE_MAP_END_INHERITING(IccInfo)
-
-NS_IMPL_ADDREF_INHERITED(CdmaIccInfo, IccInfo)
-NS_IMPL_RELEASE_INHERITED(CdmaIccInfo, IccInfo)
+NS_IMPL_ISUPPORTS_INHERITED(CdmaIccInfo, IccInfo, nsICdmaIccInfo)
 
 CdmaIccInfo::CdmaIccInfo(nsPIDOMWindow* aWindow)
   : IccInfo(aWindow)
 {
+  mPhoneNumber.SetIsVoid(true);
+}
+
+CdmaIccInfo::CdmaIccInfo(const IccInfoData& aData)
+  : IccInfo(aData)
+{
+  mPhoneNumber = aData.phoneNumber();
+  mPrlVersion = aData.prlVersion();
+}
+
+// nsICdmaIccInfo
+
+NS_IMETHODIMP
+CdmaIccInfo::GetMdn(nsAString & aMdn)
+{
+  aMdn = mPhoneNumber;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CdmaIccInfo::GetPrlVersion(int32_t *aPrlVersion)
+{
+  *aPrlVersion = mPrlVersion;
+  return NS_OK;
 }
 
 void
 CdmaIccInfo::Update(nsICdmaIccInfo* aInfo)
 {
+  MOZ_ASSERT(aInfo);
   nsCOMPtr<nsIIccInfo> iccInfo = do_QueryInterface(aInfo);
   MOZ_ASSERT(iccInfo);
 
   IccInfo::Update(iccInfo);
-  mCdmaIccInfo = aInfo;
+
+  aInfo->GetMdn(mPhoneNumber);
+  aInfo->GetPrlVersion(&mPrlVersion);
 }
 
+// WebIDL implementation
+
 JSObject*
 CdmaIccInfo::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MozCdmaIccInfoBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 CdmaIccInfo::GetMdn(nsAString& aMdn) const
 {
-  if (!mCdmaIccInfo) {
-    aMdn.SetIsVoid(true);
-    return;
-  }
-
-  mCdmaIccInfo->GetMdn(aMdn);
+  aMdn = mPhoneNumber;
 }
 
 int32_t
 CdmaIccInfo::PrlVersion() const
 {
-  if (!mCdmaIccInfo) {
-    return 0;
-  }
-
-  int32_t prlVersion;
-  mCdmaIccInfo->GetPrlVersion(&prlVersion);
-
-  return prlVersion;
+  return mPrlVersion;
 }
--- a/dom/icc/IccInfo.h
+++ b/dom/icc/IccInfo.h
@@ -9,24 +9,30 @@
 #include "nsIIccInfo.h"
 #include "nsWrapperCache.h"
 
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
-class IccInfo : public nsISupports
+namespace icc {
+class IccInfoData;
+} // namespace icc
+
+class IccInfo : public nsIIccInfo
               , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IccInfo)
+  NS_DECL_NSIICCINFO
 
   explicit IccInfo(nsPIDOMWindow* aWindow);
+  explicit IccInfo(const icc::IccInfoData& aData);
 
   void
   Update(nsIIccInfo* aInfo);
 
   nsPIDOMWindow*
   GetParentObject() const
   {
     return mWindow;
@@ -56,54 +62,68 @@ public:
   IsDisplayNetworkNameRequired() const;
 
   bool
   IsDisplaySpnRequired() const;
 
 protected:
   virtual ~IccInfo() {}
 
-protected:
   nsCOMPtr<nsPIDOMWindow> mWindow;
-  nsCOMPtr<nsIIccInfo> mIccInfo;
+  // To prevent compiling error in OS_WIN in auto-generated UnifiedBindingsXX.cpp,
+  // we have all data fields expended here instead of having a data member of
+  // |IccInfoData| defined in PIccTypes.h which indirectly includes "windows.h"
+  // See 925382 for the restriction of including "windows.h" in UnifiedBindings.cpp.
+  nsString mIccType;
+  nsString mIccid;
+  nsString mMcc;
+  nsString mMnc;
+  nsString mSpn;
+  bool mIsDisplayNetworkNameRequired;
+  bool mIsDisplaySpnRequired;
 };
 
 class GsmIccInfo final : public IccInfo
+                       , public nsIGsmIccInfo
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GsmIccInfo, IccInfo)
+  NS_FORWARD_NSIICCINFO(IccInfo::)
+  NS_DECL_NSIGSMICCINFO
 
   explicit GsmIccInfo(nsPIDOMWindow* aWindow);
+  explicit GsmIccInfo(const icc::IccInfoData& aData);
 
   void
   Update(nsIGsmIccInfo* aInfo);
 
   // WrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // MozCdmaIccInfo WebIDL
   void
   GetMsisdn(nsAString& aMsisdn) const;
 
 private:
   ~GsmIccInfo() {}
 
-private:
-  nsCOMPtr<nsIGsmIccInfo> mGsmIccInfo;
+  nsString mPhoneNumber;
 };
 
 class CdmaIccInfo final : public IccInfo
+                        , public nsICdmaIccInfo
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CdmaIccInfo, IccInfo)
+  NS_FORWARD_NSIICCINFO(IccInfo::)
+  NS_DECL_NSICDMAICCINFO
 
   explicit CdmaIccInfo(nsPIDOMWindow* aWindow);
+  explicit CdmaIccInfo(const icc::IccInfoData& aData);
 
   void
   Update(nsICdmaIccInfo* aInfo);
 
   // WrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
@@ -112,17 +132,17 @@ public:
   GetMdn(nsAString& aMdn) const;
 
   int32_t
   PrlVersion() const;
 
 private:
   ~CdmaIccInfo() {}
 
-private:
-  nsCOMPtr<nsICdmaIccInfo> mCdmaIccInfo;
+  nsString mPhoneNumber;
+  int32_t mPrlVersion;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_IccInfo_h
 
--- a/dom/icc/IccListener.cpp
+++ b/dom/icc/IccListener.cpp
@@ -15,51 +15,77 @@ using namespace mozilla::dom;
 NS_IMPL_ISUPPORTS(IccListener, nsIIccListener)
 
 IccListener::IccListener(IccManager* aIccManager, uint32_t aClientId)
   : mClientId(aClientId)
   , mIccManager(aIccManager)
 {
   MOZ_ASSERT(mIccManager);
 
+  // TODO: Bug 1114938, Refactor STK in MozIcc.webidl with IPDL.
+  //       Remove the registration to IccProvider.
   mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
 
   if (!mProvider) {
     NS_WARNING("Could not acquire nsIIccProvider!");
     return;
   }
 
+  nsCOMPtr<nsIIccService> iccService = do_GetService(ICC_SERVICE_CONTRACTID);
+
+  if (!iccService) {
+    NS_WARNING("Could not acquire nsIIccService!");
+    return;
+  }
+
+  iccService->GetIccByServiceId(mClientId, getter_AddRefs(mHandler));
+  if (!mHandler) {
+    NS_WARNING("Could not acquire nsIIcc!");
+    return;
+  }
+
   nsCOMPtr<nsIIccInfo> iccInfo;
-  mProvider->GetIccInfo(mClientId, getter_AddRefs(iccInfo));
+  mHandler->GetIccInfo(getter_AddRefs(iccInfo));
   if (iccInfo) {
     nsString iccId;
     iccInfo->GetIccid(iccId);
     if (!iccId.IsEmpty()) {
-      mIcc = new Icc(mIccManager->GetOwner(), mClientId, iccInfo);
+      mIcc = new Icc(mIccManager->GetOwner(), mClientId, mHandler, iccInfo);
     }
   }
 
-  DebugOnly<nsresult> rv = mProvider->RegisterIccMsg(mClientId, this);
+  DebugOnly<nsresult> rv = mHandler->RegisterListener(this);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
+                   "Failed registering icc listener with Icc Handler");
+
+  rv = mProvider->RegisterIccMsg(mClientId, this);
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                    "Failed registering icc messages with provider");
 }
 
 IccListener::~IccListener()
 {
   Shutdown();
 }
 
 void
 IccListener::Shutdown()
 {
+  // TODO: Bug 1114938, Refactor STK in MozIcc.webidl with IPDL.
+  //       Remove the unregistration to IccProvider.
   if (mProvider) {
     mProvider->UnregisterIccMsg(mClientId, this);
     mProvider = nullptr;
   }
 
+  if (mHandler) {
+    mHandler->UnregisterListener(this);
+    mHandler = nullptr;
+  }
+
   if (mIcc) {
     mIcc->Shutdown();
     mIcc = nullptr;
   }
 
   mIccManager = nullptr;
 }
 
@@ -93,30 +119,34 @@ IccListener::NotifyCardStateChanged()
   }
 
   return mIcc->NotifyEvent(NS_LITERAL_STRING("cardstatechange"));
 }
 
 NS_IMETHODIMP
 IccListener::NotifyIccInfoChanged()
 {
+  if (!mHandler) {
+    return NS_OK;
+  }
+
   nsCOMPtr<nsIIccInfo> iccInfo;
-  mProvider->GetIccInfo(mClientId, getter_AddRefs(iccInfo));
+  mHandler->GetIccInfo(getter_AddRefs(iccInfo));
 
   // Create/delete icc object based on current iccInfo.
   // 1. If the mIcc is nullptr and iccInfo has valid data, create icc object and
   //    notify mIccManager a new icc is added.
   // 2. If the mIcc is not nullptr and iccInfo becomes to null, delete existed
   //    icc object and notify mIccManager the icc is removed.
   if (!mIcc) {
     if (iccInfo) {
       nsString iccId;
       iccInfo->GetIccid(iccId);
       if (!iccId.IsEmpty()) {
-        mIcc = new Icc(mIccManager->GetOwner(), mClientId, iccInfo);
+        mIcc = new Icc(mIccManager->GetOwner(), mClientId, mHandler, iccInfo);
         mIccManager->NotifyIccAdd(iccId);
         mIcc->NotifyEvent(NS_LITERAL_STRING("iccinfochange"));
       }
     }
   } else {
     mIcc->UpdateIccInfo(iccInfo);
     mIcc->NotifyEvent(NS_LITERAL_STRING("iccinfochange"));
     if (!iccInfo) {
--- a/dom/icc/IccListener.h
+++ b/dom/icc/IccListener.h
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_IccListener_h
 #define mozilla_dom_IccListener_h
 
 #include "nsAutoPtr.h"
 #include "nsIIccProvider.h"
+#include "nsIIccService.h"
 
 namespace mozilla {
 namespace dom {
 
 class IccManager;
 class Icc;
 
 class IccListener final : public nsIIccListener
@@ -37,17 +38,20 @@ private:
 private:
   uint32_t mClientId;
   // We did not setup 'mIcc' and 'mIccManager' being a participant of cycle
   // collection is because in Navigator->Invalidate() it will call
   // mIccManager->Shutdown(), then IccManager will call Shutdown() of each
   // IccListener, this will release the reference and break the cycle.
   nsRefPtr<Icc> mIcc;
   nsRefPtr<IccManager> mIccManager;
-  // mProvider is a xpcom service and will be released at shutdown, so it
+  // mProvider is a xpcom service and will be released at Shutdown(), so it
   // doesn't need to be cycle collected.
   nsCOMPtr<nsIIccProvider> mProvider;
+  // mHandler will be released at Shutdown(), there is no need to join cycle
+  // collection.
+  nsCOMPtr<nsIIcc> mHandler;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_IccListener_h
--- a/dom/icc/IccManager.cpp
+++ b/dom/icc/IccManager.cpp
@@ -5,16 +5,25 @@
 #include "IccManager.h"
 #include "mozilla/dom/MozIccManagerBinding.h"
 #include "Icc.h"
 #include "IccListener.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/dom/IccChangeEvent.h"
 #include "mozilla/Preferences.h"
 #include "nsIIccInfo.h"
+// Service instantiation
+#include "ipc/IccIPCService.h"
+#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
+// TODO: Bug 815526, deprecate RILContentHelper.
+#include "nsIRadioInterfaceLayer.h"
+#include "nsRadioInterfaceLayer.h"
+#include "nsIGonkIccService.h"
+#endif
+#include "nsXULAppAPI.h" // For XRE_GetProcessType()
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IccManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IccManager,
                                                   DOMEventTargetHelper)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -124,8 +133,30 @@ IccManager::GetIccById(const nsAString& 
   for (i = 0; i < mIccListeners.Length(); ++i) {
     Icc* icc = mIccListeners[i]->GetIcc();
     if (icc && aIccId == icc->GetIccId()) {
       return icc;
     }
   }
   return nullptr;
 }
+
+already_AddRefed<nsIIccService>
+NS_CreateIccService()
+{
+  nsCOMPtr<nsIIccService> service;
+
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    service = new mozilla::dom::icc::IccIPCService();
+#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
+  } else {
+    // TODO: Bug 815526, deprecate RILContentHelper.
+    nsCOMPtr <nsIRadioInterfaceLayer> ril =
+      do_GetService(NS_RADIOINTERFACELAYER_CONTRACTID);
+    nsCOMPtr <nsIRadioInterfaceLayer_new> ril_new(do_QueryInterface(ril));
+
+    service = (ril_new) ? do_GetService(GONK_ICC_SERVICE_CONTRACTID)
+                        : do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+#endif
+  }
+
+  return service.forget();
+}
--- a/dom/icc/IccManager.h
+++ b/dom/icc/IccManager.h
@@ -2,17 +2,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_IccManager_h
 #define mozilla_dom_IccManager_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsIIccProvider.h"
 #include "nsTArrayHelpers.h"
 
 namespace mozilla {
 namespace dom {
 
 class Icc;
 class IccListener;
 
@@ -20,17 +19,17 @@ class IccManager final : public DOMEvent
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IccManager, DOMEventTargetHelper)
 
-  IccManager(nsPIDOMWindow* aWindow);
+  explicit IccManager(nsPIDOMWindow* aWindow);
 
   void
   Shutdown();
 
   nsresult
   NotifyIccAdd(const nsAString& aIccId);
 
   nsresult
new file mode 100644
--- /dev/null
+++ b/dom/icc/gonk/IccService.js
@@ -0,0 +1,437 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+var RIL = {};
+Cu.import("resource://gre/modules/ril_consts.js", RIL);
+
+const GONK_ICCSERVICE_CONTRACTID = "@mozilla.org/icc/gonkiccservice;1";
+const GONK_ICCSERVICE_CID = Components.ID("{df854256-9554-11e4-a16c-c39e8d106c26}");
+
+const NS_XPCOM_SHUTDOWN_OBSERVER_ID      = "xpcom-shutdown";
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID  = "nsPref:changed";
+
+const kPrefRilDebuggingEnabled = "ril.debugging.enabled";
+const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
+
+XPCOMUtils.defineLazyServiceGetter(this, "gRadioInterfaceLayer",
+                                   "@mozilla.org/ril;1",
+                                   "nsIRadioInterfaceLayer");
+
+let DEBUG = RIL.DEBUG_RIL;
+function debug(s) {
+  dump("IccService: " + s);
+}
+
+function IccInfo() {}
+IccInfo.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccInfo]),
+
+  // nsIIccInfo
+
+  iccType: null,
+  iccid: null,
+  mcc: null,
+  mnc: null,
+  spn: null,
+  isDisplayNetworkNameRequired: false,
+  isDisplaySpnRequired: false
+};
+
+function GsmIccInfo() {}
+GsmIccInfo.prototype = {
+  __proto__: IccInfo.prototype,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
+                                         Ci.nsIIccInfo]),
+
+  // nsIGsmIccInfo
+
+  msisdn: null
+};
+
+function CdmaIccInfo() {}
+CdmaIccInfo.prototype = {
+  __proto__: IccInfo.prototype,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsICdmaIccInfo,
+                                         Ci.nsIIccInfo]),
+
+  // nsICdmaIccInfo
+
+  mdn: null,
+  prlVersion: 0
+};
+
+function IccService() {
+  this._iccs = [];
+
+  let numClients = gRadioInterfaceLayer.numRadioInterfaces;
+  for (let i = 0; i < numClients; i++) {
+    this._iccs.push(new Icc(gRadioInterfaceLayer.getRadioInterface(i)));
+  }
+
+  this._updateDebugFlag();
+
+  Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
+  Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+}
+IccService.prototype = {
+  classID: GONK_ICCSERVICE_CID,
+
+  classInfo: XPCOMUtils.generateCI({classID: GONK_ICCSERVICE_CID,
+                                    contractID: GONK_ICCSERVICE_CONTRACTID,
+                                    classDescription: "IccService",
+                                    interfaces: [Ci.nsIIccService,
+                                                 Ci.nsIGonkIccService],
+                                    flags: Ci.nsIClassInfo.SINGLETON}),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccService,
+                                         Ci.nsIGonkIccService,
+                                         Ci.nsIObserver]),
+
+  // An array of Icc instances.
+  _iccs: null,
+
+  _updateDebugFlag: function() {
+    try {
+      DEBUG = DEBUG ||
+              Services.prefs.getBoolPref(kPrefRilDebuggingEnabled);
+    } catch (e) {}
+  },
+
+  /**
+   * nsIIccService interface.
+   */
+  getIccByServiceId: function(aServiceId) {
+    let icc = this._iccs[aServiceId];
+    if (!icc) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return icc;
+  },
+
+  /**
+   * nsIGonkIccService interface.
+   */
+  notifyCardStateChanged: function(aServiceId, aCardState) {
+    if (DEBUG) {
+      debug("notifyCardStateChanged for service Id: " + aServiceId +
+            ", CardState: " + aCardState);
+    }
+
+    this.getIccByServiceId(aServiceId)._updateCardState(aCardState);
+  },
+
+  notifyIccInfoChanged: function(aServiceId, aIccInfo) {
+    if (DEBUG) {
+      debug("notifyIccInfoChanged for service Id: " + aServiceId +
+            ", IccInfo: " + JSON.stringify(aIccInfo));
+    }
+
+    this.getIccByServiceId(aServiceId)._updateIccInfo(aIccInfo);
+  },
+
+  notifyImsiChanged: function(aServiceId, aImsi) {
+    if (DEBUG) {
+      debug("notifyImsiChanged for service Id: " + aServiceId +
+            ", Imsi: " + aImsi);
+    }
+
+    let icc = this.getIccByServiceId(aServiceId);
+    icc._imsi = aImsi;
+  },
+
+  /**
+   * nsIObserver interface.
+   */
+  observe: function(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
+        if (aData === kPrefRilDebuggingEnabled) {
+          this._updateDebugFlag();
+        }
+        break;
+      case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
+        Services.prefs.removeObserver(kPrefRilDebuggingEnabled, this);
+        Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+        break;
+    }
+  }
+};
+
+function Icc(aRadioInterface) {
+  this._radioInterface = aRadioInterface;
+  this._listeners = [];
+}
+Icc.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIIcc]),
+
+  _radioInterface: null,
+  _imsi: null,
+  _listeners: null,
+
+  _updateCardState: function(aCardState) {
+    if (this.cardState != aCardState) {
+      this.cardState = aCardState;
+    }
+
+    this._deliverListenerEvent("notifyCardStateChanged");
+  },
+
+  // An utility function to copy objects.
+  _updateInfo: function(aSrcInfo, aDestInfo) {
+    for (let key in aSrcInfo) {
+      aDestInfo[key] = aSrcInfo[key];
+    }
+  },
+
+  /**
+   * We need to consider below cases when update iccInfo:
+   * 1. Should clear iccInfo to null if there is no card detected.
+   * 2. Need to create corresponding object based on iccType.
+   */
+  _updateIccInfo: function(aIccInfo) {
+    // Card is not detected, clear iccInfo to null.
+    if (!aIccInfo || !aIccInfo.iccid) {
+      if (this.iccInfo) {
+        if (DEBUG) {
+          debug("Card is not detected, clear iccInfo to null.");
+        }
+        this.iccInfo = null;
+        this._deliverListenerEvent("notifyIccInfoChanged");
+      }
+      return;
+    }
+
+    // If iccInfo is null, new corresponding object based on iccType.
+    if (!this.iccInfo ||
+        this.iccInfo.iccType != aIccInfo.iccType) {
+      if (aIccInfo.iccType === "ruim" || aIccInfo.iccType === "csim") {
+        this.iccInfo = new CdmaIccInfo();
+      } else if (aIccInfo.iccType === "sim" || aIccInfo.iccType === "usim") {
+        this.iccInfo = new GsmIccInfo();
+      } else {
+        this.iccInfo = new IccInfo();
+      }
+    }
+
+    this._updateInfo(aIccInfo, this.iccInfo);
+
+    this._deliverListenerEvent("notifyIccInfoChanged");
+
+    // Update lastKnownSimMcc.
+    if (aIccInfo.mcc) {
+      try {
+        Services.prefs.setCharPref("ril.lastKnownSimMcc",
+                                   aIccInfo.mcc.toString());
+      } catch (e) {}
+    }
+  },
+
+  _deliverListenerEvent: function(aName, aArgs) {
+    let listeners = this._listeners.slice();
+    for (let listener of listeners) {
+      if (this._listeners.indexOf(listener) === -1) {
+        continue;
+      }
+      let handler = listener[aName];
+      if (typeof handler != "function") {
+        throw new Error("No handler for " + aName);
+      }
+      try {
+        handler.apply(listener, aArgs);
+      } catch (e) {
+        if (DEBUG) {
+          debug("listener for " + aName + " threw an exception: " + e);
+        }
+      }
+    }
+  },
+
+  _modifyCardLock: function(aOperation, aOptions, aCallback) {
+    this._radioInterface.sendWorkerMessage(aOperation,
+                                           aOptions,
+                                           (aResponse) => {
+      if (aResponse.errorMsg) {
+        let retryCount =
+          (aResponse.retryCount !== undefined) ? aResponse.retryCount : -1;
+        aCallback.notifyCardLockError(aResponse.errorMsg, retryCount);
+        return;
+      }
+
+      aCallback.notifySuccess();
+    });
+  },
+
+  /**
+   * Helper to match the MVNO pattern with IMSI.
+   *
+   * Note: Characters 'x' and 'X' in MVNO are skipped and not compared.
+   *       E.g., if the aMvnoData passed is '310260x10xxxxxx', then the
+   *       function returns true only if imsi has the same first 6 digits, 8th
+   *       and 9th digit.
+   *
+   * @param aMvnoData
+   *        MVNO pattern.
+   * @param aImsi
+   *        IMSI of this ICC.
+   *
+   * @return true if matched.
+   */
+  _isImsiMatches: function(aMvnoData, aImsi) {
+    // This should not be an error, but a mismatch.
+    if (aMvnoData.length > aImsi.length) {
+      return false;
+    }
+
+    for (let i = 0; i < aMvnoData.length; i++) {
+      let c = aMvnoData[i];
+      if ((c !== 'x') && (c !== 'X') && (c !== aImsi[i])) {
+        return false;
+      }
+    }
+    return true;
+  },
+
+  /**
+   * nsIIcc interface.
+   */
+  iccInfo: null,
+  cardState: Ci.nsIIcc.CARD_STATE_UNKNOWN,
+
+  registerListener: function(aListener) {
+    if (this._listeners.indexOf(aListener) >= 0) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    this._listeners.push(aListener);
+  },
+
+  unregisterListener: function(aListener) {
+    let index = this._listeners.indexOf(aListener);
+    if (index >= 0) {
+      this._listeners.splice(index, 1);
+    }
+  },
+
+  getCardLockEnabled: function(aLockType, aCallback) {
+    this._radioInterface.sendWorkerMessage("iccGetCardLockEnabled",
+                                           { lockType: aLockType },
+                                           (aResponse) => {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return;
+      }
+
+      aCallback.notifySuccessWithBoolean(aResponse.enabled);
+    });
+  },
+
+  unlockCardLock: function(aLockType, aPassword, aNewPin, aCallback) {
+    this._modifyCardLock("iccUnlockCardLock",
+                         { lockType: aLockType,
+                           password: aPassword,
+                           newPin: aNewPin },
+                         aCallback);
+  },
+
+  setCardLockEnabled: function(aLockType, aPassword, aEnabled, aCallback) {
+    this._modifyCardLock("iccSetCardLockEnabled",
+                         { lockType: aLockType,
+                           password: aPassword,
+                           enabled: aEnabled },
+                         aCallback);
+  },
+
+  changeCardLockPassword: function(aLockType, aPassword, aNewPassword, aCallback) {
+    this._modifyCardLock("iccChangeCardLockPassword",
+                         { lockType: aLockType,
+                           password: aPassword,
+                           newPassword: aNewPassword, },
+                         aCallback);
+  },
+
+  getCardLockRetryCount: function(aLockType, aCallback) {
+    this._radioInterface.sendWorkerMessage("iccGetCardLockRetryCount",
+                                           { lockType: aLockType },
+                                           (aResponse) => {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return;
+      }
+
+      aCallback.notifyGetCardLockRetryCount(aResponse.retryCount);
+    });
+  },
+
+  matchMvno: function(aMvnoType, aMvnoData, aCallback) {
+    if (!aMvnoData) {
+      aCallback.notifyError(RIL.GECKO_ERROR_INVALID_PARAMETER);
+      return;
+    }
+
+    switch (aMvnoType) {
+      case Ci.nsIIcc.CARD_MVNO_TYPE_IMSI:
+        let imsi = this._imsi;
+        if (!imsi) {
+          aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
+          break;
+        }
+        aCallback.notifySuccessWithBoolean(
+          this._isImsiMatches(aMvnoData, imsi));
+        break;
+      case Ci.nsIIcc.CARD_MVNO_TYPE_SPN:
+        let spn = this.iccInfo && this.iccInfo.spn;
+        if (!spn) {
+          aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
+          break;
+        }
+        aCallback.notifySuccessWithBoolean(spn == aMvnoData);
+        break;
+      case Ci.nsIIcc.CARD_MVNO_TYPE_GID:
+        this._radioInterface.sendWorkerMessage("getGID1",
+                                               null,
+                                               (aResponse) => {
+          let gid = aResponse.gid1;
+          let mvnoDataLength = aMvnoData.length;
+
+          if (!gid) {
+            aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
+          } else if (mvnoDataLength > gid.length) {
+            aCallback.notifySuccessWithBoolean(false);
+          } else {
+            let result =
+              gid.substring(0, mvnoDataLength).toLowerCase() ==
+              aMvnoData.toLowerCase();
+            aCallback.notifySuccessWithBoolean(result);
+          }
+        });
+        break;
+      default:
+        aCallback.notifyError(RIL.GECKO_ERROR_MODE_NOT_SUPPORTED);
+        break;
+    }
+  },
+
+  getServiceStateEnabled: function(aService, aCallback) {
+    this._radioInterface.sendWorkerMessage("getIccServiceState",
+                                           { service: aService },
+                                           (aResponse) => {
+      if (aResponse.errorMsg) {
+        aCallback.notifyError(aResponse.errorMsg);
+        return;
+      }
+
+      aCallback.notifySuccessWithBoolean(aResponse.result);
+    });
+  }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([IccService]);
new file mode 100644
--- /dev/null
+++ b/dom/icc/gonk/IccService.manifest
@@ -0,0 +1,6 @@
+# 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/.
+
+component {df854256-9554-11e4-a16c-c39e8d106c26} IccService.js
+contract @mozilla.org/icc/gonkiccservice;1 {df854256-9554-11e4-a16c-c39e8d106c26}
\ No newline at end of file
--- a/dom/icc/interfaces/moz.build
+++ b/dom/icc/interfaces/moz.build
@@ -1,17 +1,19 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 XPIDL_SOURCES += [
     'nsIIccInfo.idl',
-    'nsIIccProvider.idl',
+    'nsIIccProvider.idl', # TODO: Bug 815526, deprecate RILContentHelper.
+    'nsIIccService.idl',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']:
     XPIDL_SOURCES += [
+        'nsIGonkIccService.idl',
         'nsIIccMessenger.idl',
     ]
 
 XPIDL_MODULE = 'dom_icc'
new file mode 100644
--- /dev/null
+++ b/dom/icc/interfaces/nsIGonkIccService.idl
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIIccService.idl"
+
+%{C++
+#define GONK_ICC_SERVICE_CONTRACTID \
+        "@mozilla.org/icc/gonkiccservice;1"
+%}
+
+[scriptable, uuid(a037b8a2-b027-11e4-9496-c3b7af59a512)]
+interface nsIGonkIccService : nsIIccService
+{
+  // TODO: Bug 1114938 - Refactor STK in MozIcc.webidl with IPDL:
+  // void notifyStkCommand(in unsigned long aServiceId, in jsval aStkcommand);
+  // void notifyStkSessionEnd(in unsigned long aServiceId);
+  void notifyCardStateChanged(in unsigned long aServiceId, in unsigned long aCardState);
+  void notifyIccInfoChanged(in unsigned long aServiceId, in jsval aIccInfo);
+  void notifyImsiChanged(in unsigned long aServiceId, in DOMString aImsi);
+};
--- a/dom/icc/interfaces/nsIIccProvider.idl
+++ b/dom/icc/interfaces/nsIIccProvider.idl
@@ -2,25 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMDOMRequest;
 interface nsIDOMWindow;
 interface nsIIccInfo;
-
-[scriptable, uuid(7c0ada3d-d8d4-493e-9243-fa3df39855e4)]
-interface nsIIccListener : nsISupports
-{
-  void notifyStkCommand(in DOMString aMessage);
-  void notifyStkSessionEnd();
-  void notifyCardStateChanged();
-  void notifyIccInfoChanged();
-};
+interface nsIIccListener;
 
 [scriptable, uuid(6136acab-b50e-494a-a86d-df392a032897)]
 interface nsIIccChannelCallback : nsISupports
 {
   /**
    * Callback function to notify on successfully opening a logical channel.
    *
    * @param channel
@@ -53,114 +45,28 @@ interface nsIIccChannelCallback : nsISup
    *
    */
   void notifyError(in DOMString error);
 };
 
 /**
  * XPCOM component (in the content process) that provides the ICC information.
  */
-[scriptable, uuid(a203cd2e-2280-4d8e-a687-42b745d322c1)]
+[scriptable, uuid(7dd6e186-b007-11e4-9b7e-7717d7863cb8)]
 interface nsIIccProvider : nsISupports
 {
-  // MUST match enum IccCardState in MozIcc.webidl!
-  const unsigned long CARD_STATE_UNKNOWN = 0;
-  const unsigned long CARD_STATE_READY = 1;
-  const unsigned long CARD_STATE_PIN_REQUIRED = 2;
-  const unsigned long CARD_STATE_PUK_REQUIRED = 3;
-  const unsigned long CARD_STATE_PERMANENT_BLOCKED = 4;
-  const unsigned long CARD_STATE_PERSONALIZATION_IN_PROGRESS = 5;
-  const unsigned long CARD_STATE_PERSONALIZATION_READY = 6;
-  const unsigned long CARD_STATE_NETWORK_LOCKED = 7;
-  const unsigned long CARD_STATE_NETWORK_SUBSET_LOCKED = 8;
-  const unsigned long CARD_STATE_CORPORATE_LOCKED = 9;
-  const unsigned long CARD_STATE_SERVICE_PROVIDER_LOCKED = 10;
-  const unsigned long CARD_STATE_SIM_LOCKED = 11;
-  const unsigned long CARD_STATE_NETWORK_PUK_REQUIRED = 12;
-  const unsigned long CARD_STATE_NETWORK_SUBSET_PUK_REQUIRED = 13;
-  const unsigned long CARD_STATE_CORPORATE_PUK_REQUIRED = 14;
-  const unsigned long CARD_STATE_SERVICE_PROVIDER_PUK_REQUIRED = 15;
-  const unsigned long CARD_STATE_SIM_PUK_REQUIRED = 16;
-  const unsigned long CARD_STATE_NETWORK1_LOCKED = 17;
-  const unsigned long CARD_STATE_NETWORK2_LOCKED = 18;
-  const unsigned long CARD_STATE_HRPD_NETWORK_LOCKED = 19;
-  const unsigned long CARD_STATE_RUIM_CORPORATE_LOCKED = 20;
-  const unsigned long CARD_STATE_RUIM_SERVICE_PROVIDER_LOCKED = 21;
-  const unsigned long CARD_STATE_RUIM_LOCKED = 22;
-  const unsigned long CARD_STATE_NETWORK1_PUK_REQUIRED = 23;
-  const unsigned long CARD_STATE_NETWORK2_PUK_REQUIRED = 24;
-  const unsigned long CARD_STATE_HRPD_NETWORK_PUK_REQUIRED = 25;
-  const unsigned long CARD_STATE_RUIM_CORPORATE_PUK_REQUIRED = 26;
-  const unsigned long CARD_STATE_RUIM_SERVICE_PROVIDER_PUK_REQUIRED = 27;
-  const unsigned long CARD_STATE_RUIM_PUK_REQUIRED = 28;
-  const unsigned long CARD_STATE_ILLEGAL = 29;
-
-  const unsigned long CARD_STATE_UNDETECTED = 4294967295; // UINT32_MAX
-
-  // MUST match with enum IccLockType in MozIcc.webidl
-  const unsigned long CARD_LOCK_TYPE_PIN = 0;
-  const unsigned long CARD_LOCK_TYPE_PIN2 = 1;
-  const unsigned long CARD_LOCK_TYPE_PUK = 2;
-  const unsigned long CARD_LOCK_TYPE_PUK2 = 3;
-  const unsigned long CARD_LOCK_TYPE_NCK = 4;
-  const unsigned long CARD_LOCK_TYPE_NSCK = 5;
-  const unsigned long CARD_LOCK_TYPE_NCK1 = 6;
-  const unsigned long CARD_LOCK_TYPE_NCK2 = 7;
-  const unsigned long CARD_LOCK_TYPE_HNCK = 8;
-  const unsigned long CARD_LOCK_TYPE_CCK = 9;
-  const unsigned long CARD_LOCK_TYPE_SPCK = 10;
-  const unsigned long CARD_LOCK_TYPE_PCK = 11;
-  const unsigned long CARD_LOCK_TYPE_RCCK = 12;
-  const unsigned long CARD_LOCK_TYPE_RSPCK = 13;
-  const unsigned long CARD_LOCK_TYPE_NCK_PUK = 14;
-  const unsigned long CARD_LOCK_TYPE_NSCK_PUK = 15;
-  const unsigned long CARD_LOCK_TYPE_NCK1_PUK = 16;
-  const unsigned long CARD_LOCK_TYPE_NCK2_PUK = 17;
-  const unsigned long CARD_LOCK_TYPE_HNCK_PUK = 18;
-  const unsigned long CARD_LOCK_TYPE_CCK_PUK = 19;
-  const unsigned long CARD_LOCK_TYPE_SPCK_PUK = 20;
-  const unsigned long CARD_LOCK_TYPE_PCK_PUK = 21;
-  const unsigned long CARD_LOCK_TYPE_RCCK_PUK = 22;
-  const unsigned long CARD_LOCK_TYPE_RSPCK_PUK = 23;
-  const unsigned long CARD_LOCK_TYPE_FDN = 24;
-
-  // MUST match with enum IccContactType in MozIcc.webidl
-  const unsigned long CARD_CONTACT_TYPE_ADN = 0;
-  const unsigned long CARD_CONTACT_TYPE_FDN = 1;
-  const unsigned long CARD_CONTACT_TYPE_SDN = 2;
-
-  // MUST match with enum IccMvnoType in MozIcc.webidl
-  const unsigned long CARD_MVNO_TYPE_IMSI = 0;
-  const unsigned long CARD_MVNO_TYPE_SPN = 1;
-  const unsigned long CARD_MVNO_TYPE_GID = 2;
-
-  // MUST match with enum IccService in MozIcc.webidl
-  const unsigned long CARD_SERVICE_FDN = 0;
-
   /**
    * Called when a content process registers receiving unsolicited messages from
    * RadioInterfaceLayer in the chrome process. Only a content process that has
    * the 'mobileconnection' permission is allowed to register.
    */
   void registerIccMsg(in unsigned long clientId, in nsIIccListener listener);
   void unregisterIccMsg(in unsigned long clientId, in nsIIccListener listener);
 
   /**
-   * UICC Information
-   */
-  nsIIccInfo getIccInfo(in unsigned long clientId);
-
-  /**
-   * Card State
-   *
-   * One of the nsIIccProvider.CARD_STATE_* values.
-   */
-  unsigned long getCardState(in unsigned long clientId);
-
-  /**
    * STK interfaces.
    */
   void sendStkResponse(in unsigned long clientId,
                        in nsIDOMWindow window,
                        in jsval command,
                        in jsval response);
   void sendStkMenuSelection(in unsigned long clientId,
                             in nsIDOMWindow window,
@@ -169,54 +75,29 @@ interface nsIIccProvider : nsISupports
   void sendStkTimerExpiration(in unsigned long clientId,
                               in nsIDOMWindow window,
                               in jsval timer);
   void sendStkEventDownload(in unsigned long clientId,
                             in nsIDOMWindow window,
                             in jsval event);
 
   /**
-   * Card lock interfaces.
-   */
-  nsIDOMDOMRequest getCardLockEnabled(in unsigned long clientId,
-                                      in nsIDOMWindow window,
-                                      in unsigned long lockType);
-  nsIDOMDOMRequest unlockCardLock(in unsigned long clientId,
-                                  in nsIDOMWindow window,
-                                  in unsigned long lockType,
-                                  in DOMString password,
-                                  [optional] in DOMString newPin);
-  nsIDOMDOMRequest setCardLockEnabled(in unsigned long clientId,
-                                      in nsIDOMWindow window,
-                                      in unsigned long lockType,
-                                      in DOMString password,
-                                      in boolean enabled);
-  nsIDOMDOMRequest changeCardLockPassword(in unsigned long clientId,
-                                          in nsIDOMWindow window,
-                                          in unsigned long lockType,
-                                          in DOMString password,
-                                          in DOMString newPassword);
-  nsIDOMDOMRequest getCardLockRetryCount(in unsigned long clientId,
-                                         in nsIDOMWindow window,
-                                         in unsigned long lockType);
-
-  /**
    * Phonebook interfaces.
    */
   nsIDOMDOMRequest readContacts(in unsigned long clientId,
                                 in nsIDOMWindow window,
                                 in unsigned long contactType);
 
   nsIDOMDOMRequest updateContact(in unsigned long clientId,
                                  in nsIDOMWindow window,
                                  in unsigned long contactType,
                                  in jsval contact,
                                  in DOMString pin2);
 
-/**
+  /**
    * Secure Card Icc communication channel
    */
   void iccOpenChannel(in unsigned long clientId,
                       in DOMString aid,
                       in nsIIccChannelCallback callback);
 
   /**
    * Exchange Command APDU (C-APDU) with SIM on the given logical channel.
@@ -233,21 +114,9 @@ interface nsIIccProvider : nsISupports
                        in octet p2,
                        in short p3,
                        in DOMString data,
                        in nsIIccChannelCallback callback);
 
   void iccCloseChannel(in unsigned long clientId,
                        in long channel,
                        in nsIIccChannelCallback callback);
-
-  /**
-   * Helpers
-   */
-  nsIDOMDOMRequest matchMvno(in unsigned long clientId,
-                             in nsIDOMWindow window,
-                             in unsigned long mvnoType,
-                             in DOMString mvnoData);
-
-  nsISupports getServiceState(in unsigned long clientId,
-                              in nsIDOMWindow window,
-                              in unsigned long service);
 };
new file mode 100644
--- /dev/null
+++ b/dom/icc/interfaces/nsIIccService.idl
@@ -0,0 +1,329 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIIcc;
+interface nsIIccInfo;
+
+[scriptable, uuid(7c0ada3d-d8d4-493e-9243-fa3df39855e4)]
+interface nsIIccListener : nsISupports
+{
+  void notifyStkCommand(in DOMString aMessage);
+  void notifyStkSessionEnd();
+  void notifyCardStateChanged();
+  void notifyIccInfoChanged();
+};
+
+/**
+ * A callback interface for handling asynchronous response.
+ */
+[scriptable, uuid(b0e2899a-adc3-11e4-89cf-1b60eaa35b06)]
+interface nsIIccCallback : nsISupports
+{
+  /**
+   * The success callback with no result required:
+   * |unlockCardLock|, |setCardLockEnabled| and |changeCardLockPassword|.
+   */
+  void notifySuccess();
+
+  /**
+   * The success callback with boolean response:
+   * |getCardLockEnabled|, |matchMvno|, and |getServiceStateEnabled|.
+   */
+  void notifySuccessWithBoolean(in boolean aResult);
+
+  /**
+   * The success callback of |getCardLockRetryCount|.
+   *
+   * @param aCount
+   *        The number of remaining retries. -1 if unknown.
+   */
+  void notifyGetCardLockRetryCount(in long aCount);
+
+  /**
+   * The error callback of |getCardLockEnabled|, |getCardLockRetryCount|,
+   * |matchMvno|, and |getServiceStateEnabled|.
+   *
+   * @param aErrorMsg
+   *        The error message.
+   */
+  void notifyError(in DOMString aErrorMsg);
+
+  /**
+   * The error callback of |unlockCardLock|, |setCardLockEnabled| and
+   * |changeCardLockPassword|.
+   *
+   * @param aErrorMsg
+   *        The error message.
+   * @param aRetryCount
+   *        The number of remaining retries. -1 if unknown.
+   */
+  void notifyCardLockError(in DOMString aErrorMsg, in long aRetryCount);
+};
+
+%{C++
+#define ICC_SERVICE_CID \
+  { 0xbab0277a, 0x900e, 0x11e4, { 0x80, 0xc7, 0xdb, 0xd7, 0xad, 0x05, 0x24, 0x01 } }
+#define ICC_SERVICE_CONTRACTID \
+  "@mozilla.org/icc/iccservice;1"
+
+template<typename T> struct already_AddRefed;
+%}
+
+/**
+ * XPCOM Service for the selection of the ICC to be accessed.
+ */
+[scriptable, uuid(6590a04c-9ca4-11e4-ae95-570876ecc428)]
+interface nsIIccService : nsISupports
+{
+  /**
+   * Get Icc instance with specified Service Id.
+   *
+   * @param aServiceId
+   *        Started from 0 to nsIMobileConnectionService.numItems - 1;
+   *
+   * @return a nsIcc instance.
+   */
+  nsIIcc getIccByServiceId(in unsigned long aServiceId);
+};
+
+%{C++
+already_AddRefed<nsIIccService>
+NS_CreateIccService();
+%}
+
+/**
+ * XPCOM component that provides the access to the selected ICC.
+ */
+[scriptable, uuid(38a5bbe2-add6-11e4-ba9e-e390d1d19195)]
+interface nsIIcc : nsISupports
+{
+  /**
+   * Card State Constants
+   *
+   * Note: MUST be matched with enum IccCardState in MozIcc.webidl!
+   */
+  const unsigned long CARD_STATE_UNKNOWN = 0;
+  const unsigned long CARD_STATE_READY = 1;
+  const unsigned long CARD_STATE_PIN_REQUIRED = 2;
+  const unsigned long CARD_STATE_PUK_REQUIRED = 3;
+  const unsigned long CARD_STATE_PERMANENT_BLOCKED = 4;
+  const unsigned long CARD_STATE_PERSONALIZATION_IN_PROGRESS = 5;
+  const unsigned long CARD_STATE_PERSONALIZATION_READY = 6;
+  const unsigned long CARD_STATE_NETWORK_LOCKED = 7;
+  const unsigned long CARD_STATE_NETWORK_SUBSET_LOCKED = 8;
+  const unsigned long CARD_STATE_CORPORATE_LOCKED = 9;
+  const unsigned long CARD_STATE_SERVICE_PROVIDER_LOCKED = 10;
+  const unsigned long CARD_STATE_SIM_LOCKED = 11;
+  const unsigned long CARD_STATE_NETWORK_PUK_REQUIRED = 12;
+  const unsigned long CARD_STATE_NETWORK_SUBSET_PUK_REQUIRED = 13;
+  const unsigned long CARD_STATE_CORPORATE_PUK_REQUIRED = 14;
+  const unsigned long CARD_STATE_SERVICE_PROVIDER_PUK_REQUIRED = 15;
+  const unsigned long CARD_STATE_SIM_PUK_REQUIRED = 16;
+  const unsigned long CARD_STATE_NETWORK1_LOCKED = 17;
+  const unsigned long CARD_STATE_NETWORK2_LOCKED = 18;
+  const unsigned long CARD_STATE_HRPD_NETWORK_LOCKED = 19;
+  const unsigned long CARD_STATE_RUIM_CORPORATE_LOCKED = 20;
+  const unsigned long CARD_STATE_RUIM_SERVICE_PROVIDER_LOCKED = 21;
+  const unsigned long CARD_STATE_RUIM_LOCKED = 22;
+  const unsigned long CARD_STATE_NETWORK1_PUK_REQUIRED = 23;
+  const unsigned long CARD_STATE_NETWORK2_PUK_REQUIRED = 24;
+  const unsigned long CARD_STATE_HRPD_NETWORK_PUK_REQUIRED = 25;
+  const unsigned long CARD_STATE_RUIM_CORPORATE_PUK_REQUIRED = 26;
+  const unsigned long CARD_STATE_RUIM_SERVICE_PROVIDER_PUK_REQUIRED = 27;
+  const unsigned long CARD_STATE_RUIM_PUK_REQUIRED = 28;
+  const unsigned long CARD_STATE_ILLEGAL = 29;
+
+  const unsigned long CARD_STATE_UNDETECTED = 4294967295; // UINT32_MAX
+
+  /**
+   * Card Lock Constants
+   *
+   * Note: MUST be matched with enum IccLockType in MozIcc.webidl!
+   */
+  const unsigned long CARD_LOCK_TYPE_PIN = 0;
+  const unsigned long CARD_LOCK_TYPE_PIN2 = 1;
+  const unsigned long CARD_LOCK_TYPE_PUK = 2;
+  const unsigned long CARD_LOCK_TYPE_PUK2 = 3;
+  const unsigned long CARD_LOCK_TYPE_NCK = 4;
+  const unsigned long CARD_LOCK_TYPE_NSCK = 5;
+  const unsigned long CARD_LOCK_TYPE_NCK1 = 6;
+  const unsigned long CARD_LOCK_TYPE_NCK2 = 7;
+  const unsigned long CARD_LOCK_TYPE_HNCK = 8;
+  const unsigned long CARD_LOCK_TYPE_CCK = 9;
+  const unsigned long CARD_LOCK_TYPE_SPCK = 10;
+  const unsigned long CARD_LOCK_TYPE_PCK = 11;
+  const unsigned long CARD_LOCK_TYPE_RCCK = 12;
+  const unsigned long CARD_LOCK_TYPE_RSPCK = 13;
+  const unsigned long CARD_LOCK_TYPE_NCK_PUK = 14;
+  const unsigned long CARD_LOCK_TYPE_NSCK_PUK = 15;
+  const unsigned long CARD_LOCK_TYPE_NCK1_PUK = 16;
+  const unsigned long CARD_LOCK_TYPE_NCK2_PUK = 17;
+  const unsigned long CARD_LOCK_TYPE_HNCK_PUK = 18;
+  const unsigned long CARD_LOCK_TYPE_CCK_PUK = 19;
+  const unsigned long CARD_LOCK_TYPE_SPCK_PUK = 20;
+  const unsigned long CARD_LOCK_TYPE_PCK_PUK = 21;
+  const unsigned long CARD_LOCK_TYPE_RCCK_PUK = 22;
+  const unsigned long CARD_LOCK_TYPE_RSPCK_PUK = 23;
+  const unsigned long CARD_LOCK_TYPE_FDN = 24;
+
+  /**
+   * Contact Type Constants
+   *
+   * Note: MUST be matched with enum IccContactType in MozIcc.webidl!
+   */
+  const unsigned long CARD_CONTACT_TYPE_ADN = 0;
+  const unsigned long CARD_CONTACT_TYPE_FDN = 1;
+  const unsigned long CARD_CONTACT_TYPE_SDN = 2;
+
+  /**
+   * MVNO Type Constants
+   *
+   * Note: MUST be matched with enum IccMvnoType in MozIcc.webidl!
+   */
+  const unsigned long CARD_MVNO_TYPE_IMSI = 0;
+  const unsigned long CARD_MVNO_TYPE_SPN = 1;
+  const unsigned long CARD_MVNO_TYPE_GID = 2;
+
+  /**
+   * Card Service Constants
+   *
+   * Note: MUST be matched with enum IccService in MozIcc.webidl!
+   */
+  const unsigned long CARD_SERVICE_FDN = 0;
+
+  /**
+   * Called to register icc-related changes.
+   *
+   * 'mobileconnection' permission is required to register.
+   */
+  void registerListener(in nsIIccListener aListener);
+  void unregisterListener(in nsIIccListener aListener);
+
+  /**
+   * Information stored in this ICC.
+   */
+  readonly attribute nsIIccInfo iccInfo;
+
+  /**
+   * Indicates the state of this ICC.
+   *
+   * One of the CARD_STATE_* values.
+   */
+  readonly attribute unsigned long cardState;
+
+  /**
+   * Get the status of an ICC lock (e.g. the PIN lock).
+   *
+   * @param aLockType
+   *        One of the CARD_LOCK_TYPE_* values.
+   * @param aCallback
+   *        An instance of nsIIccCallback:
+   *        nsIIccCallback::notifySuccessWithBoolean() if success.
+   *        nsIIccCallback::notifyError(), otherwise.
+   */
+  void getCardLockEnabled(in unsigned long aLockType,
+                          in nsIIccCallback aCallback);
+
+  /**
+   * Unlock a card lock.
+   *
+   * @param aLockType
+   *        One of the CARD_LOCK_TYPE_* values.
+   * @param aPassword
+   *        The password of this lock.
+   * @param aNewPin (Optional)
+   *        The new PIN to be set after PUK/PUK2 is unlock.
+   * @param aCallback
+   *        An instance of nsIIccCallback:
+   *        nsIIccCallback::notifySuccess() if success.
+   *        nsIIccCallback::notifyCardLockError(), otherwise.
+   */
+  void unlockCardLock(in unsigned long aLockType,
+                      in DOMString aPassword,
+                      in DOMString aNewPin,
+                      in nsIIccCallback aCallback);
+
+  /**
+   * Enable/Disable a card lock.
+   *
+   * @param aLockType
+   *        One of the CARD_LOCK_TYPE_* values.
+   * @param aPassword
+   *        The password of this lock.
+   * @param aEnabled.
+   *        True to enable the lock. False to disable, otherwise.
+   * @param aCallback
+   *        An instance of nsIIccCallback:
+   *        nsIIccCallback::notifySuccess() if success.
+   *        nsIIccCallback::notifyCardLockError(), otherwise.
+   */
+  void setCardLockEnabled(in unsigned long aLockType,
+                          in DOMString aPassword,
+                          in boolean aEnabled,
+                          in nsIIccCallback aCallback);
+
+  /**
+   * Change the password of a card lock.
+   *
+   * @param aLockType
+   *        One of the CARD_LOCK_TYPE_* values.
+   * @param aPassword
+   *        The password of this lock.
+   * @param aNewPassword.
+   *        The new password of this lock.
+   * @param aCallback
+   *        An instance of nsIIccCallback:
+   *        nsIIccCallback::notifySuccess() if success.
+   *        nsIIccCallback::notifyCardLockError(), otherwise.
+   */
+  void changeCardLockPassword(in unsigned long aLockType,
+                              in DOMString aPassword,
+                              in DOMString aNewPassword,
+                              in nsIIccCallback aCallback);
+
+  /**
+   * Get the number of remaining tries of a lock.
+   *
+   * @param aLockType
+   *        One of the CARD_LOCK_TYPE_* values.
+   * @param aCallback
+   *        An instance of nsIIccCallback:
+   *        nsIIccCallback::notifyGetCardLockRetryCount() if success.
+   *        nsIIccCallback::notifyError(), otherwise.
+   */
+  void getCardLockRetryCount(in unsigned long aLockType,
+                             in nsIIccCallback aCallback);
+
+  /**
+   * Verify whether the passed data (matchData) matches with some ICC's field
+   * according to the mvno type (mvnoType).
+   *
+   * @param aMvnoType
+   *        One of CARD_MVNO_TYPE_* values.
+   * @param aMvnoData
+   *        Data to be compared with ICC's field.
+   * @param aCallback
+   *        An instance of nsIIccCallback:
+   *        nsIIccCallback::notifySuccessWithBoolean() if success.
+   *        nsIIccCallback::notifyError(), otherwise.
+   */
+  void matchMvno(in unsigned long aMvnoType,
+                 in DOMString aMvnoData,
+                 in nsIIccCallback aCallback);
+
+  /**
+   * Retrieve the the availability of an icc service.
+   *
+   * @param aService
+   *        One of CARD_SERVICE_* values.
+   * @param aCallback
+   *        An instance of nsIIccCallback:
+   *        nsIIccCallback::notifySuccessWithBoolean() if success.
+   *        nsIIccCallback::notifyError(), otherwise.
+   */
+  void getServiceStateEnabled(in unsigned long aService,
+                              in nsIIccCallback aCallback);
+};
new file mode 100644
--- /dev/null
+++ b/dom/icc/ipc/IccChild.cpp
@@ -0,0 +1,310 @@
+/* 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 "mozilla/dom/icc/IccChild.h"
+#include "IccInfo.h"
+
+using mozilla::dom::IccInfo;
+
+namespace mozilla {
+namespace dom {
+namespace icc {
+
+/**
+ * PIccChild Implementation.
+ */
+
+IccChild::IccChild()
+  : mCardState(nsIIcc::CARD_STATE_UNKNOWN)
+  , mIsAlive(true)
+{
+  MOZ_COUNT_CTOR(IccChild);
+}
+
+IccChild::~IccChild()
+{
+  MOZ_COUNT_DTOR(IccChild);
+}
+
+void
+IccChild::Init()
+{
+  OptionalIccInfoData infoData;
+
+  bool rv = SendInit(&infoData, &mCardState);
+  NS_ENSURE_TRUE_VOID(rv);
+
+  UpdateIccInfo(infoData);
+}
+
+void
+IccChild::Shutdown(){
+  if (mIsAlive) {
+    mIsAlive = false;
+    Send__delete__(this);
+  }
+
+  mListeners.Clear();
+  mIccInfo = nullptr;
+  mCardState = nsIIcc::CARD_STATE_UNKNOWN;
+}
+
+void
+IccChild::ActorDestroy(ActorDestroyReason why)
+{
+  mIsAlive = false;
+}
+
+bool
+IccChild::RecvNotifyCardStateChanged(const uint32_t& aCardState)
+{
+  mCardState = aCardState;
+
+  for (int32_t i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->NotifyCardStateChanged();
+  }
+
+  return true;
+}
+
+bool
+IccChild::RecvNotifyIccInfoChanged(const OptionalIccInfoData& aInfoData)
+{
+  UpdateIccInfo(aInfoData);
+
+  for (int32_t i = 0; i < mListeners.Count(); i++) {
+    mListeners[i]->NotifyIccInfoChanged();
+  }
+
+  return true;
+}
+
+PIccRequestChild*
+IccChild::AllocPIccRequestChild(const IccRequest& aRequest)
+{
+  MOZ_CRASH("Caller is supposed to manually construct a request!");
+}
+
+bool
+IccChild::DeallocPIccRequestChild(PIccRequestChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+bool
+IccChild::SendRequest(const IccRequest& aRequest, nsIIccCallback* aRequestReply)
+{
+  NS_ENSURE_TRUE(mIsAlive, false);
+
+  // Deallocated in IccChild::DeallocPIccRequestChild().
+  IccRequestChild* actor = new IccRequestChild(aRequestReply);
+  SendPIccRequestConstructor(actor, aRequest);
+
+  return true;
+}
+
+void
+IccChild::UpdateIccInfo(const OptionalIccInfoData& aInfoData) {
+  if (aInfoData.type() == OptionalIccInfoData::Tvoid_t) {
+    mIccInfo = nullptr;
+    return;
+  }
+
+  NS_ENSURE_TRUE_VOID(aInfoData.type() == OptionalIccInfoData::TIccInfoData);
+
+  nsRefPtr<IccInfo> iccInfo;
+  const IccInfoData& infoData = aInfoData.get_IccInfoData();
+  if (infoData.iccType().EqualsLiteral("sim")
+      || infoData.iccType().EqualsLiteral("usim")) {
+    iccInfo = new GsmIccInfo(infoData);
+  } else if (infoData.iccType().EqualsLiteral("ruim")
+             || infoData.iccType().EqualsLiteral("csim")){
+    iccInfo = new CdmaIccInfo(infoData);
+  } else {
+    iccInfo = new IccInfo(infoData);
+  }
+
+  // We update the orignal one instead of replacing with a new one
+  // if the IccType is the same.
+  if (mIccInfo) {
+    nsString oldIccType;
+    nsString newIccType;
+    mIccInfo->GetIccType(oldIccType);
+    iccInfo->GetIccType(newIccType);
+
+    if (oldIccType.Equals(newIccType)) {
+      mIccInfo->Update(iccInfo);
+      return;
+    }
+  }
+
+  mIccInfo = iccInfo;
+}
+
+/**
+ * nsIIcc Implementation.
+ */
+
+NS_IMPL_ISUPPORTS(IccChild, nsIIcc)
+
+NS_IMETHODIMP
+IccChild::RegisterListener(nsIIccListener *aListener)
+{
+  NS_ENSURE_TRUE(!mListeners.Contains(aListener), NS_ERROR_UNEXPECTED);
+
+  mListeners.AppendObject(aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+IccChild::UnregisterListener(nsIIccListener *aListener)
+{
+  NS_ENSURE_TRUE(mListeners.Contains(aListener), NS_ERROR_UNEXPECTED);
+
+  mListeners.RemoveObject(aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+IccChild::GetIccInfo(nsIIccInfo** aIccInfo)
+{
+  nsCOMPtr<nsIIccInfo> info(mIccInfo);
+  info.forget(aIccInfo);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+IccChild::GetCardState(uint32_t* aCardState)
+{
+  *aCardState = mCardState;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+IccChild::GetCardLockEnabled(uint32_t aLockType,
+                             nsIIccCallback* aRequestReply)
+{
+  return SendRequest(GetCardLockEnabledRequest(aLockType), aRequestReply)
+    ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+IccChild::UnlockCardLock(uint32_t aLockType,
+                         const nsAString& aPassword,
+                         const nsAString& aNewPin,
+                         nsIIccCallback* aRequestReply)
+{
+  return SendRequest(UnlockCardLockRequest(aLockType,
+                                           nsString(aPassword),
+                                           nsString(aNewPin)),
+                     aRequestReply)
+    ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+IccChild::SetCardLockEnabled(uint32_t aLockType,
+                             const nsAString& aPassword,
+                             bool aEnabled,
+                             nsIIccCallback* aRequestReply)
+{
+  return SendRequest(SetCardLockEnabledRequest(aLockType,
+                                               nsString(aPassword),
+                                               aEnabled),
+                     aRequestReply)
+    ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+IccChild::ChangeCardLockPassword(uint32_t aLockType,
+                                 const nsAString& aPassword,
+                                 const nsAString& aNewPassword,
+                                 nsIIccCallback* aRequestReply)
+{
+  return SendRequest(ChangeCardLockPasswordRequest(aLockType,
+                                                   nsString(aPassword),
+                                                   nsString(aNewPassword)),
+                     aRequestReply)
+    ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+IccChild::GetCardLockRetryCount(uint32_t aLockType,
+                                nsIIccCallback* aRequestReply)
+{
+  return SendRequest(GetCardLockRetryCountRequest(aLockType), aRequestReply)
+    ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+IccChild::MatchMvno(uint32_t aMvnoType,
+                    const nsAString& aMvnoData,
+                    nsIIccCallback* aRequestReply)
+{
+  return SendRequest(MatchMvnoRequest(aMvnoType, nsString(aMvnoData)),
+                     aRequestReply)
+    ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+IccChild::GetServiceStateEnabled(uint32_t aService,
+                                 nsIIccCallback* aRequestReply)
+{
+  return SendRequest(GetServiceStateEnabledRequest(aService),
+                     aRequestReply)
+    ? NS_OK : NS_ERROR_FAILURE;
+}
+
+/**
+ * PIccRequestChild Implementation.
+ */
+
+IccRequestChild::IccRequestChild(nsIIccCallback* aRequestReply)
+  : mRequestReply(aRequestReply)
+{
+  MOZ_COUNT_CTOR(IccRequestChild);
+  MOZ_ASSERT(aRequestReply);
+}
+
+bool
+IccRequestChild::Recv__delete__(const IccReply& aResponse)
+{
+  MOZ_ASSERT(mRequestReply);
+
+  switch(aResponse.type()) {
+    case IccReply::TIccReplySuccess:
+      return NS_SUCCEEDED(mRequestReply->NotifySuccess());
+    case IccReply::TIccReplySuccessWithBoolean: {
+      const IccReplySuccessWithBoolean& resultWithBoolean
+        = aResponse.get_IccReplySuccessWithBoolean();
+      return NS_SUCCEEDED(
+        mRequestReply->NotifySuccessWithBoolean(resultWithBoolean.result()));
+    }
+    case IccReply::TIccReplyCardLockRetryCount: {
+      const IccReplyCardLockRetryCount& retryCount
+        = aResponse.get_IccReplyCardLockRetryCount();
+      return NS_SUCCEEDED(
+        mRequestReply->NotifyGetCardLockRetryCount(retryCount.count()));
+    }
+    case IccReply::TIccReplyError: {
+      const IccReplyError& error = aResponse.get_IccReplyError();
+      return NS_SUCCEEDED(mRequestReply->NotifyError(error.message()));
+    }
+    case IccReply::TIccReplyCardLockError: {
+      const IccReplyCardLockError& error
+        = aResponse.get_IccReplyCardLockError();
+      return NS_SUCCEEDED(
+        mRequestReply->NotifyCardLockError(error.message(),
+                                           error.retryCount()));
+    }
+    default:
+      MOZ_CRASH("Received invalid response type!");
+  }
+
+  return true;
+}
+
+} // namespace icc
+} // namespace dom
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/icc/ipc/IccChild.h
@@ -0,0 +1,86 @@
+/* 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_icc_IccChild_h
+#define mozilla_dom_icc_IccChild_h
+
+#include "mozilla/dom/icc/PIccChild.h"
+#include "mozilla/dom/icc/PIccRequestChild.h"
+#include "nsIIccService.h"
+
+namespace mozilla {
+namespace dom {
+
+class IccInfo;
+
+namespace icc {
+
+class IccChild final : public PIccChild
+                     , public nsIIcc
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIICC
+
+  explicit IccChild();
+
+void
+  Init();
+
+  void
+  Shutdown();
+
+protected:
+  virtual void
+  ActorDestroy(ActorDestroyReason why) override;
+
+  virtual PIccRequestChild*
+  AllocPIccRequestChild(const IccRequest& aRequest) override;
+
+  virtual bool
+  DeallocPIccRequestChild(PIccRequestChild* aActor) override;
+
+  virtual bool
+  RecvNotifyCardStateChanged(const uint32_t& aCardState) override;
+
+  virtual bool
+  RecvNotifyIccInfoChanged(const OptionalIccInfoData& aInfoData) override;
+
+private:
+  ~IccChild();
+
+  void
+  UpdateIccInfo(const OptionalIccInfoData& aInfoData);
+
+  bool
+  SendRequest(const IccRequest& aRequest, nsIIccCallback* aRequestReply);
+
+  nsCOMArray<nsIIccListener> mListeners;
+  nsRefPtr<IccInfo> mIccInfo;
+  uint32_t mCardState;
+  bool mIsAlive;
+};
+
+class IccRequestChild final : public PIccRequestChild
+{
+public:
+  explicit IccRequestChild(nsIIccCallback* aRequestReply);
+
+protected:
+  virtual bool
+  Recv__delete__(const IccReply& aReply) override;
+
+private:
+  virtual ~IccRequestChild() {
+    MOZ_COUNT_DTOR(IccRequestChild);
+  }
+
+  nsCOMPtr<nsIIccCallback> mRequestReply;
+};
+
+} // namespace icc
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_icc_IccChild_h
new file mode 100644
--- /dev/null
+++ b/dom/icc/ipc/IccIPCService.cpp
@@ -0,0 +1,56 @@
+/* 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 "IccIPCService.h"
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/Preferences.h"
+
+namespace mozilla {
+namespace dom {
+namespace icc {
+
+NS_IMPL_ISUPPORTS(IccIPCService, nsIIccService)
+
+IccIPCService::IccIPCService()
+{
+  int32_t numRil = Preferences::GetInt("ril.numRadioInterfaces", 1);
+  mIccs.SetLength(numRil);
+}
+
+IccIPCService::~IccIPCService()
+{
+  uint32_t count = mIccs.Length();
+  for (uint32_t i = 0; i < count; i++) {
+    if (mIccs[i]) {
+      mIccs[i]->Shutdown();
+    }
+  }
+}
+
+NS_IMETHODIMP
+IccIPCService::GetIccByServiceId(uint32_t aServiceId, nsIIcc** aIcc)
+{
+  NS_ENSURE_TRUE(aServiceId < mIccs.Length(), NS_ERROR_INVALID_ARG);
+
+  if (!mIccs[aServiceId]) {
+    nsRefPtr<IccChild> child = new IccChild();
+
+    // |SendPIccConstructor| adds another reference to the child
+    // actor and removes in |DeallocPIccChild|.
+    ContentChild::GetSingleton()->SendPIccConstructor(child, aServiceId);
+    child->Init();
+
+    mIccs[aServiceId] = child;
+  }
+
+  nsCOMPtr<nsIIcc> icc(mIccs[aServiceId]);
+  icc.forget(aIcc);
+
+  return NS_OK;
+}
+
+} // namespace icc
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/icc/ipc/IccIPCService.h
@@ -0,0 +1,35 @@
+/* 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_icc_IccIPCService_h
+#define mozilla_dom_icc_IccIPCService_h
+
+#include "nsCOMPtr.h"
+#include "nsIIccService.h"
+
+namespace mozilla {
+namespace dom {
+namespace icc {
+
+class IccChild;
+
+class IccIPCService final : public nsIIccService
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIICCSERVICE
+
+  IccIPCService();
+
+private:
+  ~IccIPCService();
+
+  nsTArray<nsRefPtr<IccChild>> mIccs;
+};
+
+} // namespace icc
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_icc_IccIPCService_h
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/icc/ipc/IccParent.cpp
@@ -0,0 +1,320 @@
+/* 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 "mozilla/dom/icc/IccParent.h"
+#include "nsIIccService.h"
+#include "IccInfo.h"
+
+using mozilla::dom::IccInfo;
+
+namespace mozilla {
+namespace dom {
+namespace icc {
+
+namespace {
+
+static void
+GetIccInfoDataFromIccInfo(nsIIccInfo* aInInfo, IccInfoData& aOutData) {
+  aInInfo->GetIccType(aOutData.iccType());
+  aInInfo->GetIccid(aOutData.iccid());
+  aInInfo->GetMcc(aOutData.mcc());
+  aInInfo->GetMnc(aOutData.mnc());
+  aInInfo->GetSpn(aOutData.spn());
+  aInInfo->GetIsDisplayNetworkNameRequired(
+    &aOutData.isDisplayNetworkNameRequired());
+  aInInfo->GetIsDisplaySpnRequired(
+    &aOutData.isDisplaySpnRequired());
+
+  nsCOMPtr<nsIGsmIccInfo> gsmIccInfo(do_QueryInterface(aInInfo));
+  if (gsmIccInfo) {
+    gsmIccInfo->GetMsisdn(aOutData.phoneNumber());
+  }
+
+  nsCOMPtr<nsICdmaIccInfo> cdmaIccInfo(do_QueryInterface(aInInfo));
+  if (cdmaIccInfo) {
+    cdmaIccInfo->GetMdn(aOutData.phoneNumber());
+    cdmaIccInfo->GetPrlVersion(&aOutData.prlVersion());
+  }
+}
+
+} // anonymous namespace
+
+/**
+ * PIccParent Implementation.
+ */
+
+IccParent::IccParent(uint32_t aServiceId)
+{
+  MOZ_COUNT_CTOR(IccParent);
+
+  nsCOMPtr<nsIIccService> service =
+    do_GetService(ICC_SERVICE_CONTRACTID);
+
+  NS_ASSERTION(service, "Failed to get IccService!");
+
+  service->GetIccByServiceId(aServiceId, getter_AddRefs(mIcc));
+
+  NS_ASSERTION(mIcc, "Failed to get Icc with specified serviceId.");
+
+  mIcc->RegisterListener(this);
+}
+
+void
+IccParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  if (mIcc) {
+    mIcc->UnregisterListener(this);
+    mIcc = nullptr;
+  }
+}
+
+bool
+IccParent::RecvInit(OptionalIccInfoData* aInfoData,
+                    uint32_t* aCardState)
+{
+  NS_ENSURE_TRUE(mIcc, false);
+
+  nsresult rv = mIcc->GetCardState(aCardState);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  nsCOMPtr<nsIIccInfo> iccInfo;
+  rv = mIcc->GetIccInfo(getter_AddRefs(iccInfo));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  if (iccInfo) {
+    IccInfoData data;
+    GetIccInfoDataFromIccInfo(iccInfo, data);
+    *aInfoData = OptionalIccInfoData(data);
+
+    return true;
+  }
+
+  *aInfoData = OptionalIccInfoData(void_t());
+
+  return true;
+}
+
+PIccRequestParent*
+IccParent::AllocPIccRequestParent(const IccRequest& aRequest)
+{
+  NS_ASSERTION(mIcc, "AllocPIccRequestParent after actor was destroyed!");
+
+  IccRequestParent* actor = new IccRequestParent(mIcc);
+  // Add an extra ref for IPDL. Will be released in
+  // IccParent::DeallocPIccRequestParent().
+  actor->AddRef();
+  return actor;
+}
+
+bool
+IccParent::DeallocPIccRequestParent(PIccRequestParent* aActor)
+{
+  // IccRequestParent is refcounted, must not be freed manually.
+  static_cast<IccRequestParent*>(aActor)->Release();
+  return true;
+}
+
+bool
+IccParent::RecvPIccRequestConstructor(PIccRequestParent* aActor,
+                                      const IccRequest& aRequest)
+{
+  NS_ASSERTION(mIcc, "RecvPIccRequestConstructor after actor was destroyed!");
+
+  IccRequestParent* actor = static_cast<IccRequestParent*>(aActor);
+
+  switch (aRequest.type()) {
+    case IccRequest::TGetCardLockEnabledRequest:
+      return actor->DoRequest(aRequest.get_GetCardLockEnabledRequest());
+    case IccRequest::TUnlockCardLockRequest:
+      return actor->DoRequest(aRequest.get_UnlockCardLockRequest());
+    case IccRequest::TSetCardLockEnabledRequest:
+      return actor->DoRequest(aRequest.get_SetCardLockEnabledRequest());
+    case IccRequest::TChangeCardLockPasswordRequest:
+      return actor->DoRequest(aRequest.get_ChangeCardLockPasswordRequest());
+    case IccRequest::TGetCardLockRetryCountRequest:
+      return actor->DoRequest(aRequest.get_GetCardLockRetryCountRequest());
+    case IccRequest::TMatchMvnoRequest:
+      return actor->DoRequest(aRequest.get_MatchMvnoRequest());
+    case IccRequest::TGetServiceStateEnabledRequest:
+      return actor->DoRequest(aRequest.get_GetServiceStateEnabledRequest());
+    default:
+      MOZ_CRASH("Received invalid request type!");
+  }
+
+  return true;
+}
+
+/**
+ * nsIIccListener Implementation.
+ */
+
+NS_IMPL_ISUPPORTS(IccParent, nsIIccListener)
+
+NS_IMETHODIMP
+IccParent::NotifyStkCommand(const nsAString & aMessage)
+{
+  // Bug 1114938 - [B2G][ICC] Refactor STK in MozIcc.webidl with IPDL.
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+IccParent::NotifyStkSessionEnd()
+{
+  // Bug 1114938 - [B2G][ICC] Refactor STK in MozIcc.webidl with IPDL.
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+IccParent::NotifyCardStateChanged()
+{
+  NS_ENSURE_TRUE(mIcc, NS_ERROR_FAILURE);
+
+  uint32_t cardState;
+  nsresult rv = mIcc->GetCardState(&cardState);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return SendNotifyCardStateChanged(cardState) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+IccParent::NotifyIccInfoChanged()
+{
+  NS_ENSURE_TRUE(mIcc, NS_ERROR_FAILURE);
+
+  nsCOMPtr<nsIIccInfo> iccInfo;
+  nsresult rv = mIcc->GetIccInfo(getter_AddRefs(iccInfo));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!iccInfo) {
+    return SendNotifyIccInfoChanged(OptionalIccInfoData(void_t()))
+      ? NS_OK : NS_ERROR_FAILURE;
+  }
+
+  IccInfoData data;
+  GetIccInfoDataFromIccInfo(iccInfo, data);
+
+  return SendNotifyIccInfoChanged(OptionalIccInfoData(data))
+    ? NS_OK : NS_ERROR_FAILURE;
+}
+
+/**
+ * PIccRequestParent Implementation.
+ */
+
+IccRequestParent::IccRequestParent(nsIIcc* aIcc)
+  : mIcc(aIcc)
+{
+  MOZ_COUNT_CTOR(IccRequestParent);
+}
+
+void
+IccRequestParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mIcc = nullptr;
+}
+
+bool
+IccRequestParent::DoRequest(const GetCardLockEnabledRequest& aRequest)
+{
+  return NS_SUCCEEDED(mIcc->GetCardLockEnabled(aRequest.lockType(),
+                                               this));
+}
+
+bool
+IccRequestParent::DoRequest(const UnlockCardLockRequest& aRequest)
+{
+  return NS_SUCCEEDED(mIcc->UnlockCardLock(aRequest.lockType(),
+                                           aRequest.password(),
+                                           aRequest.newPin(),
+                                           this));
+}
+
+bool
+IccRequestParent::DoRequest(const SetCardLockEnabledRequest& aRequest)
+{
+  return NS_SUCCEEDED(mIcc->SetCardLockEnabled(aRequest.lockType(),
+                                               aRequest.password(),
+                                               aRequest.enabled(),
+                                               this));
+}
+
+bool
+IccRequestParent::DoRequest(const ChangeCardLockPasswordRequest& aRequest)
+{
+  return NS_SUCCEEDED(mIcc->ChangeCardLockPassword(aRequest.lockType(),
+                                                   aRequest.password(),
+                                                   aRequest.newPassword(),
+                                                   this));
+}
+
+bool
+IccRequestParent::DoRequest(const GetCardLockRetryCountRequest& aRequest)
+{
+  return NS_SUCCEEDED(mIcc->GetCardLockRetryCount(aRequest.lockType(),
+                                                  this));
+}
+
+bool
+IccRequestParent::DoRequest(const MatchMvnoRequest& aRequest)
+{
+  return NS_SUCCEEDED(mIcc->MatchMvno(aRequest.mvnoType(),
+                                      aRequest.mvnoData(),
+                                      this));
+}
+
+bool
+IccRequestParent::DoRequest(const GetServiceStateEnabledRequest& aRequest)
+{
+  return NS_SUCCEEDED(mIcc->GetServiceStateEnabled(aRequest.service(),
+                                                   this));
+}
+
+nsresult
+IccRequestParent::SendReply(const IccReply& aReply)
+{
+  NS_ENSURE_TRUE(mIcc, NS_ERROR_FAILURE);
+
+  return Send__delete__(this, aReply) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+/**
+ * nsIIccCallback Implementation.
+ */
+
+NS_IMPL_ISUPPORTS(IccRequestParent, nsIIccCallback)
+
+NS_IMETHODIMP
+IccRequestParent::NotifySuccess()
+{
+  return SendReply(IccReplySuccess());
+}
+
+NS_IMETHODIMP
+IccRequestParent::NotifySuccessWithBoolean(bool aResult)
+{
+  return SendReply(IccReplySuccessWithBoolean(aResult));
+}
+
+NS_IMETHODIMP
+IccRequestParent::NotifyGetCardLockRetryCount(int32_t aCount)
+{
+  return SendReply(IccReplyCardLockRetryCount(aCount));
+}
+
+NS_IMETHODIMP
+IccRequestParent::NotifyError(const nsAString & aErrorMsg)
+{
+  return SendReply(IccReplyError(nsString(aErrorMsg)));
+}
+
+NS_IMETHODIMP
+IccRequestParent::NotifyCardLockError(const nsAString & aErrorMsg,
+                                      int32_t aRetryCount)
+{
+  return SendReply(IccReplyCardLockError(aRetryCount, nsString(aErrorMsg)));
+}
+
+} // namespace icc
+} // namespace dom
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/icc/ipc/IccParent.h
@@ -0,0 +1,107 @@
+/* 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_icc_IccParent_h
+#define mozilla_dom_icc_IccParent_h
+
+#include "mozilla/dom/icc/PIccParent.h"
+#include "mozilla/dom/icc/PIccRequestParent.h"
+#include "nsIIccService.h"
+
+namespace mozilla {
+namespace dom {
+namespace icc {
+
+class IccParent final : public PIccParent
+                      , public nsIIccListener
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIICCLISTENER
+
+  explicit IccParent(uint32_t aServiceId);
+
+protected:
+  virtual
+  ~IccParent()
+  {
+    MOZ_COUNT_DTOR(IccParent);
+  }
+
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual bool
+  RecvInit(
+          OptionalIccInfoData* aInfoData,
+          uint32_t* aCardState) override;
+
+  virtual PIccRequestParent*
+  AllocPIccRequestParent(const IccRequest& aRequest) override;
+
+  virtual bool
+  DeallocPIccRequestParent(PIccRequestParent* aActor) override;
+
+  virtual bool
+  RecvPIccRequestConstructor(PIccRequestParent* aActor,
+                             const IccRequest& aRequest) override;
+
+private:
+  IccParent();
+  nsCOMPtr<nsIIcc> mIcc;
+};
+
+class IccRequestParent final : public PIccRequestParent
+                             , public nsIIccCallback
+{
+  friend class IccParent;
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIICCCALLBACK
+
+  explicit IccRequestParent(nsIIcc* icc);
+
+protected:
+  virtual void
+  ActorDestroy(ActorDestroyReason why) override;
+
+private:
+  ~IccRequestParent()
+  {
+    MOZ_COUNT_DTOR(IccRequestParent);
+  }
+
+  bool
+  DoRequest(const GetCardLockEnabledRequest& aRequest);
+
+  bool
+  DoRequest(const UnlockCardLockRequest& aRequest);
+
+  bool
+  DoRequest(const SetCardLockEnabledRequest& aRequest);
+
+  bool
+  DoRequest(const ChangeCardLockPasswordRequest& aRequest);
+
+  bool
+  DoRequest(const GetCardLockRetryCountRequest& aRequest);
+
+  bool
+  DoRequest(const MatchMvnoRequest& aRequest);
+
+  bool
+  DoRequest(const GetServiceStateEnabledRequest& aRequest);
+
+  nsresult
+  SendReply(const IccReply& aReply);
+
+  nsCOMPtr<nsIIcc> mIcc;
+};
+
+} // namespace icc
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_icc_IccParent_h
new file mode 100644
--- /dev/null
+++ b/dom/icc/ipc/PIcc.ipdl
@@ -0,0 +1,111 @@
+/* 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 protocol PContent;
+include protocol PIccRequest;
+include PIccTypes;
+
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace dom {
+namespace icc {
+
+union OptionalIccInfoData
+{
+  void_t;
+  IccInfoData;
+};
+
+struct GetCardLockEnabledRequest
+{
+  uint32_t lockType;
+};
+
+struct UnlockCardLockRequest
+{
+  uint32_t lockType;
+  nsString password;
+  nsString newPin;
+};
+
+struct SetCardLockEnabledRequest
+{
+  uint32_t lockType;
+  nsString password;
+  bool enabled;
+};
+
+struct ChangeCardLockPasswordRequest
+{
+  uint32_t lockType;
+  nsString password;
+  nsString newPassword;
+};
+
+struct GetCardLockRetryCountRequest
+{
+  uint32_t lockType;
+};
+
+struct MatchMvnoRequest
+{
+  uint32_t mvnoType;
+  nsString mvnoData;
+};
+
+struct GetServiceStateEnabledRequest
+{
+  uint32_t service;
+};
+
+union IccRequest
+{
+  GetCardLockEnabledRequest;
+  UnlockCardLockRequest;
+  SetCardLockEnabledRequest;
+  ChangeCardLockPasswordRequest;
+  GetCardLockRetryCountRequest;
+  MatchMvnoRequest;
+  GetServiceStateEnabledRequest;
+};
+
+sync protocol PIcc
+{
+  manager PContent;
+  manages PIccRequest;
+
+child:
+  /**
+   * Notify CardStateChanged with updated CardState.
+   */
+  NotifyCardStateChanged(uint32_t aCardState);
+
+  /**
+   * Notify IccInfoChanged with updated IccInfo.
+   */
+  NotifyIccInfoChanged(OptionalIccInfoData aInfoData);
+
+parent:
+  /**
+   * Sent when the child no longer needs to use PIcc.
+   */
+  __delete__();
+
+  /**
+   * Sent when the child makes an asynchronous request to the parent.
+   */
+  PIccRequest(IccRequest aRequest);
+
+  /**
+   * Sync call to initialize the updated IccInfo/CardState.
+   */
+  sync Init()
+     returns (OptionalIccInfoData aInfoData, uint32_t aCardState);
+
+};
+
+} // namespace icc
+} // namespace dom
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/icc/ipc/PIccRequest.ipdl
@@ -0,0 +1,61 @@
+/* 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 protocol PIcc;
+include PIccTypes;
+
+namespace mozilla {
+namespace dom {
+namespace icc {
+
+struct IccReplySuccess
+{
+};
+
+struct IccReplySuccessWithBoolean
+{
+  bool result;
+};
+
+struct IccReplyCardLockRetryCount
+{
+  int32_t count;
+};
+
+struct IccReplyError
+{
+  nsString message;
+};
+
+struct IccReplyCardLockError
+{
+  int32_t retryCount;
+  nsString message;
+};
+
+union IccReply
+{
+  // Success
+  IccReplySuccess;
+  IccReplySuccessWithBoolean;
+  IccReplyCardLockRetryCount;
+  // Error
+  IccReplyError;
+  IccReplyCardLockError;
+};
+
+protocol PIccRequest
+{
+  manager PIcc;
+
+child:
+  /**
+   * Sent when the asynchronous request has completed.
+   */
+  __delete__(IccReply response);
+};
+
+} // namespace icc
+} // namespace dom
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/icc/ipc/PIccTypes.ipdlh
@@ -0,0 +1,24 @@
+/* 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/. */
+
+namespace mozilla {
+namespace dom {
+namespace icc {
+
+struct IccInfoData
+{
+  nsString  iccType;
+  nsString  iccid;
+  nsString  mcc;
+  nsString  mnc;
+  nsString  spn;
+  bool      isDisplayNetworkNameRequired;
+  bool      isDisplaySpnRequired;
+  nsString  phoneNumber;
+  int32_t   prlVersion;
+};
+
+} // namespace icc
+} // namespace dom
+} // namespace mozilla
\ No newline at end of file
--- a/dom/icc/moz.build
+++ b/dom/icc/moz.build
@@ -8,29 +8,48 @@ DIRS += ['interfaces']
 
 EXPORTS.mozilla.dom += [
     'Icc.h',
     'IccCardLockError.h',
     'IccInfo.h',
     'IccManager.h',
 ]
 
+EXPORTS.mozilla.dom.icc += [
+    'ipc/IccChild.h',
+    'ipc/IccParent.h',
+]
+
 UNIFIED_SOURCES += [
     'Assertions.cpp',
     'Icc.cpp',
+    'IccCallback.cpp',
     'IccCardLockError.cpp',
     "IccInfo.cpp",
     'IccListener.cpp',
     'IccManager.cpp',
+    'ipc/IccChild.cpp',
+    'ipc/IccIPCService.cpp',
+    'ipc/IccParent.cpp',
+]
+
+IPDL_SOURCES += [
+    'ipc/PIcc.ipdl',
+    'ipc/PIccRequest.ipdl',
+    'ipc/PIccTypes.ipdlh',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']:
     EXTRA_JS_MODULES += [
         'gonk/StkProactiveCmdFactory.jsm',
     ]
+    EXTRA_COMPONENTS += [
+        'gonk/IccService.js',
+        'gonk/IccService.manifest',
+    ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -145,16 +145,17 @@
 
 #ifdef MOZ_NUWA_PROCESS
 #include <setjmp.h>
 #include "ipc/Nuwa.h"
 #endif
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/cellbroadcast/CellBroadcastIPCService.h"
+#include "mozilla/dom/icc/IccChild.h"
 #include "mozilla/dom/mobileconnection/MobileConnectionChild.h"
 #include "mozilla/dom/mobilemessage/SmsChild.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
 #include "mozilla/dom/PFileSystemRequestChild.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/bluetooth/PBluetoothChild.h"
 #include "mozilla/dom/PFMRadioChild.h"
 #include "mozilla/ipc/InputStreamUtils.h"
@@ -178,16 +179,17 @@
 #include "mozilla/net/NeckoMessageUtils.h"
 #include "mozilla/RemoteSpellCheckEngineChild.h"
 
 using namespace mozilla;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::cellbroadcast;
 using namespace mozilla::dom::devicestorage;
+using namespace mozilla::dom::icc;
 using namespace mozilla::dom::ipc;
 using namespace mozilla::dom::mobileconnection;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::dom::voicemail;
 using namespace mozilla::embedding;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
@@ -1462,16 +1464,41 @@ ContentChild::AllocPHalChild()
 
 bool
 ContentChild::DeallocPHalChild(PHalChild* aHal)
 {
     delete aHal;
     return true;
 }
 
+PIccChild*
+ContentChild::SendPIccConstructor(PIccChild* aActor,
+                                  const uint32_t& aServiceId)
+{
+    // Add an extra ref for IPDL. Will be released in
+    // ContentChild::DeallocPIccChild().
+    static_cast<IccChild*>(aActor)->AddRef();
+    return PContentChild::SendPIccConstructor(aActor, aServiceId);
+}
+
+PIccChild*
+ContentChild::AllocPIccChild(const uint32_t& aServiceId)
+{
+    NS_NOTREACHED("No one should be allocating PIccChild actors");
+    return nullptr;
+}
+
+bool
+ContentChild::DeallocPIccChild(PIccChild* aActor)
+{
+    // IccChild is refcounted, must not be freed manually.
+    static_cast<IccChild*>(aActor)->Release();
+    return true;
+}
+
 asmjscache::PAsmJSCacheEntryChild*
 ContentChild::AllocPAsmJSCacheEntryChild(
                                     const asmjscache::OpenMode& aOpenMode,
                                     const asmjscache::WriteParams& aWriteParams,
                                     const IPC::Principal& aPrincipal)
 {
     NS_NOTREACHED("Should never get here!");
     return nullptr;
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -168,16 +168,23 @@ public:
     AllocPCrashReporterChild(const mozilla::dom::NativeThreadId& id,
                              const uint32_t& processType) override;
     virtual bool
     DeallocPCrashReporterChild(PCrashReporterChild*) override;
 
     virtual PHalChild* AllocPHalChild() override;
     virtual bool DeallocPHalChild(PHalChild*) override;
 
+    PIccChild*
+    SendPIccConstructor(PIccChild* aActor, const uint32_t& aServiceId);
+    virtual PIccChild*
+    AllocPIccChild(const uint32_t& aClientId) override;
+    virtual bool
+    DeallocPIccChild(PIccChild* aActor) override;
+
     virtual PMemoryReportRequestChild*
     AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
                                    const bool& aAnonymize,
                                    const bool& aMinimizeMemoryUsage,
                                    const MaybeFileDesc& aDMDFile) override;
     virtual bool
     DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor) override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -46,16 +46,17 @@
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
 #include "mozilla/dom/PFMRadioParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
 #include "mozilla/dom/cellbroadcast/CellBroadcastParent.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
+#include "mozilla/dom/icc/IccParent.h"
 #include "mozilla/dom/mobileconnection/MobileConnectionParent.h"
 #include "mozilla/dom/mobilemessage/SmsParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/telephony/TelephonyParent.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
 #include "mozilla/dom/voicemail/VoicemailParent.h"
 #include "mozilla/embedding/printingui/PrintingParent.h"
@@ -219,16 +220,17 @@ using base::ChildPrivileges;
 using base::KillProcess;
 
 #ifdef MOZ_CRASHREPORTER
 using namespace CrashReporter;
 #endif
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::cellbroadcast;
 using namespace mozilla::dom::devicestorage;
+using namespace mozilla::dom::icc;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::dom::power;
 using namespace mozilla::dom::mobileconnection;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::dom::voicemail;
 using namespace mozilla::embedding;
 using namespace mozilla::hal;
@@ -3382,16 +3384,37 @@ ContentParent::AllocPHalParent()
 
 bool
 ContentParent::DeallocPHalParent(hal_sandbox::PHalParent* aHal)
 {
     delete aHal;
     return true;
 }
 
+PIccParent*
+ContentParent::AllocPIccParent(const uint32_t& aServiceId)
+{
+    if (!AssertAppProcessPermission(this, "mobileconnection")) {
+        return nullptr;
+    }
+    IccParent* parent = new IccParent(aServiceId);
+    // We release this ref in DeallocPIccParent().
+    parent->AddRef();
+
+    return parent;
+}
+
+bool
+ContentParent::DeallocPIccParent(PIccParent* aActor)
+{
+    // IccParent is refcounted, must not be freed manually.
+    static_cast<IccParent*>(aActor)->Release();
+    return true;
+}
+
 PMemoryReportRequestParent*
 ContentParent::AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
                                                const bool &aAnonymize,
                                                const bool &aMinimizeMemoryUsage,
                                                const MaybeFileDesc &aDMDFile)
 {
     MemoryReportRequestParent* parent = new MemoryReportRequestParent();
     return parent;
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -545,16 +545,19 @@ private:
     virtual bool RecvGetRandomValues(const uint32_t& length,
                                      InfallibleTArray<uint8_t>* randomValues) override;
 
     virtual bool RecvIsSecureURI(const uint32_t& aType, const URIParams& aURI,
                                  const uint32_t& aFlags, bool* aIsSecureURI) override;
 
     virtual bool DeallocPHalParent(PHalParent*) override;
 
+    virtual PIccParent* AllocPIccParent(const uint32_t& aServiceId) override;
+    virtual bool DeallocPIccParent(PIccParent* aActor) override;
+
     virtual PMemoryReportRequestParent*
     AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
                                     const bool &aAnonymize,
                                     const bool &aMinimizeMemoryUsage,
                                     const MaybeFileDesc &aDMDFile) override;
     virtual bool DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor) override;
 
     virtual PCycleCollectWithLogsParent*
@@ -738,17 +741,17 @@ private:
     virtual bool RecvSetFakeVolumeState(const nsString& fsName, const int32_t& fsState) override;
 
     virtual bool RecvKeywordToURI(const nsCString& aKeyword,
                                   nsString* aProviderName,
                                   OptionalInputStreamParams* aPostData,
                                   OptionalURIParams* aURI) override;
 
     virtual bool RecvNotifyKeywordSearchLoading(const nsString &aProvider,
-                                                const nsString &aKeyword) override; 
+                                                const nsString &aKeyword) override;
 
     virtual void ProcessingError(Result aCode, const char* aMsgName) override;
 
     virtual bool RecvAllocateLayerTreeId(uint64_t* aId) override;
     virtual bool RecvDeallocateLayerTreeId(const uint64_t& aId) override;
 
     virtual bool RecvGetGraphicsFeatureStatus(const int32_t& aFeature,
                                               int32_t* aStatus,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -16,16 +16,17 @@ include protocol PCycleCollectWithLogs;
 include protocol PCrashReporter;
 include protocol PDocAccessible;
 include protocol PExternalHelperApp;
 include protocol PDeviceStorageRequest;
 include protocol PFileDescriptorSet;
 include protocol PFMRadio;
 include protocol PFileSystemRequest;
 include protocol PHal;
+include protocol PIcc;
 include protocol PProcessHangMonitor;
 include protocol PImageBridge;
 include protocol PMemoryReportRequest;
 include protocol PMobileConnection;
 include protocol PNecko;
 include protocol PPluginModule;
 include protocol PPrinting;
 include protocol POfflineCacheUpdate;
@@ -374,16 +375,17 @@ prio(normal upto urgent) sync protocol P
     manages PCycleCollectWithLogs;
     manages PDocAccessible;
     manages PDeviceStorageRequest;
     manages PFileSystemRequest;
     manages PExternalHelperApp;
     manages PFileDescriptorSet;
     manages PFMRadio;
     manages PHal;
+    manages PIcc;
     manages PMemoryReportRequest;
     manages PMobileConnection;
     manages PNecko;
     manages POfflineCacheUpdate;
     manages PPrinting;
     manages PScreenManager;
     manages PSms;
     manages PSpeechSynthesis;
@@ -605,18 +607,17 @@ parent:
                             TabId openerTabId)
         returns (ContentParentId cpId, bool isForApp, bool isForBrowser, TabId tabId);
     sync BridgeToChildProcess(ContentParentId cpId);
 
     /**
      * This call connects the content process to a plugin process. While this
      * call runs, a new PluginModuleParent will be created in the ContentChild
      * via bridging. The corresponding PluginModuleChild will live in the plugin
-     * process. We use intr semantics here to ensure that the PluginModuleParent
-     * allocation message is dispatched before LoadPlugin returns.
+     * process.
      */
     sync LoadPlugin(uint32_t pluginId) returns (nsresult rv);
 
     /**
      * This call is used by asynchronous plugin instantiation to notify the
      * content parent that it is now safe to initiate the plugin bridge for
      * the specified plugin id. When this call returns, the requested bridge
      * connection has been made.
@@ -651,16 +652,18 @@ parent:
 
     async GetSystemMemory(uint64_t getterId);
 
     sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags)
         returns (bool isSecureURI);
 
     PHal();
 
+    PIcc(uint32_t serviceId);
+
     PMobileConnection(uint32_t clientId);
 
     PNecko();
 
     PPrinting();
 
     prio(high) sync PScreenManager()
         returns (uint32_t numberOfScreens,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -272,16 +272,17 @@ TabParent::TabParent(nsIContentParent* a
   , mMarkedDestroying(false)
   , mIsDestroyed(false)
   , mAppPackageFileDescriptorSent(false)
   , mSendOfflineStatus(true)
   , mChromeFlags(aChromeFlags)
   , mInitedByParent(false)
   , mTabId(aTabId)
   , mCreatingWindow(false)
+  , mNeedLayerTreeReadyNotification(false)
 {
   MOZ_ASSERT(aManager);
 }
 
 TabParent::~TabParent()
 {
 }
 
@@ -2384,16 +2385,22 @@ TabParent::DeallocPRenderFrameParent(PRe
 bool
 TabParent::RecvGetRenderFrameInfo(PRenderFrameParent* aRenderFrame,
                                   TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                   uint64_t* aLayersId)
 {
   RenderFrameParent* renderFrame = static_cast<RenderFrameParent*>(aRenderFrame);
   renderFrame->GetTextureFactoryIdentifier(aTextureFactoryIdentifier);
   *aLayersId = renderFrame->GetLayersId();
+
+  if (mNeedLayerTreeReadyNotification) {
+    RequestNotifyLayerTreeReady();
+    mNeedLayerTreeReadyNotification = false;
+  }
+
   return true;
 }
 
 bool
 TabParent::AllowContentIME()
 {
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
   NS_ENSURE_TRUE(fm, false);
@@ -2686,21 +2693,21 @@ class LayerTreeUpdateObserver : public C
   }
 };
 
 bool
 TabParent::RequestNotifyLayerTreeReady()
 {
   RenderFrameParent* frame = GetRenderFrame();
   if (!frame) {
-    return false;
+    mNeedLayerTreeReadyNotification = true;
+  } else {
+    CompositorParent::RequestNotifyLayerTreeReady(frame->GetLayersId(),
+                                                  new LayerTreeUpdateObserver());
   }
-
-  CompositorParent::RequestNotifyLayerTreeReady(frame->GetLayersId(),
-                                                new LayerTreeUpdateObserver());
   return true;
 }
 
 bool
 TabParent::RequestNotifyLayerTreeCleared()
 {
   RenderFrameParent* frame = GetRenderFrame();
   if (!frame) {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -515,16 +515,21 @@ private:
     // frame scripts for that tab are loaded before any scripts start to run in
     // the window. We can't load the frame scripts the normal way, using
     // separate IPC messages, since they won't be processed by the child until
     // returning to the event loop, which is too late. Instead, we queue up
     // frame scripts that we intend to load and send them as part of the
     // CreateWindow response. Then TabChild loads them immediately.
     nsTArray<FrameScriptInfo> mDelayedFrameScripts;
 
+    // If the user called RequestNotifyLayerTreeReady and the RenderFrameParent
+    // wasn't ready yet, we set this flag and call RequestNotifyLayerTreeReady
+    // again once the RenderFrameParent arrives.
+    bool mNeedLayerTreeReadyNotification;
+
 private:
     // This is used when APZ needs to find the TabParent associated with a layer
     // to dispatch events.
     typedef nsDataHashtable<nsUint64HashKey, TabParent*> LayerToTabParentTable;
     static LayerToTabParentTable* sLayerToTabParentTable;
 
     static void AddTabParentToTable(uint64_t aLayersId, TabParent* aTabParent);
     static void RemoveTabParentFromTable(uint64_t aLayersId);
--- a/dom/mobileconnection/MobileConnection.cpp
+++ b/dom/mobileconnection/MobileConnection.cpp
@@ -10,27 +10,24 @@
 #include "mozilla/dom/DataErrorEvent.h"
 #include "mozilla/dom/MozClirModeEvent.h"
 #include "mozilla/dom/MozEmergencyCbModeEvent.h"
 #include "mozilla/dom/MozOtaStatusEvent.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "nsIDOMDOMRequest.h"
+#include "nsIIccInfo.h"
 #include "nsIPermissionManager.h"
 #include "nsIVariant.h"
 #include "nsJSON.h"
 #include "nsJSUtils.h"
 #include "nsRadioInterfaceLayer.h"
 #include "nsServiceManagerUtils.h"
 
-#ifdef MOZ_B2G_RIL
-#include "nsIIccInfo.h"
-#endif // MOZ_B2G_RIL
-
 #define MOBILECONN_ERROR_INVALID_PARAMETER NS_LITERAL_STRING("InvalidParameter")
 #define MOBILECONN_ERROR_INVALID_PASSWORD  NS_LITERAL_STRING("InvalidPassword")
 
 #ifdef CONVERT_STRING_TO_NULLABLE_ENUM
 #undef CONVERT_STRING_TO_NULLABLE_ENUM
 #endif
 #define CONVERT_STRING_TO_NULLABLE_ENUM(_string, _enumType, _enum)      \
 {                                                                       \
@@ -44,28 +41,24 @@
   }                                                                     \
 }
 
 using mozilla::ErrorResult;
 using namespace mozilla::dom;
 using namespace mozilla::dom::mobileconnection;
 
 class MobileConnection::Listener final : public nsIMobileConnectionListener
-#ifdef MOZ_B2G_RIL
-                                           , public nsIIccListener
-#endif // MOZ_B2G_RIL
+                                       , public nsIIccListener
 {
   MobileConnection* mMobileConnection;
 
 public:
   NS_DECL_ISUPPORTS
   NS_FORWARD_SAFE_NSIMOBILECONNECTIONLISTENER(mMobileConnection)
-#ifdef MOZ_B2G_RIL
   NS_FORWARD_SAFE_NSIICCLISTENER(mMobileConnection)
-#endif // MOZ_B2G_RIL
 
   explicit Listener(MobileConnection* aMobileConnection)
     : mMobileConnection(aMobileConnection)
   {
     MOZ_ASSERT(mMobileConnection);
   }
 
   void Disconnect()
@@ -76,40 +69,38 @@ public:
 
 private:
   ~Listener()
   {
     MOZ_ASSERT(!mMobileConnection);
   }
 };
 
-#ifdef MOZ_B2G_RIL
 NS_IMPL_ISUPPORTS(MobileConnection::Listener, nsIMobileConnectionListener,
                   nsIIccListener)
-#else
-NS_IMPL_ISUPPORTS(MobileConnection::Listener, nsIMobileConnectionListener)
-#endif // MOZ_B2G_RIL
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MobileConnection)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MobileConnection,
                                                   DOMEventTargetHelper)
   // Don't traverse mListener because it doesn't keep any reference to
   // MobileConnection but a raw pointer instead. Neither does mMobileConnection
   // because it's an xpcom service owned object and is only released at shutting
   // down.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVoice)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIccHandler)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MobileConnection,
                                                 DOMEventTargetHelper)
   tmp->Shutdown();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVoice)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mData)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIccHandler)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MobileConnection)
   // MobileConnection does not expose nsIMobileConnectionListener. mListener is
   // the exposed nsIMobileConnectionListener and forwards the calls it receives
   // to us.
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
@@ -131,61 +122,62 @@ MobileConnection::MobileConnection(nsPID
   // for it explicitly below.
   if (!service) {
     NS_WARNING("Could not acquire nsIMobileConnectionService!");
     return;
   }
 
   nsresult rv = service->GetItemByServiceId(mClientId,
                                             getter_AddRefs(mMobileConnection));
-#ifdef MOZ_B2G_RIL
-  mIcc = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
 
-  if (NS_FAILED(rv) || !mMobileConnection || !mIcc) {
-    NS_WARNING("Could not acquire nsIMobileConnection or nsIIccProvider!");
-#else
   if (NS_FAILED(rv) || !mMobileConnection) {
     NS_WARNING("Could not acquire nsIMobileConnection!");
-#endif // MOZ_B2G_RIL
     return;
   }
 
   mListener = new Listener(this);
   mVoice = new MobileConnectionInfo(GetOwner());
   mData = new MobileConnectionInfo(GetOwner());
 
   if (CheckPermission("mobileconnection")) {
     DebugOnly<nsresult> rv = mMobileConnection->RegisterListener(mListener);
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                      "Failed registering mobile connection messages with service");
     UpdateVoice();
     UpdateData();
 
-#ifdef MOZ_B2G_RIL
-    rv = mIcc->RegisterIccMsg(mClientId, mListener);
+    nsCOMPtr<nsIIccService> iccService = do_GetService(ICC_SERVICE_CONTRACTID);
+
+    if (iccService) {
+      iccService->GetIccByServiceId(mClientId, getter_AddRefs(mIccHandler));
+    }
+
+    if (!mIccHandler) {
+      NS_WARNING("Could not acquire nsIMobileConnection or nsIIcc!");
+      return;
+    }
+
+    rv = mIccHandler->RegisterListener(mListener);
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                      "Failed registering icc messages with service");
     UpdateIccId();
-#endif // MOZ_B2G_RIL
   }
 }
 
 void
 MobileConnection::Shutdown()
 {
   if (mListener) {
     if (mMobileConnection) {
       mMobileConnection->UnregisterListener(mListener);
     }
 
-#ifdef MOZ_B2G_RIL
-    if (mIcc) {
-      mIcc->UnregisterIccMsg(mClientId, mListener);
+    if (mIccHandler) {
+      mIccHandler->UnregisterListener(mListener);
     }
-#endif // MOZ_B2G_RIL
 
     mListener->Disconnect();
     mListener = nullptr;
   }
 }
 
 MobileConnection::~MobileConnection()
 {
@@ -241,32 +233,30 @@ MobileConnection::UpdateData()
   nsCOMPtr<nsIMobileConnectionInfo> info;
   mMobileConnection->GetData(getter_AddRefs(info));
   mData->Update(info);
 }
 
 bool
 MobileConnection::UpdateIccId()
 {
-#ifdef MOZ_B2G_RIL
   nsAutoString iccId;
   nsCOMPtr<nsIIccInfo> iccInfo;
-  if (mIcc &&
-      NS_SUCCEEDED(mIcc->GetIccInfo(mClientId, getter_AddRefs(iccInfo))) &&
+  if (mIccHandler &&
+      NS_SUCCEEDED(mIccHandler->GetIccInfo(getter_AddRefs(iccInfo))) &&
       iccInfo) {
     iccInfo->GetIccid(iccId);
   } else {
     iccId.SetIsVoid(true);
   }
 
   if (!mIccId.Equals(iccId)) {
     mIccId = iccId;
     return true;
   }
-#endif // MOZ_B2G_RIL
 
   return false;
 }
 
 nsresult
 MobileConnection::NotifyError(nsIDOMDOMRequest* aRequest, const nsAString& aMessage)
 {
   nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
@@ -1129,17 +1119,16 @@ MobileConnection::NotifyLastKnownHomeNet
 }
 
 NS_IMETHODIMP
 MobileConnection::NotifyNetworkSelectionModeChanged()
 {
   return NS_OK;
 }
 
-#ifdef MOZ_B2G_RIL
 // nsIIccListener
 
 NS_IMETHODIMP
 MobileConnection::NotifyStkCommand(const nsAString& aMessage)
 {
   return NS_OK;
 }
 
@@ -1166,9 +1155,8 @@ MobileConnection::NotifyIccInfoChanged()
     return NS_OK;
   }
 
   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
     new AsyncEventDispatcher(this, NS_LITERAL_STRING("iccchange"), false);
 
   return asyncDispatcher->PostDOMEvent();
 }
-#endif // MOZ_B2G_RIL
--- a/dom/mobileconnection/MobileConnection.h
+++ b/dom/mobileconnection/MobileConnection.h
@@ -6,48 +6,41 @@
 #define mozilla_dom_MobileConnection_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/DOMRequest.h"
 #include "mozilla/dom/MobileConnectionInfo.h"
 #include "mozilla/dom/MobileNetworkInfo.h"
 #include "mozilla/dom/MozMobileConnectionBinding.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsIIccService.h"
 #include "nsIMobileConnectionService.h"
 #include "nsWeakPtr.h"
 
-#ifdef MOZ_B2G_RIL
-#include "nsIIccProvider.h"
-#endif // MOZ_B2G_RIL
-
 namespace mozilla {
 namespace dom {
 
 class MobileConnection final : public DOMEventTargetHelper
-                                 , private nsIMobileConnectionListener
-#ifdef MOZ_B2G_RIL
-                                 , private nsIIccListener
-#endif // MOZ_B2G_RIL
+                             , private nsIMobileConnectionListener
+                             , private nsIIccListener
 {
   /**
    * Class MobileConnection doesn't actually expose
    * nsIMobileConnectionListener. Instead, it owns an
    * nsIMobileConnectionListener derived instance mListener and passes it to
    * nsIMobileConnectionService. The onreceived events are first delivered to
    * mListener and then forwarded to its owner, MobileConnection. See also bug
    * 775997 comment #51.
    */
   class Listener;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIMOBILECONNECTIONLISTENER
-#ifdef MOZ_B2G_RIL
   NS_DECL_NSIICCLISTENER
-#endif // MOZ_B2G_RIL
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MobileConnection,
                                            DOMEventTargetHelper)
 
   MobileConnection(nsPIDOMWindow *aWindow, uint32_t aClientId);
 
   void
   Shutdown();
@@ -164,19 +157,17 @@ public:
 
 private:
   ~MobileConnection();
 
 private:
   uint32_t mClientId;
   nsString mIccId;
   nsCOMPtr<nsIMobileConnection> mMobileConnection;
-#ifdef MOZ_B2G_RIL
-  nsCOMPtr<nsIIccProvider> mIcc;
-#endif // MOZ_B2G_RIL
+  nsCOMPtr<nsIIcc> mIccHandler;
   nsRefPtr<Listener> mListener;
   nsRefPtr<MobileConnectionInfo> mVoice;
   nsRefPtr<MobileConnectionInfo> mData;
 
   bool
   CheckPermission(const char* aType) const;
 
   void
--- a/dom/mobilemessage/gonk/MmsService.js
+++ b/dom/mobilemessage/gonk/MmsService.js
@@ -401,17 +401,17 @@ MmsConnection.prototype = {
     if (!this.connected) {
       this.pendingCallbacks.push(callback);
 
       let errorStatus;
       if (getRadioDisabledState()) {
         if (DEBUG) debug("Error! Radio is disabled when sending MMS.");
         errorStatus = _HTTP_STATUS_RADIO_DISABLED;
       } else if (this.radioInterface.rilContext.cardState !=
-                 Ci.nsIIccProvider.CARD_STATE_READY) {
+                 Ci.nsIIcc.CARD_STATE_READY) {
         if (DEBUG) debug("Error! SIM card is not ready when sending MMS.");
         errorStatus = _HTTP_STATUS_NO_SIM_CARD;
       }
       if (errorStatus != null) {
         this.flushPendingCallbacks(errorStatus);
         return true;
       }
 
--- a/dom/mobilemessage/gonk/SmsService.js
+++ b/dom/mobilemessage/gonk/SmsService.js
@@ -881,17 +881,17 @@ SmsService.prototype = {
       if (!gPhoneNumberUtils.isPlainPhoneNumber(options.number)) {
         if (DEBUG) debug("Error! Address is invalid when sending SMS: " + options.number);
         errorCode = Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR;
       } else if (radioState == Ci.nsIMobileConnection.MOBILE_RADIO_STATE_UNKNOWN ||
                  radioState == Ci.nsIMobileConnection.MOBILE_RADIO_STATE_DISABLED) {
         if (DEBUG) debug("Error! Radio is disabled when sending SMS.");
         errorCode = Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR;
       } else if (gRadioInterfaces[aServiceId].rilContext.cardState !=
-                 Ci.nsIIccProvider.CARD_STATE_READY) {
+                 Ci.nsIIcc.CARD_STATE_READY) {
         if (DEBUG) debug("Error! SIM card is not ready when sending SMS.");
         errorCode = Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR;
       }
       if (errorCode) {
         if (aSilent) {
           aRequest.notifySendMessageFailed(errorCode, aSendingMessage);
           return;
         }
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -59,16 +59,17 @@ DIRS += [
     'encoding',
     'events',
     'fetch',
     'filehandle',
     'filesystem',
     'fmradio',
     'geolocation',
     'html',
+    'icc',
     'json',
     'jsurl',
     'asmjscache',
     'mathml',
     'media',
     'messages',
     'mobileconnection',
     'notification',
@@ -117,17 +118,16 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
     DIRS += [
         'speakermanager',
         'tethering',
         'wifi',
     ]
 
 if CONFIG['MOZ_B2G_RIL']:
     DIRS += [
-        'icc',
         'wappush',
     ]
 
 if CONFIG['MOZ_PAY']:
     DIRS += ['payment']
 
 if CONFIG['MOZ_GAMEPAD']:
     DIRS += ['gamepad']
--- a/dom/phonenumberutils/PhoneNumberUtils.jsm
+++ b/dom/phonenumberutils/PhoneNumberUtils.jsm
@@ -18,19 +18,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/PhoneNumberNormalizer.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "MCC_ISO3166_TABLE",
                                   "resource://gre/modules/mcc_iso3166_table.jsm");
 
 #ifdef MOZ_B2G_RIL
 XPCOMUtils.defineLazyServiceGetter(this, "mobileConnection",
                                    "@mozilla.org/mobileconnection/mobileconnectionservice;1",
                                    "nsIMobileConnectionService");
-XPCOMUtils.defineLazyServiceGetter(this, "icc",
-                                   "@mozilla.org/ril/content-helper;1",
-                                   "nsIIccProvider");
+XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
+                                   "@mozilla.org/icc/iccservice;1",
+                                   "nsIIccService");
 #endif
 
 this.PhoneNumberUtils = {
   init: function() {
     ppmm.addMessageListener(["PhoneNumberService:FuzzyMatch"], this);
   },
   //  1. See whether we have a network mcc
   //  2. If we don't have that, look for the simcard mcc
@@ -41,32 +41,33 @@ this.PhoneNumberUtils = {
   _mcc: '724',
 
   getCountryName: function getCountryName() {
     let mcc;
     let countryName;
 
 #ifdef MOZ_B2G_RIL
     // TODO: Bug 926740 - PhoneNumberUtils for multisim
-    // In Multi-sim, there is more than one client in 
-    // iccProvider/mobileConnectionProvider. Each client represents a
+    // In Multi-sim, there is more than one client in
+    // iccService/mobileConnectionService. Each client represents a
     // icc/mobileConnection service. To maintain the backward compatibility with
     // single sim, we always use client 0 for now. Adding support for multiple
     // sim will be addressed in bug 926740, if needed.
     let clientId = 0;
 
     // Get network mcc
     let connection = mobileConnection.getItemByServiceId(clientId);
     let voice = connection && connection.voice;
     if (voice && voice.network && voice.network.mcc) {
       mcc = voice.network.mcc;
     }
 
     // Get SIM mcc
-    let iccInfo = icc.getIccInfo(clientId);
+    let icc = gIccService.getIccByServiceId(clientId);
+    let iccInfo = icc && icc.iccInfo;
     if (!mcc && iccInfo && iccInfo.mcc) {
       mcc = iccInfo.mcc;
     }
 
     // Attempt to grab last known sim mcc from prefs
     if (!mcc) {
       try {
         mcc = Services.prefs.getCharPref("ril.lastKnownSimMcc");
--- a/dom/push/PushService.jsm
+++ b/dom/push/PushService.jsm
@@ -1770,24 +1770,25 @@ this.PushService = {
     try {
       if (!prefs.get("udp.wakeupEnabled")) {
         debug("UDP support disabled, we do not send any carrier info");
         throw new Error("UDP disabled");
       }
 
       let nm = Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
       if (nm.active && nm.active.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
-        let icc = Cc["@mozilla.org/ril/content-helper;1"].getService(Ci.nsIIccProvider);
+        let iccService = Cc["@mozilla.org/icc/iccservice;1"].getService(Ci.nsIIccService);
         // TODO: Bug 927721 - PushService for multi-sim
-        // In Multi-sim, there is more than one client in iccProvider. Each
-        // client represents a icc service. To maintain backward compatibility
+        // In Multi-sim, there is more than one client in iccService. Each
+        // client represents a icc handle. To maintain backward compatibility
         // with single sim, we always use client 0 for now. Adding support
         // for multiple sim will be addressed in bug 927721, if needed.
         let clientId = 0;
-        let iccInfo = icc.getIccInfo(clientId);
+        let icc = iccService.getIccByServiceId(clientId);
+        let iccInfo = icc && icc.iccInfo;
         if (iccInfo) {
           debug("Running on mobile data");
 
           let ips = {};
           let prefixLengths = {};
           nm.active.getAddresses(ips, prefixLengths);
 
           return {
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -93,18 +93,18 @@ IccInfo.prototype = {
 
   // nsIIccInfo
 
   iccType: null,
   iccid: null,
   mcc: null,
   mnc: null,
   spn: null,
-  isDisplayNetworkNameRequired: null,
-  isDisplaySpnRequired: null
+  isDisplayNetworkNameRequired: false,
+  isDisplaySpnRequired: false
 };
 
 function GsmIccInfo() {}
 GsmIccInfo.prototype = {
   __proto__: IccInfo.prototype,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
                                          Ci.nsIIccInfo]),
 
@@ -126,44 +126,49 @@ CdmaIccInfo.prototype = {
 };
 
 function RILContentHelper() {
   this.updateDebugFlag();
 
   this.numClients = gNumRadioInterfaces;
   if (DEBUG) debug("Number of clients: " + this.numClients);
 
+  this._iccs = [];
   this.rilContexts = [];
   for (let clientId = 0; clientId < this.numClients; clientId++) {
+    this._iccs.push(new Icc(this, clientId));
     this.rilContexts[clientId] = {
-      cardState: Ci.nsIIccProvider.CARD_STATE_UNKNOWN,
+      cardState: Ci.nsIIcc.CARD_STATE_UNKNOWN,
       iccInfo: null
     };
   }
 
   this.initDOMRequestHelper(/* aWindow */ null, RIL_IPC_MSG_NAMES);
   this._windowsMap = [];
+  this._requestMap = [];
   this._iccListeners = [];
   this._iccChannelCallback = [];
 
   Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 
   Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
 }
 
 RILContentHelper.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccProvider,
+                                         Ci.nsIIccService,
                                          Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
   classID:   RILCONTENTHELPER_CID,
   classInfo: XPCOMUtils.generateCI({classID: RILCONTENTHELPER_CID,
                                     classDescription: "RILContentHelper",
-                                    interfaces: [Ci.nsIIccProvider]}),
+                                    interfaces: [Ci.nsIIccProvider,
+                                                 Ci.nsIIccService]}),
 
   updateDebugFlag: function() {
     try {
       DEBUG = RIL.DEBUG_CONTENT_HELPER ||
               Services.prefs.getBoolPref(kPrefRilDebuggingEnabled);
     } catch (e) {}
   },
 
@@ -201,16 +206,18 @@ RILContentHelper.prototype = {
       }
     }
 
     this.updateInfo(newInfo, rilContext.iccInfo);
   },
 
   _windowsMap: null,
 
+  _requestMap: null,
+
   rilContexts: null,
 
   getRilContext: function(clientId) {
     // Update ril contexts by sending IPC message to chrome only when the first
     // time we require it. The information will be updated by following info
     // changed messages.
     this.getRilContext = function getRilContext(clientId) {
       return this.rilContexts[clientId];
@@ -229,152 +236,16 @@ RILContentHelper.prototype = {
 
     return this.rilContexts[clientId];
   },
 
   /**
    * nsIIccProvider
    */
 
-  getIccInfo: function(clientId) {
-    let context = this.getRilContext(clientId);
-    return context && context.iccInfo;
-  },
-
-  getCardState: function(clientId) {
-    let context = this.getRilContext(clientId);
-    return context && context.cardState;
-  },
-
-  matchMvno: function(clientId, window, mvnoType, mvnoData) {
-    if (window == null) {
-      throw Components.Exception("Can't get window object",
-                                  Cr.NS_ERROR_UNEXPECTED);
-    }
-
-    let request = Services.DOMRequest.createRequest(window);
-    let requestId = this.getRequestId(request);
-
-    cpmm.sendAsyncMessage("RIL:MatchMvno", {
-      clientId: clientId,
-      data: {
-        requestId: requestId,
-        mvnoType: mvnoType,
-        mvnoData: mvnoData
-      }
-    });
-    return request;
-  },
-
-  getCardLockEnabled: function(clientId, window, lockType) {
-    if (window == null) {
-      throw Components.Exception("Can't get window object",
-                                  Cr.NS_ERROR_UNEXPECTED);
-    }
-
-    let request = Services.DOMRequest.createRequest(window);
-    let requestId = this.getRequestId(request);
-    this._windowsMap[requestId] = window;
-
-    cpmm.sendAsyncMessage("RIL:GetCardLockEnabled", {
-      clientId: clientId,
-      data: {
-        lockType: lockType,
-        requestId: requestId
-      }
-    });
-    return request;
-  },
-
-  unlockCardLock: function(clientId, window, lockType, password, newPin) {
-    if (window == null) {
-      throw Components.Exception("Can't get window object",
-                                  Cr.NS_ERROR_UNEXPECTED);
-    }
-
-    let request = Services.DOMRequest.createRequest(window);
-    let requestId = this.getRequestId(request);
-    this._windowsMap[requestId] = window;
-
-    cpmm.sendAsyncMessage("RIL:UnlockCardLock", {
-      clientId: clientId,
-      data: {
-        lockType: lockType,
-        password: password,
-        newPin: newPin,
-        requestId: requestId
-      }
-    });
-    return request;
-  },
-
-  setCardLockEnabled: function(clientId, window, lockType, password, enabled) {
-    if (window == null) {
-      throw Components.Exception("Can't get window object",
-                                  Cr.NS_ERROR_UNEXPECTED);
-    }
-
-    let request = Services.DOMRequest.createRequest(window);
-    let requestId = this.getRequestId(request);
-    this._windowsMap[requestId] = window;
-
-    cpmm.sendAsyncMessage("RIL:SetCardLockEnabled", {
-      clientId: clientId,
-      data: {
-        lockType: lockType,
-        password: password,
-        enabled: enabled,
-        requestId: requestId
-      }
-    });
-    return request;
-  },
-
-  changeCardLockPassword: function(clientId, window, lockType, password,
-                                   newPassword) {
-    if (window == null) {
-      throw Components.Exception("Can't get window object",
-                                  Cr.NS_ERROR_UNEXPECTED);
-    }
-
-    let request = Services.DOMRequest.createRequest(window);
-    let requestId = this.getRequestId(request);
-    this._windowsMap[requestId] = window;
-
-    cpmm.sendAsyncMessage("RIL:ChangeCardLockPassword", {
-      clientId: clientId,
-      data: {
-        lockType: lockType,
-        password: password,
-        newPassword: newPassword,
-        requestId: requestId
-      }
-    });
-    return request;
-  },
-
-  getCardLockRetryCount: function(clientId, window, lockType) {
-    if (window == null) {
-      throw Components.Exception("Can't get window object",
-                                  Cr.NS_ERROR_UNEXPECTED);
-    }
-    let request = Services.DOMRequest.createRequest(window);
-    let requestId = this.getRequestId(request);
-    this._windowsMap[requestId] = window;
-
-    cpmm.sendAsyncMessage("RIL:GetCardLockRetryCount", {
-      clientId: clientId,
-      data: {
-        lockType: lockType,
-        requestId: requestId
-      }
-    });
-    return request;
-  },
-
   sendStkResponse: function(clientId, window, command, response) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     response.command = command;
     cpmm.sendAsyncMessage("RIL:SendStkResponse", {
       clientId: clientId,
@@ -540,37 +411,16 @@ RILContentHelper.prototype = {
         contact: iccContact,
         pin2: pin2
       }
     });
 
     return request;
   },
 
-  getServiceState: function(clientId, window, service) {
-    if (window == null) {
-      throw Components.Exception("Can't get window object",
-                                  Cr.NS_ERROR_UNEXPECTED);
-    }
-
-    return new window.Promise((resolve, reject) => {
-      let requestId =
-        this.getPromiseResolverId({resolve: resolve, reject: reject});
-      this._windowsMap[requestId] = window;
-
-      cpmm.sendAsyncMessage("RIL:GetServiceState", {
-        clientId: clientId,
-        data: {
-          requestId: requestId,
-          service: service
-        }
-      });
-    });
-  },
-
   _iccListeners: null,
 
   registerListener: function(listenerType, clientId, listener) {
     if (!this[listenerType]) {
       return;
     }
     let listeners = this[listenerType][clientId];
     if (!listeners) {
@@ -716,72 +566,66 @@ RILContentHelper.prototype = {
     }
 
     let data = msg.json.data;
     let clientId = msg.json.clientId;
     switch (msg.name) {
       case "RIL:CardStateChanged":
         if (this.rilContexts[clientId].cardState != data.cardState) {
           this.rilContexts[clientId].cardState = data.cardState;
-          this._deliverEvent(clientId,
-                             "_iccListeners",
-                             "notifyCardStateChanged",
-                             null);
+          this._deliverIccEvent(clientId,
+                                "notifyCardStateChanged",
+                                null);
         }
         break;
       case "RIL:IccInfoChanged":
         this.updateIccInfo(clientId, data);
-        this._deliverEvent(clientId,
-                           "_iccListeners",
-                           "notifyIccInfoChanged",
-                           null);
+        this._deliverIccEvent(clientId,
+                              "notifyIccInfoChanged",
+                              null);
         break;
       case "RIL:GetCardLockResult": {
         let requestId = data.requestId;
-        let requestWindow = this._windowsMap[requestId];
-        delete this._windowsMap[requestId];
+        let callback = this._requestMap[requestId];
+        delete this._requestMap[requestId];
 
         if (data.errorMsg) {
-          this.fireRequestError(requestId, data.errorMsg);
+          callback.notifyError(data.errorMsg);
           break;
         }
 
-        this.fireRequestSuccess(requestId,
-                                Cu.cloneInto({ enabled: data.enabled },
-                                             requestWindow));
+        callback.notifySuccessWithBoolean(data.enabled);
         break;
       }
       case "RIL:SetUnlockCardLockResult": {
         let requestId = data.requestId;
-        let requestWindow = this._windowsMap[requestId];
-        delete this._windowsMap[requestId];
+        let callback = this._requestMap[requestId];
+        delete this._requestMap[requestId];
 
         if (data.errorMsg) {
-          let cardLockError = new requestWindow.IccCardLockError(data.errorMsg,
-                                                                 data.retryCount);
-          this.fireRequestDetailedError(requestId, cardLockError);
+          let retryCount =
+            (data.retryCount !== undefined) ? data.retryCount : -1;
+          callback.notifyCardLockError(data.errorMsg, retryCount);
           break;
         }
 
-        this.fireRequestSuccess(requestId, null);
+        callback.notifySuccess();
         break;
       }
       case "RIL:CardLockRetryCount": {
         let requestId = data.requestId;
-        let requestWindow = this._windowsMap[requestId];
-        delete this._windowsMap[requestId];
+        let callback = this._requestMap[requestId];
+        delete this._requestMap[requestId];
 
         if (data.errorMsg) {
-          this.fireRequestError(data.requestId, data.errorMsg);
+          callback.notifyError(data.errorMsg);
           break;
         }
 
-        this.fireRequestSuccess(data.requestId,
-                                Cu.cloneInto({ retryCount: data.retryCount },
-                                             requestWindow));
+        callback.notifyGetCardLockRetryCount(data.retryCount);
         break;
       }
       case "RIL:StkCommand":
         this._deliverEvent(clientId, "_iccListeners", "notifyStkCommand",
                            [JSON.stringify(data)]);
         break;
       case "RIL:StkSessionEnd":
         this._deliverEvent(clientId, "_iccListeners", "notifyStkSessionEnd", null);
@@ -796,22 +640,40 @@ RILContentHelper.prototype = {
         this.handleIccExchangeAPDU(data);
         break;
       case "RIL:ReadIccContacts":
         this.handleReadIccContacts(data);
         break;
       case "RIL:UpdateIccContact":
         this.handleUpdateIccContact(data);
         break;
-      case "RIL:MatchMvno":
-        this.handleSimpleRequest(data.requestId, data.errorMsg, data.result);
+      case "RIL:MatchMvno": {
+        let requestId = data.requestId;
+        let callback = this._requestMap[requestId];
+        delete this._requestMap[requestId];
+
+        if (data.errorMsg) {
+          callback.notifyError(data.errorMsg);
+          break;
+        }
+        callback.notifySuccessWithBoolean(data.result);
         break;
-      case "RIL:GetServiceState":
-        this.handleGetServiceState(data);
+      }
+      case "RIL:GetServiceState": {
+        let requestId = data.requestId;
+        let callback = this._requestMap[requestId];
+        delete this._requestMap[requestId];
+
+        if (data.errorMsg) {
+          callback.notifyError(data.errorMsg);
+          break;
+        }
+        callback.notifySuccessWithBoolean(data.result);
         break;
+      }
     }
   },
 
   handleSimpleRequest: function(requestId, errorMsg, result) {
     if (errorMsg) {
       this.fireRequestError(requestId, errorMsg);
     } else {
       this.fireRequestSuccess(requestId, result);
@@ -904,30 +766,16 @@ RILContentHelper.prototype = {
     }
 
     let contact = new window.mozContact(prop);
     contact.id = iccContact.contactId;
 
     this.fireRequestSuccess(message.requestId, contact);
   },
 
-  handleGetServiceState: function(message) {
-    let requestId = message.requestId;
-    let requestWindow = this._windowsMap[requestId];
-    delete this._windowsMap[requestId];
-
-    let resolver = this.takePromiseResolver(requestId);
-    if (message.errorMsg) {
-      resolver.reject(new requestWindow.DOMError(message.errorMsg));
-      return;
-    }
-
-    resolver.resolve(message.result);
-  },
-
   _deliverEvent: function(clientId, listenerType, name, args) {
     if (!this[listenerType]) {
       return;
     }
     let thisListeners = this[listenerType][clientId];
     if (!thisListeners) {
       return;
     }
@@ -942,12 +790,241 @@ RILContentHelper.prototype = {
         throw new Error("No handler for " + name);
       }
       try {
         handler.apply(listener, args);
       } catch (e) {
         if (DEBUG) debug("listener for " + name + " threw an exception: " + e);
       }
     }
+  },
+
+  /**
+   * nsIIccService interface.
+   */
+
+  _iccs: null, // An array of Icc instances.
+
+  getIccByServiceId: function(serviceId) {
+    let icc = this._iccs[serviceId];
+    if (!icc) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    return icc;
+  },
+
+  /**
+   * Bridge APIs from nsIIccService to nsIIccProvider
+   */
+
+  _deliverIccEvent: function(clientId, name, args) {
+    let icc = this._iccs[clientId];
+    if (!icc) {
+      if (DEBUG) debug("_deliverIccEvent: Invalid clientId: " + clientId);
+      return;
+    }
+
+    icc.deliverListenerEvent(name, args);
+  },
+
+  getIccInfo: function(clientId) {
+    let context = this.getRilContext(clientId);
+    return context && context.iccInfo;
+  },
+
+  getCardState: function(clientId) {
+    let context = this.getRilContext(clientId);
+    return context && context.cardState;
+  },
+
+  matchMvno: function(clientId, mvnoType, mvnoData, callback) {
+    let requestId = UUIDGenerator.generateUUID().toString();
+    this._requestMap[requestId] = callback;
+
+    cpmm.sendAsyncMessage("RIL:MatchMvno", {
+      clientId: clientId,
+      data: {
+        requestId: requestId,
+        mvnoType: mvnoType,
+        mvnoData: mvnoData
+      }
+    });
+  },
+
+  getCardLockEnabled: function(clientId, lockType, callback) {
+    let requestId = UUIDGenerator.generateUUID().toString();
+    this._requestMap[requestId] = callback;
+
+    cpmm.sendAsyncMessage("RIL:GetCardLockEnabled", {
+      clientId: clientId,
+      data: {
+        lockType: lockType,
+        requestId: requestId
+      }
+    });
+  },
+
+  unlockCardLock: function(clientId, lockType, password, newPin, callback) {
+    let requestId = UUIDGenerator.generateUUID().toString();
+    this._requestMap[requestId] = callback;
+
+    cpmm.sendAsyncMessage("RIL:UnlockCardLock", {
+      clientId: clientId,
+      data: {
+        lockType: lockType,
+        password: password,
+        newPin: newPin,
+        requestId: requestId
+      }
+    });
+  },
+
+  setCardLockEnabled: function(clientId, lockType, password, enabled, callback) {
+    let requestId = UUIDGenerator.generateUUID().toString();
+    this._requestMap[requestId] = callback;
+
+    cpmm.sendAsyncMessage("RIL:SetCardLockEnabled", {
+      clientId: clientId,
+      data: {
+        lockType: lockType,
+        password: password,
+        enabled: enabled,
+        requestId: requestId
+      }
+    });
+  },
+
+  changeCardLockPassword: function(clientId, lockType, password, newPassword,
+                                   callback) {
+    let requestId = UUIDGenerator.generateUUID().toString();
+    this._requestMap[requestId] = callback;
+
+    cpmm.sendAsyncMessage("RIL:ChangeCardLockPassword", {
+      clientId: clientId,
+      data: {
+        lockType: lockType,
+        password: password,
+        newPassword: newPassword,
+        requestId: requestId
+      }
+    });
+  },
+
+  getCardLockRetryCount: function(clientId, lockType, callback) {
+    let requestId = UUIDGenerator.generateUUID().toString();
+    this._requestMap[requestId] = callback;
+
+    cpmm.sendAsyncMessage("RIL:GetCardLockRetryCount", {
+      clientId: clientId,
+      data: {
+        lockType: lockType,
+        requestId: requestId
+      }
+    });
+  },
+
+  getServiceStateEnabled: function(clientId, service, callback) {
+    let requestId = UUIDGenerator.generateUUID().toString();
+    this._requestMap[requestId] = callback;
+
+    cpmm.sendAsyncMessage("RIL:GetServiceState", {
+      clientId: clientId,
+      data: {
+        requestId: requestId,
+        service: service
+      }
+    });
+  }
+};
+
+function Icc(aIccProvider, aClientId) {
+  this._iccProvider = aIccProvider;
+  this._clientId = aClientId;
+  this._listeners = [];
+}
+Icc.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIIcc]),
+
+  _iccProvider: null,
+  _clientId: -1,
+  _listeners: null,
+
+  deliverListenerEvent: function(aName, aArgs) {
+    let listeners = this._listeners.slice();
+    for (let listener of listeners) {
+      if (this._listeners.indexOf(listener) === -1) {
+        continue;
+      }
+      let handler = listener[aName];
+      if (typeof handler != "function") {
+        throw new Error("No handler for " + aName);
+      }
+      try {
+        handler.apply(listener, aArgs);
+      } catch (e) {
+        if (DEBUG) {
+          debug("listener for " + aName + " threw an exception: " + e);
+        }
+      }
+    }
+  },
+
+  /**
+   * nsIIcc interface.
+   */
+  registerListener: function(aListener) {
+    if (this._listeners.indexOf(aListener) >= 0) {
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    this._listeners.push(aListener);
+    cpmm.sendAsyncMessage("RIL:RegisterIccMsg");
+  },
+
+  unregisterListener: function(aListener) {
+    let index = this._listeners.indexOf(aListener);
+    if (index >= 0) {
+      this._listeners.splice(index, 1);
+    }
+  },
+
+  get iccInfo() {
+    return this._iccProvider.getIccInfo(this._clientId);
+  },
+
+  get cardState() {
+    return this._iccProvider.getCardState(this._clientId);
+  },
+
+  getCardLockEnabled: function(aLockType, aCallback) {
+    this._iccProvider.getCardLockEnabled(this._clientId, aLockType, aCallback);
+  },
+
+  unlockCardLock: function(aLockType, aPassword, aNewPin, aCallback) {
+    this._iccProvider.unlockCardLock(this._clientId, aLockType,
+                                     aPassword, aNewPin, aCallback);
+  },
+
+  setCardLockEnabled: function(aLockType, aPassword, aEnabled, aCallback) {
+    this._iccProvider.setCardLockEnabled(this._clientId, aLockType,
+                                         aPassword, aEnabled, aCallback);
+  },
+
+  changeCardLockPassword: function(aLockType, aPassword, aNewPassword, aCallback) {
+    this._iccProvider.changeCardLockPassword(this._clientId, aLockType,
+                                             aPassword, aNewPassword, aCallback);
+  },
+
+  getCardLockRetryCount: function(aLockType, aCallback) {
+    this._iccProvider.getCardLockRetryCount(this._clientId, aLockType, aCallback);
+  },
+
+  matchMvno: function(aMvnoType, aMvnoData, aCallback) {
+    this._iccProvider.matchMvno(this._clientId, aMvnoType, aMvnoData, aCallback);
+  },
+
+  getServiceStateEnabled: function(aService, aCallback) {
+    this._iccProvider.getServiceStateEnabled(this._clientId, aService, aCallback);
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RILContentHelper]);
--- a/dom/system/gonk/RILContentHelper.manifest
+++ b/dom/system/gonk/RILContentHelper.manifest
@@ -8,11 +8,13 @@
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
 # RILContentHelper.js
+# TODO: Bug 815526, deprecate RILContentHelper:
+#       To be removed from b2g/installer/package-manifest.in as well.
 component {472816e1-1fd6-4405-996c-806f9ea68174} RILContentHelper.js
 contract @mozilla.org/ril/content-helper;1 {472816e1-1fd6-4405-996c-806f9ea68174}
 category profile-after-change RILContentHelper @mozilla.org/ril/content-helper;1
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -82,16 +82,17 @@ const INT32_MAX = 2147483647;
 const NETWORK_TYPE_UNKNOWN     = Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN;
 const NETWORK_TYPE_WIFI        = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
 const NETWORK_TYPE_MOBILE      = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE;
 const NETWORK_TYPE_MOBILE_MMS  = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS;
 const NETWORK_TYPE_MOBILE_SUPL = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL;
 const NETWORK_TYPE_MOBILE_IMS  = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_IMS;
 const NETWORK_TYPE_MOBILE_DUN  = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN;
 
+// TODO: Bug 815526, deprecate RILContentHelper.
 const RIL_IPC_ICCMANAGER_MSG_NAMES = [
   "RIL:GetRilContext",
   "RIL:SendStkResponse",
   "RIL:SendStkMenuSelection",
   "RIL:SendStkTimerExpiration",
   "RIL:SendStkEventDownload",
   "RIL:GetCardLockEnabled",
   "RIL:UnlockCardLock",
@@ -122,16 +123,20 @@ function updateDebugFlag() {
   DEBUG = RIL.DEBUG_RIL || debugPref;
 }
 updateDebugFlag();
 
 function debug(s) {
   dump("-*- RadioInterfaceLayer: " + s + "\n");
 }
 
+XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
+                                   "@mozilla.org/icc/gonkiccservice;1",
+                                   "nsIGonkIccService");
+
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService",
                                    "@mozilla.org/mobilemessage/mobilemessageservice;1",
                                    "nsIMobileMessageService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
                                    "@mozilla.org/sms/gonksmsservice;1",
                                    "nsIGonkSmsService");
 
@@ -172,16 +177,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "nsIIccMessenger");
 
 XPCOMUtils.defineLazyGetter(this, "gStkCmdFactory", function() {
   let stk = {};
   Cu.import("resource://gre/modules/StkProactiveCmdFactory.jsm", stk);
   return stk.StkProactiveCmdFactory;
 });
 
+// TODO: Bug 815526, deprecate RILContentHelper.
 XPCOMUtils.defineLazyGetter(this, "gMessageManager", function() {
   return {
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
                                            Ci.nsIObserver]),
 
     ril: null,
 
     // Manage message targets in terms of topic. Only the authorized and
@@ -881,18 +887,18 @@ IccInfo.prototype = {
 
   // nsIIccInfo
 
   iccType: null,
   iccid: null,
   mcc: null,
   mnc: null,
   spn: null,
-  isDisplayNetworkNameRequired: null,
-  isDisplaySpnRequired: null
+  isDisplayNetworkNameRequired: false,
+  isDisplaySpnRequired: false
 };
 
 function GsmIccInfo() {}
 GsmIccInfo.prototype = {
   __proto__: IccInfo.prototype,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
                                          Ci.nsIIccInfo]),
 
@@ -1418,28 +1424,29 @@ function RadioInterfaceLayer() {
   this.radioInterfaces = [];
   for (let clientId = 0; clientId < numIfaces; clientId++) {
     this.radioInterfaces.push(new RadioInterface(clientId, workerMessenger));
   }
 
   Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
   Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
 
-  gMessageManager.init(this);
+  gMessageManager.init(this); // TODO: Bug 815526, deprecate RILContentHelper.
   gRadioEnabledController.init(this);
   gDataConnectionManager.init(this);
 }
 RadioInterfaceLayer.prototype = {
 
   classID:   RADIOINTERFACELAYER_CID,
   classInfo: XPCOMUtils.generateCI({classID: RADIOINTERFACELAYER_CID,
                                     classDescription: "RadioInterfaceLayer",
                                     interfaces: [Ci.nsIRadioInterfaceLayer]}),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIRadioInterfaceLayer,
+                                         Ci.nsIRadioInterfaceLayer_new, // TODO: Bug 815526, deprecate RILContentHelper.
                                          Ci.nsIObserver]),
 
   /**
    * nsIObserver interface methods.
    */
 
   observe: function(subject, topic, data) {
     switch (topic) {
@@ -1671,23 +1678,24 @@ WorkerMessenger.prototype = {
     }).bind(this));
   }
 };
 
 function RadioInterface(aClientId, aWorkerMessenger) {
   this.clientId = aClientId;
   this.workerMessenger = {
     send: aWorkerMessenger.send.bind(aWorkerMessenger, aClientId),
+    // TODO: Bug 815526, deprecate RILContentHelper.
     sendWithIPCMessage:
       aWorkerMessenger.sendWithIPCMessage.bind(aWorkerMessenger, aClientId),
   };
   aWorkerMessenger.registerClient(aClientId, this);
 
   this.rilContext = {
-    cardState:      Ci.nsIIccProvider.CARD_STATE_UNKNOWN,
+    cardState:      Ci.nsIIcc.CARD_STATE_UNKNOWN,
     iccInfo:        null,
     imsi:           null
   };
 
   this.operatorInfo = {};
 
   let lock = gSettingsService.createLock();
 
@@ -1775,22 +1783,24 @@ RadioInterface.prototype = {
       }
     }
 
     return false;
   },
 
   isCardPresent: function() {
     let cardState = this.rilContext.cardState;
-    return cardState !== Ci.nsIIccProvider.CARD_STATE_UNDETECTED &&
-      cardState !== Ci.nsIIccProvider.CARD_STATE_UNKNOWN;
+    return cardState !== Ci.nsIIcc.CARD_STATE_UNDETECTED &&
+      cardState !== Ci.nsIIcc.CARD_STATE_UNKNOWN;
   },
 
   /**
    * Process a message from the content process.
+   *
+   * TODO: Bug 815526, deprecate RILContentHelper
    */
   receiveMessage: function(msg) {
     switch (msg.name) {
       case "RIL:GetRilContext":
         // This message is sync.
         return this.rilContext;
       case "RIL:GetCardLockEnabled":
         this.workerMessenger.sendWithIPCMessage(msg, "iccGetCardLockEnabled",
@@ -1911,16 +1921,19 @@ RadioInterface.prototype = {
         // gRadioEnabledController should know the radio state for each client,
         // so notify gRadioEnabledController here.
         gRadioEnabledController.notifyRadioStateChanged(this.clientId,
                                                         message.radioState);
         break;
       case "cardstatechange":
         this.rilContext.cardState = message.cardState;
         gRadioEnabledController.receiveCardState(this.clientId);
+        gIccService.notifyCardStateChanged(this.clientId,
+                                           this.rilContext.cardState);
+        // TODO: Bug 815526, deprecate RILContentHelper.
         gMessageManager.sendIccMessage("RIL:CardStateChanged",
                                        this.clientId, message);
         break;
       case "sms-received":
         this.handleSmsReceived(message);
         break;
       case "cellbroadcast-received":
         this.handleCellbroadcastMessageReceived(message);
@@ -1928,42 +1941,45 @@ RadioInterface.prototype = {
       case "nitzTime":
         this.handleNitzTime(message);
         break;
       case "iccinfochange":
         this.handleIccInfoChange(message);
         break;
       case "iccimsi":
         this.rilContext.imsi = message.imsi;
+        gIccService.notifyImsiChanged(this.clientId, this.rilContext.imsi);
         break;
       case "iccmbdn":
         this.handleIccMbdn(message);
         break;
       case "iccmwis":
         this.handleIccMwis(message.mwi);
         break;
       case "stkcommand":
         this.handleStkProactiveCommand(message);
         break;
       case "stksessionend":
+        // TODO: Bug 815526, deprecate RILContentHelper.
         gMessageManager.sendIccMessage("RIL:StkSessionEnd", this.clientId, null);
         break;
       case "cdma-info-rec-received":
         this.handleCdmaInformationRecords(message.records);
         break;
       default:
         throw new Error("Don't know about this message type: " +
                         message.rilMessageType);
     }
   },
 
   // Matches the mvnoData pattern with imsi. Characters 'x' and 'X' are skipped
   // and not compared. E.g., if the mvnoData passed is '310260x10xxxxxx',
   // then the function returns true only if imsi has the same first 6 digits,
   // 8th and 9th digit.
+  // TODO: Bug 815526, deprecate RILContentHelper.
   isImsiMatches: function(mvnoData) {
     let imsi = this.rilContext.imsi;
 
     // This should not be an error, but a mismatch.
     if (mvnoData.length > imsi.length) {
       return false;
     }
 
@@ -1971,16 +1987,17 @@ RadioInterface.prototype = {
       let c = mvnoData[i];
       if ((c !== 'x') && (c !== 'X') && (c !== imsi[i])) {
         return false;
       }
     }
     return true;
   },
 
+  // TODO: Bug 815526, deprecate RILContentHelper.
   matchMvno: function(target, message) {
     if (DEBUG) this.debug("matchMvno: " + JSON.stringify(message));
 
     if (!message || !message.mvnoData) {
       message.errorMsg = RIL.GECKO_ERROR_INVALID_PARAMETER;
     }
 
     if (!message.errorMsg) {
@@ -2208,16 +2225,18 @@ RadioInterface.prototype = {
     // Note: returnNumber and returnMessage is not available from UICC.
     service.notifyStatusChanged(this.clientId, mwi.active, mwi.msgCount,
                                 null, null);
   },
 
   handleIccInfoChange: function(message) {
     let oldSpn = this.rilContext.iccInfo ? this.rilContext.iccInfo.spn : null;
 
+    // TODO: Bug 815526, deprecate RILContentHelper:
+    //       Move the logic of updating iccInfo to IccService.js.
     if (!message || !message.iccid) {
       // If iccInfo is already `null`, don't have to clear it and send
       // RIL:IccInfoChanged.
       if (!this.rilContext.iccInfo) {
         return;
       }
 
       // Card is not detected, clear iccInfo to null.
@@ -2237,27 +2256,21 @@ RadioInterface.prototype = {
         return;
       }
 
       this.updateInfo(message, this.rilContext.iccInfo);
     }
 
     // RIL:IccInfoChanged corresponds to a DOM event that gets fired only
     // when iccInfo has changed.
+    // TODO: Bug 815526, deprecate RILContentHelper.
     gMessageManager.sendIccMessage("RIL:IccInfoChanged",
                                    this.clientId,
                                    message.iccid ? message : null);
-
-    // Update lastKnownSimMcc.
-    if (message.mcc) {
-      try {
-        Services.prefs.setCharPref("ril.lastKnownSimMcc",
-                                   message.mcc.toString());
-      } catch (e) {}
-    }
+    gIccService.notifyIccInfoChanged(this.clientId, this.rilContext.iccInfo);
 
     // Update lastKnownHomeNetwork.
     if (message.mcc && message.mnc) {
       let lastKnownHomeNetwork = message.mcc + "-" + message.mnc;
       // Append spn information if available.
       if (message.spn) {
         lastKnownHomeNetwork += "-" + message.spn;
       }
@@ -2275,16 +2288,17 @@ RadioInterface.prototype = {
   handleStkProactiveCommand: function(message) {
     if (DEBUG) this.debug("handleStkProactiveCommand " + JSON.stringify(message));
     let iccId = this.rilContext.iccInfo && this.rilContext.iccInfo.iccid;
     if (iccId) {
       gIccMessenger
         .notifyStkProactiveCommand(iccId,
                                    gStkCmdFactory.createCommand(message));
     }
+    // TODO: Bug 815526, deprecate RILContentHelper.
     gMessageManager.sendIccMessage("RIL:StkCommand", this.clientId, message);
   },
 
   _convertCbGsmGeographicalScope: function(aGeographicalScope) {
     return (aGeographicalScope != null)
       ? aGeographicalScope
       : Ci.nsICellBroadcastService.GSM_GEOGRAPHICAL_SCOPE_INVALID;
   },
--- a/dom/system/gonk/moz.build
+++ b/dom/system/gonk/moz.build
@@ -84,18 +84,18 @@ EXTRA_JS_MODULES += [
     'systemlibs.js',
 ]
 
 if CONFIG['MOZ_B2G_RIL']:
     XPIDL_SOURCES += [
         'nsIRadioInterfaceLayer.idl',
     ]
     EXTRA_COMPONENTS += [
-        'RILContentHelper.js',
-        'RILContentHelper.manifest',
+        'RILContentHelper.js', # TODO: Bug 815526, deprecate RILContentHelper.
+        'RILContentHelper.manifest', # TODO: Bug 815526, deprecate RILContentHelper.
         'RILSystemMessengerHelper.js',
         'RILSystemMessengerHelper.manifest',
     ]
     EXTRA_JS_MODULES += [
         'ril_consts.js',
         'ril_worker.js',
         'ril_worker_buf_object.js',
         'RILSystemMessenger.jsm',
--- a/dom/system/gonk/nsIRadioInterfaceLayer.idl
+++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl
@@ -20,17 +20,17 @@ interface nsIRilNetworkInterface : nsINe
   readonly attribute DOMString mmsProxy; // Empty string if not set.
   readonly attribute long      mmsPort;  // -1 if not set.
 };
 
 [scriptable, uuid(4441e660-4ad0-11e4-916c-0800200c9a66)]
 interface nsIRilContext : nsISupports
 {
   /**
-   * One of the nsIIccProvider.CARD_STATE_* values.
+   * One of the nsIIcc.CARD_STATE_* values.
    */
   readonly attribute unsigned long cardState;
 
   readonly attribute DOMString imsi;
 
   readonly attribute nsIIccInfo iccInfo;
 };
 
@@ -74,8 +74,18 @@ interface nsIRadioInterfaceLayer : nsISu
    * Select a proper client for dialing emergency call.
    *
    * @return clientId or -1 if none of the clients are avaialble.
    */
   unsigned long getClientIdForEmergencyCall();
 
   void setMicrophoneMuted(in boolean muted);
 };
+
+
+/**
+ * Helper Interface to define new APIs of nsIRadioInterfaceLayer during
+ * ril-interfaces frozen phase.
+ */
+[scriptable, uuid(f8ec63da-c22e-11e4-89f3-b767dae42a13)]
+interface nsIRadioInterfaceLayer_new : nsIRadioInterfaceLayer
+{
+};
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -2572,17 +2572,17 @@ this.CALL_FAIL_ERROR_UNSPECIFIED = 0xfff
 
 // See nsIMobileConnection::MOBILE_RADIO_STATE_*
 this.GECKO_RADIOSTATE_UNKNOWN   = -1;
 this.GECKO_RADIOSTATE_ENABLED   = 0;
 this.GECKO_RADIOSTATE_DISABLED  = 1;
 
 // Only used in ril_worker.js
 this.GECKO_CARDSTATE_UNINITIALIZED = 4294967294; // UINT32_MAX - 1
-// See nsIIccProvider::CARD_STATE_*
+// See nsIIcc::CARD_STATE_*
 this.GECKO_CARDSTATE_UNDETECTED = 4294967295; // UINT32_MAX
 this.GECKO_CARDSTATE_UNKNOWN = 0;
 this.GECKO_CARDSTATE_READY = 1;
 this.GECKO_CARDSTATE_PIN_REQUIRED = 2;
 this.GECKO_CARDSTATE_PUK_REQUIRED = 3;
 this.GECKO_CARDSTATE_PERMANENT_BLOCKED = 4;
 this.GECKO_CARDSTATE_PERSONALIZATION_IN_PROGRESS = 5;
 this.GECKO_CARDSTATE_PERSONALIZATION_READY = 6;
@@ -2605,17 +2605,17 @@ this.GECKO_CARDSTATE_RUIM_LOCKED = 22;
 this.GECKO_CARDSTATE_NETWORK1_PUK_REQUIRED = 23;
 this.GECKO_CARDSTATE_NETWORK2_PUK_REQUIRED = 24;
 this.GECKO_CARDSTATE_HRPD_NETWORK_PUK_REQUIRED = 25;
 this.GECKO_CARDSTATE_RUIM_CORPORATE_PUK_REQUIRED = 26;
 this.GECKO_CARDSTATE_RUIM_SERVICE_PROVIDER_PUK_REQUIRED = 27;
 this.GECKO_CARDSTATE_RUIM_PUK_REQUIRED = 28;
 this.GECKO_CARDSTATE_ILLEGAL = 29;
 
-// See nsIIccProvider::CARD_LOCK_TYPE_*
+// See nsIIcc::CARD_LOCK_TYPE_*
 this.GECKO_CARDLOCK_PIN = 0;
 this.GECKO_CARDLOCK_PIN2 = 1;
 this.GECKO_CARDLOCK_PUK = 2;
 this.GECKO_CARDLOCK_PUK2 = 3;
 this.GECKO_CARDLOCK_NCK = 4;
 this.GECKO_CARDLOCK_NSCK = 5;
 this.GECKO_CARDLOCK_NCK1 = 6;
 this.GECKO_CARDLOCK_NCK2 = 7;
@@ -2648,27 +2648,27 @@ GECKO_CARDLOCK_TO_SEL_CODE[GECKO_CARDLOC
 GECKO_CARDLOCK_TO_SEL_CODE[GECKO_CARDLOCK_PUK2] = ICC_SEL_CODE_SIM_PUK2;
 GECKO_CARDLOCK_TO_SEL_CODE[GECKO_CARDLOCK_NCK] = ICC_SEL_CODE_PH_NET_PIN;
 GECKO_CARDLOCK_TO_SEL_CODE[GECKO_CARDLOCK_NSCK] = ICC_SEL_CODE_PH_NETSUB_PIN;
 GECKO_CARDLOCK_TO_SEL_CODE[GECKO_CARDLOCK_CCK] = ICC_SEL_CODE_PH_CORP_PIN;
 GECKO_CARDLOCK_TO_SEL_CODE[GECKO_CARDLOCK_SPCK] = ICC_SEL_CODE_PH_SP_PIN;
 // TODO: Bug 1116072: identify the mapping between RIL_PERSOSUBSTATE_SIM_SIM @
 //       ril.h and TS 27.007, clause 8.65 for GECKO_CARDLOCK_PCK.
 
-// See nsIIccProvider::CARD_CONTACT_TYPE_*
+// See nsIIcc::CARD_CONTACT_TYPE_*
 this.GECKO_CARDCONTACT_TYPE_ADN = 0;
 this.GECKO_CARDCONTACT_TYPE_FDN = 1;
 this.GECKO_CARDCONTACT_TYPE_SDN = 2;
 
-// See nsIIccProvider::CARD_MVNO_TYPE_*
+// See nsIIcc::CARD_MVNO_TYPE_*
 this.GECKO_CARDMVNO_TYPE_IMSI = 0;
 this.GECKO_CARDMVNO_TYPE_SPN = 1;
 this.GECKO_CARDMVNO_TYPE_GID = 2;
 
-// See nsIIccProvider::CARD_MVNO_TYPE_*
+// See nsIIcc::CARD_SERVICE_*
 this.GECKO_CARDSERVICE_FDN = 0;
 
 // See ril.h RIL_PersoSubstate
 this.PERSONSUBSTATE = {};
 PERSONSUBSTATE[CARD_PERSOSUBSTATE_UNKNOWN] = GECKO_CARDSTATE_UNKNOWN;
 PERSONSUBSTATE[CARD_PERSOSUBSTATE_IN_PROGRESS] = GECKO_CARDSTATE_PERSONALIZATION_IN_PROGRESS;
 PERSONSUBSTATE[CARD_PERSOSUBSTATE_READY] = GECKO_CARDSTATE_PERSONALIZATION_READY;
 PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_NETWORK] = GECKO_CARDSTATE_NETWORK_LOCKED;
--- a/dom/system/gonk/tests/marionette/test_ril_code_quality.py
+++ b/dom/system/gonk/tests/marionette/test_ril_code_quality.py
@@ -1,13 +1,13 @@
 """
 The test performs the static code analysis check by JSHint.
 
 Target js files:
-- RILContentHelper.js
+- RILContentHelper.js TODO: Bug 815526, deprecate RILContentHelper.
 - RadioInterfaceLayer.js
 - ril_worker.js
 - ril_consts.js
 
 If the js file contains the line of 'importScript()' (Ex: ril_worker.js), the
 test will perform a special merge step before excuting JSHint.
 
 Ex: Script A
@@ -95,17 +95,17 @@ class StringUtility:
 
 
 class ResourceUriFileReader:
 
     """Handle the process of reading the source code from system."""
 
     URI_PREFIX = 'resource://gre/'
     URI_PATH = {
-        'RILContentHelper.js':    'components/RILContentHelper.js',
+        'RILContentHelper.js':    'components/RILContentHelper.js', #TODO: Bug 815526, deprecate RILContentHelper.
         'RadioInterfaceLayer.js': 'components/RadioInterfaceLayer.js',
         'ril_worker.js':          'modules/ril_worker.js',
         'ril_consts.js':          'modules/ril_consts.js',
         'systemlibs.js':          'modules/systemlibs.js',
         'worker_buf.js':          'modules/workers/worker_buf.js',
     }
 
     CODE_OPEN_CHANNEL_BY_URI = '''
@@ -349,16 +349,17 @@ class TestRILCodeQuality(MarionetteTestC
             JSHintEngine(self.marionette,
                          self._read_local_file(self.JSHINT_PATH),
                          self._read_local_file(self.JSHINTRC_PATH)),
             self._get_extended_error_message)
 
     def tearDown(self):
         MarionetteTestCase.tearDown(self)
 
+    # TODO: Bug 815526, deprecate RILContentHelper.
     def test_RILContentHelper(self):
         self._check('RILContentHelper.js')
 
     def test_RadioInterfaceLayer(self):
         self._check('RadioInterfaceLayer.js')
 
     # Bug 936504. Disable the test for 'ril_worker.js'. It sometimes runs very
     # slow and causes the timeout fail on try server.
--- a/dom/system/gonk/tests/test_ril_worker_icc_CardState.js
+++ b/dom/system/gonk/tests/test_ril_worker_icc_CardState.js
@@ -28,75 +28,75 @@ add_test(function test_personalization_s
 
     ril._isCdma = isCdma;
     ril._processICCStatus(iccStatus);
     equal(ril.cardState, geckoCardState);
   }
 
   // Test GSM personalization state.
   testPersonalization(false, CARD_PERSOSUBSTATE_SIM_NETWORK,
-                      Ci.nsIIccProvider.CARD_STATE_NETWORK_LOCKED);
+                      Ci.nsIIcc.CARD_STATE_NETWORK_LOCKED);
   testPersonalization(false, CARD_PERSOSUBSTATE_SIM_NETWORK_SUBSET,
-                      Ci.nsIIccProvider.CARD_STATE_NETWORK_SUBSET_LOCKED);
+                      Ci.nsIIcc.CARD_STATE_NETWORK_SUBSET_LOCKED);
   testPersonalization(false, CARD_PERSOSUBSTATE_SIM_CORPORATE,
-                      Ci.nsIIccProvider.CARD_STATE_CORPORATE_LOCKED);
+                      Ci.nsIIcc.CARD_STATE_CORPORATE_LOCKED);
   testPersonalization(false, CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER,
-                      Ci.nsIIccProvider.CARD_STATE_SERVICE_PROVIDER_LOCKED);
+                      Ci.nsIIcc.CARD_STATE_SERVICE_PROVIDER_LOCKED);
   testPersonalization(false, CARD_PERSOSUBSTATE_SIM_SIM,
-                      Ci.nsIIccProvider.CARD_STATE_SIM_LOCKED);
+                      Ci.nsIIcc.CARD_STATE_SIM_LOCKED);
   testPersonalization(false, CARD_PERSOSUBSTATE_SIM_NETWORK_PUK,
-                      Ci.nsIIccProvider.CARD_STATE_NETWORK_PUK_REQUIRED);
+                      Ci.nsIIcc.CARD_STATE_NETWORK_PUK_REQUIRED);
   testPersonalization(false, CARD_PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK,
-                      Ci.nsIIccProvider.CARD_STATE_NETWORK_SUBSET_PUK_REQUIRED);
+                      Ci.nsIIcc.CARD_STATE_NETWORK_SUBSET_PUK_REQUIRED);
   testPersonalization(false, CARD_PERSOSUBSTATE_SIM_CORPORATE_PUK,
-                      Ci.nsIIccProvider.CARD_STATE_CORPORATE_PUK_REQUIRED);
+                      Ci.nsIIcc.CARD_STATE_CORPORATE_PUK_REQUIRED);
   testPersonalization(false, CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK,
-                      Ci.nsIIccProvider.CARD_STATE_SERVICE_PROVIDER_PUK_REQUIRED);
+                      Ci.nsIIcc.CARD_STATE_SERVICE_PROVIDER_PUK_REQUIRED);
   testPersonalization(false, CARD_PERSOSUBSTATE_SIM_SIM_PUK,
-                      Ci.nsIIccProvider.CARD_STATE_SIM_PUK_REQUIRED);
+                      Ci.nsIIcc.CARD_STATE_SIM_PUK_REQUIRED);
 
   testPersonalization(false, CARD_PERSOSUBSTATE_UNKNOWN,
-                      Ci.nsIIccProvider.CARD_STATE_UNKNOWN);
+                      Ci.nsIIcc.CARD_STATE_UNKNOWN);
   testPersonalization(false, CARD_PERSOSUBSTATE_IN_PROGRESS,
-                      Ci.nsIIccProvider.CARD_STATE_PERSONALIZATION_IN_PROGRESS);
+                      Ci.nsIIcc.CARD_STATE_PERSONALIZATION_IN_PROGRESS);
   testPersonalization(false, CARD_PERSOSUBSTATE_READY,
-                      Ci.nsIIccProvider.CARD_STATE_PERSONALIZATION_READY);
+                      Ci.nsIIcc.CARD_STATE_PERSONALIZATION_READY);
 
   // Test CDMA personalization state.
   testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_NETWORK1,
-                      Ci.nsIIccProvider.CARD_STATE_NETWORK1_LOCKED);
+                      Ci.nsIIcc.CARD_STATE_NETWORK1_LOCKED);
   testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_NETWORK2,
-                      Ci.nsIIccProvider.CARD_STATE_NETWORK2_LOCKED);
+                      Ci.nsIIcc.CARD_STATE_NETWORK2_LOCKED);
   testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_HRPD,
-                      Ci.nsIIccProvider.CARD_STATE_HRPD_NETWORK_LOCKED);
+                      Ci.nsIIcc.CARD_STATE_HRPD_NETWORK_LOCKED);
   testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_CORPORATE,
-                      Ci.nsIIccProvider.CARD_STATE_RUIM_CORPORATE_LOCKED);
+                      Ci.nsIIcc.CARD_STATE_RUIM_CORPORATE_LOCKED);
   testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER,
-                      Ci.nsIIccProvider.CARD_STATE_RUIM_SERVICE_PROVIDER_LOCKED);
+                      Ci.nsIIcc.CARD_STATE_RUIM_SERVICE_PROVIDER_LOCKED);
   testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_RUIM,
-                      Ci.nsIIccProvider.CARD_STATE_RUIM_LOCKED);
+                      Ci.nsIIcc.CARD_STATE_RUIM_LOCKED);
   testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_NETWORK1_PUK,
-                      Ci.nsIIccProvider.CARD_STATE_NETWORK1_PUK_REQUIRED);
+                      Ci.nsIIcc.CARD_STATE_NETWORK1_PUK_REQUIRED);
   testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_NETWORK2_PUK,
-                      Ci.nsIIccProvider.CARD_STATE_NETWORK2_PUK_REQUIRED);
+                      Ci.nsIIcc.CARD_STATE_NETWORK2_PUK_REQUIRED);
   testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_HRPD_PUK,
-                      Ci.nsIIccProvider.CARD_STATE_HRPD_NETWORK_PUK_REQUIRED);
+                      Ci.nsIIcc.CARD_STATE_HRPD_NETWORK_PUK_REQUIRED);
   testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_CORPORATE_PUK,
-                      Ci.nsIIccProvider.CARD_STATE_RUIM_CORPORATE_PUK_REQUIRED);
+                      Ci.nsIIcc.CARD_STATE_RUIM_CORPORATE_PUK_REQUIRED);
   testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK,
-                      Ci.nsIIccProvider.CARD_STATE_RUIM_SERVICE_PROVIDER_PUK_REQUIRED);
+                      Ci.nsIIcc.CARD_STATE_RUIM_SERVICE_PROVIDER_PUK_REQUIRED);
   testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_RUIM_PUK,
-                      Ci.nsIIccProvider.CARD_STATE_RUIM_PUK_REQUIRED);
+                      Ci.nsIIcc.CARD_STATE_RUIM_PUK_REQUIRED);
 
   testPersonalization(true, CARD_PERSOSUBSTATE_UNKNOWN,
-                      Ci.nsIIccProvider.CARD_STATE_UNKNOWN);
+                      Ci.nsIIcc.CARD_STATE_UNKNOWN);
   testPersonalization(true, CARD_PERSOSUBSTATE_IN_PROGRESS,
-                      Ci.nsIIccProvider.CARD_STATE_PERSONALIZATION_IN_PROGRESS);
+                      Ci.nsIIcc.CARD_STATE_PERSONALIZATION_IN_PROGRESS);
   testPersonalization(true, CARD_PERSOSUBSTATE_READY,
-                      Ci.nsIIccProvider.CARD_STATE_PERSONALIZATION_READY);
+                      Ci.nsIIcc.CARD_STATE_PERSONALIZATION_READY);
 
   run_next_test();
 });
 
 /**
  * Verify SIM app_state in _processICCStatus
  */
 add_test(function test_card_app_state() {
@@ -116,27 +116,27 @@ add_test(function test_card_app_state() 
       }],
     };
 
     ril._processICCStatus(iccStatus);
     equal(ril.cardState, geckoCardState);
   }
 
   testCardAppState(CARD_APPSTATE_ILLEGAL,
-                   Ci.nsIIccProvider.CARD_STATE_ILLEGAL);
+                   Ci.nsIIcc.CARD_STATE_ILLEGAL);
   testCardAppState(CARD_APPSTATE_PIN,
-                   Ci.nsIIccProvider.CARD_STATE_PIN_REQUIRED);
+                   Ci.nsIIcc.CARD_STATE_PIN_REQUIRED);
   testCardAppState(CARD_APPSTATE_PUK,
-                   Ci.nsIIccProvider.CARD_STATE_PUK_REQUIRED);
+                   Ci.nsIIcc.CARD_STATE_PUK_REQUIRED);
   testCardAppState(CARD_APPSTATE_READY,
-                   Ci.nsIIccProvider.CARD_STATE_READY);
+                   Ci.nsIIcc.CARD_STATE_READY);
   testCardAppState(CARD_APPSTATE_UNKNOWN,
-                   Ci.nsIIccProvider.CARD_STATE_UNKNOWN);
+                   Ci.nsIIcc.CARD_STATE_UNKNOWN);
   testCardAppState(CARD_APPSTATE_DETECTED,
-                   Ci.nsIIccProvider.CARD_STATE_UNKNOWN);
+                   Ci.nsIIcc.CARD_STATE_UNKNOWN);
 
   run_next_test();
 });
 
 /**
  * Verify permanent blocked for ICC.
  */
 add_test(function test_icc_permanent_blocked() {
@@ -154,17 +154,17 @@ add_test(function test_icc_permanent_blo
       apps: [
       {
         pin1_replaced: pin1_replaced,
         pin1: pin1
       }]
     };
 
     ril._processICCStatus(iccStatus);
-    equal(ril.cardState, Ci.nsIIccProvider.CARD_STATE_PERMANENT_BLOCKED);
+    equal(ril.cardState, Ci.nsIIcc.CARD_STATE_PERMANENT_BLOCKED);
   }
 
   testPermanentBlocked(1,
                        CARD_PINSTATE_ENABLED_PERM_BLOCKED,
                        CARD_PINSTATE_UNKNOWN);
   testPermanentBlocked(1,
                        CARD_PINSTATE_ENABLED_PERM_BLOCKED,
                        CARD_PINSTATE_ENABLED_PERM_BLOCKED);
--- a/dom/webidl/FetchEvent.webidl
+++ b/dom/webidl/FetchEvent.webidl
@@ -12,17 +12,17 @@
  Exposed=(ServiceWorker)]
 interface FetchEvent : Event {
   readonly attribute Request request;
 
   // https://github.com/slightlyoff/ServiceWorker/issues/631
   readonly attribute Client? client; // The window issuing the request.
   readonly attribute boolean isReload;
 
-  [Throws] void respondWith(Promise<Response> r);
-  [Throws] void respondWith(Response r);
+  [Throws]
+  void respondWith((Response or Promise<Response>) r);
 };
 
 dictionary FetchEventInit : EventInit {
   Request request;
   Client client;
   boolean isReload;
 };
--- a/dom/webidl/MozIcc.webidl
+++ b/dom/webidl/MozIcc.webidl
@@ -124,16 +124,26 @@ dictionary IccSetCardLockOptions
 
  DOMString? newPin = null; // Used for changing password operation.
                            // Necessary for lock types: "pin", "pin2"
 
  boolean enabled; // Used for enabling/disabling operation.
                   // Necessary for lock types: "pin", "fdn"
 };
 
+dictionary IccCardLockStatus
+{
+  boolean enabled; // True when CardLock is enabled.
+};
+
+dictionary IccCardLockRetryCount
+{
+  long retryCount; // The number of remaining retries. -1 if unkown.
+};
+
 [Pref="dom.icc.enabled",
  CheckPermissions="mobileconnection",
  AvailableIn="CertifiedApps"]
 interface MozIcc : EventTarget
 {
   // Integrated Circuit Card Information.
 
   /**
@@ -242,16 +252,17 @@ interface MozIcc : EventTarget
    *
    * @param lockType
    *        Identifies the lock type.
    *
    * @return a DOMRequest.
    *         The request's result will be an object containing
    *         information about the specified lock's status.
    *         e.g. {enabled: true}.
+   *         @see IccCardLockStatus.
    */
   [Throws]
   DOMRequest getCardLock(IccLockType lockType);
 
   /**
    * Unlock a card lock.
    *
    * @param info
@@ -284,18 +295,18 @@ interface MozIcc : EventTarget
   /**
    * Retrieve the number of remaining tries for unlocking the card.
    *
    * @param lockType
    *        Identifies the lock type.
    *
    * @return a DOMRequest.
    *         The request's result will be an object containing the number of
-   *         remaining retries.
-   *         e.g. {retryCount: 3}.
+   *         remaining retries. e.g. {retryCount: 3}.
+   *         @see IccCardLockRetryCount.
    */
   [Throws]
   DOMRequest getCardLockRetryCount(IccLockType lockType);
 
   // Integrated Circuit Card Phonebook Interfaces.
 
   /**
    * Read ICC contacts.
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -224,16 +224,17 @@ WEBIDL_FILES = [
     'HTMLTableSectionElement.webidl',
     'HTMLTemplateElement.webidl',
     'HTMLTextAreaElement.webidl',
     'HTMLTimeElement.webidl',
     'HTMLTitleElement.webidl',
     'HTMLTrackElement.webidl',
     'HTMLUListElement.webidl',
     'HTMLVideoElement.webidl',
+    'IccCardLockError.webidl',
     'IDBCursor.webidl',
     'IDBDatabase.webidl',
     'IDBEnvironment.webidl',
     'IDBFactory.webidl',
     'IDBFileHandle.webidl',
     'IDBFileRequest.webidl',
     'IDBIndex.webidl',
     'IDBKeyRange.webidl',
@@ -282,16 +283,19 @@ WEBIDL_FILES = [
     'MimeType.webidl',
     'MimeTypeArray.webidl',
     'MMICall.webidl',
     'MouseEvent.webidl',
     'MouseScrollEvent.webidl',
     'MozActivity.webidl',
     'MozCellBroadcast.webidl',
     'MozCellBroadcastMessage.webidl',
+    'MozIcc.webidl',
+    'MozIccInfo.webidl',
+    'MozIccManager.webidl',
     'MozMmsMessage.webidl',
     'MozMobileCellInfo.webidl',
     'MozMobileConnection.webidl',
     'MozMobileConnectionArray.webidl',
     'MozMobileConnectionInfo.webidl',
     'MozMobileMessageManager.webidl',
     'MozMobileNetworkInfo.webidl',
     'MozPowerManager.webidl',
@@ -646,24 +650,16 @@ if CONFIG['MOZ_B2G_BT']:
         ]
     else:
         WEBIDL_FILES += [
             'BluetoothAdapter.webidl',
             'BluetoothDevice.webidl',
             'BluetoothManager.webidl',
         ]
 
-if CONFIG['MOZ_B2G_RIL']:
-    WEBIDL_FILES += [
-        'IccCardLockError.webidl',
-        'MozIcc.webidl',
-        'MozIccInfo.webidl',
-        'MozIccManager.webidl',
-    ]
-
 if CONFIG['MOZ_NFC']:
     WEBIDL_FILES += [
          'MozIsoDepTech.webidl',
          'MozNDEFRecord.webidl',
          'MozNFC.webidl',
          'MozNFCPeer.webidl',
          'MozNFCTag.webidl',
          'NfcOptions.webidl',
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -845,16 +845,26 @@ var WifiManager = (function() {
       }
       manager.handlePostWifiScan();
       notify("scanresultsavailable");
       return true;
     }
     if (eventData.indexOf("CTRL-EVENT-EAP") === 0) {
       return handleWpaEapEvents(event);
     }
+    if (eventData.indexOf("CTRL-EVENT-ASSOC-REJECT") === 0) {
+      debug("CTRL-EVENT-ASSOC-REJECT: network error");
+      notify("passwordmaybeincorrect");
+      if (manager.authenticationFailuresCount > MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
+        manager.authenticationFailuresCount = 0;
+        debug("CTRL-EVENT-ASSOC-REJECT: disconnect network");
+        notify("disconnected", {connectionInfo: manager.connectionInfo});
+      }
+      return true;
+    }
     if (eventData.indexOf("WPS-TIMEOUT") === 0) {
       notifyStateChange({ state: "WPS_TIMEOUT", BSSID: null, id: -1 });
       return true;
     }
     if (eventData.indexOf("WPS-FAIL") === 0) {
       notifyStateChange({ state: "WPS_FAIL", BSSID: null, id: -1 });
       return true;
     }
@@ -2157,17 +2167,20 @@ function WifiWorker() {
       case "ASSOCIATING":
         // id has not yet been filled in, so we can only report the ssid and
         // bssid. mode and frequency are simply made up.
         self.currentNetwork =
           { bssid: WifiManager.connectionInfo.bssid,
             ssid: quote(WifiManager.connectionInfo.ssid),
             mode: MODE_ESS,
             frequency: 0};
-        self._fireEvent("onconnecting", { network: netToDOM(self.currentNetwork) });
+        WifiManager.getNetworkConfiguration(self.currentNetwork, function (){
+          // Notify again because we get complete network information.
+          self._fireEvent("onconnecting", { network: netToDOM(self.currentNetwork) });
+        });
         break;
       case "ASSOCIATED":
         // set to full power mode when ready to do 4 way handsharke.
         WifiManager.setPowerSavingMode(false);
         if (!self.currentNetwork) {
           self.currentNetwork =
             { bssid: WifiManager.connectionInfo.bssid,
               ssid: quote(WifiManager.connectionInfo.ssid) };
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -277,47 +277,42 @@ RespondWithHandler::CancelRequest()
 {
   nsCOMPtr<nsIRunnable> runnable = new CancelChannelRunnable(mInterceptedChannel);
   NS_DispatchToMainThread(runnable);
 }
 
 } // anonymous namespace
 
 void
-FetchEvent::RespondWith(Promise& aPromise, ErrorResult& aRv)
+FetchEvent::RespondWith(const ResponseOrPromise& aArg, ErrorResult& aRv)
 {
   if (mWaitToRespond) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
+  nsRefPtr<Promise> promise;
+
+  if (aArg.IsResponse()) {
+    nsRefPtr<Response> res = &aArg.GetAsResponse();
+    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(worker);
+    worker->AssertIsOnWorkerThread();
+    promise = Promise::Create(worker->GlobalScope(), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+    promise->MaybeResolve(res);
+  } else if (aArg.IsPromise()) {
+    promise = &aArg.GetAsPromise();
+  }
   mWaitToRespond = true;
   nsRefPtr<RespondWithHandler> handler =
     new RespondWithHandler(mChannel, mServiceWorker, mRequest->Mode());
-  aPromise.AppendNativeHandler(handler);
-}
-
-void
-FetchEvent::RespondWith(Response& aResponse, ErrorResult& aRv)
-{
-  if (mWaitToRespond) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-
-  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
-  MOZ_ASSERT(worker);
-  worker->AssertIsOnWorkerThread();
-  nsRefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
-  }
-  promise->MaybeResolve(&aResponse);
-
-  RespondWith(*promise, aRv);
+  promise->AppendNativeHandler(handler);
 }
 
 already_AddRefed<ServiceWorkerClient>
 FetchEvent::GetClient()
 {
   if (!mClient) {
     if (!mClientInfo) {
       return nullptr;
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -14,16 +14,17 @@
 #include "mozilla/dom/Response.h"
 #include "nsProxyRelease.h"
 
 class nsIInterceptedChannel;
 
 namespace mozilla {
 namespace dom {
   class Request;
+  class ResponseOrPromise;
 } // namespace dom
 } // namespace mozilla
 
 BEGIN_WORKERS_NAMESPACE
 
 class ServiceWorker;
 class ServiceWorkerClient;
 
@@ -77,20 +78,17 @@ public:
 
   bool
   IsReload() const
   {
     return mIsReload;
   }
 
   void
-  RespondWith(Promise& aPromise, ErrorResult& aRv);
-
-  void
-  RespondWith(Response& aResponse, ErrorResult& aRv);
+  RespondWith(const ResponseOrPromise& aArg, ErrorResult& aRv);
 
   already_AddRefed<Promise>
   ForwardTo(const nsAString& aUrl);
 
   already_AddRefed<Promise>
   Default();
 };
 
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -2297,16 +2297,20 @@ private:
     reqInit.mMode.Construct(mRequestMode);
     reqInit.mCredentials.Construct(mRequestCredentials);
 
     ErrorResult rv;
     nsRefPtr<Request> request = Request::Constructor(globalObj, requestInfo, reqInit, rv);
     if (NS_WARN_IF(rv.Failed())) {
       return false;
     }
+    // For Telemetry, note that this Request object was created by a Fetch event.
+    nsRefPtr<InternalRequest> internalReq = request->GetInternalRequest();
+    MOZ_ASSERT(internalReq);
+    internalReq->SetCreatedByFetchEvent();
 
     RootedDictionary<FetchEventInit> init(aCx);
     init.mRequest.Construct();
     init.mRequest.Value() = request;
     init.mBubbles = false;
     init.mCancelable = true;
     init.mIsReload.Construct(mIsReload);
     nsRefPtr<FetchEvent> event =
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -844,19 +844,18 @@ LayerTransactionParent::Attach(ShadowLay
 bool
 LayerTransactionParent::RecvClearCachedResources()
 {
   if (mRoot) {
     // NB: |mRoot| here is the *child* context's root.  In this parent
     // context, it's just a subtree root.  We need to scope the clear
     // of resources to exactly that subtree, so we specify it here.
     mLayerManager->ClearCachedResources(mRoot);
-
-    mShadowLayersManager->NotifyClearCachedResources(this);
   }
+  mShadowLayersManager->NotifyClearCachedResources(this);
   return true;
 }
 
 bool
 LayerTransactionParent::RecvForceComposite()
 {
   mShadowLayersManager->ForceComposite(this);
   return true;
--- a/gfx/layers/ipc/PCompositor.ipdl
+++ b/gfx/layers/ipc/PCompositor.ipdl
@@ -24,19 +24,17 @@ namespace mozilla {
 namespace layers {
 
 
 /**
  * The PCompositor protocol is used to manage communication between
  * the main thread and the compositor thread context. It's primary
  * purpose is to manage the PLayerTransaction sub protocol.
  */
-// This should really be 'sync', but we're using 'rpc' as a workaround
-// for Bug 716631.
-intr protocol PCompositor
+sync protocol PCompositor
 {
   // A Compositor manages a single Layer Manager (PLayerTransaction)
   manages PLayerTransaction;
 
 child:
   // The child should invalidate everything so that the whole window is redrawn.
   async InvalidateAll();
 
--- a/gfx/layers/ipc/PImageBridge.ipdl
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -18,17 +18,17 @@ using mozilla::layers::TextureFlags from
 namespace mozilla {
 namespace layers {
 
 /**
  * The PImageBridge protocol is used to allow isolated threads or processes to push
  * frames directly to the compositor thread/process without relying on the main thread
  * which might be too busy dealing with content script.
  */
-intr protocol PImageBridge
+sync protocol PImageBridge
 {
   manages PCompositable;
   manages PTexture;
 
 child:
   async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
 
 parent:
--- a/gfx/tests/crashtests/358732-3.html
+++ b/gfx/tests/crashtests/358732-3.html
@@ -1,18 +1,17 @@
 <html>
 <head>
 <title>Testcase 3 - Bug 358732  Crash in _moz_cairo_win32_scaled_font_select_font, part 2</title>
 <script id="script">
 var i=0;
 function doe() {
-netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-var navigator1 = top.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebNavigation);
-var docShell = navigator1.QueryInterface(Components.interfaces.nsIDocShell);
-var docviewer = docShell.contentViewer;
+var navigator1 = SpecialPowers.wrap(top).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).getInterface(SpecialPowers.Ci.nsIWebNavigation);
+var docShell = navigator1.QueryInterface(SpecialPowers.Ci.nsIDocShell);
+var docviewer = docShell.contentViewer.QueryInterface(SpecialPowers.Ci.nsIMarkupDocumentViewer);
 docviewer.textZoom=i;
 i=i+0.2;
 if (i>10)
  i = 0;
 }
 setInterval(doe, 50);
 
 </script>
--- a/gfx/tests/crashtests/358732-iframe.html
+++ b/gfx/tests/crashtests/358732-iframe.html
@@ -7,20 +7,19 @@
 <script>
 var doc = document;
 if (document.getElementById('content')) {
   doc = document.getElementById('content').contentDocument;
 }
 
 var docviewer;
 function do_onload() {
-netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-var navigator = parent.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebNavigation);
-var docShell = navigator.QueryInterface(Components.interfaces.nsIDocShell);
-docviewer = docShell.contentViewer;
+var navigator1 = SpecialPowers.wrap(parent).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).getInterface(SpecialPowers.Ci.nsIWebNavigation);
+var docShell = navigator1.QueryInterface(SpecialPowers.Ci.nsIDocShell);
+docviewer = docShell.contentViewer.QueryInterface(SpecialPowers.Ci.nsIMarkupDocumentViewer);
 
 setTimeout(doe,500, 0.2);
 }
 do_onload();
 
 
 
 function doe(i) {
--- a/js/ipc/JavaScriptBase.h
+++ b/js/ipc/JavaScriptBase.h
@@ -66,20 +66,19 @@ class JavaScriptBase : public WrapperOwn
                       ReturnStatus *rs, bool *bp) {
         return Answer::RecvHasOwn(ObjectId::deserialize(objId), id, rs, bp);
     }
     bool RecvGet(const uint64_t &objId, const ObjectVariant &receiverVar,
                    const JSIDVariant &id,
                    ReturnStatus *rs, JSVariant *result) {
         return Answer::RecvGet(ObjectId::deserialize(objId), receiverVar, id, rs, result);
     }
-    bool RecvSet(const uint64_t &objId, const ObjectVariant &receiverVar,
-                 const JSIDVariant &id, const JSVariant &value, ReturnStatus *rs,
-                 JSVariant *result) {
-        return Answer::RecvSet(ObjectId::deserialize(objId), receiverVar, id, value, rs, result);
+    bool RecvSet(const uint64_t &objId, const JSIDVariant &id, const JSVariant &value,
+                 const JSVariant &receiverVar, ReturnStatus *rs) {
+        return Answer::RecvSet(ObjectId::deserialize(objId), id, value, receiverVar, rs);
     }
 
     bool RecvIsExtensible(const uint64_t &objId, ReturnStatus *rs,
                             bool *result) {
         return Answer::RecvIsExtensible(ObjectId::deserialize(objId), rs, result);
     }
     bool RecvCallOrConstruct(const uint64_t &objId, InfallibleTArray<JSParam> &&argv,
                              const bool &construct, ReturnStatus *rs, JSVariant *result,
@@ -156,20 +155,19 @@ class JavaScriptBase : public WrapperOwn
                     ReturnStatus *rs, bool *bp) {
         return Base::SendHasOwn(objId.serialize(), id, rs, bp);
     }
     bool SendGet(const ObjectId &objId, const ObjectVariant &receiverVar,
                  const JSIDVariant &id,
                  ReturnStatus *rs, JSVariant *result) {
         return Base::SendGet(objId.serialize(), receiverVar, id, rs, result);
     }
-    bool SendSet(const ObjectId &objId, const ObjectVariant &receiverVar,
-                 const JSIDVariant &id, const JSVariant &value, ReturnStatus *rs,
-                 JSVariant *result) {
-        return Base::SendSet(objId.serialize(), receiverVar, id, value, rs, result);
+    bool SendSet(const ObjectId &objId, const JSIDVariant &id, const JSVariant &value,
+                 const JSVariant &receiverVar, ReturnStatus *rs) {
+        return Base::SendSet(objId.serialize(), id, value, receiverVar, rs);
     }
 
     bool SendIsExtensible(const ObjectId &objId, ReturnStatus *rs,
                           bool *result) {
         return Base::SendIsExtensible(objId.serialize(), rs, result);
     }
     bool SendCallOrConstruct(const ObjectId &objId, const nsTArray<JSParam> &argv,
                              const bool &construct, ReturnStatus *rs, JSVariant *result,
--- a/js/ipc/PJavaScript.ipdl
+++ b/js/ipc/PJavaScript.ipdl
@@ -28,17 +28,17 @@ both:
     prio(high) sync GetPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
     prio(high) sync GetOwnPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
     prio(high) sync DefineProperty(uint64_t objId, JSIDVariant id, PPropertyDescriptor descriptor) returns (ReturnStatus rs);
     prio(high) sync Delete(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs);
 
     prio(high) sync Has(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
     prio(high) sync HasOwn(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
     prio(high) sync Get(uint64_t objId, ObjectVariant receiver, JSIDVariant id) returns (ReturnStatus rs, JSVariant result);
-    prio(high) sync Set(uint64_t objId, ObjectVariant receiver, JSIDVariant id, JSVariant value) returns (ReturnStatus rs, JSVariant result);
+    prio(high) sync Set(uint64_t objId, JSIDVariant id, JSVariant value, JSVariant receiver) returns (ReturnStatus rs);
 
     prio(high) sync IsExtensible(uint64_t objId) returns (ReturnStatus rs, bool result);
     prio(high) sync CallOrConstruct(uint64_t objId, JSParam[] argv, bool construct) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
     prio(high) sync HasInstance(uint64_t objId, JSVariant v) returns (ReturnStatus rs, bool has);
     prio(high) sync ObjectClassIs(uint64_t objId, uint32_t classValue) returns (bool result);
     prio(high) sync ClassName(uint64_t objId) returns (nsString name);
     prio(high) sync GetPrototype(uint64_t objId) returns (ReturnStatus rs, ObjectOrNullVariant result);
     prio(high) sync RegExpToShared(uint64_t objId) returns (ReturnStatus rs, nsString source, uint32_t flags);
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -303,52 +303,43 @@ WrapperAnswer::RecvGet(const ObjectId &o
         return fail(cx, rs);
 
     LOG("get %s.%s = %s", ReceiverObj(objId), Identifier(idVar), OutVariant(*result));
 
     return ok(rs);
 }
 
 bool
-WrapperAnswer::RecvSet(const ObjectId &objId, const ObjectVariant &receiverVar,
-                       const JSIDVariant &idVar, const JSVariant &value, ReturnStatus *rs,
-                       JSVariant *resultValue)
+WrapperAnswer::RecvSet(const ObjectId &objId, const JSIDVariant &idVar, const JSVariant &value,
+                       const JSVariant &receiverVar, ReturnStatus *rs)
 {
     // We may run scripted setters.
     AutoEntryScript aes(xpc::NativeGlobal(scopeForTargetObjects()));
     JSContext *cx = aes.cx();
 
-    // The outparam will be written to the buffer, so it must be set even if
-    // the parent won't read it.
-    *resultValue = UndefinedVariant();
-
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
-    RootedObject receiver(cx, fromObjectVariant(cx, receiverVar));
-    if (!receiver)
-        return fail(cx, rs);
-
     LOG("set %s[%s] = %s", ReceiverObj(objId), Identifier(idVar), InVariant(value));
 
     RootedId id(cx);
     if (!fromJSIDVariant(cx, idVar, &id))
         return fail(cx, rs);
 
     RootedValue val(cx);
     if (!fromVariant(cx, value, &val))
         return fail(cx, rs);
 
-    ObjectOpResult result;
-    RootedValue receiverVal(cx, ObjectValue(*receiver));
-    if (!JS_ForwardSetPropertyTo(cx, obj, id, val, receiverVal, result))
+    RootedValue receiver(cx);
+    if (!fromVariant(cx, receiverVar, &receiver))
         return fail(cx, rs);
 
-    if (!toVariant(cx, val, resultValue))
+    ObjectOpResult result;
+    if (!JS_ForwardSetPropertyTo(cx, obj, id, val, receiver, result))
         return fail(cx, rs);
 
     return ok(rs, result);
 }
 
 bool
 WrapperAnswer::RecvIsExtensible(const ObjectId &objId, ReturnStatus *rs, bool *result)
 {
--- a/js/ipc/WrapperAnswer.h
+++ b/js/ipc/WrapperAnswer.h
@@ -32,19 +32,18 @@ class WrapperAnswer : public virtual Jav
 
     bool RecvHas(const ObjectId &objId, const JSIDVariant &id,
                  ReturnStatus *rs, bool *bp);
     bool RecvHasOwn(const ObjectId &objId, const JSIDVariant &id,
                     ReturnStatus *rs, bool *bp);
     bool RecvGet(const ObjectId &objId, const ObjectVariant &receiverVar,
                  const JSIDVariant &id,
                  ReturnStatus *rs, JSVariant *result);
-    bool RecvSet(const ObjectId &objId, const ObjectVariant &receiverVar,
-                 const JSIDVariant &id, const JSVariant &value, ReturnStatus *rs,
-                 JSVariant *result);
+    bool RecvSet(const ObjectId &objId, const JSIDVariant &id, const JSVariant &value,
+                 const JSVariant &receiverVar, ReturnStatus *rs);
 
     bool RecvIsExtensible(const ObjectId &objId, ReturnStatus *rs,
                           bool *result);
     bool RecvCallOrConstruct(const ObjectId &objId, InfallibleTArray<JSParam> &&argv,
                              const bool &construct, ReturnStatus *rs, JSVariant *result,
                              nsTArray<JSParam> *outparams);
     bool RecvHasInstance(const ObjectId &objId, const JSVariant &v, ReturnStatus *rs, bool *bp);
     bool RecvObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -105,19 +105,18 @@ class CPOWProxyHandler : public BaseProx
                          ObjectOpResult &result) const override;
     virtual bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const override;
     virtual bool preventExtensions(JSContext *cx, HandleObject proxy,
                                    ObjectOpResult &result) const override;
     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const override;
     virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const override;
     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
                      HandleId id, MutableHandleValue vp) const override;
-    virtual bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
-                     JS::HandleId id, JS::MutableHandleValue vp,
<