root/trunk/WebCore/html/HTMLTokenizer.cpp

Revision 55207, 82.6 KB (checked in by pkasting@chromium.org, 3 weeks ago)

[Chromium API] Disambiguate allowJavaScript from didNotAllowScript
 https://bugs.webkit.org/show_bug.cgi?id=35205

Patch by Adam Barth < abarth@webkit.org> on 2010-02-24
Reviewed by Darin Fisher.

WebCore:

For clients that want to show a user interface element when JavaScript
was blocked on a page, we need to disambiguate between querying the
client for whether JavaScript is enabled from actually failing to
execute some script.

This patch adds a new FrameLoaderClient callback for when WebCore would
like to execute JavaScript but fails to because JavaScript is disabled.

This patch also touches every client of canExecuteScripts so they can
indicate whether we should make this callback. I was hoping there was
a better choke point, but my first two attempts were wrong in subtle
ways. pkasting points out that this will be easy to screw up in the
future, so it's better to make all the clients be explicit.

* WebCore.PluginHostProcess.exp:
* bindings/ScriptControllerBase.cpp:
(WebCore::ScriptController::canExecuteScripts):
(WebCore::ScriptController::executeScript):
* bindings/js/JSEventListener.cpp:
(WebCore::JSEventListener::handleEvent):
* bindings/js/JSLazyEventListener.cpp:
(WebCore::JSLazyEventListener::initializeJSFunction):
* bindings/js/ScheduledAction.cpp:
(WebCore::ScheduledAction::execute):
* bindings/js/ScriptController.cpp:
(WebCore::ScriptController::bindingRootObject):
(WebCore::ScriptController::windowScriptNPObject):
(WebCore::ScriptController::jsObjectForPluginElement):
(WebCore::ScriptController::executeScriptInWorld):
* bindings/js/ScriptController.h:
(WebCore::):
* bindings/js/ScriptControllerMac.mm:
(WebCore::ScriptController::windowScriptObject):
* bindings/js/ScriptDebugServer.cpp:
(WebCore::ScriptDebugServer::setJavaScriptPaused):
* bindings/js/ScriptEventListener.cpp:
(WebCore::createAttributeEventListener):
* bindings/js/ScriptState.cpp:
(WebCore::scriptStateFromNode):
* bindings/v8/ScriptController.cpp:
(WebCore::ScriptController::windowScriptNPObject):
(WebCore::ScriptController::createScriptObjectForPluginElement):
* bindings/v8/ScriptController.h:
(WebCore::):
* bindings/v8/ScriptEventListener.cpp:
(WebCore::createAttributeEventListener):
* bindings/v8/V8Proxy.cpp:
(WebCore::V8Proxy::retrieve):
* dom/ScriptElement.cpp:
(WebCore::ScriptElementData::evaluateScript):
* dom/XMLTokenizerLibxml2.cpp:
(WebCore::XMLTokenizer::startElementNs):
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::controls):
* html/HTMLTokenizer.cpp:
(WebCore::HTMLTokenizer::parseTag):
(WebCore::HTMLTokenizer::processToken):
* inspector/InspectorController.cpp:
(WebCore::canPassNodeToJavaScript):
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::isProcessingUserGesture):
(WebCore::FrameLoader::open):
(WebCore::FrameLoader::dispatchDidClearWindowObjectsInAllWorlds):
(WebCore::FrameLoader::dispatchDidClearWindowObjectInWorld):
* loader/FrameLoaderClient.h:
(WebCore::FrameLoaderClient::didNotAllowScript):

WebKit/chromium:

Plumb didNotAllowScript through Chromium's WebKit API.

* public/WebFrameClient.h:
(WebKit::WebFrameClient::didNotAllowScript):
* src/DebuggerAgentImpl.cpp:
(WebKit::DebuggerAgentImpl::createUtilityContext):
* src/FrameLoaderClientImpl.cpp:
(WebKit::FrameLoaderClientImpl::didNotAllowScript):
* src/FrameLoaderClientImpl.h:
* src/WebFrameImpl.cpp:
(WebKit::WebFrameImpl::bindToWindowObject):

WebKit/mac:

Make these two callsites explicit about not running script immediately.

* Plugins/Hosted/NetscapePluginInstanceProxy.mm:
(WebKit::NetscapePluginInstanceProxy::getWindowNPObject):
(WebKit::NetscapePluginInstanceProxy::demarshalValueFromArray):

  • Property svn:eol-style set to native
