Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 24 Feb 2014 13:16:16 +0100
changeset 170407 0f152676a9f184fe63743a269498744e795ad337
parent 170406 9f37968287a7c60360b30a1162066483124e78fa (current diff)
parent 170161 d9c58306bfbc74c920aa5c995b86b5f8a398352b (diff)
child 170408 cf82ac9f81410023b6c76fd8c36f2dd88bf42030
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
milestone30.0a1
Merge mozilla-central to fx-team
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -7,17 +7,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <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="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="ffb527b84594396ed611edf0a8a5a130d60a742f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="022eadd5917615ff00c47eaaafa792b45e9c8a28"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <!-- 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
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="97a5b461686757dbb8ecab2aac5903e41d2e1afe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ffb527b84594396ed611edf0a8a5a130d60a742f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- 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/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -7,17 +7,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <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="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="ffb527b84594396ed611edf0a8a5a130d60a742f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="022eadd5917615ff00c47eaaafa792b45e9c8a28"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "894a7f2886a7a727ce03edadbbea936ceb4aaeba", 
+    "revision": "35ef07425e808811af0462a6ba08c36409236846", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="ffb527b84594396ed611edf0a8a5a130d60a742f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -5,17 +5,17 @@
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="ffb527b84594396ed611edf0a8a5a130d60a742f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -7,17 +7,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="ffb527b84594396ed611edf0a8a5a130d60a742f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="ffb527b84594396ed611edf0a8a5a130d60a742f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="97a5b461686757dbb8ecab2aac5903e41d2e1afe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ffb527b84594396ed611edf0a8a5a130d60a742f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- 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/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <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="ffb527b84594396ed611edf0a8a5a130d60a742f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0eadf61ef60f13324fe8290d8c2b516d98230fdc"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/base/content/browser-charsetmenu.inc
+++ b/browser/base/content/browser-charsetmenu.inc
@@ -1,21 +1,19 @@
 # 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/.
 
-#filter substitution
-
-#expand <menu id="__ID_PREFIX__charsetMenu"
+<menu id="charsetMenu"
     label="&charsetMenu.label;"
 #ifndef OMIT_ACCESSKEYS
     accesskey="&charsetMenu.accesskey;"
 #endif
-    oncommand="MultiplexHandler(event)"
+    oncommand="BrowserSetForcedCharacterSet(event.target.getAttribute('charset'));"
 #ifdef OMIT_ACCESSKEYS
-#expand    onpopupshowing="CharsetMenu.build(event.target, '__ID_PREFIX__', false);"
+    onpopupshowing="CharsetMenu.build(event.target, false);"
 #else
-#expand    onpopupshowing="CharsetMenu.build(event.target, '__ID_PREFIX__');"
+    onpopupshowing="CharsetMenu.build(event.target);"
 #endif
-    onpopupshown="UpdateMenus(event);">
+    onpopupshown="UpdateCurrentCharset(this);">
   <menupopup>
   </menupopup>
 </menu>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5134,132 +5134,57 @@ function handleDroppedLink(event, url, n
       loadURI(data.url, null, data.postData, false);
   });
 
   // Keep the event from being handled by the dragDrop listeners
   // built-in to gecko if they happen to be above us.
   event.preventDefault();
 };
 
-function MultiplexHandler(event)
-{ try {
-    var node = event.target;
-    var name = node.getAttribute('name');
-
-    if (name == 'detectorGroup') {
-        BrowserCharsetReload();
-        SelectDetector(event, false);
-    } else if (name == 'charsetGroup') {
-        var charset = node.getAttribute('id');
-        charset = charset.substring(charset.indexOf('charset.') + 'charset.'.length);
-        BrowserSetForcedCharacterSet(charset);
-    } else if (name == 'charsetCustomize') {
-        //do nothing - please remove this else statement, once the charset prefs moves to the pref window
-    } else {
-        BrowserSetForcedCharacterSet(node.getAttribute('id'));
-    }
-    } catch(ex) { alert(ex); }
-}
-
-function SelectDetector(event, doReload)
-{
-    var uri =  event.target.getAttribute("id");
-    var prefvalue = uri.substring(uri.indexOf('chardet.') + 'chardet.'.length);
-    if ("off" == prefvalue) { // "off" is special value to turn off the detectors
-        prefvalue = "";
-    }
-
-    try {
-        var str =  Cc["@mozilla.org/supports-string;1"].
-                   createInstance(Ci.nsISupportsString);
-
-        str.data = prefvalue;
-        gPrefService.setComplexValue("intl.charset.detector", Ci.nsISupportsString, str);
-        if (doReload)
-          window.content.location.reload();
-    }
-    catch (ex) {
-        dump("Failed to set the intl.charset.detector preference.\n");
-    }
-}
-
 function BrowserSetForcedCharacterSet(aCharset)
 {
-  gBrowser.docShell.gatherCharsetMenuTelemetry();
-  gBrowser.docShell.charset = aCharset;
-  // Save the forced character-set
-  if (!PrivateBrowsingUtils.isWindowPrivate(window))
-    PlacesUtils.setCharsetForURI(getWebNavigation().currentURI, aCharset);
+  if (aCharset) {
+    gBrowser.docShell.gatherCharsetMenuTelemetry();
+    gBrowser.docShell.charset = aCharset;
+    // Save the forced character-set
+    if (!PrivateBrowsingUtils.isWindowPrivate(window))
+      PlacesUtils.setCharsetForURI(getWebNavigation().currentURI, aCharset);
+  }
   BrowserCharsetReload();
 }
 
 function BrowserCharsetReload()
 {
   BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
 }
 
-function charsetMenuGetElement(parent, id) {
-  return parent.getElementsByAttribute("id", id)[0];
+function charsetMenuGetElement(parent, charset) {
+  return parent.getElementsByAttribute("charset", charset)[0];
 }
 
 function UpdateCurrentCharset(target) {
     // extract the charset from DOM
     var wnd = document.commandDispatcher.focusedWindow;
     if ((window == wnd) || (wnd == null)) wnd = window.content;
 
     // Uncheck previous item
     if (gPrevCharset) {
-        var pref_item = charsetMenuGetElement(target, "charset." + gPrevCharset);
+        var pref_item = charsetMenuGetElement(target, gPrevCharset);
         if (pref_item)
           pref_item.setAttribute('checked', 'false');
     }
 
-    var menuitem = charsetMenuGetElement(target, "charset." + FoldCharset(wnd.document.characterSet));
+    var menuitem = charsetMenuGetElement(target, CharsetMenu.foldCharset(wnd.document.characterSet));
     if (menuitem) {
         menuitem.setAttribute('checked', 'true');
     }
 }
 
-function FoldCharset(charset) {
-  // For substantially similar encodings, treat two encodings as the same
-  // for the purpose of the check mark.
-  if (charset == "ISO-8859-8-I") {
-    return "windows-1255";
-  }
-
-  if (charset == "gb18030") {
-    return "gbk";
-  }
-
-  return charset;
-}
-
-function UpdateCharsetDetector(target) {
-  var prefvalue;
-
-  try {
-    prefvalue = gPrefService.getComplexValue("intl.charset.detector", Ci.nsIPrefLocalizedString).data;
-  }
-  catch (ex) {}
-
-  if (!prefvalue)
-    prefvalue = "off";
-
-  var menuitem = charsetMenuGetElement(target, "chardet." + prefvalue);
-  if (menuitem)
-    menuitem.setAttribute("checked", "true");
-}
-
-function UpdateMenus(event) {
-  UpdateCurrentCharset(event.target);
-  UpdateCharsetDetector(event.target);
-}
-
 function charsetLoadListener() {
-  var charset = FoldCharset(window.content.document.characterSet);
+  var charset = CharsetMenu.foldCharset(window.content.document.characterSet);
 
   if (charset.length > 0 && (charset != gLastBrowserCharset)) {
     gPrevCharset = gLastBrowserCharset;
     gLastBrowserCharset = charset;
   }
 }
 
 var gPageStyleMenu = {
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -954,17 +954,18 @@ protected:
    *
    * For the boolean parameters, consider using the named bools above to aid
    * code readability.
    *
    * @param aNamespaceID  namespace of attribute
    * @param aAttribute    local-name of attribute
    * @param aPrefix       aPrefix of attribute
    * @param aOldValue     previous value of attribute. Only needed if
-   *                      aFireMutation is true.
+   *                      aFireMutation is true or if the element is a
+   *                      custom element (in web components).
    * @param aParsedValue  parsed new value of attribute
    * @param aModType      nsIDOMMutationEvent::MODIFICATION or ADDITION.  Only
    *                      needed if aFireMutation or aNotify is true.
    * @param aFireMutation should mutation-events be fired?
    * @param aNotify       should we notify document-observers?
    * @param aCallAfterSetAttr should we call AfterSetAttr?
    */
   nsresult SetAttrAndNotify(int32_t aNamespaceID,
--- a/content/base/public/FragmentOrElement.h
+++ b/content/base/public/FragmentOrElement.h
@@ -202,20 +202,23 @@ public:
   virtual void AppendTextTo(nsAString& aResult) MOZ_OVERRIDE;
   virtual nsIContent *GetBindingParent() const MOZ_OVERRIDE;
   virtual nsXBLBinding *GetXBLBinding() const MOZ_OVERRIDE;
   virtual void SetXBLBinding(nsXBLBinding* aBinding,
                              nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
   virtual ShadowRoot *GetShadowRoot() const MOZ_OVERRIDE;
   virtual ShadowRoot *GetContainingShadow() const MOZ_OVERRIDE;
   virtual void SetShadowRoot(ShadowRoot* aBinding) MOZ_OVERRIDE;
-  virtual nsIContent *GetXBLInsertionParent() const;
-  virtual void SetXBLInsertionParent(nsIContent* aContent);
+  virtual nsIContent *GetXBLInsertionParent() const MOZ_OVERRIDE;
+  virtual void SetXBLInsertionParent(nsIContent* aContent) MOZ_OVERRIDE;
   virtual bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE;
 
+  virtual CustomElementData *GetCustomElementData() const MOZ_OVERRIDE;
+  virtual void SetCustomElementData(CustomElementData* aData) MOZ_OVERRIDE;
+
   virtual void DestroyContent() MOZ_OVERRIDE;
   virtual void SaveSubtreeState() MOZ_OVERRIDE;
 
   virtual const nsAttrValue* DoGetClasses() const MOZ_OVERRIDE;
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) MOZ_OVERRIDE;
 
   nsIHTMLCollection* Children();
   uint32_t ChildElementCount()
@@ -373,16 +376,21 @@ public:
      * XBL binding installed on the element.
      */
     nsRefPtr<nsXBLBinding> mXBLBinding;
 
     /**
      * XBL binding installed on the lement.
      */
     nsCOMPtr<nsIContent> mXBLInsertionParent;
+
+    /**
+     * Web components custom element data.
+     */
+    nsAutoPtr<CustomElementData> mCustomElementData;
   };
 
 protected:
   void GetMarkup(bool aIncludeSelf, nsAString& aMarkup);
   void SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError);
 
   // Override from nsINode
   virtual nsINode::nsSlots* CreateSlots() MOZ_OVERRIDE;
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -522,16 +522,22 @@ public:
    * @param aBuffer the buffer to check
    * @param aLength the length of the buffer
    * @param aCharset empty if not found
    * @return boolean indicating whether a BOM was detected.
    */
   static bool CheckForBOM(const unsigned char* aBuffer, uint32_t aLength,
                           nsACString& aCharset);
 
+  /**
+   * Returns true if |aName| is a valid name to be registered via
+   * document.registerElement.
+   */
+  static bool IsCustomElementName(nsIAtom* aName);
+
   static nsresult CheckQName(const nsAString& aQualifiedName,
                              bool aNamespaceAware = true,
                              const char16_t** aColon = nullptr);
 
   static nsresult SplitQName(const nsIContent* aNamespaceResolver,
                              const nsAFlatString& aQName,
                              int32_t *aNamespace, nsIAtom **aLocalName);
 
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -18,16 +18,17 @@ class nsAttrValue;
 class nsAttrName;
 class nsTextFragment;
 class nsIFrame;
 class nsXBLBinding;
 
 namespace mozilla {
 namespace dom {
 class ShadowRoot;
+struct CustomElementData;
 } // namespace dom
 namespace widget {
 struct IMEState;
 } // namespace widget
 } // namespace mozilla
 
 enum nsLinkState {
   eLinkState_Unvisited  = 1,
@@ -672,16 +673,32 @@ public:
    * tree. For nodes that are not filtered into an insertion point, this
    * simply returns their DOM parent in the original DOM tree.
    *
    * @return the flattened tree parent
    */
   nsIContent *GetFlattenedTreeParent() const;
 
   /**
+   * Gets the custom element data used by web components custom element.
+   * Custom element data is created at the first attempt to enqueue a callback.
+   *
+   * @return The custom element data or null if none.
+   */
+  virtual mozilla::dom::CustomElementData *GetCustomElementData() const = 0;
+
+  /**
+   * Sets the custom element data, ownership of the
+   * callback data is taken by this content.
+   *
+   * @param aCallbackData The custom element data.
+   */
+  virtual void SetCustomElementData(mozilla::dom::CustomElementData* aData) = 0;
+
+  /**
    * API to check if this is a link that's traversed in response to user input
    * (e.g. a click event). Specializations for HTML/SVG/generic XML allow for
    * different types of link in different types of content.
    *
    * @param aURI Required out param. If this content is a link, a new nsIURI
    *             set to this link's URI will be passed out.
    *
    * @note The out param, aURI, is guaranteed to be set to a non-null pointer
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -91,24 +91,26 @@ namespace css {
 class Loader;
 class ImageLoader;
 } // namespace css
 
 namespace dom {
 class Attr;
 class CDATASection;
 class Comment;
+struct CustomElementDefinition;
 class DocumentFragment;
 class DocumentType;
 class DOMImplementation;
 class Element;
 struct ElementRegistrationOptions;
 class EventTarget;
 class FrameRequestCallback;
 class HTMLBodyElement;
+struct LifecycleCallbackArgs;
 class Link;
 class GlobalObject;
 class NodeFilter;
 class NodeIterator;
 class ProcessingInstruction;
 class Touch;
 class TreeWalker;
 class UndoManager;
@@ -117,18 +119,18 @@ template<typename> class OwningNonNull;
 template<typename> class Sequence;
 
 template<typename, typename> class CallbackObjectHolder;
 typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> NodeFilterHolder;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0x56a350f4, 0xc286, 0x440c, \
-  { 0x85, 0xb1, 0xb6, 0x55, 0x77, 0xeb, 0x63, 0xfd } }
+{ 0x595492bc, 0xa26d, 0x46a9, \
+  { 0xa9, 0x35, 0x0c, 0x40, 0xdd, 0xc2, 0x77, 0x51 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
@@ -1981,20 +1983,46 @@ public:
   void GetCompatMode(nsString& retval) const;
   void GetCharacterSet(nsAString& retval) const;
   // Skip GetContentType, because our NS_IMETHOD version above works fine here.
   // GetDoctype defined above
   Element* GetDocumentElement() const
   {
     return GetRootElement();
   }
+
+  enum ElementCallbackType {
+    eCreated,
+    eEnteredView,
+    eLeftView,
+    eAttributeChanged
+  };
+
+  /**
+   * Registers an unresolved custom element that is a candidate for
+   * upgrade when the definition is registered via registerElement.
+   * |aTypeName| is the name of the custom element type, if it is not
+   * provided, then element name is used. |aTypeName| should be provided
+   * when registering a custom element that extends an existing
+   * element. e.g. <button is="x-button">.
+   */
+  virtual nsresult RegisterUnresolvedElement(Element* aElement,
+                                             nsIAtom* aTypeName = nullptr) = 0;
+  virtual void EnqueueLifecycleCallback(ElementCallbackType aType,
+                                        Element* aCustomElement,
+                                        mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
+                                        mozilla::dom::CustomElementDefinition* aDefinition = nullptr) = 0;
+  virtual void SwizzleCustomElement(Element* aElement,
+                                    const nsAString& aTypeExtension,
+                                    uint32_t aNamespaceID,
+                                    mozilla::ErrorResult& rv) = 0;
   virtual JSObject*
-  Register(JSContext* aCx, const nsAString& aName,
-           const mozilla::dom::ElementRegistrationOptions& aOptions,
-           mozilla::ErrorResult& rv) = 0;
+    RegisterElement(JSContext* aCx, const nsAString& aName,
+                    const mozilla::dom::ElementRegistrationOptions& aOptions,
+                    mozilla::ErrorResult& rv) = 0;
   already_AddRefed<nsContentList>
   GetElementsByTagName(const nsAString& aTagName)
   {
     return NS_GetContentList(this, kNameSpaceID_Unknown, aTagName);
   }
   already_AddRefed<nsContentList>
     GetElementsByTagNameNS(const nsAString& aNamespaceURI,
                            const nsAString& aLocalName,
@@ -2002,16 +2030,23 @@ public:
   already_AddRefed<nsContentList>
     GetElementsByClassName(const nsAString& aClasses);
   // GetElementById defined above
   already_AddRefed<Element> CreateElement(const nsAString& aTagName,
                                           mozilla::ErrorResult& rv);
   already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
                                             const nsAString& aQualifiedName,
                                             mozilla::ErrorResult& rv);
+  virtual already_AddRefed<Element> CreateElement(const nsAString& aTagName,
+                                                  const nsAString& aTypeExtension,
+                                                  mozilla::ErrorResult& rv) = 0;
+  virtual already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
+                                                    const nsAString& aQualifiedName,
+                                                    const nsAString& aTypeExtension,
+                                                    mozilla::ErrorResult& rv) = 0;
   already_AddRefed<mozilla::dom::DocumentFragment>
     CreateDocumentFragment() const;
   already_AddRefed<nsTextNode> CreateTextNode(const nsAString& aData) const;
   already_AddRefed<mozilla::dom::Comment>
     CreateComment(const nsAString& aData) const;
   already_AddRefed<mozilla::dom::ProcessingInstruction>
     CreateProcessingInstruction(const nsAString& target, const nsAString& data,
                                 mozilla::ErrorResult& rv) const;
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -356,16 +356,32 @@ Element::GetBindingURL(nsIDocument *aDoc
 JSObject*
 Element::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aScope)
 {
   JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx, aScope));
   if (!obj) {
     return nullptr;
   }
 
+  // Custom element prototype swizzling.
+  CustomElementData* data = GetCustomElementData();
+  if (obj && data) {
+    // If this is a registered custom element then fix the prototype.
+    JSAutoCompartment ac(aCx, obj);
+    nsDocument* document = static_cast<nsDocument*>(OwnerDoc());
+    JS::Rooted<JSObject*> prototype(aCx);
+    document->GetCustomPrototype(NodeInfo()->NamespaceID(), data->mType, &prototype);
+    if (prototype) {
+      if (!JS_WrapObject(aCx, &prototype) || !JS_SetPrototype(aCx, obj, prototype)) {
+        dom::Throw(aCx, NS_ERROR_FAILURE);
+        return nullptr;
+      }
+    }
+  }
+
   nsIDocument* doc;
   if (HasFlag(NODE_FORCE_XBL_BINDINGS)) {
     doc = OwnerDoc();
   }
   else {
     doc = GetCurrentDoc();
   }
 
