Fix for 3873234, Safari UI is unresponsive when parsing multiple HTML docs and 38732...
authorhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Nov 2004 03:47:56 +0000 (03:47 +0000)
committerhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Nov 2004 03:47:56 +0000 (03:47 +0000)
        loading large local files.

        Reviewed by mjs

        * khtml/html/htmltokenizer.cpp:
        (khtml::HTMLTokenizer::HTMLTokenizer):
        (khtml::HTMLTokenizer::reset):
        (khtml::HTMLTokenizer::write):
        (khtml::HTMLTokenizer::stopped):
        (khtml::HTMLTokenizer::processingData):
        (khtml::HTMLTokenizer::continueProcessing):
        (khtml::HTMLTokenizer::timerEvent):
        (khtml::HTMLTokenizer::allDataProcessed):
        (khtml::HTMLTokenizer::end):
        (khtml::HTMLTokenizer::finish):
        (khtml::HTMLTokenizer::notifyFinished):
        * khtml/html/htmltokenizer.h:
        * khtml/khtml_part.cpp:
        (KHTMLPart::slotFinished):
        (KHTMLPart::end):
        (KHTMLPart::stop):
        * khtml/khtml_part.h:
        (KHTMLPart::tokenizerProcessedData):
        * khtml/khtmlview.cpp:
        * khtml/xml/dom_docimpl.cpp:
        * khtml/xml/xml_tokenizer.h:
        (khtml::Tokenizer::stopped):
        (khtml::Tokenizer::processingData):
        * kwq/KWQDateTime.h:
        * kwq/KWQDateTime.mm:
        (QDateTime::secsTo):
        (KWQUIEventTime::uiEventPending):
        * kwq/KWQKHTMLPart.h:
        * kwq/KWQKHTMLPart.mm:
        (KWQKHTMLPart::tokenizerProcessedData):
        * kwq/WebCoreBridge.h:
        * kwq/WebCoreBridge.mm:
        (-[WebCoreBridge stop]):
        (-[WebCoreBridge numPendingOrLoadingRequests]):
        (-[WebCoreBridge doneProcessingData]):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@7966 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
WebCore/ChangeLog-2005-08-23
WebCore/khtml/html/htmltokenizer.cpp
WebCore/khtml/html/htmltokenizer.h
WebCore/khtml/khtml_part.cpp
WebCore/khtml/khtml_part.h
WebCore/khtml/khtmlview.cpp
WebCore/khtml/xml/dom_docimpl.cpp
WebCore/khtml/xml/xml_tokenizer.h
WebCore/kwq/KWQDateTime.h
WebCore/kwq/KWQDateTime.mm
WebCore/kwq/KWQKHTMLPart.h
WebCore/kwq/KWQKHTMLPart.mm
WebCore/kwq/WebCoreBridge.h
WebCore/kwq/WebCoreBridge.mm

index 19ad2dd831703727e990ca3e5eb25dd5bd1b9bc8..fe1c1f90868d2a34eac50c50a4c4dc55f586f100 100644 (file)
@@ -1,3 +1,47 @@
+2004-11-09  David Hyatt  <hyatt@apple.com>
+
+       Fix for 3873234, Safari UI is unresponsive when parsing multiple HTML docs and 3873233, Safari hangs when
+        loading large local files.
+       
+        Reviewed by mjs
+
+        * khtml/html/htmltokenizer.cpp:
+        (khtml::HTMLTokenizer::HTMLTokenizer):
+        (khtml::HTMLTokenizer::reset):
+        (khtml::HTMLTokenizer::write):
+        (khtml::HTMLTokenizer::stopped):
+        (khtml::HTMLTokenizer::processingData):
+        (khtml::HTMLTokenizer::continueProcessing):
+        (khtml::HTMLTokenizer::timerEvent):
+        (khtml::HTMLTokenizer::allDataProcessed):
+        (khtml::HTMLTokenizer::end):
+        (khtml::HTMLTokenizer::finish):
+        (khtml::HTMLTokenizer::notifyFinished):
+        * khtml/html/htmltokenizer.h:
+        * khtml/khtml_part.cpp:
+        (KHTMLPart::slotFinished):
+        (KHTMLPart::end):
+        (KHTMLPart::stop):
+        * khtml/khtml_part.h:
+        (KHTMLPart::tokenizerProcessedData):
+        * khtml/khtmlview.cpp:
+        * khtml/xml/dom_docimpl.cpp:
+        * khtml/xml/xml_tokenizer.h:
+        (khtml::Tokenizer::stopped):
+        (khtml::Tokenizer::processingData):
+        * kwq/KWQDateTime.h:
+        * kwq/KWQDateTime.mm:
+        (QDateTime::secsTo):
+        (KWQUIEventTime::uiEventPending):
+        * kwq/KWQKHTMLPart.h:
+        * kwq/KWQKHTMLPart.mm:
+        (KWQKHTMLPart::tokenizerProcessedData):
+        * kwq/WebCoreBridge.h:
+        * kwq/WebCoreBridge.mm:
+        (-[WebCoreBridge stop]):
+        (-[WebCoreBridge numPendingOrLoadingRequests]):
+        (-[WebCoreBridge doneProcessingData]):
+
 2004-11-09  David Harrison  <harrison@apple.com>
 
         Reviewed by Ken Kocienda.
