1/*
2 * Copyright (C) 2008 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 "CSSSelectorList.h"
29
30#include "CSSParserValues.h"
31
32namespace WebCore {
33
34static CSSSelector* const freedSelectorArrayMarker = reinterpret_cast<CSSSelector*>(0xbbadbeef);
35
36CSSSelectorList::~CSSSelectorList()
37{
38    deleteSelectors();
39}
40
41void CSSSelectorList::adopt(CSSSelectorList& list)
42{
43    deleteSelectors();
44    m_selectorArray = list.m_selectorArray;
45    list.m_selectorArray = 0;
46}
47
48void CSSSelectorList::adoptSelectorVector(Vector<OwnPtr<CSSParserSelector> >& selectorVector)
49{
50    deleteSelectors();
51    const size_t vectorSize = selectorVector.size();
52    size_t flattenedSize = 0;
53    for (size_t i = 0; i < vectorSize; ++i) {
54        for (CSSParserSelector* selector = selectorVector[i].get(); selector; selector = selector->tagHistory())
55            ++flattenedSize;
56    }
57    ASSERT(flattenedSize);
58    if (flattenedSize == 1) {
59        m_selectorArray = selectorVector[0]->releaseSelector().leakPtr();
60        m_selectorArray->setLastInSelectorList();
61        ASSERT(m_selectorArray->isLastInTagHistory());
62        selectorVector.shrink(0);
63        return;
64    }
65    m_selectorArray = reinterpret_cast<CSSSelector*>(fastMalloc(sizeof(CSSSelector) * flattenedSize));
66    size_t arrayIndex = 0;
67    for (size_t i = 0; i < vectorSize; ++i) {
68        CSSParserSelector* current = selectorVector[i].get();
69        while (current) {
70            OwnPtr<CSSSelector> selector = current->releaseSelector();
71            current = current->tagHistory();
72            move(selector.release(), &m_selectorArray[arrayIndex]);
73            ASSERT(!m_selectorArray[arrayIndex].isLastInSelectorList());
74            if (current)
75                m_selectorArray[arrayIndex].setNotLastInTagHistory();
76            ++arrayIndex;
77        }
78        ASSERT(m_selectorArray[arrayIndex - 1].isLastInTagHistory());
79    }
80    ASSERT(flattenedSize == arrayIndex);
81    m_selectorArray[arrayIndex - 1].setLastInSelectorList();
82    selectorVector.shrink(0);
83}
84
85void CSSSelectorList::deleteSelectors()
86{
87    if (!m_selectorArray)
88        return;
89
90    // FIXME: Remove once http://webkit.org/b/56124 is fixed.
91    if (m_selectorArray == freedSelectorArrayMarker)
92        CRASH();
93
94    // We had two cases in adoptSelectVector. The fast case of a 1 element
95    // vector took the CSSSelector directly, which was allocated with new.
96    // The second case we allocated a new fastMalloc buffer, which should be
97    // freed with fastFree, and the destructors called manually.
98    CSSSelector* s = m_selectorArray;
99    bool done = s->isLastInSelectorList();
100    if (done)
101        delete s;
102    else {
103        while (1) {
104            s->~CSSSelector();
105            if (done)
106                break;
107            ++s;
108            done = s->isLastInSelectorList();
109        }
110        fastFree(m_selectorArray);
111    }
112
113    m_selectorArray = freedSelectorArrayMarker;
114}
115
116
117template <typename Functor>
118static bool forEachTagSelector(Functor& functor, CSSSelector* selector)
119{
120    ASSERT(selector);
121
122    do {
123        if (functor(selector))
124            return true;
125        if (CSSSelectorList* selectorList = selector->selectorList()) {
126            for (CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) {
127                if (forEachTagSelector(functor, subSelector))
128                    return true;
129            }
130        }
131    } while ((selector = selector->tagHistory()));
132
133    return false;
134}
135
136template <typename Functor>
137static bool forEachSelector(Functor& functor, const CSSSelectorList* selectorList)
138{
139    for (CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(selector)) {
140        if (forEachTagSelector(functor, selector))
141            return true;
142    }
143
144    return false;
145}
146
147class SelectorNeedsNamespaceResolutionFunctor {
148public:
149    bool operator()(CSSSelector* selector)
150    {
151        if (selector->hasTag() && selector->tag().prefix() != nullAtom && selector->tag().prefix() != starAtom)
152            return true;
153        if (selector->hasAttribute() && selector->attribute().prefix() != nullAtom && selector->attribute().prefix() != starAtom)
154            return true;
155        return false;
156    }
157};
158
159bool CSSSelectorList::selectorsNeedNamespaceResolution()
160{
161    SelectorNeedsNamespaceResolutionFunctor functor;
162    return forEachSelector(functor, this);
163}
164
165class SelectorHasUnknownPseudoElementFunctor {
166public:
167    bool operator()(CSSSelector* selector)
168    {
169        return selector->isUnknownPseudoElement();
170    }
171};
172
173bool CSSSelectorList::hasUnknownPseudoElements() const
174{
175    SelectorHasUnknownPseudoElementFunctor functor;
176    return forEachSelector(functor, this);
177}
178
179
180
181} // namespace WebCore
182