Bug 1416999 - Remove document.registerElement; r=smaug draft
authorEdgar Chen <echen@mozilla.com>
Thu, 16 Nov 2017 12:23:15 +0800
changeset 705159 cd2ebf166e8bd9c49910387c9136d7b83b51598d
parent 705158 2b89e22bcc98afbe3dfd98d0800cfafa79b31a21
child 705160 f7ad9c9ab187f706702de6968afe80d72269956e
push id91373
push userechen@mozilla.com
push dateWed, 29 Nov 2017 14:56:10 +0000
reviewerssmaug
bugs1416999
milestone59.0a1
Bug 1416999 - Remove document.registerElement; r=smaug MozReview-Commit-ID: HiX07Vbljhk
dom/base/CustomElementRegistry.cpp
dom/base/CustomElementRegistry.h
dom/base/Element.cpp
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsIDocument.h
dom/base/test/chrome/chrome.ini
dom/base/test/chrome/custom_element_ep.js
dom/base/test/chrome/frame_custom_element_content.html
dom/base/test/chrome/frame_registerElement_content.html
dom/base/test/chrome/registerElement_ep.js
dom/base/test/chrome/test_custom_element_content.xul
dom/base/test/chrome/test_custom_element_ep.xul
dom/base/test/chrome/test_registerElement_content.xul
dom/base/test/chrome/test_registerElement_ep.xul
dom/base/test/mochitest.ini
dom/base/test/test_custom_element.html
dom/base/test/test_document_register.html
dom/html/test/mochitest.ini
dom/html/test/test_bug1081037.html
dom/tests/mochitest/webcomponents/mochitest.ini
dom/tests/mochitest/webcomponents/test_custom_element_callback_innerhtml.html
dom/tests/mochitest/webcomponents/test_custom_element_define.html
dom/tests/mochitest/webcomponents/test_custom_element_define_parser.html
dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
dom/tests/mochitest/webcomponents/test_custom_element_lifecycle.html
dom/tests/mochitest/webcomponents/test_custom_element_register_invalid_callbacks.html
dom/tests/mochitest/webcomponents/test_custom_element_stack.html
dom/tests/mochitest/webcomponents/test_custom_element_template.html
dom/tests/mochitest/webcomponents/test_document_register.html
dom/tests/mochitest/webcomponents/test_document_register_parser.html
dom/tests/mochitest/webcomponents/test_document_shared_registry.html
dom/tests/mochitest/webcomponents/test_template_custom_elements.html
dom/tests/mochitest/webcomponents/test_unresolved_pseudo_class.html
dom/webidl/Document.webidl
dom/webidl/WebComponents.webidl
js/xpconnect/tests/mochitest/test_bug1094930.html
layout/base/crashtests/1261351-iframe.html
layout/generic/crashtests/1059138-1.html
layout/style/crashtests/1017798-1.html
testing/web-platform/meta/custom-elements/historical.html.ini
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -403,29 +403,16 @@ CustomElementRegistry::EnqueueLifecycleC
   }
 
   CustomElementReactionsStack* reactionsStack =
     docGroup->CustomElementReactionsStack();
   reactionsStack->EnqueueCallbackReaction(aCustomElement, Move(callback));
 }
 
 void
-CustomElementRegistry::GetCustomPrototype(nsAtom* aAtom,
-                                          JS::MutableHandle<JSObject*> aPrototype)
-{
-  mozilla::dom::CustomElementDefinition* definition =
-    mCustomDefinitions.GetWeak(aAtom);
-  if (definition) {
-    aPrototype.set(definition->mPrototype);
-  } else {
-    aPrototype.set(nullptr);
-  }
-}
-
-void
 CustomElementRegistry::UpgradeCandidates(nsAtom* aKey,
                                          CustomElementDefinition* aDefinition,
                                          ErrorResult& aRv)
 {
   DocGroup* docGroup = mWindow->GetDocGroup();
   if (!docGroup) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
@@ -615,19 +602,19 @@ CustomElementRegistry::Define(const nsAS
     AutoSetRunningFlag as(this);
 
     { // Enter constructor's compartment.
       /**
        * 10.1. Let prototype be Get(constructor, "prototype"). Rethrow any exceptions.
        */
       JSAutoCompartment ac(cx, constructor);
       JS::Rooted<JS::Value> prototypev(cx);
-      // The .prototype on the constructor passed from document.registerElement
-      // is the "expando" of a wrapper. So we should get it from wrapper instead
-      // instead of underlying object.
+      // The .prototype on the constructor passed could be an "expando" of a
+      // wrapper. So we should get it from wrapper instead of the underlying
+      // object.
       if (!JS_GetProperty(cx, constructor, "prototype", &prototypev)) {
         aRv.StealExceptionFromJSContext(cx);
         return;
       }
 
       /**
        * 10.2. If Type(prototype) is not Object, then throw a TypeError exception.
        */
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -366,29 +366,25 @@ public:
     JSContext* aCx, JSObject *aConstructor) const;
 
   static void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
                                        Element* aCustomElement,
                                        LifecycleCallbackArgs* aArgs,
                                        LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
                                        CustomElementDefinition* aDefinition);
 
-  void GetCustomPrototype(nsAtom* aAtom,
-                          JS::MutableHandle<JSObject*> aPrototype);
-
   /**
    * Upgrade an element.
    * https://html.spec.whatwg.org/multipage/scripting.html#upgrades
    */
   static void Upgrade(Element* aElement, CustomElementDefinition* aDefinition, ErrorResult& aRv);
 
   /**
    * 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
+   * upgrade. |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">.
    */
   void RegisterUnresolvedElement(Element* aElement,
                                  nsAtom* aTypeName = nullptr);
 
   /**
@@ -415,18 +411,18 @@ private:
   typedef nsClassHashtable<nsRefPtrHashKey<nsAtom>, nsTArray<nsWeakPtr>>
     CandidateMap;
   typedef JS::GCHashMap<JS::Heap<JSObject*>,
                         RefPtr<nsAtom>,
                         js::MovableCellHasher<JS::Heap<JSObject*>>,
                         js::SystemAllocPolicy> ConstructorMap;
 
   // Hashtable for custom element definitions in web components.
-  // Custom prototypes are stored in the compartment where
-  // registerElement was called.
+  // Custom prototypes are stored in the compartment where definition was
+  // defined.
   DefinitionMap mCustomDefinitions;
 
   // Hashtable for looking up definitions by using constructor as key.
   // Custom elements' name are stored here and we need to lookup
   // mCustomDefinitions again to get definitions.
   ConstructorMap mConstructors;
 
   typedef nsRefPtrHashtable<nsRefPtrHashKey<nsAtom>, Promise>
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -510,60 +510,21 @@ Element::GetBindingURL(nsIDocument *aDoc
 
   NS_IF_ADDREF(*aResult = sc->StyleDisplay()->mBinding);
   return true;
 }
 
 JSObject*
 Element::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
-  JS::Rooted<JSObject*> givenProto(aCx, aGivenProto);
-  JS::Rooted<JSObject*> customProto(aCx);
-
-  if (!givenProto) {
-    // Custom element prototype swizzling.
-    CustomElementData* data = GetCustomElementData();
-    if (data) {
-      // If this is a registered custom element then fix the prototype.
-      nsContentUtils::GetCustomPrototype(OwnerDoc(), NodeInfo()->NamespaceID(),
-                                         data->GetCustomElementType(), &customProto);
-      if (customProto &&
-          NodePrincipal()->SubsumesConsideringDomain(nsContentUtils::ObjectPrincipal(customProto))) {
-        // The custom element prototype could be in different compartment.
-        if (!JS_WrapObject(aCx, &customProto)) {
-          return nullptr;
-        }
-        // Just go ahead and create with the right proto up front.  Set
-        // customProto to null to flag that we don't need to do any post-facto
-        // proto fixups here.
-        givenProto = customProto;
-        customProto = nullptr;
-      }
-    }
-  }
-
-  JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx, givenProto));
+  JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx, aGivenProto));
   if (!obj) {
     return nullptr;
   }
 
-  if (customProto) {
-    // We want to set the custom prototype in the compartment where it was
-    // registered. In the case that |obj| and |prototype| are in different
-    // compartments, this will set the prototype on the |obj|'s wrapper and
-    // thus only visible in the wrapper's compartment, since we know obj's
-    // principal does not subsume customProto's in this case.
-    JSAutoCompartment ac(aCx, customProto);
-    JS::Rooted<JSObject*> wrappedObj(aCx, obj);
-    if (!JS_WrapObject(aCx, &wrappedObj) ||
-        !JS_SetPrototype(aCx, wrappedObj, customProto)) {
-      return nullptr;
-    }
-  }
-
   nsIDocument* doc;
   if (HasFlag(NODE_FORCE_XBL_BINDINGS)) {
     doc = OwnerDoc();
   }
   else {
     doc = GetComposedDoc();
   }
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -10344,43 +10344,16 @@ nsContentUtils::EnqueueLifecycleCallback
     return;
   }
 
   CustomElementRegistry::EnqueueLifecycleCallback(aType, aCustomElement, aArgs,
                                                   aAdoptedCallbackArgs,
                                                   aDefinition);
 }
 
-/* static */ void
-nsContentUtils::GetCustomPrototype(nsIDocument* aDoc,
-                                   int32_t aNamespaceID,
-                                   nsAtom* aAtom,
-                                   JS::MutableHandle<JSObject*> aPrototype)
-{
-  MOZ_ASSERT(aDoc);
-
-  if ((aNamespaceID != kNameSpaceID_XHTML &&
-       aNamespaceID != kNameSpaceID_XUL) ||
-      !aDoc->GetDocShell()) {
-    return;
-  }
-
-  nsCOMPtr<nsPIDOMWindowInner> window(aDoc->GetInnerWindow());
-  if (!window) {
-    return;
-  }
-
-  RefPtr<CustomElementRegistry> registry(window->CustomElements());
-  if (!registry) {
-    return;
-  }
-
-  return registry->GetCustomPrototype(aAtom, aPrototype);
-}
-
 /* static */ bool
 nsContentUtils::AttemptLargeAllocationLoad(nsIHttpChannel* aChannel)
 {
   MOZ_ASSERT(aChannel);
 
   nsCOMPtr<nsILoadGroup> loadGroup;
   nsresult rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   if (NS_WARN_IF(NS_FAILED(rv) || !loadGroup)) {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -695,17 +695,17 @@ public:
    */
   static nsresult NewURIWithDocumentCharset(nsIURI** aResult,
                                             const nsAString& aSpec,
                                             nsIDocument* aDocument,
                                             nsIURI* aBaseURI);
 
   /**
    * Returns true if |aName| is a valid name to be registered via
-   * document.registerElement.
+   * customElements.define.
    */
   static bool IsCustomElementName(nsAtom* aName);
 
   static nsresult CheckQName(const nsAString& aQualifiedName,
                              bool aNamespaceAware = true,
                              const char16_t** aColon = nullptr);
 
   static nsresult SplitQName(const nsIContent* aNamespaceResolver,
@@ -3054,21 +3054,16 @@ public:
                                      mozilla::dom::CustomElementDefinition* aDefinition);
 
   static void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
                                        Element* aCustomElement,
                                        mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
                                        mozilla::dom::LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs = nullptr,
                                        mozilla::dom::CustomElementDefinition* aDefinition = nullptr);
 
-  static void GetCustomPrototype(nsIDocument* aDoc,
-                                 int32_t aNamespaceID,
-                                 nsAtom* aAtom,
-                                 JS::MutableHandle<JSObject*> prototype);
-
   static bool AttemptLargeAllocationLoad(nsIHttpChannel* aChannel);
 
   /**
    * Appends all "document level" native anonymous content subtree roots for
    * aDocument to aElements.  Document level NAC subtrees are those created
    * by ancestor frames of the document element's primary frame, such as
    * the scrollbar elements created by the root scroll frame.
    */
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -6010,41 +6010,16 @@ bool IsLowercaseASCII(const nsAString& a
     char16_t c = aValue[i];
     if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
       return false;
     }
   }
   return true;
 }
 
