lite-email-parser 2.0.4 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.cjs +29 -4
  3. package/dist/index.d.cts.map +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +27 -35
  6. package/package.json +15 -29
  7. package/binding.gyp +0 -47
  8. package/core/include/html_extractor.h +0 -100
  9. package/core/include/html_parser.h +0 -13
  10. package/core/src/html_extractor.cpp +0 -662
  11. package/core/src/html_parser.cpp +0 -221
  12. package/deps/gumbo-parser/src/attribute.c +0 -44
  13. package/deps/gumbo-parser/src/attribute.h +0 -37
  14. package/deps/gumbo-parser/src/char_ref.c +0 -301
  15. package/deps/gumbo-parser/src/char_ref.h +0 -70
  16. package/deps/gumbo-parser/src/char_ref_gperf.c +0 -11126
  17. package/deps/gumbo-parser/src/error.c +0 -287
  18. package/deps/gumbo-parser/src/error.h +0 -225
  19. package/deps/gumbo-parser/src/gumbo.h +0 -683
  20. package/deps/gumbo-parser/src/insertion_mode.h +0 -55
  21. package/deps/gumbo-parser/src/parser.c +0 -4433
  22. package/deps/gumbo-parser/src/parser.h +0 -57
  23. package/deps/gumbo-parser/src/string_buffer.c +0 -110
  24. package/deps/gumbo-parser/src/string_buffer.h +0 -84
  25. package/deps/gumbo-parser/src/string_piece.c +0 -48
  26. package/deps/gumbo-parser/src/string_piece.h +0 -38
  27. package/deps/gumbo-parser/src/tag.c +0 -125
  28. package/deps/gumbo-parser/src/tag_enum.h +0 -155
  29. package/deps/gumbo-parser/src/tag_gperf.h +0 -350
  30. package/deps/gumbo-parser/src/tag_sizes.h +0 -2
  31. package/deps/gumbo-parser/src/tag_strings.h +0 -155
  32. package/deps/gumbo-parser/src/token_type.h +0 -42
  33. package/deps/gumbo-parser/src/tokenizer.c +0 -3003
  34. package/deps/gumbo-parser/src/tokenizer.h +0 -123
  35. package/deps/gumbo-parser/src/tokenizer_states.h +0 -104
  36. package/deps/gumbo-parser/src/utf8.c +0 -270
  37. package/deps/gumbo-parser/src/utf8.h +0 -132
  38. package/deps/gumbo-parser/src/util.c +0 -58
  39. package/deps/gumbo-parser/src/util.h +0 -60
  40. package/deps/gumbo-parser/src/vector.c +0 -128
  41. package/deps/gumbo-parser/src/vector.h +0 -70
  42. package/dist/cjs/index.cjs +0 -63
  43. package/dist/cjs/index.d.cts +0 -9
  44. package/dist/cjs/index.d.cts.map +0 -1
  45. package/dist/cjs/index.d.ts +0 -9
  46. package/dist/cjs/index.d.ts.map +0 -1
  47. package/dist/cjs/index.js +0 -98
  48. package/dist/cjs/package.json +0 -1
  49. package/dist/cjs/parser.d.ts +0 -4
  50. package/dist/cjs/parser.d.ts.map +0 -1
  51. package/dist/cjs/parser.js +0 -118
  52. package/dist/cjs/types.d.ts +0 -14
  53. package/dist/cjs/types.d.ts.map +0 -1
  54. package/dist/cjs/types.js +0 -2
  55. package/dist/parser.d.ts +0 -4
  56. package/dist/parser.d.ts.map +0 -1
  57. package/dist/parser.js +0 -81
  58. package/src/addon.cpp +0 -141
