Merge m-c to oak.
authorStephen A Pohl <spohl.mozilla.bugs@gmail.com>
Thu, 30 Jul 2015 15:45:14 -0400
changeset 491463 e0a7687480ca0ee7face625361a6b1bcecb354a7
parent 491462 b5a0bfbf3930e58a074cdc1c514073883e0058a1 (current diff)
parent 281947 a2d31912a7a983e63ea5307eff57828ccc911356 (diff)
child 491464 007f2a7df4837f6d19b0b72e5f86675ec8aaabc4
push id47343
push userbmo:dothayer@mozilla.com
push dateWed, 01 Mar 2017 22:58:58 +0000
milestone42.0a1
Merge m-c to oak.
CLOBBER
b2g/installer/Makefile.in
b2g/installer/package-manifest.in
browser/app/profile/firefox.js
browser/base/content/test/general/browser_bug405137.js
browser/config/mozconfigs/linux32/beta
browser/config/mozconfigs/linux32/release
browser/config/mozconfigs/linux64/beta
browser/config/mozconfigs/linux64/release
browser/config/mozconfigs/macosx-universal/beta
browser/config/mozconfigs/macosx-universal/release
browser/config/mozconfigs/win32/beta
browser/config/mozconfigs/win32/release
browser/config/mozconfigs/win64/beta
browser/config/mozconfigs/win64/release
browser/devtools/styleinspector/test/browser_ruleview_override.js
browser/installer/package-manifest.in
browser/locales/en-US/chrome/browser/tabbrowser.dtd
configure.in
dom/animation/Animation.cpp
dom/animation/Animation.h
dom/base/test/iframe_cloning_fileList.html
dom/base/test/script_cloning_fileList.js
dom/base/test/test_cloning_fileList.html
dom/icc/interfaces/nsIIccProvider.idl
dom/messagechannel/MessagePortUtils.cpp
dom/messagechannel/MessagePortUtils.h
dom/mobileconnection/gonk/MobileConnectionService.js
dom/system/gonk/RILContentHelper.js
dom/system/gonk/RILContentHelper.manifest
dom/system/gonk/nsRadioInterfaceLayer.h
ipc/glue/GeckoChildProcessHost.cpp
mobile/android/base/resources/drawable-hdpi/lock_identified.png
mobile/android/base/resources/drawable-hdpi/lock_verified.png
mobile/android/base/resources/drawable-xhdpi/lock_identified.png
mobile/android/base/resources/drawable-xhdpi/lock_verified.png
mobile/android/base/resources/drawable-xxhdpi/lock_identified.png
mobile/android/base/resources/layout/site_identity_unknown.xml
python/mozbuild/mozbuild/base.py
security/manager/ssl/tests/mochitest/bugs/mochitest.ini
security/manager/ssl/tests/mochitest/bugs/test_bug480509.html
security/manager/ssl/tests/unit/test_ev_certs/cert9.db
security/manager/ssl/tests/unit/test_ev_certs/ev-valid-anypolicy-int.der
security/manager/ssl/tests/unit/test_ev_certs/ev-valid.der
security/manager/ssl/tests/unit/test_ev_certs/ev_root_generate.py
security/manager/ssl/tests/unit/test_ev_certs/evroot.csr
security/manager/ssl/tests/unit/test_ev_certs/evroot.der
security/manager/ssl/tests/unit/test_ev_certs/evroot.key
security/manager/ssl/tests/unit/test_ev_certs/evroot.p12
security/manager/ssl/tests/unit/test_ev_certs/generate.py
security/manager/ssl/tests/unit/test_ev_certs/int-ev-valid-anypolicy-int.der
security/manager/ssl/tests/unit/test_ev_certs/int-ev-valid.der
security/manager/ssl/tests/unit/test_ev_certs/int-non-ev-root.der
security/manager/ssl/tests/unit/test_ev_certs/key4.db
security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-url-cert.der
security/manager/ssl/tests/unit/test_ev_certs/non-ev-root.der
security/manager/ssl/tests/unit/test_ev_certs/non-evroot-ca.der
security/manager/ssl/tests/unit/test_ev_certs/pkcs11.txt
security/manager/ssl/tests/unit/test_keysize/ev_ee_rsa_2040-ev_int_rsa_2048-evroot.der
security/manager/ssl/tests/unit/test_keysize/ev_ee_rsa_2048-ev_int_rsa_2040-evroot.der
security/manager/ssl/tests/unit/test_keysize/ev_ee_rsa_2048-ev_int_rsa_2048-ev_root_rsa_2040.der
security/manager/ssl/tests/unit/test_keysize/ev_ee_rsa_2048-ev_int_rsa_2048-evroot.der
security/manager/ssl/tests/unit/test_keysize/ev_int_rsa_2040-evroot.der
security/manager/ssl/tests/unit/test_keysize/ev_int_rsa_2048-ev_root_rsa_2040.der
security/manager/ssl/tests/unit/test_keysize/ev_int_rsa_2048-evroot.der
security/manager/ssl/tests/unit/test_keysize/ev_root_rsa_2040.der
security/manager/ssl/tests/unit/test_validity/cert9.db
security/manager/ssl/tests/unit/test_validity/ev_ee_39_months-ev_int_60_months-evroot.der
security/manager/ssl/tests/unit/test_validity/ev_ee_40_months-ev_int_60_months-evroot.der
security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.der
security/manager/ssl/tests/unit/test_validity/generate_ev.py
security/manager/ssl/tests/unit/test_validity/key4.db
security/manager/ssl/tests/unit/test_validity/pkcs11.txt
testing/web-platform/meta/IndexedDB/idbcursor_delete_index5.htm.ini
testing/web-platform/meta/IndexedDB/idbcursor_delete_objectstore5.htm.ini
testing/web-platform/meta/workers/Worker_ErrorEvent_bubbles_cancelable.htm.ini
toolkit/components/telemetry/Histograms.json
toolkit/components/url-classifier/PrivateBrowsingTrackingProtectionWhitelist.js
toolkit/components/url-classifier/nsIPrivateBrowsingTrackingProtectionWhitelist.idl
toolkit/devtools/DevToolsUtils.jsm
toolkit/mozapps/installer/packager.mk
toolkit/mozapps/installer/upload-files.mk
toolkit/mozapps/update/nsUpdateService.js
xpcom/base/nsRefPtr.h
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -14,16 +14,18 @@ namespace mozilla {
 namespace a11y {
 
 bool
 DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
 {
   if (mShutdown)
     return true;
 
+  CheckDocTree();
+
   if (aData.NewTree().IsEmpty()) {
     NS_ERROR("no children being added");
     return false;
   }
 
   ProxyAccessible* parent = GetAccessible(aData.ID());
 
   // XXX This should really never happen, but sometimes we fail to fire the
@@ -43,16 +45,18 @@ DocAccessibleParent::RecvShowEvent(const
   MOZ_ASSERT(consumed == aData.NewTree().Length());
 #ifdef DEBUG
   for (uint32_t i = 0; i < consumed; i++) {
     uint64_t id = aData.NewTree()[i].ID();
     MOZ_ASSERT(mAccessibles.GetEntry(id));
   }
 #endif
 
+  CheckDocTree();
+
   return true;
 }
 
 uint32_t
 DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
                                 const nsTArray<a11y::AccessibleData>& aNewTree,
                                 uint32_t aIdx, uint32_t aIdxInParent)
 {
@@ -113,16 +117,18 @@ DocAccessibleParent::RecvHideEvent(const
     NS_ERROR("invalid root being removed!");
     return true;
   }
 
   ProxyAccessible* parent = root->Parent();
   parent->RemoveChild(root);
   root->Shutdown();
 
+  CheckDocTree();
+
   return true;
 }
 
 bool
 DocAccessibleParent::RecvEvent(const uint64_t& aID, const uint32_t& aEventType)
 {
   ProxyAccessible* proxy = GetAccessible(aID);
   if (!proxy) {
@@ -185,19 +191,22 @@ bool
 DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID)
 {
   // One document should never directly be the child of another.
   // We should always have at least an outer doc accessible in between.
   MOZ_ASSERT(aID);
   if (!aID)
     return false;
 
+  CheckDocTree();
+
   auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc);
   bool result = AddChildDoc(childDoc, aID, false);
   MOZ_ASSERT(result);
+  CheckDocTree();
   return result;
 }
 
 bool
 DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
                                  uint64_t aParentID, bool aCreating)
 {
   // We do not use GetAccessible here because we want to be sure to not get the
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -44,16 +44,20 @@ a11y::ProxyCreated(ProxyAccessible* aPro
   aProxy->SetWrapper(reinterpret_cast<uintptr_t>(wrapper));
 }
 
 void
 a11y::ProxyDestroyed(ProxyAccessible* aProxy)
 {
   ProxyAccessibleWrap* wrapper =
     reinterpret_cast<ProxyAccessibleWrap*>(aProxy->GetWrapper());
+  MOZ_ASSERT(wrapper);
+  if (!wrapper)
+    return;
+
   wrapper->Shutdown();
   aProxy->SetWrapper(0);
   wrapper->Release();
 }
 
 void
 a11y::ProxyEvent(ProxyAccessible*, uint32_t)
 {
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -622,19 +622,17 @@ pref("app.update.socket.retryTimeout", 3
 // Note: Offline errors will always retry when the network comes online.
 pref("app.update.socket.maxErrors", 20);
 
 // Enable update logging for now, to diagnose growing pains in the
 // field.
 pref("app.update.log", true);
 
 // SystemUpdate API
-#ifdef MOZ_WIDGET_GONK
 pref("dom.system_update.active", "@mozilla.org/updates/update-prompt;1");
-#endif
 #else
 // Explicitly disable the shutdown watchdog.  It's enabled by default.
 // When the updater is disabled, we want to know about shutdown hangs.
 pref("shutdown.watchdog.timeoutSecs", -1);
 #endif
 
 // Allow webapps update checking
 pref("webapps.update.enabled", true);
@@ -672,19 +670,16 @@ pref("dom.disable_window_showModalDialog
 // Enable new experimental html forms
 pref("dom.experimental_forms", true);
 pref("dom.forms.number", true);
 
 // Don't enable <input type=color> yet as we don't have a color picker
 // implemented for b2g (bug 875751)
 pref("dom.forms.color", false);
 
-// Turns on gralloc-based direct texturing for Gonk
-pref("gfx.gralloc.enabled", false);
-
 // This preference instructs the JS engine to discard the
 // source of any privileged JS after compilation. This saves
 // memory, but makes things like Function.prototype.toSource()
 // fail.
 pref("javascript.options.discardSystemSource", true);
 
 // XXXX REMOVE FOR PRODUCTION. Turns on GC and CC logging
 pref("javascript.options.mem.log", false);
@@ -1095,21 +1090,24 @@ pref("gfx.canvas.willReadFrequently.enab
 pref("browser.autofocus", false);
 
 // Enable wakelock
 pref("dom.wakelock.enabled", true);
 
 // Enable webapps add-ons
 pref("dom.apps.customization.enabled", true);
 
-// Enable touch caret by default
-pref("touchcaret.enabled", true);
+// Original caret implementation on collapsed selection.
+pref("touchcaret.enabled", false);
 
-// Enable selection caret by default
-pref("selectioncaret.enabled", true);
+// Original caret implementation on non-collapsed selection.
+pref("selectioncaret.enabled", false);
+
+// New implementation to unify touch-caret and selection-carets.
+pref("layout.accessiblecaret.enabled", true);
 
 // Enable sync and mozId with Firefox Accounts.
 pref("services.sync.fxaccounts.enabled", true);
 pref("identity.fxaccounts.enabled", true);
 
 // Mobile Identity API.
 pref("services.mobileid.server.uri", "https://msisdn.services.mozilla.com");
 
@@ -1157,10 +1155,13 @@ pref("dom.activities.developer_mode_only
 
 // mulet apparently loads firefox.js as well as b2g.js, so we have to explicitly
 // disable serviceworkers here to get them disabled in mulet.
 pref("dom.serviceWorkers.enabled", false);
 
 // Retain at most 10 processes' layers buffers
 pref("layers.compositor-lru-size", 10);
 
+// Enable Cardboard VR on mobile, assuming VR at all is enabled
+pref("dom.vr.cardboard.enabled", true);
+
 // In B2G by deafult any AudioChannelAgent is muted when created.
 pref("dom.audiochannel.mutedByDefault", true);
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -196,19 +196,19 @@ var shell = {
     if (this.onlineForCrashReport()) {
       this.submitQueuedCrashes();
       return;
     }
 
     debugCrashReport('Not online, postponing.');
 
     Services.obs.addObserver(function observer(subject, topic, state) {
-      let network = subject.QueryInterface(Ci.nsINetworkInterface);
-      if (network.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED
-          && network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
+      let network = subject.QueryInterface(Ci.nsINetworkInfo);
+      if (network.state == Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED
+          && network.type == Ci.nsINetworkInfo.NETWORK_TYPE_WIFI) {
         shell.submitQueuedCrashes();
 
         Services.obs.removeObserver(observer, topic);
       }
     }, "network-connection-state-changed", false);
   },
 
   get homeURL() {
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="302a448729ff2b336581cf94b66327ea836294c7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d3595836bd55b70478923d771051268a5dabf91"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
@@ -117,38 +117,38 @@
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="42fa2a0f14f965970a4b629a176bbd2666edf017"/>
   <project name="platform/external/curl" path="external/curl" revision="e68addd988448959ea8157c5de637346b4180c33"/>
   <project name="platform/external/icu4c" path="external/icu4c" revision="d3ec7428eb276db43b7ed0544e09344a6014806c"/>
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="76c4bf4bc430a1b8317f2f21ef735867733e50cc"/>
   <project name="platform/system/media" path="system/media" revision="c1332c21c608f4932a6d7e83450411cde53315ef"/>
   <default remote="caf" revision="LNX.LA.3.5.2.1.1" sync-j="4"/>
   <!-- Platform common things -->
-  <project name="device-shinano-common" path="device/sony/shinano-common" remote="b2g" revision="24763a010cda411db9e24d8eb3dac2982e12f0de"/>
+  <project name="device-shinano-common" path="device/sony/shinano-common" remote="b2g" revision="df3d2ae4345ca5d32bce717bb7b3dac18a3afa18"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="1bb28abbc215f45220620af5cd60a8ac1be93722"/>
   <project name="device/qcom/common" path="device/qcom/common" revision="2501e5940ba69ece7654ff85611c76ae5bda299c"/>
   <project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="d620691cad7aee780018e98159ff03bf99840317"/>
   <project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="d61fc97258c8b0c362430dd2eb195dcc4d266f14"/>
   <project name="init_sh" path="external/init_sh" remote="b2g" revision="3bdd26e092db9c47c5beb04b4809a35f8f767b8a"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="0a01977f34d6e86fe23d6c0ec75e96ba988bbebb"/>
   <project name="platform_external_libnfc-pn547" path="external/libnfc-pn547" remote="b2g" revision="5bb999b84b8adc14f6bea004d523ba258dea8188"/>
-  <project name="timekeep" path="external/timekeep" remote="b2g" revision="4cbb0abc00681f116f043584661307b5c4855a31"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="5b71e40213f650459e95d35b6f14af7e88d8ab62"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="a920312eb6299b6cc11f7136254c4b0ba7a663be"/>
   <project name="platform/frameworks/base" path="frameworks/base" revision="da8e6bc53c8bc669da0bb627904d08aa293f2497"/>
   <project name="platform/frameworks/native" path="frameworks/native" revision="a46a9f1ac0ed5662d614c277cbb14eb3f332f365"/>
   <project name="platform/hardware/libhardware" path="hardware/libhardware" revision="7196881a0e9dd7bfbbcf0af64c8064e70f0fa094"/>
   <project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="15a9b66de9b7d84c7ea63df3a834f095bca9e493"/>
   <project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="8d7676dfb68ee0cd069affedd5d1e97316a184ba"/>
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="2a1ded216a91bf62a72b1640cf01ab4998f37028"/>
   <project name="hardware_qcom_display" path="hardware/qcom/display" remote="b2g" revision="4a83e04c3fecffbcab75cd59bad2ae5f342778b7"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="9883ea57b0668d8f60dba025d4522dfa69a1fbfa"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="a558dc844bf5144fc38603fd8f4df8d9557052a5"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="57ee1320ed7b4a1a1274d8f3f6c177cd6b9becb2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="12b1977cc704b35f2e9db2bb423fa405348bc2f3"/>
+  <project name="timekeep" path="hardware/sony/timekeep" remote="b2g" revision="4cbb0abc00681f116f043584661307b5c4855a31"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="985bf15264d865fe7b9c5b45f61c451cbaafa43d"/>
   <project name="platform/system/core" path="system/core" revision="42839aedcf70bf6bc92a3b7ea4a5cc9bf9aef3f9"/>
   <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="5f4b68c799927b6e078f987b12722c3a6ccd4a45"/>
   <project name="platform/system/qcom" path="system/qcom" revision="63e3f6f176caad587d42bba4c16b66d953fb23c2"/>
   <project name="platform/vendor/qcom/copper" path="device/qcom/msm8974" revision="ec7bc1a26610922156d7d412b4d3de6b4adb93da"/>
   <project name="vendor_broadcom_wlan" path="vendor/broadcom/wlan" remote="b2g" revision="114b9491a8a919687da4e22fbd89fab511d6d8d7"/>
   <!-- Shinano specific things -->
   <project name="device-shinano" path="device/sony/leo" remote="b2g" revision="653f7e1f093b948e40262fcb3c665c2b4976df74"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="302a448729ff2b336581cf94b66327ea836294c7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d3595836bd55b70478923d771051268a5dabf91"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -10,21 +10,21 @@
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
-  <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
+  <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="302a448729ff2b336581cf94b66327ea836294c7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d3595836bd55b70478923d771051268a5dabf91"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="302a448729ff2b336581cf94b66327ea836294c7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d3595836bd55b70478923d771051268a5dabf91"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="302a448729ff2b336581cf94b66327ea836294c7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d3595836bd55b70478923d771051268a5dabf91"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="07c383a786f188904311a37f6062c2cb84c9b61d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="302a448729ff2b336581cf94b66327ea836294c7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d3595836bd55b70478923d771051268a5dabf91"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
@@ -138,15 +138,15 @@
   <project name="platform_frameworks_base" path="frameworks/base" remote="b2g" revision="04e26ebdc36ca83f4ee3e9e2082b3fcf04c5b971"/>
   <project name="platform_frameworks_wilhelm" path="frameworks/wilhelm" remote="b2g" revision="0dbf5baafadf6d233c0a29e392fa3293f0121673"/>
   <project name="platform_system_core" path="system/core" remote="b2g" revision="f594bc64eacac490857748b1139ffcb34c856bbd"/>
   <default remote="caf" revision="refs/tags/android-5.1.0_r1" sync-j="4"/>
   <!-- Emulator specific things -->
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="fe7df1bc8dd0fd71571505d7be1c31a4ad1e40fb"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="48132ec0b0dfe9fc29c7c3f0e799066be8999198"/>
   <!-- external/qemu for emulator-l need to be updated in bug-1121378 -->
-  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="85f91439c854061bda3c6228d98381ea8867170c"/>
+  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="59e434cbecc02653f44cedeb2ef5cc88dc8bb61b"/>
   <project name="platform_external_wpa_supplicant_8" path="external/wpa_supplicant_8" remote="b2g" revision="cbda29a58abc4ea1f7f4611fe354ab67b606219d"/>
   <project name="platform/development" path="development" revision="0c51f6e0aa2ee57fcb75ec3b2ff6bf754cece63e"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="ff4190dc603f62a7caa48342aa268acf99863c5c"/>
   <!-- hardware-ril for emulator-l need to be updated in bug-1113054 -->
   <project name="platform/hardware/ril" path="hardware/ril" revision="e00d716e7e3d31729f75399855b6921e90cb0b66"/>
 </manifest>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -10,21 +10,21 @@
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
-  <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
+  <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="302a448729ff2b336581cf94b66327ea836294c7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="1d3595836bd55b70478923d771051268a5dabf91"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="302a448729ff2b336581cf94b66327ea836294c7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d3595836bd55b70478923d771051268a5dabf91"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "302a448729ff2b336581cf94b66327ea836294c7", 
+        "git_revision": "1d3595836bd55b70478923d771051268a5dabf91", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "6b3d9ecc195e6fac9fd71a17b4a0d5d0fcac9093", 
+    "revision": "552e56f79bba1e2f7cd392361dfb0a8552176be8", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/mozconfigs/linux32_gecko/debug
+++ b/b2g/config/mozconfigs/linux32_gecko/debug
@@ -1,16 +1,14 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
 MOZ_AUTOMATION_UPDATE_PACKAGING=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 . "$topsrcdir/build/unix/mozconfig.linux32"
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 ac_add_options --enable-debug
 
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
--- a/b2g/config/mozconfigs/linux32_gecko/nightly
+++ b/b2g/config/mozconfigs/linux32_gecko/nightly
@@ -1,17 +1,15 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
 MOZ_AUTOMATION_UPDATE_PACKAGING=0
 MOZ_AUTOMATION_SDK=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 . "$topsrcdir/build/unix/mozconfig.linux32"
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
--- a/b2g/config/mozconfigs/linux64_gecko/debug
+++ b/b2g/config/mozconfigs/linux64_gecko/debug
@@ -1,16 +1,14 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
 MOZ_AUTOMATION_UPDATE_PACKAGING=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 . "$topsrcdir/build/unix/mozconfig.linux"
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 ac_add_options --enable-debug
 
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
--- a/b2g/config/mozconfigs/linux64_gecko/nightly
+++ b/b2g/config/mozconfigs/linux64_gecko/nightly
@@ -1,17 +1,15 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
 MOZ_AUTOMATION_UPDATE_PACKAGING=0
 MOZ_AUTOMATION_SDK=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 . "$topsrcdir/build/unix/mozconfig.linux"
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="302a448729ff2b336581cf94b66327ea836294c7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d3595836bd55b70478923d771051268a5dabf91"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="07c383a786f188904311a37f6062c2cb84c9b61d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="302a448729ff2b336581cf94b66327ea836294c7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="1d3595836bd55b70478923d771051268a5dabf91"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/dev/config/mozconfigs/linux64/mulet
+++ b/b2g/dev/config/mozconfigs/linux64/mulet
@@ -1,11 +1,10 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
 MOZ_AUTOMATION_UPDATE_PACKAGING=0
 MOZ_AUTOMATION_SDK=0
 . "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
 ac_add_options --enable-application=b2g/dev
 
 # Include Firefox OS fonts.
 MOZTTDIR=$topsrcdir/moz-tt
--- a/b2g/installer/Makefile.in
+++ b/b2g/installer/Makefile.in
@@ -93,19 +93,27 @@ endif
 ifneq (,$(filter rtsp,$(NECKO_PROTOCOLS)))
 DEFINES += -DMOZ_RTSP
 endif
 
 ifdef GKMEDIAS_SHARED_LIBRARY
 DEFINES += -DGKMEDIAS_SHARED_LIBRARY
 endif
 
+DEFINES += -DMOZ_ICU_VERSION=$(MOZ_ICU_VERSION)
+ifdef MOZ_NATIVE_ICU
+DEFINES += -DMOZ_NATIVE_ICU
+endif
+ifdef MOZ_SHARED_ICU
+DEFINES += -DMOZ_SHARED_ICU
+endif
 ifdef MOZ_JEMALLOC3
 DEFINES += -DMOZ_JEMALLOC3
 endif
+DEFINES += -DMOZ_ICU_DBG_SUFFIX=$(MOZ_ICU_DBG_SUFFIX)
 
 ifdef MOZ_WIDGET_GTK
 DEFINES += -DMOZ_GTK=1
 ifdef MOZ_ENABLE_GTK3
 DEFINES += -DMOZ_GTK3=1
 endif
 endif
 
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -89,16 +89,33 @@
 #ifdef MSVC_APPCRT_DLL
 @BINPATH@/@MSVC_APPCRT_DLL@
 #endif
 #ifdef MSVC_DESKTOPCRT_DLL
 @BINPATH@/@MSVC_DESKTOPCRT_DLL@
 #endif
 #endif
 #endif
+#ifndef MOZ_NATIVE_ICU
+#ifdef MOZ_SHARED_ICU
+#ifdef XP_WIN
+@BINPATH@/icudt@MOZ_ICU_DBG_SUFFIX@@MOZ_ICU_VERSION@.dll
+@BINPATH@/icuin@MOZ_ICU_DBG_SUFFIX@@MOZ_ICU_VERSION@.dll
+@BINPATH@/icuuc@MOZ_ICU_DBG_SUFFIX@@MOZ_ICU_VERSION@.dll
+#elif defined(XP_MACOSX)
+@BINPATH@/libicudata.@MOZ_ICU_VERSION@.dylib
+@BINPATH@/libicui18n.@MOZ_ICU_VERSION@.dylib
+@BINPATH@/libicuuc.@MOZ_ICU_VERSION@.dylib
+#elif defined(XP_UNIX)
+@BINPATH@/libicudata.so.@MOZ_ICU_VERSION@
+@BINPATH@/libicui18n.so.@MOZ_ICU_VERSION@
+@BINPATH@/libicuuc.so.@MOZ_ICU_VERSION@
+#endif
+#endif
+#endif
 #ifdef MOZ_SHARED_MOZGLUE
 @BINPATH@/@DLL_PREFIX@mozglue@DLL_SUFFIX@
 #endif
 #ifdef MOZ_REPLACE_MALLOC
 #ifndef MOZ_JEMALLOC3
 @BINPATH@/@DLL_PREFIX@replace_jemalloc@DLL_SUFFIX@
 #endif
 #endif
@@ -107,17 +124,17 @@
 @RESPATH@/resources.arsc
 @RESPATH@/classes.dex
 @RESPATH@/res/drawable
 @RESPATH@/res/drawable-hdpi
 @RESPATH@/res/layout
 #endif
 #ifdef MOZ_GTK3
 @BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
-@BINPATH@/@DLL_PREFIX@mozgtk2@DLL_SUFFIX@
+@BINPATH@/gtk2/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
 #endif
 
 [browser]
 ; [Base Browser Files]
 #ifndef XP_UNIX
 @BINPATH@/@MOZ_APP_NAME@.exe
 #else
 @BINPATH@/@MOZ_APP_NAME@-bin
@@ -493,36 +510,34 @@
 #endif // MOZ_WIDGET_GONK
 
 ; RIL
 #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
 @RESPATH@/components/CellBroadcastService.js
 @RESPATH@/components/CellBroadcastService.manifest
 @RESPATH@/components/DataCallManager.js
 @RESPATH@/components/DataCallManager.manifest
-@BINPATH@/components/IccService.js
-@BINPATH@/components/IccService.manifest
+@RESPATH@/components/IccService.js
+@RESPATH@/components/IccService.manifest
 @RESPATH@/components/MmsService.js
 @RESPATH@/components/MmsService.manifest
 @RESPATH@/components/MobileMessageDatabaseService.js
 @RESPATH@/components/MobileMessageDatabaseService.manifest
 #ifndef DISABLE_MOZ_RIL_GEOLOC
 @RESPATH@/components/DataCallInterfaceService.js
 @RESPATH@/components/DataCallInterfaceService.manifest
 @RESPATH@/components/MobileConnectionService.js
 @RESPATH@/components/MobileConnectionService.manifest
 @RESPATH@/components/RadioInterfaceLayer.js
 @RESPATH@/components/RadioInterfaceLayer.manifest
 @RESPATH@/components/SmsService.js
 @RESPATH@/components/SmsService.manifest
 #endif
 @RESPATH@/components/StkCmdFactory.js
 @RESPATH@/components/StkCmdFactory.manifest
-@RESPATH@/components/RILContentHelper.js
-@RESPATH@/components/RILContentHelper.manifest
 @RESPATH@/components/RILSystemMessengerHelper.js
 @RESPATH@/components/RILSystemMessengerHelper.manifest
 @RESPATH@/components/TelephonyAudioService.js
 @RESPATH@/components/TelephonyAudioService.manifest
 @RESPATH@/components/USSDReceivedWrapper.js
 @RESPATH@/components/USSDReceivedWrapper.manifest
 #ifndef DISABLE_MOZ_RIL_GEOLOC
 @RESPATH@/components/TelephonyService.js
@@ -706,18 +721,22 @@
 ; Modules
 @RESPATH@/modules/*
 
 ; Safe Browsing
 @RESPATH@/components/nsURLClassifier.manifest
 @RESPATH@/components/nsUrlClassifierHashCompleter.js
 @RESPATH@/components/nsUrlClassifierListManager.js
 @RESPATH@/components/nsUrlClassifierLib.js
+@RESPATH@/components/url-classifier.xpt
+
+; Private Browsing
+@RESPATH@/components/privatebrowsing.xpt
+@RESPATH@/components/PrivateBrowsing.manifest
 @RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
-@RESPATH@/components/url-classifier.xpt
 
 ; GNOME hooks
 #ifdef MOZ_ENABLE_GNOME_COMPONENT
 @RESPATH@/components/@DLL_PREFIX@mozgnome@DLL_SUFFIX@
 #endif
 
 ; ANGLE on Win32
 #ifdef XP_WIN32
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -103,65 +103,22 @@ pref("app.update.altwindowtype", "Browse
 pref("app.update.log", false);
 
 // The number of general background check failures to allow before notifying the
 // user of the failure. User initiated update checks always notify the user of
 // the failure.
 pref("app.update.backgroundMaxErrors", 10);
 
 // The aus update xml certificate checks for application update are disabled on
-// Windows and Mac OS X since the mar signature check are implemented on these
-// platforms and is sufficient to prevent us from applying a mar that is not
-// valid.
-#if defined(XP_WIN) || defined(XP_MACOSX)
+// Windows, Mac OS X, and Linux since the mar signature check are implemented on
+// these platforms and is sufficient to prevent us from applying a mar that is
+// not valid. Bug 1182352 will remove the update xml certificate checks and the
+// following two preferences.
 pref("app.update.cert.requireBuiltIn", false);
 pref("app.update.cert.checkAttributes", false);
-#else
-// When |app.update.cert.requireBuiltIn| is true or not specified the
-// final certificate and all certificates the connection is redirected to before
-// the final certificate for the url specified in the |app.update.url|
-// preference must be built-in.
-pref("app.update.cert.requireBuiltIn", true);
-
-// When |app.update.cert.checkAttributes| is true or not specified the
-// certificate attributes specified in the |app.update.certs.| preference branch
-// are checked against the certificate for the url specified by the
-// |app.update.url| preference.
-pref("app.update.cert.checkAttributes", true);
-
-// The number of certificate attribute check failures to allow for background
-// update checks before notifying the user of the failure. User initiated update
-// checks always notify the user of the certificate attribute check failure.
-pref("app.update.cert.maxErrors", 5);
-
-// The |app.update.certs.| preference branch contains branches that are
-// sequentially numbered starting at 1 that contain attribute name / value
-// pairs for the certificate used by the server that hosts the update xml file
-// as specified in the |app.update.url| preference. When these preferences are
-// present the following conditions apply for a successful update check:
-// 1. the uri scheme must be https
-// 2. the preference name must exist as an attribute name on the certificate and
-//    the value for the name must be the same as the value for the attribute name
-//    on the certificate.
-// If these conditions aren't met it will be treated the same as when there is
-// no update available. This validation will not be performed when the
-// |app.update.url.override| user preference has been set for testing updates or
-// when the |app.update.cert.checkAttributes| preference is set to false. Also,
-// the |app.update.url.override| preference should ONLY be used for testing.
-// IMPORTANT! media.gmp-manager.certs.* prefs should also be updated if these
-// are updated.
-
-// Non-release builds (Nightly, Aurora, etc.) have been switched over to aus4.mozilla.org.
-// This condition protects us against accidentally using it for release builds.
-pref("app.update.certs.1.issuerName", "CN=DigiCert Secure Server CA,O=DigiCert Inc,C=US");
-pref("app.update.certs.1.commonName", "aus4.mozilla.org");
-
-pref("app.update.certs.2.issuerName", "CN=Thawte SSL CA,O=\"Thawte, Inc.\",C=US");
-pref("app.update.certs.2.commonName", "aus4.mozilla.org");
-#endif
 
 // Whether or not app updates are enabled
 pref("app.update.enabled", true);
 
 // This preference turns on app.update.mode and allows automatic download and
 // install to take place. We use a separate boolean toggle for this to make
 // the UI easier to construct.
 pref("app.update.auto", true);
@@ -349,16 +306,20 @@ pref("browser.urlbar.suggest.history",  
 pref("browser.urlbar.suggest.bookmark",             true);
 pref("browser.urlbar.suggest.openpage",             true);
 #ifdef NIGHTLY_BUILD
 pref("browser.urlbar.suggest.searches",             true);
 #else
 pref("browser.urlbar.suggest.searches",             false);
 #endif
 
+// Limit the number of characters sent to the current search engine to fetch
+// suggestions.
+pref("browser.urlbar.maxCharsForSearchSuggestions", 20);
+
 // Restrictions to current suggestions can also be applied (intersection).
 // Typed suggestion works only if history is set to true.
 pref("browser.urlbar.suggest.history.onlyTyped",    false);
 
 pref("browser.urlbar.formatting.enabled", true);
 pref("browser.urlbar.trimURLs", true);
 
 pref("browser.altClickSave", false);
@@ -486,16 +447,22 @@ pref("browser.tabs.drawInTitlebar", true
 
 // When tabs opened by links in other tabs via a combination of
 // browser.link.open_newwindow being set to 3 and target="_blank" etc are
 // closed:
 // true   return to the tab that opened this tab (its owner)
 // false  return to the adjacent tab (old default)
 pref("browser.tabs.selectOwnerOnClose", true);
 
+#ifdef RELEASE_BUILD
+pref("browser.tabs.showAudioPlayingIcon", false);
+#else
+pref("browser.tabs.showAudioPlayingIcon", true);
+#endif
+
 pref("browser.ctrlTab.previews", false);
 
 // By default, do not export HTML at shutdown.
 // If true, at shutdown the bookmarks in your menu and toolbar will
 // be exported as HTML to the bookmarks.html file.
 pref("browser.bookmarks.autoExportHTML",          false);
 
 // The maximum number of daily bookmark backups to
@@ -1969,17 +1936,17 @@ pref("browser.reader.detectedFirstArticl
 // Don't limit how many nodes we care about on desktop:
 pref("reader.parse-node-limit", 0);
 
 pref("browser.pocket.enabled", true);
 pref("browser.pocket.api", "api.getpocket.com");
 pref("browser.pocket.site", "getpocket.com");
 pref("browser.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
 pref("browser.pocket.useLocaleList", true);
-pref("browser.pocket.enabledLocales", "en-US de es-ES ja ja-JP-mac ru");
+pref("browser.pocket.enabledLocales", "cs de en-GB en-US en-ZA es-ES es-MX fr hu it ja ja-JP-mac ko nl pl pt-BR pt-PT ru zh-CN zh-TW");
 
 pref("view_source.tab", true);
 
 // Enable Service Workers for desktop on non-release builds
 #ifndef RELEASE_BUILD
 pref("dom.serviceWorkers.enabled", true);
 pref("dom.serviceWorkers.interception.enabled", true);
 #endif
--- a/browser/base/content/browser-trackingprotection.js
+++ b/browser/base/content/browser-trackingprotection.js
@@ -121,16 +121,19 @@ let TrackingProtection = {
     } else {
       Services.perms.add(normalizedUrl,
         "trackingprotection", Services.perms.ALLOW_ACTION);
     }
 
     // Telemetry for disable protection.
     this.eventsHistogram.add(1);
 
+    // Hide the control center.
+    document.getElementById("identity-popup").hidePopup();
+
     BrowserReload();
   },
 
   enableForCurrentPage() {
     // Remove the current host from the 'trackingprotection' consumer
     // of the permission manager. This effectively removes this host
     // from the tracking protection allowlist.
     let normalizedUrl = Services.io.newURI(
@@ -141,16 +144,19 @@ let TrackingProtection = {
       PrivateBrowsingUtils.removeFromTrackingAllowlist(normalizedUrl);
     } else {
       Services.perms.remove(normalizedUrl, "trackingprotection");
     }
 
     // Telemetry for enable protection.
     this.eventsHistogram.add(2);
 
+    // Hide the control center.
+    document.getElementById("identity-popup").hidePopup();
+
     BrowserReload();
   },
 
   showIntroPanel: Task.async(function*() {
     let mm = gBrowser.selectedBrowser.messageManager;
     let brandBundle = document.getElementById("bundle_brand");
     let brandShortName = brandBundle.getString("brandShortName");
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1134,17 +1134,19 @@ var gBrowserInit = {
     mm.addMessageListener("PageVisibility:Show", function(message) {
       if (message.target == gBrowser.selectedBrowser) {
         setTimeout(pageShowEventHandlers, 0, message.data.persisted);
       }
     });
 
     gBrowser.addEventListener("AboutTabCrashedLoad", function(event) {
 #ifdef MOZ_CRASHREPORTER
-      TabCrashReporter.onAboutTabCrashedLoad(gBrowser.getBrowserForDocument(event.target));
+      TabCrashReporter.onAboutTabCrashedLoad(gBrowser.getBrowserForDocument(event.target), {
+        crashedTabCount: SessionStore.crashedTabCount,
+      });
 #endif
     }, false, true);
 
     gBrowser.addEventListener("AboutTabCrashedMessage", function(event) {
       let ownerDoc = event.originalTarget;
 
       if (!ownerDoc.documentURI.startsWith("about:tabcrashed")) {
         return;
@@ -1166,21 +1168,17 @@ var gBrowserInit = {
       switch (event.detail.message) {
       case "closeTab":
         gBrowser.removeTab(tab, { animate: true });
         break;
       case "restoreTab":
         SessionStore.reviveCrashedTab(tab);
         break;
       case "restoreAll":
-        for (let browserWin of browserWindows()) {
-          for (let tab of browserWin.gBrowser.tabs) {
-            SessionStore.reviveCrashedTab(tab);
-          }
-        }
+        SessionStore.reviveAllCrashedTabs();
         break;
       }
     }, false, true);
 
     let uriToLoad = this._getUriToLoad();
     if (uriToLoad && uriToLoad != "about:blank") {
       if (uriToLoad instanceof Ci.nsISupportsArray) {
         let count = uriToLoad.Count();
@@ -2406,18 +2404,20 @@ function BrowserViewSource(browser) {
     outerWindowID: browser.outerWindowID,
     URL: browser.currentURI.spec,
   });
 }
 
 // doc - document to use for source, or null for this window's document
 // initialTab - name of the initial tab to display, or null for the first tab
 // imageElement - image to load in the Media Tab of the Page Info window; can be null/omitted
-function BrowserPageInfo(doc, initialTab, imageElement) {
-  var args = {doc: doc, initialTab: initialTab, imageElement: imageElement};
+// frameOuterWindowID - the id of the frame that the context menu opened in; can be null/omitted
+function BrowserPageInfo(doc, initialTab, imageElement, frameOuterWindowID) {
+  var args = {doc: doc, initialTab: initialTab, imageElement: imageElement,
+              frameOuterWindowID: frameOuterWindowID};
   var windows = Services.wm.getEnumerator("Browser:page-info");
 
   var documentURL = doc ? doc.location : window.gBrowser.selectedBrowser.contentDocumentAsCPOW.location;
 
   // Check for windows matching the url
   while (windows.hasMoreElements()) {
     var currentWindow = windows.getNext();
     if (currentWindow.closed) {
@@ -6628,16 +6628,17 @@ var gIdentityHandler = {
   IDENTITY_MODE_UNKNOWN                                : "unknownIdentity",  // No trusted identity information
   IDENTITY_MODE_USES_WEAK_CIPHER                       : "unknownIdentity weakCipher",  // SSL with RC4 cipher suite or SSL3
   IDENTITY_MODE_MIXED_DISPLAY_LOADED                   : "unknownIdentity mixedContent mixedDisplayContent",  // SSL with unauthenticated display content
   IDENTITY_MODE_MIXED_ACTIVE_LOADED                    : "unknownIdentity mixedContent mixedActiveContent",  // SSL with unauthenticated active (and perhaps also display) content
   IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED    : "unknownIdentity mixedContent mixedDisplayContentLoadedActiveBlocked",  // SSL with unauthenticated display content; unauthenticated active content is blocked.
   IDENTITY_MODE_MIXED_ACTIVE_BLOCKED                   : "verifiedDomain mixedContent mixedActiveBlocked",  // SSL with unauthenticated active content blocked; no unauthenticated display content
   IDENTITY_MODE_MIXED_ACTIVE_BLOCKED_IDENTIFIED        : "verifiedIdentity mixedContent mixedActiveBlocked",  // SSL with unauthenticated active content blocked; no unauthenticated display content
   IDENTITY_MODE_CHROMEUI                               : "chromeUI",         // Part of the product's UI
+  IDENTITY_MODE_FILE_URI                               : "fileURI",  // File path
 
   // Cache the most recent SSLStatus and Location seen in checkIdentity
   _lastStatus : null,
   _lastUri : null,
   _mode : "unknownIdentity",
 
   // smart getters
   get _identityPopup () {
@@ -6691,16 +6692,20 @@ var gIdentityHandler = {
     delete this._overrideService;
     return this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
                                      .getService(Ci.nsICertOverrideService);
   },
   get _identityIconCountryLabel () {
     delete this._identityIconCountryLabel;
     return this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
   },
+  get _identityIcons () {
+    delete this._identityIcons;
+    return this._identityIcons = document.getElementById("identity-icons");
+  },
   get _identityIcon () {
     delete this._identityIcon;
     return this._identityIcon = document.getElementById("page-proxy-favicon");
   },
   get _permissionsContainer () {
     delete this._permissionsContainer;
     return this._permissionsContainer = document.getElementById("identity-popup-permissions");
   },
@@ -6710,22 +6715,24 @@ var gIdentityHandler = {
   },
 
   /**
    * Rebuild cache of the elements that may or may not exist depending
    * on whether there's a location bar.
    */
   _cacheElements : function() {
     delete this._identityBox;
+    delete this._identityIcons;
     delete this._identityIconLabel;
     delete this._identityIconCountryLabel;
     delete this._identityIcon;
     delete this._permissionsContainer;
     delete this._permissionList;
     this._identityBox = document.getElementById("identity-box");
+    this._identityIcons = document.getElementById("identity-icons");
     this._identityIconLabel = document.getElementById("identity-icon-label");
     this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
     this._identityIcon = document.getElementById("page-proxy-favicon");
     this._permissionsContainer = document.getElementById("identity-popup-permissions");
     this._permissionList = document.getElementById("identity-popup-permission-list");
   },
 
   /**
@@ -6837,17 +6844,30 @@ var gIdentityHandler = {
       } else if (state & nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT) {
         this.setMode(this.IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED);
       } else if (state & nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT) {
         this.setMode(this.IDENTITY_MODE_MIXED_DISPLAY_LOADED);
       } else {
         this.setMode(this.IDENTITY_MODE_USES_WEAK_CIPHER);
       }
     } else {
-      this.setMode(this.IDENTITY_MODE_UNKNOWN);
+      // Create a channel for the sole purpose of getting the resolved URI
+      // of the request to determine if it's loaded from the file system.
+      let resolvedURI = NetUtil.newChannel({uri,loadUsingSystemPrincipal:true}).URI;
+      if (resolvedURI.schemeIs("jar")) {
+        // Given a URI "jar:<jar-file-uri>!/<jar-entry>"
+        // create a new URI using <jar-file-uri>!/<jar-entry>
+        resolvedURI = NetUtil.newURI(resolvedURI.path);
+      }
+
+      if (resolvedURI.schemeIs("file")) {
+        this.setMode(this.IDENTITY_MODE_FILE_URI);
+      } else {
+        this.setMode(this.IDENTITY_MODE_UNKNOWN);
+      }
     }
 
     // Show the doorhanger when:
     // - mixed active content is blocked
     // - mixed active content is loaded (detected but not blocked)
     // - tracking content is blocked
     // - tracking content is not blocked
     if (state &
@@ -7125,17 +7145,17 @@ var gIdentityHandler = {
     this.setPopupMessages(this._identityBox.className);
 
     this.updateSitePermissions();
 
     // Add the "open" attribute to the identity box for styling
     this._identityBox.setAttribute("open", "true");
 
     // Now open the popup, anchored off the primary chrome element
-    this._identityPopup.openPopup(this._identityIcon, "bottomcenter topleft");
+    this._identityPopup.openPopup(this._identityIcons, "bottomcenter topleft");
   },
 
   onPopupShown(event) {
     if (event.target == this._identityPopup) {
       window.addEventListener("focus", this, true);
     }
   },
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -758,21 +758,23 @@
                    code fires onmousedown, and hence eats our favicon drag events.
                    We only add the identity-box button to the tab order when the location bar
                    has focus, otherwise pressing F6 focuses it instead of the location bar -->
               <box id="identity-box" role="button"
                    align="center"
                    onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
                    onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
                    ondragstart="gIdentityHandler.onDragStart(event);">
-                <image id="tracking-protection-icon"/>
-                <image id="page-proxy-favicon"
-                       consumeanchor="identity-box"
-                       onclick="PageProxyClickHandler(event);"
-                       pageproxystate="invalid"/>
+                <hbox id="identity-icons"
+                      consumeanchor="identity-box">
+                  <image id="tracking-protection-icon"/>
+                  <image id="page-proxy-favicon"
+                         onclick="PageProxyClickHandler(event);"
+                         pageproxystate="invalid"/>
+                </hbox>
                 <hbox id="identity-icon-labels">
                   <label id="identity-icon-label" class="plain" flex="1"/>
                   <label id="identity-icon-country-label" class="plain"/>
                 </hbox>
               </box>
               <box id="urlbar-display-box" align="center">
                 <label class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/>
               </box>
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -34,16 +34,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils",
   "resource:///modules/PlacesUIUtils.jsm");
 XPCOMUtils.defineLazyGetter(this, "PageMenuChild", function() {
   let tmp = {};
   Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
   return new tmp.PageMenuChild();
 });
 
+XPCOMUtils.defineLazyModuleGetter(this, "Feeds", "resource:///modules/Feeds.jsm");
+
 // TabChildGlobal
 var global = this;
 
 // Load the form validation popup handler
 var formSubmitObserver = new FormSubmitObserver(content, this);
 
 addMessageListener("ContextMenu:DoCustomCommand", function(message) {
   PageMenuChild.executeMenu(message.data);
@@ -835,8 +837,401 @@ addMessageListener("ContextMenu:SetAsDes
       Cu.reportError(e);
       disable = true;
     }
   }
 
   if (disable)
     sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result", { disable });
 });
+
+let pageInfoListener = {
+
+  init: function(chromeGlobal) {
+    chromeGlobal.addMessageListener("PageInfo:getData", this, false, true);
+  },
+
+  receiveMessage: function(message) {
+    this.imageViewRows = [];
+    this.frameList = [];
+    this.strings = message.data.strings;
+
+    let frameOuterWindowID = message.data.frameOuterWindowID;
+
+    // If inside frame then get the frame's window and document.
+    if (frameOuterWindowID) {
+      this.window = Services.wm.getOuterWindowWithId(frameOuterWindowID);
+      this.document = this.window.document;
+    }
+    else {
+      this.document = content.document;
+      this.window = content.window;
+    }
+
+    let pageInfoData = {metaViewRows: this.getMetaInfo(), docInfo: this.getDocumentInfo(),
+                        feeds: this.getFeedsInfo(), windowInfo: this.getWindowInfo()};
+    sendAsyncMessage("PageInfo:data", pageInfoData);
+
+    // Separate step so page info dialog isn't blank while waiting for this to finish.
+    this.getMediaInfo();
+
+    // Send the message after all the media elements have been walked through.
+    let pageInfoMediaData = {imageViewRows: this.imageViewRows};
+
+    this.imageViewRows = null;
+    this.frameList = null;
+    this.strings = null;
+    this.window = null;
+    this.document = null;
+
+    sendAsyncMessage("PageInfo:mediaData", pageInfoMediaData);
+  },
+
+  getMetaInfo: function() {
+    let metaViewRows = [];
+
+    // Get the meta tags from the page.
+    let metaNodes = this.document.getElementsByTagName("meta");
+
+    for (let metaNode of metaNodes) {
+      metaViewRows.push([metaNode.name || metaNode.httpEquiv || metaNode.getAttribute("property"),
+                        metaNode.content]);
+    }
+
+    return metaViewRows;
+  },
+
+  getWindowInfo: function() {
+    let windowInfo = {};
+    windowInfo.isTopWindow = this.window == this.window.top;
+
+    let hostName = null;
+    try {
+      hostName = this.window.location.host;
+    }
+    catch (exception) { }
+
+    windowInfo.hostName = hostName;
+    return windowInfo;
+  },
+
+  getDocumentInfo: function() {
+    let docInfo = {};
+    docInfo.title = this.document.title;
+    docInfo.location = this.document.location.toString();
+    docInfo.referrer = this.document.referrer;
+    docInfo.compatMode = this.document.compatMode;
+    docInfo.contentType = this.document.contentType;
+    docInfo.characterSet = this.document.characterSet;
+    docInfo.lastModified = this.document.lastModified;
+
+    let documentURIObject = {};
+    documentURIObject.spec = this.document.documentURIObject.spec;
+    documentURIObject.originCharset = this.document.documentURIObject.originCharset;
+    docInfo.documentURIObject = documentURIObject;
+
+    docInfo.isContentWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(content);
+
+    return docInfo;
+  },
+
+  getFeedsInfo: function() {
+    let feeds = [];
+    // Get the feeds from the page.
+    let linkNodes = this.document.getElementsByTagName("link");
+    let length = linkNodes.length;
+    for (let i = 0; i < length; i++) {
+      let link = linkNodes[i];
+      if (!link.href) {
+        continue;
+      }
+      let rel = link.rel && link.rel.toLowerCase();
+      let rels = {};
+
+      if (rel) {
+        for each (let relVal in rel.split(/\s+/)) {
+          rels[relVal] = true;
+        }
+      }
+
+      if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) {
+        let type = Feeds.isValidFeed(link, this.document.nodePrincipal, "feed" in rels);
+        if (type) {
+          type = this.strings[type] || this.strings["application/rss+xml"];
+          feeds.push([link.title, type, link.href]);
+        }
+      }
+    }
+    return feeds;
+  },
+
+  // Only called once to get the media tab's media elements from the content page.
+  // The actual work is done with a TreeWalker that calls doGrab() once for
+  // each element node in the document.
+  getMediaInfo: function()
+  {
+    this.goThroughFrames(this.document, this.window);
+    this.processFrames();
+  },
+
+  goThroughFrames: function(aDocument, aWindow)
+  {
+    this.frameList.push(aDocument);
+    if (aWindow && aWindow.frames.length > 0) {
+      let num = aWindow.frames.length;
+      for (let i = 0; i < num; i++) {
+        this.goThroughFrames(aWindow.frames[i].document, aWindow.frames[i]);  // recurse through the frames
+      }
+    }
+  },
+
+  processFrames: function()
+  {
+    if (this.frameList.length) {
+      let doc = this.frameList[0];
+      let iterator = doc.createTreeWalker(doc, content.NodeFilter.SHOW_ELEMENT, elem => this.grabAll(elem));
+      this.frameList.shift();
+      this.doGrab(iterator);
+    }
+  },
+
+  /**
+   * This function's previous purpose in pageInfo.js was to get loop through 500 elements at a time.
+   * The iterator filter will filter for media elements.
+   * #TODO Bug 1175794: refactor pageInfo.js to receive a media element at a time
+   * from messages and continually update UI.
+   */
+  doGrab: function(iterator)
+  {
+    while (true)
+    {
+      if (!iterator.nextNode()) {
+        this.processFrames();
+        return;
+      }
+    }
+  },
+
+  grabAll: function(elem)
+  {
+    // Check for images defined in CSS (e.g. background, borders), any node may have multiple.
+    let computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, "");
+
+    let addImage = (url, type, alt, elem, isBg) => {
+      let element = this.serializeElementInfo(url, type, alt, elem, isBg);
+      this.imageViewRows.push([url, type, alt, element, isBg]);
+    };
+
+    if (computedStyle) {
+      let addImgFunc = (label, val) => {
+        if (val.primitiveType == content.CSSPrimitiveValue.CSS_URI) {
+          addImage(val.getStringValue(), label, this.strings.notSet, elem, true);
+        }
+        else if (val.primitiveType == content.CSSPrimitiveValue.CSS_STRING) {
+          // This is for -moz-image-rect.
+          // TODO: Reimplement once bug 714757 is fixed.
+          let strVal = val.getStringValue();
+          if (strVal.search(/^.*url\(\"?/) > -1) {
+            let url = strVal.replace(/^.*url\(\"?/,"").replace(/\"?\).*$/,"");
+            addImage(url, label, this.strings.notSet, elem, true);
+          }
+        }
+        else if (val.cssValueType == content.CSSValue.CSS_VALUE_LIST) {
+          // Recursively resolve multiple nested CSS value lists.
+          for (let i = 0; i < val.length; i++) {
+            addImgFunc(label, val.item(i));
+          }
+        }
+      };
+
+      addImgFunc(this.strings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image"));
+      addImgFunc(this.strings.mediaBorderImg, computedStyle.getPropertyCSSValue("border-image-source"));
+      addImgFunc(this.strings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image"));
+      addImgFunc(this.strings.mediaCursor, computedStyle.getPropertyCSSValue("cursor"));
+    }
+
+    // One swi^H^H^Hif-else to rule them all.
+    if (elem instanceof content.HTMLImageElement) {
+      addImage(elem.src, this.strings.mediaImg,
+               (elem.hasAttribute("alt")) ? elem.alt : this.strings.notSet, elem, false);
+    }
+    else if (elem instanceof content.SVGImageElement) {
+      try {
+        // Note: makeURLAbsolute will throw if either the baseURI is not a valid URI
+        //       or the URI formed from the baseURI and the URL is not a valid URI.
+        let href = makeURLAbsolute(elem.baseURI, elem.href.baseVal);
+        addImage(href, this.strings.mediaImg, "", elem, false);
+      } catch (e) { }
+    }
+    else if (elem instanceof content.HTMLVideoElement) {
+      addImage(elem.currentSrc, this.strings.mediaVideo, "", elem, false);
+    }
+    else if (elem instanceof content.HTMLAudioElement) {
+      addImage(elem.currentSrc, this.strings.mediaAudio, "", elem, false);
+    }
+    else if (elem instanceof content.HTMLLinkElement) {
+      if (elem.rel && /\bicon\b/i.test(elem.rel)) {
+        addImage(elem.href, this.strings.mediaLink, "", elem, false);
+      }
+    }
+    else if (elem instanceof content.HTMLInputElement || elem instanceof content.HTMLButtonElement) {
+      if (elem.type.toLowerCase() == "image") {
+        addImage(elem.src, this.strings.mediaInput,
+                 (elem.hasAttribute("alt")) ? elem.alt : this.strings.notSet, elem, false);
+      }
+    }
+    else if (elem instanceof content.HTMLObjectElement) {
+      addImage(elem.data, this.strings.mediaObject, this.getValueText(elem), elem, false);
+    }
+    else if (elem instanceof content.HTMLEmbedElement) {
+      addImage(elem.src, this.strings.mediaEmbed, "", elem, false);
+    }
+
+    return content.NodeFilter.FILTER_ACCEPT;
+  },
+
+  /**
+   * Set up a JSON element object with all the instanceOf and other infomation that
+   * makePreview in pageInfo.js uses to figure out how to display the preview.
+   */
+
+  serializeElementInfo: function(url, type, alt, item, isBG)
+  {
+    // Interface for image loading content.
+    const nsIImageLoadingContent = Components.interfaces.nsIImageLoadingContent;
+
+    let result = {};
+
+    let imageText;
+    if (!isBG &&
+        !(item instanceof content.SVGImageElement) &&
+        !(this.document instanceof content.ImageDocument)) {
+      imageText = item.title || item.alt;
+
+      if (!imageText && !(item instanceof content.HTMLImageElement)) {
+        imageText = this.getValueText(item);
+      }
+    }
+
+    result.imageText = imageText;
+    result.longDesc = item.longDesc;
+    result.numFrames = 1;
+
+    if (item instanceof content.HTMLObjectElement ||
+      item instanceof content.HTMLEmbedElement ||
+      item instanceof content.HTMLLinkElement) {
+      result.mimeType = item.type;
+    }
+
+    if (!result.mimeType && !isBG && item instanceof nsIImageLoadingContent) {
+      // Interface for image loading content.
+      const nsIImageLoadingContent = Components.interfaces.nsIImageLoadingContent;
+      let imageRequest = item.getRequest(nsIImageLoadingContent.CURRENT_REQUEST);
+      if (imageRequest) {
+        result.mimeType = imageRequest.mimeType;
+        let image = !(imageRequest.imageStatus & imageRequest.STATUS_ERROR) && imageRequest.image;
+        if (image) {
+          result.numFrames = image.numFrames;
+        }
+      }
+    }
+
+    // if we have a data url, get the MIME type from the url
+    if (!result.mimeType && url.startsWith("data:")) {
+      let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
+      if (dataMimeType)
+        result.mimeType = dataMimeType[1].toLowerCase();
+    }
+
+    result.HTMLLinkElement = item instanceof content.HTMLLinkElement;
+    result.HTMLInputElement = item instanceof content.HTMLInputElement;
+    result.HTMLImageElement = item instanceof content.HTMLImageElement;
+    result.HTMLObjectElement = item instanceof content.HTMLObjectElement;
+    result.SVGImageElement = item instanceof content.SVGImageElement;
+    result.HTMLVideoElement = item instanceof content.HTMLVideoElement;
+    result.HTMLAudioElement = item instanceof content.HTMLAudioElement;
+
+    if (!isBG) {
+      result.width = item.width;
+      result.height = item.height;
+    }
+
+    if (item instanceof content.SVGImageElement) {
+      result.SVGImageElementWidth = item.width.baseVal.value;
+      result.SVGImageElementHeight = item.height.baseVal.value;
+    }
+
+    result.baseURI = item.baseURI;
+
+    return result;
+  },
+
+  //******** Other Misc Stuff
+  // Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
+  // parse a node to extract the contents of the node
+  getValueText: function(node)
+  {
+
+    let valueText = "";
+
+    // Form input elements don't generally contain information that is useful to our callers, so return nothing.
+    if (node instanceof content.HTMLInputElement ||
+        node instanceof content.HTMLSelectElement ||
+        node instanceof content.HTMLTextAreaElement) {
+      return valueText;
+    }
+
+    // Otherwise recurse for each child.
+    let length = node.childNodes.length;
+
+    for (let i = 0; i < length; i++) {
+      let childNode = node.childNodes[i];
+      let nodeType = childNode.nodeType;
+
+      // Text nodes are where the goods are.
+      if (nodeType == content.Node.TEXT_NODE) {
+        valueText += " " + childNode.nodeValue;
+      }
+      // And elements can have more text inside them.
+      else if (nodeType == content.Node.ELEMENT_NODE) {
+        // Images are special, we want to capture the alt text as if the image weren't there.
+        if (childNode instanceof content.HTMLImageElement) {
+          valueText += " " + this.getAltText(childNode);
+        }
+        else {
+          valueText += " " + this.getValueText(childNode);
+        }
+      }
+    }
+
+    return this.stripWS(valueText);
+  },
+
+  // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
+  // Traverse the tree in search of an img or area element and grab its alt tag.
+  getAltText: function(node)
+  {
+    let altText = "";
+
+    if (node.alt) {
+      return node.alt;
+    }
+    let length = node.childNodes.length;
+    for (let i = 0; i < length; i++) {
+      if ((altText = this.getAltText(node.childNodes[i]) != undefined)) { // stupid js warning...
+        return altText;
+      }
+    }
+    return "";
+  },
+
+  // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
+  // Strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space.
+  stripWS: function(text)
+  {
+    let middleRE = /\s+/g;
+    let endRE = /(^\s+)|(\s+$)/g;
+
+    text = text.replace(middleRE, " ");
+    return text.replace(endRE, "");
+  }
+};
+pageInfoListener.init(this);
\ No newline at end of file
--- a/browser/base/content/contentSearchUI.js
+++ b/browser/base/content/contentSearchUI.js
@@ -453,16 +453,18 @@ ContentSearchUIController.prototype = {
     }
     this._pendingOneOffRefresh = true;
   },
 
   _onMsgStrings: function (strings) {
     this._strings = strings;
     this._updateDefaultEngineHeader();
     this._updateSearchWithHeader();
+    document.getElementById("contentSearchSettingsButton").textContent =
+      this._strings.searchSettings;
   },
 
   _updateDefaultEngineHeader: function () {
     let header = document.getElementById("contentSearchDefaultEngineHeader");
     if (this.defaultEngine.icon) {
       header.firstChild.setAttribute("src", this.defaultEngine.icon);
     }
     if (!this._strings) {
@@ -479,22 +481,20 @@ ContentSearchUIController.prototype = {
     if (!this._strings) {
       return;
     }
     let searchWithHeader = document.getElementById("contentSearchSearchWithHeader");
     while (searchWithHeader.firstChild) {
       searchWithHeader.firstChild.remove();
     }
     if (this.input.value) {
-      searchWithHeader.appendChild(document.createTextNode(this._strings.searchFor));
-      let span = document.createElementNS(HTML_NS, "span");
-      span.setAttribute("class", "contentSearchSearchWithHeaderSearchText");
-      span.appendChild(document.createTextNode(" " + this.input.value + " "));
-      searchWithHeader.appendChild(span);
-      searchWithHeader.appendChild(document.createTextNode(this._strings.searchWith));
+      let html = "<span class='contentSearchSearchWithHeaderSearchText'>" +
+                 this.input.value + "</span>";
+      html = this._strings.searchForKeywordsWith.replace("%S", html);
+      searchWithHeader.innerHTML = html;
       return;
     }
     searchWithHeader.appendChild(document.createTextNode(this._strings.searchWithHeader));
   },
 
   _speculativeConnect: function () {
     if (this.defaultEngine) {
       this._sendMsg("SpeculativeConnect", this.defaultEngine.name);
@@ -649,17 +649,16 @@ ContentSearchUIController.prototype = {
     header = document.createElementNS(HTML_NS, "td");
     headerRow.setAttribute("class", "contentSearchHeaderRow");
     header.setAttribute("class", "contentSearchHeader");
     headerRow.appendChild(header);
     header.id = "contentSearchSearchWithHeader";
     this._oneOffsTable.appendChild(headerRow);
 
     let button = document.createElementNS(HTML_NS, "button");
-    button.appendChild(document.createTextNode("Change Search Settings"));
     button.setAttribute("class", "contentSearchSettingsButton");
     button.classList.add("contentSearchHeaderRow");
     button.classList.add("contentSearchHeader");
     button.id = "contentSearchSettingsButton";
     button.addEventListener("click", this);
     button.addEventListener("mousemove", this);
     this._table.appendChild(button);
 
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1046,17 +1046,18 @@ nsContextMenu.prototype = {
     urlSecurityCheck(this.imageDescURL,
                      this.browser.contentPrincipal,
                      Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
     openUILink(this.imageDescURL, e, { disallowInheritPrincipal: true,
                                        referrerURI: gContextMenuContentData.documentURIObject });
   },
 
   viewFrameInfo: function() {
-    BrowserPageInfo(this.target.ownerDocument);
+    BrowserPageInfo(this.target.ownerDocument, null, null,
+                    this.frameOuterWindowID);
   },
 
   reloadImage: function() {
     urlSecurityCheck(this.mediaURL,
                      this.browser.contentPrincipal,
                      Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
 
     this.browser.messageManager.sendAsyncMessage("ContextMenu:ReloadImage",
--- a/browser/base/content/pageinfo/feeds.js
+++ b/browser/base/content/pageinfo/feeds.js
@@ -1,48 +1,18 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-XPCOMUtils.defineLazyModuleGetter(this, "Feeds",
-  "resource:///modules/Feeds.jsm");
-
-function initFeedTab()
+function initFeedTab(feeds)
 {
-  const feedTypes = {
-    "application/rss+xml": gBundle.getString("feedRss"),
-    "application/atom+xml": gBundle.getString("feedAtom"),
-    "text/xml": gBundle.getString("feedXML"),
-    "application/xml": gBundle.getString("feedXML"),
-    "application/rdf+xml": gBundle.getString("feedXML")
-  };
-
-  // get the feeds
-  var linkNodes = gDocument.getElementsByTagName("link");
-  var length = linkNodes.length;
-  for (var i = 0; i < length; i++) {
-    var link = linkNodes[i];
-    if (!link.href)
-      continue;
-
-    var rel = link.rel && link.rel.toLowerCase();
-    var rels = {};
-    if (rel) {
-      for each (let relVal in rel.split(/\s+/))
-        rels[relVal] = true;
-    }
-
-    if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) {
-      var type = Feeds.isValidFeed(link, gDocument.nodePrincipal, "feed" in rels);
-      if (type) {
-        type = feedTypes[type] || feedTypes["application/rss+xml"];
-        addRow(link.title, type, link.href);
-      }
-    }
+  for (let feed of feeds) {
+    let [name, type, url] = feed;
+    addRow(name, type, url);
   }
 
   var feedListbox = document.getElementById("feedListbox");
   document.getElementById("feedTab").hidden = feedListbox.getRowCount() == 0;
 }
 
 function onSubscribeFeed()
 {
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -47,18 +47,29 @@ pageInfoTreeView.prototype = {
   {
     this.data[row][column.index] = value;
   },
 
   addRow: function(row)
   {
     this.rows = this.data.push(row);
     this.rowCountChanged(this.rows - 1, 1);
-    if (this.selection.count == 0 && this.rowCount && !gImageElement)
+    if (this.selection.count == 0 && this.rowCount && !gImageElement) {
       this.selection.select(0);
+    }
+  },
+
+  addRows: function(rows)
+  {
+    this.data = this.data.concat(rows);
+    this.rowCountChanged(this.rows, rows.length);
+    this.rows = this.data.length;
+    if (this.selection.count == 0 && this.rowCount && !gImageElement) {
+      this.selection.select(0);
+    }
   },
 
   rowCountChanged: function(index, count)
   {
     this.tree.rowCountChanged(index, count);
   },
 
   invalidate: function()
@@ -135,18 +146,17 @@ pageInfoTreeView.prototype = {
   cycleCell: function(row, column) { },
   isEditable: function(row, column) { return false; },
   isSelectable: function(row, column) { return false; },
   performAction: function(action) { },
   performActionOnCell: function(action, row, column) { }
 };
 
 // mmm, yummy. global variables.
-var gWindow = null;
-var gDocument = null;
+var gDocInfo = null;
 var gImageElement = null;
 
 // column number to help using the data array
 const COL_IMAGE_ADDRESS = 0;
 const COL_IMAGE_TYPE    = 1;
 const COL_IMAGE_SIZE    = 2;
 const COL_IMAGE_ALT     = 3;
 const COL_IMAGE_COUNT   = 4;
@@ -281,44 +291,32 @@ const XHTMLre = RegExp(XHTMLNSre + "|" +
  * These arrays are used to hold callbacks that Page Info will call at
  * various stages. Use them by simply appending a function to them.
  * For example, add a function to onLoadRegistry by invoking
  *   "onLoadRegistry.push(XXXLoadFunc);"
  * The XXXLoadFunc should be unique to the overlay module, and will be
  * invoked as "XXXLoadFunc();"
  */
 
-// These functions are called to build the data displayed in the Page
-// Info window. The global variables gDocument and gWindow are set.
+// These functions are called to build the data displayed in the Page Info window.
 var onLoadRegistry = [ ];
 
 // These functions are called to remove old data still displayed in
 // the window when the document whose information is displayed
 // changes. For example, at this time, the list of images of the Media
 // tab is cleared.
 var onResetRegistry = [ ];
 
-// These are called once for each subframe of the target document and
-// the target document itself. The frame is passed as an argument.
-var onProcessFrame = [ ];
-
-// These functions are called once for each element (in all subframes, if any)
-// in the target document. The element is passed as an argument.
-var onProcessElement = [ ];
-
 // These functions are called once when all the elements in all of the target
 // document (and all of its subframes, if any) have been processed
 var onFinished = [ ];
 
 // These functions are called once when the Page Info window is closed.
 var onUnloadRegistry = [ ];
 
-// These functions are called once when an image preview is shown.
-var onImagePreviewShown = [ ];
-
 /* Called when PageInfo window is loaded.  Arguments are:
  *  window.arguments[0] - (optional) an object consisting of
  *                         - doc: (optional) document to use for source. if not provided,
  *                                the calling window's document will be used
  *                         - initialTab: (optional) id of the inital tab to display
  */
 function onLoadPageInfo()
 {
@@ -336,48 +334,73 @@ function onLoadPageInfo()
   gStrings.mediaInput = gBundle.getString("mediaInput");
   gStrings.mediaVideo = gBundle.getString("mediaVideo");
   gStrings.mediaAudio = gBundle.getString("mediaAudio");
 
   var args = "arguments" in window &&
              window.arguments.length >= 1 &&
              window.arguments[0];
 
-  if (!args || !args.doc) {
-    gWindow = window.opener.gBrowser.selectedBrowser.contentWindowAsCPOW;
-    gDocument = gWindow.document;
-  }
-
   // init media view
   var imageTree = document.getElementById("imagetree");
   imageTree.view = gImageView;
 
   /* Select the requested tab, if the name is specified */
   loadTab(args);
   Components.classes["@mozilla.org/observer-service;1"]
             .getService(Components.interfaces.nsIObserverService)
             .notifyObservers(window, "page-info-dialog-loaded", null);
 }
 
-function loadPageInfo()
+function loadPageInfo(frameOuterWindowID)
 {
-  var titleFormat = gWindow != gWindow.top ? "pageInfo.frame.title"
-                                           : "pageInfo.page.title";
-  document.title = gBundle.getFormattedString(titleFormat, [gDocument.location]);
+  let mm = window.opener.gBrowser.selectedBrowser.messageManager;
 
-  document.getElementById("main-window").setAttribute("relatedUrl", gDocument.location);
+  gStrings["application/rss+xml"]  = gBundle.getString("feedRss");
+  gStrings["application/atom+xml"] = gBundle.getString("feedAtom");
+  gStrings["text/xml"]             = gBundle.getString("feedXML");
+  gStrings["application/xml"]      = gBundle.getString("feedXML");
+  gStrings["application/rdf+xml"]  = gBundle.getString("feedXML");
+
+  // Look for pageInfoListener in content.js. Sends message to listener with arguments.
+  mm.sendAsyncMessage("PageInfo:getData", {strings: gStrings,
+                      frameOuterWindowID: frameOuterWindowID});
+
+  let pageInfoData = null;
 
-  // do the easy stuff first
-  makeGeneralTab();
+  // Get initial pageInfoData needed to display the general, feeds, permission and security tabs.
+  mm.addMessageListener("PageInfo:data", function onmessage(message) {
+    mm.removeMessageListener("PageInfo:data", onmessage);
+    pageInfoData = message.data;
+    let docInfo = pageInfoData.docInfo;
+    let windowInfo = pageInfoData.windowInfo;
+    let uri = makeURI(docInfo.documentURIObject.spec,
+                      docInfo.documentURIObject.originCharset);
+    gDocInfo = docInfo;
+
+    var titleFormat = windowInfo.isTopWindow ? "pageInfo.frame.title"
+                                             : "pageInfo.page.title";
+    document.title = gBundle.getFormattedString(titleFormat, [docInfo.location]);
 
-  // and then the hard stuff
-  makeTabs(gDocument, gWindow);
+    document.getElementById("main-window").setAttribute("relatedUrl", docInfo.location);
+
+    makeGeneralTab(pageInfoData.metaViewRows, docInfo);
+    initFeedTab(pageInfoData.feeds);
+    onLoadPermission(uri);
+    securityOnLoad(uri, windowInfo);
+  });
 
-  initFeedTab();
-  onLoadPermission();
+  // Get the media elements from content script to setup the media tab.
+  mm.addMessageListener("PageInfo:mediaData", function onmessage(message){
+    mm.removeMessageListener("PageInfo:mediaData", onmessage);
+    makeMediaTab(message.data.imageViewRows);
+
+    // Loop through onFinished and execute the functions on it.
+    onFinished.forEach(function(func) { func(pageInfoData); });
+  });
 
   /* Call registered overlay init functions */
   onLoadRegistry.forEach(function(func) { func(); });
 }
 
 function resetPageInfo(args)
 {
   /* Reset Meta tags part */
@@ -438,25 +461,32 @@ function showTab(id)
 {
   var deck  = document.getElementById("mainDeck");
   var pagel = document.getElementById(id + "Panel");
   deck.selectedPanel = pagel;
 }
 
 function loadTab(args)
 {
-  if (args && args.doc) {
-    gDocument = args.doc;
-    gWindow = gDocument.defaultView;
+  // If the "View Image Info" context menu item was used, the related image
+  // element is provided as an argument. This can't be a background image.
+  let imageElement = args && args.imageElement;
+  if (imageElement) {
+    gImageElement = {currentSrc: imageElement.currentSrc,
+                     width: imageElement.width, height: imageElement.height,
+                     imageText: imageElement.title || imageElement.alt};
+  }
+  else {
+    gImageElement = null;
   }
 
-  gImageElement = args && args.imageElement;
+  let frameOuterWindowID = args && args.frameOuterWindowID;
 
   /* Load the page info */
-  loadPageInfo();
+  loadPageInfo(frameOuterWindowID);
 
   var initialTab = (args && args.initialTab) || "generalTab";
   var radioGroup = document.getElementById("viewGroup");
   initialTab = document.getElementById(initialTab) || document.getElementById("generalTab");
   radioGroup.selectedItem = initialTab;
   radioGroup.selectedItem.doCommand();
   radioGroup.focus();
 }
@@ -486,127 +516,83 @@ function openCacheEntry(key, cb)
     },
     onCacheEntryAvailable: function(entry, isNew, appCache, status) {
       cb(entry);
     }
   };
   diskStorage.asyncOpenURI(Services.io.newURI(key, null, null), "", nsICacheStorage.OPEN_READONLY, checkCacheListener);
 }
 
-function makeGeneralTab()
+function makeGeneralTab(metaViewRows, docInfo)
 {
-  var title = (gDocument.title) ? gBundle.getFormattedString("pageTitle", [gDocument.title]) : gBundle.getString("noPageTitle");
+  var title = (docInfo.title) ? gBundle.getFormattedString("pageTitle", [docInfo.title]) : gBundle.getString("noPageTitle");
   document.getElementById("titletext").value = title;
 
-  var url = gDocument.location.toString();
+  var url = docInfo.location;
   setItemValue("urltext", url);
 
-  var referrer = ("referrer" in gDocument && gDocument.referrer);
+  var referrer = ("referrer" in docInfo && docInfo.referrer);
   setItemValue("refertext", referrer);
 
-  var mode = ("compatMode" in gDocument && gDocument.compatMode == "BackCompat") ? "generalQuirksMode" : "generalStrictMode";
+  var mode = ("compatMode" in docInfo && docInfo.compatMode == "BackCompat") ? "generalQuirksMode" : "generalStrictMode";
   document.getElementById("modetext").value = gBundle.getString(mode);
 
   // find out the mime type
-  var mimeType = gDocument.contentType;
+  var mimeType = docInfo.contentType;
   setItemValue("typetext", mimeType);
 
   // get the document characterset
-  var encoding = gDocument.characterSet;
+  var encoding = docInfo.characterSet;
   document.getElementById("encodingtext").value = encoding;
 
-  // get the meta tags
-  var metaNodes = gDocument.getElementsByTagName("meta");
-  var length = metaNodes.length;
+  let length = metaViewRows.length;
 
   var metaGroup = document.getElementById("metaTags");
   if (!length)
     metaGroup.collapsed = true;
   else {
     var metaTagsCaption = document.getElementById("metaTagsCaption");
     if (length == 1)
       metaTagsCaption.label = gBundle.getString("generalMetaTag");
     else
       metaTagsCaption.label = gBundle.getFormattedString("generalMetaTags", [length]);
     var metaTree = document.getElementById("metatree");
     metaTree.view = gMetaView;
 
-    for (var i = 0; i < length; i++)
-      gMetaView.addRow([metaNodes[i].name || metaNodes[i].httpEquiv || metaNodes[i].getAttribute("property"),
-                        metaNodes[i].content]);
+    // Add the metaViewRows onto the general tab's meta info tree.
+    gMetaView.addRows(metaViewRows);
 
     metaGroup.collapsed = false;
   }
 
   // get the date of last modification
-  var modifiedText = formatDate(gDocument.lastModified, gStrings.notSet);
+  var modifiedText = formatDate(docInfo.lastModified, gStrings.notSet);
   document.getElementById("modifiedtext").value = modifiedText;
 
   // get cache info
   var cacheKey = url.replace(/#.*$/, "");
   openCacheEntry(cacheKey, function(cacheEntry) {
     var sizeText;
     if (cacheEntry) {
       var pageSize = cacheEntry.dataSize;
       var kbSize = formatNumber(Math.round(pageSize / 1024 * 100) / 100);
       sizeText = gBundle.getFormattedString("generalSize", [kbSize, formatNumber(pageSize)]);
     }
     setItemValue("sizetext", sizeText);
   });
-
-  securityOnLoad();
-}
-
-//******** Generic Build-a-tab
-// Assumes the views are empty. Only called once to build the tabs, and
-// does so by farming the task off to another thread via setTimeout().
-// The actual work is done with a TreeWalker that calls doGrab() once for
-// each element node in the document.
-
-var gFrameList = [ ];
-
-function makeTabs(aDocument, aWindow)
-{
-  goThroughFrames(aDocument, aWindow);
-  processFrames();
 }
 
-function goThroughFrames(aDocument, aWindow)
-{
-  gFrameList.push(aDocument);
-  if (aWindow && aWindow.frames.length > 0) {
-    var num = aWindow.frames.length;
-    for (var i = 0; i < num; i++)
-      goThroughFrames(aWindow.frames[i].document, aWindow.frames[i]);  // recurse through the frames
-  }
-}
-
-function processFrames()
+function makeMediaTab(imageViewRows)
 {
-  if (gFrameList.length) {
-    var doc = gFrameList[0];
-    onProcessFrame.forEach(function(func) { func(doc); });
-    var iterator = doc.createTreeWalker(doc, NodeFilter.SHOW_ELEMENT, grabAll);
-    gFrameList.shift();
-    setTimeout(doGrab, 10, iterator);
-    onFinished.push(selectImage);
+  // Call addImage passing in the image rows to add to the view on the Media Tab.
+  for (let image of imageViewRows) {
+    let [url, type, alt, elem, isBg] = image;
+    addImage(url, type, alt, elem, isBg);
   }
-  else
-    onFinished.forEach(function(func) { func(); });
-}
-
-function doGrab(iterator)
-{
-  for (var i = 0; i < 500; ++i)
-    if (!iterator.nextNode()) {
-      processFrames();
-      return;
-    }
-
-  setTimeout(doGrab, 10, iterator);
+  selectImage();
 }
 
 function addImage(url, type, alt, elem, isBg)
 {
   if (!url)
     return;
 
   if (!gImageHash.hasOwnProperty(url))
@@ -634,88 +620,27 @@ function addImage(url, type, alt, elem, 
       Components.classes["@mozilla.org/observer-service;1"]
                 .getService(Components.interfaces.nsIObserverService)
                 .addObserver(imagePermissionObserver, "perm-changed", false);
     }
   }
   else {
     var i = gImageHash[url][type][alt];
     gImageView.data[i][COL_IMAGE_COUNT]++;
-    if (elem == gImageElement)
+    // The same image can occur several times on the page at different sizes.
+    // If the "View Image Info" context menu item was used, ensure we select
+    // the correct element.
+    if (!gImageView.data[i][COL_IMAGE_BG] &&
+        gImageElement && url == gImageElement.currentSrc &&
+        gImageElement.width == elem.width &&
+        gImageElement.height == elem.height &&
+        gImageElement.imageText == elem.imageText) {
       gImageView.data[i][COL_IMAGE_NODE] = elem;
-  }
-}
-
-function grabAll(elem)
-{
-  // check for images defined in CSS (e.g. background, borders), any node may have multiple
-  var computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, "");
-
-  if (computedStyle) {
-    var addImgFunc = function (label, val) {
-      if (val.primitiveType == CSSPrimitiveValue.CSS_URI) {
-        addImage(val.getStringValue(), label, gStrings.notSet, elem, true);
-      }
-      else if (val.primitiveType == CSSPrimitiveValue.CSS_STRING) {
-        // This is for -moz-image-rect.
-        // TODO: Reimplement once bug 714757 is fixed
-        var strVal = val.getStringValue();
-        if (strVal.search(/^.*url\(\"?/) > -1) {
-          url = strVal.replace(/^.*url\(\"?/,"").replace(/\"?\).*$/,"");
-          addImage(url, label, gStrings.notSet, elem, true);
-        }
-      }
-      else if (val.cssValueType == CSSValue.CSS_VALUE_LIST) {
-        // recursively resolve multiple nested CSS value lists
-        for (var i = 0; i < val.length; i++)
-          addImgFunc(label, val.item(i));
-      }
-    };
-
-    addImgFunc(gStrings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image"));
-    addImgFunc(gStrings.mediaBorderImg, computedStyle.getPropertyCSSValue("border-image-source"));
-    addImgFunc(gStrings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image"));
-    addImgFunc(gStrings.mediaCursor, computedStyle.getPropertyCSSValue("cursor"));
+    }
   }
-
-  // one swi^H^H^Hif-else to rule them all
-  if (elem instanceof HTMLImageElement)
-    addImage(elem.src, gStrings.mediaImg,
-             (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false);
-  else if (elem instanceof SVGImageElement) {
-    try {
-      // Note: makeURLAbsolute will throw if either the baseURI is not a valid URI
-      //       or the URI formed from the baseURI and the URL is not a valid URI
-      var href = makeURLAbsolute(elem.baseURI, elem.href.baseVal);
-      addImage(href, gStrings.mediaImg, "", elem, false);
-    } catch (e) { }
-  }
-  else if (elem instanceof HTMLVideoElement) {
-    addImage(elem.currentSrc, gStrings.mediaVideo, "", elem, false);
-  }
-  else if (elem instanceof HTMLAudioElement) {
-    addImage(elem.currentSrc, gStrings.mediaAudio, "", elem, false);
-  }
-  else if (elem instanceof HTMLLinkElement) {
-    if (elem.rel && /\bicon\b/i.test(elem.rel))
-      addImage(elem.href, gStrings.mediaLink, "", elem, false);
-  }
-  else if (elem instanceof HTMLInputElement || elem instanceof HTMLButtonElement) {
-    if (elem.type.toLowerCase() == "image")
-      addImage(elem.src, gStrings.mediaInput,
-               (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false);
-  }
-  else if (elem instanceof HTMLObjectElement)
-    addImage(elem.data, gStrings.mediaObject, getValueText(elem), elem, false);
-  else if (elem instanceof HTMLEmbedElement)
-    addImage(elem.src, gStrings.mediaEmbed, "", elem, false);
-
-  onProcessElement.forEach(function(func) { func(elem); });
-
-  return NodeFilter.FILTER_ACCEPT;
 }
 
 //******** Link Stuff
 function openURL(target)
 {
   var url = target.parentNode.childNodes[2].value;
   window.open(url, "_blank", "chrome");
 }
@@ -809,24 +734,25 @@ function saveMedia()
     if (url) {
       var titleKey = "SaveImageTitle";
 
       if (item instanceof HTMLVideoElement)
         titleKey = "SaveVideoTitle";
       else if (item instanceof HTMLAudioElement)
         titleKey = "SaveAudioTitle";
 
-      saveURL(url, null, titleKey, false, false, makeURI(item.baseURI), gDocument);
+      saveURL(url, null, titleKey, false, false, makeURI(item.baseURI),
+              null, gDocInfo.isContentWindowPrivate);
     }
   } else {
     selectSaveFolder(function(aDirectory) {
       if (aDirectory) {
         var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
           internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
-                       aChosenData, aBaseURI, gDocument);
+                       aChosenData, aBaseURI, null, gDocInfo.isContentWindowPrivate);
         };
 
         for (var i = 0; i < rowArray.length; i++) {
           var v = rowArray[i];
           var dir = aDirectory.clone();
           var item = gImageView.data[v][COL_IMAGE_NODE];
           var uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
           var uri = makeURI(uriString);
@@ -888,37 +814,27 @@ function onImageSelect()
     mediaSaveBox.collapsed = true;
     splitter.collapsed     = false;
     previewBox.collapsed   = false;
     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);
-
-  var imageText;
-  if (!isBG &&
-      !(item instanceof SVGImageElement) &&
-      !(gDocument instanceof ImageDocument)) {
-    imageText = item.title || item.alt;
-
-    if (!imageText && !(item instanceof HTMLImageElement))
-      imageText = getValueText(item);
-  }
-  setItemValue("imagetext", imageText);
-
+  setItemValue("imagetext", item.imageText);
   setItemValue("imagelongdesctext", item.longDesc);
 
   // get cache info
   var cacheKey = url.replace(/#.*$/, "");
   openCacheEntry(cacheKey, function(cacheEntry) {
     // find out the file size
     var sizeText;
     if (cacheEntry) {
@@ -926,42 +842,18 @@ function makePreview(row)
       var kbSize = Math.round(imageSize / 1024 * 100) / 100;
       sizeText = gBundle.getFormattedString("generalSize",
                                             [formatNumber(kbSize), formatNumber(imageSize)]);
     }
     else
       sizeText = gBundle.getString("mediaUnknownNotCached");
     setItemValue("imagesizetext", sizeText);
 
-    var mimeType;
-    var numFrames = 1;
-    if (item instanceof HTMLObjectElement ||
-        item instanceof HTMLEmbedElement ||
-        item instanceof HTMLLinkElement)
-      mimeType = item.type;
-
-    if (!mimeType && !isBG && item instanceof nsIImageLoadingContent) {
-      var imageRequest = item.getRequest(nsIImageLoadingContent.CURRENT_REQUEST);
-      if (imageRequest) {
-        mimeType = imageRequest.mimeType;
-        var image = imageRequest.image;
-        if (image)
-          numFrames = image.numFrames;
-      }
-    }
-
-    if (!mimeType)
-      mimeType = getContentTypeFromHeaders(cacheEntry);
-
-    // if we have a data url, get the MIME type from the url
-    if (!mimeType && url.startsWith("data:")) {
-      let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
-      if (dataMimeType)
-        mimeType = dataMimeType[1].toLowerCase();
-    }
+    var mimeType = item.mimeType || this.getContentTypeFromHeaders(cacheEntry);
+    var numFrames = item.numFrames;
 
     var imageType;
     if (mimeType) {
       // We found the type, try to display it nicely
       let imageMimeType = /^image\/(.*)/i.exec(mimeType);
       if (imageMimeType) {
         imageType = imageMimeType[1].toUpperCase();
         if (numFrames > 1)
@@ -986,20 +878,20 @@ function makePreview(row)
 
     var isProtocolAllowed = checkProtocol(gImageView.data[row]);
 
     var newImage = new Image;
     newImage.id = "thepreviewimage";
     var physWidth = 0, physHeight = 0;
     var width = 0, height = 0;
 
-    if ((item instanceof HTMLLinkElement || item instanceof HTMLInputElement ||
-         item instanceof HTMLImageElement ||
-         item instanceof SVGImageElement ||
-         (item instanceof HTMLObjectElement && mimeType && mimeType.startsWith("image/")) || isBG) && isProtocolAllowed) {
+    if ((item.HTMLLinkElement || item.HTMLInputElement ||
+         item.HTMLImageElement || item.SVGImageElement ||
+         (item.HTMLObjectElement && mimeType && mimeType.startsWith("image/")) ||
+         isBG) && isProtocolAllowed) {
       newImage.setAttribute("src", url);
       physWidth = newImage.width || 0;
       physHeight = newImage.height || 0;
 
       // "width" and "height" attributes must be set to newImage,
       // even if there is no "width" or "height attribute in item;
       // otherwise, the preview image cannot be displayed correctly.
       if (!isBG) {
@@ -1008,39 +900,39 @@ function makePreview(row)
       }
       else {
         // the Width and Height of an HTML tag should not be used for its background image
         // (for example, "table" can have "width" or "height" attributes)
         newImage.width = newImage.naturalWidth;
         newImage.height = newImage.naturalHeight;
       }
 
-      if (item instanceof SVGImageElement) {
-        newImage.width = item.width.baseVal.value;
-        newImage.height = item.height.baseVal.value;
+      if (item.SVGImageElement) {
+        newImage.width = item.SVGImageElementWidth;
+        newImage.height = item.SVGImageElementHeight;
       }
 
       width = newImage.width;
       height = newImage.height;
 
       document.getElementById("theimagecontainer").collapsed = false
       document.getElementById("brokenimagecontainer").collapsed = true;
     }
-    else if (item instanceof HTMLVideoElement && isProtocolAllowed) {
+    else if (item.HTMLVideoElement && isProtocolAllowed) {
       newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
       newImage.id = "thepreviewimage";
       newImage.src = url;
       newImage.controls = true;
       width = physWidth = item.videoWidth;
       height = physHeight = item.videoHeight;
 
       document.getElementById("theimagecontainer").collapsed = false;
       document.getElementById("brokenimagecontainer").collapsed = true;
     }
-    else if (item instanceof HTMLAudioElement && isProtocolAllowed) {
+    else if (item.HTMLAudioElement && isProtocolAllowed) {
       newImage = new Audio;
       newImage.id = "thepreviewimage";
       newImage.src = url;
       newImage.controls = true;
       isAudio = true;
 
       document.getElementById("theimagecontainer").collapsed = false;
       document.getElementById("brokenimagecontainer").collapsed = true;
@@ -1068,18 +960,16 @@ function makePreview(row)
       }
     }
     setItemValue("imagedimensiontext", imageSize);
 
     makeBlockImage(url);
 
     imageContainer.removeChild(oldImage);
     imageContainer.appendChild(newImage);
-
-    onImagePreviewShown.forEach(function(func) { func(); });
   });
 }
 
 function makeBlockImage(url)
 {
   var permissionManager = Components.classes[PERMISSION_CONTRACTID]
                                     .getService(nsIPermissionManager);
   var prefs = Components.classes[PREFERENCES_CONTRACTID]
@@ -1125,79 +1015,19 @@ var imagePermissionObserver = {
   }
 }
 
 function getContentTypeFromHeaders(cacheEntryDescriptor)
 {
   if (!cacheEntryDescriptor)
     return null;
 
-  return (/^Content-Type:\s*(.*?)\s*(?:\;|$)/mi
-          .exec(cacheEntryDescriptor.getMetaDataElement("response-head")))[1];
-}
-
-//******** Other Misc Stuff
-// Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
-// parse a node to extract the contents of the node
-function getValueText(node)
-{
-  var valueText = "";
-
-  // form input elements don't generally contain information that is useful to our callers, so return nothing
-  if (node instanceof HTMLInputElement ||
-      node instanceof HTMLSelectElement ||
-      node instanceof HTMLTextAreaElement)
-    return valueText;
-
-  // otherwise recurse for each child
-  var length = node.childNodes.length;
-  for (var i = 0; i < length; i++) {
-    var childNode = node.childNodes[i];
-    var nodeType = childNode.nodeType;
-
-    // text nodes are where the goods are
-    if (nodeType == Node.TEXT_NODE)
-      valueText += " " + childNode.nodeValue;
-    // and elements can have more text inside them
-    else if (nodeType == Node.ELEMENT_NODE) {
-      // images are special, we want to capture the alt text as if the image weren't there
-      if (childNode instanceof HTMLImageElement)
-        valueText += " " + getAltText(childNode);
-      else
-        valueText += " " + getValueText(childNode);
-    }
-  }
-
-  return stripWS(valueText);
-}
-
-// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
-// traverse the tree in search of an img or area element and grab its alt tag
-function getAltText(node)
-{
-  var altText = "";
-
-  if (node.alt)
-    return node.alt;
-  var length = node.childNodes.length;
-  for (var i = 0; i < length; i++)
-    if ((altText = getAltText(node.childNodes[i]) != undefined))  // stupid js warning...
-      return altText;
-  return "";
-}
-
-// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
-// strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space
-function stripWS(text)
-{
-  var middleRE = /\s+/g;
-  var endRE = /(^\s+)|(\s+$)/g;
-
-  text = text.replace(middleRE, " ");
-  return text.replace(endRE, "");
+  let headers = cacheEntryDescriptor.getMetaDataElement("response-head");
+  let type = /^Content-Type:\s*(.*?)\s*(?:\;|$)/mi.exec(headers);
+  return type && type[1];
 }
 
 function setItemValue(id, value)
 {
   var item = document.getElementById(id);
   if (value) {
     item.parentNode.collapsed = false;
     item.value = value;
@@ -1276,18 +1106,23 @@ function doSelectAll()
 
 function selectImage()
 {
   if (!gImageElement)
     return;
 
   var tree = document.getElementById("imagetree");
   for (var i = 0; i < tree.view.rowCount; i++) {
-    if (gImageElement == gImageView.data[i][COL_IMAGE_NODE] &&
-        !gImageView.data[i][COL_IMAGE_BG]) {
+    // If the image row element is the image selected from the "View Image Info" context menu item.
+    let image = gImageView.data[i][COL_IMAGE_NODE];
+    if (!gImageView.data[i][COL_IMAGE_BG] &&
+        gImageElement.currentSrc == gImageView.data[i][COL_IMAGE_ADDRESS] &&
+        gImageElement.width == image.width &&
+        gImageElement.height == image.height &&
+        gImageElement.imageText == image.imageText) {
       tree.view.selection.select(i);
       tree.treeBoxObject.ensureRowIsVisible(i);
       tree.focus();
       return;
     }
   }
 }
 
--- a/browser/base/content/pageinfo/permissions.js
+++ b/browser/base/content/pageinfo/permissions.js
@@ -23,19 +23,18 @@ var permissionObserver = {
           initRow(permission.type);
         else if (permission.type.startsWith("plugin"))
           setPluginsRadioState();
       }
     }
   }
 };
 
-function onLoadPermission()
+function onLoadPermission(uri)
 {
-  var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
   var permTab = document.getElementById("permTab");
   if (SitePermissions.isSupportedURI(uri)) {
     gPermURI = uri;
     var hostText = document.getElementById("hostText");
     hostText.value = gPermURI.prePath;
 
     for (var i of gPermissions)
       initRow(i);
--- a/browser/base/content/pageinfo/security.js
+++ b/browser/base/content/pageinfo/security.js
@@ -1,39 +1,43 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
+                                  "resource://gre/modules/LoginHelper.jsm");
+
 var security = {
+  init: function(uri, windowInfo) {
+    this.uri = uri;
+    this.windowInfo = windowInfo;
+  },
+
   // 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 (gWindow != gWindow.top)
+    if (!this.windowInfo.isTopWindow)
       return null;
 
-    var hName = null;
-    try {
-      hName = gWindow.location.host;
-    }
-    catch (exception) { }
+    var hostName = this.windowInfo.hostName;
 
     var ui = security._getSecurityUI();
     if (!ui)
       return null;
 
     var isBroken =
       (ui.state & Components.interfaces.nsIWebProgressListener.STATE_IS_BROKEN);
     var isMixed =
@@ -48,26 +52,25 @@ var security = {
 
     if (!isInsecure && status) {
       status.QueryInterface(nsISSLStatus);
       var cert = status.serverCert;
       var issuerName =
         this.mapIssuerOrganization(cert.issuerOrganization) || cert.issuerName;
 
       var retval = {
-        hostName : hName,
+        hostName : hostName,
         cAName : issuerName,
         encryptionAlgorithm : undefined,
         encryptionStrength : undefined,
         version: undefined,
         isBroken : isBroken,
         isMixed : isMixed,
         isEV : isEV,
-        cert : cert,
-        fullLocation : gWindow.location
+        cert : cert
       };
 
       var version;
       try {
         retval.encryptionAlgorithm = status.cipherName;
         retval.encryptionStrength = status.secretKeyLength;
         version = status.protocolVersion;
       }
@@ -87,26 +90,26 @@ var security = {
         case nsISSLStatus.TLS_VERSION_1_2:
           retval.version = "TLS 1.2"
           break;
       }
 
       return retval;
     } else {
       return {
-        hostName : hName,
+        hostName : hostName,
         cAName : "",
         encryptionAlgorithm : "",
         encryptionStrength : 0,
         version: "",
         isBroken : isBroken,
         isMixed : isMixed,
         isEV : isEV,
-        cert : null,
-        fullLocation : gWindow.location
+        cert : null
+
       };
     }
   },
 
   // Find the secureBrowserUI object (if present)
   _getSecurityUI : function() {
     if (window.opener.gBrowser)
       return window.opener.gBrowser.securityUI;
@@ -132,56 +135,46 @@ var security = {
   {
     var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                        .getService(Components.interfaces.nsIWindowMediator);
     var win = wm.getMostRecentWindow("Browser:Cookies");
     var eTLDService = Components.classes["@mozilla.org/network/effective-tld-service;1"].
                       getService(Components.interfaces.nsIEffectiveTLDService);
 
     var eTLD;
-    var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
     try {
-      eTLD = eTLDService.getBaseDomain(uri);
+      eTLD = eTLDService.getBaseDomain(this.uri);
     }
     catch (e) {
       // getBaseDomain will fail if the host is an IP address or is empty
-      eTLD = uri.asciiHost;
+      eTLD = this.uri.asciiHost;
     }
 
     if (win) {
       win.gCookiesWindow.setFilter(eTLD);
       win.focus();
     }
     else
       window.openDialog("chrome://browser/content/preferences/cookies.xul",
                         "Browser:Cookies", "", {filterString : eTLD});
   },
 
   /**
    * Open the login manager window
    */
-  viewPasswords : function()
-  {
-    var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                       .getService(Components.interfaces.nsIWindowMediator);
-    var win = wm.getMostRecentWindow("Toolkit:PasswordManager");
-    if (win) {
-      win.setFilter(this._getSecurityInfo().hostName);
-      win.focus();
-    }
-    else
-      window.openDialog("chrome://passwordmgr/content/passwordManager.xul",
-                        "Toolkit:PasswordManager", "",
-                        {filterString : this._getSecurityInfo().hostName});
+  viewPasswords : function() {
+    LoginHelper.openPasswordManager(window, this._getSecurityInfo().hostName);
   },
 
   _cert : null
 };
 
-function securityOnLoad() {
+function securityOnLoad(uri, windowInfo) {
+  security.init(uri, windowInfo);
+
   var info = security._getSecurityInfo();
   if (!info) {
     document.getElementById("securityTab").hidden = true;
     return;
   }
   else {
     document.getElementById("securityTab").hidden = false;
   }
@@ -229,17 +222,16 @@ function securityOnLoad() {
   }
   else
     viewCert.collapsed = true;
 
   /* Set Privacy & History section text */
   var yesStr = pageInfoBundle.getString("yes");
   var noStr = pageInfoBundle.getString("no");
 
-  var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
   setText("security-privacy-cookies-value",
           hostHasCookies(uri) ? yesStr : noStr);
   setText("security-privacy-passwords-value",
           realmHasPasswords(uri) ? yesStr : noStr);
   
   var visitCount = previousVisitCount(info.hostName);
   if(visitCount > 1) {
     setText("security-privacy-history-value",
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -25,17 +25,16 @@ XPCOMUtils.defineLazyGetter(this, "Simpl
   // Register targets
   ssdp.registerDevice({
     id: "roku:ecp",
     target: "roku:ecp",
     factory: function(aService) {
       Cu.import("resource://gre/modules/RokuApp.jsm");
       return new RokuApp(aService);
     },
-    mirror: true,
     types: ["video/mp4"],
     extensions: ["mp4"]
   });
   return ssdp;
 });
 
 // TabChildGlobal
 var global = this;
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1,19 +1,14 @@
 <?xml version="1.0"?>
 
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
-<!DOCTYPE bindings [
-<!ENTITY % tabBrowserDTD SYSTEM "chrome://browser/locale/tabbrowser.dtd" >
-%tabBrowserDTD;
-]>
-
 <bindings id="tabBrowserBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="tabbrowser">
     <resources>
       <stylesheet src="chrome://browser/content/tabbrowser.css"/>
@@ -3427,16 +3422,23 @@
             startTabSwitch: function () {
               TelemetryStopwatch.cancel("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
               TelemetryStopwatch.start("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
               this.addMarker("AsyncTabSwitch:Start");
             },
 
             finishTabSwitch: function () {
               if (this.requestedTab && this.getTabState(this.requestedTab) == this.STATE_LOADED) {
+                // After this point the tab has switched from the content thread's point of view.
+                // The changes will be visible after the next refresh driver tick + composite.
+                let event = new CustomEvent("TabSwitched", {
+                  bubbles: true,
+                  cancelable: true
+                });
+                this.tabbrowser.dispatchEvent(event);
                 let time = TelemetryStopwatch.timeElapsed("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
                 if (time != -1) {
                   TelemetryStopwatch.finish("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
                   this.log("DEBUG: tab switch time = " + time);
                   this.addMarker("AsyncTabSwitch:Finish");
                 }
               }
             },
@@ -3859,21 +3861,31 @@
         <parameter name="event"/>
         <body><![CDATA[
           event.stopPropagation();
           var tab = document.tooltipNode;
           if (tab.localName != "tab") {
             event.preventDefault();
             return;
           }
-          event.target.setAttribute("label",
-                                    tab.mOverCloseButton ?
-                                    tab.getAttribute("closetabtext") :
-                                    tab.getAttribute("label") +
-                                      (this.AppConstants.E10S_TESTING_ONLY && tab.linkedBrowser && tab.linkedBrowser.isRemoteBrowser ? " - e10s" : ""));
+          var stringID, label;
+          if (tab.mOverCloseButton) {
+            stringID = "tabs.closeTab.tooltip";
+          } else if (tab._overPlayingIcon) {
+            stringID = tab.linkedBrowser.audioMuted ?
+              "tabs.mutedAudio.tooltip" :
+              "tabs.playingAudio.tooltip";
+          } else {
+            label = tab.getAttribute("label") +
+                      (this.AppConstants.E10S_TESTING_ONLY && tab.linkedBrowser && tab.linkedBrowser.isRemoteBrowser ? " - e10s" : "");
+          }
+          if (stringID && !label) {
+            label = this.mStringBundle.getString(stringID);
+          }
+          event.target.setAttribute("label", label);
         ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
           switch (aEvent.type) {
             case "keydown":
@@ -4225,16 +4237,50 @@
           browser.setAttribute("crashedPageTitle", title);
           browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
           browser.removeAttribute("crashedPageTitle");
           let tab = this.getTabForBrowser(browser);
           tab.setAttribute("crashed", true);
           this.setIcon(tab, icon);
         ]]>
       </handler>
+      <handler event="DOMMediaPlaybackStarted">
+        <![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")) {
+            tab.setAttribute("soundplaying", true);
+            this._tabAttrModified(tab, ["soundplaying"]);
+          }
+        ]]>
+      </handler>
+      <handler event="DOMMediaPlaybackStopped">
+        <![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")) {
+            tab.removeAttribute("soundplaying");
+            this._tabAttrModified(tab, ["soundplaying"]);
+          }
+        ]]>
+      </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-tabbox"
            extends="chrome://global/content/bindings/tabbox.xml#tabbox">
     <implementation>
       <property name="tabs" readonly="true"
                 onget="return document.getBindingParent(this).tabContainer;"/>
@@ -5617,17 +5663,17 @@
   </binding>
 
   <binding id="tabbrowser-tab" display="xul:hbox"
            extends="chrome://global/content/bindings/tabbox.xml#tab">
     <resources>
       <stylesheet src="chrome://browser/content/tabbrowser.css"/>
     </resources>
 
-    <content context="tabContextMenu" closetabtext="&closeTab.label;">
+    <content context="tabContextMenu">
       <xul:stack class="tab-stack" flex="1">
         <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged,fadein"
                   class="tab-background">
           <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
                     class="tab-background-start"/>
           <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
                     class="tab-background-middle"/>
           <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
@@ -5639,24 +5685,29 @@
                      class="tab-throbber"
                      role="presentation"
                      layer="true" />
           <xul:image xbl:inherits="src=image,fadein,pinned,selected,visuallyselected,busy,crashed"
                      anonid="tab-icon-image"
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
-          <xul:image xbl:inherits="crashed,busy"
+          <xul:image xbl:inherits="crashed,busy,soundplaying,pinned,muted"
+                     anonid="overlay-icon"
                      class="tab-icon-overlay"
                      role="presentation"/>
           <xul:label flex="1"
                      anonid="tab-label"
                      xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected,visuallyselected"
                      class="tab-text tab-label"
                      role="presentation"/>
+          <xul:image xbl:inherits="soundplaying,pinned,muted"
+                     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>
     </content>
 
     <implementation>
@@ -5759,16 +5810,17 @@
           return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
         </getter>
         <setter>
           this._lastAccessed = val;
         </setter>
       </property>
 
       <field name="mOverCloseButton">false</field>
+      <field name="_overPlayingIcon">false</field>
       <field name="mCorrespondingMenuitem">null</field>
 
       <!--
       While it would make sense to track this in a field, the field will get nuked
       once the node is gone from the DOM, which causes us to think the tab is not
       closed, which causes us to make wrong decisions. So we use an expando instead.
       <field name="closing">false</field>
       -->
@@ -5815,50 +5867,87 @@
           if (tabContainer._afterHoveredTab) {
             tabContainer._afterHoveredTab.removeAttribute("afterhovered");
             tabContainer._afterHoveredTab = null;
           }
 
           tabContainer._hoveredTab = null;
         ]]></body>
       </method>
+
+      <method name="_toggleMuteAudio">
+        <body>
+        <![CDATA[
+          let tabContainer = this.parentNode;
+          let browser = this.linkedBrowser;
+          if (browser.audioMuted) {
+            browser.unmute();
+            this.removeAttribute("muted");
+          } else {
+            browser.mute();
+            this.setAttribute("muted", "true");
+          }
+          tabContainer.tabbrowser._tabAttrModified(this, ["muted"]);
+        ]]>
+        </body>
+      </method>
     </implementation>
 
     <handlers>
       <handler event="mouseover"><![CDATA[
         let anonid = event.originalTarget.getAttribute("anonid");
         if (anonid == "close-button")
           this.mOverCloseButton = true;
+        else if ((anonid == "soundplaying-icon") ||
+                 ((anonid == "overlay-icon") && this.hasAttribute("soundplaying")))
+          this._overPlayingIcon = true;
 
         this._mouseenter();
       ]]></handler>
       <handler event="mouseout"><![CDATA[
         let anonid = event.originalTarget.getAttribute("anonid");
         if (anonid == "close-button")
           this.mOverCloseButton = false;
+        else if ((anonid == "soundplaying-icon") ||
+                 ((anonid == "overlay-icon") && this.hasAttribute("soundplaying")))
+          this._overPlayingIcon = false;
 
         this._mouseleave();
       ]]></handler>
       <handler event="dragstart" phase="capturing">
         this.style.MozUserFocus = '';
       </handler>
       <handler event="mousedown" phase="capturing">
       <![CDATA[
         if (this.selected) {
           this.style.MozUserFocus = 'ignore';
           this.clientTop; // just using this to flush style updates
-        } else if (this.mOverCloseButton) {
+        } else if (this.mOverCloseButton ||
+                   this._overPlayingIcon) {
           // Prevent tabbox.xml from selecting the tab.
           event.stopPropagation();
         }
       ]]>
       </handler>
       <handler event="mouseup">
         this.style.MozUserFocus = '';
       </handler>
+      <handler event="click">
+      <![CDATA[
+        if (event.button != 0) {
+          return;
+        }
+
+        let anonid = event.originalTarget.getAttribute("anonid");
+        if ((anonid == "soundplaying-icon") ||
+            ((anonid == "overlay-icon") && this.hasAttribute("soundplaying"))) {
+          this._toggleMuteAudio();
+        }
+      ]]>
+      </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-alltabs-popup"
            extends="chrome://global/content/bindings/popup.xml#popup">
     <implementation implements="nsIDOMEventListener">
       <method name="_tabOnAttrModified">
         <parameter name="aEvent"/>
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -2,16 +2,17 @@
 support-files =
   POSTSearchEngine.xml
   accounts_testRemoteCommands.html
   alltabslistener.html
   app_bug575561.html
   app_subframe_bug575561.html
   authenticate.sjs
   aboutHome_content_script.js
+  audio.ogg
   browser_bug479408_sample.html
   browser_bug678392-1.html
   browser_bug678392-2.html
   browser_bug970746.xhtml
   browser_fxa_oauth.html
   browser_fxa_oauth_with_keys.html
   browser_fxa_web_channel.html
   browser_registerProtocolHandler_notification.html
@@ -45,16 +46,17 @@ support-files =
   file_bug902156_2.html
   file_bug902156_3.html
   file_bug906190_1.html
   file_bug906190_2.html
   file_bug906190_3_4.html
   file_bug906190_redirected.html
   file_bug906190.js
   file_bug906190.sjs
+  file_mediaPlayback.html
   file_mixedContentFromOnunload.html
   file_mixedContentFromOnunload_test1.html
   file_mixedContentFromOnunload_test2.html
   file_bug970276_popup1.html
   file_bug970276_popup2.html
   file_bug970276_favicon1.ico
   file_bug970276_favicon2.ico
   file_documentnavigation_frameset.html
@@ -126,19 +128,21 @@ skip-if = os == "linux" # Bug 924307
 skip-if = e10s # Bug 1093153 - no about:home support yet
 [browser_action_keyword.js]
 [browser_action_keyword_override.js]
 [browser_action_searchengine.js]
 [browser_action_searchengine_alias.js]
 [browser_addKeywordSearch.js]
 [browser_search_favicon.js]
 [browser_alltabslistener.js]
+[browser_audioTabIcon.js]
 [browser_autocomplete_a11y_label.js]
 skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir (works on its own)
 [browser_autocomplete_cursor.js]
+[browser_autocomplete_edit_completed.js]
 [browser_autocomplete_enter_race.js]
 [browser_autocomplete_no_title.js]
 [browser_autocomplete_autoselect.js]
 [browser_autocomplete_oldschool_wrap.js]
 [browser_autocomplete_tag_star_visibility.js]
 [browser_backButtonFitts.js]
 skip-if = os == "mac" # The Fitt's Law back button is not supported on OS X
 [browser_beforeunload_duplicate_dialogs.js]
@@ -149,17 +153,16 @@ skip-if = buildapp == 'mulet' || toolkit
 [browser_bug304198.js]
 [browser_bug321000.js]
 skip-if = true # browser_bug321000.js is disabled because newline handling is shaky (bug 592528)
 [browser_bug329212.js]
 [browser_bug331772_xul_tooltiptext_in_html.js]
 [browser_bug356571.js]
 [browser_bug380960.js]
 [browser_bug386835.js]
-[browser_bug405137.js]
 [browser_bug406216.js]
 [browser_bug409481.js]
 [browser_bug409624.js]
 [browser_bug413915.js]
 [browser_bug416661.js]
 [browser_bug417483.js]
 [browser_bug419612.js]
 [browser_bug422590.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_audioTabIcon.js
@@ -0,0 +1,133 @@
+const PAGE = "https://example.com/browser/browser/base/content/test/general/file_mediaPlayback.html";
+
+function* wait_for_tab_playing_event(tab, expectPlaying) {
+  yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
+    if (event.detail.changed.indexOf("soundplaying") >= 0) {
+      is(tab.hasAttribute("soundplaying"), expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
+      return true;
+    }
+    return false;
+  });
+}
+
+function disable_non_test_mouse(disable) {
+  let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIDOMWindowUtils);
+  utils.disableNonTestMouseEvents(disable);
+}
+
+function* hover_icon(icon, tooltip) {
+  disable_non_test_mouse(true);
+
+  let popupShownPromise = BrowserTestUtils.waitForEvent(tooltip, "popupshown");
+  EventUtils.synthesizeMouse(icon, 1, 1, {type: "mouseover"});
+  EventUtils.synthesizeMouse(icon, 2, 2, {type: "mousemove"});
+  EventUtils.synthesizeMouse(icon, 3, 3, {type: "mousemove"});
+  EventUtils.synthesizeMouse(icon, 4, 4, {type: "mousemove"});
+  return popupShownPromise;
+}
+
+function leave_icon(icon) {
+  EventUtils.synthesizeMouse(icon, 0, 0, {type: "mouseout"});
+  EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
+  EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
+  EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
+
+  disable_non_test_mouse(false);
+}
+
+function* test_tooltip(icon, expectedTooltip) {
+  let tooltip = document.getElementById("tabbrowser-tab-tooltip");
+
+  yield hover_icon(icon, tooltip);
+  is(tooltip.getAttribute("label"), expectedTooltip, "Correct tooltip expected");
+  leave_icon(icon);
+}
+
+function* test_mute_tab(tab, icon, expectMuted) {
+  let mutedPromise = BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
+    if (event.detail.changed.indexOf("muted") >= 0) {
+      is(tab.hasAttribute("muted"), expectMuted, "The tab should " + (expectMuted ? "" : "not ") + "be muted");
+      return true;
+    }
+    return false;
+  });
+
+  let activeTab = gBrowser.selectedTab;
+
+  let tooltip = document.getElementById("tabbrowser-tab-tooltip");
+
+  yield hover_icon(icon, tooltip);
+  EventUtils.synthesizeMouseAtCenter(icon, {button: 0});
+  leave_icon(icon);
+
+  is(gBrowser.selectedTab, activeTab, "Clicking on mute should not change the currently selected tab");
+
+  return mutedPromise;
+}
+
+function* test_playing_icon_on_tab(tab, browser, isPinned) {
+  let icon = document.getAnonymousElementByAttribute(tab, "anonid",
+                                                     isPinned ? "overlay-icon" : "soundplaying-icon");
+
+  yield ContentTask.spawn(browser, {}, function* () {
+    let audio = content.document.querySelector("audio");
+    audio.play();
+  });
+
+  yield wait_for_tab_playing_event(tab, true);
+
+  yield test_tooltip(icon, "This tab is playing audio");
+
+  yield test_mute_tab(tab, icon, true);
+
+  yield test_tooltip(icon, "This tab has been muted");
+
+  yield test_mute_tab(tab, icon, false);
+
+  yield test_tooltip(icon, "This tab is playing audio");
+
+  yield ContentTask.spawn(browser, {}, function* () {
+    let audio = content.document.querySelector("audio");
+    audio.pause();
+  });
+  yield wait_for_tab_playing_event(tab, false);
+}
+
+function* test_on_browser(browser) {
+  let tab = gBrowser.getTabForBrowser(browser);
+
+  // Test the icon in a normal tab.
+  yield test_playing_icon_on_tab(tab, browser, false);
+
+  gBrowser.pinTab(tab);
+
+  // Test the icon in a pinned tab.
+  yield test_playing_icon_on_tab(tab, browser, true);
+
+  gBrowser.unpinTab(tab);
+
+  // Retest with another browser in the foreground tab
+  if (gBrowser.selectedBrowser.currentURI.spec == PAGE) {
+    yield BrowserTestUtils.withNewTab({
+      gBrowser,
+      url: "data:text/html,test"
+    }, () => test_on_browser(browser));
+  }
+}
+
+add_task(function*() {
+  yield new Promise((resolve) => {
+    SpecialPowers.pushPrefEnv({"set": [
+                                ["media.useAudioChannelService", true],
+                                ["browser.tabs.showAudioPlayingIcon", true],
+                              ]}, resolve);
+  });
+});
+
+add_task(function* test_page() {
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: PAGE
+  }, test_on_browser);
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_autocomplete_edit_completed.js
@@ -0,0 +1,53 @@
+add_task(function*() {
+  yield PlacesTestUtils.clearHistory();
+
+  yield PlacesTestUtils.addVisits([
+    { uri: makeURI("http://example.com/foo") },
+    { uri: makeURI("http://example.com/foo/bar") },
+  ]);
+
+  Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
+  yield* do_test();
+  Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", false);
+  yield* do_test();
+});
+
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("browser.urlbar.unifiedcomplete");
+  yield PlacesTestUtils.clearHistory();
+});
+
+function* do_test() {
+  gBrowser.selectedTab = gBrowser.addTab("about:blank");
+  gURLBar.focus();
+
+  yield promiseAutocompleteResultPopup("http://example.com");
+
+  let popup = gURLBar.popup;
+  let list = popup.richlistbox;
+  let initialIndex = list.selectedIndex;
+
+  info("Key Down to select the next item.");
+  EventUtils.synthesizeKey("VK_DOWN", {});
+
+  let nextIndex = initialIndex + 1;
+  let nextValue = gURLBar.controller.getFinalCompleteValueAt(nextIndex);
+  is(list.selectedIndex, nextIndex, "The next item is selected.");
+  is(gURLBar.value, nextValue, "The selected URL is completed.");
+
+  info("Press backspace");
+  EventUtils.synthesizeKey("VK_BACK_SPACE", {});
+  yield promiseSearchComplete();
+
+  let editedValue = gURLBar.value;
+  is(list.selectedIndex, initialIndex, "The initial index is selected again.");
+  isnot(editedValue, nextValue, "The URL has changed.");
+
+  info("Press return to load edited URL.");
+  EventUtils.synthesizeKey("VK_RETURN", {});
+  yield Promise.all([
+    promisePopupHidden(gURLBar.popup),
+    waitForDocLoadAndStopIt("http://" + editedValue)]);
+
+  gBrowser.removeTab(gBrowser.selectedTab);
+}
deleted file mode 100644
--- a/browser/base/content/test/general/browser_bug405137.js
+++ /dev/null
@@ -1,5 +0,0 @@
-function test(){
-  var tab = gBrowser.addTab();
-  ok(tab.getAttribute("closetabtext") != "", "tab has non-empty closetabtext");
-  gBrowser.removeTab(tab);  
-}
--- a/browser/base/content/test/general/browser_bug517902.js
+++ b/browser/base/content/test/general/browser_bug517902.js
@@ -9,17 +9,17 @@ function test() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
 
     var doc = gBrowser.contentDocument;
     var testImg = doc.getElementById("test-image");
     var pageInfo = BrowserPageInfo(doc, "mediaTab", testImg);
 
     pageInfo.addEventListener("load", function () {
       pageInfo.removeEventListener("load", arguments.callee, true);
-      pageInfo.onImagePreviewShown.push(function () {
+      pageInfo.onFinished.push(function () {
         executeSoon(function () {
           var pageInfoImg = pageInfo.document.getElementById("thepreviewimage");
 
           is(pageInfoImg.src, testImg.src, "selected image has the correct source");
           is(pageInfoImg.width, testImg.width, "selected image has the correct width");
           is(pageInfoImg.height, testImg.height, "selected image has the correct height");
 
           pageInfo.close();
--- a/browser/base/content/test/general/browser_bug537013.js
+++ b/browser/base/content/test/general/browser_bug537013.js
@@ -10,17 +10,18 @@ let texts = [
   "Klein bottle for sale. Inquire within.",
   "To err is human; to forgive is not company policy."
 ];
 
 let Clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
 let HasFindClipboard = Clipboard.supportsFindClipboard();
 
 function addTabWithText(aText, aCallback) {
-  let newTab = gBrowser.addTab("data:text/html,<h1 id='h1'>" + aText + "</h1>");
+  let newTab = gBrowser.addTab("data:text/html;charset=utf-8,<h1 id='h1'>" +
+                               aText + "</h1>");
   tabs.push(newTab);
   gBrowser.selectedTab = newTab;
 }
 
 function setFindString(aString) {
   gFindBar.open();
   gFindBar._findField.focus();
   gFindBar._findField.select();
@@ -73,16 +74,22 @@ function continueTests1() {
 
   // While we're here, let's test bug 253793
   gBrowser.reload();
   gBrowser.addEventListener("DOMContentLoaded", continueTests2, true);
 }
 
 function continueTests2() {
   gBrowser.removeEventListener("DOMContentLoaded", continueTests2, true);
+  waitForCondition(() => !gFindBar.getElement("highlight").checked,
+                   continueTests3,
+                   "Highlight never reset!");
+}
+
+function continueTests3() {
   ok(!gFindBar.getElement("highlight").checked, "Highlight button reset!");
   gFindBar.close();
   ok(gFindBar.hidden, "First tab doesn't show find bar!");
   gBrowser.selectedTab = tabs[1];
   ok(!gFindBar.hidden, "Second tab shows find bar!");
   // Test for bug 892384
   is(gFindBar._findField.getAttribute("focused"), "true",
      "Open findbar refocused on tab change!");
--- a/browser/base/content/test/general/browser_bug590206.js
+++ b/browser/base/content/test/general/browser_bug590206.js
@@ -1,138 +1,140 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
+/*
+ * Test the identity mode UI for a variety of page types
  */
 
 const DUMMY = "browser/browser/base/content/test/general/dummy_page.html";
 
-function loadNewTab(aURL, aCallback) {
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(aURL);
-
-  gBrowser.selectedBrowser.addEventListener("load", function() {
-    if (gBrowser.selectedBrowser.currentURI.spec != aURL)
-      return;
-    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-
-    aCallback(gBrowser.selectedTab);
-  }, true);
+function loadNewTab(url) {
+  return BrowserTestUtils.openNewForegroundTab(gBrowser, url);
 }
 
 function getIdentityMode() {
   return document.getElementById("identity-box").className;
 }
 
-var TESTS = [
-function test_webpage() {
+// This test is slow on Linux debug e10s
+requestLongerTimeout(2);
+
+add_task(function* test_webpage() {
+  let oldTab = gBrowser.selectedTab;
+
+  let newTab = yield loadNewTab("http://example.com/" + DUMMY);
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.removeTab(newTab);
+});
+
+add_task(function* test_blank() {
   let oldTab = gBrowser.selectedTab;
 
-  loadNewTab("http://example.com/" + DUMMY, function(aNewTab) {
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  let newTab = yield loadNewTab("about:blank");
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-    gBrowser.selectedTab = oldTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-    gBrowser.selectedTab = aNewTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
-
-    gBrowser.removeTab(aNewTab);
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-    runNextTest();
-  });
-},
+  gBrowser.removeTab(newTab);
+});
 
-function test_blank() {
+add_task(function* test_chrome() {
   let oldTab = gBrowser.selectedTab;
 
-  loadNewTab("about:blank", function(aNewTab) {
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  let newTab = yield loadNewTab("chrome://mozapps/content/extensions/extensions.xul");
+  is(getIdentityMode(), "fileURI", "Identity should be file");
 
-    gBrowser.selectedTab = oldTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-    gBrowser.selectedTab = aNewTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
-
-    gBrowser.removeTab(aNewTab);
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "fileURI", "Identity should be file");
 
-    runNextTest();
-  });
-},
+  gBrowser.removeTab(newTab);
+});
 
-function test_chrome() {
+add_task(function* test_https() {
   let oldTab = gBrowser.selectedTab;
 
-  // Since users aren't likely to type in full chrome URLs, we won't show
-  // the positive security indicator on it, but we will show it on about:addons.
-  loadNewTab("chrome://mozapps/content/extensions/extensions.xul", function(aNewTab) {
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
-
-    gBrowser.selectedTab = oldTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  let newTab = yield loadNewTab("https://example.com/" + DUMMY);
+  is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
 
-    gBrowser.selectedTab = aNewTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
-
-    gBrowser.removeTab(aNewTab);
-
-    runNextTest();
-  });
-},
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-function test_https() {
-  let oldTab = gBrowser.selectedTab;
-
-  loadNewTab("https://example.com/" + DUMMY, function(aNewTab) {
-    is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
-
-    gBrowser.selectedTab = oldTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
 
-    gBrowser.selectedTab = aNewTab;
-    is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
-
-    gBrowser.removeTab(aNewTab);
+  gBrowser.removeTab(newTab);
+});
 
-    runNextTest();
-  });
-},
-
-function test_addons() {
+add_task(function* test_addons() {
   let oldTab = gBrowser.selectedTab;
 
-  loadNewTab("about:addons", function(aNewTab) {
-    is(getIdentityMode(), "chromeUI", "Identity should be chrome");
+  let newTab = yield loadNewTab("about:addons");
+  is(getIdentityMode(), "chromeUI", "Identity should be chrome");
+
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-    gBrowser.selectedTab = oldTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "chromeUI", "Identity should be chrome");
 
-    gBrowser.selectedTab = aNewTab;
-    is(getIdentityMode(), "chromeUI", "Identity should be chrome");
+  gBrowser.removeTab(newTab);
+});
 
-    gBrowser.removeTab(aNewTab);
+add_task(function* test_file() {
+  let oldTab = gBrowser.selectedTab;
+  let fileURI = getTestFilePath("");
+
+  let newTab = yield loadNewTab(fileURI);
+  is(getIdentityMode(), "fileURI", "Identity should be file");
 
-    runNextTest();
-  });
-}
-];
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-var gTestStart = null;
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "fileURI", "Identity should be file");
+
+  gBrowser.removeTab(newTab);
+});
 
-function runNextTest() {
-  if (gTestStart)
-    info("Test part took " + (Date.now() - gTestStart) + "ms");
+add_task(function test_resource_uri() {
+  let oldTab = gBrowser.selectedTab;
+  let dataURI = "resource://gre/modules/Services.jsm"
+
+  let newTab = yield loadNewTab(dataURI);
 
-  if (TESTS.length == 0) {
-    finish();
-    return;
-  }
+  is(getIdentityMode(), "fileURI", "Identity should be unknown");
+
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "fileURI", "Identity should be unknown");
 
-  info("Running " + TESTS[0].name);
-  gTestStart = Date.now();
-  TESTS.shift()();
-};
+  gBrowser.removeTab(newTab);
+});
+
+add_task(function test_data_uri() {
+  let oldTab = gBrowser.selectedTab;
+  let dataURI = "data:text/html,hi"
 
-function test() {
-  waitForExplicitFinish();
+  let newTab = yield loadNewTab(dataURI);
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-  runNextTest();
-}
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.removeTab(newTab);
+});
--- a/browser/base/content/test/general/browser_pageInfo.js
+++ b/browser/base/content/test/general/browser_pageInfo.js
@@ -10,17 +10,17 @@ function test() {
     Services.obs.addObserver(observer, "page-info-dialog-loaded", false);
     pageInfo = BrowserPageInfo();
   }, true);
   content.location =
     "https://example.com/browser/browser/base/content/test/general/feed_tab.html";
 
   function observer(win, topic, data) {
     Services.obs.removeObserver(observer, "page-info-dialog-loaded");
-    handlePageInfo();
+    pageInfo.onFinished.push(handlePageInfo);
   }
 
   function handlePageInfo() {
     ok(pageInfo.document.getElementById("feedTab"), "Feed tab");
     let feedListbox = pageInfo.document.getElementById("feedListbox");
     ok(feedListbox, "Feed list");
 
     var feedRowsNum = feedListbox.getRowCount();
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -1,52 +1,64 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // This test checks that a <select> with an <optgroup> opens and can be navigated
 // in a child process. This is different than single-process as a <menulist> is used
 // to implement the dropdown list.
 
 const PAGECONTENT =
-  "<html><body onload='document.body.firstChild.focus()'><select>" +
+  "<html><body onload='gChangeEvents = 0; document.body.firstChild.focus()'><select onchange='gChangeEvents++'>" +
   "  <optgroup label='First Group'>" +
   "    <option value=One>One" +
   "    <option value=Two>Two" +
   "  </optgroup>" +
   "  <option value=Three>Three" +
   "  <optgroup label='Second Group' disabled='true'>" +
   "    <option value=Four>Four" +
   "    <option value=Five>Five" +
   "  </optgroup>" +
   "  <option value=Six disabled='true'>Six" +
   "  <optgroup label='Third Group'>" +
   "    <option value=Seven>Seven" +
   "    <option value=Eight>Eight" +
-  "  </optgroup>" +
+  "  </optgroup></select><input>" +
   "</body></html>";
 
-function openSelectPopup(selectPopup)
+function openSelectPopup(selectPopup, withMouse)
 {
-  return new Promise((resolve, reject) => {
-    selectPopup.addEventListener("popupshown", function popupListener(event) {
-      selectPopup.removeEventListener("popupshown", popupListener, false)
-      resolve();
-    }, false);
-    setTimeout(() => EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true, code: "ArrowDown" }), 1500);
-  });
+  let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
+
+  if (withMouse) {
+    return Promise.all([popupShownPromise,
+                        BrowserTestUtils.synthesizeMouseAtCenter("select", { }, gBrowser.selectedBrowser)]);
+  }
+
+  setTimeout(() => EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true, code: "ArrowDown" }), 1500);
+  return popupShownPromise;
 }
 
-function hideSelectPopup(selectPopup)
+function hideSelectPopup(selectPopup, withEscape)
 {
-  return new Promise((resolve, reject) => {
-    selectPopup.addEventListener("popuphidden", function popupListener(event) {
-      selectPopup.removeEventListener("popuphidden", popupListener, false)
-      resolve();
-    }, false);
+  let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
+
+  if (withEscape) {
+    EventUtils.synthesizeKey("KEY_Escape", { code: "Escape" });
+  }
+  else {
     EventUtils.synthesizeKey("KEY_Enter", { code: "Enter" });
+  }
+
+  return popupShownPromise;
+}
+
+function getChangeEvents()
+{
+  return ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
+    return content.wrappedJSObject.gChangeEvents;
   });
 }
 
 add_task(function*() {
   let tab = gBrowser.selectedTab = gBrowser.addTab();
   let browser = gBrowser.getBrowserForTab(tab);
   yield promiseTabLoadEvent(tab, "data:text/html," + escape(PAGECONTENT));
 
@@ -85,15 +97,34 @@ add_task(function*() {
   for (let i = 0; i < 10; i++) {
     is(menulist.getItemAtIndex(i).disabled, i >= 4 && i <= 7, "item " + i + " disabled")
   }
 
   EventUtils.synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
   is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(3), "Select item 3 again");
   is(menulist.selectedIndex, isWindows ? 3 : 1, "Select item 3 selectedIndex");
 
+  is((yield getChangeEvents()), 0, "Before closed - number of change events");
+
   yield hideSelectPopup(selectPopup);
 
   is(menulist.selectedIndex, 3, "Item 3 still selected");
+  is((yield getChangeEvents()), 1, "After closed - number of change events");
+
+  // Opening and closing the popup without changing the value should not fire a change event.
+  yield openSelectPopup(selectPopup, true);
+  yield hideSelectPopup(selectPopup, true);
+  is((yield getChangeEvents()), 1, "Open and close with no change - number of change events");
+  EventUtils.synthesizeKey("VK_TAB", { });
+  EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
+  is((yield getChangeEvents()), 1, "Tab away from select with no change - number of change events");
+
+  yield openSelectPopup(selectPopup, true);
+  EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
+  yield hideSelectPopup(selectPopup, true);
+  is((yield getChangeEvents()), isWindows ? 2 : 1, "Open and close with change - number of change events");
+  EventUtils.synthesizeKey("VK_TAB", { });
+  EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
+  is((yield getChangeEvents()), isWindows ? 2 : 1, "Tab away from select with change - number of change events");
 
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/base/content/test/general/browser_trackingUI_5.js
+++ b/browser/base/content/test/general/browser_trackingUI_5.js
@@ -17,16 +17,21 @@ registerCleanupFunction(function() {
 
 function hidden(sel) {
   let win = browser.ownerGlobal;
   let el = win.document.querySelector(sel);
   let display = win.getComputedStyle(el).getPropertyValue("display", null);
   return display === "none";
 }
 
+function identityPopupState() {
+  let win = browser.ownerGlobal;
+  return win.document.getElementById("identity-popup").state;
+}
+
 function clickButton(sel) {
   let win = browser.ownerGlobal;
   let el = win.document.querySelector(sel);
   el.doCommand();
 }
 
 function testTrackingPage(window) {
   info("Tracking content must be blocked");
@@ -80,16 +85,18 @@ add_task(function* testExceptionAddition
   info("Load a test page containing tracking elements");
   yield promiseTabLoadEvent(tab, TRACKING_PAGE);
 
   testTrackingPage(tab.ownerDocument.defaultView);
 
   info("Disable TP for the page (which reloads the page)");
   let tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-unblock");
+  is(identityPopupState(), "closed", "foobar");
+
   yield tabReloadPromise;
   testTrackingPageUnblocked();
 
   info("Test that the exception is remembered across tabs in the same private window");
   tab = browser.selectedTab = browser.addTab();
 
   info("Load a test page containing tracking elements");
   yield promiseTabLoadEvent(tab, TRACKING_PAGE);
@@ -110,13 +117,15 @@ add_task(function* testExceptionPersiste
   info("Load a test page containing tracking elements");
   yield promiseTabLoadEvent(tab, TRACKING_PAGE);
 
   testTrackingPage(tab.ownerDocument.defaultView);
 
   info("Disable TP for the page (which reloads the page)");
   let tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-unblock");
+  is(identityPopupState(), "closed", "foobar");
+
   yield tabReloadPromise;
   testTrackingPageUnblocked();
 
   privateWin.close();
 });
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/file_mediaPlayback.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<audio src="audio.ogg" controls loop>
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/browser.ini
@@ -51,17 +51,16 @@ support-files =
 [browser_CTP_context_menu.js]
 skip-if = toolkit == "gtk2" || toolkit == "gtk3"   # fails intermittently on Linux (bug 909342)
 [browser_CTP_crashreporting.js]
 skip-if = !crashreporter
 [browser_CTP_data_urls.js]
 [browser_CTP_drag_drop.js]
 [browser_CTP_hide_overlay.js]
 [browser_CTP_iframe.js]
-skip-if = os == 'linux' || os == 'mac' # Bug 984821
 [browser_CTP_multi_allow.js]
 [browser_CTP_nonplugins.js]
 [browser_CTP_notificationBar.js]
 [browser_CTP_outsideScrollArea.js]
 [browser_CTP_remove_navigate.js]
 [browser_CTP_resize.js]
 [browser_CTP_zoom.js]
 [browser_blocking.js]
--- a/browser/base/content/test/plugins/browser_pageInfo_plugins.js
+++ b/browser/base/content/test/plugins/browser_pageInfo_plugins.js
@@ -29,17 +29,17 @@ function doOnOpenPageInfo(continuation) 
   // windows if we don't keep a reference to every window we've opened.
   // So, don't reuse pointers to opened Page Info windows - simply append
   // to this list.
   gPageInfo = BrowserPageInfo(null, "permTab");
 }
 
 function pageInfoObserve(win, topic, data) {
   Services.obs.removeObserver(pageInfoObserve, "page-info-dialog-loaded");
-  executeSoon(gNextTest);
+  gPageInfo.onFinished.push(() => executeSoon(gNextTest));
 }
 
 function finishTest() {
   gPermissionManager.remove(makeURI("http://127.0.0.1:8888/"), gTestPermissionString);
   gPermissionManager.remove(makeURI("http://127.0.0.1:8888/"), gSecondTestPermissionString);
   Services.prefs.clearUserPref("plugins.click_to_play");
   gBrowser.removeCurrentTab();
 
--- a/browser/base/content/test/social/browser_social_multiworker.js
+++ b/browser/base/content/test/social/browser_social_multiworker.js
@@ -48,17 +48,18 @@ var tests = {
       port.postMessage({topic: "test-init"});
     }
 
     for (let p of Social.providers) {
       oneWorkerTest(p);
     }
 
     waitForCondition(function() messageReceived == Social.providers.length,
-                     next, "received messages from all workers");
+                     next, "received messages from all workers",
+                     /* increase timeout because shutting down a child process is slow */ 60);
   },
 
    testMultipleWorkerEnabling: function(next) {
      // test that all workers are enabled when we allow multiple workers
      for (let p of Social.providers) {
        ok(p.enabled, "provider enabled");
        let port = p.getWorkerPort();
        ok(port, "worker enabled");
--- a/browser/components/controlcenter/content/panel.inc.xul
+++ b/browser/components/controlcenter/content/panel.inc.xul
@@ -22,16 +22,18 @@
         <vbox id="identity-popup-security-content" flex="1">
           <label class="identity-popup-headline" crop="end">
             <observes element="identity-popup-content-host" attribute="value"/>
           </label>
           <label class="identity-popup-connection-secure identity-popup-text"
                  value="&identity.connectionSecure;"/>
           <label class="identity-popup-connection-not-secure identity-popup-text"
                  value="&identity.connectionNotSecure;"/>
+          <label class="identity-popup-connection-file-uri identity-popup-text"
+                 value="&identity.connectionFile;"/>
           <label class="identity-popup-connection-internal identity-popup-text"
                  value="&identity.connectionInternal;"/>
         </vbox>
         <button class="identity-popup-expander"
                 oncommand="gIdentityHandler.toggleSubView('security', this)"/>
       </hbox>
 
       <!-- Tracking Protection Section -->
@@ -90,16 +92,18 @@
       <vbox id="identity-popup-securityView-header">
         <label class="identity-popup-headline" crop="end">
           <observes element="identity-popup-content-host" attribute="value"/>
         </label>
         <label class="identity-popup-connection-secure identity-popup-text"
                value="&identity.connectionSecure;"/>
         <label class="identity-popup-connection-not-secure identity-popup-text"
                value="&identity.connectionNotSecure;"/>
+        <label class="identity-popup-connection-file-uri identity-popup-text"
+               value="&identity.connectionFile;"/>
         <label class="identity-popup-connection-internal identity-popup-text"
                value="&identity.connectionInternal;"/>
       </vbox>
 
       <description id="identity-popup-content-verifier"
                    class="identity-popup-text"/>
 
       <description id="identity-popup-securityView-connection"
--- a/browser/components/customizableui/test/browser_946320_tabs_from_other_computers.js
+++ b/browser/components/customizableui/test/browser_946320_tabs_from_other_computers.js
@@ -116,22 +116,18 @@ function configureFxAccountIdentity() {
   let MockInternal = {
     newAccountState(credentials) {
       isnot(credentials, "not expecting credentials");
       let storageManager = new MockFxaStorageManager();
       // and init storage with our user.
       storageManager.initialize(user);
       return new AccountState(storageManager);
     },
-    getCertificate(data, keyPair, mustBeValidUntil) {
-      this.cert = {
-        validUntil: this.now() + 10000,
-        cert: "certificate",
-      };
-      return Promise.resolve(this.cert.cert);
+    _getAssertion(audience) {
+      return Promise.resolve("assertion");
     },
     getCertificateSigned() {
       return Promise.resolve();
     },
   };
   let mockTSC = { // TokenServerClient
     getTokenFromBrowserIDAssertion: function(uri, assertion, cb) {
       token.uid = "username";
--- a/browser/components/loop/.eslintignore
+++ b/browser/components/loop/.eslintignore
@@ -6,16 +6,19 @@ modules/MozLoopWorker.js
 modules/MozLoopAPI.jsm
 # Libs we don't need to check
 content/libs
 content/shared/libs
 standalone/content/libs
 standalone/node_modules
 # Libs we don't need to check
 test/shared/vendor
+# Coverage files
+test/coverage
+test/node_modules
 # These are generated react files that we don't need to check
 content/js/contacts.js
 content/js/conversation.js
 content/js/conversationViews.js
 content/js/panel.js
 content/js/roomViews.js
 content/js/feedbackViews.js
 content/shared/js/textChatView.js
--- a/browser/components/loop/.gitignore
+++ b/browser/components/loop/.gitignore
@@ -1,1 +1,2 @@
 .module-cache
+test/coverage
--- a/browser/components/loop/README.txt
+++ b/browser/components/loop/README.txt
@@ -51,16 +51,27 @@ If you install eslint and the react plug
 
   npm install -g eslint
   npm install -g eslint-plugin-react
 
 You can also run it by hand in the browser/components/loop directory:
 
   eslint --ext .js --ext .jsx --ext .jsm .
 
+Test coverage
+=============
+Initial setup
+  cd test
+  npm install
+
+To run
+  npm run build-coverage
+
+It will create a `coverage` folder under test/
+
 Front-End Unit Tests
 ====================
 The unit tests for Loop reside in three directories:
 
 - test/desktop-local
 - test/shared
 - test/standalone
 
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -1375,17 +1375,19 @@ html[dir="rtl"] .standalone .room-conver
   background-image: url("../../img/gum-others.svg");
   background-position: top center;
   /* The background-image is scaled up at 2x the instrinsic size
      (witdh & height) to make it easier to see. */
   background-size: 202px 122px;
   background-repeat: no-repeat;
   border: 1rem #fff solid;
   box-shadow: 0 0 5px #000;
-  margin: 0;
+  margin: auto;
+  /* `width` here is specified by the design spec. */
+  width: 250px;
 }
 
 .standalone .prompt-media-message.chrome {
   background-image: url("../../img/gum-chrome.svg");
 }
 
 .standalone .prompt-media-message.firefox {
   background-image: url("../../img/gum-firefox.svg");
--- a/browser/components/loop/run-all-loop-tests.sh
+++ b/browser/components/loop/run-all-loop-tests.sh
@@ -2,31 +2,42 @@
 # Run from topsrcdir, no args
 
 if [ "$1" == "--help" ]; then
   echo "Usage: ./run-all-loop-tests.sh [options]"
   echo "    --skip-e10s  Skips the e10s tests"
   exit 0;
 fi
 
+# Causes script to abort immediately if error code is not checked.
 set -e
 
 # Main tests
 
 LOOPDIR=browser/components/loop
 ESLINT=standalone/node_modules/.bin/eslint
 if [ -x "${LOOPDIR}/${ESLINT}" ]; then
   echo 'running eslint; see http://eslint.org/docs/rules/ for error info'
   (cd ${LOOPDIR} && ./${ESLINT} --ext .js --ext .jsm --ext .jsx .)
   if [ $? != 0 ]; then
     exit 1;
   fi
   echo 'eslint run finished.'
 fi
 
+# Build tests coverage.
+MISSINGDEPSMSG="\nMake sure all dependencies are up to date by running
+'npm install' inside the 'browser/components/loop/test/' directory.\n"
+(
+cd ${LOOPDIR}/test
+if ! npm run-script build-coverage ; then
+  echo $MISSINGDEPSMSG && exit 1
+fi
+)
+
 ./mach xpcshell-test ${LOOPDIR}/
 ./mach marionette-test ${LOOPDIR}/manifest.ini
 
 # The browser_parsable_css.js can fail if we add some css that isn't parsable.
 #
 # The check to make sure that the media devices can be used in Loop without
 # prompting is in browser_devices_get_user_media_about_urls.js. It's possible
 # to mess this up with CSP handling, and probably other changes, too.
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -251,17 +251,18 @@ loop.standaloneRoomViews = (function(moz
       );
     }
   });
 
   var StandaloneRoomView = React.createClass({displayName: "StandaloneRoomView",
     mixins: [
       Backbone.Events,
       sharedMixins.MediaSetupMixin,
-      sharedMixins.RoomsAudioMixin
+      sharedMixins.RoomsAudioMixin,
+      sharedMixins.DocumentTitleMixin
     ],
 
     propTypes: {
       // We pass conversationStore here rather than use the mixin, to allow
       // easy configurability for the ui-showcase.
       activeRoomStore: React.PropTypes.oneOfType([
         React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
         React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
@@ -301,16 +302,24 @@ loop.standaloneRoomViews = (function(moz
     /**
      * Watches for when we transition to MEDIA_WAIT room state, so we can request
      * user media access.
      *
      * @param  {Object} nextProps (Unused)
      * @param  {Object} nextState Next state object.
      */
     componentWillUpdate: function(nextProps, nextState) {
+      if (this.state.roomState !== ROOM_STATES.READY &&
+          nextState.roomState === ROOM_STATES.READY) {
+        this.setTitle(mozL10n.get("standalone_title_with_room_name", {
+          roomName: nextState.roomName || this.state.roomName,
+          clientShortname: mozL10n.get("clientShortname2")
+        }));
+      }
+
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
         this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
           publisherConfig: this.getDefaultPublisherConfig({publishVideo: true})
         }));
       }
 
       // UX don't want to surface these errors (as they would imply the user
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -251,17 +251,18 @@ loop.standaloneRoomViews = (function(moz
       );
     }
   });
 
   var StandaloneRoomView = React.createClass({
     mixins: [
       Backbone.Events,
       sharedMixins.MediaSetupMixin,
-      sharedMixins.RoomsAudioMixin
+      sharedMixins.RoomsAudioMixin,
+      sharedMixins.DocumentTitleMixin
     ],
 
     propTypes: {
       // We pass conversationStore here rather than use the mixin, to allow
       // easy configurability for the ui-showcase.
       activeRoomStore: React.PropTypes.oneOfType([
         React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
         React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
@@ -301,16 +302,24 @@ loop.standaloneRoomViews = (function(moz
     /**
      * Watches for when we transition to MEDIA_WAIT room state, so we can request
      * user media access.
      *
      * @param  {Object} nextProps (Unused)
      * @param  {Object} nextState Next state object.
      */
     componentWillUpdate: function(nextProps, nextState) {
+      if (this.state.roomState !== ROOM_STATES.READY &&
+          nextState.roomState === ROOM_STATES.READY) {
+        this.setTitle(mozL10n.get("standalone_title_with_room_name", {
+          roomName: nextState.roomName || this.state.roomName,
+          clientShortname: mozL10n.get("clientShortname2")
+        }));
+      }
+
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
         this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
           publisherConfig: this.getDefaultPublisherConfig({publishVideo: true})
         }));
       }
 
       // UX don't want to surface these errors (as they would imply the user
--- a/browser/components/loop/standalone/content/l10n/en-US/loop.properties
+++ b/browser/components/loop/standalone/content/l10n/en-US/loop.properties
@@ -110,16 +110,20 @@ rooms_unavailable_notification_message=S
 rooms_media_denied_message=We could not get access to your microphone or camera. Please reload the page to try again.
 room_information_failure_not_available=No information about this conversation is available. Please request a new link from the person who sent it to you.
 room_information_failure_unsupported_browser=Your browser cannot access any information about this conversation. Please make sure you're using the latest version.
 
 ## LOCALIZATION_NOTE(standalone_title_with_status): {{clientShortname}} will be
 ## replaced by the brand name and {{currentStatus}} will be replaced
 ## by the current call status (Connecting, Ringing, etc.)
 standalone_title_with_status={{clientShortname}} — {{currentStatus}}
+## LOCALIZATION_NOTE(standalone_title_with_room_name): {{roomName}} will be replaced
+## by the name of the conversation and {{clientShortname}} will be
+## replaced by the brand name.
+standalone_title_with_room_name={{roomName}} — {{clientShortname}}
 status_in_conversation=In conversation
 status_conversation_ended=Conversation ended
 status_error=Something went wrong
 support_link=Get Help
 
 # Text chat strings
 
 chat_textbox_placeholder=Type here…
--- a/browser/components/loop/test/desktop-local/conversation_test.js
+++ b/browser/components/loop/test/desktop-local/conversation_test.js
@@ -73,38 +73,40 @@ describe("loop.conversation", function()
 
   afterEach(function() {
     loop.shared.mixins.setRootObject(window);
     delete navigator.mozLoop;
     sandbox.restore();
   });
 
   describe("#init", function() {
+    var OTRestore;
     beforeEach(function() {
       sandbox.stub(React, "render");
       sandbox.stub(document.mozL10n, "initialize");
 
       sandbox.stub(loop.shared.models.ConversationModel.prototype,
         "initialize");
 
       sandbox.stub(loop.Dispatcher.prototype, "dispatch");
 
       sandbox.stub(loop.shared.utils,
         "locationData").returns({
           hash: "#42",
           pathname: "/"
         });
 
+      OTRestore = window.OT;
       window.OT = {
         overrideGuidStorage: sinon.stub()
       };
     });
 
     afterEach(function() {
-      delete window.OT;
+      window.OT = OTRestore;
     });
 
     it("should initialize L10n", function() {
       loop.conversation.init();
 
       sinon.assert.calledOnce(document.mozL10n.initialize);
       sinon.assert.calledWithExactly(document.mozL10n.initialize,
         navigator.mozLoop);
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/karma/karma.conf.base.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env node */
+
+module.exports = function(config) {
+  "use strict";
+
+  return {
+
+    // Base path that will be used to resolve all patterns (eg. files, exclude).
+    basePath: "../../",
+
+    // List of files / patterns to load in the browser.
+    files: [],
+
+    // List of files to exclude.
+    exclude: [
+    ],
+
+    // Frameworks to use.
+    // Available frameworks: https://npmjs.org/browse/keyword/karma-adapter .
+    frameworks: ["mocha"],
+
+    // Test results reporter to use.
+    // Possible values: "dots", "progress".
+    // Available reporters: https://npmjs.org/browse/keyword/karma-reporter .
+    reporters: ["progress", "coverage"],
+
+    coverageReporter: {
+      type: "html",
+      dir: "test/coverage/"
+    },
+
+    // Web server port.
+    port: 9876,
+
+    // Enable / disable colors in the output (reporters and logs).
+    colors: true,
+
+    // Level of logging.
+    // Possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG.
+    logLevel: config.LOG_INFO,
+
+    // Enable / disable watching file and executing tests whenever any file changes.
+    autoWatch: false,
+
+    // Start these browsers
+    // Available browser launchers: https://npmjs.org/browse/keyword/karma-launcher .
+    browsers: ["Firefox"],
+
+    // Continuous Integration mode.
+    // If true, Karma captures browsers, runs the tests and exits.
+    singleRun: true,
+
+    // Capture console output.
+    client: {
+      captureConsole: true
+    },
+
+    plugins: [
+      "karma-coverage",
+      "karma-mocha",
+      "karma-firefox-launcher"
+    ]
+  };
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/karma/karma.coverage.desktop.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env node */
+
+module.exports = function(config) {
+  "use strict";
+
+  var baseConfig = require("./karma.conf.base.js")(config);
+
+  // List of files / patterns to load in the browser.
+  baseConfig.files = baseConfig.files.concat([
+    "content/libs/l10n.js",
+    "content/shared/libs/react-0.12.2.js",
+    "content/shared/libs/jquery-2.1.4.js",
+    "content/shared/libs/lodash-3.9.3.js",
+    "content/shared/libs/backbone-1.2.1.js",
+    "test/shared/vendor/*.js",
+    "test/karma/stubs.js", // Stub out DOM event listener due to races.
+    "content/shared/js/utils.js",
+    "content/shared/js/models.js",
+    "content/shared/js/mixins.js",
+    "content/shared/js/websocket.js",
+    "content/shared/js/actions.js",
+    "content/shared/js/otSdkDriver.js",
+    "content/shared/js/validate.js",
+    "content/shared/js/dispatcher.js",
+    "content/shared/js/store.js",
+    "content/shared/js/conversationStore.js",
+    "content/shared/js/roomStates.js",
+    "content/shared/js/fxOSActiveRoomStore.js",
+    "content/shared/js/activeRoomStore.js",
+    "content/shared/js/views.js",
+    "content/shared/js/textChatStore.js",
+    "content/shared/js/textChatView.js",
+    "content/js/feedbackViews.js",
+    "content/js/client.js",
+    "content/js/conversationAppStore.js",
+    "content/js/roomStore.js",
+    "content/js/roomViews.js",
+    "content/js/conversationViews.js",
+    "content/js/conversation.js",
+    "test/desktop-local/*.js"
+  ]);
+
+  // List of files to exclude.
+  baseConfig.exclude = baseConfig.exclude.concat([
+    "test/desktop-local/panel_test.js",
+    "test/desktop-local/contacts_test.js"
+  ]);
+
+  // Preprocess matching files before serving them to the browser.
+  // Available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor .
+  baseConfig.preprocessors = {
+    "content/js/*.js": ["coverage"]
+  };
+
+  baseConfig.coverageReporter.dir = "test/coverage/desktop";
+
+  config.set(baseConfig);
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/karma/karma.coverage.shared_standalone.js
@@ -0,0 +1,59 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env node */
+
+module.exports = function(config) {
+  "use strict";
+
+  var baseConfig = require("./karma.conf.base.js")(config);
+
+  // List of files / patterns to load in the browser.
+  baseConfig.files = baseConfig.files.concat([
+    "standalone/content/libs/l10n-gaia-02ca67948fe8.js",
+    "content/shared/libs/jquery-2.1.4.js",
+    "content/shared/libs/lodash-3.9.3.js",
+    "content/shared/libs/backbone-1.2.1.js",
+    "content/shared/libs/react-0.12.2.js",
+    "content/shared/libs/sdk.js",
+    "test/shared/vendor/*.js",
+    "content/shared/js/utils.js",
+    "content/shared/js/store.js",
+    "content/shared/js/models.js",
+    "content/shared/js/mixins.js",
+    "content/shared/js/crypto.js",
+    "content/shared/js/websocket.js",
+    "content/shared/js/validate.js",
+    "content/shared/js/actions.js",
+    "content/shared/js/dispatcher.js",
+    "content/shared/js/otSdkDriver.js",
+    "content/shared/js/roomStates.js",
+    "content/shared/js/fxOSActiveRoomStore.js",
+    "content/shared/js/activeRoomStore.js",
+    "content/shared/js/conversationStore.js",
+    "content/shared/js/views.js",
+    "content/shared/js/textChatStore.js",
+    "content/shared/js/textChatView.js",
+    "standalone/content/js/multiplexGum.js",
+    "standalone/content/js/standaloneAppStore.js",
+    "standalone/content/js/standaloneClient.js",
+    "standalone/content/js/standaloneMozLoop.js",
+    "standalone/content/js/fxOSMarketplace.js",
+    "standalone/content/js/standaloneRoomViews.js",
+    "standalone/content/js/standaloneMetricsStore.js",
+    "standalone/content/js/webapp.js",
+    "test/shared/*.js",
+    "test/standalone/*.js"
+  ]);
+
+  // Preprocess matching files before serving them to the browser.
+  // Available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor .
+  baseConfig.preprocessors = {
+    "content/shared/js/*.js": ["coverage"],
+    "standalone/content/js/*.js": ["coverage"]
+  };
+
+  baseConfig.coverageReporter.dir = "test/coverage/shared_standalone";
+
+  config.set(baseConfig);
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/karma/stubs.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Used for desktop coverage tests because triggering methods on
+// DOMContentLoaded proved to lead to race conditions.
+
+sinon.stub(document, "addEventListener");
+console.log("[stubs.js] addEventListener stubbed to prevent race conditions");
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/package.json
@@ -0,0 +1,21 @@
+{
+  "name": "FirefoxHello",
+  "version": "0.0.1",
+  "description": "Firefox Hello test coverage",
+  "main": "index.js",
+  "directories": {
+    "test": "test"
+  },
+  "devDependencies": {
+    "istanbul": "^0.3.17",
+    "karma": "^0.12.37",
+    "karma-coverage": "^0.4.2",
+    "karma-firefox-launcher": "^0.1.6",
+    "karma-mocha": "^0.2.0"
+  },
+  "scripts": {
+    "build-coverage-shared": "./node_modules/.bin/karma start karma/karma.coverage.shared_standalone.js",
+    "build-coverage-desktop": "./node_modules/.bin/karma start karma/karma.coverage.desktop.js",
+    "build-coverage": "npm run build-coverage-desktop && npm run build-coverage-shared"
+  }
+}
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -6,18 +6,18 @@ describe("loop.store.ActiveRoomStore", f
 
   var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
   var ROOM_STATES = loop.store.ROOM_STATES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
   var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
-  var sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver;
-  var fakeMultiplexGum;
+  var sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver, fakeMultiplexGum;
+  var standaloneMediaRestore;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     sandbox.useFakeTimers();
 
     dispatcher = new loop.Dispatcher();
     sandbox.stub(dispatcher, "dispatch");
     sandbox.stub(window, "close");
@@ -47,31 +47,33 @@ describe("loop.store.ActiveRoomStore", f
       forceDisconnectAll: sinon.stub().callsArg(0),
       retryPublishWithoutVideo: sinon.stub(),
       startScreenShare: sinon.stub(),
       switchAcquiredWindow: sinon.stub(),
       endScreenShare: sinon.stub().returns(true)
     };
 
     fakeMultiplexGum = {
-        reset: sandbox.spy()
+      reset: sandbox.spy()
     };
 
+    standaloneMediaRestore = loop.standaloneMedia;
     loop.standaloneMedia = {
       multiplexGum: fakeMultiplexGum
     };
 
     store = new loop.store.ActiveRoomStore(dispatcher, {
       mozLoop: fakeMozLoop,
       sdkDriver: fakeSdkDriver
     });
   });
 
   afterEach(function() {
     sandbox.restore();
+    loop.standaloneMedia = standaloneMediaRestore;
   });
 
   describe("#constructor", function() {
     it("should throw an error if mozLoop is missing", function() {
       expect(function() {
         new loop.store.ActiveRoomStore(dispatcher);
       }).to.Throw(/mozLoop/);
     });
--- a/browser/components/loop/test/shared/mixins_test.js
+++ b/browser/components/loop/test/shared/mixins_test.js
@@ -426,17 +426,18 @@ describe("loop.shared.mixins", function(
   describe("loop.shared.mixins.AudioMixin", function() {
     var view, fakeAudio, TestComp;
 
     beforeEach(function() {
       navigator.mozLoop = {
         doNotDisturb: true,
         getAudioBlob: sinon.spy(function(name, callback) {
           callback(null, new Blob([new ArrayBuffer(10)], {type: "audio/ogg"}));
-        })
+        }),
+        getLoopPref: sandbox.stub()
       };
 
       fakeAudio = {
         play: sinon.spy(),
         pause: sinon.spy(),
         removeAttribute: sinon.spy()
       };
       sandbox.stub(window, "Audio").returns(fakeAudio);
--- a/browser/components/loop/test/shared/validate_test.js
+++ b/browser/components/loop/test/shared/validate_test.js
@@ -12,16 +12,23 @@ describe("Validator", function() {
     return validator.validate.bind(validator, values);
   }
 
   // test types
   function X(){}
   function Y(){}
 
   describe("#validate", function() {
+    function mozRTCSessionDescription() {}
+    var mozRTC;
+
+    beforeEach(function() {
+      mozRTC = new mozRTCSessionDescription();
+    });
+
     it("should check for a single required dependency when no option passed",
       function() {
         expect(create({x: Number}, {}))
           .to.Throw(TypeError, /missing required x$/);
       });
 
     it("should check for a missing required dependency, undefined passed",
       function() {
@@ -62,17 +69,17 @@ describe("Validator", function() {
     });
 
     it("should check for a custom constructor dependency", function() {
       expect(create({foo: X}, {foo: null})).to.Throw(
         TypeError, /invalid dependency: foo; expected X, got null$/);
     });
 
     it("should check for a native constructor dependency", function() {
-      expect(create({foo: mozRTCSessionDescription}, {foo: "x"}))
+      expect(create({foo: mozRTC}, {foo: "x"}))
         .to.Throw(TypeError,
                   /invalid dependency: foo; expected mozRTCSessionDescription/);
     });
 
     it("should check for a null dependency", function() {
       expect(create({foo: null}, {foo: "x"})).to.Throw(
         TypeError, /invalid dependency: foo; expected null, got String$/);
     });
--- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js
+++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js
@@ -11,16 +11,17 @@ describe("loop.standaloneRoomViews", fun
   var ROOM_STATES = loop.store.ROOM_STATES;
   var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
   var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
 
   var sandbox, dispatcher, activeRoomStore, dispatch;
+  var fakeWindow;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     dispatcher = new loop.Dispatcher();
     dispatch = sandbox.stub(dispatcher, "dispatch");
     activeRoomStore = new loop.store.ActiveRoomStore(dispatcher, {
       mozLoop: {},
       sdkDriver: {}
@@ -29,22 +30,40 @@ describe("loop.standaloneRoomViews", fun
       sdkDriver: {}
     });
     loop.store.StoreMixin.register({
       activeRoomStore: activeRoomStore,
       textChatStore: textChatStore
     });
 
     sandbox.useFakeTimers();
+    fakeWindow = {
+      close: sandbox.stub(),
+      addEventListener: function() {},
+      document: { addEventListener: function(){} },
+      setTimeout: function(callback) { callback(); }
+    };
+    loop.shared.mixins.setRootObject(fakeWindow);
+
+
+    sandbox.stub(navigator.mozL10n, "get", function(key, args) {
+      switch(key) {
+        case "standalone_title_with_room_name":
+          return args.roomName + " — " + args.clientShortname;
+        default:
+          return key;
+      }
+    });
 
     // Prevents audio request errors in the test console.
     sandbox.useFakeXMLHttpRequest();
   });
 
   afterEach(function() {
+    loop.shared.mixins.setRootObject(window);
     sandbox.restore();
   });
 
   describe("StandaloneRoomHeader", function() {
     function mountTestComponent() {
       return TestUtils.renderIntoDocument(
         React.createElement(
           loop.standaloneRoomViews.StandaloneRoomHeader, {
@@ -78,16 +97,24 @@ describe("loop.standaloneRoomViews", fun
 
     function expectActionDispatched(view) {
       sinon.assert.calledOnce(dispatch);
       sinon.assert.calledWithExactly(dispatch,
         sinon.match.instanceOf(sharedActions.SetupStreamElements));
     }
 
     describe("#componentWillUpdate", function() {
+      it("should set document.title to roomName and brand name when the READY state is dispatched", function() {
+        activeRoomStore.setStoreState({roomName: "fakeName", roomState: ROOM_STATES.INIT});
+        var view = mountTestComponent();
+        activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
+
+        expect(fakeWindow.document.title).to.equal("fakeName — clientShortname2");
+      });
+
       it("should dispatch a `SetupStreamElements` action when the MEDIA_WAIT state " +
         "is entered", function() {
           activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
           var view = mountTestComponent();
 
           activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
 
           expectActionDispatched(view);
--- a/browser/components/loop/test/standalone/webapp_test.js
+++ b/browser/components/loop/test/standalone/webapp_test.js
@@ -42,21 +42,32 @@ describe("loop.webapp", function() {
     };
   });
 
   afterEach(function() {
     sandbox.restore();
   });
 
   describe("#init", function() {
+    var loopConfigRestore;
+
     beforeEach(function() {
       sandbox.stub(React, "render");
+      loopConfigRestore = loop.config;
+      loop.config = {
+        feedbackApiUrl: "http://fake.invalid",
+        serverUrl: "http://fake.invalid"
+      };
       sandbox.stub(loop.Dispatcher.prototype, "dispatch");
     });
 
+    afterEach(function() {
+      loop.config = loopConfigRestore;
+    });
+
     it("should create the WebappRootView", function() {
       loop.webapp.init();
 
       sinon.assert.calledOnce(React.render);
       sinon.assert.calledWith(React.render,
         sinon.match(function(value) {
           return TestUtils.isCompositeComponentElement(value,
             loop.webapp.WebappRootView);
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ b/browser/components/migration/ChromeProfileMigrator.js
@@ -1,35 +1,40 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
  * vim: sw=2 ts=2 sts=2 et */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.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 Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-const Cr = Components.results;
+const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 
 const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1";
 
 const S100NS_FROM1601TO1970 = 0x19DB1DED53E8000;
 const S100NS_PER_MS = 10;
 
+const AUTH_TYPE = {
+  SCHEME_HTML: 0,
+  SCHEME_BASIC: 1,
+  SCHEME_DIGEST: 2
+};
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
+                                  "resource://gre/modules/OSCrypto.jsm");
 
 /**
  * Convert Chrome time format to Date object
  *
  * @param   aTime
  *          Chrome time 
  * @return  converted Date object
  * @note    Google Chrome uses FILETIME / 10 as time.
@@ -88,17 +93,21 @@ ChromeProfileMigrator.prototype = Object
 ChromeProfileMigrator.prototype.getResources =
   function Chrome_getResources(aProfile) {
     if (this._chromeUserDataFolder) {
       let profileFolder = this._chromeUserDataFolder.clone();
       profileFolder.append(aProfile.id);
       if (profileFolder.exists()) {
         let possibleResources = [GetBookmarksResource(profileFolder),
                                  GetHistoryResource(profileFolder),
-                                 GetCookiesResource(profileFolder)];
+                                 GetCookiesResource(profileFolder),
+#ifdef XP_WIN
+                                 GetWindowsPasswordsResource(profileFolder)
+#endif
+                                 ];
         return [r for each (r in possibleResources) if (r != null)];
       }
     }
     return [];
   };
 
 Object.defineProperty(ChromeProfileMigrator.prototype, "sourceProfiles", {
   get: function Chrome_sourceProfiles() {
@@ -348,13 +357,108 @@ function GetCookiesResource(aProfileFold
           aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
         },
       });
       stmt.finalize();
     }
   }
 }
 
+function GetWindowsPasswordsResource(aProfileFolder) {
+  let loginFile = aProfileFolder.clone();
+  loginFile.append("Login Data");
+  if (!loginFile.exists())
+    return null;
+
+  return {
+    type: MigrationUtils.resourceTypes.PASSWORDS,
+
+    migrate(aCallback) {
+      let dbConn = Services.storage.openUnsharedDatabase(loginFile);
+      let stmt = dbConn.createAsyncStatement(`
+        SELECT origin_url, action_url, username_element, username_value,
+        password_element, password_value, signon_realm, scheme, date_created,
+        times_used FROM logins WHERE blacklisted_by_user = 0`);
+      let crypto = new OSCrypto();
+
+      stmt.executeAsync({
+        _rowToLoginInfo(row) {
+          let loginInfo = {
+            username: row.getResultByName("username_value"),
+            password: crypto.decryptData(row.getResultByName("password_value")),
+            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,
+          };
+
+          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);
+              break;
+            default:
+              throw new Error("Login data scheme type not supported: " +
+                              row.getResultByName("scheme"));
+          }
+
+          return loginInfo;
+        },
+
+        handleResult(aResults) {
+          for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
+            try {
+              let loginInfo = this._rowToLoginInfo(row);
+              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);
+              }
+            } catch (e) {
+              Cu.reportError(e);
+            }
+          }
+        },
+
+        handleError(aError) {
+          Cu.reportError("Async statement execution returned with '" +
+                         aError.result + "', '" + aError.message + "'");
+        },
+
+        handleCompletion(aReason) {
+          dbConn.asyncClose();
+          aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
+          crypto.finalize();
+        },
+      });
+      stmt.finalize();
+    }
+  };
+}
+
 ChromeProfileMigrator.prototype.classDescription = "Chrome Profile Migrator";
 ChromeProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chrome";
 ChromeProfileMigrator.prototype.classID = Components.ID("{4cec1de4-1671-4fc3-a53e-6c539dc77a26}");
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ChromeProfileMigrator]);
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..914149c710a8c748b97543c541aaedfee74aa1f6
GIT binary patch
literal 22528
zc%1E93vg4#8s0pb(+5F{)R5NdK}Mj`rq<4Y2qk?Gp|tiz3zUHyPm<H*n3J4v&Phwd
zh*AXyZh2K6iipF@7osqu%msWP!l3tB1p#>oTydlhBIpGLI~BaUn@5wTDJaf3;(qOU
z?f$#}zyEIb`*(9Pp|X^dS-MUTU5rfUf_R`%fP9(;0Nf9cB+v<U;0Qtiu8sRaRvV?I
zegTxq3@9gcK;1-*RvT1iwdy)b6A=8hF?)c9(&==HrU5cz;aN#$WC@=Vi>gco)h4>S
zps>_L<0N{BAaZujZMF$6hI7*s3#y976;#n><<)dqO=&4zQC0j<K~*hXVyexg-GYae
z%vQncmg(ZMYSUO#RVHoYBo`-14Bs^aDiCF}je#HaKw7BUj0{Sn%hM?U=eDskr0G1I
z56p~L7Vxtfn`X|zm!zsVjc%}BA>k}ufW}H@iL<)}xBo#CXjO697}I1rLVzwW3nb7(
z{3&LUWq4P{oDnKYr`IdyKZIT6m+_y~5l#x04tU8c@=-3zq#3Ks2@#o@v{zz9H{)W>
zEYG@F=$7c5dWQG1o#`G%k{Sfj7F{Bg<1bNIT3!gf>$SKzIa)Zpozb>R5|3)(Y*Ag3
z=@HpFRutK8vQQtIt*}bDyIc#;Se-m4!A!HI(Tv&`EhJeT(9mwQ2HPe#dP3Qm9zl{g
z*fHjyvm}43We(n=#E==8h(*vKWBd}>RH@Nz4HJPQ)Wb84W>itgQA|B7BSv&u9cyDn
zCaNzbC+9KySWz%rdCuw#RcmM6A#o2cFg84Hy;4Y^rmVQK#uPqyqQ^zo@en-@BD*vr
zW2joAGwNe~bOPf)$A68hQ&75;6vgapJkDLL%%DRVF~(8K5GUJ6mzI}}jhX-gjfj{B
z{Npf8JY%p@qZ^qL%P6G5oP*AYfz?`gQc{Nib%?s~XHS*{1Ox;G1OzvLG^J|fEj%O1
za2eq8z_18~Z}qGw!T2brmr9kE9DK-4q<(_@-wLRs@I*jBKtMo1@Yh3<Doqh!c2$~c
zq#{NLpah*8`*8rR|1Y821ggV-LqI@4KtMonlOi5P0hA&Up5rt=pz&$`rMXJINjd&(
z!w3in2ncR&<Wad=op!9kA<Lczva`{}!nqBCXwSBw@8P>ie~@vqbQSvQJ<bG6r)X=t
zD;=Y-i_LekZZ|7CM3$3%2DmO(bJR3lj^2c8k)S+d#qZ5X2D>2Gc^1iJPhiD*&dQde
zFY!$-hU0@T3FO<+6OySwuL;<Cszg~r8FlF?#*n6lh6Y0&V`VLZ;KUMac-82=gP4-4
z5^Cgt^uZYkK?&;!11DrRu$FB6rUB>nc;yI7aY{wtU53a~nExpYpe)prR4a9!dgIR?
z7X$<Z1Ox;(0|uzNjd8+anR`^-#xdOmEa|EqLI@SD>N%8ACabze4e0$ph5B6p{|N{P
z2>wEtYgD9;M2n#(CTlYi5;XDg@%`eT8WpeAYUAT$ERTNQstNtrX<zg6|5c>yPj2>s
zo#ne$oJDERO#k2wd17<%dU@aM<@ZRNkyT1;Tc0|Be<MZ1%bu4%wifV9TvLBsID-B~
zF@B|bSmM~W;qNDL_j+I7{lg9<-}`ZYQ6xWXz+)He^Pli_9N5>f_Q(svSG6SXs?SYp
zPrJpq<$njpeb)!d3>tX*y0}%dm@V`5>G$t`$C;0cAGx|5oJid;*?sU!<ND?`C)-yI
zX`^h9o!r}RcR}mX&AXQVRPoez+_No7W#xsrUgz;Wo3=i?>&?7NOFxxW|4yDA=^J2Z
zJTvjRI}4F@2VqOl3*(3{>VN=5-~y%aY6l!}g9^aFI~L~Y;1R>ap<Yu3e3T528X&=P
zBCLafHL|evsBRkGxnK$hcqoI`s7Q+kc!3N|VLAREq{-|I*<W*f)Cf_89m=qDWk4EP
zF2TPBtk;Iy*Qu*(Zl{*uF9Uv$UexXq*g_U+p#!a!pjJE7%R((Qwg80LQUn|@#|oTb
zKJV0Qb+A^aMVJxGhVXl3mwe3s$_@bk2?+j9n6Ff(PGfaBdPp9o0C^}DOpPRuYwZYL
zuUvswcJ#P=cw+>!EA2$>hqY64&YnIvMw79ZY1umd)$~p0?ikHIe%aYPJr&7^6U<L*
z+fv)TOJ2y@e>7+GqH#;EPE&ghCmzmNCY3F^<q>fbBAD$Lw72%1`s4KE{*SKSE${z4
zAJx&@Q$25E;@tB6{m$Pps^Q9Ts_GNw+&|}SwCNY`UH9OYnu~K*Z*0oSGH92M9{Bv3
zoh^e-Z`*ciNXhX7S8RLc*@yPzE<`tzU1AZkRbx~6d7?Kw?0CLsfEmvD!Pzg!L5Op%
zw-1bx{~-@~evnmg%beTG<^_x6{s=3Ip0fvrP4t<+K^N+8)DU%`yAUs$pxrWNI~rac
zc;4sXSq`L1@R<P?*s^-uuKobc|2BA!%5kvG9G1(1Ou#@^km1z<@3O)eE2y(4P6Qw}
zH&w!80`{#48exlRFcv-`uk+Ym8@y+srEV}7YNMeP;w1rd7YBS;M#fgj5iD+iPeoXo
z!L3Ctb>p)Ym-pf}((o$b*4QKJVW3_cE{)jO2>kD@MMeVtG`x)t{9{L`tBcwGpbxGm
zCeTEr*+O2ukr<#i_OlFYZS0o)`(T~^QNh7O4vc~#+#60j5`tQ<#s9JF2=c#5aSEt6
ztH!_!!Oe!IhvW3QTCGkCS#E4RWVr;mL@Mnhx$eGtyD=_0@<p#vd)7{T@fgGR-Lh)i
zOx?b{qmGPt^^R|C74oWu{Wc)^a4uT6Yk`fQvTc2~$DynlvF2{w^vW}G<CS9*<~0{2
zpFMOAeH6Usi`R~C|J?ZV@^44Jv-t3b`7o{Q>7s<J=jnpMr|xPk>vQtW*-1>k<=ptY
zzdrxog7~XXzkMJ>k#v0Sh6|gYT<x<Ky<V_FD6IMQrQwrW?1l@c`}UvR<E9)i_>ANF
zq%K3o-VXfpNq0bQY2wt##VN?ewaCSa9$kFn9)3wXtZ>}1r1kWDGdEOri28rLc8dGV
zcjeK|)t5Xuw>2E?>fxI87eAi%S?LOPVcxnO$sbvMSl4#(>dcbfjkDg{UbyJc1?1t%
zS&2I@^WW5Ocwp9Ba^~5)kcX#ST+R-9uKcUoAro_tU!FR7(+Rnu-^|8O4Rf;B&V8+_
zdD6-yj<%Jr6rK@2YkN!F@k-5_GhZY*K9Ee)uIxMg_OEf*x2cI-T{Lsl+sf<l_1uS4
zsm4lOdJ6QlF%YyRJT)dVXp7nzjhTTLrUsA06ZQ4$jrGvN@8wU7`wFPx{lC^$v|47o
zIJlj^b>rpjOFwNq>(Qm`?Ak*gO)vib%l5_f8!i>K{=6}3lialM-s%-QQ|7Jt;9Y6!
z$#m31=O+!K_up3geQRrKuMX2ki%}2x^i}`4ZOS8ezj`(08?L#g^0|sr4bQA9^mcsJ
zK6Km<cRuWyv1U=#x7H4+)OU9B;yshf-q*Aoo^|r&CjG`Kip}Drn2tfg*p%93K4j>T
zz}g?J!#g7|b0`MWgU+}px*`DX>nT6f14evjW@MRldE~qtf1d~U3J_m*@_lfZpaMn+
z0_IK3{axaLKYDKf)#nMM1#A4H_W}I=wMXm&Bx1eMu|QB`EdKARKNkP@5F!x}5D*X$
V5Zr`7u?69O0s;a80)oF6{ts6yA$0%%
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/test_Chrome_passwords.js
@@ -0,0 +1,213 @@
+Cu.import("resource://gre/modules/OSCrypto.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+const PROFILE = {
+  id: "Default",
+  name: "Person 1",
+};
+
+const TEST_LOGINS = [
+  {
+    id: 1, // id of the row in the chrome login db
+    username: "username",
+    password: "password",
+    hostname: "https://c9.io",
+    formSubmitURL: "https://c9.io",
+    httpRealm: null,
+    usernameField: "inputEmail",
+    passwordField: "inputPassword",
+    timeCreated: 1437418416037,
+    timePasswordChanged: 1437418416037,
+    timesUsed: 1,
+  },
+  {
+    id: 2,
+    username: "username@gmail.com",
+    password: "password2",
+    hostname: "https://accounts.google.com",
+    formSubmitURL: "https://accounts.google.com",
+    httpRealm: null,
+    usernameField: "Email",
+    passwordField: "Passwd",
+    timeCreated: 1437418446598,
+    timePasswordChanged: 1437418446598,
+    timesUsed: 6,
+  },
+  {
+    id: 3,
+    username: "username",
+    password: "password3",
+    hostname: "https://www.facebook.com",
+    formSubmitURL: "https://www.facebook.com",
+    httpRealm: null,
+    usernameField: "email",
+    passwordField: "pass",
+    timeCreated: 1437418478851,
+    timePasswordChanged: 1437418478851,
+    timesUsed: 1,
+  },
+  {
+    id: 4,
+    username: "user",
+    password: "password",
+    hostname: "http://httpbin.org",
+    formSubmitURL: null,
+    httpRealm: "me@kennethreitz.com", // Digest auth.
+    usernameField: "",
+    passwordField: "",
+    timeCreated: 1437787462368,
+    timePasswordChanged: 1437787462368,
+    timesUsed: 1,
+  },
+  {
+    id: 5,
+    username: "buser",
+    password: "bpassword",
+    hostname: "http://httpbin.org",
+    formSubmitURL: null,
+    httpRealm: "Fake Realm", // Basic auth.
+    usernameField: "",
+    passwordField: "",
+    timeCreated: 1437787539233,
+    timePasswordChanged: 1437787539233,
+    timesUsed: 1,
+  },
+];
+
+let crypto = new OSCrypto();
+let dbConn;
+
+function promiseSetPassword(login) {
+  return new Promise((resolve, reject) => {
+    let stmt = dbConn.createAsyncStatement(`
+      UPDATE logins
+      SET password_value = :password_value
+      WHERE rowid = :rowid
+    `);
+    let passwordValue = crypto.encryptData(login.password);
+    stmt.bindBlobByName("password_value", passwordValue, passwordValue.length);
+    stmt.params.rowid = login.id;
+
+    stmt.executeAsync({
+      handleError(aError) {
+        reject("Error with the query: " + aError.message);
+      },
+
+      handleCompletion(aReason) {
+        if (aReason === Ci.mozIStorageStatementCallback.REASON_FINISHED){
+          resolve();
+        } else {
+          reject("Query has failed: " + aReason);
+        }
+      },
+    });
+    stmt.finalize();
+  });
+}
+
+function checkLoginsAreEqual(passwordManagerLogin, chromeLogin, id) {
+  passwordManagerLogin.QueryInterface(Ci.nsILoginMetaInfo);
+
+  Assert.equal(passwordManagerLogin.username, chromeLogin.username,
+               "The two logins ID " + id + " have the same username");
+  Assert.equal(passwordManagerLogin.password, chromeLogin.password,
+               "The two logins ID " + id + " have the same password");
+  Assert.equal(passwordManagerLogin.hostname, chromeLogin.hostname,
+               "The two logins ID " + id + " have the same hostname");
+  Assert.equal(passwordManagerLogin.formSubmitURL, chromeLogin.formSubmitURL,
+               "The two logins ID " + id + " have the same formSubmitURL");
+  Assert.equal(passwordManagerLogin.httpRealm, chromeLogin.httpRealm,
+               "The two logins ID " + id + " have the same httpRealm");
+  Assert.equal(passwordManagerLogin.usernameField, chromeLogin.usernameField,
+               "The two logins ID " + id + " have the same usernameElement");
+  Assert.equal(passwordManagerLogin.passwordField, chromeLogin.passwordField,
+               "The two logins ID " + id + " have the same passwordElement");
+  Assert.equal(passwordManagerLogin.timeCreated, chromeLogin.timeCreated,
+               "The two logins ID " + id + " have the same timeCreated");
+  Assert.equal(passwordManagerLogin.timePasswordChanged, chromeLogin.timePasswordChanged,
+               "The two logins ID " + id + " have the same timePasswordChanged");
+  Assert.equal(passwordManagerLogin.timesUsed, chromeLogin.timesUsed,
+               "The two logins ID " + id + " have the same timesUsed");
+}
+
+function generateDifferentLogin(login) {
+  let newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
+
+  newLogin.init(login.hostname, login.formSubmitURL, null,
+                login.username, login.password + 1, login.usernameField + 1,
+                login.passwordField + 1);
+  newLogin.QueryInterface(Ci.nsILoginMetaInfo);
+  newLogin.timeCreated = login.timeCreated + 1;
+  newLogin.timePasswordChanged = login.timePasswordChanged + 1;
+  newLogin.timesUsed = login.timesUsed + 1;
+  return newLogin;
+}
+
+add_task(function* setup() {
+  let loginDataFile = do_get_file("AppData/Local/Google/Chrome/User Data/Default/Login Data");
+  dbConn = Services.storage.openUnsharedDatabase(loginDataFile);
+  registerFakePath("LocalAppData", do_get_file("AppData/Local/"));
+
+  do_register_cleanup(() => {
+    Services.logins.removeAllLogins();
+    dbConn.asyncClose();
+    crypto.finalize();
+  });
+});
+
+add_task(function* test_importIntoEmptyDB() {
+  for (let login of TEST_LOGINS) {
+    yield promiseSetPassword(login);
+  }
+
+  let migrator = MigrationUtils.getMigrator("chrome");
+  Assert.ok(migrator.sourceExists, "Sanity check the source exists");
+
+  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");
+
+  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() {
+  let migrator = MigrationUtils.getMigrator("chrome");
+  Assert.ok(migrator.sourceExists, "Sanity check the source exists");
+
+  Services.logins.removeAllLogins();
+  let logins = Services.logins.getAllLogins({});
+  Assert.equal(logins.length, 0, "There are no logins after removing all of them");
+
+  let newLogins = [];
+
+  // Create 3 new logins that are different but where the key properties are still the same.
+  for (let i = 0; i < 3; i++) {
+    newLogins.push(generateDifferentLogin(TEST_LOGINS[i]));
+    Services.logins.addLogin(newLogins[i]);
+  }
+
+  logins = Services.logins.getAllLogins({});
+  Assert.equal(logins.length, newLogins.length, "Check login count after the insertion");
+
+  for (let i = 0; i < newLogins.length; i++) {
+    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");
+
+  for (let i = 0; i < newLogins.length; i++) {
+    checkLoginsAreEqual(logins[i], newLogins[i], i + 1);
+  }
+});
--- a/browser/components/migration/tests/unit/xpcshell.ini
+++ b/browser/components/migration/tests/unit/xpcshell.ini
@@ -1,17 +1,20 @@
 [DEFAULT]
 head = head_migration.js
 tail =
 firefox-appdir = browser
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 support-files =
   Library/**
+  AppData/**
 
 [test_Chrome_cookies.js]
 skip-if = os != "mac" # Relies on ULibDir
+[test_Chrome_passwords.js]
+skip-if = os != "win"
 [test_fx_fhr.js]
 [test_IE_bookmarks.js]
 skip-if = os != "win"
 [test_IE_cookies.js]
 skip-if = os != "win"
 [test_Safari_bookmarks.js]
 skip-if = os != "mac"
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1099,17 +1099,16 @@ BrowserGlue.prototype = {
     }
     var rokuDevice = {
       id: "roku:ecp",
       target: "roku:ecp",
       factory: function(aService) {
         Cu.import("resource://gre/modules/RokuApp.jsm");
         return new RokuApp(aService);
       },
-      mirror: true,
       types: ["video/mp4"],
       extensions: ["mp4"]
     };
 
     // Register targets
     SimpleServiceDiscovery.registerDevice(rokuDevice);
 
     // Search for devices continuously every 120 seconds
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -363,28 +363,28 @@ var gMainPane = {
     win = wm.getMostRecentWindow("navigator:browser");
 
     if (win && win.document.documentElement
                   .getAttribute("windowtype") == "navigator:browser") {
       // We should only include visible & non-pinned tabs
 
       tabs = win.gBrowser.visibleTabs.slice(win.gBrowser._numPinnedTabs);
       
-      tabs = tabs.filter(this.isAboutPreferences);
+      tabs = tabs.filter(this.isNotAboutPreferences);
     }
     
     return tabs;
   },
   
   /**
    * Check to see if a tab is not about:preferences
    */
-  isAboutPreferences: function (aElement, aIndex, aArray)
+  isNotAboutPreferences: function (aElement, aIndex, aArray)
   {
-    return (aElement.linkedBrowser.currentURI.spec != "about:preferences");
+    return (aElement.linkedBrowser.currentURI.spec.startsWith != "about:preferences");
   },
 
   /**
    * Restores the default home page as the user's home page.
    */
   restoreDefaultHomePage: function ()
   {
     var homePage = document.getElementById("browser.startup.homepage");
--- a/browser/components/search/test/browser_abouthome_behavior.js
+++ b/browser/components/search/test/browser_abouthome_behavior.js
@@ -85,17 +85,17 @@ function test() {
       name: "Search with Google from about:home",
       searchURL: replaceUrl("https://www.google.com/search?q=foo&ie=utf-8&oe=utf-8"),
       run: function () {
         verify_about_home_search("Google");
       }
     },
     {
       name: "Search with Amazon.com from about:home",
-      searchURL: replaceUrl("http://www.amazon.com/exec/obidos/external-search/?field-keywords=foo&mode=blended&tag=mozilla-20&sourceid=Mozilla-search"),
+      searchURL: replaceUrl("https://www.amazon.com/exec/obidos/external-search/?field-keywords=foo&mode=blended&tag=mozilla-20&sourceid=Mozilla-search"),
       run: function () {
         verify_about_home_search("Amazon.com");
       }
     }
   ];
 
   function nextTest() {
     if (gTests.length) {
--- a/browser/components/search/test/browser_amazon.js
+++ b/browser/components/search/test/browser_amazon.js
@@ -8,49 +8,49 @@
 "use strict";
 
 const BROWSER_SEARCH_PREF = "browser.search.";
 
 function test() {
   let engine = Services.search.getEngineByName("Amazon.com");
   ok(engine, "Amazon.com");
 
-  let base = "http://www.amazon.com/exec/obidos/external-search/?field-keywords=foo&mode=blended&tag=mozilla-20&sourceid=Mozilla-search";
+  let base = "https://www.amazon.com/exec/obidos/external-search/?field-keywords=foo&mode=blended&tag=mozilla-20&sourceid=Mozilla-search";
   let url;
 
   // Test search URLs (including purposes).
   url = engine.getSubmission("foo").uri.spec;
   is(url, base, "Check search URL for 'foo'");
 
   // Check search suggestion URL.
   url = engine.getSubmission("foo", "application/x-suggestions+json").uri.spec;
   is(url, "https://completion.amazon.com/search/complete?q=foo&search-alias=aps&mkt=1", "Check search suggestion URL for 'foo'");
 
   // Check all other engine properties.
   const EXPECTED_ENGINE = {
     name: "Amazon.com",
     alias: null,
     description: "Amazon.com Search",
-    searchForm: "http://www.amazon.com/exec/obidos/external-search/?field-keywords=&mode=blended&tag=mozilla-20&sourceid=Mozilla-search",
+    searchForm: "https://www.amazon.com/exec/obidos/external-search/?field-keywords=&mode=blended&tag=mozilla-20&sourceid=Mozilla-search",
     type: Ci.nsISearchEngine.TYPE_MOZSEARCH,
     hidden: false,
     wrappedJSObject: {
       queryCharset: "UTF-8",
       "_iconURL": "data:image/x-icon;base64,AAABAAIAEBAAAAAAAAC0AQAAJgAAACAgAAAAAAAA6QIAANoBAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAF7SURBVDjLlZPLasJAFIaFRF+iVV+h6hO0GF+gVB9AaHwDt64qCG03tQgtdCFIuyhUelmGli66MXThSt24kNiFBUlAYi6ezjnNxSuawB/ITP7v/HNmJgQAEaZzpgHs/gwcTyTEXuXl2U6nA8ViEbK5HKler28CVRAwnB9ptVrAh8MrQuCaZ4iA8fzIqSgCxwzpTIaSuN/RWGwdYLwCUBQFZFkGSZLgqdmEE7YEN8VOAKyaSKUW4nNBAFmnYiKZpDRX1WqwBBzP089n5f/NEQsFL4WqqtsBWJlzDAJr5PwSMM1awEzzdxIbGI3Hvc6jCZeVFgRQRwpY7Qcw3ktgfpR8wLRxCPaot/X4GS95MppfF6DX9n2A3f+kAZycaT8bAZjU6r6B/duD6d3BYg9wQq/tkYzHY1blEiz5lmQyGc95mrO6r2CxgpjCBXgNsJVviolpXJiraeOIjJRE10juUa4sR8V+mO17VvmGqtuOcdNlwut8zTQJcJ0njifyB2bgTdKh6w4BAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACsElEQVRYw71XQWsTURBe2LQgeNKLB+tVemt6txcteNSD/QGC6VEIGDx5s+eKPQqFgJhLNdFLBWMP7cU0oSAWjB70koC9WHbVQ5SO8+XtS14mr7svyaYDH9m87Jv55puZt1nPi4yIzjMeMj7T9OwjI88455nGC1cZX+nsDESumJmPFDwIAqrX6z00Gg1qt9vjkJgFgUeuO16Vy3RjeZkyMzM9+MY1fsM9I9h9zyV7ZAznZrA4FAoFVwJ1z+WuOysrg1lnMolkHJX4k0igzI5sARYWF7vEZEk0rvO6iyUSuJfLJUqM7zYSqRDIra4OOUZPmNZsNrsl8UVTpkJAjh1GzmaSpJ8mAWmYeZB5urHRhW5SNOfUCCDo47W1bvPZsp2qAhipy3Nz1kaLG8dUCEBqM5AvpgElqFar01NgIZsdco7Zb7VasU2YigIYL5tjqCL7Q5YkFQXKlcqQ7DbHthIALk/IWAKor82xPIhshxWABCYioDMz51sexcVi0XoG4DPLIyvJjkTArK3scDQnRvO0MdTrUHGiKZCP4tNgO6BAEI08EQH9Z2Qow0hyPypJGIa9p6JWKCn4SA8jSKmJIDgyRvPJkcRxjfUwNGr/i8+Mo32iHzWiThBD4NM60bet9P77/ubA728RlTjMiwiH6zEEfvIrwdZFtQmMJ7W/ofIDBZD5m3mVZGwJcOP2kmILIlCkE45HoPWurwCSg0+UQRD4ZyXxId+T7gQb9+4q9sioY5ltrOG3L5vqXiiJffDx/aUi83ZJ7jr2ohcEu8Hh6/m+I7OWGiVxbWKHsz+O3vSOakqFQdsFgQeJUiKD7Wv9YKXBgCeSUC3v2kM5EJhlHDh3NcgcPlG1BXZu98sDmTuBa4fsMnz9fniJUaGzs+eMC540XuR0aDO2L8Y3qPyMcdOM+R/8XcqRA3qp9gAAAABJRU5ErkJggg==",
       _urls : [
         {
           type: "application/x-suggestions+json",
           method: "GET",
           template: "https://completion.amazon.com/search/complete?q={searchTerms}&search-alias=aps&mkt=1",
           params: "",
         },
         {
           type: "text/html",
           method: "GET",
-          template: "http://www.amazon.com/exec/obidos/external-search/",
+          template: "https://www.amazon.com/exec/obidos/external-search/",
           params: [
             {
               name: "field-keywords",
               value: "{searchTerms}",
               purpose: undefined,
             },
             {
               name: "mode",
--- a/browser/components/search/test/browser_amazon_behavior.js
+++ b/browser/components/search/test/browser_amazon_behavior.js
@@ -13,17 +13,17 @@ const BROWSER_SEARCH_PREF = "browser.sea
 function test() {
   let engine = Services.search.getEngineByName("Amazon.com");
   ok(engine, "Amazon is installed");
 
   let previouslySelectedEngine = Services.search.currentEngine;
   Services.search.currentEngine = engine;
   engine.alias = "a";
 
-  let base = "http://www.amazon.com/exec/obidos/external-search/?field-keywords=foo&mode=blended&tag=mozilla-20&sourceid=Mozilla-search";
+  let base = "https://www.amazon.com/exec/obidos/external-search/?field-keywords=foo&mode=blended&tag=mozilla-20&sourceid=Mozilla-search";
   let url;
 
   // Test search URLs (including purposes).
   url = engine.getSubmission("foo").uri.spec;
   is(url, base, "Check search URL for 'foo'");
 
   waitForExplicitFinish();
 
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -177,16 +177,20 @@ this.SessionStore = {
   get promiseInitialized() {
     return SessionStoreInternal.promiseInitialized;
   },
 
   get canRestoreLastSession() {
     return SessionStoreInternal.canRestoreLastSession;
   },
 
+  get crashedTabCount() {
+    return SessionStoreInternal._crashedBrowsersCount;
+  },
+
   set canRestoreLastSession(val) {
     SessionStoreInternal.canRestoreLastSession = val;
   },
 
   init: function ss_init() {
     SessionStoreInternal.init();
   },
 
@@ -297,16 +301,20 @@ this.SessionStore = {
   getCurrentState: function (aUpdateAll) {
     return SessionStoreInternal.getCurrentState(aUpdateAll);
   },
 
   reviveCrashedTab(aTab) {
     return SessionStoreInternal.reviveCrashedTab(aTab);
   },
 
+  reviveAllCrashedTabs() {
+    return SessionStoreInternal.reviveAllCrashedTabs();
+  },
+
   navigateAndRestore(tab, loadArguments, historyIndex) {
     return SessionStoreInternal.navigateAndRestore(tab, loadArguments, historyIndex);
   }
 };
 
 // Freeze the SessionStore object. We don't want anyone to modify it.
 Object.freeze(SessionStore);
 
@@ -326,16 +334,19 @@ let SessionStoreInternal = {
   // For each <browser> element, records the current epoch.
   _browserEpochs: new WeakMap(),
 
   // Any browsers that fires the oop-browser-crashed event gets stored in
   // here - that way we know which browsers to ignore messages from (until
   // they get restored).
   _crashedBrowsers: new WeakSet(),
 
+  // The number of crashed browsers.
+  _crashedBrowsersCount: 0,
+
   // A map (xul:browser -> nsIFrameLoader) that maps a browser to the last
   // associated frameLoader we heard about.
   _lastKnownFrameLoader: new WeakMap(),
 
   // A map (xul:browser -> object) that maps a browser associated with a
   // recently closed tab to all its necessary state information we need to
   // properly handle final update message.
   _closedTabs: new WeakMap(),
@@ -1403,16 +1414,20 @@ let SessionStoreInternal = {
 
     if (browser.frameLoader) {
       this._lastKnownFrameLoader.set(browser.permanentKey, browser.frameLoader);
     }
 
     if (!aNoNotification) {
       this.saveStateDelayed(aWindow);
     }
+
+    if (this._crashedBrowsers.has(browser.permanentKey)) {
+      this._crashedBrowsersCount++;
+    }
   },
 
   /**
    * remove listeners for a tab
    * @param aWindow
    *        Window reference
    * @param aTab
    *        Tab reference
@@ -1432,16 +1447,20 @@ let SessionStoreInternal = {
       this._resetTabRestoringState(aTab);
       if (previousState == TAB_STATE_RESTORING)
         this.restoreNextTab();
     }
 
     if (!aNoNotification) {
       this.saveStateDelayed(aWindow);
     }
+
+    if (this._crashedBrowsers.has(browser.permanentKey)) {
+      this._crashedBrowsersCount--;
+    }
   },
 
   /**
    * When a tab closes, collect its properties
    * @param aWindow
    *        Window reference
    * @param aTab
    *        Tab reference
@@ -1611,16 +1630,17 @@ let SessionStoreInternal = {
    *
    * @param aWindow
    *        The window that the crashed browser belongs to.
    * @param aBrowser
    *        The <xul:browser> that is now in the crashed state.
    */
   onBrowserCrashed: function(aWindow, aBrowser) {
     this._crashedBrowsers.add(aBrowser.permanentKey);
+    this._crashedBrowsersCount++;
     // If we never got around to restoring this tab, clear its state so
     // that we don't try restoring if the user switches to it before
     // reviving the crashed browser. This is throwing away the information
     // that the tab was in a pending state when the browser crashed, which
     // is an explicit choice. For now, when restoring all crashed tabs, based
     // on a user preference we'll either restore all of them at once, or only
     // restore the selected tab and lazily restore the rest. We'll make no
     // efforts at this time to be smart and restore all of the tabs that had
@@ -2166,16 +2186,33 @@ let SessionStoreInternal = {
     // at this point.
     if (browser.isRemoteBrowser) {
       throw new Error("SessionStore.reviveCrashedTab: " +
                       "Somehow a crashed browser is still remote.")
     }
 
     let data = TabState.collect(aTab);
     this.restoreTab(aTab, data);
+
+    this._crashedBrowsersCount--;
+  },
+
+  /**
+   * Revive all crashed tabs and reset the crashed tabs count to 0.
+   */
+  reviveAllCrashedTabs() {
+    let windowsEnum = Services.wm.getEnumerator("navigator:browser");
+    while (windowsEnum.hasMoreElements()) {
+      let window = windowsEnum.getNext();
+      for (let tab of window.gBrowser.tabs) {
+        this.reviveCrashedTab(tab);
+      }
+    }
+
+    this._crashedBrowsersCount = 0;
   },
 
   /**
    * Navigate the given |tab| by first collecting its current state and then
    * either changing only the index of the currently shown shistory entry,
    * or restoring the exact same state again and passing the new URL to load
    * in |loadArguments|. Use this method to seamlessly switch between pages
    * loaded in the parent and pages loaded in the child process.
--- a/browser/components/sessionstore/test/browser_crashedTabs.js
+++ b/browser/components/sessionstore/test/browser_crashedTabs.js
@@ -336,8 +336,54 @@ add_task(function test_close_tab_after_c
   let promise = promiseEvent(gBrowser.tabContainer, "TabClose");
 
   // Click the close tab button
   clickButton(browser, "closeTab");
   yield promise;
 
   is(gBrowser.tabs.length, 1, "Should have closed the tab");
 });
+
+/**
+ * Checks that "restore all" button is only shown if more than one tab
+ * has crashed.
+ */
+add_task(function* test_hide_restore_all_button() {
+  let newTab = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+  let browser = newTab.linkedBrowser;
+  ok(browser.isRemoteBrowser, "Should be a remote browser");
+  yield promiseBrowserLoaded(browser);
+
+  browser.loadURI(PAGE_1);
+  yield promiseBrowserLoaded(browser);
+
+  yield TabStateFlusher.flush(browser);
+
+  // Crash the tab
+  yield crashBrowser(browser);
+
+  let doc = browser.contentDocument;
+  let restoreAllButton = doc.getElementById("restoreAll");
+  let restoreOneButton = doc.getElementById("restoreTab");
+
+  is(restoreAllButton.getAttribute("hidden"), "true", "Restore All button should be hidden");
+  ok(restoreOneButton.classList.contains("primary"), "Restore Tab button should have the primary class");
+
+  let newTab2 = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+
+  browser.loadURI(PAGE_2);
+  yield promiseBrowserLoaded(browser);
+
+  // Crash the tab
+  yield crashBrowser(browser);
+
+  doc = browser.contentDocument;
+  restoreAllButton = doc.getElementById("restoreAll");
+  restoreOneButton = doc.getElementById("restoreTab");
+
+  ok(!restoreAllButton.hasAttribute("hidden"), "Restore All button should not be hidden");
+  ok(!(restoreOneButton.classList.contains("primary")), "Restore Tab button should not have the primary class");
+
+  gBrowser.removeTab(newTab);
+  gBrowser.removeTab(newTab2);
+});
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -2071,18 +2071,16 @@ this.UITour = {
   },
 };
 
 this.UITour.init();
 
 /**
  * UITour Health Report
  */
-const DAILY_DISCRETE_TEXT_FIELD = Metrics.Storage.FIELD_DAILY_DISCRETE_TEXT;
-
 /**
  * Public API to be called by the UITour code
  */
 const UITourHealthReport = {
   recordTreatmentTag: function(tag, value) {
   TelemetryController.submitExternalPing("uitour-tag",
     {
       version: 1,
@@ -2110,16 +2108,19 @@ const UITourHealthReport = {
 
       // Get the UITourMetricsProvider instance from the Health Reporter
       reporter.getProvider("org.mozilla.uitour").recordTreatmentTag(tag, value);
     });
 #endif
   }
 };
 
+#ifdef MOZ_SERVICES_HEALTHREPORT
+const DAILY_DISCRETE_TEXT_FIELD = Metrics.Storage.FIELD_DAILY_DISCRETE_TEXT;
+
 this.UITourMetricsProvider = function() {
   Metrics.Provider.call(this);
 }
 
 UITourMetricsProvider.prototype = Object.freeze({
   __proto__: Metrics.Provider.prototype,
 
   name: "org.mozilla.uitour",
@@ -2179,8 +2180,9 @@ UITourTreatmentMeasurement1.prototype = 
 
     for (let [field, data] of data) {
       result[field] = data;
     }
 
     return result;
   }
 });
+#endif
--- a/browser/config/mozconfigs/linux32/debug-asan
+++ b/browser/config/mozconfigs/linux32/debug-asan
@@ -5,15 +5,18 @@ ac_add_options --with-google-oauth-api-k
 ac_add_options --enable-debug
 ac_add_options --enable-optimize="-O1"
 
 # ASan specific options on Linux
 ac_add_options --enable-valgrind
 
 . $topsrcdir/build/unix/mozconfig.asan
 
+export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
+
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux32/nightly-asan
+++ b/browser/config/mozconfigs/linux32/nightly-asan
@@ -4,15 +4,18 @@ ac_add_options --with-google-oauth-api-k
 ac_add_options --disable-debug
 ac_add_options --enable-optimize="-O2 -g"
 
 # ASan specific options on Linux
 ac_add_options --enable-valgrind
 
 . $topsrcdir/build/unix/mozconfig.asan
 
+export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
+
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux32/valgrind
+++ b/browser/config/mozconfigs/linux32/valgrind
@@ -1,11 +1,9 @@
 . $topsrcdir/browser/config/mozconfigs/linux32/nightly
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 ac_add_options --enable-valgrind
 ac_add_options --disable-jemalloc
 ac_add_options --disable-install-strip
 
 # Include the override mozconfig again (even though the above includes it)
 # since it's supposed to override everything.
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/debug-asan
+++ b/browser/config/mozconfigs/linux64/debug-asan
@@ -3,19 +3,20 @@ ac_add_options --with-google-oauth-api-k
 # Use at least -O1 for optimization to avoid stack space
 # exhaustions caused by Clang function inlining.
 ac_add_options --enable-debug
 ac_add_options --enable-optimize="-O1"
 
 # ASan specific options on Linux
 ac_add_options --enable-valgrind
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
+. $topsrcdir/build/unix/mozconfig.asan
 
-. $topsrcdir/build/unix/mozconfig.asan
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/debug-static-analysis-clang
+++ b/browser/config/mozconfigs/linux64/debug-static-analysis-clang
@@ -2,21 +2,22 @@ MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.common"
 
 ac_add_options --enable-debug
 ac_add_options --enable-dmd
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 # Use Clang as specified in manifest
 export CC="$topsrcdir/clang/bin/clang"
 export CXX="$topsrcdir/clang/bin/clang++"
 
 # Add the static checker
 ac_add_options --enable-clang-plugin
 
 # Avoid dependency on libstdc++ 4.7
 ac_add_options --enable-stdcxx-compat
 
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
+
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/hazards
+++ b/browser/config/mozconfigs/linux64/hazards
@@ -6,26 +6,27 @@
 # tooltool-installed gcc, and the analysis works by wrapping the gcc invocation
 # with a script that invokes the real gcc with -fplugin and its configuration
 # directives. Instead, duplicate the contents of that mozconfig here:
 
 . "$topsrcdir/build/mozconfig.common"
 ac_add_options --enable-elf-hack
 ac_add_options --enable-stdcxx-compat
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 # The objdir must be at a known location so its path can be stripped from the
 # filenames stored by the analysis
 mk_add_options MOZ_OBJDIR=obj-analyzed
 
 # The configuration options are chosen to compile the most code
 # (--enable-debug, --enable-tests) in the trickiest way possible
 # (--enable-optimize) to maximize the chance of seeing tricky static orderings.
 ac_add_options --enable-debug
 ac_add_options --enable-tests
 ac_add_options --enable-optimize
 
 CFLAGS="$CFLAGS -Wno-attributes"
 CPPFLAGS="$CPPFLAGS -Wno-attributes"
 CXXFLAGS="$CXXFLAGS -Wno-attributes"
 
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
+
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/nightly-asan
+++ b/browser/config/mozconfigs/linux64/nightly-asan
@@ -2,19 +2,20 @@ ac_add_options --with-google-oauth-api-k
 
 # We still need to build with debug symbols
 ac_add_options --disable-debug
 ac_add_options --enable-optimize="-O2 -gline-tables-only"
 
 # ASan specific options on Linux
 ac_add_options --enable-valgrind
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
+. $topsrcdir/build/unix/mozconfig.asan
 
-. $topsrcdir/build/unix/mozconfig.asan
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/opt-tsan
+++ b/browser/config/mozconfigs/linux64/opt-tsan
@@ -1,8 +1,11 @@
 ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
 
 . $topsrcdir/build/unix/mozconfig.tsan
 
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
+
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=tsan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/valgrind
+++ b/browser/config/mozconfigs/linux64/valgrind
@@ -1,13 +1,9 @@
 . $topsrcdir/browser/config/mozconfigs/linux64/nightly
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 ac_add_options --enable-valgrind
 ac_add_options --disable-jemalloc
 ac_add_options --disable-install-strip
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 # Include the override mozconfig again (even though the above includes it)
 # since it's supposed to override everything.
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/tooltool-manifests/linux64/tsan.manifest
+++ b/browser/config/tooltool-manifests/linux64/tsan.manifest
@@ -3,10 +3,17 @@
 "clang_version": "r241773"
 }, 
 {
 "size": 89690541, 
 "digest": "470d258d9785a120fcba65eee90daa632a42affa0f97f57d70fc8285bd76bcc27d4d0d70b6c37577ab271a04c843b6269425391a8d6df1967718dba26dd3a73d", 
 "algorithm": "sha512", 
 "filename": "clang.tar.bz2",
 "unpack": true
+},
+{
+"size": 4431740,
+"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3",
+"algorithm": "sha512",
+"filename": "gtk3.tar.xz",
+"unpack": true
 }
 ]
new file mode 100644
--- /dev/null
+++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
@@ -0,0 +1,34 @@
+[
+{
+"clang_version": "r183744"
+}, 
+{
+"size": 70350828, 
+"digest": "6cd04e8ec44c6fef159349c22bd0476891e4a2d46479f9586283eaf3305e42f79c720d40dfec0e78d8899c1651189b12e285de60862ffd0612b0dac7a0c336c6", 
+"algorithm": "sha512", 
+"unpack": true,
+"filename": "clang.tar.bz2"
+},
+{
+"size": 2581027, 
+"digest": "9b59abef2bd4ae3a5b792de96e1336d879c1c5b6b07382797ae1bcc299e74ddf805bc95b7bc813cf3b4586db5eb4d0f41d09b2f85f0629cf27e57a4de851129c", 
+"algorithm": "sha512", 
+"unpack": true,
+"filename": "cctools.tar.gz"
+},
+{
+"size": 35215976, 
+"visibility": "internal", 
+"digest": "8be736545ddab25ebded188458ce974d5c9a7e29f3c50d2ebfbcb878f6aff853dd2ff5a3528bdefc64396a10101a1b50fd2fe52000140df33643cebe1ea759da", 
+"algorithm": "sha512", 
+"unpack": true,
+"filename": "MacOSX10.7.sdk.tar.bz2"
+},
+{
+"size": 167175,
+"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+"algorithm": "sha512",
+"unpack": true,
+"filename": "sccache.tar.bz2"
+}
+]
--- a/browser/devtools/canvasdebugger/panel.js
+++ b/browser/devtools/canvasdebugger/panel.js
@@ -4,17 +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/. */
 "use strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
 const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const { CanvasFront } = require("devtools/server/actors/canvas");
-const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 function CanvasDebuggerPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
   this._destroyer = null;
 
   EventEmitter.decorate(this);
 };
--- a/browser/devtools/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js
+++ b/browser/devtools/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js
@@ -23,18 +23,20 @@ function* ifTestingSupported() {
     "There should be no stack container available yet for the draw call.");
 
   let callStackDisplayed = once(window, EVENTS.CALL_STACK_DISPLAYED);
   EventUtils.sendMouseEvent({ type: "mousedown" }, locationLink, window);
   yield callStackDisplayed;
 
   isnot($(".call-item-stack", callItem.target), null,
     "There should be a stack container available now for the draw call.");
-  is($all(".call-item-stack-fn", callItem.target).length, 4,
-    "There should be 4 functions on the stack for the draw call.");
+  // We may have more than 4 functions, depending on whether async
+  // stacks are available.
+  ok($all(".call-item-stack-fn", callItem.target).length >= 4,
+     "There should be at least 4 functions on the stack for the draw call.");
 
   ok($all(".call-item-stack-fn-name", callItem.target)[0].getAttribute("value")
     .includes("C()"),
     "The first function on the stack has the correct name.");
   ok($all(".call-item-stack-fn-name", callItem.target)[1].getAttribute("value")
     .includes("B()"),
     "The second function on the stack has the correct name.");
   ok($all(".call-item-stack-fn-name", callItem.target)[2].getAttribute("value")
--- a/browser/devtools/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js
+++ b/browser/devtools/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js
@@ -24,18 +24,20 @@ function* ifTestingSupported() {
     "There should be no stack container available yet for the draw call.");
 
   let callStackDisplayed = once(window, EVENTS.CALL_STACK_DISPLAYED);
   EventUtils.sendMouseEvent({ type: "mousedown" }, locationLink, window);
   yield callStackDisplayed;
 
   isnot($(".call-item-stack", callItem.target), null,
     "There should be a stack container available now for the draw call.");
-  is($all(".call-item-stack-fn", callItem.target).length, 4,
-    "There should be 4 functions on the stack for the draw call.");
+  // We may have more than 4 functions, depending on whether async
+  // stacks are available.
+  ok($all(".call-item-stack-fn", callItem.target).length >= 4,
+     "There should be at least 4 functions on the stack for the draw call.");
 
   let jumpedToSource = once(window, EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER);
   EventUtils.sendMouseEvent({ type: "mousedown" }, $(".call-item-location", callItem.target));
   yield jumpedToSource;
 
   let toolbox = yield gDevTools.getToolbox(target);
   let { panelWin: { DebuggerView: view } } = toolbox.getPanel("jsdebugger");
 
--- a/browser/devtools/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js
+++ b/browser/devtools/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js
@@ -35,27 +35,31 @@ function* ifTestingSupported() {
   is(view.hasAttribute("call-stack-populated"), true,
     "The call item's view should have the stack populated now.");
   is(view.getAttribute("call-stack-expanded"), "true",
     "The call item's view should have the stack expanded now.");
   isnot($(".call-item-stack", callItem.target), null,
     "There should be a stack container available now for the draw call.");
   is($(".call-item-stack", callItem.target).hidden, false,
     "The stack container should now be visible.");
-  is($all(".call-item-stack-fn", callItem.target).length, 4,
-    "There should be 4 functions on the stack for the draw call.");
+  // We may have more than 4 functions, depending on whether async
+  // stacks are available.
+  ok($all(".call-item-stack-fn", callItem.target).length >= 4,
+     "There should be at least 4 functions on the stack for the draw call.");
 
   EventUtils.sendMouseEvent({ type: "dblclick" }, contents, window);
 
   is(view.hasAttribute("call-stack-populated"), true,
     "The call item's view should still have the stack populated.");
   is(view.getAttribute("call-stack-expanded"), "false",
     "The call item's view should not have the stack expanded anymore.");
   isnot($(".call-item-stack", callItem.target), null,
     "There should still be a stack container available for the draw call.");
   is($(".call-item-stack", callItem.target).hidden, true,
     "The stack container should now be hidden.");
-  is($all(".call-item-stack-fn", callItem.target).length, 4,
-    "There should still be 4 functions on the stack for the draw call.");
+  // We may have more than 4 functions, depending on whether async
+  // stacks are available.
+  ok($all(".call-item-stack-fn", callItem.target).length >= 4,
+     "There should still be at least 4 functions on the stack for the draw call.");
 
   yield teardown(panel);
   finish();
 }
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -97,36 +97,32 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
 Cu.import("resource:///modules/devtools/SimpleListWidget.jsm");
 Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
 Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
 Cu.import("resource:///modules/devtools/VariablesView.jsm");
 Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
-const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
+const require = devtools.require;
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 const promise = require("devtools/toolkit/deprecated-sync-thenables");
 const Editor = require("devtools/sourceeditor/editor");
 const DebuggerEditor = require("devtools/sourceeditor/debugger.js");
 const {Tooltip} = require("devtools/shared/widgets/Tooltip");
 const FastListWidget = require("devtools/shared/widgets/FastListWidget");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Parser",
   "resource:///modules/devtools/Parser.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "devtools",
-  "resource://gre/modules/devtools/Loader.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "DevToolsUtils",
-  "resource://gre/modules/devtools/DevToolsUtils.jsm");
-
 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
   "resource://gre/modules/ShortcutUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
   "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
 
 Object.defineProperty(this, "NetworkHelper", {
   get: function() {
--- a/browser/devtools/debugger/panel.js
+++ b/browser/devtools/debugger/panel.js
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 const EventEmitter = require("devtools/toolkit/event-emitter");
-const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 function DebuggerPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
   this._destroyer = null;
 
   this._view = this.panelWin.DebuggerView;
   this._controller = this.panelWin.DebuggerController;
--- a/browser/devtools/debugger/test/browser_dbg_source-maps-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_source-maps-04.js
@@ -5,17 +5,16 @@
  * Test that bogus source maps don't break debugging.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_minified_bogus_map.html";
 const JS_URL = EXAMPLE_URL + "code_math_bogus_map.js";
 
 // This test causes an error to be logged in the console, which appears in TBPL
 // logs, so we are disabling that here.
-let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
 DevToolsUtils.reportingDisabled = true;
 
 let gPanel, gDebugger, gFrames, gSources, gPrefs;
 
 function test() {
   initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -12,17 +12,17 @@ let { Services } = Cu.import("resource:/
 let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 Services.prefs.setBoolPref("devtools.debugger.log", false);
 
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise: promise } = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { require } = devtools;
-let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 let { BrowserToolboxProcess } = Cu.import("resource:///modules/devtools/ToolboxProcess.jsm", {});
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { DebuggerClient, ObjectClient } =
   Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
 let { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 let EventEmitter = require("devtools/toolkit/event-emitter");
 const { promiseInvoke } = require("devtools/async-utils");
 let TargetFactory = devtools.TargetFactory;
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -1751,16 +1751,24 @@ Toolbox.prototype = {
       return promise.resolve();
     }
 
     let outstanding = () => {
       return Task.spawn(function*() {
         yield this.highlighterUtils.stopPicker();
         yield this._inspector.destroy();
         if (this._highlighter) {
+          // Note that if the toolbox is closed, this will work fine, but will fail
+          // in case the browser is closed and will trigger a noSuchActor message.
+          // We ignore the promise that |_hideBoxModel| returns, since we should still
+          // proceed with the rest of destruction if it fails.
+          // FF42+ now does the cleanup from the actor.
+          if (!this.highlighter.traits.autoHideOnDestroy) {
+            this.highlighterUtils.unhighlight();
+          }
           yield this._highlighter.destroy();
         }
         if (this._selection) {
           this._selection.destroy();
         }
 
         if (this.walker) {
           this.walker.off("highlighter-ready", this._highlighterReady);
--- a/browser/devtools/inspector/breadcrumbs.js
+++ b/browser/devtools/inspector/breadcrumbs.js
@@ -543,31 +543,33 @@ HTMLBreadcrumbs.prototype = {
    * probably visible. See LOW_PRIORITY_ELEMENTS.
    * @param {NodeFront} node The parent node.
    * @return {Promise} Resolves to the NodeFront.
    */
   getInterestingFirstNode: function(node) {
     let deferred = promise.defer();
 
     let fallback = null;
+    let lastNode = null;
 
     let moreChildren = () => {
       this.walker.children(node, {
-        start: fallback,
+        start: lastNode,
         maxNodes: 10,
         whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
       }).then(this.selectionGuard()).then(response => {
         for (let node of response.nodes) {
           if (!(node.tagName in LOW_PRIORITY_ELEMENTS)) {
             deferred.resolve(node);
             return;
           }
           if (!fallback) {
             fallback = node;
           }
+          lastNode = node;
         }
         if (response.hasLast) {
           deferred.resolve(fallback);
           return;
         }
         moreChildren();
       }).then(null, this.selectionGuardEnd);
     };
--- a/browser/devtools/inspector/test/browser_inspector_breadcrumbs.js
+++ b/browser/devtools/inspector/test/browser_inspector_breadcrumbs.js
@@ -8,16 +8,17 @@
 const TEST_URI = TEST_URL_ROOT + "doc_inspector_breadcrumbs.html";
 const NODES = [
   {selector: "#i1111", result: "i1 i11 i111 i1111"},
   {selector: "#i22", result: "i2 i22 i221"},
   {selector: "#i2111", result: "i2 i21 i211 i2111"},
   {selector: "#i21", result: "i2 i21 i211 i2111"},
   {selector: "#i22211", result: "i2 i22 i222 i2221 i22211"},
   {selector: "#i22", result: "i2 i22 i222 i2221 i22211"},
+  {selector: "#i3", result: "i3 i31"},
 ];
 
 add_task(function*() {
   let { inspector } = yield openInspectorForURL(TEST_URI);
   let container = inspector.panelDoc.getElementById("inspector-breadcrumbs");
 
   for (let node of NODES) {
     info("Testing node " + node.selector);
--- a/browser/devtools/inspector/test/doc_inspector_breadcrumbs.html
+++ b/browser/devtools/inspector/test/doc_inspector_breadcrumbs.html
@@ -37,11 +37,32 @@
         <div id="i222">
           <div id="i2221">
             <div id="i22211">
             </div>
           </div>
         </div>
       </div>
     </article>
+    <article id="i3">
+      <link id="i31" />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+    </article>
     <div id='pseudo-container'></div>
   </body>
 </html>
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -1461,21 +1461,16 @@ MarkupView.prototype = {
    */
   destroy: function() {
     if (this._destroyer) {
       return this._destroyer;
     }
 
     this._destroyer = promise.resolve();
 
-    // Note that if the toolbox is closed, this will work fine, but will fail
-    // in case the browser is closed and will trigger a noSuchActor message.
-    // We ignore the promise that |_hideBoxModel| returns, since we should still
-    // proceed with the rest of destruction if it fails.
-    this._hideBoxModel();
     this._clearBriefBoxModelTimer();
 
     this._elt.removeEventListener("click", this._onMouseClick, false);
 
     this._hoveredNode = null;
     this._inspector.toolbox.off("picker-node-hovered", this._onToolboxPickerHover);
 
     this.htmlEditor.destroy();
@@ -1935,16 +1930,17 @@ MarkupContainer.prototype = {
 
     let isMiddleClick = event.button === 1;
     let isMetaClick = event.button === 0 && (event.metaKey || event.ctrlKey);
 
     if (isMiddleClick || isMetaClick) {
       let link = target.dataset.link;
       let type = target.dataset.type;
       this.markup._inspector.followAttributeLink(type, link);
+      return;
     }
 
     // Start dragging the container after a delay.
     this.markup._dragStartEl = target;
     setTimeout(() => {
       // Make sure the mouse is still down and on target.
       if (!this._isMouseDown || this.markup._dragStartEl !== target ||
           this.node.isPseudoElement || this.node.isAnonymous ||
--- a/browser/devtools/markupview/test/browser_markupview_dragdrop_isDragging.js
+++ b/browser/devtools/markupview/test/browser_markupview_dragdrop_isDragging.js
@@ -20,27 +20,42 @@ add_task(function*() {
   el._onMouseDown({
     target: el.tagLine,
     pageX: rect.x,
     pageY: rect.y,
     stopPropagation: function() {},
     preventDefault: function() {}
   });
 
-  is(el.isDragging, false, "isDragging should not be set to true immedietly");
+  ok(!el.isDragging, "isDragging should not be set to true immediately");
 
   info("Waiting " + (GRAB_DELAY + 1) + "ms");
   yield wait(GRAB_DELAY + 1);
   ok(el.isDragging, "isDragging true after GRAB_DELAY has passed");
 
   let dropCompleted = once(inspector.markup, "drop-completed");
 
   info("Simulating mouseUp on #test");
   el._onMouseUp({
     target: el.tagLine,
     pageX: rect.x,
     pageY: rect.y
   });
 
   yield dropCompleted;
 
-  is(el.isDragging, false, "isDragging false after mouseUp");
+  ok(!el.isDragging, "isDragging false after mouseUp");
+
+  info("Simulating middle click on #test");
+  el._onMouseDown({
+    target: el.tagLine,
+    button: 1,
+    pageX: rect.x,
+    pageY: rect.y,
+    stopPropagation: function() {},
+    preventDefault: function() {}
+  });
+  ok(!el.isDragging, "isDragging should not be set to true immediately");
+
+  info("Waiting " + (GRAB_DELAY + 1) + "ms");
+  yield wait(GRAB_DELAY + 1);
+  ok(!el.isDragging, "isDragging never starts after middle click after mouseUp");
 });
--- a/browser/devtools/netmonitor/panel.js
+++ b/browser/devtools/netmonitor/panel.js
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 const EventEmitter = require("devtools/toolkit/event-emitter");
-const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 function NetMonitorPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
   this._destroyer = null;
 
   this._view = this.panelWin.NetMonitorView;
   this._controller = this.panelWin.NetMonitorController;
--- a/browser/devtools/performance/test/head.js
+++ b/browser/devtools/performance/test/head.js
@@ -5,17 +5,17 @@
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 let { Preferences } = Cu.import("resource://gre/modules/Preferences.jsm", {});
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { console } = devtools.require("resource://gre/modules/devtools/Console.jsm");
 let { merge } = devtools.require("sdk/util/object");
 let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
 let { getPerformanceFront, PerformanceFront } = devtools.require("devtools/performance/front");
 let TargetFactory = devtools.TargetFactory;
 
 let mm = null;
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -45,28 +45,28 @@ const VARIABLES_VIEW_URL = "chrome://bro
 
 const require   = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 
 const Telemetry = require("devtools/shared/telemetry");
 const Editor    = require("devtools/sourceeditor/editor");
 const TargetFactory = require("devtools/framework/target").TargetFactory;
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const {DevToolsWorker} = require("devtools/toolkit/shared/worker");
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
 Cu.import("resource://gre/modules/jsdebugger.jsm");
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/reflect.jsm");
-Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
   "resource:///modules/devtools/VariablesView.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "VariablesViewController",
   "resource:///modules/devtools/VariablesViewController.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "EnvironmentClient",
--- a/browser/devtools/scratchpad/test/head.js
+++ b/browser/devtools/scratchpad/test/head.js
@@ -4,19 +4,20 @@
 
 "use strict";
 
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
 const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
-const {DevToolsUtils} = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
+
 let gScratchpadWindow; // Reference to the Scratchpad chrome window object
 
 DevToolsUtils.testing = true;
 SimpleTest.registerCleanupFunction(() => {
   DevToolsUtils.testing = false;
 });
 
 /**
--- a/browser/devtools/shadereditor/panel.js
+++ b/browser/devtools/shadereditor/panel.js
@@ -4,17 +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/. */
 "use strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
 const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const { WebGLFront } = require("devtools/server/actors/webgl");
-const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 function ShaderEditorPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
   this._destroyer = null;
 
   EventEmitter.decorate(this);
 };
--- a/browser/devtools/shared/Parser.jsm
+++ b/browser/devtools/shared/Parser.jsm
@@ -4,17 +4,18 @@
  * License, v. 2.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 Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
 
 XPCOMUtils.defineLazyModuleGetter(this,
   "Reflect", "resource://gre/modules/reflect.jsm");
 
 this.EXPORTED_SYMBOLS = ["Parser", "ParserHelpers", "SyntaxTreeVisitor"];
 
 /**
  * A JS parser using the reflection API.
--- a/browser/devtools/shared/SplitView.jsm
+++ b/browser/devtools/shared/SplitView.jsm
@@ -3,17 +3,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/. */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["SplitView"];
 
 /* this must be kept in sync with CSS (ie. splitview.css) */
-const LANDSCAPE_MEDIA_QUERY = "(min-width: 551px)";
+const LANDSCAPE_MEDIA_QUERY = "(min-width: 701px)";
 
 let bindings = new WeakMap();
 
 /**
  * SplitView constructor
  *
  * Initialize the split view UI on an existing DOM element.
  *
--- a/browser/devtools/shared/splitview.css
+++ b/browser/devtools/shared/splitview.css
@@ -38,22 +38,18 @@ box,
 /* only the active details pane is shown */
 .splitview-side-details > * {
   display: none;
 }
 .splitview-side-details > .splitview-active {
   display: -moz-box;
 }
 
-.splitview-landscape-resizer {
-  cursor: ew-resize;
-}
-
 /* this is to keep in sync with SplitView.jsm's LANDSCAPE_MEDIA_QUERY */
-@media (min-width: 551px) {
+@media (min-width: 701px) {
   .splitview-root {
     -moz-box-orient: horizontal;
   }
   .splitview-controller {
     max-height: none;
   }
   .splitview-details {
     display: none;
@@ -74,26 +70,14 @@ ol.splitview-nav > li.splitview-filtered
 .splitview-nav + .splitview-nav.placeholder {
   display: none;
 }
 .splitview-nav.splitview-all-filtered ~ .splitview-nav.placeholder.all-filtered,
 .splitview-nav:empty ~ .splitview-nav.placeholder.empty {
   display: -moz-box;
 }
 
-.splitview-portrait-resizer {
-  display: none;
-}
-
 /* portrait mode */
-@media (max-width: 550px) {
-  .splitview-landscape-splitter {
-    display: none;
-  }
-
-  .splitview-portrait-resizer {
-    display: -moz-box;
-  }
-
+@media (max-width: 700px) {
   .splitview-controller {
     max-width: none;
   }
 }
--- a/browser/devtools/shared/test/browser_outputparser.js
+++ b/browser/devtools/shared/test/browser_outputparser.js
@@ -13,17 +13,17 @@ add_task(function*() {
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
 function* performTest() {
   let [host, , doc] = yield createHost("bottom", "data:text/html," +
     "<h1>browser_outputParser.js</h1><div></div>");
 
-  let parser = new OutputParser();
+  let parser = new OutputParser(doc);
   testParseCssProperty(doc, parser);
   testParseCssVar(doc, parser);
 
   host.destroy();
 }
 
 // Class name used in color swatch.
 let COLOR_TEST_CLASS = "test-class";
--- a/browser/devtools/shared/widgets/VariablesView.jsm
+++ b/browser/devtools/shared/widgets/VariablesView.jsm
@@ -16,23 +16,21 @@ const PAGE_SIZE_SCROLL_HEIGHT_RATIO = 10
 const PAGE_SIZE_MAX_JUMPS = 30;
 const SEARCH_ACTION_MAX_DELAY = 300; // ms
 const ITEM_FLASH_DURATION = 300 // ms
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
-Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
 Cu.import("resource://gre/modules/Task.jsm");
 let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 
-XPCOMUtils.defineLazyModuleGetter(this, "devtools",
-  "resource://gre/modules/devtools/Loader.jsm");
-
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
   "resource://gre/modules/PluralForm.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
   "@mozilla.org/widget/clipboardhelper;1",
   "nsIClipboardHelper");
 
 Object.defineProperty(this, "WebConsoleUtils", {
--- a/browser/devtools/shared/widgets/ViewHelpers.jsm
+++ b/browser/devtools/shared/widgets/ViewHelpers.jsm
@@ -11,17 +11,18 @@ const Cu = Components.utils;
 
 const PANE_APPEARANCE_DELAY = 50;
 const PAGE_SIZE_ITEM_COUNT_RATIO = 5;
 const WIDGET_FOCUSABLE_NODES = new Set(["vbox", "hbox"]);
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
-Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
 
 this.EXPORTED_SYMBOLS = [
   "Heritage", "ViewHelpers", "WidgetMethods",
   "setNamedTimeout", "clearNamedTimeout",
   "setConditionalTimeout", "clearConditionalTimeout",
 ];
 
--- a/browser/devtools/shared/widgets/filter-widget.css
+++ b/browser/devtools/shared/widgets/filter-widget.css
@@ -134,17 +134,18 @@ html, body {
 }
 
 .filter-value input {
   flex-grow: 1;
 }
 
 
 .theme-light .add,
-.theme-light .remove-button {
+.theme-light .remove-button,
+.theme-light #toggle-presets {
   filter: invert(1);
 }
 
 .preset {
   display: flex;
   margin-bottom: 10px;
   cursor: pointer;
   padding: 3px 5px;
@@ -212,18 +213,32 @@ html, body {
 }
 
 /* message shown when there's no filter specified */
 #container p {
   text-align: center;
   line-height: 20px;
 }
 
-.add {
-  background: url(chrome://browser/skin/devtools/add.svg);
+.add,
+#toggle-presets {
   background-size: cover;
   border: none;
   width: 16px;
   height: 16px;
   font-size: 0;
   vertical-align: middle;
   cursor: pointer;
+  margin: 0 5px;
 }
+
+.add {
+  background: url(chrome://browser/skin/devtools/add.svg);
+}
+
+#toggle-presets {
+  background: url(chrome://browser/skin/devtools/pseudo-class.svg#pseudo-class);
+}
+
+.show-presets #toggle-presets {
+  background: url(chrome://browser/skin/devtools/pseudo-class.svg#pseudo-class-checked);
+  filter: none;
+}
--- a/browser/devtools/styleeditor/styleeditor.xul
+++ b/browser/devtools/styleeditor/styleeditor.xul
@@ -86,17 +86,17 @@
     <xul:command id="cmd_find" oncommand="goDoCommand('cmd_find')"/>
     <xul:command id="cmd_findAgain" oncommand="goDoCommand('cmd_findAgain')"/>
   </xul:commandset>
 
   <xul:keyset id="sourceEditorKeys"/>
 
   <xul:stack id="style-editor-chrome" class="loading theme-body">
 
-    <xul:box class="splitview-root" context="sidebar-context">
+    <xul:box class="splitview-root devtools-responsive-container" context="sidebar-context">
       <xul:box class="splitview-controller">
         <xul:box class="splitview-main">
           <xul:toolbar class="devtools-toolbar">
              <xul:hbox class="devtools-toolbarbutton-group">
               <xul:toolbarbutton class="style-editor-newButton devtools-toolbarbutton"
                           accesskey="&newButton.accesskey;"
                           tooltiptext="&newButton.tooltip;"
                           label="&newButton.label;"/>
@@ -120,42 +120,39 @@
             <p><strong>&noStyleSheet.label;</strong></p>
             <p>&noStyleSheet-tip-start.label;
               <a href="#"
                 class="style-editor-newButton">&noStyleSheet-tip-action.label;</a>
               &noStyleSheet-tip-end.label;</p>
           </div>
         </xul:box> <!-- .splitview-nav-container -->
       </xul:box>   <!-- .splitview-controller -->
-      <xul:splitter class="devtools-side-splitter splitview-landscape-splitter devtools-invisible-splitter"/>
+      <xul:splitter class="devtools-side-splitter devtools-invisible-splitter"/>
       <xul:box class="splitview-side-details devtools-main-content"/>
 
       <div id="splitview-templates" hidden="true">
         <li id="splitview-tpl-summary-stylesheet" tabindex="0">
           <xul:label class="stylesheet-enabled" tabindex="0"
             tooltiptext="&visibilityToggle.tooltip;"
             accesskey="&saveButton.accesskey;"></xul:label>
           <hgroup class="stylesheet-info">
-            <h1><a class="stylesheet-name" tabindex="0"><xul:label crop="start"/></a></h1>
+            <h1><a class="stylesheet-name" tabindex="0"><xul:label crop="center"/></a></h1>
             <div class="stylesheet-more">
               <h3 class="stylesheet-title"></h3>
               <h3 class="stylesheet-linked-file"></h3>
               <h3 class="stylesheet-rule-count"></h3>
               <xul:spacer/>
               <h3><xul:label class="stylesheet-saveButton"
                     tooltiptext="&saveButton.tooltip;"
                     accesskey="&saveButton.accesskey;">&saveButton.label;</xul:label></h3>
             </div>
           </hgroup>
         </li>
 
         <xul:box id="splitview-tpl-details-stylesheet" class="splitview-details">
-          <xul:resizer class="splitview-portrait-resizer"
-                      dir="bottom"
-                      element="splitview-resizer-target"/>
           <xul:hbox class="stylesheet-details-container">
             <xul:box class="stylesheet-editor-input textbox"
                      data-placeholder="&editorTextbox.placeholder;"/>
             <xul:splitter class="devtools-side-splitter"/>
             <xul:vbox class="stylesheet-sidebar theme-sidebar" hidden="true">
               <xul:toolbar class="devtools-toolbar">
                 &mediaRules.label;
               </xul:toolbar>
--- a/browser/devtools/styleinspector/computed-view.js
+++ b/browser/devtools/styleinspector/computed-view.js
@@ -132,17 +132,17 @@ UpdateProcess.prototype = {
 function CssComputedView(inspector, document, pageStyle) {
   this.inspector = inspector;
   this.styleDocument = document;
   this.styleWindow = this.styleDocument.defaultView;
   this.pageStyle = pageStyle;
 
   this.propertyViews = [];
 
-  this._outputParser = new OutputParser();
+  this._outputParser = new OutputParser(document);
 
   let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
     .getService(Ci.nsIXULChromeRegistry);
   this.getRTLAttr = chromeReg.isLocaleRTL("global") ? "rtl" : "ltr";
 
   // Create bound methods.
   this.focusWindow = this.focusWindow.bind(this);
   this._onKeypress = this._onKeypress.bind(this);
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -329,17 +329,21 @@ ElementStyle.prototype = {
     // Gather all the text properties applied by these rules, ordered
     // from more- to less-specific. Text properties from keyframes rule are
     // excluded from being marked as overridden since a number of criteria such
     // as time, and animation overlay are required to be check in order to
     // determine if the property is overridden.
     let textProps = [];
     for (let rule of this.rules) {
       if (rule.pseudoElement == pseudo && !rule.keyframes) {
-        textProps = textProps.concat(rule.textProps.slice(0).reverse());
+        for (let textProp of rule.textProps.slice(0).reverse()) {
+          if (textProp.enabled) {
+            textProps.push(textProp);
+          }
+        }
       }
     }
 
     // Gather all the computed properties applied by those text
     // properties.
     let computedProps = [];
     for (let textProp of textProps) {
       computedProps = computedProps.concat(textProp.computed);
@@ -1156,17 +1160,17 @@ TextProperty.prototype = {
  */
 function CssRuleView(inspector, document, aStore, aPageStyle) {
   this.inspector = inspector;
   this.styleDocument = document;
   this.styleWindow = this.styleDocument.defaultView;
   this.store = aStore || {};
   this.pageStyle = aPageStyle;
 
-  this._outputParser = new OutputParser();
+  this._outputParser = new OutputParser(document);
 
   this._onKeypress = this._onKeypress.bind(this);
   this._onAddRule = this._onAddRule.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
   this._onCopy = this._onCopy.bind(this);
   this._onFilterStyles = this._onFilterStyles.bind(this);
   this._onFilterKeyPress = this._onFilterKeyPress.bind(this);
   this._onClearSearch = this._onClearSearch.bind(this);
@@ -2850,16 +2854,23 @@ TextPropertyEditor.prototype = {
    * Boolean indicating if the name or value is being currently edited.
    */
   get editing() {
     return !!(this.nameSpan.inplaceEditor || this.valueSpan.inplaceEditor ||
       this.ruleView.tooltips.isEditing) || this.popup.isOpen;
   },
 
   /**
+   * Get the rule to the current text property
+   */
+  get rule() {
+    return this.prop.rule;
+  },
+
+  /**
    * Create the property editor's DOM.
    */
   _create: function() {
     this.element = this.doc.createElementNS(HTML_NS, "li");
     this.element.classList.add("ruleview-property");
     this.element._textPropertyEditor = this;
 
     this.container = createChild(this.element, "div", {
@@ -2999,17 +3010,17 @@ TextPropertyEditor.prototype = {
   },
 
   /**
    * Get the path from which to resolve requests for this
    * rule's stylesheet.
    * @return {string} the stylesheet's href.
    */
   get sheetHref() {
-    let domRule = this.prop.rule.domRule;
+    let domRule = this.rule.domRule;
     if (domRule) {
       return domRule.href || domRule.nodeHref;
     }
   },
 
   /**
    * Get the URI from which to resolve relative requests for
    * this rule's stylesheet.
@@ -3063,24 +3074,24 @@ TextPropertyEditor.prototype = {
       this.element.classList.remove("ruleview-overridden");
     }
 
     let name = this.prop.name;
     this.nameSpan.textContent = name;
 
     // Combine the property's value and priority into one string for
     // the value.
-    let store = this.prop.rule.elementStyle.store;
-    let val = store.userProperties.getProperty(this.prop.rule.style, name,
+    let store = this.rule.elementStyle.store;
+    let val = store.userProperties.getProperty(this.rule.style, name,
                                                this.prop.value);
     if (this.prop.priority) {
       val += " !" + this.prop.priority;
     }
 
-    let propDirty = store.userProperties.contains(this.prop.rule.style, name);
+    let propDirty = store.userProperties.contains(this.rule.style, name);
 
     if (propDirty) {
       this.element.setAttribute("dirty", "");
     } else {
       this.element.removeAttribute("dirty");
     }
 
     const sharedSwatchClass = "ruleview-swatch ";
@@ -3152,17 +3163,17 @@ TextPropertyEditor.prototype = {
     this._updateComputed();
 
     // Update the rule property highlight.
     this.ruleView._updatePropertyHighlight(this);
   },
 
   _onStartEditing: function() {
     this.element.classList.remove("ruleview-overridden");
-    this._previewValue(this.prop.value);
+    this.enable.style.visibility = "hidden";
   },
 
   /**
    * Populate the list of computed styles.
    */
   _updateComputed: function() {
     // Clear out existing viewers.
     while (this.computed.hasChildNodes()) {
@@ -3289,33 +3300,48 @@ TextPropertyEditor.prototype = {
    * commits it.
    *
    * @param {string} aValue
    *        The value contained in the editor.
    * @param {boolean} aCommit
    *        True if the change should be applied.
    */
   _onNameDone: function(aValue, aCommit) {
-    if (aCommit && !this.ruleEditor.isEditing) {
-      // Unlike the value editor, if a name is empty the entire property
-      // should always be removed.
-      if (aValue.trim() === "") {
-        this.remove();
-      } else {
-        // Adding multiple rules inside of name field overwrites the current
-        // property with the first, then adds any more onto the property list.
-        let properties = parseDeclarations(aValue);
-
-        if (properties.length) {
-          this.prop.setName(properties[0].name);
-          if (properties.length > 1) {
-            this.prop.setValue(properties[0].value, properties[0].priority);
-            this.ruleEditor.addProperties(properties.slice(1), this.prop);
-          }
-        }
+    if ((!aCommit && this.ruleEditor.isEditing) ||
+        this.committed.name == aValue) {
+      // Disable the property if the property was originally disabled.
+      if (!this.prop.enabled) {
+        this.rule.setPropertyEnabled(this.prop, this.prop.enabled);
+      }
+
+      return;
+    }
+
+    // Unlike the value editor, if a name is empty the entire property
+    // should always be removed.
+    if (aValue.trim() === "") {
+      this.remove();
+      return;
+    }
+
+    // Adding multiple rules inside of name field overwrites the current
+    // property with the first, then adds any more onto the property list.
+    let properties = parseDeclarations(aValue);
+
+    if (properties.length) {
+      this.prop.setName(properties[0].name);
+      this.committed.name = this.prop.name;
+
+      if (!this.prop.enabled) {
+        this.prop.setEnabled(true);
+      }
+
+      if (properties.length > 1) {
+        this.prop.setValue(properties[0].value, properties[0].priority);
+        this.ruleEditor.addProperties(properties.slice(1), this.prop);
       }
     }
   },
 
   /**
    * Remove property from style and the editors from DOM.
    * Begin editing next available property.
    */
@@ -3337,45 +3363,48 @@ TextPropertyEditor.prototype = {
    * Called when a value editor closes.  If the user pressed escape,
    * revert to the value this property had before editing.
    *
    * @param {string} aValue
    *        The value contained in the editor.
    * @param {bool} aCommit
    *        True if the change should be applied.
    */
-  _onValueDone: function(aValue, aCommit) {
-    if (!aCommit && !this.ruleEditor.isEditing) {
+  _onValueDone: function(aValue="", aCommit) {
+    let parsedProperties = this._getValueAndExtraProperties(aValue);
+    let val = parseSingleValue(parsedProperties.firstValue);
+    let isValueUnchanged =
+      !parsedProperties.propertiesToAdd.length &&
+      this.committed.value == val.value &&
+      this.committed.priority == val.priority;
+
+    if ((!aCommit && !this.ruleEditor.isEditing) || isValueUnchanged) {
       // A new property should be removed when escape is pressed.
       if (this.removeOnRevert) {
         this.remove();
       } else {
-        // update the editor back to committed value
-        this.update();
-
-        // undo the preview in content style
-        this.ruleEditor.rule.previewPropertyValue(this.prop,
-          this.prop.value, this.prop.priority);
+        // Disable the property if the property was originally disabled.
+        this.rule.setPropertyEnabled(this.prop, this.prop.enabled);
       }
       return;
     }
 
-    let {propertiesToAdd, firstValue} =
-        this._getValueAndExtraProperties(aValue);
-
     // First, set this property value (common case, only modified a property)
-    let val = parseSingleValue(firstValue);
-
     this.prop.setValue(val.value, val.priority);
+
+    if (!this.prop.enabled) {
+      this.prop.setEnabled(true);
+    }
+
     this.removeOnRevert = false;
     this.committed.value = this.prop.value;
     this.committed.priority = this.prop.priority;
 
     // If needed, add any new properties after this.prop.
-    this.ruleEditor.addProperties(propertiesToAdd, this.prop);
+    this.ruleEditor.addProperties(parsedProperties.propertiesToAdd, this.prop);
 
     // If the name or value is not actively being edited, and the value is
     // empty, then remove the whole property.
     // A timeout is used here to accurately check the state, since the inplace
     // editor `done` and `destroy` events fire before the next editor
     // is focused.
     if (val.value.trim() === "") {
       setTimeout(() => {
--- a/browser/devtools/styleinspector/test/browser.ini
+++ b/browser/devtools/styleinspector/test/browser.ini
@@ -87,43 +87,51 @@ skip-if = e10s # Bug 1039528: "inspect e
 [browser_ruleview_custom.js]
 [browser_ruleview_edit-property-commit.js]
 [browser_ruleview_edit-property-computed.js]
 [browser_ruleview_edit-property-increments.js]
 [browser_ruleview_edit-property-order.js]
 [browser_ruleview_edit-property_01.js]
 [browser_ruleview_edit-property_02.js]
 [browser_ruleview_edit-property_03.js]
+[browser_ruleview_edit-property_04.js]
+[browser_ruleview_edit-property_05.js]
+[browser_ruleview_edit-property_06.js]
+[browser_ruleview_edit-property_07.js]
 [browser_ruleview_edit-selector-commit.js]
 [browser_ruleview_edit-selector_01.js]
 [browser_ruleview_edit-selector_02.js]
 [browser_ruleview_edit-selector_03.js]
 [browser_ruleview_edit-selector_04.js]
 [browser_ruleview_edit-selector_05.js]
 [browser_ruleview_eyedropper.js]
 [browser_ruleview_filtereditor-appears-on-swatch-click.js]
 [browser_ruleview_filtereditor-commit-on-ENTER.js]
 [browser_ruleview_filtereditor-revert-on-ESC.js]
 skip-if = (os == "win" && debug) || e10s # bug 963492: win. bug 1040653: e10s.
 [browser_ruleview_inherit.js]
 [browser_ruleview_keybindings.js]
 [browser_ruleview_keyframes-rule_01.js]
 [browser_ruleview_keyframes-rule_02.js]
 [browser_ruleview_livepreview.js]
+[browser_ruleview_mark_overridden_01.js]
+[browser_ruleview_mark_overridden_02.js]
+[browser_ruleview_mark_overridden_03.js]
+[browser_ruleview_mark_overridden_04.js]
+[browser_ruleview_mark_overridden_05.js]
 [browser_ruleview_mathml-element.js]
 [browser_ruleview_media-queries.js]
 [browser_ruleview_multiple-properties-duplicates.js]
 [browser_ruleview_multiple-properties-priority.js]
 [browser_ruleview_multiple-properties-unfinished_01.js]
 [browser_ruleview_multiple-properties-unfinished_02.js]
 [browser_ruleview_multiple_properties_01.js]
 [browser_ruleview_multiple_properties_02.js]
 [browser_ruleview_original-source-link.js]
 [browser_ruleview_cycle-color.js]
-[browser_ruleview_override.js]
 [browser_ruleview_pseudo-element_01.js]
 [browser_ruleview_pseudo-element_02.js]
 skip-if = e10s # Bug 1090340
 [browser_ruleview_pseudo_lock_options.js]
 [browser_ruleview_refresh-on-attribute-change_01.js]
 [browser_ruleview_refresh-on-attribute-change_02.js]
 [browser_ruleview_refresh-on-style-change.js]
 [browser_ruleview_search-filter-computed-list_01.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_04.js
@@ -0,0 +1,86 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that a disabled property remains disabled when the escaping out of
+// the property editor.
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: blue;",
+  "}",
+  "</style>",
+  "<div id='testid'>Styled Node</div>",
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testDisableProperty(inspector, view);
+});
+
+function* testDisableProperty(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let propEditor = ruleEditor.rule.textProps[0].editor;
+
+  info("Disabling a property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  let newValue = yield executeInContent("Test:GetRulePropertyValue", {
+    styleSheetIndex: 0,
+    ruleIndex: 0,
+    name: "background-color"
+  });
+  is(newValue, "", "background-color should have been unset.");
+
+  yield testEditDisableProperty(view, ruleEditor, propEditor,
+    propEditor.nameSpan, "VK_ESCAPE");
+  yield testEditDisableProperty(view, ruleEditor, propEditor,
+    propEditor.valueSpan, "VK_ESCAPE");
+  yield testEditDisableProperty(view, ruleEditor, propEditor,
+    propEditor.valueSpan, "VK_TAB");
+  yield testEditDisableProperty(view, ruleEditor, propEditor,
+    propEditor.valueSpan, "VK_RETURN");
+}
+
+function* testEditDisableProperty(view, ruleEditor, propEditor,
+    editableField, commitKey) {
+  let editor = yield focusEditableField(view, editableField);
+
+  ok(!propEditor.element.classList.contains("ruleview-overridden"),
+    "property is not overridden.");
+  is(propEditor.enable.style.visibility, "hidden",
+    "property enable checkbox is hidden.");
+
+  let newValue = yield executeInContent("Test:GetRulePropertyValue", {
+    styleSheetIndex: 0,
+    ruleIndex: 0,
+    name: "background-color"
+  });
+  is(newValue, "", "background-color should remain unset.");
+
+  let onBlur = once(editor.input, "blur");
+  EventUtils.synthesizeKey(commitKey, {}, view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  ok(!propEditor.prop.enabled, "property is disabled.");
+  ok(propEditor.element.classList.contains("ruleview-overridden"),
+    "property is overridden.");
+  is(propEditor.enable.style.visibility, "visible",
+    "property enable checkbox is visible.");
+  ok(!propEditor.enable.getAttribute("checked"),
+    "property enable checkbox is not checked.");
+
+  newValue = yield executeInContent("Test:GetRulePropertyValue", {
+    styleSheetIndex: 0,
+    ruleIndex: 0,
+    name: "background-color"
+  });
+  is(newValue, "", "background-color should remain unset.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_05.js
@@ -0,0 +1,86 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that a disabled property is re-enabled if the property name or value is
+// modified
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: blue;",
+  "}",
+  "</style>",
+  "<div id='testid'>Styled Node</div>",
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testEditingDisableProperty(inspector, view);
+});
+
+function* testEditingDisableProperty(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let propEditor = ruleEditor.rule.textProps[0].editor;
+
+  info("Disabling background-color property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  let newValue = yield getRulePropertyValue("background-color");
+  is(newValue, "", "background-color should have been unset.");
+
+  yield focusEditableField(view, propEditor.nameSpan);
+
+  info("Entering a new property name, including : to commit and " +
+       "focus the value");
+  let onValueFocus = once(ruleEditor.element, "focus", true);
+  EventUtils.sendString("border-color:", view.styleWindow);
+  yield onValueFocus;
+  yield ruleEditor.rule._applyingModifications;
+
+  info("Escape editing the property value");
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
+  yield ruleEditor.rule._applyingModifications;
+
+  newValue = yield getRulePropertyValue("border-color");
+  is(newValue, "blue", "border-color should have been set.");
+
+  ok(propEditor.prop.enabled, "border-color property is enabled.");
+  ok(!propEditor.element.classList.contains("ruleview-overridden"),
+    "border-color is not overridden");
+
+  info("Disabling border-color property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  newValue = yield getRulePropertyValue("border-color");
+  is(newValue, "", "border-color should have been unset.");
+
+  info("Enter a new property value for the border-color property");
+  let editor = yield focusEditableField(view, propEditor.valueSpan);
+  let onBlur = once(editor.input, "blur");
+  EventUtils.sendString("red;", view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  newValue = yield getRulePropertyValue("border-color");
+  is(newValue, "red", "new border-color should have been set.");
+
+  ok(propEditor.prop.enabled, "border-color property is enabled.");
+  ok(!propEditor.element.classList.contains("ruleview-overridden"),
+    "border-color is not overridden");
+}
+
+function* getRulePropertyValue(name) {
+  let propValue = yield executeInContent("Test:GetRulePropertyValue", {
+    styleSheetIndex: 0,
+    ruleIndex: 0,
+    name: name
+  });
+  return propValue;
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_06.js
@@ -0,0 +1,64 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that editing a property's priority is behaving correctly, and disabling
+// and editing the property will re-enable the property.
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "body {",
+  "  background-color: green !important;",
+  "}",
+  "body {",
+  "  background-color: red;",
+  "}",
+  "</style>",
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("body", inspector);
+  yield testEditPropertyPriorityAndDisable(inspector, view);
+});
+
+function* testEditPropertyPriorityAndDisable(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let propEditor = ruleEditor.rule.textProps[0].editor;
+
+  is((yield getComputedStyleProperty("body", null, "background-color")),
+    "rgb(0, 128, 0)", "green background color is set.");
+
+  let editor = yield focusEditableField(view, propEditor.valueSpan);
+  let onBlur = once(editor.input, "blur");
+  EventUtils.sendString("red !important;", view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  is(propEditor.valueSpan.textContent, "red !important",
+    "'red !important' property value is correctly set.");
+  is((yield getComputedStyleProperty("body", null, "background-color")),
+    "rgb(255, 0, 0)", "red background color is set.");
+
+  info("Disabling red background color property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  is((yield getComputedStyleProperty("body", null, "background-color")),
+    "rgb(0, 128, 0)", "green background color is set.");
+
+  editor = yield focusEditableField(view, propEditor.valueSpan);
+  onBlur = once(editor.input, "blur");
+  EventUtils.sendString("red;", view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  is(propEditor.valueSpan.textContent, "red",
+    "'red' property value is correctly set.");
+  ok(propEditor.prop.enabled, "red background-color property is enabled.");
+  is((yield getComputedStyleProperty("body", null, "background-color")),
+    "rgb(0, 128, 0)", "green background color is set.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_07.js
@@ -0,0 +1,55 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that adding multiple values will enable the property even if the
+// property does not change, and that the extra values are added correctly.
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: red;",
+  "}",
+  "</style>",
+  "<div id='testid'>Styled Node</div>",
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testEditDisableProperty(inspector, view);
+});
+
+function* testEditDisableProperty(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let propEditor = ruleEditor.rule.textProps[0].editor;
+
+  info("Disabling red background color property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  ok(!propEditor.prop.enabled, "red background-color property is disabled.");
+
+  let editor = yield focusEditableField(view, propEditor.valueSpan);
+  let onBlur = once(editor.input, "blur");
+  EventUtils.sendString("red; color: red;", view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  is(propEditor.valueSpan.textContent, "red",
+    "'red' property value is correctly set.");
+  ok(propEditor.prop.enabled, "red background-color property is enabled.");
+  is((yield getComputedStyleProperty("#testid", null, "background-color")),
+    "rgb(255, 0, 0)", "red background color is set.");
+
+  propEditor = ruleEditor.rule.textProps[1].editor;
+  is(propEditor.nameSpan.textContent, "color",
+    "new 'color' property name is correctly set.");
+  is(propEditor.valueSpan.textContent, "red",
+    "new 'red' property value is correctly set.");
+  is((yield getComputedStyleProperty("#testid", null, "color")),
+    "rgb(255, 0, 0)", "red color is set.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_mark_overridden_01.js
@@ -0,0 +1,64 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the rule view marks overridden rules correctly based on the
+// specificity of the rule
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: blue;",
+  "}",
+  ".testclass {",
+  "  background-color: green;",
+  "}",
+  "</style>",
+  "<div id='testid' class='testclass'>Styled Node</div>"
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testMarkOverridden(inspector, view);
+});
+
+function* testMarkOverridden(inspector, view) {
+  let elementStyle = view._elementStyle;
+
+  let idRule = elementStyle.rules[1];
+  let idProp = idRule.textProps[0];
+  is(idProp.name, "background-color",
+    "First ID property should be background-color");
+  is(idProp.value, "blue", "First ID property value should be blue");
+  ok(!idProp.overridden, "ID prop should not be overridden.");
+  ok(!idProp.editor.element.classList.contains("ruleview-overridden"),
+    "ID property editor should not have ruleview-overridden class");
+
+  let classRule = elementStyle.rules[2];
+  let classProp = classRule.textProps[0];
+  is(classProp.name, "background-color",
+    "First class prop should be background-color");
+  is(classProp.value, "green", "First class property value should be green");
+  ok(classProp.overridden, "Class property should be overridden.");
+  ok(classProp.editor.element.classList.contains("ruleview-overridden"),
+    "Class property editor should have ruleview-overridden class");
+
+  // Override background-color by changing the element style.
+  let elementRule = elementStyle.rules[0];
+  elementRule.createProperty("background-color", "purple", "");
+  yield elementRule._applyingModifications;
+
+  let elementProp = elementRule.textProps[0];
+  ok(!elementProp.overridden,
+    "Element style property should not be overridden");
+  ok(idProp.overridden, "ID property should be overridden");
+  ok(idProp.editor.element.classList.contains("ruleview-overridden"),
+    "ID property editor should have ruleview-overridden class");
+  ok(classProp.overridden, "Class property should be overridden");
+  ok(classProp.editor.element.classList.contains("ruleview-overridden"),
+    "Class property editor should have ruleview-overridden class");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_mark_overridden_02.js
@@ -0,0 +1,45 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the rule view marks overridden rules correctly for short hand
+// properties and the computed list properties
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  margin-left: 1px;",
+  "}",
+  ".testclass {",
+  "  margin: 2px;",
+  "}",
+  "</style>",
+  "<div id='testid' class='testclass'>Styled Node</div>"
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testMarkOverridden(inspector, view);
+});
+
+function* testMarkOverridden(inspector, view) {
+  let elementStyle = view._elementStyle;
+
+  let classRule = elementStyle.rules[2];
+  let classProp = classRule.textProps[0];
+  ok(!classProp.overridden,
+    "Class prop shouldn't be overridden, some props are still being used.");
+
+  for (let computed of classProp.computed) {
+    if (computed.name.indexOf("margin-left") == 0) {
+      ok(computed.overridden, "margin-left props should be overridden.");
+    } else {
+      ok(!computed.overridden,
+        "Non-margin-left props should not be overridden.");
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_mark_overridden_03.js
@@ -0,0 +1,48 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the rule view marks overridden rules correctly based on the
+// priority for the rule
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: blue;",
+  "}",
+  ".testclass {",
+  "  background-color: green !important;",
+  "}",
+  "</style>",
+  "<div id='testid' class='testclass'>Styled Node</div>"
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testMarkOverridden(inspector, view);
+});
+
+function* testMarkOverridden(inspector, view) {
+  let elementStyle = view._elementStyle;
+
+  let idRule = elementStyle.rules[1];
+  let idProp = idRule.textProps[0];
+  ok(idProp.overridden, "Not-important rule should be overridden.");
+
+  let classRule = elementStyle.rules[2];
+  let classProp = classRule.textProps[0];
+  ok(!classProp.overridden, "Important rule should not be overridden.");
+
+  let elementRule = elementStyle.rules[0];
+  let elementProp = elementRule.createProperty("background-color", "purple",
+    "important");
+  yield elementRule._applyingModifications;
+
+  ok(!elementProp.overridden, "New important prop should not be overriden.");
+  ok(idProp.overridden, "ID property should be overridden.");
+  ok(classProp.overridden, "Class property should be overridden.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_mark_overridden_04.js
@@ -0,0 +1,42 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the rule view marks overridden rules correctly if a property gets
+// disabled
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: blue;",
+  "}",
+  ".testclass {",
+  "  background-color: green;",
+  "}",
+  "</style>",
+  "<div id='testid' class='testclass'>Styled Node</div>"
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testMarkOverridden(inspector, view);
+});
+
+function* testMarkOverridden(inspector, view) {
+  let elementStyle = view._elementStyle;
+
+  let idRule = elementStyle.rules[1];
+  let idProp = idRule.textProps[0];
+
+  idProp.setEnabled(false);
+  yield idRule._applyingModifications;
+
+  let classRule = elementStyle.rules[2];
+  let classProp = classRule.textProps[0];
+  ok(!classProp.overridden,
+    "Class prop should not be overridden after id prop was disabled.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_mark_overridden_05.js
@@ -0,0 +1,36 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the rule view marks overridden rules correctly based on the
+// order of the property
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: green;",
+  "}",
+  "</style>",
+  "<div id='testid' class='testclass'>Styled Node</div>"
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testMarkOverridden(inspector, view);
+});
+
+function* testMarkOverridden(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+
+  yield createNewRuleViewProperty(ruleEditor, "background-color: red;");
+
+  let firstProp = ruleEditor.rule.textProps[0];
+  let secondProp = ruleEditor.rule.textProps[1];
+
+  ok(firstProp.overridden, "First property should be overridden.");
+  ok(!secondProp.overridden, "Second property should not be overridden.");
+}
--- a/browser/devtools/styleinspector/test/browser_ruleview_multiple-properties-unfinished_01.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_multiple-properties-unfinished_01.js
@@ -31,19 +31,19 @@ function waitRuleViewChanged(view, n) {
       deferred.resolve();
     }
   }
   view.on("ruleview-changed", listener);
   return deferred.promise;
 }
 function* testCreateNewMultiUnfinished(inspector, ruleEditor, view) {
   let onMutation = inspector.once("markupmutation");
-  // There is 6 rule-view updates, one for the rule view creation,
-  // one for each new property and one last for throttle update.
-  let onRuleViewChanged = waitRuleViewChanged(view, 6);
+  // There is 5 rule-view updates, one for the rule view creation,
+  // one for each new property
+  let onRuleViewChanged = waitRuleViewChanged(view, 5);
   yield createNewRuleViewProperty(ruleEditor,
     "color:blue;background : orange   ; text-align:center; border-color: ");
   yield onMutation;
   yield onRuleViewChanged;
 
   is(ruleEditor.rule.textProps.length, 4, "Should have created new text properties.");
   is(ruleEditor.propertyList.children.length, 4, "Should have created property editors.");
 
deleted file mode 100644
--- a/browser/devtools/styleinspector/test/browser_ruleview_override.js
+++ /dev/null
@@ -1,151 +0,0 @@
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// Test the display of overridden declarations in the rule-view
-
-add_task(function*() {
-  yield addTab("data:text/html;charset=utf-8,browser_ruleview_override.js");
-  let {toolbox, inspector, view} = yield openRuleView();
-
-  yield simpleOverride(inspector, view);
-  yield partialOverride(inspector, view);
-  yield importantOverride(inspector, view);
-  yield disableOverride(inspector, view);
-});
-
-function* createTestContent(inspector, style) {
-  let onMutated = inspector.once("markupmutation");
-  let styleNode = addStyle(content.document, style);
-  content.document.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
-  yield onMutated;
-  yield selectNode("#testid", inspector);
-  return styleNode;
-}
-
-function* removeTestContent(inspector, node) {
-  let onMutated = inspector.once("markupmutation");
-  node.remove();
-  yield onMutated;
-}
-
-function* simpleOverride(inspector, view) {
-  let styleNode = yield createTestContent(inspector, '' +
-    '#testid {' +
-    '  background-color: blue;' +
-    '} ' +
-    '.testclass {' +
-    '  background-color: green;' +
-    '}');
-
-  let elementStyle = view._elementStyle;
-
-  let idRule = elementStyle.rules[1];
-  let idProp = idRule.textProps[0];
-  is(idProp.name, "background-color", "First ID prop should be background-color");
-  ok(!idProp.overridden, "ID prop should not be overridden.");
-
-  let classRule = elementStyle.rules[2];
-  let classProp = classRule.textProps[0];
-  is(classProp.name, "background-color", "First class prop should be background-color");
-  ok(classProp.overridden, "Class property should be overridden.");
-
-  // Override background-color by changing the element style.
-  let elementRule = elementStyle.rules[0];
-  elementRule.createProperty("background-color", "purple", "");
-  yield elementRule._applyingModifications;
-
-  let elementProp = elementRule.textProps[0];
-  is(classProp.name, "background-color", "First element prop should now be background-color");
-  ok(!elementProp.overridden, "Element style property should not be overridden");
-  ok(idProp.overridden, "ID property should be overridden");
-  ok(classProp.overridden, "Class property should be overridden");
-
-  yield removeTestContent(inspector, styleNode);
-}
-
-function* partialOverride(inspector, view) {
-  let styleNode = yield createTestContent(inspector, '' +
-    // Margin shorthand property...
-    '.testclass {' +
-    '  margin: 2px;' +
-    '}' +
-    // ... will be partially overridden.
-    '#testid {' +
-    '  margin-left: 1px;' +
-    '}');
-
-  let elementStyle = view._elementStyle;
-
-  let classRule = elementStyle.rules[2];
-  let classProp = classRule.textProps[0];
-  ok(!classProp.overridden,
-    "Class prop shouldn't be overridden, some props are still being used.");
-
-  for (let computed of classProp.computed) {
-    if (computed.name.indexOf("margin-left") == 0) {
-      ok(computed.overridden, "margin-left props should be overridden.");
-    } else {
-      ok(!computed.overridden, "Non-margin-left props should not be overridden.");
-    }
-  }
-
-  yield removeTestContent(inspector, styleNode);
-}
-
-function* importantOverride(inspector, view) {
-  let styleNode = yield createTestContent(inspector, '' +
-    // Margin shorthand property...
-    '.testclass {' +
-    '  background-color: green !important;' +
-    '}' +
-    // ... will be partially overridden.
-    '#testid {' +
-    '  background-color: blue;' +
-    '}');
-
-  let elementStyle = view._elementStyle;
-
-  let idRule = elementStyle.rules[1];
-  let idProp = idRule.textProps[0];
-  ok(idProp.overridden, "Not-important rule should be overridden.");
-
-  let classRule = elementStyle.rules[2];
-  let classProp = classRule.textProps[0];
-  ok(!classProp.overridden, "Important rule should not be overridden.");
-
-  yield removeTestContent(inspector, styleNode);
-
-  let elementRule = elementStyle.rules[0];
-  let elementProp = elementRule.createProperty("background-color", "purple", "important");
-  yield elementRule._applyingModifications;
-
-  ok(classProp.overridden, "New important prop should override class property.");
-  ok(!elementProp.overridden, "New important prop should not be overriden.");
-}
-
-function* disableOverride(inspector, view) {
-  let styleNode = yield createTestContent(inspector, '' +
-    '#testid {' +
-    '  background-color: blue;' +
-    '}' +
-    '.testclass {' +
-    '  background-color: green;' +
-    '}');
-
-  let elementStyle = view._elementStyle;
-
-  let idRule = elementStyle.rules[1];
-  let idProp = idRule.textProps[0];
-
-  idProp.setEnabled(false);
-  yield idRule._applyingModifications;
-
-  let classRule = elementStyle.rules[2];
-  let classProp = classRule.textProps[0];
-  ok(!classProp.overridden, "Class prop should not be overridden after id prop was disabled.");
-
-  yield removeTestContent(inspector, styleNode);
-}
--- a/browser/devtools/styleinspector/test/browser_styleinspector_output-parser.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_output-parser.js
@@ -305,17 +305,17 @@ function test() {
       name: "background",
       value: "rgb(255, var(--g-value, 0), 192)",
       test: fragment => {
 	is(fragment.textContent, "rgb(255, var(--g-value, 0), 192)");
       }
     }
   ];
 
-  let parser = new OutputParser();
+  let parser = new OutputParser(document);
   for (let i = 0; i < testData.length; i ++) {
     let data = testData[i];
     info("Output-parser test data " + i + ". {" + data.name + " : " + data.value + ";}");
     data.test(parser.parseCssProperty(data.name, data.value, {
       colorClass: COLOR_CLASS,
       urlClass: URL_CLASS,
       bezierClass: CUBIC_BEZIER_CLASS,
       defaultColorType: false
--- a/browser/devtools/styleinspector/test/head.js
+++ b/browser/devtools/styleinspector/test/head.js
@@ -409,39 +409,25 @@ function* waitForComputedStyleProperty(s
 }
 
 /**
  * Given an inplace editable element, click to switch it to edit mode, wait for
  * focus
  * @return a promise that resolves to the inplace-editor element when ready
  */
 let focusEditableField = Task.async(function*(ruleView, editable, xOffset=1, yOffset=1, options={}) {
-  // Focusing the name or value input is going to fire a preview and update the rule view
-  let expectRuleViewUpdate =
-    editable.classList.contains("ruleview-propertyname") ||
-    editable.classList.contains("ruleview-propertyvalue");
-  let onRuleViewChanged;
-  if (expectRuleViewUpdate) {
-    onRuleViewChanged = ruleView.once("ruleview-changed");
-  }
-
   let onFocus = once(editable.parentNode, "focus", true);
   info("Clicking on editable field to turn to edit mode");
   EventUtils.synthesizeMouse(editable, xOffset, yOffset, options,
     editable.ownerDocument.defaultView);
-  let event = yield onFocus;
+  yield onFocus;
 
   info("Editable field gained focus, returning the input field now");
   let onEdit = inplaceEditor(editable.ownerDocument.activeElement);
 
-  if (expectRuleViewUpdate) {
-    info("Waiting for rule view update");
-    yield onRuleViewChanged;
-  }
-
   return onEdit;
 });
 
 /**
  * Given a tooltip object instance (see Tooltip.js), checks if it is set to
  * toggle and hover and if so, checks if the given target is a valid hover target.
  * This won't actually show the tooltip (the less we interact with XUL panels
  * during test runs, the better).
@@ -796,21 +782,21 @@ function getRuleViewRuleEditor(view, chi
  * @param {RuleEditor} ruleEditor An instance of RuleEditor that will receive
  * the new property
  * @return a promise that resolves to the newly created editor when ready and
  * focused
  */
 let focusNewRuleViewProperty = Task.async(function*(ruleEditor) {
   info("Clicking on a close ruleEditor brace to start editing a new property");
   ruleEditor.closeBrace.scrollIntoView();
-  let editor = yield focusEditableField(ruleEditor.ruleView, ruleEditor.closeBrace);
+  let editor = yield focusEditableField(ruleEditor.ruleView,
+    ruleEditor.closeBrace);
 
-  is(inplaceEditor(ruleEditor.newPropSpan), editor, "Focused editor is the new property editor.");
-  is(ruleEditor.rule.textProps.length,  0, "Starting with one new text property.");
-  is(ruleEditor.propertyList.children.length, 1, "Starting with two property editors.");
+  is(inplaceEditor(ruleEditor.newPropSpan), editor,
+    "Focused editor is the new property editor.");
 
   return editor;
 });
 
 /**
  * Create a new property name in the rule-view, focusing a new property editor
  * by clicking on the close brace, and then entering the given text.
  * Keep in mind that the rule-view knows how to handle strings with multiple
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -106,16 +106,17 @@ support-files =
   test-mutation.html
   test-network-request.html
   test-network.html
   test-observe-http-ajax.html
   test-own-console.html
   test-property-provider.html
   test-repeated-messages.html
   test-result-format-as-string.html
+  test-trackingprotection-securityerrors.html
   test-webconsole-error-observer.html
   test_bug_770099_violation.html
   test_bug_770099_violation.html^headers^
   test-autocomplete-in-stackframe.html
   testscript.js
   test-bug_923281_console_log_filter.html
   test-bug_923281_test1.js
   test-bug_923281_test2.js
@@ -345,16 +346,18 @@ skip-if = e10s # Bug 1042253 - webconsol
 [browser_webconsole_property_provider.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_webconsole_scratchpad_panel_link.js]
 [browser_webconsole_split.js]
 [browser_webconsole_split_escape_key.js]
 [browser_webconsole_split_focus.js]
 [browser_webconsole_split_persist.js]
 skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
+[browser_webconsole_trackingprotection_errors.js]
+tags = trackingprotection
 [browser_webconsole_view_source.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s (expectUncaughtException)
 [browser_webconsole_reflow.js]
 [browser_webconsole_log_file_filter.js]
 [browser_webconsole_expandable_timestamps.js]
 [browser_webconsole_autocomplete_in_debugger_stackframe.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_webconsole_autocomplete_popup_close_on_tab_switch.js]
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_595934_message_categories.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_595934_message_categories.js
@@ -37,17 +37,16 @@ const TESTS = [
       form.submit();
     },
   },
   {
     // #3
     file: "test-bug-595934-workers.html",
     category: "Web Worker",
     matchString: "fooBarWorker",
-    expectError: true,
   },
   {
     // #4
     file: "test-bug-595934-malformedxml.xhtml",
     category: "malformed-xml",
     matchString: "no element found",
   },
   {
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_trackingprotection_errors.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Load a page with tracking elements that get blocked and make sure that a
+// 'learn more' link shows up in the webconsole.
+
+"use strict";
+
+const TEST_URI = "http://tracking.example.org/browser/browser/devtools/webconsole/test/test-trackingprotection-securityerrors.html";
+const LEARN_MORE_URI = "https://developer.mozilla.org/Firefox/Privacy/Tracking_Protection";
+const PREF = "privacy.trackingprotection.enabled";
+const {UrlClassifierTestUtils} = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
+
+registerCleanupFunction(function() {
+  Services.prefs.clearUserPref(PREF);
+  UrlClassifierTestUtils.cleanupTestTrackers();
+});
+
+add_task(function* testMessagesAppear() {
+  yield UrlClassifierTestUtils.addTestTrackers();
+  Services.prefs.setBoolPref(PREF, true);
+
+  let { browser } = yield loadTab(TEST_URI);
+
+  let hud = yield openConsole();
+
+  let results = yield waitForMessages({
+    webconsole: hud,
+    messages: [
+      {
+        name: "Was blocked because tracking protection is enabled",
+        text: "The resource at \"http://tracking.example.com/\" was blocked because tracking protection is enabled",
+        category: CATEGORY_SECURITY,
+        severity: SEVERITY_WARNING,
+        objects: true,
+      },
+    ],
+  });
+
+  yield testClickOpenNewTab(hud, results[0]);
+});
+
+function testClickOpenNewTab(hud, match) {
+  let warningNode = match.clickableElements[0];
+  ok(warningNode, "link element");
+  ok(warningNode.classList.contains("learn-more-link"), "link class name");
+  return simulateMessageLinkClick(warningNode, LEARN_MORE_URI);
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test-trackingprotection-securityerrors.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+  <head>
+    <meta charset="utf8">
+  </head>
+  <body>
+    <iframe src="http://tracking.example.com/"></iframe>
+  </body>
+</html>
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -37,16 +37,18 @@ loader.lazyImporter(this, "gDevTools", "
 
 const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
 let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
 
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 
 const MIXED_CONTENT_LEARN_MORE = "https://developer.mozilla.org/docs/Security/MixedContent";
 
+const TRACKING_PROTECTION_LEARN_MORE = "https://developer.mozilla.org/Firefox/Privacy/Tracking_Protection";
+
 const INSECURE_PASSWORDS_LEARN_MORE = "https://developer.mozilla.org/docs/Security/InsecurePasswords";
 
 const STRICT_TRANSPORT_SECURITY_LEARN_MORE = "https://developer.mozilla.org/docs/Security/HTTP_Strict_Transport_Security";
 
 const WEAK_SIGNATURE_ALGORITHM_LEARN_MORE = "https://developer.mozilla.org/docs/Security/Weak_Signature_Algorithm";
 
 const HELP_URL = "https://developer.mozilla.org/docs/Tools/Web_Console/Helpers";
 
@@ -1674,16 +1676,19 @@ WebConsoleFrame.prototype = {
       url = MIXED_CONTENT_LEARN_MORE;
      break;
      case "Invalid HSTS Headers":
       url = STRICT_TRANSPORT_SECURITY_LEARN_MORE;
      break;
      case "SHA-1 Signature":
       url = WEAK_SIGNATURE_ALGORITHM_LEARN_MORE;
      break;
+     case "Tracking Protection":
+      url = TRACKING_PROTECTION_LEARN_MORE;
+     break;
      default:
       // Unknown category. Return without adding more info node.
       return;
     }
 
     this.addLearnMoreWarningNode(aNode, url);
   },
 
--- a/browser/devtools/webide/themes/panel-listing.css
+++ b/browser/devtools/webide/themes/panel-listing.css
@@ -9,17 +9,17 @@ html {
 }
 
 label,
 .panel-item,
 #project-panel-projects,
 #runtime-panel-projects {
   display: block;
   float: left;
-  width: auto;
+  width: 100%;
   text-align: left;
 }
 
 .project-image,
 .panel-item span {
   display: inline-block;
   float: left;
   line-height: 20px;
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -125,17 +125,17 @@
 #endif
 #ifdef MOZ_REPLACE_MALLOC
 #ifndef MOZ_JEMALLOC3
 @BINPATH@/@DLL_PREFIX@replace_jemalloc@DLL_SUFFIX@
 #endif
 #endif
 #ifdef MOZ_GTK3
 @BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
-@BINPATH@/@DLL_PREFIX@mozgtk2@DLL_SUFFIX@
+@BINPATH@/gtk2/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
 #endif
 
 [browser]
 ; [Base Browser Files]
 #ifndef XP_UNIX
 @BINPATH@/@MOZ_APP_NAME@.exe
 #else
 @BINPATH@/@MOZ_APP_NAME@-bin
@@ -633,20 +633,24 @@
 @RESPATH@/modules/*
 
 ; Safe Browsing
 #ifdef MOZ_URL_CLASSIFIER
 @RESPATH@/components/nsURLClassifier.manifest
 @RESPATH@/components/nsUrlClassifierHashCompleter.js
 @RESPATH@/components/nsUrlClassifierListManager.js
 @RESPATH@/components/nsUrlClassifierLib.js
-@RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
 @RESPATH@/components/url-classifier.xpt
 #endif
 
+; Private Browsing
+@RESPATH@/components/privatebrowsing.xpt
+@RESPATH@/components/PrivateBrowsing.manifest
+@RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
+
 ; ANGLE GLES-on-D3D rendering library
 #ifdef MOZ_ANGLE_RENDERER
 @BINPATH@/libEGL.dll
 @BINPATH@/libGLESv2.dll
 
 #ifdef MOZ_D3DCOMPILER_VISTA_DLL
 @BINPATH@/@MOZ_D3DCOMPILER_VISTA_DLL@
 #endif
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -675,16 +675,17 @@ you can use these alternative items. Oth
 <!ENTITY spellAddDictionaries.accesskey "A">
 
 <!ENTITY editBookmark.done.label                     "Done">
 <!ENTITY editBookmark.cancel.label                   "Cancel">
 <!ENTITY editBookmark.removeBookmark.accessKey       "R">
 
 <!ENTITY identity.connectionSecure "Secure Connection">
 <!ENTITY identity.connectionNotSecure "Connection is Not Secure">
+<!ENTITY identity.connectionFile "This page is stored on your computer.">
 <!ENTITY identity.connectionVerified "&brandShortName; verified that you are securely connected to this site, run by:">
 <!ENTITY identity.connectionInternal "This is a secure &brandShortName; page.">
 
 <!ENTITY identity.moreInfoLinkText2 "More Information">
 
 <!ENTITY identity.permissions "Permissions">
 
 <!-- Name for the tabs toolbar as spoken by screen readers.
@@ -753,16 +754,17 @@ you can use these alternative items. Oth
 <!ENTITY social.markpageMenu.label "Save Page To…">
 <!ENTITY social.marklinkMenu.accesskey "L">
 <!ENTITY social.marklinkMenu.label "Save Link To…">
 
 <!ENTITY getUserMedia.selectCamera.label "Camera to share:">
 <!ENTITY getUserMedia.selectCamera.accesskey "C">
 <!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
 <!ENTITY getUserMedia.selectMicrophone.accesskey "M">
+<!ENTITY getUserMedia.audioCapture.label "Audio from the tab will be shared.">
 <!ENTITY getUserMedia.allWindowsShared.message "All visible windows on your screen will be shared.">
 
 <!-- Bad Content Blocker Doorhanger Notification -->
 <!ENTITY badContentBlocked.moreinfo "Most websites will work properly even if content is blocked.">
 
 <!ENTITY mixedContentBlocked2.message "Insecure content">
 <!ENTITY mixedContentBlocked2.moreinfo "Some unencrypted elements on this website have been blocked.">
 <!ENTITY mixedContentBlocked2.learnMore "Learn More">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -551,23 +551,27 @@ identity.next.accessKey = n
 # LOCALIZATION NOTE: shown in the popup notification when a user successfully logs into a website
 # LOCALIZATION NOTE (identity.loggedIn.description): %S is the user's identity (e.g. user@example.com)
 identity.loggedIn.description = Signed in as: %S
 identity.loggedIn.signOut.label = Sign Out
 identity.loggedIn.signOut.accessKey = O
 
 # LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message,
 #                    getUserMedia.shareScreen.message, getUserMedia.shareCameraAndMicrophone.message,
-#                    getUserMedia.shareScreenAndMicrophone.message):
+#                    getUserMedia.shareScreenAndMicrophone.message, getUserMedia.shareCameraAndAudioCapture.message,
+#                    getUserMedia.shareAudioCapture.message, getUserMedia.shareScreenAndAudioCapture.message):
 #  %S is the website origin (e.g. www.mozilla.org)
 getUserMedia.shareCamera.message = Would you like to share your camera with %S?
 getUserMedia.shareMicrophone.message = Would you like to share your microphone with %S?
 getUserMedia.shareScreen.message = Would you like to share your screen with %S?
 getUserMedia.shareCameraAndMicrophone.message = Would you like to share your camera and microphone with %S?
+getUserMedia.shareCameraAndAudioCapture.message = Would you like to share your camera and this tab's audio with %S?
 getUserMedia.shareScreenAndMicrophone.message = Would you like to share your microphone and screen with %S?
+getUserMedia.shareScreenAndAudioCapture.message = Would you like to share this tab's audio and your screen with %S?
+getUserMedia.shareAudioCapture.message = Would you like to share this tab's audio with %S?
 getUserMedia.selectWindow.label=Window to share:
 getUserMedia.selectWindow.accesskey=W
 getUserMedia.selectScreen.label=Screen to share:
 getUserMedia.selectScreen.accesskey=S
 getUserMedia.selectApplication.label=Application to share:
 getUserMedia.selectApplication.accesskey=A
 getUserMedia.noVideo.label = No Video
 getUserMedia.noApplication.label = No Application
@@ -599,62 +603,79 @@ getUserMedia.never.label = Never Share
 getUserMedia.never.accesskey = N
 getUserMedia.sharingCamera.message2 = You are currently sharing your camera with this page.
 getUserMedia.sharingMicrophone.message2 = You are currently sharing your microphone with this page.
 getUserMedia.sharingCameraAndMicrophone.message2 = You are currently sharing your camera and microphone with this page.
 getUserMedia.sharingApplication.message = You are currently sharing an application with this page.
 getUserMedia.sharingScreen.message = You are currently sharing your screen with this page.
 getUserMedia.sharingWindow.message = You are currently sharing a window with this page.
 getUserMedia.sharingBrowser.message = You are currently sharing a tab with this page.
+getUserMedia.sharingAudioCapture.message = You are currently sharing a tab's audio with this page.
 getUserMedia.continueSharing.label = Continue Sharing
 getUserMedia.continueSharing.accesskey = C
 getUserMedia.stopSharing.label = Stop Sharing
 getUserMedia.stopSharing.accesskey = S
 
 getUserMedia.sharingMenu.label = Tabs sharing devices
 getUserMedia.sharingMenu.accesskey = d
 # LOCALIZATION NOTE (getUserMedia.sharingMenuCamera
 #                    getUserMedia.sharingMenuMicrophone,
+#                    getUserMedia.sharingMenuAudioCapture,
 #                    getUserMedia.sharingMenuApplication,
 #                    getUserMedia.sharingMenuScreen,
 #                    getUserMedia.sharingMenuWindow,
 #                    getUserMedia.sharingMenuBrowser,
 #                    getUserMedia.sharingMenuCameraMicrophone,
 #                    getUserMedia.sharingMenuCameraMicrophoneApplication,
 #                    getUserMedia.sharingMenuCameraMicrophoneScreen,
 #                    getUserMedia.sharingMenuCameraMicrophoneWindow,
 #                    getUserMedia.sharingMenuCameraMicrophoneBrowser,
+#                    getUserMedia.sharingMenuCameraAudioCapture,
+#                    getUserMedia.sharingMenuCameraAudioCaptureApplication,
+#                    getUserMedia.sharingMenuCameraAudioCaptureScreen,
+#                    getUserMedia.sharingMenuCameraAudioCaptureWindow,
+#                    getUserMedia.sharingMenuCameraAudioCaptureBrowser,
 #                    getUserMedia.sharingMenuCameraApplication,
 #                    getUserMedia.sharingMenuCameraScreen,
 #                    getUserMedia.sharingMenuCameraWindow,
 #                    getUserMedia.sharingMenuCameraBrowser,
 #                    getUserMedia.sharingMenuMicrophoneApplication,
 #                    getUserMedia.sharingMenuMicrophoneScreen,
 #                    getUserMedia.sharingMenuMicrophoneWindow,
 #                    getUserMedia.sharingMenuMicrophoneBrowser):
 # %S is the website origin (e.g. www.mozilla.org)
 getUserMedia.sharingMenuCamera = %S (camera)
 getUserMedia.sharingMenuMicrophone = %S (microphone)
+getUserMedia.sharingMenuAudioCapture = %S (tab audio)
 getUserMedia.sharingMenuApplication = %S (application)
 getUserMedia.sharingMenuScreen = %S (screen)
 getUserMedia.sharingMenuWindow = %S (window)
 getUserMedia.sharingMenuBrowser = %S (tab)
 getUserMedia.sharingMenuCameraMicrophone = %S (camera and microphone)
 getUserMedia.sharingMenuCameraMicrophoneApplication = %S (camera, microphone and application)
 getUserMedia.sharingMenuCameraMicrophoneScreen = %S (camera, microphone and screen)
 getUserMedia.sharingMenuCameraMicrophoneWindow = %S (camera, microphone and window)
 getUserMedia.sharingMenuCameraMicrophoneBrowser = %S (camera, microphone and tab)
+getUserMedia.sharingMenuCameraAudioCapture = %S (camera and tab audio)
+getUserMedia.sharingMenuCameraAudioCaptureApplication = %S (camera, tab audio and application)
+getUserMedia.sharingMenuCameraAudioCaptureScreen = %S (camera, tab audio and screen)
+getUserMedia.sharingMenuCameraAudioCaptureWindow = %S (camera, tab audio and window)
+getUserMedia.sharingMenuCameraAudioCaptureBrowser = %S (camera, tab audio and tab)
 getUserMedia.sharingMenuCameraApplication = %S (camera and application)
 getUserMedia.sharingMenuCameraScreen = %S (camera and screen)
 getUserMedia.sharingMenuCameraWindow = %S (camera and window)
 getUserMedia.sharingMenuCameraBrowser = %S (camera and tab)
 getUserMedia.sharingMenuMicrophoneApplication = %S (microphone and application)
 getUserMedia.sharingMenuMicrophoneScreen = %S (microphone and screen)
 getUserMedia.sharingMenuMicrophoneWindow = %S (microphone and window)
 getUserMedia.sharingMenuMicrophoneBrowser = %S (microphone and tab)
+getUserMedia.sharingMenuMicrophoneApplication = %S (tab audio and application)
+getUserMedia.sharingMenuMicrophoneScreen = %S (tab audio and screen)
+getUserMedia.sharingMenuMicrophoneWindow = %S (tab audio and window)
+getUserMedia.sharingMenuMicrophoneBrowser = %S (tab audio and tab)
 # LOCALIZATION NOTE(getUserMedia.sharingMenuUnknownHost): this is used for the website
 # origin for the sharing menu if no readable origin could be deduced from the URL.
 getUserMedia.sharingMenuUnknownHost = Unknown origin
 
 # LOCALIZATION NOTE(emeNotifications.drmContentPlaying.message2): %S is brandShortName.
 emeNotifications.drmContentPlaying.message2 = Some audio or video on this site uses DRM software, which may limit what %S can let you do with it.
 emeNotifications.drmContentPlaying.button.label = Configure…
 emeNotifications.drmContentPlaying.button.accesskey = C
--- a/browser/locales/en-US/chrome/browser/search.properties
+++ b/browser/locales/en-US/chrome/browser/search.properties
@@ -28,19 +28,22 @@ cmd_showSuggestions_accesskey=S
 # menuitem at the bottom of the search panel.
 cmd_addFoundEngine=Add "%S"
 # LOCALIZATION NOTE (cmd_addFoundEngineMenu): When more than 5 engines
 # are offered by a web page, instead of listing all of them in the
 # search panel using the cmd_addFoundEngine string, they will be
 # grouped in a submenu using cmd_addFoundEngineMenu as a label.
 cmd_addFoundEngineMenu=Add search engine
 
-# LOCALIZATION NOTE (searchFor, searchWith):
-# These two strings are used to build the header above the list of one-click
+# LOCALIZATION NOTE (searchForKeywordsWith):
+# This string is used to build the header above the list of one-click
 # search providers:  "Search for <user-typed keywords> with:"
-searchFor=Search for 
-searchWith= with:
+searchForKeywordsWith=Search for %S with:
 
 # LOCALIZATION NOTE (searchWithHeader):
 # The wording of this string should be as close as possible to
-# searchFor and searchWith. This string will be used instead of
-# them when the user has not typed any keyword.
+# searchForKeywordsWith. This string will be used when the user
+# has not typed anything.
 searchWithHeader=Search with:
+
+# LOCALIZATION NOTE (searchSettings):
+# This is the label for the button that opens Search preferences.
+searchSettings=Change Search Settings
deleted file mode 100644
--- a/browser/locales/en-US/chrome/browser/tabbrowser.dtd
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<!ENTITY  closeTab.label         "Close Tab">
--- a/browser/locales/en-US/chrome/browser/tabbrowser.properties
+++ b/browser/locales/en-US/chrome/browser/tabbrowser.properties
@@ -24,8 +24,12 @@ tabs.closeWarningTitle=Confirm close
 # LOCALIZATION NOTE (tabs.closeWarningMultiple):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # The singular form is not considered since this string is used only for
 # multiple tabs.
 tabs.closeWarningMultiple=;You are about to close #1 tabs. Are you sure you want to continue?
 tabs.closeButtonMultiple=Close tabs
 tabs.closeWarningPromptMe=Warn me when I attempt to close multiple tabs
+
+tabs.closeTab.tooltip=Close tab
+tabs.playingAudio.tooltip=This tab is playing audio
+tabs.mutedAudio.tooltip=This tab has been muted
--- a/browser/locales/en-US/searchplugins/amazondotcom.xml
+++ b/browser/locales/en-US/searchplugins/amazondotcom.xml
@@ -5,15 +5,15 @@
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>Amazon.com</ShortName>
 <Description>Amazon.com Search</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAAAAAC0AQAAJgAAACAgAAAAAAAA6QIAANoBAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAF7SURBVDjLlZPLasJAFIaFRF+iVV+h6hO0GF+gVB9AaHwDt64qCG03tQgtdCFIuyhUelmGli66MXThSt24kNiFBUlAYi6ezjnNxSuawB/ITP7v/HNmJgQAEaZzpgHs/gwcTyTEXuXl2U6nA8ViEbK5HKler28CVRAwnB9ptVrAh8MrQuCaZ4iA8fzIqSgCxwzpTIaSuN/RWGwdYLwCUBQFZFkGSZLgqdmEE7YEN8VOAKyaSKUW4nNBAFmnYiKZpDRX1WqwBBzP089n5f/NEQsFL4WqqtsBWJlzDAJr5PwSMM1awEzzdxIbGI3Hvc6jCZeVFgRQRwpY7Qcw3ktgfpR8wLRxCPaot/X4GS95MppfF6DX9n2A3f+kAZycaT8bAZjU6r6B/duD6d3BYg9wQq/tkYzHY1blEiz5lmQyGc95mrO6r2CxgpjCBXgNsJVviolpXJiraeOIjJRE10juUa4sR8V+mO17VvmGqtuOcdNlwut8zTQJcJ0njifyB2bgTdKh6w4BAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACsElEQVRYw71XQWsTURBe2LQgeNKLB+tVemt6txcteNSD/QGC6VEIGDx5s+eKPQqFgJhLNdFLBWMP7cU0oSAWjB70koC9WHbVQ5SO8+XtS14mr7svyaYDH9m87Jv55puZt1nPi4yIzjMeMj7T9OwjI88455nGC1cZX+nsDESumJmPFDwIAqrX6z00Gg1qt9vjkJgFgUeuO16Vy3RjeZkyMzM9+MY1fsM9I9h9zyV7ZAznZrA4FAoFVwJ1z+WuOysrg1lnMolkHJX4k0igzI5sARYWF7vEZEk0rvO6iyUSuJfLJUqM7zYSqRDIra4OOUZPmNZsNrsl8UVTpkJAjh1GzmaSpJ8mAWmYeZB5urHRhW5SNOfUCCDo47W1bvPZsp2qAhipy3Nz1kaLG8dUCEBqM5AvpgElqFar01NgIZsdco7Zb7VasU2YigIYL5tjqCL7Q5YkFQXKlcqQ7DbHthIALk/IWAKor82xPIhshxWABCYioDMz51sexcVi0XoG4DPLIyvJjkTArK3scDQnRvO0MdTrUHGiKZCP4tNgO6BAEI08EQH9Z2Qow0hyPypJGIa9p6JWKCn4SA8jSKmJIDgyRvPJkcRxjfUwNGr/i8+Mo32iHzWiThBD4NM60bet9P77/ubA728RlTjMiwiH6zEEfvIrwdZFtQmMJ7W/ofIDBZD5m3mVZGwJcOP2kmILIlCkE45HoPWurwCSg0+UQRD4ZyXxId+T7gQb9+4q9sioY5ltrOG3L5vqXiiJffDx/aUi83ZJ7jr2ohcEu8Hh6/m+I7OWGiVxbWKHsz+O3vSOakqFQdsFgQeJUiKD7Wv9YKXBgCeSUC3v2kM5EJhlHDh3NcgcPlG1BXZu98sDmTuBa4fsMnz9fniJUaGzs+eMC540XuR0aDO2L8Y3qPyMcdOM+R/8XcqRA3qp9gAAAABJRU5ErkJggg==</Image>
 <Image width="65" height="26">data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAAEEAAAAaCAYAAADovjFxAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjk1RjU4MDYwQkU0RDExRTNBNkI0RTM0MDNENDQ0Q0QxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjk1RjU4MDYxQkU0RDExRTNBNkI0RTM0MDNENDQ0Q0QxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OTVGNTgwNUVCRTREMTFFM0E2QjRFMzQwM0Q0NDRDRDEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6OTVGNTgwNUZCRTREMTFFM0E2QjRFMzQwM0Q0NDRDRDEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4FxGXPAAAGU0lEQVR42uyYe2xURRSHZ+7dF7tCW6q8WhTCM1TkIUgsBUFErUZ5qFExGkKiwahEwSBEA4oviAkCEg1GMQoVUFFACa9gJEil8tSyFYqIlFrElkcjULa7946/2Z67Hi53UUvgHzvJtzP33DMzZ86cedyVSinxf0+GaEpNTmhyAiVfwOdzytPBg6AzqAe7wTtgEdN/A/QGi8EKyvuDKBhGOq+Ah0EdeBMscPV5B3gW5NMkHAKrwdNM50bwHLAB37S0safB46CWZIPBZGqvGiwDL1JdnTLIhkwwA1jgXZJvAM8Iv2n6wHqg0lAIBBEnWSWocOktBjM96o9m9cddoJ+lTG/GBfQ0uaT3WJr3P4BWpNORyXd46K7WSh8wwULQy2VEKTPuJ1cDGz0aLQFV7HkL1e3GZDUgHxSAo0zel3TzwGwwB0wAu1zta51sJisHg8EkJvuI9HJAgsn1GNay55igTjawSg5OxYNpnJBPss+Y7HOS9WayzSTrClaBL0EH1uY8pjvKZYPDAabTmWTjmewRpltLsmP0HGZjOcb0ikl2Wq+xeYRen2PAINAPmLSmYh57SQ0opvIWcA+VF1N+gNaeyeqXg7uprPt4lPaT611rnicJjoEsep4CfqbyMKZXzco/ggLQEnQiW5y0k5X30N5jOZ3qjWQSuIKeFRuE123qDCu3YOXmrtydbgOvgb5MVs/Klkt/LXPASjCLvWvNytzGU6x8tcsJNiuH+RE5h04G7YBVIAeEmEGWx2BMFinSJXfLnHI3GpR2wElQSO/mpzFyJLiVRdtIlw3xNEe9SqPD7TvHRl15PAv7EaAK+AnB8sYmx4lPMtkUcogzW076k/Ku4EMmPwxGg5uZLJpmGV3j0pH/5rIUZOVsKr/OKut1dd1FOMGZmRwma88Gey+Tj6J+Z7uW2QNgOdgItpHNPIJGUN4K9KDyfnCC9oZ/dMJuNuO7wHrwVCp+DMOMW2pywrJ12Qm7AGvD71E2WOiFKP+U6T0P1oB9LnueABFDykzdH/oVCUucilsSiOQz3vWjDa2c2T6O7C5mbU2lPMJsCXhEj9+gmTjMZmg4mKs700fJ2bgqw9RMRPco25lWgzMybFsbaAsppeGxpvk+EqF8iev2eTs4TifEkoDP1O2NxoBPxxKyEv0tCAf9mdd2at18UK/c5q1bRiTsGBJLiBLoRWwr2dVA8DW1N5yi9izdKJd77DMhD7tCUg+UXupGmoEjYDMGqgfdvaBn+0PTR4brvtsvxfSlZXkwrg0mGmtXfS8R6Yj1Nn5D5WmnSEPuhG0IQWUE/b6Blm1rz/8Ox0X1mvCZhmN4Lp0wX2FAyu8zM+oTSk/AnoeGdhaRkCn65hhiSJfYnR2y6gZIU9g1pwL791UHi6atrBPfllb60HdCjw/9CPQzjPYC3eYmGgOf8QKKBi0vI3l3WqL1jhPOSzr8wkGfOP7WVcIIqfdUXJ45WhuYsG5vWBQftESHbAODkuKFoigcYbqCQdKKUKltQaZ+vbaMBv2X7u8ipt51QihLjheWnIWmWqjk1wPeS9T2YxCWaLdsd6aogqunFJUJPRHk3EanCzqhT5e2Yis+TZQSNwlbvi1NlQm/LoRBL6Pvem17tCIiSiqC4mRdMipEfkdL7Dliiu2HEmLvkToRwoob0TsiItjKNpXHxcHqs0JHcouwT+S1C4o+uYY4UKNwg8NX0C21cID2o9RLpJI+ktrSRhhNzqSh2smAjjp8Oc0yxdZoFfqQOhoujRN6dGwldk6l93ZyFj8B95HKXPC+9KlSz0DAzGE2/w4K/j41+So1y1qgYudEylCwFCykTU7RsTk2aXjIFsu3Z4kx8/cJn0zAKcZlc4JOPUER5TpVgB16fdPxFU1tRtLj+iI9rzXZtOMPoQ0zj+SL6DNZO+RjWvcVDedPQ+V+MxF5v/yRjKRG/5/QiDqldG/QN7iZdBO8ms54QWFcmjz+VPLi9Rtdgs7QwPX5fyVoQ/cEzQ2uPvTmNRGsY/9RrEk5gCZFNrNFYa8sOKHGdQhceic4aQXRny4rY2m3zSUK/2N7eynCvnDdBnNoSbx6Xg27IWK9P28ujxOctI2YRtdafVwNoNnOoA8gH1sIMbrJ6e+HX8FW8A0oSdN+lacDKCXsix+AbPrLvemP1iYnOOkvAQYAhXFLHTlsPyYAAAAASUVORK5CYII=</Image>
 <Image width="130" height="52">data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAAIIAAAA0CAYAAABGkOCVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjk1RjU4MDVDQkU0RDExRTNBNkI0RTM0MDNENDQ0Q0QxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjk1RjU4MDVEQkU0RDExRTNBNkI0RTM0MDNENDQ0Q0QxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OTVGNTgwNUFCRTREMTFFM0E2QjRFMzQwM0Q0NDRDRDEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6OTVGNTgwNUJCRTREMTFFM0E2QjRFMzQwM0Q0NDRDRDEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5AOR4OAAAOI0lEQVR42uxcC3BU1Rn+7+4mJCSECEgAEwKI8kgNikqtGtCOgkVtRSogamGUSrVoUUF0bKso1qlF6vtRmdGiUlSoQpX6qgqodBwURd4BiYTXggjkAUk2u7f/v/tdvLk55+7dZE1i3X/mm7Dnnvs65/uf51wM0zQpJSkxUkRISYoIKUkRISUpIqQkRYSUpIiQEncipPn9zraujF8yzmecxDjWduwA43PGW4z5+K2TkYyBjHqGD39fYZQzOjAm4R59GIcZXzCeY7zjuM4JjKsZpzN6MvYwPmXMw18vIs9xKeMcxgBGtu3YbsbHjCWMxYyQy3V+zjgR7+JVAoxtjEUuffIZYxhnM3ox2jOCjM2MpYzX4jxXf4y3Ccg9VzI+xPGfMcYyfoSx38r4gDGXsTfaQ4hgwxTGLobpAWWMMY7zLbRjbFScM5VRwFijuWaEcavtOpMY32j6HmFM09zfQjbjYUaNx3daySjSXKsjY4fH6zixj5GjuKaPcTtjb5zzVzFKXN7zfsU5r+PYLJfrbmOcIf0ClmVg3M+YlgDTCxkLGGEF2w2N1uQwnoWlUVooxp8Zn+D8p13un8H4C2MXrJNTOjJeZ5yVwDudAQ38KbTGLmmMSBMtbwjn20VM8fOMcR7OP5XxJvouURwPK9q+gZW5w+W6Yn1elOv70DAxQRLYJ242zI1TVMHHBAxyPHmI8bDHZ5AXbadofyBBElgi7meWi5lvivgV4zHLIwksyQThT/PYv4Bxj8f3vdIHf6RjzUKQ5ArGP1xYNdTjw/Xx2K+IUeyxr8QQ/RxtxSCdU44wHoW//A3jvy7xTb6jrZpRxqhl1DhwGNCJxD8Hbb8l3pmh6CfXegrH/q04nsV4TGFdVHI24hkvMlwYPoxxvOKgsOmPtt/zMQgTFX1Pghn2InWYjLWMMxEI+jR9j8A9SLBVgoCPFCa7q6PtCoX21iEIXmprE9P8kYJ0OdCoHY5nuYDRXaHdMi5P4rhTKhg3OdzKbbCmTgsq5H0Jv8VVP864ztFvCOMiBN7xrBBhnB/E/W+BkjmlXwARtET/x9gOHMKDOGWBhgh5CZi4W0AEkWfwd5LGr45BxEx4maWIgL24oSpHdrDMQQJLy1/TWJ8szaRWKNpHMc7TvO/tsAh2q6h6h/dtJCDbO1/DSFe42Fc8jHU5niuI35IprALR7dJFiPAy0qcujE4IAoMYSNVAkMeJUElQEdjN1xBhpY0ElizRDKJTfo/UqDPSX9HuFZq+ezTtXsldCHOtih9ehVbbZRj8vVPeVbRJwLpJEVwPxfMF4zzbU44+pbCATstlWg//FUCaSL8HiHJuM+sWexDNepmINYq2vQlE6aWAymR2hjvp6hJ8GR6DwEfhLpwi9Ykpinbd/Uo12UCpggjHwMTHI4KK/Nu8RsES/I1AKnUySNBBw+JEZZeLL3OKKvjyNfG+p0ALhqCo0gmmv10z32cK/LVKrmfsVLT3drGWpCGULkiPpwzbPKaaDYggWnI341cO35pMSaQiZyThfifjnS5sBol0cpJLmvkE3IJKumvaKxNQCJHj4jxfBEGsp3EN2AKYJZqI0pJ9iNBzm1PSppaTi1GG1j2vaMbXsAyJEj8TMYjqvHWM6S7ntte0+xIcMyOZ4+2zFSqKNH7rJuS9pyD3/j7IQLyTigQfIPMZjHd6qAnXvxNuRpUaT0I24mayEyECJam/qwSQx/9Yk85cSg0XloLfEyLcqdFWie5vdOT03yR47XOQAqvkbpcild3nq5Tu2ASf41AyB0xYNU7D2tuo8eqiv5npY0tInibFlKxoGjVeL0gkCBYL8zeXVPFeD9fYpmnvkmAauy3ZFmGgol1852pFe584Vay2INZSq1PWU6yE65Qil2CLFAWeEzT9pejzCALirRi/tQrN/Uxzvq4crLpfjSa9bhYRAhpLofJBv9BcZ1AbIkKXBMgqGl6i6d/X8VvWHya43HekRmsXo6hk1QmWgywBRaHJKT1BbKfInpAvk+0aDM1gOotH40lf1TtDE2e0hhx2IaszdZtJjReXLJngCDbHNOFZpGYwlWJVUmtCxUp8pBlD5/hO18Q685I9aEKEco32PIWawlDky8+6RKqyN+DJZqaWyZKNmphFfO1CaK7U319A4KgTcYNzbJakOQU1qdHYV0gf0MyF7ND6HcXWLiSw/a2i3xY8e1JFzNObmjhB6vN/T7B4UwzT15oiQaFsbFGVcmW18/UErjUaGcIBavqmFEvsxTSp2byksDKdEYe4ydRkZwwWEYR5V7n4VmfKch9MVmdHYDUdOXprSx0s2Kse+3+CmoNTS4MYlwOKGCMMy7M6Eo58ESbjEKo3RsBvFKA+IUpxnEvefx2U7ScJvNv0BImcEBG2gvmy8aSHS1/ZKDqZYsuYsk7/PNqleDKxPmwuFHvs59Hw+aJhh6qClplA9SvgsU0VCC6GJt9HjZdw7SJb7GRNQBazejFusPlxiYnsy8dSWd1VH44sMMmYzw+9mt83kpudSf0LYzoUqjfp0007Lb+Uw31K0gO+qyOmOUyhxVK/kNL30xh/N5Es7ma4Dl3G4pR2mrFVra9k23cx9wBLxX92gp8NwdQuBlHsS9OTDMMYG6oPzzTJ98G4YX1pcM80uv25dWweDF9awDfDNE2nef4XYo0GD0GxfYrdHO3inz9URPL3OghRg5qHKtaRiug1cBMd8E7Wjml5nzcchJRtd7JJRyqoe2Srf/1Rh2DIil+gsFvuvuLeneiqIenkZx3vlh2mUwswLKZBb2/OiZqLzXsNembFftpQFqQ0H2u+Yewm/VqLLFxNhCXJx8TuRzwgO5WknL3ThSiySHitwkXOoMaVzOFQaLtsV21ntybHhAms0Tq9cIQ6dcyiy0t60uyLK+UrCVpVnk03vlTFmrE7egkjClZZv6/BeVH19vtayl0EENCacB2huA6dn1Ge+5KzT6CMgEGhiEn5uQGaUlLHf2uIOZKPuKiAIkZ3TF4N+UxZVt9gGLRqf3Va9ei5EVq5tjw6DnbG+dXvngMllDWdSrinFinW6YjgSUJMk5lj+9Ntww+RGTIKMRDBnQcz6MHl6bSnop5WrAtSZdURqq79dvUz75gsSk8P0I7gwegA+VuOEJ4kzCQQ+s4cN4BmnHfo6FTw5Gez1l/Jv6UaO1hTuLKklC8xMmwaW+Ys60CPvlEefWeRyuqa6Jj42tB7N5kIojH9CvPo/ZszKTejXgbrNeTCd/EAPGP4zGr5iKqyNkCrd7SnD8sMase3CnPb6OI6OjY7RDcsbEcLl5dSvY3zAR4bdjm4R+yAGdUik9IDfoq4fJll9Q/4vS26yX1C9RGyxzbiDsKs/bPGF9G0cw+SGT56rYtRGMpPYJgm8IPPE0tZXQtv5jNp9rvZdN+ijZTWhuqxTSaCWIMbLuxHsy+psAZLCjYvIl+WiPpP+F0nL99AzNjgMlnon2s60px3KqOTZ/IEfLy+nCKIcfr36kpDjs+lkr4Bumfxbtq+5yDcTWyyZdLk36FIzNyeNiA2R6s27DhKHr+vMSnqbYngwN551DErnVZv2kU18lJ81gWn96LF19baSUAIPiXC3wx/LS6g1pFuX+XIAi6n2D7P2APGHoq+PpxG586poi3l+1rSPX53RLh+5In011GV9gHLQ+1hBH5/hsKUDMZBdUmrIUmWbc2h2nqZZKJhfaooI52TM0OCr/b00HIffbU/RPsr63jidkQnWbR3yMACmjw0h8YPjt1i/qe59PSKCvq6ooa28mD7HGQoGVQYPbekbyZNPvMw5WaGaOVXHWjpOh9tDNbSE5dFqEtWqCneuQeysAz8Pg3pacNB95v06wXZNO8/pW3GKiSbCFZgJjugb7K1laEs+hwiYZdap230I0bDmJ5NrJCiptZHa4PtWZvY1bB2Dz6uOnrsaH+5BludYGU67a5MI8NhFAb1qIq2Rb2MWCfTcV+rzV0yUBLuAMsn1kH2QG5HiiYp3wD8bfSOy7bk0EUPbIpZNcP4vySCvSr3iKO+fwTpkKRubze5QgZSKAnj1i9ef28iW9Quo9hHMtaK4TBUVKUcvwxtbyNV0xJ+xGMBWvb5drYKrU+E79JBLUJJ9xVHQUk2u7yMos1cBGGJbcowMZkWvPaL118vUoK/FZMsy8t/sJFASsXWkrD9E7u4lc3hRdnUVuS7jlTKMPETFAWffBR7liCWWIiiyKA41cCWkO4orEmhS3YcfY5/D7VVMfejADfWFv+MsB1b5E5mg87vV0/tOaWMRFp/X0+ghe4zDzVyKZPKilpHRZA1GggjjlgPc7ueYmv520mzFbuZko2KpfwfA8XQ6v7U+DO6o44Fsc5dILolYiGspfi5FG9bH899cfcqGnVWb3rhvVLy/UCIYGnJHSDFVKRaqs/K/EhBBaPQVoHzJSL/EuXTIFK4QzgmJDngqIRmoVonlTrrS65uQC/cQzZ/dPZohd6AZXhfcWwMAsgylKo9yYVFASYC/WAsgl02waTKZ++y4/dKF+2zl14Fuo9D6qCpIYfVCIAERjPczWFYs8c1BLAyCGv30mRlpqCRI6HWDxRbiwiWbECRRjRMvlIej8pkU9KYdNuEJEs2Iqidj3+7ifyfD8cjxnmLvocSaAPPsBfaJjgFWYSsxp1M3v4fgGTKGsQlEuh9BEvjRSSTGEz6jakpIiQoq4G74b9lY+k5FNvv15+a/61ig1IIglBZkpY9he/BSoWacK3dpP9GMUWEJMQSmxCBCwHyEJnL9nPZT1iAts4IBNM0Ef4+BJESWMqGGtlZvA7ZyF4UuX7wkvp/FlOSIkJKUkRISYoIKUkRISUpIqTEXf4nwABZ4DL1UAe0XwAAAABJRU5ErkJggg==</Image>
 <Url type="application/x-suggestions+json" method="GET" template="https://completion.amazon.com/search/complete?q={searchTerms}&amp;search-alias=aps&amp;mkt=1"/>
-<Url type="text/html" method="GET" template="http://www.amazon.com/exec/obidos/external-search/" rel="searchform">
+<Url type="text/html" method="GET" template="https://www.amazon.com/exec/obidos/external-search/" rel="searchform">
   <Param name="field-keywords" value="{searchTerms}"/>
   <Param name="mode" value="blended"/>
   <Param name="tag" value="mozilla-20"/>
   <Param name="sourceid" value="Mozilla-search"/>
 </Url>
 </SearchPlugin>
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -89,17 +89,16 @@
     locale/browser/sanitize.dtd                    (%chrome/browser/sanitize.dtd)
     locale/browser/search.properties               (%chrome/browser/search.properties)
     locale/browser/searchbar.dtd                   (%chrome/browser/searchbar.dtd)
     locale/browser/sitePermissions.properties      (%chrome/browser/sitePermissions.properties)
     locale/browser/engineManager.dtd               (%chrome/browser/engineManager.dtd)
     locale/browser/engineManager.properties        (%chrome/browser/engineManager.properties)
     locale/browser/setDesktopBackground.dtd        (%chrome/browser/setDesktopBackground.dtd)
     locale/browser/shellservice.properties         (%chrome/browser/shellservice.properties)
-    locale/browser/tabbrowser.dtd                  (%chrome/browser/tabbrowser.dtd)
     locale/browser/tabbrowser.properties           (%chrome/browser/tabbrowser.properties)
     locale/browser/tabview.properties              (%chrome/browser/tabview.properties)
     locale/browser/taskbar.properties              (%chrome/browser/taskbar.properties)
     locale/browser/translation.dtd                 (%chrome/browser/translation.dtd)
     locale/browser/translation.properties          (%chrome/browser/translation.properties)
     locale/browser/webrtcIndicator.properties      (%chrome/browser/webrtcIndicator.properties)
     locale/browser/downloads/downloads.dtd         (%chrome/browser/downloads/downloads.dtd)
     locale/browser/downloads/downloads.properties  (%chrome/browser/downloads/downloads.properties)
--- a/browser/modules/ContentCrashReporters.jsm
+++ b/browser/modules/ContentCrashReporters.jsm
@@ -83,25 +83,35 @@ this.TabCrashReporter = {
           this.browserMap.delete(browser);
           browser.contentDocument.documentElement.classList.remove("crashDumpAvailable");
           browser.contentDocument.documentElement.classList.add("crashDumpSubmitted");
         }
       }
     }
   },
 
-  onAboutTabCrashedLoad: function (aBrowser) {
+  onAboutTabCrashedLoad: function (aBrowser, aParams) {
+    // If there was only one tab open that crashed, do not show the "restore all tabs" button
+    if (aParams.crashedTabCount == 1) {
+      this.hideRestoreAllButton(aBrowser);
+    }
+
     if (!this.childMap)
       return;
 
     let dumpID = this.childMap.get(this.browserMap.get(aBrowser));
     if (!dumpID)
       return;
 
     aBrowser.contentDocument.documentElement.classList.add("crashDumpAvailable");
+  },
+
+  hideRestoreAllButton: function (aBrowser) {
+    aBrowser.contentDocument.getElementById("restoreAll").setAttribute("hidden", true);
+    aBrowser.contentDocument.getElementById("restoreTab").setAttribute("class", "primary");
   }
 }
 
 this.PluginCrashReporter = {
   /**
    * Makes the PluginCrashReporter ready to hear about and
    * submit crash reports.
    */
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -109,18 +109,18 @@ this.ContentSearch = {
   },
 
   get searchSuggestionUIStrings() {
     if (this._searchSuggestionUIStrings) {
       return this._searchSuggestionUIStrings;
     }
     this._searchSuggestionUIStrings = {};
     let searchBundle = Services.strings.createBundle("chrome://browser/locale/search.properties");
-    let stringNames = ["searchHeader", "searchPlaceholder", "searchFor",
-                       "searchWith", "searchWithHeader"];
+    let stringNames = ["searchHeader", "searchPlaceholder", "searchForKeywordsWith",
+                       "searchWithHeader", "searchSettings"];
     for (let name of stringNames) {
       this._searchSuggestionUIStrings[name] = searchBundle.GetStringFromName(name);
     }
     return this._searchSuggestionUIStrings;
   },
 
   destroy: function () {
     if (this._destroyedPromise) {
--- a/browser/modules/ContentWebRTC.jsm
+++ b/browser/modules/ContentWebRTC.jsm
@@ -81,24 +81,31 @@ function handleRequest(aSubject, aTopic,
 
 function prompt(aContentWindow, aWindowID, aCallID, aConstraints, aDevices, aSecure) {
   let audioDevices = [];
   let videoDevices = [];
   let devices = [];
 
   // MediaStreamConstraints defines video as 'boolean or MediaTrackConstraints'.
   let video = aConstraints.video || aConstraints.picture;
+  let audio = aConstraints.audio;
   let sharingScreen = video && typeof(video) != "boolean" &&
                       video.mediaSource != "camera";
+  let sharingAudio = audio && typeof(audio) != "boolean" &&
+                     audio.mediaSource != "microphone";
   for (let device of aDevices) {
     device = device.QueryInterface(Ci.nsIMediaDevice);
     switch (device.type) {
       case "audio":
-        if (aConstraints.audio) {
-          audioDevices.push({name: device.name, deviceIndex: devices.length});
+        // Check that if we got a microphone, we have not requested an audio
+        // capture, and if we have requested an audio capture, we are not
+        // getting a microphone instead.
+        if (audio && (device.mediaSource == "microphone") != sharingAudio) {
+          audioDevices.push({name: device.name, deviceIndex: devices.length,
+                             mediaSource: device.mediaSource});
           devices.push(device);
         }
         break;
       case "video":
         // Verify that if we got a camera, we haven't requested a screen share,
         // or that if we requested a screen share we aren't getting a camera.
         if (video && (device.mediaSource == "camera") != sharingScreen) {
           videoDevices.push({name: device.name, deviceIndex: devices.length,
@@ -108,17 +115,17 @@ function prompt(aContentWindow, aWindowI
         break;
     }
   }
 
   let requestTypes = [];
   if (videoDevices.length)
     requestTypes.push(sharingScreen ? "Screen" : "Camera");
   if (audioDevices.length)
-    requestTypes.push("Microphone");
+    requestTypes.push(sharingAudio ? "AudioCapture" : "Microphone");
 
   if (!requestTypes.length) {
     denyRequest({callID: aCallID}, "NotFoundError");
     return;
   }
 
   if (!aContentWindow.pendingGetUserMediaRequests) {
     aContentWindow.pendingGetUserMediaRequests = new Map();
@@ -128,16 +135,17 @@ function prompt(aContentWindow, aWindowI
 
   let request = {
     callID: aCallID,
     windowID: aWindowID,
     documentURI: aContentWindow.document.documentURI,
     secure: aSecure,
     requestTypes: requestTypes,
     sharingScreen: sharingScreen,
+    sharingAudio: sharingAudio,
     audioDevices: audioDevices,
     videoDevices: videoDevices
   };
 
   let mm = getMessageManagerForWindow(aContentWindow);
   mm.sendAsyncMessage("webrtc:Request", request);
 }
 
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -183,30 +183,30 @@ function getHost(uri, href) {
       host = bundle.GetStringFromName("getUserMedia.sharingMenuUnknownHost");
     }
   }
   return host;
 }
 
 function prompt(aBrowser, aRequest) {
   let {audioDevices: audioDevices, videoDevices: videoDevices,
-       sharingScreen: sharingScreen, requestTypes: requestTypes} = aRequest;
+       sharingScreen: sharingScreen, sharingAudio: sharingAudio,
+       requestTypes: requestTypes} = aRequest;
   let uri = Services.io.newURI(aRequest.documentURI, null, null);
   let host = getHost(uri);
   let chromeDoc = aBrowser.ownerDocument;
   let chromeWin = chromeDoc.defaultView;
   let stringBundle = chromeWin.gNavigatorBundle;
   let stringId = "getUserMedia.share" + requestTypes.join("And") + ".message";
   let message = stringBundle.getFormattedString(stringId, [host]);
 
   let mainLabel;
-  if (sharingScreen) {
+  if (sharingScreen || sharingAudio) {
     mainLabel = stringBundle.getString("getUserMedia.shareSelectedItems.label");
-  }
-  else {
+  } else {
     let string = stringBundle.getString("getUserMedia.shareSelectedDevices.label");
     mainLabel = PluralForm.get(requestTypes.length, string);
   }
 
   let notification; // Used by action callbacks.
   let mainAction = {
     label: mainLabel,
     accessKey: stringBundle.getString("getUserMedia.shareSelectedDevices.accesskey"),
@@ -220,38 +220,38 @@ function prompt(aBrowser, aRequest) {
     {
       label: stringBundle.getString("getUserMedia.denyRequest.label"),
       accessKey: stringBundle.getString("getUserMedia.denyRequest.accesskey"),
       callback: function () {
         denyRequest(notification.browser, aRequest);
       }
     }
   ];
-
-  if (!sharingScreen) { // Bug 1037438: implement 'never' for screen sharing.
+  // Bug 1037438: implement 'never' for screen sharing.
+  if (!sharingScreen && !sharingAudio) {
     secondaryActions.push({
       label: stringBundle.getString("getUserMedia.never.label"),
       accessKey: stringBundle.getString("getUserMedia.never.accesskey"),
       callback: function () {
         denyRequest(notification.browser, aRequest);
         // Let someone save "Never" for http sites so that they can be stopped from
         // bothering you with doorhangers.
         let perms = Services.perms;
         if (audioDevices.length)
           perms.add(uri, "microphone", perms.DENY_ACTION);
         if (videoDevices.length)
           perms.add(uri, "camera", perms.DENY_ACTION);
       }
     });
   }
 
-  if (aRequest.secure && !sharingScreen) {
+  if (aRequest.secure && !sharingScreen && !sharingAudio) {
     // Don't show the 'Always' action if the connection isn't secure, or for
-    // screen sharing (because we can't guess which window the user wants to
-    // share without prompting).
+    // screen/audio sharing (because we can't guess which window the user wants
+    // to share without prompting).
     secondaryActions.unshift({
       label: stringBundle.getString("getUserMedia.always.label"),
       accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
       callback: function () {
         mainAction.callback(true);
       }
     });
   }
@@ -261,17 +261,18 @@ function prompt(aBrowser, aRequest) {
       if (aTopic == "swapping")
         return true;
 
       let chromeDoc = this.browser.ownerDocument;
 
       if (aTopic == "shown") {
         let PopupNotifications = chromeDoc.defaultView.PopupNotifications;
         let popupId = "Devices";
-        if (requestTypes.length == 1 && requestTypes[0] == "Microphone")
+        if (requestTypes.length == 1 && (requestTypes[0] == "Microphone" ||
+                                         requestTypes[0] == "AudioCapture"))
           popupId = "Microphone";
         if (requestTypes.indexOf("Screen") != -1)
           popupId = "Screen";
         PopupNotifications.panel.firstChild.setAttribute("popupid", "webRTC-share" + popupId);
       }
 
       if (aTopic != "showing")
         return false;
@@ -379,31 +380,35 @@ function prompt(aBrowser, aRequest) {
         menuitem.setAttribute("tooltiptext", deviceName);
         if (type)
           menuitem.setAttribute("devicetype", type);
         menupopup.appendChild(menuitem);
       }
 
       chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length || sharingScreen;
       chromeDoc.getElementById("webRTC-selectWindowOrScreen").hidden = !sharingScreen || !videoDevices.length;
-      chromeDoc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length;
+      chromeDoc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length || sharingAudio;
 
       let camMenupopup = chromeDoc.getElementById("webRTC-selectCamera-menupopup");
       let windowMenupopup = chromeDoc.getElementById("webRTC-selectWindow-menupopup");
       let micMenupopup = chromeDoc.getElementById("webRTC-selectMicrophone-menupopup");
       if (sharingScreen)
         listScreenShareDevices(windowMenupopup, videoDevices);
       else
         listDevices(camMenupopup, videoDevices);
-      listDevices(micMenupopup, audioDevices);
+
+      if (!sharingAudio)
+        listDevices(micMenupopup, audioDevices);
+
       if (requestTypes.length == 2) {
         let stringBundle = chromeDoc.defaultView.gNavigatorBundle;
         if (!sharingScreen)
           addDeviceToList(camMenupopup, stringBundle.getString("getUserMedia.noVideo.label"), "-1");
-        addDeviceToList(micMenupopup, stringBundle.getString("getUserMedia.noAudio.label"), "-1");
+        if (!sharingAudio)
+          addDeviceToList(micMenupopup, stringBundle.getString("getUserMedia.noAudio.label"), "-1");
       }
 
       this.mainAction.callback = function(aRemember) {
         let allowedDevices = [];
         let perms = Services.perms;
         if (videoDevices.length) {
           let listId = "webRTC-select" + (sharingScreen ? "Window" : "Camera") + "-menulist";
           let videoDeviceIndex = chromeDoc.getElementById(listId).value;
@@ -411,23 +416,28 @@ function prompt(aBrowser, aRequest) {
           if (allowCamera)
             allowedDevices.push(videoDeviceIndex);
           if (aRemember) {
             perms.add(uri, "camera",
                       allowCamera ? perms.ALLOW_ACTION : perms.DENY_ACTION);
           }
         }
         if (audioDevices.length) {
-          let audioDeviceIndex = chromeDoc.getElementById("webRTC-selectMicrophone-menulist").value;
-          let allowMic = audioDeviceIndex != "-1";
-          if (allowMic)
-            allowedDevices.push(audioDeviceIndex);
-          if (aRemember) {
-            perms.add(uri, "microphone",
-                      allowMic ? perms.ALLOW_ACTION : perms.DENY_ACTION);
+          if (!sharingAudio) {
+            let audioDeviceIndex = chromeDoc.getElementById("webRTC-selectMicrophone-menulist").value;
+            let allowMic = audioDeviceIndex != "-1";
+            if (allowMic)
+              allowedDevices.push(audioDeviceIndex);
+            if (aRemember) {
+              perms.add(uri, "microphone",
+                        allowMic ? perms.ALLOW_ACTION : perms.DENY_ACTION);
+            }
+          } else {
+            // Only one device possible for audio capture.
+            allowedDevices.push(0);
           }
         }
 
         if (!allowedDevices.length) {
           denyRequest(notification.browser, aRequest);
           return;
         }
 
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -141,16 +141,17 @@ browser.jar:
   skin/classic/browser/loop/menuPanel.png             (loop/menuPanel.png)
   skin/classic/browser/loop/menuPanel@2x.png          (loop/menuPanel@2x.png)
   skin/classic/browser/loop/toolbar.png               (loop/toolbar.png)
   skin/classic/browser/loop/toolbar@2x.png            (loop/toolbar@2x.png)
   skin/classic/browser/loop/toolbar-inverted.png      (loop/toolbar-inverted.png)
   skin/classic/browser/loop/toolbar-inverted@2x.png   (loop/toolbar-inverted@2x.png)
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
   skin/classic/browser/controlcenter/arrow-subview.svg  (../shared/controlcenter/arrow-subview.svg)
+  skin/classic/browser/controlcenter/arrow-subview-back.svg  (../shared/controlcenter/arrow-subview-back.svg)
   skin/classic/browser/controlcenter/conn-not-secure.svg  (../shared/controlcenter/conn-not-secure.svg)
   skin/classic/browser/controlcenter/conn-secure.svg  (../shared/controlcenter/conn-secure.svg)
   skin/classic/browser/controlcenter/conn-degraded.svg  (../shared/controlcenter/conn-degraded.svg)
   skin/classic/browser/controlcenter/mcb-disabled.svg  (../shared/controlcenter/mcb-disabled.svg)
   skin/classic/browser/controlcenter/permissions.svg  (../shared/controlcenter/permissions.svg)
   skin/classic/browser/controlcenter/tracking-protection.svg                 (../shared/controlcenter/tracking-protection.svg)
   skin/classic/browser/controlcenter/tracking-protection-disabled.svg        (../shared/controlcenter/tracking-protection-disabled.svg)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
@@ -237,16 +238,18 @@ browser.jar:
   skin/classic/browser/tabbrowser/alltabs.png         (tabbrowser/alltabs.png)
   skin/classic/browser/tabbrowser/alltabs-inverted.png (tabbrowser/alltabs-inverted.png)
   skin/classic/browser/tabbrowser/connecting.png      (tabbrowser/connecting.png)
   skin/classic/browser/tabbrowser/crashed.svg         (../shared/tabbrowser/crashed.svg)
   skin/classic/browser/tabbrowser/loading.png         (tabbrowser/loading.png)
   skin/classic/browser/tabbrowser/tab-active-middle.png     (tabbrowser/tab-active-middle.png)
   skin/classic/browser/tabbrowser/tab-arrow-left.png        (tabbrowser/tab-arrow-left.png)
   skin/classic/browser/tabbrowser/tab-arrow-left-inverted.png (tabbrowser/tab-arrow-left-inverted.png)
+  skin/classic/browser/tabbrowser/tab-audio.svg       (../shared/tabbrowser/tab-audio.svg)
+  skin/classic/browser/tabbrowser/tab-audio-small.svg (../shared/tabbrowser/tab-audio-small.svg)
   skin/classic/browser/tabbrowser/tab-background-end.png    (tabbrowser/tab-background-end.png)
   skin/classic/browser/tabbrowser/tab-background-middle.png (tabbrowser/tab-background-middle.png)
   skin/classic/browser/tabbrowser/tab-background-start.png  (tabbrowser/tab-background-start.png)
   skin/classic/browser/tabbrowser/tab-overflow-indicator.png (../shared/tabbrowser/tab-overflow-indicator.png)
 
 # NOTE: The following two files (tab-selected-end.svg, tab-selected-start.svg) get pre-processed in
 #       Makefile.in with a non-default marker of "%" and the result of that gets packaged.
   skin/classic/browser/tabbrowser/tab-selected-end.svg      (tab-selected-end.svg)
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -185,16 +185,17 @@ browser.jar:
   skin/classic/browser/loop/toolbar-inverted.png      (loop/toolbar-inverted.png)
   skin/classic/browser/loop/toolbar-inverted@2x.png   (loop/toolbar-inverted@2x.png)
   skin/classic/browser/yosemite/loop/menuPanel.png          (loop/menuPanel-yosemite.png)
   skin/classic/browser/yosemite/loop/menuPanel@2x.png       (loop/menuPanel-yosemite@2x.png)
   skin/classic/browser/yosemite/loop/toolbar.png            (loop/toolbar-yosemite.png)
   skin/classic/browser/yosemite/loop/toolbar@2x.png         (loop/toolbar-yosemite@2x.png)
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
   skin/classic/browser/controlcenter/arrow-subview.svg  (../shared/controlcenter/arrow-subview.svg)
+  skin/classic/browser/controlcenter/arrow-subview-back.svg  (../shared/controlcenter/arrow-subview-back.svg)
   skin/classic/browser/controlcenter/conn-not-secure.svg  (../shared/controlcenter/conn-not-secure.svg)
   skin/classic/browser/controlcenter/conn-secure.svg  (../shared/controlcenter/conn-secure.svg)
   skin/classic/browser/controlcenter/conn-degraded.svg  (../shared/controlcenter/conn-degraded.svg)
   skin/classic/browser/controlcenter/mcb-disabled.svg  (../shared/controlcenter/mcb-disabled.svg)
   skin/classic/browser/controlcenter/permissions.svg  (../shared/controlcenter/permissions.svg)
   skin/classic/browser/controlcenter/tracking-protection.svg                 (../shared/controlcenter/tracking-protection.svg)
   skin/classic/browser/controlcenter/tracking-protection-disabled.svg        (../shared/controlcenter/tracking-protection-disabled.svg)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
@@ -331,16 +332,18 @@ browser.jar:
   skin/classic/browser/tabbrowser/tab-arrow-left.png                     (tabbrowser/tab-arrow-left.png)
   skin/classic/browser/tabbrowser/tab-arrow-left@2x.png                  (tabbrowser/tab-arrow-left@2x.png)
   skin/classic/browser/tabbrowser/tab-arrow-left-inverted.png            (tabbrowser/tab-arrow-left-inverted.png)
   skin/classic/browser/tabbrowser/tab-arrow-left-inverted@2x.png         (tabbrowser/tab-arrow-left-inverted@2x.png)
   skin/classic/browser/tabbrowser/tab-arrow-right.png                    (tabbrowser/tab-arrow-right.png)
   skin/classic/browser/tabbrowser/tab-arrow-right@2x.png                 (tabbrowser/tab-arrow-right@2x.png)
   skin/classic/browser/tabbrowser/tab-arrow-right-inverted.png           (tabbrowser/tab-arrow-right-inverted.png)
   skin/classic/browser/tabbrowser/tab-arrow-right-inverted@2x.png        (tabbrowser/tab-arrow-right-inverted@2x.png)
+  skin/classic/browser/tabbrowser/tab-audio.svg                          (../shared/tabbrowser/tab-audio.svg)
+  skin/classic/browser/tabbrowser/tab-audio-small.svg                    (../shared/tabbrowser/tab-audio-small.svg)
   skin/classic/browser/tabbrowser/tab-background-end.png                 (tabbrowser/tab-background-end.png)
   skin/classic/browser/tabbrowser/tab-background-end@2x.png              (tabbrowser/tab-background-end@2x.png)
   skin/classic/browser/tabbrowser/tab-background-middle.png              (tabbrowser/tab-background-middle.png)
   skin/classic/browser/tabbrowser/tab-background-middle@2x.png           (tabbrowser/tab-background-middle@2x.png)
   skin/classic/browser/tabbrowser/tab-background-start.png               (tabbrowser/tab-background-start.png)
   skin/classic/browser/tabbrowser/tab-background-start@2x.png            (tabbrowser/tab-background-start@2x.png)
   skin/classic/browser/tabbrowser/tab-overflow-indicator.png             (../shared/tabbrowser/tab-overflow-indicator.png)
 
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/controlcenter/arrow-subview-back.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <polygon fill="#fff" points="12,3.5 10.5,2 4.625,8 10.5,14 12,12.5 7.625,8" />
+</svg>
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -7,21 +7,26 @@
    connection security. Show the organization address for EV certs. */
 #identity-popup-securityView:not(.unknownIdentity):not(.verifiedIdentity):not(.mixedContent):not(.weakCipher) > #identity-popup-content-supplemental,
 /* Show the "Connection is secure" labels only for EV and DV certs. */
 #identity-popup-security-content:not(.verifiedIdentity):not(.verifiedDomain) > .identity-popup-connection-secure,
 #identity-popup-securityView:not(.verifiedIdentity):not(.verifiedDomain) > #identity-popup-securityView-header > .identity-popup-connection-secure,
 /* Show the "Connection is not secure" labels only for non-secure sites. */
 #identity-popup-security-content:not(.unknownIdentity) > .identity-popup-connection-not-secure,
 #identity-popup-securityView:not(.unknownIdentity) > #identity-popup-securityView-header > .identity-popup-connection-not-secure,
+/* Show "This page is stored on your computer" only for file URLs. */
+#identity-popup-security-content:not(.fileURI) > .identity-popup-connection-file-uri,
+#identity-popup-securityView:not(.fileURI) > #identity-popup-securityView-header > .identity-popup-connection-file-uri,
 /* Show "This is a secure internal page" only for whitelisted pages. */
 #identity-popup-securityView:not(.chromeUI) > #identity-popup-securityView-header > .identity-popup-connection-internal,
 #identity-popup-security-content:not(.chromeUI) > .identity-popup-connection-internal,
 /* Hide the subsection arrow for whitelisted chromeUI pages. */
 #identity-popup-security-content.chromeUI + .identity-popup-expander,
+/* Hide the subsection arrow for whitelisted file URI pages. */
+#identity-popup-security-content.fileURI + .identity-popup-expander,
 /* Hide the tracking protection section for whitelisted chromeUI pages. */
 #identity-popup-mainView.chromeUI > #tracking-protection-container {
   display: none;
 }
 
 /* PANEL */
 
 #identity-popup,
@@ -60,17 +65,17 @@
 }
 
 #identity-popup-multiView > .panel-viewcontainer > .panel-viewstack > .panel-subviews:-moz-locale-dir(rtl) {
   border-bottom-right-radius: 0;
   border-bottom-left-radius: 3.5px;
 }
 
 .identity-popup-section:not(:first-child) {
-  border-top: 1px solid rgb(229,229,229);
+  border-top: 1px solid ThreeDShadow;
 }
 
 #identity-popup-securityView,
 #identity-popup-security-content,
 #identity-popup-permissions-content,
 #tracking-protection-content {
   padding: 0.75em 0 1em;
   -moz-padding-start: calc(2em + 24px);
@@ -106,32 +111,24 @@
 
 .identity-popup-expander:-moz-locale-dir(rtl) {
   transform: scaleX(-1);
 }
 
 .identity-popup-expander[panel-multiview-anchor] {
   transition: background-color 250ms ease-in;
   background-color: Highlight;
-  background-image: url("chrome://browser/skin/customizableui/subView-arrow-back-inverted.png"),
+  background-image: url("chrome://browser/skin/controlcenter/arrow-subview-back.svg"),
                     linear-gradient(rgba(255,255,255,0.3), transparent);
-  color: HighlightText;
-}
-
-@media (min-resolution: 1.1dppx) {
-  .identity-popup-expander[panel-multiview-anchor] {
-    background-image: url("chrome://browser/skin/customizableui/subView-arrow-back-inverted@2x.png"),
-                      linear-gradient(rgba(255,255,255,0.3), transparent);
-  }
 }
 
 .identity-popup-expander > .button-box {
   padding: 0;
   -moz-appearance: none;
-  border: solid #e5e5e5;
+  border: solid ThreeDShadow;
   border-width: 0 0 0 1px;
 }
 
 .identity-popup-expander:-moz-focusring > .button-box,
 .identity-popup-expander[panel-multiview-anchor] > .button-box {
   border: 0 none;
 }
 
@@ -204,27 +201,27 @@
 }
 
 #identity-popup-securityView.mixedActiveContent,
 #identity-popup-security-content.mixedActiveContent {
   background-image: url(chrome://browser/skin/controlcenter/mcb-disabled.svg);
 }
 
 #identity-popup-securityView-header {
-  border-bottom: 1px solid #e5e5e5;
+  border-bottom: 1px solid ThreeDShadow;
   padding-bottom: 1em;
   margin-bottom: 1em;
 }
 
 #identity-popup-content-owner {
   font-weight: 700;
 }
 
 #identity-popup-content-verifier {
-  color: #636363;
+  color: Graytext;
 }
 
 #identity-popup-content-owner,
 #identity-popup-securityView > #identity-popup-securityView-connection.identity-popup-text {
   margin-top: 1em;
 }
 
 /* TRACKING PROTECTION */
@@ -272,17 +269,17 @@
 /* FOOTER BUTTONS */
 
 #identity-popup-button-container {
   background-color: hsla(210,4%,10%,.07);
 }
 
 #identity-popup-more-info-button {
   border: none;
-  border-top: 1px solid hsla(210,4%,10%,.14);
+  border-top: 1px solid ThreeDShadow;
   background: transparent;
   -moz-appearance: none;
   margin-top: 5px;
   margin: 0;
 }
 
 #identity-popup-more-info-button > .button-box {
   -moz-appearance: none;
--- a/browser/themes/shared/devtools/splitview.css
+++ b/browser/themes/shared/devtools/splitview.css
@@ -135,25 +135,8 @@
 
 .splitview-main > .devtools-toolbarbutton {
   font-size: 11px;
   padding: 0 8px;
   width: auto;
   min-width: 48px;
   min-height: 0;
 }
-
-
-/* Resizers */
-
-.splitview-portrait-resizer {
-  -moz-appearance: none;
-  background: linear-gradient(black 1px, rgba(255,255,255,0.2) 1px),
-              linear-gradient(hsl(210,11%,36%), hsl(210,11%,18%));
-  height: 12px;
-  background-size: 10px 2px, 100% 12px;
-  background-clip: content-box, border-box;
-  background-repeat: repeat-y, no-repeat;
-  background-position: center center;
-  padding: 2px 0;
-  border-top: 1px solid hsla(210,8%,5%,.5);
-  border-bottom: 1px solid hsla(210,8%,5%,.5);
-}
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -106,50 +106,54 @@
 #tracking-protection-icon:not([state]) {
   margin-left: -16px;
   pointer-events: none;
   opacity: 0;
   /* Only animate the shield in, when it disappears hide it immediately. */
   transition: none;
 }
 
+#urlbar[pageproxystate="invalid"] > #identity-box > #identity-icons > #tracking-protection-icon {
+  visibility: collapse;
+}
+
 /* MAIN IDENTITY ICON */
 
 #page-proxy-favicon {
   width: 16px;
   height: 16px;
   list-style-image: url(chrome://browser/skin/identity-not-secure.svg);
 }
 
-.chromeUI > #page-proxy-favicon[pageproxystate="valid"] {
+.chromeUI > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
   list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
 }
 
-.verifiedDomain > #page-proxy-favicon[pageproxystate="valid"],
-.verifiedIdentity > #page-proxy-favicon[pageproxystate="valid"] {
+.verifiedDomain > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
+.verifiedIdentity > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
   list-style-image: url(chrome://browser/skin/identity-secure.svg);
 }
 
-.mixedActiveContent > #page-proxy-favicon[pageproxystate="valid"] {
+.mixedActiveContent > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
   list-style-image: url(chrome://browser/skin/identity-mixed-active-loaded.svg);
 }
 
-.weakCipher > #page-proxy-favicon[pageproxystate="valid"],
-.mixedDisplayContent > #page-proxy-favicon[pageproxystate="valid"],
-.mixedDisplayContentLoadedActiveBlocked > #page-proxy-favicon[pageproxystate="valid"] {
+.weakCipher > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
+.mixedDisplayContent > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
+.mixedDisplayContentLoadedActiveBlocked > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
   list-style-image: url(chrome://browser/skin/identity-mixed-passive-loaded.svg);
 }
 
-.mixedActiveBlocked > #page-proxy-favicon[pageproxystate="valid"] {
+.mixedActiveBlocked > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
   list-style-image: url(chrome://browser/skin/identity-mixed-active-blocked.svg);
 }
 
 #page-proxy-favicon[pageproxystate="invalid"] {
   opacity: 0.3;
 }
 
-#urlbar[actiontype="searchengine"] > #identity-box > #page-proxy-favicon {
+#urlbar[actiontype="searchengine"] > #identity-box > #identity-icons > #page-proxy-favicon {
   -moz-image-region: inherit;
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
   width: 16px;
   height: 16px;
   opacity: 1;
 }
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/tabbrowser/tab-audio-small.svg
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
+  <style>
+    use:not(:target) {
+      display: none;
+    }
+    .icon {
+      fill: #4d4d4d;
+    }
+    .icon.hover {
+      fill: #333333;
+    }
+    .icon.pressed {
+      fill: #000;
+    }
+    .icon.dark {
+      fill: #ccc;
+    }
+    .icon.dark.hover {
+      fill: #b2b2b2;
+    }
+    .icon.dark.pressed {
+      fill: #fff;
+    }
+    .muted {
+      opacity: .7;
+      stroke: #4d4d4d;
+      stroke-width: 0;
+    }
+    .muted.hover {
+      opacity: .85;
+      stroke: #333333;
+    }
+    .muted.pressed {
+      opacity: 1;
+      stroke: #000;
+    }
+    .muted.dark {
+      stroke: #ccc;
+    }
+    .muted.dark.hover {
+      stroke: #b2b2b2;
+    }
+    .muted.dark.pressed {
+      stroke: #fff;
+    }
+  </style>
+  <defs>
+    <clipPath id="clip-wave">
+      <path d="M 11,7 l 3,-8 l 2,0 l 0,18 l -2,0 l -3,-8 z" />
+    </clipPath>
+    <mask id="disabled-cutout">
+      <rect width="16" height="16" fill="#fff" />
+      <line x1="4" y1="14" x2="14" y2="4" stroke="#000" stroke-width="2" />
+    </mask>
+    <g id="shape-tab-audio">
+      <rect x="3" y="6" width="5" height="4" rx="1" ry="1" />
+      <polygon points="5.5,6.5 9,3 9,13 5.5,9.5" />
+      <path d="M 10,6.5 a 1.5 1.5 0 0,1 0,3 z" />
+      <path d="M 10,4 a 4 4 0 0,1 0,8 l 0,-1 a 3 3 0 0,0 0,-6 z"  clip-path="url(#clip-wave)" />
+    </g>
+    <g id="shape-tab-audio-muted">
+      <g mask="url(#disabled-cutout)">
+        <rect x="4" y="6" width="5" height="4" rx="1" ry="1" />
+        <polygon points="6.5,6.5 10,3 10,13 6.5,9.5" />
+      </g>
+      <line x1="3" y1="14" x2="13" y2="4" stroke-width="1.5" />
+    </g>
+  </defs>
+  <use id="tab-audio" class="icon" xlink:href="#shape-tab-audio"/>
+  <use id="tab-audio-hover" class="icon hover" xlink:href="#shape-tab-audio"/>
+  <use id="tab-audio-pressed" class="icon pressed" xlink:href="#shape-tab-audio"/>
+  <use id="tab-audio-muted" class="icon muted" xlink:href="#shape-tab-audio-muted" />
+  <use id="tab-audio-muted-hover" class="icon muted hover" xlink:href="#shape-tab-audio-muted" />
+  <use id="tab-audio-muted-pressed" class="icon muted pressed" xlink:href="#shape-tab-audio-muted" />
+  <use id="tab-audio-dark" class="icon dark" xlink:href="#shape-tab-audio"/>
+  <use id="tab-audio-dark-hover" class="icon hover dark" xlink:href="#shape-tab-audio"/>
+  <use id="tab-audio-dark-pressed" class="icon pressed dark" xlink:href="#shape-tab-audio"/>
+  <use id="tab-audio-muted-dark" class="icon muted dark" xlink:href="#shape-tab-audio-muted" />
+  <use id="tab-audio-muted-dark-hover" class="icon muted hover dark" xlink:href="#shape-tab-audio-muted" />
+  <use id="tab-audio-muted-dark-pressed" class="icon muted pressed dark" xlink:href="#shape-tab-audio-muted" />
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/tabbrowser/tab-audio.svg
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
+  <style>
+    use:not(:target) {
+      display: none;
+    }
+    .icon {
+      fill: #666;
+    }
+    .icon.hover {
+      fill: #4d4d4d;
+    }
+    .icon.pressed {
+      fill: #000;
+    }
+    .icon.dark {
+      fill: #999;
+    }
+    .icon.dark.hover {
+      fill: #b2b2b2;
+    }
+    .icon.dark.pressed {
+      fill: #fff;
+    }
+    .muted {
+      opacity: .7;
+      stroke: #666;
+      stroke-width: 0;
+    }
+    .muted.hover {
+      opacity: .85;
+      stroke: #4d4d4d;
+    }
+    .muted.pressed {
+      opacity: 1;
+      stroke: #000;
+    }
+    .muted.dark {
+      stroke: #999;
+    }
+    .muted.dark.hover {
+      stroke: #b2b2b2;
+    }
+    .muted.dark.pressed {
+      stroke: #fff;
+    }
+  </style>
+  <defs>
+    <clipPath id="clip-wave">
+      <path d="M 10,7 l 3,-8 l 2,0 l 0,18 l -2,0 l -3,-8 z" />
+    </clipPath>
+    <mask id="disabled-cutout">
+      <rect width="16" height="16" fill="#fff" />
+      <line x1="4" y1="14" x2="14" y2="4" stroke="#000" stroke-width="2" />
+    </mask>
+    <g id="shape-tab-audio">
+      <rect x="2" y="5" width="6" height="6" rx="2" ry="2" />
+      <polygon points="4,6 9,2 9,14 4,10" />
+      <path d="M 10,7 a 1 1 0 0,1 0,2 z" />
+      <path d="M 10,5 a 3 3 0 0,1 0,6 l 0,-1 a 2 2 0 0,0 0,-4 z"  clip-path="url(#clip-wave)" />
+      <path d="M 10,3 a 5 5 0 0,1 0,10 l 0,-1 a 4 4 0 0,0 0,-8 z" clip-path="url(#clip-wave)" />
+    </g>
+    <g id="shape-tab-audio-muted">
+      <g mask="url(#disabled-cutout)">
+        <rect x="3" y="5" width="6" height="6" rx="2" ry="2" />
+        <polygon points="5,6 10,2 10,14 5,10" />
+      </g>
+      <line x1="2" y1="13" x2="14" y2="3" stroke-width="1.5" />
+    </g>
+  </defs>
+  <use id="tab-audio" class="icon" xlink:href="#shape-tab-audio"/>
+  <use id="tab-audio-hover" class="icon hover" xlink:href="#shape-tab-audio"/>
+  <use id="tab-audio-pressed" class="icon pressed" xlink:href="#shape-tab-audio"/>
+  <use id="tab-audio-muted" class="icon muted" xlink:href="#shape-tab-audio-muted" />
+  <use id="tab-audio-muted-hover" class="icon muted hover" xlink:href="#shape-tab-audio-muted" />
+  <use id="tab-audio-muted-pressed" class="icon muted pressed" xlink:href="#shape-tab-audio-muted" />
+  <use id="tab-audio-dark" class="icon dark" xlink:href="#shape-tab-audio"/>
+  <use id="tab-audio-dark-hover" class="icon hover dark" xlink:href="#shape-tab-audio"/>
+  <use id="tab-audio-dark-pressed" class="icon pressed dark" xlink:href="#shape-tab-audio"/>
+  <use id="tab-audio-muted-dark" class="icon muted dark" xlink:href="#shape-tab-audio-muted" />
+  <use id="tab-audio-muted-dark-hover" class="icon muted hover dark" xlink:href="#shape-tab-audio-muted" />
+  <use id="tab-audio-muted-dark-pressed" class="icon muted pressed dark" xlink:href="#shape-tab-audio-muted" />
+</svg>
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -67,16 +67,17 @@
 }
 
 .tab-content[pinned] {
   -moz-padding-end: 3px;
 }
 
 .tab-throbber,
 .tab-icon-image,
+.tab-icon-sound,
 .tab-close-button {
   margin-top: 1px;
 }
 
 .tab-throbber,
 .tab-icon-image {
   height: 16px;
   width: 16px;
@@ -85,26 +86,78 @@
 
 .tab-icon-image {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .tab-icon-overlay {
   width: 16px;
   height: 16px;
-  margin-top: 10px;
+  margin-top: -12px;
   -moz-margin-start: -16px;
   display: none;
 }
 
 .tab-icon-overlay[crashed] {
   display: -moz-box;
   list-style-image: url("chrome://browser/skin/tabbrowser/crashed.svg");
 }
 
+.tab-icon-overlay[soundplaying][pinned] {
+  display: -moz-box;
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio");
+  border-radius: 8px;
+}
+
+.tab-icon-overlay[soundplaying][pinned]:hover {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-hover");
+  background-color: white;
+}
+
+.tab-icon-overlay[soundplaying][pinned]:hover:active {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-pressed");
+  background-color: white;
+}
+
+.tab-icon-overlay[muted][pinned] {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted");
+}
+
+.tab-icon-overlay[muted][pinned]:hover {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-hover");
+}
+
+.tab-icon-overlay[muted][pinned]:hover:active {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-pressed");
+}
+
+#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned] {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-dark");
+}
+
+#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned]:hover {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-hover");
+}
+
+#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned]:hover:active {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-pressed");
+}
+
+#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned] {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted");
+}
+
+#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:hover {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-hover");
+}
+
+#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:hover:active {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-pressed");
+}
+
 .tab-throbber[busy] {
   list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
 }
 
 .tab-throbber[progress] {
   list-style-image: url("chrome://browser/skin/tabbrowser/loading.png");
 }
 
@@ -114,16 +167,76 @@
 }
 
 .tab-close-button {
   -moz-margin-start: 4px;
   -moz-margin-end: -2px;
   padding: 0;
 }
 
+.tab-icon-sound {
+  -moz-margin-start: 4px;
+  width: 16px;
+  height: 16px;
+  padding: 0;
+}
+
+.tab-icon-sound:not([soundplaying]),
+.tab-icon-sound[pinned] {
+  display: none;
+}
+
+.tab-icon-sound[soundplaying] {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio");
+}
+
+.tab-icon-sound[soundplaying]:hover {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-hover");
+}
+
+.tab-icon-sound[soundplaying]:hover:active {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-pressed");
+}
+
+.tab-icon-sound[muted] {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted");
+}
+
+.tab-icon-sound[muted]:hover {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-hover");
+}
+
+.tab-icon-sound[muted]:hover:active {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-pressed");
+}
+
+#TabsToolbar[brighttext] .tab-icon-sound[soundplaying] {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-dark");
+}
+
+#TabsToolbar[brighttext] .tab-icon-sound[soundplaying]:hover {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-dark-hover");
+}
+
+#TabsToolbar[brighttext] .tab-icon-sound[soundplaying]:hover:active {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-dark-pressed");
+}
+
+#TabsToolbar[brighttext] .tab-icon-sound[muted] {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-dark");
+}
+
+#TabsToolbar[brighttext] .tab-icon-sound[muted]:hover {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-dark-hover");
+}
+
+#TabsToolbar[brighttext] .tab-icon-sound[muted]:hover:active {
+  list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-dark-pressed");
+}
+
 .tab-background,
 .tabs-newtab-button {
   /* overlap the tab curves */
   -moz-margin-end: -@tabCurveHalfWidth@;
   -moz-margin-start: -@tabCurveHalfWidth@;
 }
 
 .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox {
@@ -295,16 +408,18 @@
 
 /* Tab pointer-events */
 .tabbrowser-tab {
   pointer-events: none;
 }
 
 .tab-background-middle,
 .tabs-newtab-button,
+.tab-icon-overlay[soundplaying],
+.tab-icon-sound,
 .tab-close-button {
   pointer-events: auto;
 }
 
 /* Pinned tabs */
 
 /* Pinned tab separators need position: absolute when positioned (during overflow). */
 #tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned]::before {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1216,16 +1216,17 @@ toolbarbutton[constrain-size="true"][cui
     border-radius: 2px;
   }
 }
 
 @media (-moz-windows-default-theme) {
   #urlbar,
   .searchbar-textbox {
     @navbarTextboxCustomBorder@
+    border-radius: 1px;
   }
 
   @media (-moz-os-version: windows-vista),
          (-moz-os-version: windows-win7),
          (-moz-os-version: windows-win8) {
     #urlbar:not(:-moz-lwtheme),
     .searchbar-textbox:not(:-moz-lwtheme) {
       border-color: hsla(210,54%,20%,.25) hsla(210,54%,20%,.27) hsla(210,54%,20%,.3);
@@ -2023,16 +2024,24 @@ richlistitem[type~="action"][actiontype=
   .tab-background-end[visuallyselected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-start@2x.png);
   }
 
   .tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr)::after,
   .tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-end@2x.png);
   }
+
+  .tab-throbber[busy] {
+    list-style-image: url("chrome://browser/skin/tabbrowser/connecting@2x.png");
+  }
+
+  .tab-throbber[progress] {
+    list-style-image: url("chrome://browser/skin/tabbrowser/loading@2x.png");
+  }
 }
 
 /* Remove border between tab strip and navigation toolbar on Windows 10+ */
 @media not all and (-moz-os-version: windows-xp) {
   @media not all and (-moz-os-version: windows-vista) {
     @media not all and (-moz-os-version: windows-win7) {
       @media not all and (-moz-os-version: windows-win8) {
         .tab-background-end[visuallyselected=true]::after,
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -197,16 +197,17 @@ browser.jar:
         skin/classic/browser/loop/toolbar-lunaSilver.png             (loop/toolbar-lunaSilver.png)
         skin/classic/browser/loop/toolbar-lunaSilver@2x.png          (loop/toolbar-lunaSilver@2x.png)
         skin/classic/browser/loop/toolbar-win8.png                   (loop/toolbar-win8.png)
         skin/classic/browser/loop/toolbar-win8@2x.png                (loop/toolbar-win8@2x.png)
         skin/classic/browser/loop/toolbar-XP.png                     (loop/toolbar-XP.png)
         skin/classic/browser/loop/toolbar-XP@2x.png                  (loop/toolbar-XP@2x.png)
 *       skin/classic/browser/controlcenter/panel.css                 (controlcenter/panel.css)
         skin/classic/browser/controlcenter/arrow-subview.svg  (../shared/controlcenter/arrow-subview.svg)
+        skin/classic/browser/controlcenter/arrow-subview-back.s