Bug 1378079 - Part 3: Complete the steps related to custom elements in "create an element for a token". r=hsivonen,smaug

This commit is contained in:
Jessica Jong
2017-10-26 14:55:41 +08:00
parent d5bb6a8632
commit d74deb91b0
21 changed files with 285 additions and 265 deletions

View File

@@ -74,6 +74,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)
{
@@ -327,6 +350,48 @@ nsHtml5TreeOperation::AddAttributes(nsIContent* aNode,
return NS_OK;
}
void
nsHtml5TreeOperation::SetHTMLElementAttributes(dom::Element* aElement,
nsAtom* aName,
nsHtml5HtmlAttributes* aAttributes)
{
int32_t len = aAttributes->getLength();
for (int32_t i = 0; i < len; i++) {
nsHtml5String val = aAttributes->getValueNoBoundsCheck(i);
nsAtom* klass = val.MaybeAsAtom();
if (klass) {
aElement->SetSingleClassFromParser(klass);
} else {
// prefix doesn't need regetting. it is always null or a static atom
// local name is never null
RefPtr<nsAtom> localName =
Reget(aAttributes->getLocalNameNoBoundsCheck(i));
RefPtr<nsAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
nsString value; // Not Auto, because using it to hold nsStringBuffer*
val.ToString(value);
if (nsGkAtoms::a == aName && nsGkAtoms::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(
nsAtom* aName,
@@ -346,109 +411,132 @@ nsHtml5TreeOperation::CreateHTMLElement(
aName, nullptr, kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE);
NS_ASSERTION(nodeInfo, "Got null nodeinfo.");
nsCOMPtr<dom::Element> newElement = aCreator(nodeInfo.forget(), aFromParser);
dom::Element* newContent = nullptr;
nsIDocument* document = nodeInfo->GetDocument();
bool willExecuteScript = false;
bool isCustomElement = false;
nsString isValue;
dom::CustomElementDefinition* definition = nullptr;
MOZ_ASSERT(newElement, "Element creation created null pointer.");
dom::Element* newContent = newElement;
aBuilder->HoldElement(newElement.forget());
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 (MOZ_UNLIKELY(aName == nsGkAtoms::style || aName == nsGkAtoms::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;
// 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);
}
}
nsTArray<nsString> theContent;
nsAutoString theAttribute;
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));
(void) theFormProcessor->ProvideContent(NS_LITERAL_STRING("select"),
theContent,
theAttribute);
newContent->SetAttr(kNameSpaceID_None,
nsGkAtoms::moztype,
nullptr,
theAttribute,
false);
RefPtr<dom::NodeInfo> optionNodeInfo = aNodeInfoManager->GetNodeInfo(
nsGkAtoms::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;
}
int32_t len = aAttributes->getLength();
for (int32_t i = 0; i < len; i++) {
nsHtml5String val = aAttributes->getValueNoBoundsCheck(i);
nsAtom* klass = val.MaybeAsAtom();
if (klass) {
newContent->SetSingleClassFromParser(klass);
} else {
// prefix doesn't need regetting. it is always null or a static atom
// local name is never null
RefPtr<nsAtom> localName =
Reget(aAttributes->getLocalNameNoBoundsCheck(i));
RefPtr<nsAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
nsString value; // Not Auto, because using it to hold nsStringBuffer*
val.ToString(value);
if (nsGkAtoms::a == aName && nsGkAtoms::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);
// 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);
}
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());
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 == nsGkAtoms::style || aName == nsGkAtoms::link)) {
nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent));
if (ssle) {
ssle->InitStyleLinkElement(false);
ssle->SetEnableUpdates(false);
}
}
if (!aAttributes) {
return newContent;
}
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 == nsGkAtoms::style || aName == nsGkAtoms::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(
nsGkAtoms::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;
}