@@ -1,3003 +0,0 @@
1
- // Copyright 2010 Google Inc. All Rights Reserved.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
- //
15
- // Author: jdtang@google.com (Jonathan Tang)
16
- //
17
- // Coding conventions specific to this file:
18
- //
19
- // 1. Functions that fill in a token should be named emit_*, and should be
20
- // followed immediately by a return from the tokenizer (true if no error
21
- // occurred, false if an error occurred). Sometimes the emit functions
22
- // themselves return a boolean so that they can be combined with the return
23
- // statement; in this case, they should match this convention.
24
- // 2. Functions that shuffle data from temporaries to final API structures
25
- // should be named finish_*, and be called just before the tokenizer exits the
26
- // state that accumulates the temporary.
27
- // 3. All internal data structures should be kept in an initialized state from
28
- // tokenizer creation onwards, ready to accept input. When a buffer's flushed
29
- // and reset, it should be deallocated and immediately reinitialized.
30
- // 4. Make sure there are appropriate break statements following each state.
31
- // 5. Assertions on the state of the temporary and tag buffers are usually a
32
- // good idea, and should go at the entry point of each state when added.
33
- // 6. Statement order within states goes:
34
- // 1. Add parse errors, if appropriate.
35
- // 2. Call finish_* functions to build up tag state.
36
- // 2. Switch to new state. Set _reconsume flag if appropriate.
37
- // 3. Perform any other temporary buffer manipulation.
38
- // 4. Emit tokens
39
- // 5. Return/break.
40
- // This order ensures that we can verify that every emit is followed by a
41
- // return, ensures that the correct state is recorded with any parse errors, and
42
- // prevents parse error position from being messed up by possible mark/resets in
43
- // temporary buffer manipulation.
44
-
45
- #include "tokenizer.h"
46
-
47
- #include <assert.h>
48
- #include <stdbool.h>
49
- #include <string.h>
50
-
51
- #include "attribute.h"
52
- #include "char_ref.h"
53
- #include "error.h"
54
- #include "gumbo.h"
55
- #include "parser.h"
56
- #include "string_buffer.h"
57
- #include "string_piece.h"
58
- #include "token_type.h"
59
- #include "tokenizer_states.h"
60
- #include "utf8.h"
61
- #include "util.h"
62
- #include "vector.h"
63
-
64
- // Compared against _script_data_buffer to determine if we're in double-escaped
65
- // script mode.
66
- const GumboStringPiece kScriptTag = {"script", 6};
67
-
68
- // An enum for the return value of each individual state.
69
- typedef enum {
70
- RETURN_ERROR, // Return false (error) from the tokenizer.
71
- RETURN_SUCCESS, // Return true (success) from the tokenizer.
72
- NEXT_CHAR // Proceed to the next character and continue lexing.
73
- } StateResult;
74
-
75
- // This is a struct containing state necessary to build up a tag token,
76
- // character by character.
77
- typedef struct GumboInternalTagState {
78
- // A buffer to accumulate characters for various GumboStringPiece fields.
79
- GumboStringBuffer _buffer;
80
-
81
- // A pointer to the start of the original text corresponding to the contents
82
- // of the buffer.
83
- const char* _original_text;
84
-
85
- // The current tag enum, computed once the tag name state has finished so that
86
- // the buffer can be re-used for building up attributes.
87
- GumboTag _tag;
88
-
89
- // The starting location of the text in the buffer.
90
- GumboSourcePosition _start_pos;
91
-
92
- // The current list of attributes. This is copied (and ownership of its data
93
- // transferred) to the GumboStartTag token upon completion of the tag. New
94
- // attributes are added as soon as their attribute name state is complete, and
95
- // values are filled in by operating on _attributes.data[attributes.length-1].
96
- GumboVector /* GumboAttribute */ _attributes;
97
-
98
- // If true, the next attribute value to be finished should be dropped. This
99
- // happens if a duplicate attribute name is encountered - we want to consume
100
- // the attribute value, but shouldn't overwrite the existing value.
101
- bool _drop_next_attr_value;
102
-
103
- // The state that caused the tokenizer to switch into a character reference in
104
- // attribute value state. This is used to set the additional allowed
105
- // character, and is switched back to on completion. Initialized as the
106
- // tokenizer enters the character reference state.
107
- GumboTokenizerEnum _attr_value_state;
108
-
109
- // The last start tag to have been emitted by the tokenizer. This is
110
- // necessary to check for appropriate end tags.
111
- GumboTag _last_start_tag;
112
-
113
- // If true, then this is a start tag. If false, it's an end tag. This is
114
- // necessary to generate the appropriate token type at tag-closing time.
115
- bool _is_start_tag;
116
-
117
- // If true, then this tag is "self-closing" and doesn't have an end tag.
118
- bool _is_self_closing;
119
- } GumboTagState;
120
-
121
- // This is the main tokenizer state struct, containing all state used by in
122
- // tokenizing the input stream.
123
- typedef struct GumboInternalTokenizerState {
124
- // The current lexer state. Starts in GUMBO_LEX_DATA.
125
- GumboTokenizerEnum _state;
126
-
127
- // A flag indicating whether the current input character needs to reconsumed
128
- // in another state, or whether the next input character should be read for
129
- // the next iteration of the state loop. This is set when the spec reads
130
- // "Reconsume the current input character in..."
131
- bool _reconsume_current_input;
132
-
133
- // A flag indicating whether the current node is a foreign element. This is
134
- // set by gumbo_tokenizer_set_is_current_node_foreign and checked in the
135
- // markup declaration state.
136
- bool _is_current_node_foreign;
137
-
138
- // A flag indicating whether the tokenizer is in a CDATA section. If so, then
139
- // text tokens emitted will be GUMBO_TOKEN_CDATA.
140
- bool _is_in_cdata;
141
-
142
- // Certain states (notably character references) may emit two character tokens
143
- // at once, but the contract for lex() fills in only one token at a time. The
144
- // extra character is buffered here, and then this is checked on entry to
145
- // lex(). If a character is stored here, it's immediately emitted and control
146
- // returns from the lexer. kGumboNoChar is used to represent 'no character
147
- // stored.'
148
- //
149
- // Note that characters emitted through this mechanism will have their source
150
- // position marked as the character under the mark, i.e. multiple characters
151
- // may be emitted with the same position. This is desirable for character
152
- // references, but unsuitable for many other cases. Use the _temporary_buffer
153
- // mechanism if the buffered characters must have their original positions in
154
- // the document.
155
- int _buffered_emit_char;
156
-
157
- // A temporary buffer to accumulate characters, as described by the "temporary
158
- // buffer" phrase in the tokenizer spec. We use this in a somewhat unorthodox
159
- // way: we record the specific character to go into the buffer, which may
160
- // sometimes be a lowercased version of the actual input character. However,
161
- // we *also* use utf8iterator_mark() to record the position at tag start.
162
- // When we start flushing the temporary buffer, we set _temporary_buffer_emit
163
- // to the start of it, and then increment it for each call to the tokenizer.
164
- // We also call utf8iterator_reset(), and utf8iterator_next() through the
165
- // input stream, so that tokens emitted by emit_char have the correct position
166
- // and original text.
167
- GumboStringBuffer _temporary_buffer;
168
-
169
- // The current cursor position we're emitting from within
170
- // _temporary_buffer.data. NULL whenever we're not flushing the buffer.
171
- const char* _temporary_buffer_emit;
172
-
173
- // The temporary buffer is also used by the spec to check whether we should
174
- // enter the script data double escaped state, but we can't use the same
175
- // buffer for both because we have to flush out "<s" as emits while still
176
- // maintaining the context that will eventually become "script". This is a
177
- // separate buffer that's used in place of the temporary buffer for states
178
- // that may enter the script data double escape start state.
179
- GumboStringBuffer _script_data_buffer;
180
-
181
- // Pointer to the beginning of the current token in the original buffer; used
182
- // to record the original text.
183
- const char* _token_start;
184
-
185
- // GumboSourcePosition recording the source location of the start of the
186
- // current token.
187
- GumboSourcePosition _token_start_pos;
188
-
189
- // Current tag state.
190
- GumboTagState _tag_state;
191
-
192
- // Doctype state. We use the temporary buffer to accumulate characters (it's
193
- // not used for anything else in the doctype states), and then freshly
194
- // allocate the strings in the doctype token, then copy it over on emit.
195
- GumboTokenDocType _doc_type_state;
196
-
197
- // The UTF8Iterator over the tokenizer input.
198
- Utf8Iterator _input;
199
- } GumboTokenizerState;
200
-
201
- // Adds an ERR_UNEXPECTED_CODE_POINT parse error to the parser's error struct.
202
- static void tokenizer_add_parse_error(
203
- GumboParser* parser, GumboErrorType type) {
204
- GumboError* error = gumbo_add_error(parser);
205
- if (!error) {
206
- return;
207
- }
208
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
209
- utf8iterator_get_position(&tokenizer->_input, &error->position);
210
- error->original_text = utf8iterator_get_char_pointer(&tokenizer->_input);
211
- error->type = type;
212
- error->v.tokenizer.codepoint = utf8iterator_current(&tokenizer->_input);
213
- switch (tokenizer->_state) {
214
- case GUMBO_LEX_DATA:
215
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_DATA;
216
- break;
217
- case GUMBO_LEX_CHAR_REF_IN_DATA:
218
- case GUMBO_LEX_CHAR_REF_IN_RCDATA:
219
- case GUMBO_LEX_CHAR_REF_IN_ATTR_VALUE:
220
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_CHAR_REF;
221
- break;
222
- case GUMBO_LEX_RCDATA:
223
- case GUMBO_LEX_RCDATA_LT:
224
- case GUMBO_LEX_RCDATA_END_TAG_OPEN:
225
- case GUMBO_LEX_RCDATA_END_TAG_NAME:
226
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_RCDATA;
227
- break;
228
- case GUMBO_LEX_RAWTEXT:
229
- case GUMBO_LEX_RAWTEXT_LT:
230
- case GUMBO_LEX_RAWTEXT_END_TAG_OPEN:
231
- case GUMBO_LEX_RAWTEXT_END_TAG_NAME:
232
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_RAWTEXT;
233
- break;
234
- case GUMBO_LEX_PLAINTEXT:
235
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_PLAINTEXT;
236
- break;
237
- case GUMBO_LEX_SCRIPT:
238
- case GUMBO_LEX_SCRIPT_LT:
239
- case GUMBO_LEX_SCRIPT_END_TAG_OPEN:
240
- case GUMBO_LEX_SCRIPT_END_TAG_NAME:
241
- case GUMBO_LEX_SCRIPT_ESCAPED_START:
242
- case GUMBO_LEX_SCRIPT_ESCAPED_START_DASH:
243
- case GUMBO_LEX_SCRIPT_ESCAPED:
244
- case GUMBO_LEX_SCRIPT_ESCAPED_DASH:
245
- case GUMBO_LEX_SCRIPT_ESCAPED_DASH_DASH:
246
- case GUMBO_LEX_SCRIPT_ESCAPED_LT:
247
- case GUMBO_LEX_SCRIPT_ESCAPED_END_TAG_OPEN:
248
- case GUMBO_LEX_SCRIPT_ESCAPED_END_TAG_NAME:
249
- case GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_START:
250
- case GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED:
251
- case GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_DASH:
252
- case GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_DASH_DASH:
253
- case GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_LT:
254
- case GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_END:
255
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_SCRIPT;
256
- break;
257
- case GUMBO_LEX_TAG_OPEN:
258
- case GUMBO_LEX_END_TAG_OPEN:
259
- case GUMBO_LEX_TAG_NAME:
260
- case GUMBO_LEX_BEFORE_ATTR_NAME:
261
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_TAG;
262
- break;
263
- case GUMBO_LEX_SELF_CLOSING_START_TAG:
264
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_SELF_CLOSING_TAG;
265
- break;
266
- case GUMBO_LEX_ATTR_NAME:
267
- case GUMBO_LEX_AFTER_ATTR_NAME:
268
- case GUMBO_LEX_BEFORE_ATTR_VALUE:
269
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_ATTR_NAME;
270
- break;
271
- case GUMBO_LEX_ATTR_VALUE_DOUBLE_QUOTED:
272
- case GUMBO_LEX_ATTR_VALUE_SINGLE_QUOTED:
273
- case GUMBO_LEX_ATTR_VALUE_UNQUOTED:
274
- case GUMBO_LEX_AFTER_ATTR_VALUE_QUOTED:
275
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_ATTR_VALUE;
276
- break;
277
- case GUMBO_LEX_BOGUS_COMMENT:
278
- case GUMBO_LEX_PROCESSING_INSTRUCTION:
279
- case GUMBO_LEX_COMMENT_START:
280
- case GUMBO_LEX_COMMENT_START_DASH:
281
- case GUMBO_LEX_COMMENT:
282
- case GUMBO_LEX_COMMENT_END_DASH:
283
- case GUMBO_LEX_COMMENT_END:
284
- case GUMBO_LEX_COMMENT_END_BANG:
285
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_COMMENT;
286
- break;
287
- case GUMBO_LEX_MARKUP_DECLARATION:
288
- case GUMBO_LEX_DOCTYPE:
289
- case GUMBO_LEX_BEFORE_DOCTYPE_NAME:
290
- case GUMBO_LEX_DOCTYPE_NAME:
291
- case GUMBO_LEX_AFTER_DOCTYPE_NAME:
292
- case GUMBO_LEX_AFTER_DOCTYPE_PUBLIC_KEYWORD:
293
- case GUMBO_LEX_BEFORE_DOCTYPE_PUBLIC_ID:
294
- case GUMBO_LEX_DOCTYPE_PUBLIC_ID_DOUBLE_QUOTED:
295
- case GUMBO_LEX_DOCTYPE_PUBLIC_ID_SINGLE_QUOTED:
296
- case GUMBO_LEX_AFTER_DOCTYPE_PUBLIC_ID:
297
- case GUMBO_LEX_BETWEEN_DOCTYPE_PUBLIC_SYSTEM_ID:
298
- case GUMBO_LEX_AFTER_DOCTYPE_SYSTEM_KEYWORD:
299
- case GUMBO_LEX_BEFORE_DOCTYPE_SYSTEM_ID:
300
- case GUMBO_LEX_DOCTYPE_SYSTEM_ID_DOUBLE_QUOTED:
301
- case GUMBO_LEX_DOCTYPE_SYSTEM_ID_SINGLE_QUOTED:
302
- case GUMBO_LEX_AFTER_DOCTYPE_SYSTEM_ID:
303
- case GUMBO_LEX_BOGUS_DOCTYPE:
304
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_DOCTYPE;
305
- break;
306
- case GUMBO_LEX_CDATA:
307
- error->v.tokenizer.state = GUMBO_ERR_TOKENIZER_CDATA;
308
- break;
309
- }
310
- }
311
-
312
- static bool is_alpha(int c) {
313
- // We don't use ISO C isupper/islower functions here because they
314
- // depend upon the program's locale, while the behavior of the HTML5 spec is
315
- // independent of which locale the program is run in.
316
- return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
317
- }
318
-
319
- static int ensure_lowercase(int c) {
320
- return c >= 'A' && c <= 'Z' ? c + 0x20 : c;
321
- }
322
-
323
- static GumboTokenType get_char_token_type(bool is_in_cdata, int c) {
324
- if (is_in_cdata && c > 0) {
325
- return GUMBO_TOKEN_CDATA;
326
- }
327
-
328
- switch (c) {
329
- case '\t':
330
- case '\n':
331
- case '\r':
332
- case '\f':
333
- case ' ':
334
- return GUMBO_TOKEN_WHITESPACE;
335
- case 0:
336
- gumbo_debug("Emitted null byte.\n");
337
- return GUMBO_TOKEN_NULL;
338
- case -1:
339
- return GUMBO_TOKEN_EOF;
340
- default:
341
- return GUMBO_TOKEN_CHARACTER;
342
- }
343
- }
344
-
345
- // Starts recording characters in the temporary buffer.
346
- // Because this needs to reset the utf8iterator_mark to the beginning of the
347
- // text that will eventually be emitted, it needs to be called a couple of
348
- // states before the spec says "Set the temporary buffer to the empty string".
349
- // In general, this should be called whenever there's a transition to a
350
- // "less-than sign state". The initial < and possibly / then need to be
351
- // appended to the temporary buffer, their presence needs to be accounted for in
352
- // states that compare the temporary buffer against a literal value, and
353
- // spec stanzas that say "emit a < and / character token along with a character
354
- // token for each character in the temporary buffer" need to be adjusted to
355
- // account for the presence of the < and / inside the temporary buffer.
356
- static void clear_temporary_buffer(GumboParser* parser) {
357
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
358
- assert(!tokenizer->_temporary_buffer_emit);
359
- utf8iterator_mark(&tokenizer->_input);
360
- gumbo_string_buffer_clear(parser, &tokenizer->_temporary_buffer);
361
- // The temporary buffer and script data buffer are the same object in the
362
- // spec, so the script data buffer should be cleared as well.
363
- gumbo_string_buffer_clear(parser, &tokenizer->_script_data_buffer);
364
- }
365
-
366
- // Appends a codepoint to the temporary buffer.
367
- static void append_char_to_temporary_buffer(
368
- GumboParser* parser, int codepoint) {
369
- gumbo_string_buffer_append_codepoint(
370
- parser, codepoint, &parser->_tokenizer_state->_temporary_buffer);
371
- }
372
-
373
- // Checks to see if the temporary buffer equals a certain string.
374
- // Make sure this remains side-effect free; it's used in assertions.
375
- #ifndef NDEBUG
376
- static bool temporary_buffer_equals(GumboParser* parser, const char* text) {
377
- GumboStringBuffer* buffer = &parser->_tokenizer_state->_temporary_buffer;
378
- // TODO(jdtang): See if the extra strlen is a performance problem, and replace
379
- // it with an explicit sizeof(literal) if necessary. I don't think it will
380
- // be, as this is only used in a couple of rare states.
381
- int text_len = strlen(text);
382
- return text_len == buffer->length &&
383
- memcmp(buffer->data, text, text_len) == 0;
384
- }
385
- #endif
386
-
387
- static void doc_type_state_init(GumboParser* parser) {
388
- GumboTokenDocType* doc_type_state =
389
- &parser->_tokenizer_state->_doc_type_state;
390
- // We initialize these to NULL here so that we don't end up leaking memory if
391
- // we never see a doctype token. When we do see a doctype token, we reset
392
- // them to a freshly-allocated empty string so that we can present a uniform
393
- // interface to client code and not make them check for null. Ownership is
394
- // transferred to the doctype token when it's emitted.
395
- doc_type_state->name = NULL;
396
- doc_type_state->public_identifier = NULL;
397
- doc_type_state->system_identifier = NULL;
398
- doc_type_state->force_quirks = false;
399
- doc_type_state->has_public_identifier = false;
400
- doc_type_state->has_system_identifier = false;
401
- }
402
-
403
- // Sets the token original_text and position to the current iterator position.
404
- // This is necessary because [CDATA[ sections may include text that is ignored
405
- // by the tokenizer.
406
- static void reset_token_start_point(GumboTokenizerState* tokenizer) {
407
- tokenizer->_token_start = utf8iterator_get_char_pointer(&tokenizer->_input);
408
- utf8iterator_get_position(&tokenizer->_input, &tokenizer->_token_start_pos);
409
- }
410
-
411
- // Sets the tag buffer original text and start point to the current iterator
412
- // position. This is necessary because attribute names & values may have
413
- // whitespace preceeding them, and so we can't assume that the actual token
414
- // starting point was the end of the last tag buffer usage.
415
- static void reset_tag_buffer_start_point(GumboParser* parser) {
416
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
417
- GumboTagState* tag_state = &tokenizer->_tag_state;
418
-
419
- utf8iterator_get_position(&tokenizer->_input, &tag_state->_start_pos);
420
- tag_state->_original_text = utf8iterator_get_char_pointer(&tokenizer->_input);
421
- }
422
-
423
- // Moves the temporary buffer contents over to the specified output string,
424
- // and clears the temporary buffer.
425
- static void finish_temporary_buffer(GumboParser* parser, const char** output) {
426
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
427
- *output =
428
- gumbo_string_buffer_to_string(parser, &tokenizer->_temporary_buffer);
429
- clear_temporary_buffer(parser);
430
- }
431
-
432
- // Advances the iterator past the end of the token, and then fills in the
433
- // relevant position fields. It's assumed that after every emit, the tokenizer
434
- // will immediately return (letting the tree-construction stage read the filled
435
- // in Token). Thus, it's safe to advance the input stream here, since it will
436
- // bypass the advance at the bottom of the state machine loop.
437
- //
438
- // Since this advances the iterator and resets the current input, make sure to
439
- // call it after you've recorded any other data you need for the token.
440
- static void finish_token(GumboParser* parser, GumboToken* token) {
441
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
442
- if (!tokenizer->_reconsume_current_input) {
443
- utf8iterator_next(&tokenizer->_input);
444
- }
445
-
446
- token->position = tokenizer->_token_start_pos;
447
- token->original_text.data = tokenizer->_token_start;
448
- reset_token_start_point(tokenizer);
449
- token->original_text.length =
450
- tokenizer->_token_start - token->original_text.data;
451
- if (token->original_text.length > 0 &&
452
- token->original_text.data[token->original_text.length - 1] == '\r') {
453
- // The UTF8 iterator will ignore carriage returns in the input stream, which
454
- // means that the next token may start one past a \r character. The pointer
455
- // arithmetic above results in that \r being appended to the original text
456
- // of the preceding token, so we have to adjust its length here to chop the
457
- // \r off.
458
- --token->original_text.length;
459
- }
460
- }
461
-
462
- // Records the doctype public ID, assumed to be in the temporary buffer.
463
- // Convenience method that also sets has_public_identifier to true.
464
- static void finish_doctype_public_id(GumboParser* parser) {
465
- GumboTokenDocType* doc_type_state =
466
- &parser->_tokenizer_state->_doc_type_state;
467
- gumbo_parser_deallocate(parser, (void*) doc_type_state->public_identifier);
468
- finish_temporary_buffer(parser, &doc_type_state->public_identifier);
469
- doc_type_state->has_public_identifier = true;
470
- }
471
-
472
- // Records the doctype system ID, assumed to be in the temporary buffer.
473
- // Convenience method that also sets has_system_identifier to true.
474
- static void finish_doctype_system_id(GumboParser* parser) {
475
- GumboTokenDocType* doc_type_state =
476
- &parser->_tokenizer_state->_doc_type_state;
477
- gumbo_parser_deallocate(parser, (void*) doc_type_state->system_identifier);
478
- finish_temporary_buffer(parser, &doc_type_state->system_identifier);
479
- doc_type_state->has_system_identifier = true;
480
- }
481
-
482
- // Writes a single specified character to the output token.
483
- static void emit_char(GumboParser* parser, int c, GumboToken* output) {
484
- output->type = get_char_token_type(parser->_tokenizer_state->_is_in_cdata, c);
485
- output->v.character = c;
486
- finish_token(parser, output);
487
- }
488
-
489
- // Writes a replacement character token and records a parse error.
490
- // Always returns RETURN_ERROR, per gumbo_lex return value.
491
- static StateResult emit_replacement_char(
492
- GumboParser* parser, GumboToken* output) {
493
- // In all cases, this is because of a null byte in the input stream.
494
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
495
- emit_char(parser, kUtf8ReplacementChar, output);
496
- return RETURN_ERROR;
497
- }
498
-
499
- // Writes an EOF character token. Always returns RETURN_SUCCESS.
500
- static StateResult emit_eof(GumboParser* parser, GumboToken* output) {
501
- emit_char(parser, -1, output);
502
- return RETURN_SUCCESS;
503
- }
504
-
505
- // Writes the current input character out as a character token.
506
- // Always returns RETURN_SUCCESS.
507
- static bool emit_current_char(GumboParser* parser, GumboToken* output) {
508
- emit_char(
509
- parser, utf8iterator_current(&parser->_tokenizer_state->_input), output);
510
- return RETURN_SUCCESS;
511
- }
512
-
513
- // Writes out a doctype token, copying it from the tokenizer state.
514
- static void emit_doctype(GumboParser* parser, GumboToken* output) {
515
- output->type = GUMBO_TOKEN_DOCTYPE;
516
- output->v.doc_type = parser->_tokenizer_state->_doc_type_state;
517
- finish_token(parser, output);
518
- doc_type_state_init(parser);
519
- }
520
-
521
- // Debug-only function that explicitly sets the attribute vector data to NULL so
522
- // it can be asserted on tag creation, verifying that there are no memory leaks.
523
- static void mark_tag_state_as_empty(GumboTagState* tag_state) {
524
- #ifndef NDEBUG
525
- tag_state->_attributes = kGumboEmptyVector;
526
- #endif
527
- }
528
-
529
- // Writes out the current tag as a start or end tag token.
530
- // Always returns RETURN_SUCCESS.
531
- static StateResult emit_current_tag(GumboParser* parser, GumboToken* output) {
532
- GumboTagState* tag_state = &parser->_tokenizer_state->_tag_state;
533
- if (tag_state->_is_start_tag) {
534
- output->type = GUMBO_TOKEN_START_TAG;
535
- output->v.start_tag.tag = tag_state->_tag;
536
- output->v.start_tag.attributes = tag_state->_attributes;
537
- output->v.start_tag.is_self_closing = tag_state->_is_self_closing;
538
- tag_state->_last_start_tag = tag_state->_tag;
539
- mark_tag_state_as_empty(tag_state);
540
- gumbo_debug(
541
- "Emitted start tag %s.\n", gumbo_normalized_tagname(tag_state->_tag));
542
- } else {
543
- output->type = GUMBO_TOKEN_END_TAG;
544
- output->v.end_tag = tag_state->_tag;
545
- // In end tags, ownership of the attributes vector is not transferred to the
546
- // token, but it's still initialized as normal, so it must be manually
547
- // deallocated. There may also be attributes to destroy, in certain broken
548
- // cases like </div</th> (the "th" is an attribute there).
549
- for (unsigned int i = 0; i < tag_state->_attributes.length; ++i) {
550
- gumbo_destroy_attribute(parser, tag_state->_attributes.data[i]);
551
- }
552
- gumbo_parser_deallocate(parser, tag_state->_attributes.data);
553
- mark_tag_state_as_empty(tag_state);
554
- gumbo_debug(
555
- "Emitted end tag %s.\n", gumbo_normalized_tagname(tag_state->_tag));
556
- }
557
- gumbo_string_buffer_destroy(parser, &tag_state->_buffer);
558
- finish_token(parser, output);
559
- gumbo_debug("Original text = %.*s.\n", output->original_text.length,
560
- output->original_text.data);
561
- assert(output->original_text.length >= 2);
562
- assert(output->original_text.data[0] == '<');
563
- assert(output->original_text.data[output->original_text.length - 1] == '>');
564
- return RETURN_SUCCESS;
565
- }
566
-
567
- // In some states, we speculatively start a tag, but don't know whether it'll be
568
- // emitted as tag token or as a series of character tokens until we finish it.
569
- // We need to abandon the tag we'd started & free its memory in that case to
570
- // avoid a memory leak.
571
- static void abandon_current_tag(GumboParser* parser) {
572
- GumboTagState* tag_state = &parser->_tokenizer_state->_tag_state;
573
- for (unsigned int i = 0; i < tag_state->_attributes.length; ++i) {
574
- gumbo_destroy_attribute(parser, tag_state->_attributes.data[i]);
575
- }
576
- gumbo_parser_deallocate(parser, tag_state->_attributes.data);
577
- mark_tag_state_as_empty(tag_state);
578
- gumbo_string_buffer_destroy(parser, &tag_state->_buffer);
579
- gumbo_debug("Abandoning current tag.\n");
580
- }
581
-
582
- // Wraps the consume_char_ref function to handle its output and make the
583
- // appropriate TokenizerState modifications. Returns RETURN_ERROR if a parse
584
- // error occurred, RETURN_SUCCESS otherwise.
585
- static StateResult emit_char_ref(GumboParser* parser,
586
- int additional_allowed_char, bool is_in_attribute, GumboToken* output) {
587
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
588
- OneOrTwoCodepoints char_ref;
589
- bool status = consume_char_ref(
590
- parser, &tokenizer->_input, additional_allowed_char, false, &char_ref);
591
- if (char_ref.first != kGumboNoChar) {
592
- // consume_char_ref ends with the iterator pointing at the next character,
593
- // so we need to be sure not advance it again before reading the next token.
594
- tokenizer->_reconsume_current_input = true;
595
- emit_char(parser, char_ref.first, output);
596
- tokenizer->_buffered_emit_char = char_ref.second;
597
- } else {
598
- emit_char(parser, '&', output);
599
- }
600
- return status ? RETURN_SUCCESS : RETURN_ERROR;
601
- }
602
-
603
- // Emits a comment token. Comments use the temporary buffer to accumulate their
604
- // data, and then it's copied over and released to the 'text' field of the
605
- // GumboToken union. Always returns RETURN_SUCCESS.
606
- static StateResult emit_comment(GumboParser* parser, GumboToken* output) {
607
- output->type = GUMBO_TOKEN_COMMENT;
608
- finish_temporary_buffer(parser, &output->v.text);
609
- finish_token(parser, output);
610
- return RETURN_SUCCESS;
611
- }
612
-
613
- // Checks to see we should be flushing accumulated characters in the temporary
614
- // buffer, and fills the output token with the next output character if so.
615
- // Returns true if a character has been emitted and the tokenizer should
616
- // immediately return, false if we're at the end of the temporary buffer and
617
- // should resume normal operation.
618
- static bool maybe_emit_from_temporary_buffer(
619
- GumboParser* parser, GumboToken* output) {
620
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
621
- const char* c = tokenizer->_temporary_buffer_emit;
622
- GumboStringBuffer* buffer = &tokenizer->_temporary_buffer;
623
-
624
- if (!c || c >= buffer->data + buffer->length) {
625
- tokenizer->_temporary_buffer_emit = NULL;
626
- return false;
627
- }
628
-
629
- assert(*c == utf8iterator_current(&tokenizer->_input));
630
- // emit_char also advances the input stream. We need to do some juggling of
631
- // the _reconsume_current_input flag to get the proper behavior when emitting
632
- // previous tokens. Basically, _reconsume_current_input should *never* be set
633
- // when emitting anything from the temporary buffer, since those characters
634
- // have already been advanced past. However, it should be preserved so that
635
- // when the *next* character is encountered again, the tokenizer knows not to
636
- // advance past it.
637
- bool saved_reconsume_state = tokenizer->_reconsume_current_input;
638
- tokenizer->_reconsume_current_input = false;
639
- emit_char(parser, *c, output);
640
- ++tokenizer->_temporary_buffer_emit;
641
- tokenizer->_reconsume_current_input = saved_reconsume_state;
642
- return true;
643
- }
644
-
645
- // Sets up the tokenizer to begin flushing the temporary buffer.
646
- // This resets the input iterator stream to the start of the last tag, sets up
647
- // _temporary_buffer_emit, and then (if the temporary buffer is non-empty) emits
648
- // the first character in it. It returns true if a character was emitted, false
649
- // otherwise.
650
- static bool emit_temporary_buffer(GumboParser* parser, GumboToken* output) {
651
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
652
- assert(tokenizer->_temporary_buffer.data);
653
- utf8iterator_reset(&tokenizer->_input);
654
- tokenizer->_temporary_buffer_emit = tokenizer->_temporary_buffer.data;
655
- return maybe_emit_from_temporary_buffer(parser, output);
656
- }
657
-
658
- // Appends a codepoint to the current tag buffer. If
659
- // reinitilize_position_on_first is set, this also initializes the tag buffer
660
- // start point; the only time you would *not* want to pass true for this
661
- // parameter is if you want the original_text to include character (like an
662
- // opening quote) that doesn't appear in the value.
663
- static void append_char_to_tag_buffer(
664
- GumboParser* parser, int codepoint, bool reinitilize_position_on_first) {
665
- GumboStringBuffer* buffer = &parser->_tokenizer_state->_tag_state._buffer;
666
- if (buffer->length == 0 && reinitilize_position_on_first) {
667
- reset_tag_buffer_start_point(parser);
668
- }
669
- gumbo_string_buffer_append_codepoint(parser, codepoint, buffer);
670
- }
671
-
672
- // (Re-)initialize the tag buffer. This also resets the original_text pointer
673
- // and _start_pos field to point to the current position.
674
- static void initialize_tag_buffer(GumboParser* parser) {
675
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
676
- GumboTagState* tag_state = &tokenizer->_tag_state;
677
-
678
- gumbo_string_buffer_init(parser, &tag_state->_buffer);
679
- reset_tag_buffer_start_point(parser);
680
- }
681
-
682
- // Initializes the tag_state to start a new tag, keeping track of the opening
683
- // positions and original text. Takes a boolean indicating whether this is a
684
- // start or end tag.
685
- static void start_new_tag(GumboParser* parser, bool is_start_tag) {
686
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
687
- GumboTagState* tag_state = &tokenizer->_tag_state;
688
- int c = utf8iterator_current(&tokenizer->_input);
689
- assert(is_alpha(c));
690
- c = ensure_lowercase(c);
691
- assert(is_alpha(c));
692
-
693
- initialize_tag_buffer(parser);
694
- gumbo_string_buffer_append_codepoint(parser, c, &tag_state->_buffer);
695
-
696
- assert(tag_state->_attributes.data == NULL);
697
- // Initial size chosen by statistical analysis of a corpus of 60k webpages.
698
- // 99.5% of elements have 0 attributes, 93% of the remainder have 1. These
699
- // numbers are a bit higher for more modern websites (eg. ~45% = 0, ~40% = 1
700
- // for the HTML5 Spec), but still have basically 99% of nodes with <= 2 attrs.
701
- gumbo_vector_init(parser, 1, &tag_state->_attributes);
702
- tag_state->_drop_next_attr_value = false;
703
- tag_state->_is_start_tag = is_start_tag;
704
- tag_state->_is_self_closing = false;
705
- gumbo_debug("Starting new tag.\n");
706
- }
707
-
708
- // Fills in the specified char* with the contents of the tag buffer.
709
- static void copy_over_tag_buffer(GumboParser* parser, const char** output) {
710
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
711
- GumboTagState* tag_state = &tokenizer->_tag_state;
712
- *output = gumbo_string_buffer_to_string(parser, &tag_state->_buffer);
713
- }
714
-
715
- // Fills in:
716
- // * The original_text GumboStringPiece with the portion of the original
717
- // buffer that corresponds to the tag buffer.
718
- // * The start_pos GumboSourcePosition with the start position of the tag
719
- // buffer.
720
- // * The end_pos GumboSourcePosition with the current source position.
721
- static void copy_over_original_tag_text(GumboParser* parser,
722
- GumboStringPiece* original_text, GumboSourcePosition* start_pos,
723
- GumboSourcePosition* end_pos) {
724
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
725
- GumboTagState* tag_state = &tokenizer->_tag_state;
726
-
727
- original_text->data = tag_state->_original_text;
728
- original_text->length = utf8iterator_get_char_pointer(&tokenizer->_input) -
729
- tag_state->_original_text;
730
- if (original_text->length > 0
731
- && original_text->data[original_text->length - 1] == '\r') {
732
- // Since \r is skipped by the UTF-8 iterator, it can sometimes end up
733
- // appended to the end of original text even when it's really the first part
734
- // of the next character. If we detect this situation, shrink the length of
735
- // the original text by 1 to remove the carriage return.
736
- --original_text->length;
737
- }
738
- *start_pos = tag_state->_start_pos;
739
- utf8iterator_get_position(&tokenizer->_input, end_pos);
740
- }
741
-
742
- // Releases and then re-initializes the tag buffer.
743
- static void reinitialize_tag_buffer(GumboParser* parser) {
744
- gumbo_parser_deallocate(
745
- parser, parser->_tokenizer_state->_tag_state._buffer.data);
746
- initialize_tag_buffer(parser);
747
- }
748
-
749
- // Moves some data from the temporary buffer over the the tag-based fields in
750
- // TagState.
751
- static void finish_tag_name(GumboParser* parser) {
752
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
753
- GumboTagState* tag_state = &tokenizer->_tag_state;
754
-
755
- tag_state->_tag =
756
- gumbo_tagn_enum(tag_state->_buffer.data, tag_state->_buffer.length);
757
- reinitialize_tag_buffer(parser);
758
- }
759
-
760
- // Adds an ERR_DUPLICATE_ATTR parse error to the parser's error struct.
761
- static void add_duplicate_attr_error(GumboParser* parser, const char* attr_name,
762
- int original_index, int new_index) {
763
- GumboError* error = gumbo_add_error(parser);
764
- if (!error) {
765
- return;
766
- }
767
- GumboTagState* tag_state = &parser->_tokenizer_state->_tag_state;
768
- error->type = GUMBO_ERR_DUPLICATE_ATTR;
769
- error->position = tag_state->_start_pos;
770
- error->original_text = tag_state->_original_text;
771
- error->v.duplicate_attr.original_index = original_index;
772
- error->v.duplicate_attr.new_index = new_index;
773
- copy_over_tag_buffer(parser, &error->v.duplicate_attr.name);
774
- reinitialize_tag_buffer(parser);
775
- }
776
-
777
- // Creates a new attribute in the current tag, copying the current tag buffer to
778
- // the attribute's name. The attribute's value starts out as the empty string
779
- // (following the "Boolean attributes" section of the spec) and is only
780
- // overwritten on finish_attribute_value(). If the attribute has already been
781
- // specified, the new attribute is dropped, a parse error is added, and the
782
- // function returns false. Otherwise, this returns true.
783
- static bool finish_attribute_name(GumboParser* parser) {
784
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
785
- GumboTagState* tag_state = &tokenizer->_tag_state;
786
- // May've been set by a previous attribute without a value; reset it here.
787
- tag_state->_drop_next_attr_value = false;
788
- assert(tag_state->_attributes.data);
789
- assert(tag_state->_attributes.capacity);
790
-
791
- GumboVector* /* GumboAttribute* */ attributes = &tag_state->_attributes;
792
- for (unsigned int i = 0; i < attributes->length; ++i) {
793
- GumboAttribute* attr = attributes->data[i];
794
- if (strlen(attr->name) == tag_state->_buffer.length &&
795
- memcmp(attr->name, tag_state->_buffer.data,
796
- tag_state->_buffer.length) == 0) {
797
- // Identical attribute; bail.
798
- add_duplicate_attr_error(parser, attr->name, i, attributes->length);
799
- tag_state->_drop_next_attr_value = true;
800
- return false;
801
- }
802
- }
803
-
804
- GumboAttribute* attr = gumbo_parser_allocate(parser, sizeof(GumboAttribute));
805
- attr->attr_namespace = GUMBO_ATTR_NAMESPACE_NONE;
806
- copy_over_tag_buffer(parser, &attr->name);
807
- copy_over_original_tag_text(
808
- parser, &attr->original_name, &attr->name_start, &attr->name_end);
809
- attr->value = gumbo_copy_stringz(parser, "");
810
- copy_over_original_tag_text(
811
- parser, &attr->original_value, &attr->name_start, &attr->name_end);
812
- gumbo_vector_add(parser, attr, attributes);
813
- reinitialize_tag_buffer(parser);
814
- return true;
815
- }
816
-
817
- // Finishes an attribute value. This sets the value of the most recently added
818
- // attribute to the current contents of the tag buffer.
819
- static void finish_attribute_value(GumboParser* parser) {
820
- GumboTagState* tag_state = &parser->_tokenizer_state->_tag_state;
821
- if (tag_state->_drop_next_attr_value) {
822
- // Duplicate attribute name detected in an earlier state, so we have to
823
- // ignore the value.
824
- tag_state->_drop_next_attr_value = false;
825
- reinitialize_tag_buffer(parser);
826
- return;
827
- }
828
-
829
- GumboAttribute* attr =
830
- tag_state->_attributes.data[tag_state->_attributes.length - 1];
831
- gumbo_parser_deallocate(parser, (void*) attr->value);
832
- copy_over_tag_buffer(parser, &attr->value);
833
- copy_over_original_tag_text(
834
- parser, &attr->original_value, &attr->value_start, &attr->value_end);
835
- reinitialize_tag_buffer(parser);
836
- }
837
-
838
- // Returns true if the current end tag matches the last start tag emitted.
839
- static bool is_appropriate_end_tag(GumboParser* parser) {
840
- GumboTagState* tag_state = &parser->_tokenizer_state->_tag_state;
841
- assert(!tag_state->_is_start_tag);
842
- return tag_state->_last_start_tag != GUMBO_TAG_LAST &&
843
- tag_state->_last_start_tag == gumbo_tagn_enum(tag_state->_buffer.data,
844
- tag_state->_buffer.length);
845
- }
846
-
847
- void gumbo_tokenizer_state_init(
848
- GumboParser* parser, const char* text, size_t text_length) {
849
- GumboTokenizerState* tokenizer =
850
- gumbo_parser_allocate(parser, sizeof(GumboTokenizerState));
851
- parser->_tokenizer_state = tokenizer;
852
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
853
- tokenizer->_reconsume_current_input = false;
854
- tokenizer->_is_current_node_foreign = false;
855
- tokenizer->_is_in_cdata = false;
856
- tokenizer->_tag_state._last_start_tag = GUMBO_TAG_LAST;
857
-
858
- tokenizer->_buffered_emit_char = kGumboNoChar;
859
- gumbo_string_buffer_init(parser, &tokenizer->_temporary_buffer);
860
- tokenizer->_temporary_buffer_emit = NULL;
861
-
862
- mark_tag_state_as_empty(&tokenizer->_tag_state);
863
-
864
- gumbo_string_buffer_init(parser, &tokenizer->_script_data_buffer);
865
- tokenizer->_token_start = text;
866
- utf8iterator_init(parser, text, text_length, &tokenizer->_input);
867
- utf8iterator_get_position(&tokenizer->_input, &tokenizer->_token_start_pos);
868
- doc_type_state_init(parser);
869
- }
870
-
871
- void gumbo_tokenizer_state_destroy(GumboParser* parser) {
872
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
873
- assert(tokenizer->_doc_type_state.name == NULL);
874
- assert(tokenizer->_doc_type_state.public_identifier == NULL);
875
- assert(tokenizer->_doc_type_state.system_identifier == NULL);
876
- gumbo_string_buffer_destroy(parser, &tokenizer->_temporary_buffer);
877
- gumbo_string_buffer_destroy(parser, &tokenizer->_script_data_buffer);
878
- gumbo_parser_deallocate(parser, tokenizer);
879
- }
880
-
881
- void gumbo_tokenizer_set_state(GumboParser* parser, GumboTokenizerEnum state) {
882
- parser->_tokenizer_state->_state = state;
883
- }
884
-
885
- void gumbo_tokenizer_set_is_current_node_foreign(
886
- GumboParser* parser, bool is_foreign) {
887
- if (is_foreign != parser->_tokenizer_state->_is_current_node_foreign) {
888
- gumbo_debug("Toggling is_current_node_foreign to %s.\n",
889
- is_foreign ? "true" : "false");
890
- }
891
- parser->_tokenizer_state->_is_current_node_foreign = is_foreign;
892
- }
893
-
894
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#data-state
895
- static StateResult handle_data_state(GumboParser* parser,
896
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
897
- switch (c) {
898
- case '&':
899
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_CHAR_REF_IN_DATA);
900
- // The char_ref machinery expects to be on the & so it can mark that
901
- // and return to it if the text isn't a char ref, so we need to
902
- // reconsume it.
903
- tokenizer->_reconsume_current_input = true;
904
- return NEXT_CHAR;
905
- case '<':
906
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_TAG_OPEN);
907
- clear_temporary_buffer(parser);
908
- append_char_to_temporary_buffer(parser, '<');
909
- return NEXT_CHAR;
910
- case '\0':
911
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
912
- emit_char(parser, c, output);
913
- return RETURN_ERROR;
914
- default:
915
- return emit_current_char(parser, output);
916
- }
917
- }
918
-
919
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#character-reference-in-data-state
920
- static StateResult handle_char_ref_in_data_state(GumboParser* parser,
921
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
922
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
923
- return emit_char_ref(parser, ' ', false, output);
924
- }
925
-
926
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#rcdata-state
927
- static StateResult handle_rcdata_state(GumboParser* parser,
928
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
929
- switch (c) {
930
- case '&':
931
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_CHAR_REF_IN_RCDATA);
932
- tokenizer->_reconsume_current_input = true;
933
- return NEXT_CHAR;
934
- case '<':
935
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA_LT);
936
- clear_temporary_buffer(parser);
937
- append_char_to_temporary_buffer(parser, '<');
938
- return NEXT_CHAR;
939
- case '\0':
940
- return emit_replacement_char(parser, output);
941
- case -1:
942
- return emit_eof(parser, output);
943
- default:
944
- return emit_current_char(parser, output);
945
- }
946
- }
947
-
948
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#character-reference-in-rcdata-state
949
- static StateResult handle_char_ref_in_rcdata_state(GumboParser* parser,
950
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
951
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA);
952
- return emit_char_ref(parser, ' ', false, output);
953
- }
954
-
955
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#rawtext-state
956
- static StateResult handle_rawtext_state(GumboParser* parser,
957
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
958
- switch (c) {
959
- case '<':
960
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT_LT);
961
- clear_temporary_buffer(parser);
962
- append_char_to_temporary_buffer(parser, '<');
963
- return NEXT_CHAR;
964
- case '\0':
965
- return emit_replacement_char(parser, output);
966
- case -1:
967
- return emit_eof(parser, output);
968
- default:
969
- return emit_current_char(parser, output);
970
- }
971
- }
972
-
973
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-state
974
- static StateResult handle_script_state(GumboParser* parser,
975
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
976
- switch (c) {
977
- case '<':
978
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_LT);
979
- clear_temporary_buffer(parser);
980
- append_char_to_temporary_buffer(parser, '<');
981
- return NEXT_CHAR;
982
- case '\0':
983
- return emit_replacement_char(parser, output);
984
- case -1:
985
- return emit_eof(parser, output);
986
- default:
987
- return emit_current_char(parser, output);
988
- }
989
- }
990
-
991
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#plaintext-state
992
- static StateResult handle_plaintext_state(GumboParser* parser,
993
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
994
- switch (c) {
995
- case '\0':
996
- return emit_replacement_char(parser, output);
997
- case -1:
998
- return emit_eof(parser, output);
999
- default:
1000
- return emit_current_char(parser, output);
1001
- }
1002
- }
1003
-
1004
- static bool pi_target_is_xml_reserved(const GumboStringBuffer* target) {
1005
- return target->length >= 3
1006
- && (target->data[0] == 'x' || target->data[0] == 'X')
1007
- && (target->data[1] == 'm' || target->data[1] == 'M')
1008
- && (target->data[2] == 'l' || target->data[2] == 'L');
1009
- }
1010
-
1011
- // https://html.spec.whatwg.org/multipage/syntax.html#processing-instructions
1012
- static StateResult handle_processing_instruction_state(GumboParser* parser,
1013
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1014
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1015
-
1016
- GumboStringBuffer pi;
1017
- gumbo_string_buffer_init(parser, &pi);
1018
- while (is_alpha(c) || (c >= '0' && c <= '9') || c == '-' || c == '_') {
1019
- gumbo_string_buffer_append_codepoint(parser, c, &pi);
1020
- utf8iterator_next(&tokenizer->_input);
1021
- c = utf8iterator_current(&tokenizer->_input);
1022
- }
1023
-
1024
- bool has_separator = get_char_token_type(false, c) == GUMBO_TOKEN_WHITESPACE || c == '?' || c == '>';
1025
- bool valid_target = pi.length > 0
1026
- && (is_alpha(pi.data[0]) || pi.data[0] == '_')
1027
- && !pi_target_is_xml_reserved(&pi);
1028
-
1029
- if (c == -1 && (pi.length == 0 || (valid_target && !has_separator))) {
1030
- gumbo_string_buffer_destroy(parser, &pi);
1031
- return emit_eof(parser, output);
1032
- }
1033
-
1034
- if (!valid_target || !has_separator) {
1035
- clear_temporary_buffer(parser);
1036
- append_char_to_temporary_buffer(parser, '?');
1037
- for (size_t i = 0; i < pi.length; ++i) {
1038
- append_char_to_temporary_buffer(parser, pi.data[i]);
1039
- }
1040
- gumbo_string_buffer_destroy(parser, &pi);
1041
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_COMMENT);
1042
- tokenizer->_reconsume_current_input = true;
1043
- return NEXT_CHAR;
1044
- }
1045
-
1046
- gumbo_string_buffer_append_codepoint(parser, ' ', &pi);
1047
- while (get_char_token_type(false, c) == GUMBO_TOKEN_WHITESPACE) {
1048
- utf8iterator_next(&tokenizer->_input);
1049
- c = utf8iterator_current(&tokenizer->_input);
1050
- }
1051
-
1052
- while (c != '>') {
1053
- if (c == -1) {
1054
- gumbo_string_buffer_destroy(parser, &pi);
1055
- return emit_eof(parser, output);
1056
- }
1057
- if (c == '?') {
1058
- utf8iterator_next(&tokenizer->_input);
1059
- c = utf8iterator_current(&tokenizer->_input);
1060
- if (c != '>' && c != -1) {
1061
- gumbo_string_buffer_append_codepoint(parser, '?', &pi);
1062
- }
1063
- continue;
1064
- }
1065
- gumbo_string_buffer_append_codepoint(
1066
- parser, c == '\0' ? kUtf8ReplacementChar : c, &pi);
1067
- utf8iterator_next(&tokenizer->_input);
1068
- c = utf8iterator_current(&tokenizer->_input);
1069
- }
1070
-
1071
- assert(c == '>');
1072
- output->type = GUMBO_TOKEN_PROCESSING_INSTRUCTION;
1073
- output->v.text = gumbo_string_buffer_to_string(parser, &pi);
1074
- gumbo_string_buffer_destroy(parser, &pi);
1075
- finish_token(parser, output);
1076
- return RETURN_SUCCESS;
1077
- }
1078
-
1079
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#tag-open-state
1080
- static StateResult handle_tag_open_state(GumboParser* parser,
1081
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1082
- assert(temporary_buffer_equals(parser, "<"));
1083
- switch (c) {
1084
- case '!':
1085
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_MARKUP_DECLARATION);
1086
- clear_temporary_buffer(parser);
1087
- return NEXT_CHAR;
1088
- case '/':
1089
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_END_TAG_OPEN);
1090
- append_char_to_temporary_buffer(parser, '/');
1091
- return NEXT_CHAR;
1092
- case '?':
1093
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_PROCESSING_INSTRUCTION);
1094
- clear_temporary_buffer(parser);
1095
- return NEXT_CHAR;
1096
- default:
1097
- if (is_alpha(c)) {
1098
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_TAG_NAME);
1099
- start_new_tag(parser, true);
1100
- return NEXT_CHAR;
1101
- } else {
1102
- tokenizer_add_parse_error(parser, GUMBO_ERR_TAG_INVALID);
1103
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1104
- emit_temporary_buffer(parser, output);
1105
- return RETURN_ERROR;
1106
- }
1107
- }
1108
- }
1109
-
1110
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#end-tag-open-state
1111
- static StateResult handle_end_tag_open_state(GumboParser* parser,
1112
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1113
- assert(temporary_buffer_equals(parser, "</"));
1114
- switch (c) {
1115
- case '>':
1116
- tokenizer_add_parse_error(parser, GUMBO_ERR_CLOSE_TAG_EMPTY);
1117
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1118
- return NEXT_CHAR;
1119
- case -1:
1120
- tokenizer_add_parse_error(parser, GUMBO_ERR_CLOSE_TAG_EOF);
1121
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1122
- return emit_temporary_buffer(parser, output);
1123
- default:
1124
- if (is_alpha(c)) {
1125
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_TAG_NAME);
1126
- start_new_tag(parser, false);
1127
- } else {
1128
- tokenizer_add_parse_error(parser, GUMBO_ERR_CLOSE_TAG_INVALID);
1129
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_COMMENT);
1130
- clear_temporary_buffer(parser);
1131
- append_char_to_temporary_buffer(parser, c);
1132
- }
1133
- return NEXT_CHAR;
1134
- }
1135
- }
1136
-
1137
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#tag-name-state
1138
- static StateResult handle_tag_name_state(GumboParser* parser,
1139
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1140
- switch (c) {
1141
- case '\t':
1142
- case '\n':
1143
- case '\f':
1144
- case ' ':
1145
- finish_tag_name(parser);
1146
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME);
1147
- return NEXT_CHAR;
1148
- case '/':
1149
- finish_tag_name(parser);
1150
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG);
1151
- return NEXT_CHAR;
1152
- case '>':
1153
- finish_tag_name(parser);
1154
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1155
- return emit_current_tag(parser, output);
1156
- case '\0':
1157
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
1158
- append_char_to_tag_buffer(parser, kUtf8ReplacementChar, true);
1159
- return NEXT_CHAR;
1160
- case -1:
1161
- tokenizer_add_parse_error(parser, GUMBO_ERR_TAG_EOF);
1162
- abandon_current_tag(parser);
1163
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1164
- return NEXT_CHAR;
1165
- default:
1166
- append_char_to_tag_buffer(parser, ensure_lowercase(c), true);
1167
- return NEXT_CHAR;
1168
- }
1169
- }
1170
-
1171
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#rcdata-less-than-sign-state
1172
- static StateResult handle_rcdata_lt_state(GumboParser* parser,
1173
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1174
- assert(temporary_buffer_equals(parser, "<"));
1175
- if (c == '/') {
1176
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA_END_TAG_OPEN);
1177
- append_char_to_temporary_buffer(parser, '/');
1178
- return NEXT_CHAR;
1179
- } else {
1180
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA);
1181
- tokenizer->_reconsume_current_input = true;
1182
- return emit_temporary_buffer(parser, output);
1183
- }
1184
- }
1185
-
1186
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#rcdata-end-tag-open-state
1187
- static StateResult handle_rcdata_end_tag_open_state(GumboParser* parser,
1188
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1189
- assert(temporary_buffer_equals(parser, "</"));
1190
- if (is_alpha(c)) {
1191
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA_END_TAG_NAME);
1192
- start_new_tag(parser, false);
1193
- append_char_to_temporary_buffer(parser, c);
1194
- return NEXT_CHAR;
1195
- } else {
1196
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA);
1197
- return emit_temporary_buffer(parser, output);
1198
- }
1199
- return true;
1200
- }
1201
-
1202
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#rcdata-end-tag-name-state
1203
- static StateResult handle_rcdata_end_tag_name_state(GumboParser* parser,
1204
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1205
- assert(tokenizer->_temporary_buffer.length >= 2);
1206
- if (is_alpha(c)) {
1207
- append_char_to_tag_buffer(parser, ensure_lowercase(c), true);
1208
- append_char_to_temporary_buffer(parser, c);
1209
- return NEXT_CHAR;
1210
- } else if (is_appropriate_end_tag(parser)) {
1211
- switch (c) {
1212
- case '\t':
1213
- case '\n':
1214
- case '\f':
1215
- case ' ':
1216
- finish_tag_name(parser);
1217
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME);
1218
- return NEXT_CHAR;
1219
- case '/':
1220
- finish_tag_name(parser);
1221
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG);
1222
- return NEXT_CHAR;
1223
- case '>':
1224
- finish_tag_name(parser);
1225
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1226
- return emit_current_tag(parser, output);
1227
- }
1228
- }
1229
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RCDATA);
1230
- abandon_current_tag(parser);
1231
- return emit_temporary_buffer(parser, output);
1232
- }
1233
-
1234
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#rawtext-less-than-sign-state
1235
- static StateResult handle_rawtext_lt_state(GumboParser* parser,
1236
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1237
- assert(temporary_buffer_equals(parser, "<"));
1238
- if (c == '/') {
1239
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT_END_TAG_OPEN);
1240
- append_char_to_temporary_buffer(parser, '/');
1241
- return NEXT_CHAR;
1242
- } else {
1243
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT);
1244
- tokenizer->_reconsume_current_input = true;
1245
- return emit_temporary_buffer(parser, output);
1246
- }
1247
- }
1248
-
1249
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#rawtext-end-tag-open-state
1250
- static StateResult handle_rawtext_end_tag_open_state(GumboParser* parser,
1251
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1252
- assert(temporary_buffer_equals(parser, "</"));
1253
- if (is_alpha(c)) {
1254
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT_END_TAG_NAME);
1255
- start_new_tag(parser, false);
1256
- append_char_to_temporary_buffer(parser, c);
1257
- return NEXT_CHAR;
1258
- } else {
1259
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT);
1260
- return emit_temporary_buffer(parser, output);
1261
- }
1262
- }
1263
-
1264
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#rawtext-end-tag-name-state
1265
- static StateResult handle_rawtext_end_tag_name_state(GumboParser* parser,
1266
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1267
- assert(tokenizer->_temporary_buffer.length >= 2);
1268
- gumbo_debug("Last end tag: %*s\n", (int) tokenizer->_tag_state._buffer.length,
1269
- tokenizer->_tag_state._buffer.data);
1270
- if (is_alpha(c)) {
1271
- append_char_to_tag_buffer(parser, ensure_lowercase(c), true);
1272
- append_char_to_temporary_buffer(parser, c);
1273
- return NEXT_CHAR;
1274
- } else if (is_appropriate_end_tag(parser)) {
1275
- gumbo_debug("Is an appropriate end tag.\n");
1276
- switch (c) {
1277
- case '\t':
1278
- case '\n':
1279
- case '\f':
1280
- case ' ':
1281
- finish_tag_name(parser);
1282
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME);
1283
- return NEXT_CHAR;
1284
- case '/':
1285
- finish_tag_name(parser);
1286
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG);
1287
- return NEXT_CHAR;
1288
- case '>':
1289
- finish_tag_name(parser);
1290
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1291
- return emit_current_tag(parser, output);
1292
- }
1293
- }
1294
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_RAWTEXT);
1295
- abandon_current_tag(parser);
1296
- return emit_temporary_buffer(parser, output);
1297
- }
1298
-
1299
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-less-than-sign-state
1300
- static StateResult handle_script_lt_state(GumboParser* parser,
1301
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1302
- assert(temporary_buffer_equals(parser, "<"));
1303
- if (c == '/') {
1304
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_END_TAG_OPEN);
1305
- append_char_to_temporary_buffer(parser, '/');
1306
- return NEXT_CHAR;
1307
- } else if (c == '!') {
1308
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_START);
1309
- append_char_to_temporary_buffer(parser, '!');
1310
- return emit_temporary_buffer(parser, output);
1311
- } else {
1312
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT);
1313
- tokenizer->_reconsume_current_input = true;
1314
- return emit_temporary_buffer(parser, output);
1315
- }
1316
- }
1317
-
1318
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-end-tag-open-state
1319
- static StateResult handle_script_end_tag_open_state(GumboParser* parser,
1320
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1321
- assert(temporary_buffer_equals(parser, "</"));
1322
- if (is_alpha(c)) {
1323
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_END_TAG_NAME);
1324
- start_new_tag(parser, false);
1325
- append_char_to_temporary_buffer(parser, c);
1326
- return NEXT_CHAR;
1327
- } else {
1328
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT);
1329
- return emit_temporary_buffer(parser, output);
1330
- }
1331
- }
1332
-
1333
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-end-tag-name-state
1334
- static StateResult handle_script_end_tag_name_state(GumboParser* parser,
1335
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1336
- assert(tokenizer->_temporary_buffer.length >= 2);
1337
- if (is_alpha(c)) {
1338
- append_char_to_tag_buffer(parser, ensure_lowercase(c), true);
1339
- append_char_to_temporary_buffer(parser, c);
1340
- return NEXT_CHAR;
1341
- } else if (is_appropriate_end_tag(parser)) {
1342
- switch (c) {
1343
- case '\t':
1344
- case '\n':
1345
- case '\f':
1346
- case ' ':
1347
- finish_tag_name(parser);
1348
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME);
1349
- return NEXT_CHAR;
1350
- case '/':
1351
- finish_tag_name(parser);
1352
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG);
1353
- return NEXT_CHAR;
1354
- case '>':
1355
- finish_tag_name(parser);
1356
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1357
- return emit_current_tag(parser, output);
1358
- }
1359
- }
1360
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT);
1361
- abandon_current_tag(parser);
1362
- return emit_temporary_buffer(parser, output);
1363
- }
1364
-
1365
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escape-start-state
1366
- static StateResult handle_script_escaped_start_state(GumboParser* parser,
1367
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1368
- if (c == '-') {
1369
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_START_DASH);
1370
- return emit_current_char(parser, output);
1371
- } else {
1372
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT);
1373
- tokenizer->_reconsume_current_input = true;
1374
- return NEXT_CHAR;
1375
- }
1376
- }
1377
-
1378
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escape-start-dash-state
1379
- static StateResult handle_script_escaped_start_dash_state(GumboParser* parser,
1380
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1381
- if (c == '-') {
1382
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_DASH_DASH);
1383
- return emit_current_char(parser, output);
1384
- } else {
1385
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT);
1386
- tokenizer->_reconsume_current_input = true;
1387
- return NEXT_CHAR;
1388
- }
1389
- }
1390
-
1391
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escaped-state
1392
- static StateResult handle_script_escaped_state(GumboParser* parser,
1393
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1394
- switch (c) {
1395
- case '-':
1396
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_DASH);
1397
- return emit_current_char(parser, output);
1398
- case '<':
1399
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_LT);
1400
- clear_temporary_buffer(parser);
1401
- append_char_to_temporary_buffer(parser, c);
1402
- return NEXT_CHAR;
1403
- case '\0':
1404
- return emit_replacement_char(parser, output);
1405
- case -1:
1406
- tokenizer_add_parse_error(parser, GUMBO_ERR_SCRIPT_EOF);
1407
- return emit_eof(parser, output);
1408
- default:
1409
- return emit_current_char(parser, output);
1410
- }
1411
- }
1412
-
1413
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escaped-dash-state
1414
- static StateResult handle_script_escaped_dash_state(GumboParser* parser,
1415
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1416
- switch (c) {
1417
- case '-':
1418
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_DASH_DASH);
1419
- return emit_current_char(parser, output);
1420
- case '<':
1421
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_LT);
1422
- clear_temporary_buffer(parser);
1423
- append_char_to_temporary_buffer(parser, c);
1424
- return NEXT_CHAR;
1425
- case '\0':
1426
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED);
1427
- return emit_replacement_char(parser, output);
1428
- case -1:
1429
- tokenizer_add_parse_error(parser, GUMBO_ERR_SCRIPT_EOF);
1430
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1431
- return NEXT_CHAR;
1432
- default:
1433
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED);
1434
- return emit_current_char(parser, output);
1435
- }
1436
- }
1437
-
1438
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escaped-dash-dash-state
1439
- static StateResult handle_script_escaped_dash_dash_state(GumboParser* parser,
1440
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1441
- switch (c) {
1442
- case '-':
1443
- return emit_current_char(parser, output);
1444
- case '<':
1445
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_LT);
1446
- clear_temporary_buffer(parser);
1447
- append_char_to_temporary_buffer(parser, c);
1448
- return NEXT_CHAR;
1449
- case '>':
1450
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT);
1451
- return emit_current_char(parser, output);
1452
- case '\0':
1453
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED);
1454
- return emit_replacement_char(parser, output);
1455
- case -1:
1456
- tokenizer_add_parse_error(parser, GUMBO_ERR_SCRIPT_EOF);
1457
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1458
- return NEXT_CHAR;
1459
- default:
1460
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED);
1461
- return emit_current_char(parser, output);
1462
- }
1463
- }
1464
-
1465
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escaped-less-than-sign-state
1466
- static StateResult handle_script_escaped_lt_state(GumboParser* parser,
1467
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1468
- assert(temporary_buffer_equals(parser, "<"));
1469
- assert(!tokenizer->_script_data_buffer.length);
1470
- if (c == '/') {
1471
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_END_TAG_OPEN);
1472
- append_char_to_temporary_buffer(parser, c);
1473
- return NEXT_CHAR;
1474
- } else if (is_alpha(c)) {
1475
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_START);
1476
- append_char_to_temporary_buffer(parser, c);
1477
- gumbo_string_buffer_append_codepoint(
1478
- parser, ensure_lowercase(c), &tokenizer->_script_data_buffer);
1479
- return emit_temporary_buffer(parser, output);
1480
- } else {
1481
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED);
1482
- return emit_temporary_buffer(parser, output);
1483
- }
1484
- }
1485
-
1486
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escaped-end-tag-open-state
1487
- static StateResult handle_script_escaped_end_tag_open_state(GumboParser* parser,
1488
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1489
- assert(temporary_buffer_equals(parser, "</"));
1490
- if (is_alpha(c)) {
1491
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED_END_TAG_NAME);
1492
- start_new_tag(parser, false);
1493
- append_char_to_temporary_buffer(parser, c);
1494
- return NEXT_CHAR;
1495
- } else {
1496
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED);
1497
- return emit_temporary_buffer(parser, output);
1498
- }
1499
- }
1500
-
1501
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-escaped-end-tag-name-state
1502
- static StateResult handle_script_escaped_end_tag_name_state(GumboParser* parser,
1503
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1504
- assert(tokenizer->_temporary_buffer.length >= 2);
1505
- if (is_alpha(c)) {
1506
- append_char_to_tag_buffer(parser, ensure_lowercase(c), true);
1507
- append_char_to_temporary_buffer(parser, c);
1508
- return NEXT_CHAR;
1509
- } else if (is_appropriate_end_tag(parser)) {
1510
- switch (c) {
1511
- case '\t':
1512
- case '\n':
1513
- case '\f':
1514
- case ' ':
1515
- finish_tag_name(parser);
1516
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME);
1517
- return NEXT_CHAR;
1518
- case '/':
1519
- finish_tag_name(parser);
1520
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG);
1521
- return NEXT_CHAR;
1522
- case '>':
1523
- finish_tag_name(parser);
1524
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1525
- return emit_current_tag(parser, output);
1526
- }
1527
- }
1528
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED);
1529
- abandon_current_tag(parser);
1530
- return emit_temporary_buffer(parser, output);
1531
- }
1532
-
1533
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-double-escape-start-state
1534
- static StateResult handle_script_double_escaped_start_state(GumboParser* parser,
1535
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1536
- switch (c) {
1537
- case '\t':
1538
- case '\n':
1539
- case '\f':
1540
- case ' ':
1541
- case '/':
1542
- case '>':
1543
- gumbo_tokenizer_set_state(
1544
- parser, gumbo_string_equals(&kScriptTag,
1545
- (GumboStringPiece*) &tokenizer->_script_data_buffer)
1546
- ? GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED
1547
- : GUMBO_LEX_SCRIPT_ESCAPED);
1548
- return emit_current_char(parser, output);
1549
- default:
1550
- if (is_alpha(c)) {
1551
- gumbo_string_buffer_append_codepoint(
1552
- parser, ensure_lowercase(c), &tokenizer->_script_data_buffer);
1553
- return emit_current_char(parser, output);
1554
- } else {
1555
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_ESCAPED);
1556
- tokenizer->_reconsume_current_input = true;
1557
- return NEXT_CHAR;
1558
- }
1559
- }
1560
- }
1561
-
1562
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-double-escaped-state
1563
- static StateResult handle_script_double_escaped_state(GumboParser* parser,
1564
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1565
- switch (c) {
1566
- case '-':
1567
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_DASH);
1568
- return emit_current_char(parser, output);
1569
- case '<':
1570
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_LT);
1571
- return emit_current_char(parser, output);
1572
- case '\0':
1573
- return emit_replacement_char(parser, output);
1574
- case -1:
1575
- tokenizer_add_parse_error(parser, GUMBO_ERR_SCRIPT_EOF);
1576
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1577
- return NEXT_CHAR;
1578
- default:
1579
- return emit_current_char(parser, output);
1580
- }
1581
- }
1582
-
1583
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-double-escaped-dash-state
1584
- static StateResult handle_script_double_escaped_dash_state(GumboParser* parser,
1585
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1586
- switch (c) {
1587
- case '-':
1588
- gumbo_tokenizer_set_state(
1589
- parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_DASH_DASH);
1590
- return emit_current_char(parser, output);
1591
- case '<':
1592
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_LT);
1593
- return emit_current_char(parser, output);
1594
- case '\0':
1595
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED);
1596
- return emit_replacement_char(parser, output);
1597
- case -1:
1598
- tokenizer_add_parse_error(parser, GUMBO_ERR_SCRIPT_EOF);
1599
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1600
- return NEXT_CHAR;
1601
- default:
1602
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED);
1603
- return emit_current_char(parser, output);
1604
- }
1605
- }
1606
-
1607
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-double-escaped-dash-dash-state
1608
- static StateResult handle_script_double_escaped_dash_dash_state(
1609
- GumboParser* parser, GumboTokenizerState* tokenizer, int c,
1610
- GumboToken* output) {
1611
- switch (c) {
1612
- case '-':
1613
- return emit_current_char(parser, output);
1614
- case '<':
1615
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_LT);
1616
- return emit_current_char(parser, output);
1617
- case '>':
1618
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT);
1619
- return emit_current_char(parser, output);
1620
- case '\0':
1621
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED);
1622
- return emit_replacement_char(parser, output);
1623
- case -1:
1624
- tokenizer_add_parse_error(parser, GUMBO_ERR_SCRIPT_EOF);
1625
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1626
- return NEXT_CHAR;
1627
- default:
1628
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED);
1629
- return emit_current_char(parser, output);
1630
- }
1631
- }
1632
-
1633
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-double-escaped-less-than-sign-state
1634
- static StateResult handle_script_double_escaped_lt_state(GumboParser* parser,
1635
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1636
- if (c == '/') {
1637
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED_END);
1638
- gumbo_string_buffer_clear(parser, &tokenizer->_script_data_buffer);
1639
- return emit_current_char(parser, output);
1640
- } else {
1641
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED);
1642
- tokenizer->_reconsume_current_input = true;
1643
- return NEXT_CHAR;
1644
- }
1645
- }
1646
-
1647
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#script-data-double-escape-end-state
1648
- static StateResult handle_script_double_escaped_end_state(GumboParser* parser,
1649
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1650
- switch (c) {
1651
- case '\t':
1652
- case '\n':
1653
- case '\f':
1654
- case ' ':
1655
- case '/':
1656
- case '>':
1657
- gumbo_tokenizer_set_state(
1658
- parser, gumbo_string_equals(&kScriptTag,
1659
- (GumboStringPiece*) &tokenizer->_script_data_buffer)
1660
- ? GUMBO_LEX_SCRIPT_ESCAPED
1661
- : GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED);
1662
- return emit_current_char(parser, output);
1663
- default:
1664
- if (is_alpha(c)) {
1665
- gumbo_string_buffer_append_codepoint(
1666
- parser, ensure_lowercase(c), &tokenizer->_script_data_buffer);
1667
- return emit_current_char(parser, output);
1668
- } else {
1669
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SCRIPT_DOUBLE_ESCAPED);
1670
- tokenizer->_reconsume_current_input = true;
1671
- return NEXT_CHAR;
1672
- }
1673
- }
1674
- }
1675
-
1676
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#before-attribute-name-state
1677
- static StateResult handle_before_attr_name_state(GumboParser* parser,
1678
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1679
- switch (c) {
1680
- case '\t':
1681
- case '\n':
1682
- case '\f':
1683
- case ' ':
1684
- return NEXT_CHAR;
1685
- case '/':
1686
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG);
1687
- return NEXT_CHAR;
1688
- case '>':
1689
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1690
- return emit_current_tag(parser, output);
1691
- case '\0':
1692
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
1693
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_NAME);
1694
- append_char_to_temporary_buffer(parser, 0xfffd);
1695
- return NEXT_CHAR;
1696
- case -1:
1697
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_NAME_EOF);
1698
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1699
- abandon_current_tag(parser);
1700
- return NEXT_CHAR;
1701
- case '"':
1702
- case '\'':
1703
- case '<':
1704
- case '=':
1705
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_NAME_INVALID);
1706
- // Fall through.
1707
- default:
1708
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_NAME);
1709
- append_char_to_tag_buffer(parser, ensure_lowercase(c), true);
1710
- return NEXT_CHAR;
1711
- }
1712
- }
1713
-
1714
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#attribute-name-state
1715
- static StateResult handle_attr_name_state(GumboParser* parser,
1716
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1717
- switch (c) {
1718
- case '\t':
1719
- case '\n':
1720
- case '\f':
1721
- case ' ':
1722
- finish_attribute_name(parser);
1723
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_ATTR_NAME);
1724
- return NEXT_CHAR;
1725
- case '/':
1726
- finish_attribute_name(parser);
1727
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG);
1728
- return NEXT_CHAR;
1729
- case '=':
1730
- finish_attribute_name(parser);
1731
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_VALUE);
1732
- return NEXT_CHAR;
1733
- case '>':
1734
- finish_attribute_name(parser);
1735
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1736
- return emit_current_tag(parser, output);
1737
- case '\0':
1738
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
1739
- append_char_to_tag_buffer(parser, kUtf8ReplacementChar, true);
1740
- return NEXT_CHAR;
1741
- case -1:
1742
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1743
- abandon_current_tag(parser);
1744
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_NAME_EOF);
1745
- return NEXT_CHAR;
1746
- case '"':
1747
- case '\'':
1748
- case '<':
1749
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_NAME_INVALID);
1750
- // Fall through.
1751
- default:
1752
- append_char_to_tag_buffer(parser, ensure_lowercase(c), true);
1753
- return NEXT_CHAR;
1754
- }
1755
- }
1756
-
1757
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#after-attribute-name-state
1758
- static StateResult handle_after_attr_name_state(GumboParser* parser,
1759
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1760
- switch (c) {
1761
- case '\t':
1762
- case '\n':
1763
- case '\f':
1764
- case ' ':
1765
- return NEXT_CHAR;
1766
- case '/':
1767
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG);
1768
- return NEXT_CHAR;
1769
- case '=':
1770
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_VALUE);
1771
- return NEXT_CHAR;
1772
- case '>':
1773
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1774
- return emit_current_tag(parser, output);
1775
- case '\0':
1776
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
1777
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_NAME);
1778
- append_char_to_temporary_buffer(parser, 0xfffd);
1779
- return NEXT_CHAR;
1780
- case -1:
1781
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_NAME_EOF);
1782
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1783
- abandon_current_tag(parser);
1784
- return NEXT_CHAR;
1785
- case '"':
1786
- case '\'':
1787
- case '<':
1788
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_NAME_INVALID);
1789
- // Fall through.
1790
- default:
1791
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_NAME);
1792
- append_char_to_tag_buffer(parser, ensure_lowercase(c), true);
1793
- return NEXT_CHAR;
1794
- }
1795
- }
1796
-
1797
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#before-attribute-value-state
1798
- static StateResult handle_before_attr_value_state(GumboParser* parser,
1799
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1800
- switch (c) {
1801
- case '\t':
1802
- case '\n':
1803
- case '\f':
1804
- case ' ':
1805
- return NEXT_CHAR;
1806
- case '"':
1807
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_VALUE_DOUBLE_QUOTED);
1808
- reset_tag_buffer_start_point(parser);
1809
- return NEXT_CHAR;
1810
- case '&':
1811
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_VALUE_UNQUOTED);
1812
- tokenizer->_reconsume_current_input = true;
1813
- return NEXT_CHAR;
1814
- case '\'':
1815
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_VALUE_SINGLE_QUOTED);
1816
- reset_tag_buffer_start_point(parser);
1817
- return NEXT_CHAR;
1818
- case '\0':
1819
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
1820
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_VALUE_UNQUOTED);
1821
- append_char_to_tag_buffer(parser, kUtf8ReplacementChar, true);
1822
- return NEXT_CHAR;
1823
- case -1:
1824
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_UNQUOTED_EOF);
1825
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1826
- abandon_current_tag(parser);
1827
- tokenizer->_reconsume_current_input = true;
1828
- return NEXT_CHAR;
1829
- case '>':
1830
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_UNQUOTED_RIGHT_BRACKET);
1831
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1832
- emit_current_tag(parser, output);
1833
- return RETURN_ERROR;
1834
- case '<':
1835
- case '=':
1836
- case '`':
1837
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_UNQUOTED_EQUALS);
1838
- // Fall through.
1839
- default:
1840
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_ATTR_VALUE_UNQUOTED);
1841
- append_char_to_tag_buffer(parser, c, true);
1842
- return NEXT_CHAR;
1843
- }
1844
- }
1845
-
1846
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#attribute-value-double-quoted-state
1847
- static StateResult handle_attr_value_double_quoted_state(GumboParser* parser,
1848
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1849
- switch (c) {
1850
- case '"':
1851
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_ATTR_VALUE_QUOTED);
1852
- return NEXT_CHAR;
1853
- case '&':
1854
- tokenizer->_tag_state._attr_value_state = tokenizer->_state;
1855
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_CHAR_REF_IN_ATTR_VALUE);
1856
- tokenizer->_reconsume_current_input = true;
1857
- return NEXT_CHAR;
1858
- case '\0':
1859
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
1860
- append_char_to_tag_buffer(parser, kUtf8ReplacementChar, false);
1861
- return NEXT_CHAR;
1862
- case -1:
1863
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_DOUBLE_QUOTE_EOF);
1864
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1865
- abandon_current_tag(parser);
1866
- tokenizer->_reconsume_current_input = true;
1867
- return NEXT_CHAR;
1868
- default:
1869
- append_char_to_tag_buffer(parser, c, false);
1870
- return NEXT_CHAR;
1871
- }
1872
- }
1873
-
1874
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#attribute-value-single-quoted-state
1875
- static StateResult handle_attr_value_single_quoted_state(GumboParser* parser,
1876
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1877
- switch (c) {
1878
- case '\'':
1879
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_ATTR_VALUE_QUOTED);
1880
- return NEXT_CHAR;
1881
- case '&':
1882
- tokenizer->_tag_state._attr_value_state = tokenizer->_state;
1883
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_CHAR_REF_IN_ATTR_VALUE);
1884
- tokenizer->_reconsume_current_input = true;
1885
- return NEXT_CHAR;
1886
- case '\0':
1887
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
1888
- append_char_to_tag_buffer(parser, kUtf8ReplacementChar, false);
1889
- return NEXT_CHAR;
1890
- case -1:
1891
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_SINGLE_QUOTE_EOF);
1892
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1893
- abandon_current_tag(parser);
1894
- tokenizer->_reconsume_current_input = true;
1895
- return NEXT_CHAR;
1896
- default:
1897
- append_char_to_tag_buffer(parser, c, false);
1898
- return NEXT_CHAR;
1899
- }
1900
- }
1901
-
1902
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#attribute-value-unquoted-state
1903
- static StateResult handle_attr_value_unquoted_state(GumboParser* parser,
1904
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1905
- switch (c) {
1906
- case '\t':
1907
- case '\n':
1908
- case '\f':
1909
- case ' ':
1910
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME);
1911
- finish_attribute_value(parser);
1912
- return NEXT_CHAR;
1913
- case '&':
1914
- tokenizer->_tag_state._attr_value_state = tokenizer->_state;
1915
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_CHAR_REF_IN_ATTR_VALUE);
1916
- tokenizer->_reconsume_current_input = true;
1917
- return NEXT_CHAR;
1918
- case '>':
1919
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1920
- finish_attribute_value(parser);
1921
- return emit_current_tag(parser, output);
1922
- case '\0':
1923
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
1924
- append_char_to_tag_buffer(parser, kUtf8ReplacementChar, true);
1925
- return NEXT_CHAR;
1926
- case -1:
1927
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_UNQUOTED_EOF);
1928
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
1929
- tokenizer->_reconsume_current_input = true;
1930
- abandon_current_tag(parser);
1931
- return NEXT_CHAR;
1932
- case '<':
1933
- case '=':
1934
- case '"':
1935
- case '\'':
1936
- case '`':
1937
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_UNQUOTED_EQUALS);
1938
- // Fall through.
1939
- default:
1940
- append_char_to_tag_buffer(parser, c, true);
1941
- return NEXT_CHAR;
1942
- }
1943
- }
1944
-
1945
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#character-reference-in-attribute-value-state
1946
- static StateResult handle_char_ref_in_attr_value_state(GumboParser* parser,
1947
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1948
- OneOrTwoCodepoints char_ref;
1949
- int allowed_char;
1950
- bool is_unquoted = false;
1951
- switch (tokenizer->_tag_state._attr_value_state) {
1952
- case GUMBO_LEX_ATTR_VALUE_DOUBLE_QUOTED:
1953
- allowed_char = '"';
1954
- break;
1955
- case GUMBO_LEX_ATTR_VALUE_SINGLE_QUOTED:
1956
- allowed_char = '\'';
1957
- break;
1958
- case GUMBO_LEX_ATTR_VALUE_UNQUOTED:
1959
- allowed_char = '>';
1960
- is_unquoted = true;
1961
- break;
1962
- default:
1963
- // -Wmaybe-uninitialized is a little overzealous here, and doesn't
1964
- // get that the assert(0) means this codepath will never happen.
1965
- allowed_char = ' ';
1966
- assert(0);
1967
- }
1968
-
1969
- // Ignore the status, since we don't have a convenient way of signalling that
1970
- // a parser error has occurred when the error occurs in the middle of a
1971
- // multi-state token. We'd need a flag inside the TokenizerState to do this,
1972
- // but that's a low priority fix.
1973
- consume_char_ref(parser, &tokenizer->_input, allowed_char, true, &char_ref);
1974
- if (char_ref.first != kGumboNoChar) {
1975
- tokenizer->_reconsume_current_input = true;
1976
- append_char_to_tag_buffer(parser, char_ref.first, is_unquoted);
1977
- if (char_ref.second != kGumboNoChar) {
1978
- append_char_to_tag_buffer(parser, char_ref.second, is_unquoted);
1979
- }
1980
- } else {
1981
- append_char_to_tag_buffer(parser, '&', is_unquoted);
1982
- }
1983
- gumbo_tokenizer_set_state(parser, tokenizer->_tag_state._attr_value_state);
1984
- return NEXT_CHAR;
1985
- }
1986
-
1987
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#after-attribute-value-quoted-state
1988
- static StateResult handle_after_attr_value_quoted_state(GumboParser* parser,
1989
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
1990
- finish_attribute_value(parser);
1991
- switch (c) {
1992
- case '\t':
1993
- case '\n':
1994
- case '\f':
1995
- case ' ':
1996
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME);
1997
- return NEXT_CHAR;
1998
- case '/':
1999
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_SELF_CLOSING_START_TAG);
2000
- return NEXT_CHAR;
2001
- case '>':
2002
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2003
- return emit_current_tag(parser, output);
2004
- case -1:
2005
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_AFTER_EOF);
2006
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2007
- abandon_current_tag(parser);
2008
- tokenizer->_reconsume_current_input = true;
2009
- return NEXT_CHAR;
2010
- default:
2011
- tokenizer_add_parse_error(parser, GUMBO_ERR_ATTR_AFTER_INVALID);
2012
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME);
2013
- tokenizer->_reconsume_current_input = true;
2014
- return NEXT_CHAR;
2015
- }
2016
- }
2017
-
2018
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#self-closing-start-tag-state
2019
- static StateResult handle_self_closing_start_tag_state(GumboParser* parser,
2020
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2021
- switch (c) {
2022
- case '>':
2023
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2024
- tokenizer->_tag_state._is_self_closing = true;
2025
- return emit_current_tag(parser, output);
2026
- case -1:
2027
- tokenizer_add_parse_error(parser, GUMBO_ERR_SOLIDUS_EOF);
2028
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2029
- abandon_current_tag(parser);
2030
- return NEXT_CHAR;
2031
- default:
2032
- tokenizer_add_parse_error(parser, GUMBO_ERR_SOLIDUS_INVALID);
2033
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_ATTR_NAME);
2034
- tokenizer->_reconsume_current_input = true;
2035
- return NEXT_CHAR;
2036
- }
2037
- }
2038
-
2039
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#bogus-comment-state
2040
- static StateResult handle_bogus_comment_state(GumboParser* parser,
2041
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2042
- while (c != '>' && c != -1) {
2043
- if (c == '\0') {
2044
- c = 0xFFFD;
2045
- }
2046
- append_char_to_temporary_buffer(parser, c);
2047
- utf8iterator_next(&tokenizer->_input);
2048
- c = utf8iterator_current(&tokenizer->_input);
2049
- }
2050
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2051
- return emit_comment(parser, output);
2052
- }
2053
-
2054
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#markup-declaration-open-state
2055
- static StateResult handle_markup_declaration_state(GumboParser* parser,
2056
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2057
- if (utf8iterator_maybe_consume_match(
2058
- &tokenizer->_input, "--", sizeof("--") - 1, true)) {
2059
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_START);
2060
- tokenizer->_reconsume_current_input = true;
2061
- } else if (utf8iterator_maybe_consume_match(
2062
- &tokenizer->_input, "DOCTYPE", sizeof("DOCTYPE") - 1, false)) {
2063
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DOCTYPE);
2064
- tokenizer->_reconsume_current_input = true;
2065
- // If we get here, we know we'll eventually emit a doctype token, so now is
2066
- // the time to initialize the doctype strings. (Not in doctype_state_init,
2067
- // since then they'll leak if ownership never gets transferred to the
2068
- // doctype token.
2069
- tokenizer->_doc_type_state.name = gumbo_copy_stringz(parser, "");
2070
- tokenizer->_doc_type_state.public_identifier =
2071
- gumbo_copy_stringz(parser, "");
2072
- tokenizer->_doc_type_state.system_identifier =
2073
- gumbo_copy_stringz(parser, "");
2074
- } else if (tokenizer->_is_current_node_foreign &&
2075
- utf8iterator_maybe_consume_match(
2076
- &tokenizer->_input, "[CDATA[", sizeof("[CDATA[") - 1, true)) {
2077
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_CDATA);
2078
- tokenizer->_is_in_cdata = true;
2079
- tokenizer->_reconsume_current_input = true;
2080
- } else {
2081
- tokenizer_add_parse_error(parser, GUMBO_ERR_DASHES_OR_DOCTYPE);
2082
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_COMMENT);
2083
- tokenizer->_reconsume_current_input = true;
2084
- clear_temporary_buffer(parser);
2085
- }
2086
- return NEXT_CHAR;
2087
- }
2088
-
2089
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#comment-start-state
2090
- static StateResult handle_comment_start_state(GumboParser* parser,
2091
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2092
- switch (c) {
2093
- case '-':
2094
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_START_DASH);
2095
- return NEXT_CHAR;
2096
- case '\0':
2097
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2098
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT);
2099
- append_char_to_temporary_buffer(parser, kUtf8ReplacementChar);
2100
- return NEXT_CHAR;
2101
- case '>':
2102
- tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_INVALID);
2103
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2104
- emit_comment(parser, output);
2105
- return RETURN_ERROR;
2106
- case -1:
2107
- tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_EOF);
2108
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2109
- emit_comment(parser, output);
2110
- return RETURN_ERROR;
2111
- default:
2112
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT);
2113
- append_char_to_temporary_buffer(parser, c);
2114
- return NEXT_CHAR;
2115
- }
2116
- }
2117
-
2118
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#comment-start-dash-state
2119
- static StateResult handle_comment_start_dash_state(GumboParser* parser,
2120
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2121
- switch (c) {
2122
- case '-':
2123
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_END);
2124
- return NEXT_CHAR;
2125
- case '\0':
2126
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2127
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT);
2128
- append_char_to_temporary_buffer(parser, '-');
2129
- append_char_to_temporary_buffer(parser, kUtf8ReplacementChar);
2130
- return NEXT_CHAR;
2131
- case '>':
2132
- tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_INVALID);
2133
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2134
- emit_comment(parser, output);
2135
- return RETURN_ERROR;
2136
- case -1:
2137
- tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_EOF);
2138
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2139
- emit_comment(parser, output);
2140
- return RETURN_ERROR;
2141
- default:
2142
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT);
2143
- append_char_to_temporary_buffer(parser, '-');
2144
- append_char_to_temporary_buffer(parser, c);
2145
- return NEXT_CHAR;
2146
- }
2147
- }
2148
-
2149
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#comment-state
2150
- static StateResult handle_comment_state(GumboParser* parser,
2151
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2152
- switch (c) {
2153
- case '-':
2154
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_END_DASH);
2155
- return NEXT_CHAR;
2156
- case '\0':
2157
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2158
- append_char_to_temporary_buffer(parser, kUtf8ReplacementChar);
2159
- return NEXT_CHAR;
2160
- case -1:
2161
- tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_EOF);
2162
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2163
- emit_comment(parser, output);
2164
- return RETURN_ERROR;
2165
- default:
2166
- append_char_to_temporary_buffer(parser, c);
2167
- return NEXT_CHAR;
2168
- }
2169
- }
2170
-
2171
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#comment-end-dash-state
2172
- static StateResult handle_comment_end_dash_state(GumboParser* parser,
2173
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2174
- switch (c) {
2175
- case '-':
2176
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_END);
2177
- return NEXT_CHAR;
2178
- case '\0':
2179
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2180
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT);
2181
- append_char_to_temporary_buffer(parser, '-');
2182
- append_char_to_temporary_buffer(parser, kUtf8ReplacementChar);
2183
- return NEXT_CHAR;
2184
- case -1:
2185
- tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_EOF);
2186
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2187
- emit_comment(parser, output);
2188
- return RETURN_ERROR;
2189
- default:
2190
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT);
2191
- append_char_to_temporary_buffer(parser, '-');
2192
- append_char_to_temporary_buffer(parser, c);
2193
- return NEXT_CHAR;
2194
- }
2195
- }
2196
-
2197
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#comment-end-state
2198
- static StateResult handle_comment_end_state(GumboParser* parser,
2199
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2200
- switch (c) {
2201
- case '>':
2202
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2203
- return emit_comment(parser, output);
2204
- case '\0':
2205
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2206
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT);
2207
- append_char_to_temporary_buffer(parser, '-');
2208
- append_char_to_temporary_buffer(parser, '-');
2209
- append_char_to_temporary_buffer(parser, kUtf8ReplacementChar);
2210
- return NEXT_CHAR;
2211
- case '!':
2212
- tokenizer_add_parse_error(
2213
- parser, GUMBO_ERR_COMMENT_BANG_AFTER_DOUBLE_DASH);
2214
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_END_BANG);
2215
- return NEXT_CHAR;
2216
- case '-':
2217
- tokenizer_add_parse_error(
2218
- parser, GUMBO_ERR_COMMENT_DASH_AFTER_DOUBLE_DASH);
2219
- append_char_to_temporary_buffer(parser, '-');
2220
- return NEXT_CHAR;
2221
- case -1:
2222
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2223
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2224
- emit_comment(parser, output);
2225
- return RETURN_ERROR;
2226
- default:
2227
- tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_INVALID);
2228
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT);
2229
- append_char_to_temporary_buffer(parser, '-');
2230
- append_char_to_temporary_buffer(parser, '-');
2231
- append_char_to_temporary_buffer(parser, c);
2232
- return NEXT_CHAR;
2233
- }
2234
- }
2235
-
2236
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#comment-end-bang-state
2237
- static StateResult handle_comment_end_bang_state(GumboParser* parser,
2238
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2239
- switch (c) {
2240
- case '-':
2241
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT_END_DASH);
2242
- append_char_to_temporary_buffer(parser, '-');
2243
- append_char_to_temporary_buffer(parser, '-');
2244
- append_char_to_temporary_buffer(parser, '!');
2245
- return NEXT_CHAR;
2246
- case '>':
2247
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2248
- return emit_comment(parser, output);
2249
- case '\0':
2250
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2251
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT);
2252
- append_char_to_temporary_buffer(parser, '-');
2253
- append_char_to_temporary_buffer(parser, '-');
2254
- append_char_to_temporary_buffer(parser, '!');
2255
- append_char_to_temporary_buffer(parser, kUtf8ReplacementChar);
2256
- return NEXT_CHAR;
2257
- case -1:
2258
- tokenizer_add_parse_error(parser, GUMBO_ERR_COMMENT_END_BANG_EOF);
2259
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2260
- emit_comment(parser, output);
2261
- return RETURN_ERROR;
2262
- default:
2263
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_COMMENT);
2264
- append_char_to_temporary_buffer(parser, '-');
2265
- append_char_to_temporary_buffer(parser, '-');
2266
- append_char_to_temporary_buffer(parser, '!');
2267
- append_char_to_temporary_buffer(parser, c);
2268
- return NEXT_CHAR;
2269
- }
2270
- }
2271
-
2272
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#doctype-state
2273
- static StateResult handle_doctype_state(GumboParser* parser,
2274
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2275
- assert(!tokenizer->_temporary_buffer.length);
2276
- switch (c) {
2277
- case '\t':
2278
- case '\n':
2279
- case '\f':
2280
- case ' ':
2281
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_DOCTYPE_NAME);
2282
- return NEXT_CHAR;
2283
- case -1:
2284
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2285
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2286
- tokenizer->_doc_type_state.force_quirks = true;
2287
- emit_doctype(parser, output);
2288
- return RETURN_ERROR;
2289
- default:
2290
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_SPACE);
2291
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_DOCTYPE_NAME);
2292
- tokenizer->_reconsume_current_input = true;
2293
- tokenizer->_doc_type_state.force_quirks = true;
2294
- return NEXT_CHAR;
2295
- }
2296
- }
2297
-
2298
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#before-doctype-name-state
2299
- static StateResult handle_before_doctype_name_state(GumboParser* parser,
2300
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2301
- switch (c) {
2302
- case '\t':
2303
- case '\n':
2304
- case '\f':
2305
- case ' ':
2306
- return NEXT_CHAR;
2307
- case '\0':
2308
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2309
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DOCTYPE_NAME);
2310
- tokenizer->_doc_type_state.force_quirks = true;
2311
- append_char_to_temporary_buffer(parser, kUtf8ReplacementChar);
2312
- return NEXT_CHAR;
2313
- case '>':
2314
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_RIGHT_BRACKET);
2315
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2316
- tokenizer->_doc_type_state.force_quirks = true;
2317
- emit_doctype(parser, output);
2318
- return RETURN_ERROR;
2319
- case -1:
2320
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2321
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2322
- tokenizer->_doc_type_state.force_quirks = true;
2323
- emit_doctype(parser, output);
2324
- return RETURN_ERROR;
2325
- default:
2326
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DOCTYPE_NAME);
2327
- tokenizer->_doc_type_state.force_quirks = false;
2328
- append_char_to_temporary_buffer(parser, ensure_lowercase(c));
2329
- return NEXT_CHAR;
2330
- }
2331
- }
2332
-
2333
- // http://www.whatwg.org/specs/web-apps/current-work/complete5/tokenization.html#doctype-name-state
2334
- static StateResult handle_doctype_name_state(GumboParser* parser,
2335
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2336
- switch (c) {
2337
- case '\t':
2338
- case '\n':
2339
- case '\f':
2340
- case ' ':
2341
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_DOCTYPE_NAME);
2342
- gumbo_parser_deallocate(parser, (void*) tokenizer->_doc_type_state.name);
2343
- finish_temporary_buffer(parser, &tokenizer->_doc_type_state.name);
2344
- return NEXT_CHAR;
2345
- case '>':
2346
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2347
- gumbo_parser_deallocate(parser, (void*) tokenizer->_doc_type_state.name);
2348
- finish_temporary_buffer(parser, &tokenizer->_doc_type_state.name);
2349
- emit_doctype(parser, output);
2350
- return RETURN_SUCCESS;
2351
- case '\0':
2352
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2353
- append_char_to_temporary_buffer(parser, kUtf8ReplacementChar);
2354
- return NEXT_CHAR;
2355
- case -1:
2356
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2357
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2358
- tokenizer->_doc_type_state.force_quirks = true;
2359
- gumbo_parser_deallocate(parser, (void*) tokenizer->_doc_type_state.name);
2360
- finish_temporary_buffer(parser, &tokenizer->_doc_type_state.name);
2361
- emit_doctype(parser, output);
2362
- return RETURN_ERROR;
2363
- default:
2364
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DOCTYPE_NAME);
2365
- tokenizer->_doc_type_state.force_quirks = false;
2366
- append_char_to_temporary_buffer(parser, ensure_lowercase(c));
2367
- return NEXT_CHAR;
2368
- }
2369
- }
2370
-
2371
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#after-doctype-name-state
2372
- static StateResult handle_after_doctype_name_state(GumboParser* parser,
2373
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2374
- switch (c) {
2375
- case '\t':
2376
- case '\n':
2377
- case '\f':
2378
- case ' ':
2379
- return NEXT_CHAR;
2380
- case '>':
2381
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2382
- emit_doctype(parser, output);
2383
- return RETURN_SUCCESS;
2384
- case -1:
2385
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2386
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2387
- tokenizer->_doc_type_state.force_quirks = true;
2388
- emit_doctype(parser, output);
2389
- return RETURN_ERROR;
2390
- default:
2391
- if (utf8iterator_maybe_consume_match(
2392
- &tokenizer->_input, "PUBLIC", sizeof("PUBLIC") - 1, false)) {
2393
- gumbo_tokenizer_set_state(
2394
- parser, GUMBO_LEX_AFTER_DOCTYPE_PUBLIC_KEYWORD);
2395
- tokenizer->_reconsume_current_input = true;
2396
- } else if (utf8iterator_maybe_consume_match(&tokenizer->_input, "SYSTEM",
2397
- sizeof("SYSTEM") - 1, false)) {
2398
- gumbo_tokenizer_set_state(
2399
- parser, GUMBO_LEX_AFTER_DOCTYPE_SYSTEM_KEYWORD);
2400
- tokenizer->_reconsume_current_input = true;
2401
- } else {
2402
- tokenizer_add_parse_error(
2403
- parser, GUMBO_ERR_DOCTYPE_SPACE_OR_RIGHT_BRACKET);
2404
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE);
2405
- tokenizer->_doc_type_state.force_quirks = true;
2406
- }
2407
- return NEXT_CHAR;
2408
- }
2409
- }
2410
-
2411
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#after-doctype-public-keyword-state
2412
- static StateResult handle_after_doctype_public_keyword_state(
2413
- GumboParser* parser, GumboTokenizerState* tokenizer, int c,
2414
- GumboToken* output) {
2415
- switch (c) {
2416
- case '\t':
2417
- case '\n':
2418
- case '\f':
2419
- case ' ':
2420
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_DOCTYPE_PUBLIC_ID);
2421
- return NEXT_CHAR;
2422
- case '"':
2423
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2424
- assert(temporary_buffer_equals(parser, ""));
2425
- gumbo_tokenizer_set_state(
2426
- parser, GUMBO_LEX_DOCTYPE_PUBLIC_ID_DOUBLE_QUOTED);
2427
- return NEXT_CHAR;
2428
- case '\'':
2429
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2430
- assert(temporary_buffer_equals(parser, ""));
2431
- gumbo_tokenizer_set_state(
2432
- parser, GUMBO_LEX_DOCTYPE_PUBLIC_ID_SINGLE_QUOTED);
2433
- return NEXT_CHAR;
2434
- case '>':
2435
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_RIGHT_BRACKET);
2436
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2437
- tokenizer->_doc_type_state.force_quirks = true;
2438
- emit_doctype(parser, output);
2439
- return RETURN_ERROR;
2440
- case -1:
2441
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2442
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2443
- tokenizer->_doc_type_state.force_quirks = true;
2444
- emit_doctype(parser, output);
2445
- return RETURN_ERROR;
2446
- default:
2447
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2448
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE);
2449
- tokenizer->_doc_type_state.force_quirks = true;
2450
- emit_doctype(parser, output);
2451
- return RETURN_ERROR;
2452
- }
2453
- }
2454
-
2455
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#before-doctype-public-identifier-state
2456
- static StateResult handle_before_doctype_public_id_state(GumboParser* parser,
2457
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2458
- switch (c) {
2459
- case '\t':
2460
- case '\n':
2461
- case '\f':
2462
- case ' ':
2463
- return NEXT_CHAR;
2464
- case '"':
2465
- assert(temporary_buffer_equals(parser, ""));
2466
- gumbo_tokenizer_set_state(
2467
- parser, GUMBO_LEX_DOCTYPE_PUBLIC_ID_DOUBLE_QUOTED);
2468
- return NEXT_CHAR;
2469
- case '\'':
2470
- assert(temporary_buffer_equals(parser, ""));
2471
- gumbo_tokenizer_set_state(
2472
- parser, GUMBO_LEX_DOCTYPE_PUBLIC_ID_SINGLE_QUOTED);
2473
- return NEXT_CHAR;
2474
- case '>':
2475
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END);
2476
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2477
- tokenizer->_doc_type_state.force_quirks = true;
2478
- emit_doctype(parser, output);
2479
- return RETURN_ERROR;
2480
- case -1:
2481
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2482
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2483
- tokenizer->_doc_type_state.force_quirks = true;
2484
- emit_doctype(parser, output);
2485
- return RETURN_ERROR;
2486
- default:
2487
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2488
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE);
2489
- tokenizer->_doc_type_state.force_quirks = true;
2490
- emit_doctype(parser, output);
2491
- return RETURN_ERROR;
2492
- }
2493
- }
2494
-
2495
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#doctype-public-identifier-(double-quoted)-state
2496
- static StateResult handle_doctype_public_id_double_quoted_state(
2497
- GumboParser* parser, GumboTokenizerState* tokenizer, int c,
2498
- GumboToken* output) {
2499
- switch (c) {
2500
- case '"':
2501
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_DOCTYPE_PUBLIC_ID);
2502
- finish_doctype_public_id(parser);
2503
- return NEXT_CHAR;
2504
- case '\0':
2505
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2506
- append_char_to_temporary_buffer(parser, kUtf8ReplacementChar);
2507
- return NEXT_CHAR;
2508
- case '>':
2509
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END);
2510
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2511
- tokenizer->_doc_type_state.force_quirks = true;
2512
- finish_doctype_public_id(parser);
2513
- emit_doctype(parser, output);
2514
- return RETURN_ERROR;
2515
- case -1:
2516
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2517
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2518
- tokenizer->_doc_type_state.force_quirks = true;
2519
- finish_doctype_public_id(parser);
2520
- emit_doctype(parser, output);
2521
- return RETURN_ERROR;
2522
- default:
2523
- append_char_to_temporary_buffer(parser, c);
2524
- return NEXT_CHAR;
2525
- }
2526
- }
2527
-
2528
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#doctype-public-identifier-(single-quoted)-state
2529
- static StateResult handle_doctype_public_id_single_quoted_state(
2530
- GumboParser* parser, GumboTokenizerState* tokenizer, int c,
2531
- GumboToken* output) {
2532
- switch (c) {
2533
- case '\'':
2534
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_DOCTYPE_PUBLIC_ID);
2535
- finish_doctype_public_id(parser);
2536
- return NEXT_CHAR;
2537
- case '\0':
2538
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2539
- append_char_to_temporary_buffer(parser, kUtf8ReplacementChar);
2540
- return NEXT_CHAR;
2541
- case '>':
2542
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END);
2543
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2544
- tokenizer->_doc_type_state.force_quirks = true;
2545
- finish_doctype_public_id(parser);
2546
- emit_doctype(parser, output);
2547
- return RETURN_ERROR;
2548
- case -1:
2549
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2550
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2551
- tokenizer->_doc_type_state.force_quirks = true;
2552
- finish_doctype_public_id(parser);
2553
- emit_doctype(parser, output);
2554
- return RETURN_ERROR;
2555
- default:
2556
- append_char_to_temporary_buffer(parser, c);
2557
- return NEXT_CHAR;
2558
- }
2559
- }
2560
-
2561
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#after-doctype-public-identifier-state
2562
- static StateResult handle_after_doctype_public_id_state(GumboParser* parser,
2563
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2564
- switch (c) {
2565
- case '\t':
2566
- case '\n':
2567
- case '\f':
2568
- case ' ':
2569
- gumbo_tokenizer_set_state(
2570
- parser, GUMBO_LEX_BETWEEN_DOCTYPE_PUBLIC_SYSTEM_ID);
2571
- return NEXT_CHAR;
2572
- case '>':
2573
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2574
- emit_doctype(parser, output);
2575
- return RETURN_SUCCESS;
2576
- case '"':
2577
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2578
- assert(temporary_buffer_equals(parser, ""));
2579
- gumbo_tokenizer_set_state(
2580
- parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_DOUBLE_QUOTED);
2581
- return NEXT_CHAR;
2582
- case '\'':
2583
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2584
- assert(temporary_buffer_equals(parser, ""));
2585
- gumbo_tokenizer_set_state(
2586
- parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_SINGLE_QUOTED);
2587
- return NEXT_CHAR;
2588
- case -1:
2589
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2590
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2591
- tokenizer->_reconsume_current_input = true;
2592
- tokenizer->_doc_type_state.force_quirks = true;
2593
- emit_doctype(parser, output);
2594
- return RETURN_ERROR;
2595
- default:
2596
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2597
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE);
2598
- tokenizer->_doc_type_state.force_quirks = true;
2599
- return NEXT_CHAR;
2600
- }
2601
- }
2602
-
2603
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#between-doctype-public-and-system-identifiers-state
2604
- static StateResult handle_between_doctype_public_system_id_state(
2605
- GumboParser* parser, GumboTokenizerState* tokenizer, int c,
2606
- GumboToken* output) {
2607
- switch (c) {
2608
- case '\t':
2609
- case '\n':
2610
- case '\f':
2611
- case ' ':
2612
- return NEXT_CHAR;
2613
- case '>':
2614
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2615
- emit_doctype(parser, output);
2616
- return RETURN_SUCCESS;
2617
- case '"':
2618
- assert(temporary_buffer_equals(parser, ""));
2619
- gumbo_tokenizer_set_state(
2620
- parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_DOUBLE_QUOTED);
2621
- return NEXT_CHAR;
2622
- case '\'':
2623
- assert(temporary_buffer_equals(parser, ""));
2624
- gumbo_tokenizer_set_state(
2625
- parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_SINGLE_QUOTED);
2626
- return NEXT_CHAR;
2627
- case -1:
2628
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2629
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2630
- tokenizer->_doc_type_state.force_quirks = true;
2631
- emit_doctype(parser, output);
2632
- return RETURN_ERROR;
2633
- default:
2634
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2635
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE);
2636
- tokenizer->_doc_type_state.force_quirks = true;
2637
- emit_doctype(parser, output);
2638
- return RETURN_ERROR;
2639
- }
2640
- }
2641
-
2642
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#after-doctype-system-keyword-state
2643
- static StateResult handle_after_doctype_system_keyword_state(
2644
- GumboParser* parser, GumboTokenizerState* tokenizer, int c,
2645
- GumboToken* output) {
2646
- switch (c) {
2647
- case '\t':
2648
- case '\n':
2649
- case '\f':
2650
- case ' ':
2651
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BEFORE_DOCTYPE_SYSTEM_ID);
2652
- return NEXT_CHAR;
2653
- case '"':
2654
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2655
- assert(temporary_buffer_equals(parser, ""));
2656
- gumbo_tokenizer_set_state(
2657
- parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_DOUBLE_QUOTED);
2658
- return NEXT_CHAR;
2659
- case '\'':
2660
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2661
- assert(temporary_buffer_equals(parser, ""));
2662
- gumbo_tokenizer_set_state(
2663
- parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_SINGLE_QUOTED);
2664
- return NEXT_CHAR;
2665
- case '>':
2666
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END);
2667
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2668
- tokenizer->_doc_type_state.force_quirks = true;
2669
- emit_doctype(parser, output);
2670
- return RETURN_ERROR;
2671
- case -1:
2672
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2673
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2674
- tokenizer->_doc_type_state.force_quirks = true;
2675
- emit_doctype(parser, output);
2676
- return RETURN_ERROR;
2677
- default:
2678
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2679
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE);
2680
- tokenizer->_doc_type_state.force_quirks = true;
2681
- return NEXT_CHAR;
2682
- }
2683
- }
2684
-
2685
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#before-doctype-system-identifier-state
2686
- static StateResult handle_before_doctype_system_id_state(GumboParser* parser,
2687
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2688
- switch (c) {
2689
- case '\t':
2690
- case '\n':
2691
- case '\f':
2692
- case ' ':
2693
- return NEXT_CHAR;
2694
- case '"':
2695
- assert(temporary_buffer_equals(parser, ""));
2696
- gumbo_tokenizer_set_state(
2697
- parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_DOUBLE_QUOTED);
2698
- return NEXT_CHAR;
2699
- case '\'':
2700
- assert(temporary_buffer_equals(parser, ""));
2701
- gumbo_tokenizer_set_state(
2702
- parser, GUMBO_LEX_DOCTYPE_SYSTEM_ID_SINGLE_QUOTED);
2703
- return NEXT_CHAR;
2704
- case '>':
2705
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END);
2706
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2707
- tokenizer->_doc_type_state.force_quirks = true;
2708
- emit_doctype(parser, output);
2709
- return RETURN_ERROR;
2710
- case -1:
2711
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2712
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2713
- tokenizer->_doc_type_state.force_quirks = true;
2714
- emit_doctype(parser, output);
2715
- return RETURN_ERROR;
2716
- default:
2717
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2718
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE);
2719
- tokenizer->_doc_type_state.force_quirks = true;
2720
- return NEXT_CHAR;
2721
- }
2722
- }
2723
-
2724
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#doctype-system-identifier-(double-quoted)-state
2725
- static StateResult handle_doctype_system_id_double_quoted_state(
2726
- GumboParser* parser, GumboTokenizerState* tokenizer, int c,
2727
- GumboToken* output) {
2728
- switch (c) {
2729
- case '"':
2730
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_DOCTYPE_SYSTEM_ID);
2731
- finish_doctype_system_id(parser);
2732
- return NEXT_CHAR;
2733
- case '\0':
2734
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2735
- append_char_to_temporary_buffer(parser, kUtf8ReplacementChar);
2736
- return NEXT_CHAR;
2737
- case '>':
2738
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END);
2739
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2740
- tokenizer->_doc_type_state.force_quirks = true;
2741
- finish_doctype_system_id(parser);
2742
- emit_doctype(parser, output);
2743
- return RETURN_ERROR;
2744
- case -1:
2745
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2746
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2747
- tokenizer->_doc_type_state.force_quirks = true;
2748
- finish_doctype_system_id(parser);
2749
- emit_doctype(parser, output);
2750
- return RETURN_ERROR;
2751
- default:
2752
- append_char_to_temporary_buffer(parser, c);
2753
- return NEXT_CHAR;
2754
- }
2755
- }
2756
-
2757
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#doctype-system-identifier-(single-quoted)-state
2758
- static StateResult handle_doctype_system_id_single_quoted_state(
2759
- GumboParser* parser, GumboTokenizerState* tokenizer, int c,
2760
- GumboToken* output) {
2761
- switch (c) {
2762
- case '\'':
2763
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_AFTER_DOCTYPE_SYSTEM_ID);
2764
- finish_doctype_system_id(parser);
2765
- return NEXT_CHAR;
2766
- case '\0':
2767
- tokenizer_add_parse_error(parser, GUMBO_ERR_UTF8_NULL);
2768
- append_char_to_temporary_buffer(parser, kUtf8ReplacementChar);
2769
- return NEXT_CHAR;
2770
- case '>':
2771
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_END);
2772
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2773
- tokenizer->_doc_type_state.force_quirks = true;
2774
- finish_doctype_system_id(parser);
2775
- emit_doctype(parser, output);
2776
- return RETURN_ERROR;
2777
- case -1:
2778
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2779
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2780
- tokenizer->_doc_type_state.force_quirks = true;
2781
- finish_doctype_system_id(parser);
2782
- emit_doctype(parser, output);
2783
- return RETURN_ERROR;
2784
- default:
2785
- append_char_to_temporary_buffer(parser, c);
2786
- return NEXT_CHAR;
2787
- }
2788
- }
2789
-
2790
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#after-doctype-system-identifier-state
2791
- static StateResult handle_after_doctype_system_id_state(GumboParser* parser,
2792
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2793
- switch (c) {
2794
- case '\t':
2795
- case '\n':
2796
- case '\f':
2797
- case ' ':
2798
- return NEXT_CHAR;
2799
- case '>':
2800
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2801
- emit_doctype(parser, output);
2802
- return RETURN_SUCCESS;
2803
- case -1:
2804
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_EOF);
2805
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2806
- tokenizer->_doc_type_state.force_quirks = true;
2807
- emit_doctype(parser, output);
2808
- return RETURN_ERROR;
2809
- default:
2810
- tokenizer_add_parse_error(parser, GUMBO_ERR_DOCTYPE_INVALID);
2811
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_BOGUS_DOCTYPE);
2812
- return NEXT_CHAR;
2813
- }
2814
- }
2815
-
2816
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#bogus-doctype-state
2817
- static StateResult handle_bogus_doctype_state(GumboParser* parser,
2818
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2819
- if (c == '>' || c == -1) {
2820
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2821
- emit_doctype(parser, output);
2822
- return RETURN_ERROR;
2823
- }
2824
- return NEXT_CHAR;
2825
- }
2826
-
2827
- // http://www.whatwg.org/specs/web-apps/current-work/complete.html#cdata-section-state
2828
- static StateResult handle_cdata_state(GumboParser* parser,
2829
- GumboTokenizerState* tokenizer, int c, GumboToken* output) {
2830
- if (c == -1 || utf8iterator_maybe_consume_match(
2831
- &tokenizer->_input, "]]>", sizeof("]]>") - 1, true)) {
2832
- tokenizer->_reconsume_current_input = true;
2833
- reset_token_start_point(tokenizer);
2834
- gumbo_tokenizer_set_state(parser, GUMBO_LEX_DATA);
2835
- tokenizer->_is_in_cdata = false;
2836
- return NEXT_CHAR;
2837
- } else {
2838
- return emit_current_char(parser, output);
2839
- }
2840
- }
2841
-
2842
- typedef StateResult (*GumboLexerStateFunction)(
2843
- GumboParser*, GumboTokenizerState*, int, GumboToken*);
2844
-
2845
- static GumboLexerStateFunction dispatch_table[] = {
2846
- handle_data_state,
2847
- handle_char_ref_in_data_state,
2848
- handle_rcdata_state,
2849
- handle_char_ref_in_rcdata_state,
2850
- handle_rawtext_state,
2851
- handle_script_state,
2852
- handle_plaintext_state,
2853
- handle_tag_open_state,
2854
- handle_end_tag_open_state,
2855
- handle_tag_name_state,
2856
- handle_rcdata_lt_state,
2857
- handle_rcdata_end_tag_open_state,
2858
- handle_rcdata_end_tag_name_state,
2859
- handle_rawtext_lt_state,
2860
- handle_rawtext_end_tag_open_state,
2861
- handle_rawtext_end_tag_name_state,
2862
- handle_script_lt_state,
2863
- handle_script_end_tag_open_state,
2864
- handle_script_end_tag_name_state,
2865
- handle_script_escaped_start_state,
2866
- handle_script_escaped_start_dash_state,
2867
- handle_script_escaped_state,
2868
- handle_script_escaped_dash_state,
2869
- handle_script_escaped_dash_dash_state,
2870
- handle_script_escaped_lt_state,
2871
- handle_script_escaped_end_tag_open_state,
2872
- handle_script_escaped_end_tag_name_state,
2873
- handle_script_double_escaped_start_state,
2874
- handle_script_double_escaped_state,
2875
- handle_script_double_escaped_dash_state,
2876
- handle_script_double_escaped_dash_dash_state,
2877
- handle_script_double_escaped_lt_state,
2878
- handle_script_double_escaped_end_state,
2879
- handle_before_attr_name_state,
2880
- handle_attr_name_state,
2881
- handle_after_attr_name_state,
2882
- handle_before_attr_value_state,
2883
- handle_attr_value_double_quoted_state,
2884
- handle_attr_value_single_quoted_state,
2885
- handle_attr_value_unquoted_state,
2886
- handle_char_ref_in_attr_value_state,
2887
- handle_after_attr_value_quoted_state,
2888
- handle_self_closing_start_tag_state,
2889
- handle_bogus_comment_state,
2890
- handle_markup_declaration_state,
2891
- handle_comment_start_state,
2892
- handle_comment_start_dash_state,
2893
- handle_comment_state,
2894
- handle_comment_end_dash_state,
2895
- handle_comment_end_state,
2896
- handle_comment_end_bang_state,
2897
- handle_doctype_state,
2898
- handle_before_doctype_name_state,
2899
- handle_doctype_name_state,
2900
- handle_after_doctype_name_state,
2901
- handle_after_doctype_public_keyword_state,
2902
- handle_before_doctype_public_id_state,
2903
- handle_doctype_public_id_double_quoted_state,
2904
- handle_doctype_public_id_single_quoted_state,
2905
- handle_after_doctype_public_id_state,
2906
- handle_between_doctype_public_system_id_state,
2907
- handle_after_doctype_system_keyword_state,
2908
- handle_before_doctype_system_id_state,
2909
- handle_doctype_system_id_double_quoted_state,
2910
- handle_doctype_system_id_single_quoted_state,
2911
- handle_after_doctype_system_id_state,
2912
- handle_bogus_doctype_state,
2913
- handle_cdata_state,
2914
- handle_processing_instruction_state,
2915
- };
2916
-
2917
- bool gumbo_lex(GumboParser* parser, GumboToken* output) {
2918
- // Because of the spec requirements that...
2919
- //
2920
- // 1. Tokens be handled immediately by the parser upon emission.
2921
- // 2. Some states (eg. CDATA, or various error conditions) require the
2922
- // emission of multiple tokens in the same states.
2923
- // 3. The tokenizer often has to reconsume the same character in a different
2924
- // state.
2925
- //
2926
- // ...all state must be held in the GumboTokenizer struct instead of in local
2927
- // variables in this function. That allows us to return from this method with
2928
- // a token, and then immediately jump back to the same state with the same
2929
- // input if we need to return a different token. The various emit_* functions
2930
- // are responsible for changing state (eg. flushing the chardata buffer,
2931
- // reading the next input character) to avoid an infinite loop.
2932
- GumboTokenizerState* tokenizer = parser->_tokenizer_state;
2933
-
2934
- if (tokenizer->_buffered_emit_char != kGumboNoChar) {
2935
- tokenizer->_reconsume_current_input = true;
2936
- emit_char(parser, tokenizer->_buffered_emit_char, output);
2937
- // And now that we've avoided advancing the input, make sure we set
2938
- // _reconsume_current_input back to false to make sure the *next* character
2939
- // isn't consumed twice.
2940
- tokenizer->_reconsume_current_input = false;
2941
- tokenizer->_buffered_emit_char = kGumboNoChar;
2942
- return true;
2943
- }
2944
-
2945
- if (maybe_emit_from_temporary_buffer(parser, output)) {
2946
- return true;
2947
- }
2948
-
2949
- while (1) {
2950
- assert(!tokenizer->_temporary_buffer_emit);
2951
- assert(tokenizer->_buffered_emit_char == kGumboNoChar);
2952
- int c = utf8iterator_current(&tokenizer->_input);
2953
- gumbo_debug(
2954
- "Lexing character '%c' (%d) in state %d.\n", c, c, tokenizer->_state);
2955
- StateResult result =
2956
- dispatch_table[tokenizer->_state](parser, tokenizer, c, output);
2957
- // We need to clear reconsume_current_input before returning to prevent
2958
- // certain infinite loop states.
2959
- bool should_advance = !tokenizer->_reconsume_current_input;
2960
- tokenizer->_reconsume_current_input = false;
2961
-
2962
- if (result == RETURN_SUCCESS) {
2963
- return true;
2964
- } else if (result == RETURN_ERROR) {
2965
- return false;
2966
- }
2967
-
2968
- if (should_advance) {
2969
- utf8iterator_next(&tokenizer->_input);
2970
- }
2971
- }
2972
- }
2973
-
2974
- void gumbo_token_destroy(GumboParser* parser, GumboToken* token) {
2975
- if (!token) return;
2976
-
2977
- switch (token->type) {
2978
- case GUMBO_TOKEN_DOCTYPE:
2979
- gumbo_parser_deallocate(parser, (void*) token->v.doc_type.name);
2980
- gumbo_parser_deallocate(
2981
- parser, (void*) token->v.doc_type.public_identifier);
2982
- gumbo_parser_deallocate(
2983
- parser, (void*) token->v.doc_type.system_identifier);
2984
- return;
2985
- case GUMBO_TOKEN_START_TAG:
2986
- for (unsigned int i = 0; i < token->v.start_tag.attributes.length; ++i) {
2987
- GumboAttribute* attr = token->v.start_tag.attributes.data[i];
2988
- if (attr) {
2989
- // May have been nulled out if this token was merged with another.
2990
- gumbo_destroy_attribute(parser, attr);
2991
- }
2992
- }
2993
- gumbo_parser_deallocate(
2994
- parser, (void*) token->v.start_tag.attributes.data);
2995
- return;
2996
- case GUMBO_TOKEN_COMMENT:
2997
- case GUMBO_TOKEN_PROCESSING_INSTRUCTION:
2998
- gumbo_parser_deallocate(parser, (void*) token->v.text);
2999
- return;
3000
- default:
3001
- return;
3002
- }
3003
- }