Skip to content

Commit

Permalink
Add support for a media attribute on `<meta name="theme-color" cont…
Browse files Browse the repository at this point in the history
…ent="...">`

https://bugs.webkit.org/show_bug.cgi?id=224389
<rdar://problem/74991621>

Reviewed by Ryosuke Niwa.

Source/WebCore:

Tests: WKWebViewThemeColor.MetaElementValidNameAndColorAndMedia
       WKWebViewThemeColor.MetaElementInvalidName
       WKWebViewThemeColor.MetaElementInvalidColor
       WKWebViewThemeColor.MetaElementInvalidMedia
       WKWebViewThemeColor.MetaElementMultipleValid
       WKWebViewThemeColor.MetaElementValidSubframe
       WKWebViewThemeColor.KVO

* html/HTMLMetaElement.idl:
* html/HTMLMetaElement.h:
* html/HTMLMetaElement.cpp:
(WebCore::parseMedia): Added.
(WebCore::mediaMatches): Added.
(WebCore::HTMLMetaElement::mediaAttributeMatches): Added.
(WebCore::HTMLMetaElement::contentColor): Added.
(WebCore::HTMLMetaElement::attributeChanged):
(WebCore::HTMLMetaElement::parseAttribute):
(WebCore::HTMLMetaElement::removedFromAncestor):
(WebCore::HTMLMetaElement::process):
Add support for a reflected `media` attribute. Cache the most recently parsed `media` (which
becomes a `Ref<MediaQuerySet>`) and `content` (which can become a `Color`) to avoid doing
repeated work when determining the active theme color after media state changes. Notify the
`Document` whenever the `name` or `content` or `media` attribute changes if the new or old
value will be or would have been related to calculating the theme color.

* dom/Document.h:
(WebCore::Document::themeColor const): Deleted.
* dom/Document.cpp:
(WebCore::Document::themeColor): Added.
(WebCore::Document::metaElementThemeColorChanged): Added.
(WebCore::Document::determineActiveThemeColorMetaElement): Added.
(WebCore::Document::themeColorChanged):
(WebCore::Document::updateElementsAffectedByMediaQueries):
(WebCore::Document::processMetaElementThemeColor): Deleted.
Make calculating the theme color into a two stage process:
 1. find all `<meta name="theme-color">` that have a valid CSS color `content` in tree order
 2. return the `HTMLMetaElement::contentColor` of the first item from step 1 that `HTMLMetaElement::mediaAttributeMatches`
This is done so that `Document::updateElementsAffectedByMediaQueries` doesn't have to repeat
step 1 each time it's run (which can be often) and instead only needs to iterate a (likely
very small) list in step 2. The actions/situations listed above would clear the cached data
from both steps, meaning that the next `Document::themeColor` will do a full recalculation.
Notify the UIProcess of a change in theme color if the result of step 2 is different from a
previously cached result (if set).

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/WKWebViewThemeColor.mm:
(TEST.WKWebViewThemeColor.MetaElementValidNameAndColor): Added.
(TEST.WKWebViewThemeColor.MetaElementValidNameAndColorAndMedia): Added.
(TEST.WKWebViewThemeColor.MetaElementInvalidName): Added.
(TEST.WKWebViewThemeColor.MetaElementInvalidColor): Added.
(TEST.WKWebViewThemeColor.MetaElementInvalidMedia): Added.
(TEST.WKWebViewThemeColor.MetaElementMultipleValid): Added.
(TEST.WKWebViewThemeColor.MetaElementValidSubframe): Added.
(-[WKWebViewThemeColorObserver observeValueForKeyPath:ofObject:change:context:]):
(TEST.WKWebViewThemeColor.KVO):
(TEST.WKWebViewThemeColor.MetaElementOnLoad): Deleted.
(TEST.WKWebViewThemeColor.MetaElementMultipleTags): Deleted.


Canonical link: https://commits.webkit.org/237537@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@277270 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
dcrousso committed May 10, 2021
1 parent e813601 commit 7d97be4
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 27 deletions.
52 changes: 52 additions & 0 deletions Source/WebCore/ChangeLog
@@ -1,3 +1,55 @@
2021-05-10 Devin Rousso <drousso@apple.com>

