1 /* 2 * Copyright (C) 2009, 2011 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 32 #include "config.h" 33 34 #if ENABLE(WORKERS) 35 36 #include "WorkerContextExecutionProxy.h" 37 38 #include "DedicatedWorkerContext.h" 39 #include "Event.h" 40 #include "ScriptCallStack.h" 41 #include "SharedWorker.h" 42 #include "SharedWorkerContext.h" 43 #include "V8Binding.h" 44 #include "V8DOMMap.h" 45 #include "V8DedicatedWorkerContext.h" 46 #include "V8Proxy.h" 47 #include "V8SharedWorkerContext.h" 48 #include "Worker.h" 49 #include "WorkerContext.h" 50 #include "WorkerScriptController.h" 51 #include "WrapperTypeInfo.h" 52 #include <wtf/text/CString.h> 53 54 namespace WebCore { 55 56 static void reportFatalErrorInV8(const char* location, const char* message) 57 { 58 // FIXME: We temporarily deal with V8 internal error situations such as out-of-memory by crashing the worker. 59 CRASH(); 60 } 61 62 static void v8MessageHandler(v8::Handle<v8::Message> message, v8::Handle<v8::Value> data) 63 { 64 static bool isReportingException = false; 65 // Exceptions that occur in error handler should be ignored since in that case 66 // WorkerContext::reportException will send the exception to the worker object. 67 if (isReportingException) 68 return; 69 isReportingException = true; 70 71 // During the frame teardown, there may not be a valid context. 72 if (ScriptExecutionContext* context = getScriptExecutionContext()) { 73 String errorMessage = toWebCoreString(message->Get()); 74 int lineNumber = message->GetLineNumber(); 75 String sourceURL = toWebCoreString(message->GetScriptResourceName()); 76 context->reportException(errorMessage, lineNumber, sourceURL, 0); 77 } 78 79 isReportingException = false; 80 } 81 82 WorkerContextExecutionProxy::WorkerContextExecutionProxy(WorkerContext* workerContext) 83 : m_workerContext(workerContext) 84 , m_recursion(0) 85 { 86 initV8IfNeeded(); 87 } 88 89 WorkerContextExecutionProxy::~WorkerContextExecutionProxy() 90 { 91 dispose(); 92 } 93 94 void WorkerContextExecutionProxy::dispose() 95 { 96 // Detach all events from their JS wrappers. 97 for (size_t eventIndex = 0; eventIndex < m_events.size(); ++eventIndex) { 98 Event* event = m_events[eventIndex]; 99 if (forgetV8EventObject(event)) 100 event->deref(); 101 } 102 m_events.clear(); 103 104 // Dispose the context. 105 if (!m_context.IsEmpty()) { 106 m_context.Dispose(); 107 m_context.Clear(); 108 } 109 } 110 111 void WorkerContextExecutionProxy::initV8IfNeeded() 112 { 113 static bool v8Initialized = false; 114 115 if (v8Initialized) 116 return; 117 118 // Tell V8 not to call the default OOM handler, binding code will handle it. 119 v8::V8::IgnoreOutOfMemoryException(); 120 v8::V8::SetFatalErrorHandler(reportFatalErrorInV8); 121 122 v8::ResourceConstraints resource_constraints; 123 uint32_t here; 124 resource_constraints.set_stack_limit(&here - kWorkerMaxStackSize / sizeof(uint32_t*)); 125 v8::SetResourceConstraints(&resource_constraints); 126 127 v8Initialized = true; 128 } 129 130 bool WorkerContextExecutionProxy::initContextIfNeeded() 131 { 132 // Bail out if the context has already been initialized. 133 if (!m_context.IsEmpty()) 134 return true; 135 136 // Setup the security handlers and message listener. This only has 137 // to be done once. 138 static bool isV8Initialized = false; 139 if (!isV8Initialized) 140 v8::V8::AddMessageListener(&v8MessageHandler); 141 142 // Create a new environment 143 v8::Persistent<v8::ObjectTemplate> globalTemplate; 144 m_context = v8::Context::New(0, globalTemplate); 145 if (m_context.IsEmpty()) 146 return false; 147 148 // Starting from now, use local context only. 149 v8::Local<v8::Context> context = v8::Local<v8::Context>::New(m_context); 150 151 v8::Context::Scope scope(context); 152 153 // Set DebugId for the new context. 154 context->SetData(v8::String::New("worker")); 155 156 // Create a new JS object and use it as the prototype for the shadow global object. 157 WrapperTypeInfo* contextType = &V8DedicatedWorkerContext::info; 158 #if ENABLE(SHARED_WORKERS) 159 if (!m_workerContext->isDedicatedWorkerContext()) 160 contextType = &V8SharedWorkerContext::info; 161 #endif 162 v8::Handle<v8::Function> workerContextConstructor = V8DOMWrapper::getConstructorForContext(contextType, context); 163 v8::Local<v8::Object> jsWorkerContext = SafeAllocation::newInstance(workerContextConstructor); 164 // Bail out if allocation failed. 165 if (jsWorkerContext.IsEmpty()) { 166 dispose(); 167 return false; 168 } 169 170 // Wrap the object. 171 V8DOMWrapper::setDOMWrapper(jsWorkerContext, contextType, m_workerContext); 172 173 V8DOMWrapper::setJSWrapperForDOMObject(m_workerContext, v8::Persistent<v8::Object>::New(jsWorkerContext)); 174 m_workerContext->ref(); 175 176 // Insert the object instance as the prototype of the shadow object. 177 v8::Handle<v8::Object> globalObject = v8::Handle<v8::Object>::Cast(m_context->Global()->GetPrototype()); 178 globalObject->SetPrototype(jsWorkerContext); 179 return true; 180 } 181 182 bool WorkerContextExecutionProxy::forgetV8EventObject(Event* event) 183 { 184 if (getDOMObjectMap().contains(event)) { 185 getDOMObjectMap().forget(event); 186 return true; 187 } 188 return false; 189 } 190 191 ScriptValue WorkerContextExecutionProxy::evaluate(const String& script, const String& fileName, const TextPosition0& scriptStartPosition, WorkerContextExecutionState* state) 192 { 193 v8::HandleScope hs; 194 195 if (!initContextIfNeeded()) 196 return ScriptValue(); 197 198 v8::Context::Scope scope(m_context); 199 200 v8::TryCatch exceptionCatcher; 201 202 v8::Local<v8::String> scriptString = v8ExternalString(script); 203 v8::Handle<v8::Script> compiledScript = V8Proxy::compileScript(scriptString, fileName, scriptStartPosition); 204 v8::Local<v8::Value> result = runScript(compiledScript); 205 206 if (!exceptionCatcher.CanContinue()) { 207 m_workerContext->script()->forbidExecution(); 208 return ScriptValue(); 209 } 210 211 if (exceptionCatcher.HasCaught()) { 212 v8::Local<v8::Message> message = exceptionCatcher.Message(); 213 state->hadException = true; 214 state->errorMessage = toWebCoreString(message->Get()); 215 state->lineNumber = message->GetLineNumber(); 216 state->sourceURL = toWebCoreString(message->GetScriptResourceName()); 217 if (m_workerContext->sanitizeScriptError(state->errorMessage, state->lineNumber, state->sourceURL)) 218 state->exception = V8Proxy::throwError(V8Proxy::GeneralError, state->errorMessage.utf8().data()); 219 else 220 state->exception = ScriptValue(exceptionCatcher.Exception()); 221 222 exceptionCatcher.Reset(); 223 } else 224 state->hadException = false; 225 226 if (result.IsEmpty() || result->IsUndefined()) 227 return ScriptValue(); 228 229 return ScriptValue(result); 230 } 231 232 v8::Local<v8::Value> WorkerContextExecutionProxy::runScript(v8::Handle<v8::Script> script) 233 { 234 if (script.IsEmpty()) 235 return v8::Local<v8::Value>(); 236 237 // Compute the source string and prevent against infinite recursion. 238 if (m_recursion >= kMaxRecursionDepth) { 239 v8::Local<v8::String> code = v8ExternalString("throw RangeError('Recursion too deep')"); 240 script = V8Proxy::compileScript(code, "", TextPosition0::minimumPosition()); 241 } 242 243 if (V8Proxy::handleOutOfMemory()) 244 ASSERT(script.IsEmpty()); 245 246 if (script.IsEmpty()) 247 return v8::Local<v8::Value>(); 248 249 // Run the script and keep track of the current recursion depth. 250 v8::Local<v8::Value> result; 251 { 252 m_recursion++; 253 result = script->Run(); 254 m_recursion--; 255 } 256 257 // Handle V8 internal error situation (Out-of-memory). 258 if (result.IsEmpty()) 259 return v8::Local<v8::Value>(); 260 261 return result; 262 } 263 264 void WorkerContextExecutionProxy::trackEvent(Event* event) 265 { 266 m_events.append(event); 267 } 268 269 } // namespace WebCore 270 271 #endif // ENABLE(WORKERS) 272