blob: 67d3a448b040dea675c217365c881c62f69b6f76 [file] [log] [blame]
/*
* Copyright (C) 2008, 2009, 2011 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 "bindings/core/v8/WindowProxy.h"
#include <utility>
#include "bindings/core/v8/V8DOMWrapper.h"
#include "bindings/core/v8/V8Window.h"
#include "core/frame/Frame.h"
#include "v8/include/v8.h"
#include "wtf/Assertions.h"
#include "wtf/debug/Alias.h"
namespace blink {
WindowProxy::~WindowProxy() {
// clearForClose() or clearForNavigation() must be invoked before destruction
// starts.
DCHECK(m_lifecycle != Lifecycle::ContextInitialized);
}
DEFINE_TRACE(WindowProxy) {
visitor->trace(m_frame);
}
WindowProxy::WindowProxy(v8::Isolate* isolate,
Frame& frame,
RefPtr<DOMWrapperWorld> world)
: m_isolate(isolate),
m_frame(frame),
m_world(std::move(world)),
m_lifecycle(Lifecycle::ContextUninitialized) {}
void WindowProxy::clearForClose() {
disposeContext(DoNotDetachGlobal);
}
void WindowProxy::clearForNavigation() {
disposeContext(DetachGlobal);
}
v8::Local<v8::Object> WindowProxy::globalIfNotDetached() {
if (m_lifecycle == Lifecycle::ContextInitialized)
return m_globalProxy.newLocal(m_isolate);
return v8::Local<v8::Object>();
}
v8::Local<v8::Object> WindowProxy::releaseGlobal() {
DCHECK(m_lifecycle != Lifecycle::ContextInitialized);
DLOG_IF(FATAL, m_isGlobalProxyAttached)
<< "Context not detached by calling clearForNavigation()";
v8::Local<v8::Object> global = m_globalProxy.newLocal(m_isolate);
m_globalProxy.clear();
return global;
}
void WindowProxy::setGlobal(v8::Local<v8::Object> global) {
m_globalProxy.set(m_isolate, global);
// Initialize the window proxy now, to re-establish the connection between
// the global object and the v8::Context. This is really only needed for a
// RemoteDOMWindow, since it has no scripting environment of its own.
// Without this, existing script references to a swapped in RemoteDOMWindow
// would be broken until that RemoteDOMWindow was vended again through an
// interface like window.frames.
initializeIfNeeded();
}
// 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 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
// 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.
void WindowProxy::initializeIfNeeded() {
v8::HandleScope handleScope(m_isolate);
Lifecycle oldLifecycle = m_lifecycle;
DOMWindow* window = m_frame->domWindow();
bool isLocal = window->isLocalDOMWindow();
// Prevent these locals from getting optimized out, and hopefully, the heap
// contents captured into minidumps.
WTF::debug::alias(&oldLifecycle);
WTF::debug::alias(&window);
WTF::debug::alias(&isLocal);
// TODO(haraken): It is wrong to re-initialize an already detached window
// proxy. This must be 'if(m_lifecycle == Lifecycle::ContextUninitialized)'.
if (m_lifecycle != Lifecycle::ContextInitialized) {
initialize();
// Note: this set of CHECKs is intentionally duplicated below to distinguish
// between initializing the global with null internal fields or returning a
// global that claims to be initialized but has null internal fields.
v8::Local<v8::Object> globalProxy = m_globalProxy.newLocal(m_isolate);
CHECK(!globalProxy.IsEmpty());
CHECK(V8Window::hasInstance(globalProxy, m_isolate));
CHECK(window);
CHECK_EQ(window, V8Window::toImpl(globalProxy));
} else {
v8::Local<v8::Object> globalProxy = m_globalProxy.newLocal(m_isolate);
CHECK(!globalProxy.IsEmpty());
CHECK(V8Window::hasInstance(globalProxy, m_isolate));
CHECK(window);
CHECK_EQ(window, V8Window::toImpl(globalProxy));
}
// Sanity check: WindowProxy's frame's window should still be the same
DOMWindow* window2 = m_frame->domWindow();
WTF::debug::alias(&window2);
CHECK_EQ(window, window2);
}
} // namespace blink