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 "HTMLScriptRunner.h"
29
#include "Attribute.h"
30
#include "CachedScript.h"
31
#include "CachedResourceLoader.h"
35
#include "HTMLInputStream.h"
36
#include "HTMLNames.h"
37
#include "HTMLScriptRunnerHost.h"
38
#include "IgnoreDestructiveWriteCountIncrementer.h"
39
#include "NestingLevelIncrementer.h"
40
#include "NotImplemented.h"
41
#include "ScriptElement.h"
42
#include "ScriptSourceCode.h"
46
using namespace HTMLNames;
48
HTMLScriptRunner::HTMLScriptRunner(Document* document, HTMLScriptRunnerHost* host)
49
: m_document(document)
51
, m_scriptNestingLevel(0)
52
, m_hasScriptsWaitingForStylesheets(false)
57
HTMLScriptRunner::~HTMLScriptRunner()
59
// FIXME: Should we be passed a "done loading/parsing" callback sooner than destruction?
60
if (m_parserBlockingScript.cachedScript() && m_parserBlockingScript.watchingForLoad())
61
stopWatchingForLoad(m_parserBlockingScript);
63
while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
64
PendingScript pendingScript = m_scriptsToExecuteAfterParsing.takeFirst();
65
if (pendingScript.cachedScript() && pendingScript.watchingForLoad())
66
stopWatchingForLoad(pendingScript);
70
void HTMLScriptRunner::detach()
75
static KURL documentURLForScriptExecution(Document* document)
77
if (!document || !document->frame())
80
// Use the URL of the currently active document for this frame.
81
return document->frame()->document()->url();
84
inline PassRefPtr<Event> createScriptLoadEvent()
86
return Event::create(eventNames().loadEvent, false, false);
89
ScriptSourceCode HTMLScriptRunner::sourceFromPendingScript(const PendingScript& script, bool& errorOccurred) const
91
if (script.cachedScript()) {
92
errorOccurred = script.cachedScript()->errorOccurred();
93
ASSERT(script.cachedScript()->isLoaded());
94
return ScriptSourceCode(script.cachedScript());
96
errorOccurred = false;
97
return ScriptSourceCode(script.element()->textContent(), documentURLForScriptExecution(m_document), script.startingPosition());
100
bool HTMLScriptRunner::isPendingScriptReady(const PendingScript& script)
102
m_hasScriptsWaitingForStylesheets = !m_document->haveStylesheetsLoaded();
103
if (m_hasScriptsWaitingForStylesheets)
105
if (script.cachedScript() && !script.cachedScript()->isLoaded())
110
void HTMLScriptRunner::executeParsingBlockingScript()
113
ASSERT(!m_scriptNestingLevel);
114
ASSERT(m_document->haveStylesheetsLoaded());
115
ASSERT(isPendingScriptReady(m_parserBlockingScript));
117
InsertionPointRecord insertionPointRecord(m_host->inputStream());
118
executePendingScriptAndDispatchEvent(m_parserBlockingScript);
121
void HTMLScriptRunner::executePendingScriptAndDispatchEvent(PendingScript& pendingScript)
123
bool errorOccurred = false;
124
ScriptSourceCode sourceCode = sourceFromPendingScript(pendingScript, errorOccurred);
126
// Stop watching loads before executeScript to prevent recursion if the script reloads itself.
127
if (pendingScript.cachedScript() && pendingScript.watchingForLoad())
128
stopWatchingForLoad(pendingScript);
130
// Clear the pending script before possible rentrancy from executeScript()
131
RefPtr<Element> element = pendingScript.releaseElementAndClear();
132
if (ScriptElement* scriptElement = toScriptElement(element.get())) {
133
NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel);
134
IgnoreDestructiveWriteCountIncrementer ignoreDestructiveWriteCountIncrementer(m_document);
136
scriptElement->dispatchErrorEvent();
138
ASSERT(isExecutingScript());
139
scriptElement->executeScript(sourceCode);
140
element->dispatchEvent(createScriptLoadEvent());
143
ASSERT(!m_scriptNestingLevel);
146
void HTMLScriptRunner::watchForLoad(PendingScript& pendingScript)
148
ASSERT(!pendingScript.watchingForLoad());
149
m_host->watchForLoad(pendingScript.cachedScript());
150
pendingScript.setWatchingForLoad(true);
153
void HTMLScriptRunner::stopWatchingForLoad(PendingScript& pendingScript)
155
ASSERT(pendingScript.watchingForLoad());
156
m_host->stopWatchingForLoad(pendingScript.cachedScript());
157
pendingScript.setWatchingForLoad(false);
160
// This function should match 10.2.5.11 "An end tag whose tag name is 'script'"
161
// Script handling lives outside the tree builder to keep the each class simple.
162
void HTMLScriptRunner::execute(PassRefPtr<Element> scriptElement, const TextPosition& scriptStartPosition)
164
ASSERT(scriptElement);
165
// FIXME: If scripting is disabled, always just return.
167
bool hadPreloadScanner = m_host->hasPreloadScanner();
169
// Try to execute the script given to us.
170
runScript(scriptElement.get(), scriptStartPosition);
172
if (hasParserBlockingScript()) {
173
if (m_scriptNestingLevel)
174
return; // Unwind to the outermost HTMLScriptRunner::execute before continuing parsing.
175
// If preload scanner got created, it is missing the source after the current insertion point. Append it and scan.
176
if (!hadPreloadScanner && m_host->hasPreloadScanner())
177
m_host->appendCurrentInputStreamToPreloadScannerAndScan();
178
executeParsingBlockingScripts();
182
bool HTMLScriptRunner::hasParserBlockingScript() const
184
return !!m_parserBlockingScript.element();
187
void HTMLScriptRunner::executeParsingBlockingScripts()
189
while (hasParserBlockingScript() && isPendingScriptReady(m_parserBlockingScript))
190
executeParsingBlockingScript();
193
void HTMLScriptRunner::executeScriptsWaitingForLoad(CachedResource* cachedScript)
195
ASSERT(!m_scriptNestingLevel);
196
ASSERT(hasParserBlockingScript());
197
ASSERT_UNUSED(cachedScript, m_parserBlockingScript.cachedScript() == cachedScript);
198
ASSERT(m_parserBlockingScript.cachedScript()->isLoaded());
199
executeParsingBlockingScripts();
202
void HTMLScriptRunner::executeScriptsWaitingForStylesheets()
205
// Callers should check hasScriptsWaitingForStylesheets() before calling
206
// to prevent parser or script re-entry during </style> parsing.
207
ASSERT(hasScriptsWaitingForStylesheets());
208
ASSERT(!m_scriptNestingLevel);
209
ASSERT(m_document->haveStylesheetsLoaded());
210
executeParsingBlockingScripts();
213
bool HTMLScriptRunner::executeScriptsWaitingForParsing()
215
while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
216
ASSERT(!m_scriptNestingLevel);
217
ASSERT(!hasParserBlockingScript());
218
ASSERT(m_scriptsToExecuteAfterParsing.first().cachedScript());
219
if (!m_scriptsToExecuteAfterParsing.first().cachedScript()->isLoaded()) {
220
watchForLoad(m_scriptsToExecuteAfterParsing.first());
223
PendingScript first = m_scriptsToExecuteAfterParsing.takeFirst();
224
executePendingScriptAndDispatchEvent(first);
225
// FIXME: What is this m_document check for?
232
void HTMLScriptRunner::requestParsingBlockingScript(Element* element)
234
if (!requestPendingScript(m_parserBlockingScript, element))
237
ASSERT(m_parserBlockingScript.cachedScript());
239
// We only care about a load callback if cachedScript is not already
240
// in the cache. Callers will attempt to run the m_parserBlockingScript
241
// if possible before returning control to the parser.
242
if (!m_parserBlockingScript.cachedScript()->isLoaded())
243
watchForLoad(m_parserBlockingScript);
246
void HTMLScriptRunner::requestDeferredScript(Element* element)
248
PendingScript pendingScript;
249
if (!requestPendingScript(pendingScript, element))
252
ASSERT(pendingScript.cachedScript());
253
m_scriptsToExecuteAfterParsing.append(pendingScript);
256
bool HTMLScriptRunner::requestPendingScript(PendingScript& pendingScript, Element* script) const
258
ASSERT(!pendingScript.element());
259
pendingScript.setElement(script);
260
// This should correctly return 0 for empty or invalid srcValues.
261
CachedScript* cachedScript = toScriptElement(script)->cachedScript().get();
263
notImplemented(); // Dispatch error event.
266
pendingScript.setCachedScript(cachedScript);
270
// This method is meant to match the HTML5 definition of "running a script"
271
// http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#running-a-script
272
void HTMLScriptRunner::runScript(Element* script, const TextPosition& scriptStartPosition)
275
ASSERT(!hasParserBlockingScript());
277
InsertionPointRecord insertionPointRecord(m_host->inputStream());
278
NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel);
280
ScriptElement* scriptElement = toScriptElement(script);
282
// This contains both and ASSERTION and a null check since we should not
283
// be getting into the case of a null script element, but seem to be from
284
// time to time. The assertion is left in to help find those cases and
285
// is being tracked by <https://bugs.webkit.org/show_bug.cgi?id=60559>.
286
ASSERT(scriptElement);
290
scriptElement->prepareScript(scriptStartPosition);
292
if (!scriptElement->willBeParserExecuted())
295
if (scriptElement->willExecuteWhenDocumentFinishedParsing())
296
requestDeferredScript(script);
297
else if (scriptElement->readyToBeParserExecuted()) {
298
if (m_scriptNestingLevel == 1) {
299
m_parserBlockingScript.setElement(script);
300
m_parserBlockingScript.setStartingPosition(scriptStartPosition);
302
ScriptSourceCode sourceCode(script->textContent(), documentURLForScriptExecution(m_document), scriptStartPosition);
303
scriptElement->executeScript(sourceCode);
306
requestParsingBlockingScript(script);