-already_AddRefed<mozilla::dom::CustomElementRegistry>
-nsDocument::GetCustomElementRegistry()
-{
-  nsAutoString contentType;
-  GetContentType(contentType);
-  if (!IsHTMLDocument() &&
-      !contentType.EqualsLiteral("application/xhtml+xml")) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsPIDOMWindowInner> window(
-    do_QueryInterface(mScriptGlobalObject ? mScriptGlobalObject
-                                          : GetScopeObject()));
-  if (!window) {
-    return nullptr;
-  }
-
-  RefPtr<CustomElementRegistry> registry = window->CustomElements();
-  if (!registry) {
-    return nullptr;
-  }
-
-  return registry.forget();
-}
-
 // We only support pseudo-elements with two colons in this function.
 static CSSPseudoElementType
 GetPseudoElementType(const nsString& aString, ErrorResult& aRv)
 {
   MOZ_ASSERT(!aString.IsEmpty(), "GetPseudoElementType aString should be non-null");
   if (aString.Length() <= 2 || aString[0] != ':' || aString[1] != ':') {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return CSSPseudoElementType::NotPseudo;
@@ -6353,126 +6328,16 @@ nsIDocument::CreateAttributeNS(const nsA
     return nullptr;
   }
 
   RefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
                                     EmptyString());
   return attribute.forget();
 }
 
-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()));
-  RefPtr<nsGlobalWindowInner> window;
-  UNWRAP_OBJECT(Window, global, window);
-  MOZ_ASSERT(window, "Should have a non-null window");
-
-  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()));
-  nsAutoJSString elemName;
-  if (!elemName.init(aCx, jsFunName)) {
-    return true;
-  }
-
-  RefPtr<mozilla::dom::CustomElementRegistry> registry = window->CustomElements();
-  if (!registry) {
-    return true;
-  }
-
-  RefPtr<nsAtom> typeAtom(NS_Atomize(elemName));
-  CustomElementDefinition* definition =
-    registry->mCustomDefinitions.GetWeak(typeAtom);
-  if (!definition) {
-    return true;
-  }
-
-  RefPtr<Element> element;
-
-  // We integrate with construction stack and do prototype swizzling here, so
-  // that old upgrade behavior could also share the new upgrade steps.
-  // And this old upgrade will be remove at some point (when everything is
-  // switched to latest custom element spec).
-  nsTArray<RefPtr<Element>>& constructionStack =
-    definition->mConstructionStack;
-  if (constructionStack.Length()) {
-    element = constructionStack.LastElement();
-    NS_ENSURE_TRUE(element != ALEADY_CONSTRUCTED_MARKER, false);
-
-    // Do prototype swizzling if dom reflector exists.
-    JS::Rooted<JSObject*> reflector(aCx, element->GetWrapper());
-    if (reflector) {
-      Maybe<JSAutoCompartment> ac;
-      JS::Rooted<JSObject*> prototype(aCx, definition->mPrototype);
-      if (element->NodePrincipal()->SubsumesConsideringDomain(nsContentUtils::ObjectPrincipal(prototype))) {
-        ac.emplace(aCx, reflector);
-        if (!JS_WrapObject(aCx, &prototype) ||
-            !JS_SetPrototype(aCx, reflector, prototype)) {
-          return false;
-        }
-      } else {
-        // We want to set the custom prototype in the compartment where it was
-        // registered. We store the prototype from define() without unwrapped,
-        // hence the prototype's compartment is the compartment where it was
-        // registered.
-        // In the case that |reflector| and |prototype| are in different
-        // compartments, this will set the prototype on the |reflector|'s wrapper
-        // and thus only visible in the wrapper's compartment, since we know
-        // reflector's principal does not subsume prototype's in this case.
-        ac.emplace(aCx, prototype);
-        if (!JS_WrapObject(aCx, &reflector) ||
-            !JS_SetPrototype(aCx, reflector, prototype)) {
-          return false;
-        }
-      }
-
-      // Wrap into current context.
-      if (!JS_WrapObject(aCx, &reflector)) {
-        return false;
-      }
-
-      args.rval().setObject(*reflector);
-      return true;
-    }
-  } else {
-    RefPtr<mozilla::dom::NodeInfo> nodeInfo =
-      document->NodeInfoManager()->GetNodeInfo(definition->mLocalName, nullptr,
-                                               kNameSpaceID_XHTML,
-                                               nsIDOMNode::ELEMENT_NODE);
-
-    int32_t tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName);
-    if (tag == eHTMLTag_userdefined &&
-        nsContentUtils::IsCustomElementName(definition->mType)) {
-      element = NS_NewHTMLElement(nodeInfo.forget(), NOT_FROM_PARSER);
-    } else {
-      element = ::CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
-    }
-
-    element->SetCustomElementData(
-      new CustomElementData(definition->mType,
-                            CustomElementData::State::eCustom));
-
-    element->SetCustomElementDefinition(definition);
-
-    NS_ENSURE_TRUE(element, false);
-  }
-
-  // The prototype setup happens in Element::WrapObject().
-  nsresult rv = nsContentUtils::WrapNative(aCx, element, element, args.rval());
-  NS_ENSURE_SUCCESS(rv, true);
-
-  return true;
-}
-
 void
 nsDocument::ScheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG)
 {
   mLazySVGPresElements.PutEntry(aSVG);
 }
 
 void
 nsDocument::UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG)
@@ -6485,144 +6350,16 @@ nsDocument::ResolveScheduledSVGPresAttrs
 {
   for (auto iter = mLazySVGPresElements.Iter(); !iter.Done(); iter.Next()) {
     nsSVGElement* svg = iter.Get()->GetKey();
     svg->UpdateContentDeclarationBlock(StyleBackendType::Servo);
   }
   mLazySVGPresElements.Clear();
 }
 
