Home | History | Annotate | Download | only in v8
      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 "V8SharedWorkerGlobalScope.h"
     37 #include "V8WorkerGlobalScope.h"
     38 #include "bindings/v8/ScriptSourceCode.h"
     39 #include "bindings/v8/ScriptValue.h"
     40 #include "bindings/v8/V8GCController.h"
     41 #include "bindings/v8/V8Initializer.h"
     42 #include "bindings/v8/V8ObjectConstructor.h"
     43 #include "bindings/v8/V8ScriptRunner.h"
     44 #include "bindings/v8/WrapperTypeInfo.h"
     45 #include "core/inspector/ScriptCallStack.h"
     46 #include "core/page/DOMTimer.h"
     47 #include "core/workers/WorkerGlobalScope.h"
     48 #include "core/workers/WorkerObjectProxy.h"
     49 #include "core/workers/WorkerThread.h"
     50 #include <v8.h>
     51 
     52 #include "public/platform/Platform.h"
     53 #include "public/platform/WebWorkerRunLoop.h"
     54 
     55 namespace WebCore {
     56 
     57 WorkerScriptController::WorkerScriptController(WorkerGlobalScope* workerGlobalScope)
     58     : m_workerGlobalScope(workerGlobalScope)
     59     , m_isolate(v8::Isolate::New())
     60     , m_executionForbidden(false)
     61     , m_executionScheduledToTerminate(false)
     62 {
     63     m_isolate->Enter();
     64     v8::V8::Initialize();
     65     V8PerIsolateData* data = V8PerIsolateData::create(m_isolate);
     66     m_domDataStore = adoptPtr(new DOMDataStore(WorkerWorld));
     67     data->setWorkerDOMDataStore(m_domDataStore.get());
     68 
     69     V8Initializer::initializeWorker(m_isolate);
     70 }
     71 
     72 WorkerScriptController::~WorkerScriptController()
     73 {
     74     m_domDataStore.clear();
     75 
     76     // The corresponding call to didStartWorkerRunLoop is in
     77     // WorkerThread::workerThread().
     78     // See http://webkit.org/b/83104#c14 for why this is here.
     79     WebKit::Platform::current()->didStopWorkerRunLoop(WebKit::WebWorkerRunLoop(&m_workerGlobalScope->thread()->runLoop()));
     80 
     81     disposeContext();
     82     V8PerIsolateData::dispose(m_isolate);
     83     m_isolate->Exit();
     84     m_isolate->Dispose();
     85 }
     86 
     87 void WorkerScriptController::disposeContext()
     88 {
     89     m_perContextData.clear();
     90     m_context.clear();
     91 }
     92 
     93 bool WorkerScriptController::initializeContextIfNeeded()
     94 {
     95     if (!m_context.isEmpty())
     96         return true;
     97 
     98     m_context.set(m_isolate, v8::Context::New(m_isolate));
     99     if (m_context.isEmpty())
    100         return false;
    101 
    102     // Starting from now, use local context only.
    103     v8::Local<v8::Context> context = m_context.newLocal(m_isolate);
    104 
    105     v8::Context::Scope scope(context);
    106 
    107     m_perContextData = V8PerContextData::create(context);
    108     if (!m_perContextData->init()) {
    109         disposeContext();
    110         return false;
    111     }
    112 
    113     // Set DebugId for the new context.
    114     context->SetEmbedderData(0, v8::String::NewSymbol("worker"));
    115 
    116     // Create a new JS object and use it as the prototype for the shadow global object.
    117     WrapperTypeInfo* contextType = &V8DedicatedWorkerGlobalScope::info;
    118     if (!m_workerGlobalScope->isDedicatedWorkerGlobalScope())
    119         contextType = &V8SharedWorkerGlobalScope::info;
    120     v8::Handle<v8::Function> workerGlobalScopeConstructor = m_perContextData->constructorForType(contextType);
    121     v8::Local<v8::Object> jsWorkerGlobalScope = V8ObjectConstructor::newInstance(workerGlobalScopeConstructor);
    122     if (jsWorkerGlobalScope.IsEmpty()) {
    123         disposeContext();
    124         return false;
    125     }
    126 
    127     V8DOMWrapper::associateObjectWithWrapper<V8WorkerGlobalScope>(PassRefPtr<WorkerGlobalScope>(m_workerGlobalScope), contextType, jsWorkerGlobalScope, m_isolate, WrapperConfiguration::Dependent);
    128 
    129     // Insert the object instance as the prototype of the shadow object.
    130     v8::Handle<v8::Object> globalObject = v8::Handle<v8::Object>::Cast(m_context.newLocal(m_isolate)->Global()->GetPrototype());
    131     globalObject->SetPrototype(jsWorkerGlobalScope);
    132 
    133     return true;
    134 }
    135 
    136 ScriptValue WorkerScriptController::evaluate(const String& script, const String& fileName, const TextPosition& scriptStartPosition, WorkerGlobalScopeExecutionState* state)
    137 {
    138     v8::HandleScope handleScope(m_isolate);
    139 
    140     if (!initializeContextIfNeeded())
    141         return ScriptValue();
    142 
    143     v8::Handle<v8::Context> context = m_context.newLocal(m_isolate);
    144     if (!m_disableEvalPending.isEmpty()) {
    145         context->AllowCodeGenerationFromStrings(false);
    146         context->SetErrorMessageForCodeGenerationFromStrings(v8String(m_disableEvalPending, m_isolate));
    147         m_disableEvalPending = String();
    148     }
    149 
    150     v8::Context::Scope scope(context);
    151 
    152     v8::TryCatch block;
    153 
    154     v8::Handle<v8::String> scriptString = v8String(script, m_isolate);
    155     v8::Handle<v8::Script> compiledScript = V8ScriptRunner::compileScript(scriptString, fileName, scriptStartPosition, 0, m_isolate);
    156     v8::Local<v8::Value> result = V8ScriptRunner::runCompiledScript(compiledScript, m_workerGlobalScope);
    157 
    158     if (!block.CanContinue()) {
    159         m_workerGlobalScope->script()->forbidExecution();
    160         return ScriptValue();
    161     }
    162 
    163     if (block.HasCaught()) {
    164         v8::Local<v8::Message> message = block.Message();
    165         state->hadException = true;
    166         state->errorMessage = toWebCoreString(message->Get());
    167         state->lineNumber = message->GetLineNumber();
    168         state->columnNumber = message->GetStartColumn();
    169         state->sourceURL = toWebCoreString(message->GetScriptResourceName());
    170         block.Reset();
    171     } else
    172         state->hadException = false;
    173 
    174     if (result.IsEmpty() || result->IsUndefined())
    175         return ScriptValue();
    176 
    177     return ScriptValue(result);
    178 }
    179 
    180 void WorkerScriptController::evaluate(const ScriptSourceCode& sourceCode, RefPtr<ErrorEvent>* errorEvent)
    181 {
    182     if (isExecutionForbidden())
    183         return;
    184 
    185     WorkerGlobalScopeExecutionState state;
    186     evaluate(sourceCode.source(), sourceCode.url().string(), sourceCode.startPosition(), &state);
    187     if (state.hadException) {
    188         if (errorEvent) {
    189             *errorEvent = m_workerGlobalScope->shouldSanitizeScriptError(state.sourceURL, NotSharableCrossOrigin) ?
    190                 ErrorEvent::createSanitizedError() : ErrorEvent::create(state.errorMessage, state.sourceURL, state.lineNumber, state.columnNumber);
    191         } else {
    192             ASSERT(!m_workerGlobalScope->shouldSanitizeScriptError(state.sourceURL, NotSharableCrossOrigin));
    193             RefPtr<ErrorEvent> event = m_errorEventFromImportedScript ? m_errorEventFromImportedScript.release() : ErrorEvent::create(state.errorMessage, state.sourceURL, state.lineNumber, state.columnNumber);
    194             m_errorEventFromImportedScript.clear();
    195             m_workerGlobalScope->reportException(event, 0, NotSharableCrossOrigin);
    196         }
    197     }
    198 }
    199 
    200 void WorkerScriptController::scheduleExecutionTermination()
    201 {
    202     // The mutex provides a memory barrier to ensure that once
    203     // termination is scheduled, isExecutionTerminating will
    204     // accurately reflect that state when called from another thread.
    205     {
    206         MutexLocker locker(m_scheduledTerminationMutex);
    207         m_executionScheduledToTerminate = true;
    208     }
    209     v8::V8::TerminateExecution(m_isolate);
    210 }
    211 
    212 bool WorkerScriptController::isExecutionTerminating() const
    213 {
    214     // See comments in scheduleExecutionTermination regarding mutex usage.
    215     MutexLocker locker(m_scheduledTerminationMutex);
    216     return m_executionScheduledToTerminate;
    217 }
    218 
    219 void WorkerScriptController::forbidExecution()
    220 {
    221     ASSERT(m_workerGlobalScope->isContextThread());
    222     m_executionForbidden = true;
    223 }
    224 
    225 bool WorkerScriptController::isExecutionForbidden() const
    226 {
    227     ASSERT(m_workerGlobalScope->isContextThread());
    228     return m_executionForbidden;
    229 }
    230 
    231 void WorkerScriptController::disableEval(const String& errorMessage)
    232 {
    233     m_disableEvalPending = errorMessage;
    234 }
    235 
    236 void WorkerScriptController::rethrowExceptionFromImportedScript(PassRefPtr<ErrorEvent> errorEvent)
    237 {
    238     m_errorEventFromImportedScript = errorEvent;
    239     throwError(V8ThrowException::createError(v8GeneralError, m_errorEventFromImportedScript->message(), m_isolate));
    240 }
    241 
    242 WorkerScriptController* WorkerScriptController::controllerForContext()
    243 {
    244     // Happens on frame destruction, check otherwise GetCurrent() will crash.
    245     if (!v8::Context::InContext())
    246         return 0;
    247     v8::Handle<v8::Context> context = v8::Context::GetCurrent();
    248     v8::Handle<v8::Object> global = context->Global();
    249     global = global->FindInstanceInPrototypeChain(V8WorkerGlobalScope::GetTemplate(context->GetIsolate(), WorkerWorld));
    250     // Return 0 if the current executing context is not the worker context.
    251     if (global.IsEmpty())
    252         return 0;
    253     WorkerGlobalScope* workerGlobalScope = V8WorkerGlobalScope::toNative(global);
    254     return workerGlobalScope->script();
    255 }
    256 
    257 } // namespace WebCore
    258