@@ -1136,16 +1152,21 @@ Element::BindToTree(nsIDocument* aDocume
 
     // We no longer need to track the subtree pointer (and in fact we'll assert
     // if we do this any later).
     ClearSubtreeRootPointer();
 
     // Being added to a document.
     SetInDocument();
 
+    if (GetCustomElementData()) {
+      // Enqueue an enteredView callback for the custom element.
+      aDocument->EnqueueLifecycleCallback(nsIDocument::eEnteredView, this);
+    }
+
     // Unset this flag since we now really are in a document.
     UnsetFlags(NODE_FORCE_XBL_BINDINGS |
                // And clear the lazy frame construction bits.
                NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
                // And the restyle bits
                ELEMENT_ALL_RESTYLE_FLAGS);
 
     // Propagate scoped style sheet tracking bit.
@@ -1293,16 +1314,21 @@ Element::UnbindFromTree(bool aDeep, bool
     // anonymous content that the document is changing.
     if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
       nsContentUtils::AddScriptRunner(
         new RemoveFromBindingManagerRunnable(document->BindingManager(), this,
                                              document));
     }
 
     document->ClearBoxObjectFor(this);
+
+    if (GetCustomElementData()) {
+      // Enqueue a leftView callback for the custom element.
+      document->EnqueueLifecycleCallback(nsIDocument::eLeftView, this);
+    }
   }
 
   // Ensure that CSS transitions don't continue on an element at a
   // different place in the tree (even if reinserted before next
   // animation refresh).
   // FIXME (Bug 522599): Need a test for this.
   if (HasFlag(NODE_HAS_PROPERTIES)) {
     DeleteProperty(nsGkAtoms::transitionsOfBeforeProperty);
@@ -1660,18 +1686,20 @@ Element::MaybeCheckSameAttrVal(int32_t a
   // coming from the content sink and will almost certainly have no previous
   // value.  Even if we do, setting the value is cheap when we have no
   // listeners and don't plan to notify.  The check for aNotify here is an
   // optimization, the check for *aHasListeners is a correctness issue.
   if (*aHasListeners || aNotify) {
     nsAttrInfo info(GetAttrInfo(aNamespaceID, aName));
     if (info.mValue) {
       // Check whether the old value is the same as the new one.  Note that we
-      // only need to actually _get_ the old value if we have listeners.
-      if (*aHasListeners) {
+      // only need to actually _get_ the old value if we have listeners or
+      // if the element is a custom element (because it may have an
+      // attribute changed callback).
+      if (*aHasListeners || GetCustomElementData()) {
         // Need to store the old value.
         //
         // If the current attribute value contains a pointer to some other data
         // structure that gets updated in the process of setting the attribute
         // we'll no longer have the old value of the attribute. Therefore, we
         // should serialize the attribute value now to keep a snapshot.
         //
         // We have to serialize the value anyway in order to create the
@@ -1847,16 +1875,30 @@ Element::SetAttrAndNotify(int32_t aNames
     nsRefPtr<nsXBLBinding> binding = GetXBLBinding();
     if (binding) {
       binding->AttributeChanged(aName, aNamespaceID, false, aNotify);
     }
   }
 
   UpdateState(aNotify);
 
+  nsIDocument* ownerDoc = OwnerDoc();
+  if (ownerDoc && GetCustomElementData()) {
+    nsCOMPtr<nsIAtom> oldValueAtom = aOldValue.GetAsAtom();
+    nsCOMPtr<nsIAtom> newValueAtom = aValueForAfterSetAttr.GetAsAtom();
+    LifecycleCallbackArgs args = {
+      nsDependentAtomString(aName),
+      aModType == nsIDOMMutationEvent::ADDITION ?
+        NullString() : nsDependentAtomString(oldValueAtom),
+      nsDependentAtomString(newValueAtom)
+    };
+
+    ownerDoc->EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, this, &args);
+  }
+
   if (aCallAfterSetAttr) {
     rv = AfterSetAttr(aNamespaceID, aName, &aValueForAfterSetAttr, aNotify);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
       OnSetDirAttr(this, &aValueForAfterSetAttr,
                    hadValidDir, hadDirAuto, aNotify);
     }
@@ -2030,16 +2072,28 @@ Element::UnsetAttr(int32_t aNameSpaceID,
     nsRefPtr<nsXBLBinding> binding = GetXBLBinding();
     if (binding) {
       binding->AttributeChanged(aName, aNameSpaceID, true, aNotify);
     }
   }
 
   UpdateState(aNotify);
 
+  nsIDocument* ownerDoc = OwnerDoc();
+  if (ownerDoc && GetCustomElementData()) {
+    nsCOMPtr<nsIAtom> oldValueAtom = oldValue.GetAsAtom();
+    LifecycleCallbackArgs args = {
+      nsDependentAtomString(aName),
+      nsDependentAtomString(oldValueAtom),
+      NullString()
+    };
+
+    ownerDoc->EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, this, &args);
+  }
+
   if (aNotify) {
     nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
                                   nsIDOMMutationEvent::REMOVAL);
   }
 
   rv = AfterSetAttr(aNameSpaceID, aName, nullptr, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -558,16 +558,22 @@ FragmentOrElement::nsDOMSlots::Traverse(
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
   cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
 
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
   cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
 
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
   cb.NoteXPCOMChild(mClassList.get());
+
+  if (mCustomElementData) {
+    for (uint32_t i = 0; i < mCustomElementData->mCallbackQueue.Length(); i++) {
+      mCustomElementData->mCallbackQueue[i]->Traverse(cb);
+    }
+  }
 }
 
 void
 FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL)
 {
   mStyle = nullptr;
   mSMILOverrideStyle = nullptr;
   if (mAttributeMap) {
@@ -577,16 +583,17 @@ FragmentOrElement::nsDOMSlots::Unlink(bo
   if (aIsXUL)
     NS_IF_RELEASE(mControllers);
   mXBLBinding = nullptr;
   mXBLInsertionParent = nullptr;
   mShadowRoot = nullptr;
   mContainingShadow = nullptr;
   mChildrenList = nullptr;
   mUndoManager = nullptr;
+  mCustomElementData = nullptr;
   if (mClassList) {
     mClassList->DropReference();
     mClassList = nullptr;
   }
 }
 
 size_t
 FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
@@ -1008,16 +1015,34 @@ FragmentOrElement::SetXBLInsertionParent
 {
   nsDOMSlots *slots = DOMSlots();
   if (aContent) {
     SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
   }
   slots->mXBLInsertionParent = aContent;
 }
 
+CustomElementData*
+FragmentOrElement::GetCustomElementData() const
+{
+  nsDOMSlots *slots = GetExistingDOMSlots();
+  if (slots) {
+    return slots->mCustomElementData;
+  }
+  return nullptr;
+}
+
+void
+FragmentOrElement::SetCustomElementData(CustomElementData* aData)
+{
+  nsDOMSlots *slots = DOMSlots();
+  MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
+  slots->mCustomElementData = aData;
+}
+
 nsresult
 FragmentOrElement::InsertChildAt(nsIContent* aKid,
                                 uint32_t aIndex,
                                 bool aNotify)
 {
   NS_PRECONDITION(aKid, "null ptr");
 
   return doInsertChildAt(aKid, aIndex, aNotify, mAttrsAndChildren);
--- a/content/base/src/moz.build
+++ b/content/base/src/moz.build
@@ -193,16 +193,17 @@ MSVC_ENABLE_PGO = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'gklayout'
 LOCAL_INCLUDES += [
     '/caps/include',
     '/content/html/content/src',
     '/content/html/document/src',
+    '/content/svg/content/src',
     '/content/xml/content/src',
     '/content/xml/document/src',
     '/content/xul/content/src',
     '/content/xul/document/src',
     '/docshell/base',
     '/dom/base',
     '/dom/events',
     '/dom/ipc',
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -60,16 +60,17 @@
 #include "nsContentList.h"
 #include "nsContentPolicyUtils.h"
 #include "nsCPrefetchService.h"
 #include "nsCRT.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCycleCollector.h"
 #include "nsDataHashtable.h"
 #include "nsDocShellCID.h"
+#include "nsDocument.h"
 #include "nsDOMCID.h"
 #include "nsDOMDataTransfer.h"
 #include "nsDOMJSUtils.h"
 #include "nsDOMMutationObserver.h"
 #include "nsDOMTouchEvent.h"
 #include "nsError.h"
 #include "nsEventDispatcher.h"
 #include "nsEventListenerManager.h"
@@ -2418,16 +2419,49 @@ nsContentUtils::NewURIWithDocumentCharse
                                           nsIURI* aBaseURI)
 {
   return NS_NewURI(aResult, aSpec,
                    aDocument ? aDocument->GetDocumentCharacterSet().get() : nullptr,
                    aBaseURI, sIOService);
 }
 
 // static
+bool
+nsContentUtils::IsCustomElementName(nsIAtom* aName)
+{
+  // The custom element name identifies a custom element and is a sequence of
+  // alphanumeric ASCII characters that must match the NCName production and
+  // contain a U+002D HYPHEN-MINUS character.
+  nsDependentAtomString str(aName);
+  const char16_t* colon;
+  if (NS_FAILED(nsContentUtils::CheckQName(str, false, &colon)) || colon ||
+      str.FindChar('-') == -1) {
+    return false;
+  }
+
+  // The custom element name must not be one of the following values:
+  //  annotation-xml
+  //  color-profile
+  //  font-face
+  //  font-face-src
+  //  font-face-uri
+  //  font-face-format
+  //  font-face-name
+  //  missing-glyph
+  return aName != nsGkAtoms::annotation_xml_ &&
+         aName != nsGkAtoms::colorProfile &&
+         aName != nsGkAtoms::font_face &&
+         aName != nsGkAtoms::font_face_src &&
+         aName != nsGkAtoms::font_face_uri &&
+         aName != nsGkAtoms::font_face_format &&
+         aName != nsGkAtoms::font_face_name &&
+         aName != nsGkAtoms::missingGlyph;
+}
+
+// static
 nsresult
 nsContentUtils::CheckQName(const nsAString& aQualifiedName,
                            bool aNamespaceAware,
                            const char16_t** aColon)
 {
   const char* colon = nullptr;
   const char16_t* begin = aQualifiedName.BeginReading();
   const char16_t* end = aQualifiedName.EndReading();
@@ -4752,16 +4786,17 @@ nsContentUtils::EnterMicroTask()
 }
 
 void
 nsContentUtils::LeaveMicroTask()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (--sMicroTaskLevel == 0) {
     nsDOMMutationObserver::HandleMutations();
+    nsDocument::ProcessBaseElementQueue();
   }
 }
 
 bool
 nsContentUtils::IsInMicroTask()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return sMicroTaskLevel != 0;
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -158,30 +158,32 @@
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsEventStateManager.h"
 
 #include "nsSMILAnimationController.h"
 #include "imgIContainer.h"
 #include "nsSVGUtils.h"
+#include "SVGElementFactory.h"
 
 #include "nsRefreshDriver.h"
 
 // FOR CSP (autogenerated by xpidl)
 #include "nsIContentSecurityPolicy.h"
 #include "nsCSPService.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsHTMLCSSStyleSheet.h"
 #include "mozilla/dom/DOMImplementation.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/Comment.h"
 #include "nsTextNode.h"
 #include "mozilla/dom/Link.h"
 #include "mozilla/dom/HTMLElementBinding.h"
+#include "mozilla/dom/SVGElementBinding.h"
 #include "nsXULAppAPI.h"
 #include "nsDOMTouchEvent.h"
 #include "mozilla/dom/Touch.h"
 #include "DictionaryHelpers.h"
 #include "GeneratedEvents.h"
 
 #include "mozilla/Preferences.h"
 
@@ -307,16 +309,105 @@ nsIdentifierMapEntry::RemoveContentChang
 
 struct FireChangeArgs {
   Element* mFrom;
   Element* mTo;
   bool mImageOnly;
   bool mHaveImageOverride;
 };
 
+namespace mozilla {
+namespace dom {
+
+void
+CustomElementCallback::Call()
+{
+  ErrorResult rv;
+  switch (mType) {
+    case nsIDocument::eCreated:
+      // For the duration of this callback invocation, the element is being created
+      // flag must be set to true.
+      mOwnerData->mElementIsBeingCreated = true;
+      mOwnerData->mCreatedCallbackInvoked = true;
+      static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
+      mOwnerData->mElementIsBeingCreated = false;
+      break;
+    case nsIDocument::eEnteredView:
+      static_cast<LifecycleEnteredViewCallback *>(mCallback.get())->Call(mThisObject, rv);
+      break;
+    case nsIDocument::eLeftView:
+      static_cast<LifecycleLeftViewCallback *>(mCallback.get())->Call(mThisObject, rv);
+      break;
+    case nsIDocument::eAttributeChanged:
+      static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject,
+        mArgs.name, mArgs.oldValue, mArgs.newValue, rv);
+      break;
+  }
+}
+
+void
+CustomElementCallback::Traverse(nsCycleCollectionTraversalCallback& aCb) const
+{
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mThisObject");
+  aCb.NoteXPCOMChild(mThisObject);
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCallback");
+  aCb.NoteXPCOMChild(mCallback);
+}
+
+CustomElementCallback::CustomElementCallback(Element* aThisObject,
+                                             nsIDocument::ElementCallbackType aCallbackType,
+                                             mozilla::dom::CallbackFunction* aCallback,
+                                             CustomElementData* aOwnerData)
+  : mThisObject(aThisObject),
+    mCallback(aCallback),
+    mType(aCallbackType),
+    mOwnerData(aOwnerData)
+{
+}
+
+CustomElementDefinition::CustomElementDefinition(JSObject* aPrototype,
+                                                 nsIAtom* aType,
+                                                 nsIAtom* aLocalName,
+                                                 LifecycleCallbacks* aCallbacks,
+                                                 uint32_t aNamespaceID,
+                                                 uint32_t aDocOrder)
+  : mPrototype(aPrototype),
+    mType(aType),
+    mLocalName(aLocalName),
+    mCallbacks(aCallbacks),
+    mNamespaceID(aNamespaceID),
+    mDocOrder(aDocOrder)
+{
+}
+
+CustomElementData::CustomElementData(nsIAtom* aType)
+  : mType(aType),
+    mCurrentCallback(-1),
+    mElementIsBeingCreated(false),
+    mCreatedCallbackInvoked(true),
+    mAssociatedMicroTask(-1)
+{
+}
+
+void
+CustomElementData::RunCallbackQueue()
+{
+  // Note: It's possible to re-enter this method.
+  while (static_cast<uint32_t>(++mCurrentCallback) < mCallbackQueue.Length()) {
+    mCallbackQueue[mCurrentCallback]->Call();
+  }
+
+  mCallbackQueue.Clear();
+  mCurrentCallback = -1;
+}
+
+} // namespace dom
+} // namespace mozilla
+
 static PLDHashOperator
 FireChangeEnumerator(nsIdentifierMapEntry::ChangeCallbackEntry *aEntry, void *aArg)
 {
   FireChangeArgs* args = static_cast<FireChangeArgs*>(aArg);
   // Don't fire image changes for non-image observers, and don't fire element
   // changes for image observers when an image override is active.
   if (aEntry->mKey.mForImage ? (args->mHaveImageOverride && !args->mImageOnly) :
                                args->mImageOnly)
@@ -1433,16 +1524,22 @@ nsDocument::nsDocument(const char* aCont
            ("DOCUMENT %p created", this));
 
   if (!gCspPRLog)
     gCspPRLog = PR_NewLogModule("CSP");
 #endif
 
   // Start out mLastStyleSheetSet as null, per spec
   SetDOMStringToNull(mLastStyleSheetSet);
+
+  if (sProcessingStack.empty()) {
+    sProcessingStack.construct();
+    // Add the base queue sentinel to the processing stack.
+    sProcessingStack.ref().AppendElement((CustomElementData*) nullptr);
+  }
 }
 
 static PLDHashOperator
 ClearAllBoxObjects(nsIContent* aKey, nsPIBoxObject* aBoxObject, void* aUserArg)
 {
   if (aBoxObject) {
     aBoxObject->Clear();
   }
@@ -1508,17 +1605,19 @@ nsDocument::~nsDocument()
       }
       Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
     }
   }
 
   mInDestructor = true;
   mInUnlinkOrDeletion = true;
 
-  mCustomPrototypes.Clear();
+  if (mRegistry) {
+    mRegistry->Clear();
+  }
 
   mozilla::DropJSObjects(this);
 
   // Clear mObservers to keep it in sync with the mutationobserver list
   mObservers.Clear();
 
   if (mStyleSheetSetList) {
     mStyleSheetSetList->Disconnect();
@@ -1678,16 +1777,67 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_
   return Element::CanSkipInCC(tmp);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
   return Element::CanSkipThis(tmp);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
 
 static PLDHashOperator
+CustomDefinitionsTraverse(CustomElementHashKey* aKey,
+                          CustomElementDefinition* aDefinition,
+                          void* aArg)
+{
+  nsCycleCollectionTraversalCallback* cb =
+    static_cast<nsCycleCollectionTraversalCallback*>(aArg);
+
+  nsAutoPtr<LifecycleCallbacks>& callbacks = aDefinition->mCallbacks;
+
+  if (callbacks->mAttributeChangedCallback.WasPassed()) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
+      "mCustomDefinitions->mCallbacks->mAttributeChangedCallback");
+    cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttributeChangedCallback.Value());
+  }
+
+  if (callbacks->mCreatedCallback.WasPassed()) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
+      "mCustomDefinitions->mCallbacks->mCreatedCallback");
+    cb->NoteXPCOMChild(aDefinition->mCallbacks->mCreatedCallback.Value());
+  }
+
+  if (callbacks->mEnteredViewCallback.WasPassed()) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
+      "mCustomDefinitions->mCallbacks->mEnteredViewCallback");
+    cb->NoteXPCOMChild(aDefinition->mCallbacks->mEnteredViewCallback.Value());
+  }
+
+  if (callbacks->mLeftViewCallback.WasPassed()) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
+      "mCustomDefinitions->mCallbacks->mLeftViewCallback");
+    cb->NoteXPCOMChild(aDefinition->mCallbacks->mLeftViewCallback.Value());
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+static PLDHashOperator
+CandidatesTraverse(CustomElementHashKey* aKey,
+                   nsTArray<nsRefPtr<Element>>* aData,
+                   void* aArg)
+{
+  nsCycleCollectionTraversalCallback *cb =
+    static_cast<nsCycleCollectionTraversalCallback*>(aArg);
+  for (size_t i = 0; i < aData->Length(); ++i) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mCandidatesMap->Element");
+    cb->NoteXPCOMChild(aData->ElementAt(i));
+  }
+  return PL_DHASH_NEXT;
+}
+
+static PLDHashOperator
 SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
                 void *arg)
 {
   SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
   nsCycleCollectionTraversalCallback *cb =
     static_cast<nsCycleCollectionTraversalCallback*>(arg);
 
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mKey");
@@ -1842,50 +1992,60 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback.GetISupports());
   }
 
   // Traverse animation components
   if (tmp->mAnimationController) {
     tmp->mAnimationController->Traverse(&cb);
   }
 
+  if (tmp->mRegistry) {
+    tmp->mRegistry->mCustomDefinitions.EnumerateRead(CustomDefinitionsTraverse, &cb);
+    tmp->mRegistry->mCandidatesMap.EnumerateRead(CandidatesTraverse, &cb);
+  }
+
   if (tmp->mSubDocuments && tmp->mSubDocuments->ops) {
     PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb);
   }
 
   if (tmp->mCSSLoader) {
     tmp->mCSSLoader->TraverseCachedSheets(cb);
   }
 
   for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) {
     nsHostObjectProtocolHandler::Traverse(tmp->mHostObjectURIs[i], cb);
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-
-struct CustomPrototypeTraceArgs {
+struct CustomDefinitionTraceArgs
+{
   const TraceCallbacks& callbacks;
   void* closure;
 };
 
-
 static PLDHashOperator
-CustomPrototypeTrace(const nsAString& aName, JS::Heap<JSObject*>& aObject, void *aArg)
-{
-  CustomPrototypeTraceArgs* traceArgs = static_cast<CustomPrototypeTraceArgs*>(aArg);
-  MOZ_ASSERT(aObject, "Protocol object value must not be null");
-  traceArgs->callbacks.Trace(&aObject, "mCustomPrototypes entry", traceArgs->closure);
+CustomDefinitionTrace(CustomElementHashKey *aKey,
+                      CustomElementDefinition *aData,
+                      void *aArg)
+{
+  CustomDefinitionTraceArgs* traceArgs = static_cast<CustomDefinitionTraceArgs*>(aArg);
+  MOZ_ASSERT(aData, "Definition must not be null");
+  traceArgs->callbacks.Trace(&aData->mPrototype, "mCustomDefinitions prototype",
+                             traceArgs->closure);
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument)
-  CustomPrototypeTraceArgs customPrototypeArgs = { aCallbacks, aClosure };
-  tmp->mCustomPrototypes.Enumerate(CustomPrototypeTrace, &customPrototypeArgs);
+  CustomDefinitionTraceArgs customDefinitionArgs = { aCallbacks, aClosure };
+  if (tmp->mRegistry) {
+    tmp->mRegistry->mCustomDefinitions.EnumerateRead(CustomDefinitionTrace,
+                                                     &customDefinitionArgs);
+  }
   if (tmp->PreservingWrapper()) {
     NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando);
   }
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
@@ -1946,17 +2106,19 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
 
   // nsDocument has a pretty complex destructor, so we're going to
   // assume that *most* cycles you actually want to break somewhere
   // else, and not unlink an awful lot here.
 
   tmp->mIdentifierMap.Clear();
   tmp->mExpandoAndGeneration.Unlink();
 
-  tmp->mCustomPrototypes.Clear();
+  if (tmp->mRegistry) {
+    tmp->mRegistry->Clear();
+  }
 
   if (tmp->mAnimationController) {
     tmp->mAnimationController->Unlink();
   }
 
   tmp->mPendingTitleChangeEvent.Revoke();
 
   if (tmp->mCSSLoader) {
@@ -2150,17 +2312,19 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI
       mChildren.RemoveChildAt(i);
       nsNodeUtils::ContentRemoved(this, content, i, previousSibling);
       content->UnbindFromTree();
     }
     mCachedRootElement = nullptr;
   }
   mInUnlinkOrDeletion = oldVal;
 
-  mCustomPrototypes.Clear();
+  if (mRegistry) {
+    mRegistry->Clear();
+  }
 
   // Reset our stylesheets
   ResetStylesheetsToURI(aURI);
 
   // Release the listener manager
   if (mListenerManager) {
     mListenerManager->Disconnect();
     mListenerManager = nullptr;
@@ -4342,16 +4506,17 @@ nsDocument::SetScriptGlobalObject(nsIScr
 #endif
         bool allowDNSPrefetch;
         docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
         mAllowDNSPrefetch = allowDNSPrefetch;
       }
     }
 
     MaybeRescheduleAnimationFrameNotifications();
+    mRegistry = new Registry();
   }
 
   // Remember the pointer to our window (or lack there of), to avoid
   // having to QI every time it's asked for.
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobalObject);
   mWindow = window;
 
   // Now that we know what our window is, we can flush the CSP errors to the
@@ -4985,16 +5150,74 @@ nsIDocument::CreateElement(const nsAStri
   rv = CreateElem(needsLowercase ? lcTagName : aTagName,
                   nullptr, mDefaultElementType, getter_AddRefs(content));
   if (rv.Failed()) {
     return nullptr;
   }
   return dont_AddRef(content.forget().get()->AsElement());
 }
 
+void
+nsDocument::SwizzleCustomElement(Element* aElement,
+                                 const nsAString& aTypeExtension,
+                                 uint32_t aNamespaceID,
+                                 ErrorResult& rv)
+{
+  nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(aTypeExtension));
+  nsCOMPtr<nsIAtom> tagAtom = aElement->Tag();
+  if (!mRegistry || tagAtom == typeAtom) {
+    return;
+  }
+
+  CustomElementDefinition* data;
+  CustomElementHashKey key(aNamespaceID, typeAtom);
+  if (!mRegistry->mCustomDefinitions.Get(&key, &data)) {
+    // The type extension doesn't exist in the registry,
+    // thus we don't need to swizzle, but it is possibly
+    // an upgrade candidate.
+    RegisterUnresolvedElement(aElement, typeAtom);
+    return;
+  }
+
+  if (data->mLocalName != tagAtom) {
+    // The element doesn't match the local name for the
+    // definition, thus the element isn't a custom element
+    // and we don't need to do anything more.
+    return;
+  }
+
+  if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
+    // Swizzling in the parser happens after the "is" attribute is added.
+    aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, aTypeExtension, true);
+  }
+
+  // Enqueuing the created callback will set the CustomElementData on the
+  // element, causing prototype swizzling to occur in Element::WrapObject.
+  EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
+}
+
+already_AddRefed<Element>
+nsDocument::CreateElement(const nsAString& aTagName,
+                          const nsAString& aTypeExtension,
+                          ErrorResult& rv)
+{
+  nsRefPtr<Element> elem = nsIDocument::CreateElement(aTagName, rv);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+
+  SwizzleCustomElement(elem, aTypeExtension,
+                       GetDefaultNamespaceID(), rv);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+
+  return elem.forget();
+}
+
 NS_IMETHODIMP
 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
                             const nsAString& aQualifiedName,
                             nsIDOMElement** aReturn)
 {
   *aReturn = nullptr;
   ErrorResult rv;
   nsCOMPtr<Element> element =
@@ -5022,16 +5245,46 @@ nsIDocument::CreateElementNS(const nsASt
   rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
                      NOT_FROM_PARSER);
   if (rv.Failed()) {
     return nullptr;
   }
   return element.forget();
 }
 
+already_AddRefed<Element>
+nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
+                            const nsAString& aQualifiedName,
+                            const nsAString& aTypeExtension,
+                            ErrorResult& rv)
+{
+  nsRefPtr<Element> elem = nsIDocument::CreateElementNS(aNamespaceURI,
+                                                        aQualifiedName,
+                                                        rv);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+
+  int32_t nameSpaceId = kNameSpaceID_Wildcard;
+  if (!aNamespaceURI.EqualsLiteral("*")) {
+    rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
+                                                               nameSpaceId);
+    if (rv.Failed()) {
+      return nullptr;
+    }
+  }
+
+  SwizzleCustomElement(elem, aTypeExtension, nameSpaceId, rv);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+
+  return elem.forget();
+}
+
 NS_IMETHODIMP
 nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
 {
   *aReturn = nsIDocument::CreateTextNode(aData).get();
   return NS_OK;
 }
 
 already_AddRefed<nsTextNode>
@@ -5206,67 +5459,358 @@ nsIDocument::CreateAttributeNS(const nsA
     return nullptr;
   }
 
   nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
                                       EmptyString(), true);
   return attribute.forget();
 }
 
-static bool
-CustomElementConstructor(JSContext *aCx, unsigned aArgc, JS::Value* aVp)
+bool
+nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
 
   JS::Rooted<JSObject*> global(aCx,
     JS_GetGlobalForObject(aCx, &args.callee()));
   nsCOMPtr<nsPIDOMWindow> window = do_QueryWrapper(aCx, global);
   MOZ_ASSERT(window, "Should have a non-null window");
 
-  nsIDocument* document = window->GetDoc();
+  nsDocument* document = static_cast<nsDocument*>(window->GetDoc());
 
   // Function name is the type of the custom element.
   JSString* jsFunName =
     JS_GetFunctionId(JS_ValueToFunction(aCx, args.calleev()));
   nsDependentJSString elemName;
   if (!elemName.init(aCx, jsFunName)) {
-    return false;
-  }
+    return true;
+  }
+
+  nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(elemName));
+  CustomElementHashKey key(kNameSpaceID_None, typeAtom);
+  CustomElementDefinition* definition;
+  if (!document->mRegistry->mCustomDefinitions.Get(&key, &definition)) {
+    return true;
+  }
+
+  nsDependentAtomString localName(definition->mLocalName);
 
   nsCOMPtr<nsIContent> newElement;
-  nsresult rv = document->CreateElem(elemName, nullptr, kNameSpaceID_XHTML,
+  nsresult rv = document->CreateElem(localName, nullptr,
+                                     definition->mNamespaceID,
                                      getter_AddRefs(newElement));
+  NS_ENSURE_SUCCESS(rv, true);
+
+  ErrorResult errorResult;
+  nsCOMPtr<Element> element = do_QueryInterface(newElement);
+  document->SwizzleCustomElement(element, elemName, definition->mNamespaceID,
+                                 errorResult);
+  if (errorResult.Failed()) {
+    return true;
+  }
+
   rv = nsContentUtils::WrapNative(aCx, global, newElement, newElement,
                                   args.rval());
-  NS_ENSURE_SUCCESS(rv, false);
+  NS_ENSURE_SUCCESS(rv, true);
 
   return true;
 }
 
+nsresult
+nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
+{
+  if (!mRegistry) {
+    return NS_OK;
+  }
+
+  nsINodeInfo* info = aElement->NodeInfo();
+
+  // Candidate may be a custom element through extension,
+  // in which case the custom element type name will not
+  // match the element tag name. e.g. <button is="x-button">.
+  nsCOMPtr<nsIAtom> typeName = aTypeName;
+  if (!typeName) {
+    typeName = info->NameAtom();
+  }
+
+  CustomElementHashKey key(info->NamespaceID(), typeName);
+  if (mRegistry->mCustomDefinitions.Get(&key)) {
+    return NS_OK;
+  }
+
+  nsTArray<nsRefPtr<Element>>* unresolved;
+  mRegistry->mCandidatesMap.Get(&key, &unresolved);
+  if (!unresolved) {
+    unresolved = new nsTArray<nsRefPtr<Element>>();
+    // Ownership of unresolved is taken by mCandidatesMap.
+    mRegistry->mCandidatesMap.Put(&key, unresolved);
+  }
+
+  nsRefPtr<Element>* elem = unresolved->AppendElement();
+  *elem = aElement;
+
+  return NS_OK;
+}
+
+namespace {
+
+class ProcessStackRunner MOZ_FINAL : public nsIRunnable
+{
+  public:
+  ProcessStackRunner(bool aIsBaseQueue = false)
+    : mIsBaseQueue(aIsBaseQueue)
+  {
+  }
+  NS_DECL_ISUPPORTS
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    nsDocument::ProcessTopElementQueue(mIsBaseQueue);
+    return NS_OK;
+  }
+  bool mIsBaseQueue;
+};
+
+NS_IMPL_ISUPPORTS1(ProcessStackRunner, nsIRunnable);
+
+} // anonymous namespace
+
+void
+nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
+                                     Element* aCustomElement,
+                                     LifecycleCallbackArgs* aArgs,
+                                     CustomElementDefinition* aDefinition)
+{
+  if (!mRegistry) {
+    // The element might not belong to a document that
+    // has a browsing context, and thus no registry.
+    return;
+  }
+
+  CustomElementData* elementData = aCustomElement->GetCustomElementData();
+
+  // Let DEFINITION be ELEMENT's definition
+  CustomElementDefinition* definition = aDefinition;
+  if (!definition) {
+    nsINodeInfo* info = aCustomElement->NodeInfo();
+
+    // Make sure we get the correct definition in case the element
+    // is a extended custom element e.g. <button is="x-button">.
+    nsCOMPtr<nsIAtom> typeAtom = elementData ?
+      elementData->mType.get() : info->NameAtom();
+
+    CustomElementHashKey key(info->NamespaceID(), typeAtom);
+    if (!mRegistry->mCustomDefinitions.Get(&key, &definition) ||
+        definition->mLocalName != info->NameAtom()) {
+      // Trying to enqueue a callback for an element that is not
+      // a custom element. We are done, nothing to do.
+      return;
+    }
+  }
+
+  if (!elementData) {
+    // Create the custom element data the first time
+    // that we try to enqueue a callback.
+    elementData = new CustomElementData(definition->mType);
+    // aCustomElement takes ownership of elementData
+    aCustomElement->SetCustomElementData(elementData);
+    MOZ_ASSERT(aType == nsIDocument::eCreated,
+               "First callback should be the created callback");
+  }
+
+  // Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
+  CallbackFunction* func = nullptr;
+  switch (aType) {
+    case nsIDocument::eCreated:
+      if (definition->mCallbacks->mCreatedCallback.WasPassed()) {
+        func = definition->mCallbacks->mCreatedCallback.Value();
+      }
+      break;
+
+    case nsIDocument::eEnteredView:
+      if (definition->mCallbacks->mEnteredViewCallback.WasPassed()) {
+        func = definition->mCallbacks->mEnteredViewCallback.Value();
+      }
+      break;
+
+    case nsIDocument::eLeftView:
+      if (definition->mCallbacks->mLeftViewCallback.WasPassed()) {
+        func = definition->mCallbacks->mLeftViewCallback.Value();
+      }
+      break;
+
+    case nsIDocument::eAttributeChanged:
+      if (definition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
+        func = definition->mCallbacks->mAttributeChangedCallback.Value();
+      }
+      break;
+  }
+
+  // If there is no such callback, stop.
+  if (!func) {
+    return;
+  }
+
+  if (aType == nsIDocument::eCreated) {
+    elementData->mCreatedCallbackInvoked = false;
+  } else if (!elementData->mCreatedCallbackInvoked) {
+    // Callbacks other than created callback must not be enqueued
+    // until after the created callback has been invoked.
+    return;
+  }
+
+  // Add CALLBACK to ELEMENT's callback queue.
+  CustomElementCallback* callback = new CustomElementCallback(aCustomElement,
+                                                              aType,
+                                                              func,
+                                                              elementData);
+  // Ownership of callback is taken by mCallbackQueue.
+  elementData->mCallbackQueue.AppendElement(callback);
+  if (aArgs) {
+    callback->SetArgs(*aArgs);
+  }
+
+  if (!elementData->mElementIsBeingCreated) {
+    CustomElementData* lastData =
+      sProcessingStack.ref().SafeLastElement(nullptr);
+
+    // A new element queue needs to be pushed if the queue at the
+    // top of the stack is associated with another microtask level.
+    // Don't push a queue for the level 0 microtask (base element queue)
+    // because we don't want to process the queue until the
+    // microtask checkpoint.
+    bool shouldPushElementQueue = nsContentUtils::MicroTaskLevel() > 0 &&
+      (!lastData || lastData->mAssociatedMicroTask <
+         static_cast<int32_t>(nsContentUtils::MicroTaskLevel()));
+
+    // Push a new element queue onto the processing stack when appropriate
+    // (when we enter a new microtask).
+    if (shouldPushElementQueue) {
+      // Push a sentinel value on the processing stack to mark the
+      // boundary between the element queues.
+      sProcessingStack.ref().AppendElement((CustomElementData*) nullptr);
+    }
+
+    sProcessingStack.ref().AppendElement(elementData);
+    elementData->mAssociatedMicroTask =
+      static_cast<int32_t>(nsContentUtils::MicroTaskLevel());
+
+    // Add a script runner to pop and process the element queue at
+    // the top of the processing stack.
+    if (shouldPushElementQueue) {
+      // Lifecycle callbacks enqueued by user agent implementation
+      // should be invoked prior to returning control back to script.
+      // Create a script runner to process the top of the processing
+      // stack as soon as it is safe to run script.
+      nsContentUtils::AddScriptRunner(new ProcessStackRunner());
+    }
+  }
+}
+
+// static
+void
+nsDocument::ProcessBaseElementQueue()
+{
+  // Prevent re-entrance. Also, if a microtask checkpoint is reached
+  // and there is no processing stack to process, then we are done.
+  if (sProcessingBaseElementQueue || sProcessingStack.empty()) {
+    return;
+  }
+
+  MOZ_ASSERT(nsContentUtils::MicroTaskLevel() == 0);
+  sProcessingBaseElementQueue = true;
+  nsContentUtils::AddScriptRunner(new ProcessStackRunner(true));
+}
+
+// static
+void
+nsDocument::ProcessTopElementQueue(bool aIsBaseQueue)
+{
+  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+  nsTArray<CustomElementData*>& stack = sProcessingStack.ref();
+  uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr);
+
+  if (aIsBaseQueue && firstQueue != 0) {
+    return;
+  }
+
+  for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) {
+    // Callback queue may have already been processed in an earlier
+    // element queue or in an element queue that was popped
+    // off more recently.
+    if (stack[i]->mAssociatedMicroTask != -1) {
+      stack[i]->RunCallbackQueue();
+      stack[i]->mAssociatedMicroTask = -1;
+    }
+  }
+
+  // If this was actually the base element queue, don't bother trying to pop
+  // the first "queue" marker (sentinel).
+  if (firstQueue != 0) {
+    stack.SetLength(firstQueue);
+  } else {
+    // Don't pop sentinel for base element queue.
+    stack.SetLength(1);
+    sProcessingBaseElementQueue = false;
+  }
+}
+
 bool
 nsDocument::RegisterEnabled()
 {
   static bool sPrefValue =
     Preferences::GetBool("dom.webcomponents.enabled", false);
   return sPrefValue;
 }
 
