diff options
Diffstat (limited to 'parser/html/nsHtml5TreeOperation.cpp')
-rw-r--r-- | parser/html/nsHtml5TreeOperation.cpp | 250 |
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; } |