1/*
2 * Copyright (C) 2008, 2012 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * 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 "core/css/CSSSelectorList.h"
29
30#include "core/css/parser/CSSParserValues.h"
31#include "wtf/text/StringBuilder.h"
32
33namespace blink {
34
35CSSSelectorList::~CSSSelectorList()
36{
37    deleteSelectors();
38}
39
40CSSSelectorList::CSSSelectorList(const CSSSelectorList& other)
41{
42    unsigned otherLength = other.length();
43    m_selectorArray = reinterpret_cast<CSSSelector*>(fastMalloc(sizeof(CSSSelector) * otherLength));
44    for (unsigned i = 0; i < otherLength; ++i)
45        new (&m_selectorArray[i]) CSSSelector(other.m_selectorArray[i]);
46}
47
48void CSSSelectorList::adopt(CSSSelectorList& list)
49{
50    deleteSelectors();
51    m_selectorArray = list.m_selectorArray;
52    list.m_selectorArray = 0;
53}
54
55void CSSSelectorList::adoptSelectorVector(Vector<OwnPtr<CSSParserSelector> >& selectorVector)
56{
57    deleteSelectors();
58    size_t flattenedSize = 0;
59    for (size_t i = 0; i < selectorVector.size(); ++i) {
60        for (CSSParserSelector* selector = selectorVector[i].get(); selector; selector = selector->tagHistory())
61            ++flattenedSize;
62    }
63    ASSERT(flattenedSize);
64    m_selectorArray = reinterpret_cast<CSSSelector*>(fastMalloc(sizeof(CSSSelector) * flattenedSize));
65    size_t arrayIndex = 0;
66    for (size_t i = 0; i < selectorVector.size(); ++i) {
67        CSSParserSelector* current = selectorVector[i].get();
68        while (current) {
69            // Move item from the parser selector vector into m_selectorArray without invoking destructor (Ugh.)
70            CSSSelector* currentSelector = current->releaseSelector().leakPtr();
71            memcpy(&m_selectorArray[arrayIndex], currentSelector, sizeof(CSSSelector));
72            fastFree(currentSelector);
73
74            current = current->tagHistory();
75            ASSERT(!m_selectorArray[arrayIndex].isLastInSelectorList());
76            if (current)
77                m_selectorArray[arrayIndex].setNotLastInTagHistory();
78            ++arrayIndex;
79        }
80        ASSERT(m_selectorArray[arrayIndex - 1].isLastInTagHistory());
81    }
82    ASSERT(flattenedSize == arrayIndex);
83    m_selectorArray[arrayIndex - 1].setLastInSelectorList();
84    selectorVector.clear();
85}
86
87unsigned CSSSelectorList::length() const
88{
89    if (!m_selectorArray)
90        return 0;
91    CSSSelector* current = m_selectorArray;
92    while (!current->isLastInSelectorList())
93        ++current;
94    return (current - m_selectorArray) + 1;
95}
96
97void CSSSelectorList::deleteSelectors()
98{
99    if (!m_selectorArray)
100        return;
101
102    bool finished = false;
103    for (CSSSelector* s = m_selectorArray; !finished; ++s) {
104        finished = s->isLastInSelectorList();
105        s->~CSSSelector();
106    }
107
108    fastFree(m_selectorArray);
109}
110
111String CSSSelectorList::selectorsText() const
112{
113    StringBuilder result;
114
115    for (const CSSSelector* s = first(); s; s = next(*s)) {
116        if (s != first())
117            result.appendLiteral(", ");
118        result.append(s->selectorText());
119    }
120
121    return result.toString();
122}
123
124template <typename Functor>
125static bool forEachTagSelector(Functor& functor, const CSSSelector& selector)
126{
127    for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
128        if (functor(*current))
129            return true;
130        if (const CSSSelectorList* selectorList = current->selectorList()) {
131            for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(*subSelector)) {
132                if (forEachTagSelector(functor, *subSelector))
133                    return true;
134            }
135        }
136    }
137
138    return false;
139}
140
141template <typename Functor>
142static bool forEachSelector(Functor& functor, const CSSSelectorList* selectorList)
143{
144    for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector)) {
145        if (forEachTagSelector(functor, *selector))
146            return true;
147    }
148
149    return false;
150}
151
152class SelectorNeedsNamespaceResolutionFunctor {
153public:
154    bool operator()(const CSSSelector& selector)
155    {
156        if (selector.match() == CSSSelector::Tag && selector.tagQName().prefix() != nullAtom && selector.tagQName().prefix() != starAtom)
157            return true;
158        if (selector.isAttributeSelector() && selector.attribute().prefix() != nullAtom && selector.attribute().prefix() != starAtom)
159            return true;
160        return false;
161    }
162};
163
164bool CSSSelectorList::selectorsNeedNamespaceResolution()
165{
166    SelectorNeedsNamespaceResolutionFunctor functor;
167    return forEachSelector(functor, this);
168}
169
170class SelectorHasShadowDistributed {
171public:
172    bool operator()(const CSSSelector& selector)
173    {
174        return selector.relationIsAffectedByPseudoContent();
175    }
176};
177
178bool CSSSelectorList::hasShadowDistributedAt(size_t index) const
179{
180    SelectorHasShadowDistributed functor;
181    return forEachTagSelector(functor, selectorAt(index));
182}
183
184class SelectorCrossesTreeScopes {
185public:
186    bool operator()(const CSSSelector& selector)
187    {
188        return selector.relation() == CSSSelector::ShadowDeep || selector.isShadowPseudoElement();
189    }
190};
191
192bool CSSSelectorList::selectorCrossesTreeScopes(size_t index) const
193{
194    SelectorCrossesTreeScopes functor;
195    return forEachTagSelector(functor, selectorAt(index));
196}
197
198} // namespace blink
199