~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/html/parser/HTMLScriptRunner.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2010 Google, Inc. All Rights Reserved.
 
3
 *
 
4
 * Redistribution and use in source and binary forms, with or without
 
5
 * modification, are permitted provided that the following conditions
 
6
 * are met:
 
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.
 
12
 *
 
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. 
 
24
 */
 
25
 
 
26
#include "config.h"
 
27
#include "HTMLScriptRunner.h"
 
28
 
 
29
#include "Attribute.h"
 
30
#include "CachedScript.h"
 
31
#include "CachedResourceLoader.h"
 
32
#include "Element.h"
 
33
#include "Event.h"
 
34
#include "Frame.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"
 
43
 
 
44
namespace WebCore {
 
45
 
 
46
using namespace HTMLNames;
 
47
 
 
48
HTMLScriptRunner::HTMLScriptRunner(Document* document, HTMLScriptRunnerHost* host)
 
49
    : m_document(document)
 
50
    , m_host(host)
 
51
    , m_scriptNestingLevel(0)
 
52
    , m_hasScriptsWaitingForStylesheets(false)
 
53
{
 
54
    ASSERT(m_host);
 
55
}
 
56
 
 
57
HTMLScriptRunner::~HTMLScriptRunner()
 
58
{
 
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);
 
62
 
 
63
    while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
 
64
        PendingScript pendingScript = m_scriptsToExecuteAfterParsing.takeFirst();
 
65
        if (pendingScript.cachedScript() && pendingScript.watchingForLoad())
 
66
            stopWatchingForLoad(pendingScript);
 
67
    }
 
68
}
 
69
 
 
70
void HTMLScriptRunner::detach()
 
71
{
 
72
    m_document = 0;
 
73
}
 
74
 
 
75
static KURL documentURLForScriptExecution(Document* document)
 
76
{
 
77
    if (!document || !document->frame())
 
78
        return KURL();
 
79
 
 
80
    // Use the URL of the currently active document for this frame.
 
81
    return document->frame()->document()->url();
 
82
}
 
83
 
 
84
inline PassRefPtr<Event> createScriptLoadEvent()
 
85
{
 
86
    return Event::create(eventNames().loadEvent, false, false);
 
87
}
 
88
 
 
89
ScriptSourceCode HTMLScriptRunner::sourceFromPendingScript(const PendingScript& script, bool& errorOccurred) const
 
90
{
 
91
    if (script.cachedScript()) {
 
92
        errorOccurred = script.cachedScript()->errorOccurred();
 
93
        ASSERT(script.cachedScript()->isLoaded());
 
94
        return ScriptSourceCode(script.cachedScript());
 
95
    }
 
96
    errorOccurred = false;
 
97
    return ScriptSourceCode(script.element()->textContent(), documentURLForScriptExecution(m_document), script.startingPosition());
 
98
}
 
99
 
 
100
bool HTMLScriptRunner::isPendingScriptReady(const PendingScript& script)
 
101
{
 
102
    m_hasScriptsWaitingForStylesheets = !m_document->haveStylesheetsLoaded();
 
103
    if (m_hasScriptsWaitingForStylesheets)
 
104
        return false;
 
105
    if (script.cachedScript() && !script.cachedScript()->isLoaded())
 
106
        return false;
 
107
    return true;
 
108
}
 
109
 
 
110
void HTMLScriptRunner::executeParsingBlockingScript()
 
111
{
 
112
    ASSERT(m_document);
 
113
    ASSERT(!m_scriptNestingLevel);
 
114
    ASSERT(m_document->haveStylesheetsLoaded());
 
115
    ASSERT(isPendingScriptReady(m_parserBlockingScript));
 
116
 
 
117
    InsertionPointRecord insertionPointRecord(m_host->inputStream());
 
118
    executePendingScriptAndDispatchEvent(m_parserBlockingScript);
 
119
}
 
120
 
 
121
void HTMLScriptRunner::executePendingScriptAndDispatchEvent(PendingScript& pendingScript)
 
122
{
 
123
    bool errorOccurred = false;
 
124
    ScriptSourceCode sourceCode = sourceFromPendingScript(pendingScript, errorOccurred);
 
125
 
 
126
    // Stop watching loads before executeScript to prevent recursion if the script reloads itself.
 
127
    if (pendingScript.cachedScript() && pendingScript.watchingForLoad())
 
128
        stopWatchingForLoad(pendingScript);
 
129
 
 
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);
 
135
        if (errorOccurred)
 
136
            scriptElement->dispatchErrorEvent();
 
137
        else {
 
138
            ASSERT(isExecutingScript());
 
139
            scriptElement->executeScript(sourceCode);
 
140
            element->dispatchEvent(createScriptLoadEvent());
 
141
        }
 
142
    }
 
143
    ASSERT(!m_scriptNestingLevel);
 
144
}
 
