Replace PassRef with Ref/Ref&& across the board.
[WebKit-https.git] / Source / WebCore / dom / DocumentStyleSheetCollection.cpp
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 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 "DocumentStyleSheetCollection.h"
30
31 #include "CSSStyleSheet.h"
32 #include "Element.h"
33 #include "HTMLIFrameElement.h"
34 #include "HTMLLinkElement.h"
35 #include "HTMLStyleElement.h"
36 #include "Page.h"
37 #include "PageGroup.h"
38 #include "ProcessingInstruction.h"
39 #include "SVGNames.h"
40 #include "SVGStyleElement.h"
41 #include "Settings.h"
42 #include "StyleInvalidationAnalysis.h"
43 #include "StyleResolver.h"
44 #include "StyleSheetContents.h"
45 #include "StyleSheetList.h"
46 #include "UserContentController.h"
47 #include "UserContentURLPattern.h"
48
49 namespace WebCore {
50
51 using namespace HTMLNames;
52
53 DocumentStyleSheetCollection::DocumentStyleSheetCollection(Document& document)
54     : m_document(document)
55     , m_pendingStylesheets(0)
56     , m_injectedStyleSheetCacheValid(false)
57     , m_hadActiveLoadingStylesheet(false)
58     , m_pendingUpdateType(NoUpdate)
59     , m_usesFirstLineRules(false)
60     , m_usesFirstLetterRules(false)
61     , m_usesRemUnits(false)
62 {
63 }
64
65 void DocumentStyleSheetCollection::combineCSSFeatureFlags()
66 {
67     // Delay resetting the flags until after next style recalc since unapplying the style may not work without these set (this is true at least with before/after).
68     const StyleResolver& styleResolver = m_document.ensureStyleResolver();
69     m_usesFirstLineRules = m_usesFirstLineRules || styleResolver.usesFirstLineRules();
70     m_usesFirstLetterRules = m_usesFirstLetterRules || styleResolver.usesFirstLetterRules();
71 }
72
73 void DocumentStyleSheetCollection::resetCSSFeatureFlags()
74 {
75     const StyleResolver& styleResolver = m_document.ensureStyleResolver();
76     m_usesFirstLineRules = styleResolver.usesFirstLineRules();
77     m_usesFirstLetterRules = styleResolver.usesFirstLetterRules();
78 }
79
80 CSSStyleSheet* DocumentStyleSheetCollection::pageUserSheet()
81 {
82     if (m_pageUserSheet)
83         return m_pageUserSheet.get();
84     
85     Page* owningPage = m_document.page();
86     if (!owningPage)
87         return 0;
88     
89     String userSheetText = owningPage->userStyleSheet();
90     if (userSheetText.isEmpty())
91         return 0;
92     
93     // Parse the sheet and cache it.
94     m_pageUserSheet = CSSStyleSheet::createInline(m_document, m_document.settings()->userStyleSheetLocation());
95     m_pageUserSheet->contents().setIsUserStyleSheet(true);
96     m_pageUserSheet->contents().parseString(userSheetText);
97     return m_pageUserSheet.get();
98 }
99
100 void DocumentStyleSheetCollection::clearPageUserSheet()
101 {
102     if (m_pageUserSheet) {
103         m_pageUserSheet = 0;
104         m_document.styleResolverChanged(DeferRecalcStyle);
105     }
106 }
107
108 void DocumentStyleSheetCollection::updatePageUserSheet()
109 {
110     clearPageUserSheet();
111     if (pageUserSheet())
112         m_document.styleResolverChanged(RecalcStyleImmediately);
113 }
114
115 const Vector<RefPtr<CSSStyleSheet>>& DocumentStyleSheetCollection::injectedUserStyleSheets() const
116 {
117     updateInjectedStyleSheetCache();
118     return m_injectedUserStyleSheets;
119 }
120
121 const Vector<RefPtr<CSSStyleSheet>>& DocumentStyleSheetCollection::injectedAuthorStyleSheets() const
122 {
123     updateInjectedStyleSheetCache();
124     return m_injectedAuthorStyleSheets;
125 }
126
127 void DocumentStyleSheetCollection::updateInjectedStyleSheetCache() const
128 {
129     if (m_injectedStyleSheetCacheValid)
130         return;
131     m_injectedStyleSheetCacheValid = true;
132     m_injectedUserStyleSheets.clear();
133     m_injectedAuthorStyleSheets.clear();
134
135     Page* owningPage = m_document.page();
136     if (!owningPage)
137         return;
138
139     const auto* userContentController = owningPage->userContentController();
140     if (!userContentController)
141         return;
142
143     const UserStyleSheetMap* userStyleSheets = userContentController->userStyleSheets();
144     if (!userStyleSheets)
145         return;
146
147     for (auto& styleSheets : userStyleSheets->values()) {
148         for (const auto& sheet : *styleSheets) {
149             if (sheet->injectedFrames() == InjectInTopFrameOnly && m_document.ownerElement())
150                 continue;
151
152             if (!UserContentURLPattern::matchesPatterns(m_document.url(), sheet->whitelist(), sheet->blacklist()))
153                 continue;
154
155             RefPtr<CSSStyleSheet> groupSheet = CSSStyleSheet::createInline(const_cast<Document&>(m_document), sheet->url());
156             bool isUserStyleSheet = sheet->level() == UserStyleUserLevel;
157             if (isUserStyleSheet)
158                 m_injectedUserStyleSheets.append(groupSheet);
159             else
160                 m_injectedAuthorStyleSheets.append(groupSheet);
161
162             groupSheet->contents().setIsUserStyleSheet(isUserStyleSheet);
163             groupSheet->contents().parseString(sheet->source());
164         }
165     }
166 }
167
168 void DocumentStyleSheetCollection::invalidateInjectedStyleSheetCache()
169 {
170     if (!m_injectedStyleSheetCacheValid)
171         return;
172     m_injectedStyleSheetCacheValid = false;
173     if (m_injectedUserStyleSheets.isEmpty() && m_injectedAuthorStyleSheets.isEmpty())
174         return;
175     m_document.styleResolverChanged(DeferRecalcStyle);
176 }
177
178 void DocumentStyleSheetCollection::addAuthorSheet(Ref<StyleSheetContents>&& authorSheet)
179 {
180     ASSERT(!authorSheet.get().isUserStyleSheet());
181     m_authorStyleSheets.append(CSSStyleSheet::create(WTF::move(authorSheet), &m_document));
182     m_document.styleResolverChanged(RecalcStyleImmediately);
183 }
184
185 void DocumentStyleSheetCollection::addUserSheet(Ref<StyleSheetContents>&& userSheet)
186 {
187     ASSERT(userSheet.get().isUserStyleSheet());
188     m_userStyleSheets.append(CSSStyleSheet::create(WTF::move(userSheet), &m_document));
189     m_document.styleResolverChanged(RecalcStyleImmediately);
190 }
191
192 // This method is called whenever a top-level stylesheet has finished loading.
193 void DocumentStyleSheetCollection::removePendingSheet(RemovePendingSheetNotificationType notification)
194 {
195     // Make sure we knew this sheet was pending, and that our count isn't out of sync.
196     ASSERT(m_pendingStylesheets > 0);
197
198     m_pendingStylesheets--;
199     
200 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
201     if (!ownerElement())
202         printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets);
203 #endif
204
205     if (m_pendingStylesheets)
206         return;
207
208     if (notification == RemovePendingSheetNotifyLater) {
209         m_document.setNeedsNotifyRemoveAllPendingStylesheet();
210         return;
211     }
212     
213     m_document.didRemoveAllPendingStylesheet();
214 }
215
216 void DocumentStyleSheetCollection::addStyleSheetCandidateNode(Node& node, bool createdByParser)
217 {
218     if (!node.inDocument())
219         return;
220     
221     // Until the <body> exists, we have no choice but to compare document positions,
222     // since styles outside of the body and head continue to be shunted into the head
223     // (and thus can shift to end up before dynamically added DOM content that is also
224     // outside the body).
225     if ((createdByParser && m_document.body()) || m_styleSheetCandidateNodes.isEmpty()) {
226         m_styleSheetCandidateNodes.add(&node);
227         return;
228     }
229
230     // Determine an appropriate insertion point.
231     StyleSheetCandidateListHashSet::iterator begin = m_styleSheetCandidateNodes.begin();
232     StyleSheetCandidateListHashSet::iterator end = m_styleSheetCandidateNodes.end();
233     StyleSheetCandidateListHashSet::iterator it = end;
234     Node* followingNode = 0;
235     do {
236         --it;
237         Node* n = *it;
238         unsigned short position = n->compareDocumentPosition(&node);
239         if (position == Node::DOCUMENT_POSITION_FOLLOWING) {
240             m_styleSheetCandidateNodes.insertBefore(followingNode, &node);
241             return;
242         }
243         followingNode = n;
244     } while (it != begin);
245     
246     m_styleSheetCandidateNodes.insertBefore(followingNode, &node);
247 }
248
249 void DocumentStyleSheetCollection::removeStyleSheetCandidateNode(Node& node)
250 {
251     m_styleSheetCandidateNodes.remove(&node);
252 }
253
254 void DocumentStyleSheetCollection::collectActiveStyleSheets(Vector<RefPtr<StyleSheet>>& sheets)
255 {
256     if (m_document.settings() && !m_document.settings()->authorAndUserStylesEnabled())
257         return;
258
259     for (auto& node : m_styleSheetCandidateNodes) {
260         StyleSheet* sheet = nullptr;
261         if (is<ProcessingInstruction>(*node)) {
262             // Processing instruction (XML documents only).
263             // We don't support linking to embedded CSS stylesheets, see <https://bugs.webkit.org/show_bug.cgi?id=49281> for discussion.
264             ProcessingInstruction& pi = downcast<ProcessingInstruction>(*node);
265             sheet = pi.sheet();
266 #if ENABLE(XSLT)
267             // Don't apply XSL transforms to already transformed documents -- <rdar://problem/4132806>
268             if (pi.isXSL() && !m_document.transformSourceDocument()) {
269                 // Don't apply XSL transforms until loading is finished.
270                 if (!m_document.parsing())
271                     m_document.applyXSLTransform(&pi);
272                 return;
273             }
274 #endif
275         } else if (is<HTMLLinkElement>(*node) || is<HTMLStyleElement>(*node) || is<SVGStyleElement>(*node)) {
276             Element& element = downcast<Element>(*node);
277             AtomicString title = element.fastGetAttribute(titleAttr);
278             bool enabledViaScript = false;
279             if (is<HTMLLinkElement>(element)) {
280                 // <LINK> element
281                 HTMLLinkElement& linkElement = downcast<HTMLLinkElement>(element);
282                 if (linkElement.isDisabled())
283                     continue;
284                 enabledViaScript = linkElement.isEnabledViaScript();
285                 if (linkElement.styleSheetIsLoading()) {
286                     // it is loading but we should still decide which style sheet set to use
287                     if (!enabledViaScript && !title.isEmpty() && m_preferredStylesheetSetName.isEmpty()) {
288                         if (!linkElement.fastGetAttribute(relAttr).contains("alternate")) {
289                             m_preferredStylesheetSetName = title;
290                             m_selectedStylesheetSetName = title;
291                         }
292                     }
293                     continue;
294                 }
295                 if (!linkElement.sheet())
296                     title = nullAtom;
297             }
298             // Get the current preferred styleset. This is the
299             // set of sheets that will be enabled.
300             if (is<SVGStyleElement>(element))
301                 sheet = downcast<SVGStyleElement>(element).sheet();
302             else if (is<HTMLLinkElement>(element))
303                 sheet = downcast<HTMLLinkElement>(element).sheet();
304             else
305                 sheet = downcast<HTMLStyleElement>(element).sheet();
306             // Check to see if this sheet belongs to a styleset
307             // (thus making it PREFERRED or ALTERNATE rather than
308             // PERSISTENT).
309             auto& rel = element.fastGetAttribute(relAttr);
310             if (!enabledViaScript && !title.isEmpty()) {
311                 // Yes, we have a title.
312                 if (m_preferredStylesheetSetName.isEmpty()) {
313                     // No preferred set has been established. If
314                     // we are NOT an alternate sheet, then establish
315                     // us as the preferred set. Otherwise, just ignore
316                     // this sheet.
317                     if (is<HTMLStyleElement>(element) || !rel.contains("alternate"))
318                         m_preferredStylesheetSetName = m_selectedStylesheetSetName = title;
319                 }
320                 if (title != m_preferredStylesheetSetName)
321                     sheet = nullptr;
322             }
323
324             if (rel.contains("alternate") && title.isEmpty())
325                 sheet = nullptr;
326         }
327         if (sheet)
328             sheets.append(sheet);
329     }
330 }
331
332 void DocumentStyleSheetCollection::analyzeStyleSheetChange(UpdateFlag updateFlag, const Vector<RefPtr<CSSStyleSheet>>& newStylesheets, StyleResolverUpdateType& styleResolverUpdateType, bool& requiresFullStyleRecalc)
333 {
334     styleResolverUpdateType = Reconstruct;
335     requiresFullStyleRecalc = true;
336     
337     // Stylesheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done.
338     bool hasActiveLoadingStylesheet = false;
339     unsigned newStylesheetCount = newStylesheets.size();
340     for (unsigned i = 0; i < newStylesheetCount; ++i) {
341         if (newStylesheets[i]->isLoading())
342             hasActiveLoadingStylesheet = true;
343     }
344     if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) {
345         m_hadActiveLoadingStylesheet = false;
346         return;
347     }
348     m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet;
349
350     if (updateFlag != OptimizedUpdate)
351         return;
352     if (!m_document.styleResolverIfExists())
353         return;
354     StyleResolver& styleResolver = *m_document.styleResolverIfExists();
355
356     // Find out which stylesheets are new.
357     unsigned oldStylesheetCount = m_activeAuthorStyleSheets.size();
358     if (newStylesheetCount < oldStylesheetCount)
359         return;
360     Vector<StyleSheetContents*> addedSheets;
361     unsigned newIndex = 0;
362     for (unsigned oldIndex = 0; oldIndex < oldStylesheetCount; ++oldIndex) {
363         if (newIndex >= newStylesheetCount)
364             return;
365         while (m_activeAuthorStyleSheets[oldIndex] != newStylesheets[newIndex]) {
366             addedSheets.append(&newStylesheets[newIndex]->contents());
367             ++newIndex;
368             if (newIndex == newStylesheetCount)
369                 return;
370         }
371         ++newIndex;
372     }
373     bool hasInsertions = !addedSheets.isEmpty();
374     while (newIndex < newStylesheetCount) {
375         addedSheets.append(&newStylesheets[newIndex]->contents());
376         ++newIndex;
377     }
378     // If all new sheets were added at the end of the list we can just add them to existing StyleResolver.
379     // If there were insertions we need to re-add all the stylesheets so rules are ordered correctly.
380     styleResolverUpdateType = hasInsertions ? Reset : Additive;
381
382     // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs.
383     if (!m_document.body() || m_document.hasNodesWithPlaceholderStyle())
384         return;
385     StyleInvalidationAnalysis invalidationAnalysis(addedSheets, styleResolver.mediaQueryEvaluator());
386     if (invalidationAnalysis.dirtiesAllStyle())
387         return;
388     invalidationAnalysis.invalidateStyle(m_document);
389     requiresFullStyleRecalc = false;
390 }
391
392 static bool styleSheetsUseRemUnits(const Vector<RefPtr<CSSStyleSheet>>& sheets)
393 {
394     for (unsigned i = 0; i < sheets.size(); ++i) {
395         if (sheets[i]->contents().usesRemUnits())
396             return true;
397     }
398     return false;
399 }
400
401 static void filterEnabledNonemptyCSSStyleSheets(Vector<RefPtr<CSSStyleSheet>>& result, const Vector<RefPtr<StyleSheet>>& sheets)
402 {
403     for (unsigned i = 0; i < sheets.size(); ++i) {
404         if (!is<CSSStyleSheet>(*sheets[i]))
405             continue;
406         CSSStyleSheet& sheet = downcast<CSSStyleSheet>(*sheets[i]);
407         if (sheet.disabled())
408             continue;
409         if (!sheet.length())
410             continue;
411         result.append(&sheet);
412     }
413 }
414
415 bool DocumentStyleSheetCollection::updateActiveStyleSheets(UpdateFlag updateFlag)
416 {
417     if (m_document.inStyleRecalc()) {
418         // SVG <use> element may manage to invalidate style selector in the middle of a style recalc.
419         // https://bugs.webkit.org/show_bug.cgi?id=54344
420         // FIXME: This should be fixed in SVG and the call site replaced by ASSERT(!m_inStyleRecalc).
421         m_pendingUpdateType = FullUpdate;
422         m_document.scheduleForcedStyleRecalc();
423         return false;
424
425     }
426     if (!m_document.hasLivingRenderTree())
427         return false;
428
429     Vector<RefPtr<StyleSheet>> activeStyleSheets;
430     collectActiveStyleSheets(activeStyleSheets);
431
432     Vector<RefPtr<CSSStyleSheet>> activeCSSStyleSheets;
433     activeCSSStyleSheets.appendVector(injectedAuthorStyleSheets());
434     activeCSSStyleSheets.appendVector(documentAuthorStyleSheets());
435     filterEnabledNonemptyCSSStyleSheets(activeCSSStyleSheets, activeStyleSheets);
436
437     StyleResolverUpdateType styleResolverUpdateType;
438     bool requiresFullStyleRecalc;
439     analyzeStyleSheetChange(updateFlag, activeCSSStyleSheets, styleResolverUpdateType, requiresFullStyleRecalc);
440
441     if (styleResolverUpdateType == Reconstruct)
442         m_document.clearStyleResolver();
443     else {
444         StyleResolver& styleResolver = m_document.ensureStyleResolver();
445         if (styleResolverUpdateType == Reset) {
446             styleResolver.ruleSets().resetAuthorStyle();
447             styleResolver.appendAuthorStyleSheets(0, activeCSSStyleSheets);
448         } else {
449             ASSERT(styleResolverUpdateType == Additive);
450             styleResolver.appendAuthorStyleSheets(m_activeAuthorStyleSheets.size(), activeCSSStyleSheets);
451         }
452         resetCSSFeatureFlags();
453     }
454
455     m_weakCopyOfActiveStyleSheetListForFastLookup = nullptr;
456     m_activeAuthorStyleSheets.swap(activeCSSStyleSheets);
457     m_styleSheetsForStyleSheetList.swap(activeStyleSheets);
458
459     m_usesRemUnits = styleSheetsUseRemUnits(m_activeAuthorStyleSheets);
460     m_pendingUpdateType = NoUpdate;
461
462     return requiresFullStyleRecalc;
463 }
464
465 bool DocumentStyleSheetCollection::activeStyleSheetsContains(const CSSStyleSheet* sheet) const
466 {
467     if (!m_weakCopyOfActiveStyleSheetListForFastLookup) {
468         m_weakCopyOfActiveStyleSheetListForFastLookup = std::make_unique<HashSet<const CSSStyleSheet*>>();
469         for (unsigned i = 0; i < m_activeAuthorStyleSheets.size(); ++i)
470             m_weakCopyOfActiveStyleSheetListForFastLookup->add(m_activeAuthorStyleSheets[i].get());
471     }
472     return m_weakCopyOfActiveStyleSheetListForFastLookup->contains(sheet);
473 }
474
475 void DocumentStyleSheetCollection::detachFromDocument()
476 {
477     if (m_pageUserSheet)
478         m_pageUserSheet->detachFromDocument();
479     for (unsigned i = 0; i < m_injectedUserStyleSheets.size(); ++i)
480         m_injectedUserStyleSheets[i]->detachFromDocument();
481     for (unsigned i = 0; i < m_injectedAuthorStyleSheets.size(); ++i)
482         m_injectedAuthorStyleSheets[i]->detachFromDocument();
483     for (unsigned i = 0; i < m_userStyleSheets.size(); ++i)
484         m_userStyleSheets[i]->detachFromDocument();
485     for (unsigned i = 0; i < m_authorStyleSheets.size(); ++i)
486         m_authorStyleSheets[i]->detachFromDocument();
487 }
488
489 }