Add support for a `media` attribute on `<meta name="theme-color" content="...">`
https://bugs.webkit.org/show_bug.cgi?id=224389
<rdar://problem/74991621>

Reviewed by Ryosuke Niwa.

Tests: WKWebViewThemeColor.MetaElementValidNameAndColorAndMedia
WKWebViewThemeColor.MetaElementInvalidName
WKWebViewThemeColor.MetaElementInvalidColor
WKWebViewThemeColor.MetaElementInvalidMedia
WKWebViewThemeColor.MetaElementMultipleValid
WKWebViewThemeColor.MetaElementValidSubframe
WKWebViewThemeColor.KVO

* html/HTMLMetaElement.idl:
* html/HTMLMetaElement.h:
* html/HTMLMetaElement.cpp:
(WebCore::parseMedia): Added.
(WebCore::mediaMatches): Added.
(WebCore::HTMLMetaElement::mediaAttributeMatches): Added.
(WebCore::HTMLMetaElement::contentColor): Added.
(WebCore::HTMLMetaElement::attributeChanged):
(WebCore::HTMLMetaElement::parseAttribute):
(WebCore::HTMLMetaElement::removedFromAncestor):
(WebCore::HTMLMetaElement::process):
Add support for a reflected `media` attribute. Cache the most recently parsed `media` (which
becomes a `Ref<MediaQuerySet>`) and `content` (which can become a `Color`) to avoid doing
repeated work when determining the active theme color after media state changes. Notify the
`Document` whenever the `name` or `content` or `media` attribute changes if the new or old
value will be or would have been related to calculating the theme color.

* dom/Document.h:
(WebCore::Document::themeColor const): Deleted.
* dom/Document.cpp:
(WebCore::Document::themeColor): Added.
(WebCore::Document::metaElementThemeColorChanged): Added.
(WebCore::Document::determineActiveThemeColorMetaElement): Added.
(WebCore::Document::themeColorChanged):
(WebCore::Document::updateElementsAffectedByMediaQueries):
(WebCore::Document::processMetaElementThemeColor): Deleted.
Make calculating the theme color into a two stage process:
1. find all `<meta name="theme-color">` that have a valid CSS color `content` in tree order
2. return the `HTMLMetaElement::contentColor` of the first item from step 1 that `HTMLMetaElement::mediaAttributeMatches`
This is done so that `Document::updateElementsAffectedByMediaQueries` doesn't have to repeat
step 1 each time it's run (which can be often) and instead only needs to iterate a (likely
very small) list in step 2. The actions/situations listed above would clear the cached data
from both steps, meaning that the next `Document::themeColor` will do a full recalculation.
Notify the UIProcess of a change in theme color if the result of step 2 is different from a
previously cached result (if set).

2021-05-09 Darin Adler <darin@apple.com>

Remove all remaining uses of the String::toInt family of functions
Expand Down
54 changes: 50 additions & 4 deletions Source/WebCore/dom/Document.cpp
Expand Up @@ -106,6 +106,7 @@
#include "HTMLInputElement.h"
#include "HTMLLinkElement.h"
#include "HTMLMediaElement.h"
#include "HTMLMetaElement.h"
#include "HTMLNameCollection.h"
#include "HTMLParserIdioms.h"
#include "HTMLPictureElement.h"
Expand Down Expand Up @@ -962,6 +963,20 @@ String Document::compatMode() const
return inQuirksMode() ? "BackCompat" : "CSS1Compat";
}

const Color& Document::themeColor()
{
if (!m_cachedThemeColor.isValid()) {
if (!m_activeThemeColorMetaElement)
m_activeThemeColorMetaElement = determineActiveThemeColorMetaElement();
if (m_activeThemeColorMetaElement)
m_cachedThemeColor = m_activeThemeColorMetaElement->contentColor();

if (!m_cachedThemeColor.isValid())
m_cachedThemeColor = m_applicationManifestThemeColor;
}
return m_cachedThemeColor;
}

void Document::resetLinkColor()
{
m_linkColor = StyleColor::colorFromKeyword(CSSValueWebkitLink, styleColorOptions(nullptr));
Expand Down Expand Up @@ -3846,16 +3861,40 @@ void Document::updateViewportArguments()
}
}

