blob: e1ea95e285561acec9a6663839941d7a1fe09f05 [file] [log] [blame]
antti@apple.comd8eeff62015-09-23 15:04:16 +00001/*
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-2009, 2011-2012, 2015 Apple Inc. All rights reserved.
7 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8 * Copyright (C) 2008, 2009, 2011, 2012 Google Inc. All rights reserved.
9 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
10 * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public License
23 * along with this library; see the file COPYING.LIB. If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
26 */
27
28#include "config.h"
29#include "AuthorStyleSheets.h"
30
31#include "CSSStyleSheet.h"
32#include "Element.h"
33#include "ExtensionStyleSheets.h"
34#include "HTMLIFrameElement.h"
35#include "HTMLLinkElement.h"
36#include "HTMLStyleElement.h"
37#include "InspectorInstrumentation.h"
38#include "Page.h"
39#include "PageGroup.h"
40#include "ProcessingInstruction.h"
41#include "SVGNames.h"
42#include "SVGStyleElement.h"
43#include "Settings.h"
antti@apple.comd14e08d2015-09-25 22:06:09 +000044#include "ShadowRoot.h"
antti@apple.comd8eeff62015-09-23 15:04:16 +000045#include "StyleInvalidationAnalysis.h"
46#include "StyleResolver.h"
47#include "StyleSheetContents.h"
48#include "StyleSheetList.h"
49#include "UserContentController.h"
50#include "UserContentURLPattern.h"
51#include "UserStyleSheet.h"
52
53namespace WebCore {
54
55using namespace ContentExtensions;
56using namespace HTMLNames;
57
antti@apple.comd14e08d2015-09-25 22:06:09 +000058AuthorStyleSheets::AuthorStyleSheets(Document& document)
59 : m_document(document)
60{
61}
62
63AuthorStyleSheets::AuthorStyleSheets(ShadowRoot& shadowRoot)
64 : m_document(shadowRoot.documentScope())
65 , m_shadowRoot(&shadowRoot)
antti@apple.comd8eeff62015-09-23 15:04:16 +000066{
67}
68
69// This method is called whenever a top-level stylesheet has finished loading.
70void AuthorStyleSheets::removePendingSheet(RemovePendingSheetNotificationType notification)
71{
72 // Make sure we knew this sheet was pending, and that our count isn't out of sync.
73 ASSERT(m_pendingStyleSheetCount > 0);
74
75 m_pendingStyleSheetCount--;
76
77#ifdef INSTRUMENT_LAYOUT_SCHEDULING
78 if (!ownerElement())
79 printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets);
80#endif
81
82 if (m_pendingStyleSheetCount)
83 return;
84
85 if (notification == RemovePendingSheetNotifyLater) {
86 m_document.setNeedsNotifyRemoveAllPendingStylesheet();
87 return;
88 }
antti@apple.comd14e08d2015-09-25 22:06:09 +000089
90 if (m_shadowRoot) {
91 m_shadowRoot->updateStyle();
92 return;
93 }
94
antti@apple.comd8eeff62015-09-23 15:04:16 +000095 m_document.didRemoveAllPendingStylesheet();
96}
97
98void AuthorStyleSheets::addStyleSheetCandidateNode(Node& node, bool createdByParser)
99{
100 if (!node.inDocument())
101 return;
102
103 // Until the <body> exists, we have no choice but to compare document positions,
104 // since styles outside of the body and head continue to be shunted into the head
105 // (and thus can shift to end up before dynamically added DOM content that is also
106 // outside the body).
107 if ((createdByParser && m_document.bodyOrFrameset()) || m_styleSheetCandidateNodes.isEmpty()) {
108 m_styleSheetCandidateNodes.add(&node);
109 return;
110 }
111
112 // Determine an appropriate insertion point.
113 auto begin = m_styleSheetCandidateNodes.begin();
114 auto end = m_styleSheetCandidateNodes.end();
115 auto it = end;
116 Node* followingNode = nullptr;
117 do {
118 --it;
119 Node* n = *it;
cdumez@apple.com18a0d9d2016-07-22 16:01:48 +0000120 unsigned short position = n->compareDocumentPosition(node);
antti@apple.comd8eeff62015-09-23 15:04:16 +0000121 if (position == Node::DOCUMENT_POSITION_FOLLOWING) {
122 m_styleSheetCandidateNodes.insertBefore(followingNode, &node);
123 return;
124 }
125 followingNode = n;
126 } while (it != begin);
127
128 m_styleSheetCandidateNodes.insertBefore(followingNode, &node);
129}
130
131void AuthorStyleSheets::removeStyleSheetCandidateNode(Node& node)
132{
133 m_styleSheetCandidateNodes.remove(&node);
134}
135
136void AuthorStyleSheets::collectActiveStyleSheets(Vector<RefPtr<StyleSheet>>& sheets)
137{
138 if (m_document.settings() && !m_document.settings()->authorAndUserStylesEnabled())
139 return;
140
141 for (auto& node : m_styleSheetCandidateNodes) {
142 StyleSheet* sheet = nullptr;
143 if (is<ProcessingInstruction>(*node)) {
144 // Processing instruction (XML documents only).
145 // We don't support linking to embedded CSS stylesheets, see <https://bugs.webkit.org/show_bug.cgi?id=49281> for discussion.
146 ProcessingInstruction& pi = downcast<ProcessingInstruction>(*node);
147 sheet = pi.sheet();
148#if ENABLE(XSLT)
149 // Don't apply XSL transforms to already transformed documents -- <rdar://problem/4132806>
150 if (pi.isXSL() && !m_document.transformSourceDocument()) {
151 // Don't apply XSL transforms until loading is finished.
152 if (!m_document.parsing())
153 m_document.applyXSLTransform(&pi);
154 return;
155 }
156#endif
157 } else if (is<HTMLLinkElement>(*node) || is<HTMLStyleElement>(*node) || is<SVGStyleElement>(*node)) {
158 Element& element = downcast<Element>(*node);
rniwa@webkit.orge999a052016-07-16 15:21:55 +0000159 AtomicString title = element.attributeWithoutSynchronization(titleAttr);
antti@apple.comd8eeff62015-09-23 15:04:16 +0000160 bool enabledViaScript = false;
161 if (is<HTMLLinkElement>(element)) {
162 // <LINK> element
163 HTMLLinkElement& linkElement = downcast<HTMLLinkElement>(element);
164 if (linkElement.isDisabled())
165 continue;
166 enabledViaScript = linkElement.isEnabledViaScript();
167 if (linkElement.styleSheetIsLoading()) {
168 // it is loading but we should still decide which style sheet set to use
169 if (!enabledViaScript && !title.isEmpty() && m_preferredStylesheetSetName.isEmpty()) {
rniwa@webkit.orge999a052016-07-16 15:21:55 +0000170 if (!linkElement.attributeWithoutSynchronization(relAttr).contains("alternate")) {
antti@apple.comd8eeff62015-09-23 15:04:16 +0000171 m_preferredStylesheetSetName = title;
172 m_selectedStylesheetSetName = title;
173 }
174 }
175 continue;
176 }
177 if (!linkElement.sheet())
178 title = nullAtom;
179 }
180 // Get the current preferred styleset. This is the
181 // set of sheets that will be enabled.
182 if (is<SVGStyleElement>(element))
183 sheet = downcast<SVGStyleElement>(element).sheet();
184 else if (is<HTMLLinkElement>(element))
185 sheet = downcast<HTMLLinkElement>(element).sheet();
186 else
187 sheet = downcast<HTMLStyleElement>(element).sheet();
188 // Check to see if this sheet belongs to a styleset
189 // (thus making it PREFERRED or ALTERNATE rather than
190 // PERSISTENT).
rniwa@webkit.orge999a052016-07-16 15:21:55 +0000191 auto& rel = element.attributeWithoutSynchronization(relAttr);
antti@apple.comd8eeff62015-09-23 15:04:16 +0000192 if (!enabledViaScript && !title.isEmpty()) {
193 // Yes, we have a title.
194 if (m_preferredStylesheetSetName.isEmpty()) {
195 // No preferred set has been established. If
196 // we are NOT an alternate sheet, then establish
197 // us as the preferred set. Otherwise, just ignore
198 // this sheet.
199 if (is<HTMLStyleElement>(element) || !rel.contains("alternate"))
200 m_preferredStylesheetSetName = m_selectedStylesheetSetName = title;
201 }
202 if (title != m_preferredStylesheetSetName)
203 sheet = nullptr;
204 }
205
206 if (rel.contains("alternate") && title.isEmpty())
207 sheet = nullptr;
208 }
209 if (sheet)
210 sheets.append(sheet);
211 }
212}
213
antti@apple.com0ffd7132015-09-30 15:39:15 +0000214AuthorStyleSheets::StyleResolverUpdateType AuthorStyleSheets::analyzeStyleSheetChange(UpdateFlag updateFlag, const Vector<RefPtr<CSSStyleSheet>>& newStylesheets, bool& requiresFullStyleRecalc)
antti@apple.comd8eeff62015-09-23 15:04:16 +0000215{
antti@apple.comd8eeff62015-09-23 15:04:16 +0000216 requiresFullStyleRecalc = true;
217
218 // Stylesheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done.
219 bool hasActiveLoadingStylesheet = false;
220 unsigned newStylesheetCount = newStylesheets.size();
hs85.jeong@samsung.com22102292015-10-30 17:04:07 +0000221 for (auto& sheet : newStylesheets) {
222 if (sheet->isLoading())
antti@apple.comd8eeff62015-09-23 15:04:16 +0000223 hasActiveLoadingStylesheet = true;
224 }
225 if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) {
226 m_hadActiveLoadingStylesheet = false;
antti@apple.com0ffd7132015-09-30 15:39:15 +0000227 return Reconstruct;
antti@apple.comd8eeff62015-09-23 15:04:16 +0000228 }
229 m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet;
230
231 if (updateFlag != OptimizedUpdate)
antti@apple.com0ffd7132015-09-30 15:39:15 +0000232 return Reconstruct;
antti@apple.comd8eeff62015-09-23 15:04:16 +0000233 if (!m_document.styleResolverIfExists())
antti@apple.com0ffd7132015-09-30 15:39:15 +0000234 return Reconstruct;
235
antti@apple.comd8eeff62015-09-23 15:04:16 +0000236 StyleResolver& styleResolver = *m_document.styleResolverIfExists();
237
238 // Find out which stylesheets are new.
239 unsigned oldStylesheetCount = m_activeStyleSheets.size();
240 if (newStylesheetCount < oldStylesheetCount)
antti@apple.com0ffd7132015-09-30 15:39:15 +0000241 return Reconstruct;
242
antti@apple.comd8eeff62015-09-23 15:04:16 +0000243 Vector<StyleSheetContents*> addedSheets;
244 unsigned newIndex = 0;
245 for (unsigned oldIndex = 0; oldIndex < oldStylesheetCount; ++oldIndex) {
246 if (newIndex >= newStylesheetCount)
antti@apple.com0ffd7132015-09-30 15:39:15 +0000247 return Reconstruct;
antti@apple.comd8eeff62015-09-23 15:04:16 +0000248 while (m_activeStyleSheets[oldIndex] != newStylesheets[newIndex]) {
249 addedSheets.append(&newStylesheets[newIndex]->contents());
250 ++newIndex;
251 if (newIndex == newStylesheetCount)
antti@apple.com0ffd7132015-09-30 15:39:15 +0000252 return Reconstruct;
antti@apple.comd8eeff62015-09-23 15:04:16 +0000253 }
254 ++newIndex;
255 }
256 bool hasInsertions = !addedSheets.isEmpty();
257 while (newIndex < newStylesheetCount) {
258 addedSheets.append(&newStylesheets[newIndex]->contents());
259 ++newIndex;
260 }
261 // If all new sheets were added at the end of the list we can just add them to existing StyleResolver.
262 // If there were insertions we need to re-add all the stylesheets so rules are ordered correctly.
antti@apple.com0ffd7132015-09-30 15:39:15 +0000263 auto styleResolverUpdateType = hasInsertions ? Reset : Additive;
antti@apple.comd8eeff62015-09-23 15:04:16 +0000264
265 // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs.
266 if (!m_document.bodyOrFrameset() || m_document.hasNodesWithPlaceholderStyle())
antti@apple.com0ffd7132015-09-30 15:39:15 +0000267 return styleResolverUpdateType;
268
antti@apple.comd8eeff62015-09-23 15:04:16 +0000269 StyleInvalidationAnalysis invalidationAnalysis(addedSheets, styleResolver.mediaQueryEvaluator());
270 if (invalidationAnalysis.dirtiesAllStyle())
antti@apple.com0ffd7132015-09-30 15:39:15 +0000271 return styleResolverUpdateType;
antti@apple.comd8eeff62015-09-23 15:04:16 +0000272 invalidationAnalysis.invalidateStyle(m_document);
273 requiresFullStyleRecalc = false;
antti@apple.com0ffd7132015-09-30 15:39:15 +0000274
275 return styleResolverUpdateType;
antti@apple.comd8eeff62015-09-23 15:04:16 +0000276}
277
278static void filterEnabledNonemptyCSSStyleSheets(Vector<RefPtr<CSSStyleSheet>>& result, const Vector<RefPtr<StyleSheet>>& sheets)
279{
hs85.jeong@samsung.com22102292015-10-30 17:04:07 +0000280 for (auto& sheet : sheets) {
281 if (!is<CSSStyleSheet>(*sheet))
antti@apple.comd8eeff62015-09-23 15:04:16 +0000282 continue;
hs85.jeong@samsung.com22102292015-10-30 17:04:07 +0000283 CSSStyleSheet& styleSheet = downcast<CSSStyleSheet>(*sheet);
284 if (styleSheet.disabled())
antti@apple.comd8eeff62015-09-23 15:04:16 +0000285 continue;
hs85.jeong@samsung.com22102292015-10-30 17:04:07 +0000286 if (!styleSheet.length())
antti@apple.comd8eeff62015-09-23 15:04:16 +0000287 continue;
hs85.jeong@samsung.com22102292015-10-30 17:04:07 +0000288 result.append(&styleSheet);
antti@apple.comd8eeff62015-09-23 15:04:16 +0000289 }
290}
291
292bool AuthorStyleSheets::updateActiveStyleSheets(UpdateFlag updateFlag)
293{
antti@apple.com27d2b34b2016-07-13 19:17:46 +0000294 if (m_document.inStyleRecalc() || m_document.inRenderTreeUpdate()) {
295 // Protect against deleting style resolver in the middle of a style resolution.
296 // Crash stacks indicate we can get here when z resource load fails synchronously (for example due to content blocking).
297 // FIXME: These kind of cases should be eliminated and this path replaced by an assert.
antti@apple.comd8eeff62015-09-23 15:04:16 +0000298 m_pendingUpdateType = FullUpdate;
299 m_document.scheduleForcedStyleRecalc();
300 return false;
301
302 }
303 if (!m_document.hasLivingRenderTree())
304 return false;
305
306 Vector<RefPtr<StyleSheet>> activeStyleSheets;
307 collectActiveStyleSheets(activeStyleSheets);
308
309 Vector<RefPtr<CSSStyleSheet>> activeCSSStyleSheets;
310 activeCSSStyleSheets.appendVector(m_document.extensionStyleSheets().injectedAuthorStyleSheets());
311 activeCSSStyleSheets.appendVector(m_document.extensionStyleSheets().authorStyleSheetsForTesting());
312 filterEnabledNonemptyCSSStyleSheets(activeCSSStyleSheets, activeStyleSheets);
313
antti@apple.comd8eeff62015-09-23 15:04:16 +0000314 bool requiresFullStyleRecalc;
antti@apple.com0ffd7132015-09-30 15:39:15 +0000315 auto styleResolverUpdateType = analyzeStyleSheetChange(updateFlag, activeCSSStyleSheets, requiresFullStyleRecalc);
antti@apple.comd8eeff62015-09-23 15:04:16 +0000316
antti@apple.com0ffd7132015-09-30 15:39:15 +0000317 updateStyleResolver(activeCSSStyleSheets, styleResolverUpdateType);
antti@apple.comd8eeff62015-09-23 15:04:16 +0000318
319 m_weakCopyOfActiveStyleSheetListForFastLookup = nullptr;
320 m_activeStyleSheets.swap(activeCSSStyleSheets);
321 m_styleSheetsForStyleSheetList.swap(activeStyleSheets);
322
323 InspectorInstrumentation::activeStyleSheetsUpdated(m_document);
324
325 for (const auto& sheet : m_activeStyleSheets) {
326 if (sheet->contents().usesRemUnits())
327 m_usesRemUnits = true;
328 if (sheet->contents().usesStyleBasedEditability())
329 m_usesStyleBasedEditability = true;
330 }
331 m_pendingUpdateType = NoUpdate;
332
333 return requiresFullStyleRecalc;
334}
335
antti@apple.com0ffd7132015-09-30 15:39:15 +0000336void AuthorStyleSheets::updateStyleResolver(Vector<RefPtr<CSSStyleSheet>>& activeStyleSheets, StyleResolverUpdateType updateType)
337{
338 if (updateType == Reconstruct) {
339 if (m_shadowRoot)
340 m_shadowRoot->resetStyleResolver();
341 else
342 m_document.clearStyleResolver();
343 return;
344 }
345 auto& styleResolver = m_document.ensureStyleResolver();
346 auto& userAgentShadowTreeStyleResolver = m_document.userAgentShadowTreeStyleResolver();
347
348 if (updateType == Reset) {
349 styleResolver.ruleSets().resetAuthorStyle();
350 styleResolver.appendAuthorStyleSheets(activeStyleSheets);
351 } else {
352 ASSERT(updateType == Additive);
353 unsigned firstNewIndex = m_activeStyleSheets.size();
354 Vector<RefPtr<CSSStyleSheet>> newStyleSheets;
355 newStyleSheets.appendRange(activeStyleSheets.begin() + firstNewIndex, activeStyleSheets.end());
356 styleResolver.appendAuthorStyleSheets(newStyleSheets);
357 }
358
359 userAgentShadowTreeStyleResolver.ruleSets().resetAuthorStyle();
360 auto& authorRuleSet = *styleResolver.ruleSets().authorStyle();
361 if (authorRuleSet.hasShadowPseudoElementRules())
362 userAgentShadowTreeStyleResolver.ruleSets().authorStyle()->copyShadowPseudoElementRulesFrom(authorRuleSet);
363}
364
antti@apple.comd8eeff62015-09-23 15:04:16 +0000365const Vector<RefPtr<CSSStyleSheet>> AuthorStyleSheets::activeStyleSheetsForInspector() const
366{
367 Vector<RefPtr<CSSStyleSheet>> result;
368
369 result.appendVector(m_document.extensionStyleSheets().injectedAuthorStyleSheets());
370 result.appendVector(m_document.extensionStyleSheets().authorStyleSheetsForTesting());
371
372 for (auto& styleSheet : m_styleSheetsForStyleSheetList) {
373 if (!is<CSSStyleSheet>(*styleSheet))
374 continue;
375
376 CSSStyleSheet& sheet = downcast<CSSStyleSheet>(*styleSheet);
377 if (sheet.disabled())
378 continue;
379
380 result.append(&sheet);
381 }
382
383 return result;
384}
385
386bool AuthorStyleSheets::activeStyleSheetsContains(const CSSStyleSheet* sheet) const
387{
388 if (!m_weakCopyOfActiveStyleSheetListForFastLookup) {
389 m_weakCopyOfActiveStyleSheetListForFastLookup = std::make_unique<HashSet<const CSSStyleSheet*>>();
hs85.jeong@samsung.com22102292015-10-30 17:04:07 +0000390 for (auto& activeStyleSheet : m_activeStyleSheets)
391 m_weakCopyOfActiveStyleSheetListForFastLookup->add(activeStyleSheet.get());
antti@apple.comd8eeff62015-09-23 15:04:16 +0000392 }
393 return m_weakCopyOfActiveStyleSheetListForFastLookup->contains(sheet);
394}
395
396}