1/*
2 * Copyright (C) 2013 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#ifndef AtomicHTMLToken_h
27#define AtomicHTMLToken_h
28
29#include "core/HTMLElementLookupTrie.h"
30#include "core/dom/Attribute.h"
31#include "core/html/parser/CompactHTMLToken.h"
32#include "core/html/parser/HTMLToken.h"
33#include "wtf/RefCounted.h"
34#include "wtf/RefPtr.h"
35
36namespace blink {
37
38class AtomicHTMLToken {
39    WTF_MAKE_NONCOPYABLE(AtomicHTMLToken);
40public:
41
42    bool forceQuirks() const
43    {
44        ASSERT(m_type == HTMLToken::DOCTYPE);
45        return m_doctypeData->m_forceQuirks;
46    }
47
48    HTMLToken::Type type() const { return m_type; }
49
50    const AtomicString& name() const
51    {
52        ASSERT(usesName());
53        return m_name;
54    }
55
56    void setName(const AtomicString& name)
57    {
58        ASSERT(usesName());
59        m_name = name;
60    }
61
62    bool selfClosing() const
63    {
64        ASSERT(m_type == HTMLToken::StartTag || m_type == HTMLToken::EndTag);
65        return m_selfClosing;
66    }
67
68    Attribute* getAttributeItem(const QualifiedName& attributeName)
69    {
70        ASSERT(usesAttributes());
71        return findAttributeInVector(m_attributes, attributeName);
72    }
73
74    Vector<Attribute>& attributes()
75    {
76        ASSERT(usesAttributes());
77        return m_attributes;
78    }
79
80    const Vector<Attribute>& attributes() const
81    {
82        ASSERT(usesAttributes());
83        return m_attributes;
84    }
85
86    const String& characters() const
87    {
88        ASSERT(m_type == HTMLToken::Character);
89        return m_data;
90    }
91
92    const String& comment() const
93    {
94        ASSERT(m_type == HTMLToken::Comment);
95        return m_data;
96    }
97
98    // FIXME: Distinguish between a missing public identifer and an empty one.
99    Vector<UChar>& publicIdentifier() const
100    {
101        ASSERT(m_type == HTMLToken::DOCTYPE);
102        return m_doctypeData->m_publicIdentifier;
103    }
104
105    // FIXME: Distinguish between a missing system identifer and an empty one.
106    Vector<UChar>& systemIdentifier() const
107    {
108        ASSERT(m_type == HTMLToken::DOCTYPE);
109        return m_doctypeData->m_systemIdentifier;
110    }
111
112    explicit AtomicHTMLToken(HTMLToken& token)
113        : m_type(token.type())
114    {
115        switch (m_type) {
116        case HTMLToken::Uninitialized:
117            ASSERT_NOT_REACHED();
118            break;
119        case HTMLToken::DOCTYPE:
120            m_name = AtomicString(token.name());
121            m_doctypeData = token.releaseDoctypeData();
122            break;
123        case HTMLToken::EndOfFile:
124            break;
125        case HTMLToken::StartTag:
126        case HTMLToken::EndTag: {
127            m_selfClosing = token.selfClosing();
128            if (StringImpl* tagName = lookupHTMLTag(token.name().data(), token.name().size()))
129                m_name = AtomicString(tagName);
130            else
131                m_name = AtomicString(token.name());
132            initializeAttributes(token.attributes());
133            break;
134        }
135        case HTMLToken::Character:
136        case HTMLToken::Comment:
137            if (token.isAll8BitData())
138                m_data = String::make8BitFrom16BitSource(token.data());
139            else
140                m_data = String(token.data());
141            break;
142        }
143    }
144
145    explicit AtomicHTMLToken(const CompactHTMLToken& token)
146        : m_type(token.type())
147    {
148        switch (m_type) {
149        case HTMLToken::Uninitialized:
150            ASSERT_NOT_REACHED();
151            break;
152        case HTMLToken::DOCTYPE:
153            m_name = AtomicString(token.data());
154            m_doctypeData = adoptPtr(new DoctypeData());
155            m_doctypeData->m_hasPublicIdentifier = true;
156            append(m_doctypeData->m_publicIdentifier, token.publicIdentifier());
157            m_doctypeData->m_hasSystemIdentifier = true;
158            append(m_doctypeData->m_systemIdentifier, token.systemIdentifier());
159            m_doctypeData->m_forceQuirks = token.doctypeForcesQuirks();
160            break;
161        case HTMLToken::EndOfFile:
162            break;
163        case HTMLToken::StartTag:
164            m_attributes.reserveInitialCapacity(token.attributes().size());
165            for (Vector<CompactHTMLToken::Attribute>::const_iterator it = token.attributes().begin(); it != token.attributes().end(); ++it) {
166                QualifiedName name(nullAtom, AtomicString(it->name), nullAtom);
167                // FIXME: This is N^2 for the number of attributes.
168                if (!findAttributeInVector(m_attributes, name))
169                    m_attributes.append(Attribute(name, AtomicString(it->value)));
170            }
171            // Fall through!
172        case HTMLToken::EndTag:
173            m_selfClosing = token.selfClosing();
174            m_name = AtomicString(token.data());
175            break;
176        case HTMLToken::Character:
177        case HTMLToken::Comment:
178            m_data = token.data();
179            break;
180        }
181    }
182
183    explicit AtomicHTMLToken(HTMLToken::Type type)
184        : m_type(type)
185        , m_selfClosing(false)
186    {
187    }
188
189    AtomicHTMLToken(HTMLToken::Type type, const AtomicString& name, const Vector<Attribute>& attributes = Vector<Attribute>())
190        : m_type(type)
191        , m_name(name)
192        , m_selfClosing(false)
193        , m_attributes(attributes)
194    {
195        ASSERT(usesName());
196    }
197
198private:
199    HTMLToken::Type m_type;
200
201    void initializeAttributes(const HTMLToken::AttributeList& attributes);
202    QualifiedName nameForAttribute(const HTMLToken::Attribute&) const;
203
204    bool usesName() const;
205
206    bool usesAttributes() const;
207
208    // "name" for DOCTYPE, StartTag, and EndTag
209    AtomicString m_name;
210
211    // "data" for Comment, "characters" for Character
212    String m_data;
213
214    // For DOCTYPE
215    OwnPtr<DoctypeData> m_doctypeData;
216
217    // For StartTag and EndTag
218    bool m_selfClosing;
219
220    Vector<Attribute> m_attributes;
221};
222
223inline void AtomicHTMLToken::initializeAttributes(const HTMLToken::AttributeList& attributes)
224{
225    size_t size = attributes.size();
226    if (!size)
227        return;
228
229    m_attributes.clear();
230    m_attributes.reserveInitialCapacity(size);
231    for (size_t i = 0; i < size; ++i) {
232        const HTMLToken::Attribute& attribute = attributes[i];
233        if (attribute.name.isEmpty())
234            continue;
235
236        ASSERT(attribute.nameRange.start);
237        ASSERT(attribute.nameRange.end);
238        ASSERT(attribute.valueRange.start);
239        ASSERT(attribute.valueRange.end);
240
241        AtomicString value(attribute.value);
242        const QualifiedName& name = nameForAttribute(attribute);
243        // FIXME: This is N^2 for the number of attributes.
244        if (!findAttributeInVector(m_attributes, name))
245            m_attributes.append(Attribute(name, value));
246    }
247}
248
249}
250
251#endif
252