| <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, |
| 1, |
| 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> |