|
1 <!DOCTYPE HTML> |
|
2 <html> |
|
3 <!-- |
|
4 https://bugzilla.mozilla.org/show_bug.cgi?id= |
|
5 --> |
|
6 <head> |
|
7 <meta charset="utf-8"> |
|
8 <title>Test for Bug </title> |
|
9 |
|
10 <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> |
|
11 <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> |
|
12 <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> |
|
13 <script type="application/javascript;version=1.8"> |
|
14 Components.utils.import("resource://gre/modules/devtools/Loader.jsm"); |
|
15 const {Promise: promise} = Components.utils.import("resource://gre/modules/Promise.jsm", {}); |
|
16 |
|
17 const inspector = devtools.require("devtools/server/actors/inspector"); |
|
18 |
|
19 window.onload = function() { |
|
20 SimpleTest.waitForExplicitFinish(); |
|
21 runNextTest(); |
|
22 } |
|
23 |
|
24 var gWalker = null; |
|
25 var gClient = null; |
|
26 var gInspectee = null; |
|
27 |
|
28 function assertOwnership() { |
|
29 return assertOwnershipTrees(gWalker); |
|
30 } |
|
31 |
|
32 addTest(function setup() { |
|
33 let url = document.getElementById("inspectorContent").href; |
|
34 attachURL(url, function(err, client, tab, doc) { |
|
35 gInspectee = doc; |
|
36 let {InspectorFront} = devtools.require("devtools/server/actors/inspector"); |
|
37 let inspector = InspectorFront(client, tab); |
|
38 promiseDone(inspector.getWalker().then(walker => { |
|
39 ok(walker, "getWalker() should return an actor."); |
|
40 gClient = client; |
|
41 gWalker = walker; |
|
42 }).then(runNextTest)); |
|
43 }); |
|
44 }); |
|
45 |
|
46 // Retain a node, and a second-order child (in another document, for kicks) |
|
47 // Release the parent of the top item, which should cause one retained orphan. |
|
48 |
|
49 // Then unretain the top node, which should retain the orphan. |
|
50 |
|
51 // Then change the source of the iframe, which should kill that orphan. |
|
52 |
|
53 addTest(function testRetain() { |
|
54 let originalOwnershipSize = 0; |
|
55 let bodyFront = null; |
|
56 let frameFront = null; |
|
57 let childListFront = null; |
|
58 // Get the toplevel body element and retain it. |
|
59 promiseDone(gWalker.querySelector(gWalker.rootNode, "body").then(front => { |
|
60 bodyFront = front; |
|
61 return gWalker.retainNode(bodyFront); |
|
62 }).then(() => { |
|
63 // Get an element in the child frame and retain it. |
|
64 return gWalker.querySelector(gWalker.rootNode, "#childFrame"); |
|
65 }).then(frame => { |
|
66 frameFront = frame; |
|
67 return gWalker.children(frame, { maxNodes: 1 }).then(children => { |
|
68 return children.nodes[0]; |
|
69 }); |
|
70 }).then(childDoc => { |
|
71 return gWalker.querySelector(childDoc, "#longlist"); |
|
72 }).then(list => { |
|
73 childListFront = list; |
|
74 originalOwnershipSize = assertOwnership(); |
|
75 // and rtain it. |
|
76 return gWalker.retainNode(childListFront); |
|
77 }).then(() => { |
|
78 // OK, try releasing the parent of the first retained. |
|
79 return gWalker.releaseNode(bodyFront.parentNode()); |
|
80 }).then(() => { |
|
81 let size = assertOwnership(); |
|
82 let clientTree = clientOwnershipTree(gWalker); |
|
83 |
|
84 // That request should have freed the parent of the first retained |
|
85 // but moved the rest into the retained orphaned tree. |
|
86 is(ownershipTreeSize(clientTree.root) + ownershipTreeSize(clientTree.retained[0]) + 1, |
|
87 originalOwnershipSize, |
|
88 "Should have only lost one item overall."); |
|
89 is(gWalker._retainedOrphans.size, 1, "Should have retained one orphan"); |
|
90 ok(gWalker._retainedOrphans.has(bodyFront), "Should have retained the expected node."); |
|
91 }).then(() => { |
|
92 // Unretain the body, which should promote the childListFront to a retained orphan. |
|
93 return gWalker.unretainNode(bodyFront); |
|
94 }).then(() => { |
|
95 assertOwnership(); |
|
96 let clientTree = clientOwnershipTree(gWalker); |
|
97 |
|
98 is(gWalker._retainedOrphans.size, 1, "Should still only have one retained orphan."); |
|
99 ok(!gWalker._retainedOrphans.has(bodyFront), "Should have dropped the body node.") |
|
100 ok(gWalker._retainedOrphans.has(childListFront), "Should have retained the child node.") |
|
101 }).then(() => { |
|
102 // Change the source of the iframe, which should kill the retained orphan. |
|
103 gInspectee.querySelector("#childFrame").src = "data:text/html,<html>new child</html>"; |
|
104 return waitForMutation(gWalker, isUnretained); |
|
105 }).then(mutations => { |
|
106 assertOwnership(); |
|
107 let clientTree = clientOwnershipTree(gWalker); |
|
108 is(gWalker._retainedOrphans.size, 0, "Should have no more retained orphans."); |
|
109 |
|
110 }).then(runNextTest)); |
|
111 }); |
|
112 |
|
113 // Get a hold of a node, remove it from the doc and retain it at the same time. |
|
114 // We should always win that race (even though the mutation happens before the |
|
115 // retain request), because we haven't issued `getMutations` yet. |
|
116 addTest(function testWinRace() { |
|
117 let front = null; |
|
118 promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(node => { |
|
119 front = node; |
|
120 let contentNode = gInspectee.querySelector("#a"); |
|
121 contentNode.parentNode.removeChild(contentNode); |
|
122 // Now wait for that mutation and retain response to come in. |
|
123 return promise.all([ |
|
124 gWalker.retainNode(front), |
|
125 waitForMutation(gWalker, isChildList) |
|
126 ]); |
|
127 }).then(() => { |
|
128 assertOwnership(); |
|
129 let clientTree = clientOwnershipTree(gWalker); |
|
130 is(gWalker._retainedOrphans.size, 1, "Should have a retained orphan."); |
|
131 ok(gWalker._retainedOrphans.has(front), "Should have retained our expected node."); |
|
132 return gWalker.unretainNode(front); |
|
133 }).then(() => { |
|
134 // Make sure we're clear for the next test. |
|
135 assertOwnership(); |
|
136 let clientTree = clientOwnershipTree(gWalker); |
|
137 is(gWalker._retainedOrphans.size, 0, "Should have no more retained orphans."); |
|
138 }).then(runNextTest)); |
|
139 }); |
|
140 |
|
141 // Same as above, but issue the request right after the 'new-mutations' event, so that |
|
142 // we *lose* the race. |
|
143 addTest(function testLoseRace() { |
|
144 let front = null; |
|
145 promiseDone(gWalker.querySelector(gWalker.rootNode, "#z").then(node => { |
|
146 front = node; |
|
147 gInspectee.querySelector("#z").parentNode = null; |
|
148 let contentNode = gInspectee.querySelector("#a"); |
|
149 contentNode.parentNode.removeChild(contentNode); |
|
150 return promiseOnce(gWalker, "new-mutations"); |
|
151 }).then(() => { |
|
152 // Verify that we have an outstanding request (no good way to tell that it's a |
|
153 // getMutations request, but there's nothing else it would be). |
|
154 is(gWalker._requests.length, 1, "Should have an outstanding request."); |
|
155 return gWalker.retainNode(front) |
|
156 }).then(() => { ok(false, "Request should not have succeeded!"); }, |
|
157 (err) => { |
|
158 ok(err, "noSuchActor", "Should have lost the race."); |
|
159 let clientTree = clientOwnershipTree(gWalker); |
|
160 is(gWalker._retainedOrphans.size, 0, "Should have no more retained orphans."); |
|
161 // Don't re-throw the error. |
|
162 }).then(runNextTest)); |
|
163 }); |
|
164 |
|
165 addTest(function cleanup() { |
|
166 delete gWalker; |
|
167 delete gClient; |
|
168 runNextTest(); |
|
169 }); |
|
170 |
|
171 </script> |
|
172 </head> |
|
173 <body> |
|
174 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> |
|
175 <a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> |
|
176 <p id="display"></p> |
|
177 <div id="content" style="display: none"> |
|
178 |
|
179 </div> |
|
180 <pre id="test"> |
|
181 </pre> |
|
182 </body> |
|
183 </html> |