WebCore:
[WebKit-https.git] / WebCore / khtml / html / htmltokenizer.cpp
1 /*
2     This file is part of the KDE libraries
3
4     Copyright (C) 1997 Martin Jones (mjones@kde.org)
5               (C) 1997 Torben Weis (weis@kde.org)
6               (C) 1998 Waldo Bastian (bastian@kde.org)
7               (C) 1999 Lars Knoll (knoll@kde.org)
8               (C) 1999 Antti Koivisto (koivisto@kde.org)
9               (C) 2001 Dirk Mueller (mueller@kde.org)
10     Copyright (C) 2004 Apple Computer, Inc.
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., 59 Temple Place - Suite 330,
25     Boston, MA 02111-1307, USA.
26 */
27 //----------------------------------------------------------------------------
28 //
29 // KDE HTML Widget - Tokenizers
30
31 //#define TOKEN_DEBUG 1
32 //#define TOKEN_DEBUG 2
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 //#include <string.h>
39 #include "html/htmltokenizer.h"
40 #include "html/html_documentimpl.h"
41 #include "html/htmlparser.h"
42 #include "html/dtd.h"
43
44 #include "misc/loader.h"
45 #include "misc/htmlhashes.h"
46
47 #include "khtmlview.h"
48 #include "khtml_part.h"
49 #include "xml/dom_docimpl.h"
50 #include "css/csshelper.h"
51 #include "ecma/kjs_proxy.h"
52 #include <kcharsets.h>
53 #include <kglobal.h>
54 #include <ctype.h>
55 #include <assert.h>
56 #include <qvariant.h>
57 #include <kdebug.h>
58 #include <stdlib.h>
59
60 using DOM::AtomicString;
61 using DOM::AttributeImpl;
62 using DOM::DOMString;
63 using DOM::DOMStringImpl;
64 using DOM::DocumentImpl;
65 using DOM::emptyAtom;
66 using DOM::endTagRequirement;
67
68 // turn off inlining to void warning with newer gcc
69 #undef __inline
70 #define __inline
71 #include "kentities.c"
72 #undef __inline
73
74 // #define INSTRUMENT_LAYOUT_SCHEDULING 1
75
76 #define TOKENIZER_CHUNK_SIZE  4096
77
78 // FIXME: We would like this constant to be 200ms.  Yielding more aggressively results in increased
79 // responsiveness and better incremental rendering.  It slows down overall page-load on slower machines,
80 // though, so for now we set a value of 500.
81 #define TOKENIZER_TIME_DELAY  500
82
83 namespace khtml {
84
85 static const char commentStart [] = "<!--";
86 static const char scriptEnd [] = "</script";
87 static const char xmpEnd [] = "</xmp";
88 static const char styleEnd [] =  "</style";
89 static const char textareaEnd [] = "</textarea";
90 static const char titleEnd [] = "</title";
91
92 #define KHTML_ALLOC_QCHAR_VEC( N ) (QChar*) malloc( sizeof(QChar)*( N ) )
93 #define KHTML_REALLOC_QCHAR_VEC(P, N ) (QChar*) P = realloc(p, sizeof(QChar)*( N ))
94 #define KHTML_DELETE_QCHAR_VEC( P ) free((char*)( P ))
95
96 // Full support for MS Windows extensions to Latin-1.
97 // Technically these extensions should only be activated for pages
98 // marked "windows-1252" or "cp1252", but
99 // in the standard Microsoft way, these extensions infect hundreds of thousands
100 // of web pages.  Note that people with non-latin-1 Microsoft extensions
101 // are SOL.
102 //
103 // See: http://www.microsoft.com/globaldev/reference/WinCP.asp
104 //      http://www.bbsinc.com/iso8859.html
105 //      http://www.obviously.com/
106 //
107 // There may be better equivalents
108
109 #if APPLE_CHANGES
110
111 // Note that we have more Unicode characters than Qt, so we use the
112 // official mapping table from the Unicode 2.0 standard here instead of
113 // one with hacks to avoid certain Unicode characters. Also, we don't
114 // need the unrelated hacks to avoid Unicode characters that are in the
115 // original version.
116
117 // We need this for entities at least. For non-entity text, we could
118 // handle this in the text codec.
119
120 // To cover non-entity text, I think this function would need to be called
121 // in more places. There seem to be many places that don't call fixUpChar.
122
123 inline void fixUpChar(QChar& c) {
124     switch (c.unicode()) {
125         case 0x0080: c = 0x20AC; break;
126         case 0x0081: break;
127         case 0x0082: c = 0x201A; break;
128         case 0x0083: c = 0x0192; break;
129         case 0x0084: c = 0x201E; break;
130         case 0x0085: c = 0x2026; break;
131         case 0x0086: c = 0x2020; break;
132         case 0x0087: c = 0x2021; break;
133         case 0x0088: c = 0x02C6; break;
134         case 0x0089: c = 0x2030; break;
135         case 0x008A: c = 0x0160; break;
136         case 0x008B: c = 0x2039; break;
137         case 0x008C: c = 0x0152; break;
138         case 0x008D: break;
139         case 0x008E: c = 0x017D; break;
140         case 0x008F: break;
141         case 0x0090: break;
142         case 0x0091: c = 0x2018; break;
143         case 0x0092: c = 0x2019; break;
144         case 0x0093: c = 0x201C; break;
145         case 0x0094: c = 0x201D; break;
146         case 0x0095: c = 0x2022; break;
147         case 0x0096: c = 0x2013; break;
148         case 0x0097: c = 0x2014; break;
149         case 0x0098: c = 0x02DC; break;
150         case 0x0099: c = 0x2122; break;
151         case 0x009A: c = 0x0161; break;
152         case 0x009B: c = 0x203A; break;
153         case 0x009C: c = 0x0153; break;
154         case 0x009D: break;
155         case 0x009E: c = 0x017E; break;
156         case 0x009F: c = 0x0178; break;
157     }
158 }
159
160 #else // APPLE_CHANGES
161
162 #define fixUpChar(x) \
163             if (!(x).row() ) { \
164                 switch ((x).cell()) \
165                 { \
166                 /* ALL of these should be changed to Unicode SOON */ \
167                 case 0x80: (x) = 0x20ac; break; \
168                 case 0x82: (x) = ',';    break; \
169                 case 0x83: (x) = 0x0192; break; \
170                 case 0x84: (x) = '"';    break; \
171                 case 0x85: (x) = 0x2026; break; \
172                 case 0x86: (x) = 0x2020; break; \
173                 case 0x87: (x) = 0x2021; break; \
174                 case 0x88: (x) = 0x02C6; break; \
175                 case 0x89: (x) = 0x2030; break; \
176                 case 0x8A: (x) = 0x0160; break; \
177                 case 0x8b: (x) = '<';    break; \
178                 case 0x8C: (x) = 0x0152; break; \
179 \
180                 case 0x8E: (x) = 0x017D; break; \
181 \
182 \
183                 case 0x91: (x) = '\'';   break; \
184                 case 0x92: (x) = '\'';   break; \
185                 case 0x93: (x) = '"';    break; \
186                 case 0x94: (x) = '"';    break; \
187                 case 0x95: (x) = '*';    break; \
188                 case 0x96: (x) = '-';    break; \
189                 case 0x97: (x) = '-';    break; \
190                 case 0x98: (x) = '~';    break; \
191                 case 0x99: (x) = 0x2122; break; \
192                 case 0x9A: (x) = 0x0161; break; \
193                 case 0x9b: (x) = '>';    break; \
194                 case 0x9C: (x) = 0x0153; break; \
195 \
196                 case 0x9E: (x) = 0x017E; break; \
197                 case 0x9F: (x) = 0x0178; break; \
198                 /* This one should die */ \
199                 case 0xb7: (x) = '*';    break; \
200                 default: break; \
201                 } \
202             } \
203             else { \
204                 /* These should all die sooner rather than later */ \
205                 switch( (x).unicode() ) { \
206                 case 0x2013: (x) = '-'; break; \
207                 case 0x2014: (x) = '-'; break; \
208                 case 0x2018: (x) = '\''; break; \
209                 case 0x2019: (x) = '\''; break; \
210                 case 0x201c: (x) = '"'; break; \
211                 case 0x201d: (x) = '"'; break; \
212                 case 0x2022: (x) = '*'; break; \
213                 case 0x2122: (x) = 0x2122; break; \
214                 default: break; \
215                 } \
216             }
217
218 #endif // APPLE_CHANGES
219
220 inline bool tagMatch(const char *s1, const QChar *s2, uint length)
221 {
222     for (uint i = 0; i != length; ++i) {
223         char c1 = s1[i];
224         char uc1 = toupper(c1);
225         QChar c2 = s2[i];
226         if (c1 != c2 && uc1 != c2)
227             return false;
228     }
229     return true;
230 }
231
232 // ----------------------------------------------------------------------------
233
234 HTMLTokenizer::HTMLTokenizer(DOM::DocumentPtr *_doc, KHTMLView *_view, bool includesComments)
235     : inWrite(false)
236 {
237     view = _view;
238     buffer = 0;
239     scriptCode = 0;
240     scriptCodeSize = scriptCodeMaxSize = scriptCodeResync = 0;
241     charsets = KGlobal::charsets();
242     parser = new KHTMLParser(_view, _doc, includesComments);
243     m_executingScript = 0;
244     loadingExtScript = false;
245     onHold = false;
246     attrNamePresent = false;
247     timerId = 0;
248     includesCommentsInDOM = includesComments;
249     loadStopped = false;
250     
251     begin();
252 }
253
254 HTMLTokenizer::HTMLTokenizer(DOM::DocumentPtr *_doc, DOM::DocumentFragmentImpl *i, bool includesComments)
255     : inWrite(false)
256 {
257     view = 0;
258     buffer = 0;
259     scriptCode = 0;
260     scriptCodeSize = scriptCodeMaxSize = scriptCodeResync = 0;
261     charsets = KGlobal::charsets();
262     parser = new KHTMLParser(i, _doc, includesComments);
263     m_executingScript = 0;
264     loadingExtScript = false;
265     onHold = false;
266     timerId = 0;
267     includesCommentsInDOM = includesComments;
268     loadStopped = false;
269
270     begin();
271 }
272
273 void HTMLTokenizer::reset()
274 {
275     assert(m_executingScript == 0);
276     assert(onHold == false);
277
278     while (!cachedScript.isEmpty())
279         cachedScript.dequeue()->deref(this);
280
281     if ( buffer )
282         KHTML_DELETE_QCHAR_VEC(buffer);
283     buffer = dest = 0;
284     size = 0;
285
286     if ( scriptCode )
287         KHTML_DELETE_QCHAR_VEC(scriptCode);
288     scriptCode = 0;
289     scriptCodeSize = scriptCodeMaxSize = scriptCodeResync = 0;
290
291     if (timerId) {
292         killTimer(timerId);
293         timerId = 0;
294     }
295     timerId = 0;
296     allowYield = false;
297     forceSynchronous = false;
298
299     currToken.reset();
300 }
301
302 void HTMLTokenizer::begin()
303 {
304     m_executingScript = 0;
305     loadingExtScript = false;
306     onHold = false;
307     reset();
308     size = 254;
309     buffer = KHTML_ALLOC_QCHAR_VEC( 255 );
310     dest = buffer;
311     tag = NoTag;
312     pending = NonePending;
313     discard = NoneDiscard;
314     pre = false;
315     prePos = 0;
316     plaintext = false;
317     xmp = false;
318     processingInstruction = false;
319     script = false;
320     escaped = false;
321     style = false;
322     skipLF = false;
323     select = false;
324     comment = false;
325     server = false;
326     textarea = false;
327     title = false;
328     startTag = false;
329     tquote = NoQuote;
330     searchCount = 0;
331     Entity = NoEntity;
332     loadingExtScript = false;
333     scriptSrc = QString::null;
334     pendingSrc.clear();
335     currentPrependingSrc = 0;
336     noMoreData = false;
337     brokenComments = false;
338     brokenServer = false;
339     lineno = 0;
340     scriptStartLineno = 0;
341     tagStartLineno = 0;
342     forceSynchronous = false;
343 }
344
345 void HTMLTokenizer::setForceSynchronous(bool force)
346 {
347     forceSynchronous = force;
348 }
349
350 void HTMLTokenizer::processListing(TokenizerString list)
351 {
352     bool old_pre = pre;
353     // This function adds the listing 'list' as
354     // preformatted text-tokens to the token-collection
355     // thereby converting TABs.
356     if(!style) pre = true;
357     prePos = 0;
358
359     while ( !list.isEmpty() )
360     {
361         checkBuffer(3*TAB_SIZE);
362
363         if (skipLF && ( *list != '\n' ))
364         {
365             skipLF = false;
366         }
367
368         if (skipLF)
369         {
370             skipLF = false;
371             ++list;
372         }
373         else if (( *list == '\n' ) || ( *list == '\r' ))
374         {
375             if (discard == LFDiscard)
376             {
377                 // Ignore this LF
378                 discard = NoneDiscard; // We have discarded 1 LF
379             }
380             else
381             {
382                 // Process this LF
383                 if (pending)
384                     addPending();
385                 pending = LFPending;
386             }
387             /* Check for MS-DOS CRLF sequence */
388             if (*list == '\r')
389             {
390                 skipLF = true;
391             }
392             ++list;
393         }
394         else if (( *list == ' ' ) || ( *list == '\t'))
395         {
396             if (pending)
397                 addPending();
398             if (*list == ' ')
399                 pending = SpacePending;
400             else
401                 pending = TabPending;
402
403             ++list;
404         }
405         else
406         {
407             discard = NoneDiscard;
408             if (pending)
409                 addPending();
410
411             prePos++;
412             *dest++ = *list;
413             ++list;
414         }
415
416     }
417
418     if (pending)
419         addPending();
420
421     prePos = 0;
422
423     pre = old_pre;
424 }
425
426 void HTMLTokenizer::parseSpecial(TokenizerString &src)
427 {
428     assert( textarea || title || !Entity );
429     assert( !tag );
430     assert( xmp+textarea+title+style+script == 1 );
431     if (script)
432         scriptStartLineno = lineno+src.lineCount();
433
434     if ( comment ) parseComment( src );
435
436     while ( !src.isEmpty() ) {
437         checkScriptBuffer();
438         unsigned char ch = src->latin1();
439         if ( !scriptCodeResync && !brokenComments && !textarea && !xmp && !title && ch == '-' && scriptCodeSize >= 3 && !src.escaped() && scriptCode[scriptCodeSize-3] == '<' && scriptCode[scriptCodeSize-2] == '!' && scriptCode[scriptCodeSize-1] == '-' ) {
440             comment = true;
441             parseComment( src );
442             continue;
443         }
444         if ( scriptCodeResync && !tquote && ( ch == '>' ) ) {
445             ++src;
446             scriptCodeSize = scriptCodeResync-1;
447             scriptCodeResync = 0;
448             scriptCode[ scriptCodeSize ] = scriptCode[ scriptCodeSize + 1 ] = 0;
449             if ( script )
450                 scriptHandler();
451             else {
452                 processListing(TokenizerString(scriptCode, scriptCodeSize));
453                 processToken();
454                 if ( style )         { currToken.id = ID_STYLE + ID_CLOSE_TAG; }
455                 else if ( textarea ) { currToken.id = ID_TEXTAREA + ID_CLOSE_TAG; }
456                 else if ( title ) { currToken.id = ID_TITLE + ID_CLOSE_TAG; }
457                 else if ( xmp )  { currToken.id = ID_XMP + ID_CLOSE_TAG; }
458                 processToken();
459                 style = script = style = textarea = title = xmp = false;
460                 tquote = NoQuote;
461                 scriptCodeSize = scriptCodeResync = 0;
462             }
463             return;
464         }
465         // possible end of tagname, lets check.
466         if ( !scriptCodeResync && !escaped && !src.escaped() && ( ch == '>' || ch == '/' || ch <= ' ' ) && ch &&
467              scriptCodeSize >= searchStopperLen &&
468              tagMatch( searchStopper, scriptCode+scriptCodeSize-searchStopperLen, searchStopperLen )) {
469             scriptCodeResync = scriptCodeSize-searchStopperLen+1;
470             tquote = NoQuote;
471             continue;
472         }
473         if ( scriptCodeResync && !escaped ) {
474             if(ch == '\"')
475                 tquote = (tquote == NoQuote) ? DoubleQuote : ((tquote == SingleQuote) ? SingleQuote : NoQuote);
476             else if(ch == '\'')
477                 tquote = (tquote == NoQuote) ? SingleQuote : (tquote == DoubleQuote) ? DoubleQuote : NoQuote;
478             else if (tquote != NoQuote && (ch == '\r' || ch == '\n'))
479                 tquote = NoQuote;
480         }
481         escaped = ( !escaped && ch == '\\' );
482         if (!scriptCodeResync && (textarea||title) && !src.escaped() && ch == '&') {
483             QChar *scriptCodeDest = scriptCode+scriptCodeSize;
484             ++src;
485             parseEntity(src,scriptCodeDest,true);
486             scriptCodeSize = scriptCodeDest-scriptCode;
487         }
488         else {
489             scriptCode[scriptCodeSize] = *src;
490             fixUpChar(scriptCode[scriptCodeSize]);
491             ++scriptCodeSize;
492             ++src;
493         }
494     }
495 }
496
497 void HTMLTokenizer::scriptHandler()
498 {
499     // We are inside a <script>
500     bool doScriptExec = false;
501     CachedScript* cs = 0;
502     // don't load external scripts for standalone documents (for now)
503     if (!scriptSrc.isEmpty() && parser->doc()->part()) {
504         // forget what we just got; load from src url instead
505         if ( !parser->skipMode() ) {
506 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
507             if (!parser->doc()->ownerElement())
508                 printf("Requesting script at time %d\n", parser->doc()->elapsedTime());
509 #endif
510             if ( (cs = parser->doc()->docLoader()->requestScript(scriptSrc, scriptSrcCharset) ))
511                 cachedScript.enqueue(cs);
512         }
513         scriptSrc=QString::null;
514     }
515     else {
516 #ifdef TOKEN_DEBUG
517         kdDebug( 6036 ) << "---START SCRIPT---" << endl;
518         kdDebug( 6036 ) << QString(scriptCode, scriptCodeSize) << endl;
519         kdDebug( 6036 ) << "---END SCRIPT---" << endl;
520 #endif
521         // Parse scriptCode containing <script> info
522         doScriptExec = true;
523     }
524     processListing(TokenizerString(scriptCode, scriptCodeSize));
525     QString exScript( buffer, dest-buffer );
526     processToken();
527     currToken.id = ID_SCRIPT + ID_CLOSE_TAG;
528     processToken();
529
530     TokenizerString *savedPrependingSrc = currentPrependingSrc;
531     TokenizerString prependingSrc;
532     currentPrependingSrc = &prependingSrc;
533     if ( !parser->skipMode() ) {
534         if (cs) {
535              //kdDebug( 6036 ) << "cachedscript extern!" << endl;
536              //kdDebug( 6036 ) << "src: *" << QString( src.current(), src.length() ).latin1() << "*" << endl;
537              //kdDebug( 6036 ) << "pending: *" << pendingSrc.latin1() << "*" << endl;
538             if (savedPrependingSrc) {
539                 savedPrependingSrc->append(src);
540             } else {
541                 pendingSrc.prepend(src);
542             }
543             setSrc(TokenizerString());
544             scriptCodeSize = scriptCodeResync = 0;
545             cs->ref(this);
546             // will be 0 if script was already loaded and ref() executed it
547             if (!cachedScript.isEmpty())
548                 loadingExtScript = true;
549         }
550         else if (view && doScriptExec && javascript ) {
551             if (!m_executingScript)
552                 pendingSrc.prepend(src);
553             else
554                 prependingSrc = src;
555             setSrc(TokenizerString());
556             scriptCodeSize = scriptCodeResync = 0;
557             //QTime dt;
558             //dt.start();
559             scriptExecution( exScript, QString::null, scriptStartLineno );
560             //kdDebug( 6036 ) << "script execution time:" << dt.elapsed() << endl;
561         }
562     }
563
564     script = false;
565     scriptCodeSize = scriptCodeResync = 0;
566
567     if ( !m_executingScript && !loadingExtScript ) {
568         // kdDebug( 6036 ) << "adding pending Output to parsed string" << endl;
569         src.append(pendingSrc);
570         pendingSrc.clear();
571     } else if (!prependingSrc.isEmpty()) {
572         // restore first so that the write appends in the right place
573         // (does not hurt to do it again below)
574         currentPrependingSrc = savedPrependingSrc;
575
576         // we need to do this slightly modified bit of one of the write() cases
577         // because we want to prepend to pendingSrc rather than appending
578         // if there's no previous prependingSrc
579         if (loadingExtScript) {
580             if (currentPrependingSrc) {
581                 currentPrependingSrc->append(prependingSrc);
582             } else {
583                 pendingSrc.prepend(prependingSrc);
584             }
585         } else {
586             write(prependingSrc, false);
587         }
588     }
589
590     currentPrependingSrc = savedPrependingSrc;
591 }
592
593 void HTMLTokenizer::scriptExecution( const QString& str, QString scriptURL,
594                                      int baseLine)
595 {
596 #if APPLE_CHANGES
597     if (!view || !view->part())
598         return;
599 #endif
600     bool oldscript = script;
601     m_executingScript++;
602     script = false;
603     QString url;    
604     if (scriptURL.isNull())
605       url = view->part()->xmlDocImpl()->URL();
606     else
607       url = scriptURL;
608
609     TokenizerString *savedPrependingSrc = currentPrependingSrc;
610     TokenizerString prependingSrc;
611     currentPrependingSrc = &prependingSrc;
612
613 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
614     if (!parser->doc()->ownerElement())
615         printf("beginning script execution at %d\n", parser->doc()->elapsedTime());
616 #endif
617
618     view->part()->executeScript(url,baseLine,0,str);
619
620     allowYield = true;
621
622 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
623     if (!parser->doc()->ownerElement())
624         printf("ending script execution at %d\n", parser->doc()->elapsedTime());
625 #endif
626     
627     m_executingScript--;
628     script = oldscript;
629
630     if ( !m_executingScript && !loadingExtScript ) {
631         // kdDebug( 6036 ) << "adding pending Output to parsed string" << endl;
632         src.append(pendingSrc);
633         pendingSrc.clear();
634     } else if (!prependingSrc.isEmpty()) {
635         // restore first so that the write appends in the right place
636         // (does not hurt to do it again below)
637         currentPrependingSrc = savedPrependingSrc;
638
639         // we need to do this slightly modified bit of one of the write() cases
640         // because we want to prepend to pendingSrc rather than appending
641         // if there's no previous prependingSrc
642         if (loadingExtScript) {
643             if (currentPrependingSrc) {
644                 currentPrependingSrc->append(prependingSrc);
645             } else {
646                 pendingSrc.prepend(prependingSrc);
647             }
648         } else {
649             write(prependingSrc, false);
650         }
651     }
652
653     currentPrependingSrc = savedPrependingSrc;
654 }
655
656 void HTMLTokenizer::parseComment(TokenizerString &src)
657 {
658     // FIXME: Why does this code even run for comments inside <script>? This seems bogus.
659     bool strict = !parser->doc()->inCompatMode() && !script;
660     int delimiterCount = 0;
661     bool canClose = false;
662     checkScriptBuffer(src.length());
663     while ( !src.isEmpty() ) {
664         scriptCode[ scriptCodeSize++ ] = *src;
665 #if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
666         qDebug("comment is now: *%s*",
667                QConstString((QChar*)src.current(), kMin(16, src.length())).string().latin1());
668 #endif
669
670         if (strict) {
671             if (src->unicode() == '-') {
672                 delimiterCount++;
673                 if (delimiterCount == 2) {
674                     delimiterCount = 0;
675                     canClose = !canClose;
676                 }
677             }
678             else
679                 delimiterCount = 0;
680         }
681
682         if ((!strict || canClose) && src->unicode() == '>') {
683             bool handleBrokenComments = brokenComments && !(script || style);
684             int endCharsCount = 1; // start off with one for the '>' character
685             if (!strict) {
686                 // In quirks mode just check for -->
687                 if (scriptCodeSize > 2 && scriptCode[scriptCodeSize-3] == '-' && scriptCode[scriptCodeSize-2] == '-') {
688                     endCharsCount = 3;
689                 }
690                 else if (scriptCodeSize > 3 && scriptCode[scriptCodeSize-4] == '-' && scriptCode[scriptCodeSize-3] == '-' && 
691                     scriptCode[scriptCodeSize-2] == '!') {
692                     // Other browsers will accept --!> as a close comment, even though it's
693                     // not technically valid.
694                     endCharsCount = 4;
695                 }
696             }
697             if (canClose || handleBrokenComments || endCharsCount > 1) {
698                 ++src;
699                 if (!( script || xmp || textarea || style)) {
700                     if (includesCommentsInDOM) {
701                         checkScriptBuffer();
702                         scriptCode[ scriptCodeSize ] = 0;
703                         scriptCode[ scriptCodeSize + 1 ] = 0;
704                         currToken.id = ID_COMMENT;
705                         processListing(TokenizerString(scriptCode, scriptCodeSize - endCharsCount));
706                         processToken();
707                         currToken.id = ID_COMMENT + ID_CLOSE_TAG;
708                         processToken();
709                     }
710                     scriptCodeSize = 0;
711                 }
712                 comment = false;
713                 return; // Finished parsing comment
714             }
715         }
716         ++src;
717     }
718 }
719
720 void HTMLTokenizer::parseServer(TokenizerString &src)
721 {
722     checkScriptBuffer(src.length());
723     while ( !src.isEmpty() ) {
724         scriptCode[ scriptCodeSize++ ] = *src;
725         if (src->unicode() == '>' &&
726             scriptCodeSize > 1 && scriptCode[scriptCodeSize-2] == '%') {
727             ++src;
728             server = false;
729             scriptCodeSize = 0;
730             return; // Finished parsing server include
731         }
732         ++src;
733     }
734 }
735
736 void HTMLTokenizer::parseProcessingInstruction(TokenizerString &src)
737 {
738     char oldchar = 0;
739     while ( !src.isEmpty() )
740     {
741         unsigned char chbegin = src->latin1();
742         if(chbegin == '\'') {
743             tquote = tquote == SingleQuote ? NoQuote : SingleQuote;
744         }
745         else if(chbegin == '\"') {
746             tquote = tquote == DoubleQuote ? NoQuote : DoubleQuote;
747         }
748         // Look for '?>'
749         // some crappy sites omit the "?" before it, so
750         // we look for an unquoted '>' instead. (IE compatible)
751         else if ( chbegin == '>' && ( !tquote || oldchar == '?' ) )
752         {
753             // We got a '?>' sequence
754             processingInstruction = false;
755             ++src;
756             discard=LFDiscard;
757             return; // Finished parsing comment!
758         }
759         ++src;
760         oldchar = chbegin;
761     }
762 }
763
764 void HTMLTokenizer::parseText(TokenizerString &src)
765 {
766     while ( !src.isEmpty() )
767     {
768         // do we need to enlarge the buffer?
769         checkBuffer();
770
771         // ascii is okay because we only do ascii comparisons
772         unsigned char chbegin = src->latin1();
773
774         if (skipLF && ( chbegin != '\n' ))
775         {
776             skipLF = false;
777         }
778
779         if (skipLF)
780         {
781             skipLF = false;
782             ++src;
783         }
784         else if (( chbegin == '\n' ) || ( chbegin == '\r' ))
785         {
786             if (chbegin == '\r')
787                 skipLF = true;
788
789             *dest++ = '\n';
790             ++src;
791         }
792         else {
793             *dest = *src;
794             fixUpChar(*dest);
795             ++dest;
796             ++src;
797         }
798     }
799 }
800
801
802 void HTMLTokenizer::parseEntity(TokenizerString &src, QChar *&dest, bool start)
803 {
804     if( start )
805     {
806         cBufferPos = 0;
807         Entity = SearchEntity;
808         EntityUnicodeValue = 0;
809     }
810
811     while( !src.isEmpty() )
812     {
813         ushort cc = src->unicode();
814         switch(Entity) {
815         case NoEntity:
816             assert(Entity != NoEntity);
817             return;
818         
819         case SearchEntity:
820             if(cc == '#') {
821                 cBuffer[cBufferPos++] = cc;
822                 ++src;
823                 Entity = NumericSearch;
824             }
825             else
826                 Entity = EntityName;
827
828             break;
829
830         case NumericSearch:
831             if(cc == 'x' || cc == 'X') {
832                 cBuffer[cBufferPos++] = cc;
833                 ++src;
834                 Entity = Hexadecimal;
835             }
836             else if(cc >= '0' && cc <= '9')
837                 Entity = Decimal;
838             else
839                 Entity = SearchSemicolon;
840
841             break;
842
843         case Hexadecimal:
844         {
845             int ll = kMin(src.length(), 8U);
846             while(ll--) {
847                 QChar csrc(src->lower());
848                 cc = csrc.cell();
849
850                 if(csrc.row() || !((cc >= '0' && cc <= '9') || (cc >= 'a' && cc <= 'f'))) {
851                     break;
852                 }
853                 EntityUnicodeValue = EntityUnicodeValue*16 + (cc - ( cc < 'a' ? '0' : 'a' - 10));
854                 cBuffer[cBufferPos++] = cc;
855                 ++src;
856             }
857             Entity = SearchSemicolon;
858             break;
859         }
860         case Decimal:
861         {
862             int ll = kMin(src.length(), 9-cBufferPos);
863             while(ll--) {
864                 cc = src->cell();
865
866                 if(src->row() || !(cc >= '0' && cc <= '9')) {
867                     Entity = SearchSemicolon;
868                     break;
869                 }
870
871                 EntityUnicodeValue = EntityUnicodeValue * 10 + (cc - '0');
872                 cBuffer[cBufferPos++] = cc;
873                 ++src;
874             }
875             if(cBufferPos == 9)  Entity = SearchSemicolon;
876             break;
877         }
878         case EntityName:
879         {
880             int ll = kMin(src.length(), 9-cBufferPos);
881             while(ll--) {
882                 QChar csrc = *src;
883                 cc = csrc.cell();
884
885                 if(csrc.row() || !((cc >= 'a' && cc <= 'z') ||
886                                    (cc >= '0' && cc <= '9') || (cc >= 'A' && cc <= 'Z'))) {
887                     Entity = SearchSemicolon;
888                     break;
889                 }
890
891                 cBuffer[cBufferPos++] = cc;
892                 ++src;
893             }
894             if(cBufferPos == 9) Entity = SearchSemicolon;
895             if(Entity == SearchSemicolon) {
896                 if(cBufferPos > 1) {
897                     const entity *e = findEntity(cBuffer, cBufferPos);
898                     if(e)
899                         EntityUnicodeValue = e->code;
900
901                     // be IE compatible
902                     if(tag && EntityUnicodeValue > 255 && *src != ';')
903                         EntityUnicodeValue = 0;
904                 }
905             }
906             else
907                 break;
908         }
909         case SearchSemicolon:
910
911             //kdDebug( 6036 ) << "ENTITY " << EntityUnicodeValue << ", " << res << endl;
912
913             // Don't allow surrogate code points, or values that are more than 21 bits.
914             if ((EntityUnicodeValue > 0 && EntityUnicodeValue < 0xD800)
915                     || (EntityUnicodeValue >= 0xE000 && EntityUnicodeValue <= 0x1FFFFF)) {
916             
917                 if (*src == ';')
918                     ++src;
919
920                 if (EntityUnicodeValue <= 0xFFFF) {
921                     QChar c(EntityUnicodeValue);
922                     fixUpChar(c);
923                     checkBuffer();
924                     src.push(c);
925                 } else {
926                     // Convert to UTF-16, using surrogate code points.
927                     QChar c1(0xD800 | (((EntityUnicodeValue >> 16) - 1) << 6) | ((EntityUnicodeValue >> 10) & 0x3F));
928                     QChar c2(0xDC00 | (EntityUnicodeValue & 0x3FF));
929                     checkBuffer(2);
930                     src.push(c1);
931                     src.push(c2);
932                 }
933
934             } else {
935 #ifdef TOKEN_DEBUG
936                 kdDebug( 6036 ) << "unknown entity!" << endl;
937 #endif
938                 checkBuffer(10);
939                 // ignore the sequence, add it to the buffer as plaintext
940                 *dest++ = '&';
941                 for(unsigned int i = 0; i < cBufferPos; i++)
942                     dest[i] = cBuffer[i];
943                 dest += cBufferPos;
944                 if (pre)
945                     prePos += cBufferPos+1;
946             }
947
948             Entity = NoEntity;
949             return;
950         }
951     }
952 }
953
954 void HTMLTokenizer::parseTag(TokenizerString &src)
955 {
956     assert(!Entity );
957
958     while ( !src.isEmpty() )
959     {
960         checkBuffer();
961 #if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
962         uint l = 0;
963         while(l < src.length() && (*(src.current()+l)).latin1() != '>')
964             l++;
965         qDebug("src is now: *%s*, tquote: %d",
966                QConstString((QChar*)src.current(), l).string().latin1(), tquote);
967 #endif
968         switch(tag) {
969         case NoTag:
970         {
971             return;
972         }
973         case TagName:
974         {
975 #if defined(TOKEN_DEBUG) &&  TOKEN_DEBUG > 1
976             qDebug("TagName");
977 #endif
978             if (searchCount > 0)
979             {
980                 if (*src == commentStart[searchCount])
981                 {
982                     searchCount++;
983                     if (searchCount == 4)
984                     {
985 #ifdef TOKEN_DEBUG
986                         kdDebug( 6036 ) << "Found comment" << endl;
987 #endif
988                         // Found '<!--' sequence
989                         ++src;
990                         dest = buffer; // ignore the previous part of this tag
991                         comment = true;
992                         tag = NoTag;
993
994                         // Fix bug 34302 at kde.bugs.org.  Go ahead and treat
995                         // <!--> as a valid comment, since both mozilla and IE on windows
996                         // can handle this case.  Only do this in quirks mode. -dwh
997                         if (!src.isEmpty() && *src == '>' && parser->doc()->inCompatMode()) {
998                           comment = false;
999                           ++src;
1000                           if (!src.isEmpty())
1001                               cBuffer[cBufferPos++] = src->cell();
1002                         }
1003                         else
1004                           parseComment(src);
1005
1006                         return; // Finished parsing tag!
1007                     }
1008                     // cuts of high part, is okay
1009                     cBuffer[cBufferPos++] = src->cell();
1010                     ++src;
1011                     break;
1012                 }
1013                 else
1014                     searchCount = 0; // Stop looking for '<!--' sequence
1015             }
1016
1017             bool finish = false;
1018             unsigned int ll = kMin(src.length(), CBUFLEN-cBufferPos);
1019             while(ll--) {
1020                 ushort curchar = *src;
1021                 if(curchar <= ' ' || curchar == '>' ) {
1022                     finish = true;
1023                     break;
1024                 }
1025                 // Use tolower() instead of | 0x20 to lowercase the char because there is no 
1026                 // performance gain in using | 0x20 since tolower() is optimized and 
1027                 // | 0x20 turns characters such as '_' into junk.
1028                 cBuffer[cBufferPos++] = tolower(curchar);
1029                 ++src;
1030             }
1031
1032             // Disadvantage: we add the possible rest of the tag
1033             // as attribute names. ### judge if this causes problems
1034             if(finish || CBUFLEN == cBufferPos) {
1035                 bool beginTag;
1036                 char* ptr = cBuffer;
1037                 unsigned int len = cBufferPos;
1038                 cBuffer[cBufferPos] = '\0';
1039                 if ((cBufferPos > 0) && (*ptr == '/'))
1040                 {
1041                     // End Tag
1042                     beginTag = false;
1043                     ptr++;
1044                     len--;
1045                 }
1046                 else
1047                     // Start Tag
1048                     beginTag = true;
1049
1050                 // Accept empty xml tags like <br/>.  We trim off the "/" so that when we call
1051                 // getTagID, we'll look up "br" as the tag name and not "br/".
1052                 if(len > 1 && ptr[len-1] == '/' )
1053                     ptr[--len] = '\0';
1054
1055                 // Look up the tagID for the specified tag name (now that we've shaved off any
1056                 // invalid / that might have followed the name).
1057                 unsigned short tagID = getTagID(ptr, len);
1058                 if (!tagID) {
1059                     DOMString tagName(ptr);
1060                     DocumentImpl *doc = parser->docPtr()->document();
1061                     if (doc->isValidName(tagName))
1062                         tagID = parser->docPtr()->document()->tagId(0, tagName.implementation(), false);
1063                 }
1064                 if (tagID) {
1065 #ifdef TOKEN_DEBUG
1066                     QCString tmp(ptr, len+1);
1067                     kdDebug( 6036 ) << "found tag id=" << tagID << ": " << tmp.data() << endl;
1068 #endif
1069                     currToken.id = beginTag ? tagID : tagID + ID_CLOSE_TAG;
1070                 }
1071                 dest = buffer;
1072                 tag = SearchAttribute;
1073                 cBufferPos = 0;
1074             }
1075             break;
1076         }
1077         case SearchAttribute:
1078         {
1079 #if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
1080                 qDebug("SearchAttribute");
1081 #endif
1082             bool atespace = false;
1083             ushort curchar;
1084             while(!src.isEmpty()) {
1085                 curchar = *src;
1086                 // In this mode just ignore any quotes we encounter and treat them like spaces.
1087                 if (curchar > ' ' && curchar != '\'' && curchar != '"') {
1088                     if (curchar == '<' || curchar == '>')
1089                         tag = SearchEnd;
1090                     else
1091                         tag = AttributeName;
1092
1093                     cBufferPos = 0;
1094                     break;
1095                 }
1096                 atespace = true;
1097                 ++src;
1098             }
1099             break;
1100         }
1101         case AttributeName:
1102         {
1103 #if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
1104                 qDebug("AttributeName");
1105 #endif
1106             ushort curchar;
1107             int ll = kMin(src.length(), CBUFLEN-cBufferPos);
1108
1109             while(ll--) {
1110                 curchar = *src;
1111                 if(curchar <= '>') {
1112                     if(curchar <= ' ' || curchar == '=' || curchar == '>') {
1113                         unsigned int a;
1114                         cBuffer[cBufferPos] = '\0';
1115                         a = getAttrID(cBuffer, cBufferPos);
1116                         if (a)
1117                             attrNamePresent = true;
1118                         else {
1119                             attrName = QString::fromLatin1(QCString(cBuffer, cBufferPos+1).data());
1120                             attrNamePresent = !attrName.isEmpty();
1121
1122                             // This is a deliberate quirk to match Mozilla and Opera.  We have to do this
1123                             // since sites that use the "standards-compliant" path sometimes send
1124                             // <script src="foo.js"/>.  Both Moz and Opera will honor this, despite it
1125                             // being bogus HTML.  They do not honor the "/" for other tags.  This behavior
1126                             // also deviates from WinIE, but in this case we'll just copy Moz and Opera.
1127                             if (currToken.id == ID_SCRIPT && curchar == '>' &&
1128                                 attrName == "/")
1129                                 currToken.flat = true;
1130                         }
1131                         
1132                         dest = buffer;
1133                         *dest++ = a;
1134 #ifdef TOKEN_DEBUG
1135                         if (!a || (cBufferPos && *cBuffer == '!'))
1136                             kdDebug( 6036 ) << "Unknown attribute: *" << QCString(cBuffer, cBufferPos+1).data() << "*" << endl;
1137                         else
1138                             kdDebug( 6036 ) << "Known attribute: " << QCString(cBuffer, cBufferPos+1).data() << endl;
1139 #endif
1140
1141                         tag = SearchEqual;
1142                         break;
1143                     }
1144                 }
1145                 // Use tolower() instead of | 0x20 to lowercase the char because there is no 
1146                 // performance gain in using | 0x20 since tolower() is optimized and 
1147                 // | 0x20 turns characters such as '_' into junk.
1148                 cBuffer[cBufferPos++] = tolower(curchar);
1149                 ++src;
1150             }
1151             if ( cBufferPos == CBUFLEN ) {
1152                 cBuffer[cBufferPos] = '\0';
1153                 attrName = QString::fromLatin1(QCString(cBuffer, cBufferPos+1).data());
1154                 attrNamePresent = !attrName.isEmpty();
1155                 dest = buffer;
1156                 *dest++ = 0;
1157                 tag = SearchEqual;
1158             }
1159             break;
1160         }
1161         case SearchEqual:
1162         {
1163 #if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
1164                 qDebug("SearchEqual");
1165 #endif
1166             ushort curchar;
1167             bool atespace = false;
1168             while(!src.isEmpty()) {
1169                 curchar = src->unicode();
1170                 // In this mode just ignore any quotes we encounter and treat them like spaces.
1171                 if (curchar > ' ' && curchar != '\'' && curchar != '"') {
1172                     if(curchar == '=') {
1173 #ifdef TOKEN_DEBUG
1174                         kdDebug(6036) << "found equal" << endl;
1175 #endif
1176                         tag = SearchValue;
1177                         ++src;
1178                     }
1179                     else {
1180                         currToken.addAttribute(parser->docPtr()->document(), buffer, attrName, emptyAtom);
1181                         dest = buffer;
1182                         tag = SearchAttribute;
1183                     }
1184                     break;
1185                 }
1186                 atespace = true;
1187                 ++src;
1188             }
1189             break;
1190         }
1191         case SearchValue:
1192         {
1193             ushort curchar;
1194             while(!src.isEmpty()) {
1195                 curchar = src->unicode();
1196                 if(curchar > ' ') {
1197                     if(( curchar == '\'' || curchar == '\"' )) {
1198                         tquote = curchar == '\"' ? DoubleQuote : SingleQuote;
1199                         tag = QuotedValue;
1200                         ++src;
1201                     } else
1202                         tag = Value;
1203
1204                     break;
1205                 }
1206                 ++src;
1207             }
1208             break;
1209         }
1210         case QuotedValue:
1211         {
1212 #if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
1213                 qDebug("QuotedValue");
1214 #endif
1215             ushort curchar;
1216             while(!src.isEmpty()) {
1217                 checkBuffer();
1218
1219                 curchar = src->unicode();
1220                 if (curchar == '>' && !attrNamePresent) {
1221                     // Handle a case like <img '>.  Just go ahead and be willing
1222                     // to close the whole tag.  Don't consume the character and
1223                     // just go back into SearchEnd while ignoring the whole
1224                     // value.
1225                     // FIXME: Note that this is actually not a very good solution. It's
1226                     // an interim hack and doesn't handle the general case of
1227                     // unmatched quotes among attributes that have names. -dwh
1228                     while(dest > buffer+1 && (*(dest-1) == '\n' || *(dest-1) == '\r'))
1229                         dest--; // remove trailing newlines
1230                     AtomicString v(buffer+1, dest-buffer-1);
1231                     attrName.setUnicode(buffer+1,dest-buffer-1); 
1232                     currToken.addAttribute(parser->docPtr()->document(), buffer, attrName, v);
1233                     tag = SearchAttribute;
1234                     dest = buffer;
1235                     tquote = NoQuote;
1236                     break;
1237                 }
1238                 
1239                 if(curchar <= '\'' && !src.escaped()) {
1240                     // ### attributes like '&{blaa....};' are supposed to be treated as jscript.
1241                     if ( curchar == '&' )
1242                     {
1243                         ++src;
1244                         parseEntity(src, dest, true);
1245                         break;
1246                     }
1247                     else if ( (tquote == SingleQuote && curchar == '\'') ||
1248                               (tquote == DoubleQuote && curchar == '\"') )
1249                     {
1250                         // some <input type=hidden> rely on trailing spaces. argh
1251                         while(dest > buffer+1 && (*(dest-1) == '\n' || *(dest-1) == '\r'))
1252                             dest--; // remove trailing newlines
1253                         AtomicString v(buffer+1, dest-buffer-1);
1254                         if (!attrNamePresent)
1255                             attrName.setUnicode(buffer+1,dest-buffer-1); 
1256                         currToken.addAttribute(parser->docPtr()->document(), buffer, attrName, v);
1257
1258                         dest = buffer;
1259                         tag = SearchAttribute;
1260                         tquote = NoQuote;
1261                         ++src;
1262                         break;
1263                     }
1264                 }
1265                 *dest = *src;
1266                 fixUpChar(*dest);
1267                 ++dest;
1268                 ++src;
1269             }
1270             break;
1271         }
1272         case Value:
1273         {
1274 #if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
1275             qDebug("Value");
1276 #endif
1277             ushort curchar;
1278             while(!src.isEmpty()) {
1279                 checkBuffer();
1280                 curchar = src->unicode();
1281                 if(curchar <= '>' && !src.escaped()) {
1282                     // parse Entities
1283                     if ( curchar == '&' )
1284                     {
1285                         ++src;
1286                         parseEntity(src, dest, true);
1287                         break;
1288                     }
1289                     // no quotes. Every space means end of value
1290                     // '/' does not delimit in IE!
1291                     if ( curchar <= ' ' || curchar == '>' )
1292                     {
1293                         AtomicString v(buffer+1, dest-buffer-1);
1294                         currToken.addAttribute(parser->docPtr()->document(), buffer, attrName, v);
1295                         dest = buffer;
1296                         tag = SearchAttribute;
1297                         break;
1298                     }
1299                 }
1300
1301                 *dest = *src;
1302                 fixUpChar(*dest);
1303                 ++dest;
1304                 ++src;
1305             }
1306             break;
1307         }
1308         case SearchEnd:
1309         {
1310 #if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1
1311                 qDebug("SearchEnd");
1312 #endif
1313             while(!src.isEmpty()) {
1314                 if (*src == '>' || *src == '<')
1315                     break;
1316
1317                 if (*src == '/')
1318                     currToken.flat = true;
1319
1320                 ++src;
1321             }
1322             if (src.isEmpty()) break;
1323
1324             searchCount = 0; // Stop looking for '<!--' sequence
1325             tag = NoTag;
1326             tquote = NoQuote;
1327
1328             if (*src != '<')
1329                 ++src;
1330
1331             if ( !currToken.id ) //stop if tag is unknown
1332                 return;
1333
1334             uint tagID = currToken.id;
1335 #if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 0
1336             kdDebug( 6036 ) << "appending Tag: " << tagID << endl;
1337 #endif
1338             bool beginTag = !currToken.flat && (tagID <= ID_CLOSE_TAG);
1339
1340             if (tagID > ID_CLOSE_TAG)
1341                 tagID -= ID_CLOSE_TAG;
1342             else if (tagID == ID_SCRIPT) {
1343                 AttributeImpl* a = 0;
1344                 bool foundTypeAttribute = false;
1345                 scriptSrc = QString::null;
1346                 scriptSrcCharset = QString::null;
1347                 if ( currToken.attrs && /* potentially have a ATTR_SRC ? */
1348                      parser->doc()->part() &&
1349                      parser->doc()->part()->jScriptEnabled() && /* jscript allowed at all? */
1350                      view /* are we a regular tokenizer or just for innerHTML ? */
1351                     ) {
1352                     if ( ( a = currToken.attrs->getAttributeItem( ATTR_SRC ) ) )
1353                         scriptSrc = parser->doc()->completeURL(parseURL( a->value() ).string() );
1354                     if ( ( a = currToken.attrs->getAttributeItem( ATTR_CHARSET ) ) )
1355                         scriptSrcCharset = a->value().string().stripWhiteSpace();
1356                     if ( scriptSrcCharset.isEmpty() )
1357                         scriptSrcCharset = parser->doc()->part()->encoding();
1358                     /* Check type before language, since language is deprecated */
1359                     if ((a = currToken.attrs->getAttributeItem(ATTR_TYPE)) != 0 && !a->value().string().isEmpty())
1360                         foundTypeAttribute = true;
1361                     else
1362                         a = currToken.attrs->getAttributeItem(ATTR_LANGUAGE);
1363                 }
1364                 javascript = true;
1365
1366                 if( foundTypeAttribute ) {
1367                     /* 
1368                         Mozilla 1.5 accepts application/x-javascript, and some web references claim it is the only
1369                         correct variation, but WinIE 6 doesn't accept it.
1370                         Neither Mozilla 1.5 nor WinIE 6 accept application/javascript, application/ecmascript, or
1371                         application/x-ecmascript.
1372                         Mozilla 1.5 doesn't accept the text/javascript1.x formats, but WinIE 6 does.
1373                         Mozilla 1.5 doesn't accept text/jscript, text/ecmascript, and text/livescript, but WinIE 6 does.
1374                         Mozilla 1.5 allows leading and trailing whitespace, but WinIE 6 doesn't.
1375                         Mozilla 1.5 and WinIE 6 both accept the empty string, but neither accept a whitespace-only string.
1376                         We want to accept all the values that either of these browsers accept, but not other values.
1377                      */
1378                     QString type = a->value().string().stripWhiteSpace().lower();
1379                     if( type.compare("application/x-javascript") != 0 &&
1380                         type.compare("text/javascript") != 0 &&
1381                         type.compare("text/javascript1.0") != 0 &&
1382                         type.compare("text/javascript1.1") != 0 &&
1383                         type.compare("text/javascript1.2") != 0 &&
1384                         type.compare("text/javascript1.3") != 0 &&
1385                         type.compare("text/javascript1.4") != 0 &&
1386                         type.compare("text/javascript1.5") != 0 &&
1387                         type.compare("text/jscript") != 0 &&
1388                         type.compare("text/ecmascript") != 0 &&
1389                         type.compare("text/livescript") )
1390                         javascript = false;
1391                 } else if( a ) {
1392                     /* 
1393                      Mozilla 1.5 doesn't accept jscript or ecmascript, but WinIE 6 does.
1394                      Mozilla 1.5 accepts javascript1.0, javascript1.4, and javascript1.5, but WinIE 6 accepts only 1.1 - 1.3.
1395                      Neither Mozilla 1.5 nor WinIE 6 accept leading or trailing whitespace.
1396                      We want to accept all the values that either of these browsers accept, but not other values.
1397                      */
1398                     QString lang = a->value().string();
1399                     lang = lang.lower();
1400                     if( lang.compare("") != 0 &&
1401                         lang.compare("javascript") != 0 &&
1402                         lang.compare("javascript1.0") != 0 &&
1403                         lang.compare("javascript1.1") != 0 &&
1404                         lang.compare("javascript1.2") != 0 &&
1405                         lang.compare("javascript1.3") != 0 &&
1406                         lang.compare("javascript1.4") != 0 &&
1407                         lang.compare("javascript1.5") != 0 &&
1408                         lang.compare("ecmascript") != 0 &&
1409                         lang.compare("livescript") != 0 &&
1410                         lang.compare("jscript") )
1411                         javascript = false;
1412                 }
1413             }
1414
1415             processToken();
1416
1417             // we have to take care to close the pre block in
1418             // case we encounter an unallowed element....
1419             if(pre && beginTag && !DOM::checkChild(ID_PRE, tagID, !parser->doc()->inCompatMode())) {
1420                 kdDebug(6036) << " not allowed in <pre> " << (int)tagID << endl;
1421                 pre = false;
1422             }
1423
1424             switch( tagID ) {
1425             case ID_PRE:
1426                 prePos = 0;
1427                 pre = beginTag;
1428                 discard = LFDiscard; // Discard the first LF after we open a pre.
1429                 break;
1430             case ID_SCRIPT:
1431                 if (beginTag) {
1432                     searchStopper = scriptEnd;
1433                     searchStopperLen = 8;
1434                     script = true;
1435                     parseSpecial(src);
1436                 }
1437                 else if (tagID <= ID_CLOSE_TAG) { // Handle <script src="foo"/>
1438                     script = true;
1439                     scriptHandler();
1440                 }
1441                 break;
1442             case ID_STYLE:
1443                 if (beginTag) {
1444                     searchStopper = styleEnd;
1445                     searchStopperLen = 7;
1446                     style = true;
1447                     parseSpecial(src);
1448                 }
1449                 break;
1450             case ID_TEXTAREA:
1451                 if(beginTag) {
1452                     searchStopper = textareaEnd;
1453                     searchStopperLen = 10;
1454                     textarea = true;
1455                     parseSpecial(src);
1456                 }
1457                 break;
1458             case ID_TITLE:
1459                 if (beginTag) {
1460                     searchStopper = titleEnd;
1461                     searchStopperLen = 7;
1462                     title = true;
1463                     parseSpecial(src);
1464                 }
1465                 break;
1466             case ID_XMP:
1467                 if (beginTag) {
1468                     searchStopper = xmpEnd;
1469                     searchStopperLen = 5;
1470                     xmp = true;
1471                     parseSpecial(src);
1472                 }
1473                 break;
1474             case ID_SELECT:
1475                 select = beginTag;
1476                 break;
1477             case ID_PLAINTEXT:
1478                 plaintext = beginTag;
1479                 break;
1480             }
1481
1482             return; // Finished parsing tag!
1483         }
1484         } // end switch
1485     }
1486     return;
1487 }
1488
1489 void HTMLTokenizer::addPending()
1490 {
1491     if ( select && !script )
1492     {
1493         *dest++ = ' ';
1494     }
1495     else if ( textarea || script )
1496     {
1497         switch(pending) {
1498         case LFPending:  *dest++ = '\n'; prePos = 0; break;
1499         case SpacePending: *dest++ = ' '; ++prePos; break;
1500         case TabPending: *dest++ = '\t'; prePos += TAB_SIZE - (prePos % TAB_SIZE); break;
1501         case NonePending:
1502             assert(0);
1503         }
1504     }
1505     else
1506     {
1507         int p;
1508
1509         switch (pending)
1510         {
1511         case SpacePending:
1512             // Insert a breaking space
1513             *dest++ = QChar(' ');
1514             prePos++;
1515             break;
1516
1517         case LFPending:
1518             *dest = '\n';
1519             dest++;
1520             prePos = 0;
1521             break;
1522
1523         case TabPending:
1524             p = TAB_SIZE - ( prePos % TAB_SIZE );
1525 #ifdef TOKEN_DEBUG
1526             qDebug("tab pending, prePos: %d, toadd: %d", prePos, p);
1527 #endif
1528
1529             for ( int x = 0; x < p; x++ )
1530                 *dest++ = QChar(' ');
1531             prePos += p;
1532             break;
1533
1534         case NonePending:
1535             assert(0);
1536             break;
1537         }
1538     }
1539     
1540     pending = NonePending;
1541 }
1542
1543 void HTMLTokenizer::write(const TokenizerString &str, bool appendData)
1544 {
1545 #ifdef TOKEN_DEBUG
1546     kdDebug( 6036 ) << this << " Tokenizer::write(\"" << str << "\"," << appendData << ")" << endl;
1547 #endif
1548
1549     if (!buffer)
1550         return;
1551     
1552     if (loadStopped)
1553         return;
1554
1555     if ( ( m_executingScript && appendData ) || !cachedScript.isEmpty() ) {
1556         // don't parse; we will do this later
1557         if (currentPrependingSrc) {
1558             currentPrependingSrc->append(str);
1559         } else {
1560             pendingSrc.append(str);
1561         }
1562         return;
1563     }
1564
1565     if ( onHold ) {
1566         src.append(str);
1567         return;
1568     }
1569     
1570     if (!src.isEmpty())
1571         src.append(str);
1572     else
1573         setSrc(str);
1574
1575     // Once a timer is set, it has control of when the tokenizer continues.
1576     if (timerId)
1577         return;
1578
1579     bool wasInWrite = inWrite;
1580     inWrite = true;
1581     
1582 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1583     if (!parser->doc()->ownerElement())
1584         printf("Beginning write at time %d\n", parser->doc()->elapsedTime());
1585 #endif
1586     
1587 //     if (Entity)
1588 //         parseEntity(src, dest);
1589
1590     int processedCount = 0;
1591     QTime startTime;
1592     startTime.start();
1593     KWQUIEventTime eventTime;
1594
1595     while (!src.isEmpty() && (!parser->doc()->part() || !parser->doc()->part()->isScheduledLocationChangePending())) {
1596         if (!continueProcessing(processedCount, startTime, eventTime))
1597             break;
1598
1599         // do we need to enlarge the buffer?
1600         checkBuffer();
1601
1602         ushort cc = src->unicode();
1603
1604         if (skipLF && (cc != '\n'))
1605             skipLF = false;
1606
1607         if (skipLF) {
1608             skipLF = false;
1609             ++src;
1610         }
1611         else if ( Entity )
1612             parseEntity( src, dest );
1613         else if ( plaintext )
1614             parseText( src );
1615         else if (script)
1616             parseSpecial(src);
1617         else if (style)
1618             parseSpecial(src);
1619         else if (xmp)
1620             parseSpecial(src);
1621         else if (textarea)
1622             parseSpecial(src);
1623         else if (title)
1624             parseSpecial(src);
1625         else if (comment)
1626             parseComment(src);
1627         else if (server)
1628             parseServer(src);
1629         else if (processingInstruction)
1630             parseProcessingInstruction(src);
1631         else if (tag)
1632             parseTag(src);
1633         else if ( startTag )
1634         {
1635             startTag = false;
1636
1637             switch(cc) {
1638             case '/':
1639                 break;
1640             case '!':
1641             {
1642                 // <!-- comment -->
1643                 searchCount = 1; // Look for '<!--' sequence to start comment
1644
1645                 break;
1646             }
1647             case '?':
1648             {
1649                 // xml processing instruction
1650                 processingInstruction = true;
1651                 tquote = NoQuote;
1652                 parseProcessingInstruction(src);
1653                 continue;
1654
1655                 break;
1656             }
1657             case '%':
1658                 if (!brokenServer) {
1659                     // <% server stuff, handle as comment %>
1660                     server = true;
1661                     tquote = NoQuote;
1662                     parseServer(src);
1663                     continue;
1664                 }
1665                 // else fall through
1666             default:
1667             {
1668                 if( ((cc >= 'a') && (cc <= 'z')) || ((cc >= 'A') && (cc <= 'Z')))
1669                 {
1670                     // Start of a Start-Tag
1671                 }
1672                 else
1673                 {
1674                     // Invalid tag
1675                     // Add as is
1676                     if (pending)
1677                         addPending();
1678                     *dest = '<';
1679                     dest++;
1680                     continue;
1681                 }
1682             }
1683             }; // end case
1684
1685             if ( pending ) {
1686                 // pre context always gets its spaces/linefeeds
1687                 if ( pre || script || (!parser->selectMode() &&
1688                              (!parser->noSpaces() || dest > buffer )))
1689                     addPending();
1690                 // just forget it
1691                 else
1692                     pending = NonePending;
1693             }
1694
1695             if (cc == '/' && discard == AllDiscard)
1696                 discard = NoneDiscard; // A close tag. No need to discard LF.
1697                     
1698             processToken();
1699
1700             cBufferPos = 0;
1701             tag = TagName;
1702             parseTag(src);
1703         }
1704         else if ( cc == '&' && !src.escaped())
1705         {
1706             ++src;
1707             if ( pending )
1708                 addPending();
1709             parseEntity(src, dest, true);
1710         }
1711         else if ( cc == '<' && !src.escaped())
1712         {
1713             tagStartLineno = lineno+src.lineCount();
1714             ++src;
1715             startTag = true;
1716         }
1717         else if (( cc == '\n' ) || ( cc == '\r' ))
1718         {
1719             if (select && !script)
1720             {
1721                 if (discard == LFDiscard)
1722                 {
1723                     // Ignore this LF
1724                     discard = NoneDiscard; // We have discarded 1 LF
1725                 }
1726                 else if(discard == AllDiscard)
1727                 {
1728                 }
1729                 else
1730                  {
1731                      // Process this LF
1732                     if (pending == NonePending)
1733                          pending = LFPending;
1734                 }
1735             }
1736             else {
1737                 if (discard == LFDiscard || discard == AllDiscard)
1738                 {
1739                     // Ignore this LF
1740                     discard = NoneDiscard; // We have discarded 1 LF
1741                 }
1742                 else
1743                 {
1744                     // Process this LF
1745                     if (pending)
1746                         addPending();
1747                     pending = LFPending;
1748                 }
1749             }
1750             
1751             /* Check for MS-DOS CRLF sequence */
1752             if (cc == '\r')
1753             {
1754                 skipLF = true;
1755             }
1756             ++src;
1757         }
1758         else if (( cc == ' ' ) || ( cc == '\t' ))
1759         {
1760             if (select && !script) {
1761                 if(discard == SpaceDiscard)
1762                     discard = NoneDiscard;
1763                  else if(discard == AllDiscard)
1764                  { }
1765                  else
1766                      pending = SpacePending;
1767             
1768             }
1769             else {
1770                 if (discard == AllDiscard)
1771                     discard = NoneDiscard;
1772             
1773                 if (pending)
1774                     addPending();
1775                 if (cc == ' ')
1776                     pending = SpacePending;
1777                 else
1778                     pending = TabPending;
1779             }
1780             
1781             ++src;
1782         }
1783         else
1784         {
1785             if (pending)
1786                 addPending();
1787
1788             discard = NoneDiscard;
1789             if ( pre )
1790             {
1791                 prePos++;
1792             }
1793 #if QT_VERSION < 300
1794             unsigned char row = src->row();
1795             if ( row > 0x05 && row < 0x10 || row > 0xfd )
1796                     currToken.complexText = true;
1797 #endif
1798             *dest = *src;
1799             fixUpChar( *dest );
1800             ++dest;
1801             ++src;
1802         }
1803     }
1804     
1805 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1806     if (!parser->doc()->ownerElement())
1807         printf("Ending write at time %d\n", parser->doc()->elapsedTime());
1808 #endif
1809     
1810     inWrite = wasInWrite;
1811
1812     if (noMoreData && !inWrite && !loadingExtScript && !m_executingScript && !timerId)
1813         end(); // this actually causes us to be deleted
1814 }
1815
1816 void HTMLTokenizer::stopped()
1817 {
1818     if (timerId) {
1819         killTimer(timerId);
1820         timerId = 0;
1821     }
1822 }
1823
1824 bool HTMLTokenizer::processingData() const
1825 {
1826     return timerId != 0;
1827 }
1828
1829 bool HTMLTokenizer::continueProcessing(int& processedCount, const QTime& startTime, const KWQUIEventTime& eventTime)
1830 {
1831     // We don't want to be checking elapsed time with every character, so we only check after we've
1832     // processed a certain number of characters.
1833     bool allowedYield = allowYield;
1834     allowYield = false;
1835     if (!loadingExtScript && !forceSynchronous && !m_executingScript && (processedCount > TOKENIZER_CHUNK_SIZE || allowedYield)) {
1836         processedCount = 0;
1837         if (startTime.elapsed() > TOKENIZER_TIME_DELAY) {
1838             /* FIXME: We'd like to yield aggressively to give stylesheets the opportunity to
1839                load, but this hurts overall performance on slower machines.  For now turn this
1840                off.
1841             || (!parser->doc()->haveStylesheetsLoaded() && 
1842                 (parser->doc()->documentElement()->id() != ID_HTML || parser->doc()->body()))) {*/
1843             // Schedule the timer to keep processing as soon as possible.
1844             if (!timerId)
1845                 timerId = startTimer(0);
1846 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1847             if (eventTime.uiEventPending())
1848                 printf("Deferring processing of data because of UI event.\n");
1849             else if (startTime.elapsed() > TOKENIZER_TIME_DELAY)
1850                 printf("Deferring processing of data because 200ms elapsed away from event loop.\n");
1851 #endif
1852             return false;
1853         }
1854     }
1855     
1856     processedCount++;
1857     return true;
1858 }
1859
1860 void HTMLTokenizer::timerEvent(QTimerEvent* e)
1861 {
1862     if (e->timerId() == timerId) {
1863         // Kill the timer.
1864         killTimer(timerId);
1865         timerId = 0;
1866
1867 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1868         if (!parser->doc()->ownerElement())
1869             printf("Beginning timer write at time %d\n", parser->doc()->elapsedTime());
1870 #endif
1871
1872         if (parser->doc()->view() && parser->doc()->view()->layoutPending() && !parser->doc()->minimumLayoutDelay()) {
1873             // Restart the timer and let layout win.  This is basically a way of ensuring that the layout
1874             // timer has higher priority than our timer.
1875             timerId = startTimer(0);
1876             return;
1877         }
1878         
1879         // Invoke write() as though more data came in.
1880         bool oldNoMoreData = noMoreData;
1881         noMoreData = false;  // This prevents write() from deleting the tokenizer.
1882         write(TokenizerString(), true);
1883         noMoreData = oldNoMoreData;
1884         
1885         // If the timer dies (and stays dead after the write),  we need to let WebKit know that we're done processing the data.
1886         allDataProcessed();
1887     }
1888 }
1889
1890 void HTMLTokenizer::allDataProcessed()
1891 {
1892     if (noMoreData && !inWrite && !loadingExtScript && !m_executingScript && !onHold && !timerId) {
1893         QGuardedPtr<KHTMLView> savedView = view;
1894         end();
1895         if (savedView) {
1896             KHTMLPart *part = savedView->part();
1897             if (part) {
1898                 part->tokenizerProcessedData();
1899             }
1900         }
1901     }
1902 }
1903
1904 void HTMLTokenizer::end()
1905 {
1906     assert(timerId == 0);
1907     if (timerId) {
1908         // Clean up anyway.
1909         killTimer(timerId);
1910         timerId = 0;
1911     }
1912
1913     if ( buffer == 0 ) {
1914         parser->finished();
1915         emit finishedParsing();
1916         return;
1917     }
1918
1919     // parseTag is using the buffer for different matters
1920     if ( !tag )
1921         processToken();
1922
1923     if(buffer)
1924         KHTML_DELETE_QCHAR_VEC(buffer);
1925
1926     if(scriptCode)
1927         KHTML_DELETE_QCHAR_VEC(scriptCode);
1928
1929     scriptCode = 0;
1930     scriptCodeSize = scriptCodeMaxSize = scriptCodeResync = 0;
1931     buffer = 0;
1932     parser->finished();
1933     emit finishedParsing();
1934 }
1935
1936 void HTMLTokenizer::finish()
1937 {
1938     // do this as long as we don't find matching comment ends
1939     while((comment || server) && scriptCode && scriptCodeSize)
1940     {
1941         // we've found an unmatched comment start
1942         if (comment)
1943             brokenComments = true;
1944         else
1945             brokenServer = true;
1946         checkScriptBuffer();
1947         scriptCode[ scriptCodeSize ] = 0;
1948         scriptCode[ scriptCodeSize + 1 ] = 0;
1949         int pos;
1950         QString food;
1951         if (script || style) {
1952             food.setUnicode(scriptCode, scriptCodeSize);
1953         }
1954         else if (server) {
1955             food = "<";
1956             food += QString(scriptCode, scriptCodeSize);
1957         }
1958         else {
1959             pos = QConstString(scriptCode, scriptCodeSize).string().find('>');
1960             food.setUnicode(scriptCode+pos+1, scriptCodeSize-pos-1); // deep copy
1961         }
1962         KHTML_DELETE_QCHAR_VEC(scriptCode);
1963         scriptCode = 0;
1964         scriptCodeSize = scriptCodeMaxSize = scriptCodeResync = 0;
1965         comment = server = false;
1966         if ( !food.isEmpty() )
1967             write(food, true);
1968     }
1969     // this indicates we will not receive any more data... but if we are waiting on
1970     // an external script to load, we can't finish parsing until that is done
1971     noMoreData = true;
1972     if (!inWrite && !loadingExtScript && !m_executingScript && !onHold && !timerId)
1973         end(); // this actually causes us to be deleted
1974 }
1975
1976 void HTMLTokenizer::processToken()
1977 {
1978     KJSProxy *jsProxy = (view && view->part()) ? view->part()->jScript() : 0L;    
1979     if (jsProxy)
1980         jsProxy->setEventHandlerLineno(tagStartLineno);
1981     if ( dest > buffer )
1982     {
1983 #ifdef TOKEN_DEBUG
1984         if(currToken.id) {
1985             qDebug( "unexpected token id: %d, str: *%s*", currToken.id,QConstString( buffer,dest-buffer ).string().latin1() );
1986             assert(0);
1987         }
1988
1989 #endif
1990         currToken.text = new DOMStringImpl( buffer, dest - buffer );
1991         currToken.text->ref();
1992         if (currToken.id != ID_COMMENT)
1993             currToken.id = ID_TEXT;
1994     }
1995     else if(!currToken.id) {
1996         currToken.reset();
1997         if (jsProxy)
1998             jsProxy->setEventHandlerLineno(lineno+src.lineCount());
1999         return;
2000     }
2001
2002     dest = buffer;
2003
2004 #ifdef TOKEN_DEBUG
2005     QString name = getTagName(currToken.id).string();
2006     QString text;
2007     if(currToken.text)
2008         text = QConstString(currToken.text->s, currToken.text->l).string();
2009
2010     kdDebug( 6036 ) << "Token --> " << name << "   id = " << currToken.id << endl;
2011     if (currToken.flat)
2012         kdDebug( 6036 ) << "Token is FLAT!" << endl;
2013     if(!text.isNull())
2014         kdDebug( 6036 ) << "text: \"" << text << "\"" << endl;
2015     unsigned long l = currToken.attrs ? currToken.attrs->length() : 0;
2016     if(l) {
2017         kdDebug( 6036 ) << "Attributes: " << l << endl;
2018         for (unsigned long i = 0; i < l; ++i) {
2019             AttributeImpl* c = currToken.attrs->attributeItem(i);
2020             kdDebug( 6036 ) << "    " << c->id() << " " << parser->doc()->getDocument()->attrName(c->id()).string()
2021                             << "=\"" << c->value().string() << "\"" << endl;
2022         }
2023     }
2024     kdDebug( 6036 ) << endl;
2025 #endif
2026     
2027     if (!loadStopped) {
2028         // pass the token over to the parser, the parser DOES NOT delete the token
2029         parser->parseToken(&currToken);
2030     }
2031     
2032     currToken.reset();
2033     if (jsProxy)
2034         jsProxy->setEventHandlerLineno(0);
2035 }
2036
2037 HTMLTokenizer::~HTMLTokenizer()
2038 {
2039     assert(!inWrite);
2040     reset();
2041     delete parser;
2042 }
2043
2044
2045 void HTMLTokenizer::enlargeBuffer(int len)
2046 {
2047     int newsize = kMax(size*2, size+len);
2048     int oldoffs = (dest - buffer);
2049
2050     buffer = (QChar*)realloc(buffer, newsize*sizeof(QChar));
2051     dest = buffer + oldoffs;
2052     size = newsize;
2053 }
2054
2055 void HTMLTokenizer::enlargeScriptBuffer(int len)
2056 {
2057     int newsize = kMax(scriptCodeMaxSize*2, scriptCodeMaxSize+len);
2058     scriptCode = (QChar*)realloc(scriptCode, newsize*sizeof(QChar));
2059     scriptCodeMaxSize = newsize;
2060 }
2061
2062 void HTMLTokenizer::notifyFinished(CachedObject */*finishedObj*/)
2063 {
2064 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2065     if (!parser->doc()->ownerElement())
2066         printf("script loaded at %d\n", parser->doc()->elapsedTime());
2067 #endif
2068
2069     assert(!cachedScript.isEmpty());
2070     bool finished = false;
2071     while (!finished && cachedScript.head()->isLoaded()) {
2072 #ifdef TOKEN_DEBUG
2073         kdDebug( 6036 ) << "Finished loading an external script" << endl;
2074 #endif
2075         CachedScript* cs = cachedScript.dequeue();
2076         DOMString scriptSource = cs->script();
2077 #ifdef TOKEN_DEBUG
2078         kdDebug( 6036 ) << "External script is:" << endl << scriptSource.string() << endl;
2079 #endif
2080         setSrc(TokenizerString());
2081
2082         // make sure we forget about the script before we execute the new one
2083         // infinite recursion might happen otherwise
2084         QString cachedScriptUrl( cs->url().string() );
2085         cs->deref(this);
2086
2087 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2088         if (!parser->doc()->ownerElement())
2089             printf("external script beginning execution at %d\n", parser->doc()->elapsedTime());
2090 #endif
2091
2092         scriptExecution( scriptSource.string(), cachedScriptUrl );
2093
2094         // The state of cachedScript.isEmpty() can change inside the scriptExecution()
2095         // call above, so test afterwards.
2096         finished = cachedScript.isEmpty();
2097         if (finished) {
2098             loadingExtScript = false;
2099 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2100             if (!parser->doc()->ownerElement())
2101                 printf("external script finished execution at %d\n", parser->doc()->elapsedTime());
2102 #endif
2103         }
2104
2105         // 'script' is true when we are called synchronously from
2106         // parseScript(). In that case parseScript() will take care
2107         // of 'scriptOutput'.
2108         if ( !script ) {
2109             TokenizerString rest = pendingSrc;
2110             pendingSrc.clear();
2111             write(rest, false);
2112             // we might be deleted at this point, do not
2113             // access any members.
2114         }
2115     }
2116 }
2117
2118 bool HTMLTokenizer::isWaitingForScripts() const
2119 {
2120     return loadingExtScript;
2121 }
2122
2123 void HTMLTokenizer::setSrc(const TokenizerString &source)
2124 {
2125     lineno += src.lineCount();
2126     src = source;
2127     src.resetLineCount();
2128 }
2129
2130 void HTMLTokenizer::setOnHold(bool _onHold)
2131 {
2132     onHold = _onHold;
2133 }
2134
2135 }