JavaScriptCore:
authorggaren <ggaren@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 24 May 2006 22:42:54 +0000 (22:42 +0000)
committerggaren <ggaren@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 24 May 2006 22:42:54 +0000 (22:42 +0000)
        Reviewed by mjs.

        - JSC half of fix for <rdar://problem/4557926> TOT REGRESSSION: Crash
        occurs when attempting to view image in slideshow mode at
        http://d.smugmug.com/gallery/581716 ( KJS::IfNode::execute
        (KJS::ExecState*) + 312)

        On alternate threads, DOMObjects remain in the
        ScriptInterpreter's cache because they're not collected. So, they
        need an opportunity to mark their children.

        I'm not particularly happy with this solution because it fails to
        resolve many outstanding issues with the DOM object cache. Since none
        of those issues is a crasher or a serious compatibility concern,
        and since the behavior of other browsers is not much to go on in this
        case, I've filed <rdar://problem/4561439> about that, and I'm moving on
        with my life.

        * JavaScriptCore.xcodeproj/project.pbxproj:
        * kjs/collector.cpp:
        (KJS::Collector::collect):
        * kjs/internal.cpp:
        (KJS::InterpreterImp::mark):
        * kjs/internal.h:
        * kjs/interpreter.cpp:
        (KJS::Interpreter::mark):
        * kjs/interpreter.h:

LayoutTests:

        Layout tests for DOM object cache and garbage collection,
        <rdar://problem/4557926> TOT REGRESSION: Crash occurs when attempting
        to view image in slideshow mode at http://d.smugmug.com/gallery/581716
        ( KJS::IfNode::execute (KJS::ExecState*) + 312) if you use a PAC file

        * fast/dom/gc-8-expected.txt: Added.
        * fast/dom/gc-8.html: Added.
        * fast/dom/gc-9-expected.txt: Added.
        * fast/dom/gc-9.html: Added.

WebCore:

        Reviewed by mjs.

        - WebCore half of fix for <rdar://problem/4557926> TOT REGRESSION: Crash
        occurs when attempting to view image in slideshow mode at
        http://d.smugmug.com/gallery/581716 ( KJS::IfNode::execute
        (KJS::ExecState*) + 312)

        On alternate threads, DOMObjects remain in the
        ScriptInterpreter's cache because they're not collected. So, they
        need an opportunity to mark their children.

        I'm not particularly happy with this solution because it fails to
        resolve many outstanding issues with the DOM object cache. Since none
        of those issues is a crasher or a serious compatibility concern,
        and since the behavior of other browsers is not much to go on in this
        case, I've filed <rdar://problem/4561439> about that, and I'm moving
        on with my life.

        Also added functionality for testing garbage collection from inside
        DumpRenderTree.

        Also removed XMLHttpRequest from the DOM object cache because XMLHttpRequest
        objects aren't accessed through the DOM.

        Also added JS locking around access to some shared data structures in
        WebCoreJavaScript, even though it probably doesn't matter in practice.

        * bindings/js/JSXMLHttpRequest.cpp:
        (KJS::JSXMLHttpRequest::JSXMLHttpRequest):
        (KJS::JSXMLHttpRequest::~JSXMLHttpRequest):
        * bindings/js/kjs_binding.cpp:
        (KJS::ScriptInterpreter::mark):
        * bindings/js/kjs_binding.h:
        * bridge/mac/WebCoreJavaScript.h:
        * bridge/mac/WebCoreJavaScript.mm:
        (collect):
        (+[WebCoreJavaScript objectCount]):
        (+[WebCoreJavaScript interpreterCount]):
        (+[WebCoreJavaScript protectedObjectCount]):
        (+[WebCoreJavaScript garbageCollect]):
        (+[WebCoreJavaScript garbageCollectOnAlternateThread:]):
        (+[WebCoreJavaScript shouldPrintExceptions]):
        (+[WebCoreJavaScript setShouldPrintExceptions:]):

WebKitTools:

        Reviewed by mjs.

        Added 'GCController' to DRT to support garbage collection layout tests.

        GCController.collect() and GCController.collectOnAlternateThread() do
        what you would expect. The latter takes a boolean argument sepcifying
        whether to wait for garbage collection to finish before continuing to
        execute script.

        * DumpRenderTree/DumpRenderTree.m:
        (-[WaitUntilDoneDelegate webView:windowScriptObjectAvailable:]):
        * DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj:
        * DumpRenderTree/GCController.h: Added.
        * DumpRenderTree/GCController.mm: Added.
        (+[GCController isSelectorExcludedFromWebScript:]):
        (+[GCController webScriptNameForSelector:]):
        (-[GCController collect]):
        (-[GCController collectOnAlternateThread:]):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@14569 268f45cc-cd09-0410-ab3c-d52691b4dbfc

25 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
JavaScriptCore/kjs/collector.cpp
JavaScriptCore/kjs/internal.cpp
JavaScriptCore/kjs/internal.h
JavaScriptCore/kjs/interpreter.cpp
JavaScriptCore/kjs/interpreter.h
LayoutTests/ChangeLog
LayoutTests/fast/dom/gc-8-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/gc-8.html [new file with mode: 0755]
LayoutTests/fast/dom/gc-9-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/gc-9.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/bindings/js/JSXMLHttpRequest.cpp
WebCore/bindings/js/kjs_binding.cpp
WebCore/bindings/js/kjs_binding.h
WebCore/bridge/mac/WebCoreJavaScript.h
WebCore/bridge/mac/WebCoreJavaScript.mm
WebKit/Misc/WebCoreStatistics.h
WebKit/Misc/WebCoreStatistics.m
WebKitTools/ChangeLog
WebKitTools/DumpRenderTree/DumpRenderTree.m
WebKitTools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj
WebKitTools/DumpRenderTree/GCController.h [new file with mode: 0644]
WebKitTools/DumpRenderTree/GCController.mm [new file with mode: 0644]

