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