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