Skip to content

Commit 7d97be4

Browse files
committed
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. 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
1 parent e813601 commit 7d97be4

File tree

8 files changed

+329
-27
lines changed

8 files changed

+329
-27
lines changed

Source/WebCore/ChangeLog

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,55 @@
1+
2021-05-10 Devin Rousso <drousso@apple.com>
2+
3+
Add support for a `media` attribute on `<meta name="theme-color" content="...">`
4+
https://bugs.webkit.org/show_bug.cgi?id=224389
5+
<rdar://problem/74991621>
6+
7+
Reviewed by Ryosuke Niwa.
8+
9+
Tests: WKWebViewThemeColor.MetaElementValidNameAndColorAndMedia
10+
WKWebViewThemeColor.MetaElementInvalidName
11+
WKWebViewThemeColor.MetaElementInvalidColor
12+
WKWebViewThemeColor.MetaElementInvalidMedia
13+
WKWebViewThemeColor.MetaElementMultipleValid
14+
WKWebViewThemeColor.MetaElementValidSubframe
15+
WKWebViewThemeColor.KVO
16+
17+
* html/HTMLMetaElement.idl:
18+
* html/HTMLMetaElement.h:
19+
* html/HTMLMetaElement.cpp:
20+
(WebCore::parseMedia): Added.
21+
(WebCore::mediaMatches): Added.
22+
(WebCore::HTMLMetaElement::mediaAttributeMatches): Added.
23+
(WebCore::HTMLMetaElement::contentColor): Added.
24+
(WebCore::HTMLMetaElement::attributeChanged):
25+
(WebCore::HTMLMetaElement::parseAttribute):
26+
(WebCore::HTMLMetaElement::removedFromAncestor):
27+
(WebCore::HTMLMetaElement::process):
28+
Add support for a reflected `media` attribute. Cache the most recently parsed `media` (which
29+
becomes a `Ref<MediaQuerySet>`) and `content` (which can become a `Color`) to avoid doing
30+
repeated work when determining the active theme color after media state changes. Notify the
31+
`Document` whenever the `name` or `content` or `media` attribute changes if the new or old
32+
value will be or would have been related to calculating the theme color.
33+
34+
* dom/Document.h:
35+
(WebCore::Document::themeColor const): Deleted.
36+
* dom/Document.cpp:
37+
(WebCore::Document::themeColor): Added.
38+
(WebCore::Document::metaElementThemeColorChanged): Added.
39+
(WebCore::Document::determineActiveThemeColorMetaElement): Added.
40+
(WebCore::Document::themeColorChanged):
41+
(WebCore::Document::updateElementsAffectedByMediaQueries):
42+
(WebCore::Document::processMetaElementThemeColor): Deleted.
43+
Make calculating the theme color into a two stage process:
44+
1. find all `<meta name="theme-color">` that have a valid CSS color `content` in tree order
45+
2. return the `HTMLMetaElement::contentColor` of the first item from step 1 that `HTMLMetaElement::mediaAttributeMatches`
46+
This is done so that `Document::updateElementsAffectedByMediaQueries` doesn't have to repeat
47+
step 1 each time it's run (which can be often) and instead only needs to iterate a (likely
48+
very small) list in step 2. The actions/situations listed above would clear the cached data
49+
from both steps, meaning that the next `Document::themeColor` will do a full recalculation.
50+
Notify the UIProcess of a change in theme color if the result of step 2 is different from a
51+
previously cached result (if set).
52+
153
2021-05-09 Darin Adler <darin@apple.com>
254

355
Remove all remaining uses of the String::toInt family of functions

Source/WebCore/dom/Document.cpp

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
#include "HTMLInputElement.h"
107107
#include "HTMLLinkElement.h"
108108
#include "HTMLMediaElement.h"
109+
#include "HTMLMetaElement.h"
109110
#include "HTMLNameCollection.h"
110111
#include "HTMLParserIdioms.h"
111112
#include "HTMLPictureElement.h"
@@ -962,6 +963,20 @@ String Document::compatMode() const
962963
return inQuirksMode() ? "BackCompat" : "CSS1Compat";
963964
}
964965

