Bug 1276579: Add 'options' argument into |document.createElement[NS]()| methods and remove overloaded |createElement[NS]()| methods. r=wchen, r?smaug draft
authorJocelyn Liu <joliu@mozilla.com>
Mon, 06 Jun 2016 18:43:39 +0800
changeset 390085 c7b01fe03c3a0730d05f4c3a283be1dda2a9be9d
parent 386893 04821a70c739a00d12e12df651c0989441e22728
child 525930 872dbe09484c355ac49cb246e0531e173cb58acc
push id23596
push userbmo:joliu@mozilla.com
push dateWed, 20 Jul 2016 17:41:12 +0000
reviewerswchen, smaug
bugs1276579
milestone50.0a1
Bug 1276579: Add 'options' argument into |document.createElement[NS]()| methods and remove overloaded |createElement[NS]()| methods. r=wchen, r?smaug MozReview-Commit-ID: FWd6g33SSiR
dom/base/nsContentCreatorFunctions.h
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsIDocument.h
dom/base/nsNameSpaceManager.cpp
dom/html/nsHTMLContentSink.cpp
dom/tests/mochitest/webcomponents/test_custom_element_clone_callbacks_extended.html
dom/tests/mochitest/webcomponents/test_document_register.html
dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html
dom/tv/TVTuner.cpp
dom/webidl/Document.webidl
--- a/dom/base/nsContentCreatorFunctions.h
+++ b/dom/base/nsContentCreatorFunctions.h
@@ -25,26 +25,28 @@ namespace dom {
 class Element;
 class NodeInfo;
 } // namespace dom
 } // namespace mozilla
 
 nsresult
 NS_NewElement(mozilla::dom::Element** aResult,
               already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-              mozilla::dom::FromParser aFromParser);
+              mozilla::dom::FromParser aFromParser,
+              nsAString* aIs = nullptr);
 
 nsresult
 NS_NewXMLElement(mozilla::dom::Element** aResult,
                  already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 nsresult
 NS_NewHTMLElement(mozilla::dom::Element** aResult,
                   already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-                  mozilla::dom::FromParser aFromParser);
+                  mozilla::dom::FromParser aFromParser,
+                  nsAString* aIs = nullptr);
 
 // First argument should be nsHTMLTag, but that adds dependency to parser
 // for a bunch of files.
 already_AddRefed<nsGenericHTMLElement>
 CreateHTMLElement(uint32_t aNodeType,
                   already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                   mozilla::dom::FromParser aFromParser);
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5581,73 +5581,108 @@ nsDocument::GetDocumentElement(nsIDOMEle
 }
 
 NS_IMETHODIMP
 nsDocument::CreateElement(const nsAString& aTagName,
                           nsIDOMElement** aReturn)
 {
   *aReturn = nullptr;
   ErrorResult rv;
-  nsCOMPtr<Element> element = nsIDocument::CreateElement(aTagName, rv);
+  ElementCreationOptions options;
+  nsCOMPtr<Element> element = CreateElement(aTagName, options, rv);
   NS_ENSURE_FALSE(rv.Failed(), rv.StealNSResult());
   return CallQueryInterface(element, aReturn);
 }
 
 bool IsLowercaseASCII(const nsAString& aValue)
 {
   int32_t len = aValue.Length();
   for (int32_t i = 0; i < len; ++i) {
     char16_t c = aValue[i];
     if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
       return false;
     }
   }
   return true;
 }
 
+CustomElementDefinition*
+nsDocument::LookupCustomElementDefinition(const nsAString& aLocalName,
+                                          uint32_t aNameSpaceID,
+                                          const nsAString* aIs)
+{
+  if (!mRegistry || aNameSpaceID != kNameSpaceID_XHTML) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIAtom> localNameAtom = NS_Atomize(aLocalName);
+  nsCOMPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : localNameAtom;
+
+  CustomElementDefinition* data;
+  CustomElementHashKey key(aNameSpaceID, typeAtom);
+  if (mRegistry->mCustomDefinitions.Get(&key, &data) &&
+      data->mLocalName == localNameAtom) {
+    return data;
+  }
+
+  return nullptr;
+}
+
 already_AddRefed<Element>
-nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv)
+nsDocument::CreateElement(const nsAString& aTagName,
+                          const ElementCreationOptions& aOptions,
+                          ErrorResult& rv)
 {
   rv = nsContentUtils::CheckQName(aTagName, false);
   if (rv.Failed()) {
     return nullptr;
   }
 
   bool needsLowercase = IsHTMLDocument() && !IsLowercaseASCII(aTagName);
   nsAutoString lcTagName;
   if (needsLowercase) {
     nsContentUtils::ASCIIToLower(aTagName, lcTagName);
   }
 
-  return CreateElem(needsLowercase ? lcTagName : aTagName, nullptr,
-                    mDefaultElementType);
+  // Throw NotFoundError if 'is' is not-null and definition is null
+  nsString* is = CheckCustomElementName(
+    aOptions, needsLowercase ? lcTagName : aTagName, mDefaultElementType, rv);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+
+  RefPtr<Element> elem = CreateElem(
+    needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is);
+
+  return elem.forget();
 }
 
 void
 nsDocument::SetupCustomElement(Element* aElement,
                                uint32_t aNamespaceID,
                                const nsAString* aTypeExtension)
 {
-  if (!mRegistry) {
+  if (!mRegistry || aNamespaceID != kNameSpaceID_XHTML) {
     return;
   }
 
   nsCOMPtr<nsIAtom> tagAtom = aElement->NodeInfo()->NameAtom();
   nsCOMPtr<nsIAtom> typeAtom = aTypeExtension ?
     NS_Atomize(*aTypeExtension) : tagAtom;
 
   if (aTypeExtension && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
     // Custom element setup in the parser happens after the "is"
     // attribute is added.
     aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *aTypeExtension, true);
   }
 
-  CustomElementDefinition* data;
-  CustomElementHashKey key(aNamespaceID, typeAtom);
-  if (!mRegistry->mCustomDefinitions.Get(&key, &data)) {
+  CustomElementDefinition* data = LookupCustomElementDefinition(
+    aElement->NodeInfo()->LocalName(), aNamespaceID, aTypeExtension);
+
+  if (!data) {
     // The type extension doesn't exist in the registry,
     // thus we don't need to enqueue callback or adjust
     // the "is" attribute, but it is possibly an upgrade candidate.
     RegisterUnresolvedElement(aElement, typeAtom);
     return;
   }
 
   if (data->mLocalName != tagAtom) {
@@ -5657,105 +5692,61 @@ nsDocument::SetupCustomElement(Element* 
     return;
   }
 
   // Enqueuing the created callback will set the CustomElementData on the
   // element, causing prototype swizzling to occur in Element::WrapObject.
   EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
 }
 
-already_AddRefed<Element>
-nsDocument::CreateElement(const nsAString& aTagName,
-                          const nsAString& aTypeExtension,
-                          ErrorResult& rv)
-{
-  RefPtr<Element> elem = nsIDocument::CreateElement(aTagName, rv);
-  if (rv.Failed()) {
-    return nullptr;
-  }
-
-  if (!aTypeExtension.IsVoid() &&
-      !aTagName.Equals(aTypeExtension)) {
-    // do not process 'is' if it is null or the extended type is the same as
-    // the localName
-    SetupCustomElement(elem, GetDefaultNamespaceID(), &aTypeExtension);
-  }
-
-  return elem.forget();
-}
-
 NS_IMETHODIMP
 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
                             const nsAString& aQualifiedName,
                             nsIDOMElement** aReturn)
 {
   *aReturn = nullptr;
+  ElementCreationOptions options;
   ErrorResult rv;
   nsCOMPtr<Element> element =
-    nsIDocument::CreateElementNS(aNamespaceURI, aQualifiedName, rv);
+    CreateElementNS(aNamespaceURI, aQualifiedName, options, rv);
   NS_ENSURE_FALSE(rv.Failed(), rv.StealNSResult());
   return CallQueryInterface(element, aReturn);
 }
 
 already_AddRefed<Element>
-nsIDocument::CreateElementNS(const nsAString& aNamespaceURI,
-                             const nsAString& aQualifiedName,
-                             ErrorResult& rv)
+nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
+                            const nsAString& aQualifiedName,
+                            const ElementCreationOptions& aOptions,
+                            ErrorResult& rv)
 {
   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
   rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
                                             aQualifiedName,
                                             mNodeInfoManager,
                                             nsIDOMNode::ELEMENT_NODE,
                                             getter_AddRefs(nodeInfo));
   if (rv.Failed()) {
     return nullptr;
   }
 