index 0df7949112c03b65d9ff7d9cee32c9cabf0e4c75..7247140c7332467a3411f302dab4d272e76521b6 100644 (file)
@@ -1,3 +1,33 @@
+2006-05-24  Geoffrey Garen  <ggaren@apple.com>
+
+        Reviewed by mjs.
+        
+        - JSC half of fix for <rdar://problem/4557926> TOT REGRESSSION: Crash
+        occurs when attempting to view image in slideshow mode at
+        http://d.smugmug.com/gallery/581716 ( KJS::IfNode::execute
+        (KJS::ExecState*) + 312)
+
+        On alternate threads, DOMObjects remain in the
+        ScriptInterpreter's cache because they're not collected. So, they
+        need an opportunity to mark their children.
+        
+        I'm not particularly happy with this solution because it fails to
+        resolve many outstanding issues with the DOM object cache. Since none
+        of those issues is a crasher or a serious compatibility concern,
+        and since the behavior of other browsers is not much to go on in this
+        case, I've filed <rdar://problem/4561439> about that, and I'm moving on 
+        with my life.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * kjs/collector.cpp:
+        (KJS::Collector::collect):
+        * kjs/internal.cpp:
+        (KJS::InterpreterImp::mark):
+        * kjs/internal.h:
+        * kjs/interpreter.cpp:
+        (KJS::Interpreter::mark):
+        * kjs/interpreter.h:
+
 === JavaScriptCore-521.10 ===
 
 2006-05-22  Timothy Hatcher  <timothy@apple.com>
index 9a6306dad5810ec344be219d4230c95ac02c7a3f..803ec5feedcd79bb14bc565ebd54d2123db8097e 100644 (file)
                E195679909E7CF1200B89D13 /* UnicodeCategory.h in Headers */ = {isa = PBXBuildFile; fileRef = E195679509E7CF1200B89D13 /* UnicodeCategory.h */; };
 /* End PBXBuildFile section */
 
