Bug 1712140 - Part 3: Add support for parsing and building Declarative ShadowDOMs. r=dom-core,webidl,saschanaz,hsivonen

Differential Revision: https://phabricator.services.mozilla.com/D193675
This commit is contained in:
Adam Vandolder 2023-11-30 23:17:15 +00:00
parent c52d383fb0
commit 8c1befaa95
21 changed files with 424 additions and 23 deletions

View File

@ -1392,6 +1392,7 @@ Document::Document(const char* aContentType)
mHasUserInteractionTimerScheduled(false),
mShouldResistFingerprinting(false),
mCloningForSVGUse(false),
mAllowDeclarativeShadowRoots(false),
mXMLDeclarationBits(0),
mOnloadBlockCount(0),
mWriteLevel(0),
@ -18931,4 +18932,13 @@ RadioGroupContainer& Document::OwnedRadioGroupContainer() {
return *mRadioGroupContainer;
}
void Document::SetAllowDeclarativeShadowRoots(
bool aAllowDeclarativeShadowRoots) {
mAllowDeclarativeShadowRoots = aAllowDeclarativeShadowRoots;
}
bool Document::AllowsDeclarativeShadowRoots() const {
return mAllowDeclarativeShadowRoots;
}
} // namespace mozilla::dom

View File

@ -3858,6 +3858,9 @@ class Document : public nsINode,
*/
bool AllowsL10n() const;
void SetAllowDeclarativeShadowRoots(bool aAllowDeclarativeShadowRoots);
bool AllowsDeclarativeShadowRoots() const;
protected:
RefPtr<DocumentL10n> mDocumentL10n;
@ -4820,6 +4823,8 @@ class Document : public nsINode,
// Whether we're cloning the contents of an SVG use element.
bool mCloningForSVGUse : 1;
bool mAllowDeclarativeShadowRoots : 1;
// The fingerprinting protections overrides for this document. The value will
// override the default enabled fingerprinting protections for this document.
// This will only get populated if these is one that comes from the local

View File

@ -239,6 +239,9 @@ class ShadowRoot final : public DocumentFragment, public DocumentOrShadowRoot {
void SetIsDeclarative(Declarative aIsDeclarative) {
mIsDeclarative = aIsDeclarative;
}
void SetIsDeclarative(bool aIsDeclarative) {
mIsDeclarative = aIsDeclarative ? Declarative::Yes : Declarative::No;
}
bool IsClonable() const { return mIsClonable == Clonable::Yes; }

View File

@ -5376,6 +5376,34 @@ bool AllowsUnsanitizedContentForAboutNewTab(nsIPrincipal* aPrincipal) {
return aboutModuleFlags & nsIAboutModule::ALLOW_UNSANITIZED_CONTENT;
}
/* static */
void nsContentUtils::SetHTMLUnsafe(FragmentOrElement* aTarget,
Element* aContext,
const nsAString& aSource) {
MOZ_ASSERT(!sFragmentParsingActive, "Re-entrant fragment parsing attempted.");
mozilla::AutoRestore<bool> guard(sFragmentParsingActive);
sFragmentParsingActive = true;
if (!sHTMLFragmentParser) {
NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
// Now sHTMLFragmentParser owns the object
}
nsAtom* contextLocalName = aContext->NodeInfo()->NameAtom();
int32_t contextNameSpaceID = aContext->GetNameSpaceID();
RefPtr<Document> doc = aTarget->OwnerDoc();
RefPtr<DocumentFragment> fragment = doc->CreateDocumentFragment();
nsresult rv = sHTMLFragmentParser->ParseFragment(
aSource, fragment, contextLocalName, contextNameSpaceID,
fragment->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks,
true, true);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to parse fragment for SetHTMLUnsafe");
}
aTarget->ReplaceChildren(fragment, IgnoreErrors());
}
/* static */
nsresult nsContentUtils::ParseFragmentHTML(
const nsAString& aSourceBuffer, nsIContent* aTargetNode,
@ -5431,7 +5459,7 @@ nsresult nsContentUtils::ParseFragmentHTML(
nsresult rv = sHTMLFragmentParser->ParseFragment(
aSourceBuffer, target, aContextLocalName, aContextNamespace, aQuirks,
aPreventScriptExecution);
aPreventScriptExecution, false);
NS_ENSURE_SUCCESS(rv, rv);
if (fragment) {
@ -11270,6 +11298,25 @@ template bool nsContentUtils::AddElementToListByTreeOrder(
nsTArray<RefPtr<HTMLInputElement>>& aList, HTMLInputElement* aChild,
nsIContent* aAncestor);
nsIContent* nsContentUtils::AttachDeclarativeShadowRoot(nsIContent* aHost,
ShadowRootMode aMode,
bool aDelegatesFocus) {
RefPtr<Element> host = mozilla::dom::Element::FromNodeOrNull(aHost);
if (!host) {
return nullptr;
}
ShadowRootInit init;
init.mMode = aMode;
init.mDelegatesFocus = aDelegatesFocus;
init.mSlotAssignment = SlotAssignmentMode::Named;
init.mClonable = true;
RefPtr shadowRoot = host->AttachShadow(init, IgnoreErrors(),
Element::ShadowRootDeclarative::Yes);
return shadowRoot;
}
namespace mozilla {
std::ostream& operator<<(std::ostream& aOut,
const PreventDefaultResult aPreventDefaultResult) {

View File

@ -176,6 +176,7 @@ class DOMArena;
class Element;
class Event;
class EventTarget;
class FragmentOrElement;
class HTMLElement;
class HTMLInputElement;
class IPCTransferable;
@ -187,6 +188,7 @@ class MessageBroadcaster;
class NodeInfo;
class OwningFileOrUSVStringOrFormData;
class Selection;
enum class ShadowRootMode : uint8_t;
struct StructuredSerializeOptions;
class WorkerPrivate;
enum class ElementCallbackType;
@ -1802,6 +1804,9 @@ class nsContentUtils {
bool aPreventScriptExecution,
mozilla::ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT
static void SetHTMLUnsafe(mozilla::dom::FragmentOrElement* aTarget,
Element* aContext, const nsAString& aSource);
/**
* Invoke the fragment parsing algorithm (innerHTML) using the HTML parser.
*
@ -3457,6 +3462,11 @@ class nsContentUtils {
nsIContent* aContent2,
const nsIContent* aCommonAncestor);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
static nsIContent* AttachDeclarativeShadowRoot(
nsIContent* aHost, mozilla::dom::ShadowRootMode aMode,
bool aDelegatesFocus);
private:
static bool InitializeEventTable();

View File

@ -37,6 +37,7 @@
#include "mozilla/dom/DebuggerNotificationBinding.h"
#include "mozilla/dom/DocumentType.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/Link.h"
@ -3614,6 +3615,38 @@ already_AddRefed<nsINode> nsINode::CloneAndAdopt(
}
}
if (aClone && aNode->IsElement() &&
!nodeInfo->GetDocument()->IsStaticDocument()) {
// Clone the Shadow DOM
ShadowRoot* originalShadowRoot = aNode->AsElement()->GetShadowRoot();
if (originalShadowRoot && originalShadowRoot->IsClonable()) {
ShadowRootInit init;
init.mMode = originalShadowRoot->Mode();
init.mDelegatesFocus = originalShadowRoot->DelegatesFocus();
init.mSlotAssignment = originalShadowRoot->SlotAssignment();
init.mClonable = true;
RefPtr<ShadowRoot> newShadowRoot =
clone->AsElement()->AttachShadow(init, aError);
if (NS_WARN_IF(aError.Failed())) {
return nullptr;
}
newShadowRoot->SetIsDeclarative(originalShadowRoot->IsDeclarative());
if (aDeep) {
for (nsIContent* origChild = originalShadowRoot->GetFirstChild();
origChild; origChild = origChild->GetNextSibling()) {
nsCOMPtr<nsINode> child =
CloneAndAdopt(origChild, aClone, aDeep, nodeInfoManager,
aReparentScope, newShadowRoot, aError);
if (NS_WARN_IF(aError.Failed())) {
return nullptr;
}
}
}
}
}
// Cloning template element.
if (aDeep && aClone && aNode->IsTemplateElement()) {
DocumentFragment* origContent =

View File

@ -8,6 +8,9 @@
#include "mozilla/dom/HTMLTemplateElementBinding.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/NameSpaceConstants.h"
#include "mozilla/dom/ShadowRootBinding.h"
#include "nsGenericHTMLElement.h"
#include "nsGkAtoms.h"
#include "nsStyleConsts.h"
#include "nsAtom.h"
@ -16,6 +19,13 @@ NS_IMPL_NS_NEW_HTML_ELEMENT(Template)
namespace mozilla::dom {
static constexpr nsAttrValue::EnumTable kShadowRootModeTable[] = {
{"open", ShadowRootMode::Open},
{"closed", ShadowRootMode::Closed},
{nullptr, {}}};
const nsAttrValue::EnumTable* kShadowRootModeDefault = &kShadowRootModeTable[2];
HTMLTemplateElement::HTMLTemplateElement(
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
: nsGenericHTMLElement(std::move(aNodeInfo)) {
@ -31,7 +41,7 @@ HTMLTemplateElement::HTMLTemplateElement(
}
HTMLTemplateElement::~HTMLTemplateElement() {
if (mContent) {
if (mContent && mContent->GetHost() == this) {
mContent->SetHost(nullptr);
}
}
@ -44,7 +54,9 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLTemplateElement)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLTemplateElement,
nsGenericHTMLElement)
if (tmp->mContent) {
tmp->mContent->SetHost(nullptr);
if (tmp->mContent->GetHost() == tmp) {
tmp->mContent->SetHost(nullptr);
}
tmp->mContent = nullptr;
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@ -61,4 +73,33 @@ JSObject* HTMLTemplateElement::WrapNode(JSContext* aCx,
return HTMLTemplateElement_Binding::Wrap(aCx, this, aGivenProto);
}
void HTMLTemplateElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
nsIPrincipal* aMaybeScriptedPrincipal,
bool aNotify) {
if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::shadowrootmode &&
aValue && aValue->Type() == nsAttrValue::ValueType::eEnum &&
!mShadowRootMode.isSome()) {
mShadowRootMode.emplace(
static_cast<ShadowRootMode>(aValue->GetEnumValue()));
}
nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue,
aMaybeScriptedPrincipal, aNotify);
}
bool HTMLTemplateElement::ParseAttribute(int32_t aNamespaceID,
nsAtom* aAttribute,
const nsAString& aValue,
nsIPrincipal* aMaybeScriptedPrincipal,
nsAttrValue& aResult) {
if (aNamespaceID == kNameSpaceID_None &&
aAttribute == nsGkAtoms::shadowrootmode) {
return aResult.ParseEnumValue(aValue, kShadowRootModeTable, false, nullptr);
}
return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
aMaybeScriptedPrincipal, aResult);
}
} // namespace mozilla::dom

View File

@ -8,8 +8,11 @@
#define mozilla_dom_HTMLTemplateElement_h
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "nsGenericHTMLElement.h"
#include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/ShadowRootBinding.h"
#include "nsGkAtoms.h"
namespace mozilla::dom {
@ -26,9 +29,35 @@ class HTMLTemplateElement final : public nsGenericHTMLElement {
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLTemplateElement,
nsGenericHTMLElement)
void AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
const nsAttrValue* aValue, const nsAttrValue* aOldValue,
nsIPrincipal* aMaybeScriptedPrincipal,
bool aNotify) override;
bool ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
const nsAString& aValue,
nsIPrincipal* aMaybeScriptedPrincipal,
nsAttrValue& aResult) override;
virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
DocumentFragment* Content() { return mContent; }
void SetContent(DocumentFragment* aContent) { mContent = aContent; }
void GetShadowRootMode(nsAString& aResult) const {
GetEnumAttr(nsGkAtoms::shadowrootmode, nullptr, aResult);
}
void SetShadowRootMode(const nsAString& aValue) {
SetHTMLAttr(nsGkAtoms::shadowrootmode, aValue);
}
bool ShadowRootDelegatesFocus() {
return GetBoolAttr(nsGkAtoms::shadowrootdelegatesfocus);
}
void SetShadowRootDelegatesFocus(bool aValue) {
SetHTMLBoolAttr(nsGkAtoms::shadowrootdelegatesfocus, aValue,
IgnoredErrorResult());
}
protected:
virtual ~HTMLTemplateElement();
@ -37,6 +66,7 @@ class HTMLTemplateElement final : public nsGenericHTMLElement {
JS::Handle<JSObject*> aGivenProto) override;
RefPtr<DocumentFragment> mContent;
Maybe<ShadowRootMode> mShadowRootMode;
};
} // namespace mozilla::dom

View File

@ -3,7 +3,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html
* https://html.spec.whatwg.org/multipage/scripting.html#the-template-element
*
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
@ -13,5 +13,7 @@
interface HTMLTemplateElement : HTMLElement {
[HTMLConstructor] constructor();
readonly attribute DocumentFragment content;
readonly attribute DocumentFragment content;
[CEReactions] attribute DOMString shadowRootMode;
[CEReactions] attribute boolean shadowRootDelegatesFocus;
};

View File

@ -314,6 +314,7 @@ nsresult nsContentDLF::CreateDocument(
nsCOMPtr<nsIDocumentViewer> viewer = NS_NewDocumentViewer();
doc->SetContainer(static_cast<nsDocShell*>(aContainer));
doc->SetAllowDeclarativeShadowRoots(true);
// Initialize the document to begin loading the data. An
// nsIStreamListener connected to the parser is returned in

View File

@ -434,6 +434,8 @@ public abstract class TreeBuilder<T> implements TokenHandler,
private boolean forceNoQuirks = false;
private boolean allowDeclarativeShadowRoots = false;
// [NOCPP[
private boolean reportingDoctype = true;
@ -2958,6 +2960,20 @@ public abstract class TreeBuilder<T> implements TokenHandler,
|| (("http://www.w3.org/1998/Math/MathML" == ns) && (stackNode.getGroup() == MI_MO_MN_MS_MTEXT));
}
private T getDeclarativeShadowRoot(T currentNode, T templateNode, HtmlAttributes attributes) {
if (!isAllowDeclarativeShadowRoots()) {
return null;
}
String shadowRootMode = attributes.getValue(AttributeName.SHADOWROOTMODE);
if (shadowRootMode == null) {
return null;
}
boolean shadowRootDelegatesFocus = attributes.contains(AttributeName.SHADOWROOTDELEGATESFOCUS);
return getShadowRootFromHost(currentNode, templateNode, shadowRootMode, shadowRootDelegatesFocus);
}
/**
*
* <p>
@ -5302,9 +5318,17 @@ public abstract class TreeBuilder<T> implements TokenHandler,
T elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes, currentNode
// CPPONLY: , htmlCreator(elementName.getHtmlCreator())
);
appendElement(elt, currentNode);
if (ElementName.TEMPLATE == elementName) {
elt = getDocumentFragmentForTemplate(elt);
T root = getDeclarativeShadowRoot(currentNode, elt, attributes);
if (root != null) {
setDocumentFragmentForTemplate(elt, root);
elt = root;
} else {
appendElement(elt, currentNode);
elt = getDocumentFragmentForTemplate(elt);
}
} else {
appendElement(elt, currentNode);
}
StackNode<T> node = createStackNode(elementName, elt
// [NOCPP[
@ -5391,6 +5415,13 @@ public abstract class TreeBuilder<T> implements TokenHandler,
return template;
}
void setDocumentFragmentForTemplate(T template, T fragment) {
}
T getShadowRootFromHost(T host, T template, String shadowRootMode, boolean shadowRootDelegatesFocus) {
return null;
}
T getFormPointerForContext(T context) {
return null;
}
@ -5509,6 +5540,7 @@ public abstract class TreeBuilder<T> implements TokenHandler,
} else {
T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
elt = createElement("http://www.w3.org/1999/xhtml", name,
attributes, formOwner, currentNode
// CPPONLY: , htmlCreator(elementName.getHtmlCreator())
);
@ -5893,6 +5925,14 @@ public abstract class TreeBuilder<T> implements TokenHandler,
this.setForceNoQuirks(isSrcdocDocument);
}
public boolean isAllowDeclarativeShadowRoots() {
return allowDeclarativeShadowRoots;
}
public void setAllowDeclarativeShadowRoots(boolean allow) {
allowDeclarativeShadowRoots = allow;
}
// [NOCPP[
public void setNamePolicy(XmlViolationPolicy namePolicy) {

View File

@ -678,6 +678,8 @@ void nsHtml5Parser::StartTokenizer(bool aScriptingEnabled) {
mTreeBuilder->SetPreventScriptExecution(!aScriptingEnabled);
mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
mTreeBuilder->setAllowDeclarativeShadowRoots(
mExecutor->GetDocument()->AllowsDeclarativeShadowRoots());
mTokenizer->start();
}

View File

@ -1125,6 +1125,8 @@ nsresult nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest) {
mTreeBuilder->setScriptingEnabled(scriptingEnabled);
mTreeBuilder->SetPreventScriptExecution(
!((mMode == NORMAL) && scriptingEnabled));
mTreeBuilder->setAllowDeclarativeShadowRoots(
mExecutor->GetDocument()->AllowsDeclarativeShadowRoots());
mTokenizer->start();
mExecutor->Start();
mExecutor->StartReadingFromStage();

View File

@ -24,12 +24,10 @@ nsHtml5StringParser::nsHtml5StringParser()
nsHtml5StringParser::~nsHtml5StringParser() {}
nsresult nsHtml5StringParser::ParseFragment(const nsAString& aSourceBuffer,
nsIContent* aTargetNode,
nsAtom* aContextLocalName,
int32_t aContextNamespace,
bool aQuirks,
bool aPreventScriptExecution) {
nsresult nsHtml5StringParser::ParseFragment(
const nsAString& aSourceBuffer, nsIContent* aTargetNode,
nsAtom* aContextLocalName, int32_t aContextNamespace, bool aQuirks,
bool aPreventScriptExecution, bool aAllowDeclarativeShadowRoots) {
NS_ENSURE_TRUE(aSourceBuffer.Length() <= INT32_MAX, NS_ERROR_OUT_OF_MEMORY);
Document* doc = aTargetNode->OwnerDoc();
@ -52,7 +50,7 @@ nsresult nsHtml5StringParser::ParseFragment(const nsAString& aSourceBuffer,
mTreeBuilder->SetPreventScriptExecution(aPreventScriptExecution);
return Tokenize(aSourceBuffer, doc, true);
return Tokenize(aSourceBuffer, doc, true, aAllowDeclarativeShadowRoots);
}
nsresult nsHtml5StringParser::ParseDocument(
@ -67,12 +65,14 @@ nsresult nsHtml5StringParser::ParseDocument(
mTreeBuilder->SetPreventScriptExecution(true);
return Tokenize(aSourceBuffer, aTargetDoc,
aScriptingEnabledForNoscriptParsing);
aScriptingEnabledForNoscriptParsing,
aTargetDoc->AllowsDeclarativeShadowRoots());
}
nsresult nsHtml5StringParser::Tokenize(
const nsAString& aSourceBuffer, Document* aDocument,
bool aScriptingEnabledForNoscriptParsing) {
nsresult nsHtml5StringParser::Tokenize(const nsAString& aSourceBuffer,
Document* aDocument,
bool aScriptingEnabledForNoscriptParsing,
bool aDeclarativeShadowRootsAllowed) {
nsIURI* uri = aDocument->GetDocumentURI();
mBuilder->Init(aDocument, uri, nullptr, nullptr);
@ -85,6 +85,7 @@ nsresult nsHtml5StringParser::Tokenize(
mTreeBuilder->setScriptingEnabled(aScriptingEnabledForNoscriptParsing);
mTreeBuilder->setIsSrcdocDocument(aDocument->IsSrcdocDocument());
mTreeBuilder->setAllowDeclarativeShadowRoots(aDeclarativeShadowRootsAllowed);
mBuilder->Start();
mTokenizer->start();
if (!aSourceBuffer.IsEmpty()) {

View File

@ -41,11 +41,14 @@ class nsHtml5StringParser : public nsParserBase {
* @param aPreventScriptExecution true to prevent scripts from executing;
* don't set to false when parsing into a target node that has been bound
* to tree.
* @param aAllowDeclarativeShadowRoots allow the creation of declarative
* shadow roots.
*/
nsresult ParseFragment(const nsAString& aSourceBuffer,
nsIContent* aTargetNode, nsAtom* aContextLocalName,
int32_t aContextNamespace, bool aQuirks,
bool aPreventScriptExecution);
bool aPreventScriptExecution,
bool aAllowDeclarativeShadowRoots);
/**
* Parse an entire HTML document from a source string.
@ -61,7 +64,8 @@ class nsHtml5StringParser : public nsParserBase {
nsresult Tokenize(const nsAString& aSourceBuffer,
mozilla::dom::Document* aDocument,
bool aScriptingEnabledForNoscriptParsing);
bool aScriptingEnabledForNoscriptParsing,
bool aDeclarativeShadowRootsAllowed);
/**
* The tree operation executor

View File

@ -2093,6 +2093,23 @@ bool nsHtml5TreeBuilder::isSpecialParentInForeign(nsHtml5StackNode* stackNode) {
(stackNode->getGroup() == MI_MO_MN_MS_MTEXT));
}
nsIContentHandle* nsHtml5TreeBuilder::getDeclarativeShadowRoot(
nsIContentHandle* currentNode, nsIContentHandle* templateNode,
nsHtml5HtmlAttributes* attributes) {
if (!isAllowDeclarativeShadowRoots()) {
return nullptr;
}
nsHtml5String shadowRootMode =
attributes->getValue(nsHtml5AttributeName::ATTR_SHADOWROOTMODE);
if (!shadowRootMode) {
return nullptr;
}
bool shadowRootDelegatesFocus =
attributes->contains(nsHtml5AttributeName::ATTR_SHADOWROOTDELEGATESFOCUS);
return getShadowRootFromHost(currentNode, templateNode, shadowRootMode,
shadowRootDelegatesFocus);
}
nsHtml5String nsHtml5TreeBuilder::extractCharsetFromContent(
nsHtml5String attributeValue, nsHtml5TreeBuilder* tb) {
int32_t charsetState = CHARSET_INITIAL;
@ -4218,9 +4235,18 @@ void nsHtml5TreeBuilder::appendToCurrentNodeAndPushElement(
nsIContentHandle* elt =
createElement(kNameSpaceID_XHTML, elementName->getName(), attributes,
currentNode, htmlCreator(elementName->getHtmlCreator()));
appendElement(elt, currentNode);
if (nsHtml5ElementName::ELT_TEMPLATE == elementName) {
elt = getDocumentFragmentForTemplate(elt);
nsIContentHandle* root =
getDeclarativeShadowRoot(currentNode, elt, attributes);
if (root) {
setDocumentFragmentForTemplate(elt, root);
elt = root;
} else {
appendElement(elt, currentNode);
elt = getDocumentFragmentForTemplate(elt);
}
} else {
appendElement(elt, currentNode);
}
nsHtml5StackNode* node = createStackNode(elementName, elt);
push(node);
@ -4482,6 +4508,14 @@ void nsHtml5TreeBuilder::setIsSrcdocDocument(bool isSrcdocDocument) {
this->setForceNoQuirks(isSrcdocDocument);
}
bool nsHtml5TreeBuilder::isAllowDeclarativeShadowRoots() {
return allowDeclarativeShadowRoots;
}
void nsHtml5TreeBuilder::setAllowDeclarativeShadowRoots(bool allow) {
allowDeclarativeShadowRoots = allow;
}
void nsHtml5TreeBuilder::flushCharacters() {
if (charBufferLen > 0) {
if ((mode == IN_TABLE || mode == IN_TABLE_BODY || mode == IN_ROW) &&

View File

@ -314,6 +314,7 @@ class nsHtml5TreeBuilder : public nsAHtml5TreeBuilderState {
private:
bool quirks;
bool forceNoQuirks;
bool allowDeclarativeShadowRoots;
inline nsHtml5ContentCreatorFunction htmlCreator(
mozilla::dom::HTMLContentCreatorFunction htmlCreator) {
nsHtml5ContentCreatorFunction creator;
@ -353,6 +354,9 @@ class nsHtml5TreeBuilder : public nsAHtml5TreeBuilderState {
bool isTemplateContents();
bool isTemplateModeStackEmpty();
bool isSpecialParentInForeign(nsHtml5StackNode* stackNode);
nsIContentHandle* getDeclarativeShadowRoot(nsIContentHandle* currentNode,
nsIContentHandle* templateNode,
nsHtml5HtmlAttributes* attributes);
public:
static nsHtml5String extractCharsetFromContent(nsHtml5String attributeValue,
@ -556,6 +560,8 @@ class nsHtml5TreeBuilder : public nsAHtml5TreeBuilderState {
void setScriptingEnabled(bool scriptingEnabled);
void setForceNoQuirks(bool forceNoQuirks);
void setIsSrcdocDocument(bool isSrcdocDocument);
bool isAllowDeclarativeShadowRoots();
void setAllowDeclarativeShadowRoots(bool allow);
void flushCharacters();
private:

View File

@ -7,9 +7,12 @@
#include "ErrorList.h"
#include "nsError.h"
#include "nsHtml5AttributeName.h"
#include "nsHtml5HtmlAttributes.h"
#include "nsHtml5String.h"
#include "nsNetUtil.h"
#include "mozilla/dom/FetchPriority.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/ShadowRootBinding.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/Likely.h"
#include "mozilla/StaticPrefs_dom.h"
@ -1636,6 +1639,53 @@ nsIContentHandle* nsHtml5TreeBuilder::getDocumentFragmentForTemplate(
return fragHandle;
}
void nsHtml5TreeBuilder::setDocumentFragmentForTemplate(
nsIContentHandle* aTemplate, nsIContentHandle* aFragment) {
if (mBuilder) {
nsHtml5TreeOperation::SetDocumentFragmentForTemplate(
static_cast<nsIContent*>(aTemplate),
static_cast<nsIContent*>(aFragment));
return;
}
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
if (MOZ_UNLIKELY(!treeOp)) {
MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
return;
}
opSetDocumentFragmentForTemplate operation(aTemplate, aFragment);
treeOp->Init(mozilla::AsVariant(operation));
}
nsIContentHandle* nsHtml5TreeBuilder::getShadowRootFromHost(
nsIContentHandle* aHost, nsIContentHandle* aTemplateNode,
nsHtml5String aShadowRootMode, bool aShadowRootDelegatesFocus) {
ShadowRootMode mode;
if (aShadowRootMode.LowerCaseEqualsASCII("open")) {
mode = ShadowRootMode::Open;
} else if (aShadowRootMode.LowerCaseEqualsASCII("closed")) {
mode = ShadowRootMode::Closed;
} else {
return nullptr;
}
if (mBuilder) {
return nsContentUtils::AttachDeclarativeShadowRoot(
static_cast<nsIContent*>(aHost), mode, aShadowRootDelegatesFocus);
}
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(mozilla::fallible);
if (MOZ_UNLIKELY(!treeOp)) {
MarkAsBrokenAndRequestSuspensionWithoutBuilder(NS_ERROR_OUT_OF_MEMORY);
return nullptr;
}
nsIContentHandle* fragHandle = AllocateContentHandle();
opGetShadowRootFromHost operation(aHost, fragHandle, aTemplateNode, mode,
aShadowRootDelegatesFocus);
treeOp->Init(mozilla::AsVariant(operation));
return fragHandle;
}
nsIContentHandle* nsHtml5TreeBuilder::getFormPointerForContext(
nsIContentHandle* aContext) {
MOZ_ASSERT(mBuilder, "Must have builder.");

View File

@ -58,6 +58,13 @@ bool mActive;
void documentMode(nsHtml5DocumentMode m);
nsIContentHandle* getDocumentFragmentForTemplate(nsIContentHandle* aTemplate);
void setDocumentFragmentForTemplate(nsIContentHandle* aTemplate,
nsIContentHandle* aFragment);
nsIContentHandle* getShadowRootFromHost(nsIContentHandle* aHost,
nsIContentHandle* aTemplateNode,
nsHtml5String aShadowRootMode,
bool aShadowRootDelegatesFocus);
nsIContentHandle* getFormPointerForContext(nsIContentHandle* aContext);

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/Comment.h"
#include "mozilla/dom/CustomElementRegistry.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/DocumentType.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/LinkStyle.h"
@ -18,12 +19,14 @@
#include "mozilla/dom/HTMLImageElement.h"
#include "mozilla/dom/HTMLTemplateElement.h"
#include "mozilla/dom/MutationObservers.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/Text.h"
#include "nsAttrName.h"
#include "nsContentCreatorFunctions.h"
#include "nsContentUtils.h"
#include "nsDocElementCreatedNotificationRunner.h"
#include "nsEscape.h"
#include "nsGenericHTMLElement.h"
#include "nsHtml5AutoPauseUpdate.h"
#include "nsHtml5DocumentMode.h"
#include "nsHtml5HtmlAttributes.h"
@ -135,6 +138,10 @@ nsHtml5TreeOperation::~nsHtml5TreeOperation() {
void operator()(const opGetDocumentFragmentForTemplate& aOperation) {}
void operator()(const opSetDocumentFragmentForTemplate& aOperation) {}
void operator()(const opGetShadowRootFromHost& aOperation) {}
void operator()(const opGetFosterParent& aOperation) {}
void operator()(const opMarkAsBroken& aOperation) {}
@ -694,6 +701,12 @@ nsIContent* nsHtml5TreeOperation::GetDocumentFragmentForTemplate(
return tempElem->Content();
}
void nsHtml5TreeOperation::SetDocumentFragmentForTemplate(
nsIContent* aNode, nsIContent* aDocumentFragment) {
auto* tempElem = static_cast<HTMLTemplateElement*>(aNode);
tempElem->SetContent(static_cast<DocumentFragment*>(aDocumentFragment));
}
nsIContent* nsHtml5TreeOperation::GetFosterParent(nsIContent* aTable,
nsIContent* aStackParent) {
nsIContent* tableParent = aTable->GetParent();
@ -894,6 +907,31 @@ nsresult nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
return NS_OK;
}
nsresult operator()(const opSetDocumentFragmentForTemplate& aOperation) {
SetDocumentFragmentForTemplate(*aOperation.mTemplate,
*aOperation.mFragment);
return NS_OK;
}
nsresult operator()(const opGetShadowRootFromHost& aOperation) {
nsIContent* root = nsContentUtils::AttachDeclarativeShadowRoot(
*aOperation.mHost, aOperation.mShadowRootMode,
aOperation.mShadowRootDelegatesFocus);
if (root) {
*aOperation.mFragHandle = root;
return NS_OK;
}
// We failed to attach a new shadow root, so instead attach a template
// element and return its content.
nsHtml5TreeOperation::Append(*aOperation.mTemplateNode, *aOperation.mHost,
mBuilder);
*aOperation.mFragHandle =
static_cast<HTMLTemplateElement*>(*aOperation.mTemplateNode)
->Content();
return NS_OK;
}
nsresult operator()(const opGetFosterParent& aOperation) {
nsIContent* table = *(aOperation.mTable);
nsIContent* stackParent = *(aOperation.mStackParent);

View File

@ -8,6 +8,7 @@
#include "nsHtml5DocumentMode.h"
#include "nsHtml5HtmlAttributes.h"
#include "mozilla/dom/FromParser.h"
#include "mozilla/dom/ShadowRootBinding.h"
#include "mozilla/NotNull.h"
#include "mozilla/Variant.h"
#include "nsCharsetSource.h"
@ -264,6 +265,37 @@ struct opGetDocumentFragmentForTemplate {
}
};
struct opSetDocumentFragmentForTemplate {
nsIContent** mTemplate;
nsIContent** mFragment;
explicit opSetDocumentFragmentForTemplate(nsIContentHandle* aTemplate,
nsIContentHandle* aFragment) {
mTemplate = static_cast<nsIContent**>(aTemplate);
mFragment = static_cast<nsIContent**>(aFragment);
}
};
struct opGetShadowRootFromHost {
nsIContent** mHost;
nsIContent** mFragHandle;
nsIContent** mTemplateNode;
mozilla::dom::ShadowRootMode mShadowRootMode;
bool mShadowRootDelegatesFocus;
explicit opGetShadowRootFromHost(nsIContentHandle* aHost,
nsIContentHandle* aFragHandle,
nsIContentHandle* aTemplateNode,
mozilla::dom::ShadowRootMode aShadowRootMode,
bool aShadowRootDelegatesFocus) {
mHost = static_cast<nsIContent**>(aHost);
mFragHandle = static_cast<nsIContent**>(aFragHandle);
mTemplateNode = static_cast<nsIContent**>(aTemplateNode);
mShadowRootMode = aShadowRootMode;
mShadowRootDelegatesFocus = aShadowRootDelegatesFocus;
}
};
struct opGetFosterParent {
nsIContent** mTable;
nsIContent** mStackParent;
@ -492,7 +524,8 @@ typedef mozilla::Variant<
opCreateHTMLElement, opCreateSVGElement, opCreateMathMLElement,
opSetFormElement, opAppendText, opFosterParentText, opAppendComment,
opAppendCommentToDocument, opAppendDoctypeToDocument,
opGetDocumentFragmentForTemplate, opGetFosterParent,
opGetDocumentFragmentForTemplate, opSetDocumentFragmentForTemplate,
opGetShadowRootFromHost, opGetFosterParent,
// Gecko-specific on-pop ops
opMarkAsBroken, opRunScriptThatMayDocumentWriteOrBlock,
opRunScriptThatCannotDocumentWriteOrBlock, opPreventScriptExecution,
@ -587,6 +620,8 @@ class nsHtml5TreeOperation final {
nsHtml5DocumentBuilder* aBuilder);
static nsIContent* GetDocumentFragmentForTemplate(nsIContent* aNode);
static void SetDocumentFragmentForTemplate(nsIContent* aNode,
nsIContent* aDocumentFragment);
static nsIContent* GetFosterParent(nsIContent* aTable,
nsIContent* aStackParent);