1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012 Apple Inc. All
7 * rights reserved.
8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved.
9 * (http://www.torchmobile.com/)
10 * Copyright (C) 2008, 2009, 2011, 2012 Google Inc. All rights reserved.
11 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
12 * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Library General Public
16 * License as published by the Free Software Foundation; either
17 * version 2 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public License
25 * along with this library; see the file COPYING.LIB. If not, write to
26 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27 * Boston, MA 02110-1301, USA.
28 */
29
30 #include "third_party/blink/renderer/core/css/style_engine.h"
31
32 #include "third_party/blink/public/mojom/frame/color_scheme.mojom-blink.h"
33 #include "third_party/blink/public/platform/platform.h"
34 #include "third_party/blink/public/platform/web_theme_engine.h"
35 #include "third_party/blink/renderer/core/css/css_default_style_sheets.h"
36 #include "third_party/blink/renderer/core/css/css_font_family_value.h"
37 #include "third_party/blink/renderer/core/css/css_font_selector.h"
38 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
39 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
40 #include "third_party/blink/renderer/core/css/css_uri_value.h"
41 #include "third_party/blink/renderer/core/css/css_value_list.h"
42 #include "third_party/blink/renderer/core/css/document_style_environment_variables.h"
43 #include "third_party/blink/renderer/core/css/document_style_sheet_collector.h"
44 #include "third_party/blink/renderer/core/css/font_face_cache.h"
45 #include "third_party/blink/renderer/core/css/invalidation/invalidation_set.h"
46 #include "third_party/blink/renderer/core/css/media_feature_overrides.h"
47 #include "third_party/blink/renderer/core/css/media_values.h"
48 #include "third_party/blink/renderer/core/css/property_registration.h"
49 #include "third_party/blink/renderer/core/css/property_registry.h"
50 #include "third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h"
51 #include "third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope.h"
52 #include "third_party/blink/renderer/core/css/resolver/style_rule_usage_tracker.h"
53 #include "third_party/blink/renderer/core/css/resolver/viewport_style_resolver.h"
54 #include "third_party/blink/renderer/core/css/shadow_tree_style_sheet_collection.h"
55 #include "third_party/blink/renderer/core/css/style_change_reason.h"
56 #include "third_party/blink/renderer/core/css/style_environment_variables.h"
57 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
58 #include "third_party/blink/renderer/core/css/vision_deficiency.h"
59 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
60 #include "third_party/blink/renderer/core/dom/document_lifecycle.h"
61 #include "third_party/blink/renderer/core/dom/element.h"
62 #include "third_party/blink/renderer/core/dom/element_traversal.h"
63 #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
64 #include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
65 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
66 #include "third_party/blink/renderer/core/dom/nth_index_cache.h"
67 #include "third_party/blink/renderer/core/dom/processing_instruction.h"
68 #include "third_party/blink/renderer/core/dom/shadow_root.h"
69 #include "third_party/blink/renderer/core/dom/text.h"
70 #include "third_party/blink/renderer/core/frame/settings.h"
71 #include "third_party/blink/renderer/core/html/html_body_element.h"
72 #include "third_party/blink/renderer/core/html/html_html_element.h"
73 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
74 #include "third_party/blink/renderer/core/html/html_link_element.h"
75 #include "third_party/blink/renderer/core/html/html_slot_element.h"
76 #include "third_party/blink/renderer/core/html/imports/html_imports_controller.h"
77 #include "third_party/blink/renderer/core/html_names.h"
78 #include "third_party/blink/renderer/core/layout/layout_object.h"
79 #include "third_party/blink/renderer/core/layout/layout_theme.h"
80 #include "third_party/blink/renderer/core/layout/layout_view.h"
81 #include "third_party/blink/renderer/core/page/page.h"
82 #include "third_party/blink/renderer/core/page/page_popup_controller.h"
83 #include "third_party/blink/renderer/core/paint/paint_layer.h"
84 #include "third_party/blink/renderer/core/probe/core_probes.h"
85 #include "third_party/blink/renderer/core/style/computed_style.h"
86 #include "third_party/blink/renderer/core/style/filter_operations.h"
87 #include "third_party/blink/renderer/core/style/style_initial_data.h"
88 #include "third_party/blink/renderer/core/svg/svg_resource.h"
89 #include "third_party/blink/renderer/core/svg/svg_style_element.h"
90 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
91 #include "third_party/blink/renderer/platform/fonts/font_selector.h"
92 #include "third_party/blink/renderer/platform/heap/heap.h"
93 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
94 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
95 #include "third_party/blink/renderer/platform/wtf/vector.h"
96
97 namespace blink {
98
99 namespace {
100
CreateCSSFontSelectorFor(Document & document)101 CSSFontSelector* CreateCSSFontSelectorFor(Document& document) {
102 DCHECK(document.GetFrame());
103 if (UNLIKELY(document.GetFrame()->PagePopupOwner())) {
104 return PagePopupController::CreateCSSFontSelector(document);
105 }
106 return MakeGarbageCollected<CSSFontSelector>(&document);
107 }
108
109 } // namespace
110
StyleEngine(Document & document)111 StyleEngine::StyleEngine(Document& document)
112 : document_(&document),
113 is_html_import_(document.IsHTMLImport()),
114 document_style_sheet_collection_(
115 MakeGarbageCollected<DocumentStyleSheetCollection>(document)),
116 owner_color_scheme_(mojom::blink::ColorScheme::kLight) {
117 if (document.GetFrame()) {
118 // We don't need to create CSSFontSelector for imported document or
119 // HTMLTemplateElement's document, because those documents have no frame.
120 // Likewise for the StyleResolver.
121 font_selector_ = CreateCSSFontSelectorFor(document);
122 font_selector_->RegisterForInvalidationCallbacks(this);
123 resolver_ = MakeGarbageCollected<StyleResolver>(document);
124 if (const auto* owner = document.GetFrame()->Owner())
125 owner_color_scheme_ = owner->GetColorScheme();
126 }
127 if (document.IsInMainFrame())
128 viewport_resolver_ = MakeGarbageCollected<ViewportStyleResolver>(document);
129 if (!IsHTMLImport())
130 global_rule_set_ = MakeGarbageCollected<CSSGlobalRuleSet>();
131 if (auto* settings = GetDocument().GetSettings()) {
132 if (!settings->GetForceDarkModeEnabled())
133 preferred_color_scheme_ = settings->GetPreferredColorScheme();
134 UpdateColorSchemeMetrics();
135 }
136 if (Platform::Current() && Platform::Current()->ThemeEngine())
137 forced_colors_ = Platform::Current()->ThemeEngine()->GetForcedColors();
138 UpdateForcedBackgroundColor();
139 }
140
141 StyleEngine::~StyleEngine() = default;
142
HTMLImportRootDocument()143 inline Document* StyleEngine::HTMLImportRootDocument() {
144 if (!IsHTMLImport())
145 return document_;
146 HTMLImportsController* import = GetDocument().ImportsController();
147 // Document::ImportsController() can return null while executing its
148 // destructor.
149 if (!import)
150 return nullptr;
151 return import->TreeRoot();
152 }
153
EnsureStyleSheetCollectionFor(TreeScope & tree_scope)154 TreeScopeStyleSheetCollection& StyleEngine::EnsureStyleSheetCollectionFor(
155 TreeScope& tree_scope) {
156 if (tree_scope == document_)
157 return GetDocumentStyleSheetCollection();
158
159 StyleSheetCollectionMap::AddResult result =
160 style_sheet_collection_map_.insert(&tree_scope, nullptr);
161 if (result.is_new_entry) {
162 result.stored_value->value =
163 MakeGarbageCollected<ShadowTreeStyleSheetCollection>(
164 To<ShadowRoot>(tree_scope));
165 }
166 return *result.stored_value->value.Get();
167 }
168
StyleSheetCollectionFor(TreeScope & tree_scope)169 TreeScopeStyleSheetCollection* StyleEngine::StyleSheetCollectionFor(
170 TreeScope& tree_scope) {
171 if (tree_scope == document_)
172 return &GetDocumentStyleSheetCollection();
173
174 StyleSheetCollectionMap::iterator it =
175 style_sheet_collection_map_.find(&tree_scope);
176 if (it == style_sheet_collection_map_.end())
177 return nullptr;
178 return it->value.Get();
179 }
180
StyleSheetsForStyleSheetList(TreeScope & tree_scope)181 const HeapVector<Member<StyleSheet>>& StyleEngine::StyleSheetsForStyleSheetList(
182 TreeScope& tree_scope) {
183 DCHECK(HTMLImportRootDocument());
184 TreeScopeStyleSheetCollection& collection =
185 EnsureStyleSheetCollectionFor(tree_scope);
186 if (HTMLImportRootDocument()->IsActive())
187 collection.UpdateStyleSheetList();
188 return collection.StyleSheetsForStyleSheetList();
189 }
190
InjectSheet(const StyleSheetKey & key,StyleSheetContents * sheet,WebDocument::CSSOrigin origin)191 void StyleEngine::InjectSheet(const StyleSheetKey& key,
192 StyleSheetContents* sheet,
193 WebDocument::CSSOrigin origin) {
194 HeapVector<std::pair<StyleSheetKey, Member<CSSStyleSheet>>>&
195 injected_style_sheets =
196 origin == WebDocument::kUserOrigin ? injected_user_style_sheets_
197 : injected_author_style_sheets_;
198 injected_style_sheets.push_back(std::make_pair(
199 key, MakeGarbageCollected<CSSStyleSheet>(sheet, *document_)));
200 if (origin == WebDocument::kUserOrigin)
201 MarkUserStyleDirty();
202 else
203 MarkDocumentDirty();
204 }
205
RemoveInjectedSheet(const StyleSheetKey & key,WebDocument::CSSOrigin origin)206 void StyleEngine::RemoveInjectedSheet(const StyleSheetKey& key,
207 WebDocument::CSSOrigin origin) {
208 HeapVector<std::pair<StyleSheetKey, Member<CSSStyleSheet>>>&
209 injected_style_sheets =
210 origin == WebDocument::kUserOrigin ? injected_user_style_sheets_
211 : injected_author_style_sheets_;
212 // Remove the last sheet that matches.
213 const auto& it = std::find_if(injected_style_sheets.rbegin(),
214 injected_style_sheets.rend(),
215 [&key](const auto& item) {
216 return item.first == key;
217 });
218 if (it != injected_style_sheets.rend()) {
219 injected_style_sheets.erase(std::next(it).base());
220 if (origin == WebDocument::kUserOrigin)
221 MarkUserStyleDirty();
222 else
223 MarkDocumentDirty();
224 }
225 }
226
EnsureInspectorStyleSheet()227 CSSStyleSheet& StyleEngine::EnsureInspectorStyleSheet() {
228 if (inspector_style_sheet_)
229 return *inspector_style_sheet_;
230
231 auto* contents = MakeGarbageCollected<StyleSheetContents>(
232 MakeGarbageCollected<CSSParserContext>(*document_));
233 inspector_style_sheet_ =
234 MakeGarbageCollected<CSSStyleSheet>(contents, *document_);
235 MarkDocumentDirty();
236 // TODO(futhark@chromium.org): Making the active stylesheets up-to-date here
237 // is required by some inspector tests, at least. I theory this should not be
238 // necessary. Need to investigate to figure out if/why.
239 UpdateActiveStyle();
240 return *inspector_style_sheet_;
241 }
242
AddPendingSheet(StyleEngineContext & context)243 void StyleEngine::AddPendingSheet(StyleEngineContext& context) {
244 pending_script_blocking_stylesheets_++;
245
246 context.AddingPendingSheet(GetDocument());
247
248 if (context.AddedPendingSheetBeforeBody() &&
249 !RuntimeEnabledFeatures::BlockHTMLParserOnStyleSheetsEnabled()) {
250 pending_render_blocking_stylesheets_++;
251 } else {
252 pending_parser_blocking_stylesheets_++;
253 GetDocument().DidAddPendingParserBlockingStylesheet();
254 }
255 }
256
257 // This method is called whenever a top-level stylesheet has finished loading.
RemovePendingSheet(Node & style_sheet_candidate_node,const StyleEngineContext & context)258 void StyleEngine::RemovePendingSheet(Node& style_sheet_candidate_node,
259 const StyleEngineContext& context) {
260 if (style_sheet_candidate_node.isConnected())
261 SetNeedsActiveStyleUpdate(style_sheet_candidate_node.GetTreeScope());
262
263 if (context.AddedPendingSheetBeforeBody() &&
264 !RuntimeEnabledFeatures::BlockHTMLParserOnStyleSheetsEnabled()) {
265 DCHECK_GT(pending_render_blocking_stylesheets_, 0);
266 pending_render_blocking_stylesheets_--;
267 } else {
268 DCHECK_GT(pending_parser_blocking_stylesheets_, 0);
269 pending_parser_blocking_stylesheets_--;
270 if (!pending_parser_blocking_stylesheets_)
271 GetDocument().DidLoadAllPendingParserBlockingStylesheets();
272 }
273
274 // Make sure we knew this sheet was pending, and that our count isn't out of
275 // sync.
276 DCHECK_GT(pending_script_blocking_stylesheets_, 0);
277
278 pending_script_blocking_stylesheets_--;
279 if (pending_script_blocking_stylesheets_)
280 return;
281
282 GetDocument().DidRemoveAllPendingStylesheets();
283 }
284
SetNeedsActiveStyleUpdate(TreeScope & tree_scope)285 void StyleEngine::SetNeedsActiveStyleUpdate(TreeScope& tree_scope) {
286 DCHECK(tree_scope.RootNode().isConnected());
287 if (GetDocument().IsActive() || IsHTMLImport())
288 MarkTreeScopeDirty(tree_scope);
289 }
290
AddStyleSheetCandidateNode(Node & node)291 void StyleEngine::AddStyleSheetCandidateNode(Node& node) {
292 if (!node.isConnected() || GetDocument().IsDetached())
293 return;
294
295 DCHECK(!IsXSLStyleSheet(node));
296 TreeScope& tree_scope = node.GetTreeScope();
297 EnsureStyleSheetCollectionFor(tree_scope).AddStyleSheetCandidateNode(node);
298
299 SetNeedsActiveStyleUpdate(tree_scope);
300 if (tree_scope != document_)
301 active_tree_scopes_.insert(&tree_scope);
302 }
303
RemoveStyleSheetCandidateNode(Node & node,ContainerNode & insertion_point)304 void StyleEngine::RemoveStyleSheetCandidateNode(
305 Node& node,
306 ContainerNode& insertion_point) {
307 DCHECK(!IsXSLStyleSheet(node));
308 DCHECK(insertion_point.isConnected());
309
310 ShadowRoot* shadow_root = node.ContainingShadowRoot();
311 if (!shadow_root)
312 shadow_root = insertion_point.ContainingShadowRoot();
313
314 static_assert(std::is_base_of<TreeScope, ShadowRoot>::value,
315 "The ShadowRoot must be subclass of TreeScope.");
316 TreeScope& tree_scope =
317 shadow_root ? static_cast<TreeScope&>(*shadow_root) : GetDocument();
318 TreeScopeStyleSheetCollection* collection =
319 StyleSheetCollectionFor(tree_scope);
320 // After detaching document, collection could be null. In the case,
321 // we should not update anything. Instead, just return.
322 if (!collection)
323 return;
324 collection->RemoveStyleSheetCandidateNode(node);
325
326 SetNeedsActiveStyleUpdate(tree_scope);
327 }
328
ModifiedStyleSheetCandidateNode(Node & node)329 void StyleEngine::ModifiedStyleSheetCandidateNode(Node& node) {
330 if (node.isConnected())
331 SetNeedsActiveStyleUpdate(node.GetTreeScope());
332 }
333
AdoptedStyleSheetsWillChange(TreeScope & tree_scope,const HeapVector<Member<CSSStyleSheet>> & old_sheets,const HeapVector<Member<CSSStyleSheet>> & new_sheets)334 void StyleEngine::AdoptedStyleSheetsWillChange(
335 TreeScope& tree_scope,
336 const HeapVector<Member<CSSStyleSheet>>& old_sheets,
337 const HeapVector<Member<CSSStyleSheet>>& new_sheets) {
338 if (GetDocument().IsDetached())
339 return;
340
341 unsigned old_sheets_count = old_sheets.size();
342 unsigned new_sheets_count = new_sheets.size();
343
344 unsigned min_count = std::min(old_sheets_count, new_sheets_count);
345 unsigned index = 0;
346 while (index < min_count && old_sheets[index] == new_sheets[index]) {
347 index++;
348 }
349
350 if (old_sheets_count == new_sheets_count && index == old_sheets_count)
351 return;
352
353 for (unsigned i = index; i < old_sheets_count; ++i) {
354 old_sheets[i]->RemovedAdoptedFromTreeScope(tree_scope);
355 }
356 for (unsigned i = index; i < new_sheets_count; ++i) {
357 new_sheets[i]->AddedAdoptedToTreeScope(tree_scope);
358 }
359
360 if (!tree_scope.RootNode().isConnected())
361 return;
362
363 if (new_sheets_count) {
364 EnsureStyleSheetCollectionFor(tree_scope);
365 if (tree_scope != document_)
366 active_tree_scopes_.insert(&tree_scope);
367 } else if (!StyleSheetCollectionFor(tree_scope)) {
368 return;
369 }
370 SetNeedsActiveStyleUpdate(tree_scope);
371 }
372
AddedCustomElementDefaultStyles(const HeapVector<Member<CSSStyleSheet>> & default_styles)373 void StyleEngine::AddedCustomElementDefaultStyles(
374 const HeapVector<Member<CSSStyleSheet>>& default_styles) {
375 if (!RuntimeEnabledFeatures::CustomElementDefaultStyleEnabled() ||
376 GetDocument().IsDetached())
377 return;
378 for (CSSStyleSheet* sheet : default_styles)
379 custom_element_default_style_sheets_.insert(sheet);
380 global_rule_set_->MarkDirty();
381 }
382
383 namespace {
384
HasMediaQueries(const ActiveStyleSheetVector & active_style_sheets)385 bool HasMediaQueries(const ActiveStyleSheetVector& active_style_sheets) {
386 for (const auto& active_sheet : active_style_sheets) {
387 if (const MediaQuerySet* media_queries =
388 active_sheet.first->MediaQueries()) {
389 if (!media_queries->QueryVector().IsEmpty())
390 return true;
391 }
392 StyleSheetContents* contents = active_sheet.first->Contents();
393 if (contents->HasMediaQueries())
394 return true;
395 }
396 return false;
397 }
398
HasSizeDependentMediaQueries(const ActiveStyleSheetVector & active_style_sheets)399 bool HasSizeDependentMediaQueries(
400 const ActiveStyleSheetVector& active_style_sheets) {
401 for (const auto& active_sheet : active_style_sheets) {
402 if (active_sheet.first->HasMediaQueryResults())
403 return true;
404 StyleSheetContents* contents = active_sheet.first->Contents();
405 if (!contents->HasRuleSet())
406 continue;
407 if (contents->GetRuleSet().Features().HasMediaQueryResults())
408 return true;
409 }
410 return false;
411 }
412
413 } // namespace
414
MediaQueryAffectingValueChanged(const ActiveStyleSheetVector & active_sheets,MediaValueChange change)415 bool StyleEngine::MediaQueryAffectingValueChanged(
416 const ActiveStyleSheetVector& active_sheets,
417 MediaValueChange change) {
418 if (change == MediaValueChange::kSize)
419 return HasSizeDependentMediaQueries(active_sheets);
420
421 DCHECK(change == MediaValueChange::kOther);
422 return HasMediaQueries(active_sheets);
423 }
424
MediaQueryAffectingValueChanged(TreeScope & tree_scope,MediaValueChange change)425 void StyleEngine::MediaQueryAffectingValueChanged(TreeScope& tree_scope,
426 MediaValueChange change) {
427 auto* collection = StyleSheetCollectionFor(tree_scope);
428 DCHECK(collection);
429 if (MediaQueryAffectingValueChanged(collection->ActiveAuthorStyleSheets(),
430 change)) {
431 SetNeedsActiveStyleUpdate(tree_scope);
432 }
433 }
434
MediaQueriesChangedInScope(TreeScope & tree_scope)435 void StyleEngine::MediaQueriesChangedInScope(TreeScope& tree_scope) {
436 if (ScopedStyleResolver* resolver = tree_scope.GetScopedStyleResolver())
437 resolver->SetNeedsAppendAllSheets();
438 SetNeedsActiveStyleUpdate(tree_scope);
439 }
440
WatchedSelectorsChanged()441 void StyleEngine::WatchedSelectorsChanged() {
442 DCHECK(!IsHTMLImport());
443 DCHECK(global_rule_set_);
444 global_rule_set_->InitWatchedSelectorsRuleSet(GetDocument());
445 // TODO(futhark@chromium.org): Should be able to use RuleSetInvalidation here.
446 MarkAllElementsForStyleRecalc(StyleChangeReasonForTracing::Create(
447 style_change_reason::kDeclarativeContent));
448 }
449
ShouldUpdateDocumentStyleSheetCollection() const450 bool StyleEngine::ShouldUpdateDocumentStyleSheetCollection() const {
451 return document_scope_dirty_;
452 }
453
ShouldUpdateShadowTreeStyleSheetCollection() const454 bool StyleEngine::ShouldUpdateShadowTreeStyleSheetCollection() const {
455 return !dirty_tree_scopes_.IsEmpty();
456 }
457
MediaQueryAffectingValueChanged(UnorderedTreeScopeSet & tree_scopes,MediaValueChange change)458 void StyleEngine::MediaQueryAffectingValueChanged(
459 UnorderedTreeScopeSet& tree_scopes,
460 MediaValueChange change) {
461 for (TreeScope* tree_scope : tree_scopes) {
462 DCHECK(tree_scope != document_);
463 MediaQueryAffectingValueChanged(*tree_scope, change);
464 }
465 }
466
AddTextTrack(TextTrack * text_track)467 void StyleEngine::AddTextTrack(TextTrack* text_track) {
468 text_tracks_.insert(text_track);
469 }
470
RemoveTextTrack(TextTrack * text_track)471 void StyleEngine::RemoveTextTrack(TextTrack* text_track) {
472 text_tracks_.erase(text_track);
473 }
474
EnsureVTTOriginatingElement()475 Element* StyleEngine::EnsureVTTOriginatingElement() {
476 if (!vtt_originating_element_) {
477 vtt_originating_element_ = MakeGarbageCollected<Element>(
478 QualifiedName(g_null_atom, g_empty_atom, g_empty_atom), document_);
479 }
480 return vtt_originating_element_;
481 }
482
MediaQueryAffectingValueChanged(HeapHashSet<Member<TextTrack>> & text_tracks,MediaValueChange change)483 void StyleEngine::MediaQueryAffectingValueChanged(
484 HeapHashSet<Member<TextTrack>>& text_tracks,
485 MediaValueChange change) {
486 if (text_tracks.IsEmpty())
487 return;
488
489 for (auto text_track : text_tracks) {
490 bool style_needs_recalc = false;
491 auto style_sheets = text_track->GetCSSStyleSheets();
492 for (const auto& sheet : style_sheets) {
493 StyleSheetContents* contents = sheet->Contents();
494 if (contents->HasMediaQueries()) {
495 style_needs_recalc = true;
496 contents->ClearRuleSet();
497 }
498 }
499
500 if (style_needs_recalc) {
501 // Use kSubtreeTreeStyleChange instead of RuleSet style invalidation
502 // because it won't be expensive for tracks and we won't have dynamic
503 // changes.
504 text_track->Owner()->SetNeedsStyleRecalc(
505 kSubtreeStyleChange,
506 StyleChangeReasonForTracing::Create(style_change_reason::kShadow));
507 }
508 }
509 }
510
MediaQueryAffectingValueChanged(MediaValueChange change)511 void StyleEngine::MediaQueryAffectingValueChanged(MediaValueChange change) {
512 if (MediaQueryAffectingValueChanged(active_user_style_sheets_, change))
513 MarkUserStyleDirty();
514 MediaQueryAffectingValueChanged(GetDocument(), change);
515 MediaQueryAffectingValueChanged(active_tree_scopes_, change);
516 MediaQueryAffectingValueChanged(text_tracks_, change);
517 if (resolver_)
518 resolver_->UpdateMediaType();
519 }
520
UpdateActiveStyleSheetsInImport(StyleEngine & root_engine,DocumentStyleSheetCollector & parent_collector)521 void StyleEngine::UpdateActiveStyleSheetsInImport(
522 StyleEngine& root_engine,
523 DocumentStyleSheetCollector& parent_collector) {
524 DCHECK(RuntimeEnabledFeatures::HTMLImportsEnabled());
525 DCHECK(IsHTMLImport());
526 HeapVector<Member<StyleSheet>> sheets_for_list;
527 ImportedDocumentStyleSheetCollector subcollector(parent_collector,
528 sheets_for_list);
529 GetDocumentStyleSheetCollection().CollectStyleSheets(root_engine,
530 subcollector);
531 GetDocumentStyleSheetCollection().SwapSheetsForSheetList(sheets_for_list);
532
533 // Mark false for consistency. It is never checked for import documents.
534 document_scope_dirty_ = false;
535 }
536
UpdateActiveStyleSheetsInShadow(TreeScope * tree_scope,UnorderedTreeScopeSet & tree_scopes_removed)537 void StyleEngine::UpdateActiveStyleSheetsInShadow(
538 TreeScope* tree_scope,
539 UnorderedTreeScopeSet& tree_scopes_removed) {
540 DCHECK_NE(tree_scope, document_);
541 auto* collection =
542 To<ShadowTreeStyleSheetCollection>(StyleSheetCollectionFor(*tree_scope));
543 DCHECK(collection);
544 collection->UpdateActiveStyleSheets(*this);
545 if (!collection->HasStyleSheetCandidateNodes() &&
546 !tree_scope->HasAdoptedStyleSheets()) {
547 tree_scopes_removed.insert(tree_scope);
548 // When removing TreeScope from ActiveTreeScopes,
549 // its resolver should be destroyed by invoking resetAuthorStyle.
550 DCHECK(!tree_scope->GetScopedStyleResolver());
551 }
552 }
553
UpdateActiveUserStyleSheets()554 void StyleEngine::UpdateActiveUserStyleSheets() {
555 DCHECK(user_style_dirty_);
556
557 ActiveStyleSheetVector new_active_sheets;
558 for (auto& sheet : injected_user_style_sheets_) {
559 if (RuleSet* rule_set = RuleSetForSheet(*sheet.second))
560 new_active_sheets.push_back(std::make_pair(sheet.second, rule_set));
561 }
562
563 ApplyUserRuleSetChanges(active_user_style_sheets_, new_active_sheets);
564 new_active_sheets.swap(active_user_style_sheets_);
565 }
566
UpdateActiveStyleSheets()567 void StyleEngine::UpdateActiveStyleSheets() {
568 if (!NeedsActiveStyleSheetUpdate())
569 return;
570
571 DCHECK(!IsHTMLImport());
572 DCHECK(!GetDocument().InStyleRecalc());
573 DCHECK(GetDocument().IsActive());
574
575 TRACE_EVENT0("blink,blink_style", "StyleEngine::updateActiveStyleSheets");
576
577 if (user_style_dirty_)
578 UpdateActiveUserStyleSheets();
579
580 if (ShouldUpdateDocumentStyleSheetCollection())
581 GetDocumentStyleSheetCollection().UpdateActiveStyleSheets(*this);
582
583 if (ShouldUpdateShadowTreeStyleSheetCollection()) {
584 UnorderedTreeScopeSet tree_scopes_removed;
585 for (TreeScope* tree_scope : dirty_tree_scopes_)
586 UpdateActiveStyleSheetsInShadow(tree_scope, tree_scopes_removed);
587 for (TreeScope* tree_scope : tree_scopes_removed)
588 active_tree_scopes_.erase(tree_scope);
589 }
590
591 probe::ActiveStyleSheetsUpdated(document_);
592
593 dirty_tree_scopes_.clear();
594 document_scope_dirty_ = false;
595 tree_scopes_removed_ = false;
596 user_style_dirty_ = false;
597 }
598
UpdateViewport()599 void StyleEngine::UpdateViewport() {
600 if (viewport_resolver_)
601 viewport_resolver_->UpdateViewport(GetDocumentStyleSheetCollection());
602 }
603
NeedsActiveStyleUpdate() const604 bool StyleEngine::NeedsActiveStyleUpdate() const {
605 return (viewport_resolver_ && viewport_resolver_->NeedsUpdate()) ||
606 NeedsActiveStyleSheetUpdate() ||
607 (global_rule_set_ && global_rule_set_->IsDirty());
608 }
609
UpdateActiveStyle()610 void StyleEngine::UpdateActiveStyle() {
611 DCHECK(GetDocument().IsActive());
612 DCHECK(IsMainThread());
613 TRACE_EVENT0("blink", "Document::updateActiveStyle");
614 UpdateViewport();
615 UpdateActiveStyleSheets();
616 UpdateGlobalRuleSet();
617 }
618
ActiveStyleSheetsForInspector()619 const ActiveStyleSheetVector StyleEngine::ActiveStyleSheetsForInspector() {
620 if (GetDocument().IsActive())
621 UpdateActiveStyle();
622
623 if (active_tree_scopes_.IsEmpty())
624 return GetDocumentStyleSheetCollection().ActiveAuthorStyleSheets();
625
626 ActiveStyleSheetVector active_style_sheets;
627
628 active_style_sheets.AppendVector(
629 GetDocumentStyleSheetCollection().ActiveAuthorStyleSheets());
630 for (TreeScope* tree_scope : active_tree_scopes_) {
631 if (TreeScopeStyleSheetCollection* collection =
632 style_sheet_collection_map_.at(tree_scope))
633 active_style_sheets.AppendVector(collection->ActiveAuthorStyleSheets());
634 }
635
636 // FIXME: Inspector needs a vector which has all active stylesheets.
637 // However, creating such a large vector might cause performance regression.
638 // Need to implement some smarter solution.
639 return active_style_sheets;
640 }
641
ShadowRootInsertedToDocument(ShadowRoot & shadow_root)642 void StyleEngine::ShadowRootInsertedToDocument(ShadowRoot& shadow_root) {
643 DCHECK(shadow_root.isConnected());
644 if (GetDocument().IsDetached() || !shadow_root.HasAdoptedStyleSheets())
645 return;
646 EnsureStyleSheetCollectionFor(shadow_root);
647 SetNeedsActiveStyleUpdate(shadow_root);
648 active_tree_scopes_.insert(&shadow_root);
649 }
650
ShadowRootRemovedFromDocument(ShadowRoot * shadow_root)651 void StyleEngine::ShadowRootRemovedFromDocument(ShadowRoot* shadow_root) {
652 style_sheet_collection_map_.erase(shadow_root);
653 active_tree_scopes_.erase(shadow_root);
654 dirty_tree_scopes_.erase(shadow_root);
655 tree_scopes_removed_ = true;
656 ResetAuthorStyle(*shadow_root);
657 }
658
AddTreeBoundaryCrossingScope(const TreeScope & tree_scope)659 void StyleEngine::AddTreeBoundaryCrossingScope(const TreeScope& tree_scope) {
660 tree_boundary_crossing_scopes_.Add(&tree_scope.RootNode());
661 }
662
ResetAuthorStyle(TreeScope & tree_scope)663 void StyleEngine::ResetAuthorStyle(TreeScope& tree_scope) {
664 tree_boundary_crossing_scopes_.Remove(&tree_scope.RootNode());
665
666 ScopedStyleResolver* scoped_resolver = tree_scope.GetScopedStyleResolver();
667 if (!scoped_resolver)
668 return;
669
670 if (global_rule_set_)
671 global_rule_set_->MarkDirty();
672 if (tree_scope.RootNode().IsDocumentNode()) {
673 scoped_resolver->ResetAuthorStyle();
674 return;
675 }
676
677 tree_scope.ClearScopedStyleResolver();
678 }
679
SetRuleUsageTracker(StyleRuleUsageTracker * tracker)680 void StyleEngine::SetRuleUsageTracker(StyleRuleUsageTracker* tracker) {
681 tracker_ = tracker;
682
683 if (resolver_)
684 resolver_->SetRuleUsageTracker(tracker_);
685 }
686
RuleSetForSheet(CSSStyleSheet & sheet)687 RuleSet* StyleEngine::RuleSetForSheet(CSSStyleSheet& sheet) {
688 if (!sheet.MatchesMediaQueries(EnsureMediaQueryEvaluator()))
689 return nullptr;
690
691 AddRuleFlags add_rule_flags = kRuleHasNoSpecialState;
692 if (document_->GetExecutionContext()->GetSecurityOrigin()->CanRequest(
693 sheet.BaseURL())) {
694 add_rule_flags = kRuleHasDocumentSecurityOrigin;
695 }
696 return &sheet.Contents()->EnsureRuleSet(*media_query_evaluator_,
697 add_rule_flags);
698 }
699
ClearResolvers()700 void StyleEngine::ClearResolvers() {
701 DCHECK(!GetDocument().InStyleRecalc());
702 DCHECK(!IsHTMLImport() || !resolver_);
703
704 GetDocument().ClearScopedStyleResolver();
705 for (TreeScope* tree_scope : active_tree_scopes_)
706 tree_scope->ClearScopedStyleResolver();
707
708 if (resolver_) {
709 TRACE_EVENT1("blink", "StyleEngine::clearResolver", "frame",
710 ToTraceValue(GetDocument().GetFrame()));
711 resolver_->Dispose();
712 resolver_.Clear();
713 }
714 }
715
DidDetach()716 void StyleEngine::DidDetach() {
717 ClearResolvers();
718 if (global_rule_set_)
719 global_rule_set_->Dispose();
720 global_rule_set_ = nullptr;
721 tree_boundary_crossing_scopes_.Clear();
722 dirty_tree_scopes_.clear();
723 active_tree_scopes_.clear();
724 viewport_resolver_ = nullptr;
725 media_query_evaluator_ = nullptr;
726 style_invalidation_root_.Clear();
727 style_recalc_root_.Clear();
728 layout_tree_rebuild_root_.Clear();
729 if (font_selector_)
730 font_selector_->GetFontFaceCache()->ClearAll();
731 font_selector_ = nullptr;
732 if (environment_variables_)
733 environment_variables_->DetachFromParent();
734 environment_variables_ = nullptr;
735 }
736
ClearFontFaceCacheAndAddUserFonts()737 bool StyleEngine::ClearFontFaceCacheAndAddUserFonts() {
738 bool fonts_changed = false;
739
740 if (font_selector_ &&
741 font_selector_->GetFontFaceCache()->ClearCSSConnected()) {
742 fonts_changed = true;
743 if (resolver_)
744 resolver_->InvalidateMatchedPropertiesCache();
745 }
746
747 // Rebuild the font cache with @font-face rules from user style sheets.
748 for (unsigned i = 0; i < active_user_style_sheets_.size(); ++i) {
749 DCHECK(active_user_style_sheets_[i].second);
750 if (AddUserFontFaceRules(*active_user_style_sheets_[i].second))
751 fonts_changed = true;
752 }
753
754 return fonts_changed;
755 }
756
UpdateGenericFontFamilySettings()757 void StyleEngine::UpdateGenericFontFamilySettings() {
758 // FIXME: we should not update generic font family settings when
759 // document is inactive.
760 DCHECK(GetDocument().IsActive());
761
762 if (!font_selector_)
763 return;
764
765 font_selector_->UpdateGenericFontFamilySettings(*document_);
766 if (resolver_)
767 resolver_->InvalidateMatchedPropertiesCache();
768 FontCache::GetFontCache()->InvalidateShapeCache();
769 }
770
RemoveFontFaceRules(const HeapVector<Member<const StyleRuleFontFace>> & font_face_rules)771 void StyleEngine::RemoveFontFaceRules(
772 const HeapVector<Member<const StyleRuleFontFace>>& font_face_rules) {
773 if (!font_selector_)
774 return;
775
776 FontFaceCache* cache = font_selector_->GetFontFaceCache();
777 for (const auto& rule : font_face_rules)
778 cache->Remove(rule);
779 if (resolver_)
780 resolver_->InvalidateMatchedPropertiesCache();
781 }
782
MarkTreeScopeDirty(TreeScope & scope)783 void StyleEngine::MarkTreeScopeDirty(TreeScope& scope) {
784 if (scope == document_) {
785 MarkDocumentDirty();
786 return;
787 }
788
789 TreeScopeStyleSheetCollection* collection = StyleSheetCollectionFor(scope);
790 DCHECK(collection);
791 collection->MarkSheetListDirty();
792 dirty_tree_scopes_.insert(&scope);
793 GetDocument().ScheduleLayoutTreeUpdateIfNeeded();
794 }
795
MarkDocumentDirty()796 void StyleEngine::MarkDocumentDirty() {
797 document_scope_dirty_ = true;
798 document_style_sheet_collection_->MarkSheetListDirty();
799 if (GetDocument().ImportLoader())
800 GetDocument().TreeRootDocument().GetStyleEngine().MarkDocumentDirty();
801 else
802 GetDocument().ScheduleLayoutTreeUpdateIfNeeded();
803 }
804
MarkUserStyleDirty()805 void StyleEngine::MarkUserStyleDirty() {
806 user_style_dirty_ = true;
807 GetDocument().ScheduleLayoutTreeUpdateIfNeeded();
808 }
809
MarkViewportStyleDirty()810 void StyleEngine::MarkViewportStyleDirty() {
811 viewport_style_dirty_ = true;
812 GetDocument().ScheduleLayoutTreeUpdateIfNeeded();
813 }
814
CreateSheet(Element & element,const String & text,TextPosition start_position,StyleEngineContext & context)815 CSSStyleSheet* StyleEngine::CreateSheet(Element& element,
816 const String& text,
817 TextPosition start_position,
818 StyleEngineContext& context) {
819 DCHECK(element.GetDocument() == GetDocument());
820 CSSStyleSheet* style_sheet = nullptr;
821
822 AddPendingSheet(context);
823
824 AtomicString text_content(text);
825
826 auto result = text_to_sheet_cache_.insert(text_content, nullptr);
827 StyleSheetContents* contents = result.stored_value->value;
828 if (result.is_new_entry || !contents ||
829 !contents->IsCacheableForStyleElement()) {
830 result.stored_value->value = nullptr;
831 style_sheet = ParseSheet(element, text, start_position);
832 if (style_sheet->Contents()->IsCacheableForStyleElement()) {
833 result.stored_value->value = style_sheet->Contents();
834 sheet_to_text_cache_.insert(style_sheet->Contents(), text_content);
835 }
836 } else {
837 DCHECK(contents);
838 DCHECK(contents->IsCacheableForStyleElement());
839 DCHECK(contents->HasSingleOwnerDocument());
840 contents->SetIsUsedFromTextCache();
841 style_sheet =
842 CSSStyleSheet::CreateInline(contents, element, start_position);
843 }
844
845 DCHECK(style_sheet);
846 if (!element.IsInShadowTree()) {
847 String title = element.title();
848 if (!title.IsEmpty()) {
849 style_sheet->SetTitle(title);
850 SetPreferredStylesheetSetNameIfNotSet(title);
851 }
852 }
853 return style_sheet;
854 }
855
ParseSheet(Element & element,const String & text,TextPosition start_position)856 CSSStyleSheet* StyleEngine::ParseSheet(Element& element,
857 const String& text,
858 TextPosition start_position) {
859 CSSStyleSheet* style_sheet = nullptr;
860 style_sheet = CSSStyleSheet::CreateInline(element, NullURL(), start_position,
861 GetDocument().Encoding());
862 style_sheet->Contents()->ParseStringAtPosition(text, start_position);
863 return style_sheet;
864 }
865
CollectUserStyleFeaturesTo(RuleFeatureSet & features) const866 void StyleEngine::CollectUserStyleFeaturesTo(RuleFeatureSet& features) const {
867 for (unsigned i = 0; i < active_user_style_sheets_.size(); ++i) {
868 CSSStyleSheet* sheet = active_user_style_sheets_[i].first;
869 features.ViewportDependentMediaQueryResults().AppendVector(
870 sheet->ViewportDependentMediaQueryResults());
871 features.DeviceDependentMediaQueryResults().AppendVector(
872 sheet->DeviceDependentMediaQueryResults());
873 DCHECK(sheet->Contents()->HasRuleSet());
874 features.Add(sheet->Contents()->GetRuleSet().Features());
875 }
876 }
877
CollectScopedStyleFeaturesTo(RuleFeatureSet & features) const878 void StyleEngine::CollectScopedStyleFeaturesTo(RuleFeatureSet& features) const {
879 HeapHashSet<Member<const StyleSheetContents>>
880 visited_shared_style_sheet_contents;
881 if (GetDocument().GetScopedStyleResolver()) {
882 GetDocument().GetScopedStyleResolver()->CollectFeaturesTo(
883 features, visited_shared_style_sheet_contents);
884 }
885 for (TreeScope* tree_scope : active_tree_scopes_) {
886 if (ScopedStyleResolver* resolver = tree_scope->GetScopedStyleResolver()) {
887 resolver->CollectFeaturesTo(features,
888 visited_shared_style_sheet_contents);
889 }
890 }
891 }
892
InvalidateStyleAndLayoutForFontUpdates()893 void StyleEngine::InvalidateStyleAndLayoutForFontUpdates() {
894 if (!fonts_need_update_)
895 return;
896
897 TRACE_EVENT0("blink", "StyleEngine::InvalidateStyleAndLayoutForFontUpdates");
898
899 fonts_need_update_ = false;
900
901 if (Element* root = GetDocument().documentElement()) {
902 TRACE_EVENT0("blink", "Node::MarkSubtreeNeedsStyleRecalcForFontUpdates");
903 root->MarkSubtreeNeedsStyleRecalcForFontUpdates();
904 }
905
906 if (LayoutView* layout_view = GetDocument().GetLayoutView()) {
907 TRACE_EVENT0("blink", "LayoutObject::InvalidateSubtreeForFontUpdates");
908 layout_view->InvalidateSubtreeLayoutForFontUpdates();
909 }
910 }
911
MarkFontsNeedUpdate()912 void StyleEngine::MarkFontsNeedUpdate() {
913 fonts_need_update_ = true;
914 GetDocument().ScheduleLayoutTreeUpdateIfNeeded();
915 }
916
FontsNeedUpdate(FontSelector *,FontInvalidationReason)917 void StyleEngine::FontsNeedUpdate(FontSelector*, FontInvalidationReason) {
918 if (!GetDocument().IsActive())
919 return;
920
921 if (resolver_)
922 resolver_->InvalidateMatchedPropertiesCache();
923 MarkViewportStyleDirty();
924 MarkFontsNeedUpdate();
925
926 probe::FontsUpdated(document_->GetExecutionContext(), nullptr, String(),
927 nullptr);
928 }
929
PlatformColorsChanged()930 void StyleEngine::PlatformColorsChanged() {
931 UpdateForcedBackgroundColor();
932 if (resolver_)
933 resolver_->InvalidateMatchedPropertiesCache();
934 MarkAllElementsForStyleRecalc(StyleChangeReasonForTracing::Create(
935 style_change_reason::kPlatformColorChange));
936 }
937
ShouldSkipInvalidationFor(const Element & element) const938 bool StyleEngine::ShouldSkipInvalidationFor(const Element& element) const {
939 if (!element.InActiveDocument())
940 return true;
941 if (GetDocument().InStyleRecalc()) {
942 #if DCHECK_IS_ON()
943 // TODO(futhark): The InStyleRecalc() if-guard above should have been a
944 // DCHECK(!InStyleRecalc()), but there are a couple of cases where we try to
945 // invalidate style from style recalc:
946 //
947 // 1. We may animate the class attribute of an SVG element and change it
948 // during style recalc when applying the animation effect.
949 // 2. We may call SetInlineStyle on elements in a UA shadow tree as part of
950 // style recalc. For instance from HTMLImageFallbackHelper.
951 //
952 // If there are more cases, we need to adjust the DCHECKs below, but ideally
953 // The origin of these invalidations should be fixed.
954 if (!element.IsSVGElement()) {
955 DCHECK(element.ContainingShadowRoot());
956 DCHECK(element.ContainingShadowRoot()->IsUserAgent());
957 }
958 #endif // DCHECK_IS_ON()
959 return true;
960 }
961 if (GetDocument().GetStyleChangeType() == kSubtreeStyleChange)
962 return true;
963 Element* root = GetDocument().documentElement();
964 if (!root || root->GetStyleChangeType() == kSubtreeStyleChange)
965 return true;
966 if (!element.parentNode())
967 return true;
968 return element.parentNode()->GetStyleChangeType() == kSubtreeStyleChange;
969 }
970
ClassChangedForElement(const SpaceSplitString & changed_classes,Element & element)971 void StyleEngine::ClassChangedForElement(
972 const SpaceSplitString& changed_classes,
973 Element& element) {
974 if (ShouldSkipInvalidationFor(element))
975 return;
976 InvalidationLists invalidation_lists;
977 unsigned changed_size = changed_classes.size();
978 const RuleFeatureSet& features = GetRuleFeatureSet();
979 for (unsigned i = 0; i < changed_size; ++i) {
980 features.CollectInvalidationSetsForClass(invalidation_lists, element,
981 changed_classes[i]);
982 }
983 pending_invalidations_.ScheduleInvalidationSetsForNode(invalidation_lists,
984 element);
985 }
986
ClassChangedForElement(const SpaceSplitString & old_classes,const SpaceSplitString & new_classes,Element & element)987 void StyleEngine::ClassChangedForElement(const SpaceSplitString& old_classes,
988 const SpaceSplitString& new_classes,
989 Element& element) {
990 if (ShouldSkipInvalidationFor(element))
991 return;
992
993 if (!old_classes.size()) {
994 ClassChangedForElement(new_classes, element);
995 return;
996 }
997
998 // Class vectors tend to be very short. This is faster than using a hash
999 // table.
1000 WTF::Vector<bool> remaining_class_bits(old_classes.size());
1001
1002 InvalidationLists invalidation_lists;
1003 const RuleFeatureSet& features = GetRuleFeatureSet();
1004
1005 for (unsigned i = 0; i < new_classes.size(); ++i) {
1006 bool found = false;
1007 for (unsigned j = 0; j < old_classes.size(); ++j) {
1008 if (new_classes[i] == old_classes[j]) {
1009 // Mark each class that is still in the newClasses so we can skip doing
1010 // an n^2 search below when looking for removals. We can't break from
1011 // this loop early since a class can appear more than once.
1012 remaining_class_bits[j] = true;
1013 found = true;
1014 }
1015 }
1016 // Class was added.
1017 if (!found) {
1018 features.CollectInvalidationSetsForClass(invalidation_lists, element,
1019 new_classes[i]);
1020 }
1021 }
1022
1023 for (unsigned i = 0; i < old_classes.size(); ++i) {
1024 if (remaining_class_bits[i])
1025 continue;
1026 // Class was removed.
1027 features.CollectInvalidationSetsForClass(invalidation_lists, element,
1028 old_classes[i]);
1029 }
1030 pending_invalidations_.ScheduleInvalidationSetsForNode(invalidation_lists,
1031 element);
1032 }
1033
1034 namespace {
1035
HasAttributeDependentGeneratedContent(const Element & element)1036 bool HasAttributeDependentGeneratedContent(const Element& element) {
1037 if (PseudoElement* before = element.GetPseudoElement(kPseudoIdBefore)) {
1038 const ComputedStyle* style = before->GetComputedStyle();
1039 if (style && style->HasAttrContent())
1040 return true;
1041 }
1042 if (PseudoElement* after = element.GetPseudoElement(kPseudoIdAfter)) {
1043 const ComputedStyle* style = after->GetComputedStyle();
1044 if (style && style->HasAttrContent())
1045 return true;
1046 }
1047 return false;
1048 }
1049
1050 } // namespace
1051
AttributeChangedForElement(const QualifiedName & attribute_name,Element & element)1052 void StyleEngine::AttributeChangedForElement(
1053 const QualifiedName& attribute_name,
1054 Element& element) {
1055 if (ShouldSkipInvalidationFor(element))
1056 return;
1057
1058 InvalidationLists invalidation_lists;
1059 GetRuleFeatureSet().CollectInvalidationSetsForAttribute(
1060 invalidation_lists, element, attribute_name);
1061 pending_invalidations_.ScheduleInvalidationSetsForNode(invalidation_lists,
1062 element);
1063
1064 if (!element.NeedsStyleRecalc() &&
1065 HasAttributeDependentGeneratedContent(element)) {
1066 element.SetNeedsStyleRecalc(
1067 kLocalStyleChange,
1068 StyleChangeReasonForTracing::FromAttribute(attribute_name));
1069 }
1070 }
1071
IdChangedForElement(const AtomicString & old_id,const AtomicString & new_id,Element & element)1072 void StyleEngine::IdChangedForElement(const AtomicString& old_id,
1073 const AtomicString& new_id,
1074 Element& element) {
1075 if (ShouldSkipInvalidationFor(element))
1076 return;
1077
1078 InvalidationLists invalidation_lists;
1079 const RuleFeatureSet& features = GetRuleFeatureSet();
1080 if (!old_id.IsEmpty())
1081 features.CollectInvalidationSetsForId(invalidation_lists, element, old_id);
1082 if (!new_id.IsEmpty())
1083 features.CollectInvalidationSetsForId(invalidation_lists, element, new_id);
1084 pending_invalidations_.ScheduleInvalidationSetsForNode(invalidation_lists,
1085 element);
1086 }
1087
PseudoStateChangedForElement(CSSSelector::PseudoType pseudo_type,Element & element)1088 void StyleEngine::PseudoStateChangedForElement(
1089 CSSSelector::PseudoType pseudo_type,
1090 Element& element) {
1091 if (ShouldSkipInvalidationFor(element))
1092 return;
1093
1094 InvalidationLists invalidation_lists;
1095 GetRuleFeatureSet().CollectInvalidationSetsForPseudoClass(
1096 invalidation_lists, element, pseudo_type);
1097 pending_invalidations_.ScheduleInvalidationSetsForNode(invalidation_lists,
1098 element);
1099 }
1100
PartChangedForElement(Element & element)1101 void StyleEngine::PartChangedForElement(Element& element) {
1102 if (ShouldSkipInvalidationFor(element))
1103 return;
1104 if (element.GetTreeScope() == document_)
1105 return;
1106 if (!GetRuleFeatureSet().InvalidatesParts())
1107 return;
1108 element.SetNeedsStyleRecalc(
1109 kLocalStyleChange,
1110 StyleChangeReasonForTracing::FromAttribute(html_names::kPartAttr));
1111 }
1112
ExportpartsChangedForElement(Element & element)1113 void StyleEngine::ExportpartsChangedForElement(Element& element) {
1114 if (ShouldSkipInvalidationFor(element))
1115 return;
1116 if (!element.GetShadowRoot())
1117 return;
1118
1119 InvalidationLists invalidation_lists;
1120 GetRuleFeatureSet().CollectPartInvalidationSet(invalidation_lists);
1121 pending_invalidations_.ScheduleInvalidationSetsForNode(invalidation_lists,
1122 element);
1123 }
1124
ScheduleSiblingInvalidationsForElement(Element & element,ContainerNode & scheduling_parent,unsigned min_direct_adjacent)1125 void StyleEngine::ScheduleSiblingInvalidationsForElement(
1126 Element& element,
1127 ContainerNode& scheduling_parent,
1128 unsigned min_direct_adjacent) {
1129 DCHECK(min_direct_adjacent);
1130
1131 InvalidationLists invalidation_lists;
1132
1133 const RuleFeatureSet& features = GetRuleFeatureSet();
1134
1135 if (element.HasID()) {
1136 features.CollectSiblingInvalidationSetForId(invalidation_lists, element,
1137 element.IdForStyleResolution(),
1138 min_direct_adjacent);
1139 }
1140
1141 if (element.HasClass()) {
1142 const SpaceSplitString& class_names = element.ClassNames();
1143 for (wtf_size_t i = 0; i < class_names.size(); i++) {
1144 features.CollectSiblingInvalidationSetForClass(
1145 invalidation_lists, element, class_names[i], min_direct_adjacent);
1146 }
1147 }
1148
1149 for (const Attribute& attribute : element.Attributes()) {
1150 features.CollectSiblingInvalidationSetForAttribute(
1151 invalidation_lists, element, attribute.GetName(), min_direct_adjacent);
1152 }
1153
1154 features.CollectUniversalSiblingInvalidationSet(invalidation_lists,
1155 min_direct_adjacent);
1156
1157 pending_invalidations_.ScheduleSiblingInvalidationsAsDescendants(
1158 invalidation_lists, scheduling_parent);
1159 }
1160
ScheduleInvalidationsForInsertedSibling(Element * before_element,Element & inserted_element)1161 void StyleEngine::ScheduleInvalidationsForInsertedSibling(
1162 Element* before_element,
1163 Element& inserted_element) {
1164 unsigned affected_siblings =
1165 inserted_element.parentNode()->ChildrenAffectedByIndirectAdjacentRules()
1166 ? SiblingInvalidationSet::kDirectAdjacentMax
1167 : MaxDirectAdjacentSelectors();
1168
1169 ContainerNode* scheduling_parent =
1170 inserted_element.ParentElementOrShadowRoot();
1171 if (!scheduling_parent)
1172 return;
1173
1174 ScheduleSiblingInvalidationsForElement(inserted_element, *scheduling_parent,
1175 1);
1176
1177 for (unsigned i = 1; before_element && i <= affected_siblings;
1178 i++, before_element =
1179 ElementTraversal::PreviousSibling(*before_element)) {
1180 ScheduleSiblingInvalidationsForElement(*before_element, *scheduling_parent,
1181 i);
1182 }
1183 }
1184
ScheduleInvalidationsForRemovedSibling(Element * before_element,Element & removed_element,Element & after_element)1185 void StyleEngine::ScheduleInvalidationsForRemovedSibling(
1186 Element* before_element,
1187 Element& removed_element,
1188 Element& after_element) {
1189 unsigned affected_siblings =
1190 after_element.parentNode()->ChildrenAffectedByIndirectAdjacentRules()
1191 ? SiblingInvalidationSet::kDirectAdjacentMax
1192 : MaxDirectAdjacentSelectors();
1193
1194 ContainerNode* scheduling_parent = after_element.ParentElementOrShadowRoot();
1195 if (!scheduling_parent)
1196 return;
1197
1198 ScheduleSiblingInvalidationsForElement(removed_element, *scheduling_parent,
1199 1);
1200
1201 for (unsigned i = 1; before_element && i <= affected_siblings;
1202 i++, before_element =
1203 ElementTraversal::PreviousSibling(*before_element)) {
1204 ScheduleSiblingInvalidationsForElement(*before_element, *scheduling_parent,
1205 i);
1206 }
1207 }
1208
ScheduleNthPseudoInvalidations(ContainerNode & nth_parent)1209 void StyleEngine::ScheduleNthPseudoInvalidations(ContainerNode& nth_parent) {
1210 InvalidationLists invalidation_lists;
1211 GetRuleFeatureSet().CollectNthInvalidationSet(invalidation_lists);
1212 pending_invalidations_.ScheduleInvalidationSetsForNode(invalidation_lists,
1213 nth_parent);
1214 }
1215
ScheduleRuleSetInvalidationsForElement(Element & element,const HeapHashSet<Member<RuleSet>> & rule_sets)1216 void StyleEngine::ScheduleRuleSetInvalidationsForElement(
1217 Element& element,
1218 const HeapHashSet<Member<RuleSet>>& rule_sets) {
1219 AtomicString id;
1220 const SpaceSplitString* class_names = nullptr;
1221
1222 if (element.HasID())
1223 id = element.IdForStyleResolution();
1224 if (element.HasClass())
1225 class_names = &element.ClassNames();
1226
1227 InvalidationLists invalidation_lists;
1228 for (const auto& rule_set : rule_sets) {
1229 if (!id.IsNull()) {
1230 rule_set->Features().CollectInvalidationSetsForId(invalidation_lists,
1231 element, id);
1232 }
1233 if (class_names) {
1234 wtf_size_t class_name_count = class_names->size();
1235 for (wtf_size_t i = 0; i < class_name_count; i++) {
1236 rule_set->Features().CollectInvalidationSetsForClass(
1237 invalidation_lists, element, (*class_names)[i]);
1238 }
1239 }
1240 for (const Attribute& attribute : element.Attributes()) {
1241 rule_set->Features().CollectInvalidationSetsForAttribute(
1242 invalidation_lists, element, attribute.GetName());
1243 }
1244 }
1245 pending_invalidations_.ScheduleInvalidationSetsForNode(invalidation_lists,
1246 element);
1247 }
1248
ScheduleTypeRuleSetInvalidations(ContainerNode & node,const HeapHashSet<Member<RuleSet>> & rule_sets)1249 void StyleEngine::ScheduleTypeRuleSetInvalidations(
1250 ContainerNode& node,
1251 const HeapHashSet<Member<RuleSet>>& rule_sets) {
1252 InvalidationLists invalidation_lists;
1253 for (const auto& rule_set : rule_sets) {
1254 rule_set->Features().CollectTypeRuleInvalidationSet(invalidation_lists,
1255 node);
1256 }
1257 DCHECK(invalidation_lists.siblings.IsEmpty());
1258 pending_invalidations_.ScheduleInvalidationSetsForNode(invalidation_lists,
1259 node);
1260
1261 auto* shadow_root = DynamicTo<ShadowRoot>(node);
1262 if (!shadow_root)
1263 return;
1264
1265 Element& host = shadow_root->host();
1266 if (host.NeedsStyleRecalc())
1267 return;
1268
1269 for (auto& invalidation_set : invalidation_lists.descendants) {
1270 if (invalidation_set->InvalidatesTagName(host)) {
1271 host.SetNeedsStyleRecalc(kLocalStyleChange,
1272 StyleChangeReasonForTracing::Create(
1273 style_change_reason::kStyleSheetChange));
1274 return;
1275 }
1276 }
1277 }
1278
ScheduleCustomElementInvalidations(HashSet<AtomicString> tag_names)1279 void StyleEngine::ScheduleCustomElementInvalidations(
1280 HashSet<AtomicString> tag_names) {
1281 scoped_refptr<DescendantInvalidationSet> invalidation_set =
1282 DescendantInvalidationSet::Create();
1283 for (auto& tag_name : tag_names) {
1284 invalidation_set->AddTagName(tag_name);
1285 }
1286 invalidation_set->SetTreeBoundaryCrossing();
1287 InvalidationLists invalidation_lists;
1288 invalidation_lists.descendants.push_back(invalidation_set);
1289 pending_invalidations_.ScheduleInvalidationSetsForNode(invalidation_lists,
1290 *document_);
1291 }
1292
InvalidateStyle()1293 void StyleEngine::InvalidateStyle() {
1294 StyleInvalidator style_invalidator(
1295 pending_invalidations_.GetPendingInvalidationMap());
1296 style_invalidator.Invalidate(GetDocument(),
1297 style_invalidation_root_.RootElement());
1298 style_invalidation_root_.Clear();
1299 }
1300
InvalidateSlottedElements(HTMLSlotElement & slot)1301 void StyleEngine::InvalidateSlottedElements(HTMLSlotElement& slot) {
1302 for (auto& node : slot.FlattenedAssignedNodes()) {
1303 if (node->IsElementNode()) {
1304 node->SetNeedsStyleRecalc(kLocalStyleChange,
1305 StyleChangeReasonForTracing::Create(
1306 style_change_reason::kStyleSheetChange));
1307 }
1308 }
1309 }
1310
ScheduleInvalidationsForRuleSets(TreeScope & tree_scope,const HeapHashSet<Member<RuleSet>> & rule_sets,InvalidationScope invalidation_scope)1311 void StyleEngine::ScheduleInvalidationsForRuleSets(
1312 TreeScope& tree_scope,
1313 const HeapHashSet<Member<RuleSet>>& rule_sets,
1314 InvalidationScope invalidation_scope) {
1315 #if DCHECK_IS_ON()
1316 // Full scope recalcs should be handled while collecting the rule sets before
1317 // calling this method.
1318 for (auto rule_set : rule_sets)
1319 DCHECK(!rule_set->Features().NeedsFullRecalcForRuleSetInvalidation());
1320 #endif // DCHECK_IS_ON()
1321
1322 TRACE_EVENT0("blink,blink_style",
1323 "StyleEngine::scheduleInvalidationsForRuleSets");
1324
1325 ScheduleTypeRuleSetInvalidations(tree_scope.RootNode(), rule_sets);
1326
1327 bool invalidate_slotted = false;
1328 if (auto* shadow_root = DynamicTo<ShadowRoot>(&tree_scope.RootNode())) {
1329 Element& host = shadow_root->host();
1330 ScheduleRuleSetInvalidationsForElement(host, rule_sets);
1331 if (host.GetStyleChangeType() == kSubtreeStyleChange)
1332 return;
1333 for (auto rule_set : rule_sets) {
1334 if (rule_set->HasSlottedRules()) {
1335 invalidate_slotted = true;
1336 break;
1337 }
1338 }
1339 }
1340
1341 Node* stay_within = &tree_scope.RootNode();
1342 Element* element = ElementTraversal::FirstChild(*stay_within);
1343 while (element) {
1344 ScheduleRuleSetInvalidationsForElement(*element, rule_sets);
1345 auto* html_slot_element = DynamicTo<HTMLSlotElement>(element);
1346 if (html_slot_element && invalidate_slotted)
1347 InvalidateSlottedElements(*html_slot_element);
1348
1349 if (invalidation_scope == kInvalidateAllScopes) {
1350 if (ShadowRoot* shadow_root = element->GetShadowRoot()) {
1351 ScheduleInvalidationsForRuleSets(*shadow_root, rule_sets,
1352 kInvalidateAllScopes);
1353 }
1354 }
1355
1356 if (element->GetStyleChangeType() < kSubtreeStyleChange &&
1357 element->GetComputedStyle()) {
1358 element = ElementTraversal::Next(*element, stay_within);
1359 } else {
1360 element = ElementTraversal::NextSkippingChildren(*element, stay_within);
1361 }
1362 }
1363 }
1364
SetStatsEnabled(bool enabled)1365 void StyleEngine::SetStatsEnabled(bool enabled) {
1366 if (!enabled) {
1367 style_resolver_stats_ = nullptr;
1368 return;
1369 }
1370 if (!style_resolver_stats_)
1371 style_resolver_stats_ = std::make_unique<StyleResolverStats>();
1372 else
1373 style_resolver_stats_->Reset();
1374 }
1375
SetPreferredStylesheetSetNameIfNotSet(const String & name)1376 void StyleEngine::SetPreferredStylesheetSetNameIfNotSet(const String& name) {
1377 DCHECK(!name.IsEmpty());
1378 if (!preferred_stylesheet_set_name_.IsEmpty())
1379 return;
1380 preferred_stylesheet_set_name_ = name;
1381 MarkDocumentDirty();
1382 }
1383
SetHttpDefaultStyle(const String & content)1384 void StyleEngine::SetHttpDefaultStyle(const String& content) {
1385 if (!content.IsEmpty())
1386 SetPreferredStylesheetSetNameIfNotSet(content);
1387 }
1388
EnsureUAStyleForXrOverlay()1389 void StyleEngine::EnsureUAStyleForXrOverlay() {
1390 DCHECK(!IsHTMLImport());
1391 DCHECK(global_rule_set_);
1392 if (CSSDefaultStyleSheets::Instance().EnsureDefaultStyleSheetForXrOverlay()) {
1393 global_rule_set_->MarkDirty();
1394 UpdateActiveStyle();
1395 }
1396 }
1397
EnsureUAStyleForFullscreen()1398 void StyleEngine::EnsureUAStyleForFullscreen() {
1399 DCHECK(!IsHTMLImport());
1400 DCHECK(global_rule_set_);
1401 if (global_rule_set_->HasFullscreenUAStyle())
1402 return;
1403 CSSDefaultStyleSheets::Instance().EnsureDefaultStyleSheetForFullscreen();
1404 global_rule_set_->MarkDirty();
1405 UpdateActiveStyle();
1406 }
1407
EnsureUAStyleForElement(const Element & element)1408 void StyleEngine::EnsureUAStyleForElement(const Element& element) {
1409 DCHECK(!IsHTMLImport());
1410 DCHECK(global_rule_set_);
1411 if (CSSDefaultStyleSheets::Instance().EnsureDefaultStyleSheetsForElement(
1412 element)) {
1413 global_rule_set_->MarkDirty();
1414 UpdateActiveStyle();
1415 }
1416 }
1417
EnsureUAStyleForPseudoElement(PseudoId pseudo_id)1418 void StyleEngine::EnsureUAStyleForPseudoElement(PseudoId pseudo_id) {
1419 DCHECK(!IsHTMLImport());
1420 DCHECK(global_rule_set_);
1421 if (CSSDefaultStyleSheets::Instance()
1422 .EnsureDefaultStyleSheetsForPseudoElement(pseudo_id)) {
1423 global_rule_set_->MarkDirty();
1424 UpdateActiveStyle();
1425 }
1426 }
1427
HasRulesForId(const AtomicString & id) const1428 bool StyleEngine::HasRulesForId(const AtomicString& id) const {
1429 DCHECK(!IsHTMLImport());
1430 DCHECK(global_rule_set_);
1431 return global_rule_set_->GetRuleFeatureSet().HasSelectorForId(id);
1432 }
1433
InitialStyleChanged()1434 void StyleEngine::InitialStyleChanged() {
1435 if (viewport_resolver_)
1436 viewport_resolver_->InitialStyleChanged();
1437
1438 // Media queries may rely on the initial font size relative lengths which may
1439 // have changed.
1440 MediaQueryAffectingValueChanged(MediaValueChange::kOther);
1441 MarkViewportStyleDirty();
1442 MarkAllElementsForStyleRecalc(
1443 StyleChangeReasonForTracing::Create(style_change_reason::kSettings));
1444 }
1445
InitialViewportChanged()1446 void StyleEngine::InitialViewportChanged() {
1447 if (viewport_resolver_)
1448 viewport_resolver_->InitialViewportChanged();
1449 }
1450
ViewportRulesChanged()1451 void StyleEngine::ViewportRulesChanged() {
1452 if (viewport_resolver_)
1453 viewport_resolver_->SetNeedsCollectRules();
1454 }
1455
HtmlImportAddedOrRemoved()1456 void StyleEngine::HtmlImportAddedOrRemoved() {
1457 if (GetDocument().ImportLoader()) {
1458 GetDocument()
1459 .TreeRootDocument()
1460 .GetStyleEngine()
1461 .HtmlImportAddedOrRemoved();
1462 return;
1463 }
1464
1465 // When we remove an import link and re-insert it into the document, the
1466 // import Document and CSSStyleSheet pointers are persisted. That means the
1467 // comparison of active stylesheets is not able to figure out that the order
1468 // of the stylesheets have changed after insertion.
1469 //
1470 // This is also the case when we import the same document twice where the
1471 // last inserted document is inserted before the first one in dom order where
1472 // the last would take precedence.
1473 //
1474 // Fall back to re-add all sheets to the scoped resolver and recalculate style
1475 // for the whole document when we remove or insert an import document.
1476 if (ScopedStyleResolver* resolver = GetDocument().GetScopedStyleResolver()) {
1477 MarkDocumentDirty();
1478 resolver->SetNeedsAppendAllSheets();
1479 MarkAllElementsForStyleRecalc(StyleChangeReasonForTracing::Create(
1480 style_change_reason::kActiveStylesheetsUpdate));
1481 }
1482 }
1483
V0ShadowAddedOnV1Document()1484 void StyleEngine::V0ShadowAddedOnV1Document() {
1485 // No need to look into the ScopedStyleResolver for document, as ::slotted
1486 // never matches anything in a document tree.
1487 for (TreeScope* tree_scope : active_tree_scopes_) {
1488 if (ScopedStyleResolver* resolver = tree_scope->GetScopedStyleResolver())
1489 resolver->V0ShadowAddedOnV1Document();
1490 }
1491 }
1492
1493 namespace {
1494
1495 enum RuleSetFlags {
1496 kFontFaceRules = 1 << 0,
1497 kKeyframesRules = 1 << 1,
1498 kFullRecalcRules = 1 << 2,
1499 kPropertyRules = 1 << 3,
1500 kScrollTimelineRules = 1 << 4,
1501 };
1502
GetRuleSetFlags(const HeapHashSet<Member<RuleSet>> rule_sets)1503 unsigned GetRuleSetFlags(const HeapHashSet<Member<RuleSet>> rule_sets) {
1504 unsigned flags = 0;
1505 for (auto& rule_set : rule_sets) {
1506 rule_set->CompactRulesIfNeeded();
1507 if (!rule_set->KeyframesRules().IsEmpty())
1508 flags |= kKeyframesRules;
1509 if (!rule_set->FontFaceRules().IsEmpty())
1510 flags |= kFontFaceRules;
1511 if (rule_set->NeedsFullRecalcForRuleSetInvalidation())
1512 flags |= kFullRecalcRules;
1513 if (!rule_set->PropertyRules().IsEmpty())
1514 flags |= kPropertyRules;
1515 if (!rule_set->ScrollTimelineRules().IsEmpty())
1516 flags |= kScrollTimelineRules;
1517 }
1518 return flags;
1519 }
1520
1521 } // namespace
1522
InvalidateForRuleSetChanges(TreeScope & tree_scope,const HeapHashSet<Member<RuleSet>> & changed_rule_sets,unsigned changed_rule_flags,InvalidationScope invalidation_scope)1523 void StyleEngine::InvalidateForRuleSetChanges(
1524 TreeScope& tree_scope,
1525 const HeapHashSet<Member<RuleSet>>& changed_rule_sets,
1526 unsigned changed_rule_flags,
1527 InvalidationScope invalidation_scope) {
1528 if (tree_scope.GetDocument().HasPendingForcedStyleRecalc())
1529 return;
1530 if (!tree_scope.GetDocument().documentElement())
1531 return;
1532 if (changed_rule_sets.IsEmpty())
1533 return;
1534
1535 Element& invalidation_root =
1536 ScopedStyleResolver::InvalidationRootForTreeScope(tree_scope);
1537 if (invalidation_root.GetStyleChangeType() == kSubtreeStyleChange)
1538 return;
1539
1540 if (changed_rule_flags & kFullRecalcRules) {
1541 invalidation_root.SetNeedsStyleRecalc(
1542 kSubtreeStyleChange,
1543 StyleChangeReasonForTracing::Create(
1544 style_change_reason::kActiveStylesheetsUpdate));
1545 return;
1546 }
1547
1548 if (changed_rule_sets.IsEmpty())
1549 return;
1550 ScheduleInvalidationsForRuleSets(tree_scope, changed_rule_sets,
1551 invalidation_scope);
1552 }
1553
InvalidateInitialData()1554 void StyleEngine::InvalidateInitialData() {
1555 initial_data_ = nullptr;
1556 }
1557
ApplyUserRuleSetChanges(const ActiveStyleSheetVector & old_style_sheets,const ActiveStyleSheetVector & new_style_sheets)1558 void StyleEngine::ApplyUserRuleSetChanges(
1559 const ActiveStyleSheetVector& old_style_sheets,
1560 const ActiveStyleSheetVector& new_style_sheets) {
1561 DCHECK(!IsHTMLImport());
1562 DCHECK(global_rule_set_);
1563 HeapHashSet<Member<RuleSet>> changed_rule_sets;
1564
1565 ActiveSheetsChange change = CompareActiveStyleSheets(
1566 old_style_sheets, new_style_sheets, changed_rule_sets);
1567
1568 if (change == kNoActiveSheetsChanged)
1569 return;
1570
1571 // With rules added or removed, we need to re-aggregate rule meta data.
1572 global_rule_set_->MarkDirty();
1573
1574 unsigned changed_rule_flags = GetRuleSetFlags(changed_rule_sets);
1575 bool has_rebuilt_font_face_cache = false;
1576 if (changed_rule_flags & kFontFaceRules) {
1577 if (ScopedStyleResolver* scoped_resolver =
1578 GetDocument().GetScopedStyleResolver()) {
1579 // User style and document scope author style shares the font cache. If
1580 // @font-face rules are added/removed from user stylesheets, we need to
1581 // reconstruct the font cache because @font-face rules from author style
1582 // need to be added to the cache after user rules.
1583 scoped_resolver->SetNeedsAppendAllSheets();
1584 MarkDocumentDirty();
1585 } else {
1586 has_rebuilt_font_face_cache = ClearFontFaceCacheAndAddUserFonts();
1587 }
1588 }
1589
1590 if (changed_rule_flags & kKeyframesRules) {
1591 if (change == kActiveSheetsChanged)
1592 ClearKeyframeRules();
1593
1594 for (auto* it = new_style_sheets.begin(); it != new_style_sheets.end();
1595 it++) {
1596 DCHECK(it->second);
1597 AddUserKeyframeRules(*it->second);
1598 }
1599 ScopedStyleResolver::KeyframesRulesAdded(GetDocument());
1600 }
1601
1602 if (changed_rule_flags & (kPropertyRules | kScrollTimelineRules)) {
1603 if (changed_rule_flags & kPropertyRules) {
1604 ClearPropertyRules();
1605 AddPropertyRulesFromSheets(new_style_sheets);
1606 }
1607 if (changed_rule_flags & kScrollTimelineRules) {
1608 ClearScrollTimelineRules();
1609 AddScrollTimelineRulesFromSheets(new_style_sheets);
1610 }
1611
1612 // We just cleared all the rules, which includes any author rules. They
1613 // must be forcibly re-added.
1614 if (ScopedStyleResolver* scoped_resolver =
1615 GetDocument().GetScopedStyleResolver()) {
1616 scoped_resolver->SetNeedsAppendAllSheets();
1617 MarkDocumentDirty();
1618 }
1619 }
1620
1621 if ((changed_rule_flags & kFontFaceRules) || has_rebuilt_font_face_cache) {
1622 GetFontSelector()->FontFaceInvalidated(
1623 FontInvalidationReason::kGeneralInvalidation);
1624 }
1625
1626 InvalidateForRuleSetChanges(GetDocument(), changed_rule_sets,
1627 changed_rule_flags, kInvalidateAllScopes);
1628 }
1629
ApplyRuleSetChanges(TreeScope & tree_scope,const ActiveStyleSheetVector & old_style_sheets,const ActiveStyleSheetVector & new_style_sheets)1630 void StyleEngine::ApplyRuleSetChanges(
1631 TreeScope& tree_scope,
1632 const ActiveStyleSheetVector& old_style_sheets,
1633 const ActiveStyleSheetVector& new_style_sheets) {
1634 DCHECK(!IsHTMLImport());
1635 DCHECK(global_rule_set_);
1636 HeapHashSet<Member<RuleSet>> changed_rule_sets;
1637
1638 ActiveSheetsChange change = CompareActiveStyleSheets(
1639 old_style_sheets, new_style_sheets, changed_rule_sets);
1640
1641 unsigned changed_rule_flags = GetRuleSetFlags(changed_rule_sets);
1642
1643 bool rebuild_font_face_cache = change == kActiveSheetsChanged &&
1644 (changed_rule_flags & kFontFaceRules) &&
1645 tree_scope.RootNode().IsDocumentNode();
1646 bool rebuild_at_property_registry = false;
1647 bool rebuild_at_scroll_timeline_map = false;
1648 ScopedStyleResolver* scoped_resolver = tree_scope.GetScopedStyleResolver();
1649 if (scoped_resolver && scoped_resolver->NeedsAppendAllSheets()) {
1650 rebuild_font_face_cache = true;
1651 rebuild_at_property_registry = true;
1652 rebuild_at_scroll_timeline_map = true;
1653 change = kActiveSheetsChanged;
1654 }
1655
1656 if (change == kNoActiveSheetsChanged)
1657 return;
1658
1659 // With rules added or removed, we need to re-aggregate rule meta data.
1660 global_rule_set_->MarkDirty();
1661
1662 if (changed_rule_flags & kKeyframesRules)
1663 ScopedStyleResolver::KeyframesRulesAdded(tree_scope);
1664
1665 if ((changed_rule_flags & kPropertyRules) || rebuild_at_property_registry) {
1666 // @property rules are (for now) ignored in shadow trees, per spec.
1667 // https://drafts.css-houdini.org/css-properties-values-api-1/#at-property-rule
1668 if (tree_scope.RootNode().IsDocumentNode()) {
1669 ClearPropertyRules();
1670 AddPropertyRulesFromSheets(active_user_style_sheets_);
1671 AddPropertyRulesFromSheets(new_style_sheets);
1672 }
1673 }
1674
1675 if ((changed_rule_flags & kScrollTimelineRules) ||
1676 rebuild_at_scroll_timeline_map) {
1677 // @scroll-timeline rules are currently not allowed in shadow trees.
1678 // https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule
1679 if (tree_scope.RootNode().IsDocumentNode()) {
1680 ClearScrollTimelineRules();
1681 AddScrollTimelineRulesFromSheets(active_user_style_sheets_);
1682 AddScrollTimelineRulesFromSheets(new_style_sheets);
1683 }
1684 }
1685
1686 bool has_rebuilt_font_face_cache = false;
1687 if (rebuild_font_face_cache)
1688 has_rebuilt_font_face_cache = ClearFontFaceCacheAndAddUserFonts();
1689
1690 unsigned append_start_index = 0;
1691 if (scoped_resolver) {
1692 // - If all sheets were removed, we remove the ScopedStyleResolver.
1693 // - If new sheets were appended to existing ones, start appending after the
1694 // common prefix.
1695 // - For other diffs, reset author style and re-add all sheets for the
1696 // TreeScope.
1697 if (new_style_sheets.IsEmpty())
1698 ResetAuthorStyle(tree_scope);
1699 else if (change == kActiveSheetsAppended)
1700 append_start_index = old_style_sheets.size();
1701 else
1702 scoped_resolver->ResetAuthorStyle();
1703 }
1704
1705 if (!new_style_sheets.IsEmpty()) {
1706 tree_scope.EnsureScopedStyleResolver().AppendActiveStyleSheets(
1707 append_start_index, new_style_sheets);
1708 }
1709
1710 if (tree_scope.RootNode().IsDocumentNode()) {
1711 if ((changed_rule_flags & kFontFaceRules) || has_rebuilt_font_face_cache) {
1712 GetFontSelector()->FontFaceInvalidated(
1713 FontInvalidationReason::kGeneralInvalidation);
1714 }
1715 }
1716
1717 InvalidateForRuleSetChanges(tree_scope, changed_rule_sets, changed_rule_flags,
1718 kInvalidateCurrentScope);
1719 }
1720
LoadVisionDeficiencyFilter()1721 void StyleEngine::LoadVisionDeficiencyFilter() {
1722 VisionDeficiency old_vision_deficiency = vision_deficiency_;
1723 vision_deficiency_ = GetDocument().GetPage()->GetVisionDeficiency();
1724 if (vision_deficiency_ == old_vision_deficiency)
1725 return;
1726
1727 if (vision_deficiency_ == VisionDeficiency::kNoVisionDeficiency) {
1728 vision_deficiency_filter_ = nullptr;
1729 } else {
1730 AtomicString url = CreateVisionDeficiencyFilterUrl(vision_deficiency_);
1731 cssvalue::CSSURIValue css_uri_value(url);
1732 SVGResource* svg_resource = css_uri_value.EnsureResourceReference();
1733 // Note: The fact that we're using data: URLs here is an
1734 // implementation detail. Emulating vision deficiencies should still
1735 // work even if the Document's Content-Security-Policy disallows
1736 // data: URLs.
1737 svg_resource->LoadWithoutCSP(GetDocument());
1738 vision_deficiency_filter_ =
1739 MakeGarbageCollected<ReferenceFilterOperation>(url, svg_resource);
1740 }
1741 }
1742
VisionDeficiencyChanged()1743 void StyleEngine::VisionDeficiencyChanged() {
1744 MarkViewportStyleDirty();
1745 }
1746
ApplyVisionDeficiencyStyle(scoped_refptr<ComputedStyle> layout_view_style)1747 void StyleEngine::ApplyVisionDeficiencyStyle(
1748 scoped_refptr<ComputedStyle> layout_view_style) {
1749 LoadVisionDeficiencyFilter();
1750 if (vision_deficiency_filter_) {
1751 FilterOperations ops;
1752 ops.Operations().push_back(vision_deficiency_filter_);
1753 layout_view_style->SetFilter(ops);
1754 }
1755 }
1756
EnsureMediaQueryEvaluator()1757 const MediaQueryEvaluator& StyleEngine::EnsureMediaQueryEvaluator() {
1758 if (!media_query_evaluator_) {
1759 if (GetDocument().GetFrame()) {
1760 media_query_evaluator_ =
1761 MakeGarbageCollected<MediaQueryEvaluator>(GetDocument().GetFrame());
1762 } else {
1763 media_query_evaluator_ = MakeGarbageCollected<MediaQueryEvaluator>("all");
1764 }
1765 }
1766 return *media_query_evaluator_;
1767 }
1768
MediaQueryAffectedByViewportChange()1769 bool StyleEngine::MediaQueryAffectedByViewportChange() {
1770 DCHECK(!IsHTMLImport());
1771 DCHECK(global_rule_set_);
1772 return EnsureMediaQueryEvaluator().DidResultsChange(
1773 global_rule_set_->GetRuleFeatureSet()
1774 .ViewportDependentMediaQueryResults());
1775 }
1776
MediaQueryAffectedByDeviceChange()1777 bool StyleEngine::MediaQueryAffectedByDeviceChange() {
1778 DCHECK(!IsHTMLImport());
1779 DCHECK(global_rule_set_);
1780 return EnsureMediaQueryEvaluator().DidResultsChange(
1781 global_rule_set_->GetRuleFeatureSet().DeviceDependentMediaQueryResults());
1782 }
1783
UpdateRemUnits(const ComputedStyle * old_root_style,const ComputedStyle * new_root_style)1784 bool StyleEngine::UpdateRemUnits(const ComputedStyle* old_root_style,
1785 const ComputedStyle* new_root_style) {
1786 if (!new_root_style || !UsesRemUnits())
1787 return false;
1788 if (!old_root_style || old_root_style->SpecifiedFontSize() !=
1789 new_root_style->SpecifiedFontSize()) {
1790 // Resolved rem units are stored in the matched properties cache so we need
1791 // to make sure to invalidate the cache if the documentElement font size
1792 // changes.
1793 GetStyleResolver().InvalidateMatchedPropertiesCache();
1794 return true;
1795 }
1796 return false;
1797 }
1798
PropertyRegistryChanged()1799 void StyleEngine::PropertyRegistryChanged() {
1800 // TODO(timloh): Invalidate only elements with this custom property set
1801 MarkAllElementsForStyleRecalc(StyleChangeReasonForTracing::Create(
1802 style_change_reason::kPropertyRegistration));
1803 if (resolver_)
1804 resolver_->InvalidateMatchedPropertiesCache();
1805 InvalidateInitialData();
1806 }
1807
EnvironmentVariableChanged()1808 void StyleEngine::EnvironmentVariableChanged() {
1809 MarkAllElementsForStyleRecalc(StyleChangeReasonForTracing::Create(
1810 style_change_reason::kPropertyRegistration));
1811 if (resolver_)
1812 resolver_->InvalidateMatchedPropertiesCache();
1813 }
1814
MarkForWhitespaceReattachment()1815 void StyleEngine::MarkForWhitespaceReattachment() {
1816 for (auto element : whitespace_reattach_set_) {
1817 if (element->NeedsReattachLayoutTree() || !element->GetLayoutObject())
1818 continue;
1819 // This element might be located inside a display locked subtree, so we
1820 // might mark it for ReattachLayoutTree later on instead.
1821 if (Element* locked_ancestor =
1822 DisplayLockUtilities::NearestLockedInclusiveAncestor(*element)) {
1823 locked_ancestor->GetDisplayLockContext()->AddToWhitespaceReattachSet(
1824 *element);
1825 continue;
1826 }
1827 DCHECK(!element->NeedsStyleRecalc());
1828 DCHECK(!element->ChildNeedsStyleRecalc());
1829 if (Node* first_child = LayoutTreeBuilderTraversal::FirstChild(*element))
1830 first_child->MarkAncestorsWithChildNeedsReattachLayoutTree();
1831 }
1832 }
1833
NodeWillBeRemoved(Node & node)1834 void StyleEngine::NodeWillBeRemoved(Node& node) {
1835 if (auto* element = DynamicTo<Element>(node)) {
1836 pending_invalidations_.RescheduleSiblingInvalidationsAsDescendants(
1837 *element);
1838 }
1839
1840 // Mark closest ancestor with with LayoutObject to have all whitespace
1841 // children being considered for re-attachment during the layout tree build.
1842
1843 LayoutObject* layout_object = node.GetLayoutObject();
1844 // The removed node does not have a layout object. No sibling whitespace nodes
1845 // will change rendering.
1846 if (!layout_object)
1847 return;
1848 // Floating or out-of-flow elements do not affect whitespace siblings.
1849 if (!layout_object->AffectsWhitespaceSiblings())
1850 return;
1851 layout_object = layout_object->Parent();
1852 while (layout_object->IsAnonymous())
1853 layout_object = layout_object->Parent();
1854 DCHECK(layout_object);
1855 DCHECK(layout_object->GetNode());
1856 if (auto* layout_object_element =
1857 DynamicTo<Element>(layout_object->GetNode())) {
1858 whitespace_reattach_set_.insert(layout_object_element);
1859 GetDocument().ScheduleLayoutTreeUpdateIfNeeded();
1860 }
1861 }
1862
ChildrenRemoved(ContainerNode & parent)1863 void StyleEngine::ChildrenRemoved(ContainerNode& parent) {
1864 if (!parent.isConnected())
1865 return;
1866 if (in_dom_removal_) {
1867 // This is necessary for nested removals. There are elements which
1868 // removes parts of its UA shadow DOM as part of being removed which means
1869 // we do a removal from within another removal where isConnected() is not
1870 // completely up to date which would confuse this code. Instead we will
1871 // clean traversal roots properly when we are called from the outer remove.
1872 // TODO(crbug.com/882869): MediaControlLoadingPanelElement
1873 // TODO(crbug.com/888448): TextFieldInputType::ListAttributeTargetChanged
1874 return;
1875 }
1876 style_invalidation_root_.ChildrenRemoved(parent);
1877 style_recalc_root_.ChildrenRemoved(parent);
1878 DCHECK(!layout_tree_rebuild_root_.GetRootNode());
1879 layout_tree_rebuild_root_.ChildrenRemoved(parent);
1880 }
1881
CollectMatchingUserRules(ElementRuleCollector & collector) const1882 void StyleEngine::CollectMatchingUserRules(
1883 ElementRuleCollector& collector) const {
1884 for (unsigned i = 0; i < active_user_style_sheets_.size(); ++i) {
1885 DCHECK(active_user_style_sheets_[i].second);
1886 collector.CollectMatchingRules(
1887 MatchRequest(active_user_style_sheets_[i].second, nullptr,
1888 active_user_style_sheets_[i].first, i));
1889 }
1890 }
1891
ClearPropertyRules()1892 void StyleEngine::ClearPropertyRules() {
1893 PropertyRegistration::RemoveDeclaredProperties(GetDocument());
1894 }
1895
ClearScrollTimelineRules()1896 void StyleEngine::ClearScrollTimelineRules() {
1897 scroll_timeline_map_.clear();
1898 }
1899
AddPropertyRulesFromSheets(const ActiveStyleSheetVector & sheets)1900 void StyleEngine::AddPropertyRulesFromSheets(
1901 const ActiveStyleSheetVector& sheets) {
1902 for (const ActiveStyleSheet& active_sheet : sheets) {
1903 if (RuleSet* rule_set = active_sheet.second)
1904 AddPropertyRules(*rule_set);
1905 }
1906 }
1907
AddScrollTimelineRulesFromSheets(const ActiveStyleSheetVector & sheets)1908 void StyleEngine::AddScrollTimelineRulesFromSheets(
1909 const ActiveStyleSheetVector& sheets) {
1910 for (const ActiveStyleSheet& active_sheet : sheets) {
1911 if (RuleSet* rule_set = active_sheet.second)
1912 AddScrollTimelineRules(*rule_set);
1913 }
1914 }
1915
AddUserFontFaceRules(const RuleSet & rule_set)1916 bool StyleEngine::AddUserFontFaceRules(const RuleSet& rule_set) {
1917 if (!font_selector_)
1918 return false;
1919
1920 const HeapVector<Member<StyleRuleFontFace>> font_face_rules =
1921 rule_set.FontFaceRules();
1922 for (auto& font_face_rule : font_face_rules) {
1923 if (FontFace* font_face = FontFace::Create(document_, font_face_rule))
1924 font_selector_->GetFontFaceCache()->Add(font_face_rule, font_face);
1925 }
1926 if (resolver_ && font_face_rules.size())
1927 resolver_->InvalidateMatchedPropertiesCache();
1928 return font_face_rules.size();
1929 }
1930
AddUserKeyframeRules(const RuleSet & rule_set)1931 void StyleEngine::AddUserKeyframeRules(const RuleSet& rule_set) {
1932 if (RuntimeEnabledFeatures::CSSKeyframesMemoryReductionEnabled())
1933 return;
1934
1935 const HeapVector<Member<StyleRuleKeyframes>> keyframes_rules =
1936 rule_set.KeyframesRules();
1937 for (unsigned i = 0; i < keyframes_rules.size(); ++i)
1938 AddUserKeyframeStyle(keyframes_rules[i]);
1939 }
1940
AddUserKeyframeStyle(StyleRuleKeyframes * rule)1941 void StyleEngine::AddUserKeyframeStyle(StyleRuleKeyframes* rule) {
1942 DCHECK(!RuntimeEnabledFeatures::CSSKeyframesMemoryReductionEnabled());
1943
1944 AtomicString animation_name(rule->GetName());
1945
1946 if (rule->IsVendorPrefixed()) {
1947 KeyframesRuleMap::iterator it = keyframes_rule_map_.find(animation_name);
1948 if (it == keyframes_rule_map_.end())
1949 keyframes_rule_map_.Set(animation_name, rule);
1950 else if (it->value->IsVendorPrefixed())
1951 keyframes_rule_map_.Set(animation_name, rule);
1952 } else {
1953 keyframes_rule_map_.Set(animation_name, rule);
1954 }
1955 }
1956
AddPropertyRules(const RuleSet & rule_set)1957 void StyleEngine::AddPropertyRules(const RuleSet& rule_set) {
1958 const HeapVector<Member<StyleRuleProperty>> property_rules =
1959 rule_set.PropertyRules();
1960 for (unsigned i = 0; i < property_rules.size(); ++i) {
1961 StyleRuleProperty* rule = property_rules[i];
1962 AtomicString name(rule->GetName());
1963 PropertyRegistration::DeclareProperty(GetDocument(), name, *rule);
1964 }
1965 }
1966
AddScrollTimelineRules(const RuleSet & rule_set)1967 void StyleEngine::AddScrollTimelineRules(const RuleSet& rule_set) {
1968 const HeapVector<Member<StyleRuleScrollTimeline>> scroll_timeline_rules =
1969 rule_set.ScrollTimelineRules();
1970 if (scroll_timeline_rules.IsEmpty())
1971 return;
1972 for (const auto& rule : scroll_timeline_rules)
1973 scroll_timeline_map_.Set(AtomicString(rule->GetName()), rule);
1974 MarkAllElementsForStyleRecalc(StyleChangeReasonForTracing::Create(
1975 style_change_reason::kScrollTimeline));
1976 }
1977
KeyframeStylesForAnimation(const AtomicString & animation_name)1978 StyleRuleKeyframes* StyleEngine::KeyframeStylesForAnimation(
1979 const AtomicString& animation_name) {
1980 if (RuntimeEnabledFeatures::CSSKeyframesMemoryReductionEnabled()) {
1981 return ScopedStyleResolver::KeyframeStylesForAnimationFromActiveSheets(
1982 animation_name, active_user_style_sheets_);
1983 }
1984
1985 if (keyframes_rule_map_.IsEmpty())
1986 return nullptr;
1987
1988 KeyframesRuleMap::iterator it = keyframes_rule_map_.find(animation_name);
1989 if (it == keyframes_rule_map_.end())
1990 return nullptr;
1991
1992 return it->value.Get();
1993 }
1994
FindScrollTimelineRule(const AtomicString & name)1995 StyleRuleScrollTimeline* StyleEngine::FindScrollTimelineRule(
1996 const AtomicString& name) {
1997 return scroll_timeline_map_.at(name);
1998 }
1999
EnsureEnvironmentVariables()2000 DocumentStyleEnvironmentVariables& StyleEngine::EnsureEnvironmentVariables() {
2001 if (!environment_variables_) {
2002 environment_variables_ = DocumentStyleEnvironmentVariables::Create(
2003 StyleEnvironmentVariables::GetRootInstance(), *document_);
2004 }
2005 return *environment_variables_.get();
2006 }
2007
MaybeCreateAndGetInitialData()2008 scoped_refptr<StyleInitialData> StyleEngine::MaybeCreateAndGetInitialData() {
2009 if (initial_data_)
2010 return initial_data_;
2011 if (const PropertyRegistry* registry = document_->GetPropertyRegistry()) {
2012 if (!registry->IsEmpty())
2013 initial_data_ = StyleInitialData::Create(*registry);
2014 }
2015 return initial_data_;
2016 }
2017
RecalcStyle()2018 void StyleEngine::RecalcStyle() {
2019 DCHECK(GetDocument().documentElement());
2020 Element* root_element = &style_recalc_root_.RootElement();
2021 Element* parent = root_element->ParentOrShadowHostElement();
2022
2023 SelectorFilterRootScope filter_scope(parent);
2024 root_element->RecalcStyle({});
2025
2026 for (ContainerNode* ancestor = root_element->GetStyleRecalcParent(); ancestor;
2027 ancestor = ancestor->GetStyleRecalcParent()) {
2028 if (auto* ancestor_element = DynamicTo<Element>(ancestor))
2029 ancestor_element->RecalcStyleForTraversalRootAncestor();
2030 ancestor->ClearChildNeedsStyleRecalc();
2031 }
2032 style_recalc_root_.Clear();
2033 PropagateWritingModeAndDirectionToHTMLRoot();
2034 }
2035
ClearEnsuredDescendantStyles(Element & root)2036 void StyleEngine::ClearEnsuredDescendantStyles(Element& root) {
2037 Node* current = &root;
2038 while (current) {
2039 if (auto* element = DynamicTo<Element>(current)) {
2040 if (const auto* style = element->GetComputedStyle()) {
2041 DCHECK(style->IsEnsuredOutsideFlatTree());
2042 element->SetComputedStyle(nullptr);
2043 element->ClearNeedsStyleRecalc();
2044 element->ClearChildNeedsStyleRecalc();
2045 current = FlatTreeTraversal::Next(*current, &root);
2046 continue;
2047 }
2048 }
2049 current = FlatTreeTraversal::NextSkippingChildren(*current, &root);
2050 }
2051 }
2052
RebuildLayoutTree()2053 void StyleEngine::RebuildLayoutTree() {
2054 DCHECK(GetDocument().documentElement());
2055 DCHECK(!InRebuildLayoutTree());
2056 in_layout_tree_rebuild_ = true;
2057
2058 // We need a root scope here in case we recalc style for ::first-letter
2059 // elements as part of UpdateFirstLetterPseudoElement.
2060 SelectorFilterRootScope filter_scope(nullptr);
2061
2062 Element& root_element = layout_tree_rebuild_root_.RootElement();
2063 {
2064 WhitespaceAttacher whitespace_attacher;
2065 root_element.RebuildLayoutTree(whitespace_attacher);
2066 }
2067
2068 for (ContainerNode* ancestor = root_element.GetReattachParent(); ancestor;
2069 ancestor = ancestor->GetReattachParent()) {
2070 if (auto* ancestor_element = DynamicTo<Element>(ancestor))
2071 ancestor_element->RebuildLayoutTreeForTraversalRootAncestor();
2072 ancestor->ClearChildNeedsStyleRecalc();
2073 ancestor->ClearChildNeedsReattachLayoutTree();
2074 }
2075 layout_tree_rebuild_root_.Clear();
2076 in_layout_tree_rebuild_ = false;
2077 }
2078
UpdateStyleAndLayoutTree()2079 void StyleEngine::UpdateStyleAndLayoutTree() {
2080 // All of layout tree dirtiness and rebuilding needs to happen on a stable
2081 // flat tree. We have an invariant that all of that happens in this method
2082 // as a result of style recalc and the following layout tree rebuild.
2083 //
2084 // NeedsReattachLayoutTree() marks dirty up the flat tree ancestors. Re-
2085 // slotting on a dirty tree could break ancestor chains and fail to update the
2086 // tree properly.
2087 DCHECK(!NeedsLayoutTreeRebuild());
2088
2089 UpdateViewportStyle();
2090
2091 if (Element* document_element = GetDocument().documentElement()) {
2092 NthIndexCache nth_index_cache(GetDocument());
2093 if (NeedsStyleRecalc()) {
2094 TRACE_EVENT0("blink,blink_style", "Document::recalcStyle");
2095 SCOPED_BLINK_UMA_HISTOGRAM_TIMER_HIGHRES("Style.RecalcTime");
2096 Element* viewport_defining = GetDocument().ViewportDefiningElement();
2097 RecalcStyle();
2098 if (viewport_defining != GetDocument().ViewportDefiningElement())
2099 ViewportDefiningElementDidChange();
2100 }
2101 MarkForWhitespaceReattachment();
2102 if (NeedsLayoutTreeRebuild()) {
2103 TRACE_EVENT0("blink,blink_style", "Document::rebuildLayoutTree");
2104 SCOPED_BLINK_UMA_HISTOGRAM_TIMER_HIGHRES("Style.RebuildLayoutTreeTime");
2105 RebuildLayoutTree();
2106 }
2107 } else {
2108 style_recalc_root_.Clear();
2109 }
2110 ClearWhitespaceReattachSet();
2111 UpdateColorSchemeBackground();
2112 }
2113
ViewportDefiningElementDidChange()2114 void StyleEngine::ViewportDefiningElementDidChange() {
2115 HTMLBodyElement* body = GetDocument().FirstBodyElement();
2116 if (!body || body->NeedsReattachLayoutTree())
2117 return;
2118 LayoutObject* layout_object = body->GetLayoutObject();
2119 if (layout_object && layout_object->IsLayoutBlock()) {
2120 // When the overflow style for documentElement changes to or from visible,
2121 // it changes whether the body element's box should have scrollable overflow
2122 // on its own box or propagated to the viewport. If the body style did not
2123 // need a recalc, this will not be updated as its done as part of setting
2124 // ComputedStyle on the LayoutObject. Force a SetStyle for body when the
2125 // ViewportDefiningElement changes in order to trigger an update of
2126 // IsScrollContainer() and the PaintLayer in StyleDidChange().
2127 layout_object->SetStyle(ComputedStyle::Clone(*layout_object->Style()));
2128 }
2129 }
2130
UpdateStyleInvalidationRoot(ContainerNode * ancestor,Node * dirty_node)2131 void StyleEngine::UpdateStyleInvalidationRoot(ContainerNode* ancestor,
2132 Node* dirty_node) {
2133 DCHECK(!IsHTMLImport());
2134 if (GetDocument().IsActive()) {
2135 if (in_dom_removal_) {
2136 ancestor = nullptr;
2137 dirty_node = document_;
2138 }
2139 style_invalidation_root_.Update(ancestor, dirty_node);
2140 }
2141 }
2142
UpdateStyleRecalcRoot(ContainerNode * ancestor,Node * dirty_node)2143 void StyleEngine::UpdateStyleRecalcRoot(ContainerNode* ancestor,
2144 Node* dirty_node) {
2145 if (!GetDocument().IsActive())
2146 return;
2147 // We have at least one instance where we mark style dirty from style recalc
2148 // (from LayoutTextControl::StyleDidChange()). That means we are in the
2149 // process of traversing down the tree from the recalc root. Any updates to
2150 // the style recalc root will be cleared after the style recalc traversal
2151 // finishes and updating it may just trigger sanity DCHECKs in
2152 // StyleTraversalRoot. Just return here instead.
2153 if (GetDocument().InStyleRecalc()) {
2154 DCHECK(allow_mark_style_dirty_from_recalc_);
2155 return;
2156 }
2157 DCHECK(!in_layout_tree_rebuild_);
2158 if (in_dom_removal_) {
2159 ancestor = nullptr;
2160 dirty_node = document_;
2161 }
2162 style_recalc_root_.Update(ancestor, dirty_node);
2163 }
2164
UpdateLayoutTreeRebuildRoot(ContainerNode * ancestor,Node * dirty_node)2165 void StyleEngine::UpdateLayoutTreeRebuildRoot(ContainerNode* ancestor,
2166 Node* dirty_node) {
2167 DCHECK(!in_dom_removal_);
2168 if (GetDocument().IsActive())
2169 layout_tree_rebuild_root_.Update(ancestor, dirty_node);
2170 }
2171
SupportsDarkColorScheme()2172 bool StyleEngine::SupportsDarkColorScheme() {
2173 if (!meta_color_scheme_)
2174 return false;
2175 bool has_light = false;
2176 bool has_dark = false;
2177 if (const auto* scheme_list = DynamicTo<CSSValueList>(*meta_color_scheme_)) {
2178 for (auto& item : *scheme_list) {
2179 if (const auto* ident = DynamicTo<CSSIdentifierValue>(*item)) {
2180 if (ident->GetValueID() == CSSValueID::kDark)
2181 has_dark = true;
2182 else if (ident->GetValueID() == CSSValueID::kLight)
2183 has_light = true;
2184 }
2185 }
2186 }
2187 return has_dark &&
2188 (!has_light ||
2189 preferred_color_scheme_ == mojom::blink::PreferredColorScheme::kDark);
2190 }
2191
UpdateColorScheme()2192 void StyleEngine::UpdateColorScheme() {
2193 auto* settings = GetDocument().GetSettings();
2194 auto* web_theme_engine =
2195 Platform::Current() ? Platform::Current()->ThemeEngine() : nullptr;
2196 if (!settings || !web_theme_engine)
2197 return;
2198
2199 ForcedColors old_forced_colors = forced_colors_;
2200 forced_colors_ = web_theme_engine->GetForcedColors();
2201
2202 mojom::blink::PreferredColorScheme old_preferred_color_scheme =
2203 preferred_color_scheme_;
2204 preferred_color_scheme_ = settings->GetPreferredColorScheme();
2205 if (const auto* overrides =
2206 GetDocument().GetPage()->GetMediaFeatureOverrides()) {
2207 MediaQueryExpValue value = overrides->GetOverride("prefers-color-scheme");
2208 if (value.IsValid())
2209 preferred_color_scheme_ = CSSValueIDToPreferredColorScheme(value.id);
2210 }
2211 if (!SupportsDarkColorScheme() && settings->GetForceDarkModeEnabled()) {
2212 // Make sure we don't match (prefers-color-scheme: dark) when forced
2213 // darkening is enabled.
2214 preferred_color_scheme_ = mojom::blink::PreferredColorScheme::kLight;
2215 }
2216 if (GetDocument().Printing())
2217 preferred_color_scheme_ = mojom::blink::PreferredColorScheme::kLight;
2218
2219 bool color_scheme_changed = false;
2220 if (forced_colors_ != old_forced_colors ||
2221 preferred_color_scheme_ != old_preferred_color_scheme) {
2222 PlatformColorsChanged();
2223 color_scheme_changed = true;
2224 }
2225 UpdateColorSchemeBackground(color_scheme_changed);
2226
2227 UpdateColorSchemeMetrics();
2228 }
2229
UpdateColorSchemeMetrics()2230 void StyleEngine::UpdateColorSchemeMetrics() {
2231 auto* settings = GetDocument().GetSettings();
2232 if (settings->GetForceDarkModeEnabled())
2233 UseCounter::Count(GetDocument(), WebFeature::kForcedDarkMode);
2234
2235 // True if the preferred color scheme will match dark.
2236 if (preferred_color_scheme_ == mojom::blink::PreferredColorScheme::kDark)
2237 UseCounter::Count(GetDocument(), WebFeature::kPreferredColorSchemeDark);
2238
2239 // This is equal to kPreferredColorSchemeDark in most cases, but can differ
2240 // with forced dark mode. With the system in dark mode and forced dark mode
2241 // enabled, the preferred color scheme can be light while the setting is dark.
2242 if (settings->GetPreferredColorScheme() ==
2243 mojom::blink::PreferredColorScheme::kDark) {
2244 UseCounter::Count(GetDocument(),
2245 WebFeature::kPreferredColorSchemeDarkSetting);
2246 }
2247
2248 // Record kColorSchemeDarkSupportedOnRoot if the meta color-scheme contains
2249 // dark (though dark may not be used). This metric is also recorded in
2250 // longhands_custom.cc (see: ColorScheme::ApplyValue) if the root style
2251 // color-scheme contains dark.
2252 if (meta_color_scheme_) {
2253 const auto* scheme_list = DynamicTo<CSSValueList>(*meta_color_scheme_);
2254 if (scheme_list) {
2255 for (auto& item : *scheme_list) {
2256 const auto* ident = DynamicTo<CSSIdentifierValue>(*item);
2257 if (ident && ident->GetValueID() == CSSValueID::kDark) {
2258 UseCounter::Count(GetDocument(),
2259 WebFeature::kColorSchemeDarkSupportedOnRoot);
2260 }
2261 }
2262 }
2263 }
2264 }
2265
ColorSchemeChanged()2266 void StyleEngine::ColorSchemeChanged() {
2267 UpdateColorScheme();
2268 }
2269
SetColorSchemeFromMeta(const CSSValue * color_scheme)2270 void StyleEngine::SetColorSchemeFromMeta(const CSSValue* color_scheme) {
2271 meta_color_scheme_ = color_scheme;
2272 DCHECK(GetDocument().documentElement());
2273 GetDocument().documentElement()->SetNeedsStyleRecalc(
2274 kLocalStyleChange, StyleChangeReasonForTracing::Create(
2275 style_change_reason::kPlatformColorChange));
2276 UpdateColorScheme();
2277 }
2278
UpdateColorSchemeBackground(bool color_scheme_changed)2279 void StyleEngine::UpdateColorSchemeBackground(bool color_scheme_changed) {
2280 LocalFrameView* view = GetDocument().View();
2281 if (!view)
2282 return;
2283
2284 LocalFrameView::UseColorAdjustBackground use_color_adjust_background =
2285 LocalFrameView::UseColorAdjustBackground::kNo;
2286
2287 if (forced_colors_ != ForcedColors::kNone) {
2288 if (GetDocument().IsInMainFrame()) {
2289 use_color_adjust_background =
2290 LocalFrameView::UseColorAdjustBackground::kIfBaseNotTransparent;
2291 }
2292 } else {
2293 // Find out if we should use a canvas color that is different from the
2294 // view's base background color in order to match the root element color-
2295 // scheme. See spec:
2296 // https://drafts.csswg.org/css-color-adjust/#color-scheme-effect
2297 mojom::blink::ColorScheme root_color_scheme =
2298 mojom::blink::ColorScheme::kLight;
2299 if (auto* root_element = GetDocument().documentElement()) {
2300 if (const ComputedStyle* style = root_element->GetComputedStyle())
2301 root_color_scheme = style->UsedColorSchemeForInitialColors();
2302 else if (SupportsDarkColorScheme())
2303 root_color_scheme = mojom::blink::ColorScheme::kDark;
2304 }
2305 color_scheme_background_ =
2306 root_color_scheme == mojom::blink::ColorScheme::kLight
2307 ? Color::kWhite
2308 : Color(0x12, 0x12, 0x12);
2309 if (GetDocument().IsInMainFrame()) {
2310 if (root_color_scheme == mojom::blink::ColorScheme::kDark) {
2311 use_color_adjust_background =
2312 LocalFrameView::UseColorAdjustBackground::kIfBaseNotTransparent;
2313 }
2314 } else if (root_color_scheme != owner_color_scheme_) {
2315 // Iframes should paint a solid background if the embedding iframe has a
2316 // used color-scheme different from the used color-scheme of the embedded
2317 // root element. Normally, iframes as transparent by default.
2318 use_color_adjust_background =
2319 LocalFrameView::UseColorAdjustBackground::kYes;
2320 }
2321 }
2322
2323 view->SetUseColorAdjustBackground(use_color_adjust_background,
2324 color_scheme_changed);
2325 }
2326
SetOwnerColorScheme(mojom::blink::ColorScheme color_scheme)2327 void StyleEngine::SetOwnerColorScheme(mojom::blink::ColorScheme color_scheme) {
2328 DCHECK(!GetDocument().IsInMainFrame());
2329 if (owner_color_scheme_ == color_scheme)
2330 return;
2331 owner_color_scheme_ = color_scheme;
2332 UpdateColorSchemeBackground(true);
2333 }
2334
UpdateForcedBackgroundColor()2335 void StyleEngine::UpdateForcedBackgroundColor() {
2336 forced_background_color_ = LayoutTheme::GetTheme().SystemColor(
2337 CSSValueID::kCanvas, mojom::blink::ColorScheme::kLight);
2338 }
2339
ColorAdjustBackgroundColor() const2340 Color StyleEngine::ColorAdjustBackgroundColor() const {
2341 if (forced_colors_ != ForcedColors::kNone)
2342 return ForcedBackgroundColor();
2343 return color_scheme_background_;
2344 }
2345
MarkAllElementsForStyleRecalc(const StyleChangeReasonForTracing & reason)2346 void StyleEngine::MarkAllElementsForStyleRecalc(
2347 const StyleChangeReasonForTracing& reason) {
2348 if (Element* root = GetDocument().documentElement())
2349 root->SetNeedsStyleRecalc(kSubtreeStyleChange, reason);
2350 }
2351
UpdateViewportStyle()2352 void StyleEngine::UpdateViewportStyle() {
2353 if (!viewport_style_dirty_)
2354 return;
2355
2356 viewport_style_dirty_ = false;
2357
2358 scoped_refptr<ComputedStyle> viewport_style = resolver_->StyleForViewport();
2359 if (ComputedStyle::ComputeDifference(
2360 viewport_style.get(), GetDocument().GetLayoutView()->Style()) !=
2361 ComputedStyle::Difference::kEqual) {
2362 GetDocument().GetLayoutView()->SetStyle(std::move(viewport_style));
2363 }
2364 }
2365
NeedsFullStyleUpdate() const2366 bool StyleEngine::NeedsFullStyleUpdate() const {
2367 return NeedsActiveStyleUpdate() || NeedsWhitespaceReattachment() ||
2368 IsViewportStyleDirty();
2369 }
2370
PropagateWritingModeAndDirectionToHTMLRoot()2371 void StyleEngine::PropagateWritingModeAndDirectionToHTMLRoot() {
2372 if (HTMLHtmlElement* root_element =
2373 DynamicTo<HTMLHtmlElement>(GetDocument().documentElement()))
2374 root_element->PropagateWritingModeAndDirectionFromBody();
2375 }
2376
Trace(Visitor * visitor) const2377 void StyleEngine::Trace(Visitor* visitor) const {
2378 visitor->Trace(document_);
2379 visitor->Trace(injected_user_style_sheets_);
2380 visitor->Trace(injected_author_style_sheets_);
2381 visitor->Trace(active_user_style_sheets_);
2382 visitor->Trace(custom_element_default_style_sheets_);
2383 visitor->Trace(keyframes_rule_map_);
2384 visitor->Trace(scroll_timeline_map_);
2385 visitor->Trace(inspector_style_sheet_);
2386 visitor->Trace(document_style_sheet_collection_);
2387 visitor->Trace(style_sheet_collection_map_);
2388 visitor->Trace(dirty_tree_scopes_);
2389 visitor->Trace(active_tree_scopes_);
2390 visitor->Trace(tree_boundary_crossing_scopes_);
2391 visitor->Trace(resolver_);
2392 visitor->Trace(vision_deficiency_filter_);
2393 visitor->Trace(viewport_resolver_);
2394 visitor->Trace(media_query_evaluator_);
2395 visitor->Trace(global_rule_set_);
2396 visitor->Trace(pending_invalidations_);
2397 visitor->Trace(style_invalidation_root_);
2398 visitor->Trace(style_recalc_root_);
2399 visitor->Trace(layout_tree_rebuild_root_);
2400 visitor->Trace(whitespace_reattach_set_);
2401 visitor->Trace(font_selector_);
2402 visitor->Trace(text_to_sheet_cache_);
2403 visitor->Trace(sheet_to_text_cache_);
2404 visitor->Trace(tracker_);
2405 visitor->Trace(meta_color_scheme_);
2406 visitor->Trace(text_tracks_);
2407 visitor->Trace(vtt_originating_element_);
2408 FontSelectorClient::Trace(visitor);
2409 }
2410
2411 } // namespace blink
2412