1/*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2011, 2012 Google 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
28#include "config.h"
29#include "WorkerScriptController.h"
30
31#include "JSDOMBinding.h"
32#include "JSDedicatedWorkerGlobalScope.h"
33#include "ScriptSourceCode.h"
34#include "WebCoreJSClientData.h"
35#include "WorkerGlobalScope.h"
36#include "WorkerObjectProxy.h"
37#include "WorkerScriptDebugServer.h"
38#include "WorkerThread.h"
39#include <bindings/ScriptValue.h>
40#include <heap/StrongInlines.h>
41#include <interpreter/Interpreter.h>
42#include <runtime/Completion.h>
43#include <runtime/ExceptionHelpers.h>
44#include <runtime/Error.h>
45#include <runtime/JSLock.h>
46
47#if ENABLE(SHARED_WORKERS)
48#include "JSSharedWorkerGlobalScope.h"
49#endif
50
51using namespace JSC;
52
53namespace WebCore {
54
55WorkerScriptController::WorkerScriptController(WorkerGlobalScope* workerGlobalScope)
56    : m_vm(VM::create())
57    , m_workerGlobalScope(workerGlobalScope)
58    , m_workerGlobalScopeWrapper(*m_vm)
59    , m_executionForbidden(false)
60{
61    initNormalWorldClientData(m_vm.get());
62}
63
64WorkerScriptController::~WorkerScriptController()
65{
66    JSLockHolder lock(vm());
67    m_workerGlobalScopeWrapper.clear();
68    m_vm.clear();
69}
70
71void WorkerScriptController::initScript()
72{
73    ASSERT(!m_workerGlobalScopeWrapper);
74
75    JSLockHolder lock(m_vm.get());
76
77    // Explicitly protect the global object's prototype so it isn't collected
78    // when we allocate the global object. (Once the global object is fully
79    // constructed, it can mark its own prototype.)
80    Structure* workerGlobalScopePrototypeStructure = JSWorkerGlobalScopePrototype::createStructure(*m_vm, 0, jsNull());
81    Strong<JSWorkerGlobalScopePrototype> workerGlobalScopePrototype(*m_vm, JSWorkerGlobalScopePrototype::create(*m_vm, 0, workerGlobalScopePrototypeStructure));
82
83    if (m_workerGlobalScope->isDedicatedWorkerGlobalScope()) {
84        Structure* dedicatedContextPrototypeStructure = JSDedicatedWorkerGlobalScopePrototype::createStructure(*m_vm, 0, workerGlobalScopePrototype.get());
85        Strong<JSDedicatedWorkerGlobalScopePrototype> dedicatedContextPrototype(*m_vm, JSDedicatedWorkerGlobalScopePrototype::create(*m_vm, 0, dedicatedContextPrototypeStructure));
86        Structure* structure = JSDedicatedWorkerGlobalScope::createStructure(*m_vm, 0, dedicatedContextPrototype.get());
87
88        m_workerGlobalScopeWrapper.set(*m_vm, JSDedicatedWorkerGlobalScope::create(*m_vm, structure, static_cast<DedicatedWorkerGlobalScope*>(m_workerGlobalScope)));
89        workerGlobalScopePrototypeStructure->setGlobalObject(*m_vm, m_workerGlobalScopeWrapper.get());
90        dedicatedContextPrototypeStructure->setGlobalObject(*m_vm, m_workerGlobalScopeWrapper.get());
91        ASSERT(structure->globalObject() == m_workerGlobalScopeWrapper);
92        ASSERT(m_workerGlobalScopeWrapper->structure()->globalObject() == m_workerGlobalScopeWrapper);
93        workerGlobalScopePrototype->structure()->setGlobalObject(*m_vm, m_workerGlobalScopeWrapper.get());
94        dedicatedContextPrototype->structure()->setGlobalObject(*m_vm, m_workerGlobalScopeWrapper.get());
95#if ENABLE(SHARED_WORKERS)
96    } else {
97        ASSERT(m_workerGlobalScope->isSharedWorkerGlobalScope());
98        Structure* sharedContextPrototypeStructure = JSSharedWorkerGlobalScopePrototype::createStructure(*m_vm, 0, workerGlobalScopePrototype.get());
99        Strong<JSSharedWorkerGlobalScopePrototype> sharedContextPrototype(*m_vm, JSSharedWorkerGlobalScopePrototype::create(*m_vm, 0, sharedContextPrototypeStructure));
100        Structure* structure = JSSharedWorkerGlobalScope::createStructure(*m_vm, 0, sharedContextPrototype.get());
101
102        m_workerGlobalScopeWrapper.set(*m_vm, JSSharedWorkerGlobalScope::create(*m_vm, structure, static_cast<SharedWorkerGlobalScope*>(m_workerGlobalScope)));
103        workerGlobalScopePrototype->structure()->setGlobalObject(*m_vm, m_workerGlobalScopeWrapper.get());
104        sharedContextPrototype->structure()->setGlobalObject(*m_vm, m_workerGlobalScopeWrapper.get());
105#endif
106    }
107    ASSERT(m_workerGlobalScopeWrapper->globalObject() == m_workerGlobalScopeWrapper);
108    ASSERT(asObject(m_workerGlobalScopeWrapper->prototype())->globalObject() == m_workerGlobalScopeWrapper);
109}
110
111void WorkerScriptController::evaluate(const ScriptSourceCode& sourceCode)
112{
113    if (isExecutionForbidden())
114        return;
115
116    Deprecated::ScriptValue exception;
117    evaluate(sourceCode, &exception);
118    if (exception.jsValue()) {
119        JSLockHolder lock(vm());
120        reportException(m_workerGlobalScopeWrapper->globalExec(), exception.jsValue());
121    }
122}
123
124void WorkerScriptController::evaluate(const ScriptSourceCode& sourceCode, Deprecated::ScriptValue* exception)
125{
126    if (isExecutionForbidden())
127        return;
128
129    initScriptIfNeeded();
130
131    ExecState* exec = m_workerGlobalScopeWrapper->globalExec();
132    JSLockHolder lock(exec);
133
134    JSValue evaluationException;
135    JSC::evaluate(exec, sourceCode.jsSourceCode(), m_workerGlobalScopeWrapper->globalThis(), &evaluationException);
136
137    VM& vm = exec->vm();
138    if ((evaluationException && isTerminatedExecutionException(evaluationException))
139        || (vm.watchdog && vm.watchdog->didFire())) {
140        forbidExecution();
141        return;
142    }
143
144    if (evaluationException) {
145        String errorMessage;
146        int lineNumber = 0;
147        int columnNumber = 0;
148        String sourceURL = sourceCode.url().string();
149        if (m_workerGlobalScope->sanitizeScriptError(errorMessage, lineNumber, columnNumber, sourceURL, sourceCode.cachedScript()))
150            *exception = Deprecated::ScriptValue(*m_vm, exec->vm().throwException(exec, createError(exec, errorMessage.impl())));
151        else
152            *exception = Deprecated::ScriptValue(*m_vm, evaluationException);
153    }
154}
155
156void WorkerScriptController::setException(const Deprecated::ScriptValue& exception)
157{
158    m_workerGlobalScopeWrapper->globalExec()->vm().throwException(m_workerGlobalScopeWrapper->globalExec(), exception.jsValue());
159}
160
161void WorkerScriptController::scheduleExecutionTermination()
162{
163    // The mutex provides a memory barrier to ensure that once
164    // termination is scheduled, isExecutionTerminating will
165    // accurately reflect that state when called from another thread.
166    MutexLocker locker(m_scheduledTerminationMutex);
167    if (m_vm->watchdog)
168        m_vm->watchdog->fire();
169}
170
171bool WorkerScriptController::isExecutionTerminating() const
172{
173    // See comments in scheduleExecutionTermination regarding mutex usage.
174    MutexLocker locker(m_scheduledTerminationMutex);
175    if (m_vm->watchdog)
176        return m_vm->watchdog->didFire();
177    return false;
178}
179
180void WorkerScriptController::forbidExecution()
181{
182    ASSERT(m_workerGlobalScope->isContextThread());
183    m_executionForbidden = true;
184}
185
186bool WorkerScriptController::isExecutionForbidden() const
187{
188    ASSERT(m_workerGlobalScope->isContextThread());
189    return m_executionForbidden;
190}
191
192void WorkerScriptController::disableEval(const String& errorMessage)
193{
194    initScriptIfNeeded();
195    JSLockHolder lock(vm());
196
197    m_workerGlobalScopeWrapper->setEvalEnabled(false, errorMessage);
198}
199
200void WorkerScriptController::attachDebugger(JSC::Debugger* debugger)
201{
202    initScriptIfNeeded();
203    debugger->attach(m_workerGlobalScopeWrapper->globalObject());
204}
205
206void WorkerScriptController::detachDebugger(JSC::Debugger* debugger)
207{
208    debugger->detach(m_workerGlobalScopeWrapper->globalObject(), JSC::Debugger::TerminatingDebuggingSession);
209}
210
211} // namespace WebCore
212