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
+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.
#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 [] = "<!--";
loadingExtScript = false;
onHold = false;
attrNamePresent = false;
+ timerId = 0;
includesCommentsInDOM = includesComments;
begin();
m_executingScript = 0;
loadingExtScript = false;
onHold = false;
+ timerId = 0;
includesCommentsInDOM = includesComments;
begin();
scriptCode = 0;
scriptCodeSize = scriptCodeMaxSize = scriptCodeResync = 0;
+ if (timerId) {
+ killTimer(timerId);
+ timerId = 0;
+ }
+ timerId = 0;
+
currToken.reset();
}
return;
}
- setSrc(str);
+ if (!src.isEmpty())
+ src.append(str);
+ else
+ setSrc(str);
#ifndef NDEBUG
inWrite = true;
// 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();
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();
// 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
}
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()) {
// 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
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();
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);
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.
d->m_job = 0L;
if (d->m_doc->parsing())
- end(); //will emit completed()
+ end(); //will emit completed()
}
#if APPLE_CHANGES
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();
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)
*/
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.
*
virtual void restoreState( QDataStream &stream );
#endif
+ virtual void tokenizerProcessedData() {};
+
/**
* Returns the @p Node currently under the mouse
*/
#define PAINT_BUFFER_HEIGHT 128
-//#define INSTRUMENT_LAYOUT_SCHEDULING 1
+// #define INSTRUMENT_LAYOUT_SCHEDULING 1
using namespace DOM;
using namespace khtml;
using namespace DOM;
using namespace khtml;
-//#define INSTRUMENT_LAYOUT_SCHEDULING 1
+// #define INSTRUMENT_LAYOUT_SCHEDULING 1
DOMImplementationImpl *DOMImplementationImpl::m_instance = 0;
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
#endif
};
+class KWQUIEventTime {
+public:
+ bool uiEventPending() const;
+};
+
+
#endif
#import <Foundation/Foundation.h>
#import "KWQDateTime.h"
#import <time.h>
+#import "WebCoreGraphicsBridge.h"
static CFTimeZoneRef systemTimeZone()
{
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)
KWQWindowWidget *topLevelWidget();
+ void tokenizerProcessedData();
+
QString overrideMediaType() const;
void setMediaType(const QString &);
return _windowWidget;
}
+void KWQKHTMLPart::tokenizerProcessedData()
+{
+ checkCompleted();
+ [_bridge tokenizerProcessedData];
+}
+
int KWQKHTMLPart::selectionStartOffset() const
{
return d->m_selection.start().offset();
- (BOOL)saveDocumentToPageCache;
- (void)end;
+- (void)stop;
- (NSURL *)URL;
- (NSString *)referrer;
- (BOOL)shouldCreateRenderers;
- (int)numPendingOrLoadingRequests;
+- (BOOL)doneProcessingData;
- (void)setDrawsBackground:(BOOL)drawsBackround;
- (void)setNeedsReapplyStyles;
+- (void)tokenizerProcessedData;
+
// OK to be an NSString rather than an NSURL.
// This URL is only used for coloring visited links.
- (NSString *)requestedURLString;
#import "render_style.h"
#import "selection.h"
#import "visible_position.h"
+#import "xml_tokenizer.h"
#import <JavaScriptCore/npruntime.h>
#import <JavaScriptCore/jni_jsobject.h>
using khtml::RenderWidget;
using khtml::ReplaceSelectionCommand;
using khtml::Selection;
+using khtml::Tokenizer;
using khtml::TypingCommand;
using khtml::UPSTREAM;
using khtml::VisiblePosition;
_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.
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();