+/* Begin PBXBuildStyle section */
+               1442B6C20A24D53E00AE84F6 /* Development */ = {
+                       isa = PBXBuildStyle;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = NO;
+                       };
+                       name = Development;
+               };
+               1442B6C30A24D53E00AE84F6 /* Deployment */ = {
+                       isa = PBXBuildStyle;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = YES;
+                       };
+                       name = Deployment;
+               };
+/* End PBXBuildStyle section */
+
 /* Begin PBXContainerItemProxy section */
                65FB3F7D09D11EF300F49DEB /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                0867D690FE84028FC02AAC07 /* Project object */ = {
                        isa = PBXProject;
                        buildConfigurationList = 149C277108902AFE008A9EFC /* Build configuration list for PBXProject "JavaScriptCore" */;
+                       buildSettings = {
+                       };
+                       buildStyles = (
+                               1442B6C20A24D53E00AE84F6 /* Development */,
+                               1442B6C30A24D53E00AE84F6 /* Deployment */,
+                       );
                        hasScannedForEncodings = 1;
                        mainGroup = 0867D691FE84028FC02AAC07 /* JavaScriptCore */;
                        productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
index 0d0c664d48210a90c5ea6a18def5aaf1ec16c946..945ea240367dc71b48493858f5846e2dd75ab082 100644 (file)
@@ -452,10 +452,16 @@ bool Collector::collect()
 {
   assert(JSLock::lockCount() > 0);
 
-  if (InterpreterImp::s_hook) {
+#if USE(MULTIPLE_THREADS)
+    bool currentThreadIsMainThread = !pthread_is_threaded_np() || pthread_main_np();
+#else
+    bool currentThreadIsMainThread = true;
+#endif
+    
+    if (InterpreterImp::s_hook) {
     InterpreterImp *scr = InterpreterImp::s_hook;
     do {
-      scr->mark();
+      scr->mark(currentThreadIsMainThread);
       scr = scr->next;
     } while (scr != InterpreterImp::s_hook);
   }
@@ -471,12 +477,6 @@ bool Collector::collect()
   size_t emptyBlocks = 0;
   size_t numLiveObjects = heap.numLiveObjects;
 
-#if USE(MULTIPLE_THREADS)
-  bool currentThreadIsMainThread = !pthread_is_threaded_np() || pthread_main_np();
-#else
-  bool currentThreadIsMainThread = true;
-#endif
-  
   for (size_t block = 0; block < heap.usedBlocks; block++) {
     CollectorBlock *curBlock = heap.blocks[block];
 
index 7e077cfd84dbd92cd199d8dec9dbe09baf100440..31e088d48acb6e61fe5646709dbcf3cda7407747 100644 (file)
@@ -445,10 +445,10 @@ void InterpreterImp::clear()
   interpreterMap().remove(global);
 }
 
-void InterpreterImp::mark()
+void InterpreterImp::mark(bool currentThreadIsMainThread)
 {
   if (m_interpreter)
-    m_interpreter->mark();
+    m_interpreter->mark(currentThreadIsMainThread);
   if (_context)
     _context->mark();
   if (global)
index f6f5411e8e636c64305d54cf8bd45025989605a9..5ec2a1973b463524f56e26675fb2e6061d239718 100644 (file)
@@ -180,7 +180,7 @@ namespace KJS {
 
     void initGlobalObject();
 
-    void mark();
+    void mark(bool currentThreadIsMainThread);
 
     ExecState *globalExec() { return &globExec; }
     bool checkSyntax(const UString &code);
index 5c1bb38dea5508ad673eda0fe621529a0565d3b1..7697f6294dbd2c995f4d58c27fc4474d4093401d 100644 (file)
@@ -300,6 +300,10 @@ bool Interpreter::collect()
   return Collector::collect();
 }
 
+void Interpreter::mark(bool)
+{
+}
+
 #ifdef KJS_DEBUG_MEM
 #include "lexer.h"
 void Interpreter::finalCheck()
index a764835c6fafe76305eb1e2ae52c57718b4bcbdc..b3cf0f4ea83fae20f96ff2cc7bf9fb8dbd3fb8e7 100644 (file)
@@ -346,7 +346,7 @@ namespace KJS {
      * Called by InterpreterImp during the mark phase of the garbage collector
      * Default implementation does nothing, this exist for classes that reimplement Interpreter.
      */
-    virtual void mark() {}
+    virtual void mark(bool currentThreadIsMainThread);
 
     /**
      * Provides a way to distinguish derived classes.
index b2c44f883e5a476f07a0d3683391cfe76a642ba0..dbddc5ee4622b5ea192892a0b03e28541d0c893d 100644 (file)
@@ -1,3 +1,15 @@
+2006-05-24  Geoffrey Garen  <ggaren@apple.com>
+
+        Layout tests for DOM object cache and garbage collection,
+        <rdar://problem/4557926> TOT REGRESSION: Crash occurs when attempting 
+        to view image in slideshow mode at http://d.smugmug.com/gallery/581716 
+        ( KJS::IfNode::execute (KJS::ExecState*) + 312) if you use a PAC file
+
+        * fast/dom/gc-8-expected.txt: Added.
+        * fast/dom/gc-8.html: Added.
+        * fast/dom/gc-9-expected.txt: Added.
+        * fast/dom/gc-9.html: Added.
+
 2006-05-24  Anders Carlsson  <acarlsson@apple.com>
 
         Reviewed by Maciej.
diff --git a/LayoutTests/fast/dom/gc-8-expected.txt b/LayoutTests/fast/dom/gc-8-expected.txt
new file mode 100644 (file)
index 0000000..dd865db
--- /dev/null
@@ -0,0 +1,6 @@
+This page tests for a crash when accessing a custom property on a DOM object after the garbage collector has run on an alternate thread.
+
+If the test passes, you'll see a 'PASS' message below.
+
+PASS: document.body.style.myCustomProperty should be 1 and is.
+
diff --git a/LayoutTests/fast/dom/gc-8.html b/LayoutTests/fast/dom/gc-8.html
new file mode 100755 (executable)
index 0000000..1ed449a
--- /dev/null
@@ -0,0 +1,55 @@
+<html>
+<head>
+<script>
+function print(message, color) 
+{
+    var paragraph = document.createElement("div");
+    paragraph.appendChild(document.createTextNode(message));
+    paragraph.style.fontFamily = "monospace";
+    if (color)
+        paragraph.style.color = color;
+    document.getElementById("console").appendChild(paragraph);
+}
+
+function shouldBe(a, b)
+{
+    var evalA = eval(a);
+    if (evalA == b)
+        print("PASS: " + a + " should be " + b + " and is.", "green");
+    else
+        print("FAIL: " + a + " should be " + b + " but instead is " + evalA + ".", "red");
+}
+
+function test() 
+{
+    if (!window.layoutTestController) {
+        print("FAIL: This test can only run from inside DumpRenderTree.", "red");
+        return;
+    }
+    
+    layoutTestController.dumpAsText();
+    layoutTestController.waitUntilDone();
+    
+    document.body.style.myCustomProperty = new String("1");
+    new Date(); // allocate another object to create heap entropy
+    // Collecting on a timeout seems to make the crash more reproducible -- not exactly sure why
+    setTimeout('GCController.collectOnAlternateThread(true);', 0);
+    setTimeout('shouldBe("document.body.style.myCustomProperty", "1"); layoutTestController.notifyDone();', 1);
+    
+    // fail-safe
+    setTimeout('print("FAIL: Test hung -- bailing out"); layoutTestController.notifyDone();', 2);
+}
+
+</script>
+</head>
+
+<body onload="test();">
+<p>This page tests for a crash when accessing a custom property on a DOM object after the garbage collector
+   has run on an alternate thread.</p>
+<p>If the test passes, you'll see a 'PASS' message below.</p>
+<hr>
+
+<div id='console'></div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/gc-9-expected.txt b/LayoutTests/fast/dom/gc-9-expected.txt
new file mode 100644 (file)
index 0000000..704451d
--- /dev/null
@@ -0,0 +1,83 @@
+This page tests whether custom properties on DOM objects persist after garbage collection.
+
+If the test passes, you'll see a series of 'PASS' messages below.
+
+Because neither WinIE nor FF has reasonable or predictable behavior in this scenario, this test just documents our behavior to ensure that we don't change it accidentally. It is not a prescription for how things should behave.
+
+DOM OBJECTS BEFORE GARBAGE COLLECTION:
+PASS: document.implementation.myCustomProperty should be 1 and is.
+PASS: document.myCustomProperty should be 1 and is.
+PASS: document.body.myCustomProperty should be 1 and is.
+PASS: document.body.attributes.myCustomProperty should be 1 and is.
+PASS: document.getElementsByTagName('body').myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('canvas')[0].getContext('2d').myCustomProperty should be 1 and is.
+PASS: document.getElementsByTagName('canvas')[0].getContext('2d').createLinearGradient(0, 0, 0, 0).myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('canvas')[0].getContext('2d').createPattern(new Image(), 'no-repeat').myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('select')[0].options.myCustomProperty should be undefined and is.
+PASS: document.all.myCustomProperty should be undefined and is.
+PASS: document.body.childNodes.myCustomProperty should be undefined and is.
+PASS: document.images.myCustomProperty should be undefined and is.
+PASS: document.embeds.myCustomProperty should be undefined and is.
+PASS: document.applets.myCustomProperty should be undefined and is.
+PASS: document.links.myCustomProperty should be undefined and is.
+PASS: document.forms.myCustomProperty should be undefined and is.
+PASS: document.anchors.myCustomProperty should be undefined and is.
+PASS: document.scripts.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('form')[0].elements.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('table')[0].rows.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('table')[0].rows[0].cells.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('table')[0].tBodies.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('table')[0].tBodies[0].rows.myCustomProperty should be undefined and is.
+PASS: document.body.children.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('map')[0].areas.myCustomProperty should be undefined and is.
+PASS: document.body.style.myCustomProperty should be 1 and is.
+PASS: document.body.style.getPropertyCSSValue('color').myCustomProperty should be 1 and is.
+PASS: document.styleSheets.myCustomProperty should be 1 and is.
+PASS: document.styleSheets[0].myCustomProperty should be 1 and is.
+PASS: document.styleSheets[0].cssRules.myCustomProperty should be undefined and is.
+PASS: document.styleSheets[0].cssRules[0].myCustomProperty should be undefined and is.
+PASS: new XPathEvaluator().myCustomProperty should be undefined and is.
+PASS: new XPathEvaluator().evaluate('/', document, null, 0, null).myCustomProperty should be undefined and is.
+PASS: document.createNSResolver(document).myCustomProperty should be undefined and is.
+PASS: document.createExpression('/', document.createNSResolver(document)).myCustomProperty should be undefined and is.
+DOM OBJECTS AFTER GARBAGE COLLECTION:
+PASS: document.implementation.myCustomProperty should be undefined and is.
+PASS: document.myCustomProperty should be 1 and is.
+PASS: document.body.myCustomProperty should be 1 and is.
+PASS: document.body.attributes.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('body').myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('canvas')[0].getContext('2d').myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('canvas')[0].getContext('2d').createLinearGradient(0, 0, 0, 0).myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('canvas')[0].getContext('2d').createPattern(new Image(), 'no-repeat').myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('select')[0].options.myCustomProperty should be undefined and is.
+PASS: document.all.myCustomProperty should be undefined and is.
+PASS: document.body.childNodes.myCustomProperty should be undefined and is.
+PASS: document.images.myCustomProperty should be undefined and is.
+PASS: document.embeds.myCustomProperty should be undefined and is.
+PASS: document.applets.myCustomProperty should be undefined and is.
+PASS: document.links.myCustomProperty should be undefined and is.
+PASS: document.forms.myCustomProperty should be undefined and is.
+PASS: document.anchors.myCustomProperty should be undefined and is.
+PASS: document.scripts.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('form')[0].elements.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('table')[0].rows.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('table')[0].rows[0].cells.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('table')[0].tBodies.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('table')[0].tBodies[0].rows.myCustomProperty should be undefined and is.
+PASS: document.body.children.myCustomProperty should be undefined and is.
+PASS: document.getElementsByTagName('map')[0].areas.myCustomProperty should be undefined and is.
+PASS: document.body.style.myCustomProperty should be undefined and is.
+PASS: document.body.style.getPropertyCSSValue('color').myCustomProperty should be undefined and is.
+PASS: document.styleSheets.myCustomProperty should be undefined and is.
+PASS: document.styleSheets[0].myCustomProperty should be undefined and is.
+PASS: document.styleSheets[0].cssRules.myCustomProperty should be undefined and is.
+PASS: document.styleSheets[0].cssRules[0].myCustomProperty should be undefined and is.
+PASS: new XPathEvaluator().myCustomProperty should be undefined and is.
+PASS: new XPathEvaluator().evaluate('/', document, null, 0, null).myCustomProperty should be undefined and is.
+PASS: document.createNSResolver(document).myCustomProperty should be undefined and is.
+PASS: document.createExpression('/', document.createNSResolver(document)).myCustomProperty should be undefined and is.
+DOM EVENT BEFORE GARBAGE COLLECTION
+PASS: event.myCustomProperty should be 1 and is.
+DOM EVENT AFTER GARBAGE COLLECTION
+PASS: event.myCustomProperty should be 1 and is.
+
diff --git a/LayoutTests/fast/dom/gc-9.html b/LayoutTests/fast/dom/gc-9.html
new file mode 100644 (file)
index 0000000..e760841
--- /dev/null
@@ -0,0 +1,269 @@
+<html>
+<head>
+<style>
+    body {
+        color: black;
+        padding: 0px 0px 0px 0px;
+    }
+    
+    .hidden {
+        visibility: hidden;
+    }
+</style>
+<script>
+function print(message, color) 
+{
+    var paragraph = document.createElement("div");
+    paragraph.appendChild(document.createTextNode(message));
+    paragraph.style.fontFamily = "monospace";
+    if (color)
+        paragraph.style.color = color;
+    document.getElementById("console").appendChild(paragraph);
+}
+
+function shouldBe(a, b)
+{
+    var evalA = eval(a);
+    if (evalA == b)
+        print("PASS: " + a + " should be " + b + " and is.", "green");
+    else
+        print("FAIL: " + a + " should be " + b + " but instead is " + evalA + ".", "red");
+}
+
+function gc()
+{
+    if (window.GCController)
+        return GCController.collect();
+
+    for (var i = 0; i < 10000; i++) { // > force garbage collection (FF requires about 9K allocations before a collect)
+        var s = new String("");
+    }
+}
+
+var event;
+function parentEventListener(e)
+{
+    print("DOM EVENT AFTER GARBAGE COLLECTION");
+    gc();
+    event = e;
+    shouldBe("event.myCustomProperty", 1);
+    event = null; // clear JS reference
+}
+
+function childEventListener(e)
+{
+    print("DOM EVENT BEFORE GARBAGE COLLECTION");
+    e.myCustomProperty = 1;
+    event = e;
+    shouldBe("event.myCustomProperty", 1);
+    event = null; // clear JS reference
+}
+
+function testEvents()
+{
+    var parent = document.createElement("p");
+    var child = document.createElement("p");
+    parent.appendChild(child);
+    document.body.appendChild(parent);
+
+    if (parent.addEventListener) {
+        child.addEventListener("click", childEventListener, false);
+        parent.addEventListener("click", parentEventListener, false);
+    } else {
+        child.attachEvent("onclick", childEventListener);
+        parent.attachEvent("onclick", parentEventListener);
+    }
+
+    if (document.createEvent) {
+        var event = document.createEvent("MouseEvents");
+        event.initEvent("click", true, true);
+        child.dispatchEvent(event);
+    } else {
+        child.fireEvent("onclick");
+    }
+}
+
+function test() 
+{
+    if (window.layoutTestController)
+        layoutTestController.dumpAsText();
+        
+    generateProperties();
+
+    print("DOM OBJECTS BEFORE GARBAGE COLLECTION:");
+    testProperties(expectedResultsBeforeGC);
+
+    gc();
+    
+    print("DOM OBJECTS AFTER GARBAGE COLLECTION:");
+    testProperties(expectedResultsAfterGC);
+
+    testEvents();
+}
+
+var objectsToTest = [
+    "document.implementation", // DOMImplementation
+    "document",
+    "document.body",
+    "document.body.attributes", // NamedNodeMap
+    "document.getElementsByTagName('body')", // NodeList
+    "document.getElementsByTagName('canvas')[0].getContext('2d')", // CanvasRenderingContext2D
+    "document.getElementsByTagName('canvas')[0].getContext('2d').createLinearGradient(0, 0, 0, 0)", // CanvasGradient
+    "document.getElementsByTagName('canvas')[0].getContext('2d').createPattern(new Image(), 'no-repeat')", // CanvasPattern
+    "document.getElementsByTagName('select')[0].options",
+    "document.all",
+    "document.body.childNodes",
+
+    "document.images",
+    "document.embeds",
+    "document.applets",
+    "document.links",
+    "document.forms",
+    "document.anchors",
+    "document.scripts",
+
+    "document.getElementsByTagName('form')[0].elements",
+    "document.getElementsByTagName('table')[0].rows",
+    "document.getElementsByTagName('table')[0].rows[0].cells",
+    "document.getElementsByTagName('table')[0].tBodies",
+    "document.getElementsByTagName('table')[0].tBodies[0].rows",
+    "document.body.children",
+    "document.getElementsByTagName('map')[0].areas",
+    
+    "document.body.style",
+    "document.body.style.getPropertyCSSValue('color')",
+    "document.styleSheets",
+    "document.styleSheets[0]",
+    "document.styleSheets[0].cssRules",
+    "document.styleSheets[0].cssRules[0]",
+
+    "new XPathEvaluator()", // XPathEvaluator
+    "new XPathEvaluator().evaluate('/', document, null, 0, null)", // XPathResult
+    "document.createNSResolver(document)", // XPathNSResolver
+    "document.createExpression('/', document.createNSResolver(document))" // XPathExpression
+
+    // should not cache: NodeIterator, NodeFilter, TreeWalker, XMLHttpRequest
+    // add to test: DOMRect, MediaList, Counter, Range
+    
+];
+
+var expectedResultsBeforeGC = [
+    1,
+    1,
+    1,
+    1,
+    undefined,
+    1,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    1,
+    1,
+    1,
+    1,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+];
+
+var expectedResultsAfterGC = [
+    undefined,
+    1,
+    1,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+    undefined,
+];
+
+function generateProperties()
+{
+    for (var i = 0; i < objectsToTest.length; i++) { // >
+        try {
+            eval(objectsToTest[i] + ".myCustomProperty = 1;");
+        } catch(e) {
+            print("NOT SUPPORTED: " + objectsToTest[i] + "[ " + e.message + " ]");
+        }
+    }
+}
+
+function testProperties(expectedResults)
+{
+    for (var i = 0; i < objectsToTest.length; i++) { // >
+        try {
+            shouldBe(objectsToTest[i] + ".myCustomProperty", expectedResults[i]);
+        } catch(e) {
+        }
+    }
+}
+</script>
+</head>
+
+<body style="color: black" onload="test();">
+<p>This page tests whether custom properties on DOM objects persist after garbage collection.</p>
+<p>If the test passes, you'll see a series of 'PASS' messages below.</p>
+<p>Because neither WinIE nor FF has reasonable or predictable behavior in this scenario, this 
+   test just documents our behavior to ensure that we don't change it accidentally. It is not 
+   a prescription for how things should behave.</p>
+<hr>
+
+<div id='console'></div>
+
+<div class='hidden'>
+    <canvas></canvas>
+    <select></select>
+    <object name="object"></object>
+    <form></form>
+    <table><tbody><tr></tr></tbody></table>
+    <map></map>
+</div>
+
+</body>
+</html>
index c72e8a9a22364b06ceb9e797027b134cbe8d37d3..6bcbc5e55189501faedec0ca3bb684e17d61e87e 100644 (file)
@@ -1,3 +1,49 @@
+2006-05-24  Geoffrey Garen  <ggaren@apple.com>
+
+        Reviewed by mjs.
+
+        - WebCore half of fix for <rdar://problem/4557926> TOT REGRESSION: Crash
+        occurs when attempting to view image in slideshow mode at
+        http://d.smugmug.com/gallery/581716 ( KJS::IfNode::execute
+        (KJS::ExecState*) + 312)
+
+        On alternate threads, DOMObjects remain in the
+        ScriptInterpreter's cache because they're not collected. So, they
+        need an opportunity to mark their children.
+        
+        I'm not particularly happy with this solution because it fails to
+        resolve many outstanding issues with the DOM object cache. Since none
+        of those issues is a crasher or a serious compatibility concern,
+        and since the behavior of other browsers is not much to go on in this
+        case, I've filed <rdar://problem/4561439> about that, and I'm moving 
+        on with my life.
+
+        Also added functionality for testing garbage collection from inside
+        DumpRenderTree.
+        
+        Also removed XMLHttpRequest from the DOM object cache because XMLHttpRequest
+        objects aren't accessed through the DOM.
+        
+        Also added JS locking around access to some shared data structures in
+        WebCoreJavaScript, even though it probably doesn't matter in practice.
+
+        * bindings/js/JSXMLHttpRequest.cpp:
+        (KJS::JSXMLHttpRequest::JSXMLHttpRequest):
+        (KJS::JSXMLHttpRequest::~JSXMLHttpRequest):
+        * bindings/js/kjs_binding.cpp:
+        (KJS::ScriptInterpreter::mark):
+        * bindings/js/kjs_binding.h:
+        * bridge/mac/WebCoreJavaScript.h:
+        * bridge/mac/WebCoreJavaScript.mm:
+        (collect):
+        (+[WebCoreJavaScript objectCount]):
+        (+[WebCoreJavaScript interpreterCount]):
+        (+[WebCoreJavaScript protectedObjectCount]):
+        (+[WebCoreJavaScript garbageCollect]):
+        (+[WebCoreJavaScript garbageCollectOnAlternateThread:]):
+        (+[WebCoreJavaScript shouldPrintExceptions]):
+        (+[WebCoreJavaScript setShouldPrintExceptions:]):
+
 2006-05-24  Dave Hyatt <hyatt@apple.com>
 
         Fix the font cache corruption problems on Win32.  Move the
index c71023aab7cd0560ebe1b669ff97f53bac70b496..c59c35c3f88640dd58288620dcd812ae54fa611b 100644 (file)
@@ -154,14 +154,12 @@ JSXMLHttpRequest::JSXMLHttpRequest(ExecState *exec, Document *d)
   : m_impl(new XMLHttpRequest(d))
 {
   setPrototype(JSXMLHttpRequestProto::self(exec));
-  ScriptInterpreter::putDOMObject(m_impl.get(), this);
 }
 
 JSXMLHttpRequest::~JSXMLHttpRequest()
 {
   m_impl->setOnReadyStateChangeListener(0);
   m_impl->setOnLoadListener(0);
-  ScriptInterpreter::forgetDOMObject(m_impl.get());
 }
 
 JSValue* JSXMLHttpRequestProtoFunc::callAsFunction(ExecState *exec, JSObject* thisObj, const List& args)
index 6cef30f91f3c53418a31eb13c2d49d63a5c05c97..400f5a82c7020f12bb515abdfab95c632973cce4 100644 (file)
@@ -135,7 +135,7 @@ void ScriptInterpreter::forgetAllDOMNodesForDocument(WebCore::Document *document
     }
 }
 
-void ScriptInterpreter::mark()
+void ScriptInterpreter::mark(bool currentThreadIsMainThread)
 {
   NodePerDocMap::iterator dictEnd = domNodesPerDocument()->end();
   for (NodePerDocMap::iterator dictIt = domNodesPerDocument()->begin();
@@ -156,6 +156,19 @@ void ScriptInterpreter::mark()
             node->mark();
       }
   }
+  
+  if (!currentThreadIsMainThread) {
+      // On alternate threads, DOMObjects remain in the cache because they're not collected.
+      // So, they need an opportunity to mark their children.
+      DOMObjectMap::iterator objectEnd = domObjects()->end();
+      for (DOMObjectMap::iterator objectIt = domObjects()->begin();
+           objectIt != objectEnd;
+           ++objectIt) {
+          DOMObject* object = objectIt->second;
+          if (!object->marked())
+              object->mark();
+      }
+  }
 }
 
 ExecState *ScriptInterpreter::globalExec()
index cc3d2352e46e50ebf9de658cdfcbba687cf478d7..f0977cd77781469363a880a442b3bc183d2a4687 100644 (file)
@@ -90,7 +90,7 @@ namespace KJS {
      */
     bool wasRunByUserGesture() const;
 
-    virtual void mark();
+    virtual void mark(bool currentThreadIsMainThread);
     virtual ExecState *globalExec();
     
     WebCore::Event *getCurrentEvent() const { return m_evt; }
index f307218e15a82e7fa09c7277e1b3455b7cc222dd..c7a9b7e5ec125f6fcb44d4f9e6505af4e1bf7b2a 100644 (file)
@@ -35,6 +35,7 @@
 + (NSCountedSet *)rootObjectTypeCounts;
 
 + (void)garbageCollect;
++ (void)garbageCollectOnAlternateThread:(BOOL)waitUntilDone;
 
 + (BOOL)shouldPrintExceptions;
 + (void)setShouldPrintExceptions:(BOOL)print;
index a61edb38ddfe07132ef93984aa65af375c74961b..5bbbcab396469a165d75ff9c41d2e9bd890272d8 100644 (file)
@@ -35,20 +35,30 @@ using KJS::Collector;
 using KJS::Interpreter;
 using KJS::JSLock;
 
+void* collect(void*)
+{
+    JSLock lock;
+    Collector::collect();
+    return 0;
+}
+
 @implementation WebCoreJavaScript
 
 + (size_t)objectCount
 {
+    JSLock lock;
     return Collector::size();
 }
 
 + (size_t)interpreterCount
 {
+    JSLock lock;
     return Collector::numInterpreters();
 }
 
 + (size_t)protectedObjectCount
 {
+    JSLock lock;
     return Collector::numProtectedObjects();
 }
 
@@ -69,17 +79,29 @@ using KJS::JSLock;
 
 + (void)garbageCollect
 {
-    JSLock lock;
-    while (Collector::collect()) { }
+    collect(NULL);
+}
+
++ (void)garbageCollectOnAlternateThread:(BOOL)waitUntilDone
+{
+    pthread_t thread;
+    pthread_create(&thread, NULL, collect, NULL);
+
+    if (waitUntilDone) {
+        JSLock::DropAllLocks dropLocks; // Otherwise our lock would deadlock the collect thread we're joining
+        pthread_join(thread, NULL);
+    }
 }
 
 + (BOOL)shouldPrintExceptions
 {
+    JSLock lock;
     return Interpreter::shouldPrintExceptions();
 }
 
 + (void)setShouldPrintExceptions:(BOOL)print
 {
+    JSLock lock;
     Interpreter::setShouldPrintExceptions(print);
 }
 
index db7b3f4fd7493cff9c0a8e5cb03319254d7851d4..8e74e436a59e412c0a4ab23d04f19926274bc160 100644 (file)
@@ -43,6 +43,7 @@
 + (size_t)javaScriptProtectedObjectsCount;
 + (NSCountedSet *)javaScriptRootObjectTypeCounts;
 + (void)garbageCollectJavaScriptObjects;
++ (void)garbageCollectJavaScriptObjectsOnAlternateThread:(BOOL)waitUntilDone;
 
 
 // deprecated
index c875067eb3ae46a939849c5d7198bf8f75f82078..63ad94b08d169fae3d04f44357e6b4e9d189e03e 100644 (file)
     [WebCoreJavaScript garbageCollect];
 }
 
++ (void)garbageCollectJavaScriptObjectsOnAlternateThread:(BOOL)waitUntilDone;
+{
+    [WebCoreJavaScript garbageCollectOnAlternateThread:waitUntilDone];
+}
+
 + (BOOL)shouldPrintExceptions
 {
     return [WebCoreJavaScript shouldPrintExceptions];
index 7db413164d64cf2d366f2c4259ef4cb5b57f91bf..54165f4f4dc3820016f9f570e6f220d68cc723e3 100644 (file)
@@ -1,3 +1,24 @@
+2006-05-24  Geoffrey Garen  <ggaren@apple.com>
+
+        Reviewed by mjs.
+        
+        Added 'GCController' to DRT to support garbage collection layout tests.
+        
+        GCController.collect() and GCController.collectOnAlternateThread() do
+        what you would expect. The latter takes a boolean argument sepcifying
+        whether to wait for garbage collection to finish before continuing to
+        execute script.
+
+        * DumpRenderTree/DumpRenderTree.m:
+        (-[WaitUntilDoneDelegate webView:windowScriptObjectAvailable:]):
+        * DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj:
+        * DumpRenderTree/GCController.h: Added.
+        * DumpRenderTree/GCController.mm: Added.
+        (+[GCController isSelectorExcludedFromWebScript:]):
+        (+[GCController webScriptNameForSelector:]):
+        (-[GCController collect]):
+        (-[GCController collectOnAlternateThread:]):
+
 2006-05-23  John Sullivan  <sullivan@apple.com>
 
         Reviewed by Maciej.
index ef1428aa80a07ecaa2029c87b6844e59a56d5208..9555816fad6daded1c6797433e7e723a6ac90310 100644 (file)
@@ -54,6 +54,7 @@
 #import "DumpRenderTreeDraggingInfo.h"
 #import "EditingDelegate.h"
 #import "EventSendingController.h"
+#import "GCController.h"
 #import "NavigationController.h"
 #import "ObjCPlugin.h"
 #import "ObjCPluginFunction.h"
@@ -536,6 +537,10 @@ static void dump(void)
     [obj setValue:asc forKey:@"appleScriptController"];
     [asc release];
     
+    GCController *gcc = [[GCController alloc] init];
+    [obj setValue:gcc forKey:@"GCController"];
+    [gcc release];
+    
     [obj setValue:navigationController forKey:@"navigationController"];
     
     ObjCPlugin *plugin = [[ObjCPlugin alloc] init];
index 27d9db0760a239179fce851381e4dc9315ed432d..44a37356c5fd00e7eaeb8ed3cf6099bcc7e8e3d2 100644 (file)
@@ -32,6 +32,8 @@
                141BF44B096A45C800E0753C /* PluginObject.c in Sources */ = {isa = PBXBuildFile; fileRef = 141BF446096A45C800E0753C /* PluginObject.c */; };
                141BF44C096A45C800E0753C /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 141BF448096A45C800E0753C /* Info.plist */; };
                141BF453096A45EB00E0753C /* PluginObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 141BF447096A45C800E0753C /* PluginObject.h */; };
+               14770FE20A22ADF7009342EE /* GCController.h in Headers */ = {isa = PBXBuildFile; fileRef = 14770FE00A22ADF7009342EE /* GCController.h */; };
+               14770FE30A22ADF7009342EE /* GCController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 14770FE10A22ADF7009342EE /* GCController.mm */; };
                14A6FB8A0971CAE5008B014F /* NavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 14A6FB880971CAE5008B014F /* NavigationController.h */; };
                14A6FB8B0971CAE5008B014F /* NavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 14A6FB890971CAE5008B014F /* NavigationController.m */; };
                22181BD109DC8C4B008342E8 /* ObjCPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 22181BCD09DC8C4B008342E8 /* ObjCPlugin.h */; };
