Skip to content

Commit

Permalink
We should cache the first base element in Document
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=264113
rdar://117871556

Reviewed by Ryosuke Niwa.

Sometimes, we consume much time for document.querySelector("base"),
because this document ends up not having `base`. It means that it is traversing all the tree.
But this traversal is already paid cost when we call Document::processBaseElement, which is called
everytime `base` element is inserted or removed into the document to update baseURL. So we should just
cache the first element here, and use this information in querySelector and querySelectorAll.

This patch adds Document::firstBaseElement and adding a fast path for querySelector and querySelectorAll.
querySelector uses this element directly. And querySelectorAll stops traversal when this firstBaseElement is nullptr.

* Source/WebCore/dom/Document.cpp:
(WebCore::Document::firstBaseElement const):
(WebCore::Document::processBaseElement):
* Source/WebCore/dom/Document.h:
* Source/WebCore/dom/SelectorQuery.cpp:
(WebCore::elementsForLocalName):

Canonical link: https://commits.webkit.org/270150@main
  • Loading branch information
Constellation committed Nov 3, 2023
1 parent f1ab7fe commit 6a6005d
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 0 deletions.
10 changes: 10 additions & 0 deletions Source/WebCore/dom/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3776,13 +3776,22 @@ void Document::setBaseURLOverride(const URL& url)
updateBaseURL();
}

HTMLBaseElement* Document::firstBaseElement() const
{
return m_firstBaseElement.get();
}

void Document::processBaseElement()
{
// Find the first href attribute in a base element and the first target attribute in a base element.
AtomString href;
AtomString target;
RefPtr<HTMLBaseElement> baseElement;
auto baseDescendants = descendantsOfType<HTMLBaseElement>(*this);
for (auto& base : baseDescendants) {
if (!baseElement)
baseElement = &base;

if (href.isNull()) {
auto& value = base.attributeWithoutSynchronization(hrefAttr);
if (!value.isNull()) {
Expand Down Expand Up @@ -3816,6 +3825,7 @@ void Document::processBaseElement()
}

m_baseTarget = WTFMove(target);
m_firstBaseElement = WTFMove(baseElement);
}

String Document::userAgent(const URL& url) const
Expand Down
4 changes: 4 additions & 0 deletions Source/WebCore/dom/Document.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class GPUCanvasContext;
class GraphicsClient;
class HTMLAllCollection;
class HTMLAttachmentElement;
class HTMLBaseElement;
class HTMLBodyElement;
class HTMLCanvasElement;
class HTMLCollection;
Expand Down Expand Up @@ -789,6 +790,7 @@ class Document
const URL& baseURLOverride() const { return m_baseURLOverride; }
const URL& baseElementURL() const { return m_baseElementURL; }
const AtomString& baseTarget() const { return m_baseTarget; }
HTMLBaseElement* firstBaseElement() const;
void processBaseElement();

URL baseURLForComplete(const URL& baseURLOverride) const;
Expand Down Expand Up @@ -2006,6 +2008,8 @@ class Document

AtomString m_baseTarget;

WeakPtr<HTMLBaseElement, WeakPtrImplWithEventTargetData> m_firstBaseElement;

// MIME type of the document in case it was cloned or created by XHR.
String m_overriddenMIMEType;

Expand Down
11 changes: 11 additions & 0 deletions Source/WebCore/dom/SelectorQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "CSSTokenizer.h"
#include "CommonAtomStrings.h"
#include "ElementAncestorIteratorInlines.h"
#include "HTMLBaseElement.h"
#include "HTMLNames.h"
#include "SelectorChecker.h"
#include "StaticNodeList.h"
Expand Down Expand Up @@ -277,6 +278,16 @@ static ALWAYS_INLINE bool localNameMatches(const Element& element, const AtomStr
template<typename OutputType>
static inline void elementsForLocalName(const ContainerNode& rootNode, const AtomString& localName, const AtomString& lowercaseLocalName, OutputType& output)
{
if (is<Document>(rootNode) && lowercaseLocalName == HTMLNames::baseTag->localName()) {
RefPtr firstBaseElement = downcast<Document>(rootNode).firstBaseElement();
if (!firstBaseElement)
return;
if constexpr (std::is_same_v<OutputType, Element*>) {
appendOutputForElement(output, *firstBaseElement);
return;
}
}

if (localName == lowercaseLocalName) {
for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) {
if (element.tagQName().localName() == localName) {
Expand Down

0 comments on commit 6a6005d

Please sign in to comment.