1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Neither the name of Google Inc. nor the names of its
11 * contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "HTMLContentElement.h"
29
30#include "CSSParser.h"
31#include "ContentDistributor.h"
32#include "HTMLNames.h"
33#include "QualifiedName.h"
34#include "RuntimeEnabledFeatures.h"
35#include "ShadowRoot.h"
36#include <wtf/StdLibExtras.h>
37
38namespace WebCore {
39
40using HTMLNames::selectAttr;
41
42#if ENABLE(SHADOW_DOM)
43
44const QualifiedName& HTMLContentElement::contentTagName(Document*)
45{
46    if (!RuntimeEnabledFeatures::shadowDOMEnabled())
47        return HTMLNames::webkitShadowContentTag;
48    return HTMLNames::contentTag;
49}
50
51PassRefPtr<HTMLContentElement> HTMLContentElement::create(Document* document)
52{
53    return adoptRef(new HTMLContentElement(contentTagName(document), document));
54}
55
56PassRefPtr<HTMLContentElement> HTMLContentElement::create(const QualifiedName& tagName, Document* document)
57{
58    return adoptRef(new HTMLContentElement(tagName, document));
59}
60
61HTMLContentElement::HTMLContentElement(const QualifiedName& name, Document* document)
62    : InsertionPoint(name, document)
63    , m_shouldParseSelectorList(false)
64    , m_isValidSelector(true)
65{
66}
67
68HTMLContentElement::~HTMLContentElement()
69{
70}
71
72InsertionPoint::MatchType HTMLContentElement::matchTypeFor(Node*)
73{
74    if (select().isNull() || select().isEmpty())
75        return AlwaysMatches;
76    if (!isSelectValid())
77        return NeverMatches;
78    return HasToMatchSelector;
79}
80
81const AtomicString& HTMLContentElement::select() const
82{
83    return getAttribute(selectAttr);
84}
85
86bool HTMLContentElement::isSelectValid()
87{
88    ensureSelectParsed();
89    return m_isValidSelector;
90}
91
92void HTMLContentElement::ensureSelectParsed()
93{
94    if (!m_shouldParseSelectorList)
95        return;
96
97    CSSParser parser(document());
98    parser.parseSelector(select(), m_selectorList);
99    m_shouldParseSelectorList = false;
100    m_isValidSelector = validateSelect();
101    if (!m_isValidSelector)
102        m_selectorList = CSSSelectorList();
103}
104
105void HTMLContentElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
106{
107    if (name == selectAttr) {
108        if (ShadowRoot* root = containingShadowRoot())
109            root->owner()->willAffectSelector();
110        m_shouldParseSelectorList = true;
111    } else
112        InsertionPoint::parseAttribute(name, value);
113}
114
115static bool validateSubSelector(const CSSSelector* selector)
116{
117    switch (selector->m_match) {
118    case CSSSelector::Tag:
119    case CSSSelector::Id:
120    case CSSSelector::Class:
121    case CSSSelector::Exact:
122    case CSSSelector::Set:
123    case CSSSelector::List:
124    case CSSSelector::Hyphen:
125    case CSSSelector::Contain:
126    case CSSSelector::Begin:
127    case CSSSelector::End:
128        return true;
129    case CSSSelector::PseudoElement:
130        return false;
131    case CSSSelector::PagePseudoClass:
132    case CSSSelector::PseudoClass:
133        break;
134    }
135
136    switch (selector->pseudoType()) {
137    case CSSSelector::PseudoEmpty:
138    case CSSSelector::PseudoLink:
139    case CSSSelector::PseudoVisited:
140    case CSSSelector::PseudoTarget:
141    case CSSSelector::PseudoEnabled:
142    case CSSSelector::PseudoDisabled:
143    case CSSSelector::PseudoChecked:
144    case CSSSelector::PseudoIndeterminate:
145    case CSSSelector::PseudoNthChild:
146    case CSSSelector::PseudoNthLastChild:
147    case CSSSelector::PseudoNthOfType:
148    case CSSSelector::PseudoNthLastOfType:
149    case CSSSelector::PseudoFirstChild:
150    case CSSSelector::PseudoLastChild:
151    case CSSSelector::PseudoFirstOfType:
152    case CSSSelector::PseudoLastOfType:
153    case CSSSelector::PseudoOnlyOfType:
154        return true;
155    default:
156        return false;
157    }
158}
159
160static bool validateSelector(const CSSSelector* selector)
161{
162    ASSERT(selector);
163
164    if (!validateSubSelector(selector))
165        return false;
166
167    const CSSSelector* prevSubSelector = selector;
168    const CSSSelector* subSelector = selector->tagHistory();
169
170    while (subSelector) {
171        if (prevSubSelector->relation() != CSSSelector::SubSelector)
172            return false;
173        if (!validateSubSelector(subSelector))
174            return false;
175
176        prevSubSelector = subSelector;
177        subSelector = subSelector->tagHistory();
178    }
179
180    return true;
181}
182
183bool HTMLContentElement::validateSelect() const
184{
185    ASSERT(!m_shouldParseSelectorList);
186
187    if (select().isNull() || select().isEmpty())
188        return true;
189
190    if (!m_selectorList.isValid())
191        return false;
192
193    for (const CSSSelector* selector = m_selectorList.first(); selector; selector = m_selectorList.next(selector)) {
194        if (!validateSelector(selector))
195            return false;
196    }
197
198    return true;
199}
200
201#endif // if ENABLE(SHADOW_DOM)
202
203}
204
205