1 /*
2  * Copyright (C) 2006, 2007 Rob Buis
3  * Copyright (C) 2008 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/style_element.h"
22 
23 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
24 #include "third_party/blink/renderer/core/css/media_list.h"
25 #include "third_party/blink/renderer/core/css/media_query_evaluator.h"
26 #include "third_party/blink/renderer/core/css/style_engine.h"
27 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
28 #include "third_party/blink/renderer/core/dom/document.h"
29 #include "third_party/blink/renderer/core/dom/element.h"
30 #include "third_party/blink/renderer/core/dom/scriptable_document_parser.h"
31 #include "third_party/blink/renderer/core/dom/shadow_root.h"
32 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
33 #include "third_party/blink/renderer/core/frame/local_frame.h"
34 #include "third_party/blink/renderer/core/html/html_style_element.h"
35 #include "third_party/blink/renderer/core/probe/core_probes.h"
36 #include "third_party/blink/renderer/core/svg/svg_style_element.h"
37 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
38 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
39 
40 namespace blink {
41 
IsCSS(const Element & element,const AtomicString & type)42 static bool IsCSS(const Element& element, const AtomicString& type) {
43   return type.IsEmpty() ||
44          (element.IsHTMLElement() ? EqualIgnoringASCIICase(type, "text/css")
45                                   : (type == "text/css"));
46 }
47 
StyleElement(Document * document,bool created_by_parser)48 StyleElement::StyleElement(Document* document, bool created_by_parser)
49     : created_by_parser_(created_by_parser),
50       loading_(false),
51       registered_as_candidate_(false),
52       start_position_(TextPosition::BelowRangePosition()) {
53   if (created_by_parser && document &&
54       document->GetScriptableDocumentParser() &&
55       !document->IsInDocumentWrite()) {
56     start_position_ =
57         document->GetScriptableDocumentParser()->GetTextPosition();
58   }
59 }
60 
61 StyleElement::~StyleElement() = default;
62 
ProcessStyleSheet(Document & document,Element & element)63 StyleElement::ProcessingResult StyleElement::ProcessStyleSheet(
64     Document& document,
65     Element& element) {
66   TRACE_EVENT0("blink", "StyleElement::processStyleSheet");
67   DCHECK(element.isConnected());
68 
69   registered_as_candidate_ = true;
70   document.GetStyleEngine().AddStyleSheetCandidateNode(element);
71   if (created_by_parser_)
72     return kProcessingSuccessful;
73 
74   return Process(element);
75 }
76 
RemovedFrom(Element & element,ContainerNode & insertion_point)77 void StyleElement::RemovedFrom(Element& element,
78                                ContainerNode& insertion_point) {
79   if (!insertion_point.isConnected())
80     return;
81 
82   Document& document = element.GetDocument();
83   if (registered_as_candidate_) {
84     document.GetStyleEngine().RemoveStyleSheetCandidateNode(element,
85                                                             insertion_point);
86     registered_as_candidate_ = false;
87   }
88 
89   if (sheet_)
90     ClearSheet(element);
91 }
92 
ChildrenChanged(Element & element)93 StyleElement::ProcessingResult StyleElement::ChildrenChanged(Element& element) {
94   if (created_by_parser_)
95     return kProcessingSuccessful;
96   probe::WillChangeStyleElement(&element);
97   return Process(element);
98 }
99 
FinishParsingChildren(Element & element)100 StyleElement::ProcessingResult StyleElement::FinishParsingChildren(
101     Element& element) {
102   ProcessingResult result = Process(element);
103   created_by_parser_ = false;
104   return result;
105 }
106 
Process(Element & element)107 StyleElement::ProcessingResult StyleElement::Process(Element& element) {
108   if (!element.isConnected())
109     return kProcessingSuccessful;
110   return CreateSheet(element, element.TextFromChildren());
111 }
112 
ClearSheet(Element & owner_element)113 void StyleElement::ClearSheet(Element& owner_element) {
114   DCHECK(sheet_);
115 
116   if (sheet_->IsLoading()) {
117     owner_element.GetDocument().GetStyleEngine().RemovePendingSheet(
118         owner_element, style_engine_context_);
119   }
120 
121   sheet_.Release()->ClearOwnerNode();
122 }
123 
IsInUserAgentShadowDOM(const Element & element)124 static bool IsInUserAgentShadowDOM(const Element& element) {
125   ShadowRoot* root = element.ContainingShadowRoot();
126   return root && root->IsUserAgent();
127 }
128 
CreateSheet(Element & element,const String & text)129 StyleElement::ProcessingResult StyleElement::CreateSheet(Element& element,
130                                                          const String& text) {
131   DCHECK(element.isConnected());
132   Document& document = element.GetDocument();
133 
134   const ContentSecurityPolicy* csp =
135       document.GetContentSecurityPolicyForWorld();
136 
137   // CSP is bypassed for style elements in user agent shadow DOM.
138   bool passes_content_security_policy_checks =
139       IsInUserAgentShadowDOM(element) ||
140       csp->AllowInline(ContentSecurityPolicy::InlineType::kStyle, &element,
141                        text, element.nonce(), document.Url(),
142                        start_position_.line_);
143 
144   // Clearing the current sheet may remove the cache entry so create the new
145   // sheet first
146   CSSStyleSheet* new_sheet = nullptr;
147 
148   // If type is empty or CSS, this is a CSS style sheet.
149   const AtomicString& type = this->type();
150   if (IsCSS(element, type) && passes_content_security_policy_checks) {
151     scoped_refptr<MediaQuerySet> media_queries;
152     const AtomicString& media_string = media();
153     if (!media_string.IsEmpty()) {
154       media_queries =
155           MediaQuerySet::Create(media_string, element.GetExecutionContext());
156     }
157     loading_ = true;
158     TextPosition start_position =
159         start_position_ == TextPosition::BelowRangePosition()
160             ? TextPosition::MinimumPosition()
161             : start_position_;
162     new_sheet = document.GetStyleEngine().CreateSheet(
163         element, text, start_position, style_engine_context_);
164     new_sheet->SetMediaQueries(media_queries);
165     loading_ = false;
166   }
167 
168   if (sheet_)
169     ClearSheet(element);
170 
171   sheet_ = new_sheet;
172   if (sheet_)
173     sheet_->Contents()->CheckLoaded();
174 
175   return passes_content_security_policy_checks ? kProcessingSuccessful
176                                                : kProcessingFatalError;
177 }
178 
IsLoading() const179 bool StyleElement::IsLoading() const {
180   if (loading_)
181     return true;
182   return sheet_ ? sheet_->IsLoading() : false;
183 }
184 
SheetLoaded(Document & document)185 bool StyleElement::SheetLoaded(Document& document) {
186   if (IsLoading())
187     return false;
188 
189   document.GetStyleEngine().RemovePendingSheet(*sheet_->ownerNode(),
190                                                style_engine_context_);
191   return true;
192 }
193 
StartLoadingDynamicSheet(Document & document)194 void StyleElement::StartLoadingDynamicSheet(Document& document) {
195   document.GetStyleEngine().AddPendingSheet(style_engine_context_);
196 }
197 
Trace(Visitor * visitor)198 void StyleElement::Trace(Visitor* visitor) {
199   visitor->Trace(sheet_);
200 }
201 
202 }  // namespace blink
203