|
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 gInspectee = null; |
|
25 var gWalker = null; |
|
26 var gClient = null; |
|
27 var gCleanupConnection = null; |
|
28 |
|
29 function setup(callback) { |
|
30 let url = document.getElementById("inspectorContent").href; |
|
31 gCleanupConnection = attachURL(url, function(err, client, tab, doc) { |
|
32 gInspectee = doc; |
|
33 let {InspectorFront} = devtools.require("devtools/server/actors/inspector"); |
|
34 let inspector = InspectorFront(client, tab); |
|
35 promiseDone(inspector.getWalker().then(walker => { |
|
36 gClient = client; |
|
37 gWalker = walker; |
|
38 }).then(callback)); |
|
39 }); |
|
40 } |
|
41 |
|
42 function teardown() { |
|
43 gWalker = null; |
|
44 gClient = null; |
|
45 gInspectee = null; |
|
46 if (gCleanupConnection) { |
|
47 gCleanupConnection(); |
|
48 gCleanupConnection = null; |
|
49 } |
|
50 } |
|
51 |
|
52 function assertOwnership() { |
|
53 let num = assertOwnershipTrees(gWalker); |
|
54 } |
|
55 |
|
56 function setParent(nodeSelector, newParentSelector) { |
|
57 let node = gInspectee.querySelector(nodeSelector); |
|
58 if (newParentSelector) { |
|
59 let newParent = gInspectee.querySelector(newParentSelector); |
|
60 newParent.appendChild(node); |
|
61 } else { |
|
62 node.parentNode.removeChild(node); |
|
63 } |
|
64 } |
|
65 |
|
66 function loadSelector(selector) { |
|
67 return gWalker.querySelectorAll(gWalker.rootNode, selector).then(nodeList => { |
|
68 return nodeList.items(); |
|
69 }); |
|
70 } |
|
71 |
|
72 function loadSelectors(selectors) { |
|
73 return promise.all([loadSelector(sel) for (sel of selectors)]); |
|
74 } |
|
75 |
|
76 function doMoves(moves) { |
|
77 for (let move of moves) { |
|
78 setParent(move[0], move[1]); |
|
79 } |
|
80 } |
|
81 |
|
82 /** |
|
83 * Test a set of tree rearrangements and make sure they cause the expected changes. |
|
84 */ |
|
85 |
|
86 var gDummySerial = 0; |
|
87 |
|
88 function mutationTest(testSpec) { |
|
89 return function() { |
|
90 setup(() => { |
|
91 promiseDone(loadSelectors(testSpec.load || ["html"]).then(() => { |
|
92 gWalker.autoCleanup = !!testSpec.autoCleanup; |
|
93 if (testSpec.preCheck) { |
|
94 testSpec.preCheck(); |
|
95 } |
|
96 doMoves(testSpec.moves || []); |
|
97 |
|
98 // Some of these moves will trigger no mutation events, |
|
99 // so do a dummy change to the root node to trigger |
|
100 // a mutation event anyway. |
|
101 gInspectee.documentElement.setAttribute("data-dummy", gDummySerial++); |
|
102 |
|
103 gWalker.once("mutations", (mutations) => { |
|
104 // Filter out our dummy mutation. |
|
105 let mutations = mutations.filter(change => { |
|
106 if (change.type == "attributes" && |
|
107 change.attributeName == "data-dummy") { |
|
108 return false; |
|
109 } |
|
110 return true; |
|
111 }); |
|
112 assertOwnership(); |
|
113 if (testSpec.postCheck) { |
|
114 testSpec.postCheck(mutations); |
|
115 } |
|
116 teardown(); |
|
117 runNextTest(); |
|
118 }); |
|
119 })); |
|
120 }) |
|
121 } |
|
122 } |
|
123 |
|
124 // Verify that our dummy mutation works. |
|
125 addTest(mutationTest({ |
|
126 autoCleanup: false, |
|
127 postCheck: function(mutations) { |
|
128 is(mutations.length, 0, "Dummy mutation is filtered out."); |
|
129 } |
|
130 })); |
|
131 |
|
132 // Test a simple move to a different location in the sibling list for the same |
|
133 // parent. |
|
134 addTest(mutationTest({ |
|
135 autoCleanup: false, |
|
136 load: ["#longlist div"], |
|
137 moves: [ |
|
138 ["#a", "#longlist"] |
|
139 ], |
|
140 postCheck: function(mutations) { |
|
141 let remove = mutations[0]; |
|
142 is(remove.type, "childList", "First mutation should be a childList.") |
|
143 ok(remove.removed.length > 0, "First mutation should be a removal.") |
|
144 let add = mutations[1]; |
|
145 is(add.type, "childList", "Second mutation should be a childList removal.") |
|
146 ok(add.added.length > 0, "Second mutation should be an addition.") |
|
147 let a = add.added[0]; |
|
148 is(a.id, "a", "Added node should be #a"); |
|
149 is(a.parentNode(), remove.target, "Should still be a child of longlist."); |
|
150 is(remove.target, add.target, "First and second mutations should be against the same node."); |
|
151 } |
|
152 })); |
|
153 |
|
154 // Test a move to another location that is within our ownership tree. |
|
155 addTest(mutationTest({ |
|
156 autoCleanup: false, |
|
157 load: ["#longlist div", "#longlist-sibling"], |
|
158 moves: [ |
|
159 ["#a", "#longlist-sibling"] |
|
160 ], |
|
161 postCheck: function(mutations) { |
|
162 let remove = mutations[0]; |
|
163 is(remove.type, "childList", "First mutation should be a childList.") |
|
164 ok(remove.removed.length > 0, "First mutation should be a removal.") |
|
165 let add = mutations[1]; |
|
166 is(add.type, "childList", "Second mutation should be a childList removal.") |
|
167 ok(add.added.length > 0, "Second mutation should be an addition.") |
|
168 let a = add.added[0]; |
|
169 is(a.id, "a", "Added node should be #a"); |
|
170 is(a.parentNode(), add.target, "Should still be a child of longlist."); |
|
171 is(add.target.id, "longlist-sibling", "long-sibling should be the target."); |
|
172 } |
|
173 })); |
|
174 |
|
175 // Move an unseen node with a seen parent into our ownership tree - should generate a |
|
176 // childList pair with no adds or removes. |
|
177 addTest(mutationTest({ |
|
178 autoCleanup: false, |
|
179 load: ["#longlist"], |
|
180 moves: [ |
|
181 ["#longlist-sibling", "#longlist"] |
|
182 ], |
|
183 postCheck: function(mutations) { |
|
184 is(mutations.length, 2, "Should generate two mutations"); |
|
185 is(mutations[0].type, "childList", "Should be childList mutations."); |
|
186 is(mutations[0].added.length, 0, "Should have no adds."); |
|
187 is(mutations[0].removed.length, 0, "Should have no removes."); |
|
188 is(mutations[1].type, "childList", "Should be childList mutations."); |
|
189 is(mutations[1].added.length, 0, "Should have no adds."); |
|
190 is(mutations[1].removed.length, 0, "Should have no removes."); |
|
191 } |
|
192 })); |
|
193 |
|
194 // Move an unseen node with an unseen parent into our ownership tree. Should only |
|
195 // generate one childList mutation with no adds or removes. |
|
196 addTest(mutationTest({ |
|
197 autoCleanup: false, |
|
198 load: ["#longlist div"], |
|
199 moves: [ |
|
200 ["#longlist-sibling-firstchild", "#longlist"] |
|
201 ], |
|
202 postCheck: function(mutations) { |
|
203 is(mutations.length, 1, "Should generate two mutations"); |
|
204 is(mutations[0].type, "childList", "Should be childList mutations."); |
|
205 is(mutations[0].added.length, 0, "Should have no adds."); |
|
206 is(mutations[0].removed.length, 0, "Should have no removes."); |
|
207 } |
|
208 })); |
|
209 |
|
210 // Move a node between unseen nodes, should generate no mutations. |
|
211 addTest(mutationTest({ |
|
212 autoCleanup: false, |
|
213 load: ["html"], |
|
214 moves: [ |
|
215 ["#longlist-sibling", "#longlist"] |
|
216 ], |
|
217 postCheck: function(mutations) { |
|
218 is(mutations.length, 0, "Should generate no mutations."); |
|
219 } |
|
220 })); |
|
221 |
|
222 // Orphan a node and don't clean it up |
|
223 addTest(mutationTest({ |
|
224 autoCleanup: false, |
|
225 load: ["#longlist div"], |
|
226 moves: [ |
|
227 ["#longlist", null] |
|
228 ], |
|
229 postCheck: function(mutations) { |
|
230 is(mutations.length, 1, "Should generate one mutation."); |
|
231 let change = mutations[0]; |
|
232 is(change.type, "childList", "Should be a childList."); |
|
233 is(change.removed.length, 1, "Should have removed a child."); |
|
234 let ownership = clientOwnershipTree(gWalker); |
|
235 is(ownership.orphaned.length, 1, "Should have one orphaned subtree."); |
|
236 is(ownershipTreeSize(ownership.orphaned[0]), 27, "Should have orphaned longlist and 26 children."); |
|
237 } |
|
238 })); |
|
239 |
|
240 // Orphan a node, and do clean it up. |
|
241 addTest(mutationTest({ |
|
242 autoCleanup: true, |
|
243 load: ["#longlist div"], |
|
244 moves: [ |
|
245 ["#longlist", null] |
|
246 ], |
|
247 postCheck: function(mutations) { |
|
248 is(mutations.length, 1, "Should generate one mutation."); |
|
249 let change = mutations[0]; |
|
250 is(change.type, "childList", "Should be a childList."); |
|
251 is(change.removed.length, 1, "Should have removed a child."); |
|
252 let ownership = clientOwnershipTree(gWalker); |
|
253 is(ownership.orphaned.length, 0, "Should have no orphaned subtrees."); |
|
254 } |
|
255 })); |
|
256 |
|
257 // Orphan a node by moving it into the tree but out of our visible subtree. |
|
258 addTest(mutationTest({ |
|
259 autoCleanup: false, |
|
260 load: ["#longlist div"], |
|
261 moves: [ |
|
262 ["#longlist", "#longlist-sibling"] |
|
263 ], |
|
264 postCheck: function(mutations) { |
|
265 is(mutations.length, 1, "Should generate one mutation."); |
|
266 let change = mutations[0]; |
|
267 is(change.type, "childList", "Should be a childList."); |
|
268 is(change.removed.length, 1, "Should have removed a child."); |
|
269 let ownership = clientOwnershipTree(gWalker); |
|
270 is(ownership.orphaned.length, 1, "Should have one orphaned subtree."); |
|
271 is(ownershipTreeSize(ownership.orphaned[0]), 27, "Should have orphaned longlist and 26 children."); |
|
272 } |
|
273 })); |
|
274 |
|
275 // Orphan a node by moving it into the tree but out of our visible subtree, and clean it up. |
|
276 addTest(mutationTest({ |
|
277 autoCleanup: true, |
|
278 load: ["#longlist div"], |
|
279 moves: [ |
|
280 ["#longlist", "#longlist-sibling"] |
|
281 ], |
|
282 postCheck: function(mutations) { |
|
283 is(mutations.length, 1, "Should generate one mutation."); |
|
284 let change = mutations[0]; |
|
285 is(change.type, "childList", "Should be a childList."); |
|
286 is(change.removed.length, 1, "Should have removed a child."); |
|
287 let ownership = clientOwnershipTree(gWalker); |
|
288 is(ownership.orphaned.length, 0, "Should have no orphaned subtrees."); |
|
289 } |
|
290 })); |
|
291 |
|
292 |
|
293 addTest(function cleanup() { |
|
294 delete gInspectee; |
|
295 delete gWalker; |
|
296 delete gClient; |
|
297 runNextTest(); |
|
298 }); |
|
299 |
|
300 |
|
301 </script> |
|
302 </head> |
|
303 <body> |
|
304 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> |
|
305 <a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> |
|
306 <p id="display"></p> |
|
307 <div id="content" style="display: none"> |
|
308 |
|
309 </div> |
|
310 <pre id="test"> |
|
311 </pre> |
|
312 </body> |
|
313 </html> |