1 /* 2 * Copyright (C) 2009 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 "DOMCoreException.h" 39 #include "DedicatedWorkerContext.h" 40 #include "Event.h" 41 #include "EventSource.h" 42 #include "Notification.h" 43 #include "NotificationCenter.h" 44 #include "EventException.h" 45 #include "MessagePort.h" 46 #include "RangeException.h" 47 #include "SharedWorker.h" 48 #include "SharedWorkerContext.h" 49 #include "V8Binding.h" 50 #include "V8DOMMap.h" 51 #include "V8Index.h" 52 #include "V8Proxy.h" 53 #include "V8WorkerContext.h" 54 #include "V8WorkerContextEventListener.h" 55 #if ENABLE(WEB_SOCKETS) 56 #include "WebSocket.h" 57 #endif 58 #include "Worker.h" 59 #include "WorkerContext.h" 60 #include "WorkerLocation.h" 61 #include "WorkerNavigator.h" 62 #include "WorkerScriptController.h" 63 #include "XMLHttpRequest.h" 64 #include "XMLHttpRequestException.h" 65 66 namespace WebCore { 67 68 static void reportFatalErrorInV8(const char* location, const char* message) 69 { 70 // FIXME: We temporarily deal with V8 internal error situations such as out-of-memory by crashing the worker. 71 CRASH(); 72 } 73 74 WorkerContextExecutionProxy::WorkerContextExecutionProxy(WorkerContext* workerContext) 75 : m_workerContext(workerContext) 76 , m_recursion(0) 77 { 78 initV8IfNeeded(); 79 } 80 81 WorkerContextExecutionProxy::~WorkerContextExecutionProxy() 82 { 83 dispose(); 84 } 85 86 void WorkerContextExecutionProxy::dispose() 87 { 88 // Detach all events from their JS wrappers. 89 for (size_t eventIndex = 0; eventIndex < m_events.size(); ++eventIndex) { 90 Event* event = m_events[eventIndex]; 91 if (forgetV8EventObject(event)) 92 event->deref(); 93 } 94 m_events.clear(); 95 96 // Dispose the context. 97 if (!m_context.IsEmpty()) { 98 m_context.Dispose(); 99 m_context.Clear(); 100 } 101 } 102 103 WorkerContextExecutionProxy* WorkerContextExecutionProxy::retrieve() 104 { 105 // Happens on frame destruction, check otherwise GetCurrent() will crash. 106 if (!v8::Context::InContext()) 107 return 0; 108 v8::Handle<v8::Context> context = v8::Context::GetCurrent(); 109 v8::Handle<v8::Object> global = context->Global(); 110 global = V8DOMWrapper::lookupDOMWrapper(V8WorkerContext::GetTemplate(), global); 111 // Return 0 if the current executing context is not the worker context. 112 if (global.IsEmpty()) 113 return 0; 114 WorkerContext* workerContext = V8WorkerContext::toNative(global); 115 return workerContext->script()->proxy(); 116 } 117 118 void WorkerContextExecutionProxy::initV8IfNeeded() 119 { 120 static bool v8Initialized = false; 121 122 if (v8Initialized) 123 return; 124 125 // Tell V8 not to call the default OOM handler, binding code will handle it. 126 v8::V8::IgnoreOutOfMemoryException(); 127 v8::V8::SetFatalErrorHandler(reportFatalErrorInV8); 128 129 v8::ResourceConstraints resource_constraints; 130 uint32_t here; 131 resource_constraints.set_stack_limit(&here - kWorkerMaxStackSize / sizeof(uint32_t*)); 132 v8::SetResourceConstraints(&resource_constraints); 133 134 v8Initialized = true; 135 } 136 137 void WorkerContextExecutionProxy::initContextIfNeeded() 138 { 139 // Bail out if the context has already been initialized. 140 if (!m_context.IsEmpty()) 141 return; 142 143 // Create a new environment 144 v8::Persistent<v8::ObjectTemplate> globalTemplate; 145 m_context = v8::Context::New(0, globalTemplate); 146 147 // Starting from now, use local context only. 148 v8::Local<v8::Context> context = v8::Local<v8::Context>::New(m_context); 149 v8::Context::Scope scope(context); 150 151 // Allocate strings used during initialization. 152 v8::Handle<v8::String> implicitProtoString = v8::String::New("__proto__"); 153 154 // Create a new JS object and use it as the prototype for the shadow global object. 155 V8ClassIndex::V8WrapperType contextType = V8ClassIndex::DEDICATEDWORKERCONTEXT; 156 #if ENABLE(SHARED_WORKERS) 157 if (!m_workerContext->isDedicatedWorkerContext()) 158 contextType = V8ClassIndex::SHAREDWORKERCONTEXT; 159 #endif 160 v8::Handle<v8::Function> workerContextConstructor = V8DOMWrapper::getConstructorForContext(contextType, context); 161 v8::Local<v8::Object> jsWorkerContext = SafeAllocation::newInstance(workerContextConstructor); 162 // Bail out if allocation failed. 163 if (jsWorkerContext.IsEmpty()) { 164 dispose(); 165 return; 166 } 167 168 // Wrap the object. 169 V8DOMWrapper::setDOMWrapper(jsWorkerContext, V8ClassIndex::ToInt(contextType), m_workerContext); 170 171 V8DOMWrapper::setJSWrapperForDOMObject(m_workerContext, v8::Persistent<v8::Object>::New(jsWorkerContext)); 172 m_workerContext->ref(); 173 174 // Insert the object instance as the prototype of the shadow object. 175 v8::Handle<v8::Object> globalObject = m_context->Global(); 176 globalObject->Set(implicitProtoString, jsWorkerContext); 177 } 178 179 bool WorkerContextExecutionProxy::forgetV8EventObject(Event* event) 180 { 181 if (getDOMObjectMap().contains(event)) { 182 getDOMObjectMap().forget(event); 183 return true; 184 } 185 return false; 186 } 187 188 ScriptValue WorkerContextExecutionProxy::evaluate(const String& script, const String& fileName, int baseLine, WorkerContextExecutionState* state) 189 { 190 v8::HandleScope hs; 191 192 initContextIfNeeded(); 193 v8::Context::Scope scope(m_context); 194 195 v8::TryCatch exceptionCatcher; 196 197 v8::Local<v8::String> scriptString = v8ExternalString(script); 198 v8::Handle<v8::Script> compiledScript = V8Proxy::compileScript(scriptString, fileName, baseLine); 199 v8::Local<v8::Value> result = runScript(compiledScript); 200 201 if (exceptionCatcher.HasCaught()) { 202 v8::Local<v8::Message> message = exceptionCatcher.Message(); 203 state->hadException = true; 204 state->exception = ScriptValue(exceptionCatcher.Exception()); 205 state->errorMessage = toWebCoreString(message->Get()); 206 state->lineNumber = message->GetLineNumber(); 207 state->sourceURL = toWebCoreString(message->GetScriptResourceName()); 208 exceptionCatcher.Reset(); 209 } else 210 state->hadException = false; 211 212 if (result.IsEmpty() || result->IsUndefined()) 213 return ScriptValue(); 214 215 return ScriptValue(result); 216 } 217 218 v8::Local<v8::Value> WorkerContextExecutionProxy::runScript(v8::Handle<v8::Script> script) 219 { 220 if (script.IsEmpty()) 221 return v8::Local<v8::Value>(); 222 223 // Compute the source string and prevent against infinite recursion. 224 if (m_recursion >= kMaxRecursionDepth) { 225 v8::Local<v8::String> code = v8ExternalString("throw RangeError('Recursion too deep')"); 226 script = V8Proxy::compileScript(code, "", 0); 227 } 228 229 if (V8Proxy::handleOutOfMemory()) 230 ASSERT(script.IsEmpty()); 231 232 if (script.IsEmpty()) 233 return v8::Local<v8::Value>(); 234 235 // Run the script and keep track of the current recursion depth. 236 v8::Local<v8::Value> result; 237 { 238 m_recursion++; 239 result = script->Run(); 240 m_recursion--; 241 } 242 243 // Handle V8 internal error situation (Out-of-memory). 244 if (result.IsEmpty()) 245 return v8::Local<v8::Value>(); 246 247 return result; 248 } 249 250 PassRefPtr<V8EventListener> WorkerContextExecutionProxy::findOrCreateEventListener(v8::Local<v8::Value> object, bool isInline, bool findOnly) 251 { 252 return findOnly ? V8EventListenerList::findWrapper(object, isInline) : V8EventListenerList::findOrCreateWrapper<V8WorkerContextEventListener>(object, isInline); 253 } 254 255 void WorkerContextExecutionProxy::trackEvent(Event* event) 256 { 257 m_events.append(event); 258 } 259 260 } // namespace WebCore 261 262 #endif // ENABLE(WORKERS) 263