@@ -92,6 +94,8 @@
                141BF446096A45C800E0753C /* PluginObject.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = PluginObject.c; sourceTree = "<group>"; };
                141BF447096A45C800E0753C /* PluginObject.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PluginObject.h; sourceTree = "<group>"; };
                141BF448096A45C800E0753C /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xml; path = Info.plist; sourceTree = "<group>"; };
+               14770FE00A22ADF7009342EE /* GCController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCController.h; sourceTree = "<group>"; };
+               14770FE10A22ADF7009342EE /* GCController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GCController.mm; sourceTree = "<group>"; };
                14A6FB880971CAE5008B014F /* NavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NavigationController.h; sourceTree = "<group>"; };
                14A6FB890971CAE5008B014F /* NavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NavigationController.m; sourceTree = "<group>"; };
                22181BCD09DC8C4B008342E8 /* ObjCPlugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ObjCPlugin.h; sourceTree = "<group>"; };
                22181BD009DC8C4B008342E8 /* ObjCPluginFunction.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = ObjCPluginFunction.m; sourceTree = "<group>"; };
                32A70AAB03705E1F00C91783 /* DumpRenderTreePrefix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DumpRenderTreePrefix.h; sourceTree = "<group>"; };
                9335435F03D75502008635CE /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = WebKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
-               9340995408540CAF007F3BC8 /* DumpRenderTree */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "compiled.mach-o.executable"; path = DumpRenderTree; sourceTree = BUILT_PRODUCTS_DIR; };
+               9340995408540CAF007F3BC8 /* DumpRenderTree */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = DumpRenderTree; sourceTree = BUILT_PRODUCTS_DIR; };
                93442CF408F8BA4900BFE8CA /* TextInputController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextInputController.h; sourceTree = "<group>"; };
                93442CF508F8BA4900BFE8CA /* TextInputController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextInputController.m; sourceTree = "<group>"; };
                A803FCB709CAAAB0009B2A37 /* EventSendingController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = EventSendingController.h; sourceTree = "<group>"; };
                                32A70AAB03705E1F00C91783 /* DumpRenderTreePrefix.h */,
                                A803FCB709CAAAB0009B2A37 /* EventSendingController.h */,
                                A803FCB809CAAAB1009B2A37 /* EventSendingController.m */,
