Merge autoland to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 02 Nov 2016 17:15:58 -0700
changeset 347228 ac55a6776435142feebf3c20bbabfee100686416
parent 347082 3bfde35a0d18a643485ffd5073f3bc6a79e0ae48 (current diff)
parent 347227 3e357271c0f733762d9cc5f5c70112b655e07a1e (diff)
child 347426 ade8d4a63e57560410de106450f37b50ed71cca5
child 347467 10c5b8d1bef5a8220c13d77127cac8bd1487eb43
child 347506 227bccbfea15ddf0b0105cdecf54c77fbeb87ad3
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
Merge autoland to m-c a=merge
dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
dom/bluetooth/bluedroid/BluetoothA2dpManager.h
dom/bluetooth/bluedroid/BluetoothAvrcpManager.cpp
dom/bluetooth/bluedroid/BluetoothAvrcpManager.h
dom/bluetooth/bluedroid/BluetoothDaemonA2dpInterface.cpp
dom/bluetooth/bluedroid/BluetoothDaemonA2dpInterface.h
dom/bluetooth/bluedroid/BluetoothDaemonAvrcpInterface.cpp
dom/bluetooth/bluedroid/BluetoothDaemonAvrcpInterface.h
dom/bluetooth/bluedroid/BluetoothDaemonCoreInterface.cpp
dom/bluetooth/bluedroid/BluetoothDaemonCoreInterface.h
dom/bluetooth/bluedroid/BluetoothDaemonGattInterface.cpp
dom/bluetooth/bluedroid/BluetoothDaemonGattInterface.h
dom/bluetooth/bluedroid/BluetoothDaemonHandsfreeInterface.cpp
dom/bluetooth/bluedroid/BluetoothDaemonHandsfreeInterface.h
dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp
dom/bluetooth/bluedroid/BluetoothDaemonHelpers.h
dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.cpp
dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.h
dom/bluetooth/bluedroid/BluetoothDaemonInterface.cpp
dom/bluetooth/bluedroid/BluetoothDaemonInterface.h
dom/bluetooth/bluedroid/BluetoothDaemonSetupInterface.cpp
dom/bluetooth/bluedroid/BluetoothDaemonSetupInterface.h
dom/bluetooth/bluedroid/BluetoothDaemonSocketInterface.cpp
dom/bluetooth/bluedroid/BluetoothDaemonSocketInterface.h
dom/bluetooth/bluedroid/BluetoothGattManager.cpp
dom/bluetooth/bluedroid/BluetoothGattManager.h
dom/bluetooth/bluedroid/BluetoothHidManager.cpp
dom/bluetooth/bluedroid/BluetoothHidManager.h
dom/bluetooth/bluedroid/BluetoothMapBMessage.cpp
dom/bluetooth/bluedroid/BluetoothMapBMessage.h
dom/bluetooth/bluedroid/BluetoothMapFolder.cpp
dom/bluetooth/bluedroid/BluetoothMapFolder.h
dom/bluetooth/bluedroid/BluetoothMapSmsManager.cpp
dom/bluetooth/bluedroid/BluetoothMapSmsManager.h
dom/bluetooth/bluedroid/BluetoothOppManager.cpp
dom/bluetooth/bluedroid/BluetoothOppManager.h
dom/bluetooth/bluedroid/BluetoothPbapManager.cpp
dom/bluetooth/bluedroid/BluetoothPbapManager.h
dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
dom/bluetooth/bluedroid/BluetoothServiceBluedroid.h
dom/bluetooth/bluedroid/BluetoothSocket.cpp
dom/bluetooth/bluedroid/BluetoothSocket.h
dom/bluetooth/bluedroid/BluetoothSocketMessageWatcher.cpp
dom/bluetooth/bluedroid/BluetoothSocketMessageWatcher.h
dom/bluetooth/bluedroid/hfp-fallback/BluetoothHfpManager.cpp
dom/bluetooth/bluedroid/hfp-fallback/BluetoothHfpManager.h
dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.h
dom/bluetooth/bluez/BluetoothA2dpManager.cpp
dom/bluetooth/bluez/BluetoothA2dpManager.h
dom/bluetooth/bluez/BluetoothAvrcpManager.cpp
dom/bluetooth/bluez/BluetoothAvrcpManager.h
dom/bluetooth/bluez/BluetoothDBusService.cpp
dom/bluetooth/bluez/BluetoothDBusService.h
dom/bluetooth/bluez/BluetoothHfpManager.cpp
dom/bluetooth/bluez/BluetoothHfpManager.h
dom/bluetooth/bluez/BluetoothHidManager.cpp
dom/bluetooth/bluez/BluetoothHidManager.h
dom/bluetooth/bluez/BluetoothOppManager.cpp
dom/bluetooth/bluez/BluetoothOppManager.h
dom/bluetooth/bluez/BluetoothSocket.cpp
dom/bluetooth/bluez/BluetoothSocket.h
dom/bluetooth/bluez/BluetoothUnixSocketConnector.cpp
dom/bluetooth/bluez/BluetoothUnixSocketConnector.h
dom/bluetooth/common/BluetoothCommon.cpp
dom/bluetooth/common/BluetoothCommon.h
dom/bluetooth/common/BluetoothGattReplyRunnable.cpp
dom/bluetooth/common/BluetoothGattReplyRunnable.h
dom/bluetooth/common/BluetoothGattUUIDName.h
dom/bluetooth/common/BluetoothHashKeys.h
dom/bluetooth/common/BluetoothHfpManagerBase.h
dom/bluetooth/common/BluetoothInterface.cpp
dom/bluetooth/common/BluetoothInterface.h
dom/bluetooth/common/BluetoothProfileController.cpp
dom/bluetooth/common/BluetoothProfileController.h
dom/bluetooth/common/BluetoothProfileManagerBase.h
dom/bluetooth/common/BluetoothReplyRunnable.cpp
dom/bluetooth/common/BluetoothReplyRunnable.h
dom/bluetooth/common/BluetoothRilListener.cpp
dom/bluetooth/common/BluetoothRilListener.h
dom/bluetooth/common/BluetoothService.cpp
dom/bluetooth/common/BluetoothService.h
dom/bluetooth/common/BluetoothSocketObserver.h
dom/bluetooth/common/BluetoothUtils.cpp
dom/bluetooth/common/BluetoothUtils.h
dom/bluetooth/common/BluetoothUuidHelper.cpp
dom/bluetooth/common/BluetoothUuidHelper.h
dom/bluetooth/common/ObexBase.cpp
dom/bluetooth/common/ObexBase.h
dom/bluetooth/common/webapi/BluetoothAdapter.cpp
dom/bluetooth/common/webapi/BluetoothAdapter.h
dom/bluetooth/common/webapi/BluetoothClassOfDevice.cpp
dom/bluetooth/common/webapi/BluetoothClassOfDevice.h
dom/bluetooth/common/webapi/BluetoothDevice.cpp
dom/bluetooth/common/webapi/BluetoothDevice.h
dom/bluetooth/common/webapi/BluetoothDiscoveryHandle.cpp
dom/bluetooth/common/webapi/BluetoothDiscoveryHandle.h
dom/bluetooth/common/webapi/BluetoothGatt.cpp
dom/bluetooth/common/webapi/BluetoothGatt.h
dom/bluetooth/common/webapi/BluetoothGattAttributeEvent.cpp
dom/bluetooth/common/webapi/BluetoothGattAttributeEvent.h
dom/bluetooth/common/webapi/BluetoothGattCharacteristic.cpp
dom/bluetooth/common/webapi/BluetoothGattCharacteristic.h
dom/bluetooth/common/webapi/BluetoothGattDescriptor.cpp
dom/bluetooth/common/webapi/BluetoothGattDescriptor.h
dom/bluetooth/common/webapi/BluetoothGattServer.cpp
dom/bluetooth/common/webapi/BluetoothGattServer.h
dom/bluetooth/common/webapi/BluetoothGattService.cpp
dom/bluetooth/common/webapi/BluetoothGattService.h
dom/bluetooth/common/webapi/BluetoothLeDeviceEvent.cpp
dom/bluetooth/common/webapi/BluetoothLeDeviceEvent.h
dom/bluetooth/common/webapi/BluetoothManager.cpp
dom/bluetooth/common/webapi/BluetoothManager.h
dom/bluetooth/common/webapi/BluetoothMapRequestHandle.cpp
dom/bluetooth/common/webapi/BluetoothMapRequestHandle.h
dom/bluetooth/common/webapi/BluetoothObexAuthHandle.cpp
dom/bluetooth/common/webapi/BluetoothObexAuthHandle.h
dom/bluetooth/common/webapi/BluetoothPairingHandle.cpp
dom/bluetooth/common/webapi/BluetoothPairingHandle.h
dom/bluetooth/common/webapi/BluetoothPairingListener.cpp
dom/bluetooth/common/webapi/BluetoothPairingListener.h
dom/bluetooth/common/webapi/BluetoothPbapRequestHandle.cpp
dom/bluetooth/common/webapi/BluetoothPbapRequestHandle.h
dom/bluetooth/common/webapi/BluetoothUUID.cpp
dom/bluetooth/common/webapi/BluetoothUUID.h
dom/bluetooth/ipc/BluetoothChild.cpp
dom/bluetooth/ipc/BluetoothChild.h
dom/bluetooth/ipc/BluetoothMessageUtils.h
dom/bluetooth/ipc/BluetoothParent.cpp
dom/bluetooth/ipc/BluetoothParent.h
dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
dom/bluetooth/ipc/BluetoothServiceChildProcess.h
dom/bluetooth/ipc/BluetoothTypes.ipdlh
dom/bluetooth/ipc/PBluetooth.ipdl
dom/bluetooth/ipc/PBluetoothRequest.ipdl
dom/bluetooth/moz.build
dom/bluetooth/tests/marionette/head.js
dom/bluetooth/tests/marionette/manifest.ini
dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_discovery.js
dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_enable.js
dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_pair.js
dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_setters.js
dom/bluetooth/tests/marionette/test_dom_BluetoothDevice.js
dom/bluetooth/tests/marionette/test_dom_BluetoothManager.js
dom/contacts/ContactManager.js
dom/contacts/ContactManager.manifest
dom/contacts/fallback/ContactDB.jsm
dom/contacts/fallback/ContactService.jsm
dom/contacts/moz.build
dom/contacts/tests/chrome.ini
dom/contacts/tests/file_contacts_basics.html
dom/contacts/tests/file_contacts_basics2.html
dom/contacts/tests/file_contacts_blobs.html
dom/contacts/tests/file_contacts_events.html
dom/contacts/tests/file_contacts_getall.html
dom/contacts/tests/file_contacts_getall2.html
dom/contacts/tests/file_contacts_international.html
dom/contacts/tests/file_contacts_substringmatching.html
dom/contacts/tests/file_contacts_substringmatchingCL.html
dom/contacts/tests/file_contacts_substringmatchingVE.html
dom/contacts/tests/file_migration.html
dom/contacts/tests/mochitest.ini
dom/contacts/tests/shared.js
dom/contacts/tests/test_contacts_a_cache.xul
dom/contacts/tests/test_contacts_a_shutdown.xul
dom/contacts/tests/test_contacts_a_upgrade.xul
dom/contacts/tests/test_contacts_basics.html
dom/contacts/tests/test_contacts_basics2.html
dom/contacts/tests/test_contacts_blobs.html
dom/contacts/tests/test_contacts_events.html
dom/contacts/tests/test_contacts_getall.html
dom/contacts/tests/test_contacts_getall2.html
dom/contacts/tests/test_contacts_international.html
dom/contacts/tests/test_contacts_substringmatching.html
dom/contacts/tests/test_contacts_substringmatchingCL.html
dom/contacts/tests/test_contacts_substringmatchingVE.html
dom/contacts/tests/test_migration.html
dom/contacts/tests/test_migration_chrome.js
dom/contacts/tests/test_permission_denied.html
dom/icc/Assertions.cpp
dom/icc/Icc.cpp
dom/icc/Icc.h
dom/icc/IccCallback.cpp
dom/icc/IccCallback.h
dom/icc/IccCardLockError.cpp
dom/icc/IccCardLockError.h
dom/icc/IccContact.cpp
dom/icc/IccContact.h
dom/icc/IccInfo.cpp
dom/icc/IccInfo.h
dom/icc/IccListener.cpp
dom/icc/IccListener.h
dom/icc/IccManager.cpp
dom/icc/IccManager.h
dom/icc/gonk/IccService.js
dom/icc/gonk/StkCmdFactory.js
dom/icc/interfaces/moz.build
dom/icc/interfaces/nsIGonkIccService.idl
dom/icc/interfaces/nsIIccInfo.idl
dom/icc/interfaces/nsIIccMessenger.idl
dom/icc/interfaces/nsIIccService.idl
dom/icc/interfaces/nsIStkCmdFactory.idl
dom/icc/interfaces/nsIStkProactiveCmd.idl
dom/icc/ipc/IccChild.cpp
dom/icc/ipc/IccChild.h
dom/icc/ipc/IccIPCService.cpp
dom/icc/ipc/IccIPCService.h
dom/icc/ipc/IccIPCUtils.cpp
dom/icc/ipc/IccParent.cpp
dom/icc/ipc/IccParent.h
dom/icc/ipc/PIcc.ipdl
dom/icc/ipc/PIccRequest.ipdl
dom/icc/ipc/PIccTypes.ipdlh
dom/icc/moz.build
dom/icc/tests/marionette/head.js
dom/icc/tests/marionette/manifest.ini
dom/icc/tests/marionette/test_icc_access_invalid_object.js
dom/icc/tests/marionette/test_icc_card_lock_change_pin.js
dom/icc/tests/marionette/test_icc_card_lock_get_retry_count.js
dom/icc/tests/marionette/test_icc_card_state.js
dom/icc/tests/marionette/test_icc_contact_add.js
dom/icc/tests/marionette/test_icc_contact_read.js
dom/icc/tests/marionette/test_icc_contact_update.js
dom/icc/tests/marionette/test_icc_detected_undetected_event.js
dom/icc/tests/marionette/test_icc_info.js
dom/icc/tests/marionette/test_icc_match_mvno.js
dom/icc/tests/marionette/test_stk_bip_command.js
dom/icc/tests/marionette/test_stk_display_text.js
dom/icc/tests/marionette/test_stk_get_inkey.js
dom/icc/tests/marionette/test_stk_get_input.js
dom/icc/tests/marionette/test_stk_launch_browser.js
dom/icc/tests/marionette/test_stk_local_info.js
dom/icc/tests/marionette/test_stk_poll_off.js
dom/icc/tests/marionette/test_stk_refresh.js
dom/icc/tests/marionette/test_stk_select_item.js
dom/icc/tests/marionette/test_stk_send_dtmf.js
dom/icc/tests/marionette/test_stk_send_sms.js
dom/icc/tests/marionette/test_stk_send_ss.js
dom/icc/tests/marionette/test_stk_send_ussd.js
dom/icc/tests/marionette/test_stk_setup_call.js
dom/icc/tests/marionette/test_stk_setup_event_list.js
dom/icc/tests/marionette/test_stk_setup_idle_mode_text.js
dom/icc/tests/marionette/test_stk_setup_menu.js
dom/icc/tests/marionette/test_stk_timer_management.js
dom/mobileconnection/Assertions.cpp
dom/mobileconnection/MobileCallForwardingOptions.cpp
dom/mobileconnection/MobileCallForwardingOptions.h
dom/mobileconnection/MobileCellInfo.cpp
dom/mobileconnection/MobileCellInfo.h
dom/mobileconnection/MobileConnection.cpp
dom/mobileconnection/MobileConnection.h
dom/mobileconnection/MobileConnectionArray.cpp
dom/mobileconnection/MobileConnectionArray.h
dom/mobileconnection/MobileConnectionCallback.cpp
dom/mobileconnection/MobileConnectionCallback.h
dom/mobileconnection/MobileConnectionInfo.cpp
dom/mobileconnection/MobileConnectionInfo.h
dom/mobileconnection/MobileNetworkInfo.cpp
dom/mobileconnection/MobileNetworkInfo.h
dom/mobileconnection/gonk/MobileConnectionService.js
dom/mobileconnection/gonk/MobileConnectionService.manifest
dom/mobileconnection/gonk/nsIGonkMobileConnectionService.idl
dom/mobileconnection/gonk/nsIMobileConnectionMessenger.idl
dom/mobileconnection/interfaces/nsICellInfo.idl
dom/mobileconnection/interfaces/nsIMobileConnectionService.idl
dom/mobileconnection/interfaces/nsINeighboringCellInfo.idl
dom/mobileconnection/ipc/MobileConnectionChild.cpp
dom/mobileconnection/ipc/MobileConnectionChild.h
dom/mobileconnection/ipc/MobileConnectionIPCSerializer.h
dom/mobileconnection/ipc/MobileConnectionIPCService.cpp
dom/mobileconnection/ipc/MobileConnectionIPCService.h
dom/mobileconnection/ipc/MobileConnectionParent.cpp
dom/mobileconnection/ipc/MobileConnectionParent.h
dom/mobileconnection/ipc/PMobileConnection.ipdl
dom/mobileconnection/ipc/PMobileConnectionRequest.ipdl
dom/mobileconnection/ipc/PMobileConnectionTypes.ipdlh
dom/mobileconnection/moz.build
dom/mobileconnection/tests/marionette/head.js
dom/mobileconnection/tests/marionette/head_chrome.js
dom/mobileconnection/tests/marionette/manifest.ini
dom/mobileconnection/tests/marionette/test_call_barring_basic_operations.js
dom/mobileconnection/tests/marionette/test_call_barring_change_password.js
dom/mobileconnection/tests/marionette/test_call_barring_get_error.js
dom/mobileconnection/tests/marionette/test_call_barring_set_error.js
dom/mobileconnection/tests/marionette/test_dsds_mobile_data_connection.js
dom/mobileconnection/tests/marionette/test_mobile_call_forwarding.js
dom/mobileconnection/tests/marionette/test_mobile_call_forwarding_get_error.js
dom/mobileconnection/tests/marionette/test_mobile_call_forwarding_set_error.js
dom/mobileconnection/tests/marionette/test_mobile_cell_Info_list.js
dom/mobileconnection/tests/marionette/test_mobile_clir_radio_off.js
dom/mobileconnection/tests/marionette/test_mobile_connections_array_uninitialized.js
dom/mobileconnection/tests/marionette/test_mobile_data_connection.js
dom/mobileconnection/tests/marionette/test_mobile_data_ipv6.js
dom/mobileconnection/tests/marionette/test_mobile_data_location.js
dom/mobileconnection/tests/marionette/test_mobile_data_state.js
dom/mobileconnection/tests/marionette/test_mobile_icc_change.js
dom/mobileconnection/tests/marionette/test_mobile_last_known_network.js
dom/mobileconnection/tests/marionette/test_mobile_neighboring_cell_ids.js
dom/mobileconnection/tests/marionette/test_mobile_networks.js
dom/mobileconnection/tests/marionette/test_mobile_operator_names.js
dom/mobileconnection/tests/marionette/test_mobile_operator_names_plmnlist.js
dom/mobileconnection/tests/marionette/test_mobile_operator_names_roaming.js
dom/mobileconnection/tests/marionette/test_mobile_preferred_network_type.js
dom/mobileconnection/tests/marionette/test_mobile_roaming_preference.js
dom/mobileconnection/tests/marionette/test_mobile_set_radio.js
dom/mobileconnection/tests/marionette/test_mobile_signal_strength.js
dom/mobileconnection/tests/marionette/test_mobile_voice_location.js
dom/mobileconnection/tests/marionette/test_mobile_voice_state.js
dom/mobileconnection/tests/mochitest/test_mobileconnection_permission.html
dom/mobileconnection/tests/mochitest/test_mobilenetwork_permission.html
dom/permission/tests/mochitest-bt.ini
dom/permission/tests/mochitest-ril.ini
dom/permission/tests/test_bluetooth.html
dom/permission/tests/test_mobileconnection.html
dom/webidl/BluetoothAdapter.webidl
dom/webidl/BluetoothAdapterEvent.webidl
dom/webidl/BluetoothAttributeEvent.webidl
dom/webidl/BluetoothClassOfDevice.webidl
dom/webidl/BluetoothDevice.webidl
dom/webidl/BluetoothDeviceEvent.webidl
dom/webidl/BluetoothDiscoveryHandle.webidl
dom/webidl/BluetoothGatt.webidl
dom/webidl/BluetoothGattAttributeEvent.webidl
dom/webidl/BluetoothGattCharacteristic.webidl
dom/webidl/BluetoothGattCharacteristicEvent.webidl
dom/webidl/BluetoothGattDescriptor.webidl
dom/webidl/BluetoothGattServer.webidl
dom/webidl/BluetoothGattService.webidl
dom/webidl/BluetoothLeDeviceEvent.webidl
dom/webidl/BluetoothManager.webidl
dom/webidl/BluetoothMapFolderListingEvent.webidl
dom/webidl/BluetoothMapGetMessageEvent.webidl
dom/webidl/BluetoothMapMessageUpdateEvent.webidl
dom/webidl/BluetoothMapMessagesListingEvent.webidl
dom/webidl/BluetoothMapParameters.webidl
dom/webidl/BluetoothMapRequestHandle.webidl
dom/webidl/BluetoothMapSendMessageEvent.webidl
dom/webidl/BluetoothMapSetMessageStatusEvent.webidl
dom/webidl/BluetoothObexAuthEvent.webidl
dom/webidl/BluetoothObexAuthHandle.webidl
dom/webidl/BluetoothPairingEvent.webidl
dom/webidl/BluetoothPairingHandle.webidl
dom/webidl/BluetoothPairingListener.webidl
dom/webidl/BluetoothPbapParameters.webidl
dom/webidl/BluetoothPbapRequestHandle.webidl
dom/webidl/BluetoothPhonebookPullingEvent.webidl
dom/webidl/BluetoothStatusChangedEvent.webidl
dom/webidl/BluetoothUUID.webidl
dom/webidl/BluetoothVCardListingEvent.webidl
dom/webidl/BluetoothVCardPullingEvent.webidl
dom/webidl/CFStateChangeEvent.webidl
dom/webidl/Contacts.webidl
dom/webidl/DataErrorEvent.webidl
dom/webidl/IccCardLockError.webidl
dom/webidl/IccChangeEvent.webidl
dom/webidl/MozClirModeEvent.webidl
dom/webidl/MozContactChangeEvent.webidl
dom/webidl/MozEmergencyCbModeEvent.webidl
dom/webidl/MozIcc.webidl
dom/webidl/MozIccInfo.webidl
dom/webidl/MozIccManager.webidl
dom/webidl/MozMobileConnection.webidl
dom/webidl/MozMobileConnectionArray.webidl
dom/webidl/MozMobileNetworkInfo.webidl
dom/webidl/MozOtaStatusEvent.webidl
dom/webidl/MozStkCommandEvent.webidl
--- a/Makefile.in
+++ b/Makefile.in
@@ -277,18 +277,18 @@ else
 DUMP_SYMS_BIN ?= $(topsrcdir)/toolkit/crashreporter/tools/win32/dump_syms_vc$(_MSC_VER).exe
 endif
 # PDB files don't get moved to dist, so we need to scan the whole objdir
 MAKE_SYM_STORE_PATH := .
 endif
 ifeq ($(OS_ARCH),Darwin)
 # need to pass arch flags for universal builds
 ifdef UNIVERSAL_BINARY
-MAKE_SYM_STORE_ARGS := -c -a 'i386 x86_64' --vcs-info
-MAKE_SYM_STORE_PATH := $(DIST)/universal
+MAKE_SYM_STORE_ARGS := -c --vcs-info
+MAKE_SYM_STORE_PATH := $(DIST)/bin $(UNIFY_DIST)/bin
 else
 MAKE_SYM_STORE_ARGS := -c -a $(OS_TEST) --vcs-info
 MAKE_SYM_STORE_PATH := $(DIST)/bin
 endif
 DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms
 endif
 ifeq (,$(filter-out Linux SunOS,$(OS_ARCH)))
 MAKE_SYM_STORE_ARGS := -c --vcs-info