-  nsCOMPtr<Element> element;
-  rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
-                     NOT_FROM_PARSER);
-  if (rv.Failed()) {
-    return nullptr;
-  }
-  return element.forget();
-}
-
-already_AddRefed<Element>
-nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
-                            const nsAString& aQualifiedName,
-                            const nsAString& aTypeExtension,
-                            ErrorResult& rv)
-{
-  RefPtr<Element> elem = nsIDocument::CreateElementNS(aNamespaceURI,
-                                                        aQualifiedName,
-                                                        rv);
+  // Throw NotFoundError if 'is' is not-null and definition is null
+  nsString* is = CheckCustomElementName(
+    aOptions, aQualifiedName, nodeInfo->NamespaceID(), rv);
   if (rv.Failed()) {
     return nullptr;
   }
 
-  if (aTypeExtension.IsVoid() ||
-      aQualifiedName.Equals(aTypeExtension)) {
-    // do not process 'is' if it is null or the extended type is the same as
-    // the localName
-    return elem.forget();
-  }
-
-  int32_t nameSpaceId = kNameSpaceID_Wildcard;
-  if (!aNamespaceURI.EqualsLiteral("*")) {
-    rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
-                                                               nameSpaceId);
-    if (rv.Failed()) {
-      return nullptr;
-    }
-  }
-
-  SetupCustomElement(elem, nameSpaceId, &aTypeExtension);
-
-  return elem.forget();
+  nsCOMPtr<Element> element;
+  rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
+                     NOT_FROM_PARSER, is);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+
+  return element.forget();
 }
 
 NS_IMETHODIMP
 nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
 {
   *aReturn = nsIDocument::CreateTextNode(aData).take();
   return NS_OK;
 }
@@ -8774,17 +8765,17 @@ nsDocument::RetrieveRelevantHeaders(nsIC
   mLastModified.Truncate();
   if (modDate != 0) {
     GetFormattedTimeString(modDate, mLastModified);
   }
 }
 
 already_AddRefed<Element>
 nsDocument::CreateElem(const nsAString& aName, nsIAtom *aPrefix,
-                       int32_t aNamespaceID)
+                       int32_t aNamespaceID, nsAString* aIs)
 {
 #ifdef DEBUG
   nsAutoString qName;
   if (aPrefix) {
     aPrefix->ToString(qName);
     qName.Append(':');
   }
   qName.Append(aName);
@@ -8801,17 +8792,17 @@ nsDocument::CreateElem(const nsAString& 
   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
   mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID,
                                 nsIDOMNode::ELEMENT_NODE,
                                 getter_AddRefs(nodeInfo));
   NS_ENSURE_TRUE(nodeInfo, nullptr);
 
   nsCOMPtr<Element> element;
   nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
-                              NOT_FROM_PARSER);
+                              NOT_FROM_PARSER, aIs);
   return NS_SUCCEEDED(rv) ? element.forget() : nullptr;
 }
 
 bool
 nsDocument::IsSafeToFlush() const
 {
   nsIPresShell* shell = GetShell();
   if (!shell)
@@ -13668,8 +13659,31 @@ nsIDocument::UpdateStyleBackendType()
     nsLayoutUtils::SupportsServoStyleBackend(this) &&
     mDocumentContainer ?
       StyleBackendType::Servo :
       StyleBackendType::Gecko;
 #else
   mStyleBackendType = StyleBackendType::Gecko;
 #endif
 }
+
+nsString*
+nsDocument::CheckCustomElementName(const ElementCreationOptions& aOptions,
+                                   const nsAString& aLocalName,
+                                   uint32_t aNamespaceID,
+                                   ErrorResult& rv)
+{
+  // only check aOptions if 'is' is passed and the webcomponents preference
+  // is enabled
+  if (!aOptions.mIs.WasPassed() ||
+      !Preferences::GetBool("dom.webcomponents.enabled")) {
+      return nullptr;
+  }
+
+  nsString* is = const_cast<nsString*>(&(aOptions.mIs.Value()));
+
+  // Throw NotFoundError if 'is' is not-null and definition is null
+  if (!LookupCustomElementDefinition(aLocalName, aNamespaceID, is)) {
+      rv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
+  }
+
+  return is;
+}
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -999,17 +999,18 @@ public:
   NS_DECL_NSIOBSERVER
 
   NS_DECL_NSIDOMXPATHEVALUATOR
 
   virtual nsresult Init();
 
   virtual already_AddRefed<Element> CreateElem(const nsAString& aName,
                                                nsIAtom* aPrefix,
-                                               int32_t aNamespaceID) override;
+                                               int32_t aNamespaceID,
+                                               nsAString* aIs = nullptr) override;
 
   virtual void Sanitize() override;
 
   virtual void EnumerateSubDocuments(nsSubDocEnumFunc aCallback,
                                                  void *aData) override;
 
   virtual bool CanSavePresentation(nsIRequest *aNewRequest) override;
   virtual void Destroy() override;
@@ -1329,24 +1330,22 @@ public:
                     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;
-  using nsIDocument::CreateElement;
-  using nsIDocument::CreateElementNS;
   virtual already_AddRefed<Element> CreateElement(const nsAString& aTagName,
-                                                  const nsAString& aTypeExtension,
-                                                  mozilla::ErrorResult& rv) override;
+                                                  const mozilla::dom::ElementCreationOptions& aOptions,
+                                                  ErrorResult& rv) override;
   virtual already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
                                                     const nsAString& aQualifiedName,
-                                                    const nsAString& aTypeExtension,
+                                                    const mozilla::dom::ElementCreationOptions& aOptions,
                                                     mozilla::ErrorResult& rv) override;
   virtual void UseRegistryFromDocument(nsIDocument* aDocument) override;
 
   virtual nsIDocument* MasterDocument() override
   {
     return mMasterDocument ? mMasterDocument.get()
                            : this;
   }
@@ -1583,16 +1582,37 @@ private:
   // element queues. Each queue is represented by a sequence of
   // CustomElementData in this array, separated by nullptr that
   // represent the boundaries of the items in the stack. The first
   // queue in the stack is the base element queue.
   static mozilla::Maybe<nsTArray<RefPtr<mozilla::dom::CustomElementData>>> sProcessingStack;
 
   static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
 
+  /**
+   * Looking up a custom element definition.
+   * https://html.spec.whatwg.org/#look-up-a-custom-element-definition
+   */
+  mozilla::dom::CustomElementDefinition* LookupCustomElementDefinition(
+    const nsAString& aLocalName, uint32_t aNameSpaceID, const nsAString* aIs);
+
+  /**
+   * Check if the passed custom element name, aOptions.mIs, is a registered
+   * custom element type or not, then return the custom element name for future
+   * usage.
+   *
+   * If there is no existing custom element definition for this name, throw a
+   * NotFoundError.
+   */
+  nsString* CheckCustomElementName(
+    const mozilla::dom::ElementCreationOptions& aOptions,
+    const nsAString& aLocalName,
+    uint32_t aNamespaceID,
+    ErrorResult& rv);
+
 public:
   // Enqueue created callback or register upgrade candidate for
   // newly created custom elements, possibly extending an existing type.
   // ex. <x-button>, <button is="x-button> (type extension)
   virtual void SetupCustomElement(Element* aElement,
                                   uint32_t aNamespaceID,
                                   const nsAString* aTypeExtension) override;
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -113,16 +113,17 @@ class CDATASection;
 class Comment;
 struct CustomElementDefinition;
 class DocumentFragment;
 class DocumentTimeline;
 class DocumentType;
 class DOMImplementation;
 class DOMStringList;
 class Element;
