summaryrefslogtreecommitdiffstats
path: root/webkit/port/bindings/v8/v8_proxy.cpp
diff options
context:
space:
mode:
authorfqian@google.com <fqian@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-22 23:07:54 +0000
committerfqian@google.com <fqian@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-22 23:07:54 +0000
commit7747859358a1a201fc8d8e65664435b979f99c37 (patch)
tree5f25b6ca30524c16a7e399966b4d839c88fc70f7 /webkit/port/bindings/v8/v8_proxy.cpp
parent9bd6f38ddfe66712e8e04de142ae64fa70ea5ada (diff)
downloadchromium_src-7747859358a1a201fc8d8e65664435b979f99c37.zip
chromium_src-7747859358a1a201fc8d8e65664435b979f99c37.tar.gz
chromium_src-7747859358a1a201fc8d8e65664435b979f99c37.tar.bz2
Second part of split window support in the binding part.
The major change is that when a frame is loading a new page, the frame loader calls V8Proxy::clearWindowShell when it receives the first piece of data. At that point, the global object is detached from its current context. FrameLoader continues on preparing new environment, including creating new DOMWindow and security origin for the frame. When new DOMWindow is ready, the frame loader calls ScriptController::notifyNewDOMWindowReady(), which uses the existing global object to initialize the new context for the frame. Between detaching global from old context and creating new context using global, there should no JavaScript executed. The implication is that if the new page does not use JavaScript, its JS context is still created. But the overhead should be very small because V8 is warmed up already. Review URL: http://codereview.chromium.org/7838 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3785 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/port/bindings/v8/v8_proxy.cpp')
-rw-r--r--webkit/port/bindings/v8/v8_proxy.cpp474
1 files changed, 290 insertions, 184 deletions
diff --git a/webkit/port/bindings/v8/v8_proxy.cpp b/webkit/port/bindings/v8/v8_proxy.cpp
index 81df91f..fe67a22 100644
--- a/webkit/port/bindings/v8/v8_proxy.cpp
+++ b/webkit/port/bindings/v8/v8_proxy.cpp
@@ -114,6 +114,7 @@
namespace WebCore {
+
// DOM binding algorithm:
//
// There are two kinds of DOM objects:
@@ -179,7 +180,8 @@ static inline void USE_VAR(T) { }
// The function is the place to set the break point to inspect
// live global handles. Leaks are often come from leaked global handles.
-static void EnumerateGlobalHandles() {
+static void EnumerateGlobalHandles()
+{
for (GlobalHandleMap::iterator it = global_handle_map().begin(),
end = global_handle_map().end(); it != end; ++it) {
GlobalHandleInfo* info = it->second;
@@ -190,7 +192,8 @@ static void EnumerateGlobalHandles() {
}
void V8Proxy::RegisterGlobalHandle(GlobalHandleType type, void* host,
- v8::Persistent<v8::Value> handle) {
+ v8::Persistent<v8::Value> handle)
+{
ASSERT(!global_handle_map().contains(*handle));
global_handle_map().set(*handle, new GlobalHandleInfo(host, type));
}
@@ -208,7 +211,8 @@ void V8Proxy::UnregisterGlobalHandle(void* host, v8::Persistent<v8::Value> handl
void BatchConfigureAttributes(v8::Handle<v8::ObjectTemplate> inst,
v8::Handle<v8::ObjectTemplate> proto,
const BatchedAttribute* attrs,
- size_t num_attrs) {
+ size_t num_attrs)
+{
for (size_t i = 0; i < num_attrs; ++i) {
const BatchedAttribute* a = &attrs[i];
(a->on_proto ? proto : inst)->SetAccessor(
@@ -226,7 +230,8 @@ void BatchConfigureAttributes(v8::Handle<v8::ObjectTemplate> inst,
void BatchConfigureConstants(v8::Handle<v8::FunctionTemplate> desc,
v8::Handle<v8::ObjectTemplate> proto,
const BatchedConstant* consts,
- size_t num_consts) {
+ size_t num_consts)
+{
for (size_t i = 0; i < num_consts; ++i) {
const BatchedConstant* c = &consts[i];
desc->Set(v8::String::New(c->name),
@@ -250,20 +255,23 @@ class DOMPeerableWrapperMap : public DOMWrapperMap<T> {
DOMWrapperMap<T>(callback) { }
// Get the JS wrapper object of an object.
- v8::Persistent<v8::Object> get(T* obj) {
+ v8::Persistent<v8::Object> get(T* obj)
+ {
v8::Object* peer = static_cast<v8::Object*>(obj->peer());
ASSERT(peer == this->map_.get(obj));
return peer ? v8::Persistent<v8::Object>(peer)
: v8::Persistent<v8::Object>();
}
- void set(T* obj, v8::Persistent<v8::Object> peer_handle) {
+ void set(T* obj, v8::Persistent<v8::Object> peer_handle)
+ {
ASSERT(obj->peer() == 0);
obj->setPeer(*peer_handle);
DOMWrapperMap<T>::set(obj, peer_handle);
}
- void forget(T* obj) {
+ void forget(T* obj)
+ {
v8::Object* peer = static_cast<v8::Object*>(obj->peer());
ASSERT(peer == this->map_.get(obj));
if (peer)
@@ -276,14 +284,16 @@ class DOMPeerableWrapperMap : public DOMWrapperMap<T> {
static void WeakPeerableCallback(v8::Persistent<v8::Object> obj, void* para);
static void WeakNodeCallback(v8::Persistent<v8::Object> obj, void* para);
// A map from DOM node to its JS wrapper.
-static DOMWrapperMap<Node>& dom_node_map() {
+static DOMWrapperMap<Node>& dom_node_map()
+{
static DOMPeerableWrapperMap<Node> static_dom_node_map(&WeakNodeCallback);
return static_dom_node_map;
}
// A map from a non-DOM node (peerable) to its JS wrapper.
-static DOMWrapperMap<Peerable>& dom_object_map() {
+static DOMWrapperMap<Peerable>& dom_object_map()
+{
static DOMPeerableWrapperMap<Peerable>
static_dom_object_map(&WeakPeerableCallback);
return static_dom_object_map;
@@ -294,14 +304,16 @@ static void WeakSVGElementInstanceCallback(v8::Persistent<v8::Object> obj,
void* param);
// A map for SVGElementInstances, which are not peerable
-static DOMWrapperMap<SVGElementInstance>& dom_svg_element_instance_map() {
+static DOMWrapperMap<SVGElementInstance>& dom_svg_element_instance_map()
+{
static DOMWrapperMap<SVGElementInstance>
static_dom_svg_element_instance_map(&WeakSVGElementInstanceCallback);
return static_dom_svg_element_instance_map;
}
static void WeakSVGElementInstanceCallback(v8::Persistent<v8::Object> obj,
- void* param) {
+ void* param)
+{
SVGElementInstance* instance = static_cast<SVGElementInstance*>(param);
ASSERT(dom_svg_element_instance_map().contains(instance));
@@ -310,7 +322,8 @@ static void WeakSVGElementInstanceCallback(v8::Persistent<v8::Object> obj,
}
v8::Handle<v8::Value> V8Proxy::SVGElementInstanceToV8Object(
- SVGElementInstance* instance) {
+ SVGElementInstance* instance)
+{
if (!instance)
return v8::Null();
@@ -377,7 +390,8 @@ v8::Handle<v8::Value> V8Proxy::SVGObjectWithContextToV8Object(
}
static void WeakSVGObjectWithContext(v8::Persistent<v8::Object> obj,
- void* param) {
+ void* param)
+{
Peerable* dom_obj = static_cast<Peerable*>(param);
ASSERT(dom_svg_object_with_context_map().contains(dom_obj));
@@ -419,7 +433,8 @@ SVGElement* V8Proxy::GetSVGContext(void* obj)
// Called when obj is near death (not reachable from JS roots)
// It is time to remove the entry from the table and dispose
// the handle.
-static void WeakPeerableCallback(v8::Persistent<v8::Object> obj, void* para) {
+static void WeakPeerableCallback(v8::Persistent<v8::Object> obj, void* para)
+{
Peerable* dom_obj = static_cast<Peerable*>(para);
ASSERT(dom_object_map().contains(dom_obj));
@@ -484,7 +499,8 @@ static void GCPrologue()
}
-static void GCEpilogue() {
+static void GCEpilogue()
+{
#ifndef NDEBUG
// Check all survivals are weak.
PeerableMap peer_map = dom_object_map().impl();
@@ -536,7 +552,8 @@ void V8Proxy::GCProtect(Peerable* dom_object)
// static
-void V8Proxy::GCUnprotect(Peerable* dom_object) {
+void V8Proxy::GCUnprotect(Peerable* dom_object)
+{
if (!dom_object)
return;
if (!gc_protected_map().contains(dom_object))
@@ -615,7 +632,8 @@ Vector<JavaScriptConsoleMessage>* ConsoleMessageManager::m_delayed = NULL;
void ConsoleMessageManager::AddMessage(
Page* page,
- const JavaScriptConsoleMessage& message) {
+ const JavaScriptConsoleMessage& message)
+{
// Process any delayed messages to make sure that messages
// appear in the right order in the console.
ProcessDelayedMessages();
@@ -636,32 +654,32 @@ void ConsoleMessageManager::AddDelayedMessage(const JavaScriptConsoleMessage& me
void ConsoleMessageManager::ProcessDelayedMessages()
{
- // If we have a delayed vector it cannot be empty.
- if (!m_delayed)
- return;
- ASSERT(!m_delayed->isEmpty());
-
- // Add the delayed messages to the page of the active
- // context. If that for some bizarre reason does not
- // exist, we clear the list of delayed messages to avoid
- // posting messages. We still deallocate the vector.
- Frame* frame = V8Proxy::retrieveActiveFrame();
- Page* page = NULL;
- if (frame)
- page = frame->page();
- if (!page)
- m_delayed->clear();
+ // If we have a delayed vector it cannot be empty.
+ if (!m_delayed)
+ return;
+ ASSERT(!m_delayed->isEmpty());
+
+ // Add the delayed messages to the page of the active
+ // context. If that for some bizarre reason does not
+ // exist, we clear the list of delayed messages to avoid
+ // posting messages. We still deallocate the vector.
+ Frame* frame = V8Proxy::retrieveActiveFrame();
+ Page* page = NULL;
+ if (frame)
+ page = frame->page();
+ if (!page)
+ m_delayed->clear();
- // Iterate through all the delayed messages and add them
- // to the console.
- const int size = m_delayed->size();
- for (int i = 0; i < size; i++) {
- m_delayed->at(i).AddToPage(page);
- }
+ // Iterate through all the delayed messages and add them
+ // to the console.
+ const int size = m_delayed->size();
+ for (int i = 0; i < size; i++) {
+ m_delayed->at(i).AddToPage(page);
+ }
- // Deallocate the delayed vector.
- delete m_delayed;
- m_delayed = NULL;
+ // Deallocate the delayed vector.
+ delete m_delayed;
+ m_delayed = NULL;
}
@@ -683,7 +701,8 @@ void log_info(Frame* frame, const String& msg, const String& url)
}
static void HandleConsoleMessage(v8::Handle<v8::Message> message,
- v8::Handle<v8::Value> data) {
+ v8::Handle<v8::Value> data)
+{
// Use the frame where JavaScript is called from.
Frame* frame = V8Proxy::retrieveActiveFrame();
if (!frame)
@@ -714,21 +733,24 @@ enum DelayReporting {
};
-static void ReportUnsafeAccessTo(Frame* target, DelayReporting delay) {
+static void ReportUnsafeAccessTo(Frame* target, DelayReporting delay)
+{
ASSERT(target);
Document* targetDocument = target->document();
if (!targetDocument)
return;
Frame* source = V8Proxy::retrieveActiveFrame();
+ if (!source || !source->document())
+ return; // Ignore error if the source document is gone.
+
Document* sourceDocument = source->document();
- ASSERT(sourceDocument);
// FIXME: This error message should contain more specifics of why the same
// origin check has failed.
String str = String::format("Unsafe JavaScript attempt to access frame "
- "with URL %s from frame with URL %s. Domains, protocols and ports must "
- "match.\n",
+ "with URL %s from frame with URL %s. "
+ "Domains, protocols and ports must match.\n",
targetDocument->url().string().utf8().data(),
sourceDocument->url().string().utf8().data());
@@ -754,7 +776,8 @@ static void ReportUnsafeAccessTo(Frame* target, DelayReporting delay) {
static void ReportUnsafeJavaScriptAccess(v8::Local<v8::Object> host,
v8::AccessType type,
- v8::Local<v8::Value> data) {
+ v8::Local<v8::Value> data)
+{
// Do not report error if the access type is HAS.
if (type == v8::ACCESS_HAS)
return;
@@ -782,7 +805,7 @@ static void HandleFatalErrorInV8()
V8Proxy::~V8Proxy()
{
- clear();
+ clearForClose();
DestroyGlobal();
}
@@ -827,7 +850,8 @@ PassRefPtr<EventListener> V8Proxy::createSVGEventHandler(const String& functionN
static V8EventListener* FindEventListenerInList(V8EventListenerList& list,
v8::Local<v8::Value> listener,
- bool html) {
+ bool html)
+{
ASSERT(v8::Context::InContext());
if (!listener->IsObject())
@@ -895,15 +919,16 @@ PassRefPtr<V8EventListener> V8Proxy::FindOrCreateV8EventListener(v8::Local<v8::V
// The persistent reference is made weak in the constructor
// of V8XHREventListener.
-PassRefPtr<V8EventListener> V8Proxy::FindXHREventListener(v8::Local<v8::Value> listener,
- bool html) {
+PassRefPtr<V8EventListener> V8Proxy::FindXHREventListener(
+ v8::Local<v8::Value> listener, bool html)
+{
return FindEventListenerInList(m_xhr_listeners, listener, html);
}
-PassRefPtr<V8EventListener>
-V8Proxy::FindOrCreateXHREventListener(v8::Local<v8::Value> obj,
- bool html) {
+PassRefPtr<V8EventListener> V8Proxy::FindOrCreateXHREventListener(
+ v8::Local<v8::Value> obj, bool html)
+{
ASSERT(v8::Context::InContext());
if (!obj->IsObject()) return 0;
@@ -922,7 +947,8 @@ V8Proxy::FindOrCreateXHREventListener(v8::Local<v8::Value> obj,
static void RemoveEventListenerFromList(V8EventListenerList& list,
- V8EventListener* listener) {
+ V8EventListener* listener)
+{
V8EventListenerList::iterator p = list.begin();
while (p != list.end()) {
if (*p == listener) {
@@ -934,17 +960,20 @@ static void RemoveEventListenerFromList(V8EventListenerList& list,
}
-void V8Proxy::RemoveV8EventListener(V8EventListener* listener) {
+void V8Proxy::RemoveV8EventListener(V8EventListener* listener)
+{
RemoveEventListenerFromList(m_event_listeners, listener);
}
-void V8Proxy::RemoveXHREventListener(V8XHREventListener* listener) {
+void V8Proxy::RemoveXHREventListener(V8XHREventListener* listener)
+{
RemoveEventListenerFromList(m_xhr_listeners, listener);
}
-static void DisconnectEventListenersInList(V8EventListenerList& list) {
+static void DisconnectEventListenersInList(V8EventListenerList& list)
+{
V8EventListenerList::iterator p = list.begin();
while (p != list.end()) {
(*p)->disconnectFrame();
@@ -954,7 +983,8 @@ static void DisconnectEventListenersInList(V8EventListenerList& list) {
}
-void V8Proxy::DisconnectEventListeners() {
+void V8Proxy::DisconnectEventListeners()
+{
DisconnectEventListenersInList(m_event_listeners);
DisconnectEventListenersInList(m_xhr_listeners);
}
@@ -985,7 +1015,7 @@ bool V8Proxy::HandleOutOfMemory()
V8Proxy* proxy = V8Proxy::retrieve(frame);
// Clean m_context, m_document, and event handlers.
- proxy->clear();
+ proxy->clearForClose();
// Destroy the global object.
proxy->DestroyGlobal();
@@ -1020,7 +1050,8 @@ v8::Local<v8::Value> V8Proxy::Evaluate(const String& fileName, int baseLine,
}
v8::Local<v8::Value> V8Proxy::RunScript(v8::Handle<v8::Script> script,
- bool inline_code) {
+ bool inline_code)
+{
if (script.IsEmpty())
return v8::Local<v8::Value>();
@@ -1082,7 +1113,8 @@ v8::Local<v8::Value> V8Proxy::RunScript(v8::Handle<v8::Script> script,
v8::Local<v8::Value> V8Proxy::CallFunction(v8::Handle<v8::Function> function,
v8::Handle<v8::Object> receiver,
int argc,
- v8::Handle<v8::Value> args[]) {
+ v8::Handle<v8::Value> args[])
+{
// For now, we don't put any artificial limitations on the depth
// of recursion that stems from calling functions. This is in
// contrast to the script evaluations.
@@ -1108,7 +1140,8 @@ v8::Local<v8::Value> V8Proxy::CallFunction(v8::Handle<v8::Function> function,
v8::Persistent<v8::FunctionTemplate> V8Proxy::GetTemplate(
- V8ClassIndex::V8WrapperType type) {
+ V8ClassIndex::V8WrapperType type)
+{
v8::Persistent<v8::FunctionTemplate>* cache_cell =
V8ClassIndex::GetCache(type);
if (!(*cache_cell).IsEmpty()) return *cache_cell;
@@ -1165,14 +1198,14 @@ v8::Persistent<v8::FunctionTemplate> V8Proxy::GetTemplate(
0,
0,
0,
- v8::External::New(reinterpret_cast<void*>(V8ClassIndex::NODE)));
+ v8::Integer::New(V8ClassIndex::NODE));
desc->InstanceTemplate()->SetIndexedPropertyHandler(
NodeCollectionIndexedPropertyGetter<HTMLSelectElement>,
USE_INDEXED_PROPERTY_SETTER(HTMLSelectElementCollection),
0,
0,
NodeCollectionIndexedPropertyEnumerator<HTMLSelectElement>,
- v8::External::New(reinterpret_cast<void*>(V8ClassIndex::NODE)));
+ v8::Integer::New(V8ClassIndex::NODE));
break;
case V8ClassIndex::HTMLDOCUMENT: {
desc->InstanceTemplate()->SetNamedPropertyHandler(
@@ -1238,7 +1271,7 @@ v8::Persistent<v8::FunctionTemplate> V8Proxy::GetTemplate(
0,
0,
NodeCollectionIndexedPropertyEnumerator<HTMLFormElement>,
- v8::External::New(reinterpret_cast<void*>(V8ClassIndex::NODE)));
+ v8::Integer::New(V8ClassIndex::NODE));
break;
case V8ClassIndex::CANVASPIXELARRAY:
desc->InstanceTemplate()->SetIndexedPropertyHandler(
@@ -1274,7 +1307,7 @@ v8::Persistent<v8::FunctionTemplate> V8Proxy::GetTemplate(
0,
0,
CollectionIndexedPropertyEnumerator<NamedNodeMap>,
- v8::External::New(reinterpret_cast<void*>(V8ClassIndex::NODE)));
+ v8::Integer::New(V8ClassIndex::NODE));
break;
case V8ClassIndex::NODELIST:
SetCollectionIndexedGetter<NodeList, Node>(desc, V8ClassIndex::NODE);
@@ -1324,7 +1357,16 @@ v8::Persistent<v8::FunctionTemplate> V8Proxy::GetTemplate(
v8::Local<v8::ObjectTemplate> instance_template =
desc->InstanceTemplate();
instance_template->SetInternalFieldCount(
- V8Custom::kDOMWindowInternalFieldCount);
+ V8Custom::kDOMWindowInternalFieldCount);
+
+ // Set access check callbacks, but turned off initially.
+ // When a context is detached from a frame, turn on the access check.
+ // Turning on checks also invalidates inline caches of the object.
+ instance_template->SetAccessCheckCallbacks(
+ V8Custom::v8DOMWindowNamedSecurityCheck,
+ V8Custom::v8DOMWindowIndexedSecurityCheck,
+ v8::Integer::New(V8ClassIndex::DOMWINDOW),
+ false);
break;
}
case V8ClassIndex::LOCATION: {
@@ -1374,31 +1416,36 @@ v8::Persistent<v8::FunctionTemplate> V8Proxy::GetTemplate(
return desc;
}
+
bool V8Proxy::ContextInitialized()
{
return !m_context.IsEmpty();
}
+
DOMWindow* V8Proxy::retrieveWindow()
{
// TODO: This seems very fragile. How do we know that the global object
// from the current context is something sensible? Do we need to use the
// last entered here? Who calls this?
v8::Handle<v8::Object> global = v8::Context::GetCurrent()->Global();
- if (global.IsEmpty())
- return 0;
- v8::Handle<v8::Value> window = global->GetPrototype();
- return ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, window);
+ ASSERT(!global.IsEmpty());
+ global = LookupDOMWrapper(V8ClassIndex::DOMWINDOW, global);
+ ASSERT(!global.IsEmpty());
+ return ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, global);
}
+
Frame* V8Proxy::retrieveFrame(v8::Handle<v8::Context> context)
{
v8::Handle<v8::Object> global = context->Global();
- v8::Handle<v8::Value> window_peer = global->GetPrototype();
- DOMWindow* window = ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, window_peer);
+ global = LookupDOMWrapper(V8ClassIndex::DOMWINDOW, global);
+ ASSERT(!global.IsEmpty());
+ DOMWindow* window = ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, global);
return window->frame();
}
+
Frame* V8Proxy::retrieveActiveFrame()
{
v8::Handle<v8::Context> context = v8::Context::GetEntered();
@@ -1407,12 +1454,14 @@ Frame* V8Proxy::retrieveActiveFrame()
return retrieveFrame(context);
}
+
Frame* V8Proxy::retrieveFrame()
{
DOMWindow* window = retrieveWindow();
return window ? window->frame() : 0;
}
+
V8Proxy* V8Proxy::retrieve()
{
DOMWindow* window = retrieveWindow();
@@ -1432,11 +1481,12 @@ void V8Proxy::disconnectFrame()
// disconnect all event listeners
DisconnectEventListeners();
- // clear all timeouts.
+ // remove all timeouts
if (m_frame->domWindow())
- m_frame->domWindow()->clearAllTimeouts();
+ m_frame->domWindow()->clearAllTimeouts();
}
+
bool V8Proxy::isEnabled()
{
Settings* settings = m_frame->settings();
@@ -1481,7 +1531,6 @@ bool V8Proxy::isEnabled()
void V8Proxy::clearDocumentWrapper()
{
- v8::HandleScope handle_scope;
v8::Local<v8::Context> context = GetContext();
if (context.IsEmpty())
return; // not initialize yet
@@ -1499,56 +1548,90 @@ void V8Proxy::clearDocumentWrapper()
void V8Proxy::DomainChanged(Frame* frame)
{
V8Proxy* proxy = retrieve(frame);
- proxy->ClearSecurityToken();
+ // Restore to default security token.
+ proxy->m_context->UseDefaultSecurityToken();
}
-void V8Proxy::ClearSecurityToken()
+
+void V8Proxy::clearForClose()
{
- m_context->SetSecurityToken(m_global);
+ if (m_context.IsEmpty())
+ return;
+
+ { v8::HandleScope handle_scope;
+ clearDocumentWrapper();
+ }
+
+ m_context.Dispose();
+ m_context.Clear();
}
-void V8Proxy::clear()
+
+void V8Proxy::clearForNavigation()
{
if (m_context.IsEmpty())
return;
- ClearSecurityToken();
+ { v8::HandleScope handle;
+ clearDocumentWrapper();
- if (m_frame->domWindow())
- m_frame->domWindow()->clearAllTimeouts();
+ // Turn on access check on the old DOMWindow wrapper.
+ v8::Handle<v8::Object> wrapper =
+ LookupDOMWrapper(V8ClassIndex::DOMWINDOW, m_global);
+ ASSERT(!wrapper.IsEmpty());
+ wrapper->TurnOnAccessCheck();
- clearDocumentWrapper();
+ // Clear all timeouts.
+ DOMWindow* domWindow =
+ ToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, wrapper);
+ domWindow->clearAllTimeouts();
+
+ // Separate the context from its global object.
+ m_context->DetachGlobal();
+ }
// Corresponds to the context creation in initContextIfNeeded().
m_context.Dispose();
m_context.Clear();
}
-// Check if two frames are from the same origin.
-// This function is equivalent to
-// KJS::Window::allowsAccessFrom(const JSGlobalObject*,
-// SecurityOrigin::Reason&, String& message) const.
-static bool SameOrigin(Frame* source, Frame* target, String& message)
+
+void V8Proxy::domWindowReady()
{
- if (!source || !target)
- return false;
+ // Reinitialize context so the global object is reused by
+ // the new context.
+ if (!m_global.IsEmpty() && m_context.IsEmpty()) {
+ v8::HandleScope scope;
+ initContextIfNeeded();
+ }
+}
- // Allow access if the frames the windows represent are the same.
- if (source == target)
- return true;
- Document* target_document = target->document();
+// Check if the current execution context can access a target frame.
+// First it checks same domain policy using the lexical context
+//
+// This is equivalent to KJS::Window::allowsAccessFrom(ExecState*, String&).
+bool V8Proxy::CanAccessPrivate(DOMWindow* target_window)
+{
+ ASSERT(target_window);
+
+ String message;
- // JS may be attempting to access the "window" object, which should be valid,
- // even if the document hasn't been constructed yet. If the document doesn't
- // exist yet allow JS to access the window object.
- if (!target_document)
+ DOMWindow* origin_window = retrieveWindow();
+ if (origin_window == target_window)
return true;
- Document* act_document = source->document();
+ if (!origin_window)
+ return false;
+
+ // JS may be attempting to access the "window" object, which should be
+ // valid, even if the document hasn't been constructed yet.
+ // If the document doesn't exist yet allow JS to access the window object.
+ if (!origin_window->document())
+ return true;
- const SecurityOrigin* active_security_origin = act_document->securityOrigin();
- const SecurityOrigin* target_security_origin = target_document->securityOrigin();
+ const SecurityOrigin* active_security_origin = origin_window->securityOrigin();
+ const SecurityOrigin* target_security_origin = target_window->securityOrigin();
String ui_resource_protocol = webkit_glue::StdStringToString(webkit_glue::GetUIResourceProtocol());
if (active_security_origin->protocol() == ui_resource_protocol) {
@@ -1568,39 +1651,23 @@ static bool SameOrigin(Frame* source, Frame* target, String& message)
if (active_security_origin->canAccess(target_security_origin))
return true;
- return false;
-}
-
-// Check if the current execution context can access a target frame.
-// First it checks same domain policy using the security context
-// (where the script is invoked), if domain check failed due to
-// setting document.domain, using the lexical context of the function
-// to check domain policy.
-//
-// This is equivalent to KJS::Window::allowsAccessFrom(ExecState*, String&).
-bool V8Proxy::CanAccess(Frame* target)
-{
- String message;
-
- // Check dynamic (security) context first.
- Frame* source = V8Proxy::retrieveFrame(v8::Context::GetCurrentSecurityContext());
- if (SameOrigin(source, target, message))
- return true;
-
- source = V8Proxy::retrieveFrame(v8::Context::GetCurrent());
- if (SameOrigin(source, target, message))
+ // Allow access to a "about:blank" page if the dynamic context is a
+ // detached context of the same frame as the blank page.
+ if (target_security_origin->isEmpty() &&
+ origin_window->frame() == target_window->frame())
return true;
return false;
}
-bool V8Proxy::IsFromSameOrigin(Frame* target, bool report_error)
+
+bool V8Proxy::CanAccessFrame(Frame* target, bool report_error)
{
// The subject is detached from a frame, deny accesses.
if (!target)
return false;
- if (!CanAccess(target)) {
+ if (!CanAccessPrivate(target->domWindow())) {
if (report_error)
ReportUnsafeAccessTo(target, REPORT_NOW);
return false;
@@ -1608,6 +1675,7 @@ bool V8Proxy::IsFromSameOrigin(Frame* target, bool report_error)
return true;
}
+
bool V8Proxy::CheckNodeSecurity(Node* node)
{
if (!node)
@@ -1618,9 +1686,39 @@ bool V8Proxy::CheckNodeSecurity(Node* node)
if (!target)
return false;
- return IsFromSameOrigin(target, true);
+ return CanAccessFrame(target, true);
}
+
+static void GenerateSecurityToken(v8::Local<v8::Context> context)
+{
+ Document* document = V8Proxy::retrieveFrame(context)->document();
+ if (!document) {
+ context->UseDefaultSecurityToken();
+ return;
+ }
+
+ // Ask the document's SecurityOrigin to generate a security token.
+ // If two tokens are equal, then the SecurityOrigins canAccess each other.
+ // If two tokens are not equal, then we have to call canAccess.
+ String token = document->securityOrigin()->securityToken();
+
+ // An empty token means we always have to call canAccess. In this case, we
+ // use the global object as the security token to avoid calling canAccess
+ // when a script accesses its own objects.
+ if (token.isEmpty()) {
+ context->UseDefaultSecurityToken();
+ return;
+ }
+
+ CString utf8_token = token.utf8();
+ // NOTE: V8 does identity comparison in fast path, must use a symbol
+ // as the security token.
+ context->SetSecurityToken(
+ v8::String::NewSymbol(utf8_token.data(), utf8_token.length()));
+}
+
+
// Create a new environment and setup the global object.
//
// The global object corresponds to a DOMWindow instance. However, to
@@ -1634,6 +1732,29 @@ bool V8Proxy::CheckNodeSecurity(Node* node)
// 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.
+
void V8Proxy::initContextIfNeeded()
{
// Bail out if the context has already been initialized.
@@ -1673,18 +1794,12 @@ void V8Proxy::initContextIfNeeded()
return;
// Install a security handler with V8.
- {
- v8::Local<v8::External> external =
- v8::External::New(reinterpret_cast<void*>(V8ClassIndex::DOMWINDOW));
- if (external.IsEmpty())
- return;
-
- global_template->SetAccessCheckCallbacks(
- V8Custom::v8DOMWindowNamedSecurityCheck,
- V8Custom::v8DOMWindowIndexedSecurityCheck,
- external);
- }
+ global_template->SetAccessCheckCallbacks(
+ V8Custom::v8DOMWindowNamedSecurityCheck,
+ V8Custom::v8DOMWindowIndexedSecurityCheck,
+ v8::Integer::New(V8ClassIndex::DOMWINDOW));
+ // Create a new context.
m_context = v8::Context::New(NULL, global_template, m_global);
if (m_context.IsEmpty())
return;
@@ -1712,9 +1827,6 @@ void V8Proxy::initContextIfNeeded()
DOMWindow* window = m_frame->domWindow();
- // Get rid of the old window peer object if one exists.
- dom_object_map().forget(window);
-
// Setup the peer object for the DOM window.
dom_object_map().set(window, v8::Persistent<v8::Object>::New(window_peer));
// Wrap the window.
@@ -1725,7 +1837,8 @@ void V8Proxy::initContextIfNeeded()
v8::Handle<v8::Object> v8_global = context->Global();
v8_global->Set(v8::String::New("__proto__"), window_peer);
- context->SetSecurityToken(GenerateSecurityToken(context));
+ // Setup security origin and security token
+ GenerateSecurityToken(context);
V8Proxy::retrieveFrame(context)->loader()->dispatchWindowObjectAvailable();
@@ -1752,30 +1865,6 @@ void V8Proxy::initContextIfNeeded()
}
-v8::Handle<v8::Value> V8Proxy::GenerateSecurityToken(
- v8::Local<v8::Context> context) {
- Document* document = V8Proxy::retrieveFrame(context)->document();
- if (!document)
- return context->Global();
-
- // Ask the document's SecurityOrigin to generate a security token.
- // If two tokens are equal, then the SecurityOrigins canAccess each other.
- // If two tokens are not equal, then we have to call canAccess.
- String token = document->securityOrigin()->securityToken();
-
- // An empty token means we always have to call canAccess. In this case, we
- // use the global object as the security token to avoid calling canAccess
- // when a script accesses its own objects.
- if (token.isEmpty())
- return context->Global();
-
- CString utf8_token = token.utf8();
- // NOTE: V8 does identity comparison in fast path, must use a symbol
- // as the security token.
- return v8::String::NewSymbol(utf8_token.data(), utf8_token.length());
-}
-
-
void V8Proxy::SetDOMException(int exception_code)
{
if (exception_code <= 0)
@@ -1922,7 +2011,8 @@ v8::Handle<v8::Value> V8Proxy::ToV8Object(V8ClassIndex::V8WrapperType type, void
void V8Proxy::SetHiddenWindowReference(Frame* frame,
const int internal_index,
- v8::Handle<v8::Object> jsobj) {
+ v8::Handle<v8::Object> jsobj)
+{
// Get DOMWindow
if (!frame) return; // Object might be detached from window
v8::Handle<v8::Context> context = GetContext(frame);
@@ -1931,9 +2021,9 @@ void V8Proxy::SetHiddenWindowReference(Frame* frame,
ASSERT(internal_index < V8Custom::kDOMWindowInternalFieldCount);
v8::Handle<v8::Object> global = context->Global();
- ASSERT(!global.IsEmpty());
// Look for real DOM wrapper.
global = LookupDOMWrapper(V8ClassIndex::DOMWINDOW, global);
+ ASSERT(!global.IsEmpty());
ASSERT(global->GetInternalField(internal_index)->IsUndefined());
global->SetInternalField(internal_index, jsobj);
}
@@ -1949,7 +2039,8 @@ V8ClassIndex::V8WrapperType V8Proxy::GetDOMWrapperType(v8::Handle<v8::Object> ob
void* V8Proxy::ToNativeObjectImpl(V8ClassIndex::V8WrapperType type,
- v8::Handle<v8::Value> object) {
+ v8::Handle<v8::Value> object)
+{
// Native event listener is per frame, it cannot be handled
// by this generic function.
ASSERT(type != V8ClassIndex::EVENTLISTENER);
@@ -2020,7 +2111,8 @@ PassRefPtr<NodeFilter> V8Proxy::ToNativeNodeFilter(v8::Handle<v8::Value> filter)
v8::Local<v8::Object> V8Proxy::InstantiateV8Object(
V8ClassIndex::V8WrapperType desc_type,
V8ClassIndex::V8WrapperType cptr_type,
- void* imp) {
+ void* imp)
+{
// Make a special case for document.all
if (desc_type == V8ClassIndex::HTMLCOLLECTION &&
static_cast<HTMLCollection*>(imp)->type() == HTMLCollection::DocAll) {
@@ -2052,7 +2144,8 @@ v8::Handle<v8::Value> V8Proxy::CheckNewLegal(const v8::Arguments& args)
return args.This();
}
-void V8Proxy::SetDOMWrapper(v8::Handle<v8::Object> obj, int type, void* cptr) {
+void V8Proxy::SetDOMWrapper(v8::Handle<v8::Object> obj, int type, void* cptr)
+{
ASSERT(obj->InternalFieldCount() >= 2);
obj->SetInternalField(V8Custom::kDOMWrapperObjectIndex, WrapCPointer(cptr));
obj->SetInternalField(V8Custom::kDOMWrapperTypeIndex, v8::Integer::New(type));
@@ -2060,7 +2153,8 @@ void V8Proxy::SetDOMWrapper(v8::Handle<v8::Object> obj, int type, void* cptr) {
#ifndef NDEBUG
-bool V8Proxy::MaybeDOMWrapper(v8::Handle<v8::Value> value) {
+bool V8Proxy::MaybeDOMWrapper(v8::Handle<v8::Value> value)
+{
if (value.IsEmpty() || !value->IsObject()) return false;
v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(value);
@@ -2084,14 +2178,16 @@ bool V8Proxy::MaybeDOMWrapper(v8::Handle<v8::Value> value) {
#endif
-bool V8Proxy::IsDOMEventWrapper(v8::Handle<v8::Value> value) {
+bool V8Proxy::IsDOMEventWrapper(v8::Handle<v8::Value> value)
+{
// All kinds of events use EVENT as dom type in JS wrappers.
// See EventToV8Object
return IsWrapperOfType(value, V8ClassIndex::EVENT);
}
bool V8Proxy::IsWrapperOfType(v8::Handle<v8::Value> value,
- V8ClassIndex::V8WrapperType classType) {
+ V8ClassIndex::V8WrapperType classType)
+{
if (value.IsEmpty() || !value->IsObject()) return false;
v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(value);
@@ -2186,7 +2282,8 @@ bool V8Proxy::IsWrapperOfType(v8::Handle<v8::Value> value,
macro(ul, ULIST) \
macro(xmp, PRE)
-V8ClassIndex::V8WrapperType V8Proxy::GetHTMLElementType(HTMLElement* element) {
+V8ClassIndex::V8WrapperType V8Proxy::GetHTMLElementType(HTMLElement* element)
+{
static HashMap<String, V8ClassIndex::V8WrapperType> map;
if (map.isEmpty()) {
#define ADD_TO_HASH_MAP(tag, name) \
@@ -2311,7 +2408,8 @@ FOR_EACH_TAG(ADD_TO_HASH_MAP)
macro(view, VIEW) \
// end of macro
-V8ClassIndex::V8WrapperType V8Proxy::GetSVGElementType(SVGElement* element) {
+V8ClassIndex::V8WrapperType V8Proxy::GetSVGElementType(SVGElement* element)
+{
static HashMap<String, V8ClassIndex::V8WrapperType> map;
if (map.isEmpty()) {
#define ADD_TO_HASH_MAP(tag, name) \
@@ -2389,7 +2487,8 @@ v8::Handle<v8::Value> V8Proxy::EventToV8Object(Event* event)
// Caller checks node is not null.
-v8::Handle<v8::Value> V8Proxy::NodeToV8Object(Node* node) {
+v8::Handle<v8::Value> V8Proxy::NodeToV8Object(Node* node)
+{
if (!node) return v8::Null();
v8::Handle<v8::Object> peer = dom_node_map().get(node);
@@ -2511,7 +2610,8 @@ v8::Handle<v8::Value> V8Proxy::NodeToV8Object(Node* node) {
}
-void V8Proxy::UpdateDocumentHandle(v8::Local<v8::Object> handle) {
+void V8Proxy::UpdateDocumentHandle(v8::Local<v8::Object> handle)
+{
// If the old handle is not empty, release it.
if (!m_document.IsEmpty()) {
#ifndef NDEBUG
@@ -2559,7 +2659,8 @@ v8::Handle<v8::Value> V8Proxy::EventTargetToV8Object(EventTarget* target)
v8::Handle<v8::Value> V8Proxy::EventListenerToV8Object(
- EventListener* listener) {
+ EventListener* listener)
+{
if (listener == 0) return v8::Null();
// TODO(fqian): can a user take a lazy event listener and set to other places?
@@ -2570,7 +2671,8 @@ v8::Handle<v8::Value> V8Proxy::EventListenerToV8Object(
v8::Handle<v8::Value> V8Proxy::DOMImplementationToV8Object(
- DOMImplementation* impl) {
+ DOMImplementation* impl)
+{
v8::Handle<v8::Object> result =
InstantiateV8Object(V8ClassIndex::DOMIMPLEMENTATION,
V8ClassIndex::DOMIMPLEMENTATION,
@@ -2584,7 +2686,8 @@ v8::Handle<v8::Value> V8Proxy::DOMImplementationToV8Object(
}
-v8::Handle<v8::Value> V8Proxy::StyleSheetToV8Object(StyleSheet* sheet) {
+v8::Handle<v8::Value> V8Proxy::StyleSheetToV8Object(StyleSheet* sheet)
+{
if (!sheet) return v8::Null();
v8::Handle<v8::Object> peer = dom_object_map().get(sheet);
@@ -2614,7 +2717,8 @@ v8::Handle<v8::Value> V8Proxy::StyleSheetToV8Object(StyleSheet* sheet) {
}
-v8::Handle<v8::Value> V8Proxy::CSSValueToV8Object(CSSValue* value) {
+v8::Handle<v8::Value> V8Proxy::CSSValueToV8Object(CSSValue* value)
+{
if (!value) return v8::Null();
v8::Handle<v8::Object> peer = dom_object_map().get(value);
@@ -2647,7 +2751,8 @@ v8::Handle<v8::Value> V8Proxy::CSSValueToV8Object(CSSValue* value) {
}
-v8::Handle<v8::Value> V8Proxy::CSSRuleToV8Object(CSSRule* rule) {
+v8::Handle<v8::Value> V8Proxy::CSSRuleToV8Object(CSSRule* rule)
+{
if (!rule) return v8::Null();
v8::Handle<v8::Object> peer = dom_object_map().get(rule);
@@ -2698,7 +2803,8 @@ v8::Handle<v8::Value> V8Proxy::CSSRuleToV8Object(CSSRule* rule) {
return result;
}
-v8::Handle<v8::Value> V8Proxy::WindowToV8Object(DOMWindow* window) {
+v8::Handle<v8::Value> V8Proxy::WindowToV8Object(DOMWindow* window)
+{
if (!window) return v8::Null();
// Initializes environment of a frame, and return the global object
// of the frame.