diff -r 000000000000 -r 6474c204b198 browser/base/content/test/social/browser_social_chatwindowfocus.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/browser/base/content/test/social/browser_social_chatwindowfocus.js Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,361 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Is the currently opened tab focused? +function isTabFocused() { + let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab); + return Services.focus.focusedWindow == tabb.contentWindow; +} + +function isChatFocused(chat) { + return SocialChatBar.chatbar._isChatFocused(chat); +} + +function openChatViaUser() { + let sidebarDoc = document.getElementById("social-sidebar-browser").contentDocument; + let button = sidebarDoc.getElementById("chat-opener"); + // Note we must use synthesizeMouseAtCenter() rather than calling + // .click() directly as this causes nsIDOMWindowUtils.isHandlingUserInput + // to be true. + EventUtils.synthesizeMouseAtCenter(button, {}, sidebarDoc.defaultView); +} + +function openChatViaSidebarMessage(port, data, callback) { + port.onmessage = function (e) { + if (e.data.topic == "chatbox-opened") + callback(); + } + port.postMessage({topic: "test-chatbox-open", data: data}); +} + +function openChatViaWorkerMessage(port, data, callback) { + // sadly there is no message coming back to tell us when the chat has + // been opened, so we wait until one appears. + let chatbar = SocialChatBar.chatbar; + let numExpected = chatbar.childElementCount + 1; + port.postMessage({topic: "test-worker-chat", data: data}); + waitForCondition(function() chatbar.childElementCount == numExpected, + function() { + // so the child has been added, but we don't know if it + // has been intialized - re-request it and the callback + // means it's done. Minimized, same as the worker. + SocialChatBar.openChat(SocialSidebar.provider, + data, + function() { + callback(); + }, + "minimized"); + }, + "No new chat appeared"); +} + + +let isSidebarLoaded = false; + +function startTestAndWaitForSidebar(callback) { + let doneCallback; + let port = SocialSidebar.provider.getWorkerPort(); + function maybeCallback() { + if (!doneCallback) + callback(port); + doneCallback = true; + } + port.onmessage = function(e) { + let topic = e.data.topic; + switch (topic) { + case "got-sidebar-message": + // if sidebar loaded too fast, we need a backup ping + case "got-isVisible-response": + isSidebarLoaded = true; + maybeCallback(); + break; + case "test-init-done": + if (isSidebarLoaded) + maybeCallback(); + else + port.postMessage({topic: "test-isVisible"}); + break; + } + } + port.postMessage({topic: "test-init"}); +} + +let manifest = { // normal provider + name: "provider 1", + origin: "https://example.com", + sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html", + workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js", + iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png" +}; + +function test() { + waitForExplicitFinish(); + + // Note that (probably) due to bug 604289, if a tab is focused but the + // focused element is null, our chat windows can "steal" focus. This is + // avoided if we explicitly focus an element in the tab. + // So we load a page with an field and focus that before testing. + let url = "data:text/html;charset=utf-8," + encodeURI(''); + let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true}); + tab.linkedBrowser.addEventListener("load", function tabLoad(event) { + tab.linkedBrowser.removeEventListener("load", tabLoad, true); + // before every test we focus the input field. + let preSubTest = function(cb) { + // XXX - when bug 604289 is fixed it should be possible to just do: + // tab.linkedBrowser.contentWindow.focus() + // but instead we must do: + tab.linkedBrowser.contentDocument.getElementById("theinput").focus(); + waitForCondition(function() isTabFocused(), cb, "tab should have focus"); + } + let postSubTest = function(cb) { + window.SocialChatBar.chatbar.removeAll(); + cb(); + } + // and run the tests. + runSocialTestWithProvider(manifest, function (finishcb) { + SocialSidebar.show(); + runSocialTests(tests, preSubTest, postSubTest, function () { + finishcb(); + }); + }); + }, true); + registerCleanupFunction(function() { + gBrowser.removeTab(tab); + }); + +} + +var tests = { + // In this test the worker asks the sidebar to open a chat. As that means + // we aren't handling user-input we will not focus the chatbar. + // Then we do it again - should still not be focused. + // Then we perform a user-initiated request - it should get focus. + testNoFocusWhenViaWorker: function(next) { + startTestAndWaitForSidebar(function(port) { + openChatViaSidebarMessage(port, {stealFocus: 1}, function() { + ok(true, "got chatbox message"); + is(SocialChatBar.chatbar.childElementCount, 1, "exactly 1 chat open"); + ok(isTabFocused(), "tab should still be focused"); + // re-request the same chat via a message. + openChatViaSidebarMessage(port, {stealFocus: 1}, function() { + is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open"); + ok(isTabFocused(), "tab should still be focused"); + // re-request the same chat via user event. + openChatViaUser(); + waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat), + function() { + is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open"); + is(SocialChatBar.chatbar.selectedChat, SocialChatBar.chatbar.firstElementChild, "chat should be selected"); + next(); + }, "chat should be focused"); + }); + }); + }); + }, + + // In this test we arrange for the sidebar to open the chat via a simulated + // click. This should cause the new chat to be opened and focused. + testFocusWhenViaUser: function(next) { + startTestAndWaitForSidebar(function(port) { + openChatViaUser(); + ok(SocialChatBar.chatbar.firstElementChild, "chat opened"); + waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat), + function() { + is(SocialChatBar.chatbar.selectedChat, SocialChatBar.chatbar.firstElementChild, "chat is selected"); + next(); + }, "chat should be focused"); + }); + }, + + // Open a chat via the worker - it will open and not have focus. + // Then open the same chat via a sidebar message - it will be restored but + // should still not have grabbed focus. + testNoFocusOnAutoRestore: function(next) { + const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html?id=1"; + let chatbar = SocialChatBar.chatbar; + startTestAndWaitForSidebar(function(port) { + openChatViaWorkerMessage(port, chatUrl, function() { + is(chatbar.childElementCount, 1, "exactly 1 chat open"); + // bug 865086 opening minimized still sets the window as selected + todo(chatbar.selectedChat != chatbar.firstElementChild, "chat is not selected"); + ok(isTabFocused(), "tab should be focused"); + openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() { + is(chatbar.childElementCount, 1, "still 1 chat open"); + ok(!chatbar.firstElementChild.minimized, "chat no longer minimized"); + // bug 865086 because we marked it selected on open, it still is + todo(chatbar.selectedChat != chatbar.firstElementChild, "chat is not selected"); + ok(isTabFocused(), "tab should still be focused"); + next(); + }); + }); + }); + }, + + // Here we open a chat, which will not be focused. Then we minimize it and + // restore it via a titlebar clock - it should get focus at that point. + testFocusOnExplicitRestore: function(next) { + startTestAndWaitForSidebar(function(port) { + openChatViaSidebarMessage(port, {stealFocus: 1}, function() { + ok(true, "got chatbox message"); + ok(isTabFocused(), "tab should still be focused"); + let chatbox = SocialChatBar.chatbar.firstElementChild; + ok(chatbox, "chat opened"); + chatbox.minimized = true; + ok(isTabFocused(), "tab should still be focused"); + // pretend we clicked on the titlebar + chatbox.onTitlebarClick({button: 0}); + waitForCondition(function() isChatFocused(SocialChatBar.chatbar.selectedChat), + function() { + ok(!chatbox.minimized, "chat should have been restored"); + ok(isChatFocused(chatbox), "chat should be focused"); + is(chatbox, SocialChatBar.chatbar.selectedChat, "chat is marked selected"); + next(); + }, "chat should have focus"); + }); + }); + }, + + // Open 2 chats and give 1 focus. Minimize the focused one - the second + // should get focus. + testMinimizeFocused: function(next) { + let chatbar = SocialChatBar.chatbar; + startTestAndWaitForSidebar(function(port) { + openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() { + let chat1 = chatbar.firstElementChild; + openChatViaSidebarMessage(port, {stealFocus: 1, id: 2}, function() { + is(chatbar.childElementCount, 2, "exactly 2 chats open"); + let chat2 = chat1.nextElementSibling || chat1.previousElementSibling; + chatbar.selectedChat = chat1; + chatbar.focus(); + waitForCondition(function() isChatFocused(chat1), + function() { + is(chat1, SocialChatBar.chatbar.selectedChat, "chat1 is marked selected"); + isnot(chat2, SocialChatBar.chatbar.selectedChat, "chat2 is not marked selected"); + chat1.minimized = true; + waitForCondition(function() isChatFocused(chat2), + function() { + // minimizing the chat with focus should give it to another. + isnot(chat1, SocialChatBar.chatbar.selectedChat, "chat1 is not marked selected"); + is(chat2, SocialChatBar.chatbar.selectedChat, "chat2 is marked selected"); + next(); + }, "chat2 should have focus"); + }, "chat1 should have focus"); + }); + }); + }); + }, + + // Open 2 chats, select (but not focus) one, then re-request it be + // opened via a message. Focus should not move. + testReopenNonFocused: function(next) { + let chatbar = SocialChatBar.chatbar; + startTestAndWaitForSidebar(function(port) { + openChatViaSidebarMessage(port, {id: 1}, function() { + let chat1 = chatbar.firstElementChild; + openChatViaSidebarMessage(port, {id: 2}, function() { + let chat2 = chat1.nextElementSibling || chat1.previousElementSibling; + chatbar.selectedChat = chat2; + // tab still has focus + ok(isTabFocused(), "tab should still be focused"); + // re-request the first. + openChatViaSidebarMessage(port, {id: 1}, function() { + is(chatbar.selectedChat, chat1, "chat1 now selected"); + ok(isTabFocused(), "tab should still be focused"); + next(); + }); + }); + }); + }); + }, + + // Open 2 chats, select and focus the second. Pressing the TAB key should + // cause focus to move between all elements in our chat window before moving + // to the next chat window. + testTab: function(next) { + function sendTabAndWaitForFocus(chat, eltid, callback) { + // ideally we would use the 'focus' event here, but that doesn't work + // as expected for the iframe - the iframe itself never gets the focus + // event (apparently the sub-document etc does.) + // So just poll for the correct element getting focus... + let doc = chat.contentDocument; + EventUtils.sendKey("tab"); + waitForCondition(function() { + let elt = eltid ? doc.getElementById(eltid) : doc.documentElement; + return doc.activeElement == elt; + }, callback, "element " + eltid + " never got focus"); + } + + let chatbar = SocialChatBar.chatbar; + startTestAndWaitForSidebar(function(port) { + openChatViaSidebarMessage(port, {id: 1}, function() { + let chat1 = chatbar.firstElementChild; + openChatViaSidebarMessage(port, {id: 2}, function() { + let chat2 = chat1.nextElementSibling || chat1.previousElementSibling; + chatbar.selectedChat = chat2; + chatbar.focus(); + waitForCondition(function() isChatFocused(chatbar.selectedChat), + function() { + // Our chats have 3 focusable elements, so it takes 4 TABs to move + // to the new chat. + sendTabAndWaitForFocus(chat2, "input1", function() { + is(chat2.contentDocument.activeElement.getAttribute("id"), "input1", + "first input field has focus"); + ok(isChatFocused(chat2), "new chat still focused after first tab"); + sendTabAndWaitForFocus(chat2, "input2", function() { + ok(isChatFocused(chat2), "new chat still focused after tab"); + is(chat2.contentDocument.activeElement.getAttribute("id"), "input2", + "second input field has focus"); + sendTabAndWaitForFocus(chat2, "iframe", function() { + ok(isChatFocused(chat2), "new chat still focused after tab"); + is(chat2.contentDocument.activeElement.getAttribute("id"), "iframe", + "iframe has focus"); + // this tab now should move to the next chat, but focus the + // document element itself (hence the null eltid) + sendTabAndWaitForFocus(chat1, null, function() { + ok(isChatFocused(chat1), "first chat is focused"); + next(); + }); + }); + }); + }); + }, "chat should have focus"); + }); + }); + }); + }, + + // Open a chat and focus an element other than the first. Move focus to some + // other item (the tab itself in this case), then focus the chatbar - the + // same element that was previously focused should still have focus. + testFocusedElement: function(next) { + let chatbar = SocialChatBar.chatbar; + startTestAndWaitForSidebar(function(port) { + openChatViaUser(); + let chat = chatbar.firstElementChild; + // need to wait for the content to load before we can focus it. + chat.addEventListener("DOMContentLoaded", function DOMContentLoaded() { + chat.removeEventListener("DOMContentLoaded", DOMContentLoaded); + chat.contentDocument.getElementById("input2").focus(); + waitForCondition(function() isChatFocused(chat), + function() { + is(chat.contentDocument.activeElement.getAttribute("id"), "input2", + "correct input field has focus"); + // set focus to the tab. + let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab); + Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0); + waitForCondition(function() isTabFocused(), + function() { + chatbar.focus(); + waitForCondition(function() isChatFocused(chat), + function() { + is(chat.contentDocument.activeElement.getAttribute("id"), "input2", + "correct input field still has focus"); + next(); + }, "chat took focus"); + }, "tab has focus"); + }, "chat took focus"); + }); + }); + }, +};