1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
5 * Copyright (C) 2005-2017 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
10 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
11 * Copyright (C) 2012 Google Inc. All rights reserved.
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Library General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Library General Public License for more details.
22 *
23 * You should have received a copy of the GNU Library General Public License
24 * along with this library; see the file COPYING.LIB. If not, write to
25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 * Boston, MA 02110-1301, USA.
27 */
28
29#include "config.h"
30#include "CSSDefaultStyleSheets.h"
31
32#include "Chrome.h"
33#include "ChromeClient.h"
34#include "FullscreenManager.h"
35#include "HTMLAnchorElement.h"
36#include "HTMLBRElement.h"
37#include "HTMLBodyElement.h"
38#include "HTMLDataListElement.h"
39#include "HTMLDivElement.h"
40#include "HTMLEmbedElement.h"
41#include "HTMLHeadElement.h"
42#include "HTMLHtmlElement.h"
43#include "HTMLInputElement.h"
44#include "HTMLMediaElement.h"
45#include "HTMLObjectElement.h"
46#include "HTMLSpanElement.h"
47#include "MathMLElement.h"
48#include "MediaQueryEvaluator.h"
49#include "Page.h"
50#include "RenderTheme.h"
51#include "RuleSet.h"
52#include "SVGElement.h"
53#include "StyleSheetContents.h"
54#include "UserAgentStyleSheets.h"
55#include <wtf/NeverDestroyed.h>
56
57namespace WebCore {
58
59using namespace HTMLNames;
60
61RuleSet* CSSDefaultStyleSheets::defaultStyle;
62RuleSet* CSSDefaultStyleSheets::defaultQuirksStyle;
63RuleSet* CSSDefaultStyleSheets::defaultPrintStyle;
64unsigned CSSDefaultStyleSheets::defaultStyleVersion;
65
66StyleSheetContents* CSSDefaultStyleSheets::simpleDefaultStyleSheet;
67StyleSheetContents* CSSDefaultStyleSheets::defaultStyleSheet;
68StyleSheetContents* CSSDefaultStyleSheets::quirksStyleSheet;
69StyleSheetContents* CSSDefaultStyleSheets::svgStyleSheet;
70StyleSheetContents* CSSDefaultStyleSheets::mathMLStyleSheet;
71StyleSheetContents* CSSDefaultStyleSheets::mediaControlsStyleSheet;
72StyleSheetContents* CSSDefaultStyleSheets::fullscreenStyleSheet;
73StyleSheetContents* CSSDefaultStyleSheets::plugInsStyleSheet;
74StyleSheetContents* CSSDefaultStyleSheets::imageControlsStyleSheet;
75StyleSheetContents* CSSDefaultStyleSheets::mediaQueryStyleSheet;
76#if ENABLE(DATALIST_ELEMENT)
77StyleSheetContents* CSSDefaultStyleSheets::dataListStyleSheet;
78#endif
79#if ENABLE(INPUT_TYPE_COLOR)
80StyleSheetContents* CSSDefaultStyleSheets::colorInputStyleSheet;
81#endif
82
83// FIXME: It would be nice to use some mechanism that guarantees this is in sync with the real UA stylesheet.
84#if HAVE(OS_DARK_MODE_SUPPORT)
85// The only difference in the simple style sheet for dark mode is the addition of html{color:text}.
86static const char* simpleUserAgentStyleSheet = "html,body,div{display:block}html{color:text}head{display:none}body{margin:8px}div:focus,span:focus,a:focus{outline:auto 5px -webkit-focus-ring-color}a:any-link{color:-webkit-link;text-decoration:underline}a:any-link:active{color:-webkit-activelink}";
87#else
88static const char* simpleUserAgentStyleSheet = "html,body,div{display:block}head{display:none}body{margin:8px}div:focus,span:focus,a:focus{outline:auto 5px -webkit-focus-ring-color}a:any-link{color:-webkit-link;text-decoration:underline}a:any-link:active{color:-webkit-activelink}";
89#endif
90
91static inline bool elementCanUseSimpleDefaultStyle(const Element& element)
92{
93 return is<HTMLHtmlElement>(element) || is<HTMLHeadElement>(element)
94 || is<HTMLBodyElement>(element) || is<HTMLDivElement>(element)
95 || is<HTMLSpanElement>(element) || is<HTMLBRElement>(element)
96 || is<HTMLAnchorElement>(element);
97}
98
99static const MediaQueryEvaluator& screenEval()
100{
101 static NeverDestroyed<const MediaQueryEvaluator> staticScreenEval(String(MAKE_STATIC_STRING_IMPL("screen")));
102 return staticScreenEval;
103}
104
105static const MediaQueryEvaluator& printEval()
106{
107 static NeverDestroyed<const MediaQueryEvaluator> staticPrintEval(String(MAKE_STATIC_STRING_IMPL("print")));
108 return staticPrintEval;
109}
110
111static StyleSheetContents* parseUASheet(const String& str)
112{
113 StyleSheetContents& sheet = StyleSheetContents::create(CSSParserContext(UASheetMode)).leakRef(); // leak the sheet on purpose
114 sheet.parseString(str);
115 return &sheet;
116}
117
118static StyleSheetContents* parseUASheet(const char* characters, unsigned size)
119{
120 return parseUASheet(String(characters, size));
121}
122
123void CSSDefaultStyleSheets::initDefaultStyle(const Element* root)
124{
125 if (!defaultStyle) {
126 if (!root || elementCanUseSimpleDefaultStyle(*root))
127 loadSimpleDefaultStyle();
128 else
129 loadFullDefaultStyle();
130 }
131}
132
133void CSSDefaultStyleSheets::addToDefaultStyle(StyleSheetContents& sheet)
134{
135 defaultStyle->addRulesFromSheet(sheet, screenEval());
136 defaultPrintStyle->addRulesFromSheet(sheet, printEval());
137
138 // Build a stylesheet consisting of non-trivial media queries seen in default style.
139 // Rulesets for these can't be global and need to be built in document context.
140 for (auto& rule : sheet.childRules()) {
141 if (!is<StyleRuleMedia>(*rule))
142 continue;
143 auto& mediaRule = downcast<StyleRuleMedia>(*rule);
144 auto* mediaQuery = mediaRule.mediaQueries();
145 if (!mediaQuery)
146 continue;
147 if (screenEval().evaluate(*mediaQuery, nullptr))
148 continue;
149 if (printEval().evaluate(*mediaQuery, nullptr))
150 continue;
151 mediaQueryStyleSheet->parserAppendRule(mediaRule.copy());
152 }
153
154 ++defaultStyleVersion;
155}
156
157void CSSDefaultStyleSheets::loadFullDefaultStyle()
158{
159 if (defaultStyle && !simpleDefaultStyleSheet)
160 return;
161
162 if (simpleDefaultStyleSheet) {
163 ASSERT(defaultStyle);
164 ASSERT(defaultPrintStyle == defaultStyle);
165 delete defaultStyle;
166 simpleDefaultStyleSheet->deref();
167 simpleDefaultStyleSheet = nullptr;
168 } else {
169 ASSERT(!defaultStyle);
170 defaultQuirksStyle = std::make_unique<RuleSet>().release();
171 }
172
173 defaultStyle = std::make_unique<RuleSet>().release();
174 defaultPrintStyle = std::make_unique<RuleSet>().release();
175 mediaQueryStyleSheet = &StyleSheetContents::create(CSSParserContext(UASheetMode)).leakRef();
176
177 // Strict-mode rules.
178 String defaultRules = String(htmlUserAgentStyleSheet, sizeof(htmlUserAgentStyleSheet)) + RenderTheme::singleton().extraDefaultStyleSheet();
179 defaultStyleSheet = parseUASheet(defaultRules);
180 addToDefaultStyle(*defaultStyleSheet);
181
182 // Quirks-mode rules.
183 String quirksRules = String(quirksUserAgentStyleSheet, sizeof(quirksUserAgentStyleSheet)) + RenderTheme::singleton().extraQuirksStyleSheet();
184 quirksStyleSheet = parseUASheet(quirksRules);
185 defaultQuirksStyle->addRulesFromSheet(*quirksStyleSheet, screenEval());
186}
187
188void CSSDefaultStyleSheets::loadSimpleDefaultStyle()
189{
190 ASSERT(!defaultStyle);
191 ASSERT(!simpleDefaultStyleSheet);
192
193 defaultStyle = std::make_unique<RuleSet>().release();
194 // There are no media-specific rules in the simple default style.
195 defaultPrintStyle = defaultStyle;
196 defaultQuirksStyle = std::make_unique<RuleSet>().release();
197
198 simpleDefaultStyleSheet = parseUASheet(simpleUserAgentStyleSheet, strlen(simpleUserAgentStyleSheet));
199 defaultStyle->addRulesFromSheet(*simpleDefaultStyleSheet, screenEval());
200 ++defaultStyleVersion;
201 // No need to initialize quirks sheet yet as there are no quirk rules for elements allowed in simple default style.
202}
203
204void CSSDefaultStyleSheets::ensureDefaultStyleSheetsForElement(const Element& element)
205{
206 if (simpleDefaultStyleSheet && !elementCanUseSimpleDefaultStyle(element)) {
207 loadFullDefaultStyle();
208 ++defaultStyleVersion;
209 }
210
211 if (is<HTMLElement>(element)) {
212 if (is<HTMLObjectElement>(element) || is<HTMLEmbedElement>(element)) {
213 if (!plugInsStyleSheet && element.document().page()) {
214 String plugInsRules = RenderTheme::singleton().extraPlugInsStyleSheet() + element.document().page()->chrome().client().plugInExtraStyleSheet();
215 if (plugInsRules.isEmpty())
216 plugInsRules = String(plugInsUserAgentStyleSheet, sizeof(plugInsUserAgentStyleSheet));
217 plugInsStyleSheet = parseUASheet(plugInsRules);
218 addToDefaultStyle(*plugInsStyleSheet);
219 }
220 }
221#if ENABLE(VIDEO)
222 else if (is<HTMLMediaElement>(element)) {
223 if (!mediaControlsStyleSheet) {
224 String mediaRules = RenderTheme::singleton().mediaControlsStyleSheet();
225 if (mediaRules.isEmpty())
226 mediaRules = String(mediaControlsUserAgentStyleSheet, sizeof(mediaControlsUserAgentStyleSheet)) + RenderTheme::singleton().extraMediaControlsStyleSheet();
227 mediaControlsStyleSheet = parseUASheet(mediaRules);
228 addToDefaultStyle(*mediaControlsStyleSheet);
229
230 }
231 }
232#endif // ENABLE(VIDEO)
233#if ENABLE(SERVICE_CONTROLS)
234 else if (is<HTMLDivElement>(element) && element.isImageControlsRootElement()) {
235 if (!imageControlsStyleSheet) {
236 String imageControlsRules = RenderTheme::singleton().imageControlsStyleSheet();
237 imageControlsStyleSheet = parseUASheet(imageControlsRules);
238 addToDefaultStyle(*imageControlsStyleSheet);
239 }
240 }
241#endif // ENABLE(SERVICE_CONTROLS)
242#if ENABLE(DATALIST_ELEMENT)
243 else if (!dataListStyleSheet && is<HTMLDataListElement>(element)) {
244 dataListStyleSheet = parseUASheet(RenderTheme::singleton().dataListStyleSheet());
245 addToDefaultStyle(*dataListStyleSheet);
246 }
247#endif // ENABLE(DATALIST_ELEMENT)
248#if ENABLE(INPUT_TYPE_COLOR)
249 else if (!colorInputStyleSheet && is<HTMLInputElement>(element) && downcast<HTMLInputElement>(element).isColorControl()) {
250 colorInputStyleSheet = parseUASheet(RenderTheme::singleton().colorInputStyleSheet());
251 addToDefaultStyle(*colorInputStyleSheet);
252 }
253#endif // ENABLE(INPUT_TYPE_COLOR)
254 } else if (is<SVGElement>(element)) {
255 if (!svgStyleSheet) {
256 // SVG rules.
257 svgStyleSheet = parseUASheet(svgUserAgentStyleSheet, sizeof(svgUserAgentStyleSheet));
258 addToDefaultStyle(*svgStyleSheet);
259 }
260 }
261#if ENABLE(MATHML)
262 else if (is<MathMLElement>(element)) {
263 if (!mathMLStyleSheet) {
264 // MathML rules.
265 mathMLStyleSheet = parseUASheet(mathmlUserAgentStyleSheet, sizeof(mathmlUserAgentStyleSheet));
266 addToDefaultStyle(*mathMLStyleSheet);
267 }
268 }
269#endif // ENABLE(MATHML)
270
271#if ENABLE(FULLSCREEN_API)
272 if (!fullscreenStyleSheet && element.document().fullscreenManager().isFullscreen()) {
273 String fullscreenRules = String(fullscreenUserAgentStyleSheet, sizeof(fullscreenUserAgentStyleSheet)) + RenderTheme::singleton().extraFullScreenStyleSheet();
274 fullscreenStyleSheet = parseUASheet(fullscreenRules);
275 addToDefaultStyle(*fullscreenStyleSheet);
276 }
277#endif // ENABLE(FULLSCREEN_API)
278
279 ASSERT(defaultStyle->features().idsInRules.isEmpty());
280 ASSERT(mathMLStyleSheet || defaultStyle->features().siblingRules.isEmpty());
281}
282
283} // namespace WebCore
284