CTTE Timer and DeferrableOneShotTimer
[WebKit-https.git] / Source / WebCore / html / parser / HTMLParserScheduler.cpp
1 /*
2  * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3  * Copyright (C) 2013 Apple, Inc. All Rights Reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "HTMLParserScheduler.h"
29
30 #include "Document.h"
31 #include "FrameView.h"
32 #include "HTMLDocumentParser.h"
33 #include "Page.h"
34
35 // defaultParserChunkSize is used to define how many tokens the parser will
36 // process before checking against parserTimeLimit and possibly yielding.
37 // This is a performance optimization to prevent checking after every token.
38 static const int defaultParserChunkSize = 4096;
39
40 // defaultParserTimeLimit is the seconds the parser will run in one write() call
41 // before yielding. Inline <script> execution can cause it to exceed the limit.
42 // FIXME: We would like this value to be 0.2.
43 static const double defaultParserTimeLimit = 0.500;
44
45 namespace WebCore {
46
47 static double parserTimeLimit(Page* page)
48 {
49     // We're using the poorly named customHTMLTokenizerTimeDelay setting.
50     if (page && page->hasCustomHTMLTokenizerTimeDelay())
51         return page->customHTMLTokenizerTimeDelay();
52     return defaultParserTimeLimit;
53 }
54
55 static int parserChunkSize(Page* page)
56 {
57     // FIXME: We may need to divide the value from customHTMLTokenizerChunkSize
58     // by some constant to translate from the "character" based behavior of the
59     // old LegacyHTMLDocumentParser to the token-based behavior of this parser.
60     if (page && page->hasCustomHTMLTokenizerChunkSize())
61         return page->customHTMLTokenizerChunkSize();
62     return defaultParserChunkSize;
63 }
64
65 ActiveParserSession::ActiveParserSession(Document* document)
66     : m_document(document)
67 {
68     if (!m_document)
69         return;
70     m_document->incrementActiveParserCount();
71 }
72
73 ActiveParserSession::~ActiveParserSession()
74 {
75     if (!m_document)
76         return;
77     m_document->decrementActiveParserCount();
78 }
79
80 PumpSession::PumpSession(unsigned& nestingLevel, Document* document)
81     : NestingLevelIncrementer(nestingLevel)
82     , ActiveParserSession(document)
83     // Setting processedTokens to INT_MAX causes us to check for yields
84     // after any token during any parse where yielding is allowed.
85     // At that time we'll initialize startTime.
86     , processedTokens(INT_MAX)
87     , startTime(0)
88     , needsYield(false)
89     , didSeeScript(false)
90 {
91 }
92
93 PumpSession::~PumpSession()
94 {
95 }
96
97 HTMLParserScheduler::HTMLParserScheduler(HTMLDocumentParser& parser)
98     : m_parser(parser)
99     , m_parserTimeLimit(parserTimeLimit(m_parser.document()->page()))
100     , m_parserChunkSize(parserChunkSize(m_parser.document()->page()))
101     , m_continueNextChunkTimer(this, &HTMLParserScheduler::continueNextChunkTimerFired)
102     , m_isSuspendedWithActiveTimer(false)
103 #if !ASSERT_DISABLED
104     , m_suspended(false)
105 #endif
106 {
107 }
108
109 HTMLParserScheduler::~HTMLParserScheduler()
110 {
111     m_continueNextChunkTimer.stop();
112 }
113
114 void HTMLParserScheduler::continueNextChunkTimerFired(Timer<HTMLParserScheduler>& timer)
115 {
116     ASSERT(!m_suspended);
117     ASSERT_UNUSED(timer, &timer == &m_continueNextChunkTimer);
118
119     // FIXME: The timer class should handle timer priorities instead of this code.
120     // If a layout is scheduled, wait again to let the layout timer run first.
121     if (m_parser.document()->isLayoutTimerActive()) {
122         m_continueNextChunkTimer.startOneShot(0);
123         return;
124     }
125     m_parser.resumeParsingAfterYield();
126 }
127
128 void HTMLParserScheduler::checkForYieldBeforeScript(PumpSession& session)
129 {
130     // If we've never painted before and a layout is pending, yield prior to running
131     // scripts to give the page a chance to paint earlier.
132     Document* document = m_parser.document();
133     bool needsFirstPaint = document->view() && !document->view()->hasEverPainted();
134     if (needsFirstPaint && document->isLayoutTimerActive())
135         session.needsYield = true;
136     session.didSeeScript = true;
137 }
138
139 void HTMLParserScheduler::scheduleForResume()
140 {
141     ASSERT(!m_suspended);
142     m_continueNextChunkTimer.startOneShot(0);
143 }
144
145 void HTMLParserScheduler::suspend()
146 {
147     ASSERT(!m_suspended);
148     ASSERT(!m_isSuspendedWithActiveTimer);
149 #if !ASSERT_DISABLED
150     m_suspended = true;
151 #endif
152
153     if (!m_continueNextChunkTimer.isActive())
154         return;
155     m_isSuspendedWithActiveTimer = true;
156     m_continueNextChunkTimer.stop();
157 }
158
159 void HTMLParserScheduler::resume()
160 {
161     ASSERT(m_suspended);
162     ASSERT(!m_continueNextChunkTimer.isActive());
163 #if !ASSERT_DISABLED
164     m_suspended = false;
165 #endif
166
167     if (!m_isSuspendedWithActiveTimer)
168         return;
169     m_isSuspendedWithActiveTimer = false;
170     m_continueNextChunkTimer.startOneShot(0);
171 }
172
173 }