1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/base/content/test/social/browser_social_chatwindowfocus.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,361 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +// Is the currently opened tab focused? 1.9 +function isTabFocused() { 1.10 + let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab); 1.11 + return Services.focus.focusedWindow == tabb.contentWindow; 1.12 +} 1.13 + 1.14 +function isChatFocused(chat) { 1.15 + return SocialChatBar.chatbar._isChatFocused(chat); 1.16 +} 1.17 + 1.18 +function openChatViaUser() { 1.19 + let sidebarDoc = document.getElementById("social-sidebar-browser").contentDocument; 1.20 + let button = sidebarDoc.getElementById("chat-opener"); 1.21 + // Note we must use synthesizeMouseAtCenter() rather than calling 1.22 + // .click() directly as this causes nsIDOMWindowUtils.isHandlingUserInput 1.23 + // to be true. 1.24 + EventUtils.synthesizeMouseAtCenter(button, {}, sidebarDoc.defaultView); 1.25 +} 1.26 + 1.27 +function openChatViaSidebarMessage(port, data, callback) { 1.28 + port.onmessage = function (e) { 1.29 + if (e.data.topic == "chatbox-opened") 1.30 + callback(); 1.31 + } 1.32 + port.postMessage({topic: "test-chatbox-open", data: data}); 1.33 +} 1.34 + 1.35 +function openChatViaWorkerMessage(port, data, callback) { 1.36 + // sadly there is no message coming back to tell us when the chat has 1.37 + // been opened, so we wait until one appears. 1.38 + let chatbar = SocialChatBar.chatbar; 1.39 + let numExpected = chatbar.childElementCount + 1; 1.40 + port.postMessage({topic: "test-worker-chat", data: data}); 1.41 + waitForCondition(function() chatbar.childElementCount == numExpected, 1.42 + function() { 1.43 + // so the child has been added, but we don't know if it 1.44 + // has been intialized - re-request it and the callback 1.45 + // means it's done. Minimized, same as the worker. 1.46 + SocialChatBar.openChat(SocialSidebar.provider, 1.47 + data, 1.48 + function() { 1.49 + callback(); 1.50 + }, 1.51 + "minimized"); 1.52 + }, 1.53 + "No new chat appeared"); 1.54 +} 1.55 + 1.56 + 1.57 +let isSidebarLoaded = false; 1.58 + 1.59 +function startTestAndWaitForSidebar(callback) { 1.60 + let doneCallback; 1.61 + let port = SocialSidebar.provider.getWorkerPort(); 1.62 + function maybeCallback() { 1.63 + if (!doneCallback) 1.64 + callback(port); 1.65 + doneCallback = true; 1.66 + } 1.67 + port.onmessage = function(e) { 1.68 + let topic = e.data.topic; 1.69 + switch (topic) { 1.70 + case "got-sidebar-message": 1.71 + // if sidebar loaded too fast, we need a backup ping 1.72 + case "got-isVisible-response": 1.73 + isSidebarLoaded = true; 1.74 + maybeCallback(); 1.75 + break; 1.76 + case "test-init-done": 1.77 + if (isSidebarLoaded) 1.78 + maybeCallback(); 1.79 + else 1.80 + port.postMessage({topic: "test-isVisible"}); 1.81 + break; 1.82 + } 1.83 + } 1.84 + port.postMessage({topic: "test-init"}); 1.85 +} 1.86 + 1.87 +let manifest = { // normal provider 1.88 + name: "provider 1", 1.89 + origin: "https://example.com", 1.90 + sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html", 1.91 + workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js", 1.92 + iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png" 1.93 +}; 1.94 + 1.95 +function test() { 1.96 + waitForExplicitFinish(); 1.97 + 1.98 + // Note that (probably) due to bug 604289, if a tab is focused but the 1.99 + // focused element is null, our chat windows can "steal" focus. This is 1.100 + // avoided if we explicitly focus an element in the tab. 1.101 + // So we load a page with an <input> field and focus that before testing. 1.102 + let url = "data:text/html;charset=utf-8," + encodeURI('<input id="theinput">'); 1.103 + let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true}); 1.104 + tab.linkedBrowser.addEventListener("load", function tabLoad(event) { 1.105 + tab.linkedBrowser.removeEventListener("load", tabLoad, true); 1.106 + // before every test we focus the input field. 1.107 + let preSubTest = function(cb) { 1.108 + // XXX - when bug 604289 is fixed it should be possible to just do: 1.109 + // tab.linkedBrowser.contentWindow.focus() 1.110 + // but instead we must do: 1.111 + tab.linkedBrowser.contentDocument.getElementById("theinput").focus(); 1.112 + waitForCondition(function() isTabFocused(), cb, "tab should have focus"); 1.113 + } 1.114 + let postSubTest = function(cb) { 1.115 + window.SocialChatBar.chatbar.removeAll(); 1.116 + cb(); 1.117 + } 1.118 + // and run the tests. 1.119 + runSocialTestWithProvider(manifest, function (finishcb) { 1.120 + SocialSidebar.show(); 1.121 + runSocialTests(tests, preSubTest, postSubTest, function () { 1.122 + finishcb(); 1.123 + }); 1.124 + }); 1.125 + }, true); 1.126 + registerCleanupFunction(function() { 1.127 + gBrowser.removeTab(tab); 1.128 + }); 1.129 + 1.130 +} 1.131 + 1.132 +var tests = { 1.133 + // In this test the worker asks the sidebar to open a chat. As that means 1.134 + // we aren't handling user-input we will not focus the chatbar. 1.135 + // Then we do it again - should still not be focused. 1.136 + // Then we perform a user-initiated request - it should get focus. 1.137 + testNoFocusWhenViaWorker: function(next) { 1.138 + startTestAndWaitForSidebar(function(port) { 1.139 + openChatViaSidebarMessage(port, {stealFocus: 1}, function() { 1.140 + ok(true, "got chatbox message"); 1.141 + is(SocialChatBar.chatbar.childElementCount, 1, "exactly 1 chat open"); 1.142 + ok(isTabFocused(), "tab should still be focused"); 1.143 + // re-request the same chat via a message. 1.144 + openChatViaSidebarMessage(port, {stealFocus: 1}, function() { 1.145 + is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open"); 1.146 + ok(isTabFocused(), "tab should still be focused"); 1.147 + // re-request the same chat via user event. 1.148 + openChatViaUser(); 1.149 + waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat), 1.150 + function() { 1.151 + is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open"); 1.152 + is(SocialChatBar.chatbar.selectedChat, SocialChatBar.chatbar.firstElementChild, "chat should be selected"); 1.153 + next(); 1.154 + }, "chat should be focused"); 1.155 + }); 1.156 + }); 1.157 + }); 1.158 + }, 1.159 + 1.160 + // In this test we arrange for the sidebar to open the chat via a simulated 1.161 + // click. This should cause the new chat to be opened and focused. 1.162 + testFocusWhenViaUser: function(next) { 1.163 + startTestAndWaitForSidebar(function(port) { 1.164 + openChatViaUser(); 1.165 + ok(SocialChatBar.chatbar.firstElementChild, "chat opened"); 1.166 + waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat), 1.167 + function() { 1.168 + is(SocialChatBar.chatbar.selectedChat, SocialChatBar.chatbar.firstElementChild, "chat is selected"); 1.169 + next(); 1.170 + }, "chat should be focused"); 1.171 + }); 1.172 + }, 1.173 + 1.174 + // Open a chat via the worker - it will open and not have focus. 1.175 + // Then open the same chat via a sidebar message - it will be restored but 1.176 + // should still not have grabbed focus. 1.177 + testNoFocusOnAutoRestore: function(next) { 1.178 + const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html?id=1"; 1.179 + let chatbar = SocialChatBar.chatbar; 1.180 + startTestAndWaitForSidebar(function(port) { 1.181 + openChatViaWorkerMessage(port, chatUrl, function() { 1.182 + is(chatbar.childElementCount, 1, "exactly 1 chat open"); 1.183 + // bug 865086 opening minimized still sets the window as selected 1.184 + todo(chatbar.selectedChat != chatbar.firstElementChild, "chat is not selected"); 1.185 + ok(isTabFocused(), "tab should be focused"); 1.186 + openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() { 1.187 + is(chatbar.childElementCount, 1, "still 1 chat open"); 1.188 + ok(!chatbar.firstElementChild.minimized, "chat no longer minimized"); 1.189 + // bug 865086 because we marked it selected on open, it still is 1.190 + todo(chatbar.selectedChat != chatbar.firstElementChild, "chat is not selected"); 1.191 + ok(isTabFocused(), "tab should still be focused"); 1.192 + next(); 1.193 + }); 1.194 + }); 1.195 + }); 1.196 + }, 1.197 + 1.198 + // Here we open a chat, which will not be focused. Then we minimize it and 1.199 + // restore it via a titlebar clock - it should get focus at that point. 1.200 + testFocusOnExplicitRestore: function(next) { 1.201 + startTestAndWaitForSidebar(function(port) { 1.202 + openChatViaSidebarMessage(port, {stealFocus: 1}, function() { 1.203 + ok(true, "got chatbox message"); 1.204 + ok(isTabFocused(), "tab should still be focused"); 1.205 + let chatbox = SocialChatBar.chatbar.firstElementChild; 1.206 + ok(chatbox, "chat opened"); 1.207 + chatbox.minimized = true; 1.208 + ok(isTabFocused(), "tab should still be focused"); 1.209 + // pretend we clicked on the titlebar 1.210 + chatbox.onTitlebarClick({button: 0}); 1.211 + waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat), 1.212 + function() { 1.213 + ok(!chatbox.minimized, "chat should have been restored"); 1.214 + ok(isChatFocused(chatbox), "chat should be focused"); 1.215 + is(chatbox, SocialChatBar.chatbar.selectedChat, "chat is marked selected"); 1.216 + next(); 1.217 + }, "chat should have focus"); 1.218 + }); 1.219 + }); 1.220 + }, 1.221 + 1.222 + // Open 2 chats and give 1 focus. Minimize the focused one - the second 1.223 + // should get focus. 1.224 + testMinimizeFocused: function(next) { 1.225 + let chatbar = SocialChatBar.chatbar; 1.226 + startTestAndWaitForSidebar(function(port) { 1.227 + openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() { 1.228 + let chat1 = chatbar.firstElementChild; 1.229 + openChatViaSidebarMessage(port, {stealFocus: 1, id: 2}, function() { 1.230 + is(chatbar.childElementCount, 2, "exactly 2 chats open"); 1.231 + let chat2 = chat1.nextElementSibling || chat1.previousElementSibling; 1.232 + chatbar.selectedChat = chat1; 1.233 + chatbar.focus(); 1.234 + waitForCondition(function() isChatFocused(chat1), 1.235 + function() { 1.236 + is(chat1, SocialChatBar.chatbar.selectedChat, "chat1 is marked selected"); 1.237 + isnot(chat2, SocialChatBar.chatbar.selectedChat, "chat2 is not marked selected"); 1.238 + chat1.minimized = true; 1.239 + waitForCondition(function() isChatFocused(chat2), 1.240 + function() { 1.241 + // minimizing the chat with focus should give it to another. 1.242 + isnot(chat1, SocialChatBar.chatbar.selectedChat, "chat1 is not marked selected"); 1.243 + is(chat2, SocialChatBar.chatbar.selectedChat, "chat2 is marked selected"); 1.244 + next(); 1.245 + }, "chat2 should have focus"); 1.246 + }, "chat1 should have focus"); 1.247 + }); 1.248 + }); 1.249 + }); 1.250 + }, 1.251 + 1.252 + // Open 2 chats, select (but not focus) one, then re-request it be 1.253 + // opened via a message. Focus should not move. 1.254 + testReopenNonFocused: function(next) { 1.255 + let chatbar = SocialChatBar.chatbar; 1.256 + startTestAndWaitForSidebar(function(port) { 1.257 + openChatViaSidebarMessage(port, {id: 1}, function() { 1.258 + let chat1 = chatbar.firstElementChild; 1.259 + openChatViaSidebarMessage(port, {id: 2}, function() { 1.260 + let chat2 = chat1.nextElementSibling || chat1.previousElementSibling; 1.261 + chatbar.selectedChat = chat2; 1.262 + // tab still has focus 1.263 + ok(isTabFocused(), "tab should still be focused"); 1.264 + // re-request the first. 1.265 + openChatViaSidebarMessage(port, {id: 1}, function() { 1.266 + is(chatbar.selectedChat, chat1, "chat1 now selected"); 1.267 + ok(isTabFocused(), "tab should still be focused"); 1.268 + next(); 1.269 + }); 1.270 + }); 1.271 + }); 1.272 + }); 1.273 + }, 1.274 + 1.275 + // Open 2 chats, select and focus the second. Pressing the TAB key should 1.276 + // cause focus to move between all elements in our chat window before moving 1.277 + // to the next chat window. 1.278 + testTab: function(next) { 1.279 + function sendTabAndWaitForFocus(chat, eltid, callback) { 1.280 + // ideally we would use the 'focus' event here, but that doesn't work 1.281 + // as expected for the iframe - the iframe itself never gets the focus 1.282 + // event (apparently the sub-document etc does.) 1.283 + // So just poll for the correct element getting focus... 1.284 + let doc = chat.contentDocument; 1.285 + EventUtils.sendKey("tab"); 1.286 + waitForCondition(function() { 1.287 + let elt = eltid ? doc.getElementById(eltid) : doc.documentElement; 1.288 + return doc.activeElement == elt; 1.289 + }, callback, "element " + eltid + " never got focus"); 1.290 + } 1.291 + 1.292 + let chatbar = SocialChatBar.chatbar; 1.293 + startTestAndWaitForSidebar(function(port) { 1.294 + openChatViaSidebarMessage(port, {id: 1}, function() { 1.295 + let chat1 = chatbar.firstElementChild; 1.296 + openChatViaSidebarMessage(port, {id: 2}, function() { 1.297 + let chat2 = chat1.nextElementSibling || chat1.previousElementSibling; 1.298 + chatbar.selectedChat = chat2; 1.299 + chatbar.focus(); 1.300 + waitForCondition(function() isChatFocused(chatbar.selectedChat), 1.301 + function() { 1.302 + // Our chats have 3 focusable elements, so it takes 4 TABs to move 1.303 + // to the new chat. 1.304 + sendTabAndWaitForFocus(chat2, "input1", function() { 1.305 + is(chat2.contentDocument.activeElement.getAttribute("id"), "input1", 1.306 + "first input field has focus"); 1.307 + ok(isChatFocused(chat2), "new chat still focused after first tab"); 1.308 + sendTabAndWaitForFocus(chat2, "input2", function() { 1.309 + ok(isChatFocused(chat2), "new chat still focused after tab"); 1.310 + is(chat2.contentDocument.activeElement.getAttribute("id"), "input2", 1.311 + "second input field has focus"); 1.312 + sendTabAndWaitForFocus(chat2, "iframe", function() { 1.313 + ok(isChatFocused(chat2), "new chat still focused after tab"); 1.314 + is(chat2.contentDocument.activeElement.getAttribute("id"), "iframe", 1.315 + "iframe has focus"); 1.316 + // this tab now should move to the next chat, but focus the 1.317 + // document element itself (hence the null eltid) 1.318 + sendTabAndWaitForFocus(chat1, null, function() { 1.319 + ok(isChatFocused(chat1), "first chat is focused"); 1.320 + next(); 1.321 + }); 1.322 + }); 1.323 + }); 1.324 + }); 1.325 + }, "chat should have focus"); 1.326 + }); 1.327 + }); 1.328 + }); 1.329 + }, 1.330 + 1.331 + // Open a chat and focus an element other than the first. Move focus to some 1.332 + // other item (the tab itself in this case), then focus the chatbar - the 1.333 + // same element that was previously focused should still have focus. 1.334 + testFocusedElement: function(next) { 1.335 + let chatbar = SocialChatBar.chatbar; 1.336 + startTestAndWaitForSidebar(function(port) { 1.337 + openChatViaUser(); 1.338 + let chat = chatbar.firstElementChild; 1.339 + // need to wait for the content to load before we can focus it. 1.340 + chat.addEventListener("DOMContentLoaded", function DOMContentLoaded() { 1.341 + chat.removeEventListener("DOMContentLoaded", DOMContentLoaded); 1.342 + chat.contentDocument.getElementById("input2").focus(); 1.343 + waitForCondition(function() isChatFocused(chat), 1.344 + function() { 1.345 + is(chat.contentDocument.activeElement.getAttribute("id"), "input2", 1.346 + "correct input field has focus"); 1.347 + // set focus to the tab. 1.348 + let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab); 1.349 + Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0); 1.350 + waitForCondition(function() isTabFocused(), 1.351 + function() { 1.352 + chatbar.focus(); 1.353 + waitForCondition(function() isChatFocused(chat), 1.354 + function() { 1.355 + is(chat.contentDocument.activeElement.getAttribute("id"), "input2", 1.356 + "correct input field still has focus"); 1.357 + next(); 1.358 + }, "chat took focus"); 1.359 + }, "tab has focus"); 1.360 + }, "chat took focus"); 1.361 + }); 1.362 + }); 1.363 + }, 1.364 +};