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 390465 f66c3bd94cc5749ea6c019b756c68089f24cb5ad
parent 390029 d224fc999cb6accb208af0a105f14433375e2e77
child 526005 f2942627289f89e79409f9eb6b896e27cda9a664
push id23676
push userbmo:joliu@mozilla.com
push dateThu, 21 Jul 2016 06:31:31 +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
testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-interface-type-is-a-local-name.html.ini
testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-is-attribute.html.ini
testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-namespace.html.ini
testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/created-callback-create-element-ns.html.ini
testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/created-callback-create-element.html.ini
testing/web-platform/meta/custom-elements/v0/instantiating/unchanged-attribute.html.ini
--- 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
@@ -5585,73 +5585,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) {
@@ -5661,105 +5696,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;
 }
@@ -8779,17 +8770,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);
@@ -8806,17 +8797,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)
@@ -13492,8 +13483,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;
@@ -1327,24 +1328,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;
   }
@@ -1581,16 +1580,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;
 };
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-interface-type-is-a-local-name.html.ini
@@ -0,0 +1,6 @@
+[create-element-interface-type-is-a-local-name.html]
+  type: testharness
+  [Test Document.createElement() creates custom element of type, specified by localName argument, if an element definition with matching localName, namespace, and type is not registered]
+    expected: FAIL
+  [Test Document.createElementNS() creates custom element of type, specified by localName argument, if an element definition with matching localName, namespace, and type is not registered]
+    expected: FAIL
--- a/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-is-attribute.html.ini
+++ b/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-is-attribute.html.ini
@@ -1,8 +1,11 @@
 [create-element-is-attribute.html]
   type: testharness
   [Test Document.createElement() sets the element's IS attribute value to type, if type is not the same as localName]
     expected: FAIL
-
+  [Test Document.createElement() sets the element's IS attribute value to type, if type is not the same as localName and an element definition with matching localName, namespace, and type is not registered]
+    expected: FAIL
   [Test Document.createElementNS() sets the element's IS attribute value to type, if type is not the same as localName]
     expected: FAIL
+  [Test Document.createElementNS() sets the element's IS attribute value to type, if type is not the same as localNameand and an element definition with matching localName, namespace, and type is not registered ]
+    expected: FAIL
 
--- a/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-namespace.html.ini
+++ b/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-namespace.html.ini
@@ -1,5 +1,6 @@
 [create-element-namespace.html]
   type: testharness
   [Document.createElement() sets custom element namespace to HTML Namespace. Custom element is extending standard HTML tag]
     expected: FAIL
-
+  [Document.createElement() sets custom element namespace to HTML Namespace. Document.createElement() is called with standard HTML tag name and type without registered custom element of such type]
+    expected: FAIL
--- a/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/created-callback-create-element-ns.html.ini
+++ b/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/created-callback-create-element-ns.html.ini
@@ -1,5 +1,6 @@
 [created-callback-create-element-ns.html]
   type: testharness
   [Test Document.createElementNS() enqueues created callback for custom elements that are extensions of HTML5 elements]
     expected: FAIL
-
+  [Test Document.createElementNS() with typeExtension argument enqueues created callback]
+    expected: FAIL
--- a/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/created-callback-create-element.html.ini
+++ b/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/created-callback-create-element.html.ini
@@ -1,5 +1,6 @@
 [created-callback-create-element.html]
   type: testharness
   [Test Document.createElement() enqueues created callback for custom elements that are extensions of HTML5 elements]
     expected: FAIL
-
+  [Test Document.createElement() with typeExtension argument enqueues created callback]
+    expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/custom-elements/v0/instantiating/unchanged-attribute.html.ini
@@ -0,0 +1,5 @@
+[unchanged-attribute.html]
+  type: testharness
+  [After a custom element is instantiated, changing the value of the is attribute must not affect this element's custom element type.]
+    expected: FAIL
+