Commit 835dd901 authored by tonyg@chromium.org's avatar tonyg@chromium.org

2011-02-18 Tony Gentilcore <tonyg@chromium.org>

        Reviewed by Eric Seidel.

        Let the parser yield for layout before running scripts
        https://bugs.webkit.org/show_bug.cgi?id=54355

        Prior to this patch, the parser would yield to perform a layout/paint before running a
        script only if the script or a stylesheet blocking the script is not loaded yet. Since we
        don't preload scan into the body while parsing the head, typically we'll block on a script
        early in the body that causes us to yield to do the first paint within a reasonable time.

        However, I'm planning to change the PreloadScanner to scan into the body from the head.
        That significantly improves overall load time, but would hurt first paint time because
        fewer scripts would be blocked during parsing and thus wouldn't yield.

        This change causes us to yield before running scripts if we haven't painted yet (regardless
        of whether or not the script is loaded). In addition to allowing the above mentioned
        PreloadScanner change to be implemented without regressing first paint time, this also
        improves first paint time by itself.

        I tested Alexa's top 45 websites using Web Page Replay to control the content and simulate
        bandwidth. This patch improved average first paint time by 1% over an unlimited connection,
        6% over a 1Mbps connection and 11% over a 5Mbps connection. There was no statistically
        signifcant change in page load time.

        Within the pages tested, 33 had no statistically significant change in time to first paint,
        12 improved, and none regressed. Of the improved, some of the standouts from the 1Mbps set
        are: 20% on youtube, 37% on wiki, 27% on ebay, 13% on cnn, 16% on espn, 74% on sohu.

        * html/parser/HTMLDocumentParser.cpp:
        (WebCore::HTMLDocumentParser::canTakeNextToken): This is the new yield point.
        (WebCore::HTMLDocumentParser::pumpTokenizer): Remove ASSERT that we are not paused. isPaused
        means that we are waiting for a script. Bug 54574 changed pumpTokenizer() so that it does
        the right thing whether we are just before a token or waiting for a script. Now that we may
        yield before a token or before a script, this may be called while paused.
        * html/parser/HTMLParserScheduler.cpp:
        (WebCore::isLayoutTimerActive): Added a FIXME because r52919 changed minimumLayoutDelay()
        to return m_extraLayoutDelay instead of 0 as a minimum. So checking !minimumLayoutDelay()
        no longer works. The fix is to change it to check minimumLayoutDelay() ==
        m_extraLayoutDelay. But this is all the more reason to move this method onto Document. I'll
        do this in a follow up.
        (WebCore::HTMLParserScheduler::checkForYieldBeforeScript): Added.
        * page/FrameView.h:
        (WebCore::FrameView::hasEverPainted): Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@79104 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 2f3a352e
