Skip to content
Permalink
Browse files
Prototype streaming for declarative shadow DOM
https://bugs.webkit.org/show_bug.cgi?id=245817

Reviewed by Chris Dumez.

Add a very experimental internal settings to make declarative shadow DOM stream by attaching shadow root
when the start tag of a template element is parsed instead of when the end tag is parsed as proposed.

Having this internal setting allow us to analyze the performance characteristics of both approaches.

* Source/WTF/Scripts/Preferences/WebPreferencesInternal.yaml: Added a new internal settings.
We don't use experimental features since this feature needs to be disabled by default in WKTR.

* Source/WebCore/html/HTMLTemplateElement.cpp:
(WebCore::HTMLTemplateElement::fragmentForInsertion const): Added. This is an abstraction around
regular template and template element, which is a parser macro for shadow root.
(WebCore::HTMLTemplateElement::content const): Assert that nobody calls this version unless we're
dealing with a regular template element (i.e. not a declarative shadow DOM).
(WebCore::HTMLTemplateElement::setDeclarativeShadowRoot): Added.
(WebCore::HTMLTemplateElement::attachAsDeclarativeShadowRootIfNeeded): Exit early if we had attached
the shadow root with the start tag.

* Source/WebCore/html/HTMLTemplateElement.h:

* Source/WebCore/html/parser/HTMLConstructionSite.cpp:
(WebCore::insert):
(WebCore::HTMLConstructionSite::insertHTMLTemplateElement): Added. This implements the main logic
of streaming declarative shadow DOM, which attaches shadow root as soon as the start tag is parsed.
(WebCore::HTMLConstructionSite::ownerDocumentForCurrentNode):

* Source/WebCore/html/parser/HTMLConstructionSite.h:

* Source/WebCore/html/parser/HTMLTreeBuilder.cpp:
(WebCore::HTMLTreeBuilder::processTemplateStartTag):

Canonical link: https://commits.webkit.org/255020@main
  • Loading branch information
rniwa committed Sep 30, 2022
1 parent 95606b1 commit c0bda7319ffbab5ebf316e8dec18e50afbfde67a
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 3 deletions.
@@ -950,6 +950,18 @@ SpeechSynthesisAPIEnabled:
WebCore:
default: true

StreamingDeclarativeShadowDOMEnabled:
type: bool
humanReadableName: "Streaming for Declarative Shadow DOM"
humanReadableDescription: "Enable Streaming for Declarative Shadow DOM"
defaultValue:
WebKitLegacy:
default: false
WebKit:
default: false
WebCore:
default: false

# FIXME: Is this implemented for WebKitLegacy? If not, this should be excluded from WebKitLegacy entirely.
TextAutosizingUsesIdempotentMode:
type: bool
@@ -33,6 +33,7 @@

#include "Document.h"
#include "DocumentFragment.h"
#include "ElementRareData.h"
#include "HTMLNames.h"
#include "ShadowRoot.h"
#include "ShadowRootInit.h"
@@ -68,13 +69,26 @@ DocumentFragment* HTMLTemplateElement::contentIfAvailable() const
return m_content.get();
}

DocumentFragment& HTMLTemplateElement::fragmentForInsertion() const
{
if (m_declarativeShadowRoot)
return *m_declarativeShadowRoot;
return content();
}

DocumentFragment& HTMLTemplateElement::content() const
{
ASSERT(!m_declarativeShadowRoot);
if (!m_content)
m_content = TemplateContentDocumentFragment::create(document().ensureTemplateDocument(), *this);
return *m_content;
}

void HTMLTemplateElement::setDeclarativeShadowRoot(ShadowRoot& shadowRoot)
{
m_declarativeShadowRoot = shadowRoot;
}

Ref<Node> HTMLTemplateElement::cloneNodeInternal(Document& targetDocument, CloningOperation type)
{
RefPtr<Node> clone;
@@ -104,6 +118,11 @@ void HTMLTemplateElement::didMoveToNewDocument(Document& oldDocument, Document&

void HTMLTemplateElement::attachAsDeclarativeShadowRootIfNeeded(Element& host)
{
if (m_declarativeShadowRoot) {
ASSERT(host.shadowRoot());
return;
}

auto modeString = attributeWithoutSynchronization(HTMLNames::shadowrootAttr);
std::optional<ShadowRootMode> mode;

@@ -43,9 +43,11 @@ class HTMLTemplateElement final : public HTMLElement {
static Ref<HTMLTemplateElement> create(const QualifiedName&, Document&);
virtual ~HTMLTemplateElement();

DocumentFragment& fragmentForInsertion() const;
DocumentFragment& content() const;
DocumentFragment* contentIfAvailable() const;

void setDeclarativeShadowRoot(ShadowRoot&);
void attachAsDeclarativeShadowRootIfNeeded(Element&);

private:
@@ -55,6 +57,7 @@ class HTMLTemplateElement final : public HTMLElement {
void didMoveToNewDocument(Document& oldDocument, Document& newDocument) final;

mutable RefPtr<TemplateContentDocumentFragment> m_content;
WeakPtr<ShadowRoot, WeakPtrImplWithEventTargetData> m_declarativeShadowRoot;
};

} // namespace WebCore
@@ -51,6 +51,7 @@
#include "NotImplemented.h"
#include "SVGElementInlines.h"
#include "Settings.h"
#include "ShadowRoot.h"
#include "Text.h"
#include <unicode/ubrk.h>
#include <wtf/text/TextBreakIterator.h>
@@ -130,7 +131,7 @@ static inline bool isAllWhitespace(const String& string)
static inline void insert(HTMLConstructionSiteTask& task)
{
if (is<HTMLTemplateElement>(*task.parent)) {
task.parent = &downcast<HTMLTemplateElement>(*task.parent).content();
task.parent = &downcast<HTMLTemplateElement>(*task.parent).fragmentForInsertion();
task.nextChild = nullptr;
}

@@ -531,6 +532,36 @@ void HTMLConstructionSite::insertHTMLElement(AtomHTMLToken&& token)
m_openElements.push(HTMLStackItem(WTFMove(element), WTFMove(token)));
}

void HTMLConstructionSite::insertHTMLTemplateElement(AtomHTMLToken&& token)
{
if (m_document.settings().streamingDeclarativeShadowDOMEnabled() && m_parserContentPolicy.contains(ParserContentPolicy::AllowDeclarativeShadowDOM)
&& !currentElement().document().templateDocumentHost()) {
std::optional<ShadowRootMode> mode;
bool delegatesFocus = false;
for (auto& attribute : token.attributes()) {
if (attribute.name() == HTMLNames::shadowrootAttr) {
if (equalLettersIgnoringASCIICase(attribute.value(), "closed"_s))
mode = ShadowRootMode::Closed;
else if (equalLettersIgnoringASCIICase(attribute.value(), "open"_s))
mode = ShadowRootMode::Open;
} else if (attribute.name() == HTMLNames::shadowrootdelegatesfocusAttr)
delegatesFocus = true;
}
if (mode) {
auto exceptionOrShadowRoot = currentElement().attachDeclarativeShadow(*mode, delegatesFocus);
if (!exceptionOrShadowRoot.hasException()) {
Ref shadowRoot = exceptionOrShadowRoot.releaseReturnValue();
auto element = createHTMLElement(token);
RELEASE_ASSERT(is<HTMLTemplateElement>(element));
downcast<HTMLTemplateElement>(element.get()).setDeclarativeShadowRoot(shadowRoot);
m_openElements.push(HTMLStackItem(WTFMove(element), WTFMove(token)));
return;
}
}
}
insertHTMLElement(WTFMove(token));
}

std::unique_ptr<CustomElementConstructionData> HTMLConstructionSite::insertHTMLElementOrFindCustomElementInterface(AtomHTMLToken&& token)
{
JSCustomElementInterface* elementInterface = nullptr;
@@ -727,7 +758,7 @@ Ref<Element> HTMLConstructionSite::createElement(AtomHTMLToken& token, const Ato
inline Document& HTMLConstructionSite::ownerDocumentForCurrentNode()
{
if (is<HTMLTemplateElement>(currentNode()))
return downcast<HTMLTemplateElement>(currentNode()).content().document();
return downcast<HTMLTemplateElement>(currentNode()).fragmentForInsertion().document();
return currentNode().document();
}

@@ -120,6 +120,7 @@ class HTMLConstructionSite {
void insertCommentOnDocument(AtomHTMLToken&&);
void insertCommentOnHTMLHtmlElement(AtomHTMLToken&&);
void insertHTMLElement(AtomHTMLToken&&);
void insertHTMLTemplateElement(AtomHTMLToken&&);
std::unique_ptr<CustomElementConstructionData> insertHTMLElementOrFindCustomElementInterface(AtomHTMLToken&&);
void insertCustomElement(Ref<Element>&&, Vector<Attribute>&&);
void insertSelfClosingHTMLElement(AtomHTMLToken&&);
@@ -885,7 +885,7 @@ void HTMLTreeBuilder::didCreateCustomOrFallbackElement(Ref<Element>&& element, C
void HTMLTreeBuilder::processTemplateStartTag(AtomHTMLToken&& token)
{
m_tree.activeFormattingElements().appendMarker();
m_tree.insertHTMLElement(WTFMove(token));
m_tree.insertHTMLTemplateElement(WTFMove(token));
m_templateInsertionModes.append(InsertionMode::TemplateContents);
m_insertionMode = InsertionMode::TemplateContents;
}

0 comments on commit c0bda73

Please sign in to comment.