+struct ElementCreationOptions;
 struct ElementRegistrationOptions;
 class Event;
 class EventTarget;
 class FontFaceSet;
 class FrameRequestCallback;
 struct FullscreenRequest;
 class ImportManager;
 class HTMLBodyElement;
@@ -1503,17 +1504,18 @@ public:
   virtual bool IsScriptEnabled() = 0;
 
   /**
    * Create an element with the specified name, prefix and namespace ID.
    * Returns null if element name parsing failed.
    */
   virtual already_AddRefed<Element> CreateElem(const nsAString& aName,
                                                nsIAtom* aPrefix,
-                                               int32_t aNamespaceID) = 0;
+                                               int32_t aNamespaceID,
+                                               nsAString* aIs = nullptr) = 0;
 
   /**
    * Get the security info (i.e. SSL state etc) that the document got
    * from the channel/document that created the content of the
    * document.
    *
    * @see nsIChannel
    */
@@ -2513,28 +2515,25 @@ public:
   }
   already_AddRefed<nsContentList>
     GetElementsByTagNameNS(const nsAString& aNamespaceURI,
                            const nsAString& aLocalName,
                            mozilla::ErrorResult& aResult);
   already_AddRefed<nsContentList>
     GetElementsByClassName(const nsAString& aClasses);
   // GetElementById defined above
-  already_AddRefed<Element> CreateElement(const nsAString& aTagName,
-                                          mozilla::ErrorResult& rv);
-  already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
-                                            const nsAString& aQualifiedName,
-                                            mozilla::ErrorResult& rv);
-  virtual already_AddRefed<Element> CreateElement(const nsAString& aTagName,
-                                                  const nsAString& aTypeExtension,
-                                                  mozilla::ErrorResult& rv) = 0;
-  virtual already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
-                                                    const nsAString& aQualifiedName,
-                                                    const nsAString& aTypeExtension,
-                                                    mozilla::ErrorResult& rv) = 0;
+  virtual already_AddRefed<Element>
+    CreateElement(const nsAString& aTagName,
+                  const mozilla::dom::ElementCreationOptions& aOptions,
+                  mozilla::ErrorResult& rv) = 0;
+  virtual already_AddRefed<Element>
+    CreateElementNS(const nsAString& aNamespaceURI,
+                    const nsAString& aQualifiedName,
+                    const mozilla::dom::ElementCreationOptions& aOptions,
+                    mozilla::ErrorResult& rv) = 0;
   already_AddRefed<mozilla::dom::DocumentFragment>
     CreateDocumentFragment() const;
   already_AddRefed<nsTextNode> CreateTextNode(const nsAString& aData) const;
   already_AddRefed<mozilla::dom::Comment>
     CreateComment(const nsAString& aData) const;
   already_AddRefed<mozilla::dom::ProcessingInstruction>
     CreateProcessingInstruction(const nsAString& target, const nsAString& data,
                                 mozilla::ErrorResult& rv) const;
--- a/dom/base/nsNameSpaceManager.cpp
+++ b/dom/base/nsNameSpaceManager.cpp
@@ -134,22 +134,23 @@ nsNameSpaceManager::GetNameSpaceID(nsIAt
   }
 
   return kNameSpaceID_Unknown;
 }
 
 nsresult
 NS_NewElement(Element** aResult,
               already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-              FromParser aFromParser)
+              FromParser aFromParser,
+              nsAString* aIs)
 {
   RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
   int32_t ns = ni->NamespaceID();
   if (ns == kNameSpaceID_XHTML) {
-    return NS_NewHTMLElement(aResult, ni.forget(), aFromParser);
+    return NS_NewHTMLElement(aResult, ni.forget(), aFromParser, aIs);
   }
 #ifdef MOZ_XUL
   if (ns == kNameSpaceID_XUL) {
     return NS_NewXULElement(aResult, ni.forget());
   }
 #endif
   if (ns == kNameSpaceID_MathML) {
     return NS_NewMathMLElement(aResult, ni.forget());
--- a/dom/html/nsHTMLContentSink.cpp
+++ b/dom/html/nsHTMLContentSink.cpp
@@ -233,17 +233,17 @@ public:
 
   Node* mStack;
   int32_t mStackSize;
   int32_t mStackPos;
 };
 
 nsresult
 NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-                  FromParser aFromParser)