-void
-nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
-                            const ElementRegistrationOptions& aOptions,
-                            JS::MutableHandle<JSObject*> aRetval,
-                            ErrorResult& rv)
-{
-  RefPtr<CustomElementRegistry> registry(GetCustomElementRegistry());
-  if (!registry) {
-    rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return;
-  }
-
-  AutoCEReaction ceReaction(this->GetDocGroup()->CustomElementReactionsStack(),
-                            aCx);
-  // Unconditionally convert TYPE to lowercase.
-  nsAutoString lcType;
-  nsContentUtils::ASCIIToLower(aType, lcType);
-
-  nsIGlobalObject* sgo = GetScopeObject();
-  if (!sgo) {
-    rv.Throw(NS_ERROR_UNEXPECTED);
-    return;
-  }
-
-  JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject());
-  JS::Rooted<JSObject*> protoObject(aCx);
-
-  if (!aOptions.mPrototype) {
-    JS::Rooted<JSObject*> htmlProto(aCx);
-    htmlProto = HTMLElementBinding::GetProtoObjectHandle(aCx);
-    if (!htmlProto) {
-      rv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return;
-    }
-
-    protoObject = JS_NewObjectWithGivenProto(aCx, nullptr, htmlProto);
-    if (!protoObject) {
-      rv.Throw(NS_ERROR_UNEXPECTED);
-      return;
-    }
-  } else {
-    protoObject = aOptions.mPrototype;
-
-    // Get the unwrapped prototype to do some checks.
-    JS::Rooted<JSObject*> protoObjectUnwrapped(aCx, js::CheckedUnwrap(protoObject));
-    if (!protoObjectUnwrapped) {
-      // If the caller's compartment does not have permission to access the
-      // unwrapped prototype then throw.
-      rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
-      return;
-    }
-
-    // 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(protoObjectUnwrapped);
-    if (IsDOMIfaceAndProtoClass(clasp)) {
-      rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-      return;
-    }
-
-    JS::Rooted<JS::PropertyDescriptor> descRoot(aCx);
-    JS::MutableHandle<JS::PropertyDescriptor> desc(&descRoot);
-    // This check may go through a wrapper, but as we checked above
-    // it should be transparent or an xray. This should be fine for now,
-    // until the spec is sorted out.
-    if (!JS_GetPropertyDescriptor(aCx, protoObject, "constructor", desc)) {
-      rv.Throw(NS_ERROR_UNEXPECTED);
-      return;
-    }
-
-    if (!desc.configurable()) {
-      rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-      return;
-    }
-  }
-
-  JS::Rooted<JSFunction*> constructor(aCx);
-  {
-    // Go into the document's global compartment when creating the constructor
-    // function because we want to get the correct document (where the
-    // definition is registered) when it is called.
-    JSAutoCompartment ac(aCx, global);
-
-    // Create constructor to return. Store the name of the custom element as the
-    // name of the function.
-    constructor = JS_NewFunction(aCx, nsDocument::CustomElementConstructor, 0,
-                                 JSFUN_CONSTRUCTOR,
-                                 NS_ConvertUTF16toUTF8(lcType).get());
-    if (!constructor) {
-      rv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return;
-    }
-  }
-
-  JS::Rooted<JSObject*> wrappedConstructor(aCx);
-  wrappedConstructor = JS_GetFunctionObject(constructor);
-  if (!JS_WrapObject(aCx, &wrappedConstructor)) {
-    rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return;
-  }
-
-  if (!JS_LinkConstructorAndPrototype(aCx, wrappedConstructor, protoObject)) {
-    rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return;
-  }
-
-  ElementDefinitionOptions options;
-  if (!aOptions.mExtends.IsVoid()) {
-    // Only convert NAME to lowercase in HTML documents.
-    nsAutoString lcName;
-    IsHTMLDocument() ? nsContentUtils::ASCIIToLower(aOptions.mExtends, lcName)
-                     : lcName.Assign(aOptions.mExtends);
-
-    options.mExtends.Construct(lcName);
-  }
-
-  // Note: No calls that might run JS or trigger CC after this, or there's a
-  // (vanishingly small) risk of our constructor being nulled before Define()
-  // can access it.
-  RefPtr<Function> functionConstructor =
-    new Function(aCx, wrappedConstructor, sgo);
-
-  registry->Define(lcType, *functionConstructor, options, rv);
-
-  aRetval.set(wrappedConstructor);
-}
-
 NS_IMETHODIMP
 nsDocument::GetElementsByTagName(const nsAString& aTagname,
                                  nsIDOMNodeList** aReturn)
 {
   RefPtr<nsContentList> list = GetElementsByTagName(aTagname);
   NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
 
   // transfer ref to aReturn
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -989,21 +989,16 @@ public:
   virtual void DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const override;
   // DocAddSizeOfIncludingThis is inherited from nsIDocument.
 
   virtual nsIDOMNode* AsDOMNode() override { return this; }
 
   // WebIDL bits
   virtual mozilla::dom::DOMImplementation*
     GetImplementation(mozilla::ErrorResult& rv) override;
-  virtual void
-    RegisterElement(JSContext* aCx, const nsAString& aName,
-                    const mozilla::dom::ElementRegistrationOptions& aOptions,
-                    JS::MutableHandle<JSObject*> aRetval,
-                    mozilla::ErrorResult& rv) override;
   virtual mozilla::dom::StyleSheetList* StyleSheets() override;
   virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) override;
   virtual void GetLastStyleSheetSet(nsString& aSheetSet) override;
   virtual mozilla::dom::DOMStringList* StyleSheetSets() override;
   virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) override;
   virtual already_AddRefed<Element> CreateElement(const nsAString& aTagName,
                                                   const mozilla::dom::ElementCreationOptionsOrString& aOptions,
                                                   ErrorResult& rv) override;
@@ -1184,22 +1179,18 @@ protected:
 
   RefPtr<PrincipalFlashClassifier> mPrincipalFlashClassifier;
   mozilla::dom::FlashClassification mFlashClassification;
   // Do not use this value directly. Call the |IsThirdParty()| method, which
   // caches its result here.
   mozilla::Maybe<bool> mIsThirdParty;
 private:
   void UpdatePossiblyStaleDocumentState();
-  static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
 
 public:
-  virtual already_AddRefed<mozilla::dom::CustomElementRegistry>
-    GetCustomElementRegistry() override;
-
   RefPtr<mozilla::EventListenerManager> mListenerManager;
   RefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets;
   RefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
   RefPtr<mozilla::dom::ScriptLoader> mScriptLoader;
   nsDocHeaderData* mHeaderData;
   /* mIdentifierMap works as follows for IDs:
    * 1) Attribute changes affect the table immediately (removing and adding
    *    entries as needed).
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -134,17 +134,16 @@ class DocGroup;
 class DocumentFragment;
 class DocumentTimeline;
 class DocumentType;
 class DOMImplementation;
 class DOMIntersectionObserver;
 class DOMStringList;
 class Element;
 struct ElementCreationOptions;
-struct ElementRegistrationOptions;
 class Event;
 class EventTarget;
 class FontFaceSet;
 class FrameRequestCallback;
 struct FullscreenRequest;
 class ImageTracker;
 class HTMLBodyElement;
 struct LifecycleCallbackArgs;
@@ -2856,24 +2855,16 @@ public:
     eConnected,
     eDisconnected,
     eAdopted,
     eAttributeChanged
   };
 
   nsIDocument* GetTopLevelContentDocument();
 
-  virtual void
-    RegisterElement(JSContext* aCx, const nsAString& aName,
-                    const mozilla::dom::ElementRegistrationOptions& aOptions,
-                    JS::MutableHandle<JSObject*> aRetval,
-                    mozilla::ErrorResult& rv) = 0;
-  virtual already_AddRefed<mozilla::dom::CustomElementRegistry>
-    GetCustomElementRegistry() = 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,
--- a/dom/base/test/chrome/chrome.ini
+++ b/dom/base/test/chrome/chrome.ini
@@ -13,18 +13,18 @@ support-files =
   file_bug990812-2.xul
   file_bug990812-3.xul
   file_bug990812-4.xul
   file_bug990812-5.xul
   file_bug1139964.xul
   file_bug1209621.xul
   fileconstructor_file.png
   frame_bug814638.xul
-  frame_registerElement_content.html
-  registerElement_ep.js
+  frame_custom_element_content.html
+  custom_element_ep.js
   host_bug814638.xul
   window_nsITextInputProcessor.xul
   title_window.xul
   window_swapFrameLoaders.xul
   window_groupedSHistory.xul
 
 [test_bug120684.xul]
 [test_bug206691.xul]
@@ -59,18 +59,18 @@ support-files = ../file_bug357450.js
 [test_bug914381.html]
 [test_bug990812.xul]
 [test_bug1063837.xul]
 [test_bug1139964.xul]
 [test_bug1209621.xul]
 [test_bug1346936.html]
 [test_cpows.xul]
 [test_getElementsWithGrid.html]
-[test_registerElement_content.xul]
-[test_registerElement_ep.xul]
+[test_custom_element_content.xul]
+[test_custom_element_ep.xul]
 [test_domparsing.xul]
 [test_fileconstructor.xul]
 [test_nsITextInputProcessor.xul]
 [test_permission_isHandlingUserInput.xul]
 support-files = ../dummy.html
 [test_range_getClientRectsAndTexts.html]
 [test_title.xul]
 support-files = file_title.xul
rename from dom/base/test/chrome/registerElement_ep.js
rename to dom/base/test/chrome/custom_element_ep.js
--- a/dom/base/test/chrome/registerElement_ep.js
+++ b/dom/base/test/chrome/custom_element_ep.js
@@ -1,8 +1,13 @@
-var proto = Object.create(HTMLElement.prototype);
-proto.magicNumber = 42;
-proto.connectedCallback = function() {
-  finishTest(this.magicNumber === 42);
+class XFoo extends HTMLElement {
+  constructor() {
+    super();
+    this.magicNumber = 42;
+  }
+
+  connectedCallback() {
+    finishTest(this.magicNumber === 42);
+  }
 };
-document.registerElement("x-foo", { prototype: proto });
+customElements.define("x-foo", XFoo);
 
 document.firstChild.appendChild(document.createElement("x-foo"));
rename from dom/base/test/chrome/frame_registerElement_content.html
rename to dom/base/test/chrome/frame_custom_element_content.html
rename from dom/base/test/chrome/test_registerElement_content.xul
rename to dom/base/test/chrome/test_custom_element_content.xul
--- a/dom/base/test/chrome/test_registerElement_content.xul
+++ b/dom/base/test/chrome/test_custom_element_content.xul
@@ -9,44 +9,48 @@ https://bugzilla.mozilla.org/show_bug.cg
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1130028"
      target="_blank">Mozilla Bug 1130028</a>
-  <iframe onload="startTests()" id="frame" src="http://example.com/chrome/dom/base/test/chrome/frame_registerElement_content.html"></iframe>
+  <iframe onload="startTests()" id="frame" src="http://example.com/chrome/dom/base/test/chrome/frame_custom_element_content.html"></iframe>
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
   /** Test for Bug 1130028 **/
   var connectedCallbackCount = 0;
 
-  // Callback should be called only once by element created in content.
-  function connectedCallbackCalled() {
-    connectedCallbackCount++;
-    is(connectedCallbackCount, 1, "Connected callback called, should be called once in test.");
-    is(this.magicNumber, 42, "Callback should be able to see the custom prototype.");
-  }
-
   function startTests() {
     var frame = $("frame");
 
-    var c = frame.contentDocument.registerElement("x-foo");
-    var elem = new c();
+    class XFoo extends frame.contentWindow.HTMLElement {};
+    frame.contentWindow.customElements.define("x-foo", XFoo);
+    var elem = new XFoo();
     is(elem.tagName, "X-FOO", "Constructor should create an x-foo element.");
 
-    var proto = Object.create(frame.contentWindow.HTMLElement.prototype);
-    proto.magicNumber = 42;
-    proto.connectedCallback = connectedCallbackCalled;
+    class XBar extends frame.contentWindow.HTMLElement {
+      constructor() {
+        super();
+        this.magicNumber = 42;
+      }
 
-    frame.contentDocument.registerElement("x-bar", { prototype: proto });
+      connectedCallback() {
+        connectedCallbackCount++;
+        // Callback should be called only once by element created in content.
+        is(connectedCallbackCount, 1, "Connected callback called, should be called once in test.");
+        is(this.magicNumber, 42, "Callback should be able to see the custom prototype.");
+      }
+    };
+
+    frame.contentWindow.customElements.define("x-bar", XBar);
     is(connectedCallbackCount, 1, "Connected callback should be called by element created in content.");
 
     var element = frame.contentDocument.createElement("x-bar");
     is(element.magicNumber, 42, "Should be able to see the custom prototype on created element.");
   }
 
   ]]></script>
 </window>
