| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| 3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) | 3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) |
| 4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com) | 4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com) |
| 5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc.
All rights reserved. | 5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc.
All rights reserved. |
| 6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> | 6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> |
| 7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org> | 7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org> |
| 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.t
orchmobile.com/) | 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.t
orchmobile.com/) |
| 9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. | 9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. |
| 10 * Copyright (C) Research In Motion Limited 2011. All rights reserved. | 10 * Copyright (C) Research In Motion Limited 2011. All rights reserved. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 * | 21 * |
| 22 * You should have received a copy of the GNU Library General Public License | 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 | 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, | 24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 25 * Boston, MA 02110-1301, USA. | 25 * Boston, MA 02110-1301, USA. |
| 26 */ | 26 */ |
| 27 | 27 |
| 28 #include "sky/engine/config.h" | 28 #include "sky/engine/config.h" |
| 29 #include "sky/engine/core/css/SelectorChecker.h" | 29 #include "sky/engine/core/css/SelectorChecker.h" |
| 30 | 30 |
| 31 #include "sky/engine/core/css/CSSSelector.h" |
| 31 #include "sky/engine/core/css/CSSSelectorList.h" | 32 #include "sky/engine/core/css/CSSSelectorList.h" |
| 32 #include "sky/engine/core/dom/Document.h" | 33 #include "sky/engine/core/dom/Document.h" |
| 33 #include "sky/engine/core/dom/shadow/ShadowRoot.h" | 34 #include "sky/engine/core/dom/shadow/ShadowRoot.h" |
| 34 #include "sky/engine/core/editing/FrameSelection.h" | 35 #include "sky/engine/core/editing/FrameSelection.h" |
| 35 #include "sky/engine/core/frame/LocalFrame.h" | 36 #include "sky/engine/core/frame/LocalFrame.h" |
| 36 #include "sky/engine/core/html/parser/HTMLParserIdioms.h" | 37 #include "sky/engine/core/html/parser/HTMLParserIdioms.h" |
| 37 #include "sky/engine/core/page/FocusController.h" | 38 #include "sky/engine/core/page/FocusController.h" |
| 38 #include "sky/engine/core/rendering/style/RenderStyle.h" | 39 #include "sky/engine/core/rendering/style/RenderStyle.h" |
| 39 | 40 |
| 40 namespace blink { | 41 namespace blink { |
| 41 | 42 |
| 42 SelectorChecker::SelectorChecker() | 43 static bool matchesFocusPseudoClass(const Element& element) |
| 43 : m_matchedAttributeSelector(false) | 44 { |
| 45 if (!element.focused()) |
| 46 return false; |
| 47 LocalFrame* frame = element.document().frame(); |
| 48 if (!frame) |
| 49 return false; |
| 50 if (!frame->selection().isFocusedAndActive()) |
| 51 return false; |
| 52 return true; |
| 53 } |
| 54 |
| 55 SelectorChecker::SelectorChecker(const Element& element) |
| 56 : m_element(element) |
| 57 , m_matchedAttributeSelector(false) |
| 44 , m_matchedFocusSelector(false) | 58 , m_matchedFocusSelector(false) |
| 45 , m_matchedHoverSelector(false) | 59 , m_matchedHoverSelector(false) |
| 46 , m_matchedActiveSelector(false) | 60 , m_matchedActiveSelector(false) |
| 47 { | 61 { |
| 48 } | 62 } |
| 49 | 63 |
| 50 static bool scopeContainsLastMatchedElement(const SelectorChecker::SelectorCheck
ingContext& context) | 64 bool SelectorChecker::match(const CSSSelector& selector, const ContainerNode* sc
ope) |
| 51 { | 65 { |
| 52 if (!context.scope) | 66 bool isShadowHost = isHostInItsShadowTree(m_element, scope); |
| 53 return true; | |
| 54 | 67 |
| 55 ASSERT(context.scope); | 68 const CSSSelector* current = &selector; |
| 56 if (context.scope->treeScope() == context.element->treeScope()) | |
| 57 return true; | |
| 58 | |
| 59 // Because Blink treats a shadow host's TreeScope as a separate one from its
descendent shadow roots, | |
| 60 // if the last matched element is a shadow host, the condition above isn't m
et, even though it | |
| 61 // should be. | |
| 62 return context.element == context.scope->shadowHost(); | |
| 63 } | |
| 64 | |
| 65 bool SelectorChecker::match(const SelectorCheckingContext& context) | |
| 66 { | |
| 67 // FIXME(sky): Get rid of SelectorCheckingContext. | |
| 68 SelectorCheckingContext matchContext(context); | |
| 69 | |
| 70 bool isShadowHost = isHostInItsShadowTree(*context.element, context.scope); | |
| 71 | |
| 72 while (true) { | 69 while (true) { |
| 73 const CSSSelector& selector = *matchContext.selector; | 70 // Only :host should match the host: |
| 74 // Only :host and :host-context() should match the host: | |
| 75 // http://drafts.csswg.org/css-scoping/#host-element | 71 // http://drafts.csswg.org/css-scoping/#host-element |
| 76 if (isShadowHost && !selector.isHostPseudoClass()) | 72 if (isShadowHost && !current->isHostPseudoClass()) |
| 77 return false; | 73 return false; |
| 78 if (!checkOne(matchContext)) | 74 if (!checkOne(*current, scope)) |
| 79 return false; | 75 return false; |
| 80 if (selector.isLastInTagHistory()) | 76 if (current->isLastInTagHistory()) { |
| 81 return scopeContainsLastMatchedElement(matchContext); | 77 // Only rules in the same scope, or from :host can match. |
| 82 matchContext.selector = selector.tagHistory(); | 78 return !scope || |
| 79 scope->treeScope() == m_element.treeScope() || |
| 80 m_element == scope->shadowHost(); |
| 81 } |
| 82 current = current->tagHistory(); |
| 83 } | 83 } |
| 84 | 84 |
| 85 ASSERT_NOT_REACHED(); | 85 ASSERT_NOT_REACHED(); |
| 86 return false; | 86 return false; |
| 87 } | 87 } |
| 88 | 88 |
| 89 template<typename CharType> | 89 template<typename CharType> |
| 90 static inline bool containsHTMLSpaceTemplate(const CharType* string, unsigned le
ngth) | 90 static inline bool containsHTMLSpaceTemplate(const CharType* string, unsigned le
ngth) |
| 91 { | 91 { |
| 92 for (unsigned i = 0; i < length; ++i) | 92 for (unsigned i = 0; i < length; ++i) |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 182 if (!attributeItem.matches(selectorAttr)) | 182 if (!attributeItem.matches(selectorAttr)) |
| 183 continue; | 183 continue; |
| 184 | 184 |
| 185 if (attributeValueMatches(attributeItem, match, selectorValue, !caseInse
nsitive)) | 185 if (attributeValueMatches(attributeItem, match, selectorValue, !caseInse
nsitive)) |
| 186 return true; | 186 return true; |
| 187 } | 187 } |
| 188 | 188 |
| 189 return false; | 189 return false; |
| 190 } | 190 } |
| 191 | 191 |
| 192 bool SelectorChecker::checkOne(const SelectorCheckingContext& context) | 192 bool SelectorChecker::checkOne(const CSSSelector& selector, const ContainerNode*
scope) |
| 193 { | 193 { |
| 194 ASSERT(context.element); | |
| 195 const Element& element = *context.element; | |
| 196 ASSERT(context.selector); | |
| 197 const CSSSelector& selector = *context.selector; | |
| 198 | |
| 199 switch (selector.match()) { | 194 switch (selector.match()) { |
| 200 case CSSSelector::Tag: | 195 case CSSSelector::Tag: |
| 201 return tagMatches(element, selector.tagQName()); | 196 { |
| 197 const AtomicString& localName = selector.tagQName().localName(); |
| 198 return localName == starAtom || localName == m_element.localName(); |
| 199 } |
| 202 case CSSSelector::Class: | 200 case CSSSelector::Class: |
| 203 return element.hasClass() && element.classNames().contains(selector.valu
e()); | 201 return m_element.hasClass() && m_element.classNames().contains(selector.
value()); |
| 204 case CSSSelector::Id: | 202 case CSSSelector::Id: |
| 205 return element.hasID() && element.idForStyleResolution() == selector.val
ue(); | 203 return m_element.hasID() && m_element.idForStyleResolution() == selector
.value(); |
| 206 case CSSSelector::Exact: | 204 case CSSSelector::Exact: |
| 207 case CSSSelector::Set: | 205 case CSSSelector::Set: |
| 208 case CSSSelector::Hyphen: | 206 case CSSSelector::Hyphen: |
| 209 case CSSSelector::List: | 207 case CSSSelector::List: |
| 210 case CSSSelector::Contain: | 208 case CSSSelector::Contain: |
| 211 case CSSSelector::Begin: | 209 case CSSSelector::Begin: |
| 212 case CSSSelector::End: | 210 case CSSSelector::End: |
| 213 if (anyAttributeMatches(element, selector.match(), selector)) { | 211 if (anyAttributeMatches(m_element, selector.match(), selector)) { |
| 214 m_matchedAttributeSelector = true; | 212 m_matchedAttributeSelector = true; |
| 215 return true; | 213 return true; |
| 216 } | 214 } |
| 217 return false; | 215 return false; |
| 218 case CSSSelector::PseudoClass: | 216 case CSSSelector::PseudoClass: |
| 219 return checkPseudoClass(context); | 217 return checkPseudoClass(selector, scope); |
| 220 // FIXME(sky): Remove pseudo elements completely. | 218 // FIXME(sky): Remove pseudo elements completely. |
| 221 case CSSSelector::PseudoElement: | 219 case CSSSelector::PseudoElement: |
| 222 case CSSSelector::Unknown: | 220 case CSSSelector::Unknown: |
| 223 return false; | 221 return false; |
| 224 } | 222 } |
| 225 ASSERT_NOT_REACHED(); | 223 ASSERT_NOT_REACHED(); |
| 226 return false; | 224 return false; |
| 227 } | 225 } |
| 228 | 226 |
| 229 bool SelectorChecker::checkPseudoClass(const SelectorCheckingContext& context) | 227 bool SelectorChecker::checkPseudoClass(const CSSSelector& selector, const Contai
nerNode* scope) |
| 230 { | 228 { |
| 231 ASSERT(context.element); | |
| 232 const Element& element = *context.element; | |
| 233 ASSERT(context.selector); | |
| 234 const CSSSelector& selector = *context.selector; | |
| 235 ASSERT(selector.match() == CSSSelector::PseudoClass); | |
| 236 | |
| 237 // Normal element pseudo class checking. | |
| 238 switch (selector.pseudoType()) { | 229 switch (selector.pseudoType()) { |
| 239 case CSSSelector::PseudoFocus: | 230 case CSSSelector::PseudoFocus: |
| 240 m_matchedFocusSelector = true; | 231 m_matchedFocusSelector = true; |
| 241 return matchesFocusPseudoClass(element); | 232 return matchesFocusPseudoClass(m_element); |
| 242 | 233 |
| 243 case CSSSelector::PseudoHover: | 234 case CSSSelector::PseudoHover: |
| 244 m_matchedHoverSelector = true; | 235 m_matchedHoverSelector = true; |
| 245 return element.hovered(); | 236 return m_element.hovered(); |
| 246 | 237 |
| 247 case CSSSelector::PseudoActive: | 238 case CSSSelector::PseudoActive: |
| 248 m_matchedActiveSelector = true; | 239 m_matchedActiveSelector = true; |
| 249 return element.active(); | 240 return m_element.active(); |
| 250 | 241 |
| 251 case CSSSelector::PseudoLang: | 242 case CSSSelector::PseudoLang: |
| 252 { | 243 { |
| 253 AtomicString value = element.computeInheritedLanguage(); | 244 AtomicString value = m_element.computeInheritedLanguage(); |
| 254 const AtomicString& argument = selector.argument(); | 245 const AtomicString& argument = selector.argument(); |
| 255 if (value.isEmpty() || !value.startsWith(argument, false)) | 246 if (value.isEmpty() || !value.startsWith(argument, false)) |
| 256 break; | 247 break; |
| 257 if (value.length() != argument.length() && value[argument.length()]
!= '-') | 248 if (value.length() != argument.length() && value[argument.length()]
!= '-') |
| 258 break; | 249 break; |
| 259 return true; | 250 return true; |
| 260 } | 251 } |
| 261 | 252 |
| 262 case CSSSelector::PseudoUnresolved: | 253 case CSSSelector::PseudoUnresolved: |
| 263 return element.isUnresolvedCustomElement(); | 254 return m_element.isUnresolvedCustomElement(); |
| 264 | 255 |
| 265 case CSSSelector::PseudoHost: | 256 case CSSSelector::PseudoHost: |
| 266 { | 257 { |
| 267 // :host only matches a shadow host when :host is in a shadow tree o
f the shadow host. | 258 const ContainerNode* shadowHost = scope->shadowHost(); |
| 268 if (!context.scope) | 259 if (!shadowHost || shadowHost != m_element) |
| 269 return false; | 260 return false; |
| 270 const ContainerNode* shadowHost = context.scope->shadowHost(); | |
| 271 if (!shadowHost || shadowHost != element) | |
| 272 return false; | |
| 273 ASSERT(element.shadow()); | |
| 274 | 261 |
| 275 // For empty parameter case, i.e. just :host or :host(). | 262 // For empty parameter case, i.e. just :host or :host(). |
| 276 if (!selector.selectorList()) | 263 if (!selector.selectorList()) |
| 277 return true; | 264 return true; |
| 278 | 265 |
| 279 SelectorCheckingContext subContext(context); | |
| 280 | |
| 281 // Treat the inside of :host() rules as if they were defined in the | 266 // Treat the inside of :host() rules as if they were defined in the |
| 282 // same scope as the host. | 267 // same scope as the host. |
| 283 subContext.scope = &context.element->treeScope().rootNode(); | 268 const ContainerNode& scope = m_element.treeScope().rootNode(); |
| 284 | 269 |
| 285 for (subContext.selector = selector.selectorList()->first(); subCont
ext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector))
{ | 270 for (const CSSSelector* current = selector.selectorList()->first();
current; current = CSSSelectorList::next(*current)) { |
| 286 if (match(subContext)) | 271 if (match(*current, &scope)) |
| 287 return true; | 272 return true; |
| 288 } | 273 } |
| 289 return false; | 274 return false; |
| 290 } | 275 } |
| 291 | 276 |
| 292 case CSSSelector::PseudoUnknown: | 277 case CSSSelector::PseudoUnknown: |
| 293 case CSSSelector::PseudoNotParsed: | 278 case CSSSelector::PseudoNotParsed: |
| 294 case CSSSelector::PseudoUserAgentCustomElement: | 279 case CSSSelector::PseudoUserAgentCustomElement: |
| 295 return false; | 280 return false; |
| 296 } | 281 } |
| 297 ASSERT_NOT_REACHED(); | 282 ASSERT_NOT_REACHED(); |
| 298 return false; | 283 return false; |
| 299 } | 284 } |
| 300 | 285 |
| 301 bool SelectorChecker::matchesFocusPseudoClass(const Element& element) | |
| 302 { | |
| 303 if (!element.focused()) | |
| 304 return false; | |
| 305 LocalFrame* frame = element.document().frame(); | |
| 306 if (!frame) | |
| 307 return false; | |
| 308 if (!frame->selection().isFocusedAndActive()) | |
| 309 return false; | |
| 310 return true; | |
| 311 } | 286 } |
| 312 | |
| 313 } | |
| OLD | NEW |