145
 
 
146
void HTMLScriptRunner::watchForLoad(PendingScript& pendingScript)
 
147
{
 
148
    ASSERT(!pendingScript.watchingForLoad());
 
149
    m_host->watchForLoad(pendingScript.cachedScript());
 
150
    pendingScript.setWatchingForLoad(true);
 
151
}
 
152
 
 
153
void HTMLScriptRunner::stopWatchingForLoad(PendingScript& pendingScript)
 
154
{
 
155
    ASSERT(pendingScript.watchingForLoad());
 
156
    m_host->stopWatchingForLoad(pendingScript.cachedScript());
 
157
    pendingScript.setWatchingForLoad(false);
 
158
}
 
159
 
 
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)
 
163
{
 
164
    ASSERT(scriptElement);
 
165
    // FIXME: If scripting is disabled, always just return.
 
166
 
 
167
    bool hadPreloadScanner = m_host->hasPreloadScanner();
 
168
 
 
169
    // Try to execute the script given to us.
 
170
    runScript(scriptElement.get(), scriptStartPosition);
 
171
 
 
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();
 
179
    }
 
180
}
 
181
 
 
182
bool HTMLScriptRunner::hasParserBlockingScript() const
 
183
{
 
184
    return !!m_parserBlockingScript.element();
 
185
}
 
186
 
 
187
void HTMLScriptRunner::executeParsingBlockingScripts()
 
188
{
 
189
    while (hasParserBlockingScript() && isPendingScriptReady(m_parserBlockingScript))
 
190
        executeParsingBlockingScript();
 
191
}
 
192
 
 
193
void HTMLScriptRunner::executeScriptsWaitingForLoad(CachedResource* cachedScript)
 
194
{
 
195
    ASSERT(!m_scriptNestingLevel);
 
196
    ASSERT(hasParserBlockingScript());
 
197
    ASSERT_UNUSED(cachedScript, m_parserBlockingScript.cachedScript() == cachedScript);
 
198
    ASSERT(m_parserBlockingScript.cachedScript()->isLoaded());
 
199
    executeParsingBlockingScripts();
 
200
}
 
201
 
 
202
void HTMLScriptRunner::executeScriptsWaitingForStylesheets()
 
203
{
 
204
    ASSERT(m_document);
 
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();
 
211
}
 
212
 
 
213
bool HTMLScriptRunner::executeScriptsWaitingForParsing()
 
214
{
 
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());
 
221
            return false;
 
222
        }
 
223
        PendingScript first = m_scriptsToExecuteAfterParsing.takeFirst();
 
224
        executePendingScriptAndDispatchEvent(first);
 
225
        // FIXME: What is this m_document check for?
 
226
        if (!m_document)
 
227
            return false;
 
228
    }
 
229
    return true;
 
230
}
 
231
 
 
232
void HTMLScriptRunner::requestParsingBlockingScript(Element* element)
 
233
{
 
234
    if (!requestPendingScript(m_parserBlockingScript, element))
 
235
        return;
 
236
 
 
237
    ASSERT(m_parserBlockingScript.cachedScript());
 
238
 
 
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);
 
244
}
 
245
 
 
246
void HTMLScriptRunner::requestDeferredScript(Element* element)
 
247
{
 
248
    PendingScript pendingScript;
 
249
    if (!requestPendingScript(pendingScript, element))
 
250
        return;
 
251
 
 
252
    ASSERT(pendingScript.cachedScript());
 
253
    m_scriptsToExecuteAfterParsing.append(pendingScript);
 
254
}
 
255
 
 
256
bool HTMLScriptRunner::requestPendingScript(PendingScript& pendingScript, Element* script) const
 
257
{
 
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();
 
262
    if (!cachedScript) {
 
263
        notImplemented(); // Dispatch error event.
 
264
        return false;
 
265
    }
 
266
    pendingScript.setCachedScript(cachedScript);
 
267
    return true;
 
268
}
 
269
 
 
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)
 
273
{
 
274
    ASSERT(m_document);
 
275
    ASSERT(!hasParserBlockingScript());
 
276
    {
 
277
        InsertionPointRecord insertionPointRecord(m_host->inputStream());
 
278
        NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel);
 
279
 
 
280
        ScriptElement* scriptElement = toScriptElement(script);
 
281
 
 
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);
 
287
        if (!scriptElement)
 
288
            return;
 
289
 
 
290
        scriptElement->prepareScript(scriptStartPosition);
 
291
 
 
292
        if (!scriptElement->willBeParserExecuted())
 
293
            return;
 
294
 
 
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);
 
301
            } else {
 
302
                ScriptSourceCode sourceCode(script->textContent(), documentURLForScriptExecution(m_document), scriptStartPosition);
 
303
                scriptElement->executeScript(sourceCode);
 
304
            }
 
305
        } else
 
306
            requestParsingBlockingScript(script);
 
307
    }
 
308
}
 
309
 
 
310
}