2 * Copyright (C) 2010 Google, Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "core/html/parser/HTMLParserScheduler.h"
29 #include "core/dom/Document.h"
30 #include "core/html/parser/HTMLDocumentParser.h"
31 #include "core/frame/FrameView.h"
32 #include "public/platform/Platform.h"
33 #include "public/platform/WebScheduler.h"
34 #include "public/platform/WebThread.h"
35 #include "wtf/CurrentTime.h"
39 ActiveParserSession::ActiveParserSession(unsigned& nestingLevel
, Document
* document
)
40 : NestingLevelIncrementer(nestingLevel
)
41 , m_document(document
)
45 m_document
->incrementActiveParserCount();
48 ActiveParserSession::~ActiveParserSession()
52 m_document
->decrementActiveParserCount();
55 PumpSession::PumpSession(unsigned& nestingLevel
, Document
* document
)
56 : ActiveParserSession(nestingLevel
, document
)
60 PumpSession::~PumpSession()
64 SpeculationsPumpSession::SpeculationsPumpSession(unsigned& nestingLevel
, Document
* document
)
65 : ActiveParserSession(nestingLevel
, document
)
66 , m_startTime(currentTime())
67 , m_processedElementTokens(0)
71 SpeculationsPumpSession::~SpeculationsPumpSession()
75 inline double SpeculationsPumpSession::elapsedTime() const
77 return currentTime() - m_startTime
;
80 void SpeculationsPumpSession::addedElementTokens(size_t count
)
82 m_processedElementTokens
+= count
;
85 HTMLParserScheduler::HTMLParserScheduler(HTMLDocumentParser
* parser
)
87 , m_loadingTaskRunner(Platform::current()->currentThread()->scheduler()->loadingTaskRunner())
88 , m_cancellableContinueParse(CancellableTaskFactory::create(this, &HTMLParserScheduler::continueParsing
))
89 , m_isSuspendedWithActiveTimer(false)
93 HTMLParserScheduler::~HTMLParserScheduler()
97 void HTMLParserScheduler::scheduleForResume()
99 ASSERT(!m_isSuspendedWithActiveTimer
);
100 m_loadingTaskRunner
->postTask(FROM_HERE
, m_cancellableContinueParse
->cancelAndCreate());
103 void HTMLParserScheduler::suspend()
105 ASSERT(!m_isSuspendedWithActiveTimer
);
106 if (!m_cancellableContinueParse
->isPending())
108 m_isSuspendedWithActiveTimer
= true;
109 m_cancellableContinueParse
->cancel();
112 void HTMLParserScheduler::resume()
114 ASSERT(!m_cancellableContinueParse
->isPending());
115 if (!m_isSuspendedWithActiveTimer
)
117 m_isSuspendedWithActiveTimer
= false;
119 m_loadingTaskRunner
->postTask(FROM_HERE
, m_cancellableContinueParse
->cancelAndCreate());
122 void HTMLParserScheduler::detach()
124 m_cancellableContinueParse
->cancel();
125 m_isSuspendedWithActiveTimer
= false;
128 inline bool HTMLParserScheduler::shouldYield(const SpeculationsPumpSession
& session
, bool startingScript
) const
130 if (Platform::current()->currentThread()->scheduler()->shouldYieldForHighPriorityWork())
133 const double parserTimeLimit
= 0.5;
134 if (session
.elapsedTime() > parserTimeLimit
)
137 // Yield if a lot of DOM work has been done in this session and a script tag is
138 // about to be parsed. This significantly improves render performance for documents
139 // that place their scripts at the bottom of the page. Yielding too often
140 // significantly slows down the parsing so a balance needs to be struck to
141 // only yield when enough changes have happened to make it worthwhile.
142 // Emperical testing shows that anything > ~40 and < ~200 gives all of the benefit
143 // without impacting parser performance, only adding a few yields per page but at
144 // just the right times.
145 const size_t sufficientWork
= 50;
146 if (startingScript
&& session
.processedElementTokens() > sufficientWork
)
152 bool HTMLParserScheduler::yieldIfNeeded(const SpeculationsPumpSession
& session
, bool startingScript
)
154 if (shouldYield(session
, startingScript
)) {
162 void HTMLParserScheduler::forceResumeAfterYield()
164 ASSERT(!m_cancellableContinueParse
->isPending());
165 m_isSuspendedWithActiveTimer
= true;
168 void HTMLParserScheduler::continueParsing()
170 m_parser
->resumeParsingAfterYield();