1 /* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 * Copyright (C) 2010 Google, Inc. All Rights Reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #ifndef HTMLTokenizer_h 28 #define HTMLTokenizer_h 29 30 #include "core/html/parser/HTMLParserOptions.h" 31 #include "core/html/parser/HTMLToken.h" 32 #include "core/html/parser/InputStreamPreprocessor.h" 33 #include "core/platform/text/SegmentedString.h" 34 35 namespace WebCore { 36 37 class HTMLTokenizer { 38 WTF_MAKE_NONCOPYABLE(HTMLTokenizer); 39 WTF_MAKE_FAST_ALLOCATED; 40 public: 41 static PassOwnPtr<HTMLTokenizer> create(const HTMLParserOptions& options) { return adoptPtr(new HTMLTokenizer(options)); } 42 ~HTMLTokenizer(); 43 44 void reset(); 45 46 enum State { 47 DataState, 48 CharacterReferenceInDataState, 49 RCDATAState, 50 CharacterReferenceInRCDATAState, 51 RAWTEXTState, 52 ScriptDataState, 53 PLAINTEXTState, 54 TagOpenState, 55 EndTagOpenState, 56 TagNameState, 57 RCDATALessThanSignState, 58 RCDATAEndTagOpenState, 59 RCDATAEndTagNameState, 60 RAWTEXTLessThanSignState, 61 RAWTEXTEndTagOpenState, 62 RAWTEXTEndTagNameState, 63 ScriptDataLessThanSignState, 64 ScriptDataEndTagOpenState, 65 ScriptDataEndTagNameState, 66 ScriptDataEscapeStartState, 67 ScriptDataEscapeStartDashState, 68 ScriptDataEscapedState, 69 ScriptDataEscapedDashState, 70 ScriptDataEscapedDashDashState, 71 ScriptDataEscapedLessThanSignState, 72 ScriptDataEscapedEndTagOpenState, 73 ScriptDataEscapedEndTagNameState, 74 ScriptDataDoubleEscapeStartState, 75 ScriptDataDoubleEscapedState, 76 ScriptDataDoubleEscapedDashState, 77 ScriptDataDoubleEscapedDashDashState, 78 ScriptDataDoubleEscapedLessThanSignState, 79 ScriptDataDoubleEscapeEndState, 80 BeforeAttributeNameState, 81 AttributeNameState, 82 AfterAttributeNameState, 83 BeforeAttributeValueState, 84 AttributeValueDoubleQuotedState, 85 AttributeValueSingleQuotedState, 86 AttributeValueUnquotedState, 87 CharacterReferenceInAttributeValueState, 88 AfterAttributeValueQuotedState, 89 SelfClosingStartTagState, 90 BogusCommentState, 91 // The ContinueBogusCommentState is not in the HTML5 spec, but we use 92 // it internally to keep track of whether we've started the bogus 93 // comment token yet. 94 ContinueBogusCommentState, 95 MarkupDeclarationOpenState, 96 CommentStartState, 97 CommentStartDashState, 98 CommentState, 99 CommentEndDashState, 100 CommentEndState, 101 CommentEndBangState, 102 DOCTYPEState, 103 BeforeDOCTYPENameState, 104 DOCTYPENameState, 105 AfterDOCTYPENameState, 106 AfterDOCTYPEPublicKeywordState, 107 BeforeDOCTYPEPublicIdentifierState, 108 DOCTYPEPublicIdentifierDoubleQuotedState, 109 DOCTYPEPublicIdentifierSingleQuotedState, 110 AfterDOCTYPEPublicIdentifierState, 111 BetweenDOCTYPEPublicAndSystemIdentifiersState, 112 AfterDOCTYPESystemKeywordState, 113 BeforeDOCTYPESystemIdentifierState, 114 DOCTYPESystemIdentifierDoubleQuotedState, 115 DOCTYPESystemIdentifierSingleQuotedState, 116 AfterDOCTYPESystemIdentifierState, 117 BogusDOCTYPEState, 118 CDATASectionState, 119 // These CDATA states are not in the HTML5 spec, but we use them internally. 120 CDATASectionRightSquareBracketState, 121 CDATASectionDoubleRightSquareBracketState, 122 }; 123 124 struct Checkpoint { 125 HTMLParserOptions options; 126 State state; 127 UChar additionalAllowedCharacter; 128 bool skipNextNewLine; 129 bool forceNullCharacterReplacement; 130 bool shouldAllowCDATA; 131 132 Checkpoint() 133 : options(0) 134 , state() 135 , additionalAllowedCharacter('\0') 136 , skipNextNewLine(false) 137 , forceNullCharacterReplacement(false) 138 , shouldAllowCDATA(false) 139 { 140 } 141 }; 142 143 bool canCreateCheckpoint() const; 144 void createCheckpoint(Checkpoint&) const; 145 void restoreFromCheckpoint(const Checkpoint&); 146 147 // This function returns true if it emits a token. Otherwise, callers 148 // must provide the same (in progress) token on the next call (unless 149 // they call reset() first). 150 bool nextToken(SegmentedString&, HTMLToken&); 151 152 // Returns a copy of any characters buffered internally by the tokenizer. 153 // The tokenizer buffers characters when searching for the </script> token 154 // that terminates a script element. 155 String bufferedCharacters() const; 156 157 size_t numberOfBufferedCharacters() const 158 { 159 // Notice that we add 2 to the length of the m_temporaryBuffer to 160 // account for the "</" characters, which are effecitvely buffered in 161 // the tokenizer's state machine. 162 return m_temporaryBuffer.size() ? m_temporaryBuffer.size() + 2 : 0; 163 } 164 165 // Updates the tokenizer's state according to the given tag name. This is 166 // an approximation of how the tree builder would update the tokenizer's 167 // state. This method is useful for approximating HTML tokenization. To 168 // get exactly the correct tokenization, you need the real tree builder. 169 // 170 // The main failures in the approximation are as follows: 171 // 172 // * The first set of character tokens emitted for a <pre> element might 173 // contain an extra leading newline. 174 // * The replacement of U+0000 with U+FFFD will not be sensitive to the 175 // tree builder's insertion mode. 176 // * CDATA sections in foreign content will be tokenized as bogus comments 177 // instead of as character tokens. 178 // 179 void updateStateFor(const AtomicString& tagName); 180 181 bool forceNullCharacterReplacement() const { return m_forceNullCharacterReplacement; } 182 void setForceNullCharacterReplacement(bool value) { m_forceNullCharacterReplacement = value; } 183 184 bool shouldAllowCDATA() const { return m_shouldAllowCDATA; } 185 void setShouldAllowCDATA(bool value) { m_shouldAllowCDATA = value; } 186 187 State state() const { return m_state; } 188 void setState(State state) { m_state = state; } 189 190 inline bool shouldSkipNullCharacters() const 191 { 192 return !m_forceNullCharacterReplacement 193 && (m_state == HTMLTokenizer::DataState 194 || m_state == HTMLTokenizer::RCDATAState 195 || m_state == HTMLTokenizer::RAWTEXTState); 196 } 197 198 private: 199 explicit HTMLTokenizer(const HTMLParserOptions&); 200 201 inline bool processEntity(SegmentedString&); 202 203 inline void parseError(); 204 205 inline void bufferCharacter(UChar character) 206 { 207 ASSERT(character != kEndOfFileMarker); 208 m_token->ensureIsCharacterToken(); 209 m_token->appendToCharacter(character); 210 } 211 212 inline bool emitAndResumeIn(SegmentedString& source, State state) 213 { 214 saveEndTagNameIfNeeded(); 215 m_state = state; 216 source.advanceAndUpdateLineNumber(); 217 return true; 218 } 219 220 inline bool emitAndReconsumeIn(SegmentedString&, State state) 221 { 222 saveEndTagNameIfNeeded(); 223 m_state = state; 224 return true; 225 } 226 227 inline bool emitEndOfFile(SegmentedString& source) 228 { 229 if (haveBufferedCharacterToken()) 230 return true; 231 m_state = HTMLTokenizer::DataState; 232 source.advanceAndUpdateLineNumber(); 233 m_token->clear(); 234 m_token->makeEndOfFile(); 235 return true; 236 } 237 238 inline bool flushEmitAndResumeIn(SegmentedString&, State); 239 240 // Return whether we need to emit a character token before dealing with 241 // the buffered end tag. 242 inline bool flushBufferedEndTag(SegmentedString&); 243 inline bool temporaryBufferIs(const String&); 244 245 // Sometimes we speculatively consume input characters and we don't 246 // know whether they represent end tags or RCDATA, etc. These 247 // functions help manage these state. 248 inline void addToPossibleEndTag(LChar cc); 249 250 inline void saveEndTagNameIfNeeded() 251 { 252 ASSERT(m_token->type() != HTMLToken::Uninitialized); 253 if (m_token->type() == HTMLToken::StartTag) 254 m_appropriateEndTagName = m_token->name(); 255 } 256 inline bool isAppropriateEndTag(); 257 258 259 inline bool haveBufferedCharacterToken() 260 { 261 return m_token->type() == HTMLToken::Character; 262 } 263 264 State m_state; 265 bool m_forceNullCharacterReplacement; 266 bool m_shouldAllowCDATA; 267 268 // m_token is owned by the caller. If nextToken is not on the stack, 269 // this member might be pointing to unallocated memory. 270 HTMLToken* m_token; 271 272 // http://www.whatwg.org/specs/web-apps/current-work/#additional-allowed-character 273 UChar m_additionalAllowedCharacter; 274 275 // http://www.whatwg.org/specs/web-apps/current-work/#preprocessing-the-input-stream 276 InputStreamPreprocessor<HTMLTokenizer> m_inputStreamPreprocessor; 277 278 Vector<UChar, 32> m_appropriateEndTagName; 279 280 // http://www.whatwg.org/specs/web-apps/current-work/#temporary-buffer 281 Vector<LChar, 32> m_temporaryBuffer; 282 283 // We occationally want to emit both a character token and an end tag 284 // token (e.g., when lexing script). We buffer the name of the end tag 285 // token here so we remember it next time we re-enter the tokenizer. 286 Vector<LChar, 32> m_bufferedEndTagName; 287 288 HTMLParserOptions m_options; 289 }; 290 291 } 292 293 #endif 294