82781100905ca041454a29b8aa82d08c5a40d05d
[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 ActiveParserSession::ActiveParserSession(Document* document)
56     : m_document(document)
57 {
58     if (!m_document)
59         return;
60     m_document->incrementActiveParserCount();
61 }
62
63 ActiveParserSession::~ActiveParserSession()
64 {
65     if (!m_document)
66         return;
67     m_document->decrementActiveParserCount();
68 }
69
70 PumpSession::PumpSession(unsigned& nestingLevel, Document* document)
71     : NestingLevelIncrementer(nestingLevel)
72     , ActiveParserSession(document)
73     // Setting processedTokens to INT_MAX causes us to check for yields
74     // after any token during any parse where yielding is allowed.
75     // At that time we'll initialize startTime.
76     , processedTokens(INT_MAX)
77     , startTime(0)
78     , needsYield(false)
79     , didSeeScript(false)
80 {
81 }
82
83 PumpSession::~PumpSession()
84 {
85 }
86
87 HTMLParserScheduler::HTMLParserScheduler(HTMLDocumentParser& parser)
88     : m_parser(parser)
89     , m_parserTimeLimit(parserTimeLimit(m_parser.document()->page()))
90     , m_parserChunkSize(defaultParserChunkSize)
91     , m_continueNextChunkTimer(this, &HTMLParserScheduler::continueNextChunkTimerFired)
92     , m_isSuspendedWithActiveTimer(false)
93 #if !ASSERT_DISABLED
94     , m_suspended(false)
95 #endif
96 {
97 }
98
99 HTMLParserScheduler::~HTMLParserScheduler()
100 {
101     m_continueNextChunkTimer.stop();
102 }
103
104 void HTMLParserScheduler::continueNextChunkTimerFired(Timer& timer)
105 {
106     ASSERT(!m_suspended);
107     ASSERT_UNUSED(timer, &timer == &m_continueNextChunkTimer);
108
109     // FIXME: The timer class should handle timer priorities instead of this code.
110     // If a layout is scheduled, wait again to let the layout timer run first.
111     if (m_parser.document()->isLayoutTimerActive()) {
112         m_continueNextChunkTimer.startOneShot(0);
113         return;
114     }
115     m_parser.resumeParsingAfterYield();
116 }
117
118 void HTMLParserScheduler::checkForYieldBeforeScript(PumpSession& session)
119 {
120     // If we've never painted before and a layout is pending, yield prior to running
121     // scripts to give the page a chance to paint earlier.
122     Document* document = m_parser.document();
123     bool needsFirstPaint = document->view() && !document->view()->hasEverPainted();
124     if (needsFirstPaint && document->isLayoutTimerActive())
125         session.needsYield = true;
126     session.didSeeScript = true;
127 }
128
129 void HTMLParserScheduler::scheduleForResume()
130 {
131     ASSERT(!m_suspended);
132     m_continueNextChunkTimer.startOneShot(0);
133 }
134
135 void HTMLParserScheduler::suspend()
136 {
137     ASSERT(!m_suspended);
138     ASSERT(!m_isSuspendedWithActiveTimer);
139 #if !ASSERT_DISABLED
140     m_suspended = true;
141 #endif
142
143     if (!m_continueNextChunkTimer.isActive())
144         return;
145     m_isSuspendedWithActiveTimer = true;
146     m_continueNextChunkTimer.stop();
147 }
148
149 void HTMLParserScheduler::resume()
150 {
151     ASSERT(m_suspended);
152     ASSERT(!m_continueNextChunkTimer.isActive());
153 #if !ASSERT_DISABLED
154     m_suspended = false;
155 #endif
156
157     if (!m_isSuspendedWithActiveTimer)
158         return;
159     m_isSuspendedWithActiveTimer = false;
160     m_continueNextChunkTimer.startOneShot(0);
161 }
162
163 }