1/*
2 * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3 * Copyright (C) 2011-2017 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 "ScriptRunner.h"
29
30#include "Element.h"
31#include "PendingScript.h"
32#include "ScriptElement.h"
33
34namespace WebCore {
35
36ScriptRunner::ScriptRunner(Document& document)
37 : m_document(document)
38 , m_timer(*this, &ScriptRunner::timerFired)
39{
40}
41
42ScriptRunner::~ScriptRunner()
43{
44 for (auto& pendingScript : m_scriptsToExecuteSoon) {
45 UNUSED_PARAM(pendingScript);
46 m_document.decrementLoadEventDelayCount();
47 }
48 for (auto& pendingScript : m_scriptsToExecuteInOrder) {
49 if (pendingScript->watchingForLoad())
50 pendingScript->clearClient();
51 m_document.decrementLoadEventDelayCount();
52 }
53 for (auto& pendingScript : m_pendingAsyncScripts) {
54 if (pendingScript->watchingForLoad())
55 pendingScript->clearClient();
56 m_document.decrementLoadEventDelayCount();
57 }
58}
59
60void ScriptRunner::queueScriptForExecution(ScriptElement& scriptElement, LoadableScript& loadableScript, ExecutionType executionType)
61{
62 ASSERT(scriptElement.element().isConnected());
63
64 m_document.incrementLoadEventDelayCount();
65
66 auto pendingScript = PendingScript::create(scriptElement, loadableScript);
67 switch (executionType) {
68 case ASYNC_EXECUTION:
69 m_pendingAsyncScripts.add(pendingScript.copyRef());
70 break;
71 case IN_ORDER_EXECUTION:
72 m_scriptsToExecuteInOrder.append(pendingScript.copyRef());
73 break;
74 }
75 pendingScript->setClient(*this);
76}
77
78void ScriptRunner::suspend()
79{
80 m_timer.stop();
81}
82
83void ScriptRunner::resume()
84{
85 if (hasPendingScripts() && !m_document.hasActiveParserYieldToken())
86 m_timer.startOneShot(0_s);
87}
88
89void ScriptRunner::documentFinishedParsing()
90{
91 if (!m_scriptsToExecuteSoon.isEmpty() && !m_timer.isActive())
92 resume();
93}
94
95void ScriptRunner::notifyFinished(PendingScript& pendingScript)
96{
97 if (pendingScript.element().willExecuteInOrder())
98 ASSERT(!m_scriptsToExecuteInOrder.isEmpty());
99 else {
100 ASSERT(m_pendingAsyncScripts.contains(pendingScript));
101 m_scriptsToExecuteSoon.append(m_pendingAsyncScripts.take(pendingScript)->ptr());
102 }
103 pendingScript.clearClient();
104
105 if (!m_document.hasActiveParserYieldToken())
106 m_timer.startOneShot(0_s);
107}
108
109void ScriptRunner::timerFired()
110{
111 Ref<Document> protect(m_document);
112
113 Vector<RefPtr<PendingScript>> scripts;
114
115 if (!m_document.shouldDeferAsynchronousScriptsUntilParsingFinishes())
116 scripts.swap(m_scriptsToExecuteSoon);
117
118 size_t numInOrderScriptsToExecute = 0;
119 for (; numInOrderScriptsToExecute < m_scriptsToExecuteInOrder.size() && m_scriptsToExecuteInOrder[numInOrderScriptsToExecute]->isLoaded(); ++numInOrderScriptsToExecute)
120 scripts.append(m_scriptsToExecuteInOrder[numInOrderScriptsToExecute].ptr());
121 if (numInOrderScriptsToExecute)
122 m_scriptsToExecuteInOrder.remove(0, numInOrderScriptsToExecute);
123
124 for (auto& currentScript : scripts) {
125 auto script = WTFMove(currentScript);
126 ASSERT(script);
127 // Paper over https://bugs.webkit.org/show_bug.cgi?id=144050
128 if (!script)
129 continue;
130 ASSERT(script->needsLoading());
131 script->element().executePendingScript(*script);
132 m_document.decrementLoadEventDelayCount();
133 }
134}
135
136}
137