diff options
Diffstat (limited to 'parser/html/nsHtml5TreeOperation.cpp')
-rw-r--r-- | parser/html/nsHtml5TreeOperation.cpp | 411 |
1 files changed, 318 insertions, 93 deletions
diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp index 3877e01b8..22c805859 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) { @@ -88,8 +112,11 @@ nsHtml5TreeOperation::~nsHtml5TreeOperation() case eTreeOpAddAttributes: delete mTwo.attributes; break; - case eTreeOpCreateElementNetwork: - case eTreeOpCreateElementNotNetwork: + case eTreeOpCreateHTMLElementNetwork: + case eTreeOpCreateHTMLElementNotNetwork: + case eTreeOpCreateSVGElementNetwork: + case eTreeOpCreateSVGElementNotNetwork: + case eTreeOpCreateMathMLElement: delete mThree.attributes; break; case eTreeOpAppendDoctypeToDocument: @@ -329,79 +356,222 @@ 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::CreateElement(int32_t aNs, - nsIAtom* aName, - nsHtml5HtmlAttributes* aAttributes, - mozilla::dom::FromParser aFromParser, - nsNodeInfoManager* aNodeInfoManager, - nsHtml5DocumentBuilder* aBuilder) +nsHtml5TreeOperation::CreateHTMLElement( + nsIAtom* aName, + nsHtml5HtmlAttributes* aAttributes, + mozilla::dom::FromParser aFromParser, + nsNodeInfoManager* aNodeInfoManager, + nsHtml5DocumentBuilder* aBuilder, + mozilla::dom::HTMLContentCreatorFunction aCreator) { - bool isKeygen = (aName == nsHtml5Atoms::keygen && aNs == kNameSpaceID_XHTML); + bool isKeygen = (aName == nsHtml5Atoms::keygen); if (MOZ_UNLIKELY(isKeygen)) { aName = nsHtml5Atoms::select; + aCreator = NS_NewHTMLSelectElement; } - nsCOMPtr<dom::Element> newElement; - RefPtr<dom::NodeInfo> nodeInfo = aNodeInfoManager-> - GetNodeInfo(aName, nullptr, aNs, nsIDOMNode::ELEMENT_NODE); + RefPtr<dom::NodeInfo> nodeInfo = aNodeInfoManager->GetNodeInfo( + aName, nullptr, kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE); NS_ASSERTION(nodeInfo, "Got null nodeinfo."); - NS_NewElement(getter_AddRefs(newElement), - nodeInfo.forget(), - aFromParser); - NS_ASSERTION(newElement, "Element creation created null pointer."); - dom::Element* newContent = newElement; - aBuilder->HoldElement(newElement.forget()); + 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); + } + } - if (MOZ_UNLIKELY(aName == nsHtml5Atoms::style || aName == nsHtml5Atoms::link)) { - nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent)); - if (ssle) { - ssle->InitStyleLinkElement(false); - ssle->SetEnableUpdates(false); + isCustomElement = (aCreator == NS_NewCustomElement || !isValue.IsEmpty()); + if (isCustomElement && aFromParser != dom::FROM_PARSER_FRAGMENT) { + RefPtr<nsIAtom> tagAtom = nodeInfo->NameAtom(); + RefPtr<nsIAtom> typeAtom = + isValue.IsEmpty() ? tagAtom : NS_Atomize(isValue); + + MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName())); + definition = nsContentUtils::LookupCustomElementDefinition(document, + nodeInfo->NameAtom(), nodeInfo->NamespaceID(), typeAtom); + + if (definition) { + willExecuteScript = true; + } + } + } + + if (willExecuteScript) { // This will cause custom element constructors to run + AutoSetThrowOnDynamicMarkupInsertionCounter + throwOnDynamicMarkupInsertionCounter(document); + mozAutoPauseContentUpdate autoPauseContentUpdate(document); + { + nsAutoMicroTask mt; + } + dom::AutoCEReaction + autoCEReaction(document->GetDocGroup()->CustomElementReactionsStack(), + nullptr); + + 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); + } } - } else if (MOZ_UNLIKELY(isKeygen)) { - // Adapted from CNavDTD - nsresult rv; - nsCOMPtr<nsIFormProcessor> theFormProcessor = - do_GetService(kFormProcessorCID, &rv); - if (NS_FAILED(rv)) { + + 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) { - nsCOMPtr<dom::Element> optionElt; - RefPtr<dom::NodeInfo> ni = optionNodeInfo; - NS_NewElement(getter_AddRefs(optionElt), - ni.forget(), - aFromParser); - RefPtr<nsTextNode> optionText = new nsTextNode(aNodeInfoManager); - (void) optionText->SetText(theContent[i], false); - optionElt->AppendChildTo(optionText, false); - newContent->AppendChildTo(optionElt, false); - // XXXsmaug Shouldn't we call this after adding all the child nodes. + 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); + } + + 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); + } + } 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); + + 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; +} + +nsIContent* +nsHtml5TreeOperation::CreateSVGElement( + nsIAtom* aName, + nsHtml5HtmlAttributes* aAttributes, + mozilla::dom::FromParser aFromParser, + nsNodeInfoManager* aNodeInfoManager, + nsHtml5DocumentBuilder* aBuilder, + mozilla::dom::SVGContentCreatorFunction aCreator) +{ + nsCOMPtr<nsIContent> newElement; + RefPtr<dom::NodeInfo> nodeInfo = aNodeInfoManager->GetNodeInfo( + aName, nullptr, kNameSpaceID_SVG, nsIDOMNode::ELEMENT_NODE); + MOZ_ASSERT(nodeInfo, "Got null nodeinfo."); + + mozilla::DebugOnly<nsresult> rv = + aCreator(getter_AddRefs(newElement), nodeInfo.forget(), aFromParser); + MOZ_ASSERT(NS_SUCCEEDED(rv) && newElement); + + dom::Element* newContent = newElement->AsElement(); + aBuilder->HoldElement(newElement.forget()); + + if (MOZ_UNLIKELY(aName == nsHtml5Atoms::style)) { + nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent)); + if (ssle) { + ssle->InitStyleLinkElement(false); + ssle->SetEnableUpdates(false); + } } if (!aAttributes) { @@ -419,30 +589,45 @@ nsHtml5TreeOperation::CreateElement(int32_t aNs, nsString value; // Not Auto, because using it to hold nsStringBuffer* aAttributes->getValueNoBoundsCheck(i).ToString(value); - if (aNs == kNameSpaceID_XHTML && - 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, - false); + newContent->SetAttr(nsuri, localName, prefix, value, false); + } + return newContent; +} - // Custom element setup may be needed if there is an "is" attribute. - if (kNameSpaceID_None == nsuri && !prefix && nsGkAtoms::is == localName) { - nsContentUtils::SetupCustomElement(newContent, &value); - } - } +nsIContent* +nsHtml5TreeOperation::CreateMathMLElement(nsIAtom* aName, + nsHtml5HtmlAttributes* aAttributes, + nsNodeInfoManager* aNodeInfoManager, + nsHtml5DocumentBuilder* aBuilder) +{ + nsCOMPtr<dom::Element> newElement; + RefPtr<dom::NodeInfo> nodeInfo = aNodeInfoManager->GetNodeInfo( + aName, nullptr, kNameSpaceID_MathML, nsIDOMNode::ELEMENT_NODE); + NS_ASSERTION(nodeInfo, "Got null nodeinfo."); + + mozilla::DebugOnly<nsresult> rv = + NS_NewMathMLElement(getter_AddRefs(newElement), nodeInfo.forget()); + MOZ_ASSERT(NS_SUCCEEDED(rv) && newElement); + + dom::Element* newContent = newElement; + aBuilder->HoldElement(newElement.forget()); + + if (!aAttributes) { + return newContent; + } + + 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); + newContent->SetAttr(nsuri, localName, prefix, value, false); } return newContent; } @@ -673,10 +858,56 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder, aBuilder->SetDocumentMode(mOne.mode); return NS_OK; } - case eTreeOpCreateElementNetwork: - case eTreeOpCreateElementNotNetwork: { + case eTreeOpCreateHTMLElementNetwork: + case eTreeOpCreateHTMLElementNotNetwork: { + nsIContent** target = mOne.node; + mozilla::dom::HTMLContentCreatorFunction creator = mFour.htmlCreator; + nsCOMPtr<nsIAtom> name = Reget(mTwo.atom); + nsHtml5HtmlAttributes* attributes = mThree.attributes; + nsIContent* intendedParent = mFive.node ? *(mFive.node) : nullptr; + + // intendedParent == nullptr is a special case where the + // intended parent is the document. + nsNodeInfoManager* nodeInfoManager = + intendedParent ? intendedParent->OwnerDoc()->NodeInfoManager() + : aBuilder->GetNodeInfoManager(); + + *target = CreateHTMLElement(name, + attributes, + mOpCode == eTreeOpCreateHTMLElementNetwork + ? dom::FROM_PARSER_NETWORK + : dom::FROM_PARSER_DOCUMENT_WRITE, + nodeInfoManager, + aBuilder, + creator); + return NS_OK; + } + case eTreeOpCreateSVGElementNetwork: + case eTreeOpCreateSVGElementNotNetwork: { + nsIContent** target = mOne.node; + mozilla::dom::SVGContentCreatorFunction creator = mFour.svgCreator; + nsCOMPtr<nsIAtom> name = Reget(mTwo.atom); + nsHtml5HtmlAttributes* attributes = mThree.attributes; + nsIContent* intendedParent = mFive.node ? *(mFive.node) : nullptr; + + // intendedParent == nullptr is a special case where the + // intended parent is the document. + nsNodeInfoManager* nodeInfoManager = + intendedParent ? intendedParent->OwnerDoc()->NodeInfoManager() + : aBuilder->GetNodeInfoManager(); + + *target = CreateSVGElement(name, + attributes, + mOpCode == eTreeOpCreateSVGElementNetwork + ? dom::FROM_PARSER_NETWORK + : dom::FROM_PARSER_DOCUMENT_WRITE, + nodeInfoManager, + aBuilder, + creator); + return NS_OK; + } + case eTreeOpCreateMathMLElement: { nsIContent** target = mOne.node; - int32_t ns = mFour.integer; nsCOMPtr<nsIAtom> name = Reget(mTwo.atom); nsHtml5HtmlAttributes* attributes = mThree.attributes; nsIContent* intendedParent = mFive.node ? *(mFive.node) : nullptr; @@ -687,14 +918,8 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder, intendedParent->OwnerDoc()->NodeInfoManager() : aBuilder->GetNodeInfoManager(); - *target = CreateElement(ns, - name, - attributes, - mOpCode == eTreeOpCreateElementNetwork ? - dom::FROM_PARSER_NETWORK : - dom::FROM_PARSER_DOCUMENT_WRITE, - nodeInfoManager, - aBuilder); + *target = + CreateMathMLElement(name, attributes, nodeInfoManager, aBuilder); return NS_OK; } case eTreeOpSetFormElement: { |