Merge m-c to graphics
authorKartikaya Gupta <kgupta@mozilla.com>
Thu, 02 Mar 2017 10:27:31 -0500
changeset 374822 8744594fc366cf463313d3636e62789a907b0877
parent 374821 6422bc2ab9747390455a1b8a63f34c8965282ff9 (current diff)
parent 374582 66535e831760421b270662aa8d0773b0fde7c9f3 (diff)
child 374823 39628e255960d93b70dfc9b6b7891bfccf1130af
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.0a1
Merge m-c to graphics MozReview-Commit-ID: 2ILLtbCmQL3
browser/base/content/aboutaccounts/images/fox.png
browser/components/extensions/ext-theme.js
browser/components/extensions/schemas/theme.json
browser/components/extensions/test/browser/browser_ext_themes_chromeparity.js
browser/components/extensions/test/browser/browser_ext_themes_dynamic_updates.js
browser/components/extensions/test/browser/browser_ext_themes_lwtsupport.js
browser/modules/test/.eslintrc.js
browser/modules/test/browser.ini
browser/modules/test/browser_BrowserUITelemetry_buckets.js
browser/modules/test/browser_BrowserUITelemetry_defaults.js
browser/modules/test/browser_BrowserUITelemetry_sidebar.js
browser/modules/test/browser_BrowserUITelemetry_syncedtabs.js
browser/modules/test/browser_ContentSearch.js
browser/modules/test/browser_NetworkPrioritizer.js
browser/modules/test/browser_PermissionUI.js
browser/modules/test/browser_PermissionUI_prompts.js
browser/modules/test/browser_ProcessHangNotifications.js
browser/modules/test/browser_SelfSupportBackend.js
browser/modules/test/browser_SitePermissions.js
browser/modules/test/browser_SitePermissions_combinations.js
browser/modules/test/browser_SitePermissions_expiry.js
browser/modules/test/browser_SitePermissions_tab_urls.js
browser/modules/test/browser_UnsubmittedCrashHandler.js
browser/modules/test/browser_UsageTelemetry.js
browser/modules/test/browser_UsageTelemetry_content.js
browser/modules/test/browser_UsageTelemetry_content_aboutHome.js
browser/modules/test/browser_UsageTelemetry_private_and_restore.js
browser/modules/test/browser_UsageTelemetry_searchbar.js
browser/modules/test/browser_UsageTelemetry_urlbar.js
browser/modules/test/browser_bug1319078.js
browser/modules/test/browser_taskbar_preview.js
browser/modules/test/browser_urlBar_zoom.js
browser/modules/test/contentSearch.js
browser/modules/test/contentSearchBadImage.xml
browser/modules/test/contentSearchSuggestions.sjs
browser/modules/test/contentSearchSuggestions.xml
browser/modules/test/head.js
browser/modules/test/unit/social/.eslintrc.js
browser/modules/test/usageTelemetrySearchSuggestions.sjs
browser/modules/test/usageTelemetrySearchSuggestions.xml
browser/modules/test/xpcshell/.eslintrc.js
browser/modules/test/xpcshell/test_AttributionCode.js
browser/modules/test/xpcshell/test_DirectoryLinksProvider.js
browser/modules/test/xpcshell/test_E10SUtils_nested_URIs.js
browser/modules/test/xpcshell/test_LaterRun.js
browser/modules/test/xpcshell/test_SitePermissions.js
browser/modules/test/xpcshell/xpcshell.ini
browser/themes/linux/browser-lightweightTheme.css
browser/themes/osx/browser-lightweightTheme.css
browser/themes/windows/browser-lightweightTheme.css
devtools/client/inspector/layout/actions/grids.js
devtools/client/inspector/layout/actions/highlighter-settings.js
devtools/client/inspector/layout/actions/index.js
devtools/client/inspector/layout/actions/moz.build
devtools/client/inspector/layout/components/Grid.js
devtools/client/inspector/layout/components/GridDisplaySettings.js
devtools/client/inspector/layout/components/GridItem.js
devtools/client/inspector/layout/components/GridList.js
devtools/client/inspector/layout/components/GridOutline.js
devtools/client/inspector/layout/reducers/grids.js
devtools/client/inspector/layout/reducers/highlighter-settings.js
devtools/client/inspector/layout/reducers/moz.build
devtools/client/inspector/layout/types.js
devtools/client/inspector/layout/utils/l10n.js
devtools/client/inspector/layout/utils/moz.build
dom/workers/test/serviceworkers/claim_fetch_worker.js
dom/workers/test/serviceworkers/test_claim_fetch.html
gfx/thebes/gfxPrefs.h
intl/unicharutil/util/internal/moz.build
intl/unicharutil/util/objs.mozbuild
media/mtransport/testlib/moz.build
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testTabStripPrivacyMode.java
modules/libpref/init/all.js
rdf/util/internal/moz.build
rdf/util/moz.build
rdf/util/nsRDFResource.cpp
rdf/util/nsRDFResource.h
rdf/util/objs.mozbuild
security/sandbox/chromium/sandbox/win/wow_helper/service64_resolver.cc
security/sandbox/chromium/sandbox/win/wow_helper/service64_resolver.h
security/sandbox/chromium/sandbox/win/wow_helper/target_code.cc
security/sandbox/chromium/sandbox/win/wow_helper/target_code.h
security/sandbox/chromium/sandbox/win/wow_helper/wow_helper.cc
security/sandbox/win/wow_helper/Makefile.in
security/sandbox/win/wow_helper/moz.build
taskcluster/ci/marionette-harness/kind.yml
taskcluster/ci/test/tests.yml
taskcluster/scripts/tester/harness-test-linux.sh
testing/config/marionette_harness_test_requirements.txt
testing/marionette/harness/marionette_harness/tests/unit/test_window_type.py
testing/mozharness/scripts/marionette_harness_tests.py
testing/web-platform/meta/fetch/api/policies/referrer-origin-service-worker.https.html.ini
testing/web-platform/meta/fetch/api/policies/referrer-origin-worker.html.ini
testing/web-platform/meta/fetch/api/policies/referrer-origin.html.ini
testing/web-platform/meta/fetch/api/request/request-init-001.sub.html.ini
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
toolkit/profile/nsProfileStringTypes.h
tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-browserjs-globals.js
tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-test-globals.js
tools/profiler/core/SyncProfile.cpp
tools/profiler/core/SyncProfile.h
xpcom/string/nsEmbedString.h
xpfe/test/child-window.html
xpfe/test/winopen.js
xpfe/test/winopen.xul
--- a/.eslintignore
+++ b/.eslintignore
@@ -57,17 +57,16 @@ browser/app/**
 browser/branding/**/firefox-branding.js
 browser/base/content/browser-social.js
 browser/base/content/nsContextMenu.js
 browser/base/content/sanitizeDialog.js
 browser/base/content/test/general/file_csp_block_all_mixedcontent.html
 browser/base/content/test/urlbar/file_blank_but_not_blank.html
 browser/base/content/newtab/**
 browser/components/downloads/**
-browser/components/privatebrowsing/**
 browser/components/sessionstore/**
 browser/components/tabview/**
 # generated files in cld2
 browser/components/translation/cld2/cld-worker.js
 browser/extensions/pdfjs/content/build**
 browser/extensions/pdfjs/content/web**
 # generated or library files in pocket
 browser/extensions/pocket/content/panels/js/tmpl.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Touching clobber for Telemetry IPC refactor in bug 1339749.
+Touching clobber for Bug 1322703 - Use -Fd to specify unique PDB filename per-compile for MSVC
--- a/accessible/base/SelectionManager.h
+++ b/accessible/base/SelectionManager.h
@@ -116,17 +116,17 @@ protected:
 
   /**
    * Process DOM selection change. Fire selection and caret move events.
    */
   void ProcessSelectionChanged(SelData* aSelData);
 
 private:
   // Currently focused control.
-  nsWeakFrame mCurrCtrlFrame;
+  WeakFrame mCurrCtrlFrame;
   int32_t mCaretOffset;
   HyperTextAccessible* mAccWithCaret;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -106,17 +106,17 @@ nsCoreUtils::DispatchClickEvent(nsITreeB
 
   int32_t tcX = 0;
   tcBoxObj->GetX(&tcX);
 
   int32_t tcY = 0;
   tcBoxObj->GetY(&tcY);
 
   // Dispatch mouse events.
-  nsWeakFrame tcFrame = tcContent->GetPrimaryFrame();
+  AutoWeakFrame tcFrame = tcContent->GetPrimaryFrame();
   nsIFrame* rootFrame = presShell->GetRootFrame();
 
   nsPoint offset;
   nsIWidget *rootWidget =
     rootFrame->GetView()->GetNearestWidget(&offset);
 
   RefPtr<nsPresContext> presContext = presShell->GetPresContext();
 
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -1825,17 +1825,17 @@ Accessible::DispatchClickEvent(nsIConten
   nsCOMPtr<nsIPresShell> presShell = mDoc->PresShell();
 
   // Scroll into view.
   presShell->ScrollContentIntoView(aContent,
                                    nsIPresShell::ScrollAxis(),
                                    nsIPresShell::ScrollAxis(),
                                    nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
 
-  nsWeakFrame frame = aContent->GetPrimaryFrame();
+  AutoWeakFrame frame = aContent->GetPrimaryFrame();
   if (!frame)
     return;
 
   // Compute x and y coordinates.
   nsPoint point;
   nsCOMPtr<nsIWidget> widget = frame->GetNearestWidget(point);
   if (!widget)
     return;
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -16,16 +16,17 @@
 #include "AccessibleWrap.h"
 #include "Compatibility.h"
 #include "nsWinUtils.h"
 #include "RootAccessible.h"
 #endif
 
 namespace mozilla {
 namespace a11y {
+uint64_t DocAccessibleParent::sMaxDocID = 0;
 
 mozilla::ipc::IPCResult
 DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
                                    const bool& aFromUser)
 {
   if (mShutdown)
     return IPC_OK();
 
@@ -428,30 +429,29 @@ DocAccessibleParent::AddChildDoc(DocAcce
   // here.
   if (outerDoc->ChildrenCount() > 1 ||
       (outerDoc->ChildrenCount() == 1 && !outerDoc->ChildAt(0)->IsDoc())) {
     return IPC_FAIL(this, "binding to proxy that can't be a outerDoc!");
   }
 
   aChildDoc->SetParent(outerDoc);
   outerDoc->SetChildDoc(aChildDoc);
-  mChildDocs.AppendElement(aChildDoc->IProtocol::Id());
-  aChildDoc->mParentDoc = IProtocol::Id();
+  mChildDocs.AppendElement(aChildDoc->mActorID);
+  aChildDoc->mParentDoc = mActorID;
 
   if (aCreating) {
     ProxyCreated(aChildDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 DocAccessibleParent::RecvShutdown()
 {
-  MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(IProtocol::Id()));
   Destroy();
 
   auto mgr = static_cast<dom::TabParent*>(Manager());
   if (!mgr->IsDestroyed()) {
     if (!PDocAccessibleParent::Send__delete__(this)) {
       return IPC_FAIL_NO_REASON(mgr);
     }
   }
@@ -465,41 +465,70 @@ DocAccessibleParent::Destroy()
   // If we are already shutdown that is because our containing tab parent is
   // shutting down in which case we don't need to do anything.
   if (mShutdown) {
     return;
   }
 
   mShutdown = true;
 
+  MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(mActorID));
   uint32_t childDocCount = mChildDocs.Length();
   for (uint32_t i = 0; i < childDocCount; i++) {
     for (uint32_t j = i + 1; j < childDocCount; j++) {
       MOZ_DIAGNOSTIC_ASSERT(mChildDocs[i] != mChildDocs[j]);
     }
   }
 
-  for (uint32_t i = childDocCount - 1; i < childDocCount; i--)
-    ChildDocAt(i)->Destroy();
+  // XXX This indirection through the hash map of live documents shouldn't be
+  // needed, but be paranoid for now.
+  int32_t actorID = mActorID;
+  for (uint32_t i = childDocCount - 1; i < childDocCount; i--) {
+    DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
+    MOZ_ASSERT(thisDoc);
+    if (!thisDoc) {
+      return;
+    }
+
+    thisDoc->ChildDocAt(i)->Destroy();
+  }
 
   for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
     MOZ_ASSERT(iter.Get()->mProxy != this);
     ProxyDestroyed(iter.Get()->mProxy);
     iter.Remove();
   }
 
+  DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
+  MOZ_ASSERT(thisDoc);
+  if (!thisDoc) {
+    return;
+  }
+
   // The code above should have already completely cleared these, but to be
   // extra safe make sure they are cleared here.
-  mAccessibles.Clear();
-  mChildDocs.Clear();
+  thisDoc->mAccessibles.Clear();
+  thisDoc->mChildDocs.Clear();
+
+  DocManager::NotifyOfRemoteDocShutdown(thisDoc);
+  thisDoc = LiveDocs().Get(actorID);
+  MOZ_ASSERT(thisDoc);
+  if (!thisDoc) {
+    return;
+  }
 
-  DocManager::NotifyOfRemoteDocShutdown(this);
-  ProxyDestroyed(this);
-  if (DocAccessibleParent* parentDoc = ParentDoc())
-    parentDoc->RemoveChildDoc(this);
+  ProxyDestroyed(thisDoc);
+  thisDoc = LiveDocs().Get(actorID);
+  MOZ_ASSERT(thisDoc);
+  if (!thisDoc) {
+    return;
+  }
+
+  if (DocAccessibleParent* parentDoc = thisDoc->ParentDoc())
+    parentDoc->RemoveChildDoc(thisDoc);
   else if (IsTopLevel())
     GetAccService()->RemoteDocShutdown(this);
 }
 
 DocAccessibleParent*
 DocAccessibleParent::ParentDoc() const
 {
   if (mParentDoc == kNoParentDoc) {
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -28,20 +28,26 @@ class DocAccessibleParent : public Proxy
 {
 public:
   DocAccessibleParent() :
     ProxyAccessible(this), mParentDoc(kNoParentDoc),
     mTopLevel(false), mShutdown(false)
 #if defined(XP_WIN)
                                       , mEmulatedWindowHandle(nullptr)
 #endif // defined(XP_WIN)
-  { MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible); }
+  {
+    MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
+    sMaxDocID++;
+    mActorID = sMaxDocID;
+    MOZ_ASSERT(!LiveDocs().Get(mActorID));
+    LiveDocs().Put(mActorID, this);
+  }
   ~DocAccessibleParent()
   {
-    LiveDocs().Remove(IProtocol::Id());
+    LiveDocs().Remove(mActorID);
     MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
     MOZ_ASSERT(mChildDocs.Length() == 0);
     MOZ_ASSERT(!ParentDoc());
   }
 
   void SetTopLevel() { mTopLevel = true; }
   bool IsTopLevel() const { return mTopLevel; }
 
@@ -54,21 +60,16 @@ public:
    */
   void MarkAsShutdown()
   {
     MOZ_ASSERT(mChildDocs.IsEmpty());
     MOZ_ASSERT(mAccessibles.Count() == 0);
     mShutdown = true;
   }
 
-  /**
-   * Add this document to the set of tracked documents.
-   */
-  void AddToMap() { LiveDocs().Put(IProtocol::Id(), this); }
-
   /*
    * Called when a message from a document in a child process notifies the main
    * process it is firing an event.
    */
   virtual mozilla::ipc::IPCResult RecvEvent(const uint64_t& aID, const uint32_t& aType)
     override;
 
   virtual mozilla::ipc::IPCResult RecvShowEvent(const ShowEventData& aData, const bool& aFromUser)
@@ -113,17 +114,17 @@ public:
       Destroy();
   }
 
   /*
    * Return the main processes representation of the parent document (if any)
    * of the document this object represents.
    */
   DocAccessibleParent* ParentDoc() const;
-  static const int32_t kNoParentDoc = INT32_MIN;
+  static const uint64_t kNoParentDoc = UINT64_MAX;
 
   /*
    * Called when a document in a content process notifies the main process of a
    * new child document.
    */
   ipc::IPCResult AddChildDoc(DocAccessibleParent* aChildDoc,
                              uint64_t aParentID, bool aCreating = true);
 
@@ -133,17 +134,17 @@ public:
    */
   void RemoveChildDoc(DocAccessibleParent* aChildDoc)
   {
     ProxyAccessible* parent = aChildDoc->Parent();
     MOZ_ASSERT(parent);
     if (parent) {
       aChildDoc->Parent()->ClearChildDoc(aChildDoc);
     }
-    DebugOnly<bool> result = mChildDocs.RemoveElement(aChildDoc->IProtocol::Id());
+    DebugOnly<bool> result = mChildDocs.RemoveElement(aChildDoc->mActorID);
     aChildDoc->mParentDoc = kNoParentDoc;
     MOZ_ASSERT(result);
     MOZ_ASSERT(aChildDoc->mChildDocs.Length() == 0);
   }
 
   void RemoveAccessible(ProxyAccessible* aAccessible)
   {
     MOZ_DIAGNOSTIC_ASSERT(mAccessibles.GetEntry(aAccessible->ID()));
@@ -212,32 +213,34 @@ private:
   };
 
   uint32_t AddSubtree(ProxyAccessible* aParent,
                       const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
                       uint32_t aIdxInParent);
   MOZ_MUST_USE bool CheckDocTree() const;
   xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy);
 
-  nsTArray<int32_t> mChildDocs;
-  int32_t mParentDoc;
+  nsTArray<uint64_t> mChildDocs;
+  uint64_t mParentDoc;
 
 #if defined(XP_WIN)
   // The handle associated with the emulated window that contains this document
   HWND mEmulatedWindowHandle;
 #endif
 
   /*
    * Conceptually this is a map from IDs to proxies, but we store the ID in the
    * proxy object so we can't use a real map.
    */
   nsTHashtable<ProxyEntry> mAccessibles;
+  uint64_t mActorID;
   bool mTopLevel;
   bool mShutdown;
 
+  static uint64_t sMaxDocID;
   static nsDataHashtable<nsUint64HashKey, DocAccessibleParent*>&
     LiveDocs()
     {
       static nsDataHashtable<nsUint64HashKey, DocAccessibleParent*> sLiveDocs;
       return sLiveDocs;
     }
 };
 
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -860,24 +860,16 @@
     <emItem blockID="i532" id="249911bc-d1bd-4d66-8c17-df533609e6d8@c76f3de9-939e-4922-b73c-5d7a3139375d.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i65" id="activity@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i656" id="hdv@vovcacik.addons.mozilla.org">
-      <prefs/>
-      <versionRange minVersion="102.0" maxVersion="102.0" severity="3"/>
-    </emItem>
-    <emItem blockID="i722" id="{9802047e-5a84-4da3-b103-c55995d147d1}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i228" id="crossriderapp5060@crossrider.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i470" id="extension@FastFreeConverter.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
@@ -1715,16 +1707,24 @@
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i720" id="FXqG@xeeR.net">
       <prefs>
         <pref>browser.startup.homepage</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="d33f6d48-a555-49dd-96ff-8d75473403a8" id="mozilla_cc2@internetdownloadmanager.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="6.26.11" severity="1">
+        <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+          <versionRange maxVersion="*" minVersion="53.0a1"/>
+        </targetApplication>
+      </versionRange>
+    </emItem>
     <emItem blockID="i493" id="12x3q@3244516.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1265" id="@video_downloader_pro">
       <prefs/>
       <versionRange minVersion="1.2.1" maxVersion="1.2.5" severity="1"/>
     </emItem>
@@ -2030,16 +2030,24 @@
           <versionRange maxVersion="9.*" minVersion="9.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
     <emItem blockID="i1264" id="suchpony@suchpony.de">
       <prefs/>
       <versionRange minVersion="0" maxVersion="1.6.7" severity="3"/>
     </emItem>
+    <emItem blockID="i656" id="hdv@vovcacik.addons.mozilla.org">
+      <prefs/>
+      <versionRange minVersion="102.0" maxVersion="102.0" severity="3"/>
+    </emItem>
+    <emItem blockID="i722" id="{9802047e-5a84-4da3-b103-c55995d147d1}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
   </emItems>
   <pluginItems>
     <pluginItem blockID="p416">
       <match exp="JavaAppletPlugin\.plugin" name="filename"/>
       <versionRange maxVersion="Java 6 Update 45" minVersion="Java 6 Update 42" severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="17.0"/>
         </targetApplication>
@@ -2271,22 +2279,22 @@
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="17.0.*" minVersion="17.0.4"/>
         </targetApplication>
       </versionRange>
     </pluginItem>
     <pluginItem blockID="3f136e56-4c93-4619-8c0d-d86258c1065d">
       <match exp="(nppdf32\.dll)|(AdobePDFViewerNPAPI\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/reader/</infoURL>
-      <versionRange maxVersion="15.006.30244" minVersion="15.006" severity="1" vulnerabilitystatus="1"/>
+      <versionRange maxVersion="15.006.30244" minVersion="15.006" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="43b45ad8-a373-42c1-89c6-64e2746885e5">
       <match exp="(nppdf32\.dll)|(AdobePDFViewerNPAPI\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/reader/</infoURL>
-      <versionRange maxVersion="15.020.20042" minVersion="15.020" severity="1" vulnerabilitystatus="1"/>
+      <versionRange maxVersion="15.020.20042" minVersion="15.020" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p366">
       <match exp="Scorch\.plugin" name="filename"/>
       <versionRange maxVersion="6.2.0" minVersion="6.2.0" severity="1"/>
     </pluginItem>
     <pluginItem blockID="p936" os="Linux">
       <match exp="libflashplayer\.so" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
@@ -2361,17 +2369,17 @@
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="17.0"/>
         </targetApplication>
       </versionRange>
     </pluginItem>
     <pluginItem blockID="59c31ade-88d6-4b22-8601-5316f82e3977">
       <match exp="(nppdf32\.dll)|(AdobePDFViewerNPAPI\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/reader/</infoURL>
-      <versionRange maxVersion="11.0.18" minVersion="11.0" severity="1" vulnerabilitystatus="1"/>
+      <versionRange maxVersion="11.0.18" minVersion="11.0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p1004">
       <match exp="Unity Web Player\.plugin" name="filename"/>
       <match exp="^($|Unity Web Player version 5.0(\.([0-2]|3f1))?[^0-9.])" name="description"/>
       <versionRange severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p459">
       <match exp="JavaAppletPlugin\.plugin" name="filename"/>
@@ -3011,16 +3019,21 @@
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="18.0.0.254" minVersion="18.0.0.233" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p828">
       <match exp="(NPSWF32.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="16.0.0.287" minVersion="15.0.0.243" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
+    <pluginItem blockID="e939e3f9-cb55-494d-b95a-c5ac82bd8d3d" os="Linux">
+      <match exp="libflashplayer\.so" name="filename"/>
+      <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+      <versionRange maxVersion="24.0.0.194" minVersion="24.0.0.186" severity="0" vulnerabilitystatus="1"/>
+    </pluginItem>
     <pluginItem blockID="p113">
       <match exp="npuplaypc\.dll" name="filename"/>
       <versionRange maxVersion="1.0.0.0" minVersion="0" severity="1"/>
     </pluginItem>
     <pluginItem blockID="p1247">
       <match exp="(nppdf32\.dll)|(AdobePDFViewerNPAPI\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/reader</infoURL>
       <versionRange maxVersion="15.006.30174" minVersion="15.006.30174" severity="0" vulnerabilitystatus="1"/>
@@ -3086,16 +3099,21 @@
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="20.0.0.235" minVersion="19.0.0.246" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p1150" os="Linux">
       <match exp="libflashplayer\.so" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="11.2.202.577" minVersion="11.2.202.569" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
+    <pluginItem blockID="f77960ca-28f3-4664-994d-2b713d2a1434">
+      <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
+      <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+      <versionRange maxVersion="24.0.0.194" minVersion="24.0.0.186" severity="0" vulnerabilitystatus="1"/>
+    </pluginItem>
     <pluginItem blockID="p1138" os="Linux">
       <match exp="libflashplayer\.so" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="11.2.202.569" minVersion="11.2.202.559" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p1224" os="Linux">
       <match exp="libflashplayer\.so" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -16,17 +16,16 @@
 #endif
 
 #include <stdio.h>
 #include <stdarg.h>
 #include <time.h>
 
 #include "nsCOMPtr.h"
 #include "nsIFile.h"
-#include "nsStringGlue.h"
 
 #ifdef XP_WIN
 #ifdef MOZ_ASAN
 // ASAN requires firefox.exe to be built with -MD, and it's OK if we don't
 // support Windows XP SP2 in ASAN builds.
 #define XRE_DONT_SUPPORT_XPSP2
 #endif
 #define XRE_WANT_ENVIRON
@@ -35,17 +34,17 @@
 #include "mozilla/sandboxing/SandboxInitialization.h"
 #endif
 #endif
 #include "BinaryPath.h"
 
 #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL
 
 #include "mozilla/Sprintf.h"
-#include "mozilla/Telemetry.h"
+#include "mozilla/StartupTimeline.h"
 #include "mozilla/WindowsDllBlocklist.h"
 
 #ifdef LIBFUZZER
 #include "FuzzerDefs.h"
 #endif
 
 #ifdef MOZ_LINUX_32_SSE2_STARTUP_ERROR
 #include <cpuid.h>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1524,17 +1524,21 @@ pref("toolkit.pageThumbs.minHeight", 190
 
 // Enable speech synthesis
 pref("media.webspeech.synth.enabled", true);
 
 pref("browser.esedbreader.loglevel", "Error");
 
 pref("browser.laterrun.enabled", false);
 
+#ifdef EARLY_BETA_OR_EARLIER
+pref("browser.migrate.automigrate.enabled", true);
+#else
 pref("browser.migrate.automigrate.enabled", false);
+#endif
 // 4 here means the suggestion notification will be automatically
 // hidden the 4th day, so it will actually be shown on 3 different days.
 pref("browser.migrate.automigrate.daysToOfferUndo", 4);
 pref("browser.migrate.automigrate.ui.enabled", true);
 
 // See comments in bug 1340115 on how we got to these numbers.
 pref("browser.migrate.chrome.history.limit", 2000);
 pref("browser.migrate.chrome.history.maxAgeInDays", 180);
deleted file mode 100644
index 83af78d6c48476cbf19f517c4967642fcdcc42fd..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -784,86 +784,8 @@ var LightWeightThemeWebInstaller = {
     if (!uri.schemeIs("https")) {
       return false;
     }
 
     let pm = Services.perms;
     return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
   }
 };
-
-/*
- * Listen for Lightweight Theme styling changes and update the browser's theme accordingly.
- */
-var LightweightThemeListener = {
-  _modifiedStyles: [],
-
-  init() {
-    XPCOMUtils.defineLazyGetter(this, "styleSheet", function() {
-      for (let i = document.styleSheets.length - 1; i >= 0; i--) {
-        let sheet = document.styleSheets[i];
-        if (sheet.href == "chrome://browser/skin/browser-lightweightTheme.css")
-          return sheet;
-      }
-      return undefined;
-    });
-
-    Services.obs.addObserver(this, "lightweight-theme-styling-update", false);
-    Services.obs.addObserver(this, "lightweight-theme-optimized", false);
-    if (document.documentElement.hasAttribute("lwtheme"))
-      this.updateStyleSheet(document.documentElement.style.backgroundImage);
-  },
-
-  uninit() {
-    Services.obs.removeObserver(this, "lightweight-theme-styling-update");
-    Services.obs.removeObserver(this, "lightweight-theme-optimized");
-  },
-
-  /**
-   * Append the headerImage to the background-image property of all rulesets in
-   * browser-lightweightTheme.css.
-   *
-   * @param headerImage - a string containing a CSS image for the lightweight theme header.
-   */
-  updateStyleSheet(headerImage) {
-    if (!this.styleSheet)
-      return;
-    this.substituteRules(this.styleSheet.cssRules, headerImage);
-  },
-
-  substituteRules(ruleList, headerImage, existingStyleRulesModified = 0) {
-    let styleRulesModified = 0;
-    for (let i = 0; i < ruleList.length; i++) {
-      let rule = ruleList[i];
-      if (rule instanceof Ci.nsIDOMCSSGroupingRule) {
-        // Add the number of modified sub-rules to the modified count
-        styleRulesModified += this.substituteRules(rule.cssRules, headerImage, existingStyleRulesModified + styleRulesModified);
-      } else if (rule instanceof Ci.nsIDOMCSSStyleRule) {
-        if (!rule.style.backgroundImage)
-          continue;
-        let modifiedIndex = existingStyleRulesModified + styleRulesModified;
-        if (!this._modifiedStyles[modifiedIndex])
-          this._modifiedStyles[modifiedIndex] = { backgroundImage: rule.style.backgroundImage };
-
-        rule.style.backgroundImage = this._modifiedStyles[modifiedIndex].backgroundImage + ", " + headerImage;
-        styleRulesModified++;
-      } else {
-        Cu.reportError("Unsupported rule encountered");
-      }
-    }
-    return styleRulesModified;
-  },
-
-  // nsIObserver
-  observe(aSubject, aTopic, aData) {
-    if ((aTopic != "lightweight-theme-styling-update" && aTopic != "lightweight-theme-optimized") ||
-          !this.styleSheet)
-      return;
-
-    if (aTopic == "lightweight-theme-optimized" && aSubject != window)
-      return;
-
-    let themeData = JSON.parse(aData);
-    if (!themeData)
-      return;
-    this.updateStyleSheet("url(" + themeData.headerURL + ")");
-  },
-};
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -6,16 +6,25 @@
 @namespace html url("http://www.w3.org/1999/xhtml");
 @namespace svg url("http://www.w3.org/2000/svg");
 
 :root {
   --identity-popup-expander-width: 38px;
   --panelui-subview-transition-duration: 150ms;
 }
 
+:root:-moz-lwtheme {
+  color: var(--lwt-text-color) !important;
+}
+
+:root:-moz-lwtheme:not([customization-lwtheme]) {
+  background-color: var(--lwt-accent-color) !important;
+  background-image: var(--lwt-header-image) !important;
+}
+
 #main-window:not([chromehidden~="toolbar"]) {
 %ifdef XP_MACOSX
   min-width: 335px;
 %else
   min-width: 300px;
 %endif
 }
 
@@ -440,16 +449,18 @@ toolbar:not(#TabsToolbar) > #personal-bo
 #main-window[inFullscreen="true"] {
   padding-top: 0; /* override drawintitlebar="true" */
 }
 %endif
 
 #browser-bottombox[lwthemefooter="true"] {
   background-repeat: no-repeat;
   background-position: bottom left;
+  background-color: var(--lwt-accent-color);
+  background-image: var(--lwt-header-image);
 }
 
 .menuitem-iconic-tooltip {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-iconic-tooltip");
 }
 
 /* Hide menu elements intended for keyboard access support */
 #main-menubar[openedwithkey=false] .show-only-for-keyboard {
@@ -970,20 +981,24 @@ notification[value="translation"] {
 toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > .toolbarbutton-badge-stack > image.toolbarbutton-icon {
   display: -moz-box;
 }
 
 toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > #downloads-indicator-anchor {
   display: none;
 }
 
-#downloads-button:-moz-any([progress], [counter], [paused]) #downloads-indicator-icon,
-#downloads-button:not(:-moz-any([progress], [counter], [paused]))
-                                                   #downloads-indicator-progress-area
-{
+#downloads-button.withProgressBar:-moz-any([progress], [counter], [paused]) #downloads-indicator-icon,
+#downloads-button:not(:-moz-any([progress], [counter], [paused])) #downloads-indicator-progress-area {
+  visibility: hidden;
+}
+
+/* Hide elements for another type of progressmeter if it's not in use. */
+#downloads-button.withProgressBar #downloads-indicator-progress-icon,
+#downloads-button:not(.withProgressBar) #downloads-indicator-progress-area {
   visibility: hidden;
 }
 
 /* Combobox dropdown renderer */
 #ContentSelectDropdown > menupopup {
   /* The menupopup itself should always be rendered LTR to ensure the scrollbar aligns with
    * the dropdown arrow on the dropdown widget. If a menuitem is RTL, its style will be set accordingly */
   direction: ltr;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -22,17 +22,18 @@ Cu.import("resource://gre/modules/Notifi
           GMPInstallManager:false, LightweightThemeManager:false, Log:false,
           LoginManagerParent:false, NewTabUtils:false, PageThumbs:false,
           PluralForm:false, Preferences:false, PrivateBrowsingUtils:false,
           ProcessHangMonitor:false, PromiseUtils:false, ReaderMode:false,
           ReaderParent:false, RecentWindow:false, SessionStore:false,
           ShortcutUtils:false, SimpleServiceDiscovery:false, SitePermissions:false,
           Social:false, TabCrashHandler:false, Task:false, TelemetryStopwatch:false,
           Translation:false, UITour:false, UpdateUtils:false, Weave:false,
-          fxAccounts:false, gDevTools:false, gDevToolsBrowser:false, webrtcUI:false
+          fxAccounts:false, gDevTools:false, gDevToolsBrowser:false, webrtcUI:false,
+          URLBarZoom:false
  */
 
 /**
  * IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL.
  * XXX Bug 1325373 is for making eslint detect these automatically.
  */
 [
   ["AboutHome", "resource:///modules/AboutHome.jsm"],
@@ -69,21 +70,22 @@ Cu.import("resource://gre/modules/Notifi
   ["SitePermissions", "resource:///modules/SitePermissions.jsm"],
   ["Social", "resource:///modules/Social.jsm"],
   ["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"],
   ["Task", "resource://gre/modules/Task.jsm"],
   ["TelemetryStopwatch", "resource://gre/modules/TelemetryStopwatch.jsm"],
   ["Translation", "resource:///modules/translation/Translation.jsm"],
   ["UITour", "resource:///modules/UITour.jsm"],
   ["UpdateUtils", "resource://gre/modules/UpdateUtils.jsm"],
+  ["URLBarZoom", "resource:///modules/URLBarZoom.jsm"],
   ["Weave", "resource://services-sync/main.js"],
   ["fxAccounts", "resource://gre/modules/FxAccounts.jsm"],
   ["gDevTools", "resource://devtools/client/framework/gDevTools.jsm"],
   ["gDevToolsBrowser", "resource://devtools/client/framework/gDevTools.jsm"],
-  ["webrtcUI", "resource:///modules/webrtcUI.jsm", ]
+  ["webrtcUI", "resource:///modules/webrtcUI.jsm"],
 ].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource));
 
 XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
   "resource://gre/modules/SafeBrowsing.jsm");
 
 if (AppConstants.MOZ_CRASHREPORTER) {
   XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
     "resource:///modules/ContentCrashHandlers.jsm");
@@ -1127,16 +1129,17 @@ var gBrowserInit = {
     LanguageDetectionListener.init();
     BrowserOnClick.init();
     FeedHandler.init();
     CompactTheme.init();
     AboutPrivateBrowsingListener.init();
     TrackingProtection.init();
     RefreshBlocker.init();
     CaptivePortalWatcher.init();
+    URLBarZoom.init(window);
 
     let mm = window.getGroupMessageManager("browsers");
     mm.loadFrameScript("chrome://browser/content/tab-content.js", true);
     mm.loadFrameScript("chrome://browser/content/content.js", true);
     mm.loadFrameScript("chrome://browser/content/content-UITour.js", true);
     mm.loadFrameScript("chrome://global/content/manifestMessages.js", true);
 
     // initialize observers and listeners
@@ -1362,17 +1365,16 @@ var gBrowserInit = {
     if (AppConstants.E10S_TESTING_ONLY)
       gRemoteTabsUI.init();
 
     // Initialize the full zoom setting.
     // We do this before the session restore service gets initialized so we can
     // apply full zoom settings to tabs restored by the session restore service.
     FullZoom.init();
     PanelUI.init();
-    LightweightThemeListener.init();
 
     UpdateUrlbarSearchSplitterState();
 
     if (!(isBlankPageURL(uriToLoad) || uriToLoad == "about:privatebrowsing") ||
         !focusAndSelectUrlBar()) {
       if (gBrowser.selectedBrowser.isRemoteBrowser) {
         // If the initial browser is remote, in order to optimize for first paint,
         // we'll defer switching focus to that browser until it has painted.
@@ -1699,17 +1701,16 @@ var gBrowserInit = {
       }
 
       if (this.gmpInstallManager) {
         this.gmpInstallManager.uninit();
       }
 
       BrowserOffline.uninit();
       IndexedDBPromptHelper.uninit();
-      LightweightThemeListener.uninit();
       PanelUI.uninit();
       AutoShowBookmarksToolbar.uninit();
     }
 
     // Final window teardown, do this last.
     window.XULBrowserWindow = null;
     window.QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIWebNavigation)
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -8,17 +8,16 @@
 
 <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/devtools-browser.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/controlcenter/panel.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/customizableui/panelUI.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/browser-lightweightTheme.css" type="text/css"?>
 
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
 
 # All DTD information is stored in a separate file so that it can be shared by
 # hiddenWindow.xul.
 #include browser-doctype.inc
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -567,22 +567,22 @@ body.compact #newtab-search-container {
 
 .newtab-customize-panel-item,
 .newtab-customize-complex-option {
   display: block;
   text-align: start;
   background-color: #F9F9F9;
 }
 
-.newtab-customize-panel-item[selected]:-moz-locale-dir(rtl) {
+.newtab-customize-panel-item[selected]:dir(rtl){
   background-position: right 15px center;
 }
 
-.newtab-customize-complex-option:hover > .selectable:not([selected]):-moz-locale-dir(rtl),
-.selectable:not([selected]):hover:-moz-locale-dir(rtl) {
+.newtab-customize-complex-option:hover > .selectable:not([selected]):dir(rtl),
+.selectable:not([selected]):hover:dir(rtl) {
   background-position: right 15px center;
 }
 
 .newtab-customize-panel-item:not([selected]),
 .newtab-customize-panel-subitem:not([selected]){
   color: #7A7A7A;
 }
 
--- a/browser/base/content/test/general/browser_invalid_uri_back_forward_manipulation.js
+++ b/browser/base/content/test/general/browser_invalid_uri_back_forward_manipulation.js
@@ -5,21 +5,22 @@
  * Verify that loading an invalid URI does not clobber a previously-loaded page's history
  * entry, but that the invalid URI gets its own history entry instead. We're checking this
  * using nsIWebNavigation's canGoBack, as well as actually going back and then checking
  * canGoForward.
  */
 add_task(function* checkBackFromInvalidURI() {
   yield pushPrefs(["keyword.enabled", false]);
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots", true);
+  info("Loaded about:robots");
+
   gURLBar.value = "::2600";
-  gURLBar.focus();
 
   let promiseErrorPageLoaded = BrowserTestUtils.waitForErrorPage(tab.linkedBrowser);
-  EventUtils.synthesizeKey("VK_RETURN", {});
+  gURLBar.handleCommand();
   yield promiseErrorPageLoaded;
 
   ok(gBrowser.webNavigation.canGoBack, "Should be able to go back");
   if (gBrowser.webNavigation.canGoBack) {
     // Can't use DOMContentLoaded here because the page is bfcached. Can't use pageshow for
     // the error page because it doesn't seem to fire for those.
     let promiseOtherPageLoaded = BrowserTestUtils.waitForEvent(tab.linkedBrowser, "pageshow", false,
       // Be paranoid we *are* actually seeing this other page load, not some kind of race
--- a/browser/base/content/test/urlbar/browser_canonizeURL.js
+++ b/browser/base/content/test/urlbar/browser_canonizeURL.js
@@ -1,34 +1,40 @@
-var pairs = [
-  ["example", "http://www.example.net/"],
-  ["ex-ample", "http://www.ex-ample.net/"],
-  ["  example ", "http://www.example.net/"],
-  [" example/foo ", "http://www.example.net/foo"],
-  [" example/foo bar ", "http://www.example.net/foo%20bar"],
-  ["example.net", "http://example.net/"],
-  ["http://example", "http://example/"],
-  ["example:8080", "http://example:8080/"],
-  ["ex-ample.foo", "http://ex-ample.foo/"],
-  ["example.foo/bar ", "http://example.foo/bar"],
-  ["1.1.1.1", "http://1.1.1.1/"],
-  ["ftp://example", "ftp://example/"],
-  ["ftp.example.bar", "http://ftp.example.bar/"],
-  ["ex ample", Services.search.defaultEngine.getSubmission("ex ample", null, "keyword").uri.spec],
-];
+add_task(function*() {
+  let testcases = [
+    ["example", "http://www.example.net/", { shiftKey: true }],
+    // Check that a direct load is not overwritten by a previous canonization.
+    ["http://example.com/test/", "http://example.com/test/", {}],
+    ["ex-ample", "http://www.ex-ample.net/", { shiftKey: true }],
+    ["  example ", "http://www.example.net/", { shiftKey: true }],
+    [" example/foo ", "http://www.example.net/foo", { shiftKey: true }],
+    [" example/foo bar ", "http://www.example.net/foo%20bar", { shiftKey: true }],
+    ["example.net", "http://example.net/", { shiftKey: true }],
+    ["http://example", "http://example/", { shiftKey: true }],
+    ["example:8080", "http://example:8080/", { shiftKey: true }],
+    ["ex-ample.foo", "http://ex-ample.foo/", { shiftKey: true }],
+    ["example.foo/bar ", "http://example.foo/bar", { shiftKey: true }],
+    ["1.1.1.1", "http://1.1.1.1/", { shiftKey: true }],
+    ["ftp://example", "ftp://example/", { shiftKey: true }],
+    ["ftp.example.bar", "http://ftp.example.bar/", { shiftKey: true }],
+    ["ex ample", Services.search.defaultEngine.getSubmission("ex ample", null, "keyword").uri.spec, { shiftKey: true }],
+  ];
 
-add_task(function*() {
   // Disable autoFill for this test, since it could mess up the results.
   let autoFill = Preferences.get("browser.urlbar.autoFill");
   Preferences.set("browser.urlbar.autoFill", false);
   registerCleanupFunction(() => {
     Preferences.set("browser.urlbar.autoFill", autoFill);
   });
 
-  for (let [inputValue, expectedURL] of pairs) {
+  for (let [inputValue, expectedURL, options] of testcases) {
     let promiseLoad = waitForDocLoadAndStopIt(expectedURL);
     gURLBar.focus();
-    gURLBar.inputField.value = inputValue.slice(0, -1);
-    EventUtils.synthesizeKey(inputValue.slice(-1), {});
-    EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true });
+    if (Object.keys(options).length > 0) {
+      gURLBar.inputField.value = inputValue.slice(0, -1);
+      EventUtils.synthesizeKey(inputValue.slice(-1), {});
+    } else {
+      gURLBar.textValue = inputValue;
+    }
+    EventUtils.synthesizeKey("VK_RETURN", options);
     yield promiseLoad;
   }
 });
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1148,16 +1148,17 @@ file, You can obtain one at http://mozil
             searchString: this.mController.searchString,
             event
           };
 
           if (this.popup.selectedIndex != 0 || this.gotResultForCurrentQuery) {
             this.maybeCanonizeURL(event, this.value);
             let rv = this.mController.handleEnter(false, event);
             this.handleEnterInstance = null;
+            this.popup.overrideValue = null;
             return rv;
           }
 
           return true;
         ]]></body>
       </method>
 
       <method name="handleDelete">
@@ -1827,16 +1828,17 @@ file, You can obtain one at http://mozil
               // Don't handle this immediately or we could cause a recursive
               // loop where the controller sets popupOpen and re-enters here.
               setTimeout(() => {
                 // Safety check: handle only if the search string didn't change.
                 let { event, searchString } = instance;
                 if (this.input.mController.searchString == searchString) {
                   this.input.maybeCanonizeURL(event, searchString);
                   this.input.mController.handleEnter(false, event);
+                  this.overrideValue = null;
                 }
               }, 0);
             }
           ]]>
         </body>
       </method>
 
       <method name="_onSearchBegin">
@@ -1848,17 +1850,16 @@ file, You can obtain one at http://mozil
           // 1. if a search starts we set selectedIndex to 0 here, and it will
           //    be updated by onResultsAdded. Since selectedIndex is 0,
           //    handleEnter will delay the action if a result didn't arrive yet.
           // 2. if a search doesn't start (for example if autocomplete is
           //    disabled), this won't be called, and the selectedIndex will be
           //    the default -1 value. Then handleEnter will know it should not
           //    delay the action, cause a result wont't ever arrive.
           this.input.controller.setInitiallySelectedIndex(0);
-          this.overrideValue = null;
         ]]></body>
       </method>
 
       <field name="_addonIframe">null</field>
       <field name="_addonIframeOwner">null</field>
       <field name="_addonIframeOverriddenFunctionsByName">{}</field>
 
       <!-- These methods must be overridden and properly handled by the API
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -47,17 +47,16 @@ browser.jar:
         content/browser/abouthealthreport/abouthealth.js      (content/abouthealthreport/abouthealth.js)
         content/browser/abouthealthreport/abouthealth.css     (content/abouthealthreport/abouthealth.css)
 #endif
         content/browser/aboutaccounts/aboutaccounts.xhtml                     (content/aboutaccounts/aboutaccounts.xhtml)
         content/browser/aboutaccounts/aboutaccounts.js                        (content/aboutaccounts/aboutaccounts.js)
         content/browser/aboutaccounts/aboutaccounts.css                       (content/aboutaccounts/aboutaccounts.css)
         content/browser/aboutaccounts/main.css                                (content/aboutaccounts/main.css)
         content/browser/aboutaccounts/normalize.css                           (content/aboutaccounts/normalize.css)
-        content/browser/aboutaccounts/images/fox.png                          (content/aboutaccounts/images/fox.png)
 
 
         content/browser/aboutRobots-icon.png          (content/aboutRobots-icon.png)
         content/browser/aboutRobots-widget-left.png   (content/aboutRobots-widget-left.png)
         content/browser/aboutSocialError.xhtml        (content/aboutSocialError.xhtml)
         content/browser/aboutProviderDirectory.xhtml  (content/aboutProviderDirectory.xhtml)
         content/browser/aboutTabCrashed.css           (content/aboutTabCrashed.css)
         content/browser/aboutTabCrashed.js            (content/aboutTabCrashed.js)
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -442,17 +442,17 @@ CustomizeMode.prototype = {
     undoResetButton.hidden = resetButton.disabled = true;
 
     this._transitioning = true;
 
     Task.spawn(function*() {
       yield this.depopulatePalette();
 
       yield this._doTransition(false);
-      this.removeLWTStyling();
+      this.updateLWTStyling({});
 
       Services.obs.removeObserver(this, "lightweight-theme-window-updated");
 
       if (this.browser.selectedTab == gTab) {
         if (gTab.linkedBrowser.currentURI.spec == "about:blank") {
           closeGlobalTab();
         } else {
           unregisterGlobalTab();
@@ -606,78 +606,26 @@ CustomizeMode.prototype = {
   updateLWTStyling(aData) {
     let docElement = this.document.documentElement;
     if (!aData) {
       let lwt = docElement._lightweightTheme;
       aData = lwt.getData();
     }
     let headerURL = aData && aData.headerURL;
     if (!headerURL) {
-      this.removeLWTStyling();
+      docElement.removeAttribute("customization-lwtheme");
       return;
     }
+    docElement.setAttribute("customization-lwtheme", "true");
 
     let deck = this.document.getElementById("tab-view-deck");
-    let headerImageRef = this._getHeaderImageRef(aData);
-    docElement.setAttribute("customization-lwtheme", "true");
-
     let toolboxRect = this.window.gNavToolbox.getBoundingClientRect();
     let height = toolboxRect.bottom;
-
-    if (AppConstants.platform == "macosx") {
-      let drawingInTitlebar = !docElement.hasAttribute("drawtitle");
-      let titlebar = this.document.getElementById("titlebar");
-      if (drawingInTitlebar) {
-        titlebar.style.backgroundImage = headerImageRef;
-      } else {
-        titlebar.style.removeProperty("background-image");
-      }
-    }
-
-    let limitedBG = "-moz-image-rect(" + headerImageRef + ", 0, 100%, " +
-                    height + ", 0)";
-
-    let ridgeStart = height - 1;
-    let ridgeCenter = (ridgeStart + 1) + "px";
-    let ridgeEnd = (ridgeStart + 2) + "px";
-    ridgeStart = ridgeStart + "px";
-
-    let ridge = "linear-gradient(to bottom, " +
-                                 "transparent " + ridgeStart +
-                                 ", rgba(0,0,0,0.25) " + ridgeStart +
-                                 ", rgba(0,0,0,0.25) " + ridgeCenter +
-                                 ", rgba(255,255,255,0.5) " + ridgeCenter +
-                                 ", rgba(255,255,255,0.5) " + ridgeEnd + ", " +
-                                 "transparent " + ridgeEnd + ")";
-    deck.style.backgroundImage = ridge + ", " + limitedBG;
-
-    /* Remove the background styles from the <window> so we can style it instead. */
-    docElement.style.removeProperty("background-image");
-    docElement.style.removeProperty("background-color");
-  },
-
-  removeLWTStyling() {
-    let affectedNodes = AppConstants.platform == "macosx" ?
-                          ["tab-view-deck", "titlebar"] :
-                          ["tab-view-deck"];
-    for (let id of affectedNodes) {
-      let node = this.document.getElementById(id);
-      node.style.removeProperty("background-image");
-    }
-    let docElement = this.document.documentElement;
-    docElement.removeAttribute("customization-lwtheme");
-    let data = docElement._lightweightTheme.getData();
-    if (data && data.headerURL) {
-      docElement.style.backgroundImage = this._getHeaderImageRef(data);
-      docElement.style.backgroundColor = data.accentcolor || "white";
-    }
-  },
-
-  _getHeaderImageRef(aData) {
-    return "url(\"" + aData.headerURL.replace(/"/g, '\\"') + "\")";
+    deck.style.setProperty("--toolbox-rect-height", `${height}`);
+    deck.style.setProperty("--toolbox-rect-height-with-unit", `${height}px`);
   },
 
   maybeShowTip(aAnchor) {
     let shown = false;
     const kShownPref = "browser.customizemode.tip0.shown";
     try {
       shown = Services.prefs.getBoolPref(kShownPref);
     } catch (ex) {}
@@ -1539,21 +1487,17 @@ CustomizeMode.prototype = {
         this._updateUndoResetButton();
         if (AppConstants.CAN_DRAW_IN_TITLEBAR) {
           this._updateTitlebarButton();
         }
         break;
       case "lightweight-theme-window-updated":
         if (aSubject == this.window) {
           aData = JSON.parse(aData);
-          if (!aData) {
-            this.removeLWTStyling();
-          } else {
-            this.updateLWTStyling(aData);
-          }
+          this.updateLWTStyling(aData);
         }
         break;
     }
   },
 
   _updateTitlebarButton() {
     if (!AppConstants.CAN_DRAW_IN_TITLEBAR) {
       return;
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -142,16 +142,23 @@ PrefObserver.register({
  */
 this.DownloadsCommon = {
   ATTENTION_NONE: "",
   ATTENTION_SUCCESS: "success",
   ATTENTION_WARNING: "warning",
   ATTENTION_SEVERE: "severe",
 
   /**
+   * This can be used by add-on experiments as a killswitch for the new style
+   * progress indication. This will be removed in bug 1329109 after the new
+   * indicator is released.
+   **/
+  arrowStyledIndicator: true,
+
+  /**
    * Returns an object whose keys are the string names from the downloads string
    * bundle, and whose values are either the translated strings or functions
    * returning formatted strings.
    */
   get strings() {
     let strings = {};
     let sb = Services.strings.createBundle(kDownloadsStringBundleUrl);
     let enumerator = sb.getSimpleEnumeration();
--- a/browser/components/downloads/content/indicator.js
+++ b/browser/components/downloads/content/indicator.js
@@ -212,20 +212,31 @@ const DownloadsIndicatorView = {
    * Prepares the downloads indicator to be displayed.
    */
   ensureInitialized() {
     if (this._initialized) {
       return;
     }
     this._initialized = true;
 
+    this._setIndicatorType();
     window.addEventListener("unload", this.onWindowUnload);
     DownloadsCommon.getIndicatorData(window).addView(this);
   },
 
+  _setIndicatorType() {
+    // We keep a killerswitch for old-styled progressbar for now. Corresponding
+    // css class is added here to reflect the type chosen for showing progress.
+    let node = CustomizableUI.getWidget("downloads-button")
+                             .forWindow(window).node;
+
+    node.classList.toggle("withProgressBar",
+                          !DownloadsCommon.arrowStyledIndicator);
+  },
+
   /**
    * Frees the internal resources related to the indicator.
    */
   ensureTerminated() {
     if (!this._initialized) {
       return;
     }
     this._initialized = false;
@@ -406,31 +417,50 @@ const DownloadsIndicatorView = {
       // XBL binding isn't applied if the element is invisible for any reason.
       this._indicatorCounter.setAttribute("value", aValue);
     }
     return aValue;
   },
   _counter: null,
 
   /**
-   * Progress indication to display, from 0 to 100, or -1 if unknown.  The
-   * progress bar is hidden if the current progress is unknown and no status
-   * text is set in the "counter" property.
+   * Progress indication to display, from 0 to 100, or -1 if unknown.
+   * Bar-type:
+   *   The progress bar is hidden if the current progress is unknown and no
+   *   status text is set in the "counter" property.
+   * Arrow-type:
+   *   progress is not visible if the current progress is unknown.
    */
   set percentComplete(aValue) {
+    // For arrow type only:
+    // We show portion of the success icon in propotional with the download
+    // progress as an indicator. the PROGRESS_ICON_EMPTY_HEIGHT_PERCENT and
+    // PROGRESS_ICON_FULL_HEIGHT_PERCENT correspond to how much portion of the
+    // icon should be displayed in 0% and 100%.
+    const PROGRESS_ICON_EMPTY_HEIGHT_PERCENT = 35;
+    const PROGRESS_ICON_FULL_HEIGHT_PERCENT = 87;
+
     if (!this._operational) {
       return this._percentComplete;
     }
 
     if (this._percentComplete !== aValue) {
       this._percentComplete = aValue;
-      if (this._percentComplete >= 0)
+      this._refreshAttention();
+
+      if (this._percentComplete >= 0) {
         this.indicator.setAttribute("progress", "true");
-      else
+        this._progressIcon.style.height = (this._percentComplete *
+          (PROGRESS_ICON_FULL_HEIGHT_PERCENT -
+           PROGRESS_ICON_EMPTY_HEIGHT_PERCENT) / 100 +
+           PROGRESS_ICON_EMPTY_HEIGHT_PERCENT) + "%";
+      } else {
         this.indicator.removeAttribute("progress");
+        this._progressIcon.style.height = "0";
+      }
       // We have to set the attribute instead of using the property because the
       // XBL binding isn't applied if the element is invisible for any reason.
       this._indicatorProgress.setAttribute("value", Math.max(aValue, 0));
     }
     return aValue;
   },
   _percentComplete: null,
 
@@ -458,39 +488,49 @@ const DownloadsIndicatorView = {
 
   /**
    * Set when the indicator should draw user attention to itself.
    */
   set attention(aValue) {
     if (!this._operational) {
       return this._attention;
     }
-
     if (this._attention != aValue) {
       this._attention = aValue;
+      this._refreshAttention();
+    }
+    return this._attention;
+  },
 
-      // Check if the downloads button is in the menu panel, to determine which
-      // button needs to get a badge.
-      let widgetGroup = CustomizableUI.getWidget("downloads-button");
-      let inMenu = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
+  _refreshAttention() {
+    // Check if the downloads button is in the menu panel, to determine which
+    // button needs to get a badge.
+    let widgetGroup = CustomizableUI.getWidget("downloads-button");
+    let inMenu = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
 
-      if (aValue == DownloadsCommon.ATTENTION_NONE) {
-        this.indicator.removeAttribute("attention");
-        if (inMenu) {
-          gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_DOWNLOAD);
-        }
-      } else {
-        this.indicator.setAttribute("attention", aValue);
-        if (inMenu) {
-          let badgeClass = "download-" + aValue;
-          gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_DOWNLOAD, badgeClass);
-        }
+    // For arrow-Styled indicator, suppress success attention if we have
+    // progress in toolbar
+    let suppressAttention = DownloadsCommon.arrowStyledIndicator && !inMenu &&
+      this._attention == DownloadsCommon.ATTENTION_SUCCESS &&
+      this._percentComplete >= 0;
+
+    if (suppressAttention || this._attention == DownloadsCommon.ATTENTION_NONE) {
+      this.indicator.removeAttribute("attention");
+      if (inMenu) {
+        gMenuButtonBadgeManager.removeBadge(
+                                      gMenuButtonBadgeManager.BADGEID_DOWNLOAD);
+      }
+    } else {
+      this.indicator.setAttribute("attention", this._attention);
+      if (inMenu) {
+        let badgeClass = "download-" + this._attention;
+        gMenuButtonBadgeManager.addBadge(
+                          gMenuButtonBadgeManager.BADGEID_DOWNLOAD, badgeClass);
       }
     }
-    return aValue;
   },
   _attention: DownloadsCommon.ATTENTION_NONE,
 
   //////////////////////////////////////////////////////////////////////////////
   //// User interface event functions
 
   onWindowUnload() {
     // This function is registered as an event listener, we can't use "this".
@@ -534,16 +574,17 @@ const DownloadsIndicatorView = {
     if (handled) {
       aEvent.preventDefault();
     }
   },
 
   _indicator: null,
   __indicatorCounter: null,
   __indicatorProgress: null,
+  __progressIcon: null,
 
   /**
    * Returns a reference to the main indicator element, or null if the element
    * is not present in the browser window yet.
    */
   get indicator() {
     if (this._indicator) {
       return this._indicator;
@@ -571,23 +612,29 @@ const DownloadsIndicatorView = {
       (this.__indicatorCounter = document.getElementById("downloads-indicator-counter"));
   },
 
   get _indicatorProgress() {
     return this.__indicatorProgress ||
       (this.__indicatorProgress = document.getElementById("downloads-indicator-progress"));
   },
 
+  get _progressIcon() {
+    return this.__progressIcon ||
+      (this.__progressIcon = document.getElementById("downloads-indicator-progress-icon"));
+  },
+
   get notifier() {
     return this._notifier ||
       (this._notifier = document.getElementById("downloads-notification-anchor"));
   },
 
   _onCustomizedAway() {
     this._indicator = null;
+    this.__progressIcon = null;
     this.__indicatorCounter = null;
     this.__indicatorProgress = null;
   },
 
   afterCustomize() {
     // If the cached indicator is not the one currently in the document,
     // invalidate our references
     if (this._indicator != document.getElementById("downloads-button")) {
--- a/browser/components/downloads/content/indicatorOverlay.xul
+++ b/browser/components/downloads/content/indicatorOverlay.xul
@@ -20,17 +20,19 @@
        downloads-button. -->
   <toolbarbutton id="downloads-button" indicator="true">
     <!-- The panel's anchor area is smaller than the outer button, but must
          always be visible and must not move or resize when the indicator
          state changes, otherwise the panel could change its position or lose
          its arrow unexpectedly. -->
     <stack id="downloads-indicator-anchor"
            consumeanchor="downloads-button">
+      <vbox id="downloads-indicator-icon">
+        <vbox id="downloads-indicator-progress-icon"/>
+      </vbox>
       <vbox id="downloads-indicator-progress-area" pack="center">
         <description id="downloads-indicator-counter"/>
         <progressmeter id="downloads-indicator-progress" class="plain"
                        min="0" max="100"/>
       </vbox>
-      <vbox id="downloads-indicator-icon"/>
     </stack>
   </toolbarbutton>
 </overlay>
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -27,17 +27,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
           FileUtils:false, FormValidationHandler:false, Integration:false,
           LightweightThemeManager:false, LoginHelper:false, LoginManagerParent:false,
           NetUtil:false, NewTabMessages:false, NewTabUtils:false, OS:false,
           PageThumbs:false, PdfJs:false, PermissionUI:false, PlacesBackups:false,
           PlacesUtils:false, PluralForm:false, PrivateBrowsingUtils:false,
           ProcessHangMonitor:false, ReaderParent:false, RecentWindow:false,
           RemotePrompt:false, SelfSupportBackend:false, SessionStore:false,
           ShellService:false, SimpleServiceDiscovery:false, TabCrashHandler:false,
-          Task:false, UITour:false, URLBarZoom:false, WebChannel:false,
+          Task:false, UITour:false, WebChannel:false,
           WindowsRegistry:false, webrtcUI:false */
 
 /**
  * IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL.
  * XXX Bug 1325373 is for making eslint detect these automatically.
  */
 
 [
@@ -81,17 +81,16 @@ XPCOMUtils.defineLazyServiceGetter(this,
   ["RemotePrompt", "resource:///modules/RemotePrompt.jsm"],
   ["SelfSupportBackend", "resource:///modules/SelfSupportBackend.jsm"],
   ["SessionStore", "resource:///modules/sessionstore/SessionStore.jsm"],
   ["ShellService", "resource:///modules/ShellService.jsm"],
   ["SimpleServiceDiscovery", "resource://gre/modules/SimpleServiceDiscovery.jsm"],
   ["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"],
   ["Task", "resource://gre/modules/Task.jsm"],
   ["UITour", "resource:///modules/UITour.jsm"],
-  ["URLBarZoom", "resource:///modules/URLBarZoom.jsm"],
   ["WebChannel", "resource://gre/modules/WebChannel.jsm"],
   ["WindowsRegistry", "resource://gre/modules/WindowsRegistry.jsm"],
   ["webrtcUI", "resource:///modules/webrtcUI.jsm"],
 ].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource));
 
 if (AppConstants.MOZ_CRASHREPORTER) {
   XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
                                     "resource:///modules/ContentCrashHandlers.jsm");
@@ -626,17 +625,16 @@ BrowserGlue.prototype = {
 
     ContentClick.init();
     RemotePrompt.init();
     Feeds.init();
     ContentPrefServiceParent.init();
 
     LoginManagerParent.init();
     ReaderParent.init();
-    URLBarZoom.init();
 
     SelfSupportBackend.init();
 
     // Ensure we keep track of places/pw-mananager undo by init'ing this early.
     Cu.import("resource:///modules/AutoMigrate.jsm");
 
     if (AppConstants.INSTALL_COMPACT_THEMES) {
       let vendorShortName = gBrandBundle.GetStringFromName("vendorShortName");
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
@@ -13,17 +13,17 @@ const FAVICON_PRIVACY = "chrome://browse
 
 var stringBundle = Services.strings.createBundle(
                     "chrome://browser/locale/aboutPrivateBrowsing.properties");
 
 var prefBranch = Services.prefs.getBranch("privacy.trackingprotection.");
 var prefObserver = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference]),
- observe: function () {
+ observe() {
    let tpSubHeader = document.getElementById("tpSubHeader");
    let tpToggle = document.getElementById("tpToggle");
    let tpButton = document.getElementById("tpButton");
    let title = document.getElementById("title");
    let titleTracking = document.getElementById("titleTracking");
    let globalTrackingEnabled = prefBranch.getBoolPref("enabled");
    let trackingEnabled = globalTrackingEnabled ||
                          prefBranch.getBoolPref("pbmode.enabled");
@@ -37,30 +37,30 @@ var prefObserver = {
 };
 prefBranch.addObserver("pbmode.enabled", prefObserver, true);
 prefBranch.addObserver("enabled", prefObserver, true);
 
 function setFavIcon(url) {
  document.getElementById("favicon").setAttribute("href", url);
 }
 
-document.addEventListener("DOMContentLoaded", function () {
+document.addEventListener("DOMContentLoaded", function() {
  if (!PrivateBrowsingUtils.isContentWindowPrivate(window)) {
    document.documentElement.classList.remove("private");
    document.documentElement.classList.add("normal");
    document.title = stringBundle.GetStringFromName("title.normal");
    document.getElementById("favicon")
            .setAttribute("href", FAVICON_QUESTION);
    document.getElementById("startPrivateBrowsing")
            .addEventListener("command", openPrivateWindow);
    return;
  }
 
  let tpToggle = document.getElementById("tpToggle");
- document.getElementById("tpButton").addEventListener('click', () => {
+ document.getElementById("tpButton").addEventListener("click", () => {
    tpToggle.click();
  });
 
  document.title = stringBundle.GetStringFromName("title.head");
  document.getElementById("favicon")
          .setAttribute("href", FAVICON_PRIVACY);
  tpToggle.addEventListener("change", toggleTrackingProtection);
  document.getElementById("startTour")
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_DownloadLastDirWithCPS.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_DownloadLastDirWithCPS.js
@@ -52,17 +52,17 @@ async function runTest() {
   let dir3 = newDirectory();
 
   let uri1 = Services.io.newURI("http://test1.com/");
   let uri2 = Services.io.newURI("http://test2.com/");
   let uri3 = Services.io.newURI("http://test3.com/");
   let uri4 = Services.io.newURI("http://test4.com/");
 
   // cleanup functions registration
-  registerCleanupFunction(function () {
+  registerCleanupFunction(function() {
     Services.prefs.clearUserPref("browser.download.lastDir.savePerSite");
     Services.prefs.clearUserPref("browser.download.lastDir");
     [dir1, dir2, dir3].forEach(dir => dir.remove(true));
     win.close();
     pbWin.close();
   });
 
   function checkDownloadLastDir(gDownloadLastDir, aLastDir) {
@@ -168,18 +168,16 @@ async function runTest() {
      "uri3 should return dir3"); // set in CPS
   is((await getFile(downloadLastDir, uri4)).path, dir2.path,
      "uri4 should return dir2"); // fallback
 
   await clearHistoryAndWait();
 
   // check clearHistory removes all data
   is(downloadLastDir.file, null, "clearHistory removes all data");
-  //is(Services.contentPrefs.hasPref(uri1, "browser.download.lastDir", null),
-  //   false, "LastDir preference should be absent");
   is((await getFile(downloadLastDir, uri1)), null, "uri1 should point to null");
   is((await getFile(downloadLastDir, uri2)), null, "uri2 should point to null");
   is((await getFile(downloadLastDir, uri3)), null, "uri3 should point to null");
   is((await getFile(downloadLastDir, uri4)), null, "uri4 should point to null");
 
   await setFile(downloadLastDir, null, tmpDir);
 
   // check data set outside PB mode is remembered
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
@@ -13,48 +13,48 @@ function* openAboutPrivateBrowsing() {
   return { win, tab };
 }
 
 /**
  * Clicks the given link and checks this opens a new tab with the given URI.
  */
 function* testLinkOpensTab({ win, tab, elementId, expectedUrl }) {
   let newTabPromise = BrowserTestUtils.waitForNewTab(win.gBrowser, expectedUrl);
-  yield ContentTask.spawn(tab, { elementId }, function* ({ elementId }) {
-    content.document.getElementById(elementId).click();
+  yield ContentTask.spawn(tab, elementId, function* (elemId) {
+    content.document.getElementById(elemId).click();
   });
   let newTab = yield newTabPromise;
   ok(true, `Clicking ${elementId} opened ${expectedUrl} in a new tab.`);
   yield BrowserTestUtils.removeTab(newTab);
 }
 
 /**
  * Clicks the given link and checks this opens the given URI in the same tab.
  *
  * This function does not return to the previous page.
  */
 function* testLinkOpensUrl({ win, tab, elementId, expectedUrl }) {
   let loadedPromise = BrowserTestUtils.browserLoaded(tab);
-  yield ContentTask.spawn(tab, { elementId }, function* ({ elementId }) {
-    content.document.getElementById(elementId).click();
+  yield ContentTask.spawn(tab, elementId, function* (elemId) {
+    content.document.getElementById(elemId).click();
   });
   yield loadedPromise;
   is(tab.currentURI.spec, expectedUrl,
      `Clicking ${elementId} opened ${expectedUrl} in the same tab.`);
 }
 
 /**
  * Tests the links in "about:privatebrowsing".
  */
 add_task(function* test_links() {
   // Use full version and change the remote URLs to prevent network access.
   Services.prefs.setCharPref("app.support.baseURL", "https://example.com/");
   Services.prefs.setCharPref("privacy.trackingprotection.introURL",
                              "https://example.com/tour");
-  registerCleanupFunction(function () {
+  registerCleanupFunction(function() {
     Services.prefs.clearUserPref("privacy.trackingprotection.introURL");
     Services.prefs.clearUserPref("app.support.baseURL");
   });
 
   let { win, tab } = yield openAboutPrivateBrowsing();
 
   yield testLinkOpensTab({ win, tab,
     elementId: "learnMore",
@@ -72,29 +72,29 @@ add_task(function* test_links() {
 /**
  * Tests the action to disable and re-enable Tracking Protection in
  * "about:privatebrowsing".
  */
 add_task(function* test_toggleTrackingProtection() {
   // Use tour version but disable Tracking Protection.
   Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled",
                              true);
-  registerCleanupFunction(function () {
+  registerCleanupFunction(function() {
     Services.prefs.clearUserPref("privacy.trackingprotection.pbmode.enabled");
   });
 
   let { win, tab } = yield openAboutPrivateBrowsing();
 
   // Set up the observer for the preference change before triggering the action.
   let prefBranch =
       Services.prefs.getBranch("privacy.trackingprotection.pbmode.");
   let waitForPrefChanged = () => new Promise(resolve => {
     let prefObserver = {
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
-      observe: function () {
+      observe() {
         prefBranch.removeObserver("enabled", prefObserver);
         resolve();
       },
     };
     prefBranch.addObserver("enabled", prefObserver, false);
   });
 
   let promisePrefChanged = waitForPrefChanged();
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cache.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cache.js
@@ -17,17 +17,17 @@ Cc["@mozilla.org/moz/jssubscript-loader;
 var Sanitizer = tmp.Sanitizer;
 
 function test() {
 
   waitForExplicitFinish();
 
   sanitizeCache();
 
-  let nrEntriesR1 = getStorageEntryCount("regular", function(nrEntriesR1) {
+  getStorageEntryCount("regular", function(nrEntriesR1) {
     is(nrEntriesR1, 0, "Disk cache reports 0KB and has no entries");
 
     get_cache_for_private_window();
   });
 }
 
 function cleanup() {
   let prefs = Services.prefs.getBranch("privacy.cpd.");
@@ -80,35 +80,33 @@ function getStorageEntryCount(device, go
     storage = cs.diskCacheStorage(LoadContextInfo.default, false);
     break;
   default:
     throw "Unknown device " + device + " at getStorageEntryCount";
   }
 
   var visitor = {
     entryCount: 0,
-    onCacheStorageInfo: function (aEntryCount, aConsumption) {
+    onCacheStorageInfo(aEntryCount, aConsumption) {
     },
-    onCacheEntryInfo: function(uri)
-    {
+    onCacheEntryInfo(uri) {
       var urispec = uri.asciiSpec;
       info(device + ":" + urispec + "\n");
       if (urispec.match(/^http:\/\/example.org\//))
         ++this.entryCount;
     },
-    onCacheEntryVisitCompleted: function()
-    {
+    onCacheEntryVisitCompleted() {
       goon(this.entryCount);
     }
   };
 
   storage.asyncVisitStorage(visitor, true);
 }
 
-function get_cache_for_private_window () {
+function get_cache_for_private_window() {
   let win = whenNewWindowLoaded({private: true}, function() {
 
     executeSoon(function() {
 
       ok(true, "The private window got loaded");
 
       let tab = win.gBrowser.addTab("http://example.org");
       win.gBrowser.selectedTab = tab;
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_certexceptionsui.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_certexceptionsui.js
@@ -2,19 +2,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This test makes sure that certificate exceptions UI behaves correctly
 // in private browsing windows, based on whether it's opened from the prefs
 // window or from the SSL error page (see bug 461627).
 
 function test() {
-  const EXCEPTIONS_DLG_URL = 'chrome://pippki/content/exceptionDialog.xul';
-  const EXCEPTIONS_DLG_FEATURES = 'chrome,centerscreen';
-  const INVALID_CERT_LOCATION = 'https://nocert.example.com/';
+  const EXCEPTIONS_DLG_URL = "chrome://pippki/content/exceptionDialog.xul";
+  const EXCEPTIONS_DLG_FEATURES = "chrome,centerscreen";
+  const INVALID_CERT_LOCATION = "https://nocert.example.com/";
   waitForExplicitFinish();
 
   // open a private browsing window
   var pbWin = OpenBrowserWindow({private: true});
   pbWin.addEventListener("load", function() {
     doTest();
   }, {once: true});
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent.js
@@ -13,76 +13,76 @@
 
 add_task(function* setup() {
   yield SpecialPowers.pushPrefEnv({
     set: [["dom.ipc.processCount", 1]]
   });
 });
 
 add_task(function* test() {
-  let prefix = 'http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent_page.html';
+  let prefix = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent_page.html";
 
   function getElts(browser) {
-    return browser.contentTitle.split('|');
-  };
+    return browser.contentTitle.split("|");
+  }
 
   // Step 1
   let non_private_browser = gBrowser.selectedBrowser;
-  non_private_browser.loadURI(prefix + '?action=set&name=test&value=value&initial=true');
+  non_private_browser.loadURI(prefix + "?action=set&name=test&value=value&initial=true");
   yield BrowserTestUtils.browserLoaded(non_private_browser);
 
 
   // Step 2
   let private_window = yield BrowserTestUtils.openNewBrowserWindow({ private : true });
   let private_browser = private_window.getBrowser().selectedBrowser;
-  private_browser.loadURI(prefix + '?action=set&name=test2&value=value2');
+  private_browser.loadURI(prefix + "?action=set&name=test2&value=value2");
   yield BrowserTestUtils.browserLoaded(private_browser);
 
 
   // Step 3
-  non_private_browser.loadURI(prefix + '?action=get&name=test2');
+  non_private_browser.loadURI(prefix + "?action=get&name=test2");
   yield BrowserTestUtils.browserLoaded(non_private_browser);
   let elts = yield getElts(non_private_browser);
-  isnot(elts[0], 'value2', "public window shouldn't see private storage");
-  is(elts[1], '1', "public window should only see public items");
+  isnot(elts[0], "value2", "public window shouldn't see private storage");
+  is(elts[1], "1", "public window should only see public items");
 
 
   // Step 4
-  private_browser.loadURI(prefix + '?action=get&name=test');
+  private_browser.loadURI(prefix + "?action=get&name=test");
   yield BrowserTestUtils.browserLoaded(private_browser);
   elts = yield getElts(private_browser);
-  isnot(elts[0], 'value', "private window shouldn't see public storage");
-  is(elts[1], '1', "private window should only see private items");
+  isnot(elts[0], "value", "private window shouldn't see public storage");
+  is(elts[1], "1", "private window should only see private items");
 
 
   // Reopen the private window again, without privateBrowsing, which should clear the
   // the private storage.
   private_window.close();
   private_window = yield BrowserTestUtils.openNewBrowserWindow({ private : false });
   private_browser = null;
   yield new Promise(resolve => Cu.schedulePreciseGC(resolve));
   private_browser = private_window.getBrowser().selectedBrowser;
 
-  private_browser.loadURI(prefix + '?action=get&name=test2');
+  private_browser.loadURI(prefix + "?action=get&name=test2");
   yield BrowserTestUtils.browserLoaded(private_browser);
   elts = yield getElts(private_browser);
-  isnot(elts[0], 'value2', "public window shouldn't see cleared private storage");
-  is(elts[1], '1', "public window should only see public items");
+  isnot(elts[0], "value2", "public window shouldn't see cleared private storage");
+  is(elts[1], "1", "public window should only see public items");
 
 
   // Making it private again should clear the storage and it shouldn't
   // be able to see the old private storage as well.
   private_window.close();
   private_window = yield BrowserTestUtils.openNewBrowserWindow({ private : true });
   private_browser = null;
   yield new Promise(resolve => Cu.schedulePreciseGC(resolve));
   private_browser = private_window.getBrowser().selectedBrowser;
 
-  private_browser.loadURI(prefix + '?action=set&name=test3&value=value3');
+  private_browser.loadURI(prefix + "?action=set&name=test3&value=value3");
   yield BrowserTestUtils.browserLoaded(private_browser);
   elts = yield getElts(private_browser);
-  is(elts[1], '1', "private window should only see new private items");
+  is(elts[1], "1", "private window should only see new private items");
 
   // Cleanup.
-  non_private_browser.loadURI(prefix + '?final=true');
+  non_private_browser.loadURI(prefix + "?final=true");
   yield BrowserTestUtils.browserLoaded(non_private_browser);
   private_window.close();
 });
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent_page.html
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent_page.html
@@ -1,33 +1,33 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <script type="text/javascript">
-  var oGetVars = {};  
-      
-  if (window.location.search.length > 1) {  
+  var oGetVars = {};
+
+  if (window.location.search.length > 1) {
     for (var aItKey, nKeyId = 0, aCouples = window.location.search.substr(1).split("&");
          nKeyId < aCouples.length;
-         nKeyId++) {  
-      aItKey = aCouples[nKeyId].split("=");  
-      oGetVars[unescape(aItKey[0])] = aItKey.length > 1 ? unescape(aItKey[1]) : "";  
-    }  
+         nKeyId++) {
+      aItKey = aCouples[nKeyId].split("=");
+      oGetVars[unescape(aItKey[0])] = aItKey.length > 1 ? unescape(aItKey[1]) : "";
+    }
   }
 
-  if (oGetVars.initial == 'true') {
+  if (oGetVars.initial == "true") {
     localStorage.clear();
   }
 
-  if (oGetVars.action == 'set') {
+  if (oGetVars.action == "set") {
     localStorage.setItem(oGetVars.name, oGetVars.value);
     document.title = localStorage.getItem(oGetVars.name) + "|" + localStorage.length;
-  } else if (oGetVars.action == 'get') {
+  } else if (oGetVars.action == "get") {
     document.title = localStorage.getItem(oGetVars.name) + "|" + localStorage.length;
   }
 
-  if (oGetVars.final == 'true') {
+  if (oGetVars.final == "true") {
     localStorage.clear();
   }
 </script>
 </head>
 <body>
 </body>
 </html>
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_context_and_chromeFlags.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_context_and_chromeFlags.js
@@ -25,18 +25,18 @@ function assertWindowIsPrivate(win) {
               "Should have the private window chrome flag");
   }
 
   let loadContext = winDocShell.QueryInterface(Ci.nsILoadContext);
   Assert.ok(loadContext.usePrivateBrowsing,
             "The parent window should be using private browsing");
 
   return ContentTask.spawn(win.gBrowser.selectedBrowser, null, function*() {
-    let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
-    Assert.ok(loadContext.usePrivateBrowsing,
+    let contentLoadContext = docShell.QueryInterface(Ci.nsILoadContext);
+    Assert.ok(contentLoadContext.usePrivateBrowsing,
               "Content docShell should be using private browsing");
   });
 }
 
 /**
  * Tests that chromeFlags bits and the nsILoadContext.usePrivateBrowsing
  * attribute are properly set when opening a new private browsing
  * window.
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_crh.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_crh.js
@@ -1,25 +1,25 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-// This test makes sure that the Clear Recent History menu item and command 
+// This test makes sure that the Clear Recent History menu item and command
 // is disabled inside the private browsing mode.
 
 add_task(function* test() {
   function checkDisableOption(aPrivateMode, aWindow) {
     let crhCommand = aWindow.document.getElementById("Tools:Sanitize");
     ok(crhCommand, "The clear recent history command should exist");
 
     is(PrivateBrowsingUtils.isWindowPrivate(aWindow), aPrivateMode,
       "PrivateBrowsingUtils should report the correct per-window private browsing status");
     is(crhCommand.hasAttribute("disabled"), aPrivateMode,
       "Clear Recent History command should be disabled according to the private browsing mode");
-  };
+  }
 
   let testURI = "http://mochi.test:8888/";
 
   let privateWin = yield BrowserTestUtils.openNewBrowserWindow({private: true});
   let privateBrowser = privateWin.gBrowser.selectedBrowser;
   privateBrowser.loadURI(testURI);
   yield BrowserTestUtils.browserLoaded(privateBrowser);
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir.js
@@ -26,17 +26,17 @@ function test() {
   let dir1 = newDirectory();
   let dir2 = newDirectory();
   let dir3 = newDirectory();
   let file1 = newFileInDirectory(dir1);
   let file2 = newFileInDirectory(dir2);
   let file3 = newFileInDirectory(dir3);
 
   // cleanup functions registration
-  registerCleanupFunction(function () {
+  registerCleanupFunction(function() {
     Services.prefs.clearUserPref("browser.download.lastDir");
     [dir1, dir2, dir3].forEach(dir => dir.remove(true));
     MockFilePicker.cleanup();
   });
   prefs.setComplexValue("lastDir", Ci.nsIFile, tmpDir);
 
   function testOnWindow(aPrivate, aCallback) {
     whenNewWindowLoaded({private: aPrivate}, function(win) {
@@ -53,17 +53,17 @@ function test() {
        "LastDir should be the expected display dir");
     // Check gDownloadLastDir value.
     is(gDownloadLastDir.file.path, aDisplayDir.path,
        "gDownloadLastDir should be the expected display dir");
 
     MockFilePicker.returnFiles = [aFile];
     MockFilePicker.displayDirectory = null;
 
-    launcher.saveDestinationAvailable = function (file) {
+    launcher.saveDestinationAvailable = function(file) {
       ok(!!file, "promptForSaveToFile correctly returned a file");
 
       // File picker should start with expected display dir.
       is(MockFilePicker.displayDirectory.path, aDisplayDir.path,
         "File picker should start with browser.download.lastDir");
       // browser.download.lastDir should be modified on not private windows
       is(prefs.getComplexValue("lastDir", Ci.nsIFile).path, aLastDir.path,
          "LastDir should be the expected last dir");
@@ -76,18 +76,18 @@ function test() {
       aCallback();
     };
 
     launcherDialog.promptForSaveToFileAsync(launcher, aWin, null, null, null);
   }
 
   testOnWindow(false, function(win, downloadDir) {
     testDownloadDir(win, downloadDir, file1, tmpDir, dir1, dir1, function() {
-      testOnWindow(true, function(win, downloadDir) {
-        testDownloadDir(win, downloadDir, file2, dir1, dir1, dir2, function() {
-          testOnWindow(false, function(win, downloadDir) {
-            testDownloadDir(win, downloadDir, file3, dir1, dir3, dir3, finish);
+      testOnWindow(true, function(win1, downloadDir1) {
+        testDownloadDir(win1, downloadDir1, file2, dir1, dir1, dir2, function() {
+          testOnWindow(false, function(win2, downloadDir2) {
+            testDownloadDir(win2, downloadDir2, file3, dir1, dir3, dir3, finish);
           });
         });
       });
     });
   });
 }
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js
@@ -21,17 +21,17 @@ function test() {
   let dir1 = newDirectory();
   let dir2 = newDirectory();
   let dir3 = newDirectory();
   let file1 = newFileInDirectory(dir1);
   let file2 = newFileInDirectory(dir2);
   let file3 = newFileInDirectory(dir3);
 
   // cleanup function registration
-  registerCleanupFunction(function () {
+  registerCleanupFunction(function() {
     Services.prefs.clearUserPref("browser.download.lastDir");
     [dir1, dir2, dir3].forEach(dir => dir.remove(true));
     MockFilePicker.cleanup();
     validateFileName = validateFileNameToRestore;
   });
 
   // Overwrite validateFileName to validate everything
   validateFileName = foo => foo;
@@ -78,18 +78,18 @@ function test() {
       gDownloadLastDir.cleanupPrivateFile();
       aWin.close();
       aCallback();
     }).then(null, function() { ok(false); });
   }
 
   testOnWindow(false, function(win, downloadDir) {
     testDownloadDir(win, downloadDir, file1, tmpDir, dir1, dir1, function() {
-      testOnWindow(true, function(win, downloadDir) {
-        testDownloadDir(win, downloadDir, file2, dir1, dir1, dir2, function() {
-          testOnWindow(false, function(win, downloadDir) {
-            testDownloadDir(win, downloadDir, file3, dir1, dir3, dir3, finish);
+      testOnWindow(true, function(win1, downloadDir1) {
+        testDownloadDir(win1, downloadDir1, file2, dir1, dir1, dir2, function() {
+          testOnWindow(false, function(win2, downloadDir2) {
+            testDownloadDir(win2, downloadDir2, file3, dir1, dir3, dir3, finish);
           });
         });
       });
     });
   });
 }
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_toggle.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_toggle.js
@@ -5,17 +5,17 @@ Cu.import("resource://gre/modules/Downlo
  * Tests how the browser remembers the last download folder
  * from download to download, with a particular emphasis
  * on how it behaves when private browsing windows open.
  */
 add_task(function* test_downloads_last_dir_toggle() {
   let tmpDir = FileUtils.getDir("TmpD", [], true);
   let dir1 = newDirectory();
 
-  registerCleanupFunction(function () {
+  registerCleanupFunction(function() {
     Services.prefs.clearUserPref("browser.download.lastDir");
     dir1.remove(true);
   });
 
   let win = yield BrowserTestUtils.openNewBrowserWindow();
   let gDownloadLastDir = new DownloadLastDir(win);
   is(typeof gDownloadLastDir, "object",
      "gDownloadLastDir should be a valid object");
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
@@ -157,17 +157,17 @@ function waitOnFaviconLoaded(aFaviconURL
         }
       },
     };
 
     PlacesUtils.history.addObserver(observer, false);
   });
 }
 
-function* assignCookies(aBrowser, aURL, aCookieValue){
+function* assignCookies(aBrowser, aURL, aCookieValue) {
   let tabInfo = yield openTab(aBrowser, aURL);
 
   yield ContentTask.spawn(tabInfo.browser, aCookieValue, function* (value) {
     content.document.cookie = value;
   });
 
   yield BrowserTestUtils.removeTab(tabInfo.tab);
 }
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt.js
@@ -12,32 +12,32 @@ add_task(function* test() {
   function checkGeolocation(aPrivateMode, aWindow) {
     return Task.spawn(function* () {
       aWindow.gBrowser.selectedTab = aWindow.gBrowser.addTab(testPageURL);
       yield BrowserTestUtils.browserLoaded(aWindow.gBrowser.selectedBrowser);
 
       let notification = aWindow.PopupNotifications.getNotification("geolocation");
 
       // Wait until the notification is available.
-      while (!notification){
+      while (!notification) {
         yield new Promise(resolve => { executeSoon(resolve); });
-        let notification = aWindow.PopupNotifications.getNotification("geolocation");
+        notification = aWindow.PopupNotifications.getNotification("geolocation");
       }
 
       if (aPrivateMode) {
         // Make sure the notification is correctly displayed without a remember control
         ok(!notification.options.checkbox.show, "Secondary actions should exist (always/never remember)");
       } else {
         ok(notification.options.checkbox.show, "Secondary actions should exist (always/never remember)");
       }
       notification.remove();
 
       aWindow.gBrowser.removeCurrentTab();
     });
-  };
+  }
 
   let win = yield BrowserTestUtils.openNewBrowserWindow();
   let browser = win.gBrowser.selectedBrowser;
   browser.loadURI(testPageURL);
   yield BrowserTestUtils.browserLoaded(browser);
 
   yield checkGeolocation(false, win);
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html
@@ -1,13 +1,13 @@
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
 <html>
   <head>
     <title>Geolocation invoker</title>
   </head>
   <body>
     <script type="text/javascript">
-      navigator.geolocation.getCurrentPosition(function (pos) {
+      navigator.geolocation.getCurrentPosition(function(pos) {
         // ignore
       });
     </script>
   </body>
 </html>
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_lastpbcontextexited.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_lastpbcontextexited.js
@@ -6,25 +6,25 @@ function test() {
   // We need to open a new window for this so that its docshell would get destroyed
   // when clearing the PB mode flag.
   function runTest(aCloseWindow, aCallback) {
     let newWin = OpenBrowserWindow({private: true});
     SimpleTest.waitForFocus(function() {
       let expectedExiting = true;
       let expectedExited = false;
       let observerExiting = {
-        observe: function(aSubject, aTopic, aData) {
+        observe(aSubject, aTopic, aData) {
           is(aTopic, "last-pb-context-exiting", "Correct topic should be dispatched (exiting)");
           is(expectedExiting, true, "notification not expected yet (exiting)");
           expectedExited = true;
           Services.obs.removeObserver(observerExiting, "last-pb-context-exiting");
         }
       };
       let observerExited = {
-        observe: function(aSubject, aTopic, aData) {
+        observe(aSubject, aTopic, aData) {
           is(aTopic, "last-pb-context-exited", "Correct topic should be dispatched (exited)");
           is(expectedExited, true, "notification not expected yet (exited)");
           Services.obs.removeObserver(observerExited, "last-pb-context-exited");
           aCallback();
         }
       };
       Services.obs.addObserver(observerExiting, "last-pb-context-exiting", false);
       Services.obs.addObserver(observerExited, "last-pb-context-exited", false);
@@ -35,15 +35,15 @@ function test() {
     }, newWin);
   }
 
   waitForExplicitFinish();
 
   runTest(function(newWin) {
       // Simulate pressing the window close button
       newWin.document.getElementById("cmd_closeWindow").doCommand();
-    }, function () {
+    }, function() {
       runTest(function(newWin) {
           // Simulate closing the last tab
           newWin.document.getElementById("cmd_close").doCommand();
         }, finish);
     });
 }
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage.js
@@ -1,25 +1,25 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
  add_task(function* test() {
   requestLongerTimeout(2);
-  const page1 = 'http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/' +
-                'browser_privatebrowsing_localStorage_page1.html'
+  const page1 = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/" +
+                "browser_privatebrowsing_localStorage_page1.html"
 
   let win = yield BrowserTestUtils.openNewBrowserWindow({private: true});
 
-  let tab = win.gBrowser.selectedTab = win.gBrowser.addTab(page1);
+  win.gBrowser.selectedTab = win.gBrowser.addTab(page1);
   let browser = win.gBrowser.selectedBrowser;
   yield BrowserTestUtils.browserLoaded(browser);
 
   browser.loadURI(
-    'http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/' +
-    'browser_privatebrowsing_localStorage_page2.html');
+    "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/" +
+    "browser_privatebrowsing_localStorage_page2.html");
   yield BrowserTestUtils.browserLoaded(browser);
 
-  is(browser.contentTitle, '2', "localStorage should contain 2 items");
+  is(browser.contentTitle, "2", "localStorage should contain 2 items");
 
   // Cleanup
   yield BrowserTestUtils.closeWindow(win);
  });
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after.js
@@ -6,31 +6,30 @@
 // allow any data to leak due to cached values.
 
 // Step 1: Load browser_privatebrowsing_localStorage_before_after_page.html in a private tab, causing a storage
 //   item to exist. Close the tab.
 // Step 2: Load the same page in a non-private tab, ensuring that the storage instance reports only one item
 //   existing.
 
 add_task(function* test() {
-  let testURI = "about:blank";
-  let prefix = 'http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/';
+  let prefix = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/";
 
   // Step 1.
   let privateWin = yield BrowserTestUtils.openNewBrowserWindow({private: true});
   let privateBrowser = privateWin.gBrowser.addTab(
-    prefix + 'browser_privatebrowsing_localStorage_before_after_page.html').linkedBrowser;
+    prefix + "browser_privatebrowsing_localStorage_before_after_page.html").linkedBrowser;
   yield BrowserTestUtils.browserLoaded(privateBrowser);
 
-  is(privateBrowser.contentTitle, '1', "localStorage should contain 1 item");
+  is(privateBrowser.contentTitle, "1", "localStorage should contain 1 item");
 
   // Step 2.
   let win = yield BrowserTestUtils.openNewBrowserWindow();
   let browser = win.gBrowser.addTab(
-    prefix + 'browser_privatebrowsing_localStorage_before_after_page2.html').linkedBrowser;
+    prefix + "browser_privatebrowsing_localStorage_before_after_page2.html").linkedBrowser;
   yield BrowserTestUtils.browserLoaded(browser);
 
-  is(browser.contentTitle, 'null|0', 'localStorage should contain 0 items');
+  is(browser.contentTitle, "null|0", "localStorage should contain 0 items");
 
   // Cleanup
   yield BrowserTestUtils.closeWindow(privateWin);
   yield BrowserTestUtils.closeWindow(win);
 });
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after_page.html
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after_page.html
@@ -1,11 +1,11 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <script type="text/javascript">
   localStorage.clear();
-  localStorage.setItem('zzztest', 'zzzvalue');
+  localStorage.setItem("zzztest", "zzzvalue");
   document.title = localStorage.length;
 </script>
 </head>
 <body>
 </body>
 </html>
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after_page2.html
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after_page2.html
@@ -1,10 +1,10 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <script type="text/javascript">
-  document.title = localStorage.getItem('zzztest', 'zzzvalue') + '|' + localStorage.length;
+  document.title = localStorage.getItem("zzztest", "zzzvalue") + "|" + localStorage.length;
   localStorage.clear();
 </script>
 </head>
 <body>
 </body>
 </html>
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_page1.html
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_page1.html
@@ -1,10 +1,10 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <script type="text/javascript">
   localStorage.clear();
-  localStorage.setItem('test1', 'value1');
+  localStorage.setItem("test1", "value1");
 </script>
 </head>
 <body>
 </body>
 </html>
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_page2.html
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_page2.html
@@ -1,10 +1,10 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <script type="text/javascript">
-  localStorage.setItem('test2', 'value2');
+  localStorage.setItem("test2", "value2");
   document.title = localStorage.length;
 </script>
 </head>
 <body>
 </body>
 </html>
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_opendir.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_opendir.js
@@ -76,17 +76,17 @@ function test() {
       setupCleanSlate(privateWindow);
 
       // Test 2: the user first tries to open a file inside the private browsing mode
 
       // test the private window
       ok(!privateWindow.gLastOpenDirectory.path,
          "No original path should exist inside the private browsing mode");
       privateWindow.gLastOpenDirectory.path = dir1;
-      is(privateWindow.gLastOpenDirectory.path.path, dir1.path, 
+      is(privateWindow.gLastOpenDirectory.path.path, dir1.path,
          "The path should be successfully set inside the private browsing mode");
       // test the non-private window
       ok(!nonPrivateWindow.gLastOpenDirectory.path,
          "The path set inside the private browsing mode should not leak when leaving that mode");
 
       setupCleanSlate(nonPrivateWindow);
       setupCleanSlate(privateWindow);
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.js
@@ -13,33 +13,33 @@ add_task(function* test() {
   const TEST_URL = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.html"
   const TEST_URI = Services.io.newURI(TEST_URL);
   const TITLE_1 = "Title 1";
   const TITLE_2 = "Title 2";
 
   function waitForTitleChanged() {
     return new Promise(resolve => {
       let historyObserver = {
-        onTitleChanged: function(uri, pageTitle) {
+        onTitleChanged(uri, pageTitle) {
           PlacesUtils.history.removeObserver(historyObserver, false);
-          resolve({uri: uri, pageTitle: pageTitle});
+          resolve({uri, pageTitle});
         },
-        onBeginUpdateBatch: function () {},
-        onEndUpdateBatch: function () {},
-        onVisit: function () {},
-        onDeleteURI: function () {},
-        onClearHistory: function () {},
-        onPageChanged: function () {},
-        onDeleteVisits: function() {},
+        onBeginUpdateBatch() {},
+        onEndUpdateBatch() {},
+        onVisit() {},
+        onDeleteURI() {},
+        onClearHistory() {},
+        onPageChanged() {},
+        onDeleteVisits() {},
         QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver])
       };
 
       PlacesUtils.history.addObserver(historyObserver, false);
     });
-  };
+  }
 
   yield PlacesTestUtils.clearHistory();
 
   let tabToClose = gBrowser.selectedTab = gBrowser.addTab(TEST_URL);
   yield waitForTitleChanged();
   is(PlacesUtils.history.getPageTitle(TEST_URI), TITLE_1, "The title matches the orignal title after first visit");
 
   let place = {
@@ -47,18 +47,18 @@ add_task(function* test() {
     title: TITLE_2,
     visits: [{
       visitDate: Date.now() * 1000,
       transitionType: Ci.nsINavHistoryService.TRANSITION_LINK
     }]
   };
   PlacesUtils.asyncHistory.updatePlaces(place, {
     handleError: () => ok(false, "Unexpected error in adding visit."),
-    handleResult: function () { },
-    handleCompletion: function () {}
+    handleResult() { },
+    handleCompletion() {}
   });
 
   yield waitForTitleChanged();
   is(PlacesUtils.history.getPageTitle(TEST_URI), TITLE_2, "The title matches the updated title after updating visit");
 
   let privateWin = yield BrowserTestUtils.openNewBrowserWindow({private:true});
   yield BrowserTestUtils.browserLoaded(privateWin.gBrowser.addTab(TEST_URL).linkedBrowser);
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js
@@ -20,17 +20,17 @@ add_task(function* test() {
   yield cleanup();
 
   let deferredFirst = PromiseUtils.defer();
   let deferredSecond = PromiseUtils.defer();
   let deferredThird = PromiseUtils.defer();
 
   let testNumber = 0;
   let historyObserver = {
-    onTitleChanged: function(aURI, aPageTitle) {
+    onTitleChanged(aURI, aPageTitle) {
       if (aURI.spec != TEST_URL)
         return;
       switch (++testNumber) {
         case 1:
           // The first time that the page is loaded
           deferredFirst.resolve(aPageTitle);
           break;
         case 2:
@@ -43,23 +43,23 @@ add_task(function* test() {
           break;
         default:
           // Checks that opening the page in a private window should not fire a
           // title change.
           ok(false, "Title changed. Unexpected pass: " + testNumber);
       }
     },
 
-    onBeginUpdateBatch: function () {},
-    onEndUpdateBatch: function () {},
-    onVisit: function () {},
-    onDeleteURI: function () {},
-    onClearHistory: function () {},
-    onPageChanged: function () {},
-    onDeleteVisits: function() {},
+    onBeginUpdateBatch() {},
+    onEndUpdateBatch() {},
+    onVisit() {},
+    onDeleteURI() {},
+    onClearHistory() {},
+    onPageChanged() {},
+    onDeleteVisits() {},
     QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver])
   };
   PlacesUtils.history.addObserver(historyObserver, false);
 
 
   let win = yield BrowserTestUtils.openNewBrowserWindow();
   win.gBrowser.selectedTab = win.gBrowser.addTab(TEST_URL);
   let aPageTitle = yield deferredFirst.promise;
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_sidebar.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_sidebar.js
@@ -15,55 +15,55 @@ function test() {
     return win.SidebarUI.show("viewBookmarksSidebar").then(() => win);
   }
 
   let windowCache = [];
   function cacheWindow(w) {
     windowCache.push(w);
     return w;
   }
-  function closeCachedWindows () {
+  function closeCachedWindows() {
     windowCache.forEach(w => w.close());
   }
 
   // Part 1: NON PRIVATE WINDOW -> PRIVATE WINDOW
   openWindow(window, {}, 1).
     then(cacheWindow).
     then(openSidebar).
     then(win => openWindow(win, { private: true })).
     then(cacheWindow).
     then(function({ document }) {
       let sidebarBox = document.getElementById("sidebar-box");
-      is(sidebarBox.hidden, true, 'Opening a private window from reg window does not open the sidebar');
+      is(sidebarBox.hidden, true, "Opening a private window from reg window does not open the sidebar");
     }).
     // Part 2: NON PRIVATE WINDOW -> NON PRIVATE WINDOW
     then(() => openWindow(window)).
     then(cacheWindow).
     then(openSidebar).
     then(win => openWindow(win)).
     then(cacheWindow).
     then(function({ document }) {
       let sidebarBox = document.getElementById("sidebar-box");
-      is(sidebarBox.hidden, false, 'Opening a reg window from reg window does open the sidebar');
+      is(sidebarBox.hidden, false, "Opening a reg window from reg window does open the sidebar");
     }).
     // Part 3: PRIVATE WINDOW -> NON PRIVATE WINDOW
     then(() => openWindow(window, { private: true })).
     then(cacheWindow).
     then(openSidebar).
     then(win => openWindow(win)).
     then(cacheWindow).
     then(function({ document }) {
       let sidebarBox = document.getElementById("sidebar-box");
-      is(sidebarBox.hidden, true, 'Opening a reg window from a private window does not open the sidebar');
+      is(sidebarBox.hidden, true, "Opening a reg window from a private window does not open the sidebar");
     }).
     // Part 4: PRIVATE WINDOW -> PRIVATE WINDOW
     then(() => openWindow(window, { private: true })).
     then(cacheWindow).
     then(openSidebar).
     then(win => openWindow(win, { private: true })).
     then(cacheWindow).
     then(function({ document }) {
       let sidebarBox = document.getElementById("sidebar-box");
-      is(sidebarBox.hidden, false, 'Opening a private window from private window does open the sidebar');
+      is(sidebarBox.hidden, false, "Opening a private window from private window does open the sidebar");
     }).
     then(closeCachedWindows).
     then(finish);
 }
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_ui.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_ui.js
@@ -28,41 +28,41 @@ function test() {
       is(PrivateBrowsingUtils.isWindowPrivate(aWindow), aIsPrivateMode,
         "PrivateBrowsingUtils should report the correct per-window private browsing status (privateBrowsing should be " +
         aIsPrivateMode + ")");
 
       aCallback();
     }, {capture: true, once: true});
 
     aWindow.gBrowser.selectedBrowser.loadURI(testURI);
-  };
+  }
 
   function openPrivateBrowsingModeByUI(aWindow, aCallback) {
     Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
       aSubject.addEventListener("load", function() {
         Services.obs.removeObserver(observer, "domwindowopened");
           windowsToClose.push(aSubject);
           aCallback(aSubject);
       }, {once: true});
     }, "domwindowopened", false);
 
     cmd = aWindow.document.getElementById("Tools:PrivateBrowsing");
     var func = new Function("", cmd.getAttribute("oncommand"));
     func.call(cmd);
-  };
+  }
 
   function testOnWindow(aOptions, aCallback) {
     whenNewWindowLoaded(aOptions, function(aWin) {
       windowsToClose.push(aWin);
       // execute should only be called when need, like when you are opening
       // web pages on the test. If calling executeSoon() is not necesary, then
       // call whenNewWindowLoaded() instead of testOnWindow() on your test.
       executeSoon(() => aCallback(aWin));
     });
-  };
+  }
 
    // this function is called after calling finish() on the test.
   registerCleanupFunction(function() {
     windowsToClose.forEach(function(aWin) {
       aWin.close();
     });
   });
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js
@@ -22,18 +22,17 @@ add_task(function* test() {
   let pb_about_pb_title;
   if (isOSX) {
     page_with_title = test_title;
     page_without_title = app_name;
     about_pb_title = "Open a private window?";
     pb_page_with_title = test_title + " - (Private Browsing)";
     pb_page_without_title = app_name + " - (Private Browsing)";
     pb_about_pb_title = "Private Browsing - (Private Browsing)";
-  }
-  else {
+  } else {
     page_with_title = test_title + " - " + app_name;
     page_without_title = app_name;
     about_pb_title = "Open a private window?" + " - " + app_name;
     pb_page_with_title = test_title + " - " + app_name + " (Private Browsing)";
     pb_page_without_title = app_name + " (Private Browsing)";
     pb_about_pb_title = "Private Browsing - " + app_name + " (Private Browsing)";
   }
 
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -1560,28 +1560,29 @@
             this.settingsButton.setAttribute("width", buttonWidth);
             if (rowCount == 1 && hasDummyItems) {
               // When there's only one row, make the compact settings button
               // hug the right edge of the panel.  It may not due to the panel's
               // width not being an integral multiple of the button width.  (See
               // the "There will be an emtpy area" comment above.)  Increase the
               // width of the last dummy item by the remainder.
               //
-              // There's one weird thing to guard against.  When layout pixels
-              // aren't an integral multiple of device pixels, the calculated
-              // remainder can end up being ~1px too big, at least on Windows,
-              // which pushes the settings button to a new row.  The remainder
-              // is integral, not a fraction, so that's not the problem.  To
-              // work around that, unscale the remainder, floor it, scale it
-              // back, and then floor that.
+              // There's one weird thing to guard against: when layout pixels
+              // aren't an integral multiple of device pixels, the settings
+              // button sometimes gets pushed to a new row, depending on the
+              // panel and button widths.  It's as if `remainder` is somehow
+              // too big, even though it's an integer.  To work around that,
+              // decrement the remainder if the scale is not an integer.
               let scale = window.QueryInterface(Ci.nsIInterfaceRequestor)
                                 .getInterface(Ci.nsIDOMWindowUtils)
                                 .screenPixelsPerCSSPixel;
               let remainder = panelWidth - (enginesPerRow * buttonWidth);
-              remainder = Math.floor(Math.floor(remainder * scale) / scale);
+              if (Math.floor(scale) != scale) {
+                remainder--;
+              }
               let width = remainder + buttonWidth;
               let lastDummyItem = this.settingsButton.previousSibling;
               lastDummyItem.setAttribute("width", width);
             }
           }
         ]]></body>
       </method>
 
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -51,15 +51,14 @@ MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3
 # This should usually be the same as the value MAR_CHANNEL_ID.
 # If more than one ID is needed, then you should use a comma separated list
 # of values.
 ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
 # The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
 MAR_CHANNEL_ID=firefox-mozilla-central
 MOZ_PROFILE_MIGRATOR=1
 MOZ_JSDOWNLOADS=1
-MOZ_RUST_MP4PARSE=1
 
 # Enable checking that add-ons are signed by the trusted root
 MOZ_ADDON_SIGNING=1
 
 # Include the DevTools client, not just the server (which is the default)
 MOZ_DEVTOOLS=all
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -228,30 +228,19 @@ let ProfileAutocomplete = {
 
     if (selectedIndex == -1 ||
         !this._lastAutoCompleteResult ||
         this._lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill-profile") {
       return;
     }
 
     let profile = JSON.parse(this._lastAutoCompleteResult.getCommentAt(selectedIndex));
+    let formHandler = FormAutofillContent.getFormHandler(focusedInput);
 
-    // TODO: FormAutofillHandler.autofillFormFields will be used for filling
-    // fields logic eventually.
-    for (let inputInfo of formDetails) {
-      // Skip filling the value of focused input which is filled in
-      // FormFillController.
-      if (inputInfo.element === focusedInput) {
-        continue;
-      }
-      let value = profile[inputInfo.fieldName];
-      if (value) {
-        inputInfo.element.setUserInput(value);
-      }
-    }
+    formHandler.autofillFormFields(profile, focusedInput);
   },
 };
 
 /**
  * Handles content's interactions for the process.
  *
  * NOTE: Declares it by "var" to make it accessible in unit tests.
  */
@@ -291,28 +280,41 @@ var FormAutofillContent = {
       if (element == detail.element) {
         return detail;
       }
     }
     return null;
   },
 
   /**
+   * Get the form's handler from cache which is created after page identified.
+   *
+   * @param {HTMLInputElement} element Focused input which triggered profile searching
+   * @returns {Array<Object>|null}
+   *          Return target form's handler from content cache
+   *          (or return null if the information is not found in the cache).
+   *
+   */
+  getFormHandler(element) {
+    let rootElement = FormLikeFactory.findRootForField(element);
+    return this._formsDetails.get(rootElement);
+  },
+
+  /**
    * Get the form's information from cache which is created after page identified.
    *
    * @param {HTMLInputElement} element Focused input which triggered profile searching
    * @returns {Array<Object>|null}
-   *          Return target form's information that cloned from content cache
+   *          Return target form's information from content cache
    *          (or return null if the information is not found in the cache).
    *
    */
   getFormDetails(element) {
-    let rootElement = FormLikeFactory.findRootForField(element);
-    let formDetails = this._formsDetails.get(rootElement);
-    return formDetails ? formDetails.fieldDetails : null;
+    let formHandler = this.getFormHandler(element);
+    return formHandler ? formHandler.fieldDetails : null;
   },
 
   getAllFieldNames(element) {
     let formDetails = this.getFormDetails(element);
     return formDetails.map(record => record.fieldName);
   },
 
   identifyAutofillFields(doc) {
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -98,50 +98,35 @@ FormAutofillHandler.prototype = {
 
     log.debug("Collected details on", autofillData.length, "fields");
 
     return autofillData;
   },
 
   /**
    * Processes form fields that can be autofilled, and populates them with the
-   * data provided by backend.
+   * profile provided by backend.
    *
-   * @param {Array<Object>} autofillResult
-   *        Data returned by the user interface.
-   *        [{
-   *          section: Value originally provided to the user interface.
-   *          addressType: Value originally provided to the user interface.
-   *          contactType: Value originally provided to the user interface.
-   *          fieldName: Value originally provided to the user interface.
-   *          value: String with which the field should be updated.
-   *          index: Index to match the input in fieldDetails
-   *        }],
-   *        }
+   * @param {Object} profile
+   *        A profile to be filled in.
+   * @param {Object} focusedInput
+   *        A focused input element which is skipped for filling.
    */
-  autofillFormFields(autofillResult) {
-    log.debug("autofillFormFields:", autofillResult);
-    for (let field of autofillResult) {
-      // TODO: Skip filling the value of focused input which is filled in
-      // FormFillController.
+  autofillFormFields(profile, focusedInput) {
+    log.debug("profile in autofillFormFields:", profile);
+    for (let fieldDetail of this.fieldDetails) {
+      // Avoid filling field value in the following cases:
+      // 1. the focused input which is filled in FormFillController.
+      // 2. a non-empty input field
+      // 3. the invalid value set
 
-      // Get the field details, if it was processed by the user interface.
-      let fieldDetail = this.fieldDetails[field.index];
-
-      // Avoid the invalid value set
-      if (!fieldDetail || !field.value) {
+      if (fieldDetail.element === focusedInput ||
+          fieldDetail.element.value) {
         continue;
       }
 
-      let info = FormAutofillHeuristics.getInfo(fieldDetail.element);
-      if (!info ||
-          field.section != info.section ||
-          field.addressType != info.addressType ||
-          field.contactType != info.contactType ||
-          field.fieldName != info.fieldName) {
-        Cu.reportError("Autocomplete tokens mismatched");
-        continue;
+      let value = profile[fieldDetail.fieldName];
+      if (value) {
+        fieldDetail.element.setUserInput(value);
       }
-
-      fieldDetail.element.setUserInput(field.value);
     }
   },
 };
--- a/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
@@ -8,17 +8,17 @@ Cu.import("resource://formautofill/FormA
 
 const TESTCASES = [
   {
     description: "Form without autocomplete property",
     document: `<form><input id="given-name"><input id="family-name">
                <input id="street-addr"><input id="city"><input id="country">
                <input id='email'><input id="tel"></form>`,
     fieldDetails: [],
-    profileData: [],
+    profileData: {},
     expectedResult: {
       "street-addr": "",
       "city": "",
       "country": "",
       "email": "",
       "tel": "",
     },
   },
@@ -35,25 +35,23 @@ const TESTCASES = [
       {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "country", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "email", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "tel", "element": {}},
     ],
-    profileData: [
-      {"section": "", "addressType": "", "fieldName": "given-name", "contactType": "", "index": 0, "value": "foo"},
-      {"section": "", "addressType": "", "fieldName": "family-name", "contactType": "", "index": 1, "value": "bar"},
-      {"section": "", "addressType": "", "fieldName": "street-address", "contactType": "", "index": 2, "value": "2 Harrison St"},
-      {"section": "", "addressType": "", "fieldName": "address-level2", "contactType": "", "index": 3, "value": "San Francisco"},
-      {"section": "", "addressType": "", "fieldName": "country", "contactType": "", "index": 4, "value": "US"},
-      {"section": "", "addressType": "", "fieldName": "email", "contactType": "", "index": 5, "value": "foo@mozilla.com"},
-      {"section": "", "addressType": "", "fieldName": "tel", "contactType": "", "index": 6, "value": "1234567"},
-    ],
+    profileData: {
+      "street-address": "2 Harrison St",
+      "address-level2": "San Francisco",
+      "country": "US",
+      "email": "foo@mozilla.com",
+      "tel": "1234567",
+    },
     expectedResult: {
       "street-addr": "2 Harrison St",
       "city": "San Francisco",
       "country": "US",
       "email": "foo@mozilla.com",
       "tel": "1234567",
     },
   },
@@ -70,25 +68,23 @@ const TESTCASES = [
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
     ],
-    profileData: [
-      {"section": "", "addressType": "shipping", "fieldName": "given-name", "contactType": "", "index": 0, "value": "foo"},
-      {"section": "", "addressType": "shipping", "fieldName": "family-name", "contactType": "", "index": 1, "value": "bar"},
-      {"section": "", "addressType": "shipping", "fieldName": "street-address", "contactType": "", "index": 2, "value": "2 Harrison St"},
-      {"section": "", "addressType": "shipping", "fieldName": "address-level2", "contactType": "", "index": 3, "value": "San Francisco"},
-      {"section": "", "addressType": "shipping", "fieldName": "country", "contactType": "", "index": 4, "value": "US"},
-      {"section": "", "addressType": "shipping", "fieldName": "email", "contactType": "", "index": 5, "value": "foo@mozilla.com"},
-      {"section": "", "addressType": "shipping", "fieldName": "tel", "contactType": "", "index": 6, "value": "1234567"},
-    ],
+    profileData: {
+      "street-address": "2 Harrison St",
+      "address-level2": "San Francisco",
+      "country": "US",
+      "email": "foo@mozilla.com",
+      "tel": "1234567",
+    },
     expectedResult: {
       "street-addr": "2 Harrison St",
       "city": "San Francisco",
       "country": "US",
       "email": "foo@mozilla.com",
       "tel": "1234567",
     },
   },
@@ -105,25 +101,23 @@ const TESTCASES = [
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
     ],
-    profileData: [
-      {"section": "", "addressType": "shipping", "fieldName": "given-name", "contactType": "", "index": 0, "value": "foo"},
-      {"section": "", "addressType": "shipping", "fieldName": "family-name", "contactType": "", "index": 1, "value": "bar"},
-      {"section": "", "addressType": "shipping", "fieldName": "street-address", "contactType": "", "index": 2, "value": "2 Harrison St"},
-      {"section": "", "addressType": "shipping", "fieldName": "address-level2", "contactType": "", "index": 3, "value": "San Francisco"},
-      {"section": "", "addressType": "shipping", "fieldName": "country", "contactType": "", "index": 4, "value": "US"},
-      {"section": "", "addressType": "shipping", "fieldName": "email", "contactType": "", "index": 5},
-      {"section": "", "addressType": "shipping", "fieldName": "tel", "contactType": "", "index": 6},
-    ],
+    profileData: {
+      "street-address": "2 Harrison St",
+      "address-level2": "San Francisco",
+      "country": "US",
+      "email": "",
+      "tel": "",
+    },
     expectedResult: {
       "street-addr": "2 Harrison St",
       "city": "San Francisco",
       "country": "US",
       "email": "",
       "tel": "",
     },
   },
@@ -140,25 +134,23 @@ const TESTCASES = [
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
     ],
-    profileData: [
-      {"section": "", "addressType": "shipping", "fieldName": "given-name", "contactType": "", "index": 0, "value": "foo"},
-      {"section": "", "addressType": "shipping", "fieldName": "family-name", "contactType": "", "index": 1, "value": "bar"},
-      {"section": "", "addressType": "shipping", "fieldName": "street-address", "contactType": "", "index": 2, "value": "2 Harrison St"},
-      {"section": "", "addressType": "shipping", "fieldName": "address-level2", "contactType": "", "index": 3, "value": "San Francisco"},
-      {"section": "", "addressType": "shipping", "fieldName": "country", "contactType": "", "index": 4, "value": "US"},
-      {"section": "", "addressType": "shipping", "fieldName": "email", "contactType": "", "index": 5, "value": "foo@mozilla.com"},
-      {"section": "", "addressType": "shipping", "fieldName": "tel", "contactType": "", "index": 6, "value": "1234567"},
-    ],
+    profileData: {
+      "street-address": "",
+      "address-level2": "",
+      "country": "",
+      "email": "foo@mozilla.com",
+      "tel": "1234567",
+    },
     expectedResult: {
       "street-addr": "",
       "city": "",
       "country": "",
       "email": "foo@mozilla.com",
       "tel": "1234567",
     },
   },
@@ -169,22 +161,34 @@ for (let tc of TESTCASES) {
     let testcase = tc;
     add_task(function* () {
       do_print("Starting testcase: " + testcase.description);
 
       let doc = MockDocument.createTestDocument("http://localhost:8080/test/",
                                                 testcase.document);
       let form = doc.querySelector("form");
       let handler = new FormAutofillHandler(form);
+      let onChangePromises = [];
 
       handler.fieldDetails = testcase.fieldDetails;
       handler.fieldDetails.forEach((field, index) => {
-        field.element = doc.querySelectorAll("input")[index];
+        let element = doc.querySelectorAll("input")[index];
+        field.element = element;
+        if (!testcase.profileData[field.fieldName]) {
+          // Avoid waiting for `change` event of a input with a blank value to
+          // be filled.
+          return;
+        }
+        onChangePromises.push(new Promise(resolve => {
+          element.addEventListener("change", () => {
+            let id = element.id;
+            Assert.equal(element.value, testcase.expectedResult[id],
+                        "Check the " + id + " fields were filled with correct data");
+            resolve();
+          }, {once: true});
+        }));
       });
 
       handler.autofillFormFields(testcase.profileData);
-      for (let id in testcase.expectedResult) {
-        Assert.equal(doc.getElementById(id).value, testcase.expectedResult[id],
-                    "Check the " + id + " fields were filled with correct data");
-      }
+      yield Promise.all(onChangePromises);
     });
   })();
 }
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.7.312
+Current extension version is: 1.7.337
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -83,25 +83,26 @@ return /******/ (function(modules) { // 
 
 /******/ 	// Object.prototype.hasOwnProperty.call
 /******/ 	__w_pdfjs_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
 
 /******/ 	// __webpack_public_path__
 /******/ 	__w_pdfjs_require__.p = "";
 
 /******/ 	// Load entry module and return exports
-/******/ 	return __w_pdfjs_require__(__w_pdfjs_require__.s = 13);
+/******/ 	return __w_pdfjs_require__(__w_pdfjs_require__.s = 14);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 /* WEBPACK VAR INJECTION */(function(global) {
+var compatibility = __w_pdfjs_require__(13);
 var globalScope = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this;
 var FONT_IDENTITY_MATRIX = [
  0.001,
  0,
  0,
  0.001,
  0,
  0
@@ -1180,64 +1181,16 @@ function isNodeJS() {
 function createPromiseCapability() {
  var capability = {};
  capability.promise = new Promise(function (resolve, reject) {
   capability.resolve = resolve;
   capability.reject = reject;
  });
  return capability;
 }
-(function PromiseClosure() {
- if (globalScope.Promise) {
-  if (typeof globalScope.Promise.all !== 'function') {
-   globalScope.Promise.all = function (iterable) {
-    var count = 0, results = [], resolve, reject;
-    var promise = new globalScope.Promise(function (resolve_, reject_) {
-     resolve = resolve_;
-     reject = reject_;
-    });
-    iterable.forEach(function (p, i) {
-     count++;
-     p.then(function (result) {
-      results[i] = result;
-      count--;
-      if (count === 0) {
-       resolve(results);
-      }
-     }, reject);
-    });
-    if (count === 0) {
-     resolve(results);
-    }
-    return promise;
-   };
-  }
-  if (typeof globalScope.Promise.resolve !== 'function') {
-   globalScope.Promise.resolve = function (value) {
-    return new globalScope.Promise(function (resolve) {
-     resolve(value);
-    });
-   };
-  }
-  if (typeof globalScope.Promise.reject !== 'function') {
-   globalScope.Promise.reject = function (reason) {
-    return new globalScope.Promise(function (resolve, reject) {
-     reject(reason);
-    });
-   };
-  }
-  if (typeof globalScope.Promise.prototype.catch !== 'function') {
-   globalScope.Promise.prototype.catch = function (onReject) {
-    return globalScope.Promise.prototype.then(undefined, onReject);
-   };
-  }
-  return;
- }
- throw new Error('DOM Promise is not present');
-}());
 var StatTimer = function StatTimerClosure() {
  function rpad(str, pad, length) {
   while (str.length < length) {
    str += pad;
   }
   return str;
  }
  function StatTimer() {
@@ -1686,16 +1639,18 @@ function getDefaultSetting(id) {
  case 'disableWebGL':
   return globalSettings ? globalSettings.disableWebGL : true;
  case 'cMapUrl':
   return globalSettings ? globalSettings.cMapUrl : null;
  case 'cMapPacked':
   return globalSettings ? globalSettings.cMapPacked : false;
  case 'postMessageTransfers':
   return globalSettings ? globalSettings.postMessageTransfers : true;
+ case 'workerPort':
+  return globalSettings ? globalSettings.workerPort : null;
  case 'workerSrc':
   return globalSettings ? globalSettings.workerSrc : null;
  case 'disableWorker':
   return globalSettings ? globalSettings.disableWorker : false;
  case 'maxImageSize':
   return globalSettings ? globalSettings.maxImageSize : -1;
  case 'imageResourcesPath':
   return globalSettings ? globalSettings.imageResourcesPath : '';
@@ -2487,17 +2442,18 @@ function getDocument(src, pdfDataRangeTr
    continue;
   }
   params[key] = source[key];
  }
  params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
  params.disableNativeImageDecoder = params.disableNativeImageDecoder === true;
  var CMapReaderFactory = params.CMapReaderFactory || DOMCMapReaderFactory;
  if (!worker) {
-  worker = new PDFWorker();
+  var workerPort = getDefaultSetting('workerPort');
+  worker = workerPort ? new PDFWorker(null, workerPort) : new PDFWorker();
   task._worker = worker;
  }
  var docId = task.docId;
  worker.promise.then(function () {
   if (task.destroyed) {
    throw new Error('Loading aborted');
   }
   return _fetchDocument(worker, params, rangeTransport, docId).then(function (workerId) {
@@ -2997,35 +2953,46 @@ var PDFWorker = function PDFWorkerClosur
   terminate: function () {
    this._listeners = [];
   }
  };
  function createCDNWrapper(url) {
   var wrapper = 'importScripts(\'' + url + '\');';
   return URL.createObjectURL(new Blob([wrapper]));
  }
- function PDFWorker(name) {
+ function PDFWorker(name, port) {
   this.name = name;
   this.destroyed = false;
   this._readyCapability = createPromiseCapability();
   this._port = null;
   this._webWorker = null;
   this._messageHandler = null;
+  if (port) {
+   this._initializeFromPort(port);
+   return;
+  }
   this._initialize();
  }
  PDFWorker.prototype = {
   get promise() {
    return this._readyCapability.promise;
   },
   get port() {
    return this._port;
   },
   get messageHandler() {
    return this._messageHandler;
   },
+  _initializeFromPort: function PDFWorker_initializeFromPort(port) {
+   this._port = port;
+   this._messageHandler = new MessageHandler('main', 'worker', port);
+   this._messageHandler.on('ready', function () {
+   });
+   this._readyCapability.resolve();
+  },
   _initialize: function PDFWorker_initialize() {
    if (!isWorkerDisabled && !getDefaultSetting('disableWorker') && typeof Worker !== 'undefined') {
     var workerSrc = getWorkerSrc();
     try {
      var worker = new Worker(workerSrc);
      var messageHandler = new MessageHandler('main', 'worker', worker);
      var terminateEarly = function () {
       worker.removeEventListener('error', onWorkerError);
@@ -3695,18 +3662,18 @@ var _UnsupportedManager = function Unsup
   },
   notify: function (featureId) {
    for (var i = 0, ii = listeners.length; i < ii; i++) {
     listeners[i](featureId);
    }
   }
  };
 }();
-exports.version = '1.7.312';
-exports.build = 'cada411a';
+exports.version = '1.7.337';
+exports.build = '9163a6fb';
 exports.getDocument = getDocument;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports._UnsupportedManager = _UnsupportedManager;
 
 /***/ }),
@@ -4713,18 +4680,18 @@ var deprecated = sharedUtil.deprecated;
 var warn = sharedUtil.warn;
 var LinkTarget = displayDOMUtils.LinkTarget;
 var DEFAULT_LINK_REL = displayDOMUtils.DEFAULT_LINK_REL;
 var isWorker = typeof window === 'undefined';
 if (!globalScope.PDFJS) {
  globalScope.PDFJS = {};
 }
 var PDFJS = globalScope.PDFJS;
-PDFJS.version = '1.7.312';
-PDFJS.build = 'cada411a';
+PDFJS.version = '1.7.337';
+PDFJS.build = '9163a6fb';
 PDFJS.pdfBug = false;
 if (PDFJS.verbosity !== undefined) {
  sharedUtil.setVerbosityLevel(PDFJS.verbosity);
 }
 delete PDFJS.verbosity;
 Object.defineProperty(PDFJS, 'verbosity', {
  get: function () {
   return sharedUtil.getVerbosityLevel();
@@ -4763,16 +4730,17 @@ PDFJS.PageViewport = sharedUtil.PageView
 PDFJS.createPromiseCapability = sharedUtil.createPromiseCapability;
 PDFJS.maxImageSize = PDFJS.maxImageSize === undefined ? -1 : PDFJS.maxImageSize;
 PDFJS.cMapUrl = PDFJS.cMapUrl === undefined ? null : PDFJS.cMapUrl;
 PDFJS.cMapPacked = PDFJS.cMapPacked === undefined ? false : PDFJS.cMapPacked;
 PDFJS.disableFontFace = PDFJS.disableFontFace === undefined ? false : PDFJS.disableFontFace;
 PDFJS.imageResourcesPath = PDFJS.imageResourcesPath === undefined ? '' : PDFJS.imageResourcesPath;
 PDFJS.disableWorker = PDFJS.disableWorker === undefined ? false : PDFJS.disableWorker;
 PDFJS.workerSrc = PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc;
+PDFJS.workerPort = PDFJS.workerPort === undefined ? null : PDFJS.workerPort;
 PDFJS.disableRange = PDFJS.disableRange === undefined ? false : PDFJS.disableRange;
 PDFJS.disableStream = PDFJS.disableStream === undefined ? false : PDFJS.disableStream;
 PDFJS.disableAutoFetch = PDFJS.disableAutoFetch === undefined ? false : PDFJS.disableAutoFetch;
 PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug;
 PDFJS.postMessageTransfers = PDFJS.postMessageTransfers === undefined ? true : PDFJS.postMessageTransfers;
 PDFJS.disableCreateObjectURL = PDFJS.disableCreateObjectURL === undefined ? false : PDFJS.disableCreateObjectURL;
 PDFJS.disableWebGL = PDFJS.disableWebGL === undefined ? true : PDFJS.disableWebGL;
 PDFJS.externalLinkTarget = PDFJS.externalLinkTarget === undefined ? LinkTarget.NONE : PDFJS.externalLinkTarget;
@@ -7206,18 +7174,25 @@ exports.getShadingPatternFromIR = getSha
 exports.TilingPattern = TilingPattern;
 
 /***/ }),
 /* 13 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
-var pdfjsVersion = '1.7.312';
-var pdfjsBuild = 'cada411a';
+
+/***/ }),
+/* 14 */
+/***/ (function(module, exports, __w_pdfjs_require__) {
+
+"use strict";
+
+var pdfjsVersion = '1.7.337';
+var pdfjsBuild = '9163a6fb';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayGlobal = __w_pdfjs_require__(8);
 var pdfjsDisplayAPI = __w_pdfjs_require__(3);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(5);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(2);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(4);
 exports.PDFJS = pdfjsDisplayGlobal.PDFJS;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -83,25 +83,26 @@ return /******/ (function(modules) { // 
 
 /******/ 	// Object.prototype.hasOwnProperty.call
 /******/ 	__w_pdfjs_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
 
 /******/ 	// __webpack_public_path__
 /******/ 	__w_pdfjs_require__.p = "";
 
 /******/ 	// Load entry module and return exports
-/******/ 	return __w_pdfjs_require__(__w_pdfjs_require__.s = 35);
+/******/ 	return __w_pdfjs_require__(__w_pdfjs_require__.s = 36);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 /* WEBPACK VAR INJECTION */(function(global) {
+var compatibility = __w_pdfjs_require__(35);
 var globalScope = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this;
 var FONT_IDENTITY_MATRIX = [
  0.001,
  0,
  0,
  0.001,
  0,
  0
@@ -1180,64 +1181,16 @@ function isNodeJS() {
 function createPromiseCapability() {
  var capability = {};
  capability.promise = new Promise(function (resolve, reject) {
   capability.resolve = resolve;
   capability.reject = reject;
  });
  return capability;
 }
-(function PromiseClosure() {
- if (globalScope.Promise) {
-  if (typeof globalScope.Promise.all !== 'function') {
-   globalScope.Promise.all = function (iterable) {
-    var count = 0, results = [], resolve, reject;
-    var promise = new globalScope.Promise(function (resolve_, reject_) {
-     resolve = resolve_;
-     reject = reject_;
-    });
-    iterable.forEach(function (p, i) {
-     count++;
-     p.then(function (result) {
-      results[i] = result;
-      count--;
-      if (count === 0) {
-       resolve(results);
-      }
-     }, reject);
-    });
-    if (count === 0) {
-     resolve(results);
-    }
-    return promise;
-   };
-  }
-  if (typeof globalScope.Promise.resolve !== 'function') {
-   globalScope.Promise.resolve = function (value) {
-    return new globalScope.Promise(function (resolve) {
-     resolve(value);
-    });
-   };
-  }
-  if (typeof globalScope.Promise.reject !== 'function') {
-   globalScope.Promise.reject = function (reason) {
-    return new globalScope.Promise(function (resolve, reject) {
-     reject(reason);
-    });
-   };
-  }
-  if (typeof globalScope.Promise.prototype.catch !== 'function') {
-   globalScope.Promise.prototype.catch = function (onReject) {
-    return globalScope.Promise.prototype.then(undefined, onReject);
-   };
-  }
-  return;
- }
- throw new Error('DOM Promise is not present');
-}());
 var StatTimer = function StatTimerClosure() {
  function rpad(str, pad, length) {
   while (str.length < length) {
    str += pad;
   }
   return str;
  }
  function StatTimer() {
@@ -30238,17 +30191,16 @@ var Catalog = function CatalogClosure() 
    }
    return this.pagePromises[pageIndex];
   },
   getPageDict: function Catalog_getPageDict(pageIndex) {
    var capability = createPromiseCapability();
    var nodesToVisit = [this.catDict.getRaw('Pages')];
    var currentPageIndex = 0;
    var xref = this.xref;
-   var checkAllKids = false;
    function next() {
     while (nodesToVisit.length) {
      var currentNode = nodesToVisit.pop();
      if (isRef(currentNode)) {
       xref.fetchAsync(currentNode).then(function (obj) {
        if (isDict(obj, 'Page') || isDict(obj) && !obj.has('Kids')) {
         if (pageIndex === currentPageIndex) {
          capability.resolve([
@@ -30263,33 +30215,24 @@ var Catalog = function CatalogClosure() 
        }
        nodesToVisit.push(obj);
        next();
       }, capability.reject);
       return;
      }
      assert(isDict(currentNode), 'page dictionary kid reference points to wrong type of object');
      var count = currentNode.get('Count');
-     if (count === 0) {
-      checkAllKids = true;
-     }
      if (currentPageIndex + count <= pageIndex) {
       currentPageIndex += count;
       continue;
      }
      var kids = currentNode.get('Kids');
      assert(isArray(kids), 'page dictionary kids object is not an array');
-     if (!checkAllKids && count === kids.length) {
-      nodesToVisit = [kids[pageIndex - currentPageIndex]];
-      currentPageIndex = pageIndex;
-      continue;
-     } else {
-      for (var last = kids.length - 1; last >= 0; last--) {
-       nodesToVisit.push(kids[last]);
-      }
+     for (var last = kids.length - 1; last >= 0; last--) {
+      nodesToVisit.push(kids[last]);
      }
     }
     capability.reject('Page index ' + pageIndex + ' not found.');
    }
    next();
    return capability.promise;
   },
   getPageIndex: function Catalog_getPageIndex(pageRef) {
@@ -30441,20 +30384,23 @@ var Catalog = function CatalogClosure() 
     } else if (isString(jsAction)) {
      js = jsAction;
     }
     if (js) {
      var URL_OPEN_METHODS = [
       'app.launchURL',
       'window.open'
      ];
-     var regex = new RegExp('^(?:' + URL_OPEN_METHODS.join('|') + ')' + '\\((?:\'|\")(\\S+)(?:\'|\")(?:,|\\))');
-     var jsUrl = regex.exec(stringToPDFString(js), 'i');
-     if (jsUrl && jsUrl[1]) {
-      url = jsUrl[1];
+     var regex = new RegExp('^\\s*(' + URL_OPEN_METHODS.join('|').split('.').join('\\.') + ')\\((?:\'|\")([^\'\"]*)(?:\'|\")(?:,\\s*(\\w+)\\)|\\))', 'i');
+     var jsUrl = regex.exec(stringToPDFString(js));
+     if (jsUrl && jsUrl[2]) {
+      url = jsUrl[2];
+      if (jsUrl[3] === 'true' && jsUrl[1] === 'app.launchURL') {
+       resultObj.newWindow = true;
+      }
       break;
      }
     }
    default:
     warn('Catalog_parseDestDictionary: Unrecognized link type "' + linkType + '".');
     break;
    }
   } else if (destDict.has('Dest')) {
@@ -30887,16 +30833,19 @@ var XRef = function XRefClosure() {
    }
    return this.fetch(obj, suppressEncryption);
   },
   fetch: function XRef_fetch(ref, suppressEncryption) {
    assert(isRef(ref), 'ref object is not a reference');
    var num = ref.num;
    if (num in this.cache) {
     var cacheEntry = this.cache[num];
+    if (isDict(cacheEntry) && !cacheEntry.objId) {
+     cacheEntry.objId = ref.toString();
+    }
     return cacheEntry;
    }
    var xrefEntry = this.getEntry(num);
    if (xrefEntry === null) {
     return this.cache[num] = null;
    }
    if (xrefEntry.uncompressed) {
     xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
@@ -33957,25 +33906,23 @@ var MissingPDFException = sharedUtil.Mis
 var UnexpectedResponseException = sharedUtil.UnexpectedResponseException;
 var PasswordException = sharedUtil.PasswordException;
 var UnknownErrorException = sharedUtil.UnknownErrorException;
 var XRefParseException = sharedUtil.XRefParseException;
 var arrayByteLength = sharedUtil.arrayByteLength;
 var arraysToBytes = sharedUtil.arraysToBytes;
 var assert = sharedUtil.assert;
 var createPromiseCapability = sharedUtil.createPromiseCapability;
-var error = sharedUtil.error;
 var info = sharedUtil.info;
 var warn = sharedUtil.warn;
 var setVerbosityLevel = sharedUtil.setVerbosityLevel;
 var isNodeJS = sharedUtil.isNodeJS;
 var Ref = corePrimitives.Ref;
 var LocalPdfManager = corePdfManager.LocalPdfManager;
 var NetworkPdfManager = corePdfManager.NetworkPdfManager;
-var globalScope = sharedUtil.globalScope;
 var WorkerTask = function WorkerTaskClosure() {
  function WorkerTask(name) {
   this.name = name;
   this.terminated = false;
   this._capability = createPromiseCapability();
  }
  WorkerTask.prototype = {
   get finished() {
@@ -34960,31 +34907,16 @@ var Annotation = function AnnotationClos
     return evaluator.getOperatorList(self.appearance, task, resources, opList).then(function () {
      opList.addOp(OPS.endAnnotation, []);
      self.appearance.reset();
      return opList;
     });
    });
   }
  };
- Annotation.appendToOperatorList = function Annotation_appendToOperatorList(annotations, opList, partialEvaluator, task, intent, renderForms) {
-  var annotationPromises = [];
-  for (var i = 0, n = annotations.length; i < n; ++i) {
-   if (intent === 'display' && annotations[i].viewable || intent === 'print' && annotations[i].printable) {
-    annotationPromises.push(annotations[i].getOperatorList(partialEvaluator, task, renderForms));
-   }
-  }
-  return Promise.all(annotationPromises).then(function (operatorLists) {
-   opList.addOp(OPS.beginAnnotations, []);
-   for (var i = 0, n = operatorLists.length; i < n; ++i) {
-    opList.addOpList(operatorLists[i]);
-   }
-   opList.addOp(OPS.endAnnotations, []);
-  });
- };
  return Annotation;
 }();
 var AnnotationBorderStyle = function AnnotationBorderStyleClosure() {
  function AnnotationBorderStyle() {
   this.width = 1;
   this.style = AnnotationBorderStyleType.SOLID;
   this.dashArray = [3];
   this.horizontalCornerRadius = 0;
@@ -35193,17 +35125,17 @@ var ButtonWidgetAnnotation = function Bu
   }
  });
  return ButtonWidgetAnnotation;
 }();
 var ChoiceWidgetAnnotation = function ChoiceWidgetAnnotationClosure() {
  function ChoiceWidgetAnnotation(params) {
   WidgetAnnotation.call(this, params);
   this.data.options = [];
-  var options = params.dict.get('Opt');
+  var options = Util.getInheritableProperty(params.dict, 'Opt');
   if (isArray(options)) {
    var xref = params.xref;
    for (var i = 0, ii = options.length; i < ii; i++) {
     var option = xref.fetchIfRef(options[i]);
     var isOptionArray = isArray(option);
     this.data.options[i] = {
      exportValue: isOptionArray ? xref.fetchIfRef(option[0]) : option,
      displayValue: isOptionArray ? xref.fetchIfRef(option[1]) : option
@@ -37478,16 +37410,17 @@ exports.IdentityCMap = IdentityCMap;
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
 var coreStream = __w_pdfjs_require__(2);
 var coreObj = __w_pdfjs_require__(14);
 var coreParser = __w_pdfjs_require__(5);
 var coreCrypto = __w_pdfjs_require__(11);
 var coreEvaluator = __w_pdfjs_require__(12);
 var coreAnnotation = __w_pdfjs_require__(19);
+var OPS = sharedUtil.OPS;
 var MissingDataException = sharedUtil.MissingDataException;
 var Util = sharedUtil.Util;
 var assert = sharedUtil.assert;
 var error = sharedUtil.error;
 var info = sharedUtil.info;
 var isArray = sharedUtil.isArray;
 var isArrayBuffer = sharedUtil.isArrayBuffer;
 var isNum = sharedUtil.isNum;
@@ -37506,26 +37439,28 @@ var Stream = coreStream.Stream;
 var StreamsSequenceStream = coreStream.StreamsSequenceStream;
 var Catalog = coreObj.Catalog;
 var ObjectLoader = coreObj.ObjectLoader;
 var XRef = coreObj.XRef;
 var Linearization = coreParser.Linearization;
 var calculateMD5 = coreCrypto.calculateMD5;
 var OperatorList = coreEvaluator.OperatorList;
 var PartialEvaluator = coreEvaluator.PartialEvaluator;
-var Annotation = coreAnnotation.Annotation;
 var AnnotationFactory = coreAnnotation.AnnotationFactory;
 var Page = function PageClosure() {
  var DEFAULT_USER_UNIT = 1.0;
  var LETTER_SIZE_MEDIABOX = [
   0,
   0,
   612,
   792
  ];
+ function isAnnotationRenderable(annotation, intent) {
+  return intent === 'display' && annotation.viewable || intent === 'print' && annotation.printable;
+ }
  function Page(pdfManager, xref, pageIndex, pageDict, ref, fontCache, builtInCMapCache) {
   this.pdfManager = pdfManager;
   this.pageIndex = pageIndex;
   this.pageDict = pageDict;
   this.xref = xref;
   this.ref = ref;
   this.fontCache = fontCache;
   this.builtInCMapCache = builtInCMapCache;
@@ -37677,18 +37612,28 @@ var Page = function PageClosure() {
     annotationsPromise
    ]).then(function (datas) {
     var pageOpList = datas[0];
     var annotations = datas[1];
     if (annotations.length === 0) {
      pageOpList.flush(true);
      return pageOpList;
     }
-    var annotationsReadyPromise = Annotation.appendToOperatorList(annotations, pageOpList, partialEvaluator, task, intent, renderInteractiveForms);
-    return annotationsReadyPromise.then(function () {
+    var i, ii, opListPromises = [];
+    for (i = 0, ii = annotations.length; i < ii; i++) {
+     if (isAnnotationRenderable(annotations[i], intent)) {
+      opListPromises.push(annotations[i].getOperatorList(partialEvaluator, task, renderInteractiveForms));
+     }
+    }
+    return Promise.all(opListPromises).then(function (opLists) {
+     pageOpList.addOp(OPS.beginAnnotations, []);
+     for (i = 0, ii = opLists.length; i < ii; i++) {
+      pageOpList.addOpList(opLists[i]);
+     }
+     pageOpList.addOp(OPS.endAnnotations, []);
      pageOpList.flush(true);
      return pageOpList;
     });
    });
   },
   extractTextContent: function Page_extractTextContent(task, normalizeWhitespace, combineTextItems) {
    var handler = {
     on: function nullHandlerOn() {
@@ -37713,22 +37658,19 @@ var Page = function PageClosure() {
     var partialEvaluator = new PartialEvaluator(pdfManager, self.xref, handler, self.pageIndex, self.idFactory, self.fontCache, self.builtInCMapCache, self.evaluatorOptions);
     return partialEvaluator.getTextContent(contentStream, task, self.resources, null, normalizeWhitespace, combineTextItems);
    });
   },
   getAnnotationsData: function Page_getAnnotationsData(intent) {
    var annotations = this.annotations;
    var annotationsData = [];
    for (var i = 0, n = annotations.length; i < n; ++i) {
-    if (intent) {
-     if (!(intent === 'display' && annotations[i].viewable) && !(intent === 'print' && annotations[i].printable)) {
-      continue;
-     }
-    }
-    annotationsData.push(annotations[i].data);
+    if (!intent || isAnnotationRenderable(annotations[i], intent)) {
+     annotationsData.push(annotations[i].data);
+    }
    }
    return annotationsData;
   },
   get annotations() {
    var annotations = [];
    var annotationRefs = this.getInheritedPageProp('Annots') || [];
    var annotationFactory = new AnnotationFactory();
    for (var i = 0, n = annotationRefs.length; i < n; ++i) {
@@ -49171,17 +49113,24 @@ var Type1Parser = function Type1ParserCl
 exports.Type1Parser = Type1Parser;
 
 /***/ }),
 /* 35 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
-var pdfjsVersion = '1.7.312';
-var pdfjsBuild = 'cada411a';
+
+/***/ }),
+/* 36 */
+/***/ (function(module, exports, __w_pdfjs_require__) {
+
+"use strict";
+
+var pdfjsVersion = '1.7.337';
+var pdfjsBuild = '9163a6fb';
 var pdfjsCoreWorker = __w_pdfjs_require__(17);
 ;
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ })
 /******/ ]);
 });
\ No newline at end of file
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -497,17 +497,25 @@ exports.localized = localized;
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 {
- module.exports = window['pdfjs-dist/build/pdf'];
+ var pdfjsLib;
+ if (typeof __pdfjsdev_webpack__ === 'undefined') {
+  if (typeof require === 'function') {
+   pdfjsLib = require('../build/pdf.js');
+  } else {
+   pdfjsLib = window['pdfjs-dist/build/pdf'];
+  }
+ }
+ module.exports = pdfjsLib;
 }
 
 /***/ }),
 /* 2 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
--- a/browser/extensions/pocket/content/main.js
+++ b/browser/extensions/pocket/content/main.js
@@ -37,17 +37,17 @@
 
 // TODO : Get the toolbar icons from Firefox's build (Nikki needs to give us a red saved icon)
 // TODO : [needs clarificaiton from Fx] Firefox's plan was to hide Pocket from context menus until the user logs in. Now that it's an extension I'm wondering if we still need to do this.
 // TODO : [needs clarificaiton from Fx] Reader mode (might be a something they need to do since it's in html, need to investigate their code)
 // TODO : [needs clarificaiton from Fx] Move prefs within pktApi.s to sqlite or a local file so it's not editable (and is safer)
 // TODO : [nice to have] - Immediately save, buffer the actions in a local queue and send (so it works offline, works like our native extensions)
 
 /* eslint-disable no-shadow */
-/* eslint "mozilla/import-browserjs-globals": "error" */
+/* eslint-env mozilla/browser-window */
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
   "resource://gre/modules/ReaderMode.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "pktApi",
   "chrome://pocket/content/pktApi.jsm");
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -133,23 +133,16 @@ endif
 DEFINES += -DMOZ_ICU_DBG_SUFFIX=$(MOZ_ICU_DBG_SUFFIX)
 DEFINES += -DICU_DATA_FILE=$(ICU_DATA_FILE)
 ifdef CLANG_CXX
 DEFINES += -DCLANG_CXX
 endif
 ifdef CLANG_CL
 DEFINES += -DCLANG_CL
 endif
-ifeq (x86,$(CPU_ARCH))
-ifdef _MSC_VER
-ifndef CLANG_CL
-DEFINES += -DWOW_HELPER
-endif
-endif
-endif
 
 
 # Builds using the hybrid FasterMake/RecursiveMake backend will
 # fail to produce a langpack. See bug 1255096.
 libs::
 ifeq (,$(filter FasterMake+RecursiveMake,$(BUILD_BACKENDS)))
 	$(MAKE) -C $(DEPTH)/browser/locales langpack
 endif
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -722,24 +722,16 @@
 #endif
 @RESPATH@/chrome/pippki@JAREXT@
 @RESPATH@/chrome/pippki.manifest
 @RESPATH@/components/pipnss.xpt
 @RESPATH@/components/pippki.xpt
 
 ; For process sandboxing
 #if defined(MOZ_SANDBOX)
-#if defined(XP_WIN)
-#if defined(WOW_HELPER)
-@BINPATH@/wow_helper.exe
-#endif
-#endif
-#endif
-
-#if defined(MOZ_SANDBOX)
 #if defined(XP_LINUX)
 @BINPATH@/@DLL_PREFIX@mozsandbox@DLL_SUFFIX@
 @RESPATH@/components/sandbox.xpt
 #endif
 #endif
 
 ; for Solaris SPARC
 #ifdef SOLARIS
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -382,58 +382,22 @@ Section "-Application" APP_IDX
                                  "${AppRegName} Document" ""
   ${AddDisabledDDEHandlerValues} "FirefoxURL-$AppUserModelID" "$2" "$8,1" \
                                  "${AppRegName} URL" "true"
 
   ; For pre win8, the following keys should only be set if we can write to HKLM.
   ; For post win8, the keys below can be set in HKCU if needed.
   ${If} $TmpVal == "HKLM"
     ; Set the Start Menu Internet and Registered App HKLM registry keys.
-    ; If we're upgrading an existing install, replacing the old registry entries
-    ; (without a path hash) would cause the default browser to be reset.
-    ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\FIREFOX.EXE\DefaultIcon" ""
-    StrCpy $0 $0 -2
-    ${If} $0 != "$INSTDIR\${FileMainEXE}"
-      ${SetStartMenuInternet} "HKLM"
-      ${FixShellIconHandler} "HKLM"
-    ${EndIf}
-
-    ; If we are writing to HKLM and create either the desktop or start menu
-    ; shortcuts set IconsVisible to 1 otherwise to 0.
-    ; Taskbar shortcuts imply having a start menu shortcut.
-    StrCpy $0 "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID\InstallInfo"
-    ${If} $AddDesktopSC == 1
-    ${OrIf} $AddStartMenuSC == 1
-    ${OrIf} $AddTaskbarSC == 1
-      WriteRegDWORD HKLM "$0" "IconsVisible" 1
-    ${Else}
-      WriteRegDWORD HKLM "$0" "IconsVisible" 0
-    ${EndIf}
+    ${SetStartMenuInternet} "HKLM"
+    ${FixShellIconHandler} "HKLM"
   ${ElseIf} ${AtLeastWin8}
     ; Set the Start Menu Internet and Registered App HKCU registry keys.
-    ; If we're upgrading an existing install, replacing the old registry entries
-    ; (without a path hash) would cause the default browser to be reset.
-    ReadRegStr $0 HKCU "Software\Clients\StartMenuInternet\FIREFOX.EXE\DefaultIcon" ""
-    StrCpy $0 $0 -2
-    ${If} $0 != "$INSTDIR\${FileMainEXE}"
-      ${SetStartMenuInternet} "HKCU"
-      ${FixShellIconHandler} "HKCU"
-    ${EndIf}
-
-    ; If we create either the desktop or start menu shortcuts, then
-    ; set IconsVisible to 1 otherwise to 0.
-    ; Taskbar shortcuts imply having a start menu shortcut.
-    StrCpy $0 "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID\InstallInfo"
-    ${If} $AddDesktopSC == 1
-    ${OrIf} $AddStartMenuSC == 1
-    ${OrIf} $AddTaskbarSC == 1
-      WriteRegDWORD HKCU "$0" "IconsVisible" 1
-    ${Else}
-      WriteRegDWORD HKCU "$0" "IconsVisible" 0
-    ${EndIf}
+    ${SetStartMenuInternet} "HKCU"
+    ${FixShellIconHandler} "HKCU"
   ${EndIf}
 
 !ifdef MOZ_MAINTENANCE_SERVICE
   ; If the maintenance service page was displayed then a value was already
   ; explicitly selected for installing the maintenance service and
   ; and so InstallMaintenanceService will already be 0 or 1.
   ; If the maintenance service page was not displayed then
   ; InstallMaintenanceService will be equal to "".
--- a/browser/installer/windows/nsis/shared.nsh
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -145,27 +145,30 @@
 !macro SetAsDefaultAppGlobal
   ${RemoveDeprecatedKeys} ; Does not use SHCTX
 
   SetShellVarContext all      ; Set SHCTX to all users (e.g. HKLM)
   ${SetHandlers} ; Uses SHCTX
   ${SetStartMenuInternet} "HKLM"
   ${FixShellIconHandler} "HKLM"
   ${ShowShortcuts}
-  ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
-  WriteRegStr HKLM "Software\Clients\StartMenuInternet" "" "$R9-$AppUserModelID"
 !macroend
 !define SetAsDefaultAppGlobal "!insertmacro SetAsDefaultAppGlobal"
 
 ; Removes shortcuts for this installation. This should also remove the
 ; application from Open With for the file types the application handles
 ; (bug 370480).
 !macro HideShortcuts
-  ${StrFilter} "${FileMainEXE}" "+" "" "" $0
-  StrCpy $R1 "Software\Clients\StartMenuInternet\$0\InstallInfo"
+  ; Find the correct registry path to clear IconsVisible.
+  StrCpy $R1 "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID\InstallInfo"
+  ReadRegDWORD $0 HKLM "$R1" "ShowIconsCommand"
+  ${If} ${Errors}
+    ${StrFilter} "${FileMainEXE}" "+" "" "" $0
+    StrCpy $R1 "Software\Clients\StartMenuInternet\$0\InstallInfo"
+  ${EndIf}
   WriteRegDWORD HKLM "$R1" "IconsVisible" 0
   ${If} ${AtLeastWin8}
     WriteRegDWORD HKCU "$R1" "IconsVisible" 0
   ${EndIf}
 
   SetShellVarContext all  ; Set $DESKTOP to All Users
   ${Unless} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
     SetShellVarContext current  ; Set $DESKTOP to the current user's desktop
@@ -216,17 +219,23 @@
     ${EndIf}
   ${EndIf}
 !macroend
 !define HideShortcuts "!insertmacro HideShortcuts"
 
 ; Adds shortcuts for this installation. This should also add the application
 ; to Open With for the file types the application handles (bug 370480).
 !macro ShowShortcuts
+  ; Find the correct registry path to set IconsVisible.
   StrCpy $R1 "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID\InstallInfo"
+  ReadRegDWORD $0 HKLM "$R1" "ShowIconsCommand"
+  ${If} ${Errors}
+    ${StrFilter} "${FileMainEXE}" "+" "" "" $0
+    StrCpy $R1 "Software\Clients\StartMenuInternet\$0\InstallInfo"
+  ${EndIf}
   WriteRegDWORD HKLM "$R1" "IconsVisible" 1
   ${If} ${AtLeastWin8}
     WriteRegDWORD HKCU "$R1" "IconsVisible" 1
   ${EndIf}
 
   SetShellVarContext all  ; Set $DESKTOP to All Users
   ${Unless} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
     CreateShortCut "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
@@ -287,78 +296,87 @@
     ${If} ${FileExists} "$QUICKLAUNCH\${BrandFullName}.lnk"
       ShellLink::SetShortCutWorkingDirectory "$QUICKLAUNCH\${BrandFullName}.lnk" \
                                              "$INSTDIR"
     ${EndIf}
   ${EndUnless}
 !macroend
 !define ShowShortcuts "!insertmacro ShowShortcuts"
 
-!macro AddAssociationIfNoneExist FILE_TYPE
+!macro AddAssociationIfNoneExist FILE_TYPE KEY
   ClearErrors
   EnumRegKey $7 HKCR "${FILE_TYPE}" 0
   ${If} ${Errors}
-    WriteRegStr SHCTX "SOFTWARE\Classes\${FILE_TYPE}"  "" "FirefoxHTML-$AppUserModelID"
+    WriteRegStr SHCTX "SOFTWARE\Classes\${FILE_TYPE}"  "" ${KEY}
   ${EndIf}
-  WriteRegStr SHCTX "SOFTWARE\Classes\${FILE_TYPE}\OpenWithProgids" "FirefoxHTML-$AppUserModelID" ""
+  WriteRegStr SHCTX "SOFTWARE\Classes\${FILE_TYPE}\OpenWithProgids" ${KEY} ""
 !macroend
 !define AddAssociationIfNoneExist "!insertmacro AddAssociationIfNoneExist"
 
 ; Adds the protocol and file handler registry entries for making Firefox the
 ; default handler (uses SHCTX).
 !macro SetHandlers
   ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
 
+  ; See if we're using path hash suffixed registry keys for this install.
+  StrCpy $5 ""
+  ${StrFilter} "${FileMainEXE}" "+" "" "" $2
+  ReadRegStr $0 SHCTX "Software\Clients\StartMenuInternet\$2\DefaultIcon" ""
+  StrCpy $0 $0 -2
+  ${If} $0 != $8
+    StrCpy $5 "-$AppUserModelID"
+  ${EndIf}
+
   StrCpy $0 "SOFTWARE\Classes"
   StrCpy $2 "$\"$8$\" -osint -url $\"%1$\""
 
   ; Associate the file handlers with FirefoxHTML, if they aren't already.
   ReadRegStr $6 SHCTX "$0\.htm" ""
   ${WordFind} "$6" "-" "+1{" $6
   ${If} "$6" != "FirefoxHTML"
-    WriteRegStr SHCTX "$0\.htm"   "" "FirefoxHTML-$AppUserModelID"
+    WriteRegStr SHCTX "$0\.htm"   "" "FirefoxHTML$5"
   ${EndIf}
 
   ReadRegStr $6 SHCTX "$0\.html" ""
   ${WordFind} "$6" "-" "+1{" $6
   ${If} "$6" != "FirefoxHTML"
-    WriteRegStr SHCTX "$0\.html"  "" "FirefoxHTML-$AppUserModelID"
+    WriteRegStr SHCTX "$0\.html"  "" "FirefoxHTML$5"
   ${EndIf}
 
   ReadRegStr $6 SHCTX "$0\.shtml" ""
   ${WordFind} "$6" "-" "+1{" $6
   ${If} "$6" != "FirefoxHTML"
-    WriteRegStr SHCTX "$0\.shtml" "" "FirefoxHTML-$AppUserModelID"
+    WriteRegStr SHCTX "$0\.shtml" "" "FirefoxHTML$5"
   ${EndIf}
 
   ReadRegStr $6 SHCTX "$0\.xht" ""
   ${WordFind} "$6" "-" "+1{" $6
   ${If} "$6" != "FirefoxHTML"
-    WriteRegStr SHCTX "$0\.xht"   "" "FirefoxHTML-$AppUserModelID"
+    WriteRegStr SHCTX "$0\.xht"   "" "FirefoxHTML$5"
   ${EndIf}
 
   ReadRegStr $6 SHCTX "$0\.xhtml" ""
   ${WordFind} "$6" "-" "+1{" $6
   ${If} "$6" != "FirefoxHTML"
-    WriteRegStr SHCTX "$0\.xhtml" "" "FirefoxHTML-$AppUserModelID"
+    WriteRegStr SHCTX "$0\.xhtml" "" "FirefoxHTML$5"
   ${EndIf}
 
-  ${AddAssociationIfNoneExist} ".pdf"
-  ${AddAssociationIfNoneExist} ".oga"
-  ${AddAssociationIfNoneExist} ".ogg"
-  ${AddAssociationIfNoneExist} ".ogv"
-  ${AddAssociationIfNoneExist} ".pdf"
-  ${AddAssociationIfNoneExist} ".webm"
+  ${AddAssociationIfNoneExist} ".pdf" "FirefoxHTML$5"
+  ${AddAssociationIfNoneExist} ".oga" "FirefoxHTML$5"
+  ${AddAssociationIfNoneExist} ".ogg" "FirefoxHTML$5"
+  ${AddAssociationIfNoneExist} ".ogv" "FirefoxHTML$5"
+  ${AddAssociationIfNoneExist} ".pdf" "FirefoxHTML$5"
+  ${AddAssociationIfNoneExist} ".webm" "FirefoxHTML$5"
 
   ; An empty string is used for the 5th param because FirefoxHTML is not a
   ; protocol handler
-  ${AddDisabledDDEHandlerValues} "FirefoxHTML-$AppUserModelID" "$2" "$8,1" \
+  ${AddDisabledDDEHandlerValues} "FirefoxHTML$5" "$2" "$8,1" \
                                  "${AppRegName} HTML Document" ""
 
-  ${AddDisabledDDEHandlerValues} "FirefoxURL-$AppUserModelID" "$2" "$8,1" "${AppRegName} URL" \
+  ${AddDisabledDDEHandlerValues} "FirefoxURL$5" "$2" "$8,1" "${AppRegName} URL" \
                                  "true"
   ; An empty string is used for the 4th & 5th params because the following
   ; protocol handlers already have a display name and the additional keys
   ; required for a protocol handler.
   ${AddDisabledDDEHandlerValues} "ftp" "$2" "$8,1" "" ""
   ${AddDisabledDDEHandlerValues} "http" "$2" "$8,1" "" ""
   ${AddDisabledDDEHandlerValues} "https" "$2" "$8,1" "" ""
 !macroend
@@ -381,82 +399,87 @@
 ;
 ; This function also writes our RegisteredApplications entry, which gets us
 ; listed in the Settings app's default browser options on Windows 8+, and in
 ; Set Program Access and Defaults on earlier versions.
 !macro SetStartMenuInternet RegKey
   ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
   ${GetLongPath} "$INSTDIR\uninstall\helper.exe" $7
 
-  StrCpy $0 "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID"
-
-  WriteRegStr ${RegKey} "$0" "" "${BrandFullName}"
-
-  WriteRegStr ${RegKey} "$0\DefaultIcon" "" "$8,0"
+  ; Avoid writing new keys at the hash-suffixed path if this installation
+  ; already has keys at the old FIREFOX.EXE path. Otherwise we would create a
+  ; second entry in Default Apps for the same installation.
+  ${StrFilter} "${FileMainEXE}" "+" "" "" $1
+  ReadRegStr $0 ${RegKey} "Software\Clients\StartMenuInternet\$1\DefaultIcon" ""
+  StrCpy $0 $0 -2
+  ${If} $0 != $8
+    StrCpy $0 "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID"
 
-  ; The Reinstall Command is defined at
-  ; http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/programmersguide/shell_adv/registeringapps.asp
-  WriteRegStr ${RegKey} "$0\InstallInfo" "HideIconsCommand" "$\"$7$\" /HideShortcuts"
-  WriteRegStr ${RegKey} "$0\InstallInfo" "ShowIconsCommand" "$\"$7$\" /ShowShortcuts"
-  WriteRegStr ${RegKey} "$0\InstallInfo" "ReinstallCommand" "$\"$7$\" /SetAsDefaultAppGlobal"
+    WriteRegStr ${RegKey} "$0" "" "${BrandFullName}"
 
-  ClearErrors
-  ReadRegDWORD $1 ${RegKey} "$0\InstallInfo" "IconsVisible"
-  ; If the IconsVisible name value pair doesn't exist add it otherwise the
-  ; application won't be displayed in Set Program Access and Defaults.
-  ${If} ${Errors}
-    ${If} ${FileExists} "$QUICKLAUNCH\${BrandFullName}.lnk"
-      WriteRegDWORD ${RegKey} "$0\InstallInfo" "IconsVisible" 1
-    ${Else}
-      WriteRegDWORD ${RegKey} "$0\InstallInfo" "IconsVisible" 0
-    ${EndIf}
-  ${EndIf}
+    WriteRegStr ${RegKey} "$0\DefaultIcon" "" "$8,0"
 
-  WriteRegStr ${RegKey} "$0\shell\open\command" "" "$\"$8$\""
+    ; The Reinstall Command is defined at
+    ; http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/programmersguide/shell_adv/registeringapps.asp
+    WriteRegStr ${RegKey} "$0\InstallInfo" "HideIconsCommand" "$\"$7$\" /HideShortcuts"
+    WriteRegStr ${RegKey} "$0\InstallInfo" "ShowIconsCommand" "$\"$7$\" /ShowShortcuts"
+    WriteRegStr ${RegKey} "$0\InstallInfo" "ReinstallCommand" "$\"$7$\" /SetAsDefaultAppGlobal"
+    WriteRegDWORD ${RegKey} "$0\InstallInfo" "IconsVisible" 1
+
+    WriteRegStr ${RegKey} "$0\shell\open\command" "" "$\"$8$\""
 
-  WriteRegStr ${RegKey} "$0\shell\properties" "" "$(CONTEXT_OPTIONS)"
-  WriteRegStr ${RegKey} "$0\shell\properties\command" "" "$\"$8$\" -preferences"
+    WriteRegStr ${RegKey} "$0\shell\properties" "" "$(CONTEXT_OPTIONS)"
+    WriteRegStr ${RegKey} "$0\shell\properties\command" "" "$\"$8$\" -preferences"
 
-  WriteRegStr ${RegKey} "$0\shell\safemode" "" "$(CONTEXT_SAFE_MODE)"
-  WriteRegStr ${RegKey} "$0\shell\safemode\command" "" "$\"$8$\" -safe-mode"
+    WriteRegStr ${RegKey} "$0\shell\safemode" "" "$(CONTEXT_SAFE_MODE)"
+    WriteRegStr ${RegKey} "$0\shell\safemode\command" "" "$\"$8$\" -safe-mode"
 
-  ; Capabilities registry keys
-  WriteRegStr ${RegKey} "$0\Capabilities" "ApplicationDescription" "$(REG_APP_DESC)"
-  WriteRegStr ${RegKey} "$0\Capabilities" "ApplicationIcon" "$8,0"
-  WriteRegStr ${RegKey} "$0\Capabilities" "ApplicationName" "${BrandShortName}"
+    ; Capabilities registry keys
+    WriteRegStr ${RegKey} "$0\Capabilities" "ApplicationDescription" "$(REG_APP_DESC)"
+    WriteRegStr ${RegKey} "$0\Capabilities" "ApplicationIcon" "$8,0"
+    WriteRegStr ${RegKey} "$0\Capabilities" "ApplicationName" "${BrandShortName}"
 
-  WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".htm"   "FirefoxHTML-$AppUserModelID"
-  WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".html"  "FirefoxHTML-$AppUserModelID"
-  WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".shtml" "FirefoxHTML-$AppUserModelID"
-  WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xht"   "FirefoxHTML-$AppUserModelID"
-  WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xhtml" "FirefoxHTML-$AppUserModelID"
+    WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".htm"   "FirefoxHTML-$AppUserModelID"
+    WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".html"  "FirefoxHTML-$AppUserModelID"
+    WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".shtml" "FirefoxHTML-$AppUserModelID"
+    WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xht"   "FirefoxHTML-$AppUserModelID"
+    WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xhtml" "FirefoxHTML-$AppUserModelID"
+
+    WriteRegStr ${RegKey} "$0\Capabilities\StartMenu" "StartMenuInternet" "${AppRegName}-$AppUserModelID"
 
-  WriteRegStr ${RegKey} "$0\Capabilities\StartMenu" "StartMenuInternet" "${AppRegName}-$AppUserModelID"
+    WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "ftp"    "FirefoxURL-$AppUserModelID"
+    WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "http"   "FirefoxURL-$AppUserModelID"
+    WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "https"  "FirefoxURL-$AppUserModelID"
 
-  WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "ftp"    "FirefoxURL-$AppUserModelID"
-  WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "http"   "FirefoxURL-$AppUserModelID"
-  WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "https"  "FirefoxURL-$AppUserModelID"
-
-  ; Registered Application
-  WriteRegStr ${RegKey} "Software\RegisteredApplications" "${AppRegName}-$AppUserModelID" "$0\Capabilities"
+    ; Registered Application
+    WriteRegStr ${RegKey} "Software\RegisteredApplications" "${AppRegName}-$AppUserModelID" "$0\Capabilities"
+  ${EndIf}
 !macroend
 !define SetStartMenuInternet "!insertmacro SetStartMenuInternet"
 
 ; The IconHandler reference for FirefoxHTML can end up in an inconsistent state
 ; due to changes not being detected by the IconHandler for side by side
 ; installs (see bug 268512). The symptoms can be either an incorrect icon or no
 ; icon being displayed for files associated with Firefox (does not use SHCTX).
 !macro FixShellIconHandler RegKey
+  ; Find the correct key to update, either FirefoxHTML or FirefoxHTML-[PathHash]
+  StrCpy $3 "FirefoxHTML-$AppUserModelID"
   ClearErrors
-  ReadRegStr $1 ${RegKey} "Software\Classes\FirefoxHTML-$AppUserModelID\ShellEx\IconHandler" ""
+  ReadRegStr $0 ${RegKey} "Software\Classes\$3\DefaultIcon" ""
+  ${If} ${Errors}
+    StrCpy $3 "FirefoxHTML"
+  ${EndIf}
+
+  ClearErrors
+  ReadRegStr $1 ${RegKey} "Software\Classes\$3\ShellEx\IconHandler" ""
   ${Unless} ${Errors}
-    ReadRegStr $1 ${RegKey} "Software\Classes\FirefoxHTML-$AppUserModelID\DefaultIcon" ""
+    ReadRegStr $1 ${RegKey} "Software\Classes\$3\DefaultIcon" ""
     ${GetLongPath} "$INSTDIR\${FileMainEXE}" $2
     ${If} "$1" != "$2,1"
-      WriteRegStr ${RegKey} "Software\Classes\FirefoxHTML-$AppUserModelID\DefaultIcon" "" "$2,1"
+      WriteRegStr ${RegKey} "Software\Classes\$3\DefaultIcon" "" "$2,1"
     ${EndIf}
   ${EndUnless}
 !macroend
 !define FixShellIconHandler "!insertmacro FixShellIconHandler"
 
 ; Add Software\Mozilla\ registry entries (uses SHCTX).
 !macro SetAppKeys
   ; Check if this is an ESR release and if so add registry values so it is
@@ -601,17 +624,17 @@
     ; value from both HKCU and HKLM if it set to FirefoxHTML.
     ${If} "$0" == "FirefoxHTML"
       DeleteRegValue HKCU "Software\Classes\${FILE_TYPE}" ""
     ${EndIf}
     ${If} "$1" == "FirefoxHTML"
       DeleteRegValue HKLM "Software\Classes\${FILE_TYPE}" ""
     ${EndIf}
   ${ElseIf} "$0" == "FirefoxHTML"
-    ; Since KHCU is set to FirefoxHTML remove FirefoxHTML as the default value
+    ; Since HKCU is set to FirefoxHTML remove FirefoxHTML as the default value
     ; from HKCU if HKLM is set to a value other than an empty string.
     ${If} "$1" != ""
       DeleteRegValue HKCU "Software\Classes\${FILE_TYPE}" ""
     ${EndIf}
   ${EndIf}
 !macroend
 !define FixBadFileAssociation "!insertmacro FixBadFileAssociation"
 
@@ -663,22 +686,34 @@
   ; for this install location.
 
   ${IsHandlerForInstallDir} "FirefoxHTML-$AppUserModelID" $R9
   ${If} "$R9" == "true"
     ; An empty string is used for the 5th param because FirefoxHTML is not a
     ; protocol handler.
     ${AddDisabledDDEHandlerValues} "FirefoxHTML-$AppUserModelID" "$2" "$8,1" \
                                    "${AppRegName} HTML Document" ""
+  ${Else}
+    ${IsHandlerForInstallDir} "FirefoxHTML" $R9
+    ${If} "$R9" == "true"
+      ${AddDisabledDDEHandlerValues} "FirefoxHTML" "$2" "$8,1" \
+                                     "${AppRegName} HTML Document" ""
+    ${EndIf}
   ${EndIf}
 
   ${IsHandlerForInstallDir} "FirefoxURL-$AppUserModelID" $R9
   ${If} "$R9" == "true"
     ${AddDisabledDDEHandlerValues} "FirefoxURL-$AppUserModelID" "$2" "$8,1" \
                                    "${AppRegName} URL" "true"
+  ${Else}
+    ${IsHandlerForInstallDir} "FirefoxURL" $R9
+    ${If} "$R9" == "true"
+      ${AddDisabledDDEHandlerValues} "FirefoxURL" "$2" "$8,1" \
+                                     "${AppRegName} URL" "true"
+    ${EndIf}
   ${EndIf}
 
   ; An empty string is used for the 4th & 5th params because the following
   ; protocol handlers already have a display name and the additional keys
   ; required for a protocol handler.
   ${IsHandlerForInstallDir} "ftp" $R9
   ${If} "$R9" == "true"
     ${AddDisabledDDEHandlerValues} "ftp" "$2" "$8,1" "" ""
@@ -1210,33 +1245,45 @@
 ; Sets this installation as the default browser by setting the registry keys
 ; under HKEY_CURRENT_USER via registry calls and using the AppAssocReg NSIS
 ; plugin. This is a function instead of a macro so it is
 ; easily called from an elevated instance of the binary. Since this can be
 ; called by an elevated instance logging is not performed in this function.
 Function SetAsDefaultAppUserHKCU
   ; Only set as the user's StartMenuInternet browser if the StartMenuInternet
   ; registry keys are for this install.
-  ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+  StrCpy $R9 "${AppRegName}-$AppUserModelID"
   ClearErrors
-  ReadRegStr $0 HKCU "Software\Clients\StartMenuInternet\$R9-$AppUserModelID\DefaultIcon" ""
+  ReadRegStr $0 HKCU "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
   ${If} ${Errors}
   ${OrIf} ${AtMostWin2008R2}
     ClearErrors
-    ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\$R9-$AppUserModelID\DefaultIcon" ""
+    ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
   ${EndIf}
   ${Unless} ${Errors}
-    ${GetPathFromString} "$0" $0
-    ${GetParent} "$0" $0
-    ${If} ${FileExists} "$0"
-      ${GetLongPath} "$0" $0
-      ${If} "$0" == "$INSTDIR"
-        WriteRegStr HKCU "Software\Clients\StartMenuInternet" "" "$R9-$AppUserModelID"
+    WriteRegStr HKCU "Software\Clients\StartMenuInternet" "" "$R9"
+  ${Else}
+    ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+    ClearErrors
+    ReadRegStr $0 HKCU "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
+    ${If} ${Errors}
+    ${OrIf} ${AtMostWin2008R2}
+      ClearErrors
+      ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
+    ${EndIf}
+    ${Unless} ${Errors}
+      ${GetPathFromString} "$0" $0
+      ${GetParent} "$0" $0
+      ${If} ${FileExists} "$0"
+        ${GetLongPath} "$0" $0
+        ${If} "$0" == "$INSTDIR"
+          WriteRegStr HKCU "Software\Clients\StartMenuInternet" "" "$R9"
+        ${EndIf}
       ${EndIf}
-    ${EndIf}
+    ${EndUnless}
   ${EndUnless}
 
   SetShellVarContext current  ; Set SHCTX to the current user (e.g. HKCU)
 
   ${If} ${AtLeastWin8}
     ${SetStartMenuInternet} "HKCU"
     ${FixShellIconHandler} "HKCU"
     ${FixClassKeys} ; Does not use SHCTX
@@ -1244,21 +1291,21 @@ Function SetAsDefaultAppUserHKCU
 
   ${SetHandlers}
 
   ; Only register as the handler if the app registry name
   ; exists under the RegisteredApplications registry key. The protocol and
   ; file handlers set previously at the user level will associate this install
   ; as the default browser.
   ClearErrors
-  ReadRegStr $0 HKLM "Software\RegisteredApplications" "${AppRegName}-$AppUserModelID"
+  ReadRegStr $0 HKLM "Software\RegisteredApplications" "$R9"
   ${Unless} ${Errors}
     ; This is all protected by a user choice hash in Windows 8 so it won't
     ; help, but it also won't hurt.
-    AppAssocReg::SetAppAsDefaultAll "${AppRegName}-$AppUserModelID"
+    AppAssocReg::SetAppAsDefaultAll "$R9"
   ${EndUnless}
   ${RemoveDeprecatedKeys}
   ${MigrateTaskBarShortcut}
 FunctionEnd
 
 ; Helper for updating the shortcut application model IDs.
 Function FixShortcutAppModelIDs
   ${If} ${AtLeastWin7}
@@ -1300,24 +1347,34 @@ Function SetAsDefaultAppUser
     Return ; Nothing more needs to be done
   ${EndIf}
 
   ; Before Win8, it is only possible to set this installation of the application
   ; as the StartMenuInternet handler if it was added to the HKLM
   ; StartMenuInternet registry keys.
   ; http://support.microsoft.com/kb/297878
 
-  ; Check if this install location registered as the StartMenuInternet client
+  ; Check if this install location registered as a StartMenuInternet client
   ClearErrors
   ReadRegStr $0 HKCU "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID\DefaultIcon" ""
   ${If} ${Errors}
   ${OrIf} ${AtMostWin2008R2}
     ClearErrors
     ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\${AppRegName}-$AppUserModelID\DefaultIcon" ""
   ${EndIf}
+  ${If} ${Errors}
+    ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+    ClearErrors
+    ReadRegStr $0 HKCU "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
+  ${EndIf}
+  ${If} ${Errors}
+  ${OrIf} ${AtMostWin2008R2}
+    ClearErrors
+    ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
+  ${EndIf}
 
   ${Unless} ${Errors}
     ${GetPathFromString} "$0" $0
     ${GetParent} "$0" $0
     ${If} ${FileExists} "$0"
       ${GetLongPath} "$0" $0
       ${If} "$0" == "$INSTDIR"
         ; Check if this is running in an elevated process
--- a/browser/modules/URLBarZoom.jsm
+++ b/browser/modules/URLBarZoom.jsm
@@ -5,47 +5,71 @@
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = [ "URLBarZoom" ];
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 var URLBarZoom = {
-
   init(aWindow) {
-    // Register ourselves with the service so we know when the zoom prefs change.
-    Services.obs.addObserver(updateZoomButton, "browser-fullZoom:zoomChange", false);
-    Services.obs.addObserver(updateZoomButton, "browser-fullZoom:zoomReset", false);
-    Services.obs.addObserver(updateZoomButton, "browser-fullZoom:location-change", false);
+    aWindow.addEventListener("EndSwapDocShells", onEndSwapDocShells, true);
+    aWindow.addEventListener("unload", () => {
+      aWindow.removeEventListener("EndSwapDocShells", onEndSwapDocShells, true);
+    }, {once: true});
   },
 }
 
-function updateZoomButton(aSubject, aTopic) {
-  let win = aSubject.ownerGlobal;
+function fullZoomObserver(aSubject, aTopic) {
+  // If the tab was the last one in its window and has been dragged to another
+  // window, the original browser's window will be unavailable here. Since that
+  // window is closing, we can just ignore this notification.
+  if (!aSubject.ownerGlobal) {
+    return;
+  }
+
+  // Only allow pulse animation for zoom changes, not tab switching.
+  let animate = (aTopic != "browser-fullZoom:location-change");
+  updateZoomButton(aSubject, animate);
+}
+
+function onEndSwapDocShells(event) {
+  updateZoomButton(event.originalTarget);
+}
+
+function updateZoomButton(aBrowser, aAnimate = false) {
+  let win = aBrowser.ownerGlobal;
+  if (aBrowser != win.gBrowser.selectedBrowser) {
+    return;
+  }
+
   let customizableZoomControls = win.document.getElementById("zoom-controls");
   let zoomResetButton = win.document.getElementById("urlbar-zoom-button");
-  let zoomFactor = Math.round(win.ZoomManager.zoom * 100);
 
   // Ensure that zoom controls haven't already been added to browser in Customize Mode
   if (customizableZoomControls &&
       customizableZoomControls.getAttribute("cui-areatype") == "toolbar") {
     zoomResetButton.hidden = true;
     return;
   }
+
+  let zoomFactor = Math.round(win.ZoomManager.zoom * 100);
   if (zoomFactor != 100) {
     // Check if zoom button is visible and update label if it is
     if (zoomResetButton.hidden) {
       zoomResetButton.hidden = false;
     }
-    // Only allow pulse animation for zoom changes, not tab switching
-    if (aTopic != "browser-fullZoom:location-change") {
+    if (aAnimate) {
       zoomResetButton.setAttribute("animate", "true");
     } else {
       zoomResetButton.removeAttribute("animate");
     }
     zoomResetButton.setAttribute("label",
         win.gNavigatorBundle.getFormattedString("urlbar-zoom-button.label", [zoomFactor]));
-  // Hide button if zoom is at 100%
   } else {
-      zoomResetButton.hidden = true;
+    // Hide button if zoom is at 100%
+    zoomResetButton.hidden = true;
   }
 }
+
+Services.obs.addObserver(fullZoomObserver, "browser-fullZoom:zoomChange", false);
+Services.obs.addObserver(fullZoomObserver, "browser-fullZoom:zoomReset", false);
+Services.obs.addObserver(fullZoomObserver, "browser-fullZoom:location-change", false);
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -1,18 +1,18 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
+BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
 XPCSHELL_TESTS_MANIFESTS += [
     'test/unit/social/xpcshell.ini',
-    'test/xpcshell/xpcshell.ini',
+    'test/unit/xpcshell.ini',
 ]
 
 EXTRA_JS_MODULES += [
     'AboutHome.jsm',
     'AboutNewTab.jsm',
     'AttributionCode.jsm',
     'BrowserUITelemetry.jsm',
     'BrowserUsageTelemetry.jsm',
rename from browser/modules/test/.eslintrc.js
rename to browser/modules/test/browser/.eslintrc.js
--- a/browser/modules/test/.eslintrc.js
+++ b/browser/modules/test/browser/.eslintrc.js
@@ -1,7 +1,7 @@
 "use strict";
 
 module.exports = {
   "extends": [
-    "../../../testing/mochitest/browser.eslintrc.js"
+    "../../../../testing/mochitest/browser.eslintrc.js"
   ]
 };
rename from browser/modules/test/browser.ini
rename to browser/modules/test/browser/browser.ini
--- a/browser/modules/test/browser.ini
+++ b/browser/modules/test/browser/browser.ini
@@ -16,18 +16,18 @@ support-files =
   !/browser/components/search/test/testEngine.xml
 [browser_NetworkPrioritizer.js]
 [browser_PermissionUI.js]
 [browser_PermissionUI_prompts.js]
 [browser_ProcessHangNotifications.js]
 skip-if = !e10s
 [browser_SelfSupportBackend.js]
 support-files =
-  ../../components/uitour/test/uitour.html
-  ../../components/uitour/UITour-lib.js
+  ../../../components/uitour/test/uitour.html
+  ../../../components/uitour/UITour-lib.js
 [browser_SitePermissions.js]
 [browser_SitePermissions_combinations.js]
 [browser_SitePermissions_expiry.js]
 [browser_SitePermissions_tab_urls.js]
 [browser_taskbar_preview.js]
 skip-if = os != "win"
 [browser_UnsubmittedCrashHandler.js]
 run-if = crashreporter
rename from browser/modules/test/browser_BrowserUITelemetry_buckets.js
rename to browser/modules/test/browser/browser_BrowserUITelemetry_buckets.js
rename from browser/modules/test/browser_BrowserUITelemetry_defaults.js
rename to browser/modules/test/browser/browser_BrowserUITelemetry_defaults.js
rename from browser/modules/test/browser_BrowserUITelemetry_sidebar.js
rename to browser/modules/test/browser/browser_BrowserUITelemetry_sidebar.js
rename from browser/modules/test/browser_BrowserUITelemetry_syncedtabs.js
rename to browser/modules/test/browser/browser_BrowserUITelemetry_syncedtabs.js
rename from browser/modules/test/browser_ContentSearch.js
rename to browser/modules/test/browser/browser_ContentSearch.js
--- a/browser/modules/test/browser_ContentSearch.js
+++ b/browser/modules/test/browser/browser_ContentSearch.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const TEST_MSG = "ContentSearchTest";
 const CONTENT_SEARCH_MSG = "ContentSearch";
 const TEST_CONTENT_SCRIPT_BASENAME = "contentSearch.js";
 
 var gMsgMan;
 /* eslint no-undef:"error" */
-/* import-globals-from ../../components/search/test/head.js */
+/* import-globals-from ../../../components/search/test/head.js */
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/browser/components/search/test/head.js",
   this);
 
 let originalEngine = Services.search.currentEngine;
 
 add_task(function* setup() {
   yield promiseNewEngine("testEngine.xml", {
rename from browser/modules/test/browser_NetworkPrioritizer.js
rename to browser/modules/test/browser/browser_NetworkPrioritizer.js
rename from browser/modules/test/browser_PermissionUI.js
rename to browser/modules/test/browser/browser_PermissionUI.js
rename from browser/modules/test/browser_PermissionUI_prompts.js
rename to browser/modules/test/browser/browser_PermissionUI_prompts.js
rename from browser/modules/test/browser_ProcessHangNotifications.js
rename to browser/modules/test/browser/browser_ProcessHangNotifications.js
rename from browser/modules/test/browser_SelfSupportBackend.js
rename to browser/modules/test/browser/browser_SelfSupportBackend.js
rename from browser/modules/test/browser_SitePermissions.js
rename to browser/modules/test/browser/browser_SitePermissions.js
rename from browser/modules/test/browser_SitePermissions_combinations.js
rename to browser/modules/test/browser/browser_SitePermissions_combinations.js
rename from browser/modules/test/browser_SitePermissions_expiry.js
rename to browser/modules/test/browser/browser_SitePermissions_expiry.js
rename from browser/modules/test/browser_SitePermissions_tab_urls.js
rename to browser/modules/test/browser/browser_SitePermissions_tab_urls.js
rename from browser/modules/test/browser_UnsubmittedCrashHandler.js
rename to browser/modules/test/browser/browser_UnsubmittedCrashHandler.js
rename from browser/modules/test/browser_UsageTelemetry.js
rename to browser/modules/test/browser/browser_UsageTelemetry.js
rename from browser/modules/test/browser_UsageTelemetry_content.js
rename to browser/modules/test/browser/browser_UsageTelemetry_content.js
rename from browser/modules/test/browser_UsageTelemetry_content_aboutHome.js
rename to browser/modules/test/browser/browser_UsageTelemetry_content_aboutHome.js
rename from browser/modules/test/browser_UsageTelemetry_private_and_restore.js
rename to browser/modules/test/browser/browser_UsageTelemetry_private_and_restore.js
rename from browser/modules/test/browser_UsageTelemetry_searchbar.js
rename to browser/modules/test/browser/browser_UsageTelemetry_searchbar.js
rename from browser/modules/test/browser_UsageTelemetry_urlbar.js
rename to browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
rename from browser/modules/test/browser_bug1319078.js
rename to browser/modules/test/browser/browser_bug1319078.js
rename from browser/modules/test/browser_taskbar_preview.js
rename to browser/modules/test/browser/browser_taskbar_preview.js
rename from browser/modules/test/browser_urlBar_zoom.js
rename to browser/modules/test/browser/browser_urlBar_zoom.js
rename from browser/modules/test/contentSearch.js
rename to browser/modules/test/browser/contentSearch.js
rename from browser/modules/test/contentSearchBadImage.xml
rename to browser/modules/test/browser/contentSearchBadImage.xml
rename from browser/modules/test/contentSearchSuggestions.sjs
rename to browser/modules/test/browser/contentSearchSuggestions.sjs
rename from browser/modules/test/contentSearchSuggestions.xml
rename to browser/modules/test/browser/contentSearchSuggestions.xml
--- a/browser/modules/test/contentSearchSuggestions.xml
+++ b/browser/modules/test/browser/contentSearchSuggestions.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>browser_ContentSearch contentSearchSuggestions.xml</ShortName>
-<Url type="application/x-suggestions+json" method="GET" template="http://mochi.test:8888/browser/browser/modules/test/contentSearchSuggestions.sjs?{searchTerms}"/>
+<Url type="application/x-suggestions+json" method="GET" template="http://mochi.test:8888/browser/browser/modules/test/browser/contentSearchSuggestions.sjs?{searchTerms}"/>
 <Url type="text/html" method="GET" template="http://browser-ContentSearch.com/contentSearchSuggestions" rel="searchform"/>
 </SearchPlugin>
rename from browser/modules/test/head.js
rename to browser/modules/test/browser/head.js
rename from browser/modules/test/usageTelemetrySearchSuggestions.sjs
rename to browser/modules/test/browser/usageTelemetrySearchSuggestions.sjs
rename from browser/modules/test/usageTelemetrySearchSuggestions.xml
rename to browser/modules/test/browser/usageTelemetrySearchSuggestions.xml
--- a/browser/modules/test/usageTelemetrySearchSuggestions.xml
+++ b/browser/modules/test/browser/usageTelemetrySearchSuggestions.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>browser_UsageTelemetry usageTelemetrySearchSuggestions.xml</ShortName>
-<Url type="application/x-suggestions+json" method="GET" template="http://mochi.test:8888/browser/browser/modules/test/usageTelemetrySearchSuggestions.sjs?{searchTerms}"/>
+<Url type="application/x-suggestions+json" method="GET" template="http://mochi.test:8888/browser/browser/modules/test/browser/usageTelemetrySearchSuggestions.sjs?{searchTerms}"/>
 <Url type="text/html" method="GET" template="http://example.com" rel="searchform"/>
 </SearchPlugin>
rename from browser/modules/test/xpcshell/.eslintrc.js
rename to browser/modules/test/unit/.eslintrc.js
deleted file mode 100644
--- a/browser/modules/test/unit/social/.eslintrc.js
+++ /dev/null
@@ -1,7 +0,0 @@
-"use strict";
-
-module.exports = {
-  "extends": [
-    "../../../../../testing/xpcshell/xpcshell.eslintrc.js"
-  ]
-};
--- a/browser/modules/test/unit/social/head.js
+++ b/browser/modules/test/unit/social/head.js
@@ -30,17 +30,16 @@ const gProfD = do_get_profile();
 
 function createAppInfo(ID, name, version, platformVersion = "1.0") {
   let tmp = {};
   Cu.import("resource://testing-common/AppInfo.jsm", tmp);
   tmp.updateAppInfo({
     ID, name, version, platformVersion,
     crashReporter: true,
   });
-  gAppInfo = tmp.getAppInfo();
 }
 
 function initApp() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
   // prepare a blocklist file for the blocklist service
   var blocklistFile = gProfD.clone();
   blocklistFile.append("blocklist.xml");
   if (blocklistFile.exists())
rename from browser/modules/test/xpcshell/test_AttributionCode.js
rename to browser/modules/test/unit/test_AttributionCode.js
rename from browser/modules/test/xpcshell/test_DirectoryLinksProvider.js
rename to browser/modules/test/unit/test_DirectoryLinksProvider.js
rename from browser/modules/test/xpcshell/test_E10SUtils_nested_URIs.js
rename to browser/modules/test/unit/test_E10SUtils_nested_URIs.js
rename from browser/modules/test/xpcshell/test_LaterRun.js
rename to browser/modules/test/unit/test_LaterRun.js
rename from browser/modules/test/xpcshell/test_SitePermissions.js
rename to browser/modules/test/unit/test_SitePermissions.js
rename from browser/modules/test/xpcshell/xpcshell.ini
rename to browser/modules/test/unit/xpcshell.ini
deleted file mode 100644
--- a/browser/themes/linux/browser-lightweightTheme.css
+++ /dev/null
@@ -1,31 +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/. */
-
-%include linuxShared.inc
-%filter substitution
-
-/*
- * LightweightThemeListener will append the current lightweight theme's header
- * image to the background-image for each of the following rulesets.
- */
-
-/* Lightweight theme on tabs */
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
-  background-attachment: scroll, fixed;
-  background-color: transparent;
-  background-image: @fgTabTextureLWT@;/*, lwtHeader;*/
-  background-position: 0 0, right top;
-  background-repeat: repeat-x, no-repeat;
-}
-
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
-  background-attachment: scroll, scroll, fixed;
-  background-color: transparent;
-  background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
-                    @fgTabTextureLWT@;/*,
-                    lwtHeader;*/
-  background-position: 0 0, 0 0, right top;
-  background-repeat: repeat-x, repeat-x, no-repeat;
-}
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1519,25 +1519,16 @@ notification.pluginVulnerable > .notific
 %include ../shared/customizableui/customizeMode.inc.css
 
 #main-window[customize-entered] > #tab-view-deck {
   background-image: url("chrome://browser/skin/customizableui/customizeMode-gridTexture.png"),
                     linear-gradient(to bottom, #bcbcbc, #b5b5b5);
   background-attachment: fixed;
 }
 
-#main-window[customization-lwtheme] > #tab-view-deck:-moz-lwtheme {
-  background-repeat: no-repeat;
-  background-position: right top;
-  background-attachment: fixed;
-  /* The image will get set from CustomizeMode.jsm */
-  background-image: none;
-  background-color: transparent;
-}
-
 #main-window[customization-lwtheme]:-moz-lwtheme {
   background-image: url("chrome://browser/skin/customizableui/customizeMode-gridTexture.png"),
                     url("chrome://browser/skin/customizableui/background-noise-toolbar.png"),
                     linear-gradient(to bottom, #bcbcbc, #b5b5b5);
   background-color: #b5b5b5;
   background-repeat: repeat;
   background-attachment: fixed;
   background-position: left top;
--- a/browser/themes/linux/downloads/indicator.css
+++ b/browser/themes/linux/downloads/indicator.css
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+%include ../../shared/downloads/indicator.inc.css
+
 /*** Status and progress indicator ***/
 
 #downloads-animation-container {
   min-height: 1px;
   min-width: 1px;
   height: 1px;
   margin-bottom: -1px;
   /* Makes the outermost animation container element positioned, so that its
@@ -16,41 +18,41 @@
   /* The selected tab may overlap #downloads-indicator-notification */
   z-index: 5;
 }
 
 /*** Main indicator icon ***/
 
 @media not all and (min-resolution: 1.1dppx) {
   #downloads-button {
-    --downloads-indicator-icon: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 0, 198, 18, 180);
-    --downloads-indicator-icon-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 18, 198, 36, 180);
-    --downloads-indicator-icon-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
-    --downloads-indicator-icon-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 18, 198, 36, 180);
+    --downloads-indicator-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 0, 198, 18, 180);
+    --downloads-indicator-image-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 18, 198, 36, 180);
+    --downloads-indicator-image-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
+    --downloads-indicator-image-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 18, 198, 36, 180);
   }
 }
 
 @media (min-resolution: 1.1dppx) {
   #downloads-button {
-    --downloads-indicator-icon: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
-    --downloads-indicator-icon-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 36, 396, 72, 360);
-    --downloads-indicator-icon-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 0, 396, 36, 360);
-    --downloads-indicator-icon-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 36, 396, 72, 360);
+    --downloads-indicator-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
+    --downloads-indicator-image-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 36, 396, 72, 360);
+    --downloads-indicator-image-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 0, 396, 36, 360);
+    --downloads-indicator-image-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 36, 396, 72, 360);
   }
 }
 
 #downloads-button[cui-areatype="toolbar"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background: var(--downloads-indicator-icon) center no-repeat;
+  background: var(--downloads-indicator-image) center no-repeat;
   width: 18px;
   height: 18px;
   background-size: 18px;
 }
 
 toolbar[brighttext] #downloads-button[cui-areatype="toolbar"]:not([attention="success"]) > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: var(--downloads-indicator-icon-inverted);
+  background-image: var(--downloads-indicator-image-inverted);
 }
 
 #downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
 #downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   display: -moz-box;
   height: 8px;
   width: 8px;
   min-width: 0;
@@ -74,46 +76,46 @@ toolbar[brighttext] #downloads-button[cu
 }
 
 #downloads-button[cui-areatype="toolbar"][attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive,
 #downloads-button[cui-areatype="toolbar"][attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive {
   filter: none;
 }
 
 #downloads-button[cui-areatype="toolbar"][attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: var(--downloads-indicator-icon-attention);
+  background-image: var(--downloads-indicator-image-attention);
 }
 
 toolbar[brighttext] #downloads-button[cui-areatype="toolbar"][attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: var(--downloads-indicator-icon-attention-inverted);
+  background-image: var(--downloads-indicator-image-attention-inverted);
 }
 
 #downloads-button[cui-areatype="menu-panel"][attention="success"] {
   list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel.png");
   -moz-image-region: auto;
 }
 
 /* In the next few rules, we use :not([counter]) as a shortcut that is
    equivalent to -moz-any([progress], [paused]). */
 
 #downloads-button:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background: var(--downloads-indicator-icon) center no-repeat;
+  background: var(--downloads-indicator-image) center no-repeat;
   background-size: 12px;
 }
 
 toolbar[brighttext] #downloads-button:not([counter]):not([attention="success"]) > #downloads-indicator-anchor > #downloads-button-progress-area > #downloads-indicator-counter {
-  background-image: var(--downloads-indicator-icon-inverted);
+  background-image: var(--downloads-indicator-image-inverted);
 }
 
 #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: var(--downloads-indicator-icon-attention);
+  background-image: var(--downloads-indicator-image-attention);
 }
 
 toolbar[brighttext] #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: var(--downloads-indicator-icon-attention-inverted);
+  background-image: var(--downloads-indicator-image-attention-inverted);
 }
 
 /*** Download notifications ***/
 
 #downloads-indicator-notification {
   opacity: 0;
   background-size: 16px;
   background-position: center;
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -7,17 +7,16 @@ browser.jar:
 % override chrome://global/skin/icons/warning-16.png moz-icon://stock/gtk-dialog-warning?size=menu
 #include ../shared/jar.inc.mn
   skin/classic/browser/sanitizeDialog.css
   skin/classic/browser/aboutSessionRestore-window-icon.png
   skin/classic/browser/aboutSyncTabs.css
 * skin/classic/browser/syncedtabs/sidebar.css     (syncedtabs/sidebar.css)
 * skin/classic/browser/browser.css
 * skin/classic/browser/compacttheme.css
-* skin/classic/browser/browser-lightweightTheme.css
   skin/classic/browser/click-to-play-warning-stripes.png
   skin/classic/browser/Info.png
   skin/classic/browser/menuPanel-customize.png
   skin/classic/browser/menuPanel-customize@2x.png
   skin/classic/browser/menuPanel-exit.png
   skin/classic/browser/menuPanel-exit@2x.png
   skin/classic/browser/menuPanel-help.png
   skin/classic/browser/menuPanel-help@2x.png
deleted file mode 100644
--- a/browser/themes/osx/browser-lightweightTheme.css
+++ /dev/null
@@ -1,38 +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/. */
-
-%include shared.inc
-
-/*
- * LightweightThemeListener will append the current lightweight theme's header
- * image to the background-image for each of the following rulesets.
- */
-
-/* Lightweight theme on tabs */
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
-  background-attachment: scroll, fixed;
-  background-color: transparent;
-  background-image: @fgTabTextureLWT@;/*, lwtHeader;*/
-  background-position: 0 0, right top;
-  background-repeat: repeat-x, no-repeat;
-}
-
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
-  background-attachment: scroll, scroll, fixed;
-  background-color: transparent;
-  background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
-                    @fgTabTextureLWT@;/*,
-                    lwtHeader;*/
-  background-position: 0 0, 0 0, right top;
-  background-repeat: repeat-x, repeat-x, no-repeat;
-}
-
-@media (min-resolution: 2dppx) {
-  #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
-    background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
-                      @fgTabTextureLWT@;/*,
-                      lwtHeader;*/
-  }
-}
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3185,24 +3185,22 @@ menulist.translate-infobar-element > .me
 #main-window[privatebrowsingmode=temporary]:not([tabsintitlebar])[customize-entered] > #titlebar,
 #main-window[customize-entered] > #tab-view-deck {
   background-image: url("chrome://browser/skin/customizableui/customizeMode-gridTexture.png"),
                     url("chrome://browser/skin/customizableui/background-noise-toolbar.png"),
                     linear-gradient(to bottom, rgb(233,233,233), rgb(178,178,178) 40px);
   background-attachment: fixed;
 }
 
-#main-window[tabsintitlebar][customization-lwtheme] > #titlebar:-moz-lwtheme,
-#main-window[customization-lwtheme] > #tab-view-deck:-moz-lwtheme {
+#main-window[tabsintitlebar][customization-lwtheme] > #titlebar:-moz-lwtheme {
   background-repeat: no-repeat;
   background-position: right top;
   background-attachment: fixed;
-  /* The image will get set from CustomizeMode.jsm */
-  background-image: none;
-  background-color: transparent;
+  background-image: var(--lwt-header-image);
+  background-color: var(--lwt-accent-color);
 }
 
 #main-window[customization-lwtheme]:-moz-lwtheme {
   background-image: url("chrome://browser/skin/customizableui/customizeMode-gridTexture.png"),
                     url("chrome://browser/skin/customizableui/background-noise-toolbar.png");
   background-color: rgb(178,178,178);
   background-repeat: repeat;
   background-attachment: fixed;
--- a/browser/themes/osx/downloads/indicator.css
+++ b/browser/themes/osx/downloads/indicator.css
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+%include ../../shared/downloads/indicator.inc.css
+
 /*** Status and progress indicator ***/
 
 #downloads-indicator-anchor {
   min-width: 18px;
   min-height: 18px;
 }
 
 #downloads-animation-container {
@@ -19,23 +21,39 @@
      This is required by the animated event notification. */
   position: relative;
   /* The selected tab may overlap #downloads-indicator-notification */
   z-index: 5;
 }
 
 /*** Main indicator icon ***/
 
+#downloads-button {
+  --downloads-indicator-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 0, 198, 18, 180);
+  --downloads-indicator-image-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 36, 198, 54, 180);
+  --downloads-indicator-image-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
+  --downloads-indicator-image-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 36, 198, 54, 180);
+}
+
+@media (min-resolution: 2dppx) {
+  #downloads-button {
+    --downloads-indicator-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
+    --downloads-indicator-image-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 72, 396, 108, 360);
+    --downloads-indicator-image-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 0, 396, 36, 360);
+    --downloads-indicator-image-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 72, 396, 108, 360);
+  }
+}
+
 #downloads-indicator-icon {
-  background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"),
-                              0, 198, 18, 180) center no-repeat;
+  background: var(--downloads-indicator-image) center no-repeat;
+  background-size: 18px;
 }
 
 toolbar[brighttext] #downloads-indicator-icon {
-  background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
+  background-image: var(--downloads-indicator-image-inverted);
 }
 
 #downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
 #downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   display: -moz-box;
   height: 8px;
   width: 8px;
   min-width: 0;
@@ -59,100 +77,67 @@ toolbar[brighttext] #downloads-indicator
 }
 
 #downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive,
 #downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive {
   filter: none;
 }
 
 #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 36, 198, 54, 180);
+  background-image: var(--downloads-indicator-image-attention);
 }
 
 toolbar[brighttext] #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 36, 198, 54, 180);
+  background-image: var(--downloads-indicator-image-attention-inverted);
 }
 
 #downloads-button[cui-areatype="menu-panel"][attention="success"] {
   list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel.png");
   -moz-image-region: auto;
 }
 
 /* In the next few rules, we use :not([counter]) as a shortcut that is
    equivalent to -moz-any([progress], [paused]). */
 
 #downloads-button:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"),
-                              0, 198, 18, 180) center no-repeat;
+  background: var(--downloads-indicator-image) center no-repeat;
   background-size: 12px;
 }
 
 toolbar[brighttext] #downloads-button:not([counter]):not([attention="success"]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
+  background-image: var(--downloads-indicator-image-inverted);
 }
 
 #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 36, 198, 54, 180);
+  background-image: var(--downloads-indicator-image-attention);
 }
 
 toolbar[brighttext] #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 36, 198, 54, 180);
+  background-image: var(--downloads-indicator-image-attention-inverted);
 }
 
 @media (min-resolution: 2dppx) {
-  #downloads-indicator-icon {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
-    background-size: 18px;
-  }
-
-  toolbar[brighttext] #downloads-indicator-icon {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 0, 396, 36, 360);
-  }
-
-  #downloads-button:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
-  }
-
-  toolbar[brighttext] #downloads-button:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"),
-                                      0, 396, 36, 360);
-  }
-
-  #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 72, 396, 108, 360);
-  }
-
-  toolbar[brighttext] #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 72, 396, 108, 360);
-  }
-
   #downloads-button[cui-areatype="menu-panel"][attention="success"] {
     list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel@2x.png");
   }
-
-  #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 72, 396, 108, 360);
-  }
-
-  toolbar[brighttext] #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 72, 396, 108, 360);
-  }
 }
 
 /*** Download notifications ***/
 
 #downloads-indicator-notification {
   opacity: 0;
   background-size: 16px;
   background-position: center;
   background-repeat: no-repeat;
   width: 16px;
   height: 16px;
 }
 
+/*** Progress bar and text ***/
+
 @keyframes downloadsIndicatorNotificationStartRight {
   from { opacity: 0; transform: translate(-128px, 128px) scale(8); }
   20%  { opacity: .85; animation-timing-function: ease-out; }
   to   { opacity: 0; transform: translate(0) scale(1); }
 }
 
 @keyframes downloadsIndicatorNotificationStartLeft {
   from { opacity: 0; transform: translate(128px, 128px) scale(8); }
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -6,17 +6,16 @@ browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
 #include ../shared/jar.inc.mn
   skin/classic/browser/sanitizeDialog.css
   skin/classic/browser/aboutSessionRestore-window-icon.png
   skin/classic/browser/aboutSyncTabs.css
 * skin/classic/browser/syncedtabs/sidebar.css          (syncedtabs/sidebar.css)
 * skin/classic/browser/browser.css
 * skin/classic/browser/compacttheme.css
-* skin/classic/browser/browser-lightweightTheme.css
   skin/classic/browser/click-to-play-warning-stripes.png
   skin/classic/browser/Info.png
   skin/classic/browser/keyhole-circle.png
   skin/classic/browser/keyhole-circle@2x.png
   skin/classic/browser/subtle-pattern.png
   skin/classic/browser/menu-back.png
   skin/classic/browser/menu-forward.png
   skin/classic/browser/menuPanel-customize.png
--- a/browser/themes/shared/compacttheme.inc.css
+++ b/browser/themes/shared/compacttheme.inc.css
@@ -207,16 +207,25 @@ toolbar[brighttext] #downloads-indicator
 }
 
 window:not([chromehidden~="toolbar"]) #urlbar-wrapper {
   overflow: -moz-hidden-unscrollable;
   clip-path: none;
   margin-inline-start: 0;
 }
 
+window:not([chromehidden~="toolbar"]) #urlbar-wrapper:-moz-locale-dir(rtl),
+window:not([chromehidden~="toolbar"]) #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
+  /* Resolves text blurring issue when hovering, see bug 1340206 */
+  transform: none;
+  /* For some reason, this property must be specified here, even though the same
+     value is set in the previous rule set. o_O */
+  margin-inline-start: 0;
+}
+
 #urlbar-zoom-button:-moz-lwtheme-brighttext:hover {
   background-color: rgba(255,255,255,.2);
 }
 
 #urlbar-zoom-button:-moz-lwtheme-brighttext:hover:active {
   background-color: rgba(255,255,255,.3);
 }
 
--- a/browser/themes/shared/customizableui/customizeMode.inc.css
+++ b/browser/themes/shared/customizableui/customizeMode.inc.css
@@ -3,16 +3,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Customization mode */
 
 :root {
   --drag-drop-transition-duration: .3s;
 }
 
+#main-window[customization-lwtheme] #tab-view-deck:-moz-lwtheme {
+  background-repeat: no-repeat;
+  background-position: right top;
+  background-attachment: fixed;
+  background-color: transparent;
+  background-image: -moz-image-rect(var(--lwt-header-image), 0, 100%,
+                      var(--toolbox-rect-height), 0),
+                    linear-gradient(to bottom,
+                      var(--lwt-accent-color) calc(var(--toolbox-rect-height-with-unit) - 1px),
+                      rgba(0,0,0,0.25) calc(var(--toolbox-rect-height-with-unit) - 1px),
+                      rgba(0,0,0,0.25) calc(var(--toolbox-rect-height-with-unit) + 1px),
+                      rgba(255,255,255,0.5) calc(var(--toolbox-rect-height-with-unit) + 1px),
+                      rgba(255,255,255,0.5) calc(var(--toolbox-rect-height-with-unit) + 2px),
+                      transparent calc(var(--toolbox-rect-height-with-unit) + 2px));
+}
+
 #main-window:-moz-any([customize-entering],[customize-entered]) #browser-bottombox {
   margin-bottom: 2em;
 }
 
 #main-window:-moz-any([customize-entering],[customize-entered]) #content-deck,
 #main-window:-moz-any([customize-entering],[customize-entered]) #browser-bottombox,
 #main-window:-moz-any([customize-entering],[customize-entered]) #navigator-toolbox {
   margin-left: 2em;
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/downloads/indicator.inc.css
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#downloads-indicator-icon {
+  position: relative;
+}
+
+#downloads-indicator-progress-icon {
+  background: var(--downloads-indicator-image-attention) bottom no-repeat;
+  background-size: 18px;
+  position: absolute;
+  bottom: 0;
+  width: 100%;
+  height: 0;
+  transition: height 0.5s;
+}
+
+toolbar[brighttext] #downloads-indicator-progress-icon {
+  background-image: var(--downloads-indicator-image-attention-inverted);
+}
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -378,38 +378,64 @@
   background-repeat: repeat-x;
   background-size: var(--tab-stroke-background-size), auto 100%;
   /* The padding-top combined with background-clip: content-box (the bottom-most) ensure the
      background-color doesn't extend above the top border. */
   padding-top: 2px;
 }
 
 /* Selected tab lightweight theme styles.
-   See browser-lightweightTheme.css for information about run-time changes to LWT styles. */
+   See the "Lightweight theme on tabs" section of this file
+   for information about run-time changes to LWT styles. */
 .tab-background-middle[selected=true]:-moz-lwtheme {
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
-                    @fgTabTextureLWT@;/*,
-                    lwtHeader;*/
+                    @fgTabTextureLWT@;
   /* Don't stretch the LWT header images */
   background-size: var(--tab-stroke-background-size), auto 100%, auto auto;
 }
 
-/* These LWT styles are normally overridden by browser-lightweightTheme.css */
+/* These LWT styles are normally overridden by the "Lightweight theme on tabs"
+   section of this file. */
 .tab-background-start[selected=true]:-moz-lwtheme::before,
 .tab-background-end[selected=true]:-moz-lwtheme::before {
   background-image: @fgTabTextureLWT@;
 }
 
 .tab-background-start[selected=true]:-moz-lwtheme::before,
 .tab-background-end[selected=true]:-moz-lwtheme::before,
 .tab-background-middle[selected=true]:-moz-lwtheme {
   background-color: transparent;
 }
 
+/*
+ * LightweightThemeListener will set the current lightweight theme's header
+ * image to the lwt-header-image variable, used in each of the following rulesets.
+ */
+
+/* Lightweight theme on tabs */
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
+  background-attachment: scroll, fixed;
+  background-color: transparent;
+  background-image: @fgTabTextureLWT@, var(--lwt-header-image);
+  background-position: 0 0, right top;
+  background-repeat: repeat-x, no-repeat;
+}
+
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
+  background-attachment: scroll, scroll, fixed;
+  background-color: transparent;
+  background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
+                    @fgTabTextureLWT@,
+                    var(--lwt-header-image);
+  background-position: 0 0, 0 0, right top;
+  background-repeat: repeat-x, repeat-x, no-repeat;
+}
+
 /* End selected tab */
 
 /* new tab button border and gradient on hover */
 .tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]),
 .tabs-newtab-button:hover {
   background-image: url(chrome://browser/skin/tabbrowser/tab-background-start.png),
                     url(chrome://browser/skin/tabbrowser/tab-background-middle.png),
                     url(chrome://browser/skin/tabbrowser/tab-background-end.png);
@@ -525,16 +551,22 @@
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-start@2x.png);
   }
 
   .tab-background-end[selected=true]:-moz-locale-dir(ltr)::after,
   .tab-background-start[selected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-end@2x.png);
   }
 
+  #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
+    background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
+                      @fgTabTextureLWT@,
+                      var(--lwt-header-image);
+  }
+
   .tab-throbber[busy] {
     list-style-image: url("chrome://browser/skin/tabbrowser/connecting@2x.png");
   }
 
   .tab-icon-image {
     list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
   }
 
deleted file mode 100644
--- a/browser/themes/windows/browser-lightweightTheme.css
+++ /dev/null
@@ -1,39 +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/. */
-
-%include windowsShared.inc
-%filter substitution
-
-/*
- * LightweightThemeListener will append the current lightweight theme's header
- * image to the background-image for each of the following rulesets.
- */
-
-/* Lightweight theme on tabs */
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-start[selected=true]:-moz-lwtheme::before,
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-end[selected=true]:-moz-lwtheme::before {
-  background-attachment: scroll, fixed;
-  background-color: transparent;
-  background-image: @fgTabTextureLWT@;/*, lwtHeader;*/
-  background-position: 0 0, right top;
-  background-repeat: repeat-x, no-repeat;
-}
-
-#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
-  background-attachment: scroll, scroll, fixed;
-  background-color: transparent;
-  background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
-                    @fgTabTextureLWT@;/*,
-                    lwtHeader;*/
-  background-position: 0 0, 0 0, right top;
-  background-repeat: repeat-x, repeat-x, no-repeat;
-}
-
-@media (min-resolution: 1.25dppx) {
-  #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
-    background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
-                      @fgTabTextureLWT@;/*,
-                      lwtHeader;*/
-  }
-}
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -21,17 +21,16 @@
   --space-above-tabbar: 15px;
 
   --backbutton-urlbar-overlap: 6px;
 
   /* icon width + border + horizontal padding (without the overlap from backbutton-urlbar-overlap) */
   --forwardbutton-width: 25px;
 
   --toolbarbutton-vertical-inner-padding: 2px;
-  --toolbarbutton-vertical-outer-padding: 8px;
 
   --toolbarbutton-hover-background: rgba(0,0,0,.1);
   --toolbarbutton-hover-bordercolor: rgba(0,0,0,.2);
   --toolbarbutton-hover-boxshadow: none;
 
   --toolbarbutton-active-background: rgba(0,0,0,.15);
   --toolbarbutton-active-bordercolor: rgba(0,0,0,.3);
   --toolbarbutton-active-boxshadow: 0 0 0 1px rgba(0,0,0,.15) inset;
@@ -635,17 +634,17 @@ toolbar[brighttext] .toolbarbutton-1 > .
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
   -moz-appearance: none;
   padding: 0;
 }
 
 #nav-bar .toolbarbutton-1:not([type=menu-button]),
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button,
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
-  padding: var(--toolbarbutton-vertical-outer-padding) 2px;
+  padding: 0 2px;
   -moz-box-pack: center;
 }
 
 #nav-bar #PanelUI-menu-button {
   padding-inline-start: 5px;
   padding-inline-end: 5px;
 }
 
@@ -1184,17 +1183,16 @@ toolbar[brighttext] #close-button {
   .searchbar-textbox {
     font-size: 1.15em;
     min-height: 28px;
   }
 
   :root {
     /* let toolbar buttons match the location and search bar's minimum height */
     --toolbarbutton-vertical-inner-padding: 4px;
-    --toolbarbutton-vertical-outer-padding: 5px;
   }
 }
 
 #urlbar:-moz-lwtheme,
 .searchbar-textbox:-moz-lwtheme {
   background-color: rgba(255,255,255,.8);
   color: black;
 }
@@ -2322,25 +2320,16 @@ notification.pluginVulnerable > .notific
   transform: perspective(0.01px);
 }
 
 #main-window[customize-entered] > #tab-view-deck {
   background-image: url("chrome://browser/skin/customizableui/customizeMode-gridTexture.png");
   background-attachment: fixed;
 }
 
-#main-window[customization-lwtheme] > #tab-view-deck:-moz-lwtheme {
-  background-repeat: no-repeat;
-  background-position: right top;
-  background-attachment: fixed;
-  /* The image will get set from CustomizeMode.jsm */
-  background-image: none;
-  background-color: transparent;
-}
-
 #main-window[customization-lwtheme]:-moz-lwtheme {
   background-image: url("chrome://browser/skin/customizableui/customizeMode-gridTexture.png");
   background-repeat: repeat;
   background-attachment: fixed;
   background-position: left top;
 }
 
 #main-window[customize-entered] #browser-bottombox,
--- a/browser/themes/windows/downloads/indicator.css
+++ b/browser/themes/windows/downloads/indicator.css
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+%include ../../shared/downloads/indicator.inc.css
+
 /*** Status and progress indicator ***/
 
 #downloads-animation-container {
   min-height: 1px;
   min-width: 1px;
   height: 1px;
   margin-bottom: -1px;
   /* Makes the outermost animation container element positioned, so that its
@@ -16,41 +18,41 @@
   /* The selected tab may overlap #downloads-indicator-notification */
   z-index: 5;
 }
 
 /*** Main indicator icon ***/
 
 @media not all and (min-resolution: 1.1dppx) {
   #downloads-button {
-    --downloads-indicator-icon: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 0, 198, 18, 180);
-    --downloads-indicator-icon-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 18, 198, 36, 180);
-    --downloads-indicator-icon-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
-    --downloads-indicator-icon-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 18, 198, 36, 180);
+    --downloads-indicator-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 0, 198, 18, 180);
+    --downloads-indicator-image-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 18, 198, 36, 180);
+    --downloads-indicator-image-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
+    --downloads-indicator-image-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 18, 198, 36, 180);
   }
 }
 
 @media (min-resolution: 1.1dppx) {
   #downloads-button {
-    --downloads-indicator-icon: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
-    --downloads-indicator-icon-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 36, 396, 72, 360);
-    --downloads-indicator-icon-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 0, 396, 36, 360);
-    --downloads-indicator-icon-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 36, 396, 72, 360);
+    --downloads-indicator-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
+    --downloads-indicator-image-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 36, 396, 72, 360);
+    --downloads-indicator-image-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 0, 396, 36, 360);
+    --downloads-indicator-image-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 36, 396, 72, 360);
   }
 }
 
 #downloads-indicator-icon {
-  background: var(--downloads-indicator-icon) center no-repeat;
+  background: var(--downloads-indicator-image) center no-repeat;
   width: 18px;
   height: 18px;
   background-size: 18px;
 }
 
 toolbar[brighttext] #downloads-button:not([attention="success"]) > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: var(--downloads-indicator-icon-inverted);
+  background-image: var(--downloads-indicator-image-inverted);
 }
 
 #downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
 #downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   display: -moz-box;
   height: 8px;
   width: 8px;
   min-width: 0;
@@ -74,46 +76,46 @@ toolbar[brighttext] #downloads-button:no
 }
 
 #downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive,
 #downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive {
   filter: none;
 }
 
 #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: var(--downloads-indicator-icon-attention);
+  background-image: var(--downloads-indicator-image-attention);
 }
 
 toolbar[brighttext] #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: var(--downloads-indicator-icon-attention-inverted);
+  background-image: var(--downloads-indicator-image-attention-inverted);
 }
 
 #downloads-button[cui-areatype="menu-panel"][attention="success"] {
   list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel.png");
   -moz-image-region: auto;
 }
 
 /* In the next few rules, we use :not([counter]) as a shortcut that is
    equivalent to -moz-any([progress], [paused]). */
 
 #downloads-button:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background: var(--downloads-indicator-icon) center no-repeat;
+  background: var(--downloads-indicator-image) center no-repeat;
   background-size: 12px;
 }
 
 toolbar[brighttext] #downloads-button:not([counter]):not([attention="success"]) > #downloads-indicator-anchor > #downloads-button-progress-area > #downloads-indicator-counter {
-  background-image: var(--downloads-indicator-icon-inverted);
+  background-image: var(--downloads-indicator-image-inverted);
 }
 
 #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: var(--downloads-indicator-icon-attention);
+  background-image: var(--downloads-indicator-image-attention);
 }
 
 toolbar[brighttext] #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: var(--downloads-indicator-icon-attention-inverted);
+  background-image: var(--downloads-indicator-image-attention-inverted);
 }
 
 /*** Download notifications ***/
 
 #downloads-indicator-notification {
   opacity: 0;
   background-size: 16px;
   background-position: center;
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -6,17 +6,16 @@ browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
 #include ../shared/jar.inc.mn
   skin/classic/browser/sanitizeDialog.css
   skin/classic/browser/aboutSessionRestore-window-icon.png
   skin/classic/browser/aboutSyncTabs.css
 * skin/classic/browser/syncedtabs/sidebar.css     (syncedtabs/sidebar.css)
 * skin/classic/browser/browser.css
 * skin/classic/browser/compacttheme.css
-* skin/classic/browser/browser-lightweightTheme.css
   skin/classic/browser/caption-buttons.svg
   skin/classic/browser/click-to-play-warning-stripes.png
   skin/classic/browser/Info.png
   skin/classic/browser/livemark-folder.png
   skin/classic/browser/menu-back.png
   skin/classic/browser/menu-forward.png
   skin/classic/browser/menuPanel-customize.png
   skin/classic/browser/menuPanel-customize@2x.png
--- a/build/buildconfig.py
+++ b/build/buildconfig.py
@@ -11,10 +11,10 @@ config = MozbuildObject.from_environment
 for var in ('topsrcdir', 'topobjdir', 'defines', 'non_global_defines',
             'substs'):
     value = getattr(config, var)
     setattr(sys.modules[__name__], var, value)
 
 substs = dict(substs)
 
 for var in os.environ:
-    if var != 'SHELL' and var in substs:
+    if var not in ('CPP', 'CXXCPP', 'SHELL') and var in substs:
         substs[var] = os.environ[var]
--- a/build/clang-plugin/RefCountedInsideLambdaChecker.cpp
+++ b/build/clang-plugin/RefCountedInsideLambdaChecker.cpp
@@ -23,25 +23,29 @@ void RefCountedInsideLambdaChecker::regi
       this);
   AstMatcher->addMatcher(
       classTemplateSpecializationDecl(hasAnyTemplateArgument(refersToType(
         recordType(hasDeclaration(cxxRecordDecl(
           isLambdaDecl()).bind("decl")))))),
       this);
 }
 
+void RefCountedInsideLambdaChecker::emitDiagnostics(SourceLocation Loc,
+                                                    StringRef Name,
+                                                    QualType Type) {
+  diag(Loc, "Refcounted variable '%0' of type %1 cannot be captured by a lambda",
+       DiagnosticIDs::Error) << Name << Type;
+  diag(Loc, "Please consider using a smart pointer",
+       DiagnosticIDs::Note);
+}
+
 void RefCountedInsideLambdaChecker::check(
     const MatchFinder::MatchResult &Result) {
   static DenseSet<const CXXRecordDecl*> CheckedDecls;
 
-  const char* Error =
-      "Refcounted variable %0 of type %1 cannot be captured by a lambda";
-  const char* Note =
-      "Please consider using a smart pointer";
-
   const CXXRecordDecl *Lambda = Result.Nodes.getNodeAs<CXXRecordDecl>("decl");
 
   if (const LambdaExpr *OuterLambda =
     Result.Nodes.getNodeAs<LambdaExpr>("lambdaExpr")) {
     const CXXMethodDecl *OpCall = OuterLambda->getCallOperator();
     QualType ReturnTy = OpCall->getReturnType();
     if (const CXXRecordDecl *Record = ReturnTy->getAsCXXRecordDecl()) {
       Lambda = Record;
@@ -53,23 +57,93 @@ void RefCountedInsideLambdaChecker::chec
   }
 
   // Don't report errors on the same declarations more than once.
   if (CheckedDecls.count(Lambda)) {
     return;
   }
   CheckedDecls.insert(Lambda);
 
-  for (const LambdaCapture Capture : Lambda->captures()) {
-    if (Capture.capturesVariable() && Capture.getCaptureKind() != LCK_ByRef) {
+  bool StrongRefToThisCaptured = false;
+
+  for (const LambdaCapture& Capture : Lambda->captures()) {
+    // Check if any of the captures are ByRef. If they are, we have nothing to
+    // report, as it's OK to capture raw pointers to refcounted objects so long as
+    // the Lambda doesn't escape the current scope, which is required by ByRef
+    // captures already.
+    if (Capture.getCaptureKind() == LCK_ByRef) {
+      return;
+    }
+
+    // Check if this capture is byvalue, and captures a strong reference to this.
+    // XXX: Do we want to make sure that this type which we are capturing is a "Smart Pointer" somehow?
+    if (!StrongRefToThisCaptured &&
+        Capture.capturesVariable() &&
+        Capture.getCaptureKind() == LCK_ByCopy) {
+      const VarDecl *Var = Capture.getCapturedVar();
+      if (Var->hasInit()) {
+        const Stmt *Init = Var->getInit();
+
+        // Ignore single argument constructors, and trivial nodes.
+        while (true) {
+          auto NewInit = IgnoreTrivials(Init);
+          if (auto ConstructExpr = dyn_cast<CXXConstructExpr>(NewInit)) {
+            if (ConstructExpr->getNumArgs() == 1) {
+              NewInit = ConstructExpr->getArg(0);
+            }
+          }
+          if (Init == NewInit) {
+            break;
+          }
+          Init = NewInit;
+        }
+
+        if (isa<CXXThisExpr>(Init)) {
+          StrongRefToThisCaptured = true;
+        }
+      }
+    }
+  }
+
+  // Now we can go through and produce errors for any captured variables or this pointers.
+  for (const LambdaCapture& Capture : Lambda->captures()) {
+    if (Capture.capturesVariable()) {
       QualType Pointee = Capture.getCapturedVar()->getType()->getPointeeType();
 
       if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
-        diag(Capture.getLocation(), Error,
-             DiagnosticIDs::Error) << Capture.getCapturedVar()
-                                   << Pointee;
-        diag(Capture.getLocation(), Note,
-             DiagnosticIDs::Note);
+        emitDiagnostics(Capture.getLocation(), Capture.getCapturedVar()->getName(), Pointee);
+        return;
+      }
+    }
+
+    // The situation with captures of `this` is more complex. All captures of
+    // `this` look the same-ish (they are LCK_This). We want to complain about
+    // captures of `this` where `this` is a refcounted type, and the capture is
+    // actually used in the body of the lambda (if the capture isn't used, then
+    // we don't care, because it's only being captured in order to give access
+    // to private methods).
+    //
+    // In addition, we don't complain about this, even if it is used, if it was
+    // captured implicitly when the LambdaCaptureDefault was LCD_ByRef, as that
+    // expresses the intent that the lambda won't leave the enclosing scope.
+    bool ImplicitByRefDefaultedCapture =
+      Capture.isImplicit() && Lambda->getLambdaCaptureDefault() == LCD_ByRef;
+    if (Capture.capturesThis() &&
+        !ImplicitByRefDefaultedCapture &&
+        !StrongRefToThisCaptured) {
+      ThisVisitor V(*this);
+      bool NotAborted = V.TraverseDecl(const_cast<CXXMethodDecl *>(Lambda->getLambdaCallOperator()));
+      if (!NotAborted) {
         return;
       }
     }
   }
 }
+
+bool RefCountedInsideLambdaChecker::ThisVisitor::VisitCXXThisExpr(CXXThisExpr *This) {
+  QualType Pointee = This->getType()->getPointeeType();
+  if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
+    Checker.emitDiagnostics(This->getLocStart(), "this", Pointee);
+    return false;
+  }
+
+  return true;
+}
--- a/build/clang-plugin/RefCountedInsideLambdaChecker.h
+++ b/build/clang-plugin/RefCountedInsideLambdaChecker.h
@@ -9,11 +9,24 @@
 
 class RefCountedInsideLambdaChecker : public BaseCheck {
 public:
   RefCountedInsideLambdaChecker(StringRef CheckName,
                                 ContextType *Context = nullptr)
     : BaseCheck(CheckName, Context) {}
   void registerMatchers(MatchFinder* AstMatcher) override;
   void check(const MatchFinder::MatchResult &Result) override;
+
+  void emitDiagnostics(SourceLocation Loc, StringRef Name, QualType Type);
+
+private:
+  class ThisVisitor : public RecursiveASTVisitor<ThisVisitor> {
+  public:
+    explicit ThisVisitor(RefCountedInsideLambdaChecker& Checker)
+      : Checker(Checker) {}
+
+    bool VisitCXXThisExpr(CXXThisExpr *This);
+  private:
+    RefCountedInsideLambdaChecker& Checker;
+  };
 };
 
 #endif
--- a/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp
+++ b/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp
@@ -1,24 +1,29 @@
 #include <functional>
 #define MOZ_STRONG_REF __attribute__((annotate("moz_strong_ref")))
+#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
 
 struct RefCountedBase {
   void AddRef();
   void Release();
 };
 
 template <class T>
 struct SmartPtr {
+  SmartPtr();
+  MOZ_IMPLICIT SmartPtr(T*);
   T* MOZ_STRONG_REF t;
   T* operator->() const;
 };
 
 struct R : RefCountedBase {
   void method();
+private:
+  void privateMethod();
 };
 
 void take(...);
 void foo() {
   R* ptr;
   SmartPtr<R> sp;
   take([&](R* argptr) {
     R* localptr;
@@ -542,8 +547,75 @@ void e() {
     return ([&sp](SmartPtr<R> argsp) {
       SmartPtr<R> localsp;
       take(sp);
       take(argsp);
       take(localsp);
     });
   };
 }
+
+void
+R::privateMethod() {
+  SmartPtr<R> self = this;
+  std::function<void()>([&]() {
+    self->method();
+  });
+  std::function<void()>([&]() {
+    self->privateMethod();
+  });
+  std::function<void()>([&]() {
+    this->method();
+  });
+  std::function<void()>([&]() {
+    this->privateMethod();
+  });
+  std::function<void()>([=]() {
+    self->method();
+  });
+  std::function<void()>([=]() {
+    self->privateMethod();
+  });
+  std::function<void()>([=]() {
+    this->method(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([=]() {
+    this->privateMethod(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([self]() {
+    self->method();
+  });
+  std::function<void()>([self]() {
+    self->privateMethod();
+  });
+  std::function<void()>([this]() {
+    this->method(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([this]() {
+    this->privateMethod(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([this]() {
+    method(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([this]() {
+    privateMethod(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([=]() {
+    method(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([=]() {
+    privateMethod(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([&]() {
+    method();
+  });
+  std::function<void()>([&]() {
+    privateMethod();
+  });
+
+  // It should be OK to go through `this` if we have captured a reference to it.
+  std::function<void()>([this, self]() {
+    this->method();
+    this->privateMethod();
+    method();
+    privateMethod();
+  });
+}
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -403,17 +403,18 @@ class ImportHook(object):
         # loading, remove the .pyc/.pyo file, and reload the module.
         # Since we already loaded the .pyc/.pyo module, if it had side
         # effects, they will have happened already, and loading the module
         # with the same name, from another directory may have the same side
         # effects (or different ones). We assume it's not a problem for the
         # python modules under our source directory (either because it
         # doesn't happen or because it doesn't matter).
         if not os.path.exists(module.__file__[:-1]):
-            os.remove(module.__file__)
+            if os.path.exists(module.__file__):
+                os.remove(module.__file__)
             del sys.modules[module.__name__]
             module = self(name, globals, locals, fromlist, level)
 
         return module
 
 
 # Install our hook
 __builtin__.__import__ = ImportHook(__builtin__.__import__)
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -148,17 +148,32 @@ EMBED_MANIFEST_AT=2
 
 endif # MKSHLIB
 endif # FORCE_SHARED_LIB
 endif # LIBRARY
 
 ifeq ($(OS_ARCH),WINNT)
 ifndef GNU_CC
 
+#
+# Unless we're building SIMPLE_PROGRAMS, all C++ files share a PDB file per
+# directory. For parallel builds, this PDB file is shared and locked by
+# MSPDBSRV.EXE, starting with MSVC8 SP1. If you're using MSVC 7.1 or MSVC8
+# without SP1, don't do parallel builds.
+#
+# The final PDB for libraries and programs is created by the linker and uses
+# a different name from the single PDB file created by the compiler. See
+# bug 462740.
+#
+
+ifdef SIMPLE_PROGRAMS
 COMPILE_PDB_FLAG ?= -Fd$(basename $(@F)).pdb
+else
+COMPILE_PDB_FLAG ?= -Fdgenerated.pdb
+endif
 COMPILE_CFLAGS += $(COMPILE_PDB_FLAG)
 COMPILE_CXXFLAGS += $(COMPILE_PDB_FLAG)
 
 LINK_PDBFILE ?= $(basename $(@F)).pdb
 ifdef MOZ_DEBUG
 CODFILE=$(basename $(@F)).cod
 endif
 
--- a/devtools/client/framework/components/moz.build
+++ b/devtools/client/framework/components/moz.build
@@ -3,10 +3,11 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
 DevToolsModules(
   'toolbox-controller.js',
   'toolbox-tab.js',
+  'toolbox-tabs.js',
   'toolbox-toolbar.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/components/toolbox-tabs.js
@@ -0,0 +1,154 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 {DOM, createClass, createFactory, PropTypes} = require("devtools/client/shared/vendor/react");
+
+const {findDOMNode} = require("devtools/client/shared/vendor/react-dom");
+const {button, div} = DOM;
+
+const Menu = require("devtools/client/framework/menu");
+const MenuItem = require("devtools/client/framework/menu-item");
+const ToolboxTab = createFactory(require("devtools/client/framework/components/toolbox-tab"));
+
+module.exports = createClass({
+  displayName: "ToolboxTabs",
+
+  // See toolbox-toolbar propTypes for details on the props used here.
+  propTypes: {
+    currentToolId: PropTypes.string,
+    focusButton: PropTypes.func,
+    focusedButton: PropTypes.string,
+    highlightedTool: PropTypes.string,
+    panelDefinitions: PropTypes.array,
+    selectTool: PropTypes.func,
+    toolbox: PropTypes.object,
+    L10N: PropTypes.object,
+  },
+
+  getInitialState() {
+    return {
+      overflow: false,
+    };
+  },
+
+  componentDidUpdate() {
+    this.addFlowEvents();
+  },
+
+  componentWillUnmount() {
+    this.removeFlowEvents();
+  },
+
+  addFlowEvents() {
+    this.removeFlowEvents();
+    let node = findDOMNode(this);
+    if (node) {
+      node.addEventListener("overflow", this.onOverflow);
+      node.addEventListener("underflow", this.onUnderflow);
+    }
+  },
+
+  removeFlowEvents() {
+    let node = findDOMNode(this);
+    if (node) {
+      node.removeEventListener("overflow", this.onOverflow);
+      node.removeEventListener("underflow", this.onUnderflow);
+    }
+  },
+
+  onOverflow() {
+    this.setState({
+      overflow: true
+    });
+  },
+
+  onUnderflow() {
+    this.setState({
+      overflow: false
+    });
+  },
+
+  /**
+   * Render all of the tabs, based on the panel definitions and builds out
+   * a toolbox tab for each of them. Will render an all-tabs button if the
+   * container has an overflow.
+   */
+  render() {
+    let {
+      currentToolId,
+      focusButton,
+      focusedButton,
+      highlightedTool,
+      panelDefinitions,
+      selectTool,
+    } = this.props;
+
+    let tabs = panelDefinitions.map(panelDefinition => ToolboxTab({
+      currentToolId,
+      focusButton,
+      focusedButton,
+      highlightedTool,
+      panelDefinition,
+      selectTool,
+    }));
+
+    // A wrapper is needed to get flex sizing correct in XUL.
+    return div(
+      {
+        className: "toolbox-tabs-wrapper"
+      },
+      div(
+        {
+          className: "toolbox-tabs"
+        },
+        tabs
+      ),
+      this.state.overflow ? renderAllToolsButton(this.props) : null
+    );
+  },
+});
+
+/**
+ * Render a button to access all tools, displayed only when the toolbar presents an
+ * overflow.
+ */
+function renderAllToolsButton(props) {
+  let {
+    currentToolId,
+    panelDefinitions,
+    selectTool,
+    toolbox,
+    L10N,
+  } = props;
+
+  return button({
+    className: "all-tools-menu all-tabs-menu",
+    tabIndex: -1,
+    title: L10N.getStr("toolbox.allToolsButton.tooltip"),
+    onClick: ({ target }) => {
+      let menu = new Menu({
+        id: "all-tools-menupopup"
+      });
+      panelDefinitions.forEach(({id, label}) => {
+        menu.append(new MenuItem({
+          checked: currentToolId === id,
+          click: () => {
+            selectTool(id);
+          },
+          id: "all-tools-menupopup-" + id,
+          label,
+        }));
+      });
+
+      let rect = target.getBoundingClientRect();
+      let screenX = target.ownerDocument.defaultView.mozInnerScreenX;
+      let screenY = target.ownerDocument.defaultView.mozInnerScreenY;
+
+      // Display the popup below the button.
+      menu.popup(rect.left + screenX, rect.bottom + screenY, toolbox);
+      return menu;
+    },
+  });
+}
--- a/devtools/client/framework/components/toolbox-toolbar.js
+++ b/devtools/client/framework/components/toolbox-toolbar.js
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {DOM, createClass, createFactory, PropTypes} = require("devtools/client/shared/vendor/react");
 const {div, button} = DOM;
+
 const ToolboxTab = createFactory(require("devtools/client/framework/components/toolbox-tab"));
+const ToolboxTabs = createFactory(require("devtools/client/framework/components/toolbox-tabs"));
 
 /**
  * This is the overall component for the toolbox toolbar. It is designed to not know how
  * the state is being managed, and attempts to be as pure as possible. The
  * ToolboxController component controls the changing state, and passes in everything as
  * props.
  */
 module.exports = createClass({
@@ -34,69 +36,43 @@ module.exports = createClass({
     focusButton: PropTypes.func,
     // The options button definition.
     optionsPanel: PropTypes.object,
     // Hold off displaying the toolbar until enough information is ready for it to render
     // nicely.
     canRender: PropTypes.bool,
     // Localization interface.
     L10N: PropTypes.object,
+    // The devtools toolbox
+    toolbox: PropTypes.object,
   },
 
   /**
    * The render function is kept fairly short for maintainability. See the individual
    * render functions for how each of the sections is rendered.
    */
   render() {
     const containerProps = {className: "devtools-tabbar"};
     return this.props.canRender
       ? (
         div(
           containerProps,
           renderToolboxButtonsStart(this.props),
-          renderTabs(this.props),
+          ToolboxTabs(this.props),
           renderToolboxButtonsEnd(this.props),
           renderOptions(this.props),
           renderSeparator(),
           renderDockButtons(this.props)
         )
       )
       : div(containerProps);
   }
 });
 
 /**
- * Render all of the tabs, this takes in the panel definitions and builds out
- * the buttons for each of them.
- *
- * @param {Array}    panelDefinitions - Array of objects that define panels.
- * @param {String}   currentToolId - The currently selected tool's id; e.g. "inspector".
- * @param {String}   highlightedTool - If a tool is highlighted, this is it's id.
- * @param {Function} selectTool - Function to select a tool in the toolbox.
- * @param {String}   focusedButton - The id of the focused button.
- * @param {Function} focusButton - Keep a record of the currently focused button.
- */
-function renderTabs({panelDefinitions, currentToolId, highlightedTool, selectTool,
-                     focusedButton, focusButton}) {
-  // A wrapper is needed to get flex sizing correct in XUL.
-  return div({className: "toolbox-tabs-wrapper"},
-    div({className: "toolbox-tabs"},
-      ...panelDefinitions.map(panelDefinition => ToolboxTab({
-        panelDefinition,
-        currentToolId,
-        highlightedTool,
-        selectTool,
-        focusedButton,
-        focusButton,
-      }))
-    )
-  );
-}
-
-/**
  * A little helper function to call renderToolboxButtons for buttons at the start
  * of the toolbox.
  */
 function renderToolboxButtonsStart(props) {
   return renderToolboxButtons(props, true);
 }
 
 /**
@@ -145,17 +121,17 @@ function renderToolboxButtons({toolboxBu
         onFocus: () => focusButton(id),
         tabIndex: id === focusedButton ? "0" : "-1"
       });
     })
   );
 }
 
 /**
- * The options button is a ToolboxTab just like in the renderTabs() function. However
+ * The options button is a ToolboxTab just like in the ToolboxTabs component. However
  * it is separate from the normal tabs, so deal with it separately here.
  *
  * @param {Object}   optionsPanel - A single panel definition for the options panel.
  * @param {String}   currentToolId - The currently selected tool's id; e.g. "inspector".
  * @param {Function} selectTool - Function to select a tool in the toolbox.
  * @param {String}   focusedButton - The id of the focused button.
  * @param {Function} focusButton - Keep a record of the currently focused button.
  */
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -75,16 +75,17 @@ skip-if = e10s # Bug 1069044 - destroyIn
 [browser_toolbox_target.js]
 [browser_toolbox_tabsswitch_shortcuts.js]
 [browser_toolbox_textbox_context_menu.js]
 [browser_toolbox_theme.js]
 [browser_toolbox_theme_registration.js]
 [browser_toolbox_toggle.js]
 [browser_toolbox_tool_ready.js]
 [browser_toolbox_tool_remote_reopen.js]
+[browser_toolbox_toolbar_overflow.js]
 [browser_toolbox_tools_per_toolbox_registration.js]
 [browser_toolbox_transport_events.js]
 [browser_toolbox_view_source_01.js]
 [browser_toolbox_view_source_02.js]
 [browser_toolbox_view_source_03.js]
 [browser_toolbox_view_source_04.js]
 [browser_toolbox_window_reload_target.js]
 [browser_toolbox_window_shortcuts.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/browser_toolbox_toolbar_overflow.js
@@ -0,0 +1,87 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/ */
+
+/* import-globals-from shared-head.js */
+"use strict";
+
+// Test that a button to access tools hidden by toolbar overflow is displayed when the
+// toolbar starts to present an overflow.
+let { Toolbox } = require("devtools/client/framework/toolbox");
+
+add_task(function* () {
+  let tab = yield addTab("about:blank");
+
+  info("Open devtools on the Inspector in a separate window");
+  let toolbox = yield openToolboxForTab(tab, "inspector", Toolbox.HostType.WINDOW);
+
+  let hostWindow = toolbox.win.parent;
+  let originalWidth = hostWindow.outerWidth;
+  let originalHeight = hostWindow.outerHeight;
+
+  info("Resize devtools window to a width that should not trigger any overflow");
+  let onResize = once(hostWindow, "resize");
+  hostWindow.resizeTo(640, 300);
+  yield onResize;
+
+  let allToolsButton = toolbox.doc.querySelector(".all-tools-menu");
+  ok(!allToolsButton, "The all tools button is not displayed");
+
+  info("Resize devtools window to a width that should trigger an overflow");
+  onResize = once(hostWindow, "resize");
+  hostWindow.resizeTo(300, 300);
+  yield onResize;
+
+  info("Wait until the all tools button is available");
+  yield waitUntil(() => toolbox.doc.querySelector(".all-tools-menu"));
+
+  allToolsButton = toolbox.doc.querySelector(".all-tools-menu");
+  ok(allToolsButton, "The all tools button is displayed");
+
+  info("Open the all-tools-menupopup and verify that the inspector button is checked");
+  let menuPopup = yield openAllToolsMenu(toolbox);
+
+  let inspectorButton = toolbox.doc.querySelector("#all-tools-menupopup-inspector");
+  ok(inspectorButton, "The inspector button is available");
+  ok(inspectorButton.getAttribute("checked"), "The inspector button is checked");
+
+  let consoleButton = toolbox.doc.querySelector("#all-tools-menupopup-webconsole");
+  ok(consoleButton, "The console button is available");
+  ok(!consoleButton.getAttribute("checked"), "The console button is not checked");
+
+  info("Switch to the webconsole using the all-tools-menupopup popup");
+  let onSelected = toolbox.once("webconsole-selected");
+  consoleButton.click();
+  yield onSelected;
+
+  info("Closing the all-tools-menupopup popup");
+  let onPopupHidden = once(menuPopup, "popuphidden");
+  menuPopup.hidePopup();
+  yield onPopupHidden;
+
+  info("Re-open the all-tools-menupopup and verify that the console button is checked");
+  menuPopup = yield openAllToolsMenu(toolbox);
+
+  inspectorButton = toolbox.doc.querySelector("#all-tools-menupopup-inspector");
+  ok(!inspectorButton.getAttribute("checked"), "The inspector button is not checked");
+
+  consoleButton = toolbox.doc.querySelector("#all-tools-menupopup-webconsole");
+  ok(consoleButton.getAttribute("checked"), "The console button is checked");
+
+  info("Restore the original window size");
+  hostWindow.resizeTo(originalWidth, originalHeight);
+});
+
+function* openAllToolsMenu(toolbox) {
+  let allToolsButton = toolbox.doc.querySelector(".all-tools-menu");
+  EventUtils.synthesizeMouseAtCenter(allToolsButton, {}, toolbox.win);
+
+  let menuPopup = toolbox.doc.querySelector("#all-tools-menupopup");
+  ok(menuPopup, "all-tools-menupopup is available");
+
+  info("Waiting for the menu popup to be displayed");
+  yield waitUntil(() => menuPopup && menuPopup.state === "open");
+
+  return menuPopup;
+}
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -999,16 +999,17 @@ Toolbox.prototype = {
     // Ensure the toolbar doesn't try to render until the tool is ready.
     const element = this.React.createElement(this.ToolboxController, {
       L10N,
       currentToolId: this.currentToolId,
       selectTool: this.selectTool,
       closeToolbox: this.destroy,
       focusButton: this._onToolbarFocus,
       toggleMinimizeMode: this._toggleMinimizeMode,
+      toolbox: this
     });
 
     this.component = this.ReactDOM.render(element, this._componentMount);
   },
 
   /**
    * Reset tabindex attributes across all focusable elements inside the toolbar.
    * Only have one element with tabindex=0 at a time to make sure that tabbing
rename from devtools/client/inspector/layout/actions/grids.js
rename to devtools/client/inspector/grids/actions/grids.js
rename from devtools/client/inspector/layout/actions/highlighter-settings.js
rename to devtools/client/inspector/grids/actions/highlighter-settings.js
rename from devtools/client/inspector/layout/actions/index.js
rename to devtools/client/inspector/grids/actions/index.js
rename from devtools/client/inspector/layout/actions/moz.build
rename to devtools/client/inspector/grids/actions/moz.build
rename from devtools/client/inspector/layout/components/Grid.js
rename to devtools/client/inspector/grids/components/Grid.js
--- a/devtools/client/inspector/layout/components/Grid.js
+++ b/devtools/client/inspector/grids/components/Grid.js
@@ -22,16 +22,17 @@ module.exports = createClass({
     getSwatchColorPickerTooltip: PropTypes.func.isRequired,
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
     highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
     setSelectedNode: PropTypes.func.isRequired,
     showGridOutline: PropTypes.bool.isRequired,
     onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onSetGridOverlayColor: PropTypes.func.isRequired,
     onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
+    onShowGridAreaHighlight: PropTypes.func.isRequired,
     onToggleGridHighlighter: PropTypes.func.isRequired,
     onToggleShowGridLineNumbers: PropTypes.func.isRequired,
     onToggleShowInfiniteLines: PropTypes.func.isRequired,
   },
 
   mixins: [ addons.PureRenderMixin ],
 
   render() {
@@ -42,26 +43,28 @@ module.exports = createClass({
       setSelectedNode,
       showGridOutline,
       onHideBoxModelHighlighter,
       onSetGridOverlayColor,
       onShowBoxModelHighlighterForNode,
       onToggleGridHighlighter,
       onToggleShowGridLineNumbers,
       onToggleShowInfiniteLines,
+      onShowGridAreaHighlight,
     } = this.props;
 
     return grids.length ?
       dom.div(
         {
           id: "layout-grid-container",
         },
         showGridOutline ?
           GridOutline({
             grids,
+            onShowGridAreaHighlight,
           })
           :
           null,
         dom.div(
           {
             className: "grid-content",
           },
           GridList({
rename from devtools/client/inspector/layout/components/GridDisplaySettings.js
rename to devtools/client/inspector/grids/components/GridDisplaySettings.js
rename from devtools/client/inspector/layout/components/GridItem.js
rename to devtools/client/inspector/grids/components/GridItem.js
rename from devtools/client/inspector/layout/components/GridList.js
rename to devtools/client/inspector/grids/components/GridList.js
rename from devtools/client/inspector/layout/components/GridOutline.js
rename to devtools/client/inspector/grids/components/GridOutline.js
--- a/devtools/client/inspector/layout/components/GridOutline.js
+++ b/devtools/client/inspector/grids/components/GridOutline.js
@@ -18,98 +18,183 @@ const VIEWPORT_HEIGHT = 100;
 const VIEWPORT_WIDTH = 450;
 
 module.exports = createClass({
 
   displayName: "GridOutline",
 
   propTypes: {
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
+    onShowGridAreaHighlight: PropTypes.func.isRequired,
   },
 
   mixins: [ addons.PureRenderMixin ],
 
   getInitialState() {
     return {
       selectedGrids: [],
     };
   },
 
   componentWillReceiveProps({ grids }) {
     this.setState({
       selectedGrids: grids.filter(grid => grid.highlighted),
     });
   },
 
-  renderGridCell(columnNumber, rowNumber, color) {
-    return dom.rect(
-      {
-        className: "grid-outline-cell",
-        x: columnNumber,
-        y: rowNumber,
-        width: 10,
-        height: 10,
-        stroke: color,
-      }
+  /**
+   * Returns the grid area name if the given grid cell is part of a grid area, otherwise
+   * null.
+   *
+   * @param  {Number} columnNumber
+   *         The column number of the grid cell.
+   * @param  {Number} rowNumber
+   *         The row number of the grid cell.
+   * @param  {Array} areas
+   *         Array of grid areas data stored in the grid fragment.
+   * @return {String} If there is a grid area return area name, otherwise null.
+   */
+  getGridAreaName(columnNumber, rowNumber, areas) {
+    const gridArea = areas.find(area =>
+      (area.rowStart <= rowNumber && area.rowEnd > rowNumber) &&
+      (area.columnStart <= columnNumber && area.columnEnd > columnNumber)
     );
+
+    if (!gridArea) {
+      return null;
+    }
+
+    return gridArea.name;
   },
 
-  renderGridFragment({ color, gridFragments }) {
+  /**
+   * Renders the grid outline for the given grid container object.
+   *
+   * @param  {Object} grid
+   *         A single grid container in the document.
+   */
+  renderGrid(grid) {
+    const { id, color, gridFragments } = grid;
     // TODO: We are drawing the first fragment since only one is currently being stored.
-    // In future we will need to iterate over all fragments of a grid.
-    const { rows, cols } = gridFragments[0];
-    const numOfColLines = cols.lines.length - 1;
-    const numOfRowLines = rows.lines.length - 1;
+    // In the future we will need to iterate over all fragments of a grid.
+    const { rows, cols, areas } = gridFragments[0];
+    const numberOfColumns = cols.lines.length - 1;
+    const numberOfRows = rows.lines.length - 1;
     const rectangles = [];
 
-    // Draw a rectangle that acts as a border for the grid outline
-    const border = this.renderGridOutlineBorder(numOfRowLines, numOfColLines, color);
+    // Draw a rectangle that acts as a border for the grid outline.
+    const border = this.renderGridOutlineBorder(numberOfRows, numberOfColumns, color);
     rectangles.push(border);
 
     let x = 1;
     let y = 1;
     const width = 10;
     const height = 10;
 
-    // Draw the cells within the grid outline border
-    for (let row = 0; row < numOfRowLines; row++) {
-      for (let col = 0; col < numOfColLines; col++) {
-        rectangles.push(this.renderGridCell(x, y, color));
+    // Draw the cells within the grid outline border.
+    for (let rowNumber = 1; rowNumber <= numberOfRows; rowNumber++) {
+      for (let columnNumber = 1; columnNumber <= numberOfColumns; columnNumber++) {
+        const gridAreaName = this.getGridAreaName(columnNumber, rowNumber, areas);
+        const gridCell = this.renderGridCell(id, x, y, rowNumber, columnNumber, color,
+          gridAreaName);
+
+        rectangles.push(gridCell);
         x += width;
       }
 
       x = 1;
       y += height;
     }
 
     return rectangles;
   },
 
-  renderGridOutline(gridFragments) {
+  /**
+   * Renders the grid cell of a grid fragment.
+   *
+   * @param  {Number} id
+   *         The grid id stored on the grid fragment
+   * @param  {Number} x
+   *         The x-position of the grid cell.
+   * @param  {Number} y
+   *         The y-position of the grid cell.
+   * @param  {Number} rowNumber
+   *         The row number of the grid cell.
+   * @param  {Number} columnNumber
+   *         The column number of the grid cell.
+   * @param  {String|null} gridAreaName
+   *         The grid area name or null if the grid cell is not part of a grid area.
+   */
+  renderGridCell(id, x, y, rowNumber, columnNumber, color, gridAreaName) {
+    return dom.rect(
+      {
+        className: "grid-outline-cell",
+        "data-grid-area-name": gridAreaName,
+        "data-grid-id": id,
+        x,
+        y,
+        width: 10,
+        height: 10,
+        fill: "none",
+        stroke: color,
+        onMouseOver: this.onMouseOverCell,
+        onMouseOut: this.onMouseLeaveCell,
+      }
+    );
+  },
+
+  renderGridOutline(grids) {
     return dom.g(
       {
         className: "grid-cell-group",
       },
-      gridFragments.map(gridFragment => this.renderGridFragment(gridFragment))
+      grids.map(grid => this.renderGrid(grid))
     );
   },
 
-  renderGridOutlineBorder(numberOfRows, numberOfCols, color) {
+  renderGridOutlineBorder(numberOfRows, numberOfColumns, color) {
     return dom.rect(
       {
         className: "grid-outline-border",
         x: 1,
         y: 1,
-        width: numberOfCols * 10,
+        width: numberOfColumns * 10,
         height: numberOfRows * 10,
         stroke: color,
       }
     );
   },
 
+  onMouseLeaveCell({ target }) {
+    const {
+      grids,
+      onShowGridAreaHighlight,
+    } = this.props;
+    const id = target.getAttribute("data-grid-id");
+    const color = target.getAttribute("stroke");
+
+    target.setAttribute("fill", "none");
+
+    onShowGridAreaHighlight(grids[id].nodeFront, null, color);
+  },
+
+  onMouseOverCell({ target }) {
+    const {
+      grids,
+      onShowGridAreaHighlight,
+    } = this.props;
+    const name = target.getAttribute("data-grid-area-name");
+    const id = target.getAttribute("data-grid-id");
+    const color = target.getAttribute("stroke");
+
+    target.setAttribute("fill", color);
+
+    onShowGridAreaHighlight(grids[id].nodeFront, name, color);
+  },
+
   render() {
     return this.state.selectedGrids.length ?
       dom.svg(
         {
           className: "grid-outline",
           width: "100%",
           height: 100,
           viewBox: `${TRANSLATE_X} ${TRANSLATE_Y} ${VIEWPORT_WIDTH} ${VIEWPORT_HEIGHT}`,
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/components/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'Grid.js',
+    'GridDisplaySettings.js',
+    'GridItem.js',
+    'GridList.js',
+    'GridOutline.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -0,0 +1,404 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 Services = require("Services");
+const { Task } = require("devtools/shared/task");
+
+const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
+
+const {
+  updateGridColor,
+  updateGridHighlighted,
+  updateGrids,
+} = require("./actions/grids");
+const {
+  updateShowGridLineNumbers,
+  updateShowInfiniteLines,
+} = require("./actions/highlighter-settings");
+
+const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
+const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";
+
+// Default grid colors.
+const GRID_COLORS = [
+  "#05E4EE",
+  "#BB9DFF",
+  "#FFB53B",
+  "#71F362",
+  "#FF90FF",
+  "#FF90FF",
+  "#1B80FF",
+  "#FF2647"
+];
+
+function GridInspector(inspector, window) {
+  this.document = window.document;
+  this.highlighters = inspector.highlighters;
+  this.inspector = inspector;
+  this.store = inspector.store;
+  this.walker = this.inspector.walker;
+
+  this.getSwatchColorPickerTooltip = this.getSwatchColorPickerTooltip.bind(this);
+  this.setSelectedNode = this.setSelectedNode.bind(this);
+  this.updateGridPanel = this.updateGridPanel.bind(this);
+
+  this.onGridLayoutChange = this.onGridLayoutChange.bind(this);
+  this.onHighlighterChange = this.onHighlighterChange.bind(this);
+  this.onSetGridOverlayColor = this.onSetGridOverlayColor.bind(this);
+  this.onShowBoxModelHighlighterForNode =
+    this.onShowBoxModelHighlighterForNode.bind(this);
+  this.onShowGridAreaHighlight = this.onShowGridAreaHighlight.bind(this);
+  this.onSidebarSelect = this.onSidebarSelect.bind(this);
+  this.onToggleGridHighlighter = this.onToggleGridHighlighter.bind(this);
+  this.onToggleShowGridLineNumbers = this.onToggleShowGridLineNumbers.bind(this);
+  this.onToggleShowInfiniteLines = this.onToggleShowInfiniteLines.bind(this);
+
+  this.init();
+}
+
+GridInspector.prototype = {
+
+  /**
+   * Initializes the grid inspector by fetching the LayoutFront from the walker, loading
+   * the highlighter settings and initalizing the SwatchColorPicker instance.
+   */
+  init: Task.async(function* () {
+    if (!this.inspector) {
+      return;
+    }
+
+    this.layoutInspector = yield this.inspector.walker.getLayoutInspector();
+
+    this.loadHighlighterSettings();
+
+    // Create a shared SwatchColorPicker instance to be reused by all GridItem components.
+    this.swatchColorPickerTooltip = new SwatchColorPickerTooltip(
+      this.inspector.toolbox.doc,
+      this.inspector,
+      {
+        supportsCssColor4ColorFunction: () => false
+      }
+    );
+
+    this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
+    this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
+    this.inspector.sidebar.on("select", this.onSidebarSelect);
+
+    this.onSidebarSelect();
+  }),
+
+  /**
+   * Destruction function called when the inspector is destroyed. Removes event listeners
+   * and cleans up references.
+   */
+  destroy() {
+    this.highlighters.off("grid-highlighter-hidden", this.onHighlighterChange);
+    this.highlighters.off("grid-highlighter-shown", this.onHighlighterChange);
+    this.inspector.sidebar.off("select", this.onSidebarSelect);
+    this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
+
+    this.swatchColorPickerTooltip.destroy();
+
+    this.document = null;
+    this.highlighters = null;
+    this.inspector = null;
+    this.layoutInspector = null;
+    this.store = null;
+    this.swatchColorPickerTooltip = null;
+    this.walker = null;
+  },
+
+  getComponentProps() {
+    return {
+      getSwatchColorPickerTooltip: this.getSwatchColorPickerTooltip,
+      setSelectedNode: this.setSelectedNode,
+      onSetGridOverlayColor: this.onSetGridOverlayColor,
+      onShowBoxModelHighlighterForNode: this.onShowBoxModelHighlighterForNode,
+      onShowGridAreaHighlight: this.onShowGridAreaHighlight,
+      onToggleGridHighlighter: this.onToggleGridHighlighter,
+      onToggleShowGridLineNumbers: this.onToggleShowGridLineNumbers,
+      onToggleShowInfiniteLines: this.onToggleShowInfiniteLines,
+    };
+  },
+
+  /**
+   * Returns the color set for the grid highlighter associated with the provided
+   * nodeFront.
+   *
+   * @param  {NodeFront} nodeFront
+   *         The NodeFront for which we need the color.
+   */
+  getGridColorForNodeFront(nodeFront) {
+    let { grids } = this.store.getState();
+
+    for (let grid of grids) {
+      if (grid.nodeFront === nodeFront) {
+        return grid.color;
+      }
+    }
+
+    return null;
+  },
+
+  /**
+   * Create a highlighter settings object for the provided nodeFront.
+   *
+   * @param  {NodeFront} nodeFront
+   *         The NodeFront for which we need highlighter settings.
+   */
+  getGridHighlighterSettings(nodeFront) {
+    let { highlighterSettings } = this.store.getState();
+
+    // Get the grid color for the provided nodeFront.
+    let color = this.getGridColorForNodeFront(nodeFront);
+
+    // Merge the grid color to the generic highlighter settings.
+    return Object.assign({}, highlighterSettings, {
+      color
+    });
+  },
+
+  /**
+   * Retrieve the shared SwatchColorPicker instance.
+   */
+  getSwatchColorPickerTooltip() {
+    return this.swatchColorPickerTooltip;
+  },
+
+  /**
+   * Returns true if the layout panel is visible, and false otherwise.
+   */
+  isPanelVisible() {
+    return this.inspector.toolbox.currentToolId === "inspector" &&
+           this.inspector.sidebar &&
+           this.inspector.sidebar.getCurrentTabID() === "layoutview";
+  },
+
+  /**
+   * Load the grid highligher display settings into the store from the stored preferences.
+   */
+  loadHighlighterSettings() {
+    let { dispatch } = this.store;
+
+    let showGridLineNumbers = Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS);
+    let showInfinteLines = Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF);
+
+    dispatch(updateShowGridLineNumbers(showGridLineNumbers));
+    dispatch(updateShowInfiniteLines(showInfinteLines));
+  },
+
+  /**
+   * Set the inspector selection.
+   *
+   * @param {NodeFront} nodeFront
+   *        The NodeFront corresponding to the new selection.
+   */
+  setSelectedNode(nodeFront) {
+    this.inspector.selection.setNodeFront(nodeFront, "layout-panel");
+  },
+
+  /**
+   * Updates the grid panel by dispatching the new grid data. This is called when the
+   * layout view becomes visible or the view needs to be updated with new grid data.
+   *
+   * @param  {Array|null} gridFronts
+   *         Optional array of all GridFront in the current page.
+   */
+  updateGridPanel: Task.async(function* (gridFronts) {
+    // Stop refreshing if the inspector or store is already destroyed.
+    if (!this.inspector || !this.store) {
+      return;
+    }
+
+    // Get all the GridFront from the server if no gridFronts were provided.
+    if (!gridFronts) {
+      gridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode);
+    }
+
+    let grids = [];
+    for (let i = 0; i < gridFronts.length; i++) {
+      let grid = gridFronts[i];
+      let nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
+
+      let fallbackColor = GRID_COLORS[i % GRID_COLORS.length];
+      let color = this.getGridColorForNodeFront(nodeFront) || fallbackColor;
+
+      grids.push({
+        id: i,
+        color,
+        gridFragments: grid.gridFragments,
+        highlighted: nodeFront == this.highlighters.gridHighlighterShown,
+        nodeFront,
+      });
+    }
+
+    this.store.dispatch(updateGrids(grids));
+  }),
+
+  /**
+   * Handler for "grid-layout-changed" events emitted from the LayoutActor.
+   *
+   * @param  {Array} grids
+   *         Array of all GridFront in the current page.
+   */
+  onGridLayoutChange(grids) {
+    if (this.isPanelVisible()) {
+      this.updateGridPanel(grids);
+    }
+  },
+
+  /**
+   * Handler for "grid-highlighter-shown" and "grid-highlighter-hidden" events emitted
+   * from the HighlightersOverlay. Updates the NodeFront's grid highlighted state.
+   *
+   * @param  {Event} event
+   *         Event that was triggered.
+   * @param  {NodeFront} nodeFront
+   *         The NodeFront of the grid container element for which the grid highlighter
+   *         is shown for.
+   */
+  onHighlighterChange(event, nodeFront) {
+    let highlighted = event === "grid-highlighter-shown";
+    this.store.dispatch(updateGridHighlighted(nodeFront, highlighted));
+  },
+
+  /**
+   * Handler for a change in the grid overlay color picker for a grid container.
+   *
+   * @param  {NodeFront} node
+   *         The NodeFront of the grid container element for which the grid color is
+   *         being updated.
+   * @param  {String} color
+   *         A hex string representing the color to use.
+   */
+  onSetGridOverlayColor(node, color) {
+    this.store.dispatch(updateGridColor(node, color));
+    let { grids } = this.store.getState();
+
+    // If the grid for which the color was updated currently has a highlighter, update
+    // the color.
+    for (let grid of grids) {
+      if (grid.nodeFront === node && grid.highlighted) {
+        let highlighterSettings = this.getGridHighlighterSettings(node);
+        this.highlighters.showGridHighlighter(node, highlighterSettings);
+      }
+    }
+  },
+
+  /**
+   * Shows the box-model highlighter on the element corresponding to the provided
+   * NodeFront.
+   *
+   * @param  {NodeFront} nodeFront
+   *         The node to highlight.
+   * @param  {Object} options
+   *         Options passed to the highlighter actor.
+   */
+  onShowBoxModelHighlighterForNode(nodeFront, options) {
+    let toolbox = this.inspector.toolbox;
+    toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
+  },
+
+  /**
+   * Highlights the grid area in the CSS Grid Highlighter for the given grid.
+   *
+   * @param  {NodeFront} node
+   *         The NodeFront of the grid container element for which the grid
+   *         highlighter is highlighted for.
+   * @param  {String} gridAreaName
+   *         The name of the grid area for which the grid highlighter
+   *         is highlighted for.
+   * @param  {String} color
+   *         The color of the grid area for which the grid highlighter
+   *         is highlighted for.
+   */
+  onShowGridAreaHighlight(node, gridAreaName, color) {
+    let { highlighterSettings } = this.store.getState();
+
+    highlighterSettings.showGridArea = gridAreaName;
+    highlighterSettings.color = color;
+
+    this.highlighters.showGridHighlighter(node, highlighterSettings);
+  },
+
+  /**
+   * Handler for the inspector sidebar select event. Starts listening for
+   * "grid-layout-changed" if the layout panel is visible. Otherwise, stop
+   * listening for grid layout changes. Finally, refresh the layout view if
+   * it is visible.
+   */
+  onSidebarSelect() {
+    if (!this.isPanelVisible()) {
+      this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
+      return;
+    }
+
+    this.layoutInspector.on("grid-layout-changed", this.onGridLayoutChange);
+    this.updateGridPanel();
+  },
+
+  /**
+   * Handler for a change in the input checkboxes in the GridList component.
+   * Toggles on/off the grid highlighter for the provided grid container element.
+   *
+   * @param  {NodeFront} node
+   *         The NodeFront of the grid container element for which the grid
+   *         highlighter is toggled on/off for.
+   */
+  onToggleGridHighlighter(node) {
+    let highlighterSettings = this.getGridHighlighterSettings(node);
+    this.highlighters.toggleGridHighlighter(node, highlighterSettings);
+  },
+
+  /**
+   * Handler for a change in the show grid line numbers checkbox in the
+   * GridDisplaySettings component. Toggles on/off the option to show the grid line
+   * numbers in the grid highlighter. Refreshes the shown grid highlighter for the
+   * grids currently highlighted.
+   *
+   * @param  {Boolean} enabled
+   *         Whether or not the grid highlighter should show the grid line numbers.
+   */
+  onToggleShowGridLineNumbers(enabled) {
+    this.store.dispatch(updateShowGridLineNumbers(enabled));
+    Services.prefs.setBoolPref(SHOW_GRID_LINE_NUMBERS, enabled);
+
+    let { grids } = this.store.getState();
+
+    for (let grid of grids) {
+      if (grid.highlighted) {
+        let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
+        this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
+      }
+    }
+  },
+
+  /**
+   * Handler for a change in the extend grid lines infinitely checkbox in the
+   * GridDisplaySettings component. Toggles on/off the option to extend the grid
+   * lines infinitely in the grid highlighter. Refreshes the shown grid highlighter
+   * for grids currently highlighted.
+   *
+   * @param  {Boolean} enabled
+   *         Whether or not the grid highlighter should extend grid lines infinitely.
+   */
+  onToggleShowInfiniteLines(enabled) {
+    this.store.dispatch(updateShowInfiniteLines(enabled));
+    Services.prefs.setBoolPref(SHOW_INFINITE_LINES_PREF, enabled);
+
+    let { grids } = this.store.getState();
+
+    for (let grid of grids) {
+      if (grid.highlighted) {
+        let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
+        this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
+      }
+    }
+  },
+
+};
+
+module.exports = GridInspector;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+    'actions',
+    'components',
+    'reducers',
+    'utils',
+]
+
+DevToolsModules(
+    'grid-inspector.js',
+    'types.js',
+)
rename from devtools/client/inspector/layout/reducers/grids.js
rename to devtools/client/inspector/grids/reducers/grids.js
rename from devtools/client/inspector/layout/reducers/highlighter-settings.js
rename to devtools/client/inspector/grids/reducers/highlighter-settings.js
rename from devtools/client/inspector/layout/reducers/moz.build
rename to devtools/client/inspector/grids/reducers/moz.build
rename from devtools/client/inspector/layout/types.js
rename to devtools/client/inspector/grids/types.js
rename from devtools/client/inspector/layout/utils/l10n.js
rename to devtools/client/inspector/grids/utils/l10n.js
rename from devtools/client/inspector/layout/utils/moz.build
rename to devtools/client/inspector/grids/utils/moz.build
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -20,16 +20,17 @@ const nodeConstants = require("devtools/
 const Telemetry = require("devtools/client/shared/telemetry");
 
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 
 const {HTMLBreadcrumbs} = require("devtools/client/inspector/breadcrumbs");
 const BoxModel = require("devtools/client/inspector/boxmodel/box-model");
 const {FontInspector} = require("devtools/client/inspector/fonts/fonts");
+const GridInspector = require("devtools/client/inspector/grids/grid-inspector");
 const {InspectorSearch} = require("devtools/client/inspector/inspector-search");
 const {RuleViewTool} = require("devtools/client/inspector/rules/rules");
 const HighlightersOverlay = require("devtools/client/inspector/shared/highlighters-overlay");
 const {ToolSidebar} = require("devtools/client/inspector/toolsidebar");
 const MarkupView = require("devtools/client/inspector/markup/markup");
 const {CommandUtils} = require("devtools/client/shared/developer-toolbar");
 const {ViewHelpers} = require("devtools/client/shared/widgets/view-helpers");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
@@ -572,16 +573,18 @@ Inspector.prototype = {
     this.ruleview = new RuleViewTool(this, this.panelWin);
     this.boxmodel = new BoxModel(this, this.panelWin);
 
     const {ComputedViewTool} =
       this.browserRequire("devtools/client/inspector/computed/computed");
     this.computedview = new ComputedViewTool(this, this.panelWin);
 
     if (Services.prefs.getBoolPref("devtools.layoutview.enabled")) {
+      this.gridInspector = new GridInspector(this, this.panelWin);
+
       const LayoutView = this.browserRequire("devtools/client/inspector/layout/layout");
       this.layoutview = new LayoutView(this, this.panelWin);
     }
 
     if (this.target.form.animationsActor) {
       this.sidebar.addFrameTab(
         "animationinspector",
         INSPECTOR_L10N.getStr("inspector.sidebar.animationInspectorTitle"),
--- a/devtools/client/inspector/layout/components/App.js
+++ b/devtools/client/inspector/layout/components/App.js
@@ -5,36 +5,39 @@
 "use strict";
 
 const { addons, createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
-const Accordion = createFactory(require("./Accordion"));
-const Grid = createFactory(require("./Grid"));
+const BoxModel = createFactory(require("devtools/client/inspector/boxmodel/components/BoxModel"));
+const Grid = createFactory(require("devtools/client/inspector/grids/components/Grid"));
 
-const BoxModel = createFactory(require("devtools/client/inspector/boxmodel/components/BoxModel"));
+const BoxModelTypes = require("devtools/client/inspector/boxmodel/types");
+const GridTypes = require("devtools/client/inspector/grids/types");
 
-const Types = require("../types");
-const { getStr } = require("../utils/l10n");
+const Accordion = createFactory(require("./Accordion"));
 
 const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
 const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
 
+const LAYOUT_STRINGS_URI = "devtools/client/locales/layout.properties";
+const LAYOUT_L10N = new LocalizationHelper(LAYOUT_STRINGS_URI);
+
 const App = createClass({
 
   displayName: "App",
 
   propTypes: {
-    boxModel: PropTypes.shape(Types.boxModel).isRequired,
+    boxModel: PropTypes.shape(BoxModelTypes.boxModel).isRequired,
     getSwatchColorPickerTooltip: PropTypes.func.isRequired,
-    grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
-    highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
+    grids: PropTypes.arrayOf(PropTypes.shape(GridTypes.grid)).isRequired,
+    highlighterSettings: PropTypes.shape(GridTypes.highlighterSettings).isRequired,
     setSelectedNode: PropTypes.func.isRequired,
     showBoxModelProperties: PropTypes.bool.isRequired,
     showGridOutline: PropTypes.bool.isRequired,
     onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onSetGridOverlayColor: PropTypes.func.isRequired,
     onShowBoxModelEditor: PropTypes.func.isRequired,
     onShowBoxModelHighlighter: PropTypes.func.isRequired,
     onToggleGridHighlighter: PropTypes.func.isRequired,
@@ -53,17 +56,17 @@ const App = createClass({
         items: [
           {
             header: BOXMODEL_L10N.getStr("boxmodel.title"),
             component: BoxModel,
             componentProps: this.props,
             opened: true,
           },
           {
-            header: getStr("layout.header"),
+            header: LAYOUT_L10N.getStr("layout.header"),
             component: Grid,
             componentProps: this.props,
             opened: true,
           },
         ]
       })
     );
   },
--- a/devtools/client/inspector/layout/components/moz.build
+++ b/devtools/client/inspector/layout/components/moz.build
@@ -3,14 +3,9 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'Accordion.css',
     'Accordion.js',
     'App.js',
-    'Grid.js',
-    'GridDisplaySettings.js',
-    'GridItem.js',
-    'GridList.js',
-    'GridOutline.js',
 )
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -1,231 +1,83 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.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 Services = require("Services");
-const { Task } = require("devtools/shared/task");
 
 const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
-const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
-
-const {
-  updateGridColor,
-  updateGridHighlighted,
-  updateGrids,
-} = require("./actions/grids");
-const {
-  updateShowGridLineNumbers,
-  updateShowInfiniteLines,
-} = require("./actions/highlighter-settings");
-
 const App = createFactory(require("./components/App"));
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
   new LocalizationHelper("devtools/client/locales/inspector.properties");
 
-const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
 const SHOW_GRID_OUTLINE_PREF = "devtools.gridinspector.showGridOutline";
-const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";
-
-// Default grid colors.
-const GRID_COLORS = [
-  "#05E4EE",
-  "#BB9DFF",
-  "#FFB53B",
-  "#71F362",
-  "#FF90FF",
-  "#FF90FF",
-  "#1B80FF",
-  "#FF2647"
-];
 
 function LayoutView(inspector, window) {
   this.document = window.document;
-  this.highlighters = inspector.highlighters;
   this.inspector = inspector;
   this.store = inspector.store;
-  this.walker = this.inspector.walker;
-
-  this.onGridLayoutChange = this.onGridLayoutChange.bind(this);
-  this.onHighlighterChange = this.onHighlighterChange.bind(this);
-  this.onSidebarSelect = this.onSidebarSelect.bind(this);
 
   this.init();
 }
 
 LayoutView.prototype = {
 
-  /**
-   * Initializes the layout view by fetching the LayoutFront from the walker, creating
-   * the redux store and adding the view into the inspector sidebar.
-   */
-  init: Task.async(function* () {
+  init() {
     if (!this.inspector) {
       return;
     }
 
     let {
       onHideBoxModelHighlighter,
       onShowBoxModelEditor,
       onShowBoxModelHighlighter,
     } = this.inspector.boxmodel.getComponentProps();
 
-    this.layoutInspector = yield this.inspector.walker.getLayoutInspector();
-
-    this.loadHighlighterSettings();
-
-    this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
-    this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
-    this.inspector.sidebar.on("select", this.onSidebarSelect);
-
-    // Create a shared SwatchColorPicker instance to be reused by all GridItem components.
-    this.swatchColorPickerTooltip = new SwatchColorPickerTooltip(
-      this.inspector.toolbox.doc,
-      this.inspector,
-      {
-        supportsCssColor4ColorFunction: () => false
-      }
-    );
+    let {
+      getSwatchColorPickerTooltip,
+      setSelectedNode,
+      onSetGridOverlayColor,
+      onShowBoxModelHighlighterForNode,
+      onShowGridAreaHighlight,
+      onToggleGridHighlighter,
+      onToggleShowGridLineNumbers,
+      onToggleShowInfiniteLines,
+    } = this.inspector.gridInspector.getComponentProps();
 
     let app = App({
-      /**
-       * Retrieve the shared SwatchColorPicker instance.
-       */
-      getSwatchColorPickerTooltip: () => {
-        return this.swatchColorPickerTooltip;
-      },
-
-      /**
-       * Set the inspector selection.
-       * @param {NodeFront} nodeFront
-       *        The NodeFront corresponding to the new selection.
-       */
-      setSelectedNode: (nodeFront) => {
-        this.inspector.selection.setNodeFront(nodeFront, "layout-panel");
-      },
-
+      getSwatchColorPickerTooltip,
+      setSelectedNode,
       /**
        * Shows the box model properties under the box model if true, otherwise, hidden by
        * default.
        */
       showBoxModelProperties: true,
 
       /**
        * Shows the grid outline if user preferences are set to true, otherwise, hidden by
        * default.
        */
       showGridOutline: Services.prefs.getBoolPref(SHOW_GRID_OUTLINE_PREF),
 
       onHideBoxModelHighlighter,
-
-      /**
-       * Handler for a change in the grid overlay color picker for a grid container.
-       *
-       * @param  {NodeFront} node
-       *         The NodeFront of the grid container element for which the grid color is
-       *         being updated.
-       * @param  {String} color
-       *         A hex string representing the color to use.
-       */
-      onSetGridOverlayColor: (node, color) => {
-        this.store.dispatch(updateGridColor(node, color));
-        let { grids } = this.store.getState();
-
-        // If the grid for which the color was updated currently has a highlighter, update
-        // the color.
-        for (let grid of grids) {
-          if (grid.nodeFront === node && grid.highlighted) {
-            let highlighterSettings = this.getGridHighlighterSettings(node);
-            this.highlighters.showGridHighlighter(node, highlighterSettings);
-          }
-        }
-      },
-
+      onSetGridOverlayColor,
       onShowBoxModelEditor,
       onShowBoxModelHighlighter,
-
-     /**
-       * Shows the box-model highlighter on the element corresponding to the provided
-       * NodeFront.
-       *
-       * @param  {NodeFront} nodeFront
-       *         The node to highlight.
-       * @param  {Object} options
-       *         Options passed to the highlighter actor.
-       */
-      onShowBoxModelHighlighterForNode: (nodeFront, options) => {
-        let toolbox = this.inspector.toolbox;
-        toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
-      },
-
-      /**
-       * Handler for a change in the input checkboxes in the GridList component.
-       * Toggles on/off the grid highlighter for the provided grid container element.
-       *
-       * @param  {NodeFront} node
-       *         The NodeFront of the grid container element for which the grid
-       *         highlighter is toggled on/off for.
-       */
-      onToggleGridHighlighter: node => {
-        let highlighterSettings = this.getGridHighlighterSettings(node);
-        this.highlighters.toggleGridHighlighter(node, highlighterSettings);
-      },
-
-      /**
-       * Handler for a change in the show grid line numbers checkbox in the
-       * GridDisplaySettings component. Toggles on/off the option to show the grid line
-       * numbers in the grid highlighter. Refreshes the shown grid highlighter for the
-       * grids currently highlighted.
-       *
-       * @param  {Boolean} enabled
-       *         Whether or not the grid highlighter should show the grid line numbers.
-       */
-      onToggleShowGridLineNumbers: enabled => {
-        this.store.dispatch(updateShowGridLineNumbers(enabled));
-        Services.prefs.setBoolPref(SHOW_GRID_LINE_NUMBERS, enabled);
-
-        let { grids } = this.store.getState();
-
-        for (let grid of grids) {
-          if (grid.highlighted) {
-            let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
-            this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
-          }
-        }
-      },
-
-      /**
-       * Handler for a change in the extend grid lines infinitely checkbox in the
-       * GridDisplaySettings component. Toggles on/off the option to extend the grid
-       * lines infinitely in the grid highlighter. Refreshes the shown grid highlighter
-       * for grids currently highlighted.
-       *
-       * @param  {Boolean} enabled
-       *         Whether or not the grid highlighter should extend grid lines infinitely.
-       */
-      onToggleShowInfiniteLines: enabled => {
-        this.store.dispatch(updateShowInfiniteLines(enabled));
-        Services.prefs.setBoolPref(SHOW_INFINITE_LINES_PREF, enabled);
-
-        let { grids } = this.store.getState();
-
-        for (let grid of grids) {
-          if (grid.highlighted) {
-            let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
-            this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
-          }
-        }
-      }
+      onShowBoxModelHighlighterForNode,
+      onShowGridAreaHighlight,
+      onToggleGridHighlighter,
+      onToggleShowGridLineNumbers,
+      onToggleShowInfiniteLines,
     });
 
     let provider = createElement(Provider, {
       store: this.store,
       id: "layoutview",
       title: INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2"),
       key: "layoutview",
     }, app);
@@ -233,170 +85,22 @@ LayoutView.prototype = {
     let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
 
     this.inspector.addSidebarTab(
       "layoutview",
       INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2"),
       provider,
       defaultTab == "layoutview"
     );
-  }),
-
-  /**
-   * Destruction function called when the inspector is destroyed. Removes event listeners
-   * and cleans up references.
-   */
-  destroy() {
-    this.highlighters.off("grid-highlighter-hidden", this.onHighlighterChange);
-    this.highlighters.off("grid-highlighter-shown", this.onHighlighterChange);
-    this.inspector.sidebar.off("select", this.onSidebarSelect);
-    this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
-
-    this.document = null;
-    this.inspector = null;
-    this.layoutInspector = null;
-    this.store = null;
-    this.walker = null;
-  },
-
-  /**
-   * Returns the color set for the grid highlighter associated with the provided
-   * nodeFront.
-   *
-   * @param  {NodeFront} nodeFront
-   *         The NodeFront for which we need the color.
-   */
-  getGridColorForNodeFront(nodeFront) {
-    let { grids } = this.store.getState();
-
-    for (let grid of grids) {
-      if (grid.nodeFront === nodeFront) {
-        return grid.color;
-      }
-    }
-
-    return null;
-  },
-
-  /**
-   * Create a highlighter settings object for the provided nodeFront.
-   *
-   * @param  {NodeFront} nodeFront
-   *         The NodeFront for which we need highlighter settings.
-   */
-  getGridHighlighterSettings(nodeFront) {
-    let { highlighterSettings } = this.store.getState();
-
-    // Get the grid color for the provided nodeFront.
-    let color = this.getGridColorForNodeFront(nodeFront);
-
-    // Merge the grid color to the generic highlighter settings.
-    return Object.assign({}, highlighterSettings, {
-      color
-    });
-  },
-
-  /**
-   * Returns true if the layout panel is visible, and false otherwise.
-   */
-  isPanelVisible() {
-    return this.inspector.toolbox.currentToolId === "inspector" &&
-           this.inspector.sidebar &&
-           this.inspector.sidebar.getCurrentTabID() === "layoutview";
-  },
-
-  /**
-   * Load the grid highligher display settings into the store from the stored preferences.
-   */
-  loadHighlighterSettings() {
-    let { dispatch } = this.store;
-
-    let showGridLineNumbers = Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS);
-    let showInfinteLines = Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF);
-
-    dispatch(updateShowGridLineNumbers(showGridLineNumbers));
-    dispatch(updateShowInfiniteLines(showInfinteLines));
   },
 
   /**
-   * Updates the grid panel by dispatching the new grid data. This is called when the
-   * layout view becomes visible or the view needs to be updated with new grid data.
-   *
-   * @param {Array|null} gridFronts
-   *        Optional array of all GridFront in the current page.
+   * Destruction function called when the inspector is destroyed. Cleans up references.
    */
-  updateGridPanel: Task.async(function* (gridFronts) {
-    // Stop refreshing if the inspector or store is already destroyed.
-    if (!this.inspector || !this.store) {
-      return;
-    }
-
-    // Get all the GridFront from the server if no gridFronts were provided.
-    if (!gridFronts) {
-      gridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode);
-    }
-
-    let grids = [];
-    for (let i = 0; i < gridFronts.length; i++) {
-      let grid = gridFronts[i];
-      let nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
-
-      let fallbackColor = GRID_COLORS[i % GRID_COLORS.length];
-      let color = this.getGridColorForNodeFront(nodeFront) || fallbackColor;
-
-      grids.push({
-        id: i,
-        color,
-        gridFragments: grid.gridFragments,
-        highlighted: nodeFront == this.highlighters.gridHighlighterShown,
-        nodeFront,
-      });
-    }
-
-    this.store.dispatch(updateGrids(grids));
-  }),
-
-  /**
-   * Handler for "grid-layout-changed" events emitted from the LayoutActor.
-   *
-   * @param  {Array} grids
-   *         Array of all GridFront in the current page.
-   */
-  onGridLayoutChange(grids) {
-    if (this.isPanelVisible()) {
-      this.updateGridPanel(grids);
-    }
-  },
-
-  /**
-   * Handler for "grid-highlighter-shown" and "grid-highlighter-hidden" events emitted
-   * from the HighlightersOverlay. Updates the NodeFront's grid highlighted state.
-   *
-   * @param  {Event} event
-   *         Event that was triggered.
-   * @param  {NodeFront} nodeFront
-   *         The NodeFront of the grid container element for which the grid highlighter
-   *         is shown for.
-   */
-  onHighlighterChange(event, nodeFront) {
-    let highlighted = event === "grid-highlighter-shown";
-    this.store.dispatch(updateGridHighlighted(nodeFront, highlighted));
-  },
-
-  /**
-   * Handler for the inspector sidebar select event. Starts listening for
-   * "grid-layout-changed" if the layout panel is visible. Otherwise, stop
-   * listening for grid layout changes. Finally, refresh the layout view if
-   * it is visible.
-   */
-  onSidebarSelect() {
-    if (!this.isPanelVisible()) {
-      this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
-      return;
-    }
-
-    this.layoutInspector.on("grid-layout-changed", this.onGridLayoutChange);
-    this.updateGridPanel();
+  destroy() {
+    this.document = null;
+    this.inspector = null;
+    this.store = null;
   },
 
 };
 
 module.exports = LayoutView;
--- a/devtools/client/inspector/layout/moz.build
+++ b/devtools/client/inspector/layout/moz.build
@@ -1,17 +1,13 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
-    'actions',
     'components',
-    'reducers',
-    'utils',
 ]
 
 DevToolsModules(
     'layout.js',
-    'types.js',
 )
--- a/devtools/client/inspector/moz.build
+++ b/devtools/client/inspector/moz.build
@@ -2,16 +2,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/.
 
 DIRS += [
     'boxmodel',
     'components',
     'computed',
     'fonts',
+    'grids',
     'layout',
     'markup',
     'rules',
     'shared'
 ]
 
 DevToolsModules(
     'breadcrumbs.js',
--- a/devtools/client/inspector/reducers.js
+++ b/devtools/client/inspector/reducers.js
@@ -3,10 +3,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // This file exposes the Redux reducers of the box model, grid and grid highlighter
 // settings.
 
 exports.boxModel = require("devtools/client/inspector/boxmodel/reducers/box-model");
-exports.grids = require("devtools/client/inspector/layout/reducers/grids");
-exports.highlighterSettings = require("devtools/client/inspector/layout/reducers/highlighter-settings");
+exports.grids = require("devtools/client/inspector/grids/reducers/grids");
+exports.highlighterSettings = require("devtools/client/inspector/grids/reducers/highlighter-settings");
--- a/devtools/client/jsonview/converter-sniffer.js
+++ b/devtools/client/jsonview/converter-sniffer.js
@@ -9,17 +9,17 @@
 const {Cc, Ci, Cu, Cm, Cr, components} = require("chrome");
 const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
 const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
 
 const categoryManager = Cc["@mozilla.org/categorymanager;1"]
   .getService(Ci.nsICategoryManager);
 
 // Constants
-const JSON_TYPE = "application/json";
+const JSON_TYPES = ["application/json", "application/manifest+json"];
 const CONTRACT_ID = "@mozilla.org/devtools/jsonview-sniffer;1";
 const CLASS_ID = components.ID("{4148c488-dca1-49fc-a621-2a0097a62422}");
 const CLASS_DESCRIPTION = "JSONView content sniffer";
 const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
 const JSON_VIEW_TYPE = "JSON View";
 const CONTENT_SNIFFER_CATEGORY = "net-content-sniffers";
 
 function isTopLevelLoad(request) {
@@ -57,19 +57,20 @@ Sniffer.prototype = {
         if (request.contentDisposition ==
           Ci.nsIChannel.DISPOSITION_ATTACHMENT) {
           return "";
         }
       } catch (e) {
         // Channel doesn't support content dispositions
       }
 
-      // Check the response content type and if it's application/json
+      // Check the response content type and if it's a valid type
+      // such as application/json or application/manifest+json
       // change it to new internal type consumed by JSON View.
-      if (request.contentType == JSON_TYPE) {
+      if (JSON_TYPES.includes(request.contentType)) {
         return JSON_VIEW_MIME_TYPE;
       }
     }
 
     return "";
   }
 };
 
--- a/devtools/client/jsonview/test/browser.ini
+++ b/devtools/client/jsonview/test/browser.ini
@@ -3,16 +3,18 @@ tags = devtools
 subsuite = devtools
 support-files =
   array_json.json
   array_json.json^headers^
   doc_frame_script.js
   head.js
   invalid_json.json
   invalid_json.json^headers^
+  manifest_json.json
+  manifest_json.json^headers^
   simple_json.json
   simple_json.json^headers^
   valid_json.json
   valid_json.json^headers^
   !/devtools/client/commandline/test/head.js
   !/devtools/client/framework/test/head.js
   !/devtools/client/framework/test/shared-head.js
 
@@ -24,8 +26,9 @@ subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_jsonview_copy_rawdata.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_jsonview_filter.js]
 [browser_jsonview_invalid_json.js]
 [browser_jsonview_valid_json.js]
 [browser_jsonview_save_json.js]
+[browser_jsonview_manifest.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/jsonview/test/browser_jsonview_manifest.js
@@ -0,0 +1,17 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_JSON_URL = URL_ROOT + "manifest_json.json";
+
+add_task(function* () {
+  info("Test manifest JSON file started");
+
+  yield addJsonViewTab(TEST_JSON_URL);
+
+  let count = yield getElementCount(".jsonPanelBox .treeTable .treeRow");
+  is(count, 37, "There must be expected number of rows");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/jsonview/test/manifest_json.json
@@ -0,0 +1,39 @@
+{
+  "name": "HackerWeb",
+  "short_name": "HackerWeb",
+  "start_url": ".",
+  "display": "standalone",
+  "background_color": "#fff",
+  "description": "A simply readable Hacker News app.",
+  "icons": [{
+    "src": "images/touch/homescreen48.png",
+    "sizes": "48x48",
+    "type": "image/png"
+  }, {
+    "src": "images/touch/homescreen72.png",
+    "sizes": "72x72",
+    "type": "image/png"
+  }, {
+    "src": "images/touch/homescreen96.png",
+    "sizes": "96x96",
+    "type": "image/png"
+  }, {
+    "src": "images/touch/homescreen144.png",
+    "sizes": "144x144",
+    "type": "image/png"
+  }, {
+    "src": "images/touch/homescreen168.png",
+    "sizes": "168x168",
+    "type": "image/png"
+  }, {
+    "src": "images/touch/homescreen192.png",
+    "sizes": "192x192",
+    "type": "image/png"
+  }],
+  "related_applications": [{
+    "platform": "web"
+  }, {
+    "platform": "play",
+    "url": "https://play.google.com/store/apps/details?id=cheeaun.hackerweb"
+  }]
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/jsonview/test/manifest_json.json^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/manifest+json; charset=utf-8
--- a/devtools/client/locales/en-US/toolbox.properties
+++ b/devtools/client/locales/en-US/toolbox.properties
@@ -168,8 +168,12 @@ toolbox.frames.tooltip=Select an iframe 
 # the button to force the popups/panels to stay visible on blur.
 # This is only visible in the browser toolbox as it is meant for
 # addon developers and Firefox contributors.
 toolbox.noautohide.tooltip=Disable popup auto hide
 
 # LOCALIZATION NOTE (toolbox.closebutton.tooltip): This is the tooltip for
 # the close button the developer tools toolbox.
 toolbox.closebutton.tooltip=Close Developer Tools
+
+# LOCALIZATION NOTE (toolbox.allToolsButton.tooltip): This is the tooltip for the
+# "all tools" button displayed when some tools are hidden by overflow of the toolbar.
+toolbox.allToolsButton.tooltip=Select another tool
--- a/devtools/client/netmonitor/components/network-monitor.js
+++ b/devtools/client/netmonitor/components/network-monitor.js
@@ -7,28 +7,28 @@
 const {
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 // Components
-const MonitoPanel = createFactory(require("./monitor-panel"));
+const MonitorPanel = createFactory(require("./monitor-panel"));
 const StatisticsPanel = createFactory(require("./statistics-panel"));
 
 const { div } = DOM;
 
 /*
  * Network monitor component
  */
 function NetworkMonitor({ statisticsOpen }) {
   return (
     div({ className: "network-monitor" },
-      !statisticsOpen ? MonitoPanel() : StatisticsPanel()
+      !statisticsOpen ? MonitorPanel() : StatisticsPanel()
     )
   );
 }
 
 NetworkMonitor.displayName = "NetworkMonitor";
 
 NetworkMonitor.propTypes = {
   statisticsOpen: PropTypes.bool.isRequired,
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -1,25 +1,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.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 Services = require("Services");
 const { TimelineFront } = require("devtools/shared/fronts/timeline");
 const { CurlUtils } = require("devtools/client/shared/curl");
 const { ACTIVITY_TYPE, EVENTS } = require("./constants");
 const { configureStore } = require("./store");
 const Actions = require("./actions/index");
 const {
   fetchHeaders,
   formDataURI,
 } = require("./utils/request-utils");
 const {
+  onFirefoxConnect,
+  onFirefoxDisconnect,
+} = require("./utils/client");
+const {
   getRequestById,
   getDisplayedRequestById,
 } = require("./selectors/index");
 
 const gStore = window.gStore = configureStore();
 
 /**
  * Object defining the network monitor controller components.
@@ -49,17 +52,18 @@ var NetMonitorController = {
    *         A promise that is resolved when the monitor finishes shutdown.
    */
   shutdownNetMonitor() {
     if (this._shutdown) {
       return this._shutdown;
     }
     this._shutdown = new Promise(async (resolve) => {
       gStore.dispatch(Actions.batchReset());
-      this.TargetEventsHandler.disconnect();
+      onFirefoxDisconnect(this._target);
+      this._target.off("close", this._onTabDetached);
       this.NetworkEventsHandler.disconnect();
       await this.disconnect();
       resolve();
     });
 
     return this._shutdown;
   },
 
@@ -71,38 +75,40 @@ var NetMonitorController = {
    *
    * @return object
    *         A promise that is resolved when the monitor finishes connecting.
    */
   connect() {
     if (this._connection) {
       return this._connection;
     }
+    this._onTabDetached = this.shutdownNetMonitor.bind(this);
+
     this._connection = new Promise(async (resolve) => {
       // Some actors like AddonActor or RootActor for chrome debugging
       // aren't actual tabs.
       if (this._target.isTabActor) {
         this.tabClient = this._target.activeTab;
       }
+      this.webConsoleClient = this._target.activeConsole;
 
       let connectTimeline = () => {
         // Don't start up waiting for timeline markers if the server isn't
         // recent enough to emit the markers we're interested in.
         if (this._target.getTrait("documentLoadingMarkers")) {
           this.timelineFront = new TimelineFront(this._target.client,
             this._target.form);
           return this.timelineFront.start({ withDocLoadingEvents: true });
         }
         return undefined;
       };
-
-      this.webConsoleClient = this._target.activeConsole;
       await connectTimeline();
 
-      this.TargetEventsHandler.connect();
+      onFirefoxConnect(this._target);
+      this._target.on("close", this._onTabDetached);
       this.NetworkEventsHandler.connect();
 
       window.emit(EVENTS.CONNECTED);
 
       resolve();
       this._connected = true;
     });
     return this._connection;
@@ -354,88 +360,16 @@ var NetMonitorController = {
 
       window.on(EVENTS.NETWORK_EVENT, onRequest);
       window.on(EVENTS.RECEIVED_EVENT_TIMINGS, onTimings);
     });
   },
 };
 
 /**
- * Functions handling target-related lifetime events.
- */
-function TargetEventsHandler() {
-  this._onTabNavigated = this._onTabNavigated.bind(this);
-  this._onTabDetached = this._onTabDetached.bind(this);
-}
-
-TargetEventsHandler.prototype = {
-  get target() {
-    return NetMonitorController._target;
-  },
-
-  /**
-   * Listen for events emitted by the current tab target.
-   */
-  connect: function () {
-    this.target.on("close", this._onTabDetached);
-    this.target.on("navigate", this._onTabNavigated);
-    this.target.on("will-navigate", this._onTabNavigated);
-  },
-
-  /**
-   * Remove events emitted by the current tab target.
-   */
-  disconnect: function () {
-    if (!this.target) {
-      return;
-    }
-    this.target.off("close", this._onTabDetached);
-    this.target.off("navigate", this._onTabNavigated);
-    this.target.off("will-navigate", this._onTabNavigated);
-  },
-
-  /**
-   * Called for each location change in the monitored tab.
-   *
-   * @param string type
-   *        Packet type.
-   * @param object packet
-   *        Packet received from the server.
-   */
-  _onTabNavigated: function (type, packet) {
-    switch (type) {
-      case "will-navigate": {
-        // Reset UI.
-        if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
-          gStore.dispatch(Actions.batchReset());
-          gStore.dispatch(Actions.clearRequests());
-        } else {
-          // If the log is persistent, just clear all accumulated timing markers.
-          gStore.dispatch(Actions.clearTimingMarkers());
-        }
-
-        window.emit(EVENTS.TARGET_WILL_NAVIGATE);
-        break;
-      }
-      case "navigate": {
-        window.emit(EVENTS.TARGET_DID_NAVIGATE);
-        break;
-      }
-    }
-  },
-
-  /**
-   * Called when the monitored tab is closed.
-   */
-  _onTabDetached: function () {
-    NetMonitorController.shutdownNetMonitor();
-  }
-};
-
-/**
  * Functions handling target network events.
  */
 function NetworkEventsHandler() {
   this.addRequest = this.addRequest.bind(this);
   this.updateRequest = this.updateRequest.bind(this);
   this.getString = this.getString.bind(this);
   this._onNetworkEvent = this._onNetworkEvent.bind(this);
   this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
@@ -886,13 +820,15 @@ NetworkEventsHandler.prototype = {
     if (typeof stringGrip === "string") {
       return Promise.resolve(stringGrip);
     }
 
     return this.webConsoleClient.getString(stringGrip);
   }
 };
 
-NetMonitorController.TargetEventsHandler = new TargetEventsHandler();
+/**
+ * Preliminary setup for the NetMonitorController object.
+ */
 NetMonitorController.NetworkEventsHandler = new NetworkEventsHandler();
 window.gNetwork = NetMonitorController.NetworkEventsHandler;
 
 exports.NetMonitorController = NetMonitorController;
--- a/devtools/client/netmonitor/netmonitor.js
+++ b/devtools/client/netmonitor/netmonitor.js
@@ -1,53 +1,52 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* exported Netmonitor */
+
 "use strict";
 
 const { BrowserLoader } = Components.utils.import("resource://devtools/client/shared/browser-loader.js", {});
 
-function Netmonitor(toolbox) {
-  const require = window.windowRequire = BrowserLoader({
-    baseURI: "resource://devtools/client/netmonitor/",
-    window,
-    commonLibRequire: toolbox.browserRequire,
-  }).require;
+var Netmonitor = {
+  bootstrap: ({ tabTarget, toolbox }) => {
+    const require = window.windowRequire = BrowserLoader({
+      baseURI: "resource://devtools/client/netmonitor/",
+      window,
+      commonLibRequire: toolbox.browserRequire,
+    }).require;
 
-  // Inject EventEmitter into netmonitor window.
-  const EventEmitter = require("devtools/shared/event-emitter");
-  EventEmitter.decorate(window);
-
-  window.NetMonitorController = require("./netmonitor-controller").NetMonitorController;
-  window.NetMonitorController._toolbox = toolbox;
-  window.NetMonitorController._target = toolbox.target;
-}
-
-Netmonitor.prototype = {
-  init() {
-    const require = window.windowRequire;
+    const EventEmitter = require("devtools/shared/event-emitter");
     const { createFactory } = require("devtools/client/shared/vendor/react");
     const { render } = require("devtools/client/shared/vendor/react-dom");
     const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
 
     // Components
     const NetworkMonitor = createFactory(require("./components/network-monitor"));
 
-    this.networkMonitor = document.querySelector(".root");
+    // Inject EventEmitter into netmonitor window.
+    EventEmitter.decorate(window);
+
+    window.NetMonitorController = require("./netmonitor-controller").NetMonitorController;
+    window.NetMonitorController._toolbox = toolbox;
+    window.NetMonitorController._target = tabTarget;
+
+    this.root = document.querySelector(".root");
 
     render(Provider(
       { store: window.gStore },
-      NetworkMonitor({ toolbox: window.NetMonitorController._toolbox }),
-    ), this.networkMonitor);
+      NetworkMonitor(),
+    ), this.root);
 
     return window.NetMonitorController.startupNetMonitor();
   },
 
-  destroy() {
+  destroy: () => {
     const require = window.windowRequire;
     const { unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
 
-    unmountComponentAtNode(this.networkMonitor);
+    unmountComponentAtNode(this.root);
 
     return window.NetMonitorController.shutdownNetMonitor();
   }
 };
--- a/devtools/client/netmonitor/panel.js
+++ b/devtools/client/netmonitor/panel.js
@@ -1,32 +1,33 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 function NetMonitorPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
-  this.panelDoc = iframeWindow.document;
   this.toolbox = toolbox;
-  this.netmonitor = new iframeWindow.Netmonitor(toolbox);
 }
 
 NetMonitorPanel.prototype = {
   async open() {
     if (!this.toolbox.target.isRemote) {
       await this.toolbox.target.makeRemote();
     }
-    await this.netmonitor.init();
+    await this.panelWin.Netmonitor.bootstrap({
+      tabTarget: this.toolbox.target,
+      toolbox: this.toolbox,
+    });
     this.emit("ready");
     this.isReady = true;
     return this;
   },
 
   async destroy() {
-    await this.netmonitor.destroy();
+    await this.panelWin.Netmonitor.destroy();
     this.emit("destroyed");
     return this;
   },
 };
 
 exports.NetMonitorPanel = NetMonitorPanel;
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/utils/client.js
@@ -0,0 +1,65 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* global gStore */
+
+"use strict";
+
+const Services = require("Services");
+const Actions = require("../actions/index");
+const { EVENTS } = require("../constants");
+
+/**
+ * Called for each location change in the monitored tab.
+ *
+ * @param {String} type Packet type.
+ * @param {Object} packet Packet received from the server.
+ */
+function navigated(type) {
+  window.emit(EVENTS.TARGET_DID_NAVIGATE);
+}
+
+/**
+ * Called for each location change in the monitored tab.
+ *
+ * @param {String} type Packet type.
+ * @param {Object} packet Packet received from the server.
+ */
+function willNavigate(type) {
+  // Reset UI.
+  if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
+    gStore.dispatch(Actions.batchReset());
+    gStore.dispatch(Actions.clearRequests());
+  } else {
+    // If the log is persistent, just clear all accumulated timing markers.
+    gStore.dispatch(Actions.clearTimingMarkers());
+  }
+
+  window.emit(EVENTS.TARGET_WILL_NAVIGATE);
+}
+
+/**
+ * Process connection events.
+ *
+ * @param {Object} tabTarget
+ */
+function onFirefoxConnect(tabTarget) {
+  tabTarget.on("navigate", navigated);
+  tabTarget.on("will-navigate", willNavigate);
+}
+
+/**
+ * Process disconnect events.
+ *
+ * @param {Object} tabTarget
+ */
+function onFirefoxDisconnect(tabTarget) {
+  tabTarget.off("navigate", navigated);
+  tabTarget.off("will-navigate", willNavigate);
+}
+
+module.exports = {
+  onFirefoxConnect,
+  onFirefoxDisconnect,
+};
--- a/devtools/client/netmonitor/utils/moz.build
+++ b/devtools/client/netmonitor/utils/moz.build
@@ -1,14 +1,15 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
+    'client.js',
     'filter-predicates.js',
     'format-utils.js',
     'l10n.js',
     'mdn-utils.js',
     'prefs.js',
     'request-utils.js',
     'sort-predicates.js',
 )
--- a/devtools/client/responsive.html/components/device-modal.js
+++ b/devtools/client/responsive.html/components/device-modal.js
@@ -33,32 +33,46 @@ module.exports = createClass({
   },
 
   componentDidMount() {
     window.addEventListener("keydown", this.onKeyDown, true);
   },
 
   componentWillReceiveProps(nextProps) {
     let {
+      devices: oldDevices,
+    } = this.props;
+    let {
       devices,
     } = nextProps;
 
-    for (let type of devices.types) {
-      for (let device of devices[type]) {
-        this.setState({
-          [device.name]: device.displayed,
-        });
+    // Refresh component state only when model transitions from closed to open
+    if (!oldDevices.isModalOpen && devices.isModalOpen) {
+      for (let type of devices.types) {
+        for (let device of devices[type]) {
+          this.setState({
+            [device.name]: device.displayed,
+          });
+        }
       }
     }
   },
 
   componentWillUnmount() {
     window.removeEventListener("keydown", this.onKeyDown, true);
   },
 
+  onAddCustomDevice(device) {
+    this.props.onAddCustomDevice(device);
+    // Default custom devices to enabled
+    this.setState({
+      [device.name]: true,
+    });
+  },
+
   onDeviceCheckboxChange({ nativeEvent: { button }, target }) {
     if (button !== 0) {
       return;
     }
     this.setState({
       [target.value]: !this.state[target.value]
     });
   },
@@ -108,21 +122,24 @@ module.exports = createClass({
       onUpdateDeviceModal(false);
     }
   },
 
   render() {
     let {
       deviceAdderViewportTemplate,
       devices,
-      onAddCustomDevice,
       onRemoveCustomDevice,
       onUpdateDeviceModal,
     } = this.props;
 
+    let {
+      onAddCustomDevice,
+    } = this;
+
     const sortedDevices = {};
     for (let type of devices.types) {
       sortedDevices[type] = Object.assign([], devices[type])
         .sort((a, b) => a.name.localeCompare(b.name));
     }
 
     return dom.div(
       {
--- a/devtools/client/responsive.html/test/browser/browser_device_custom.js
+++ b/devtools/client/responsive.html/test/browser/browser_device_custom.js
@@ -48,22 +48,22 @@ addRDMTask(TEST_URL, function* ({ ui }) 
 
   info("Fill out device adder form and save");
   setDeviceAdder(ui, device);
   let adderSave = document.querySelector("#device-adder-save");
   let saved = waitUntilState(store, state => state.devices.custom.length == 1);
   Simulate.click(adderSave);
   yield saved;
 
-  info("Enable device in modal");
+  info("Verify device defaults to enabled in modal");
   let deviceCb = [...document.querySelectorAll(".device-input-checkbox")].find(cb => {
     return cb.value == device.name;
   });
   ok(deviceCb, "Custom device checkbox added to modal");
-  deviceCb.click();
+  ok(deviceCb.checked, "Custom device enabled");
   Simulate.click(submitButton);
 
   info("Look for custom device in device selector");
   let selectorOption = [...deviceSelector.options].find(opt => opt.value == device.name);
   ok(selectorOption, "Custom device option added to device selector");
 });
 
 addRDMTask(TEST_URL, function* ({ ui }) {
--- a/devtools/client/shared/browser-loader.js
+++ b/devtools/client/shared/browser-loader.js
@@ -9,16 +9,17 @@ const { devtools } = Cu.import("resource
 const { joinURI } = devtools.require("devtools/shared/path");
 const { assert } = devtools.require("devtools/shared/DevToolsUtils");
 const Services = devtools.require("Services");
 const { AppConstants } = devtools.require("resource://gre/modules/AppConstants.jsm");
 
 const BROWSER_BASED_DIRS = [
   "resource://devtools/client/inspector/boxmodel",
   "resource://devtools/client/inspector/computed",
+  "resource://devtools/client/inspector/grids",
   "resource://devtools/client/inspector/layout",
   "resource://devtools/client/jsonview",
   "resource://devtools/client/shared/vendor",
   "resource://devtools/client/shared/redux",
 ];
 
 const COMMON_LIBRARY_DIRS = [
   "resource://devtools/client/shared/vendor",
--- a/devtools/client/shared/components/tabs/tabs.css
+++ b/devtools/client/shared/components/tabs/tabs.css
@@ -46,29 +46,16 @@
 .tabs .panels {
   height: calc(100% - 24px);
 }
 
 .tabs .tab-panel {
   height: 100%;
 }
 
-.tabs .all-tabs-menu  {
-  position: absolute;
-  top: 0;
-  offset-inline-end: 0;
-  width: 15px;
-  height: 100%;
-  border-inline-start: 1px solid var(--theme-splitter-color);
-  background: var(--theme-tab-toolbar-background);
-  background-image: url("chrome://devtools/skin/images/dropmarker.svg");
-  background-repeat: no-repeat;
-  background-position: center;
-}
-
 .tabs .tabs-navigation,
 .tabs .tabs-navigation {
   position: relative;
   border-bottom: 1px solid var(--theme-splitter-color);
   background: var(--theme-tab-toolbar-background);
 }
 
 .theme-dark .tabs .tabs-menu-item,
--- a/devtools/client/themes/common.css
+++ b/devtools/client/themes/common.css
@@ -667,8 +667,26 @@ checkbox:-moz-focusring {
 @keyframes throbber-spin {
   from {
     transform: none;
   }
   to {
     transform: rotate(360deg);
   }
 }
+
+/* Common tabs styles */
+
+.all-tabs-menu {
+  position: absolute;
+
+  top: 0;
+  offset-inline-end: 0;
+  width: 15px;
+  height: 100%;
+
+  border-inline-start: 1px solid var(--theme-splitter-color);
+
+  background: var(--theme-tab-toolbar-background);
+  background-image: url("chrome://devtools/skin/images/dropmarker.svg");
+  background-repeat: no-repeat;
+  background-position: center;
+}
--- a/devtools/client/themes/images/grid.svg
+++ b/devtools/client/themes/images/grid.svg
@@ -1,6 +1,6 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.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 width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" stroke="#696969">
-  <path fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" d="M2 4h12M2 8h12M2 12h12M4 14V2M8 14V2M12 14V2"/>
+<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" stroke="#696969">
+  <path fill="none" d="M1 2.5h9m-9 3h9m-9 3h9M2.5 1v9m3-9v9m3-9v9"/>
 </svg>
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -62,21 +62,24 @@
 }
 
 .grid-outline-border {
   stroke-width: 0.75;
   fill: none;
 }
 
 .grid-outline-cell {
-  fill: none;
   pointer-events: all;
   stroke-dasharray: 0.5, 2;
 }
 
+.grid-outline-cell:hover {
+  opacity: 0.45;
+}
+
 /**
  * Container when no grids are present
  */
 
 .layout-no-grids {
   font-style: italic;
   text-align: center;
   padding: 0.5em;
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -411,17 +411,16 @@
   margin-top: -1px;
   margin-inline-end: 5px;
   display: inline-block;
   position: relative;
 }
 
 .ruleview-grid {
   background: url("chrome://devtools/skin/images/grid.svg");
-  background-size: 1em;
   border-radius: 0;
 }
 
 .ruleview-colorswatch::before {
   content: '';
   background-color: #eee;
   background-image: linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc),
                     linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc);
--- a/devtools/client/themes/toolbox.css
+++ b/devtools/client/themes/toolbox.css
@@ -50,24 +50,31 @@
   background: var(--theme-tab-toolbar-background);
   border-bottom-color: var(--theme-splitter-color);
   display: flex;
 }
 
 .toolbox-tabs {
   margin: 0;
   flex: 1;
+  overflow: hidden;
 }
 
 .toolbox-tabs-wrapper {
   position: relative;
   display: flex;
   flex: 1;
 }
 
+.toolbox-tabs-wrapper .all-tools-menu {
+  border-inline-end: 1px solid var(--theme-splitter-color);
+  border-top-width: 0;
+  border-bottom-width: 0;
+}
+
 .toolbox-tabs {
   position: absolute;
   top: 0;
   left: 0;
   right: 0;
   bottom: 0;
   display: flex;
 }
--- a/devtools/client/themes/variables.css
+++ b/devtools/client/themes/variables.css
@@ -16,23 +16,25 @@
 
 :root.theme-light {
   --theme-body-background: white;
   --theme-sidebar-background: white;
   --theme-contrast-background: #e6b064;
 
   --theme-tab-toolbar-background: #fcfcfc;
   --theme-toolbar-background: #fcfcfc;
+  --theme-toolbar-background-alt: #f5f5f5;
   --theme-toolbar-hover: rgba(170, 170, 170, .2);
   --theme-toolbar-hover-active: rgba(170, 170, 170, .4);
   --theme-selection-background: #4c9ed9;
   --theme-selection-background-semitransparent: rgba(76, 158, 217, 0.15);
   --theme-selection-color: #f5f7fa;
   --theme-splitter-color: #dde1e4;
   --theme-comment: #696969;
+  --theme-comment-alt: #ccd1d5;
 
   --theme-body-color: #393f4c;
   --theme-body-color-alt: #585959;
   --theme-body-color-inactive: #999797;
   --theme-content-color1: #292e33;
   --theme-content-color2: #8fa1b2;
   --theme-content-color3: #667380;
 
@@ -78,23 +80,25 @@
 
 :root.theme-dark {
   --theme-body-background: #393f4c;
   --theme-sidebar-background: #393f4c;
   --theme-contrast-background: #ffb35b;
 
   --theme-tab-toolbar-background: #272b35;
   --theme-toolbar-background: #272b35;
+  --theme-toolbar-background-alt: #2F343E;
   --theme-toolbar-hover: rgba(110, 120, 130, 0.1);
   --theme-toolbar-hover-active: rgba(110, 120, 130, 0.2);
   --theme-selection-background: #5675B9;
   --theme-selection-background-semitransparent: rgba(86, 117, 185, 0.5);
   --theme-selection-color: #f5f7fa;
   --theme-splitter-color: #454d5d;
   --theme-comment: #757873;
+  --theme-comment-alt: #5a6375;
 
   --theme-body-color: #8fa1b2;
   --theme-body-color-alt: #b6babf;
   --theme-body-color-inactive: #8fa1b2;
   --theme-content-color1: #a9bacb;
   --theme-content-color2: #8fa1b2;
   --theme-content-color3: #5f7387;
 
--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -225,23 +225,25 @@
   left: 0;
   image-rendering: -moz-crisp-edges;
 }
 
 :-moz-native-anonymous .css-grid-regions {
   opacity: 0.6;
 }
 
-:-moz-native-anonymous .css-grid-areas {
+:-moz-native-anonymous .css-grid-areas,
+:-moz-native-anonymous .css-grid-cells {
   fill: #CEC0ED;
   stroke: none;
 }
 
-:-moz-native-anonymous .css-grid-infobar-areaname {
-  color: hsl(285,100%, 75%);
+:-moz-native-anonymous .css-grid-area-infobar-name,
+:-moz-native-anonymous .css-grid-cell-infobar-position {
+  color: hsl(285, 100%, 75%);
 }
 
 /* CSS Transform Highlighter */
 
 :-moz-native-anonymous .css-transform-transformed {
   fill: var(--highlighter-content-color);
   opacity: 0.8;
 }
--- a/devtools/server/actors/highlighters/css-grid.js
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -16,19 +16,21 @@ const {
 const {
   getCurrentZoom,
   setIgnoreLayoutChanges,
   getWindowDimensions
 } = require("devtools/shared/layout/utils");
 const { stringifyGridFragments } = require("devtools/server/actors/utils/css-grid-utils");
 
 const CSS_GRID_ENABLED_PREF = "layout.css.grid.enabled";
+
 const DEFAULT_GRID_COLOR = "#4B0082";
+
+const COLUMNS = "cols";
 const ROWS = "rows";
-const COLUMNS = "cols";
 
 const GRID_LINES_PROPERTIES = {
   "edge": {
     lineDash: [0, 0],
     alpha: 1,
   },
   "explicit": {
     lineDash: [5, 3],
@@ -64,41 +66,56 @@ const COLUMN_KEY = {};
  * h.show(node, options);
  * h.hide();
  * h.destroy();
  *
  * Available Options:
  * - color(colorValue)
  *     @param  {String} colorValue
  *     The color that should be used to draw the highlighter for this grid.
+ * - showAllGridAreas(isShown)
+ *     @param  {Boolean} isShown
+ *     Shows all the grid area highlights for the current grid if isShown is true.
  * - showGridArea(areaName)
  *     @param  {String} areaName
  *     Shows the grid area highlight for the given area name.
- * - showAllGridAreas
- *     Shows all the grid area highlights for the current grid.
+ * - showGridCell({ gridFragmentIndex: Number, rowNumber: Number, columnNumber: Number })
+ *     @param  {Object} { gridFragmentIndex: Number, rowNumber: Number,
+ *                        columnNumber: Number }
+ *     An object containing the grid fragment index, row and column numbers to the
+ *     corresponding grid cell to highlight for the current grid.
  * - showGridLineNumbers(isShown)
- *     @param  {Boolean}
+ *     @param  {Boolean} isShown
  *     Displays the grid line numbers on the grid lines if isShown is true.
  * - showInfiniteLines(isShown)
  *     @param  {Boolean} isShown
  *     Displays an infinite line to represent the grid lines if isShown is true.
  *
  * Structure:
  * <div class="highlighter-container">
  *   <canvas id="css-grid-canvas" class="css-grid-canvas">
  *   <svg class="css-grid-elements" hidden="true">
  *     <g class="css-grid-regions">
  *       <path class="css-grid-areas" points="..." />
+ *       <path class="css-grid-cells" points="..." />
  *     </g>
  *   </svg>
- *   <div class="css-grid-infobar-container">
+ *   <div class="css-grid-area-infobar-container">
  *     <div class="css-grid-infobar">
  *       <div class="css-grid-infobar-text">
- *         <span class="css-grid-infobar-areaname">Grid Area Name</span>
- *         <span class="css-grid-infobar-dimensions"Grid Area Dimensions></span>
+ *         <span class="css-grid-area-infobar-name">Grid Area Name</span>
+ *         <span class="css-grid-area-infobar-dimensions"Grid Area Dimensions></span>
+ *       </div>
+ *     </div>
+ *   </div>
+ *   <div class="css-grid-cell-infobar-container">
+ *     <div class="css-grid-infobar">
+ *       <div class="css-grid-infobar-text">
+ *         <span class="css-grid-cell-infobar-position">Grid Cell Position</span>
+ *         <span class="css-grid-cell-infobar-dimensions"Grid Cell Dimensions></span>
  *       </div>
  *     </div>
  *   </div>
  * </div>
  */
 function CssGridHighlighter(highlighterEnv) {
   AutoRefreshHighlighter.call(this, highlighterEnv);
 
@@ -178,58 +195,114 @@ CssGridHighlighter.prototype = extend(Au
       parent: regions,
       attributes: {
         "class": "areas",
         "id": "areas"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
-    // Building the grid infobar markup
-    let infobarContainer = createNode(this.win, {
+    createSVGNode(this.win, {
+      nodeType: "path",
+      parent: regions,
+      attributes: {
+        "class": "cells",
+        "id": "cells"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    // Building the grid area infobar markup
+    let areaInfobarContainer = createNode(this.win, {
       parent: container,
       attributes: {
-        "class": "infobar-container",
-        "id": "infobar-container",
+        "class": "area-infobar-container",
+        "id": "area-infobar-container",
         "position": "top",
         "hidden": "true"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
-    let infobar = createNode(this.win, {
-      parent: infobarContainer,
+    let areaInfobar = createNode(this.win, {
+      parent: areaInfobarContainer,
       attributes: {
         "class": "infobar"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
-    let textbox = createNode(this.win, {
-      parent: infobar,
+    let areaTextbox = createNode(this.win, {
+      parent: areaInfobar,
       attributes: {
         "class": "infobar-text"
       },
       prefix: this.ID_CLASS_PREFIX
     });
     createNode(this.win, {
       nodeType: "span",
-      parent: textbox,
+      parent: areaTextbox,
       attributes: {
-        "class": "infobar-areaname",
-        "id": "infobar-areaname"
+        "class": "area-infobar-name",
+        "id": "area-infobar-name"
       },
       prefix: this.ID_CLASS_PREFIX
     });
     createNode(this.win, {
       nodeType: "span",
-      parent: textbox,
+      parent: areaTextbox,
+      attributes: {
+        "class": "area-infobar-dimensions",
+        "id": "area-infobar-dimensions"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    // Building the grid cell infobar markup
+    let cellInfobarContainer = createNode(this.win, {
+      parent: container,
+      attributes: {
+        "class": "cell-infobar-container",
+        "id": "cell-infobar-container",
+        "position": "top",
+        "hidden": "true"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    let cellInfobar = createNode(this.win, {
+      parent: cellInfobarContainer,
       attributes: {
-        "class": "infobar-dimensions",
-        "id": "infobar-dimensions"
+        "class": "infobar"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    let cellTextbox = createNode(this.win, {
+      parent: cellInfobar,
+      attributes: {
+        "class": "infobar-text"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+    createNode(this.win, {
+      nodeType: "span",
+      parent: cellTextbox,
+      attributes: {
+        "class": "cell-infobar-position",
+        "id": "cell-infobar-position"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+    createNode(this.win, {
+      nodeType: "span",
+      parent: cellTextbox,
+      attributes: {
+        "class": "cell-infobar-dimensions",
+        "id": "cell-infobar-dimensions"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
     return container;
   },
 
   destroy() {
@@ -323,49 +396,72 @@ CssGridHighlighter.prototype = extend(Au
     if (Services.prefs.getBoolPref(CSS_GRID_ENABLED_PREF) && !this.isGrid()) {
       this.hide();
       return false;
     }
 
     // The grid pattern cache should be cleared in case the color changed.
     this._clearCache();
 
+    // Hide the canvas, grid element highlights and infobar.
+    this._hide();
+
     return this._update();
   },
 
   _clearCache() {
     gCachedGridPattern.delete(ROW_KEY);
     gCachedGridPattern.delete(COLUMN_KEY);
   },
 
   /**
    * Shows the grid area highlight for the given area name.
    *
    * @param  {String} areaName
    *         Grid area name.
    */
   showGridArea(areaName) {
     this.renderGridArea(areaName);
-    this._showGridArea();
   },
 
   /**
    * Shows all the grid area highlights for the current grid.
    */
   showAllGridAreas() {
     this.renderGridArea();
-    this._showGridArea();
   },
 
   /**
    * Clear the grid area highlights.
    */
   clearGridAreas() {
-    let box = this.getElement("areas");
-    box.setAttribute("d", "");
+    let areas = this.getElement("areas");
+    areas.setAttribute("d", "");
+  },
+
+  /**
+   * Shows the grid cell highlight for the given grid cell options.
+   *
+   * @param  {Number} options.gridFragmentIndex
+   *         Index of the grid fragment to render the grid cell highlight.
+   * @param  {Number} options.rowNumber
+   *         Row number of the grid cell to highlight.
+   * @param  {Number} options.columnNumber
+   *         Column number of the grid cell to highlight.
+   */
+  showGridCell({ gridFragmentIndex, rowNumber, columnNumber }) {
+    this.renderGridCell(gridFragmentIndex, rowNumber, columnNumber);
+  },
+
+  /**
+   * Clear the grid cell highlights.
+   */
+  clearGridCell() {
+    let cells = this.getElement("cells");
+    cells.setAttribute("d", "");
   },
 
   /**
    * Checks if the current node has a CSS Grid layout.
    *
    * @return  {Boolean} true if the current node has a CSS grid layout, false otherwise.
    */
   isGrid() {
@@ -417,76 +513,97 @@ CssGridHighlighter.prototype = extend(Au
 
     // Display the grid area highlights if needed.
     if (this.options.showAllGridAreas) {
       this.showAllGridAreas();
     } else if (this.options.showGridArea) {
       this.showGridArea(this.options.showGridArea);
     }
 
+    // Display the grid cell highlights if needed.
+    if (this.options.showGridCell) {
+      this.showGridCell(this.options.showGridCell);
+    }
+
     this._showGrid();
+    this._showGridElements();
 
     root.setAttribute("style",
       `position:absolute; width:${width}px;height:${height}px; overflow:hidden`);
 
     setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
     return true;
   },
 
   /**
-   * Update the grid information displayed in the grid info bar.
+   * Update the grid information displayed in the grid area info bar.
    *
    * @param  {GridArea} area
    *         The grid area object.
    * @param  {Number} x1
    *         The first x-coordinate of the grid area rectangle.
    * @param  {Number} x2
    *         The second x-coordinate of the grid area rectangle.
    * @param  {Number} y1
    *         The first y-coordinate of the grid area rectangle.
    * @param  {Number} y2
    *         The second y-coordinate of the grid area rectangle.
    */
-  _updateInfobar(area, x1, x2, y1, y2) {
+  _updateGridAreaInfobar(area, x1, x2, y1, y2) {
     let width = x2 - x1;
     let height = y2 - y1;
     let dim = parseFloat(width.toPrecision(6)) +
               " \u00D7 " +
               parseFloat(height.toPrecision(6));
 
-    this.getElement("infobar-areaname").setTextContent(area.name);
-    this.getElement("infobar-dimensions").setTextContent(dim);
+    this.getElement("area-infobar-name").setTextContent(area.name);
+    this.getElement("area-infobar-dimensions").setTextContent(dim);
+
+    let container = this.getElement("area-infobar-container");
+    this._moveInfobar(container, x1, x2, y1, y2);
+  },
 
-    this._moveInfobar(x1, x2, y1, y2);
+  _updateGridCellInfobar(rowNumber, columnNumber, x1, x2, y1, y2) {
+    let width = x2 - x1;
+    let height = y2 - y1;
+    let dim = parseFloat(width.toPrecision(6)) +
+              " \u00D7 " +
+              parseFloat(height.toPrecision(6));
+    let position = `${rowNumber}\/${columnNumber}`;
+
+    this.getElement("cell-infobar-position").setTextContent(position);
+    this.getElement("cell-infobar-dimensions").setTextContent(dim);
+
+    let container = this.getElement("cell-infobar-container");
+    this._moveInfobar(container, x1, x2, y1, y2);
   },
 
   /**
-   * Move the grid infobar to the right place in the highlighter.
+   * Move the given grid infobar to the right place in the highlighter.
    *
    * @param  {Number} x1
-   *         The first x-coordinate of the grid area rectangle.
+   *         The first x-coordinate of the grid rectangle.
    * @param  {Number} x2
-   *         The second x-coordinate of the grid area rectangle.
+   *         The second x-coordinate of the grid rectangle.
    * @param  {Number} y1
-   *         The first y-coordinate of the grid area rectangle.
+   *         The first y-coordinate of the grid rectangle.
    * @param  {Number} y2
-   *         The second y-coordinate of the grid area rectangle.
+   *         The second y-coordinate of the grid rectangle.
    */
-  _moveInfobar(x1, x2, y1, y2) {
+  _moveInfobar(container, x1, x2, y1, y2) {
     let bounds = {
       bottom: y2,
       height: y2 - y1,
       left: x1,
       right: x2,
       top: y1,
       width: x2 - x1,
       x: x1,
       y: y1,
     };
-    let container = this.getElement("infobar-container");
 
     moveInfobar(container, bounds, this.win);
   },
 
   clearCanvas(width, height) {
     let ratio = parseFloat((this.win.devicePixelRatio || 1).toFixed(2));
 
     // Resize the canvas taking the dpr into account so as to have crisp lines.
@@ -610,17 +727,17 @@ CssGridHighlighter.prototype = extend(Au
    *         The line position along the x-axis for a column grid line and
    *         y-axis for a row grid line.
    * @param  {Number} startPos
    *         The start position of the cross side of the grid line.
    * @param  {Number} endPos
    *         The end position of the cross side of the grid line.
    * @param  {String} dimensionType
    *         The grid dimension type which is either the constant COLUMNS or ROWS.
-   * @param  {[type]} lineType
+   * @param  {String} lineType
    *         The grid line type - "edge", "explicit", or "implicit".
    */
   renderLine(linePos, startPos, endPos, dimensionType, lineType) {
     this.ctx.save();
     this.ctx.setLineDash(GRID_LINES_PROPERTIES[lineType].lineDash);
     this.ctx.beginPath();
     this.ctx.translate(.5, .5);
 
@@ -734,56 +851,108 @@ CssGridHighlighter.prototype = extend(Au
         let path = "M" + x1 + "," + y1 + " " +
                    "L" + x2 + "," + y1 + " " +
                    "L" + x2 + "," + y2 + " " +
                    "L" + x1 + "," + y2;
         paths.push(path);
 
         // Update and show the info bar when only displaying a single grid area.
         if (areaName) {
-          this._updateInfobar(area, x1, x2, y1, y2);
-          this._showInfoBar();
+          this._updateGridAreaInfobar(area, x1, x2, y1, y2);
+          this._showGridAreaInfoBar();
         }
       }
     }
 
-    let box = this.getElement("areas");
-    box.setAttribute("d", paths.join(" "));
+    let areas = this.getElement("areas");
+    areas.setAttribute("d", paths.join(" "));
   },
 
   /**
-   * Hide the highlighter, the canvas and the infobar.
+   * Render the grid cell highlight for the given grid fragment index, row and column
+   * number.
+   *
+   * @param  {Number} gridFragmentIndex
+   *         Index of the grid fragment to render the grid cell highlight.
+   * @param  {Number} rowNumber
+   *         Row number of the grid cell to highlight.
+   * @param  {Number} columnNumber
+   *         Column number of the grid cell to highlight.
+   */
+  renderGridCell(gridFragmentIndex, rowNumber, columnNumber) {
+    let fragment = this.gridData[gridFragmentIndex];
+    if (!fragment) {
+      return;
+    }
+
+    let row = fragment.rows.tracks[rowNumber - 1];
+    let column = fragment.cols.tracks[columnNumber - 1];
+
+    if (!row || !column) {
+      return;
+    }
+
+    let currentZoom = getCurrentZoom(this.win);
+    let {bounds} = this.currentQuads.content[gridFragmentIndex];
+
+    let x1 = column.start + (bounds.left / currentZoom);
+    let x2 = column.start + column.breadth + (bounds.left / currentZoom);
+    let y1 = row.start + (bounds.top / currentZoom);
+    let y2 = row.start + row.breadth + (bounds.top / currentZoom);
+
+    let path = "M" + x1 + "," + y1 + " " +
+               "L" + x2 + "," + y1 + " " +
+               "L" + x2 + "," + y2 + " " +
+               "L" + x1 + "," + y2;
+    let cells = this.getElement("cells");
+    cells.setAttribute("d", path);
+
+    this._updateGridCellInfobar(rowNumber, columnNumber, x1, x2, y1, y2);
+    this._showGridCellInfoBar();
+  },
+
+  /**
+   * Hide the highlighter, the canvas and the infobars.
    */
   _hide() {
     setIgnoreLayoutChanges(true);
     this._hideGrid();
-    this._hideGridArea();
-    this._hideInfoBar();
+    this._hideGridElements();
+    this._hideGridAreaInfoBar();
+    this._hideGridCellInfoBar();
     setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
   },
 
   _hideGrid() {
     this.getElement("canvas").setAttribute("hidden", "true");
   },
 
   _showGrid() {
     this.getElement("canvas").removeAttribute("hidden");
   },
 
-  _hideGridArea() {
+  _hideGridElements() {
     this.getElement("elements").setAttribute("hidden", "true");
   },
 
-  _showGridArea() {
+  _showGridElements() {
     this.getElement("elements").removeAttribute("hidden");
   },
 
-  _hideInfoBar() {
-    this.getElement("infobar-container").setAttribute("hidden", "true");
+  _hideGridAreaInfoBar() {
+    this.getElement("area-infobar-container").setAttribute("hidden", "true");
   },
 
-  _showInfoBar() {
-    this.getElement("infobar-container").removeAttribute("hidden");
+  _showGridAreaInfoBar() {
+    this.getElement("area-infobar-container").removeAttribute("hidden");
+  },
+
+  _hideGridCellInfoBar() {
+    this.getElement("cell-infobar-container").setAttribute("hidden", "true");
+  },
+
+  _showGridCellInfoBar() {
+    this.getElement("cell-infobar-container").removeAttribute("hidden");
   },
 
 });
 
 exports.CssGridHighlighter = CssGridHighlighter;
--- a/devtools/server/actors/root.js
+++ b/devtools/server/actors/root.js
@@ -385,30 +385,31 @@ RootActor.prototype = {
 
   onListAddons: function () {
     let addonList = this._parameters.addonList;
     if (!addonList) {
       return { from: this.actorID, error: "noAddons",
                message: "This root actor has no browser addons." };
     }
 
+    // Reattach the onListChanged listener now that a client requested the list.
+    addonList.onListChanged = this._onAddonListChanged;
+
     return addonList.getList().then((addonActors) => {
       let addonActorPool = new ActorPool(this.conn);
       for (let addonActor of addonActors) {
         addonActorPool.addActor(addonActor);
       }
 
       if (this._addonActorPool) {
         this.conn.removeActorPool(this._addonActorPool);
       }
       this._addonActorPool = addonActorPool;
       this.conn.addActorPool(this._addonActorPool);
 
-      addonList.onListChanged = this._onAddonListChanged;
-
       return {
         "from": this.actorID,
         "addons": addonActors.map(addonActor => addonActor.form())
       };
     });
   },
 
   onAddonListChanged: function () {
@@ -418,28 +419,29 @@ RootActor.prototype = {
 
   onListWorkers: function () {
     let workerList = this._parameters.workerList;
     if (!workerList) {
       return { from: this.actorID, error: "noWorkers",
                message: "This root actor has no workers." };
     }
 
+    // Reattach the onListChanged listener now that a client requested the list.
+    workerList.onListChanged = this._onWorkerListChanged;
+
     return workerList.getList().then(actors => {
       let pool = new ActorPool(this.conn);
       for (let actor of actors) {
         pool.addActor(actor);
       }
 
       this.conn.removeActorPool(this._workerActorPool);
       this._workerActorPool = pool;
       this.conn.addActorPool(this._workerActorPool);
 
-      workerList.onListChanged = this._onWorkerListChanged;
-
       return {
         "from": this.actorID,
         "workers": actors.map(actor => actor.form())
       };
     });
   },
 
   onWorkerListChanged: function () {
@@ -449,28 +451,29 @@ RootActor.prototype = {
 
   onListServiceWorkerRegistrations: function () {
     let registrationList = this._parameters.serviceWorkerRegistrationList;
     if (!registrationList) {
       return { from: this.actorID, error: "noServiceWorkerRegistrations",
                message: "This root actor has no service worker registrations." };
     }
 
+    // Reattach the onListChanged listener now that a client requested the list.
+    registrationList.onListChanged = this._onServiceWorkerRegistrationListChanged;
+
     return registrationList.getList().then(actors => {
       let pool = new ActorPool(this.conn);
       for (let actor of actors) {
         pool.addActor(actor);
       }
 
       this.conn.removeActorPool(this._serviceWorkerRegistrationActorPool);
       this._serviceWorkerRegistrationActorPool = pool;
       this.conn.addActorPool(this._serviceWorkerRegistrationActorPool);
 
-      registrationList.onListChanged = this._onServiceWorkerRegistrationListChanged;
-
       return {
         "from": this.actorID,
         "registrations": actors.map(actor => actor.form())
       };
     });
   },
 
   onServiceWorkerRegistrationListChanged: function () {
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -3040,16 +3040,17 @@ exports.CSS_PROPERTIES = {
       "text-decoration-color",
       "text-decoration-line",
       "text-decoration-style",
       "text-emphasis-color",
       "text-emphasis-position",
       "text-emphasis-style",
       "-webkit-text-fill-color",
       "text-indent",
+      "text-justify",
       "text-orientation",
       "text-overflow",
       "text-rendering",
       "text-shadow",
       "-moz-text-size-adjust",
       "-webkit-text-stroke-color",
       "-webkit-text-stroke-width",
       "text-transform",
@@ -9482,16 +9483,20 @@ exports.PREFERENCES = [
     "text-combine-upright",
     "layout.css.text-combine-upright.enabled"
   ],
   [
     "-webkit-text-fill-color",
     "layout.css.prefixes.webkit"
   ],
   [
+    "text-justify",
+    "layout.css.text-justify.enabled"
+  ],
+  [
     "-webkit-text-stroke",
     "layout.css.prefixes.webkit"
   ],
   [
     "-webkit-text-stroke-color",
     "layout.css.prefixes.webkit"
   ],
   [
--- a/dom/audiochannel/moz.build
+++ b/dom/audiochannel/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "DOM")
+
 XPIDL_SOURCES += [
     'nsIAudioChannelAgent.idl',
     'nsIAudioChannelService.idl',
 ]
 
 XPIDL_MODULE = 'dom_audiochannel'
 
 EXPORTS += [
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -853,17 +853,17 @@ Element::SetScrollLeft(int32_t aScrollLe
 bool
 Element::ScrollByNoFlush(int32_t aDx, int32_t aDy)
 {
   nsIScrollableFrame* sf = GetScrollFrame(nullptr, false);
   if (!sf) {
     return false;
   }
 
-  nsWeakFrame weakRef(sf->GetScrolledFrame());
+  AutoWeakFrame weakRef(sf->GetScrolledFrame());
 
   CSSIntPoint before = sf->GetScrollPositionCSSPixels();
   sf->ScrollToCSSPixelsApproximate(CSSIntPoint(before.x + aDx, before.y + aDy));
 
   // The frame was destroyed, can't keep on scrolling.
   if (!weakRef.IsAlive()) {
     return false;
   }
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "DOM")
+
 TEST_DIRS += ['test']
 
 XPIDL_SOURCES += [
     'mozIDOMWindow.idl',
     'nsIContentPolicy.idl',
     'nsIContentPolicyBase.idl',
     'nsIDocumentEncoder.idl',
     'nsIDOMDataChannel.idl',
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2694,17 +2694,17 @@ nsFrameLoader::UpdateBaseWindowPositionA
   GetDocShell(getter_AddRefs(docShell));
   nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(docShell));
 
   // resize the sub document
   if (baseWindow) {
     int32_t x = 0;
     int32_t y = 0;
 
-    nsWeakFrame weakFrame(aIFrame);
+    AutoWeakFrame weakFrame(aIFrame);
 
     baseWindow->GetPosition(&x, &y);
 
     if (!weakFrame.IsAlive()) {
       // GetPosition() killed us
       return;
     }
 
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -324,17 +324,17 @@ private:
 
   // After the frameloader has been removed from the DOM but before all of the
   // messages from the frame have been received, we keep a strong reference to
   // our <browser> element.
   RefPtr<mozilla::dom::Element> mOwnerContentStrong;
 
   // Stores the root frame of the subdocument while the subdocument is being
   // reframed. Used to restore the presentation after reframing.
-  nsWeakFrame mDetachedSubdocFrame;
+  WeakFrame mDetachedSubdocFrame;
   // Stores the containing document of the frame corresponding to this
   // frame loader. This is reference is kept valid while the subframe's
   // presentation is detached and stored in mDetachedSubdocFrame. This
   // enables us to detect whether the frame has moved documents during
   // a reframe, so that we know not to restore the presentation.
   nsCOMPtr<nsIDocument> mContainerDocWhileDetached;
 
   // An opener window which should be used when the docshell is created.
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -710,16 +710,16 @@ class nsObjectLoadingContent : public ns
     // videos.
     bool                        mRewrittenYoutubeEmbed : 1;
 
     // Cache the answer of PreferFallback() because ShouldPlay is called several
     // times during the load process.
     bool                        mPreferFallback : 1;
     bool                        mPreferFallbackKnown : 1;
 
-    nsWeakFrame                 mPrintFrame;
+    WeakFrame                   mPrintFrame;
 
     RefPtr<nsPluginInstanceOwner> mInstanceOwner;
     nsTArray<mozilla::dom::MozPluginParameter> mCachedAttributes;
     nsTArray<mozilla::dom::MozPluginParameter> mCachedParameters;
 };
 
 #endif
--- a/dom/encoding/moz.build
+++ b/dom/encoding/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Internationalization")
+
 EXPORTS.mozilla.dom += [
     'EncodingUtils.h',
     'FallbackEncoding.h',
     'TextDecoder.h',
     'TextEncoder.h',
 ]
 
 UNIFIED_SOURCES += [
--- a/dom/events/DeviceMotionEvent.cpp
+++ b/dom/events/DeviceMotionEvent.cpp
@@ -65,16 +65,21 @@ DeviceMotionEvent::InitDeviceMotionEvent
                            aAccelIncludingGravity.mZ);
 
   mRotationRate = new DeviceRotationRate(this, aRotationRate.mAlpha,
                                          aRotationRate.mBeta,
                                          aRotationRate.mGamma);
   mInterval = aInterval;
   if (!aTimeStamp.IsNull()) {
     mEvent->mTime = aTimeStamp.Value();
+
+    static mozilla::TimeStamp sInitialNow = mozilla::TimeStamp::Now();
+    static uint64_t sInitialEventTime = aTimeStamp.Value();
+    mEvent->mTimeStamp = sInitialNow + mozilla::TimeDuration::FromMicroseconds(
+      aTimeStamp.Value() - sInitialEventTime);
   }
 }
 
 already_AddRefed<DeviceMotionEvent>
 DeviceMotionEvent::Constructor(const GlobalObject& aGlobal,
                                const nsAString& aType,
                                const DeviceMotionEventInit& aEventInitDict,
                                ErrorResult& aRv)
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -110,17 +110,17 @@ using namespace dom;
 
 static const LayoutDeviceIntPoint kInvalidRefPoint = LayoutDeviceIntPoint(-1,-1);
 
 static uint32_t gMouseOrKeyboardEventCounter = 0;
 static nsITimer* gUserInteractionTimer = nullptr;
 static nsITimerCallback* gUserInteractionTimerCallback = nullptr;
 
 static const double kCursorLoadingTimeout = 1000; // ms
-static nsWeakFrame gLastCursorSourceFrame;
+static AutoWeakFrame gLastCursorSourceFrame;
 static TimeStamp gLastCursorUpdateTime;
 
 static inline int32_t
 RoundDown(double aDouble)
 {
   return (aDouble > 0) ? static_cast<int32_t>(floor(aDouble)) :
                          static_cast<int32_t>(ceil(aDouble));
 }
@@ -278,17 +278,17 @@ NS_INTERFACE_MAP_END
 static uint32_t sESMInstanceCount = 0;
 static bo