index 45b4357a404bf55e366b20eff0ca7e8bb874f9de..bd2fb353d59abb93ee04e577a0ade5305123f683 100644 (file)
@@ -73,6 +73,11 @@ using DOM::endTag;
 #include "kentities.c"
 #undef __inline
 
+// #define INSTRUMENT_LAYOUT_SCHEDULING 1
+
+#define TOKENIZER_CHUNK_SIZE  4096
+#define TOKENIZER_TIME_DELAY  500
+
 namespace khtml {
 
 static const char commentStart [] = "<!--";
@@ -239,6 +244,7 @@ HTMLTokenizer::HTMLTokenizer(DOM::DocumentPtr *_doc, KHTMLView *_view, bool incl
     loadingExtScript = false;
     onHold = false;
     attrNamePresent = false;
+    timerId = 0;
     includesCommentsInDOM = includesComments;
     
     begin();
@@ -258,6 +264,7 @@ HTMLTokenizer::HTMLTokenizer(DOM::DocumentPtr *_doc, DOM::DocumentFragmentImpl *
     m_executingScript = 0;
     loadingExtScript = false;
     onHold = false;
+    timerId = 0;
     includesCommentsInDOM = includesComments;
 
     begin();
@@ -281,6 +288,12 @@ void HTMLTokenizer::reset()
     scriptCode = 0;
     scriptCodeSize = scriptCodeMaxSize = scriptCodeResync = 0;
 
+    if (timerId) {
+        killTimer(timerId);
+        timerId = 0;
+    }
+    timerId = 0;
+
     currToken.reset();
 }
 
@@ -1522,7 +1535,10 @@ void HTMLTokenizer::write(const TokenizerString &str, bool appendData)
         return;
     }
     
-    setSrc(str);
+    if (!src.isEmpty())
+        src.append(str);
+    else
+        setSrc(str);
 
 #ifndef NDEBUG
     inWrite = true;
@@ -1531,7 +1547,15 @@ void HTMLTokenizer::write(const TokenizerString &str, bool appendData)
 //     if (Entity)
 //         parseEntity(src, dest);
 
+    int processedCount = 0;
+    QTime startTime;
+    startTime.start();
+    KWQUIEventTime eventTime;
+
     while (!src.isEmpty() && (!parser->doc()->part() || !parser->doc()->part()->isScheduledLocationChangePending())) {
+        if (!continueProcessing(processedCount, startTime, eventTime))
+            break;
+
         // do we need to enlarge the buffer?
         checkBuffer();
 
@@ -1744,12 +1768,81 @@ void HTMLTokenizer::write(const TokenizerString &str, bool appendData)
     inWrite = false;
 #endif
 
-    if (noMoreData && !loadingExtScript && !m_executingScript )
+    if (noMoreData && !loadingExtScript && !m_executingScript && !timerId)
         end(); // this actually causes us to be deleted
 }
 
+void HTMLTokenizer::stopped()
+{
+    if (timerId) {
+        killTimer(timerId);
+        timerId = 0;
+    }
+}
+
+bool HTMLTokenizer::processingData() const
+{
+    return timerId != 0;
+}
+
+bool HTMLTokenizer::continueProcessing(int& processedCount, const QTime& startTime, const KWQUIEventTime& eventTime)
+{
+    // FIXME: For now, we don't bother trying to break out of the middle of an executing script.
+    // We don't want to be checking elapsed time with every character, so we only check after we've
+    // processed a certain number of characters.
+    if (!loadingExtScript && !m_executingScript && processedCount > TOKENIZER_CHUNK_SIZE) {
+        processedCount = 0;
+        if (eventTime.uiEventPending() || startTime.elapsed() > TOKENIZER_TIME_DELAY || !parser->doc()->haveStylesheetsLoaded() ||
+            (parser->doc()->ownerElement() && parser->doc()->ownerElement()->getDocument()->parsing())) {
+            // Schedule the timer to keep processing as soon as possible.
+            if (!timerId)
+                timerId = startTimer(0);
+            return false;
+        }
+    }
+    
+    processedCount++;
+    return true;
+}
+
+void HTMLTokenizer::timerEvent(QTimerEvent* e)
+{
+    if (e->timerId() == timerId) {
+        // Kill the timer.
+        killTimer(timerId);
+        timerId = 0;
+
+        // Invoke write() as though more data came in.
+        bool oldNoMoreData = noMoreData;
+        noMoreData = false;  // This prevents write() from deleting the tokenizer.
+        write(TokenizerString(), true);
+        noMoreData = oldNoMoreData;
+        
+        // If the timer dies (and stays dead after the write),  we need to let WebKit know that we're done processing the data.
+        allDataProcessed();
+    }
+}
+
+void HTMLTokenizer::allDataProcessed()
+{
+    if (noMoreData && !loadingExtScript && !m_executingScript && !onHold && !timerId) {
+        if (!parser || !parser->doc() || !parser->doc()->part())
+            return;
+        KHTMLPart* part = parser->doc()->part();
+        end();
+        part->tokenizerProcessedData();
+    }
+}
+
 void HTMLTokenizer::end()
 {
+    assert(timerId == 0);
+    if (timerId) {
+        // Clean up anyway.
+        killTimer(timerId);
+        timerId = 0;
+    }
+
     if ( buffer == 0 ) {
         parser->finished();
         emit finishedParsing();
@@ -1809,7 +1902,7 @@ void HTMLTokenizer::finish()
     // this indicates we will not receive any more data... but if we are waiting on
     // an external script to load, we can't finish parsing until that is done
     noMoreData = true;
-    if (!loadingExtScript && !m_executingScript && !onHold)
+    if (!loadingExtScript && !m_executingScript && !onHold && !timerId)
         end(); // this actually causes us to be deleted
 }
 
@@ -1898,6 +1991,11 @@ void HTMLTokenizer::enlargeScriptBuffer(int len)
 
 void HTMLTokenizer::notifyFinished(CachedObject */*finishedObj*/)
 {
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+    if (!parser->doc()->ownerElement())
+        printf("script loaded at %d\n", parser->doc()->elapsedTime());
+#endif
+
     assert(!cachedScript.isEmpty());
     bool finished = false;
     while (!finished && cachedScript.head()->isLoaded()) {
@@ -1921,7 +2019,13 @@ void HTMLTokenizer::notifyFinished(CachedObject */*finishedObj*/)
         // The state of cachedScript.isEmpty() can change inside the scriptExecution()
         // call above, so test afterwards.
         finished = cachedScript.isEmpty();
-        if (finished) loadingExtScript = false;
+        if (finished) {
+            loadingExtScript = false;
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+            if (!parser->doc()->ownerElement())
+                printf("external script finished execution at %d\n", parser->doc()->elapsedTime());
+#endif
+        }
 
         // 'script' is true when we are called synchronously from
         // parseScript(). In that case parseScript() will take care
index 52b2aff62af0dfde763089563f0cd268890e6d31..e0ca963cbeeccad6bfb60795933767722b1d1cdf 100644 (file)
@@ -131,6 +131,8 @@ public:
     virtual void write(const TokenizerString &str, bool appendData);
     virtual void finish();
     virtual void setOnHold(bool _onHold);
+    virtual void stopped();
+    virtual bool processingData() const;
 
 protected:
     void begin();
@@ -170,6 +172,10 @@ protected:
     void enlargeBuffer(int len);
     void enlargeScriptBuffer(int len);
 
+    bool continueProcessing(int& processedCount, const QTime& startTime, const KWQUIEventTime& eventTime);
+    void timerEvent(QTimerEvent*);
+    void allDataProcessed();
+
     // from CachedObjectClient
     void notifyFinished(CachedObject *finishedObj);
 
@@ -344,6 +350,9 @@ protected:
     int scriptStartLineno;
     int tagStartLineno;
 
+    // The timer for continued processing.
+    int timerId;
+
     bool includesCommentsInDOM;
 
 // This buffer can hold arbitrarily long user-defined attribute names, such as in EMBED tags.
index 3a41d31b67485d11c301dc70ebe673d4fd5d1f55..a3afc5382a67e29ebabcc2662580f8adf068b662 100644 (file)
@@ -1431,7 +1431,7 @@ void KHTMLPart::slotFinished( KIO::Job * job )
   d->m_job = 0L;
 
   if (d->m_doc->parsing())
-    end(); //will emit completed()
+      end(); //will emit completed()
 }
 
 #if APPLE_CHANGES
@@ -1656,7 +1656,7 @@ void KHTMLPart::write( const QString &str )
 void KHTMLPart::end()
 {
     // make sure nothing's left in there...
-    if(d->m_decoder)
+    if (d->m_decoder)
         write(d->m_decoder->flush());
     if (d->m_doc)
        d->m_doc->finishParsing();
@@ -1668,6 +1668,22 @@ void KHTMLPart::end()
         checkCompleted();
 }
 
+void KHTMLPart::stop()
+{
+    // make sure nothing's left in there...
+    Tokenizer* t = d->m_doc ? d->m_doc->tokenizer() : 0;
+    if (t)
+        t->stopped();
+    if (d->m_doc)
+       d->m_doc->finishParsing();
+    else
+        // WebKit partially uses WebCore when loading non-HTML docs.  In these cases doc==nil, but
+        // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to
+        // become true.  An example is when a subframe is a pure text doc, and that subframe is the
+        // last one to complete.
+        checkCompleted();
+}
+
 #if !APPLE_CHANGES
 
 void KHTMLPart::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
index a0f2363529b833bc8cdabcc1ebd4e6ff2e7b8a5e..81b24a9fe1cf0f256db26bd86eb80711b0e26104 100644 (file)
@@ -462,6 +462,11 @@ public:
    */
   virtual void end();
 
+  /**
+   * Similar to end, but called to abort a load rather than cleanly end.
+   */
+  void stop();
+
   /*
    * Prints the current HTML page layed out for the printer.
    *
@@ -757,6 +762,8 @@ public:
   virtual void restoreState( QDataStream &stream );
 #endif
 
+  virtual void tokenizerProcessedData() {};
+
   /**
    * Returns the @p Node currently under the mouse
    */
index 62866a13f61d79d4b823bc16b34731f4a397ff43..cf50b374718d2c7dad12822730efd3edae369e21 100644 (file)
@@ -69,7 +69,7 @@
 
 #define PAINT_BUFFER_HEIGHT 128
 
-//#define INSTRUMENT_LAYOUT_SCHEDULING 1
+// #define INSTRUMENT_LAYOUT_SCHEDULING 1
 
 using namespace DOM;
 using namespace khtml;
index 1f4c5b87c6d3984351743952f93d00eb18a8e81a..7a6ff6049287696a84202b99c2781c3d46cf7c09 100644 (file)
@@ -98,7 +98,7 @@ using XBL::XBLBindingManager;
 using namespace DOM;
 using namespace khtml;
 
-//#define INSTRUMENT_LAYOUT_SCHEDULING 1
+// #define INSTRUMENT_LAYOUT_SCHEDULING 1
 
 DOMImplementationImpl *DOMImplementationImpl::m_instance = 0;
 
index 1493a1845394e8a486f82c78f83122e213c8a850..b95beec5ac95ff71c012c08c3aedd6a3512e6bb8 100644 (file)
@@ -58,6 +58,9 @@ public:
     virtual void setOnHold(bool onHold) = 0;
     virtual bool isWaitingForScripts() = 0;
 
+    virtual void stopped() {};
+    virtual bool processingData() const { return false; }
+
 #ifdef KHTML_XSLT
     virtual void setTransformSource(DOM::DocumentImpl* doc) {};
 #endif
index 288ac2a9e070cd11ab19fc29da2b81bc1dc33fb5..3c4f8d058503869ba1b9741861716fea02d137a8 100644 (file)
@@ -88,4 +88,10 @@ private:
 #endif
 };
 
+class KWQUIEventTime {
+public:
+    bool uiEventPending() const;
+};
+
+
 #endif
index f9bcfb929efe7b172f07296e0e1e77185b650dbe..7e44b7751881362e9a8f0dc30def10b0edd53818 100644 (file)
@@ -26,6 +26,7 @@
 #import <Foundation/Foundation.h>
 #import "KWQDateTime.h"
 #import <time.h>
+#import "WebCoreGraphicsBridge.h"
 
 static CFTimeZoneRef systemTimeZone()
 {
@@ -84,6 +85,14 @@ int QDateTime::secsTo(const QDateTime &b) const
     return (int)(b.dateInSeconds - dateInSeconds);
 }
 
+bool KWQUIEventTime::uiEventPending() const
+{
+    unsigned int mask = NSAnyEventMask & 
+      ~(NSFlagsChangedMask | NSAppKitDefinedMask | NSSystemDefinedMask | NSApplicationDefinedMask | NSPeriodicMask | NSCursorUpdateMask);
+    return [[NSApplication sharedApplication] nextEventMatchingMask:mask untilDate:[NSDate distantPast] 
+                                              inMode:NSEventTrackingRunLoopMode dequeue:NO] != nil;
+}
+
 #ifdef _KWQ_IOSTREAM_
 
 std::ostream &operator<<(std::ostream &o, const QDate &date)
index 19f88543f3471ed0908ff13661d276949c826f49..fd5d48d3371bdd1e054b10855da5108ddd49d87d 100644 (file)
@@ -296,6 +296,8 @@ public:
 
     KWQWindowWidget *topLevelWidget();
     
+    void tokenizerProcessedData();
+
     QString overrideMediaType() const;
     
     void setMediaType(const QString &);
index 4e1c491287aa74b7d1c37b6d52b040da984f5a82..74ffdb6f70a2b9820725e45e0a7de46a0fb771ff 100644 (file)
@@ -3588,6 +3588,12 @@ KWQWindowWidget *KWQKHTMLPart::topLevelWidget()
     return _windowWidget;
 }
 
+void KWQKHTMLPart::tokenizerProcessedData()
+{
+    checkCompleted();
+    [_bridge tokenizerProcessedData];
+}
+
 int KWQKHTMLPart::selectionStartOffset() const
 {
     return d->m_selection.start().offset();
index e7d3e9c70e94c022db53af219c0783804f403817..b57b2193cede446e9d4cf874b1e4708925540294 100644 (file)
@@ -179,6 +179,7 @@ typedef enum {
 - (BOOL)saveDocumentToPageCache;
 
 - (void)end;
+- (void)stop;
 
 - (NSURL *)URL;
 - (NSString *)referrer;
@@ -304,6 +305,7 @@ typedef enum {
 - (BOOL)shouldCreateRenderers;
 
 - (int)numPendingOrLoadingRequests;
+- (BOOL)doneProcessingData;
 
 - (void)setDrawsBackground:(BOOL)drawsBackround;
 
@@ -461,6 +463,8 @@ typedef enum {
 
 - (void)setNeedsReapplyStyles;
 
+- (void)tokenizerProcessedData;
+
 // OK to be an NSString rather than an NSURL.
 // This URL is only used for coloring visited links.
 - (NSString *)requestedURLString;
index 074512aa8d8942e22c462b672430e059ce752bf9..a89cdb38f2b66280820b373ebc393cf865c03dd4 100644 (file)
@@ -52,6 +52,7 @@
 #import "render_style.h"
 #import "selection.h"
 #import "visible_position.h"
+#import "xml_tokenizer.h"
 
 #import <JavaScriptCore/npruntime.h>
 #import <JavaScriptCore/jni_jsobject.h>
@@ -116,6 +117,7 @@ using khtml::RenderStyle;
 using khtml::RenderWidget;
 using khtml::ReplaceSelectionCommand;
 using khtml::Selection;
+using khtml::Tokenizer;
 using khtml::TypingCommand;
 using khtml::UPSTREAM;
 using khtml::VisiblePosition;
@@ -445,6 +447,11 @@ static bool initializedKJS = FALSE;
     _part->end();
 }
 
+- (void)stop
+{
+    _part->stop();
+}
+
 - (void)createKHTMLViewWithNSView:(NSView *)view marginWidth:(int)mw marginHeight:(int)mh
 {
     // If we own the view, delete the old one - otherwise the render _part will take care of deleting the view.
@@ -1329,6 +1336,17 @@ static HTMLFormElementImpl *formElementFromDOMElement(DOMElement *element)
     return 0;
 }
 
+- (BOOL)doneProcessingData
+{
+    DocumentImpl *doc = _part->xmlDocImpl();
+    if (doc) {
+        Tokenizer* tok = doc->tokenizer();
+        if (tok)
+            return !tok->processingData();
+    }
+    return YES;
+}
+
 - (NSColor *)bodyBackgroundColor
 {
     return _part->bodyBackgroundColor();