966+
const Color& Document::themeColor()
967+
{
968+
if (!m_cachedThemeColor.isValid()) {
969+
if (!m_activeThemeColorMetaElement)
970+
m_activeThemeColorMetaElement = determineActiveThemeColorMetaElement();
971+
if (m_activeThemeColorMetaElement)
972+
m_cachedThemeColor = m_activeThemeColorMetaElement->contentColor();
973+
974+
if (!m_cachedThemeColor.isValid())
975+
m_cachedThemeColor = m_applicationManifestThemeColor;
976+
}
977+
return m_cachedThemeColor;
978+
}
979+
965980
void Document::resetLinkColor()
966981
{
967982
m_linkColor = StyleColor::colorFromKeyword(CSSValueWebkitLink, styleColorOptions(nullptr));
@@ -3846,16 +3861,40 @@ void Document::updateViewportArguments()
38463861
}
38473862
}
38483863

3849-
void Document::processMetaElementThemeColor(const String& themeColorString)
3864+
void Document::metaElementThemeColorChanged(HTMLMetaElement& metaElement)
38503865
{
3851-
auto oldThemeColor = themeColor();
3852-
m_metaElementThemeColor = CSSParser::parseColor(themeColorString);
3866+
// If the current content color isn't valid and it wasn't previously in the list of elements
3867+
// with a valid content color, don't bother recalculating `m_metaThemeColorElements`.
3868+
if (!metaElement.contentColor().isValid() && m_metaThemeColorElements && !m_metaThemeColorElements->contains(&metaElement))
3869+
return;
3870+
3871+
auto oldThemeColor = std::exchange(m_cachedThemeColor, Color());
3872+
m_metaThemeColorElements = WTF::nullopt;
3873+
m_activeThemeColorMetaElement = nullptr;
38533874
if (themeColor() == oldThemeColor)
38543875
return;
38553876

38563877
themeColorChanged();
38573878
}
38583879