+                  FromParser aFromParser, nsAString* aIs)
 {
   *aResult = nullptr;
 
   RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
 
   nsIParserService* parserService = nsContentUtils::GetParserService();
   if (!parserService)
     return NS_ERROR_OUT_OF_MEMORY;
@@ -251,26 +251,27 @@ NS_NewHTMLElement(Element** aResult, alr
   nsIAtom *name = nodeInfo->NameAtom();
 
   NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML), 
                "Trying to HTML elements that don't have the XHTML namespace");
 
   // Per the Custom Element specification, unknown tags that are valid custom
   // element names should be HTMLElement instead of HTMLUnknownElement.
   int32_t tag = parserService->HTMLCaseSensitiveAtomTagToId(name);
-  if (tag == eHTMLTag_userdefined &&
-      nsContentUtils::IsCustomElementName(name)) {
+  if ((tag == eHTMLTag_userdefined &&
+      nsContentUtils::IsCustomElementName(name)) ||
+      aIs) {
     nsIDocument* doc = nodeInfo->GetDocument();
 
     NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
     if (!*aResult) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    doc->SetupCustomElement(*aResult, kNameSpaceID_XHTML);
+    doc->SetupCustomElement(*aResult, kNameSpaceID_XHTML, aIs);
 
     return NS_OK;
   }
 
   *aResult = CreateHTMLElement(tag,
                                nodeInfo.forget(), aFromParser).take();
   return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
--- a/dom/tests/mochitest/webcomponents/test_custom_element_clone_callbacks_extended.html
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_clone_callbacks_extended.html
@@ -9,48 +9,32 @@ https://bugzilla.mozilla.org/show_bug.cg
   <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=1081039">Bug 1081039</a>
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
-// Test to make sure created callback is called on clones that are upgraded and clones
-// created after registering the custom element.
-
-var callbackCalledOnUpgrade = false;
-var callbackCalledOnClone = false;
+// Test to make sure created callback is called on clones created after
+// registering the custom element.
 
-var foo = document.createElement("button", "x-foo");
-is(foo.getAttribute("is"), "x-foo");
-
-var fooClone = foo.cloneNode(true);
-
+var count = 0;
 var p = Object.create(HTMLButtonElement.prototype);
-p.createdCallback = function() {
+p.createdCallback = function() { // should be called by createElement and cloneNode
   is(this.__proto__, p, "Correct prototype should be set on custom elements.");
 
-  if (this == fooClone) {
-    // Callback called for the element created before registering the custom element.
-    // Should be called on element upgrade.
-    is(callbackCalledOnUpgrade, false, "Upgrade should only be called once per clone.");
-    callbackCalledOnUpgrade = true;
-  } else if (this != foo) {
-    // Callback called for the element created after registering the custom element.
-    is(callbackCalledOnClone, false, "Upgrade should only be called once per clone.");
-    callbackCalledOnClone = true;
-  }
-
-  if (callbackCalledOnUpgrade && callbackCalledOnClone) {
+  if (++count == 2) {
     SimpleTest.finish();
   }
 };
 
 document.registerElement("x-foo", { prototype: p, extends: "button" });
+var foo = document.createElement("button", {is: "x-foo"});
+is(foo.getAttribute("is"), "x-foo");
 
-var anotherFooClone = foo.cloneNode(true);
+var fooClone = foo.cloneNode(true);
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_document_register.html
+++ b/dom/tests/mochitest/webcomponents/test_document_register.html
@@ -111,60 +111,66 @@ function startTest() {
   testRegisterSimple("X-SVG-DUPE-ME", Object.create(HTMLElement.prototype), true);
   testRegisterSimple("x-svg-dupe-me", null, true);
   testRegisterExtend("x-svg-dupe-me", "span", Object.create(HTMLElement.prototype), true);
   testRegisterExtend("x-svg-dupe-me", "shape", Object.create(SVGElement.prototype), true);
 
   // document.createElement with extended type.
   var extendedProto = Object.create(HTMLButtonElement.prototype);
   var buttonConstructor = document.registerElement("x-extended-button", { prototype: extendedProto, extends: "button" });
-  var extendedButton = document.createElement("button", "x-extended-button");
+  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.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
 
   // document.createElementNS with different namespace than definition.
-  var svgButton = document.createElementNS("http://www.w3.org/2000/svg", "button", "x-extended-button");
-  isnot(svgButton.__proto__, extendedProto, "Definition for element is in html namespace, registration should not apply for SVG elements.");
+  try {
+    var svgButton = document.createElementNS("http://www.w3.org/2000/svg", "button", {is: "x-extended-button"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // document.createElementNS with no namespace.
-  var noNamespaceButton = document.createElementNS("", "button", "x-extended-button");
-  isnot(noNamespaceButton.__proto__, extendedProto, "Definition for element is in html namespace, registration should not apply for elements with no namespace.");
+  try {
+    var noNamespaceButton = document.createElementNS("", "button", {is: "x-extended-button"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // document.createElement with non-existant extended type.
-  var normalButton = document.createElement("button", "x-non-existant");
-  is(normalButton.__proto__, HTMLButtonElement.prototype, "When the extended type doesn't exist, prototype should not change.");
+  try {
+    var normalButton = document.createElement("button", {is: "x-non-existant"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // document.createElement with exteneded type that does not match with local name of element.
-  var normalDiv = document.createElement("div", "x-extended-button");
-  is(normalDiv.__proto__, HTMLDivElement.prototype, "Prototype should not change when local name of extended type defintion does not match.");
+  try {
+    var normalDiv = document.createElement("div", {is: "x-extended-button"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // Custom element constructor.
   var constructedButton = new buttonConstructor();
   is(constructedButton.tagName, "BUTTON", "Created element should have local name of BUTTON");
   is(constructedButton.__proto__, extendedProto, "Created element should have the prototype of the extended type.");
   is(constructedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
 
-  // document.createElementNS with extended type.
-  var svgExtendedProto = Object.create(SVGTextElement.prototype);
-  var svgConstructor = document.registerElement("x-extended-text", { prototype: svgExtendedProto, extends: "text"});
-  var extendedText = document.createElementNS("http://www.w3.org/2000/svg", "text", "x-extended-text");
-  is(extendedText.tagName, "text", "Created element should have a local name of |text|.");
-  is(extendedText.__proto__, svgExtendedProto, "Created element have the registered prototype.");
-  is(extendedText.getAttribute("is"), "x-extended-text", "The |is| attribute of the created element should be the extended type.");
-
   // document.createElement with different namespace than definition for extended element.
-  var htmlText = document.createElement("text", "x-extended-text");
-  isnot(htmlText.__proto__, svgExtendedProto, "Definition for element in SVG namespace should not apply to HTML elements.");
-
-  // Custom element constructor for a SVG element.
-  var constructedText = new svgConstructor();
-  is(constructedText.tagName, "text", "Created element should have a local name of |text|.");
-  is(constructedText.__proto__, svgExtendedProto, "Created element have the registered prototype.");
-  is(constructedText.getAttribute("is"), "x-extended-text", "The |is| attribute of the created element should be the extended type.");
+  try {
+    var htmlText = document.createElement("text", {is: "x-extended-text"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // Try creating an element with a custom element name, but not in the html namespace.
   var htmlNamespaceProto = Object.create(HTMLElement.prototype);
   document.registerElement("x-in-html-namespace", { prototype: htmlNamespaceProto });
   var wrongNamespaceElem = document.createElementNS("http://www.w3.org/2000/svg", "x-in-html-namespace");
   isnot(wrongNamespaceElem.__proto__, htmlNamespaceProto, "Definition for element in html namespace should not apply to SVG elements.");
 }
 
--- a/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html
+++ b/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html
@@ -295,17 +295,17 @@ function testAttributeChangedExtended() 
   p.attributeChangedCallback = function(name, oldValue, newValue) {
     is(callbackCalled, false, "Callback should only be called once in this test.");
     callbackCalled = true;
     runNextTest();
   };
 
   document.registerElement("x-extended-attribute-change", { prototype: p, extends: "button" });
 
-  var elem = document.createElement("button", "x-extended-attribute-change");
+  var elem = document.createElement("button", {is: "x-extended-attribute-change"});
   elem.setAttribute("foo", "bar");
 }
 
 // Creates a custom element that is an upgrade candidate (no registration)
 // and mutate the element in ways that would call callbacks for registered
 // elements.
 function testUpgradeCandidate() {
   var createdElement = document.createElement("x-upgrade-candidate");
--- a/dom/tv/TVTuner.cpp
+++ b/dom/tv/TVTuner.cpp
@@ -230,17 +230,18 @@ TVTuner::CreateSimulatedMediaStream()
   }
 
   nsIDocument* doc = domWin->GetExtantDoc();
   if (NS_WARN_IF(!doc)) {
     return nullptr;
   }
 
   ErrorResult error;
-  RefPtr<Element> element = doc->CreateElement(VIDEO_TAG, error);
+  ElementCreationOptions options;
+  RefPtr<Element> element = doc->CreateElement(VIDEO_TAG, options, error);
   if (NS_WARN_IF(error.Failed())) {
     return nullptr;
   }
 
   nsCOMPtr<nsIContent> content(do_QueryInterface(element));
   if (NS_WARN_IF(!content)) {
     return nullptr;
   }
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -9,16 +9,21 @@
 interface WindowProxy;
 interface nsISupports;
 interface URI;
 interface nsIDocShell;
 interface nsILoadGroup;
 
 enum VisibilityState { "hidden", "visible", "prerender" };
 
+/* https://dom.spec.whatwg.org/#dictdef-elementcreationoptions */
+dictionary ElementCreationOptions {
+  DOMString is;
+};
+
 /* http://dom.spec.whatwg.org/#interface-document */
 [Constructor]
 interface Document : Node {
   [Throws]
   readonly attribute DOMImplementation implementation;
   [Pure]
   readonly attribute DOMString URL;
   [Pure]
@@ -43,19 +48,19 @@ interface Document : Node {
   [Pure, Throws]
   HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
   [Pure]
   HTMLCollection getElementsByClassName(DOMString classNames);
   [Pure]
   Element? getElementById(DOMString elementId);
 
   [NewObject, Throws]
-  Element createElement(DOMString localName);
+  Element createElement(DOMString localName, optional ElementCreationOptions options);
   [NewObject, Throws]
-  Element createElementNS(DOMString? namespace, DOMString qualifiedName);
+  Element createElementNS(DOMString? namespace, DOMString qualifiedName, optional ElementCreationOptions options);
   [NewObject]
   DocumentFragment createDocumentFragment();
   [NewObject]
   Text createTextNode(DOMString data);
   [NewObject]
   Comment createComment(DOMString data);
   [NewObject, Throws]
   ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data);
@@ -255,24 +260,16 @@ partial interface Document {
 };
 
 //http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
 partial interface Document {
     [Throws, Func="nsDocument::IsWebComponentsEnabled"]
     object registerElement(DOMString name, optional ElementRegistrationOptions options);
 };
 
-//http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
-partial interface Document {
-    [NewObject, Throws]
-    Element createElement(DOMString localName, DOMString? typeExtension);
-    [NewObject, Throws]
-    Element createElementNS(DOMString? namespace, DOMString qualifiedName, DOMString? typeExtension);
-};
-
 // http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html#sec-document-interface
 partial interface Document {
   readonly attribute boolean hidden;
   readonly attribute boolean mozHidden;
   readonly attribute VisibilityState visibilityState;
   readonly attribute VisibilityState mozVisibilityState;
 };