rename from dom/base/test/chrome/test_registerElement_ep.xul
rename to dom/base/test/chrome/test_custom_element_ep.xul
--- a/dom/base/test/chrome/test_registerElement_ep.xul
+++ b/dom/base/test/chrome/test_custom_element_ep.xul
@@ -9,17 +9,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1130028"
      target="_blank">Mozilla Bug 1130028</a>
-  <iframe onload="startTests()" id="frame" src="http://example.com/chrome/dom/base/test/chrome/frame_registerElement_content.html"></iframe>
+  <iframe onload="startTests()" id="frame" src="http://example.com/chrome/dom/base/test/chrome/frame_custom_element_content.html"></iframe>
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
   Components.utils.import("resource://gre/modules/Services.jsm");
 
   /** Test for Bug 1130028 **/
@@ -32,13 +32,13 @@ https://bugzilla.mozilla.org/show_bug.cg
   }
 
   function startTests() {
     var frame = $("frame");
 
     // Create a sandbox with an extended principal then run a script that registers a custom element in the sandbox.
     var sandbox = Components.utils.Sandbox([frame.contentWindow], { sandboxPrototype: frame.contentWindow });
     sandbox.finishTest = finishTest;
-    Services.scriptloader.loadSubScript("chrome://mochitests/content/chrome/dom/base/test/chrome/registerElement_ep.js", sandbox);
+    Services.scriptloader.loadSubScript("chrome://mochitests/content/chrome/dom/base/test/chrome/custom_element_ep.js", sandbox);
   }
 
   ]]></script>
 </window>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -630,17 +630,17 @@ subsuite = clipboard
 skip-if = toolkit == 'android' #bug 904183
 [test_createHTMLDocument.html]
 [test_declare_stylesheet_obsolete.html]
 [test_data_uri.html]
 [test_document.all_iteration.html]
 [test_document.all_unqualified.html]
 [test_document_constructor.html]
 [test_document_importNode_document.html]
-[test_document_register.html]
+[test_custom_element.html]
 [test_domcursor.html]
 [test_domparser_null_char.html]
 [test_domparsing.html]
 [test_domrequest.html]
 [test_domwindowutils.html]
 [test_element.matches.html]
 [test_element_closest.html]
 [test_elementTraversal.html]
rename from dom/base/test/test_document_register.html
rename to dom/base/test/test_custom_element.html
--- a/dom/base/test/test_document_register.html
+++ b/dom/base/test/test_custom_element.html
@@ -8,18 +8,20 @@
     <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129" target="_blank">Mozilla Bug 783129</a>
     <iframe id="fooframe" src="/"></iframe>
     <script type="application/javascript">
 
     /** Test for Bug 783129 **/
     SimpleTest.waitForExplicitFinish();
 
     function startTests() {
-      var c = document.getElementById("fooframe").contentDocument.registerElement("x-foo");
-      var elem = new c();
+      var frame = document.getElementById("fooframe");
+      class XFoo extends frame.contentWindow.HTMLElement {};
+      frame.contentWindow.customElements.define("x-foo", XFoo);
+      var elem = new XFoo();
       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();
     }
 
     </script>
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -565,17 +565,16 @@ skip-if = toolkit == 'android'
 [test_bug741266.html]
 skip-if = toolkit == "android" || toolkit == "windows" # Android: needs control of popup window size, windows(bug 1234520)
 [test_non-ascii-cookie.html]
 support-files = file_cookiemanager.js
 [test_bug765780.html]
 [test_bug871161.html]
 support-files = file_bug871161-1.html file_bug871161-2.html
 [test_bug1013316.html]
-[test_bug1081037.html]
 [test_window_open_close.html]
 tags = openwindow
 [test_viewport_resize.html]
 [test_image_clone_load.html]
 [test_bug1203668.html]
 [test_bug1166138.html]
 [test_bug1230665.html]
 [test_filepicker_default_directory.html]
deleted file mode 100644
--- a/dom/html/test/test_bug1081037.html
+++ /dev/null
@@ -1,133 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1081037
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 1081037</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="application/javascript">
-
-  /** Test for Bug 1081037 **/
-
-function shouldThrow(fun, msg, ex, todo) {
-  try {
-    fun();
-    ok(todo, msg);
-  } catch (e) {
-    ok(new RegExp(ex).test(e), msg + " (thrown:" + e + ")")
-  }
-}
-
-var Foo = document.registerElement('x-foo', {
-  prototype: {bar: 5}
-});
-
-Foo.prototype.bar = 6;
-var foo = new Foo();
-is(foo.bar, 6, "prototype of the ctor returned from registerElement works");
-
-var protoDesc = Object.getOwnPropertyDescriptor(Foo, "prototype");
-is(protoDesc.configurable, false, "proto should be non-configurable");
-is(protoDesc.enumerable, false, "proto should be non-enumerable");
-is(protoDesc.writable, false, "proto should be non-writable");
-
-// TODO: FIXME!
-shouldThrow(function() {
-              document.registerElement('x-foo2', {
-                prototype: Foo.prototype
-              });
-            },
-            "if proto is an interface prototype object, registerElement should throw",
-            "not supported",
-            /* todo = */ true);
-
-var nonConfigReadonlyProto = Object.create(HTMLElement.prototype,
-  { constructor: { configurable: false, writable: false, value: 42 } });
-
-shouldThrow(function() {
-              document.registerElement('x-nonconfig-readonly', {
-                prototype: nonConfigReadonlyProto
-              });
-            },
-            "non-configurable and not-writable constructor property",
-            "not supported");
-
-
-// this is not defined in current spec:
-var readonlyProto = Object.create(HTMLElement.prototype,
-  { constructor: { configurable: true, writable: false, value: 42 } });
-
-var Readonly = document.registerElement('x-nonconfig-readonly', {
-  prototype: readonlyProto
-});
-
-is(Readonly.prototype, readonlyProto, "configurable readonly constructor property");
-
-var handler = {
-  getOwnPropertyDescriptor: function(target, name) {
-    return name == "constructor" ? undefined : Object.getOwnPropertyDescriptor(target,name);
-  },
-  defineProperty: function(target, name, propertyDescriptor) {
-     if (name == "constructor") {
-      throw "spec this";
-    }
-
-    return Object.defineProperty(target, name, propertyDescriptor);
-  },
-  has: function(target, name) {
-    if (name == "constructor") {
-      return false;
-    }
-    return name in target;
-  }
-};
-var proxy = new Proxy({}, handler);
-
-shouldThrow(function() {
-              document.registerElement('x-proxymagic', {
-                prototype: proxy
-              });
-            },
-            "proxy magic",
-            "spec this");
-
-var getOwn = 0;
-var defineProp = 0;
-var handler2 = {
-  getOwnPropertyDescriptor: function(target, name) {
-    if (name == "constructor") {
-      getOwn++;
-    }
-    return Object.getOwnPropertyDescriptor(target,name);
-  },
-  defineProperty: function(target, name, propertyDescriptor) {
-    if (name == "constructor") {
-      defineProp++;
-    }
-    return Object.defineProperty(target, name, propertyDescriptor);
-  }
-};
-var proxy2 = new Proxy({}, handler2);
-
-document.registerElement('x-proxymagic2', {
-  prototype: proxy2
-});
-
-is(getOwn, 1, "number of getOwnPropertyDescriptor calls from registerElement: " + getOwn);
-is(defineProp, 1, "number of defineProperty calls from registerElement: " + defineProp);
-
-  </script>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081037">Mozilla Bug 1081037</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-</pre>
-</body>
-</html>
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -11,43 +11,41 @@ support-files =
 skip-if = true # Triggers assertions about flattened tree inconsistencies and it's going away in bug 1418002.
 [test_custom_element_callback_innerhtml.html]
 [test_custom_element_htmlconstructor.html]
 skip-if = os == 'android' # bug 1323645
 support-files =
   htmlconstructor_autonomous_tests.js
   htmlconstructor_builtin_tests.js
 [test_custom_element_in_shadow.html]
-[test_custom_element_register_invalid_callbacks.html]
 [test_custom_element_throw_on_dynamic_markup_insertion.html]
 [test_custom_element_get.html]
 [test_custom_element_when_defined.html]
 [test_custom_element_uncatchable_exception.html]
 skip-if = !debug # TestFunctions only applied in debug builds
 [test_custom_element_upgrade.html]
 support-files =
   test_upgrade_page.html
   upgrade_tests.js
 [test_custom_element_lifecycle.html]
 [test_custom_element_stack.html]
+[test_custom_element_define.html]
+[test_custom_element_define_parser.html]
+[test_custom_element_template.html]
 [test_nested_content_element.html]
 [test_dest_insertion_points.html]
 [test_fallback_dest_insertion_points.html]
 [test_detached_style.html]
 [test_dynamic_content_element_matching.html]
 [test_document_adoptnode.html]
 [test_document_importnode.html]
-[test_document_register.html]
-[test_document_register_parser.html]
-[test_document_shared_registry.html]
 [test_event_retarget.html]
 [test_event_stopping.html]
 [test_template.html]
 [test_template_xhtml.html]
-[test_template_custom_elements.html]
 [test_shadowroot.html]
 [test_shadowroot_inert_element.html]
 [test_shadowroot_style.html]
 [test_shadowroot_style_order.html]
 [test_style_fallback_content.html]
 skip-if = stylo # Bug 1410170
 [test_unresolved_pseudo_class.html]
 [test_link_prefetch.html]
