1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 4 * Copyright (C) 2012 Google Inc. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "StyleScopeResolver.h" 29 30#if ENABLE(STYLE_SCOPED) || ENABLE(SHADOW_DOM) 31 32#include "CSSStyleRule.h" 33#include "CSSStyleSheet.h" 34#include "ContentDistributor.h" 35#include "ContextFeatures.h" 36#include "ElementShadow.h" 37#include "HTMLNames.h" 38#include "HTMLStyleElement.h" 39#include "RuleFeature.h" 40#include "RuleSet.h" 41#include "ShadowRoot.h" 42 43namespace WebCore { 44 45StyleScopeResolver::StyleScopeResolver() 46 : m_stackParent(0) 47 , m_stackParentBoundsIndex(0) 48{ 49} 50 51StyleScopeResolver::~StyleScopeResolver() 52{ 53} 54 55const ContainerNode* StyleScopeResolver::scopeFor(const CSSStyleSheet* sheet) 56{ 57 ASSERT(sheet); 58 59 Document* document = sheet->ownerDocument(); 60 if (!document) 61 return 0; 62 Node* ownerNode = sheet->ownerNode(); 63 if (!ownerNode || !ownerNode->isHTMLElement() || !ownerNode->hasTagName(HTMLNames::styleTag)) 64 return 0; 65 66 HTMLStyleElement* styleElement = static_cast<HTMLStyleElement*>(ownerNode); 67 if (!styleElement->scoped()) 68 return styleElement->isInShadowTree() ? styleElement->containingShadowRoot() : 0; 69 70 ContainerNode* parent = styleElement->parentNode(); 71 if (!parent) 72 return 0; 73 74 return (parent->isElementNode() || parent->isShadowRoot()) ? parent : 0; 75} 76 77inline RuleSet* StyleScopeResolver::ruleSetFor(const ContainerNode* scope) const 78{ 79 if (!scope->hasScopedHTMLStyleChild()) 80 return 0; 81 ScopedRuleSetMap::const_iterator it = m_authorStyles.find(scope); 82 return it != m_authorStyles.end() ? it->value.get() : 0; 83} 84 85RuleSet* StyleScopeResolver::ensureRuleSetFor(const ContainerNode* scope) 86{ 87 ScopedRuleSetMap::AddResult addResult = m_authorStyles.add(scope, nullptr); 88 if (addResult.isNewEntry) 89 addResult.iterator->value = RuleSet::create(); 90 return addResult.iterator->value.get(); 91} 92 93void StyleScopeResolver::setupStack(const ContainerNode* parent) 94{ 95 // The scoping element stack shouldn't be used if <style scoped> isn't used anywhere. 96 ASSERT(!m_authorStyles.isEmpty()); 97 98 m_stack.shrink(0); 99 int authorStyleBoundsIndex = 0; 100 for (const ContainerNode* scope = parent; scope; scope = scope->parentOrShadowHostNode()) { 101 RuleSet* ruleSet = ruleSetFor(scope); 102 if (ruleSet) 103 m_stack.append(StackFrame(scope, authorStyleBoundsIndex, ruleSet)); 104 if (scope->isShadowRoot() && !toShadowRoot(scope)->applyAuthorStyles()) 105 --authorStyleBoundsIndex; 106 } 107 108 m_stack.reverse(); 109 m_stackParent = parent; 110 m_stackParentBoundsIndex = 0; 111} 112 113void StyleScopeResolver::push(const ContainerNode* scope, const ContainerNode* scopeParent) 114{ 115 // Shortcut: Don't bother with the scoping element stack if <style scoped> isn't used anywhere. 116 if (m_authorStyles.isEmpty()) { 117 ASSERT(!m_stackParent); 118 ASSERT(m_stack.isEmpty()); 119 return; 120 } 121 122 // In some wacky cases during style resolve we may get invoked for random elements. 123 // Recreate the whole scoping element stack in such cases. 124 if (!stackIsConsistent(scopeParent)) { 125 setupStack(scope); 126 return; 127 } 128 129 if (scope->isShadowRoot() && !toShadowRoot(scope)->applyAuthorStyles()) 130 ++m_stackParentBoundsIndex; 131 // Otherwise just push the parent onto the stack. 132 RuleSet* ruleSet = ruleSetFor(scope); 133 if (ruleSet) 134 m_stack.append(StackFrame(scope, m_stackParentBoundsIndex, ruleSet)); 135 m_stackParent = scope; 136} 137 138void StyleScopeResolver::pop(const ContainerNode* scope) 139{ 140 // Only bother to update the scoping element stack if it is consistent. 141 if (stackIsConsistent(scope)) { 142 if (!m_stack.isEmpty() && m_stack.last().m_scope == scope) 143 m_stack.removeLast(); 144 if (scope->isShadowRoot() && !toShadowRoot(scope)->applyAuthorStyles()) 145 --m_stackParentBoundsIndex; 146 m_stackParent = scope->parentOrShadowHostNode(); 147 } 148} 149 150void StyleScopeResolver::collectFeaturesTo(RuleFeatureSet& features) 151{ 152 for (ScopedRuleSetMap::iterator it = m_authorStyles.begin(); it != m_authorStyles.end(); ++it) 153 features.add(it->value->features()); 154#if ENABLE(SHADOW_DOM) 155 for (ScopedRuleSetMap::iterator it = m_atHostRules.begin(); it != m_atHostRules.end(); ++it) 156 features.add(it->value->features()); 157#endif 158} 159 160inline RuleSet* StyleScopeResolver::ensureAtHostRuleSetFor(const ShadowRoot* shadowRoot) 161{ 162 ScopedRuleSetMap::AddResult addResult = m_atHostRules.add(shadowRoot, nullptr); 163 if (addResult.isNewEntry) 164 addResult.iterator->value = RuleSet::create(); 165 return addResult.iterator->value.get(); 166} 167 168inline RuleSet* StyleScopeResolver::atHostRuleSetFor(const ShadowRoot* shadowRoot) const 169{ 170 ScopedRuleSetMap::const_iterator it = m_atHostRules.find(shadowRoot); 171 return it != m_atHostRules.end() ? it->value.get() : 0; 172} 173 174#if ENABLE(SHADOW_DOM) 175void StyleScopeResolver::addHostRule(StyleRuleHost* hostRule, bool hasDocumentSecurityOrigin, const ContainerNode* scope) 176{ 177 if (!scope || !scope->isInShadowTree()) 178 return; 179 180 ShadowRoot* shadowRoot = scope->containingShadowRoot(); 181 if (!shadowRoot || !shadowRoot->host()) 182 return; 183 184 RuleSet* rule = ensureAtHostRuleSetFor(shadowRoot); 185 186 const Vector<RefPtr<StyleRuleBase> >& childRules = hostRule->childRules(); 187 AddRuleFlags addRuleFlags = hasDocumentSecurityOrigin ? RuleHasDocumentSecurityOrigin : RuleHasNoSpecialState; 188 addRuleFlags = static_cast<AddRuleFlags>(addRuleFlags | RuleCanUseFastCheckSelector); 189 for (unsigned i = 0; i < childRules.size(); ++i) { 190 StyleRuleBase* hostStylingRule = childRules[i].get(); 191 if (hostStylingRule->isStyleRule()) 192 rule->addStyleRule(static_cast<StyleRule*>(hostStylingRule), addRuleFlags); 193 } 194} 195#endif 196 197bool StyleScopeResolver::styleSharingCandidateMatchesHostRules(const Element* element) 198{ 199 if (m_atHostRules.isEmpty()) 200 return false; 201 202 ElementShadow* shadow = element->shadow(); 203 if (!shadow) 204 return false; 205 206 // FIXME(99827): https://bugs.webkit.org/show_bug.cgi?id=99827 207 // add a new flag to ElementShadow and cache whether any@host @-rules are 208 // applied to the element or not. So we can avoid always traversing 209 // shadow roots. 210 if (ShadowRoot* shadowRoot = shadow->shadowRoot()) { 211 if (atHostRuleSetFor(shadowRoot)) 212 return true; 213 } 214 return false; 215} 216 217void StyleScopeResolver::matchHostRules(const Element* element, Vector<RuleSet*>& matchedRules) 218{ 219 if (m_atHostRules.isEmpty()) 220 return; 221 222 ElementShadow* shadow = element->shadow(); 223 if (!shadow) 224 return; 225 226 // FIXME(99827): https://bugs.webkit.org/show_bug.cgi?id=99827 227 // add a new flag to ElementShadow and cache whether any @host @-rules are 228 // applied to the element or not. So we can quickly exit this method 229 // by using the flag. 230 if (ShadowRoot* shadowRoot = shadow->shadowRoot()) { 231 if (RuleSet* ruleSet = atHostRuleSetFor(shadowRoot)) 232 matchedRules.append(ruleSet); 233 } 234} 235 236} 237 238#endif // ENABLE(STYLE_SCOPED) 239