// Copyright (c) 2009, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "config.h" #if ENABLE(WORKERS) #include "WorkerContextExecutionProxy.h" #include "v8_binding.h" #include "v8_index.h" #include "v8_proxy.h" #include "Event.h" #include "V8WorkerContextEventListener.h" #include "WorkerContext.h" #include "WorkerLocation.h" #include "WorkerNavigator.h" #include "WorkerScriptController.h" namespace WebCore { WorkerContextExecutionProxy::WorkerContextExecutionProxy( WorkerContext* workerContext) : m_workerContext(workerContext), m_recursion(0) { } WorkerContextExecutionProxy::~WorkerContextExecutionProxy() { Dispose(); } void WorkerContextExecutionProxy::Dispose() { // Disconnect all event listeners. for (EventListenerList::iterator iter = m_listeners.begin(); iter != m_listeners.end(); ++iter) { static_cast(*iter)->disconnect(); } m_listeners.clear(); // Detach all events from their JS wrappers. for (EventSet::iterator iter = m_events.begin(); iter != m_events.end(); ++iter) { Event* event = *iter; if (ForgetV8EventObject(event)) { event->deref(); } } m_events.clear(); // Dispose the context. if (!m_context.IsEmpty()) { m_context.Dispose(); m_context.Clear(); } // Remove the wrapping between JS object and DOM object. This is because // the worker context object is going to be disposed immediately when a // worker thread is tearing down. We do not want to re-delete the real object // when JS object is garbage collected. v8::Locker locker; v8::HandleScope scope; v8::Persistent wrapper = GetDOMObjectMap().get(m_workerContext); if (!wrapper.IsEmpty()) V8Proxy::SetDOMWrapper(wrapper, V8ClassIndex::INVALID_CLASS_INDEX, NULL); GetDOMObjectMap().forget(m_workerContext); } WorkerContextExecutionProxy* WorkerContextExecutionProxy::retrieve() { v8::Handle context = v8::Context::GetCurrent(); v8::Handle global = context->Global(); global = V8Proxy::LookupDOMWrapper(V8ClassIndex::WORKERCONTEXT, global); ASSERT(!global.IsEmpty()); WorkerContext* worker_context = V8Proxy::ToNativeObject(V8ClassIndex::WORKERCONTEXT, global); return worker_context->script()->proxy(); } void WorkerContextExecutionProxy::InitContextIfNeeded() { // Bail out if the context has already been initialized. if (!m_context.IsEmpty()) { return; } // Create a new environment v8::Persistent global_template; m_context = v8::Context::New(NULL, global_template); // Starting from now, use local context only. v8::Local context = v8::Local::New(m_context); v8::Context::Scope scope(context); // Allocate strings used during initialization. v8::Handle implicit_proto_string = v8::String::New("__proto__"); // Create a new JS object and use it as the prototype for the // shadow global object. v8::Handle worker_context_constructor = GetConstructor(V8ClassIndex::WORKERCONTEXT); v8::Local js_worker_context = SafeAllocation::NewInstance(worker_context_constructor); // Bail out if allocation failed. if (js_worker_context.IsEmpty()) { Dispose(); return; } // Wrap the object. V8Proxy::SetDOMWrapper(js_worker_context, V8ClassIndex::ToInt(V8ClassIndex::WORKERCONTEXT), m_workerContext); V8Proxy::SetJSWrapperForDOMObject(m_workerContext, v8::Persistent::New(js_worker_context)); // Insert the object instance as the prototype of the shadow object. v8::Handle v8_global = m_context->Global(); v8_global->Set(implicit_proto_string, js_worker_context); } v8::Local WorkerContextExecutionProxy::GetConstructor( V8ClassIndex::V8WrapperType t) { // Enter the context of the proxy to make sure that the // function is constructed in the context corresponding to // this proxy. v8::Context::Scope scope(m_context); v8::Handle templ = V8Proxy::GetTemplate(t); // Getting the function might fail if we're running out of // stack or memory. v8::TryCatch try_catch; v8::Local value = templ->GetFunction(); if (value.IsEmpty()) { return v8::Local(); } return value; } v8::Handle WorkerContextExecutionProxy::ToV8Object( V8ClassIndex::V8WrapperType type, void* imp) { if (!imp) return v8::Null(); if (type == V8ClassIndex::WORKERCONTEXT) return WorkerContextToV8Object(static_cast(imp)); // Non DOM node v8::Persistent result = GetDOMObjectMap().get(imp); if (result.IsEmpty()) { v8::Local v8obj = InstantiateV8Object(type, type, imp); if (!v8obj.IsEmpty()) { switch (type) { case V8ClassIndex::WORKERLOCATION: static_cast(imp)->ref(); break; case V8ClassIndex::WORKERNAVIGATOR: static_cast(imp)->ref(); break; default: ASSERT(false); } result = v8::Persistent::New(v8obj); V8Proxy::SetJSWrapperForDOMObject(imp, result); } } return result; } v8::Handle WorkerContextExecutionProxy::EventToV8Object( Event* event) { if (!event) return v8::Null(); v8::Handle wrapper = GetDOMObjectMap().get(event); if (!wrapper.IsEmpty()) return wrapper; V8ClassIndex::V8WrapperType type = V8ClassIndex::EVENT; if (event->isMessageEvent()) type = V8ClassIndex::MESSAGEEVENT; v8::Handle result = InstantiateV8Object(type, V8ClassIndex::EVENT, event); if (result.IsEmpty()) { // Instantiation failed. Avoid updating the DOM object map and // return null which is already handled by callers of this function // in case the event is NULL. return v8::Null(); } event->ref(); // fast ref V8Proxy::SetJSWrapperForDOMObject( event, v8::Persistent::New(result)); return result; } // A JS object of type EventTarget in the worker context can only be // WorkerContext. v8::Handle WorkerContextExecutionProxy::EventTargetToV8Object( EventTarget* target) { if (!target) return v8::Null(); WorkerContext* worker_context = target->toWorkerContext(); if (worker_context) return WorkerContextToV8Object(worker_context); ASSERT(0); return v8::Handle(); } v8::Handle WorkerContextExecutionProxy::WorkerContextToV8Object( WorkerContext* worker_context) { if (!worker_context) return v8::Null(); v8::Handle context = worker_context->script()->proxy()->GetContext(); v8::Handle global = context->Global(); ASSERT(!global.IsEmpty()); return global; } v8::Local WorkerContextExecutionProxy::InstantiateV8Object( V8ClassIndex::V8WrapperType desc_type, V8ClassIndex::V8WrapperType cptr_type, void* imp) { v8::Local function; WorkerContextExecutionProxy* proxy = retrieve(); if (proxy) { function = proxy->GetConstructor(desc_type); } else { function = V8Proxy::GetTemplate(desc_type)->GetFunction(); } v8::Local instance = SafeAllocation::NewInstance(function); if (!instance.IsEmpty()) { // Avoid setting the DOM wrapper for failed allocations. V8Proxy::SetDOMWrapper(instance, V8ClassIndex::ToInt(cptr_type), imp); } return instance; } bool WorkerContextExecutionProxy::ForgetV8EventObject(Event* event) { if (GetDOMObjectMap().contains(event)) { GetDOMObjectMap().forget(event); return true; } else { return false; } } v8::Local WorkerContextExecutionProxy::Evaluate( const String& str, const String& file_name, int base_line) { v8::Locker locker; v8::HandleScope hs; InitContextIfNeeded(); v8::Context::Scope scope(m_context); v8::Local code = v8ExternalString(str); v8::Handle script = V8Proxy::CompileScript(code, file_name, base_line); return RunScript(script); } v8::Local WorkerContextExecutionProxy::RunScript( v8::Handle script) { if (script.IsEmpty()) return v8::Local(); // Compute the source string and prevent against infinite recursion. if (m_recursion >= kMaxRecursionDepth) { v8::Local code = v8ExternalString("throw RangeError('Recursion too deep')"); script = V8Proxy::CompileScript(code, "", 0); } if (V8Proxy::HandleOutOfMemory()) ASSERT(script.IsEmpty()); if (script.IsEmpty()) return v8::Local(); // Run the script and keep track of the current recursion depth. v8::Local result; { m_recursion++; result = script->Run(); m_recursion--; } // Handle V8 internal error situation (Out-of-memory). if (result.IsEmpty()) return v8::Local(); return result; } PassRefPtr WorkerContextExecutionProxy::FindOrCreateEventListener( v8::Local obj, bool is_inline, bool find_only) { if (!obj->IsObject()) return 0; for (EventListenerList::iterator iter = m_listeners.begin(); iter != m_listeners.end(); ++iter) { V8EventListener* el = *iter; if (el->isInline() == is_inline && el->getListenerObject() == obj) return el; } if (find_only) return NULL; // Create a new one, and add to cache. RefPtr listener = V8WorkerContextEventListener::create( this, v8::Local::Cast(obj), is_inline); m_listeners.push_back(listener.get()); return listener.release(); } void WorkerContextExecutionProxy::RemoveEventListener( V8EventListener* listener) { for (EventListenerList::iterator iter = m_listeners.begin(); iter != m_listeners.end(); ++iter) { if (*iter == listener) { m_listeners.erase(iter); return; } } } void WorkerContextExecutionProxy::TrackEvent(Event* event) { m_events.add(event); } } // namespace WebCore #endif // ENABLE(WORKERS)