+// static
+Maybe<nsTArray<mozilla::dom::CustomElementData*>>
+nsDocument::sProcessingStack;
+
+// static
+bool
+nsDocument::sProcessingBaseElementQueue;
+
 JSObject*
-nsDocument::Register(JSContext* aCx, const nsAString& aName,
-                     const ElementRegistrationOptions& aOptions,
-                     ErrorResult& rv)
-{
+nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
+                            const ElementRegistrationOptions& aOptions,
+                            ErrorResult& rv)
+{
+  if (!mRegistry) {
+    rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  Registry::DefinitionMap& definitions = mRegistry->mCustomDefinitions;
+
+  // Unconditionally convert TYPE to lowercase.
+  nsAutoString lcType;
+  nsContentUtils::ASCIIToLower(aType, lcType);
+
+  // Only convert NAME to lowercase in HTML documents. Note that NAME is
+  // options.extends.
   nsAutoString lcName;
-  nsContentUtils::ASCIIToLower(aName, lcName);
-  if (!StringBeginsWith(lcName, NS_LITERAL_STRING("x-"))) {
-    rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
+  if (IsHTML()) {
+    nsContentUtils::ASCIIToLower(aOptions.mExtends, lcName);
+  } else {
+    lcName.Assign(aOptions.mExtends);
+  }
+
+  nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(lcType));
+  if (!nsContentUtils::IsCustomElementName(typeAtom)) {
+    rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return nullptr;
   }
-  if (NS_FAILED(nsContentUtils::CheckQName(lcName, false))) {
-    rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
+
+  // If there already exists a definition with the same TYPE, set ERROR to
+  // DuplicateDefinition and stop.
+  // Note that we need to find existing custom elements from either namespace.
+  CustomElementHashKey duplicateFinder(kNameSpaceID_None, typeAtom);
+  if (definitions.Get(&duplicateFinder)) {
+    rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
 
   nsIGlobalObject* sgo = GetScopeObject();
   if (!sgo) {
     rv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
@@ -5276,119 +5820,181 @@ nsDocument::Register(JSContext* aCx, con
 
   JS::Handle<JSObject*> htmlProto(
     HTMLElementBinding::GetProtoObject(aCx, global));
   if (!htmlProto) {
     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
 
+  int32_t namespaceID = kNameSpaceID_XHTML;
   JS::Rooted<JSObject*> protoObject(aCx);
   if (!aOptions.mPrototype) {
     protoObject = JS_NewObject(aCx, nullptr, htmlProto, JS::NullPtr());
     if (!protoObject) {
       rv.Throw(NS_ERROR_UNEXPECTED);
       return nullptr;
     }
   } else {
-    // If a prototype is provided, we must check to ensure that it inherits
-    // from HTMLElement.
+    // If a prototype is provided, we must check to ensure that it is from the
+    // same browsing context as us.
     protoObject = aOptions.mPrototype;
-    if (!JS_WrapObject(aCx, &protoObject)) {
+    if (JS_GetGlobalForObject(aCx, protoObject) != global) {
+      rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+      return nullptr;
+    }
+
+    // If PROTOTYPE is already an interface prototype object for any interface
+    // object or PROTOTYPE has a non-configurable property named constructor,
+    // throw a NotSupportedError and stop.
+    const js::Class* clasp = js::GetObjectClass(protoObject);
+    if (IsDOMIfaceAndProtoClass(clasp)) {
+      rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+      return nullptr;
+    }
+
+    JS::Rooted<JSPropertyDescriptor> descRoot(aCx);
+    JS::MutableHandle<JSPropertyDescriptor> desc(&descRoot);
+    if(!JS_GetPropertyDescriptor(aCx, protoObject, "constructor", 0, desc)) {
       rv.Throw(NS_ERROR_UNEXPECTED);
       return nullptr;
     }
 
-    // Check the proto chain for HTMLElement prototype.
-    JS::Rooted<JSObject*> protoProto(aCx);
-    if (!JS_GetPrototype(aCx, protoObject, &protoProto)) {
-      rv.Throw(NS_ERROR_UNEXPECTED);
+    // Check if non-configurable
+    if (desc.isPermanent()) {
+      rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return nullptr;
     }
+
+    JS::Handle<JSObject*> svgProto(
+      SVGElementBinding::GetProtoObject(aCx, global));
+    if (!svgProto) {
+      rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return nullptr;
+    }
+
+    JS::Rooted<JSObject*> protoProto(aCx, protoObject);
+
+    // If PROTOTYPE's interface inherits from SVGElement, set NAMESPACE to SVG
+    // Namespace.
     while (protoProto) {
       if (protoProto == htmlProto) {
         break;
       }
+
+      if (protoProto == svgProto) {
+        namespaceID = kNameSpaceID_SVG;
+        break;
+      }
+
       if (!JS_GetPrototype(aCx, protoProto, &protoProto)) {
         rv.Throw(NS_ERROR_UNEXPECTED);
         return nullptr;
       }
     }
-
-    if (!protoProto) {
-      rv.Throw(NS_ERROR_DOM_TYPE_MISMATCH_ERR);
+  }
+
+  // If name was provided and not null...
+  nsCOMPtr<nsIAtom> nameAtom;
+  if (!lcName.IsEmpty()) {
+    // Let BASE be the element interface for NAME and NAMESPACE.
+    bool known = false;
+    nameAtom = do_GetAtom(lcName);
+    if (namespaceID == kNameSpaceID_XHTML) {
+      nsIParserService* ps = nsContentUtils::GetParserService();
+      if (!ps) {
+        rv.Throw(NS_ERROR_UNEXPECTED);
+        return nullptr;
+      }
+
+      known =
+        ps->HTMLCaseSensitiveAtomTagToId(nameAtom) != eHTMLTag_userdefined;
+    } else {
+      known = SVGElementFactory::Exists(nameAtom);
+    }
+
+    // If BASE does not exist or is an interface for a custom element, set ERROR
+    // to InvalidName and stop.
+    // If BASE exists, then it cannot be an interface for a custom element.
+    if (!known) {
+      rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
       return nullptr;
     }
-  }
-
-  // Associate the prototype with the custom element.
-  mCustomPrototypes.Put(lcName, protoObject);
+  } else {
+    // If NAMESPACE is SVG Namespace, set ERROR to InvalidName and stop.
+    if (namespaceID == kNameSpaceID_SVG) {
+      rv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    nameAtom = typeAtom;
+  }
+
+  nsAutoPtr<LifecycleCallbacks> callbacksHolder(new LifecycleCallbacks());
+  JS::RootedValue rootedv(aCx, JS::ObjectValue(*protoObject));
+  if (!callbacksHolder->Init(aCx, rootedv)) {
+    return nullptr;
+  }
+
+  // Associate the definition with the custom element.
+  CustomElementHashKey key(namespaceID, typeAtom);
+  LifecycleCallbacks* callbacks = callbacksHolder.forget();
+  CustomElementDefinition* definition =
+    new CustomElementDefinition(protoObject,
+                                typeAtom,
+                                nameAtom,
+                                callbacks,
+                                namespaceID,
+                                0 /* TODO dependent on HTML imports. Bug 877072 */);
+  definitions.Put(&key, definition);
 
   // Do element upgrade.
-  nsRefPtr<nsContentList> list = GetElementsByTagName(lcName);
-  for (uint32_t i = 0; i < list->Length(false); i++) {
-    nsCOMPtr<nsINode> oldNode = list->Item(i, false);
-
-    // TODO(wchen): Perform upgrade on Shadow DOM when implemented.
-    // Bug 806506.
-    nsCOMPtr<nsINode> newNode;
-    rv = nsNodeUtils::Clone(oldNode, true, getter_AddRefs(newNode));
-    if (rv.Failed()) {
-      return nullptr;
-    }
-
-    nsINode* parentNode = oldNode->GetParentNode();
-    MOZ_ASSERT(parentNode, "Node obtained by GetElementsByTagName.");
-    nsCOMPtr<Element> newElement = do_QueryInterface(newNode);
-    MOZ_ASSERT(newElement, "Cloned of node obtained by GetElementsByTagName.");
-
-    parentNode->ReplaceChild(*newNode, *oldNode, rv);
-    if (rv.Failed()) {
-      return nullptr;
-    }
-
-    // Dispatch elementreplaced to replaced elements.
-    nsCOMPtr<nsIDOMEvent> event;
-    rv = CreateEvent(NS_LITERAL_STRING("elementreplace"), getter_AddRefs(event));
-    if (rv.Failed()) {
-      return nullptr;
-    }
-
-    if (aOptions.mLifecycle.mCreated) {
-      // Don't abort the upgrade algorithm if the callback throws an
-      // exception.
-      ErrorResult dummy;
-      aOptions.mLifecycle.mCreated->Call(newElement, dummy);
-    }
-
-    nsCOMPtr<nsIDOMElementReplaceEvent> ptEvent = do_QueryInterface(event);
-    MOZ_ASSERT(ptEvent);
-
-    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(newElement);
-    rv = ptEvent->InitElementReplaceEvent(NS_LITERAL_STRING("elementreplace"),
-                                          false, false, element);
-    if (rv.Failed()) {
-      return nullptr;
-    }
-
-    event->SetTrusted(true);
-    event->SetTarget(oldNode);
-    nsEventDispatcher::DispatchDOMEvent(oldNode, nullptr, event,
-                                        nullptr, nullptr);
-  }
-
-  nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
-                                       NS_LITERAL_STRING("elementupgrade"),
-                                       true, true);
+  nsAutoPtr<nsTArray<nsRefPtr<Element>>> candidates;
+  mRegistry->mCandidatesMap.RemoveAndForget(&key, candidates);
+  if (candidates) {
+    for (size_t i = 0; i < candidates->Length(); ++i) {
+      Element *elem = candidates->ElementAt(i);
+
+      // Make sure that the element name matches the name in the definition.
+      // (e.g. a definition for x-button extending button should match
+      // <button is="x-button"> but not <x-button>.
+      if (elem->NodeInfo()->NameAtom() != nameAtom) {
+        // Skip over this element because definition does not apply.
+        continue;
+      }
+
+      nsWrapperCache* cache;
+      CallQueryInterface(elem, &cache);
+      MOZ_ASSERT(cache, "Element doesn't support wrapper cache?");
+
+      JS::RootedObject wrapper(aCx);
+      if ((wrapper = cache->GetWrapper())) {
+        if (!JS_SetPrototype(aCx, wrapper, protoObject)) {
+          continue;
+        }
+      }
+
+      EnqueueLifecycleCallback(nsIDocument::eCreated, elem, nullptr, definition);
+      if (elem->GetCurrentDoc()) {
+        // Normally callbacks can not be enqueued until the created
+        // callback has been invoked, however, the entered view callback
+        // in element upgrade is an exception so pretend the created
+        // callback has been invoked.
+        elem->GetCustomElementData()->mCreatedCallbackInvoked = true;
+
+        EnqueueLifecycleCallback(nsIDocument::eEnteredView, elem, nullptr, definition);
+      }
+    }
+  }
 
   // Create constructor to return. Store the name of the custom element as the
   // name of the function.
-  JSFunction* constructor = JS_NewFunction(aCx, CustomElementConstructor, 0,
+  JSFunction* constructor = JS_NewFunction(aCx, nsDocument::CustomElementConstructor, 0,
                                            JSFUN_CONSTRUCTOR, JS::NullPtr(),
-                                           NS_ConvertUTF16toUTF8(lcName).get());
+                                           NS_ConvertUTF16toUTF8(lcType).get());
   JSObject* constructorObject = JS_GetFunctionObject(constructor);
   return constructorObject;
 }
 
 NS_IMETHODIMP
 nsDocument::GetElementsByTagName(const nsAString& aTagname,
                                  nsIDOMNodeList** aReturn)
 {
@@ -7846,17 +8452,19 @@ nsDocument::Destroy()
 
   mLayoutHistoryState = nullptr;
 
   // Shut down our external resource map.  We might not need this for
   // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
   // tearing down all those frame trees right now is the right thing to do.
   mExternalResourceMap.Shutdown();
 
-  mCustomPrototypes.Clear();
+  if (mRegistry) {
+    mRegistry->Clear();
+  }
 
   // XXX We really should let cycle collection do this, but that currently still
   //     leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
   ReleaseWrapper(static_cast<nsINode*>(this));
 }
 
 void
 nsDocument::RemovedFromDocShell()
@@ -11102,16 +11710,17 @@ nsIDocument::GetMozPointerLockElement()
 
   return pointerLockedElement;
 }
 
 void
 nsDocument::XPCOMShutdown()
 {
   gPendingPointerLockRequest = nullptr;
+  sProcessingStack.destroyIfConstructed();
 }
 
 void
 nsDocument::UpdateVisibilityState()
 {
   dom::VisibilityState oldState = mVisibilityState;
   mVisibilityState = GetVisibilityState();
   if (oldState != mVisibilityState) {
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -1,9 +1,10 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et 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/. */
 
 /*
  * Base class for all our document implementations.
  */
 
@@ -94,16 +95,18 @@ class nsWindowSizes;
 class nsHtml5TreeOpExecutor;
 class nsDocumentOnStack;
 class nsPointerLockPermissionRequest;
 class nsISecurityConsoleMessage;
 
 namespace mozilla {
 namespace dom {
 class UndoManager;
+class LifecycleCallbacks;
+class CallbackFunction;
 }
 }
 
 /**
  * Right now our identifier map entries contain information for 'name'
  * and 'id' mappings of a given string. This is so that
  * nsHTMLDocument::ResolveName only has to do one hash lookup instead
  * of two. It's not clear whether this still matters for performance.
@@ -232,16 +235,163 @@ private:
   // empty if there are no elements with this ID.
   // The elements are stored as weak pointers.
   nsSmallVoidArray mIdContentList;
   nsRefPtr<nsBaseContentList> mNameContentList;
   nsAutoPtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks;
   nsRefPtr<Element> mImageElement;
 };
 
+namespace mozilla {
+namespace dom {
+
+class CustomElementHashKey : public PLDHashEntryHdr
+{
+public:
+  typedef CustomElementHashKey *KeyType;
+  typedef const CustomElementHashKey *KeyTypePointer;
+
+  CustomElementHashKey(int32_t aNamespaceID, nsIAtom *aAtom)
+    : mNamespaceID(aNamespaceID),
+      mAtom(aAtom)
+  {}
+  CustomElementHashKey(const CustomElementHashKey *aKey)
+    : mNamespaceID(aKey->mNamespaceID),
+      mAtom(aKey->mAtom)
+  {}
+  ~CustomElementHashKey()
+  {}
+
+  KeyType GetKey() const { return const_cast<KeyType>(this); }
+  bool KeyEquals(const KeyTypePointer aKey) const
+  {
+    MOZ_ASSERT(mNamespaceID != kNameSpaceID_None,
+               "This equals method is not transitive, nor symmetric. "
+               "A key with a namespace of kNamespaceID_None should "
+               "not be stored in a hashtable.");
+    return (kNameSpaceID_None == aKey->mNamespaceID ||
+            mNamespaceID == aKey->mNamespaceID) &&
+           aKey->mAtom == mAtom;
+  }
+
+  static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+  static PLDHashNumber HashKey(const KeyTypePointer aKey)
+  {
+    return aKey->mAtom->hash();
+  }
+  enum { ALLOW_MEMMOVE = true };
+
+private:
+  int32_t mNamespaceID;
+  nsCOMPtr<nsIAtom> mAtom;
+};
+
+struct LifecycleCallbackArgs
+{
+  nsString name;
+  nsString oldValue;
+  nsString newValue;
+};
+
+struct CustomElementData;
+
+class CustomElementCallback
+{
+public:
+  CustomElementCallback(Element* aThisObject,
+                        nsIDocument::ElementCallbackType aCallbackType,
+                        mozilla::dom::CallbackFunction* aCallback,
+                        CustomElementData* aOwnerData);
+  void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
+  void Call();
+  void SetArgs(LifecycleCallbackArgs& aArgs)
+  {
+    MOZ_ASSERT(mType == nsIDocument::eAttributeChanged,
+               "Arguments are only used by attribute changed callback.");
+    mArgs = aArgs;
+  }
+
+private:
+  // The this value to use for invocation of the callback.
+  nsRefPtr<mozilla::dom::Element> mThisObject;
+  nsRefPtr<mozilla::dom::CallbackFunction> mCallback;
+  // The type of callback (eCreated, eEnteredView, etc.)
+  nsIDocument::ElementCallbackType mType;
+  // Arguments to be passed to the callback,
+  // used by the attribute changed callback.
+  LifecycleCallbackArgs mArgs;
+  // CustomElementData that contains this callback in the
+  // callback queue.
+  CustomElementData* mOwnerData;
+};
+
+// Each custom element has an associated callback queue and an element is
+// being created flag.
+struct CustomElementData
+{
+  CustomElementData(nsIAtom* aType);
+  // Objects in this array are transient and empty after each microtask
+  // checkpoint.
+  nsTArray<nsAutoPtr<CustomElementCallback>> mCallbackQueue;
+  // Custom element type, for <button is="x-button"> or <x-button>
+  // this would be x-button.
+  nsCOMPtr<nsIAtom> mType;
+  // The callback that is next to be processed upon calling RunCallbackQueue.
+  int32_t mCurrentCallback;
+  // Element is being created flag as described in the custom elements spec.
+  bool mElementIsBeingCreated;
+  // Flag to determine if the created callback has been invoked, thus it
+  // determines if other callbacks can be enqueued.
+  bool mCreatedCallbackInvoked;
+  // The microtask level associated with the callbacks in the callback queue,
+  // it is used to determine if a new queue needs to be pushed onto the
+  // processing stack.
+  int32_t mAssociatedMicroTask;
+
+  // Empties the callback queue.
+  void RunCallbackQueue();
+};
+
+// The required information for a custom element as defined in:
+// https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html
+struct CustomElementDefinition
+{
+  CustomElementDefinition(JSObject* aPrototype,
+                          nsIAtom* aType,
+                          nsIAtom* aLocalName,
+                          mozilla::dom::LifecycleCallbacks* aCallbacks,
+                          uint32_t aNamespaceID,
+                          uint32_t aDocOrder);
+
+  // The prototype to use for new custom elements of this type.
+  JS::Heap<JSObject *> mPrototype;
+
+  // The type (name) for this custom element.
+  nsCOMPtr<nsIAtom> mType;
+
+  // The localname to (e.g. <button is=type> -- this would be button).
+  nsCOMPtr<nsIAtom> mLocalName;
+
+  // The lifecycle callbacks to call for this custom element.
+  nsAutoPtr<mozilla::dom::LifecycleCallbacks> mCallbacks;
+
+  // Whether we're currently calling the created callback for a custom element
+  // of this type.
+  bool mElementIsBeingCreated;
+
+  // Element namespace.
+  int32_t mNamespaceID;
+
+  // The document custom element order.
+  uint32_t mDocOrder;
+};
+
+} // namespace dom
+} // namespace mozilla
+
 class nsDocHeaderData
 {
 public:
   nsDocHeaderData(nsIAtom* aField, const nsAString& aData)
     : mField(aField), mData(aData), mNext(nullptr)
   {
   }
 
@@ -991,35 +1141,67 @@ public:
   // Posts an event to call UpdateVisibilityState
   virtual void PostVisibilityUpdateEvent() MOZ_OVERRIDE;
 
   virtual void DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const MOZ_OVERRIDE;
   // DocAddSizeOfIncludingThis is inherited from nsIDocument.
 
   virtual nsIDOMNode* AsDOMNode() MOZ_OVERRIDE { return this; }
 
-  void GetCustomPrototype(const nsAString& aElementName, JS::MutableHandle<JSObject*> prototype)
+  virtual void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
+                                        Element* aCustomElement,
+                                        mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
+                                        mozilla::dom::CustomElementDefinition* aDefinition = nullptr) MOZ_OVERRIDE;
+
+  static void ProcessTopElementQueue(bool aIsBaseQueue = false);
+
+  void GetCustomPrototype(int32_t aNamespaceID,
+                          nsIAtom* aAtom,
+                          JS::MutableHandle<JSObject*> prototype)
   {
-    mCustomPrototypes.Get(aElementName, prototype.address());
+    if (!mRegistry) {
+      prototype.set(nullptr);
+      return;
+    }
+
+    mozilla::dom::CustomElementHashKey key(aNamespaceID, aAtom);
+    mozilla::dom::CustomElementDefinition* definition;
+    if (mRegistry->mCustomDefinitions.Get(&key, &definition)) {
+      prototype.set(definition->mPrototype);
+    } else {
+      prototype.set(nullptr);
+    }
   }
 
   static bool RegisterEnabled();
 
+  virtual nsresult RegisterUnresolvedElement(mozilla::dom::Element* aElement,
+                                             nsIAtom* aTypeName = nullptr) MOZ_OVERRIDE;
+
   // WebIDL bits
   virtual mozilla::dom::DOMImplementation*
     GetImplementation(mozilla::ErrorResult& rv) MOZ_OVERRIDE;
   virtual JSObject*
-  Register(JSContext* aCx, const nsAString& aName,
-           const mozilla::dom::ElementRegistrationOptions& aOptions,
-           mozilla::ErrorResult& rv) MOZ_OVERRIDE;
+    RegisterElement(JSContext* aCx, const nsAString& aName,
+                    const mozilla::dom::ElementRegistrationOptions& aOptions,
+                    mozilla::ErrorResult& rv) MOZ_OVERRIDE;
   virtual nsIDOMStyleSheetList* StyleSheets() MOZ_OVERRIDE;
   virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) MOZ_OVERRIDE;
   virtual void GetLastStyleSheetSet(nsString& aSheetSet) MOZ_OVERRIDE;
   virtual nsIDOMDOMStringList* StyleSheetSets() MOZ_OVERRIDE;
   virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) MOZ_OVERRIDE;
+  using nsIDocument::CreateElement;
+  using nsIDocument::CreateElementNS;
+  virtual already_AddRefed<Element> CreateElement(const nsAString& aTagName,
+                                                  const nsAString& aTypeExtension,
+                                                  mozilla::ErrorResult& rv) MOZ_OVERRIDE;
+  virtual already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
+                                                    const nsAString& aQualifiedName,
+                                                    const nsAString& aTypeExtension,
+                                                    mozilla::ErrorResult& rv) MOZ_OVERRIDE;
 
 protected:
   friend class nsNodeUtils;
   friend class nsDocumentOnStack;
 
   void IncreaseStackRefCnt()
   {
     ++mStackRefCnt;
@@ -1170,19 +1352,69 @@ protected:
   // full-screen element onto this stack, and when we cancel full-screen we
   // pop one off this stack, restoring the previous full-screen state
   nsTArray<nsWeakPtr> mFullScreenStack;
 
   // The root of the doc tree in which this document is in. This is only
   // non-null when this document is in fullscreen mode.
   nsWeakPtr mFullscreenRoot;
 
-  // Hashtable for custom element prototypes in web components.
-  // Custom prototypes are in the document's compartment.
-  nsJSThingHashtable<nsStringHashKey, JSObject*> mCustomPrototypes;
+private:
+  struct Registry
+  {
+    NS_INLINE_DECL_REFCOUNTING(Registry)
+
+    typedef nsClassHashtable<mozilla::dom::CustomElementHashKey,
+                             mozilla::dom::CustomElementDefinition>
+      DefinitionMap;
+    typedef nsClassHashtable<mozilla::dom::CustomElementHashKey,
+                             nsTArray<nsRefPtr<mozilla::dom::Element>>>
+      CandidateMap;
+
+    // Hashtable for custom element definitions in web components.
+    // Custom prototypes are in the document's compartment.
+    DefinitionMap mCustomDefinitions;
+
+    // The "upgrade candidates map" from the web components spec. Maps from a
+    // namespace id and local name to a list of elements to upgrade if that
+    // element is registered as a custom element.
+    CandidateMap mCandidatesMap;
+
+    void Clear()
+    {
+      mCustomDefinitions.Clear();
+      mCandidatesMap.Clear();
+    }
+  };
+
+  // Array representing the processing stack in the custom elements
+  // specification. The processing stack is conceptually a stack of
+  // element queues. Each queue is represented by a sequence of
+  // CustomElementData in this array, separated by nullptr that
+  // represent the boundaries of the items in the stack. The first
+  // queue in the stack is the base element queue.
+  static mozilla::Maybe<nsTArray<mozilla::dom::CustomElementData*>> sProcessingStack;
+
+  // Flag to prevent re-entrance into base element queue as described in the
+  // custom elements speicification.
+  static bool sProcessingBaseElementQueue;
+
+  static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
+
+public:
+  static void ProcessBaseElementQueue();
+
+  // Modify the prototype and "is" attribute of newly created custom elements.
+  virtual void SwizzleCustomElement(Element* aElement,
+                                    const nsAString& aTypeExtension,
+                                    uint32_t aNamespaceID,
+                                    mozilla::ErrorResult& rv);
+
+  // The "registry" from the web components spec.
+  nsRefPtr<Registry> mRegistry;
 
   nsRefPtr<nsEventListenerManager> mListenerManager;
   nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets;
   nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
   nsRefPtr<nsScriptLoader> mScriptLoader;
   nsDocHeaderData* mHeaderData;
   /* mIdentifierMap works as follows for IDs:
    * 1) Attribute changes affect the table immediately (removing and adding
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -708,16 +708,27 @@ nsGenericDOMDataNode::SetXBLInsertionPar
 {
   nsDataSlots *slots = DataSlots();
   if (aContent) {
     SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
   }
   slots->mXBLInsertionParent = aContent;
 }
 
+CustomElementData *
+nsGenericDOMDataNode::GetCustomElementData() const
+{
+  return nullptr;
+}
+
+void
+nsGenericDOMDataNode::SetCustomElementData(CustomElementData* aData)
+{
+}
+
 bool
 nsGenericDOMDataNode::IsNodeOfType(uint32_t aFlags) const
 {
   return !(aFlags & ~(eCONTENT | eDATA_NODE));
 }
 
 void
 nsGenericDOMDataNode::SaveSubtreeState()
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -156,21 +156,24 @@ public:
 
   virtual nsIContent *GetBindingParent() const MOZ_OVERRIDE;
   virtual nsXBLBinding *GetXBLBinding() const MOZ_OVERRIDE;
   virtual void SetXBLBinding(nsXBLBinding* aBinding,
                              nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
   virtual mozilla::dom::ShadowRoot *GetContainingShadow() const MOZ_OVERRIDE;
   virtual mozilla::dom::ShadowRoot *GetShadowRoot() const MOZ_OVERRIDE;
   virtual void SetShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot) MOZ_OVERRIDE;
-  virtual nsIContent *GetXBLInsertionParent() const;
-  virtual void SetXBLInsertionParent(nsIContent* aContent);
+  virtual nsIContent *GetXBLInsertionParent() const MOZ_OVERRIDE;
+  virtual void SetXBLInsertionParent(nsIContent* aContent) MOZ_OVERRIDE;
   virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
   virtual bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE;
 
+  virtual mozilla::dom::CustomElementData* GetCustomElementData() const MOZ_OVERRIDE;
+  virtual void SetCustomElementData(mozilla::dom::CustomElementData* aData) MOZ_OVERRIDE;
+
   virtual nsIAtom* DoGetID() const MOZ_OVERRIDE;
   virtual const nsAttrValue* DoGetClasses() const MOZ_OVERRIDE;
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) MOZ_OVERRIDE;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
   virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
                                               int32_t aModType) const;
   virtual nsIAtom *GetClassAttributeName() const;
 
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -470,16 +470,17 @@ GK_ATOM(inputmode, "inputmode")
 GK_ATOM(ins, "ins")
 GK_ATOM(insertafter, "insertafter")
 GK_ATOM(insertbefore, "insertbefore")
 GK_ATOM(instanceOf, "instanceOf")
 GK_ATOM(int32, "int32")
 GK_ATOM(int64, "int64")
 GK_ATOM(integer, "integer")
 GK_ATOM(intersection, "intersection")
+GK_ATOM(is, "is")
 GK_ATOM(iscontainer, "iscontainer")
 GK_ATOM(isempty, "isempty")
 GK_ATOM(ismap, "ismap")
 GK_ATOM(itemid, "itemid")
 GK_ATOM(itemprop, "itemprop")
 GK_ATOM(itemref, "itemref")
 GK_ATOM(itemscope, "itemscope")
 GK_ATOM(itemtype, "itemtype")
--- a/content/base/test/chrome/test_document_register.xul
+++ b/content/base/test/chrome/test_document_register.xul
@@ -19,17 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
   /** Test for Bug 783129 **/
   SimpleTest.waitForExplicitFinish();
 
   function startTests() {
-    var c = $("fooframe").contentDocument.register("x-foo");
+    var c = $("fooframe").contentDocument.registerElement("x-foo");
     var elem = new c();
     is(elem.tagName, "X-FOO", "Constructor should create an x-foo element.");
 
     var anotherElem = $("fooframe").contentDocument.createElement("x-foo");
     is(anotherElem.tagName, "X-FOO", "createElement should create an x-foo element.");
     SimpleTest.finish();
   }
 
--- a/content/html/content/src/HTMLUnknownElement.cpp
+++ b/content/html/content/src/HTMLUnknownElement.cpp
@@ -11,28 +11,15 @@
 NS_IMPL_NS_NEW_HTML_ELEMENT(Unknown)
 
 namespace mozilla {
 namespace dom {
 
 JSObject*
 HTMLUnknownElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aScope)
 {
-  JS::Rooted<JSObject*> obj(aCx,
-    HTMLUnknownElementBinding::Wrap(aCx, aScope, this));
-  if (obj && Substring(NodeName(), 0, 2).LowerCaseEqualsLiteral("x-")) {
-    // If we have a registered x-tag then we fix the prototype.
-    JSAutoCompartment ac(aCx, obj);
-    nsDocument* document = static_cast<nsDocument*>(OwnerDoc());
-    JS::Rooted<JSObject*> prototype(aCx);
-    document->GetCustomPrototype(LocalName(), &prototype);
-    if (prototype) {
-      NS_ENSURE_TRUE(JS_WrapObject(aCx, &prototype), nullptr);
-      NS_ENSURE_TRUE(JS_SetPrototype(aCx, obj, prototype), nullptr);
-    }
-  }
-  return obj;
+  return HTMLUnknownElementBinding::Wrap(aCx, aScope, this);
 }
 
 NS_IMPL_ELEMENT_CLONE(HTMLUnknownElement)
 
 } // namespace dom
 } // namespace mozilla
--- a/content/html/content/test/test_ignoreuserfocus.html
+++ b/content/html/content/test/test_ignoreuserfocus.html
@@ -36,17 +36,17 @@
         var witness = document.createElement("input");
         witness.setAttribute("type", "text");
         var witness2 = document.createElement("input");
         witness2.setAttribute("type", "text");
 
         var iframe = document.createElement("iframe");
         iframe.setAttribute("mozbrowser", "true");
         iframe.setAttribute("ignoreuserfocus", "true");
-        iframe.setAttribute("height", "500px");
+        iframe.setAttribute("height", "300px");
         iframe.setAttribute("src", "file_ignoreuserfocus.html");
 
         iframe.addEventListener('load', function (e) {
           // Get privileged iframe because mozbrowser iframe is not same origin
           // with the parent. We need to access its content through the wrapper.
           var privilegedIframe = SpecialPowers.wrap(iframe);
           privilegedIframe.contentWindow.addEventListener("MozAfterPaint", function afterPaint(e) {
             privilegedIframe.contentWindow.removeEventListener("MozAfterPaint", afterPaint);
@@ -97,17 +97,16 @@
             isnot(document.activeElement, iframe, "[explicit innerIframe.focus()] iframe should not have the focus");
 
             witness.focus();
             synthesizeMouseAtCenter(innerIframe, {}, iframeWindow);
             is(document.activeElement, witness, "[synthesize mouse click inner iframe] witness should have the focus");
 
             // Test the case when iframe contains <area> and .focus()
             // is called and explicit focus using mouse
-            witness.focus();
 
             // Wait for paint to setup frame for area. Currently the area frame
             // map is reset for each paint. If we are in the middle of a paint
             // then the area will not be focusable.
             privilegedIframe.contentWindow.addEventListener("MozAfterPaint", function afterPaintArea(e) {
               privilegedIframe.contentWindow.removeEventListener("MozAfterPaint", afterPaintArea);
               var innerArea = privilegedIframe.contentDocument.getElementsByTagName("area")[0];
               innerArea.focus();
@@ -121,17 +120,22 @@
               witness.focus();
               is(document.activeElement, witness, "witness should have the focus");
               synthesizeKey("VK_TAB", {});
               isnot(document.activeElement, iframe, "[synthesize tab key] iframe should not have the focus");
               is(document.activeElement, witness2, "witness2 should have the focus");
 
               SimpleTest.finish();
             });
+            witness.focus();
+            // force reflow
+            var reflow = iframe.offsetLeft;
           });
+          // force reflow
+          var reflow = iframe.offsetLeft;
         });
 
         document.body.appendChild(witness);
         document.body.appendChild(iframe);
         document.body.appendChild(witness2);
       }
       addEventListener("load", function() {
         SpecialPowers.addPermission("browser", true, document);
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -252,19 +252,41 @@ NS_NewHTMLElement(Element** aResult, alr
   nsIParserService* parserService = nsContentUtils::GetParserService();
   if (!parserService)
     return NS_ERROR_OUT_OF_MEMORY;
 
   nsIAtom *name = nodeInfo->NameAtom();
 
   NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML), 
                "Trying to HTML elements that don't have the XHTML namespace");
-  
-  *aResult = CreateHTMLElement(parserService->
-                                 HTMLCaseSensitiveAtomTagToId(name),
+
+  // Per the Custom Element specification, unknown tags that are valid custom
+  // element names should be HTMLElement instead of HTMLUnknownElement.
+  int32_t tag = parserService->HTMLCaseSensitiveAtomTagToId(name);
+  if (tag == eHTMLTag_userdefined &&
+      nsContentUtils::IsCustomElementName(name)) {
+    NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
+    if (!*aResult) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    nsIDocument* doc = aNodeInfo.get()->GetDocument();
+
+    // Element may be unresolved at this point.
+    doc->RegisterUnresolvedElement(*aResult);
+
+    // Try to enqueue a created callback. The custom element data will be set
+    // and created callback will be enqueued if the custom element type
+    // has already been registered.
+    doc->EnqueueLifecycleCallback(nsIDocument::eCreated, *aResult);
+
+    return NS_OK;
+  }
+
+  *aResult = CreateHTMLElement(tag,
                                nodeInfo.forget(), aFromParser).get();
   return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 already_AddRefed<nsGenericHTMLElement>
 CreateHTMLElement(uint32_t aNodeType, already_AddRefed<nsINodeInfo> aNodeInfo,
                   FromParser aFromParser)
 {
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -81,16 +81,18 @@ public:
     return mWriteLevel != uint32_t(0);
   }
 
   virtual NS_HIDDEN_(nsContentList*) GetForms();
  
   virtual NS_HIDDEN_(nsContentList*) GetFormControls();
  
   // nsIDOMDocument interface
+  using nsDocument::CreateElement;
+  using nsDocument::CreateElementNS;
   NS_FORWARD_NSIDOMDOCUMENT(nsDocument::)
 
   // And explicitly import the things from nsDocument that we just shadowed
   using nsDocument::GetImplementation;
   using nsDocument::GetTitle;
   using nsDocument::SetTitle;
   using nsDocument::GetLastStyleSheetSet;
   using nsDocument::MozSetImageElement;
--- a/content/svg/content/src/SVGElementFactory.cpp
+++ b/content/svg/content/src/SVGElementFactory.cpp
@@ -98,16 +98,24 @@ void
 SVGElementFactory::Shutdown()
 {
   if (sTagAtomTable) {
     PL_HashTableDestroy(sTagAtomTable);
     sTagAtomTable = nullptr;
   }
 }
 
+bool
+SVGElementFactory::Exists(nsIAtom *aTag)
+{
+  MOZ_ASSERT(sTagAtomTable, "no lookup table, needs SVGElementFactory::Init");
+  void* tag = PL_HashTableLookupConst(sTagAtomTable, aTag);
+  return tag != nullptr;
+}
+
 nsresult
 NS_NewSVGElement(Element** aResult, already_AddRefed<nsINodeInfo> aNodeInfo,
                  FromParser aFromParser)
 {
   NS_ASSERTION(sTagAtomTable, "no lookup table, needs SVGElementFactory::Init");
 
   nsIAtom* name = aNodeInfo.get()->NameAtom();
 
--- a/content/svg/content/src/SVGElementFactory.h
+++ b/content/svg/content/src/SVGElementFactory.h
@@ -1,21 +1,25 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_SVGElementFactory_h
 #define mozilla_dom_SVGElementFactory_h
 
+class nsIAtom;
+
 namespace mozilla {
 namespace dom {
 
 class SVGElementFactory {
 public:
   static void Init();
   static void Shutdown();
+
+  static bool Exists(nsIAtom *aTag);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_SVGElementFactory_h */
--- a/content/xul/document/src/XULDocument.h
+++ b/content/xul/document/src/XULDocument.h
@@ -139,16 +139,18 @@ public:
 
     // nsINode interface overrides
     virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
     // nsIDOMNode interface
     NS_FORWARD_NSIDOMNODE_TO_NSINODE
 
     // nsIDOMDocument interface
+    using nsDocument::CreateElement;
+    using nsDocument::CreateElementNS;
     NS_FORWARD_NSIDOMDOCUMENT(XMLDocument::)
     // And explicitly import the things from nsDocument that we just shadowed
     using nsDocument::GetImplementation;
     using nsDocument::GetTitle;
     using nsDocument::SetTitle;
     using nsDocument::GetLastStyleSheetSet;
     using nsDocument::MozSetImageElement;
     using nsDocument::GetMozFullScreenElement;
new file mode 100644
--- /dev/null
+++ b/dom/ipc/ColorPickerParent.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set sw=4 ts=8 et 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 "ColorPickerParent.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIDocument.h"
+#include "nsIDOMWindow.h"
+#include "mozilla/unused.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/TabParent.h"
+
+using mozilla::unused;
+using namespace mozilla::dom;
+
+NS_IMPL_ISUPPORTS1(ColorPickerParent::ColorPickerShownCallback,
+                   nsIColorPickerShownCallback);
+
+NS_IMETHODIMP
+ColorPickerParent::ColorPickerShownCallback::Update(const nsAString& aColor)
+{
+  if (mColorPickerParent) {
+    unused << mColorPickerParent->SendUpdate(nsString(aColor));
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ColorPickerParent::ColorPickerShownCallback::Done(const nsAString& aColor)
+{
+  if (mColorPickerParent) {
+    unused << mColorPickerParent->Send__delete__(mColorPickerParent,
+                                                 nsString(aColor));
+  }
+  return NS_OK;
+}
+
+void
+ColorPickerParent::ColorPickerShownCallback::Destroy()
+{
+  mColorPickerParent = nullptr;
+}
+
+bool
+ColorPickerParent::CreateColorPicker()
+{
+  mPicker = do_CreateInstance("@mozilla.org/colorpicker;1");
+  if (!mPicker) {
+    return false;
+  }
+
+  Element* ownerElement = static_cast<TabParent*>(Manager())->GetOwnerElement();
+  if (!ownerElement) {
+    return false;
+  }
+
+  nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(ownerElement->OwnerDoc()->GetWindow());
+  if (!window) {
+    return false;
+  }
+
+  return NS_SUCCEEDED(mPicker->Init(window, mTitle, mInitialColor));
+}
+
+bool
+ColorPickerParent::RecvOpen()
+{
+  if (!CreateColorPicker()) {
+    unused << Send__delete__(this, mInitialColor);
+    return true;
+  }
+
+  mCallback = new ColorPickerShownCallback(this);
+
+  mPicker->Open(mCallback);
+  return true;
+};
+
+void
+ColorPickerParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  if (mCallback) {
+    mCallback->Destroy();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/ipc/ColorPickerParent.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set sw=4 ts=8 et 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/. */
+
+#ifndef mozilla_dom_ColorPickerParent_h
+#define mozilla_dom_ColorPickerParent_h
+
+#include "mozilla/dom/PColorPickerParent.h"
+#include "nsIColorPicker.h"
+
+namespace mozilla {
+namespace dom {
+
+class ColorPickerParent : public PColorPickerParent
+{
+ public:
+  ColorPickerParent(const nsString& aTitle,
+                    const nsString& aInitialColor)
+  : mTitle(aTitle)
+  , mInitialColor(aInitialColor)
+  {}
+
+  virtual ~ColorPickerParent() {}
+
+  virtual bool RecvOpen() MOZ_OVERRIDE;
+  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  class ColorPickerShownCallback MOZ_FINAL
+    : public nsIColorPickerShownCallback
+  {
+  public:
+    ColorPickerShownCallback(ColorPickerParent* aColorPickerParnet)
+      : mColorPickerParent(aColorPickerParnet)
+    {}
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICOLORPICKERSHOWNCALLBACK
+
+    void Destroy();
+
+  private:
+    ColorPickerParent* mColorPickerParent;
+  };
+
+ private:
+  bool CreateColorPicker();
+
+  nsRefPtr<ColorPickerShownCallback> mCallback;
+  nsCOMPtr<nsIColorPicker> mPicker;
+
+  nsString mTitle;
+  nsString mInitialColor;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ColorPickerParent_h
\ No newline at end of file
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
 
 /* 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 PBlob;
+include protocol PColorPicker;
 include protocol PContent;
 include protocol PContentDialog;
 include protocol PDocumentRenderer;
 include protocol PContentPermissionRequest;
 include protocol PFilePicker;
 include protocol PRenderFrame;
 include protocol POfflineCacheUpdate;
 include protocol PIndexedDB;
@@ -52,16 +53,17 @@ using mozilla::CSSToScreenScale from "Un
 
 namespace mozilla {
 namespace dom {
 
 intr protocol PBrowser
 {
     manager PContent;
 
+    manages PColorPicker;
     manages PContentDialog;
     manages PDocumentRenderer;
     manages PContentPermissionRequest;
     manages PFilePicker;
     manages PRenderFrame;
     manages POfflineCacheUpdate;
     manages PIndexedDB;
 
@@ -217,16 +219,22 @@ parent:
     /**
      * Show/hide a tooltip when the mouse hovers over an element in the content
      * document.
      */
     ShowTooltip(uint32_t x, uint32_t y, nsString tooltip);
     HideTooltip();
 
     /**
+     * Create an asynchronous color picker on the parent side,
+     * but don't open it yet.
+     */
+    PColorPicker(nsString title, nsString initialColor);
+
+    /**
      * Initiates an asynchronous request for permission for the
      * provided principal.
      *
      * @param aRequests
      *   The array of permissions to request.
      * @param aPrincipal
      *   The principal of the request.
      *
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PColorPicker.ipdl
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+
+/* 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 PBrowser;
+
+namespace mozilla {
+namespace dom {
+
+protocol PColorPicker
+{
+    manager PBrowser;
+
+parent:
+    Open();
+
+child:
+    Update(nsString color);
+
+    __delete__(nsString color);
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -70,16 +70,18 @@
 #include "StructuredCloneUtils.h"
 #include "nsViewportInfo.h"
 #include "JavaScriptChild.h"
 #include "APZCCallbackHelper.h"
 #include "nsILoadContext.h"
 #include "ipc/nsGUIEventIPC.h"
 #include "mozilla/gfx/Matrix.h"
 
+#include "nsColorPickerProxy.h"
+
 #ifdef DEBUG
 #include "PCOMContentPermissionRequestChild.h"
 #endif /* DEBUG */
 
 #define BROWSER_ELEMENT_CHILD_SCRIPT \
     NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
 
 using namespace mozilla;
@@ -2088,16 +2090,31 @@ TabChild::RecvPDocumentRendererConstruct
                                       renderFlags, flushLayout,
                                       renderSize, data);
     if (!ret)
         return true; // silently ignore
 
     return PDocumentRendererChild::Send__delete__(actor, renderSize, data);
 }
 
+PColorPickerChild*
+TabChild::AllocPColorPickerChild(const nsString&, const nsString&)
+{
+  NS_RUNTIMEABORT("unused");
+  return nullptr;
+}
+
+bool
+TabChild::DeallocPColorPickerChild(PColorPickerChild* aColorPicker)
+{
+  nsColorPickerProxy* picker = static_cast<nsColorPickerProxy*>(aColorPicker);
+  NS_RELEASE(picker);
+  return true;
+}
+
 PContentDialogChild*
 TabChild::AllocPContentDialogChild(const uint32_t&,
                                    const nsCString&,
                                    const nsCString&,
                                    const InfallibleTArray<int>&,
                                    const InfallibleTArray<nsString>&)
 {
   return new ContentDialogChild();
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -273,23 +273,29 @@ public:
     virtual bool RecvPDocumentRendererConstructor(PDocumentRendererChild* actor,
                                                   const nsRect& documentRect,
                                                   const gfx::Matrix& transform,
                                                   const nsString& bgcolor,
                                                   const uint32_t& renderFlags,
                                                   const bool& flushLayout,
                                                   const nsIntSize& renderSize) MOZ_OVERRIDE;
 
+    virtual PColorPickerChild*
+    AllocPColorPickerChild(const nsString& title, const nsString& initialColor) MOZ_OVERRIDE;
+    virtual bool DeallocPColorPickerChild(PColorPickerChild* actor) MOZ_OVERRIDE;
+
+
     virtual PContentDialogChild* AllocPContentDialogChild(const uint32_t&,
                                                           const nsCString&,
                                                           const nsCString&,
                                                           const InfallibleTArray<int>&,
                                                           const InfallibleTArray<nsString>&)
                                                           MOZ_OVERRIDE;
     virtual bool DeallocPContentDialogChild(PContentDialogChild* aDialog) MOZ_OVERRIDE;
+
     static void ParamsToArrays(nsIDialogParamBlock* aParams,
                                InfallibleTArray<int>& aIntParams,
                                InfallibleTArray<nsString>& aStringParams);
     static void ArraysToParams(const InfallibleTArray<int>& aIntParams,
                                const InfallibleTArray<nsString>& aStringParams,
                                nsIDialogParamBlock* aParams);
 
 #ifdef DEBUG
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -50,16 +50,17 @@
 #include "nsIWindowWatcher.h"
 #include "nsPIDOMWindow.h"
 #include "nsPrintfCString.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "private/pprio.h"
 #include "PermissionMessageUtils.h"
 #include "StructuredCloneUtils.h"
+#include "ColorPickerParent.h"
 #include "JavaScriptParent.h"
 #include "FilePickerParent.h"
 #include "TabChild.h"
 #include "LoadContext.h"
 #include "nsNetCID.h"
 #include <algorithm>
 
 using namespace mozilla::dom;
@@ -1620,16 +1621,30 @@ TabParent::GetAuthPrompt(uint32_t aPromp
     window = do_QueryInterface(frame->OwnerDoc()->GetWindow());
 
   // Get an auth prompter for our window so that the parenting
   // of the dialogs works as it should when using tabs.
   return wwatch->GetPrompt(window, iid,
                            reinterpret_cast<void**>(aResult));
 }
 
+PColorPickerParent*
+TabParent::AllocPColorPickerParent(const nsString& aTitle,
+                                   const nsString& aInitialColor)
+{
+  return new ColorPickerParent(aTitle, aInitialColor);
+}
+
+bool
+TabParent::DeallocPColorPickerParent(PColorPickerParent* actor)
+{
+  delete actor;
+  return true;
+}
+
 PContentDialogParent*
 TabParent::AllocPContentDialogParent(const uint32_t& aType,
                                      const nsCString& aName,
                                      const nsCString& aFeatures,
                                      const InfallibleTArray<int>& aIntParams,
                                      const InfallibleTArray<nsString>& aStringParams)
 {
   ContentDialogParent* parent = new ContentDialogParent();
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -182,16 +182,21 @@ public:
                                 const ViewID& aViewId,
                                 const CSSRect& aRect) MOZ_OVERRIDE;
     virtual bool RecvUpdateZoomConstraints(const uint32_t& aPresShellId,
                                            const ViewID& aViewId,
                                            const bool& aIsRoot,
                                            const ZoomConstraints& aConstraints) MOZ_OVERRIDE;
     virtual bool RecvContentReceivedTouch(const ScrollableLayerGuid& aGuid,
                                           const bool& aPreventDefault) MOZ_OVERRIDE;
+
+    virtual PColorPickerParent*
+    AllocPColorPickerParent(const nsString& aTitle, const nsString& aInitialColor) MOZ_OVERRIDE;
+    virtual bool DeallocPColorPickerParent(PColorPickerParent* aColorPicker) MOZ_OVERRIDE;
+
     virtual PContentDialogParent*
     AllocPContentDialogParent(const uint32_t& aType,
                               const nsCString& aName,
                               const nsCString& aFeatures,
                               const InfallibleTArray<int>& aIntParams,
                               const InfallibleTArray<nsString>& aStringParams) MOZ_OVERRIDE;
     virtual bool DeallocPContentDialogParent(PContentDialogParent* aDialog) MOZ_OVERRIDE
     {
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -35,16 +35,17 @@ EXPORTS.mozilla.dom += [
 EXPORTS.mozilla += [
     'AppProcessChecker.h',
     'PreallocatedProcessManager.h',
     'ProcessPriorityManager.h',
 ]
 
 UNIFIED_SOURCES += [
     'AppProcessChecker.cpp',
+    'ColorPickerParent.cpp',
     'ContentChild.cpp',
     'ContentParent.cpp',
     'ContentProcess.cpp',
     'CrashReporterParent.cpp',
     'FilePickerParent.cpp',
     'PermissionMessageUtils.cpp',
     'PreallocatedProcessManager.cpp',
     'ProcessPriorityManager.cpp',
@@ -63,16 +64,17 @@ SOURCES += [
     'CrashReporterChild.cpp',
 ]
 
 IPDL_SOURCES += [
     'DOMTypes.ipdlh',
     'PBlob.ipdl',
     'PBlobStream.ipdl',
     'PBrowser.ipdl',
+    'PColorPicker.ipdl',
     'PContent.ipdl',
     'PContentDialog.ipdl',
     'PContentPermission.ipdlh',
     'PContentPermissionRequest.ipdl',
     'PCrashReporter.ipdl',
     'PDocumentRenderer.ipdl',
     'PFilePicker.ipdl',
     'PMemoryReportRequest.ipdl',
--- a/dom/mobileconnection/interfaces/nsIDOMMobileConnection.idl
+++ b/dom/mobileconnection/interfaces/nsIDOMMobileConnection.idl
@@ -138,18 +138,19 @@ interface nsIDOMMozMobileConnection : ns
   nsIDOMDOMRequest selectNetworkAutomatically();
 
   /**
    * Set preferred network type
    *
    * @param type
    *        DOMString indicates the desired preferred network type.
    *        Possible values: 'wcdma/gsm', 'gsm', 'wcdma', 'wcdma/gsm-auto',
-   *                         'cdma/evdo', 'cdma', 'evdo', or
-   *                         'wcdma/gsm/cdma/evdo'.
+   *                         'cdma/evdo', 'cdma', 'evdo', 'wcdma/gsm/cdma/evdo',
+   *                         'lte/cdma/evdo', 'lte/wcdma/gsm',
+   *                         'lte/wcdma/gsm/cdma/evdo' or 'lte'.
    *
    * If successful, the request's onsuccess will be called.
    *
    * Otherwise, the request's onerror will be called, and the request's error
    * will be either 'RadioNotAvailable', 'RequestNotSupported',
    * 'InvalidParameter', 'ModeNotSupported' or 'GenericFailure'
    *
    * TODO: param "type" should be a WebIDL enum when this interface is converted
@@ -158,17 +159,18 @@ interface nsIDOMMozMobileConnection : ns
   nsIDOMDOMRequest setPreferredNetworkType(in DOMString type);
 
   /**
    * Query current preferred network type
    *
    * If successful, the request's onsuccess will be called. And the request's
    * result will be a string indicating the current preferred network type.
    * The value will be either 'wcdma/gsm', 'gsm', 'wcdma', 'wcdma/gsm-auto',
-   * 'cdma/evdo', 'cdma', 'evdo', or 'wcdma/gsm/cdma/evdo'.
+   * 'cdma/evdo', 'cdma', 'evdo', 'wcdma/gsm/cdma/evdo', 'lte/cdma/evdo',
+   * 'lte/wcdma/gsm', 'lte/wcdma/gsm/cdma/evdo' or 'lte'.
    *
    * Otherwise, the request's onerror will be called, and the request's error
    * will be either 'RadioNotAvailable', 'RequestNotSupported',
    * or 'GenericFailure'
    */
   nsIDOMDOMRequest getPreferredNetworkType();
 
   /**
--- a/dom/mobileconnection/tests/marionette/test_mobile_preferred_network_type.js
+++ b/dom/mobileconnection/tests/marionette/test_mobile_preferred_network_type.js
@@ -1,153 +1,131 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = "mobile_header.js";
 
-SpecialPowers.addPermission("mobileconnection", true, document);
+function doSetAndVerifyPreferredNetworkType(preferredNetworkType, callback) {
+  log("setPreferredNetworkType to '" + preferredNetworkType + "'.");
+  let setRequest = mobileConnection.setPreferredNetworkType(preferredNetworkType);
+  ok(setRequest instanceof DOMRequest,
+     "setRequest instanceof " + setRequest.constructor);
+
+  setRequest.onsuccess = function() {
+    log("Verify preferred network.");
+    let getRequest = mobileConnection.getPreferredNetworkType();
+    ok(getRequest instanceof DOMRequest,
+       "getRequest instanceof " + getRequest.constructor);
+
+    getRequest.onsuccess = function() {
+      is(getRequest.result, preferredNetworkType, "Check preferred network type.");
+      callback();
+    };
+
+    getRequest.onerror = function() {
+      ok(false, "getPreferredNetworkType got error: " + getRequest.error.name);
+      callback();
+    };
+  };
 
-let connection = navigator.mozMobileConnections[0];
-ok(connection instanceof MozMobileConnection,
-   "connection is instanceof " + connection.constructor);
+  setRequest.onerror = function() {
+    ok(false, "setPreferredNetwork got error: " + setRequest.error.name);
+    callback();
+  };
+}
+
+function doFailToSetPreferredNetworkType(preferredNetworkType, expectedError, callback) {
+  log("setPreferredNetworkType to '" + preferredNetworkType + "'.");
+  let request = mobileConnection.setPreferredNetworkType(preferredNetworkType);
+  ok(request instanceof DOMRequest,
+     "request instanceof " + request.constructor);
 
-function testSupportedNetworkTypes() {
-  let supportedNetworkTypes = connection.supportedNetworkTypes;
+  request.onsuccess = function() {
+    ok(false, "Should not success");
+    callback();
+  };
+
+  request.onerror = function() {
+    is(request.error.name, expectedError, "Check error message.");
+    callback();
+  };
+}
+
+/* Test supportedNetworkTypes */
+taskHelper.push(function testSupportedNetworkTypes() {
+  let supportedNetworkTypes = mobileConnection.supportedNetworkTypes;
 
   ok(Array.isArray(supportedNetworkTypes), "supportedNetworkTypes should be an array");
   ok(supportedNetworkTypes.indexOf("gsm") >= 0, "Should support 'gsm'");
   ok(supportedNetworkTypes.indexOf("wcdma") >= 0, "Should support 'wcdma'");
   ok(supportedNetworkTypes.indexOf("cdma") >= 0, "Should support 'cdma'");
   ok(supportedNetworkTypes.indexOf("evdo") >= 0, "Should support 'evdo'");
 
-  runNextTest();
-}
-
-function setPreferredNetworkType(type, callback) {
-  log("setPreferredNetworkType: " + type);
-
-  let request = connection.setPreferredNetworkType(type);
-  ok(request instanceof DOMRequest,
-     "request instanceof " + request.constructor);
-
-  request.onsuccess = function onsuccess() {
-    ok(true, "request success");
-    callback();
-  }
-  request.onerror = function onerror() {
-    ok(false, request.error);
-    callback();
-  }
-}
-
-function getPreferredNetworkType(callback) {
-  log("getPreferredNetworkType");
-
-  let request = connection.getPreferredNetworkType();
-  ok(request instanceof DOMRequest,
-     "request instanceof " + request.constructor);
+  taskHelper.runNext();
+});
 
-  request.onsuccess = function onsuccess() {
-    ok(true, "request success");
-    log("getPreferredNetworkType: " + request.result);
-    callback(request.result);
-  }
-  request.onerror = function onerror() {
-    ok(false, request.error);
-    callback();
-  }
-}
-
-function failToSetPreferredNetworkType(type, expectedError, callback) {
-  log("failToSetPreferredNetworkType: " + type + ", expected error: "
-    + expectedError);
-
-  let request = connection.setPreferredNetworkType(type);
-  ok(request instanceof DOMRequest,
-     "request instanceof " + request.constructor);
+/* Test switching to supported preferred types */
+taskHelper.push(function testPreferredNetworkTypes() {
+  let supportedTypes = [
+    'gsm',
+    'wcdma',
+    'wcdma/gsm-auto',
+    'cdma/evdo',
+    'evdo',
+    'cdma',
+    'wcdma/gsm/cdma/evdo',
+    // Restore to default
+    'wcdma/gsm'
+  ];
 
-  request.onsuccess = function onsuccess() {
-    ok(false, "request should not succeed");
-    callback();
-  }
-  request.onerror = function onerror() {
-    ok(true, "request error");
-    is(request.error.name, expectedError);
-    callback();
-  }
-}
-
-function setAndVerifyNetworkType(type) {
-  setPreferredNetworkType(type, function() {
-    getPreferredNetworkType(function(result) {
-      is(result, type);
-      testPreferredNetworkTypes();
-    });
-  });
-}
+  // Run all test data.
+  (function do_call() {
+    let type = supportedTypes.shift();
+    if (!type) {
+      taskHelper.runNext();
+      return;
+    }
+    doSetAndVerifyPreferredNetworkType(type, do_call);
+  })();
+});
 
-function testPreferredNetworkTypes() {
-  let networkType = supportedTypes.shift();
-  if (!networkType) {
-    runNextTest();
-    return;
-  }
-  setAndVerifyNetworkType(networkType);
-}
+/* Test switching to unsupported preferred types */
+taskHelper.push(function testUnsupportedPreferredNetworkTypes() {
+  // Currently emulator doesn't support lte network
+  let unsupportedTypes = [
+    'lte/cdma/evdo',
+    'lte/wcdma/gsm',
+    'lte/wcdma/gsm/cdma/evdo',
+    'lte'
+  ];
 
-function failToSetAndVerifyNetworkType(type, expectedError, previousType) {
-  failToSetPreferredNetworkType(type, expectedError, function() {
-    getPreferredNetworkType(function(result) {
-      // should return the previous selected type.
-      is(result, previousType);
-      testInvalidNetworkTypes();
-    });
-  });
-}
-
-function testInvalidNetworkTypes() {
-  let networkType = invalidTypes.shift();
-  if (!networkType) {
-    runNextTest();
-    return;
-  }
-  failToSetAndVerifyNetworkType(networkType, "InvalidParameter",
-                                "wcdma/gsm");
-}
+  // Run all test data.
+  (function do_call() {
+    let type = unsupportedTypes.shift();
+    if (!type) {
+      taskHelper.runNext();
+      return;
+    }
+    doFailToSetPreferredNetworkType(type, "ModeNotSupported", do_call);
+  })();
+});
 
-let supportedTypes = [
-  'gsm',
-  'wcdma',
-  'wcdma/gsm-auto',
-  'cdma/evdo',
-  'evdo',
-  'cdma',
-  'wcdma/gsm/cdma/evdo',
-  'wcdma/gsm' // restore to default
-];
-
-let invalidTypes = [
-  ' ',
-  'AnInvalidType'
-];
+/* Test switching to invalid preferred types */
+taskHelper.push(function testInvalidPreferredNetworkTypes() {
+  let invalidTypes = [
+    ' ',
+    'AnInvalidType'
+  ];
 
-let tests = [
-  testSupportedNetworkTypes,
-  testPreferredNetworkTypes,
-  testInvalidNetworkTypes
-];
+  // Run all test data.
+  (function do_call() {
+    let type = invalidTypes.shift();
+    if (!type) {
+      taskHelper.runNext();
+      return;
+    }
+    doFailToSetPreferredNetworkType(type, "InvalidParameter", do_call);
+  })();
+});
 
-function runNextTest() {
-  let test = tests.shift();
-  if (!test) {
-    cleanUp();
-    return;
-  }
-
-  test();
-}
-
-function cleanUp() {
-  SpecialPowers.removePermission("mobileconnection", document);
-  finish();
-}
-
-runNextTest();
+// Start test
+taskHelper.runNext();
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -407,26 +407,34 @@ this.NETWORK_INFO_MESSAGE_TYPES = [
 this.GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM = "wcdma/gsm";
 this.GECKO_PREFERRED_NETWORK_TYPE_GSM_ONLY = "gsm";
 this.GECKO_PREFERRED_NETWORK_TYPE_WCDMA_ONLY = "wcdma";
 this.GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM_AUTO = "wcdma/gsm-auto";
 this.GECKO_PREFERRED_NETWORK_TYPE_CDMA_EVDO = "cdma/evdo";
 this.GECKO_PREFERRED_NETWORK_TYPE_CDMA_ONLY = "cdma";
 this.GECKO_PREFERRED_NETWORK_TYPE_EVDO_ONLY = "evdo";
 this.GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM_CDMA_EVDO = "wcdma/gsm/cdma/evdo";
+this.GECKO_PREFERRED_NETWORK_TYPE_LTE_CDMA_EVDO = "lte/cdma/evdo";
+this.GECKO_PREFERRED_NETWORK_TYPE_LTE_WCDMA_GSM = "lte/wcdma/gsm";
+this.GECKO_PREFERRED_NETWORK_TYPE_LTE_WCDMA_GSM_CDMA_EVDO = "lte/wcdma/gsm/cdma/evdo";
+this.GECKO_PREFERRED_NETWORK_TYPE_LTE_ONLY = "lte";
 this.GECKO_PREFERRED_NETWORK_TYPE_DEFAULT = GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM;
 this.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO = [
   GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM,
   GECKO_PREFERRED_NETWORK_TYPE_GSM_ONLY,
   GECKO_PREFERRED_NETWORK_TYPE_WCDMA_ONLY,
   GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM_AUTO,
   GECKO_PREFERRED_NETWORK_TYPE_CDMA_EVDO,
   GECKO_PREFERRED_NETWORK_TYPE_CDMA_ONLY,
   GECKO_PREFERRED_NETWORK_TYPE_EVDO_ONLY,
-  GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM_CDMA_EVDO
+  GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM_CDMA_EVDO,
+  GECKO_PREFERRED_NETWORK_TYPE_LTE_CDMA_EVDO,
+  GECKO_PREFERRED_NETWORK_TYPE_LTE_WCDMA_GSM,
+  GECKO_PREFERRED_NETWORK_TYPE_LTE_WCDMA_GSM_CDMA_EVDO,
+  GECKO_PREFERRED_NETWORK_TYPE_LTE_ONLY
 ];
 
 this.GECKO_SUPPORTED_NETWORK_TYPES_DEFAULT = "gsm,wcdma,cdma,evdo";
 this.GECKO_SUPPORTED_NETWORK_TYPES = [
   "gsm",
   "wcdma",
   "cdma",
   "evdo",
@@ -2437,61 +2445,105 @@ this.GECKO_CARDSTATE_UNDETECTED         
 this.GECKO_CARDSTATE_ILLEGAL                       = "illegal";
 this.GECKO_CARDSTATE_UNKNOWN                       = "unknown";
 this.GECKO_CARDSTATE_PIN_REQUIRED                  = "pinRequired";
 this.GECKO_CARDSTATE_PUK_REQUIRED                  = "pukRequired";
 this.GECKO_CARDSTATE_PERSONALIZATION_IN_PROGRESS   = "personalizationInProgress";
 this.GECKO_CARDSTATE_PERSONALIZATION_READY         = "personalizationReady";
 this.GECKO_CARDSTATE_NETWORK_LOCKED                = "networkLocked";
 this.GECKO_CARDSTATE_NETWORK_SUBSET_LOCKED         = "networkSubsetLocked";
+this.GECKO_CARDSTATE_NETWORK1_LOCKED               = "network1Locked";
+this.GECKO_CARDSTATE_NETWORK2_LOCKED               = "network2Locked";
+this.GECKO_CARDSTATE_HRPD_NETWORK_LOCKED           = "hrpdNetworkLocked";
 this.GECKO_CARDSTATE_CORPORATE_LOCKED              = "corporateLocked";
 this.GECKO_CARDSTATE_SERVICE_PROVIDER_LOCKED       = "serviceProviderLocked";
 this.GECKO_CARDSTATE_SIM_LOCKED                    = "simPersonalizationLock";
+this.GECKO_CARDSTATE_RUIM_CORPORATE_LOCKED         = "ruimCorporateLocked";
+this.GECKO_CARDSTATE_RUIM_SERVICE_PROVIDER_LOCKED  = "ruimServiceProviderLocked";
+this.GECKO_CARDSTATE_RUIM_LOCKED                   = "ruimPersonalizationLock";
 this.GECKO_CARDSTATE_NETWORK_PUK_REQUIRED          = "networkPukRequired";
 this.GECKO_CARDSTATE_NETWORK_SUBSET_PUK_REQUIRED   = "networkSubsetPukRequired";
+this.GECKO_CARDSTATE_NETWORK1_PUK_REQUIRED         = "network1PukRequired";
+this.GECKO_CARDSTATE_NETWORK2_PUK_REQUIRED         = "network2PukRequired";
+this.GECKO_CARDSTATE_HRPD_NETWORK_PUK_REQUIRED     = "hrpdNetworkPukRequired";
 this.GECKO_CARDSTATE_CORPORATE_PUK_REQUIRED        = "corporatePukRequired";
 this.GECKO_CARDSTATE_SERVICE_PROVIDER_PUK_REQUIRED = "serviceProviderPukRequired";
 this.GECKO_CARDSTATE_SIM_PUK_REQUIRED              = "simPersonalizationPukRequired";
+this.GECKO_CARDSTATE_RUIM_CORPORATE_PUK_REQUIRED   = "ruimCorporatePukRequired";
+this.GECKO_CARDSTATE_RUIM_SERVICE_PROVIDER_PUK_REQUIRED = "ruimServiceProviderPukRequired";
+this.GECKO_CARDSTATE_RUIM_PUK_REQUIRED             = "ruimPersonalizationPukRequired";
 this.GECKO_CARDSTATE_READY                         = "ready";
 this.GECKO_CARDSTATE_PERMANENT_BLOCKED             = "permanentBlocked";
 
 this.GECKO_CARDLOCK_PIN      = "pin";
 this.GECKO_CARDLOCK_PIN2     = "pin2";
 this.GECKO_CARDLOCK_PUK      = "puk";
 this.GECKO_CARDLOCK_PUK2     = "puk2";
 this.GECKO_CARDLOCK_FDN      = "fdn";
 this.GECKO_CARDLOCK_NCK      = "nck";
+this.GECKO_CARDLOCK_NCK1     = "nck1";
+this.GECKO_CARDLOCK_NCK2     = "nck2";
+this.GECKO_CARDLOCK_HNCK     = "hnck";
 this.GECKO_CARDLOCK_CCK      = "cck";
 this.GECKO_CARDLOCK_SPCK     = "spck";
+this.GECKO_CARDLOCK_RCCK     = "rcck";
+this.GECKO_CARDLOCK_RSPCK    = "rspck";
 this.GECKO_CARDLOCK_NCK_PUK  = "nckPuk";
+this.GECKO_CARDLOCK_NCK1_PUK = "nck1Puk";
+this.GECKO_CARDLOCK_NCK2_PUK = "nck2Puk";
+this.GECKO_CARDLOCK_HNCK_PUK = "hnckPuk";
 this.GECKO_CARDLOCK_CCK_PUK  = "cckPuk";
 this.GECKO_CARDLOCK_SPCK_PUK = "spckPuk";
+this.GECKO_CARDLOCK_RCCK_PUK = "rcckPuk";
+this.GECKO_CARDLOCK_RSPCK_PUK = "rspckPuk";
 
 // 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;
 PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_NETWORK_SUBSET] = GECKO_CARDSTATE_NETWORK_SUBSET_LOCKED;
 PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_CORPORATE] = GECKO_CARDSTATE_CORPORATE_LOCKED;
 PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER] = GECKO_CARDSTATE_SERVICE_PROVIDER_LOCKED;
 PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_SIM] = GECKO_CARDSTATE_SIM_LOCKED;
 PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_NETWORK_PUK] = GECKO_CARDSTATE_NETWORK_PUK_REQUIRED;
 PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK] = GECKO_CARDSTATE_NETWORK_SUBSET_PUK_REQUIRED;
 PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_CORPORATE_PUK] = GECKO_CARDSTATE_CORPORATE_PUK_REQUIRED;
 PERSONSUBSTATE[CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK] = GECKO_CARDSTATE_SERVICE_PROVIDER_PUK_REQUIRED;
+PERSONSUBSTATE[CARD_PERSOSUBSTATE_RUIM_NETWORK1] = GECKO_CARDSTATE_NETWORK1_LOCKED;
+PERSONSUBSTATE[CARD_PERSOSUBSTATE_RUIM_NETWORK2] = GECKO_CARDSTATE_NETWORK2_LOCKED;
+PERSONSUBSTATE[CARD_PERSOSUBSTATE_RUIM_HRPD] = GECKO_CARDSTATE_HRPD_NETWORK_LOCKED;
+PERSONSUBSTATE[CARD_PERSOSUBSTATE_RUIM_CORPORATE] = GECKO_CARDSTATE_RUIM_CORPORATE_LOCKED;
+PERSONSUBSTATE[CARD_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER] = GECKO_CARDSTATE_RUIM_SERVICE_PROVIDER_LOCKED;
+PERSONSUBSTATE[CARD_PERSOSUBSTATE_RUIM_RUIM] = GECKO_CARDSTATE_RUIM_LOCKED;
+PERSONSUBSTATE[CARD_PERSOSUBSTATE_RUIM_NETWORK1_PUK] = GECKO_CARDSTATE_NETWORK1_PUK_REQUIRED;
+PERSONSUBSTATE[CARD_PERSOSUBSTATE_RUIM_NETWORK2_PUK] = GECKO_CARDSTATE_NETWORK2_PUK_REQUIRED;
+PERSONSUBSTATE[CARD_PERSOSUBSTATE_RUIM_HRPD_PUK] = GECKO_CARDSTATE_HRPD_NETWORK_PUK_REQUIRED;
+PERSONSUBSTATE[CARD_PERSOSUBSTATE_RUIM_CORPORATE_PUK] = GECKO_CARDSTATE_RUIM_CORPORATE_PUK_REQUIRED;
+PERSONSUBSTATE[CARD_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK] = GECKO_CARDSTATE_RUIM_SERVICE_PROVIDER_PUK_REQUIRED;
+PERSONSUBSTATE[CARD_PERSOSUBSTATE_RUIM_RUIM_PUK] = GECKO_CARDSTATE_RUIM_PUK_REQUIRED;
 
 this.GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK = {};
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_NCK] = CARD_PERSOSUBSTATE_SIM_NETWORK;
+GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_NCK1] = CARD_PERSOSUBSTATE_RUIM_NETWORK1;
+GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_NCK2] = CARD_PERSOSUBSTATE_RUIM_NETWORK2;
+GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_HNCK] = CARD_PERSOSUBSTATE_RUIM_HRPD;
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_CCK] = CARD_PERSOSUBSTATE_SIM_CORPORATE;
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_SPCK] = CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER;
+GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_RCCK] = CARD_PERSOSUBSTATE_RUIM_CORPORATE;
+GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_RSPCK] = CARD_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER;
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_NCK_PUK] = CARD_PERSOSUBSTATE_SIM_NETWORK_PUK;
+GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_NCK1_PUK] = CARD_PERSOSUBSTATE_RUIM_NETWORK1_PUK;
+GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_NCK2_PUK] = CARD_PERSOSUBSTATE_RUIM_NETWORK2_PUK;
+GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_HNCK_PUK] = CARD_PERSOSUBSTATE_RUIM_HRPD_PUK;
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_CCK_PUK] = CARD_PERSOSUBSTATE_SIM_CORPORATE_PUK;
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_SPCK_PUK] = CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK;
+GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_RCCK_PUK] = CARD_PERSOSUBSTATE_RUIM_CORPORATE_PUK;
+GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_RSPCK_PUK] = CARD_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK;
 
 this.GECKO_NETWORK_SELECTION_UNKNOWN   = null;
 this.GECKO_NETWORK_SELECTION_AUTOMATIC = "automatic";
 this.GECKO_NETWORK_SELECTION_MANUAL    = "manual";
 
 this.GECKO_MOBILE_CONNECTION_STATE_UNKNOWN = null;
 this.GECKO_MOBILE_CONNECTION_STATE_NOTSEARCHING = "notSearching";
 this.GECKO_MOBILE_CONNECTION_STATE_SEARCHING = "searching";
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -482,25 +482,35 @@ RilObject.prototype = {
         break;
       case GECKO_CARDLOCK_PUK:
         this.enterICCPUK(options);
         break;
       case GECKO_CARDLOCK_PUK2:
         this.enterICCPUK2(options);
         break;
       case GECKO_CARDLOCK_NCK:
-      case GECKO_CARDLOCK_CCK: // Fall through.
-      case GECKO_CARDLOCK_SPCK: {
+      case GECKO_CARDLOCK_NCK1:
+      case GECKO_CARDLOCK_NCK2:
+      case GECKO_CARDLOCK_HNCK:
+      case GECKO_CARDLOCK_CCK:
+      case GECKO_CARDLOCK_SPCK:
+      case GECKO_CARDLOCK_RCCK: // Fall through.
+      case GECKO_CARDLOCK_RSPCK: {
         let type = GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[options.lockType];
         this.enterDepersonalization(type, options.pin, options);
         break;
       }
       case GECKO_CARDLOCK_NCK_PUK:
-      case GECKO_CARDLOCK_CCK_PUK: // Fall through.
-      case GECKO_CARDLOCK_SPCK_PUK: {
+      case GECKO_CARDLOCK_NCK1_PUK:
+      case GECKO_CARDLOCK_NCK2_PUK:
+      case GECKO_CARDLOCK_HNCK_PUK:
+      case GECKO_CARDLOCK_CCK_PUK:
+      case GECKO_CARDLOCK_SPCK_PUK:
+      case GECKO_CARDLOCK_RCCK_PUK: // Fall through.
+      case GECKO_CARDLOCK_RSPCK_PUK: {
         let type = GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[options.lockType];
         this.enterDepersonalization(type, options.puk, options);
         break;
       }
       default:
         options.errorMsg = "Unsupported Card Lock.";
         options.success = false;
         this.sendChromeMessage(options);
--- a/dom/system/gonk/tests/test_ril_worker_icc.js
+++ b/dom/system/gonk/tests/test_ril_worker_icc.js
@@ -2104,46 +2104,75 @@ add_test(function test_error_message_upd
 
 add_test(function test_personalization_state() {
   let worker = newUint8Worker();
   let context = worker.ContextPool._contexts[0];
   let ril = context.RIL;
 
   context.ICCRecordHelper.readICCID = function fakeReadICCID() {};
 
-  function testPersonalization(cardPersoState, geckoCardState) {
+  function testPersonalization(isCdma, cardPersoState, geckoCardState) {
     let iccStatus = {
       cardState: CARD_STATE_PRESENT,
-      gsmUmtsSubscriptionAppIndex: 0,
+      gsmUmtsSubscriptionAppIndex: (!isCdma) ? 0 : -1,
+      cdmaSubscriptionAppIndex: (isCdma) ? 0 : -1,
       apps: [
         {
           app_state: CARD_APPSTATE_SUBSCRIPTION_PERSO,
           perso_substate: cardPersoState
         }],
     };
 
+    ril._isCdma = isCdma;
     ril._processICCStatus(iccStatus);
     do_check_eq(ril.cardState, geckoCardState);
   }
 
-  testPersonalization(CARD_PERSOSUBSTATE_SIM_NETWORK,
+  // Test GSM personalization state.
+  testPersonalization(false, CARD_PERSOSUBSTATE_SIM_NETWORK,
                       GECKO_CARDSTATE_NETWORK_LOCKED);
-  testPersonalization(CARD_PERSOSUBSTATE_SIM_CORPORATE,
+  testPersonalization(false, CARD_PERSOSUBSTATE_SIM_CORPORATE,
                       GECKO_CARDSTATE_CORPORATE_LOCKED);
-  testPersonalization(CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER,
+  testPersonalization(false, CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER,
                       GECKO_CARDSTATE_SERVICE_PROVIDER_LOCKED);
-  testPersonalization(CARD_PERSOSUBSTATE_SIM_NETWORK_PUK,
+  testPersonalization(false, CARD_PERSOSUBSTATE_SIM_NETWORK_PUK,
                       GECKO_CARDSTATE_NETWORK_PUK_REQUIRED);
-  testPersonalization(CARD_PERSOSUBSTATE_SIM_CORPORATE_PUK,
+  testPersonalization(false, CARD_PERSOSUBSTATE_SIM_CORPORATE_PUK,
                       GECKO_CARDSTATE_CORPORATE_PUK_REQUIRED);
-  testPersonalization(CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK,
+  testPersonalization(false, CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK,
                       GECKO_CARDSTATE_SERVICE_PROVIDER_PUK_REQUIRED);
-  testPersonalization(CARD_PERSOSUBSTATE_READY,
+  testPersonalization(false, CARD_PERSOSUBSTATE_READY,
                       GECKO_CARDSTATE_PERSONALIZATION_READY);
 
+  // Test CDMA personalization state.
+  testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_NETWORK1,
+                      GECKO_CARDSTATE_NETWORK1_LOCKED);
+  testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_NETWORK2,
+                      GECKO_CARDSTATE_NETWORK2_LOCKED);
+  testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_HRPD,
+                      GECKO_CARDSTATE_HRPD_NETWORK_LOCKED);
+  testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_CORPORATE,
+                      GECKO_CARDSTATE_RUIM_CORPORATE_LOCKED);
+  testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER,
+                      GECKO_CARDSTATE_RUIM_SERVICE_PROVIDER_LOCKED);
+  testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_RUIM,
+                      GECKO_CARDSTATE_RUIM_LOCKED);
+  testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_NETWORK1_PUK,
+                      GECKO_CARDSTATE_NETWORK1_PUK_REQUIRED);
+  testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_NETWORK2_PUK,
+                      GECKO_CARDSTATE_NETWORK2_PUK_REQUIRED);
+  testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_HRPD_PUK,
+                      GECKO_CARDSTATE_HRPD_NETWORK_PUK_REQUIRED);
+  testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_CORPORATE_PUK,
+                      GECKO_CARDSTATE_RUIM_CORPORATE_PUK_REQUIRED);
+  testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK,
+                      GECKO_CARDSTATE_RUIM_SERVICE_PROVIDER_PUK_REQUIRED);
+  testPersonalization(true, CARD_PERSOSUBSTATE_RUIM_RUIM_PUK,
+                      GECKO_CARDSTATE_RUIM_PUK_REQUIRED);
+
   run_next_test();
 });
 
 /**
  * Verify SIM app_state in _processICCStatus
  */
 add_test(function test_card_app_state() {
   let worker = newUint8Worker();
@@ -2291,21 +2320,31 @@ add_test(function test_unlock_card_lock_
   let context = worker.ContextPool._contexts[0];
   let ril = context.RIL;
   let buf = context.Buf;
   const pin = "12345678";
   const puk = "12345678";
 
   let GECKO_CARDLOCK_TO_PASSWORD_TYPE = {};
   GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_NCK] = "pin";
+  GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_NCK1] = "pin";
+  GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_NCK2] = "pin";
+  GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_HNCK] = "pin";
   GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_CCK] = "pin";
   GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_SPCK] = "pin";
