OLD | NEW |
| (Empty) |
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 "bindings/core/v8/V8DedicatedWorkerGlobalScope.h" | |
36 #include "bindings/core/v8/V8SharedWorkerGlobalScope.h" | |
37 #include "bindings/core/v8/V8WorkerGlobalScope.h" | |
38 #include "bindings/modules/v8/V8ServiceWorkerGlobalScope.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/events/ErrorEvent.h" | |
48 #include "core/frame/DOMTimer.h" | |
49 #include "core/inspector/ScriptCallStack.h" | |
50 #include "core/workers/SharedWorkerGlobalScope.h" | |
51 #include "core/workers/WorkerGlobalScope.h" | |
52 #include "core/workers/WorkerObjectProxy.h" | |
53 #include "core/workers/WorkerThread.h" | |
54 #include "platform/heap/ThreadState.h" | |
55 #include <v8.h> | |
56 | |
57 #include "public/platform/Platform.h" | |
58 #include "public/platform/WebWorkerRunLoop.h" | |
59 | |
60 namespace WebCore { | |
61 | |
62 WorkerScriptController::WorkerScriptController(WorkerGlobalScope& workerGlobalSc
ope) | |
63 : m_isolate(v8::Isolate::New()) | |
64 , m_workerGlobalScope(workerGlobalScope) | |
65 , m_executionForbidden(false) | |
66 , m_executionScheduledToTerminate(false) | |
67 { | |
68 m_isolate->Enter(); | |
69 V8Initializer::initializeWorker(m_isolate); | |
70 v8::V8::Initialize(); | |
71 V8PerIsolateData::ensureInitialized(m_isolate); | |
72 m_world = DOMWrapperWorld::create(WorkerWorldId); | |
73 m_interruptor = adoptPtr(new V8IsolateInterruptor(m_isolate)); | |
74 ThreadState::current()->addInterruptor(m_interruptor.get()); | |
75 } | |
76 | |
77 // We need to postpone V8 Isolate destruction until the very end of | |
78 // worker thread finalization when all objects on the worker heap | |
79 // are destroyed. | |
80 class IsolateCleanupTask : public ThreadState::CleanupTask { | |
81 public: | |
82 static PassOwnPtr<IsolateCleanupTask> create(v8::Isolate* isolate) | |
83 { | |
84 return adoptPtr(new IsolateCleanupTask(isolate)); | |
85 } | |
86 | |
87 virtual void postCleanup() | |
88 { | |
89 V8PerIsolateData::dispose(m_isolate); | |
90 m_isolate->Exit(); | |
91 m_isolate->Dispose(); | |
92 } | |
93 | |
94 private: | |
95 explicit IsolateCleanupTask(v8::Isolate* isolate) : m_isolate(isolate) { } | |
96 | |
97 v8::Isolate* m_isolate; | |
98 }; | |
99 | |
100 WorkerScriptController::~WorkerScriptController() | |
101 { | |
102 ThreadState::current()->removeInterruptor(m_interruptor.get()); | |
103 | |
104 m_world->dispose(); | |
105 | |
106 // The corresponding call to didStartWorkerRunLoop is in | |
107 // WorkerThread::workerThread(). | |
108 // See http://webkit.org/b/83104#c14 for why this is here. | |
109 blink::Platform::current()->didStopWorkerRunLoop(blink::WebWorkerRunLoop(&m_
workerGlobalScope.thread()->runLoop())); | |
110 | |
111 if (isContextInitialized()) | |
112 m_scriptState->disposePerContextData(); | |
113 | |
114 ThreadState::current()->addCleanupTask(IsolateCleanupTask::create(m_isolate)
); | |
115 } | |
116 | |
117 bool WorkerScriptController::initializeContextIfNeeded() | |
118 { | |
119 v8::HandleScope handleScope(m_isolate); | |
120 | |
121 if (isContextInitialized()) | |
122 return true; | |
123 | |
124 v8::Handle<v8::Context> context = v8::Context::New(m_isolate); | |
125 if (context.IsEmpty()) | |
126 return false; | |
127 | |
128 m_scriptState = ScriptState::create(context, m_world); | |
129 | |
130 ScriptState::Scope scope(m_scriptState.get()); | |
131 | |
132 // Set DebugId for the new context. | |
133 context->SetEmbedderData(0, v8AtomicString(m_isolate, "worker")); | |
134 | |
135 // Create a new JS object and use it as the prototype for the shadow global
object. | |
136 const WrapperTypeInfo* contextType = &V8DedicatedWorkerGlobalScope::wrapperT
ypeInfo; | |
137 if (m_workerGlobalScope.isServiceWorkerGlobalScope()) | |
138 contextType = &V8ServiceWorkerGlobalScope::wrapperTypeInfo; | |
139 else if (!m_workerGlobalScope.isDedicatedWorkerGlobalScope()) | |
140 contextType = &V8SharedWorkerGlobalScope::wrapperTypeInfo; | |
141 v8::Handle<v8::Function> workerGlobalScopeConstructor = m_scriptState->perCo
ntextData()->constructorForType(contextType); | |
142 v8::Local<v8::Object> jsWorkerGlobalScope = V8ObjectConstructor::newInstance
(m_isolate, workerGlobalScopeConstructor); | |
143 if (jsWorkerGlobalScope.IsEmpty()) { | |
144 m_scriptState->disposePerContextData(); | |
145 return false; | |
146 } | |
147 | |
148 V8DOMWrapper::associateObjectWithWrapper<V8WorkerGlobalScope>(PassRefPtrWill
BeRawPtr<WorkerGlobalScope>(&m_workerGlobalScope), contextType, jsWorkerGlobalSc
ope, m_isolate, WrapperConfiguration::Dependent); | |
149 | |
150 // Insert the object instance as the prototype of the shadow object. | |
151 v8::Handle<v8::Object> globalObject = v8::Handle<v8::Object>::Cast(m_scriptS
tate->context()->Global()->GetPrototype()); | |
152 globalObject->SetPrototype(jsWorkerGlobalScope); | |
153 | |
154 return true; | |
155 } | |
156 | |
157 ScriptValue WorkerScriptController::evaluate(const String& script, const String&
fileName, const TextPosition& scriptStartPosition, WorkerGlobalScopeExecutionSt
ate* state) | |
158 { | |
159 if (!initializeContextIfNeeded()) | |
160 return ScriptValue(); | |
161 | |
162 ScriptState::Scope scope(m_scriptState.get()); | |
163 | |
164 if (!m_disableEvalPending.isEmpty()) { | |
165 m_scriptState->context()->AllowCodeGenerationFromStrings(false); | |
166 m_scriptState->context()->SetErrorMessageForCodeGenerationFromStrings(v8
String(m_isolate, m_disableEvalPending)); | |
167 m_disableEvalPending = String(); | |
168 } | |
169 | |
170 v8::TryCatch block; | |
171 | |
172 v8::Handle<v8::String> scriptString = v8String(m_isolate, script); | |
173 v8::Handle<v8::Script> compiledScript = V8ScriptRunner::compileScript(script
String, fileName, scriptStartPosition, 0, m_isolate); | |
174 v8::Local<v8::Value> result = V8ScriptRunner::runCompiledScript(compiledScri
pt, &m_workerGlobalScope, m_isolate); | |
175 | |
176 if (!block.CanContinue()) { | |
177 m_workerGlobalScope.script()->forbidExecution(); | |
178 return ScriptValue(); | |
179 } | |
180 | |
181 if (block.HasCaught()) { | |
182 v8::Local<v8::Message> message = block.Message(); | |
183 state->hadException = true; | |
184 state->errorMessage = toCoreString(message->Get()); | |
185 state->lineNumber = message->GetLineNumber(); | |
186 state->columnNumber = message->GetStartColumn() + 1; | |
187 TOSTRING_DEFAULT(V8StringResource<>, sourceURL, message->GetScriptOrigin
().ResourceName(), ScriptValue()); | |
188 state->sourceURL = sourceURL; | |
189 state->exception = ScriptValue(m_scriptState.get(), block.Exception()); | |
190 block.Reset(); | |
191 } else | |
192 state->hadException = false; | |
193 | |
194 if (result.IsEmpty() || result->IsUndefined()) | |
195 return ScriptValue(); | |
196 | |
197 return ScriptValue(m_scriptState.get(), result); | |
198 } | |
199 | |
200 void WorkerScriptController::evaluate(const ScriptSourceCode& sourceCode, RefPtr
WillBeRawPtr<ErrorEvent>* errorEvent) | |
201 { | |
202 if (isExecutionForbidden()) | |
203 return; | |
204 | |
205 WorkerGlobalScopeExecutionState state; | |
206 evaluate(sourceCode.source(), sourceCode.url().string(), sourceCode.startPos
ition(), &state); | |
207 if (state.hadException) { | |
208 if (errorEvent) { | |
209 *errorEvent = m_workerGlobalScope.shouldSanitizeScriptError(state.so
urceURL, NotSharableCrossOrigin) ? | |
210 ErrorEvent::createSanitizedError(m_world.get()) : ErrorEvent::cr
eate(state.errorMessage, state.sourceURL, state.lineNumber, state.columnNumber,
m_world.get()); | |
211 V8ErrorHandler::storeExceptionOnErrorEventWrapper(errorEvent->get(),
state.exception.v8Value(), m_scriptState->context()->Global(), m_isolate); | |
212 } else { | |
213 ASSERT(!m_workerGlobalScope.shouldSanitizeScriptError(state.sourceUR
L, NotSharableCrossOrigin)); | |
214 RefPtrWillBeRawPtr<ErrorEvent> event = nullptr; | |
215 if (m_errorEventFromImportedScript) { | |
216 event = m_errorEventFromImportedScript.release(); | |
217 } else { | |
218 event = ErrorEvent::create(state.errorMessage, state.sourceURL,
state.lineNumber, state.columnNumber, m_world.get()); | |
219 } | |
220 m_workerGlobalScope.reportException(event, nullptr, NotSharableCross
Origin); | |
221 } | |
222 } | |
223 } | |
224 | |
225 void WorkerScriptController::scheduleExecutionTermination() | |
226 { | |
227 // The mutex provides a memory barrier to ensure that once | |
228 // termination is scheduled, isExecutionTerminating will | |
229 // accurately reflect that state when called from another thread. | |
230 { | |
231 MutexLocker locker(m_scheduledTerminationMutex); | |
232 m_executionScheduledToTerminate = true; | |
233 } | |
234 v8::V8::TerminateExecution(m_isolate); | |
235 } | |
236 | |
237 bool WorkerScriptController::isExecutionTerminating() const | |
238 { | |
239 // See comments in scheduleExecutionTermination regarding mutex usage. | |
240 MutexLocker locker(m_scheduledTerminationMutex); | |
241 return m_executionScheduledToTerminate; | |
242 } | |
243 | |
244 void WorkerScriptController::forbidExecution() | |
245 { | |
246 ASSERT(m_workerGlobalScope.isContextThread()); | |
247 m_executionForbidden = true; | |
248 } | |
249 | |
250 bool WorkerScriptController::isExecutionForbidden() const | |
251 { | |
252 ASSERT(m_workerGlobalScope.isContextThread()); | |
253 return m_executionForbidden; | |
254 } | |
255 | |
256 void WorkerScriptController::disableEval(const String& errorMessage) | |
257 { | |
258 m_disableEvalPending = errorMessage; | |
259 } | |
260 | |
261 void WorkerScriptController::rethrowExceptionFromImportedScript(PassRefPtrWillBe
RawPtr<ErrorEvent> errorEvent) | |
262 { | |
263 m_errorEventFromImportedScript = errorEvent; | |
264 throwError(V8ThrowException::createError(v8GeneralError, m_errorEventFromImp
ortedScript->message(), m_isolate), m_isolate); | |
265 } | |
266 | |
267 } // namespace WebCore | |
OLD | NEW |