1 /*
2 * Copyright (C) 2009, 2012 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 are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32
33 #include "bindings/v8/WorkerScriptController.h"
34
35 #include "V8DedicatedWorkerGlobalScope.h"
36 #include "V8ServiceWorkerGlobalScope.h"
37 #include "V8SharedWorkerGlobalScope.h"
38 #include "V8WorkerGlobalScope.h"
39 #include "bindings/v8/ScriptSourceCode.h"
40 #include "bindings/v8/ScriptValue.h"
41 #include "bindings/v8/V8ErrorHandler.h"
42 #include "bindings/v8/V8GCController.h"
43 #include "bindings/v8/V8Initializer.h"
44 #include "bindings/v8/V8ObjectConstructor.h"
45 #include "bindings/v8/V8ScriptRunner.h"
46 #include "bindings/v8/WrapperTypeInfo.h"
47 #include "core/inspector/ScriptCallStack.h"
48 #include "core/frame/DOMTimer.h"
49 #include "core/workers/SharedWorkerGlobalScope.h"
50 #include "core/workers/WorkerGlobalScope.h"
51 #include "core/workers/WorkerObjectProxy.h"
52 #include "core/workers/WorkerThread.h"
53 #include <v8.h>
54
55 #include "public/platform/Platform.h"
56 #include "public/platform/WebWorkerRunLoop.h"
57
58 namespace WebCore {
59
WorkerScriptController(WorkerGlobalScope & workerGlobalScope)60 WorkerScriptController::WorkerScriptController(WorkerGlobalScope& workerGlobalScope)
61 : m_workerGlobalScope(workerGlobalScope)
62 , m_executionForbidden(false)
63 , m_executionScheduledToTerminate(false)
64 {
65 v8::Isolate* isolate = v8::Isolate::New();
66 isolate->Enter();
67 V8Initializer::initializeWorker(isolate);
68 v8::V8::Initialize();
69 m_isolateHolder = adoptPtr(new gin::IsolateHolder(isolate));
70 V8PerIsolateData* data = V8PerIsolateData::create(isolate);
71 m_domDataStore = adoptPtr(new DOMDataStore(WorkerWorld));
72 data->setWorkerDOMDataStore(m_domDataStore.get());
73 }
74
~WorkerScriptController()75 WorkerScriptController::~WorkerScriptController()
76 {
77 m_domDataStore.clear();
78
79 // The corresponding call to didStartWorkerRunLoop is in
80 // WorkerThread::workerThread().
81 // See http://webkit.org/b/83104#c14 for why this is here.
82 blink::Platform::current()->didStopWorkerRunLoop(blink::WebWorkerRunLoop(&m_workerGlobalScope.thread()->runLoop()));
83
84 disposeContext();
85 V8PerIsolateData::dispose(isolate());
86 v8::Isolate* v8Isolate = isolate();
87 v8Isolate->Exit();
88 m_isolateHolder.clear();
89 v8Isolate->Dispose();
90 }
91
disposeContext()92 void WorkerScriptController::disposeContext()
93 {
94 m_perContextData.clear();
95 m_contextHolder.clear();
96 }
97
initializeContextIfNeeded()98 bool WorkerScriptController::initializeContextIfNeeded()
99 {
100 if (m_contextHolder)
101 return true;
102
103 v8::Handle<v8::Context> context = v8::Context::New(isolate());
104 if (context.IsEmpty())
105 return false;
106
107 m_contextHolder = adoptPtr(new gin::ContextHolder(isolate()));
108 m_contextHolder->SetContext(context);
109
110 v8::Context::Scope scope(context);
111
112 V8PerContextDataHolder::install(context);
113
114 m_perContextData = V8PerContextData::create(context);
115 if (!m_perContextData->init()) {
116 disposeContext();
117 return false;
118 }
119
120 // Set DebugId for the new context.
121 context->SetEmbedderData(0, v8AtomicString(isolate(), "worker"));
122
123 // Create a new JS object and use it as the prototype for the shadow global object.
124 const WrapperTypeInfo* contextType = &V8DedicatedWorkerGlobalScope::wrapperTypeInfo;
125 if (m_workerGlobalScope.isServiceWorkerGlobalScope())
126 contextType = &V8ServiceWorkerGlobalScope::wrapperTypeInfo;
127 else if (!m_workerGlobalScope.isDedicatedWorkerGlobalScope())
128 contextType = &V8SharedWorkerGlobalScope::wrapperTypeInfo;
129 v8::Handle<v8::Function> workerGlobalScopeConstructor = m_perContextData->constructorForType(contextType);
130 v8::Local<v8::Object> jsWorkerGlobalScope = V8ObjectConstructor::newInstance(workerGlobalScopeConstructor);
131 if (jsWorkerGlobalScope.IsEmpty()) {
132 disposeContext();
133 return false;
134 }
135
136 V8DOMWrapper::associateObjectWithWrapper<V8WorkerGlobalScope>(PassRefPtr<WorkerGlobalScope>(m_workerGlobalScope), contextType, jsWorkerGlobalScope, isolate(), WrapperConfiguration::Dependent);
137
138 // Insert the object instance as the prototype of the shadow object.
139 v8::Handle<v8::Object> globalObject = v8::Handle<v8::Object>::Cast(m_contextHolder->context()->Global()->GetPrototype());
140 globalObject->SetPrototype(jsWorkerGlobalScope);
141
142 return true;
143 }
144
evaluate(const String & script,const String & fileName,const TextPosition & scriptStartPosition,WorkerGlobalScopeExecutionState * state)145 ScriptValue WorkerScriptController::evaluate(const String& script, const String& fileName, const TextPosition& scriptStartPosition, WorkerGlobalScopeExecutionState* state)
146 {
147 v8::HandleScope handleScope(isolate());
148
149 if (!initializeContextIfNeeded())
150 return ScriptValue();
151
152 v8::Handle<v8::Context> context = m_contextHolder->context();
153 if (!m_disableEvalPending.isEmpty()) {
154 context->AllowCodeGenerationFromStrings(false);
155 context->SetErrorMessageForCodeGenerationFromStrings(v8String(isolate(), m_disableEvalPending));
156 m_disableEvalPending = String();
157 }
158
159 v8::Context::Scope scope(context);
160
161 v8::TryCatch block;
162
163 v8::Handle<v8::String> scriptString = v8String(isolate(), script);
164 v8::Handle<v8::Script> compiledScript = V8ScriptRunner::compileScript(scriptString, fileName, scriptStartPosition, 0, isolate());
165 v8::Local<v8::Value> result = V8ScriptRunner::runCompiledScript(compiledScript, &m_workerGlobalScope, isolate());
166
167 if (!block.CanContinue()) {
168 m_workerGlobalScope.script()->forbidExecution();
169 return ScriptValue();
170 }
171
172 if (block.HasCaught()) {
173 v8::Local<v8::Message> message = block.Message();
174 state->hadException = true;
175 state->errorMessage = toCoreString(message->Get());
176 state->lineNumber = message->GetLineNumber();
177 state->columnNumber = message->GetStartColumn() + 1;
178 V8TRYCATCH_FOR_V8STRINGRESOURCE_RETURN(V8StringResource<>, sourceURL, message->GetScriptResourceName(), ScriptValue());
179 state->sourceURL = sourceURL;
180 state->exception = ScriptValue(block.Exception(), isolate());
181 block.Reset();
182 } else
183 state->hadException = false;
184
185 if (result.IsEmpty() || result->IsUndefined())
186 return ScriptValue();
187
188 return ScriptValue(result, isolate());
189 }
190
evaluate(const ScriptSourceCode & sourceCode,RefPtr<ErrorEvent> * errorEvent)191 void WorkerScriptController::evaluate(const ScriptSourceCode& sourceCode, RefPtr<ErrorEvent>* errorEvent)
192 {
193 if (isExecutionForbidden())
194 return;
195
196 WorkerGlobalScopeExecutionState state;
197 evaluate(sourceCode.source(), sourceCode.url().string(), sourceCode.startPosition(), &state);
198 if (state.hadException) {
199 if (errorEvent) {
200 *errorEvent = m_workerGlobalScope.shouldSanitizeScriptError(state.sourceURL, NotSharableCrossOrigin) ?
201 ErrorEvent::createSanitizedError(0) : ErrorEvent::create(state.errorMessage, state.sourceURL, state.lineNumber, state.columnNumber, 0);
202 V8ErrorHandler::storeExceptionOnErrorEventWrapper(errorEvent->get(), state.exception.v8Value(), isolate());
203 } else {
204 ASSERT(!m_workerGlobalScope.shouldSanitizeScriptError(state.sourceURL, NotSharableCrossOrigin));
205 RefPtr<ErrorEvent> event = m_errorEventFromImportedScript ? m_errorEventFromImportedScript.release() : ErrorEvent::create(state.errorMessage, state.sourceURL, state.lineNumber, state.columnNumber, 0);
206 m_workerGlobalScope.reportException(event, 0, NotSharableCrossOrigin);
207 }
208 }
209 }
210
scheduleExecutionTermination()211 void WorkerScriptController::scheduleExecutionTermination()
212 {
213 // The mutex provides a memory barrier to ensure that once
214 // termination is scheduled, isExecutionTerminating will
215 // accurately reflect that state when called from another thread.
216 {
217 MutexLocker locker(m_scheduledTerminationMutex);
218 m_executionScheduledToTerminate = true;
219 }
220 v8::V8::TerminateExecution(isolate());
221 }
222
isExecutionTerminating() const223 bool WorkerScriptController::isExecutionTerminating() const
224 {
225 // See comments in scheduleExecutionTermination regarding mutex usage.
226 MutexLocker locker(m_scheduledTerminationMutex);
227 return m_executionScheduledToTerminate;
228 }
229
forbidExecution()230 void WorkerScriptController::forbidExecution()
231 {
232 ASSERT(m_workerGlobalScope.isContextThread());
233 m_executionForbidden = true;
234 }
235
isExecutionForbidden() const236 bool WorkerScriptController::isExecutionForbidden() const
237 {
238 ASSERT(m_workerGlobalScope.isContextThread());
239 return m_executionForbidden;
240 }
241
disableEval(const String & errorMessage)242 void WorkerScriptController::disableEval(const String& errorMessage)
243 {
244 m_disableEvalPending = errorMessage;
245 }
246
rethrowExceptionFromImportedScript(PassRefPtr<ErrorEvent> errorEvent)247 void WorkerScriptController::rethrowExceptionFromImportedScript(PassRefPtr<ErrorEvent> errorEvent)
248 {
249 m_errorEventFromImportedScript = errorEvent;
250 throwError(V8ThrowException::createError(v8GeneralError, m_errorEventFromImportedScript->message(), isolate()), isolate());
251 }
252
controllerForContext()253 WorkerScriptController* WorkerScriptController::controllerForContext()
254 {
255 v8::Isolate* isolate = v8::Isolate::GetCurrent();
256 // Happens on frame destruction, check otherwise GetCurrent() will crash.
257 if (!isolate || !isolate->InContext())
258 return 0;
259 v8::Handle<v8::Context> context = isolate->GetCurrentContext();
260 v8::Handle<v8::Object> global = context->Global();
261 global = global->FindInstanceInPrototypeChain(V8WorkerGlobalScope::domTemplate(isolate, WorkerWorld));
262 // Return 0 if the current executing context is not the worker context.
263 if (global.IsEmpty())
264 return 0;
265 WorkerGlobalScope* workerGlobalScope = V8WorkerGlobalScope::toNative(global);
266 return workerGlobalScope->script();
267 }
268
269 } // namespace WebCore
270