--- a/dom/tests/mochitest/webcomponents/test_custom_element_callback_innerhtml.html
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_callback_innerhtml.html
@@ -13,29 +13,29 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="container"></div>
 
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
 var connectedCallbackCount = 0;
 
-var p = Object.create(HTMLElement.prototype);
+class Foo extends HTMLElement {
+  connectedCallback() {
+    ok(true, "connectedCallback should be called when the parser creates an element in the document.");
+    connectedCallbackCount++;
+    // connectedCallback should be called twice, once for the element created for innerHTML and
+    // once for the element created in this document.
+    if (connectedCallbackCount == 2) {
+      SimpleTest.finish();
+    }
+  }
+};
 
-p.connectedCallback = function() {
-  ok(true, "connectedCallback should be called when the parser creates an element in the document.");
-  connectedCallbackCount++;
-  // connectedCallback should be called twice, once for the element created for innerHTML and
-  // once for the element created in this document.
-  if (connectedCallbackCount == 2) {
-    SimpleTest.finish();
-  }
-}
-
-document.registerElement("x-foo", { prototype: p });
+customElements.define("x-foo", Foo);
 
 var container = document.getElementById("container");
 container.innerHTML = '<x-foo></x-foo>';
 
 </script>
 
 <x-foo></x-foo>
 
rename from dom/tests/mochitest/webcomponents/test_document_register.html
rename to dom/tests/mochitest/webcomponents/test_custom_element_define.html
--- a/dom/tests/mochitest/webcomponents/test_document_register.html
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_define.html
@@ -1,122 +1,103 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=783129
 -->
 <head>
-  <title>Test for document.registerElement using custom prototype</title>
+  <title>Test for customElements.define</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>
 <x-unresolved id="unresolved"></x-unresolved>
 </div>
 
 <script>
 
