diff options
author | sgjesse@chromium.org <sgjesse@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-20 11:01:33 +0000 |
---|---|---|
committer | sgjesse@chromium.org <sgjesse@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-20 11:01:33 +0000 |
commit | 349feecde71c4d71834870c88bb701830e75f927 (patch) | |
tree | b415ba0265ecfd07bf1cb650f09facff53fc86b6 /webkit/port/bindings | |
parent | 292c90d8dfa53f696f79d6ba50e9699146a9602d (diff) | |
download | chromium_src-349feecde71c4d71834870c88bb701830e75f927.zip chromium_src-349feecde71c4d71834870c88bb701830e75f927.tar.gz chromium_src-349feecde71c4d71834870c88bb701830e75f927.tar.bz2 |
Added GC protection support for objects with possible pending activity which is only backed by weak wrappers.
The objects which can have pending activity are XMLHttpRequest and MessagePort. In WebKit these two classes are both instances of ActiveDOMObject. In the V8 binding layer these two types of objects are now beeing tracked so that before GC their wrappers can be made not weak if there is currently pending activity. In addition to that MessagePort wrappers now have an additional internal field to reference their entangled port when entangled.
Before MessagePort where added to WebKit pending activity for XMLHttpRequest was handled through a direct GC protect mechanism which protected/unprotected the object when its state changed. However, using this for MessagePort would require an GC protection check in each MessagePort message as the transition to/from pending is not as explicit as for XMLHttpRequest.
Changed direct calls to dom_object_map().set(...) to use SetJSWrapperForDOMObject and added an ASSERT to check that SetJSWrapperForDOMObject is called with a DOM wrapper object. Changed the order of calls to SetDOMWrapper and SetJSWrapperForDOMObject to make sure that SetDOMWrapper is called first.
This fixes 3 GC related layout tests
chrome/fast/dom/xmlhttprequest-gc.html
LayoutTests/fast/events/message-channel-gc-2.html
LayoutTests/fast/events/message-channel-gc-3.html
Added a chrome specific GC layout test for MessagePort (chrome/fast/dom/xmlhttprequest-gc.html) to match the existing one for XMLHttpRequest (chrome/fast/dom/xmlhttprequest-gc.html).
This change forks MessagePort.cpp.
Review URL: http://codereview.chromium.org/11205
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@5770 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/port/bindings')
-rw-r--r-- | webkit/port/bindings/v8/ScriptController.cpp | 31 | ||||
-rw-r--r-- | webkit/port/bindings/v8/ScriptController.h | 5 | ||||
-rw-r--r-- | webkit/port/bindings/v8/V8XMLHttpRequestCustom.cpp | 2 | ||||
-rw-r--r-- | webkit/port/bindings/v8/v8_custom.h | 4 | ||||
-rw-r--r-- | webkit/port/bindings/v8/v8_proxy.cpp | 137 | ||||
-rw-r--r-- | webkit/port/bindings/v8/v8_proxy.h | 4 |
6 files changed, 168 insertions, 15 deletions
diff --git a/webkit/port/bindings/v8/ScriptController.cpp b/webkit/port/bindings/v8/ScriptController.cpp index 7eda483..bf4b029 100644 --- a/webkit/port/bindings/v8/ScriptController.cpp +++ b/webkit/port/bindings/v8/ScriptController.cpp @@ -110,6 +110,37 @@ void ScriptController::gcUnprotectJSWrapper(void* dom_object) V8Proxy::GCUnprotect(static_cast<Peerable*>(dom_object)); } +void ScriptController::entangleMessagePorts(MessagePort *port1, + MessagePort *port2) +{ + // When two message ports are entangled make sure that the two wrappers are + // also entangled. If one of the ports has pending activity it's wrapper + // will be protected from beeing GC'ed and by entangeling the wrappers the + // wrapper for the entangled port will also be protected. + v8::Handle<v8::Value> port1_wrapper = + V8Proxy::ToV8Object(V8ClassIndex::MESSAGEPORT, port1); + v8::Handle<v8::Value> port2_wrapper = + V8Proxy::ToV8Object(V8ClassIndex::MESSAGEPORT, port2); + ASSERT(port1_wrapper->IsObject()); + v8::Handle<v8::Object>::Cast(port1_wrapper)->SetInternalField( + V8Custom::kMessagePortEntangledPortIndex, port2_wrapper); + ASSERT(port2_wrapper->IsObject()); + v8::Handle<v8::Object>::Cast(port2_wrapper)->SetInternalField( + V8Custom::kMessagePortEntangledPortIndex, port1_wrapper); +} + +void ScriptController::unentangleMessagePort(MessagePort *port) +{ + // Remove the wrapper entanglement when a port is unentangled. + if (port->peer() != NULL) { + v8::Handle<v8::Value> wrapper = + V8Proxy::ToV8Object(V8ClassIndex::MESSAGEPORT, port); + ASSERT(wrapper->IsObject()); + v8::Handle<v8::Object>::Cast(wrapper)->SetInternalField( + V8Custom::kMessagePortEntangledPortIndex, v8::Undefined()); + } +} + void ScriptController::pauseTimeouts(OwnPtr<PausedTimeouts>& result) { DOMWindow* window = m_frame->domWindow(); diff --git a/webkit/port/bindings/v8/ScriptController.h b/webkit/port/bindings/v8/ScriptController.h index 34de5f0..da87bf6 100644 --- a/webkit/port/bindings/v8/ScriptController.h +++ b/webkit/port/bindings/v8/ScriptController.h @@ -34,6 +34,7 @@ #define ScriptController_h #include "HashMap.h" +#include "MessagePort.h" #include "SecurityOrigin.h" #include "bindings/npruntime.h" @@ -209,6 +210,10 @@ public: static void gcProtectJSWrapper(void* object); static void gcUnprotectJSWrapper(void* object); + // Handle entangle/unentangle of message ports. + static void entangleMessagePorts(MessagePort *port1, MessagePort *port2); + static void unentangleMessagePort(MessagePort *port); + // Get/Set RecordPlaybackMode flag. // This is a special mode where JS helps the browser implement // playback/record mode. Generally, in this mode, some functions diff --git a/webkit/port/bindings/v8/V8XMLHttpRequestCustom.cpp b/webkit/port/bindings/v8/V8XMLHttpRequestCustom.cpp index e7a3702..3a9653c 100644 --- a/webkit/port/bindings/v8/V8XMLHttpRequestCustom.cpp +++ b/webkit/port/bindings/v8/V8XMLHttpRequestCustom.cpp @@ -59,7 +59,7 @@ CALLBACK_FUNC_DECL(XMLHttpRequestConstructor) { V8Proxy::SetDOMWrapper(args.Holder(), V8ClassIndex::ToInt(V8ClassIndex::XMLHTTPREQUEST), xhr.get()); // Set object as the peer. - V8Proxy::SetJSWrapperForDOMObject(xhr.get(), + V8Proxy::SetJSWrapperForActiveDOMObject(xhr.get(), v8::Persistent<v8::Object>::New(args.Holder())); return args.Holder(); } diff --git a/webkit/port/bindings/v8/v8_custom.h b/webkit/port/bindings/v8/v8_custom.h index d3bee4c..de64bd7 100644 --- a/webkit/port/bindings/v8/v8_custom.h +++ b/webkit/port/bindings/v8/v8_custom.h @@ -63,8 +63,10 @@ class V8Custom { static const int kMessagePortRequestCacheIndex = kDefaultWrapperInternalFieldCount + 0; - static const int kMessagePortInternalFieldCount = + static const int kMessagePortEntangledPortIndex = kDefaultWrapperInternalFieldCount + 1; + static const int kMessagePortInternalFieldCount = + kDefaultWrapperInternalFieldCount + 2; static const int kDOMWindowLocationIndex = kDefaultWrapperInternalFieldCount + 0; diff --git a/webkit/port/bindings/v8/v8_proxy.cpp b/webkit/port/bindings/v8/v8_proxy.cpp index 822c298..a39f06b 100644 --- a/webkit/port/bindings/v8/v8_proxy.cpp +++ b/webkit/port/bindings/v8/v8_proxy.cpp @@ -279,7 +279,9 @@ class DOMPeerableWrapperMap : public DOMWrapperMap<T> { }; -static void WeakPeerableCallback(v8::Persistent<v8::Value> obj, void* para); +static void WeakDOMObjectCallback(v8::Persistent<v8::Value> obj, void* para); +static void WeakActiveDOMObjectCallback(v8::Persistent<v8::Value> obj, + void* para); static void WeakNodeCallback(v8::Persistent<v8::Value> obj, void* para); // A map from DOM node to its JS wrapper. static DOMWrapperMap<Node>& dom_node_map() @@ -289,14 +291,24 @@ static DOMWrapperMap<Node>& dom_node_map() } -// A map from a non-DOM node (peerable) to its JS wrapper. +// A map from a non-DOM node (peerable) to its JS wrapper. This map does not +// contain the DOM objects which can have pending activity. static DOMWrapperMap<Peerable>& dom_object_map() { static DOMPeerableWrapperMap<Peerable> - static_dom_object_map(&WeakPeerableCallback); + static_dom_object_map(&WeakDOMObjectCallback); return static_dom_object_map; } +// A map from a non-DOM node (peerable) to its JS wrapper for DOM objects which +// can have pending activity. +static DOMWrapperMap<Peerable>& active_dom_object_map() +{ + static DOMPeerableWrapperMap<Peerable> + static_active_dom_object_map(&WeakActiveDOMObjectCallback); + return static_active_dom_object_map; +} + #if ENABLE(SVG) static void WeakSVGElementInstanceCallback(v8::Persistent<v8::Value> obj, void* param); @@ -431,7 +443,7 @@ 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::Value> obj, void* para) +static void WeakDOMObjectCallback(v8::Persistent<v8::Value> obj, void* para) { Peerable* dom_obj = static_cast<Peerable*>(para); ASSERT(dom_object_map().contains(dom_obj)); @@ -441,6 +453,17 @@ static void WeakPeerableCallback(v8::Persistent<v8::Value> obj, void* para) dom_object_map().forget(dom_obj); } +static void WeakActiveDOMObjectCallback(v8::Persistent<v8::Value> obj, + void* para) +{ + Peerable* dom_obj = static_cast<Peerable*>(para); + ASSERT(active_dom_object_map().contains(dom_obj)); + + // forget function removes object from the map, + // disposes the wrapper and clears the peer. + active_dom_object_map().forget(dom_obj); +} + static void WeakNodeCallback(v8::Persistent<v8::Value> obj, void* param) { Node* node = static_cast<Node*>(param); @@ -452,6 +475,8 @@ static void WeakNodeCallback(v8::Persistent<v8::Value> obj, void* param) // Create object groups for DOM tree nodes. static void GCPrologue() { + v8::HandleScope scope; + #ifndef NDEBUG // Check that all references in the map are weak. PeerableMap peer_map = dom_object_map().impl(); @@ -463,6 +488,28 @@ static void GCPrologue() } #endif + // Run through all objects with possible pending activity making their + // wrappers non weak if there is pending activity. + PeerableMap active_map = active_dom_object_map().impl(); + for (PeerableMap::iterator it = active_map.begin(), end = active_map.end(); + it != end; ++it) { + Peerable* obj = it->first; + v8::Persistent<v8::Object> wrapper = v8::Persistent<v8::Object>(it->second); + ASSERT(wrapper.IsWeak()); + V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); + if (type == V8ClassIndex::XMLHTTPREQUEST) { + XMLHttpRequest* xhr = static_cast<XMLHttpRequest*>(obj); + if (xhr->hasPendingActivity()) { + wrapper.ClearWeak(); + } + } else if (type == V8ClassIndex::MESSAGEPORT) { + MessagePort* message_port = static_cast<MessagePort*>(obj); + if (message_port->hasPendingActivity()) { + wrapper.ClearWeak(); + } + } + } + // Create object groups. NodeMap node_map = dom_node_map().impl(); for (NodeMap::iterator it = node_map.begin(), end = node_map.end(); @@ -499,6 +546,32 @@ static void GCPrologue() static void GCEpilogue() { + v8::HandleScope scope; + + // Run through all objects with pending activity making their wrappers weak + // again. + PeerableMap active_map = active_dom_object_map().impl(); + for (PeerableMap::iterator it = active_map.begin(), end = active_map.end(); + it != end; ++it) { + Peerable* obj = it->first; + v8::Persistent<v8::Object> wrapper = v8::Persistent<v8::Object>(it->second); + V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); + if (type == V8ClassIndex::XMLHTTPREQUEST) { + XMLHttpRequest* xhr = static_cast<XMLHttpRequest*>(obj); + if (xhr->hasPendingActivity()) { + ASSERT(!wrapper.IsWeak()); + wrapper.MakeWeak(xhr, &WeakActiveDOMObjectCallback); + } + } else if (type == V8ClassIndex::MESSAGEPORT) { + MessagePort* message_port = static_cast<MessagePort*>(obj); + if (message_port->hasPendingActivity()) { + ASSERT(!wrapper.IsWeak()); + wrapper.MakeWeak(message_port, &WeakActiveDOMObjectCallback); + } + } + ASSERT(wrapper.IsWeak()); + } + #ifndef NDEBUG // Check all survivals are weak. PeerableMap peer_map = dom_object_map().impl(); @@ -817,11 +890,29 @@ void V8Proxy::DestroyGlobal() void V8Proxy::SetJSWrapperForDOMObject(Peerable* obj, v8::Persistent<v8::Object> wrapper) { + ASSERT(MaybeDOMWrapper(wrapper)); +#ifndef NDEBUG + V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); + ASSERT(type != V8ClassIndex::XMLHTTPREQUEST && + type != V8ClassIndex::MESSAGEPORT); +#endif dom_object_map().set(obj, wrapper); } +void V8Proxy::SetJSWrapperForActiveDOMObject(Peerable* obj, v8::Persistent<v8::Object> wrapper) +{ + ASSERT(MaybeDOMWrapper(wrapper)); +#ifndef NDEBUG + V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(wrapper); + ASSERT(type == V8ClassIndex::XMLHTTPREQUEST || + type == V8ClassIndex::MESSAGEPORT); +#endif + active_dom_object_map().set(obj, wrapper); +} + void V8Proxy::SetJSWrapperForDOMNode(Node* node, v8::Persistent<v8::Object> wrapper) { + ASSERT(MaybeDOMWrapper(wrapper)); dom_node_map().set(node, wrapper); } @@ -1862,12 +1953,13 @@ void V8Proxy::initContextIfNeeded() DOMWindow* window = m_frame->domWindow(); - // Setup the peer object for the DOM window. - dom_object_map().set(window, v8::Persistent<v8::Object>::New(window_peer)); // Wrap the window. SetDOMWrapper(window_peer, V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), window); + // Setup the peer object for the DOM window. + V8Proxy::SetJSWrapperForDOMObject(window, + v8::Persistent<v8::Object>::New(window_peer)); // Insert the window instance as the prototype of the shadow object. v8::Handle<v8::Object> v8_global = context->Global(); v8_global->Set(v8::String::New("__proto__"), window_peer); @@ -2002,6 +2094,9 @@ v8::Handle<v8::Value> V8Proxy::ToV8Object(V8ClassIndex::V8WrapperType type, void return StyleSheetToV8Object(static_cast<StyleSheet*>(imp)); case V8ClassIndex::DOMWINDOW: return WindowToV8Object(static_cast<DOMWindow*>(imp)); + case V8ClassIndex::XMLHTTPREQUEST: + case V8ClassIndex::MESSAGEPORT: + return ActiveDOMObjectToV8Object(type, static_cast<Peerable*>(imp)); #if ENABLE(SVG) SVGNONNODE_WRAPPER_TYPES(MAKE_CASE) if (type == V8ClassIndex::SVGELEMENTINSTANCE) @@ -2023,7 +2118,7 @@ v8::Handle<v8::Value> V8Proxy::ToV8Object(V8ClassIndex::V8WrapperType type, void v8::Local<v8::Object> v8obj = InstantiateV8Object(type, type, imp); if (!v8obj.IsEmpty()) { result = v8::Persistent<v8::Object>::New(v8obj); - dom_object_map().set(obj, result); + SetJSWrapperForDOMObject(obj, result); // Special case for Location and Navigator. Both Safari and FF let // Location and Navigator JS wrappers survive GC. To mimic their @@ -2516,7 +2611,7 @@ v8::Handle<v8::Value> V8Proxy::EventToV8Object(Event* event) return v8::Null(); } - dom_object_map().set(event, v8::Persistent<v8::Object>::New(result)); + SetJSWrapperForDOMObject(event, v8::Persistent<v8::Object>::New(result)); return result; } @@ -2664,7 +2759,7 @@ v8::Handle<v8::Value> V8Proxy::EventTargetToV8Object(EventTarget* target) // XMLHttpRequest is created within its JS counterpart. XMLHttpRequest* xhr = target->toXMLHttpRequest(); if (xhr) { - v8::Handle<v8::Object> peer = dom_object_map().get(xhr); + v8::Handle<v8::Object> peer = active_dom_object_map().get(xhr); ASSERT(!peer.IsEmpty()); return peer; } @@ -2672,7 +2767,7 @@ v8::Handle<v8::Value> V8Proxy::EventTargetToV8Object(EventTarget* target) // MessagePort is created within its JS counterpart MessagePort* port = target->toMessagePort(); if (port) { - v8::Handle<v8::Object> peer = dom_object_map().get(port); + v8::Handle<v8::Object> peer = active_dom_object_map().get(port); ASSERT(!peer.IsEmpty()); return peer; } @@ -2733,7 +2828,7 @@ v8::Handle<v8::Value> V8Proxy::StyleSheetToV8Object(StyleSheet* sheet) InstantiateV8Object(type, V8ClassIndex::STYLESHEET, sheet); if (!result.IsEmpty()) { // Only update the DOM object map if the result is non-empty. - dom_object_map().set(sheet, v8::Persistent<v8::Object>::New(result)); + SetJSWrapperForDOMObject(sheet, v8::Persistent<v8::Object>::New(result)); } // Add a hidden reference from stylesheet object to its owner node. @@ -2777,7 +2872,7 @@ v8::Handle<v8::Value> V8Proxy::CSSValueToV8Object(CSSValue* value) InstantiateV8Object(type, V8ClassIndex::CSSVALUE, value); if (!result.IsEmpty()) // Only update the DOM object map if the result is non-empty. - dom_object_map().set(value, v8::Persistent<v8::Object>::New(result)); + SetJSWrapperForDOMObject(value, v8::Persistent<v8::Object>::New(result)); return result; } @@ -2830,7 +2925,7 @@ v8::Handle<v8::Value> V8Proxy::CSSRuleToV8Object(CSSRule* rule) InstantiateV8Object(type, V8ClassIndex::CSSRULE, rule); if (!result.IsEmpty()) // Only update the DOM object map if the result is non-empty. - dom_object_map().set(rule, v8::Persistent<v8::Object>::New(result)); + SetJSWrapperForDOMObject(rule, v8::Persistent<v8::Object>::New(result)); return result; } @@ -2852,6 +2947,22 @@ v8::Handle<v8::Value> V8Proxy::WindowToV8Object(DOMWindow* window) return global; } +v8::Handle<v8::Value> V8Proxy::ActiveDOMObjectToV8Object( + V8ClassIndex::V8WrapperType type, Peerable* obj) +{ + v8::Handle<v8::Object> result = active_dom_object_map().get(obj); + if (!result.IsEmpty()) + return result; + + // Set the peer object for future access. + result = InstantiateV8Object(type, type, obj); + if (!result.IsEmpty()) + // Only update the DOM object map if the result is non-empty. + SetJSWrapperForActiveDOMObject(obj, + v8::Persistent<v8::Object>::New(result)); + return result; +} + void V8Proxy::BindJSObjectToWindow(Frame* frame, const char* name, int type, diff --git a/webkit/port/bindings/v8/v8_proxy.h b/webkit/port/bindings/v8/v8_proxy.h index 05c3315..bb16921 100644 --- a/webkit/port/bindings/v8/v8_proxy.h +++ b/webkit/port/bindings/v8/v8_proxy.h @@ -393,6 +393,8 @@ class V8Proxy { // Set JS wrapper of a DOM object static void SetJSWrapperForDOMObject(Peerable* obj, v8::Persistent<v8::Object> wrapper); + static void SetJSWrapperForActiveDOMObject(Peerable* obj, + v8::Persistent<v8::Object> wrapper); static void SetJSWrapperForDOMNode(Node* node, v8::Persistent<v8::Object> wrapper); @@ -442,6 +444,8 @@ class V8Proxy { // Returns the JS wrapper of a window object, initializes the environment // of the window frame if needed. static v8::Handle<v8::Value> WindowToV8Object(DOMWindow* window); + static v8::Handle<v8::Value> ActiveDOMObjectToV8Object( + V8ClassIndex::V8WrapperType type, Peerable* obj); #if ENABLE(SVG) static v8::Handle<v8::Value> SVGElementInstanceToV8Object( |