1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2006, 2007, 2012 Apple Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
22 
23 #include "third_party/blink/renderer/bindings/core/v8/media_list_or_string.h"
24 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
25 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
26 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
27 #include "third_party/blink/renderer/bindings/core/v8/v8_css_style_sheet_init.h"
28 #include "third_party/blink/renderer/core/css/css_import_rule.h"
29 #include "third_party/blink/renderer/core/css/css_rule_list.h"
30 #include "third_party/blink/renderer/core/css/media_list.h"
31 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
32 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
33 #include "third_party/blink/renderer/core/css/parser/css_parser_impl.h"
34 #include "third_party/blink/renderer/core/css/style_engine.h"
35 #include "third_party/blink/renderer/core/css/style_rule.h"
36 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
37 #include "third_party/blink/renderer/core/dom/document.h"
38 #include "third_party/blink/renderer/core/dom/node.h"
39 #include "third_party/blink/renderer/core/frame/deprecation.h"
40 #include "third_party/blink/renderer/core/html/html_link_element.h"
41 #include "third_party/blink/renderer/core/html/html_style_element.h"
42 #include "third_party/blink/renderer/core/html_names.h"
43 #include "third_party/blink/renderer/core/inspector/console_message.h"
44 #include "third_party/blink/renderer/core/probe/core_probes.h"
45 #include "third_party/blink/renderer/core/svg/svg_style_element.h"
46 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
47 #include "third_party/blink/renderer/platform/bindings/script_state.h"
48 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
49 #include "third_party/blink/renderer/platform/heap/heap.h"
50 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
51 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
52 
53 namespace blink {
54 
55 class StyleSheetCSSRuleList final : public CSSRuleList {
56  public:
StyleSheetCSSRuleList(CSSStyleSheet * sheet)57   StyleSheetCSSRuleList(CSSStyleSheet* sheet) : style_sheet_(sheet) {}
58 
Trace(Visitor * visitor) const59   void Trace(Visitor* visitor) const override {
60     visitor->Trace(style_sheet_);
61     CSSRuleList::Trace(visitor);
62   }
63 
64  private:
length() const65   unsigned length() const override { return style_sheet_->length(); }
item(unsigned index) const66   CSSRule* item(unsigned index) const override {
67     return style_sheet_->item(index);
68   }
69 
GetStyleSheet() const70   CSSStyleSheet* GetStyleSheet() const override { return style_sheet_; }
71 
72   Member<CSSStyleSheet> style_sheet_;
73 };
74 
75 #if DCHECK_IS_ON()
IsAcceptableCSSStyleSheetParent(const Node & parent_node)76 static bool IsAcceptableCSSStyleSheetParent(const Node& parent_node) {
77   // Only these nodes can be parents of StyleSheets, and they need to call
78   // clearOwnerNode() when moved out of document. Note that destructor of
79   // the nodes don't call clearOwnerNode() with Oilpan.
80   return parent_node.IsDocumentNode() || IsA<HTMLLinkElement>(parent_node) ||
81          IsA<HTMLStyleElement>(parent_node) ||
82          IsA<SVGStyleElement>(parent_node) ||
83          parent_node.getNodeType() == Node::kProcessingInstructionNode;
84 }
85 #endif
86 
87 // static
SingleOwnerDocument(const CSSStyleSheet * style_sheet)88 const Document* CSSStyleSheet::SingleOwnerDocument(
89     const CSSStyleSheet* style_sheet) {
90   if (style_sheet)
91     return StyleSheetContents::SingleOwnerDocument(style_sheet->Contents());
92   return nullptr;
93 }
94 
Create(Document & document,const CSSStyleSheetInit * options,ExceptionState & exception_state)95 CSSStyleSheet* CSSStyleSheet::Create(Document& document,
96                                      const CSSStyleSheetInit* options,
97                                      ExceptionState& exception_state) {
98   auto* parser_context = MakeGarbageCollected<CSSParserContext>(document);
99   if (AdTracker::IsAdScriptExecutingInDocument(&document))
100     parser_context->SetIsAdRelated();
101 
102   // Following steps at spec draft
103   // https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-cssstylesheet
104   auto* contents = MakeGarbageCollected<StyleSheetContents>(parser_context);
105   CSSStyleSheet* sheet = MakeGarbageCollected<CSSStyleSheet>(contents, nullptr);
106   sheet->SetConstructorDocument(document);
107   sheet->SetTitle(options->title());
108   sheet->ClearOwnerNode();
109   sheet->ClearOwnerRule();
110   contents->RegisterClient(sheet);
111   scoped_refptr<MediaQuerySet> media_query_set;
112   if (options->media().IsString()) {
113     media_query_set = MediaQuerySet::Create(options->media().GetAsString(),
114                                             document.GetExecutionContext());
115   } else {
116     media_query_set = options->media().GetAsMediaList()->Queries()->Copy();
117   }
118   auto* media_list = MakeGarbageCollected<MediaList>(
119       media_query_set, const_cast<CSSStyleSheet*>(sheet));
120   sheet->SetMedia(media_list);
121   if (options->alternate())
122     sheet->SetAlternateFromConstructor(true);
123   if (options->disabled())
124     sheet->setDisabled(true);
125   return sheet;
126 }
127 
CreateInline(StyleSheetContents * sheet,Node & owner_node,const TextPosition & start_position)128 CSSStyleSheet* CSSStyleSheet::CreateInline(StyleSheetContents* sheet,
129                                            Node& owner_node,
130                                            const TextPosition& start_position) {
131   DCHECK(sheet);
132   return MakeGarbageCollected<CSSStyleSheet>(sheet, owner_node, true,
133                                              start_position);
134 }
135 
CreateInline(Node & owner_node,const KURL & base_url,const TextPosition & start_position,const WTF::TextEncoding & encoding)136 CSSStyleSheet* CSSStyleSheet::CreateInline(Node& owner_node,
137                                            const KURL& base_url,
138                                            const TextPosition& start_position,
139                                            const WTF::TextEncoding& encoding) {
140   auto* parser_context = MakeGarbageCollected<CSSParserContext>(
141       owner_node.GetDocument(), owner_node.GetDocument().BaseURL(),
142       true /* origin_clean */, owner_node.GetDocument().GetReferrerPolicy(),
143       encoding);
144   if (AdTracker::IsAdScriptExecutingInDocument(&owner_node.GetDocument()))
145     parser_context->SetIsAdRelated();
146   auto* sheet = MakeGarbageCollected<StyleSheetContents>(parser_context,
147                                                          base_url.GetString());
148   return MakeGarbageCollected<CSSStyleSheet>(sheet, owner_node, true,
149                                              start_position);
150 }
151 
CSSStyleSheet(StyleSheetContents * contents,CSSImportRule * owner_rule)152 CSSStyleSheet::CSSStyleSheet(StyleSheetContents* contents,
153                              CSSImportRule* owner_rule)
154     : contents_(contents),
155       owner_rule_(owner_rule),
156       start_position_(TextPosition::MinimumPosition()) {
157   contents_->RegisterClient(this);
158 }
159 
CSSStyleSheet(StyleSheetContents * contents,Node & owner_node,bool is_inline_stylesheet,const TextPosition & start_position)160 CSSStyleSheet::CSSStyleSheet(StyleSheetContents* contents,
161                              Node& owner_node,
162                              bool is_inline_stylesheet,
163                              const TextPosition& start_position)
164     : contents_(contents),
165       is_inline_stylesheet_(is_inline_stylesheet),
166       owner_node_(&owner_node),
167       start_position_(start_position) {
168 #if DCHECK_IS_ON()
169   DCHECK(IsAcceptableCSSStyleSheetParent(owner_node));
170 #endif
171   contents_->RegisterClient(this);
172 }
173 
174 CSSStyleSheet::~CSSStyleSheet() = default;
175 
WillMutateRules()176 void CSSStyleSheet::WillMutateRules() {
177   // If we are the only client it is safe to mutate.
178   if (!contents_->IsUsedFromTextCache() &&
179       !contents_->IsReferencedFromResource()) {
180     contents_->ClearRuleSet();
181     contents_->SetMutable();
182     return;
183   }
184   // Only cacheable stylesheets should have multiple clients.
185   DCHECK(contents_->IsCacheableForStyleElement() ||
186          contents_->IsCacheableForResource());
187 
188   // Copy-on-write.
189   contents_->UnregisterClient(this);
190   contents_ = contents_->Copy();
191   contents_->RegisterClient(this);
192 
193   contents_->SetMutable();
194 
195   // Any existing CSSOM wrappers need to be connected to the copied child rules.
196   ReattachChildRuleCSSOMWrappers();
197 }
198 
DidMutate(Mutation mutation)199 void CSSStyleSheet::DidMutate(Mutation mutation) {
200   if (mutation == Mutation::kRules) {
201     DCHECK(contents_->IsMutable());
202     DCHECK_LE(contents_->ClientSize(), 1u);
203   }
204   Document* document = OwnerDocument();
205   if (!document || !document->IsActive())
206     return;
207   if (!custom_element_tag_names_.IsEmpty()) {
208     document->GetStyleEngine().ScheduleCustomElementInvalidations(
209         custom_element_tag_names_);
210   }
211   bool invalidate_matched_properties_cache = false;
212   if (ownerNode() && ownerNode()->isConnected()) {
213     document->GetStyleEngine().SetNeedsActiveStyleUpdate(
214         ownerNode()->GetTreeScope());
215     invalidate_matched_properties_cache = true;
216   } else if (!adopted_tree_scopes_.IsEmpty()) {
217     for (auto tree_scope : adopted_tree_scopes_) {
218       // It is currently required that adopted sheets can not be moved between
219       // documents.
220       DCHECK(tree_scope->GetDocument() == document);
221       if (!tree_scope->RootNode().isConnected())
222         continue;
223       document->GetStyleEngine().SetNeedsActiveStyleUpdate(*tree_scope);
224       invalidate_matched_properties_cache = true;
225     }
226   }
227   if (mutation == Mutation::kRules) {
228     if (invalidate_matched_properties_cache)
229       document->GetStyleResolver().InvalidateMatchedPropertiesCache();
230     probe::DidMutateStyleSheet(document, this);
231   }
232 }
233 
EnableRuleAccessForInspector()234 void CSSStyleSheet::EnableRuleAccessForInspector() {
235   enable_rule_access_for_inspector_ = true;
236 }
DisableRuleAccessForInspector()237 void CSSStyleSheet::DisableRuleAccessForInspector() {
238   enable_rule_access_for_inspector_ = false;
239 }
240 
InspectorMutationScope(CSSStyleSheet * sheet)241 CSSStyleSheet::InspectorMutationScope::InspectorMutationScope(
242     CSSStyleSheet* sheet)
243     : style_sheet_(sheet) {
244   style_sheet_->EnableRuleAccessForInspector();
245 }
246 
~InspectorMutationScope()247 CSSStyleSheet::InspectorMutationScope::~InspectorMutationScope() {
248   style_sheet_->DisableRuleAccessForInspector();
249 }
250 
ReattachChildRuleCSSOMWrappers()251 void CSSStyleSheet::ReattachChildRuleCSSOMWrappers() {
252   for (unsigned i = 0; i < child_rule_cssom_wrappers_.size(); ++i) {
253     if (!child_rule_cssom_wrappers_[i])
254       continue;
255     child_rule_cssom_wrappers_[i]->Reattach(contents_->RuleAt(i));
256   }
257 }
258 
setDisabled(bool disabled)259 void CSSStyleSheet::setDisabled(bool disabled) {
260   if (disabled == is_disabled_)
261     return;
262   is_disabled_ = disabled;
263 
264   DidMutate(Mutation::kSheet);
265 }
266 
SetMediaQueries(scoped_refptr<MediaQuerySet> media_queries)267 void CSSStyleSheet::SetMediaQueries(
268     scoped_refptr<MediaQuerySet> media_queries) {
269   media_queries_ = std::move(media_queries);
270   if (media_cssom_wrapper_ && media_queries_)
271     media_cssom_wrapper_->Reattach(media_queries_.get());
272 }
273 
MatchesMediaQueries(const MediaQueryEvaluator & evaluator)274 bool CSSStyleSheet::MatchesMediaQueries(const MediaQueryEvaluator& evaluator) {
275   viewport_dependent_media_query_results_.clear();
276   device_dependent_media_query_results_.clear();
277 
278   if (!media_queries_)
279     return true;
280   return evaluator.Eval(*media_queries_,
281                         &viewport_dependent_media_query_results_,
282                         &device_dependent_media_query_results_);
283 }
284 
length() const285 unsigned CSSStyleSheet::length() const {
286   return contents_->RuleCount();
287 }
288 
item(unsigned index)289 CSSRule* CSSStyleSheet::item(unsigned index) {
290   unsigned rule_count = length();
291   if (index >= rule_count)
292     return nullptr;
293 
294   if (child_rule_cssom_wrappers_.IsEmpty())
295     child_rule_cssom_wrappers_.Grow(rule_count);
296   DCHECK_EQ(child_rule_cssom_wrappers_.size(), rule_count);
297 
298   Member<CSSRule>& css_rule = child_rule_cssom_wrappers_[index];
299   if (!css_rule)
300     css_rule = contents_->RuleAt(index)->CreateCSSOMWrapper(this);
301   return css_rule.Get();
302 }
303 
ClearOwnerNode()304 void CSSStyleSheet::ClearOwnerNode() {
305   DidMutate(Mutation::kSheet);
306   if (owner_node_)
307     contents_->UnregisterClient(this);
308   owner_node_ = nullptr;
309 }
310 
CanAccessRules() const311 bool CSSStyleSheet::CanAccessRules() const {
312   return enable_rule_access_for_inspector_ || contents_->IsOriginClean();
313 }
314 
rules(ExceptionState & exception_state)315 CSSRuleList* CSSStyleSheet::rules(ExceptionState& exception_state) {
316   return cssRules(exception_state);
317 }
318 
insertRule(const String & rule_string,unsigned index,ExceptionState & exception_state)319 unsigned CSSStyleSheet::insertRule(const String& rule_string,
320                                    unsigned index,
321                                    ExceptionState& exception_state) {
322   if (!CanAccessRules()) {
323     exception_state.ThrowSecurityError(
324         "Cannot access StyleSheet to insertRule");
325     return 0;
326   }
327 
328   DCHECK(child_rule_cssom_wrappers_.IsEmpty() ||
329          child_rule_cssom_wrappers_.size() == contents_->RuleCount());
330 
331   if (index > length()) {
332     exception_state.ThrowDOMException(
333         DOMExceptionCode::kIndexSizeError,
334         "The index provided (" + String::Number(index) +
335             ") is larger than the maximum index (" + String::Number(length()) +
336             ").");
337     return 0;
338   }
339   const auto* context =
340       MakeGarbageCollected<CSSParserContext>(contents_->ParserContext(), this);
341   StyleRuleBase* rule =
342       CSSParser::ParseRule(context, contents_.Get(), rule_string);
343 
344   if (!rule) {
345     exception_state.ThrowDOMException(
346         DOMExceptionCode::kSyntaxError,
347         "Failed to parse the rule '" + rule_string + "'.");
348     return 0;
349   }
350   RuleMutationScope mutation_scope(this);
351   if (rule->IsImportRule() && IsConstructed()) {
352     exception_state.ThrowDOMException(
353         DOMExceptionCode::kSyntaxError,
354         "Can't insert @import rules into a constructed stylesheet.");
355     return 0;
356   }
357   bool success = contents_->WrapperInsertRule(rule, index);
358   if (!success) {
359     if (rule->IsNamespaceRule())
360       exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
361                                         "Failed to insert the rule");
362     else
363       exception_state.ThrowDOMException(
364           DOMExceptionCode::kHierarchyRequestError,
365           "Failed to insert the rule.");
366     return 0;
367   }
368   if (!child_rule_cssom_wrappers_.IsEmpty())
369     child_rule_cssom_wrappers_.insert(index, Member<CSSRule>(nullptr));
370 
371   return index;
372 }
373 
deleteRule(unsigned index,ExceptionState & exception_state)374 void CSSStyleSheet::deleteRule(unsigned index,
375                                ExceptionState& exception_state) {
376   if (!CanAccessRules()) {
377     exception_state.ThrowSecurityError(
378         "Cannot access StyleSheet to deleteRule");
379     return;
380   }
381 
382   DCHECK(child_rule_cssom_wrappers_.IsEmpty() ||
383          child_rule_cssom_wrappers_.size() == contents_->RuleCount());
384 
385   if (index >= length()) {
386     if (length()) {
387       exception_state.ThrowDOMException(
388           DOMExceptionCode::kIndexSizeError,
389           "The index provided (" + String::Number(index) +
390               ") is larger than the maximum index (" +
391               String::Number(length() - 1) + ").");
392     } else {
393       exception_state.ThrowDOMException(DOMExceptionCode::kIndexSizeError,
394                                         "Style sheet is empty (length 0).");
395     }
396     return;
397   }
398   RuleMutationScope mutation_scope(this);
399 
400   bool success = contents_->WrapperDeleteRule(index);
401   if (!success) {
402     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
403                                       "Failed to delete rule");
404     return;
405   }
406 
407   if (!child_rule_cssom_wrappers_.IsEmpty()) {
408     if (child_rule_cssom_wrappers_[index])
409       child_rule_cssom_wrappers_[index]->SetParentStyleSheet(nullptr);
410     child_rule_cssom_wrappers_.EraseAt(index);
411   }
412 }
413 
addRule(const String & selector,const String & style,int index,ExceptionState & exception_state)414 int CSSStyleSheet::addRule(const String& selector,
415                            const String& style,
416                            int index,
417                            ExceptionState& exception_state) {
418   StringBuilder text;
419   text.Append(selector);
420   text.Append(" { ");
421   text.Append(style);
422   if (!style.IsEmpty())
423     text.Append(' ');
424   text.Append('}');
425   insertRule(text.ToString(), index, exception_state);
426 
427   // As per Microsoft documentation, always return -1.
428   return -1;
429 }
430 
addRule(const String & selector,const String & style,ExceptionState & exception_state)431 int CSSStyleSheet::addRule(const String& selector,
432                            const String& style,
433                            ExceptionState& exception_state) {
434   return addRule(selector, style, length(), exception_state);
435 }
436 
replace(ScriptState * script_state,const String & text)437 ScriptPromise CSSStyleSheet::replace(ScriptState* script_state,
438                                      const String& text) {
439   if (!IsConstructed()) {
440     return ScriptPromise::RejectWithDOMException(
441         script_state,
442         MakeGarbageCollected<DOMException>(
443             DOMExceptionCode::kNotAllowedError,
444             "Can't call replace on non-constructed CSSStyleSheets."));
445   }
446   SetText(text, CSSImportRules::kIgnoreWithWarning);
447   // We currently parse synchronously, and since @import support was removed,
448   // nothing else happens asynchronously. This API is left as-is, so that future
449   // async parsing can still be supported here.
450   return ScriptPromise::Cast(script_state, ToV8(this, script_state));
451 }
452 
replaceSync(const String & text,ExceptionState & exception_state)453 void CSSStyleSheet::replaceSync(const String& text,
454                                 ExceptionState& exception_state) {
455   if (!IsConstructed()) {
456     return exception_state.ThrowDOMException(
457         DOMExceptionCode::kNotAllowedError,
458         "Can't call replaceSync on non-constructed CSSStyleSheets.");
459   }
460   SetText(text, CSSImportRules::kIgnoreWithWarning);
461 }
462 
cssRules(ExceptionState & exception_state)463 CSSRuleList* CSSStyleSheet::cssRules(ExceptionState& exception_state) {
464   if (!CanAccessRules()) {
465     exception_state.ThrowSecurityError("Cannot access rules");
466     return nullptr;
467   }
468   if (!rule_list_cssom_wrapper_) {
469     rule_list_cssom_wrapper_ =
470         MakeGarbageCollected<StyleSheetCSSRuleList>(this);
471   }
472   return rule_list_cssom_wrapper_.Get();
473 }
474 
href() const475 String CSSStyleSheet::href() const {
476   return contents_->OriginalURL();
477 }
478 
BaseURL() const479 KURL CSSStyleSheet::BaseURL() const {
480   return contents_->BaseURL();
481 }
482 
IsLoading() const483 bool CSSStyleSheet::IsLoading() const {
484   return contents_->IsLoading();
485 }
486 
media()487 MediaList* CSSStyleSheet::media() {
488   if (!media_queries_)
489     media_queries_ = MediaQuerySet::Create();
490 
491   if (!media_cssom_wrapper_) {
492     media_cssom_wrapper_ = MakeGarbageCollected<MediaList>(
493         media_queries_.get(), const_cast<CSSStyleSheet*>(this));
494   }
495   return media_cssom_wrapper_.Get();
496 }
497 
SetMedia(MediaList * media_list)498 void CSSStyleSheet::SetMedia(MediaList* media_list) {
499   media_cssom_wrapper_ = media_list;
500 }
501 
parentStyleSheet() const502 CSSStyleSheet* CSSStyleSheet::parentStyleSheet() const {
503   return owner_rule_ ? owner_rule_->parentStyleSheet() : nullptr;
504 }
505 
OwnerDocument() const506 Document* CSSStyleSheet::OwnerDocument() const {
507   if (CSSStyleSheet* parent = parentStyleSheet())
508     return parent->OwnerDocument();
509   if (IsConstructed()) {
510     DCHECK(!ownerNode());
511     return ConstructorDocument();
512   }
513   return ownerNode() ? &ownerNode()->GetDocument() : nullptr;
514 }
515 
SheetLoaded()516 bool CSSStyleSheet::SheetLoaded() {
517   DCHECK(owner_node_);
518   SetLoadCompleted(owner_node_->SheetLoaded());
519   return load_completed_;
520 }
521 
StartLoadingDynamicSheet()522 void CSSStyleSheet::StartLoadingDynamicSheet() {
523   SetLoadCompleted(false);
524   owner_node_->StartLoadingDynamicSheet();
525 }
526 
SetLoadCompleted(bool completed)527 void CSSStyleSheet::SetLoadCompleted(bool completed) {
528   if (completed == load_completed_)
529     return;
530 
531   load_completed_ = completed;
532 
533   if (completed)
534     contents_->ClientLoadCompleted(this);
535   else
536     contents_->ClientLoadStarted(this);
537 }
538 
SetText(const String & text,CSSImportRules import_rules)539 void CSSStyleSheet::SetText(const String& text, CSSImportRules import_rules) {
540   child_rule_cssom_wrappers_.clear();
541 
542   CSSStyleSheet::RuleMutationScope mutation_scope(this);
543   contents_->ClearRules();
544   bool allow_imports = import_rules == CSSImportRules::kAllow;
545   if (contents_->ParseString(text, allow_imports) ==
546           ParseSheetResult::kHasUnallowedImportRule &&
547       import_rules == CSSImportRules::kIgnoreWithWarning) {
548     OwnerDocument()->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
549         mojom::blink::ConsoleMessageSource::kJavaScript,
550         mojom::blink::ConsoleMessageLevel::kWarning,
551         "@import rules are not allowed here. See "
552         "https://github.com/WICG/construct-stylesheets/issues/"
553         "119#issuecomment-588352418."));
554   }
555 }
556 
SetAlternateFromConstructor(bool alternate_from_constructor)557 void CSSStyleSheet::SetAlternateFromConstructor(
558     bool alternate_from_constructor) {
559   alternate_from_constructor_ = alternate_from_constructor;
560 }
561 
IsAlternate() const562 bool CSSStyleSheet::IsAlternate() const {
563   if (owner_node_) {
564     auto* owner_element = DynamicTo<Element>(owner_node_.Get());
565     return owner_element &&
566            owner_element->FastGetAttribute(html_names::kRelAttr)
567                .Contains("alternate");
568   }
569   return alternate_from_constructor_;
570 }
571 
CanBeActivated(const String & current_preferrable_name) const572 bool CSSStyleSheet::CanBeActivated(
573     const String& current_preferrable_name) const {
574   if (disabled())
575     return false;
576 
577   if (owner_node_ && owner_node_->IsInShadowTree()) {
578     if (IsA<HTMLStyleElement>(owner_node_.Get()) ||
579         IsA<SVGStyleElement>(owner_node_.Get()))
580       return true;
581     auto* html_link_element = DynamicTo<HTMLLinkElement>(owner_node_.Get());
582     if (html_link_element && html_link_element->IsImport())
583       return !IsAlternate();
584   }
585 
586   auto* html_link_element = DynamicTo<HTMLLinkElement>(owner_node_.Get());
587   if (!owner_node_ ||
588       owner_node_->getNodeType() == Node::kProcessingInstructionNode ||
589       !html_link_element || !html_link_element->IsEnabledViaScript()) {
590     if (!title_.IsEmpty() && title_ != current_preferrable_name)
591       return false;
592   }
593 
594   if (IsAlternate() && title_.IsEmpty())
595     return false;
596 
597   return true;
598 }
599 
Trace(Visitor * visitor) const600 void CSSStyleSheet::Trace(Visitor* visitor) const {
601   visitor->Trace(contents_);
602   visitor->Trace(owner_node_);
603   visitor->Trace(owner_rule_);
604   visitor->Trace(media_cssom_wrapper_);
605   visitor->Trace(child_rule_cssom_wrappers_);
606   visitor->Trace(rule_list_cssom_wrapper_);
607   visitor->Trace(adopted_tree_scopes_);
608   visitor->Trace(constructor_document_);
609   StyleSheet::Trace(visitor);
610 }
611 
612 }  // namespace blink
613