+  GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_RCCK] = "pin";
+  GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_RSPCK] = "pin";
   GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_NCK_PUK] = "puk";
+  GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_NCK1_PUK] = "puk";
+  GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_NCK2_PUK] = "puk";
+  GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_HNCK_PUK] = "puk";
   GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_CCK_PUK] = "puk";
   GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_SPCK_PUK] = "puk";
+  GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_RCCK_PUK] = "puk";
+  GECKO_CARDLOCK_TO_PASSWORD_TYPE[GECKO_CARDLOCK_RSPCK_PUK] = "puk";
 
   function do_test(aLock, aPassword) {
     buf.sendParcel = function fakeSendParcel () {
       // Request Type.
       do_check_eq(this.readInt32(), REQUEST_ENTER_NETWORK_DEPERSONALIZATION_CODE);
 
       // Token : we don't care
       this.readInt32();
@@ -2319,21 +2358,31 @@ add_test(function test_unlock_card_lock_
     };
 
     let lock = {lockType: aLock};
     lock[GECKO_CARDLOCK_TO_PASSWORD_TYPE[aLock]] = aPassword;
     ril.iccUnlockCardLock(lock);
   }
 
   do_test(GECKO_CARDLOCK_NCK, pin);
+  do_test(GECKO_CARDLOCK_NCK1, pin);
+  do_test(GECKO_CARDLOCK_NCK2, pin);
+  do_test(GECKO_CARDLOCK_HNCK, pin);
   do_test(GECKO_CARDLOCK_CCK, pin);
   do_test(GECKO_CARDLOCK_SPCK, pin);
+  do_test(GECKO_CARDLOCK_RCCK, pin);
+  do_test(GECKO_CARDLOCK_RSPCK, pin);
   do_test(GECKO_CARDLOCK_NCK_PUK, puk);
+  do_test(GECKO_CARDLOCK_NCK1_PUK, puk);
+  do_test(GECKO_CARDLOCK_NCK2_PUK, puk);
+  do_test(GECKO_CARDLOCK_HNCK_PUK, puk);
   do_test(GECKO_CARDLOCK_CCK_PUK, puk);
   do_test(GECKO_CARDLOCK_SPCK_PUK, puk);
+  do_test(GECKO_CARDLOCK_RCCK_PUK, puk);
+  do_test(GECKO_CARDLOCK_RSPCK_PUK, puk);
 
   run_next_test();
 });
 
 /**
  * Verify MCC and MNC parsing
  */
 add_test(function test_mcc_mnc_parsing() {
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -1,16 +1,20 @@
 [DEFAULT]
+support-files =
+  inert_style.css
 
 [test_bug900724.html]
 [test_content_element.html]
 [test_nested_content_element.html]
 [test_dyanmic_content_element_matching.html]
 [test_document_register.html]
+[test_document_register_base_queue.html]
 [test_document_register_lifecycle.html]
+[test_document_register_parser.html]
+[test_document_register_stack.html]
 [test_template.html]
 [test_shadow_root.html]
 [test_shadow_root_inert_element.html]
-[inert_style.css]
 [test_shadow_root_style.html]
 [test_shadow_root_style_multiple_shadow.html]
 [test_shadow_root_style_order.html]
 [test_style_fallback_content.html]
--- a/dom/tests/mochitest/webcomponents/test_document_register.html
+++ b/dom/tests/mochitest/webcomponents/test_document_register.html
@@ -1,90 +1,171 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=783129
 -->
 <head>
-  <title>Test for document.register using custom prototype</title>
+  <title>Test for document.registerElement using custom prototype</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
+<div>
+<x-unresolved id="unresolved"></x-unresolved>
+</div>
 
 <script>
-var gElementUpgraded = false;
-var gElementReplaced = false;
 
-function elementUpgrade() {
-  gElementUpgraded = true;
-
-  // Check for prototype on upgraded element.
-  var documentElement = document.getElementById("grabme");
-  ok(documentElement.hello, "Upgraded element should inherit 'hello' method from prototype.");
-  documentElement.hello();
-
-  var customChild = document.getElementById("kid");
-  ok(customChild, "Upgrade should preserve children.");
-  ok(customChild.parentNode == documentElement, "Parent should be updated to new custom element.");
-
-  // Try creating new element and checking for prototype.
-  var constructedElement = document.createElement("x-hello");
-  ok(constructedElement.hello, "Created element should inherit 'hello' method from prototype.");
-  constructedElement.hello();
+function testRegisterExtend(tag, extend, proto, expectException) {
+  try {
+    document.registerElement(tag, { prototype: proto, extends: extend });
+    ok(!expectException, "Registered " + tag + " extending " + extend + " containing " + proto + " in proto chain.");
+  } catch (ex) {
+    ok(expectException, "Did not register " + tag + " extending " + extend + " containing " + proto + " in proto chain.");
+  }
 }
 
-function elementReplace(e) {
-  gElementReplaced = true;
-
-  ok(e.upgrade != e.target, "Upgraded element should be different from the target.");
-  ok(e.upgrade.firstElementChild.id == "kid", "Upgrade element should have a child.");
-  ok(e.target.firstElementChild.id == "kid", "Replacement element should have a child.");
+function testRegisterSimple(tag, proto, expectException) {
+  try {
+    document.registerElement(tag, { prototype: proto });
+    ok(!expectException, "Registered " + tag + " containing " + proto + " in proto chain.");
+  } catch (ex) {
+    ok(expectException, "Did not register " + tag + " containing " + proto + " in proto chain.");
+  }
 }
 
 function startTest() {
-  // Create a prototype that inheits from HTMLElement.
-  var customProto = {};
-  customProto.__proto__ = HTMLElement.prototype;
-  customProto.hello = function() {
-    ok(true, "Custom element should use provided prototype.");
-  };
+  // Test registering some simple prototypes.
+  testRegisterSimple("x-html-obj-elem", Object.create(HTMLElement.prototype), false);
+  testRegisterSimple("x-html-obj-p", Object.create(HTMLParagraphElement.prototype), false);
+
+  // If prototype is an interface prototype object for any interface object,
+  // registration will throw.
+  testRegisterSimple("x-html-elem", HTMLElement.prototype, true);
+  testRegisterSimple("x-html-select", HTMLSelectElement.prototype, true);
+  testRegisterSimple("some-elem", HTMLElement.prototype, true);
+  testRegisterSimple("x-html-p", HTMLParagraphElement.prototype, true);
+  testRegisterSimple("x-html-span", HTMLSpanElement.prototype, true);
+  testRegisterSimple("x-svg-proto", SVGElement.prototype, true);
+
+  // Make sure the prototype on unresolved elements is HTMLElement not HTMLUnknownElement.
+  var unresolved = document.getElementById("unresolved");
+  is(unresolved.__proto__, HTMLElement.prototype, "Unresolved custom elements should have HTMLElement as prototype.");
+
+  var anotherUnresolved = document.createElement("maybe-custom-element");
+  is(anotherUnresolved.__proto__, HTMLElement.prototype, "Unresolved custom elements should have HTMLElement as prototype.");
+
+  // Registering without a prototype should automatically create one inheriting from HTMLElement.
+  testRegisterSimple("x-elem-no-proto", null, false);
+  var simpleElem = document.createElement("x-elem-no-proto");
+  is(simpleElem.__proto__.__proto__, HTMLElement.prototype, "Default prototype should inherit from HTMLElement");
 
-  var oldElem = document.getElementById("grabme");
-  oldElem.addEventListener("elementreplace", elementReplace);
+  var simpleProto = Object.create(HTMLElement.prototype);
+  testRegisterSimple("x-elem-simple-proto", simpleProto, false);
+  var simpleProtoElem = document.createElement("x-elem-simple-proto");
+  is(simpleProtoElem.__proto__, simpleProto, "Custom element should use registered prototype.");
+  var anotherSimpleElem = document.createElementNS("http://www.w3.org/1999/xhtml", "x-elem-simple-proto");
+  is(anotherSimpleElem.__proto__, simpleProto, "Custom element should use registered prototype.");
+
+  // Test registering some invalid prototypes.
+  testRegisterSimple("x-invalid-number", 42, true);
+  testRegisterSimple("x-invalid-boolean", false, true);
+  testRegisterSimple("x-invalid-float", 1.0, true);
+  // Can not register with a prototype that inherits from SVGElement
+  // without extending an existing element type.
+  testRegisterSimple("x-html-obj-svg", Object.create(SVGElement.prototype), true);
+  // A prototype with a non-configurable "constructor" property must throw.
+  var nonConfigProto = Object.create(HTMLElement.prototype,
+    { constructor: { configurable: false, value: function() {} } });
+  testRegisterSimple("x-non-config-proto", nonConfigProto, true);
 
-  document.addEventListener("elementupgrade", elementUpgrade);
-  var elementConstructor = document.register("x-hello", { prototype: customProto });
+  // Test invalid custom element names.
+  testRegisterSimple("invalid", Object.create(HTMLElement.prototype), true);
+  testRegisterSimple("annotation-xml", Object.create(HTMLElement.prototype), true);
+  testRegisterSimple("color-profile", Object.create(HTMLElement.prototype), true);
+  testRegisterSimple("font-face", Object.create(HTMLElement.prototype), true);
+  testRegisterSimple("font-face-src", Object.create(HTMLElement.prototype), true);
+  testRegisterSimple("font-face-uri", Object.create(HTMLElement.prototype), true);
+  testRegisterSimple("font-face-format", Object.create(HTMLElement.prototype), true);
+  testRegisterSimple("font-face-name", Object.create(HTMLElement.prototype), true);
+  testRegisterSimple("missing-glyph", Object.create(HTMLElement.prototype), true);
 
-  // Try creating new element and checking for prototype.
-  var constructedElement = new elementConstructor();
-  ok(constructedElement.hello, "Created element should inherit 'hello' method from prototype.");
-  constructedElement.hello();
+  // Test registering elements that extend from an existing element.
+  testRegisterExtend("x-extend-span", "span", Object.create(HTMLElement.prototype), false);
+  testRegisterExtend("x-extend-span-caps", "SPAN", Object.create(HTMLElement.prototype), false);
+
+  // Test registering elements that extend from a non-existing element.
+  testRegisterExtend("x-extend-span-nonexist", "nonexisting", Object.create(HTMLElement.prototype), true);
+  testRegisterExtend("x-extend-svg-nonexist", "nonexisting", Object.create(SVGElement.prototype), true);
 
-  ok(!oldElem.hello, "Element obtained prior to registration should not have inherited prototype.");
+  // Test registration with duplicate type.
+  testRegisterSimple("x-dupe-me", Object.create(HTMLElement.prototype), false);
+  testRegisterSimple("x-dupe-me", Object.create(HTMLElement.prototype), true);
+  testRegisterSimple("X-DUPE-ME", Object.create(HTMLElement.prototype), true);
+  testRegisterSimple("x-dupe-me", null, true);
+  testRegisterExtend("x-dupe-me", "span", Object.create(HTMLElement.prototype), true);
+  testRegisterExtend("x-dupe-me", "shape", Object.create(SVGElement.prototype), true);
 
-  ok(gElementUpgraded && gElementReplaced, "Upgrade and replace events should have been fired.");
+  testRegisterExtend("x-svg-dupe-me", "circle", Object.create(SVGElement.prototype), false);
+  testRegisterSimple("x-svg-dupe-me", Object.create(HTMLElement.prototype), true);
+  testRegisterSimple("X-SVG-DUPE-ME", Object.create(HTMLElement.prototype), true);
+  testRegisterSimple("x-svg-dupe-me", null, true);
+  testRegisterExtend("x-svg-dupe-me", "span", Object.create(HTMLElement.prototype), true);
+  testRegisterExtend("x-svg-dupe-me", "shape", Object.create(SVGElement.prototype), true);
 
-  // Check that custom elements registered without a prototype inherit from HTMLElement.
-  document.register("x-bye");
-  var byeElement = document.createElement("x-bye");
-  ok(byeElement.__proto__.__proto__, HTMLElement.prototype, "Element registered without prototype should inherit from HTMLElement.");
+  // document.createElement with extended type.
+  var extendedProto = Object.create(HTMLButtonElement.prototype);
+  var buttonConstructor = document.registerElement("x-extended-button", { prototype: extendedProto, extends: "button" });
+  var extendedButton = document.createElement("button", "x-extended-button");
+  is(extendedButton.tagName, "BUTTON", "Created element should have local name of BUTTON");
+  is(extendedButton.__proto__, extendedProto, "Created element should have the prototype of the extended type.");
+  is(extendedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
+
+  // document.createElementNS with different namespace than definition.
+  var svgButton = document.createElementNS("http://www.w3.org/2000/svg", "button", "x-extended-button");
+  isnot(svgButton.__proto__, extendedProto, "Definition for element is in html namespace, registration should not apply for SVG elements.");
+
+  // document.createElement with non-existant extended type.
+  var normalButton = document.createElement("button", "x-non-existant");
+  is(normalButton.__proto__, HTMLButtonElement.prototype, "When the extended type doesn't exist, prototype should not change.");
 
-  // Check that element registration with a prototype that does not inherit from HTMLElement results in exception.
-  try {
-    document.register("x-foo", { "prototype": {} });
-    ok(false, "Prototype that does not inherit from HTMLElement should throw exception");
-  } catch (ex) {
-    ok(true, "Prototype that does not inherit from HTMLElement should throw exception");
-  }
+  // document.createElement with exteneded type that does not match with local name of element.
+  var normalDiv = document.createElement("div", "x-extended-button");
+  is(normalDiv.__proto__, HTMLDivElement.prototype, "Prototype should not change when local name of extended type defintion does not match.");
+
+  // Custom element constructor.
+  var constructedButton = new buttonConstructor();
+  is(constructedButton.tagName, "BUTTON", "Created element should have local name of BUTTON");
+  is(constructedButton.__proto__, extendedProto, "Created element should have the prototype of the extended type.");
+  is(constructedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
 
-  SimpleTest.finish();
+  // document.createElementNS with extended type.
+  var svgExtendedProto = Object.create(SVGTextElement.prototype);
+  var svgConstructor = document.registerElement("x-extended-text", { prototype: svgExtendedProto, extends: "text"});
+  var extendedText = document.createElementNS("http://www.w3.org/2000/svg", "text", "x-extended-text");
+  is(extendedText.tagName, "text", "Created element should have a local name of |text|.");
+  is(extendedText.__proto__, svgExtendedProto, "Created element have the registered prototype.");
+  is(extendedText.getAttribute("is"), "x-extended-text", "The |is| attribute of the created element should be the extended type.");
+
+  // document.createElement with different namespace than definition for extended element.
+  var htmlText = document.createElement("text", "x-extended-text");
+  isnot(htmlText.__proto__, svgExtendedProto, "Definition for element in SVG namespace should not apply to HTML elements.");
+
+  // Custom element constructor for a SVG element.
+  var constructedText = new svgConstructor();
+  is(constructedText.tagName, "text", "Created element should have a local name of |text|.");
+  is(constructedText.__proto__, svgExtendedProto, "Created element have the registered prototype.");
+  is(constructedText.getAttribute("is"), "x-extended-text", "The |is| attribute of the created element should be the extended type.");
+
+  // Try creating an element with a custom element name, but not in the html namespace.
+  var htmlNamespaceProto = Object.create(HTMLElement.prototype);
+  document.registerElement("x-in-html-namespace", { prototype: htmlNamespaceProto });
+  var wrongNamespaceElem = document.createElementNS("http://www.w3.org/2000/svg", "x-in-html-namespace");
+  isnot(wrongNamespaceElem.__proto__, htmlNamespaceProto, "Definition for element in html namespace should not apply to SVG elements.");
 }
 
-SimpleTest.waitForExplicitFinish();
-</script>
+startTest();
 
-</head>
-<body onload="startTest()">
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
-<x-hello id="grabme">
-<div id="kid"></div>
-</x-hello>
+</script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_document_register_base_queue.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=783129
+-->
+<head>
+  <title>Test for document.registerElement lifecycle callback</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<script>
+var p = Object.create(HTMLElement.prototype);
+
+var createdCallbackCallCount = 0;
+
+// By the time the base element queue is processed via the microtask,
+// both x-hello elements should be in the document.
+p.createdCallback = function() {
+  var one = document.getElementById("one");
+  var two = document.getElementById("two");
+  isnot(one, null, "First x-hello element should be in the tree.");
+  isnot(two, null, "Second x-hello element should be in the tree.");
+  createdCallbackCallCount++;
+  if (createdCallbackCallCount == 2) {
+    SimpleTest.finish();
+  }
+
+  if (createdCallbackCallCount > 2) {
+    ok(false, "Created callback called too much.");
+  }
+};
+
+p.attributeChangedCallback = function(name, oldValue, newValue) {
+  ok(false, "Attribute changed callback should not be called because callbacks should not be queued until created callback invoked.");
+};
+
+document.registerElement("x-hello", { prototype: p });
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
+<x-hello id="one"></x-hello>
+<x-hello id="two"></x-hello>
+<script>
+</script>
+</body>
+</html>
--- a/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html
+++ b/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html
@@ -1,49 +1,402 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=783129
 -->
 <head>
-  <title>Test for document.register lifecycle callback</title>
+  <title>Test for document.registerElement lifecycle callback</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
+<div id="container">
+  <x-hello id="hello"></x-hello>
+  <button id="extbutton" is="x-button"></button>
+</div>
 <script>
 
-var gLifecycleCallbackCalled = false;
+var container = document.getElementById("container");
+
+// Tests callbacks after registering element type that is already in the document.
+// create element in document -> register -> remove from document
+function testRegisterUnresolved() {
+  var helloElem = document.getElementById("hello");
+
+  var createdCallbackCalled = false;
+  var enteredViewCallbackCalled = false;
+  var leftViewCallbackCalled = false;
+
+  var p = Object.create(HTMLElement.prototype);
+  p.createdCallback = function() {
+    is(helloElem.__proto__, p, "Prototype should be adjusted just prior to invoking the created callback.");
+    is(createdCallbackCalled, false, "Created callback should only be called once in this tests.");
+    is(this, helloElem, "The 'this' value should be the custom element.");
+    createdCallbackCalled = true;
+  };
+
+  p.enteredViewCallback = function() {
+    is(createdCallbackCalled, true, "Created callback should be called before enteredView");
+    is(enteredViewCallbackCalled, false, "enteredView callback should only be called once in this test.");
+    is(this, helloElem, "The 'this' value should be the custom element.");
+    enteredViewCallbackCalled = true;
+  };
+
+  p.leftViewCallback = function() {
+    is(enteredViewCallbackCalled, true, "enteredView callback should be called before leftView");
+    is(leftViewCallbackCalled, false, "leftView callback should only be called once in this test.");
+    leftViewCallbackCalled = true;
+    is(this, helloElem, "The 'this' value should be the custom element.");
+    runNextTest();
+  };
+
+  p.attributeChangedCallback = function(name, oldValue, newValue) {
+    ok(false, "attributeChanged callback should never be called in this test.");
+  };
+
+  document.registerElement("x-hello", { prototype: p });
+  is(createdCallbackCalled, true, "created callback should be called when control returns to script from user agent code");
+
+  // Remove element from document to trigger leftView callback.
+  container.removeChild(helloElem);
+}
 
-var lifecycleCallbacks = {
-  created: function() {
-    is(this.getAttribute("id"), "grabme", "|this| value should be the upgrade element");
-    gLifecycleCallbackCalled = true;
-  }
-};
+// Tests callbacks after registering an extended element type that is already in the document.
+// create element in document -> register -> remove from document
+function testRegisterUnresolvedExtended() {
+  var buttonElem = document.getElementById("extbutton");
+
+  var createdCallbackCalled = false;
+  var enteredViewCallbackCalled = false;
+  var leftViewCallbackCalled = false;
+
+  var p = Object.create(HTMLButtonElement.prototype);
+  p.createdCallback = function() {
+    is(buttonElem.__proto__, p, "Prototype should be adjusted just prior to invoking the created callback.");
+    is(createdCallbackCalled, false, "Created callback should only be called once in this tests.");
+    is(this, buttonElem, "The 'this' value should be the custom element.");
+    createdCallbackCalled = true;
+  };
+
+  p.enteredViewCallback = function() {
+    is(createdCallbackCalled, true, "Created callback should be called before enteredView");
+    is(enteredViewCallbackCalled, false, "enteredView callback should only be called once in this test.");
+    is(this, buttonElem, "The 'this' value should be the custom element.");
+    enteredViewCallbackCalled = true;
+  };
+
+  p.leftViewCallback = function() {
+    is(enteredViewCallbackCalled, true, "enteredView callback should be called before leftView");
+    is(leftViewCallbackCalled, false, "leftView callback should only be called once in this test.");
+    leftViewCallbackCalled = true;
+    is(this, buttonElem, "The 'this' value should be the custom element.");
+    runNextTest();
+  };
+
+  p.attributeChangedCallback = function(name, oldValue, newValue) {
+    ok(false, "attributeChanged callback should never be called in this test.");
+  };
+
+  document.registerElement("x-button", { prototype: p, extends: "button" });
+  is(createdCallbackCalled, true, "created callback should be called when control returns to script from user agent code");
+
+  // Remove element from document to trigger leftView callback.
+  container.removeChild(buttonElem);
+}
+
+function testInnerHTML() {
+  var createdCallbackCalled = false;
 
-function startTest() {
-  var HtmlProto = function() {};
-  HtmlProto.prototype = HTMLElement.prototype;
+  var p = Object.create(HTMLElement.prototype);
+  p.createdCallback = function() {
+    is(createdCallbackCalled, false, "created callback should only be called once in this test.");
+    createdCallbackCalled = true;
+  };
+
+  document.registerElement("x-inner-html", { prototype: p });
+  var div = document.createElement(div);
+  div.innerHTML = '<x-inner-html></x-inner-html>';
+  is(createdCallbackCalled, true, "created callback should be called after setting innerHTML.");
+  runNextTest();
+}
+
+function testInnerHTMLExtended() {
+  var createdCallbackCalled = false;
+
+  var p = Object.create(HTMLButtonElement.prototype);
+  p.createdCallback = function() {
+    is(createdCallbackCalled, false, "created callback should only be called once in this test.");
+    createdCallbackCalled = true;
+  };
+
+  document.registerElement("x-inner-html-extended", { prototype: p, extends: "button" });
+  var div = document.createElement(div);
+  div.innerHTML = '<button is="x-inner-html-extended"></button>';
+  is(createdCallbackCalled, true, "created callback should be called after setting innerHTML.");
+  runNextTest();
+}
+
+function testInnerHTMLUpgrade() {
+  var createdCallbackCalled = false;
+
+  var div = document.createElement(div);
+  div.innerHTML = '<x-inner-html-upgrade></x-inner-html-upgrade>';
+
+  var p = Object.create(HTMLElement.prototype);
+  p.createdCallback = function() {
+    is(createdCallbackCalled, false, "created callback should only be called once in this test.");
+    createdCallbackCalled = true;
+  };
+
+  document.registerElement("x-inner-html-upgrade", { prototype: p });
+  is(createdCallbackCalled, true, "created callback should be called after registering.");
+  runNextTest();
+}
 
-  // Create a prototype that inheits from HTMLElement.
-  var customProto = new HtmlProto();
-  customProto.hello = function() {
-    ok(true, "Custom element should use provided prototype.");
+function testInnerHTMLExtendedUpgrade() {
+  var createdCallbackCalled = false;
+
+  var div = document.createElement(div);
+  div.innerHTML = '<button is="x-inner-html-extended-upgrade"></button>';
+
+  var p = Object.create(HTMLButtonElement.prototype);
+  p.createdCallback = function() {
+    is(createdCallbackCalled, false, "created callback should only be called once in this test.");
+    createdCallbackCalled = true;
+  };
+
+  document.registerElement("x-inner-html-extended-upgrade", { prototype: p, extends: "button" });
+  is(createdCallbackCalled, true, "created callback should be called after registering.");
+  runNextTest();
+}
+
+// Test callback when creating element after registering an element type.
+// register -> create element -> insert into document -> remove from document
+function testRegisterResolved() {
+  var createdCallbackCalled = false;
+  var enteredViewCallbackCalled = false;
+  var leftViewCallbackCalled = false;
+
+  var createdCallbackThis;
+
+  var p = Object.create(HTMLElement.prototype);
+  p.createdCallback = function() {
+    is(createdCallbackCalled, false, "Created callback should only be called once in this test.");
+    createdCallbackThis = this;
+    createdCallbackCalled = true;
+  };
+
+  p.enteredViewCallback = function() {
+    is(createdCallbackCalled, true, "created callback should be called before enteredView callback.");
+    is(enteredViewCallbackCalled, false, "enteredView callback should only be called on in this test.");
+    is(this, createdElement, "The 'this' value should be the custom element.");
+    enteredViewCallbackCalled = true;
+  };
+
+  p.leftViewCallback = function() {
+    is(enteredViewCallbackCalled, true, "enteredView callback should be called before leftView");
+    is(leftViewCallbackCalled, false, "leftView callback should only be called once in this test.");
+    is(this, createdElement, "The 'this' value should be the custom element.");
+    leftViewCallbackCalled = true;
+    runNextTest();
   };
 
-  var elementConstructor = document.register("x-hello", { prototype: customProto, lifecycle: lifecycleCallbacks });
+  p.attributeChangedCallback = function() {
+    ok(false, "attributeChanged callback should never be called in this test.");
+  };
+
+  document.registerElement("x-resolved", { prototype: p });
+  is(createdCallbackCalled, false, "Created callback should not be called when custom element instance has not been created.");
+
+  var createdElement = document.createElement("x-resolved");
+  is(createdCallbackThis, createdElement, "The 'this' value in the created callback should be the custom element.");
+  is(createdElement.__proto__, p, "Prototype of custom element should be the registered prototype.");
+
+  // Insert element into document to trigger enteredView callback.
+  container.appendChild(createdElement);
+
+  // Remove element from document to trigger leftView callback.
+  container.removeChild(createdElement);
+}
+
+// Callbacks should always be the same ones when registered.
+function testChangingCallback() {
+  var p = Object.create(HTMLElement.prototype);
+  var callbackCalled = false;
+  p.attributeChangedCallback = function(name, oldValue, newValue) {
+    is(callbackCalled, false, "Callback should only be called once in this test.");
+    callbackCalled = true;
+    runNextTest();
+  };
+
+  document.registerElement("x-test-callback", { prototype: p });
+
+  p.attributeChangedCallback = function(name, oldValue, newValue) {
+    ok(false, "Only callbacks at registration should be called.");
+  };
+
+  var elem = document.createElement("x-test-callback");
+  elem.setAttribute("foo", "bar");
+}
+
+function testAttributeChanged() {
+  var createdCallbackCalled = false;
+
+  var createdElement;
+  var createdCallbackThis;
+
+  var p = Object.create(HTMLElement.prototype);
+  p.createdCallback = function() {
+    is(createdCallbackCalled, false, "Created callback should only be called once in this test.");
+    createdCallbackThis = this;
+    createdCallbackCalled = true;
+  };
+
+  // Sequence of callback arguments that we expect from attribute changed callback
+  // after changing attributes values in a specific order.
+  var expectedCallbackArguments = [
+    // [oldValue, newValue]
+    [null, "newvalue"], // Setting the attribute value to "newvalue"
+    ["newvalue", "nextvalue"], // Changing the attribute value from "newvalue" to "nextvalue"
+    ["nextvalue", ""], // Changing the attribute value from "nextvalue" to empty string
+    ["", null], // Removing the attribute.
+  ];
+
+  p.attributeChangedCallback = function(name, oldValue, newValue) {
+    is(createdCallbackCalled, true, "created callback should be called before attribute changed.");
+    is(this, createdElement, "The 'this' value should be the custom element.");
+    ok(expectedCallbackArguments.length > 0, "Attribute changed callback should not be called more than expected.");
+
+    is(name, "changeme", "name arugment in attribute changed callback should be the name of the changed attribute.");
+
+    var expectedArgs = expectedCallbackArguments.shift();
+    is(oldValue, expectedArgs[0], "The old value argument should match the expected value.");
+    is(newValue, expectedArgs[1], "The new value argument should match the expected value.");
+
+    if (expectedCallbackArguments.length === 0) {
+      // Done with the attribute changed callback test.
+      runNextTest();
+    }
+  };
+
+  document.registerElement("x-attrchange", { prototype: p });
+
+  var createdElement = document.createElement("x-attrchange");
+  is(createdCallbackThis, createdElement, "The 'this' value in the created callback should be the custom element.");
+  createdElement.setAttribute("changeme", "newvalue");
+  createdElement.setAttribute("changeme", "nextvalue");
+  createdElement.setAttribute("changeme", "");
+  createdElement.removeAttribute("changeme");
+}
 
-  ok(gLifecycleCallbackCalled, "Lifecycle callback should be called.");
+function testAttributeChangedExtended() {
+  var p = Object.create(HTMLButtonElement.prototype);
+  var callbackCalled = false;
+  p.attributeChangedCallback = function(name, oldValue, newValue) {
+    is(callbackCalled, false, "Callback should only be called once in this test.");
+    callbackCalled = true;
+    runNextTest();
+  };
+
+  document.registerElement("x-extended-attribute-change", { prototype: p, extends: "button" });
+
+  var elem = document.createElement("button", "x-extended-attribute-change");
+  elem.setAttribute("foo", "bar");
+}
+
+// Creates a custom element that is an upgrade candidate (no registration)
+// and mutate the element in ways that would call callbacks for registered
+// elements.
+function testUpgradeCandidate() {
+  var createdElement = document.createElement("x-upgrade-candidate");
+  container.appendChild(createdElement);
+  createdElement.setAttribute("foo", "bar");
+  container.removeChild(createdElement);
+  ok(true, "Nothing bad should happen when trying to mutating upgrade candidates.");
+  runNextTest();
+}
+
+function testNotInDocEnterLeave() {
+  var p = Object.create(HTMLElement.prototype);
+
+  p.enteredView = function() {
+    ok(false, "enteredView should not be called when not entering the document.");
+  };
+
+  p.leftView = function() {
+    ok(false, "leaveView should not be called when not leaving the document.");
+  };
+
+  var createdElement = document.createElement("x-destined-for-fragment");
+
+  document.registerElement("x-destined-for-fragment", { prototype: p });
+
+  var fragment = new DocumentFragment();
+  fragment.appendChild(createdElement);
+  fragment.removeChild(createdElement);
+
+  var divNotInDoc = document.createElement("div");
+  divNotInDoc.appendChild(createdElement);
+  divNotInDoc.removeChild(createdElement);
 
-  SimpleTest.finish();
+  runNextTest();
+}
+
+function testEnterLeaveView() {
+  var enteredViewCallbackCalled = false;
+  var leftViewCallbackCalled = false;
+
+  var p = Object.create(HTMLElement.prototype);
+  p.enteredViewCallback = function() {
+    is(enteredViewCallbackCalled, false, "enteredView callback should only be called on in this test.");
+    enteredViewCallbackCalled = true;
+  };
+
+  p.leftViewCallback = function() {
+    is(enteredViewCallbackCalled, true, "enteredView callback should be called before leftView");
+    is(leftViewCallbackCalled, false, "leftView callback should only be called once in this test.");
+    leftViewCallbackCalled = true;
+    runNextTest();
+  };
+
+  var div = document.createElement("div");
+  document.registerElement("x-element-in-div", { prototype: p });
+  var customElement = document.createElement("x-element-in-div");
+  div.appendChild(customElement);
+  is(enteredViewCallbackCalled, false, "Appending a custom element to a node that is not in the document should not call the enteredView callback.");
+
+  container.appendChild(div);
+  container.removeChild(div);
+}
+
+var testFunctions = [
+  testRegisterUnresolved,
+  testRegisterUnresolvedExtended,
+  testInnerHTML,
+  testInnerHTMLExtended,
+  testInnerHTMLUpgrade,
+  testInnerHTMLExtendedUpgrade,
+  testRegisterResolved,
+  testAttributeChanged,
+  testAttributeChangedExtended,
+  testUpgradeCandidate,
+  testChangingCallback,
+  testNotInDocEnterLeave,
+  testEnterLeaveView,
+  SimpleTest.finish
+];
+
+function runNextTest() {
+  if (testFunctions.length > 0) {
+    var nextTestFunction = testFunctions.shift();
+    nextTestFunction();
+  }
 }
 
 SimpleTest.waitForExplicitFinish();
-</script>
 
-</head>
-<body onload="startTest()">
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
-<x-hello id="grabme">
-<div id="kid"></div>
-</x-hello>
+runNextTest();
+
+</script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_document_register_parser.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=783129
+-->
+<head>
+  <title>Test for document.registerElement for elements created by the parser</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<script>
+
+var extendedButtonProto = Object.create(HTMLButtonElement.prototype);
+var buttonCallbackCalled = false;
+extendedButtonProto.createdCallback = function() {
+  is(buttonCallbackCalled, false, "created callback for x-button should only be called once.");
+  is(this.tagName, "BUTTON", "Only the <button> element should be upgraded.");
+  buttonCallbackCalled = true;
+};
+
+document.registerElement("x-button", { prototype: extendedButtonProto, extends: "button" });
+
+var divProto = Object.create(HTMLDivElement.prototype);
+var divCallbackCalled = false;
+divProto.createdCallback = function() {
+  is(divCallbackCalled, false, "created callback for x-div should only be called once.");
+  is(buttonCallbackCalled, true, "crated callback should be called for x-button before x-div.");
+  is(this.tagName, "X-DIV", "Only the <x-div> element should be upgraded.");
+  divCallbackCalled = true;
+  SimpleTest.finish();
+};
+
+document.registerElement("x-div", { prototype: divProto });
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
+<button is="x-button"></button><!-- should be upgraded -->
+<x-button></x-button><!-- should not be upgraded -->
+<span is="x-button"></span><!-- should not be upgraded -->
+<div is="x-div"></div><!-- should not be upgraded -->
+<x-div></x-div><!-- should be upgraded -->
+<script>
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_document_register_stack.html
@@ -0,0 +1,152 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=783129
+-->
+<head>
+  <title>Test for document.registerElement lifecycle callback</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
+<div id="container">
+</div>
+<script>
+
+var container = document.getElementById("container");
+
+// Test changing attributes in the created callback on an element
+// created after registration.
+function testChangeAttributeInCreatedCallback() {
+  var createdCallbackCalled = false;
+  var attributeChangedCallbackCalled = false;
+
+  var p = Object.create(HTMLElement.prototype);
+  p.createdCallback = function() {
+    is(createdCallbackCalled, false, "Created callback should be called before enteredView callback.");
+    createdCallbackCalled = true;
+    is(attributeChangedCallbackCalled, false, "Attribute changed callback should not have been called prior to setting the attribute.");
+    this.setAttribute("foo", "bar");
+    is(attributeChangedCallbackCalled, false, "While element is being created, element should not be added to the current element callback queue.");
+  };
+
+  p.attributeChangedCallback = function(name, oldValue, newValue) {
+    is(createdCallbackCalled, true, "attributeChanged callback should be called after the created callback because it was enqueued during created callback.");
+    is(attributeChangedCallbackCalled, false, "attributeChanged callback should only be called once in this tests.");
+    is(newValue, "bar", "The new value should be 'bar'");
+    attributeChangedCallbackCalled = true;
+    runNextTest();
+  };
+
+  document.registerElement("x-one", { prototype: p });
+  document.createElement("x-one");
+}
+
+function testChangeAttributeInEnteredViewCallback() {
+  var p = Object.create(HTMLElement.prototype);
+  var attributeChangedCallbackCalled = false;
+  var enteredViewCallbackCalled = false;
+
+  p.enteredViewCallback = function() {
+    is(enteredViewCallbackCalled, false, "enteredView callback should be called only once in this test.");
+    enteredViewCallbackCalled = true;
+    is(attributeChangedCallbackCalled, false, "Attribute changed callback should not be called before changing attribute.");
+    this.setAttribute("foo", "bar");
+    is(attributeChangedCallbackCalled, true, "Transition from user-agent implementation to script should result in attribute changed callback being called.");
+    runNextTest();
+  };
+
+  p.attributeChangedCallback = function() {
+    is(enteredViewCallbackCalled, true, "enteredView callback should have been called prior to attribute changed callback.");
+    is(attributeChangedCallbackCalled, false, "attributeChanged callback should only be called once in this tests.");
+    attributeChangedCallbackCalled = true;
+  };
+
+  document.registerElement("x-two", { prototype: p });
+  var elem = document.createElement("x-two");
+
+  var container = document.getElementById("container");
+  container.appendChild(elem);
+}
+
+function testLeaveViewInEnteredViewCallback() {
+  var p = Object.create(HTMLElement.prototype);
+  var enteredViewCallbackCalled = false;
+  var leftViewCallbackCalled = false;
+  var container = document.getElementById("container");
+
+  p.enteredViewCallback = function() {
+    is(this.parentNode, container, "Parent node should the container in which the node was appended.");
+    is(enteredViewCallbackCalled, false, "enteredView callback should be called only once in this test.");
+    enteredViewCallbackCalled = true;
+    is(leftViewCallbackCalled, false, "leftView callback should not be called prior to removing element from document.");
+    container.removeChild(this);
+    is(leftViewCallbackCalled, true, "Transition from user-agent implementation to script should run left view callback.");
+    runNextTest();
+  };
+
+  p.leftViewCallback = function() {
+    is(leftViewCallbackCalled, false, "The left view callback should only be called once in this test.");
+    is(enteredViewCallbackCalled, true, "The entered view callback should be called prior to left view callback.");
+    leftViewCallbackCalled = true;
+  };
+
+  document.registerElement("x-three", { prototype: p });
+  var elem = document.createElement("x-three");
+
+  container.appendChild(elem);
+}
+
+function testStackedAttributeChangedCallback() {
+  var p = Object.create(HTMLElement.prototype);
+  var attributeChangedCallbackCount = 0;
+
+  var attributeSequence = ["foo", "bar", "baz"];
+
+  p.attributeChangedCallback = function(attrName, oldValue, newValue) {
+    if (newValue == "baz") {
+      return;
+    }
+
+    var nextAttribute = attributeSequence.shift();
+    ok(true, nextAttribute);
+    // Setting this attribute will call this function again, when
+    // control returns to the script, the last attribute in the sequence should
+    // be set on the element.
+    this.setAttribute("foo", nextAttribute);
+    is(this.getAttribute("foo"), "baz", "The last value in the sequence should be the value of the attribute.");
+
+    attributeChangedCallbackCount++;
+    if (attributeChangedCallbackCount == 3) {
+      runNextTest();
+    }
+  };
+
+  document.registerElement("x-four", { prototype: p });
+  var elem = document.createElement("x-four");
+  elem.setAttribute("foo", "changeme");
+}
+
+var testFunctions = [
+  testChangeAttributeInCreatedCallback,
+  testChangeAttributeInEnteredViewCallback,
+  testLeaveViewInEnteredViewCallback,
+  testStackedAttributeChangedCallback,
+  SimpleTest.finish
+];
+
+function runNextTest() {
+  if (testFunctions.length > 0) {
+    var nextTestFunction = testFunctions.shift();
+    nextTestFunction();
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+runNextTest();
+
+</script>
+</body>
+</html>
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -235,17 +235,25 @@ partial interface Document {
 partial interface Document {
     readonly attribute Element? mozPointerLockElement;
     void mozExitPointerLock ();
 };
 
 //http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
 partial interface Document {
     [Throws, Pref="dom.webcomponents.enabled"]
-    object register(DOMString name, optional ElementRegistrationOptions options);
+    object registerElement(DOMString name, optional ElementRegistrationOptions options);
+};
+
+//http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
+partial interface Document {
+    [NewObject, Throws]
+    Element createElement(DOMString localName, DOMString typeExtension);
+    [NewObject, Throws]
+    Element createElementNS(DOMString? namespace, DOMString qualifiedName, DOMString typeExtension);
 };
 
 // http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html#sec-document-interface
 partial interface Document {
   readonly attribute boolean hidden;
   readonly attribute boolean mozHidden;
   readonly attribute VisibilityState visibilityState;
   readonly attribute VisibilityState mozVisibilityState;
--- a/dom/webidl/DummyBinding.webidl
+++ b/dom/webidl/DummyBinding.webidl
@@ -3,12 +3,13 @@
  * 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/.
  */
 
 // Dummy bindings that we need to force generation of things that
 // aren't actually referenced anywhere in IDL yet but are used in C++.
 
 interface DummyInterface {
+  void lifecycleCallbacks(optional LifecycleCallbacks arg);
 };
 
 interface DummyInterfaceWorkers {
 };
--- a/dom/webidl/MozIcc.webidl
+++ b/dom/webidl/MozIcc.webidl
@@ -24,21 +24,25 @@ interface MozIcc : EventTarget
    */
   attribute EventHandler oniccinfochange;
 
   // Integrated Circuit Card State.
 
   /**
    * Indicates the state of the device's ICC.
    *
-   * Possible values: 'illegal', 'unknown', 'pinRequired',
-   * 'pukRequired', 'personalizationInProgress', 'networkLocked',
-   * 'corporateLocked', 'serviceProviderLocked', 'networkPukRequired',
-   * 'corporatePukRequired', 'serviceProviderPukRequired',
-   * 'personalizationReady', 'ready', 'permanentBlocked'.
+   * Possible values: 'illegal', 'unknown', 'pinRequired', 'pukRequired',
+   * 'personalizationInProgress', 'networkLocked', 'network1Locked',
+   * 'network2Locked', 'hrpdNetworkLocked', 'corporateLocked',
+   * 'serviceProviderLocked', 'ruimCorporateLocked', 'ruimServiceProviderLocked',
+   * 'networkPukRequired', 'network1PukRequired', 'network2PukRequired',
+   * 'hrpdNetworkPukRequired', 'corporatePukRequired',
+   * 'serviceProviderPukRequired', 'ruimCorporatePukRequired',
+   * 'ruimServiceProviderPukRequired', 'personalizationReady', 'ready',
+   * 'permanentBlocked'.
    *
    * Once the ICC becomes undetectable, cardstatechange event will be notified.
    * Also, the attribute is set to null and this MozIcc object becomes invalid.
    * Calling asynchronous functions raises exception then.
    */
   readonly attribute DOMString? cardState;
 
   /**
@@ -154,44 +158,104 @@ interface MozIcc : EventTarget
    *                   puk: "...",
    *                   newPin: "..."});
    *
    * (3) Network depersonalization. Unlocking the network control key (NCK).
    *
    *   unlockCardLock({lockType: "nck",
    *                   pin: "..."});
    *
-   * (4) Corporate depersonalization. Unlocking the corporate control key (CCK).
+   * (4) Network type 1 depersonalization. Unlocking the network type 1 control
+   *     key (NCK1).
+   *
+   *   unlockCardLock({lockType: "nck1",
+   *                   pin: "..."});
+   *
+   * (5) Network type 2 depersonalization. Unlocking the network type 2 control
+   *     key (NCK2).
+   *
+   *   unlockCardLock({lockType: "nck2",
+   *                   pin: "..."});
+   *
+   * (6) HRPD network depersonalization. Unlocking the HRPD network control key
+   *     (HNCK).
+   *
+   *   unlockCardLock({lockType: "hnck",
+   *                   pin: "..."});
+   *
+   * (7) Corporate depersonalization. Unlocking the corporate control key (CCK).
    *
    *   unlockCardLock({lockType: "cck",
    *                   pin: "..."});
    *
-   * (5) Service Provider depersonalization. Unlocking the service provider
+   * (8) Service provider depersonalization. Unlocking the service provider
    *     control key (SPCK).
    *
    *   unlockCardLock({lockType: "spck",
    *                   pin: "..."});
    *
-   * (6) Network PUK depersonalization. Unlocking the network control key (NCK).
+   * (9) RUIM corporate depersonalization. Unlocking the RUIM corporate control
+   *     key (RCCK).
+   *
+   *   unlockCardLock({lockType: "rcck",
+   *                   pin: "..."});
+   *
+   * (10) RUIM service provider depersonalization. Unlocking the RUIM service
+   *      provider control key (RSPCK).
+   *
+   *   unlockCardLock({lockType: "rspck",
+   *                   pin: "..."});
+   *
+   * (11) Network PUK depersonalization. Unlocking the network control key (NCK).
    *
    *   unlockCardLock({lockType: "nckPuk",
    *                   puk: "..."});
    *
-   * (7) Corporate PUK depersonalization. Unlocking the corporate control key
-   *     (CCK).
+   * (12) Network type 1 PUK depersonalization. Unlocking the network type 1
+   *      control key (NCK1).
+   *
+   *   unlockCardLock({lockType: "nck1Puk",
+   *                   pin: "..."});
+   *
+   * (13) Network type 2 PUK depersonalization. Unlocking the Network type 2
+   *      control key (NCK2).
+   *
+   *   unlockCardLock({lockType: "nck2Puk",
+   *                   pin: "..."});
+   *
+   * (14) HRPD network PUK depersonalization. Unlocking the HRPD network control
+   *      key (HNCK).
+   *
+   *   unlockCardLock({lockType: "hnckPuk",
+   *                   pin: "..."});
+   *
+   * (15) Corporate PUK depersonalization. Unlocking the corporate control key
+   *      (CCK).
    *
    *   unlockCardLock({lockType: "cckPuk",
    *                   puk: "..."});
    *
-   * (8) Service Provider PUK depersonalization. Unlocking the service provider
-   *     control key (SPCK).
+   * (16) Service provider PUK depersonalization. Unlocking the service provider
+   *      control key (SPCK).
    *
    *   unlockCardLock({lockType: "spckPuk",
    *                   puk: "..."});
    *
+   * (17) RUIM corporate PUK depersonalization. Unlocking the RUIM corporate
+   *      control key (RCCK).
+   *
+   *   unlockCardLock({lockType: "rcckPuk",
+   *                   puk: "..."});
+   *
+   * (18) RUIM service provider PUK depersonalization. Unlocking the service
+   *      provider control key (SPCK).
+   *
+   *   unlockCardLock({lockType: "rspckPuk",
+   *                   puk: "..."});
+   *
    * @return a DOMRequest.
    *         The request's result will be an object containing
    *         information about the unlock operation.
    *
    * Examples:
    *
    * (1) Unlocking failed:
    *
--- a/dom/webidl/WebComponents.webidl
+++ b/dom/webidl/WebComponents.webidl
@@ -6,18 +6,23 @@
  * The origin of this IDL file is
  * http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 callback LifecycleCreatedCallback = void();
+callback LifecycleEnteredViewCallback = void();
+callback LifecycleLeftViewCallback = void();
+callback LifecycleAttributeChangedCallback = void(DOMString attrName, DOMString? oldValue, DOMString? newValue);
 
 dictionary LifecycleCallbacks {
-  LifecycleCreatedCallback? created = null;
+  LifecycleCreatedCallback? createdCallback;
+  LifecycleEnteredViewCallback? enteredViewCallback;
+  LifecycleLeftViewCallback? leftViewCallback;
+  LifecycleAttributeChangedCallback? attributeChangedCallback;
 };
 
 dictionary ElementRegistrationOptions {
   object? prototype = null;
-  LifecycleCallbacks lifecycle;
+  DOMString? extends = null;
 };
-
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -708,26 +708,32 @@ TEST(AsyncPanZoomController, MediumPress
   mcc->CheckHasDelayedTask();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSIntPoint(10, 10), 0, apzc->GetGuid())).Times(1);
   mcc->RunDelayedTask();
 
   apzc->Destroy();
 }
 
-TEST(AsyncPanZoomController, LongPress) {
+void
+DoLongPressTest(bool aShouldUseTouchAction, uint32_t aBehavior) {
   nsRefPtr<MockContentControllerDelayed> mcc = new MockContentControllerDelayed();
   nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(
     0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR);
 
   apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
   apzc->UpdateZoomConstraints(ZoomConstraints(false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
 
+  nsTArray<uint32_t> values;
+  values.AppendElement(aBehavior);
+  apzc->SetTouchActionEnabled(aShouldUseTouchAction);
+  apzc->SetAllowedTouchBehavior(values);
+
   int time = 0;
 
   nsEventStatus status = ApzcDown(apzc, 10, 10, time);
   EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
 
   MockFunction<void(std::string checkPointName)> check;
 
   {
@@ -834,16 +840,27 @@ TEST(AsyncPanZoomController, LongPressPr
   apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
 
   EXPECT_EQ(pointOut, ScreenPoint());
   EXPECT_EQ(viewTransformOut, ViewTransform());
 
   apzc->Destroy();
 }
 
+TEST(AsyncPanZoomController, LongPress) {
+  DoLongPressTest(false, mozilla::layers::AllowedTouchBehavior::NONE);
+}
+
+TEST(AsyncPanZoomController, LongPressPanAndZoom) {
+  DoLongPressTest(true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
+                      | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
+                      | mozilla::layers::AllowedTouchBehavior::ZOOM);
+}
+
+
 // Layer tree for HitTesting1
 static already_AddRefed<mozilla::layers::Layer>
 CreateTestLayerTree1(nsRefPtr<LayerManager>& aLayerManager, nsTArray<nsRefPtr<Layer> >& aLayers) {
   const char* layerTreeSyntax = "c(ttcc)";
   // LayerID                     0 1234
   nsIntRegion layerVisibleRegion[] = {
     nsIntRegion(nsIntRect(0,0,100,100)),
     nsIntRegion(nsIntRect(0,0,100,100)),
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -430,34 +430,50 @@ nsHtml5TreeOperation::Perform(nsHtml5Tre
 
       int32_t len = attributes->getLength();
       for (int32_t i = len; i > 0;) {
         --i;
         // prefix doesn't need regetting. it is always null or a static atom
         // local name is never null
         nsCOMPtr<nsIAtom> localName =
           Reget(attributes->getLocalNameNoBoundsCheck(i));
+        nsCOMPtr<nsIAtom> prefix = attributes->getPrefixNoBoundsCheck(i);
+        int32_t nsuri = attributes->getURINoBoundsCheck(i);
+
         if (ns == kNameSpaceID_XHTML &&
             nsHtml5Atoms::a == name &&
             nsHtml5Atoms::name == localName) {
           // This is an HTML5-incompliant Geckoism.
           // Remove when fixing bug 582361
           NS_ConvertUTF16toUTF8 cname(*(attributes->getValueNoBoundsCheck(i)));
           NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
-          newContent->SetAttr(attributes->getURINoBoundsCheck(i),
+          newContent->SetAttr(nsuri,
                               localName,
-                              attributes->getPrefixNoBoundsCheck(i),
+                              prefix,
                               uv,
                               false);
         } else {
-          newContent->SetAttr(attributes->getURINoBoundsCheck(i),
+          nsString& value = *(attributes->getValueNoBoundsCheck(i));
+
+          newContent->SetAttr(nsuri,
                               localName,
-                              attributes->getPrefixNoBoundsCheck(i),
-                              *(attributes->getValueNoBoundsCheck(i)),
+                              prefix,
+                              value,
                               false);
+
+          // Custom element prototype swizzling may be needed if there is an
+          // "is" attribute.
+          if (kNameSpaceID_None == nsuri && !prefix && nsGkAtoms::is == localName) {
+            ErrorResult errorResult;
+            newContent->OwnerDoc()->SwizzleCustomElement(newContent,
+                                                         value,
+                                                         newContent->GetNameSpaceID(),
+                                                         errorResult);
+
+          }
         }
       }
 
       return rv;
     }
     case eTreeOpSetFormElement: {
       nsIContent* node = *(mOne.node);
       nsIContent* parent = *(mTwo.node);
--- a/toolkit/components/viewsource/content/viewSource.js
+++ b/toolkit/components/viewsource/content/viewSource.js
@@ -652,92 +652,21 @@ function BrowserCharsetReload()
   if (isHistoryEnabled()) {
     gPageLoader.loadPage(gPageLoader.currentDescriptor,
                          gPageLoader.DISPLAY_NORMAL);
   } else {
     gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
   }
 }
 
-function BrowserSetForcedCharacterSet(aCharset)
-{
-  gBrowser.docShell.charset = aCharset;
-  BrowserCharsetReload();
-}
-
-function MultiplexHandler(event)
-{
-  var node = event.target;
-  var name = node.getAttribute("name");
-
-  if (name == "detectorGroup") {
-    SelectDetector(event);
-    BrowserCharsetReload();
-  } else if (name == "charsetGroup") {
-    var charset = node.getAttribute("id");
-    charset = charset.substring(charset.indexOf("charset.") + "charset.".length);
-    BrowserSetForcedCharacterSet(charset);
-  }
-}
-
-function SelectDetector(event)
+function BrowserSetCharacterSet(aEvent)
 {
-  var uri =  event.target.getAttribute("id");
-  var prefvalue = uri.substring(uri.indexOf("chardet.") + "chardet.".length);
-  if ("off" == prefvalue) { // "off" is special value to turn off the detectors
-    prefvalue = "";
-  }
-
-  try {
-    var str = Cc["@mozilla.org/supports-string;1"].
-              createInstance(Ci.nsISupportsString);
-    str.data = prefvalue;
-    gPrefService.setComplexValue("intl.charset.detector", Ci.nsISupportsString, str);
-  }
-  catch (ex) {
-    dump("Failed to set the intl.charset.detector preference.\n");
-  }
-}
-
-function FoldCharset(charset) {
-  // For substantially similar encodings, treat two encodings as the same
-  // for the purpose of the check mark.
-  if (charset == "ISO-8859-8-I") {
-    return "windows-1255";
-  } else if (charset == "gb18030") {
-    return "gbk";
-  }
-  return charset;
-}
-
-function UpdateCurrentCharset() {
-  var menuitem = document.getElementById("charset." + FoldCharset(content.document.characterSet));
-  if (menuitem)
-    menuitem.setAttribute("checked", "true");
-}
-
-function UpdateCharsetDetector() {
-  var prefvalue;
-
-  try {
-    prefvalue = gPrefService.getComplexValue("intl.charset.detector", Ci.nsIPrefLocalizedString).data;
-  }
-  catch (ex) {}
-
-  if (!prefvalue)
-    prefvalue = "off";
-
-  var menuitem = document.getElementById("chardet." + prefvalue);
-  if (menuitem)
-    menuitem.setAttribute("checked", "true");
-}
-
-function UpdateMenus() {
-  UpdateCurrentCharset();
-  UpdateCharsetDetector();
+  if (aEvent.target.hasAttribute("charset"))
+    gBrowser.docShell.charset = aEvent.target.getAttribute("charset");
+  BrowserCharsetReload();
 }
 
 function BrowserForward(aEvent) {
   try {
     gBrowser.goForward();
   }
   catch(ex) {
   }
--- a/toolkit/components/viewsource/content/viewSource.xul
+++ b/toolkit/components/viewsource/content/viewSource.xul
@@ -196,21 +196,20 @@
                         key="key_textZoomReset"/>
             </menupopup>
           </menu>
 
           <!-- Charset Menu -->
           <menu id="charsetMenu"
                 label="&charsetMenu.label;"
                 accesskey="&charsetMenu.accesskey;"
-                oncommand="MultiplexHandler(event);"
+                oncommand="BrowserSetCharacterSet(event);"
                 onpopupshowing="CharsetMenu.build(event.target);"
-                onpopupshown="UpdateMenus();">
-            <menupopup>
-            </menupopup>
+                onpopupshown="CharsetMenu.update(event, content.document.characterSet);">
+            <menupopup/>
           </menu>
           <menuseparator/>
           <menuitem id="menu_wrapLongLines" type="checkbox" command="cmd_wrapLongLines"
                     label="&menu_wrapLongLines.title;" accesskey="&menu_wrapLongLines.accesskey;"/>
           <menuitem type="checkbox" id="menu_highlightSyntax" command="cmd_highlightSyntax"
                     label="&menu_highlightSyntax.label;" accesskey="&menu_highlightSyntax.accesskey;"/>
         </menupopup>
       </menu>
--- a/toolkit/modules/CharsetMenu.jsm
+++ b/toolkit/modules/CharsetMenu.jsm
@@ -9,17 +9,17 @@ const { classes: Cc, interfaces: Ci, uti
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyGetter(this, "gBundle", function() {
   const kUrl = "chrome://global/locale/charsetMenu.properties";
   return Services.strings.createBundle(kUrl);
 });
 
 const kAutoDetectors = [
-  ["off", "off"],
+  ["off", ""],
   ["ja", "ja_parallel_state_machine"],
   ["ru", "ruprob"],
   ["uk", "ukprob"]
 ];
 
 /**
  * This set contains encodings that are in the Encoding Standard, except:
  *  - XSS-dangerous encodings (except ISO-2022-JP which is assumed to be
@@ -84,56 +84,81 @@ const kEncodings = new Set([
 // Always at the start of the menu, in this order, followed by a separator.
 const kPinned = [
   "UTF-8",
   "windows-1252"
 ];
 
 kPinned.forEach(x => kEncodings.delete(x));
 
+function CharsetComparator(a, b) {
+  // Normal sorting sorts the part in parenthesis in an order that
+  // happens to make the less frequently-used items first.
+  let titleA = a.label.replace(/\(.*/, "") + b.value;
+  let titleB = b.label.replace(/\(.*/, "") + a.value;
+  // Secondarily reverse sort by encoding name to sort "windows" or
+  // "shift_jis" first.
+  return titleA.localeCompare(titleB) || b.value.localeCompare(a.value);
+}
+
+function SetDetector(event) {
+  let str = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+  str.data = event.target.getAttribute("detector");
+  Services.prefs.setComplexValue("intl.charset.detector", Ci.nsISupportsString, str);
+}
+
+function UpdateDetectorMenu(event) {
+  event.stopPropagation();
+  let detector = Services.prefs.getComplexValue("intl.charset.detector", Ci.nsIPrefLocalizedString);
+  let menuitem = this.getElementsByAttribute("detector", detector).item(0);
+  if (menuitem) {
+    menuitem.setAttribute("checked", "true");
+  }
+}
 
 let gDetectorInfoCache, gCharsetInfoCache, gPinnedInfoCache;
 
 let CharsetMenu = {
-  build: function(parent, idPrefix="", showAccessKeys=true) {
+  build: function(parent, showAccessKeys=true, showDetector=true) {
     function createDOMNode(doc, nodeInfo) {
       let node = doc.createElement("menuitem");
       node.setAttribute("type", "radio");
-      node.setAttribute("name", nodeInfo.name);
+      node.setAttribute("name", nodeInfo.name + "Group");
+      node.setAttribute(nodeInfo.name, nodeInfo.value);
       node.setAttribute("label", nodeInfo.label);
       if (showAccessKeys && nodeInfo.accesskey) {
         node.setAttribute("accesskey", nodeInfo.accesskey);
       }
-      if (idPrefix) {
-        node.id = idPrefix + nodeInfo.id;
-      } else {
-        node.id = nodeInfo.id;
-      }
       return node;
     }
 
-    if (parent.childElementCount > 0) {
+    if (parent.hasChildNodes()) {
       // Detector menu or charset menu already built
       return;
     }
+    this._ensureDataReady();
     let doc = parent.ownerDocument;
 
-    let menuNode = doc.createElement("menu");
-    menuNode.setAttribute("label", gBundle.GetStringFromName("charsetMenuAutodet"));
-    if (showAccessKeys) {
-      menuNode.setAttribute("accesskey", gBundle.GetStringFromName("charsetMenuAutodet.key"));
-    }
-    parent.appendChild(menuNode);
+    if (showDetector) {
+      let menuNode = doc.createElement("menu");
+      menuNode.setAttribute("label", gBundle.GetStringFromName("charsetMenuAutodet"));
+      if (showAccessKeys) {
+        menuNode.setAttribute("accesskey", gBundle.GetStringFromName("charsetMenuAutodet.key"));
+      }
+      parent.appendChild(menuNode);
 
-    let menuPopupNode = doc.createElement("menupopup");
-    menuNode.appendChild(menuPopupNode);
+      let menuPopupNode = doc.createElement("menupopup");
+      menuNode.appendChild(menuPopupNode);
+      menuPopupNode.addEventListener("command", SetDetector);
+      menuPopupNode.addEventListener("popupshown", UpdateDetectorMenu);
 
-    this._ensureDataReady();
-    gDetectorInfoCache.forEach(detectorInfo => menuPopupNode.appendChild(createDOMNode(doc, detectorInfo)));
-    parent.appendChild(doc.createElement("menuseparator"));
+      gDetectorInfoCache.forEach(detectorInfo => menuPopupNode.appendChild(createDOMNode(doc, detectorInfo)));
+      parent.appendChild(doc.createElement("menuseparator"));
+    }
+
     gPinnedInfoCache.forEach(charsetInfo => parent.appendChild(createDOMNode(doc, charsetInfo)));
     parent.appendChild(doc.createElement("menuseparator"));
     gCharsetInfoCache.forEach(charsetInfo => parent.appendChild(createDOMNode(doc, charsetInfo)));
   },
 
   getData: function() {
     this._ensureDataReady();
     return {
@@ -142,68 +167,40 @@ let CharsetMenu = {
       otherCharsets: gCharsetInfoCache
     };
   },
 
   _ensureDataReady: function() {
     if (!gDetectorInfoCache) {
       gDetectorInfoCache = this.getDetectorInfo();
       gPinnedInfoCache = this.getCharsetInfo(kPinned, false);
-      gCharsetInfoCache = this.getCharsetInfo([...kEncodings]);
+      gCharsetInfoCache = this.getCharsetInfo(kEncodings);
     }
   },
 
   getDetectorInfo: function() {
     return kAutoDetectors.map(([detectorName, nodeId]) => ({
-      id: "chardet." + nodeId,
       label: this._getDetectorLabel(detectorName),
       accesskey: this._getDetectorAccesskey(detectorName),
-      name: "detectorGroup",
+      name: "detector",
+      value: nodeId
     }));
   },
 
   getCharsetInfo: function(charsets, sort=true) {
-    let list = charsets.map(charset => ({
-      id: "charset." + charset,
+    let list = [{
       label: this._getCharsetLabel(charset),
       accesskey: this._getCharsetAccessKey(charset),
-      name: "charsetGroup",
-    }));
-
-    if (!sort) {
-      return list;
-    }
+      name: "charset",
+      value: charset
+    } for (charset of charsets)];
 
-    list.sort(function (a, b) {
-      let titleA = a.label;
-      let titleB = b.label;
-      // Normal sorting sorts the part in parenthesis in an order that
-      // happens to make the less frequently-used items first.
-      let index;
-      if ((index = titleA.indexOf("(")) > -1) {
-        titleA = titleA.substring(0, index);
-      }
-      if ((index = titleB.indexOf("(")) > -1) {
-        titleA = titleB.substring(0, index);
-      }
-      let comp = titleA.localeCompare(titleB);
-      if (comp) {
-        return comp;
-      }
-      // secondarily reverse sort by encoding name to sort "windows" or
-      // "shift_jis" first. This works regardless of localization, because
-      // the ids aren't localized.
-      if (a.id < b.id) {
-        return 1;
-      }
-      if (b.id < a.id) {
-        return -1;
-      }
-      return 0;
-    });
+    if (sort) {
+      list.sort(CharsetComparator);
+    }
     return list;
   },
 
   _getDetectorLabel: function(detector) {
     try {
       return gBundle.GetStringFromName("charsetMenuAutodet." + detector);
     } catch (ex) {}
     return detector;
@@ -230,12 +227,36 @@ let CharsetMenu = {
       // Localization key has been revised
       charset = "gbk.bis";
     }
     try {
       return gBundle.GetStringFromName(charset + ".key");
     } catch (ex) {}
     return "";
   },
+
+  /**
+   * For substantially similar encodings, treat two encodings as the same
+   * for the purpose of the check mark.
+   */
+  foldCharset: function(charset) {
+    switch (charset) {
+      case "ISO-8859-8-I":
+        return "windows-1255";
+
+      case "gb18030":
+        return "gbk";
+
+      default:
+        return charset;
+    }
+  },
+
+  update: function(event, charset) {
+    let menuitem = event.target.getElementsByAttribute("charset", this.foldCharset(charset)).item(0);
+    if (menuitem) {
+      menuitem.setAttribute("checked", "true");
+    }
+  },
 };
 
 Object.freeze(CharsetMenu);
 
--- a/toolkit/xre/glxtest.cpp
+++ b/toolkit/xre/glxtest.cpp
@@ -204,17 +204,17 @@ static void glxtest()
   void* glXBindTexImageEXT = glXGetProcAddress("glXBindTexImageEXT"); 
 
   ///// Get GL vendor/renderer/versions strings /////
   enum { bufsize = 1024 };
   char buf[bufsize];
   const GLubyte *vendorString = glGetString(GL_VENDOR);
   const GLubyte *rendererString = glGetString(GL_RENDERER);
   const GLubyte *versionString = glGetString(GL_VERSION);
-  
+
   if (!vendorString || !rendererString || !versionString)
     fatal_error("glGetString returned null");
 
   int length = snprintf(buf, bufsize,
                         "VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n",
                         vendorString,
                         rendererString,
                         versionString,
@@ -224,17 +224,26 @@ static void glxtest()
 
   ///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it doesn't need to check GL info)
   ///// so we might be staying alive for longer than expected, so it's important to consume as little memory as
   ///// possible. Also we want to check that we're able to do that too without generating X errors.
   glXMakeCurrent(dpy, None, nullptr); // must release the GL context before destroying it
   glXDestroyContext(dpy, context);
   XDestroyWindow(dpy, window);
   XFreeColormap(dpy, swa.colormap);
+
+#ifdef NS_FREE_PERMANENT_DATA // conditionally defined in nscore.h, don't forget to #include it above
   XCloseDisplay(dpy);
+#else
+  // This XSync call wanted to be instead:
+  //   XCloseDisplay(dpy);
+  // but this can cause 1-minute stalls on certain setups using Nouveau, see bug 973192
+  XSync(dpy, False);
+#endif
+
   dlclose(libgl);
 
   ///// Finally write data to the pipe
   write(write_end_of_the_pipe, buf, length);
 }
 
 /** \returns true in the child glxtest process, false in the parent process */
 bool fire_glxtest_process()
--- a/widget/xpwidgets/moz.build
+++ b/widget/xpwidgets/moz.build
@@ -21,16 +21,17 @@ UNIFIED_SOURCES += [
     'GfxInfoWebGL.cpp',
     'InputData.cpp',
     'nsBaseAppShell.cpp',
     'nsBaseDragService.cpp',
     'nsBaseScreen.cpp',
     'nsClipboardHelper.cpp',
     'nsClipboardPrivacyHandler.cpp',
     'nsClipboardProxy.cpp',
+    'nsColorPickerProxy.cpp',
     'nsContentProcessWidgetFactory.cpp',
     'nsFilePickerProxy.cpp',
     'nsHTMLFormatConverter.cpp',
     'nsIdleService.cpp',
     'nsIWidgetListener.cpp',
     'nsPrimitiveHelpers.cpp',
     'nsPrintOptionsImpl.cpp',
     'nsPrintSession.cpp',
new file mode 100644
--- /dev/null
+++ b/widget/xpwidgets/nsColorPickerProxy.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "nsColorPickerProxy.h"
+
+#include "mozilla/dom/TabChild.h"
+
+using namespace mozilla::dom;
+
+NS_IMPL_ISUPPORTS1(nsColorPickerProxy, nsIColorPicker)
+
+/* void init (in nsIDOMWindow parent, in AString title, in short mode); */
+NS_IMETHODIMP
+nsColorPickerProxy::Init(nsIDOMWindow* aParent, const nsAString& aTitle,
+                         const nsAString& aInitialColor)
+{
+  TabChild* tabChild = TabChild::GetFrom(aParent);
+  if (!tabChild) {
+    return NS_ERROR_FAILURE;
+  }
+
+  tabChild->SendPColorPickerConstructor(this,
+                                        nsString(aTitle),
+                                        nsString(aInitialColor));
+  NS_ADDREF_THIS();
+  return NS_OK;
+}
+
+/* void open (in nsIColorPickerShownCallback aColorPickerShownCallback); */
+NS_IMETHODIMP
+nsColorPickerProxy::Open(nsIColorPickerShownCallback* aColorPickerShownCallback)
+{
+  NS_ENSURE_STATE(!mCallback);
+  mCallback = aColorPickerShownCallback;
+
+  SendOpen();
+  return NS_OK;
+}
+
+bool
+nsColorPickerProxy::RecvUpdate(const nsString& aColor)
+{
+  if (mCallback) {
+    mCallback->Update(aColor);
+  }
+  return true;
+}
+
+bool
+nsColorPickerProxy::Recv__delete__(const nsString& aColor)
+{
+  if (mCallback) {
+    mCallback->Done(aColor);
+    mCallback = nullptr;
+  }
+  return true;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/widget/xpwidgets/nsColorPickerProxy.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsColorPickerProxy_h
+#define nsColorPickerProxy_h
+
+#include "nsIColorPicker.h"
+
+#include "mozilla/dom/PColorPickerChild.h"
+
+class nsColorPickerProxy MOZ_FINAL : public nsIColorPicker,
+                                     public mozilla::dom::PColorPickerChild
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICOLORPICKER
+
+  nsColorPickerProxy() {}
+  ~nsColorPickerProxy() {}
+
+  virtual bool RecvUpdate(const nsString& aColor) MOZ_OVERRIDE;
+  virtual bool Recv__delete__(const nsString& aColor) MOZ_OVERRIDE;
+
+private:
+  nsCOMPtr<nsIColorPickerShownCallback> mCallback;
+  nsString mTitle;
+  nsString mInitialColor;
+};
+
+#endif // nsColorPickerProxy_h
--- a/widget/xpwidgets/nsContentProcessWidgetFactory.cpp
+++ b/widget/xpwidgets/nsContentProcessWidgetFactory.cpp
@@ -3,38 +3,44 @@
  */
 /* 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/ModuleUtils.h"
 #include "nsWidgetsCID.h"
 #include "nsClipboardProxy.h"
+#include "nsColorPickerProxy.h"
 #include "nsFilePickerProxy.h"
 
 using namespace mozilla;
 
 #ifndef MOZ_B2G
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardProxy)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsColorPickerProxy)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFilePickerProxy)
 
 NS_DEFINE_NAMED_CID(NS_CLIPBOARD_CID);
+NS_DEFINE_NAMED_CID(NS_COLORPICKER_CID);
 NS_DEFINE_NAMED_CID(NS_FILEPICKER_CID);
 
 static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
     { &kNS_CLIPBOARD_CID, false, nullptr, nsClipboardProxyConstructor,
       Module::CONTENT_PROCESS_ONLY },
+    { &kNS_COLORPICKER_CID, false, nullptr, nsColorPickerProxyConstructor,
+      Module::CONTENT_PROCESS_ONLY },
     { &kNS_FILEPICKER_CID, false, nullptr, nsFilePickerProxyConstructor,
       Module::CONTENT_PROCESS_ONLY },
     { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
     { "@mozilla.org/widget/clipboard;1", &kNS_CLIPBOARD_CID, Module::CONTENT_PROCESS_ONLY },
+    { "@mozilla.org/colorpicker;1", &kNS_COLORPICKER_CID, Module::CONTENT_PROCESS_ONLY },
     { "@mozilla.org/filepicker;1", &kNS_FILEPICKER_CID, Module::CONTENT_PROCESS_ONLY },
     { nullptr }
 };
 
 static const mozilla::Module kWidgetModule = {
     mozilla::Module::kVersion,
     kWidgetCIDs,
     kWidgetContracts
--- a/xpfe/components/autocomplete/resources/content/autocomplete.xml
+++ b/xpfe/components/autocomplete/resources/content/autocomplete.xml
@@ -5,24 +5,24 @@
 
 
 <bindings id="autocompleteBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:html="http://www.w3.org/1999/xhtml"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
 
-  <binding id="autocomplete" role="xulcombobox"
+  <binding id="autocomplete" role="xul:combobox"
            extends="chrome://global/content/bindings/textbox.xml#textbox">
     <resources>
       <stylesheet src="chrome://global/content/autocomplete.css"/>
       <stylesheet src="chrome://global/skin/autocomplete.css"/>
     </resources>
 
-    <content sizetopopup="pref">
+    <content>
       <children includes="menupopup"/>
 
       <xul:hbox class="autocomplete-textbox-container" flex="1" align="center">
         <children includes="image|deck|stack|box">
           <xul:image class="autocomplete-icon" allowevents="true"/>
         </children>
 
         <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,tooltiptext=inputtooltiptext">
@@ -1623,16 +1623,19 @@
   <binding id="history-dropmarker" extends="chrome://global/content/bindings/general.xml#dropmarker">
 
     <implementation>
       <method name="showPopup">
         <body><![CDATA[
           var textbox = document.getBindingParent(this);
           var kids = textbox.getElementsByClassName("autocomplete-history-popup");
           if (kids.item(0) && textbox.getAttribute("open") != "true") { // Open history popup
+            var w = textbox.boxObject.width;
+            if (w != kids[0].boxObject.width)
+              kids[0].width = w;
             kids[0].showPopup(textbox, -1, -1, "popup", "bottomleft", "topleft");
             textbox.setAttribute("open", "true");
           }
         ]]></body>
       </method>
     </implementation>
 
     <handlers>