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"
52 using namespace HTMLNames;
54 DocumentStyleSheetCollection::DocumentStyleSheetCollection(Document* document)
55 : m_document(document)
56 , m_pendingStylesheets(0)
57 , m_pageGroupUserSheetCacheValid(false)
58 , m_hadActiveLoadingStylesheet(false)
59 , m_needsUpdateActiveStylesheetsOnStyleRecalc(false)
60 , m_usesSiblingRules(false)
61 , m_usesSiblingRulesOverride(false)
62 , m_usesFirstLineRules(false)
63 , m_usesFirstLetterRules(false)
64 , m_usesBeforeAfterRules(false)
65 , m_usesBeforeAfterRulesOverride(false)
66 , m_usesRemUnits(false)
70 DocumentStyleSheetCollection::~DocumentStyleSheetCollection()
73 m_pageUserSheet->clearOwnerNode();
74 if (m_pageGroupUserSheets) {
75 for (size_t i = 0; i < m_pageGroupUserSheets->size(); ++i)
76 (*m_pageGroupUserSheets)[i]->clearOwnerNode();
79 for (size_t i = 0; i < m_userSheets->size(); ++i)
80 (*m_userSheets)[i]->clearOwnerNode();
84 void DocumentStyleSheetCollection::combineCSSFeatureFlags()
86 // 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).
87 StyleResolver* styleResolver = m_document->styleResolver();
88 m_usesSiblingRules = m_usesSiblingRules || styleResolver->usesSiblingRules();
89 m_usesFirstLineRules = m_usesFirstLineRules || styleResolver->usesFirstLineRules();
90 m_usesBeforeAfterRules = m_usesBeforeAfterRules || styleResolver->usesBeforeAfterRules();
93 void DocumentStyleSheetCollection::resetCSSFeatureFlags()
95 StyleResolver* styleResolver = m_document->styleResolver();
96 m_usesSiblingRules = styleResolver->usesSiblingRules();
97 m_usesFirstLineRules = styleResolver->usesFirstLineRules();
98 m_usesBeforeAfterRules = styleResolver->usesBeforeAfterRules();
101 CSSStyleSheet* DocumentStyleSheetCollection::pageUserSheet()
104 return m_pageUserSheet.get();
106 Page* owningPage = m_document->page();
110 String userSheetText = owningPage->userStyleSheet();
111 if (userSheetText.isEmpty())
114 // Parse the sheet and cache it.
115 m_pageUserSheet = CSSStyleSheet::createInline(m_document, m_document->settings()->userStyleSheetLocation());
116 m_pageUserSheet->contents()->setIsUserStyleSheet(true);
117 m_pageUserSheet->contents()->parseString(userSheetText);
118 return m_pageUserSheet.get();
121 void DocumentStyleSheetCollection::clearPageUserSheet()
123 if (m_pageUserSheet) {
125 m_document->styleResolverChanged(DeferRecalcStyle);
129 void DocumentStyleSheetCollection::updatePageUserSheet()
131 clearPageUserSheet();
133 m_document->styleResolverChanged(RecalcStyleImmediately);
136 const Vector<RefPtr<CSSStyleSheet> >* DocumentStyleSheetCollection::pageGroupUserSheets() const
138 if (m_pageGroupUserSheetCacheValid)
139 return m_pageGroupUserSheets.get();
141 m_pageGroupUserSheetCacheValid = true;
143 Page* owningPage = m_document->page();
147 const PageGroup& pageGroup = owningPage->group();
148 const UserStyleSheetMap* sheetsMap = pageGroup.userStyleSheets();
152 UserStyleSheetMap::const_iterator end = sheetsMap->end();
153 for (UserStyleSheetMap::const_iterator it = sheetsMap->begin(); it != end; ++it) {
154 const UserStyleSheetVector* sheets = it->second.get();
155 for (unsigned i = 0; i < sheets->size(); ++i) {
156 const UserStyleSheet* sheet = sheets->at(i).get();
157 if (sheet->injectedFrames() == InjectInTopFrameOnly && m_document->ownerElement())
159 if (!UserContentURLPattern::matchesPatterns(m_document->url(), sheet->whitelist(), sheet->blacklist()))
161 RefPtr<CSSStyleSheet> groupSheet = CSSStyleSheet::createInline(const_cast<Document*>(m_document), sheet->url());
162 if (!m_pageGroupUserSheets)
163 m_pageGroupUserSheets = adoptPtr(new Vector<RefPtr<CSSStyleSheet> >);
164 m_pageGroupUserSheets->append(groupSheet);
165 groupSheet->contents()->setIsUserStyleSheet(sheet->level() == UserStyleUserLevel);
166 groupSheet->contents()->parseString(sheet->source());
170 return m_pageGroupUserSheets.get();
173 void DocumentStyleSheetCollection::clearPageGroupUserSheets()
175 m_pageGroupUserSheetCacheValid = false;
176 if (m_pageGroupUserSheets && m_pageGroupUserSheets->size()) {
177 m_pageGroupUserSheets->clear();
178 m_document->styleResolverChanged(DeferRecalcStyle);
182 void DocumentStyleSheetCollection::updatePageGroupUserSheets()
184 clearPageGroupUserSheets();
185 if (pageGroupUserSheets() && pageGroupUserSheets()->size())
186 m_document->styleResolverChanged(RecalcStyleImmediately);
189 void DocumentStyleSheetCollection::addUserSheet(PassRefPtr<StyleSheetContents> userSheet)
192 m_userSheets = adoptPtr(new Vector<RefPtr<CSSStyleSheet> >);
193 m_userSheets->append(CSSStyleSheet::create(userSheet, m_document));
194 m_document->styleResolverChanged(RecalcStyleImmediately);
197 // This method is called whenever a top-level stylesheet has finished loading.
198 void DocumentStyleSheetCollection::removePendingSheet(RemovePendingSheetNotificationType notification)
200 // Make sure we knew this sheet was pending, and that our count isn't out of sync.
201 ASSERT(m_pendingStylesheets > 0);
203 m_pendingStylesheets--;
205 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
207 printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets);
210 if (m_pendingStylesheets)
213 if (notification == RemovePendingSheetNotifyLater) {
214 m_document->setNeedsNotifyRemoveAllPendingStylesheet();
218 m_document->didRemoveAllPendingStylesheet();
221 void DocumentStyleSheetCollection::addStyleSheetCandidateNode(Node* node, bool createdByParser)
223 if (!node->inDocument())
226 // Until the <body> exists, we have no choice but to compare document positions,
227 // since styles outside of the body and head continue to be shunted into the head
228 // (and thus can shift to end up before dynamically added DOM content that is also
229 // outside the body).
230 if ((createdByParser && m_document->body()) || m_styleSheetCandidateNodes.isEmpty()) {
231 m_styleSheetCandidateNodes.add(node);
235 // Determine an appropriate insertion point.
236 StyleSheetCandidateListHashSet::iterator begin = m_styleSheetCandidateNodes.begin();
237 StyleSheetCandidateListHashSet::iterator end = m_styleSheetCandidateNodes.end();
238 StyleSheetCandidateListHashSet::iterator it = end;
239 Node* followingNode = 0;
243 unsigned short position = n->compareDocumentPosition(node);
244 if (position == Node::DOCUMENT_POSITION_FOLLOWING) {
245 m_styleSheetCandidateNodes.insertBefore(followingNode, node);
249 } while (it != begin);
251 m_styleSheetCandidateNodes.insertBefore(followingNode, node);
254 void DocumentStyleSheetCollection::removeStyleSheetCandidateNode(Node* node)
256 m_styleSheetCandidateNodes.remove(node);
259 void DocumentStyleSheetCollection::collectActiveStyleSheets(Vector<RefPtr<StyleSheet> >& sheets)
261 if (m_document->settings() && !m_document->settings()->authorAndUserStylesEnabled())
264 StyleSheetCandidateListHashSet::iterator begin = m_styleSheetCandidateNodes.begin();
265 StyleSheetCandidateListHashSet::iterator end = m_styleSheetCandidateNodes.end();
266 for (StyleSheetCandidateListHashSet::iterator it = begin; it != end; ++it) {
268 StyleSheet* sheet = 0;
269 if (n->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) {
270 // Processing instruction (XML documents only).
271 // We don't support linking to embedded CSS stylesheets, see <https://bugs.webkit.org/show_bug.cgi?id=49281> for discussion.
272 ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(n);
275 // Don't apply XSL transforms to already transformed documents -- <rdar://problem/4132806>
276 if (pi->isXSL() && !m_document->transformSourceDocument()) {
277 // Don't apply XSL transforms until loading is finished.
278 if (!m_document->parsing())
279 m_document->applyXSLTransform(pi);
283 } else if ((n->isHTMLElement() && (n->hasTagName(linkTag) || n->hasTagName(styleTag)))
285 || (n->isSVGElement() && n->hasTagName(SVGNames::styleTag))
288 Element* e = static_cast<Element*>(n);
289 AtomicString title = e->getAttribute(titleAttr);
290 bool enabledViaScript = false;
291 if (e->hasLocalName(linkTag)) {
293 HTMLLinkElement* linkElement = static_cast<HTMLLinkElement*>(n);
294 if (linkElement->isDisabled())
296 enabledViaScript = linkElement->isEnabledViaScript();
297 if (linkElement->styleSheetIsLoading()) {
298 // it is loading but we should still decide which style sheet set to use
299 if (!enabledViaScript && !title.isEmpty() && m_preferredStylesheetSetName.isEmpty()) {
300 const AtomicString& rel = e->getAttribute(relAttr);
301 if (!rel.contains("alternate")) {
302 m_preferredStylesheetSetName = title;
303 m_selectedStylesheetSetName = title;
308 if (!linkElement->sheet())
311 // Get the current preferred styleset. This is the
312 // set of sheets that will be enabled.
314 if (n->isSVGElement() && n->hasTagName(SVGNames::styleTag))
315 sheet = static_cast<SVGStyleElement*>(n)->sheet();
318 if (e->hasLocalName(linkTag))
319 sheet = static_cast<HTMLLinkElement*>(n)->sheet();
322 sheet = static_cast<HTMLStyleElement*>(n)->sheet();
323 // Check to see if this sheet belongs to a styleset
324 // (thus making it PREFERRED or ALTERNATE rather than
326 AtomicString rel = e->getAttribute(relAttr);
327 if (!enabledViaScript && !title.isEmpty()) {
328 // Yes, we have a title.
329 if (m_preferredStylesheetSetName.isEmpty()) {
330 // No preferred set has been established. If
331 // we are NOT an alternate sheet, then establish
332 // us as the preferred set. Otherwise, just ignore
334 if (e->hasLocalName(styleTag) || !rel.contains("alternate"))
335 m_preferredStylesheetSetName = m_selectedStylesheetSetName = title;
337 if (title != m_preferredStylesheetSetName)
341 if (rel.contains("alternate") && title.isEmpty())
345 sheets.append(sheet);
349 bool DocumentStyleSheetCollection::testAddedStyleSheetRequiresStyleRecalc(StyleSheetContents* stylesheet)
351 // See if all rules on the sheet are scoped to some specific ids or classes.
352 // Then test if we actually have any of those in the tree at the moment.
353 HashSet<AtomicStringImpl*> idScopes;
354 HashSet<AtomicStringImpl*> classScopes;
355 if (!StyleResolver::determineStylesheetSelectorScopes(stylesheet, idScopes, classScopes))
357 // Invalidate the subtrees that match the scopes.
358 Node* node = m_document->firstChild();
360 if (!node->isStyledElement()) {
361 node = node->traverseNextNode();
364 StyledElement* element = static_cast<StyledElement*>(node);
365 if (SelectorChecker::elementMatchesSelectorScopes(element, idScopes, classScopes)) {
366 element->setNeedsStyleRecalc();
367 // The whole subtree is now invalidated, we can skip to the next sibling.
368 node = node->traverseNextSibling();
371 node = node->traverseNextNode();
376 void DocumentStyleSheetCollection::analyzeStyleSheetChange(UpdateFlag updateFlag, const Vector<RefPtr<StyleSheet> >& newStylesheets, bool& requiresStyleResolverReset, bool& requiresFullStyleRecalc)
378 requiresStyleResolverReset = true;
379 requiresFullStyleRecalc = true;
381 // Stylesheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done.
382 bool hasActiveLoadingStylesheet = false;
383 unsigned newStylesheetCount = newStylesheets.size();
384 for (unsigned i = 0; i < newStylesheetCount; ++i) {
385 if (newStylesheets[i]->isLoading())
386 hasActiveLoadingStylesheet = true;
388 if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) {
389 m_hadActiveLoadingStylesheet = false;
392 m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet;
394 if (updateFlag != OptimizedUpdate)
396 if (!m_document->styleResolverIfExists())
399 // See if we are just adding stylesheets.
400 unsigned oldStylesheetCount = m_authorStyleSheets.size();
401 if (newStylesheetCount < oldStylesheetCount)
403 for (unsigned i = 0; i < oldStylesheetCount; ++i) {
404 if (m_authorStyleSheets[i] != newStylesheets[i])
407 requiresStyleResolverReset = false;
409 // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs.
410 if (!m_document->body() || m_document->hasNodesWithPlaceholderStyle())
412 for (unsigned i = oldStylesheetCount; i < newStylesheetCount; ++i) {
413 if (!newStylesheets[i]->isCSSStyleSheet())
415 if (newStylesheets[i]->disabled())
417 if (testAddedStyleSheetRequiresStyleRecalc(static_cast<CSSStyleSheet*>(newStylesheets[i].get())->contents()))
420 requiresFullStyleRecalc = false;
423 static bool styleSheetsUseRemUnits(const Vector<RefPtr<StyleSheet> >& sheets)
425 for (unsigned i = 0; i < sheets.size(); ++i) {
426 if (!sheets[i]->isCSSStyleSheet())
428 if (static_cast<CSSStyleSheet*>(sheets[i].get())->contents()->usesRemUnits())
434 bool DocumentStyleSheetCollection::updateActiveStyleSheets(UpdateFlag updateFlag)
436 if (m_document->inStyleRecalc()) {
437 // SVG <use> element may manage to invalidate style selector in the middle of a style recalc.
438 // https://bugs.webkit.org/show_bug.cgi?id=54344
439 // FIXME: This should be fixed in SVG and the call site replaced by ASSERT(!m_inStyleRecalc).
440 m_needsUpdateActiveStylesheetsOnStyleRecalc = true;
441 m_document->scheduleForcedStyleRecalc();
445 if (!m_document->renderer() || !m_document->attached())
448 Vector<RefPtr<StyleSheet> > newStylesheets;
449 collectActiveStyleSheets(newStylesheets);
451 bool requiresStyleResolverReset;
452 bool requiresFullStyleRecalc;
453 analyzeStyleSheetChange(updateFlag, newStylesheets, requiresStyleResolverReset, requiresFullStyleRecalc);
455 if (requiresStyleResolverReset)
456 m_document->clearStyleResolver();
458 m_document->styleResolver()->appendAuthorStylesheets(m_authorStyleSheets.size(), newStylesheets);
459 resetCSSFeatureFlags();
461 m_authorStyleSheets.swap(newStylesheets);
463 m_usesRemUnits = styleSheetsUseRemUnits(m_authorStyleSheets);
464 m_needsUpdateActiveStylesheetsOnStyleRecalc = false;
466 m_document->notifySeamlessChildDocumentsOfStylesheetUpdate();
468 return requiresFullStyleRecalc;
471 void DocumentStyleSheetCollection::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
473 MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::DOM);
474 info.addMember(m_pageUserSheet);
475 if (m_pageGroupUserSheets)
476 info.addInstrumentedVectorPtr(m_pageGroupUserSheets);
478 info.addInstrumentedVectorPtr(m_userSheets);
479 info.addListHashSet(m_styleSheetCandidateNodes);
480 info.addMember(m_preferredStylesheetSetName);
481 info.addMember(m_selectedStylesheetSetName);