8a0349c0ab89e201247d3d6034e920f5deebc4fa
[WebKit-https.git] / Source / WebCore / html / parser / HTMLToken.h
1 /*
2  * Copyright (C) 2013 Google, Inc. All Rights Reserved.
3  * Copyright (C) 2015 Apple 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 #ifndef HTMLToken_h
28 #define HTMLToken_h
29
30 #include "Attribute.h"
31
32 namespace WebCore {
33
34 struct DoctypeData {
35     bool hasPublicIdentifier { false };
36     bool hasSystemIdentifier { false };
37     Vector<UChar> publicIdentifier;
38     Vector<UChar> systemIdentifier;
39     bool forceQuirks { false };
40 };
41
42 class HTMLToken {
43     WTF_MAKE_FAST_ALLOCATED;
44 public:
45     enum Type {
46         Uninitialized,
47         DOCTYPE,
48         StartTag,
49         EndTag,
50         Comment,
51         Character,
52         EndOfFile,
53     };
54
55     struct Attribute {
56         struct Range {
57             unsigned start;
58             unsigned end;
59         };
60
61         Range nameRange;
62         Range valueRange;
63         Vector<UChar, 32> name;
64         Vector<UChar, 32> value;
65     };
66
67     typedef Vector<Attribute, 10> AttributeList;
68     typedef Vector<UChar, 256> DataVector;
69
70     HTMLToken();
71
72     void clear();
73
74     Type type() const;
75
76     // Used by HTMLSourceTracker.
77     void setBaseOffset(unsigned); // Base for attribute offsets, and the end of token offset.
78     void setEndOffset(unsigned);
79     unsigned length() const;
80
81     // EndOfFile
82
83     void makeEndOfFile();
84
85     // StartTag, EndTag, DOCTYPE.
86
87     const DataVector& name() const;
88
89     void appendToName(UChar);
90
91     // DOCTYPE.
92
93     void beginDOCTYPE();
94     void beginDOCTYPE(UChar);
95
96     void setForceQuirks();
97
98     void setPublicIdentifierToEmptyString();
99     void setSystemIdentifierToEmptyString();
100
101     void appendToPublicIdentifier(UChar);
102     void appendToSystemIdentifier(UChar);
103
104     std::unique_ptr<DoctypeData> releaseDoctypeData();
105
106     // StartTag, EndTag.
107
108     bool selfClosing() const;
109     const AttributeList& attributes() const;
110
111     void beginStartTag(UChar);
112
113     void beginEndTag(LChar);
114     void beginEndTag(const Vector<LChar, 32>&);
115
116     void addNewAttribute();
117
118     void beginAttributeName(unsigned offset);
119     void appendToAttributeName(UChar);
120     void endAttributeName(unsigned offset);
121
122     void beginAttributeValue(unsigned offset);
123     void appendToAttributeValue(UChar);
124     void endAttributeValue(unsigned offset);
125
126     void setSelfClosing();
127
128 public:
129     // Used by the XSSAuditor to nuke XSS-laden attributes.
130     void eraseValueOfAttribute(unsigned index);
131     void appendToAttributeValue(unsigned index, StringView value);
132
133     // Character.
134
135     // Starting a character token works slightly differently than starting
136     // other types of tokens because we want to save a per-character branch.
137     // There is no beginCharacters, and appending a character sets the type.
138
139     const DataVector& characters() const;
140     bool charactersIsAll8BitData() const;
141
142     void appendToCharacter(LChar);
143     void appendToCharacter(UChar);
144     void appendToCharacter(const Vector<LChar, 32>&);
145
146     // Comment.
147
148     const DataVector& comment() const;
149     bool commentIsAll8BitData() const;
150
151     void beginComment();
152     void appendToComment(UChar);
153
154 private:
155     Type m_type;
156
157     unsigned m_baseOffset;
158     unsigned m_length;
159
160     DataVector m_data;
161     UChar m_data8BitCheck;
162
163     // For StartTag and EndTag
164     bool m_selfClosing;
165     AttributeList m_attributes;
166     Attribute* m_currentAttribute;
167
168     // For DOCTYPE
169     std::unique_ptr<DoctypeData> m_doctypeData;
170 };
171
172 const HTMLToken::Attribute* findAttribute(const Vector<HTMLToken::Attribute>&, StringView name);
173
174 inline HTMLToken::HTMLToken()
175 {
176     clear();
177 }
178
179 inline void HTMLToken::clear()
180 {
181     m_type = Uninitialized;
182     m_data.clear();
183     m_data8BitCheck = 0;
184
185     m_length = 0;
186     m_baseOffset = 0;
187 }
188
189 inline HTMLToken::Type HTMLToken::type() const
190 {
191     return m_type;
192 }
193
194 inline void HTMLToken::makeEndOfFile()
195 {
196     ASSERT(m_type == Uninitialized);
197     m_type = EndOfFile;
198 }
199
200 inline unsigned HTMLToken::length() const
201 {
202     return m_length;
203 }
204
205 inline void HTMLToken::setBaseOffset(unsigned offset)
206 {
207     m_baseOffset = offset;
208 }
209
210 inline void HTMLToken::setEndOffset(unsigned endOffset)
211 {
212     m_length = endOffset - m_baseOffset;
213 }
214
215 inline const HTMLToken::DataVector& HTMLToken::name() const
216 {
217     ASSERT(m_type == StartTag || m_type == EndTag || m_type == DOCTYPE);
218     return m_data;
219 }
220
221 inline void HTMLToken::appendToName(UChar character)
222 {
223     ASSERT(m_type == StartTag || m_type == EndTag || m_type == DOCTYPE);
224     ASSERT(character);
225     m_data.append(character);
226     m_data8BitCheck |= character;
227 }
228
229 inline void HTMLToken::setForceQuirks()
230 {
231     ASSERT(m_type == DOCTYPE);
232     m_doctypeData->forceQuirks = true;
233 }
234
235 inline void HTMLToken::beginDOCTYPE()
236 {
237     ASSERT(m_type == Uninitialized);
238     m_type = DOCTYPE;
239     m_doctypeData = std::make_unique<DoctypeData>();
240 }
241
242 inline void HTMLToken::beginDOCTYPE(UChar character)
243 {
244     ASSERT(character);
245     beginDOCTYPE();
246     m_data.append(character);
247     m_data8BitCheck |= character;
248 }
249
250 inline void HTMLToken::setPublicIdentifierToEmptyString()
251 {
252     ASSERT(m_type == DOCTYPE);
253     m_doctypeData->hasPublicIdentifier = true;
254     m_doctypeData->publicIdentifier.clear();
255 }
256
257 inline void HTMLToken::setSystemIdentifierToEmptyString()
258 {
259     ASSERT(m_type == DOCTYPE);
260     m_doctypeData->hasSystemIdentifier = true;
261     m_doctypeData->systemIdentifier.clear();
262 }
263
264 inline void HTMLToken::appendToPublicIdentifier(UChar character)
265 {
266     ASSERT(character);
267     ASSERT(m_type == DOCTYPE);
268     ASSERT(m_doctypeData->hasPublicIdentifier);
269     m_doctypeData->publicIdentifier.append(character);
270 }
271
272 inline void HTMLToken::appendToSystemIdentifier(UChar character)
273 {
274     ASSERT(character);
275     ASSERT(m_type == DOCTYPE);
276     ASSERT(m_doctypeData->hasSystemIdentifier);
277     m_doctypeData->systemIdentifier.append(character);
278 }
279
280 inline std::unique_ptr<DoctypeData> HTMLToken::releaseDoctypeData()
281 {
282     return WTF::move(m_doctypeData);
283 }
284
285 inline bool HTMLToken::selfClosing() const
286 {
287     ASSERT(m_type == StartTag || m_type == EndTag);
288     return m_selfClosing;
289 }
290
291 inline void HTMLToken::setSelfClosing()
292 {
293     ASSERT(m_type == StartTag || m_type == EndTag);
294     m_selfClosing = true;
295 }
296
297 inline void HTMLToken::beginStartTag(UChar character)
298 {
299     ASSERT(character);
300     ASSERT(m_type == Uninitialized);
301     m_type = StartTag;
302     m_selfClosing = false;
303     m_currentAttribute = nullptr;
304     m_attributes.clear();
305
306     m_data.append(character);
307     m_data8BitCheck = character;
308 }
309
310 inline void HTMLToken::beginEndTag(LChar character)
311 {
312     ASSERT(m_type == Uninitialized);
313     m_type = EndTag;
314     m_selfClosing = false;
315     m_currentAttribute = nullptr;
316     m_attributes.clear();
317
318     m_data.append(character);
319 }
320
321 inline void HTMLToken::beginEndTag(const Vector<LChar, 32>& characters)
322 {
323     ASSERT(m_type == Uninitialized);
324     m_type = EndTag;
325     m_selfClosing = false;
326     m_currentAttribute = nullptr;
327     m_attributes.clear();
328
329     m_data.appendVector(characters);
330 }
331
332 inline void HTMLToken::addNewAttribute()
333 {
334     ASSERT(m_type == StartTag || m_type == EndTag);
335     m_attributes.grow(m_attributes.size() + 1);
336     m_currentAttribute = &m_attributes.last();
337
338 #if !ASSERT_DISABLED
339     m_currentAttribute->nameRange.start = 0;
340     m_currentAttribute->nameRange.end = 0;
341     m_currentAttribute->valueRange.start = 0;
342     m_currentAttribute->valueRange.end = 0;
343 #endif
344 }
345
346 inline void HTMLToken::beginAttributeName(unsigned offset)
347 {
348     ASSERT(offset);
349     ASSERT(!m_currentAttribute->nameRange.start);
350     m_currentAttribute->nameRange.start = offset - m_baseOffset;
351 }
352
353 inline void HTMLToken::endAttributeName(unsigned offset)
354 {
355     ASSERT(offset);
356     ASSERT(m_currentAttribute->nameRange.start);
357     ASSERT(!m_currentAttribute->nameRange.end);
358
359     unsigned adjustedOffset = offset - m_baseOffset;
360     m_currentAttribute->nameRange.end = adjustedOffset;
361
362     // FIXME: Is this intentional? Why point the value at the end of the name?
363     m_currentAttribute->valueRange.start = adjustedOffset;
364     m_currentAttribute->valueRange.end = adjustedOffset;
365 }
366
367 inline void HTMLToken::beginAttributeValue(unsigned offset)
368 {
369     ASSERT(offset);
370     m_currentAttribute->valueRange.start = offset - m_baseOffset;
371 }
372
373 inline void HTMLToken::endAttributeValue(unsigned offset)
374 {
375     ASSERT(offset);
376     m_currentAttribute->valueRange.end = offset - m_baseOffset;
377 }
378
379 inline void HTMLToken::appendToAttributeName(UChar character)
380 {
381     ASSERT(character);
382     ASSERT(m_type == StartTag || m_type == EndTag);
383     ASSERT(m_currentAttribute->nameRange.start);
384     m_currentAttribute->name.append(character);
385 }
386
387 inline void HTMLToken::appendToAttributeValue(UChar character)
388 {
389     ASSERT(character);
390     ASSERT(m_type == StartTag || m_type == EndTag);
391     ASSERT(m_currentAttribute->valueRange.start);
392     m_currentAttribute->value.append(character);
393 }
394
395 inline void HTMLToken::appendToAttributeValue(unsigned i, StringView value)
396 {
397     ASSERT(!value.isEmpty());
398     ASSERT(m_type == StartTag || m_type == EndTag);
399     append(m_attributes[i].value, value);
400 }
401
402 inline const HTMLToken::AttributeList& HTMLToken::attributes() const
403 {
404     ASSERT(m_type == StartTag || m_type == EndTag);
405     return m_attributes;
406 }
407
408 // Used by the XSSAuditor to nuke XSS-laden attributes.
409 inline void HTMLToken::eraseValueOfAttribute(unsigned i)
410 {
411     ASSERT(m_type == StartTag || m_type == EndTag);
412     ASSERT(i < m_attributes.size());
413     m_attributes[i].value.clear();
414 }
415
416 inline const HTMLToken::DataVector& HTMLToken::characters() const
417 {
418     ASSERT(m_type == Character);
419     return m_data;
420 }
421
422 inline bool HTMLToken::charactersIsAll8BitData() const
423 {
424     ASSERT(m_type == Character);
425     return m_data8BitCheck <= 0xFF;
426 }
427
428 inline void HTMLToken::appendToCharacter(LChar character)
429 {
430     ASSERT(m_type == Uninitialized || m_type == Character);
431     m_type = Character;
432     m_data.append(character);
433 }
434
435 inline void HTMLToken::appendToCharacter(UChar character)
436 {
437     ASSERT(m_type == Uninitialized || m_type == Character);
438     m_type = Character;
439     m_data.append(character);
440     m_data8BitCheck |= character;
441 }
442
443 inline void HTMLToken::appendToCharacter(const Vector<LChar, 32>& characters)
444 {
445     ASSERT(m_type == Uninitialized || m_type == Character);
446     m_type = Character;
447     m_data.appendVector(characters);
448 }
449
450 inline const HTMLToken::DataVector& HTMLToken::comment() const
451 {
452     ASSERT(m_type == Comment);
453     return m_data;
454 }
455
456 inline bool HTMLToken::commentIsAll8BitData() const
457 {
458     ASSERT(m_type == Comment);
459     return m_data8BitCheck <= 0xFF;
460 }
461
462 inline void HTMLToken::beginComment()
463 {
464     ASSERT(m_type == Uninitialized);
465     m_type = Comment;
466 }
467
468 inline void HTMLToken::appendToComment(UChar character)
469 {
470     ASSERT(character);
471     ASSERT(m_type == Comment);
472     m_data.append(character);
473     m_data8BitCheck |= character;
474 }
475
476 inline bool nameMatches(const HTMLToken::Attribute& attribute, StringView name)
477 {
478     unsigned size = name.length();
479     if (attribute.name.size() != size)
480         return false;
481     for (unsigned i = 0; i < size; ++i) {
482         // FIXME: The one caller that uses this probably wants to ignore letter case.
483         if (attribute.name[i] != name[i])
484             return false;
485     }
486     return true;
487 }
488
489 inline const HTMLToken::Attribute* findAttribute(const HTMLToken::AttributeList& attributes, StringView name)
490 {
491     for (auto& attribute : attributes) {
492         if (nameMatches(attribute, name))
493             return &attribute;
494     }
495     return nullptr;
496 }
497
498 }
499
500 #endif