diff options
6 files changed, 121 insertions, 9 deletions
diff --git a/webkit/data/layout_tests/chrome/fast/dom/Window/customized-property-survives-gc-expected.txt b/webkit/data/layout_tests/chrome/fast/dom/Window/customized-property-survives-gc-expected.txt new file mode 100644 index 0000000..7eef580 --- /dev/null +++ b/webkit/data/layout_tests/chrome/fast/dom/Window/customized-property-survives-gc-expected.txt @@ -0,0 +1,12 @@ +This tests that customized properties on window.location and window.navigator won't get lost after a GC. +TEST running. +screen.myProp did not survive GC. +history.myProp did not survive GC. +locationbar.myProp did not survive GC. +menubar.myProp did not survive GC. +personalbar.myProp did not survive GC. +scrollbars.myProp did not survive GC. +statusbar.myProp did not survive GC. +toolbar.myProp did not survive GC. +location.myProp survived GC. +navigator.myProp survived GC. diff --git a/webkit/data/layout_tests/chrome/fast/dom/Window/customized-property-survives-gc.html b/webkit/data/layout_tests/chrome/fast/dom/Window/customized-property-survives-gc.html new file mode 100644 index 0000000..5dc2d1f --- /dev/null +++ b/webkit/data/layout_tests/chrome/fast/dom/Window/customized-property-survives-gc.html @@ -0,0 +1,50 @@ +<html> +<script> +if (window.layoutTestController) + layoutTestController.dumpAsText(); + +function CollectGarbage() { + if (window.gc) { + // Chrome code + window.gc(); + } else { + // Safari hack + for (var i = 0; i < 50000; i++) + new Object(); + } +} + + +function check(name) { + if (!window[name]) return; + + window[name].myProp = 10; + CollectGarbage(); + var r = document.getElementById("result"); + if (window[name].myProp) { + r.innerHTML += name + ".myProp survived GC.<br>"; + } else { + r.innerHTML += name + ".myProp did not survive GC.<br>"; + } +} + +function runTest() { + check("screen"); + check("history"); + check("locationbar"); + check("menubar"); + check("personalbar"); + check("scrollbars"); + check("statusbar"); + check("toolbar"); + check("location"); + check("navigator"); +} +</script> + +<body onload="runTest()"> +This tests that customized properties on window.location and window.navigator +won't get lost after a GC. +<div id="result">TEST running.<br></div> +</body> +</html> diff --git a/webkit/port/bindings/v8/v8_custom.h b/webkit/port/bindings/v8/v8_custom.h index b22854f..d501362 100644 --- a/webkit/port/bindings/v8/v8_custom.h +++ b/webkit/port/bindings/v8/v8_custom.h @@ -22,26 +22,31 @@ class V8Custom { // Constants. static const int kDOMWrapperObjectIndex = 0; static const int kDOMWrapperTypeIndex = 1; - static const int kDefaultWrapperInternalFieldCount = - kDOMWrapperTypeIndex + 1; + static const int kDefaultWrapperInternalFieldCount = 2; - static const int kDocumentMinimumInternalFieldCount = - kDefaultWrapperInternalFieldCount + 1; static const int kDocumentImplementationIndex = kDefaultWrapperInternalFieldCount + 0; + static const int kDocumentMinimumInternalFieldCount = + kDefaultWrapperInternalFieldCount + 1; - static const int kHTMLDocumentInternalFieldCount = - kDocumentMinimumInternalFieldCount + 2; static const int kHTMLDocumentMarkerIndex = kDocumentMinimumInternalFieldCount + 0; static const int kHTMLDocumentShadowIndex = kDocumentMinimumInternalFieldCount + 1; + static const int kHTMLDocumentInternalFieldCount = + kDocumentMinimumInternalFieldCount + 2; - static const int kXMLHttpRequestInternalFieldCount = - kDefaultWrapperInternalFieldCount + 1; static const int kXMLHttpRequestCacheIndex = kDefaultWrapperInternalFieldCount + 0; + static const int kXMLHttpRequestInternalFieldCount = + kDefaultWrapperInternalFieldCount + 1; + static const int kDOMWindowLocationIndex = + kDefaultWrapperInternalFieldCount + 0; + static const int kDOMWindowNavigatorIndex = + kDefaultWrapperInternalFieldCount + 1; + static const int kDOMWindowInternalFieldCount = + kDefaultWrapperInternalFieldCount + 2; #define DECLARE_PROPERTY_ACCESSOR_GETTER(NAME) \ static v8::Handle<v8::Value> v8##NAME##AccessorGetter(\ diff --git a/webkit/port/bindings/v8/v8_proxy.cpp b/webkit/port/bindings/v8/v8_proxy.cpp index d9152ed..184063e 100644 --- a/webkit/port/bindings/v8/v8_proxy.cpp +++ b/webkit/port/bindings/v8/v8_proxy.cpp @@ -55,6 +55,7 @@ #include "HTMLOptionsCollection.h" #include "Page.h" #include "DOMWindow.h" +#include "Location.h" #include "Navigator.h" // for MimeTypeArray #include "V8DOMWindow.h" #include "V8HTMLElement.h" @@ -1245,7 +1246,12 @@ v8::Persistent<v8::FunctionTemplate> V8Proxy::GetTemplate( default_signature), v8::None); desc->SetHiddenPrototype(true); - + + // Reserve spaces for references to location and navigator objects. + v8::Local<v8::ObjectTemplate> instance_template = + desc->InstanceTemplate(); + instance_template->SetInternalFieldCount( + V8Custom::kDOMWindowInternalFieldCount); break; } case V8ClassIndex::LOCATION: { @@ -1834,12 +1840,44 @@ v8::Handle<v8::Value> V8Proxy::ToV8Object(V8ClassIndex::V8WrapperType type, if (!v8obj.IsEmpty()) { result = v8::Persistent<v8::Object>::New(v8obj); dom_object_map().set(obj, result); + + // Special case for Location and Navigator. Both Safari and FF let + // Location and Navigator JS wrappers survive GC. To mimic their + // behaviors, V8 creates hidden references from the DOMWindow to + // location and navigator objects. These references get cleared + // when the DOMWindow is reused by a new page. + if (type == V8ClassIndex::LOCATION) { + SetHiddenWindowReference(static_cast<Location*>(imp)->frame(), + V8Custom::kDOMWindowLocationIndex, result); + } else if (type == V8ClassIndex::NAVIGATOR) { + SetHiddenWindowReference(static_cast<Navigator*>(imp)->frame(), + V8Custom::kDOMWindowNavigatorIndex, result); + } } } return result; } +void V8Proxy::SetHiddenWindowReference(Frame* frame, + const int internal_index, + v8::Handle<v8::Object> jsobj) { + // Get DOMWindow + if (!frame) return; // Object might be detached from window + v8::Handle<v8::Context> context = GetContext(frame); + if (context.IsEmpty()) return; + + 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->GetInternalField(internal_index)->IsUndefined()); + global->SetInternalField(internal_index, jsobj); +} + + V8ClassIndex::V8WrapperType V8Proxy::GetDOMWrapperType( v8::Handle<v8::Object> object) { if (!MaybeDOMWrapper(object)) { diff --git a/webkit/port/bindings/v8/v8_proxy.h b/webkit/port/bindings/v8/v8_proxy.h index 23d1f95..e426cc7 100644 --- a/webkit/port/bindings/v8/v8_proxy.h +++ b/webkit/port/bindings/v8/v8_proxy.h @@ -437,6 +437,11 @@ class V8Proxy { Peerable* object, V8ClassIndex::V8WrapperType type); #endif + // Set hidden references in a DOMWindow object of a frame. + static void SetHiddenWindowReference(Frame* frame, + const int internal_index, + v8::Handle<v8::Object> jsobj); + static V8ClassIndex::V8WrapperType GetHTMLElementType(HTMLElement* elm); static v8::Local<v8::Object> InstantiateV8Object( diff --git a/webkit/port/page/Navigator.h b/webkit/port/page/Navigator.h index 90660a7..983daea 100644 --- a/webkit/port/page/Navigator.h +++ b/webkit/port/page/Navigator.h @@ -243,6 +243,8 @@ class Navigator : public RefCounted<Navigator> { return m_frame->settings()->isJavaEnabled(); } + Frame* frame() { return m_frame; } + void disconnectFrame() { m_frame = NULL; } private: |