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 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 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 92 void WorkerScriptController::disposeContext() 93 { 94 m_perContextData.clear(); 95 m_contextHolder.clear(); 96 } 97 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 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 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 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 223 bool WorkerScriptController::isExecutionTerminating() const 224 { 225 // See comments in scheduleExecutionTermination regarding mutex usage. 226 MutexLocker locker(m_scheduledTerminationMutex); 227 return m_executionScheduledToTerminate; 228 } 229 230 void WorkerScriptController::forbidExecution() 231 { 232 ASSERT(m_workerGlobalScope.isContextThread()); 233 m_executionForbidden = true; 234 } 235 236 bool WorkerScriptController::isExecutionForbidden() const 237 { 238 ASSERT(m_workerGlobalScope.isContextThread()); 239 return m_executionForbidden; 240 } 241 242 void WorkerScriptController::disableEval(const String& errorMessage) 243 { 244 m_disableEvalPending = errorMessage; 245 } 246 247 void WorkerScriptController::rethrowExceptionFromImportedScript(PassRefPtr<ErrorEvent> errorEvent) 248 { 249 m_errorEventFromImportedScript = errorEvent; 250 throwError(V8ThrowException::createError(v8GeneralError, m_errorEventFromImportedScript->message(), isolate()), isolate()); 251 } 252 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