|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 /** |
|
6 * ChromeUtils.js is a set of mochitest utilities that are used to |
|
7 * synthesize events in the browser. These are only used by |
|
8 * mochitest-chrome and browser-chrome tests. Originally these functions were in |
|
9 * EventUtils.js, but when porting to specialPowers, we didn't want |
|
10 * to move unnecessary functions. |
|
11 * |
|
12 * ChromeUtils.js depends on EventUtils.js being loaded. |
|
13 * |
|
14 */ |
|
15 |
|
16 /** |
|
17 * Synthesize a query text content event. |
|
18 * |
|
19 * @param aOffset The character offset. 0 means the first character in the |
|
20 * selection root. |
|
21 * @param aLength The length of getting text. If the length is too long, |
|
22 * the extra length is ignored. |
|
23 * @param aWindow Optional (If null, current |window| will be used) |
|
24 * @return An nsIQueryContentEventResult object. If this failed, |
|
25 * the result might be null. |
|
26 */ |
|
27 function synthesizeQueryTextContent(aOffset, aLength, aWindow) |
|
28 { |
|
29 var utils = _getDOMWindowUtils(aWindow); |
|
30 if (!utils) { |
|
31 return nullptr; |
|
32 } |
|
33 return utils.sendQueryContentEvent(utils.QUERY_TEXT_CONTENT, |
|
34 aOffset, aLength, 0, 0); |
|
35 } |
|
36 |
|
37 /** |
|
38 * Synthesize a query caret rect event. |
|
39 * |
|
40 * @param aOffset The caret offset. 0 means left side of the first character |
|
41 * in the selection root. |
|
42 * @param aWindow Optional (If null, current |window| will be used) |
|
43 * @return An nsIQueryContentEventResult object. If this failed, |
|
44 * the result might be null. |
|
45 */ |
|
46 function synthesizeQueryCaretRect(aOffset, aWindow) |
|
47 { |
|
48 var utils = _getDOMWindowUtils(aWindow); |
|
49 if (!utils) { |
|
50 return nullptr; |
|
51 } |
|
52 return utils.sendQueryContentEvent(utils.QUERY_CARET_RECT, |
|
53 aOffset, 0, 0, 0); |
|
54 } |
|
55 |
|
56 /** |
|
57 * Synthesize a query text rect event. |
|
58 * |
|
59 * @param aOffset The character offset. 0 means the first character in the |
|
60 * selection root. |
|
61 * @param aLength The length of the text. If the length is too long, |
|
62 * the extra length is ignored. |
|
63 * @param aWindow Optional (If null, current |window| will be used) |
|
64 * @return An nsIQueryContentEventResult object. If this failed, |
|
65 * the result might be null. |
|
66 */ |
|
67 function synthesizeQueryTextRect(aOffset, aLength, aWindow) |
|
68 { |
|
69 var utils = _getDOMWindowUtils(aWindow); |
|
70 if (!utils) { |
|
71 return nullptr; |
|
72 } |
|
73 return utils.sendQueryContentEvent(utils.QUERY_TEXT_RECT, |
|
74 aOffset, aLength, 0, 0); |
|
75 } |
|
76 |
|
77 /** |
|
78 * Synthesize a query editor rect event. |
|
79 * |
|
80 * @param aWindow Optional (If null, current |window| will be used) |
|
81 * @return An nsIQueryContentEventResult object. If this failed, |
|
82 * the result might be null. |
|
83 */ |
|
84 function synthesizeQueryEditorRect(aWindow) |
|
85 { |
|
86 var utils = _getDOMWindowUtils(aWindow); |
|
87 if (!utils) { |
|
88 return nullptr; |
|
89 } |
|
90 return utils.sendQueryContentEvent(utils.QUERY_EDITOR_RECT, 0, 0, 0, 0); |
|
91 } |
|
92 |
|
93 /** |
|
94 * Synthesize a character at point event. |
|
95 * |
|
96 * @param aX, aY The offset in the client area of the DOM window. |
|
97 * @param aWindow Optional (If null, current |window| will be used) |
|
98 * @return An nsIQueryContentEventResult object. If this failed, |
|
99 * the result might be null. |
|
100 */ |
|
101 function synthesizeCharAtPoint(aX, aY, aWindow) |
|
102 { |
|
103 var utils = _getDOMWindowUtils(aWindow); |
|
104 if (!utils) { |
|
105 return nullptr; |
|
106 } |
|
107 return utils.sendQueryContentEvent(utils.QUERY_CHARACTER_AT_POINT, |
|
108 0, 0, aX, aY); |
|
109 } |
|
110 |
|
111 /** |
|
112 * Emulate a dragstart event. |
|
113 * element - element to fire the dragstart event on |
|
114 * expectedDragData - the data you expect the data transfer to contain afterwards |
|
115 * This data is in the format: |
|
116 * [ [ {type: value, data: value, test: function}, ... ], ... ] |
|
117 * can be null |
|
118 * aWindow - optional; defaults to the current window object. |
|
119 * x - optional; initial x coordinate |
|
120 * y - optional; initial y coordinate |
|
121 * Returns null if data matches. |
|
122 * Returns the event.dataTransfer if data does not match |
|
123 * |
|
124 * eqTest is an optional function if comparison can't be done with x == y; |
|
125 * function (actualData, expectedData) {return boolean} |
|
126 * @param actualData from dataTransfer |
|
127 * @param expectedData from expectedDragData |
|
128 * see bug 462172 for example of use |
|
129 * |
|
130 */ |
|
131 function synthesizeDragStart(element, expectedDragData, aWindow, x, y) |
|
132 { |
|
133 if (!aWindow) |
|
134 aWindow = window; |
|
135 x = x || 2; |
|
136 y = y || 2; |
|
137 const step = 9; |
|
138 |
|
139 var result = "trapDrag was not called"; |
|
140 var trapDrag = function(event) { |
|
141 try { |
|
142 var dataTransfer = event.dataTransfer; |
|
143 result = null; |
|
144 if (!dataTransfer) |
|
145 throw "no dataTransfer"; |
|
146 if (expectedDragData == null || |
|
147 dataTransfer.mozItemCount != expectedDragData.length) |
|
148 throw dataTransfer; |
|
149 for (var i = 0; i < dataTransfer.mozItemCount; i++) { |
|
150 var dtTypes = dataTransfer.mozTypesAt(i); |
|
151 if (dtTypes.length != expectedDragData[i].length) |
|
152 throw dataTransfer; |
|
153 for (var j = 0; j < dtTypes.length; j++) { |
|
154 if (dtTypes[j] != expectedDragData[i][j].type) |
|
155 throw dataTransfer; |
|
156 var dtData = dataTransfer.mozGetDataAt(dtTypes[j],i); |
|
157 if (expectedDragData[i][j].eqTest) { |
|
158 if (!expectedDragData[i][j].eqTest(dtData, expectedDragData[i][j].data)) |
|
159 throw dataTransfer; |
|
160 } |
|
161 else if (expectedDragData[i][j].data != dtData) |
|
162 throw dataTransfer; |
|
163 } |
|
164 } |
|
165 } catch(ex) { |
|
166 result = ex; |
|
167 } |
|
168 event.preventDefault(); |
|
169 event.stopPropagation(); |
|
170 } |
|
171 aWindow.addEventListener("dragstart", trapDrag, false); |
|
172 synthesizeMouse(element, x, y, { type: "mousedown" }, aWindow); |
|
173 x += step; y += step; |
|
174 synthesizeMouse(element, x, y, { type: "mousemove" }, aWindow); |
|
175 x += step; y += step; |
|
176 synthesizeMouse(element, x, y, { type: "mousemove" }, aWindow); |
|
177 aWindow.removeEventListener("dragstart", trapDrag, false); |
|
178 synthesizeMouse(element, x, y, { type: "mouseup" }, aWindow); |
|
179 return result; |
|
180 } |
|
181 |
|
182 /** |
|
183 * Emulate a drop by emulating a dragstart and firing events dragenter, dragover, and drop. |
|
184 * srcElement - the element to use to start the drag, usually the same as destElement |
|
185 * but if destElement isn't suitable to start a drag on pass a suitable |
|
186 * element for srcElement |
|
187 * destElement - the element to fire the dragover, dragleave and drop events |
|
188 * dragData - the data to supply for the data transfer |
|
189 * This data is in the format: |
|
190 * [ [ {type: value, data: value}, ...], ... ] |
|
191 * dropEffect - the drop effect to set during the dragstart event, or 'move' if null |
|
192 * aWindow - optional; defaults to the current window object. |
|
193 * eventUtils - optional; allows you to pass in a reference to EventUtils.js. |
|
194 * If the eventUtils parameter is not passed in, we assume EventUtils.js is |
|
195 * in the scope. Used by browser-chrome tests. |
|
196 * |
|
197 * Returns the drop effect that was desired. |
|
198 */ |
|
199 function synthesizeDrop(srcElement, destElement, dragData, dropEffect, aWindow, eventUtils) |
|
200 { |
|
201 if (!aWindow) |
|
202 aWindow = window; |
|
203 |
|
204 var synthesizeMouseAtCenter = (eventUtils || window).synthesizeMouseAtCenter; |
|
205 var synthesizeMouse = (eventUtils || window).synthesizeMouse; |
|
206 |
|
207 var gWindowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor). |
|
208 getInterface(Components.interfaces.nsIDOMWindowUtils); |
|
209 var ds = Components.classes["@mozilla.org/widget/dragservice;1"]. |
|
210 getService(Components.interfaces.nsIDragService); |
|
211 |
|
212 var dataTransfer; |
|
213 var trapDrag = function(event) { |
|
214 dataTransfer = event.dataTransfer; |
|
215 for (var i = 0; i < dragData.length; i++) { |
|
216 var item = dragData[i]; |
|
217 for (var j = 0; j < item.length; j++) { |
|
218 dataTransfer.mozSetDataAt(item[j].type, item[j].data, i); |
|
219 } |
|
220 } |
|
221 dataTransfer.dropEffect = dropEffect || "move"; |
|
222 event.preventDefault(); |
|
223 event.stopPropagation(); |
|
224 } |
|
225 |
|
226 ds.startDragSession(); |
|
227 |
|
228 try { |
|
229 // need to use real mouse action |
|
230 aWindow.addEventListener("dragstart", trapDrag, true); |
|
231 synthesizeMouseAtCenter(srcElement, { type: "mousedown" }, aWindow); |
|
232 |
|
233 var rect = srcElement.getBoundingClientRect(); |
|
234 var x = rect.width / 2; |
|
235 var y = rect.height / 2; |
|
236 synthesizeMouse(srcElement, x, y, { type: "mousemove" }, aWindow); |
|
237 synthesizeMouse(srcElement, x+10, y+10, { type: "mousemove" }, aWindow); |
|
238 aWindow.removeEventListener("dragstart", trapDrag, true); |
|
239 |
|
240 event = aWindow.document.createEvent("DragEvents"); |
|
241 event.initDragEvent("dragenter", true, true, aWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer); |
|
242 gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true); |
|
243 |
|
244 var event = aWindow.document.createEvent("DragEvents"); |
|
245 event.initDragEvent("dragover", true, true, aWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer); |
|
246 if (gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true)) { |
|
247 synthesizeMouseAtCenter(destElement, { type: "mouseup" }, aWindow); |
|
248 return "none"; |
|
249 } |
|
250 |
|
251 if (dataTransfer.dropEffect != "none") { |
|
252 event = aWindow.document.createEvent("DragEvents"); |
|
253 event.initDragEvent("drop", true, true, aWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer); |
|
254 gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true); |
|
255 } |
|
256 |
|
257 synthesizeMouseAtCenter(destElement, { type: "mouseup" }, aWindow); |
|
258 |
|
259 return dataTransfer.dropEffect; |
|
260 } finally { |
|
261 ds.endDragSession(true); |
|
262 } |
|
263 }; |
|
264 |
|
265 var PluginUtils = |
|
266 { |
|
267 withTestPlugin : function(callback) |
|
268 { |
|
269 if (typeof Components == "undefined") |
|
270 { |
|
271 todo(false, "Not a Mozilla-based browser"); |
|
272 return false; |
|
273 } |
|
274 |
|
275 var ph = Components.classes["@mozilla.org/plugin/host;1"] |
|
276 .getService(Components.interfaces.nsIPluginHost); |
|
277 var tags = ph.getPluginTags(); |
|
278 |
|
279 // Find the test plugin |
|
280 for (var i = 0; i < tags.length; i++) |
|
281 { |
|
282 if (tags[i].name == "Test Plug-in") |
|
283 { |
|
284 callback(tags[i]); |
|
285 return true; |
|
286 } |
|
287 } |
|
288 todo(false, "Need a test plugin on this platform"); |
|
289 return false; |
|
290 } |
|
291 }; |