<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[277270] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/277270">277270</a></dd>
<dt>Author</dt> <dd>drousso@apple.com</dd>
<dt>Date</dt> <dd>2021-05-10 10:20:11 -0700 (Mon, 10 May 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>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.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredomDocumentcpp">trunk/Source/WebCore/dom/Document.cpp</a></li>
<li><a href="#trunkSourceWebCoredomDocumenth">trunk/Source/WebCore/dom/Document.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLMetaElementcpp">trunk/Source/WebCore/html/HTMLMetaElement.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLMetaElementh">trunk/Source/WebCore/html/HTMLMetaElement.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLMetaElementidl">trunk/Source/WebCore/html/HTMLMetaElement.idl</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKitCocoaWKWebViewThemeColormm">trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewThemeColor.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (277269 => 277270)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2021-05-10 16:57:02 UTC (rev 277269)
+++ trunk/Source/WebCore/ChangeLog      2021-05-10 17:20:11 UTC (rev 277270)
</span><span class="lines">@@ -1,3 +1,55 @@
</span><ins>+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).
+
</ins><span class="cx"> 2021-05-09  Darin Adler  <darin@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Remove all remaining uses of the String::toInt family of functions
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.cpp (277269 => 277270)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.cpp    2021-05-10 16:57:02 UTC (rev 277269)
+++ trunk/Source/WebCore/dom/Document.cpp       2021-05-10 17:20:11 UTC (rev 277270)
</span><span class="lines">@@ -106,6 +106,7 @@
</span><span class="cx"> #include "HTMLInputElement.h"
</span><span class="cx"> #include "HTMLLinkElement.h"
</span><span class="cx"> #include "HTMLMediaElement.h"
</span><ins>+#include "HTMLMetaElement.h"
</ins><span class="cx"> #include "HTMLNameCollection.h"
</span><span class="cx"> #include "HTMLParserIdioms.h"
</span><span class="cx"> #include "HTMLPictureElement.h"
</span><span class="lines">@@ -962,6 +963,20 @@
</span><span class="cx">     return inQuirksMode() ? "BackCompat" : "CSS1Compat";
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+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;
+}
+
</ins><span class="cx"> void Document::resetLinkColor()
</span><span class="cx"> {
</span><span class="cx">     m_linkColor = StyleColor::colorFromKeyword(CSSValueWebkitLink, styleColorOptions(nullptr));
</span><span class="lines">@@ -3846,10 +3861,16 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void Document::processMetaElementThemeColor(const String& themeColorString)
</del><ins>+void Document::metaElementThemeColorChanged(HTMLMetaElement& metaElement)
</ins><span class="cx"> {
</span><del>-    auto oldThemeColor = themeColor();
-    m_metaElementThemeColor = CSSParser::parseColor(themeColorString);
</del><ins>+    // 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;
</ins><span class="cx">     if (themeColor() == oldThemeColor)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -3856,6 +3877,24 @@
</span><span class="cx">     themeColorChanged();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+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;
+}
+
</ins><span class="cx"> void Document::themeColorChanged()
</span><span class="cx"> {
</span><span class="cx">     scheduleRenderingUpdate({ });
</span><span class="lines">@@ -4170,7 +4209,7 @@
</span><span class="cx"> 
</span><span class="cx"> void Document::processApplicationManifest(const ApplicationManifest& applicationManifest)
</span><span class="cx"> {
</span><del>-    auto oldThemeColor = themeColor();
</del><ins>+    auto oldThemeColor = std::exchange(m_cachedThemeColor, Color());
</ins><span class="cx">     m_applicationManifestThemeColor = applicationManifest.themeColor;
</span><span class="cx">     if (themeColor() == oldThemeColor)
</span><span class="cx">         return;
</span><span class="lines">@@ -4356,6 +4395,13 @@
</span><span class="cx"> {
</span><span class="cx">     ScriptDisallowedScope::InMainThread scriptDisallowedScope;
</span><span class="cx"> 
</span><ins>+    if (auto activeThemeColorElement = determineActiveThemeColorMetaElement(); m_activeThemeColorMetaElement != activeThemeColorElement) {
+        auto oldThemeColor = std::exchange(m_cachedThemeColor, Color());
+        m_activeThemeColorMetaElement = WTFMove(activeThemeColorElement);
+        if (themeColor() != oldThemeColor)
+            themeColorChanged();
+    }
+
</ins><span class="cx">     // FIXME: copyToVector doesn't work with WeakHashSet
</span><span class="cx">     Vector<Ref<HTMLImageElement>> images;
</span><span class="cx">     images.reserveInitialCapacity(m_dynamicMediaQueryDependentImages.computeSize());
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumenth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.h (277269 => 277270)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.h      2021-05-10 16:57:02 UTC (rev 277269)
+++ trunk/Source/WebCore/dom/Document.h 2021-05-10 17:20:11 UTC (rev 277270)
</span><span class="lines">@@ -158,6 +158,7 @@
</span><span class="cx"> class HTMLImageElement;
</span><span class="cx"> class HTMLMapElement;
</span><span class="cx"> class HTMLMediaElement;
</span><ins>+class HTMLMetaElement;
</ins><span class="cx"> class HTMLVideoElement;
</span><span class="cx"> class HighlightRegister;
</span><span class="cx"> class HitTestLocation;
</span><span class="lines">@@ -741,7 +742,7 @@
</span><span class="cx">     Seconds timeSinceDocumentCreation() const { return MonotonicTime::now() - m_documentCreationTime; };
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-    const Color& themeColor() const { return m_metaElementThemeColor.isValid() ? m_metaElementThemeColor : m_applicationManifestThemeColor; }
</del><ins>+    const Color& themeColor();
</ins><span class="cx"> 
</span><span class="cx">     const Color& sampledPageTopColor() const { return m_sampledPageTopColor; }
</span><span class="cx"> 
</span><span class="lines">@@ -912,8 +913,9 @@
</span><span class="cx">     void processDisabledAdaptations(const String& adaptations);
</span><span class="cx">     void updateViewportArguments();
</span><span class="cx">     void processReferrerPolicy(const String& policy, ReferrerPolicySource);
</span><del>-    void processMetaElementThemeColor(const String& themeColor);
</del><span class="cx"> 
</span><ins>+    void metaElementThemeColorChanged(HTMLMetaElement&);
+
</ins><span class="cx"> #if ENABLE(DARK_MODE_CSS)
</span><span class="cx">     void processColorScheme(const String& colorScheme);
</span><span class="cx"> #endif
</span><span class="lines">@@ -1672,6 +1674,7 @@
</span><span class="cx">     void updateTitle(const StringWithDirection&);
</span><span class="cx">     void updateBaseURL();
</span><span class="cx"> 
</span><ins>+    WeakPtr<HTMLMetaElement> determineActiveThemeColorMetaElement();
</ins><span class="cx">     void themeColorChanged();
</span><span class="cx"> 
</span><span class="cx">     void determineSampledPageTopColor();
</span><span class="lines">@@ -1794,7 +1797,9 @@
</span><span class="cx"> 
</span><span class="cx">     std::unique_ptr<FormController> m_formController;
</span><span class="cx"> 
</span><del>-    Color m_metaElementThemeColor;
</del><ins>+    Color m_cachedThemeColor;
+    Optional<Vector<WeakPtr<HTMLMetaElement>>> m_metaThemeColorElements;
+    WeakPtr<HTMLMetaElement> m_activeThemeColorMetaElement;
</ins><span class="cx">     Color m_applicationManifestThemeColor;
</span><span class="cx"> 
</span><span class="cx">     Color m_sampledPageTopColor;
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMetaElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMetaElement.cpp (277269 => 277270)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMetaElement.cpp    2021-05-10 16:57:02 UTC (rev 277269)
+++ trunk/Source/WebCore/html/HTMLMetaElement.cpp       2021-05-10 17:20:11 UTC (rev 277270)
</span><span class="lines">@@ -24,11 +24,18 @@
</span><span class="cx"> #include "HTMLMetaElement.h"
</span><span class="cx"> 
</span><span class="cx"> #include "Attribute.h"
</span><ins>+#include "Color.h"
</ins><span class="cx"> #include "Document.h"
</span><span class="cx"> #include "HTMLHeadElement.h"
</span><span class="cx"> #include "HTMLNames.h"
</span><ins>+#include "MediaList.h"
+#include "MediaQueryEvaluator.h"
+#include "MediaQueryParser.h"
+#include "RenderStyle.h"
</ins><span class="cx"> #include "Settings.h"
</span><ins>+#include "StyleResolveForDocument.h"
</ins><span class="cx"> #include <wtf/IsoMallocInlines.h>
</span><ins>+#include <wtf/Optional.h>
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="lines">@@ -52,24 +59,72 @@
</span><span class="cx">     return adoptRef(*new HTMLMetaElement(tagName, document));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+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;
+}
+
</ins><span class="cx"> void HTMLMetaElement::attributeChanged(const QualifiedName& name, const AtomString& oldValue, const AtomString& newValue, AttributeModificationReason reason)
</span><span class="cx"> {
</span><span class="cx">     HTMLElement::attributeChanged(name, oldValue, newValue, reason);
</span><span class="cx"> 
</span><del>-    if (name == nameAttr && equalLettersIgnoringASCIICase(oldValue, "theme-color") && !equalLettersIgnoringASCIICase(newValue, "theme-color"))
-        document().processMetaElementThemeColor(emptyString());
</del><ins>+    if (!isConnected())
+        return;
+
+    if (name == nameAttr) {
+        if (equalLettersIgnoringASCIICase(oldValue, "theme-color") && !equalLettersIgnoringASCIICase(newValue, "theme-color"))
+            document().metaElementThemeColorChanged(*this);
+        return;
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void HTMLMetaElement::parseAttribute(const QualifiedName& name, const AtomString& value)
</span><span class="cx"> {
</span><del>-    if (name == http_equivAttr)
</del><ins>+    if (name == nameAttr) {
</ins><span class="cx">         process();
</span><del>-    else if (name == contentAttr)
</del><ins>+        return;
+    }
+
+    if (name == contentAttr) {
+        m_contentColor = WTF::nullopt;
</ins><span class="cx">         process();
</span><del>-    else if (name == nameAttr)
</del><ins>+        return;
+    }
+
+    if (name == http_equivAttr) {
</ins><span class="cx">         process();
</span><del>-    else
-        HTMLElement::parseAttribute(name, value);
</del><ins>+        return;
+    }
+
+    if (name == mediaAttr) {
+        m_media = nullptr;
+        process();
+        return;
+    }
+
+    HTMLElement::parseAttribute(name, value);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> Node::InsertedIntoAncestorResult HTMLMetaElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
</span><span class="lines">@@ -89,8 +144,8 @@
</span><span class="cx"> {
</span><span class="cx">     HTMLElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
</span><span class="cx"> 
</span><del>-    if (!isConnected() && equalLettersIgnoringASCIICase(name(), "theme-color"))
-        oldParentOfRemovedTree.document().processMetaElementThemeColor(emptyString());
</del><ins>+    if (removalType.disconnectedFromDocument && equalLettersIgnoringASCIICase(name(), "theme-color"))
+        oldParentOfRemovedTree.document().metaElementThemeColorChanged(*this);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void HTMLMetaElement::process()
</span><span class="lines">@@ -112,7 +167,7 @@
</span><span class="cx">         document().processColorScheme(contentValue);
</span><span class="cx"> #endif
</span><span class="cx">     else if (equalLettersIgnoringASCIICase(name(), "theme-color"))
</span><del>-        document().processMetaElementThemeColor(contentValue);
</del><ins>+        document().metaElementThemeColorChanged(*this);
</ins><span class="cx"> #if PLATFORM(IOS_FAMILY)
</span><span class="cx">     else if (equalLettersIgnoringASCIICase(name(), "format-detection"))
</span><span class="cx">         document().processFormatDetection(contentValue);
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMetaElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMetaElement.h (277269 => 277270)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMetaElement.h      2021-05-10 16:57:02 UTC (rev 277269)
+++ trunk/Source/WebCore/html/HTMLMetaElement.h 2021-05-10 17:20:11 UTC (rev 277270)
</span><span class="lines">@@ -26,6 +26,9 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+class Color;
+class MediaQuerySet;
+
</ins><span class="cx"> class HTMLMetaElement final : public HTMLElement {
</span><span class="cx">     WTF_MAKE_ISO_ALLOCATED(HTMLMetaElement);
</span><span class="cx"> public:
</span><span class="lines">@@ -36,6 +39,10 @@
</span><span class="cx">     const AtomString& httpEquiv() const;
</span><span class="cx">     const AtomString& name() const;
</span><span class="cx"> 
</span><ins>+    bool mediaAttributeMatches();
+
+    const Color& contentColor();
+
</ins><span class="cx"> private:
</span><span class="cx">     HTMLMetaElement(const QualifiedName&, Document&);
</span><span class="cx"> 
</span><span class="lines">@@ -46,6 +53,10 @@
</span><span class="cx">     void removedFromAncestor(RemovalType, ContainerNode&) final;
</span><span class="cx"> 
</span><span class="cx">     void process();
</span><ins>+
+    RefPtr<MediaQuerySet> m_media;
+
+    Optional<Color> m_contentColor;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMetaElementidl"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMetaElement.idl (277269 => 277270)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMetaElement.idl    2021-05-10 16:57:02 UTC (rev 277269)
+++ trunk/Source/WebCore/html/HTMLMetaElement.idl       2021-05-10 17:20:11 UTC (rev 277270)
</span><span class="lines">@@ -22,6 +22,7 @@
</span><span class="cx"> ] interface HTMLMetaElement : HTMLElement {
</span><span class="cx">     [CEReactions=NotNeeded, Reflect] attribute DOMString content;
</span><span class="cx">     [CEReactions=NotNeeded, Reflect=http_equiv] attribute DOMString httpEquiv;
</span><ins>+    [CEReactions=NotNeeded, Reflect] attribute DOMString media;
</ins><span class="cx">     [CEReactions=NotNeeded, Reflect] attribute DOMString name;
</span><span class="cx">     [CEReactions=NotNeeded, Reflect] attribute DOMString scheme;
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (277269 => 277270)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog    2021-05-10 16:57:02 UTC (rev 277269)
+++ trunk/Tools/ChangeLog       2021-05-10 17:20:11 UTC (rev 277270)
</span><span class="lines">@@ -1,3 +1,24 @@
</span><ins>+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.
+
</ins><span class="cx"> 2021-05-09  Darin Adler  <darin@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Remove all remaining uses of the String::toInt family of functions
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKitCocoaWKWebViewThemeColormm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewThemeColor.mm (277269 => 277270)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewThemeColor.mm       2021-05-10 16:57:02 UTC (rev 277269)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewThemeColor.mm  2021-05-10 17:20:11 UTC (rev 277270)
</span><span class="lines">@@ -41,7 +41,7 @@
</span><span class="cx"> constexpr CGFloat redColorComponents[4] = { 1, 0, 0, 1 };
</span><span class="cx"> constexpr CGFloat blueColorComponents[4] = { 0, 0, 1, 1 };
</span><span class="cx"> 
</span><del>-TEST(WKWebViewThemeColor, MetaElementOnLoad)
</del><ins>+TEST(WKWebViewThemeColor, MetaElementValidNameAndColor)
</ins><span class="cx"> {
</span><span class="cx">     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
</span><span class="cx">     EXPECT_TRUE(![webView themeColor]);
</span><span class="lines">@@ -51,24 +51,80 @@
</span><span class="cx">     auto sRGBColorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
</span><span class="cx">     auto redColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), redColorComponents));
</span><span class="cx">     EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, redColor.get()));
</span><ins>+}
</ins><span class="cx"> 
</span><del>-    [webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<meta name='not-theme-color' content='red'>"];
</del><ins>+TEST(WKWebViewThemeColor, MetaElementValidNameAndColorAndMedia)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    EXPECT_TRUE(![webView themeColor]);
</ins><span class="cx"> 
</span><ins>+    [webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<meta name='theme-color' content='red' media='screen'>"];
+
+    auto sRGBColorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
+    auto redColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), redColorComponents));
+    EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, redColor.get()));
+}
+
+TEST(WKWebViewThemeColor, MetaElementInvalidName)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
</ins><span class="cx">     EXPECT_TRUE(![webView themeColor]);
</span><ins>+
+    [webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<meta name='not-theme-color' content='blue'><meta name='theme-color' content='red'>"];
+
+    auto sRGBColorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
+    auto redColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), redColorComponents));
+    EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, redColor.get()));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-TEST(WKWebViewThemeColor, MultipleMetaElements)
</del><ins>+TEST(WKWebViewThemeColor, MetaElementInvalidColor)
</ins><span class="cx"> {
</span><span class="cx">     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
</span><span class="cx">     EXPECT_TRUE(![webView themeColor]);
</span><span class="cx"> 
</span><del>-    [webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<meta name='theme-color' content='red'><meta name='theme-color' content='blue'>"];
</del><ins>+    [webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<meta name='theme-color' content='invalid'><meta name='theme-color' content='red'>"];
</ins><span class="cx"> 
</span><span class="cx">     auto sRGBColorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
</span><del>-    auto blueColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), blueColorComponents));
-    EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, blueColor.get()));
</del><ins>+    auto redColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), redColorComponents));
+    EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, redColor.get()));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+TEST(WKWebViewThemeColor, MetaElementInvalidMedia)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    EXPECT_TRUE(![webView themeColor]);
+
+    [webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<meta name='theme-color' content='blue' media='invalid'><meta name='theme-color' content='red' media='screen'>"];
+
+    auto sRGBColorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
+    auto redColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), redColorComponents));
+    EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, redColor.get()));
+}
+
+TEST(WKWebViewThemeColor, MetaElementMultipleValid)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    EXPECT_TRUE(![webView themeColor]);
+
+    [webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<div><meta name='theme-color' content='red'></div><meta name='theme-color' content='blue'>"];
+
+    auto sRGBColorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
+    auto redColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), redColorComponents));
+    EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, redColor.get()));
+}
+
+TEST(WKWebViewThemeColor, MetaElementValidSubframe)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    EXPECT_TRUE(![webView themeColor]);
+
+    [webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:@"<iframe srcdoc=\"<meta name='theme-color' content='blue'>\"></iframe><meta name='theme-color' content='red'>"];
+
+    auto sRGBColorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
+    auto redColor = adoptCF(CGColorCreate(sRGBColorSpace.get(), redColorComponents));
+    EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, redColor.get()));
+}
+
</ins><span class="cx"> @interface WKWebViewThemeColorObserver : NSObject
</span><span class="cx"> 
</span><span class="cx"> - (instancetype)initWithWebView:(WKWebView *)webView;
</span><span class="lines">@@ -105,11 +161,16 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if ([_state isEqualToString:@"before-content-change"]) {
-        _state = @"after-content-change";
</del><ins>+    if ([_state isEqualToString:@"before-content-change-invalid"]) {
+        _state = @"after-content-change-invalid";
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    if ([_state isEqualToString:@"before-content-change-valid"]) {
+        _state = @"after-content-change-valid";
+        return;
+    }
+
</ins><span class="cx">     if ([_state isEqualToString:@"before-name-change-not-theme-color"]) {
</span><span class="cx">         _state = @"after-name-change-not-theme-color";
</span><span class="cx">         return;
</span><span class="lines">@@ -120,6 +181,26 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    if ([_state isEqualToString:@"before-invalid-media-added"]) {
+        _state = @"after-invalid-media-added";
+        return;
+    }
+
+    if ([_state isEqualToString:@"before-media-changed-valid"]) {
+        _state = @"after-media-changed-valid";
+        return;
+    }
+
+    if ([_state isEqualToString:@"before-media-state-changed"]) {
+        _state = @"after-media-state-changed";
+        return;
+    }
+
+    if ([_state isEqualToString:@"before-node-inserted"]) {
+        _state = @"after-node-inserted";
+        return;
+    }
+
</ins><span class="cx">     if ([_state isEqualToString:@"before-node-removed"]) {
</span><span class="cx">         _state = @"after-node-removed";
</span><span class="cx">         return;
</span><span class="lines">@@ -146,10 +227,16 @@
</span><span class="cx">     EXPECT_NSSTRING_EQ("after-load", [themeColorObserver state]);
</span><span class="cx">     EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, redColor.get()));
</span><span class="cx"> 
</span><del>-    [themeColorObserver setState:@"before-content-change"];
</del><ins>+    [themeColorObserver setState:@"before-content-change-invalid"];
+    [webView objectByEvaluatingJavaScript:@"document.querySelector('meta').setAttribute('content', 'invalid')"];
+    [webView waitForNextPresentationUpdate];
+    EXPECT_NSSTRING_EQ("after-content-change-invalid", [themeColorObserver state]);
+    EXPECT_TRUE(![webView themeColor]);
+
+    [themeColorObserver setState:@"before-content-change-valid"];
</ins><span class="cx">     [webView objectByEvaluatingJavaScript:@"document.querySelector('meta').setAttribute('content', 'blue')"];
</span><span class="cx">     [webView waitForNextPresentationUpdate];
</span><del>-    EXPECT_NSSTRING_EQ("after-content-change", [themeColorObserver state]);
</del><ins>+    EXPECT_NSSTRING_EQ("after-content-change-valid", [themeColorObserver state]);
</ins><span class="cx">     EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, blueColor.get()));
</span><span class="cx"> 
</span><span class="cx">     [themeColorObserver setState:@"before-name-change-not-theme-color"];
</span><span class="lines">@@ -164,6 +251,30 @@
</span><span class="cx">     EXPECT_NSSTRING_EQ("after-name-change-theme-color", [themeColorObserver state]);
</span><span class="cx">     EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, blueColor.get()));
</span><span class="cx"> 
</span><ins>+    [themeColorObserver setState:@"before-invalid-media-added"];
+    [webView objectByEvaluatingJavaScript:@"document.querySelector('meta').setAttribute('media', 'invalid')"];
+    [webView waitForNextPresentationUpdate];
+    EXPECT_NSSTRING_EQ("after-invalid-media-added", [themeColorObserver state]);
+    EXPECT_TRUE(![webView themeColor]);
+
+    [themeColorObserver setState:@"before-media-changed-valid"];
+    [webView objectByEvaluatingJavaScript:@"document.querySelector('meta').setAttribute('media', 'screen')"];
+    [webView waitForNextPresentationUpdate];
+    EXPECT_NSSTRING_EQ("after-media-changed-valid", [themeColorObserver state]);
+    EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, blueColor.get()));
+
+    [themeColorObserver setState:@"before-media-state-changed"];
+    [webView setMediaType:@"print"];
+    [webView waitForNextPresentationUpdate];
+    EXPECT_NSSTRING_EQ("after-media-state-changed", [themeColorObserver state]);
+    EXPECT_TRUE(![webView themeColor]);
+
+    [themeColorObserver setState:@"before-node-inserted"];
+    [webView objectByEvaluatingJavaScript:@"document.querySelector('meta').insertAdjacentHTML('beforebegin', \"<meta name='theme-color' content='red'>\")"];
+    [webView waitForNextPresentationUpdate];
+    EXPECT_NSSTRING_EQ("after-node-inserted", [themeColorObserver state]);
+    EXPECT_TRUE(CGColorEqualToColor([webView themeColor].CGColor, redColor.get()));
+
</ins><span class="cx">     [themeColorObserver setState:@"before-node-removed"];
</span><span class="cx">     [webView objectByEvaluatingJavaScript:@"document.querySelector('meta').remove()"];
</span><span class="cx">     [webView waitForNextPresentationUpdate];
</span></span></pre>
</div>
</div>

</body>
</html>