void Document::processMetaElementThemeColor(const String& themeColorString)
void Document::metaElementThemeColorChanged(HTMLMetaElement& metaElement)
{
auto oldThemeColor = themeColor();
m_metaElementThemeColor = CSSParser::parseColor(themeColorString);
// If the current content color isn't valid and it wasn't previously in the list of elements
// with a valid content color, don't bother recalculating `m_metaThemeColorElements`.
if (!metaElement.contentColor().isValid() && m_metaThemeColorElements && !m_metaThemeColorElements->contains(&metaElement))
return;

auto oldThemeColor = std::exchange(m_cachedThemeColor, Color());
m_metaThemeColorElements = WTF::nullopt;
m_activeThemeColorMetaElement = nullptr;
if (themeColor() == oldThemeColor)
return;

themeColorChanged();
}

WeakPtr<HTMLMetaElement> Document::determineActiveThemeColorMetaElement()
{
if (!m_metaThemeColorElements) {
Vector<WeakPtr<HTMLMetaElement>> metaThemeColorElements;
for (auto& metaElement : descendantsOfType<HTMLMetaElement>(*this)) {
if (equalLettersIgnoringASCIICase(metaElement.name(), "theme-color") && metaElement.contentColor().isValid())
metaThemeColorElements.append(makeWeakPtr(metaElement));
}
m_metaThemeColorElements = WTFMove(metaThemeColorElements);
}

for (auto& metaElement : *m_metaThemeColorElements) {
if (metaElement && metaElement->contentColor().isValid() && metaElement->mediaAttributeMatches())
return metaElement;
}
return nullptr;
}

