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)));
    }
}
예제 #3
0
// 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;
}