Bug 1016240 - Exterminate CR+LF line endings. r=briansmith,cpearce,ehsan,gavin
authorBirunthan Mohanathas <birunthan@mohanathas.com>
Wed, 18 Jun 2014 17:56:02 -0700
changeset 189502 ca251a28d3ddae784436d28829fb019025f4b127
parent 189501 d4bf4c76854e44dc47569e2de1aae57201473348
child 189503 fcf15eb82338c3339cb39066828467b504ccc1a1
push idunknown
push userunknown
push dateunknown
reviewersbriansmith, cpearce, ehsan, gavin
bugs1016240
milestone33.0a1
Bug 1016240 - Exterminate CR+LF line endings. r=briansmith,cpearce,ehsan,gavin
browser/base/content/test/chat/browser_chatwindow.js
browser/base/content/test/chat/browser_focus.js
browser/base/content/test/chat/browser_tearoff.js
browser/base/content/test/chat/head.js
browser/base/content/test/general/content_aboutAccounts.js
browser/base/content/test/social/browser_social_workercrash.js
browser/components/customizableui/src/logging.js
browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js
browser/components/customizableui/test/browser_888817_currentset_updating.js
browser/components/customizableui/test/browser_890140_orphaned_placeholders.js
browser/components/customizableui/test/browser_901207_searchbar_in_panel.js
browser/components/customizableui/test/browser_909779_overflow_toolbars_new_window.js
browser/components/customizableui/test/browser_914863_disabled_help_quit_buttons.js
browser/components/customizableui/test/browser_932928_show_notice_when_palette_empty.js
browser/components/customizableui/test/browser_934951_zoom_in_toolbar.js
browser/components/customizableui/test/browser_938980_navbar_collapsed.js
browser/components/customizableui/test/browser_940107_home_button_in_bookmarks_toolbar.js
browser/components/customizableui/test/browser_962069_drag_to_overflow_chevron.js
browser/components/customizableui/test/browser_962884_opt_in_disable_hyphens.js
browser/components/customizableui/test/browser_968447_bookmarks_toolbar_items_in_panel.js
browser/components/customizableui/test/browser_970511_undo_restore_default.js
browser/components/customizableui/test/browser_980155_add_overflow_toolbar.js
browser/components/customizableui/test/browser_981305_separator_insertion.js
browser/components/customizableui/test/browser_982656_restore_defaults_builtin_widgets.js
browser/components/customizableui/test/browser_984455_bookmarks_items_reparenting.js
browser/components/customizableui/test/browser_989751_subviewbutton_class.js
browser/components/customizableui/test/browser_996364_registerArea_different_properties.js
browser/components/dirprovider/tests/unit/head_dirprovider.js
browser/components/dirprovider/tests/unit/test_bookmark_pref.js
browser/components/migration/src/nsIEHistoryEnumerator.cpp
browser/components/migration/src/nsIEHistoryEnumerator.h
browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm
browser/devtools/inspector/selector-search.js
browser/metro/base/content/bindings/circularprogress.xml
browser/metro/base/content/commandUtil.js
browser/metro/base/content/helperui/ItemPinHelper.js
browser/metro/base/tests/mochitest/browser_circular_progress_indicator.js
browser/metro/base/tests/mochitest/browser_notifications.js
browser/metro/base/tests/mochitest/browser_progress_indicator.xul
browser/metro/base/tests/mochitest/browser_tabs.js
browser/metro/base/tests/mochitest/browser_tabs_container.js
browser/metro/base/tests/mochitest/browser_ui_telemetry.js
browser/metro/base/tests/unit/test_util_populateFragmentFromString.js
browser/metro/modules/ContentUtil.jsm
browser/metro/modules/View.jsm
browser/metro/shell/commandexecutehandler/CommandExecuteHandler.cpp
browser/metro/theme/platform.css
browser/modules/Chat.jsm
browser/modules/UITour.jsm
browser/modules/Windows8WindowFrameColor.jsm
browser/modules/test/uitour.js
browser/themes/linux/devtools/scratchpad.css
browser/themes/osx/devtools/scratchpad.css
browser/themes/shared/devtools/app-manager/images/add.svg
browser/themes/shared/devtools/app-manager/images/error.svg
browser/themes/shared/devtools/app-manager/images/plus.svg
browser/themes/shared/devtools/app-manager/images/rocket.svg
browser/themes/shared/devtools/app-manager/images/warning.svg
browser/themes/shared/devtools/scratchpad.inc.css
browser/themes/shared/translation/infobar.inc.css
browser/themes/windows/devtools/scratchpad.css
build/win32/procmem.py
content/canvas/src/CanvasImageCache.h
content/canvas/src/WebGLUniformLocation.cpp
content/media/ogg/OggReader.cpp
content/media/test/make-headers.sh
content/media/wmf/DXVA2Manager.cpp
content/media/wmf/DXVA2Manager.h
content/media/wmf/WMF.h
content/media/wmf/WMFByteStream.cpp
content/media/wmf/WMFByteStream.h
content/media/wmf/WMFDecoder.cpp
content/media/wmf/WMFDecoder.h
content/media/wmf/WMFReader.h
content/media/wmf/WMFSourceReaderCallback.cpp
content/media/wmf/WMFSourceReaderCallback.h
content/media/wmf/WMFUtils.cpp
content/media/wmf/WMFUtils.h
dom/media/tests/mochitest/head.js
dom/media/tests/mochitest/mediaStreamPlayback.js
dom/media/tests/mochitest/pc.js
editor/libeditor/base/EditActionListener.h
extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/dupe-dictionary.pl
gfx/2d/genshaders.sh
gfx/2d/unittest/SanityChecks.cpp
gfx/2d/unittest/SanityChecks.h
gfx/2d/unittest/TestBase.cpp
gfx/2d/unittest/TestDrawTargetBase.cpp
gfx/2d/unittest/TestDrawTargetBase.h
gfx/2d/unittest/TestDrawTargetD2D.cpp
gfx/2d/unittest/TestDrawTargetD2D.h
gfx/2d/unittest/TestPoint.cpp
gfx/2d/unittest/TestPoint.h
gfx/2d/unittest/TestScaling.cpp
gfx/2d/unittest/TestScaling.h
js/src/tests/test262/shell.js
js/xpconnect/tests/unit/test_bug451678.js
mobile/android/base/resources/layout/notification_progress.xml
mobile/android/base/resources/layout/notification_progress_text.xml
mobile/android/base/sync/jpake/stage/DecryptDataStage.java
mobile/android/branding/aurora/locales/en-US/brand.dtd
mobile/android/branding/aurora/locales/en-US/brand.properties
mobile/android/branding/beta/locales/en-US/brand.dtd
mobile/android/branding/beta/locales/en-US/brand.properties
mobile/android/branding/nightly/locales/en-US/brand.dtd
mobile/android/branding/nightly/locales/en-US/brand.properties
mobile/android/branding/official/locales/en-US/brand.dtd
mobile/android/branding/official/locales/en-US/brand.properties
mozglue/build/fixcrt.py
netwerk/base/src/AutoClose.h
python/mozbuild/mozbuild/action/xpccheck.py
security/manager/ssl/src/PSMRunnable.cpp
security/manager/ssl/src/PSMRunnable.h
security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp
storage/test/unit/test_bug-429521.js
testing/marionette/client/marionette/www/javascriptPage.html
testing/tools/proxyserver/proxyserver.py
testing/tps/pages/page1.html
testing/tps/pages/page2.html
testing/tps/pages/page3.html
testing/tps/pages/page4.html
testing/tps/pages/page5.html
testing/xpcshell/example/unit/test_sample.js
toolkit/components/places/ClusterLib.js
toolkit/components/places/ColorConversion.js
toolkit/components/places/tests/bookmarks/test_818584-discard-duplicate-backups.js
toolkit/components/places/tests/bookmarks/test_818587_compress-bookmarks-backups.js
toolkit/components/places/tests/bookmarks/test_992901-backup-unsorted-hierarchy.js
toolkit/components/places/tests/bookmarks/test_997030-bookmarks-html-encode.js
toolkit/components/social/test/browser/browser_frameworker_sandbox.js
toolkit/content/aboutAbout.js
toolkit/crashreporter/InjectCrashReporter.cpp
toolkit/crashreporter/InjectCrashReporter.h
toolkit/crashreporter/LoadLibraryRemote.cpp
toolkit/crashreporter/LoadLibraryRemote.h
toolkit/crashreporter/client/crashreporter.rc
toolkit/crashreporter/injector/injector.cpp
toolkit/devtools/acorn/moz.build
toolkit/locales/en-US/chrome/global/webapps.properties
toolkit/modules/PermissionsUtils.jsm
toolkit/modules/WindowsRegistry.jsm
toolkit/mozapps/downloads/tests/chrome/test_bug_429247.xul
toolkit/mozapps/extensions/internal/moz.build
toolkit/themes/windows/global/arrow/panelarrow-horizontal-themed.svg
toolkit/themes/windows/global/arrow/panelarrow-vertical-themed.svg
webapprt/gtk/webapprt.cpp
widget/tests/chrome_context_menus_win.xul
widget/tests/test_chrome_context_menus_win.xul
widget/windows/nsColorPicker.cpp
widget/windows/nsColorPicker.h
widget/windows/winrt/WakeLockListener.h
widget/xpwidgets/ContentHelper.cpp
widget/xpwidgets/ContentHelper.h
xpcom/build/PoisonIOInterposer.h
xpcom/build/PoisonIOInterposerWin.cpp
xpcom/components/ManifestParser.h
xpcom/glue/nsClassInfoImpl.cpp
xpcom/tests/unit/test_bug725015.js
xpcom/threads/HangMonitor.h
xulrunner/app/install_app.py
--- 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 &shy; 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 &shy; 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 &shy; 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 &shy; 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 &shy; 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 &shy; 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 &shy; 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 &shy; 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 &shy; 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 &shy; 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/. -->
-