--- a/accessible/ipc/win/moz.build
+++ b/accessible/ipc/win/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-DIRS += ['typelib']
+if CONFIG['COMPILE_ENVIRONMENT']:
+    DIRS += ['typelib']
 
 # With --disable-accessibility, we need to compile PDocAccessible.ipdl (which
 # also depends on COMPtrTypes.h), but not the C++.
 IPDL_SOURCES += ['PDocAccessible.ipdl']
 EXPORTS.mozilla.a11y += ['COMPtrTypes.h']
 
 if CONFIG['ACCESSIBILITY']:
     EXPORTS.mozilla.a11y += [
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -995,23 +995,16 @@ pref("dom.webnotifications.serviceworker
 #endif
 
 // Retain at most 10 processes' layers buffers
 pref("layers.compositor-lru-size", 10);
 
 // In B2G by deafult any AudioChannelAgent is muted when created.
 pref("dom.audiochannel.mutedByDefault", true);
 
-// The app origin of bluetooth app, which is responsible for listening pairing
-// requests.
-pref("dom.bluetooth.app-origin", "app://bluetooth.gaiamobile.org");
-
-// Enable W3C WebBluetooth API and disable B2G only GATT client API.
-pref("dom.bluetooth.webbluetooth.enabled", false);
-
 // Default device name for Presentation API
 pref("dom.presentation.device.name", "Firefox OS");
 
 // Enable notification of performance timing
 pref("dom.performance.enable_notify_performance_timing", true);
 
 // Multi-screen
 pref("b2g.multiscreen.chrome_remote_url", "chrome://b2g/content/shell_remote.html");
--- a/b2g/common.configure
+++ b/b2g/common.configure
@@ -4,16 +4,17 @@
 # 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/.
 
 # Truetype fonts for B2G
 # ==============================================================
 option(env='MOZTTDIR', nargs=1, help='Path to truetype fonts for B2G')
 
 @depends('MOZTTDIR')
+@imports('os')
 def mozttdir(value):
     if value:
         path = value[0]
         if not os.path.isdir(path):
             die('MOZTTDIR "%s" is not a valid directory', path)
         return path
 
 set_config('MOZTTDIR', mozttdir)
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -155,19 +155,16 @@
 @RESPATH@/components/dom_workers.xpt
 #ifdef MOZ_WIDGET_GONK
 @RESPATH@/components/dom_wifi.xpt
 @RESPATH@/components/dom_system_gonk.xpt
 #endif
 #ifdef MOZ_B2G_RIL
 @RESPATH@/components/dom_mobileconnection.xpt
 #endif
-#ifdef MOZ_B2G_BT
-@RESPATH@/components/dom_bluetooth.xpt
-#endif
 @RESPATH@/components/dom_canvas.xpt
 @RESPATH@/components/dom_contacts.xpt
 @RESPATH@/components/dom_core.xpt
 @RESPATH@/components/dom_css.xpt
 @RESPATH@/components/dom_events.xpt
 @RESPATH@/components/dom_geolocation.xpt
 @RESPATH@/components/dom_media.xpt
 @RESPATH@/components/dom_network.xpt
new file mode 100644
--- /dev/null
+++ b/browser/base/.eslintrc.js
@@ -0,0 +1,11 @@
+"use strict";
+
+module.exports = {
+  "rules": {
+    "no-unused-vars": ["error", {
+      "vars": "local",
+      "varsIgnorePattern": "^Cc|Ci|Cu|Cr|EXPORTED_SYMBOLS",
+      "args": "none",
+    }]
+  }
+};
--- a/browser/base/content/aboutDialog-appUpdater.js
+++ b/browser/base/content/aboutDialog-appUpdater.js
@@ -361,17 +361,16 @@ appUpdater.prototype =
       break;
     case Components.results.NS_BINDING_ABORTED:
       // Do not remove UI listener since the user may resume downloading again.
       break;
     case Components.results.NS_OK:
       this.removeDownloadListener();
       if (this.backgroundUpdateEnabled) {
         this.selectPanel("applying");
-        let update = this.um.activeUpdate;
         let self = this;
         Services.obs.addObserver(function (aSubject, aTopic, aData) {
           // Update the UI when the background updater is finished
           let status = aData;
           if (status == "applied" || status == "applied-service" ||
               status == "pending" || status == "pending-service" ||
               status == "pending-elevate") {
             // If the update is successfully applied, or if the updater has
--- a/browser/base/content/browser-data-submission-info-bar.js
+++ b/browser/base/content/browser-data-submission-info-bar.js
@@ -64,17 +64,17 @@ var gDataNotificationInfoBar = {
       popup: null,
       callback: () => {
         this._actionTaken = true;
         window.openAdvancedPreferences("dataChoicesTab");
       },
     }];
 
     this._log.info("Creating data reporting policy notification.");
-    let notification = this._notificationBox.appendNotification(
+    this._notificationBox.appendNotification(
       message,
       this._DATA_REPORTING_NOTIFICATION,
       null,
       this._notificationBox.PRIORITY_INFO_HIGH,
       buttons,
       event => {
         if (event == "removed") {
           Services.obs.notifyObservers(null, "datareporting:notify-data-policy:close", null);
@@ -120,9 +120,8 @@ var gDataNotificationInfoBar = {
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference,
   ]),
 };
-
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -747,19 +747,16 @@ HistoryMenu.prototype = {
   },
 
   /**
    * Populate when the history menu is opened
    */
   populateUndoWindowSubmenu: function PHM_populateUndoWindowSubmenu() {
     let undoMenu = this._rootElt.getElementsByClassName("recentlyClosedWindowsMenu")[0];
     let undoPopup = undoMenu.firstChild;
-    let menuLabelString = gNavigatorBundle.getString("menuUndoCloseWindowLabel");
-    let menuLabelStringSingleTab =
-      gNavigatorBundle.getString("menuUndoCloseWindowSingleTabLabel");
 
     // remove existing menu items
     while (undoPopup.hasChildNodes())
       undoPopup.removeChild(undoPopup.firstChild);
 
     // no restorable windows, so make sure menu is disabled, and return
     if (SessionStore.getClosedWindowCount() == 0) {
       undoMenu.setAttribute("disabled", true);
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -190,17 +190,16 @@ var gPluginHandler = {
         aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
         break;
       default:
         Cu.reportError(Error("Unexpected plugin state: " + aNewState));
         return;
     }
 
     let browser = aNotification.browser;
-    let contentWindow = browser.contentWindow;
     if (aNewState != "continue") {
       let principal = aNotification.options.principal;
       Services.perms.addFromPrincipal(principal, aPluginInfo.permissionString,
                                       permission, expireType, expireTime);
       aPluginInfo.pluginPermissionType = expireType;
     }
 
     browser.messageManager.sendAsyncMessage("BrowserPlugins:ActivatePlugins", {
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -436,20 +436,16 @@ var gSyncUI = {
       dateFormat = {month: 'long', day: 'numeric'};
     } else {
       dateFormat = {weekday: 'long', hour: 'numeric', minute: 'numeric'};
     }
     let lastSyncDateString = date.toLocaleDateString(undefined, dateFormat);
     return this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDateString], 1);
   },
 
-  onSyncFinish: function SUI_onSyncFinish() {
-    let title = this._stringBundle.GetStringFromName("error.sync.title");
-  },
-
   onClientsSynced: function() {
     let broadcaster = document.getElementById("sync-syncnow-state");
     if (broadcaster) {
       if (Weave.Service.clientsEngine.stats.numClients > 1) {
         broadcaster.setAttribute("devices-status", "multi");
       } else {
         broadcaster.setAttribute("devices-status", "single");
       }
@@ -479,17 +475,17 @@ var gSyncUI = {
       case "weave:service:sync:error":
         this.onActivityStop();
         break;
     }
     // Now non-activity state (eg, enabled, errors, etc)
     // Note that sync uses the ":ui:" notifications for errors because sync.
     switch (topic) {
       case "weave:ui:sync:finish":
-        this.onSyncFinish();
+        // Do nothing.
         break;
       case "weave:ui:sync:error":
       case "weave:service:setup-complete":
       case "weave:service:login:finish":
       case "weave:service:login:start":
       case "weave:service:start-over":
         this.updateUI();
         break;
--- a/browser/base/content/browser-tabsintitlebar.js
+++ b/browser/base/content/browser-tabsintitlebar.js
@@ -111,18 +111,16 @@ var TabsInTitlebar = {
 
     // In some edgecases it is possible for this to fire before we've initialized.
     // Don't run now, but don't forget to run it when we do initialize.
     if (!this._initialized) {
       this._updateOnInit = true;
       return;
     }
 
-    let allowed = true;
-
     if (!aForce) {
       // _update is called on resize events, because the window is not ready
       // after sizemode events. However, we only care about the event when the
       // sizemode is different from the last time we updated the appearance of
       // the tabs in the titlebar.
       let sizemode = document.documentElement.getAttribute("sizemode");
       if (this._lastSizeMode == sizemode) {
         return;
@@ -133,20 +131,17 @@ var TabsInTitlebar = {
       // still changing in the consequent "fullscreen" event. Code there will
       // call this function again when everything is ready.
       // See browser-fullScreen.js: FullScreen.toggle and bug 1173768.
       if (oldSizeMode == "fullscreen") {
         return;
       }
     }
 
-    for (let something in this._disallowed) {
-      allowed = false;
-      break;
-    }
+    let allowed = (Object.keys(this._disallowed)).length == 0;
 
     let titlebar = $("titlebar");
     let titlebarContent = $("titlebar-content");
     let menubar = $("toolbar-menubar");
 
     if (allowed) {
       // We set the tabsintitlebar attribute first so that our CSS for
       // tabsintitlebar manifests before we do our measurements.
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -45,16 +45,17 @@ Cu.import("resource://gre/modules/Notifi
   ["SimpleServiceDiscovery", "resource://gre/modules/SimpleServiceDiscovery.jsm"],
   ["SitePermissions", "resource:///modules/SitePermissions.jsm"],
   ["Social", "resource:///modules/Social.jsm"],
   ["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"],
   ["Task", "resource://gre/modules/Task.jsm"],
   ["TelemetryStopwatch", "resource://gre/modules/TelemetryStopwatch.jsm"],
   ["Translation", "resource:///modules/translation/Translation.jsm"],
   ["UITour", "resource:///modules/UITour.jsm"],
+  ["URLBarZoom", "resource:///modules/URLBarZoom.jsm"],
   ["UpdateUtils", "resource://gre/modules/UpdateUtils.jsm"],
   ["Weave", "resource://services-sync/main.js"],
   ["fxAccounts", "resource://gre/modules/FxAccounts.jsm"],
   ["gDevTools", "resource://devtools/client/framework/gDevTools.jsm"],
   ["gDevToolsBrowser", "resource://devtools/client/framework/gDevTools.jsm"],
   ["webrtcUI", "resource:///modules/webrtcUI.jsm", ]
 ].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource));
 
@@ -616,17 +617,17 @@ var gPopupBlockerObserver = {
     var target = aEvent.target;
     var popupReportIndex = target.getAttribute("popupReportIndex");
     let browser = target.popupReportBrowser;
     browser.unblockPopup(popupReportIndex);
   },
 
   showAllBlockedPopups: function (aBrowser)
   {
-    let popups = aBrowser.retrieveListOfBlockedPopups().then(popups => {
+    aBrowser.retrieveListOfBlockedPopups().then(popups => {
       for (let i = 0; i < popups.length; i++) {
         if (popups[i].popupWindowURIspec)
           aBrowser.unblockPopup(i);
       }
     }, null);
   },
 
   editPopupSettings: function ()
@@ -791,17 +792,16 @@ function gKeywordURIFixup({ target: brow
 function _loadURIWithFlags(browser, uri, params) {
   if (!uri) {
     uri = "about:blank";
   }
   let flags = params.flags || 0;
   let referrer = params.referrerURI;
   let referrerPolicy = ('referrerPolicy' in params ? params.referrerPolicy :
                         Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT);
-  let charset = params.charset;
   let postData = params.postData;
 
   let wasRemote = browser.isRemoteBrowser;
 
   let process = browser.isRemoteBrowser ? Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
                                         : Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
   let mustChangeProcess = gMultiProcessBrowser &&
                           !E10SUtils.canLoadURIInProcess(uri, process);
@@ -3099,17 +3099,16 @@ function mirrorMenuItemClicked(event) {
                                                            {service: event.originalTarget._service});
 }
 
 function populateMirrorTabMenu(popup) {
   popup.innerHTML = null;
   if (!Services.prefs.getBoolPref("browser.casting.enabled")) {
     return;
   }
-  let videoEl = this.target;
   let doc = popup.ownerDocument;
   let services = CastingApps.getServicesForMirroring();
   services.forEach(service => {
     let item = doc.createElement("menuitem");
     item.setAttribute("label", service.friendlyName);
     item._service = service;
     item.addEventListener("command", mirrorMenuItemClicked);
     popup.appendChild(item);
@@ -4452,26 +4451,23 @@ var XULBrowserWindow = {
           location == "") {  // Second condition is for new tabs, otherwise
                              // reload function is enabled until tab is refreshed.
         this.reloadCommand.setAttribute("disabled", "true");
       } else {
         this.reloadCommand.removeAttribute("disabled");
       }
 
       URLBarSetURI(aLocationURI);
-
       BookmarkingUI.onLocationChange();
-
       gIdentityHandler.onLocationChange();
-
       SocialUI.updateState();
-
       UITour.onLocationChange(location);
-
       gTabletModePageCounter.inc();
+      ReaderParent.updateReaderButton(gBrowser.selectedBrowser);
+      URLBarZoom.updateZoomButton(gBrowser.selectedBrowser, "browser-fullZoom:location-change");
 
       // Utility functions for disabling find
       var shouldDisableFind = function shouldDisableFind(aDocument) {
         let docElt = aDocument.documentElement;
         return docElt && docElt.getAttribute("disablefastfind") == "true";
       }
 
       var disableFindCommands = function disableFindCommands(aDisable) {
@@ -4515,17 +4511,16 @@ var XULBrowserWindow = {
           gBrowser.selectedTab.hasAttribute("customizemode")) {
         gCustomizeMode.enter();
       } else if (CustomizationHandler.isEnteringCustomizeMode ||
                  CustomizationHandler.isCustomizing()) {
         gCustomizeMode.exit();
       }
     }
     UpdateBackForwardCommands(gBrowser.webNavigation);
-    ReaderParent.updateReaderButton(gBrowser.selectedBrowser);
 
     gGestureSupport.restoreRotationState();
 
     // See bug 358202, when tabs are switched during a drag operation,
     // timers don't fire on windows (bug 203573)
     if (aRequest)
       setTimeout(function () { XULBrowserWindow.asyncUpdateUI(); }, 0);
     else
@@ -5638,17 +5633,17 @@ function handleDroppedLink(event, urlOrL
   let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
 
   let userContextId = gBrowser.selectedBrowser.getAttribute("usercontextid");
 
   // event is null if links are dropped in content process.
   // inBackground should be false, as it's loading into current browser.
   let inBackground = false;
   if (event) {
-    let inBackground = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
+    inBackground = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
     if (event.shiftKey)
       inBackground = !inBackground;
   }
 
   Task.spawn(function*() {
     let urls = [];
     let postDatas = [];
     for (let link of links) {
--- a/browser/base/content/contentSearchUI.js
+++ b/browser/base/content/contentSearchUI.js
@@ -4,17 +4,16 @@
 
 "use strict";
 
 this.ContentSearchUIController = (function () {
 
 const MAX_DISPLAYED_SUGGESTIONS = 6;
 const SUGGESTION_ID_PREFIX = "searchSuggestion";
 const ONE_OFF_ID_PREFIX = "oneOff";
-const CSS_URI = "chrome://browser/content/contentSearchUI.css";
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 /**
  * Creates a new object that manages search suggestions and their UI for a text
  * box.
  *
  * The UI consists of an html:table that's inserted into the DOM after the given
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -819,17 +819,16 @@ function onImageSelect()
     tree.flex = 0;
     makePreview(getSelectedRows(tree)[0]);
   }
 }
 
 // Makes the media preview (image, video, etc) for the selected row on the media tab.
 function makePreview(row)
 {
-  var imageTree = document.getElementById("imagetree");
   var item = gImageView.data[row][COL_IMAGE_NODE];
   var url = gImageView.data[row][COL_IMAGE_ADDRESS];
   var isBG = gImageView.data[row][COL_IMAGE_BG];
   var isAudio = false;
 
   setItemValue("imageurltext", url);
   setItemValue("imagetext", item.imageText);
   setItemValue("imagelongdesctext", item.longDesc);
@@ -1010,17 +1009,16 @@ var imagePermissionObserver = {
     if (document.getElementById("mediaPreviewBox").collapsed)
       return;
 
     if (aTopic == "perm-changed") {
       var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission);
       if (permission.type == "image") {
         var imageTree = document.getElementById("imagetree");
         var row = getSelectedRow(imageTree);
-        var item = gImageView.data[row][COL_IMAGE_NODE];
         var url = gImageView.data[row][COL_IMAGE_ADDRESS];
         if (permission.matchesURI(makeURI(url), true)) {
           makeBlockImage(url);
         }
       }
     }
   }
 }
--- a/browser/base/content/pageinfo/security.js
+++ b/browser/base/content/pageinfo/security.js
@@ -16,19 +16,16 @@ var security = {
 
   // Display the server certificate (static)
   viewCert : function () {
     var cert = security._cert;
     viewCertHelper(window, cert);
   },
 
   _getSecurityInfo : function() {
-    const nsIX509Cert = Components.interfaces.nsIX509Cert;
-    const nsIX509CertDB = Components.interfaces.nsIX509CertDB;
-    const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
     const nsISSLStatusProvider = Components.interfaces.nsISSLStatusProvider;
     const nsISSLStatus = Components.interfaces.nsISSLStatus;
 
     // We don't have separate info for a frame, return null until further notice
     // (see bug 138479)
     if (!this.windowInfo.isTopWindow)
       return null;
 
--- a/browser/base/content/safeMode.js
+++ b/browser/base/content/safeMode.js
@@ -60,17 +60,16 @@ function onExtra1() {
     return true;
   }
   // The reset dialog will handle starting the reset process if the user confirms.
   showResetDialog();
   return false;
 }
 
 function onLoad() {
-  let dialog = document.documentElement;
   if (appStartup.automaticSafeModeNecessary) {
     document.getElementById("autoSafeMode").hidden = false;
     document.getElementById("safeMode").hidden = true;
     if (ResetProfile.resetSupported()) {
       document.getElementById("resetProfile").hidden = false;
     } else {
       // Hide the reset button is it's not supported.
       document.documentElement.getButton("extra1").hidden = true;
--- a/browser/base/content/sync/aboutSyncTabs.js
+++ b/browser/base/content/sync/aboutSyncTabs.js
@@ -206,17 +206,17 @@ var RemoteTabViewer = {
 
   _generateWeaveTabList: function () {
     let engine = Weave.Service.engineManager.get("tabs");
     let list = this._tabsList;
 
     let seenURLs = new Set();
     let localURLs = engine.getOpenURLs();
 
-    for (let [guid, client] of Object.entries(engine.getAllClients())) {
+    for (let [, client] of Object.entries(engine.getAllClients())) {
       // Create the client node, but don't add it in-case we don't show any tabs
       let appendClient = true;
 
       client.tabs.forEach(function({title, urlHistory, icon}) {
         let url = urlHistory[0];
         if (!url || localURLs.has(url) || seenURLs.has(url)) {
           return;
         }
--- a/browser/base/content/sync/genericChange.js
+++ b/browser/base/content/sync/genericChange.js
@@ -27,17 +27,16 @@ var Change = {
 
   get _updatingPassphrase() {
     return this._dialogType == "UpdatePassphrase";
   },
 
   onLoad: function Change_onLoad() {
     /* Load labels */
     let introText = document.getElementById("introText");
-    let introText2 = document.getElementById("introText2");
     let warningText = document.getElementById("warningText");
 
     // load some other elements & info from the window
     this._dialog = document.getElementById("change-dialog");
     this._dialogType = window.arguments[0];
     this._duringSetup = window.arguments[1];
     this._status = document.getElementById("status");
     this._statusIcon = document.getElementById("statusIcon");
--- a/browser/base/content/sync/setup.js
+++ b/browser/base/content/sync/setup.js
@@ -792,17 +792,16 @@ var gSyncSetup = {
     }, 1000);
   },
 
   checkServer: function () {
     delete this._checkServerTimer;
     let el = document.getElementById("server");
     let valid = false;
     let feedback = document.getElementById("serverFeedbackRow");
-    let str = "";
     if (el.value) {
       valid = this._validateServer(el);
       let str = valid ? "" : "serverInvalid.label";
       this._setFeedbackMessage(feedback, valid, str);
     }
     else
       this._setFeedbackMessage(feedback, true);
 
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -267,17 +267,16 @@ var AboutReaderListener = {
     addEventListener("pagehide", this, false);
     addMessageListener("Reader:ToggleReaderMode", this);
     addMessageListener("Reader:PushState", this);
   },
 
   receiveMessage: function(message) {
     switch (message.name) {
       case "Reader:ToggleReaderMode":
-        let url = content.document.location.href;
         if (!this.isAboutReader) {
           this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
           ReaderMode.enterReaderMode(docShell, content);
         } else {
           this._isLeavingReaderMode = true;
           ReaderMode.leaveReaderMode(docShell, content);
         }
         break;
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1761,17 +1761,17 @@
 
             // Restore the securityUI state.
             let securityUI = aBrowser.securityUI;
             let state = securityUI ? securityUI.state
                                    : Ci.nsIWebProgressListener.STATE_IS_INSECURE;
             // Include the true final argument to indicate that this event is
             // simulated (instead of being observed by the webProgressListener).
             this._callProgressListeners(aBrowser, "onSecurityChange",
-                                        [aBrowser.webProgress, null, securityUI.state, true],
+                                        [aBrowser.webProgress, null, state, true],
                                         true, false);
 
             if (aShouldBeRemote) {
               // Switching the browser to be remote will connect to a new child
               // process so the browser can no longer be considered to be
               // crashed.
               tab.removeAttribute("crashed");
             } else {
@@ -3783,17 +3783,17 @@
             },
 
             // This function runs before every event. It fixes up the state
             // to account for closed tabs.
             preActions: function() {
               this.assert(this.tabbrowser._switcher);
               this.assert(this.tabbrowser._switcher === this);
 
-              for (let [tab, state] of this.tabState) {
+              for (let [tab, ] of this.tabState) {
                 if (!tab.linkedBrowser) {
                   this.tabState.delete(tab);
                 }
               }
 
               if (this.lastVisibleTab && !this.lastVisibleTab.linkedBrowser) {
                 this.lastVisibleTab = null;
               }
@@ -4742,17 +4742,16 @@
               this._findAsYouType = Services.prefs.getBoolPref("accessibility.typeaheadfind");
               break;
           }
         ]]></body>
       </method>
 
       <constructor>
         <![CDATA[
-          let browserStack = document.getAnonymousElementByAttribute(this, "anonid", "browserStack");
           this.mCurrentBrowser = document.getAnonymousElementByAttribute(this, "anonid", "initialBrowser");
           this.mCurrentBrowser.permanentKey = {};
 
           Services.obs.addObserver(this, "live-resize-start", false);
           Services.obs.addObserver(this, "live-resize-end", false);
 
           this.mCurrentTab = this.tabContainer.firstChild;
           const nsIEventListenerService =
@@ -5072,38 +5071,52 @@
           var browser = event.originalTarget;
           var tab = this.getTabForBrowser(browser);
           if (!tab)
             return;
 
           clearTimeout(tab._soundPlayingAttrRemovalTimer);
           tab._soundPlayingAttrRemovalTimer = 0;
 
+          let modifiedAttrs = [];
+          if (tab.hasAttribute("soundplaying-scheduledremoval")) {
+            tab.removeAttribute("soundplaying-scheduledremoval");
+            modifiedAttrs.push("soundplaying-scheduledremoval");
+          }
+
           if (!tab.hasAttribute("soundplaying")) {
             tab.setAttribute("soundplaying", true);
-            this._tabAttrModified(tab, ["soundplaying"]);
+            modifiedAttrs.push("soundplaying");
           }
+
+          this._tabAttrModified(tab, modifiedAttrs);
         ]]>
       </handler>
       <handler event="DOMAudioPlaybackStopped">
         <![CDATA[
           if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") ||
               !event.isTrusted)
             return;
 
           var browser = event.originalTarget;
           var tab = this.getTabForBrowser(browser);
           if (!tab)
             return;
 
           if (tab.hasAttribute("soundplaying")) {
             let removalDelay = Services.prefs.getIntPref("browser.tabs.delayHidingAudioPlayingIconMS");
+
+            tab.style.setProperty("--soundplaying-removal-delay", `${removalDelay - 300}ms`);
+            tab.setAttribute("soundplaying-scheduledremoval", "true");
+            this._tabAttrModified(tab, ["soundplaying-scheduledremoval"]);
+
             tab._soundPlayingAttrRemovalTimer = setTimeout(() => {
+              tab.removeAttribute("soundplaying-scheduledremoval");
               tab.removeAttribute("soundplaying");
-              this._tabAttrModified(tab, ["soundplaying"]);
+              this._tabAttrModified(tab, ["soundplaying", "soundplaying-scheduledremoval"]);
             }, removalDelay);
           }
         ]]>
       </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-tabbox"
@@ -5589,17 +5602,16 @@
 
           if (!("animLastScreenX" in draggedTab._dragData))
             draggedTab._dragData.animLastScreenX = draggedTab._dragData.screenX;
 
           let screenX = event.screenX;
           if (screenX == draggedTab._dragData.animLastScreenX)
             return;
 
-          let draggingRight = screenX > draggedTab._dragData.animLastScreenX;
           draggedTab._dragData.animLastScreenX = screenX;
 
           let rtl = (window.getComputedStyle(this).direction == "rtl");
           let pinned = draggedTab.pinned;
           let numPinned = this.tabbrowser._numPinnedTabs;
           let tabs = this.tabbrowser.visibleTabs
                                     .slice(pinned ? 0 : numPinned,
                                            pinned ? numPinned : undefined);
@@ -6539,25 +6551,25 @@
                      anonid="tab-icon-image"
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
           <xul:image xbl:inherits="sharing,selected=visuallyselected"
                      anonid="sharing-icon"
                      class="tab-sharing-icon-overlay"
                      role="presentation"/>
-          <xul:image xbl:inherits="crashed,busy,soundplaying,pinned,muted,selected=visuallyselected"
+          <xul:image xbl:inherits="crashed,busy,soundplaying,soundplaying-scheduledremoval,pinned,muted,selected=visuallyselected"
                      anonid="overlay-icon"
                      class="tab-icon-overlay"
                      role="presentation"/>
           <xul:label flex="1"
                      xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected=visuallyselected,attention"
                      class="tab-text tab-label"
                      role="presentation"/>
-          <xul:image xbl:inherits="soundplaying,pinned,muted,selected=visuallyselected"
+          <xul:image xbl:inherits="soundplaying,soundplaying-scheduledremoval,pinned,muted,selected=visuallyselected"
                      anonid="soundplaying-icon"
                      class="tab-icon-sound"
                      role="presentation"/>
           <xul:toolbarbutton anonid="close-button"
                              xbl:inherits="fadein,pinned,selected=visuallyselected"
                              class="tab-close-button close-icon"/>
         </xul:hbox>
       </xul:stack>
@@ -7154,18 +7166,16 @@
         </getter>
 
         <setter>
         <![CDATA[
           if (val < 0 || val >= this.childNodes.length)
             return val;
 
           let toTab = this.getRelatedElement(this.childNodes[val]);
-          let fromTab = this._selectedPanel ? this.getRelatedElement(this._selectedPanel)
-                                            : null;
 
           gBrowser._getSwitcher().requestTab(toTab);
 
           var panel = this._selectedPanel;
           var newPanel = this.childNodes[val];
           this._selectedPanel = newPanel;
           if (this._selectedPanel != panel) {
             var event = document.createEvent("Events");
--- a/browser/base/content/test/alerts/browser_notification_close.js
+++ b/browser/base/content/test/alerts/browser_notification_close.js
@@ -1,14 +1,13 @@
 "use strict";
 
 const {PlacesTestUtils} =
   Cu.import("resource://testing-common/PlacesTestUtils.jsm", {});
 
-let tab;
 let notificationURL = "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html";
 let oldShowFavicons;
 
 add_task(function* test_notificationClose() {
   let pm = Services.perms;
   let notificationURI = makeURI(notificationURL);
   pm.add(notificationURI, "desktop-notification", pm.ALLOW_ACTION);
 
@@ -48,17 +47,17 @@ add_task(function* test_notificationClos
 
     let alertCloseButton = alertWindow.document.querySelector(".alertCloseButton");
     is(alertCloseButton.localName, "toolbarbutton", "close button found");
     let promiseBeforeUnloadEvent =
       BrowserTestUtils.waitForEvent(alertWindow, "beforeunload");
     let closedTime = alertWindow.Date.now();
     alertCloseButton.click();
     info("Clicked on close button");
-    let beforeUnloadEvent = yield promiseBeforeUnloadEvent;
+    yield promiseBeforeUnloadEvent;
 
     ok(true, "Alert should close when the close button is clicked");
     let currentTime = alertWindow.Date.now();
     // The notification will self-close at 12 seconds, so this checks
     // that the notification closed before the timeout.
     ok(currentTime - closedTime < 5000,
        "Close requested at " + closedTime + ", actually closed at " + currentTime);
   });
--- a/browser/base/content/test/general/browser_aboutAccounts.js
+++ b/browser/base/content/test/general/browser_aboutAccounts.js
@@ -124,17 +124,17 @@ var gTests = [
 },
 {
   desc: "Test action=signin - captive portal",
   teardown: () => gBrowser.removeCurrentTab(),
   run: function* ()
   {
     const signinUrl = "https://redirproxy.example.com/test";
     setPref("identity.fxaccounts.remote.signin.uri", signinUrl);
-    let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin");
+    let [tab, ] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin");
     yield checkVisibilities(tab, {
       stage: true, // parent of 'manage' and 'intro'
       manage: false,
       intro: false, // this is  "get started"
       remote: false,
       networkError: true
     });
   }
@@ -147,17 +147,17 @@ var gTests = [
   },
   run: function* ()
   {
     BrowserOffline.toggleOfflineStatus();
     Services.cache2.clear();
 
     const signinUrl = "https://unknowndomain.cow";
     setPref("identity.fxaccounts.remote.signin.uri", signinUrl);
-    let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin");
+    let [tab, ] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin");
     yield checkVisibilities(tab, {
       stage: true, // parent of 'manage' and 'intro'
       manage: false,
       intro: false, // this is  "get started"
       remote: false,
       networkError: true
     });
   }
@@ -206,28 +206,19 @@ var gTests = [
   teardown: function* () {
     gBrowser.removeCurrentTab();
     yield signOut();
   },
   run: function* ()
   {
     const expected_url = "https://example.com/?is_force_auth";
     setPref("identity.fxaccounts.remote.force_auth.uri", expected_url);
-    let userData = {
-      email: "foo@example.com",
-      uid: "1234@lcip.org",
-      assertion: "foobar",
-      sessionToken: "dead",
-      kA: "beef",
-      kB: "cafe",
-      verified: true
-    };
 
     yield setSignedInUser();
-    let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=reauth");
+    let [, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=reauth");
     // The current user will be appended to the url
     let expected = expected_url + "&email=foo%40example.com";
     is(url, expected, "action=reauth got the expected URL");
   },
 },
 {
   desc: "Test with migrateToDevEdition enabled (success)",
   teardown: function* () {
@@ -346,69 +337,69 @@ var gTests = [
   }
 },
 {
   desc: "Test entrypoint query string, no action, no user logged in",
   teardown: () => gBrowser.removeCurrentTab(),
   run: function* () {
     // When this loads with no user logged-in, we expect the "normal" URL
     setPref("identity.fxaccounts.remote.signup.uri", "https://example.com/");
-    let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?entrypoint=abouthome");
+    let [, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?entrypoint=abouthome");
     is(url, "https://example.com/?entrypoint=abouthome", "entrypoint=abouthome got the expected URL");
   },
 },
 {
   desc: "Test entrypoint query string for signin",
   teardown: () => gBrowser.removeCurrentTab(),
   run: function* () {
     // When this loads with no user logged-in, we expect the "normal" URL
     const expected_url = "https://example.com/?is_sign_in";
     setPref("identity.fxaccounts.remote.signin.uri", expected_url);
-    let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin&entrypoint=abouthome");
+    let [, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin&entrypoint=abouthome");
     is(url, expected_url + "&entrypoint=abouthome", "entrypoint=abouthome got the expected URL");
   },
 },
 {
   desc: "Test entrypoint query string for signup",
   teardown: () => gBrowser.removeCurrentTab(),
   run: function* () {
     // When this loads with no user logged-in, we expect the "normal" URL
     const sign_up_url = "https://example.com/?is_sign_up";
     setPref("identity.fxaccounts.remote.signup.uri", sign_up_url);
-    let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?entrypoint=abouthome&action=signup");
+    let [, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?entrypoint=abouthome&action=signup");
     is(url, sign_up_url + "&entrypoint=abouthome", "entrypoint=abouthome got the expected URL");
   },
 },
 {
   desc: "about:accounts URL params should be copied to remote URL params " +
         "when remote URL has no URL params, except for 'action'",
   teardown() {
     gBrowser.removeCurrentTab();
   },
   run: function* () {
     let signupURL = "https://example.com/";
     setPref("identity.fxaccounts.remote.signup.uri", signupURL);
     let queryStr = "email=foo%40example.com&foo=bar&baz=quux";
-    let [tab, url] =
+    let [, url] =
       yield promiseNewTabWithIframeLoadEvent("about:accounts?" + queryStr +
                                              "&action=action");
     is(url, signupURL + "?" + queryStr, "URL params are copied to signup URL");
   },
 },
 {
   desc: "about:accounts URL params should be copied to remote URL params " +
         "when remote URL already has some URL params, except for 'action'",
   teardown() {
     gBrowser.removeCurrentTab();
   },
   run: function* () {
     let signupURL = "https://example.com/?param";
     setPref("identity.fxaccounts.remote.signup.uri", signupURL);
     let queryStr = "email=foo%40example.com&foo=bar&baz=quux";
-    let [tab, url] =
+    let [, url] =
       yield promiseNewTabWithIframeLoadEvent("about:accounts?" + queryStr +
                                              "&action=action");
     is(url, signupURL + "&" + queryStr, "URL params are copied to signup URL");
   },
 },
 ]; // gTests
 
 function test()
--- a/browser/base/content/test/general/browser_aboutCertError.js
+++ b/browser/base/content/test/general/browser_aboutCertError.js
@@ -82,17 +82,17 @@ add_task(function* checkReturnToPrevious
   is(browser.webNavigation.canGoForward, true, "webNavigation.canGoForward");
   is(gBrowser.currentURI.spec, GOOD_PAGE, "Went back");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(function* checkBadStsCert() {
   info("Loading a badStsCert and making sure exception button doesn't show up");
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
   let browser = gBrowser.selectedBrowser;
 
   info("Loading and waiting for the cert error");
   let certErrorLoaded = waitForCertErrorLoad(browser);
   BrowserTestUtils.loadURI(browser, BAD_STS_CERT);
   yield certErrorLoaded;
 
   let exceptionButtonHidden = yield ContentTask.spawn(browser, null, function* () {
@@ -106,17 +106,17 @@ add_task(function* checkBadStsCert() {
 });
 
 const PREF_BLOCKLIST_CLOCK_SKEW_SECONDS = "services.blocklist.clock_skew_seconds";
 
 add_task(function* checkWrongSystemTimeWarning() {
   function* setUpPage() {
     let browser;
     let certErrorLoaded;
-    let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
+    yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
       gBrowser.selectedTab = gBrowser.addTab(BAD_CERT);
       browser = gBrowser.selectedBrowser;
       certErrorLoaded = waitForCertErrorLoad(browser);
     }, false);
 
     info("Loading and waiting for the cert error");
     yield certErrorLoaded;
 
@@ -206,17 +206,17 @@ add_task(function* checkWrongSystemTimeW
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(function* checkAdvancedDetails() {
   info("Loading a bad cert page and verifying the advanced details section");
   let browser;
   let certErrorLoaded;
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     gBrowser.selectedTab = gBrowser.addTab(BAD_CERT);
     browser = gBrowser.selectedBrowser;
     certErrorLoaded = waitForCertErrorLoad(browser);
   }, false);
 
   info("Loading and waiting for the cert error");
   yield certErrorLoaded;
 
@@ -233,19 +233,16 @@ add_task(function* checkAdvancedDetails(
 
   message = yield ContentTask.spawn(browser, null, function* () {
     let doc = content.document;
     let errorCode = doc.getElementById("errorCode");
     errorCode.click();
     let div = doc.getElementById("certificateErrorDebugInformation");
     let text = doc.getElementById("certificateErrorText");
 
-    let docshell = content.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsIDocShell);
     let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
                      .getService(Ci.nsISerializationHelper);
     let serializable =  docShell.failedChannel.securityInfo
                                 .QueryInterface(Ci.nsITransportSecurityInfo)
                                 .QueryInterface(Ci.nsISerializable);
     let serializedSecurityInfo = serhelper.serializeToString(serializable);
     return {
       divDisplay: content.getComputedStyle(div).display,
@@ -266,17 +263,17 @@ add_task(function* checkAdvancedDetails(
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(function* checkAdvancedDetailsForHSTS() {
   info("Loading a bad STS cert page and verifying the advanced details section");
   let browser;
   let certErrorLoaded;
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     gBrowser.selectedTab = gBrowser.addTab(BAD_STS_CERT);
     browser = gBrowser.selectedBrowser;
     certErrorLoaded = waitForCertErrorLoad(browser);
   }, false);
 
   info("Loading and waiting for the cert error");
   yield certErrorLoaded;
 
@@ -305,19 +302,16 @@ add_task(function* checkAdvancedDetailsF
 
   message = yield ContentTask.spawn(browser, null, function* () {
     let doc = content.document;
     let errorCode = doc.getElementById("errorCode");
     errorCode.click();
     let div = doc.getElementById("certificateErrorDebugInformation");
     let text = doc.getElementById("certificateErrorText");
 
-    let docshell = content.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsIDocShell);
     let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
                      .getService(Ci.nsISerializationHelper);
     let serializable =  docShell.failedChannel.securityInfo
                                 .QueryInterface(Ci.nsITransportSecurityInfo)
                                 .QueryInterface(Ci.nsISerializable);
     let serializedSecurityInfo = serhelper.serializeToString(serializable);
     return {
       divDisplay: content.getComputedStyle(div).display,
@@ -338,17 +332,17 @@ add_task(function* checkAdvancedDetailsF
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(function* checkUnknownIssuerLearnMoreLink() {
   info("Loading a cert error for self-signed pages and checking the correct link is shown");
   let browser;
   let certErrorLoaded;
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     gBrowser.selectedTab = gBrowser.addTab(UNKNOWN_ISSUER);
     browser = gBrowser.selectedBrowser;
     certErrorLoaded = waitForCertErrorLoad(browser);
   }, false);
 
   info("Loading and waiting for the cert error");
   yield certErrorLoaded;
 
--- a/browser/base/content/test/general/browser_aboutNetError.js
+++ b/browser/base/content/test/general/browser_aboutNetError.js
@@ -10,17 +10,17 @@ Services.prefs.setIntPref("security.tls.
 const LOW_TLS_VERSION = "https://tls1.example.com/";
 const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
 const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 
 add_task(function* checkReturnToPreviousPage() {
   info("Loading a TLS page that isn't supported, ensure we have a fix button and clicking it then loads the page");
   let browser;
   let pageLoaded;
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     gBrowser.selectedTab = gBrowser.addTab(LOW_TLS_VERSION);
     browser = gBrowser.selectedBrowser;
     pageLoaded = BrowserTestUtils.waitForErrorPage(browser);
   }, false);
 
   info("Loading and waiting for the net error");
   yield pageLoaded;
 
@@ -34,9 +34,8 @@ add_task(function* checkReturnToPrevious
     content.document.getElementById("prefResetButton").click();
   });
   yield pageshowPromise;
 
   Assert.equal(content.document.documentURI, LOW_TLS_VERSION, "Should not be showing page");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
-
--- a/browser/base/content/test/general/browser_aboutTabCrashed_showForm.js
+++ b/browser/base/content/test/general/browser_aboutTabCrashed_showForm.js
@@ -11,18 +11,16 @@ requestLongerTimeout(2);
  * Tests that we show the about:tabcrashed additional details form
  * if the "submit a crash report" checkbox was checked by default.
  */
 add_task(function* test_show_form() {
   return BrowserTestUtils.withNewTab({
     gBrowser,
     url: PAGE,
   }, function*(browser) {
-    let tab = gBrowser.getTabForBrowser(browser);
-
     // Flip the pref so that the checkbox should be checked
     // by default.
     let pref = TabCrashHandler.prefs.root + "sendReport";
     yield pushPrefs([pref, true]);
 
     // Now crash the browser.
     yield BrowserTestUtils.crashBrowser(browser);
 
--- a/browser/base/content/test/general/browser_bug409624.js
+++ b/browser/base/content/test/general/browser_bug409624.js
@@ -21,19 +21,16 @@ add_task(function* test() {
                            }
                          },
                        });
   });
 
   let prefService = Cc["@mozilla.org/preferences-service;1"]
                     .getService(Components.interfaces.nsIPrefService);
 
-  let findBar = gFindBar;
-  let textbox = gFindBar.getElement("findbar-textbox");
-
   let tempScope = {};
   Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
                                              .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
   let Sanitizer = tempScope.Sanitizer;
   let s = new Sanitizer();
   s.prefDomain = "privacy.cpd.";
   let prefBranch = prefService.getBranch(s.prefDomain);
 
--- a/browser/base/content/test/general/browser_bug431826.js
+++ b/browser/base/content/test/general/browser_bug431826.js
@@ -14,18 +14,16 @@ add_task(function* () {
   yield promise;
 
   yield remote(() => {
     // Confirm that we are displaying the contributed error page, not the default
     let uri = content.document.documentURI;
     Assert.ok(uri.startsWith("about:certerror"), "Broken page should go to about:certerror, not about:neterror");
   });
 
-  let advancedDiv, advancedDivVisibility, technicalDivCollapsed;
-
   yield remote(() => {
     let div = content.document.getElementById("badCertAdvancedPanel");
     // Confirm that the expert section is collapsed
     Assert.ok(div, "Advanced content div should exist");
     Assert.equal(div.ownerGlobal.getComputedStyle(div).display,
       "none", "Advanced content should not be visible by default");
   });
 
--- a/browser/base/content/test/general/browser_bug460146.js
+++ b/browser/base/content/test/general/browser_bug460146.js
@@ -3,17 +3,16 @@
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
 
   gBrowser.selectedBrowser.addEventListener("load", function () {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
 
-    var doc = gBrowser.contentDocument;
     var pageInfo = BrowserPageInfo(gBrowser.selectedBrowser.currentURI.spec,
                                    "mediaTab");
 
     pageInfo.addEventListener("load", function () {
       pageInfo.removeEventListener("load", arguments.callee, true);
       pageInfo.onFinished.push(function () {
         executeSoon(function () {
           var imageTree = pageInfo.document.getElementById("imagetree");
--- a/browser/base/content/test/general/browser_bug553455.js
+++ b/browser/base/content/test/general/browser_bug553455.js
@@ -1020,24 +1020,16 @@ function test_renotifyInstalled() {
 
     Services.perms.remove(makeURI("http://example.com/"), "install");
     yield removeTab();
   });
 },
 
 function test_cancel() {
   return Task.spawn(function* () {
-    function complete_install(callback) {
-      let url = TESTROOT + "slowinstall.sjs?continue=true"
-      NetUtil.asyncFetch({
-        uri: url,
-        loadUsingSystemPrincipal: true
-      }, callback || (() => {}));
-    }
-
     let pm = Services.perms;
     pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
     let notificationPromise = waitForNotification(PROGRESS_NOTIFICATION);
     let triggers = encodeURIComponent(JSON.stringify({
       "XPI": "slowinstall.sjs?file=amosigned.xpi"
     }));
     BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
--- a/browser/base/content/test/general/browser_bug585830.js
+++ b/browser/base/content/test/general/browser_bug585830.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 function test() {
   let tab1 = gBrowser.selectedTab;
   let tab2 = gBrowser.addTab("about:blank", {skipAnimation: true});
-  let tab3 = gBrowser.addTab();
+  gBrowser.addTab();
   gBrowser.selectedTab = tab2;
 
   gBrowser.removeCurrentTab({animate: true});
   gBrowser.tabContainer.advanceSelectedTab(-1, true);
   is(gBrowser.selectedTab, tab1, "First tab should be selected");
   gBrowser.removeTab(tab2);
 
   // test for "null has no properties" fix. See Bug 585830 Comment 13
--- a/browser/base/content/test/general/browser_bug592338.js
+++ b/browser/base/content/test/general/browser_bug592338.js
@@ -52,20 +52,19 @@ function test_install_lwtheme() {
   gBrowser.selectedTab = gBrowser.addTab("https://example.com/browser/browser/base/content/test/general/bug592338.html");
   gBrowser.selectedBrowser.addEventListener("pageshow", function() {
     if (gBrowser.contentDocument.location.href == "about:blank")
       return;
 
     gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee, false);
 
     BrowserTestUtils.synthesizeMouse("#theme-install", 2, 2, {}, gBrowser.selectedBrowser);
-    let notification;
     let notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
     waitForCondition(
-      () => (notification = notificationBox.getNotificationWithValue("lwtheme-install-notification")),
+      () => notificationBox.getNotificationWithValue("lwtheme-install-notification"),
       () => {
         is(LightweightThemeManager.currentTheme.id, "test", "Should have installed the test theme");
 
         LightweightThemeManager.currentTheme = null;
         gBrowser.removeTab(gBrowser.selectedTab);
         Services.perms.remove(makeURI("http://example.com/"), "install");
 
         runNextTest();
--- a/browser/base/content/test/general/browser_bug676619.js
+++ b/browser/base/content/test/general/browser_bug676619.js
@@ -1,18 +1,18 @@
 function test () {
   requestLongerTimeout(3);
   waitForExplicitFinish();
 
   var isHTTPS = false;
 
   function loadListener() {
     function testLocation(link, url, next) {
-      var tabOpenListener = new TabOpenListener(url, function () {
-          gBrowser.removeTab(this.tab);
+      new TabOpenListener(url, function () {
+        gBrowser.removeTab(this.tab);
       }, function () {
         next();
       });
 
       ContentTask.spawn(gBrowser.selectedBrowser, link, link => {
         content.document.getElementById(link).click();
       });
     }
--- a/browser/base/content/test/general/browser_bug678392.js
+++ b/browser/base/content/test/general/browser_bug678392.js
@@ -152,17 +152,16 @@ function test1() {
   });
 }
 
 function test2() {
   // Test growing of snapshot array across tabs.
   let tab = gBrowser.selectedTab;
 
   load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
-    var historyIndex = gBrowser.webNavigation.sessionHistory.index - 1;
     load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
       is(gHistorySwipeAnimation._trackedSnapshots.length, 2, "Length of " +
          "snapshot array is equal to 2 after loading two pages");
       let prevTab = tab;
       tab = gBrowser.addTab("about:newtab");
       gBrowser.selectedTab = tab;
       load(tab, HTTPROOT + "browser_bug678392-2.html" /* initial page */,
            function() {
--- a/browser/base/content/test/general/browser_bug822367.js
+++ b/browser/base/content/test/general/browser_bug822367.js
@@ -120,17 +120,16 @@ function MixedTest4A() {
 function MixedTest4B() {
   waitForCondition(() => content.document.location == gHttpTestRoot + "file_bug822367_4B.html", MixedTest4C, "Waited too long for mixed script to run in Test 4");
 }
 function MixedTest4C() {
   ok(content.document.location == gHttpTestRoot + "file_bug822367_4B.html", "Location didn't change in test 4");
 
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
-  let {gIdentityHandler} = gTestBrowser.ownerGlobal;
   waitForCondition(() => content.document.getElementById('p1').innerHTML == "", MixedTest4D, "Mixed script loaded in test 4 after location change!");
 }
 function MixedTest4D() {
   ok(content.document.getElementById('p1').innerHTML == "", "p1.innerHTML changed; mixed script loaded after location change in Test 4");
   MixedTest5();
 }
 
 // Mixed script attempts to load in a document.open()
--- a/browser/base/content/test/general/browser_contentSearchUI.js
+++ b/browser/base/content/test/general/browser_contentSearchUI.js
@@ -577,17 +577,16 @@ add_task(function* search() {
                           { str: "xfoo", type: "formHistory" }, "xbar"], 0);
 
   // Mouse over the second suggestion.
   state = yield msg("mousemove", 1);
   checkState(state, "x", [{ str: "x", type: "formHistory" },
                           { str: "xfoo", type: "formHistory" }, "xbar"], 1);
 
   modifiers.button = 0;
-  let currentTab = gBrowser.selectedTab;
   p = msg("waitForSearch");
   yield msg("click", { eltIdx: 1, modifiers: modifiers });
   mesg = yield p;
   eventData.searchString = "xfoo";
   eventData.originalEvent = modifiers;
   eventData.selection = {
     index: 1,
     kind: "mouse",
@@ -755,17 +754,17 @@ function promiseMsg(name, type, msgMan) 
 
 function setUpEngines() {
   return Task.spawn(function* () {
     info("Removing default search engines");
     let currentEngineName = Services.search.currentEngine.name;
     let currentEngines = Services.search.getVisibleEngines();
     info("Adding test search engines");
     let engine1 = yield promiseNewSearchEngine(TEST_ENGINE_BASENAME);
-    let engine2 = yield promiseNewSearchEngine(TEST_ENGINE_2_BASENAME);
+    yield promiseNewSearchEngine(TEST_ENGINE_2_BASENAME);
     Services.search.currentEngine = engine1;
     for (let engine of currentEngines) {
       Services.search.removeEngine(engine);
     }
     registerCleanupFunction(() => {
       Services.search.restoreDefaultEngines();
       Services.search.currentEngine = Services.search.getEngineByName(currentEngineName);
     });
--- a/browser/base/content/test/general/browser_contextmenu_childprocess.js
+++ b/browser/base/content/test/general/browser_contextmenu_childprocess.js
@@ -7,17 +7,17 @@ add_task(function *() {
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gBaseURL + "subtst_contextmenu.html");
 
   let contextMenu = document.getElementById("contentAreaContextMenu");
 
   // Get the point of the element with the page menu (test-pagemenu) and
   // synthesize a right mouse click there.
   let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
   yield BrowserTestUtils.synthesizeMouse("#test-pagemenu", 5, 5, { type : "contextmenu", button : 2 }, tab.linkedBrowser);
-  let event = yield popupShownPromise;
+  yield popupShownPromise;
 
   checkMenu(contextMenu);
 
   let popupHiddenPromise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
   contextMenu.hidePopup();
   yield popupHiddenPromise;
 
   yield BrowserTestUtils.removeTab(tab);
--- a/browser/base/content/test/general/browser_datachoices_notification.js
+++ b/browser/base/content/test/general/browser_datachoices_notification.js
@@ -87,17 +87,17 @@ var checkInfobarButton = Task.async(func
   // Add an observer to ensure the "advanced" pane opened (but don't bother
   // closing it - we close the entire window when done.)
   let paneLoadedPromise = promiseTopicObserved("advanced-pane-loaded");
 
   // Click on the button.
   button.click();
 
   // Wait for the preferences panel to open.
-  let preferenceWindow = yield paneLoadedPromise;
+  yield paneLoadedPromise;
   yield promiseNextTick();
 });
 
 add_task(function* setup() {
   const bypassNotification = Preferences.get(PREF_BYPASS_NOTIFICATION, true);
   const currentPolicyVersion = Preferences.get(PREF_CURRENT_POLICY_VERSION, 1);
 
   // Register a cleanup function to reset our preferences.
--- a/browser/base/content/test/general/browser_double_close_tab.js
+++ b/browser/base/content/test/general/browser_double_close_tab.js
@@ -39,17 +39,17 @@ function waitForDialogDestroyed(node, ca
 add_task(function*() {
   testTab = gBrowser.selectedTab = gBrowser.addTab();
   yield promiseTabLoadEvent(testTab, TEST_PAGE);
   // XXXgijs the reason this has nesting and callbacks rather than promises is
   // that DOM promises resolve on the next tick. So they're scheduled
   // in an event queue. So when we spin a new event queue for a modal dialog...
   // everything gets messed up and the promise's .then callbacks never get
   // called, despite resolve() being called just fine.
-  let dialogNode = yield new Promise(resolveOuter => {
+  yield new Promise(resolveOuter => {
     waitForDialog(dialogNode => {
       waitForDialogDestroyed(dialogNode, () => {
         let doCompletion = () => setTimeout(resolveOuter, 0);
         info("Now checking if dialog is destroyed");
         ok(!dialogNode.parentNode, "onbeforeunload dialog should be gone.");
         if (dialogNode.parentNode) {
           // Failed to remove onbeforeunload dialog, so do it ourselves:
           let leaveBtn = dialogNode.ui.button0;
@@ -73,10 +73,8 @@ registerCleanupFunction(function() {
   if (testTab.parentNode) {
     // Remove the handler, or closing this tab will prove tricky:
     try {
       testTab.linkedBrowser.contentWindow.onbeforeunload = null;
     } catch (ex) {}
     gBrowser.removeTab(testTab);
   }
 });
-
-
--- a/browser/base/content/test/general/browser_fullscreen-window-open.js
+++ b/browser/base/content/test/general/browser_fullscreen-window-open.js
@@ -177,17 +177,16 @@ function test_open_from_chrome() {
       title: "test_open_from_chrome",
       param: "",
     },
     finalizeFn: function () {}
   });
 }
 
 function waitForTabOpen(aOptions) {
-  let start = Date.now();
   let message = aOptions.message;
 
   if (!message.title) {
     ok(false, "Can't get message.title.");
     aOptions.finalizeFn();
     runNextTest();
     return;
   }
@@ -226,17 +225,16 @@ function waitForTabOpen(aOptions) {
     uri: URI,
     title: message.title,
     option: message.param,
   });
 }
 
 
 function waitForWindowOpen(aOptions) {
-  let start = Date.now();
   let message = aOptions.message;
   let url = aOptions.url || "about:blank";
 
   if (!message.title) {
     ok(false, "Can't get message.title");
     aOptions.finalizeFn();
     runNextTest();
     return;
@@ -268,17 +266,16 @@ function executeWindowOpenInContent(aPar
   ContentTask.spawn(gBrowser.selectedBrowser, JSON.stringify(aParam), function* (dataTestParam) {
     let testElm = content.document.getElementById("test");
     testElm.setAttribute("data-test-param", dataTestParam);
     testElm.click();
   });
 }
 
 function waitForWindowOpenFromChrome(aOptions) {
-  let start = Date.now();
   let message = aOptions.message;
   let url = aOptions.url || "about:blank";
 
   if (!message.title) {
     ok(false, "Can't get message.title");
     aOptions.finalizeFn();
     runNextTest();
     return;
@@ -294,17 +291,17 @@ function waitForWindowOpenFromChrome(aOp
   };
 
   let listener = new WindowListener(message.title, getBrowserURL(), {
     onSuccess: aOptions.successFn,
     onFinalize: onFinalize,
   });
   Services.wm.addListener(listener);
 
-  let testWindow = window.open(url, message.title, message.option);
+  window.open(url, message.title, message.option);
 }
 
 function WindowListener(aTitle, aUrl, aCallBackObj) {
   this.test_title = aTitle;
   this.test_url = aUrl;
   this.callback_onSuccess = aCallBackObj.onSuccess;
   this.callBack_onFinalize = aCallBackObj.onFinalize;
 }
--- a/browser/base/content/test/general/browser_gestureSupport.js
+++ b/browser/base/content/test/general/browser_gestureSupport.js
@@ -541,17 +541,16 @@ function test_rotateHelperGetImageRotati
 function test_rotateHelperOneGesture(aImageElement, aCurrentRotation,
                                      aDirection, aAmount, aStop)
 {
   if (aAmount <= 0 || aAmount > 90) // Bound to 0 < aAmount <= 90
     return;
 
   // easier to type names for the direction constants
   let clockwise = SimpleGestureEvent.ROTATION_CLOCKWISE;
-  let cclockwise = SimpleGestureEvent.ROTATION_COUNTERCLOCKWISE;
 
   let delta = aAmount * (aDirection == clockwise ? 1 : -1);
 
   // Kill transition time on image so test isn't wrong and doesn't take 10 seconds
   aImageElement.style.transitionDuration = "0s";
 
   // Start the gesture, perform an update, and force flush
   test_utils.sendSimpleGestureEvent("MozRotateGestureStart", 0, 0, aDirection, .001, 0);
--- a/browser/base/content/test/general/browser_page_style_menu_update.js
+++ b/browser/base/content/test/general/browser_page_style_menu_update.js
@@ -40,17 +40,16 @@ add_task(function*() {
   gPageStyleMenu.fillPopup(menupopup);
 
   // page_style_sample.html should default us to selecting the stylesheet
   // with the title "6" first.
   let selected = menupopup.querySelector("menuitem[checked='true']");
   is(selected.getAttribute("label"), "6", "Should have '6' stylesheet selected by default");
 
   // Now select stylesheet "1"
-  let targets = menupopup.querySelectorAll("menuitem");
   let target = menupopup.querySelector("menuitem[label='1']");
   target.click();
 
   // Now we need to wait for the content process to send its stylesheet
   // update for the selected tab to the parent. Because messages are
   // guaranteed to be sent in order, we'll make sure we do the check
   // after the parent has been updated by yielding until the child
   // has finished running a ContentTask for us.
--- a/browser/base/content/test/general/browser_parsable_script.js
+++ b/browser/base/content/test/general/browser_parsable_script.js
@@ -43,20 +43,19 @@ function uriIsWhiteListed(uri) {
 
 function parsePromise(uri) {
   let promise = new Promise((resolve, reject) => {
     let xhr = new XMLHttpRequest();
     xhr.open("GET", uri, true);
     xhr.onreadystatechange = function() {
       if (this.readyState == this.DONE) {
         let scriptText = this.responseText;
-        let ast;
         try {
           info("Checking " + uri);
-          ast = Reflect.parse(scriptText);
+          Reflect.parse(scriptText);
           resolve(true);
         } catch (ex) {
           let errorMsg = "Script error reading " + uri + ": " + ex;
           ok(false, errorMsg);
           resolve(false);
         }
       }
     };
@@ -126,9 +125,8 @@ add_task(function* checkAllTheJS() {
       continue;
     }
     allPromises.push(parsePromise(uri.spec));
   }
 
   let promiseResults = yield Promise.all(allPromises);
   is(promiseResults.filter((x) => !x).length, 0, "There should be 0 parsing errors");
 });
-
--- a/browser/base/content/test/general/browser_permissions.js
+++ b/browser/base/content/test/general/browser_permissions.js
@@ -27,17 +27,16 @@ function* openIdentityPopup() {
 function* closeIdentityPopup() {
   let {gIdentityHandler} = gBrowser.ownerGlobal;
   let promise = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popuphidden");
   gIdentityHandler._identityPopup.hidePopup();
   return promise;
 }
 
 add_task(function* testMainViewVisible() {
-  let {gIdentityHandler} = gBrowser.ownerGlobal;
   let tab = gBrowser.selectedTab = gBrowser.addTab();
   yield promiseTabLoadEvent(tab, PERMISSIONS_PAGE);
 
   let permissionsList = document.getElementById("identity-popup-permission-list");
   let emptyLabel = permissionsList.nextSibling.nextSibling;
 
   yield openIdentityPopup();
 
@@ -97,17 +96,16 @@ add_task(function* testIdentityIcon() {
     "identity-box signals granted permissions");
 
   SitePermissions.remove(gBrowser.currentURI, "geo");
   SitePermissions.remove(gBrowser.currentURI, "camera");
   SitePermissions.remove(gBrowser.currentURI, "cookie");
 });
 
 add_task(function* testCancelPermission() {
-  let {gIdentityHandler} = gBrowser.ownerGlobal;
   let tab = gBrowser.selectedTab = gBrowser.addTab();
   yield promiseTabLoadEvent(tab, PERMISSIONS_PAGE);
 
   let permissionsList = document.getElementById("identity-popup-permission-list");
   let emptyLabel = permissionsList.nextSibling.nextSibling;
 
   SitePermissions.set(gBrowser.currentURI, "geo", SitePermissions.ALLOW);
   SitePermissions.set(gBrowser.currentURI, "camera", SitePermissions.BLOCK);
@@ -125,17 +123,16 @@ add_task(function* testCancelPermission(
   cancelButtons[1].click();
   labels = permissionsList.querySelectorAll(".identity-popup-permission-label");
   is(labels.length, 0, "One permission should be removed");
 
   yield closeIdentityPopup();
 });
 
 add_task(function* testPermissionHints() {
-  let {gIdentityHandler} = gBrowser.ownerGlobal;
   let tab = gBrowser.selectedTab = gBrowser.addTab();
   yield promiseTabLoadEvent(tab, PERMISSIONS_PAGE);
 
   let permissionsList = document.getElementById("identity-popup-permission-list");
   let emptyHint = document.getElementById("identity-popup-permission-empty-hint");
   let reloadHint = document.getElementById("identity-popup-permission-reload-hint");
 
   yield openIdentityPopup();
--- a/browser/base/content/test/general/browser_purgehistory_clears_sh.js
+++ b/browser/base/content/test/general/browser_purgehistory_clears_sh.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const url = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
 
 add_task(function* purgeHistoryTest() {
-  let tab = yield BrowserTestUtils.withNewTab({
+  yield BrowserTestUtils.withNewTab({
     gBrowser,
     url,
   }, function* purgeHistoryTestInner(browser) {
     let backButton = browser.ownerDocument.getElementById("Browser:Back");
     let forwardButton = browser.ownerDocument.getElementById("Browser:Forward");
 
     ok(!browser.webNavigation.canGoBack,
        "Initial value for webNavigation.canGoBack");
--- a/browser/base/content/test/general/browser_readerMode.js
+++ b/browser/base/content/test/general/browser_readerMode.js
@@ -108,21 +108,19 @@ add_task(function* test_getOriginalUrl()
 add_task(function* test_reader_view_element_attribute_transform() {
   registerCleanupFunction(function() {
     while (gBrowser.tabs.length > 1) {
       gBrowser.removeCurrentTab();
     }
   });
 
   function observeAttribute(element, attribute, triggerFn, checkFn) {
-    let initValue = element.getAttribute(attribute);
     return new Promise(resolve => {
       let observer = new MutationObserver((mutations) => {
         mutations.forEach( mu => {
-          let muValue = element.getAttribute(attribute);
           if (element.getAttribute(attribute) !== mu.oldValue) {
             checkFn();
             resolve();
             observer.disconnect();
           }
         });
       });
 
--- a/browser/base/content/test/general/browser_relatedTabs.js
+++ b/browser/base/content/test/general/browser_relatedTabs.js
@@ -5,17 +5,16 @@
 add_task(function*() {
   is(gBrowser.tabs.length, 1, "one tab is open initially");
 
   // Add several new tabs in sequence, interrupted by selecting a
   // different tab, moving a tab around and closing a tab,
   // returning a list of opened tabs for verifying the expected order.
   // The new tab behaviour is documented in bug 465673
   let tabs = [];
-  let promises = [];
   function addTab(aURL, aReferrer) {
     let tab = gBrowser.addTab(aURL, {referrerURI: aReferrer});
     tabs.push(tab);
     return BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   }
 
   yield addTab("http://mochi.test:8888/#0");
   gBrowser.selectedTab = tabs[0];
--- a/browser/base/content/test/general/browser_removeTabsToTheEnd.js
+++ b/browser/base/content/test/general/browser_removeTabsToTheEnd.js
@@ -1,17 +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/. */
 
 function test() {
   // Add two new tabs after the original tab. Pin the first one.
   let originalTab = gBrowser.selectedTab;
   let newTab1 = gBrowser.addTab();
-  let newTab2 = gBrowser.addTab();
+  gBrowser.addTab();
   gBrowser.pinTab(newTab1);
 
   // Check that there is only one closable tab from originalTab to the end
   is(gBrowser.getTabsToTheEndFrom(originalTab).length, 1,
     "One unpinned tab to the right");
 
   // Remove tabs to the end
   gBrowser.removeTabsToTheEndFrom(originalTab);
--- a/browser/base/content/test/general/browser_sanitizeDialog.js
+++ b/browser/base/content/test/general/browser_sanitizeDialog.js
@@ -131,17 +131,16 @@ add_task(function* test_history_download
   for (let i = 0; i < 5; i++) {
     pURI = makeURI("http://" + (61 + i) + "-minutes-ago.com/");
     places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(61 + i)});
     olderURIs.push(pURI);
   }
   let promiseSanitized = promiseSanitizationComplete();
 
   yield PlacesTestUtils.addVisits(places);
-  let totalHistoryVisits = uris.length + olderURIs.length;
 
   let wh = new WindowHelper();
   wh.onload = function () {
     this.selectDuration(Sanitizer.TIMESPAN_HOUR);
     this.checkPrefCheckbox("history", true);
     this.acceptDialog();
   };
   wh.onunload = function* () {
--- a/browser/base/content/test/general/browser_save_video_frame.js
+++ b/browser/base/content/test/general/browser_save_video_frame.js
@@ -99,17 +99,16 @@ add_task(function*() {
 
   let tab = gBrowser.addTab();
   gBrowser.selectedTab = tab;
   let browser = tab.linkedBrowser;
   info("Loading video tab");
   yield promiseTabLoadEvent(tab, VIDEO_URL);
   info("Video tab loaded.");
 
-  let video = browser.contentDocument.getElementById("video1");
   let context = document.getElementById("contentAreaContextMenu");
   let popupPromise = promisePopupShown(context);
 
   info("Synthesizing right-click on video element");
   rightClickVideo(browser);
   info("Waiting for popup to fire popupshown.");
   yield popupPromise;
   info("Popup fired popupshown");
--- a/browser/base/content/test/general/browser_tabDrop.js
+++ b/browser/base/content/test/general/browser_tabDrop.js
@@ -83,17 +83,16 @@ function* drop(dragData, expectedTabOpen
   var event = {
     clientX: 0,
     clientY: 0,
     screenX: 0,
     screenY: 0,
   };
   EventUtils.synthesizeDrop(gBrowser.selectedTab, gBrowser.selectedTab, dragData, "link", window, undefined, event);
   let tabsOpened = false;
-  let tabOpened = false;
   if (awaitTabOpen) {
     yield awaitTabOpen;
     info("Got TabOpen event");
     tabsOpened = true;
     for (let tab of openedTabs) {
       yield BrowserTestUtils.removeTab(tab);
     }
   }
--- a/browser/base/content/test/general/browser_tabfocus.js
+++ b/browser/base/content/test/general/browser_tabfocus.js
@@ -141,17 +141,16 @@ add_task(function*() {
 
   var childFocusScript = "data:,(" + focusInChild.toString() + ")();";
   browser1.messageManager.loadFrameScript(childFocusScript, true);
   browser2.messageManager.loadFrameScript(childFocusScript, true);
 
   gURLBar.focus();
   yield SimpleTest.promiseFocus();
 
-  var messages = "";
   if (gMultiProcessBrowser) {
     messageManager.addMessageListener("Browser:FocusChanged", message => {
       actualEvents.push(message.data.details);
       compareFocusResults();
     });
   }
 
   _lastfocus = "urlbar";
@@ -560,9 +559,8 @@ function* expectFocusShift(callback, exp
 
     // No events are expected, so resolve the promise immediately.
     if (expectedEvents["main-window"].length + expectedEvents["window1"].length + expectedEvents["window2"].length == 0) {
       currentPromiseResolver();
       currentPromiseResolver = null;
     }
   });
 }
-
--- a/browser/base/content/test/general/browser_tabkeynavigation.js
+++ b/browser/base/content/test/general/browser_tabkeynavigation.js
@@ -135,17 +135,16 @@ add_task(function* test() {
     gBrowser.selectedTab = tab2;
     EventUtils.synthesizeKey("VK_F4", { type: "keydown", ctrlKey: true });
 
     isnot(gBrowser.selectedTab, tab2,
           "Tab2 should be closed by pressing Ctrl+F4 on Tab2");
     is(gBrowser.tabs.length, 3,
       "The count of tabs should be 3 since tab2 should be closed");
 
-    let activeWindow = gBrowser.getBrowserForTab(gBrowser.selectedTab).contentWindow;
     // NOTE: keypress event shouldn't be fired since the keydown event should
     //       be consumed by tab2.
       EventUtils.synthesizeKey("VK_F4", { type: "keyup", ctrlKey: true });
       is(gBrowser.tabs.length, 3,
         "The count of tabs should be 3 since renaming key events shouldn't close other tabs");
   }
 
   gBrowser.selectedTab = tab3;
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -1052,17 +1052,17 @@ function getPropertyBagValue(bag, key) {
  *        in expectedExtra. It's possible that the crash report
  *        will contain other extra information that is not
  *        compared against.
  * @returns Promise
  */
 function promiseCrashReport(expectedExtra={}) {
   return Task.spawn(function*() {
     info("Starting wait on crash-report-status");
-    let [subject, data] =
+    let [subject, ] =
       yield TestUtils.topicObserved("crash-report-status", (subject, data) => {
         return data == "success";
       });
     info("Topic observed!");
 
     if (!(subject instanceof Ci.nsIPropertyBag2)) {
       throw new Error("Subject was not a Ci.nsIPropertyBag2");
     }
--- a/browser/base/content/test/newtab/browser_newtab_bug991111.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug991111.js
@@ -8,17 +8,16 @@ add_task(function* () {
   yield* addNewTabPageTab();
   // we need a second newtab to honor max rows
   yield* addNewTabPageTab();
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {index: 0}, function* (args) {
     let {site} = content.wrappedJSObject.gGrid.cells[args.index];
 
     let origOnClick = site.onClick;
-    let clicked = false;
     site.onClick = e => {
       origOnClick.call(site, e);
       sendAsyncMessage("test:clicked-on-cell", {});
     };
   });
 
   let mm = gBrowser.selectedBrowser.messageManager;
   let messagePromise = new Promise(resolve => {
--- a/browser/base/content/test/newtab/browser_newtab_bug991210.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug991210.js
@@ -10,17 +10,17 @@ add_task(function* () {
     getLinks: function(callback) {
       this.callback = callback;
     },
     addObserver: function() {},
   };
   NewTabUtils.links.addProvider(afterLoadProvider);
 
   // wait until about:newtab loads before calling provider callback
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:newtab");
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:newtab");
 
   afterLoadProvider.callback([]);
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
     let {_cellMargin, _cellHeight, _cellWidth, node} = content.gGrid;
     Assert.notEqual(_cellMargin, null, "grid has a computed cell margin");
     Assert.notEqual(_cellHeight, null, "grid has a computed cell height");
     Assert.notEqual(_cellWidth, null, "grid has a computed cell width");
--- a/browser/base/content/test/newtab/browser_newtab_bug998387.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug998387.js
@@ -8,17 +8,16 @@ add_task(function* () {
   yield* addNewTabPageTab();
   // we need a second newtab to honor max rows
   yield* addNewTabPageTab();
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {index: 0}, function* (args) {
     let {site} = content.wrappedJSObject.gGrid.cells[args.index];
 
     let origOnClick = site.onClick;
-    let clicked = false;
     site.onClick = e => {
       origOnClick.call(site, e);
       sendAsyncMessage("test:clicked-on-cell", {});
     };
   });
 
   let mm = gBrowser.selectedBrowser.messageManager;
   let messagePromise = new Promise(resolve => {
@@ -33,9 +32,8 @@ add_task(function* () {
                                                  {button: 1}, gBrowser.selectedBrowser);
 
   yield messagePromise;
   ok(true, "middle click triggered click listener");
 
   // Make sure the cell didn't actually get blocked
   yield* checkGrid("0");
 });
-
--- a/browser/base/content/test/newtab/browser_newtab_enhanced.js
+++ b/browser/base/content/test/newtab/browser_newtab_enhanced.js
@@ -116,17 +116,16 @@ add_task(function* () {
   data = yield getData(1);
   is(data, null, "directory link still pushed out by pinned history link");
 
   yield unpinCell(0);
 
 
 
   // Test that a suggested tile is not enhanced by a directory tile
-  let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
   NewTabUtils.isTopPlacesSite = () => true;
   yield setLinks("-1,2,3,4,5,6,7,8");
 
   // Test with enhanced = false
   yield* addNewTabPageTab();
   ({type, enhanced, title, suggested} = yield getData(0));
   isnot(type, "enhanced", "history link is not enhanced");
   is(enhanced, "", "history link has no enhanced image");
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -146,17 +146,17 @@ add_task(function* setup() {
   * @param aIndex index of cell
   * @param aFn function to call in child process or tab.
   * @returns result of calling the function.
   */
 function performOnCell(aIndex, aFn) {
   return ContentTask.spawn(gWindow.gBrowser.selectedBrowser,
                            { index: aIndex, fn: aFn.toString() }, function* (args) {
     let cell = content.gGrid.cells[args.index];
-    return eval("(" + args.fn + ")(cell)");
+    return eval(args.fn)(cell);
   });
 }
 
 /**
  * Allows to provide a list of links that is used to construct the grid.
  * @param aLinksPattern the pattern (see below)
  *
  * Example: setLinks("-1,0,1,2,3")
@@ -409,18 +409,16 @@ function* simulateExternalDrop(aDestInde
     return new Promise(resolve => {
       const url = "data:text/html;charset=utf-8," +
                   "<a id='link' href='http://example99.com/'>link</a>";
 
       let doc = content.document;
       let iframe = doc.createElement("iframe");
 
       function iframeLoaded() {
-        let link = iframe.contentDocument.getElementById("link");
-
         let dataTransfer = new iframe.contentWindow.DataTransfer("dragstart", false);
         dataTransfer.mozSetDataAt("text/x-moz-url", "http://example99.com/", 0);
 
         let event = content.document.createEvent("DragEvent");
         event.initDragEvent("drop", true, true, content, 0, 0, 0, 0, 0,
                             false, false, false, false, 0, null, dataTransfer);
 
         let target = content.gGrid.cells[dropIndex].node;
--- a/browser/base/content/test/plugins/browser_CTP_crashreporting.js
+++ b/browser/base/content/test/plugins/browser_CTP_crashreporting.js
@@ -134,17 +134,17 @@ add_task(function*() {
 
       // And wait for the parent to say that the crash report was submitted
       // successfully.
       yield ContentTaskUtils.waitForCondition(() => {
         return statusDiv.getAttribute("status") == "success";
       }, "Timed out waiting for plugin binding to be in success state");
     });
 
-    let [subject, data] = yield crashReportPromise;
+    let [subject, ] = yield crashReportPromise;
 
     ok(subject instanceof Ci.nsIPropertyBag,
        "The crash report subject should be an nsIPropertyBag.");
 
     let crashData = convertPropertyBag(subject);
     ok(crashData.serverCrashID, "Should have a serverCrashID set.");
 
     // Remove the submitted report file after ensuring it exists.
@@ -206,17 +206,17 @@ add_task(function*() {
     // Then click the button to submit the crash report.
     let buttons = notification.querySelectorAll(".notification-button");
     is(buttons.length, 2, "Should have two buttons.");
 
     // The "Submit Crash Report" button should be the second one.
     let submitButton = buttons[1];
     submitButton.click();
 
-    let [subject, data] = yield crashReportPromise;
+    let [subject, ] = yield crashReportPromise;
 
     ok(subject instanceof Ci.nsIPropertyBag,
        "The crash report subject should be an nsIPropertyBag.");
 
     let crashData = convertPropertyBag(subject);
     ok(crashData.serverCrashID, "Should have a serverCrashID set.");
 
     // Remove the submitted report file after ensuring it exists.
--- a/browser/base/content/test/plugins/browser_pageInfo_plugins.js
+++ b/browser/base/content/test/plugins/browser_pageInfo_plugins.js
@@ -75,17 +75,16 @@ function testPart1a() {
 
   doOnOpenPageInfo(testPart1b);
 }
 
 function testPart1b() {
   let testRadioGroup = gPageInfo.document.getElementById(gTestPermissionString + "RadioGroup");
   let testRadioDefault = gPageInfo.document.getElementById(gTestPermissionString + "#0");
 
-  var qString = "#" + gTestPermissionString.replace(':', '\\:') + "\\#0";
   is(testRadioGroup.selectedItem, testRadioDefault, "part 1b: Test radio group should be set to 'Default'");
   let testRadioAllow = gPageInfo.document.getElementById(gTestPermissionString + "#1");
   testRadioGroup.selectedItem = testRadioAllow;
   testRadioAllow.doCommand();
 
   let secondtestRadioGroup = gPageInfo.document.getElementById(gSecondTestPermissionString + "RadioGroup");
   let secondtestRadioDefault = gPageInfo.document.getElementById(gSecondTestPermissionString + "#0");
   is(secondtestRadioGroup.selectedItem, secondtestRadioDefault, "part 1b: Second Test radio group should be set to 'Default'");
--- a/browser/base/content/test/plugins/browser_pluginnotification.js
+++ b/browser/base/content/test/plugins/browser_pluginnotification.js
@@ -282,18 +282,16 @@ add_task(function* () {
 
   let pluginInfo = yield promiseForPluginInfo("test");
   ok(!pluginInfo.activated, "Test 18g, Plugin should not be activated");
 
   ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed,
      "Test 19e, Doorhanger should start out dismissed");
 
   yield ContentTask.spawn(gTestBrowser, null, function* () {
-    let doc = content.document;
-    let plugin = doc.getElementById("test");
     let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
     utils.sendMouseEvent("mousedown", 50, 50, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", 50, 50, 0, 1, 0, false, 0, 0);
   });
 
   let condition = () => !PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed &&
     PopupNotifications.panel.firstChild;
--- a/browser/base/content/test/plugins/head.js
+++ b/browser/base/content/test/plugins/head.js
@@ -10,18 +10,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 // The blocklist shim running in the content process does not initialize at
 // start up, so it's not active until we load content that needs to do a
 // check. This helper bypasses the delay to get the svc up and running
 // immediately. Note, call this after remote content has loaded.
 function promiseInitContentBlocklistSvc(aBrowser)
 {
   return ContentTask.spawn(aBrowser, {}, function* () {
     try {
-      let bls = Cc["@mozilla.org/extensions/blocklist;1"]
-                          .getService(Ci.nsIBlocklistService);
+      Cc["@mozilla.org/extensions/blocklist;1"]
+        .getService(Ci.nsIBlocklistService);
     } catch (ex) {
       return ex.message;
     }
     return null;
   });
 }
 
 /**
@@ -203,17 +203,16 @@ function promisePlayObject(aId, aBrowser
     objLoadingContent.playPlugin();
   });
 }
 
 function promiseCrashObject(aId, aBrowser) {
   let browser = aBrowser || gTestBrowser;
   return ContentTask.spawn(browser, aId, function* (aId) {
     let plugin = content.document.getElementById(aId);
-    let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
     Components.utils.waiveXrays(plugin).crash();
   });
 }
 
 // Return a promise and call the plugin's getObjectValue() method.
 function promiseObjectValueResult(aId, aBrowser) {
   let browser = aBrowser || gTestBrowser;
   return ContentTask.spawn(browser, aId, function* (aId) {
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_4.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_4.js
@@ -98,17 +98,16 @@ var tests = [
   // Moving a tab to a new window should remove non-swappable notifications.
   { id: "Test#5",
     run: function() {
       gBrowser.selectedTab = gBrowser.addTab("about:blank");
       let notifyObj = new BasicNotification(this.id);
       showNotification(notifyObj);
       let win = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
       whenDelayedStartupFinished(win, function() {
-        let [tab] = win.gBrowser.tabs;
         let anchor = win.document.getElementById("default-notification-icon");
         win.PopupNotifications._reshowNotifications(anchor);
         ok(win.PopupNotifications.panel.childNodes.length == 0,
            "no notification displayed in new window");
         ok(notifyObj.swappingCallbackTriggered, "the swapping callback was triggered");
         ok(notifyObj.removedCallbackTriggered, "the removed callback was triggered");
         win.close();
         goNext();
@@ -127,18 +126,16 @@ var tests = [
       notifyObj.options.eventCallback = function (eventName) {
         originalCallback(eventName);
         return eventName == "swapping";
       };
 
       let notification = showNotification(notifyObj);
       let win = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
       yield whenDelayedStartupFinished(win);
-      let [tab] = win.gBrowser.tabs;
-      let anchor = win.document.getElementById("default-notification-icon");
 
       yield new Promise(resolve => {
         let originalCallback = notification.options.eventCallback;
         notification.options.eventCallback = function (eventName) {
           originalCallback(eventName);
           if (eventName == "shown") {
             resolve();
           }
--- a/browser/base/content/test/social/browser_addons.js
+++ b/browser/base/content/test/social/browser_addons.js
@@ -194,17 +194,16 @@ var tests = {
     AddonManager.addAddonListener(installListener(next, manifest2));
 
     BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown").then(() => {
       let panel = document.getElementById("servicesInstall-notification");
       info("servicesInstall-notification panel opened");
       panel.button.click();
     });
 
-    let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
     Services.prefs.setCharPref("social.directories", manifest2.origin);
     is(SocialService.getOriginActivationType(manifest2.origin), "directory", "testing directory install");
     let data = {
       origin: manifest2.origin,
       url: manifest2.origin + "/directory",
       manifest: manifest2,
       window: window
     }
--- a/browser/base/content/test/social/browser_share.js
+++ b/browser/base/content/test/social/browser_share.js
@@ -113,17 +113,16 @@ var corpus = [
       previews: ["http://example.com/1234/56789.jpg"],
       url: "http://www.example.com/photos/56789/",
       shortUrl: "http://imshort/p/abcde"
     }
   }
 ];
 
 function hasoptions(testOptions, options) {
-  let msg;
   for (let option in testOptions) {
     let data = testOptions[option];
     info("data: "+JSON.stringify(data));
     let message_data = options[option];
     info("message_data: "+JSON.stringify(message_data));
     if (Array.isArray(data)) {
       // the message may have more array elements than we are testing for, this
       // is ok since some of those are hard to test. So we just test that
@@ -177,18 +176,16 @@ var tests = {
         ok(!shareButton.hasAttribute("disabled"), "share button is enabled");
         // button should be visible
         is(shareButton.hidden, false, "share button is visible");
         BrowserTestUtils.removeTab(tab).then(next);
       });
     });
   },
   testSharePage: function(next) {
-    let provider = Social._getProviderFromOrigin(manifest.origin);
-
     let testTab;
     let testIndex = 0;
     let testData = corpus[testIndex++];
 
     // initialize the button into the navbar
     CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
     // ensure correct state
     SocialUI.onCustomizeEnd(window);
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -105,17 +105,16 @@ function runSocialTestWithProvider(manif
       removeProvider(m.origin, callback);
     });
   }
   function finishSocialTest(cleanup) {
     removeAddedProviders(cleanup);
   }
 
   let providersAdded = 0;
-  let firstProvider;
 
   manifests.forEach(function (m) {
     SocialService.addProvider(m, function(provider) {
 
       providersAdded++;
       info("runSocialTestWithProvider: provider added");
 
       // we want to set the first specified provider as the UI's provider
--- a/browser/base/content/test/urlbar/browser_autocomplete_a11y_label.js
+++ b/browser/base/content/test/urlbar/browser_autocomplete_a11y_label.js
@@ -4,17 +4,16 @@
 const SUGGEST_ALL_PREF = "browser.search.suggest.enabled";
 const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
 const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
 
 add_task(function* switchToTab() {
   let tab = gBrowser.addTab("about:about");
   yield promiseTabLoaded(tab);
 
-  let actionURL = makeActionURI("switchtab", {url: "about:about"}).spec;
   yield promiseAutocompleteResultPopup("% about");
 
   ok(gURLBar.popup.richlistbox.children.length > 1, "Should get at least 2 results");
   let result = gURLBar.popup.richlistbox.children[1];
   is(result.getAttribute("type"), "switchtab", "Expect right type attribute");
   is(result.label, "about:about about:about Tab", "Result a11y label should be: <title> <url> Tab");
 
   gURLBar.popup.hidePopup();
--- a/browser/base/content/test/urlbar/browser_bug1104165-switchtab-decodeuri.js
+++ b/browser/base/content/test/urlbar/browser_bug1104165-switchtab-decodeuri.js
@@ -1,15 +1,15 @@
 add_task(function* test_switchtab_decodeuri() {
   info("Opening first tab");
   let tab = gBrowser.addTab("http://example.org/browser/browser/base/content/test/urlbar/dummy_page.html#test%7C1");
   yield promiseTabLoadEvent(tab);
 
   info("Opening and selecting second tab");
-  let newTab = gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedTab = gBrowser.addTab();
 
   info("Wait for autocomplete")
   yield promiseAutocompleteResultPopup("dummy_page");
 
   info("Select autocomplete popup entry");
   EventUtils.synthesizeKey("VK_DOWN", {});
   ok(gURLBar.value.startsWith("moz-action:switchtab"), "switch to tab entry found");
 
--- a/browser/base/content/test/urlbar/browser_search_favicon.js
+++ b/browser/base/content/test/urlbar/browser_search_favicon.js
@@ -20,17 +20,17 @@ add_task(function*() {
   gEngine = Services.search.getEngineByName("SearchEngine");
   gEngine.addParam("q", "{searchTerms}", null);
   gOriginalEngine = Services.search.currentEngine;
   Services.search.currentEngine = gEngine;
 
   let uri = NetUtil.newURI("http://s.example.com/search?q=foo&client=1");
   yield PlacesTestUtils.addVisits({ uri: uri, title: "Foo - SearchEngine Search" });
 
-  let tab = gBrowser.selectedTab = gBrowser.addTab("about:mozilla", {animate: false});
+  gBrowser.selectedTab = gBrowser.addTab("about:mozilla", {animate: false});
   yield promiseTabLoaded(gBrowser.selectedTab);
 
   // The first autocomplete result has the action searchengine, while
   // the second result is the "search favicon" element.
   yield promiseAutocompleteResultPopup("foo");
   let result = gURLBar.popup.richlistbox.children[1];
 
   isnot(result, null, "Expect a search result");
--- a/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar_perwindowpb.js
+++ b/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar_perwindowpb.js
@@ -18,17 +18,17 @@ add_task(function*() {
   yield BrowserTestUtils.closeWindow(privateWindow);
 
   normalWindow = yield BrowserTestUtils.openNewBrowserWindow();
   yield runTest(normalWindow, normalWindow, true);
   yield BrowserTestUtils.closeWindow(normalWindow);
 });
 
 function* runTest(aSourceWindow, aDestWindow, aExpectSwitch, aCallback) {
-  let baseTab = yield BrowserTestUtils.openNewForegroundTab(aSourceWindow.gBrowser, testURL);
+  yield BrowserTestUtils.openNewForegroundTab(aSourceWindow.gBrowser, testURL);
   let testTab = yield BrowserTestUtils.openNewForegroundTab(aDestWindow.gBrowser);
 
   info("waiting for focus on the window");
   yield SimpleTest.promiseFocus(aDestWindow);
   info("got focus on the window");
 
   // Select the testTab
   aDestWindow.gBrowser.selectedTab = testTab;
--- a/browser/base/content/test/urlbar/browser_urlbarDecode.js
+++ b/browser/base/content/test/urlbar/browser_urlbarDecode.js
@@ -45,17 +45,17 @@ add_task(function* actionURILosslessDeco
   // simply `url`.  Key down and back around until the heuristic result is
   // selected again, and at that point the urlbar's value should be a visiturl
   // moz-action.
 
   do {
     gURLBar.controller.handleKeyNavigation(KeyEvent.DOM_VK_DOWN);
   } while (gURLBar.popup.selectedIndex != 0);
 
-  let [, type, params] = gURLBar.value.match(/^moz-action:([^,]+),(.*)$/);
+  let [, type, ] = gURLBar.value.match(/^moz-action:([^,]+),(.*)$/);
   Assert.equal(type, "visiturl",
                "visiturl action URI should be in the urlbar");
 
   Assert.equal(gURLBar.inputField.value, urlNoScheme,
                "The string displayed in the textbox should not be escaped");
 
   gURLBar.value = "";
   gURLBar.handleRevert();
--- a/browser/base/content/test/urlbar/browser_urlbarEnterAfterMouseOver.js
+++ b/browser/base/content/test/urlbar/browser_urlbarEnterAfterMouseOver.js
@@ -18,17 +18,16 @@ function is_selected(index) {
 let gMaxResults;
 
 add_task(function*() {
   registerCleanupFunction(function* () {
     yield PlacesTestUtils.clearHistory();
   });
 
   yield PlacesTestUtils.clearHistory();
-  let tabCount = gBrowser.tabs.length;
 
   gMaxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults");
 
   let visits = [];
   repeat(gMaxResults, i => {
     visits.push({
       uri: makeURI("http://example.com/autocomplete/?" + i),
     });
--- a/browser/base/content/test/urlbar/browser_urlbarPrivateBrowsingWindowChange.js
+++ b/browser/base/content/test/urlbar/browser_urlbarPrivateBrowsingWindowChange.js
@@ -2,18 +2,16 @@
 
 /**
  * Test that when opening a private browsing window and typing in it before about:privatebrowsing
  * loads, we don't clear the URL bar.
  */
 add_task(function*() {
   let urlbarTestValue = "Mary had a little lamb";
   let win = OpenBrowserWindow({private: true});
-  let delayedStartupFinished = TestUtils.topicObserved("browser-delayed-startup-finished",
-                                                       subject => subject == win);
   yield BrowserTestUtils.waitForEvent(win, "load");
   let urlbar = win.document.getElementById("urlbar");
   urlbar.value = urlbarTestValue;
   // Need this so the autocomplete controller attaches:
   let focusEv = new FocusEvent("focus", {});
   urlbar.dispatchEvent(focusEv);
   // And so we know input happened:
   let inputEv = new InputEvent("input", {data: "", view: win, bubbles: true});
--- a/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions.js
+++ b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions.js
@@ -37,17 +37,16 @@ add_task(function* clickSuggestion() {
   Assert.ok(uri.equals(gBrowser.currentURI),
             "The search results page should have loaded");
   gBrowser.removeTab(gBrowser.selectedTab);
 });
 
 function getFirstSuggestion() {
   let controller = gURLBar.popup.input.controller;
   let matchCount = controller.matchCount;
-  let present = false;
   for (let i = 0; i < matchCount; i++) {
     let url = controller.getValueAt(i);
     let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/);
     if (mozActionMatch) {
       let [, type, paramStr] = mozActionMatch;
       let params = JSON.parse(paramStr);
       if (type == "searchengine" && "searchSuggestion" in params) {
         return [i, params.searchSuggestion, params.engineName];
--- a/browser/base/content/test/urlbar/browser_urlbarSearchSuggestionsNotification.js
+++ b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestionsNotification.js
@@ -216,17 +216,16 @@ function setUserMadeChoicePref(userMadeC
       resolve();
     }
   });
 }
 
 function suggestionsPresent() {
   let controller = gURLBar.popup.input.controller;
   let matchCount = controller.matchCount;
-  let present = false;
   for (let i = 0; i < matchCount; i++) {
     let url = controller.getValueAt(i);
     let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/);
     if (mozActionMatch) {
       let [, type, paramStr] = mozActionMatch;
       let params = JSON.parse(paramStr);
       if (type == "searchengine" && "searchSuggestion" in params) {
         return true;
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -72,17 +72,16 @@ file, You can obtain one at http://mozil
         this.inputField.controllers.insertControllerAt(0, this._copyCutController);
         this.inputField.addEventListener("paste", this, false);
         this.inputField.addEventListener("mousedown", this, false);
         this.inputField.addEventListener("mousemove", this, false);
         this.inputField.addEventListener("mouseout", this, false);
         this.inputField.addEventListener("overflow", this, false);
         this.inputField.addEventListener("underflow", this, false);
 
-        const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
         var textBox = document.getAnonymousElementByAttribute(this,
                                                 "anonid", "textbox-input-box");
         var cxmenu = document.getAnonymousElementByAttribute(textBox,
                                             "anonid", "input-box-contextmenu");
         var pasteAndGo;
         cxmenu.addEventListener("popupshowing", function() {
           if (!pasteAndGo)
             return;
@@ -2336,17 +2335,17 @@ file, You can obtain one at http://mozil
       </method>
       <method name="_setupSingleState">
         <body><![CDATA[
           var action = this._items[0].action;
           var prePath = action.pluginPermissionPrePath;
           let chromeWin = window.QueryInterface(Ci.nsIDOMChromeWindow);
           let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(chromeWin);
 
-          let label, linkLabel, linkUrl, button1, button2;
+          let label, linkLabel, button1, button2;
 
           if (action.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
             button1 = {
               label: "pluginBlockNow.label",
               accesskey: "pluginBlockNow.accesskey",
               action: "_singleBlock"
             };
             button2 = {
@@ -2463,17 +2462,16 @@ file, You can obtain one at http://mozil
           }
         ]]></body>
       </method>
       <method name="_setupDescription">
         <parameter name="baseString" />
         <parameter name="pluginName" /> <!-- null for the multiple-plugin case -->
         <parameter name="prePath" />
         <body><![CDATA[
-          var bsn = this._brandShortName;
           var span = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description");
           while (span.lastChild) {
             span.removeChild(span.lastChild);
           }
 
           var args = ["__prepath__", this._brandShortName];
           if (pluginName) {
             args.unshift(pluginName);
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -70,24 +70,18 @@ extensions.on("page-shutdown", (type, co
       let tab = gBrowser.getTabForBrowser(context.xulBrowser);
       if (tab) {
         gBrowser.removeTab(tab);
       }
     }
   }
 });
 
-extensions.on("fill-browser-data", (type, browser, data, result) => {
-  let tabId = TabManager.getBrowserId(browser);
-  if (tabId == -1) {
-    result.cancel = true;
-    return;
-  }
-
-  data.tabId = tabId;
+extensions.on("fill-browser-data", (type, browser, data) => {
+  data.tabId = browser ? TabManager.getBrowserId(browser) : -1;
 });
 /* eslint-enable mozilla/balanced-listeners */
 
 global.currentWindow = function(context) {
   let {xulWindow} = context;
   if (xulWindow && context.viewType != "background") {
     return xulWindow;
   }
--- a/browser/components/migration/360seProfileMigrator.js
+++ b/browser/components/migration/360seProfileMigrator.js
@@ -150,25 +150,25 @@ Bookmarks.prototype = {
               parentGuid =
                 yield MigrationUtils.createImportedBookmarksFolder("360se", parentGuid);
             }
             idToGuid.set("fallback", parentGuid);
           }
 
           try {
             if (is_folder == 1) {
-              let newFolderGuid = (yield PlacesUtils.bookmarks.insert({
+              let newFolderGuid = (yield MigrationUtils.insertBookmarkWrapper({
                 parentGuid,
                 type: PlacesUtils.bookmarks.TYPE_FOLDER,
                 title
               })).guid;
 
               idToGuid.set(id, newFolderGuid);
             } else {
-              yield PlacesUtils.bookmarks.insert({
+              yield MigrationUtils.insertBookmarkWrapper({
                 parentGuid,
                 url,
                 title
               });
             }
           } catch (ex) {
             Cu.reportError(ex);
           }
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ b/browser/components/migration/ChromeProfileMigrator.js
@@ -83,21 +83,21 @@ function* insertBookmarkItems(parentGuid
   for (let item of items) {
     try {
       if (item.type == "url") {
         if (item.url.trim().startsWith("chrome:")) {
           // Skip invalid chrome URIs. Creating an actual URI always reports
           // messages to the console, so we avoid doing that.
           continue;
         }
-        yield PlacesUtils.bookmarks.insert({
+        yield MigrationUtils.insertBookmarkWrapper({
           parentGuid, url: item.url, title: item.name
         });
       } else if (item.type == "folder") {
-        let newFolderGuid = (yield PlacesUtils.bookmarks.insert({
+        let newFolderGuid = (yield MigrationUtils.insertBookmarkWrapper({
           parentGuid, type: PlacesUtils.bookmarks.TYPE_FOLDER, title: item.name
         })).guid;
 
         yield insertBookmarkItems(newFolderGuid, item.children, errorAccumulator);
       }
     } catch (e) {
       Cu.reportError(e);
       errorAccumulator(e);
@@ -334,17 +334,17 @@ function GetHistoryResource(aProfileFold
             });
           } catch (e) {
             Cu.reportError(e);
           }
         }
 
         if (places.length > 0) {
           yield new Promise((resolve, reject) => {
-            PlacesUtils.asyncHistory.updatePlaces(places, {
+            MigrationUtils.insertVisitsWrapper(places, {
               _success: false,
               handleResult: function() {
                 // Importing any entry is considered a successful import.
                 this._success = true;
               },
               handleError: function() {},
               handleCompletion: function() {
                 if (this._success) {
@@ -442,17 +442,17 @@ function GetWindowsPasswordsResource(aPr
       let crypto = new OSCrypto();
 
       for (let row of rows) {
         let loginInfo = {
           username: row.getResultByName("username_value"),
           password: crypto.
                     decryptData(crypto.arrayToString(row.getResultByName("password_value")),
                                                      null),
-          hostName: NetUtil.newURI(row.getResultByName("origin_url")).prePath,
+          hostname: NetUtil.newURI(row.getResultByName("origin_url")).prePath,
           submitURL: null,
           httpRealm: null,
           usernameElement: row.getResultByName("username_element"),
           passwordElement: row.getResultByName("password_element"),
           timeCreated: chromeTimeToDate(row.getResultByName("date_created") + 0).getTime(),
           timesUsed: row.getResultByName("times_used") + 0,
         };
 
@@ -460,42 +460,23 @@ function GetWindowsPasswordsResource(aPr
           switch (row.getResultByName("scheme")) {
             case AUTH_TYPE.SCHEME_HTML:
               loginInfo.submitURL = NetUtil.newURI(row.getResultByName("action_url")).prePath;
               break;
             case AUTH_TYPE.SCHEME_BASIC:
             case AUTH_TYPE.SCHEME_DIGEST:
               // signon_realm format is URIrealm, so we need remove URI
               loginInfo.httpRealm = row.getResultByName("signon_realm")
-                                    .substring(loginInfo.hostName.length + 1);
+                                       .substring(loginInfo.hostname.length + 1);
               break;
             default:
               throw new Error("Login data scheme type not supported: " +
                               row.getResultByName("scheme"));
           }
-          let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
-
-          login.init(loginInfo.hostName, loginInfo.submitURL, loginInfo.httpRealm,
-                     loginInfo.username, loginInfo.password, loginInfo.usernameElement,
-                     loginInfo.passwordElement);
-          login.QueryInterface(Ci.nsILoginMetaInfo);
-          login.timeCreated = loginInfo.timeCreated;
-          login.timeLastUsed = loginInfo.timeCreated;
-          login.timePasswordChanged = loginInfo.timeCreated;
-          login.timesUsed = loginInfo.timesUsed;
-
-          // Add the login only if there's not an existing entry
-          let logins = Services.logins.findLogins({}, login.hostname,
-                                                  login.formSubmitURL,
-                                                  login.httpRealm);
-
-          // Bug 1187190: Password changes should be propagated depending on timestamps.
-          if (!logins.some(l => login.matches(l, true))) {
-            Services.logins.addLogin(login);
-          }
+          MigrationUtils.insertLoginWrapper(loginInfo);
         } catch (e) {
           Cu.reportError(e);
         }
       }
       crypto.finalize();
       aCallback(true);
     }),
   };
--- a/browser/components/migration/EdgeProfileMigrator.js
+++ b/browser/components/migration/EdgeProfileMigrator.js
@@ -133,17 +133,17 @@ EdgeTypedURLMigrator.prototype = {
       });
     }
 
     if (places.length == 0) {
       aCallback(typedURLs.size == 0);
       return;
     }
 
-    PlacesUtils.asyncHistory.updatePlaces(places, {
+    MigrationUtils.insertVisitsWrapper(places, {
       _success: false,
       handleResult: function() {
         // Importing any entry is considered a successful import.
         this._success = true;
       },
       handleError: function() {},
       handleCompletion: function() {
         aCallback(this._success);
@@ -196,17 +196,17 @@ EdgeReadingListMigrator.prototype = {
     if (!readingListItems.length) {
       return;
     }
 
     let destFolderGuid = yield this._ensureReadingListFolder(parentGuid);
     let exceptionThrown;
     for (let item of readingListItems) {
       let dateAdded = item.AddedDate || new Date();
-      yield PlacesUtils.bookmarks.insert({
+      yield MigrationUtils.insertBookmarkWrapper({
         parentGuid: destFolderGuid, url: item.URL, title: item.Title, dateAdded
       }).catch(ex => {
         if (!exceptionThrown) {
           exceptionThrown = ex;
         }
         Cu.reportError(ex);
       });
     }
@@ -214,17 +214,17 @@ EdgeReadingListMigrator.prototype = {
       throw exceptionThrown;
     }
   }),
 
   _ensureReadingListFolder: Task.async(function*(parentGuid) {
     if (!this.__readingListFolderGuid) {
       let folderTitle = MigrationUtils.getLocalizedString("importedEdgeReadingList");
       let folderSpec = {type: PlacesUtils.bookmarks.TYPE_FOLDER, parentGuid, title: folderTitle};
-      this.__readingListFolderGuid = (yield PlacesUtils.bookmarks.insert(folderSpec)).guid;
+      this.__readingListFolderGuid = (yield MigrationUtils.insertBookmarkWrapper(folderSpec)).guid;
     }
     return this.__readingListFolderGuid;
   }),
 };
 
 function EdgeBookmarksMigrator(dbOverride) {
   this.dbOverride = dbOverride;
 }
@@ -317,17 +317,17 @@ EdgeBookmarksMigrator.prototype = {
       }
       let placesInfo = {
         parentGuid,
         url: bookmark.URL,
         dateAdded: bookmark.DateUpdated || new Date(),
         title: bookmark.Title,
       };
 
-      yield PlacesUtils.bookmarks.insert(placesInfo).catch(ex => {
+      yield MigrationUtils.insertBookmarkWrapper(placesInfo).catch(ex => {
         if (!exceptionThrown) {
           exceptionThrown = ex;
         }
         Cu.reportError(ex);
       });
     }
 
     if (exceptionThrown) {
@@ -385,17 +385,17 @@ EdgeBookmarksMigrator.prototype = {
     let parentGuid = yield this._getGuidForFolder(folder.ParentId, folderMap, rootGuid);
     let folderInfo = {
       title: folder.Title,
       type: PlacesUtils.bookmarks.TYPE_FOLDER,
       dateAdded: folder.DateUpdated || new Date(),
       parentGuid,
     };
     // and add ourselves as a kid, and return the guid we got.
-    let parentBM = yield PlacesUtils.bookmarks.insert(folderInfo);
+    let parentBM = yield MigrationUtils.insertBookmarkWrapper(folderInfo);
     folder._guid = parentBM.guid;
     return folder._guid;
   }),
 };
 
 function EdgeProfileMigrator() {
   this.wrappedJSObject = this;
 }
--- a/browser/components/migration/IEProfileMigrator.js
+++ b/browser/components/migration/IEProfileMigrator.js
@@ -14,17 +14,16 @@ const kMainKey = "Software\\Microsoft\\I
 
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/osfile.jsm"); /* globals OS */
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm"); /* globals MigratorPrototype */
 Cu.import("resource:///modules/MSMigrationUtils.jsm");
-Cu.import("resource://gre/modules/LoginHelper.jsm");
 
 
 XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
                                   "resource://gre/modules/ctypes.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
                                   "resource://gre/modules/OSCrypto.jsm");
@@ -88,17 +87,17 @@ History.prototype = {
     }
 
     // Check whether there is any history to import.
     if (places.length == 0) {
       aCallback(true);
       return;
     }
 
-    PlacesUtils.asyncHistory.updatePlaces(places, {
+    MigrationUtils.insertVisitsWrapper(places, {
       _success: false,
       handleResult: function() {
         // Importing any entry is considered a successful import.
         this._success = true;
       },
       handleError: function() {},
       handleCompletion: function() {
         aCallback(this._success);
@@ -244,17 +243,17 @@ IE7FormPasswords.prototype = {
       try {
         // create a new login
         let login = {
           username: ieLogin.username,
           password: ieLogin.password,
           hostname: ieLogin.url,
           timeCreated: ieLogin.creation,
         };
-        LoginHelper.maybeImportLogin(login);
+        MigrationUtils.insertLoginWrapper(login);
       } catch (e) {
         Cu.reportError(e);
       }
     }
   },
 
   /**
    * Extract the details of one or more logins from the raw decrypted data.
--- a/browser/components/migration/MSMigrationUtils.jsm
+++ b/browser/components/migration/MSMigrationUtils.jsm
@@ -8,17 +8,16 @@ this.EXPORTED_SYMBOLS = ["MSMigrationUti
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm");
-Cu.import("resource://gre/modules/LoginHelper.jsm");
 
 Cu.importGlobalProperties(["FileReader"]);
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
                                   "resource://gre/modules/WindowsRegistry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
@@ -402,17 +401,17 @@ Bookmarks.prototype = {
             folderGuid = PlacesUtils.bookmarks.toolbarGuid;
             if (!MigrationUtils.isStartupMigration) {
               folderGuid =
                 yield MigrationUtils.createImportedBookmarksFolder(this.importedAppLabel, folderGuid);
             }
           }
           else {
             // Import to a new folder.
-            folderGuid = (yield PlacesUtils.bookmarks.insert({
+            folderGuid = (yield MigrationUtils.insertBookmarkWrapper({
               type: PlacesUtils.bookmarks.TYPE_FOLDER,
               parentGuid: aDestFolderGuid,
               title: entry.leafName
             })).guid;
           }
 
           if (entry.isReadable()) {
             // Recursively import the folder.
@@ -424,17 +423,17 @@ Bookmarks.prototype = {
           // and get the associated title.
           let matches = entry.leafName.match(/(.+)\.url$/i);
           if (matches) {
             let fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"].
                               getService(Ci.nsIFileProtocolHandler);
             let uri = fileHandler.readURLFile(entry);
             let title = matches[1];
 
-            yield PlacesUtils.bookmarks.insert({
+            yield MigrationUtils.insertBookmarkWrapper({
               parentGuid: aDestFolderGuid, url: uri, title
             });
           }
         }
       } catch (ex) {
         Components.utils.reportError("Unable to import " + this.importedAppLabel + " favorite (" + entry.leafName + "): " + ex);
         succeeded = false;
       }
@@ -833,17 +832,17 @@ WindowsVaultFormPasswords.prototype = {
             // Ignore exceptions in the dates and just create the login for right now.
           }
           // create a new login
           let login = {
             username, password,
             hostname: realURL.prePath,
             timeCreated: creation,
           };
-          LoginHelper.maybeImportLogin(login);
+          MigrationUtils.insertLoginWrapper(login);
 
           // close current item
           error = ctypesVaultHelpers._functions.VaultFree(credential);
           if (error == FREE_CLOSE_FAILED) {
             throw new Error("Unable to free item: " + error);
           }
         } catch (e) {
           migrationSucceeded = false;
--- a/browser/components/migration/MigrationUtils.jsm
+++ b/browser/components/migration/MigrationUtils.jsm
@@ -15,16 +15,18 @@ Cu.import("resource://gre/modules/AppCon
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AutoMigrate",
                                   "resource:///modules/AutoMigrate.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils",
                                   "resource://gre/modules/BookmarkHTMLUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
+                                  "resource://gre/modules/LoginHelper.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
                                   "resource://gre/modules/PromiseUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
                                   "resource://gre/modules/Sqlite.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
                                   "resource://gre/modules/TelemetryStopwatch.jsm");
@@ -249,16 +251,27 @@ this.MigratorPrototype = {
     };
     let maybeStopTelemetryStopwatch = (resourceType, resource) => {
       let histogram = getHistogramForResourceType(resourceType);
       if (histogram) {
         TelemetryStopwatch.finishKeyed(histogram, this.getKey(), resource);
       }
     };
 
+    let collectQuantityTelemetry = () => {
+      try {
+        for (let resourceType of Object.keys(MigrationUtils._importQuantities)) {
+          let histogramId =
+            "FX_MIGRATION_" + resourceType.toUpperCase() + "_QUANTITY";
+          let histogram = Services.telemetry.getKeyedHistogram(histogramId);
+          histogram.add(this.getKey(), MigrationUtils._importQuantities[resourceType]);
+        }
+      } catch (ex) { /* Telemetry is exception-happy */ }
+    };
+
     // Called either directly or through the bookmarks import callback.
     let doMigrate = Task.async(function*() {
       let resourcesGroupedByItems = new Map();
       resources.forEach(function(resource) {
         if (!resourcesGroupedByItems.has(resource.type)) {
           resourcesGroupedByItems.set(resource.type, new Set());
         }
         resourcesGroupedByItems.get(resource.type).add(resource);
@@ -266,16 +279,19 @@ this.MigratorPrototype = {
 
       if (resourcesGroupedByItems.size == 0)
         throw new Error("No items to import");
 
       let notify = function(aMsg, aItemType) {
         Services.obs.notifyObservers(null, aMsg, aItemType);
       };
 
+      for (let resourceType of Object.keys(MigrationUtils._importQuantities)) {
+        MigrationUtils._importQuantities[resourceType] = 0;
+      }
       notify("Migration:Started");
       for (let [key, value] of resourcesGroupedByItems) {
         // Workaround bug 449811.
         let migrationType = key, itemResources = value;
 
         notify("Migration:ItemBeforeMigrate", migrationType);
 
         let itemSuccess = false;
@@ -289,16 +305,17 @@ this.MigratorPrototype = {
             itemResources.delete(resource);
             itemSuccess |= aSuccess;
             if (itemResources.size == 0) {
               notify(itemSuccess ?
                      "Migration:ItemAfterMigrate" : "Migration:ItemError",
                      migrationType);
               resourcesGroupedByItems.delete(migrationType);
               if (resourcesGroupedByItems.size == 0) {
+                collectQuantityTelemetry();
                 notify("Migration:Ended");
               }
             }
             completeDeferred.resolve();
           };
 
           // If migrate throws, an error occurred, and the callback
           // (itemMayBeDone) might haven't been called.
@@ -669,30 +686,35 @@ this.MigrationUtils = Object.freeze({
    */
   getMigratorKeyForDefaultBrowser() {
     // Canary uses the same description as Chrome so we can't distinguish them.
     const APP_DESC_TO_KEY = {
       "Internet Explorer":                 "ie",
       "Microsoft Edge":                    "edge",
       "Safari":                            "safari",
       "Firefox":                           "firefox",
+      "Nightly":                           "firefox",
       "Google Chrome":                     "chrome",  // Windows, Linux
       "Chrome":                            "chrome",  // OS X
       "Chromium":                          "chromium", // Windows, OS X
       "Chromium Web Browser":              "chromium", // Linux
       "360\u5b89\u5168\u6d4f\u89c8\u5668": "360se",
     };
 
     let key = "";
     try {
       let browserDesc =
         Cc["@mozilla.org/uriloader/external-protocol-service;1"]
           .getService(Ci.nsIExternalProtocolService)
           .getApplicationDescription("http");
       key = APP_DESC_TO_KEY[browserDesc] || "";
+      // Handle devedition, as well as "FirefoxNightly" on OS X.
+      if (!key && browserDesc.startsWith("Firefox")) {
+        key = "firefox";
+      }
     }
     catch (ex) {
       Cu.reportError("Could not detect default browser: " + ex);
     }
 
     // "firefox" is the least useful entry here, and might just be because we've set
     // ourselves as the default (on Windows 7 and below). In that case, check if we
     // have a registry key that tells us where to go:
@@ -902,16 +924,37 @@ this.MigrationUtils = Object.freeze({
       migrator,
       aProfileStartup,
       skipSourcePage,
       aProfileToMigrate,
     ];
     this.showMigrationWizard(null, params);
   },
 
+  _importQuantities: {
+    bookmarks: 0,
+    logins: 0,
+    history: 0,
+  },
+
+  insertBookmarkWrapper(bookmark) {
+    this._importQuantities.bookmarks++;
+    return PlacesUtils.bookmarks.insert(bookmark);
+  },
+
+  insertVisitsWrapper(places, options) {
+    this._importQuantities.history += places.length;
+    return PlacesUtils.asyncHistory.updatePlaces(places, options);
+  },
+
+  insertLoginWrapper(login) {
+    this._importQuantities.logins++;
+    return LoginHelper.maybeImportLogin(login);
+  },
+
   /**
    * Cleans up references to migrators and nsIProfileInstance instances.
    */
   finishMigration: function MU_finishMigration() {
     gMigrators = null;
     gProfileStartup = null;
     gMigrationBundle = null;
   },
--- a/browser/components/migration/SafariProfileMigrator.js
+++ b/browser/components/migration/SafariProfileMigrator.js
@@ -126,17 +126,17 @@ Bookmarks.prototype = {
             yield MigrationUtils.createImportedBookmarksFolder("Safari", folderGuid);
         }
         break;
       }
       case this.READING_LIST_COLLECTION: {
         // Reading list items are imported as regular bookmarks.
         // They are imported under their own folder, created either under the
         // bookmarks menu (in the case of startup migration).
-        folderGuid = (yield PlacesUtils.bookmarks.insert({
+        folderGuid = (yield MigrationUtils.insertBookmarkWrapper({
           parentGuid: PlacesUtils.bookmarks.menuGuid,
           type: PlacesUtils.bookmarks.TYPE_FOLDER,
           title: MigrationUtils.getLocalizedString("importedSafariReadingList"),
         })).guid;
         break;
       }
       default:
         throw new Error("Unexpected value for aCollection!");
@@ -149,31 +149,31 @@ Bookmarks.prototype = {
 
   // migrate the given array of safari bookmarks to the given places
   // folder.
   _migrateEntries: Task.async(function* (entries, parentGuid) {
     for (let entry of entries) {
       let type = entry.get("WebBookmarkType");
       if (type == "WebBookmarkTypeList" && entry.has("Children")) {
         let title = entry.get("Title");
-        let newFolderGuid = (yield PlacesUtils.bookmarks.insert({
+        let newFolderGuid = (yield MigrationUtils.insertBookmarkWrapper({
           parentGuid, type: PlacesUtils.bookmarks.TYPE_FOLDER, title
         })).guid;
 
         // Empty folders may not have a children array.
         if (entry.has("Children"))
           yield this._migrateEntries(entry.get("Children"), newFolderGuid, false);
       }
       else if (type == "WebBookmarkTypeLeaf" && entry.has("URLString")) {
         let title;
         if (entry.has("URIDictionary"))
           title = entry.get("URIDictionary").get("title");
 
         try {
-          yield PlacesUtils.bookmarks.insert({
+          yield MigrationUtils.insertBookmarkWrapper({
             parentGuid, url: entry.get("URLString"), title
           });
         } catch (ex) {
           Cu.reportError("Invalid Safari bookmark: " + ex);
         }
       }
     }
   })
@@ -225,17 +225,17 @@ History.prototype = {
             catch (ex) {
               // Safari's History file may contain malformed URIs which
               // will be ignored.
               Cu.reportError(ex);
             }
           }
         }
         if (places.length > 0) {
-          PlacesUtils.asyncHistory.updatePlaces(places, {
+          MigrationUtils.insertVisitsWrapper(places, {
             _success: false,
             handleResult: function() {
               // Importing any entry is considered a successful import.
               this._success = true;
             },
             handleError: function() {},
             handleCompletion: function() {
               aCallback(this._success);
--- a/browser/components/migration/tests/unit/test_Chrome_passwords.js
+++ b/browser/components/migration/tests/unit/test_Chrome_passwords.js
@@ -168,16 +168,18 @@ add_task(function* test_importIntoEmptyD
   let logins = Services.logins.getAllLogins({});
   Assert.equal(logins.length, 0, "There are no logins initially");
 
   // Migrate the logins.
   yield promiseMigration(migrator, MigrationUtils.resourceTypes.PASSWORDS, PROFILE);
 
   logins = Services.logins.getAllLogins({});
   Assert.equal(logins.length, TEST_LOGINS.length, "Check login count after importing the data");
+  Assert.equal(logins.length, MigrationUtils._importQuantities.logins,
+               "Check telemetry matches the actual import.");
 
   for (let i = 0; i < TEST_LOGINS.length; i++) {
     checkLoginsAreEqual(logins[i], TEST_LOGINS[i], i + 1);
   }
 });
 
 // Test that existing logins for the same primary key don't get overwritten
 add_task(function* test_importExistingLogins() {
@@ -203,13 +205,15 @@ add_task(function* test_importExistingLo
     checkLoginsAreEqual(logins[i], newLogins[i], i + 1);
   }
   // Migrate the logins.
   yield promiseMigration(migrator, MigrationUtils.resourceTypes.PASSWORDS, PROFILE);
 
   logins = Services.logins.getAllLogins({});
   Assert.equal(logins.length, TEST_LOGINS.length,
                "Check there are still the same number of logins after re-importing the data");
+  Assert.equal(logins.length, MigrationUtils._importQuantities.logins,
+               "Check telemetry matches the actual import.");
 
   for (let i = 0; i < newLogins.length; i++) {
     checkLoginsAreEqual(logins[i], newLogins[i], i + 1);
   }
 });
--- a/browser/components/migration/tests/unit/test_Edge_db_migration.js
+++ b/browser/components/migration/tests/unit/test_Edge_db_migration.js
@@ -382,16 +382,19 @@ add_task(function*() {
     {type: COLUMN_TYPES.JET_coltypGUID, name: "ParentId"},
   ], itemsInDB);
 
   let migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=edge"]
                  .createInstance(Ci.nsIBrowserProfileMigrator);
   let bookmarksMigrator = migrator.wrappedJSObject.getESEMigratorForTesting(db);
   Assert.ok(bookmarksMigrator.exists, "Should recognize table we just created");
 
+  let source = MigrationUtils.getLocalizedString("sourceNameEdge");
+  let sourceLabel = MigrationUtils.getLocalizedString("importedBookmarksFolder", [source]);
+
   let seenBookmarks = [];
   let bookmarkObserver = {
     onItemAdded(itemId, parentId, index, itemType, url, title, dateAdded, itemGuid, parentGuid) {
       if (title.startsWith("Deleted")) {
         ok(false, "Should not see deleted items being bookmarked!");
       }
       seenBookmarks.push({itemId, parentId, index, itemType, url, title, dateAdded, itemGuid, parentGuid});
     },
@@ -407,16 +410,19 @@ add_task(function*() {
   let migrateResult = yield new Promise(resolve => bookmarksMigrator.migrate(resolve)).catch(ex => {
     Cu.reportError(ex);
     Assert.ok(false, "Got an exception trying to migrate data! " + ex);
     return false;
   });
   PlacesUtils.bookmarks.removeObserver(bookmarkObserver);
   Assert.ok(migrateResult, "Migration should succeed");
   Assert.equal(seenBookmarks.length, 7, "Should have seen 7 items being bookmarked.");
+  Assert.equal(seenBookmarks.filter(bm => bm.title != sourceLabel).length,
+               MigrationUtils._importQuantities.bookmarks,
+               "Telemetry should have items except for 'From Microsoft Edge' folders");
 
   let menuParents = seenBookmarks.filter(item => item.parentGuid == PlacesUtils.bookmarks.menuGuid);
   Assert.equal(menuParents.length, 1, "Should have a single folder added to the menu");
   let toolbarParents = seenBookmarks.filter(item => item.parentGuid == PlacesUtils.bookmarks.toolbarGuid);
   Assert.equal(toolbarParents.length, 1, "Should have a single item added to the toolbar");
   let menuParentGuid = menuParents[0].itemGuid;
   let toolbarParentGuid = toolbarParents[0].itemGuid;
 
--- a/browser/components/migration/tests/unit/test_IE7_passwords.js
+++ b/browser/components/migration/tests/unit/test_IE7_passwords.js
@@ -373,16 +373,25 @@ add_task(function* test_passwordsAvailab
 
     migrator._migrateURIs(uris);
     logins = Services.logins.getAllLogins({});
     // check that the number of logins in the password manager has increased as expected which means
     // that all the values for the current website were imported
     loginCount += website.logins.length;
     Assert.equal(logins.length, loginCount,
                  "The number of logins has increased after the migration");
+    // NB: because telemetry records any login data passed to the login manager, it
+    // also gets told about logins that are duplicates or invalid (for one reason
+    // or another) and so its counts might exceed those of the login manager itself.
+    Assert.greaterOrEqual(MigrationUtils._importQuantities.logins, loginCount,
+                          "Telemetry quantities equal or exceed the actual import.");
+    // Reset - this normally happens at the start of a new migration, but we're calling
+    // the migrator directly so can't rely on that:
+    MigrationUtils._importQuantities.logins = 0;
+
     let startIndex = loginCount - website.logins.length;
     // compares the imported password manager logins with their expected logins
     for (let i = 0; i < website.logins.length; i++) {
       checkLoginsAreEqual(logins[startIndex + i], website.logins[i],
                           " " + current + " - " + i + " ");
     }
   }
 });
--- a/browser/components/migration/tests/unit/test_IE_bookmarks.js
+++ b/browser/components/migration/tests/unit/test_IE_bookmarks.js
@@ -8,31 +8,37 @@ add_task(function* () {
   // Wait for the imported bookmarks.  Check that "From Internet Explorer"
   // folders are created in the menu and on the toolbar.
   let source = MigrationUtils.getLocalizedString("sourceNameIE");
   let label = MigrationUtils.getLocalizedString("importedBookmarksFolder", [source]);
 
   let expectedParents = [ PlacesUtils.bookmarksMenuFolderId,
                           PlacesUtils.toolbarFolderId ];
 
-  PlacesUtils.bookmarks.addObserver({
+  let itemCount = 0;
+  let bmObserver = {
     onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle) {
-      if (aTitle == label) {
+      if (aTitle != label) {
+        itemCount++;
+      }
+      if (expectedParents.length > 0 && aTitle == label) {
         let index = expectedParents.indexOf(aParentId);
         Assert.notEqual(index, -1);
         expectedParents.splice(index, 1);
-        if (expectedParents.length == 0)
-          PlacesUtils.bookmarks.removeObserver(this);
       }
     },
     onBeginUpdateBatch() {},
     onEndUpdateBatch() {},
     onItemRemoved() {},
     onItemChanged() {},
     onItemVisited() {},
     onItemMoved() {},
-  }, false);
+  };
+  PlacesUtils.bookmarks.addObserver(bmObserver, false);
 
   yield promiseMigration(migrator, MigrationUtils.resourceTypes.BOOKMARKS);
+  PlacesUtils.bookmarks.removeObserver(bmObserver);
+  Assert.equal(MigrationUtils._importQuantities.bookmarks, itemCount,
+               "Ensure telemetry matches actual number of imported items.");
 
   // Check the bookmarks have been imported to all the expected parents.
-  Assert.equal(expectedParents.length, 0);
+  Assert.equal(expectedParents.length, 0, "Got all the expected parents");
 });
--- a/browser/components/migration/tests/unit/test_Safari_bookmarks.js
+++ b/browser/components/migration/tests/unit/test_Safari_bookmarks.js
@@ -8,32 +8,39 @@ add_task(function* () {
   Assert.ok(migrator.sourceExists);
 
   // Wait for the imported bookmarks.  Check that "From Safari"
   // folders are created on the toolbar.
   let source = MigrationUtils.getLocalizedString("sourceNameSafari");
   let label = MigrationUtils.getLocalizedString("importedBookmarksFolder", [source]);
 
   let expectedParents = [ PlacesUtils.toolbarFolderId ];
+  let itemCount = 0;
 
-  PlacesUtils.bookmarks.addObserver({
+  let bmObserver = {
     onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle) {
-      if (aTitle == label) {
+      if (aTitle != label) {
+        itemCount++;
+      }
+      if (expectedParents.length > 0 && aTitle == label) {
         let index = expectedParents.indexOf(aParentId);
-        Assert.notEqual(index, -1);
+        Assert.ok(index != -1, "Found expected parent");
         expectedParents.splice(index, 1);
-        if (expectedParents.length == 0)
-          PlacesUtils.bookmarks.removeObserver(this);
       }
     },
     onBeginUpdateBatch() {},
     onEndUpdateBatch() {},
     onItemRemoved() {},
     onItemChanged() {},
     onItemVisited() {},
     onItemMoved() {},
-  }, false);
+  };
+  PlacesUtils.bookmarks.addObserver(bmObserver, false);
 
   yield promiseMigration(migrator, MigrationUtils.resourceTypes.BOOKMARKS);
+  PlacesUtils.bookmarks.removeObserver(bmObserver);
 
   // Check the bookmarks have been imported to all the expected parents.
-  Assert.equal(expectedParents.length, 0);
+  Assert.ok(!expectedParents.length, "No more expected parents");
+  Assert.equal(itemCount, 13, "Should import all 13 items.");
+  // Check that the telemetry matches:
+  Assert.equal(MigrationUtils._importQuantities.bookmarks, itemCount, "Telemetry reporting correct.");
 });
--- a/browser/components/originattributes/test/browser/head.js
+++ b/browser/components/originattributes/test/browser/head.js
@@ -276,16 +276,21 @@ this.IsolationTestTools = {
    *    An optional function which is called before any tabs are created so
    *    that the test case can set up/reset local state.
    */
   runTests(aURL, aGetResultFuncs, aCompareResultFunc, aBeforeFunc) {
     let pageURL;
     let firstFrameSetting;
     let secondFrameSetting;
 
+    // Request a longer timeout since the test will run a test for three times
+    // with different settings. Thus, one test here represents three tests.
+    // For this reason, we triple the timeout.
+    requestLongerTimeout(3);
+
     if (typeof aURL === "string") {
       pageURL = aURL;
     } else if (typeof aURL === "object") {
       pageURL = aURL.url;
       firstFrameSetting = aURL.firstFrameSetting;
       secondFrameSetting = aURL.secondFrameSetting;
     }
 
--- a/browser/components/places/tests/unit/head_bookmarks.js
+++ b/browser/components/places/tests/unit/head_bookmarks.js
@@ -73,16 +73,20 @@ var createCorruptDB = Task.async(functio
  * exception generated.
  *
  * @return {Promise}
  *         Resolved when done.
  */
 function rebuildSmartBookmarks() {
   let consoleListener = {
     observe(aMsg) {
+      if (aMsg.message.startsWith("[JavaScript Warning:")) {
+        // TODO (Bug 1300416): Ignore spurious strict warnings.
+        return;
+      }
       do_throw("Got console message: " + aMsg.message);
     },
     QueryInterface: XPCOMUtils.generateQI([ Ci.nsIConsoleListener ]),
   };
   Services.console.reset();
   Services.console.registerListener(consoleListener);
   do_register_cleanup(() => {
     try {
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -206,16 +206,20 @@ this.SessionStore = {
   get canRestoreLastSession() {
     return SessionStoreInternal.canRestoreLastSession;
   },
 
   set canRestoreLastSession(val) {
     SessionStoreInternal.canRestoreLastSession = val;
   },
 
+  get lastClosedObjectType() {
+    return SessionStoreInternal.lastClosedObjectType;
+  },
+
   init: function ss_init() {
     SessionStoreInternal.init();
   },
 
   getBrowserState: function ss_getBrowserState() {
     return SessionStoreInternal.getBrowserState();
   },
 
@@ -334,16 +338,20 @@ this.SessionStore = {
   navigateAndRestore(tab, loadArguments, historyIndex) {
     return SessionStoreInternal.navigateAndRestore(tab, loadArguments, historyIndex);
   },
 
   getSessionHistory(tab, updatedCallback) {
     return SessionStoreInternal.getSessionHistory(tab, updatedCallback);
   },
 
+  undoCloseById(aClosedId) {
+    return SessionStoreInternal.undoCloseById(aClosedId);
+  },
+
   /**
    * Determines whether the passed version number is compatible with
    * the current version number of the SessionStore.
    *
    * @param version The format and version of the file, as an array, e.g.
    * ["sessionrestore", 1]
    */
   isFormatVersionCompatible(version) {
@@ -373,16 +381,19 @@ var SessionStoreInternal = {
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIDOMEventListener,
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference
   ]),
 
   _globalState: new GlobalState(),
 
+  // A counter to be used to generate a unique ID for each closed tab or window.
+  _nextClosedId: 0,
+
   // During the initial restore and setBrowserState calls tracks the number of
   // windows yet to be restored
   _restoreCount: -1,
 
   // For each <browser> element, records the current epoch.
   _browserEpochs: new WeakMap(),
 
   // Any browsers that fires the oop-browser-crashed event gets stored in
@@ -492,16 +503,43 @@ var SessionStoreInternal = {
   set canRestoreLastSession(val) {
     // Cheat a bit; only allow false.
     if (!val) {
       LastSession.clear();
     }
   },
 
   /**
+   * Returns a string describing the last closed object, either "tab" or "window".
+   *
+   * This was added to support the sessions.restore WebExtensions API.
+   */
+  get lastClosedObjectType() {
+    if (this._closedWindows.length) {
+      // Since there are closed windows, we need to check if there's a closed tab
+      // in one of the currently open windows that was closed after the
+      // last-closed window.
+      let tabTimestamps = [];
+      let windowsEnum = Services.wm.getEnumerator("navigator:browser");
+      while (windowsEnum.hasMoreElements()) {
+        let window = windowsEnum.getNext();
+        let windowState = this._windows[window.__SSi];
+        if (windowState && windowState._closedTabs[0]) {
+          tabTimestamps.push(windowState._closedTabs[0].closedAt);
+        }
+      }
+      if (!tabTimestamps.length ||
+          (tabTimestamps.sort((a, b) => b - a)[0] < this._closedWindows[0].closedAt)) {
+        return "window";
+      }
+    }
+    return "tab";
+  },
+
+  /**
    * Initialize the sessionstore service.
    */
   init: function () {
     if (this._initialized) {
       throw new Error("SessionStore.init() must only be called once!");
     }
 
     TelemetryTimestamps.add("sessionRestoreInitialized");
@@ -1426,16 +1464,19 @@ var SessionStoreInternal = {
         });
 
         // If we found no tab closed before our
         // tab then just append it to the list.
         if (index == -1) {
           index = this._closedWindows.length;
         }
 
+        // About to save the closed window, add a unique ID.
+        winData.closedId = this._nextClosedId++;
+
         // Insert tabData at the right position.
         this._closedWindows.splice(index, 0, winData);
         this._capClosedWindows();
       } else if (!shouldStore && alreadyStored) {
         this._closedWindows.splice(winIndex, 1);
       }
     }
   },
@@ -1847,16 +1888,19 @@ var SessionStoreInternal = {
     });
 
     // If we found no tab closed before our
     // tab then just append it to the list.
     if (index == -1) {
       index = closedTabs.length;
     }
 
+    // About to save the closed tab, add a unique ID.
+    tabData.closedId = this._nextClosedId++;
+
     // Insert tabData at the right position.
     closedTabs.splice(index, 0, tabData);
 
     // Truncate the list of closed tabs, if needed.
     if (closedTabs.length > this._max_tabs_undo) {
       closedTabs.splice(this._max_tabs_undo, closedTabs.length);
     }
   },
@@ -2393,16 +2437,52 @@ var SessionStoreInternal = {
   },
 
   persistTabAttribute: function ssi_persistTabAttribute(aName) {
     if (TabAttributes.persist(aName)) {
       this.saveStateDelayed();
     }
   },
 
+
+  /**
+   * Undoes the closing of a tab or window which corresponds
+   * to the closedId passed in.
+   *
+   * @param aClosedId
+   *        The closedId of the tab or window
+   *
+   * @returns a tab or window object
+   */
+  undoCloseById(aClosedId) {
+    // Check for a window first.
+    for (let i = 0, l = this._closedWindows.length; i < l; i++) {
+      if (this._closedWindows[i].closedId == aClosedId) {
+        return this.undoCloseWindow(i);
+      }
+    }
+
+    // Check for a tab.
+    let windowsEnum = Services.wm.getEnumerator("navigator:browser");
+    while (windowsEnum.hasMoreElements()) {
+      let window = windowsEnum.getNext();
+      let windowState = this._windows[window.__SSi];
+      if (windowState) {
+        for (let j = 0, l = windowState._closedTabs.length; j < l; j++) {
+          if (windowState._closedTabs[j].closedId == aClosedId) {
+            return this.undoCloseTab(window, j);
+          }
+        }
+      }
+    }
+
+    // Neither a tab nor a window was found, return undefined and let the caller decide what to do about it.
+    return undefined;
+  },
+
   /**
    * Restores the session state stored in LastSession. This will attempt
    * to merge data into the current session. If a window was opened at startup
    * with pinned tab(s), then the remaining data from the previous session for
    * that window will be opened into that window. Otherwise new windows will
    * be opened.
    */
   restoreLastSession: function ssi_restoreLastSession() {
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -228,9 +228,13 @@ run-if = e10s
 [browser_parentProcessRestoreHash.js]
 run-if = e10s
 [browser_sessionStoreContainer.js]
 [browser_windowStateContainer.js]
 [browser_1234021.js]
 [browser_remoteness_flip_on_restore.js]
 run-if = e10s
 [browser_background_tab_crash.js]
-run-if = e10s && crashreporter
\ No newline at end of file
+run-if = e10s && crashreporter
+
+# Disabled on Linux debug for frequent intermittent failures:
+[browser_undoCloseById.js]
+skip-if = os == "linux" && debug
--- a/browser/components/sessionstore/test/browser_switch_remoteness.js
+++ b/browser/components/sessionstore/test/browser_switch_remoteness.js
@@ -8,18 +8,21 @@ function countHistoryEntries(browser, ex
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     let history = webNavigation.sessionHistory.QueryInterface(Ci.nsISHistoryInternal);
     Assert.equal(history && history.count, args.expected,
       "correct number of shistory entries");
   });
 }
 
 add_task(function* () {
+  // Open a new window.
+  let win = yield promiseNewWindowLoaded();
+
   // Add a new tab.
-  let tab = gBrowser.addTab("about:blank");
+  let tab = win.gBrowser.addTab("about:blank");
   let browser = tab.linkedBrowser;
   yield promiseBrowserLoaded(browser);
   ok(browser.isRemoteBrowser, "browser is remote");
 
   // Get the maximum number of preceding entries to save.
   const MAX_BACK = Services.prefs.getIntPref("browser.sessionstore.max_serialize_back");
   ok(MAX_BACK > -1, "check that the default has a value that caps data");
 
@@ -37,10 +40,10 @@ add_task(function* () {
   browser.loadURI("about:robots");
   yield promiseTabRestored(tab);
   ok(!browser.isRemoteBrowser, "browser is not remote anymore");
 
   // Check that we didn't lose any shistory entries.
   yield countHistoryEntries(browser, MAX_BACK + 3);
 
   // Cleanup.
-  gBrowser.removeTab(tab);
+  yield BrowserTestUtils.closeWindow(win);
 });
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_undoCloseById.js
@@ -0,0 +1,118 @@
+"use strict";
+
+/**
+ * This test is for the undoCloseById function.
+ */
+
+Cu.import("resource:///modules/sessionstore/SessionStore.jsm");
+
+function openAndCloseTab(window, url) {
+  let tab = window.gBrowser.addTab(url);
+  yield promiseBrowserLoaded(tab.linkedBrowser, true, url);
+  yield TabStateFlusher.flush(tab.linkedBrowser);
+  yield promiseRemoveTab(tab);
+}
+
+function* openWindow(url) {
+  let win = yield promiseNewWindowLoaded();
+  let flags = Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
+  win.gBrowser.selectedBrowser.loadURIWithFlags(url, flags);
+  yield promiseBrowserLoaded(win.gBrowser.selectedBrowser, true, url);
+  return win;
+}
+
+function closeWindow(win) {
+  yield BrowserTestUtils.closeWindow(win);
+  // Wait 20 ms to allow SessionStorage a chance to register the closed window.
+  yield new Promise(resolve => setTimeout(resolve, 20));
+}
+
+add_task(function* test_undoCloseById() {
+  // Clear the lists of closed windows and tabs.
+  forgetClosedWindows();
+  while (SessionStore.getClosedTabCount(window)) {
+    SessionStore.forgetClosedTab(window, 0);
+  }
+
+  // Open a new window.
+  let win = yield openWindow("about:robots");
+
+  // Open and close a tab.
+  yield openAndCloseTab(win, "about:mozilla");
+  is(SessionStore.lastClosedObjectType, "tab", "The last closed object is a tab");
+
+  // Record the first closedId created.
+  let initialClosedId = SessionStore.getClosedTabData(win, false)[0].closedId;
+
+  // Open and close another window.
+  let win2 = yield openWindow("about:mozilla");
+  yield closeWindow(win2);  // closedId == initialClosedId + 1
+  is(SessionStore.lastClosedObjectType, "window", "The last closed object is a window");
+
+  // Open and close another tab in the first window.
+  yield openAndCloseTab(win, "about:robots");  // closedId == initialClosedId + 2
+  is(SessionStore.lastClosedObjectType, "tab", "The last closed object is a tab");
+
+  // Undo closing the second tab.
+  let tab = SessionStore.undoCloseById(initialClosedId + 2);
+  yield promiseBrowserLoaded(tab.linkedBrowser);
+  is(tab.linkedBrowser.currentURI.spec, "about:robots", "The expected tab was re-opened");
+
+  let notTab = SessionStore.undoCloseById(initialClosedId + 2);
+  is(notTab, undefined, "Re-opened tab cannot be unClosed again by closedId");
+
+  // Now the last closed object should be a window again.
+  is(SessionStore.lastClosedObjectType, "window", "The last closed object is a window");
+
+  // Undo closing the first tab.
+  let tab2 = SessionStore.undoCloseById(initialClosedId);
+  yield promiseBrowserLoaded(tab2.linkedBrowser);
+  is(tab2.linkedBrowser.currentURI.spec, "about:mozilla", "The expected tab was re-opened");
+
+  // Close the two tabs we re-opened.
+  yield promiseRemoveTab(tab); // closedId == initialClosedId + 3
+  is(SessionStore.lastClosedObjectType, "tab", "The last closed object is a tab");
+  yield promiseRemoveTab(tab2); // closedId == initialClosedId + 4
+  is(SessionStore.lastClosedObjectType, "tab", "The last closed object is a tab");
+
+  // Open another new window.
+  let win3 = yield openWindow("about:mozilla");
+
+  // Close both windows.
+  yield closeWindow(win); // closedId == initialClosedId + 5
+  is(SessionStore.lastClosedObjectType, "window", "The last closed object is a window");
+  yield closeWindow(win3); // closedId == initialClosedId + 6
+  is(SessionStore.lastClosedObjectType, "window", "The last closed object is a window");
+
+  // Undo closing the second window.
+  win = SessionStore.undoCloseById(initialClosedId + 6);
+  yield BrowserTestUtils.waitForEvent(win, "load");
+
+  // Make sure we wait until this window is restored.
+  yield BrowserTestUtils.waitForEvent(win.gBrowser.tabContainer,
+                                      "SSTabRestored");
+
+  is(win.gBrowser.selectedBrowser.currentURI.spec, "about:mozilla", "The expected window was re-opened");
+
+  let notWin = SessionStore.undoCloseById(initialClosedId + 6);
+  is(notWin, undefined, "Re-opened window cannot be unClosed again by closedId");
+
+  // Close the window again.
+  yield closeWindow(win);
+  is(SessionStore.lastClosedObjectType, "window", "The last closed object is a window");
+
+  // Undo closing the first window.
+  win = SessionStore.undoCloseById(initialClosedId + 5);
+
+  yield BrowserTestUtils.waitForEvent(win, "load");
+
+  // Make sure we wait until this window is restored.
+  yield BrowserTestUtils.waitForEvent(win.gBrowser.tabContainer,
+                                      "SSTabRestored");
+
+  is(win.gBrowser.selectedBrowser.currentURI.spec, "about:robots", "The expected window was re-opened");
+
+  // Close the window again.
+  yield closeWindow(win);
+  is(SessionStore.lastClosedObjectType, "window", "The last closed object is a window");
+});
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -278,18 +278,18 @@ var promiseForEachSessionRestoreFile = T
       if (!(ex instanceof OS.File.Error && ex.becauseNoSuchFile)) {
         throw ex;
       }
     }
     cb(data, key);
   }
 });
 
-function promiseBrowserLoaded(aBrowser, ignoreSubFrames = true) {
-  return BrowserTestUtils.browserLoaded(aBrowser, !ignoreSubFrames);
+function promiseBrowserLoaded(aBrowser, ignoreSubFrames = true, wantLoad = null) {
+  return BrowserTestUtils.browserLoaded(aBrowser, !ignoreSubFrames, wantLoad);
 }
 
 function whenWindowLoaded(aWindow, aCallback = next) {
   aWindow.addEventListener("load", function windowLoadListener() {
     aWindow.removeEventListener("load", windowLoadListener, false);
     executeSoon(function executeWhenWindowLoaded() {
       aCallback(aWindow);
     });
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -5,16 +5,17 @@
 STANDALONE_MAKEFILE := 1
 DIST_SUBDIR := browser
 
 include $(topsrcdir)/config/rules.mk
 
 MOZ_PKG_REMOVALS = $(srcdir)/removed-files.in
 
 MOZ_PKG_MANIFEST = $(srcdir)/package-manifest.in
+MOZ_PKG_DUPEFLAGS = -f $(srcdir)/allowed-dupes.mn
 
 # Some files have been already bundled with xulrunner
 ifndef MOZ_MULET
 MOZ_PKG_FATAL_WARNINGS = 1
 else
 DEFINES += -DMOZ_MULET
 endif
 
new file mode 100644
--- /dev/null
+++ b/browser/installer/allowed-dupes.mn
@@ -0,0 +1,222 @@
+# Known duplicate files
+# This file is ideally removed, but some existing files will be grandfathered in
+# See bug 1303184
+#
+# PLEASE DO NOT ADD MORE EXCEPTIONS TO THIS LIST
+#
+
+# updater on osx is bug 1311194
+LaunchServices/org.mozilla.updater
+updater.app/Contents/MacOS/org.mozilla.updater
+updater.app/Contents/PkgInfo
+browser/chrome.manifest
+# browser branding / themes is bug 1313106
+browser/chrome/browser/content/branding/icon128.png
+browser/chrome/browser/content/branding/icon16.png
+browser/chrome/browser/content/branding/icon32.png
+browser/chrome/browser/content/branding/icon48.png
+browser/chrome/browser/content/browser/defaultthemes/5.footer.png
+browser/chrome/browser/content/browser/defaultthemes/5.header.png
+browser/chrome/browser/content/browser/extension.svg
+browser/chrome/browser/content/browser/places/bookmarkProperties.xul
+browser/chrome/browser/content/browser/places/bookmarkProperties2.xul
+browser/chrome/browser/skin/classic/browser/addons/addon-install-confirm.svg
+browser/chrome/browser/skin/classic/browser/connection-secure.svg
+browser/chrome/browser/skin/classic/browser/controlcenter/warning-gray.svg
+browser/chrome/browser/skin/classic/browser/newtab/close.png
+browser/chrome/browser/skin/classic/browser/theme-switcher-icon.png
+# devtools reduction is bug 1311178
+browser/chrome/devtools/content/dom/content/dom-view.css
+browser/chrome/devtools/content/dom/dom.html
+browser/chrome/devtools/content/dom/main.js
+browser/chrome/devtools/content/framework/toolbox-options.js
+browser/chrome/devtools/content/inspector/fonts/fonts.js
+browser/chrome/devtools/content/inspector/inspector.xhtml
+browser/chrome/devtools/content/memory/initializer.js
+browser/chrome/devtools/content/projecteditor/lib/helpers/readdir.js
+browser/chrome/devtools/content/shared/frame-script-utils.js
+browser/chrome/devtools/content/shared/theme-switching.js
+browser/chrome/devtools/modules/devtools/client/dom/content/dom-view.css
+browser/chrome/devtools/modules/devtools/client/dom/dom.html
+browser/chrome/devtools/modules/devtools/client/dom/main.js
+browser/chrome/devtools/modules/devtools/client/framework/toolbox-options.js
+browser/chrome/devtools/modules/devtools/client/inspector/fonts/fonts.js
+browser/chrome/devtools/modules/devtools/client/inspector/inspector.xhtml
+browser/chrome/devtools/modules/devtools/client/jsonview/css/controls.png
+browser/chrome/devtools/modules/devtools/client/jsonview/css/controls@2x.png
+browser/chrome/devtools/modules/devtools/client/memory/initializer.js
+browser/chrome/devtools/modules/devtools/client/projecteditor/lib/helpers/readdir.js
+browser/chrome/devtools/modules/devtools/client/shared/frame-script-utils.js
+browser/chrome/devtools/modules/devtools/client/shared/theme-switching.js
+browser/chrome/devtools/modules/devtools/client/themes/common.css
+browser/chrome/devtools/modules/devtools/client/themes/variables.css
+browser/chrome/devtools/skin/common.css
+browser/chrome/devtools/skin/images/command-scratchpad.svg
+browser/chrome/devtools/skin/images/controls.png
+browser/chrome/devtools/skin/images/controls@2x.png
+browser/chrome/devtools/skin/images/debugger-blackbox.svg
+browser/chrome/devtools/skin/images/debugger-prettyprint.svg
+browser/chrome/devtools/skin/images/filetypes/store.svg
+browser/chrome/devtools/skin/images/itemToggle.svg
+browser/chrome/devtools/skin/images/security-state-broken.svg
+browser/chrome/devtools/skin/images/security-state-local.svg
+browser/chrome/devtools/skin/images/security-state-secure.svg
+browser/chrome/devtools/skin/images/tabs-icon.svg
+browser/chrome/devtools/skin/images/tool-scratchpad.svg
+browser/chrome/devtools/skin/images/tool-storage.svg
+browser/chrome/devtools/skin/images/tool-styleeditor.svg
+browser/chrome/devtools/skin/promisedebugger.css
+browser/chrome/devtools/skin/variables.css
+modules/devtools/Console.jsm
+modules/devtools/Loader.jsm
+modules/devtools/Simulator.jsm
+modules/devtools/shared/Console.jsm
+modules/devtools/shared/Loader.jsm
+modules/devtools/shared/apps/Simulator.jsm
+browser/modules/devtools/client/framework/gDevTools.jsm
+browser/modules/devtools/gDevTools.jsm
+browser/chrome/icons/default/default16.png
+browser/chrome/icons/default/default32.png
+browser/chrome/icons/default/default48.png
+browser/chrome/pdfjs/content/web/images/findbarButton-next-rtl.png
+browser/chrome/pdfjs/content/web/images/findbarButton-next-rtl@2x.png
+browser/chrome/pdfjs/content/web/images/findbarButton-next.png
+browser/chrome/pdfjs/content/web/images/findbarButton-next@2x.png
+browser/chrome/pdfjs/content/web/images/findbarButton-previous-rtl.png
+browser/chrome/pdfjs/content/web/images/findbarButton-previous-rtl@2x.png
+browser/chrome/pdfjs/content/web/images/findbarButton-previous.png
+browser/chrome/pdfjs/content/web/images/findbarButton-previous@2x.png
+browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/icon.png
+browser/features/firefox@getpocket.com/chrome/skin/linux/menuPanel.png
+browser/features/firefox@getpocket.com/chrome/skin/linux/menuPanel@2x.png
+browser/features/firefox@getpocket.com/chrome/skin/windows/menuPanel.png
+browser/features/firefox@getpocket.com/chrome/skin/windows/menuPanel@2x.png
+# flyweb reduction is bug 1313107
+browser/features/flyweb@mozilla.org/chrome/skin/linux/flyweb.css
+browser/features/flyweb@mozilla.org/chrome/skin/linux/icon-16.png
+browser/features/flyweb@mozilla.org/chrome/skin/linux/icon-32-anchored.png
+browser/features/flyweb@mozilla.org/chrome/skin/linux/icon-32.png
+browser/features/flyweb@mozilla.org/chrome/skin/linux/icon-64-anchored.png
+browser/features/flyweb@mozilla.org/chrome/skin/linux/icon-64.png
+browser/features/flyweb@mozilla.org/chrome/skin/osx/flyweb.css
+browser/features/flyweb@mozilla.org/chrome/skin/osx/icon-32-anchored.png
+browser/features/flyweb@mozilla.org/chrome/skin/osx/icon-64-anchored.png
+browser/features/flyweb@mozilla.org/chrome/skin/windows/flyweb.css
+browser/features/flyweb@mozilla.org/chrome/skin/windows/icon-16.png
+browser/features/flyweb@mozilla.org/chrome/skin/windows/icon-32-anchored.png
+browser/features/flyweb@mozilla.org/chrome/skin/windows/icon-32.png
+browser/features/flyweb@mozilla.org/chrome/skin/windows/icon-64-anchored.png
+browser/features/flyweb@mozilla.org/chrome/skin/windows/icon-64.png
+browser/icons/mozicon128.png
+chrome.manifest
+chrome/en-US/locale/en-US/browser/overrides/AccessFu.properties
+chrome/en-US/locale/en-US/browser/overrides/about.dtd
+chrome/en-US/locale/en-US/browser/overrides/aboutAbout.dtd
+chrome/en-US/locale/en-US/browser/overrides/aboutReader.properties
+chrome/en-US/locale/en-US/browser/overrides/aboutRights.dtd
+chrome/en-US/locale/en-US/browser/overrides/charsetMenu.properties
+chrome/en-US/locale/en-US/browser/overrides/commonDialogs.properties
+chrome/en-US/locale/en-US/browser/overrides/crashreporter/crashes.dtd
+chrome/en-US/locale/en-US/browser/overrides/crashreporter/crashes.properties
+chrome/en-US/locale/en-US/browser/overrides/dom/dom.properties
+chrome/en-US/locale/en-US/browser/overrides/global.dtd
+chrome/en-US/locale/en-US/browser/overrides/global/aboutSupport.dtd
+chrome/en-US/locale/en-US/browser/overrides/global/aboutSupport.properties
+chrome/en-US/locale/en-US/browser/overrides/global/aboutTelemetry.dtd
+chrome/en-US/locale/en-US/browser/overrides/global/aboutTelemetry.properties
+chrome/en-US/locale/en-US/browser/overrides/global/aboutWebrtc.properties
+chrome/en-US/locale/en-US/browser/overrides/global/mozilla.dtd
+chrome/en-US/locale/en-US/browser/overrides/intl.css
+chrome/en-US/locale/en-US/browser/overrides/intl.properties
+chrome/en-US/locale/en-US/browser/overrides/passwordmgr.properties
+chrome/en-US/locale/en-US/browser/overrides/plugins.properties
+chrome/en-US/locale/en-US/browser/overrides/plugins/pluginproblem.dtd
+chrome/en-US/locale/en-US/browser/overrides/search/search.properties
+chrome/en-US/locale/en-US/global-platform/mac/intl.properties
+chrome/en-US/locale/en-US/global-platform/unix/accessible.properties
+chrome/en-US/locale/en-US/global-platform/unix/intl.properties
+chrome/en-US/locale/en-US/global-platform/unix/platformKeys.properties
+chrome/en-US/locale/en-US/global-platform/win/accessible.properties
+chrome/en-US/locale/en-US/global-platform/win/intl.properties
+chrome/en-US/locale/en-US/global-platform/win/platformKeys.properties
+chrome/en-US/locale/en-US/global/AccessFu.properties
+chrome/en-US/locale/en-US/global/about.dtd
+chrome/en-US/locale/en-US/global/aboutAbout.dtd
+chrome/en-US/locale/en-US/global/aboutReader.properties
+chrome/en-US/locale/en-US/global/aboutRights.dtd
+chrome/en-US/locale/en-US/global/aboutSupport.dtd
+chrome/en-US/locale/en-US/global/aboutSupport.properties
+chrome/en-US/locale/en-US/global/aboutTelemetry.dtd
+chrome/en-US/locale/en-US/global/aboutTelemetry.properties
+chrome/en-US/locale/en-US/global/aboutWebrtc.properties
+chrome/en-US/locale/en-US/global/charsetMenu.properties
+chrome/en-US/locale/en-US/global/commonDialogs.properties
+chrome/en-US/locale/en-US/global/crashes.dtd
+chrome/en-US/locale/en-US/global/crashes.properties
+chrome/en-US/locale/en-US/global/dom/dom.properties
+chrome/en-US/locale/en-US/global/global.dtd
+chrome/en-US/locale/en-US/global/intl.css
+chrome/en-US/locale/en-US/global/intl.properties
+chrome/en-US/locale/en-US/global/mozilla.dtd
+chrome/en-US/locale/en-US/global/plugins.properties
+chrome/en-US/locale/en-US/global/search/search.properties
+chrome/en-US/locale/en-US/passwordmgr/passwordmgr.properties
+chrome/en-US/locale/en-US/pluginproblem/pluginproblem.dtd
+chrome/toolkit/skin/classic/global/autocomplete.css
+chrome/toolkit/skin/classic/global/button.css
+chrome/toolkit/skin/classic/global/checkbox.css
+chrome/toolkit/skin/classic/global/dialog.css
+chrome/toolkit/skin/classic/global/dropmarker.css
+chrome/toolkit/skin/classic/global/global.css
+chrome/toolkit/skin/classic/global/groupbox.css
+chrome/toolkit/skin/classic/global/icons/close-XPVista7.png
+chrome/toolkit/skin/classic/global/icons/tabprompts-bgtexture.png
+chrome/toolkit/skin/classic/global/listbox.css
+chrome/toolkit/skin/classic/global/media/clicktoplay-bgtexture.png
+chrome/toolkit/skin/classic/global/menu.css
+chrome/toolkit/skin/classic/global/menulist.css
+chrome/toolkit/skin/classic/global/numberbox.css
+chrome/toolkit/skin/classic/global/popup.css
+chrome/toolkit/skin/classic/global/preferences.css
+chrome/toolkit/skin/classic/global/progressmeter.css
+chrome/toolkit/skin/classic/global/radio.css
+chrome/toolkit/skin/classic/global/resizer.css
+chrome/toolkit/skin/classic/global/richlistbox.css
+chrome/toolkit/skin/classic/global/scale.css
+chrome/toolkit/skin/classic/global/scrollbars.css
+chrome/toolkit/skin/classic/global/scrollbox.css
+chrome/toolkit/skin/classic/global/spinbuttons.css
+chrome/toolkit/skin/classic/global/splitter.css
+chrome/toolkit/skin/classic/global/tabbox.css
+chrome/toolkit/skin/classic/global/textbox.css
+chrome/toolkit/skin/classic/global/toolbar.css
+chrome/toolkit/skin/classic/global/toolbarbutton.css
+chrome/toolkit/skin/classic/global/tree.css
+chrome/toolkit/skin/classic/global/wizard.css
+chrome/toolkit/skin/classic/mozapps/downloads/buttons.png
+chrome/toolkit/skin/classic/mozapps/downloads/downloadButtons-XP.png
+chrome/toolkit/skin/classic/mozapps/downloads/downloadButtons.png
+chrome/toolkit/skin/classic/mozapps/extensions/category-dictionaries.png
+chrome/toolkit/skin/classic/mozapps/extensions/category-experiments.png
+chrome/toolkit/skin/classic/mozapps/extensions/dictionaryGeneric.png
+chrome/toolkit/skin/classic/mozapps/extensions/experimentGeneric.png
+chrome/toolkit/skin/classic/mozapps/update/buttons.png
+chrome/toolkit/skin/classic/mozapps/update/downloadButtons-XP.png
+chrome/toolkit/skin/classic/mozapps/update/downloadButtons.png
+components/FxAccountsPush.js
+crashreporter.app/Contents/Resources/English.lproj/MainMenu.nib/classes.nib
+crashreporter.app/Contents/Resources/English.lproj/MainMenuRTL.nib/classes.nib
+# firefox/firefox-bin is bug 658850
+firefox
+firefox-bin
+modules/FxAccountsPush.js
+modules/commonjs/index.js
+modules/commonjs/sdk/ui/button/view/events.js
+modules/commonjs/sdk/ui/state/events.js
+plugin-container.app/Contents/PkgInfo
+res/table-remove-column-active.gif
+res/table-remove-column-hover.gif
+res/table-remove-column.gif
+res/table-remove-row-active.gif
+res/table-remove-row-hover.gif
+res/table-remove-row.gif
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -179,19 +179,16 @@
 @RESPATH@/components/content_xslt.xpt
 @RESPATH@/components/cookie.xpt
 @RESPATH@/components/directory.xpt
 @RESPATH@/components/docshell.xpt
 @RESPATH@/components/dom.xpt
 @RESPATH@/components/dom_apps.xpt
 @RESPATH@/components/dom_base.xpt
 @RESPATH@/components/dom_system.xpt
-#ifdef MOZ_B2G_BT
-@RESPATH@/components/dom_bluetooth.xpt
-#endif
 @RESPATH@/components/dom_canvas.xpt
 @RESPATH@/components/dom_core.xpt
 @RESPATH@/components/dom_css.xpt
 @RESPATH@/components/dom_events.xpt
 @RESPATH@/components/dom_geolocation.xpt
 @RESPATH@/components/dom_media.xpt
 @RESPATH@/components/dom_network.xpt
 @RESPATH@/components/dom_notification.xpt
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -316,16 +316,19 @@ this.PermissionPromptPrototype = {
                 promptAction.action == Ci.nsIPermissionManager.ALLOW_ACTION) {
               this.allow();
             } else {
               this.cancel();
             }
           }
         },
       };
+      if (promptAction.dismiss) {
+        action.dismiss = promptAction.dismiss
+      }
 
       popupNotificationActions.push(action);
     }
 
     let mainAction = popupNotificationActions.length ?
                      popupNotificationActions[0] : null;
     let secondaryActions = popupNotificationActions.splice(1);
 
--- a/browser/modules/URLBarZoom.jsm
+++ b/browser/modules/URLBarZoom.jsm
@@ -6,46 +6,57 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = [ "URLBarZoom" ];
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 var URLBarZoom = {
 
-  init: function(aWindow) {
+  init(aWindow) {
     // Register ourselves with the service so we know when the zoom prefs change.
-    Services.obs.addObserver(updateZoomButton, "browser-fullZoom:zoomChange", false);
-    Services.obs.addObserver(updateZoomButton, "browser-fullZoom:zoomReset", false);
-    Services.obs.addObserver(updateZoomButton, "browser-fullZoom:location-change", false);
+    Services.obs.addObserver(this, "browser-fullZoom:zoomChange", false);
+    Services.obs.addObserver(this, "browser-fullZoom:zoomReset", false);
+    Services.obs.addObserver(this, "browser-fullZoom:location-change", false);
+  },
+
+  observe(aSubject, aTopic) {
+    this.updateZoomButton(aSubject, aTopic);
   },
-}
 
-function updateZoomButton(aSubject, aTopic) {
-  let win = aSubject.ownerDocument.defaultView;
-  let customizableZoomControls = win.document.getElementById("zoom-controls");
-  let zoomResetButton = win.document.getElementById("urlbar-zoom-button");
-  let zoomFactor = Math.round(win.ZoomManager.zoom * 100);
+  updateZoomButton(aSubject, aTopic) {
+    // aSubject.ownerGlobal may no longer exist if a tab has been dragged to a
+    // new window. In this case, aSubject.ownerGlobal will be supplied by
+    // updateZoomButton() called in XULBrowserWindow.onLocationChange().
+    if (!aSubject.ownerGlobal) {
+      return;
+    }
+
+    let win = aSubject.ownerGlobal;
+    let customizableZoomControls = win.document.getElementById("zoom-controls");
+    let zoomResetButton = win.document.getElementById("urlbar-zoom-button");
+    let zoomFactor = Math.round(win.ZoomManager.zoom * 100);
 
-  // Ensure that zoom controls haven't already been added to browser in Customize Mode
-  if (customizableZoomControls &&
-      customizableZoomControls.getAttribute("cui-areatype") == "toolbar") {
-    zoomResetButton.hidden = true;
-    return;
-  }
-  if (zoomFactor != 100) {
-    // Check if zoom button is visible and update label if it is
-    if (zoomResetButton.hidden) {
-      zoomResetButton.hidden = false;
+    // Ensure that zoom controls haven't already been added to browser in Customize Mode
+    if (customizableZoomControls &&
+        customizableZoomControls.getAttribute("cui-areatype") == "toolbar") {
+      zoomResetButton.hidden = true;
+      return;
     }
-    // Only allow pulse animation for zoom changes, not tab switching
-    if (aTopic != "browser-fullZoom:location-change") {
-      zoomResetButton.setAttribute("animate", "true");
+    if (zoomFactor != 100) {
+      // Check if zoom button is visible and update label if it is
+      if (zoomResetButton.hidden) {
+        zoomResetButton.hidden = false;
+      }
+      // Only allow pulse animation for zoom changes, not tab switching
+      if (aTopic != "browser-fullZoom:location-change") {
+        zoomResetButton.setAttribute("animate", "true");
+      } else {
+        zoomResetButton.removeAttribute("animate");
+      }
+      zoomResetButton.setAttribute("label",
+        win.gNavigatorBundle.getFormattedString("urlbar-zoom-button.label", [zoomFactor]));
     } else {
-      zoomResetButton.removeAttribute("animate");
+      // Hide button if zoom is at 100%
+      zoomResetButton.hidden = true;
     }
-    zoomResetButton.setAttribute("label",
-        win.gNavigatorBundle.getFormattedString("urlbar-zoom-button.label", [zoomFactor]));
-  // Hide button if zoom is at 100%
-  } else {
-      zoomResetButton.hidden = true;
-  }
-}
+  },
+};
--- a/browser/modules/test/browser_CaptivePortalWatcher.js
+++ b/browser/modules/test/browser_CaptivePortalWatcher.js
@@ -91,32 +91,56 @@ function ensureNoPortalTab(win) {
 
 function ensureNoPortalNotification(win) {
   let notificationBox =
     win.document.getElementById("high-priority-global-notificationbox");
   is(notificationBox.getNotificationWithValue(PORTAL_NOTIFICATION_VALUE), null,
     "There should be no captive portal notification in the window.");
 }
 
+/**
+ * Some tests open a new window and close it later. When the window is closed,
+ * the original window opened by mochitest gains focus, generating a
+ * xul-window-visible notification. If the next test also opens a new window
+ * before this notification has a chance to fire, CaptivePortalWatcher picks
+ * up the first one instead of the one from the new window. To avoid this
+ * unfortunate intermittent timing issue, we wait for the notification from
+ * the original window every time we close a window that we opened.
+ */
+function waitForXulWindowVisible() {
+  return new Promise(resolve => {
+    Services.obs.addObserver(function observe() {
+      Services.obs.removeObserver(observe, "xul-window-visible");
+      resolve();
+    }, "xul-window-visible", false);
+  });
+}
+
+function* closeWindowAndWaitForXulWindowVisible(win) {
+  let p = waitForXulWindowVisible();
+  yield BrowserTestUtils.closeWindow(win);
+  yield p;
+}
+
 // Each of the test cases below is run twice: once for login-success and once
 // for login-abort (aSuccess set to true and false respectively).
 let testCasesForBothSuccessAndAbort = [
   /**
    * A portal is detected when there's no browser window,
    * then a browser window is opened, then the portal is freed.
    * The portal tab should be added and focused when the window is
    * opened, and closed automatically when the success event is fired.
    */
   function* test_detectedWithNoBrowserWindow_Open(aSuccess) {
     yield portalDetectedNoBrowserWindow();
     let win = yield openWindowAndWaitForPortalTabAndNotification();
     freePortal(aSuccess);
     ensureNoPortalTab(win);
     ensureNoPortalNotification(win);
-    yield BrowserTestUtils.closeWindow(win);
+    yield closeWindowAndWaitForXulWindowVisible(win);
   },
 
   /**
    * A portal is detected when there's no browser window, and the
    * portal is freed before a browser window is opened. No portal
    * tab should be added when a browser window is opened.
    */
   function* test_detectedWithNoBrowserWindow_GoneBeforeOpen(aSuccess) {
@@ -124,17 +148,17 @@ let testCasesForBothSuccessAndAbort = [
     freePortal(aSuccess);
     let win = yield BrowserTestUtils.openNewBrowserWindow();
     // Wait for a while to make sure no tab is opened.
     yield new Promise(resolve => {
       setTimeout(resolve, 1000);
     });
     ensureNoPortalTab(win);
     ensureNoPortalNotification(win);
-    yield BrowserTestUtils.closeWindow(win);
+    yield closeWindowAndWaitForXulWindowVisible(win);
   },
 
   /**
    * A portal is detected when a browser window has focus. A portal tab should be
    * opened in the background in the focused browser window. If the portal is
    * freed when the tab isn't focused, the tab should be closed automatically.
    */
   function* test_detectedWithFocus(aSuccess) {
@@ -186,17 +210,17 @@ let singleRunTestCases = [
     let browser = win.gBrowser.selectedTab.linkedBrowser;
     let loadPromise =
       BrowserTestUtils.browserLoaded(browser, false, CANONICAL_URL_REDIRECTED);
     BrowserTestUtils.loadURI(browser, CANONICAL_URL_REDIRECTED);
     yield loadPromise;
     freePortal(true);
     ensurePortalTab(win);
     ensureNoPortalNotification(win);
-    yield BrowserTestUtils.closeWindow(win);
+    yield closeWindowAndWaitForXulWindowVisible(win);
   },
 
   /**
    * A portal is detected when a browser window has focus. A portal tab should be
    * opened in the background in the focused browser window. If the portal is
    * freed when the tab isn't focused, the tab should be closed automatically,
    * even if the portal has redirected to a URL other than CANONICAL_URL.
    */
--- a/browser/themes/linux/searchbar.css
+++ b/browser/themes/linux/searchbar.css
@@ -118,26 +118,49 @@ menuitem[cmd="cmd_clearhistory"][disable
   }
 
   .search-go-button:hover:active {
     -moz-image-region: rect(56px, 84px, 84px, 56px);
   }
 }
 
 .search-panel-current-engine {
-  border-top: none !important;
   -moz-box-align: center;
 }
 
+/**
+ * The borders of the various elements are specified as follows.
+ *
+ * The current engine always has a bottom border.
+ * The search results never have a border.
+ *
+ * When the search results are not collapsed:
+ * - The elements underneath the search results all have a top border.
+ *
+ * When the search results are collapsed:
+ * - The elements underneath the search results all have a bottom border, except
+ *   the lowest one: search-setting-button.
+ */
+
 .search-panel-current-engine {
-  border-bottom: none;
+  border-top: none !important;
+  border-bottom: 1px solid var(--panel-separator-color) !important;
 }
 
-.search-panel-tree {
-  border-top: 1px solid var(--panel-separator-color) !important;
+.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-header,
+.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-one-offs,
+.search-panel-tree[collapsed=true] + .search-one-offs > vbox > .addengine-item:first-of-type {
+  border-top: none !important;
+}
+
+.search-panel-tree[collapsed=true] + .search-one-offs > .searchbar-engine-one-off-item,
+.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-current-input,
+.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-one-offs,
+.search-panel-tree[collapsed=true] + .search-one-offs > vbox > .addengine-item:last-of-type {
+  border-bottom: 1px solid var(--panel-separator-color) !important;
 }
 
 .search-panel-header {
   font-weight: normal;
   background-color: var(--arrowpanel-dimmed);
   border: none;
   border-top: 1px solid var(--panel-separator-color);
   padding: 3px 5px;
--- a/browser/themes/osx/searchbar.css
+++ b/browser/themes/osx/searchbar.css
@@ -107,26 +107,49 @@
   }
 
   .searchbar-search-button:hover:active {
     -moz-image-region: rect(0, 120px, 40px, 80px);
   }
 }
 
 .search-panel-current-engine {
-  border-top: none !important;
   border-radius: 4px 4px 0 0;
 }
 
+/**
+ * The borders of the various elements are specified as follows.
+ *
+ * The current engine always has a bottom border.
+ * The search results never have a border.
+ *
+ * When the search results are not collapsed:
+ * - The elements underneath the search results all have a top border.
+ *
+ * When the search results are collapsed:
+ * - The elements underneath the search results all have a bottom border, except
+ *   the lowest one: search-setting-button.
+ */
+
 .search-panel-current-engine {
-  border-bottom: none;
+  border-top: none !important;
+  border-bottom: 1px solid var(--panel-separator-color);
 }
 
-.search-panel-tree {
-  border-top: 1px solid var(--panel-separator-color) !important;
+.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-header,
+.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-one-offs,
+.search-panel-tree[collapsed=true] + .search-one-offs > vbox > .addengine-item:first-of-type {
+  border-top: none;
+}
+
+.search-panel-tree[collapsed=true] + .search-one-offs > .searchbar-engine-one-off-item,
+.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-current-input,
+.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-one-offs,
+.search-panel-tree[collapsed=true] + .search-one-offs > vbox > .addengine-item:last-of-type {
+  border-bottom: 1px solid var(--panel-separator-color);
 }
 
 .search-panel-header {
   font-size: 10px;
   font-weight: normal;
   background-color: var(--arrowpanel-dimmed);
   border-top: 1px solid var(--panel-separator-color);
   margin: 0;
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -213,16 +213,22 @@
   filter: url(chrome://browser/skin/filters.svg#fill) drop-shadow(1px 1px 1px black);
 }
 
 .tab-icon-sound[soundplaying]:not(:hover),
 .tab-icon-sound[muted]:not(:hover) {
   opacity: .8;
 }
 
+.tab-icon-sound[soundplaying-scheduledremoval]:not(:hover),
+.tab-icon-overlay[soundplaying-scheduledremoval]:not(:hover) {
+  transition: opacity .3s linear var(--soundplaying-removal-delay);
+  opacity: 0;
+}
+
 .tab-background,
 .tabs-newtab-button {
   /* overlap the tab curves */
   margin-inline-end: -@tabCurveHalfWidth@;
   margin-inline-start: -@tabCurveHalfWidth@;
 }
 
 .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox {
--- a/browser/themes/windows/searchbar.css
+++ b/browser/themes/windows/searchbar.css
@@ -113,26 +113,49 @@
   }
 
   .search-go-button:hover:active {
     -moz-image-region: rect(56px, 84px, 84px, 56px);
   }
 }
 
 .search-panel-current-engine {
-  border-top: none !important;
   -moz-box-align: center;
 }
 
+/**
+ * The borders of the various elements are specified as follows.
+ *
+ * The current engine always has a bottom border.
+ * The search results never have a border.
+ *
+ * When the search results are not collapsed:
+ * - The elements underneath the search results all have a top border.
+ *
+ * When the search results are collapsed:
+ * - The elements underneath the search results all have a bottom border, except
+ *   the lowest one: search-setting-button.
+ */
+
 .search-panel-current-engine {
-  border-bottom: none;
+  border-top: none !important;
+  border-bottom: 1px solid var(--panel-separator-color) !important;
 }
 
-.search-panel-tree {
-  border-top: 1px solid var(--panel-separator-color) !important;
+.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-header,
+.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-one-offs,
+.search-panel-tree[collapsed=true] + .search-one-offs > vbox > .addengine-item:first-of-type {
+  border-top: none !important;
+}
+
+.search-panel-tree[collapsed=true] + .search-one-offs > .searchbar-engine-one-off-item,
+.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-current-input,
+.search-panel-tree[collapsed=true] + .search-one-offs > .search-panel-one-offs,
+.search-panel-tree[collapsed=true] + .search-one-offs > vbox > .addengine-item:last-of-type {
+  border-bottom: 1px solid var(--panel-separator-color) !important;
 }
 
 .search-panel-header {
   font-weight: normal;
   background-color: var(--arrowpanel-dimmed);
   border: none;
   border-top: 1px solid var(--panel-separator-color);
   margin: 0;
--- a/build/moz-automation.mk
+++ b/build/moz-automation.mk
@@ -83,22 +83,16 @@ automation/upload: automation/sdk
 # binaries/libs, and that's what we package/test.
 automation/pretty-package: automation/buildsymbols
 
 # The installer, sdk and packager all run stage-package, and may conflict
 # with each other.
 automation/installer: automation/package
 automation/sdk: automation/installer automation/package
 
-# Universal builds need package staging happening before buildsymbols
-# (bug 834228)
-ifdef UNIVERSAL_BINARY
-automation/buildsymbols: automation/package
-endif
-
 # The 'pretty' versions of targets run before the regular ones to avoid
 # conflicts in writing to the same files.
 automation/installer: automation/pretty-installer
 automation/package: automation/pretty-package
 automation/package-tests: automation/pretty-package-tests
 automation/l10n-check: automation/pretty-l10n-check
 automation/update-packaging: automation/pretty-update-packaging
 
--- a/build/moz.configure/android-ndk.configure
+++ b/build/moz.configure/android-ndk.configure
@@ -9,17 +9,17 @@ js_option('--with-android-ndk', nargs=1,
           help='location where the Android NDK can be found')
 
 js_option('--with-android-toolchain', nargs=1,
           help='location of the Android toolchain')
 
 js_option('--with-android-gnu-compiler-version', nargs=1,
           help='GNU compiler version to use')
 
-min_android_version = dependable('9')
+min_android_version = dependable(lambda: '9')
 
 js_option('--with-android-version',
           nargs=1,
           help='android platform version',
           default=min_android_version)
 
 @depends('--with-android-version', min_android_version)
 @imports(_from='__builtin__', _import='ValueError')
@@ -49,16 +49,17 @@ def ndk(value, build_project):
     if value:
         return value[0]
 
 set_config('ANDROID_NDK', ndk)
 add_old_configure_assignment('android_ndk', ndk)
 
 @depends(target, android_version, ndk)
 @checking('for android platform directory')
+@imports('os')
 def android_platform(target, android_version, ndk):
     if target.os != 'Android':
         return
 
     if 'mips' in target.cpu:
         target_dir_name = 'mips'
     elif 'aarch64' == target.cpu:
         target_dir_name = 'arm64'
@@ -92,16 +93,17 @@ def extra_toolchain_flags(platform_dir):
     if not platform_dir:
         return []
     return ['-idirafter',
             os.path.join(platform_dir, 'usr', 'include')]
 
 @depends(target, host, ndk, '--with-android-toolchain',
          '--with-android-gnu-compiler-version')
 @checking('for the Android toolchain directory', lambda x: x or 'not found')
+@imports('os')
 @imports(_from='mozbuild.shellutil', _import='quote')
 def android_toolchain(target, host, ndk, toolchain, gnu_compiler_version):
     if not ndk:
         return
     if toolchain:
         return toolchain[0]
     else:
         if target.cpu == 'arm' and target.endianness == 'little':
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -7,16 +7,17 @@
 include('util.configure')
 include('checks.configure')
 
 option(env='DIST', nargs=1, help='DIST directory')
 
 # Do not allow objdir == srcdir builds.
 # ==============================================================
 @depends('--help', 'DIST')
+@imports('os')
 def check_build_environment(help, dist):
     topobjdir = os.path.realpath(os.path.abspath('.'))
     topsrcdir = os.path.realpath(os.path.abspath(
         os.path.join(os.path.dirname(__file__), '..', '..')))
 
     if dist:
         dist = normsep(dist[0])
     else:
@@ -628,16 +629,17 @@ def default_project(build_env, help):
 option('--enable-project', nargs=1, default=default_project,
        help='Project to build')
 
 option('--with-external-source-dir', env='EXTERNAL_SOURCE_DIR', nargs=1,
        help='External directory containing additional build files')
 
 @depends('--enable-project', '--with-external-source-dir',
          check_build_environment, '--help')
+@imports('os')
 def include_project_configure(project, external_source_dir, build_env, help):
     if not project:
         die('--enable-project is required.')
 
     base_dir = build_env.topsrcdir
     if external_source_dir:
         base_dir = os.path.join(base_dir, external_source_dir[0])
 
@@ -806,11 +808,11 @@ def js_option(*args, **kwargs):
     add_old_configure_arg(js_option)
 
 
 # Bug 1278542: This function is a workaround to resolve
 # |android_ndk_include|'s dependency on 'gonkdir.' The
 # actual implementation is located in b2g/moz.configure.
 # Remove this function as soon as 'android_ndk_include'
 # depends on 'target.'
-@dependable
-def gonkdir():
+@depends('--help')
+def gonkdir(_):
     return None
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -10,16 +10,17 @@ def encoded_open(path, mode):
     encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8'
     return codecs.open(path, mode, encoding)
 
 
 option(env='AUTOCONF', nargs=1, help='Path to autoconf 2.13')
 
 @depends(mozconfig, 'AUTOCONF')
 @checking('for autoconf')
+@imports('os')
 @imports('re')
 def autoconf(mozconfig, autoconf):
     mozconfig_autoconf = None
     if mozconfig['path']:
         make_extra = mozconfig['make_extra']
         if make_extra:
             for assignment in make_extra:
                 m = re.match('(?:export\s+)?AUTOCONF\s*:?=\s*(.+)$',
@@ -59,30 +60,31 @@ set_config('AUTOCONF', autoconf)
 
 
 @depends('OLD_CONFIGURE', mozconfig, autoconf, check_build_environment, shell,
          old_configure_assignments, build_project)
 @imports(_from='__builtin__', _import='open')
 @imports(_from='__builtin__', _import='print')
 @imports('glob')
 @imports('itertools')
+@imports('os')
 @imports('subprocess')
 # Import getmtime without overwriting the sandbox os.path.
 @imports(_from='os.path', _import='getmtime')
 @imports(_from='mozbuild.shellutil', _import='quote')
 def prepare_configure(old_configure, mozconfig, autoconf, build_env, shell,
                       old_configure_assignments, build_project):
     # os.path.abspath in the sandbox will ensure forward slashes on Windows,
     # which is actually necessary because this path actually ends up literally
     # as $0, and backslashes there breaks autoconf's detection of the source
     # directory.
     old_configure = os.path.abspath(old_configure[0])
     if build_project == 'js':
         old_configure_dir = os.path.dirname(old_configure)
-        if not old_configure_dir.endswith('/js/src'):
+        if not old_configure_dir.replace(os.sep, '/').endswith('/js/src'):
             old_configure = os.path.join(old_configure_dir, 'js', 'src',
                                          os.path.basename(old_configure))
 
     refresh = True
     if os.path.exists(old_configure):
         mtime = getmtime(old_configure)
         aclocal = os.path.join(build_env.topsrcdir, 'build', 'autoconf',
                                '*.m4')
@@ -402,20 +404,20 @@ def post_old_configure(raw_config):
 
 
 # Assuming no other option is declared after this function, handle the
 # env options that were injected by mozconfig_options by creating dummy
 # Option instances and having the sandbox's CommandLineHelper handle
 # them. We only do so for options that haven't been declared so far,
 # which should be a proxy for the options that old-configure handles
 # and that we don't know anything about.
-@dependable
+@depends('--help')
 @imports('__sandbox__')
 @imports(_from='mozbuild.configure.options', _import='Option')
-def remaining_mozconfig_options():
+def remaining_mozconfig_options(_):
     helper = __sandbox__._helper
     for arg in helper:
         if helper._origins[arg] != 'mozconfig':
             continue
         name = arg.split('=', 1)[0]
         if name.isupper() and name not in __sandbox__._options:
             option = Option(env=name, nargs='*', help=name)
             helper.handle(option)
--- a/build/moz.configure/util.configure
+++ b/build/moz.configure/util.configure
@@ -113,16 +113,19 @@ def normalize_path():
             path = normsep(path)
             if quote(path) == path:
                 return path
             size = 0
             while True:
                 out = ctypes.create_unicode_buffer(size)
                 needed = GetShortPathNameW(path, out, size)
                 if size >= needed:
+                    if ' ' in out.value:
+                        die("GetShortPathName returned a long path name. "
+                            "Are 8dot3 filenames disabled?")
                     return normsep(out.value)
                 size = needed
 
     else:
         def normalize_path(path):
             return normsep(path)
 
     return normalize_path
@@ -333,51 +336,43 @@ def namespace(**kwargs):
 # or, for convenience, a @depends function.
 @template
 @imports(_from='inspect', _import='isfunction')
 @imports(_from='mozbuild.configure', _import='SandboxDependsFunction')
 def dependable(obj):
     if isinstance(obj, SandboxDependsFunction):
         return obj
     if isfunction(obj):
-        return depends('--help')(lambda _: obj())
-    return depends('--help')(lambda _: obj)
+        return depends(when=True)(obj)
+    return depends(when=True)(lambda: obj)
 
 
 always = dependable(True)
 never = dependable(False)
 
 
 # Some @depends function return namespaces, and one could want to use one
 # specific attribute from such a namespace as a "value" given to functions
 # such as `set_config`. But those functions do not take immediate values.
 # The `delayed_getattr` function allows access to attributes from the result
 # of a @depends function in a non-immediate manner.
 #   @depends('--option')
 #   def option(value)
 #       return namespace(foo=value)
 #   set_config('FOO', delayed_getattr(option, 'foo')
 @template
-@imports('__sandbox__')
 def delayed_getattr(func, key):
-    deps = __sandbox__._depends.get(func, ())
-    if deps:
-        deps = deps.sandboxed_dependencies
-
-    def result(value, _=None):
+    @depends(func)
+    def result(value):
         # The @depends function we're being passed may have returned
         # None, or an object that simply doesn't have the wanted key.
         # In that case, just return None.
         return getattr(value, key, None)
 
-    # Automatically add a dependency on --help when the given @depends
-    # function itself depends on --help.
-    if __sandbox__._help_option in deps:
-        return depends(func, '--help')(result)
-    return depends(func)(result)
+    return result
 
 
 # Like @depends, but the decorated function is only called if one of the
 # arguments it would be called with has a positive value (bool(value) is True)
 @template
 def depends_if(*args):
     def decorator(func):
         @depends(*args)
--- a/build/moz.configure/windows.configure
+++ b/build/moz.configure/windows.configure
@@ -260,16 +260,17 @@ def vc_path(c_compiler):
         result = next
         if p.lower() == 'bin':
             break
     return result
 
 
 @depends_win(vc_path)
 @checking('for the Debug Interface Access SDK', lambda x: x or 'not found')
+@imports('os')
 def dia_sdk_dir(vc_path):
     if vc_path:
         path = os.path.join(os.path.dirname(vc_path), 'DIA SDK')
         if os.path.isdir(path):
             return path
 
 
 @depends_win(vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir)
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -75,27 +75,26 @@ included_inclnames_to_ignore = set([
     'private/pprio.h',          # NSPR
     'prlink.h',                 # NSPR
     'prlock.h',                 # NSPR
     'prprf.h',                  # NSPR
     'prthread.h',               # NSPR
     'prtypes.h',                # NSPR
     'selfhosted.out.h',         # generated in $OBJDIR
     'shellmoduleloader.out.h',  # generated in $OBJDIR
-    'unicode/locid.h',          # ICU
-    'unicode/numsys.h',         # ICU
     'unicode/timezone.h',       # ICU
     'unicode/ucal.h',           # ICU
     'unicode/uclean.h',         # ICU
     'unicode/ucol.h',           # ICU
     'unicode/udat.h',           # ICU
     'unicode/udatpg.h',         # ICU
     'unicode/uenum.h',          # ICU
     'unicode/unorm.h',          # ICU
     'unicode/unum.h',           # ICU
+    'unicode/unumsys.h',        # ICU
     'unicode/ustring.h',        # ICU
     'unicode/utypes.h',         # ICU
     'vtune/VTuneWrapper.h'      # VTune
 ])
 
 # These files have additional constraints on where they are #included, so we
 # ignore #includes of them when checking #include ordering.
 oddly_ordered_inclnames = set([
--- a/devtools/client/framework/target.js
+++ b/devtools/client/framework/target.js
@@ -9,24 +9,26 @@ const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const EventEmitter = require("devtools/shared/event-emitter");
 const Services = require("Services");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "gDevTools",
+  "devtools/client/framework/devtools", true);
 
 const targets = new WeakMap();
 const promiseTargets = new WeakMap();
 
 /**
  * Functions for creating Targets
  */
-exports.TargetFactory = {
+const TargetFactory = exports.TargetFactory = {
   /**
    * Construct a Target
    * @param {XULTab} tab
    *        The tab to use in creating a new target.
    *
    * @return A target object
    */
   forTab: function (tab) {
@@ -449,29 +451,31 @@ TabTarget.prototype = {
    * Listen to the different events.
    */
   _setupListeners: function () {
     this._webProgressListener = new TabWebProgressListener(this);
     this.tab.linkedBrowser.addProgressListener(this._webProgressListener);
     this.tab.addEventListener("TabClose", this);
     this.tab.parentNode.addEventListener("TabSelect", this);
     this.tab.ownerDocument.defaultView.addEventListener("unload", this);
+    this.tab.addEventListener("TabRemotenessChange", this);
   },
 
   /**
    * Teardown event listeners.
    */
   _teardownListeners: function () {
     if (this._webProgressListener) {
       this._webProgressListener.destroy();
     }
 
     this._tab.ownerDocument.defaultView.removeEventListener("unload", this);
     this._tab.removeEventListener("TabClose", this);
     this._tab.parentNode.removeEventListener("TabSelect", this);
+    this._tab.removeEventListener("TabRemotenessChange", this);
   },
 
   /**
    * Setup listeners for remote debugging, updating existing ones as necessary.
    */
   _setupRemoteListeners: function () {
     this.client.addListener("closed", this.destroy);
 
@@ -543,19 +547,48 @@ TabTarget.prototype = {
         break;
       case "TabSelect":
         if (this.tab.selected) {
           this.emit("visible", event);
         } else {
           this.emit("hidden", event);
         }
         break;
+      case "TabRemotenessChange":
+        this.onRemotenessChange();
+        break;
     }
   },
 
+  // Automatically respawn the toolbox when the tab changes between being
+  // loaded within the parent process and loaded from a content process.
+  // Process change can go in both ways.
+  onRemotenessChange: function () {
+    // Responsive design do a crazy dance around tabs and triggers
+    // remotenesschange events. But we should ignore them as at the end
+    // the content doesn't change its remoteness.
+    if (this._tab.isReponsiveDesignMode) {
+      return;
+    }
+
+    // Save a reference to the tab as it will be nullified on destroy
+    let tab = this._tab;
+    let onToolboxDestroyed = (event, target) => {
+      if (target != this) {
+        return;
+      }
+      gDevTools.off("toolbox-destroyed", target);
+
+      // Recreate a fresh target instance as the current one is now destroyed
+      let newTarget = TargetFactory.forTab(tab);
+      gDevTools.showToolbox(newTarget);
+    };
+    gDevTools.on("toolbox-destroyed", onToolboxDestroyed);
+  },
+
   /**
    * Target is not alive anymore.
    */
   destroy: function () {
     // If several things call destroy then we give them all the same
     // destruction promise so we're sure to destroy only once
     if (this._destroyer) {
       return this._destroyer.promise;
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -56,16 +56,18 @@ skip-if = true # Bug 1177463 - Temporari
 [browser_toolbox_options_disable_buttons.js]
 [browser_toolbox_options_disable_cache-01.js]
 [browser_toolbox_options_disable_cache-02.js]
 [browser_toolbox_options_disable_js.js]
 [browser_toolbox_options_enable_serviceworkers_testing.js]
 # [browser_toolbox_raise.js] # Bug 962258
 # skip-if = os == "win"
 [browser_toolbox_ready.js]
+[browser_toolbox_remoteness_change.js]
+run-if = e10s
 [browser_toolbox_select_event.js]
 skip-if = e10s # Bug 1069044 - destroyInspector may hang during shutdown
 [browser_toolbox_selected_tool_unavailable.js]
 [browser_toolbox_sidebar.js]
 [browser_toolbox_sidebar_events.js]
 [browser_toolbox_sidebar_existing_tabs.js]
 [browser_toolbox_sidebar_overflow_menu.js]
 [browser_toolbox_split_console.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/browser_toolbox_remoteness_change.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var {Toolbox} = require("devtools/client/framework/toolbox");
+
+const URL_1 = "about:robots";
+const URL_2 = "data:text/html;charset=UTF-8," +
+  encodeURIComponent("<div id=\"remote-page\">foo</div>");
+
+add_task(function* () {
+  info("Open a tab on a URL supporting only running in parent process");
+  let tab = yield addTab(URL_1);
+  is(tab.linkedBrowser.currentURI.spec, URL_1, "We really are on the expected document");
+  is(tab.linkedBrowser.getAttribute("remote"), "", "And running in parent process");
+
+  let toolbox = yield openToolboxForTab(tab);
+
+  let onToolboxDestroyed = toolbox.once("destroyed");
+  let onToolboxCreated = gDevTools.once("toolbox-created");
+
+  info("Navigate to a URL supporting remote process");
+  let onLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  gBrowser.loadURI(URL_2);
+  yield onLoaded;
+
+  is(tab.linkedBrowser.getAttribute("remote"), "true", "Navigated to a data: URI and switching to remote");
+
+  info("Waiting for the toolbox to be destroyed");
+  yield onToolboxDestroyed;
+
+  info("Waiting for a new toolbox to be created");
+  toolbox = yield onToolboxCreated;
+
+  info("Waiting for the new toolbox to be ready");
+  yield toolbox.once("ready");
+
+  info("Veryify we are inspecting the new document");
+  let console = yield toolbox.selectTool("webconsole");
+  let { jsterm } = console.hud;
+  let url = yield jsterm.execute("document.location.href");
+  is(url.textContent, URL_2, "The console inspects the second document");
+});
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* globals template */
-
 "use strict";
 
 const promise = require("promise");
 const Services = require("Services");
 const defer = require("devtools/shared/defer");
 const {Task} = require("devtools/shared/task");
 const nodeConstants = require("devtools/shared/dom-node-constants");
 const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -1,14 +1,13 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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/. */
-/* globals gDevTools */
 
 "use strict";
 
 const promise = require("promise");
 const Services = require("Services");
 const {Task} = require("devtools/shared/task");
 const {Tools} = require("devtools/client/definitions");
 const {l10n} = require("devtools/shared/inspector/css-logic");
--- a/devtools/client/locales/en-US/netmonitor.properties
+++ b/devtools/client/locales/en-US/netmonitor.properties
@@ -487,20 +487,16 @@ netmonitor.toolbar.filterFreetext.key=Cm
 # LOCALIZATION NOTE (netmonitor.toolbar.clear): This is the label displayed
 # in the network toolbar for the "Clear" button.
 netmonitor.toolbar.clear=Clear
 
 # LOCALIZATION NOTE (netmonitor.toolbar.perf): This is the label displayed
 # in the network toolbar for the performance analysis button.
 netmonitor.toolbar.perf=Toggle performance analysis…
 
-# LOCALIZATION NOTE (netmonitor.panesButton.tooltip): This is the tooltip for
-# the button that toggles the panes visible or hidden in the netmonitor UI.
-netmonitor.panesButton.tooltip=Toggle network info
-
 # LOCALIZATION NOTE (netmonitor.summary.url): This is the label displayed
 # in the network details headers tab identifying the URL.
 netmonitor.summary.url=Request URL:
 
 # LOCALIZATION NOTE (netmonitor.summary.method): This is the label displayed
 # in the network details headers tab identifying the method.
 netmonitor.summary.method=Request method:
 
--- a/devtools/client/netmonitor/actions/index.js
+++ b/devtools/client/netmonitor/actions/index.js
@@ -1,8 +1,9 @@
 /* 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 filters = require("./filters");
+const sidebar = require("./sidebar");
 
-module.exports = Object.assign({}, filters);
+module.exports = Object.assign({}, filters, sidebar);
--- a/devtools/client/netmonitor/actions/moz.build
+++ b/devtools/client/netmonitor/actions/moz.build
@@ -1,9 +1,10 @@
 # vim: set filetype=python:
 # 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/.
 
 DevToolsModules(
     'filters.js',
-    'index.js'
+    'index.js',
+    'sidebar.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/actions/sidebar.js
@@ -0,0 +1,49 @@
+/* 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 {
+  DISABLE_TOGGLE_BUTTON,
+  SHOW_SIDEBAR,
+  TOGGLE_SIDEBAR,
+} = require("../constants");
+
+/**
+ * Change ToggleButton disabled state.
+ *
+ * @param {boolean} disabled - expected button disabled state
+ */
+function disableToggleButton(disabled) {
+  return {
+    type: DISABLE_TOGGLE_BUTTON,
+    disabled: disabled,
+  };
+}
+
+/**
+ * Change sidebar visible state.
+ *
+ * @param {boolean} visible - expected sidebar visible state
+ */
+function showSidebar(visible) {
+  return {
+    type: SHOW_SIDEBAR,
+    visible: visible,
+  };
+}
+
+/**
+ * Toggle to show/hide sidebar.
+ */
+function toggleSidebar() {
+  return {
+    type: TOGGLE_SIDEBAR,
+  };
+}
+
+module.exports = {
+  disableToggleButton,
+  showSidebar,
+  toggleSidebar,
+};
--- a/devtools/client/netmonitor/components/moz.build
+++ b/devtools/client/netmonitor/components/moz.build
@@ -1,8 +1,9 @@
 # vim: set filetype=python:
 # 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/.
 
 DevToolsModules(
     'filter-buttons.js',
+    'toggle-button.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/components/toggle-button.js
@@ -0,0 +1,69 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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/. */
+/* globals NetMonitorView */
+"use strict";
+
+const { DOM, PropTypes } = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { L10N } = require("../l10n");
+const Actions = require("../actions/index");
+
+// Shortcuts
+const { button } = DOM;
+
+/**
+ * Button used to toggle sidebar
+ */
+function ToggleButton({
+  disabled,
+  onToggle,
+  visible,
+}) {
+  let className = ["devtools-button"];
+  if (!visible) {
+    className.push("pane-collapsed");
+  }
+  let titleMsg = visible ? L10N.getStr("collapseDetailsPane") :
+                           L10N.getStr("expandDetailsPane");
+
+  return button({
+    id: "details-pane-toggle",
+    className: className.join(" "),
+    title: titleMsg,
+    disabled: disabled,
+    tabIndex: "0",
+    onMouseDown: onToggle,
+  });
+}
+
+ToggleButton.propTypes = {
+  disabled: PropTypes.bool.isRequired,
+  onToggle: PropTypes.func.isRequired,
+  visible: PropTypes.bool.isRequired,
+};
+
+module.exports = connect(
+  (state) => ({
+    disabled: state.sidebar.toggleButtonDisabled,
+    visible: state.sidebar.visible,
+  }),
+  (dispatch) => ({
+    onToggle: () => {
+      dispatch(Actions.toggleSidebar());
+
+      let requestsMenu = NetMonitorView.RequestsMenu;
+      let selectedIndex = requestsMenu.selectedIndex;
+
+      // Make sure there's a selection if the button is pressed, to avoid
+      // showing an empty network details pane.
+      if (selectedIndex == -1 && requestsMenu.itemCount) {
+        requestsMenu.selectedIndex = 0;
+      } else {
+        requestsMenu.selectedIndex = -1;
+      }
+    },
+  })
+)(ToggleButton);
--- a/devtools/client/netmonitor/constants.js
+++ b/devtools/client/netmonitor/constants.js
@@ -1,11 +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";
 
 const actionTypes = {
   TOGGLE_FILTER: "TOGGLE_FILTER",
   ENABLE_FILTER_ONLY: "ENABLE_FILTER_ONLY",
+  TOGGLE_SIDEBAR: "TOGGLE_SIDEBAR",
+  SHOW_SIDEBAR: "SHOW_SIDEBAR",
+  DISABLE_TOGGLE_BUTTON: "DISABLE_TOGGLE_BUTTON",
 };
 
 module.exports = actionTypes;
--- a/devtools/client/netmonitor/netmonitor-view.js
+++ b/devtools/client/netmonitor/netmonitor-view.js
@@ -111,20 +111,16 @@ var NetMonitorView = {
   /**
    * Initializes the UI for all the displayed panes.
    */
   _initializePanes: function () {
     dumpn("Initializing the NetMonitorView panes");
 
     this._body = $("#body");
     this._detailsPane = $("#details-pane");
-    this._detailsPaneToggleButton = $("#details-pane-toggle");
-
-    this._collapsePaneString = L10N.getStr("collapseDetailsPane");
-    this._expandPaneString = L10N.getStr("expandDetailsPane");
 
     this._detailsPane.setAttribute("width", Prefs.networkDetailsWidth);
     this._detailsPane.setAttribute("height", Prefs.networkDetailsHeight);
     this.toggleDetailsPane({ visible: false });
 
     // Disable the performance statistics mode.
     if (!Prefs.statistics) {
       $("#request-menu-context-perf").hidden = true;
@@ -138,17 +134,16 @@ var NetMonitorView = {
    */
   _destroyPanes: Task.async(function* () {
     dumpn("Destroying the NetMonitorView panes");
 
     Prefs.networkDetailsWidth = this._detailsPane.getAttribute("width");
     Prefs.networkDetailsHeight = this._detailsPane.getAttribute("height");
 
     this._detailsPane = null;
-    this._detailsPaneToggleButton = null;
 
     for (let p of this._editorPromises.values()) {
       let editor = yield p;
       editor.destroy();
     }
   }),
 
   /**
@@ -167,29 +162,24 @@ var NetMonitorView = {
    *        - visible: true if the pane should be shown, false to hide
    *        - animated: true to display an animation on toggle
    *        - delayed: true to wait a few cycles before toggle
    *        - callback: a function to invoke when the toggle finishes
    * @param number tabIndex [optional]
    *        The index of the intended selected tab in the details pane.
    */
   toggleDetailsPane: function (flags, tabIndex) {
-    let pane = this._detailsPane;
-    let button = this._detailsPaneToggleButton;
-
-    ViewHelpers.togglePane(flags, pane);
+    ViewHelpers.togglePane(flags, this._detailsPane);
 
     if (flags.visible) {
       this._body.classList.remove("pane-collapsed");
-      button.classList.remove("pane-collapsed");
-      button.setAttribute("tooltiptext", this._collapsePaneString);
+      gStore.dispatch(Actions.showSidebar(true));
     } else {
       this._body.classList.add("pane-collapsed");
-      button.classList.add("pane-collapsed");
-      button.setAttribute("tooltiptext", this._expandPaneString);
+      gStore.dispatch(Actions.showSidebar(false));
     }
 
     if (tabIndex !== undefined) {
       $("#event-details-pane").selectedIndex = tabIndex;
     }
   },
 
   /**
@@ -284,19 +274,16 @@ var NetMonitorView = {
     let editor = new Editor(DEFAULT_EDITOR_CONFIG);
     editor.appendTo($(id)).then(() => deferred.resolve(editor));
 
     return deferred.promise;
   },
 
   _body: null,
   _detailsPane: null,
-  _detailsPaneToggleButton: null,
-  _collapsePaneString: "",
-  _expandPaneString: "",
   _editorPromises: new Map()
 };
 
 /**
  * Functions handling the sidebar details view.
  */
 function SidebarView() {
   dumpn("SidebarView was instantiated");
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -31,21 +31,18 @@
         <toolbarbutton id="requests-menu-network-summary-button"
                        class="devtools-toolbarbutton icon-and-text"
                        data-localization="tooltiptext=netmonitor.toolbar.perf"/>
         <textbox id="requests-menu-filter-freetext-text"
                  class="devtools-filterinput"
                  type="search"
                  required="true"
                  data-localization="placeholder=netmonitor.toolbar.filterFreetext.label"/>
-        <toolbarbutton id="details-pane-toggle"
-                       class="devtools-toolbarbutton"
-                       data-localization="tooltiptext=netmonitor.panesButton.tooltip"
-                       disabled="true"
-                       tabindex="0"/>
+        <html:div xmlns="http://www.w3.org/1999/xhtml"
+                  id="react-details-pane-toggle-hook"/>
       </hbox>
       <hbox id="network-table-and-sidebar"
             class="devtools-responsive-container"
             flex="1">
         <vbox id="network-table" flex="1" class="devtools-main-content">
           <toolbar id="requests-menu-toolbar"
                    class="devtools-toolbar"
                    align="center">
--- a/devtools/client/netmonitor/reducers/index.js
+++ b/devtools/client/netmonitor/reducers/index.js
@@ -1,11 +1,13 @@
 /* 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 { combineReducers } = require("devtools/client/shared/vendor/redux");
 const filters = require("./filters");
+const sidebar = require("./sidebar");
 
 module.exports = combineReducers({
   filters,
+  sidebar,
 });
--- a/devtools/client/netmonitor/reducers/moz.build
+++ b/devtools/client/netmonitor/reducers/moz.build
@@ -1,9 +1,10 @@
 # vim: set filetype=python:
 # 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/.
 
 DevToolsModules(
     'filters.js',
-    'index.js'
+    'index.js',
+    'sidebar.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/reducers/sidebar.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";
+
+const I = require("devtools/client/shared/vendor/immutable");
+const {
+  DISABLE_TOGGLE_BUTTON,
+  SHOW_SIDEBAR,
+  TOGGLE_SIDEBAR,
+} = require("../constants");
+
+const SidebarState = I.Record({
+  toggleButtonDisabled: true,
+  visible: false,
+});
+
+function disableToggleButton(state, action) {
+  return state.set("toggleButtonDisabled", action.disabled);
+}
+
+function showSidebar(state, action) {
+  return state.set("visible", action.visible);
+}
+
+function toggleSidebar(state, action) {
+  return state.set("visible", !state.visible);
+}
+
+function sidebar(state = new SidebarState(), action) {
+  switch (action.type) {
+    case DISABLE_TOGGLE_BUTTON:
+      return disableToggleButton(state, action);
+    case SHOW_SIDEBAR:
+      return showSidebar(state, action);
+    case TOGGLE_SIDEBAR:
+      return toggleSidebar(state, action);
+    default:
+      return state;
+  }
+}
+
+module.exports = sidebar;
--- a/devtools/client/netmonitor/requests-menu-view.js
+++ b/devtools/client/netmonitor/requests-menu-view.js
@@ -122,16 +122,18 @@ function RequestsMenuView() {
 
 RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
   /**
    * Initialization function, called when the network monitor is started.
    */
   initialize: function (store) {
     dumpn("Initializing the RequestsMenuView");
 
+    this.store = store;
+
     let widgetParentEl = $("#requests-menu-contents");
     this.widget = new SideMenuWidget(widgetParentEl);
     this._splitter = $("#network-inspector-view-splitter");
     this._summary = $("#requests-menu-network-summary-button");
     this._summary.setAttribute("label", L10N.getStr("networkMenu.empty"));
     this.userInputTimer = Cc["@mozilla.org/timer;1"]
       .createInstance(Ci.nsITimer);
 
@@ -760,17 +762,17 @@ RequestsMenuView.prototype = Heritage.ex
 
   /**
    * Removes all network requests and closes the sidebar if open.
    */
   clear: function () {
     NetMonitorController.NetworkEventsHandler.clearMarkers();
     NetMonitorView.Sidebar.toggle(false);
 
-    $("#details-pane-toggle").disabled = true;
+    this.store.dispatch(Actions.disableToggleButton(true));
     $("#requests-menu-empty-notice").hidden = false;
 
     this.empty();
     this.refreshSummary();
   },
 
   /**
    * Refreshes the status displayed in this container's footer, providing
@@ -1073,17 +1075,17 @@ RequestsMenuView.prototype = Heritage.ex
         NetMonitorView.NetworkDetails.populate(selectedItem.attachment);
       }
     }
 
     // We're done flushing all the requests, clear the update queue.
     this._updateQueue = [];
     this._addQueue = [];
 
-    $("#details-pane-toggle").disabled = !this.itemCount;
+    this.store.dispatch(Actions.disableToggleButton(!this.itemCount));
     $("#requests-menu-empty-notice").hidden = !!this.itemCount;
 
     // Make sure all the requests are sorted and filtered.
     // Freshly added requests may not yet contain all the information required
     // for sorting and filtering predicates, so this is done each time the
     // network requests table is flushed (don't worry, events are drained first
     // so this doesn't happen once per network event update).
     this.sortContents();
--- a/devtools/client/netmonitor/toolbar-view.js
+++ b/devtools/client/netmonitor/toolbar-view.js
@@ -1,38 +1,38 @@
 /* globals dumpn, $, NetMonitorView */
 "use strict";
 
 const { createFactory, DOM } = require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
 const FilterButtons = createFactory(require("./components/filter-buttons"));
+const ToggleButton = createFactory(require("./components/toggle-button"));
 const { L10N } = require("./l10n");
 
 // Shortcuts
 const { button } = DOM;
 
 /**
  * Functions handling the toolbar view: expand/collapse button etc.
  */
 function ToolbarView() {
   dumpn("ToolbarView was instantiated");
-
-  this._onTogglePanesPressed = this._onTogglePanesPressed.bind(this);
 }
 
 ToolbarView.prototype = {
   /**
    * Initialization function, called when the debugger is started.
    */
   initialize: function (store) {
     dumpn("Initializing the ToolbarView");
 
     this._clearContainerNode = $("#react-clear-button-hook");
     this._filterContainerNode = $("#react-filter-buttons-hook");
+    this._toggleContainerNode = $("#react-details-pane-toggle-hook");
 
     // clear button
     ReactDOM.render(button({
       id: "requests-menu-clear-button",
       className: "devtools-button devtools-clear-icon",
       title: L10N.getStr("netmonitor.toolbar.clear"),
       onClick: () => {
         NetMonitorView.RequestsMenu.clear();
@@ -40,46 +40,27 @@ ToolbarView.prototype = {
     }), this._clearContainerNode);
 
     // filter button
     ReactDOM.render(Provider(
       { store },
       FilterButtons()
     ), this._filterContainerNode);
 
-    this._detailsPaneToggleButton = $("#details-pane-toggle");
-    this._detailsPaneToggleButton.addEventListener("mousedown",
-      this._onTogglePanesPressed, false);
+    ReactDOM.render(Provider(
+      { store },
+      ToggleButton()
+    ), this._toggleContainerNode);
   },
 
   /**
    * Destruction function, called when the debugger is closed.
    */
   destroy: function () {
     dumpn("Destroying the ToolbarView");
 
     ReactDOM.unmountComponentAtNode(this._clearContainerNode);
     ReactDOM.unmountComponentAtNode(this._filterContainerNode);
-
-    this._detailsPaneToggleButton.removeEventListener("mousedown",
-      this._onTogglePanesPressed, false);
-  },
-
-  /**
-   * Listener handling the toggle button click event.
-   */
-  _onTogglePanesPressed: function () {
-    let requestsMenu = NetMonitorView.RequestsMenu;
-    let selectedIndex = requestsMenu.selectedIndex;
-
-    // Make sure there's a selection if the button is pressed, to avoid
-    // showing an empty network details pane.
-    if (selectedIndex == -1 && requestsMenu.itemCount) {
-      requestsMenu.selectedIndex = 0;
-    } else {
-      requestsMenu.selectedIndex = -1;
-    }
-  },
-
-  _detailsPaneToggleButton: null
+    ReactDOM.unmountComponentAtNode(this._toggleContainerNode);
+  }
 };
 
 exports.ToolbarView = ToolbarView;
--- a/devtools/client/responsive.html/browser/swap.js
+++ b/devtools/client/responsive.html/browser/swap.js
@@ -47,16 +47,18 @@ function swapToInnerBrowser({ tab, conta
       bubbles: true,
     });
     from.dispatchEvent(event);
   };
 
   return {
 
     start: Task.async(function* () {
+      tab.isReponsiveDesignMode = true;
+
       // Freeze navigation temporarily to avoid "blinking" in the location bar.
       freezeNavigationState(tab);
 
       // 1. Create a temporary, hidden tab to load the tool UI.
       let containerTab = gBrowser.addTab(containerURL, {
         skipAnimation: true,
       });
       gBrowser.hideTab(containerTab);
@@ -145,16 +147,18 @@ function swapToInnerBrowser({ tab, conta
       dispatchDevToolsBrowserSwap(contentBrowser, tab.linkedBrowser);
       gBrowser.swapBrowsersAndCloseOther(tab, contentTab);
       gBrowser = null;
 
       // The focus manager seems to get a little dizzy after all this swapping.  If a
       // content element had been focused inside the viewport before stopping, it will
       // have lost focus.  Activate the frame to restore expected focus.
       tab.linkedBrowser.frameLoader.activateRemoteFrame();
+
+      delete tab.isReponsiveDesignMode;
     },
 
   };
 }
 
 /**
  * Browser navigation properties we'll freeze temporarily to avoid "blinking" in the
  * location bar, etc. caused by the containerURL peeking through before the swap is
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -2,17 +2,18 @@
 /* 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/. */
 
 #toolbar-labels {
   overflow: hidden;
 }
 
-#react-clear-button-hook {
+#react-clear-button-hook,
+#react-details-pane-toggle-hook {
   display: flex;
 }
 
 /**
  * Collapsed details pane needs to be truly hidden to prevent both accessibility
  * tools and keyboard from accessing its contents.
  */
 #details-pane.pane-collapsed {
@@ -563,24 +564,24 @@
 /* Size Column */
 .theme-firebug .requests-menu-subitem.requests-menu-size {
   text-align: end;
   padding-inline-end: 4px;
 }
 
 /* Network request details */
 
-#details-pane-toggle:-moz-locale-dir(ltr),
-#details-pane-toggle.pane-collapsed:-moz-locale-dir(rtl) {
-  list-style-image: var(--theme-pane-collapse-image);
+#details-pane-toggle:-moz-locale-dir(ltr)::before,
+#details-pane-toggle.pane-collapsed:-moz-locale-dir(rtl)::before {
+  background-image: var(--theme-pane-collapse-image);
 }
 
-#details-pane-toggle.pane-collapsed:-moz-locale-dir(ltr),
-#details-pane-toggle:-moz-locale-dir(rtl) {
-  list-style-image: var(--theme-pane-expand-image);
+#details-pane-toggle.pane-collapsed:-moz-locale-dir(ltr)::before,
+#details-pane-toggle:-moz-locale-dir(rtl)::before {
+  background-image: var(--theme-pane-expand-image);
 }
 
 /* Network request details tabpanels */
 
 .tabpanel-content {
   background-color: var(--theme-sidebar-background);
 }
 
--- a/devtools/shared/touch/simulator-core.js
+++ b/devtools/shared/touch/simulator-core.js
@@ -138,18 +138,17 @@ SimulatorCore.prototype = {
       case "mouseleave":
         // Don't propagate events which are not related to touch events
         evt.stopPropagation();
         break;
 
       case "mousedown":
         this.target = evt.target;
 
-        this.contextMenuTimeout =
-          this.sendContextMenu(evt.target, evt.pageX, evt.pageY);
+        this.contextMenuTimeout = this.sendContextMenu(evt);
 
         this.cancelClick = false;
         this.startX = evt.pageX;
         this.startY = evt.pageY;
 
         // Capture events so if a different window show up the events
         // won't be dispatched to something else.
         evt.target.setCapture(false);
@@ -230,23 +229,28 @@ SimulatorCore.prototype = {
   fireMouseEvent(type, evt) {
     let content = this.getContent(evt.target);
     let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDOMWindowUtils);
     utils.sendMouseEvent(type, evt.clientX, evt.clientY, 0, 1, 0, true, 0,
                          Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
   },
 
-  sendContextMenu(target, x, y) {
-    let doc = target.ownerDocument;
-    let evt = doc.createEvent("MouseEvent");
-    evt.initMouseEvent("contextmenu", true, true, doc.defaultView,
-                       0, x, y, x, y, false, false, false, false,
-                       0, null);
-
+  sendContextMenu({ target, clientX, clientY, screenX, screenY }) {
+    let view = target.ownerDocument.defaultView;
+    let { MouseEvent } = view;
+    let evt = new MouseEvent("contextmenu", {
+      bubbles: true,
+      cancelable: true,
+      view,
+      screenX,
+      screenY,
+      clientX,
+      clientY,
+    });
     let content = this.getContent(target);
     let timeout = content.setTimeout((function contextMenu() {
       target.dispatchEvent(evt);
       this.cancelClick = true;
     }).bind(this), delay);
 
     return timeout;
   },
--- a/dom/apps/PermissionsTable.jsm
+++ b/dom/apps/PermissionsTable.jsm
@@ -131,21 +131,16 @@ this.PermissionsTable =  { geolocation: 
                              privileged: ALLOW_ACTION,
                              certified: ALLOW_ACTION
                            },
                            "browser:embedded-system-app": {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
-                           bluetooth: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
                            mobileconnection: {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
                            mobilenetwork: {
                              app: DENY_ACTION,
                              privileged: ALLOW_ACTION,
--- a/dom/base/DOMException.cpp
+++ b/dom/base/DOMException.cpp
@@ -70,29 +70,16 @@ enum DOM4ErrorTypeCodeMap {
   NotReadableError         = 0,
 
   /* FileHandle API errors */
   FileHandleInactiveError = 0,
 
   /* WebCrypto errors https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-DataError */
   OperationError           = 0,
 
-  /* Bluetooth API errors */
-  BtFailError              = 0,
-  BtNotReadyError          = 0,
-  BtNoMemError             = 0,
-  BtBusyError              = 0,
-  BtDoneError              = 0,
-  BtUnsupportedError       = 0,
-  BtParmInvalidError       = 0,
-  BtUnhandledError         = 0,
-  BtAuthFailureError       = 0,
-  BtRmtDevDownError        = 0,
-  BtAuthRejectedError      = 0,
-
   /* Push API errors */
   NotAllowedError          = 0,
 };
 
 #define DOM4_MSG_DEF(name, message, nsresult) {(nsresult), name, #name, message},
 #define DOM_MSG_DEF(val, message) {(val), NS_ERROR_GET_CODE(val), #val, message},
 
 static const struct ResultStruct
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -74,19 +74,16 @@
 #include "nsStreamUtils.h"
 #include "nsIAppsService.h"
 #include "mozIApplication.h"
 #include "WidgetUtils.h"
 #include "nsIPresentationService.h"
 
 #include "mozilla/dom/MediaDevices.h"
 #include "MediaManager.h"
-#ifdef MOZ_B2G_BT
-#include "BluetoothManager.h"
-#endif
 
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
 #include "AudioChannelManager.h"
 #endif
 
 #include "nsIDOMGlobalPropertyInitializer.h"
 #include "nsJSUtils.h"
 
@@ -216,19 +213,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPowerManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIccManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputPortManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager)
 #ifdef MOZ_B2G_RIL
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMobileConnections)
 #endif
-#ifdef MOZ_B2G_BT
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBluetooth)
-#endif
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelManager)
 #endif
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimeManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
@@ -299,22 +293,16 @@ Navigator::Invalidate()
   }
 
 #ifdef MOZ_B2G_RIL
   if (mMobileConnections) {
     mMobileConnections = nullptr;
   }
 #endif
 
-#ifdef MOZ_B2G_BT
-  if (mBluetooth) {
-    mBluetooth = nullptr;
-  }
-#endif
-
   mMediaDevices = nullptr;
 
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   if (mAudioChannelManager) {
     mAudioChannelManager = nullptr;
   }
 #endif
 
@@ -1795,32 +1783,16 @@ Navigator::GetConnection(ErrorResult& aR
       return nullptr;
     }
     mConnection = new network::Connection(mWindow);
   }
 
   return mConnection;
 }
 
-#ifdef MOZ_B2G_BT
-bluetooth::BluetoothManager*
-Navigator::GetMozBluetooth(ErrorResult& aRv)
-{
-  if (!mBluetooth) {
-    if (!mWindow) {
-      aRv.Throw(NS_ERROR_UNEXPECTED);
-      return nullptr;
-    }
-    mBluetooth = bluetooth::BluetoothManager::Create(mWindow);
-  }
-
-  return mBluetooth;
-}
-#endif //MOZ_B2G_BT
-
 #ifdef MOZ_TIME_MANAGER
 time::TimeManager*
 Navigator::GetMozTime(ErrorResult& aRv)
 {
   if (!mWindow) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -67,22 +67,16 @@ class GamepadServiceTest;
 class NavigatorUserMediaSuccessCallback;
 class NavigatorUserMediaErrorCallback;
 class MozGetUserMediaDevicesSuccessCallback;
 
 namespace network {
 class Connection;
 } // namespace network
 
-#ifdef MOZ_B2G_BT
-namespace bluetooth {
-class BluetoothManager;
-} // namespace bluetooth
-#endif // MOZ_B2G_BT
-
 #ifdef MOZ_B2G_RIL
 class MobileConnectionArray;
 #endif
 
 class PowerManager;
 class IccManager;
 class InputPortManager;
 class DeviceStorageAreaListener;
@@ -230,19 +224,16 @@ public:
   MobileConnectionArray* GetMozMobileConnections(ErrorResult& aRv);
 #endif // MOZ_B2G_RIL
 #ifdef MOZ_GAMEPAD
   void GetGamepads(nsTArray<RefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
   GamepadServiceTest* RequestGamepadServiceTest();
 #endif // MOZ_GAMEPAD
   already_AddRefed<Promise> GetVRDisplays(ErrorResult& aRv);
   void GetActiveVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays) const;
-#ifdef MOZ_B2G_BT
-  bluetooth::BluetoothManager* GetMozBluetooth(ErrorResult& aRv);
-#endif // MOZ_B2G_BT
 #ifdef MOZ_TIME_MANAGER
   time::TimeManager* GetMozTime(ErrorResult& aRv);
 #endif // MOZ_TIME_MANAGER
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   system::AudioChannelManager* GetMozAudioChannelManager(ErrorResult& aRv);
 #endif // MOZ_AUDIO_CHANNEL_MANAGER
 
   Presentation* GetPresentation(ErrorResult& aRv);
@@ -321,19 +312,16 @@ private:
   RefPtr<Promise> mBatteryPromise;
   RefPtr<PowerManager> mPowerManager;
   RefPtr<IccManager> mIccManager;
   RefPtr<InputPortManager> mInputPortManager;
   RefPtr<network::Connection> mConnection;
 #ifdef MOZ_B2G_RIL
   RefPtr<MobileConnectionArray> mMobileConnections;
 #endif
-#ifdef MOZ_B2G_BT
-  RefPtr<bluetooth::BluetoothManager> mBluetooth;
-#endif
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   RefPtr<system::AudioChannelManager> mAudioChannelManager;
 #endif
   RefPtr<MediaDevices> mMediaDevices;
   nsTArray<nsWeakPtr> mDeviceStorageStores;
   RefPtr<time::TimeManager> mTimeManager;
   RefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
--- a/dom/base/domerr.msg
+++ b/dom/base/domerr.msg
@@ -98,29 +98,16 @@ DOM_MSG_DEF(NS_ERROR_DOM_PROP_ACCESS_DEN
 DOM_MSG_DEF(NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED, "Access to XPConnect service denied")
 DOM_MSG_DEF(NS_ERROR_DOM_BAD_URI, "Access to restricted URI denied")
 DOM_MSG_DEF(NS_ERROR_DOM_RETVAL_UNDEFINED, "Return value is undefined")
 DOM_MSG_DEF(NS_ERROR_DOM_QUOTA_REACHED, "Persistent storage maximum size reached")
 
 DOM4_MSG_DEF(NotFoundError, "File was not found", NS_ERROR_DOM_FILE_NOT_FOUND_ERR)
 DOM4_MSG_DEF(NotReadableError, "File could not be read", NS_ERROR_DOM_FILE_NOT_READABLE_ERR)
 
-/* Non-standard Bluetooth DOM errors. */
-DOM4_MSG_DEF(BtFailError,         "Fail",  NS_ERROR_DOM_BLUETOOTH_FAIL)
-DOM4_MSG_DEF(BtNotReadyError,     "Not ready",  NS_ERROR_DOM_BLUETOOTH_NOT_READY)
-DOM4_MSG_DEF(BtNoMemError,        "No memory",  NS_ERROR_DOM_BLUETOOTH_NOMEM)
-DOM4_MSG_DEF(BtBusyError,         "Device busy with another command",  NS_ERROR_DOM_BLUETOOTH_BUSY)
-DOM4_MSG_DEF(BtDoneError,         "Request already done",  NS_ERROR_DOM_BLUETOOTH_DONE)
-DOM4_MSG_DEF(BtUnsupportedError,  "Unsupported",  NS_ERROR_DOM_BLUETOOTH_UNSUPPORTED)
-DOM4_MSG_DEF(BtParmInvalidError,  "Invalid parameter",  NS_ERROR_DOM_BLUETOOTH_PARM_INVALID)
-DOM4_MSG_DEF(BtUnhandledError,    "Unhandled",  NS_ERROR_DOM_BLUETOOTH_UNHANDLED)
-DOM4_MSG_DEF(BtAuthFailureError,  "Authentication failure",  NS_ERROR_DOM_BLUETOOTH_AUTH_FAILURE)
-DOM4_MSG_DEF(BtRmtDevDownError,   "Remote device down",  NS_ERROR_DOM_BLUETOOTH_RMT_DEV_DOWN)
-DOM4_MSG_DEF(BtAuthRejectedError, "Authentication rejected",  NS_ERROR_DOM_BLUETOOTH_AUTH_REJECTED)
-
 /* Web Animations errors */
 
 DOM4_MSG_DEF(NotSupportedError, "Animation to or from an underlying value is not yet supported.", NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR)
 
 /* common global codes (from nsError.h) */
 
 DOM_MSG_DEF(NS_OK                                  , "Success")
 DOM_MSG_DEF(NS_ERROR_NOT_INITIALIZED               , "Component not initialized")
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -416,18 +416,16 @@ if CONFIG['MOZ_BUILD_APP'] != 'mobile/an
 
 EXTRA_JS_MODULES += [
     'DOMRequestHelper.jsm',
     'IndexedDBHelper.jsm',
 ]
 
 LOCAL_INCLUDES += [
     '../battery',
-    '../bluetooth/common',
-    '../bluetooth/common/webapi',
     '../events',
     '../media',
     '../network',
     '../time',
     '/caps',
     '/docshell/base',
     '/dom/base',
     '/dom/geolocation',
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -102,85 +102,16 @@ DOMInterfaces = {
     'headerFile': 'mozilla/dom/File.h',
 },
 
 'BatteryManager': {
     'nativeType': 'mozilla::dom::battery::BatteryManager',
     'headerFile': 'BatteryManager.h'
 },
 
-'BluetoothAdapter': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothAdapter',
-},
-
-'BluetoothClassOfDevice': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothClassOfDevice',
-},
-
-'BluetoothDevice': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothDevice',
-},
-
-'BluetoothDiscoveryHandle': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothDiscoveryHandle',
-},
-
-'BluetoothGatt': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothGatt',
-},
-
-'BluetoothGattAttributeEvent': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothGattAttributeEvent',
-},
-
-'BluetoothGattCharacteristic': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothGattCharacteristic',
-},
-
-'BluetoothGattDescriptor': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothGattDescriptor',
-},
-
-'BluetoothGattServer': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothGattServer',
-},
-
-'BluetoothGattService': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothGattService',
-},
-
-'BluetoothLeDeviceEvent': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothLeDeviceEvent',
-},
-
-'BluetoothManager': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothManager',
-},
-
-'BluetoothObexAuthHandle': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothObexAuthHandle',
-},
-
-'BluetoothPairingHandle': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothPairingHandle',
-},
-
-'BluetoothPairingListener': {
-    'nativeType':
-      'mozilla::dom::bluetooth::BluetoothPairingListener',
-},
-
-'BluetoothPbapRequestHandle': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothPbapRequestHandle',
-},
-
-'BluetoothMapRequestHandle': {
-    'nativeType': 'mozilla::dom::bluetooth::BluetoothMapRequestHandle',
-},
-
 'BoxObject': {
     'resultNotAddRefed': ['element'],
 },
 
 'Cache': {
     'implicitJSContext': [ 'add', 'addAll' ],
     'nativeType': 'mozilla::dom::cache::Cache',
 },
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -188,17 +188,16 @@ CreateException(JSContext* aCx, nsresult
 {
   // Do we use DOM exceptions for this error code?
   switch (NS_ERROR_GET_MODULE(aRv)) {
   case NS_ERROR_MODULE_DOM:
   case NS_ERROR_MODULE_SVG:
   case NS_ERROR_MODULE_DOM_XPATH:
   case NS_ERROR_MODULE_DOM_INDEXEDDB:
   case NS_ERROR_MODULE_DOM_FILEHANDLE:
-  case NS_ERROR_MODULE_DOM_BLUETOOTH:
   case NS_ERROR_MODULE_DOM_ANIM:
   case NS_ERROR_MODULE_DOM_PUSH:
   case NS_ERROR_MODULE_DOM_MEDIA:
     if (aMessage.IsEmpty()) {
       return DOMException::Create(aRv);
     }
     return DOMException::Create(aRv, aMessage);
   default:
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -54,17 +54,16 @@ EXPORTS.mozilla.dom += [
 # Bug 932082 tracks.
 LOCAL_INCLUDES += [
     '!/dist/include/mozilla/dom',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/base',
     '/dom/battery',
-    '/dom/bluetooth/common/webapi',
     '/dom/canvas',
     '/dom/geolocation',
     '/dom/html',
     '/dom/indexedDB',
     '/dom/media/webaudio',
     '/dom/media/webspeech/recognition',
     '/dom/svg',
     '/dom/workers',
deleted file mode 100644
--- a/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
+++ /dev/null
@@ -1,779 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "base/basictypes.h"
-
-#include "BluetoothA2dpManager.h"
-#include "BluetoothCommon.h"
-#include "BluetoothService.h"
-#include "BluetoothSocket.h"
-#include "BluetoothUtils.h"
-
-#include "mozilla/dom/bluetooth/BluetoothTypes.h"
-#include "mozilla/Services.h"
-#include "mozilla/StaticPtr.h"
-#include "MainThreadUtils.h"
-#include "nsIObserverService.h"
-#include "nsThreadUtils.h"
-
-using namespace mozilla;
-USING_BLUETOOTH_NAMESPACE
-// AVRC_ID op code follows bluedroid avrc_defs.h
-#define AVRC_ID_REWIND  0x48
-#define AVRC_ID_FAST_FOR 0x49
-#define AVRC_KEY_PRESS_STATE  1
-#define AVRC_KEY_RELEASE_STATE  0
-// bluedroid bt_rc.h
-#define AVRC_MAX_ATTR_STR_LEN 255
-
-namespace {
-  StaticRefPtr<BluetoothA2dpManager> sBluetoothA2dpManager;
-  bool sInShutdown = false;
-  static BluetoothA2dpInterface* sBtA2dpInterface;
-} // namespace
-
-const int BluetoothA2dpManager::MAX_NUM_CLIENTS = 1;
-
-NS_IMETHODIMP
-BluetoothA2dpManager::Observe(nsISupports* aSubject,
-                              const char* aTopic,
-                              const char16_t* aData)
-{
-  MOZ_ASSERT(sBluetoothA2dpManager);
-
-  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
-    HandleShutdown();
-    return NS_OK;
-  }
-
-  MOZ_ASSERT(false, "BluetoothA2dpManager got unexpected topic!");
-  return NS_ERROR_UNEXPECTED;
-}
-
-BluetoothA2dpManager::BluetoothA2dpManager()
-{
-  Reset();
-}
-
-void
-BluetoothA2dpManager::Reset()
-{
-  mA2dpConnected = false;
-  mSinkState = SinkState::SINK_DISCONNECTED;
-  mController = nullptr;
-}
-
-static void
-AvStatusToSinkString(BluetoothA2dpConnectionState aState, nsAString& aString)
-{
-  switch (aState) {
-    case A2DP_CONNECTION_STATE_DISCONNECTED:
-      aString.AssignLiteral("disconnected");
-      break;
-    case A2DP_CONNECTION_STATE_CONNECTING:
-      aString.AssignLiteral("connecting");
-      break;
-    case A2DP_CONNECTION_STATE_CONNECTED:
-      aString.AssignLiteral("connected");
-      break;
-    case A2DP_CONNECTION_STATE_DISCONNECTING:
-      aString.AssignLiteral("disconnecting");
-      break;
-    default:
-      BT_WARNING("Unknown sink state %d", static_cast<int>(aState));
-      return;
-  }
-}
-
-class BluetoothA2dpManager::RegisterModuleResultHandler final
-  : public BluetoothSetupResultHandler
-{
-public:
-  RegisterModuleResultHandler(BluetoothA2dpInterface* aInterface,
-                              BluetoothProfileResultHandler* aRes)
-    : mInterface(aInterface)
-    , mRes(aRes)
-  { }
-
-  void OnError(BluetoothStatus aStatus) override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    BT_WARNING("BluetoothSetupInterface::RegisterModule failed for A2DP: %d",
-               (int)aStatus);
-
-    mInterface->SetNotificationHandler(nullptr);
-
-    if (mRes) {
-      mRes->OnError(NS_ERROR_FAILURE);
-    }
-  }
-
-  void RegisterModule() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    sBtA2dpInterface = mInterface;
-
-    if (mRes) {
-      mRes->Init();
-    }
-  }
-
-private:
-  BluetoothA2dpInterface* mInterface;
-  RefPtr<BluetoothProfileResultHandler> mRes;
-};
-
-class BluetoothA2dpManager::InitProfileResultHandlerRunnable final
-  : public Runnable
-{
-public:
-  InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
-                                   nsresult aRv)
-    : mRes(aRes)
-    , mRv(aRv)
-  {
-    MOZ_ASSERT(mRes);
-  }
-
-  NS_IMETHOD Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (NS_SUCCEEDED(mRv)) {
-      mRes->Init();
-    } else {
-      mRes->OnError(mRv);
-    }
-    return NS_OK;
-  }
-
-private:
-  RefPtr<BluetoothProfileResultHandler> mRes;
-  nsresult mRv;
-};
-
-/*
- * This function will be only called when Bluetooth is turning on.
- * It is important to register a2dp callbacks before enable() gets called.
- * It is required to register a2dp callbacks before a2dp media task
- * starts up.
- */
-// static
-void
-BluetoothA2dpManager::InitA2dpInterface(BluetoothProfileResultHandler* aRes)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (sBtA2dpInterface) {
-    BT_LOGR("Bluetooth A2DP interface is already initalized.");
-    RefPtr<Runnable> r =
-      new InitProfileResultHandlerRunnable(aRes, NS_OK);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch A2DP Init runnable");
-    }
-    return;
-  }
-
-  auto btInf = BluetoothInterface::GetInstance();
-
-  if (NS_WARN_IF(!btInf)) {
-    // If there's no Bluetooth interface, we dispatch a runnable
-    // that calls the profile result handler.
-    RefPtr<Runnable> r =
-      new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch A2DP OnError runnable");
-    }
-    return;
-  }
-
-  auto setupInterface = btInf->GetBluetoothSetupInterface();
-
-  if (NS_WARN_IF(!setupInterface)) {
-    // If there's no Setup interface, we dispatch a runnable
-    // that calls the profile result handler.
-    RefPtr<Runnable> r =
-      new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch A2DP OnError runnable");
-    }
-    return;
-  }
-
-  auto a2dpInterface = btInf->GetBluetoothA2dpInterface();
-
-  if (NS_WARN_IF(!a2dpInterface)) {
-    // If there's no A2DP interface, we dispatch a runnable
-    // that calls the profile result handler.
-    RefPtr<Runnable> r =
-      new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch A2DP OnError runnable");
-    }
-    return;
-  }
-
-  // Set notification handler _before_ registering the module. It could
-  // happen that we receive notifications, before the result handler runs.
-  a2dpInterface->SetNotificationHandler(BluetoothA2dpManager::Get());
-
-  setupInterface->RegisterModule(
-    SETUP_SERVICE_ID_A2DP, 0, MAX_NUM_CLIENTS,
-    new RegisterModuleResultHandler(a2dpInterface, aRes));
-}
-
-BluetoothA2dpManager::~BluetoothA2dpManager()
-{ }
-
-void
-BluetoothA2dpManager::Uninit()
-{
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  NS_ENSURE_TRUE_VOID(obs);
-  if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
-    BT_WARNING("Failed to remove shutdown observer!");
-  }
-}
-
-/*
- * Static functions
- */
-
-static BluetoothA2dpManager::SinkState
-StatusStringToSinkState(const nsAString& aStatus)
-{
-  BluetoothA2dpManager::SinkState state =
-    BluetoothA2dpManager::SinkState::SINK_UNKNOWN;
-  if (aStatus.EqualsLiteral("disconnected")) {
-    state = BluetoothA2dpManager::SinkState::SINK_DISCONNECTED;
-  } else if (aStatus.EqualsLiteral("connecting")) {
-    state = BluetoothA2dpManager::SinkState::SINK_CONNECTING;
-  } else if (aStatus.EqualsLiteral("connected")) {
-    state = BluetoothA2dpManager::SinkState::SINK_CONNECTED;
-  } else if (aStatus.EqualsLiteral("playing")) {
-    state = BluetoothA2dpManager::SinkState::SINK_PLAYING;
-  } else {
-    BT_WARNING("Unknown sink state");
-  }
-  return state;
-}
-
-//static
-BluetoothA2dpManager*
-BluetoothA2dpManager::Get()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // If sBluetoothA2dpManager already exists, exit early
-  if (sBluetoothA2dpManager) {
-    return sBluetoothA2dpManager;
-  }
-
-  // If we're in shutdown, don't create a new instance
-  NS_ENSURE_FALSE(sInShutdown, nullptr);
-
-  // Create a new instance and return
-  sBluetoothA2dpManager = new BluetoothA2dpManager();
-
-  return sBluetoothA2dpManager;
-}
-
-class BluetoothA2dpManager::UnregisterModuleResultHandler final
-  : public BluetoothSetupResultHandler
-{
-public:
-  UnregisterModuleResultHandler(BluetoothProfileResultHandler* aRes)
-    : mRes(aRes)
-  { }
-
-  void OnError(BluetoothStatus aStatus) override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    BT_WARNING("BluetoothSetupInterface::UnregisterModule failed for A2DP: %d",
-               (int)aStatus);
-
-    sBtA2dpInterface->SetNotificationHandler(nullptr);
-    sBtA2dpInterface = nullptr;
-
-    sBluetoothA2dpManager->Uninit();
-    sBluetoothA2dpManager = nullptr;
-
-    if (mRes) {
-      mRes->OnError(NS_ERROR_FAILURE);
-    }
-  }
-
-  void UnregisterModule() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    sBtA2dpInterface->SetNotificationHandler(nullptr);
-    sBtA2dpInterface = nullptr;
-
-    sBluetoothA2dpManager->Uninit();
-    sBluetoothA2dpManager = nullptr;
-
-    if (mRes) {
-      mRes->Deinit();
-    }
-  }
-
-private:
-  RefPtr<BluetoothProfileResultHandler> mRes;
-};
-
-class BluetoothA2dpManager::DeinitProfileResultHandlerRunnable final
-  : public Runnable
-{
-public:
-  DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
-                                     nsresult aRv)
-    : mRes(aRes)
-    , mRv(aRv)
-  {
-    MOZ_ASSERT(mRes);
-  }
-
-  NS_IMETHOD Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (NS_SUCCEEDED(mRv)) {
-      mRes->Deinit();
-    } else {
-      mRes->OnError(mRv);
-    }
-    return NS_OK;
-  }
-
-private:
-  RefPtr<BluetoothProfileResultHandler> mRes;
-  nsresult mRv;
-};
-
-// static
-void
-BluetoothA2dpManager::DeinitA2dpInterface(BluetoothProfileResultHandler* aRes)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!sBtA2dpInterface) {
-    BT_LOGR("Bluetooth A2DP interface has not been initalized.");
-    RefPtr<Runnable> r =
-      new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch A2DP Deinit runnable");
-    }
-    return;
-  }
-
-  auto btInf = BluetoothInterface::GetInstance();
-
-  if (NS_WARN_IF(!btInf)) {
-    // If there's no backend interface, we dispatch a runnable
-    // that calls the profile result handler.
-    RefPtr<Runnable> r =
-      new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch A2DP OnError runnable");
-    }
-    return;
-  }
-
-  auto setupInterface = btInf->GetBluetoothSetupInterface();
-
-  if (NS_WARN_IF(!setupInterface)) {
-    // If there's no Setup interface, we dispatch a runnable
-    // that calls the profile result handler.
-    RefPtr<Runnable> r =
-      new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch A2DP OnError runnable");
-    }
-    return;
-  }
-
-  setupInterface->UnregisterModule(
-    SETUP_SERVICE_ID_A2DP,
-    new UnregisterModuleResultHandler(aRes));
-}
-
-void
-BluetoothA2dpManager::HandleShutdown()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  sInShutdown = true;
-  Disconnect(nullptr);
-  sBluetoothA2dpManager = nullptr;
-}
-
-void
-BluetoothA2dpManager::OnConnectError()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  mController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
-
-  mController = nullptr;
-  mDeviceAddress.Clear();
-}
-
-class BluetoothA2dpManager::ConnectResultHandler final
-  : public BluetoothA2dpResultHandler
-{
-public:
-  void OnError(BluetoothStatus aStatus) override
-  {
-    BT_LOGR("BluetoothA2dpInterface::Connect failed: %d", (int)aStatus);
-
-    NS_ENSURE_TRUE_VOID(sBluetoothA2dpManager);
-    sBluetoothA2dpManager->OnConnectError();
-  }
-};
-
-void
-BluetoothA2dpManager::Connect(const BluetoothAddress& aDeviceAddress,
-                              BluetoothProfileController* aController)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!aDeviceAddress.IsCleared());
-  MOZ_ASSERT(aController);
-
-  BluetoothService* bs = BluetoothService::Get();
-  if (!bs || sInShutdown) {
-    aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
-    return;
-  }
-
-  if (mA2dpConnected) {
-    aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED));
-    return;
-  }
-
-  mDeviceAddress = aDeviceAddress;
-  mController = aController;
-
-  if (!sBtA2dpInterface) {
-    BT_LOGR("sBluetoothA2dpInterface is null");
-    aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
-    return;
-  }
-
-  sBtA2dpInterface->Connect(mDeviceAddress, new ConnectResultHandler());
-}
-
-void
-BluetoothA2dpManager::OnDisconnectError()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  NS_ENSURE_TRUE_VOID(mController);
-
-  mController->NotifyCompletion(NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED));
-}
-
-class BluetoothA2dpManager::DisconnectResultHandler final
-  : public BluetoothA2dpResultHandler
-{
-public:
-  void OnError(BluetoothStatus aStatus) override
-  {
-    BT_LOGR("BluetoothA2dpInterface::Disconnect failed: %d", (int)aStatus);
-
-    NS_ENSURE_TRUE_VOID(sBluetoothA2dpManager);
-    sBluetoothA2dpManager->OnDisconnectError();
-  }
-};
-
-void
-BluetoothA2dpManager::Disconnect(BluetoothProfileController* aController)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mController);
-
-  BluetoothService* bs = BluetoothService::Get();
-  if (!bs) {
-    if (aController) {
-      aController->NotifyCompletion(
-        NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
-    }
-    return;
-  }
-
-  if (!mA2dpConnected) {
-    if (aController) {
-      aController->NotifyCompletion(
-        NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED));
-    }
-    return;
-  }
-
-  MOZ_ASSERT(!mDeviceAddress.IsCleared());
-
-  mController = aController;
-
-  if (!sBtA2dpInterface) {
-    BT_LOGR("sBluetoothA2dpInterface is null");
-    if (aController) {
-      aController->NotifyCompletion(
-        NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
-    }
-    return;
-  }
-
-  sBtA2dpInterface->Disconnect(mDeviceAddress, new DisconnectResultHandler());
-}
-
-void
-BluetoothA2dpManager::OnConnect(const nsAString& aErrorStr)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  /**
-   * On the one hand, notify the controller that we've done for outbound
-   * connections. On the other hand, we do nothing for inbound connections.
-   */
-  NS_ENSURE_TRUE_VOID(mController);
-
-  RefPtr<BluetoothProfileController> controller = mController.forget();
-  controller->NotifyCompletion(aErrorStr);
-}
-
-void
-BluetoothA2dpManager::OnDisconnect(const nsAString& aErrorStr)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  /**
-   * On the one hand, notify the controller that we've done for outbound
-   * connections. On the other hand, we do nothing for inbound connections.
-   */
-  NS_ENSURE_TRUE_VOID(mController);
-
-  RefPtr<BluetoothProfileController> controller = mController.forget();
-  controller->NotifyCompletion(aErrorStr);
-
-  Reset();
-}
-
-/* HandleSinkPropertyChanged update sink state in A2dp
- *
- * Possible values: "disconnected", "connecting", "connected", "playing"
- *
- * 1. "disconnected" -> "connecting"
- *    Either an incoming or outgoing connection attempt ongoing
- * 2. "connecting" -> "disconnected"
- *    Connection attempt failed
- * 3. "connecting" -> "connected"
- *    Successfully connected
- * 4. "connected" -> "playing"
- *    Audio stream active
- * 5. "playing" -> "connected"
- *    Audio stream suspended
- * 6. "connected" -> "disconnected"
- *    "playing" -> "disconnected"
- *    Disconnected from local or the remote device
- */
-void
-BluetoothA2dpManager::HandleSinkPropertyChanged(const BluetoothSignal& aSignal)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aSignal.value().type() ==
-             BluetoothValue::TArrayOfBluetoothNamedValue);
-
-  BluetoothAddress address;
-  NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(StringToAddress(aSignal.path(), address)));
-
-  /**
-   * Update sink property only if
-   * - mDeviceAddress is empty (A2dp is disconnected), or
-   * - this property change is from the connected sink.
-   */
-  NS_ENSURE_TRUE_VOID(mDeviceAddress.IsCleared() || mDeviceAddress == address);
-
-  const InfallibleTArray<BluetoothNamedValue>& arr =
-    aSignal.value().get_ArrayOfBluetoothNamedValue();
-  MOZ_ASSERT(arr.Length() == 1);
-
-  /**
-   * There are three properties:
-   * - "State": a string
-   * - "Connected": a boolean value
-   * - "Playing": a boolean value
-   *
-   * Note that only "State" is handled in this function.
-   */
-
-  const nsString& name = arr[0].name();
-  NS_ENSURE_TRUE_VOID(name.EqualsLiteral("State"));
-
-  const BluetoothValue& value = arr[0].value();
-  MOZ_ASSERT(value.type() == BluetoothValue::TnsString);
-  SinkState newState = StatusStringToSinkState(value.get_nsString());
-  NS_ENSURE_TRUE_VOID((newState != SinkState::SINK_UNKNOWN) &&
-                      (newState != mSinkState));
-
-  SinkState prevState = mSinkState;
-  mSinkState = newState;
-
-  switch(mSinkState) {
-    case SinkState::SINK_CONNECTING:
-      // case 1: Either an incoming or outgoing connection attempt ongoing
-      MOZ_ASSERT(prevState == SinkState::SINK_DISCONNECTED);
-      break;
-    case SinkState::SINK_PLAYING:
-      // case 4: Audio stream active
-      MOZ_ASSERT(prevState == SinkState::SINK_CONNECTED);
-      break;
-    case SinkState::SINK_CONNECTED:
-      // case 5: Audio stream suspended
-      if (prevState == SinkState::SINK_PLAYING ||
-          prevState == SinkState::SINK_CONNECTED) {
-        break;
-      }
-
-      // case 3: Successfully connected
-      mA2dpConnected = true;
-      mDeviceAddress = address;
-      NotifyConnectionStatusChanged();
-
-      OnConnect(EmptyString());
-      break;
-    case SinkState::SINK_DISCONNECTED:
-      // case 2: Connection attempt failed
-      if (prevState == SinkState::SINK_CONNECTING) {
-        OnConnect(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
-        break;
-      }
-
-      // case 6: Disconnected from the remote device
-      MOZ_ASSERT(prevState == SinkState::SINK_CONNECTED ||
-                 prevState == SinkState::SINK_PLAYING) ;
-
-      mA2dpConnected = false;
-      NotifyConnectionStatusChanged();
-      mDeviceAddress.Clear();
-      OnDisconnect(EmptyString());
-      break;
-    default:
-      break;
-  }
-}
-
-/*
- * Reset connection state to DISCONNECTED to handle backend error. The state
- * change triggers UI status bar update as ordinary bluetooth turn-off sequence.
- */
-void
-BluetoothA2dpManager::HandleBackendError()
-{
-  if (mSinkState != SinkState::SINK_DISCONNECTED) {
-    ConnectionStateNotification(A2DP_CONNECTION_STATE_DISCONNECTED,
-                                mDeviceAddress);
-  }
-}
-
-void
-BluetoothA2dpManager::NotifyConnectionStatusChanged()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // Notify Gecko observers
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  NS_ENSURE_TRUE_VOID(obs);
-
-  nsAutoString deviceAddressStr;
-  AddressToString(mDeviceAddress, deviceAddressStr);
-
-  if (NS_FAILED(obs->NotifyObservers(this,
-                                     BLUETOOTH_A2DP_STATUS_CHANGED_ID,
-                                     deviceAddressStr.get()))) {
-    BT_WARNING("Failed to notify bluetooth-a2dp-status-changed observsers!");
-  }
-
-  // Dispatch an event of status change
-  DispatchStatusChangedEvent(
-    NS_LITERAL_STRING(A2DP_STATUS_CHANGED_ID), mDeviceAddress, mA2dpConnected);
-}
-
-void
-BluetoothA2dpManager::OnGetServiceChannel(const BluetoothAddress& aDeviceAddress,
-                                          const BluetoothUuid& aServiceUuid,
-                                          int aChannel)
-{
-}
-
-void
-BluetoothA2dpManager::OnUpdateSdpRecords(const BluetoothAddress& aDeviceAddress)
-{
-}
-
-void
-BluetoothA2dpManager::GetAddress(BluetoothAddress& aDeviceAddress)
-{
-  aDeviceAddress = mDeviceAddress;
-}
-
-bool
-BluetoothA2dpManager::IsConnected()
-{
-  return mA2dpConnected;
-}
-
-/*
- * Notifications
- */
-
-void
-BluetoothA2dpManager::ConnectionStateNotification(
-  BluetoothA2dpConnectionState aState, const BluetoothAddress& aBdAddr)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsString a2dpState;
-  AvStatusToSinkString(aState, a2dpState);
-
-  InfallibleTArray<BluetoothNamedValue> props;
-  AppendNamedValue(props, "State", a2dpState);
-
-  nsAutoString addressStr;
-  AddressToString(aBdAddr, addressStr);
-
-  HandleSinkPropertyChanged(BluetoothSignal(NS_LITERAL_STRING("AudioSink"),
-                                            addressStr, props));
-}
-
-void
-BluetoothA2dpManager::AudioStateNotification(BluetoothA2dpAudioState aState,
-                                             const BluetoothAddress& aBdAddr)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsString a2dpState;
-
-  if (aState == A2DP_AUDIO_STATE_STARTED) {
-    a2dpState = NS_LITERAL_STRING("playing");
-  } else if (aState == A2DP_AUDIO_STATE_STOPPED) {
-    // for avdtp state stop stream
-    a2dpState = NS_LITERAL_STRING("connected");
-  } else if (aState == A2DP_AUDIO_STATE_REMOTE_SUSPEND) {
-    // for avdtp state suspend stream from remote side
-    a2dpState = NS_LITERAL_STRING("connected");
-  }
-
-  InfallibleTArray<BluetoothNamedValue> props;
-  AppendNamedValue(props, "State", a2dpState);
-
-  nsAutoString addressStr;
-  AddressToString(aBdAddr, addressStr);
-
-  HandleSinkPropertyChanged(BluetoothSignal(NS_LITERAL_STRING("AudioSink"),
-                                            addressStr, props));
-}
-
-NS_IMPL_ISUPPORTS(BluetoothA2dpManager, nsIObserver)
deleted file mode 100644
--- a/dom/bluetooth/bluedroid/BluetoothA2dpManager.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_bluetooth_bluedroid_BluetoothA2dpManager_h
-#define mozilla_dom_bluetooth_bluedroid_BluetoothA2dpManager_h
-
-#include "BluetoothCommon.h"
-#include "BluetoothInterface.h"
-#include "BluetoothProfileController.h"
-#include "BluetoothProfileManagerBase.h"
-
-BEGIN_BLUETOOTH_NAMESPACE
-class BluetoothA2dpManager : public BluetoothProfileManagerBase
-                           , public BluetoothA2dpNotificationHandler
-{
-public:
-  static const int MAX_NUM_CLIENTS;
-
-  BT_DECL_PROFILE_MGR_BASE
-  virtual void GetName(nsACString& aName)
-  {
-    aName.AssignLiteral("A2DP");
-  }
-
-  enum SinkState {
-    SINK_UNKNOWN,
-    SINK_DISCONNECTED,
-    SINK_CONNECTING,
-    SINK_CONNECTED,
-    SINK_PLAYING,
-  };
-
-  static BluetoothA2dpManager* Get();
-  static void InitA2dpInterface(BluetoothProfileResultHandler* aRes);
-  static void DeinitA2dpInterface(BluetoothProfileResultHandler* aRes);
-
-  void OnConnectError();
-  void OnDisconnectError();
-
-  // A2DP-specific functions
-  void HandleSinkPropertyChanged(const BluetoothSignal& aSignal);
-
-  void HandleBackendError();
-
-protected:
-  virtual ~BluetoothA2dpManager();
-
-private:
-  class ConnectResultHandler;
-  class DeinitProfileResultHandlerRunnable;
-  class DisconnectResultHandler;
-  class InitProfileResultHandlerRunnable;
-  class RegisterModuleResultHandler;
-  class UnregisterModuleResultHandler;
-
-  BluetoothA2dpManager();
-
-  void Uninit();
-  void HandleShutdown();
-  void NotifyConnectionStatusChanged();
-
-  void ConnectionStateNotification(BluetoothA2dpConnectionState aState,
-                                   const BluetoothAddress& aBdAddr) override;
-  void AudioStateNotification(BluetoothA2dpAudioState aState,
-                              const BluetoothAddress& aBdAddr) override;
-
-  BluetoothAddress mDeviceAddress;
-  RefPtr<BluetoothProfileController> mController;
-
-  // A2DP data member
-  bool mA2dpConnected;
-  SinkState mSinkState;
-};
-
-END_BLUETOOTH_NAMESPACE
-
-#endif
deleted file mode 100644
--- a/dom/bluetooth/bluedroid/BluetoothAvrcpManager.cpp
+++ /dev/null
@@ -1,951 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "base/basictypes.h"
-
-#include "BluetoothAvrcpManager.h"
-#include "BluetoothCommon.h"
-#include "BluetoothService.h"
-#include "BluetoothSocket.h"
-#include "BluetoothUtils.h"
-
-#include "mozilla/dom/bluetooth/BluetoothTypes.h"
-#include "mozilla/Services.h"
-#include "mozilla/StaticPtr.h"
-#include "mozilla/UniquePtr.h"
-#include "MainThreadUtils.h"
-#include "nsIObserverService.h"
-#include "nsThreadUtils.h"
-
-using namespace mozilla;
-USING_BLUETOOTH_NAMESPACE
-// AVRC_ID op code follows bluedroid avrc_defs.h
-#define AVRC_ID_REWIND  0x48
-#define AVRC_ID_FAST_FOR 0x49
-#define AVRC_KEY_PRESS_STATE  1
-#define AVRC_KEY_RELEASE_STATE  0
-// bluedroid bt_rc.h
-#define AVRC_MAX_ATTR_STR_LEN 255
-
-namespace {
-  StaticRefPtr<BluetoothAvrcpManager> sBluetoothAvrcpManager;
-  bool sInShutdown = false;
-  static BluetoothAvrcpInterface* sBtAvrcpInterface;
-} // namespace
-
-const int BluetoothAvrcpManager::MAX_NUM_CLIENTS = 1;
-
-/*
- * This function maps attribute id and returns corresponding values
- */
-static void
-ConvertAttributeString(BluetoothAvrcpMediaAttribute aAttrId,
-                       nsAString& aAttrStr)
-{
-  BluetoothAvrcpManager* avrcp = BluetoothAvrcpManager::Get();
-  NS_ENSURE_TRUE_VOID(avrcp);
-
-  switch (aAttrId) {
-    case AVRCP_MEDIA_ATTRIBUTE_TITLE:
-      avrcp->GetTitle(aAttrStr);
-      /*
-       * bluedroid can only send string length AVRC_MAX_ATTR_STR_LEN - 1
-       */
-      if (aAttrStr.Length() >= AVRC_MAX_ATTR_STR_LEN) {
-        aAttrStr.Truncate(AVRC_MAX_ATTR_STR_LEN - 1);
-        BT_WARNING("Truncate media item attribute title, length is over 255");
-      }
-      break;
-    case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
-      avrcp->GetArtist(aAttrStr);
-      if (aAttrStr.Length() >= AVRC_MAX_ATTR_STR_LEN) {
-        aAttrStr.Truncate(AVRC_MAX_ATTR_STR_LEN - 1);
-        BT_WARNING("Truncate media item attribute artist, length is over 255");
-      }
-      break;
-    case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
-      avrcp->GetAlbum(aAttrStr);
-      if (aAttrStr.Length() >= AVRC_MAX_ATTR_STR_LEN) {
-        aAttrStr.Truncate(AVRC_MAX_ATTR_STR_LEN - 1);
-        BT_WARNING("Truncate media item attribute album, length is over 255");
-      }
-      break;
-    case AVRCP_MEDIA_ATTRIBUTE_TRACK_NUM:
-      aAttrStr.AppendInt(avrcp->GetMediaNumber());
-      break;
-    case AVRCP_MEDIA_ATTRIBUTE_NUM_TRACKS:
-      aAttrStr.AppendInt(avrcp->GetTotalMediaNumber());
-      break;
-    case AVRCP_MEDIA_ATTRIBUTE_GENRE:
-      // TODO: we currently don't support genre from music player
-      aAttrStr.Truncate();
-      break;
-    case AVRCP_MEDIA_ATTRIBUTE_PLAYING_TIME:
-      aAttrStr.AppendInt(avrcp->GetDuration());
-      break;
-  }
-}
-
-NS_IMETHODIMP
-BluetoothAvrcpManager::Observe(nsISupports* aSubject,
-                               const char* aTopic,
-                               const char16_t* aData)
-{
-  MOZ_ASSERT(sBluetoothAvrcpManager);
-
-  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
-    HandleShutdown();
-    return NS_OK;
-  }
-
-  MOZ_ASSERT(false, "BluetoothAvrcpManager got unexpected topic!");
-  return NS_ERROR_UNEXPECTED;
-}
-
-BluetoothAvrcpManager::BluetoothAvrcpManager()
-{
-  Reset();
-}
-
-void
-BluetoothAvrcpManager::Reset()
-{
-  mAvrcpConnected = false;
-  mDuration = 0;
-  mMediaNumber = 0;
-  mTotalMediaCount = 0;
-  mPosition = 0;
-  mPlayStatus = ControlPlayStatus::PLAYSTATUS_STOPPED;
-}
-
-class BluetoothAvrcpManager::RegisterModuleResultHandler final
-  : public BluetoothSetupResultHandler
-{
-public:
-  RegisterModuleResultHandler(BluetoothAvrcpInterface* aInterface,
-                              BluetoothProfileResultHandler* aRes)
-    : mInterface(aInterface)
-    , mRes(aRes)
-  {
-    MOZ_ASSERT(mInterface);
-  }
-
-  void OnError(BluetoothStatus aStatus) override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    BT_WARNING("BluetoothSetupInterface::RegisterModule failed for AVRCP: %d",
-               (int)aStatus);
-
-    mInterface->SetNotificationHandler(nullptr);
-
-    if (mRes) {
-      if (aStatus == STATUS_UNSUPPORTED) {
-        /* Not all versions of Bluedroid support AVRCP. So if the
-         * initialization fails with STATUS_UNSUPPORTED, we still
-         * signal success.
-         */
-        mRes->Init();
-      } else {
-        mRes->OnError(NS_ERROR_FAILURE);
-      }
-    }
-  }
-
-  void RegisterModule() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    sBtAvrcpInterface = mInterface;
-
-    if (mRes) {
-      mRes->Init();
-    }
-  }
-
-private:
-  BluetoothAvrcpInterface* mInterface;
-  RefPtr<BluetoothProfileResultHandler> mRes;
-};
-
-class BluetoothAvrcpManager::InitProfileResultHandlerRunnable final
-  : public Runnable
-{
-public:
-  InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
-                                   nsresult aRv)
-    : mRes(aRes)
-    , mRv(aRv)
-  {
-    MOZ_ASSERT(mRes);
-  }
-
-  NS_IMETHOD Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (NS_SUCCEEDED(mRv)) {
-      mRes->Init();
-    } else {
-      mRes->OnError(mRv);
-    }
-    return NS_OK;
-  }
-
-private:
-  RefPtr<BluetoothProfileResultHandler> mRes;
-  nsresult mRv;
-};
-
-/*
- * This function will be only called when Bluetooth is turning on.
- */
-// static
-void
-BluetoothAvrcpManager::InitAvrcpInterface(BluetoothProfileResultHandler* aRes)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (sBtAvrcpInterface) {
-    BT_LOGR("Bluetooth AVRCP interface is already initalized.");
-    RefPtr<Runnable> r =
-      new InitProfileResultHandlerRunnable(aRes, NS_OK);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch AVRCP Init runnable");
-    }
-    return;
-  }
-
-  auto btInf = BluetoothInterface::GetInstance();
-
-  if (NS_WARN_IF(!btInf)) {
-    // If there's no Bluetooth interface, we dispatch a runnable
-    // that calls the profile result handler.
-    RefPtr<Runnable> r =
-      new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch AVRCP OnError runnable");
-    }
-    return;
-  }
-
-  auto setupInterface = btInf->GetBluetoothSetupInterface();
-
-  if (NS_WARN_IF(!setupInterface)) {
-    // If there's no Setup interface, we dispatch a runnable
-    // that calls the profile result handler.
-    RefPtr<Runnable> r =
-      new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch AVRCP OnError runnable");
-    }
-    return;
-  }
-
-  auto avrcpInterface = btInf->GetBluetoothAvrcpInterface();
-
-  if (NS_WARN_IF(!avrcpInterface)) {
-    // If there's no AVRCP interface, we dispatch a runnable
-    // that calls the profile result handler.
-    RefPtr<Runnable> r =
-      new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch AVRCP OnError runnable");
-    }
-    return;
-  }
-
-  // Set notification handler _before_ registering the module. It could
-  // happen that we receive notifications, before the result handler runs.
-  avrcpInterface->SetNotificationHandler(BluetoothAvrcpManager::Get());
-
-  setupInterface->RegisterModule(
-    SETUP_SERVICE_ID_AVRCP, 0, MAX_NUM_CLIENTS,
-    new RegisterModuleResultHandler(avrcpInterface, aRes));
-}
-
-BluetoothAvrcpManager::~BluetoothAvrcpManager()
-{ }
-
-void
-BluetoothAvrcpManager::Uninit()
-{
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  NS_ENSURE_TRUE_VOID(obs);
-  if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
-    BT_WARNING("Failed to remove shutdown observer!");
-  }
-}
-
-/*
- * Static functions
- */
-
-//static
-BluetoothAvrcpManager*
-BluetoothAvrcpManager::Get()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // If sBluetoothAvrcpManager already exists, exit early
-  if (sBluetoothAvrcpManager) {
-    return sBluetoothAvrcpManager;
-  }
-
-  // If we're in shutdown, don't create a new instance
-  NS_ENSURE_FALSE(sInShutdown, nullptr);
-
-  // Create a new instance and return
-  sBluetoothAvrcpManager = new BluetoothAvrcpManager();
-
-  return sBluetoothAvrcpManager;
-}
-
-class BluetoothAvrcpManager::UnregisterModuleResultHandler final
-  : public BluetoothSetupResultHandler
-{
-public:
-  UnregisterModuleResultHandler(BluetoothProfileResultHandler* aRes)
-    : mRes(aRes)
-  { }
-
-  void OnError(BluetoothStatus aStatus) override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    BT_WARNING("BluetoothSetupInterface::UnregisterModule failed for AVRCP: %d",
-               (int)aStatus);
-
-    sBtAvrcpInterface->SetNotificationHandler(nullptr);
-    sBtAvrcpInterface = nullptr;
-
-    sBluetoothAvrcpManager->Uninit();
-    sBluetoothAvrcpManager = nullptr;
-
-    if (mRes) {
-      mRes->OnError(NS_ERROR_FAILURE);
-    }
-  }
-
-  void UnregisterModule() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    sBtAvrcpInterface->SetNotificationHandler(nullptr);
-    sBtAvrcpInterface = nullptr;
-
-    sBluetoothAvrcpManager->Uninit();
-    sBluetoothAvrcpManager = nullptr;
-
-    if (mRes) {
-      mRes->Deinit();
-    }
-  }
-
-private:
-  RefPtr<BluetoothProfileResultHandler> mRes;
-};
-
-class BluetoothAvrcpManager::DeinitProfileResultHandlerRunnable final
-  : public Runnable
-{
-public:
-  DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
-                                     nsresult aRv)
-    : mRes(aRes)
-    , mRv(aRv)
-  {
-    MOZ_ASSERT(mRes);
-  }
-
-  NS_IMETHOD Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (NS_SUCCEEDED(mRv)) {
-      mRes->Deinit();
-    } else {
-      mRes->OnError(mRv);
-    }
-    return NS_OK;
-  }
-
-private:
-  RefPtr<BluetoothProfileResultHandler> mRes;
-  nsresult mRv;
-};
-
-// static
-void
-BluetoothAvrcpManager::DeinitAvrcpInterface(BluetoothProfileResultHandler* aRes)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!sBtAvrcpInterface) {
-    BT_LOGR("Bluetooth AVRCP interface has not been initalized.");
-    RefPtr<Runnable> r =
-      new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch AVRCP Deinit runnable");
-    }
-    return;
-  }
-
-  auto btInf = BluetoothInterface::GetInstance();
-
-  if (NS_WARN_IF(!btInf)) {
-    // If there's no backend interface, we dispatch a runnable
-    // that calls the profile result handler.
-    RefPtr<Runnable> r =
-      new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch AVRCP OnError runnable");
-    }
-    return;
-  }
-
-  auto setupInterface = btInf->GetBluetoothSetupInterface();
-
-  if (NS_WARN_IF(!setupInterface)) {
-    // If there's no Setup interface, we dispatch a runnable
-    // that calls the profile result handler.
-    RefPtr<Runnable> r =
-      new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
-    if (NS_FAILED(NS_DispatchToMainThread(r))) {
-      BT_LOGR("Failed to dispatch AVRCP OnError runnable");
-    }
-    return;
-  }
-
-  setupInterface->UnregisterModule(
-    SETUP_SERVICE_ID_AVRCP,
-    new UnregisterModuleResultHandler(aRes));
-}
-
-void
-BluetoothAvrcpManager::HandleShutdown()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  sInShutdown = true;
-  Disconnect(nullptr);
-  sBluetoothAvrcpManager = nullptr;
-}
-
-class BluetoothAvrcpManager::ConnectRunnable final : public Runnable
-{
-public:
-  ConnectRunnable(BluetoothAvrcpManager* aManager)
-    : mManager(aManager)
-  {
-    MOZ_ASSERT(mManager);
-  }
-  NS_IMETHOD Run() override
-  {
-    mManager->OnConnect(EmptyString());
-    return NS_OK;
-  }
-private:
-  BluetoothAvrcpManager* mManager;
-};
-
-void
-BluetoothAvrcpManager::Connect(const BluetoothAddress& aDeviceAddress,
-                               BluetoothProfileController* aController)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!aDeviceAddress.IsCleared());
-  MOZ_ASSERT(aController);
-
-  // AVRCP doesn't require connecting. We just set the remote address here.
-  mDeviceAddress = aDeviceAddress;
-  mController = aController;
-  SetConnected(true);
</