blob: ff5c008f3113640e4b3f57c2063a56a79c629146 [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
* Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
* Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
* Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
* Copyright (C) Research In Motion Limited 2011. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "sky/engine/core/css/SelectorChecker.h"
#include "sky/engine/core/css/CSSSelector.h"
#include "sky/engine/core/css/CSSSelectorList.h"
#include "sky/engine/core/dom/Document.h"
#include "sky/engine/core/frame/LocalFrame.h"
#include "sky/engine/core/html/parser/HTMLParserIdioms.h"
#include "sky/engine/core/rendering/style/RenderStyle.h"
namespace blink {
SelectorChecker::SelectorChecker(const Element& element)
: m_element(element)
, m_matchedAttributeSelector(false)
, m_matchedHoverSelector(false)
, m_matchedActiveSelector(false)
{
}
bool SelectorChecker::match(const CSSSelector& selector)
{
const CSSSelector* current = &selector;
do {
if (!checkOne(*current))
return false;
current = current->tagHistory();
} while (current);
return true;
}
static bool anyAttributeMatches(const Element& element, CSSSelector::Match match, const CSSSelector& selector)
{
const QualifiedName& selectorAttr = selector.attribute();
ASSERT(selectorAttr.localName() != starAtom); // Should not be possible from the CSS grammar.
if (match == CSSSelector::Set)
return element.hasAttribute(selectorAttr);
ASSERT(match == CSSSelector::Exact);
const AtomicString& selectorValue = selector.value();
const AtomicString& value = element.getAttribute(selectorAttr);
if (value.isNull())
return false;
if (selector.attributeMatchType() == CSSSelector::CaseInsensitive)
return equalIgnoringCase(selectorValue, value);
return selectorValue == value;
}
bool SelectorChecker::checkOne(const CSSSelector& selector)
{
switch (selector.match()) {
case CSSSelector::Tag:
{
const AtomicString& localName = selector.tagQName().localName();
return localName == starAtom || localName == m_element.localName();
}
case CSSSelector::Class:
return m_element.hasClass() && m_element.classNames().contains(selector.value());
case CSSSelector::Id:
return m_element.hasID() && m_element.idForStyleResolution() == selector.value();
case CSSSelector::Exact:
case CSSSelector::Set:
if (anyAttributeMatches(m_element, selector.match(), selector)) {
m_matchedAttributeSelector = true;
return true;
}
return false;
case CSSSelector::PseudoClass:
return checkPseudoClass(selector);
// FIXME(sky): Remove pseudo elements completely.
case CSSSelector::PseudoElement:
case CSSSelector::Unknown:
return false;
}
ASSERT_NOT_REACHED();
return false;
}
bool SelectorChecker::checkPseudoClass(const CSSSelector& selector)
{
switch (selector.pseudoType()) {
case CSSSelector::PseudoHover:
m_matchedHoverSelector = true;
return m_element.hovered();
case CSSSelector::PseudoActive:
m_matchedActiveSelector = true;
return m_element.active();
case CSSSelector::PseudoLang:
{
AtomicString value = m_element.computeInheritedLanguage();
const AtomicString& argument = selector.argument();
if (value.isEmpty() || !value.startsWith(argument, false))
break;
if (value.length() != argument.length() && value[argument.length()] != '-')
break;
return true;
}
case CSSSelector::PseudoHost:
{
// We can only get here if the selector was defined in the right
// scope so we don't need to check it.
// For empty parameter case, i.e. just :host or :host().
if (!selector.selectorList())
return true;
for (const CSSSelector* current = selector.selectorList()->first(); current; current = CSSSelectorList::next(*current)) {
if (match(*current))
return true;
}
return false;
}
case CSSSelector::PseudoUnknown:
case CSSSelector::PseudoNotParsed:
return false;
}
ASSERT_NOT_REACHED();
return false;
}
}