-function testRegisterExtend(tag, extend, proto, expectException) {
+function testDefineExtend(tag, extend, definition, expectException) {
   try {
-    document.registerElement(tag, { prototype: proto, extends: extend });
-    ok(!expectException, "Registered " + tag + " extending " + extend + " containing " + proto + " in proto chain.");
+    customElements.define(tag, definition, { extends: extend });
+    ok(!expectException, "Defined " + tag + " extending " + extend + ".");
   } catch (ex) {
-    ok(expectException, "Did not register " + tag + " extending " + extend + " containing " + proto + " in proto chain.");
+    ok(expectException, "Did not define " + tag + " extending " + extend + ".");
   }
 }
 
-function testRegisterSimple(tag, proto, expectException) {
+function testDefineSimple(tag, definition, expectException) {
   try {
-    document.registerElement(tag, { prototype: proto });
-    ok(!expectException, "Registered " + tag + " containing " + proto + " in proto chain.");
+    customElements.define(tag, definition);
+    ok(!expectException, "Defined " + tag + " extending HTMLElement.");
   } catch (ex) {
-    ok(expectException, "Did not register " + tag + " containing " + proto + " in proto chain.");
+    ok(expectException, "Did not define " + tag + " extending HTMLElement.");
   }
 }
 
 function startTest() {
-  // 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);
+  // Test defining some simple definition.
+  testDefineSimple("x-html-obj-elem", class extends HTMLElement {}, false);
+  testDefineSimple("x-html-obj-p", class extends HTMLParagraphElement {}, false);
 
   // 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 simpleProto = Object.create(HTMLElement.prototype);
-  testRegisterSimple("x-elem-simple-proto", simpleProto, false);
-
-  // Test registering some invalid prototypes.
-  testRegisterSimple("x-invalid-number", 42, true);
-  testRegisterSimple("x-invalid-boolean", false, true);
-  testRegisterSimple("x-invalid-float", 1.0, 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);
+  // Test defining some invalid definition.
+  testDefineSimple("x-invalid-number", 42, true);
+  testDefineSimple("x-invalid-boolean", false, true);
+  testDefineSimple("x-invalid-float", 1.0, true);
 
   // 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);
+  testDefineSimple("invalid", class extends HTMLElement {}, true);
+  testDefineSimple("annotation-xml", class extends HTMLElement {}, true);
+  testDefineSimple("color-profile", class extends HTMLElement {}, true);
+  testDefineSimple("font-face", class extends HTMLElement {}, true);
+  testDefineSimple("font-face-src", class extends HTMLElement {}, true);
+  testDefineSimple("font-face-uri", class extends HTMLElement {}, true);
+  testDefineSimple("font-face-format", class extends HTMLElement {}, true);
+  testDefineSimple("font-face-name", class extends HTMLElement {}, true);
+  testDefineSimple("missing-glyph", class extends HTMLElement {}, true);
 
-  // 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 defining elements that extend from an existing element.
+  testDefineExtend("x-extend-span", "span", class extends HTMLElement {}, false);
+  testDefineExtend("x-extend-span-caps", "SPAN", class extends HTMLElement {}, true);
 
-  // Test registering elements that extend from a non-existing element.
-  testRegisterExtend("x-extend-span-nonexist", "nonexisting", Object.create(HTMLElement.prototype), true);
+  // Test defining elements that extend from a non-existing element.
+  testDefineExtend("x-extend-span-nonexist", "nonexisting", class extends HTMLElement {}, true);
 
   // 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);
+  testDefineSimple("x-dupe-me", class extends HTMLElement {}, false);
+  testDefineSimple("x-dupe-me", class extends HTMLElement {}, true);
+  testDefineSimple("X-DUPE-ME", class extends HTMLElement {}, true);
+  testDefineExtend("x-dupe-me", "span", class extends HTMLElement {}, true);
 
-  // document.createElement with extended type.
-  var extendedProto = Object.create(HTMLButtonElement.prototype);
-  var buttonConstructor = document.registerElement("x-extended-button", { prototype: extendedProto, extends: "button" });
+  // customElements.define with extended type.
+  class ExtendButton extends HTMLButtonElement {};
+  customElements.define("x-extended-button", ExtendButton, { extends: "button" });
   var extendedButton = document.createElement("button", {is: "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.__proto__, ExtendButton.prototype, "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.");
   is(extendedButton.type, "submit", "Created element should be a button with type of \"submit\"");
 
   // Custom element constructor.
-  var constructedButton = new buttonConstructor();
+  var constructedButton = new ExtendButton();
   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.__proto__, ExtendButton.prototype, "Created element should have the prototype of 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 });
+  class XInHTMLNamespace extends HTMLElement {};
+  customElements.define("x-in-html-namespace", XInHTMLNamespace);
   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.");
+  isnot(wrongNamespaceElem.__proto__, XInHTMLNamespace.prototype, "Definition for element in html namespace should not apply to SVG elements.");
 }
 
 startTest();
 
 </script>
 </body>
 </html>
rename from dom/tests/mochitest/webcomponents/test_document_register_parser.html
rename to dom/tests/mochitest/webcomponents/test_custom_element_define_parser.html
--- a/dom/tests/mochitest/webcomponents/test_document_register_parser.html
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_define_parser.html
@@ -1,47 +1,61 @@
 <!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>
+  <title>Test for customElements.define 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.connectedCallback = 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;
+var uncaughtError;
+window.onerror = function(message, url, lineNumber, columnNumber, error) {
+  uncaughtError = error;
+};
+
+var isConnectedCallbackCalled = false;
+class XButton extends HTMLButtonElement {
+  connectedCallback() {
+    ok(!isConnectedCallbackCalled, "ConnectedCallback should only be called once.");
+    is(this.tagName, "BUTTON", "Only the <button> element should be upgraded.");
+    isConnectedCallbackCalled = true;
+  }
 };
 
-document.registerElement("x-button", { prototype: extendedButtonProto, extends: "button" });
+customElements.define("x-button", XButton, { extends: "button" });
 
-var divProto = Object.create(HTMLDivElement.prototype);
-var divCallbackCalled = false;
-divProto.connectedCallback = 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();
+class XDiv extends HTMLDivElement {
+  constructor() {
+    // Queue a task to check error and callbacks.
+    setTimeout(() => {
+      ok(isConnectedCallbackCalled, "ConnectedCallback should be called.");
+      ok(uncaughtError instanceof TypeError,
+       "TypeError should be filed for upgrading <x-div> element.");
+      SimpleTest.finish();
+    }, 0);
+    super();
+  }
+
+  connectedCallback() {
+    ok(false, "Connected callback for x-div should not be called.");
+  }
 };
 
-document.registerElement("x-div", { prototype: divProto });
+customElements.define("x-div", XDiv);
 
 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 -->
+<x-div></x-div><!-- should be upgraded, but failed -->
 <script>
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
@@ -100,21 +100,21 @@ is(disconnectedCallbackCount, 1, "discon
 
 connectedCallbackCount = 0;
 disconnectedCallbackCount = 0;
 attributeChangedCallbackCount = 0;
 
 shadow = container.shadowRoot;
 shadow.innerHTML = "<x-bar></x-bar>";
 
-var p2 = Object.create(HTMLElement.prototype);
-
-p2.connectedCallback = function() {
-  connectedCallbackCount++;
+class Bar extends HTMLElement {
+  connectedCallback() {
+    connectedCallbackCount++;
+  }
 };
 
-document.registerElement("x-bar", { prototype: p2 });
+customElements.define("x-bar", Bar);
 is(connectedCallbackCount, 1, "connectedCallback should be called after upgrading element in composed document.");
 
 </script>
 
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_custom_element_lifecycle.html
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_lifecycle.html
@@ -1,193 +1,200 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=783129
 -->
 <head>
-  <title>Test for document.registerElement lifecycle callback</title>
+  <title>Test for custom elements 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 container = document.getElementById("container");
 
-// Tests callbacks after registering element type that is already in the document.
-// create element in document -> register -> remove from document
+// Tests callbacks after defining element type that is already in the document.
+// create element in document -> define -> remove from document
 function testRegisterUnresolved() {
   var helloElem = document.getElementById("hello");
 
   var connectedCallbackCalled = false;
   var disconnectedCallbackCalled = false;
 
-  var p = Object.create(HTMLElement.prototype);
-  p.connectedCallback = function() {
-    is(connectedCallbackCalled, false, "Connected callback should only be called once in this test.");
-    is(this, helloElem, "The 'this' value should be the custom element.");
-    connectedCallbackCalled = true;
+  class Hello extends HTMLElement {
+    connectedCallback() {
+      is(connectedCallbackCalled, false, "Connected callback should only be called once in this test.");
+      is(this, helloElem, "The 'this' value should be the custom element.");
+      connectedCallbackCalled = true;
+    }
+
+    disconnectedCallback() {
+      is(connectedCallbackCalled, true, "Connected callback should be called before detached");
+      is(disconnectedCallbackCalled, false, "Disconnected callback should only be called once in this test.");
+      disconnectedCallbackCalled = true;
+      is(this, helloElem, "The 'this' value should be the custom element.");
+      runNextTest();
+    }
+
+    attributeChangedCallback(name, oldValue, newValue) {
+      ok(false, "attributeChanged callback should never be called in this test.");
+    }
   };
 
-  p.disconnectedCallback = function() {
-    is(connectedCallbackCalled, true, "Connected callback should be called before detached");
-    is(disconnectedCallbackCalled, false, "Disconnected callback should only be called once in this test.");
-    disconnectedCallbackCalled = 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 });
+  customElements.define("x-hello", Hello);
 
   // Remove element from document to trigger disconnected callback.
   container.removeChild(helloElem);
 }
 
-// Tests callbacks after registering an extended element type that is already in the document.
-// create element in document -> register -> remove from document
+// Tests callbacks after defining an extended element type that is already in the document.
+// create element in document -> define -> remove from document
 function testRegisterUnresolvedExtended() {
   var buttonElem = document.getElementById("extbutton");
 
   var connectedCallbackCalled = false;
   var disconnectedCallbackCalled = false;
 
-  var p = Object.create(HTMLButtonElement.prototype);
-  p.connectedCallback = function() {
-    is(connectedCallbackCalled, false, "Connected callback should only be called once in this test.");
-    is(this, buttonElem, "The 'this' value should be the custom element.");
-    connectedCallbackCalled = true;
+  class XButton extends HTMLButtonElement {
+    connectedCallback() {
+      is(connectedCallbackCalled, false, "Connected callback should only be called once in this test.");
+      is(this, buttonElem, "The 'this' value should be the custom element.");
+      connectedCallbackCalled = true;
+    }
+
+    disconnectedCallback() {
+      is(connectedCallbackCalled, true, "Connected callback should be called before detached");
+      is(disconnectedCallbackCalled, false, "Disconnected callback should only be called once in this test.");
+      disconnectedCallbackCalled = true;
+      is(this, buttonElem, "The 'this' value should be the custom element.");
+      runNextTest();
+    }
+
+    attributeChangedCallback(name, oldValue, newValue) {
+      ok(false, "attributeChanged callback should never be called in this test.");
+    }
   };
 
-  p.disconnectedCallback = function() {
-    is(connectedCallbackCalled, true, "Connected callback should be called before detached");
-    is(disconnectedCallbackCalled, false, "Disconnected callback should only be called once in this test.");
-    disconnectedCallbackCalled = 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" });
+  customElements.define("x-button", XButton, { extends: "button" });
 
   // Remove element from document to trigger disconnected callback.
   container.removeChild(buttonElem);
 }
 
 function testInnerHTML() {
   var connectedCallbackCalled = false;
 
-  var p = Object.create(HTMLElement.prototype);
-  p.connectedCallback = function() {
-    is(connectedCallbackCalled, false, "Connected callback should only be called once in this test.");
-    connectedCallbackCalled = true;
+  class XInnerHTML extends HTMLElement {
+    connectedCallback() {
+      is(connectedCallbackCalled, false, "Connected callback should only be called once in this test.");
+      connectedCallbackCalled = true;
+    }
   };
 
-  document.registerElement("x-inner-html", { prototype: p });
+  customElements.define("x-inner-html", XInnerHTML);
   var div = document.createElement(div);
   document.documentElement.appendChild(div);
   div.innerHTML = '<x-inner-html></x-inner-html>';
   is(connectedCallbackCalled, true, "Connected callback should be called after setting innerHTML.");
   runNextTest();
 }
 
 function testInnerHTMLExtended() {
   var connectedCallbackCalled = false;
 
-  var p = Object.create(HTMLButtonElement.prototype);
-  p.connectedCallback = function() {
-    is(connectedCallbackCalled, false, "Connected callback should only be called once in this test.");
-    connectedCallbackCalled = true;
+  class XInnerHTMLExtend extends HTMLButtonElement {
+    connectedCallback() {
+      is(connectedCallbackCalled, false, "Connected callback should only be called once in this test.");
+      connectedCallbackCalled = true;
+    }
   };
 
-  document.registerElement("x-inner-html-extended", { prototype: p, extends: "button" });
+  customElements.define("x-inner-html-extended", XInnerHTMLExtend, { extends: "button" });
   var div = document.createElement(div);
   document.documentElement.appendChild(div);
   div.innerHTML = '<button is="x-inner-html-extended"></button>';
   is(connectedCallbackCalled, true, "Connected callback should be called after setting innerHTML.");
   runNextTest();
 }
 
 function testInnerHTMLUpgrade() {
   var connectedCallbackCalled = false;
 
   var div = document.createElement(div);
   document.documentElement.appendChild(div);
   div.innerHTML = '<x-inner-html-upgrade></x-inner-html-upgrade>';
 
-  var p = Object.create(HTMLElement.prototype);
-  p.connectedCallback = function() {
-    is(connectedCallbackCalled, false, "Connected callback should only be called once in this test.");
-    connectedCallbackCalled = true;
+  class XInnerHTMLUpgrade extends HTMLElement {
+    connectedCallback() {
+      is(connectedCallbackCalled, false, "Connected callback should only be called once in this test.");
+      connectedCallbackCalled = true;
+    }
   };
 
-  document.registerElement("x-inner-html-upgrade", { prototype: p });
+  customElements.define("x-inner-html-upgrade", XInnerHTMLUpgrade);
   is(connectedCallbackCalled, true, "Connected callback should be called after registering.");
   runNextTest();
 }
 
 function testInnerHTMLExtendedUpgrade() {
   var connectedCallbackCalled = false;
 
   var div = document.createElement(div);
   document.documentElement.appendChild(div);
   div.innerHTML = '<button is="x-inner-html-extended-upgrade"></button>';
 
-  var p = Object.create(HTMLButtonElement.prototype);
-  p.connectedCallback = function() {
-    is(connectedCallbackCalled, false, "Connected callback should only be called once in this test.");
-    connectedCallbackCalled = true;
+  class XInnerHTMLExtnedUpgrade extends HTMLButtonElement {
+    connectedCallback() {
+      is(connectedCallbackCalled, false, "Connected callback should only be called once in this test.");
+      connectedCallbackCalled = true;
+    }
   };
 
-  document.registerElement("x-inner-html-extended-upgrade", { prototype: p, extends: "button" });
+  customElements.define("x-inner-html-extended-upgrade", XInnerHTMLExtnedUpgrade, { extends: "button" });
   is(connectedCallbackCalled, true, "Connected 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
+// Test callback when creating element after defining an element type.
+// define -> create element -> insert into document -> remove from document
 function testRegisterResolved() {
   var connectedCallbackCalled = false;
   var disconnectedCallbackCalled = false;
 
-  var p = Object.create(HTMLElement.prototype);
-  p.connectedCallback = function() {
-    is(connectedCallbackCalled, false, "Connected callback should only be called on in this test.");
-    is(this, createdElement, "The 'this' value should be the custom element.");
-    connectedCallbackCalled = true;
+  class Resolved extends HTMLElement {
+    connectedCallback() {
+      is(connectedCallbackCalled, false, "Connected callback should only be called on in this test.");
+      is(this, createdElement, "The 'this' value should be the custom element.");
+      connectedCallbackCalled = true;
+    }
+
+    disconnectedCallback() {
+      is(connectedCallbackCalled, true, "Connected callback should be called before detached");
+      is(disconnectedCallbackCalled, false, "Disconnected callback should only be called once in this test.");
+      is(this, createdElement, "The 'this' value should be the custom element.");
+      disconnectedCallbackCalled = true;
+      runNextTest();
+    }
+
+    attributeChangedCallback() {
+      ok(false, "attributeChanged callback should never be called in this test.");
+    }
   };
 
-  p.disconnectedCallback = function() {
-    is(connectedCallbackCalled, true, "Connected callback should be called before detached");
-    is(disconnectedCallbackCalled, false, "Disconnected callback should only be called once in this test.");
-    is(this, createdElement, "The 'this' value should be the custom element.");
-    disconnectedCallbackCalled = true;
-    runNextTest();
-  };
-
-  p.attributeChangedCallback = function() {
-    ok(false, "attributeChanged callback should never be called in this test.");
-  };
-
-  document.registerElement("x-resolved", { prototype: p });
+  customElements.define("x-resolved", Resolved);
 
   var createdElement = document.createElement("x-resolved");
-  is(createdElement.__proto__, p, "Prototype of custom element should be the registered prototype.");
+  is(createdElement.__proto__, Resolved.prototype, "Prototype of custom element should be the defined prototype.");
 
   // Insert element into document to trigger attached callback.
   container.appendChild(createdElement);
 
   // Remove element from document to trigger detached callback.
   container.removeChild(createdElement);
 }
 
@@ -293,60 +300,61 @@ function testUpgradeCandidate() {
   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);
+  class DestinedForFragment extends HTMLElement {
+    connectedCallback() {
+      ok(false, "Connected callback should not be called.");
+    }
 
-  p.attached = function() {
-    ok(false, "attached should not be called when not entering the document.");
-  };
-
-  p.detached = function() {
-    ok(false, "leaveView should not be called when not leaving the document.");
+    disconnectedCallback() {
+      ok(false, "Disconnected callback should not be called.");
+    }
   };
 
   var createdElement = document.createElement("x-destined-for-fragment");
 
-  document.registerElement("x-destined-for-fragment", { prototype: p });
+  customElements.define("x-destined-for-fragment", DestinedForFragment);
 
   var fragment = new DocumentFragment();
   fragment.appendChild(createdElement);
   fragment.removeChild(createdElement);
 
   var divNotInDoc = document.createElement("div");
   divNotInDoc.appendChild(createdElement);
   divNotInDoc.removeChild(createdElement);
 
   runNextTest();
 }
 
 function testEnterLeaveView() {
   var connectedCallbackCalled = false;
   var disconnectedCallbackCalled = false;
 
-  var p = Object.create(HTMLElement.prototype);
-  p.connectedCallback = function() {
-    is(connectedCallbackCalled, false, "Connected callback should only be called on in this test.");
-    connectedCallbackCalled = true;
-  };
+  class ElementInDiv extends HTMLElement {
+    connectedCallback() {
+      is(connectedCallbackCalled, false, "Connected callback should only be called on in this test.");
+      connectedCallbackCalled = true;
+    }
 
-  p.disconnectedCallback = function() {
-    is(connectedCallbackCalled, true, "Connected callback should be called before detached");
-    is(disconnectedCallbackCalled, false, "Disconnected callback should only be called once in this test.");
-    disconnectedCallbackCalled = true;
-    runNextTest();
+    disconnectedCallback() {
+      is(connectedCallbackCalled, true, "Connected callback should be called before detached");
+      is(disconnectedCallbackCalled, false, "Disconnected callback should only be called once in this test.");
+      disconnectedCallbackCalled = true;
+      runNextTest();
+    }
   };
 
   var div = document.createElement("div");
-  document.registerElement("x-element-in-div", { prototype: p });
+  customElements.define("x-element-in-div", ElementInDiv);
   var customElement = document.createElement("x-element-in-div");
   div.appendChild(customElement);
   is(connectedCallbackCalled, false, "Appending a custom element to a node that is not in the document should not call the connected callback.");
 
   container.appendChild(div);
   container.removeChild(div);
 }
 
deleted file mode 100644
--- a/dom/tests/mochitest/webcomponents/test_custom_element_register_invalid_callbacks.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1275835
--->
-<head>
-  <title>Test registering invalid lifecycle callbacks for custom elements.</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=1275835">Bug 1275835</a>
-<iframe id="iframe"></iframe>
-<script>
-
-// Use window from iframe to isolate the test.
-const testWindow = iframe.contentDocument.defaultView;
-
-// This is for backward compatibility.
-// We should do the same checks for the callbacks from v0 spec.
-[
-  'attributeChangedCallback',
-].forEach(callback => {
-  var c = class {};
-  var p = c.prototype;
-
-  // Test getting callback throws exception.
-  Object.defineProperty(p, callback, {
-    get() {
-      const e = new Error('this is rethrown');
-      e.name = 'rethrown';
-      throw e;
-    }
-  });
-
-  SimpleTest.doesThrow(() => {
-    testWindow.document.registerElement(`test-register-${callback}-rethrown`,
-                                        { prototype: p });
-  }, `document.registerElement should throw exception if prototype.${callback} throws`);
-
-  SimpleTest.doesThrow(() => {
-    testWindow.customElements.define(`test-define-${callback}-rethrown`, c);
-  }, `customElements.define should throw exception if constructor.${callback} throws`);
-
-  // Test callback is not callable.
-  [
-    { name: 'null', value: null },
-    { name: 'object', value: {} },
-    { name: 'integer', value: 1 },
-  ].forEach(data => {
-    var c = class {};
-    var p = c.prototype;
-
-    p[callback] = data.value;
-
-    SimpleTest.doesThrow(() => {
-      testWindow.document.registerElement(`test-register-${callback}-${data.name}`,
-                                             { prototype: p });
-    }, `document.registerElement should throw exception if ${callback} is ${data.name}`);
-
-    SimpleTest.doesThrow(() => {
-      testWindow.customElements.define(`test-define-${callback}-${data.name}`, c);
-    }, `customElements.define should throw exception if ${callback} is ${data.name}`);
-  });
-});
-
-</script>
-</body>
-</html>
--- a/dom/tests/mochitest/webcomponents/test_custom_element_stack.html
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_stack.html
@@ -1,15 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=783129
 -->
 <head>
-  <title>Test for document.registerElement lifecycle callback</title>
+  <title>Test for custom elements 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>
@@ -45,38 +45,39 @@ function testChangeAttributeInEnteredVie
   customElements.define("x-two", Two);
   var elem = document.createElement("x-two");
 
   var container = document.getElementById("container");
   container.appendChild(elem);
 }
 
 function testLeaveViewInEnteredViewCallback() {
-  var p = Object.create(HTMLElement.prototype);
   var connectedCallbackCalled = false;
   var disconnectedCallbackCalled = false;
   var container = document.getElementById("container");
 
-  p.connectedCallback = function() {
-    is(this.parentNode, container, "Parent node should the container in which the node was appended.");
-    is(connectedCallbackCalled, false, "Connected callback should be called only once in this test.");
-    connectedCallbackCalled = true;
-    is(disconnectedCallbackCalled, false, "Disconnected callback should not be called prior to removing element from document.");
-    container.removeChild(this);
-    is(disconnectedCallbackCalled, true, "Transition from user-agent implementation to script should run left view callback.");
-    runNextTest();
+  class Three extends HTMLElement {
+    connectedCallback() {
+      is(this.parentNode, container, "Parent node should the container in which the node was appended.");
+      is(connectedCallbackCalled, false, "Connected callback should be called only once in this test.");
+      connectedCallbackCalled = true;
+      is(disconnectedCallbackCalled, false, "Disconnected callback should not be called prior to removing element from document.");
+      container.removeChild(this);
+      is(disconnectedCallbackCalled, true, "Transition from user-agent implementation to script should run left view callback.");
+      runNextTest();
+    }
+
+    disconnectedCallback() {
+      is(disconnectedCallbackCalled, false, "The disconnected callback should only be called once in this test.");
+      is(connectedCallbackCalled, true, "The connected callback should be called prior to disconnected callback.");
+      disconnectedCallbackCalled = true;
+    }
   };
 
-  p.disconnectedCallback = function() {
-    is(disconnectedCallbackCalled, false, "The disconnected callback should only be called once in this test.");
-    is(connectedCallbackCalled, true, "The connected callback should be called prior to disconnected callback.");
-    disconnectedCallbackCalled = true;
-  };
-
-  document.registerElement("x-three", { prototype: p });
+  customElements.define("x-three", Three);
   var elem = document.createElement("x-three");
 
   container.appendChild(elem);
 }
 
 function testStackedAttributeChangedCallback() {
   var attributeChangedCallbackCount = 0;
 
rename from dom/tests/mochitest/webcomponents/test_template_custom_elements.html
rename to dom/tests/mochitest/webcomponents/test_custom_element_template.html
--- a/dom/tests/mochitest/webcomponents/test_template_custom_elements.html
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_template.html
@@ -10,23 +10,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <template>
   <x-foo></x-foo>
 </template>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1091425">Bug 1091425</a>
 <script>
 
-var p = {};
-p.createdCallback = function() {
-  ok(false, "Created callback should not be called for custom elements in templates.");
+class XFoo extends HTMLElement {
+  connectedCallback() {
+    ok(false, "Connected callback should not be called for custom elements in templates.");
+  }
 };
 
-document.registerElement("x-foo", { prototype: p });
+customElements.define("x-foo", XFoo);
 
-ok(true, "Created callback should not be called for custom elements in templates.");
+ok(true, "Connected callback should not be called for custom elements in templates.");
 
 </script>
 <template>
   <x-foo></x-foo>
 </template>
 </body>
 </html>
deleted file mode 100644
--- a/dom/tests/mochitest/webcomponents/test_document_shared_registry.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=783129
--->
-<head>
-  <title>Test shared registry for associated HTML documents.</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<div id="container"></div>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
-<script>
-var container = document.getElementById("container");
-
-function registerNoRegistryDoc() {
-  var assocDoc = document.implementation.createDocument(null, "html");
-  try {
-    assocDoc.registerElement("x-dummy", { prototype: Object.create(HTMLElement.prototype) });
-    ok(false, "Registring element in document without registry should throw.");
-  } catch (ex) {
-    ok(true, "Registring element in document without registry should throw.");
-  }
-
-  runNextTest();
-}
-
-function runNextTest() {
-  if (testFunctions.length > 0) {
-    var nextTestFunction = testFunctions.shift();
-    nextTestFunction();
-  }
-}
-
-var testFunctions = [
-  registerNoRegistryDoc,
-  SimpleTest.finish
-];
-
-SimpleTest.waitForExplicitFinish();
-
-runNextTest();
-</script>
-</body>
-</html>
--- a/dom/tests/mochitest/webcomponents/test_unresolved_pseudo_class.html
+++ b/dom/tests/mochitest/webcomponents/test_unresolved_pseudo_class.html
@@ -37,63 +37,65 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="container"></div>
 <x-foo id="foo"></x-foo>
 <span id="span1">This text should be green</span>
 <span id="bar" is="x-bar"></span>
 <x-baz id="baz"></x-baz>
 <span id="del" is="x-del"></span>
 <script>
 
-// Before registerElement
+// Before define
 var foo = document.querySelector('#foo');
 is(getComputedStyle(foo).color, "rgb(0, 0, 255)", "foo - color");
 is(getComputedStyle(foo).backgroundColor, "rgb(0, 0, 255)", "foo - backgroundColor");
 
 var bar = document.querySelector('#bar');
 is(getComputedStyle(bar).color, "rgb(0, 0, 255)", "bar - color");
 is(getComputedStyle(bar).backgroundColor, "rgb(255, 0, 0)", "bar - backgroundColor");
 
 var baz = document.querySelector('#baz');
 is(getComputedStyle(baz).color, "rgb(0, 0, 255)", "baz - color");
 is(getComputedStyle(baz).backgroundColor, "rgb(0, 0, 255)", "baz - backgroundColor");
 
 var span1 = document.querySelector('#span1');
 is(getComputedStyle(span1).color, "rgb(255, 0, 0)", "span1 - color");
 
-var Foo = document.registerElement('x-foo', { prototype: Object.create(HTMLElement.prototype) });
+customElements.define('x-foo', class extends HTMLElement {});
 
-var Bar = document.registerElement('x-bar', { extends: 'span', prototype: Object.create(HTMLSpanElement.prototype) });
+customElements.define('x-bar', class extends HTMLSpanElement {}, { extends: 'span' });
 
-var Baz = document.registerElement('x-baz', { prototype: Object.create(HTMLElement.prototype) });
+customElements.define('x-baz', class extends HTMLElement {});
 
-// After registerElement
+// After define
 is(getComputedStyle(foo).color, "rgb(255, 0, 0)",
-   "foo - color (after registerElement)");
+   "foo - color (after define)");
 
 is(getComputedStyle(bar).color,
-   "rgb(255, 0, 0)", "bar - color (after registerElement)");
+   "rgb(255, 0, 0)", "bar - color (after define)");
 
 is(getComputedStyle(baz).color,
-   "rgb(255, 0, 0)", "baz - color (after registerElement)");
+   "rgb(255, 0, 0)", "baz - color (after define)");
 is(getComputedStyle(baz).backgroundColor,
-   "rgb(255, 0, 0)", "baz - backgroundColor (after registerElement)");
+   "rgb(255, 0, 0)", "baz - backgroundColor (after define)");
 
-is(getComputedStyle(span1).color, "rgb(0, 255, 0)", "span1 - color (after registerElement)");
+is(getComputedStyle(span1).color, "rgb(0, 255, 0)", "span1 - color (after define)");
 
 // After tree removal
 var del = document.querySelector('#del');
 is(getComputedStyle(del).color, "rgb(0, 0, 255)", "del - color");
 var par = del.parentNode;
 par.removeChild(del);
 // Changing is attribute after creation should not change the type
 // of a custom element, even if the element was removed and re-append to the tree.
 del.setAttribute("is", "foobar");
 par.appendChild(del);
 is(getComputedStyle(del).color, "rgb(0, 0, 255)", "del - color (after reappend)");
-var Del = document.registerElement('x-del', { extends: 'span', prototype: Object.create(HTMLSpanElement.prototype) });
+
+class Del extends HTMLSpanElement {};
+customElements.define('x-del', Del, { extends: 'span' });
 // [is="x-del"] will not match any longer so the rule of span will apply
-is(getComputedStyle(del).color, "rgb(0, 255, 0)", "del - color (after registerElement)");
+is(getComputedStyle(del).color, "rgb(0, 255, 0)", "del - color (after define)");
 // but the element should have been upgraded:
 ok(del instanceof Del, "element was upgraded correctly after changing |is|");
 
 </script>
 </body>
 </html>
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -259,23 +259,16 @@ partial interface Document {
   readonly attribute Element? pointerLockElement;
   void exitPointerLock();
 
   // Event handlers
   attribute EventHandler onpointerlockchange;
   attribute EventHandler onpointerlockerror;
 };
 
-//http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
-partial interface Document {
-    // this is deprecated from CustomElements v0
-    [Throws, Func="CustomElementRegistry::IsCustomElementEnabled"]
-    object registerElement(DOMString name, optional ElementRegistrationOptions options);
-};
-
 // http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html#sec-document-interface
 // https://w3c.github.io/page-visibility/#extensions-to-the-document-interface
 partial interface Document {
   readonly attribute boolean hidden;
   readonly attribute VisibilityState visibilityState;
            attribute EventHandler onvisibilitychange;
 };
 
--- a/dom/webidl/WebComponents.webidl
+++ b/dom/webidl/WebComponents.webidl
@@ -20,13 +20,8 @@ callback LifecycleAttributeChangedCallba
                                                   DOMString? namespaceURI);
 
 dictionary LifecycleCallbacks {
   LifecycleConnectedCallback? connectedCallback;
   LifecycleDisconnectedCallback? disconnectedCallback;
   LifecycleAdoptedCallback? adoptedCallback;
   LifecycleAttributeChangedCallback? attributeChangedCallback;
 };
-
-dictionary ElementRegistrationOptions {
-  object? prototype = null;
-  DOMString? extends = null;
-};
--- a/js/xpconnect/tests/mochitest/test_bug1094930.html
+++ b/js/xpconnect/tests/mochitest/test_bug1094930.html
@@ -10,20 +10,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <iframe id="ifr"></iframe>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1094930">Mozilla Bug 1094930</a>
 <p id="display"></p>
 <script type="application/javascript">
   SimpleTest.waitForExplicitFinish();
-  var proto = { 
-    connectedCallback: function() {
+  class XFoo extends frames[0].HTMLElement {
+    connectedCallback() {
       ok(true, "connectedCallback was called");
-      SimpleTest.finish()
+      SimpleTest.finish();
     }
   };
 
-  var f = document.registerElement.call(frames[0].document, "x-foo", { prototype: proto });
-  frames[0].document.firstChild.appendChild(new f());
+  customElements.define.call(frames[0].customElements, "x-foo", XFoo);
+  frames[0].document.firstChild.appendChild(new XFoo());
 </script>
 </body>
 </html>
--- a/layout/base/crashtests/1261351-iframe.html
+++ b/layout/base/crashtests/1261351-iframe.html
@@ -1,26 +1,28 @@
 <body>
 <script type="application/javascript">
   'use strict';
   // -sp-context: content
   (function () {
-    let proto = Object.create(HTMLDivElement.prototype);
-    proto.template = `<style></style>`;
-    proto.createdCallback = function() {
-    let shadow = this.createShadowRoot();
-    if (this.template) {
-      let te = document.createElement('template');
-      te.innerHTML = this.template;
-      shadow.appendChild(document.importNode(te.content, true));
-    }
-  };
+    class UiComponentTest extends HTMLDivElement {
+      constructor() {
+        super();
+        this.template = `<style></style>`;
+      }
 
-  let UiComponentTest = document.registerElement('ui-component-test', {
-    prototype: proto,
-  });
+      connectedCallback() {
+        let shadow = this.createShadowRoot();
+        if (this.template) {
+          let te = document.createElement('template');
+          te.innerHTML = this.template;
+          shadow.appendChild(document.importNode(te.content, true));
+        }
+      }
+    };
 
-  let uic = new UiComponentTest();
-  document.body.appendChild(uic);
+    customElements.define('ui-component-test', UiComponentTest, { extend: 'div'} );
 
+    let uic = new UiComponentTest();
+    document.body.appendChild(uic);
   })();
 </script>
 </body>
--- a/layout/generic/crashtests/1059138-1.html
+++ b/layout/generic/crashtests/1059138-1.html
@@ -12,29 +12,27 @@
   </div>
 </template>
 
 <script>
   // Gets content from <template>
   var template = document.querySelector('template').content;
 
   // Creates an object based in the HTML Element prototype
-  var MyElementProto = Object.create(HTMLElement.prototype);
+  class MyElement extends HTMLElement {
+    // Fires when an instance of the element is connected
+    connectedCallback() {
+      // Creates the shadow root
+      var shadowRoot = this.createShadowRoot();
 
-  // Fires when an instance of the element is created
-  MyElementProto.createdCallback = function() {
-    // Creates the shadow root
-    var shadowRoot = this.createShadowRoot();
-
-    // Adds a template clone into shadow root
-    var clone = document.importNode(template, true);
-    shadowRoot.appendChild(clone);
+      // Adds a template clone into shadow root
+      var clone = document.importNode(template, true);
+      shadowRoot.appendChild(clone);
+    }
   };
   // Registers <my-elem> in the main document
-  window.MyElement = document.registerElement('my-elem', {
-      prototype: MyElementProto
-  });
+  customElements.define('my-elem', MyElement);
 </script>
 </head>
 <body>
   <my-elem><div>ThisIsADivFlexItem</div></my-elem>
 </body>
 </html>
--- a/layout/style/crashtests/1017798-1.html
+++ b/layout/style/crashtests/1017798-1.html
@@ -45,49 +45,50 @@ gaia_switch/examples/index.html from the
 }(window));
 </script>
 <script>
 'use strict';
 /* global ComponentUtils */
 
 window.GaiaSwitch = (function(win) {
   // Extend from the HTMLElement prototype
-  var proto = Object.create(HTMLElement.prototype);
-
-  proto.createdCallback = function() {
-    var shadow = this.createShadowRoot();
-    this._template = template.content.cloneNode(true);
-    this._input = this._template.querySelector('input[type="checkbox"]');
+  class GaiaSwitch extends HTMLElement {
+    connectedCallback() {
+      var shadow = this.createShadowRoot();
+      this._template = template.content.cloneNode(true);
+      this._input = this._template.querySelector('input[type="checkbox"]');
 
-    var checked = this.getAttribute('checked');
-    if (checked !== null) {
-      this._input.checked = true;
-    }
+      var checked = this.getAttribute('checked');
+      if (checked !== null) {
+        this._input.checked = true;
+      }
 
-    shadow.appendChild(this._template);
+      shadow.appendChild(this._template);
 
-    ComponentUtils.style.call(this, '');
+      ComponentUtils.style.call(this, '');
+    }
   };
 
+
   /**
    * Proxy the checked property to the input element.
    */
-  Object.defineProperty( proto, 'checked', {
+  Object.defineProperty( GaiaSwitch.prototype, 'checked', {
     get: function() {
       return this._input.checked;
     },
     set: function(value) {
       this._input.checked = value;
     }
   });
 
   /**
    * Proxy the name property to the input element.
    */
-  Object.defineProperty( proto, 'name', {
+  Object.defineProperty( GaiaSwitch.prototype, 'name', {
     get: function() {
       return this.getAttribute('name');
     },
     set: function(value) {
       this.setAttribute('name', value);
     }
   });
 
deleted file mode 100644
--- a/testing/web-platform/meta/custom-elements/historical.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[historical.html]
-  [document.registerElement should not exist]
-    expected: FAIL
-