Line 
1/*
2    Copyright (C) 1997 Martin Jones (mjones@kde.org)
3              (C) 1997 Torben Weis (weis@kde.org)
4              (C) 1998 Waldo Bastian (bastian@kde.org)
5              (C) 1999 Lars Knoll (knoll@kde.org)
6              (C) 1999 Antti Koivisto (koivisto@kde.org)
7              (C) 2001 Dirk Mueller (mueller@kde.org)
8    Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
9    Copyright (C) 2005, 2006 Alexey Proskuryakov (ap@nypop.com)
10    Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
11
12    This library is free software; you can redistribute it and/or
13    modify it under the terms of the GNU Library General Public
14    License as published by the Free Software Foundation; either
15    version 2 of the License, or (at your option) any later version.
16
17    This library is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20    Library General Public License for more details.
21
22    You should have received a copy of the GNU Library General Public License
23    along with this library; see the file COPYING.LIB.  If not, write to
24    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25    Boston, MA 02110-1301, USA.
26*/
27
28#include "config.h"
29#include "HTMLTokenizer.h"
30
31#include "CSSHelper.h"
32#include "Cache.h"
33#include "CachedScript.h"
34#include "DocLoader.h"
35#include "DocumentFragment.h"
36#include "Event.h"
37#include "EventNames.h"
38#include "Frame.h"
39#include "FrameLoader.h"
40#include "FrameView.h"
41#include "HTMLElement.h"
42#include "HTMLNames.h"
43#include "HTMLParser.h"
44#include "HTMLScriptElement.h"
45#include "HTMLViewSourceDocument.h"
46#include "ImageLoader.h"
47#include "InspectorTimelineAgent.h"
48#include "MappedAttribute.h"
49#include "Page.h"
50#include "PreloadScanner.h"
51#include "ScriptController.h"
52#include "ScriptSourceCode.h"
53#include "ScriptValue.h"
54#include "XSSAuditor.h"
55#include <wtf/ASCIICType.h>
56#include <wtf/CurrentTime.h>
57
58#include "HTMLEntityNames.c"
59
60#define PRELOAD_SCANNER_ENABLED 1
61// #define INSTRUMENT_LAYOUT_SCHEDULING 1
62
63using namespace WTF;
64using namespace std;
65
66namespace WebCore {
67
68using namespace HTMLNames;
69
70#if MOBILE
71// The mobile device needs to be responsive, as such the tokenizer chunk size is reduced.
72// This value is used to define how many characters the tokenizer will process before
73// yeilding control.
74static const int defaultTokenizerChunkSize = 256;
75#else
76static const int defaultTokenizerChunkSize = 4096;
77#endif
78
79#if MOBILE
80// As the chunks are smaller (above), the tokenizer should not yield for as long a period, otherwise
81// it will take way to long to load a page.
82static const double defaultTokenizerTimeDelay = 0.300;
83#else
84// FIXME: We would like this constant to be 200ms.
85// Yielding more aggressively results in increased responsiveness and better incremental rendering.
86// It slows down overall page-load on slower machines, though, so for now we set a value of 500.
87static const double defaultTokenizerTimeDelay = 0.500;
88#endif
89
90static const char commentStart [] = "<!--";
91static const char doctypeStart [] = "<!doctype";
92static const char publicStart [] = "public";
93static const char systemStart [] = "system";
94static const char scriptEnd [] = "</script";
95static const char xmpEnd [] = "</xmp";
96static const char styleEnd [] =  "</style";
97static const char textareaEnd [] = "</textarea";
98static const char titleEnd [] = "</title";
99static const char iframeEnd [] = "</iframe";
100
101// Full support for MS Windows extensions to Latin-1.
102// Technically these extensions should only be activated for pages
103// marked "windows-1252" or "cp1252", but
104// in the standard Microsoft way, these extensions infect hundreds of thousands
105// of web pages.  Note that people with non-latin-1 Microsoft extensions
106// are SOL.
107//
108// See: http://www.microsoft.com/globaldev/reference/WinCP.asp
109//      http://www.bbsinc.com/iso8859.html
110//      http://www.obviously.com/
111//
112// There may be better equivalents
113
114// We only need this for entities. For non-entity text, we handle this in the text encoding.
115
116static const UChar windowsLatin1ExtensionArray[32] = {
117    0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, // 80-87
118    0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F, // 88-8F
119    0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 90-97
120    0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178  // 98-9F
121};
122
123static inline UChar fixUpChar(UChar c)
124{
125    if ((c & ~0x1F) != 0x0080)
126        return c;
127    return windowsLatin1ExtensionArray[c - 0x80];
128}
129
130static inline bool tagMatch(const char* s1, const UChar* s2, unsigned length)
131{
132    for (unsigned i = 0; i != length; ++i) {
133        unsigned char c1 = s1[i];
134        unsigned char uc1 = toASCIIUpper(static_cast<char>(c1));
135        UChar c2 = s2[i];
136        if (c1 != c2 && uc1 != c2)
137            return false;
138    }
139    return true;
140}
141
142inline void Token::addAttribute(AtomicString& attrName, const AtomicString& attributeValue, bool viewSourceMode)
143{
144    if (!attrName.isEmpty()) {
145        ASSERT(!attrName.contains('/'));
146        RefPtr<MappedAttribute> a = MappedAttribute::create(attrName, attributeValue);
147        if (!attrs) {
148            attrs = NamedMappedAttrMap::create();
149            attrs->reserveInitialCapacity(10);
150        }
151        attrs->insertAttribute(a.release(), viewSourceMode);
152    }
153   
154    attrName = emptyAtom;
155}
156
157// ----------------------------------------------------------------------------
158
159HTMLTokenizer::HTMLTokenizer(HTMLDocument* doc, bool reportErrors)
160    : Tokenizer()
161    , m_buffer(0)
162    , m_scriptCode(0)
163    , m_scriptCodeSize(0)
164    , m_scriptCodeCapacity(0)
165    , m_scriptCodeResync(0)
166    , m_executingScript(0)
167    , m_requestingScript(false)
168    , m_hasScriptsWaitingForStylesheets(false)
169    , m_timer(this, &HTMLTokenizer::timerFired)
170    , m_externalScriptsTimer(this, &HTMLTokenizer::executeExternalScriptsTimerFired)
171    , m_doc(doc)
172    , m_parser(new HTMLParser(doc, reportErrors))
173    , m_inWrite(false)
174    , m_fragment(false)
175    , m_scriptingPermission(FragmentScriptingAllowed)
176{
177    begin();
178}
179
180HTMLTokenizer::HTMLTokenizer(HTMLViewSourceDocument* doc)
181    : Tokenizer(true)
182    , m_buffer(0)
183    , m_scriptCode(0)
184    , m_scriptCodeSize(0)
185    , m_scriptCodeCapacity(0)
186    , m_scriptCodeResync(0)
187    , m_executingScript(0)
188    , m_requestingScript(false)
189    , m_hasScriptsWaitingForStylesheets(false)
190    , m_timer(this, &HTMLTokenizer::timerFired)
191    , m_externalScriptsTimer(this, &HTMLTokenizer::executeExternalScriptsTimerFired)
192    , m_doc(doc)
193    , m_parser(0)
194    , m_inWrite(false)
195    , m_fragment(false)
196    , m_scriptingPermission(FragmentScriptingAllowed)
197{
198    begin();
199}
200
201HTMLTokenizer::HTMLTokenizer(DocumentFragment* frag, FragmentScriptingPermission scriptingPermission)
202    : m_buffer(0)
203    , m_scriptCode(0)
204    , m_scriptCodeSize(0)
205    , m_scriptCodeCapacity(0)
206    , m_scriptCodeResync(0)
207    , m_executingScript(0)
208    , m_requestingScript(false)
209    , m_hasScriptsWaitingForStylesheets(false)
210    , m_timer(this, &HTMLTokenizer::timerFired)
211    , m_externalScriptsTimer(this, &HTMLTokenizer::executeExternalScriptsTimerFired)
212    , m_doc(frag->document())
213    , m_parser(new HTMLParser(frag, scriptingPermission))
214    , m_inWrite(false)
215    , m_fragment(true)
216    , m_scriptingPermission(scriptingPermission)
217{
218    begin();
219}
220
221void HTMLTokenizer::reset()
222{
223    ASSERT(m_executingScript == 0);
224
225    while (!m_pendingScripts.isEmpty()) {
226        CachedScript* cs = m_pendingScripts.first().get();
227        m_pendingScripts.removeFirst();
228        ASSERT(cache()->disabled() || cs->accessCount() > 0);
229        cs->removeClient(this);
230    }
231
232    fastFree(m_buffer);
233    m_buffer = m_dest = 0;
234    m_bufferSize = 0;
235
236    fastFree(m_scriptCode);
237    m_scriptCode = 0;
238    m_scriptCodeSize = m_scriptCodeCapacity = m_scriptCodeResync = 0;
239
240    m_timer.stop();
241    m_externalScriptsTimer.stop();
242
243    m_state.setAllowYield(false);
244    m_state.setForceSynchronous(false);
245
246    m_currentToken.reset();
247    m_doctypeToken.reset();
248    m_doctypeSearchCount = 0;
249    m_doctypeSecondarySearchCount = 0;
250    m_hasScriptsWaitingForStylesheets = false;
251}
252
253void HTMLTokenizer::begin()
254{
255    m_executingScript = 0;
256    m_requestingScript = false;
257    m_hasScriptsWaitingForStylesheets = false;
258    m_state.setLoadingExtScript(false);
259    reset();
260    m_bufferSize = 254;
261    m_buffer = static_cast<UChar*>(fastMalloc(sizeof(UChar) * 254));
262    m_dest = m_buffer;
263    tquote = NoQuote;
264    searchCount = 0;
265    m_state.setEntityState(NoEntity);
266    m_scriptTagSrcAttrValue = String();
267    m_pendingSrc.clear();
268    m_currentPrependingSrc = 0;
269    m_noMoreData = false;
270    m_brokenComments = false;
271    m_brokenServer = false;
272    m_lineNumber = 0;
273    m_currentScriptTagStartLineNumber = 0;
274    m_currentTagStartLineNumber = 0;
275    m_state.setForceSynchronous(false);
276
277    Page* page = m_doc->page();
278    if (page && page->hasCustomHTMLTokenizerTimeDelay())
279        m_tokenizerTimeDelay = page->customHTMLTokenizerTimeDelay();
280    else
281        m_tokenizerTimeDelay = defaultTokenizerTimeDelay;
282
283    if (page && page->hasCustomHTMLTokenizerChunkSize())
284        m_tokenizerChunkSize = page->customHTMLTokenizerChunkSize();
285    else
286        m_tokenizerChunkSize = defaultTokenizerChunkSize;
287}
288
289void HTMLTokenizer::setForceSynchronous(bool force)
290{
291    m_state.setForceSynchronous(force);
292}
293
294HTMLTokenizer::State HTMLTokenizer::processListing(SegmentedString list, State state)
295{
296    // This function adds the listing 'list' as
297    // preformatted text-tokens to the token-collection
298    while (!list.isEmpty()) {
299        if (state.skipLF()) {
300            state.setSkipLF(false);
301            if (*list == '\n') {
302                list.advance();
303                continue;
304            }
305        }
306
307        checkBuffer();
308
309        if (*list == '\n' || *list == '\r') {
310            if (state.discardLF())
311                // Ignore this LF
312                state.setDiscardLF(false); // We have discarded 1 LF
313            else
314                *m_dest++ = '\n';
315
316            /* Check for MS-DOS CRLF sequence */
317            if (*list == '\r')
318                state.setSkipLF(true);
319
320            list.advance();
321        } else {
322            state.setDiscardLF(false);
323            *m_dest++ = *list;
324            list.advance();
325        }
326    }
327
328    return state;
329}
330
331HTMLTokenizer::State HTMLTokenizer::parseNonHTMLText(SegmentedString& src, State state)
332{
333    ASSERT(state.inTextArea() || state.inTitle() || state.inIFrame() || !state.hasEntityState());
334    ASSERT(!state.hasTagState());
335    ASSERT(state.inXmp() + state.inTextArea() + state.inTitle() + state.inStyle() + state.inScript() + state.inIFrame() == 1);
336    if (state.inScript() && !m_currentScriptTagStartLineNumber)
337        m_currentScriptTagStartLineNumber = m_lineNumber;
338
339    if (state.inComment()) 
340        state = parseComment(src, state);
341
342    int lastDecodedEntityPosition = -1;
343    while (!src.isEmpty()) {
344        checkScriptBuffer();
345        UChar ch = *src;
346
347        if (!m_scriptCodeResync && !m_brokenComments &&
348            !state.inXmp() && ch == '-' && m_scriptCodeSize >= 3 && !src.escaped() &&
349            m_scriptCode[m_scriptCodeSize - 3] == '<' && m_scriptCode[m_scriptCodeSize - 2] == '!' && m_scriptCode[m_scriptCodeSize - 1] == '-' &&
350            (lastDecodedEntityPosition < m_scriptCodeSize - 3)) {
351            state.setInComment(true);
352            state = parseComment(src, state);
353            continue;
354        }
355        if (m_scriptCodeResync && !tquote && ch == '>') {
356            src.advancePastNonNewline();
357            m_scriptCodeSize = m_scriptCodeResync - 1;
358            m_scriptCodeResync = 0;
359            m_scriptCode[m_scriptCodeSize] = m_scriptCode[m_scriptCodeSize + 1] = 0;
360            if (state.inScript())
361                state = scriptHandler(state);
362            else {
363                state = processListing(SegmentedString(m_scriptCode, m_scriptCodeSize), state);
364                processToken();
365                if (state.inStyle()) { 
366                    m_currentToken.tagName = styleTag.localName();
367                    m_currentToken.beginTag = false;
368                } else if (state.inTextArea()) { 
369                    m_currentToken.tagName = textareaTag.localName();
370                    m_currentToken.beginTag = false;
371                } else if (state.inTitle()) { 
372                    m_currentToken.tagName = titleTag.localName();
373                    m_currentToken.beginTag = false;
374                } else if (state.inXmp()) {
375                    m_currentToken.tagName = xmpTag.localName();
376                    m_currentToken.beginTag = false;
377                } else if (state.inIFrame()) {
378                    m_currentToken.tagName = iframeTag.localName();
379                    m_currentToken.beginTag = false;
380                }
381                processToken();
382                state.setInStyle(false);
383                state.setInScript(false);
384                state.setInTextArea(false);
385                state.setInTitle(false);
386                state.setInXmp(false);
387                state.setInIFrame(false);
388                tquote = NoQuote;
389                m_scriptCodeSize = m_scriptCodeResync = 0;
390            }
391            return state;
392        }
393        // possible end of tagname, lets check.
394        if (!m_scriptCodeResync && !state.escaped() && !src.escaped() && (ch == '>' || ch == '/' || isASCIISpace(ch)) &&
395             m_scriptCodeSize >= m_searchStopperLength &&
396             tagMatch(m_searchStopper, m_scriptCode + m_scriptCodeSize - m_searchStopperLength, m_searchStopperLength) &&
397             (lastDecodedEntityPosition < m_scriptCodeSize - m_searchStopperLength)) {
398            m_scriptCodeResync = m_scriptCodeSize-m_searchStopperLength+1;
399            tquote = NoQuote;
400            continue;
401        }
402        if (m_scriptCodeResync && !state.escaped()) {
403            if (ch == '\"')
404                tquote = (tquote == NoQuote) ? DoubleQuote : ((tquote == SingleQuote) ? SingleQuote : NoQuote);
405            else if (ch == '\'')
406                tquote = (tquote == NoQuote) ? SingleQuote : (tquote == DoubleQuote) ? DoubleQuote : NoQuote;
407            else if (tquote != NoQuote && (ch == '\r' || ch == '\n'))
408                tquote = NoQuote;
409        }
410        state.setEscaped(!state.escaped() && ch == '\\');
411        if (!m_scriptCodeResync && (state.inTextArea() || state.inTitle() || state.inIFrame()) && !src.escaped() && ch == '&') {
412            UChar* scriptCodeDest = m_scriptCode + m_scriptCodeSize;
413            src.advancePastNonNewline();
414            state = parseEntity(src, scriptCodeDest, state, m_cBufferPos, true, false);
415            if (scriptCodeDest == m_scriptCode + m_scriptCodeSize)
416                lastDecodedEntityPosition = m_scriptCodeSize;
417            else
418                m_scriptCodeSize = scriptCodeDest - m_scriptCode;
419        } else {
420            m_scriptCode[m_scriptCodeSize++] = ch;
421            src.advance(m_lineNumber);
422        }
423    }
424
425    return state;
426}
427   
428HTMLTokenizer::State HTMLTokenizer::scriptHandler(State state)
429{
430    // We are inside a <script>
431    bool doScriptExec = false;
432    int startLine = m_currentScriptTagStartLineNumber + 1; // Script line numbers are 1 based, HTMLTokenzier line numbers are 0 based
433   
434    // Reset m_currentScriptTagStartLineNumber to indicate that we've finished parsing the current script element
435    m_currentScriptTagStartLineNumber = 0;
436
437    // (Bugzilla 3837) Scripts following a frameset element should not execute or,
438    // in the case of extern scripts, even load.
439    bool followingFrameset = (m_doc->body() && m_doc->body()->hasTagName(framesetTag));
440 
441    CachedScript* cs = 0;
442    // don't load external scripts for standalone documents (for now)
443    if (!inViewSourceMode()) {
444        if (!m_scriptTagSrcAttrValue.isEmpty() && m_doc->frame()) {
445            // forget what we just got; load from src url instead
446            if (!m_parser->skipMode() && !followingFrameset) {
447#ifdef INSTRUMENT_LAYOUT_SCHEDULING
448                if (!m_doc->ownerElement())
449                    printf("Requesting script at time %d\n", m_doc->elapsedTime());
450#endif
451                // The parser might have been stopped by for example a window.close call in an earlier script.
452                // If so, we don't want to load scripts.
453                if (!m_parserStopped && m_scriptNode->dispatchBeforeLoadEvent(m_scriptTagSrcAttrValue) &&
454                    (cs = m_doc->docLoader()->requestScript(m_scriptTagSrcAttrValue, m_scriptTagCharsetAttrValue)))
455                    m_pendingScripts.append(cs);
456                else
457                    m_scriptNode = 0;
458            } else
459                m_scriptNode = 0;
460            m_scriptTagSrcAttrValue = String();
461        } else {
462            // Parse m_scriptCode containing <script> info
463            doScriptExec = m_scriptNode->shouldExecuteAsJavaScript();
464#if ENABLE(XHTMLMP)
465            if (!doScriptExec)
466                m_doc->setShouldProcessNoscriptElement(true);
467#endif
468            m_scriptNode = 0;
469        }
470    }
471
472    state = processListing(SegmentedString(m_scriptCode, m_scriptCodeSize), state);
473    RefPtr<Node> node = processToken();
474   
475    if (node && m_scriptingPermission == FragmentScriptingNotAllowed) {
476        ExceptionCode ec;
477        node->remove(ec);
478        node = 0;
479    }
480   
481    String scriptString = node ? node->textContent() : "";
482    m_currentToken.tagName = scriptTag.localName();
483    m_currentToken.beginTag = false;
484    processToken();
485
486    state.setInScript(false);
487    m_scriptCodeSize = m_scriptCodeResync = 0;
488   
489    // FIXME: The script should be syntax highlighted.
490    if (inViewSourceMode())
491        return state;
492
493    SegmentedString* savedPrependingSrc = m_currentPrependingSrc;
494    SegmentedString prependingSrc;
495    m_currentPrependingSrc = &prependingSrc;
496
497    if (!m_parser->skipMode() && !followingFrameset) {
498        if (cs) {
499            if (savedPrependingSrc)
500                savedPrependingSrc->append(m_src);
501            else
502                m_pendingSrc.prepend(m_src);
503            setSrc(SegmentedString());
504
505            // the ref() call below may call notifyFinished if the script is already in cache,
506            // and that mucks with the state directly, so we must write it back to the object.
507            m_state = state;
508            bool savedRequestingScript = m_requestingScript;
509            m_requestingScript = true;
510            cs->addClient(this);
511            m_requestingScript = savedRequestingScript;
512            state = m_state;
513            // will be 0 if script was already loaded and ref() executed it
514            if (!m_pendingScripts.isEmpty())
515                state.setLoadingExtScript(true);
516        } else if (!m_fragment && doScriptExec) {
517            if (!m_executingScript)
518                m_pendingSrc.prepend(m_src);
519            else
520                prependingSrc = m_src;
521            setSrc(SegmentedString());
522            state = scriptExecution(ScriptSourceCode(scriptString, m_doc->frame() ? m_doc->frame()->document()->url() : KURL(), startLine), state);
523        }
524    }
525
526    if (!m_executingScript && !state.loadingExtScript()) {
527        m_src.append(m_pendingSrc);
528        m_pendingSrc.clear();
529    } else if (!prependingSrc.isEmpty()) {
530        // restore first so that the write appends in the right place
531        // (does not hurt to do it again below)
532        m_currentPrependingSrc = savedPrependingSrc;
533
534        // we need to do this slightly modified bit of one of the write() cases
535        // because we want to prepend to m_pendingSrc rather than appending
536        // if there's no previous prependingSrc
537        if (!m_pendingScripts.isEmpty()) {
538            if (m_currentPrependingSrc)
539                m_currentPrependingSrc->append(prependingSrc);
540            else
541                m_pendingSrc.prepend(prependingSrc);
542        } else {
543            m_state = state;
544            write(prependingSrc, false);
545            state = m_state;
546        }
547    }
548   
549#if PRELOAD_SCANNER_ENABLED
550    if (!m_pendingScripts.isEmpty() && !m_executingScript) {
551        if (!m_preloadScanner)
552            m_preloadScanner.set(new PreloadScanner(m_doc));
553        if (!m_preloadScanner->inProgress()) {
554            m_preloadScanner->begin();
555            m_preloadScanner->write(m_pendingSrc);
556        }
557    }
558#endif
559    m_currentPrependingSrc = savedPrependingSrc;
560
561    return state;
562}
563
564HTMLTokenizer::State HTMLTokenizer::scriptExecution(const ScriptSourceCode& sourceCode, State state)
565{
566    if (m_fragment || !m_doc->frame())
567        return state;
568    m_executingScript++;
569   
570    SegmentedString* savedPrependingSrc = m_currentPrependingSrc;
571    SegmentedString prependingSrc;
572    m_currentPrependingSrc = &prependingSrc;
573
574#ifdef INSTRUMENT_LAYOUT_SCHEDULING
575    if (!m_doc->ownerElement())
576        printf("beginning script execution at %d\n", m_doc->elapsedTime());
577#endif
578
579    m_state = state;
580    m_doc->frame()->script()->executeScript(sourceCode);
581    state = m_state;
582
583    state.setAllowYield(true);
584
585#ifdef INSTRUMENT_LAYOUT_SCHEDULING
586    if (!m_doc->ownerElement())
587        printf("ending script execution at %d\n", m_doc->elapsedTime());
588#endif
589   
590    m_executingScript--;
591
592    if (!m_executingScript && !state.loadingExtScript()) {
593        m_pendingSrc.prepend(prependingSrc);       
594        m_src.append(m_pendingSrc);
595        m_pendingSrc.clear();
596    } else if (!prependingSrc.isEmpty()) {
597        // restore first so that the write appends in the right place
598        // (does not hurt to do it again below)
599        m_currentPrependingSrc = savedPrependingSrc;
600
601        // we need to do this slightly modified bit of one of the write() cases
602        // because we want to prepend to m_pendingSrc rather than appending
603        // if there's no previous prependingSrc
604        if (!m_pendingScripts.isEmpty()) {
605            if (m_currentPrependingSrc)
606                m_currentPrependingSrc->append(prependingSrc);
607            else
608                m_pendingSrc.prepend(prependingSrc);
609           
610#if PRELOAD_SCANNER_ENABLED
611            // We are stuck waiting for another script. Lets check the source that
612            // was just document.write()n for anything to load.
613            PreloadScanner documentWritePreloadScanner(m_doc);
614            documentWritePreloadScanner.begin();
615            documentWritePreloadScanner.write(prependingSrc);
616            documentWritePreloadScanner.end();
617#endif
618        } else {
619            m_state = state;
620            write(prependingSrc, false);
621            state = m_state;
622        }
623    }
624
625    m_currentPrependingSrc = savedPrependingSrc;
626   
627    return state;
628}
629
630HTMLTokenizer::State HTMLTokenizer::parseComment(SegmentedString& src, State state)
631{
632    // FIXME: Why does this code even run for comments inside <script> and <style>? This seems bogus.
633    checkScriptBuffer(src.length());
634    while (!src.isEmpty()) {
635        UChar ch = *src;
636        m_scriptCode[m_scriptCodeSize++] = ch;
637        if (ch == '>') {
638            bool handleBrokenComments = m_brokenComments && !(state.inScript() || state.inStyle());
639            int endCharsCount = 1; // start off with one for the '>' character
640            if (m_scriptCodeSize > 2 && m_scriptCode[m_scriptCodeSize-3] == '-' && m_scriptCode[m_scriptCodeSize-2] == '-') {
641                endCharsCount = 3;
642            } else if (m_scriptCodeSize > 3 && m_scriptCode[m_scriptCodeSize-4] == '-' && m_scriptCode[m_scriptCodeSize-3] == '-' && 
643                m_scriptCode[m_scriptCodeSize-2] == '!') {
644                // Other browsers will accept --!> as a close comment, even though it's
645                // not technically valid.
646                endCharsCount = 4;
647            }
648            if (handleBrokenComments || endCharsCount > 1) {
649                src.advancePastNonNewline();
650                if (!(state.inTitle() || state.inScript() || state.inXmp() || state.inTextArea() || state.inStyle() || state.inIFrame())) {
651                    checkScriptBuffer();
652                    m_scriptCode[m_scriptCodeSize] = 0;
653                    m_scriptCode[m_scriptCodeSize + 1] = 0;
654                    m_currentToken.tagName = commentAtom;
655                    m_currentToken.beginTag = true;
656                    state = processListing(SegmentedString(m_scriptCode, m_scriptCodeSize - endCharsCount), state);
657                    processToken();
658                    m_currentToken.tagName = commentAtom;
659                    m_currentToken.beginTag = false;
660                    processToken();
661                    m_scriptCodeSize = 0;
662                }
663                state.setInComment(false);
664                return state; // Finished parsing comment
665            }
666        }
667        src.advance(m_lineNumber);
668    }
669
670    return state;
671}
672
673HTMLTokenizer::State HTMLTokenizer::parseServer(SegmentedString& src, State state)
674{
675    checkScriptBuffer(src.length());
676    while (!src.isEmpty()) {
677        UChar ch = *src;
678        m_scriptCode[m_scriptCodeSize++] = ch;
679        if (ch == '>' && m_scriptCodeSize > 1 && m_scriptCode[m_scriptCodeSize - 2] == '%') {
680            src.advancePastNonNewline();
681            state.setInServer(false);
682            m_scriptCodeSize = 0;
683            return state; // Finished parsing server include
684        }
685        src.advance(m_lineNumber);
686    }
687    return state;
688}
689
690HTMLTokenizer::State HTMLTokenizer::parseProcessingInstruction(SegmentedString& src, State state)
691{
692    UChar oldchar = 0;
693    while (!src.isEmpty()) {
694        UChar chbegin = *src;
695        if (chbegin == '\'')
696            tquote = tquote == SingleQuote ? NoQuote : SingleQuote;
697        else if (chbegin == '\"')
698            tquote = tquote == DoubleQuote ? NoQuote : DoubleQuote;
699        // Look for '?>'
700        // Some crappy sites omit the "?" before it, so
701        // we look for an unquoted '>' instead. (IE compatible)
702        else if (chbegin == '>' && (!tquote || oldchar == '?')) {
703            // We got a '?>' sequence
704            state.setInProcessingInstruction(false);
705            src.advancePastNonNewline();
706            state.setDiscardLF(true);
707            return state; // Finished parsing comment!
708        }
709        src.advance(m_lineNumber);
710        oldchar = chbegin;
711    }
712   
713    return state;
714}
715
716HTMLTokenizer::State HTMLTokenizer::parseText(SegmentedString& src, State state)
717{
718    while (!src.isEmpty()) {
719        UChar cc = *src;
720
721        if (state.skipLF()) {
722            state.setSkipLF(false);
723            if (cc == '\n') {
724                src.advancePastNewline(m_lineNumber);
725                continue;
726            }
727        }
728
729        // do we need to enlarge the buffer?
730        checkBuffer();
731
732        if (cc == '\r') {
733            state.setSkipLF(true);
734            *m_dest++ = '\n';
735        } else
736            *m_dest++ = cc;
737        src.advance(m_lineNumber);
738    }
739
740    return state;
741}
742
743
744HTMLTokenizer::State HTMLTokenizer::parseEntity(SegmentedString& src, UChar*& dest, State state, unsigned& cBufferPos, bool start, bool parsingTag)
745{
746    if (start) {
747        cBufferPos = 0;
748        state.setEntityState(SearchEntity);
749        EntityUnicodeValue = 0;
750    }
751
752    while (!src.isEmpty()) {
753        UChar cc = *src;
754        switch (state.entityState()) {
755        case NoEntity:
756            ASSERT(state.entityState() != NoEntity);
757            return state;
758       
759        case SearchEntity:
760            if (cc == '#') {
761                m_cBuffer[cBufferPos++] = cc;
762                src.advancePastNonNewline();
763                state.setEntityState(NumericSearch);
764            } else
765                state.setEntityState(EntityName);
766            break;
767
768        case NumericSearch:
769            if (cc == 'x' || cc == 'X') {
770                m_cBuffer[cBufferPos++] = cc;
771                src.advancePastNonNewline();
772                state.setEntityState(Hexadecimal);
773            } else if (cc >= '0' && cc <= '9')
774                state.setEntityState(Decimal);
775            else
776                state.setEntityState(SearchSemicolon);
777            break;
778
779        case Hexadecimal: {
780            int ll = min(src.length(), 10 - cBufferPos);
781            while (ll--) {
782                cc = *src;
783                if (!((cc >= '0' && cc <= '9') || (cc >= 'a' && cc <= 'f') || (cc >= 'A' && cc <= 'F'))) {
784                    state.setEntityState(SearchSemicolon);
785                    break;
786                }
787                int digit;
788                if (cc < 'A')
789                    digit = cc - '0';
790                else
791                    digit = (cc - 'A' + 10) & 0xF; // handle both upper and lower case without a branch
792                EntityUnicodeValue = EntityUnicodeValue * 16 + digit;
793                m_cBuffer[cBufferPos++] = cc;
794                src.advancePastNonNewline();
795            }
796            if (cBufferPos == 10) 
797                state.setEntityState(SearchSemicolon);
798            break;
799        }
800        case Decimal:
801        {
802            int ll = min(src.length(), 9-cBufferPos);
803            while (ll--) {
804                cc = *src;
805
806                if (!(cc >= '0' && cc <= '9')) {
807                    state.setEntityState(SearchSemicolon);
808                    break;
809                }
810
811                EntityUnicodeValue = EntityUnicodeValue * 10 + (cc - '0');
812                m_cBuffer[cBufferPos++] = cc;
813                src.advancePastNonNewline();
814            }
815            if (cBufferPos == 9) 
816                state.setEntityState(SearchSemicolon);
817            break;
818        }
819        case EntityName:
820        {
821            int ll = min(src.length(), 9-cBufferPos);
822            while (ll--) {
823                cc = *src;
824
825                if (!((cc >= 'a' && cc <= 'z') || (cc >= '0' && cc <= '9') || (cc >= 'A' && cc <= 'Z'))) {
826                    state.setEntityState(SearchSemicolon);
827                    break;
828                }
829
830                m_cBuffer[cBufferPos++] = cc;
831                src.advancePastNonNewline();
832            }
833            if (cBufferPos == 9) 
834                state.setEntityState(SearchSemicolon);
835            if (state.entityState() == SearchSemicolon) {
836                if (cBufferPos > 1) {
837                    // Since the maximum length of entity name is 9,
838                    // so a single char array which is allocated on
839                    // the stack, its length is 10, should be OK.
840                    // Also if we have an illegal character, we treat it
841                    // as illegal entity name.
842                    unsigned testedEntityNameLen = 0;
843                    char tmpEntityNameBuffer[10];
844
845                    ASSERT(cBufferPos < 10);
846                    for (; testedEntityNameLen < cBufferPos; ++testedEntityNameLen) {
847                        if (m_cBuffer[testedEntityNameLen] > 0x7e)
848                            break;
849                        tmpEntityNameBuffer[testedEntityNameLen] = m_cBuffer[testedEntityNameLen];
850                    }
851
852                    const Entity *e;
853
854                    if (testedEntityNameLen == cBufferPos)
855                        e = findEntity(tmpEntityNameBuffer, cBufferPos);
856                    else
857                        e = 0;
858
859                    if (e)
860                        EntityUnicodeValue = e->code;
861
862                    // be IE compatible
863                    if (parsingTag && EntityUnicodeValue > 255 && *src != ';')
864                        EntityUnicodeValue = 0;
865                }
866            }
867            else
868                break;
869        }
870        case SearchSemicolon:
871            // Don't allow values that are more than 21 bits.
872            if (EntityUnicodeValue > 0 && EntityUnicodeValue <= 0x10FFFF) {
873                if (!inViewSourceMode()) {
874                    if (*src == ';')
875                        src.advancePastNonNewline();
876                    if (EntityUnicodeValue <= 0xFFFF) {
877                        checkBuffer();
878                        src.push(fixUpChar(EntityUnicodeValue));
879                    } else {
880                        // Convert to UTF-16, using surrogate code points.
881                        checkBuffer(2);
882                        src.push(U16_LEAD(EntityUnicodeValue));
883                        src.push(U16_TRAIL(EntityUnicodeValue));
884                    }
885                } else {
886                    // FIXME: We should eventually colorize entities by sending them as a special token.
887                    // 12 bytes required: up to 10 bytes in m_cBuffer plus the
888                    // leading '&' and trailing ';'
889                    checkBuffer(12);
890                    *dest++ = '&';
891                    for (unsigned i = 0; i < cBufferPos; i++)
892                        dest[i] = m_cBuffer[i];
893                    dest += cBufferPos;
894                    if (*src == ';') {
895                        *dest++ = ';';
896                        src.advancePastNonNewline();
897                    }
898                }
899            } else {
900                // 11 bytes required: up to 10 bytes in m_cBuffer plus the
901                // leading '&'
902                checkBuffer(11);
903                // ignore the sequence, add it to the buffer as plaintext
904                *dest++ = '&';
905                for (unsigned i = 0; i < cBufferPos; i++)
906                    dest[i] = m_cBuffer[i];
907                dest += cBufferPos;
908            }
909
910            state.setEntityState(NoEntity);
911            return state;
912        }
913    }
914
915    return state;
916}
917
918HTMLTokenizer::State HTMLTokenizer::parseDoctype(SegmentedString& src, State state)
919{
920    ASSERT(state.inDoctype());
921    while (!src.isEmpty() && state.inDoctype()) {
922        UChar c = *src;
923        bool isWhitespace = c == '\r' || c == '\n' || c == '\t' || c == ' ';
924        switch (m_doctypeToken.state()) {
925            case DoctypeBegin: {
926                m_doctypeToken.setState(DoctypeBeforeName);
927                if (isWhitespace) {
928                    src.advance(m_lineNumber);
929                    if (inViewSourceMode())
930                        m_doctypeToken.m_source.append(c);
931                }
932                break;
933            }
934            case DoctypeBeforeName: {
935                if (c == '>') {
936                    // Malformed.  Just exit.
937                    src.advancePastNonNewline();
938                    state.setInDoctype(false);
939                    if (inViewSourceMode())
940                        processDoctypeToken();
941                } else if (isWhitespace) {
942                    src.advance(m_lineNumber);
943                    if (inViewSourceMode())
944                        m_doctypeToken.m_source.append(c);
945                } else
946                    m_doctypeToken.setState(DoctypeName);
947                break;
948            }
949            case DoctypeName: {
950                if (c == '>') {
951                    // Valid doctype. Emit it.
952                    src.advancePastNonNewline();
953                    state.setInDoctype(false);
954                    processDoctypeToken();
955                } else if (isWhitespace) {
956                    m_doctypeSearchCount = 0; // Used now to scan for PUBLIC
957                    m_doctypeSecondarySearchCount = 0; // Used now to scan for SYSTEM
958                    m_doctypeToken.setState(DoctypeAfterName);
959                    src.advance(m_lineNumber);
960                    if (inViewSourceMode())
961                        m_doctypeToken.m_source.append(c);
962                } else {
963                    src.advancePastNonNewline();
964                    m_doctypeToken.m_name.append(c);
965                    if (inViewSourceMode())
966                        m_doctypeToken.m_source.append(c);
967                }
968                break;
969            }
970            case DoctypeAfterName: {
971                if (c == '>') {
972                    // Valid doctype. Emit it.
973                    src.advancePastNonNewline();
974                    state.setInDoctype(false);
975                    processDoctypeToken();
976                } else if (!isWhitespace) {
977                    src.advancePastNonNewline();
978                    if (toASCIILower(c) == publicStart[m_doctypeSearchCount]) {
979                        m_doctypeSearchCount++;
980                        if (m_doctypeSearchCount == 6)
981                            // Found 'PUBLIC' sequence
982                            m_doctypeToken.setState(DoctypeBeforePublicID);
983                    } else if (m_doctypeSearchCount > 0) {
984                        m_doctypeSearchCount = 0;
985                        m_doctypeToken.setState(DoctypeBogus);
986                    } else if (toASCIILower(c) == systemStart[m_doctypeSecondarySearchCount]) {
987                        m_doctypeSecondarySearchCount++;
988                        if (m_doctypeSecondarySearchCount == 6)
989                            // Found 'SYSTEM' sequence
990                            m_doctypeToken.setState(DoctypeBeforeSystemID);
991                    } else {
992                        m_doctypeSecondarySearchCount = 0;
993                        m_doctypeToken.setState(DoctypeBogus);
994                    }
995                    if (inViewSourceMode())
996                        m_doctypeToken.m_source.append(c);
997                } else {
998                    src.advance(m_lineNumber); // Whitespace keeps us in the after name state.
999                    if (inViewSourceMode())
1000                        m_doctypeToken.m_source.append(c);
1001                }
1002                break;
1003            }
1004            case DoctypeBeforePublicID: {
1005                if (c == '\"' || c == '\'') {
1006                    tquote = c == '\"' ? DoubleQuote : SingleQuote;
1007                    m_doctypeToken.setState(DoctypePublicID);
1008                    src.advancePastNonNewline();
1009                    if (inViewSourceMode())
1010                        m_doctypeToken.m_source.append(c);
1011                } else if (c == '>') {
1012                    // Considered bogus.  Don't process the doctype.
1013                    src.advancePastNonNewline();
1014                    state.setInDoctype(false);
1015                    if (inViewSourceMode())
1016                        processDoctypeToken();
1017                } else if (isWhitespace) {
1018                    src.advance(m_lineNumber);
1019                    if (inViewSourceMode())
1020                        m_doctypeToken.m_source.append(c);
1021                } else
1022                    m_doctypeToken.setState(DoctypeBogus);
1023                break;
1024            }
1025            case DoctypePublicID: {
1026                if ((c == '\"' && tquote == DoubleQuote) || (c == '\'' && tquote == SingleQuote)) {
1027                    src.advancePastNonNewline();
1028                    m_doctypeToken.setState(DoctypeAfterPublicID);
1029                    if (inViewSourceMode())
1030                        m_doctypeToken.m_source.append(c);
1031                } else if (c == '>') {
1032                     // Considered bogus.  Don't process the doctype.
1033                    src.advancePastNonNewline();
1034                    state.setInDoctype(false);
1035                    if (inViewSourceMode())
1036                        processDoctypeToken();
1037                } else {
1038                    m_doctypeToken.m_publicID.append(c);
1039                    src.advance(m_lineNumber);
1040                    if (inViewSourceMode())
1041                        m_doctypeToken.m_source.append(c);
1042                }
1043                break;
1044            }
1045            case DoctypeAfterPublicID:
1046                if (c == '\"' || c == '\'') {
1047                    tquote = c == '\"' ? DoubleQuote : SingleQuote;
1048                    m_doctypeToken.setState(DoctypeSystemID);
1049                    src.advancePastNonNewline();
1050                    if (inViewSourceMode())
1051                        m_doctypeToken.m_source.append(c);
1052                } else if (c == '>') {
1053                    // Valid doctype. Emit it now.
1054                    src.advancePastNonNewline();
1055                    state.setInDoctype(false);
1056                    processDoctypeToken();
1057                } else if (isWhitespace) {
1058                    src.advance(m_lineNumber);
1059                    if (inViewSourceMode())
1060                        m_doctypeToken.m_source.append(c);
1061                } else
1062                    m_doctypeToken.setState(DoctypeBogus);
1063                break;
1064            case DoctypeBeforeSystemID:
1065                if (c == '\"' || c == '\'') {
1066                    tquote = c == '\"' ? DoubleQuote : SingleQuote;
1067                    m_doctypeToken.setState(DoctypeSystemID);
1068                    src.advancePastNonNewline();
1069                    if (inViewSourceMode())
1070                        m_doctypeToken.m_source.append(c);
1071                } else if (c == '>') {
1072                    // Considered bogus.  Don't process the doctype.
1073                    src.advancePastNonNewline();
1074                    state.setInDoctype(false);
1075                } else if (isWhitespace) {
1076                    src.advance(m_lineNumber);
1077                    if (inViewSourceMode())
1078                        m_doctypeToken.m_source.append(c);
1079                } else
1080                    m_doctypeToken.setState(DoctypeBogus);
1081                break;
1082            case DoctypeSystemID:
1083                if ((c == '\"' && tquote == DoubleQuote) || (c == '\'' && tquote == SingleQuote)) {
1084                    src.advancePastNonNewline();
1085                    m_doctypeToken.setState(DoctypeAfterSystemID);
1086                    if (inViewSourceMode())
1087                        m_doctypeToken.m_source.append(c);
1088                } else if (c == '>') {
1089                     // Considered bogus.  Don't process the doctype.
1090                    src.advancePastNonNewline();
1091                    state.setInDoctype(false);
1092                    if (inViewSourceMode())
1093                        processDoctypeToken();
1094                } else {
1095                    m_doctypeToken.m_systemID.append(c);
1096                    src.advance(m_lineNumber);
1097                    if (inViewSourceMode())
1098                        m_doctypeToken.m_source.append(c);
1099                }
1100                break;
1101            case DoctypeAfterSystemID:
1102                if (c == '>') {
1103                    // Valid doctype. Emit it now.
1104                    src.advancePastNonNewline();
1105                    state.setInDoctype(false);
1106                    processDoctypeToken();
1107                } else if (isWhitespace) {
1108                    src.advance(m_lineNumber);
1109                    if (inViewSourceMode())
1110                        m_doctypeToken.m_source.append(c);
1111                } else
1112                    m_doctypeToken.setState(DoctypeBogus);
1113                break;
1114            case DoctypeBogus:
1115                if (c == '>') {
1116                    // Done with the bogus doctype.
1117                    src.advancePastNonNewline();
1118                    state.setInDoctype(false);
1119                    if (inViewSourceMode())
1120                       processDoctypeToken();
1121                } else {
1122                    src.advance(m_lineNumber); // Just keep scanning for '>'
1123                    if (inViewSourceMode())
1124                        m_doctypeToken.m_source.append(c);
1125                }
1126                break;
1127            default:
1128                break;
1129        }
1130    }
1131    return state;
1132}
1133
1134HTMLTokenizer::State HTMLTokenizer::parseTag(SegmentedString& src, State state)
1135{
1136    ASSERT(!state.hasEntityState());
1137
1138    unsigned cBufferPos = m_cBufferPos;
1139
1140    bool lastIsSlash = false;
1141
1142    while (!src.isEmpty()) {
1143        checkBuffer();
1144        switch (state.tagState()) {
1145        case NoTag:
1146        {
1147            m_cBufferPos = cBufferPos;
1148            return state;
1149        }
1150        case TagName:
1151        {
1152            if (searchCount > 0) {
1153                if (*src == commentStart[searchCount]) {
1154                    searchCount++;
1155                    if (searchCount == 2)
1156                        m_doctypeSearchCount++; // A '!' is also part of a doctype, so we are moving through that still as well.
1157                    else
1158                        m_doctypeSearchCount = 0;
1159                    if (searchCount == 4) {
1160                        // Found '<!--' sequence
1161                        src.advancePastNonNewline();
1162                        m_dest = m_buffer; // ignore the previous part of this tag
1163                        state.setInComment(true);
1164                        state.setTagState(NoTag);
1165
1166                        // Fix bug 34302 at kde.bugs.org.  Go ahead and treat
1167                        // <!--> as a valid comment, since both mozilla and IE on windows
1168                        // can handle this case.  Only do this in quirks mode. -dwh
1169                        if (!src.isEmpty() && *src == '>' && m_doc->inCompatMode()) {
1170                            state.setInComment(false);
1171                            src.advancePastNonNewline();
1172                            if (!src.isEmpty())
1173                                m_cBuffer[cBufferPos++] = *src;
1174                        } else
1175                          state = parseComment(src, state);
1176
1177                        m_cBufferPos = cBufferPos;
1178                        return state; // Finished parsing tag!
1179                    }
1180                    m_cBuffer[cBufferPos++] = *src;
1181                    src.advancePastNonNewline();
1182                    break;
1183                } else
1184                    searchCount = 0; // Stop looking for '<!--' sequence
1185            }
1186           
1187            if (m_doctypeSearchCount > 0) {
1188                if (toASCIILower(*src) == doctypeStart[m_doctypeSearchCount]) {
1189                    m_doctypeSearchCount++;
1190                    m_cBuffer[cBufferPos++] = *src;
1191                    src.advancePastNonNewline();
1192                    if (m_doctypeSearchCount == 9) {
1193                        // Found '<!DOCTYPE' sequence
1194                        state.setInDoctype(true);
1195                        state.setTagState(NoTag);
1196                        m_doctypeToken.reset();
1197                        if (inViewSourceMode())
1198                            m_doctypeToken.m_source.append(m_cBuffer, cBufferPos);
1199                        state = parseDoctype(src, state);
1200                        m_cBufferPos = cBufferPos;
1201                        return state;
1202                    }
1203                    break;
1204                } else
1205                    m_doctypeSearchCount = 0; // Stop looking for '<!DOCTYPE' sequence
1206            }
1207
1208            bool finish = false;
1209            unsigned int ll = min(src.length(), CBUFLEN - cBufferPos);
1210            while (ll--) {
1211                UChar curchar = *src;
1212                if (isASCIISpace(curchar) || curchar == '>' || curchar == '<') {
1213                    finish = true;
1214                    break;
1215                }
1216               
1217                // tolower() shows up on profiles. This is faster!
1218                if (curchar >= 'A' && curchar <= 'Z' && !inViewSourceMode())
1219                    m_cBuffer[cBufferPos++] = curchar + ('a' - 'A');
1220                else
1221                    m_cBuffer[cBufferPos++] = curchar;
1222                src.advancePastNonNewline();
1223            }
1224
1225            // Disadvantage: we add the possible rest of the tag
1226            // as attribute names. ### judge if this causes problems
1227            if (finish || CBUFLEN == cBufferPos) {
1228                bool beginTag;
1229                UChar* ptr = m_cBuffer;
1230                unsigned int len = cBufferPos;
1231                m_cBuffer[cBufferPos] = '\0';
1232                if ((cBufferPos > 0) && (*ptr == '/')) {
1233                    // End Tag
1234                    beginTag = false;
1235                    ptr++;
1236                    len--;
1237                }
1238                else
1239                    // Start Tag
1240                    beginTag = true;
1241
1242                // Ignore the / in fake xml tags like <br/>.  We trim off the "/" so that we'll get "br" as the tag name and not "br/".
1243                if (len > 1 && ptr[len-1] == '/' && !inViewSourceMode())
1244                    ptr[--len] = '\0';
1245
1246                // Now that we've shaved off any invalid / that might have followed the name), make the tag.
1247                // FIXME: FireFox and WinIE turn !foo nodes into comments, we ignore comments. (fast/parser/tag-with-exclamation-point.html)
1248                if (ptr[0] != '!' || inViewSourceMode()) {
1249                    m_currentToken.tagName = AtomicString(ptr);
1250                    m_currentToken.beginTag = beginTag;
1251                }
1252                m_dest = m_buffer;
1253                state.setTagState(SearchAttribute);
1254                cBufferPos = 0;
1255            }
1256            break;
1257        }
1258        case SearchAttribute:
1259            while (!src.isEmpty()) {
1260                UChar curchar = *src;
1261                // In this mode just ignore any quotes we encounter and treat them like spaces.
1262                if (!isASCIISpace(curchar) && curchar != '\'' && curchar != '"') {
1263                    if (curchar == '<' || curchar == '>')
1264                        state.setTagState(SearchEnd);
1265                    else
1266                        state.setTagState(AttributeName);
1267
1268                    cBufferPos = 0;
1269                    break;
1270                }
1271                if (inViewSourceMode())
1272                    m_currentToken.addViewSourceChar(curchar);
1273                src.advance(m_lineNumber);
1274            }
1275            break;
1276        case AttributeName:
1277        {
1278            m_rawAttributeBeforeValue.clear();
1279            int ll = min(src.length(), CBUFLEN - cBufferPos);
1280            while (ll--) {
1281                UChar curchar = *src;
1282                // If we encounter a "/" when scanning an attribute name, treat it as a delimiter.  This allows the
1283                // cases like <input type=checkbox checked/> to work (and accommodates XML-style syntax as per HTML5).
1284                if (curchar <= '>' && (curchar >= '<' || isASCIISpace(curchar) || curchar == '/')) {
1285                    m_cBuffer[cBufferPos] = '\0';
1286                    m_attrName = AtomicString(m_cBuffer);
1287                    m_dest = m_buffer;
1288                    *m_dest++ = 0;
1289                    state.setTagState(SearchEqual);
1290                    if (inViewSourceMode())
1291                        m_currentToken.addViewSourceChar('a');
1292                    break;
1293                }
1294               
1295                // tolower() shows up on profiles. This is faster!
1296                if (curchar >= 'A' && curchar <= 'Z' && !inViewSourceMode())
1297                    m_cBuffer[cBufferPos++] = curchar + ('a' - 'A');
1298                else
1299                    m_cBuffer[cBufferPos++] = curchar;
1300                   
1301                m_rawAttributeBeforeValue.append(curchar);
1302                src.advance(m_lineNumber);
1303            }
1304            if (cBufferPos == CBUFLEN) {
1305                m_cBuffer[cBufferPos] = '\0';
1306                m_attrName = AtomicString(m_cBuffer);
1307                m_dest = m_buffer;
1308                *m_dest++ = 0;
1309                state.setTagState(SearchEqual);
1310                if (inViewSourceMode())
1311                    m_currentToken.addViewSourceChar('a');
1312            }
1313            break;
1314        }
1315        case SearchEqual:
1316            while (!src.isEmpty()) {
1317                UChar curchar = *src;
1318
1319                if (lastIsSlash && curchar == '>') {
1320                    // This is a quirk (with a long sad history).  We have to do this
1321                    // since widgets do <script src="foo.js"/> and expect the tag to close.
1322                    if (m_currentToken.tagName == scriptTag)
1323                        m_currentToken.selfClosingTag = true;
1324                    m_currentToken.brokenXMLStyle = true;
1325                }
1326
1327                // In this mode just ignore any quotes or slashes we encounter and treat them like spaces.
1328                if (!isASCIISpace(curchar) && curchar != '\'' && curchar != '"' && curchar != '/') {
1329                    if (curchar == '=') {
1330                        state.setTagState(SearchValue);
1331                        if (inViewSourceMode())
1332                            m_currentToken.addViewSourceChar(curchar);
1333                        m_rawAttributeBeforeValue.append(curchar);
1334                        src.advancePastNonNewline();
1335                    } else {
1336                        m_currentToken.addAttribute(m_attrName, emptyAtom, inViewSourceMode());
1337                        m_dest = m_buffer;
1338                        state.setTagState(SearchAttribute);
1339                        lastIsSlash = false;
1340                    }
1341                    break;
1342                }
1343
1344                lastIsSlash = curchar == '/';
1345
1346                if (inViewSourceMode())
1347                    m_currentToken.addViewSourceChar(curchar);
1348                m_rawAttributeBeforeValue.append(curchar);
1349                src.advance(m_lineNumber);
1350            }
1351            break;
1352        case SearchValue:
1353            while (!src.isEmpty()) {
1354                UChar curchar = *src;
1355                if (!isASCIISpace(curchar)) {
1356                    if (curchar == '\'' || curchar == '\"') {
1357                        tquote = curchar == '\"' ? DoubleQuote : SingleQuote;
1358                        state.setTagState(QuotedValue);
1359                        if (inViewSourceMode())
1360                            m_currentToken.addViewSourceChar(curchar);
1361                        m_rawAttributeBeforeValue.append(curchar);
1362                        src.advancePastNonNewline();
1363                    } else
1364                        state.setTagState(Value);
1365
1366                    break;
1367                }
1368                if (inViewSourceMode())
1369                    m_currentToken.addViewSourceChar(curchar);
1370                m_rawAttributeBeforeValue.append(curchar);
1371                src.advance(m_lineNumber);
1372            }
1373            break;
1374        case QuotedValue:
1375            while (!src.isEmpty()) {
1376                checkBuffer();
1377
1378                UChar curchar = *src;
1379                if (curchar <= '>' && !src.escaped()) {
1380                    if (curchar == '>' && m_attrName.isEmpty()) {
1381                        // Handle a case like <img '>.  Just go ahead and be willing
1382                        // to close the whole tag.  Don't consume the character and
1383                        // just go back into SearchEnd while ignoring the whole
1384                        // value.
1385                        // FIXME: Note that this is actually not a very good solution.
1386                        // It doesn't handle the general case of
1387                        // unmatched quotes among attributes that have names. -dwh
1388                        while (m_dest > m_buffer + 1 && (m_dest[-1] == '\n' || m_dest[-1] == '\r'))
1389                            m_dest--; // remove trailing newlines
1390                        AtomicString attributeValue(m_buffer + 1, m_dest - m_buffer - 1);
1391                        if (!attributeValue.contains('/'))
1392                            m_attrName = attributeValue; // Just make the name/value match. (FIXME: Is this some WinIE quirk?)
1393                        m_currentToken.addAttribute(m_attrName, attributeValue, inViewSourceMode());
1394                        if (inViewSourceMode())
1395                            m_currentToken.addViewSourceChar('x');
1396                        state.setTagState(SearchAttribute);
1397                        m_dest = m_buffer;
1398                        tquote = NoQuote;
1399                        break;
1400                    }
1401                   
1402                    if (curchar == '&') {
1403                        src.advancePastNonNewline();
1404                        state = parseEntity(src, m_dest, state, cBufferPos, true, true);
1405                        break;
1406                    }
1407
1408                    if ((tquote == SingleQuote && curchar == '\'') || (tquote == DoubleQuote && curchar == '\"')) {
1409                        // some <input type=hidden> rely on trailing spaces. argh
1410                        while (m_dest > m_buffer + 1 && (m_dest[-1] == '\n' || m_dest[-1] == '\r'))
1411                            m_dest--; // remove trailing newlines
1412                        AtomicString attributeValue(m_buffer + 1, m_dest - m_buffer - 1);
1413                        if (m_attrName.isEmpty() && !attributeValue.contains('/')) {
1414                            m_attrName = attributeValue; // Make the name match the value. (FIXME: Is this a WinIE quirk?)
1415                            if (inViewSourceMode())
1416                                m_currentToken.addViewSourceChar('x');
1417                        } else if (inViewSourceMode())
1418                            m_currentToken.addViewSourceChar('v');
1419
1420                        if (m_currentToken.beginTag && m_currentToken.tagName == scriptTag && !inViewSourceMode() && !m_parser->skipMode() && m_attrName == srcAttr) {
1421                            String context(m_rawAttributeBeforeValue.data(), m_rawAttributeBeforeValue.size());
1422                            if (m_XSSAuditor && !m_XSSAuditor->canLoadExternalScriptFromSrc(context, attributeValue))
1423                                attributeValue = blankURL().string();
1424                        }
1425
1426                        m_currentToken.addAttribute(m_attrName, attributeValue, inViewSourceMode());
1427                        m_dest = m_buffer;
1428                        state.setTagState(SearchAttribute);
1429                        tquote = NoQuote;
1430                        if (inViewSourceMode())
1431                            m_currentToken.addViewSourceChar(curchar);
1432                        src.advancePastNonNewline();
1433                        break;
1434                    }
1435                }
1436
1437                *m_dest++ = curchar;
1438                src.advance(m_lineNumber);
1439            }
1440            break;
1441        case Value:
1442            while (!src.isEmpty()) {
1443                checkBuffer();
1444                UChar curchar = *src;
1445                if (curchar <= '>' && !src.escaped()) {
1446                    // parse Entities
1447                    if (curchar == '&') {
1448                        src.advancePastNonNewline();
1449                        state = parseEntity(src, m_dest, state, cBufferPos, true, true);
1450                        break;
1451                    }
1452                    // no quotes. Every space means end of value
1453                    // '/' does not delimit in IE!
1454                    if (isASCIISpace(curchar) || curchar == '>') {
1455                        AtomicString attributeValue(m_buffer + 1, m_dest - m_buffer - 1);
1456
1457                        if (m_currentToken.beginTag && m_currentToken.tagName == scriptTag && !inViewSourceMode() && !m_parser->skipMode() && m_attrName == srcAttr) {
1458                            String context(m_rawAttributeBeforeValue.data(), m_rawAttributeBeforeValue.size());
1459                            if (m_XSSAuditor && !m_XSSAuditor->canLoadExternalScriptFromSrc(context, attributeValue))
1460                                attributeValue = blankURL().string();
1461                        }
1462
1463                        m_currentToken.addAttribute(m_attrName, attributeValue, inViewSourceMode());
1464                        if (inViewSourceMode())
1465                            m_currentToken.addViewSourceChar('v');
1466                        m_dest = m_buffer;
1467                        state.setTagState(SearchAttribute);
1468                        break;
1469                    }
1470                }
1471
1472                *m_dest++ = curchar;
1473                src.advance(m_lineNumber);
1474            }
1475            break;
1476        case SearchEnd:
1477        {
1478            while (!src.isEmpty()) {
1479                UChar ch = *src;
1480                if (ch == '>' || ch == '<')
1481                    break;
1482                if (ch == '/')
1483                    m_currentToken.selfClosingTag = true;
1484                if (inViewSourceMode())
1485                    m_currentToken.addViewSourceChar(ch);
1486                src.advance(m_lineNumber);
1487            }
1488            if (src.isEmpty())
1489                break;
1490
1491            searchCount = 0; // Stop looking for '<!--' sequence
1492            state.setTagState(NoTag);
1493            tquote = NoQuote;
1494
1495            if (*src != '<')
1496                src.advance(m_lineNumber);
1497
1498            if (m_currentToken.tagName == nullAtom) { //stop if tag is unknown
1499                m_cBufferPos = cBufferPos;
1500                return state;
1501            }
1502
1503            AtomicString tagName = m_currentToken.tagName;
1504
1505            // Handle <script src="foo"/> like Mozilla/Opera. We have to do this now for Dashboard
1506            // compatibility.
1507            bool isSelfClosingScript = m_currentToken.selfClosingTag && m_currentToken.beginTag && m_currentToken.tagName == scriptTag;
1508            bool beginTag = !m_currentToken.selfClosingTag && m_currentToken.beginTag;
1509            if (m_currentToken.beginTag && m_currentToken.tagName == scriptTag && !inViewSourceMode() && !m_parser->skipMode()) {
1510                Attribute* a = 0;
1511                m_scriptTagSrcAttrValue = String();
1512                m_scriptTagCharsetAttrValue = String();
1513                if (m_currentToken.attrs && !m_fragment) {
1514                    if (m_doc->frame() && m_doc->frame()->script()->canExecuteScripts(NotAboutToExecuteScript)) {
1515                        if ((a = m_currentToken.attrs->getAttributeItem(srcAttr)))
1516                            m_scriptTagSrcAttrValue = m_doc->completeURL(deprecatedParseURL(a->value())).string();
1517                    }
1518                }
1519            }
1520
1521            RefPtr<Node> n = processToken();
1522            m_cBufferPos = cBufferPos;
1523            if (n || inViewSourceMode()) {
1524                State savedState = state;
1525                SegmentedString savedSrc = src;
1526                long savedLineno = m_lineNumber;
1527                if ((tagName == preTag || tagName == listingTag) && !inViewSourceMode()) {
1528                    if (beginTag)
1529                        state.setDiscardLF(true); // Discard the first LF after we open a pre.
1530                } else if (tagName == scriptTag) {
1531                    ASSERT(!m_scriptNode);
1532                    m_scriptNode = static_pointer_cast<HTMLScriptElement>(n);
1533                    if (m_scriptNode)
1534                        m_scriptTagCharsetAttrValue = m_scriptNode->scriptCharset();
1535                    if (beginTag) {
1536                        m_searchStopper = scriptEnd;
1537                        m_searchStopperLength = 8;
1538                        state.setInScript(true);
1539                        state = parseNonHTMLText(src, state);
1540                    } else if (isSelfClosingScript) { // Handle <script src="foo"/>
1541                        state.setInScript(true);
1542                        state = scriptHandler(state);
1543                    }
1544                } else if (tagName == styleTag) {
1545                    if (beginTag) {
1546                        m_searchStopper = styleEnd;
1547                        m_searchStopperLength = 7;
1548                        state.setInStyle(true);
1549                        state = parseNonHTMLText(src, state);
1550                    }
1551                } else if (tagName == textareaTag) {
1552                    if (beginTag) {
1553                        m_searchStopper = textareaEnd;
1554                        m_searchStopperLength = 10;
1555                        state.setInTextArea(true);
1556                        state = parseNonHTMLText(src, state);
1557                    }
1558                } else if (tagName == titleTag) {
1559                    if (beginTag) {
1560                        m_searchStopper = titleEnd;
1561                        m_searchStopperLength = 7;
1562                        state.setInTitle(true);
1563                        state = parseNonHTMLText(src, state);
1564                    }
1565                } else if (tagName == xmpTag) {
1566                    if (beginTag) {
1567                        m_searchStopper = xmpEnd;
1568                        m_searchStopperLength = 5;
1569                        state.setInXmp(true);
1570                        state = parseNonHTMLText(src, state);
1571                    }
1572                } else if (tagName == iframeTag) {
1573                    if (beginTag) {
1574                        m_searchStopper = iframeEnd;
1575                        m_searchStopperLength = 8;
1576                        state.setInIFrame(true);
1577                        state = parseNonHTMLText(src, state);
1578                    }
1579                }
1580                if (src.isEmpty() && (state.inTitle() || inViewSourceMode()) && !state.inComment() && !(state.inScript() && m_currentScriptTagStartLineNumber)) {
1581                    // We just ate the rest of the document as the #text node under the special tag!
1582                    // Reset the state then retokenize without special handling.
1583                    // Let the parser clean up the missing close tag.
1584                    // FIXME: This is incorrect, because src.isEmpty() doesn't mean we're
1585                    // at the end of the document unless m_noMoreData is also true. We need
1586                    // to detect this case elsewhere, and save the state somewhere other
1587                    // than a local variable.
1588                    state = savedState;
1589                    src = savedSrc;
1590                    m_lineNumber = savedLineno;
1591                    m_scriptCodeSize = 0;
1592                }
1593            }
1594            if (tagName == plaintextTag)
1595                state.setInPlainText(beginTag);
1596            return state; // Finished parsing tag!
1597        }
1598        } // end switch
1599    }
1600    m_cBufferPos = cBufferPos;
1601    return state;
1602}
1603
1604inline bool HTMLTokenizer::continueProcessing(int& processedCount, double startTime, State &state)
1605{
1606    // We don't want to be checking elapsed time with every character, so we only check after we've
1607    // processed a certain number of characters.
1608    bool allowedYield = state.allowYield();
1609    state.setAllowYield(false);
1610    if (!state.loadingExtScript() && !state.forceSynchronous() && !m_executingScript && (processedCount > m_tokenizerChunkSize || allowedYield)) {
1611        processedCount = 0;
1612        if (currentTime() - startTime > m_tokenizerTimeDelay) {
1613            /* FIXME: We'd like to yield aggressively to give stylesheets the opportunity to
1614               load, but this hurts overall performance on slower machines.  For now turn this
1615               off.
1616            || (!m_doc->haveStylesheetsLoaded() &&
1617                (m_doc->documentElement()->id() != ID_HTML || m_doc->body()))) {*/
1618            // Schedule the timer to keep processing as soon as possible.
1619            m_timer.startOneShot(0);
1620#ifdef INSTRUMENT_LAYOUT_SCHEDULING
1621            if (currentTime() - startTime > m_tokenizerTimeDelay)
1622                printf("Deferring processing of data because 500ms elapsed away from event loop.\n");
1623#endif
1624            return false;
1625        }
1626    }
1627   
1628    processedCount++;
1629    return true;
1630}
1631   
1632void HTMLTokenizer::write(const SegmentedString& str, bool appendData)
1633{
1634    if (!m_buffer)
1635        return;
1636   
1637    if (m_parserStopped)
1638        return;
1639
1640    SegmentedString source(str);
1641    if (m_executingScript)
1642        source.setExcludeLineNumbers();
1643
1644    if ((m_executingScript && appendData) || !m_pendingScripts.isEmpty()) {
1645        // don't parse; we will do this later
1646        if (m_currentPrependingSrc)
1647            m_currentPrependingSrc->append(source);
1648        else {
1649            m_pendingSrc.append(source);
1650#if PRELOAD_SCANNER_ENABLED
1651            if (m_preloadScanner && m_preloadScanner->inProgress() && appendData)
1652                m_preloadScanner->write(source);
1653#endif
1654        }
1655        return;
1656    }
1657   
1658#if PRELOAD_SCANNER_ENABLED
1659    if (m_preloadScanner && m_preloadScanner->inProgress() && appendData)
1660        m_preloadScanner->end();
1661#endif
1662
1663    if (!m_src.isEmpty())
1664        m_src.append(source);
1665    else
1666        setSrc(source);
1667
1668    // Once a timer is set, it has control of when the tokenizer continues.
1669    if (m_timer.isActive())
1670        return;
1671
1672    bool wasInWrite = m_inWrite;
1673    m_inWrite = true;
1674   
1675#ifdef INSTRUMENT_LAYOUT_SCHEDULING
1676    if (!m_doc->ownerElement())
1677        printf("Beginning write at time %d\n", m_doc->elapsedTime());
1678#endif
1679
1680    int processedCount = 0;
1681    double startTime = currentTime();
1682
1683#if ENABLE(INSPECTOR)
1684    if (InspectorTimelineAgent* timelineAgent = m_doc->inspectorTimelineAgent())
1685        timelineAgent->willWriteHTML(source.length(), m_lineNumber);
1686#endif
1687 
1688    Frame* frame = m_doc->frame();
1689
1690    State state = m_state;
1691
1692    while (!m_src.isEmpty() && (!frame || !frame->redirectScheduler()->locationChangePending())) {
1693        if (!continueProcessing(processedCount, startTime, state))
1694            break;
1695
1696        // do we need to enlarge the buffer?
1697        checkBuffer();
1698
1699        UChar cc = *m_src;
1700
1701        bool wasSkipLF = state.skipLF();
1702        if (wasSkipLF)
1703            state.setSkipLF(false);
1704
1705        if (wasSkipLF && (cc == '\n'))
1706            m_src.advance();
1707        else if (state.needsSpecialWriteHandling()) {
1708            // it's important to keep needsSpecialWriteHandling with the flags this block tests
1709            if (state.hasEntityState())
1710                state = parseEntity(m_src, m_dest, state, m_cBufferPos, false, state.hasTagState());
1711            else if (state.inPlainText())
1712                state = parseText(m_src, state);
1713            else if (state.inAnyNonHTMLText())
1714                state = parseNonHTMLText(m_src, state);
1715            else if (state.inComment())
1716                state = parseComment(m_src, state);
1717            else if (state.inDoctype())
1718                state = parseDoctype(m_src, state);
1719            else if (state.inServer())
1720                state = parseServer(m_src, state);
1721            else if (state.inProcessingInstruction())
1722                state = parseProcessingInstruction(m_src, state);
1723            else if (state.hasTagState())
1724                state = parseTag(m_src, state);
1725            else if (state.startTag()) {
1726                state.setStartTag(false);
1727               
1728                switch (cc) {
1729                case '/':
1730                    break;
1731                case '!': {
1732                    // <!-- comment --> or <!DOCTYPE ...>
1733                    searchCount = 1; // Look for '<!--' sequence to start comment or '<!DOCTYPE' sequence to start doctype
1734                    m_doctypeSearchCount = 1;
1735                    break;
1736                }
1737                case '?': {
1738                    // xml processing instruction
1739                    state.setInProcessingInstruction(true);
1740                    tquote = NoQuote;
1741                    state = parseProcessingInstruction(m_src, state);
1742                    continue;
1743
1744                    break;
1745                }
1746                case '%':
1747                    if (!m_brokenServer) {
1748                        // <% server stuff, handle as comment %>
1749                        state.setInServer(true);
1750                        tquote = NoQuote;
1751                        state = parseServer(m_src, state);
1752                        continue;
1753                    }
1754                    // else fall through
1755                default: {
1756                    if ( ((cc >= 'a') && (cc <= 'z')) || ((cc >= 'A') && (cc <= 'Z'))) {
1757                        // Start of a Start-Tag
1758                    } else {
1759                        // Invalid tag
1760                        // Add as is
1761                        *m_dest = '<';
1762                        m_dest++;
1763                        continue;
1764                    }
1765                }
1766                }; // end case
1767
1768                processToken();
1769
1770                m_cBufferPos = 0;
1771                state.setTagState(TagName);
1772                state = parseTag(m_src, state);
1773            }
1774        } else if (cc == '&' && !m_src.escaped()) {
1775            m_src.advancePastNonNewline();
1776            state = parseEntity(m_src, m_dest, state, m_cBufferPos, true, state.hasTagState());
1777        } else if (cc == '<' && !m_src.escaped()) {
1778            m_currentTagStartLineNumber = m_lineNumber;
1779            m_src.advancePastNonNewline();
1780            state.setStartTag(true);
1781            state.setDiscardLF(false);
1782        } else if (cc == '\n' || cc == '\r') {
1783            if (state.discardLF())
1784                // Ignore this LF
1785                state.setDiscardLF(false); // We have discarded 1 LF
1786            else {
1787                // Process this LF
1788                *m_dest++ = '\n';
1789                if (cc == '\r' && !m_src.excludeLineNumbers())
1790                    m_lineNumber++;
1791            }
1792
1793            /* Check for MS-DOS CRLF sequence */
1794            if (cc == '\r')
1795                state.setSkipLF(true);
1796            m_src.advance(m_lineNumber);
1797        } else {
1798            state.setDiscardLF(false);
1799            *m_dest++ = cc;
1800            m_src.advancePastNonNewline();
1801        }
1802    }
1803   
1804#ifdef INSTRUMENT_LAYOUT_SCHEDULING
1805    if (!m_doc->ownerElement())
1806        printf("Ending write at time %d\n", m_doc->elapsedTime());
1807#endif
1808
1809#if ENABLE(INSPECTOR)
1810    if (InspectorTimelineAgent* timelineAgent = m_doc->inspectorTimelineAgent())
1811        timelineAgent->didWriteHTML(m_lineNumber);
1812#endif
1813
1814    m_inWrite = wasInWrite;
1815
1816    m_state = state;
1817
1818    if (m_noMoreData && !m_inWrite && !state.loadingExtScript() && !m_executingScript && !m_timer.isActive())
1819        end(); // this actually causes us to be deleted
1820   
1821    // After parsing, go ahead and dispatch image beforeload events.
1822    ImageLoader::dispatchPendingBeforeLoadEvents();
1823}
1824
1825void HTMLTokenizer::stopParsing()
1826{
1827    Tokenizer::stopParsing();
1828    m_timer.stop();
1829
1830    // The part needs to know that the tokenizer has finished with its data,
1831    // regardless of whether it happened naturally or due to manual intervention.
1832    if (!m_fragment && m_doc->frame())
1833        m_doc->frame()->loader()->tokenizerProcessedData();
1834}
1835
1836bool HTMLTokenizer::processingData() const
1837{
1838    return m_timer.isActive() || m_inWrite;
1839}
1840
1841void HTMLTokenizer::timerFired(Timer<HTMLTokenizer>*)
1842{
1843#ifdef INSTRUMENT_LAYOUT_SCHEDULING
1844    if (!m_doc->ownerElement())
1845        printf("Beginning timer write at time %d\n", m_doc->elapsedTime());
1846#endif
1847
1848    if (m_doc->view() && m_doc->view()->layoutPending() && !m_doc->minimumLayoutDelay()) {
1849        // Restart the timer and let layout win.  This is basically a way of ensuring that the layout
1850        // timer has higher priority than our timer.
1851        m_timer.startOneShot(0);
1852        return;
1853    }
1854
1855    // Invoke write() as though more data came in. This might cause us to get deleted.
1856    write(SegmentedString(), true);
1857}
1858
1859void HTMLTokenizer::end()
1860{
1861    ASSERT(!m_timer.isActive());
1862    m_timer.stop(); // Only helps if assertion above fires, but do it anyway.
1863
1864    if (m_buffer) {
1865        // parseTag is using the buffer for different matters
1866        if (!m_state.hasTagState())
1867            processToken();
1868
1869        fastFree(m_scriptCode);
1870        m_scriptCode = 0;
1871        m_scriptCodeSize = m_scriptCodeCapacity = m_scriptCodeResync = 0;
1872
1873        fastFree(m_buffer);
1874        m_buffer = 0;
1875    }
1876
1877    if (!inViewSourceMode())
1878        m_parser->finished();
1879    else
1880        m_doc->finishedParsing();
1881}
1882
1883void HTMLTokenizer::finish()
1884{
1885    // do this as long as we don't find matching comment ends
1886    while ((m_state.inComment() || m_state.inServer()) && m_scriptCode && m_scriptCodeSize) {
1887        // we've found an unmatched comment start
1888        if (m_state.inComment())
1889            m_brokenComments = true;
1890        else
1891            m_brokenServer = true;
1892        checkScriptBuffer();
1893        m_scriptCode[m_scriptCodeSize] = 0;
1894        m_scriptCode[m_scriptCodeSize + 1] = 0;
1895        int pos;
1896        String food;
1897        if (m_state.inScript() || m_state.inStyle() || m_state.inTextArea())
1898            food = String(m_scriptCode, m_scriptCodeSize);
1899        else if (m_state.inServer()) {
1900            food = "<";
1901            food.append(m_scriptCode, m_scriptCodeSize);
1902        } else {
1903            pos = find(m_scriptCode, m_scriptCodeSize, '>');
1904            food = String(m_scriptCode + pos + 1, m_scriptCodeSize - pos - 1);
1905        }
1906        fastFree(m_scriptCode);
1907        m_scriptCode = 0;
1908        m_scriptCodeSize = m_scriptCodeCapacity = m_scriptCodeResync = 0;
1909        m_state.setInComment(false);
1910        m_state.setInServer(false);
1911        if (!food.isEmpty())
1912            write(food, true);
1913    }
1914    // this indicates we will not receive any more data... but if we are waiting on
1915    // an external script to load, we can't finish parsing until that is done
1916    m_noMoreData = true;
1917    if (!m_inWrite && !m_state.loadingExtScript() && !m_executingScript && !m_timer.isActive())
1918        end(); // this actually causes us to be deleted
1919}
1920
1921PassRefPtr<Node> HTMLTokenizer::processToken()
1922{
1923    ScriptController* scriptController = (!m_fragment && m_doc->frame()) ? m_doc->frame()->script() : 0;
1924    if (scriptController && scriptController->canExecuteScripts(NotAboutToExecuteScript))
1925        // FIXME: Why isn't this m_currentScriptTagStartLineNumber?  I suspect this is wrong.
1926        scriptController->setEventHandlerLineNumber(m_currentTagStartLineNumber + 1); // Script line numbers are 1 based.
1927    if (m_dest > m_buffer) {
1928        m_currentToken.text = StringImpl::createStrippingNullCharacters(m_buffer, m_dest - m_buffer);
1929        if (m_currentToken.tagName != commentAtom)
1930            m_currentToken.tagName = textAtom;
1931    } else if (m_currentToken.tagName == nullAtom) {
1932        m_currentToken.reset();
1933        if (scriptController)
1934            scriptController->setEventHandlerLineNumber(m_lineNumber + 1); // Script line numbers are 1 based.
1935        return 0;
1936    }
1937
1938    m_dest = m_buffer;
1939
1940    RefPtr<Node> n;
1941   
1942    if (!m_parserStopped) {
1943        if (NamedMappedAttrMap* map = m_currentToken.attrs.get())
1944            map->shrinkToLength();
1945        if (inViewSourceMode())
1946            static_cast<HTMLViewSourceDocument*>(m_doc)->addViewSourceToken(&m_currentToken);
1947        else
1948            // pass the token over to the parser, the parser DOES NOT delete the token
1949            n = m_parser->parseToken(&m_currentToken);
1950    }
1951    m_currentToken.reset();
1952    if (scriptController)
1953        scriptController->setEventHandlerLineNumber(0);
1954
1955    return n.release();
1956}
1957
1958void HTMLTokenizer::processDoctypeToken()
1959{
1960    if (inViewSourceMode())
1961        static_cast<HTMLViewSourceDocument*>(m_doc)->addViewSourceDoctypeToken(&m_doctypeToken);
1962    else
1963        m_parser->parseDoctypeToken(&m_doctypeToken);
1964}
1965
1966HTMLTokenizer::~HTMLTokenizer()
1967{
1968    ASSERT(!m_inWrite);
1969    reset();
1970}
1971
1972
1973void HTMLTokenizer::enlargeBuffer(int len)
1974{
1975    // Resize policy: Always at least double the size of the buffer each time.
1976    int delta = max(len, m_bufferSize);
1977
1978    // Check for overflow.
1979    // For now, handle overflow the same way we handle fastRealloc failure, with CRASH.
1980    static const int maxSize = INT_MAX / sizeof(UChar);
1981    if (delta > maxSize - m_bufferSize)
1982        CRASH();
1983
1984    int newSize = m_bufferSize + delta;
1985    int oldOffset = m_dest - m_buffer;
1986    m_buffer = static_cast<UChar*>(fastRealloc(m_buffer, newSize * sizeof(UChar)));
1987    m_dest = m_buffer + oldOffset;
1988    m_bufferSize = newSize;
1989}
1990
1991void HTMLTokenizer::enlargeScriptBuffer(int len)
1992{
1993    // Resize policy: Always at least double the size of the buffer each time.
1994    int delta = max(len, m_scriptCodeCapacity);
1995
1996    // Check for overflow.
1997    // For now, handle overflow the same way we handle fastRealloc failure, with CRASH.
1998    static const int maxSize = INT_MAX / sizeof(UChar);
1999    if (delta > maxSize - m_scriptCodeCapacity)
2000        CRASH();
2001
2002    int newSize = m_scriptCodeCapacity + delta;
2003    // If we allow fastRealloc(ptr, 0), it will call CRASH(). We run into this
2004    // case if the HTML being parsed begins with "<!--" and there's more data
2005    // coming.
2006    if (!newSize) {
2007        ASSERT(!m_scriptCode);
2008        return;
2009    }
2010
2011    m_scriptCode = static_cast<UChar*>(fastRealloc(m_scriptCode, newSize * sizeof(UChar)));
2012    m_scriptCodeCapacity = newSize;
2013}
2014   
2015void HTMLTokenizer::executeScriptsWaitingForStylesheets()
2016{
2017    ASSERT(m_doc->haveStylesheetsLoaded());
2018
2019    if (m_hasScriptsWaitingForStylesheets)
2020        notifyFinished(0);
2021}
2022
2023void HTMLTokenizer::notifyFinished(CachedResource*)
2024{
2025    executeExternalScriptsIfReady();
2026}
2027
2028void HTMLTokenizer::executeExternalScriptsIfReady()
2029{
2030#ifdef INSTRUMENT_LAYOUT_SCHEDULING
2031    if (!m_doc->ownerElement())
2032        printf("script loaded at %d\n", m_doc->elapsedTime());
2033#endif
2034
2035    ASSERT(!m_pendingScripts.isEmpty());
2036
2037    // Make external scripts wait for external stylesheets.
2038    // FIXME: This needs to be done for inline scripts too.
2039    m_hasScriptsWaitingForStylesheets = !m_doc->haveStylesheetsLoaded();
2040    if (m_hasScriptsWaitingForStylesheets)
2041        return;
2042
2043    bool finished = false;
2044   
2045    double startTime = currentTime();
2046    while (!finished && m_pendingScripts.first()->isLoaded()) {
2047        if (!continueExecutingExternalScripts(startTime))
2048            break;
2049
2050        CachedScript* cs = m_pendingScripts.first().get();
2051        m_pendingScripts.removeFirst();
2052        ASSERT(cache()->disabled() || cs->accessCount() > 0);
2053
2054        setSrc(SegmentedString());
2055
2056        // make sure we forget about the script before we execute the new one
2057        // infinite recursion might happen otherwise
2058        ScriptSourceCode sourceCode(cs);
2059        bool errorOccurred = cs->errorOccurred();
2060        cs->removeClient(this);
2061
2062        RefPtr<Node> n = m_scriptNode.release();
2063
2064#ifdef INSTRUMENT_LAYOUT_SCHEDULING
2065        if (!m_doc->ownerElement())
2066            printf("external script beginning execution at %d\n", m_doc->elapsedTime());
2067#endif
2068
2069        if (errorOccurred)
2070            n->dispatchEvent(Event::create(eventNames().errorEvent, true, false));
2071        else {
2072            if (static_cast<HTMLScriptElement*>(n.get())->shouldExecuteAsJavaScript())
2073                m_state = scriptExecution(sourceCode, m_state);
2074#if ENABLE(XHTMLMP)
2075            else
2076                m_doc->setShouldProcessNoscriptElement(true);
2077#endif
2078            n->dispatchEvent(Event::create(eventNames().loadEvent, false, false));
2079        }
2080
2081        // The state of m_pendingScripts.isEmpty() can change inside the scriptExecution()
2082        // call above, so test afterwards.
2083        finished = m_pendingScripts.isEmpty();
2084        if (finished) {
2085            ASSERT(!m_hasScriptsWaitingForStylesheets);
2086            m_state.setLoadingExtScript(false);
2087#ifdef INSTRUMENT_LAYOUT_SCHEDULING
2088            if (!m_doc->ownerElement())
2089                printf("external script finished execution at %d\n", m_doc->elapsedTime());
2090#endif
2091        } else if (m_hasScriptsWaitingForStylesheets) {
2092            // m_hasScriptsWaitingForStylesheets flag might have changed during the script execution.
2093            // If it did we are now blocked waiting for stylesheets and should not execute more scripts until they arrive.
2094            finished = true;
2095        }
2096
2097        // 'm_requestingScript' is true when we are called synchronously from
2098        // scriptHandler(). In that case scriptHandler() will take care
2099        // of m_pendingSrc.
2100        if (!m_requestingScript) {
2101            SegmentedString rest = m_pendingSrc;
2102            m_pendingSrc.clear();
2103            write(rest, false);
2104            // we might be deleted at this point, do not access any members.
2105        }
2106    }
2107}
2108
2109void HTMLTokenizer::executeExternalScriptsTimerFired(Timer<HTMLTokenizer>*)
2110{
2111    if (m_doc->view() && m_doc->view()->layoutPending() && !m_doc->minimumLayoutDelay()) {
2112        // Restart the timer and do layout first.
2113        m_externalScriptsTimer.startOneShot(0);
2114        return;
2115    }
2116
2117    // Continue executing external scripts.
2118    executeExternalScriptsIfReady();
2119}
2120
2121bool HTMLTokenizer::continueExecutingExternalScripts(double startTime)
2122{
2123    if (m_externalScriptsTimer.isActive())
2124        return false;
2125
2126    if (currentTime() - startTime > m_tokenizerTimeDelay) {
2127        // Schedule the timer to keep processing as soon as possible.
2128        m_externalScriptsTimer.startOneShot(0);
2129        return false;
2130    }
2131    return true;
2132}
2133
2134bool HTMLTokenizer::isWaitingForScripts() const
2135{
2136    return m_state.loadingExtScript();
2137}
2138
2139void HTMLTokenizer::setSrc(const SegmentedString& source)
2140{
2141    m_src = source;
2142}
2143
2144void parseHTMLDocumentFragment(const String& source, DocumentFragment* fragment, FragmentScriptingPermission scriptingPermission)
2145{
2146    HTMLTokenizer tok(fragment, scriptingPermission);
2147    tok.setForceSynchronous(true);
2148    tok.write(source, true);
2149    tok.finish();
2150    ASSERT(!tok.processingData());      // make sure we're done (see 3963151)
2151}
2152
2153UChar decodeNamedEntity(const char* name)
2154{
2155    const Entity* e = findEntity(name, strlen(name));
2156    return e ? e->code : 0;
2157}
2158
2159}
Note: See TracBrowser for help on using the browser.