3880+
WeakPtr<HTMLMetaElement> Document::determineActiveThemeColorMetaElement()
3881+
{
3882+
if (!m_metaThemeColorElements) {
3883+
Vector<WeakPtr<HTMLMetaElement>> metaThemeColorElements;
3884+
for (auto& metaElement : descendantsOfType<HTMLMetaElement>(*this)) {
3885+
if (equalLettersIgnoringASCIICase(metaElement.name(), "theme-color") && metaElement.contentColor().isValid())
3886+
metaThemeColorElements.append(makeWeakPtr(metaElement));
3887+
}
3888+
m_metaThemeColorElements = WTFMove(metaThemeColorElements);
3889+
}
3890+
3891+
for (auto& metaElement : *m_metaThemeColorElements) {
3892+
if (metaElement && metaElement->contentColor().isValid() && metaElement->mediaAttributeMatches())
3893+
return metaElement;
3894+
}
3895+
return nullptr;
3896+
}
3897+
38593898
void Document::themeColorChanged()
38603899
{
38613900
scheduleRenderingUpdate({ });
@@ -4170,7 +4209,7 @@ void Document::processReferrerPolicy(const String& policy, ReferrerPolicySource
41704209

41714210
void Document::processApplicationManifest(const ApplicationManifest& applicationManifest)
41724211
{
4173-
auto oldThemeColor = themeColor();
4212+
auto oldThemeColor = std::exchange(m_cachedThemeColor, Color());
41744213
m_applicationManifestThemeColor = applicationManifest.themeColor;
41754214
if (themeColor() == oldThemeColor)
41764215
return;
@@ -4356,6 +4395,13 @@ void Document::updateElementsAffectedByMediaQueries()
43564395
{
43574396
ScriptDisallowedScope::InMainThread scriptDisallowedScope;
43584397

4398+
if (auto activeThemeColorElement = determineActiveThemeColorMetaElement(); m_activeThemeColorMetaElement != activeThemeColorElement) {
4399+
auto oldThemeColor = std::exchange(m_cachedThemeColor, Color());
4400+
m_activeThemeColorMetaElement = WTFMove(activeThemeColorElement);
4401+
if (themeColor() != oldThemeColor)
4402+
themeColorChanged();
4403+
}
4404+
43594405
// FIXME: copyToVector doesn't work with WeakHashSet
43604406
Vector<Ref<HTMLImageElement>> images;
43614407
images.reserveInitialCapacity(m_dynamicMediaQueryDependentImages.computeSize());

Source/WebCore/dom/Document.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ class HTMLIFrameElement;
158158
class HTMLImageElement;
159159
class HTMLMapElement;
160160
class HTMLMediaElement;
161+
class HTMLMetaElement;
161162
class HTMLVideoElement;
162163
class HighlightRegister;
163164
class HitTestLocation;
@@ -741,7 +742,7 @@ class Document
741742
Seconds timeSinceDocumentCreation() const { return MonotonicTime::now() - m_documentCreationTime; };
742743
#endif
743744

744-
const Color& themeColor() const { return m_metaElementThemeColor.isValid() ? m_metaElementThemeColor : m_applicationManifestThemeColor; }
745+
const Color& themeColor();
745746

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

@@ -912,7 +913,8 @@ class Document
912913
void processDisabledAdaptations(const String& adaptations);
913914
void updateViewportArguments();
914915
void processReferrerPolicy(const String& policy, ReferrerPolicySource);
915-
void processMetaElementThemeColor(const String& themeColor);
916+
917+
void metaElementThemeColorChanged(HTMLMetaElement&);
916918

917919
#if ENABLE(DARK_MODE_CSS)
918920
void processColorScheme(const String& colorScheme);
@@ -1672,6 +1674,7 @@ class Document
16721674
void updateTitle(const StringWithDirection&);
16731675
void updateBaseURL();
16741676

1677+
WeakPtr<HTMLMetaElement> determineActiveThemeColorMetaElement();
16751678
void themeColorChanged();
16761679

16771680
void determineSampledPageTopColor();
@@ -1794,7 +1797,9 @@ class Document
17941797

17951798
std::unique_ptr<FormController> m_formController;
17961799

1797-
Color m_metaElementThemeColor;
1800+
Color m_cachedThemeColor;
1801+
Optional<Vector<WeakPtr<HTMLMetaElement>>> m_metaThemeColorElements;
1802+
WeakPtr<HTMLMetaElement> m_activeThemeColorMetaElement;
17981803
Color m_applicationManifestThemeColor;
17991804

18001805
Color m_sampledPageTopColor;

Source/WebCore/html/HTMLMetaElement.cpp

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,18 @@
2424
#include "HTMLMetaElement.h"
2525

2626
#include "Attribute.h"
27+
#include "Color.h"
2728
#include "Document.h"
2829
#include "HTMLHeadElement.h"
2930
#include "HTMLNames.h"
31+
#include "MediaList.h"
32+
#include "MediaQueryEvaluator.h"
33+
#include "MediaQueryParser.h"
34+
#include "RenderStyle.h"
3035
#include "Settings.h"
36+
#include "StyleResolveForDocument.h"
3137
#include <wtf/IsoMallocInlines.h>
38+
#include <wtf/Optional.h>
3239

3340
namespace WebCore {
3441

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

62+
bool HTMLMetaElement::mediaAttributeMatches()
63+
{
64+
auto& document = this->document();
65+
66+
if (!m_media)
67+
m_media = MediaQuerySet::create(attributeWithoutSynchronization(mediaAttr).convertToASCIILowercase(), MediaQueryParserContext(document));
68+
69+
Optional<RenderStyle> documentStyle;
70+
if (document.hasLivingRenderTree())
71+
documentStyle = Style::resolveForDocument(document);
72+
73+
String mediaType;
74+
if (auto* frame = document.frame()) {
75+
if (auto* frameView = frame->view())
76+
mediaType = frameView->mediaType();
77+
}
78+
79+
return MediaQueryEvaluator(mediaType, document, documentStyle ? &*documentStyle : nullptr).evaluate(*m_media);
80+
}
81+
82+
const Color& HTMLMetaElement::contentColor()
83+
{
84+
if (!m_contentColor)
85+
m_contentColor = CSSParser::parseColor(content());
86+
return *m_contentColor;
87+
}
88+
5589
void HTMLMetaElement::attributeChanged(const QualifiedName& name, const AtomString& oldValue, const AtomString& newValue, AttributeModificationReason reason)
5690
{
5791
HTMLElement::attributeChanged(name, oldValue, newValue, reason);
5892

59-
if (name == nameAttr && equalLettersIgnoringASCIICase(oldValue, "theme-color") && !equalLettersIgnoringASCIICase(newValue, "theme-color"))
60-
document().processMetaElementThemeColor(emptyString());
93+
if (!isConnected())
94+
return;
95+
96+
if (name == nameAttr) {
97+
if (equalLettersIgnoringASCIICase(oldValue, "theme-color") && !equalLettersIgnoringASCIICase(newValue, "theme-color"))
98+
document().metaElementThemeColorChanged(*this);
99+
return;
100+
}
61101
}
62102

63103
void HTMLMetaElement::parseAttribute(const QualifiedName& name, const AtomString& value)
64104
{
65-
if (name == http_equivAttr)
105+
if (name == nameAttr) {
66106
process();
67-
else if (name == contentAttr)
107+
return;
108+
}
109+
110+
if (name == contentAttr) {
111+
m_contentColor = WTF::nullopt;
68112
process();
69-
else if (name == nameAttr)
113+
return;
114+
}
115+
116+
if (name == http_equivAttr) {
70117
process();
71-
else
72-
HTMLElement::parseAttribute(name, value);
118+
return;
119+
}
120+
121+
if (name == mediaAttr) {
122+
m_media = nullptr;
123+
process();
124+
return;
125+
}
126+
127+
HTMLElement::parseAttribute(name, value);
73128
}
74129

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

92-
if (!isConnected() && equalLettersIgnoringASCIICase(name(), "theme-color"))
93-
oldParentOfRemovedTree.document().processMetaElementThemeColor(emptyString());
147+
if (removalType.disconnectedFromDocument && equalLettersIgnoringASCIICase(name(), "theme-color"))
148+
oldParentOfRemovedTree.document().metaElementThemeColorChanged(*this);
94149
}
95150

96151
void HTMLMetaElement::process()
@@ -112,7 +167,7 @@ void HTMLMetaElement::process()
112167
document().processColorScheme(contentValue);
113168
#endif
114169
else if (equalLettersIgnoringASCIICase(name(), "theme-color"))
115-
document().processMetaElementThemeColor(contentValue);
170+
document().metaElementThemeColorChanged(*this);
116171
#if PLATFORM(IOS_FAMILY)
117172
else if (equalLettersIgnoringASCIICase(name(), "format-detection"))
118173
document().processFormatDetection(contentValue);

Source/WebCore/html/HTMLMetaElement.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626

2727
namespace WebCore {
2828

29+
class Color;
30+
class MediaQuerySet;
31+
2932
class HTMLMetaElement final : public HTMLElement {
3033
WTF_MAKE_ISO_ALLOCATED(HTMLMetaElement);
3134
public:
@@ -36,6 +39,10 @@ class HTMLMetaElement final : public HTMLElement {
3639
const AtomString& httpEquiv() const;
3740
const AtomString& name() const;
3841

42+
bool mediaAttributeMatches();
43+
44+
const Color& contentColor();
45+
3946
private:
4047
HTMLMetaElement(const QualifiedName&, Document&);
4148

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

4855
void process();
56+
57+
RefPtr<MediaQuerySet> m_media;
58+
59+
Optional<Color> m_contentColor;
4960
};
5061

5162
} // namespace WebCore

Source/WebCore/html/HTMLMetaElement.idl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
] interface HTMLMetaElement : HTMLElement {
2323
[CEReactions=NotNeeded, Reflect] attribute DOMString content;
2424
[CEReactions=NotNeeded, Reflect=http_equiv] attribute DOMString httpEquiv;
25+
[CEReactions=NotNeeded, Reflect] attribute DOMString media;
2526
[CEReactions=NotNeeded, Reflect] attribute DOMString name;
2627
[CEReactions=NotNeeded, Reflect] attribute DOMString scheme;
2728
};

Tools/ChangeLog

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
2021-05-10 Devin Rousso <drousso@apple.com>
2+
3+
Add support for a `media` attribute on `<meta name="theme-color" content="...">`
4+
https://bugs.webkit.org/show_bug.cgi?id=224389
5+
<rdar://problem/74991621>
6+
7+
Reviewed by Ryosuke Niwa.
8+
9+
* TestWebKitAPI/Tests/WebKitCocoa/WKWebViewThemeColor.mm:
10+
(TEST.WKWebViewThemeColor.MetaElementValidNameAndColor): Added.
11+
(TEST.WKWebViewThemeColor.MetaElementValidNameAndColorAndMedia): Added.
12+
(TEST.WKWebViewThemeColor.MetaElementInvalidName): Added.
13+
(TEST.WKWebViewThemeColor.MetaElementInvalidColor): Added.
14+
(TEST.WKWebViewThemeColor.MetaElementInvalidMedia): Added.
15+
(TEST.WKWebViewThemeColor.MetaElementMultipleValid): Added.
16+
(TEST.WKWebViewThemeColor.MetaElementValidSubframe): Added.
17+
(-[WKWebViewThemeColorObserver observeValueForKeyPath:ofObject:change:context:]):
18+
(TEST.WKWebViewThemeColor.KVO):
19+
(TEST.WKWebViewThemeColor.MetaElementOnLoad): Deleted.
20+
(TEST.WKWebViewThemeColor.MetaElementMultipleTags): Deleted.
21+
122
2021-05-09 Darin Adler <darin@apple.com>
223

324
Remove all remaining uses of the String::toInt family of functions

0 commit comments

Comments
 (0)