--- a/browser/base/content/test/chat/browser_chatwindow.js
+++ b/browser/base/content/test/chat/browser_chatwindow.js
@@ -1,135 +1,135 @@
-/* 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/. */
-
-let chatbar = document.getElementById("pinnedchats");
-
-add_chat_task(function* testOpenCloseChat() {
- let chatbox = yield promiseOpenChat("http://example.com");
- Assert.strictEqual(chatbox, chatbar.selectedChat);
- // we requested a "normal" chat, so shouldn't be minimized
- Assert.ok(!chatbox.minimized, "chat is not minimized");
- Assert.equal(chatbar.childNodes.length, 1, "should be 1 chat open");
-
-
- // now request the same URL again - we should get the same chat.
- let chatbox2 = yield promiseOpenChat("http://example.com");
- Assert.strictEqual(chatbox2, chatbox, "got the same chat");
- Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
-
- chatbox.toggle();
- is(chatbox.minimized, true, "chat is now minimized");
- // was no other chat to select, so selected becomes null.
- is(chatbar.selectedChat, null);
-
- // We check the content gets an unload event as we close it.
- let promiseClosed = promiseOneEvent(chatbox.content, "unload", true);
- chatbox.close();
- yield promiseClosed;
-});
-
-// In this case we open a chat minimized, then request the same chat again
-// without specifying minimized. On that second call the chat should open,
-// selected, and no longer minimized.
-add_chat_task(function* testMinimized() {
- let chatbox = yield promiseOpenChat("http://example.com", "minimized");
- Assert.strictEqual(chatbox, chatbar.selectedChat);
- Assert.ok(chatbox.minimized, "chat is minimized");
- Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
- yield promiseOpenChat("http://example.com");
- Assert.ok(!chatbox.minimized, false, "chat is no longer minimized");
-});
-
-// open enough chats to overflow the window, then check
-// if the menupopup is visible
-add_chat_task(function* testManyChats() {
- Assert.ok(chatbar.menupopup.parentNode.collapsed, "popup nub collapsed at start");
- // we should *never* find a test box that needs more than this to cause
- // an overflow!
- let maxToOpen = 20;
- let numOpened = 0;
- for (let i = 0; i < maxToOpen; i++) {
- yield promiseOpenChat("http://example.com#" + i);
- if (!chatbar.menupopup.parentNode.collapsed) {
- info("the menu popup appeared");
- return;
- }
- }
- Assert.ok(false, "We didn't find a collapsed chat after " + maxToOpen + "chats!");
-});
-
-// Check that closeAll works as expected.
-add_chat_task(function* testOpenTwiceCallbacks() {
- yield promiseOpenChat("http://example.com#1");
- yield promiseOpenChat("http://example.com#2");
- yield promiseOpenChat("http://test2.example.com");
- Assert.equal(numChatsInWindow(window), 3, "should be 3 chats open");
- Chat.closeAll("http://example.com");
- Assert.equal(numChatsInWindow(window), 1, "should have closed 2 chats");
- Chat.closeAll("http://test2.example.com");
- Assert.equal(numChatsInWindow(window), 0, "should have closed last chat");
-});
-
-// Check that when we open the same chat twice, the callbacks are called back
-// twice.
-add_chat_task(function* testOpenTwiceCallbacks() {
- yield promiseOpenChatCallback("http://example.com");
- yield promiseOpenChatCallback("http://example.com");
-});
-
-// Bug 817782 - check chats work in new top-level windows.
-add_chat_task(function* testSecondTopLevelWindow() {
- const chatUrl = "http://example.com";
- let secondWindow = OpenBrowserWindow();
- yield promiseOneEvent(secondWindow, "load");
- yield promiseOpenChat(chatUrl);
- // the chat was created - let's make sure it was created in the second window.
- Assert.equal(numChatsInWindow(window), 0, "main window has no chats");
- Assert.equal(numChatsInWindow(secondWindow), 1, "second window has 1 chat");
- secondWindow.close();
-});
-
-// Test that chats are created in the correct window.
-add_chat_task(function* testChatWindowChooser() {
- let chat = yield promiseOpenChat("http://example.com");
- Assert.equal(numChatsInWindow(window), 1, "first window has the chat");
- // create a second window - this will be the "most recent" and will
- // therefore be the window that hosts the new chat (see bug 835111)
- let secondWindow = OpenBrowserWindow();
- yield promiseOneEvent(secondWindow, "load");
- Assert.equal(numChatsInWindow(secondWindow), 0, "second window starts with no chats");
- yield promiseOpenChat("http://example.com#2");
- Assert.equal(numChatsInWindow(secondWindow), 1, "second window now has chats");
- Assert.equal(numChatsInWindow(window), 1, "first window still has 1 chat");
- chat.close();
- Assert.equal(numChatsInWindow(window), 0, "first window now has no chats");
- // now open another chat - it should still open in the second.
- yield promiseOpenChat("http://example.com#3");
- Assert.equal(numChatsInWindow(window), 0, "first window still has no chats");
- Assert.equal(numChatsInWindow(secondWindow), 2, "second window has both chats");
-
- // focus the first window, and open yet another chat - it
- // should open in the first window.
- window.focus();
- yield promiseWaitForFocus();
- chat = yield promiseOpenChat("http://example.com#4");
- Assert.equal(numChatsInWindow(window), 1, "first window got new chat");
- chat.close();
- Assert.equal(numChatsInWindow(window), 0, "first window has no chats");
-
- let privateWindow = OpenBrowserWindow({private: true});
- yield promiseOneEvent(privateWindow, "load")
-
- // open a last chat - the focused window can't accept
- // chats (it's a private window), so the chat should open
- // in the window that was selected before. This is known
- // to be broken on Linux.
- chat = yield promiseOpenChat("http://example.com#5");
- let os = Services.appinfo.OS;
- const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
- let fn = BROKEN_WM_Z_ORDER ? todo : ok;
- fn(numChatsInWindow(window) == 1, "first window got the chat");
- chat.close();
- privateWindow.close();
- secondWindow.close();
-});
+/* 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/. */
+
+let chatbar = document.getElementById("pinnedchats");
+
+add_chat_task(function* testOpenCloseChat() {
+ let chatbox = yield promiseOpenChat("http://example.com");
+ Assert.strictEqual(chatbox, chatbar.selectedChat);
+ // we requested a "normal" chat, so shouldn't be minimized
+ Assert.ok(!chatbox.minimized, "chat is not minimized");
+ Assert.equal(chatbar.childNodes.length, 1, "should be 1 chat open");
+
+
+ // now request the same URL again - we should get the same chat.
+ let chatbox2 = yield promiseOpenChat("http://example.com");
+ Assert.strictEqual(chatbox2, chatbox, "got the same chat");
+ Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+
+ chatbox.toggle();
+ is(chatbox.minimized, true, "chat is now minimized");
+ // was no other chat to select, so selected becomes null.
+ is(chatbar.selectedChat, null);
+
+ // We check the content gets an unload event as we close it.
+ let promiseClosed = promiseOneEvent(chatbox.content, "unload", true);
+ chatbox.close();
+ yield promiseClosed;
+});
+
+// In this case we open a chat minimized, then request the same chat again
+// without specifying minimized. On that second call the chat should open,
+// selected, and no longer minimized.
+add_chat_task(function* testMinimized() {
+ let chatbox = yield promiseOpenChat("http://example.com", "minimized");
+ Assert.strictEqual(chatbox, chatbar.selectedChat);
+ Assert.ok(chatbox.minimized, "chat is minimized");
+ Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+ yield promiseOpenChat("http://example.com");
+ Assert.ok(!chatbox.minimized, false, "chat is no longer minimized");
+});
+
+// open enough chats to overflow the window, then check
+// if the menupopup is visible
+add_chat_task(function* testManyChats() {
+ Assert.ok(chatbar.menupopup.parentNode.collapsed, "popup nub collapsed at start");
+ // we should *never* find a test box that needs more than this to cause
+ // an overflow!
+ let maxToOpen = 20;
+ let numOpened = 0;
+ for (let i = 0; i < maxToOpen; i++) {
+ yield promiseOpenChat("http://example.com#" + i);
+ if (!chatbar.menupopup.parentNode.collapsed) {
+ info("the menu popup appeared");
+ return;
+ }
+ }
+ Assert.ok(false, "We didn't find a collapsed chat after " + maxToOpen + "chats!");
+});
+
+// Check that closeAll works as expected.
+add_chat_task(function* testOpenTwiceCallbacks() {
+ yield promiseOpenChat("http://example.com#1");
+ yield promiseOpenChat("http://example.com#2");
+ yield promiseOpenChat("http://test2.example.com");
+ Assert.equal(numChatsInWindow(window), 3, "should be 3 chats open");
+ Chat.closeAll("http://example.com");
+ Assert.equal(numChatsInWindow(window), 1, "should have closed 2 chats");
+ Chat.closeAll("http://test2.example.com");
+ Assert.equal(numChatsInWindow(window), 0, "should have closed last chat");
+});
+
+// Check that when we open the same chat twice, the callbacks are called back
+// twice.
+add_chat_task(function* testOpenTwiceCallbacks() {
+ yield promiseOpenChatCallback("http://example.com");
+ yield promiseOpenChatCallback("http://example.com");
+});
+
+// Bug 817782 - check chats work in new top-level windows.
+add_chat_task(function* testSecondTopLevelWindow() {
+ const chatUrl = "http://example.com";
+ let secondWindow = OpenBrowserWindow();
+ yield promiseOneEvent(secondWindow, "load");
+ yield promiseOpenChat(chatUrl);
+ // the chat was created - let's make sure it was created in the second window.
+ Assert.equal(numChatsInWindow(window), 0, "main window has no chats");
+ Assert.equal(numChatsInWindow(secondWindow), 1, "second window has 1 chat");
+ secondWindow.close();
+});
+
+// Test that chats are created in the correct window.
+add_chat_task(function* testChatWindowChooser() {
+ let chat = yield promiseOpenChat("http://example.com");
+ Assert.equal(numChatsInWindow(window), 1, "first window has the chat");
+ // create a second window - this will be the "most recent" and will
+ // therefore be the window that hosts the new chat (see bug 835111)
+ let secondWindow = OpenBrowserWindow();
+ yield promiseOneEvent(secondWindow, "load");
+ Assert.equal(numChatsInWindow(secondWindow), 0, "second window starts with no chats");
+ yield promiseOpenChat("http://example.com#2");
+ Assert.equal(numChatsInWindow(secondWindow), 1, "second window now has chats");
+ Assert.equal(numChatsInWindow(window), 1, "first window still has 1 chat");
+ chat.close();
+ Assert.equal(numChatsInWindow(window), 0, "first window now has no chats");
+ // now open another chat - it should still open in the second.
+ yield promiseOpenChat("http://example.com#3");
+ Assert.equal(numChatsInWindow(window), 0, "first window still has no chats");
+ Assert.equal(numChatsInWindow(secondWindow), 2, "second window has both chats");
+
+ // focus the first window, and open yet another chat - it
+ // should open in the first window.
+ window.focus();
+ yield promiseWaitForFocus();
+ chat = yield promiseOpenChat("http://example.com#4");
+ Assert.equal(numChatsInWindow(window), 1, "first window got new chat");
+ chat.close();
+ Assert.equal(numChatsInWindow(window), 0, "first window has no chats");
+
+ let privateWindow = OpenBrowserWindow({private: true});
+ yield promiseOneEvent(privateWindow, "load")
+
+ // open a last chat - the focused window can't accept
+ // chats (it's a private window), so the chat should open
+ // in the window that was selected before. This is known
+ // to be broken on Linux.
+ chat = yield promiseOpenChat("http://example.com#5");
+ let os = Services.appinfo.OS;
+ const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
+ let fn = BROKEN_WM_Z_ORDER ? todo : ok;
+ fn(numChatsInWindow(window) == 1, "first window got the chat");
+ chat.close();
+ privateWindow.close();
+ secondWindow.close();
+});
--- a/browser/base/content/test/chat/browser_focus.js
+++ b/browser/base/content/test/chat/browser_focus.js
@@ -1,230 +1,230 @@
-/* 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/. */
-
-// Tests the focus functionality.
-
-const CHAT_URL = "https://example.com/browser/browser/base/content/test/chat/chat.html";
-
-// Is the currently opened tab focused?
-function isTabFocused() {
- let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
- return Services.focus.focusedWindow == tabb.contentWindow;
-}
-
-// Is the specified chat focused?
-function isChatFocused(chat) {
- return chat.chatbar._isChatFocused(chat);
-}
-
-let chatbar = document.getElementById("pinnedchats");
-
-function* setUp() {
- // 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 <input> field and focus that before testing.
- let html = '<input id="theinput"><button id="chat-opener"></button>';
- let url = "data:text/html;charset=utf-8," + encodeURI(html);
- let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true});
- yield promiseOneEvent(tab.linkedBrowser, "load", true);
- tab.linkedBrowser.contentDocument.getElementById("theinput").focus();
- registerCleanupFunction(function() {
- gBrowser.removeTab(tab);
- });
-}
-
-// Test default focus - not user input.
-add_chat_task(function* testDefaultFocus() {
- yield setUp();
- let chat = yield promiseOpenChat("http://example.com");
- // we used the default focus behaviour, which means that because this was
- // not the direct result of user action the chat should not be focused.
- Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
- Assert.ok(isTabFocused(), "the tab should remain focused.");
- Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
-});
-
-// Test default focus via user input.
-add_chat_task(function* testDefaultFocus() {
- yield setUp();
- let tab = gBrowser.selectedTab;
- let deferred = Promise.defer();
- let button = tab.linkedBrowser.contentDocument.getElementById("chat-opener");
- button.addEventListener("click", function onclick() {
- button.removeEventListener("click", onclick);
- promiseOpenChat("http://example.com").then(
- chat => deferred.resolve(chat)
- );
- })
- // Note we must use synthesizeMouseAtCenter() rather than calling
- // .click() directly as this causes nsIDOMWindowUtils.isHandlingUserInput
- // to be true.
- EventUtils.synthesizeMouseAtCenter(button, {}, button.ownerDocument.defaultView);
- let chat = yield deferred.promise;
-
- // we use the default focus behaviour but the chat was opened via user input,
- // so the chat should be focused.
- Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
- Assert.ok(!isTabFocused(), "the tab should have lost focus.");
- Assert.ok(isChatFocused(chat), "the chat should have got focus.");
-});
-
-// We explicitly ask for the chat to be focused.
-add_chat_task(function* testExplicitFocus() {
- yield setUp();
- let chat = yield promiseOpenChat("http://example.com", undefined, true);
- // we use the default focus behaviour, which means that because this was
- // not the direct result of user action the chat should not be focused.
- Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
- Assert.ok(!isTabFocused(), "the tab should have lost focus.");
- Assert.ok(isChatFocused(chat), "the chat should have got focus.");
-});
-
-// Open a minimized chat via default focus behaviour - it will open and not
-// have focus. Then open the same chat without 'minimized' - it will be
-// restored but should still not have grabbed focus.
-add_chat_task(function* testNoFocusOnAutoRestore() {
- yield setUp();
- let chat = yield promiseOpenChat("http://example.com", "minimized");
- Assert.ok(chat.minimized, "chat is minimized");
- Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
- Assert.ok(isTabFocused(), "the tab should remain focused.");
- Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
- yield promiseOpenChat("http://example.com");
- Assert.ok(!chat.minimized, "chat should be restored");
- Assert.ok(isTabFocused(), "the tab should remain focused.");
- Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
-});
-
-// 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.
-add_chat_task(function* testFocusOnExplicitRestore() {
- yield setUp();
- let chat = yield promiseOpenChat("http://example.com");
- Assert.ok(!chat.minimized, "chat should have been opened restored");
- Assert.ok(isTabFocused(), "the tab should remain focused.");
- Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
- chat.minimized = true;
- Assert.ok(isTabFocused(), "tab should still be focused");
- Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
-
- let promise = promiseOneEvent(chat.contentWindow, "focus");
- // pretend we clicked on the titlebar
- chat.onTitlebarClick({button: 0});
- yield promise; // wait for focus event.
- Assert.ok(!chat.minimized, "chat should have been restored");
- Assert.ok(isChatFocused(chat), "chat should be focused");
- Assert.strictEqual(chat, chatbar.selectedChat, "chat is marked selected");
-});
-
-// Open 2 chats and give 1 focus. Minimize the focused one - the second
-// should get focus.
-add_chat_task(function* testMinimizeFocused() {
- yield setUp();
- let chat1 = yield promiseOpenChat("http://example.com#1");
- let chat2 = yield promiseOpenChat("http://example.com#2");
- Assert.equal(numChatsInWindow(window), 2, "2 chats open");
- Assert.strictEqual(chatbar.selectedChat, chat2, "chat2 is selected");
- let promise = promiseOneEvent(chat1.contentWindow, "focus");
- chatbar.selectedChat = chat1;
- chatbar.focus();
- yield promise; // wait for chat1 to get focus.
- Assert.strictEqual(chat1, chatbar.selectedChat, "chat1 is marked selected");
- Assert.notStrictEqual(chat2, chatbar.selectedChat, "chat2 is not marked selected");
- promise = promiseOneEvent(chat2.contentWindow, "focus");
- chat1.minimized = true;
- yield promise; // wait for chat2 to get focus.
- Assert.notStrictEqual(chat1, chatbar.selectedChat, "chat1 is not marked selected");
- Assert.strictEqual(chat2, chatbar.selectedChat, "chat2 is marked selected");
-});
-
-// 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.
-add_chat_task(function* testTab() {
- yield setUp();
-
- function sendTabAndWaitForFocus(chat, eltid) {
- let doc = chat.contentDocument;
- EventUtils.sendKey("tab");
- // 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 deferred = Promise.defer();
- let tries = 0;
- let interval = setInterval(function() {
- if (tries >= 30) {
- clearInterval(interval);
- deferred.reject("never got focus");
- return;
- }
- tries ++;
- let elt = eltid ? doc.getElementById(eltid) : doc.documentElement;
- if (doc.activeElement == elt) {
- clearInterval(interval);
- deferred.resolve();
- }
- info("retrying wait for focus: " + tries);
- info("(the active element is " + doc.activeElement + "/" + doc.activeElement.getAttribute("id") + ")");
- }, 100);
- info("waiting for element " + eltid + " to get focus");
- return deferred.promise;
- }
-
- let chat1 = yield promiseOpenChat(CHAT_URL + "#1");
- let chat2 = yield promiseOpenChat(CHAT_URL + "#2");
- chatbar.selectedChat = chat2;
- let promise = promiseOneEvent(chat2.contentWindow, "focus");
- chatbar.focus();
- info("waiting for second chat to get focus");
- yield promise;
-
- // Our chats have 3 focusable elements, so it takes 4 TABs to move
- // to the new chat.
- yield sendTabAndWaitForFocus(chat2, "input1");
- Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "input1",
- "first input field has focus");
- Assert.ok(isChatFocused(chat2), "new chat still focused after first tab");
-
- yield sendTabAndWaitForFocus(chat2, "input2");
- Assert.ok(isChatFocused(chat2), "new chat still focused after tab");
- Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "input2",
- "second input field has focus");
-
- yield sendTabAndWaitForFocus(chat2, "iframe");
- Assert.ok(isChatFocused(chat2), "new chat still focused after tab");
- Assert.equal(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)
- yield sendTabAndWaitForFocus(chat1, null);
- Assert.ok(isChatFocused(chat1), "first chat is focused");
-});
-
-// 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.
-add_chat_task(function* testFocusedElement() {
- yield setUp();
-
- // open a chat with focus requested.
- let chat = yield promiseOpenChat(CHAT_URL, undefined, true);
-
- chat.contentDocument.getElementById("input2").focus();
-
- // set focus to the tab.
- let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
- let promise = promiseOneEvent(tabb.contentWindow, "focus");
- Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0);
- yield promise;
-
- promise = promiseOneEvent(chat.contentWindow, "focus");
- chatbar.focus();
- yield promise;
-
- Assert.equal(chat.contentDocument.activeElement.getAttribute("id"), "input2",
- "correct input field still has focus");
-});
+/* 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/. */
+
+// Tests the focus functionality.
+
+const CHAT_URL = "https://example.com/browser/browser/base/content/test/chat/chat.html";
+
+// Is the currently opened tab focused?
+function isTabFocused() {
+ let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
+ return Services.focus.focusedWindow == tabb.contentWindow;
+}
+
+// Is the specified chat focused?
+function isChatFocused(chat) {
+ return chat.chatbar._isChatFocused(chat);
+}
+
+let chatbar = document.getElementById("pinnedchats");
+
+function* setUp() {
+ // 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 <input> field and focus that before testing.
+ let html = '<input id="theinput"><button id="chat-opener"></button>';
+ let url = "data:text/html;charset=utf-8," + encodeURI(html);
+ let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true});
+ yield promiseOneEvent(tab.linkedBrowser, "load", true);
+ tab.linkedBrowser.contentDocument.getElementById("theinput").focus();
+ registerCleanupFunction(function() {
+ gBrowser.removeTab(tab);
+ });
+}
+
+// Test default focus - not user input.
+add_chat_task(function* testDefaultFocus() {
+ yield setUp();
+ let chat = yield promiseOpenChat("http://example.com");
+ // we used the default focus behaviour, which means that because this was
+ // not the direct result of user action the chat should not be focused.
+ Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+ Assert.ok(isTabFocused(), "the tab should remain focused.");
+ Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
+});
+
+// Test default focus via user input.
+add_chat_task(function* testDefaultFocus() {
+ yield setUp();
+ let tab = gBrowser.selectedTab;
+ let deferred = Promise.defer();
+ let button = tab.linkedBrowser.contentDocument.getElementById("chat-opener");
+ button.addEventListener("click", function onclick() {
+ button.removeEventListener("click", onclick);
+ promiseOpenChat("http://example.com").then(
+ chat => deferred.resolve(chat)
+ );
+ })
+ // Note we must use synthesizeMouseAtCenter() rather than calling
+ // .click() directly as this causes nsIDOMWindowUtils.isHandlingUserInput
+ // to be true.
+ EventUtils.synthesizeMouseAtCenter(button, {}, button.ownerDocument.defaultView);
+ let chat = yield deferred.promise;
+
+ // we use the default focus behaviour but the chat was opened via user input,
+ // so the chat should be focused.
+ Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+ Assert.ok(!isTabFocused(), "the tab should have lost focus.");
+ Assert.ok(isChatFocused(chat), "the chat should have got focus.");
+});
+
+// We explicitly ask for the chat to be focused.
+add_chat_task(function* testExplicitFocus() {
+ yield setUp();
+ let chat = yield promiseOpenChat("http://example.com", undefined, true);
+ // we use the default focus behaviour, which means that because this was
+ // not the direct result of user action the chat should not be focused.
+ Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+ Assert.ok(!isTabFocused(), "the tab should have lost focus.");
+ Assert.ok(isChatFocused(chat), "the chat should have got focus.");
+});
+
+// Open a minimized chat via default focus behaviour - it will open and not
+// have focus. Then open the same chat without 'minimized' - it will be
+// restored but should still not have grabbed focus.
+add_chat_task(function* testNoFocusOnAutoRestore() {
+ yield setUp();
+ let chat = yield promiseOpenChat("http://example.com", "minimized");
+ Assert.ok(chat.minimized, "chat is minimized");
+ Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+ Assert.ok(isTabFocused(), "the tab should remain focused.");
+ Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
+ yield promiseOpenChat("http://example.com");
+ Assert.ok(!chat.minimized, "chat should be restored");
+ Assert.ok(isTabFocused(), "the tab should remain focused.");
+ Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
+});
+
+// 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.
+add_chat_task(function* testFocusOnExplicitRestore() {
+ yield setUp();
+ let chat = yield promiseOpenChat("http://example.com");
+ Assert.ok(!chat.minimized, "chat should have been opened restored");
+ Assert.ok(isTabFocused(), "the tab should remain focused.");
+ Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
+ chat.minimized = true;
+ Assert.ok(isTabFocused(), "tab should still be focused");
+ Assert.ok(!isChatFocused(chat), "the chat should not be focused.");
+
+ let promise = promiseOneEvent(chat.contentWindow, "focus");
+ // pretend we clicked on the titlebar
+ chat.onTitlebarClick({button: 0});
+ yield promise; // wait for focus event.
+ Assert.ok(!chat.minimized, "chat should have been restored");
+ Assert.ok(isChatFocused(chat), "chat should be focused");
+ Assert.strictEqual(chat, chatbar.selectedChat, "chat is marked selected");
+});
+
+// Open 2 chats and give 1 focus. Minimize the focused one - the second
+// should get focus.
+add_chat_task(function* testMinimizeFocused() {
+ yield setUp();
+ let chat1 = yield promiseOpenChat("http://example.com#1");
+ let chat2 = yield promiseOpenChat("http://example.com#2");
+ Assert.equal(numChatsInWindow(window), 2, "2 chats open");
+ Assert.strictEqual(chatbar.selectedChat, chat2, "chat2 is selected");
+ let promise = promiseOneEvent(chat1.contentWindow, "focus");
+ chatbar.selectedChat = chat1;
+ chatbar.focus();
+ yield promise; // wait for chat1 to get focus.
+ Assert.strictEqual(chat1, chatbar.selectedChat, "chat1 is marked selected");
+ Assert.notStrictEqual(chat2, chatbar.selectedChat, "chat2 is not marked selected");
+ promise = promiseOneEvent(chat2.contentWindow, "focus");
+ chat1.minimized = true;
+ yield promise; // wait for chat2 to get focus.
+ Assert.notStrictEqual(chat1, chatbar.selectedChat, "chat1 is not marked selected");
+ Assert.strictEqual(chat2, chatbar.selectedChat, "chat2 is marked selected");
+});
+
+// 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.
+add_chat_task(function* testTab() {
+ yield setUp();
+
+ function sendTabAndWaitForFocus(chat, eltid) {
+ let doc = chat.contentDocument;
+ EventUtils.sendKey("tab");
+ // 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 deferred = Promise.defer();
+ let tries = 0;
+ let interval = setInterval(function() {
+ if (tries >= 30) {
+ clearInterval(interval);
+ deferred.reject("never got focus");
+ return;
+ }
+ tries ++;
+ let elt = eltid ? doc.getElementById(eltid) : doc.documentElement;
+ if (doc.activeElement == elt) {
+ clearInterval(interval);
+ deferred.resolve();
+ }
+ info("retrying wait for focus: " + tries);
+ info("(the active element is " + doc.activeElement + "/" + doc.activeElement.getAttribute("id") + ")");
+ }, 100);
+ info("waiting for element " + eltid + " to get focus");
+ return deferred.promise;
+ }
+
+ let chat1 = yield promiseOpenChat(CHAT_URL + "#1");
+ let chat2 = yield promiseOpenChat(CHAT_URL + "#2");
+ chatbar.selectedChat = chat2;
+ let promise = promiseOneEvent(chat2.contentWindow, "focus");
+ chatbar.focus();
+ info("waiting for second chat to get focus");
+ yield promise;
+
+ // Our chats have 3 focusable elements, so it takes 4 TABs to move
+ // to the new chat.
+ yield sendTabAndWaitForFocus(chat2, "input1");
+ Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "input1",
+ "first input field has focus");
+ Assert.ok(isChatFocused(chat2), "new chat still focused after first tab");
+
+ yield sendTabAndWaitForFocus(chat2, "input2");
+ Assert.ok(isChatFocused(chat2), "new chat still focused after tab");
+ Assert.equal(chat2.contentDocument.activeElement.getAttribute("id"), "input2",
+ "second input field has focus");
+
+ yield sendTabAndWaitForFocus(chat2, "iframe");
+ Assert.ok(isChatFocused(chat2), "new chat still focused after tab");
+ Assert.equal(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)
+ yield sendTabAndWaitForFocus(chat1, null);
+ Assert.ok(isChatFocused(chat1), "first chat is focused");
+});
+
+// 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.
+add_chat_task(function* testFocusedElement() {
+ yield setUp();
+
+ // open a chat with focus requested.
+ let chat = yield promiseOpenChat(CHAT_URL, undefined, true);
+
+ chat.contentDocument.getElementById("input2").focus();
+
+ // set focus to the tab.
+ let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);
+ let promise = promiseOneEvent(tabb.contentWindow, "focus");
+ Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0);
+ yield promise;
+
+ promise = promiseOneEvent(chat.contentWindow, "focus");
+ chatbar.focus();
+ yield promise;
+
+ Assert.equal(chat.contentDocument.activeElement.getAttribute("id"), "input2",
+ "correct input field still has focus");
+});
--- a/browser/base/content/test/chat/browser_tearoff.js
+++ b/browser/base/content/test/chat/browser_tearoff.js
@@ -1,128 +1,128 @@
-/* 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/. */
-
-let chatbar = document.getElementById("pinnedchats");
-
-function promiseNewWindowLoaded() {
- let deferred = Promise.defer();
- Services.wm.addListener({
- onWindowTitleChange: function() {},
- onCloseWindow: function(xulwindow) {},
- onOpenWindow: function(xulwindow) {
- var domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
- .getInterface(Components.interfaces.nsIDOMWindow);
- Services.wm.removeListener(this);
- // wait for load to ensure the window is ready for us to test
- domwindow.addEventListener("load", function _load(event) {
- let doc = domwindow.document;
- if (event.target != doc)
- return;
- domwindow.removeEventListener("load", _load);
- deferred.resolve(domwindow);
- });
- },
- });
- return deferred.promise;
-}
-
-add_chat_task(function* testTearoffChat() {
- let chatbox = yield promiseOpenChat("http://example.com");
- Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
-
- let chatDoc = chatbox.contentDocument;
- let chatTitle = chatDoc.title;
-
- Assert.equal(chatbox.getAttribute("label"), chatTitle,
- "the new chatbox should show the title of the chat window");
-
- // mutate the chat document a bit before we tear it off.
- let div = chatDoc.createElement("div");
- div.setAttribute("id", "testdiv");
- div.setAttribute("test", "1");
- chatDoc.body.appendChild(div);
-
- // chatbox is open, lets detach. The new chat window will be caught in
- // the window watcher below
- let promise = promiseNewWindowLoaded();
-
- let swap = document.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
- swap.click();
-
- // and wait for the new window.
- let domwindow = yield promise;
-
- Assert.equal(domwindow.document.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
- Assert.equal(numChatsInWindow(window), 0, "should be no chats in the chat bar");
-
- // get the chatbox from the new window.
- chatbox = domwindow.document.getElementById("chatter")
- Assert.equal(chatbox.getAttribute("label"), chatTitle, "window should have same title as chat");
-
- div = chatbox.contentDocument.getElementById("testdiv");
- Assert.equal(div.getAttribute("test"), "1", "docshell should have been swapped");
- div.setAttribute("test", "2");
-
- // swap the window back to the chatbar
- promise = promiseOneEvent(domwindow, "unload");
- swap = domwindow.document.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
- swap.click();
-
- yield promise;
-
- Assert.equal(numChatsInWindow(window), 1, "chat should be docked back in the window");
- chatbox = chatbar.selectedChat;
- Assert.equal(chatbox.getAttribute("label"), chatTitle,
- "the new chatbox should show the title of the chat window again");
-
- div = chatbox.contentDocument.getElementById("testdiv");
- Assert.equal(div.getAttribute("test"), "2", "docshell should have been swapped");
-});
-
-// Similar test but with 2 chats.
-add_chat_task(function* testReattachTwice() {
- let chatbox1 = yield promiseOpenChat("http://example.com#1");
- let chatbox2 = yield promiseOpenChat("http://example.com#2");
- Assert.equal(numChatsInWindow(window), 2, "both chats should be docked in the window");
-
- info("chatboxes are open, detach from window");
- let promise = promiseNewWindowLoaded();
- document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
- let domwindow1 = yield promise;
- chatbox1 = domwindow1.document.getElementById("chatter");
- Assert.equal(numChatsInWindow(window), 1, "only second chat should be docked in the window");
-
- promise = promiseNewWindowLoaded();
- document.getAnonymousElementByAttribute(chatbox2, "anonid", "swap").click();
- let domwindow2 = yield promise;
- chatbox2 = domwindow2.document.getElementById("chatter");
- Assert.equal(numChatsInWindow(window), 0, "should be no docked chats");
-
- promise = promiseOneEvent(domwindow2, "unload");
- domwindow2.document.getAnonymousElementByAttribute(chatbox2, "anonid", "swap").click();
- yield promise;
- Assert.equal(numChatsInWindow(window), 1, "one chat should be docked back in the window");
-
- promise = promiseOneEvent(domwindow1, "unload");
- domwindow1.document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
- yield promise;
- Assert.equal(numChatsInWindow(window), 2, "both chats should be docked back in the window");
-});
-
-// Check that Chat.closeAll() also closes detached windows.
-add_chat_task(function* testCloseAll() {
- let chatbox1 = yield promiseOpenChat("http://example.com#1");
- let chatbox2 = yield promiseOpenChat("http://example.com#2");
-
- let promise = promiseNewWindowLoaded();
- document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
- let domwindow = yield promise;
- chatbox1 = domwindow.document.getElementById("chatter");
-
- let promiseWindowUnload = promiseOneEvent(domwindow, "unload");
-
- Assert.equal(numChatsInWindow(window), 1, "second chat should still be docked");
- Chat.closeAll("http://example.com");
- yield promiseWindowUnload;
- Assert.equal(numChatsInWindow(window), 0, "should be no chats left");
-});
+/* 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/. */
+
+let chatbar = document.getElementById("pinnedchats");
+
+function promiseNewWindowLoaded() {
+ let deferred = Promise.defer();
+ Services.wm.addListener({
+ onWindowTitleChange: function() {},
+ onCloseWindow: function(xulwindow) {},
+ onOpenWindow: function(xulwindow) {
+ var domwindow = xulwindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindow);
+ Services.wm.removeListener(this);
+ // wait for load to ensure the window is ready for us to test
+ domwindow.addEventListener("load", function _load(event) {
+ let doc = domwindow.document;
+ if (event.target != doc)
+ return;
+ domwindow.removeEventListener("load", _load);
+ deferred.resolve(domwindow);
+ });
+ },
+ });
+ return deferred.promise;
+}
+
+add_chat_task(function* testTearoffChat() {
+ let chatbox = yield promiseOpenChat("http://example.com");
+ Assert.equal(numChatsInWindow(window), 1, "should be 1 chat open");
+
+ let chatDoc = chatbox.contentDocument;
+ let chatTitle = chatDoc.title;
+
+ Assert.equal(chatbox.getAttribute("label"), chatTitle,
+ "the new chatbox should show the title of the chat window");
+
+ // mutate the chat document a bit before we tear it off.
+ let div = chatDoc.createElement("div");
+ div.setAttribute("id", "testdiv");
+ div.setAttribute("test", "1");
+ chatDoc.body.appendChild(div);
+
+ // chatbox is open, lets detach. The new chat window will be caught in
+ // the window watcher below
+ let promise = promiseNewWindowLoaded();
+
+ let swap = document.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
+ swap.click();
+
+ // and wait for the new window.
+ let domwindow = yield promise;
+
+ Assert.equal(domwindow.document.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
+ Assert.equal(numChatsInWindow(window), 0, "should be no chats in the chat bar");
+
+ // get the chatbox from the new window.
+ chatbox = domwindow.document.getElementById("chatter")
+ Assert.equal(chatbox.getAttribute("label"), chatTitle, "window should have same title as chat");
+
+ div = chatbox.contentDocument.getElementById("testdiv");
+ Assert.equal(div.getAttribute("test"), "1", "docshell should have been swapped");
+ div.setAttribute("test", "2");
+
+ // swap the window back to the chatbar
+ promise = promiseOneEvent(domwindow, "unload");
+ swap = domwindow.document.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
+ swap.click();
+
+ yield promise;
+
+ Assert.equal(numChatsInWindow(window), 1, "chat should be docked back in the window");
+ chatbox = chatbar.selectedChat;
+ Assert.equal(chatbox.getAttribute("label"), chatTitle,
+ "the new chatbox should show the title of the chat window again");
+
+ div = chatbox.contentDocument.getElementById("testdiv");
+ Assert.equal(div.getAttribute("test"), "2", "docshell should have been swapped");
+});
+
+// Similar test but with 2 chats.
+add_chat_task(function* testReattachTwice() {
+ let chatbox1 = yield promiseOpenChat("http://example.com#1");
+ let chatbox2 = yield promiseOpenChat("http://example.com#2");
+ Assert.equal(numChatsInWindow(window), 2, "both chats should be docked in the window");
+
+ info("chatboxes are open, detach from window");
+ let promise = promiseNewWindowLoaded();
+ document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
+ let domwindow1 = yield promise;
+ chatbox1 = domwindow1.document.getElementById("chatter");
+ Assert.equal(numChatsInWindow(window), 1, "only second chat should be docked in the window");
+
+ promise = promiseNewWindowLoaded();
+ document.getAnonymousElementByAttribute(chatbox2, "anonid", "swap").click();
+ let domwindow2 = yield promise;
+ chatbox2 = domwindow2.document.getElementById("chatter");
+ Assert.equal(numChatsInWindow(window), 0, "should be no docked chats");
+
+ promise = promiseOneEvent(domwindow2, "unload");
+ domwindow2.document.getAnonymousElementByAttribute(chatbox2, "anonid", "swap").click();
+ yield promise;
+ Assert.equal(numChatsInWindow(window), 1, "one chat should be docked back in the window");
+
+ promise = promiseOneEvent(domwindow1, "unload");
+ domwindow1.document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
+ yield promise;
+ Assert.equal(numChatsInWindow(window), 2, "both chats should be docked back in the window");
+});
+
+// Check that Chat.closeAll() also closes detached windows.
+add_chat_task(function* testCloseAll() {
+ let chatbox1 = yield promiseOpenChat("http://example.com#1");
+ let chatbox2 = yield promiseOpenChat("http://example.com#2");
+
+ let promise = promiseNewWindowLoaded();
+ document.getAnonymousElementByAttribute(chatbox1, "anonid", "swap").click();
+ let domwindow = yield promise;
+ chatbox1 = domwindow.document.getElementById("chatter");
+
+ let promiseWindowUnload = promiseOneEvent(domwindow, "unload");
+
+ Assert.equal(numChatsInWindow(window), 1, "second chat should still be docked");
+ Chat.closeAll("http://example.com");
+ yield promiseWindowUnload;
+ Assert.equal(numChatsInWindow(window), 0, "should be no chats left");
+});
--- a/browser/base/content/test/chat/head.js
+++ b/browser/base/content/test/chat/head.js
@@ -1,91 +1,91 @@
-/* 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/. */
-
-// Utility functions for Chat tests.
-
-let Chat = Cu.import("resource:///modules/Chat.jsm", {}).Chat;
-
-function promiseOpenChat(url, mode, focus) {
- let uri = Services.io.newURI(url, null, null);
- let origin = uri.prePath;
- let title = origin;
- let deferred = Promise.defer();
- // we just through a few hoops to ensure the content document is fully
- // loaded, otherwise tests that rely on that content may intermittently fail.
- let callback = function(chatbox) {
- if (chatbox.contentDocument.readyState == "complete") {
- // already loaded.
- deferred.resolve(chatbox);
- return;
- }
- chatbox.addEventListener("load", function onload(event) {
- if (event.target != chatbox.contentDocument || chatbox.contentDocument.location.href == "about:blank") {
- return;
- }
- chatbox.removeEventListener("load", onload, true);
- deferred.resolve(chatbox);
- }, true);
- }
- let chatbox = Chat.open(null, origin, title, url, mode, focus, callback);
- return deferred.promise;
-}
-
-// Opens a chat, returns a promise resolved when the chat callback fired.
-function promiseOpenChatCallback(url, mode) {
- let uri = Services.io.newURI(url, null, null);
- let origin = uri.prePath;
- let title = origin;
- let deferred = Promise.defer();
- let callback = deferred.resolve;
- Chat.open(null, origin, title, url, mode, undefined, callback);
- return deferred.promise;
-}
-
-// Opens a chat, returns the chat window's promise which fires when the chat
-// starts loading.
-function promiseOneEvent(target, eventName, capture) {
- let deferred = Promise.defer();
- target.addEventListener(eventName, function handler(event) {
- target.removeEventListener(eventName, handler, capture);
- deferred.resolve();
- }, capture);
- return deferred.promise;
-}
-
-// Return the number of chats in a browser window.
-function numChatsInWindow(win) {
- let chatbar = win.document.getElementById("pinnedchats");
- return chatbar.childElementCount;
-}
-
-function promiseWaitForFocus() {
- let deferred = Promise.defer();
- waitForFocus(deferred.resolve);
- return deferred.promise;
-}
-
-// A simple way to clean up after each test.
-function add_chat_task(genFunction) {
- add_task(function* () {
- info("Starting chat test " + genFunction.name);
- try {
- yield genFunction();
- } finally {
- info("Finished chat test " + genFunction.name + " - cleaning up.");
- // close all docked chats.
- while (chatbar.childNodes.length) {
- chatbar.childNodes[0].close();
- }
- // and non-docked chats.
- let winEnum = Services.wm.getEnumerator("Social:Chat");
- while (winEnum.hasMoreElements()) {
- let win = winEnum.getNext();
- if (win.closed) {
- continue;
- }
- win.close();
- }
- }
- });
-}
+/* 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/. */
+
+// Utility functions for Chat tests.
+
+let Chat = Cu.import("resource:///modules/Chat.jsm", {}).Chat;
+
+function promiseOpenChat(url, mode, focus) {
+ let uri = Services.io.newURI(url, null, null);
+ let origin = uri.prePath;
+ let title = origin;
+ let deferred = Promise.defer();
+ // we just through a few hoops to ensure the content document is fully
+ // loaded, otherwise tests that rely on that content may intermittently fail.
+ let callback = function(chatbox) {
+ if (chatbox.contentDocument.readyState == "complete") {
+ // already loaded.
+ deferred.resolve(chatbox);
+ return;
+ }
+ chatbox.addEventListener("load", function onload(event) {
+ if (event.target != chatbox.contentDocument || chatbox.contentDocument.location.href == "about:blank") {
+ return;
+ }
+ chatbox.removeEventListener("load", onload, true);
+ deferred.resolve(chatbox);
+ }, true);
+ }
+ let chatbox = Chat.open(null, origin, title, url, mode, focus, callback);
+ return deferred.promise;
+}
+
+// Opens a chat, returns a promise resolved when the chat callback fired.
+function promiseOpenChatCallback(url, mode) {
+ let uri = Services.io.newURI(url, null, null);
+ let origin = uri.prePath;
+ let title = origin;
+ let deferred = Promise.defer();
+ let callback = deferred.resolve;
+ Chat.open(null, origin, title, url, mode, undefined, callback);
+ return deferred.promise;
+}
+
+// Opens a chat, returns the chat window's promise which fires when the chat
+// starts loading.
+function promiseOneEvent(target, eventName, capture) {
+ let deferred = Promise.defer();
+ target.addEventListener(eventName, function handler(event) {
+ target.removeEventListener(eventName, handler, capture);
+ deferred.resolve();
+ }, capture);
+ return deferred.promise;
+}
+
+// Return the number of chats in a browser window.
+function numChatsInWindow(win) {
+ let chatbar = win.document.getElementById("pinnedchats");
+ return chatbar.childElementCount;
+}
+
+function promiseWaitForFocus() {
+ let deferred = Promise.defer();
+ waitForFocus(deferred.resolve);
+ return deferred.promise;
+}
+
+// A simple way to clean up after each test.
+function add_chat_task(genFunction) {
+ add_task(function* () {
+ info("Starting chat test " + genFunction.name);
+ try {
+ yield genFunction();
+ } finally {
+ info("Finished chat test " + genFunction.name + " - cleaning up.");
+ // close all docked chats.
+ while (chatbar.childNodes.length) {
+ chatbar.childNodes[0].close();
+ }
+ // and non-docked chats.
+ let winEnum = Services.wm.getEnumerator("Social:Chat");
+ while (winEnum.hasMoreElements()) {
+ let win = winEnum.getNext();
+ if (win.closed) {
+ continue;
+ }
+ win.close();
+ }
+ }
+ });
+}
--- a/browser/base/content/test/general/content_aboutAccounts.js
+++ b/browser/base/content/test/general/content_aboutAccounts.js
@@ -1,48 +1,48 @@
-/* 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/. */
-
-// This file is loaded as a "content script" for browser_aboutAccounts tests
-"use strict";
-
-addEventListener("load", function load(event) {
- if (event.target != content.document) {
- return;
- }
-// content.document.removeEventListener("load", load, true);
- sendAsyncMessage("test:document:load");
-}, true);
-
-addEventListener("DOMContentLoaded", function domContentLoaded(event) {
- removeEventListener("DOMContentLoaded", domContentLoaded, true);
- let iframe = content.document.getElementById("remote");
- iframe.addEventListener("load", function iframeLoaded(event) {
- if (iframe.contentWindow.location.href == "about:blank" ||
- event.target != iframe) {
- return;
- }
- iframe.removeEventListener("load", iframeLoaded, true);
- sendAsyncMessage("test:iframe:load", {url: iframe.getAttribute("src")});
- }, true);
-}, true);
-
-// Return the visibility state of a list of ids.
-addMessageListener("test:check-visibilities", function (message) {
- let result = {};
- for (let id of message.data.ids) {
- let elt = content.document.getElementById(id);
- if (elt) {
- let displayStyle = content.window.getComputedStyle(elt).display;
- if (displayStyle == 'none') {
- result[id] = false;
- } else if (displayStyle == 'block') {
- result[id] = true;
- } else {
- result[id] = "strange: " + displayStyle; // tests should fail!
- }
- } else {
- result[id] = "doesn't exist: " + id;
- }
- }
- sendAsyncMessage("test:check-visibilities-response", result);
-});
+/* 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/. */
+
+// This file is loaded as a "content script" for browser_aboutAccounts tests
+"use strict";
+
+addEventListener("load", function load(event) {
+ if (event.target != content.document) {
+ return;
+ }
+// content.document.removeEventListener("load", load, true);
+ sendAsyncMessage("test:document:load");
+}, true);
+
+addEventListener("DOMContentLoaded", function domContentLoaded(event) {
+ removeEventListener("DOMContentLoaded", domContentLoaded, true);
+ let iframe = content.document.getElementById("remote");
+ iframe.addEventListener("load", function iframeLoaded(event) {
+ if (iframe.contentWindow.location.href == "about:blank" ||
+ event.target != iframe) {
+ return;
+ }
+ iframe.removeEventListener("load", iframeLoaded, true);
+ sendAsyncMessage("test:iframe:load", {url: iframe.getAttribute("src")});
+ }, true);
+}, true);
+
+// Return the visibility state of a list of ids.
+addMessageListener("test:check-visibilities", function (message) {
+ let result = {};
+ for (let id of message.data.ids) {
+ let elt = content.document.getElementById(id);
+ if (elt) {
+ let displayStyle = content.window.getComputedStyle(elt).display;
+ if (displayStyle == 'none') {
+ result[id] = false;
+ } else if (displayStyle == 'block') {
+ result[id] = true;
+ } else {
+ result[id] = "strange: " + displayStyle; // tests should fail!
+ }
+ } else {
+ result[id] = "doesn't exist: " + id;
+ }
+ }
+ sendAsyncMessage("test:check-visibilities-response", result);
+});
--- a/browser/base/content/test/social/browser_social_workercrash.js
+++ b/browser/base/content/test/social/browser_social_workercrash.js
@@ -1,157 +1,157 @@
-/* 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/. */
-
-// This tests our recovery if a child content process hosting providers
-// crashes.
-
-// A content script we inject into one of our browsers
-const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/browser/base/content/test/social/social_crash_content_helper.js";
-
-let {getFrameWorkerHandle} = Cu.import("resource://gre/modules/FrameWorker.jsm", {});
-let {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
-
-function test() {
- waitForExplicitFinish();
-
- // We need to ensure all our workers are in the same content process.
- Services.prefs.setIntPref("dom.ipc.processCount", 1);
-
- // This test generates many uncaught promises that should not cause failures.
- Promise.Debugging.clearUncaughtErrorObservers();
-
- runSocialTestWithProvider(gProviders, function (finishcb) {
- runSocialTests(tests, undefined, undefined, function() {
- Services.prefs.clearUserPref("dom.ipc.processCount");
- finishcb();
- });
- });
-}
-
-let gProviders = [
- {
- name: "provider 1",
- origin: "https://example.com",
- sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?provider1",
- workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
- iconURL: "chrome://branding/content/icon48.png"
- },
- {
- name: "provider 2",
- origin: "https://test1.example.com",
- sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider2",
- workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
- iconURL: "chrome://branding/content/icon48.png"
- }
-];
-
-var tests = {
- testCrash: function(next) {
- // open the sidebar, then crash the child.
- let sbrowser = document.getElementById("social-sidebar-browser");
- onSidebarLoad(function() {
- // get the browser element for our provider.
- let fw = getFrameWorkerHandle(gProviders[0].workerURL);
- fw.port.close();
- fw._worker.browserPromise.then(browser => {
- let mm = browser.messageManager;
- mm.loadFrameScript(TEST_CONTENT_HELPER, false);
- // add an observer for the crash - after it sees the crash we attempt
- // a reload.
- let observer = new crashObserver(function() {
- info("Saw the process crash.")
- Services.obs.removeObserver(observer, 'ipc:content-shutdown');
- // Add another sidebar load listener - it should be the error page.
- onSidebarLoad(function() {
- ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page");
- // after reloading, the sidebar should reload
- onSidebarLoad(function() {
- // now ping both workers - they should both be alive.
- ensureWorkerLoaded(gProviders[0], function() {
- ensureWorkerLoaded(gProviders[1], function() {
- // and we are done!
- next();
- });
- });
- });
- // click the try-again button.
- sbrowser.contentDocument.getElementById("btnTryAgain").click();
- });
- });
- Services.obs.addObserver(observer, 'ipc:content-shutdown', false);
- // and cause the crash.
- mm.sendAsyncMessage("social-test:crash");
- });
- })
- SocialSidebar.show();
- },
-}
-
-function onSidebarLoad(callback) {
- let sbrowser = document.getElementById("social-sidebar-browser");
- sbrowser.addEventListener("load", function load() {
- sbrowser.removeEventListener("load", load, true);
- callback();
- }, true);
-}
-
-function ensureWorkerLoaded(manifest, callback) {
- let fw = getFrameWorkerHandle(manifest.workerURL);
- // once the worker responds to a ping we know it must be up.
- let port = fw.port;
- port.onmessage = function(msg) {
- if (msg.data.topic == "pong") {
- port.close();
- callback();
- }
- }
- port.postMessage({topic: "ping"})
-}
-
-// More duplicated code from browser_thumbnails_brackground_crash.
-// Bug 915518 exists to unify these.
-
-// This observer is needed so we can clean up all evidence of the crash so
-// the testrunner thinks things are peachy.
-let crashObserver = function(callback) {
- this.callback = callback;
-}
-crashObserver.prototype = {
- observe: function(subject, topic, data) {
- is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
- ok(subject instanceof Components.interfaces.nsIPropertyBag2,
- 'Subject implements nsIPropertyBag2.');
- // we might see this called as the process terminates due to previous tests.
- // We are only looking for "abnormal" exits...
- if (!subject.hasKey("abnormal")) {
- info("This is a normal termination and isn't the one we are looking for...");
- return;
- }
-
- var dumpID;
- if ('nsICrashReporter' in Components.interfaces) {
- dumpID = subject.getPropertyAsAString('dumpID');
- ok(dumpID, "dumpID is present and not an empty string");
- }
-
- if (dumpID) {
- var minidumpDirectory = getMinidumpDirectory();
- removeFile(minidumpDirectory, dumpID + '.dmp');
- removeFile(minidumpDirectory, dumpID + '.extra');
- }
- this.callback();
- }
-}
-
-function getMinidumpDirectory() {
- var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
- dir.append("minidumps");
- return dir;
-}
-function removeFile(directory, filename) {
- var file = directory.clone();
- file.append(filename);
- if (file.exists()) {
- file.remove(false);
- }
-}
+/* 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/. */
+
+// This tests our recovery if a child content process hosting providers
+// crashes.
+
+// A content script we inject into one of our browsers
+const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/browser/base/content/test/social/social_crash_content_helper.js";
+
+let {getFrameWorkerHandle} = Cu.import("resource://gre/modules/FrameWorker.jsm", {});
+let {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
+
+function test() {
+ waitForExplicitFinish();
+
+ // We need to ensure all our workers are in the same content process.
+ Services.prefs.setIntPref("dom.ipc.processCount", 1);
+
+ // This test generates many uncaught promises that should not cause failures.
+ Promise.Debugging.clearUncaughtErrorObservers();
+
+ runSocialTestWithProvider(gProviders, function (finishcb) {
+ runSocialTests(tests, undefined, undefined, function() {
+ Services.prefs.clearUserPref("dom.ipc.processCount");
+ finishcb();
+ });
+ });
+}
+
+let gProviders = [
+ {
+ name: "provider 1",
+ origin: "https://example.com",
+ sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?provider1",
+ workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
+ iconURL: "chrome://branding/content/icon48.png"
+ },
+ {
+ name: "provider 2",
+ origin: "https://test1.example.com",
+ sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider2",
+ workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
+ iconURL: "chrome://branding/content/icon48.png"
+ }
+];
+
+var tests = {
+ testCrash: function(next) {
+ // open the sidebar, then crash the child.
+ let sbrowser = document.getElementById("social-sidebar-browser");
+ onSidebarLoad(function() {
+ // get the browser element for our provider.
+ let fw = getFrameWorkerHandle(gProviders[0].workerURL);
+ fw.port.close();
+ fw._worker.browserPromise.then(browser => {
+ let mm = browser.messageManager;
+ mm.loadFrameScript(TEST_CONTENT_HELPER, false);
+ // add an observer for the crash - after it sees the crash we attempt
+ // a reload.
+ let observer = new crashObserver(function() {
+ info("Saw the process crash.")
+ Services.obs.removeObserver(observer, 'ipc:content-shutdown');
+ // Add another sidebar load listener - it should be the error page.
+ onSidebarLoad(function() {
+ ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page");
+ // after reloading, the sidebar should reload
+ onSidebarLoad(function() {
+ // now ping both workers - they should both be alive.
+ ensureWorkerLoaded(gProviders[0], function() {
+ ensureWorkerLoaded(gProviders[1], function() {
+ // and we are done!
+ next();
+ });
+ });
+ });
+ // click the try-again button.
+ sbrowser.contentDocument.getElementById("btnTryAgain").click();
+ });
+ });
+ Services.obs.addObserver(observer, 'ipc:content-shutdown', false);
+ // and cause the crash.
+ mm.sendAsyncMessage("social-test:crash");
+ });
+ })
+ SocialSidebar.show();
+ },
+}
+
+function onSidebarLoad(callback) {
+ let sbrowser = document.getElementById("social-sidebar-browser");
+ sbrowser.addEventListener("load", function load() {
+ sbrowser.removeEventListener("load", load, true);
+ callback();
+ }, true);
+}
+
+function ensureWorkerLoaded(manifest, callback) {
+ let fw = getFrameWorkerHandle(manifest.workerURL);
+ // once the worker responds to a ping we know it must be up.
+ let port = fw.port;
+ port.onmessage = function(msg) {
+ if (msg.data.topic == "pong") {
+ port.close();
+ callback();
+ }
+ }
+ port.postMessage({topic: "ping"})
+}
+
+// More duplicated code from browser_thumbnails_brackground_crash.
+// Bug 915518 exists to unify these.
+
+// This observer is needed so we can clean up all evidence of the crash so
+// the testrunner thinks things are peachy.
+let crashObserver = function(callback) {
+ this.callback = callback;
+}
+crashObserver.prototype = {
+ observe: function(subject, topic, data) {
+ is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
+ ok(subject instanceof Components.interfaces.nsIPropertyBag2,
+ 'Subject implements nsIPropertyBag2.');
+ // we might see this called as the process terminates due to previous tests.
+ // We are only looking for "abnormal" exits...
+ if (!subject.hasKey("abnormal")) {
+ info("This is a normal termination and isn't the one we are looking for...");
+ return;
+ }
+
+ var dumpID;
+ if ('nsICrashReporter' in Components.interfaces) {
+ dumpID = subject.getPropertyAsAString('dumpID');
+ ok(dumpID, "dumpID is present and not an empty string");
+ }
+
+ if (dumpID) {
+ var minidumpDirectory = getMinidumpDirectory();
+ removeFile(minidumpDirectory, dumpID + '.dmp');
+ removeFile(minidumpDirectory, dumpID + '.extra');
+ }
+ this.callback();
+ }
+}
+
+function getMinidumpDirectory() {
+ var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
+ dir.append("minidumps");
+ return dir;
+}
+function removeFile(directory, filename) {
+ var file = directory.clone();
+ file.append(filename);
+ if (file.exists()) {
+ file.remove(false);
+ }
+}
--- a/browser/components/customizableui/src/logging.js
+++ b/browser/components/customizableui/src/logging.js
@@ -1,31 +1,31 @@
-#if 0
-/* 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/. */
-#endif
-
-XPCOMUtils.defineLazyModuleGetter(this, "console",
- "resource://gre/modules/devtools/Console.jsm");
-
-let gDebug = false;
-try {
- gDebug = Services.prefs.getBoolPref(kPrefCustomizationDebug);
-} catch (e) {}
-
-function LOG(...args) {
- if (gDebug) {
- args.unshift(gModuleName);
- console.log.apply(console, args);
- }
-}
-
-function ERROR(...args) {
- args.unshift(gModuleName);
- console.error.apply(console, args);
-}
-
-function INFO(...args) {
- args.unshift(gModuleName);
- console.info.apply(console, args);
-}
-
+#if 0
+/* 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/. */
+#endif
+
+XPCOMUtils.defineLazyModuleGetter(this, "console",
+ "resource://gre/modules/devtools/Console.jsm");
+
+let gDebug = false;
+try {
+ gDebug = Services.prefs.getBoolPref(kPrefCustomizationDebug);
+} catch (e) {}
+
+function LOG(...args) {
+ if (gDebug) {
+ args.unshift(gModuleName);
+ console.log.apply(console, args);
+ }
+}
+
+function ERROR(...args) {
+ args.unshift(gModuleName);
+ console.error.apply(console, args);
+}
+
+function INFO(...args) {
+ args.unshift(gModuleName);
+ console.info.apply(console, args);
+}
+
--- a/browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js
+++ b/browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js
@@ -1,39 +1,39 @@
-/* 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/. */
-
-"use strict";
-
-// Observers should be disabled when in customization mode.
-add_task(function() {
- // Open and close the panel to make sure that the
- // area is generated before getting a child of the area.
- let shownPanelPromise = promisePanelShown(window);
- PanelUI.toggle({type: "command"});
- yield shownPanelPromise;
- let hiddenPanelPromise = promisePanelHidden(window);
- PanelUI.toggle({type: "command"});
- yield hiddenPanelPromise;
-
- let fullscreenButton = document.getElementById("fullscreen-button");
- ok(!fullscreenButton.checked, "Fullscreen button should not be checked when not in fullscreen.")
-
- BrowserFullScreen();
- yield waitForCondition(function() fullscreenButton.checked);
- ok(fullscreenButton.checked, "Fullscreen button should be checked when in fullscreen.")
-
- yield startCustomizing();
-
- let fullscreenButtonWrapper = document.getElementById("wrapper-fullscreen-button");
- ok(fullscreenButtonWrapper.hasAttribute("itemobserves"), "Observer should be moved to wrapper");
- fullscreenButton = document.getElementById("fullscreen-button");
- ok(!fullscreenButton.hasAttribute("observes"), "Observer should be removed from button");
- ok(!fullscreenButton.checked, "Fullscreen button should no longer be checked during customization mode");
-
- yield endCustomizing();
-
- BrowserFullScreen();
- fullscreenButton = document.getElementById("fullscreen-button");
- yield waitForCondition(function() !fullscreenButton.checked);
- ok(!fullscreenButton.checked, "Fullscreen button should not be checked when not in fullscreen.")
-});
+/* 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/. */
+
+"use strict";
+
+// Observers should be disabled when in customization mode.
+add_task(function() {
+ // Open and close the panel to make sure that the
+ // area is generated before getting a child of the area.
+ let shownPanelPromise = promisePanelShown(window);
+ PanelUI.toggle({type: "command"});
+ yield shownPanelPromise;
+ let hiddenPanelPromise = promisePanelHidden(window);
+ PanelUI.toggle({type: "command"});
+ yield hiddenPanelPromise;
+
+ let fullscreenButton = document.getElementById("fullscreen-button");
+ ok(!fullscreenButton.checked, "Fullscreen button should not be checked when not in fullscreen.")
+
+ BrowserFullScreen();
+ yield waitForCondition(function() fullscreenButton.checked);
+ ok(fullscreenButton.checked, "Fullscreen button should be checked when in fullscreen.")
+
+ yield startCustomizing();
+
+ let fullscreenButtonWrapper = document.getElementById("wrapper-fullscreen-button");
+ ok(fullscreenButtonWrapper.hasAttribute("itemobserves"), "Observer should be moved to wrapper");
+ fullscreenButton = document.getElementById("fullscreen-button");
+ ok(!fullscreenButton.hasAttribute("observes"), "Observer should be removed from button");
+ ok(!fullscreenButton.checked, "Fullscreen button should no longer be checked during customization mode");
+
+ yield endCustomizing();
+
+ BrowserFullScreen();
+ fullscreenButton = document.getElementById("fullscreen-button");
+ yield waitForCondition(function() !fullscreenButton.checked);
+ ok(!fullscreenButton.checked, "Fullscreen button should not be checked when not in fullscreen.")
+});
--- a/browser/components/customizableui/test/browser_888817_currentset_updating.js
+++ b/browser/components/customizableui/test/browser_888817_currentset_updating.js
@@ -1,57 +1,57 @@
-/* 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/. */
-
-"use strict";
-
-// Adding, moving and removing items should update the relevant currentset attributes
-add_task(function() {
- ok(CustomizableUI.inDefaultState, "Should be in the default state when we start");
- let personalbar = document.getElementById(CustomizableUI.AREA_BOOKMARKS);
- setToolbarVisibility(personalbar, true);
- ok(!CustomizableUI.inDefaultState, "Making the bookmarks toolbar visible takes it out of the default state");
-
- let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
- let personalbar = document.getElementById(CustomizableUI.AREA_BOOKMARKS);
- let navbarCurrentset = navbar.getAttribute("currentset") || navbar.currentSet;
- let personalbarCurrentset = personalbar.getAttribute("currentset") || personalbar.currentSet;
-
- let otherWin = yield openAndLoadWindow();
- let otherNavbar = otherWin.document.getElementById(CustomizableUI.AREA_NAVBAR);
- let otherPersonalbar = otherWin.document.getElementById(CustomizableUI.AREA_BOOKMARKS);
-
- CustomizableUI.moveWidgetWithinArea("home-button", 0);
- navbarCurrentset = "home-button," + navbarCurrentset.replace(",home-button", "");
- is(navbar.getAttribute("currentset"), navbarCurrentset,
- "Should have updated currentSet after move.");
- is(otherNavbar.getAttribute("currentset"), navbarCurrentset,
- "Should have updated other window's currentSet after move.");
-
- CustomizableUI.addWidgetToArea("home-button", CustomizableUI.AREA_BOOKMARKS);
- navbarCurrentset = navbarCurrentset.replace("home-button,", "");
- personalbarCurrentset = personalbarCurrentset + ",home-button";
- is(navbar.getAttribute("currentset"), navbarCurrentset,
- "Should have updated navbar currentSet after implied remove.");
- is(otherNavbar.getAttribute("currentset"), navbarCurrentset,
- "Should have updated other window's navbar currentSet after implied remove.");
- is(personalbar.getAttribute("currentset"), personalbarCurrentset,
- "Should have updated personalbar currentSet after add.");
- is(otherPersonalbar.getAttribute("currentset"), personalbarCurrentset,
- "Should have updated other window's personalbar currentSet after add.");
-
- CustomizableUI.removeWidgetFromArea("home-button");
- personalbarCurrentset = personalbarCurrentset.replace(",home-button", "");
- is(personalbar.getAttribute("currentset"), personalbarCurrentset,
- "Should have updated currentSet after remove.");
- is(otherPersonalbar.getAttribute("currentset"), personalbarCurrentset,
- "Should have updated other window's currentSet after remove.");
-
- yield promiseWindowClosed(otherWin);
- // Reset in asyncCleanup will put our button back for us.
-});
-
-add_task(function asyncCleanup() {
- let personalbar = document.getElementById(CustomizableUI.AREA_BOOKMARKS);
- setToolbarVisibility(personalbar, false);
- yield resetCustomization();
-});
+/* 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/. */
+
+"use strict";
+
+// Adding, moving and removing items should update the relevant currentset attributes
+add_task(function() {
+ ok(CustomizableUI.inDefaultState, "Should be in the default state when we start");
+ let personalbar = document.getElementById(CustomizableUI.AREA_BOOKMARKS);
+ setToolbarVisibility(personalbar, true);
+ ok(!CustomizableUI.inDefaultState, "Making the bookmarks toolbar visible takes it out of the default state");
+
+ let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+ let personalbar = document.getElementById(CustomizableUI.AREA_BOOKMARKS);
+ let navbarCurrentset = navbar.getAttribute("currentset") || navbar.currentSet;
+ let personalbarCurrentset = personalbar.getAttribute("currentset") || personalbar.currentSet;
+
+ let otherWin = yield openAndLoadWindow();
+ let otherNavbar = otherWin.document.getElementById(CustomizableUI.AREA_NAVBAR);
+ let otherPersonalbar = otherWin.document.getElementById(CustomizableUI.AREA_BOOKMARKS);
+
+ CustomizableUI.moveWidgetWithinArea("home-button", 0);
+ navbarCurrentset = "home-button," + navbarCurrentset.replace(",home-button", "");
+ is(navbar.getAttribute("currentset"), navbarCurrentset,
+ "Should have updated currentSet after move.");
+ is(otherNavbar.getAttribute("currentset"), navbarCurrentset,
+ "Should have updated other window's currentSet after move.");
+
+ CustomizableUI.addWidgetToArea("home-button", CustomizableUI.AREA_BOOKMARKS);
+ navbarCurrentset = navbarCurrentset.replace("home-button,", "");
+ personalbarCurrentset = personalbarCurrentset + ",home-button";
+ is(navbar.getAttribute("currentset"), navbarCurrentset,
+ "Should have updated navbar currentSet after implied remove.");
+ is(otherNavbar.getAttribute("currentset"), navbarCurrentset,
+ "Should have updated other window's navbar currentSet after implied remove.");
+ is(personalbar.getAttribute("currentset"), personalbarCurrentset,
+ "Should have updated personalbar currentSet after add.");
+ is(otherPersonalbar.getAttribute("currentset"), personalbarCurrentset,
+ "Should have updated other window's personalbar currentSet after add.");
+
+ CustomizableUI.removeWidgetFromArea("home-button");
+ personalbarCurrentset = personalbarCurrentset.replace(",home-button", "");
+ is(personalbar.getAttribute("currentset"), personalbarCurrentset,
+ "Should have updated currentSet after remove.");
+ is(otherPersonalbar.getAttribute("currentset"), personalbarCurrentset,
+ "Should have updated other window's currentSet after remove.");
+
+ yield promiseWindowClosed(otherWin);
+ // Reset in asyncCleanup will put our button back for us.
+});
+
+add_task(function asyncCleanup() {
+ let personalbar = document.getElementById(CustomizableUI.AREA_BOOKMARKS);
+ setToolbarVisibility(personalbar, false);
+ yield resetCustomization();
+});
--- a/browser/components/customizableui/test/browser_890140_orphaned_placeholders.js
+++ b/browser/components/customizableui/test/browser_890140_orphaned_placeholders.js
@@ -1,167 +1,167 @@
-/* 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/. */
-
-"use strict";
-
-requestLongerTimeout(2);
-
-// One orphaned item should have two placeholders next to it.
-add_task(function() {
- yield startCustomizing();
- let btn = document.getElementById("open-file-button");
- let panel = document.getElementById(CustomizableUI.AREA_PANEL);
- let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
-
- if (isInWin8()) {
- CustomizableUI.removeWidgetFromArea("switch-to-metro-button");
- placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
- ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
- } else {
- ok(CustomizableUI.inDefaultState, "Should be in default state.");
- }
-
- assertAreaPlacements(CustomizableUI.AREA_PANEL, placements);
- is(getVisiblePlaceholderCount(panel), 2, "Should only have 2 visible placeholders before exiting");
-
- yield endCustomizing();
- yield startCustomizing();
- is(getVisiblePlaceholderCount(panel), 2, "Should only have 2 visible placeholders after re-entering");
-
- if (isInWin8()) {
- CustomizableUI.addWidgetToArea("switch-to-metro-button", CustomizableUI.AREA_PANEL);
- }
- ok(CustomizableUI.inDefaultState, "Should be in default state again.");
-});
-
-// Two orphaned items should have one placeholder next to them (case 1).
-add_task(function() {
- yield startCustomizing();
- let btn = document.getElementById("open-file-button");
- let panel = document.getElementById(CustomizableUI.AREA_PANEL);
- let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
-
- let placementsAfterAppend = placements;
-
- if (!isInWin8()) {
- placementsAfterAppend = placements.concat(["open-file-button"]);
- simulateItemDrag(btn, panel);
- }
-
- assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
- is(CustomizableUI.inDefaultState, isInWin8(), "Should only be in default state if on Win8");
- is(getVisiblePlaceholderCount(panel), 1, "Should only have 1 visible placeholder before exiting");
-
- yield endCustomizing();
- yield startCustomizing();
- is(getVisiblePlaceholderCount(panel), 1, "Should only have 1 visible placeholder after re-entering");
-
- let palette = document.getElementById("customization-palette");
- simulateItemDrag(btn, palette);
-
- if (!isInWin8()) {
- btn = document.getElementById("open-file-button");
- simulateItemDrag(btn, palette);
- }
- ok(CustomizableUI.inDefaultState, "Should be in default state again.");
-});
-
-// Two orphaned items should have one placeholder next to them (case 2).
-add_task(function() {
- yield startCustomizing();
- let btn = document.getElementById("add-ons-button");
- let btn2 = document.getElementById("developer-button");
- let btn3 = document.getElementById("switch-to-metro-button");
- let panel = document.getElementById(CustomizableUI.AREA_PANEL);
- let palette = document.getElementById("customization-palette");
- let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
-
- let placementsAfterAppend = placements.filter(p => p != btn.id && p != btn2.id);
- simulateItemDrag(btn, palette);
- simulateItemDrag(btn2, palette);
-
- if (isInWin8()) {
- placementsAfterAppend = placementsAfterAppend.filter(p => p != btn3.id);
- simulateItemDrag(btn3, palette);
- }
-
- assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
- ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
- is(getVisiblePlaceholderCount(panel), 1, "Should only have 1 visible placeholder before exiting");
-
- yield endCustomizing();
- yield startCustomizing();
- is(getVisiblePlaceholderCount(panel), 1, "Should only have 1 visible placeholder after re-entering");
-
- simulateItemDrag(btn, panel);
- simulateItemDrag(btn2, panel);
-
- if (isInWin8()) {
- simulateItemDrag(btn3, panel);
- }
-
- assertAreaPlacements(CustomizableUI.AREA_PANEL, placements);
- ok(CustomizableUI.inDefaultState, "Should be in default state again.");
-});
-
-// A wide widget at the bottom of the panel should have three placeholders after it.
-add_task(function() {
- yield startCustomizing();
- let btn = document.getElementById("edit-controls");
- let developerButton = document.getElementById("developer-button");
- let metroBtn = document.getElementById("switch-to-metro-button");
- let panel = document.getElementById(CustomizableUI.AREA_PANEL);
- let palette = document.getElementById("customization-palette");
- let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
-
- placements.pop();
- simulateItemDrag(developerButton, palette);
- if (isInWin8()) {
- // Remove switch-to-metro-button
- placements.pop();
- simulateItemDrag(metroBtn, palette);
- }
-
- let placementsAfterAppend = placements.concat([placements.shift()]);
- simulateItemDrag(btn, panel);
- assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
- ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
- is(getVisiblePlaceholderCount(panel), 3, "Should have 3 visible placeholders before exiting");
-
- yield endCustomizing();
- yield startCustomizing();
- is(getVisiblePlaceholderCount(panel), 3, "Should have 3 visible placeholders after re-entering");
-
- simulateItemDrag(developerButton, panel);
- if (isInWin8()) {
- simulateItemDrag(metroBtn, panel);
- }
- let zoomControls = document.getElementById("zoom-controls");
- simulateItemDrag(btn, zoomControls);
- ok(CustomizableUI.inDefaultState, "Should be in default state again.");
-});
-
-// The default placements should have two placeholders at the bottom (or 1 in win8).
-add_task(function() {
- yield startCustomizing();
- let numPlaceholders = isInWin8() ? 1 : 2;
- let panel = document.getElementById(CustomizableUI.AREA_PANEL);
- ok(CustomizableUI.inDefaultState, "Should be in default state.");
- is(getVisiblePlaceholderCount(panel), numPlaceholders, "Should have " + numPlaceholders + " visible placeholders before exiting");
-
- yield endCustomizing();
- yield startCustomizing();
- is(getVisiblePlaceholderCount(panel), numPlaceholders, "Should have " + numPlaceholders + " visible placeholders after re-entering");
-
- ok(CustomizableUI.inDefaultState, "Should still be in default state.");
-});
-
-add_task(function asyncCleanup() {
- yield endCustomizing();
- yield resetCustomization();
-});
-
-function getVisiblePlaceholderCount(aPanel) {
- let visiblePlaceholders = aPanel.querySelectorAll(".panel-customization-placeholder:not([hidden=true])");
- return visiblePlaceholders.length;
-}
+/* 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/. */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+// One orphaned item should have two placeholders next to it.
+add_task(function() {
+ yield startCustomizing();
+ let btn = document.getElementById("open-file-button");
+ let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+ let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
+
+ if (isInWin8()) {
+ CustomizableUI.removeWidgetFromArea("switch-to-metro-button");
+ placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
+ ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+ } else {
+ ok(CustomizableUI.inDefaultState, "Should be in default state.");
+ }
+
+ assertAreaPlacements(CustomizableUI.AREA_PANEL, placements);
+ is(getVisiblePlaceholderCount(panel), 2, "Should only have 2 visible placeholders before exiting");
+
+ yield endCustomizing();
+ yield startCustomizing();
+ is(getVisiblePlaceholderCount(panel), 2, "Should only have 2 visible placeholders after re-entering");
+
+ if (isInWin8()) {
+ CustomizableUI.addWidgetToArea("switch-to-metro-button", CustomizableUI.AREA_PANEL);
+ }
+ ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+});
+
+// Two orphaned items should have one placeholder next to them (case 1).
+add_task(function() {
+ yield startCustomizing();
+ let btn = document.getElementById("open-file-button");
+ let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+ let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
+
+ let placementsAfterAppend = placements;
+
+ if (!isInWin8()) {
+ placementsAfterAppend = placements.concat(["open-file-button"]);
+ simulateItemDrag(btn, panel);
+ }
+
+ assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
+ is(CustomizableUI.inDefaultState, isInWin8(), "Should only be in default state if on Win8");
+ is(getVisiblePlaceholderCount(panel), 1, "Should only have 1 visible placeholder before exiting");
+
+ yield endCustomizing();
+ yield startCustomizing();
+ is(getVisiblePlaceholderCount(panel), 1, "Should only have 1 visible placeholder after re-entering");
+
+ let palette = document.getElementById("customization-palette");
+ simulateItemDrag(btn, palette);
+
+ if (!isInWin8()) {
+ btn = document.getElementById("open-file-button");
+ simulateItemDrag(btn, palette);
+ }
+ ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+});
+
+// Two orphaned items should have one placeholder next to them (case 2).
+add_task(function() {
+ yield startCustomizing();
+ let btn = document.getElementById("add-ons-button");
+ let btn2 = document.getElementById("developer-button");
+ let btn3 = document.getElementById("switch-to-metro-button");
+ let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+ let palette = document.getElementById("customization-palette");
+ let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
+
+ let placementsAfterAppend = placements.filter(p => p != btn.id && p != btn2.id);
+ simulateItemDrag(btn, palette);
+ simulateItemDrag(btn2, palette);
+
+ if (isInWin8()) {
+ placementsAfterAppend = placementsAfterAppend.filter(p => p != btn3.id);
+ simulateItemDrag(btn3, palette);
+ }
+
+ assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
+ ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+ is(getVisiblePlaceholderCount(panel), 1, "Should only have 1 visible placeholder before exiting");
+
+ yield endCustomizing();
+ yield startCustomizing();
+ is(getVisiblePlaceholderCount(panel), 1, "Should only have 1 visible placeholder after re-entering");
+
+ simulateItemDrag(btn, panel);
+ simulateItemDrag(btn2, panel);
+
+ if (isInWin8()) {
+ simulateItemDrag(btn3, panel);
+ }
+
+ assertAreaPlacements(CustomizableUI.AREA_PANEL, placements);
+ ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+});
+
+// A wide widget at the bottom of the panel should have three placeholders after it.
+add_task(function() {
+ yield startCustomizing();
+ let btn = document.getElementById("edit-controls");
+ let developerButton = document.getElementById("developer-button");
+ let metroBtn = document.getElementById("switch-to-metro-button");
+ let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+ let palette = document.getElementById("customization-palette");
+ let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
+
+ placements.pop();
+ simulateItemDrag(developerButton, palette);
+ if (isInWin8()) {
+ // Remove switch-to-metro-button
+ placements.pop();
+ simulateItemDrag(metroBtn, palette);
+ }
+
+ let placementsAfterAppend = placements.concat([placements.shift()]);
+ simulateItemDrag(btn, panel);
+ assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
+ ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+ is(getVisiblePlaceholderCount(panel), 3, "Should have 3 visible placeholders before exiting");
+
+ yield endCustomizing();
+ yield startCustomizing();
+ is(getVisiblePlaceholderCount(panel), 3, "Should have 3 visible placeholders after re-entering");
+
+ simulateItemDrag(developerButton, panel);
+ if (isInWin8()) {
+ simulateItemDrag(metroBtn, panel);
+ }
+ let zoomControls = document.getElementById("zoom-controls");
+ simulateItemDrag(btn, zoomControls);
+ ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+});
+
+// The default placements should have two placeholders at the bottom (or 1 in win8).
+add_task(function() {
+ yield startCustomizing();
+ let numPlaceholders = isInWin8() ? 1 : 2;
+ let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+ ok(CustomizableUI.inDefaultState, "Should be in default state.");
+ is(getVisiblePlaceholderCount(panel), numPlaceholders, "Should have " + numPlaceholders + " visible placeholders before exiting");
+
+ yield endCustomizing();
+ yield startCustomizing();
+ is(getVisiblePlaceholderCount(panel), numPlaceholders, "Should have " + numPlaceholders + " visible placeholders after re-entering");
+
+ ok(CustomizableUI.inDefaultState, "Should still be in default state.");
+});
+
+add_task(function asyncCleanup() {
+ yield endCustomizing();
+ yield resetCustomization();
+});
+
+function getVisiblePlaceholderCount(aPanel) {
+ let visiblePlaceholders = aPanel.querySelectorAll(".panel-customization-placeholder:not([hidden=true])");
+ return visiblePlaceholders.length;
+}
--- a/browser/components/customizableui/test/browser_901207_searchbar_in_panel.js
+++ b/browser/components/customizableui/test/browser_901207_searchbar_in_panel.js
@@ -1,148 +1,148 @@
-/* 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/. */
-
-"use strict";
-
-let openUILinkInCalled = false;
-let expectOpenUILinkInCall = false;
-this.originalOpenUILinkIn = openUILinkIn;
-openUILinkIn = (aUrl, aWhichTab) => {
- is(aUrl, "about:home", "about:home should be requested to open.");
- is(aWhichTab, "current", "Should use the current tab for the search page.");
- openUILinkInCalled = true;
- if (!expectOpenUILinkInCall) {
- ok(false, "OpenUILinkIn was called when it shouldn't have been.");
- }
-};
-logActiveElement();
-
-function* waitForSearchBarFocus()
-{
- let searchbar = document.getElementById("searchbar");
- yield waitForCondition(function () {
- logActiveElement();
- return document.activeElement === searchbar.textbox.inputField;
- });
-}
-
-// Ctrl+K should open the menu panel and focus the search bar if the search bar is in the panel.
-add_task(function() {
- let searchbar = document.getElementById("searchbar");
- gCustomizeMode.addToPanel(searchbar);
- let placement = CustomizableUI.getPlacementOfWidget("search-container");
- is(placement.area, CustomizableUI.AREA_PANEL, "Should be in panel");
-
- let shownPanelPromise = promisePanelShown(window);
- sendWebSearchKeyCommand();
- yield shownPanelPromise;
-
- yield waitForSearchBarFocus();
-
- let hiddenPanelPromise = promisePanelHidden(window);
- EventUtils.synthesizeKey("VK_ESCAPE", {});
- yield hiddenPanelPromise;
- CustomizableUI.reset();
-});
-
-// Ctrl+K should give focus to the searchbar when the searchbar is in the menupanel and the panel is already opened.
-add_task(function() {
- let searchbar = document.getElementById("searchbar");
- gCustomizeMode.addToPanel(searchbar);
- let placement = CustomizableUI.getPlacementOfWidget("search-container");
- is(placement.area, CustomizableUI.AREA_PANEL, "Should be in panel");
-
- let shownPanelPromise = promisePanelShown(window);
- PanelUI.toggle({type: "command"});
- yield shownPanelPromise;
-
- sendWebSearchKeyCommand();
-
- yield waitForSearchBarFocus();
-
- let hiddenPanelPromise = promisePanelHidden(window);
- EventUtils.synthesizeKey("VK_ESCAPE", {});
- yield hiddenPanelPromise;
- CustomizableUI.reset();
-});
-
-// Ctrl+K should open the overflow panel and focus the search bar if the search bar is overflowed.
-add_task(function() {
- this.originalWindowWidth = window.outerWidth;
- let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
- ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
- ok(CustomizableUI.inDefaultState, "Should start in default state.");
-
- window.resizeTo(360, window.outerHeight);
- yield waitForCondition(() => navbar.getAttribute("overflowing") == "true");
- ok(!navbar.querySelector("#search-container"), "Search container should be overflowing");
-
- let shownPanelPromise = promiseOverflowShown(window);
- sendWebSearchKeyCommand();
- yield shownPanelPromise;
-
- let chevron = document.getElementById("nav-bar-overflow-button");
- yield waitForCondition(function() chevron.open);
-
- yield waitForSearchBarFocus();
-
- let hiddenPanelPromise = promiseOverflowHidden(window);
- EventUtils.synthesizeKey("VK_ESCAPE", {});
- yield hiddenPanelPromise;
- let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
- window.resizeTo(this.originalWindowWidth, window.outerHeight);
- yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
- ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
-});
-
-// Ctrl+K should focus the search bar if it is in the navbar and not overflowing.
-add_task(function() {
- let placement = CustomizableUI.getPlacementOfWidget("search-container");
- is(placement.area, CustomizableUI.AREA_NAVBAR, "Should be in nav-bar");
-
- sendWebSearchKeyCommand();
-
- yield waitForSearchBarFocus();
-});
-
-// Ctrl+K should open the search page if the search bar has been customized out.
-add_task(function() {
- try {
- expectOpenUILinkInCall = true;
- CustomizableUI.removeWidgetFromArea("search-container");
- let placement = CustomizableUI.getPlacementOfWidget("search-container");
- is(placement, null, "Search container should be in palette");
-
- openUILinkInCalled = false;
-
- sendWebSearchKeyCommand();
- yield waitForCondition(function() openUILinkInCalled);
- ok(openUILinkInCalled, "The search page should have been opened.")
- expectOpenUILinkInCall = false;
- } catch (e) {
- ok(false, e);
- }
- CustomizableUI.reset();
-});
-
-registerCleanupFunction(function() {
- openUILinkIn = this.originalOpenUILinkIn;
- delete this.originalOpenUILinkIn;
-});
-
-function sendWebSearchKeyCommand() {
- if (Services.appinfo.OS === "Darwin")
- EventUtils.synthesizeKey("k", { accelKey: true });
- else
- EventUtils.synthesizeKey("k", { ctrlKey: true });
-}
-
-function logActiveElement() {
- let element = document.activeElement;
- let str = "";
- while (element && element.parentNode) {
- str = " (" + element.localName + "#" + element.id + "." + [...element.classList].join(".") + ") >" + str;
- element = element.parentNode;
- }
- info("Active element: " + element ? str : "null");
-}
+/* 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/. */
+
+"use strict";
+
+let openUILinkInCalled = false;
+let expectOpenUILinkInCall = false;
+this.originalOpenUILinkIn = openUILinkIn;
+openUILinkIn = (aUrl, aWhichTab) => {
+ is(aUrl, "about:home", "about:home should be requested to open.");
+ is(aWhichTab, "current", "Should use the current tab for the search page.");
+ openUILinkInCalled = true;
+ if (!expectOpenUILinkInCall) {
+ ok(false, "OpenUILinkIn was called when it shouldn't have been.");
+ }
+};
+logActiveElement();
+
+function* waitForSearchBarFocus()
+{
+ let searchbar = document.getElementById("searchbar");
+ yield waitForCondition(function () {
+ logActiveElement();
+ return document.activeElement === searchbar.textbox.inputField;
+ });
+}
+
+// Ctrl+K should open the menu panel and focus the search bar if the search bar is in the panel.
+add_task(function() {
+ let searchbar = document.getElementById("searchbar");
+ gCustomizeMode.addToPanel(searchbar);
+ let placement = CustomizableUI.getPlacementOfWidget("search-container");
+ is(placement.area, CustomizableUI.AREA_PANEL, "Should be in panel");
+
+ let shownPanelPromise = promisePanelShown(window);
+ sendWebSearchKeyCommand();
+ yield shownPanelPromise;
+
+ yield waitForSearchBarFocus();
+
+ let hiddenPanelPromise = promisePanelHidden(window);
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ yield hiddenPanelPromise;
+ CustomizableUI.reset();
+});
+
+// Ctrl+K should give focus to the searchbar when the searchbar is in the menupanel and the panel is already opened.
+add_task(function() {
+ let searchbar = document.getElementById("searchbar");
+ gCustomizeMode.addToPanel(searchbar);
+ let placement = CustomizableUI.getPlacementOfWidget("search-container");
+ is(placement.area, CustomizableUI.AREA_PANEL, "Should be in panel");
+
+ let shownPanelPromise = promisePanelShown(window);
+ PanelUI.toggle({type: "command"});
+ yield shownPanelPromise;
+
+ sendWebSearchKeyCommand();
+
+ yield waitForSearchBarFocus();
+
+ let hiddenPanelPromise = promisePanelHidden(window);
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ yield hiddenPanelPromise;
+ CustomizableUI.reset();
+});
+
+// Ctrl+K should open the overflow panel and focus the search bar if the search bar is overflowed.
+add_task(function() {
+ this.originalWindowWidth = window.outerWidth;
+ let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+ ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
+ ok(CustomizableUI.inDefaultState, "Should start in default state.");
+
+ window.resizeTo(360, window.outerHeight);
+ yield waitForCondition(() => navbar.getAttribute("overflowing") == "true");
+ ok(!navbar.querySelector("#search-container"), "Search container should be overflowing");
+
+ let shownPanelPromise = promiseOverflowShown(window);
+ sendWebSearchKeyCommand();
+ yield shownPanelPromise;
+
+ let chevron = document.getElementById("nav-bar-overflow-button");
+ yield waitForCondition(function() chevron.open);
+
+ yield waitForSearchBarFocus();
+
+ let hiddenPanelPromise = promiseOverflowHidden(window);
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ yield hiddenPanelPromise;
+ let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+ window.resizeTo(this.originalWindowWidth, window.outerHeight);
+ yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+ ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
+});
+
+// Ctrl+K should focus the search bar if it is in the navbar and not overflowing.
+add_task(function() {
+ let placement = CustomizableUI.getPlacementOfWidget("search-container");
+ is(placement.area, CustomizableUI.AREA_NAVBAR, "Should be in nav-bar");
+
+ sendWebSearchKeyCommand();
+
+ yield waitForSearchBarFocus();
+});
+
+// Ctrl+K should open the search page if the search bar has been customized out.
+add_task(function() {
+ try {
+ expectOpenUILinkInCall = true;
+ CustomizableUI.removeWidgetFromArea("search-container");
+ let placement = CustomizableUI.getPlacementOfWidget("search-container");
+ is(placement, null, "Search container should be in palette");
+
+ openUILinkInCalled = false;
+
+ sendWebSearchKeyCommand();
+ yield waitForCondition(function() openUILinkInCalled);
+ ok(openUILinkInCalled, "The search page should have been opened.")
+ expectOpenUILinkInCall = false;
+ } catch (e) {
+ ok(false, e);
+ }
+ CustomizableUI.reset();
+});
+
+registerCleanupFunction(function() {
+ openUILinkIn = this.originalOpenUILinkIn;
+ delete this.originalOpenUILinkIn;
+});
+
+function sendWebSearchKeyCommand() {
+ if (Services.appinfo.OS === "Darwin")
+ EventUtils.synthesizeKey("k", { accelKey: true });
+ else
+ EventUtils.synthesizeKey("k", { ctrlKey: true });
+}
+
+function logActiveElement() {
+ let element = document.activeElement;
+ let str = "";
+ while (element && element.parentNode) {
+ str = " (" + element.localName + "#" + element.id + "." + [...element.classList].join(".") + ") >" + str;
+ element = element.parentNode;
+ }
+ info("Active element: " + element ? str : "null");
+}
--- a/browser/components/customizableui/test/browser_909779_overflow_toolbars_new_window.js
+++ b/browser/components/customizableui/test/browser_909779_overflow_toolbars_new_window.js
@@ -1,31 +1,31 @@
-/* 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/. */
-
-"use strict";
-
-// Resize to a small window, open a new window, check that new window handles overflow properly
-add_task(function() {
- let originalWindowWidth = window.outerWidth;
- let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
- ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
- let oldChildCount = navbar.customizationTarget.childElementCount;
- window.resizeTo(400, window.outerHeight);
- yield waitForCondition(() => navbar.hasAttribute("overflowing"));
- ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
-
- ok(navbar.customizationTarget.childElementCount < oldChildCount, "Should have fewer children.");
- let newWindow = yield openAndLoadWindow();
- let otherNavBar = newWindow.document.getElementById(CustomizableUI.AREA_NAVBAR);
- yield waitForCondition(() => otherNavBar.hasAttribute("overflowing"));
- ok(otherNavBar.hasAttribute("overflowing"), "Other window should have an overflowing toolbar.");
- yield promiseWindowClosed(newWindow);
-
- window.resizeTo(originalWindowWidth, window.outerHeight);
- yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
- ok(!navbar.hasAttribute("overflowing"), "Should no longer have an overflowing toolbar.");
-});
-
-add_task(function asyncCleanup() {
- yield resetCustomization();
-});
+/* 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/. */
+
+"use strict";
+
+// Resize to a small window, open a new window, check that new window handles overflow properly
+add_task(function() {
+ let originalWindowWidth = window.outerWidth;
+ let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+ ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
+ let oldChildCount = navbar.customizationTarget.childElementCount;
+ window.resizeTo(400, window.outerHeight);
+ yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+ ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+
+ ok(navbar.customizationTarget.childElementCount < oldChildCount, "Should have fewer children.");
+ let newWindow = yield openAndLoadWindow();
+ let otherNavBar = newWindow.document.getElementById(CustomizableUI.AREA_NAVBAR);
+ yield waitForCondition(() => otherNavBar.hasAttribute("overflowing"));
+ ok(otherNavBar.hasAttribute("overflowing"), "Other window should have an overflowing toolbar.");
+ yield promiseWindowClosed(newWindow);
+
+ window.resizeTo(originalWindowWidth, window.outerHeight);
+ yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+ ok(!navbar.hasAttribute("overflowing"), "Should no longer have an overflowing toolbar.");
+});
+
+add_task(function asyncCleanup() {
+ yield resetCustomization();
+});
--- a/browser/components/customizableui/test/browser_914863_disabled_help_quit_buttons.js
+++ b/browser/components/customizableui/test/browser_914863_disabled_help_quit_buttons.js
@@ -1,16 +1,16 @@
-/* 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/. */
-
-// Entering then exiting customization mode should reenable the Help and Exit buttons.
-add_task(function() {
- yield startCustomizing();
- let helpButton = document.getElementById("PanelUI-help");
- let quitButton = document.getElementById("PanelUI-quit");
- ok(helpButton.getAttribute("disabled") == "true", "Help button should be disabled while in customization mode.");
- ok(quitButton.getAttribute("disabled") == "true", "Quit button should be disabled while in customization mode.");
- yield endCustomizing();
-
- ok(!helpButton.hasAttribute("disabled"), "Help button should not be disabled.");
- ok(!quitButton.hasAttribute("disabled"), "Quit button should not be disabled.");
-});
+/* 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/. */
+
+// Entering then exiting customization mode should reenable the Help and Exit buttons.
+add_task(function() {
+ yield startCustomizing();
+ let helpButton = document.getElementById("PanelUI-help");
+ let quitButton = document.getElementById("PanelUI-quit");
+ ok(helpButton.getAttribute("disabled") == "true", "Help button should be disabled while in customization mode.");
+ ok(quitButton.getAttribute("disabled") == "true", "Quit button should be disabled while in customization mode.");
+ yield endCustomizing();
+
+ ok(!helpButton.hasAttribute("disabled"), "Help button should not be disabled.");
+ ok(!quitButton.hasAttribute("disabled"), "Quit button should not be disabled.");
+});
--- a/browser/components/customizableui/test/browser_932928_show_notice_when_palette_empty.js
+++ b/browser/components/customizableui/test/browser_932928_show_notice_when_palette_empty.js
@@ -1,35 +1,35 @@
-/* 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/. */
-
-"use strict";
-
-// There should be an advert to get more addons when the palette is empty.
-add_task(function() {
- yield startCustomizing();
- let visiblePalette = document.getElementById("customization-palette");
- let emptyPaletteNotice = document.getElementById("customization-empty");
- is(emptyPaletteNotice.hidden, true, "The empty palette notice should not be shown when there are items in the palette.");
-
- while (visiblePalette.childElementCount) {
- gCustomizeMode.addToToolbar(visiblePalette.children[0]);
- }
- is(visiblePalette.childElementCount, 0, "There shouldn't be any items remaining in the visible palette.");
- is(emptyPaletteNotice.hidden, false, "The empty palette notice should be shown when there are no items in the palette.");
-
- yield endCustomizing();
- yield startCustomizing();
- visiblePalette = document.getElementById("customization-palette");
- emptyPaletteNotice = document.getElementById("customization-empty");
- is(emptyPaletteNotice.hidden, false,
- "The empty palette notice should be shown when there are no items in the palette and cust. mode is re-entered.");
-
- gCustomizeMode.removeFromArea(document.getElementById("wrapper-home-button"));
- is(emptyPaletteNotice.hidden, true,
- "The empty palette notice should not be shown when there is at least one item in the palette.");
-});
-
-add_task(function asyncCleanup() {
- yield endCustomizing();
- yield resetCustomization();
-});
+/* 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/. */
+
+"use strict";
+
+// There should be an advert to get more addons when the palette is empty.
+add_task(function() {
+ yield startCustomizing();
+ let visiblePalette = document.getElementById("customization-palette");
+ let emptyPaletteNotice = document.getElementById("customization-empty");
+ is(emptyPaletteNotice.hidden, true, "The empty palette notice should not be shown when there are items in the palette.");
+
+ while (visiblePalette.childElementCount) {
+ gCustomizeMode.addToToolbar(visiblePalette.children[0]);
+ }
+ is(visiblePalette.childElementCount, 0, "There shouldn't be any items remaining in the visible palette.");
+ is(emptyPaletteNotice.hidden, false, "The empty palette notice should be shown when there are no items in the palette.");
+
+ yield endCustomizing();
+ yield startCustomizing();
+ visiblePalette = document.getElementById("customization-palette");
+ emptyPaletteNotice = document.getElementById("customization-empty");
+ is(emptyPaletteNotice.hidden, false,
+ "The empty palette notice should be shown when there are no items in the palette and cust. mode is re-entered.");
+
+ gCustomizeMode.removeFromArea(document.getElementById("wrapper-home-button"));
+ is(emptyPaletteNotice.hidden, true,
+ "The empty palette notice should not be shown when there is at least one item in the palette.");
+});
+
+add_task(function asyncCleanup() {
+ yield endCustomizing();
+ yield resetCustomization();
+});
--- a/browser/components/customizableui/test/browser_934951_zoom_in_toolbar.js
+++ b/browser/components/customizableui/test/browser_934951_zoom_in_toolbar.js
@@ -1,82 +1,82 @@
-/* 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/. */
-
-"use strict";
-
-const kTimeoutInMS = 20000;
-
-// Bug 934951 - Zoom controls percentage label doesn't update when it's in the toolbar and you navigate.
-add_task(function() {
- CustomizableUI.addWidgetToArea("zoom-controls", CustomizableUI.AREA_NAVBAR);
- let tab1 = gBrowser.addTab("about:mozilla");
- let tab2 = gBrowser.addTab("about:newtab");
- gBrowser.selectedTab = tab1;
- let zoomResetButton = document.getElementById("zoom-reset-button");
-
- registerCleanupFunction(() => {
- info("Cleaning up.");
- CustomizableUI.reset();
- gBrowser.removeTab(tab2);
- gBrowser.removeTab(tab1);
- });
-
- is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:mozilla");
- let zoomChangePromise = promiseObserverNotification("browser-fullZoom:zoomChange");
- FullZoom.enlarge();
- yield zoomChangePromise;
- is(parseInt(zoomResetButton.label, 10), 110, "Zoom is changed to 110% for about:mozilla");
-
- let tabSelectPromise = promiseTabSelect();
- gBrowser.selectedTab = tab2;
- yield tabSelectPromise;
- is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:newtab");
-
- gBrowser.selectedTab = tab1;
- let zoomResetPromise = promiseObserverNotification("browser-fullZoom:zoomReset");
- FullZoom.reset();
- yield zoomResetPromise;
- is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:mozilla");
-
- // Test zoom label updates while navigating pages in the same tab.
- FullZoom.enlarge();
- yield zoomChangePromise;
- is(parseInt(zoomResetButton.label, 10), 110, "Zoom is changed to 110% for about:mozilla");
- yield promiseTabLoadEvent(tab1, "about:home");
- is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:home");
- yield promiseTabHistoryNavigation(-1, function() {
- return parseInt(zoomResetButton.label, 10) == 110;
- });
- is(parseInt(zoomResetButton.label, 10), 110, "Zoom is still 110% for about:mozilla");
-});
-
-function promiseObserverNotification(aObserver) {
- let deferred = Promise.defer();
- function notificationCallback(e) {
- Services.obs.removeObserver(notificationCallback, aObserver, false);
- clearTimeout(timeoutId);
- deferred.resolve();
- };
- let timeoutId = setTimeout(() => {
- Services.obs.removeObserver(notificationCallback, aObserver, false);
- deferred.reject("Notification '" + aObserver + "' did not happen within 20 seconds.");
- }, kTimeoutInMS);
- Services.obs.addObserver(notificationCallback, aObserver, false);
- return deferred.promise;
-}
-
-function promiseTabSelect() {
- let deferred = Promise.defer();
- let container = window.gBrowser.tabContainer;
- let timeoutId = setTimeout(() => {
- container.removeEventListener("TabSelect", callback);
- deferred.reject("TabSelect did not happen within 20 seconds");
- }, kTimeoutInMS);
- function callback(e) {
- container.removeEventListener("TabSelect", callback);
- clearTimeout(timeoutId);
- executeSoon(deferred.resolve);
- };
- container.addEventListener("TabSelect", callback);
- return deferred.promise;
-}
+/* 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/. */
+
+"use strict";
+
+const kTimeoutInMS = 20000;
+
+// Bug 934951 - Zoom controls percentage label doesn't update when it's in the toolbar and you navigate.
+add_task(function() {
+ CustomizableUI.addWidgetToArea("zoom-controls", CustomizableUI.AREA_NAVBAR);
+ let tab1 = gBrowser.addTab("about:mozilla");
+ let tab2 = gBrowser.addTab("about:newtab");
+ gBrowser.selectedTab = tab1;
+ let zoomResetButton = document.getElementById("zoom-reset-button");
+
+ registerCleanupFunction(() => {
+ info("Cleaning up.");
+ CustomizableUI.reset();
+ gBrowser.removeTab(tab2);
+ gBrowser.removeTab(tab1);
+ });
+
+ is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:mozilla");
+ let zoomChangePromise = promiseObserverNotification("browser-fullZoom:zoomChange");
+ FullZoom.enlarge();
+ yield zoomChangePromise;
+ is(parseInt(zoomResetButton.label, 10), 110, "Zoom is changed to 110% for about:mozilla");
+
+ let tabSelectPromise = promiseTabSelect();
+ gBrowser.selectedTab = tab2;
+ yield tabSelectPromise;
+ is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:newtab");
+
+ gBrowser.selectedTab = tab1;
+ let zoomResetPromise = promiseObserverNotification("browser-fullZoom:zoomReset");
+ FullZoom.reset();
+ yield zoomResetPromise;
+ is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:mozilla");
+
+ // Test zoom label updates while navigating pages in the same tab.
+ FullZoom.enlarge();
+ yield zoomChangePromise;
+ is(parseInt(zoomResetButton.label, 10), 110, "Zoom is changed to 110% for about:mozilla");
+ yield promiseTabLoadEvent(tab1, "about:home");
+ is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:home");
+ yield promiseTabHistoryNavigation(-1, function() {
+ return parseInt(zoomResetButton.label, 10) == 110;
+ });
+ is(parseInt(zoomResetButton.label, 10), 110, "Zoom is still 110% for about:mozilla");
+});
+
+function promiseObserverNotification(aObserver) {
+ let deferred = Promise.defer();
+ function notificationCallback(e) {
+ Services.obs.removeObserver(notificationCallback, aObserver, false);
+ clearTimeout(timeoutId);
+ deferred.resolve();
+ };
+ let timeoutId = setTimeout(() => {
+ Services.obs.removeObserver(notificationCallback, aObserver, false);
+ deferred.reject("Notification '" + aObserver + "' did not happen within 20 seconds.");
+ }, kTimeoutInMS);
+ Services.obs.addObserver(notificationCallback, aObserver, false);
+ return deferred.promise;
+}
+
+function promiseTabSelect() {
+ let deferred = Promise.defer();
+ let container = window.gBrowser.tabContainer;
+ let timeoutId = setTimeout(() => {
+ container.removeEventListener("TabSelect", callback);
+ deferred.reject("TabSelect did not happen within 20 seconds");
+ }, kTimeoutInMS);
+ function callback(e) {
+ container.removeEventListener("TabSelect", callback);
+ clearTimeout(timeoutId);
+ executeSoon(deferred.resolve);
+ };
+ container.addEventListener("TabSelect", callback);
+ return deferred.promise;
+}
--- a/browser/components/customizableui/test/browser_938980_navbar_collapsed.js
+++ b/browser/components/customizableui/test/browser_938980_navbar_collapsed.js
@@ -1,122 +1,122 @@
-/* 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/. */
-
-"use strict";
-
-let bookmarksToolbar = document.getElementById("PersonalToolbar");
-let navbar = document.getElementById("nav-bar");
-let tabsToolbar = document.getElementById("TabsToolbar");
-
-// Customization reset should restore visibility to default-visible toolbars.
-add_task(function() {
- is(navbar.collapsed, false, "Test should start with navbar visible");
- setToolbarVisibility(navbar, false);
- is(navbar.collapsed, true, "navbar should be hidden now");
-
- yield resetCustomization();
-
- is(navbar.collapsed, false, "Customization reset should restore visibility to the navbar");
-});
-
-// Customization reset should restore collapsed-state to default-collapsed toolbars.
-add_task(function() {
- ok(CustomizableUI.inDefaultState, "Everything should be in its default state");
-
- is(bookmarksToolbar.collapsed, true, "Test should start with bookmarks toolbar collapsed");
- is(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should have height=0");
- isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
- is(navbar.collapsed, false, "The nav-bar should be shown by default");
-
- setToolbarVisibility(bookmarksToolbar, true);
- setToolbarVisibility(navbar, false);
- isnot(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should be visible now");
- ok(navbar.getBoundingClientRect().height <= 1, "navbar should have height=0 or 1 (due to border)");
- is(CustomizableUI.inDefaultState, false, "Should no longer be in default state");
-
- yield startCustomizing();
- gCustomizeMode.reset();
- yield waitForCondition(function() !gCustomizeMode.resetting);
- yield endCustomizing();
-
- is(bookmarksToolbar.collapsed, true, "Customization reset should restore collapsed-state to the bookmarks toolbar");
- isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
- is(bookmarksToolbar.getBoundingClientRect().height, 0, "The bookmarksToolbar should have height=0 after reset");
- ok(CustomizableUI.inDefaultState, "Everything should be back to default state");
-});
-
-// Check that the menubar will be collapsed by resetting, if the platform supports it.
-add_task(function() {
- let menubar = document.getElementById("toolbar-menubar");
- const canMenubarCollapse = CustomizableUI.isToolbarDefaultCollapsed(menubar.id);
- if (!canMenubarCollapse) {
- return;
- }
- ok(CustomizableUI.inDefaultState, "Everything should be in its default state");
-
- is(menubar.getBoundingClientRect().height, 0, "menubar should be hidden by default");
- setToolbarVisibility(menubar, true);
- isnot(menubar.getBoundingClientRect().height, 0, "menubar should be visible now");
-
- yield startCustomizing();
- gCustomizeMode.reset();
- yield waitForCondition(function() !gCustomizeMode.resetting);
-
- is(menubar.getAttribute("autohide"), "true", "The menubar should have autohide=true after reset in customization mode");
- is(menubar.getBoundingClientRect().height, 0, "The menubar should have height=0 after reset in customization mode");
-
- yield endCustomizing();
-
- is(menubar.getAttribute("autohide"), "true", "The menubar should have autohide=true after reset");
- is(menubar.getBoundingClientRect().height, 0, "The menubar should have height=0 after reset");
-});
-
-// Customization reset should restore collapsed-state to default-collapsed toolbars.
-add_task(function() {
- ok(CustomizableUI.inDefaultState, "Everything should be in its default state");
- is(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should have height=0");
- isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
-
- setToolbarVisibility(bookmarksToolbar, true);
- isnot(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should be visible now");
- is(CustomizableUI.inDefaultState, false, "Should no longer be in default state");
-
- yield startCustomizing();
-
- isnot(bookmarksToolbar.getBoundingClientRect().height, 0, "The bookmarksToolbar should be visible before reset");
- isnot(navbar.getBoundingClientRect().height, 0, "The navbar should be visible before reset");
- isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
-
- gCustomizeMode.reset();
- yield waitForCondition(function() !gCustomizeMode.resetting);
-
- is(bookmarksToolbar.getBoundingClientRect().height, 0, "The bookmarksToolbar should have height=0 after reset");
- isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
- isnot(navbar.getBoundingClientRect().height, 0, "The navbar should still be visible after reset");
- ok(CustomizableUI.inDefaultState, "Everything should be back to default state");
- yield endCustomizing();
-});
-
-// Check that the menubar will be collapsed by resetting, if the platform supports it.
-add_task(function() {
- let menubar = document.getElementById("toolbar-menubar");
- const canMenubarCollapse = CustomizableUI.isToolbarDefaultCollapsed(menubar.id);
- if (!canMenubarCollapse) {
- return;
- }
- ok(CustomizableUI.inDefaultState, "Everything should be in its default state");
- yield startCustomizing();
- let resetButton = document.getElementById("customization-reset-button");
- is(resetButton.disabled, true, "The reset button should be disabled when in default state");
-
- setToolbarVisibility(menubar, true);
- is(resetButton.disabled, false, "The reset button should be enabled when not in default state")
- ok(!CustomizableUI.inDefaultState, "No longer in default state when the menubar is shown");
-
- yield gCustomizeMode.reset();
-
- is(resetButton.disabled, true, "The reset button should be disabled when in default state");
- ok(CustomizableUI.inDefaultState, "Everything should be in its default state");
-
- yield endCustomizing();
-});
+/* 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/. */
+
+"use strict";
+
+let bookmarksToolbar = document.getElementById("PersonalToolbar");
+let navbar = document.getElementById("nav-bar");
+let tabsToolbar = document.getElementById("TabsToolbar");
+
+// Customization reset should restore visibility to default-visible toolbars.
+add_task(function() {
+ is(navbar.collapsed, false, "Test should start with navbar visible");
+ setToolbarVisibility(navbar, false);
+ is(navbar.collapsed, true, "navbar should be hidden now");
+
+ yield resetCustomization();
+
+ is(navbar.collapsed, false, "Customization reset should restore visibility to the navbar");
+});
+
+// Customization reset should restore collapsed-state to default-collapsed toolbars.
+add_task(function() {
+ ok(CustomizableUI.inDefaultState, "Everything should be in its default state");
+
+ is(bookmarksToolbar.collapsed, true, "Test should start with bookmarks toolbar collapsed");
+ is(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should have height=0");
+ isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
+ is(navbar.collapsed, false, "The nav-bar should be shown by default");
+
+ setToolbarVisibility(bookmarksToolbar, true);
+ setToolbarVisibility(navbar, false);
+ isnot(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should be visible now");
+ ok(navbar.getBoundingClientRect().height <= 1, "navbar should have height=0 or 1 (due to border)");
+ is(CustomizableUI.inDefaultState, false, "Should no longer be in default state");
+
+ yield startCustomizing();
+ gCustomizeMode.reset();
+ yield waitForCondition(function() !gCustomizeMode.resetting);
+ yield endCustomizing();
+
+ is(bookmarksToolbar.collapsed, true, "Customization reset should restore collapsed-state to the bookmarks toolbar");
+ isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
+ is(bookmarksToolbar.getBoundingClientRect().height, 0, "The bookmarksToolbar should have height=0 after reset");
+ ok(CustomizableUI.inDefaultState, "Everything should be back to default state");
+});
+
+// Check that the menubar will be collapsed by resetting, if the platform supports it.
+add_task(function() {
+ let menubar = document.getElementById("toolbar-menubar");
+ const canMenubarCollapse = CustomizableUI.isToolbarDefaultCollapsed(menubar.id);
+ if (!canMenubarCollapse) {
+ return;
+ }
+ ok(CustomizableUI.inDefaultState, "Everything should be in its default state");
+
+ is(menubar.getBoundingClientRect().height, 0, "menubar should be hidden by default");
+ setToolbarVisibility(menubar, true);
+ isnot(menubar.getBoundingClientRect().height, 0, "menubar should be visible now");
+
+ yield startCustomizing();
+ gCustomizeMode.reset();
+ yield waitForCondition(function() !gCustomizeMode.resetting);
+
+ is(menubar.getAttribute("autohide"), "true", "The menubar should have autohide=true after reset in customization mode");
+ is(menubar.getBoundingClientRect().height, 0, "The menubar should have height=0 after reset in customization mode");
+
+ yield endCustomizing();
+
+ is(menubar.getAttribute("autohide"), "true", "The menubar should have autohide=true after reset");
+ is(menubar.getBoundingClientRect().height, 0, "The menubar should have height=0 after reset");
+});
+
+// Customization reset should restore collapsed-state to default-collapsed toolbars.
+add_task(function() {
+ ok(CustomizableUI.inDefaultState, "Everything should be in its default state");
+ is(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should have height=0");
+ isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
+
+ setToolbarVisibility(bookmarksToolbar, true);
+ isnot(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should be visible now");
+ is(CustomizableUI.inDefaultState, false, "Should no longer be in default state");
+
+ yield startCustomizing();
+
+ isnot(bookmarksToolbar.getBoundingClientRect().height, 0, "The bookmarksToolbar should be visible before reset");
+ isnot(navbar.getBoundingClientRect().height, 0, "The navbar should be visible before reset");
+ isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
+
+ gCustomizeMode.reset();
+ yield waitForCondition(function() !gCustomizeMode.resetting);
+
+ is(bookmarksToolbar.getBoundingClientRect().height, 0, "The bookmarksToolbar should have height=0 after reset");
+ isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
+ isnot(navbar.getBoundingClientRect().height, 0, "The navbar should still be visible after reset");
+ ok(CustomizableUI.inDefaultState, "Everything should be back to default state");
+ yield endCustomizing();
+});
+
+// Check that the menubar will be collapsed by resetting, if the platform supports it.
+add_task(function() {
+ let menubar = document.getElementById("toolbar-menubar");
+ const canMenubarCollapse = CustomizableUI.isToolbarDefaultCollapsed(menubar.id);
+ if (!canMenubarCollapse) {
+ return;
+ }
+ ok(CustomizableUI.inDefaultState, "Everything should be in its default state");
+ yield startCustomizing();
+ let resetButton = document.getElementById("customization-reset-button");
+ is(resetButton.disabled, true, "The reset button should be disabled when in default state");
+
+ setToolbarVisibility(menubar, true);
+ is(resetButton.disabled, false, "The reset button should be enabled when not in default state")
+ ok(!CustomizableUI.inDefaultState, "No longer in default state when the menubar is shown");
+
+ yield gCustomizeMode.reset();
+
+ is(resetButton.disabled, true, "The reset button should be disabled when in default state");
+ ok(CustomizableUI.inDefaultState, "Everything should be in its default state");
+
+ yield endCustomizing();
+});
--- a/browser/components/customizableui/test/browser_940107_home_button_in_bookmarks_toolbar.js
+++ b/browser/components/customizableui/test/browser_940107_home_button_in_bookmarks_toolbar.js
@@ -1,41 +1,41 @@
-/* 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/. */
-
-"use strict";
-
-// Bug 940107 - Home icon not displayed correctly when in bookmarks toolbar.
-add_task(function() {
- ok(CustomizableUI.inDefaultState, "Should be in default state when test starts.");
- let bookmarksToolbar = document.getElementById(CustomizableUI.AREA_BOOKMARKS);
- bookmarksToolbar.collapsed = false;
-
- let homeButton = document.getElementById("home-button");
- ok(homeButton.classList.contains("toolbarbutton-1"), "Home Button should have toolbarbutton-1 when in the nav-bar");
- ok(!homeButton.classList.contains("bookmark-item"), "Home Button should not be displayed as a bookmarks item");
-
- yield startCustomizing();
- CustomizableUI.addWidgetToArea(homeButton.id, CustomizableUI.AREA_BOOKMARKS);
- yield endCustomizing();
- ok(homeButton.classList.contains("bookmark-item"), "Home Button should be displayed as a bookmarks item");
- ok(!homeButton.classList.contains("toolbarbutton-1"), "Home Button should not be displayed as a nav-bar item");
-
- gCustomizeMode.addToPanel(homeButton);
- let panelShownPromise = promisePanelShown(window);
- PanelUI.toggle();
- yield panelShownPromise;
-
- ok(homeButton.classList.contains("toolbarbutton-1"), "Home Button should have toolbarbutton-1 when in the panel");
- ok(!homeButton.classList.contains("bookmark-item"), "Home Button should not be displayed as a bookmarks item");
-
- gCustomizeMode.addToToolbar(homeButton);
- let panelHiddenPromise = promisePanelHidden(window);
- PanelUI.toggle();
- yield panelHiddenPromise;
-
- ok(homeButton.classList.contains("toolbarbutton-1"), "Home Button should have toolbarbutton-1 when in the nav-bar");
- ok(!homeButton.classList.contains("bookmark-item"), "Home Button should not be displayed as a bookmarks item");
-
- bookmarksToolbar.collapsed = true;
- CustomizableUI.reset();
-});
+/* 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/. */
+
+"use strict";
+
+// Bug 940107 - Home icon not displayed correctly when in bookmarks toolbar.
+add_task(function() {
+ ok(CustomizableUI.inDefaultState, "Should be in default state when test starts.");
+ let bookmarksToolbar = document.getElementById(CustomizableUI.AREA_BOOKMARKS);
+ bookmarksToolbar.collapsed = false;
+
+ let homeButton = document.getElementById("home-button");
+ ok(homeButton.classList.contains("toolbarbutton-1"), "Home Button should have toolbarbutton-1 when in the nav-bar");
+ ok(!homeButton.classList.contains("bookmark-item"), "Home Button should not be displayed as a bookmarks item");
+
+ yield startCustomizing();
+ CustomizableUI.addWidgetToArea(homeButton.id, CustomizableUI.AREA_BOOKMARKS);
+ yield endCustomizing();
+ ok(homeButton.classList.contains("bookmark-item"), "Home Button should be displayed as a bookmarks item");
+ ok(!homeButton.classList.contains("toolbarbutton-1"), "Home Button should not be displayed as a nav-bar item");
+
+ gCustomizeMode.addToPanel(homeButton);
+ let panelShownPromise = promisePanelShown(window);
+ PanelUI.toggle();
+ yield panelShownPromise;
+
+ ok(homeButton.classList.contains("toolbarbutton-1"), "Home Button should have toolbarbutton-1 when in the panel");
+ ok(!homeButton.classList.contains("bookmark-item"), "Home Button should not be displayed as a bookmarks item");
+
+ gCustomizeMode.addToToolbar(homeButton);
+ let panelHiddenPromise = promisePanelHidden(window);
+ PanelUI.toggle();
+ yield panelHiddenPromise;
+
+ ok(homeButton.classList.contains("toolbarbutton-1"), "Home Button should have toolbarbutton-1 when in the nav-bar");
+ ok(!homeButton.classList.contains("bookmark-item"), "Home Button should not be displayed as a bookmarks item");
+
+ bookmarksToolbar.collapsed = true;
+ CustomizableUI.reset();
+});
--- a/browser/components/customizableui/test/browser_962069_drag_to_overflow_chevron.js
+++ b/browser/components/customizableui/test/browser_962069_drag_to_overflow_chevron.js
@@ -1,43 +1,43 @@
-/* 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/. */
-
-"use strict";
-
-let originalWindowWidth;
-
-// Drag to overflow chevron should open the overflow panel.
-add_task(function*() {
- originalWindowWidth = window.outerWidth;
- let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
- ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
- ok(CustomizableUI.inDefaultState, "Should start in default state.");
- let oldChildCount = navbar.customizationTarget.childElementCount;
- window.resizeTo(400, window.outerHeight);
- yield waitForCondition(() => navbar.hasAttribute("overflowing"));
- ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
-
- let widgetOverflowPanel = document.getElementById("widget-overflow");
- let panelShownPromise = promisePanelElementShown(window, widgetOverflowPanel);
- let identityBox = document.getElementById("identity-box");
- let overflowChevron = document.getElementById("nav-bar-overflow-button");
-
- // Listen for hiding immediately so we don't miss the event because of the
- // async-ness of the 'shown' yield...
- let panelHiddenPromise = promisePanelElementHidden(window, widgetOverflowPanel);
-
- ChromeUtils.synthesizeDrop(identityBox, overflowChevron, [], null);
- yield panelShownPromise;
-
- info("Overflow panel is shown.");
-
- widgetOverflowPanel.hidePopup();
- yield panelHiddenPromise;
-});
-
-add_task(function*() {
- window.resizeTo(originalWindowWidth, window.outerHeight);
- let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
- yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
- ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
-});
+/* 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/. */
+
+"use strict";
+
+let originalWindowWidth;
+
+// Drag to overflow chevron should open the overflow panel.
+add_task(function*() {
+ originalWindowWidth = window.outerWidth;
+ let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+ ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
+ ok(CustomizableUI.inDefaultState, "Should start in default state.");
+ let oldChildCount = navbar.customizationTarget.childElementCount;
+ window.resizeTo(400, window.outerHeight);
+ yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+ ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+
+ let widgetOverflowPanel = document.getElementById("widget-overflow");
+ let panelShownPromise = promisePanelElementShown(window, widgetOverflowPanel);
+ let identityBox = document.getElementById("identity-box");
+ let overflowChevron = document.getElementById("nav-bar-overflow-button");
+
+ // Listen for hiding immediately so we don't miss the event because of the
+ // async-ness of the 'shown' yield...
+ let panelHiddenPromise = promisePanelElementHidden(window, widgetOverflowPanel);
+
+ ChromeUtils.synthesizeDrop(identityBox, overflowChevron, [], null);
+ yield panelShownPromise;
+
+ info("Overflow panel is shown.");
+
+ widgetOverflowPanel.hidePopup();
+ yield panelHiddenPromise;
+});
+
+add_task(function*() {
+ window.resizeTo(originalWindowWidth, window.outerHeight);
+ let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+ yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+ ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
+});
--- a/browser/components/customizableui/test/browser_962884_opt_in_disable_hyphens.js
+++ b/browser/components/customizableui/test/browser_962884_opt_in_disable_hyphens.js
@@ -1,67 +1,67 @@
-/* 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/. */
-
-"use strict";
-
-add_task(function() {
- const kNormalLabel = "Character Encoding";
- CustomizableUI.addWidgetToArea("characterencoding-button", CustomizableUI.AREA_NAVBAR);
- let characterEncoding = document.getElementById("characterencoding-button");
- const kOriginalLabel = characterEncoding.getAttribute("label");
- characterEncoding.setAttribute("label", "\u00ad" + kNormalLabel);
- CustomizableUI.addWidgetToArea("characterencoding-button", CustomizableUI.AREA_PANEL);
-
- yield PanelUI.show();
-
- is(characterEncoding.getAttribute("auto-hyphens"), "off",
- "Hyphens should be disabled if the ­ character is present in the label");
- let multilineText = document.getAnonymousElementByAttribute(characterEncoding, "class", "toolbarbutton-multiline-text");
- let multilineTextCS = getComputedStyle(multilineText);
- is(multilineTextCS.MozHyphens, "manual", "-moz-hyphens should be set to manual when the ­ character is present.")
-
- let hiddenPanelPromise = promisePanelHidden(window);
- PanelUI.toggle();
- yield hiddenPanelPromise;
-
- characterEncoding.setAttribute("label", kNormalLabel);
-
- yield PanelUI.show();
-
- isnot(characterEncoding.getAttribute("auto-hyphens"), "off",
- "Hyphens should not be disabled if the ­ character is not present in the label");
- multilineText = document.getAnonymousElementByAttribute(characterEncoding, "class", "toolbarbutton-multiline-text");
- let multilineTextCS = getComputedStyle(multilineText);
- is(multilineTextCS.MozHyphens, "auto", "-moz-hyphens should be set to auto by default.")
-
- hiddenPanelPromise = promisePanelHidden(window);
- PanelUI.toggle();
- yield hiddenPanelPromise;
-
- characterEncoding.setAttribute("label", "\u00ad" + kNormalLabel);
- CustomizableUI.removeWidgetFromArea("characterencoding-button");
- yield startCustomizing();
-
- isnot(characterEncoding.getAttribute("auto-hyphens"), "off",
- "Hyphens should not be disabled when the widget is in the palette");
-
- gCustomizeMode.addToPanel(characterEncoding);
- is(characterEncoding.getAttribute("auto-hyphens"), "off",
- "Hyphens should be disabled if the ­ character is present in the label in customization mode");
- let multilineText = document.getAnonymousElementByAttribute(characterEncoding, "class", "toolbarbutton-multiline-text");
- let multilineTextCS = getComputedStyle(multilineText);
- is(multilineTextCS.MozHyphens, "manual", "-moz-hyphens should be set to manual when the ­ character is present in customization mode.")
-
- yield endCustomizing();
-
- CustomizableUI.addWidgetToArea("characterencoding-button", CustomizableUI.AREA_NAVBAR);
- ok(!characterEncoding.hasAttribute("auto-hyphens"),
- "Removing the widget from the panel should remove the auto-hyphens attribute");
-
- characterEncoding.setAttribute("label", kOriginalLabel);
-});
-
-add_task(function asyncCleanup() {
- yield endCustomizing();
- yield resetCustomization();
-});
+/* 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/. */
+
+"use strict";
+
+add_task(function() {
+ const kNormalLabel = "Character Encoding";
+ CustomizableUI.addWidgetToArea("characterencoding-button", CustomizableUI.AREA_NAVBAR);
+ let characterEncoding = document.getElementById("characterencoding-button");
+ const kOriginalLabel = characterEncoding.getAttribute("label");
+ characterEncoding.setAttribute("label", "\u00ad" + kNormalLabel);
+ CustomizableUI.addWidgetToArea("characterencoding-button", CustomizableUI.AREA_PANEL);
+
+ yield PanelUI.show();
+
+ is(characterEncoding.getAttribute("auto-hyphens"), "off",
+ "Hyphens should be disabled if the ­ character is present in the label");
+ let multilineText = document.getAnonymousElementByAttribute(characterEncoding, "class", "toolbarbutton-multiline-text");
+ let multilineTextCS = getComputedStyle(multilineText);
+ is(multilineTextCS.MozHyphens, "manual", "-moz-hyphens should be set to manual when the ­ character is present.")
+
+ let hiddenPanelPromise = promisePanelHidden(window);
+ PanelUI.toggle();
+ yield hiddenPanelPromise;
+
+ characterEncoding.setAttribute("label", kNormalLabel);
+
+ yield PanelUI.show();
+
+ isnot(characterEncoding.getAttribute("auto-hyphens"), "off",
+ "Hyphens should not be disabled if the ­ character is not present in the label");
+ multilineText = document.getAnonymousElementByAttribute(characterEncoding, "class", "toolbarbutton-multiline-text");
+ let multilineTextCS = getComputedStyle(multilineText);
+ is(multilineTextCS.MozHyphens, "auto", "-moz-hyphens should be set to auto by default.")
+
+ hiddenPanelPromise = promisePanelHidden(window);
+ PanelUI.toggle();
+ yield hiddenPanelPromise;
+
+ characterEncoding.setAttribute("label", "\u00ad" + kNormalLabel);
+ CustomizableUI.removeWidgetFromArea("characterencoding-button");
+ yield startCustomizing();
+
+ isnot(characterEncoding.getAttribute("auto-hyphens"), "off",
+ "Hyphens should not be disabled when the widget is in the palette");
+
+ gCustomizeMode.addToPanel(characterEncoding);
+ is(characterEncoding.getAttribute("auto-hyphens"), "off",
+ "Hyphens should be disabled if the ­ character is present in the label in customization mode");
+ let multilineText = document.getAnonymousElementByAttribute(characterEncoding, "class", "toolbarbutton-multiline-text");
+ let multilineTextCS = getComputedStyle(multilineText);
+ is(multilineTextCS.MozHyphens, "manual", "-moz-hyphens should be set to manual when the ­ character is present in customization mode.")
+
+ yield endCustomizing();
+
+ CustomizableUI.addWidgetToArea("characterencoding-button", CustomizableUI.AREA_NAVBAR);
+ ok(!characterEncoding.hasAttribute("auto-hyphens"),
+ "Removing the widget from the panel should remove the auto-hyphens attribute");
+
+ characterEncoding.setAttribute("label", kOriginalLabel);
+});
+
+add_task(function asyncCleanup() {
+ yield endCustomizing();
+ yield resetCustomization();
+});
--- a/browser/components/customizableui/test/browser_968447_bookmarks_toolbar_items_in_panel.js
+++ b/browser/components/customizableui/test/browser_968447_bookmarks_toolbar_items_in_panel.js
@@ -1,65 +1,65 @@
-/* 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/. */
-
-"use strict";
-
-// Bug 968447 - The Bookmarks Toolbar Items doesn't appear as a
-// normal menu panel button in new windows.
-add_task(function() {
- const buttonId = "bookmarks-toolbar-placeholder";
- yield startCustomizing();
- CustomizableUI.addWidgetToArea("personal-bookmarks", CustomizableUI.AREA_PANEL);
- yield endCustomizing();
-
- yield PanelUI.show();
-
- let bookmarksToolbarPlaceholder = document.getElementById(buttonId);
- ok(bookmarksToolbarPlaceholder.classList.contains("toolbarbutton-1"),
- "Button should have toolbarbutton-1 class");
- is(bookmarksToolbarPlaceholder.getAttribute("wrap"), "true",
- "Button should have the 'wrap' attribute");
-
- info("Waiting for panel to close");
- let panelHiddenPromise = promisePanelHidden(window);
- PanelUI.hide();
- yield panelHiddenPromise;
-
- info("Waiting for window to open");
- let newWin = yield openAndLoadWindow({}, true);
-
- info("Waiting for panel in new window to open");
- let hideTrace = function() {
- info(new Error().stack);
- info("Panel was hidden.");
- };
- newWin.PanelUI.panel.addEventListener("popuphidden", hideTrace);
-
- yield newWin.PanelUI.show();
- let newWinBookmarksToolbarPlaceholder = newWin.document.getElementById(buttonId);
- ok(newWinBookmarksToolbarPlaceholder.classList.contains("toolbarbutton-1"),
- "Button in new window should have toolbarbutton-1 class");
- is(newWinBookmarksToolbarPlaceholder.getAttribute("wrap"), "true",
- "Button in new window should have 'wrap' attribute");
-
- newWin.PanelUI.panel.removeEventListener("popuphidden", hideTrace);
- //XXXgijs on Linux, we're sometimes seeing the panel being hidden early
- // in the newly created window, probably because something else steals focus.
- if (newWin.PanelUI.panel.state != "closed") {
- info("Panel is still open in new window, waiting for it to close");
- panelHiddenPromise = promisePanelHidden(newWin);
- newWin.PanelUI.hide();
- yield panelHiddenPromise;
- } else {
- info("panel was already closed");
- }
-
- info("Waiting for new window to close");
- yield promiseWindowClosed(newWin);
-});
-
-add_task(function asyncCleanUp() {
- yield endCustomizing();
- CustomizableUI.reset();
-});
-
+/* 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/. */
+
+"use strict";
+
+// Bug 968447 - The Bookmarks Toolbar Items doesn't appear as a
+// normal menu panel button in new windows.
+add_task(function() {
+ const buttonId = "bookmarks-toolbar-placeholder";
+ yield startCustomizing();
+ CustomizableUI.addWidgetToArea("personal-bookmarks", CustomizableUI.AREA_PANEL);
+ yield endCustomizing();
+
+ yield PanelUI.show();
+
+ let bookmarksToolbarPlaceholder = document.getElementById(buttonId);
+ ok(bookmarksToolbarPlaceholder.classList.contains("toolbarbutton-1"),
+ "Button should have toolbarbutton-1 class");
+ is(bookmarksToolbarPlaceholder.getAttribute("wrap"), "true",
+ "Button should have the 'wrap' attribute");
+
+ info("Waiting for panel to close");
+ let panelHiddenPromise = promisePanelHidden(window);
+ PanelUI.hide();
+ yield panelHiddenPromise;
+
+ info("Waiting for window to open");
+ let newWin = yield openAndLoadWindow({}, true);
+
+ info("Waiting for panel in new window to open");
+ let hideTrace = function() {
+ info(new Error().stack);
+ info("Panel was hidden.");
+ };
+ newWin.PanelUI.panel.addEventListener("popuphidden", hideTrace);
+
+ yield newWin.PanelUI.show();
+ let newWinBookmarksToolbarPlaceholder = newWin.document.getElementById(buttonId);
+ ok(newWinBookmarksToolbarPlaceholder.classList.contains("toolbarbutton-1"),
+ "Button in new window should have toolbarbutton-1 class");
+ is(newWinBookmarksToolbarPlaceholder.getAttribute("wrap"), "true",
+ "Button in new window should have 'wrap' attribute");
+
+ newWin.PanelUI.panel.removeEventListener("popuphidden", hideTrace);
+ //XXXgijs on Linux, we're sometimes seeing the panel being hidden early
+ // in the newly created window, probably because something else steals focus.
+ if (newWin.PanelUI.panel.state != "closed") {
+ info("Panel is still open in new window, waiting for it to close");
+ panelHiddenPromise = promisePanelHidden(newWin);
+ newWin.PanelUI.hide();
+ yield panelHiddenPromise;
+ } else {
+ info("panel was already closed");
+ }
+
+ info("Waiting for new window to close");
+ yield promiseWindowClosed(newWin);
+});
+
+add_task(function asyncCleanUp() {
+ yield endCustomizing();
+ CustomizableUI.reset();
+});
+
--- a/browser/components/customizableui/test/browser_970511_undo_restore_default.js
+++ b/browser/components/customizableui/test/browser_970511_undo_restore_default.js
@@ -1,107 +1,107 @@
-/* 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/. */
-
-"use strict";
-
-// Restoring default should show an "undo" option which undoes the restoring operation.
-add_task(function() {
- let homeButtonId = "home-button";
- CustomizableUI.removeWidgetFromArea(homeButtonId);
- yield startCustomizing();
- ok(!CustomizableUI.inDefaultState, "Not in default state to begin with");
- is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
- let undoResetButton = document.getElementById("customization-undo-reset-button");
- is(undoResetButton.hidden, true, "The undo button is hidden before reset");
-
- yield gCustomizeMode.reset();
-
- ok(CustomizableUI.inDefaultState, "In default state after reset");
- is(undoResetButton.hidden, false, "The undo button is visible after reset");
-
- undoResetButton.click();
- yield waitForCondition(function() !gCustomizeMode.resetting);
- ok(!CustomizableUI.inDefaultState, "Not in default state after reset-undo");
- is(undoResetButton.hidden, true, "The undo button is hidden after clicking on the undo button");
- is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
-
- yield gCustomizeMode.reset();
-});
-
-// Performing an action after a reset will hide the reset button.
-add_task(function() {
- let homeButtonId = "home-button";
- CustomizableUI.removeWidgetFromArea(homeButtonId);
- ok(!CustomizableUI.inDefaultState, "Not in default state to begin with");
- is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
- let undoResetButton = document.getElementById("customization-undo-reset-button");
- is(undoResetButton.hidden, true, "The undo button is hidden before reset");
-
- yield gCustomizeMode.reset();
-
- ok(CustomizableUI.inDefaultState, "In default state after reset");
- is(undoResetButton.hidden, false, "The undo button is visible after reset");
-
- CustomizableUI.addWidgetToArea(homeButtonId, CustomizableUI.AREA_PANEL);
- is(undoResetButton.hidden, true, "The undo button is hidden after another change");
-});
-
-// "Restore defaults", exiting customize, and re-entering shouldn't show the Undo button
-add_task(function() {
- let undoResetButton = document.getElementById("customization-undo-reset-button");
- is(undoResetButton.hidden, true, "The undo button is hidden before a reset");
- ok(!CustomizableUI.inDefaultState, "The browser should not be in default state");
- yield gCustomizeMode.reset();
-
- is(undoResetButton.hidden, false, "The undo button is visible after a reset");
- yield endCustomizing();
- yield startCustomizing();
- is(undoResetButton.hidden, true, "The undo reset button should be hidden after entering customization mode");
-});
-
-// Bug 971626 - Restore Defaults should collapse the Title Bar
-add_task(function() {
- if (Services.appinfo.OS != "WINNT" &&
- Services.appinfo.OS != "Darwin") {
- return;
- }
- let prefName = "browser.tabs.drawInTitlebar";
- let defaultValue = Services.prefs.getBoolPref(prefName);
- let restoreDefaultsButton = document.getElementById("customization-reset-button");
- let titleBarButton = document.getElementById("customization-titlebar-visibility-button");
- let undoResetButton = document.getElementById("customization-undo-reset-button");
- ok(CustomizableUI.inDefaultState, "Should be in default state at start of test");
- ok(restoreDefaultsButton.disabled, "Restore defaults button should be disabled when in default state");
- is(titleBarButton.hasAttribute("checked"), !defaultValue, "Title bar button should reflect pref value");
- is(undoResetButton.hidden, true, "Undo reset button should be hidden at start of test");
-
- Services.prefs.setBoolPref(prefName, !defaultValue);
- ok(!restoreDefaultsButton.disabled, "Restore defaults button should be enabled when pref changed");
- is(titleBarButton.hasAttribute("checked"), defaultValue, "Title bar button should reflect changed pref value");
- ok(!CustomizableUI.inDefaultState, "With titlebar flipped, no longer default");
- is(undoResetButton.hidden, true, "Undo reset button should be hidden after pref change");
-
- yield gCustomizeMode.reset();
- ok(restoreDefaultsButton.disabled, "Restore defaults button should be disabled after reset");
- is(titleBarButton.hasAttribute("checked"), !defaultValue, "Title bar button should reflect default value after reset");
- is(Services.prefs.getBoolPref(prefName), defaultValue, "Reset should reset drawInTitlebar");
- ok(CustomizableUI.inDefaultState, "In default state after titlebar reset");
- is(undoResetButton.hidden, false, "Undo reset button should be visible after reset");
- ok(!undoResetButton.disabled, "Undo reset button should be enabled after reset");
-
- yield gCustomizeMode.undoReset();
- ok(!restoreDefaultsButton.disabled, "Restore defaults button should be enabled after undo-reset");
- is(titleBarButton.hasAttribute("checked"), defaultValue, "Title bar button should reflect undo-reset value");
- ok(!CustomizableUI.inDefaultState, "No longer in default state after undo");
- is(Services.prefs.getBoolPref(prefName), !defaultValue, "Undo-reset goes back to previous pref value");
- is(undoResetButton.hidden, true, "Undo reset button should be hidden after undo-reset clicked");
-
- Services.prefs.clearUserPref(prefName);
- ok(CustomizableUI.inDefaultState, "In default state after pref cleared");
- is(undoResetButton.hidden, true, "Undo reset button should be hidden at end of test");
-});
-
-add_task(function asyncCleanup() {
- yield gCustomizeMode.reset();
- yield endCustomizing();
-});
+/* 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/. */
+
+"use strict";
+
+// Restoring default should show an "undo" option which undoes the restoring operation.
+add_task(function() {
+ let homeButtonId = "home-button";
+ CustomizableUI.removeWidgetFromArea(homeButtonId);
+ yield startCustomizing();
+ ok(!CustomizableUI.inDefaultState, "Not in default state to begin with");
+ is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
+ let undoResetButton = document.getElementById("customization-undo-reset-button");
+ is(undoResetButton.hidden, true, "The undo button is hidden before reset");
+
+ yield gCustomizeMode.reset();
+
+ ok(CustomizableUI.inDefaultState, "In default state after reset");
+ is(undoResetButton.hidden, false, "The undo button is visible after reset");
+
+ undoResetButton.click();
+ yield waitForCondition(function() !gCustomizeMode.resetting);
+ ok(!CustomizableUI.inDefaultState, "Not in default state after reset-undo");
+ is(undoResetButton.hidden, true, "The undo button is hidden after clicking on the undo button");
+ is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
+
+ yield gCustomizeMode.reset();
+});
+
+// Performing an action after a reset will hide the reset button.
+add_task(function() {
+ let homeButtonId = "home-button";
+ CustomizableUI.removeWidgetFromArea(homeButtonId);
+ ok(!CustomizableUI.inDefaultState, "Not in default state to begin with");
+ is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
+ let undoResetButton = document.getElementById("customization-undo-reset-button");
+ is(undoResetButton.hidden, true, "The undo button is hidden before reset");
+
+ yield gCustomizeMode.reset();
+
+ ok(CustomizableUI.inDefaultState, "In default state after reset");
+ is(undoResetButton.hidden, false, "The undo button is visible after reset");
+
+ CustomizableUI.addWidgetToArea(homeButtonId, CustomizableUI.AREA_PANEL);
+ is(undoResetButton.hidden, true, "The undo button is hidden after another change");
+});
+
+// "Restore defaults", exiting customize, and re-entering shouldn't show the Undo button
+add_task(function() {
+ let undoResetButton = document.getElementById("customization-undo-reset-button");
+ is(undoResetButton.hidden, true, "The undo button is hidden before a reset");
+ ok(!CustomizableUI.inDefaultState, "The browser should not be in default state");
+ yield gCustomizeMode.reset();
+
+ is(undoResetButton.hidden, false, "The undo button is visible after a reset");
+ yield endCustomizing();
+ yield startCustomizing();
+ is(undoResetButton.hidden, true, "The undo reset button should be hidden after entering customization mode");
+});
+
+// Bug 971626 - Restore Defaults should collapse the Title Bar
+add_task(function() {
+ if (Services.appinfo.OS != "WINNT" &&
+ Services.appinfo.OS != "Darwin") {
+ return;
+ }
+ let prefName = "browser.tabs.drawInTitlebar";
+ let defaultValue = Services.prefs.getBoolPref(prefName);
+ let restoreDefaultsButton = document.getElementById("customization-reset-button");
+ let titleBarButton = document.getElementById("customization-titlebar-visibility-button");
+ let undoResetButton = document.getElementById("customization-undo-reset-button");
+ ok(CustomizableUI.inDefaultState, "Should be in default state at start of test");
+ ok(restoreDefaultsButton.disabled, "Restore defaults button should be disabled when in default state");
+ is(titleBarButton.hasAttribute("checked"), !defaultValue, "Title bar button should reflect pref value");
+ is(undoResetButton.hidden, true, "Undo reset button should be hidden at start of test");
+
+ Services.prefs.setBoolPref(prefName, !defaultValue);
+ ok(!restoreDefaultsButton.disabled, "Restore defaults button should be enabled when pref changed");
+ is(titleBarButton.hasAttribute("checked"), defaultValue, "Title bar button should reflect changed pref value");
+ ok(!CustomizableUI.inDefaultState, "With titlebar flipped, no longer default");
+ is(undoResetButton.hidden, true, "Undo reset button should be hidden after pref change");
+
+ yield gCustomizeMode.reset();
+ ok(restoreDefaultsButton.disabled, "Restore defaults button should be disabled after reset");
+ is(titleBarButton.hasAttribute("checked"), !defaultValue, "Title bar button should reflect default value after reset");
+ is(Services.prefs.getBoolPref(prefName), defaultValue, "Reset should reset drawInTitlebar");
+ ok(CustomizableUI.inDefaultState, "In default state after titlebar reset");
+ is(undoResetButton.hidden, false, "Undo reset button should be visible after reset");
+ ok(!undoResetButton.disabled, "Undo reset button should be enabled after reset");
+
+ yield gCustomizeMode.undoReset();
+ ok(!restoreDefaultsButton.disabled, "Restore defaults button should be enabled after undo-reset");
+ is(titleBarButton.hasAttribute("checked"), defaultValue, "Title bar button should reflect undo-reset value");
+ ok(!CustomizableUI.inDefaultState, "No longer in default state after undo");
+ is(Services.prefs.getBoolPref(prefName), !defaultValue, "Undo-reset goes back to previous pref value");
+ is(undoResetButton.hidden, true, "Undo reset button should be hidden after undo-reset clicked");
+
+ Services.prefs.clearUserPref(prefName);
+ ok(CustomizableUI.inDefaultState, "In default state after pref cleared");
+ is(undoResetButton.hidden, true, "Undo reset button should be hidden at end of test");
+});
+
+add_task(function asyncCleanup() {
+ yield gCustomizeMode.reset();
+ yield endCustomizing();
+});
--- a/browser/components/customizableui/test/browser_980155_add_overflow_toolbar.js
+++ b/browser/components/customizableui/test/browser_980155_add_overflow_toolbar.js
@@ -1,51 +1,51 @@
-/* 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/. */
-
-"use strict";
-
-const kToolbarName = "test-new-overflowable-toolbar";
-const kTestWidgetPrefix = "test-widget-for-overflowable-toolbar-";
-
-add_task(function addOverflowingToolbar() {
- let originalWindowWidth = window.outerWidth;
-
- let widgetIds = [];
- for (let i = 0; i < 10; i++) {
- let id = kTestWidgetPrefix + i;
- widgetIds.push(id);
- let spec = {id: id, type: "button", removable: true, label: "test", tooltiptext: "" + i};
- CustomizableUI.createWidget(spec);
- }
-
- let toolbarNode = createOverflowableToolbarWithPlacements(kToolbarName, widgetIds);
- assertAreaPlacements(kToolbarName, widgetIds);
-
- for (let id of widgetIds) {
- document.getElementById(id).style.minWidth = "200px";
- }
-
- isnot(toolbarNode.overflowable, null, "Toolbar should have overflowable controller");
- isnot(toolbarNode.customizationTarget, null, "Toolbar should have customization target");
- isnot(toolbarNode.customizationTarget, toolbarNode, "Customization target should not be toolbar node");
-
- let oldChildCount = toolbarNode.customizationTarget.childElementCount;
- let overflowableList = document.getElementById(kToolbarName + "-overflow-list");
- let oldOverflowCount = overflowableList.childElementCount;
-
- isnot(oldChildCount, 0, "Toolbar should have non-overflowing widgets");
-
- window.resizeTo(400, window.outerHeight);
- yield waitForCondition(() => toolbarNode.hasAttribute("overflowing"));
- ok(toolbarNode.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
- ok(toolbarNode.customizationTarget.childElementCount < oldChildCount, "Should have fewer children.");
- ok(overflowableList.childElementCount > oldOverflowCount, "Should have more overflowed widgets.");
-
- window.resizeTo(originalWindowWidth, window.outerHeight);
-});
-
-
-add_task(function asyncCleanup() {
- removeCustomToolbars();
- yield resetCustomization();
-});
+/* 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/. */
+
+"use strict";
+
+const kToolbarName = "test-new-overflowable-toolbar";
+const kTestWidgetPrefix = "test-widget-for-overflowable-toolbar-";
+
+add_task(function addOverflowingToolbar() {
+ let originalWindowWidth = window.outerWidth;
+
+ let widgetIds = [];
+ for (let i = 0; i < 10; i++) {
+ let id = kTestWidgetPrefix + i;
+ widgetIds.push(id);
+ let spec = {id: id, type: "button", removable: true, label: "test", tooltiptext: "" + i};
+ CustomizableUI.createWidget(spec);
+ }
+
+ let toolbarNode = createOverflowableToolbarWithPlacements(kToolbarName, widgetIds);
+ assertAreaPlacements(kToolbarName, widgetIds);
+
+ for (let id of widgetIds) {
+ document.getElementById(id).style.minWidth = "200px";
+ }
+
+ isnot(toolbarNode.overflowable, null, "Toolbar should have overflowable controller");
+ isnot(toolbarNode.customizationTarget, null, "Toolbar should have customization target");
+ isnot(toolbarNode.customizationTarget, toolbarNode, "Customization target should not be toolbar node");
+
+ let oldChildCount = toolbarNode.customizationTarget.childElementCount;
+ let overflowableList = document.getElementById(kToolbarName + "-overflow-list");
+ let oldOverflowCount = overflowableList.childElementCount;
+
+ isnot(oldChildCount, 0, "Toolbar should have non-overflowing widgets");
+
+ window.resizeTo(400, window.outerHeight);
+ yield waitForCondition(() => toolbarNode.hasAttribute("overflowing"));
+ ok(toolbarNode.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+ ok(toolbarNode.customizationTarget.childElementCount < oldChildCount, "Should have fewer children.");
+ ok(overflowableList.childElementCount > oldOverflowCount, "Should have more overflowed widgets.");
+
+ window.resizeTo(originalWindowWidth, window.outerHeight);
+});
+
+
+add_task(function asyncCleanup() {
+ removeCustomToolbars();
+ yield resetCustomization();
+});
--- a/browser/components/customizableui/test/browser_981305_separator_insertion.js
+++ b/browser/components/customizableui/test/browser_981305_separator_insertion.js
@@ -1,73 +1,73 @@
-/* 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/. */
-
-"use strict";
-
-let tempElements = [];
-
-function insertTempItemsIntoMenu(parentMenu) {
- // Last element is null to insert at the end:
- let beforeEls = [parentMenu.firstChild, parentMenu.lastChild, null];
- for (let i = 0; i < beforeEls.length; i++) {
- let sep = document.createElement("menuseparator");
- tempElements.push(sep);
- parentMenu.insertBefore(sep, beforeEls[i]);
- let menu = document.createElement("menu");
- tempElements.push(menu);
- parentMenu.insertBefore(menu, beforeEls[i]);
- // And another separator for good measure:
- sep = document.createElement("menuseparator");
- tempElements.push(sep);
- parentMenu.insertBefore(sep, beforeEls[i]);
- }
-}
-
-function checkSeparatorInsertion(menuId, buttonId, subviewId) {
- return function() {
- info("Checking for duplicate separators in " + buttonId + " widget");
- let menu = document.getElementById(menuId);
- insertTempItemsIntoMenu(menu);
-
- let placement = CustomizableUI.getPlacementOfWidget(buttonId);
- let changedPlacement = false;
- if (!placement || placement.area != CustomizableUI.AREA_PANEL) {
- CustomizableUI.addWidgetToArea(buttonId, CustomizableUI.AREA_PANEL);
- changedPlacement = true;
- }
- yield PanelUI.show();
-
- let button = document.getElementById(buttonId);
- button.click();
-
- yield waitForCondition(() => !PanelUI.multiView.hasAttribute("transitioning"));
- let subview = document.getElementById(subviewId);
- ok(subview.firstChild, "Subview should have a kid");
- is(subview.firstChild.localName, "toolbarbutton", "There should be no separators to start with");
-
- for (let kid of subview.children) {
- if (kid.localName == "menuseparator") {
- ok(kid.previousSibling && kid.previousSibling.localName != "menuseparator",
- "Separators should never have another separator next to them, and should never be the first node.");
- }
- }
-
- let panelHiddenPromise = promisePanelHidden(window);
- PanelUI.hide();
- yield panelHiddenPromise;
-
- if (changedPlacement) {
- CustomizableUI.reset();
- }
- };
-}
-
-add_task(checkSeparatorInsertion("menuWebDeveloperPopup", "developer-button", "PanelUI-developerItems"));
-add_task(checkSeparatorInsertion("viewSidebarMenu", "sidebar-button", "PanelUI-sidebarItems"));
-
-registerCleanupFunction(function() {
- for (let el of tempElements) {
- el.remove();
- }
- tempElements = null;
-});
+/* 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/. */
+
+"use strict";
+
+let tempElements = [];
+
+function insertTempItemsIntoMenu(parentMenu) {
+ // Last element is null to insert at the end:
+ let beforeEls = [parentMenu.firstChild, parentMenu.lastChild, null];
+ for (let i = 0; i < beforeEls.length; i++) {
+ let sep = document.createElement("menuseparator");
+ tempElements.push(sep);
+ parentMenu.insertBefore(sep, beforeEls[i]);
+ let menu = document.createElement("menu");
+ tempElements.push(menu);
+ parentMenu.insertBefore(menu, beforeEls[i]);
+ // And another separator for good measure:
+ sep = document.createElement("menuseparator");
+ tempElements.push(sep);
+ parentMenu.insertBefore(sep, beforeEls[i]);
+ }
+}
+
+function checkSeparatorInsertion(menuId, buttonId, subviewId) {
+ return function() {
+ info("Checking for duplicate separators in " + buttonId + " widget");
+ let menu = document.getElementById(menuId);
+ insertTempItemsIntoMenu(menu);
+
+ let placement = CustomizableUI.getPlacementOfWidget(buttonId);
+ let changedPlacement = false;
+ if (!placement || placement.area != CustomizableUI.AREA_PANEL) {
+ CustomizableUI.addWidgetToArea(buttonId, CustomizableUI.AREA_PANEL);
+ changedPlacement = true;
+ }
+ yield PanelUI.show();
+
+ let button = document.getElementById(buttonId);
+ button.click();
+
+ yield waitForCondition(() => !PanelUI.multiView.hasAttribute("transitioning"));
+ let subview = document.getElementById(subviewId);
+ ok(subview.firstChild, "Subview should have a kid");
+ is(subview.firstChild.localName, "toolbarbutton", "There should be no separators to start with");
+
+ for (let kid of subview.children) {
+ if (kid.localName == "menuseparator") {
+ ok(kid.previousSibling && kid.previousSibling.localName != "menuseparator",
+ "Separators should never have another separator next to them, and should never be the first node.");
+ }
+ }
+
+ let panelHiddenPromise = promisePanelHidden(window);
+ PanelUI.hide();
+ yield panelHiddenPromise;
+
+ if (changedPlacement) {
+ CustomizableUI.reset();
+ }
+ };
+}
+
+add_task(checkSeparatorInsertion("menuWebDeveloperPopup", "developer-button", "PanelUI-developerItems"));
+add_task(checkSeparatorInsertion("viewSidebarMenu", "sidebar-button", "PanelUI-sidebarItems"));
+
+registerCleanupFunction(function() {
+ for (let el of tempElements) {
+ el.remove();
+ }
+ tempElements = null;
+});
--- a/browser/components/customizableui/test/browser_982656_restore_defaults_builtin_widgets.js
+++ b/browser/components/customizableui/test/browser_982656_restore_defaults_builtin_widgets.js
@@ -1,57 +1,57 @@
-/* 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/. */
-
-"use strict";
-
-// Restoring default should not place addon widgets back in the toolbar
-add_task(function() {
- ok(CustomizableUI.inDefaultState, "Default state to begin");
-
- const kWidgetId = "bug982656-add-on-widget-should-not-restore-to-default-area";
- let widgetSpec = {
- id: kWidgetId,
- defaultArea: CustomizableUI.AREA_NAVBAR
- };
- CustomizableUI.createWidget(widgetSpec);
-
- ok(!CustomizableUI.inDefaultState, "Not in default state after widget added");
- is(CustomizableUI.getPlacementOfWidget(kWidgetId).area, CustomizableUI.AREA_NAVBAR, "Widget should be in navbar");
-
- yield resetCustomization();
-
- ok(CustomizableUI.inDefaultState, "Back in default state after reset");
- is(CustomizableUI.getPlacementOfWidget(kWidgetId), null, "Widget now in palette");
- CustomizableUI.destroyWidget(kWidgetId);
-});
-
-
-// resetCustomization shouldn't move 3rd party widgets out of custom toolbars
-add_task(function() {
- const kToolbarId = "bug982656-toolbar-with-defaultset";
- const kWidgetId = "bug982656-add-on-widget-should-restore-to-default-area-when-area-is-not-builtin";
- ok(CustomizableUI.inDefaultState, "Everything should be in its default state.");
- let toolbar = createToolbarWithPlacements(kToolbarId);
- ok(CustomizableUI.areas.indexOf(kToolbarId) != -1,
- "Toolbar has been registered.");
- is(CustomizableUI.getAreaType(kToolbarId), CustomizableUI.TYPE_TOOLBAR,
- "Area should be registered as toolbar");
-
- let widgetSpec = {
- id: kWidgetId,
- defaultArea: kToolbarId
- };
- CustomizableUI.createWidget(widgetSpec);
-
- ok(!CustomizableUI.inDefaultState, "No longer in default state after toolbar is registered and visible.");
- is(CustomizableUI.getPlacementOfWidget(kWidgetId).area, kToolbarId, "Widget should be in custom toolbar");
-
- yield resetCustomization();
- ok(CustomizableUI.inDefaultState, "Back in default state after reset");
- is(CustomizableUI.getPlacementOfWidget(kWidgetId).area, kToolbarId, "Widget still in custom toolbar");
- ok(toolbar.collapsed, "Custom toolbar should be collapsed after reset");
-
- toolbar.remove();
- CustomizableUI.destroyWidget(kWidgetId);
- CustomizableUI.unregisterArea(kToolbarId);
-});
+/* 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/. */
+
+"use strict";
+
+// Restoring default should not place addon widgets back in the toolbar
+add_task(function() {
+ ok(CustomizableUI.inDefaultState, "Default state to begin");
+
+ const kWidgetId = "bug982656-add-on-widget-should-not-restore-to-default-area";
+ let widgetSpec = {
+ id: kWidgetId,
+ defaultArea: CustomizableUI.AREA_NAVBAR
+ };
+ CustomizableUI.createWidget(widgetSpec);
+
+ ok(!CustomizableUI.inDefaultState, "Not in default state after widget added");
+ is(CustomizableUI.getPlacementOfWidget(kWidgetId).area, CustomizableUI.AREA_NAVBAR, "Widget should be in navbar");
+
+ yield resetCustomization();
+
+ ok(CustomizableUI.inDefaultState, "Back in default state after reset");
+ is(CustomizableUI.getPlacementOfWidget(kWidgetId), null, "Widget now in palette");
+ CustomizableUI.destroyWidget(kWidgetId);
+});
+
+
+// resetCustomization shouldn't move 3rd party widgets out of custom toolbars
+add_task(function() {
+ const kToolbarId = "bug982656-toolbar-with-defaultset";
+ const kWidgetId = "bug982656-add-on-widget-should-restore-to-default-area-when-area-is-not-builtin";
+ ok(CustomizableUI.inDefaultState, "Everything should be in its default state.");
+ let toolbar = createToolbarWithPlacements(kToolbarId);
+ ok(CustomizableUI.areas.indexOf(kToolbarId) != -1,
+ "Toolbar has been registered.");
+ is(CustomizableUI.getAreaType(kToolbarId), CustomizableUI.TYPE_TOOLBAR,
+ "Area should be registered as toolbar");
+
+ let widgetSpec = {
+ id: kWidgetId,
+ defaultArea: kToolbarId
+ };
+ CustomizableUI.createWidget(widgetSpec);
+
+ ok(!CustomizableUI.inDefaultState, "No longer in default state after toolbar is registered and visible.");
+ is(CustomizableUI.getPlacementOfWidget(kWidgetId).area, kToolbarId, "Widget should be in custom toolbar");
+
+ yield resetCustomization();
+ ok(CustomizableUI.inDefaultState, "Back in default state after reset");
+ is(CustomizableUI.getPlacementOfWidget(kWidgetId).area, kToolbarId, "Widget still in custom toolbar");
+ ok(toolbar.collapsed, "Custom toolbar should be collapsed after reset");
+
+ toolbar.remove();
+ CustomizableUI.destroyWidget(kWidgetId);
+ CustomizableUI.unregisterArea(kToolbarId);
+});
--- a/browser/components/customizableui/test/browser_984455_bookmarks_items_reparenting.js
+++ b/browser/components/customizableui/test/browser_984455_bookmarks_items_reparenting.js
@@ -1,267 +1,267 @@
-/* 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/. */
-
-"use strict";
-
-let gNavBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
-let gOverflowList = document.getElementById(gNavBar.getAttribute("overflowtarget"));
-
-const kBookmarksButton = "bookmarks-menu-button";
-const kBookmarksItems = "personal-bookmarks";
-const kOriginalWindowWidth = window.outerWidth;
-const kSmallWidth = 400;
-
-/**
- * Helper function that opens the bookmarks menu, and returns a Promise that
- * resolves as soon as the menu is ready for interaction.
- */
-function bookmarksMenuPanelShown() {
- let deferred = Promise.defer();
- let bookmarksMenuPopup = document.getElementById("BMB_bookmarksPopup");
- let onTransitionEnd = (e) => {
- if (e.target == bookmarksMenuPopup) {
- bookmarksMenuPopup.removeEventListener("transitionend", onTransitionEnd);
- deferred.resolve();
- }
- }
- bookmarksMenuPopup.addEventListener("transitionend", onTransitionEnd);
- return deferred.promise;
-}
-
-/**
- * Checks that the placesContext menu is correctly attached to the
- * controller of some view. Returns a Promise that resolves as soon
- * as the context menu is closed.
- *
- * @param aItemWithContextMenu the item that we need to synthesize hte
- * right click on in order to open the context menu.
- */
-function checkPlacesContextMenu(aItemWithContextMenu) {
- return Task.spawn(function* () {
- let contextMenu = document.getElementById("placesContext");
- let newBookmarkItem = document.getElementById("placesContext_new:bookmark");
- info("Waiting for context menu on " + aItemWithContextMenu.id);
- let shownPromise = popupShown(contextMenu);
- EventUtils.synthesizeMouseAtCenter(aItemWithContextMenu,
- {type: "contextmenu", button: 2});
- yield shownPromise;
-
- ok(!newBookmarkItem.hasAttribute("disabled"),
- "New bookmark item shouldn't be disabled");
-
- info("Closing context menu");
- yield closePopup(contextMenu);
- });
-}
-
-/**
- * Opens the bookmarks menu panel, and then opens each of the "special"
- * submenus in that list. Then it checks that those submenu's context menus
- * are properly hooked up to a controller.
- */
-function checkSpecialContextMenus() {
- return Task.spawn(function* () {
- let contextMenu = document.getElementById("placesContext");
- let bookmarksMenuButton = document.getElementById(kBookmarksButton);
- let bookmarksMenuPopup = document.getElementById("BMB_bookmarksPopup");
-
- const kSpecialItemIDs = {
- "BMB_bookmarksToolbar": "BMB_bookmarksToolbarPopup",
- "BMB_unsortedBookmarks": "BMB_unsortedBookmarksPopup",
- };
-
- // Open the bookmarks menu button context menus and ensure that
- // they have the proper views attached.
- let shownPromise = bookmarksMenuPanelShown();
- let dropmarker = document.getAnonymousElementByAttribute(bookmarksMenuButton,
- "anonid", "dropmarker");
- EventUtils.synthesizeMouseAtCenter(dropmarker, {});
- info("Waiting for bookmarks menu popup to show after clicking dropmarker.")
- yield shownPromise;
-
- for (let menuID in kSpecialItemIDs) {
- let menuItem = document.getElementById(menuID);
- let menuPopup = document.getElementById(kSpecialItemIDs[menuID]);
- info("Waiting to open menu for " + menuID);
- let shownPromise = popupShown(menuPopup);
- menuPopup.openPopup(menuItem, null, 0, 0, false, false, null);
- yield shownPromise;
-
- yield checkPlacesContextMenu(menuPopup);
- info("Closing menu for " + menuID);
- yield closePopup(menuPopup);
- }
-
- info("Closing bookmarks menu");
- yield closePopup(bookmarksMenuPopup);
- });
-}
-
-/**
- * Closes a focused popup by simulating pressing the Escape key,
- * and returns a Promise that resolves as soon as the popup is closed.
- *
- * @param aPopup the popup node to close.
- */
-function closePopup(aPopup) {
- let hiddenPromise = popupHidden(aPopup);
- EventUtils.synthesizeKey("VK_ESCAPE", {});
- return hiddenPromise;
-}
-
-/**
- * Helper function that checks that the context menu of the
- * bookmark toolbar items chevron popup is correctly hooked up
- * to the controller of a view.
- */
-function checkBookmarksItemsChevronContextMenu() {
- return Task.spawn(function*() {
- let chevronPopup = document.getElementById("PlacesChevronPopup");
- let shownPromise = popupShown(chevronPopup);
- let chevron = document.getElementById("PlacesChevron");
- EventUtils.synthesizeMouseAtCenter(chevron, {});
- info("Waiting for bookmark toolbar item chevron popup to show");
- yield shownPromise;
- yield waitForCondition(() => {
- for (let child of chevronPopup.children) {
- if (child.style.visibility != "hidden")
- return true;
- }
- });
- yield checkPlacesContextMenu(chevronPopup);
- info("Waiting for bookmark toolbar item chevron popup to close");
- yield closePopup(chevronPopup);
- });
-}
-
-/**
- * Forces the window to a width that causes the nav-bar to overflow
- * its contents. Returns a Promise that resolves as soon as the
- * overflowable nav-bar is showing its chevron.
- */
-function overflowEverything() {
- info("Waiting for overflow");
- window.resizeTo(kSmallWidth, window.outerHeight);
- return waitForCondition(() => gNavBar.hasAttribute("overflowing"));
-}
-
-/**
- * Returns the window to its original size from the start of the test,
- * and returns a Promise that resolves when the nav-bar is no longer
- * overflowing.
- */
-function stopOverflowing() {
- info("Waiting until we stop overflowing");
- window.resizeTo(kOriginalWindowWidth, window.outerHeight);
- return waitForCondition(() => !gNavBar.hasAttribute("overflowing"));
-}
-
-/**
- * Checks that an item with ID aID is overflowing in the nav-bar.
- *
- * @param aID the ID of the node to check for overflowingness.
- */
-function checkOverflowing(aID) {
- ok(!gNavBar.querySelector("#" + aID),
- "Item with ID " + aID + " should no longer be in the gNavBar");
- let item = gOverflowList.querySelector("#" + aID);
- ok(item, "Item with ID " + aID + " should be overflowing");
- is(item.getAttribute("overflowedItem"), "true",
- "Item with ID " + aID + " should have overflowedItem attribute");
-}
-
-/**
- * Checks that an item with ID aID is not overflowing in the nav-bar.
- *
- * @param aID the ID of hte node to check for non-overflowingness.
- */
-function checkNotOverflowing(aID) {
- ok(!gOverflowList.querySelector("#" + aID),
- "Item with ID " + aID + " should no longer be overflowing");
- let item = gNavBar.querySelector("#" + aID);
- ok(item, "Item with ID " + aID + " should be in the nav bar");
- ok(!item.hasAttribute("overflowedItem"),
- "Item with ID " + aID + " should not have overflowedItem attribute");
-}
-
-/**
- * Test that overflowing the bookmarks menu button doesn't break the
- * context menus for the Unsorted and Bookmarks Toolbar menu items.
- */
-add_task(function* testOverflowingBookmarksButtonContextMenu() {
- ok(!gNavBar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
- ok(CustomizableUI.inDefaultState, "Should start in default state.");
-
- // Open the Unsorted and Bookmarks Toolbar context menus and ensure
- // that they have views attached.
- yield checkSpecialContextMenus();
-
- yield overflowEverything();
- checkOverflowing(kBookmarksButton);
-
- yield stopOverflowing();
- checkNotOverflowing(kBookmarksButton);
-
- yield checkSpecialContextMenus();
-});
-
-/**
- * Test that the bookmarks toolbar items context menu still works if moved
- * to the menu from the overflow panel, and then back to the toolbar.
- */
-add_task(function* testOverflowingBookmarksItemsContextMenu() {
- info("Ensuring panel is ready.");
- yield PanelUI.ensureReady();
-
- let bookmarksToolbarItems = document.getElementById(kBookmarksItems);
- gCustomizeMode.addToToolbar(bookmarksToolbarItems);
- yield checkPlacesContextMenu(bookmarksToolbarItems);
-
- yield overflowEverything();
- checkOverflowing(kBookmarksItems)
-
- gCustomizeMode.addToPanel(bookmarksToolbarItems);
-
- yield stopOverflowing();
-
- gCustomizeMode.addToToolbar(bookmarksToolbarItems);
- yield checkPlacesContextMenu(bookmarksToolbarItems);
-});
-
-/**
- * Test that overflowing the bookmarks toolbar items doesn't cause the
- * context menu in the bookmarks toolbar items chevron to stop working.
- */
-add_task(function* testOverflowingBookmarksItemsChevronContextMenu() {
- // If it's not already there, let's move the bookmarks toolbar items to
- // the nav-bar.
- let bookmarksToolbarItems = document.getElementById(kBookmarksItems);
- gCustomizeMode.addToToolbar(bookmarksToolbarItems);
-
- // We make the PlacesToolbarItems element be super tiny in order to force
- // the bookmarks toolbar items into overflowing and making the chevron
- // show itself.
- let placesToolbarItems = document.getElementById("PlacesToolbarItems");
- let placesChevron = document.getElementById("PlacesChevron");
- placesToolbarItems.style.maxWidth = "10px";
- info("Waiting for chevron to no longer be collapsed");
- yield waitForCondition(() => !placesChevron.collapsed);
-
- yield checkBookmarksItemsChevronContextMenu();
-
- yield overflowEverything();
- checkOverflowing(kBookmarksItems);
-
- yield stopOverflowing();
- checkNotOverflowing(kBookmarksItems);
-
- yield checkBookmarksItemsChevronContextMenu();
-
- placesToolbarItems.style.removeProperty("max-width");
-});
-
-add_task(function* asyncCleanup() {
- window.resizeTo(kOriginalWindowWidth, window.outerHeight);
- yield resetCustomization();
-});
+/* 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/. */
+
+"use strict";
+
+let gNavBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+let gOverflowList = document.getElementById(gNavBar.getAttribute("overflowtarget"));
+
+const kBookmarksButton = "bookmarks-menu-button";
+const kBookmarksItems = "personal-bookmarks";
+const kOriginalWindowWidth = window.outerWidth;
+const kSmallWidth = 400;
+
+/**
+ * Helper function that opens the bookmarks menu, and returns a Promise that
+ * resolves as soon as the menu is ready for interaction.
+ */
+function bookmarksMenuPanelShown() {
+ let deferred = Promise.defer();
+ let bookmarksMenuPopup = document.getElementById("BMB_bookmarksPopup");
+ let onTransitionEnd = (e) => {
+ if (e.target == bookmarksMenuPopup) {
+ bookmarksMenuPopup.removeEventListener("transitionend", onTransitionEnd);
+ deferred.resolve();
+ }
+ }
+ bookmarksMenuPopup.addEventListener("transitionend", onTransitionEnd);
+ return deferred.promise;
+}
+
+/**
+ * Checks that the placesContext menu is correctly attached to the
+ * controller of some view. Returns a Promise that resolves as soon
+ * as the context menu is closed.
+ *
+ * @param aItemWithContextMenu the item that we need to synthesize hte
+ * right click on in order to open the context menu.
+ */
+function checkPlacesContextMenu(aItemWithContextMenu) {
+ return Task.spawn(function* () {
+ let contextMenu = document.getElementById("placesContext");
+ let newBookmarkItem = document.getElementById("placesContext_new:bookmark");
+ info("Waiting for context menu on " + aItemWithContextMenu.id);
+ let shownPromise = popupShown(contextMenu);
+ EventUtils.synthesizeMouseAtCenter(aItemWithContextMenu,
+ {type: "contextmenu", button: 2});
+ yield shownPromise;
+
+ ok(!newBookmarkItem.hasAttribute("disabled"),
+ "New bookmark item shouldn't be disabled");
+
+ info("Closing context menu");
+ yield closePopup(contextMenu);
+ });
+}
+
+/**
+ * Opens the bookmarks menu panel, and then opens each of the "special"
+ * submenus in that list. Then it checks that those submenu's context menus
+ * are properly hooked up to a controller.
+ */
+function checkSpecialContextMenus() {
+ return Task.spawn(function* () {
+ let contextMenu = document.getElementById("placesContext");
+ let bookmarksMenuButton = document.getElementById(kBookmarksButton);
+ let bookmarksMenuPopup = document.getElementById("BMB_bookmarksPopup");
+
+ const kSpecialItemIDs = {
+ "BMB_bookmarksToolbar": "BMB_bookmarksToolbarPopup",
+ "BMB_unsortedBookmarks": "BMB_unsortedBookmarksPopup",
+ };
+
+ // Open the bookmarks menu button context menus and ensure that
+ // they have the proper views attached.
+ let shownPromise = bookmarksMenuPanelShown();
+ let dropmarker = document.getAnonymousElementByAttribute(bookmarksMenuButton,
+ "anonid", "dropmarker");
+ EventUtils.synthesizeMouseAtCenter(dropmarker, {});
+ info("Waiting for bookmarks menu popup to show after clicking dropmarker.")
+ yield shownPromise;
+
+ for (let menuID in kSpecialItemIDs) {
+ let menuItem = document.getElementById(menuID);
+ let menuPopup = document.getElementById(kSpecialItemIDs[menuID]);
+ info("Waiting to open menu for " + menuID);
+ let shownPromise = popupShown(menuPopup);
+ menuPopup.openPopup(menuItem, null, 0, 0, false, false, null);
+ yield shownPromise;
+
+ yield checkPlacesContextMenu(menuPopup);
+ info("Closing menu for " + menuID);
+ yield closePopup(menuPopup);
+ }
+
+ info("Closing bookmarks menu");
+ yield closePopup(bookmarksMenuPopup);
+ });
+}
+
+/**
+ * Closes a focused popup by simulating pressing the Escape key,
+ * and returns a Promise that resolves as soon as the popup is closed.
+ *
+ * @param aPopup the popup node to close.
+ */
+function closePopup(aPopup) {
+ let hiddenPromise = popupHidden(aPopup);
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ return hiddenPromise;
+}
+
+/**
+ * Helper function that checks that the context menu of the
+ * bookmark toolbar items chevron popup is correctly hooked up
+ * to the controller of a view.
+ */
+function checkBookmarksItemsChevronContextMenu() {
+ return Task.spawn(function*() {
+ let chevronPopup = document.getElementById("PlacesChevronPopup");
+ let shownPromise = popupShown(chevronPopup);
+ let chevron = document.getElementById("PlacesChevron");
+ EventUtils.synthesizeMouseAtCenter(chevron, {});
+ info("Waiting for bookmark toolbar item chevron popup to show");
+ yield shownPromise;
+ yield waitForCondition(() => {
+ for (let child of chevronPopup.children) {
+ if (child.style.visibility != "hidden")
+ return true;
+ }
+ });
+ yield checkPlacesContextMenu(chevronPopup);
+ info("Waiting for bookmark toolbar item chevron popup to close");
+ yield closePopup(chevronPopup);
+ });
+}
+
+/**
+ * Forces the window to a width that causes the nav-bar to overflow
+ * its contents. Returns a Promise that resolves as soon as the
+ * overflowable nav-bar is showing its chevron.
+ */
+function overflowEverything() {
+ info("Waiting for overflow");
+ window.resizeTo(kSmallWidth, window.outerHeight);
+ return waitForCondition(() => gNavBar.hasAttribute("overflowing"));
+}
+
+/**
+ * Returns the window to its original size from the start of the test,
+ * and returns a Promise that resolves when the nav-bar is no longer
+ * overflowing.
+ */
+function stopOverflowing() {
+ info("Waiting until we stop overflowing");
+ window.resizeTo(kOriginalWindowWidth, window.outerHeight);
+ return waitForCondition(() => !gNavBar.hasAttribute("overflowing"));
+}
+
+/**
+ * Checks that an item with ID aID is overflowing in the nav-bar.
+ *
+ * @param aID the ID of the node to check for overflowingness.
+ */
+function checkOverflowing(aID) {
+ ok(!gNavBar.querySelector("#" + aID),
+ "Item with ID " + aID + " should no longer be in the gNavBar");
+ let item = gOverflowList.querySelector("#" + aID);
+ ok(item, "Item with ID " + aID + " should be overflowing");
+ is(item.getAttribute("overflowedItem"), "true",
+ "Item with ID " + aID + " should have overflowedItem attribute");
+}
+
+/**
+ * Checks that an item with ID aID is not overflowing in the nav-bar.
+ *
+ * @param aID the ID of hte node to check for non-overflowingness.
+ */
+function checkNotOverflowing(aID) {
+ ok(!gOverflowList.querySelector("#" + aID),
+ "Item with ID " + aID + " should no longer be overflowing");
+ let item = gNavBar.querySelector("#" + aID);
+ ok(item, "Item with ID " + aID + " should be in the nav bar");
+ ok(!item.hasAttribute("overflowedItem"),
+ "Item with ID " + aID + " should not have overflowedItem attribute");
+}
+
+/**
+ * Test that overflowing the bookmarks menu button doesn't break the
+ * context menus for the Unsorted and Bookmarks Toolbar menu items.
+ */
+add_task(function* testOverflowingBookmarksButtonContextMenu() {
+ ok(!gNavBar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
+ ok(CustomizableUI.inDefaultState, "Should start in default state.");
+
+ // Open the Unsorted and Bookmarks Toolbar context menus and ensure
+ // that they have views attached.
+ yield checkSpecialContextMenus();
+
+ yield overflowEverything();
+ checkOverflowing(kBookmarksButton);
+
+ yield stopOverflowing();
+ checkNotOverflowing(kBookmarksButton);
+
+ yield checkSpecialContextMenus();
+});
+
+/**
+ * Test that the bookmarks toolbar items context menu still works if moved
+ * to the menu from the overflow panel, and then back to the toolbar.
+ */
+add_task(function* testOverflowingBookmarksItemsContextMenu() {
+ info("Ensuring panel is ready.");
+ yield PanelUI.ensureReady();
+
+ let bookmarksToolbarItems = document.getElementById(kBookmarksItems);
+ gCustomizeMode.addToToolbar(bookmarksToolbarItems);
+ yield checkPlacesContextMenu(bookmarksToolbarItems);
+
+ yield overflowEverything();
+ checkOverflowing(kBookmarksItems)
+
+ gCustomizeMode.addToPanel(bookmarksToolbarItems);
+
+ yield stopOverflowing();
+
+ gCustomizeMode.addToToolbar(bookmarksToolbarItems);
+ yield checkPlacesContextMenu(bookmarksToolbarItems);
+});
+
+/**
+ * Test that overflowing the bookmarks toolbar items doesn't cause the
+ * context menu in the bookmarks toolbar items chevron to stop working.
+ */
+add_task(function* testOverflowingBookmarksItemsChevronContextMenu() {
+ // If it's not already there, let's move the bookmarks toolbar items to
+ // the nav-bar.
+ let bookmarksToolbarItems = document.getElementById(kBookmarksItems);
+ gCustomizeMode.addToToolbar(bookmarksToolbarItems);
+
+ // We make the PlacesToolbarItems element be super tiny in order to force
+ // the bookmarks toolbar items into overflowing and making the chevron
+ // show itself.
+ let placesToolbarItems = document.getElementById("PlacesToolbarItems");
+ let placesChevron = document.getElementById("PlacesChevron");
+ placesToolbarItems.style.maxWidth = "10px";
+ info("Waiting for chevron to no longer be collapsed");
+ yield waitForCondition(() => !placesChevron.collapsed);
+
+ yield checkBookmarksItemsChevronContextMenu();
+
+ yield overflowEverything();
+ checkOverflowing(kBookmarksItems);
+
+ yield stopOverflowing();
+ checkNotOverflowing(kBookmarksItems);
+
+ yield checkBookmarksItemsChevronContextMenu();
+
+ placesToolbarItems.style.removeProperty("max-width");
+});
+
+add_task(function* asyncCleanup() {
+ window.resizeTo(kOriginalWindowWidth, window.outerHeight);
+ yield resetCustomization();
+});
--- a/browser/components/customizableui/test/browser_989751_subviewbutton_class.js
+++ b/browser/components/customizableui/test/browser_989751_subviewbutton_class.js
@@ -1,62 +1,62 @@
-/* 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/. */
-
-"use strict";
-
-const kCustomClass = "acustomclassnoonewilluse";
-let tempElement = null;
-
-function insertClassNameToMenuChildren(parentMenu) {
- let el = parentMenu.querySelector("menuitem:first-of-type");
- el.classList.add(kCustomClass);
- tempElement = el;
-}
-
-function checkSubviewButtonClass(menuId, buttonId, subviewId) {
- return function() {
- info("Checking for items without the subviewbutton class in " + buttonId + " widget");
- let menu = document.getElementById(menuId);
- insertClassNameToMenuChildren(menu);
-
- let placement = CustomizableUI.getPlacementOfWidget(buttonId);
- let changedPlacement = false;
- if (!placement || placement.area != CustomizableUI.AREA_PANEL) {
- CustomizableUI.addWidgetToArea(buttonId, CustomizableUI.AREA_PANEL);
- changedPlacement = true;
- }
- yield PanelUI.show();
-
- let button = document.getElementById(buttonId);
- button.click();
-
- yield waitForCondition(() => !PanelUI.multiView.hasAttribute("transitioning"));
- let subview = document.getElementById(subviewId);
- ok(subview.firstChild, "Subview should have a kid");
- let subviewchildren = subview.querySelectorAll("toolbarbutton");
- for (let i = 0; i < subviewchildren.length; i++) {
- let item = subviewchildren[i];
- let itemReadable = "Item '" + item.label + "' (classes: " + item.className + ")";
- ok(item.classList.contains("subviewbutton"), itemReadable + " should have the subviewbutton class.");
- if (i == 0) {
- ok(item.classList.contains(kCustomClass), itemReadable + " should still have its own class, too.");
- }
- }
-
- let panelHiddenPromise = promisePanelHidden(window);
- PanelUI.hide();
- yield panelHiddenPromise;
-
- if (changedPlacement) {
- CustomizableUI.reset();
- }
- };
-}
-
-add_task(checkSubviewButtonClass("menuWebDeveloperPopup", "developer-button", "PanelUI-developerItems"));
-add_task(checkSubviewButtonClass("viewSidebarMenu", "sidebar-button", "PanelUI-sidebarItems"));
-
-registerCleanupFunction(function() {
- tempElement.classList.remove(kCustomClass)
- tempElement = null;
-});
+/* 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/. */
+
+"use strict";
+
+const kCustomClass = "acustomclassnoonewilluse";
+let tempElement = null;
+
+function insertClassNameToMenuChildren(parentMenu) {
+ let el = parentMenu.querySelector("menuitem:first-of-type");
+ el.classList.add(kCustomClass);
+ tempElement = el;
+}
+
+function checkSubviewButtonClass(menuId, buttonId, subviewId) {
+ return function() {
+ info("Checking for items without the subviewbutton class in " + buttonId + " widget");
+ let menu = document.getElementById(menuId);
+ insertClassNameToMenuChildren(menu);
+
+ let placement = CustomizableUI.getPlacementOfWidget(buttonId);
+ let changedPlacement = false;
+ if (!placement || placement.area != CustomizableUI.AREA_PANEL) {
+ CustomizableUI.addWidgetToArea(buttonId, CustomizableUI.AREA_PANEL);
+ changedPlacement = true;
+ }
+ yield PanelUI.show();
+
+ let button = document.getElementById(buttonId);
+ button.click();
+
+ yield waitForCondition(() => !PanelUI.multiView.hasAttribute("transitioning"));
+ let subview = document.getElementById(subviewId);
+ ok(subview.firstChild, "Subview should have a kid");
+ let subviewchildren = subview.querySelectorAll("toolbarbutton");
+ for (let i = 0; i < subviewchildren.length; i++) {
+ let item = subviewchildren[i];
+ let itemReadable = "Item '" + item.label + "' (classes: " + item.className + ")";
+ ok(item.classList.contains("subviewbutton"), itemReadable + " should have the subviewbutton class.");
+ if (i == 0) {
+ ok(item.classList.contains(kCustomClass), itemReadable + " should still have its own class, too.");
+ }
+ }
+
+ let panelHiddenPromise = promisePanelHidden(window);
+ PanelUI.hide();
+ yield panelHiddenPromise;
+
+ if (changedPlacement) {
+ CustomizableUI.reset();
+ }
+ };
+}
+
+add_task(checkSubviewButtonClass("menuWebDeveloperPopup", "developer-button", "PanelUI-developerItems"));
+add_task(checkSubviewButtonClass("viewSidebarMenu", "sidebar-button", "PanelUI-sidebarItems"));
+
+registerCleanupFunction(function() {
+ tempElement.classList.remove(kCustomClass)
+ tempElement = null;
+});
--- a/browser/components/customizableui/test/browser_996364_registerArea_different_properties.js
+++ b/browser/components/customizableui/test/browser_996364_registerArea_different_properties.js
@@ -1,112 +1,112 @@
-/* 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/. */
-
-"use strict";
-
-// Calling CustomizableUI.registerArea twice with no
-// properties should not throw an exception.
-add_task(function() {
- try {
- CustomizableUI.registerArea("area-996364", {});
- CustomizableUI.registerArea("area-996364", {});
- } catch (ex) {
- ok(false, ex.message);
- }
-
- CustomizableUI.unregisterArea("area-996364", true);
-});
-
-add_task(function() {
- let exceptionThrown = false;
- try {
- CustomizableUI.registerArea("area-996364-2", {type: CustomizableUI.TYPE_TOOLBAR, defaultCollapsed: "false"});
- } catch (ex) {
- exceptionThrown = true;
- }
- ok(exceptionThrown, "defaultCollapsed is not allowed as an external property");
-
- // No need to unregister the area because registration fails.
-});
-
-add_task(function() {
- let exceptionThrown;
- try {
- CustomizableUI.registerArea("area-996364-3", {type: CustomizableUI.TYPE_TOOLBAR});
- CustomizableUI.registerArea("area-996364-3", {type: CustomizableUI.TYPE_MENU_PANEL});
- } catch (ex) {
- exceptionThrown = ex;
- }
- ok(exceptionThrown, "Exception expected, an area cannot change types: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
-
- CustomizableUI.unregisterArea("area-996364-3", true);
-});
-
-add_task(function() {
- let exceptionThrown;
- try {
- CustomizableUI.registerArea("area-996364-4", {type: CustomizableUI.TYPE_MENU_PANEL});
- CustomizableUI.registerArea("area-996364-4", {type: CustomizableUI.TYPE_TOOLBAR});
- } catch (ex) {
- exceptionThrown = ex;
- }
- ok(exceptionThrown, "Exception expected, an area cannot change types: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
-
- CustomizableUI.unregisterArea("area-996364-4", true);
-});
-
-add_task(function() {
- let exceptionThrown;
- try {
- CustomizableUI.registerArea("area-996899-1", { anchor: "PanelUI-menu-button",
- type: CustomizableUI.TYPE_MENU_PANEL,
- defaultPlacements: [] });
- CustomizableUI.registerArea("area-996899-1", { anchor: "home-button",
- type: CustomizableUI.TYPE_MENU_PANEL,
- defaultPlacements: [] });
- } catch (ex) {
- exceptionThrown = ex;
- }
- ok(!exceptionThrown, "Changing anchors shouldn't throw an exception: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
- CustomizableUI.unregisterArea("area-996899-1", true);
-});
-
-add_task(function() {
- let exceptionThrown;
- try {
- CustomizableUI.registerArea("area-996899-2", { anchor: "PanelUI-menu-button",
- type: CustomizableUI.TYPE_MENU_PANEL,
- defaultPlacements: [] });
- CustomizableUI.registerArea("area-996899-2", { anchor: "PanelUI-menu-button",
- type: CustomizableUI.TYPE_MENU_PANEL,
- defaultPlacements: ["feed-button"] });
- } catch (ex) {
- exceptionThrown = ex;
- }
- ok(!exceptionThrown, "Changing defaultPlacements shouldn't throw an exception: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
- CustomizableUI.unregisterArea("area-996899-2", true);
-});
-
-add_task(function() {
- let exceptionThrown;
- try {
- CustomizableUI.registerArea("area-996899-3", { legacy: true });
- CustomizableUI.registerArea("area-996899-3", { legacy: false });
- } catch (ex) {
- exceptionThrown = ex;
- }
- ok(exceptionThrown, "Changing 'legacy' should throw an exception: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
- CustomizableUI.unregisterArea("area-996899-3", true);
-});
-
-add_task(function() {
- let exceptionThrown;
- try {
- CustomizableUI.registerArea("area-996899-4", { overflowable: true });
- CustomizableUI.registerArea("area-996899-4", { overflowable: false });
- } catch (ex) {
- exceptionThrown = ex;
- }
- ok(exceptionThrown, "Changing 'overflowable' should throw an exception: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
- CustomizableUI.unregisterArea("area-996899-4", true);
-});
+/* 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/. */
+
+"use strict";
+
+// Calling CustomizableUI.registerArea twice with no
+// properties should not throw an exception.
+add_task(function() {
+ try {
+ CustomizableUI.registerArea("area-996364", {});
+ CustomizableUI.registerArea("area-996364", {});
+ } catch (ex) {
+ ok(false, ex.message);
+ }
+
+ CustomizableUI.unregisterArea("area-996364", true);
+});
+
+add_task(function() {
+ let exceptionThrown = false;
+ try {
+ CustomizableUI.registerArea("area-996364-2", {type: CustomizableUI.TYPE_TOOLBAR, defaultCollapsed: "false"});
+ } catch (ex) {
+ exceptionThrown = true;
+ }
+ ok(exceptionThrown, "defaultCollapsed is not allowed as an external property");
+
+ // No need to unregister the area because registration fails.
+});
+
+add_task(function() {
+ let exceptionThrown;
+ try {
+ CustomizableUI.registerArea("area-996364-3", {type: CustomizableUI.TYPE_TOOLBAR});
+ CustomizableUI.registerArea("area-996364-3", {type: CustomizableUI.TYPE_MENU_PANEL});
+ } catch (ex) {
+ exceptionThrown = ex;
+ }
+ ok(exceptionThrown, "Exception expected, an area cannot change types: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
+
+ CustomizableUI.unregisterArea("area-996364-3", true);
+});
+
+add_task(function() {
+ let exceptionThrown;
+ try {
+ CustomizableUI.registerArea("area-996364-4", {type: CustomizableUI.TYPE_MENU_PANEL});
+ CustomizableUI.registerArea("area-996364-4", {type: CustomizableUI.TYPE_TOOLBAR});
+ } catch (ex) {
+ exceptionThrown = ex;
+ }
+ ok(exceptionThrown, "Exception expected, an area cannot change types: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
+
+ CustomizableUI.unregisterArea("area-996364-4", true);
+});
+
+add_task(function() {
+ let exceptionThrown;
+ try {
+ CustomizableUI.registerArea("area-996899-1", { anchor: "PanelUI-menu-button",
+ type: CustomizableUI.TYPE_MENU_PANEL,
+ defaultPlacements: [] });
+ CustomizableUI.registerArea("area-996899-1", { anchor: "home-button",
+ type: CustomizableUI.TYPE_MENU_PANEL,
+ defaultPlacements: [] });
+ } catch (ex) {
+ exceptionThrown = ex;
+ }
+ ok(!exceptionThrown, "Changing anchors shouldn't throw an exception: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
+ CustomizableUI.unregisterArea("area-996899-1", true);
+});
+
+add_task(function() {
+ let exceptionThrown;
+ try {
+ CustomizableUI.registerArea("area-996899-2", { anchor: "PanelUI-menu-button",
+ type: CustomizableUI.TYPE_MENU_PANEL,
+ defaultPlacements: [] });
+ CustomizableUI.registerArea("area-996899-2", { anchor: "PanelUI-menu-button",
+ type: CustomizableUI.TYPE_MENU_PANEL,
+ defaultPlacements: ["feed-button"] });
+ } catch (ex) {
+ exceptionThrown = ex;
+ }
+ ok(!exceptionThrown, "Changing defaultPlacements shouldn't throw an exception: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
+ CustomizableUI.unregisterArea("area-996899-2", true);
+});
+
+add_task(function() {
+ let exceptionThrown;
+ try {
+ CustomizableUI.registerArea("area-996899-3", { legacy: true });
+ CustomizableUI.registerArea("area-996899-3", { legacy: false });
+ } catch (ex) {
+ exceptionThrown = ex;
+ }
+ ok(exceptionThrown, "Changing 'legacy' should throw an exception: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
+ CustomizableUI.unregisterArea("area-996899-3", true);
+});
+
+add_task(function() {
+ let exceptionThrown;
+ try {
+ CustomizableUI.registerArea("area-996899-4", { overflowable: true });
+ CustomizableUI.registerArea("area-996899-4", { overflowable: false });
+ } catch (ex) {
+ exceptionThrown = ex;
+ }
+ ok(exceptionThrown, "Changing 'overflowable' should throw an exception: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
+ CustomizableUI.unregisterArea("area-996899-4", true);
+});
--- a/browser/components/dirprovider/tests/unit/head_dirprovider.js
+++ b/browser/components/dirprovider/tests/unit/head_dirprovider.js
@@ -1,20 +1,20 @@
-/* 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/. */
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-var gProfD = do_get_profile();
-var gDirSvc = Cc["@mozilla.org/file/directory_service;1"].
- getService(Ci.nsIProperties);
-var gPrefSvc = Cc["@mozilla.org/preferences-service;1"].
- getService(Ci.nsIPrefBranch);
-
-function writeTestFile(aParent, aName) {
- let file = aParent.clone();
- file.append(aName);
- file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0644);
- return file;
-}
-
+/* 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/. */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+var gProfD = do_get_profile();
+var gDirSvc = Cc["@mozilla.org/file/directory_service;1"].
+ getService(Ci.nsIProperties);
+var gPrefSvc = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch);
+
+function writeTestFile(aParent, aName) {
+ let file = aParent.clone();
+ file.append(aName);
+ file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0644);
+ return file;
+}
+
--- a/browser/components/dirprovider/tests/unit/test_bookmark_pref.js
+++ b/browser/components/dirprovider/tests/unit/test_bookmark_pref.js
@@ -1,14 +1,14 @@
-/* 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/. */
-
-// We need to run this test separately since DirectoryProvider persists BMarks
-
-function run_test() {
- let dir = gProfD.clone();
- let tfile = writeTestFile(dir, "bookmarkfile.test");
- gPrefSvc.setCharPref("browser.bookmarks.file", tfile.path);
-
- let bmarks = gDirSvc.get("BMarks", Ci.nsIFile);
- do_check_true(tfile.equals(bmarks));
-}
+/* 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/. */
+
+// We need to run this test separately since DirectoryProvider persists BMarks
+
+function run_test() {
+ let dir = gProfD.clone();
+ let tfile = writeTestFile(dir, "bookmarkfile.test");
+ gPrefSvc.setCharPref("browser.bookmarks.file", tfile.path);
+
+ let bmarks = gDirSvc.get("BMarks", Ci.nsIFile);
+ do_check_true(tfile.equals(bmarks));
+}
--- a/browser/components/migration/src/nsIEHistoryEnumerator.cpp
+++ b/browser/components/migration/src/nsIEHistoryEnumerator.cpp
@@ -1,139 +1,139 @@
-/* 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/. */
-
-#include "nsIEHistoryEnumerator.h"
-
-#include <urlhist.h>
-#include <shlguid.h>
-
-#include "nsStringAPI.h"
-#include "nsNetUtil.h"
-#include "nsIVariant.h"
-#include "nsCOMArray.h"
-#include "nsArrayEnumerator.h"
-
-namespace {
-
- PRTime FileTimeToPRTime(FILETIME* filetime)
- {
- SYSTEMTIME st;
- ::FileTimeToSystemTime(filetime, &st);
- PRExplodedTime prt;
- prt.tm_year = st.wYear;
- // SYSTEMTIME's day-of-month parameter is 1-based,
- // PRExplodedTime's is 0-based.
- prt.tm_month = st.wMonth - 1;
- prt.tm_mday = st.wDay;
- prt.tm_hour = st.wHour;
- prt.tm_min = st.wMinute;
- prt.tm_sec = st.wSecond;
- prt.tm_usec = st.wMilliseconds * 1000;
- prt.tm_wday = 0;
- prt.tm_yday = 0;
- prt.tm_params.tp_gmt_offset = 0;
- prt.tm_params.tp_dst_offset = 0;
- return PR_ImplodeTime(&prt);
- }
-
-} // Anonymous namespace.
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsIEHistoryEnumerator
-
-NS_IMPL_ISUPPORTS(nsIEHistoryEnumerator, nsISimpleEnumerator)
-
-nsIEHistoryEnumerator::nsIEHistoryEnumerator()
-{
- ::CoInitialize(nullptr);
-}
-
-nsIEHistoryEnumerator::~nsIEHistoryEnumerator()
-{
- ::CoUninitialize();
-}
-
-void
-nsIEHistoryEnumerator::EnsureInitialized()
-{
- if (mURLEnumerator)
- return;
-
- HRESULT hr = ::CoCreateInstance(CLSID_CUrlHistory,
- nullptr,
- CLSCTX_INPROC_SERVER,
- IID_IUrlHistoryStg2,
- getter_AddRefs(mIEHistory));
- if (FAILED(hr))
- return;
-
- hr = mIEHistory->EnumUrls(getter_AddRefs(mURLEnumerator));
- if (FAILED(hr))
- return;
-}
-
-NS_IMETHODIMP
-nsIEHistoryEnumerator::HasMoreElements(bool* _retval)
-{
- *_retval = false;
-
- EnsureInitialized();
- MOZ_ASSERT(mURLEnumerator, "Should have instanced an IE History URLEnumerator");
- if (!mURLEnumerator)
- return NS_OK;
-
- STATURL statURL;
- ULONG fetched;
-
- // First argument is not implemented, so doesn't matter what we pass.
- HRESULT hr = mURLEnumerator->Next(1, &statURL, &fetched);
- if (FAILED(hr) || fetched != 1UL) {
- // Reached the last entry.
- return NS_OK;
- }
-
- nsCOMPtr<nsIURI> uri;
- if (statURL.pwcsUrl) {
- nsDependentString url(statURL.pwcsUrl);
- nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
- ::CoTaskMemFree(statURL.pwcsUrl);
- if (NS_FAILED(rv)) {
- // Got a corrupt or invalid URI, continue to the next entry.
- return HasMoreElements(_retval);
- }
- }
-
- nsDependentString title(statURL.pwcsTitle);
-
- PRTime lastVisited = FileTimeToPRTime(&(statURL.ftLastVisited));
-
- mCachedNextEntry = do_CreateInstance("@mozilla.org/hash-property-bag;1");
- MOZ_ASSERT(mCachedNextEntry, "Should have instanced a new property bag");
- if (mCachedNextEntry) {
- mCachedNextEntry->SetPropertyAsInterface(NS_LITERAL_STRING("uri"), uri);
- mCachedNextEntry->SetPropertyAsAString(NS_LITERAL_STRING("title"), title);
- mCachedNextEntry->SetPropertyAsInt64(NS_LITERAL_STRING("time"), lastVisited);
-
- *_retval = true;
- }
-
- if (statURL.pwcsTitle)
- ::CoTaskMemFree(statURL.pwcsTitle);
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsIEHistoryEnumerator::GetNext(nsISupports** _retval)
-{
- *_retval = nullptr;
-
- if (!mCachedNextEntry)
- return NS_ERROR_FAILURE;
-
- NS_ADDREF(*_retval = mCachedNextEntry);
- // Release the cached entry, so it can't be returned twice.
- mCachedNextEntry = nullptr;
-
- return NS_OK;
-}
+/* 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/. */
+
+#include "nsIEHistoryEnumerator.h"
+
+#include <urlhist.h>
+#include <shlguid.h>
+
+#include "nsStringAPI.h"
+#include "nsNetUtil.h"
+#include "nsIVariant.h"
+#include "nsCOMArray.h"
+#include "nsArrayEnumerator.h"
+
+namespace {
+
+ PRTime FileTimeToPRTime(FILETIME* filetime)
+ {
+ SYSTEMTIME st;
+ ::FileTimeToSystemTime(filetime, &st);
+ PRExplodedTime prt;
+ prt.tm_year = st.wYear;
+ // SYSTEMTIME's day-of-month parameter is 1-based,
+ // PRExplodedTime's is 0-based.
+ prt.tm_month = st.wMonth - 1;
+ prt.tm_mday = st.wDay;
+ prt.tm_hour = st.wHour;
+ prt.tm_min = st.wMinute;
+ prt.tm_sec = st.wSecond;
+ prt.tm_usec = st.wMilliseconds * 1000;
+ prt.tm_wday = 0;
+ prt.tm_yday = 0;
+ prt.tm_params.tp_gmt_offset = 0;
+ prt.tm_params.tp_dst_offset = 0;
+ return PR_ImplodeTime(&prt);
+ }
+
+} // Anonymous namespace.
+
+////////////////////////////////////////////////////////////////////////////////
+//// nsIEHistoryEnumerator
+
+NS_IMPL_ISUPPORTS(nsIEHistoryEnumerator, nsISimpleEnumerator)
+
+nsIEHistoryEnumerator::nsIEHistoryEnumerator()
+{
+ ::CoInitialize(nullptr);
+}
+
+nsIEHistoryEnumerator::~nsIEHistoryEnumerator()
+{
+ ::CoUninitialize();
+}
+
+void
+nsIEHistoryEnumerator::EnsureInitialized()
+{
+ if (mURLEnumerator)
+ return;
+
+ HRESULT hr = ::CoCreateInstance(CLSID_CUrlHistory,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_IUrlHistoryStg2,
+ getter_AddRefs(mIEHistory));
+ if (FAILED(hr))
+ return;
+
+ hr = mIEHistory->EnumUrls(getter_AddRefs(mURLEnumerator));
+ if (FAILED(hr))
+ return;
+}
+
+NS_IMETHODIMP
+nsIEHistoryEnumerator::HasMoreElements(bool* _retval)
+{
+ *_retval = false;
+
+ EnsureInitialized();
+ MOZ_ASSERT(mURLEnumerator, "Should have instanced an IE History URLEnumerator");
+ if (!mURLEnumerator)
+ return NS_OK;
+
+ STATURL statURL;
+ ULONG fetched;
+
+ // First argument is not implemented, so doesn't matter what we pass.
+ HRESULT hr = mURLEnumerator->Next(1, &statURL, &fetched);
+ if (FAILED(hr) || fetched != 1UL) {
+ // Reached the last entry.
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ if (statURL.pwcsUrl) {
+ nsDependentString url(statURL.pwcsUrl);
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
+ ::CoTaskMemFree(statURL.pwcsUrl);
+ if (NS_FAILED(rv)) {
+ // Got a corrupt or invalid URI, continue to the next entry.
+ return HasMoreElements(_retval);
+ }
+ }
+
+ nsDependentString title(statURL.pwcsTitle);
+
+ PRTime lastVisited = FileTimeToPRTime(&(statURL.ftLastVisited));
+
+ mCachedNextEntry = do_CreateInstance("@mozilla.org/hash-property-bag;1");
+ MOZ_ASSERT(mCachedNextEntry, "Should have instanced a new property bag");
+ if (mCachedNextEntry) {
+ mCachedNextEntry->SetPropertyAsInterface(NS_LITERAL_STRING("uri"), uri);
+ mCachedNextEntry->SetPropertyAsAString(NS_LITERAL_STRING("title"), title);
+ mCachedNextEntry->SetPropertyAsInt64(NS_LITERAL_STRING("time"), lastVisited);
+
+ *_retval = true;
+ }
+
+ if (statURL.pwcsTitle)
+ ::CoTaskMemFree(statURL.pwcsTitle);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIEHistoryEnumerator::GetNext(nsISupports** _retval)
+{
+ *_retval = nullptr;
+
+ if (!mCachedNextEntry)
+ return NS_ERROR_FAILURE;
+
+ NS_ADDREF(*_retval = mCachedNextEntry);
+ // Release the cached entry, so it can't be returned twice.
+ mCachedNextEntry = nullptr;
+
+ return NS_OK;
+}
--- a/browser/components/migration/src/nsIEHistoryEnumerator.h
+++ b/browser/components/migration/src/nsIEHistoryEnumerator.h
@@ -1,37 +1,37 @@
-/* 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/. */
-
-#ifndef iehistoryenumerator___h___
-#define iehistoryenumerator___h___
-
-#include <urlhist.h>
-
-#include "mozilla/Attributes.h"
-#include "nsISimpleEnumerator.h"
-#include "nsIWritablePropertyBag2.h"
-#include "nsAutoPtr.h"
-
-class nsIEHistoryEnumerator MOZ_FINAL : public nsISimpleEnumerator
-{
-public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSISIMPLEENUMERATOR
-
- nsIEHistoryEnumerator();
-
-private:
- ~nsIEHistoryEnumerator();
-
- /**
- * Initializes the history reader, if needed.
- */
- void EnsureInitialized();
-
- nsRefPtr<IUrlHistoryStg2> mIEHistory;
- nsRefPtr<IEnumSTATURL> mURLEnumerator;
-
- nsCOMPtr<nsIWritablePropertyBag2> mCachedNextEntry;
-};
-
-#endif
+/* 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/. */
+
+#ifndef iehistoryenumerator___h___
+#define iehistoryenumerator___h___
+
+#include <urlhist.h>
+
+#include "mozilla/Attributes.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIWritablePropertyBag2.h"
+#include "nsAutoPtr.h"
+
+class nsIEHistoryEnumerator MOZ_FINAL : public nsISimpleEnumerator
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ nsIEHistoryEnumerator();
+
+private:
+ ~nsIEHistoryEnumerator();
+
+ /**
+ * Initializes the history reader, if needed.
+ */
+ void EnsureInitialized();
+
+ nsRefPtr<IUrlHistoryStg2> mIEHistory;
+ nsRefPtr<IEnumSTATURL> mURLEnumerator;
+
+ nsCOMPtr<nsIWritablePropertyBag2> mCachedNextEntry;
+};
+
+#endif
--- a/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm
+++ b/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm
@@ -1,176 +1,176 @@
-/* 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/. */
-
-this.EXPORTED_SYMBOLS = ["RecentlyClosedTabsAndWindowsMenuUtils"];
-
-const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
-var Ci = Components.interfaces;
-var Cc = Components.classes;
-var Cr = Components.results;
-var Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
- "resource://gre/modules/PluralForm.jsm");
-
-let navigatorBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
-let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
-
-this.RecentlyClosedTabsAndWindowsMenuUtils = {
-
- /**
- * Builds up a document fragment of UI items for the recently closed tabs.
- * @param aWindow
- * The window that the tabs were closed in.
- * @param aTagName
- * The tag name that will be used when creating the UI items.
- * @param aPrefixRestoreAll (defaults to false)
- * Whether the 'restore all tabs' item is suffixed or prefixed to the list.
- * If suffixed (the default) a separator will be inserted before it.
- * @param aRestoreAllLabel (defaults to "menuRestoreAllTabs.label")
- * Which localizable string to use for the 'restore all tabs' item.
- * @returns A document fragment with UI items for each recently closed tab.
- */
- getTabsFragment: function(aWindow, aTagName, aPrefixRestoreAll=false,
- aRestoreAllLabel="menuRestoreAllTabs.label") {
- let doc = aWindow.document;
- let fragment = doc.createDocumentFragment();
- if (ss.getClosedTabCount(aWindow) != 0) {
- let closedTabs = JSON.parse(ss.getClosedTabData(aWindow));
- for (let i = 0; i < closedTabs.length; i++) {
- let element = doc.createElementNS(kNSXUL, aTagName);
- element.setAttribute("label", closedTabs[i].title);
- if (closedTabs[i].image) {
- setImage(closedTabs[i], element);
- }
- element.setAttribute("value", i);
- if (aTagName == "menuitem") {
- element.setAttribute("class", "menuitem-iconic bookmark-item menuitem-with-favicon");
- }
- element.setAttribute("oncommand", "undoCloseTab(" + i + ");");
-
- // Set the targetURI attribute so it will be shown in tooltip and trigger
- // onLinkHovered. SessionStore uses one-based indexes, so we need to
- // normalize them.
- let tabData = closedTabs[i].state;
- let activeIndex = (tabData.index || tabData.entries.length) - 1;
- if (activeIndex >= 0 && tabData.entries[activeIndex]) {
- element.setAttribute("targetURI", tabData.entries[activeIndex].url);
- }
-
- element.addEventListener("click", this._undoCloseMiddleClick, false);
- if (i == 0)
- element.setAttribute("key", "key_undoCloseTab");
- fragment.appendChild(element);
- }
-
- let restoreAllTabs = doc.createElementNS(kNSXUL, aTagName);
- restoreAllTabs.classList.add("restoreallitem");
- restoreAllTabs.setAttribute("label", navigatorBundle.GetStringFromName(aRestoreAllLabel));
- restoreAllTabs.setAttribute("oncommand",
- "for (var i = 0; i < " + closedTabs.length + "; i++) undoCloseTab();");
- if (!aPrefixRestoreAll) {
- fragment.appendChild(doc.createElementNS(kNSXUL, "menuseparator"));
- fragment.appendChild(restoreAllTabs);
- } else {
- fragment.insertBefore(restoreAllTabs, fragment.firstChild);
- }
- }
- return fragment;
- },
-
- /**
- * Builds up a document fragment of UI items for the recently closed windows.
- * @param aWindow
- * A window that can be used to create the elements and document fragment.
- * @param aTagName
- * The tag name that will be used when creating the UI items.
- * @param aPrefixRestoreAll (defaults to false)
- * Whether the 'restore all windows' item is suffixed or prefixed to the list.
- * If suffixed (the default) a separator will be inserted before it.
- * @param aRestoreAllLabel (defaults to "menuRestoreAllWindows.label")
- * Which localizable string to use for the 'restore all windows' item.
- * @returns A document fragment with UI items for each recently closed window.
- */
- getWindowsFragment: function(aWindow, aTagName, aPrefixRestoreAll=false,
- aRestoreAllLabel="menuRestoreAllWindows.label") {
- let closedWindowData = JSON.parse(ss.getClosedWindowData());
- let fragment = aWindow.document.createDocumentFragment();
- if (closedWindowData.length != 0) {
- let menuLabelString = navigatorBundle.GetStringFromName("menuUndoCloseWindowLabel");
- let menuLabelStringSingleTab =
- navigatorBundle.GetStringFromName("menuUndoCloseWindowSingleTabLabel");
-
- let doc = aWindow.document;
- for (let i = 0; i < closedWindowData.length; i++) {
- let undoItem = closedWindowData[i];
- let otherTabsCount = undoItem.tabs.length - 1;
- let label = (otherTabsCount == 0) ? menuLabelStringSingleTab
- : PluralForm.get(otherTabsCount, menuLabelString);
- let menuLabel = label.replace("#1", undoItem.title)
- .replace("#2", otherTabsCount);
- let item = doc.createElementNS(kNSXUL, aTagName);
- item.setAttribute("label", menuLabel);
- let selectedTab = undoItem.tabs[undoItem.selected - 1];
- if (selectedTab.image) {
- setImage(selectedTab, item);
- }
- if (aTagName == "menuitem") {
- item.setAttribute("class", "menuitem-iconic bookmark-item menuitem-with-favicon");
- }
- item.setAttribute("oncommand", "undoCloseWindow(" + i + ");");
-
- // Set the targetURI attribute so it will be shown in tooltip.
- // SessionStore uses one-based indexes, so we need to normalize them.
- let activeIndex = (selectedTab.index || selectedTab.entries.length) - 1;
- if (activeIndex >= 0 && selectedTab.entries[activeIndex])
- item.setAttribute("targetURI", selectedTab.entries[activeIndex].url);
-
- if (i == 0)
- item.setAttribute("key", "key_undoCloseWindow");
- fragment.appendChild(item);
- }
-
- // "Open All in Windows"
- let restoreAllWindows = doc.createElementNS(kNSXUL, aTagName);
- restoreAllWindows.classList.add("restoreallitem");
- restoreAllWindows.setAttribute("label", navigatorBundle.GetStringFromName(aRestoreAllLabel));
- restoreAllWindows.setAttribute("oncommand",
- "for (var i = 0; i < " + closedWindowData.length + "; i++) undoCloseWindow();");
- if (!aPrefixRestoreAll) {
- fragment.appendChild(doc.createElementNS(kNSXUL, "menuseparator"));
- fragment.appendChild(restoreAllWindows);
- } else {
- fragment.insertBefore(restoreAllWindows, fragment.firstChild);
- }
- }
- return fragment;
- },
-
-
- /**
- * Re-open a closed tab and put it to the end of the tab strip.
- * Used for a middle click.
- * @param aEvent
- * The event when the user clicks the menu item
- */
- _undoCloseMiddleClick: function(aEvent) {
- if (aEvent.button != 1)
- return;
-
- aEvent.view.undoCloseTab(aEvent.originalTarget.getAttribute("value"));
- aEvent.view.gBrowser.moveTabToEnd();
- },
-};
-
-function setImage(aItem, aElement) {
- let iconURL = aItem.image;
- // don't initiate a connection just to fetch a favicon (see bug 467828)
- if (/^https?:/.test(iconURL))
- iconURL = "moz-anno:favicon:" + iconURL;
- aElement.setAttribute("image", iconURL);
-}
+/* 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/. */
+
+this.EXPORTED_SYMBOLS = ["RecentlyClosedTabsAndWindowsMenuUtils"];
+
+const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+var Cr = Components.results;
+var Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+ "resource://gre/modules/PluralForm.jsm");
+
+let navigatorBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
+let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
+
+this.RecentlyClosedTabsAndWindowsMenuUtils = {
+
+ /**
+ * Builds up a document fragment of UI items for the recently closed tabs.
+ * @param aWindow
+ * The window that the tabs were closed in.
+ * @param aTagName
+ * The tag name that will be used when creating the UI items.
+ * @param aPrefixRestoreAll (defaults to false)
+ * Whether the 'restore all tabs' item is suffixed or prefixed to the list.
+ * If suffixed (the default) a separator will be inserted before it.
+ * @param aRestoreAllLabel (defaults to "menuRestoreAllTabs.label")
+ * Which localizable string to use for the 'restore all tabs' item.
+ * @returns A document fragment with UI items for each recently closed tab.
+ */
+ getTabsFragment: function(aWindow, aTagName, aPrefixRestoreAll=false,
+ aRestoreAllLabel="menuRestoreAllTabs.label") {
+ let doc = aWindow.document;
+ let fragment = doc.createDocumentFragment();
+ if (ss.getClosedTabCount(aWindow) != 0) {
+ let closedTabs = JSON.parse(ss.getClosedTabData(aWindow));
+ for (let i = 0; i < closedTabs.length; i++) {
+ let element = doc.createElementNS(kNSXUL, aTagName);
+ element.setAttribute("label", closedTabs[i].title);
+ if (closedTabs[i].image) {
+ setImage(closedTabs[i], element);
+ }
+ element.setAttribute("value", i);
+ if (aTagName == "menuitem") {
+ element.setAttribute("class", "menuitem-iconic bookmark-item menuitem-with-favicon");
+ }
+ element.setAttribute("oncommand", "undoCloseTab(" + i + ");");
+
+ // Set the targetURI attribute so it will be shown in tooltip and trigger
+ // onLinkHovered. SessionStore uses one-based indexes, so we need to
+ // normalize them.
+ let tabData = closedTabs[i].state;
+ let activeIndex = (tabData.index || tabData.entries.length) - 1;
+ if (activeIndex >= 0 && tabData.entries[activeIndex]) {
+ element.setAttribute("targetURI", tabData.entries[activeIndex].url);
+ }
+
+ element.addEventListener("click", this._undoCloseMiddleClick, false);
+ if (i == 0)
+ element.setAttribute("key", "key_undoCloseTab");
+ fragment.appendChild(element);
+ }
+
+ let restoreAllTabs = doc.createElementNS(kNSXUL, aTagName);
+ restoreAllTabs.classList.add("restoreallitem");
+ restoreAllTabs.setAttribute("label", navigatorBundle.GetStringFromName(aRestoreAllLabel));
+ restoreAllTabs.setAttribute("oncommand",
+ "for (var i = 0; i < " + closedTabs.length + "; i++) undoCloseTab();");
+ if (!aPrefixRestoreAll) {
+ fragment.appendChild(doc.createElementNS(kNSXUL, "menuseparator"));
+ fragment.appendChild(restoreAllTabs);
+ } else {
+ fragment.insertBefore(restoreAllTabs, fragment.firstChild);
+ }
+ }
+ return fragment;
+ },
+
+ /**
+ * Builds up a document fragment of UI items for the recently closed windows.
+ * @param aWindow
+ * A window that can be used to create the elements and document fragment.
+ * @param aTagName
+ * The tag name that will be used when creating the UI items.
+ * @param aPrefixRestoreAll (defaults to false)
+ * Whether the 'restore all windows' item is suffixed or prefixed to the list.
+ * If suffixed (the default) a separator will be inserted before it.
+ * @param aRestoreAllLabel (defaults to "menuRestoreAllWindows.label")
+ * Which localizable string to use for the 'restore all windows' item.
+ * @returns A document fragment with UI items for each recently closed window.
+ */
+ getWindowsFragment: function(aWindow, aTagName, aPrefixRestoreAll=false,
+ aRestoreAllLabel="menuRestoreAllWindows.label") {
+ let closedWindowData = JSON.parse(ss.getClosedWindowData());
+ let fragment = aWindow.document.createDocumentFragment();
+ if (closedWindowData.length != 0) {
+ let menuLabelString = navigatorBundle.GetStringFromName("menuUndoCloseWindowLabel");
+ let menuLabelStringSingleTab =
+ navigatorBundle.GetStringFromName("menuUndoCloseWindowSingleTabLabel");
+
+ let doc = aWindow.document;
+ for (let i = 0; i < closedWindowData.length; i++) {
+ let undoItem = closedWindowData[i];
+ let otherTabsCount = undoItem.tabs.length - 1;
+ let label = (otherTabsCount == 0) ? menuLabelStringSingleTab
+ : PluralForm.get(otherTabsCount, menuLabelString);
+ let menuLabel = label.replace("#1", undoItem.title)
+ .replace("#2", otherTabsCount);
+ let item = doc.createElementNS(kNSXUL, aTagName);
+ item.setAttribute("label", menuLabel);
+ let selectedTab = undoItem.tabs[undoItem.selected - 1];
+ if (selectedTab.image) {
+ setImage(selectedTab, item);
+ }
+ if (aTagName == "menuitem") {
+ item.setAttribute("class", "menuitem-iconic bookmark-item menuitem-with-favicon");
+ }
+ item.setAttribute("oncommand", "undoCloseWindow(" + i + ");");
+
+ // Set the targetURI attribute so it will be shown in tooltip.
+ // SessionStore uses one-based indexes, so we need to normalize them.
+ let activeIndex = (selectedTab.index || selectedTab.entries.length) - 1;
+ if (activeIndex >= 0 && selectedTab.entries[activeIndex])
+ item.setAttribute("targetURI", selectedTab.entries[activeIndex].url);
+
+ if (i == 0)
+ item.setAttribute("key", "key_undoCloseWindow");
+ fragment.appendChild(item);
+ }
+
+ // "Open All in Windows"
+ let restoreAllWindows = doc.createElementNS(kNSXUL, aTagName);
+ restoreAllWindows.classList.add("restoreallitem");
+ restoreAllWindows.setAttribute("label", navigatorBundle.GetStringFromName(aRestoreAllLabel));
+ restoreAllWindows.setAttribute("oncommand",
+ "for (var i = 0; i < " + closedWindowData.length + "; i++) undoCloseWindow();");
+ if (!aPrefixRestoreAll) {
+ fragment.appendChild(doc.createElementNS(kNSXUL, "menuseparator"));
+ fragment.appendChild(restoreAllWindows);
+ } else {
+ fragment.insertBefore(restoreAllWindows, fragment.firstChild);
+ }
+ }
+ return fragment;
+ },
+
+
+ /**
+ * Re-open a closed tab and put it to the end of the tab strip.
+ * Used for a middle click.
+ * @param aEvent
+ * The event when the user clicks the menu item
+ */
+ _undoCloseMiddleClick: function(aEvent) {
+ if (aEvent.button != 1)
+ return;
+
+ aEvent.view.undoCloseTab(aEvent.originalTarget.getAttribute("value"));
+ aEvent.view.gBrowser.moveTabToEnd();
+ },
+};
+
+function setImage(aItem, aElement) {
+ let iconURL = aItem.image;
+ // don't initiate a connection just to fetch a favicon (see bug 467828)
+ if (/^https?:/.test(iconURL))
+ iconURL = "moz-anno:favicon:" + iconURL;
+ aElement.setAttribute("image", iconURL);
+}
--- a/browser/devtools/inspector/selector-search.js
+++ b/browser/devtools/inspector/selector-search.js
@@ -1,492 +1,492 @@
-/* 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/. */
-
-"use strict";
-
-const promise = require("devtools/toolkit/deprecated-sync-thenables");
-
-loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup);
-
-// Maximum number of selector suggestions shown in the panel.
-const MAX_SUGGESTIONS = 15;
-
-/**
- * Converts any input box on a page to a CSS selector search and suggestion box.
- *
- * @constructor
- * @param InspectorPanel aInspector
- * The InspectorPanel whose `walker` attribute should be used for
- * document traversal.
- * @param nsiInputElement aInputNode
- * The input element to which the panel will be attached and from where
- * search input will be taken.
- */
-function SelectorSearch(aInspector, aInputNode) {
- this.inspector = aInspector;
- this.searchBox = aInputNode;
- this.panelDoc = this.searchBox.ownerDocument;
-
- // initialize variables.
- this._lastSearched = null;
- this._lastValidSearch = "";
- this._lastToLastValidSearch = null;
- this._searchResults = null;
- this._searchSuggestions = {};
- this._searchIndex = 0;
-
- // bind!
- this._showPopup = this._showPopup.bind(this);
- this._onHTMLSearch = this._onHTMLSearch.bind(this);
- this._onSearchKeypress = this._onSearchKeypress.bind(this);
- this._onListBoxKeypress = this._onListBoxKeypress.bind(this);
-
- // Options for the AutocompletePopup.
- let options = {
- panelId: "inspector-searchbox-panel",
- listBoxId: "searchbox-panel-listbox",
- autoSelect: true,
- position: "before_start",
- direction: "ltr",
- theme: "auto",
- onClick: this._onListBoxKeypress,
- onKeypress: this._onListBoxKeypress
- };
- this.searchPopup = new AutocompletePopup(this.panelDoc, options);
-
- // event listeners.
- this.searchBox.addEventListener("command", this._onHTMLSearch, true);
- this.searchBox.addEventListener("keypress", this._onSearchKeypress, true);
-
- // For testing, we need to be able to wait for the most recent node request
- // to finish. Tests can watch this promise for that.
- this._lastQuery = promise.resolve(null);
-}
-
-exports.SelectorSearch = SelectorSearch;
-
-SelectorSearch.prototype = {
-
- get walker() this.inspector.walker,
-
- // The possible states of the query.
- States: {
- CLASS: "class",
- ID: "id",
- TAG: "tag",
- },
-
- // The current state of the query.
- _state: null,
-
- // The query corresponding to last state computation.
- _lastStateCheckAt: null,
-
- /**
- * Computes the state of the query. State refers to whether the query
- * currently requires a class suggestion, or a tag, or an Id suggestion.
- * This getter will effectively compute the state by traversing the query
- * character by character each time the query changes.
- *
- * @example
- * '#f' requires an Id suggestion, so the state is States.ID
- * 'div > .foo' requires class suggestion, so state is States.CLASS
- */
- get state() {
- if (!this.searchBox || !this.searchBox.value) {
- return null;
- }
-
- let query = this.searchBox.value;
- if (this._lastStateCheckAt == query) {
- // If query is the same, return early.
- return this._state;
- }
- this._lastStateCheckAt = query;
-
- this._state = null;
- let subQuery = "";
- // Now we iterate over the query and decide the state character by character.
- // The logic here is that while iterating, the state can go from one to
- // another with some restrictions. Like, if the state is Class, then it can
- // never go to Tag state without a space or '>' character; Or like, a Class
- // state with only '.' cannot go to an Id state without any [a-zA-Z] after
- // the '.' which means that '.#' is a selector matching a class name '#'.
- // Similarily for '#.' which means a selctor matching an id '.'.
- for (let i = 1; i <= query.length; i++) {
- // Calculate the state.
- subQuery = query.slice(0, i);
- let [secondLastChar, lastChar] = subQuery.slice(-2);
- switch (this._state) {
- case null:
- // This will happen only in the first iteration of the for loop.
- lastChar = secondLastChar;
- case this.States.TAG:
- this._state = lastChar == "."
- ? this.States.CLASS
- : lastChar == "#"
- ? this.States.ID
- : this.States.TAG;
- break;
-
- case this.States.CLASS:
- if (subQuery.match(/[\.]+[^\.]*$/)[0].length > 2) {
- // Checks whether the subQuery has atleast one [a-zA-Z] after the '.'.
- this._state = (lastChar == " " || lastChar == ">")
- ? this.States.TAG
- : lastChar == "#"
- ? this.States.ID
- : this.States.CLASS;
- }
- break;
-
- case this.States.ID:
- if (subQuery.match(/[#]+[^#]*$/)[0].length > 2) {
- // Checks whether the subQuery has atleast one [a-zA-Z] after the '#'.
- this._state = (lastChar == " " || lastChar == ">")
- ? this.States.TAG
- : lastChar == "."
- ? this.States.CLASS
- : this.States.ID;
- }
- break;
- }
- }
- return this._state;
- },
-
- /**
- * Removes event listeners and cleans up references.
- */
- destroy: function() {
- // event listeners.
- this.searchBox.removeEventListener("command", this._onHTMLSearch, true);
- this.searchBox.removeEventListener("keypress", this._onSearchKeypress, true);
- this.searchPopup.destroy();
- this.searchPopup = null;
- this.searchBox = null;
- this.panelDoc = null;
- this._searchResults = null;
- this._searchSuggestions = null;
- },
-
- _selectResult: function(index) {
- return this._searchResults.item(index).then(node => {
- this.inspector.selection.setNodeFront(node, "selectorsearch");
- });
- },
-
- /**
- * The command callback for the input box. This function is automatically
- * invoked as the user is typing if the input box type is search.
- */
- _onHTMLSearch: function() {
- let query = this.searchBox.value;
- if (query == this._lastSearched) {
- return;
- }
- this._lastSearched = query;
- this._searchResults = [];
- this._searchIndex = 0;
-
- if (query.length == 0) {
- this._lastValidSearch = "";
- this.searchBox.removeAttribute("filled");
- this.searchBox.classList.remove("devtools-no-search-result");
- if (this.searchPopup.isOpen) {
- this.searchPopup.hidePopup();
- }
- return;
- }
-
- this.searchBox.setAttribute("filled", true);
- let queryList = null;
-
- this._lastQuery = this.walker.querySelectorAll(this.walker.rootNode, query).then(list => {
- return list;
- }, (err) => {
- // Failures are ok here, just use a null item list;
- return null;
- }).then(queryList => {
- // Value has changed since we started this request, we're done.
- if (query != this.searchBox.value) {
- if (queryList) {
- queryList.release();
- }
- return promise.reject(null);
- }
-
- this._searchResults = queryList || [];
- if (this._searchResults && this._searchResults.length > 0) {
- this._lastValidSearch = query;
- // Even though the selector matched atleast one node, there is still
- // possibility of suggestions.
- if (query.match(/[\s>+]$/)) {
- // If the query has a space or '>' at the end, create a selector to match
- // the children of the selector inside the search box by adding a '*'.
- this._lastValidSearch += "*";
- }
- else if (query.match(/[\s>+][\.#a-zA-Z][\.#>\s+]*$/)) {
- // If the query is a partial descendant selector which does not matches
- // any node, remove the last incomplete part and add a '*' to match
- // everything. For ex, convert 'foo > b' to 'foo > *' .
- let lastPart = query.match(/[\s>+][\.#a-zA-Z][^>\s+]*$/)[0];
- this._lastValidSearch = query.slice(0, -1 * lastPart.length + 1) + "*";
- }
-
- if (!query.slice(-1).match(/[\.#\s>+]/)) {
- // Hide the popup if we have some matching nodes and the query is not
- // ending with [.# >] which means that the selector is not at the
- // beginning of a new class, tag or id.
- if (this.searchPopup.isOpen) {
- this.searchPopup.hidePopup();
- }
- this.searchBox.classList.remove("devtools-no-search-result");
-
- return this._selectResult(0);
- }
- return this._selectResult(0).then(() => {
- this.searchBox.classList.remove("devtools-no-search-result");
- }).then(() => this.showSuggestions());
- }
- if (query.match(/[\s>+]$/)) {
- this._lastValidSearch = query + "*";
- }
- else if (query.match(/[\s>+][\.#a-zA-Z][\.#>\s+]*$/)) {
- let lastPart = query.match(/[\s+>][\.#a-zA-Z][^>\s+]*$/)[0];
- this._lastValidSearch = query.slice(0, -1 * lastPart.length + 1) + "*";
- }
- this.searchBox.classList.add("devtools-no-search-result");
- return this.showSuggestions();
- });
- },
-
- /**
- * Handles keypresses inside the input box.
- */
- _onSearchKeypress: function(aEvent) {
- let query = this.searchBox.value;
- switch(aEvent.keyCode) {
- case aEvent.DOM_VK_RETURN:
- if (query == this._lastSearched && this._searchResults) {
- this._searchIndex = (this._searchIndex + 1) % this._searchResults.length;
- }
- else {
- this._onHTMLSearch();
- return;
- }
- break;
-
- case aEvent.DOM_VK_UP:
- if (this.searchPopup.isOpen && this.searchPopup.itemCount > 0) {
- this.searchPopup.focus();
- if (this.searchPopup.selectedIndex == this.searchPopup.itemCount - 1) {
- this.searchPopup.selectedIndex =
- Math.max(0, this.searchPopup.itemCount - 2);
- }
- else {
- this.searchPopup.selectedIndex = this.searchPopup.itemCount - 1;
- }
- this.searchBox.value = this.searchPopup.selectedItem.label;
- }
- else if (--this._searchIndex < 0) {
- this._searchIndex = this._searchResults.length - 1;
- }
- break;
-
- case aEvent.DOM_VK_DOWN:
- if (this.searchPopup.isOpen && this.searchPopup.itemCount > 0) {
- this.searchPopup.focus();
- this.searchPopup.selectedIndex = 0;
- this.searchBox.value = this.searchPopup.selectedItem.label;
- }
- this._searchIndex = (this._searchIndex + 1) % this._searchResults.length;
- break;
-
- case aEvent.DOM_VK_TAB:
- if (this.searchPopup.isOpen &&
- this.searchPopup.getItemAtIndex(this.searchPopup.itemCount - 1)
- .preLabel == query) {
- this.searchPopup.selectedIndex = this.searchPopup.itemCount - 1;
- this.searchBox.value = this.searchPopup.selectedItem.label;
- this._onHTMLSearch();
- }
- break;
-
- case aEvent.DOM_VK_BACK_SPACE:
- case aEvent.DOM_VK_DELETE:
- // need to throw away the lastValidSearch.
- this._lastToLastValidSearch = null;
- // This gets the most complete selector from the query. For ex.
- // '.foo.ba' returns '.foo' , '#foo > .bar.baz' returns '#foo > .bar'
- // '.foo +bar' returns '.foo +' and likewise.
- this._lastValidSearch = (query.match(/(.*)[\.#][^\.# ]{0,}$/) ||
- query.match(/(.*[\s>+])[a-zA-Z][^\.# ]{0,}$/) ||
- ["",""])[1];
- return;
-
- default:
- return;
- }
-
- aEvent.preventDefault();
- aEvent.stopPropagation();
- if (this._searchResults && this._searchResults.length > 0) {
- this._lastQuery = this._selectResult(this._searchIndex);
- }
- },
-
- /**
- * Handles keypress and mouse click on the suggestions richlistbox.
- */
- _onListBoxKeypress: function(aEvent) {
- switch(aEvent.keyCode || aEvent.button) {
- case aEvent.DOM_VK_RETURN:
- case aEvent.DOM_VK_TAB:
- case 0: // left mouse button
- aEvent.stopPropagation();
- aEvent.preventDefault();
- this.searchBox.value = this.searchPopup.selectedItem.label;
- this.searchBox.focus();
- this._onHTMLSearch();
- break;
-
- case aEvent.DOM_VK_UP:
- if (this.searchPopup.selectedIndex == 0) {
- this.searchPopup.selectedIndex = -1;
- aEvent.stopPropagation();
- aEvent.preventDefault();
- this.searchBox.focus();
- }
- else {
- let index = this.searchPopup.selectedIndex;
- this.searchBox.value = this.searchPopup.getItemAtIndex(index - 1).label;
- }
- break;
-
- case aEvent.DOM_VK_DOWN:
- if (this.searchPopup.selectedIndex == this.searchPopup.itemCount - 1) {
- this.searchPopup.selectedIndex = -1;
- aEvent.stopPropagation();
- aEvent.preventDefault();
- this.searchBox.focus();
- }
- else {
- let index = this.searchPopup.selectedIndex;
- this.searchBox.value = this.searchPopup.getItemAtIndex(index + 1).label;
- }
- break;
-
- case aEvent.DOM_VK_BACK_SPACE:
- aEvent.stopPropagation();
- aEvent.preventDefault();
- this.searchBox.focus();
- if (this.searchBox.selectionStart > 0) {
- this.searchBox.value =
- this.searchBox.value.substring(0, this.searchBox.selectionStart - 1);
- }
- this._lastToLastValidSearch = null;
- let query = this.searchBox.value;
- this._lastValidSearch = (query.match(/(.*)[\.#][^\.# ]{0,}$/) ||
- query.match(/(.*[\s>+])[a-zA-Z][^\.# ]{0,}$/) ||
- ["",""])[1];
- this._onHTMLSearch();
- break;
- }
- },
-
- /**
- * Populates the suggestions list and show the suggestion popup.
- */
- _showPopup: function(aList, aFirstPart) {
- let total = 0;
- let query = this.searchBox.value;
- let toLowerCase = false;
- let items = [];
- // In case of tagNames, change the case to small.
- if (query.match(/.*[\.#][^\.#]{0,}$/) == null) {
- toLowerCase = true;
- }
- for (let [value, count] of aList) {
- // for cases like 'div ' or 'div >' or 'div+'
- if (query.match(/[\s>+]$/)) {
- value = query + value;
- }
- // for cases like 'div #a' or 'div .a' or 'div > d' and likewise
- else if (query.match(/[\s>+][\.#a-zA-Z][^\s>+\.#]*$/)) {
- let lastPart = query.match(/[\s>+][\.#a-zA-Z][^>\s+\.#]*$/)[0];
- value = query.slice(0, -1 * lastPart.length + 1) + value;
- }
- // for cases like 'div.class' or '#foo.bar' and likewise
- else if (query.match(/[a-zA-Z][#\.][^#\.\s+>]*$/)) {
- let lastPart = query.match(/[a-zA-Z][#\.][^#\.\s>+]*$/)[0];
- value = query.slice(0, -1 * lastPart.length + 1) + value;
- }
- let item = {
- preLabel: query,
- label: value,
- count: count
- };
- if (toLowerCase) {
- item.label = value.toLowerCase();
- }
- items.unshift(item);
- if (++total > MAX_SUGGESTIONS - 1) {
- break;
- }
- }
- if (total > 0) {
- this.searchPopup.setItems(items);
- this.searchPopup.openPopup(this.searchBox);
- }
- else {
- this.searchPopup.hidePopup();
- }
- },
-
- /**
- * Suggests classes,ids and tags based on the user input as user types in the
- * searchbox.
- */
- showSuggestions: function() {
- let query = this.searchBox.value;
- let firstPart = "";
- if (this.state == this.States.TAG) {
- // gets the tag that is being completed. For ex. 'div.foo > s' returns 's',
- // 'di' returns 'di' and likewise.
- firstPart = (query.match(/[\s>+]?([a-zA-Z]*)$/) || ["", query])[1];
- query = query.slice(0, query.length - firstPart.length);
- }
- else if (this.state == this.States.CLASS) {
- // gets the class that is being completed. For ex. '.foo.b' returns 'b'
- firstPart = query.match(/\.([^\.]*)$/)[1];
- query = query.slice(0, query.length - firstPart.length - 1);
- }
- else if (this.state == this.States.ID) {
- // gets the id that is being completed. For ex. '.foo#b' returns 'b'
- firstPart = query.match(/#([^#]*)$/)[1];
- query = query.slice(0, query.length - firstPart.length - 1);
- }
- // TODO: implement some caching so that over the wire request is not made
- // everytime.
- if (/[\s+>~]$/.test(query)) {
- query += "*";
- }
- this._currentSuggesting = query;
- return this.walker.getSuggestionsForQuery(query, firstPart, this.state).then(result => {
- if (this._currentSuggesting != result.query) {
- // This means that this response is for a previous request and the user
- // as since typed something extra leading to a new request.
- return;
- }
- this._lastToLastValidSearch = this._lastValidSearch;
- if (this.state == this.States.CLASS) {
- firstPart = "." + firstPart;
- }
- else if (this.state == this.States.ID) {
- firstPart = "#" + firstPart;
- }
- this._showPopup(result.suggestions, firstPart);
- });
- }
-};
+/* 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/. */
+
+"use strict";
+
+const promise = require("devtools/toolkit/deprecated-sync-thenables");
+
+loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup);
+
+// Maximum number of selector suggestions shown in the panel.
+const MAX_SUGGESTIONS = 15;
+
+/**
+ * Converts any input box on a page to a CSS selector search and suggestion box.
+ *
+ * @constructor
+ * @param InspectorPanel aInspector
+ * The InspectorPanel whose `walker` attribute should be used for
+ * document traversal.
+ * @param nsiInputElement aInputNode
+ * The input element to which the panel will be attached and from where
+ * search input will be taken.
+ */
+function SelectorSearch(aInspector, aInputNode) {
+ this.inspector = aInspector;
+ this.searchBox = aInputNode;
+ this.panelDoc = this.searchBox.ownerDocument;
+
+ // initialize variables.
+ this._lastSearched = null;
+ this._lastValidSearch = "";
+ this._lastToLastValidSearch = null;
+ this._searchResults = null;
+ this._searchSuggestions = {};
+ this._searchIndex = 0;
+
+ // bind!
+ this._showPopup = this._showPopup.bind(this);
+ this._onHTMLSearch = this._onHTMLSearch.bind(this);
+ this._onSearchKeypress = this._onSearchKeypress.bind(this);
+ this._onListBoxKeypress = this._onListBoxKeypress.bind(this);
+
+ // Options for the AutocompletePopup.
+ let options = {
+ panelId: "inspector-searchbox-panel",
+ listBoxId: "searchbox-panel-listbox",
+ autoSelect: true,
+ position: "before_start",
+ direction: "ltr",
+ theme: "auto",
+ onClick: this._onListBoxKeypress,
+ onKeypress: this._onListBoxKeypress
+ };
+ this.searchPopup = new AutocompletePopup(this.panelDoc, options);
+
+ // event listeners.
+ this.searchBox.addEventListener("command", this._onHTMLSearch, true);
+ this.searchBox.addEventListener("keypress", this._onSearchKeypress, true);
+
+ // For testing, we need to be able to wait for the most recent node request
+ // to finish. Tests can watch this promise for that.
+ this._lastQuery = promise.resolve(null);
+}
+
+exports.SelectorSearch = SelectorSearch;
+
+SelectorSearch.prototype = {
+
+ get walker() this.inspector.walker,
+
+ // The possible states of the query.
+ States: {
+ CLASS: "class",
+ ID: "id",
+ TAG: "tag",
+ },
+
+ // The current state of the query.
+ _state: null,
+
+ // The query corresponding to last state computation.
+ _lastStateCheckAt: null,
+
+ /**
+ * Computes the state of the query. State refers to whether the query
+ * currently requires a class suggestion, or a tag, or an Id suggestion.
+ * This getter will effectively compute the state by traversing the query
+ * character by character each time the query changes.
+ *
+ * @example
+ * '#f' requires an Id suggestion, so the state is States.ID
+ * 'div > .foo' requires class suggestion, so state is States.CLASS
+ */
+ get state() {
+ if (!this.searchBox || !this.searchBox.value) {
+ return null;
+ }
+
+ let query = this.searchBox.value;
+ if (this._lastStateCheckAt == query) {
+ // If query is the same, return early.
+ return this._state;
+ }
+ this._lastStateCheckAt = query;
+
+ this._state = null;
+ let subQuery = "";
+ // Now we iterate over the query and decide the state character by character.
+ // The logic here is that while iterating, the state can go from one to
+ // another with some restrictions. Like, if the state is Class, then it can
+ // never go to Tag state without a space or '>' character; Or like, a Class
+ // state with only '.' cannot go to an Id state without any [a-zA-Z] after
+ // the '.' which means that '.#' is a selector matching a class name '#'.
+ // Similarily for '#.' which means a selctor matching an id '.'.
+ for (let i = 1; i <= query.length; i++) {
+ // Calculate the state.
+ subQuery = query.slice(0, i);
+ let [secondLastChar, lastChar] = subQuery.slice(-2);
+ switch (this._state) {
+ case null:
+ // This will happen only in the first iteration of the for loop.
+ lastChar = secondLastChar;
+ case this.States.TAG:
+ this._state = lastChar == "."
+ ? this.States.CLASS
+ : lastChar == "#"
+ ? this.States.ID
+ : this.States.TAG;
+ break;
+
+ case this.States.CLASS:
+ if (subQuery.match(/[\.]+[^\.]*$/)[0].length > 2) {
+ // Checks whether the subQuery has atleast one [a-zA-Z] after the '.'.
+ this._state = (lastChar == " " || lastChar == ">")
+ ? this.States.TAG
+ : lastChar == "#"
+ ? this.States.ID
+ : this.States.CLASS;
+ }
+ break;
+
+ case this.States.ID:
+ if (subQuery.match(/[#]+[^#]*$/)[0].length > 2) {
+ // Checks whether the subQuery has atleast one [a-zA-Z] after the '#'.
+ this._state = (lastChar == " " || lastChar == ">")
+ ? this.States.TAG
+ : lastChar == "."
+ ? this.States.CLASS
+ : this.States.ID;
+ }
+ break;
+ }
+ }
+ return this._state;
+ },
+
+ /**
+ * Removes event listeners and cleans up references.
+ */
+ destroy: function() {
+ // event listeners.
+ this.searchBox.removeEventListener("command", this._onHTMLSearch, true);
+ this.searchBox.removeEventListener("keypress", this._onSearchKeypress, true);
+ this.searchPopup.destroy();
+ this.searchPopup = null;
+ this.searchBox = null;
+ this.panelDoc = null;
+ this._searchResults = null;
+ this._searchSuggestions = null;
+ },
+
+ _selectResult: function(index) {
+ return this._searchResults.item(index).then(node => {
+ this.inspector.selection.setNodeFront(node, "selectorsearch");
+ });
+ },
+
+ /**
+ * The command callback for the input box. This function is automatically
+ * invoked as the user is typing if the input box type is search.
+ */
+ _onHTMLSearch: function() {
+ let query = this.searchBox.value;
+ if (query == this._lastSearched) {
+ return;
+ }
+ this._lastSearched = query;
+ this._searchResults = [];
+ this._searchIndex = 0;
+
+ if (query.length == 0) {
+ this._lastValidSearch = "";
+ this.searchBox.removeAttribute("filled");
+ this.searchBox.classList.remove("devtools-no-search-result");
+ if (this.searchPopup.isOpen) {
+ this.searchPopup.hidePopup();
+ }
+ return;
+ }
+
+ this.searchBox.setAttribute("filled", true);
+ let queryList = null;
+
+ this._lastQuery = this.walker.querySelectorAll(this.walker.rootNode, query).then(list => {
+ return list;
+ }, (err) => {
+ // Failures are ok here, just use a null item list;
+ return null;
+ }).then(queryList => {
+ // Value has changed since we started this request, we're done.
+ if (query != this.searchBox.value) {
+ if (queryList) {
+ queryList.release();
+ }
+ return promise.reject(null);
+ }
+
+ this._searchResults = queryList || [];
+ if (this._searchResults && this._searchResults.length > 0) {
+ this._lastValidSearch = query;
+ // Even though the selector matched atleast one node, there is still
+ // possibility of suggestions.
+ if (query.match(/[\s>+]$/)) {
+ // If the query has a space or '>' at the end, create a selector to match
+ // the children of the selector inside the search box by adding a '*'.
+ this._lastValidSearch += "*";
+ }
+ else if (query.match(/[\s>+][\.#a-zA-Z][\.#>\s+]*$/)) {
+ // If the query is a partial descendant selector which does not matches
+ // any node, remove the last incomplete part and add a '*' to match
+ // everything. For ex, convert 'foo > b' to 'foo > *' .
+ let lastPart = query.match(/[\s>+][\.#a-zA-Z][^>\s+]*$/)[0];
+ this._lastValidSearch = query.slice(0, -1 * lastPart.length + 1) + "*";
+ }
+
+ if (!query.slice(-1).match(/[\.#\s>+]/)) {
+ // Hide the popup if we have some matching nodes and the query is not
+ // ending with [.# >] which means that the selector is not at the
+ // beginning of a new class, tag or id.
+ if (this.searchPopup.isOpen) {
+ this.searchPopup.hidePopup();
+ }
+ this.searchBox.classList.remove("devtools-no-search-result");
+
+ return this._selectResult(0);
+ }
+ return this._selectResult(0).then(() => {
+ this.searchBox.classList.remove("devtools-no-search-result");
+ }).then(() => this.showSuggestions());
+ }
+ if (query.match(/[\s>+]$/)) {
+ this._lastValidSearch = query + "*";
+ }
+ else if (query.match(/[\s>+][\.#a-zA-Z][\.#>\s+]*$/)) {
+ let lastPart = query.match(/[\s+>][\.#a-zA-Z][^>\s+]*$/)[0];
+ this._lastValidSearch = query.slice(0, -1 * lastPart.length + 1) + "*";
+ }
+ this.searchBox.classList.add("devtools-no-search-result");
+ return this.showSuggestions();
+ });
+ },
+
+ /**
+ * Handles keypresses inside the input box.
+ */
+ _onSearchKeypress: function(aEvent) {
+ let query = this.searchBox.value;
+ switch(aEvent.keyCode) {
+ case aEvent.DOM_VK_RETURN:
+ if (query == this._lastSearched && this._searchResults) {
+ this._searchIndex = (this._searchIndex + 1) % this._searchResults.length;
+ }
+ else {
+ this._onHTMLSearch();
+ return;
+ }
+ break;
+
+ case aEvent.DOM_VK_UP:
+ if (this.searchPopup.isOpen && this.searchPopup.itemCount > 0) {
+ this.searchPopup.focus();
+ if (this.searchPopup.selectedIndex == this.searchPopup.itemCount - 1) {
+ this.searchPopup.selectedIndex =
+ Math.max(0, this.searchPopup.itemCount - 2);
+ }
+ else {
+ this.searchPopup.selectedIndex = this.searchPopup.itemCount - 1;
+ }
+ this.searchBox.value = this.searchPopup.selectedItem.label;
+ }
+ else if (--this._searchIndex < 0) {
+ this._searchIndex = this._searchResults.length - 1;
+ }
+ break;
+
+ case aEvent.DOM_VK_DOWN:
+ if (this.searchPopup.isOpen && this.searchPopup.itemCount > 0) {
+ this.searchPopup.focus();
+ this.searchPopup.selectedIndex = 0;
+ this.searchBox.value = this.searchPopup.selectedItem.label;
+ }
+ this._searchIndex = (this._searchIndex + 1) % this._searchResults.length;
+ break;
+
+ case aEvent.DOM_VK_TAB:
+ if (this.searchPopup.isOpen &&
+ this.searchPopup.getItemAtIndex(this.searchPopup.itemCount - 1)
+ .preLabel == query) {
+ this.searchPopup.selectedIndex = this.searchPopup.itemCount - 1;
+ this.searchBox.value = this.searchPopup.selectedItem.label;
+ this._onHTMLSearch();
+ }
+ break;
+
+ case aEvent.DOM_VK_BACK_SPACE:
+ case aEvent.DOM_VK_DELETE:
+ // need to throw away the lastValidSearch.
+ this._lastToLastValidSearch = null;
+ // This gets the most complete selector from the query. For ex.
+ // '.foo.ba' returns '.foo' , '#foo > .bar.baz' returns '#foo > .bar'
+ // '.foo +bar' returns '.foo +' and likewise.
+ this._lastValidSearch = (query.match(/(.*)[\.#][^\.# ]{0,}$/) ||
+ query.match(/(.*[\s>+])[a-zA-Z][^\.# ]{0,}$/) ||
+ ["",""])[1];
+ return;
+
+ default:
+ return;
+ }
+
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ if (this._searchResults && this._searchResults.length > 0) {
+ this._lastQuery = this._selectResult(this._searchIndex);
+ }
+ },
+
+ /**
+ * Handles keypress and mouse click on the suggestions richlistbox.
+ */
+ _onListBoxKeypress: function(aEvent) {
+ switch(aEvent.keyCode || aEvent.button) {
+ case aEvent.DOM_VK_RETURN:
+ case aEvent.DOM_VK_TAB:
+ case 0: // left mouse button
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ this.searchBox.value = this.searchPopup.selectedItem.label;
+ this.searchBox.focus();
+ this._onHTMLSearch();
+ break;
+
+ case aEvent.DOM_VK_UP:
+ if (this.searchPopup.selectedIndex == 0) {
+ this.searchPopup.selectedIndex = -1;
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ this.searchBox.focus();
+ }
+ else {
+ let index = this.searchPopup.selectedIndex;
+ this.searchBox.value = this.searchPopup.getItemAtIndex(index - 1).label;
+ }
+ break;
+
+ case aEvent.DOM_VK_DOWN:
+ if (this.searchPopup.selectedIndex == this.searchPopup.itemCount - 1) {
+ this.searchPopup.selectedIndex = -1;
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ this.searchBox.focus();
+ }
+ else {
+ let index = this.searchPopup.selectedIndex;
+ this.searchBox.value = this.searchPopup.getItemAtIndex(index + 1).label;
+ }
+ break;
+
+ case aEvent.DOM_VK_BACK_SPACE:
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ this.searchBox.focus();
+ if (this.searchBox.selectionStart > 0) {
+ this.searchBox.value =
+ this.searchBox.value.substring(0, this.searchBox.selectionStart - 1);
+ }
+ this._lastToLastValidSearch = null;
+ let query = this.searchBox.value;
+ this._lastValidSearch = (query.match(/(.*)[\.#][^\.# ]{0,}$/) ||
+ query.match(/(.*[\s>+])[a-zA-Z][^\.# ]{0,}$/) ||
+ ["",""])[1];
+ this._onHTMLSearch();
+ break;
+ }
+ },
+
+ /**
+ * Populates the suggestions list and show the suggestion popup.
+ */
+ _showPopup: function(aList, aFirstPart) {
+ let total = 0;
+ let query = this.searchBox.value;
+ let toLowerCase = false;
+ let items = [];
+ // In case of tagNames, change the case to small.
+ if (query.match(/.*[\.#][^\.#]{0,}$/) == null) {
+ toLowerCase = true;
+ }
+ for (let [value, count] of aList) {
+ // for cases like 'div ' or 'div >' or 'div+'
+ if (query.match(/[\s>+]$/)) {
+ value = query + value;
+ }
+ // for cases like 'div #a' or 'div .a' or 'div > d' and likewise
+ else if (query.match(/[\s>+][\.#a-zA-Z][^\s>+\.#]*$/)) {
+ let lastPart = query.match(/[\s>+][\.#a-zA-Z][^>\s+\.#]*$/)[0];
+ value = query.slice(0, -1 * lastPart.length + 1) + value;
+ }
+ // for cases like 'div.class' or '#foo.bar' and likewise
+ else if (query.match(/[a-zA-Z][#\.][^#\.\s+>]*$/)) {
+ let lastPart = query.match(/[a-zA-Z][#\.][^#\.\s>+]*$/)[0];
+ value = query.slice(0, -1 * lastPart.length + 1) + value;
+ }
+ let item = {
+ preLabel: query,
+ label: value,
+ count: count
+ };
+ if (toLowerCase) {
+ item.label = value.toLowerCase();
+ }
+ items.unshift(item);
+ if (++total > MAX_SUGGESTIONS - 1) {
+ break;
+ }
+ }
+ if (total > 0) {
+ this.searchPopup.setItems(items);
+ this.searchPopup.openPopup(this.searchBox);
+ }
+ else {
+ this.searchPopup.hidePopup();
+ }
+ },
+
+ /**
+ * Suggests classes,ids and tags based on the user input as user types in the
+ * searchbox.
+ */
+ showSuggestions: function() {
+ let query = this.searchBox.value;
+ let firstPart = "";
+ if (this.state == this.States.TAG) {
+ // gets the tag that is being completed. For ex. 'div.foo > s' returns 's',
+ // 'di' returns 'di' and likewise.
+ firstPart = (query.match(/[\s>+]?([a-zA-Z]*)$/) || ["", query])[1];
+ query = query.slice(0, query.length - firstPart.length);
+ }
+ else if (this.state == this.States.CLASS) {
+ // gets the class that is being completed. For ex. '.foo.b' returns 'b'
+ firstPart = query.match(/\.([^\.]*)$/)[1];
+ query = query.slice(0, query.length - firstPart.length - 1);
+ }
+ else if (this.state == this.States.ID) {
+ // gets the id that is being completed. For ex. '.foo#b' returns 'b'
+ firstPart = query.match(/#([^#]*)$/)[1];
+ query = query.slice(0, query.length - firstPart.length - 1);
+ }
+ // TODO: implement some caching so that over the wire request is not made
+ // everytime.
+ if (/[\s+>~]$/.test(query)) {
+ query += "*";
+ }
+ this._currentSuggesting = query;
+ return this.walker.getSuggestionsForQuery(query, firstPart, this.state).then(result => {
+ if (this._currentSuggesting != result.query) {
+ // This means that this response is for a previous request and the user
+ // as since typed something extra leading to a new request.
+ return;
+ }
+ this._lastToLastValidSearch = this._lastValidSearch;
+ if (this.state == this.States.CLASS) {
+ firstPart = "." + firstPart;
+ }
+ else if (this.state == this.States.ID) {
+ firstPart = "#" + firstPart;
+ }
+ this._showPopup(result.suggestions, firstPart);
+ });
+ }
+};
--- a/browser/metro/base/content/bindings/circularprogress.xml
+++ b/browser/metro/base/content/bindings/circularprogress.xml
@@ -1,134 +1,134 @@
-<?xml version="1.0"?>
-
-<!-- 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/. -->
-
-<bindings xmlns="http://www.mozilla.org/xbl"
- xmlns:xbl="http://www.mozilla.org/xbl"
- xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
- xmlns:html="http://www.w3.org/1999/xhtml">
- <binding id="circular-progress-indicator">
- <resources>
- <stylesheet src="chrome://browser/skin/circularprogress.css"/>
- </resources>
-
- <content>
- <xul:stack>
- <xul:toolbarbutton anonid="progressButton"
- class="circularprogressindicator-progressButton"/>
- <html:div anonid="progressTrack"
- xbl:inherits="progress"
- class="circularprogressindicator-progressTrack">
- </html:div>
- <html:canvas anonid="progressRing"
- xbl:inherits="progress"
- class="circularprogressindicator-progressRing"
- width="40"
- height="40">
- </html:canvas>
- <html:div anonid="progressNotification"
- xbl:inherits="progress"
- class="circularprogressindicator-progressNotification">
- </html:div>
- </xul:stack>
- </content>
-
- <implementation>
- <field name="_progressCanvas">
- document.getAnonymousElementByAttribute(this, "anonid", "progressRing");
- </field>
- <field name="_progressNotification">
- document.getAnonymousElementByAttribute(this, "anonid",
- "progressNotification");
- </field>
- <field name="_progressCircleCtx">null</field>
- <field name="_img">null</field>
- <constructor>
- <![CDATA[
- this._progressCircleCtx = this._progressCanvas.getContext('2d');
- ]]>
- </constructor>
- <method name="updateProgress">
- <parameter name="percentComplete"/>
- <body>
- <![CDATA[
- const PROGRESS_RING_IMG = "chrome://browser/skin/images/progresscircle.png";
-
- // show ring background even if % is 0.
- this.setAttribute("progress", percentComplete);
-
- let startAngle = 1.5 * Math.PI;
- let endAngle = startAngle + (2 * Math.PI * (percentComplete / 100));
-
- if (!this._img) {
- this._img = new Image();
- this._img.onload = () => {
- this.updateProgress(this.getAttribute("progress"))
- }
- this._img.src = PROGRESS_RING_IMG;
- }
- else if (this._img.complete) {
- let ctx = this._progressCircleCtx;
- ctx.clearRect(0, 0,
- this._progressCanvas.width, this._progressCanvas.height);
-
- // Save the state, so we can undo the clipping
- ctx.save();
-
- ctx.beginPath();
- let center = this._progressCanvas.width / 2;
- ctx.arc(center, center, center, startAngle, endAngle, false);
- ctx.lineTo(center, center);
- ctx.closePath();
- ctx.clip();
-
- // Draw circle image.
- ctx.translate(center, center);
- ctx.rotate(endAngle);
- ctx.drawImage(this._img, -center, -center);
-
- ctx.restore();
- } else {
- // Image is still loading
- }
- return [startAngle, endAngle];
- ]]>
- </body>
- </method>
- <method name="reset">
- <body>
- <![CDATA[
- if(this._img && !this._img.complete) {
- // cancel any pending updateProgress
- this._img.onload = () => {};
- }
- this._progressCircleCtx.clearRect(0, 0,
- this._progressCanvas.width, this._progressCanvas.height);
- this.removeAttribute("progress");
- ]]>
- </body>
- </method>
- <method name="notify">
- <body>
- <![CDATA[
- this.addEventListener("transitionend", this._onNotificationEnd);
-
- this._progressNotification.classList.add(
- "progressNotification-active");
- ]]>
- </body>
- </method>
- <method name="_onNotificationEnd">
- <body>
- <![CDATA[
- this.removeEventListener("transitionend", this._onNotificationEnd);
-
- this._progressNotification.classList.remove(
- "progressNotification-active");
- ]]>
- </body>
- </method>
- </implementation>
- </binding>
-</bindings>
+<?xml version="1.0"?>
+
+<!-- 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/. -->
+
+<bindings xmlns="http://www.mozilla.org/xbl"
+ xmlns:xbl="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml">
+ <binding id="circular-progress-indicator">
+ <resources>
+ <stylesheet src="chrome://browser/skin/circularprogress.css"/>
+ </resources>
+
+ <content>
+ <xul:stack>
+ <xul:toolbarbutton anonid="progressButton"
+ class="circularprogressindicator-progressButton"/>
+ <html:div anonid="progressTrack"
+ xbl:inherits="progress"
+ class="circularprogressindicator-progressTrack">
+ </html:div>
+ <html:canvas anonid="progressRing"
+ xbl:inherits="progress"
+ class="circularprogressindicator-progressRing"
+ width="40"
+ height="40">
+ </html:canvas>
+ <html:div anonid="progressNotification"
+ xbl:inherits="progress"
+ class="circularprogressindicator-progressNotification">
+ </html:div>
+ </xul:stack>
+ </content>
+
+ <implementation>
+ <field name="_progressCanvas">
+ document.getAnonymousElementByAttribute(this, "anonid", "progressRing");
+ </field>
+ <field name="_progressNotification">
+ document.getAnonymousElementByAttribute(this, "anonid",
+ "progressNotification");
+ </field>
+ <field name="_progressCircleCtx">null</field>
+ <field name="_img">null</field>
+ <constructor>
+ <![CDATA[
+ this._progressCircleCtx = this._progressCanvas.getContext('2d');
+ ]]>
+ </constructor>
+ <method name="updateProgress">
+ <parameter name="percentComplete"/>
+ <body>
+ <![CDATA[
+ const PROGRESS_RING_IMG = "chrome://browser/skin/images/progresscircle.png";
+
+ // show ring background even if % is 0.
+ this.setAttribute("progress", percentComplete);
+
+ let startAngle = 1.5 * Math.PI;
+ let endAngle = startAngle + (2 * Math.PI * (percentComplete / 100));
+
+ if (!this._img) {
+ this._img = new Image();
+ this._img.onload = () => {
+ this.updateProgress(this.getAttribute("progress"))
+ }
+ this._img.src = PROGRESS_RING_IMG;
+ }
+ else if (this._img.complete) {
+ let ctx = this._progressCircleCtx;
+ ctx.clearRect(0, 0,
+ this._progressCanvas.width, this._progressCanvas.height);
+
+ // Save the state, so we can undo the clipping
+ ctx.save();
+
+ ctx.beginPath();
+ let center = this._progressCanvas.width / 2;
+ ctx.arc(center, center, center, startAngle, endAngle, false);
+ ctx.lineTo(center, center);
+ ctx.closePath();
+ ctx.clip();
+
+ // Draw circle image.
+ ctx.translate(center, center);
+ ctx.rotate(endAngle);
+ ctx.drawImage(this._img, -center, -center);
+
+ ctx.restore();
+ } else {
+ // Image is still loading
+ }
+ return [startAngle, endAngle];
+ ]]>
+ </body>
+ </method>
+ <method name="reset">
+ <body>
+ <![CDATA[
+ if(this._img && !this._img.complete) {
+ // cancel any pending updateProgress
+ this._img.onload = () => {};
+ }
+ this._progressCircleCtx.clearRect(0, 0,
+ this._progressCanvas.width, this._progressCanvas.height);
+ this.removeAttribute("progress");
+ ]]>
+ </body>
+ </method>
+ <method name="notify">
+ <body>
+ <![CDATA[
+ this.addEventListener("transitionend", this._onNotificationEnd);
+
+ this._progressNotification.classList.add(
+ "progressNotification-active");
+ ]]>
+ </body>
+ </method>
+ <method name="_onNotificationEnd">
+ <body>
+ <![CDATA[
+ this.removeEventListener("transitionend", this._onNotificationEnd);
+
+ this._progressNotification.classList.remove(
+ "progressNotification-active");
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+</bindings>
--- a/browser/metro/base/content/commandUtil.js
+++ b/browser/metro/base/content/commandUtil.js
@@ -1,164 +1,164 @@
-/* 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/. */
-
-
-/**
- * Command Updater
- */
-var CommandUpdater = {
- /**
- * Gets a controller that can handle a particular command.
- * @param command
- * A command to locate a controller for, preferring controllers that
- * show the command as enabled.
- * @returns In this order of precedence:
- * - the first controller supporting the specified command
- * associated with the focused element that advertises the
- * command as ENABLED
- * - the first controller supporting the specified command
- * associated with the global window that advertises the
- * command as ENABLED
- * - the first controller supporting the specified command
- * associated with the focused element
- * - the first controller supporting the specified command
- * associated with the global window
- */
- _getControllerForCommand: function(command) {
- try {
- var controller = top.document.commandDispatcher.getControllerForCommand(command);
- if (controller && controller.isCommandEnabled(command))
- return controller;
- }
- catch(e) {
- }
- var controllerCount = window.controllers.getControllerCount();
- for (var i = 0; i < controllerCount; ++i) {
- var current = window.controllers.getControllerAt(i);
- try {
- if (current.supportsCommand(command) && current.isCommandEnabled(command))
- return current;
- }
- catch (e) {
- }
- }
- return controller || window.controllers.getControllerForCommand(command);
- },
-
- /**
- * Updates the state of a XUL <command> element for the specified command
- * depending on its state.
- * @param command
- * The name of the command to update the XUL <command> element for
- */
- updateCommand: function(command) {
- var enabled = false;
- try {
- var controller = this._getControllerForCommand(command);
- if (controller) {
- enabled = controller.isCommandEnabled(command);
- }
- }
- catch(ex) { }
-
- this.enableCommand(command, enabled);
- },
-
- /**
- * Updates the state of a XUL <command> element for the specified command
- * depending on its state.
- * @param command
- * The name of the command to update the XUL <command> element for
- */
- updateCommands: function(_commands) {
- var commands = _commands.split(",");
- for (var command in commands) {
- this.updateCommand(commands[command]);
- }
- },
-
- /**
- * Enables or disables a XUL <command> element.
- * @param command
- * The name of the command to enable or disable
- * @param enabled
- * true if the command should be enabled, false otherwise.
- */
- enableCommand: function(command, enabled) {
- var element = document.getElementById(command);
- if (!element)
- return;
- if (enabled)
- element.removeAttribute("disabled");
- else
- element.setAttribute("disabled", "true");
- },
-
- /**
- * Performs the action associated with a specified command using the most
- * relevant controller.
- * @param command
- * The command to perform.
- */
- doCommand: function(command) {
- var controller = this._getControllerForCommand(command);
- if (!controller)
- return;
- controller.doCommand(command);
- },
-
- /**
- * Changes the label attribute for the specified command.
- * @param command
- * The command to update.
- * @param labelAttribute
- * The label value to use.
- */
- setMenuValue: function(command, labelAttribute) {
- var commandNode = top.document.getElementById(command);
- if (commandNode)
- {
- var label = commandNode.getAttribute(labelAttribute);
- if ( label )
- commandNode.setAttribute('label', label);
- }
- },
-
- /**
- * Changes the accesskey attribute for the specified command.
- * @param command
- * The command to update.
- * @param valueAttribute
- * The value attribute to use.
- */
- setAccessKey: function(command, valueAttribute) {
- var commandNode = top.document.getElementById(command);
- if (commandNode)
- {
- var value = commandNode.getAttribute(valueAttribute);
- if ( value )
- commandNode.setAttribute('accesskey', value);
- }
- },
-
- /**
- * Inform all the controllers attached to a node that an event has occurred
- * (e.g. the tree controllers need to be informed of blur events so that they can change some of the
- * menu items back to their default values)
- * @param node
- * The node receiving the event
- * @param event
- * The event.
- */
- onEvent: function(node, event) {
- var numControllers = node.controllers.getControllerCount();
- var controller;
-
- for ( var controllerIndex = 0; controllerIndex < numControllers; controllerIndex++ )
- {
- controller = node.controllers.getControllerAt(controllerIndex);
- if ( controller )
- controller.onEvent(event);
- }
- }
-};
+/* 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/. */
+
+
+/**
+ * Command Updater
+ */
+var CommandUpdater = {
+ /**
+ * Gets a controller that can handle a particular command.
+ * @param command
+ * A command to locate a controller for, preferring controllers that
+ * show the command as enabled.
+ * @returns In this order of precedence:
+ * - the first controller supporting the specified command
+ * associated with the focused element that advertises the
+ * command as ENABLED
+ * - the first controller supporting the specified command
+ * associated with the global window that advertises the
+ * command as ENABLED
+ * - the first controller supporting the specified command
+ * associated with the focused element
+ * - the first controller supporting the specified command
+ * associated with the global window
+ */
+ _getControllerForCommand: function(command) {
+ try {
+ var controller = top.document.commandDispatcher.getControllerForCommand(command);
+ if (controller && controller.isCommandEnabled(command))
+ return controller;
+ }
+ catch(e) {
+ }
+ var controllerCount = window.controllers.getControllerCount();
+ for (var i = 0; i < controllerCount; ++i) {
+ var current = window.controllers.getControllerAt(i);
+ try {
+ if (current.supportsCommand(command) && current.isCommandEnabled(command))
+ return current;
+ }
+ catch (e) {
+ }
+ }
+ return controller || window.controllers.getControllerForCommand(command);
+ },
+
+ /**
+ * Updates the state of a XUL <command> element for the specified command
+ * depending on its state.
+ * @param command
+ * The name of the command to update the XUL <command> element for
+ */
+ updateCommand: function(command) {
+ var enabled = false;
+ try {
+ var controller = this._getControllerForCommand(command);
+ if (controller) {
+ enabled = controller.isCommandEnabled(command);
+ }
+ }
+ catch(ex) { }
+
+ this.enableCommand(command, enabled);
+ },
+
+ /**
+ * Updates the state of a XUL <command> element for the specified command
+ * depending on its state.
+ * @param command
+ * The name of the command to update the XUL <command> element for
+ */
+ updateCommands: function(_commands) {
+ var commands = _commands.split(",");
+ for (var command in commands) {
+ this.updateCommand(commands[command]);
+ }
+ },
+
+ /**
+ * Enables or disables a XUL <command> element.
+ * @param command
+ * The name of the command to enable or disable
+ * @param enabled
+ * true if the command should be enabled, false otherwise.
+ */
+ enableCommand: function(command, enabled) {
+ var element = document.getElementById(command);
+ if (!element)
+ return;
+ if (enabled)
+ element.removeAttribute("disabled");
+ else
+ element.setAttribute("disabled", "true");
+ },
+
+ /**
+ * Performs the action associated with a specified command using the most
+ * relevant controller.
+ * @param command
+ * The command to perform.
+ */
+ doCommand: function(command) {
+ var controller = this._getControllerForCommand(command);
+ if (!controller)
+ return;
+ controller.doCommand(command);
+ },
+
+ /**
+ * Changes the label attribute for the specified command.
+ * @param command
+ * The command to update.
+ * @param labelAttribute
+ * The label value to use.
+ */
+ setMenuValue: function(command, labelAttribute) {
+ var commandNode = top.document.getElementById(command);
+ if (commandNode)
+ {
+ var label = commandNode.getAttribute(labelAttribute);
+ if ( label )
+ commandNode.setAttribute('label', label);
+ }
+ },
+
+ /**
+ * Changes the accesskey attribute for the specified command.
+ * @param command
+ * The command to update.
+ * @param valueAttribute
+ * The value attribute to use.
+ */
+ setAccessKey: function(command, valueAttribute) {
+ var commandNode = top.document.getElementById(command);
+ if (commandNode)
+ {
+ var value = commandNode.getAttribute(valueAttribute);
+ if ( value )
+ commandNode.setAttribute('accesskey', value);
+ }
+ },
+
+ /**
+ * Inform all the controllers attached to a node that an event has occurred
+ * (e.g. the tree controllers need to be informed of blur events so that they can change some of the
+ * menu items back to their default values)
+ * @param node
+ * The node receiving the event
+ * @param event
+ * The event.
+ */
+ onEvent: function(node, event) {
+ var numControllers = node.controllers.getControllerCount();
+ var controller;
+
+ for ( var controllerIndex = 0; controllerIndex < numControllers; controllerIndex++ )
+ {
+ controller = node.controllers.getControllerAt(controllerIndex);
+ if ( controller )
+ controller.onEvent(event);
+ }
+ }
+};
--- a/browser/metro/base/content/helperui/ItemPinHelper.js
+++ b/browser/metro/base/content/helperui/ItemPinHelper.js
@@ -1,59 +1,59 @@
-// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
-/* 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/. */
-
-"use strict";
-
-function ItemPinHelper(aUnpinnedPrefName) {
- this._prefKey = aUnpinnedPrefName;
-}
-
-// Cache preferences on a static variable shared
-// by all instances registered to the same pref key.
-ItemPinHelper._prefValue = {};
-
-ItemPinHelper.prototype = {
- _getPrefValue: function _getPrefValue() {
- if (ItemPinHelper._prefValue[this._prefKey])
- return ItemPinHelper._prefValue[this._prefKey];
-
- try {
- // getComplexValue throws if pref never set. Really.
- let prefValue = Services.prefs.getComplexValue(this._prefKey, Ci.nsISupportsString);
- ItemPinHelper._prefValue[this._prefKey] = JSON.parse(prefValue.data);
- } catch(e) {
- ItemPinHelper._prefValue[this._prefKey] = [];
- }
-
- return ItemPinHelper._prefValue[this._prefKey];
- },
-
- _setPrefValue: function _setPrefValue(aNewValue) {
- let stringified = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
- stringified.data = JSON.stringify(aNewValue);
-
- Services.prefs.setComplexValue(this._prefKey, Ci.nsISupportsString, stringified);
- ItemPinHelper._prefValue[this._prefKey] = aNewValue;
- },
-
- isPinned: function isPinned(aItemId) {
- // Bookmarks are visible on StartUI (pinned) by default
- return this._getPrefValue().indexOf(aItemId) === -1;
- },
-
- setUnpinned: function setPinned(aItemId) {
- let unpinned = this._getPrefValue();
- unpinned.push(aItemId);
- this._setPrefValue(unpinned);
- },
-
- setPinned: function unsetPinned(aItemId) {
- let unpinned = this._getPrefValue();
-
- let index = unpinned.indexOf(aItemId);
- unpinned.splice(index, 1);
-
- this._setPrefValue(unpinned);
- },
-}
+// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
+/* 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/. */
+
+"use strict";
+
+function ItemPinHelper(aUnpinnedPrefName) {
+ this._prefKey = aUnpinnedPrefName;
+}
+
+// Cache preferences on a static variable shared
+// by all instances registered to the same pref key.
+ItemPinHelper._prefValue = {};
+
+ItemPinHelper.prototype = {
+ _getPrefValue: function _getPrefValue() {
+ if (ItemPinHelper._prefValue[this._prefKey])
+ return ItemPinHelper._prefValue[this._prefKey];
+
+ try {
+ // getComplexValue throws if pref never set. Really.
+ let prefValue = Services.prefs.getComplexValue(this._prefKey, Ci.nsISupportsString);
+ ItemPinHelper._prefValue[this._prefKey] = JSON.parse(prefValue.data);
+ } catch(e) {
+ ItemPinHelper._prefValue[this._prefKey] = [];
+ }
+
+ return ItemPinHelper._prefValue[this._prefKey];
+ },
+
+ _setPrefValue: function _setPrefValue(aNewValue) {
+ let stringified = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+ stringified.data = JSON.stringify(aNewValue);
+
+ Services.prefs.setComplexValue(this._prefKey, Ci.nsISupportsString, stringified);
+ ItemPinHelper._prefValue[this._prefKey] = aNewValue;
+ },
+
+ isPinned: function isPinned(aItemId) {
+ // Bookmarks are visible on StartUI (pinned) by default
+ return this._getPrefValue().indexOf(aItemId) === -1;
+ },
+
+ setUnpinned: function setPinned(aItemId) {
+ let unpinned = this._getPrefValue();
+ unpinned.push(aItemId);
+ this._setPrefValue(unpinned);
+ },
+
+ setPinned: function unsetPinned(aItemId) {
+ let unpinned = this._getPrefValue();
+
+ let index = unpinned.indexOf(aItemId);
+ unpinned.splice(index, 1);
+
+ this._setPrefValue(unpinned);
+ },
+}
--- a/browser/metro/base/tests/mochitest/browser_circular_progress_indicator.js
+++ b/browser/metro/base/tests/mochitest/browser_circular_progress_indicator.js
@@ -1,57 +1,57 @@
-/* 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/. */
-
-let doc;
-
-function test() {
- waitForExplicitFinish();
- Task.spawn(function(){
- info(chromeRoot + "browser_progress_indicator.xul");
- yield addTab(chromeRoot + "browser_progress_indicator.xul");
- doc = Browser.selectedTab.browser.contentWindow.document;
- }).then(runTests);
-}
-
-gTests.push({
- desc: "circular progress indicator binding is applied.",
- run: function() {
- ok(doc, "doc got defined");
-
- let progressIndicator = doc.querySelector("#progress-indicator");
- ok(progressIndicator, "progress-indicator is found");
- is(typeof progressIndicator.reset, "function", "#progress-indicator has a reset() function");
- is(typeof progressIndicator.updateProgress, "function", "#progress-indicator has a updateProgress() function");
- }
-});
-
-gTests.push({
- desc: "start and end angles are correct for various percents complete",
- run: function() {
- let progressIndicator = doc.querySelector("#progress-indicator");
- ok(progressIndicator, "progress-indicator is found");
- is(typeof progressIndicator.updateProgress, "function", "#progress-indicator has a updateProgress() function");
-
- let expectedStartAngle = 1.5 * Math.PI;
-
- let percentComplete = 0;
- let [startAngle, endAngle] = progressIndicator.updateProgress(percentComplete);
- is(startAngle, expectedStartAngle, "start angle is correct");
- is(endAngle, startAngle + (2 * Math.PI * (percentComplete / 100)), "end angle is correct");
-
- percentComplete = 0.05;
- [startAngle, endAngle] = progressIndicator.updateProgress(percentComplete);
- is(startAngle, expectedStartAngle, "start angle is correct");
- is(endAngle, startAngle + (2 * Math.PI * (percentComplete / 100)), "end angle is correct");
-
- percentComplete = 0.5;
- [startAngle, endAngle] = progressIndicator.updateProgress(percentComplete);
- is(startAngle, expectedStartAngle, "start angle is correct");
- is(endAngle, startAngle + (2 * Math.PI * (percentComplete / 100)), "end angle is correct");
-
- percentComplete = 1;
- [startAngle, endAngle] = progressIndicator.updateProgress(percentComplete);
- is(startAngle, expectedStartAngle, "start angle is correct");
- is(endAngle, startAngle + (2 * Math.PI * (percentComplete / 100)), "end angle is correct");
- }
+/* 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/. */
+
+let doc;
+
+function test() {
+ waitForExplicitFinish();
+ Task.spawn(function(){
+ info(chromeRoot + "browser_progress_indicator.xul");
+ yield addTab(chromeRoot + "browser_progress_indicator.xul");
+ doc = Browser.selectedTab.browser.contentWindow.document;
+ }).then(runTests);
+}
+
+gTests.push({
+ desc: "circular progress indicator binding is applied.",
+ run: function() {
+ ok(doc, "doc got defined");
+
+ let progressIndicator = doc.querySelector("#progress-indicator");
+ ok(progressIndicator, "progress-indicator is found");
+ is(typeof progressIndicator.reset, "function", "#progress-indicator has a reset() function");
+ is(typeof progressIndicator.updateProgress, "function", "#progress-indicator has a updateProgress() function");
+ }
+});
+
+gTests.push({
+ desc: "start and end angles are correct for various percents complete",
+ run: function() {
+ let progressIndicator = doc.querySelector("#progress-indicator");
+ ok(progressIndicator, "progress-indicator is found");
+ is(typeof progressIndicator.updateProgress, "function", "#progress-indicator has a updateProgress() function");
+
+ let expectedStartAngle = 1.5 * Math.PI;
+
+ let percentComplete = 0;
+ let [startAngle, endAngle] = progressIndicator.updateProgress(percentComplete);
+ is(startAngle, expectedStartAngle, "start angle is correct");
+ is(endAngle, startAngle + (2 * Math.PI * (percentComplete / 100)), "end angle is correct");
+
+ percentComplete = 0.05;
+ [startAngle, endAngle] = progressIndicator.updateProgress(percentComplete);
+ is(startAngle, expectedStartAngle, "start angle is correct");
+ is(endAngle, startAngle + (2 * Math.PI * (percentComplete / 100)), "end angle is correct");
+
+ percentComplete = 0.5;
+ [startAngle, endAngle] = progressIndicator.updateProgress(percentComplete);
+ is(startAngle, expectedStartAngle, "start angle is correct");
+ is(endAngle, startAngle + (2 * Math.PI * (percentComplete / 100)), "end angle is correct");
+
+ percentComplete = 1;
+ [startAngle, endAngle] = progressIndicator.updateProgress(percentComplete);
+ is(startAngle, expectedStartAngle, "start angle is correct");
+ is(endAngle, startAngle + (2 * Math.PI * (percentComplete / 100)), "end angle is correct");
+ }
});
\ No newline at end of file
--- a/browser/metro/base/tests/mochitest/browser_notifications.js
+++ b/browser/metro/base/tests/mochitest/browser_notifications.js
@@ -1,95 +1,95 @@
-/* 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/. */
-
-"use strict";
-
-function test() {
- runTests();
-}
-
-function cleanup() {
- let notificationBox = Browser.getNotificationBox();
- notificationBox && notificationBox.removeAllNotifications(true);
-}
-
-const XHTML_NS = "http://www.w3.org/1999/xhtml";
-
-function createTestNotification(aLabel, aID) {
- let notificationBox = Browser.getNotificationBox();
- let notn = notificationBox.appendNotification(aLabel || "some label", aID || "test-notification",
- "", notificationBox.PRIORITY_INFO_LOW, []);
- return notn;
-}
-
-gTests.push({
- desc: "Verify notification bindings are correct",
- run: function () {
- let notificationBox = Browser.getNotificationBox();
- let notn = createTestNotification();
-
- let binding = notn && getComputedStyle(notn).MozBinding;
- is(binding,
- "url(\"chrome://browser/content/bindings/notification.xml#notification\")",
- "notification has expected binding");
-
- is(getComputedStyle(notificationBox).MozBinding,
- "url(\"chrome://browser/content/bindings/notification.xml#notificationbox\")",
- "notificationbox has expected binding");
- },
- tearDown: cleanup
-});
-
-gTests.push({
- desc: "Check label property handling",
- run: function () {
- let notn = createTestNotification("the label");
- is(notn.label, "the label");
-
- let doc = notn.ownerDocument;
- let fragment = doc.createDocumentFragment();
- try {
- let boldLabel = doc.createElementNS(XHTML_NS, "b");
- boldLabel.innerHTML = 'The <span class="foo">label</span>';
- fragment.appendChild(boldLabel);
- notn.label = fragment;
- } catch (ex) {
- ok(!ex, "Exception creating notification label with markup: "+ex.message);
- }
-
- // expect to get a documentFragment back when the label has markup
- let labelNode = notn.label;
- is(labelNode.nodeType,
- Components.interfaces.nsIDOMNode.DOCUMENT_FRAGMENT_NODE,
- "notification label getter returns documentFragment nodeType "+Components.interfaces.nsIDOMNode.DOCUMENT_FRAGMENT_NODE+", when value contains markup");
- ok(labelNode !== fragment,
- "label fragment is a newly created fragment, not the one we assigned in the setter");
- ok(labelNode.querySelector("b > .foo"),
- "label fragment has the expected elements in it");
- },
- tearDown: cleanup
-});
-
-gTests.push({
- desc: "Check adoptNotification does what we expect",
- setUp: function() {
- yield addTab("about:start");
- yield addTab("about:mozilla");
- },
- run: function () {
- let browser = getBrowser();
- let notn = createTestNotification("label", "adopt-notification");
- let firstBox = Browser.getNotificationBox();
- let nextTab = Browser.getNextTab(Browser.getTabForBrowser(browser));
- let nextBox = Browser.getNotificationBox(nextTab.browser);
-
- ok(firstBox.getNotificationWithValue("adopt-notification"), "notificationbox has our notification intially");
- nextBox.adoptNotification(notn);
-
- ok(!firstBox.getNotificationWithValue("adopt-notification"), "after adoptNotification, original notificationbox no longer has our notification");
- ok(nextBox.getNotificationWithValue("adopt-notification"), "next notificationbox has our notification");
- },
- // leave browser in clean state for next tests
- tearDown: cleanUpOpenedTabs
-});
-
+/* 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/. */
+
+"use strict";
+
+function test() {
+ runTests();
+}
+
+function cleanup() {
+ let notificationBox = Browser.getNotificationBox();
+ notificationBox && notificationBox.removeAllNotifications(true);
+}
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+function createTestNotification(aLabel, aID) {
+ let notificationBox = Browser.getNotificationBox();
+ let notn = notificationBox.appendNotification(aLabel || "some label", aID || "test-notification",
+ "", notificationBox.PRIORITY_INFO_LOW, []);
+ return notn;
+}
+
+gTests.push({
+ desc: "Verify notification bindings are correct",
+ run: function () {
+ let notificationBox = Browser.getNotificationBox();
+ let notn = createTestNotification();
+
+ let binding = notn && getComputedStyle(notn).MozBinding;
+ is(binding,
+ "url(\"chrome://browser/content/bindings/notification.xml#notification\")",
+ "notification has expected binding");
+
+ is(getComputedStyle(notificationBox).MozBinding,
+ "url(\"chrome://browser/content/bindings/notification.xml#notificationbox\")",
+ "notificationbox has expected binding");
+ },
+ tearDown: cleanup
+});
+
+gTests.push({
+ desc: "Check label property handling",
+ run: function () {
+ let notn = createTestNotification("the label");
+ is(notn.label, "the label");
+
+ let doc = notn.ownerDocument;
+ let fragment = doc.createDocumentFragment();
+ try {
+ let boldLabel = doc.createElementNS(XHTML_NS, "b");
+ boldLabel.innerHTML = 'The <span class="foo">label</span>';
+ fragment.appendChild(boldLabel);
+ notn.label = fragment;
+ } catch (ex) {
+ ok(!ex, "Exception creating notification label with markup: "+ex.message);
+ }
+
+ // expect to get a documentFragment back when the label has markup
+ let labelNode = notn.label;
+ is(labelNode.nodeType,
+ Components.interfaces.nsIDOMNode.DOCUMENT_FRAGMENT_NODE,
+ "notification label getter returns documentFragment nodeType "+Components.interfaces.nsIDOMNode.DOCUMENT_FRAGMENT_NODE+", when value contains markup");
+ ok(labelNode !== fragment,
+ "label fragment is a newly created fragment, not the one we assigned in the setter");
+ ok(labelNode.querySelector("b > .foo"),
+ "label fragment has the expected elements in it");
+ },
+ tearDown: cleanup
+});
+
+gTests.push({
+ desc: "Check adoptNotification does what we expect",
+ setUp: function() {
+ yield addTab("about:start");
+ yield addTab("about:mozilla");
+ },
+ run: function () {
+ let browser = getBrowser();
+ let notn = createTestNotification("label", "adopt-notification");
+ let firstBox = Browser.getNotificationBox();
+ let nextTab = Browser.getNextTab(Browser.getTabForBrowser(browser));
+ let nextBox = Browser.getNotificationBox(nextTab.browser);
+
+ ok(firstBox.getNotificationWithValue("adopt-notification"), "notificationbox has our notification intially");
+ nextBox.adoptNotification(notn);
+
+ ok(!firstBox.getNotificationWithValue("adopt-notification"), "after adoptNotification, original notificationbox no longer has our notification");
+ ok(nextBox.getNotificationWithValue("adopt-notification"), "next notificationbox has our notification");
+ },
+ // leave browser in clean state for next tests
+ tearDown: cleanUpOpenedTabs
+});
+
--- a/browser/metro/base/tests/mochitest/browser_progress_indicator.xul
+++ b/browser/metro/base/tests/mochitest/browser_progress_indicator.xul
@@ -1,15 +1,15 @@
-<?xml version="1.0"?>
-
-<!-- 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/. -->
-