Bug 1276579: Add 'options' argument into |document.createElement[NS]()| methods and remove overloaded |createElement[NS]()| methods. r=smaug,wchen
authorJocelyn Liu <joliu@mozilla.com>
Mon, 06 Jun 2016 18:43:39 +0800
changeset 330921 fc1d44b2cb7b7d7c47a365bddfc23ff991505003
parent 330920 db0243f4762576ce4874d56bd3c92c56fcf78ab7
child 330922 29ead859749af91a4e70d10a278a0ca3fca9d2b4
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, wchen
bugs1276579
milestone50.0a1
Bug 1276579: Add 'options' argument into |document.createElement[NS]()| methods and remove overloaded |createElement[NS]()| methods. r=smaug,wchen 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
+