diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/bindings/v8/V8WindowShell.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/bindings/v8/V8WindowShell.cpp | 248 |
1 files changed, 120 insertions, 128 deletions
diff --git a/chromium/third_party/WebKit/Source/bindings/v8/V8WindowShell.cpp b/chromium/third_party/WebKit/Source/bindings/v8/V8WindowShell.cpp index 3474312da86..8b9e8b2dbfb 100644 --- a/chromium/third_party/WebKit/Source/bindings/v8/V8WindowShell.cpp +++ b/chromium/third_party/WebKit/Source/bindings/v8/V8WindowShell.cpp @@ -31,28 +31,30 @@ #include "config.h" #include "bindings/v8/V8WindowShell.h" -#include "RuntimeEnabledFeatures.h" -#include "V8Document.h" -#include "V8HTMLCollection.h" -#include "V8HTMLDocument.h" -#include "V8Window.h" +#include "bindings/core/v8/V8Document.h" +#include "bindings/core/v8/V8HTMLCollection.h" +#include "bindings/core/v8/V8HTMLDocument.h" +#include "bindings/core/v8/V8Window.h" #include "bindings/v8/DOMWrapperWorld.h" #include "bindings/v8/ScriptController.h" #include "bindings/v8/V8Binding.h" +#include "bindings/v8/V8DOMActivityLogger.h" #include "bindings/v8/V8GCForContextDispose.h" -#include "bindings/v8/V8HiddenPropertyName.h" +#include "bindings/v8/V8HiddenValue.h" #include "bindings/v8/V8Initializer.h" #include "bindings/v8/V8ObjectConstructor.h" -#include "bindings/v8/V8PerContextData.h" +#include "core/dom/ScriptForbiddenScope.h" +#include "core/frame/LocalFrame.h" +#include "core/frame/csp/ContentSecurityPolicy.h" #include "core/html/HTMLCollection.h" #include "core/html/HTMLIFrameElement.h" #include "core/inspector/InspectorInstrumentation.h" #include "core/loader/DocumentLoader.h" #include "core/loader/FrameLoader.h" #include "core/loader/FrameLoaderClient.h" -#include "core/frame/ContentSecurityPolicy.h" -#include "core/frame/Frame.h" +#include "platform/RuntimeEnabledFeatures.h" #include "platform/TraceEvent.h" +#include "platform/heap/Handle.h" #include "platform/weborigin/SecurityOrigin.h" #include "public/platform/Platform.h" #include "wtf/Assertions.h" @@ -77,33 +79,31 @@ static void setInjectedScriptContextDebugId(v8::Handle<v8::Context> targetContex V8PerContextDebugData::setContextDebugData(targetContext, "injected", debugId); } -PassOwnPtr<V8WindowShell> V8WindowShell::create(Frame* frame, PassRefPtr<DOMWrapperWorld> world, v8::Isolate* isolate) +PassOwnPtr<V8WindowShell> V8WindowShell::create(LocalFrame* frame, DOMWrapperWorld& world, v8::Isolate* isolate) { - return adoptPtr(new V8WindowShell(frame, world, isolate)); + return adoptPtr(new V8WindowShell(frame, &world, isolate)); } -V8WindowShell::V8WindowShell(Frame* frame, PassRefPtr<DOMWrapperWorld> world, v8::Isolate* isolate) +V8WindowShell::V8WindowShell(LocalFrame* frame, PassRefPtr<DOMWrapperWorld> world, v8::Isolate* isolate) : m_frame(frame) - , m_world(world) , m_isolate(isolate) + , m_world(world) { } void V8WindowShell::disposeContext(GlobalDetachmentBehavior behavior) { - m_perContextData.clear(); - - if (!m_contextHolder) + if (!isContextInitialized()) return; v8::HandleScope handleScope(m_isolate); - v8::Handle<v8::Context> context = m_contextHolder->context(); + v8::Handle<v8::Context> context = m_scriptState->context(); m_frame->loader().client()->willReleaseScriptContext(context, m_world->worldId()); if (behavior == DetachGlobal) context->DetachGlobal(); - m_contextHolder.clear(); + m_scriptState->disposePerContextData(); // It's likely that disposing the context has created a lot of // garbage. Notify V8 about this so it'll have a chance of cleaning @@ -111,12 +111,9 @@ void V8WindowShell::disposeContext(GlobalDetachmentBehavior behavior) V8GCForContextDispose::instanceTemplate().notifyContextDisposed(m_frame->isMainFrame()); } -void V8WindowShell::clearForClose(bool destroyGlobal) +void V8WindowShell::clearForClose() { - if (destroyGlobal) - m_global.clear(); - - if (!m_contextHolder) + if (!isContextInitialized()) return; m_document.clear(); @@ -125,21 +122,19 @@ void V8WindowShell::clearForClose(bool destroyGlobal) void V8WindowShell::clearForNavigation() { - if (!m_contextHolder) + if (!isContextInitialized()) return; - v8::HandleScope handleScope(m_isolate); - m_document.clear(); + ScriptState::Scope scope(m_scriptState.get()); - v8::Handle<v8::Context> context = m_contextHolder->context(); - v8::Context::Scope contextScope(context); + m_document.clear(); // Clear the document wrapper cache before turning on access checks on - // the old DOMWindow wrapper. This way, access to the document wrapper - // will be protected by the security checks on the DOMWindow wrapper. + // the old LocalDOMWindow wrapper. This way, access to the document wrapper + // will be protected by the security checks on the LocalDOMWindow wrapper. clearDocumentProperty(); - v8::Handle<v8::Object> windowWrapper = m_global.newLocal(m_isolate)->FindInstanceInPrototypeChain(V8Window::domTemplate(m_isolate, worldTypeInMainThread(m_isolate))); + v8::Handle<v8::Object> windowWrapper = V8Window::findInstanceInPrototypeChain(m_global.newLocal(m_isolate), m_isolate); ASSERT(!windowWrapper.IsEmpty()); windowWrapper->TurnOnAccessCheck(); disposeContext(DetachGlobal); @@ -147,16 +142,16 @@ void V8WindowShell::clearForNavigation() // 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 +// The global object corresponds to a LocalDOMWindow instance. However, to +// allow properties of the JS LocalDOMWindow instance to be shadowed, we +// use a shadow object as the global object and use the JS LocalDOMWindow +// instance as the prototype for that shadow object. The JS LocalDOMWindow // 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 +// The shadow object and the LocalDOMWindow 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 +// LocalDOMWindow instance is the shadow object. When mapping a LocalDOMWindow // instance to a V8 object, we return the shadow object. // // To implement split-window, see @@ -169,7 +164,7 @@ void V8WindowShell::clearForNavigation() // 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 +// The outer window sticks to a LocalFrame, 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 @@ -182,27 +177,31 @@ void V8WindowShell::clearForNavigation() // it won't be able to reach the outer window via its global object. bool V8WindowShell::initializeIfNeeded() { - if (m_contextHolder) + if (isContextInitialized()) return true; - TRACE_EVENT0("v8", "V8WindowShell::initializeIfNeeded"); - - v8::HandleScope handleScope(m_isolate); - - createContext(); - if (!m_contextHolder) - return false; + DOMWrapperWorld::setWorldOfInitializingWindow(m_world.get()); + bool result = initialize(); + DOMWrapperWorld::setWorldOfInitializingWindow(0); + return result; +} - v8::Handle<v8::Context> context = m_contextHolder->context(); +bool V8WindowShell::initialize() +{ + TRACE_EVENT0("v8", "V8WindowShell::initialize"); + TRACE_EVENT_SCOPED_SAMPLING_STATE("Blink", "InitializeWindow"); - V8PerContextDataHolder::install(context); + ScriptForbiddenScope::AllowUserAgentScript allowScript; - m_world->setIsolatedWorldField(context); + v8::HandleScope handleScope(m_isolate); - bool isMainWorld = m_world->isMainWorld(); + createContext(); - v8::Context::Scope contextScope(context); + if (!isContextInitialized()) + return false; + ScriptState::Scope scope(m_scriptState.get()); + v8::Handle<v8::Context> context = m_scriptState->context(); if (m_global.isEmpty()) { m_global.set(m_isolate, context->Global()); if (m_global.isEmpty()) { @@ -211,44 +210,32 @@ bool V8WindowShell::initializeIfNeeded() } } - if (!isMainWorld) { - V8WindowShell* mainWindow = m_frame->script().existingWindowShell(mainThreadNormalWorld()); + if (!m_world->isMainWorld()) { + V8WindowShell* mainWindow = m_frame->script().existingWindowShell(DOMWrapperWorld::mainWorld()); 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) { + if (m_world->isMainWorld()) { + // ActivityLogger for main world is updated within updateDocument(). updateDocument(); - setSecurityToken(); if (m_frame->document()) { + setSecurityToken(m_frame->document()->securityOrigin()); 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(); - + updateActivityLogger(); SecurityOrigin* origin = m_world->isolatedWorldSecurityOrigin(); + setSecurityToken(origin); if (origin && InspectorInstrumentation::hasFrontends()) { - ScriptState* scriptState = ScriptState::forContext(v8::Local<v8::Context>::New(m_isolate, context)); - InspectorInstrumentation::didCreateIsolatedContext(m_frame, scriptState, origin); + InspectorInstrumentation::didCreateIsolatedContext(m_frame, m_scriptState.get(), origin); } } m_frame->loader().client()->didCreateScriptContext(context, m_world->extensionGroup(), m_world->worldId()); @@ -257,14 +244,14 @@ bool V8WindowShell::initializeIfNeeded() void V8WindowShell::createContext() { - // The activeDocumentLoader pointer could be 0 during frame shutdown. + // The documentLoader pointer could be 0 during frame shutdown. // FIXME: Can we remove this check? - if (!m_frame->loader().activeDocumentLoader()) + if (!m_frame->loader().documentLoader()) return; // Create a new environment using an empty template for the shadow // object. Reuse the global object if one has been created earlier. - v8::Handle<v8::ObjectTemplate> globalTemplate = V8Window::GetShadowObjectTemplate(m_isolate, m_world->isMainWorld() ? MainWorld : IsolatedWorld); + v8::Handle<v8::ObjectTemplate> globalTemplate = V8Window::getShadowObjectTemplate(m_isolate); if (globalTemplate.IsEmpty()) return; @@ -284,12 +271,10 @@ void V8WindowShell::createContext() } v8::ExtensionConfiguration extensionConfiguration(index, extensionNames.get()); - v8::HandleScope handleScope(m_isolate); v8::Handle<v8::Context> context = v8::Context::New(m_isolate, &extensionConfiguration, globalTemplate, m_global.newLocal(m_isolate)); - if (!context.IsEmpty()) { - m_contextHolder = adoptPtr(new gin::ContextHolder(m_isolate)); - m_contextHolder->SetContext(context); - } + if (context.IsEmpty()) + return; + m_scriptState = ScriptState::create(context, m_world); double contextCreationDurationInMilliseconds = (currentTime() - contextCreationStartInSeconds) * 1000; const char* histogramName = "WebCore.V8WindowShell.createContext.MainWorld"; @@ -298,36 +283,46 @@ void V8WindowShell::createContext() blink::Platform::current()->histogramCustomCounts(histogramName, contextCreationDurationInMilliseconds, 0, 10000, 50); } +static v8::Handle<v8::Object> toInnerGlobalObject(v8::Handle<v8::Context> context) +{ + return v8::Handle<v8::Object>::Cast(context->Global()->GetPrototype()); +} + bool V8WindowShell::installDOMWindow() { - DOMWrapperWorld::setInitializingWindow(true); - DOMWindow* window = m_frame->domWindow(); - v8::Local<v8::Object> windowWrapper = V8ObjectConstructor::newInstance(V8PerContextData::from(m_contextHolder->context())->constructorForType(&V8Window::wrapperTypeInfo)); + LocalDOMWindow* window = m_frame->domWindow(); + v8::Local<v8::Object> windowWrapper = V8ObjectConstructor::newInstance(m_isolate, m_scriptState->perContextData()->constructorForType(&V8Window::wrapperTypeInfo)); if (windowWrapper.IsEmpty()) return false; V8Window::installPerContextEnabledProperties(windowWrapper, window, m_isolate); - V8DOMWrapper::setNativeInfo(v8::Handle<v8::Object>::Cast(windowWrapper->GetPrototype()), &V8Window::wrapperTypeInfo, window); + V8DOMWrapper::setNativeInfoForHiddenWrapper(v8::Handle<v8::Object>::Cast(windowWrapper->GetPrototype()), &V8Window::wrapperTypeInfo, window); // Install the windowWrapper as the prototype of the innerGlobalObject. // The full structure of the global object is as follows: // // outerGlobalObject (Empty object, remains after navigation) // -- has prototype --> innerGlobalObject (Holds global variables, changes during navigation) - // -- has prototype --> DOMWindow instance + // -- has prototype --> LocalDOMWindow instance // -- has prototype --> Window.prototype // -- has prototype --> Object.prototype // // Note: Much of this prototype structure is hidden from web content. The - // outer, inner, and DOMWindow instance all appear to be the same + // outer, inner, and LocalDOMWindow instance all appear to be the same // JavaScript object. // - v8::Handle<v8::Object> innerGlobalObject = toInnerGlobalObject(m_contextHolder->context()); - V8DOMWrapper::setNativeInfo(innerGlobalObject, &V8Window::wrapperTypeInfo, window); + // Note: With Oilpan, the LocalDOMWindow object is garbage collected. + // Persistent references to this inner global object view of the LocalDOMWindow + // aren't kept, as that would prevent the global object from ever being released. + // It is safe not to do so, as the wrapper for the LocalDOMWindow being installed here + // already keeps a persistent reference, and it along with the inner global object + // views of the LocalDOMWindow will die together once that wrapper clears the persistent + // reference. + v8::Handle<v8::Object> innerGlobalObject = toInnerGlobalObject(m_scriptState->context()); + V8DOMWrapper::setNativeInfoForHiddenWrapper(innerGlobalObject, &V8Window::wrapperTypeInfo, window); innerGlobalObject->SetPrototype(windowWrapper); - V8DOMWrapper::associateObjectWithWrapper<V8Window>(PassRefPtr<DOMWindow>(window), &V8Window::wrapperTypeInfo, windowWrapper, m_isolate, WrapperConfiguration::Dependent); - DOMWrapperWorld::setInitializingWindow(false); + V8DOMWrapper::associateObjectWithWrapper<V8Window>(PassRefPtrWillBeRawPtr<LocalDOMWindow>(window), &V8Window::wrapperTypeInfo, windowWrapper, m_isolate, WrapperConfiguration::Dependent); return true; } @@ -342,18 +337,16 @@ void V8WindowShell::updateDocumentProperty() if (!m_world->isMainWorld()) return; - v8::HandleScope handleScope(m_isolate); - v8::Handle<v8::Context> context = m_contextHolder->context(); - v8::Context::Scope contextScope(context); - - v8::Handle<v8::Value> documentWrapper = toV8(m_frame->document(), v8::Handle<v8::Object>(), context->GetIsolate()); + ScriptState::Scope scope(m_scriptState.get()); + v8::Handle<v8::Context> context = m_scriptState->context(); + v8::Handle<v8::Value> documentWrapper = toV8(m_frame->document(), context->Global(), context->GetIsolate()); ASSERT(documentWrapper == m_document.newLocal(m_isolate) || m_document.isEmpty()); if (m_document.isEmpty()) updateDocumentWrapper(v8::Handle<v8::Object>::Cast(documentWrapper)); checkDocumentWrapper(m_document.newLocal(m_isolate), m_frame->document()); // If instantiation of the document wrapper fails, clear the cache - // and let the DOMWindow accessor handle access to the document. + // and let the LocalDOMWindow accessor handle access to the document. if (documentWrapper.IsEmpty()) { clearDocumentProperty(); return; @@ -362,38 +355,40 @@ void V8WindowShell::updateDocumentProperty() context->Global()->ForceSet(v8AtomicString(m_isolate, "document"), documentWrapper, static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete)); // We also stash a reference to the document on the inner global object so that - // DOMWindow objects we obtain from JavaScript references are guaranteed to have + // LocalDOMWindow objects we obtain from JavaScript references are guaranteed to have // live Document objects. - toInnerGlobalObject(context)->SetHiddenValue(V8HiddenPropertyName::document(m_isolate), documentWrapper); + V8HiddenValue::setHiddenValue(m_isolate, toInnerGlobalObject(context), V8HiddenValue::document(m_isolate), documentWrapper); } void V8WindowShell::clearDocumentProperty() { - ASSERT(m_contextHolder); + ASSERT(isContextInitialized()); if (!m_world->isMainWorld()) return; v8::HandleScope handleScope(m_isolate); - m_contextHolder->context()->Global()->ForceDelete(v8AtomicString(m_isolate, "document")); + m_scriptState->context()->Global()->ForceDelete(v8AtomicString(m_isolate, "document")); } -void V8WindowShell::setSecurityToken() +void V8WindowShell::updateActivityLogger() { - ASSERT(m_world->isMainWorld()); - - Document* document = m_frame->document(); + m_scriptState->perContextData()->setActivityLogger(V8DOMActivityLogger::activityLogger( + m_world->worldId(), m_frame->document() ? m_frame->document()->baseURI() : KURL())); +} - // Ask the document's SecurityOrigin to generate a security token. +void V8WindowShell::setSecurityToken(SecurityOrigin* origin) +{ // If two tokens are equal, then the SecurityOrigins canAccess each other. // If two tokens are not equal, then we have to call canAccess. // Note: we can't use the HTTPOrigin if it was set from the DOM. - SecurityOrigin* origin = document->securityOrigin(); String token; // We stick with an empty token if document.domain was modified or if we // are in the initial empty document, so that we can do a full canAccess // check in those cases. - if (!origin->domainWasSetInDOM() - && !m_frame->loader().stateMachine()->isDisplayingInitialEmptyDocument()) - token = document->securityOrigin()->toString(); + bool delaySet = m_world->isMainWorld() + && (origin->domainWasSetInDOM() + || m_frame->loader().stateMachine()->isDisplayingInitialEmptyDocument()); + if (origin && !delaySet) + token = origin->toString(); // An empty or "null" token means we always have to call // canAccess. The toString method on securityOrigins returns the @@ -402,7 +397,7 @@ void V8WindowShell::setSecurityToken() // case, we use the global object as the security token to avoid // calling canAccess when a script accesses its own objects. v8::HandleScope handleScope(m_isolate); - v8::Handle<v8::Context> context = m_contextHolder->context(); + v8::Handle<v8::Context> context = m_scriptState->context(); if (token.isEmpty() || token == "null") { context->UseDefaultSecurityToken(); return; @@ -417,12 +412,13 @@ void V8WindowShell::setSecurityToken() void V8WindowShell::updateDocument() { ASSERT(m_world->isMainWorld()); - if (m_global.isEmpty()) + if (!isGlobalInitialized()) return; - if (!m_contextHolder) + if (!isContextInitialized()) return; + updateActivityLogger(); updateDocumentProperty(); - updateSecurityOrigin(); + updateSecurityOrigin(m_frame->document()->securityOrigin()); } static v8::Handle<v8::Value> getNamedProperty(HTMLDocument* htmlDocument, const AtomicString& key, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate) @@ -430,16 +426,17 @@ static v8::Handle<v8::Value> getNamedProperty(HTMLDocument* htmlDocument, const if (!htmlDocument->hasNamedItem(key) && !htmlDocument->hasExtraNamedItem(key)) return v8Undefined(); - RefPtr<HTMLCollection> items = htmlDocument->documentNamedItems(key); + RefPtrWillBeRawPtr<HTMLCollection> items = htmlDocument->documentNamedItems(key); if (items->isEmpty()) return v8Undefined(); if (items->hasExactlyOneItem()) { - Node* node = items->item(0); + Element* element = items->item(0); + ASSERT(element); Frame* frame = 0; - if (node->hasTagName(HTMLNames::iframeTag) && (frame = toHTMLIFrameElement(node)->contentFrame())) + if (isHTMLIFrameElement(*element) && (frame = toHTMLIFrameElement(*element).contentFrame())) return toV8(frame->domWindow(), creationContext, isolate); - return toV8(node, creationContext, isolate); + return toV8(element, creationContext, isolate); } return toV8(items.release(), creationContext, isolate); } @@ -466,12 +463,10 @@ void V8WindowShell::namedItemAdded(HTMLDocument* document, const AtomicString& n { ASSERT(m_world->isMainWorld()); - if (!m_contextHolder) + if (!isContextInitialized()) return; - v8::HandleScope handleScope(m_isolate); - v8::Context::Scope contextScope(m_contextHolder->context()); - + ScriptState::Scope scope(m_scriptState.get()); ASSERT(!m_document.isEmpty()); v8::Handle<v8::Object> documentHandle = m_document.newLocal(m_isolate); checkDocumentWrapper(documentHandle, document); @@ -482,28 +477,25 @@ void V8WindowShell::namedItemRemoved(HTMLDocument* document, const AtomicString& { ASSERT(m_world->isMainWorld()); - if (!m_contextHolder) + if (!isContextInitialized()) return; if (document->hasNamedItem(name) || document->hasExtraNamedItem(name)) return; - v8::HandleScope handleScope(m_isolate); - v8::Context::Scope contextScope(m_contextHolder->context()); - + ScriptState::Scope scope(m_scriptState.get()); ASSERT(!m_document.isEmpty()); v8::Handle<v8::Object> documentHandle = m_document.newLocal(m_isolate); checkDocumentWrapper(documentHandle, document); documentHandle->Delete(v8String(m_isolate, name)); } -void V8WindowShell::updateSecurityOrigin() +void V8WindowShell::updateSecurityOrigin(SecurityOrigin* origin) { ASSERT(m_world->isMainWorld()); - if (!m_contextHolder) + if (!isContextInitialized()) return; - v8::HandleScope handleScope(m_isolate); - setSecurityToken(); + setSecurityToken(origin); } } // WebCore |