summaryrefslogtreecommitdiff
path: root/parser/html/nsHtml5TreeOperation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'parser/html/nsHtml5TreeOperation.cpp')
-rw-r--r--parser/html/nsHtml5TreeOperation.cpp250
1 files changed, 168 insertions, 82 deletions
diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp
index 0c5aad566e..a7d3da2595 100644
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -24,6 +24,7 @@
#include "nsIFormControl.h"
#include "nsIStyleSheetLinkingElement.h"
#include "nsIDOMDocumentType.h"
+#include "DocGroup.h"
#include "nsIObserverService.h"
#include "mozilla/Services.h"
#include "nsIMutationObserver.h"
@@ -74,6 +75,29 @@ class MOZ_STACK_CLASS nsHtml5OtherDocUpdate {
nsCOMPtr<nsIDocument> mDocument;
};
+/**
+ * Helper class to temporary break out of the document update batch. Use this
+ * with caution as this will cause blocked scripts to run.
+ */
+class MOZ_RAII mozAutoPauseContentUpdate final
+{
+public:
+ explicit mozAutoPauseContentUpdate(nsIDocument* aDocument)
+ : mDocument(aDocument)
+ {
+ MOZ_ASSERT(mDocument);
+ mDocument->EndUpdate(UPDATE_CONTENT_MODEL);
+ }
+
+ ~mozAutoPauseContentUpdate()
+ {
+ mDocument->BeginUpdate(UPDATE_CONTENT_MODEL);
+ }
+
+private:
+ nsCOMPtr<nsIDocument> mDocument;
+};
+
nsHtml5TreeOperation::nsHtml5TreeOperation()
: mOpCode(eTreeOpUninitialized)
{
@@ -332,6 +356,41 @@ nsHtml5TreeOperation::AddAttributes(nsIContent* aNode,
return NS_OK;
}
+void
+nsHtml5TreeOperation::SetHTMLElementAttributes(dom::Element* aElement,
+ nsIAtom* aName,
+ nsHtml5HtmlAttributes* aAttributes)
+{
+ int32_t len = aAttributes->getLength();
+ for (int32_t i = 0; i < len; i++) {
+ // prefix doesn't need regetting. it is always null or a static atom
+ // local name is never null
+ nsCOMPtr<nsIAtom> localName =
+ Reget(aAttributes->getLocalNameNoBoundsCheck(i));
+ nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
+ int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
+
+ nsString value; // Not Auto, because using it to hold nsStringBuffer*
+ aAttributes->getValueNoBoundsCheck(i).ToString(value);
+ if (nsHtml5Atoms::a == aName && nsHtml5Atoms::name == localName) {
+ // This is an HTML5-incompliant Geckoism.
+ // Remove when fixing bug 582361
+ NS_ConvertUTF16toUTF8 cname(value);
+ NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
+ aElement->SetAttr(nsuri,
+ localName,
+ prefix,
+ uv,
+ false);
+ } else {
+ aElement->SetAttr(nsuri,
+ localName,
+ prefix,
+ value,
+ false);
+ }
+ }
+ }
nsIContent*
nsHtml5TreeOperation::CreateHTMLElement(
@@ -351,106 +410,133 @@ nsHtml5TreeOperation::CreateHTMLElement(
RefPtr<dom::NodeInfo> nodeInfo = aNodeInfoManager->GetNodeInfo(
aName, nullptr, kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE);
NS_ASSERTION(nodeInfo, "Got null nodeinfo.");
- nsCOMPtr<dom::Element> newElement = aCreator(nodeInfo.forget(), aFromParser);
- MOZ_ASSERT(newElement, "Element creation created null pointer.");
+ dom::Element* newContent = nullptr;
+ nsIDocument* document = nodeInfo->GetDocument();
+ bool willExecuteScript = false;
+ bool isCustomElement = false;
+ nsString isValue;
+ dom::CustomElementDefinition* definition = nullptr;
+
+ // Avoid overhead by checking if custom elements pref is enabled or not.
+ if (nsContentUtils::IsCustomElementsEnabled()) {
+ if (aAttributes) {
+ nsHtml5String is = aAttributes->getValue(nsHtml5AttributeName::ATTR_IS);
+ if (is) {
+ is.ToString(isValue);
+ }
+ }
- dom::Element* newContent = newElement;
- aBuilder->HoldElement(newElement.forget());
+ isCustomElement = (aCreator == NS_NewCustomElement || !isValue.IsEmpty());
+ if (isCustomElement && aFromParser != dom::FROM_PARSER_FRAGMENT) {
+ definition = nsContentUtils::LookupCustomElementDefinition(document,
+ nodeInfo->LocalName(), nodeInfo->NamespaceID(),
+ (isValue.IsEmpty() ? nullptr : &isValue));
- if (aCreator == NS_NewCustomElement) {
- // Not inlining the call below into NS_NewCustomElement itself, because
- // in the near future, the code here will need to break out of an update
- // batch here.
- nsContentUtils::SetupCustomElement(newContent);
+ if (definition) {
+ willExecuteScript = true;
+ }
+ }
}
- if (MOZ_UNLIKELY(aName == nsHtml5Atoms::style || aName == nsHtml5Atoms::link)) {
- nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent));
- if (ssle) {
- ssle->InitStyleLinkElement(false);
- ssle->SetEnableUpdates(false);
+ if (willExecuteScript) { // This will cause custom element constructors to run
+ AutoSetThrowOnDynamicMarkupInsertionCounter
+ throwOnDynamicMarkupInsertionCounter(document);
+ mozAutoPauseContentUpdate autoPauseContentUpdate(document);
+ {
+ nsAutoMicroTask mt;
}
- } else if (MOZ_UNLIKELY(isKeygen)) {
- // Adapted from CNavDTD
- nsresult rv;
- nsCOMPtr<nsIFormProcessor> theFormProcessor =
- do_GetService(kFormProcessorCID, &rv);
- if (NS_FAILED(rv)) {
+ dom::AutoCEReaction
+ autoCEReaction(document->GetDocGroup()->CustomElementReactionsStack());
+
+ nsCOMPtr<dom::Element> newElement;
+ NS_NewHTMLElement(getter_AddRefs(newElement), nodeInfo.forget(),
+ aFromParser, (isValue.IsEmpty() ? nullptr : &isValue),
+ definition);
+
+ MOZ_ASSERT(newElement, "Element creation created null pointer.");
+ newContent = newElement;
+ aBuilder->HoldElement(newElement.forget());
+
+ if (MOZ_UNLIKELY(aName == nsHtml5Atoms::style || aName == nsHtml5Atoms::link)) {
+ nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent));
+ if (ssle) {
+ ssle->InitStyleLinkElement(false);
+ ssle->SetEnableUpdates(false);
+ }
+ }
+
+ if (!aAttributes) {
return newContent;
}
- nsTArray<nsString> theContent;
- nsAutoString theAttribute;
-
- (void) theFormProcessor->ProvideContent(NS_LITERAL_STRING("select"),
- theContent,
- theAttribute);
-
- newContent->SetAttr(kNameSpaceID_None,
- nsGkAtoms::moztype,
- nullptr,
- theAttribute,
- false);
-
- RefPtr<dom::NodeInfo> optionNodeInfo =
- aNodeInfoManager->GetNodeInfo(nsHtml5Atoms::option,
- nullptr,
- kNameSpaceID_XHTML,
- nsIDOMNode::ELEMENT_NODE);
-
- for (uint32_t i = 0; i < theContent.Length(); ++i) {
- RefPtr<dom::NodeInfo> ni = optionNodeInfo;
- nsCOMPtr<dom::Element> optionElt =
- NS_NewHTMLOptionElement(ni.forget(), aFromParser);
- RefPtr<nsTextNode> optionText = new nsTextNode(aNodeInfoManager);
- (void) optionText->SetText(theContent[i], false);
- optionElt->AppendChildTo(optionText, false);
- newContent->AppendChildTo(optionElt, false);
+ SetHTMLElementAttributes(newContent, aName, aAttributes);
+ } else {
+ nsCOMPtr<dom::Element> newElement;
+
+ if (isCustomElement) {
+ NS_NewHTMLElement(getter_AddRefs(newElement), nodeInfo.forget(),
+ aFromParser, (isValue.IsEmpty() ? nullptr : &isValue),
+ definition);
+ } else {
+ newElement = aCreator(nodeInfo.forget(), aFromParser);
}
- newContent->DoneAddingChildren(false);
- }
- if (!aAttributes) {
- return newContent;
- }
+ MOZ_ASSERT(newElement, "Element creation created null pointer.");
- int32_t len = aAttributes->getLength();
- for (int32_t i = 0; i < len; i++) {
- // prefix doesn't need regetting. it is always null or a static atom
- // local name is never null
- nsCOMPtr<nsIAtom> localName =
- Reget(aAttributes->getLocalNameNoBoundsCheck(i));
- nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
- int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
+ newContent = newElement;
+ aBuilder->HoldElement(newElement.forget());
- nsString value; // Not Auto, because using it to hold nsStringBuffer*
- aAttributes->getValueNoBoundsCheck(i).ToString(value);
- if (nsHtml5Atoms::a == aName && nsHtml5Atoms::name == localName) {
- // This is an HTML5-incompliant Geckoism.
- // Remove when fixing bug 582361
- NS_ConvertUTF16toUTF8 cname(value);
- NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
- newContent->SetAttr(nsuri,
- localName,
- prefix,
- uv,
- false);
- } else {
- newContent->SetAttr(nsuri,
- localName,
- prefix,
- value,
+ if (MOZ_UNLIKELY(aName == nsHtml5Atoms::style || aName == nsHtml5Atoms::link)) {
+ nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent));
+ if (ssle) {
+ ssle->InitStyleLinkElement(false);
+ ssle->SetEnableUpdates(false);
+ }
+ } else if (MOZ_UNLIKELY(isKeygen)) {
+ // Adapted from CNavDTD
+ nsresult rv;
+ nsCOMPtr<nsIFormProcessor> theFormProcessor =
+ do_GetService(kFormProcessorCID, &rv);
+ if (NS_FAILED(rv)) {
+ return newContent;
+ }
+
+ nsTArray<nsString> theContent;
+ nsAutoString theAttribute;
+
+ (void) theFormProcessor->ProvideContent(NS_LITERAL_STRING("select"),
+ theContent,
+ theAttribute);
+
+ newContent->SetAttr(kNameSpaceID_None,
+ nsGkAtoms::moztype,
+ nullptr,
+ theAttribute,
false);
- // Custom element setup may be needed if there is an "is" attribute.
- if (nsContentUtils::IsWebComponentsEnabled() &&
- kNameSpaceID_None == nsuri &&
- !prefix && nsGkAtoms::is == localName) {
- nsContentUtils::SetupCustomElement(newContent, &value);
+ RefPtr<dom::NodeInfo> optionNodeInfo = aNodeInfoManager->GetNodeInfo(
+ nsHtml5Atoms::option, nullptr, kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE);
+
+ for (uint32_t i = 0; i < theContent.Length(); ++i) {
+ RefPtr<dom::NodeInfo> ni = optionNodeInfo;
+ nsCOMPtr<dom::Element> optionElt =
+ NS_NewHTMLOptionElement(ni.forget(), aFromParser);
+ RefPtr<nsTextNode> optionText = new nsTextNode(aNodeInfoManager);
+ (void) optionText->SetText(theContent[i], false);
+ optionElt->AppendChildTo(optionText, false);
+ newContent->AppendChildTo(optionElt, false);
}
+ newContent->DoneAddingChildren(false);
}
+
+ if (!aAttributes) {
+ return newContent;
+ }
+
+ SetHTMLElementAttributes(newContent, aName, aAttributes);
}
+
return newContent;
}