+                               14770FE00A22ADF7009342EE /* GCController.h */,
+                               14770FE10A22ADF7009342EE /* GCController.mm */,
                                93442CF408F8BA4900BFE8CA /* TextInputController.h */,
                                93442CF508F8BA4900BFE8CA /* TextInputController.m */,
                                E13307FE099624DA00AC0A91 /* AppleScriptController.h */,
                                A803FFF509CAAFE0009B2A37 /* EditingDelegate.h in Headers */,
                                22181BD109DC8C4B008342E8 /* ObjCPlugin.h in Headers */,
                                22181BD309DC8C4B008342E8 /* ObjCPluginFunction.h in Headers */,
+                               14770FE20A22ADF7009342EE /* GCController.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                A803FFF609CAAFE0009B2A37 /* EditingDelegate.m in Sources */,
                                22181BD209DC8C4B008342E8 /* ObjCPlugin.m in Sources */,
                                22181BD409DC8C4B008342E8 /* ObjCPluginFunction.m in Sources */,
+                               14770FE30A22ADF7009342EE /* GCController.mm in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
diff --git a/WebKitTools/DumpRenderTree/GCController.h b/WebKitTools/DumpRenderTree/GCController.h
new file mode 100644 (file)
index 0000000..86a9c23
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  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. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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.
+ */
+
+#import <WebKit/WebView.h>
+
+@interface GCController : NSObject {
+}
++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector;
++ (NSString *)webScriptNameForSelector:(SEL)aSelector;
+- (void)collect;
+- (void)collectOnAlternateThread:(BOOL)waitUntilDone;
+@end
diff --git a/WebKitTools/DumpRenderTree/GCController.mm b/WebKitTools/DumpRenderTree/GCController.mm
new file mode 100644 (file)
index 0000000..58ee44c
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  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. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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.
+ */
+
+#import "GCController.h"
+#import <WebKit/WebCoreStatistics.h>
+
+@implementation GCController
++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
+{
+    if (aSelector == @selector(collect))
+        return NO;
+    if (aSelector == @selector(collectOnAlternateThread:))
+        return NO;
+    return YES;
+}
+
++ (NSString *)webScriptNameForSelector:(SEL)aSelector
+{
+    if (aSelector == @selector(collectOnAlternateThread:))
+        return @"collectOnAlternateThread";
+    
+    return nil;
+}
+
+- (void)collect
+{
+    [WebCoreStatistics garbageCollectJavaScriptObjects];
+}
+
+- (void)collectOnAlternateThread:(BOOL)waitUntilDone
+{
+    [WebCoreStatistics garbageCollectJavaScriptObjectsOnAlternateThread:waitUntilDone];
+}
+@end