V8DOMWindowShell* ScriptController::windowShell(DOMWrapperWorld* world) { ASSERT(world); V8DOMWindowShell* shell = 0; if (world->isMainWorld()) shell = m_windowShell.get(); else { IsolatedWorldMap::iterator iter = m_isolatedWorlds.find(world->worldId()); if (iter != m_isolatedWorlds.end()) shell = iter->value.get(); else { OwnPtr<V8DOMWindowShell> isolatedWorldShell = V8DOMWindowShell::create(m_frame, world, m_isolate); shell = isolatedWorldShell.get(); m_isolatedWorlds.set(world->worldId(), isolatedWorldShell.release()); } } if (!shell->isContextInitialized() && shell->initializeIfNeeded()) { if (world->isMainWorld()) { // FIXME: Remove this if clause. See comment with existingWindowShellWorkaroundWorld(). m_frame->loader()->dispatchDidClearWindowObjectInWorld(existingWindowShellWorkaroundWorld()); } else m_frame->loader()->dispatchDidClearWindowObjectInWorld(world); } return shell; }
void ScriptController::evaluateInIsolatedWorld(int worldID, const Vector<ScriptSourceCode>& sources, int extensionGroup, Vector<ScriptValue>* results) { // Except in the test runner, worldID should be non 0 as it conflicts with the mainWorldId. // FIXME: Change the test runner to perform this swap and make this an ASSERT. if (UNLIKELY(!worldID)) worldID = DOMWrapperWorld::uninitializedWorldId; v8::HandleScope handleScope; v8::Local<v8::Array> v8Results; { v8::HandleScope evaluateHandleScope; V8DOMWindowShell* isolatedWorldShell = ensureIsolatedWorldContext(worldID, extensionGroup); if (worldID != DOMWrapperWorld::uninitializedWorldId) { IsolatedWorldSecurityOriginMap::iterator securityOriginIter = m_isolatedWorldSecurityOrigins.find(worldID); if (securityOriginIter != m_isolatedWorldSecurityOrigins.end()) isolatedWorldShell->setIsolatedWorldSecurityOrigin(securityOriginIter->value); } isolatedWorldShell->initializeIfNeeded(); if (isolatedWorldShell->context().IsEmpty()) return; v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolatedWorldShell->context()); v8::Context::Scope contextScope(context); v8::Local<v8::Array> resultArray = v8::Array::New(sources.size()); for (size_t i = 0; i < sources.size(); ++i) { v8::Local<v8::Value> evaluationResult = compileAndRunScript(sources[i]); if (evaluationResult.IsEmpty()) evaluationResult = v8::Local<v8::Value>::New(v8::Undefined()); resultArray->Set(i, evaluationResult); } // Mark temporary shell for weak destruction. if (worldID == DOMWrapperWorld::uninitializedWorldId) { int actualWorldId = isolatedWorldShell->world()->worldId(); m_isolatedWorlds.remove(actualWorldId); isolatedWorldShell->destroyIsolatedShell(); } v8Results = evaluateHandleScope.Close(resultArray); } if (results && !v8Results.IsEmpty()) { for (size_t i = 0; i < v8Results->Length(); ++i) results->append(ScriptValue(v8Results->Get(i))); } }
// Create a new environment and setup the global object. // // The global object corresponds to a DOMWindow instance. However, to // allow properties of the JS DOMWindow instance to be shadowed, we // use a shadow object as the global object and use the JS DOMWindow // instance as the prototype for that shadow object. The JS DOMWindow // instance is undetectable from JavaScript code because the __proto__ // accessors skip that object. // // The shadow object and the DOMWindow instance are seen as one object // from JavaScript. The JavaScript object that corresponds to a // DOMWindow instance is the shadow object. When mapping a DOMWindow // instance to a V8 object, we return the shadow object. // // To implement split-window, see // 1) https://bugs.webkit.org/show_bug.cgi?id=17249 // 2) https://wiki.mozilla.org/Gecko:SplitWindow // 3) https://bugzilla.mozilla.org/show_bug.cgi?id=296639 // we need to split the shadow object further into two objects: // an outer window and an inner window. The inner window is the hidden // prototype of the outer window. The inner window is the default // global object of the context. A variable declared in the global // scope is a property of the inner window. // // The outer window sticks to a Frame, it is exposed to JavaScript // via window.window, window.self, window.parent, etc. The outer window // has a security token which is the domain. The outer window cannot // have its own properties. window.foo = 'x' is delegated to the // inner window. // // When a frame navigates to a new page, the inner window is cut off // the outer window, and the outer window identify is preserved for // the frame. However, a new inner window is created for the new page. // If there are JS code holds a closure to the old inner window, // it won't be able to reach the outer window via its global object. bool V8DOMWindowShell::initializeIfNeeded() { if (!m_context.isEmpty()) return true; v8::HandleScope handleScope; initializeV8IfNeeded(); createContext(); if (m_context.isEmpty()) return false; bool isMainWorld = m_world->isMainWorld(); v8::Local<v8::Context> context = v8::Local<v8::Context>::New(m_context.get()); v8::Context::Scope contextScope(context); if (m_global.isEmpty()) { m_global.set(context->Global()); if (m_global.isEmpty()) { disposeContext(); return false; } } // Flag context as isolated. if (!isMainWorld) { V8DOMWindowShell* mainWindow = m_frame->script()->windowShell(); mainWindow->initializeIfNeeded(); if (!mainWindow->context().IsEmpty()) setInjectedScriptContextDebugId(m_context.get(), m_frame->script()->contextDebugId(mainWindow->context())); setIsolatedWorldField(this, context); } m_perContextData = V8PerContextData::create(m_context.get()); if (!m_perContextData->init()) { disposeContext(); return false; } if (!installDOMWindow()) { disposeContext(); return false; } if (isMainWorld) { updateDocument(); setSecurityToken(); if (m_frame->document()) context->AllowCodeGenerationFromStrings(m_frame->document()->contentSecurityPolicy()->allowEval(0, ContentSecurityPolicy::SuppressReport)); } else { // Using the default security token means that the canAccess is always // called, which is slow. // FIXME: Use tokens where possible. This will mean keeping track of all // created contexts so that they can all be updated when the // document domain // changes. m_context->UseDefaultSecurityToken(); } m_frame->loader()->client()->didCreateScriptContext(m_context.get(), m_world->extensionGroup(), m_world->worldId()); if (isMainWorld) { // FIXME: This call is probably in the wrong spot, but causes a test timeout for http/tests/misc/window-open-then-write.html when removed. // Additionally, ScriptController::existingWindowShell cannot be correctly implemented until this call is gone. m_frame->loader()->dispatchDidClearWindowObjectInWorld(0); } return true; }