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// defaultParserTimeLimit is the seconds the parser will run in one write() call
36// before yielding. Inline <script> execution can cause it to exceed the limit.
37// FIXME: We would like this value to be 0.2.
38static const double defaultParserTimeLimit = 0.500;
39
40namespace WebCore {
41
42static double parserTimeLimit(Page* page)
43{
44 // We're using the poorly named customHTMLTokenizerTimeDelay setting.
45 if (page && page->hasCustomHTMLTokenizerTimeDelay())
46 return page->customHTMLTokenizerTimeDelay();
47 return defaultParserTimeLimit;
48}
49
50ActiveParserSession::ActiveParserSession(Document* document)
51 : m_document(document)
52{
53 if (!m_document)
54 return;
55 m_document->incrementActiveParserCount();
56}
57
58ActiveParserSession::~ActiveParserSession()
59{
60 if (!m_document)
61 return;
62 m_document->decrementActiveParserCount();
63}
64
65PumpSession::PumpSession(unsigned& nestingLevel, Document* document)
66 : NestingLevelIncrementer(nestingLevel)
67 , ActiveParserSession(document)
68 // Setting processedTokens to INT_MAX causes us to check for yields
69 // after any token during any parse where yielding is allowed.
70 // At that time we'll initialize startTime.
71 , processedTokens(INT_MAX)
72 , didSeeScript(false)
73{
74}
75
76PumpSession::~PumpSession() = default;
77
78HTMLParserScheduler::HTMLParserScheduler(HTMLDocumentParser& parser)
79 : m_parser(parser)
80 , m_parserTimeLimit(Seconds(parserTimeLimit(m_parser.document()->page())))
81 , m_continueNextChunkTimer(*this, &HTMLParserScheduler::continueNextChunkTimerFired)
82 , m_isSuspendedWithActiveTimer(false)
83#if !ASSERT_DISABLED
84 , m_suspended(false)
85#endif
86{
87}
88
89HTMLParserScheduler::~HTMLParserScheduler()
90{
91 m_continueNextChunkTimer.stop();
92}
93
94void HTMLParserScheduler::continueNextChunkTimerFired()
95{
96 ASSERT(!m_suspended);
97
98 // FIXME: The timer class should handle timer priorities instead of this code.
99 // If a layout is scheduled, wait again to let the layout timer run first.
100 if (m_parser.document()->isLayoutTimerActive()) {
101 m_continueNextChunkTimer.startOneShot(0_s);
102 return;
103 }
104 m_parser.resumeParsingAfterYield();
105}
106
107bool HTMLParserScheduler::shouldYieldBeforeExecutingScript(PumpSession& session)
108{
109 // If we've never painted before and a layout is pending, yield prior to running
110 // scripts to give the page a chance to paint earlier.
111 RefPtr<Document> document = m_parser.document();
112 bool needsFirstPaint = document->view() && !document->view()->hasEverPainted();
113 session.didSeeScript = true;
114
115 if (UNLIKELY(m_documentHasActiveParserYieldTokens))
116 return true;
117
118 return needsFirstPaint && document->isLayoutTimerActive();
119}
120
121void HTMLParserScheduler::scheduleForResume()
122{
123 ASSERT(!m_suspended);
124 m_continueNextChunkTimer.startOneShot(0_s);
125}
126
127void HTMLParserScheduler::suspend()
128{
129 ASSERT(!m_suspended);
130 ASSERT(!m_isSuspendedWithActiveTimer);
131#if !ASSERT_DISABLED
132 m_suspended = true;
133#endif
134
135 if (!m_continueNextChunkTimer.isActive())
136 return;
137 m_isSuspendedWithActiveTimer = true;
138 m_continueNextChunkTimer.stop();
139}
140
141void HTMLParserScheduler::resume()
142{
143 ASSERT(m_suspended);
144 ASSERT(!m_continueNextChunkTimer.isActive());
145#if !ASSERT_DISABLED
146 m_suspended = false;
147#endif
148
149 if (!m_isSuspendedWithActiveTimer)
150 return;
151 m_isSuspendedWithActiveTimer = false;
152 m_continueNextChunkTimer.startOneShot(0_s);
153}
154
155}
156