and subsequently backed out).
Reviewed by kocienda
* khtml/html/html_baseimpl.cpp:
(HTMLBodyElementImpl::insertedIntoDocument):
* khtml/html/htmltokenizer.cpp:
(khtml::HTMLTokenizer::reset):
(khtml::HTMLTokenizer::scriptHandler):
(khtml::HTMLTokenizer::scriptExecution):
(khtml::HTMLTokenizer::write):
(khtml::HTMLTokenizer::continueProcessing):
(khtml::HTMLTokenizer::timerEvent):
(khtml::HTMLTokenizer::notifyFinished):
* khtml/html/htmltokenizer.h:
* khtml/khtmlview.cpp:
(KHTMLViewPrivate::KHTMLViewPrivate):
(KHTMLViewPrivate::reset):
(KHTMLView::clear):
(KHTMLView::layout):
(KHTMLView::timerEvent):
(KHTMLView::scheduleRelayout):
(KHTMLView::layoutPending):
(KHTMLView::haveDelayedLayoutScheduled):
(KHTMLView::unscheduleRelayout):
* khtml/khtmlview.h:
* khtml/xml/dom_docimpl.cpp:
(DocumentImpl::DocumentImpl):
(DocumentImpl::close):
(DocumentImpl::setParsing):
(DocumentImpl::shouldScheduleLayout):
(DocumentImpl::minimumLayoutDelay):
(DocumentImpl::write):
(DocumentImpl::finishParsing):
(DocumentImpl::stylesheetLoaded):
(DocumentImpl::updateStyleSelector):
* khtml/xml/dom_docimpl.h:
(DOM::DocumentImpl::parsing):
* kwq/KWQDateTime.mm:
(KWQUIEventTime::uiEventPending):
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@8031
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2004-11-17 David Hyatt <hyatt@apple.com>
+
+ Improve responsiveness by being willing to break out of the tokenizer. (This patch was landed already
+ and subsequently backed out).
+
+ Reviewed by kocienda
+
+ * khtml/html/html_baseimpl.cpp:
+ (HTMLBodyElementImpl::insertedIntoDocument):
+ * khtml/html/htmltokenizer.cpp:
+ (khtml::HTMLTokenizer::reset):
+ (khtml::HTMLTokenizer::scriptHandler):
+ (khtml::HTMLTokenizer::scriptExecution):
+ (khtml::HTMLTokenizer::write):
+ (khtml::HTMLTokenizer::continueProcessing):
+ (khtml::HTMLTokenizer::timerEvent):
+ (khtml::HTMLTokenizer::notifyFinished):
+ * khtml/html/htmltokenizer.h:
+ * khtml/khtmlview.cpp:
+ (KHTMLViewPrivate::KHTMLViewPrivate):
+ (KHTMLViewPrivate::reset):
+ (KHTMLView::clear):
+ (KHTMLView::layout):
+ (KHTMLView::timerEvent):
+ (KHTMLView::scheduleRelayout):
+ (KHTMLView::layoutPending):
+ (KHTMLView::haveDelayedLayoutScheduled):
+ (KHTMLView::unscheduleRelayout):
+ * khtml/khtmlview.h:
+ * khtml/xml/dom_docimpl.cpp:
+ (DocumentImpl::DocumentImpl):
+ (DocumentImpl::close):
+ (DocumentImpl::setParsing):
+ (DocumentImpl::shouldScheduleLayout):
+ (DocumentImpl::minimumLayoutDelay):
+ (DocumentImpl::write):
+ (DocumentImpl::finishParsing):
+ (DocumentImpl::stylesheetLoaded):
+ (DocumentImpl::updateStyleSelector):
+ * khtml/xml/dom_docimpl.h:
+ (DOM::DocumentImpl::parsing):
+ * kwq/KWQDateTime.mm:
+ (KWQUIEventTime::uiEventPending):
+
2004-11-17 David Harrison <harrison@apple.com>
Reviewed by Ken Kocienda.
s.sprintf( "%d", w->marginHeight() );
setAttribute(ATTR_MARGINHEIGHT, s);
}
+
+ w->scheduleRelayout();
}
bool HTMLBodyElementImpl::isURLAttribute(AttributeImpl *attr) const
// #define INSTRUMENT_LAYOUT_SCHEDULING 1
#define TOKENIZER_CHUNK_SIZE 4096
+
+// FIXME: We would like this constant to be 200ms. Yielding more aggressively results in increased
+// responsiveness and better incremental rendering. It slows down overall page-load on slower machines,
+// though, so for now we set a value of 500.
#define TOKENIZER_TIME_DELAY 500
namespace khtml {
timerId = 0;
}
timerId = 0;
+ allowYield = false;
currToken.reset();
}
if (!scriptSrc.isEmpty() && parser->doc()->part()) {
// forget what we just got; load from src url instead
if ( !parser->skipMode() ) {
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!parser->doc()->ownerElement())
+ printf("Requesting script at time %d\n", parser->doc()->elapsedTime());
+#endif
if ( (cs = parser->doc()->docLoader()->requestScript(scriptSrc, scriptSrcCharset) ))
cachedScript.enqueue(cs);
}
TokenizerString prependingSrc;
currentPrependingSrc = &prependingSrc;
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!parser->doc()->ownerElement())
+ printf("beginning script execution at %d\n", parser->doc()->elapsedTime());
+#endif
+
view->part()->executeScript(url,baseLine,Node(),str);
+ allowYield = true;
+
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!parser->doc()->ownerElement())
+ printf("ending script execution at %d\n", parser->doc()->elapsedTime());
+#endif
+
m_executingScript--;
script = oldscript;
kdDebug( 6036 ) << this << " Tokenizer::write(\"" << str << "\"," << appendData << ")" << endl;
#endif
- if ( !buffer )
+ if (!buffer)
return;
if ( ( m_executingScript && appendData ) || !cachedScript.isEmpty() ) {
else
setSrc(str);
+ // Once a timer is set, it has control of when the tokenizer continues.
+ if (timerId)
+ return;
+
#ifndef NDEBUG
inWrite = true;
#endif
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!parser->doc()->ownerElement())
+ printf("Beginning write at time %d\n", parser->doc()->elapsedTime());
+#endif
+
// if (Entity)
// parseEntity(src, dest);
}
}
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!parser->doc()->ownerElement())
+ printf("Ending write at time %d\n", parser->doc()->elapsedTime());
+#endif
+
#ifndef NDEBUG
inWrite = false;
#endif
bool HTMLTokenizer::continueProcessing(int& processedCount, const QTime& startTime, const KWQUIEventTime& eventTime)
{
- return true;
- /* Hurt PLT, so comment this out for now until we figure something out.
- // 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) {
+ bool allowedYield = allowYield;
+ allowYield = false;
+ if (!loadingExtScript && !m_executingScript && (processedCount > TOKENIZER_CHUNK_SIZE || allowedYield)) {
processedCount = 0;
- if (eventTime.uiEventPending() || startTime.elapsed() > TOKENIZER_TIME_DELAY || !parser->doc()->haveStylesheetsLoaded() ||
- (parser->doc()->ownerElement() && parser->doc()->ownerElement()->getDocument()->parsing())) {
+ if (startTime.elapsed() > TOKENIZER_TIME_DELAY) {
+ /* FIXME: We'd like to yield aggressively to give stylesheets the opportunity to
+ load, but this hurts overall performance on slower machines. For now turn this
+ off.
+ || (!parser->doc()->haveStylesheetsLoaded() &&
+ (parser->doc()->documentElement()->id() != ID_HTML || parser->doc()->body()))) {*/
// Schedule the timer to keep processing as soon as possible.
if (!timerId)
timerId = startTimer(0);
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (eventTime.uiEventPending())
+ printf("Deferring processing of data because of UI event.\n");
+ else if (startTime.elapsed() > TOKENIZER_TIME_DELAY)
+ printf("Deferring processing of data because 200ms elapsed away from event loop.\n");
+#endif
return false;
}
}
processedCount++;
- return true;*/
+ return true;
}
void HTMLTokenizer::timerEvent(QTimerEvent* e)
killTimer(timerId);
timerId = 0;
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!parser->doc()->ownerElement())
+ printf("Beginning timer write at time %d\n", parser->doc()->elapsedTime());
+#endif
+
+ if (parser->doc()->view() && parser->doc()->view()->layoutPending() && !parser->doc()->minimumLayoutDelay()) {
+ // Restart the timer and let layout win. This is basically a way of ensuring that the layout
+ // timer has higher priority than our timer.
+ timerId = startTimer(0);
+ return;
+ }
+
// Invoke write() as though more data came in.
bool oldNoMoreData = noMoreData;
noMoreData = false; // This prevents write() from deleting the tokenizer.
QString cachedScriptUrl( cs->url().string() );
cs->deref(this);
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!parser->doc()->ownerElement())
+ printf("external script beginning execution at %d\n", parser->doc()->elapsedTime());
+#endif
+
scriptExecution( scriptSource.string(), cachedScriptUrl );
// The state of cachedScript.isEmpty() can change inside the scriptExecution()
// The timer for continued processing.
int timerId;
+ bool allowYield;
bool includesCommentsInDOM;
formCompletions=0;
#endif
layoutTimerId = 0;
- allDataReceivedWhenTimerSet = false;
+ delayedLayout = false;
mousePressed = false;
#ifndef QT_NO_TOOLTIP
tooltip = 0;
isDoubleClick = false;
scrollingSelf = false;
layoutTimerId = 0;
- allDataReceivedWhenTimerSet = false;
+ delayedLayout = false;
mousePressed = false;
doFullRepaint = true;
layoutSchedulingEnabled = true;
int prevMouseX, prevMouseY;
bool scrollingSelf;
int layoutTimerId;
- bool allDataReceivedWhenTimerSet;
+ bool delayedLayout;
bool layoutSchedulingEnabled;
bool layoutSuppressed;
m_part->clearSelection();
d->reset();
+
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (d->layoutTimerId && m_part->xmlDocImpl() && !m_part->xmlDocImpl()->ownerElement())
+ printf("Killing the layout timer from a clear at %d\n", m_part->xmlDocImpl()->elapsedTime());
+#endif
+
killTimers();
emit cleared();
d->layoutSchedulingEnabled=false;
killTimer(d->layoutTimerId);
d->layoutTimerId = 0;
- d->allDataReceivedWhenTimerSet = false;
+ d->delayedLayout = false;
if (!m_part) {
// FIXME: Do we need to set _width here?
void KHTMLView::timerEvent ( QTimerEvent *e )
{
- if (e->timerId()==d->layoutTimerId)
+ if (e->timerId()==d->layoutTimerId) {
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (m_part->xmlDocImpl() && !m_part->xmlDocImpl()->ownerElement())
+ printf("Layout timer fired at %d\n", m_part->xmlDocImpl()->elapsedTime());
+#endif
layout();
+ }
}
void KHTMLView::scheduleRelayout()
if (!d->layoutSchedulingEnabled)
return;
- if (d->layoutTimerId || (m_part->xmlDocImpl() && !m_part->xmlDocImpl()->shouldScheduleLayout()))
+ if (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->shouldScheduleLayout())
return;
- d->allDataReceivedWhenTimerSet = m_part->xmlDocImpl() && !m_part->xmlDocImpl()->allDataReceived();
+ int delay = m_part->xmlDocImpl()->minimumLayoutDelay();
+ if (d->layoutTimerId && d->delayedLayout && !delay)
+ unscheduleRelayout();
+ if (d->layoutTimerId)
+ return;
+
+ d->delayedLayout = delay != 0;
#ifdef INSTRUMENT_LAYOUT_SCHEDULING
if (!m_part->xmlDocImpl()->ownerElement())
- printf("Scheduling layout for %d\n", m_part->xmlDocImpl()->minimumLayoutDelay());
+ printf("Scheduling layout for %d\n", delay);
#endif
- d->layoutTimerId = startTimer(m_part->xmlDocImpl() ? m_part->xmlDocImpl()->minimumLayoutDelay() : 0);
+ d->layoutTimerId = startTimer(delay);
+}
+
+bool KHTMLView::layoutPending()
+{
+ return d->layoutTimerId;
}
bool KHTMLView::haveDelayedLayoutScheduled()
{
- return d->layoutTimerId && d->allDataReceivedWhenTimerSet;
+ return d->layoutTimerId && d->delayedLayout;
}
void KHTMLView::unscheduleRelayout()
if (!d->layoutTimerId)
return;
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (m_part->xmlDocImpl() && !m_part->xmlDocImpl()->ownerElement())
+ printf("Layout timer unscheduled at %d\n", m_part->xmlDocImpl()->elapsedTime());
+#endif
+
killTimer(d->layoutTimerId);
d->layoutTimerId = 0;
- d->allDataReceivedWhenTimerSet = false;
+ d->delayedLayout = false;
}
bool KHTMLView::isTransparent() const
bool isTransparent() const;
void setTransparent(bool isTransparent);
+ void scheduleRelayout();
+ void unscheduleRelayout();
+ bool haveDelayedLayoutScheduled();
+ bool layoutPending();
+
#if APPLE_CHANGES
QWidget *topLevelWidget() const;
QPoint mapToGlobal(const QPoint &) const;
void resetCursor();
void invalidateClick();
- void scheduleRelayout();
- void unscheduleRelayout();
- bool haveDelayedLayoutScheduled();
-
/**
* Paints the HTML document to a QPainter.
* The document will be scaled to match the width of
// #define INSTRUMENT_LAYOUT_SCHEDULING 1
+// This amount of time must have elapsed before we will even consider scheduling a layout without a delay.
+// FIXME: For faster machines this value can really be lowered to 200. 250 is adequate, but a little high
+// for dual G5s. :)
+const int cLayoutScheduleThreshold = 250;
+
DOMImplementationImpl *DOMImplementationImpl::m_instance = 0;
DOMImplementationImpl::DOMImplementationImpl()
visuallyOrdered = false;
m_loadingSheet = false;
m_bParsing = false;
- m_bAllDataReceived = false;
m_docChanged = false;
m_sheet = 0;
m_elemSheet = 0;
m_tokenizer = 0;
dispatchImageLoadEventsNow();
body()->dispatchWindowEvent(EventImpl::LOAD_EVENT, false, false);
+
#ifdef INSTRUMENT_LAYOUT_SCHEDULING
if (!ownerElement())
printf("onload fired at %d\n", elapsedTime());
bool isLocationChangePending = part() && part()->isScheduledLocationChangePending();
- if (doload && !wasLocationChangePending && isLocationChangePending && m_startTime.elapsed() < cLayoutTimerDelay) {
+ if (doload && !wasLocationChangePending && isLocationChangePending && m_startTime.elapsed() < cLayoutScheduleThreshold) {
// Just bail out. During the onload we were shifted to another page.
- // i-Bench does this. When this happens don't bother painting or laying out.
+ // The old i-Bench suite does this. When this happens don't bother painting or laying out.
delete m_tokenizer;
m_tokenizer = 0;
view()->unscheduleRelayout();
void DocumentImpl::setParsing(bool b)
{
m_bParsing = b;
+ if (!m_bParsing && view())
+ view()->scheduleRelayout();
+
#ifdef INSTRUMENT_LAYOUT_SCHEDULING
if (!ownerElement() && !m_bParsing)
- printf("Parsing turned off at %d\n", elapsedTime());
+ printf("Parsing finished at %d\n", elapsedTime());
#endif
}
bool DocumentImpl::shouldScheduleLayout()
{
- return renderer() && haveStylesheetsLoaded();
+ // We can update layout if:
+ // (a) we actually need a layout
+ // (b) our stylesheets are all loaded
+ // (c) we have a <body>
+ return (renderer() && renderer()->needsLayout() && haveStylesheetsLoaded() &&
+ documentElement() && documentElement()->renderer() &&
+ (documentElement()->id() != ID_HTML || body()));
}
int DocumentImpl::minimumLayoutDelay()
{
- if (allDataReceived() && m_overMinimumLayoutThreshold)
+ if (m_overMinimumLayoutThreshold)
return 0;
int elapsed = m_startTime.elapsed();
m_overMinimumLayoutThreshold = elapsed > cLayoutScheduleThreshold;
- if (!allDataReceived()) // Always want the nearest multiple of the timer delay.
- return cLayoutTimerDelay - elapsed % cLayoutTimerDelay;
// We'll want to schedule the timer to fire at the minimum layout threshold.
return kMax(0, cLayoutScheduleThreshold - elapsed);
}
void DocumentImpl::write( const QString &text )
{
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!ownerElement())
+ printf("Beginning a document.write at %d\n", elapsedTime());
+#endif
+
if (!m_tokenizer) {
open();
write(QString::fromLatin1("<html>"));
if (m_view && m_view->part()->jScript())
m_view->part()->jScript()->appendSourceFile(m_url,text);
+
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!ownerElement())
+ printf("Ending a document.write at %d\n", elapsedTime());
+#endif
}
void DocumentImpl::writeln( const DOMString &text )
void DocumentImpl::finishParsing()
{
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!ownerElement())
+ printf("Received all data at %d\n", elapsedTime());
+#endif
+
// Let the tokenizer go through as much data as it can. There will be three possible outcomes after
// finish() is called:
// (1) All remaining data is parsed, document isn't loaded yet
// (3) Data is still remaining to be parsed.
if (m_tokenizer)
m_tokenizer->finish();
-
- // Don't say we've really received all the data until we've given the tokenizer
- // a chance to try to eat as much of the data as it can.
- m_bAllDataReceived = true;
-
- if (m_tokenizer) {
- // Update layout *now* if possible. If we don't have a tokenizer any more, we already updated
- // layout because of the onload firing.
-
-#ifdef INSTRUMENT_LAYOUT_SCHEDULING
- if (!ownerElement())
- printf("Received all data and parsed as much as possible at time: %d with delay until next layout of %d\n", elapsedTime(), minimumLayoutDelay());
-#endif
-
- // We can update layout if:
- // (a) we're an XML document or we're an HTML document and our body has been parsed (ensuring we'll have
- // the background ready to paint)
- // (b) our stylesheets are all loaded
- // (c) we're over the minimum layout threshold
- // (d) we are an iframe in a document that has flowed us already or we aren't an iframe at all
- // (e) we actually need a layout
- if ((!isHTMLDocument() || body()) && haveStylesheetsLoaded() && !minimumLayoutDelay() &&
- (!ownerElement() || (ownerElement()->renderer() && !ownerElement()->renderer()->needsLayout())) &&
- renderer() && renderer()->needsLayout())
- updateLayout();
- }
}
void DocumentImpl::clear()
assert(m_pendingStylesheets > 0);
m_pendingStylesheets--;
+
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!ownerElement())
+ printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets);
+#endif
+
updateStyleSelector();
}
if (!haveStylesheetsLoaded())
return;
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!ownerElement())
+ printf("Beginning update of style selector at time %d.\n", elapsedTime());
+#endif
+
recalcStyleSelector();
recalcStyle(Force);
#if 0
#ifdef INSTRUMENT_LAYOUT_SCHEDULING
if (!ownerElement())
- printf("Dirtying renderer from stylesheet load at time %d\n", elapsedTime());
+ printf("Finished update of style selector at time %d\n", elapsedTime());
#endif
if (renderer()) {
renderer()->setNeedsLayoutAndMinMaxRecalc();
- if (allDataReceived() && view()->haveDelayedLayoutScheduled()) {
- view()->unscheduleRelayout();
+ if (view())
view()->scheduleRelayout();
- }
}
}
class KWQAccObjectCache;
#endif
-// This amount of time must have elapsed before we will even consider scheduling a layout without a delay.
-const int cLayoutScheduleThreshold = 250;
-
-// This is the amount of time we will delay doing a layout whenever we are either still parsing, or
-// when the minimum amount of time according to the |cLayoutScheduleThreshold| has not yet elapsed.
-const int cLayoutTimerDelay = 1000;
-
namespace khtml {
class CSSStyleSelector;
class DocLoader;
void setParsing(bool b);
bool parsing() const { return m_bParsing; }
- bool allDataReceived() const { return m_bAllDataReceived; }
int minimumLayoutDelay();
bool shouldScheduleLayout();
int elapsedTime() const;
bool KWQUIEventTime::uiEventPending() const
{
+ return false;
+/*
unsigned int mask = NSAnyEventMask &
~(NSFlagsChangedMask | NSAppKitDefinedMask | NSSystemDefinedMask | NSApplicationDefinedMask | NSPeriodicMask | NSCursorUpdateMask);
- return [[NSApplication sharedApplication] nextEventMatchingMask:mask untilDate:[NSDate distantPast]
+ return [[NSApplication sharedApplication] nextEventMatchingMask:mask untilDate:nil
inMode:NSEventTrackingRunLoopMode dequeue:NO] != nil;
+*/
}
#ifdef _KWQ_IOSTREAM_