1/*
2 * Copyright (C) 2008, 2012, 2013 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#include <wtf/text/StringBuilder.h>
32
33namespace WebCore {
34
35CSSSelectorList::~CSSSelectorList()
36{
37    deleteSelectors();
38}
39
40CSSSelectorList::CSSSelectorList(const CSSSelectorList& other)
41{
42    unsigned otherComponentCount = other.componentCount();
43    ASSERT_WITH_SECURITY_IMPLICATION(otherComponentCount);
44    m_selectorArray = reinterpret_cast<CSSSelector*>(fastMalloc(sizeof(CSSSelector) * otherComponentCount));
45    for (unsigned i = 0; i < otherComponentCount; ++i)
46        new (NotNull, &m_selectorArray[i]) CSSSelector(other.m_selectorArray[i]);
47}
48
49void CSSSelectorList::adopt(CSSSelectorList& list)
50{
51    deleteSelectors();
52    m_selectorArray = list.m_selectorArray;
53    list.m_selectorArray = 0;
54}
55
56void CSSSelectorList::adoptSelectorVector(Vector<OwnPtr<CSSParserSelector> >& selectorVector)
57{
58    ASSERT_WITH_SECURITY_IMPLICATION(!selectorVector.isEmpty());
59
60    deleteSelectors();
61    size_t flattenedSize = 0;
62    for (size_t i = 0; i < selectorVector.size(); ++i) {
63        for (CSSParserSelector* selector = selectorVector[i].get(); selector; selector = selector->tagHistory())
64            ++flattenedSize;
65    }
66    ASSERT(flattenedSize);
67    m_selectorArray = reinterpret_cast<CSSSelector*>(fastMalloc(sizeof(CSSSelector) * flattenedSize));
68    size_t arrayIndex = 0;
69    for (size_t i = 0; i < selectorVector.size(); ++i) {
70        CSSParserSelector* current = selectorVector[i].get();
71        while (current) {
72            {
73                // Move item from the parser selector vector into m_selectorArray without invoking destructor (Ugh.)
74                CSSSelector* currentSelector = current->releaseSelector().leakPtr();
75                memcpy(&m_selectorArray[arrayIndex], currentSelector, sizeof(CSSSelector));
76                fastDeleteSkippingDestructor(currentSelector);
77            }
78            current = current->tagHistory();
79            ASSERT(!m_selectorArray[arrayIndex].isLastInSelectorList());
80            if (current)
81                m_selectorArray[arrayIndex].setNotLastInTagHistory();
82            ++arrayIndex;
83        }
84        ASSERT(m_selectorArray[arrayIndex - 1].isLastInTagHistory());
85    }
86    ASSERT(flattenedSize == arrayIndex);
87    m_selectorArray[arrayIndex - 1].setLastInSelectorList();
88    selectorVector.clear();
89}
90
91unsigned CSSSelectorList::componentCount() const
92{
93    if (!m_selectorArray)
94        return 0;
95    CSSSelector* current = m_selectorArray;
96    while (!current->isLastInSelectorList())
97        ++current;
98    return (current - m_selectorArray) + 1;
99}
100
101void CSSSelectorList::deleteSelectors()
102{
103    if (!m_selectorArray)
104        return;
105
106    for (CSSSelector* s = m_selectorArray; ; ++s) {
107        s->~CSSSelector();
108        if (s->isLastInSelectorList())
109            break;
110    }
111    fastFree(m_selectorArray);
112}
113
114String CSSSelectorList::selectorsText() const
115{
116    StringBuilder result;
117
118    for (const CSSSelector* s = first(); s; s = next(s)) {
119        if (s != first())
120            result.append(", ");
121        result.append(s->selectorText());
122    }
123
124    return result.toString();
125}
126
127template <typename Functor>
128static bool forEachTagSelector(Functor& functor, const CSSSelector* selector)
129{
130    ASSERT(selector);
131
132    do {
133        if (functor(selector))
134            return true;
135        if (const CSSSelectorList* selectorList = selector->selectorList()) {
136            for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) {
137                if (forEachTagSelector(functor, subSelector))
138                    return true;
139            }
140        }
141    } while ((selector = selector->tagHistory()));
142
143    return false;
144}
145
146template <typename Functor>
147static bool forEachSelector(Functor& functor, const CSSSelectorList* selectorList)
148{
149    for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(selector)) {
150        if (forEachTagSelector(functor, selector))
151            return true;
152    }
153
154    return false;
155}
156
157class SelectorNeedsNamespaceResolutionFunctor {
158public:
159    bool operator()(const CSSSelector* selector)
160    {
161        if (selector->m_match == CSSSelector::Tag && selector->tagQName().prefix() != nullAtom && selector->tagQName().prefix() != starAtom)
162            return true;
163        if (selector->isAttributeSelector() && selector->attribute().prefix() != nullAtom && selector->attribute().prefix() != starAtom)
164            return true;
165        return false;
166    }
167};
168
169bool CSSSelectorList::selectorsNeedNamespaceResolution()
170{
171    SelectorNeedsNamespaceResolutionFunctor functor;
172    return forEachSelector(functor, this);
173}
174
175class SelectorHasInvalidSelectorFunctor {
176public:
177    bool operator()(const CSSSelector* selector)
178    {
179        return selector->isUnknownPseudoElement() || selector->isCustomPseudoElement();
180    }
181};
182
183bool CSSSelectorList::hasInvalidSelector() const
184{
185    SelectorHasInvalidSelectorFunctor functor;
186    return forEachSelector(functor, this);
187}
188
189} // namespace WebCore
190