From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001
From: "Matt A. Tobin" <mattatobin@localhost.localdomain>
Date: Fri, 2 Feb 2018 04:16:08 -0500
Subject: Add m-esr52 at 52.6.0

---
 .../components/customizableui/test/.eslintrc.js    |   7 +
 browser/components/customizableui/test/browser.ini | 154 +++++++
 .../test/browser_1003588_no_specials_in_panel.js   | 107 +++++
 .../browser_1007336_lwthemes_in_customize_mode.js  | 108 +++++
 .../test/browser_1008559_anchor_undo_restore.js    |  71 +++
 .../browser_1042100_default_placements_update.js   | 107 +++++
 .../test/browser_1058573_showToolbarsDropdown.js   |  25 ++
 .../test/browser_1087303_button_fullscreen.js      |  46 ++
 .../test/browser_1087303_button_preferences.js     |  50 +++
 ...owser_1089591_still_customizable_after_reset.js |  24 +
 .../browser_1096763_seen_widgets_post_reset.js     |  31 ++
 ...browser_1161838_inserted_new_default_buttons.js |  78 ++++
 .../test/browser_873501_handle_specials.js         |  79 ++++
 .../test/browser_876926_customize_mode_wrapping.js | 185 ++++++++
 ...browser_876944_customize_mode_create_destroy.js |  61 +++
 .../test/browser_877006_missing_view.js            |  41 ++
 .../test/browser_877178_unregisterArea.js          |  50 +++
 .../test/browser_877447_skip_missing_ids.js        |  25 ++
 .../test/browser_878452_drag_to_panel.js           |  65 +++
 .../browser_880164_customization_context_menus.js  | 414 +++++++++++++++++
 .../browser_880382_drag_wide_widgets_in_panel.js   | 497 ++++++++++++++++++++
 .../test/browser_884402_customize_from_overflow.js |  81 ++++
 ...wser_885052_customize_mode_observers_disabed.js |  45 ++
 .../test/browser_885530_showInPrivateBrowsing.js   | 134 ++++++
 .../browser_886323_buildArea_removable_nodes.js    |  46 ++
 .../test/browser_887438_currentset_shim.js         |  75 ++++
 .../test/browser_888817_currentset_updating.js     |  57 +++
 .../test/browser_890140_orphaned_placeholders.js   | 210 +++++++++
 ...wser_890262_destroyWidget_after_add_to_panel.js |  68 +++
 ...892955_isWidgetRemovable_for_removed_widgets.js |  30 ++
 ...owser_892956_destroyWidget_defaultPlacements.js |  24 +
 .../test/browser_901207_searchbar_in_panel.js      | 113 +++++
 .../browser_909779_overflow_toolbars_new_window.js |  31 ++
 .../test/browser_913972_currentset_overflow.js     |  55 +++
 ...owser_914138_widget_API_overflowable_toolbar.js | 131 ++++++
 .../browser_914863_disabled_help_quit_buttons.js   |  16 +
 .../test/browser_918049_skipintoolbarset_dnd.js    |  38 ++
 ...7_customize_mode_event_wrapping_during_reset.js |  24 +
 .../browser_927717_customize_drag_empty_toolbar.js |  26 ++
 ...rowser_932928_show_notice_when_palette_empty.js |  35 ++
 .../test/browser_934113_menubar_removable.js       |  30 ++
 .../test/browser_934951_zoom_in_toolbar.js         |  89 ++++
 .../test/browser_938980_navbar_collapsed.js        | 121 +++++
 .../browser_938995_indefaultstate_nonremovable.js  |  25 ++
 ...40013_registerToolbarNode_calls_registerArea.js |  70 +++
 .../browser_940307_panel_click_closure_handling.js | 136 ++++++
 ...r_940946_removable_from_navbar_customizemode.js |  22 +
 ...941083_invalidate_wrapper_cache_createWidget.js |  31 ++
 ...owser_942581_unregisterArea_keeps_placements.js | 106 +++++
 .../test/browser_943683_migration_test.js          |  50 +++
 ...4887_destroyWidget_should_destroy_in_palette.js |  17 +
 ..._945739_showInPrivateBrowsing_customize_mode.js |  35 ++
 .../test/browser_947914_button_addons.js           |  33 ++
 .../test/browser_947914_button_copy.js             |  59 +++
 .../test/browser_947914_button_cut.js              |  57 +++
 .../test/browser_947914_button_find.js             |  22 +
 .../test/browser_947914_button_history.js          |  24 +
 .../test/browser_947914_button_newPrivateWindow.js |  48 ++
 .../test/browser_947914_button_newWindow.js        |  47 ++
 .../test/browser_947914_button_paste.js            |  41 ++
 .../test/browser_947914_button_print.js            |  45 ++
 .../test/browser_947914_button_savePage.js         |  20 +
 .../test/browser_947914_button_zoomIn.js           |  37 ++
 .../test/browser_947914_button_zoomOut.js          |  38 ++
 .../test/browser_947914_button_zoomReset.js        |  40 ++
 .../test/browser_947987_removable_default.js       |  68 +++
 .../browser_948985_non_removable_defaultArea.js    |  32 ++
 .../test/browser_952963_areaType_getter_no_area.js |  52 +++
 .../test/browser_956602_remove_special_widget.js   |  31 ++
 .../browser_962069_drag_to_overflow_chevron.js     |  54 +++
 .../test/browser_962884_opt_in_disable_hyphens.js  |  67 +++
 ...stomizing_attribute_non_customizable_toolbar.js |  34 ++
 .../test/browser_967000_button_charEncoding.js     |  62 +++
 .../test/browser_967000_button_feeds.js            |  60 +++
 .../test/browser_967000_button_sync.js             | 335 ++++++++++++++
 ...wser_968447_bookmarks_toolbar_items_in_panel.js |  65 +++
 .../browser_968565_insert_before_hidden_items.js   |  56 +++
 ...969427_recreate_destroyed_widget_after_reset.js |  34 ++
 ...er_969661_character_encoding_navbar_disabled.js |  26 ++
 .../test/browser_970511_undo_restore_default.js    | 128 ++++++
 .../browser_972267_customizationchange_events.js   |  46 ++
 .../test/browser_973641_button_addon.js            |  71 +++
 .../test/browser_973932_addonbar_currentset.js     |  30 ++
 .../browser_975719_customtoolbars_behaviour.js     | 145 ++++++
 .../test/browser_976792_insertNodeInWindow.js      | 414 +++++++++++++++++
 .../test/browser_978084_dragEnd_after_move.js      |  46 ++
 .../test/browser_980155_add_overflow_toolbar.js    |  51 +++
 .../test/browser_981305_separator_insertion.js     |  73 +++
 ...rowser_981418-widget-onbeforecreated-handler.js |  93 ++++
 ...wser_982656_restore_defaults_builtin_widgets.js |  57 +++
 .../browser_984455_bookmarks_items_reparenting.js  | 267 +++++++++++
 ...rowser_985815_propagate_setToolbarVisibility.js |  45 ++
 .../test/browser_987177_destroyWidget_xul.js       |  33 ++
 .../test/browser_987177_xul_wrapper_updating.js    |  74 +++
 .../test/browser_987185_syncButton.js              |  77 ++++
 .../test/browser_987492_window_api.js              |  54 +++
 .../test/browser_987640_charEncoding.js            |  60 +++
 .../test/browser_988072_sidebar_events.js          | 392 ++++++++++++++++
 .../browser_989338_saved_placements_not_resaved.js |  56 +++
 .../test/browser_989751_subviewbutton_class.js     |  62 +++
 ...rowser_992747_toggle_noncustomizable_toolbar.js |  26 ++
 .../test/browser_993322_widget_notoolbar.js        |  36 ++
 ...er_995164_registerArea_during_customize_mode.js | 149 ++++++
 ...ser_996364_registerArea_different_properties.js | 112 +++++
 .../test/browser_996635_remove_non_widgets.js      |  43 ++
 .../test/browser_bootstrapped_custom_toolbar.js    |  81 ++++
 .../test/browser_check_tooltips_in_navbar.js       |  14 +
 ...er_customizemode_contextmenu_menubuttonstate.js |  24 +
 .../customizableui/test/browser_panel_toggle.js    |  43 ++
 .../test/browser_switch_to_customize_mode.js       |  34 ++
 browser/components/customizableui/test/head.js     | 499 +++++++++++++++++++++
 .../test/support/feeds_test_page.html              |  10 +
 .../customizableui/test/support/test-feed.xml      |  23 +
 .../support/test_967000_charEncoding_page.html     |  11 +
 114 files changed, 9062 insertions(+)
 create mode 100644 browser/components/customizableui/test/.eslintrc.js
 create mode 100644 browser/components/customizableui/test/browser.ini
 create mode 100644 browser/components/customizableui/test/browser_1003588_no_specials_in_panel.js
 create mode 100644 browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js
 create mode 100644 browser/components/customizableui/test/browser_1008559_anchor_undo_restore.js
 create mode 100644 browser/components/customizableui/test/browser_1042100_default_placements_update.js
 create mode 100644 browser/components/customizableui/test/browser_1058573_showToolbarsDropdown.js
 create mode 100644 browser/components/customizableui/test/browser_1087303_button_fullscreen.js
 create mode 100644 browser/components/customizableui/test/browser_1087303_button_preferences.js
 create mode 100644 browser/components/customizableui/test/browser_1089591_still_customizable_after_reset.js
 create mode 100644 browser/components/customizableui/test/browser_1096763_seen_widgets_post_reset.js
 create mode 100644 browser/components/customizableui/test/browser_1161838_inserted_new_default_buttons.js
 create mode 100644 browser/components/customizableui/test/browser_873501_handle_specials.js
 create mode 100644 browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
 create mode 100644 browser/components/customizableui/test/browser_876944_customize_mode_create_destroy.js
 create mode 100644 browser/components/customizableui/test/browser_877006_missing_view.js
 create mode 100644 browser/components/customizableui/test/browser_877178_unregisterArea.js
 create mode 100644 browser/components/customizableui/test/browser_877447_skip_missing_ids.js
 create mode 100644 browser/components/customizableui/test/browser_878452_drag_to_panel.js
 create mode 100644 browser/components/customizableui/test/browser_880164_customization_context_menus.js
 create mode 100644 browser/components/customizableui/test/browser_880382_drag_wide_widgets_in_panel.js
 create mode 100644 browser/components/customizableui/test/browser_884402_customize_from_overflow.js
 create mode 100644 browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js
 create mode 100644 browser/components/customizableui/test/browser_885530_showInPrivateBrowsing.js
 create mode 100644 browser/components/customizableui/test/browser_886323_buildArea_removable_nodes.js
 create mode 100644 browser/components/customizableui/test/browser_887438_currentset_shim.js
 create mode 100644 browser/components/customizableui/test/browser_888817_currentset_updating.js
 create mode 100644 browser/components/customizableui/test/browser_890140_orphaned_placeholders.js
 create mode 100644 browser/components/customizableui/test/browser_890262_destroyWidget_after_add_to_panel.js
 create mode 100644 browser/components/customizableui/test/browser_892955_isWidgetRemovable_for_removed_widgets.js
 create mode 100644 browser/components/customizableui/test/browser_892956_destroyWidget_defaultPlacements.js
 create mode 100644 browser/components/customizableui/test/browser_901207_searchbar_in_panel.js
 create mode 100644 browser/components/customizableui/test/browser_909779_overflow_toolbars_new_window.js
 create mode 100644 browser/components/customizableui/test/browser_913972_currentset_overflow.js
 create mode 100644 browser/components/customizableui/test/browser_914138_widget_API_overflowable_toolbar.js
 create mode 100644 browser/components/customizableui/test/browser_914863_disabled_help_quit_buttons.js
 create mode 100644 browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js
 create mode 100644 browser/components/customizableui/test/browser_923857_customize_mode_event_wrapping_during_reset.js
 create mode 100644 browser/components/customizableui/test/browser_927717_customize_drag_empty_toolbar.js
 create mode 100644 browser/components/customizableui/test/browser_932928_show_notice_when_palette_empty.js
 create mode 100644 browser/components/customizableui/test/browser_934113_menubar_removable.js
 create mode 100644 browser/components/customizableui/test/browser_934951_zoom_in_toolbar.js
 create mode 100644 browser/components/customizableui/test/browser_938980_navbar_collapsed.js
 create mode 100644 browser/components/customizableui/test/browser_938995_indefaultstate_nonremovable.js
 create mode 100644 browser/components/customizableui/test/browser_940013_registerToolbarNode_calls_registerArea.js
 create mode 100644 browser/components/customizableui/test/browser_940307_panel_click_closure_handling.js
 create mode 100644 browser/components/customizableui/test/browser_940946_removable_from_navbar_customizemode.js
 create mode 100644 browser/components/customizableui/test/browser_941083_invalidate_wrapper_cache_createWidget.js
 create mode 100644 browser/components/customizableui/test/browser_942581_unregisterArea_keeps_placements.js
 create mode 100644 browser/components/customizableui/test/browser_943683_migration_test.js
 create mode 100644 browser/components/customizableui/test/browser_944887_destroyWidget_should_destroy_in_palette.js
 create mode 100644 browser/components/customizableui/test/browser_945739_showInPrivateBrowsing_customize_mode.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_addons.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_copy.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_cut.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_find.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_history.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_newPrivateWindow.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_newWindow.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_paste.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_print.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_savePage.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_zoomIn.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_zoomOut.js
 create mode 100644 browser/components/customizableui/test/browser_947914_button_zoomReset.js
 create mode 100644 browser/components/customizableui/test/browser_947987_removable_default.js
 create mode 100644 browser/components/customizableui/test/browser_948985_non_removable_defaultArea.js
 create mode 100644 browser/components/customizableui/test/browser_952963_areaType_getter_no_area.js
 create mode 100644 browser/components/customizableui/test/browser_956602_remove_special_widget.js
 create mode 100644 browser/components/customizableui/test/browser_962069_drag_to_overflow_chevron.js
 create mode 100644 browser/components/customizableui/test/browser_962884_opt_in_disable_hyphens.js
 create mode 100644 browser/components/customizableui/test/browser_963639_customizing_attribute_non_customizable_toolbar.js
 create mode 100644 browser/components/customizableui/test/browser_967000_button_charEncoding.js
 create mode 100644 browser/components/customizableui/test/browser_967000_button_feeds.js
 create mode 100644 browser/components/customizableui/test/browser_967000_button_sync.js
 create mode 100644 browser/components/customizableui/test/browser_968447_bookmarks_toolbar_items_in_panel.js
 create mode 100644 browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
 create mode 100644 browser/components/customizableui/test/browser_969427_recreate_destroyed_widget_after_reset.js
 create mode 100644 browser/components/customizableui/test/browser_969661_character_encoding_navbar_disabled.js
 create mode 100644 browser/components/customizableui/test/browser_970511_undo_restore_default.js
 create mode 100644 browser/components/customizableui/test/browser_972267_customizationchange_events.js
 create mode 100755 browser/components/customizableui/test/browser_973641_button_addon.js
 create mode 100644 browser/components/customizableui/test/browser_973932_addonbar_currentset.js
 create mode 100644 browser/components/customizableui/test/browser_975719_customtoolbars_behaviour.js
 create mode 100644 browser/components/customizableui/test/browser_976792_insertNodeInWindow.js
 create mode 100644 browser/components/customizableui/test/browser_978084_dragEnd_after_move.js
 create mode 100644 browser/components/customizableui/test/browser_980155_add_overflow_toolbar.js
 create mode 100644 browser/components/customizableui/test/browser_981305_separator_insertion.js
 create mode 100644 browser/components/customizableui/test/browser_981418-widget-onbeforecreated-handler.js
 create mode 100644 browser/components/customizableui/test/browser_982656_restore_defaults_builtin_widgets.js
 create mode 100644 browser/components/customizableui/test/browser_984455_bookmarks_items_reparenting.js
 create mode 100644 browser/components/customizableui/test/browser_985815_propagate_setToolbarVisibility.js
 create mode 100644 browser/components/customizableui/test/browser_987177_destroyWidget_xul.js
 create mode 100644 browser/components/customizableui/test/browser_987177_xul_wrapper_updating.js
 create mode 100755 browser/components/customizableui/test/browser_987185_syncButton.js
 create mode 100644 browser/components/customizableui/test/browser_987492_window_api.js
 create mode 100644 browser/components/customizableui/test/browser_987640_charEncoding.js
 create mode 100644 browser/components/customizableui/test/browser_988072_sidebar_events.js
 create mode 100644 browser/components/customizableui/test/browser_989338_saved_placements_not_resaved.js
 create mode 100644 browser/components/customizableui/test/browser_989751_subviewbutton_class.js
 create mode 100644 browser/components/customizableui/test/browser_992747_toggle_noncustomizable_toolbar.js
 create mode 100644 browser/components/customizableui/test/browser_993322_widget_notoolbar.js
 create mode 100644 browser/components/customizableui/test/browser_995164_registerArea_during_customize_mode.js
 create mode 100644 browser/components/customizableui/test/browser_996364_registerArea_different_properties.js
 create mode 100644 browser/components/customizableui/test/browser_996635_remove_non_widgets.js
 create mode 100644 browser/components/customizableui/test/browser_bootstrapped_custom_toolbar.js
 create mode 100644 browser/components/customizableui/test/browser_check_tooltips_in_navbar.js
 create mode 100644 browser/components/customizableui/test/browser_customizemode_contextmenu_menubuttonstate.js
 create mode 100644 browser/components/customizableui/test/browser_panel_toggle.js
 create mode 100644 browser/components/customizableui/test/browser_switch_to_customize_mode.js
 create mode 100644 browser/components/customizableui/test/head.js
 create mode 100644 browser/components/customizableui/test/support/feeds_test_page.html
 create mode 100644 browser/components/customizableui/test/support/test-feed.xml
 create mode 100644 browser/components/customizableui/test/support/test_967000_charEncoding_page.html

(limited to 'browser/components/customizableui/test')

diff --git a/browser/components/customizableui/test/.eslintrc.js b/browser/components/customizableui/test/.eslintrc.js
new file mode 100644
index 000000000..c764b133d
--- /dev/null
+++ b/browser/components/customizableui/test/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+  "extends": [
+    "../../../../testing/mochitest/browser.eslintrc.js"
+  ]
+};
diff --git a/browser/components/customizableui/test/browser.ini b/browser/components/customizableui/test/browser.ini
new file mode 100644
index 000000000..1c1f30498
--- /dev/null
+++ b/browser/components/customizableui/test/browser.ini
@@ -0,0 +1,154 @@
+[DEFAULT]
+support-files =
+  head.js
+  support/test_967000_charEncoding_page.html
+  support/feeds_test_page.html
+  support/test-feed.xml
+
+[browser_873501_handle_specials.js]
+[browser_876926_customize_mode_wrapping.js]
+[browser_876944_customize_mode_create_destroy.js]
+[browser_877006_missing_view.js]
+[browser_877178_unregisterArea.js]
+[browser_877447_skip_missing_ids.js]
+[browser_878452_drag_to_panel.js]
+[browser_880164_customization_context_menus.js]
+[browser_880382_drag_wide_widgets_in_panel.js]
+[browser_884402_customize_from_overflow.js]
+skip-if = os == "linux"
+[browser_885052_customize_mode_observers_disabed.js]
+tags = fullscreen
+# Bug 951403 - Disabled on OSX for frequent failures
+skip-if = os == "mac"
+
+[browser_885530_showInPrivateBrowsing.js]
+[browser_886323_buildArea_removable_nodes.js]
+[browser_887438_currentset_shim.js]
+[browser_888817_currentset_updating.js]
+[browser_890140_orphaned_placeholders.js]
+[browser_890262_destroyWidget_after_add_to_panel.js]
+[browser_892955_isWidgetRemovable_for_removed_widgets.js]
+[browser_892956_destroyWidget_defaultPlacements.js]
+[browser_909779_overflow_toolbars_new_window.js]
+skip-if = os == "linux"
+
+[browser_901207_searchbar_in_panel.js]
+[browser_913972_currentset_overflow.js]
+skip-if = os == "linux"
+
+[browser_914138_widget_API_overflowable_toolbar.js]
+skip-if = os == "linux"
+
+[browser_914863_disabled_help_quit_buttons.js]
+[browser_918049_skipintoolbarset_dnd.js]
+[browser_923857_customize_mode_event_wrapping_during_reset.js]
+[browser_927717_customize_drag_empty_toolbar.js]
+
+# Bug 1163231 - Causes failures on Developer Edition on Windows 7.
+# [browser_932928_show_notice_when_palette_empty.js]
+
+[browser_934113_menubar_removable.js]
+# Because this test is about the menubar, it can't be run on mac
+skip-if = os == "mac"
+
+[browser_934951_zoom_in_toolbar.js]
+[browser_938980_navbar_collapsed.js]
+[browser_938995_indefaultstate_nonremovable.js]
+[browser_940013_registerToolbarNode_calls_registerArea.js]
+[browser_940307_panel_click_closure_handling.js]
+[browser_940946_removable_from_navbar_customizemode.js]
+[browser_941083_invalidate_wrapper_cache_createWidget.js]
+[browser_942581_unregisterArea_keeps_placements.js]
+[browser_943683_migration_test.js]
+[browser_944887_destroyWidget_should_destroy_in_palette.js]
+[browser_945739_showInPrivateBrowsing_customize_mode.js]
+[browser_947914_button_addons.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_copy.js]
+subsuite = clipboard
+skip-if = os == "linux" # Intermittent failures on Linux
+[browser_947914_button_cut.js]
+subsuite = clipboard
+skip-if = os == "linux" # Intermittent failures on Linux
+[browser_947914_button_find.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_history.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_newPrivateWindow.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_newWindow.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_paste.js]
+subsuite = clipboard
+skip-if = os == "linux" # Intermittent failures on Linux
+[browser_947914_button_print.js]
+skip-if = os == "linux" # Intermittent failures on Linux
+[browser_947914_button_savePage.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_zoomIn.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_zoomOut.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_zoomReset.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947987_removable_default.js]
+[browser_948985_non_removable_defaultArea.js]
+[browser_952963_areaType_getter_no_area.js]
+[browser_956602_remove_special_widget.js]
+[browser_962069_drag_to_overflow_chevron.js]
+[browser_962884_opt_in_disable_hyphens.js]
+[browser_963639_customizing_attribute_non_customizable_toolbar.js]
+[browser_967000_button_charEncoding.js]
+[browser_967000_button_feeds.js]
+[browser_967000_button_sync.js]
+[browser_968447_bookmarks_toolbar_items_in_panel.js]
+skip-if = os == "linux" # Intemittent failures - bug 979207
+[browser_968565_insert_before_hidden_items.js]
+[browser_969427_recreate_destroyed_widget_after_reset.js]
+[browser_969661_character_encoding_navbar_disabled.js]
+[browser_970511_undo_restore_default.js]
+[browser_972267_customizationchange_events.js]
+[browser_973641_button_addon.js]
+[browser_973932_addonbar_currentset.js]
+[browser_975719_customtoolbars_behaviour.js]
+[browser_976792_insertNodeInWindow.js]
+skip-if = os == "linux"
+[browser_978084_dragEnd_after_move.js]
+[browser_980155_add_overflow_toolbar.js]
+[browser_981305_separator_insertion.js]
+[browser_981418-widget-onbeforecreated-handler.js]
+[browser_982656_restore_defaults_builtin_widgets.js]
+[browser_984455_bookmarks_items_reparenting.js]
+skip-if = os == "linux"
+[browser_985815_propagate_setToolbarVisibility.js]
+[browser_987177_destroyWidget_xul.js]
+[browser_987177_xul_wrapper_updating.js]
+[browser_987185_syncButton.js]
+[browser_987492_window_api.js]
+[browser_987640_charEncoding.js]
+[browser_988072_sidebar_events.js]
+[browser_989338_saved_placements_not_resaved.js]
+[browser_989751_subviewbutton_class.js]
+[browser_992747_toggle_noncustomizable_toolbar.js]
+[browser_993322_widget_notoolbar.js]
+[browser_995164_registerArea_during_customize_mode.js]
+[browser_996364_registerArea_different_properties.js]
+[browser_996635_remove_non_widgets.js]
+[browser_1003588_no_specials_in_panel.js]
+[browser_1007336_lwthemes_in_customize_mode.js]
+skip-if = os == "linux" # crashing on Linux due to bug 1271683
+[browser_1008559_anchor_undo_restore.js]
+[browser_1042100_default_placements_update.js]
+[browser_1058573_showToolbarsDropdown.js]
+[browser_1087303_button_fullscreen.js]
+tags = fullscreen
+skip-if = os == "mac"
+[browser_1087303_button_preferences.js]
+[browser_1089591_still_customizable_after_reset.js]
+[browser_1096763_seen_widgets_post_reset.js]
+[browser_1161838_inserted_new_default_buttons.js]
+[browser_bootstrapped_custom_toolbar.js]
+[browser_customizemode_contextmenu_menubuttonstate.js]
+[browser_panel_toggle.js]
+[browser_switch_to_customize_mode.js]
+[browser_check_tooltips_in_navbar.js]
diff --git a/browser/components/customizableui/test/browser_1003588_no_specials_in_panel.js b/browser/components/customizableui/test/browser_1003588_no_specials_in_panel.js
new file mode 100644
index 000000000..22fbb5c0c
--- /dev/null
+++ b/browser/components/customizableui/test/browser_1003588_no_specials_in_panel.js
@@ -0,0 +1,107 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function simulateItemDragAndEnd(aToDrag, aTarget) {
+  var ds = Components.classes["@mozilla.org/widget/dragservice;1"].
+           getService(Components.interfaces.nsIDragService);
+
+  ds.startDragSession();
+  try {
+    var [result, dataTransfer] = EventUtils.synthesizeDragOver(aToDrag.parentNode, aTarget);
+    EventUtils.synthesizeDropAfterDragOver(result, dataTransfer, aTarget);
+    // Send dragend to move dragging item back to initial place.
+    EventUtils.sendDragEvent({ type: "dragend", dataTransfer: dataTransfer },
+                             aToDrag.parentNode);
+  } finally {
+    ds.endDragSession(true);
+  }
+}
+
+add_task(function* checkNoAddingToPanel() {
+  let area = CustomizableUI.AREA_PANEL;
+  let previousPlacements = getAreaWidgetIds(area);
+  CustomizableUI.addWidgetToArea("separator", area);
+  CustomizableUI.addWidgetToArea("spring", area);
+  CustomizableUI.addWidgetToArea("spacer", area);
+  assertAreaPlacements(area, previousPlacements);
+
+  let oldNumberOfItems = previousPlacements.length;
+  if (getAreaWidgetIds(area).length != oldNumberOfItems) {
+    CustomizableUI.reset();
+  }
+});
+
+add_task(function* checkAddingToToolbar() {
+  let area = CustomizableUI.AREA_NAVBAR;
+  let previousPlacements = getAreaWidgetIds(area);
+  CustomizableUI.addWidgetToArea("separator", area);
+  CustomizableUI.addWidgetToArea("spring", area);
+  CustomizableUI.addWidgetToArea("spacer", area);
+  let expectedPlacements = [...previousPlacements].concat([
+    /separator/,
+    /spring/,
+    /spacer/
+  ]);
+  assertAreaPlacements(area, expectedPlacements);
+
+  let newlyAddedElements = getAreaWidgetIds(area).slice(-3);
+  while (newlyAddedElements.length) {
+    CustomizableUI.removeWidgetFromArea(newlyAddedElements.shift());
+  }
+
+  assertAreaPlacements(area, previousPlacements);
+
+  let oldNumberOfItems = previousPlacements.length;
+  if (getAreaWidgetIds(area).length != oldNumberOfItems) {
+    CustomizableUI.reset();
+  }
+});
+
+
+add_task(function* checkDragging() {
+  let startArea = CustomizableUI.AREA_NAVBAR;
+  let targetArea = CustomizableUI.AREA_PANEL;
+  let startingToolbarPlacements = getAreaWidgetIds(startArea);
+  let startingTargetPlacements = getAreaWidgetIds(targetArea);
+
+  CustomizableUI.addWidgetToArea("separator", startArea);
+  CustomizableUI.addWidgetToArea("spring", startArea);
+  CustomizableUI.addWidgetToArea("spacer", startArea);
+
+  let placementsWithSpecials = getAreaWidgetIds(startArea);
+  let elementsToMove = [];
+  for (let id of placementsWithSpecials) {
+    if (CustomizableUI.isSpecialWidget(id)) {
+      elementsToMove.push(id);
+    }
+  }
+  is(elementsToMove.length, 3, "Should have 3 elements to try and drag.");
+
+  yield startCustomizing();
+  for (let id of elementsToMove) {
+    simulateItemDragAndEnd(document.getElementById(id), PanelUI.contents);
+  }
+
+  assertAreaPlacements(startArea, placementsWithSpecials);
+  assertAreaPlacements(targetArea, startingTargetPlacements);
+
+  for (let id of elementsToMove) {
+    simulateItemDrag(document.getElementById(id), gCustomizeMode.visiblePalette);
+  }
+
+  assertAreaPlacements(startArea, startingToolbarPlacements);
+  assertAreaPlacements(targetArea, startingTargetPlacements);
+
+  ok(!gCustomizeMode.visiblePalette.querySelector("toolbarspring,toolbarseparator,toolbarspacer"),
+     "No specials should make it to the palette alive.");
+  yield endCustomizing();
+});
+
+
+add_task(function* asyncCleanup() {
+  yield endCustomizing();
+  CustomizableUI.reset();
+});
+
diff --git a/browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js b/browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js
new file mode 100644
index 000000000..db4f88e6d
--- /dev/null
+++ b/browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js
@@ -0,0 +1,108 @@
+/* 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 DEFAULT_THEME_ID = "{972ce4c6-7e08-4474-a285-3208198ce6fd}";
+const {LightweightThemeManager} = Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", {});
+
+add_task(function* () {
+  Services.prefs.clearUserPref("lightweightThemes.usedThemes");
+  Services.prefs.clearUserPref("lightweightThemes.recommendedThemes");
+  LightweightThemeManager.clearBuiltInThemes();
+
+  yield startCustomizing();
+
+  let themesButton = document.getElementById("customization-lwtheme-button");
+  let popup = document.getElementById("customization-lwtheme-menu");
+
+  let popupShownPromise = popupShown(popup);
+  EventUtils.synthesizeMouseAtCenter(themesButton, {});
+  info("Clicked on themes button");
+  yield popupShownPromise;
+
+  // close current tab and re-open Customize menu to confirm correct number of Themes
+  yield endCustomizing();
+  info("Exited customize mode");
+  yield startCustomizing();
+  info("Started customizing a second time");
+  popupShownPromise = popupShown(popup);
+  EventUtils.synthesizeMouseAtCenter(themesButton, {});
+  info("Clicked on themes button a second time");
+  yield popupShownPromise;
+
+  let header = document.getElementById("customization-lwtheme-menu-header");
+  let recommendedHeader = document.getElementById("customization-lwtheme-menu-recommended");
+
+  is(header.nextSibling.nextSibling, recommendedHeader,
+     "There should only be one theme (default) in the 'My Themes' section by default");
+  is(header.nextSibling.theme.id, DEFAULT_THEME_ID, "That theme should be the default theme");
+
+  let firstLWTheme = recommendedHeader.nextSibling;
+  let firstLWThemeId = firstLWTheme.theme.id;
+  let themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
+  firstLWTheme.doCommand();
+  info("Clicked on first theme");
+  yield themeChangedPromise;
+
+  popupShownPromise = popupShown(popup);
+  EventUtils.synthesizeMouseAtCenter(themesButton, {});
+  info("Clicked on themes button");
+  yield popupShownPromise;
+
+  is(header.nextSibling.theme.id, DEFAULT_THEME_ID, "The first theme should be the Default theme");
+  let installedThemeId = header.nextSibling.nextSibling.theme.id;
+  ok(installedThemeId.startsWith(firstLWThemeId),
+     "The second theme in the 'My Themes' section should be the newly installed theme: " +
+     "Installed theme id: " + installedThemeId + "; First theme ID: " + firstLWThemeId);
+  is(header.nextSibling.nextSibling.nextSibling, recommendedHeader,
+     "There should be two themes in the 'My Themes' section");
+
+  let defaultTheme = header.nextSibling;
+  defaultTheme.doCommand();
+  is(Services.prefs.getCharPref("lightweightThemes.selectedThemeID"), "", "No lwtheme should be selected");
+
+  // ensure current theme isn't set to "Default"
+  popupShownPromise = popupShown(popup);
+  EventUtils.synthesizeMouseAtCenter(themesButton, {});
+  info("Clicked on themes button a second time");
+  yield popupShownPromise;
+
+  firstLWTheme = recommendedHeader.nextSibling;
+  themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
+  firstLWTheme.doCommand();
+  info("Clicked on first theme again");
+  yield themeChangedPromise;
+
+  // check that "Restore Defaults" button resets theme
+  yield gCustomizeMode.reset();
+  is(LightweightThemeManager.currentTheme, null, "Current theme reset to default");
+
+  yield endCustomizing();
+  Services.prefs.setCharPref("lightweightThemes.usedThemes", "[]");
+  Services.prefs.setCharPref("lightweightThemes.recommendedThemes", "[]");
+  info("Removed all recommended themes");
+  yield startCustomizing();
+  popupShownPromise = popupShown(popup);
+  EventUtils.synthesizeMouseAtCenter(themesButton, {});
+  info("Clicked on themes button a second time");
+  yield popupShownPromise;
+  header = document.getElementById("customization-lwtheme-menu-header");
+  is(header.hidden, false, "Header should never be hidden");
+  is(header.nextSibling.theme.id, DEFAULT_THEME_ID, "The first theme should be the Default theme");
+  is(header.nextSibling.hidden, false, "The default theme should never be hidden");
+  recommendedHeader = document.getElementById("customization-lwtheme-menu-recommended");
+  is(header.nextSibling.nextSibling, recommendedHeader,
+     "There should only be one theme (default) in the 'My Themes' section by default");
+  let footer = document.getElementById("customization-lwtheme-menu-footer");
+  is(recommendedHeader.nextSibling.id, footer.id, "There should be no recommended themes in the menu");
+  is(recommendedHeader.hidden, true, "The recommendedHeader should be hidden since there are no recommended themes");
+});
+
+add_task(function* asyncCleanup() {
+  yield endCustomizing();
+
+  Services.prefs.clearUserPref("lightweightThemes.usedThemes");
+  Services.prefs.clearUserPref("lightweightThemes.recommendedThemes");
+});
diff --git a/browser/components/customizableui/test/browser_1008559_anchor_undo_restore.js b/browser/components/customizableui/test/browser_1008559_anchor_undo_restore.js
new file mode 100644
index 000000000..56657914b
--- /dev/null
+++ b/browser/components/customizableui/test/browser_1008559_anchor_undo_restore.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const kAnchorAttribute = "cui-anchorid";
+
+/**
+ * Check that anchor gets set correctly when moving an item from the panel to the toolbar
+ * using 'undo'
+ */
+add_task(function*() {
+  yield startCustomizing();
+  let button = document.getElementById("history-panelmenu");
+  is(button.getAttribute(kAnchorAttribute), "PanelUI-menu-button",
+     "Button (" + button.id + ") starts out with correct anchor");
+
+  let navbar = document.getElementById("nav-bar").customizationTarget;
+  simulateItemDrag(button, navbar);
+  is(CustomizableUI.getPlacementOfWidget(button.id).area, "nav-bar",
+     "Button (" + button.id + ") ends up in nav-bar");
+
+  ok(!button.hasAttribute(kAnchorAttribute),
+     "Button (" + button.id + ") has no anchor in toolbar");
+
+  let resetButton = document.getElementById("customization-reset-button");
+  ok(!resetButton.hasAttribute("disabled"), "Should be able to reset now.");
+  yield gCustomizeMode.reset();
+
+  is(button.getAttribute(kAnchorAttribute), "PanelUI-menu-button",
+     "Button (" + button.id + ") has anchor again");
+
+  let undoButton = document.getElementById("customization-undo-reset-button");
+  ok(!undoButton.hasAttribute("disabled"), "Should be able to undo now.");
+  yield gCustomizeMode.undoReset();
+
+  ok(!button.hasAttribute(kAnchorAttribute),
+     "Button (" + button.id + ") once again has no anchor in toolbar");
+
+  yield gCustomizeMode.reset();
+
+  yield endCustomizing();
+});
+
+
+/**
+ * Check that anchor gets set correctly when moving an item from the panel to the toolbar
+ * using 'reset'
+ */
+add_task(function*() {
+  yield startCustomizing();
+  let button = document.getElementById("bookmarks-menu-button");
+  ok(!button.hasAttribute(kAnchorAttribute),
+     "Button (" + button.id + ") has no anchor in toolbar");
+
+  let panel = document.getElementById("PanelUI-contents");
+  simulateItemDrag(button, panel);
+  is(CustomizableUI.getPlacementOfWidget(button.id).area, "PanelUI-contents",
+     "Button (" + button.id + ") ends up in panel");
+  is(button.getAttribute(kAnchorAttribute), "PanelUI-menu-button",
+     "Button (" + button.id + ") has correct anchor in the panel");
+
+  let resetButton = document.getElementById("customization-reset-button");
+  ok(!resetButton.hasAttribute("disabled"), "Should be able to reset now.");
+  yield gCustomizeMode.reset();
+
+  ok(!button.hasAttribute(kAnchorAttribute),
+     "Button (" + button.id + ") once again has no anchor in toolbar");
+
+  yield endCustomizing();
+});
diff --git a/browser/components/customizableui/test/browser_1042100_default_placements_update.js b/browser/components/customizableui/test/browser_1042100_default_placements_update.js
new file mode 100644
index 000000000..129dbd754
--- /dev/null
+++ b/browser/components/customizableui/test/browser_1042100_default_placements_update.js
@@ -0,0 +1,107 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// NB: This uses some ugly hacks to get into the CUI module from elsewhere...
+// don't try this at home, kids.
+function test() {
+  // Customize something to make sure stuff changed:
+  CustomizableUI.addWidgetToArea("feed-button", CustomizableUI.AREA_NAVBAR);
+
+  // Check what version we're on:
+  let CustomizableUIBSPass = Cu.import("resource:///modules/CustomizableUI.jsm", {});
+
+  is(CustomizableUIBSPass.gFuturePlacements.size, 0,
+     "All future placements should be dealt with by now.");
+
+  let {CustomizableUIInternal, gFuturePlacements, gPalette} = CustomizableUIBSPass;
+  CustomizableUIInternal._introduceNewBuiltinWidgets();
+  is(gFuturePlacements.size, 0,
+     "No change to future placements initially.");
+
+  let currentVersion = CustomizableUIBSPass.kVersion;
+
+
+  // Add our widget to the defaults:
+  let testWidgetNew = {
+    id: "test-messing-with-default-placements-new",
+    label: "Test messing with default placements - should be inserted",
+    defaultArea: CustomizableUI.AREA_NAVBAR,
+    introducedInVersion: currentVersion + 1,
+  };
+
+  let normalizedWidget = CustomizableUIInternal.normalizeWidget(testWidgetNew,
+                                                                CustomizableUI.SOURCE_BUILTIN);
+  ok(normalizedWidget, "Widget should be normalizable");
+  if (!normalizedWidget) {
+    return;
+  }
+  CustomizableUIBSPass.gPalette.set(testWidgetNew.id, normalizedWidget);
+
+  let testWidgetOld = {
+    id: "test-messing-with-default-placements-old",
+    label: "Test messing with default placements - should NOT be inserted",
+    defaultArea: CustomizableUI.AREA_NAVBAR,
+    introducedInVersion: currentVersion,
+  };
+
+  normalizedWidget = CustomizableUIInternal.normalizeWidget(testWidgetOld,
+                                                            CustomizableUI.SOURCE_BUILTIN);
+  ok(normalizedWidget, "Widget should be normalizable");
+  if (!normalizedWidget) {
+    return;
+  }
+  CustomizableUIBSPass.gPalette.set(testWidgetOld.id, normalizedWidget);
+
+
+  // Now increase the version in the module:
+  CustomizableUIBSPass.kVersion++;
+
+  let hadSavedState = !!CustomizableUIBSPass.gSavedState
+  if (!hadSavedState) {
+    CustomizableUIBSPass.gSavedState = {currentVersion: CustomizableUIBSPass.kVersion - 1};
+  }
+
+  // Then call the re-init routine so we re-add the builtin widgets
+  CustomizableUIInternal._introduceNewBuiltinWidgets();
+  is(gFuturePlacements.size, 1,
+     "Should have 1 more future placement");
+  let haveNavbarPlacements = gFuturePlacements.has(CustomizableUI.AREA_NAVBAR);
+  ok(haveNavbarPlacements, "Should have placements for nav-bar");
+  if (haveNavbarPlacements) {
+    let placements = [...gFuturePlacements.get(CustomizableUI.AREA_NAVBAR)];
+
+    // Ignore widgets that are placed using the pref facility and not the
+    // versioned facility.  They're independent of kVersion and the saved
+    // state's current version, so they may be present in the placements.
+    for (let i = 0; i < placements.length; ) {
+      if (placements[i] == testWidgetNew.id) {
+        i++;
+        continue;
+      }
+      let pref = "browser.toolbarbuttons.introduced." + placements[i];
+      let introduced = false;
+      try {
+        introduced = Services.prefs.getBoolPref(pref);
+      } catch (ex) {}
+      if (!introduced) {
+        i++;
+        continue;
+      }
+      placements.splice(i, 1);
+    }
+
+    is(placements.length, 1, "Should have 1 newly placed widget in nav-bar");
+    is(placements[0], testWidgetNew.id, "Should have our test widget to be placed in nav-bar");
+  }
+
+  gFuturePlacements.delete(CustomizableUI.AREA_NAVBAR);
+  CustomizableUIBSPass.kVersion--;
+  gPalette.delete(testWidgetNew.id);
+  gPalette.delete(testWidgetOld.id);
+  if (!hadSavedState) {
+    CustomizableUIBSPass.gSavedState = null;
+  }
+}
+
diff --git a/browser/components/customizableui/test/browser_1058573_showToolbarsDropdown.js b/browser/components/customizableui/test/browser_1058573_showToolbarsDropdown.js
new file mode 100644
index 000000000..42a032ff8
--- /dev/null
+++ b/browser/components/customizableui/test/browser_1058573_showToolbarsDropdown.js
@@ -0,0 +1,25 @@
+/* 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*() {
+  info("Check that toggleable toolbars dropdown in always shown");
+
+  info("Remove all possible custom toolbars");
+  yield removeCustomToolbars();
+
+  info("Enter customization mode");
+  yield startCustomizing();
+
+  let toolbarsToggle = document.getElementById("customization-toolbar-visibility-button");
+  ok(toolbarsToggle, "The toolbars toggle dropdown exists");
+  ok(!toolbarsToggle.hasAttribute("hidden"),
+     "The toolbars toggle dropdown is displayed");
+});
+
+add_task(function* asyncCleanup() {
+  info("Exit customization mode");
+  yield endCustomizing();
+});
diff --git a/browser/components/customizableui/test/browser_1087303_button_fullscreen.js b/browser/components/customizableui/test/browser_1087303_button_fullscreen.js
new file mode 100644
index 000000000..c6b87d6ab
--- /dev/null
+++ b/browser/components/customizableui/test/browser_1087303_button_fullscreen.js
@@ -0,0 +1,46 @@
+/* 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*() {
+  info("Check fullscreen button existence and functionality");
+
+  yield PanelUI.show();
+
+  let fullscreenButton = document.getElementById("fullscreen-button");
+  ok(fullscreenButton, "Fullscreen button appears in Panel Menu");
+
+  let fullscreenPromise = promiseFullscreenChange();
+  fullscreenButton.click();
+  yield fullscreenPromise;
+
+  ok(window.fullScreen, "Fullscreen mode was opened");
+
+  // exit full screen mode
+  fullscreenPromise = promiseFullscreenChange();
+  window.fullScreen = !window.fullScreen;
+  yield fullscreenPromise;
+
+  ok(!window.fullScreen, "Successfully exited fullscreen");
+});
+
+function promiseFullscreenChange() {
+  let deferred = Promise.defer();
+  info("Wait for fullscreen change");
+
+  let timeoutId = setTimeout(() => {
+    window.removeEventListener("fullscreen", onFullscreenChange, true);
+    deferred.reject("Fullscreen change did not happen within " + 20000 + "ms");
+  }, 20000);
+
+  function onFullscreenChange(event) {
+    clearTimeout(timeoutId);
+    window.removeEventListener("fullscreen", onFullscreenChange, true);
+    info("Fullscreen event received");
+    deferred.resolve();
+  }
+  window.addEventListener("fullscreen", onFullscreenChange, true);
+  return deferred.promise;
+}
diff --git a/browser/components/customizableui/test/browser_1087303_button_preferences.js b/browser/components/customizableui/test/browser_1087303_button_preferences.js
new file mode 100644
index 000000000..b1fdb85b6
--- /dev/null
+++ b/browser/components/customizableui/test/browser_1087303_button_preferences.js
@@ -0,0 +1,50 @@
+/* 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";
+
+var newTab = null;
+
+add_task(function*() {
+  info("Check preferences button existence and functionality");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let preferencesButton = document.getElementById("preferences-button");
+  ok(preferencesButton, "Preferences button exists in Panel Menu");
+  preferencesButton.click();
+
+  newTab = gBrowser.selectedTab;
+  yield waitForPageLoad(newTab);
+
+  let openedPage = gBrowser.currentURI.spec;
+  is(openedPage, "about:preferences", "Preferences page was opened");
+});
+
+add_task(function asyncCleanup() {
+  if (gBrowser.tabs.length == 1)
+    gBrowser.addTab("about:blank");
+
+  gBrowser.removeTab(gBrowser.selectedTab);
+  info("Tabs were restored");
+});
+
+function waitForPageLoad(aTab) {
+  let deferred = Promise.defer();
+
+  let timeoutId = setTimeout(() => {
+    aTab.linkedBrowser.removeEventListener("load", onTabLoad, true);
+    deferred.reject("Page didn't load within " + 20000 + "ms");
+  }, 20000);
+
+  function onTabLoad(event) {
+    clearTimeout(timeoutId);
+    aTab.linkedBrowser.removeEventListener("load", onTabLoad, true);
+    info("Tab event received: " + "load");
+    deferred.resolve();
+ }
+  aTab.linkedBrowser.addEventListener("load", onTabLoad, true, true);
+  return deferred.promise;
+}
diff --git a/browser/components/customizableui/test/browser_1089591_still_customizable_after_reset.js b/browser/components/customizableui/test/browser_1089591_still_customizable_after_reset.js
new file mode 100644
index 000000000..1f502e8e2
--- /dev/null
+++ b/browser/components/customizableui/test/browser_1089591_still_customizable_after_reset.js
@@ -0,0 +1,24 @@
+"use strict";
+
+// Dragging the elements again after a reset should work
+add_task(function* () {
+  yield startCustomizing();
+  let historyButton = document.getElementById("wrapper-history-panelmenu");
+  let devButton = document.getElementById("wrapper-developer-button");
+
+  ok(historyButton && devButton, "Draggable elements should exist");
+  simulateItemDrag(historyButton, devButton);
+  yield gCustomizeMode.reset();
+  ok(CustomizableUI.inDefaultState, "Should be back in default state");
+
+  historyButton = document.getElementById("wrapper-history-panelmenu");
+  devButton = document.getElementById("wrapper-developer-button");
+  ok(historyButton && devButton, "Draggable elements should exist");
+  simulateItemDrag(historyButton, devButton);
+
+  yield endCustomizing();
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_1096763_seen_widgets_post_reset.js b/browser/components/customizableui/test/browser_1096763_seen_widgets_post_reset.js
new file mode 100644
index 000000000..b5a325afb
--- /dev/null
+++ b/browser/components/customizableui/test/browser_1096763_seen_widgets_post_reset.js
@@ -0,0 +1,31 @@
+"use strict";
+
+const BUTTONID = "test-seenwidget-post-reset";
+
+add_task(function*() {
+  CustomizableUI.createWidget({
+    id: BUTTONID,
+    label: "Test widget seen post reset",
+    defaultArea: CustomizableUI.AREA_NAVBAR
+  });
+
+  const kPrefCustomizationState = "browser.uiCustomization.state";
+  let bsPass = Cu.import("resource:///modules/CustomizableUI.jsm", {});
+  ok(bsPass.gSeenWidgets.has(BUTTONID), "Widget should be seen after createWidget is called.");
+  CustomizableUI.reset();
+  ok(bsPass.gSeenWidgets.has(BUTTONID), "Widget should still be seen after reset.");
+  ok(!Services.prefs.prefHasUserValue(kPrefCustomizationState), "Pref shouldn't be set right now, because that'd break undo.");
+  CustomizableUI.addWidgetToArea(BUTTONID, CustomizableUI.AREA_NAVBAR);
+  gCustomizeMode.removeFromArea(document.getElementById(BUTTONID));
+  let hasUserValue = Services.prefs.prefHasUserValue(kPrefCustomizationState);
+  ok(hasUserValue, "Pref should be set right now.");
+  if (hasUserValue) {
+    let seenArray = JSON.parse(Services.prefs.getCharPref(kPrefCustomizationState)).seen;
+    isnot(seenArray.indexOf(BUTTONID), -1, "Widget should be in saved 'seen' list.");
+  }
+});
+
+registerCleanupFunction(function() {
+  CustomizableUI.destroyWidget(BUTTONID);
+  CustomizableUI.reset();
+});
diff --git a/browser/components/customizableui/test/browser_1161838_inserted_new_default_buttons.js b/browser/components/customizableui/test/browser_1161838_inserted_new_default_buttons.js
new file mode 100644
index 000000000..42768debf
--- /dev/null
+++ b/browser/components/customizableui/test/browser_1161838_inserted_new_default_buttons.js
@@ -0,0 +1,78 @@
+"use strict";
+
+// NB: This uses some ugly hacks to get into the CUI module from elsewhere...
+// don't try this at home, kids.
+function test() {
+  // Customize something to make sure stuff changed:
+  CustomizableUI.addWidgetToArea("feed-button", CustomizableUI.AREA_NAVBAR);
+
+  let CustomizableUIBSPass = Cu.import("resource:///modules/CustomizableUI.jsm", {});
+
+  is(CustomizableUIBSPass.gFuturePlacements.size, 0,
+     "All future placements should be dealt with by now.");
+
+  let {CustomizableUIInternal, gFuturePlacements, gPalette} = CustomizableUIBSPass;
+
+  // Force us to have a saved state:
+  CustomizableUIInternal.saveState();
+  CustomizableUIInternal.loadSavedState();
+
+  CustomizableUIInternal._introduceNewBuiltinWidgets();
+  is(gFuturePlacements.size, 0,
+     "No change to future placements initially.");
+
+  // Add our widget to the defaults:
+  let testWidgetNew = {
+    id: "test-messing-with-default-placements-new-pref",
+    label: "Test messing with default placements - pref-based",
+    defaultArea: CustomizableUI.AREA_NAVBAR,
+    introducedInVersion: "pref",
+  };
+
+  let normalizedWidget = CustomizableUIInternal.normalizeWidget(testWidgetNew,
+                                                                CustomizableUI.SOURCE_BUILTIN);
+  ok(normalizedWidget, "Widget should be normalizable");
+  if (!normalizedWidget) {
+    return;
+  }
+  CustomizableUIBSPass.gPalette.set(testWidgetNew.id, normalizedWidget);
+
+  // Now adjust default placements for area:
+  let navbarArea = CustomizableUIBSPass.gAreas.get(CustomizableUI.AREA_NAVBAR);
+  let navbarPlacements = navbarArea.get("defaultPlacements");
+  navbarPlacements.splice(navbarPlacements.indexOf("bookmarks-menu-button") + 1, 0, testWidgetNew.id);
+
+  let savedPlacements = CustomizableUIBSPass.gSavedState.placements[CustomizableUI.AREA_NAVBAR];
+  // Then call the re-init routine so we re-add the builtin widgets
+  CustomizableUIInternal._introduceNewBuiltinWidgets();
+  is(gFuturePlacements.size, 1,
+     "Should have 1 more future placement");
+  let futureNavbarPlacements = gFuturePlacements.get(CustomizableUI.AREA_NAVBAR);
+  ok(futureNavbarPlacements, "Should have placements for nav-bar");
+  if (futureNavbarPlacements) {
+    ok(futureNavbarPlacements.has(testWidgetNew.id), "widget should be in future placements");
+  }
+  CustomizableUIInternal._placeNewDefaultWidgetsInArea(CustomizableUI.AREA_NAVBAR);
+
+  let indexInSavedPlacements = savedPlacements.indexOf(testWidgetNew.id);
+  info("Saved placements: " + savedPlacements.join(', '));
+  isnot(indexInSavedPlacements, -1, "Widget should have been inserted");
+  is(indexInSavedPlacements, savedPlacements.indexOf("bookmarks-menu-button") + 1,
+     "Widget should be in the right place.");
+
+  if (futureNavbarPlacements) {
+    ok(!futureNavbarPlacements.has(testWidgetNew.id), "widget should be out of future placements");
+  }
+
+  if (indexInSavedPlacements != -1) {
+    savedPlacements.splice(indexInSavedPlacements, 1);
+  }
+
+  gFuturePlacements.delete(CustomizableUI.AREA_NAVBAR);
+  let indexInDefaultPlacements = navbarPlacements.indexOf(testWidgetNew.id);
+  if (indexInDefaultPlacements != -1) {
+    navbarPlacements.splice(indexInDefaultPlacements, 1);
+  }
+  gPalette.delete(testWidgetNew.id);
+  CustomizableUI.reset();
+}
diff --git a/browser/components/customizableui/test/browser_873501_handle_specials.js b/browser/components/customizableui/test/browser_873501_handle_specials.js
new file mode 100644
index 000000000..b07c8e0d7
--- /dev/null
+++ b/browser/components/customizableui/test/browser_873501_handle_specials.js
@@ -0,0 +1,79 @@
+/* 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-specials-toolbar";
+
+registerCleanupFunction(removeCustomToolbars);
+
+// Add a toolbar with two springs and the downloads button.
+add_task(function* addToolbarWith2SpringsAndDownloadsButton() {
+  // Create the toolbar with a single spring:
+  createToolbarWithPlacements(kToolbarName, ["spring"]);
+  ok(document.getElementById(kToolbarName), "Toolbar should be created.");
+
+  // Check it's there with a generated ID:
+  assertAreaPlacements(kToolbarName, [/customizableui-special-spring\d+/]);
+  let [springId] = getAreaWidgetIds(kToolbarName);
+
+  // Add a second spring, check if that's there and doesn't share IDs
+  CustomizableUI.addWidgetToArea("spring", kToolbarName);
+  assertAreaPlacements(kToolbarName, [springId,
+                                      /customizableui-special-spring\d+/]);
+  let [, spring2Id] = getAreaWidgetIds(kToolbarName);
+
+  isnot(springId, spring2Id, "Springs shouldn't have identical IDs.");
+
+  // Try moving the downloads button to this new toolbar, between the two springs:
+  CustomizableUI.addWidgetToArea("downloads-button", kToolbarName, 1);
+  assertAreaPlacements(kToolbarName, [springId, "downloads-button", spring2Id]);
+  yield removeCustomToolbars();
+});
+
+// Add separators around the downloads button.
+add_task(function* addSeparatorsAroundDownloadsButton() {
+  createToolbarWithPlacements(kToolbarName, ["separator"]);
+  ok(document.getElementById(kToolbarName), "Toolbar should be created.");
+
+  // Check it's there with a generated ID:
+  assertAreaPlacements(kToolbarName, [/customizableui-special-separator\d+/]);
+  let [separatorId] = getAreaWidgetIds(kToolbarName);
+
+  CustomizableUI.addWidgetToArea("separator", kToolbarName);
+  assertAreaPlacements(kToolbarName, [separatorId,
+                                      /customizableui-special-separator\d+/]);
+  let [, separator2Id] = getAreaWidgetIds(kToolbarName);
+
+  isnot(separatorId, separator2Id, "Separator ids shouldn't be equal.");
+
+  CustomizableUI.addWidgetToArea("downloads-button", kToolbarName, 1);
+  assertAreaPlacements(kToolbarName, [separatorId, "downloads-button", separator2Id]);
+  yield removeCustomToolbars();
+});
+
+// Add spacers around the downloads button.
+add_task(function* addSpacersAroundDownloadsButton() {
+  createToolbarWithPlacements(kToolbarName, ["spacer"]);
+  ok(document.getElementById(kToolbarName), "Toolbar should be created.");
+
+  // Check it's there with a generated ID:
+  assertAreaPlacements(kToolbarName, [/customizableui-special-spacer\d+/]);
+  let [spacerId] = getAreaWidgetIds(kToolbarName);
+
+  CustomizableUI.addWidgetToArea("spacer", kToolbarName);
+  assertAreaPlacements(kToolbarName, [spacerId,
+                                      /customizableui-special-spacer\d+/]);
+  let [, spacer2Id] = getAreaWidgetIds(kToolbarName);
+
+  isnot(spacerId, spacer2Id, "Spacer ids shouldn't be equal.");
+
+  CustomizableUI.addWidgetToArea("downloads-button", kToolbarName, 1);
+  assertAreaPlacements(kToolbarName, [spacerId, "downloads-button", spacer2Id]);
+  yield removeCustomToolbars();
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js b/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
new file mode 100644
index 000000000..a3204c271
--- /dev/null
+++ b/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
@@ -0,0 +1,185 @@
+/* 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 kXULWidgetId = "a-test-button"; // we'll create a button with this ID.
+const kAPIWidgetId = "feed-button";
+const kPanel = CustomizableUI.AREA_PANEL;
+const kToolbar = CustomizableUI.AREA_NAVBAR;
+const kVisiblePalette = "customization-palette";
+const kPlaceholderClass = "panel-customization-placeholder";
+
+function checkWrapper(id) {
+  is(document.querySelectorAll("#wrapper-" + id).length, 1, "There should be exactly 1 wrapper for " + id + " in the customizing window.");
+}
+
+var move = {
+  "drag": function(id, target) {
+    let targetNode = document.getElementById(target);
+    if (targetNode.customizationTarget) {
+      targetNode = targetNode.customizationTarget;
+    }
+    simulateItemDrag(document.getElementById(id), targetNode);
+  },
+  "dragToItem": function(id, target) {
+    let targetNode = document.getElementById(target);
+    if (targetNode.customizationTarget) {
+      targetNode = targetNode.customizationTarget;
+    }
+    let items = targetNode.querySelectorAll("toolbarpaletteitem:not(." + kPlaceholderClass + ")");
+    if (target == kPanel) {
+      targetNode = items[items.length - 1];
+    } else {
+      targetNode = items[0];
+    }
+    simulateItemDrag(document.getElementById(id), targetNode);
+  },
+  "API": function(id, target) {
+    if (target == kVisiblePalette) {
+      return CustomizableUI.removeWidgetFromArea(id);
+    }
+    return CustomizableUI.addWidgetToArea(id, target, null);
+  }
+};
+
+function isLast(containerId, defaultPlacements, id) {
+  assertAreaPlacements(containerId, defaultPlacements.concat([id]));
+  is(document.getElementById(containerId).customizationTarget.lastChild.firstChild.id, id,
+     "Widget " + id + " should be in " + containerId + " in customizing window.");
+  is(otherWin.document.getElementById(containerId).customizationTarget.lastChild.id, id,
+     "Widget " + id + " should be in " + containerId + " in other window.");
+}
+
+function getLastVisibleNodeInToolbar(containerId, win=window) {
+  let container = win.document.getElementById(containerId).customizationTarget;
+  let rv = container.lastChild;
+  while (rv && (rv.getAttribute('hidden') == 'true' || (rv.firstChild && rv.firstChild.getAttribute('hidden') == 'true'))) {
+    rv = rv.previousSibling;
+  }
+  return rv;
+}
+
+function isLastVisibleInToolbar(containerId, defaultPlacements, id) {
+  let newPlacements;
+  for (let i = defaultPlacements.length - 1; i >= 0; i--) {
+    let el = document.getElementById(defaultPlacements[i]);
+    if (el && el.getAttribute('hidden') != 'true') {
+      newPlacements = [...defaultPlacements];
+      newPlacements.splice(i + 1, 0, id);
+      break;
+    }
+  }
+  if (!newPlacements) {
+    assertAreaPlacements(containerId, defaultPlacements.concat([id]));
+  } else {
+    assertAreaPlacements(containerId, newPlacements);
+  }
+  is(getLastVisibleNodeInToolbar(containerId).firstChild.id, id,
+     "Widget " + id + " should be in " + containerId + " in customizing window.");
+  is(getLastVisibleNodeInToolbar(containerId, otherWin).id, id,
+     "Widget " + id + " should be in " + containerId + " in other window.");
+}
+
+function isFirst(containerId, defaultPlacements, id) {
+  assertAreaPlacements(containerId, [id].concat(defaultPlacements));
+  is(document.getElementById(containerId).customizationTarget.firstChild.firstChild.id, id,
+     "Widget " + id + " should be in " + containerId + " in customizing window.");
+  is(otherWin.document.getElementById(containerId).customizationTarget.firstChild.id, id,
+     "Widget " + id + " should be in " + containerId + " in other window.");
+}
+
+function checkToolbar(id, method) {
+  // Place at start of the toolbar:
+  let toolbarPlacements = getAreaWidgetIds(kToolbar);
+  move[method](id, kToolbar);
+  if (method == "dragToItem") {
+    isFirst(kToolbar, toolbarPlacements, id);
+  } else if (method == "drag") {
+    isLastVisibleInToolbar(kToolbar, toolbarPlacements, id);
+  } else {
+    isLast(kToolbar, toolbarPlacements, id);
+  }
+  checkWrapper(id);
+}
+
+function checkPanel(id, method) {
+  let panelPlacements = getAreaWidgetIds(kPanel);
+  move[method](id, kPanel);
+  let children = document.getElementById(kPanel).querySelectorAll("toolbarpaletteitem:not(." + kPlaceholderClass + ")");
+  let otherChildren = otherWin.document.getElementById(kPanel).children;
+  let newPlacements = panelPlacements.concat([id]);
+  // Relative position of the new item from the end:
+  let position = -1;
+  // For the drag to item case, we drag to the last item, making the dragged item the
+  // penultimate item. We can't well use the first item because the panel has complicated
+  // rules about rearranging wide items (which, by default, the first two items are).
+  if (method == "dragToItem") {
+    newPlacements.pop();
+    newPlacements.splice(panelPlacements.length - 1, 0, id);
+    position = -2;
+  }
+  assertAreaPlacements(kPanel, newPlacements);
+  is(children[children.length + position].firstChild.id, id,
+     "Widget " + id + " should be in " + kPanel + " in customizing window.");
+  is(otherChildren[otherChildren.length + position].id, id,
+     "Widget " + id + " should be in " + kPanel + " in other window.");
+  checkWrapper(id);
+}
+
+function checkPalette(id, method) {
+  // Move back to palette:
+  move[method](id, kVisiblePalette);
+  ok(CustomizableUI.inDefaultState, "Should end in default state");
+  let visibleChildren = gCustomizeMode.visiblePalette.children;
+  let expectedChild = method == "dragToItem" ? visibleChildren[0] : visibleChildren[visibleChildren.length - 1];
+  is(expectedChild.firstChild.id, id, "Widget " + id + " was moved using " + method + " and should now be wrapped in palette in customizing window.");
+  if (id == kXULWidgetId) {
+    ok(otherWin.gNavToolbox.palette.querySelector("#" + id), "Widget " + id + " should be in invisible palette in other window.");
+  }
+  checkWrapper(id);
+}
+
+// This test needs a XUL button that's in the palette by default. No such
+// button currently exists, so we create a simple one.
+function createXULButtonForWindow(win) {
+  createDummyXULButton(kXULWidgetId, "test-button", win);
+}
+
+function removeXULButtonForWindow(win) {
+  win.gNavToolbox.palette.querySelector(`#${kXULWidgetId}`).remove();
+}
+
+var otherWin;
+
+// Moving widgets in two windows, one with customize mode and one without, should work.
+add_task(function* MoveWidgetsInTwoWindows() {
+  yield startCustomizing();
+  otherWin = yield openAndLoadWindow(null, true);
+  yield otherWin.PanelUI.ensureReady();
+  // Create the XUL button to use in the test in both windows.
+  createXULButtonForWindow(window);
+  createXULButtonForWindow(otherWin);
+  ok(CustomizableUI.inDefaultState, "Should start in default state");
+
+  for (let widgetId of [kXULWidgetId, kAPIWidgetId]) {
+    for (let method of ["API", "drag", "dragToItem"]) {
+      info("Moving widget " + widgetId + " using " + method);
+      checkToolbar(widgetId, method);
+      checkPanel(widgetId, method);
+      checkPalette(widgetId, method);
+      checkPanel(widgetId, method);
+      checkToolbar(widgetId, method);
+      checkPalette(widgetId, method);
+    }
+  }
+  yield promiseWindowClosed(otherWin);
+  otherWin = null;
+  yield endCustomizing();
+  removeXULButtonForWindow(window);
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_876944_customize_mode_create_destroy.js b/browser/components/customizableui/test/browser_876944_customize_mode_create_destroy.js
new file mode 100644
index 000000000..ec454dc8d
--- /dev/null
+++ b/browser/components/customizableui/test/browser_876944_customize_mode_create_destroy.js
@@ -0,0 +1,61 @@
+/* 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 kTestWidget1 = "test-customize-mode-create-destroy1";
+const kTestWidget2 = "test-customize-mode-create-destroy2";
+
+// Creating and destroying a widget should correctly wrap/unwrap stuff
+add_task(function* testWrapUnwrap() {
+  yield startCustomizing();
+  CustomizableUI.createWidget({id: kTestWidget1, label: 'Pretty label', tooltiptext: 'Pretty tooltip'});
+  let elem = document.getElementById(kTestWidget1);
+  let wrapper = document.getElementById("wrapper-" + kTestWidget1);
+  ok(elem, "There should be an item");
+  ok(wrapper, "There should be a wrapper");
+  is(wrapper.firstChild.id, kTestWidget1, "Wrapper should have test widget");
+  is(wrapper.parentNode.id, "customization-palette", "Wrapper should be in palette");
+  CustomizableUI.destroyWidget(kTestWidget1);
+  wrapper = document.getElementById("wrapper-" + kTestWidget1);
+  ok(!wrapper, "There should be a wrapper");
+  let item = document.getElementById(kTestWidget1);
+  ok(!item, "There should no longer be an item");
+});
+
+// Creating and destroying a widget should correctly deal with panel placeholders
+add_task(function* testPanelPlaceholders() {
+  let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+  // The value of expectedPlaceholders depends on the default palette layout.
+  // Bug 1229236 is for these tests to be smarter so the test doesn't need to
+  // change when the default placements change.
+  let expectedPlaceholders = 1 + (isInDevEdition() ? 1 : 0);
+  is(panel.querySelectorAll(".panel-customization-placeholder").length, expectedPlaceholders, "The number of placeholders should be correct.");
+  CustomizableUI.createWidget({id: kTestWidget2, label: 'Pretty label', tooltiptext: 'Pretty tooltip', defaultArea: CustomizableUI.AREA_PANEL});
+  let elem = document.getElementById(kTestWidget2);
+  let wrapper = document.getElementById("wrapper-" + kTestWidget2);
+  ok(elem, "There should be an item");
+  ok(wrapper, "There should be a wrapper");
+  is(wrapper.firstChild.id, kTestWidget2, "Wrapper should have test widget");
+  is(wrapper.parentNode, panel, "Wrapper should be in panel");
+  expectedPlaceholders = isInDevEdition() ? 1 : 3;
+  is(panel.querySelectorAll(".panel-customization-placeholder").length, expectedPlaceholders, "The number of placeholders should be correct.");
+  CustomizableUI.destroyWidget(kTestWidget2);
+  wrapper = document.getElementById("wrapper-" + kTestWidget2);
+  ok(!wrapper, "There should be a wrapper");
+  let item = document.getElementById(kTestWidget2);
+  ok(!item, "There should no longer be an item");
+  yield endCustomizing();
+});
+
+add_task(function* asyncCleanup() {
+  yield endCustomizing();
+  try {
+    CustomizableUI.destroyWidget(kTestWidget1);
+  } catch (ex) {}
+  try {
+    CustomizableUI.destroyWidget(kTestWidget2);
+  } catch (ex) {}
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_877006_missing_view.js b/browser/components/customizableui/test/browser_877006_missing_view.js
new file mode 100644
index 000000000..a1495c1fe
--- /dev/null
+++ b/browser/components/customizableui/test/browser_877006_missing_view.js
@@ -0,0 +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";
+
+// Should be able to add broken view widget
+add_task(function testAddbrokenViewWidget() {
+  const kWidgetId = 'test-877006-broken-widget';
+  let widgetSpec = {
+    id: kWidgetId,
+    type: 'view',
+    viewId: 'idontexist',
+    /* Empty handler so we try to attach it maybe? */
+    onViewShowing: function() {
+    }
+  };
+
+  let noError = true;
+  try {
+    CustomizableUI.createWidget(widgetSpec);
+    CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_NAVBAR);
+  } catch (ex) {
+    Cu.reportError(ex);
+    noError = false;
+  }
+  ok(noError, "Should not throw an exception trying to add a broken view widget.");
+
+  noError = true;
+  try {
+    CustomizableUI.destroyWidget(kWidgetId);
+  } catch (ex) {
+    Cu.reportError(ex);
+    noError = false;
+  }
+  ok(noError, "Should not throw an exception trying to remove the broken view widget.");
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_877178_unregisterArea.js b/browser/components/customizableui/test/browser_877178_unregisterArea.js
new file mode 100644
index 000000000..28037787b
--- /dev/null
+++ b/browser/components/customizableui/test/browser_877178_unregisterArea.js
@@ -0,0 +1,50 @@
+/* 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";
+
+registerCleanupFunction(removeCustomToolbars);
+
+// Sanity checks
+add_task(function sanityChecks() {
+  SimpleTest.doesThrow(() => CustomizableUI.registerArea("@foo"),
+                       "Registering areas with an invalid ID should throw.");
+
+  SimpleTest.doesThrow(() => CustomizableUI.registerArea([]),
+                       "Registering areas with an invalid ID should throw.");
+
+  SimpleTest.doesThrow(() => CustomizableUI.unregisterArea("@foo"),
+                       "Unregistering areas with an invalid ID should throw.");
+
+  SimpleTest.doesThrow(() => CustomizableUI.unregisterArea([]),
+                       "Unregistering areas with an invalid ID should throw.");
+
+  SimpleTest.doesThrow(() => CustomizableUI.unregisterArea("unknown"),
+                       "Unregistering an area that's not registered should throw.");
+});
+
+// Check areas are loaded with their default placements.
+add_task(function checkLoadedAres() {
+  ok(CustomizableUI.inDefaultState, "Everything should be in its default state.");
+});
+
+// Check registering and unregistering a new area.
+add_task(function checkRegisteringAndUnregistering() {
+  const kToolbarId = "test-registration-toolbar";
+  const kButtonId = "test-registration-button";
+  createDummyXULButton(kButtonId);
+  createToolbarWithPlacements(kToolbarId, ["spring", kButtonId, "spring"]);
+  assertAreaPlacements(kToolbarId,
+                       [/customizableui-special-spring\d+/,
+                        kButtonId,
+                        /customizableui-special-spring\d+/]);
+  ok(!CustomizableUI.inDefaultState, "With a new toolbar it is no longer in a default state.");
+  removeCustomToolbars(); // Will call unregisterArea for us
+  ok(CustomizableUI.inDefaultState, "When the toolbar is unregistered, " +
+     "everything will return to the default state.");
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_877447_skip_missing_ids.js b/browser/components/customizableui/test/browser_877447_skip_missing_ids.js
new file mode 100644
index 000000000..0cba7ae4f
--- /dev/null
+++ b/browser/components/customizableui/test/browser_877447_skip_missing_ids.js
@@ -0,0 +1,25 @@
+/* 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";
+
+registerCleanupFunction(removeCustomToolbars);
+
+add_task(function skipMissingIDS() {
+  const kButtonId = "look-at-me-disappear-button";
+  CustomizableUI.reset();
+  ok(CustomizableUI.inDefaultState, "Should be in the default state.");
+  let btn = createDummyXULButton(kButtonId, "Gone!");
+  CustomizableUI.addWidgetToArea(kButtonId, CustomizableUI.AREA_NAVBAR);
+  ok(!CustomizableUI.inDefaultState, "Should no longer be in the default state.");
+  is(btn.parentNode.parentNode.id, CustomizableUI.AREA_NAVBAR, "Button should be in navbar");
+  btn.remove();
+  is(btn.parentNode, null, "Button is no longer in the navbar");
+  ok(CustomizableUI.inDefaultState, "Should be back in the default state, " +
+                                    "despite unknown button ID in placements.");
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_878452_drag_to_panel.js b/browser/components/customizableui/test/browser_878452_drag_to_panel.js
new file mode 100644
index 000000000..8a8d82294
--- /dev/null
+++ b/browser/components/customizableui/test/browser_878452_drag_to_panel.js
@@ -0,0 +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";
+
+// Dragging an item from the palette to another button in the panel should work.
+add_task(function*() {
+  yield startCustomizing();
+  let btn = document.getElementById("feed-button");
+  let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
+
+  let lastButtonIndex = placements.length - 1;
+  let lastButton = placements[lastButtonIndex];
+  let placementsAfterInsert = placements.slice(0, lastButtonIndex).concat(["feed-button", lastButton]);
+  let lastButtonNode = document.getElementById(lastButton);
+  simulateItemDrag(btn, lastButtonNode);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterInsert);
+  ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+  let palette = document.getElementById("customization-palette");
+  simulateItemDrag(btn, palette);
+  ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+});
+
+// Dragging an item from the palette to the panel itself should also work.
+add_task(function*() {
+  yield startCustomizing();
+  let btn = document.getElementById("feed-button");
+  let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+  let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
+
+  let placementsAfterAppend = placements.concat(["feed-button"]);
+  simulateItemDrag(btn, panel);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
+  ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+  let palette = document.getElementById("customization-palette");
+  simulateItemDrag(btn, palette);
+  ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+});
+
+// Dragging an item from the palette to an empty panel should also work.
+add_task(function*() {
+  let widgetIds = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
+  while (widgetIds.length) {
+    CustomizableUI.removeWidgetFromArea(widgetIds.shift());
+  }
+  yield startCustomizing();
+  let btn = document.getElementById("feed-button");
+  let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+
+  assertAreaPlacements(panel.id, []);
+
+  let placementsAfterAppend = ["feed-button"];
+  simulateItemDrag(btn, panel);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
+  ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+  let palette = document.getElementById("customization-palette");
+  simulateItemDrag(btn, palette);
+  assertAreaPlacements(panel.id, []);
+});
+
+add_task(function* asyncCleanup() {
+  yield endCustomizing();
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_880164_customization_context_menus.js b/browser/components/customizableui/test/browser_880164_customization_context_menus.js
new file mode 100644
index 000000000..57a0db773
--- /dev/null
+++ b/browser/components/customizableui/test/browser_880164_customization_context_menus.js
@@ -0,0 +1,414 @@
+/* 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);
+
+const isOSX = (Services.appinfo.OS === "Darwin");
+
+// Right-click on the home button should
+// show a context menu with options to move it.
+add_task(function*() {
+  let contextMenu = document.getElementById("toolbar-context-menu");
+  let shownPromise = popupShown(contextMenu);
+  let homeButton = document.getElementById("home-button");
+  EventUtils.synthesizeMouse(homeButton, 2, 2, {type: "contextmenu", button: 2 });
+  yield shownPromise;
+
+  let expectedEntries = [
+    [".customize-context-moveToPanel", true],
+    [".customize-context-removeFromToolbar", true],
+    ["---"]
+  ];
+  if (!isOSX) {
+    expectedEntries.push(["#toggle_toolbar-menubar", true]);
+  }
+  expectedEntries.push(
+    ["#toggle_PersonalToolbar", true],
+    ["---"],
+    [".viewCustomizeToolbar", true]
+  );
+  checkContextMenu(contextMenu, expectedEntries);
+
+  let hiddenPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenPromise;
+});
+
+// Right-click on an empty bit of tabstrip should
+// show a context menu without options to move it,
+// but with tab-specific options instead.
+add_task(function*() {
+  // ensure there are tabs to reload/bookmark:
+  let extraTab = gBrowser.selectedTab = gBrowser.addTab();
+  yield promiseTabLoadEvent(extraTab, "http://example.com/");
+  let contextMenu = document.getElementById("toolbar-context-menu");
+  let shownPromise = popupShown(contextMenu);
+  let tabstrip = document.getElementById("tabbrowser-tabs");
+  let rect = tabstrip.getBoundingClientRect();
+  EventUtils.synthesizeMouse(tabstrip, rect.width - 2, 2, {type: "contextmenu", button: 2 });
+  yield shownPromise;
+
+  let closedTabsAvailable = SessionStore.getClosedTabCount(window) == 0;
+  info("Closed tabs: " + closedTabsAvailable);
+  let expectedEntries = [
+    ["#toolbar-context-reloadAllTabs", true],
+    ["#toolbar-context-bookmarkAllTabs", true],
+    ["#toolbar-context-undoCloseTab", !closedTabsAvailable],
+    ["---"]
+  ];
+  if (!isOSX) {
+    expectedEntries.push(["#toggle_toolbar-menubar", true]);
+  }
+  expectedEntries.push(
+    ["#toggle_PersonalToolbar", true],
+    ["---"],
+    [".viewCustomizeToolbar", true]
+  );
+  checkContextMenu(contextMenu, expectedEntries);
+
+  let hiddenPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenPromise;
+  gBrowser.removeTab(extraTab);
+});
+
+// Right-click on an empty bit of extra toolbar should
+// show a context menu with moving options disabled,
+// and a toggle option for the extra toolbar
+add_task(function*() {
+  let contextMenu = document.getElementById("toolbar-context-menu");
+  let shownPromise = popupShown(contextMenu);
+  let toolbar = createToolbarWithPlacements("880164_empty_toolbar", []);
+  toolbar.setAttribute("context", "toolbar-context-menu");
+  toolbar.setAttribute("toolbarname", "Fancy Toolbar for Context Menu");
+  EventUtils.synthesizeMouseAtCenter(toolbar, {type: "contextmenu", button: 2 });
+  yield shownPromise;
+
+  let expectedEntries = [
+    [".customize-context-moveToPanel", false],
+    [".customize-context-removeFromToolbar", false],
+    ["---"]
+  ];
+  if (!isOSX) {
+    expectedEntries.push(["#toggle_toolbar-menubar", true]);
+  }
+  expectedEntries.push(
+    ["#toggle_PersonalToolbar", true],
+    ["#toggle_880164_empty_toolbar", true],
+    ["---"],
+    [".viewCustomizeToolbar", true]
+  );
+  checkContextMenu(contextMenu, expectedEntries);
+
+  let hiddenPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenPromise;
+  removeCustomToolbars();
+});
+
+
+// Right-click on the urlbar-container should
+// show a context menu with disabled options to move it.
+add_task(function*() {
+  let contextMenu = document.getElementById("toolbar-context-menu");
+  let shownPromise = popupShown(contextMenu);
+  let urlBarContainer = document.getElementById("urlbar-container");
+  // Need to make sure not to click within an edit field.
+  EventUtils.synthesizeMouse(urlBarContainer, 100, 1, {type: "contextmenu", button: 2 });
+  yield shownPromise;
+
+  let expectedEntries = [
+    [".customize-context-moveToPanel", false],
+    [".customize-context-removeFromToolbar", false],
+    ["---"]
+  ];
+  if (!isOSX) {
+    expectedEntries.push(["#toggle_toolbar-menubar", true]);
+  }
+  expectedEntries.push(
+    ["#toggle_PersonalToolbar", true],
+    ["---"],
+    [".viewCustomizeToolbar", true]
+  );
+  checkContextMenu(contextMenu, expectedEntries);
+
+  let hiddenPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenPromise;
+});
+
+// Right-click on the searchbar and moving it to the menu
+// and back should move the search-container instead.
+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;
+  let hiddenPanelPromise = promisePanelHidden(window);
+  PanelUI.toggle({type: "command"});
+  yield hiddenPanelPromise;
+
+  gCustomizeMode.addToToolbar(searchbar);
+  placement = CustomizableUI.getPlacementOfWidget("search-container");
+  is(placement.area, CustomizableUI.AREA_NAVBAR, "Should be in navbar");
+  gCustomizeMode.removeFromArea(searchbar);
+  placement = CustomizableUI.getPlacementOfWidget("search-container");
+  is(placement, null, "Should be in palette");
+  CustomizableUI.reset();
+  placement = CustomizableUI.getPlacementOfWidget("search-container");
+  is(placement.area, CustomizableUI.AREA_NAVBAR, "Should be in navbar");
+});
+
+// Right-click on an item within the menu panel should
+// show a context menu with options to move it.
+add_task(function*() {
+  let shownPanelPromise = promisePanelShown(window);
+  PanelUI.toggle({type: "command"});
+  yield shownPanelPromise;
+
+  let contextMenu = document.getElementById("customizationPanelItemContextMenu");
+  let shownContextPromise = popupShown(contextMenu);
+  let newWindowButton = document.getElementById("new-window-button");
+  ok(newWindowButton, "new-window-button was found");
+  EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2});
+  yield shownContextPromise;
+
+  is(PanelUI.panel.state, "open", "The PanelUI should still be open.");
+
+  let expectedEntries = [
+    [".customize-context-moveToToolbar", true],
+    [".customize-context-removeFromPanel", true],
+    ["---"],
+    [".viewCustomizeToolbar", true]
+  ];
+  checkContextMenu(contextMenu, expectedEntries);
+
+  let hiddenContextPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenContextPromise;
+
+  let hiddenPromise = promisePanelHidden(window);
+  PanelUI.toggle({type: "command"});
+  yield hiddenPromise;
+});
+
+// Right-click on the home button while in customization mode
+// should show a context menu with options to move it.
+add_task(function*() {
+  yield startCustomizing();
+  let contextMenu = document.getElementById("toolbar-context-menu");
+  let shownPromise = popupShown(contextMenu);
+  let homeButton = document.getElementById("wrapper-home-button");
+  EventUtils.synthesizeMouse(homeButton, 2, 2, {type: "contextmenu", button: 2});
+  yield shownPromise;
+
+  let expectedEntries = [
+    [".customize-context-moveToPanel", true],
+    [".customize-context-removeFromToolbar", true],
+    ["---"]
+  ];
+  if (!isOSX) {
+    expectedEntries.push(["#toggle_toolbar-menubar", true]);
+  }
+  expectedEntries.push(
+    ["#toggle_PersonalToolbar", true],
+    ["---"],
+    [".viewCustomizeToolbar", false]
+  );
+  checkContextMenu(contextMenu, expectedEntries);
+
+  let hiddenContextPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenContextPromise;
+});
+
+// Right-click on an item in the palette should
+// show a context menu with options to move it.
+add_task(function*() {
+  let contextMenu = document.getElementById("customizationPaletteItemContextMenu");
+  let shownPromise = popupShown(contextMenu);
+  let openFileButton = document.getElementById("wrapper-open-file-button");
+  EventUtils.synthesizeMouse(openFileButton, 2, 2, {type: "contextmenu", button: 2});
+  yield shownPromise;
+
+  let expectedEntries = [
+    [".customize-context-addToToolbar", true],
+    [".customize-context-addToPanel", true]
+  ];
+  checkContextMenu(contextMenu, expectedEntries);
+
+  let hiddenContextPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenContextPromise;
+});
+
+// Right-click on an item in the panel while in customization mode
+// should show a context menu with options to move it.
+add_task(function*() {
+  let contextMenu = document.getElementById("customizationPanelItemContextMenu");
+  let shownPromise = popupShown(contextMenu);
+  let newWindowButton = document.getElementById("wrapper-new-window-button");
+  EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2});
+  yield shownPromise;
+
+  let expectedEntries = [
+    [".customize-context-moveToToolbar", true],
+    [".customize-context-removeFromPanel", true],
+    ["---"],
+    [".viewCustomizeToolbar", false]
+  ];
+  checkContextMenu(contextMenu, expectedEntries);
+
+  let hiddenContextPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenContextPromise;
+  yield endCustomizing();
+});
+
+// Test the toolbarbutton panel context menu in customization mode
+// without opening the panel before customization mode
+add_task(function*() {
+  this.otherWin = yield openAndLoadWindow(null, true);
+
+  yield new Promise(resolve => waitForFocus(resolve, this.otherWin));
+
+  yield startCustomizing(this.otherWin);
+
+  let contextMenu = this.otherWin.document.getElementById("customizationPanelItemContextMenu");
+  let shownPromise = popupShown(contextMenu);
+  let newWindowButton = this.otherWin.document.getElementById("wrapper-new-window-button");
+  EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2}, this.otherWin);
+  yield shownPromise;
+
+  let expectedEntries = [
+    [".customize-context-moveToToolbar", true],
+    [".customize-context-removeFromPanel", true],
+    ["---"],
+    [".viewCustomizeToolbar", false]
+  ];
+  checkContextMenu(contextMenu, expectedEntries, this.otherWin);
+
+  let hiddenContextPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenContextPromise;
+  yield endCustomizing(this.otherWin);
+  yield promiseWindowClosed(this.otherWin);
+  this.otherWin = null;
+
+  yield new Promise(resolve => waitForFocus(resolve, window));
+});
+
+// Bug 945191 - Combined buttons show wrong context menu options
+// when they are in the toolbar.
+add_task(function*() {
+  yield startCustomizing();
+  let contextMenu = document.getElementById("customizationPanelItemContextMenu");
+  let shownPromise = popupShown(contextMenu);
+  let zoomControls = document.getElementById("wrapper-zoom-controls");
+  EventUtils.synthesizeMouse(zoomControls, 2, 2, {type: "contextmenu", button: 2});
+  yield shownPromise;
+  // Execute the command to move the item from the panel to the toolbar.
+  contextMenu.childNodes[0].doCommand();
+  let hiddenPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenPromise;
+  yield endCustomizing();
+
+  zoomControls = document.getElementById("zoom-controls");
+  is(zoomControls.parentNode.id, "nav-bar-customization-target", "Zoom-controls should be on the nav-bar");
+
+  contextMenu = document.getElementById("toolbar-context-menu");
+  shownPromise = popupShown(contextMenu);
+  EventUtils.synthesizeMouse(zoomControls, 2, 2, {type: "contextmenu", button: 2});
+  yield shownPromise;
+
+  let expectedEntries = [
+    [".customize-context-moveToPanel", true],
+    [".customize-context-removeFromToolbar", true],
+    ["---"]
+  ];
+  if (!isOSX) {
+    expectedEntries.push(["#toggle_toolbar-menubar", true]);
+  }
+  expectedEntries.push(
+    ["#toggle_PersonalToolbar", true],
+    ["---"],
+    [".viewCustomizeToolbar", true]
+  );
+  checkContextMenu(contextMenu, expectedEntries);
+
+  hiddenPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenPromise;
+  yield resetCustomization();
+});
+
+// Bug 947586 - After customization, panel items show wrong context menu options
+add_task(function*() {
+  yield startCustomizing();
+  yield endCustomizing();
+
+  yield PanelUI.show();
+
+  let contextMenu = document.getElementById("customizationPanelItemContextMenu");
+  let shownContextPromise = popupShown(contextMenu);
+  let newWindowButton = document.getElementById("new-window-button");
+  ok(newWindowButton, "new-window-button was found");
+  EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2});
+  yield shownContextPromise;
+
+  is(PanelUI.panel.state, "open", "The PanelUI should still be open.");
+
+  let expectedEntries = [
+    [".customize-context-moveToToolbar", true],
+    [".customize-context-removeFromPanel", true],
+    ["---"],
+    [".viewCustomizeToolbar", true]
+  ];
+  checkContextMenu(contextMenu, expectedEntries);
+
+  let hiddenContextPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenContextPromise;
+
+  let hiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield hiddenPromise;
+});
+
+
+// Bug 982027 - moving icon around removes custom context menu.
+add_task(function*() {
+  let widgetId = "custom-context-menu-toolbarbutton";
+  let expectedContext = "myfancycontext";
+  let widget = createDummyXULButton(widgetId, "Test ctxt menu");
+  widget.setAttribute("context", expectedContext);
+  CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR);
+  is(widget.getAttribute("context"), expectedContext, "Should have context menu when added to the toolbar.");
+
+  yield startCustomizing();
+  is(widget.getAttribute("context"), "", "Should not have own context menu in the toolbar now that we're customizing.");
+  is(widget.getAttribute("wrapped-context"), expectedContext, "Should keep own context menu wrapped when in toolbar.");
+
+  let panel = PanelUI.contents;
+  simulateItemDrag(widget, panel);
+  is(widget.getAttribute("context"), "", "Should not have own context menu when in the panel.");
+  is(widget.getAttribute("wrapped-context"), expectedContext, "Should keep own context menu wrapped now that we're in the panel.");
+
+  simulateItemDrag(widget, document.getElementById("nav-bar").customizationTarget);
+  is(widget.getAttribute("context"), "", "Should not have own context menu when back in toolbar because we're still customizing.");
+  is(widget.getAttribute("wrapped-context"), expectedContext, "Should keep own context menu wrapped now that we're back in the toolbar.");
+
+  yield endCustomizing();
+  is(widget.getAttribute("context"), expectedContext, "Should have context menu again now that we're out of customize mode.");
+  CustomizableUI.removeWidgetFromArea(widgetId);
+  widget.remove();
+  ok(CustomizableUI.inDefaultState, "Should be in default state after removing button.");
+});
diff --git a/browser/components/customizableui/test/browser_880382_drag_wide_widgets_in_panel.js b/browser/components/customizableui/test/browser_880382_drag_wide_widgets_in_panel.js
new file mode 100644
index 000000000..9057d0557
--- /dev/null
+++ b/browser/components/customizableui/test/browser_880382_drag_wide_widgets_in_panel.js
@@ -0,0 +1,497 @@
+/* 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(5);
+
+// Dragging the zoom controls to be before the print button should not move any controls.
+add_task(function*() {
+  yield startCustomizing();
+  let zoomControls = document.getElementById("zoom-controls");
+  let printButton = document.getElementById("print-button");
+  let placementsAfterMove = ["edit-controls",
+                             "new-window-button",
+                             "privatebrowsing-button",
+                             "save-page-button",
+                             "zoom-controls",
+                             "print-button",
+                             "history-panelmenu",
+                             "fullscreen-button",
+                             "find-button",
+                             "preferences-button",
+                             "add-ons-button",
+                             "developer-button",
+                             "sync-button",
+                            ];
+  removeDeveloperButtonIfDevEdition(placementsAfterMove);
+  simulateItemDrag(zoomControls, printButton);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+  ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+  let newWindowButton = document.getElementById("new-window-button");
+  simulateItemDrag(zoomControls, newWindowButton);
+  ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+});
+
+// Dragging the zoom controls to be before the save button should not move any controls.
+add_task(function*() {
+  yield startCustomizing();
+  let zoomControls = document.getElementById("zoom-controls");
+  let savePageButton = document.getElementById("save-page-button");
+  let placementsAfterMove = ["edit-controls",
+                             "zoom-controls",
+                             "new-window-button",
+                             "privatebrowsing-button",
+                             "save-page-button",
+                             "print-button",
+                             "history-panelmenu",
+                             "fullscreen-button",
+                             "find-button",
+                             "preferences-button",
+                             "add-ons-button",
+                             "developer-button",
+                             "sync-button",
+                            ];
+  removeDeveloperButtonIfDevEdition(placementsAfterMove);
+  simulateItemDrag(zoomControls, savePageButton);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+  ok(CustomizableUI.inDefaultState, "Should be in default state.");
+});
+
+
+// Dragging the zoom controls to be before the new-window button should not move any widgets.
+add_task(function*() {
+  yield startCustomizing();
+  let zoomControls = document.getElementById("zoom-controls");
+  let newWindowButton = document.getElementById("new-window-button");
+  let placementsAfterMove = ["edit-controls",
+                             "zoom-controls",
+                             "new-window-button",
+                             "privatebrowsing-button",
+                             "save-page-button",
+                             "print-button",
+                             "history-panelmenu",
+                             "fullscreen-button",
+                             "find-button",
+                             "preferences-button",
+                             "add-ons-button",
+                             "developer-button",
+                             "sync-button",
+                            ];
+  removeDeveloperButtonIfDevEdition(placementsAfterMove);
+  simulateItemDrag(zoomControls, newWindowButton);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+  ok(CustomizableUI.inDefaultState, "Should still be in default state.");
+});
+
+// Dragging the zoom controls to be before the history-panelmenu should move the zoom-controls in to the row higher than the history-panelmenu.
+add_task(function*() {
+  yield startCustomizing();
+  let zoomControls = document.getElementById("zoom-controls");
+  let historyPanelMenu = document.getElementById("history-panelmenu");
+  let placementsAfterMove = ["edit-controls",
+                             "new-window-button",
+                             "privatebrowsing-button",
+                             "save-page-button",
+                             "zoom-controls",
+                             "print-button",
+                             "history-panelmenu",
+                             "fullscreen-button",
+                             "find-button",
+                             "preferences-button",
+                             "add-ons-button",
+                             "developer-button",
+                             "sync-button",
+                            ];
+  removeDeveloperButtonIfDevEdition(placementsAfterMove);
+  simulateItemDrag(zoomControls, historyPanelMenu);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+  ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+  let newWindowButton = document.getElementById("new-window-button");
+  simulateItemDrag(zoomControls, newWindowButton);
+  ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+});
+
+// Dragging the zoom controls to be before the preferences-button should move the zoom-controls
+// in to the row higher than the preferences-button.
+add_task(function*() {
+  yield startCustomizing();
+  let zoomControls = document.getElementById("zoom-controls");
+  let preferencesButton = document.getElementById("preferences-button");
+  let placementsAfterMove = ["edit-controls",
+                             "new-window-button",
+                             "privatebrowsing-button",
+                             "save-page-button",
+                             "print-button",
+                             "history-panelmenu",
+                             "fullscreen-button",
+                             "zoom-controls",
+                             "find-button",
+                             "preferences-button",
+                             "add-ons-button",
+                             "developer-button",
+                             "sync-button",
+                            ];
+  removeDeveloperButtonIfDevEdition(placementsAfterMove);
+  simulateItemDrag(zoomControls, preferencesButton);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+  ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+  let newWindowButton = document.getElementById("new-window-button");
+  simulateItemDrag(zoomControls, newWindowButton);
+  ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+});
+
+// Dragging an item from the palette to before the zoom-controls should move it and two other buttons before the zoom controls.
+add_task(function*() {
+  yield startCustomizing();
+  let openFileButton = document.getElementById("open-file-button");
+  let zoomControls = document.getElementById("zoom-controls");
+  let placementsAfterInsert = ["edit-controls",
+                               "open-file-button",
+                               "new-window-button",
+                               "privatebrowsing-button",
+                               "zoom-controls",
+                               "save-page-button",
+                               "print-button",
+                               "history-panelmenu",
+                               "fullscreen-button",
+                               "find-button",
+                               "preferences-button",
+                               "add-ons-button",
+                               "developer-button",
+                               "sync-button",
+                              ];
+  removeDeveloperButtonIfDevEdition(placementsAfterInsert);
+  simulateItemDrag(openFileButton, zoomControls);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterInsert);
+  ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+  let palette = document.getElementById("customization-palette");
+  // Check that the palette items are re-wrapped correctly.
+  let feedWrapper = document.getElementById("wrapper-feed-button");
+  let feedButton = document.getElementById("feed-button");
+  is(feedButton.parentNode, feedWrapper,
+     "feed-button should be a child of wrapper-feed-button");
+  is(feedWrapper.getAttribute("place"), "palette",
+     "The feed-button wrapper should have it's place set to 'palette'");
+  simulateItemDrag(openFileButton, palette);
+  is(openFileButton.parentNode.tagName, "toolbarpaletteitem",
+     "The open-file-button should be wrapped by a toolbarpaletteitem");
+  let newWindowButton = document.getElementById("new-window-button");
+  simulateItemDrag(zoomControls, newWindowButton);
+  ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+});
+
+// Dragging an item from the palette to before the edit-controls
+// should move it and two other buttons before the edit and zoom controls.
+add_task(function*() {
+  yield startCustomizing();
+  let openFileButton = document.getElementById("open-file-button");
+  let editControls = document.getElementById("edit-controls");
+  let placementsAfterInsert = ["open-file-button",
+                               "new-window-button",
+                               "privatebrowsing-button",
+                               "edit-controls",
+                               "zoom-controls",
+                               "save-page-button",
+                               "print-button",
+                               "history-panelmenu",
+                               "fullscreen-button",
+                               "find-button",
+                               "preferences-button",
+                               "add-ons-button",
+                               "developer-button",
+                               "sync-button",
+                              ];
+  removeDeveloperButtonIfDevEdition(placementsAfterInsert);
+  simulateItemDrag(openFileButton, editControls);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterInsert);
+  ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+  let palette = document.getElementById("customization-palette");
+  // Check that the palette items are re-wrapped correctly.
+  let feedWrapper = document.getElementById("wrapper-feed-button");
+  let feedButton = document.getElementById("feed-button");
+  is(feedButton.parentNode, feedWrapper,
+     "feed-button should be a child of wrapper-feed-button");
+  is(feedWrapper.getAttribute("place"), "palette",
+     "The feed-button wrapper should have it's place set to 'palette'");
+  simulateItemDrag(openFileButton, palette);
+  is(openFileButton.parentNode.tagName, "toolbarpaletteitem",
+     "The open-file-button should be wrapped by a toolbarpaletteitem");
+  ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+});
+
+// Dragging the edit-controls to be before the zoom-controls button
+// should not move any widgets.
+add_task(function*() {
+  yield startCustomizing();
+  let editControls = document.getElementById("edit-controls");
+  let zoomControls = document.getElementById("zoom-controls");
+  let placementsAfterMove = ["edit-controls",
+                             "zoom-controls",
+                             "new-window-button",
+                             "privatebrowsing-button",
+                             "save-page-button",
+                             "print-button",
+                             "history-panelmenu",
+                             "fullscreen-button",
+                             "find-button",
+                             "preferences-button",
+                             "add-ons-button",
+                             "developer-button",
+                             "sync-button",
+                            ];
+  removeDeveloperButtonIfDevEdition(placementsAfterMove);
+  simulateItemDrag(editControls, zoomControls);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+  ok(CustomizableUI.inDefaultState, "Should still be in default state.");
+});
+
+// Dragging the edit-controls to be before the new-window-button should
+// move the zoom-controls before the edit-controls.
+add_task(function*() {
+  yield startCustomizing();
+  let editControls = document.getElementById("edit-controls");
+  let newWindowButton = document.getElementById("new-window-button");
+  let placementsAfterMove = ["zoom-controls",
+                             "edit-controls",
+                             "new-window-button",
+                             "privatebrowsing-button",
+                             "save-page-button",
+                             "print-button",
+                             "history-panelmenu",
+                             "fullscreen-button",
+                             "find-button",
+                             "preferences-button",
+                             "add-ons-button",
+                             "developer-button",
+                             "sync-button",
+                            ];
+  removeDeveloperButtonIfDevEdition(placementsAfterMove);
+  simulateItemDrag(editControls, newWindowButton);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+  let zoomControls = document.getElementById("zoom-controls");
+  simulateItemDrag(editControls, zoomControls);
+  ok(CustomizableUI.inDefaultState, "Should still be in default state.");
+});
+
+// Dragging the edit-controls to be before the privatebrowsing-button
+// should move the edit-controls in to the row higher than the
+// privatebrowsing-button.
+add_task(function*() {
+  yield startCustomizing();
+  let editControls = document.getElementById("edit-controls");
+  let privateBrowsingButton = document.getElementById("privatebrowsing-button");
+  let placementsAfterMove = ["zoom-controls",
+                             "edit-controls",
+                             "new-window-button",
+                             "privatebrowsing-button",
+                             "save-page-button",
+                             "print-button",
+                             "history-panelmenu",
+                             "fullscreen-button",
+                             "find-button",
+                             "preferences-button",
+                             "add-ons-button",
+                             "developer-button",
+                             "sync-button",
+                            ];
+  removeDeveloperButtonIfDevEdition(placementsAfterMove);
+  simulateItemDrag(editControls, privateBrowsingButton);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+  let zoomControls = document.getElementById("zoom-controls");
+  simulateItemDrag(editControls, zoomControls);
+  ok(CustomizableUI.inDefaultState, "Should still be in default state.");
+});
+
+// Dragging the edit-controls to be before the save-page-button
+// should move the edit-controls in to the row higher than the
+// save-page-button.
+add_task(function*() {
+  yield startCustomizing();
+  let editControls = document.getElementById("edit-controls");
+  let savePageButton = document.getElementById("save-page-button");
+  let placementsAfterMove = ["zoom-controls",
+                             "edit-controls",
+                             "new-window-button",
+                             "privatebrowsing-button",
+                             "save-page-button",
+                             "print-button",
+                             "history-panelmenu",
+                             "fullscreen-button",
+                             "find-button",
+                             "preferences-button",
+                             "add-ons-button",
+                             "developer-button",
+                             "sync-button",
+                            ];
+  removeDeveloperButtonIfDevEdition(placementsAfterMove);
+  simulateItemDrag(editControls, savePageButton);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+  let zoomControls = document.getElementById("zoom-controls");
+  simulateItemDrag(editControls, zoomControls);
+  ok(CustomizableUI.inDefaultState, "Should still be in default state.");
+});
+
+// Dragging the edit-controls to the panel itself should append
+// the edit controls to the bottom of the panel.
+add_task(function*() {
+  yield startCustomizing();
+  let editControls = document.getElementById("edit-controls");
+  let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+  let placementsAfterMove = ["zoom-controls",
+                             "new-window-button",
+                             "privatebrowsing-button",
+                             "save-page-button",
+                             "print-button",
+                             "history-panelmenu",
+                             "fullscreen-button",
+                             "find-button",
+                             "preferences-button",
+                             "add-ons-button",
+                             "edit-controls",
+                             "developer-button",
+                             "sync-button",
+                            ];
+  removeDeveloperButtonIfDevEdition(placementsAfterMove);
+  simulateItemDrag(editControls, panel);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+  let zoomControls = document.getElementById("zoom-controls");
+  simulateItemDrag(editControls, zoomControls);
+  ok(CustomizableUI.inDefaultState, "Should still be in default state.");
+});
+
+// Dragging the edit-controls to the customization-palette and
+// back should work.
+add_task(function*() {
+  yield startCustomizing();
+  let editControls = document.getElementById("edit-controls");
+  let palette = document.getElementById("customization-palette");
+  let placementsAfterMove = ["zoom-controls",
+                             "new-window-button",
+                             "privatebrowsing-button",
+                             "save-page-button",
+                             "print-button",
+                             "history-panelmenu",
+                             "fullscreen-button",
+                             "find-button",
+                             "preferences-button",
+                             "add-ons-button",
+                             "developer-button",
+                             "sync-button",
+                            ];
+  removeDeveloperButtonIfDevEdition(placementsAfterMove);
+  let paletteChildElementCount = palette.childElementCount;
+  simulateItemDrag(editControls, palette);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+  is(paletteChildElementCount + 1, palette.childElementCount,
+     "The palette should have a new child, congratulations!");
+  is(editControls.parentNode.id, "wrapper-edit-controls",
+     "The edit-controls should be properly wrapped.");
+  is(editControls.parentNode.getAttribute("place"), "palette",
+     "The edit-controls should have the place of 'palette'.");
+  let zoomControls = document.getElementById("zoom-controls");
+  simulateItemDrag(editControls, zoomControls);
+  is(paletteChildElementCount, palette.childElementCount,
+     "The palette child count should have returned to its prior value.");
+  ok(CustomizableUI.inDefaultState, "Should still be in default state.");
+});
+
+// Dragging the edit-controls to each of the panel placeholders
+// should append the edit-controls to the bottom of the panel.
+add_task(function*() {
+  yield startCustomizing();
+  let editControls = document.getElementById("edit-controls");
+  let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+  let numPlaceholders = 2;
+  for (let i = 0; i < numPlaceholders; i++) {
+    // This test relies on there being a specific number of widgets in the
+    // panel. The addition of sync-button screwed this up, so we remove it
+    // here. We should either fix the tests to not rely on the specific layout,
+    // or fix bug 1007910 which would change the placeholder logic in different
+    // ways. Bug 1229236 is for these tests to be smarter.
+    CustomizableUI.removeWidgetFromArea("sync-button");
+    // NB: We can't just iterate over all of the placeholders
+    // because each drag-drop action recreates them.
+    let placeholder = panel.getElementsByClassName("panel-customization-placeholder")[i];
+    let placementsAfterMove = ["zoom-controls",
+                               "new-window-button",
+                               "privatebrowsing-button",
+                               "save-page-button",
+                               "print-button",
+                               "history-panelmenu",
+                               "fullscreen-button",
+                               "find-button",
+                               "preferences-button",
+                               "add-ons-button",
+                               "edit-controls",
+                               "developer-button"];
+    removeDeveloperButtonIfDevEdition(placementsAfterMove);
+    simulateItemDrag(editControls, placeholder);
+    assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+    let zoomControls = document.getElementById("zoom-controls");
+    simulateItemDrag(editControls, zoomControls);
+    CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
+    ok(CustomizableUI.inDefaultState, "Should still be in default state.");
+  }
+});
+
+// Dragging the open-file-button back on to itself should work.
+add_task(function*() {
+  yield startCustomizing();
+  let openFileButton = document.getElementById("open-file-button");
+  is(openFileButton.parentNode.tagName, "toolbarpaletteitem",
+     "open-file-button should be wrapped by a toolbarpaletteitem");
+  simulateItemDrag(openFileButton, openFileButton);
+  is(openFileButton.parentNode.tagName, "toolbarpaletteitem",
+     "open-file-button should be wrapped by a toolbarpaletteitem");
+  let editControls = document.getElementById("edit-controls");
+  is(editControls.parentNode.tagName, "toolbarpaletteitem",
+     "edit-controls should be wrapped by a toolbarpaletteitem");
+  ok(CustomizableUI.inDefaultState, "Should still be in default state.");
+});
+
+// Dragging a small button onto the last big button should work.
+add_task(function*() {
+  // Bug 1007910 requires there be a placeholder on the final row for this
+  // test to work as written. The addition of sync-button meant that's not true
+  // so we remove it from here. Bug 1229236 is for these tests to be smarter.
+  CustomizableUI.removeWidgetFromArea("sync-button");
+  yield startCustomizing();
+  let editControls = document.getElementById("edit-controls");
+  let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+  let target = panel.getElementsByClassName("panel-customization-placeholder")[0];
+  let placementsAfterMove = ["zoom-controls",
+                             "new-window-button",
+                             "privatebrowsing-button",
+                             "save-page-button",
+                             "print-button",
+                             "history-panelmenu",
+                             "fullscreen-button",
+                             "find-button",
+                             "preferences-button",
+                             "add-ons-button",
+                             "edit-controls",
+                             "developer-button"];
+  removeDeveloperButtonIfDevEdition(placementsAfterMove);
+  simulateItemDrag(editControls, target);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+  let itemToDrag = "email-link-button"; // any button in the palette by default.
+  let button = document.getElementById(itemToDrag);
+  placementsAfterMove.splice(11, 0, itemToDrag);
+  simulateItemDrag(button, editControls);
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
+
+  // Put stuff back:
+  let palette = document.getElementById("customization-palette");
+  let zoomControls = document.getElementById("zoom-controls");
+  simulateItemDrag(button, palette);
+  simulateItemDrag(editControls, zoomControls);
+  CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
+  ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+});
+
+add_task(function* asyncCleanup() {
+  yield endCustomizing();
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_884402_customize_from_overflow.js b/browser/components/customizableui/test/browser_884402_customize_from_overflow.js
new file mode 100644
index 000000000..f50767c06
--- /dev/null
+++ b/browser/components/customizableui/test/browser_884402_customize_from_overflow.js
@@ -0,0 +1,81 @@
+"use strict";
+
+var overflowPanel = document.getElementById("widget-overflow");
+
+const isOSX = (Services.appinfo.OS === "Darwin");
+
+var originalWindowWidth;
+registerCleanupFunction(function() {
+  overflowPanel.removeAttribute("animate");
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+});
+
+// Right-click on an item within the overflow panel should
+// show a context menu with options to move it.
+add_task(function*() {
+
+  overflowPanel.setAttribute("animate", "false");
+
+  originalWindowWidth = window.outerWidth;
+  let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+  ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
+  window.resizeTo(400, window.outerHeight);
+
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+  ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+
+  let chevron = document.getElementById("nav-bar-overflow-button");
+  let shownPanelPromise = promisePanelElementShown(window, overflowPanel);
+  chevron.click();
+  yield shownPanelPromise;
+
+  let contextMenu = document.getElementById("toolbar-context-menu");
+  let shownContextPromise = popupShown(contextMenu);
+  let homeButton = document.getElementById("home-button");
+  ok(homeButton, "home-button was found");
+  is(homeButton.getAttribute("overflowedItem"), "true", "Home button is overflowing");
+  EventUtils.synthesizeMouse(homeButton, 2, 2, {type: "contextmenu", button: 2});
+  yield shownContextPromise;
+
+  is(overflowPanel.state, "open", "The widget overflow panel should still be open.");
+
+  let expectedEntries = [
+    [".customize-context-moveToPanel", true],
+    [".customize-context-removeFromToolbar", true],
+    ["---"]
+  ];
+  if (!isOSX) {
+    expectedEntries.push(["#toggle_toolbar-menubar", true]);
+  }
+  expectedEntries.push(
+    ["#toggle_PersonalToolbar", true],
+    ["---"],
+    [".viewCustomizeToolbar", true]
+  );
+  checkContextMenu(contextMenu, expectedEntries);
+
+  let hiddenContextPromise = popupHidden(contextMenu);
+  let hiddenPromise = promisePanelElementHidden(window, overflowPanel);
+  let moveToPanel = contextMenu.querySelector(".customize-context-moveToPanel");
+  if (moveToPanel) {
+    moveToPanel.click();
+  }
+  contextMenu.hidePopup();
+  yield hiddenContextPromise;
+  yield hiddenPromise;
+
+  let homeButtonPlacement = CustomizableUI.getPlacementOfWidget("home-button");
+  ok(homeButtonPlacement, "Home button should still have a placement");
+  is(homeButtonPlacement && homeButtonPlacement.area, "PanelUI-contents", "Home button should be in the panel now");
+  CustomizableUI.reset();
+
+  // In some cases, it can take a tick for the navbar to overflow again. Wait for it:
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+  ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+
+  homeButtonPlacement = CustomizableUI.getPlacementOfWidget("home-button");
+  ok(homeButtonPlacement, "Home button should still have a placement");
+  is(homeButtonPlacement && homeButtonPlacement.area, "nav-bar", "Home button should be back in the navbar now");
+
+  is(homeButton.getAttribute("overflowedItem"), "true", "Home button should still be overflowed");
+});
diff --git a/browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js b/browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js
new file mode 100644
index 000000000..ea6f5a4e3
--- /dev/null
+++ b/browser/components/customizableui/test/browser_885052_customize_mode_observers_disabed.js
@@ -0,0 +1,45 @@
+/* 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 isFullscreenSizeMode() {
+  let sizemode = document.documentElement.getAttribute("sizemode");
+  return sizemode == "fullscreen";
+}
+
+// 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.")
+  ok(!isFullscreenSizeMode(), "Should not be in fullscreen sizemode before we enter fullscreen.");
+
+  BrowserFullScreen();
+  yield waitForCondition(() => isFullscreenSizeMode());
+  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(() => !isFullscreenSizeMode());
+  ok(!fullscreenButton.checked, "Fullscreen button should not be checked when not in fullscreen.")
+});
diff --git a/browser/components/customizableui/test/browser_885530_showInPrivateBrowsing.js b/browser/components/customizableui/test/browser_885530_showInPrivateBrowsing.js
new file mode 100644
index 000000000..e55c21862
--- /dev/null
+++ b/browser/components/customizableui/test/browser_885530_showInPrivateBrowsing.js
@@ -0,0 +1,134 @@
+/* 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 kWidgetId = "some-widget";
+
+function assertWidgetExists(aWindow, aExists) {
+  if (aExists) {
+    ok(aWindow.document.getElementById(kWidgetId),
+       "Should have found test widget in the window");
+  } else {
+    is(aWindow.document.getElementById(kWidgetId), null,
+       "Should not have found test widget in the window");
+  }
+}
+
+// A widget that is created with showInPrivateBrowsing undefined should
+// have that value default to true.
+add_task(function() {
+  let wrapper = CustomizableUI.createWidget({
+    id: kWidgetId
+  });
+  ok(wrapper.showInPrivateBrowsing,
+     "showInPrivateBrowsing should have defaulted to true.");
+  CustomizableUI.destroyWidget(kWidgetId);
+});
+
+// Add a widget via the API with showInPrivateBrowsing set to false
+// and ensure it does not appear in pre-existing or newly created
+// private windows.
+add_task(function*() {
+  let plain1 = yield openAndLoadWindow();
+  let private1 = yield openAndLoadWindow({private: true});
+  CustomizableUI.createWidget({
+    id: kWidgetId,
+    removable: true,
+    showInPrivateBrowsing: false
+  });
+  CustomizableUI.addWidgetToArea(kWidgetId,
+                                 CustomizableUI.AREA_NAVBAR);
+  assertWidgetExists(plain1, true);
+  assertWidgetExists(private1, false);
+
+  // Now open up some new windows. The widget should exist in the new
+  // plain window, but not the new private window.
+  let plain2 = yield openAndLoadWindow();
+  let private2 = yield openAndLoadWindow({private: true});
+  assertWidgetExists(plain2, true);
+  assertWidgetExists(private2, false);
+
+  // Try moving the widget around and make sure it doesn't get added
+  // to the private windows. We'll start by appending it to the tabstrip.
+  CustomizableUI.addWidgetToArea(kWidgetId,
+                                 CustomizableUI.AREA_TABSTRIP);
+  assertWidgetExists(plain1, true);
+  assertWidgetExists(plain2, true);
+  assertWidgetExists(private1, false);
+  assertWidgetExists(private2, false);
+
+  // And then move it to the beginning of the tabstrip.
+  CustomizableUI.moveWidgetWithinArea(kWidgetId, 0);
+  assertWidgetExists(plain1, true);
+  assertWidgetExists(plain2, true);
+  assertWidgetExists(private1, false);
+  assertWidgetExists(private2, false);
+
+  CustomizableUI.removeWidgetFromArea("some-widget");
+  assertWidgetExists(plain1, false);
+  assertWidgetExists(plain2, false);
+  assertWidgetExists(private1, false);
+  assertWidgetExists(private2, false);
+
+  yield Promise.all([plain1, plain2, private1, private2].map(promiseWindowClosed));
+
+  CustomizableUI.destroyWidget("some-widget");
+});
+
+// Add a widget via the API with showInPrivateBrowsing set to true,
+// and ensure that it appears in pre-existing or newly created
+// private browsing windows.
+add_task(function*() {
+  let plain1 = yield openAndLoadWindow();
+  let private1 = yield openAndLoadWindow({private: true});
+
+  CustomizableUI.createWidget({
+    id: kWidgetId,
+    removable: true,
+    showInPrivateBrowsing: true
+  });
+  CustomizableUI.addWidgetToArea(kWidgetId,
+                                 CustomizableUI.AREA_NAVBAR);
+  assertWidgetExists(plain1, true);
+  assertWidgetExists(private1, true);
+
+  // Now open up some new windows. The widget should exist in the new
+  // plain window, but not the new private window.
+  let plain2 = yield openAndLoadWindow();
+  let private2 = yield openAndLoadWindow({private: true});
+
+  assertWidgetExists(plain2, true);
+  assertWidgetExists(private2, true);
+
+  // Try moving the widget around and make sure it doesn't get added
+  // to the private windows. We'll start by appending it to the tabstrip.
+  CustomizableUI.addWidgetToArea(kWidgetId,
+                                 CustomizableUI.AREA_TABSTRIP);
+  assertWidgetExists(plain1, true);
+  assertWidgetExists(plain2, true);
+  assertWidgetExists(private1, true);
+  assertWidgetExists(private2, true);
+
+  // And then move it to the beginning of the tabstrip.
+  CustomizableUI.moveWidgetWithinArea(kWidgetId, 0);
+  assertWidgetExists(plain1, true);
+  assertWidgetExists(plain2, true);
+  assertWidgetExists(private1, true);
+  assertWidgetExists(private2, true);
+
+  CustomizableUI.removeWidgetFromArea("some-widget");
+  assertWidgetExists(plain1, false);
+  assertWidgetExists(plain2, false);
+  assertWidgetExists(private1, false);
+  assertWidgetExists(private2, false);
+
+  yield Promise.all([plain1, plain2, private1, private2].map(promiseWindowClosed));
+
+  CustomizableUI.destroyWidget("some-widget");
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_886323_buildArea_removable_nodes.js b/browser/components/customizableui/test/browser_886323_buildArea_removable_nodes.js
new file mode 100644
index 000000000..f46141c4f
--- /dev/null
+++ b/browser/components/customizableui/test/browser_886323_buildArea_removable_nodes.js
@@ -0,0 +1,46 @@
+/* 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 kButtonId = "test-886323-removable-moved-node";
+const kLazyAreaId = "test-886323-lazy-area-for-removability-testing";
+
+var gNavBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+var gLazyArea;
+
+// Removable nodes shouldn't be moved by buildArea
+add_task(function*() {
+  let dummyBtn = createDummyXULButton(kButtonId, "Dummy");
+  dummyBtn.setAttribute("removable", "true");
+  gNavBar.customizationTarget.appendChild(dummyBtn);
+  let popupSet = document.getElementById("mainPopupSet");
+  gLazyArea = document.createElementNS(kNSXUL, "panel");
+  gLazyArea.id = kLazyAreaId;
+  gLazyArea.setAttribute("hidden", "true");
+  popupSet.appendChild(gLazyArea);
+  CustomizableUI.registerArea(kLazyAreaId, {
+    type: CustomizableUI.TYPE_MENU_PANEL,
+    defaultPlacements: []
+  });
+  CustomizableUI.addWidgetToArea(kButtonId, kLazyAreaId);
+  assertAreaPlacements(kLazyAreaId, [kButtonId],
+                       "Placements should have changed because widget is removable.");
+  let btn = document.getElementById(kButtonId);
+  btn.setAttribute("removable", "false");
+  gLazyArea.customizationTarget = gLazyArea;
+  CustomizableUI.registerToolbarNode(gLazyArea, []);
+  assertAreaPlacements(kLazyAreaId, [], "Placements should no longer include widget.");
+  is(btn.parentNode.id, gNavBar.customizationTarget.id,
+     "Button shouldn't actually have moved as it's not removable");
+  btn = document.getElementById(kButtonId);
+  if (btn) btn.remove();
+  CustomizableUI.removeWidgetFromArea(kButtonId);
+  CustomizableUI.unregisterArea(kLazyAreaId);
+  gLazyArea.remove();
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_887438_currentset_shim.js b/browser/components/customizableui/test/browser_887438_currentset_shim.js
new file mode 100644
index 000000000..a04299819
--- /dev/null
+++ b/browser/components/customizableui/test/browser_887438_currentset_shim.js
@@ -0,0 +1,75 @@
+/* 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";
+
+var navbar = document.getElementById("nav-bar");
+var navbarCT = navbar.customizationTarget;
+var overflowPanelList = document.getElementById("widget-overflow-list");
+
+// Reading currentset
+add_task(function() {
+  let nodeIds = [];
+  for (let node of navbarCT.childNodes) {
+    if (node.getAttribute("skipintoolbarset") != "true") {
+      nodeIds.push(node.id);
+    }
+  }
+  for (let node of overflowPanelList.childNodes) {
+    if (node.getAttribute("skipintoolbarset") != "true") {
+      nodeIds.push(node.id);
+    }
+  }
+  let currentSet = navbar.currentSet;
+  is(currentSet.split(',').length, nodeIds.length, "Should be just as many nodes as there are.");
+  is(currentSet, nodeIds.join(','), "Current set and node IDs should match.");
+});
+
+// Insert, then remove items
+add_task(function() {
+  let currentSet = navbar.currentSet;
+  let newCurrentSet = currentSet.replace('home-button', 'feed-button,sync-button,home-button');
+  navbar.currentSet = newCurrentSet;
+  is(newCurrentSet, navbar.currentSet, "Current set should match expected current set.");
+  let feedBtn = document.getElementById("feed-button");
+  let syncBtn = document.getElementById("sync-button");
+  ok(feedBtn, "Feed button should have been added.");
+  ok(syncBtn, "Sync button should have been added.");
+  if (feedBtn && syncBtn) {
+    let feedParent = feedBtn.parentNode;
+    let syncParent = syncBtn.parentNode;
+    ok(feedParent == navbarCT || feedParent == overflowPanelList,
+       "Feed button should be in navbar or overflow");
+    ok(syncParent == navbarCT || syncParent == overflowPanelList,
+       "Feed button should be in navbar or overflow");
+    is(feedBtn.nextElementSibling, syncBtn, "Feed button should be next to sync button.");
+    let homeBtn = document.getElementById("home-button");
+    is(syncBtn.nextElementSibling, homeBtn, "Sync button should be next to home button.");
+  }
+  navbar.currentSet = currentSet;
+  is(currentSet, navbar.currentSet, "Should be able to remove the added items.");
+});
+
+// Simultaneous insert/remove:
+add_task(function() {
+  let currentSet = navbar.currentSet;
+  let newCurrentSet = currentSet.replace('home-button', 'feed-button');
+  navbar.currentSet = newCurrentSet;
+  is(newCurrentSet, navbar.currentSet, "Current set should match expected current set.");
+  let feedBtn = document.getElementById("feed-button");
+  ok(feedBtn, "Feed button should have been added.");
+  let homeBtn = document.getElementById("home-button");
+  ok(!homeBtn, "Home button should have been removed.");
+  if (feedBtn) {
+    let feedParent = feedBtn.parentNode;
+    ok(feedParent == navbarCT || feedParent == overflowPanelList,
+       "Feed button should be in navbar or overflow");
+  }
+  navbar.currentSet = currentSet;
+  is(currentSet, navbar.currentSet, "Should be able to return to original state.");
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_888817_currentset_updating.js b/browser/components/customizableui/test/browser_888817_currentset_updating.js
new file mode 100644
index 000000000..6e7c4e95a
--- /dev/null
+++ b/browser/components/customizableui/test/browser_888817_currentset_updating.js
@@ -0,0 +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);
+  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();
+});
diff --git a/browser/components/customizableui/test/browser_890140_orphaned_placeholders.js b/browser/components/customizableui/test/browser_890140_orphaned_placeholders.js
new file mode 100644
index 000000000..84b126a9b
--- /dev/null
+++ b/browser/components/customizableui/test/browser_890140_orphaned_placeholders.js
@@ -0,0 +1,210 @@
+/* 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();
+
+  if (isInDevEdition()) {
+    CustomizableUI.addWidgetToArea("developer-button", CustomizableUI.AREA_PANEL);
+    ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+  }
+  if (!isInDevEdition()) {
+    ok(CustomizableUI.inDefaultState, "Should be in default state.");
+  } else {
+    ok(!CustomizableUI.inDefaultState, "Should not be in default state if on DevEdition.");
+  }
+
+  // This test relies on an exact number of widgets being in the panel.
+  // Remove the sync-button to satisfy that. (bug 1229236)
+  CustomizableUI.removeWidgetFromArea("sync-button");
+  let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+  let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
+
+  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 (isInDevEdition()) {
+    CustomizableUI.addWidgetToArea("developer-button", CustomizableUI.AREA_NAVBAR, 2);
+  }
+
+  CustomizableUI.addWidgetToArea("sync-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();
+
+  if (isInDevEdition()) {
+    CustomizableUI.addWidgetToArea("developer-button", CustomizableUI.AREA_PANEL);
+  }
+
+  // This test relies on an exact number of widgets being in the panel.
+  // Remove the sync-button to satisfy that. (bug 1229236)
+  CustomizableUI.removeWidgetFromArea("sync-button");
+
+  let btn = document.getElementById("open-file-button");
+  let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+  let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
+  let placementsAfterAppend = placements;
+
+  placementsAfterAppend = placements.concat(["open-file-button"]);
+  simulateItemDrag(btn, panel);
+
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
+
+  ok(!CustomizableUI.inDefaultState, "Should not 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");
+
+  let palette = document.getElementById("customization-palette");
+  simulateItemDrag(btn, palette);
+
+  btn = document.getElementById("open-file-button");
+  simulateItemDrag(btn, palette);
+
+  if (isInDevEdition()) {
+    CustomizableUI.addWidgetToArea("developer-button", CustomizableUI.AREA_NAVBAR, 2);
+  }
+
+  CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
+  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();
+
+  if (isInDevEdition()) {
+    CustomizableUI.addWidgetToArea("developer-button", CustomizableUI.AREA_PANEL);
+  }
+  // This test relies on an exact number of widgets being in the panel.
+  // Remove the sync-button to satisfy that. (bug 1229236)
+  CustomizableUI.removeWidgetFromArea("sync-button");
+
+  let btn = document.getElementById("add-ons-button");
+  let btn2 = document.getElementById("developer-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);
+
+  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);
+
+  assertAreaPlacements(CustomizableUI.AREA_PANEL, placements);
+
+  if (isInDevEdition()) {
+    CustomizableUI.addWidgetToArea("developer-button", CustomizableUI.AREA_NAVBAR, 2);
+  }
+
+  CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
+  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();
+
+  if (isInDevEdition()) {
+    CustomizableUI.addWidgetToArea("developer-button", CustomizableUI.AREA_PANEL);
+  }
+
+  // This test relies on an exact number of widgets being in the panel.
+  // Remove the sync-button to satisfy that. (bug 1229236)
+  CustomizableUI.removeWidgetFromArea("sync-button");
+
+  let btn = document.getElementById("edit-controls");
+  let btn2 = document.getElementById("developer-button");
+  let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+  let palette = document.getElementById("customization-palette");
+  let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
+
+  placements.pop();
+  simulateItemDrag(btn2, 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(btn2, panel);
+
+  let zoomControls = document.getElementById("zoom-controls");
+  simulateItemDrag(btn, zoomControls);
+
+  if (isInDevEdition()) {
+    CustomizableUI.addWidgetToArea("developer-button", CustomizableUI.AREA_NAVBAR, 2);
+  }
+
+  CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
+  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 = -1;
+
+  if (isInDevEdition()) {
+    numPlaceholders = 3;
+  } else {
+    numPlaceholders = 2;
+  }
+
+  let panel = document.getElementById(CustomizableUI.AREA_PANEL);
+  ok(CustomizableUI.inDefaultState, "Should be in default state.");
+
+  // This test relies on an exact number of widgets being in the panel.
+  // Remove the sync-button to satisfy that. (bug 1229236)
+  CustomizableUI.removeWidgetFromArea("sync-button");
+
+  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");
+
+  CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
+  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;
+}
diff --git a/browser/components/customizableui/test/browser_890262_destroyWidget_after_add_to_panel.js b/browser/components/customizableui/test/browser_890262_destroyWidget_after_add_to_panel.js
new file mode 100644
index 000000000..13f2bd7ba
--- /dev/null
+++ b/browser/components/customizableui/test/browser_890262_destroyWidget_after_add_to_panel.js
@@ -0,0 +1,68 @@
+/* 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 kLazyAreaId = "test-890262-lazy-area";
+const kWidget1Id  = "test-890262-widget1";
+const kWidget2Id  = "test-890262-widget2";
+
+setupArea();
+
+// Destroying a widget after defaulting it to a non-legacy area should work.
+add_task(function() {
+  CustomizableUI.createWidget({
+    id: kWidget1Id,
+    removable: true,
+    defaultArea: kLazyAreaId
+  });
+  let noError = true;
+  try {
+    CustomizableUI.destroyWidget(kWidget1Id);
+  } catch (ex) {
+    Cu.reportError(ex);
+    noError = false;
+  }
+  ok(noError, "Shouldn't throw an exception for a widget that was created in a not-yet-constructed area");
+});
+
+// Destroying a widget after moving it to a non-legacy area should work.
+add_task(function() {
+  CustomizableUI.createWidget({
+    id: kWidget2Id,
+    removable: true,
+    defaultArea: CustomizableUI.AREA_NAVBAR
+  });
+
+  CustomizableUI.addWidgetToArea(kWidget2Id, kLazyAreaId);
+  let noError = true;
+  try {
+    CustomizableUI.destroyWidget(kWidget2Id);
+  } catch (ex) {
+    Cu.reportError(ex);
+    noError = false;
+  }
+  ok(noError, "Shouldn't throw an exception for a widget that was added to a not-yet-constructed area");
+});
+
+add_task(function* asyncCleanup() {
+  let lazyArea = document.getElementById(kLazyAreaId);
+  if (lazyArea) {
+    lazyArea.remove();
+  }
+  try {
+    CustomizableUI.unregisterArea(kLazyAreaId);
+  } catch (ex) {} // If we didn't register successfully for some reason
+  yield resetCustomization();
+});
+
+function setupArea() {
+  let lazyArea = document.createElementNS(kNSXUL, "hbox");
+  lazyArea.id = kLazyAreaId;
+  document.getElementById("nav-bar").appendChild(lazyArea);
+  CustomizableUI.registerArea(kLazyAreaId, {
+    type: CustomizableUI.TYPE_TOOLBAR,
+    defaultPlacements: []
+  });
+}
diff --git a/browser/components/customizableui/test/browser_892955_isWidgetRemovable_for_removed_widgets.js b/browser/components/customizableui/test/browser_892955_isWidgetRemovable_for_removed_widgets.js
new file mode 100644
index 000000000..67ef82b82
--- /dev/null
+++ b/browser/components/customizableui/test/browser_892955_isWidgetRemovable_for_removed_widgets.js
@@ -0,0 +1,30 @@
+/* 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 kWidgetId = "test-892955-remove-widget";
+
+// Removing a destroyed widget should work.
+add_task(function*() {
+  let widgetSpec = {
+    id: kWidgetId,
+    defaultArea: CustomizableUI.AREA_NAVBAR
+  };
+
+  CustomizableUI.createWidget(widgetSpec);
+  CustomizableUI.destroyWidget(kWidgetId);
+  let noError = true;
+  try {
+    CustomizableUI.removeWidgetFromArea(kWidgetId);
+  } catch (ex) {
+    noError = false;
+    Cu.reportError(ex);
+  }
+  ok(noError, "Shouldn't throw an error removing a destroyed widget.");
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_892956_destroyWidget_defaultPlacements.js b/browser/components/customizableui/test/browser_892956_destroyWidget_defaultPlacements.js
new file mode 100644
index 000000000..c7047c797
--- /dev/null
+++ b/browser/components/customizableui/test/browser_892956_destroyWidget_defaultPlacements.js
@@ -0,0 +1,24 @@
+/* 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 kWidgetId = "test-892956-destroyWidget-defaultPlacement";
+
+// destroyWidget should clean up defaultPlacements if the widget had a defaultArea
+add_task(function*() {
+  ok(CustomizableUI.inDefaultState, "Should be in the default state when we start");
+
+  let widgetSpec = {
+    id: kWidgetId,
+    defaultArea: CustomizableUI.AREA_NAVBAR
+  };
+  CustomizableUI.createWidget(widgetSpec);
+  CustomizableUI.destroyWidget(kWidgetId);
+  ok(CustomizableUI.inDefaultState, "Should be in the default state when we finish");
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_901207_searchbar_in_panel.js b/browser/components/customizableui/test/browser_901207_searchbar_in_panel.js
new file mode 100644
index 000000000..3bc449add
--- /dev/null
+++ b/browser/components/customizableui/test/browser_901207_searchbar_in_panel.js
@@ -0,0 +1,113 @@
+/* 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";
+
+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(() => chevron.open);
+
+  yield waitForSearchBarFocus();
+
+  let hiddenPanelPromise = promiseOverflowHidden(window);
+  EventUtils.synthesizeKey("VK_ESCAPE", {});
+  yield hiddenPanelPromise;
+  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();
+});
+
+
+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");
+}
diff --git a/browser/components/customizableui/test/browser_909779_overflow_toolbars_new_window.js b/browser/components/customizableui/test/browser_909779_overflow_toolbars_new_window.js
new file mode 100644
index 000000000..f39d13ff4
--- /dev/null
+++ b/browser/components/customizableui/test/browser_909779_overflow_toolbars_new_window.js
@@ -0,0 +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();
+});
diff --git a/browser/components/customizableui/test/browser_913972_currentset_overflow.js b/browser/components/customizableui/test/browser_913972_currentset_overflow.js
new file mode 100644
index 000000000..7d754d79b
--- /dev/null
+++ b/browser/components/customizableui/test/browser_913972_currentset_overflow.js
@@ -0,0 +1,55 @@
+/* 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";
+
+var navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+// Resize to a small window, resize back, shouldn't affect currentSet
+add_task(function*() {
+  let originalWindowWidth = window.outerWidth;
+  let oldCurrentSet = navbar.currentSet;
+  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.");
+  is(navbar.currentSet, oldCurrentSet, "Currentset should be the same when overflowing.");
+  ok(CustomizableUI.inDefaultState, "Should still be in default state when overflowing.");
+  ok(navbar.customizationTarget.childElementCount < oldChildCount, "Should have fewer children.");
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+  yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+  ok(!navbar.hasAttribute("overflowing"), "Should no longer have an overflowing toolbar.");
+  is(navbar.currentSet, oldCurrentSet, "Currentset should still be the same now we're no longer overflowing.");
+  ok(CustomizableUI.inDefaultState, "Should still be in default state now we're no longer overflowing.");
+
+  // Verify actual physical placements match those of the placement array:
+  let placementCounter = 0;
+  let placements = CustomizableUI.getWidgetIdsInArea(CustomizableUI.AREA_NAVBAR);
+  for (let node of navbar.customizationTarget.childNodes) {
+    if (node.getAttribute("skipintoolbarset") == "true") {
+      continue;
+    }
+    is(placements[placementCounter++], node.id, "Nodes should match after overflow");
+  }
+  is(placements.length, placementCounter, "Should have as many nodes as expected");
+  is(navbar.customizationTarget.childElementCount, oldChildCount, "Number of nodes should match");
+});
+
+// Enter and exit customization mode, check that currentSet works
+add_task(function*() {
+  let oldCurrentSet = navbar.currentSet;
+  ok(CustomizableUI.inDefaultState, "Should start in default state.");
+  yield startCustomizing();
+  ok(CustomizableUI.inDefaultState, "Should be in default state in customization mode.");
+  is(navbar.currentSet, oldCurrentSet, "Currentset should be the same in customization mode.");
+  yield endCustomizing();
+  ok(CustomizableUI.inDefaultState, "Should be in default state after customization mode.");
+  is(navbar.currentSet, oldCurrentSet, "Currentset should be the same after customization mode.");
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_914138_widget_API_overflowable_toolbar.js b/browser/components/customizableui/test/browser_914138_widget_API_overflowable_toolbar.js
new file mode 100644
index 000000000..35ba79bec
--- /dev/null
+++ b/browser/components/customizableui/test/browser_914138_widget_API_overflowable_toolbar.js
@@ -0,0 +1,131 @@
+/* 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";
+
+var navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+var overflowList = document.getElementById(navbar.getAttribute("overflowtarget"));
+
+const kTestBtn1 = "test-addWidgetToArea-overflow";
+const kTestBtn2 = "test-removeWidgetFromArea-overflow";
+const kTestBtn3 = "test-createWidget-overflow";
+const kHomeBtn = "home-button";
+const kDownloadsBtn = "downloads-button";
+const kSearchBox = "search-container";
+const kStarBtn = "bookmarks-menu-button";
+
+var originalWindowWidth;
+
+// Adding a widget should add it next to the widget it's being inserted next to.
+add_task(function*() {
+  originalWindowWidth = window.outerWidth;
+  createDummyXULButton(kTestBtn1, "Test");
+  ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
+  ok(CustomizableUI.inDefaultState, "Should start in default state.");
+
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+  ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+  ok(!navbar.querySelector("#" + kHomeBtn), "Home button should no longer be in the navbar");
+  let homeBtnNode = overflowList.querySelector("#" + kHomeBtn);
+  ok(homeBtnNode, "Home button should be overflowing");
+  ok(homeBtnNode && homeBtnNode.getAttribute("overflowedItem") == "true", "Home button should have overflowedItem attribute");
+
+  let placementOfHomeButton = CustomizableUI.getWidgetIdsInArea(navbar.id).indexOf(kHomeBtn);
+  CustomizableUI.addWidgetToArea(kTestBtn1, navbar.id, placementOfHomeButton);
+  ok(!navbar.querySelector("#" + kTestBtn1), "New button should not be in the navbar");
+  let newButtonNode = overflowList.querySelector("#" + kTestBtn1);
+  ok(newButtonNode, "New button should be overflowing");
+  ok(newButtonNode && newButtonNode.getAttribute("overflowedItem") == "true", "New button should have overflowedItem attribute");
+  let nextEl = newButtonNode && newButtonNode.nextSibling;
+  is(nextEl && nextEl.id, kHomeBtn, "Test button should be next to home button.");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+  yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+  ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
+  ok(navbar.querySelector("#" + kHomeBtn), "Home button should be in the navbar");
+  ok(homeBtnNode && (homeBtnNode.getAttribute("overflowedItem") != "true"), "Home button should no longer have overflowedItem attribute");
+  ok(!overflowList.querySelector("#" + kHomeBtn), "Home button should no longer be overflowing");
+  ok(navbar.querySelector("#" + kTestBtn1), "Test button should be in the navbar");
+  ok(!overflowList.querySelector("#" + kTestBtn1), "Test button should no longer be overflowing");
+  ok(newButtonNode && (newButtonNode.getAttribute("overflowedItem") != "true"), "New button should no longer have overflowedItem attribute");
+  let el = document.getElementById(kTestBtn1);
+  if (el) {
+    CustomizableUI.removeWidgetFromArea(kTestBtn1);
+    el.remove();
+  }
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+});
+
+// Removing a widget should remove it from the overflow list if that is where it is, and update it accordingly.
+add_task(function*() {
+  createDummyXULButton(kTestBtn2, "Test");
+  ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
+  ok(CustomizableUI.inDefaultState, "Should start in default state.");
+  CustomizableUI.addWidgetToArea(kTestBtn2, navbar.id);
+  ok(!navbar.hasAttribute("overflowing"), "Should still have a non-overflowing toolbar.");
+
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+  ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+  ok(!navbar.querySelector("#" + kTestBtn2), "Test button should not be in the navbar");
+  ok(overflowList.querySelector("#" + kTestBtn2), "Test button should be overflowing");
+
+  CustomizableUI.removeWidgetFromArea(kTestBtn2);
+
+  ok(!overflowList.querySelector("#" + kTestBtn2), "Test button should not be overflowing.");
+  ok(!navbar.querySelector("#" + kTestBtn2), "Test button should not be in the navbar");
+  ok(gNavToolbox.palette.querySelector("#" + kTestBtn2), "Test button should be in the palette");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+  yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+  ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
+  let el = document.getElementById(kTestBtn2);
+  if (el) {
+    CustomizableUI.removeWidgetFromArea(kTestBtn2);
+    el.remove();
+  }
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+});
+
+// Constructing a widget while overflown should set the right class on it.
+add_task(function*() {
+  originalWindowWidth = window.outerWidth;
+  ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
+  ok(CustomizableUI.inDefaultState, "Should start in default state.");
+
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+  ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+  ok(!navbar.querySelector("#" + kHomeBtn), "Home button should no longer be in the navbar");
+  let homeBtnNode = overflowList.querySelector("#" + kHomeBtn);
+  ok(homeBtnNode, "Home button should be overflowing");
+  ok(homeBtnNode && homeBtnNode.getAttribute("overflowedItem") == "true", "Home button should have overflowedItem class");
+
+  let testBtnSpec = {id: kTestBtn3, label: "Overflowable widget test", defaultArea: "nav-bar"};
+  CustomizableUI.createWidget(testBtnSpec);
+  let testNode = overflowList.querySelector("#" + kTestBtn3);
+  ok(testNode, "Test button should be overflowing");
+  ok(testNode && testNode.getAttribute("overflowedItem") == "true", "Test button should have overflowedItem class");
+
+  CustomizableUI.destroyWidget(kTestBtn3);
+  testNode = document.getElementById(kTestBtn3);
+  ok(!testNode, "Test button should be gone");
+
+  CustomizableUI.createWidget(testBtnSpec);
+  testNode = overflowList.querySelector("#" + kTestBtn3);
+  ok(testNode, "Test button should be overflowing");
+  ok(testNode && testNode.getAttribute("overflowedItem") == "true", "Test button should have overflowedItem class");
+
+  CustomizableUI.removeWidgetFromArea(kTestBtn3);
+  testNode = document.getElementById(kTestBtn3);
+  ok(!testNode, "Test button should be gone");
+  CustomizableUI.destroyWidget(kTestBtn3);
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+});
+
+add_task(function* asyncCleanup() {
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_914863_disabled_help_quit_buttons.js b/browser/components/customizableui/test/browser_914863_disabled_help_quit_buttons.js
new file mode 100644
index 000000000..b5757eabb
--- /dev/null
+++ b/browser/components/customizableui/test/browser_914863_disabled_help_quit_buttons.js
@@ -0,0 +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.");
+});
diff --git a/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js b/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js
new file mode 100644
index 000000000..dffe388dc
--- /dev/null
+++ b/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js
@@ -0,0 +1,38 @@
+/* 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";
+
+var navbar;
+var skippedItem;
+
+// Attempting to drag a skipintoolbarset item should work.
+add_task(function*() {
+  navbar = document.getElementById("nav-bar");
+  skippedItem = document.createElement("toolbarbutton");
+  skippedItem.id = "test-skipintoolbarset-item";
+  skippedItem.setAttribute("label", "Test");
+  skippedItem.setAttribute("skipintoolbarset", "true");
+  skippedItem.setAttribute("removable", "true");
+  navbar.customizationTarget.appendChild(skippedItem);
+  let downloadsButton = document.getElementById("downloads-button");
+  yield startCustomizing();
+  ok(CustomizableUI.inDefaultState, "Should still be in default state");
+  simulateItemDrag(skippedItem, downloadsButton);
+  ok(CustomizableUI.inDefaultState, "Should still be in default state");
+  let skippedItemWrapper = skippedItem.parentNode;
+  is(skippedItemWrapper.nextSibling && skippedItemWrapper.nextSibling.id,
+     downloadsButton.parentNode.id, "Should be next to downloads button");
+  simulateItemDrag(downloadsButton, skippedItem);
+  let downloadWrapper = downloadsButton.parentNode;
+  is(downloadWrapper.nextSibling && downloadWrapper.nextSibling.id,
+     skippedItem.parentNode.id, "Should be next to skipintoolbarset item");
+  ok(CustomizableUI.inDefaultState, "Should still be in default state");
+});
+
+add_task(function* asyncCleanup() {
+  yield endCustomizing();
+  skippedItem.remove();
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_923857_customize_mode_event_wrapping_during_reset.js b/browser/components/customizableui/test/browser_923857_customize_mode_event_wrapping_during_reset.js
new file mode 100644
index 000000000..87aca51eb
--- /dev/null
+++ b/browser/components/customizableui/test/browser_923857_customize_mode_event_wrapping_during_reset.js
@@ -0,0 +1,24 @@
+/* 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";
+
+// Customize mode reset button should revert correctly
+add_task(function*() {
+  yield startCustomizing();
+  let devButton = document.getElementById("developer-button");
+  let downloadsButton = document.getElementById("downloads-button");
+  let searchBox = document.getElementById("search-container");
+  let palette = document.getElementById("customization-palette");
+  ok(devButton && downloadsButton && searchBox && palette, "Stuff should exist");
+  simulateItemDrag(devButton, downloadsButton);
+  simulateItemDrag(searchBox, palette);
+  yield gCustomizeMode.reset();
+  ok(CustomizableUI.inDefaultState, "Should be back in default state");
+  yield endCustomizing();
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_927717_customize_drag_empty_toolbar.js b/browser/components/customizableui/test/browser_927717_customize_drag_empty_toolbar.js
new file mode 100644
index 000000000..d79f6e364
--- /dev/null
+++ b/browser/components/customizableui/test/browser_927717_customize_drag_empty_toolbar.js
@@ -0,0 +1,26 @@
+/* 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 kTestToolbarId = "test-empty-drag";
+
+// Attempting to drag an item to an empty container should work.
+add_task(function*() {
+  yield createToolbarWithPlacements(kTestToolbarId, []);
+  yield startCustomizing();
+  let downloadButton = document.getElementById("downloads-button");
+  let customToolbar = document.getElementById(kTestToolbarId);
+  simulateItemDrag(downloadButton, customToolbar);
+  assertAreaPlacements(kTestToolbarId, ["downloads-button"]);
+  ok(downloadButton.parentNode && downloadButton.parentNode.parentNode == customToolbar,
+     "Button should really be in toolbar");
+  yield endCustomizing();
+  removeCustomToolbars();
+});
+
+add_task(function* asyncCleanup() {
+  yield endCustomizing();
+  yield resetCustomization();
+});
diff --git 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
new file mode 100644
index 000000000..3cbf6be42
--- /dev/null
+++ b/browser/components/customizableui/test/browser_932928_show_notice_when_palette_empty.js
@@ -0,0 +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();
+});
diff --git a/browser/components/customizableui/test/browser_934113_menubar_removable.js b/browser/components/customizableui/test/browser_934113_menubar_removable.js
new file mode 100644
index 000000000..1d788bced
--- /dev/null
+++ b/browser/components/customizableui/test/browser_934113_menubar_removable.js
@@ -0,0 +1,30 @@
+/* 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";
+
+// Attempting to drag the menubar to the navbar shouldn't work.
+add_task(function*() {
+  yield startCustomizing();
+  let menuItems = document.getElementById("menubar-items");
+  let navbar = document.getElementById("nav-bar");
+  let menubar = document.getElementById("toolbar-menubar");
+  // Force the menu to be shown.
+  const kAutohide = menubar.getAttribute("autohide");
+  menubar.setAttribute("autohide", "false");
+  simulateItemDrag(menuItems, navbar.customizationTarget);
+
+  is(getAreaWidgetIds("nav-bar").indexOf("menubar-items"), -1, "Menu bar shouldn't be in the navbar.");
+  ok(!navbar.querySelector("#menubar-items"), "Shouldn't find menubar items in the navbar.");
+  ok(menubar.querySelector("#menubar-items"), "Should find menubar items in the menubar.");
+  isnot(getAreaWidgetIds("toolbar-menubar").indexOf("menubar-items"), -1,
+        "Menubar items shouldn't be missing from the navbar.");
+  menubar.setAttribute("autohide", kAutohide);
+  yield endCustomizing();
+});
+
+add_task(function* asyncCleanup() {
+  yield endCustomizing();
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_934951_zoom_in_toolbar.js b/browser/components/customizableui/test/browser_934951_zoom_in_toolbar.js
new file mode 100644
index 000000000..dcc183051
--- /dev/null
+++ b/browser/components/customizableui/test/browser_934951_zoom_in_toolbar.js
@@ -0,0 +1,89 @@
+/* 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");
+  yield BrowserTestUtils.browserLoaded(tab1.linkedBrowser);
+  let tab2 = gBrowser.addTab("about:robots");
+  yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+  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:robots");
+
+  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");
+  let attributeChangePromise = promiseAttributeMutation(zoomResetButton, "label", (v) => {
+    return parseInt(v, 10) == 100;
+  });
+  yield promiseTabLoadEvent(tab1, "about:home");
+  yield attributeChangePromise;
+  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");
+  FullZoom.reset();
+});
+
+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;
+}
diff --git a/browser/components/customizableui/test/browser_938980_navbar_collapsed.js b/browser/components/customizableui/test/browser_938980_navbar_collapsed.js
new file mode 100644
index 000000000..fc7fa1a0a
--- /dev/null
+++ b/browser/components/customizableui/test/browser_938980_navbar_collapsed.js
@@ -0,0 +1,121 @@
+/* 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);
+
+var bookmarksToolbar = document.getElementById("PersonalToolbar");
+var navbar = document.getElementById("nav-bar");
+var 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");
+  ok(bookmarksToolbar.collapsed, "bookmarksToolbar should be collapsed");
+  ok(!tabsToolbar.collapsed, "TabsToolbar should not be collapsed");
+  is(navbar.collapsed, false, "The nav-bar should be shown by default");
+
+  setToolbarVisibility(bookmarksToolbar, true);
+  setToolbarVisibility(navbar, false);
+  ok(!bookmarksToolbar.collapsed, "bookmarksToolbar should be visible now");
+  ok(navbar.collapsed, "navbar should be collapsed");
+  is(CustomizableUI.inDefaultState, false, "Should no longer be in default state");
+
+  yield startCustomizing();
+  yield gCustomizeMode.reset();
+  yield endCustomizing();
+
+  is(bookmarksToolbar.collapsed, true, "Customization reset should restore collapsed-state to the bookmarks toolbar");
+  ok(!tabsToolbar.collapsed, "TabsToolbar should not be collapsed");
+  ok(bookmarksToolbar.collapsed, "The bookmarksToolbar should be collapsed 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();
+  yield gCustomizeMode.reset();
+
+  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");
+  ok(bookmarksToolbar.collapsed, "bookmarksToolbar should be collapsed");
+  ok(!tabsToolbar.collapsed, "TabsToolbar should not be collapsed");
+
+  setToolbarVisibility(bookmarksToolbar, true);
+  ok(!bookmarksToolbar.collapsed, "bookmarksToolbar should be visible now");
+  is(CustomizableUI.inDefaultState, false, "Should no longer be in default state");
+
+  yield startCustomizing();
+
+  ok(!bookmarksToolbar.collapsed, "The bookmarksToolbar should be visible before reset");
+  ok(!navbar.collapsed, "The navbar should be visible before reset");
+  ok(!tabsToolbar.collapsed, "TabsToolbar should not be collapsed");
+
+  yield gCustomizeMode.reset();
+
+  ok(bookmarksToolbar.collapsed, "The bookmarksToolbar should be collapsed after reset");
+  ok(!tabsToolbar.collapsed, "TabsToolbar should not be collapsed");
+  ok(!navbar.collapsed, "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();
+});
diff --git a/browser/components/customizableui/test/browser_938995_indefaultstate_nonremovable.js b/browser/components/customizableui/test/browser_938995_indefaultstate_nonremovable.js
new file mode 100644
index 000000000..1f06c1aac
--- /dev/null
+++ b/browser/components/customizableui/test/browser_938995_indefaultstate_nonremovable.js
@@ -0,0 +1,25 @@
+/* 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 kWidgetId = "test-non-removable-widget";
+
+// Adding non-removable items to a toolbar or the panel shouldn't change inDefaultState
+add_task(function() {
+  ok(CustomizableUI.inDefaultState, "Should start in default state");
+
+  let button = createDummyXULButton(kWidgetId, "Test non-removable inDefaultState handling");
+  CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_NAVBAR);
+  button.setAttribute("removable", "false");
+  ok(CustomizableUI.inDefaultState, "Should still be in default state after navbar addition");
+  button.remove();
+
+  button = createDummyXULButton(kWidgetId, "Test non-removable inDefaultState handling");
+  CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_PANEL);
+  button.setAttribute("removable", "false");
+  ok(CustomizableUI.inDefaultState, "Should still be in default state after panel addition");
+  button.remove();
+  ok(CustomizableUI.inDefaultState, "Should be in default state after destroying both widgets");
+});
diff --git a/browser/components/customizableui/test/browser_940013_registerToolbarNode_calls_registerArea.js b/browser/components/customizableui/test/browser_940013_registerToolbarNode_calls_registerArea.js
new file mode 100644
index 000000000..c554bffab
--- /dev/null
+++ b/browser/components/customizableui/test/browser_940013_registerToolbarNode_calls_registerArea.js
@@ -0,0 +1,70 @@
+/* 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 kToolbarId = "test-registerToolbarNode-toolbar";
+const kButtonId = "test-registerToolbarNode-button";
+registerCleanupFunction(cleanup);
+
+// Registering a toolbar with defaultset attribute should work
+add_task(function*() {
+  ok(CustomizableUI.inDefaultState, "Everything should be in its default state.");
+  let btn = createDummyXULButton(kButtonId);
+  let toolbar = document.createElement("toolbar");
+  toolbar.id = kToolbarId;
+  toolbar.setAttribute("customizable", true);
+  toolbar.setAttribute("defaultset", kButtonId);
+  gNavToolbox.appendChild(toolbar);
+  ok(CustomizableUI.areas.indexOf(kToolbarId) != -1,
+     "Toolbar should have been registered automatically.");
+  is(CustomizableUI.getAreaType(kToolbarId), CustomizableUI.TYPE_TOOLBAR,
+     "Area should be registered as toolbar");
+  assertAreaPlacements(kToolbarId, [kButtonId]);
+  ok(!CustomizableUI.inDefaultState, "No longer in default state after toolbar is registered and visible.");
+  CustomizableUI.unregisterArea(kToolbarId, true);
+  toolbar.remove();
+  ok(CustomizableUI.inDefaultState, "Everything should be in its default state.");
+  btn.remove();
+});
+
+// Registering a toolbar without a defaultset attribute should
+// wait for the registerArea call
+add_task(function*() {
+  ok(CustomizableUI.inDefaultState, "Everything should be in its default state.");
+  let btn = createDummyXULButton(kButtonId);
+  let toolbar = document.createElement("toolbar");
+  toolbar.id = kToolbarId;
+  toolbar.setAttribute("customizable", true);
+  gNavToolbox.appendChild(toolbar);
+  ok(CustomizableUI.areas.indexOf(kToolbarId) == -1,
+     "Toolbar should not yet have been registered automatically.");
+  CustomizableUI.registerArea(kToolbarId, {defaultPlacements: [kButtonId]});
+  ok(CustomizableUI.areas.indexOf(kToolbarId) != -1,
+     "Toolbar should have been registered now.");
+  is(CustomizableUI.getAreaType(kToolbarId), CustomizableUI.TYPE_TOOLBAR,
+     "Area should be registered as toolbar");
+  assertAreaPlacements(kToolbarId, [kButtonId]);
+  ok(!CustomizableUI.inDefaultState, "No longer in default state after toolbar is registered and visible.");
+  CustomizableUI.unregisterArea(kToolbarId, true);
+  toolbar.remove();
+  ok(CustomizableUI.inDefaultState, "Everything should be in its default state.");
+  btn.remove();
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
+
+function cleanup() {
+  let toolbar = document.getElementById(kToolbarId);
+  if (toolbar) {
+    toolbar.remove();
+  }
+  let btn = document.getElementById(kButtonId) ||
+            gNavToolbox.querySelector("#" + kButtonId);
+  if (btn) {
+    btn.remove();
+  }
+}
diff --git a/browser/components/customizableui/test/browser_940307_panel_click_closure_handling.js b/browser/components/customizableui/test/browser_940307_panel_click_closure_handling.js
new file mode 100644
index 000000000..944879a1b
--- /dev/null
+++ b/browser/components/customizableui/test/browser_940307_panel_click_closure_handling.js
@@ -0,0 +1,136 @@
+/* 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";
+
+var button, menuButton;
+/* Clicking a button should close the panel */
+add_task(function*() {
+  button = document.createElement("toolbarbutton");
+  button.id = "browser_940307_button";
+  button.setAttribute("label", "Button");
+  PanelUI.contents.appendChild(button);
+  yield PanelUI.show();
+  let hiddenAgain = promisePanelHidden(window);
+  EventUtils.synthesizeMouseAtCenter(button, {});
+  yield hiddenAgain;
+  button.remove();
+});
+
+/* Clicking a menu button should close the panel, opening the popup shouldn't.  */
+add_task(function*() {
+  menuButton = document.createElement("toolbarbutton");
+  menuButton.setAttribute("type", "menu-button");
+  menuButton.id = "browser_940307_menubutton";
+  menuButton.setAttribute("label", "Menu button");
+
+  let menuPopup = document.createElement("menupopup");
+  menuPopup.id = "browser_940307_menupopup";
+
+  let menuItem = document.createElement("menuitem");
+  menuItem.setAttribute("label", "Menu item");
+  menuItem.id = "browser_940307_menuitem";
+
+  menuPopup.appendChild(menuItem);
+  menuButton.appendChild(menuPopup);
+  PanelUI.contents.appendChild(menuButton);
+
+  yield PanelUI.show();
+  let hiddenAgain = promisePanelHidden(window);
+  let innerButton = document.getAnonymousElementByAttribute(menuButton, "anonid", "button");
+  EventUtils.synthesizeMouseAtCenter(innerButton, {});
+  yield hiddenAgain;
+
+  // Now click the dropmarker to show the menu
+  yield PanelUI.show();
+  hiddenAgain = promisePanelHidden(window);
+  let menuShown = promisePanelElementShown(window, menuPopup);
+  let dropmarker = document.getAnonymousElementByAttribute(menuButton, "type", "menu-button");
+  EventUtils.synthesizeMouseAtCenter(dropmarker, {});
+  yield menuShown;
+  // Panel should stay open:
+  ok(isPanelUIOpen(), "Panel should still be open");
+  let menuHidden = promisePanelElementHidden(window, menuPopup);
+  // Then click the menu item to close all the things
+  EventUtils.synthesizeMouseAtCenter(menuItem, {});
+  yield menuHidden;
+  yield hiddenAgain;
+  menuButton.remove();
+});
+
+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");
+  yield PanelUI.show();
+  yield waitForCondition(() => "value" in searchbar && searchbar.value === "");
+
+  // Focusing a non-empty searchbox will cause us to open the
+  // autocomplete panel and search for suggestions, which would
+  // trigger network requests. Temporarily disable suggestions.
+  yield SpecialPowers.pushPrefEnv({set: [["browser.search.suggest.enabled", false]]});
+
+  searchbar.value = "foo";
+  searchbar.focus();
+  // Reaching into this context menu is pretty evil, but hey... it's a test.
+  let textbox = document.getAnonymousElementByAttribute(searchbar.textbox, "anonid", "textbox-input-box");
+  let contextmenu = document.getAnonymousElementByAttribute(textbox, "anonid", "input-box-contextmenu");
+  let contextMenuShown = promisePanelElementShown(window, contextmenu);
+  EventUtils.synthesizeMouseAtCenter(searchbar, {type: "contextmenu", button: 2});
+  yield contextMenuShown;
+
+  ok(isPanelUIOpen(), "Panel should still be open");
+
+  let selectAll = contextmenu.querySelector("[cmd='cmd_selectAll']");
+  let contextMenuHidden = promisePanelElementHidden(window, contextmenu);
+  EventUtils.synthesizeMouseAtCenter(selectAll, {});
+  yield contextMenuHidden;
+
+  // Hide the suggestion panel.
+  searchbar.textbox.popup.hidePopup();
+
+  ok(isPanelUIOpen(), "Panel should still be open");
+
+  let hiddenPanelPromise = promisePanelHidden(window);
+  EventUtils.synthesizeKey("VK_ESCAPE", {});
+  yield hiddenPanelPromise;
+  ok(!isPanelUIOpen(), "Panel should no longer be open");
+
+  // We focused the search bar earlier - ensure we don't keep doing that.
+  gURLBar.select();
+
+  CustomizableUI.reset();
+});
+
+add_task(function*() {
+  button = document.createElement("toolbarbutton");
+  button.id = "browser_946166_button_disabled";
+  button.setAttribute("disabled", "true");
+  button.setAttribute("label", "Button");
+  PanelUI.contents.appendChild(button);
+  yield PanelUI.show();
+  EventUtils.synthesizeMouseAtCenter(button, {});
+  is(PanelUI.panel.state, "open", "Popup stays open");
+  button.removeAttribute("disabled");
+  let hiddenAgain = promisePanelHidden(window);
+  EventUtils.synthesizeMouseAtCenter(button, {});
+  yield hiddenAgain;
+  button.remove();
+});
+
+registerCleanupFunction(function() {
+  if (button && button.parentNode) {
+    button.remove();
+  }
+  if (menuButton && menuButton.parentNode) {
+    menuButton.remove();
+  }
+  // Sadly this isn't task.jsm-enabled, so we can't wait for this to happen. But we should
+  // definitely close it here and hope it won't interfere with other tests.
+  // Of course, all the tests are meant to do this themselves, but if they fail...
+  if (isPanelUIOpen()) {
+    PanelUI.hide();
+  }
+});
diff --git a/browser/components/customizableui/test/browser_940946_removable_from_navbar_customizemode.js b/browser/components/customizableui/test/browser_940946_removable_from_navbar_customizemode.js
new file mode 100644
index 000000000..c81b004c1
--- /dev/null
+++ b/browser/components/customizableui/test/browser_940946_removable_from_navbar_customizemode.js
@@ -0,0 +1,22 @@
+/* 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 kTestBtnId = "test-removable-navbar-customize-mode";
+
+// Items without the removable attribute in the navbar should be considered non-removable
+add_task(function*() {
+  let btn = createDummyXULButton(kTestBtnId, "Test removable in navbar in customize mode");
+  document.getElementById("nav-bar").customizationTarget.appendChild(btn);
+  yield startCustomizing();
+  ok(!CustomizableUI.isWidgetRemovable(kTestBtnId), "Widget should not be considered removable");
+  yield endCustomizing();
+  document.getElementById(kTestBtnId).remove();
+});
+
+add_task(function* asyncCleanup() {
+  yield endCustomizing();
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_941083_invalidate_wrapper_cache_createWidget.js b/browser/components/customizableui/test/browser_941083_invalidate_wrapper_cache_createWidget.js
new file mode 100644
index 000000000..1d7f86fd2
--- /dev/null
+++ b/browser/components/customizableui/test/browser_941083_invalidate_wrapper_cache_createWidget.js
@@ -0,0 +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";
+
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=941083
+
+const kWidgetId = "test-invalidate-wrapper-cache";
+
+// Check createWidget invalidates the widget cache
+add_task(function() {
+  let groupWrapper = CustomizableUI.getWidget(kWidgetId);
+  ok(groupWrapper, "Should get group wrapper.");
+  let singleWrapper = groupWrapper.forWindow(window);
+  ok(singleWrapper, "Should get single wrapper.");
+
+  CustomizableUI.createWidget({id: kWidgetId, label: "Test invalidating widgets caching"});
+
+  let newGroupWrapper = CustomizableUI.getWidget(kWidgetId);
+  ok(newGroupWrapper, "Should get a group wrapper again.");
+  isnot(newGroupWrapper, groupWrapper, "Wrappers shouldn't be the same.");
+  isnot(newGroupWrapper.provider, groupWrapper.provider, "Wrapper providers shouldn't be the same.");
+
+  let newSingleWrapper = newGroupWrapper.forWindow(window);
+  isnot(newSingleWrapper, singleWrapper, "Single wrappers shouldn't be the same.");
+  isnot(newSingleWrapper.provider, singleWrapper.provider, "Single wrapper providers shouldn't be the same.");
+
+  CustomizableUI.destroyWidget(kWidgetId);
+  ok(!CustomizableUI.getWidget(kWidgetId), "Shouldn't get a wrapper after destroying the widget.");
+});
diff --git a/browser/components/customizableui/test/browser_942581_unregisterArea_keeps_placements.js b/browser/components/customizableui/test/browser_942581_unregisterArea_keeps_placements.js
new file mode 100644
index 000000000..61adac982
--- /dev/null
+++ b/browser/components/customizableui/test/browser_942581_unregisterArea_keeps_placements.js
@@ -0,0 +1,106 @@
+/* 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-unregisterArea-placements-toolbar";
+const kTestWidgetPfx = "test-widget-for-unregisterArea-placements-";
+const kTestWidgetCount = 3;
+registerCleanupFunction(removeCustomToolbars);
+
+// unregisterArea should keep placements by default and restore them when re-adding the area
+add_task(function*() {
+  let widgetIds = [];
+  for (let i = 0; i < kTestWidgetCount; i++) {
+    let id = kTestWidgetPfx + i;
+    widgetIds.push(id);
+    let spec = {id: id, type: 'button', removable: true, label: "unregisterArea test", tooltiptext: "" + i};
+    CustomizableUI.createWidget(spec);
+  }
+  for (let i = kTestWidgetCount; i < kTestWidgetCount * 2; i++) {
+    let id = kTestWidgetPfx + i;
+    widgetIds.push(id);
+    createDummyXULButton(id, "unregisterArea XUL test " + i);
+  }
+  let toolbarNode = createToolbarWithPlacements(kToolbarName, widgetIds);
+  checkAbstractAndRealPlacements(toolbarNode, widgetIds);
+
+  // Now move one of them:
+  CustomizableUI.moveWidgetWithinArea(kTestWidgetPfx + kTestWidgetCount, 0);
+  // Clone the array so we know this is the modified one:
+  let modifiedWidgetIds = [...widgetIds];
+  let movedWidget = modifiedWidgetIds.splice(kTestWidgetCount, 1)[0];
+  modifiedWidgetIds.unshift(movedWidget);
+
+  // Check it:
+  checkAbstractAndRealPlacements(toolbarNode, modifiedWidgetIds);
+
+  // Then unregister
+  CustomizableUI.unregisterArea(kToolbarName);
+
+  // Check we tell the outside world no dangerous things:
+  checkWidgetFates(widgetIds);
+  // Only then remove the real node
+  toolbarNode.remove();
+
+  // Now move one of the items to the palette, and another to the navbar:
+  let lastWidget = modifiedWidgetIds.pop();
+  CustomizableUI.removeWidgetFromArea(lastWidget);
+  lastWidget = modifiedWidgetIds.pop();
+  CustomizableUI.addWidgetToArea(lastWidget, CustomizableUI.AREA_NAVBAR);
+
+  // Recreate ourselves with the default placements being the same:
+  toolbarNode = createToolbarWithPlacements(kToolbarName, widgetIds);
+  // Then check that after doing this, our actual placements match
+  // the modified list, not the default one.
+  checkAbstractAndRealPlacements(toolbarNode, modifiedWidgetIds);
+
+  // Now remove completely:
+  CustomizableUI.unregisterArea(kToolbarName, true);
+  checkWidgetFates(modifiedWidgetIds);
+  toolbarNode.remove();
+
+  // One more time:
+  // Recreate ourselves with the default placements being the same:
+  toolbarNode = createToolbarWithPlacements(kToolbarName, widgetIds);
+  // Should now be back to default:
+  checkAbstractAndRealPlacements(toolbarNode, widgetIds);
+  CustomizableUI.unregisterArea(kToolbarName, true);
+  checkWidgetFates(widgetIds);
+  toolbarNode.remove();
+
+  // XXXgijs: ensure cleanup function doesn't barf:
+  gAddedToolbars.delete(kToolbarName);
+
+  // Remove all the XUL widgets, destroy the others:
+  for (let widget of widgetIds) {
+    let widgetWrapper = CustomizableUI.getWidget(widget);
+    if (widgetWrapper.provider == CustomizableUI.PROVIDER_XUL) {
+      gNavToolbox.palette.querySelector("#" + widget).remove();
+    } else {
+      CustomizableUI.destroyWidget(widget);
+    }
+  }
+});
+
+function checkAbstractAndRealPlacements(aNode, aExpectedPlacements) {
+  assertAreaPlacements(kToolbarName, aExpectedPlacements);
+  let physicalWidgetIds = Array.from(aNode.childNodes, (node) => node.id);
+  placementArraysEqual(aNode.id, physicalWidgetIds, aExpectedPlacements);
+}
+
+function checkWidgetFates(aWidgetIds) {
+  for (let widget of aWidgetIds) {
+    ok(!CustomizableUI.getPlacementOfWidget(widget), "Widget should be in palette");
+    ok(!document.getElementById(widget), "Widget should not be in the DOM");
+    let widgetInPalette = !!gNavToolbox.palette.querySelector("#" + widget);
+    let widgetProvider = CustomizableUI.getWidget(widget).provider;
+    let widgetIsXULWidget = widgetProvider == CustomizableUI.PROVIDER_XUL;
+    is(widgetInPalette, widgetIsXULWidget, "Just XUL Widgets should be in the palette");
+  }
+}
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_943683_migration_test.js b/browser/components/customizableui/test/browser_943683_migration_test.js
new file mode 100644
index 000000000..fe30df9e3
--- /dev/null
+++ b/browser/components/customizableui/test/browser_943683_migration_test.js
@@ -0,0 +1,50 @@
+/* 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 kWidgetId = "test-addonbar-migration";
+const kWidgetId2 = "test-addonbar-migration2";
+
+var addonbar = document.getElementById(CustomizableUI.AREA_ADDONBAR);
+var navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+var btn;
+var btn2;
+
+// Check we migrate normal stuff to the navbar
+add_task(function*() {
+  btn = createDummyXULButton(kWidgetId, "Test");
+  btn2 = createDummyXULButton(kWidgetId2, "Test2");
+  addonbar.insertItem(btn.id);
+  ok(btn.parentNode == navbar.customizationTarget, "Button should end up in navbar");
+  let migrationArray = addonbar.getMigratedItems();
+  is(migrationArray.length, 1, "Should have migrated 1 item");
+  is(migrationArray[0], kWidgetId, "Should have migrated our 1 item");
+
+  addonbar.currentSet = addonbar.currentSet + "," + kWidgetId2;
+  ok(btn2.parentNode == navbar.customizationTarget, "Second button should end up in the navbar");
+  migrationArray = addonbar.getMigratedItems();
+  is(migrationArray.length, 2, "Should have migrated 2 items");
+  isnot(migrationArray.indexOf(kWidgetId2), -1, "Should have migrated our second item");
+
+  let otherWindow = yield openAndLoadWindow(undefined, true);
+  try {
+    let addonBar = otherWindow.document.getElementById("addon-bar");
+    let otherMigrationArray = addonBar.getMigratedItems();
+    is(migrationArray.length, otherMigrationArray.length,
+       "Other window should have the same number of migrated items.");
+    if (migrationArray.length == otherMigrationArray.length) {
+      for (let widget of migrationArray) {
+        isnot(otherMigrationArray.indexOf(widget), -1,
+              "Migrated widget " + widget + " should also be listed as migrated in the other window.");
+      }
+    }
+  } finally {
+    yield promiseWindowClosed(otherWindow);
+  }
+  btn.remove();
+  btn2.remove();
+  CustomizableUI.reset();
+});
diff --git a/browser/components/customizableui/test/browser_944887_destroyWidget_should_destroy_in_palette.js b/browser/components/customizableui/test/browser_944887_destroyWidget_should_destroy_in_palette.js
new file mode 100644
index 000000000..a724b0c7f
--- /dev/null
+++ b/browser/components/customizableui/test/browser_944887_destroyWidget_should_destroy_in_palette.js
@@ -0,0 +1,17 @@
+/* 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 kWidgetId = "test-destroy-in-palette";
+
+// Check destroyWidget destroys the node if it's in the palette
+add_task(function*() {
+  CustomizableUI.createWidget({id: kWidgetId, label: "Test destroying widgets in palette."});
+  yield startCustomizing();
+  yield endCustomizing();
+  ok(gNavToolbox.palette.querySelector("#" + kWidgetId), "Widget still exists in palette.");
+  CustomizableUI.destroyWidget(kWidgetId);
+  ok(!gNavToolbox.palette.querySelector("#" + kWidgetId), "Widget no longer exists in palette.");
+});
diff --git a/browser/components/customizableui/test/browser_945739_showInPrivateBrowsing_customize_mode.js b/browser/components/customizableui/test/browser_945739_showInPrivateBrowsing_customize_mode.js
new file mode 100644
index 000000000..6b8acbee0
--- /dev/null
+++ b/browser/components/customizableui/test/browser_945739_showInPrivateBrowsing_customize_mode.js
@@ -0,0 +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";
+
+const kWidgetId = "test-private-browsing-customize-mode-widget";
+
+// Add a widget via the API with showInPrivateBrowsing set to false
+// and ensure it does not appear in the list of unused widgets in private
+// windows.
+add_task(function* testPrivateBrowsingCustomizeModeWidget() {
+  CustomizableUI.createWidget({
+    id: kWidgetId,
+    showInPrivateBrowsing: false
+  });
+
+  let normalWidgetArray = CustomizableUI.getUnusedWidgets(gNavToolbox.palette);
+  normalWidgetArray = normalWidgetArray.map((w) => w.id);
+  ok(normalWidgetArray.indexOf(kWidgetId) > -1,
+     "Widget should appear as unused in non-private window");
+
+  let privateWindow = yield openAndLoadWindow({private: true});
+  let privateWidgetArray = CustomizableUI.getUnusedWidgets(privateWindow.gNavToolbox.palette);
+  privateWidgetArray = privateWidgetArray.map((w) => w.id);
+  is(privateWidgetArray.indexOf(kWidgetId), -1,
+     "Widget should not appear as unused in private window");
+  yield promiseWindowClosed(privateWindow);
+
+  CustomizableUI.destroyWidget(kWidgetId);
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_947914_button_addons.js b/browser/components/customizableui/test/browser_947914_button_addons.js
new file mode 100644
index 000000000..b942ee771
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_addons.js
@@ -0,0 +1,33 @@
+/* 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";
+
+var initialLocation = gBrowser.currentURI.spec;
+var newTab = null;
+
+add_task(function*() {
+  info("Check addons button existence and functionality");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let addonsButton = document.getElementById("add-ons-button");
+  ok(addonsButton, "Add-ons button exists in Panel Menu");
+  addonsButton.click();
+
+  newTab = gBrowser.selectedTab;
+  yield waitForCondition(() => gBrowser.currentURI &&
+                               gBrowser.currentURI.spec == "about:addons");
+
+  let addonsPage = gBrowser.selectedBrowser.contentWindow.document.
+                            getElementById("addons-page");
+  ok(addonsPage, "Add-ons page was opened");
+});
+
+add_task(function* asyncCleanup() {
+  gBrowser.addTab(initialLocation);
+  gBrowser.removeTab(gBrowser.selectedTab);
+  info("Tabs were restored");
+});
diff --git a/browser/components/customizableui/test/browser_947914_button_copy.js b/browser/components/customizableui/test/browser_947914_button_copy.js
new file mode 100644
index 000000000..c778c956f
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_copy.js
@@ -0,0 +1,59 @@
+/* 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";
+
+var initialLocation = gBrowser.currentURI.spec;
+var globalClipboard;
+
+add_task(function*() {
+  yield BrowserTestUtils.withNewTab({gBrowser, url: "about:blank"}, function*() {
+    info("Check copy button existence and functionality");
+
+    let testText = "copy text test";
+
+    gURLBar.focus();
+    info("The URL bar was focused");
+    yield PanelUI.show();
+    info("Menu panel was opened");
+
+    let copyButton = document.getElementById("copy-button");
+    ok(copyButton, "Copy button exists in Panel Menu");
+    ok(copyButton.getAttribute("disabled"), "Copy button is initially disabled");
+
+    // copy text from URL bar
+    gURLBar.value = testText;
+    gURLBar.focus();
+    gURLBar.select();
+    yield PanelUI.show();
+    info("Menu panel was opened");
+
+    ok(!copyButton.hasAttribute("disabled"), "Copy button is enabled when selecting");
+
+    copyButton.click();
+    is(gURLBar.value, testText, "Selected text is unaltered when clicking copy");
+
+    // check that the text was added to the clipboard
+    let clipboard = Services.clipboard;
+    let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
+    globalClipboard = clipboard.kGlobalClipboard;
+
+    transferable.init(null);
+    transferable.addDataFlavor("text/unicode");
+    clipboard.getData(transferable, globalClipboard);
+    let str = {}, strLength = {};
+    transferable.getTransferData("text/unicode", str, strLength);
+    let clipboardValue = "";
+
+    if (str.value) {
+      str.value.QueryInterface(Ci.nsISupportsString);
+      clipboardValue = str.value.data;
+    }
+    is(clipboardValue, testText, "Data was copied to the clipboard.");
+  });
+});
+
+registerCleanupFunction(function cleanup() {
+  Services.clipboard.emptyClipboard(globalClipboard);
+});
diff --git a/browser/components/customizableui/test/browser_947914_button_cut.js b/browser/components/customizableui/test/browser_947914_button_cut.js
new file mode 100644
index 000000000..e6e614368
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_cut.js
@@ -0,0 +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";
+
+var initialLocation = gBrowser.currentURI.spec;
+var globalClipboard;
+
+add_task(function*() {
+  yield BrowserTestUtils.withNewTab({gBrowser, url: "about:blank"}, function*() {
+    info("Check cut button existence and functionality");
+
+    let testText = "cut text test";
+
+    gURLBar.focus();
+    yield PanelUI.show();
+    info("Menu panel was opened");
+
+    let cutButton = document.getElementById("cut-button");
+    ok(cutButton, "Cut button exists in Panel Menu");
+    ok(cutButton.hasAttribute("disabled"), "Cut button is disabled");
+
+    // cut text from URL bar
+    gURLBar.value = testText;
+    gURLBar.focus();
+    gURLBar.select();
+    yield PanelUI.show();
+    info("Menu panel was opened");
+
+    ok(!cutButton.hasAttribute("disabled"), "Cut button is enabled when selecting");
+    cutButton.click();
+    is(gURLBar.value, "", "Selected text is removed from source when clicking on cut");
+
+    // check that the text was added to the clipboard
+    let clipboard = Services.clipboard;
+    let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
+    globalClipboard = clipboard.kGlobalClipboard;
+
+    transferable.init(null);
+    transferable.addDataFlavor("text/unicode");
+    clipboard.getData(transferable, globalClipboard);
+    let str = {}, strLength = {};
+    transferable.getTransferData("text/unicode", str, strLength);
+    let clipboardValue = "";
+
+    if (str.value) {
+      str.value.QueryInterface(Ci.nsISupportsString);
+      clipboardValue = str.value.data;
+    }
+    is(clipboardValue, testText, "Data was copied to the clipboard.");
+  });
+});
+
+registerCleanupFunction(function cleanup() {
+  Services.clipboard.emptyClipboard(globalClipboard);
+});
diff --git a/browser/components/customizableui/test/browser_947914_button_find.js b/browser/components/customizableui/test/browser_947914_button_find.js
new file mode 100644
index 000000000..cf3b79e34
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_find.js
@@ -0,0 +1,22 @@
+/* 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*() {
+  info("Check find button existence and functionality");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let findButton = document.getElementById("find-button");
+  ok(findButton, "Find button exists in Panel Menu");
+
+  findButton.click();
+  ok(!gFindBar.hasAttribute("hidden"), "Findbar opened successfully");
+
+  // close find bar
+  gFindBar.close();
+  info("Findbar was closed");
+});
diff --git a/browser/components/customizableui/test/browser_947914_button_history.js b/browser/components/customizableui/test/browser_947914_button_history.js
new file mode 100644
index 000000000..64080fcc3
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_history.js
@@ -0,0 +1,24 @@
+/* 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*() {
+  info("Check history button existence and functionality");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let historyButton = document.getElementById("history-panelmenu");
+  ok(historyButton, "History button appears in Panel Menu");
+
+  historyButton.click();
+  let historyPanel = document.getElementById("PanelUI-history");
+  ok(historyPanel.getAttribute("current"), "History Panel is in view");
+
+  let panelHiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHiddenPromise
+  info("Menu panel was closed");
+});
diff --git a/browser/components/customizableui/test/browser_947914_button_newPrivateWindow.js b/browser/components/customizableui/test/browser_947914_button_newPrivateWindow.js
new file mode 100644
index 000000000..c2006bef0
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_newPrivateWindow.js
@@ -0,0 +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/. */
+
+"use strict";
+
+add_task(function*() {
+  info("Check private browsing button existence and functionality");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let windowWasHandled = false;
+  let privateWindow = null;
+
+  let observerWindowOpened = {
+    observe: function(aSubject, aTopic, aData) {
+      if (aTopic == "domwindowopened") {
+        privateWindow = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
+        privateWindow.addEventListener("load", function newWindowHandler() {
+          privateWindow.removeEventListener("load", newWindowHandler, false);
+          is(privateWindow.location.href, "chrome://browser/content/browser.xul",
+             "A new browser window was opened");
+          ok(PrivateBrowsingUtils.isWindowPrivate(privateWindow), "Window is private");
+          windowWasHandled = true;
+        }, false);
+      }
+    }
+  }
+
+  Services.ww.registerNotification(observerWindowOpened);
+
+  let privateBrowsingButton = document.getElementById("privatebrowsing-button");
+  ok(privateBrowsingButton, "Private browsing button exists in Panel Menu");
+  privateBrowsingButton.click();
+
+  try {
+    yield waitForCondition(() => windowWasHandled);
+    yield promiseWindowClosed(privateWindow);
+    info("The new private window was closed");
+  }
+  catch (e) {
+    ok(false, "The new private browser window was not properly handled");
+  }
+  finally {
+    Services.ww.unregisterNotification(observerWindowOpened);
+  }
+});
diff --git a/browser/components/customizableui/test/browser_947914_button_newWindow.js b/browser/components/customizableui/test/browser_947914_button_newWindow.js
new file mode 100644
index 000000000..47162ee86
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_newWindow.js
@@ -0,0 +1,47 @@
+/* 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*() {
+  info("Check new window button existence and functionality");
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let windowWasHandled = false;
+  let newWindow = null;
+
+  let observerWindowOpened = {
+    observe: function(aSubject, aTopic, aData) {
+      if (aTopic == "domwindowopened") {
+        newWindow = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
+        newWindow.addEventListener("load", function newWindowHandler() {
+          newWindow.removeEventListener("load", newWindowHandler, false);
+          is(newWindow.location.href, "chrome://browser/content/browser.xul",
+             "A new browser window was opened");
+          ok(!PrivateBrowsingUtils.isWindowPrivate(newWindow), "Window is not private");
+          windowWasHandled = true;
+        }, false);
+      }
+    }
+  }
+
+  Services.ww.registerNotification(observerWindowOpened);
+
+  let newWindowButton = document.getElementById("new-window-button");
+  ok(newWindowButton, "New Window button exists in Panel Menu");
+  newWindowButton.click();
+
+  try {
+    yield waitForCondition(() => windowWasHandled);
+    yield promiseWindowClosed(newWindow);
+    info("The new window was closed");
+  }
+  catch (e) {
+    ok(false, "The new browser window was not properly handled");
+  }
+  finally {
+    Services.ww.unregisterNotification(observerWindowOpened);
+  }
+});
diff --git a/browser/components/customizableui/test/browser_947914_button_paste.js b/browser/components/customizableui/test/browser_947914_button_paste.js
new file mode 100644
index 000000000..fc83ead56
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_paste.js
@@ -0,0 +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";
+
+var initialLocation = gBrowser.currentURI.spec;
+var globalClipboard;
+
+add_task(function*() {
+  yield BrowserTestUtils.withNewTab({gBrowser, url: "about:blank"}, function*() {
+    info("Check paste button existence and functionality");
+
+    let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
+    globalClipboard = Services.clipboard.kGlobalClipboard;
+
+    yield PanelUI.show();
+    info("Menu panel was opened");
+
+    let pasteButton = document.getElementById("paste-button");
+    ok(pasteButton, "Paste button exists in Panel Menu");
+
+    // add text to clipboard
+    let text = "Sample text for testing";
+    clipboard.copyString(text);
+
+    // test paste button by pasting text to URL bar
+    gURLBar.focus();
+    yield PanelUI.show();
+    info("Menu panel was opened");
+
+    ok(!pasteButton.hasAttribute("disabled"), "Paste button is enabled");
+    pasteButton.click();
+
+    is(gURLBar.value, text, "Text pasted successfully");
+  });
+});
+
+registerCleanupFunction(function cleanup() {
+  Services.clipboard.emptyClipboard(globalClipboard);
+});
diff --git a/browser/components/customizableui/test/browser_947914_button_print.js b/browser/components/customizableui/test/browser_947914_button_print.js
new file mode 100644
index 000000000..af7abcaeb
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_print.js
@@ -0,0 +1,45 @@
+/* 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 isOSX = (Services.appinfo.OS === "Darwin");
+
+add_task(function*() {
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: "http://example.com/",
+  }, function* () {
+    info("Check print button existence and functionality");
+
+    yield PanelUI.show();
+    info("Menu panel was opened");
+
+    yield waitForCondition(() => document.getElementById("print-button") != null);
+
+    let printButton = document.getElementById("print-button");
+    ok(printButton, "Print button exists in Panel Menu");
+
+    if (isOSX) {
+      let panelHiddenPromise = promisePanelHidden(window);
+      PanelUI.hide();
+      yield panelHiddenPromise;
+      info("Menu panel was closed");
+    }
+    else {
+      printButton.click();
+      yield waitForCondition(() => gInPrintPreviewMode);
+
+      ok(gInPrintPreviewMode, "Entered print preview mode");
+
+      // close print preview
+      if (gInPrintPreviewMode) {
+        PrintUtils.exitPrintPreview();
+        yield waitForCondition(() => !window.gInPrintPreviewMode);
+        info("Exited print preview")
+      }
+    }
+  });
+});
+
diff --git a/browser/components/customizableui/test/browser_947914_button_savePage.js b/browser/components/customizableui/test/browser_947914_button_savePage.js
new file mode 100644
index 000000000..543ff3ca6
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_savePage.js
@@ -0,0 +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/. */
+
+"use strict";
+
+add_task(function*() {
+  info("Check save page button existence");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let savePageButton = document.getElementById("save-page-button");
+  ok(savePageButton, "Save Page button exists in Panel Menu");
+
+  let panelHiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHiddenPromise;
+  info("Menu panel was closed");
+});
diff --git a/browser/components/customizableui/test/browser_947914_button_zoomIn.js b/browser/components/customizableui/test/browser_947914_button_zoomIn.js
new file mode 100644
index 000000000..4463d87d6
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_zoomIn.js
@@ -0,0 +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/. */
+
+"use strict";
+
+var initialPageZoom = ZoomManager.zoom;
+
+add_task(function*() {
+  info("Check zoom in button existence and functionality");
+
+  is(initialPageZoom, 1, "Initial zoom factor should be 1");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let zoomInButton = document.getElementById("zoom-in-button");
+  ok(zoomInButton, "Zoom in button exists in Panel Menu");
+
+  zoomInButton.click();
+  let pageZoomLevel = parseInt(ZoomManager.zoom * 100);
+  let zoomResetButton = document.getElementById("zoom-reset-button");
+  let expectedZoomLevel = parseInt(zoomResetButton.getAttribute("label"), 10);
+  ok(pageZoomLevel > 100 && pageZoomLevel == expectedZoomLevel, "Page zoomed in correctly");
+
+  // close the Panel
+  let panelHiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHiddenPromise;
+  info("Menu panel was closed");
+});
+
+add_task(function* asyncCleanup() {
+  // reset zoom level
+  ZoomManager.zoom = initialPageZoom;
+  info("Zoom level was restored");
+});
diff --git a/browser/components/customizableui/test/browser_947914_button_zoomOut.js b/browser/components/customizableui/test/browser_947914_button_zoomOut.js
new file mode 100644
index 000000000..f9f51ac9a
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_zoomOut.js
@@ -0,0 +1,38 @@
+/* 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";
+
+var initialPageZoom = ZoomManager.zoom;
+
+add_task(function*() {
+  info("Check zoom out button existence and functionality");
+
+  is(initialPageZoom, 1, "Initial zoom factor should be 1");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let zoomOutButton = document.getElementById("zoom-out-button");
+  ok(zoomOutButton, "Zoom out button exists in Panel Menu");
+
+  zoomOutButton.click();
+  let pageZoomLevel = Math.round(ZoomManager.zoom * 100);
+
+  let zoomResetButton = document.getElementById("zoom-reset-button");
+  let expectedZoomLevel = parseInt(zoomResetButton.getAttribute("label"), 10);
+  ok(pageZoomLevel < 100 && pageZoomLevel == expectedZoomLevel, "Page zoomed out correctly");
+
+  // close the panel
+  let panelHiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHiddenPromise;
+  info("Menu panel was closed");
+});
+
+add_task(function* asyncCleanup() {
+  // reset zoom level
+  ZoomManager.zoom = initialPageZoom;
+  info("Zoom level was restored");
+});
diff --git a/browser/components/customizableui/test/browser_947914_button_zoomReset.js b/browser/components/customizableui/test/browser_947914_button_zoomReset.js
new file mode 100644
index 000000000..372097665
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_zoomReset.js
@@ -0,0 +1,40 @@
+/* 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";
+
+var initialPageZoom = ZoomManager.zoom;
+
+add_task(function*() {
+  info("Check zoom reset button existence and functionality");
+
+  is(initialPageZoom, 1, "Page zoom reset correctly");
+  ZoomManager.zoom = 0.5;
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let zoomResetButton = document.getElementById("zoom-reset-button");
+  ok(zoomResetButton, "Zoom reset button exists in Panel Menu");
+
+  zoomResetButton.click();
+  yield new Promise(SimpleTest.executeSoon);
+
+  let pageZoomLevel = Math.floor(ZoomManager.zoom * 100);
+  let expectedZoomLevel = 100;
+  let buttonZoomLevel = parseInt(zoomResetButton.getAttribute("label"), 10);
+  is(pageZoomLevel, expectedZoomLevel, "Page zoom reset correctly");
+  is(pageZoomLevel, buttonZoomLevel, "Button displays the correct zoom level");
+
+  // close the panel
+  let panelHiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHiddenPromise;
+  info("Menu panel was closed");
+});
+
+add_task(function* asyncCleanup() {
+  // reset zoom level
+  ZoomManager.zoom = initialPageZoom;
+  info("Zoom level was restored");
+});
diff --git a/browser/components/customizableui/test/browser_947987_removable_default.js b/browser/components/customizableui/test/browser_947987_removable_default.js
new file mode 100644
index 000000000..98325ec2a
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947987_removable_default.js
@@ -0,0 +1,68 @@
+/* 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";
+
+var kWidgetId = "test-removable-widget-default";
+const kNavBar = CustomizableUI.AREA_NAVBAR;
+var widgetCounter = 0;
+
+registerCleanupFunction(removeCustomToolbars);
+
+// Sanity checks
+add_task(function() {
+  let brokenSpec = {id: kWidgetId + (widgetCounter++), removable: false};
+  SimpleTest.doesThrow(() => CustomizableUI.createWidget(brokenSpec),
+                       "Creating non-removable widget without defaultArea should throw.");
+
+  // Widget without removable set should be removable:
+  let wrapper = CustomizableUI.createWidget({id: kWidgetId + (widgetCounter++)});
+  ok(CustomizableUI.isWidgetRemovable(wrapper.id), "Should be removable by default.");
+  CustomizableUI.destroyWidget(wrapper.id);
+});
+
+// Test non-removable widget with defaultArea
+add_task(function*() {
+  // Non-removable widget with defaultArea should work:
+  let spec = {id: kWidgetId + (widgetCounter++), removable: false,
+              defaultArea: kNavBar};
+  let widgetWrapper;
+  try {
+    widgetWrapper = CustomizableUI.createWidget(spec);
+  } catch (ex) {
+    ok(false, "Creating a non-removable widget with a default area should not throw.");
+    return;
+  }
+
+  let placement = CustomizableUI.getPlacementOfWidget(spec.id);
+  ok(placement, "Widget should be placed.");
+  is(placement.area, kNavBar, "Widget should be in navbar");
+  let singleWrapper = widgetWrapper.forWindow(window);
+  ok(singleWrapper, "Widget should exist in window.");
+  ok(singleWrapper.node, "Widget node should exist in window.");
+  let expectedParent = CustomizableUI.getCustomizeTargetForArea(kNavBar, window);
+  is(singleWrapper.node.parentNode, expectedParent, "Widget should be in navbar.");
+
+  let otherWin = yield openAndLoadWindow(true);
+  placement = CustomizableUI.getPlacementOfWidget(spec.id);
+  ok(placement, "Widget should be placed.");
+  is(placement && placement.area, kNavBar, "Widget should be in navbar");
+
+  singleWrapper = widgetWrapper.forWindow(otherWin);
+  ok(singleWrapper, "Widget should exist in other window.");
+  if (singleWrapper) {
+    ok(singleWrapper.node, "Widget node should exist in other window.");
+    if (singleWrapper.node) {
+      let expectedParent = CustomizableUI.getCustomizeTargetForArea(kNavBar, otherWin);
+      is(singleWrapper.node.parentNode, expectedParent,
+         "Widget should be in navbar in other window.");
+    }
+  }
+  CustomizableUI.destroyWidget(spec.id);
+  yield promiseWindowClosed(otherWin);
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_948985_non_removable_defaultArea.js b/browser/components/customizableui/test/browser_948985_non_removable_defaultArea.js
new file mode 100644
index 000000000..456c9ed02
--- /dev/null
+++ b/browser/components/customizableui/test/browser_948985_non_removable_defaultArea.js
@@ -0,0 +1,32 @@
+/* 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 kWidgetId = "test-destroy-non-removable-defaultArea";
+
+add_task(function() {
+  let spec = {id: kWidgetId, label: "Test non-removable defaultArea re-adding.",
+              removable: false, defaultArea: CustomizableUI.AREA_NAVBAR};
+  CustomizableUI.createWidget(spec);
+  let placement = CustomizableUI.getPlacementOfWidget(kWidgetId);
+  ok(placement, "Should have placed the widget.");
+  is(placement && placement.area, CustomizableUI.AREA_NAVBAR, "Widget should be in navbar");
+  CustomizableUI.destroyWidget(kWidgetId);
+  CustomizableUI.removeWidgetFromArea(kWidgetId);
+
+  CustomizableUI.createWidget(spec);
+  ok(placement, "Should have placed the widget.");
+  is(placement && placement.area, CustomizableUI.AREA_NAVBAR, "Widget should be in navbar");
+  CustomizableUI.destroyWidget(kWidgetId);
+  CustomizableUI.removeWidgetFromArea(kWidgetId);
+
+  const kPrefCustomizationAutoAdd = "browser.uiCustomization.autoAdd";
+  Services.prefs.setBoolPref(kPrefCustomizationAutoAdd, false);
+  CustomizableUI.createWidget(spec);
+  ok(placement, "Should have placed the widget.");
+  is(placement && placement.area, CustomizableUI.AREA_NAVBAR, "Widget should be in navbar");
+  CustomizableUI.destroyWidget(kWidgetId);
+  CustomizableUI.removeWidgetFromArea(kWidgetId);
+  Services.prefs.clearUserPref(kPrefCustomizationAutoAdd);
+});
+
diff --git a/browser/components/customizableui/test/browser_952963_areaType_getter_no_area.js b/browser/components/customizableui/test/browser_952963_areaType_getter_no_area.js
new file mode 100644
index 000000000..fc05a99fd
--- /dev/null
+++ b/browser/components/customizableui/test/browser_952963_areaType_getter_no_area.js
@@ -0,0 +1,52 @@
+/* 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-unregisterArea-areaType";
+const kUnregisterAreaTestWidget = "test-widget-for-unregisterArea-areaType";
+const kTestWidget = "test-widget-no-area-areaType";
+registerCleanupFunction(removeCustomToolbars);
+
+function checkAreaType(widget) {
+  try {
+    is(widget.areaType, null, "areaType should be null");
+  } catch (ex) {
+    info("Fetching areaType threw: " + ex);
+    ok(false, "areaType getter shouldn't throw.");
+  }
+}
+
+// widget wrappers in unregisterArea'd areas and nowhere shouldn't throw when checking areaTypes.
+add_task(function*() {
+  // Using the ID before it's been created will imply a XUL wrapper; we'll test
+  // an API-based wrapper below
+  let toolbarNode = createToolbarWithPlacements(kToolbarName, [kUnregisterAreaTestWidget]);
+  CustomizableUI.unregisterArea(kToolbarName);
+  toolbarNode.remove();
+
+  let w = CustomizableUI.getWidget(kUnregisterAreaTestWidget);
+  checkAreaType(w);
+
+  w = CustomizableUI.getWidget(kTestWidget);
+  checkAreaType(w);
+
+  let spec = {id: kUnregisterAreaTestWidget, type: 'button', removable: true,
+              label: "areaType test", tooltiptext: "areaType test"};
+  CustomizableUI.createWidget(spec);
+  toolbarNode = createToolbarWithPlacements(kToolbarName, [kUnregisterAreaTestWidget]);
+  CustomizableUI.unregisterArea(kToolbarName);
+  toolbarNode.remove();
+  w = CustomizableUI.getWidget(spec.id);
+  checkAreaType(w);
+  CustomizableUI.removeWidgetFromArea(kUnregisterAreaTestWidget);
+  checkAreaType(w);
+  // XXXgijs: ensure cleanup function doesn't barf:
+  gAddedToolbars.delete(kToolbarName);
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
+
diff --git a/browser/components/customizableui/test/browser_956602_remove_special_widget.js b/browser/components/customizableui/test/browser_956602_remove_special_widget.js
new file mode 100644
index 000000000..f87b2e4c8
--- /dev/null
+++ b/browser/components/customizableui/test/browser_956602_remove_special_widget.js
@@ -0,0 +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";
+
+
+// Adding a separator and then dragging it out of the navbar shouldn't throw
+add_task(function*() {
+  try {
+    let navbar = document.getElementById("nav-bar");
+    let separatorSelector = "toolbarseparator[id^=customizableui-special-separator]";
+    ok(!navbar.querySelector(separatorSelector), "Shouldn't be a separator in the navbar");
+    CustomizableUI.addWidgetToArea('separator', 'nav-bar');
+    yield startCustomizing();
+    let separator = navbar.querySelector(separatorSelector);
+    ok(separator, "There should be a separator in the navbar now.");
+    let palette = document.getElementById("customization-palette");
+    simulateItemDrag(separator, palette);
+    ok(!palette.querySelector(separatorSelector), "No separator in the palette.");
+  } catch (ex) {
+    Cu.reportError(ex);
+    ok(false, "Shouldn't throw an exception moving an item to the navbar.");
+  } finally {
+    yield endCustomizing();
+  }
+});
+
+add_task(function* asyncCleanup() {
+  resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_962069_drag_to_overflow_chevron.js b/browser/components/customizableui/test/browser_962069_drag_to_overflow_chevron.js
new file mode 100644
index 000000000..7c4f6cfa4
--- /dev/null
+++ b/browser/components/customizableui/test/browser_962069_drag_to_overflow_chevron.js
@@ -0,0 +1,54 @@
+/* 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";
+
+var 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.");
+  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);
+
+  var ds = Components.classes["@mozilla.org/widget/dragservice;1"].
+           getService(Components.interfaces.nsIDragService);
+
+  ds.startDragSession();
+  try {
+    var [result, dataTransfer] = EventUtils.synthesizeDragOver(identityBox, overflowChevron);
+
+    // Wait for showing panel before ending drag session.
+    yield panelShownPromise;
+
+    EventUtils.synthesizeDropAfterDragOver(result, dataTransfer, overflowChevron);
+  } finally {
+    ds.endDragSession(true);
+  }
+
+  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.");
+});
diff --git a/browser/components/customizableui/test/browser_962884_opt_in_disable_hyphens.js b/browser/components/customizableui/test/browser_962884_opt_in_disable_hyphens.js
new file mode 100644
index 000000000..cf2603999
--- /dev/null
+++ b/browser/components/customizableui/test/browser_962884_opt_in_disable_hyphens.js
@@ -0,0 +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");
+  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");
+  multilineText = document.getAnonymousElementByAttribute(characterEncoding, "class", "toolbarbutton-multiline-text");
+  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();
+});
diff --git a/browser/components/customizableui/test/browser_963639_customizing_attribute_non_customizable_toolbar.js b/browser/components/customizableui/test/browser_963639_customizing_attribute_non_customizable_toolbar.js
new file mode 100644
index 000000000..e5710c50a
--- /dev/null
+++ b/browser/components/customizableui/test/browser_963639_customizing_attribute_non_customizable_toolbar.js
@@ -0,0 +1,34 @@
+/* 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 kToolbar = "test-toolbar-963639-non-customizable-customizing-attribute";
+
+add_task(function*() {
+  info("Test for Bug 963639 - CustomizeMode _onToolbarVisibilityChange sets @customizing on non-customizable toolbars");
+
+  let toolbar = document.createElement("toolbar");
+  toolbar.id = kToolbar;
+  gNavToolbox.appendChild(toolbar);
+
+  let testToolbar = document.getElementById(kToolbar)
+  ok(testToolbar, "Toolbar was created.");
+  is(gNavToolbox.getElementsByAttribute("id", kToolbar).length, 1,
+     "Toolbar was added to the navigator toolbox");
+
+  toolbar.setAttribute("toolbarname", "NonCustomizableToolbarCustomizingAttribute");
+  toolbar.setAttribute("collapsed", "true");
+
+  yield startCustomizing();
+  window.setToolbarVisibility(toolbar, "true");
+  isnot(toolbar.getAttribute("customizing"), "true",
+        "Toolbar doesn't have the customizing attribute");
+
+  yield endCustomizing();
+  gNavToolbox.removeChild(toolbar);
+
+  is(gNavToolbox.getElementsByAttribute("id", kToolbar).length, 0,
+     "Toolbar was removed from the navigator toolbox");
+});
diff --git a/browser/components/customizableui/test/browser_967000_button_charEncoding.js b/browser/components/customizableui/test/browser_967000_button_charEncoding.js
new file mode 100644
index 000000000..0688ebbd6
--- /dev/null
+++ b/browser/components/customizableui/test/browser_967000_button_charEncoding.js
@@ -0,0 +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 TEST_PAGE = "http://mochi.test:8888/browser/browser/components/customizableui/test/support/test_967000_charEncoding_page.html";
+
+add_task(function*() {
+  info("Check Character Encoding button functionality");
+
+  // add the Character Encoding button to the panel
+  CustomizableUI.addWidgetToArea("characterencoding-button",
+                                  CustomizableUI.AREA_PANEL);
+
+  // check the button's functionality
+  yield PanelUI.show();
+
+  let charEncodingButton = document.getElementById("characterencoding-button");
+  ok(charEncodingButton, "The Character Encoding button was added to the Panel Menu");
+  is(charEncodingButton.getAttribute("disabled"), "true",
+     "The Character encoding button is initially disabled");
+
+  let panelHidePromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHidePromise;
+
+  let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE, true, true);
+
+  yield PanelUI.show();
+  ok(!charEncodingButton.hasAttribute("disabled"), "The Character encoding button gets enabled");
+  let characterEncodingView = document.getElementById("PanelUI-characterEncodingView");
+  let subviewShownPromise = subviewShown(characterEncodingView);
+  charEncodingButton.click();
+  yield subviewShownPromise;
+
+  ok(characterEncodingView.hasAttribute("current"), "The Character encoding panel is displayed");
+
+  let pinnedEncodings = document.getElementById("PanelUI-characterEncodingView-pinned");
+  let charsetsList = document.getElementById("PanelUI-characterEncodingView-charsets");
+  ok(pinnedEncodings, "Pinned charsets are available");
+  ok(charsetsList, "Charsets list is available");
+
+  let checkedButtons = characterEncodingView.querySelectorAll("toolbarbutton[checked='true']");
+  is(checkedButtons.length, 2, "There should be 2 checked items (1 charset, 1 detector).");
+  is(checkedButtons[0].getAttribute("label"), "Unicode", "The unicode encoding is correctly selected");
+  is(characterEncodingView.querySelectorAll("#PanelUI-characterEncodingView-autodetect toolbarbutton[checked='true']").length,
+     1,
+     "There should be 1 checked detector.");
+
+  panelHidePromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHidePromise;
+
+  yield BrowserTestUtils.removeTab(newTab);
+});
+
+add_task(function* asyncCleanup() {
+  // reset the panel to the default state
+  yield resetCustomization();
+  ok(CustomizableUI.inDefaultState, "The UI is in default state again.");
+});
diff --git a/browser/components/customizableui/test/browser_967000_button_feeds.js b/browser/components/customizableui/test/browser_967000_button_feeds.js
new file mode 100644
index 000000000..8f391941a
--- /dev/null
+++ b/browser/components/customizableui/test/browser_967000_button_feeds.js
@@ -0,0 +1,60 @@
+/* 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 TEST_PAGE = "http://mochi.test:8888/browser/browser/components/customizableui/test/support/feeds_test_page.html";
+const TEST_FEED = "http://mochi.test:8888/browser/browser/components/customizableui/test/support/test-feed.xml"
+
+var newTab = null;
+var initialLocation = gBrowser.currentURI.spec;
+
+add_task(function*() {
+  info("Check Subscribe button functionality");
+
+  // add the Subscribe button to the panel
+  CustomizableUI.addWidgetToArea("feed-button",
+                                  CustomizableUI.AREA_PANEL);
+
+  // check the button's functionality
+  yield PanelUI.show();
+
+  let feedButton = document.getElementById("feed-button");
+  ok(feedButton, "The Subscribe button was added to the Panel Menu");
+  is(feedButton.getAttribute("disabled"), "true", "The Subscribe button is initially disabled");
+
+  let panelHidePromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHidePromise;
+
+  newTab = gBrowser.selectedTab;
+  yield promiseTabLoadEvent(newTab, TEST_PAGE);
+
+  yield PanelUI.show();
+
+  yield waitForCondition(() => !feedButton.hasAttribute("disabled"));
+  ok(!feedButton.hasAttribute("disabled"), "The Subscribe button gets enabled");
+
+  feedButton.click();
+  yield promiseTabLoadEvent(newTab, TEST_FEED);
+
+  is(gBrowser.currentURI.spec, TEST_FEED, "Subscribe page opened");
+  ok(!isPanelUIOpen(), "Panel is closed");
+
+  if (isPanelUIOpen()) {
+    panelHidePromise = promisePanelHidden(window);
+    PanelUI.hide();
+    yield panelHidePromise;
+  }
+});
+
+add_task(function* asyncCleanup() {
+  // reset the panel UI to the default state
+  yield resetCustomization();
+  ok(CustomizableUI.inDefaultState, "The UI is in default state again.");
+
+  // restore the initial location
+  gBrowser.addTab(initialLocation);
+  gBrowser.removeTab(newTab);
+});
diff --git a/browser/components/customizableui/test/browser_967000_button_sync.js b/browser/components/customizableui/test/browser_967000_button_sync.js
new file mode 100644
index 000000000..15a3235e0
--- /dev/null
+++ b/browser/components/customizableui/test/browser_967000_button_sync.js
@@ -0,0 +1,335 @@
+/* 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);
+
+let {SyncedTabs} = Cu.import("resource://services-sync/SyncedTabs.jsm", {});
+
+XPCOMUtils.defineLazyModuleGetter(this, "UITour", "resource:///modules/UITour.jsm");
+
+// These are available on the widget implementation, but it seems impossible
+// to grab that impl at runtime.
+const DECKINDEX_TABS = 0;
+const DECKINDEX_TABSDISABLED = 1;
+const DECKINDEX_FETCHING = 2;
+const DECKINDEX_NOCLIENTS = 3;
+
+var initialLocation = gBrowser.currentURI.spec;
+var newTab = null;
+
+// A helper to notify there are new tabs. Returns a promise that is resolved
+// once the UI has been updated.
+function updateTabsPanel() {
+  let promiseTabsUpdated = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
+  Services.obs.notifyObservers(null, SyncedTabs.TOPIC_TABS_CHANGED, null);
+  return promiseTabsUpdated;
+}
+
+// This is the mock we use for SyncedTabs.jsm - tests may override various
+// functions.
+let mockedInternal = {
+  get isConfiguredToSyncTabs() { return true; },
+  getTabClients() { return []; },
+  syncTabs() {},
+  hasSyncedThisSession: false,
+};
+
+
+add_task(function* setup() {
+  let oldInternal = SyncedTabs._internal;
+  SyncedTabs._internal = mockedInternal;
+
+  registerCleanupFunction(() => {
+    SyncedTabs._internal = oldInternal;
+  });
+});
+
+// The test expects the about:preferences#sync page to open in the current tab
+function* openPrefsFromMenuPanel(expectedPanelId, entryPoint) {
+  info("Check Sync button functionality");
+  Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri", "http://example.com/");
+
+  // add the Sync button to the panel
+  CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
+
+  // check the button's functionality
+  yield PanelUI.show();
+
+  if (entryPoint == "uitour") {
+    UITour.tourBrowsersByWindow.set(window, new Set());
+    UITour.tourBrowsersByWindow.get(window).add(gBrowser.selectedBrowser);
+  }
+
+  let syncButton = document.getElementById("sync-button");
+  ok(syncButton, "The Sync button was added to the Panel Menu");
+
+  syncButton.click();
+  let syncPanel = document.getElementById("PanelUI-remotetabs");
+  ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
+
+  // Sync is not configured - verify that state is reflected.
+  let subpanel = document.getElementById(expectedPanelId)
+  ok(!subpanel.hidden, "sync setup element is visible");
+
+  // Find and click the "setup" button.
+  let setupButton = subpanel.querySelector(".PanelUI-remotetabs-prefs-button");
+  setupButton.click();
+
+  let deferred = Promise.defer();
+  let handler = (e) => {
+    if (e.originalTarget != gBrowser.selectedBrowser.contentDocument ||
+        e.target.location.href == "about:blank") {
+      info("Skipping spurious 'load' event for " + e.target.location.href);
+      return;
+    }
+    gBrowser.selectedBrowser.removeEventListener("load", handler, true);
+    deferred.resolve();
+  }
+  gBrowser.selectedBrowser.addEventListener("load", handler, true);
+
+  yield deferred.promise;
+  newTab = gBrowser.selectedTab;
+
+  is(gBrowser.currentURI.spec, "about:preferences?entrypoint=" + entryPoint + "#sync",
+    "Firefox Sync preference page opened with `menupanel` entrypoint");
+  ok(!isPanelUIOpen(), "The panel closed");
+
+  if (isPanelUIOpen()) {
+    let panelHidePromise = promisePanelHidden(window);
+    PanelUI.hide();
+    yield panelHidePromise;
+  }
+}
+
+function* asyncCleanup() {
+  Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
+  // reset the panel UI to the default state
+  yield resetCustomization();
+  ok(CustomizableUI.inDefaultState, "The panel UI is in default state again.");
+
+  // restore the tabs
+  gBrowser.addTab(initialLocation);
+  gBrowser.removeTab(newTab);
+  UITour.tourBrowsersByWindow.delete(window);
+}
+
+// When Sync is not setup.
+add_task(() => openPrefsFromMenuPanel("PanelUI-remotetabs-setupsync", "synced-tabs"));
+add_task(asyncCleanup);
+
+// When Sync is configured in a "needs reauthentication" state.
+add_task(function* () {
+  // configure our broadcasters so we are in the right state.
+  document.getElementById("sync-reauth-state").hidden = false;
+  document.getElementById("sync-setup-state").hidden = true;
+  document.getElementById("sync-syncnow-state").hidden = true;
+  yield openPrefsFromMenuPanel("PanelUI-remotetabs-reauthsync", "synced-tabs")
+});
+
+// Test the mobile promo links
+add_task(function* () {
+  // change the preferences for the mobile links.
+  Services.prefs.setCharPref("identity.mobilepromo.android", "http://example.com/?os=android&tail=");
+  Services.prefs.setCharPref("identity.mobilepromo.ios", "http://example.com/?os=ios&tail=");
+
+  mockedInternal.getTabClients = () => [];
+  mockedInternal.syncTabs = () => Promise.resolve();
+
+  document.getElementById("sync-reauth-state").hidden = true;
+  document.getElementById("sync-setup-state").hidden = true;
+  document.getElementById("sync-syncnow-state").hidden = false;
+
+  CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
+
+  let syncPanel = document.getElementById("PanelUI-remotetabs");
+  let links = syncPanel.querySelectorAll(".remotetabs-promo-link");
+
+  is(links.length, 2, "found 2 links as expected");
+
+  // test each link and left and middle mouse buttons
+  for (let link of links) {
+    for (let button = 0; button < 2; button++) {
+      yield PanelUI.show();
+      EventUtils.sendMouseEvent({ type: "click", button }, link, window);
+      // the panel should have been closed.
+      ok(!isPanelUIOpen(), "click closed the panel");
+      // should be a new tab - wait for the load.
+      is(gBrowser.tabs.length, 2, "there's a new tab");
+      yield new Promise(resolve => {
+        if (gBrowser.selectedBrowser.currentURI.spec == "about:blank") {
+          gBrowser.selectedBrowser.addEventListener("load", function listener(e) {
+            gBrowser.selectedBrowser.removeEventListener("load", listener, true);
+            resolve();
+          }, true);
+          return;
+        }
+        // the new tab has already transitioned away from about:blank so we
+        // are good to go.
+        resolve();
+      });
+
+      let os = link.getAttribute("mobile-promo-os");
+      let expectedUrl = `http://example.com/?os=${os}&tail=synced-tabs`;
+      is(gBrowser.selectedBrowser.currentURI.spec, expectedUrl, "correct URL");
+      gBrowser.removeTab(gBrowser.selectedTab);
+    }
+  }
+
+  // test each link and right mouse button - should be a noop.
+  yield PanelUI.show();
+  for (let link of links) {
+    EventUtils.sendMouseEvent({ type: "click", button: 2 }, link, window);
+    // the panel should still be open
+    ok(isPanelUIOpen(), "panel remains open after right-click");
+    is(gBrowser.tabs.length, 1, "no new tab was opened");
+  }
+  PanelUI.hide();
+
+  Services.prefs.clearUserPref("identity.mobilepromo.android");
+  Services.prefs.clearUserPref("identity.mobilepromo.ios");
+});
+
+// Test the "Sync Now" button
+add_task(function* () {
+  mockedInternal.getTabClients = () => [];
+  mockedInternal.syncTabs = () => {
+    return Promise.resolve();
+  }
+
+  // configure our broadcasters so we are in the right state.
+  document.getElementById("sync-reauth-state").hidden = true;
+  document.getElementById("sync-setup-state").hidden = true;
+  document.getElementById("sync-syncnow-state").hidden = false;
+
+  // add the Sync button to the panel
+  CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
+  yield PanelUI.show();
+  document.getElementById("sync-button").click();
+  let syncPanel = document.getElementById("PanelUI-remotetabs");
+  ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
+
+  let subpanel = document.getElementById("PanelUI-remotetabs-main")
+  ok(!subpanel.hidden, "main pane is visible");
+  let deck = document.getElementById("PanelUI-remotetabs-deck");
+
+  // The widget is still fetching tabs, as we've neutered everything that
+  // provides them
+  is(deck.selectedIndex, DECKINDEX_FETCHING, "first deck entry is visible");
+
+  let syncNowButton = document.getElementById("PanelUI-remotetabs-syncnow");
+
+  let didSync = false;
+  let oldDoSync = gSyncUI.doSync;
+  gSyncUI.doSync = function() {
+    didSync = true;
+    mockedInternal.hasSyncedThisSession = true;
+    gSyncUI.doSync = oldDoSync;
+  }
+  syncNowButton.click();
+  ok(didSync, "clicking the button called the correct function");
+
+  // Tell the widget there are tabs available, but with zero clients.
+  mockedInternal.getTabClients = () => {
+    return Promise.resolve([]);
+  }
+  yield updateTabsPanel();
+  // The UI should be showing the "no clients" pane.
+  is(deck.selectedIndex, DECKINDEX_NOCLIENTS, "no-clients deck entry is visible");
+
+  // Tell the widget there are tabs available - we have 3 clients, one with no
+  // tabs.
+  mockedInternal.getTabClients = () => {
+    return Promise.resolve([
+      {
+        id: "guid_mobile",
+        type: "client",
+        name: "My Phone",
+        tabs: [],
+      },
+      {
+        id: "guid_desktop",
+        type: "client",
+        name: "My Desktop",
+        tabs: [
+          {
+            title: "http://example.com/10",
+            lastUsed: 10, // the most recent
+          },
+          {
+            title: "http://example.com/1",
+            lastUsed: 1, // the least recent.
+          },
+          {
+            title: "http://example.com/5",
+            lastUsed: 5,
+          },
+        ],
+      },
+      {
+        id: "guid_second_desktop",
+        name: "My Other Desktop",
+        tabs: [
+          {
+            title: "http://example.com/6",
+            lastUsed: 6,
+          }
+        ],
+      },
+    ]);
+  };
+  yield updateTabsPanel();
+
+  // The UI should be showing tabs!
+  is(deck.selectedIndex, DECKINDEX_TABS, "no-clients deck entry is visible");
+  let tabList = document.getElementById("PanelUI-remotetabs-tabslist");
+  let node = tabList.firstChild;
+  // First entry should be the client with the most-recent tab.
+  is(node.getAttribute("itemtype"), "client", "node is a client entry");
+  is(node.textContent, "My Desktop", "correct client");
+  // Next entry is the most-recent tab
+  node = node.nextSibling;
+  is(node.getAttribute("itemtype"), "tab", "node is a tab");
+  is(node.getAttribute("label"), "http://example.com/10");
+
+  // Next entry is the next-most-recent tab
+  node = node.nextSibling;
+  is(node.getAttribute("itemtype"), "tab", "node is a tab");
+  is(node.getAttribute("label"), "http://example.com/5");
+
+  // Next entry is the least-recent tab from the first client.
+  node = node.nextSibling;
+  is(node.getAttribute("itemtype"), "tab", "node is a tab");
+  is(node.getAttribute("label"), "http://example.com/1");
+
+  // Next is a menuseparator between the clients.
+  node = node.nextSibling;
+  is(node.nodeName, "menuseparator");
+
+  // Next is the client with 1 tab.
+  node = node.nextSibling;
+  is(node.getAttribute("itemtype"), "client", "node is a client entry");
+  is(node.textContent, "My Other Desktop", "correct client");
+  // Its single tab
+  node = node.nextSibling;
+  is(node.getAttribute("itemtype"), "tab", "node is a tab");
+  is(node.getAttribute("label"), "http://example.com/6");
+
+  // Next is a menuseparator between the clients.
+  node = node.nextSibling;
+  is(node.nodeName, "menuseparator");
+
+  // Next is the client with no tab.
+  node = node.nextSibling;
+  is(node.getAttribute("itemtype"), "client", "node is a client entry");
+  is(node.textContent, "My Phone", "correct client");
+  // There is a single node saying there's no tabs for the client.
+  node = node.nextSibling;
+  is(node.nodeName, "label", "node is a label");
+  is(node.getAttribute("itemtype"), "", "node is neither a tab nor a client");
+
+  node = node.nextSibling;
+  is(node, null, "no more entries");
+});
diff --git 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
new file mode 100644
index 000000000..88c30bf81
--- /dev/null
+++ b/browser/components/customizableui/test/browser_968447_bookmarks_toolbar_items_in_panel.js
@@ -0,0 +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();
+});
+
diff --git a/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js b/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
new file mode 100644
index 000000000..f7504fc41
--- /dev/null
+++ b/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
@@ -0,0 +1,56 @@
+/* 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 kHidden1Id = "test-hidden-button-1";
+const kHidden2Id = "test-hidden-button-2";
+
+var navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+// When we drag an item onto a customizable area, and not over a specific target, we
+// should assume that we're appending them to the area. If doing so, we should scan
+// backwards over any hidden items and insert the item before those hidden items.
+add_task(function*() {
+  ok(CustomizableUI.inDefaultState, "Should be in the default state");
+
+  // Iterate backwards over the items in the nav-bar until we find the first
+  // one that is not hidden.
+  let placements = CustomizableUI.getWidgetsInArea(CustomizableUI.AREA_NAVBAR);
+  let lastVisible = null;
+  for (let widgetGroup of placements.reverse()) {
+    let widget = widgetGroup.forWindow(window);
+    if (widget && widget.node && !widget.node.hidden) {
+      lastVisible = widget.node;
+      break;
+    }
+  }
+
+  if (!lastVisible) {
+    ok(false, "Apparently, there are no visible items in the nav-bar.");
+  }
+
+  info("The last visible item in the nav-bar has ID: " + lastVisible.id);
+
+  let hidden1 = createDummyXULButton(kHidden1Id, "You can't see me");
+  let hidden2 = createDummyXULButton(kHidden2Id, "You can't see me either.");
+  hidden1.hidden = hidden2.hidden = true;
+
+  // Make sure we have some hidden items at the end of the nav-bar.
+  navbar.insertItem(hidden1.id);
+  navbar.insertItem(hidden2.id);
+
+  // Drag an item and drop it onto the nav-bar customization target, but
+  // not over a particular item.
+  yield startCustomizing();
+  let downloadsButton = document.getElementById("downloads-button");
+  simulateItemDrag(downloadsButton, navbar.customizationTarget);
+
+  yield endCustomizing();
+
+  is(downloadsButton.previousSibling.id, lastVisible.id,
+     "The downloads button should be placed after the last visible item.");
+
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_969427_recreate_destroyed_widget_after_reset.js b/browser/components/customizableui/test/browser_969427_recreate_destroyed_widget_after_reset.js
new file mode 100644
index 000000000..b5479fcb7
--- /dev/null
+++ b/browser/components/customizableui/test/browser_969427_recreate_destroyed_widget_after_reset.js
@@ -0,0 +1,34 @@
+/* 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 getPlacementArea(id) {
+  let placement = CustomizableUI.getPlacementOfWidget(id);
+  return placement && placement.area;
+}
+
+// Check that a destroyed widget recreated after a reset call goes to
+// the navigation bar.
+add_task(function() {
+  const kWidgetId = "test-recreate-after-reset";
+  let spec = {id: kWidgetId, label: "Test re-create after reset.",
+              removable: true, defaultArea: CustomizableUI.AREA_NAVBAR};
+
+  CustomizableUI.createWidget(spec);
+  is(getPlacementArea(kWidgetId), CustomizableUI.AREA_NAVBAR,
+     "widget is in the navigation bar");
+
+  CustomizableUI.destroyWidget(kWidgetId);
+  isnot(getPlacementArea(kWidgetId), CustomizableUI.AREA_NAVBAR,
+        "widget removed from the navigation bar");
+
+  CustomizableUI.reset();
+
+  CustomizableUI.createWidget(spec);
+  is(getPlacementArea(kWidgetId), CustomizableUI.AREA_NAVBAR,
+     "widget recreated and added back to the nav bar");
+
+  CustomizableUI.destroyWidget(kWidgetId);
+});
diff --git a/browser/components/customizableui/test/browser_969661_character_encoding_navbar_disabled.js b/browser/components/customizableui/test/browser_969661_character_encoding_navbar_disabled.js
new file mode 100644
index 000000000..6f057a100
--- /dev/null
+++ b/browser/components/customizableui/test/browser_969661_character_encoding_navbar_disabled.js
@@ -0,0 +1,26 @@
+/* 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 the character encoding menu to the panel, exiting customize mode,
+// and moving it to the nav-bar should have it enabled, not disabled.
+add_task(function*() {
+  yield startCustomizing();
+  CustomizableUI.addWidgetToArea("characterencoding-button", "PanelUI-contents");
+  yield endCustomizing();
+  yield PanelUI.show();
+  let panelHiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHiddenPromise;
+  CustomizableUI.addWidgetToArea("characterencoding-button", 'nav-bar');
+  let button = document.getElementById("characterencoding-button");
+  ok(!button.hasAttribute("disabled"), "Button shouldn't be disabled");
+});
+
+add_task(function asyncCleanup() {
+  resetCustomization();
+});
+
diff --git a/browser/components/customizableui/test/browser_970511_undo_restore_default.js b/browser/components/customizableui/test/browser_970511_undo_restore_default.js
new file mode 100644
index 000000000..e7b3ca674
--- /dev/null
+++ b/browser/components/customizableui/test/browser_970511_undo_restore_default.js
@@ -0,0 +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/. */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+// Restoring default should reset theme and 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");
+
+  let themesButton = document.getElementById("customization-lwtheme-button");
+  let popup = document.getElementById("customization-lwtheme-menu");
+  let popupShownPromise = popupShown(popup);
+  EventUtils.synthesizeMouseAtCenter(themesButton, {});
+  info("Clicked on themes button");
+  yield popupShownPromise;
+
+  let recommendedHeader = document.getElementById("customization-lwtheme-menu-recommended");
+  let firstLWTheme = recommendedHeader.nextSibling;
+  let firstLWThemeId = firstLWTheme.theme.id;
+  let themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
+  firstLWTheme.doCommand();
+  info("Clicked on first theme");
+  yield themeChangedPromise;
+
+  is(LightweightThemeManager.currentTheme.id, firstLWThemeId, "Theme changed to first option");
+
+  yield gCustomizeMode.reset();
+
+  ok(CustomizableUI.inDefaultState, "In default state after reset");
+  is(undoResetButton.hidden, false, "The undo button is visible after reset");
+  is(LightweightThemeManager.currentTheme, null, "Theme reset to default");
+
+  yield gCustomizeMode.undoReset()
+
+  is(LightweightThemeManager.currentTheme.id, firstLWThemeId, "Theme has been reset from default to original choice");
+  ok(!CustomizableUI.inDefaultState, "Not in default state after undo-reset");
+  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();
+});
diff --git a/browser/components/customizableui/test/browser_972267_customizationchange_events.js b/browser/components/customizableui/test/browser_972267_customizationchange_events.js
new file mode 100644
index 000000000..b37dbe954
--- /dev/null
+++ b/browser/components/customizableui/test/browser_972267_customizationchange_events.js
@@ -0,0 +1,46 @@
+/* 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";
+
+// Create a new window, then move the home button to the menu and check both windows have
+// customizationchange events fire on the toolbox:
+add_task(function*() {
+  let newWindow = yield openAndLoadWindow();
+  let otherToolbox = newWindow.gNavToolbox;
+
+  let handlerCalledCount = 0;
+  let handler = (ev) => {
+    handlerCalledCount++;
+  };
+
+  let homeButton = document.getElementById("home-button");
+
+  gNavToolbox.addEventListener("customizationchange", handler);
+  otherToolbox.addEventListener("customizationchange", handler);
+
+  gCustomizeMode.addToPanel(homeButton);
+
+  is(handlerCalledCount, 2, "Should be called for both windows.");
+
+  // If the test is run in isolation and the panel has never been open,
+  // the button will be in the palette. Deal with this case:
+  if (homeButton.parentNode.id == "BrowserToolbarPalette") {
+    yield PanelUI.ensureReady();
+    isnot(homeButton.parentNode.id, "BrowserToolbarPalette", "Home button should now be in panel");
+  }
+
+  handlerCalledCount = 0;
+  gCustomizeMode.addToToolbar(homeButton);
+  is(handlerCalledCount, 2, "Should be called for both windows.");
+
+  gNavToolbox.removeEventListener("customizationchange", handler);
+  otherToolbox.removeEventListener("customizationchange", handler);
+
+  yield promiseWindowClosed(newWindow);
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_973641_button_addon.js b/browser/components/customizableui/test/browser_973641_button_addon.js
new file mode 100755
index 000000000..796bf3d0e
--- /dev/null
+++ b/browser/components/customizableui/test/browser_973641_button_addon.js
@@ -0,0 +1,71 @@
+/* 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 kButton = "test_button_for_addon";
+var initialLocation = gBrowser.currentURI.spec;
+
+add_task(function*() {
+  info("Check addon button functionality");
+
+  // create mocked addon button on the navigation bar
+  let widgetSpec = {
+    id: kButton,
+    type: 'button',
+    onClick: function() {
+      gBrowser.selectedTab = gBrowser.addTab("about:addons");
+    }
+  };
+  CustomizableUI.createWidget(widgetSpec);
+  CustomizableUI.addWidgetToArea(kButton, CustomizableUI.AREA_NAVBAR);
+
+  // check the button's functionality in navigation bar
+  let addonButton = document.getElementById(kButton);
+  let navBar = document.getElementById("nav-bar");
+  ok(addonButton, "Addon button exists");
+  ok(navBar.contains(addonButton), "Addon button is in the navbar");
+  yield checkButtonFunctionality(addonButton);
+
+  resetTabs();
+
+  // move the add-on button in the Panel Menu
+  CustomizableUI.addWidgetToArea(kButton, CustomizableUI.AREA_PANEL);
+  ok(!navBar.contains(addonButton), "Addon button was removed from the browser bar");
+
+  // check the addon button's functionality in the Panel Menu
+  yield PanelUI.show();
+  var panelMenu = document.getElementById("PanelUI-mainView");
+  let addonButtonInPanel = panelMenu.getElementsByAttribute("id", kButton);
+  ok(panelMenu.contains(addonButton), "Addon button was added to the Panel Menu");
+  yield checkButtonFunctionality(addonButtonInPanel[0]);
+});
+
+add_task(function* asyncCleanup() {
+  resetTabs();
+
+  // reset the UI to the default state
+  yield resetCustomization();
+  ok(CustomizableUI.inDefaultState, "The UI is in default state again.");
+
+  // destroy the widget
+  CustomizableUI.destroyWidget(kButton);
+});
+
+function resetTabs() {
+  // close all opened tabs
+  while (gBrowser.tabs.length > 1) {
+    gBrowser.removeTab(gBrowser.selectedTab);
+  }
+
+  // restore the initial tab
+  gBrowser.addTab(initialLocation);
+  gBrowser.removeTab(gBrowser.selectedTab);
+}
+
+function* checkButtonFunctionality(aButton) {
+  aButton.click();
+  yield waitForCondition(() => gBrowser.currentURI &&
+                               gBrowser.currentURI.spec == "about:addons");
+}
diff --git a/browser/components/customizableui/test/browser_973932_addonbar_currentset.js b/browser/components/customizableui/test/browser_973932_addonbar_currentset.js
new file mode 100644
index 000000000..66fa6ef47
--- /dev/null
+++ b/browser/components/customizableui/test/browser_973932_addonbar_currentset.js
@@ -0,0 +1,30 @@
+/* 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";
+
+var addonbarID = CustomizableUI.AREA_ADDONBAR;
+var addonbar = document.getElementById(addonbarID);
+
+// Check that currentset is correctly updated after a reset:
+add_task(function*() {
+  let placements = CustomizableUI.getWidgetIdsInArea(addonbarID);
+  is(placements.join(','), addonbar.getAttribute("currentset"), "Addon-bar currentset should match default placements");
+  ok(CustomizableUI.inDefaultState, "Should be in default state");
+  info("Adding a spring to add-on bar shim");
+  CustomizableUI.addWidgetToArea("spring", addonbarID, 1);
+  ok(addonbar.getElementsByTagName("toolbarspring").length, "There should be a spring in the toolbar");
+  ok(!CustomizableUI.inDefaultState, "Should no longer be in default state");
+  placements = CustomizableUI.getWidgetIdsInArea(addonbarID);
+  is(placements.join(','), addonbar.getAttribute("currentset"), "Addon-bar currentset should match placements after spring addition");
+
+  yield startCustomizing();
+  yield gCustomizeMode.reset();
+  ok(CustomizableUI.inDefaultState, "Should be in default state after reset");
+  placements = CustomizableUI.getWidgetIdsInArea(addonbarID);
+  is(placements.join(','), addonbar.getAttribute("currentset"), "Addon-bar currentset should match default placements after reset");
+  ok(!addonbar.getElementsByTagName("toolbarspring").length, "There should be no spring in the toolbar");
+  yield endCustomizing();
+});
+
diff --git a/browser/components/customizableui/test/browser_975719_customtoolbars_behaviour.js b/browser/components/customizableui/test/browser_975719_customtoolbars_behaviour.js
new file mode 100644
index 000000000..73fc7c1ff
--- /dev/null
+++ b/browser/components/customizableui/test/browser_975719_customtoolbars_behaviour.js
@@ -0,0 +1,145 @@
+/* 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);
+
+const kXULWidgetId = "a-test-button"; // we'll create a button with this ID.
+
+add_task(function setup() {
+  // create a XUL button and add it to the palette.
+  createDummyXULButton(kXULWidgetId, "test-button");
+});
+
+add_task(function* customizeToolbarAndKeepIt() {
+  ok(gNavToolbox.toolbarset, "There should be a toolbarset");
+  let toolbarID = "testAustralisCustomToolbar";
+  gNavToolbox.appendCustomToolbar(toolbarID, "");
+  let toolbarDOMID = getToolboxCustomToolbarId(toolbarID);
+  let toolbarElement = document.getElementById(toolbarDOMID);
+  ok(toolbarElement, "There should be a toolbar");
+  if (!toolbarElement) {
+    ok(false, "No toolbar created, bailing out of the test.");
+    return;
+  }
+  is(toolbarElement.nextSibling, gNavToolbox.toolbarset,
+     "Toolbar should have been inserted in toolbox, before toolbarset element");
+  let cuiAreaType = CustomizableUI.getAreaType(toolbarDOMID);
+  is(cuiAreaType, CustomizableUI.TYPE_TOOLBAR,
+     "CustomizableUI should know the area and think it's a toolbar");
+  if (cuiAreaType != CustomizableUI.TYPE_TOOLBAR) {
+    ok(false, "Toolbar not registered successfully, bailing out of the test.");
+    toolbarElement.remove();
+    return;
+  }
+  ok(!CustomizableUI.getWidgetIdsInArea(toolbarDOMID).length, "There should be no widgets in the area yet.");
+  CustomizableUI.addWidgetToArea("open-file-button", toolbarDOMID, 0);
+  ok(toolbarElement.hasChildNodes(), "Toolbar should now have a button.");
+  assertAreaPlacements(toolbarDOMID, ["open-file-button"]);
+
+  gNavToolbox.toolbarset.setAttribute("toolbar1", toolbarID + ":open-file-button");
+  document.persist(gNavToolbox.toolbarset.id, "toolbar1");
+
+  yield startCustomizing();
+  // First, exit customize mode without doing anything, and verify the toolbar doesn't get removed.
+  yield endCustomizing();
+  ok(!CustomizableUI.inDefaultState, "Shouldn't be in default state, the toolbar should still be there.");
+  cuiAreaType = CustomizableUI.getAreaType(toolbarDOMID);
+  is(cuiAreaType, CustomizableUI.TYPE_TOOLBAR,
+     "CustomizableUI should still know the area and think it's a toolbar");
+  ok(toolbarElement.parentNode, "Toolbar should still be in the DOM.");
+  ok(toolbarElement.hasChildNodes(), "Toolbar should still have items in it.");
+  assertAreaPlacements(toolbarDOMID, ["open-file-button"]);
+
+  let newWindow = yield openAndLoadWindow({}, true);
+  is(newWindow.gNavToolbox.toolbarset.getAttribute("toolbar1"),
+     gNavToolbox.toolbarset.getAttribute("toolbar1"),
+     "Attribute should be the same in new window");
+  yield promiseWindowClosed(newWindow);
+
+  // Then customize again, and this time empty out the toolbar and verify it *does* get removed.
+  yield startCustomizing();
+  let openFileButton = document.getElementById("open-file-button");
+  let palette = document.getElementById("customization-palette");
+  simulateItemDrag(openFileButton, palette);
+  ok(!CustomizableUI.inDefaultState, "Shouldn't be in default state because there's still a non-collapsed toolbar.");
+  ok(!toolbarElement.hasChildNodes(), "Toolbar should have no more child nodes.");
+
+  toolbarElement.collapsed = true;
+  ok(CustomizableUI.inDefaultState, "Should be in default state because there's now just a collapsed toolbar.");
+  toolbarElement.collapsed = false;
+  ok(!CustomizableUI.inDefaultState, "Shouldn't be in default state because there's a non-collapsed toolbar again.");
+  yield endCustomizing();
+  ok(CustomizableUI.inDefaultState, "Should be in default state because the toolbar should have been removed.");
+
+  newWindow = yield openAndLoadWindow({}, true);
+  ok(!newWindow.gNavToolbox.toolbarset.hasAttribute("toolbar1"),
+     "Attribute should be gone in new window");
+  yield promiseWindowClosed(newWindow);
+
+  ok(!toolbarElement.parentNode, "Toolbar should no longer be in the DOM.");
+  cuiAreaType = CustomizableUI.getAreaType(toolbarDOMID);
+  is(cuiAreaType, null, "CustomizableUI should have forgotten all about the area");
+});
+
+add_task(function* resetShouldDealWithCustomToolbars() {
+  ok(gNavToolbox.toolbarset, "There should be a toolbarset");
+  let toolbarID = "testAustralisCustomToolbar";
+  gNavToolbox.appendCustomToolbar(toolbarID, "");
+  let toolbarDOMID = getToolboxCustomToolbarId(toolbarID);
+  let toolbarElement = document.getElementById(toolbarDOMID);
+  ok(toolbarElement, "There should be a toolbar");
+  if (!toolbarElement) {
+    ok(false, "No toolbar created, bailing out of the test.");
+    return;
+  }
+  is(toolbarElement.nextSibling, gNavToolbox.toolbarset,
+     "Toolbar should have been inserted in toolbox, before toolbarset element");
+  let cuiAreaType = CustomizableUI.getAreaType(toolbarDOMID);
+  is(cuiAreaType, CustomizableUI.TYPE_TOOLBAR,
+     "CustomizableUI should know the area and think it's a toolbar");
+  if (cuiAreaType != CustomizableUI.TYPE_TOOLBAR) {
+    ok(false, "Toolbar not registered successfully, bailing out of the test.");
+    toolbarElement.remove();
+    return;
+  }
+  ok(!CustomizableUI.getWidgetIdsInArea(toolbarDOMID).length, "There should be no widgets in the area yet.");
+  CustomizableUI.addWidgetToArea(kXULWidgetId, toolbarDOMID, 0);
+  ok(toolbarElement.hasChildNodes(), "Toolbar should now have a button.");
+  assertAreaPlacements(toolbarDOMID, [kXULWidgetId]);
+
+  gNavToolbox.toolbarset.setAttribute("toolbar2", `${toolbarID}:${kXULWidgetId}`);
+  document.persist(gNavToolbox.toolbarset.id, "toolbar2");
+
+  let newWindow = yield openAndLoadWindow({}, true);
+  is(newWindow.gNavToolbox.toolbarset.getAttribute("toolbar2"),
+     gNavToolbox.toolbarset.getAttribute("toolbar2"),
+     "Attribute should be the same in new window");
+  yield promiseWindowClosed(newWindow);
+
+  CustomizableUI.reset();
+
+  newWindow = yield openAndLoadWindow({}, true);
+  ok(!newWindow.gNavToolbox.toolbarset.hasAttribute("toolbar2"),
+     "Attribute should be gone in new window");
+  yield promiseWindowClosed(newWindow);
+
+  ok(CustomizableUI.inDefaultState, "Should be in default state after reset.");
+  let xulButton = document.getElementById(kXULWidgetId);
+  ok(!xulButton, "XUL button shouldn't be in the document anymore.");
+  ok(gNavToolbox.palette.querySelector(`#${kXULWidgetId}`), "XUL button should be in the palette");
+  ok(!toolbarElement.hasChildNodes(), "Toolbar should have no more child nodes.");
+  ok(!toolbarElement.parentNode, "Toolbar should no longer be in the DOM.");
+  cuiAreaType = CustomizableUI.getAreaType(toolbarDOMID);
+  is(cuiAreaType, null, "CustomizableUI should have forgotten all about the area");
+});
+
+
+add_task(function*() {
+  let newWin = yield openAndLoadWindow({}, true);
+  ok(!newWin.gNavToolbox.toolbarset.hasAttribute("toolbar1"), "New window shouldn't have attribute toolbar1");
+  ok(!newWin.gNavToolbox.toolbarset.hasAttribute("toolbar2"), "New window shouldn't have attribute toolbar2");
+  yield promiseWindowClosed(newWin);
+});
diff --git a/browser/components/customizableui/test/browser_976792_insertNodeInWindow.js b/browser/components/customizableui/test/browser_976792_insertNodeInWindow.js
new file mode 100644
index 000000000..3bfa8c25d
--- /dev/null
+++ b/browser/components/customizableui/test/browser_976792_insertNodeInWindow.js
@@ -0,0 +1,414 @@
+/* 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-insertNodeInWindow-placements-toolbar";
+const kTestWidgetPrefix = "test-widget-for-insertNodeInWindow-placements-";
+
+
+/*
+Tries to replicate the situation of having a placement list like this:
+
+exists-1,trying-to-insert-this,doesn't-exist,exists-2
+*/
+add_task(function*() {
+  let testWidgetExists = [true, false, false, true];
+  let widgetIds = [];
+  for (let i = 0; i < testWidgetExists.length; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    if (testWidgetExists[i]) {
+      let spec = {id: id, type: "button", removable: true, label: "test", tooltiptext: "" + i};
+      CustomizableUI.createWidget(spec);
+    }
+  }
+
+  let toolbarNode = createToolbarWithPlacements(kToolbarName, widgetIds);
+  assertAreaPlacements(kToolbarName, widgetIds);
+
+  let btnId = kTestWidgetPrefix + 1;
+  let btn = createDummyXULButton(btnId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(btnId, window);
+
+  is(btn.parentNode.id, kToolbarName, "New XUL widget should be placed inside new toolbar");
+
+  is(btn.previousSibling.id, toolbarNode.firstChild.id,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  btn.remove();
+  removeCustomToolbars();
+  yield resetCustomization();
+});
+
+
+/*
+Tests nodes get placed inside the toolbar's overflow as expected. Replicates a
+situation similar to:
+
+exists-1,exists-2,overflow-1,trying-to-insert-this,overflow-2
+*/
+add_task(function*() {
+  let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+  let widgetIds = [];
+  for (let i = 0; i < 5; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    let spec = {id: id, type: "button", removable: true, label: "insertNodeInWindow test", tooltiptext: "" + i};
+    CustomizableUI.createWidget(spec);
+    CustomizableUI.addWidgetToArea(id, "nav-bar");
+  }
+
+  for (let id of widgetIds) {
+    document.getElementById(id).style.minWidth = "200px";
+  }
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+
+  let testWidgetId = kTestWidgetPrefix + 3;
+
+  CustomizableUI.destroyWidget(testWidgetId);
+
+  let btn = createDummyXULButton(testWidgetId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(testWidgetId, window);
+
+  is(btn.parentNode.id, navbar.overflowable._list.id, "New XUL widget should be placed inside overflow of toolbar");
+  is(btn.previousSibling.id, kTestWidgetPrefix + 2,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+  is(btn.nextSibling.id, kTestWidgetPrefix + 4,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  CustomizableUI.removeWidgetFromArea(btn.id, kToolbarName);
+  btn.remove();
+  yield resetCustomization();
+  yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+});
+
+
+/*
+Tests nodes get placed inside the toolbar's overflow as expected. Replicates a
+placements situation similar to:
+
+exists-1,exists-2,overflow-1,doesn't-exist,trying-to-insert-this,overflow-2
+*/
+add_task(function*() {
+  let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+  let widgetIds = [];
+  for (let i = 0; i < 5; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    let spec = {id: id, type: "button", removable: true, label: "insertNodeInWindow test", tooltiptext: "" + i};
+    CustomizableUI.createWidget(spec);
+    CustomizableUI.addWidgetToArea(id, "nav-bar");
+  }
+
+  for (let id of widgetIds) {
+    document.getElementById(id).style.minWidth = "200px";
+  }
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+
+  let testWidgetId = kTestWidgetPrefix + 3;
+
+  CustomizableUI.destroyWidget(kTestWidgetPrefix + 2);
+  CustomizableUI.destroyWidget(testWidgetId);
+
+  let btn = createDummyXULButton(testWidgetId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(testWidgetId, window);
+
+  is(btn.parentNode.id, navbar.overflowable._list.id, "New XUL widget should be placed inside overflow of toolbar");
+  is(btn.previousSibling.id, kTestWidgetPrefix + 1,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+  is(btn.nextSibling.id, kTestWidgetPrefix + 4,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  CustomizableUI.removeWidgetFromArea(btn.id, kToolbarName);
+  btn.remove();
+  yield resetCustomization();
+  yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+});
+
+
+/*
+Tests nodes get placed inside the toolbar's overflow as expected. Replicates a
+placements situation similar to:
+
+exists-1,exists-2,overflow-1,doesn't-exist,trying-to-insert-this,doesn't-exist
+*/
+add_task(function*() {
+  let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+  let widgetIds = [];
+  for (let i = 0; i < 5; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    let spec = {id: id, type: "button", removable: true, label: "insertNodeInWindow test", tooltiptext: "" + i};
+    CustomizableUI.createWidget(spec);
+    CustomizableUI.addWidgetToArea(id, "nav-bar");
+  }
+
+  for (let id of widgetIds) {
+    document.getElementById(id).style.minWidth = "200px";
+  }
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+
+  let testWidgetId = kTestWidgetPrefix + 3;
+
+  CustomizableUI.destroyWidget(kTestWidgetPrefix + 2);
+  CustomizableUI.destroyWidget(testWidgetId);
+  CustomizableUI.destroyWidget(kTestWidgetPrefix + 4);
+
+  let btn = createDummyXULButton(testWidgetId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(testWidgetId, window);
+
+  is(btn.parentNode.id, navbar.overflowable._list.id, "New XUL widget should be placed inside overflow of toolbar");
+  is(btn.previousSibling.id, kTestWidgetPrefix + 1,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+  is(btn.nextSibling, null,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  CustomizableUI.removeWidgetFromArea(btn.id, kToolbarName);
+  btn.remove();
+  yield resetCustomization();
+  yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+});
+
+
+/*
+Tests nodes get placed inside the toolbar's overflow as expected. Replicates a
+placements situation similar to:
+
+exists-1,exists-2,overflow-1,can't-overflow,trying-to-insert-this,overflow-2
+*/
+add_task(function*() {
+  let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+  let widgetIds = [];
+  for (let i = 5; i >= 0; i--) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    let spec = {id: id, type: "button", removable: true, label: "insertNodeInWindow test", tooltiptext: "" + i};
+    CustomizableUI.createWidget(spec);
+    CustomizableUI.addWidgetToArea(id, "nav-bar", 0);
+  }
+
+  for (let i = 10; i < 15; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    let spec = {id: id, type: "button", removable: true, label: "insertNodeInWindow test", tooltiptext: "" + i};
+    CustomizableUI.createWidget(spec);
+    CustomizableUI.addWidgetToArea(id, "nav-bar");
+  }
+
+  for (let id of widgetIds) {
+    document.getElementById(id).style.minWidth = "200px";
+  }
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+
+  // Find last widget that doesn't allow overflowing
+  let nonOverflowing = navbar.customizationTarget.lastChild;
+  is(nonOverflowing.getAttribute("overflows"), "false", "Last child is expected to not allow overflowing");
+  isnot(nonOverflowing.getAttribute("skipintoolbarset"), "true", "Last child is expected to not be skipintoolbarset");
+
+  let testWidgetId = kTestWidgetPrefix + 10;
+  CustomizableUI.destroyWidget(testWidgetId);
+
+  let btn = createDummyXULButton(testWidgetId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(testWidgetId, window);
+
+  is(btn.parentNode.id, navbar.overflowable._list.id, "New XUL widget should be placed inside overflow of toolbar");
+  is(btn.nextSibling.id, kTestWidgetPrefix + 11,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  CustomizableUI.removeWidgetFromArea(btn.id, kToolbarName);
+  btn.remove();
+  yield resetCustomization();
+  yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
+});
+
+
+/*
+Tests nodes get placed inside the toolbar's overflow as expected. Replicates a
+placements situation similar to:
+
+exists-1,exists-2,overflow-1,trying-to-insert-this,can't-overflow,overflow-2
+*/
+add_task(function*() {
+  let widgetIds = [];
+  let missingId = 2;
+  let nonOverflowableId = 3;
+  for (let i = 0; i < 5; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    if (i != missingId) {
+      // Setting min-width to make the overflow state not depend on styling of the button and/or
+      // screen width
+      let spec = {id: id, type: "button", removable: true, label: "test", tooltiptext: "" + i,
+                  onCreated: function(node) {
+                    node.style.minWidth = "200px";
+                    if (id == (kTestWidgetPrefix + nonOverflowableId)) {
+                      node.setAttribute("overflows", false);
+                    }
+                 }};
+      info("Creating: " + id);
+      CustomizableUI.createWidget(spec);
+    }
+  }
+
+  let toolbarNode = createOverflowableToolbarWithPlacements(kToolbarName, widgetIds);
+  assertAreaPlacements(kToolbarName, widgetIds);
+  ok(!toolbarNode.hasAttribute("overflowing"), "Toolbar shouldn't overflow to start with.");
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => toolbarNode.hasAttribute("overflowing"));
+  ok(toolbarNode.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+
+  let btnId = kTestWidgetPrefix + missingId;
+  let btn = createDummyXULButton(btnId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(btnId, window);
+
+  is(btn.parentNode.id, kToolbarName + "-overflow-list", "New XUL widget should be placed inside new toolbar's overflow");
+  is(btn.previousSibling.id, kTestWidgetPrefix + 1,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+  is(btn.nextSibling.id, kTestWidgetPrefix + 4,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+  yield waitForCondition(() => !toolbarNode.hasAttribute("overflowing"));
+
+  btn.remove();
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  removeCustomToolbars();
+  yield resetCustomization();
+});
+
+
+/*
+Tests nodes do *not* get placed in the toolbar's overflow. Replicates a
+plcements situation similar to:
+
+exists-1,trying-to-insert-this,exists-2,overflowed-1
+*/
+add_task(function*() {
+  let widgetIds = [];
+  let missingId = 1;
+  for (let i = 0; i < 5; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    if (i != missingId) {
+      // Setting min-width to make the overflow state not depend on styling of the button and/or
+      // screen width
+      let spec = {id: id, type: "button", removable: true, label: "test", tooltiptext: "" + i,
+                  onCreated: function(node) { node.style.minWidth = "100px"; }};
+      info("Creating: " + id);
+      CustomizableUI.createWidget(spec);
+    }
+  }
+
+  let toolbarNode = createOverflowableToolbarWithPlacements(kToolbarName, widgetIds);
+  assertAreaPlacements(kToolbarName, widgetIds);
+  ok(!toolbarNode.hasAttribute("overflowing"), "Toolbar shouldn't overflow to start with.");
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => toolbarNode.hasAttribute("overflowing"));
+  ok(toolbarNode.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+
+  let btnId = kTestWidgetPrefix + missingId;
+  let btn = createDummyXULButton(btnId, "test");
+  CustomizableUI.ensureWidgetPlacedInWindow(btnId, window);
+
+  is(btn.parentNode.id, kToolbarName + "-target", "New XUL widget should be placed inside new toolbar");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+  yield waitForCondition(() => !toolbarNode.hasAttribute("overflowing"));
+
+  btn.remove();
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  removeCustomToolbars();
+  yield resetCustomization();
+});
+
+
+/*
+Tests inserting a node onto the end of an overflowing toolbar *doesn't* put it in
+the overflow list when the widget disallows overflowing. ie:
+
+exists-1,exists-2,overflows-1,trying-to-insert-this
+
+Where trying-to-insert-this has overflows=false
+*/
+add_task(function*() {
+  let widgetIds = [];
+  let missingId = 3;
+  for (let i = 0; i < 5; i++) {
+    let id = kTestWidgetPrefix + i;
+    widgetIds.push(id);
+    if (i != missingId) {
+      // Setting min-width to make the overflow state not depend on styling of the button and/or
+      // screen width
+      let spec = {id: id, type: "button", removable: true, label: "test", tooltiptext: "" + i,
+                  onCreated: function(node) { node.style.minWidth = "200px"; }};
+      info("Creating: " + id);
+      CustomizableUI.createWidget(spec);
+    }
+  }
+
+  let toolbarNode = createOverflowableToolbarWithPlacements(kToolbarName, widgetIds);
+  assertAreaPlacements(kToolbarName, widgetIds);
+  ok(!toolbarNode.hasAttribute("overflowing"), "Toolbar shouldn't overflow to start with.");
+
+  let originalWindowWidth = window.outerWidth;
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => toolbarNode.hasAttribute("overflowing"));
+  ok(toolbarNode.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+
+  let btnId = kTestWidgetPrefix + missingId;
+  let btn = createDummyXULButton(btnId, "test");
+  btn.setAttribute("overflows", false);
+  CustomizableUI.ensureWidgetPlacedInWindow(btnId, window);
+
+  is(btn.parentNode.id, kToolbarName + "-target", "New XUL widget should be placed inside new toolbar");
+  is(btn.nextSibling, null,
+     "insertNodeInWindow should have placed new XUL widget in correct place in DOM according to placements");
+
+  window.resizeTo(originalWindowWidth, window.outerHeight);
+  yield waitForCondition(() => !toolbarNode.hasAttribute("overflowing"));
+
+  btn.remove();
+  widgetIds.forEach(id => CustomizableUI.destroyWidget(id));
+  removeCustomToolbars();
+  yield resetCustomization();
+});
+
+
+add_task(function* asyncCleanUp() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_978084_dragEnd_after_move.js b/browser/components/customizableui/test/browser_978084_dragEnd_after_move.js
new file mode 100644
index 000000000..a653c2d51
--- /dev/null
+++ b/browser/components/customizableui/test/browser_978084_dragEnd_after_move.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+var draggedItem;
+
+/**
+ * Check that customizing-movingItem gets removed on a drop when the item is moved.
+ */
+
+// Drop on the palette
+add_task(function*() {
+  draggedItem = document.createElement("toolbarbutton");
+  draggedItem.id = "test-dragEnd-after-move1";
+  draggedItem.setAttribute("label", "Test");
+  draggedItem.setAttribute("removable", "true");
+  let navbar = document.getElementById("nav-bar");
+  navbar.customizationTarget.appendChild(draggedItem);
+  yield startCustomizing();
+  simulateItemDrag(draggedItem, gCustomizeMode.visiblePalette);
+  is(document.documentElement.hasAttribute("customizing-movingItem"), false,
+     "Make sure customizing-movingItem is removed after dragging to the palette");
+  yield endCustomizing();
+});
+
+// Drop on a customization target itself
+add_task(function*() {
+  draggedItem = document.createElement("toolbarbutton");
+  draggedItem.id = "test-dragEnd-after-move2";
+  draggedItem.setAttribute("label", "Test");
+  draggedItem.setAttribute("removable", "true");
+  let dest = createToolbarWithPlacements("test-dragEnd");
+  let navbar = document.getElementById("nav-bar");
+  navbar.customizationTarget.appendChild(draggedItem);
+  yield startCustomizing();
+  simulateItemDrag(draggedItem, dest.customizationTarget);
+  is(document.documentElement.hasAttribute("customizing-movingItem"), false,
+     "Make sure customizing-movingItem is removed");
+  yield endCustomizing();
+});
+
+add_task(function* asyncCleanup() {
+  yield endCustomizing();
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_980155_add_overflow_toolbar.js b/browser/components/customizableui/test/browser_980155_add_overflow_toolbar.js
new file mode 100644
index 000000000..15197ac86
--- /dev/null
+++ b/browser/components/customizableui/test/browser_980155_add_overflow_toolbar.js
@@ -0,0 +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();
+});
diff --git a/browser/components/customizableui/test/browser_981305_separator_insertion.js b/browser/components/customizableui/test/browser_981305_separator_insertion.js
new file mode 100644
index 000000000..8d4d86c2a
--- /dev/null
+++ b/browser/components/customizableui/test/browser_981305_separator_insertion.js
@@ -0,0 +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";
+
+var 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;
+});
diff --git a/browser/components/customizableui/test/browser_981418-widget-onbeforecreated-handler.js b/browser/components/customizableui/test/browser_981418-widget-onbeforecreated-handler.js
new file mode 100644
index 000000000..9a7227a47
--- /dev/null
+++ b/browser/components/customizableui/test/browser_981418-widget-onbeforecreated-handler.js
@@ -0,0 +1,93 @@
+/* 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 kWidgetId = 'test-981418-widget-onbeforecreated';
+
+// Should be able to add broken view widget
+add_task(function* testAddOnBeforeCreatedWidget() {
+  let viewShownDeferred = Promise.defer();
+  let onBeforeCreatedCalled = false;
+  let widgetSpec = {
+    id: kWidgetId,
+    type: 'view',
+    viewId: kWidgetId + 'idontexistyet',
+    onBeforeCreated: function(doc) {
+      let view = doc.createElement("panelview");
+      view.id = kWidgetId + 'idontexistyet';
+      let label = doc.createElement("label");
+      label.setAttribute("value", "Hello world");
+      label.className = 'panel-subview-header';
+      view.appendChild(label);
+      document.getElementById("PanelUI-multiView").appendChild(view);
+      onBeforeCreatedCalled = true;
+    },
+    onViewShowing: function() {
+      viewShownDeferred.resolve();
+    }
+  };
+
+  let noError = true;
+  try {
+    CustomizableUI.createWidget(widgetSpec);
+    CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_NAVBAR);
+  } catch (ex) {
+    Cu.reportError(ex);
+    noError = false;
+  }
+  ok(noError, "Should not throw an exception trying to add the widget.");
+  ok(onBeforeCreatedCalled, "onBeforeCreated should have been called");
+
+  let widgetNode = document.getElementById(kWidgetId);
+  ok(widgetNode, "Widget should exist");
+  if (widgetNode) {
+    try {
+      widgetNode.click();
+
+      let tempPanel = document.getElementById("customizationui-widget-panel");
+      let panelShownPromise = promisePanelElementShown(window, tempPanel);
+
+      let shownTimeout = setTimeout(() => viewShownDeferred.reject("Panel not shown within 20s"), 20000);
+      yield viewShownDeferred.promise;
+      yield panelShownPromise;
+      clearTimeout(shownTimeout);
+      ok(true, "Found view shown");
+
+      let panelHiddenPromise = promisePanelElementHidden(window, tempPanel);
+      tempPanel.hidePopup();
+      yield panelHiddenPromise;
+
+      CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_PANEL);
+      yield PanelUI.show();
+
+      viewShownDeferred = Promise.defer();
+      widgetNode.click();
+
+      shownTimeout = setTimeout(() => viewShownDeferred.reject("Panel not shown within 20s"), 20000);
+      yield viewShownDeferred.promise;
+      clearTimeout(shownTimeout);
+      ok(true, "Found view shown");
+
+      let panelHidden = promisePanelHidden(window);
+      PanelUI.hide();
+      yield panelHidden;
+    } catch (ex) {
+      ok(false, "Unexpected exception (like a timeout for one of the yields) " +
+                "when testing view widget.");
+    }
+  }
+
+  noError = true;
+  try {
+    CustomizableUI.destroyWidget(kWidgetId);
+  } catch (ex) {
+    Cu.reportError(ex);
+    noError = false;
+  }
+  ok(noError, "Should not throw an exception trying to remove the broken view widget.");
+});
+
+add_task(function* asyncCleanup() {
+  yield resetCustomization();
+});
diff --git a/browser/components/customizableui/test/browser_982656_restore_defaults_builtin_widgets.js b/browser/components/customizableui/test/browser_982656_restore_defaults_builtin_widgets.js
new file mode 100644
index 000000000..e7f8d0cf4
--- /dev/null
+++ b/browser/components/customizableui/test/browser_982656_restore_defaults_builtin_widgets.js
@@ -0,0 +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);
+});
diff --git a/browser/components/customizableui/test/browser_984455_bookmarks_items_reparenting.js b/browser/components/customizableui/test/browser_984455_bookmarks_items_reparenting.js
new file mode 100644
index 000000000..42b346c10
--- /dev/null
+++ b/browser/components/customizableui/test/browser_984455_bookmarks_items_reparenting.js
@@ -0,0 +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";
+
+var gNavBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+var 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 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;
+      }
+      return false;
+    });
+    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();
+});
diff --git a/browser/components/customizableui/test/browser_985815_propagate_setToolbarVisibility.js b/browser/components/customizableui/test/browser_985815_propagate_setToolbarVisibility.js
new file mode 100644
index 000000000..c341c2158
--- /dev/null
+++ b/browser/components/customizableui/test/browser_985815_propagate_setToolbarVisibility.js
@@ -0,0 +1,45 @@
+/* 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*() {
+  ok(CustomizableUI.inDefaultState, "Should start in default state.");
+  this.otherWin = yield openAndLoadWindow({private: true}, true);
+  yield startCustomizing(this.otherWin);
+  let resetButton = this.otherWin.document.getElementById("customization-reset-button");
+  ok(resetButton.disabled, "Reset button should be disabled");
+
+  if (typeof CustomizableUI.setToolbarVisibility == "function") {
+    CustomizableUI.setToolbarVisibility("PersonalToolbar", true);
+  } else {
+    setToolbarVisibility(document.getElementById("PersonalToolbar"), true);
+  }
+
+  let otherPersonalToolbar = this.otherWin.document.getElementById("PersonalToolbar");
+  let personalToolbar = document.getElementById("PersonalToolbar");
+  ok(!otherPersonalToolbar.collapsed, "Toolbar should be uncollapsed in private window");
+  ok(!personalToolbar.collapsed, "Toolbar should be uncollapsed in normal window");
+  ok(!resetButton.disabled, "Reset button should be enabled");
+
+  yield this.otherWin.gCustomizeMode.reset();
+
+  ok(otherPersonalToolbar.collapsed, "Toolbar should be collapsed in private window");
+  ok(personalToolbar.collapsed, "Toolbar should be collapsed in normal window");
+  ok(resetButton.disabled, "Reset button should be disabled");
+
+  yield endCustomizing(this.otherWin);
+
+  yield promiseWindowClosed(this.otherWin);
+});
+
+
+add_task(function* asyncCleanup() {
+  if (this.otherWin && !this.otherWin.closed) {
+    yield promiseWindowClosed(this.otherWin);
+  }
+  if (!CustomizableUI.inDefaultState) {
+    CustomizableUI.reset();
+  }
+});
diff --git a/browser/components/customizableui/test/browser_987177_destroyWidget_xul.js b/browser/components/customizableui/test/browser_987177_destroyWidget_xul.js
new file mode 100644
index 000000000..6a4d0aab4
--- /dev/null
+++ b/browser/components/customizableui/test/browser_987177_destroyWidget_xul.js
@@ -0,0 +1,33 @@
+/* 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 BUTTONID = "test-XUL-wrapper-destroyWidget";
+
+
+add_task(function() {
+  let btn = createDummyXULButton(BUTTONID, "XUL btn");
+  gNavToolbox.palette.appendChild(btn);
+  let firstWrapper = CustomizableUI.getWidget(BUTTONID).forWindow(window);
+  ok(firstWrapper, "Should get a wrapper");
+  ok(firstWrapper.node, "Node should be there on first wrapper.");
+
+  btn.remove();
+  CustomizableUI.destroyWidget(BUTTONID);
+  let secondWrapper = CustomizableUI.getWidget(BUTTONID).forWindow(window);
+  isnot(firstWrapper, secondWrapper, "Wrappers should be different after destroyWidget call.");
+  ok(!firstWrapper.node, "No node should be there on old wrapper.");
+  ok(!secondWrapper.node, "No node should be there on new wrapper.");
+
+  btn = createDummyXULButton(BUTTONID, "XUL btn");
+  gNavToolbox.palette.appendChild(btn);
+  let thirdWrapper = CustomizableUI.getWidget(BUTTONID).forWindow(window);
+  ok(thirdWrapper, "Should get a wrapper");
+  is(secondWrapper, thirdWrapper, "Should get the second wrapper again.");
+  ok(firstWrapper.node, "Node should be there on old wrapper.");
+  ok(secondWrapper.node, "Node should be there on second wrapper.");
+  ok(thirdWrapper.node, "Node should be there on third wrapper.");
+});
+
diff --git a/browser/components/customizableui/test/browser_987177_xul_wrapper_updating.js b/browser/components/customizableui/test/browser_987177_xul_wrapper_updating.js
new file mode 100644
index 000000000..f838e204d
--- /dev/null
+++ b/browser/components/customizableui/test/browser_987177_xul_wrapper_updating.js
@@ -0,0 +1,74 @@
+/* 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 BUTTONID = "test-XUL-wrapper-widget";
+add_task(function() {
+  let btn = createDummyXULButton(BUTTONID, "XUL btn");
+  gNavToolbox.palette.appendChild(btn);
+  let groupWrapper = CustomizableUI.getWidget(BUTTONID);
+  ok(groupWrapper, "Should get a group wrapper");
+  let singleWrapper = groupWrapper.forWindow(window);
+  ok(singleWrapper, "Should get a single wrapper");
+  is(singleWrapper.node, btn, "Node should be in the wrapper");
+  is(groupWrapper.instances.length, 1, "There should be 1 instance on the group wrapper");
+  is(groupWrapper.instances[0].node, btn, "Button should be that instance.");
+
+  CustomizableUI.addWidgetToArea(BUTTONID, CustomizableUI.AREA_NAVBAR);
+
+  let otherSingleWrapper = groupWrapper.forWindow(window);
+  is(singleWrapper, otherSingleWrapper, "Should get the same wrapper after adding the node to the navbar.");
+  is(singleWrapper.node, btn, "Node should be in the wrapper");
+  is(groupWrapper.instances.length, 1, "There should be 1 instance on the group wrapper");
+  is(groupWrapper.instances[0].node, btn, "Button should be that instance.");
+
+  CustomizableUI.removeWidgetFromArea(BUTTONID);
+
+  otherSingleWrapper = groupWrapper.forWindow(window);
+  isnot(singleWrapper, otherSingleWrapper, "Shouldn't get the same wrapper after removing it from the navbar.");
+  singleWrapper = otherSingleWrapper;
+  is(singleWrapper.node, btn, "Node should be in the wrapper");
+  is(groupWrapper.instances.length, 1, "There should be 1 instance on the group wrapper");
+  is(groupWrapper.instances[0].node, btn, "Button should be that instance.");
+
+  btn.remove();
+  otherSingleWrapper = groupWrapper.forWindow(window);
+  is(singleWrapper, otherSingleWrapper, "Should get the same wrapper after physically removing the node.");
+  is(singleWrapper.node, null, "Wrapper's node should be null now that it's left the DOM.");
+  is(groupWrapper.instances.length, 1, "There should be 1 instance on the group wrapper");
+  is(groupWrapper.instances[0].node, null, "That instance should be null.");
+
+  btn = createDummyXULButton(BUTTONID, "XUL btn");
+  gNavToolbox.palette.appendChild(btn);
+  otherSingleWrapper = groupWrapper.forWindow(window);
+  is(singleWrapper, otherSingleWrapper, "Should get the same wrapper after readding the node.");
+  is(singleWrapper.node, btn, "Node should be in the wrapper");
+  is(groupWrapper.instances.length, 1, "There should be 1 instance on the group wrapper");
+  is(groupWrapper.instances[0].node, btn, "Button should be that instance.");
+
+  CustomizableUI.addWidgetToArea(BUTTONID, CustomizableUI.AREA_NAVBAR);
+
+  otherSingleWrapper = groupWrapper.forWindow(window);
+  is(singleWrapper, otherSingleWrapper, "Should get the same wrapper after adding the node to the navbar.");
+  is(singleWrapper.node, btn, "Node should be in the wrapper");
+  is(groupWrapper.instances.length, 1, "There should be 1 instance on the group wrapper");
+  is(groupWrapper.instances[0].node, btn, "Button should be that instance.");
+
+  CustomizableUI.removeWidgetFromArea(BUTTONID);
+
+  otherSingleWrapper = groupWrapper.forWindow(window);
+  isnot(singleWrapper, otherSingleWrapper, "Shouldn't get the same wrapper after removing it from the navbar.");
+  singleWrapper = otherSingleWrapper;
+  is(singleWrapper.node, btn, "Node should be in the wrapper");
+  is(groupWrapper.instances.length, 1, "There should be 1 instance on the group wrapper");
+  is(groupWrapper.instances[0].node, btn, "Button should be that instance.");
+
+  btn.remove();
+  otherSingleWrapper = groupWrapper.forWindow(window);
+  is(singleWrapper, otherSingleWrapper, "Should get the same wrapper after physically removing the node.");
+  is(singleWrapper.node, null, "Wrapper's node should be null now that it's left the DOM.");
+  is(groupWrapper.instances.length, 1, "There should be 1 instance on the group wrapper");
+  is(groupWrapper.instances[0].node, null, "That instance should be null.");
+});
diff --git a/browser/components/customizableui/test/browser_987185_syncButton.js b/browser/components/customizableui/test/browser_987185_syncButton.js
new file mode 100755
index 000000000..988d738be
--- /dev/null
+++ b/browser/components/customizableui/test/browser_987185_syncButton.js
@@ -0,0 +1,77 @@
+/* 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";
+
+var syncService = {};
+Components.utils.import("resource://services-sync/service.js", syncService);
+
+var needsSetup;
+var originalSync;
+var service = syncService.Service;
+var syncWasCalled = false;
+
+add_task(function* testSyncButtonFunctionality() {
+  info("Check Sync button functionality");
+  storeInitialValues();
+  mockFunctions();
+
+  // add the Sync button to the panel
+  CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
+
+  // check the button's functionality
+  yield PanelUI.show();
+  info("The panel menu was opened");
+
+  let syncButton = document.getElementById("sync-button");
+  ok(syncButton, "The Sync button was added to the Panel Menu");
+  // click the button - the panel should open.
+  syncButton.click();
+  let syncPanel = document.getElementById("PanelUI-remotetabs");
+  ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
+
+  // Find and click the "setup" button.
+  let syncNowButton = document.getElementById("PanelUI-remotetabs-syncnow");
+  syncNowButton.click();
+
+  info("The sync button was clicked");
+
+  yield waitForCondition(() => syncWasCalled);
+});
+
+add_task(function* asyncCleanup() {
+  // reset the panel UI to the default state
+  yield resetCustomization();
+  ok(CustomizableUI.inDefaultState, "The panel UI is in default state again.");
+
+  if (isPanelUIOpen()) {
+    let panelHidePromise = promisePanelHidden(window);
+    PanelUI.hide();
+    yield panelHidePromise;
+  }
+
+  restoreValues();
+});
+
+function mockFunctions() {
+  // mock needsSetup
+  gSyncUI._needsSetup = () => Promise.resolve(false);
+
+  // mock service.errorHandler.syncAndReportErrors()
+  service.errorHandler.syncAndReportErrors = mocked_syncAndReportErrors;
+}
+
+function mocked_syncAndReportErrors() {
+  syncWasCalled = true;
+}
+
+function restoreValues() {
+  gSyncUI._needsSetup = needsSetup;
+  service.sync = originalSync;
+}
+
+function storeInitialValues() {
+  needsSetup = gSyncUI._needsSetup;
+  originalSync = service.sync;
+}
diff --git a/browser/components/customizableui/test/browser_987492_window_api.js b/browser/components/customizableui/test/browser_987492_window_api.js
new file mode 100644
index 000000000..1718303e1
--- /dev/null
+++ b/browser/components/customizableui/test/browser_987492_window_api.js
@@ -0,0 +1,54 @@
+/* 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* testOneWindow() {
+  let windows = [];
+  for (let win of CustomizableUI.windows)
+    windows.push(win);
+  is(windows.length, 1, "Should have one customizable window");
+});
+
+
+add_task(function* testOpenCloseWindow() {
+  let newWindow = null;
+  let openListener = {
+    onWindowOpened: function(window) {
+      newWindow = window;
+    }
+  }
+  CustomizableUI.addListener(openListener);
+  let win = yield openAndLoadWindow(null, true);
+  isnot(newWindow, null, "Should have gotten onWindowOpen event");
+  is(newWindow, win, "onWindowOpen event should have received expected window");
+  CustomizableUI.removeListener(openListener);
+
+  let windows = [];
+  for (let win of CustomizableUI.windows)
+    windows.push(win);
+  is(windows.length, 2, "Should have two customizable windows");
+  isnot(windows.indexOf(window), -1, "Current window should be in window collection.");
+  isnot(windows.indexOf(newWindow), -1, "New window should be in window collection.");
+
+  let closedWindow = null;
+  let closeListener = {
+    onWindowClosed: function(window) {
+      closedWindow = window;
+    }
+  }
+  CustomizableUI.addListener(closeListener);
+  yield promiseWindowClosed(newWindow);
+  isnot(closedWindow, null, "Should have gotten onWindowClosed event")
+  is(newWindow, closedWindow, "Closed window should match previously opened window");
+  CustomizableUI.removeListener(closeListener);
+
+  windows = [];
+  for (let win of CustomizableUI.windows)
+    windows.push(win);
+  is(windows.length, 1, "Should have one customizable window");
+  isnot(windows.indexOf(window), -1, "Current window should be in window collection.");
+  is(windows.indexOf(closedWindow), -1, "Closed window should not be in window collection.");
+});
diff --git a/browser/components/customizableui/test/browser_987640_charEncoding.js b/browser/components/customizableui/test/browser_987640_charEncoding.js
new file mode 100644
index 000000000..dfe02f940
--- /dev/null
+++ b/browser/components/customizableui/test/browser_987640_charEncoding.js
@@ -0,0 +1,60 @@
+/* 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 TEST_PAGE = "http://mochi.test:8888/browser/browser/components/customizableui/test/support/test_967000_charEncoding_page.html";
+
+add_task(function*() {
+  info("Check Character Encoding panel functionality");
+
+  // add the Character Encoding button to the panel
+  CustomizableUI.addWidgetToArea("characterencoding-button",
+                                  CustomizableUI.AREA_PANEL);
+
+  let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE, true, true);
+
+  yield PanelUI.show();
+  let charEncodingButton = document.getElementById("characterencoding-button");
+  let characterEncodingView = document.getElementById("PanelUI-characterEncodingView");
+  let subviewShownPromise = subviewShown(characterEncodingView);
+  charEncodingButton.click();
+  yield subviewShownPromise;
+
+  let checkedButtons = characterEncodingView.querySelectorAll("toolbarbutton[checked='true']");
+  let initialEncoding = checkedButtons[0];
+  is(initialEncoding.getAttribute("label"), "Unicode", "The unicode encoding is initially selected");
+
+  // change the encoding
+  let encodings = characterEncodingView.querySelectorAll("toolbarbutton");
+  let newEncoding = encodings[0].hasAttribute("checked") ? encodings[1] : encodings[0];
+  let tabLoadPromise = promiseTabLoadEvent(gBrowser.selectedTab, TEST_PAGE);
+  newEncoding.click();
+  yield tabLoadPromise;
+
+  // check that the new encodng is applied
+  yield PanelUI.show();
+  charEncodingButton.click();
+  checkedButtons = characterEncodingView.querySelectorAll("toolbarbutton[checked='true']");
+  let selectedEncodingName = checkedButtons[0].getAttribute("label");
+  ok(selectedEncodingName != "Unicode", "The encoding was changed to " + selectedEncodingName);
+
+  // reset the initial encoding
+  yield PanelUI.show();
+  charEncodingButton.click();
+  tabLoadPromise = promiseTabLoadEvent(gBrowser.selectedTab, TEST_PAGE);
+  initialEncoding.click();
+  yield tabLoadPromise;
+  yield PanelUI.show();
+  charEncodingButton.click();
+  checkedButtons = characterEncodingView.querySelectorAll("toolbarbutton[checked='true']");
+  is(checkedButtons[0].getAttribute("label"), "Unicode", "The encoding was reset to Unicode");
+  yield BrowserTestUtils.removeTab(newTab);
+});
+
+add_task(function* asyncCleanup() {
+  // reset the panel to the default state
+  yield resetCustomization();
+  ok(CustomizableUI.inDefaultState, "The UI is in default state again.");
+});
diff --git a/browser/components/customizableui/test/browser_988072_sidebar_events.js b/browser/components/customizableui/test/browser_988072_sidebar_events.js
new file mode 100644
index 000000000..6791be67a
--- /dev/null
+++ b/browser/components/customizableui/test/browser_988072_sidebar_events.js
@@ -0,0 +1,392 @@
+/* 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";
+
+var gSidebarMenu = document.getElementById("viewSidebarMenu");
+var gTestSidebarItem = null;
+
+var EVENTS = {
+  click: 0, command: 0,
+  onclick: 0, oncommand: 0
+};
+
+window.sawEvent = function(event, isattr) {
+  let type = (isattr ? "on" : "") + event.type
+  EVENTS[type]++;
+};
+
+registerCleanupFunction(() => {
+  delete window.sawEvent;
+
+  // Ensure sidebar is hidden after each test:
+  if (!document.getElementById("sidebar-box").hidden) {
+    SidebarUI.hide();
+  }
+});
+
+function checkExpectedEvents(expected) {
+  for (let type of Object.keys(EVENTS)) {
+    let count = (type in expected ? expected[type] : 0);
+    is(EVENTS[type], count, "Should have seen the right number of " + type + " events");
+    EVENTS[type] = 0;
+  }
+}
+
+function createSidebarItem() {
+  gTestSidebarItem = document.createElement("menuitem");
+  gTestSidebarItem.id = "testsidebar";
+  gTestSidebarItem.setAttribute("label", "Test Sidebar");
+  gSidebarMenu.insertBefore(gTestSidebarItem, gSidebarMenu.firstChild);
+}
+
+function addWidget() {
+  CustomizableUI.addWidgetToArea("sidebar-button", "nav-bar");
+  PanelUI.disableSingleSubviewPanelAnimations();
+}
+
+function removeWidget() {
+  CustomizableUI.removeWidgetFromArea("sidebar-button");
+  PanelUI.enableSingleSubviewPanelAnimations();
+}
+
+// Filters out the trailing menuseparators from the sidebar list
+function getSidebarList() {
+  let sidebars = [...gSidebarMenu.children].filter(sidebar => {
+    if (sidebar.localName == "menuseparator")
+      return false;
+    if (sidebar.getAttribute("hidden") == "true")
+      return false;
+    return true;
+  });
+  return sidebars;
+}
+
+function compareElements(original, displayed) {
+  let attrs = ["label", "key", "disabled", "hidden", "origin", "image", "checked"];
+  for (let attr of attrs) {
+    is(displayed.getAttribute(attr), original.getAttribute(attr), "Should have the same " + attr + " attribute");
+  }
+}
+
+function compareList(original, displayed) {
+  is(displayed.length, original.length, "Should have the same number of children");
+
+  for (let i = 0; i < Math.min(original.length, displayed.length); i++) {
+    compareElements(displayed[i], original[i]);
+  }
+}
+
+var showSidebarPopup = Task.async(function*() {
+  let button = document.getElementById("sidebar-button");
+  let subview = document.getElementById("PanelUI-sidebar");
+
+  let popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown");
+
+  let subviewShownPromise = subviewShown(subview);
+  EventUtils.synthesizeMouseAtCenter(button, {});
+  return Promise.all([subviewShownPromise, popupShownPromise]);
+});
+
+// Check the sidebar widget shows the default items
+add_task(function*() {
+  addWidget();
+
+  yield showSidebarPopup();
+
+  let sidebars = getSidebarList();
+  let displayed = [...document.getElementById("PanelUI-sidebarItems").children];
+  compareList(sidebars, displayed);
+
+  let subview = document.getElementById("PanelUI-sidebar");
+  let subviewHiddenPromise = subviewHidden(subview);
+  document.getElementById("customizationui-widget-panel").hidePopup();
+  yield subviewHiddenPromise;
+
+  removeWidget();
+});
+
+function add_sidebar_task(description, setup, teardown) {
+  add_task(function*() {
+    info(description);
+    createSidebarItem();
+    addWidget();
+    yield setup();
+
+    CustomizableUI.addWidgetToArea("sidebar-button", "nav-bar");
+
+    yield showSidebarPopup();
+
+    let sidebars = getSidebarList();
+    let displayed = [...document.getElementById("PanelUI-sidebarItems").children];
+    compareList(sidebars, displayed);
+
+    is(displayed[0].label, "Test Sidebar", "Should have the right element at the top");
+    let subview = document.getElementById("PanelUI-sidebar");
+    let subviewHiddenPromise = subviewHidden(subview);
+    EventUtils.synthesizeMouseAtCenter(displayed[0], {});
+    yield subviewHiddenPromise;
+
+    yield teardown();
+    gTestSidebarItem.remove();
+    removeWidget();
+  });
+}
+
+add_sidebar_task(
+  "Check that a sidebar that uses a command event listener works",
+function*() {
+  gTestSidebarItem.addEventListener("command", window.sawEvent);
+}, function*() {
+  checkExpectedEvents({ command: 1 });
+});
+
+add_sidebar_task(
+  "Check that a sidebar that uses a click event listener works",
+function*() {
+  gTestSidebarItem.addEventListener("click", window.sawEvent);
+}, function*() {
+  checkExpectedEvents({ click: 1 });
+});
+
+add_sidebar_task(
+  "Check that a sidebar that uses both click and command event listeners works",
+function*() {
+  gTestSidebarItem.addEventListener("command", window.sawEvent);
+  gTestSidebarItem.addEventListener("click", window.sawEvent);
+}, function*() {
+  checkExpectedEvents({ command: 1, click: 1 });
+});
+
+add_sidebar_task(
+  "Check that a sidebar that uses an oncommand attribute works",
+function*() {
+  gTestSidebarItem.setAttribute("oncommand", "window.sawEvent(event, true)");
+}, function*() {
+  checkExpectedEvents({ oncommand: 1 });
+});
+
+add_sidebar_task(
+  "Check that a sidebar that uses an onclick attribute works",
+function*() {
+  gTestSidebarItem.setAttribute("onclick", "window.sawEvent(event, true)");
+}, function*() {
+  checkExpectedEvents({ onclick: 1 });
+});
+
+add_sidebar_task(
+  "Check that a sidebar that uses both onclick and oncommand attributes works",
+function*() {
+  gTestSidebarItem.setAttribute("onclick", "window.sawEvent(event, true)");
+  gTestSidebarItem.setAttribute("oncommand", "window.sawEvent(event, true)");
+}, function*() {
+  checkExpectedEvents({ onclick: 1, oncommand: 1 });
+});
+
+add_sidebar_task(
+  "Check that a sidebar that uses an onclick attribute and a command listener works",
+function*() {
+  gTestSidebarItem.setAttribute("onclick", "window.sawEvent(event, true)");
+  gTestSidebarItem.addEventListener("command", window.sawEvent);
+}, function*() {
+  checkExpectedEvents({ onclick: 1, command: 1 });
+});
+
+add_sidebar_task(
+  "Check that a sidebar that uses an oncommand attribute and a click listener works",
+function*() {
+  gTestSidebarItem.setAttribute("oncommand", "window.sawEvent(event, true)");
+  gTestSidebarItem.addEventListener("click", window.sawEvent);
+}, function*() {
+  checkExpectedEvents({ click: 1, oncommand: 1 });
+});
+
+add_sidebar_task(
+  "A sidebar with both onclick attribute and click listener sees only one event :(",
+function*() {
+  gTestSidebarItem.setAttribute("onclick", "window.sawEvent(event, true)");
+  gTestSidebarItem.addEventListener("click", window.sawEvent);
+}, function*() {
+  checkExpectedEvents({ onclick: 1 });
+});
+
+add_sidebar_task(
+  "A sidebar with both oncommand attribute and command listener sees only one event :(",
+function*() {
+  gTestSidebarItem.setAttribute("oncommand", "window.sawEvent(event, true)");
+  gTestSidebarItem.addEventListener("command", window.sawEvent);
+}, function*() {
+  checkExpectedEvents({ oncommand: 1 });
+});
+
+add_sidebar_task(
+  "Check that a sidebar that uses a broadcaster with an oncommand attribute works",
+function*() {
+  let broadcaster = document.createElement("broadcaster");
+  broadcaster.setAttribute("id", "testbroadcaster");
+  broadcaster.setAttribute("oncommand", "window.sawEvent(event, true)");
+  broadcaster.setAttribute("label", "Test Sidebar");
+  document.getElementById("mainBroadcasterSet").appendChild(broadcaster);
+
+  gTestSidebarItem.setAttribute("observes", "testbroadcaster");
+}, function*() {
+  checkExpectedEvents({ oncommand: 1 });
+  document.getElementById("testbroadcaster").remove();
+});
+
+add_sidebar_task(
+  "Check that a sidebar that uses a broadcaster with an onclick attribute works",
+function*() {
+  let broadcaster = document.createElement("broadcaster");
+  broadcaster.setAttribute("id", "testbroadcaster");
+  broadcaster.setAttribute("onclick", "window.sawEvent(event, true)");
+  broadcaster.setAttribute("label", "Test Sidebar");
+  document.getElementById("mainBroadcasterSet").appendChild(broadcaster);
+
+  gTestSidebarItem.setAttribute("observes", "testbroadcaster");
+}, function*() {
+  checkExpectedEvents({ onclick: 1 });
+  document.getElementById("testbroadcaster").remove();
+});
+
+add_sidebar_task(
+  "Check that a sidebar that uses a broadcaster with both onclick and oncommand attributes works",
+function*() {
+  let broadcaster = document.createElement("broadcaster");
+  broadcaster.setAttribute("id", "testbroadcaster");
+  broadcaster.setAttribute("onclick", "window.sawEvent(event, true)");
+  broadcaster.setAttribute("oncommand", "window.sawEvent(event, true)");
+  broadcaster.setAttribute("label", "Test Sidebar");
+  document.getElementById("mainBroadcasterSet").appendChild(broadcaster);
+
+  gTestSidebarItem.setAttribute("observes", "testbroadcaster");
+}, function*() {
+  checkExpectedEvents({ onclick: 1, oncommand: 1 });
+  document.getElementById("testbroadcaster").remove();
+});
+
+add_sidebar_task(
+  "Check that a sidebar with a click listener and a broadcaster with an oncommand attribute works",
+function*() {
+  let broadcaster = document.createElement("broadcaster");
+  broadcaster.setAttribute("id", "testbroadcaster");
+  broadcaster.setAttribute("oncommand", "window.sawEvent(event, true)");
+  broadcaster.setAttribute("label", "Test Sidebar");
+  document.getElementById("mainBroadcasterSet").appendChild(broadcaster);
+
+  gTestSidebarItem.setAttribute("observes", "testbroadcaster");
+  gTestSidebarItem.addEventListener("click", window.sawEvent);
+}, function*() {
+  checkExpectedEvents({ click: 1, oncommand: 1 });
+  document.getElementById("testbroadcaster").remove();
+});
+
+add_sidebar_task(
+  "Check that a sidebar with a command listener and a broadcaster with an onclick attribute works",
+function*() {
+  let broadcaster = document.createElement("broadcaster");
+  broadcaster.setAttribute("id", "testbroadcaster");
+  broadcaster.setAttribute("onclick", "window.sawEvent(event, true)");
+  broadcaster.setAttribute("label", "Test Sidebar");
+  document.getElementById("mainBroadcasterSet").appendChild(broadcaster);
+
+  gTestSidebarItem.setAttribute("observes", "testbroadcaster");
+  gTestSidebarItem.addEventListener("command", window.sawEvent);
+}, function*() {
+  checkExpectedEvents({ onclick: 1, command: 1 });
+  document.getElementById("testbroadcaster").remove();
+});
+
+add_sidebar_task(
+  "Check that a sidebar with a click listener and a broadcaster with an onclick " +
+  "attribute only sees one event :(",
+function*() {
+  let broadcaster = document.createElement("broadcaster");
+  broadcaster.setAttribute("id", "testbroadcaster");
+  broadcaster.setAttribute("onclick", "window.sawEvent(event, true)");
+  broadcaster.setAttribute("label", "Test Sidebar");
+  document.getElementById("mainBroadcasterSet").appendChild(broadcaster);
+
+  gTestSidebarItem.setAttribute("observes", "testbroadcaster");
+  gTestSidebarItem.addEventListener("click", window.sawEvent);
+}, function*() {
+  checkExpectedEvents({ onclick: 1 });
+  document.getElementById("testbroadcaster").remove();
+});
+
+add_sidebar_task(
+  "Check that a sidebar with a command listener and a broadcaster with an oncommand " +
+  "attribute only sees one event :(",
+function*() {
+  let broadcaster = document.createElement("broadcaster");
+  broadcaster.setAttribute("id", "testbroadcaster");
+  broadcaster.setAttribute("oncommand", "window.sawEvent(event, true)");
+  broadcaster.setAttribute("label", "Test Sidebar");
+  document.getElementById("mainBroadcasterSet").appendChild(broadcaster);
+
+  gTestSidebarItem.setAttribute("observes", "testbroadcaster");
+  gTestSidebarItem.addEventListener("command", window.sawEvent);
+}, function*() {
+  checkExpectedEvents({ oncommand: 1 });
+  document.getElementById("testbroadcaster").remove();
+});
+
+add_sidebar_task(
+  "Check that a sidebar that uses a command element with a command event listener works",
+function*() {
+  let command = document.createElement("command");
+  command.setAttribute("id", "testcommand");
+  document.getElementById("mainCommandSet").appendChild(command);
+  command.addEventListener("command", window.sawEvent);
+
+  gTestSidebarItem.setAttribute("command", "testcommand");
+}, function*() {
+  checkExpectedEvents({ command: 1 });
+  document.getElementById("testcommand").remove();
+});
+
+add_sidebar_task(
+  "Check that a sidebar that uses a command element with an oncommand attribute works",
+function*() {
+  let command = document.createElement("command");
+  command.setAttribute("id", "testcommand");
+  command.setAttribute("oncommand", "window.sawEvent(event, true)");
+  document.getElementById("mainCommandSet").appendChild(command);
+
+  gTestSidebarItem.setAttribute("command", "testcommand");
+}, function*() {
+  checkExpectedEvents({ oncommand: 1 });
+  document.getElementById("testcommand").remove();
+});
+
+add_sidebar_task("Check that a sidebar that uses a command element with a " +
+  "command event listener and oncommand attribute works",
+function*() {
+  let command = document.createElement("command");
+  command.setAttribute("id", "testcommand");
+  command.setAttribute("oncommand", "window.sawEvent(event, true)");
+  document.getElementById("mainCommandSet").appendChild(command);
+  command.addEventListener("command", window.sawEvent);
+
+  gTestSidebarItem.setAttribute("command", "testcommand");
+}, function*() {
+  checkExpectedEvents({ command: 1, oncommand: 1 });
+  document.getElementById("testcommand").remove();
+});
+
+add_sidebar_task(
+  "A sidebar with a command element will still see click events",
+function*() {
+  let command = document.createElement("command");
+  command.setAttribute("id", "testcommand");
+  command.setAttribute("oncommand", "window.sawEvent(event, true)");
+  document.getElementById("mainCommandSet").appendChild(command);
+  command.addEventListener("command", window.sawEvent);
+
+  gTestSidebarItem.setAttribute("command", "testcommand");
+  gTestSidebarItem.addEventListener("click", window.sawEvent);
+}, function*() {
+  checkExpectedEvents({ click: 1, command: 1, oncommand: 1 });
+  document.getElementById("testcommand").remove();
+});
diff --git a/browser/components/customizableui/test/browser_989338_saved_placements_not_resaved.js b/browser/components/customizableui/test/browser_989338_saved_placements_not_resaved.js
new file mode 100644
index 000000000..2a1b01bf7
--- /dev/null
+++ b/browser/components/customizableui/test/browser_989338_saved_placements_not_resaved.js
@@ -0,0 +1,56 @@
+/* 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 BUTTONID = "test-widget-saved-earlier";
+const AREAID = "test-area-saved-earlier";
+
+var hadSavedState;
+function test() {
+  // Hack our way into the module to fake a saved state that isn't there...
+  let backstagePass = Cu.import("resource:///modules/CustomizableUI.jsm", {});
+  hadSavedState = backstagePass.gSavedState != null;
+  if (!hadSavedState) {
+    backstagePass.gSavedState = {placements: {}};
+  }
+  backstagePass.gSavedState.placements[AREAID] = [BUTTONID];
+  // Put bogus stuff in the saved state for the nav-bar, so as to check the current placements
+  // override this one...
+  backstagePass.gSavedState.placements[CustomizableUI.AREA_NAVBAR] = ["bogus-navbar-item"];
+
+  backstagePass.gDirty = true;
+  backstagePass.CustomizableUIInternal.saveState();
+
+  let newSavedState = JSON.parse(Services.prefs.getCharPref("browser.uiCustomization.state"));
+  let savedArea = Array.isArray(newSavedState.placements[AREAID]);
+  ok(savedArea, "Should have re-saved the state, even though the area isn't registered");
+
+  if (savedArea) {
+    placementArraysEqual(AREAID, newSavedState.placements[AREAID], [BUTTONID]);
+  }
+  ok(!backstagePass.gPlacements.has(AREAID), "Placements map shouldn't have been affected");
+
+  let savedNavbar = Array.isArray(newSavedState.placements[CustomizableUI.AREA_NAVBAR]);
+  ok(savedNavbar, "Should have saved nav-bar contents");
+  if (savedNavbar) {
+    placementArraysEqual(CustomizableUI.AREA_NAVBAR, newSavedState.placements[CustomizableUI.AREA_NAVBAR],
+                         CustomizableUI.getWidgetIdsInArea(CustomizableUI.AREA_NAVBAR));
+  }
+}
+
+registerCleanupFunction(function() {
+  let backstagePass = Cu.import("resource:///modules/CustomizableUI.jsm", {});
+  if (!hadSavedState) {
+    backstagePass.gSavedState = null;
+  } else {
+    let savedPlacements = backstagePass.gSavedState.placements;
+    delete savedPlacements[AREAID];
+    let realNavBarPlacements = CustomizableUI.getWidgetIdsInArea(CustomizableUI.AREA_NAVBAR);
+    savedPlacements[CustomizableUI.AREA_NAVBAR] = realNavBarPlacements;
+  }
+  backstagePass.gDirty = true;
+  backstagePass.CustomizableUIInternal.saveState();
+});
+
diff --git a/browser/components/customizableui/test/browser_989751_subviewbutton_class.js b/browser/components/customizableui/test/browser_989751_subviewbutton_class.js
new file mode 100644
index 000000000..0d11324ed
--- /dev/null
+++ b/browser/components/customizableui/test/browser_989751_subviewbutton_class.js
@@ -0,0 +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";
+var 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;
+});
diff --git a/browser/components/customizableui/test/browser_992747_toggle_noncustomizable_toolbar.js b/browser/components/customizableui/test/browser_992747_toggle_noncustomizable_toolbar.js
new file mode 100644
index 000000000..eb0a8c8ee
--- /dev/null
+++ b/browser/components/customizableui/test/browser_992747_toggle_noncustomizable_toolbar.js
@@ -0,0 +1,26 @@
+/* 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 TOOLBARID = "test-noncustomizable-toolbar-for-toggling";
+function test() {
+  let tb = document.createElementNS(kNSXUL, "toolbar");
+  tb.id = TOOLBARID;
+  gNavToolbox.appendChild(tb);
+  try {
+    CustomizableUI.setToolbarVisibility(TOOLBARID, false);
+  } catch (ex) {
+    ok(false, "Should not throw exceptions trying to set toolbar visibility.");
+  }
+  is(tb.getAttribute("collapsed"), "true", "Toolbar should be collapsed");
+  try {
+    CustomizableUI.setToolbarVisibility(TOOLBARID, true);
+  } catch (ex) {
+    ok(false, "Should not throw exceptions trying to set toolbar visibility.");
+  }
+  is(tb.getAttribute("collapsed"), "false", "Toolbar should be uncollapsed");
+  tb.remove();
+}
+
diff --git a/browser/components/customizableui/test/browser_993322_widget_notoolbar.js b/browser/components/customizableui/test/browser_993322_widget_notoolbar.js
new file mode 100644
index 000000000..9264eb78a
--- /dev/null
+++ b/browser/components/customizableui/test/browser_993322_widget_notoolbar.js
@@ -0,0 +1,36 @@
+/* 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 BUTTONID = "test-API-created-widget-toolbar-gone";
+const TOOLBARID = "test-API-created-extra-toolbar";
+
+add_task(function*() {
+  let toolbar = createToolbarWithPlacements(TOOLBARID, []);
+  CustomizableUI.addWidgetToArea(BUTTONID, TOOLBARID);
+  is(CustomizableUI.getPlacementOfWidget(BUTTONID).area, TOOLBARID, "Should be on toolbar");
+  is(toolbar.children.length, 0, "Toolbar has no kid");
+
+  CustomizableUI.unregisterArea(TOOLBARID);
+  CustomizableUI.createWidget({id: BUTTONID, label: "Test widget toolbar gone"});
+
+  let currentWidget = CustomizableUI.getWidget(BUTTONID);
+
+  yield startCustomizing();
+  let buttonNode = document.getElementById(BUTTONID);
+  ok(buttonNode, "Should find button in window");
+  if (buttonNode) {
+    is(buttonNode.parentNode.localName, "toolbarpaletteitem", "Node should be wrapped");
+    is(buttonNode.parentNode.getAttribute("place"), "palette", "Node should be in palette");
+    is(buttonNode, gNavToolbox.palette.querySelector("#" + BUTTONID), "Node should really be in palette.");
+  }
+  is(currentWidget.forWindow(window).node, buttonNode, "Should have the same node for customize mode");
+  yield endCustomizing();
+
+  CustomizableUI.destroyWidget(BUTTONID);
+  CustomizableUI.unregisterArea(TOOLBARID, true);
+  toolbar.remove();
+  gAddedToolbars.clear();
+});
diff --git a/browser/components/customizableui/test/browser_995164_registerArea_during_customize_mode.js b/browser/components/customizableui/test/browser_995164_registerArea_during_customize_mode.js
new file mode 100644
index 000000000..4d292a929
--- /dev/null
+++ b/browser/components/customizableui/test/browser_995164_registerArea_during_customize_mode.js
@@ -0,0 +1,149 @@
+/* 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 TOOLBARID = "test-toolbar-added-during-customize-mode";
+
+// The ID of a button that is not placed (ie, is in the palette) by default
+const kNonPlacedWidgetId = "open-file-button";
+
+add_task(function*() {
+  yield startCustomizing();
+  let toolbar = createToolbarWithPlacements(TOOLBARID, []);
+  CustomizableUI.addWidgetToArea(kNonPlacedWidgetId, TOOLBARID);
+  let button = document.getElementById(kNonPlacedWidgetId);
+  ok(button, "Button should exist.");
+  is(button.parentNode.localName, "toolbarpaletteitem", "Button's parent node should be a wrapper.");
+
+  simulateItemDrag(button, gNavToolbox.palette);
+  ok(!CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId), "Button moved to the palette");
+  ok(gNavToolbox.palette.querySelector(`#${kNonPlacedWidgetId}`), "Button really is in palette.");
+
+  button.scrollIntoView();
+  simulateItemDrag(button, toolbar);
+  ok(CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId), "Button moved out of palette");
+  is(CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId).area, TOOLBARID, "Button's back on toolbar");
+  ok(toolbar.querySelector(`#${kNonPlacedWidgetId}`), "Button really is on toolbar.");
+
+  yield endCustomizing();
+  isnot(button.parentNode.localName, "toolbarpaletteitem", "Button's parent node should not be a wrapper outside customize mode.");
+  yield startCustomizing();
+
+  is(button.parentNode.localName, "toolbarpaletteitem", "Button's parent node should be a wrapper back in customize mode.");
+
+  simulateItemDrag(button, gNavToolbox.palette);
+  ok(!CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId), "Button moved to the palette");
+  ok(gNavToolbox.palette.querySelector(`#${kNonPlacedWidgetId}`), "Button really is in palette.");
+
+  ok(!CustomizableUI.inDefaultState, "Not in default state while toolbar is not collapsed yet.");
+  setToolbarVisibility(toolbar, false);
+  ok(CustomizableUI.inDefaultState, "In default state while toolbar is collapsed.");
+
+  setToolbarVisibility(toolbar, true);
+
+  info("Check that removing the area registration from within customize mode works");
+  CustomizableUI.unregisterArea(TOOLBARID);
+  ok(CustomizableUI.inDefaultState, "Now that the toolbar is no longer registered, should be in default state.");
+  ok(!gCustomizeMode.areas.has(toolbar), "Toolbar shouldn't be known to customize mode.");
+
+  CustomizableUI.registerArea(TOOLBARID, {legacy: true, defaultPlacements: []});
+  CustomizableUI.registerToolbarNode(toolbar, []);
+  ok(!CustomizableUI.inDefaultState, "Now that the toolbar is registered again, should no longer be in default state.");
+  ok(gCustomizeMode.areas.has(toolbar), "Toolbar should be known to customize mode again.");
+
+  button.scrollIntoView();
+  simulateItemDrag(button, toolbar);
+  ok(CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId), "Button moved out of palette");
+  is(CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId).area, TOOLBARID, "Button's back on toolbar");
+  ok(toolbar.querySelector(`#${kNonPlacedWidgetId}`), "Button really is on toolbar.");
+
+  let otherWin = yield openAndLoadWindow({}, true);
+  let otherTB = otherWin.document.createElementNS(kNSXUL, "toolbar");
+  otherTB.id = TOOLBARID;
+  otherTB.setAttribute("customizable", "true");
+  let wasInformedCorrectlyOfAreaAppearing = false;
+  let listener = {
+    onAreaNodeRegistered: function(aArea, aNode) {
+      if (aNode == otherTB) {
+        wasInformedCorrectlyOfAreaAppearing = true;
+      }
+    }
+  };
+  CustomizableUI.addListener(listener);
+  otherWin.gNavToolbox.appendChild(otherTB);
+  ok(wasInformedCorrectlyOfAreaAppearing, "Should have been told area was registered.");
+  CustomizableUI.removeListener(listener);
+
+  ok(otherTB.querySelector(`#${kNonPlacedWidgetId}`), "Button is on other toolbar, too.");
+
+  simulateItemDrag(button, gNavToolbox.palette);
+  ok(!CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId), "Button moved to the palette");
+  ok(gNavToolbox.palette.querySelector(`#${kNonPlacedWidgetId}`), "Button really is in palette.");
+  ok(!otherTB.querySelector(`#${kNonPlacedWidgetId}`), "Button is in palette in other window, too.");
+
+  button.scrollIntoView();
+  simulateItemDrag(button, toolbar);
+  ok(CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId), "Button moved out of palette");
+  is(CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId).area, TOOLBARID, "Button's back on toolbar");
+  ok(toolbar.querySelector(`#${kNonPlacedWidgetId}`), "Button really is on toolbar.");
+  ok(otherTB.querySelector(`#${kNonPlacedWidgetId}`), "Button is on other toolbar, too.");
+
+  let wasInformedCorrectlyOfAreaDisappearing = false;
+  // XXXgijs So we could be using promiseWindowClosed here. However, after
+  // repeated random oranges, I'm instead relying on onWindowClosed below to
+  // fire appropriately - it is linked to an unload event as well, and so
+  // reusing it prevents a potential race between unload handlers where the
+  // one from promiseWindowClosed could fire before the onWindowClosed
+  // (and therefore onAreaNodeRegistered) one, causing the test to fail.
+  let windowCloseDeferred = Promise.defer();
+  listener = {
+    onAreaNodeUnregistered: function(aArea, aNode, aReason) {
+      if (aArea == TOOLBARID) {
+        is(aNode, otherTB, "Should be informed about other toolbar");
+        is(aReason, CustomizableUI.REASON_WINDOW_CLOSED, "Reason should be correct.");
+        wasInformedCorrectlyOfAreaDisappearing = (aReason === CustomizableUI.REASON_WINDOW_CLOSED);
+      }
+    },
+    onWindowClosed: function(aWindow) {
+      if (aWindow == otherWin) {
+        windowCloseDeferred.resolve(aWindow);
+      } else {
+        info("Other window was closed!");
+        info("Other window title: " + (aWindow.document && aWindow.document.title));
+        info("Our window title: " + (otherWin.document && otherWin.document.title));
+      }
+    },
+  };
+  CustomizableUI.addListener(listener);
+  otherWin.close();
+  let windowClosed = yield windowCloseDeferred.promise;
+
+  is(windowClosed, otherWin, "Window should have sent onWindowClosed notification.");
+  ok(wasInformedCorrectlyOfAreaDisappearing, "Should be told about window closing.");
+  // Closing the other window should not be counted against this window's customize mode:
+  is(button.parentNode.localName, "toolbarpaletteitem", "Button's parent node should still be a wrapper.");
+  ok(gCustomizeMode.areas.has(toolbar), "Toolbar should still be a customizable area for this customize mode instance.");
+
+  yield gCustomizeMode.reset();
+
+  yield endCustomizing();
+
+  CustomizableUI.removeListener(listener);
+  wasInformedCorrectlyOfAreaDisappearing = false;
+  listener = {
+    onAreaNodeUnregistered: function(aArea, aNode, aReason) {
+      if (aArea == TOOLBARID) {
+        is(aNode, toolbar, "Should be informed about this window's toolbar");
+        is(aReason, CustomizableUI.REASON_AREA_UNREGISTERED, "Reason for final removal should be correct.");
+        wasInformedCorrectlyOfAreaDisappearing = (aReason === CustomizableUI.REASON_AREA_UNREGISTERED);
+      }
+    },
+  }
+  CustomizableUI.addListener(listener);
+  removeCustomToolbars();
+  ok(wasInformedCorrectlyOfAreaDisappearing, "Should be told about area being unregistered.");
+  CustomizableUI.removeListener(listener);
+  ok(CustomizableUI.inDefaultState, "Should be fine after exiting customize mode.");
+});
diff --git a/browser/components/customizableui/test/browser_996364_registerArea_different_properties.js b/browser/components/customizableui/test/browser_996364_registerArea_different_properties.js
new file mode 100644
index 000000000..b9de5f687
--- /dev/null
+++ b/browser/components/customizableui/test/browser_996364_registerArea_different_properties.js
@@ -0,0 +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);
+});
diff --git a/browser/components/customizableui/test/browser_996635_remove_non_widgets.js b/browser/components/customizableui/test/browser_996635_remove_non_widgets.js
new file mode 100644
index 000000000..14a446eec
--- /dev/null
+++ b/browser/components/customizableui/test/browser_996635_remove_non_widgets.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// NB: This is testing what happens if something that /isn't/ a customizable
+// widget gets used in CustomizableUI APIs. Don't use this as an example of
+// what should happen in a "normal" case or how you should use the API.
+function test() {
+  // First create a button that isn't customizable, and add it in the nav-bar,
+  // but not in the customizable part of it (the customization target) but
+  // next to the main (hamburger) menu button.
+  const buttonID = "Test-non-widget-non-removable-button";
+  let btn = document.createElement("toolbarbutton");
+  btn.id = buttonID;
+  btn.label = "Hi";
+  btn.setAttribute("style", "width: 20px; height: 20px; background-color: red");
+  document.getElementById("nav-bar").appendChild(btn);
+  registerCleanupFunction(function() {
+    btn.remove();
+  });
+
+  // Now try to add this non-customizable button to the tabstrip. This will
+  // update the internal bookkeeping (ie placements) information, but shouldn't
+  // move the node.
+  CustomizableUI.addWidgetToArea(buttonID, CustomizableUI.AREA_TABSTRIP);
+  let placement = CustomizableUI.getPlacementOfWidget(buttonID);
+  // Check our bookkeeping
+  ok(placement, "Button should be placed");
+  is(placement && placement.area, CustomizableUI.AREA_TABSTRIP, "Should be placed on tabstrip.");
+  // Check we didn't move the node.
+  is(btn.parentNode && btn.parentNode.id, "nav-bar", "Actual button should still be on navbar.");
+
+  // Now remove the node again. This should remove the bookkeeping, but again
+  // not affect the actual node.
+  CustomizableUI.removeWidgetFromArea(buttonID);
+  placement = CustomizableUI.getPlacementOfWidget(buttonID);
+  // Check our bookkeeping:
+  ok(!placement, "Button should no longer have a placement.");
+  // Check our node.
+  is(btn.parentNode && btn.parentNode.id, "nav-bar", "Actual button should still be on navbar.");
+}
+
diff --git a/browser/components/customizableui/test/browser_bootstrapped_custom_toolbar.js b/browser/components/customizableui/test/browser_bootstrapped_custom_toolbar.js
new file mode 100644
index 000000000..2c5f0c79c
--- /dev/null
+++ b/browser/components/customizableui/test/browser_bootstrapped_custom_toolbar.js
@@ -0,0 +1,81 @@
+/* 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);
+
+const kTestBarID = "testBar";
+const kWidgetID = "characterencoding-button";
+
+function createTestBar(aLegacy) {
+  let testBar = document.createElement("toolbar");
+  testBar.id = kTestBarID;
+  testBar.setAttribute("customizable", "true");
+  CustomizableUI.registerArea(kTestBarID, {
+    type: CustomizableUI.TYPE_TOOLBAR,
+    legacy: aLegacy,
+  });
+  gNavToolbox.appendChild(testBar);
+  return testBar;
+}
+
+/**
+ * Helper function that does the following:
+ *
+ * 1) Creates a custom toolbar and registers it
+ *    with CustomizableUI. Sets the legacy attribute
+ *    of the object passed to registerArea to aLegacy.
+ * 2) Adds the widget with ID aWidgetID to that new
+ *    toolbar.
+ * 3) Enters customize mode and makes sure that the
+ *    widget is still in the right toolbar.
+ * 4) Exits customize mode, then removes and deregisters
+ *    the custom toolbar.
+ * 5) Checks that the widget has no placement.
+ * 6) Re-adds and re-registers a custom toolbar with the same
+ *    ID and options as the first one.
+ * 7) Enters customize mode and checks that the widget is
+ *    properly back in the toolbar.
+ * 8) Exits customize mode, removes and de-registers the
+ *    toolbar, and resets the toolbars to default.
+ */
+function checkRestoredPresence(aWidgetID, aLegacy) {
+  return Task.spawn(function* () {
+    let testBar = createTestBar(aLegacy);
+    CustomizableUI.addWidgetToArea(aWidgetID, kTestBarID);
+    let placement = CustomizableUI.getPlacementOfWidget(aWidgetID);
+    is(placement.area, kTestBarID,
+       "Expected " + aWidgetID + " to be in the test toolbar");
+
+    CustomizableUI.unregisterArea(testBar.id);
+    testBar.remove();
+
+    placement = CustomizableUI.getPlacementOfWidget(aWidgetID);
+    is(placement, null, "Expected " + aWidgetID + " to be in the palette");
+
+    testBar = createTestBar(aLegacy);
+
+    yield startCustomizing();
+    placement = CustomizableUI.getPlacementOfWidget(aWidgetID);
+    is(placement.area, kTestBarID,
+       "Expected " + aWidgetID + " to be in the test toolbar");
+    yield endCustomizing();
+
+    CustomizableUI.unregisterArea(testBar.id);
+    testBar.remove();
+
+    yield resetCustomization();
+  });
+}
+
+add_task(function* () {
+  yield checkRestoredPresence("downloads-button", false);
+  yield checkRestoredPresence("downloads-button", true);
+});
+
+add_task(function* () {
+  yield checkRestoredPresence("characterencoding-button", false);
+  yield checkRestoredPresence("characterencoding-button", true);
+});
diff --git a/browser/components/customizableui/test/browser_check_tooltips_in_navbar.js b/browser/components/customizableui/test/browser_check_tooltips_in_navbar.js
new file mode 100644
index 000000000..31dd42ad8
--- /dev/null
+++ b/browser/components/customizableui/test/browser_check_tooltips_in_navbar.js
@@ -0,0 +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/. */
+
+"use strict";
+
+add_task(function* check_tooltips_in_navbar() {
+  yield startCustomizing();
+  let homeButtonWrapper = document.getElementById("wrapper-home-button");
+  let homeButton = document.getElementById("home-button");
+  is(homeButtonWrapper.getAttribute("tooltiptext"), homeButton.getAttribute("label"), "the wrapper's tooltip should match the button's label");
+  ok(homeButtonWrapper.getAttribute("tooltiptext"), "the button should have tooltip text");
+  yield endCustomizing();
+});
diff --git a/browser/components/customizableui/test/browser_customizemode_contextmenu_menubuttonstate.js b/browser/components/customizableui/test/browser_customizemode_contextmenu_menubuttonstate.js
new file mode 100644
index 000000000..8e1950291
--- /dev/null
+++ b/browser/components/customizableui/test/browser_customizemode_contextmenu_menubuttonstate.js
@@ -0,0 +1,24 @@
+"use strict";
+
+add_task(function*() {
+  ok(!PanelUI.menuButton.hasAttribute("open"), "Menu button should not be 'pressed' outside customize mode");
+  yield startCustomizing();
+
+  is(PanelUI.menuButton.getAttribute("open"), "true", "Menu button should be 'pressed' when in customize mode");
+
+  let contextMenu = document.getElementById("customizationPanelItemContextMenu");
+  let shownPromise = popupShown(contextMenu);
+  let newWindowButton = document.getElementById("wrapper-new-window-button");
+  EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2});
+  yield shownPromise;
+  is(PanelUI.menuButton.getAttribute("open"), "true", "Menu button should be 'pressed' when in customize mode after opening a context menu");
+
+  let hiddenContextPromise = popupHidden(contextMenu);
+  contextMenu.hidePopup();
+  yield hiddenContextPromise;
+  is(PanelUI.menuButton.getAttribute("open"), "true", "Menu button should be 'pressed' when in customize mode after hiding a context menu");
+  yield endCustomizing();
+
+  ok(!PanelUI.menuButton.hasAttribute("open"), "Menu button should not be 'pressed' after ending customize mode");
+});
+
diff --git a/browser/components/customizableui/test/browser_panel_toggle.js b/browser/components/customizableui/test/browser_panel_toggle.js
new file mode 100644
index 000000000..4c286fb85
--- /dev/null
+++ b/browser/components/customizableui/test/browser_panel_toggle.js
@@ -0,0 +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";
+
+/**
+ * Test opening and closing the menu panel UI.
+ */
+
+// Show and hide the menu panel programmatically without an event (like UITour.jsm would)
+add_task(function*() {
+  let shownPromise = promisePanelShown(window);
+  PanelUI.show();
+  yield shownPromise;
+
+  is(PanelUI.panel.getAttribute("panelopen"), "true", "Check that panel has panelopen attribute");
+  is(PanelUI.panel.state, "open", "Check that panel state is 'open'");
+
+  let hiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield hiddenPromise;
+
+  ok(!PanelUI.panel.hasAttribute("panelopen"), "Check that panel doesn't have the panelopen attribute");
+  is(PanelUI.panel.state, "closed", "Check that panel state is 'closed'");
+});
+
+// Toggle the menu panel open and closed
+add_task(function*() {
+  let shownPromise = promisePanelShown(window);
+  PanelUI.toggle({type: "command"});
+  yield shownPromise;
+
+  is(PanelUI.panel.getAttribute("panelopen"), "true", "Check that panel has panelopen attribute");
+  is(PanelUI.panel.state, "open", "Check that panel state is 'open'");
+
+  let hiddenPromise = promisePanelHidden(window);
+  PanelUI.toggle({type: "command"});
+  yield hiddenPromise;
+
+  ok(!PanelUI.panel.hasAttribute("panelopen"), "Check that panel doesn't have the panelopen attribute");
+  is(PanelUI.panel.state, "closed", "Check that panel state is 'closed'");
+});
diff --git a/browser/components/customizableui/test/browser_switch_to_customize_mode.js b/browser/components/customizableui/test/browser_switch_to_customize_mode.js
new file mode 100644
index 000000000..459ea7a1c
--- /dev/null
+++ b/browser/components/customizableui/test/browser_switch_to_customize_mode.js
@@ -0,0 +1,34 @@
+"use strict";
+
+add_task(function*() {
+  yield startCustomizing();
+  is(gBrowser.tabs.length, 2, "Should have 2 tabs");
+
+  let paletteKidCount = document.getElementById("customization-palette").childElementCount;
+  let nonCustomizingTab = gBrowser.tabContainer.querySelector("tab:not([customizemode=true])");
+  let finishedCustomizing = BrowserTestUtils.waitForEvent(gNavToolbox, "aftercustomization");
+  yield BrowserTestUtils.switchTab(gBrowser, nonCustomizingTab);
+  yield finishedCustomizing;
+
+  let startedCount = 0;
+  let handler = e => startedCount++;
+  gNavToolbox.addEventListener("customizationstarting", handler);
+  yield startCustomizing();
+  CustomizableUI.removeWidgetFromArea("home-button");
+  yield gCustomizeMode.reset().catch(e => {
+    ok(false, "Threw an exception trying to reset after making modifications in customize mode: " + e);
+  });
+
+  let newKidCount = document.getElementById("customization-palette").childElementCount;
+  is(newKidCount, paletteKidCount, "Should have just as many items in the palette as before.");
+  yield endCustomizing();
+  is(startedCount, 1, "Should have only started once");
+  gNavToolbox.removeEventListener("customizationstarting", handler);
+  let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true])");
+  for (let toolbar of customizableToolbars) {
+    ok(!toolbar.hasAttribute("customizing"), "Toolbar " + toolbar.id + " is no longer customizing");
+  }
+  let menuitem = document.getElementById("PanelUI-customize");
+  isnot(menuitem.getAttribute("label"), menuitem.getAttribute("exitLabel"), "Should have exited successfully");
+});
+
diff --git a/browser/components/customizableui/test/head.js b/browser/components/customizableui/test/head.js
new file mode 100644
index 000000000..7b8d84e20
--- /dev/null
+++ b/browser/components/customizableui/test/head.js
@@ -0,0 +1,499 @@
+/* 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";
+
+// Avoid leaks by using tmp for imports...
+var tmp = {};
+Cu.import("resource://gre/modules/Promise.jsm", tmp);
+Cu.import("resource:///modules/CustomizableUI.jsm", tmp);
+Cu.import("resource://gre/modules/AppConstants.jsm", tmp);
+var {Promise, CustomizableUI, AppConstants} = tmp;
+
+var EventUtils = {};
+Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
+
+Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
+registerCleanupFunction(() => Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck"));
+
+// Remove temporary e10s related new window options in customize ui,
+// they break a lot of tests.
+CustomizableUI.destroyWidget("e10s-button");
+CustomizableUI.removeWidgetFromArea("e10s-button");
+
+var {synthesizeDragStart, synthesizeDrop} = EventUtils;
+
+const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const kTabEventFailureTimeoutInMs = 20000;
+
+function createDummyXULButton(id, label, win = window) {
+  let btn = document.createElementNS(kNSXUL, "toolbarbutton");
+  btn.id = id;
+  btn.setAttribute("label", label || id);
+  btn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
+  win.gNavToolbox.palette.appendChild(btn);
+  return btn;
+}
+
+var gAddedToolbars = new Set();
+
+function createToolbarWithPlacements(id, placements = []) {
+  gAddedToolbars.add(id);
+  let tb = document.createElementNS(kNSXUL, "toolbar");
+  tb.id = id;
+  tb.setAttribute("customizable", "true");
+  CustomizableUI.registerArea(id, {
+    type: CustomizableUI.TYPE_TOOLBAR,
+    defaultPlacements: placements
+  });
+  gNavToolbox.appendChild(tb);
+  return tb;
+}
+
+function createOverflowableToolbarWithPlacements(id, placements) {
+  gAddedToolbars.add(id);
+
+  let tb = document.createElementNS(kNSXUL, "toolbar");
+  tb.id = id;
+  tb.setAttribute("customizationtarget", id + "-target");
+
+  let customizationtarget = document.createElementNS(kNSXUL, "hbox");
+  customizationtarget.id = id + "-target";
+  customizationtarget.setAttribute("flex", "1");
+  tb.appendChild(customizationtarget);
+
+  let overflowPanel = document.createElementNS(kNSXUL, "panel");
+  overflowPanel.id = id + "-overflow";
+  document.getElementById("mainPopupSet").appendChild(overflowPanel);
+
+  let overflowList = document.createElementNS(kNSXUL, "vbox");
+  overflowList.id = id + "-overflow-list";
+  overflowPanel.appendChild(overflowList);
+
+  let chevron = document.createElementNS(kNSXUL, "toolbarbutton");
+  chevron.id = id + "-chevron";
+  tb.appendChild(chevron);
+
+  CustomizableUI.registerArea(id, {
+    type: CustomizableUI.TYPE_TOOLBAR,
+    defaultPlacements: placements,
+    overflowable: true,
+  });
+
+  tb.setAttribute("customizable", "true");
+  tb.setAttribute("overflowable", "true");
+  tb.setAttribute("overflowpanel", overflowPanel.id);
+  tb.setAttribute("overflowtarget", overflowList.id);
+  tb.setAttribute("overflowbutton", chevron.id);
+
+  gNavToolbox.appendChild(tb);
+  return tb;
+}
+
+function removeCustomToolbars() {
+  CustomizableUI.reset();
+  for (let toolbarId of gAddedToolbars) {
+    CustomizableUI.unregisterArea(toolbarId, true);
+    let tb = document.getElementById(toolbarId);
+    if (tb.hasAttribute("overflowpanel")) {
+      let panel = document.getElementById(tb.getAttribute("overflowpanel"));
+      if (panel)
+        panel.remove();
+    }
+    tb.remove();
+  }
+  gAddedToolbars.clear();
+}
+
+function getToolboxCustomToolbarId(toolbarName) {
+  return "__customToolbar_" + toolbarName.replace(" ", "_");
+}
+
+function resetCustomization() {
+  return CustomizableUI.reset();
+}
+
+function isInDevEdition() {
+  return AppConstants.MOZ_DEV_EDITION;
+}
+
+function removeDeveloperButtonIfDevEdition(areaPanelPlacements) {
+  if (isInDevEdition()) {
+    areaPanelPlacements.splice(areaPanelPlacements.indexOf("developer-button"), 1);
+  }
+}
+
+function assertAreaPlacements(areaId, expectedPlacements) {
+  let actualPlacements = getAreaWidgetIds(areaId);
+  placementArraysEqual(areaId, actualPlacements, expectedPlacements);
+}
+
+function placementArraysEqual(areaId, actualPlacements, expectedPlacements) {
+  is(actualPlacements.length, expectedPlacements.length,
+     "Area " + areaId + " should have " + expectedPlacements.length + " items.");
+  let minItems = Math.min(expectedPlacements.length, actualPlacements.length);
+  for (let i = 0; i < minItems; i++) {
+    if (typeof expectedPlacements[i] == "string") {
+      is(actualPlacements[i], expectedPlacements[i],
+         "Item " + i + " in " + areaId + " should match expectations.");
+    } else if (expectedPlacements[i] instanceof RegExp) {
+      ok(expectedPlacements[i].test(actualPlacements[i]),
+         "Item " + i + " (" + actualPlacements[i] + ") in " +
+         areaId + " should match " + expectedPlacements[i]);
+    } else {
+      ok(false, "Unknown type of expected placement passed to " +
+                " assertAreaPlacements. Is your test broken?");
+    }
+  }
+}
+
+function todoAssertAreaPlacements(areaId, expectedPlacements) {
+  let actualPlacements = getAreaWidgetIds(areaId);
+  let isPassing = actualPlacements.length == expectedPlacements.length;
+  let minItems = Math.min(expectedPlacements.length, actualPlacements.length);
+  for (let i = 0; i < minItems; i++) {
+    if (typeof expectedPlacements[i] == "string") {
+      isPassing = isPassing && actualPlacements[i] == expectedPlacements[i];
+    } else if (expectedPlacements[i] instanceof RegExp) {
+      isPassing = isPassing && expectedPlacements[i].test(actualPlacements[i]);
+    } else {
+      ok(false, "Unknown type of expected placement passed to " +
+                " assertAreaPlacements. Is your test broken?");
+    }
+  }
+  todo(isPassing, "The area placements for " + areaId +
+                  " should equal the expected placements.");
+}
+
+function getAreaWidgetIds(areaId) {
+  return CustomizableUI.getWidgetIdsInArea(areaId);
+}
+
+function simulateItemDrag(aToDrag, aTarget) {
+  synthesizeDrop(aToDrag.parentNode, aTarget);
+}
+
+function endCustomizing(aWindow=window) {
+  if (aWindow.document.documentElement.getAttribute("customizing") != "true") {
+    return true;
+  }
+  Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", true);
+  let deferredEndCustomizing = Promise.defer();
+  function onCustomizationEnds() {
+    Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", false);
+    aWindow.gNavToolbox.removeEventListener("aftercustomization", onCustomizationEnds);
+    deferredEndCustomizing.resolve();
+  }
+  aWindow.gNavToolbox.addEventListener("aftercustomization", onCustomizationEnds);
+  aWindow.gCustomizeMode.exit();
+
+  return deferredEndCustomizing.promise;
+}
+
+function startCustomizing(aWindow=window) {
+  if (aWindow.document.documentElement.getAttribute("customizing") == "true") {
+    return null;
+  }
+  Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", true);
+  let deferred = Promise.defer();
+  function onCustomizing() {
+    aWindow.gNavToolbox.removeEventListener("customizationready", onCustomizing);
+    Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", false);
+    deferred.resolve();
+  }
+  aWindow.gNavToolbox.addEventListener("customizationready", onCustomizing);
+  aWindow.gCustomizeMode.enter();
+  return deferred.promise;
+}
+
+function promiseObserverNotified(aTopic) {
+  let deferred = Promise.defer();
+  Services.obs.addObserver(function onNotification(aSubject, aTopic, aData) {
+    Services.obs.removeObserver(onNotification, aTopic);
+      deferred.resolve({subject: aSubject, data: aData});
+    }, aTopic, false);
+  return deferred.promise;
+}
+
+function openAndLoadWindow(aOptions, aWaitForDelayedStartup=false) {
+  let deferred = Promise.defer();
+  let win = OpenBrowserWindow(aOptions);
+  if (aWaitForDelayedStartup) {
+    Services.obs.addObserver(function onDS(aSubject, aTopic, aData) {
+      if (aSubject != win) {
+        return;
+      }
+      Services.obs.removeObserver(onDS, "browser-delayed-startup-finished");
+      deferred.resolve(win);
+    }, "browser-delayed-startup-finished", false);
+
+  } else {
+    win.addEventListener("load", function onLoad() {
+      win.removeEventListener("load", onLoad);
+      deferred.resolve(win);
+    });
+  }
+  return deferred.promise;
+}
+
+function promiseWindowClosed(win) {
+  let deferred = Promise.defer();
+  win.addEventListener("unload", function onunload() {
+    win.removeEventListener("unload", onunload);
+    deferred.resolve();
+  });
+  win.close();
+  return deferred.promise;
+}
+
+function promisePanelShown(win) {
+  let panelEl = win.PanelUI.panel;
+  return promisePanelElementShown(win, panelEl);
+}
+
+function promiseOverflowShown(win) {
+  let panelEl = win.document.getElementById("widget-overflow");
+  return promisePanelElementShown(win, panelEl);
+}
+
+function promisePanelElementShown(win, aPanel) {
+  let deferred = Promise.defer();
+  let timeoutId = win.setTimeout(() => {
+    deferred.reject("Panel did not show within 20 seconds.");
+  }, 20000);
+  function onPanelOpen(e) {
+    aPanel.removeEventListener("popupshown", onPanelOpen);
+    win.clearTimeout(timeoutId);
+    deferred.resolve();
+  }
+  aPanel.addEventListener("popupshown", onPanelOpen);
+  return deferred.promise;
+}
+
+function promisePanelHidden(win) {
+  let panelEl = win.PanelUI.panel;
+  return promisePanelElementHidden(win, panelEl);
+}
+
+function promiseOverflowHidden(win) {
+  let panelEl = document.getElementById("widget-overflow");
+  return promisePanelElementHidden(win, panelEl);
+}
+
+function promisePanelElementHidden(win, aPanel) {
+  let deferred = Promise.defer();
+  let timeoutId = win.setTimeout(() => {
+    deferred.reject("Panel did not hide within 20 seconds.");
+  }, 20000);
+  function onPanelClose(e) {
+    aPanel.removeEventListener("popuphidden", onPanelClose);
+    win.clearTimeout(timeoutId);
+    deferred.resolve();
+  }
+  aPanel.addEventListener("popuphidden", onPanelClose);
+  return deferred.promise;
+}
+
+function isPanelUIOpen() {
+  return PanelUI.panel.state == "open" || PanelUI.panel.state == "showing";
+}
+
+function subviewShown(aSubview) {
+  let deferred = Promise.defer();
+  let win = aSubview.ownerGlobal;
+  let timeoutId = win.setTimeout(() => {
+    deferred.reject("Subview (" + aSubview.id + ") did not show within 20 seconds.");
+  }, 20000);
+  function onViewShowing(e) {
+    aSubview.removeEventListener("ViewShowing", onViewShowing);
+    win.clearTimeout(timeoutId);
+    deferred.resolve();
+  }
+  aSubview.addEventListener("ViewShowing", onViewShowing);
+  return deferred.promise;
+}
+
+function subviewHidden(aSubview) {
+  let deferred = Promise.defer();
+  let win = aSubview.ownerGlobal;
+  let timeoutId = win.setTimeout(() => {
+    deferred.reject("Subview (" + aSubview.id + ") did not hide within 20 seconds.");
+  }, 20000);
+  function onViewHiding(e) {
+    aSubview.removeEventListener("ViewHiding", onViewHiding);
+    win.clearTimeout(timeoutId);
+    deferred.resolve();
+  }
+  aSubview.addEventListener("ViewHiding", onViewHiding);
+  return deferred.promise;
+}
+
+function waitForCondition(aConditionFn, aMaxTries=50, aCheckInterval=100) {
+  function tryNow() {
+    tries++;
+    if (aConditionFn()) {
+      deferred.resolve();
+    } else if (tries < aMaxTries) {
+      tryAgain();
+    } else {
+      deferred.reject("Condition timed out: " + aConditionFn.toSource());
+    }
+  }
+  function tryAgain() {
+    setTimeout(tryNow, aCheckInterval);
+  }
+  let deferred = Promise.defer();
+  let tries = 0;
+  tryAgain();
+  return deferred.promise;
+}
+
+function waitFor(aTimeout=100) {
+  let deferred = Promise.defer();
+  setTimeout(() => deferred.resolve(), aTimeout);
+  return deferred.promise;
+}
+
+/**
+ * Starts a load in an existing tab and waits for it to finish (via some event).
+ *
+ * @param aTab       The tab to load into.
+ * @param aUrl       The url to load.
+ * @param aEventType The load event type to wait for.  Defaults to "load".
+ * @return {Promise} resolved when the event is handled.
+ */
+function promiseTabLoadEvent(aTab, aURL) {
+  let browser = aTab.linkedBrowser;
+
+  BrowserTestUtils.loadURI(browser, aURL);
+  return BrowserTestUtils.browserLoaded(browser);
+}
+
+/**
+ * Navigate back or forward in tab history and wait for it to finish.
+ *
+ * @param aDirection   Number to indicate to move backward or forward in history.
+ * @param aConditionFn Function that returns the result of an evaluated condition
+ *                     that needs to be `true` to resolve the promise.
+ * @return {Promise} resolved when navigation has finished.
+ */
+function promiseTabHistoryNavigation(aDirection = -1, aConditionFn) {
+  let deferred = Promise.defer();
+
+  let timeoutId = setTimeout(() => {
+    gBrowser.removeEventListener("pageshow", listener, true);
+    deferred.reject("Pageshow did not happen within " + kTabEventFailureTimeoutInMs + "ms");
+  }, kTabEventFailureTimeoutInMs);
+
+  function listener(event) {
+    gBrowser.removeEventListener("pageshow", listener, true);
+    clearTimeout(timeoutId);
+
+    if (aConditionFn) {
+      waitForCondition(aConditionFn).then(() => deferred.resolve(),
+                                          aReason => deferred.reject(aReason));
+    } else {
+      deferred.resolve();
+    }
+  }
+  gBrowser.addEventListener("pageshow", listener, true);
+
+  content.history.go(aDirection);
+
+  return deferred.promise;
+}
+
+/**
+ * Wait for an attribute on a node to change
+ *
+ * @param aNode      Node on which the mutation is expected
+ * @param aAttribute The attribute we're interested in
+ * @param aFilterFn  A function to check if the new value is what we want.
+ * @return {Promise} resolved when the requisite mutation shows up.
+ */
+function promiseAttributeMutation(aNode, aAttribute, aFilterFn) {
+  return new Promise((resolve, reject) => {
+    info("waiting for mutation of attribute '" + aAttribute + "'.");
+    let obs = new MutationObserver((mutations) => {
+      for (let mut of mutations) {
+        let attr = mut.attributeName;
+        let newValue = mut.target.getAttribute(attr);
+        if (aFilterFn(newValue)) {
+          ok(true, "mutation occurred: attribute '" + attr + "' changed to '" + newValue + "' from '" + mut.oldValue + "'.");
+          obs.disconnect();
+          resolve();
+        } else {
+          info("Ignoring mutation that produced value " + newValue + " because of filter.");
+        }
+      }
+    });
+    obs.observe(aNode, {attributeFilter: [aAttribute]});
+  });
+}
+
+function popupShown(aPopup) {
+  return promisePopupEvent(aPopup, "shown");
+}
+
+function popupHidden(aPopup) {
+  return promisePopupEvent(aPopup, "hidden");
+}
+
+/**
+ * Returns a Promise that resolves when aPopup fires an event of type
+ * aEventType. Times out and rejects after 20 seconds.
+ *
+ * @param aPopup the popup to monitor for events.
+ * @param aEventSuffix the _suffix_ for the popup event type to watch for.
+ *
+ * Example usage:
+ *   let popupShownPromise = promisePopupEvent(somePopup, "shown");
+ *   // ... something that opens a popup
+ *   yield popupShownPromise;
+ *
+ *  let popupHiddenPromise = promisePopupEvent(somePopup, "hidden");
+ *  // ... something that hides a popup
+ *  yield popupHiddenPromise;
+ */
+function promisePopupEvent(aPopup, aEventSuffix) {
+  let deferred = Promise.defer();
+  let eventType = "popup" + aEventSuffix;
+
+  function onPopupEvent(e) {
+    aPopup.removeEventListener(eventType, onPopupEvent);
+    deferred.resolve();
+  }
+
+  aPopup.addEventListener(eventType, onPopupEvent);
+  return deferred.promise;
+}
+
+// This is a simpler version of the context menu check that
+// exists in contextmenu_common.js.
+function checkContextMenu(aContextMenu, aExpectedEntries, aWindow=window) {
+  let childNodes = [...aContextMenu.childNodes];
+  // Ignore hidden nodes:
+  childNodes = childNodes.filter((n) => !n.hidden);
+
+  for (let i = 0; i < childNodes.length; i++) {
+    let menuitem = childNodes[i];
+    try {
+      if (aExpectedEntries[i][0] == "---") {
+        is(menuitem.localName, "menuseparator", "menuseparator expected");
+        continue;
+      }
+
+      let selector = aExpectedEntries[i][0];
+      ok(menuitem.matches(selector), "menuitem should match " + selector + " selector");
+      let commandValue = menuitem.getAttribute("command");
+      let relatedCommand = commandValue ? aWindow.document.getElementById(commandValue) : null;
+      let menuItemDisabled = relatedCommand ?
+                               relatedCommand.getAttribute("disabled") == "true" :
+                               menuitem.getAttribute("disabled") == "true";
+      is(menuItemDisabled, !aExpectedEntries[i][1], "disabled state for " + selector);
+    } catch (e) {
+      ok(false, "Exception when checking context menu: " + e);
+    }
+  }
+}
diff --git a/browser/components/customizableui/test/support/feeds_test_page.html b/browser/components/customizableui/test/support/feeds_test_page.html
new file mode 100644
index 000000000..be78e4dff
--- /dev/null
+++ b/browser/components/customizableui/test/support/feeds_test_page.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+  <title>Feeds test page</title>
+  <link rel="alternate" type="application/rss+xml" href="test-feed.xml" title="Test feed">
+</head>
+
+<body>
+  This is a test page for feeds
+</body>
+</html>
diff --git a/browser/components/customizableui/test/support/test-feed.xml b/browser/components/customizableui/test/support/test-feed.xml
new file mode 100644
index 000000000..0e700b6d8
--- /dev/null
+++ b/browser/components/customizableui/test/support/test-feed.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+
+  <title>Example Feed</title>
+  <link href="http://example.org/"/>
+  <updated>2010-08-22T18:30:02Z</updated>
+
+  <author>
+    <name>John Doe</name>
+  </author>
+  <id>urn:uuid:e2df8375-99be-4848-b05e-b9d407555267</id>
+
+  <entry>
+
+    <title>Item</title>
+    <link href="http://example.org/first"/>
+    <id>urn:uuid:9e0f4bed-33d3-4a9d-97ab-ecaa31b3f14a</id>
+    <updated>2010-08-22T18:30:02Z</updated>
+
+    <summary>Some text.</summary>
+  </entry>
+
+</feed>
diff --git a/browser/components/customizableui/test/support/test_967000_charEncoding_page.html b/browser/components/customizableui/test/support/test_967000_charEncoding_page.html
new file mode 100644
index 000000000..c8d35115c
--- /dev/null
+++ b/browser/components/customizableui/test/support/test_967000_charEncoding_page.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Test page</title>
+  </head>
+
+  <body>
+  This is a test page
+  </body>
+</html>
-- 
cgit v1.2.3