void V8Proxy::evaluateInIsolatedWorld(int worldID, const Vector<ScriptSourceCode>& sources, int extensionGroup) { // FIXME: This will need to get reorganized once we have a windowShell for the isolated world. windowShell()->initContextIfNeeded(); v8::HandleScope handleScope; V8IsolatedContext* isolatedContext = 0; if (worldID > 0) { IsolatedWorldMap::iterator iter = m_isolatedWorlds.find(worldID); if (iter != m_isolatedWorlds.end()) { isolatedContext = iter->second; } else { isolatedContext = new V8IsolatedContext(this, extensionGroup); if (isolatedContext->context().IsEmpty()) { delete isolatedContext; return; } // FIXME: We should change this to using window shells to match JSC. m_isolatedWorlds.set(worldID, isolatedContext); // Setup context id for JS debugger. if (!setInjectedScriptContextDebugId(isolatedContext->context())) { m_isolatedWorlds.take(worldID); delete isolatedContext; return; } } } else { isolatedContext = new V8IsolatedContext(this, extensionGroup); if (isolatedContext->context().IsEmpty()) { delete isolatedContext; return; } } v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolatedContext->context()); v8::Context::Scope context_scope(context); for (size_t i = 0; i < sources.size(); ++i) evaluate(sources[i], 0); if (worldID == 0) isolatedContext->destroy(); }
V8IsolatedContext::V8IsolatedContext(V8Proxy* proxy, int extensionGroup, int worldId) : m_world(DOMWrapperWorld::create(worldId)), m_frame(proxy->frame()) { v8::HandleScope scope; v8::Handle<v8::Context> mainWorldContext = proxy->windowShell()->context(); if (mainWorldContext.IsEmpty()) return; // FIXME: We should be creating a new V8DOMWindowShell here instead of riping out the context. m_context = SharedPersistent<v8::Context>::create(proxy->windowShell()->createNewContext(v8::Handle<v8::Object>(), extensionGroup, m_world->worldId())); if (m_context->get().IsEmpty()) return; // Run code in the new context. v8::Context::Scope contextScope(m_context->get()); // Setup context id for JS debugger. setInjectedScriptContextDebugId(m_context->get(), proxy->contextDebugId(mainWorldContext)); getGlobalObject(m_context->get())->SetPointerInInternalField(V8DOMWindow::enteredIsolatedWorldIndex, this); m_perContextData = V8PerContextData::create(m_context->get()); m_perContextData->init(); // FIXME: This will go away once we have a windowShell for the isolated world. proxy->windowShell()->installDOMWindow(m_context->get(), m_frame->document()->domWindow()); // 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->get()->UseDefaultSecurityToken(); m_frame->loader()->client()->didCreateScriptContext(context(), extensionGroup, m_world->worldId()); }
// 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; }
// 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 V8WindowShell::initializeIfNeeded() { if (m_contextHolder) return true; TRACE_EVENT0("v8", "V8WindowShell::initializeIfNeeded"); v8::HandleScope handleScope(m_isolate); createContext(); if (!m_contextHolder) return false; v8::Handle<v8::Context> context = m_contextHolder->context(); V8PerContextDataHolder::install(context); m_world->setIsolatedWorldField(context); bool isMainWorld = m_world->isMainWorld(); v8::Context::Scope contextScope(context); if (m_global.isEmpty()) { m_global.set(m_isolate, context->Global()); if (m_global.isEmpty()) { disposeContext(DoNotDetachGlobal); return false; } } if (!isMainWorld) { V8WindowShell* mainWindow = m_frame->script().existingWindowShell(mainThreadNormalWorld()); if (mainWindow && !mainWindow->context().IsEmpty()) setInjectedScriptContextDebugId(context, m_frame->script().contextDebugId(mainWindow->context())); } m_perContextData = V8PerContextData::create(context); if (!m_perContextData->init()) { disposeContext(DoNotDetachGlobal); return false; } m_perContextData->setActivityLogger(DOMWrapperWorld::activityLogger(m_world->worldId())); if (!installDOMWindow()) { disposeContext(DoNotDetachGlobal); return false; } if (isMainWorld) { updateDocument(); setSecurityToken(); if (m_frame->document()) { ContentSecurityPolicy* csp = m_frame->document()->contentSecurityPolicy(); context->AllowCodeGenerationFromStrings(csp->allowEval(0, ContentSecurityPolicy::SuppressReport)); context->SetErrorMessageForCodeGenerationFromStrings(v8String(m_isolate, csp->evalDisabledErrorMessage())); } } 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. context->UseDefaultSecurityToken(); SecurityOrigin* origin = m_world->isolatedWorldSecurityOrigin(); if (origin && InspectorInstrumentation::hasFrontends()) { ScriptState* scriptState = ScriptState::forContext(v8::Local<v8::Context>::New(m_isolate, context)); InspectorInstrumentation::didCreateIsolatedContext(m_frame, scriptState, origin); } } m_frame->loader().client()->didCreateScriptContext(context, m_world->extensionGroup(), m_world->worldId()); return true; }