| <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 testDOMImplementation() |
| { |
| var impl = document.implementation.createHTMLDocument('').implementation; |
| gc(); |
| impl.createHTMLDocument(''); // May crash or throw an exception if we collect parent document of impl. |
| } |
| |
| function test() |
| { |
| if (window.testRunner) |
| testRunner.dumpAsText(); |
| |
| print("DOM OBJECTS BEFORE GARBAGE COLLECTION:"); |
| generateProperties(); |
| |
| gc(); |
| |
| print("DOM OBJECTS AFTER GARBAGE COLLECTION:"); |
| testPropertiesAgain(); |
| |
| testEvents(); |
| testDOMImplementation(); |
| } |
| |
| // By default, we expect that custom properties are not allowed. |
| // If "allow custom" is specified, then custom properties are allowed and survive garbage collection. |
| // If "allow custom skip" is specified, this means this property is not guaranteed to survive garbage |
| // collection, so we don't test it at all, since there is a chance it might due to conservative GC algorithm. |
| // Any uses of "allow custom skip" represent bugs that need to be fixed. |
| |
| var objectsToTest = [ |
| [ "document.implementation", "allow custom" ], // DOMImplementation |
| [ "document", "allow custom" ], |
| [ "document.body", "allow custom" ], |
| [ "document.body.attributes", "allow custom" ], // NamedNodeMap |
| [ "document.getElementsByTagName('body')", "allow custom" ], // NodeList |
| [ "document.getElementsByTagName('canvas')[0].getContext('2d')", "allow custom" ], // CanvasRenderingContext2D |
| [ "document.getElementsByTagName('canvas')[0].getContext('2d').createLinearGradient(0, 0, 0, 0)" ], // CanvasGradient |
| [ "document.getElementsByTagName('select')[0].options", "allow custom" ], |
| [ "document.body.childNodes", "allow custom" ], |
| |
| [ "document.all", "allow custom" ], |
| [ "document.images", "allow custom" ], |
| [ "document.embeds", "allow custom" ], |
| [ "document.applets", "allow custom" ], |
| [ "document.links", "allow custom" ], |
| [ "document.forms", "allow custom" ], |
| [ "document.anchors", "allow custom" ], |
| [ "document.scripts", "allow custom" ], |
| |
| [ "document.getElementsByTagName('form')[0].elements", "allow custom" ], |
| [ "document.getElementsByTagName('table')[0].rows", "allow custom" ], |
| [ "document.getElementsByTagName('table')[0].rows[0].cells", "allow custom" ], |
| [ "document.getElementsByTagName('table')[0].tBodies", "allow custom" ], |
| [ "document.getElementsByTagName('table')[0].tBodies[0].rows", "allow custom" ], |
| [ "document.body.children", "allow custom" ], |
| [ "document.getElementsByTagName('map')[0].areas", "allow custom" ], |
| |
| [ "document.body.style", "allow custom" ], |
| [ "document.body.style.getPropertyCSSValue('color')", "allow custom" ], |
| [ "document.styleSheets", "allow custom" ], |
| [ "document.styleSheets[0]", "allow custom" ], |
| [ "document.styleSheets[0].cssRules", "allow custom"], |
| [ "document.styleSheets[0].cssRules[0]", "allow custom" ], |
| |
| [ "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 |
| ]; |
| |
| function generateProperties() |
| { |
| for (var i = 0; i < objectsToTest.length; i++) { // > |
| try { |
| eval(objectsToTest[i][0] + ".myCustomProperty = 1;"); |
| } catch(e) { |
| print("NOT SUPPORTED: " + objectsToTest[i][0] + "[ " + e.message + " ]"); |
| } |
| var expectedResult = objectsToTest[i][1] ? 1 : undefined; |
| try { |
| shouldBe(objectsToTest[i][0] + ".myCustomProperty", expectedResult); |
| } catch(e) { |
| } |
| } |
| } |
| |
| function testPropertiesAgain() |
| { |
| for (var i = 0; i < objectsToTest.length; i++) { // > |
| if (objectsToTest[i][1] === "allow custom skip") |
| continue; |
| var expectedResult = objectsToTest[i][1] ? 1 : undefined; |
| try { |
| shouldBe(objectsToTest[i][0] + ".myCustomProperty", expectedResult); |
| } 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> |