void Document::themeColorChanged()
{
scheduleRenderingUpdate({ });
Expand Down Expand Up @@ -4170,7 +4209,7 @@ void Document::processReferrerPolicy(const String& policy, ReferrerPolicySource

void Document::processApplicationManifest(const ApplicationManifest& applicationManifest)
{
auto oldThemeColor = themeColor();
auto oldThemeColor = std::exchange(m_cachedThemeColor, Color());
m_applicationManifestThemeColor = applicationManifest.themeColor;
if (themeColor() == oldThemeColor)
return;
Expand Down Expand Up @@ -4356,6 +4395,13 @@ void Document::updateElementsAffectedByMediaQueries()
{
ScriptDisallowedScope::InMainThread scriptDisallowedScope;

if (auto activeThemeColorElement = determineActiveThemeColorMetaElement(); m_activeThemeColorMetaElement != activeThemeColorElement) {
auto oldThemeColor = std::exchange(m_cachedThemeColor, Color());
m_activeThemeColorMetaElement = WTFMove(activeThemeColorElement);
if (themeColor() != oldThemeColor)
themeColorChanged();
}

// FIXME: copyToVector doesn't work with WeakHashSet
Vector<Ref<HTMLImageElement>> images;
images.reserveInitialCapacity(m_dynamicMediaQueryDependentImages.computeSize());
Expand Down
11 changes: 8 additions & 3 deletions Source/WebCore/dom/Document.h
Expand Up @@ -158,6 +158,7 @@ class HTMLIFrameElement;
class HTMLImageElement;
class HTMLMapElement;
class HTMLMediaElement;
class HTMLMetaElement;
class HTMLVideoElement;
class HighlightRegister;
class HitTestLocation;
Expand Down Expand Up @@ -741,7 +742,7 @@ class Document
Seconds timeSinceDocumentCreation() const { return MonotonicTime::now() - m_documentCreationTime; };
#endif

const Color& themeColor() const { return m_metaElementThemeColor.isValid() ? m_metaElementThemeColor : m_applicationManifestThemeColor; }
const Color& themeColor();

const Color& sampledPageTopColor() const { return m_sampledPageTopColor; }

Expand Down Expand Up @@ -912,7 +913,8 @@ class Document
void processDisabledAdaptations(const String& adaptations);
void updateViewportArguments();
void processReferrerPolicy(const String& policy, ReferrerPolicySource);
void processMetaElementThemeColor(const String& themeColor);

void metaElementThemeColorChanged(HTMLMetaElement&);

#if ENABLE(DARK_MODE_CSS)
void processColorScheme(const String& colorScheme);
Expand Down Expand Up @@ -1672,6 +1674,7 @@ class Document
void updateTitle(const StringWithDirection&);
void updateBaseURL();

WeakPtr<HTMLMetaElement> determineActiveThemeColorMetaElement();
void themeColorChanged();

void determineSampledPageTopColor();
Expand Down Expand Up @@ -1794,7 +1797,9 @@ class Document

std::unique_ptr<FormController> m_formController;

Color m_metaElementThemeColor;
Color m_cachedThemeColor;
Optional<Vector<WeakPtr<HTMLMetaElement>>> m_metaThemeColorElements;
WeakPtr<HTMLMetaElement> m_activeThemeColorMetaElement;
Color m_applicationManifestThemeColor;

Color m_sampledPageTopColor;
Expand Down
75 changes: 65 additions & 10 deletions Source/WebCore/html/HTMLMetaElement.cpp
Expand Up @@ -24,11 +24,18 @@
#include "HTMLMetaElement.h"

#include "Attribute.h"
#include "Color.h"
#include "Document.h"
#include "HTMLHeadElement.h"
#include "HTMLNames.h"
#include "MediaList.h"
#include "MediaQueryEvaluator.h"
#include "MediaQueryParser.h"
#include "RenderStyle.h"
#include "Settings.h"
#include "StyleResolveForDocument.h"
#include <wtf/IsoMallocInlines.h>
#include <wtf/Optional.h>

namespace WebCore {

Expand All @@ -52,24 +59,72 @@ Ref<HTMLMetaElement> HTMLMetaElement::create(const QualifiedName& tagName, Docum
return adoptRef(*new HTMLMetaElement(tagName, document));
}

bool HTMLMetaElement::mediaAttributeMatches()
{
auto& document = this->document();

if (!m_media)
m_media = MediaQuerySet::create(attributeWithoutSynchronization(mediaAttr).convertToASCIILowercase(), MediaQueryParserContext(document));

Optional<RenderStyle> documentStyle;
if (document.hasLivingRenderTree())
documentStyle = Style::resolveForDocument(document);

String mediaType;
if (auto* frame = document.frame()) {
if (auto* frameView = frame->view())
mediaType = frameView->mediaType();
}

return MediaQueryEvaluator(mediaType, document, documentStyle ? &*documentStyle : nullptr).evaluate(*m_media);
}

const Color& HTMLMetaElement::contentColor()
{
if (!m_contentColor)
m_contentColor = CSSParser::parseColor(content());
return *m_contentColor;
}

void HTMLMetaElement::attributeChanged(const QualifiedName& name, const AtomString& oldValue, const AtomString& newValue, AttributeModificationReason reason)
{
HTMLElement::attributeChanged(name, oldValue, newValue, reason);

if (name == nameAttr && equalLettersIgnoringASCIICase(oldValue, "theme-color") && !equalLettersIgnoringASCIICase(newValue, "theme-color"))
document().processMetaElementThemeColor(emptyString());
if (!isConnected())
return;

if (name == nameAttr) {
if (equalLettersIgnoringASCIICase(oldValue, "theme-color") && !equalLettersIgnoringASCIICase(newValue, "theme-color"))
document().metaElementThemeColorChanged(*this);
return;
}
}

void HTMLMetaElement::parseAttribute(const QualifiedName& name, const AtomString& value)
{
if (name == http_equivAttr)
if (name == nameAttr) {
process();
else if (name == contentAttr)
return;
}

if (name == contentAttr) {
m_contentColor = WTF::nullopt;
process();
else if (name == nameAttr)
return;
}

if (name == http_equivAttr) {
process();
else
HTMLElement::parseAttribute(name, value);
return;
}

if (name == mediaAttr) {
m_media = nullptr;
process();
return;
}

HTMLElement::parseAttribute(name, value);
}

Node::InsertedIntoAncestorResult HTMLMetaElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
Expand All @@ -89,8 +144,8 @@ void HTMLMetaElement::removedFromAncestor(RemovalType removalType, ContainerNode
{
HTMLElement::removedFromAncestor(removalType, oldParentOfRemovedTree);

if (!isConnected() && equalLettersIgnoringASCIICase(name(), "theme-color"))
oldParentOfRemovedTree.document().processMetaElementThemeColor(emptyString());
if (removalType.disconnectedFromDocument && equalLettersIgnoringASCIICase(name(), "theme-color"))
oldParentOfRemovedTree.document().metaElementThemeColorChanged(*this);
}

void HTMLMetaElement::process()
Expand All @@ -112,7 +167,7 @@ void HTMLMetaElement::process()
document().processColorScheme(contentValue);
#endif
else if (equalLettersIgnoringASCIICase(name(), "theme-color"))
document().processMetaElementThemeColor(contentValue);
document().metaElementThemeColorChanged(*this);
#if PLATFORM(IOS_FAMILY)
else if (equalLettersIgnoringASCIICase(name(), "format-detection"))
document().processFormatDetection(contentValue);
Expand Down
11 changes: 11 additions & 0 deletions Source/WebCore/html/HTMLMetaElement.h
Expand Up @@ -26,6 +26,9 @@

namespace WebCore {

class Color;
class MediaQuerySet;

class HTMLMetaElement final : public HTMLElement {
WTF_MAKE_ISO_ALLOCATED(HTMLMetaElement);
public:
Expand All @@ -36,6 +39,10 @@ class HTMLMetaElement final : public HTMLElement {
const AtomString& httpEquiv() const;
const AtomString& name() const;

bool mediaAttributeMatches();

const Color& contentColor();

private:
HTMLMetaElement(const QualifiedName&, Document&);

Expand All @@ -46,6 +53,10 @@ class HTMLMetaElement final : public HTMLElement {
void removedFromAncestor(RemovalType, ContainerNode&) final;

void process();

RefPtr<MediaQuerySet> m_media;

Optional<Color> m_contentColor;
};

} // namespace WebCore
1 change: 1 addition & 0 deletions Source/WebCore/html/HTMLMetaElement.idl
Expand Up @@ -22,6 +22,7 @@
] interface HTMLMetaElement : HTMLElement {
[CEReactions=NotNeeded, Reflect] attribute DOMString content;
[CEReactions=NotNeeded, Reflect=http_equiv] attribute DOMString httpEquiv;
[CEReactions=NotNeeded, Reflect] attribute DOMString media;
[CEReactions=NotNeeded, Reflect] attribute DOMString name;
[CEReactions=NotNeeded, Reflect] attribute DOMString scheme;
};
Expand Down
21 changes: 21 additions & 0 deletions Tools/ChangeLog
@@ -1,3 +1,24 @@
2021-05-10 Devin Rousso <drousso@apple.com>

Add support for a `media` attribute on `<meta name="theme-color" content="...">`
https://bugs.webkit.org/show_bug.cgi?id=224389
<rdar://problem/74991621>

Reviewed by Ryosuke Niwa.

* TestWebKitAPI/Tests/WebKitCocoa/WKWebViewThemeColor.mm:
(TEST.WKWebViewThemeColor.MetaElementValidNameAndColor): Added.
(TEST.WKWebViewThemeColor.MetaElementValidNameAndColorAndMedia): Added.
(TEST.WKWebViewThemeColor.MetaElementInvalidName): Added.
(TEST.WKWebViewThemeColor.MetaElementInvalidColor): Added.
(TEST.WKWebViewThemeColor.MetaElementInvalidMedia): Added.
(TEST.WKWebViewThemeColor.MetaElementMultipleValid): Added.
(TEST.WKWebViewThemeColor.MetaElementValidSubframe): Added.
(-[WKWebViewThemeColorObserver observeValueForKeyPath:ofObject:change:context:]):
(TEST.WKWebViewThemeColor.KVO):
(TEST.WKWebViewThemeColor.MetaElementOnLoad): Deleted.
(TEST.WKWebViewThemeColor.MetaElementMultipleTags): Deleted.

2021-05-09 Darin Adler <darin@apple.com>

Remove all remaining uses of the String::toInt family of functions
Expand Down

0 comments on commit 7d97be4

Please sign in to comment.