|
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 // Is the currently opened tab focused? |
|
6 function isTabFocused() { |
|
7 let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab); |
|
8 return Services.focus.focusedWindow == tabb.contentWindow; |
|
9 } |
|
10 |
|
11 function isChatFocused(chat) { |
|
12 return SocialChatBar.chatbar._isChatFocused(chat); |
|
13 } |
|
14 |
|
15 function openChatViaUser() { |
|
16 let sidebarDoc = document.getElementById("social-sidebar-browser").contentDocument; |
|
17 let button = sidebarDoc.getElementById("chat-opener"); |
|
18 // Note we must use synthesizeMouseAtCenter() rather than calling |
|
19 // .click() directly as this causes nsIDOMWindowUtils.isHandlingUserInput |
|
20 // to be true. |
|
21 EventUtils.synthesizeMouseAtCenter(button, {}, sidebarDoc.defaultView); |
|
22 } |
|
23 |
|
24 function openChatViaSidebarMessage(port, data, callback) { |
|
25 port.onmessage = function (e) { |
|
26 if (e.data.topic == "chatbox-opened") |
|
27 callback(); |
|
28 } |
|
29 port.postMessage({topic: "test-chatbox-open", data: data}); |
|
30 } |
|
31 |
|
32 function openChatViaWorkerMessage(port, data, callback) { |
|
33 // sadly there is no message coming back to tell us when the chat has |
|
34 // been opened, so we wait until one appears. |
|
35 let chatbar = SocialChatBar.chatbar; |
|
36 let numExpected = chatbar.childElementCount + 1; |
|
37 port.postMessage({topic: "test-worker-chat", data: data}); |
|
38 waitForCondition(function() chatbar.childElementCount == numExpected, |
|
39 function() { |
|
40 // so the child has been added, but we don't know if it |
|
41 // has been intialized - re-request it and the callback |
|
42 // means it's done. Minimized, same as the worker. |
|
43 SocialChatBar.openChat(SocialSidebar.provider, |
|
44 data, |
|
45 function() { |
|
46 callback(); |
|
47 }, |
|
48 "minimized"); |
|
49 }, |
|
50 "No new chat appeared"); |
|
51 } |
|
52 |
|
53 |
|
54 let isSidebarLoaded = false; |
|
55 |
|
56 function startTestAndWaitForSidebar(callback) { |
|
57 let doneCallback; |
|
58 let port = SocialSidebar.provider.getWorkerPort(); |
|
59 function maybeCallback() { |
|
60 if (!doneCallback) |
|
61 callback(port); |
|
62 doneCallback = true; |
|
63 } |
|
64 port.onmessage = function(e) { |
|
65 let topic = e.data.topic; |
|
66 switch (topic) { |
|
67 case "got-sidebar-message": |
|
68 // if sidebar loaded too fast, we need a backup ping |
|
69 case "got-isVisible-response": |
|
70 isSidebarLoaded = true; |
|
71 maybeCallback(); |
|
72 break; |
|
73 case "test-init-done": |
|
74 if (isSidebarLoaded) |
|
75 maybeCallback(); |
|
76 else |
|
77 port.postMessage({topic: "test-isVisible"}); |
|
78 break; |
|
79 } |
|
80 } |
|
81 port.postMessage({topic: "test-init"}); |
|
82 } |
|
83 |
|
84 let manifest = { // normal provider |
|
85 name: "provider 1", |
|
86 origin: "https://example.com", |
|
87 sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html", |
|
88 workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js", |
|
89 iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png" |
|
90 }; |
|
91 |
|
92 function test() { |
|
93 waitForExplicitFinish(); |
|
94 |
|
95 // Note that (probably) due to bug 604289, if a tab is focused but the |
|
96 // focused element is null, our chat windows can "steal" focus. This is |
|
97 // avoided if we explicitly focus an element in the tab. |
|
98 // So we load a page with an <input> field and focus that before testing. |
|
99 let url = "data:text/html;charset=utf-8," + encodeURI('<input id="theinput">'); |
|
100 let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true}); |
|
101 tab.linkedBrowser.addEventListener("load", function tabLoad(event) { |
|
102 tab.linkedBrowser.removeEventListener("load", tabLoad, true); |
|
103 // before every test we focus the input field. |
|
104 let preSubTest = function(cb) { |
|
105 // XXX - when bug 604289 is fixed it should be possible to just do: |
|
106 // tab.linkedBrowser.contentWindow.focus() |
|
107 // but instead we must do: |
|
108 tab.linkedBrowser.contentDocument.getElementById("theinput").focus(); |
|
109 waitForCondition(function() isTabFocused(), cb, "tab should have focus"); |
|
110 } |
|
111 let postSubTest = function(cb) { |
|
112 window.SocialChatBar.chatbar.removeAll(); |
|
113 cb(); |
|
114 } |
|
115 // and run the tests. |
|
116 runSocialTestWithProvider(manifest, function (finishcb) { |
|
117 SocialSidebar.show(); |
|
118 runSocialTests(tests, preSubTest, postSubTest, function () { |
|
119 finishcb(); |
|
120 }); |
|
121 }); |
|
122 }, true); |
|
123 registerCleanupFunction(function() { |
|
124 gBrowser.removeTab(tab); |
|
125 }); |
|
126 |
|
127 } |
|
128 |
|
129 var tests = { |
|
130 // In this test the worker asks the sidebar to open a chat. As that means |
|
131 // we aren't handling user-input we will not focus the chatbar. |
|
132 // Then we do it again - should still not be focused. |
|
133 // Then we perform a user-initiated request - it should get focus. |
|
134 testNoFocusWhenViaWorker: function(next) { |
|
135 startTestAndWaitForSidebar(function(port) { |
|
136 openChatViaSidebarMessage(port, {stealFocus: 1}, function() { |
|
137 ok(true, "got chatbox message"); |
|
138 is(SocialChatBar.chatbar.childElementCount, 1, "exactly 1 chat open"); |
|
139 ok(isTabFocused(), "tab should still be focused"); |
|
140 // re-request the same chat via a message. |
|
141 openChatViaSidebarMessage(port, {stealFocus: 1}, function() { |
|
142 is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open"); |
|
143 ok(isTabFocused(), "tab should still be focused"); |
|
144 // re-request the same chat via user event. |
|
145 openChatViaUser(); |
|
146 waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat), |
|
147 function() { |
|
148 is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open"); |
|
149 is(SocialChatBar.chatbar.selectedChat, SocialChatBar.chatbar.firstElementChild, "chat should be selected"); |
|
150 next(); |
|
151 }, "chat should be focused"); |
|
152 }); |
|
153 }); |
|
154 }); |
|
155 }, |
|
156 |
|
157 // In this test we arrange for the sidebar to open the chat via a simulated |
|
158 // click. This should cause the new chat to be opened and focused. |
|
159 testFocusWhenViaUser: function(next) { |
|
160 startTestAndWaitForSidebar(function(port) { |
|
161 openChatViaUser(); |
|
162 ok(SocialChatBar.chatbar.firstElementChild, "chat opened"); |
|
163 waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat), |
|
164 function() { |
|
165 is(SocialChatBar.chatbar.selectedChat, SocialChatBar.chatbar.firstElementChild, "chat is selected"); |
|
166 next(); |
|
167 }, "chat should be focused"); |
|
168 }); |
|
169 }, |
|
170 |
|
171 // Open a chat via the worker - it will open and not have focus. |
|
172 // Then open the same chat via a sidebar message - it will be restored but |
|
173 // should still not have grabbed focus. |
|
174 testNoFocusOnAutoRestore: function(next) { |
|
175 const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html?id=1"; |
|
176 let chatbar = SocialChatBar.chatbar; |
|
177 startTestAndWaitForSidebar(function(port) { |
|
178 openChatViaWorkerMessage(port, chatUrl, function() { |
|
179 is(chatbar.childElementCount, 1, "exactly 1 chat open"); |
|
180 // bug 865086 opening minimized still sets the window as selected |
|
181 todo(chatbar.selectedChat != chatbar.firstElementChild, "chat is not selected"); |
|
182 ok(isTabFocused(), "tab should be focused"); |
|
183 openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() { |
|
184 is(chatbar.childElementCount, 1, "still 1 chat open"); |
|
185 ok(!chatbar.firstElementChild.minimized, "chat no longer minimized"); |
|
186 // bug 865086 because we marked it selected on open, it still is |
|
187 todo(chatbar.selectedChat != chatbar.firstElementChild, "chat is not selected"); |
|
188 ok(isTabFocused(), "tab should still be focused"); |
|
189 next(); |
|
190 }); |
|
191 }); |
|
192 }); |
|
193 }, |
|
194 |
|
195 // Here we open a chat, which will not be focused. Then we minimize it and |
|
196 // restore it via a titlebar clock - it should get focus at that point. |
|
197 testFocusOnExplicitRestore: function(next) { |
|
198 startTestAndWaitForSidebar(function(port) { |
|
199 openChatViaSidebarMessage(port, {stealFocus: 1}, function() { |
|
200 ok(true, "got chatbox message"); |
|
201 ok(isTabFocused(), "tab should still be focused"); |
|
202 let chatbox = SocialChatBar.chatbar.firstElementChild; |
|
203 ok(chatbox, "chat opened"); |
|
204 chatbox.minimized = true; |
|
205 ok(isTabFocused(), "tab should still be focused"); |
|
206 // pretend we clicked on the titlebar |
|
207 chatbox.onTitlebarClick({button: 0}); |
|
208 waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat), |
|
209 function() { |
|
210 ok(!chatbox.minimized, "chat should have been restored"); |
|
211 ok(isChatFocused(chatbox), "chat should be focused"); |
|
212 is(chatbox, SocialChatBar.chatbar.selectedChat, "chat is marked selected"); |
|
213 next(); |
|
214 }, "chat should have focus"); |
|
215 }); |
|
216 }); |
|
217 }, |
|
218 |
|
219 // Open 2 chats and give 1 focus. Minimize the focused one - the second |
|
220 // should get focus. |
|
221 testMinimizeFocused: function(next) { |
|
222 let chatbar = SocialChatBar.chatbar; |
|
223 startTestAndWaitForSidebar(function(port) { |
|
224 openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() { |
|
225 let chat1 = chatbar.firstElementChild; |
|
226 openChatViaSidebarMessage(port, {stealFocus: 1, id: 2}, function() { |
|
227 is(chatbar.childElementCount, 2, "exactly 2 chats open"); |
|
228 let chat2 = chat1.nextElementSibling || chat1.previousElementSibling; |
|
229 chatbar.selectedChat = chat1; |
|
230 chatbar.focus(); |
|
231 waitForCondition(function() isChatFocused(chat1), |
|
232 function() { |
|
233 is(chat1, SocialChatBar.chatbar.selectedChat, "chat1 is marked selected"); |
|
234 isnot(chat2, SocialChatBar.chatbar.selectedChat, "chat2 is not marked selected"); |
|
235 chat1.minimized = true; |
|
236 waitForCondition(function() isChatFocused(chat2), |
|
237 function() { |
|
238 // minimizing the chat with focus should give it to another. |
|
239 isnot(chat1, SocialChatBar.chatbar.selectedChat, "chat1 is not marked selected"); |
|
240 is(chat2, SocialChatBar.chatbar.selectedChat, "chat2 is marked selected"); |
|
241 next(); |
|
242 }, "chat2 should have focus"); |
|
243 }, "chat1 should have focus"); |
|
244 }); |
|
245 }); |
|
246 }); |
|
247 }, |
|
248 |
|
249 // Open 2 chats, select (but not focus) one, then re-request it be |
|
250 // opened via a message. Focus should not move. |
|
251 testReopenNonFocused: function(next) { |
|
252 let chatbar = SocialChatBar.chatbar; |
|
253 startTestAndWaitForSidebar(function(port) { |
|
254 openChatViaSidebarMessage(port, {id: 1}, function() { |
|
255 let chat1 = chatbar.firstElementChild; |
|
256 openChatViaSidebarMessage(port, {id: 2}, function() { |
|
257 let chat2 = chat1.nextElementSibling || chat1.previousElementSibling; |
|
258 chatbar.selectedChat = chat2; |
|
259 // tab still has focus |
|
260 ok(isTabFocused(), "tab should still be focused"); |
|
261 // re-request the first. |
|
262 openChatViaSidebarMessage(port, {id: 1}, function() { |
|
263 is(chatbar.selectedChat, chat1, "chat1 now selected"); |
|
264 ok(isTabFocused(), "tab should still be focused"); |
|
265 next(); |
|
266 }); |
|
267 }); |
|
268 }); |
|
269 }); |
|
270 }, |
|
271 |
|
272 // Open 2 chats, select and focus the second. Pressing the TAB key should |
|
273 // cause focus to move between all elements in our chat window before moving |
|
274 // to the next chat window. |
|
275 testTab: function(next) { |
|
276 function sendTabAndWaitForFocus(chat, eltid, callback) { |
|
277 // ideally we would use the 'focus' event here, but that doesn't work |
|
278 // as expected for the iframe - the iframe itself never gets the focus |
|
279 // event (apparently the sub-document etc does.) |
|
280 // So just poll for the correct element getting focus... |
|
281 let doc = chat.contentDocument; |
|
282 EventUtils.sendKey("tab"); |
|
283 waitForCondition(function() { |
|
284 let elt = eltid ? doc.getElementById(eltid) : doc.documentElement; |
|
285 return doc.activeElement == elt; |
|
286 }, callback, "element " + eltid + " never got focus"); |
|
287 } |
|
288 |
|
289 let chatbar = SocialChatBar.chatbar; |
|
290 startTestAndWaitForSidebar(function(port) { |
|
291 openChatViaSidebarMessage(port, {id: 1}, function() { |
|
292 let chat1 = chatbar.firstElementChild; |
|
293 openChatViaSidebarMessage(port, {id: 2}, function() { |
|
294 let chat2 = chat1.nextElementSibling || chat1.previousElementSibling; |
|
295 chatbar.selectedChat = chat2; |
|
296 chatbar.focus(); |
|
297 waitForCondition(function() isChatFocused(chatbar.selectedChat), |
|
298 function() { |
|
299 // Our chats have 3 focusable elements, so it takes 4 TABs to move |
|
300 // to the new chat. |
|
301 sendTabAndWaitForFocus(chat2, "input1", function() { |
|
302 is(chat2.contentDocument.activeElement.getAttribute("id"), "input1", |
|
303 "first input field has focus"); |
|
304 ok(isChatFocused(chat2), "new chat still focused after first tab"); |
|
305 sendTabAndWaitForFocus(chat2, "input2", function() { |
|
306 ok(isChatFocused(chat2), "new chat still focused after tab"); |
|
307 is(chat2.contentDocument.activeElement.getAttribute("id"), "input2", |
|
308 "second input field has focus"); |
|
309 sendTabAndWaitForFocus(chat2, "iframe", function() { |
|
310 ok(isChatFocused(chat2), "new chat still focused after tab"); |
|
311 is(chat2.contentDocument.activeElement.getAttribute("id"), "iframe", |
|
312 "iframe has focus"); |
|
313 // this tab now should move to the next chat, but focus the |
|
314 // document element itself (hence the null eltid) |
|
315 sendTabAndWaitForFocus(chat1, null, function() { |
|
316 ok(isChatFocused(chat1), "first chat is focused"); |
|
317 next(); |
|
318 }); |
|
319 }); |
|
320 }); |
|
321 }); |
|
322 }, "chat should have focus"); |
|
323 }); |
|
324 }); |
|
325 }); |
|
326 }, |
|
327 |
|
328 // Open a chat and focus an element other than the first. Move focus to some |
|
329 // other item (the tab itself in this case), then focus the chatbar - the |
|
330 // same element that was previously focused should still have focus. |
|
331 testFocusedElement: function(next) { |
|
332 let chatbar = SocialChatBar.chatbar; |
|
333 startTestAndWaitForSidebar(function(port) { |
|
334 openChatViaUser(); |
|
335 let chat = chatbar.firstElementChild; |
|
336 // need to wait for the content to load before we can focus it. |
|
337 chat.addEventListener("DOMContentLoaded", function DOMContentLoaded() { |
|
338 chat.removeEventListener("DOMContentLoaded", DOMContentLoaded); |
|
339 chat.contentDocument.getElementById("input2").focus(); |
|
340 waitForCondition(function() isChatFocused(chat), |
|
341 function() { |
|
342 is(chat.contentDocument.activeElement.getAttribute("id"), "input2", |
|
343 "correct input field has focus"); |
|
344 // set focus to the tab. |
|
345 let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab); |
|
346 Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0); |
|
347 waitForCondition(function() isTabFocused(), |
|
348 function() { |
|
349 chatbar.focus(); |
|
350 waitForCondition(function() isChatFocused(chat), |
|
351 function() { |
|
352 is(chat.contentDocument.activeElement.getAttribute("id"), "input2", |
|
353 "correct input field still has focus"); |
|
354 next(); |
|
355 }, "chat took focus"); |
|
356 }, "tab has focus"); |
|
357 }, "chat took focus"); |
|
358 }); |
|
359 }); |
|
360 }, |
|
361 }; |