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.
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.
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.
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.
29 #include "DocumentStyleSheetCollection.h"
31 #include "CSSStyleSheet.h"
34 #include "HTMLLinkElement.h"
35 #include "HTMLNames.h"
36 #include "HTMLStyleElement.h"
38 #include "PageGroup.h"
39 #include "ProcessingInstruction.h"
41 #include "SVGStyleElement.h"
42 #include "SelectorChecker.h"
44 #include "StyleResolver.h"
45 #include "StyleSheetContents.h"
46 #include "StyleSheetList.h"
47 #include "UserContentURLPattern.h"
48 #include "WebCoreMemoryInstrumentation.h"
49 #include <wtf/MemoryInstrumentationVector.h>
53 using namespace HTMLNames;
55 DocumentStyleSheetCollection::DocumentStyleSheetCollection(Document* document)
56 : m_document(document)
57 , m_pendingStylesheets(0)
58 , m_pageGroupUserSheetCacheValid(false)
59 , m_hadActiveLoadingStylesheet(false)
60 , m_needsUpdateActiveStylesheetsOnStyleRecalc(false)
61 , m_usesSiblingRules(false)
62 , m_usesSiblingRulesOverride(false)
63 , m_usesFirstLineRules(false)
64 , m_usesFirstLetterRules(false)
65 , m_usesBeforeAfterRules(false)
66 , m_usesBeforeAfterRulesOverride(false)
67 , m_usesRemUnits(false)
71 DocumentStyleSheetCollection::~DocumentStyleSheetCollection()
74 m_pageUserSheet->clearOwnerNode();
75 if (m_pageGroupUserSheets) {
76 for (size_t i = 0; i < m_pageGroupUserSheets->size(); ++i)
77 (*m_pageGroupUserSheets)[i]->clearOwnerNode();
80 for (size_t i = 0; i < m_userSheets->size(); ++i)
81 (*m_userSheets)[i]->clearOwnerNode();
85 void DocumentStyleSheetCollection::combineCSSFeatureFlags()
87 // 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).
88 StyleResolver* styleResolver = m_document->styleResolver();
89 m_usesSiblingRules = m_usesSiblingRules || styleResolver->usesSiblingRules();
90 m_usesFirstLineRules = m_usesFirstLineRules || styleResolver->usesFirstLineRules();
91 m_usesBeforeAfterRules = m_usesBeforeAfterRules || styleResolver->usesBeforeAfterRules();
94 void DocumentStyleSheetCollection::resetCSSFeatureFlags()
96 StyleResolver* styleResolver = m_document->styleResolver();
97 m_usesSiblingRules = styleResolver->usesSiblingRules();
98 m_usesFirstLineRules = styleResolver->usesFirstLineRules();
99 m_usesBeforeAfterRules = styleResolver->usesBeforeAfterRules();
102 CSSStyleSheet* DocumentStyleSheetCollection::pageUserSheet()
105 return m_pageUserSheet.get();
107 Page* owningPage = m_document->page();
111 String userSheetText = owningPage->userStyleSheet();
112 if (userSheetText.isEmpty())
115 // Parse the sheet and cache it.
116 m_pageUserSheet = CSSStyleSheet::createInline(m_document, m_document->settings()->userStyleSheetLocation());
117 m_pageUserSheet->contents()->setIsUserStyleSheet(true);
118 m_pageUserSheet->contents()->parseString(userSheetText);
119 return m_pageUserSheet.get();
122 void DocumentStyleSheetCollection::clearPageUserSheet()
124 if (m_pageUserSheet) {
126 m_document->styleResolverChanged(DeferRecalcStyle);
130 void DocumentStyleSheetCollection::updatePageUserSheet()
132 clearPageUserSheet();
134 m_document->styleResolverChanged(RecalcStyleImmediately);
137 const Vector<RefPtr<CSSStyleSheet> >* DocumentStyleSheetCollection::pageGroupUserSheets() const
139 if (m_pageGroupUserSheetCacheValid)
140 return m_pageGroupUserSheets.get();
142 m_pageGroupUserSheetCacheValid = true;
144 Page* owningPage = m_document->page();
148 const PageGroup& pageGroup = owningPage->group();
149 const UserStyleSheetMap* sheetsMap = pageGroup.userStyleSheets();
153 UserStyleSheetMap::const_iterator end = sheetsMap->end();
154 for (UserStyleSheetMap::const_iterator it = sheetsMap->begin(); it != end; ++it) {
155 const UserStyleSheetVector* sheets = it->second.get();
156 for (unsigned i = 0; i < sheets->size(); ++i) {
157 const UserStyleSheet* sheet = sheets->at(i).get();
158 if (sheet->injectedFrames() == InjectInTopFrameOnly && m_document->ownerElement())
160 if (!UserContentURLPattern::matchesPatterns(m_document->url(), sheet->whitelist(), sheet->blacklist()))
162 RefPtr<CSSStyleSheet> groupSheet = CSSStyleSheet::createInline(const_cast<Document*>(m_document), sheet->url());
163 if (!m_pageGroupUserSheets)
164 m_pageGroupUserSheets = adoptPtr(new Vector<RefPtr<CSSStyleSheet> >);
165 m_pageGroupUserSheets->append(groupSheet);
166 groupSheet->contents()->setIsUserStyleSheet(sheet->level() == UserStyleUserLevel);
167 groupSheet->contents()->parseString(sheet->source());
171 return m_pageGroupUserSheets.get();
174 void DocumentStyleSheetCollection::clearPageGroupUserSheets()
176 m_pageGroupUserSheetCacheValid = false;
177 if (m_pageGroupUserSheets && m_pageGroupUserSheets->size()) {
178 m_pageGroupUserSheets->clear();
179 m_document->styleResolverChanged(DeferRecalcStyle);
183 void DocumentStyleSheetCollection::updatePageGroupUserSheets()
185 clearPageGroupUserSheets();
186 if (pageGroupUserSheets() && pageGroupUserSheets()->size())
187 m_document->styleResolverChanged(RecalcStyleImmediately);
190 void DocumentStyleSheetCollection::addUserSheet(PassRefPtr<StyleSheetContents> userSheet)
193 m_userSheets = adoptPtr(new Vector<RefPtr<CSSStyleSheet> >);
194 m_userSheets->append(CSSStyleSheet::create(userSheet, m_document));
195 m_document->styleResolverChanged(RecalcStyleImmediately);
198 // This method is called whenever a top-level stylesheet has finished loading.
199 void DocumentStyleSheetCollection::removePendingSheet(RemovePendingSheetNotificationType notification)
201 // Make sure we knew this sheet was pending, and that our count isn't out of sync.
202 ASSERT(m_pendingStylesheets > 0);
204 m_pendingStylesheets--;
206 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
208 printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets);
211 if (m_pendingStylesheets)
214 if (notification == RemovePendingSheetNotifyLater) {
215 m_document->setNeedsNotifyRemoveAllPendingStylesheet();
219 m_document->didRemoveAllPendingStylesheet();
222 void DocumentStyleSheetCollection::addStyleSheetCandidateNode(Node* node, bool createdByParser)
224 if (!node->inDocument())
227 // Until the <body> exists, we have no choice but to compare document positions,
228 // since styles outside of the body and head continue to be shunted into the head
229 // (and thus can shift to end up before dynamically added DOM content that is also
230 // outside the body).
231 if ((createdByParser && m_document->body()) || m_styleSheetCandidateNodes.isEmpty()) {
232 m_styleSheetCandidateNodes.add(node);
236 // Determine an appropriate insertion point.
237 StyleSheetCandidateListHashSet::iterator begin = m_styleSheetCandidateNodes.begin();
238 StyleSheetCandidateListHashSet::iterator end = m_styleSheetCandidateNodes.end();
239 StyleSheetCandidateListHashSet::iterator it = end;
240 Node* followingNode = 0;
244 unsigned short position = n->compareDocumentPosition(node);
245 if (position == Node::DOCUMENT_POSITION_FOLLOWING) {
246 m_styleSheetCandidateNodes.insertBefore(followingNode, node);
250 } while (it != begin);
252 m_styleSheetCandidateNodes.insertBefore(followingNode, node);
255 void DocumentStyleSheetCollection::removeStyleSheetCandidateNode(Node* node)
257 m_styleSheetCandidateNodes.remove(node);
260 void DocumentStyleSheetCollection::collectActiveStyleSheets(Vector<RefPtr<StyleSheet> >& sheets)
262 if (m_document->settings() && !m_document->settings()->authorAndUserStylesEnabled())
265 StyleSheetCandidateListHashSet::iterator begin = m_styleSheetCandidateNodes.begin();
266 StyleSheetCandidateListHashSet::iterator end = m_styleSheetCandidateNodes.end();
267 for (StyleSheetCandidateListHashSet::iterator it = begin; it != end; ++it) {
269 StyleSheet* sheet = 0;
270 if (n->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) {
271 // Processing instruction (XML documents only).
272 // We don't support linking to embedded CSS stylesheets, see <https://bugs.webkit.org/show_bug.cgi?id=49281> for discussion.
273 ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(n);
276 // Don't apply XSL transforms to already transformed documents -- <rdar://problem/4132806>
277 if (pi->isXSL() && !m_document->transformSourceDocument()) {
278 // Don't apply XSL transforms until loading is finished.
279 if (!m_document->parsing())
280 m_document->applyXSLTransform(pi);
284 } else if ((n->isHTMLElement() && (n->hasTagName(linkTag) || n->hasTagName(styleTag)))
286 || (n->isSVGElement() && n->hasTagName(SVGNames::styleTag))
289 Element* e = static_cast<Element*>(n);
290 AtomicString title = e->getAttribute(titleAttr);
291 bool enabledViaScript = false;
292 if (e->hasLocalName(linkTag)) {
294 HTMLLinkElement* linkElement = static_cast<HTMLLinkElement*>(n);
295 if (linkElement->isDisabled())
297 enabledViaScript = linkElement->isEnabledViaScript();
298 if (linkElement->styleSheetIsLoading()) {
299 // it is loading but we should still decide which style sheet set to use
300 if (!enabledViaScript && !title.isEmpty() && m_preferredStylesheetSetName.isEmpty()) {
301 const AtomicString& rel = e->getAttribute(relAttr);
302 if (!rel.contains("alternate")) {
303 m_preferredStylesheetSetName = title;
304 m_selectedStylesheetSetName = title;
309 if (!linkElement->sheet())
312 // Get the current preferred styleset. This is the
313 // set of sheets that will be enabled.
315 if (n->isSVGElement() && n->hasTagName(SVGNames::styleTag))
316 sheet = static_cast<SVGStyleElement*>(n)->sheet();
319 if (e->hasLocalName(linkTag))
320 sheet = static_cast<HTMLLinkElement*>(n)->sheet();
323 sheet = static_cast<HTMLStyleElement*>(n)->sheet();
324 // Check to see if this sheet belongs to a styleset
325 // (thus making it PREFERRED or ALTERNATE rather than
327 AtomicString rel = e->getAttribute(relAttr);
328 if (!enabledViaScript && !title.isEmpty()) {
329 // Yes, we have a title.
330 if (m_preferredStylesheetSetName.isEmpty()) {
331 // No preferred set has been established. If
332 // we are NOT an alternate sheet, then establish
333 // us as the preferred set. Otherwise, just ignore
335 if (e->hasLocalName(styleTag) || !rel.contains("alternate"))
336 m_preferredStylesheetSetName = m_selectedStylesheetSetName = title;
338 if (title != m_preferredStylesheetSetName)
342 if (rel.contains("alternate") && title.isEmpty())
346 sheets.append(sheet);
350 bool DocumentStyleSheetCollection::testAddedStyleSheetRequiresStyleRecalc(StyleSheetContents* stylesheet)
352 // See if all rules on the sheet are scoped to some specific ids or classes.
353 // Then test if we actually have any of those in the tree at the moment.
354 HashSet<AtomicStringImpl*> idScopes;
355 HashSet<AtomicStringImpl*> classScopes;
356 if (!StyleResolver::determineStylesheetSelectorScopes(stylesheet, idScopes, classScopes))
358 // Invalidate the subtrees that match the scopes.
359 Node* node = m_document->firstChild();
361 if (!node->isStyledElement()) {
362 node = node->traverseNextNode();
365 StyledElement* element = static_cast<StyledElement*>(node);
366 if (SelectorChecker::elementMatchesSelectorScopes(element, idScopes, classScopes)) {
367 element->setNeedsStyleRecalc();
368 // The whole subtree is now invalidated, we can skip to the next sibling.
369 node = node->traverseNextSibling();
372 node = node->traverseNextNode();
377 void DocumentStyleSheetCollection::analyzeStyleSheetChange(UpdateFlag updateFlag, const Vector<RefPtr<StyleSheet> >& newStylesheets, bool& requiresStyleResolverReset, bool& requiresFullStyleRecalc)
379 requiresStyleResolverReset = true;
380 requiresFullStyleRecalc = true;
382 // Stylesheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done.
383 bool hasActiveLoadingStylesheet = false;
384 unsigned newStylesheetCount = newStylesheets.size();
385 for (unsigned i = 0; i < newStylesheetCount; ++i) {
386 if (newStylesheets[i]->isLoading())
387 hasActiveLoadingStylesheet = true;
389 if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) {
390 m_hadActiveLoadingStylesheet = false;
393 m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet;
395 if (updateFlag != OptimizedUpdate)
397 if (!m_document->styleResolverIfExists())
400 // See if we are just adding stylesheets.
401 unsigned oldStylesheetCount = m_authorStyleSheets.size();
402 if (newStylesheetCount < oldStylesheetCount)
404 for (unsigned i = 0; i < oldStylesheetCount; ++i) {
405 if (m_authorStyleSheets[i] != newStylesheets[i])
408 requiresStyleResolverReset = false;
410 // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs.
411 if (!m_document->body() || m_document->hasNodesWithPlaceholderStyle())
413 for (unsigned i = oldStylesheetCount; i < newStylesheetCount; ++i) {
414 if (!newStylesheets[i]->isCSSStyleSheet())
416 if (newStylesheets[i]->disabled())
418 if (testAddedStyleSheetRequiresStyleRecalc(static_cast<CSSStyleSheet*>(newStylesheets[i].get())->contents()))
421 requiresFullStyleRecalc = false;
424 static bool styleSheetsUseRemUnits(const Vector<RefPtr<StyleSheet> >& sheets)
426 for (unsigned i = 0; i < sheets.size(); ++i) {
427 if (!sheets[i]->isCSSStyleSheet())
429 if (static_cast<CSSStyleSheet*>(sheets[i].get())->contents()->usesRemUnits())
435 bool DocumentStyleSheetCollection::updateActiveStyleSheets(UpdateFlag updateFlag)
437 if (m_document->inStyleRecalc()) {
438 // SVG <use> element may manage to invalidate style selector in the middle of a style recalc.
439 // https://bugs.webkit.org/show_bug.cgi?id=54344
440 // FIXME: This should be fixed in SVG and the call site replaced by ASSERT(!m_inStyleRecalc).
441 m_needsUpdateActiveStylesheetsOnStyleRecalc = true;
442 m_document->scheduleForcedStyleRecalc();
446 if (!m_document->renderer() || !m_document->attached())
449 Vector<RefPtr<StyleSheet> > newStylesheets;
450 collectActiveStyleSheets(newStylesheets);
452 bool requiresStyleResolverReset;
453 bool requiresFullStyleRecalc;
454 analyzeStyleSheetChange(updateFlag, newStylesheets, requiresStyleResolverReset, requiresFullStyleRecalc);
456 if (requiresStyleResolverReset)
457 m_document->clearStyleResolver();
459 m_document->styleResolver()->appendAuthorStylesheets(m_authorStyleSheets.size(), newStylesheets);
460 resetCSSFeatureFlags();
462 m_authorStyleSheets.swap(newStylesheets);
464 m_usesRemUnits = styleSheetsUseRemUnits(m_authorStyleSheets);
465 m_needsUpdateActiveStylesheetsOnStyleRecalc = false;
467 m_document->notifySeamlessChildDocumentsOfStylesheetUpdate();
469 return requiresFullStyleRecalc;
472 void DocumentStyleSheetCollection::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
474 MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::DOM);
475 info.addMember(m_pageUserSheet);
476 info.addMember(m_pageGroupUserSheets);
477 info.addMember(m_userSheets);
478 info.addMember(m_authorStyleSheets);
479 info.addListHashSet(m_styleSheetCandidateNodes);
480 info.addMember(m_preferredStylesheetSetName);
481 info.addMember(m_selectedStylesheetSetName);