2011-02-18 Tony Gentilcore <tonyg@chromium.org>
Reviewed by Eric Seidel.
Let the parser yield for layout before running scripts
https://bugs.webkit.org/show_bug.cgi?id=54355
Prior to this patch, the parser would yield to perform a layout/paint before running a
script only if the script or a stylesheet blocking the script is not loaded yet. Since we
don't preload scan into the body while parsing the head, typically we'll block on a script
early in the body that causes us to yield to do the first paint within a reasonable time.
However, I'm planning to change the PreloadScanner to scan into the body from the head.
That significantly improves overall load time, but would hurt first paint time because
fewer scripts would be blocked during parsing and thus wouldn't yield.
This change causes us to yield before running scripts if we haven't painted yet (regardless
of whether or not the script is loaded). In addition to allowing the above mentioned
PreloadScanner change to be implemented without regressing first paint time, this also
improves first paint time by itself.
I tested Alexa's top 45 websites using Web Page Replay to control the content and simulate
bandwidth. This patch improved average first paint time by 1% over an unlimited connection,
6% over a 1Mbps connection and 11% over a 5Mbps connection. There was no statistically
signifcant change in page load time.
Within the pages tested, 33 had no statistically significant change in time to first paint,
12 improved, and none regressed. Of the improved, some of the standouts from the 1Mbps set
are: 20% on youtube, 37% on wiki, 27% on ebay, 13% on cnn, 16% on espn, 74% on sohu.
* html/parser/HTMLDocumentParser.cpp:
(WebCore::HTMLDocumentParser::canTakeNextToken): This is the new yield point.
(WebCore::HTMLDocumentParser::pumpTokenizer): Remove ASSERT that we are not paused. isPaused
means that we are waiting for a script. Bug 54574 changed pumpTokenizer() so that it does
the right thing whether we are just before a token or waiting for a script. Now that we may
yield before a token or before a script, this may be called while paused.
* html/parser/HTMLParserScheduler.cpp:
(WebCore::isLayoutTimerActive): Added a FIXME because r52919 changed minimumLayoutDelay()
to return m_extraLayoutDelay instead of 0 as a minimum. So checking !minimumLayoutDelay()
no longer works. The fix is to change it to check minimumLayoutDelay() ==
m_extraLayoutDelay. But this is all the more reason to move this method onto Document. I'll
do this in a follow up.
(WebCore::HTMLParserScheduler::checkForYieldBeforeScript): Added.
* page/FrameView.h:
(WebCore::FrameView::hasEverPainted): Added.
2011-02-18 Dawit Alemayehu <adawit@kde.org>
Reviewed by Andreas Kling.
......
......@@ -207,6 +207,13 @@ bool HTMLDocumentParser::canTakeNextToken(SynchronousMode mode, PumpSession& ses
// The parser will pause itself when waiting on a script to load or run.
if (m_treeBuilder->isPaused()) {
if (mode == AllowYield)
m_parserScheduler->checkForYieldBeforeScript(session);
// If we don't run the script, we cannot allow the next token to be taken.
if (session.needsYield)
return false;
// If we're paused waiting for a script, we try to execute scripts before continuing.
bool shouldContinueParsing = runScriptsForPausedTreeBuilder();
m_treeBuilder->setPaused(!shouldContinueParsing);
......@@ -233,7 +240,6 @@ bool HTMLDocumentParser::canTakeNextToken(SynchronousMode mode, PumpSession& ses
void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
{
ASSERT(!isStopped());
ASSERT(!m_treeBuilder->isPaused());
ASSERT(!isScheduledForResume());
// ASSERT that this object is both attached to the Document and protected.
ASSERT(refCount() >= 2);
......
......@@ -78,6 +78,7 @@ HTMLParserScheduler::~HTMLParserScheduler()
static bool isLayoutTimerActive(Document* doc)
{
ASSERT(doc);
// FIXME: This is broken on Android because minimumLayoutDelay is never 0.
return doc->view() && doc->view()->layoutPending() && !doc->minimumLayoutDelay();
}
......@@ -93,6 +94,16 @@ void HTMLParserScheduler::continueNextChunkTimerFired(Timer<HTMLParserScheduler>
m_parser->resumeParsingAfterYield();
}
void HTMLParserScheduler::checkForYieldBeforeScript(PumpSession& session)
{
// If we've never painted before and a layout is pending, yield prior to running
// scripts to give the page a chance to paint earlier.
Document* document = m_parser->document();
bool needsFirstPaint = document->view() && !document->view()->hasEverPainted();
if (needsFirstPaint && isLayoutTimerActive(document))
session.needsYield = true;
}
void HTMLParserScheduler::scheduleForResume()
{
m_continueNextChunkTimer.startOneShot(0);
......
......@@ -67,6 +67,7 @@ public:
}
++session.processedTokens;
}
void checkForYieldBeforeScript(PumpSession&);
void scheduleForResume();
bool isScheduledForResume() const { return m_isSuspendedWithActiveTimer || m_continueNextChunkTimer.isActive(); }
......
......@@ -207,6 +207,7 @@ public:
void setPaintBehavior(PaintBehavior);
PaintBehavior paintBehavior() const;
bool isPainting() const;
bool hasEverPainted() const { return m_lastPaintTime; }
void setNodeToDraw(Node*);
virtual void paintOverhangAreas(GraphicsContext*, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment