Merge m-c to b2g-inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 06 Aug 2015 15:49:38 -0400
changeset 288278 803e16a6ed392e58bb55483f27d0bf33b4b1da48
parent 288277 e8411faa7112af46f8e0b5e72fb2c8772cd26c96 (current diff)
parent 288228 03e3d77d1b6b213c44102cff4d0fd7ca6dda760d (diff)
child 288279 29ad4a27752c0999654a2fba5bb0502171ab3b25
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound. a=merge
browser/devtools/webconsole/test/browser_webconsole_bug_846918_hsts_invalid-headers.js
browser/devtools/webconsole/test/browser_webconsole_hpkp_invalid-headers.js
browser/devtools/webconsole/test/browser_webconsole_hsts_invalid-headers.js
browser/devtools/webconsole/test/test-bug-846918-hsts-invalid-headers.html
browser/devtools/webconsole/test/test-bug-846918-hsts-invalid-headers.html^headers^
dom/push/test/test_push_manager_worker.html
mobile/android/tests/browser/robocop/session_formdata_sample.html
mobile/android/tests/browser/robocop/simpleservice.xml
mobile/android/tests/browser/robocop/testHomeProvider.java
mobile/android/tests/browser/robocop/testHomeProvider.js
mobile/android/tests/browser/robocop/testJNI.java
mobile/android/tests/browser/robocop/testJNI.js
mobile/android/tests/browser/robocop/testJavaAddons.java
mobile/android/tests/browser/robocop/testJavaAddons.js
mobile/android/tests/browser/robocop/testMigrateUI.java
mobile/android/tests/browser/robocop/testMigrateUI.js
mobile/android/tests/browser/robocop/testNetworkManager.java
mobile/android/tests/browser/robocop/testNetworkManager.js
mobile/android/tests/browser/robocop/testOfflinePage.java
mobile/android/tests/browser/robocop/testOfflinePage.js
mobile/android/tests/browser/robocop/testReaderView.java
mobile/android/tests/browser/robocop/testReaderView.js
mobile/android/tests/browser/robocop/testRestrictedProfiles.java
mobile/android/tests/browser/robocop/testRestrictedProfiles.js
mobile/android/tests/browser/robocop/testSessionFormData.java
mobile/android/tests/browser/robocop/testSessionFormData.js
mobile/android/tests/browser/robocop/testSharedPreferences.java
mobile/android/tests/browser/robocop/testSharedPreferences.js
mobile/android/tests/browser/robocop/testSimpleDiscovery.java
mobile/android/tests/browser/robocop/testSimpleDiscovery.js
mobile/android/tests/browser/robocop/testVideoDiscovery.java
mobile/android/tests/browser/robocop/testVideoDiscovery.js
mobile/android/tests/browser/robocop/testWebChannel.html
mobile/android/tests/browser/robocop/testWebChannel.java
mobile/android/tests/browser/robocop/testWebChannel.js
mobile/android/tests/browser/robocop/video_discovery.html
--- 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.
 
-Bug 1186748 needed a CLOBBER yet again
+Bug 1191675 to fix xpcshell failures
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/DocAccessibleChild.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DocAccessibleChild.h"
 
 #include "Accessible-inl.h"
 #include "ProxyAccessible.h"
 #include "Relation.h"
 #include "HyperTextAccessible-inl.h"
+#include "TextLeafAccessible.h"
 #include "ImageAccessible.h"
 #include "TableAccessible.h"
 #include "TableCellAccessible.h"
 #include "nsIPersistentProperties2.h"
 #include "nsISimpleEnumerator.h"
 #include "nsAccUtils.h"
 
 namespace mozilla {
@@ -92,16 +93,23 @@ DocAccessibleChild::IdToAccessibleSelect
 
 HyperTextAccessible*
 DocAccessibleChild::IdToHyperTextAccessible(const uint64_t& aID) const
 {
   Accessible* acc = IdToAccessible(aID);
   return acc && acc->IsHyperText() ? acc->AsHyperText() : nullptr;
 }
 
+TextLeafAccessible*
+DocAccessibleChild::IdToTextLeafAccessible(const uint64_t& aID) const
+{
+  Accessible* acc = IdToAccessible(aID);
+  return acc && acc->IsTextLeaf() ? acc->AsTextLeaf() : nullptr;
+}
+
 ImageAccessible*
 DocAccessibleChild::IdToImageAccessible(const uint64_t& aID) const
 {
   Accessible* acc = IdToAccessible(aID);
   return (acc && acc->IsImage()) ? acc->AsImage() : nullptr;
 }
 
 TableCellAccessible*
@@ -338,16 +346,34 @@ DocAccessibleChild::RecvARIARoleAtom(con
       roleAtom->ToString(*aRole);
     }
   }
 
   return true;
 }
 
 bool
+DocAccessibleChild::RecvGetLevelInternal(const uint64_t& aID, int32_t* aLevel)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (acc) {
+    *aLevel = acc->GetLevelInternal();
+  }
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvCaretLineNumber(const uint64_t& aID, int32_t* aLineNumber)
+{
+  HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+  *aLineNumber = acc && acc->IsTextRole() ? acc->CaretLineNumber() : 0;
+  return true;
+}
+
+bool
 DocAccessibleChild::RecvCaretOffset(const uint64_t& aID, int32_t* aOffset)
 {
   HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
   *aOffset = acc && acc->IsTextRole() ? acc->CaretOffset() : 0;
   return true;
 }
 
 bool
@@ -631,16 +657,27 @@ DocAccessibleChild::RecvScrollSubstringT
   if (acc) {
     acc->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoordinateType,
                                 aX, aY);
   }
 
   return true;
 }
 
+bool
+DocAccessibleChild::RecvText(const uint64_t& aID,
+                             nsString* aText)
+{
+  TextLeafAccessible* acc = IdToTextLeafAccessible(aID);
+  if (acc) {
+    *aText = acc->Text();
+  }
+
+  return true;
+}
 
 bool
 DocAccessibleChild::RecvReplaceText(const uint64_t& aID,
                                     const nsString& aText)
 {
   HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
   if (acc && acc->IsTextRole()) {
     acc->ReplaceText(aText);
@@ -1795,16 +1832,29 @@ DocAccessibleChild::RecvDocType(const ui
   if (acc && acc->IsDoc()) {
     acc->AsDoc()->DocType(*aType);
   }
 
   return true;
 }
 
 bool
+DocAccessibleChild::RecvTitle(const uint64_t& aID,
+                            nsString* aTitle)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (acc) {
+    mozilla::ErrorResult rv;
+    acc->GetContent()->GetTextContent(*aTitle, rv);
+  }
+
+  return true;
+}
+
+bool
 DocAccessibleChild::RecvURL(const uint64_t& aID,
                             nsString* aURL)
 {
   Accessible* acc = IdToAccessible(aID);
   if (acc && acc->IsDoc()) {
     acc->AsDoc()->URL(*aURL);
   }
 
--- a/accessible/ipc/DocAccessibleChild.h
+++ b/accessible/ipc/DocAccessibleChild.h
@@ -10,16 +10,17 @@
 #include "mozilla/a11y/DocAccessible.h"
 #include "mozilla/a11y/PDocAccessibleChild.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 namespace a11y {
 class Accessible;
 class HyperTextAccessible;
+class TextLeafAccessible;
 class ImageAccessible;
 class TableAccessible;
 class TableCellAccessible;
 class AccShowEvent;
 
   /*
    * These objects handle content side communication for an accessible document,
    * and their lifetime is the same as the document they represent.
@@ -88,19 +89,23 @@ public:
     override;
 
   virtual bool RecvIsSearchbox(const uint64_t& aID, bool* aRetVal) override;
 
   virtual bool RecvLandmarkRole(const uint64_t& aID, nsString* aLandmark) override;
 
   virtual bool RecvARIARoleAtom(const uint64_t& aID, nsString* aRole) override;
 
+  virtual bool RecvGetLevelInternal(const uint64_t& aID, int32_t* aLevel) override;
+
   virtual bool RecvAttributes(const uint64_t& aID,
                               nsTArray<Attribute> *aAttributes) override;
 
+  virtual bool RecvCaretLineNumber(const uint64_t& aID, int32_t* aLineNumber)
+    override;
   virtual bool RecvCaretOffset(const uint64_t& aID, int32_t* aOffset)
     override;
   virtual bool RecvSetCaretOffset(const uint64_t& aID, const int32_t& aOffset,
                                   bool* aValid) override;
 
   virtual bool RecvCharacterCount(const uint64_t& aID, int32_t* aCount)
      override;
   virtual bool RecvSelectionCount(const uint64_t& aID, int32_t* aCount)
@@ -189,16 +194,19 @@ public:
 
   virtual bool RecvScrollSubstringToPoint(const uint64_t& aID,
                                           const int32_t& aStartOffset,
                                           const int32_t& aEndOffset,
                                           const uint32_t& aCoordinateType,
                                           const int32_t& aX,
                                           const int32_t& aY) override;
 
+  virtual bool RecvText(const uint64_t& aID,
+                        nsString* aText) override;
+
   virtual bool RecvReplaceText(const uint64_t& aID,
                                const nsString& aText) override;
 
   virtual bool RecvInsertText(const uint64_t& aID,
                               const nsString& aText,
                               const int32_t& aPosition, bool* aValid) override;
 
   virtual bool RecvCopyText(const uint64_t& aID,
@@ -443,28 +451,30 @@ public:
                                 const uint32_t& aWhich,
                                 uint64_t* aChild,
                                 bool* aOk) override;
 
   virtual bool RecvBounds(const uint64_t& aID, nsIntRect* aRect) override;
 
   virtual bool RecvLanguage(const uint64_t& aID, nsString* aLocale) override;
   virtual bool RecvDocType(const uint64_t& aID, nsString* aType) override;
+  virtual bool RecvTitle(const uint64_t& aID, nsString* aTitle) override;
   virtual bool RecvURL(const uint64_t& aID, nsString* aURL) override;
   virtual bool RecvMimeType(const uint64_t& aID, nsString* aMime) override;
   virtual bool RecvURLDocTypeMimeType(const uint64_t& aID,
                                       nsString* aURL,
                                       nsString* aDocType,
                                       nsString* aMimeType) override;
 private:
 
   Accessible* IdToAccessible(const uint64_t& aID) const;
   Accessible* IdToAccessibleLink(const uint64_t& aID) const;
   Accessible* IdToAccessibleSelect(const uint64_t& aID) const;
   HyperTextAccessible* IdToHyperTextAccessible(const uint64_t& aID) const;
+  TextLeafAccessible* IdToTextLeafAccessible(const uint64_t& aID) const;
   ImageAccessible* IdToImageAccessible(const uint64_t& aID) const;
   TableCellAccessible* IdToTableCellAccessible(const uint64_t& aID) const;
   TableAccessible* IdToTableAccessible(const uint64_t& aID) const;
 
   bool PersistentPropertiesToArray(nsIPersistentProperties* aProps,
                                    nsTArray<Attribute>* aAttributes);
 
   DocAccessible* mDoc;
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -80,20 +80,22 @@ child:
   prio(high) sync Description(uint64_t aID) returns(nsString desc);
   prio(high) sync Attributes(uint64_t aID) returns(Attribute[] attributes);
   prio(high) sync RelationByType(uint64_t aID, uint32_t aRelationType)
     returns(uint64_t[] targets);
   prio(high) sync Relations(uint64_t aID) returns(RelationTargets[] relations);
   prio(high) sync IsSearchbox(uint64_t aID) returns(bool retval);
   prio(high) sync LandmarkRole(uint64_t aID) returns(nsString landmark);
   prio(high) sync ARIARoleAtom(uint64_t aID) returns(nsString role);
+  prio(high) sync GetLevelInternal(uint64_t aID) returns(int32_t aLevel);
 
   // AccessibleText
 
   // TextSubstring is getText in IDL.
+  prio(high) sync CaretLineNumber(uint64_t aID) returns(int32_t aLineNumber);
   prio(high) sync CaretOffset(uint64_t aID) returns(int32_t aOffset);
   prio(high) sync SetCaretOffset(uint64_t aID, int32_t aOffset) returns (bool aValid);
   prio(high) sync CharacterCount(uint64_t aID) returns(int32_t aCount);
   prio(high) sync SelectionCount(uint64_t aID) returns(int32_t aCount);
   prio(high) sync TextSubstring(uint64_t aID, int32_t aStartOffset, int32_t
                                 aEndOffset) returns(nsString aText, bool aValid);
   prio(high) sync GetTextAfterOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
     returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
@@ -130,16 +132,17 @@ child:
   ScrollSubstringTo(uint64_t aID, int32_t aStartOffset, int32_t aEndOffset,
                     uint32_t aScrollType);
   ScrollSubstringToPoint(uint64_t aID,
                          int32_t aStartOffset,
                          int32_t aEndOffset,
                          uint32_t aCoordinateType,
                          int32_t aX, int32_t aY);
 
+  prio(high) sync Text(uint64_t aID) returns(nsString aText);
   prio(high) sync ReplaceText(uint64_t aID, nsString aText);
   prio(high) sync InsertText(uint64_t aID, nsString aText, int32_t aPosition)
     returns(bool aValid);
   prio(high) sync CopyText(uint64_t aID, int32_t aStartPos, int32_t aEndPos)
     returns(bool aValid);
   prio(high) sync CutText(uint64_t aID, int32_t aStartPos, int32_t aEndPos)
     returns(bool aValid);
   prio(high) sync DeleteText(uint64_t aID, int32_t aStartPos, int32_t aEndPos)
@@ -230,15 +233,16 @@ child:
   prio(high) sync FocusedChild(uint64_t aID)
     returns(uint64_t aChild, bool aOk);
   prio(high) sync ChildAtPoint(uint64_t aID, int32_t aX, int32_t aY, uint32_t aWhich)
     returns(uint64_t aChild, bool aOk);
   prio(high) sync Bounds(uint64_t aID) returns(nsIntRect aRect);
 
   prio(high) sync Language(uint64_t aID) returns(nsString aLocale);
   prio(high) sync DocType(uint64_t aID) returns(nsString aType);
+  prio(high) sync Title(uint64_t aID) returns(nsString aTitle);
   prio(high) sync URL(uint64_t aID) returns(nsString aURL);
   prio(high) sync MimeType(uint64_t aID) returns(nsString aMime);
   prio(high) sync URLDocTypeMimeType(uint64_t aID) returns(nsString aURL, nsString aDocType, nsString aMimeType);
 };
 
 }
 }
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/ProxyAccessible.cpp
@@ -28,17 +28,17 @@ ProxyAccessible::Shutdown()
   if (!mOuterDoc) {
     uint32_t childCount = mChildren.Length();
     for (uint32_t idx = 0; idx < childCount; idx++)
       mChildren[idx]->Shutdown();
   } else {
     if (mChildren.Length() != 1)
       MOZ_CRASH("outer doc doesn't own adoc!");
 
-    static_cast<DocAccessibleParent*>(mChildren[0])->Unbind();
+    mChildren[0]->AsDoc()->Unbind();
   }
 
   mChildren.Clear();
   ProxyDestroyed(this);
   mDoc->RemoveAccessible(this);
 }
 
 void
@@ -187,16 +187,32 @@ nsIAtom*
 ProxyAccessible::ARIARoleAtom() const
 {
   nsString role;
   unused << mDoc->SendARIARoleAtom(mID, &role);
   return NS_GetStaticAtom(role);
 }
 
 int32_t
+ProxyAccessible::GetLevelInternal()
+{
+  int32_t level = 0;
+  unused << mDoc->SendGetLevelInternal(mID, &level);
+  return level;
+}
+
+int32_t
+ProxyAccessible::CaretLineNumber()
+{
+  int32_t line = -1;
+  unused << mDoc->SendCaretOffset(mID, &line);
+  return line;
+}
+
+int32_t
 ProxyAccessible::CaretOffset()
 {
   int32_t offset = 0;
   unused << mDoc->SendCaretOffset(mID, &offset);
   return offset;
 }
 
 bool
@@ -367,16 +383,22 @@ ProxyAccessible::ScrollSubstringToPoint(
                                         uint32_t aCoordinateType,
                                         int32_t aX, int32_t aY)
 {
   unused << mDoc->SendScrollSubstringToPoint(mID, aStartOffset, aEndOffset,
                                              aCoordinateType, aX, aY);
 }
 
 void
+ProxyAccessible::Text(nsString* aText)
+{
+  unused << mDoc->SendText(mID, aText);
+}
+
+void
 ProxyAccessible::ReplaceText(const nsString& aText)
 {
   unused << mDoc->SendReplaceText(mID, aText);
 }
 
 bool
 ProxyAccessible::InsertText(const nsString& aText, int32_t aPosition)
 {
@@ -1011,16 +1033,22 @@ ProxyAccessible::Language(nsString& aLoc
 
 void
 ProxyAccessible::DocType(nsString& aType)
 {
   unused << mDoc->SendDocType(mID, &aType);
 }
 
 void
+ProxyAccessible::Title(nsString& aTitle)
+{
+  unused << mDoc->SendTitle(mID, &aTitle);
+}
+
+void
 ProxyAccessible::URL(nsString& aURL)
 {
   unused << mDoc->SendURL(mID, &aURL);
 }
 
 void
 ProxyAccessible::MimeType(nsString aMime)
 {
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_ProxyAccessible_h
 #define mozilla_a11y_ProxyAccessible_h
 
 #include "mozilla/a11y/Role.h"
 #include "nsIAccessibleText.h"
+#include "nsIAccessibleTypes.h"
 #include "Accessible.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsRect.h"
 #include "Accessible.h"
 
 namespace mozilla {
 namespace a11y {
@@ -25,17 +26,17 @@ enum class RelationType;
 
 class ProxyAccessible
 {
 public:
 
   ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
                   DocAccessibleParent* aDoc, role aRole) :
      mParent(aParent), mDoc(aDoc), mWrapper(0), mID(aID), mRole(aRole),
-     mOuterDoc(false)
+     mOuterDoc(false), mIsDoc(false)
   {
     MOZ_COUNT_CTOR(ProxyAccessible);
   }
   ~ProxyAccessible()
   {
     MOZ_COUNT_DTOR(ProxyAccessible);
     MOZ_ASSERT(!mWrapper);
   }
@@ -121,16 +122,19 @@ public:
                  nsTArray<nsTArray<ProxyAccessible*>>* aTargetSets) const;
 
   bool IsSearchbox() const;
 
   nsIAtom* LandmarkRole() const;
 
   nsIAtom* ARIARoleAtom() const;
 
+  int32_t GetLevelInternal();
+
+  int32_t CaretLineNumber();
   int32_t CaretOffset();
   bool SetCaretOffset(int32_t aOffset);
 
   int32_t CharacterCount();
   int32_t SelectionCount();
 
   /**
    * Get the text between the given offsets.
@@ -155,17 +159,17 @@ public:
   void TextAttributes(bool aIncludeDefAttrs,
                       const int32_t aOffset,
                       nsTArray<Attribute>* aAttributes,
                       int32_t* aStartOffset,
                       int32_t* aEndOffset);
   void DefaultTextAttributes(nsTArray<Attribute>* aAttrs);
 
   nsIntRect TextBounds(int32_t aStartOffset, int32_t aEndOffset,
-                       uint32_t aCoordType);
+                       uint32_t aCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE);
 
   nsIntRect CharBounds(int32_t aOffset, uint32_t aCoordType);
 
   int32_t OffsetAtPoint(int32_t aX, int32_t aY, uint32_t aCoordType);
 
   bool SelectionBoundsAt(int32_t aSelectionNum,
                          nsString& aData,
                          int32_t* aStartOffset,
@@ -183,16 +187,18 @@ public:
   void ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
                          uint32_t aScrollType);
 
   void ScrollSubstringToPoint(int32_t aStartOffset,
                               int32_t aEndOffset,
                               uint32_t aCoordinateType,
                               int32_t aX, int32_t aY);
 
+  void Text(nsString* aText);
+
   void ReplaceText(const nsString& aText);
 
   bool InsertText(const nsString& aText, int32_t aPosition);
 
   bool CopyText(int32_t aStartPos, int32_t aEndPos);
 
   bool CutText(int32_t aStartPos, int32_t aEndPos);
 
@@ -301,48 +307,62 @@ public:
   void TakeFocus();
   ProxyAccessible* FocusedChild();
   ProxyAccessible* ChildAtPoint(int32_t aX, int32_t aY,
                                 Accessible::EWhichChildAtPoint aWhichChild);
   nsIntRect Bounds();
 
   void Language(nsString& aLocale);
   void DocType(nsString& aType);
+  void Title(nsString& aTitle);
   void URL(nsString& aURL);
   void MimeType(nsString aMime);
   void URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
                           nsString& aMimeType);
 
   /**
    * Allow the platform to store a pointers worth of data on us.
    */
   uintptr_t GetWrapper() const { return mWrapper; }
   void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
 
   /*
    * Return the ID of the accessible being proxied.
    */
   uint64_t ID() const { return mID; }
 
+  /**
+   * Return the document containing this proxy, or the proxy itself if it is a
+   * document.
+   */
+  DocAccessibleParent* Document() const { return mDoc; }
+
+  /**
+   * Return true if this proxy is a DocAccessibleParent.
+   */
+  bool IsDoc() const { return mIsDoc; }
+  DocAccessibleParent* AsDoc() const { return IsDoc() ? mDoc : nullptr; }
+
 protected:
   explicit ProxyAccessible(DocAccessibleParent* aThisAsDoc) :
     mParent(nullptr), mDoc(aThisAsDoc), mWrapper(0), mID(0),
-    mRole(roles::DOCUMENT), mOuterDoc(false)
+    mRole(roles::DOCUMENT), mOuterDoc(false), mIsDoc(true)
   { MOZ_COUNT_CTOR(ProxyAccessible); }
 
 protected:
   ProxyAccessible* mParent;
 
 private:
   nsTArray<ProxyAccessible*> mChildren;
   DocAccessibleParent* mDoc;
   uintptr_t mWrapper;
   uint64_t mID;
-  role mRole : 31;
+  role mRole : 30;
   bool mOuterDoc : 1;
+  const bool mIsDoc: 1;
 };
 
 enum Interfaces
 {
   HYPERTEXT = 1,
   HYPERLINK = 2,
   IMAGE = 4,
   VALUE = 8,
--- a/accessible/mac/mozHTMLAccessible.mm
+++ b/accessible/mac/mozHTMLAccessible.mm
@@ -12,113 +12,127 @@
 
 #import "nsCocoaUtils.h"
 
 @implementation mozHeadingAccessible
 
 - (NSString*)title
 {
   nsAutoString title;
-  mozilla::ErrorResult rv;
-  // XXX use the flattening API when there are available
-  // see bug 768298
-  [self getGeckoAccessible]->GetContent()->GetTextContent(title, rv);
+  if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+    mozilla::ErrorResult rv;
+    // XXX use the flattening API when there are available
+    // see bug 768298
+    accWrap->GetContent()->GetTextContent(title, rv);
+  } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+    proxy->Title(title);
+  }
 
   return nsCocoaUtils::ToNSString(title);
 }
 
 - (id)value
 {
-  AccessibleWrap* accWrap = [self getGeckoAccessible];
+  uint32_t level = 0;
+  if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+    level = accWrap->GetLevelInternal();
+  } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+    level = proxy->GetLevelInternal();
+  }
 
-  if (!accWrap || !accWrap->IsHyperText())
-    return nil;
-
-  uint32_t level = accWrap->AsHyperText()->GetLevelInternal();
   return [NSNumber numberWithInt:level];
 }
 
 @end
 
 @interface mozLinkAccessible ()
 -(NSURL*)url;
 @end
 
 @implementation mozLinkAccessible
 
 - (NSArray*)accessibilityAttributeNames
 {
   // if we're expired, we don't support any attributes.
-  if (![self getGeckoAccessible])
+  if (![self getGeckoAccessible] && ![self getProxyAccessible])
     return [NSArray array];
-  
+
   static NSMutableArray* attributes = nil;
-  
+
   if (!attributes) {
     attributes = [[super accessibilityAttributeNames] mutableCopy];
     [attributes addObject:NSAccessibilityURLAttribute];
   }
 
   return attributes;
 }
 
 - (id)accessibilityAttributeValue:(NSString *)attribute
 {
   if ([attribute isEqualToString:NSAccessibilityURLAttribute])
     return [self url];
 
   return [super accessibilityAttributeValue:attribute];
 }
 
-- (NSArray*)accessibilityActionNames 
+- (NSArray*)accessibilityActionNames
 {
     // if we're expired, we don't support any attributes.
-  if (![self getGeckoAccessible])
+  if (![self getGeckoAccessible] && ![self getProxyAccessible])
     return [NSArray array];
 
   static NSArray* actionNames = nil;
 
   if (!actionNames) {
     actionNames = [[NSArray alloc] initWithObjects:NSAccessibilityPressAction,
                                    nil];
   }
 
   return actionNames;
 }
 
-- (void)accessibilityPerformAction:(NSString*)action 
+- (void)accessibilityPerformAction:(NSString*)action
 {
   AccessibleWrap* accWrap = [self getGeckoAccessible];
-
-  if (!accWrap)
+  ProxyAccessible* proxy = [self getProxyAccessible];
+  if (!accWrap && !proxy) {
     return;
+  }
 
-  if ([action isEqualToString:NSAccessibilityPressAction])
-    accWrap->DoAction(0);
-  else
-    [super accessibilityPerformAction:action];
+  if ([action isEqualToString:NSAccessibilityPressAction]) {
+    if (accWrap) {
+      accWrap->DoAction(0);
+    } else if (proxy) {
+      proxy->DoAction(0);
+    }
+    return;
+  }
+
+  [super accessibilityPerformAction:action];
+
 }
 
 - (NSString*)customDescription
 {
   return @"";
 }
 
 - (NSString*)value
 {
   return @"";
 }
 
 - (NSURL*)url
 {
-  if (![self getGeckoAccessible] || [self getGeckoAccessible]->IsDefunct())
-    return nil;
-
   nsAutoString value;
-  [self getGeckoAccessible]->Value(value);
+  if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+    accWrap->Value(value);
+  } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+    proxy->Value(value);
+  }
 
   NSString* urlString = value.IsEmpty() ? nil : nsCocoaUtils::ToNSString(value);
   if (!urlString)
     return nil;
 
   return [NSURL URLWithString:urlString];
 }
 
--- a/accessible/mac/mozTextAccessible.mm
+++ b/accessible/mac/mozTextAccessible.mm
@@ -109,23 +109,33 @@ ToNSString(id aValue)
     if ([[self role] isEqualToString:NSAccessibilityStaticTextRole]) {
       NSString* selectedText = [self selectedText];
       return (selectedText && [selectedText length]) ? selectedText : [self text];
     }
 
     return [self text];
   }
 
-  AccessibleWrap* accWrap = [self getGeckoAccessible];
+  if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+    if ([attribute isEqualToString:@"AXRequired"]) {
+      return [NSNumber numberWithBool:!!(accWrap->State() & states::REQUIRED)];
+    }
 
-  if ([attribute isEqualToString:@"AXRequired"])
-    return [NSNumber numberWithBool:!!(accWrap->State() & states::REQUIRED)];
+    if ([attribute isEqualToString:@"AXInvalid"]) {
+      return [NSNumber numberWithBool:!!(accWrap->State() & states::INVALID)];
+    }
+  } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+    if ([attribute isEqualToString:@"AXRequired"]) {
+      return [NSNumber numberWithBool:!!(proxy->State() & states::REQUIRED)];
+    }
 
-  if ([attribute isEqualToString:@"AXInvalid"])
-    return [NSNumber numberWithBool:!!(accWrap->State() & states::INVALID)];
+    if ([attribute isEqualToString:@"AXInvalid"]) {
+      return [NSNumber numberWithBool:!!(proxy->State() & states::INVALID)];
+    }
+  }
 
   if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute])
     return [self visibleCharacterRange];
 
   // let mozAccessible handle all other attributes
   return [super accessibilityAttributeValue:attribute];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
@@ -152,18 +162,20 @@ ToNSString(id aValue)
     ];
   }
   return supportedParametrizedAttributes;
 }
 
 - (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter
 {
   AccessibleWrap* accWrap = [self getGeckoAccessible];
+  ProxyAccessible* proxy = [self getProxyAccessible];
+
   HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
-  if (!textAcc)
+  if (!textAcc && !proxy)
     return nil;
 
   if ([attribute isEqualToString:NSAccessibilityStringForRangeParameterizedAttribute]) {
     NSRange range;
     if (!ToNSRange(parameter, &range)) {
 #if DEBUG
       NSLog(@"%@: range not set", attribute);
 #endif
@@ -201,17 +213,22 @@ ToNSString(id aValue)
 #if DEBUG
       NSLog(@"%@:no range", attribute);
 #endif
       return nil;
     }
 
     int32_t start = range.location;
     int32_t end = start + range.length;
-    nsIntRect bounds = textAcc->TextBounds(start, end);
+    nsIntRect bounds;
+    if (textAcc) {
+      bounds = textAcc->TextBounds(start, end);
+    } else if (proxy) {
+      bounds = proxy->TextBounds(start, end);
+    }
 
     return [NSValue valueWithRect:nsCocoaUtils::GeckoRectToCocoaRect(bounds)];
   }
 
 #if DEBUG
   NSLog(@"unhandled attribute:%@ forParameter:%@", attribute, parameter);
 #endif
 
@@ -235,57 +252,76 @@ ToNSString(id aValue)
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
 }
 
 - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
+  ProxyAccessible* proxy = [self getProxyAccessible];
+
   HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
-  if (!textAcc)
+  if (!textAcc && !proxy)
     return;
 
   if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
     [self setText:ToNSString(value)];
 
     return;
   }
 
   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
     NSString* stringValue = ToNSString(value);
     if (!stringValue)
       return;
 
     int32_t start = 0, end = 0;
-    textAcc->SelectionBoundsAt(0, &start, &end);
-    textAcc->DeleteText(start, end - start);
-
     nsString text;
-    nsCocoaUtils::GetStringForNSString(stringValue, text);
-    textAcc->InsertText(text, start);
+    if (textAcc) {
+      textAcc->SelectionBoundsAt(0, &start, &end);
+      textAcc->DeleteText(start, end - start);
+      nsCocoaUtils::GetStringForNSString(stringValue, text);
+      textAcc->InsertText(text, start);
+    } else if (proxy) {
+      nsString data;
+      proxy->SelectionBoundsAt(0, data, &start, &end);
+      proxy->DeleteText(start, end - start);
+      nsCocoaUtils::GetStringForNSString(stringValue, text);
+      proxy->InsertText(text, start);
+    }
   }
 
   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
     NSRange range;
     if (!ToNSRange(value, &range))
       return;
 
-    textAcc->SetSelectionBoundsAt(0, range.location,
+    if (textAcc) {
+      textAcc->SetSelectionBoundsAt(0, range.location,
+                                    range.location + range.length);
+    } else if (proxy) {
+      proxy->SetSelectionBoundsAt(0, range.location,
                                   range.location + range.length);
+    }
     return;
   }
 
   if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) {
     NSRange range;
     if (!ToNSRange(value, &range))
       return;
 
-    textAcc->ScrollSubstringTo(range.location, range.location + range.length,
+    if (textAcc) {
+      textAcc->ScrollSubstringTo(range.location, range.location + range.length,
+                                 nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE);
+    } else if (proxy) {
+      proxy->ScrollSubstringTo(range.location, range.location + range.length,
                                nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE);
+    }
     return;
   }
 
   [super accessibilitySetValue:value forAttribute:attribute];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
@@ -306,145 +342,194 @@ ToNSString(id aValue)
   if ([[self role] isEqualToString:NSAccessibilityStaticTextRole])
     return YES;
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
   HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
   if (textAcc)
     return (accWrap->State() & states::READONLY) == 0;
 
+  if (ProxyAccessible* proxy = [self getProxyAccessible])
+    return (proxy->State() & states::READONLY) == 0;
+
   return NO;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
 }
 
 - (NSNumber*)caretLineNumber
 {
   AccessibleWrap* accWrap = [self getGeckoAccessible];
   HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
-  int32_t lineNumber = textAcc ?
-    textAcc->CaretLineNumber() - 1 : -1;
+
+  int32_t lineNumber = -1;
+  if (textAcc) {
+    lineNumber = textAcc->CaretLineNumber() - 1;
+  } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+    lineNumber = proxy->CaretLineNumber() - 1;
+  }
 
   return (lineNumber >= 0) ? [NSNumber numberWithInt:lineNumber] : nil;
 }
 
 - (void)setText:(NSString*)aNewString
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
   HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+
+  nsString text;
+  nsCocoaUtils::GetStringForNSString(aNewString, text);
   if (textAcc) {
-    nsString text;
-    nsCocoaUtils::GetStringForNSString(aNewString, text);
     textAcc->ReplaceText(text);
+  } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+    proxy->ReplaceText(text);
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (NSString*)text
 {
   AccessibleWrap* accWrap = [self getGeckoAccessible];
+  ProxyAccessible* proxy = [self getProxyAccessible];
   HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
-  if (!accWrap || !textAcc)
+  if (!textAcc && !proxy)
     return nil;
 
   // A password text field returns an empty value
   if (mRole == roles::PASSWORD_TEXT)
     return @"";
 
   nsAutoString text;
-  textAcc->TextSubstring(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT, text);
+  if (textAcc) {
+    textAcc->TextSubstring(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT, text);
+  } else if (proxy) {
+    proxy->TextSubstring(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT, text);
+  }
+
   return nsCocoaUtils::ToNSString(text);
 }
 
 - (long)textLength
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
+  ProxyAccessible* proxy = [self getProxyAccessible];
   HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
-  if (!accWrap || !textAcc)
+  if (!textAcc && !proxy)
     return 0;
 
-  return textAcc ? textAcc->CharacterCount() : 0;
+  return textAcc ? textAcc->CharacterCount() : proxy->CharacterCount();
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
 }
 
 - (long)selectedTextLength
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
+  ProxyAccessible* proxy = [self getProxyAccessible];
   HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+  if (!textAcc && !proxy)
+    return 0;
+
+  int32_t start = 0, end = 0;
   if (textAcc) {
-    int32_t start = 0, end = 0;
     textAcc->SelectionBoundsAt(0, &start, &end);
-    return (end - start);
+  } else if (proxy) {
+    nsString data;
+    proxy->SelectionBoundsAt(0, data, &start, &end);
   }
-  return 0;
+  return (end - start);
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
 }
 
 - (NSString*)selectedText
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
+  ProxyAccessible* proxy = [self getProxyAccessible];
   HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+  if (!textAcc && !proxy)
+    return nil;
+
+  int32_t start = 0, end = 0;
+  nsAutoString selText;
   if (textAcc) {
-    int32_t start = 0, end = 0;
     textAcc->SelectionBoundsAt(0, &start, &end);
     if (start != end) {
-      nsAutoString selText;
       textAcc->TextSubstring(start, end, selText);
-      return nsCocoaUtils::ToNSString(selText);
     }
+  } else if (proxy) {
+    proxy->SelectionBoundsAt(0, selText, &start, &end);
   }
-  return nil;
+
+  return nsCocoaUtils::ToNSString(selText);
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (NSValue*)selectedTextRange
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
+  ProxyAccessible* proxy = [self getProxyAccessible];
   HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+
+  int32_t start = 0;
+  int32_t end = 0;
+  int32_t count = 0;
   if (textAcc) {
-    int32_t start = 0;
-    int32_t end = 0;
-    int32_t count = textAcc->SelectionCount();
-
+    count = textAcc->SelectionCount();
     if (count) {
       textAcc->SelectionBoundsAt(0, &start, &end);
       return [NSValue valueWithRange:NSMakeRange(start, end - start)];
     }
 
     start = textAcc->CaretOffset();
-    return [NSValue valueWithRange:NSMakeRange(start != -1 ? start : 0, 0)]; 
+    return [NSValue valueWithRange:NSMakeRange(start != -1 ? start : 0, 0)];
   }
+
+  if (proxy) {
+    count = proxy->SelectionCount();
+    if (count) {
+      nsString data;
+      proxy->SelectionBoundsAt(0, data, &start, &end);
+      return [NSValue valueWithRange:NSMakeRange(start, end - start)];
+    }
+
+    start = proxy->CaretOffset();
+    return [NSValue valueWithRange:NSMakeRange(start != -1 ? start : 0, 0)];
+  }
+
   return [NSValue valueWithRange:NSMakeRange(0, 0)];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (NSValue*)visibleCharacterRange
 {
   // XXX this won't work with Textarea and such as we actually don't give
   // the visible character range.
   AccessibleWrap* accWrap = [self getGeckoAccessible];
+  ProxyAccessible* proxy = [self getProxyAccessible];
   HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+  if (!textAcc && !proxy)
+    return 0;
+
   return [NSValue valueWithRange:
     NSMakeRange(0, textAcc ?
-                textAcc->CharacterCount() : 0)];
+                textAcc->CharacterCount() : proxy->CharacterCount())];
 }
 
 - (void)valueDidChange
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
                                   NSAccessibilityValueChangedNotification);
@@ -458,23 +543,30 @@ ToNSString(id aValue)
                                   NSAccessibilitySelectedTextChangedNotification);
 }
 
 - (NSString*)stringFromRange:(NSRange*)range
 {
   NS_PRECONDITION(range, "no range");
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
+  ProxyAccessible* proxy = [self getProxyAccessible];
   HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
-  if (!textAcc)
+  if (!textAcc && !proxy)
     return nil;
 
   nsAutoString text;
-  textAcc->TextSubstring(range->location,
-                         range->location + range->length, text);
+  if (textAcc) {
+    textAcc->TextSubstring(range->location,
+                           range->location + range->length, text);
+  } else if (proxy) {
+    proxy->TextSubstring(range->location,
+                           range->location + range->length, text);
+  }
+
   return nsCocoaUtils::ToNSString(text);
 }
 
 @end
 
 @implementation mozTextLeafAccessible
 
 - (NSArray*)accessibilityAttributeNames
@@ -496,25 +588,37 @@ ToNSString(id aValue)
   if ([attribute isEqualToString:NSAccessibilityValueAttribute])
     return [self text];
 
   return [super accessibilityAttributeValue:attribute];
 }
 
 - (NSString*)text
 {
-  AccessibleWrap* accWrap = [self getGeckoAccessible];
-  if (!accWrap)
-    return nil;
+  if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+    return nsCocoaUtils::ToNSString(accWrap->AsTextLeaf()->Text());
+  }
 
-  return nsCocoaUtils::ToNSString(accWrap->AsTextLeaf()->Text());
+  if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+    nsString text;
+    proxy->Text(&text);
+    return nsCocoaUtils::ToNSString(text);
+  }
+
+  return nil;
 }
 
 - (long)textLength
 {
-  AccessibleWrap* accWrap = [self getGeckoAccessible];
-  if (!accWrap)
-    return 0;
+  if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+    return accWrap->AsTextLeaf()->Text().Length();
+  }
 
-  return accWrap->AsTextLeaf()->Text().Length();
+  if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+    nsString text;
+    proxy->Text(&text);
+    return text.Length();
+  }
+
+  return 0;
 }
 
 @end
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -17,17 +17,17 @@
 
   <commandset id="mainCommandSet">
     <command id="cmd_newNavigator" oncommand="OpenBrowserWindow()" reserved="true"/>
     <command id="cmd_handleBackspace" oncommand="BrowserHandleBackspace();" />
     <command id="cmd_handleShiftBackspace" oncommand="BrowserHandleShiftBackspace();" />
 
     <command id="cmd_newNavigatorTab" oncommand="BrowserOpenNewTabOrWindow(event);" reserved="true"/>
     <command id="Browser:OpenFile"  oncommand="BrowserOpenFileWindow();"/>
-    <command id="Browser:SavePage" oncommand="saveDocument(gBrowser.selectedBrowser.contentDocumentAsCPOW);"/>
+    <command id="Browser:SavePage" oncommand="saveBrowser(gBrowser.selectedBrowser);"/>
 
     <command id="Browser:SendLink"
              oncommand="MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);"/>
 
     <command id="cmd_pageSetup" oncommand="PrintUtils.showPageSetup();"/>
     <command id="cmd_print" oncommand="PrintUtils.printWindow(window.gBrowser.selectedBrowser.outerWindowID, window.gBrowser.selectedBrowser);"/>
     <command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
     <command id="cmd_close" oncommand="BrowserCloseTabOrWindow()" reserved="true"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1791,17 +1791,17 @@ function HandleAppCommandEvent(evt) {
   case "Open":
     BrowserOpenFileWindow();
     break;
   case "Print":
     PrintUtils.printWindow(gBrowser.selectedBrowser.outerWindowID,
                            gBrowser.selectedBrowser);
     break;
   case "Save":
-    saveDocument(gBrowser.selectedBrowser.contentDocumentAsCPOW);
+    saveBrowser(gBrowser.selectedBrowser);
     break;
   case "SendMail":
     MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);
     break;
   default:
     return;
   }
   evt.stopPropagation();
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1665,17 +1665,17 @@ nsContextMenu.prototype = {
     SocialShare.sharePage(null, { url: this.mediaURL, source: this.mediaURL }, this.target);
   },
 
   shareSelect: function CM_shareSelect() {
     SocialShare.sharePage(null, { url: this.browser.currentURI.spec, text: this.textSelected }, this.target);
   },
 
   savePageAs: function CM_savePageAs() {
-    saveDocument(this.browser.contentDocumentAsCPOW);
+    saveBrowser(this.browser);
   },
 
   saveLinkToPocket: function CM_saveLinkToPocket() {
     Pocket.savePage(this.browser, this.linkURL);
   },
 
   savePageToPocket: function CM_saveToPocket() {
     Pocket.savePage(this.browser, this.browser.currentURI.spec, this.browser.contentTitle);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -676,16 +676,21 @@
                 // We need to add 2 because loadURIWithFlags may have
                 // cancelled a pending load which would have cleared
                 // its anchor scroll detection temporary increment.
                 if (aWebProgress.isTopLevel) {
                   this.mBrowser.userTypedClear += 2;
 
                   // If the browser is loading it must not be crashed anymore
                   this.mTab.removeAttribute("crashed");
+
+                  // If the browser was previously muted, we should restore the muted state.
+                  if (this.mTab.hasAttribute("muted")) {
+                    this.mTab.linkedBrowser.mute();
+                  }
                 }
 
                 if (this._shouldShowProgress(aRequest)) {
                   if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
                     this.mTab.setAttribute("busy", "true");
                     if (!(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
                       this.mTabBrowser.setTabTitleLoading(this.mTab);
                   }
@@ -1514,22 +1519,27 @@
             // Unhook our progress listener.
             let tab = this.getTabForBrowser(aBrowser);
             let index = tab._tPos;
             let filter = this.mTabFilters[index];
             aBrowser.webProgress.removeProgressListener(filter);
             // Make sure the browser is destroyed so it unregisters from observer notifications
             aBrowser.destroy();
 
+            // Make sure to restore the original droppedLinkHandler.
+            let droppedLinkHandler = aBrowser.droppedLinkHandler;
+
             // Change the "remote" attribute.
             let parent = aBrowser.parentNode;
             parent.removeChild(aBrowser);
             aBrowser.setAttribute("remote", aShouldBeRemote ? "true" : "false");
             parent.appendChild(aBrowser);
 
+            aBrowser.droppedLinkHandler = droppedLinkHandler;
+
             // Switching a browser's remoteness will create a new frameLoader.
             // As frameLoaders start out with an active docShell we have to
             // deactivate it if this is not the selected tab's browser or the
             // browser window is minimized.
             aBrowser.docShellIsActive = (aBrowser == this.selectedBrowser &&
                                          window.windowState != window.STATE_MINIMIZED);
 
             // Restore the progress listener.
@@ -4260,16 +4270,17 @@
 
           this.updateBrowserRemotenessByURL(browser, "about:tabcrashed");
 
           browser.setAttribute("crashedPageTitle", title);
           browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
           browser.removeAttribute("crashedPageTitle");
           let tab = this.getTabForBrowser(browser);
           tab.setAttribute("crashed", true);
+          tab.removeAttribute("soundplaying");
           this.setIcon(tab, icon);
         ]]>
       </handler>
       <handler event="DOMAudioPlaybackStarted">
         <![CDATA[
           if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") ||
               !event.isTrusted)
             return;
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -310,18 +310,18 @@ const CustomizableWidgets = [
     id: "save-page-button",
     shortcutId: "key_savePage",
     tooltiptext: "save-page-button.tooltiptext3",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
       let win = aEvent.target &&
                 aEvent.target.ownerDocument &&
                 aEvent.target.ownerDocument.defaultView;
-      if (win && typeof win.saveDocument == "function") {
-        win.saveDocument(win.gBrowser.selectedBrowser.contentDocumentAsCPOW);
+      if (win && typeof win.saveBrowser == "function") {
+        win.saveBrowser(win.gBrowser.selectedBrowser);
       }
     }
   }, {
     id: "find-button",
     shortcutId: "key_find",
     tooltiptext: "find-button.tooltiptext3",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
--- a/browser/components/loop/.eslintrc
+++ b/browser/components/loop/.eslintrc
@@ -37,31 +37,31 @@
 
     // Eslint built-in rules are documented at <http://eslint.org/docs/rules/>
     "callback-return": 0,         // TBD
     "camelcase": 0,               // TODO: set to 2
     "comma-spacing": 2,
     "computed-property-spacing": [2, "never"],
     "consistent-return": 0,       // TODO: set to 2
     "curly": [2, "all"],
-    dot-location: 0,              // [2, property],
+    "dot-location": [2, "property"],
     "eol-last": 2,
     "eqeqeq": 0,                  // TBD. Might need to be separate for content & chrome
     "key-spacing": [2, {"beforeColon": false, "afterColon": true }],
     "linebreak-style": [2, "unix"],
     "new-cap": 0,                 // TODO: set to 2
     "new-parens": 2,
     "no-alert": 2,
     "no-array-constructor": 2,
     "no-caller": 2,
     "no-catch-shadow": 0,         // TODO: set to 2
     "no-class-assign": 2,
     "no-const-assign": 2,
     "no-console": 0,              // Leave as 0. We use console logging in content code.
-    "no-empty": 0,                // TODO: set to 2
+    "no-empty": 2,
     "no-empty-label": 2,
     "no-eval": 2,
     "no-extend-native": 2, // XXX
     "no-extra-bind": 0,           // Leave as 0
     "no-extra-parens": 0,         // TODO: (bug?) [2, "functions"],
     "no-implied-eval": 2,
     "no-invalid-this": 0,         // TBD
     "no-iterator": 2,
--- a/browser/components/loop/content/js/roomStore.js
+++ b/browser/components/loop/content/js/roomStore.js
@@ -514,17 +514,19 @@ loop.store = loop.store || {};
         // 2) a new URL is provided as of now,
         // 3) the URL data has changed.
         var diff = loop.shared.utils.objectDiff(oldRoomURL, newRoomURL);
         if (diff.added.length || diff.updated.length) {
           newRoomURL = _.extend(oldRoomURL || {}, newRoomURL);
           var isValidURL = false;
           try {
             isValidURL = new URL(newRoomURL.location);
-          } catch(ex) {}
+          } catch(ex) {
+            // URL may throw, default to false;
+          }
           if (isValidURL) {
             roomData.urls = [newRoomURL];
           }
         }
         // TODO: there currently is no clear UX defined on what to do when all
         // context data was cleared, e.g. when diff.removed contains all the
         // context properties. Until then, we can't deal with context removal here.
 
--- a/browser/components/loop/modules/CardDavImporter.jsm
+++ b/browser/components/loop/modules/CardDavImporter.jsm
@@ -104,18 +104,18 @@ this.CardDavImporter.prototype = {
 
       // Get list of contact URLs
       let body = "<d:propfind xmlns:d='DAV:'><d:prop><d:getetag />" +
                  "</d:prop></d:propfind>";
       let abook = yield this._davPromise("PROPFIND", startURL, auth,
                                          DEPTH_RESOURCE_AND_CHILDREN, body);
 
       // Build multiget REPORT body from URLs in PROPFIND result
-      let contactElements = abook.responseXML.
-                            getElementsByTagNameNS("DAV:", "href");
+      let contactElements = abook.responseXML.getElementsByTagNameNS(
+                            "DAV:", "href");
 
       body = "<c:addressbook-multiget xmlns:d='DAV:' " +
              "xmlns:c='urn:ietf:params:xml:ns:carddav'>" +
              "<d:prop><d:getetag /> <c:address-data /></d:prop>\n";
 
       for (let element of contactElements) {
         let href = element.textContent;
         if (href.substr(-1) == "/") {
@@ -425,18 +425,18 @@ this.CardDavImporter.prototype = {
    * @param {String} body   Body to include in the WebDAV (HTTP) request
    *
    * @return {Object} Promise representing the request operation outcome.
    *                  If resolved, the resolution value is the XMLHttpRequest
    *                  that was used to perform the request.
    */
   _davPromise: function(method, url, auth, depth, body) {
     return new Promise((resolve, reject) => {
-      let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
-                createInstance(Ci.nsIXMLHttpRequest);
+      let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(
+                Ci.nsIXMLHttpRequest);
       let user = "";
       let password = "";
 
       if (auth.method == "basic") {
         user = auth.user;
         password = auth.password;
       }
 
--- a/browser/components/loop/modules/GoogleImporter.jsm
+++ b/browser/components/loop/modules/GoogleImporter.jsm
@@ -542,24 +542,28 @@ this.GoogleImporter.prototype = {
         } else if ("givenName" in contact) {
           contact.name = [contact.givenName[0]];
         } else if ("org" in contact) {
           contact.name = [contact.org[0]];
         } else {
           let email;
           try {
             email = getPreferred(contact);
-          } catch (ex) {}
+          } catch (ex) {
+            // Do nothing
+          }
           if (email) {
             contact.name = [email.value];
           } else {
             let tel;
             try {
               tel = getPreferred(contact, "tel");
-            } catch (ex) {}
+            } catch (ex) {
+              // Do nothing
+            }
             if (tel) {
               contact.name = [tel.value];
             }
           }
         }
       }
     }
 
--- a/browser/components/loop/modules/MozLoopPushHandler.jsm
+++ b/browser/components/loop/modules/MozLoopPushHandler.jsm
@@ -201,17 +201,19 @@ PushSocket.prototype = {
     // Do not pass through any callbacks after this point.
     this._onStart = function() {};
     this._onMsg = this._onStart;
     this._onClose = this._onStart;
 
     try {
       this._websocket.close(this._websocket.CLOSE_NORMAL);
     }
-    catch (e) {}
+    catch (e) {
+      // Do nothing
+    }
   }
 };
 
 
 /**
  * Create a RetryManager object. Class to handle retrying a UserAgent
  * to PushServer request following a retry back-off scheme managed by
  * this class. The current delay mechanism is to double the delay
@@ -778,23 +780,25 @@ let MozLoopPushHandler = {
       this._pushSocket = undefined;
       this._retryManager.retry(() => this._openSocket());
       return;
     };
 
     try {
       this.pushServerUri = Services.prefs.getCharPref("loop.debug.pushserver");
     }
-    catch (e) {}
+    catch (e) {
+      // Do nothing
+    }
 
     if (!this.pushServerUri) {
       // Get push server to use from the Loop server
       let pushUrlEndpoint = Services.prefs.getCharPref("loop.server") + "/push-server-config";
-      let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
-                  createInstance(Ci.nsIXMLHttpRequest);
+      let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(
+                Ci.nsIXMLHttpRequest);
       req.open("GET", pushUrlEndpoint);
       req.onload = () => {
         if (req.status >= 200 && req.status < 300) {
           let pushServerConfig;
           try {
             pushServerConfig = JSON.parse(req.responseText);
           } catch (e) {
             consoleLog.warn("PushHandler: Error parsing JSON response for push server URL");
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -494,34 +494,38 @@ describe("loop.panel", function() {
         navigator.mozLoop.getLoopPref = function() {
           return true;
         };
         var view = createTestPanelView();
 
         try {
           TestUtils.findRenderedComponentWithType(view, loop.panel.GettingStartedView);
           sinon.assert.fail("Should not find the GettingStartedView if it has been seen");
-        } catch (ex) {}
+        } catch (ex) {
+          // Do nothing
+        }
       });
 
       it("should render a SignInRequestView when mozLoop.hasEncryptionKey is false", function() {
         fakeMozLoop.hasEncryptionKey = false;
 
         var view = createTestPanelView();
 
         TestUtils.findRenderedComponentWithType(view, loop.panel.SignInRequestView);
       });
 
       it("should render a SignInRequestView when mozLoop.hasEncryptionKey is true", function() {
         var view = createTestPanelView();
 
         try {
           TestUtils.findRenderedComponentWithType(view, loop.panel.SignInRequestView);
           sinon.assert.fail("Should not find the GettingStartedView if it has been seen");
-        } catch (ex) {}
+        } catch (ex) {
+          // Do nothing
+        }
       });
     });
   });
 
   describe("loop.panel.RoomEntry", function() {
     var dispatcher, roomData;
 
     beforeEach(function() {
--- a/browser/components/loop/test/desktop-local/roomViews_test.js
+++ b/browser/components/loop/test/desktop-local/roomViews_test.js
@@ -254,18 +254,18 @@ describe("loop.roomViews", function () {
         expect(view.refs.menu.props.show).to.eql(true);
       });
     });
 
     describe("Edit Context", function() {
       it("should show the 'Add some context' link", function() {
         view = mountTestComponent();
 
-        expect(view.getDOMNode().querySelector(".room-invitation-addcontext")).
-          to.not.eql(null);
+        expect(view.getDOMNode().querySelector(
+          ".room-invitation-addcontext")).to.not.eql(null);
       });
 
       it("should call a callback when the link is clicked", function() {
         var onAddContextClick = sinon.stub();
         view = mountTestComponent({
           onAddContextClick: onAddContextClick
         });
 
--- a/browser/components/loop/test/mochitest/browser_loop_fxa_server.js
+++ b/browser/components/loop/test/mochitest/browser_loop_fxa_server.js
@@ -88,33 +88,33 @@ add_task(function* token_request_invalid
   is(request.response, null, "Check token response body");
 });
 
 
 // Helper methods
 
 function promiseParams() {
   return new Promise((resolve, reject) => {
-    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
-                createInstance(Ci.nsIXMLHttpRequest);
+    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(
+              Ci.nsIXMLHttpRequest);
     xhr.open("POST", BASE_URL + "/fxa-oauth/params", true);
     xhr.responseType = "json";
     xhr.addEventListener("load", () => {
       info("/fxa-oauth/params response:\n" + JSON.stringify(xhr.response, null, 4));
       resolve(xhr);
     });
     xhr.addEventListener("error", reject);
     xhr.send();
   });
 }
 
 function promiseToken(code, state) {
   return new Promise((resolve, reject) => {
-    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
-                createInstance(Ci.nsIXMLHttpRequest);
+    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(
+              Ci.nsIXMLHttpRequest);
     xhr.open("POST", BASE_URL + "/fxa-oauth/token", true);
     xhr.setRequestHeader("Authorization", "Hawk ...");
     xhr.responseType = "json";
     xhr.addEventListener("load", () => {
       info("/fxa-oauth/token response:\n" + JSON.stringify(xhr.response, null, 4));
       resolve(xhr);
     });
     xhr.addEventListener("error", reject);
--- a/browser/components/loop/test/mochitest/head.js
+++ b/browser/components/loop/test/mochitest/head.js
@@ -126,18 +126,18 @@ function loadLoopPanel(aOverrideOptions 
   loopPanel.setAttribute("animate", "false");
 
   // Now get the actual API loaded into gMozLoopAPI.
   return promiseGetMozLoopAPI();
 }
 
 function promiseOAuthParamsSetup(baseURL, params) {
   return new Promise((resolve, reject) => {
-    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
-                createInstance(Ci.nsIXMLHttpRequest);
+    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(
+              Ci.nsIXMLHttpRequest);
     xhr.open("POST", baseURL + "/setup_params", true);
     xhr.setRequestHeader("X-Params", JSON.stringify(params));
     xhr.addEventListener("load", () => resolve(xhr));
     xhr.addEventListener("error", error => reject(error));
     xhr.send();
   });
 }
 
@@ -168,18 +168,18 @@ function checkLoggedOutState() {
   checkFxAOAuthTokenData(null);
   const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
   is(Services.prefs.getPrefType(fxASessionPref), Services.prefs.PREF_INVALID,
      "FxA hawk session should be cleared anyways");
 }
 
 function promiseDeletedOAuthParams(baseURL) {
   return new Promise((resolve, reject) => {
-    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
-                createInstance(Ci.nsIXMLHttpRequest);
+    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(
+              Ci.nsIXMLHttpRequest);
     xhr.open("DELETE", baseURL + "/setup_params", true);
     xhr.addEventListener("load", () => resolve(xhr));
     xhr.addEventListener("error", reject);
     xhr.send();
   });
 }
 
 function promiseObserverNotified(aTopic, aExpectedData = null) {
@@ -192,18 +192,18 @@ function promiseObserverNotified(aTopic,
   });
 }
 
 /**
  * Get the last registration on the test server.
  */
 function promiseOAuthGetRegistration(baseURL) {
   return new Promise((resolve, reject) => {
-    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
-                createInstance(Ci.nsIXMLHttpRequest);
+    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(
+              Ci.nsIXMLHttpRequest);
     xhr.open("GET", baseURL + "/get_registration", true);
     xhr.responseType = "json";
     xhr.addEventListener("load", () => resolve(xhr));
     xhr.addEventListener("error", reject);
     xhr.send();
   });
 }
 
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -313,35 +313,34 @@ describe("loop.store.ActiveRoomStore", f
         roomOwner: "Alfred",
         roomUrl: "http://invalid"
       };
 
       store = new loop.store.ActiveRoomStore(dispatcher, {
         mozLoop: fakeMozLoop,
         sdkDriver: {}
       });
-      fakeMozLoop.rooms.get.
-        withArgs(fakeToken).
-        callsArgOnWith(1, // index of callback argument
+      fakeMozLoop.rooms.get.withArgs(fakeToken).callsArgOnWith(
+        1, // index of callback argument
         store, // |this| to call it on
         null, // args to call the callback with...
         fakeRoomData
       );
     });
 
     it("should set the state to `GATHER`",
       function() {
         store.setupWindowData(new sharedActions.SetupWindowData({
           windowId: "42",
           type: "room",
           roomToken: fakeToken
         }));
 
-        expect(store.getStoreState()).
-          to.have.property("roomState", ROOM_STATES.GATHER);
+        expect(store.getStoreState()).to.have.property(
+          "roomState", ROOM_STATES.GATHER);
       });
 
     it("should dispatch an SetupRoomInfo action if the get is successful",
       function() {
         store.setupWindowData(new sharedActions.SetupWindowData({
           windowId: "42",
           type: "room",
           roomToken: fakeToken
@@ -372,19 +371,18 @@ describe("loop.store.ActiveRoomStore", f
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.JoinRoom());
       });
 
     it("should dispatch a RoomFailure action if the get fails",
       function() {
 
         var fakeError = new Error("fake error");
-        fakeMozLoop.rooms.get.
-          withArgs(fakeToken).
-          callsArgOnWith(1, // index of callback argument
+        fakeMozLoop.rooms.get.withArgs(fakeToken).callsArgOnWith(
+          1, // index of callback argument
           store, // |this| to call it on
           fakeError); // args to call the callback with...
 
         store.setupWindowData(new sharedActions.SetupWindowData({
           windowId: "42",
           type: "room",
           roomToken: fakeToken
         }));
--- a/browser/components/loop/test/shared/otSdkDriver_test.js
+++ b/browser/components/loop/test/shared/otSdkDriver_test.js
@@ -333,18 +333,18 @@ describe("loop.OTSdkDriver", function ()
       sinon.assert.calledWith(session.connect, "1234567890", "1357924680");
     });
 
     it("should set the two-way media start time to 'uninitialized' " +
        "when sessionData.sendTwoWayMediaTelemetry is true'", function() {
       driver.connectSession(_.extend(sessionData,
                                      {sendTwoWayMediaTelemetry: true}));
 
-      expect(driver._getTwoWayMediaStartTime()).to.
-        eql(driver.CONNECTION_START_TIME_UNINITIALIZED);
+      expect(driver._getTwoWayMediaStartTime()).to.eql(
+        driver.CONNECTION_START_TIME_UNINITIALIZED);
     });
 
     describe("On connection complete", function() {
       beforeEach(function() {
         sandbox.stub(window.console, "error");
       });
 
       it("should publish the stream if the publisher is ready", function() {
@@ -451,35 +451,35 @@ describe("loop.OTSdkDriver", function ()
       var startTime = 1;
       driver._sendTwoWayMediaTelemetry = true;
       driver._setTwoWayMediaStartTime(startTime);
       sandbox.stub(performance, "now");
       sandbox.stub(driver, "_noteConnectionLengthIfNeeded");
 
       driver.disconnectSession();
 
-      expect(driver._getTwoWayMediaStartTime()).to.
-        eql(driver.CONNECTION_START_TIME_UNINITIALIZED);
+      expect(driver._getTwoWayMediaStartTime()).to.eql(
+        driver.CONNECTION_START_TIME_UNINITIALIZED);
     });
   });
 
   describe("#_noteConnectionLengthIfNeeded", function() {
     var startTimeMS;
     beforeEach(function() {
       startTimeMS = 1;
       driver._sendTwoWayMediaTelemetry = true;
       driver._setTwoWayMediaStartTime(startTimeMS);
     });
 
     it("should set two-way media start time to CONNECTION_START_TIME_ALREADY_NOTED", function() {
       var endTimeMS = 3;
       driver._noteConnectionLengthIfNeeded(startTimeMS, endTimeMS);
 
-      expect(driver._getTwoWayMediaStartTime()).to.
-        eql(driver.CONNECTION_START_TIME_ALREADY_NOTED);
+      expect(driver._getTwoWayMediaStartTime()).to.eql(
+        driver.CONNECTION_START_TIME_ALREADY_NOTED);
     });
 
     it("should call mozLoop.noteConnectionLength with SHORTER_THAN_10S for calls less than 10s", function() {
       var endTimeMS = 9000;
 
       driver._noteConnectionLengthIfNeeded(startTimeMS, endTimeMS);
 
       sinon.assert.calledOnce(mozLoop.telemetryAddValue);
--- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js
+++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js
@@ -553,18 +553,18 @@ describe("loop.standaloneRoomViews", fun
         it("should render a local avatar if the room HAS_PARTICIPANTS and" +
           " .videoMuted is true",
           function() {
             activeRoomStore.setStoreState({
               roomState: ROOM_STATES.HAS_PARTICIPANTS,
               videoMuted: true
             });
 
-            expect(view.getDOMNode().querySelector(".local .avatar")).
-              not.eql(null);
+            expect(view.getDOMNode().querySelector(".local .avatar")).not.eql(
+              null);
           });
       });
 
       describe("Marketplace hidden iframe", function() {
 
         it("should set src when the store state change",
            function(done) {
 
--- a/browser/components/loop/test/xpcshell/test_loopservice_token_save.js
+++ b/browser/components/loop/test/xpcshell/test_loopservice_token_save.js
@@ -18,16 +18,17 @@ add_test(function test_registration_retu
     response.finish();
   });
 
   MozLoopService.promiseRegisteredWithServers().then(() => {
     var hawkSessionPref;
     try {
       hawkSessionPref = Services.prefs.getCharPref("loop.hawk-session-token");
     } catch (ex) {
+      // Do nothing
     }
     Assert.equal(hawkSessionPref, fakeSessionToken, "Should store" +
       " Hawk-Session-Token header contents in loop.hawk-session-token pref");
 
     run_next_test();
   }, err => {
     do_throw("shouldn't error on a successful request");
   });
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -486,18 +486,25 @@ BrowserGlue.prototype = {
           }
         }
         break;
 #endif
       case "flash-plugin-hang":
         this._handleFlashHang();
         break;
       case "xpi-signature-changed":
-        if (JSON.parse(data).disabled.length)
-          this._notifyUnsignedAddonsDisabled();
+        let disabledAddons = JSON.parse(data).disabled;
+        AddonManager.getAddonsByIDs(disabledAddons, (addons) => {
+          for (let addon of addons) {
+            if (addon.type != "experiment") {
+              this._notifyUnsignedAddonsDisabled();
+              break;
+            }
+          }
+        });
         break;
       case "autocomplete-did-enter-text":
         this._handleURLBarTelemetry(subject.QueryInterface(Ci.nsIAutoCompleteInput));
         break;
     }
   },
 
   _handleURLBarTelemetry(input) {
@@ -1000,17 +1007,17 @@ BrowserGlue.prototype = {
     // parent only: configure default prefs, set up pref observers, register
     // pdf content handler, and initializes parent side message manager
     // shim for privileged api access.
     PdfJs.init(true);
     // child only: similar to the call above for parent - register content
     // handler and init message manager child shim for privileged api access.
     // With older versions of the extension installed, this load will fail
     // passively.
-    aWindow.messageManager.loadFrameScript("resource://pdf.js/pdfjschildbootstrap.js", true);
+    Services.ppmm.loadProcessScript("resource://pdf.js/pdfjschildbootstrap.js", true);
 
 #ifdef NIGHTLY_BUILD
     // Registering Shumway bootstrap script the child processes.
     aWindow.messageManager.loadFrameScript("chrome://shumway/content/bootstrap-content.js", true);
     // Initializing Shumway (shall be run after child script registration).
     ShumwayUtils.init();
 #endif
 
@@ -1053,26 +1060,16 @@ BrowserGlue.prototype = {
     } catch(e) {}
     if (!disableResetPrompt && lastUse &&
         Date.now() - lastUse >= OFFER_PROFILE_RESET_INTERVAL_MS) {
       this._resetUnusedProfileNotification();
     }
 
     this._checkForOldBuildUpdates();
 
-    let disabledAddons = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_DISABLED);
-    AddonManager.getAddonsByIDs(disabledAddons, (addons) => {
-      for (let addon of addons) {
-        if (addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
-          this._notifyUnsignedAddonsDisabled();
-          break;
-        }
-      }
-    });
-
     this._firstWindowTelemetry(aWindow);
   },
 
   /**
    * Application shutdown handler.
    */
   _onQuitApplicationGranted: function () {
     // This pref must be set here because SessionStore will use its value
@@ -1162,16 +1159,29 @@ BrowserGlue.prototype = {
           if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE))
             return;
 
           win.openUILinkIn("about:newaddon?id=" + aAddon.id, "tab");
         })
       });
     }
 
+    let disabledAddons = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_DISABLED);
+    AddonManager.getAddonsByIDs(disabledAddons, (addons) => {
+      for (let addon of addons) {
+        if (addon.type == "experiment")
+          continue;
+
+        if (addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
+          this._notifyUnsignedAddonsDisabled();
+          break;
+        }
+      }
+    });
+
     // Perform default browser checking.
     if (ShellService) {
 #ifdef DEBUG
       let shouldCheck = false;
 #else
       let shouldCheck = ShellService.shouldCheckDefaultBrowser;
 #endif
       let willRecoverSession = false;
--- a/browser/components/translation/Translation.jsm
+++ b/browser/components/translation/Translation.jsm
@@ -105,17 +105,17 @@ this.Translation = {
     return this.supportedEngines.keys[0];
   },
 
   /**
    * Returns the name of the preferred translation engine.
    */
   get translationEngine() {
     let engine = Services.prefs.getCharPref("browser.translation.engine");
-    return Object.keys(this.supportedEngines).includes(engine) ? engine : this.defaultEngine;
+    return Object.keys(this.supportedEngines).indexOf(engine) == -1 ? this.defaultEngine : engine;
   },
 };
 
 /* TranslationUI objects keep the information related to translation for
  * a specific browser.  This object is passed to the translation
  * infobar so that it can initialize itself.  The properties exposed to
  * the infobar are:
  * - detectedLanguage, code of the language detected on the web page.
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -93,21 +93,30 @@ this.UITour = {
   availableTargetsCache: new WeakMap(),
 
   _annotationPanelMutationObservers: new WeakMap(),
 
   highlightEffects: ["random", "wobble", "zoom", "color"],
   targets: new Map([
     ["accountStatus", {
       query: (aDocument) => {
+        // If the user is logged in, use the avatar element.
+        let fxAFooter = aDocument.getElementById("PanelUI-footer-fxa");
+        if (fxAFooter.getAttribute("fxastatus")) {
+          return aDocument.getElementById("PanelUI-fxa-avatar");
+        }
+
+        // Otherwise use the sync setup icon.
         let statusButton = aDocument.getElementById("PanelUI-fxa-label");
         return aDocument.getAnonymousElementByAttribute(statusButton,
                                                         "class",
                                                         "toolbarbutton-icon");
       },
+      // This is a fake widgetName starting with the "PanelUI-" prefix so we know
+      // to automatically open the appMenu when annotating this target.
       widgetName: "PanelUI-fxa-label",
     }],
     ["addons",      {query: "#add-ons-button"}],
     ["appMenu",     {
       addTargetListener: (aDocument, aCallback) => {
         let panelPopup = aDocument.getElementById("PanelUI-popup");
         panelPopup.addEventListener("popupshown", aCallback);
       },
--- a/browser/components/uitour/test/browser.ini
+++ b/browser/components/uitour/test/browser.ini
@@ -2,16 +2,18 @@
 support-files =
   head.js
   image.png
   uitour.html
   ../UITour-lib.js
 
 [browser_backgroundTab.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
+[browser_fxa.js]
+skip-if = e10s || debug || asan # Bug 1073247 - UITour tests not e10s friendly # updateAppMenuItem leaks
 [browser_no_tabs.js]
 [browser_openPreferences.js]
 skip-if = e10s # Bug 1073247 - UITour tests not e10s friendly
 [browser_openSearchPanel.js]
 skip-if = true # Bug 1113038 - Intermittent "Popup was opened"
 [browser_trackingProtection.js]
 skip-if = os == "linux" # Intermittent NS_ERROR_NOT_AVAILABLE [nsIUrlClassifierDBService.beginUpdate]
 tag = trackingprotection
new file mode 100644
--- /dev/null
+++ b/browser/components/uitour/test/browser_fxa.js
@@ -0,0 +1,68 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
+                                  "resource://gre/modules/FxAccounts.jsm");
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+
+function test() {
+  UITourTest();
+}
+
+registerCleanupFunction(function*() {
+  yield signOut();
+  gFxAccounts.updateAppMenuItem();
+});
+
+let tests = [
+  taskify(function* test_highlight_accountStatus_loggedOut() {
+    let userData = yield fxAccounts.getSignedInUser();
+    is(userData, null, "Not logged in initially");
+    yield showMenuPromise("appMenu");
+    yield showHighlightPromise("accountStatus");
+    let highlight = document.getElementById("UITourHighlightContainer");
+    is(highlight.getAttribute("targetName"), "accountStatus", "Correct highlight target");
+  }),
+
+  taskify(function* test_highlight_accountStatus_loggedIn() {
+    yield setSignedInUser();
+    let userData = yield fxAccounts.getSignedInUser();
+    isnot(userData, null, "Logged in now");
+    gFxAccounts.updateAppMenuItem(); // Causes a leak
+    yield showMenuPromise("appMenu");
+    yield showHighlightPromise("accountStatus");
+    let highlight = document.getElementById("UITourHighlightContainer");
+    is(highlight.popupBoxObject.anchorNode.id, "PanelUI-fxa-avatar", "Anchored on avatar");
+    is(highlight.getAttribute("targetName"), "accountStatus", "Correct highlight target");
+  }),
+];
+
+// Helpers copied from browser_aboutAccounts.js
+// watch out - these will fire observers which if you aren't careful, may
+// interfere with the tests.
+function setSignedInUser(data) {
+  if (!data) {
+    data = {
+      email: "foo@example.com",
+      uid: "1234@lcip.org",
+      assertion: "foobar",
+      sessionToken: "dead",
+      kA: "beef",
+      kB: "cafe",
+      verified: true
+    };
+  }
+ return fxAccounts.setSignedInUser(data);
+}
+
+function signOut() {
+  // we always want a "localOnly" signout here...
+  return fxAccounts.signOut(true);
+}
--- a/browser/components/uitour/test/head.js
+++ b/browser/components/uitour/test/head.js
@@ -134,16 +134,22 @@ function hideInfoPromise(...args) {
 }
 
 function showInfoPromise(...args) {
   let popup = document.getElementById("UITourTooltip");
   gContentAPI.showInfo.apply(gContentAPI, args);
   return promisePanelElementShown(window, popup);
 }
 
+function showHighlightPromise(...args) {
+  let popup = document.getElementById("UITourHighlightContainer");
+  gContentAPI.showHighlight.apply(gContentAPI, args);
+  return promisePanelElementShown(window, popup);
+}
+
 function showMenuPromise(name) {
   return new Promise(resolve => {
     gContentAPI.showMenu(name, () => resolve());
   });
 }
 
 function waitForCallbackResultPromise() {
   return waitForConditionPromise(() => {
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -13,16 +13,17 @@ const DEFAULT_MAX_CHILDREN = 100;
 const COLLAPSE_ATTRIBUTE_LENGTH = 120;
 const COLLAPSE_DATA_URL_REGEX = /^data.+base64/;
 const COLLAPSE_DATA_URL_LENGTH = 60;
 const NEW_SELECTION_HIGHLIGHTER_TIMER = 1000;
 const GRAB_DELAY = 400;
 const DRAG_DROP_AUTOSCROLL_EDGE_DISTANCE = 50;
 const DRAG_DROP_MIN_AUTOSCROLL_SPEED = 5;
 const DRAG_DROP_MAX_AUTOSCROLL_SPEED = 15;
+const AUTOCOMPLETE_POPUP_PANEL_ID = "markupview_autoCompletePopup";
 
 const {UndoStack} = require("devtools/shared/undo");
 const {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
 const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 const {HTMLEditor} = require("devtools/markupview/html-editor");
 const promise = require("resource://gre/modules/Promise.jsm").Promise;
 const {Tooltip} = require("devtools/shared/widgets/Tooltip");
 const EventEmitter = require("devtools/toolkit/event-emitter");
@@ -80,17 +81,20 @@ function MarkupView(aInspector, aFrame, 
     this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize");
   } catch(ex) {
     this.maxChildren = DEFAULT_MAX_CHILDREN;
   }
 
   // Creating the popup to be used to show CSS suggestions.
   let options = {
     autoSelect: true,
-    theme: "auto"
+    theme: "auto",
+    // panelId option prevents the markupView autocomplete popup from
+    // sharing XUL elements with other views, such as ruleView (see Bug 1191093)
+    panelId: AUTOCOMPLETE_POPUP_PANEL_ID
   };
   this.popup = new AutocompletePopup(this.doc.defaultView.parent.document, options);
 
   this.undo = new UndoStack();
   this.undo.installController(aControllerWindow);
 
   this._containers = new Map();
 
--- a/browser/devtools/styleinspector/test/browser_ruleview_completion-existing-property_01.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_completion-existing-property_01.js
@@ -56,28 +56,37 @@ let testData = [
 
 let TEST_URL = "data:text/html;charset=utf-8,<h1 style='font: 24px serif'>Filename" +
                ": browser_bug893965_css_property_completion_existing_property.js</h1>";
 
 add_task(function*() {
   yield addTab(TEST_URL);
   let {toolbox, inspector, view} = yield openRuleView();
 
+  info("Test autocompletion after 1st page load");
+  yield runAutocompletionTest(toolbox, inspector, view);
+
+  info("Test autocompletion after page navigation");
+  yield reloadPage(inspector);
+  yield runAutocompletionTest(toolbox, inspector, view);
+});
+
+function* runAutocompletionTest(toolbox, inspector, view) {
   info("Selecting the test node");
   yield selectNode("h1", inspector);
 
   info("Focusing the css property editable field");
   let propertyName = view.styleDocument.querySelectorAll(".ruleview-propertyname")[0];
   let editor = yield focusEditableField(view, propertyName);
 
   info("Starting to test for css property completion");
-  for (let i = 0; i < testData.length; i ++) {
+  for (let i = 0; i < testData.length; i++) {
     yield testCompletion(testData[i], editor, view);
   }
-});
+}
 
 function* testCompletion([key, completion, index, total], editor, view) {
   info("Pressing key " + key);
   info("Expecting " + completion + ", " + index + ", " + total);
 
   let onSuggest;
 
   if (/(left|right|back_space|escape|home|end|page_up|page_down)/ig.test(key)) {
--- a/browser/devtools/styleinspector/test/browser_ruleview_completion-existing-property_02.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_completion-existing-property_02.js
@@ -38,31 +38,40 @@ let testData = [
 
 let TEST_URL = "data:text/html;charset=utf-8,<h1 style='color: red'>Filename: " +
                "browser_bug894376_css_value_completion_existing_property_value_pair.js</h1>";
 
 add_task(function*() {
   yield addTab(TEST_URL);
   let {toolbox, inspector, view} = yield openRuleView();
 
+  info("Test autocompletion after 1st page load");
+  yield runAutocompletionTest(toolbox, inspector, view);
+
+  info("Test autocompletion after page navigation");
+  yield reloadPage(inspector);
+  yield runAutocompletionTest(toolbox, inspector, view);
+});
+
+function* runAutocompletionTest(toolbox, inspector, view) {
   info("Selecting the test node");
   yield selectNode("h1", inspector);
 
   info("Focusing the css property editable value");
   let value = view.styleDocument.querySelectorAll(".ruleview-propertyvalue")[0];
   let editor = yield focusEditableField(view, value);
 
   info("Starting to test for css property completion");
-  for (let i = 0; i < testData.length; i ++) {
+  for (let i = 0; i < testData.length; i++) {
     // Re-define the editor at each iteration, because the focus may have moved
     // from property to value and back
     editor = inplaceEditor(view.styleDocument.activeElement);
     yield testCompletion(testData[i], editor, view);
   }
-});
+}
 
 function* testCompletion([key, modifiers, completion, index, total], editor, view) {
   info("Pressing key " + key);
   info("Expecting " + completion + ", " + index + ", " + total);
 
   let onKeyPress;
 
   if (/tab/ig.test(key)) {
--- a/browser/devtools/styleinspector/test/browser_ruleview_completion-new-property_01.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_completion-new-property_01.js
@@ -39,28 +39,37 @@ let testData = [
 
 let TEST_URL = "data:text/html;charset=utf-8,<h1 style='border: 1px solid red'>Filename:" +
                "browser_bug893965_css_property_completion_new_property.js</h1>";
 
 add_task(function*() {
   yield addTab(TEST_URL);
   let {toolbox, inspector, view} = yield openRuleView();
 
+  info("Test autocompletion after 1st page load");
+  yield runAutocompletionTest(toolbox, inspector, view);
+
+  info("Test autocompletion after page navigation");
+  yield reloadPage(inspector);
+  yield runAutocompletionTest(toolbox, inspector, view);
+});
+
+function* runAutocompletionTest(toolbox, inspector, view) {
   info("Selecting the test node");
   yield selectNode("h1", inspector);
 
   info("Focusing the css property editable field");
   let brace = view.styleDocument.querySelector(".ruleview-ruleclose");
   let editor = yield focusEditableField(view, brace);
 
   info("Starting to test for css property completion");
-  for (let i = 0; i < testData.length; i ++) {
+  for (let i = 0; i < testData.length; i++) {
     yield testCompletion(testData[i], editor, view);
   }
-});
+}
 
 function* testCompletion([key, completion, index, total], editor, view) {
   info("Pressing key " + key);
   info("Expecting " + completion + ", " + index + ", " + total);
 
   let onSuggest;
 
   if (/(right|back_space|escape)/ig.test(key)) {
--- a/browser/devtools/styleinspector/test/browser_ruleview_completion-new-property_02.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_completion-new-property_02.js
@@ -41,31 +41,40 @@ let testData = [
 
 let TEST_URL = "data:text/html;charset=utf-8,<style>h1{border: 1px solid red}</style>" +
   "<h1>Test element</h1>";
 
 add_task(function*() {
   yield addTab(TEST_URL);
   let {toolbox, inspector, view} = yield openRuleView();
 
+  info("Test autocompletion after 1st page load");
+  yield runAutocompletionTest(toolbox, inspector, view);
+
+  info("Test autocompletion after page navigation");
+  yield reloadPage(inspector);
+  yield runAutocompletionTest(toolbox, inspector, view);
+});
+
+function* runAutocompletionTest(toolbox, inspector, view) {
   info("Selecting the test node");
   yield selectNode("h1", inspector);
 
   info("Focusing a new css property editable property");
   let brace = view.styleDocument.querySelectorAll(".ruleview-ruleclose")[1];
   let editor = yield focusEditableField(view, brace);
 
   info("Starting to test for css property completion");
   for (let i = 0; i < testData.length; i ++) {
     // Re-define the editor at each iteration, because the focus may have moved
     // from property to value and back
     editor = inplaceEditor(view.styleDocument.activeElement);
     yield testCompletion(testData[i], editor, view);
   }
-});
+}
 
 function* testCompletion([key, modifiers, completion, index, total], editor, view) {
   info("Pressing key " + key);
   info("Expecting " + completion + ", " + index + ", " + total);
 
   let onKeyPress;
 
   if (/tab/ig.test(key)) {
--- a/browser/devtools/styleinspector/test/browser_ruleview_user-property-reset.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_user-property-reset.js
@@ -77,14 +77,8 @@ function* getContainerStyleAttrValue(id,
 
 function* assertRuleAndMarkupViewWidth(id, value, ruleView, inspector) {
   let valueSpan = getStyleRule(ruleView).querySelector(".ruleview-propertyvalue");
   is(valueSpan.textContent, value, "Rule-view style width is " + value + " as expected");
 
   let attr = yield getContainerStyleAttrValue(id, inspector);
   is(attr.textContent.replace(/\s/g, ""), "width:" + value + ";", "Markup-view style attribute width is " + value);
 }
-
-function reloadPage(inspector) {
-  let onNewRoot = inspector.once("new-root");
-  content.location.reload();
-  return onNewRoot.then(inspector.markup._waitForChildren);
-}
--- a/browser/devtools/styleinspector/test/head.js
+++ b/browser/devtools/styleinspector/test/head.js
@@ -994,8 +994,21 @@ function waitForStyleEditor(toolbox, hre
     if (!gotEditor("styleeditor-selected", panel.UI.selectedEditor)) {
       // The expected editor is not selected (yet). Wait for it.
       panel.UI.on("editor-selected", gotEditor);
     }
   });
 
   return def.promise;
 }
+
+/**
+ * Reload the current page and wait for the inspector to be initialized after
+ * the navigation
+ * @param {InspectorPanel} inspector
+ *        The instance of InspectorPanel currently loaded in the toolbox
+ * @return a promise that resolves after page reload and inspector initialization
+ */
+function reloadPage(inspector) {
+  let onNewRoot = inspector.once("new-root");
+  content.location.reload();
+  return onNewRoot.then(inspector.markup._waitForChildren);
+}
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -58,18 +58,16 @@ support-files =
   test-bug-762593-insecure-passwords-web-console-warning.html
   test-bug-766001-console-log.js
   test-bug-766001-js-console-links.html
   test-bug-766001-js-errors.js
   test-bug-782653-css-errors-1.css
   test-bug-782653-css-errors-2.css
   test-bug-782653-css-errors.html
   test-bug-837351-security-errors.html
-  test-bug-846918-hsts-invalid-headers.html
-  test-bug-846918-hsts-invalid-headers.html^headers^
   test-bug-859170-longstring-hang.html
   test-bug-869003-iframe.html
   test-bug-869003-top-window.html
   test-closure-optimized-out.html
   test-closures.html
   test-console-assert.html
   test-console-count.html
   test-console-count-external-file.js
@@ -91,16 +89,18 @@ support-files =
   test-data.json^headers^
   test-duplicate-error.html
   test-encoding-ISO-8859-1.html
   test-error.html
   test-eval-in-stackframe.html
   test-file-location.js
   test-filter.html
   test-for-of.html
+  test_hpkp-invalid-headers.sjs
+  test_hsts-invalid-headers.sjs
   test-iframe-762593-insecure-form-action.html
   test-iframe-762593-insecure-frame.html
   test-iframe1.html
   test-iframe2.html
   test-iframe3.html
   test-image.png
   test-mixedcontent-securityerrors.html
   test-mutation.html
@@ -297,18 +297,16 @@ skip-if = buildapp == 'mulet' || e10s # 
 [browser_webconsole_bug_770099_violation.js]
 [browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js]
 skip-if = buildapp == 'mulet'
 [browser_webconsole_bug_804845_ctrl_key_nav.js]
 skip-if = os != "mac"
 [browser_webconsole_bug_817834_add_edited_input_to_history.js]
 [browser_webconsole_bug_837351_securityerrors.js]
 skip-if = buildapp == 'mulet'
-[browser_webconsole_bug_846918_hsts_invalid-headers.js]
-skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
 [browser_webconsole_bug_915141_toggle_response_logging_with_keyboard.js]
 [browser_webconsole_filter_buttons_contextmenu.js]
 [browser_webconsole_bug_1006027_message_timestamps_incorrect.js]
 skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug intermittent)
 [browser_webconsole_bug_1010953_cspro.js]
 [browser_webconsole_certificate_messages.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_webconsole_show_subresource_security_errors.js]
@@ -323,16 +321,19 @@ skip-if = e10s # Bug 1042253 - webconsol
 [browser_webconsole_console_extras.js]
 [browser_webconsole_console_logging_api.js]
 [browser_webconsole_console_logging_workers_api.js]
 [browser_webconsole_count.js]
 [browser_webconsole_dont_navigate_on_doubleclick.js]
 [browser_webconsole_execution_scope.js]
 [browser_webconsole_for_of.js]
 [browser_webconsole_history.js]
+[browser_webconsole_hpkp_invalid-headers.js]
+[browser_webconsole_hsts_invalid-headers.js]
+skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
 [browser_webconsole_input_field_focus_on_panel_select.js]
 [browser_webconsole_inspect-parsed-documents.js]
 [browser_webconsole_js_input_expansion.js]
 [browser_webconsole_jsterm.js]
 skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
 [browser_webconsole_live_filtering_of_message_types.js]
 [browser_webconsole_live_filtering_on_search_strings.js]
 [browser_webconsole_message_node_id.js]
rename from browser/devtools/webconsole/test/browser_webconsole_bug_846918_hsts_invalid-headers.js
rename to browser/devtools/webconsole/test/browser_webconsole_hpkp_invalid-headers.js
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_846918_hsts_invalid-headers.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_hpkp_invalid-headers.js
@@ -1,41 +1,100 @@
  /* Any copyright is dedicated to the Public Domain.
   * http://creativecommons.org/publicdomain/zero/1.0/ */
-/* Tests that errors about invalid HSTS security headers are logged
+/* Tests that errors about invalid HPKP security headers are logged
  *  to the web console */
 
 "use strict";
 
-const TEST_URI = "https://example.com/browser/browser/devtools/webconsole/" +
-                 "test/test-bug-846918-hsts-invalid-headers.html";
-const HSTS_INVALID_HEADER_MSG = "The site specified an invalid " +
-                                "Strict-Transport-Security header.";
-const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Security/" +
-                       "HTTP_Strict_Transport_Security";
+const TEST_URI = "data:text/html;charset=utf-8,Web Console HPKP invalid " +
+                 "header test";
+const SJS_URL = "https://example.com/browser/browser/devtools/webconsole/" +
+                "test/test_hpkp-invalid-headers.sjs";
+const NON_BUILTIN_ROOT_PREF = "security.cert_pinning.process_headers_from_" +
+                              "non_builtin_roots";
 
 let test = asyncTest(function* () {
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref(NON_BUILTIN_ROOT_PREF);
+  });
+  // The root used for mochitests is not built-in, so set the relevant pref to
+  // true to force all pinning error messages to appear.
+  Services.prefs.setBoolPref(NON_BUILTIN_ROOT_PREF, true);
+
   yield loadTab(TEST_URI);
 
   let hud = yield openConsole();
 
-  let results = yield waitForMessages({
+  yield* checkForMessage({
+    url: SJS_URL + "?badSyntax",
+    name: "Could not parse header error displayed successfully",
+    text: "Public-Key-Pins: The site specified a header that could not be " +
+          "parsed successfully."
+  }, hud);
+
+  yield* checkForMessage({
+    url: SJS_URL + "?noMaxAge",
+    name: "No max-age error displayed successfully",
+    text: "Public-Key-Pins: The site specified a header that did not include " +
+          "a 'max-age' directive."
+  }, hud);
+
+  yield* checkForMessage({
+    url: SJS_URL + "?invalidIncludeSubDomains",
+    name: "Invalid includeSubDomains error displayed successfully",
+    text: "Public-Key-Pins: The site specified a header that included an " +
+          "invalid 'includeSubDomains' directive."
+  }, hud);
+
+  yield* checkForMessage({
+    url: SJS_URL + "?invalidMaxAge",
+    name: "Invalid max-age error displayed successfully",
+    text: "Public-Key-Pins: The site specified a header that included an " +
+          "invalid 'max-age' directive."
+  }, hud);
+
+  yield* checkForMessage({
+    url: SJS_URL + "?multipleIncludeSubDomains",
+    name: "Multiple includeSubDomains error displayed successfully",
+    text: "Public-Key-Pins: The site specified a header that included " +
+          "multiple 'includeSubDomains' directives."
+  }, hud);
+
+  yield* checkForMessage({
+    url: SJS_URL + "?multipleMaxAge",
+    name: "Multiple max-age error displayed successfully",
+    text: "Public-Key-Pins: The site specified a header that included " +
+          "multiple 'max-age' directives."
+  }, hud);
+
+  yield* checkForMessage({
+    url: SJS_URL + "?multipleReportURIs",
+    name: "Multiple report-uri error displayed successfully",
+    text: "Public-Key-Pins: The site specified a header that included " +
+          "multiple 'report-uri' directives."
+  }, hud);
+
+  yield* checkForMessage({
+    url: SJS_URL + "?pinsetDoesNotMatch",
+    name: "Non-matching pinset error displayed successfully",
+    text: "Public-Key-Pins: The site specified a header that did not include " +
+          "a matching pin."
+  }, hud);
+});
+
+function* checkForMessage(curTest, hud) {
+  hud.jsterm.clearOutput();
+
+  content.location = curTest.url;
+
+  yield waitForMessages({
     webconsole: hud,
     messages: [
       {
-        name: "Invalid HSTS header error displayed successfully",
-        text: HSTS_INVALID_HEADER_MSG,
+        name: curTest.name,
+        text: curTest.text,
         category: CATEGORY_SECURITY,
         severity: SEVERITY_WARNING,
-        objects: true,
       },
     ],
   });
-
-  yield testClickOpenNewTab(hud, results);
-});
-
-function testClickOpenNewTab(hud, results) {
-  let warningNode = results[0].clickableElements[0];
-  ok(warningNode, "link element");
-  ok(warningNode.classList.contains("learn-more-link"), "link class name");
-  return simulateMessageLinkClick(warningNode, LEARN_MORE_URI);
 }
copy from browser/devtools/webconsole/test/browser_webconsole_bug_846918_hsts_invalid-headers.js
copy to browser/devtools/webconsole/test/browser_webconsole_hsts_invalid-headers.js
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_846918_hsts_invalid-headers.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_hsts_invalid-headers.js
@@ -1,41 +1,89 @@
  /* Any copyright is dedicated to the Public Domain.
   * http://creativecommons.org/publicdomain/zero/1.0/ */
 /* Tests that errors about invalid HSTS security headers are logged
  *  to the web console */
 
 "use strict";
 
-const TEST_URI = "https://example.com/browser/browser/devtools/webconsole/" +
-                 "test/test-bug-846918-hsts-invalid-headers.html";
-const HSTS_INVALID_HEADER_MSG = "The site specified an invalid " +
-                                "Strict-Transport-Security header.";
+const TEST_URI = "data:text/html;charset=utf-8,Web Console HSTS invalid " +
+                 "header test";
+const SJS_URL = "https://example.com/browser/browser/devtools/webconsole/" +
+                "test/test_hsts-invalid-headers.sjs";
 const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Security/" +
                        "HTTP_Strict_Transport_Security";
 
 let test = asyncTest(function* () {
   yield loadTab(TEST_URI);
 
   let hud = yield openConsole();
 
+  yield* checkForMessage({
+    url: SJS_URL + "?badSyntax",
+    name: "Could not parse header error displayed successfully",
+    text: "Strict-Transport-Security: The site specified a header that could " +
+          "not be parsed successfully."
+  }, hud);
+
+  yield* checkForMessage({
+    url: SJS_URL + "?noMaxAge",
+    name: "No max-age error displayed successfully",
+    text: "Strict-Transport-Security: The site specified a header that did " +
+          "not include a 'max-age' directive."
+  }, hud);
+
+  yield* checkForMessage({
+    url: SJS_URL + "?invalidIncludeSubDomains",
+    name: "Invalid includeSubDomains error displayed successfully",
+    text: "Strict-Transport-Security: The site specified a header that " +
+          "included an invalid 'includeSubDomains' directive."
+  }, hud);
+
+  yield* checkForMessage({
+    url: SJS_URL + "?invalidMaxAge",
+    name: "Invalid max-age error displayed successfully",
+    text: "Strict-Transport-Security: The site specified a header that " +
+          "included an invalid 'max-age' directive."
+  }, hud);
+
+  yield* checkForMessage({
+    url: SJS_URL + "?multipleIncludeSubDomains",
+    name: "Multiple includeSubDomains error displayed successfully",
+    text: "Strict-Transport-Security: The site specified a header that " +
+          "included multiple 'includeSubDomains' directives."
+  }, hud);
+
+  yield* checkForMessage({
+    url: SJS_URL + "?multipleMaxAge",
+    name: "Multiple max-age error displayed successfully",
+    text: "Strict-Transport-Security: The site specified a header that " +
+          "included multiple 'max-age' directives."
+  }, hud);
+});
+
+function* checkForMessage(curTest, hud) {
+  hud.jsterm.clearOutput();
+
+  content.location = curTest.url;
+
   let results = yield waitForMessages({
     webconsole: hud,
     messages: [
       {
-        name: "Invalid HSTS header error displayed successfully",
-        text: HSTS_INVALID_HEADER_MSG,
+        name: curTest.name,
+        text: curTest.text,
         category: CATEGORY_SECURITY,
         severity: SEVERITY_WARNING,
         objects: true,
       },
     ],
   });
 
   yield testClickOpenNewTab(hud, results);
-});
+}
 
 function testClickOpenNewTab(hud, results) {
   let warningNode = results[0].clickableElements[0];
   ok(warningNode, "link element");
   ok(warningNode.classList.contains("learn-more-link"), "link class name");
   return simulateMessageLinkClick(warningNode, LEARN_MORE_URI);
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_show_subresource_security_errors.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_show_subresource_security_errors.js
@@ -3,21 +3,21 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Ensure non-toplevel security errors are displayed
 
 "use strict";
 
-const TEST_URI = "data:text/html;charset=utf8,Web Console subresource STS " +
+const TEST_URI = "data:text/html;charset=utf-8,Web Console subresource STS " +
                  "warning test";
 const TEST_DOC = "https://example.com/browser/browser/devtools/webconsole/" +
                  "test/test_bug1092055_shouldwarn.html";
-const SAMPLE_MSG = "invalid Strict-Transport-Security header";
+const SAMPLE_MSG = "specified a header that could not be parsed successfully.";
 
 let test = asyncTest(function* () {
   let { browser } = yield loadTab(TEST_URI);
 
   let hud = yield openConsole();
 
   hud.jsterm.clearOutput();
 
deleted file mode 100644
--- a/browser/devtools/webconsole/test/test-bug-846918-hsts-invalid-headers.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!doctype html>
- <html>
-   <head>
-     <meta charset="utf8">
-     <title>Bug 846918 - Report invalid strict-transport-security
-       headers to the web console</title>
-     <!-- Any copyright is dedicated to the Public Domain.
-         http://creativecommons.org/publicdomain/zero/1.0/ -->
-   </head>
-   <body>
-     <p>This page is served with an invalid STS header.</p>
-   </body>
- </html>
deleted file mode 100644
--- a/browser/devtools/webconsole/test/test-bug-846918-hsts-invalid-headers.html^headers^
+++ /dev/null
@@ -1,1 +0,0 @@
-Strict-Transport-Security: max-age444
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test_hpkp-invalid-headers.sjs
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(request, response)
+{
+  response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
+
+  let issue;
+  switch (request.queryString) {
+    case "badSyntax":
+      response.setHeader("Public-Key-Pins", "\"");
+      issue = "is not syntactically correct.";
+      break;
+    case "noMaxAge":
+      response.setHeader("Public-Key-Pins", "max-age444");
+      issue = "does not include a max-age directive.";
+      break;
+    case "invalidIncludeSubDomains":
+      response.setHeader("Public-Key-Pins", "includeSubDomains=abc");
+      issue = "includes an invalid includeSubDomains directive.";
+      break;
+    case "invalidMaxAge":
+      response.setHeader("Public-Key-Pins", "max-age=abc");
+      issue = "includes an invalid max-age directive.";
+      break;
+    case "multipleIncludeSubDomains":
+      response.setHeader("Public-Key-Pins",
+                         "includeSubDomains; includeSubDomains");
+      issue = "includes multiple includeSubDomains directives.";
+      break;
+    case "multipleMaxAge":
+      response.setHeader("Public-Key-Pins",
+                         "max-age=444; max-age=999");
+      issue = "includes multiple max-age directives.";
+      break;
+    case "multipleReportURIs":
+      response.setHeader("Public-Key-Pins",
+                         'report-uri="http://example.com"; ' +
+                         'report-uri="http://example.com"');
+      issue = "includes multiple report-uri directives.";
+      break;
+    case "pinsetDoesNotMatch":
+      response.setHeader(
+        "Public-Key-Pins",
+        'max-age=999; ' +
+        'pin-sha256="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; ' +
+        'pin-sha256="BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="');
+      issue = "does not include a matching pin.";
+      break;
+  }
+
+  response.write("This page is served with a PKP header that " + issue);
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test_hsts-invalid-headers.sjs
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(request, response)
+{
+  response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
+
+  let issue;
+  switch (request.queryString) {
+    case "badSyntax":
+      response.setHeader("Strict-Transport-Security", "\"");
+      issue = "is not syntactically correct.";
+      break;
+    case "noMaxAge":
+      response.setHeader("Strict-Transport-Security", "max-age444");
+      issue = "does not include a max-age directive.";
+      break;
+    case "invalidIncludeSubDomains":
+      response.setHeader("Strict-Transport-Security", "includeSubDomains=abc");
+      issue = "includes an invalid includeSubDomains directive.";
+      break;
+    case "invalidMaxAge":
+      response.setHeader("Strict-Transport-Security", "max-age=abc");
+      issue = "includes an invalid max-age directive.";
+      break;
+    case "multipleIncludeSubDomains":
+      response.setHeader("Strict-Transport-Security",
+                         "includeSubDomains; includeSubDomains");
+      issue = "includes multiple includeSubDomains directives.";
+      break;
+    case "multipleMaxAge":
+      response.setHeader("Strict-Transport-Security",
+                         "max-age=444; max-age=999");
+      issue = "includes multiple max-age directives.";
+      break;
+  }
+
+  response.write("This page is served with a STS header that " + issue);
+}
--- a/browser/locales/en-US/chrome/browser/devtools/markers.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/markers.properties
@@ -73,8 +73,82 @@ marker.field.causeName=Cause:
 # General "type" for a marker (Cycle Collection, Garbage Collection)
 marker.field.type=Type:
 
 # Strings used in the waterfall sidebar as values.
 marker.value.unknownFrame=<unknown location>
 marker.value.DOMEventTargetPhase=Target
 marker.value.DOMEventCapturingPhase=Capture
 marker.value.DOMEventBubblingPhase=Bubbling
+
+# LOCALIZATION NOTE (marker.gcreason.label.*):
+# These strings are used to give a concise but readable description of a GC reason.
+marker.gcreason.label.API=API Call
+marker.gcreason.label.EAGER_ALLOC_TRIGGER=Eager Allocation Trigger
+marker.gcreason.label.DESTROY_RUNTIME=Shutdown
+marker.gcreason.label.DESTROY_CONTEXT=Shutdown
+marker.gcreason.label.LAST_DITCH=Out of Memory
+marker.gcreason.label.TOO_MUCH_MALLOC=Too Many Bytes Allocated
+marker.gcreason.label.ALLOC_TRIGGER=Too Many Allocations
+marker.gcreason.label.DEBUG_GC=Debug GC
+marker.gcreason.label.COMPARTMENT_REVIVED=Dead Global Revived
+marker.gcreason.label.RESET=Finish Incremental Cycle
+marker.gcreason.label.OUT_OF_NURSERY=Nursery is Full
+marker.gcreason.label.EVICT_NURSERY=Nursery Eviction
+marker.gcreason.label.FULL_STORE_BUFFER=Nursery Objects Too Active
+marker.gcreason.label.SHARED_MEMORY_LIMIT=Large Allocation Failed
+marker.gcreason.label.PERIODIC_FULL_GC=Periodic Full GC
+marker.gcreason.label.INCREMENTAL_TOO_SLOW=Allocations Rate Too Fast
+marker.gcreason.label.COMPONENT_UTILS=Cu.forceGC
+marker.gcreason.label.MEM_PRESSURE=Low Memory
+marker.gcreason.label.CC_WAITING=Forced by Cycle Collection
+marker.gcreason.label.CC_FORCED=Forced by Cycle Collection
+marker.gcreason.label.LOAD_END=Page Load Finished
+marker.gcreason.label.PAGE_HIDE=Moved to Background
+marker.gcreason.label.NSJSCONTEXT_DESTROY=Destroy JS Context
+marker.gcreason.label.SET_NEW_DOCUMENT=New Document
+marker.gcreason.label.SET_DOC_SHELL=New Document
+marker.gcreason.label.DOM_UTILS=API Call
+marker.gcreason.label.DOM_IPC=IPC
+marker.gcreason.label.DOM_WORKER=Periodic Worker GC
+marker.gcreason.label.INTER_SLICE_GC=Periodic Incremental GC Slice
+marker.gcreason.label.FULL_GC_TIMER=Periodic Full GC
+marker.gcreason.label.SHUTDOWN_CC=Shutdown
+marker.gcreason.label.FINISH_LARGE_EVALUATE=Large Eval
+marker.gcreason.label.DOM_WINDOW_UTILS=User Inactive
+marker.gcreason.label.USER_INACTIVE=User Inactive
+
+# LOCALIZATION NOTE (marker.gcreason.description.*):
+# These strings are used to give an expanded description of why a GC occurred.
+marker.gcreason.description.API=There was an API call to force garbage collection.
+marker.gcreason.description.EAGER_ALLOC_TRIGGER=JavaScript returned to the event loop and there were enough bytes allocated since the last GC that a new GC cycle was triggered.
+marker.gcreason.description.DESTROY_RUNTIME=Firefox destroyed a JavaScript runtime or context, and this was the final garbage collection before shutting down.
+marker.gcreason.description.DESTROY_CONTEXT=Firefox destroyed a JavaScript runtime or context, and this was the final garbage collection before shutting down.
+marker.gcreason.description.LAST_DITCH=JavaScript attempted to allocate, but there was no memory available. Doing a full compacting garbage collection as an attempt to free up memory for the allocation.
+marker.gcreason.description.TOO_MUCH_MALLOC=JavaScript allocated too many bytes, and forced a garbage collection.
+marker.gcreason.description.ALLOC_TRIGGER=JavaScript allocated too many times, and forced a garbage collection.
+marker.gcreason.description.DEBUG_GC=GC due to Zeal debug settings.
+marker.gcreason.description.COMPARTMENT_REVIVED=A global object that was thought to be dead at the start of the GC cycle was revived by the end of the GC cycle.
+marker.gcreason.description.RESET=The active incremental GC cycle was forced to finish immediately.
+marker.gcreason.description.OUT_OF_NURSERY=JavaScript allocated enough new objects in the nursery that it became full and triggered a minor GC.
+marker.gcreason.description.EVICT_NURSERY=Work needed to be done on the tenured heap, requiring the nursery to be empty.
+marker.gcreason.description.FULL_STORE_BUFFER=There were too many properties on tenured objects whose value was an object in the nursery.
+marker.gcreason.description.SHARED_MEMORY_LIMIT=A large allocation was requested, but there was not enough memory.
+marker.gcreason.description.PERIODIC_FULL_GC=JavaScript returned to the event loop, and it has been a relatively long time since Firefox performed a garbage collection.
+marker.gcreason.description.INCREMENTAL_TOO_SLOW=A full, non-incremental garbage collection was triggered because there was a faster rate of allocations than the existing incremental garbage collection cycle could keep up with.
+marker.gcreason.description.COMPONENT_UTILS=Components.utils.forceGC() was called to force a garbage collection.
+marker.gcreason.description.MEM_PRESSURE=There was very low memory available.
+marker.gcreason.description.CC_WAITING=The cycle collector required a garbage collection.
+marker.gcreason.description.CC_FORCED=The cycle collector required a garbage collection.
+marker.gcreason.description.LOAD_END=The document finished loading.
+marker.gcreason.description.PAGE_HIDE=The tab or window was moved to the background.
+marker.gcreason.description.NSJSCONTEXT_DESTROY=Firefox destroyed a JavaScript runtime or context, and this was the final garbage collection before shutting down.
+marker.gcreason.description.SET_NEW_DOCUMENT=The page has been navigated to a new document.
+marker.gcreason.description.SET_DOC_SHELL=The page has been navigated to a new document.
+marker.gcreason.description.DOM_UTILS=There was an API call to force garbage collection.
+marker.gcreason.description.DOM_IPC=Received an inter-process message that requested a garbage collection.
+marker.gcreason.description.DOM_WORKER=The worker was idle for a relatively long time.
+marker.gcreason.description.INTER_SLICE_GC=There has been a relatively long time since the last incremental GC slice.
+marker.gcreason.description.FULL_GC_TIMER=JavaScript returned to the event loop, and it has been a relatively long time since we performed a garbage collection.
+marker.gcreason.description.SHUTDOWN_CC=Firefox destroyed a JavaScript runtime or context, and this was the final garbage collection before shutting down.
+marker.gcreason.description.FINISH_LARGE_EVALUATE=Firefox finished evaluating a large script, and performed a GC because the script will never be run again.
+marker.gcreason.description.DOM_WINDOW_UTILS=The user was inactive for a long time. Took the opportunity to perform GC when it was unlikely to be noticed.
+marker.gcreason.description.USER_INACTIVE=The user was inactive for a long time. Firefox took the opportunity to perform GC when it was unlikely to be noticed.
--- a/browser/locales/en-US/chrome/browser/devtools/performance.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/performance.dtd
@@ -46,17 +46,17 @@
   -  on a button that opens a dialog to import a saved profile data file. -->
 <!ENTITY performanceUI.importButton "Import…">
 
 <!-- LOCALIZATION NOTE (performanceUI.exportButton): This string is displayed
   -  on a button that opens a dialog to export a saved profile data file. -->
 <!ENTITY performanceUI.exportButton "Save">
 
 <!-- LOCALIZATION NOTE (performanceUI.clearButton): This string is displayed
-  -  on a button that remvoes all the recordings. -->
+  -  on a button that removes all the recordings. -->
 <!ENTITY performanceUI.clearButton "Clear">
 
 <!-- LOCALIZATION NOTE (performanceUI.toolbar.*): These strings are displayed
   -  in the toolbar on buttons that select which view is currently shown. -->
 <!ENTITY performanceUI.toolbar.waterfall "Waterfall">
 <!ENTITY performanceUI.toolbar.js-calltree "Call Tree">
 <!ENTITY performanceUI.toolbar.memory-calltree "Allocations">
 <!ENTITY performanceUI.toolbar.js-flamegraph "JS Flame Chart">
--- a/browser/locales/en-US/chrome/browser/devtools/performance.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/performance.properties
@@ -31,17 +31,18 @@ performance.accesskey=P
 performance.tooltip=Performance (%S)
 
 # LOCALIZATION NOTE (noRecordingsText): The text to display in the
 # recordings menu when there are no recorded profiles yet.
 noRecordingsText=There are no profiles yet.
 
 # LOCALIZATION NOTE (recordingsList.itemLabel):
 # This string is displayed in the recordings list of the Performance Tools,
-# identifying a set of function calls.
+# identifying a set of function calls. %S represents the number of recording,
+# iterating for every new recording, resulting in "Recording #1", "Recording #2", etc.
 recordingsList.itemLabel=Recording #%S
 
 # LOCALIZATION NOTE (recordingsList.recordingLabel):
 # This string is displayed in the recordings list of the Performance Tools,
 # for an item that has not finished recording.
 recordingsList.recordingLabel=In progress…
 
 # LOCALIZATION NOTE (recordingsList.loadingLabel):
@@ -164,9 +165,9 @@ timeline.tick=%S ms
 timeline.records=RECORDS
 
 # LOCALIZATION NOTE (profiler.bufferFull):
 # This string is displayed when recording, indicating how much of the
 # buffer is currently be used.
 # %S is the percentage of the buffer used -- there are two "%"s after to escape
 # the % that is actually displayed.
 # Example: "Buffer 54% full"
-profiler.bufferFull=Buffer %S%% full
+profiler.bufferFull=Buffer %S%% full
\ No newline at end of file
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -30,16 +30,18 @@
   --toolbarbutton-active-background: rgba(154,154,154,.5) linear-gradient(hsla(0,0%,100%,.7), hsla(0,0%,100%,.4));
 
   --toolbarbutton-checkedhover-backgroundcolor: rgba(90%,90%,90%,.4);
 
   --toolbarbutton-combined-boxshadow: 0 0 0 1px hsla(0,0%,100%,.2);
   --toolbarbutton-combined-backgroundimage: linear-gradient(hsla(210,54%,20%,.2) 0, hsla(210,54%,20%,.2) 18px);
 
   --identity-box-verified-background-color: #fff;
+
+  --panel-separator-color: ThreeDShadow;
 }
 
 #menubar-items {
   -moz-box-orient: vertical; /* for flex hack */
 }
 
 #main-menubar {
   -moz-box-flex: 1; /* make menu items expand to fill toolbar height */
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -39,16 +39,18 @@
   --toolbarbutton-combined-backgroundimage: linear-gradient(hsla(0,0%,0%,.15) 0, hsla(0,0%,0%,.15) 18px);
 
   --urlbar-dropmarker-url: url("chrome://browser/skin/urlbar-history-dropmarker.png");
   --urlbar-dropmarker-region: rect(0, 11px, 14px, 0);
   --urlbar-dropmarker-active-region: rect(0, 22px, 14px, 11px);
   --urlbar-dropmarker-2x-url: url("chrome://browser/skin/urlbar-history-dropmarker@2x.png");
   --urlbar-dropmarker-2x-region: rect(0, 22px, 28px, 0);
   --urlbar-dropmarker-active-2x-region: rect(0, 44px, 28px, 22px);
+
+  --panel-separator-color: hsla(210,4%,10%,.14);
 }
 
 #urlbar:-moz-lwtheme:not([focused="true"]),
 .searchbar-textbox:-moz-lwtheme:not([focused="true"]) {
   opacity: .9;
 }
 
 #navigator-toolbox::after {
@@ -144,16 +146,23 @@ toolbarseparator {
   min-height: 22px;
 }
 
 #navigator-toolbox > toolbar:not(#TabsToolbar):not(#nav-bar):not(:-moz-lwtheme) {
   -moz-appearance: none;
   background: url(chrome://browser/skin/Toolbar-background-noise.png) hsl(0,0%,83%);
 }
 
+/* remove noise texture on Yosemite */
+@media (-moz-mac-yosemite-theme) {
+  #navigator-toolbox > toolbar:not(#TabsToolbar):not(#nav-bar):not(:-moz-lwtheme) {
+    background-image: none;
+  }
+}
+
 #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar):not(#addon-bar) {
   overflow: -moz-hidden-unscrollable;
   max-height: 4em;
   transition: min-height 170ms ease-out, max-height 170ms ease-out;
 }
 
 #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar):not(#addon-bar)[collapsed=true] {
   min-height: 0.1px;
@@ -179,16 +188,23 @@ toolbarseparator {
 }
 
 @media (min-resolution: 2dppx) {
   #nav-bar {
     background-size: 100px 100px, auto;
   }
 }
 
+/* remove noise texture on Yosemite */
+@media (-moz-mac-yosemite-theme) {
+  #nav-bar {
+    background: linear-gradient(hsl(0,0%,93%), hsl(0,0%,83%));
+  }
+}
+
 /* Draw the bottom border of the tabs toolbar when it's not using
    -moz-appearance: toolbar. */
 #main-window:-moz-any([sizemode="fullscreen"],[customize-entered]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
 #main-window:not([tabsintitlebar]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
 #TabsToolbar:not([collapsed="true"]) + #nav-bar:-moz-lwtheme {
   border-top: 1px solid hsla(0,0%,0%,.3);
   background-clip: padding-box;
   margin-top: calc(-1 * var(--tab-toolbar-navbar-overlap));
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -65,17 +65,17 @@
 }
 
 #identity-popup-multiView > .panel-viewcontainer > .panel-viewstack > .panel-subviews:-moz-locale-dir(rtl) {
   border-bottom-right-radius: 0;
   border-bottom-left-radius: 3.5px;
 }
 
 .identity-popup-section:not(:first-child) {
-  border-top: 1px solid ThreeDShadow;
+  border-top: 1px solid var(--panel-separator-color);
 }
 
 #identity-popup-securityView,
 #identity-popup-security-content,
 #identity-popup-permissions-content,
 #tracking-protection-content {
   padding: 0.75em 0 1em;
   -moz-padding-start: calc(2em + 24px);
@@ -118,23 +118,23 @@
   background-color: Highlight;
   background-image: url("chrome://browser/skin/controlcenter/arrow-subview-back.svg"),
                     linear-gradient(rgba(255,255,255,0.3), transparent);
 }
 
 .identity-popup-expander > .button-box {
   padding: 0;
   -moz-appearance: none;
-  border: solid ThreeDShadow;
-  border-width: 0 0 0 1px;
+  border-style: none;
+  border-left: 1px solid var(--panel-separator-color);
 }
 
 .identity-popup-expander:-moz-focusring > .button-box,
 .identity-popup-expander[panel-multiview-anchor] > .button-box {
-  border: 0 none;
+  border-style: none;
 }
 
 .identity-popup-expander:hover {
   background-color: hsla(210,4%,10%,.07);
 }
 
 .identity-popup-expander:hover:active {
   background-color: hsla(210,4%,10%,.12);
@@ -201,17 +201,17 @@
 }
 
 #identity-popup-securityView.mixedActiveContent,
 #identity-popup-security-content.mixedActiveContent {
   background-image: url(chrome://browser/skin/controlcenter/mcb-disabled.svg);
 }
 
 #identity-popup-securityView-header {
-  border-bottom: 1px solid ThreeDShadow;
+  border-bottom: 1px solid var(--panel-separator-color);
   padding-bottom: 1em;
   margin-bottom: 1em;
 }
 
 #identity-popup-content-owner {
   font-weight: 700;
 }
 
@@ -269,17 +269,17 @@
 /* FOOTER BUTTONS */
 
 #identity-popup-button-container {
   background-color: hsla(210,4%,10%,.07);
 }
 
 #identity-popup-more-info-button {
   border: none;
-  border-top: 1px solid ThreeDShadow;
+  border-top: 1px solid var(--panel-separator-color);
   background: transparent;
   -moz-appearance: none;
   margin-top: 5px;
   margin: 0;
 }
 
 #identity-popup-more-info-button > .button-box {
   -moz-appearance: none;
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -43,16 +43,18 @@
   --urlbar-dropmarker-url: url("chrome://browser/skin/urlbar-history-dropmarker.png");
   --urlbar-dropmarker-region: rect(0px, 11px, 14px, 0px);
   --urlbar-dropmarker-hover-region: rect(0px, 22px, 14px, 11px);
   --urlbar-dropmarker-active-region: rect(0px, 33px, 14px, 22px);
   --urlbar-dropmarker-2x-url: url("chrome://browser/skin/urlbar-history-dropmarker@2x.png");
   --urlbar-dropmarker-2x-region: rect(0, 22px, 28px, 0);
   --urlbar-dropmarker-hover-2x-region: rect(0, 44px, 28px, 22px);
   --urlbar-dropmarker-active-2x-region: rect(0, 66px, 28px, 44px);
+
+  --panel-separator-color: ThreeDLightShadow;
 }
 
 #menubar-items {
   -moz-box-orient: vertical; /* for flex hack */
 }
 
 #main-menubar {
   -moz-box-flex: 1; /* make menu items expand to fill toolbar height */
--- a/build/autoconf/nspr-build.m4
+++ b/build/autoconf/nspr-build.m4
@@ -243,17 +243,17 @@ if test -z "$MOZ_NATIVE_NSPR"; then
     _SAVE_LDFLAGS="$LDFLAGS"
 
     if test -n "$MOZ_LINKER" -a "$ac_cv_func_dladdr" = no ; then
       # dladdr is supported by the new linker, even when the system linker doesn't
       # support it. Trick nspr into using dladdr when it's not supported.
       export CPPFLAGS="-include $_topsrcdir/mozglue/linker/dladdr.h $CPPFLAGS"
     fi
     export LDFLAGS="$LDFLAGS $NSPR_LDFLAGS"
-    export CFLAGS="$CFLAGS $MOZ_FRAMEPTR_FLAGS"
+    export CFLAGS="$CFLAGS $MOZ_FRAMEPTR_FLAGS $MOZ_FOLD_LIBS_FLAGS"
 
     AC_OUTPUT_SUBDIRS(nsprpub)
 
     # .. and restore them
     CFLAGS="$_SAVE_CFLAGS"
     CPPFLAGS="$_SAVE_CPPFLAGS"
     LDFLAGS="$_SAVE_LDFLAGS"
 
--- a/config/external/nss/Makefile.in
+++ b/config/external/nss/Makefile.in
@@ -258,16 +258,20 @@ DEFAULT_GMAKE_FLAGS += MODULE_INCLUDES='
 
 # Work around NSS's MAKE_OBJDIR being racy. See bug #836220
 DEFAULT_GMAKE_FLAGS += MAKE_OBJDIR='$$(INSTALL) -D $$(OBJDIR)'
 
 # Work around NSS adding IMPORT_LIBRARY to TARGETS with no rule for
 # it, creating race conditions. See bug #836220
 DEFAULT_GMAKE_FLAGS += TARGETS='$$(LIBRARY) $$(SHARED_LIBRARY) $$(PROGRAM)'
 
+ifdef MOZ_FOLD_LIBS_FLAGS
+DEFAULT_GMAKE_FLAGS += XCFLAGS='$(MOZ_FOLD_LIBS_FLAGS)'
+endif
+
 NSS_SRCDIR = $(topsrcdir)
 
 NSS_DIRS =
 ifndef MOZ_FOLD_LIBS
 NSS_DIRS += nss/lib
 else
 ifndef NSS_DISABLE_DBM
 NSS_DIRS += nss/lib/dbm
--- a/configure.in
+++ b/configure.in
@@ -2196,16 +2196,22 @@ ia64*-hpux*)
         LIBS="$LIBS -luuid -lgdi32 -lwinmm -lwsock32 -luserenv -lsecur32 -lnetapi32"
         MOZ_FIX_LINK_PATHS=
         DLL_PREFIX=
         IMPORT_LIB_SUFFIX=a
 
         WIN32_CONSOLE_EXE_LDFLAGS=-mconsole
         WIN32_GUI_EXE_LDFLAGS=-mwindows
 
+        # GCC/binutils can't link to a function if we try to include dllexport function
+        # in the same library as dllimport caller. To work around it, we build NSPR
+        # and NSS with -mnop-fun-dllimport flag. The drawback of this solution is that
+        # function thunks need to be generated for cross-DLL calls.
+        MOZ_FOLD_LIBS_FLAGS=-mnop-fun-dllimport
+
         # We use mix of both POSIX and Win32 printf format across the tree, so format
         # warnings are useless on mingw.
         MOZ_C_SUPPORTS_WARNING(-Wno-, format, ac_c_has_wno_format)
         MOZ_CXX_SUPPORTS_WARNING(-Wno-, format, ac_cxx_has_wno_format)
     else
         TARGET_COMPILER_ABI=msvc
         HOST_CC='$(CC)'
         HOST_CXX='$(CXX)'
@@ -4190,19 +4196,16 @@ dnl ====================================
 MOZ_WIDGET_GTK=
 
 case "$MOZ_WIDGET_TOOLKIT" in
 
 cairo-windows)
     MOZ_WIDGET_TOOLKIT=windows
     MOZ_PDF_PRINTING=1
     MOZ_INSTRUMENT_EVENT_LOOP=1
-    if test -n "$GNU_CC"; then
-        MOZ_FOLD_LIBS=
-    fi
     ;;
 
 cairo-gtk3)
     MOZ_WIDGET_TOOLKIT=gtk3
     MOZ_ENABLE_GTK=1
     MOZ_ENABLE_GTK3=1
     MOZ_ENABLE_XREMOTE=1
     MOZ_GL_DEFAULT_PROVIDER=GLX
@@ -8975,16 +8978,17 @@ AC_SUBST(MOZ_CODE_COVERAGE)
 AC_SUBST(LIBJPEG_TURBO_AS)
 AC_SUBST_LIST(LIBJPEG_TURBO_ASFLAGS)
 AC_SUBST(MOZ_LIBAV_FFT)
 AC_SUBST(LIBAV_FFT_AS)
 AC_SUBST_LIST(LIBAV_FFT_ASFLAGS)
 
 AC_SUBST(MOZ_PACKAGE_JSSHELL)
 AC_SUBST(MOZ_FOLD_LIBS)
+AC_SUBST(MOZ_FOLD_LIBS_FLAGS)
 AC_SUBST(SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE)
 
 AC_SUBST(MOZ_ENABLE_SZIP)
 AC_SUBST(MOZ_SZIP_FLAGS)
 
 dnl Host JavaScript runtime, if any, to use during cross compiles.
 AC_SUBST(JS_BINARY)
 
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -44,17 +44,17 @@ AudioChannelAgent::~AudioChannelAgent()
 {
   Shutdown();
 }
 
 void
 AudioChannelAgent::Shutdown()
 {
   if (mIsRegToService) {
-    NotifyStoppedPlaying();
+    NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
   }
 }
 
 NS_IMETHODIMP AudioChannelAgent::GetAudioChannelType(int32_t *aAudioChannelType)
 {
   *aAudioChannelType = mAudioChannelType;
   return NS_OK;
 }
@@ -122,46 +122,47 @@ AudioChannelAgent::InitInternal(nsIDOMWi
     mWeakCallback = do_GetWeakReference(aCallback);
   } else {
     mCallback = aCallback;
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP AudioChannelAgent::NotifyStartedPlaying(float *aVolume,
+NS_IMETHODIMP AudioChannelAgent::NotifyStartedPlaying(uint32_t aNotifyPlayback,
+                                                      float *aVolume,
                                                       bool* aMuted)
 {
   MOZ_ASSERT(aVolume);
   MOZ_ASSERT(aMuted);
 
   nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       service == nullptr || mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
-  service->RegisterAudioChannelAgent(this,
+  service->RegisterAudioChannelAgent(this, aNotifyPlayback,
     static_cast<AudioChannel>(mAudioChannelType));
 
   service->GetState(mWindow, mAudioChannelType, aVolume, aMuted);
 
   mIsRegToService = true;
   return NS_OK;
 }
 
-NS_IMETHODIMP AudioChannelAgent::NotifyStoppedPlaying(void)
+NS_IMETHODIMP AudioChannelAgent::NotifyStoppedPlaying(uint32_t aNotifyPlayback)
 {
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       !mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
-  service->UnregisterAudioChannelAgent(this);
+  service->UnregisterAudioChannelAgent(this, aNotifyPlayback);
   mIsRegToService = false;
   return NS_OK;
 }
 
 already_AddRefed<nsIAudioChannelAgentCallback>
 AudioChannelAgent::GetCallback()
 {
   nsCOMPtr<nsIAudioChannelAgentCallback> callback = mCallback;
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -228,16 +228,17 @@ AudioChannelService::AudioChannelService
 }
 
 AudioChannelService::~AudioChannelService()
 {
 }
 
 void
 AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
+                                               uint32_t aNotifyPlayback,
                                                AudioChannel aChannel)
 {
   uint64_t windowID = aAgent->WindowID();
   AudioChannelWindow* winData = GetWindowData(windowID);
   if (!winData) {
     winData = new AudioChannelWindow(windowID);
     mWindows.AppendElement(winData);
   }
@@ -248,27 +249,29 @@ AudioChannelService::RegisterAudioChanne
   ++winData->mChannels[(uint32_t)aChannel].mNumberOfAgents;
 
   // The first one, we must inform the BrowserElementAudioChannel.
   if (winData->mChannels[(uint32_t)aChannel].mNumberOfAgents == 1) {
     NotifyChannelActive(aAgent->WindowID(), aChannel, true);
   }
 
   // If this is the first agent for this window, we must notify the observers.
-  if (winData->mAgents.Length() == 1) {
+  if (aNotifyPlayback == nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY &&
+      winData->mAgents.Length() == 1) {
     nsRefPtr<MediaPlaybackRunnable> runnable =
       new MediaPlaybackRunnable(aAgent->Window(), true /* active */);
     NS_DispatchToCurrentThread(runnable);
   }
 
   MaybeSendStatusUpdate();
 }
 
 void
-AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
+AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent,
+                                                 uint32_t aNotifyPlayback)
 {
   AudioChannelWindow* winData = GetWindowData(aAgent->WindowID());
   if (!winData) {
     return;
   }
 
   if (winData->mAgents.Contains(aAgent)) {
     int32_t channel = aAgent->AudioChannelType();
@@ -290,17 +293,18 @@ AudioChannelService::UnregisterAudioChan
 #ifdef MOZ_WIDGET_GONK
   bool active = AnyAudioChannelIsActive();
   for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
     mSpeakerManager[i]->SetAudioChannelActive(active);
   }
 #endif
 
   // If this is the last agent for this window, we must notify the observers.
-  if (winData->mAgents.IsEmpty()) {
+  if (aNotifyPlayback == nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY &&
+      winData->mAgents.IsEmpty()) {
     nsRefPtr<MediaPlaybackRunnable> runnable =
       new MediaPlaybackRunnable(aAgent->Window(), false /* active */);
     NS_DispatchToCurrentThread(runnable);
   }
 
   MaybeSendStatusUpdate();
 }
 
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -44,23 +44,26 @@ public:
   static already_AddRefed<AudioChannelService> GetOrCreate();
 
   static bool IsAudioChannelMutedByDefault();
 
   /**
    * Any audio channel agent that starts playing should register itself to
    * this service, sharing the AudioChannel.
    */
-  void RegisterAudioChannelAgent(AudioChannelAgent* aAgent, AudioChannel aChannel);
+  void RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
+                                 uint32_t aNotifyPlayback,
+                                 AudioChannel aChannel);
 
   /**
    * Any audio channel agent that stops playing should unregister itself to
    * this service.
    */
-  void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
+  void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent,
+                                   uint32_t aNotifyPlayback);
 
   /**
    * Return the state to indicate this audioChannel for his window should keep
    * playing/muted.
    */
   void GetState(nsPIDOMWindow* aWindow, uint32_t aChannel,
                 float* aVolume, bool* aMuted);
 
--- a/dom/audiochannel/nsIAudioChannelAgent.idl
+++ b/dom/audiochannel/nsIAudioChannelAgent.idl
@@ -29,17 +29,17 @@ interface nsIAudioChannelAgentCallback :
  *   3. Notifying the agent when they start/stop using this channel.
  *   4. Notifying the agent of changes to the visibility of the component using
  *      this channel.
  *
  * The agent will invoke a callback to notify Gecko components of
  *   1. Changes to the playable status of this channel.
  */
 
-[uuid(ee39a34b-a5c7-4b30-b1ac-cd64ceedef67)]
+[uuid(62e0037c-9786-4b79-b986-27111f6e553b)]
 interface nsIAudioChannelAgent : nsISupports
 {
   const long AUDIO_AGENT_CHANNEL_NORMAL             = 0;
   const long AUDIO_AGENT_CHANNEL_CONTENT            = 1;
   const long AUDIO_AGENT_CHANNEL_NOTIFICATION       = 2;
   const long AUDIO_AGENT_CHANNEL_ALARM              = 3;
   const long AUDIO_AGENT_CHANNEL_TELEPHONY          = 4;
   const long AUDIO_AGENT_CHANNEL_RINGER             = 5;
@@ -47,16 +47,19 @@ interface nsIAudioChannelAgent : nsISupp
   const long AUDIO_AGENT_CHANNEL_SYSTEM             = 7;
 
   const long AUDIO_AGENT_CHANNEL_ERROR              = 1000;
 
   const long AUDIO_AGENT_STATE_NORMAL               = 0;
   const long AUDIO_AGENT_STATE_MUTED                = 1;
   const long AUDIO_AGENT_STATE_FADED                = 2;
 
+  const long AUDIO_AGENT_DONT_NOTIFY                = 0;
+  const long AUDIO_AGENT_NOTIFY                     = 1;
+
   /**
    * Before init() is called, this returns AUDIO_AGENT_CHANNEL_ERROR.
    */
   readonly attribute long audioChannelType;
 
   %{C++
   inline int32_t AudioChannelType() {
     int32_t channel;
@@ -93,29 +96,36 @@ interface nsIAudioChannelAgent : nsISupp
   void initWithWeakCallback(in nsIDOMWindow window, in long channelType,
                             in nsIAudioChannelAgentCallback callback);
 
   /**
    * Notify the agent that we want to start playing.
    * Note: Gecko component SHOULD call this function first then start to
    *          play audio stream only when return value is true.
    *
+   * @param notifyPlaying
+   *   Whether to send audio-playback notifications, one of AUDIO_CHANNEL_NOTIFY
+   *   or AUDIO_CHANNEL_DONT_NOTIFY.
    *
    * @return
    *    normal state: the agent has registered with audio channel service and
    *          the component should start playback.
    *    muted state: the agent has registered with audio channel service but
    *          the component should not start playback.
    *    faded state: the agent has registered with audio channel service the
    *          component should start playback as well as reducing the volume.
    */
-  void notifyStartedPlaying(out float volume, out bool muted);
+  void notifyStartedPlaying(in unsigned long notifyPlayback, out float volume, out bool muted);
 
   /**
    * Notify the agent we no longer want to play.
    *
+   * @param notifyPlaying
+   *   Whether to send audio-playback notifications, one of AUDIO_CHANNEL_NOTIFY
+   *   or AUDIO_CHANNEL_DONT_NOTIFY.
+   *
    * Note : even if notifyStartedPlaying() returned false, the agent would
    * still be registered with the audio channel service and receive callbacks
    * for status changes. So notifyStoppedPlaying must still eventually be
    * called to unregister the agent with the channel service.
    */
-  void notifyStoppedPlaying();
+  void notifyStoppedPlaying(in unsigned long notifyPlayback);
 };
--- a/dom/base/nsContentPermissionHelper.cpp
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -367,17 +367,19 @@ nsContentPermissionUtils::AskPermission(
     req->Sendprompt();
     return NS_OK;
   }
 
   // for chrome process
   nsCOMPtr<nsIContentPermissionPrompt> prompt =
     do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
   if (prompt) {
-    prompt->Prompt(aRequest);
+    if (NS_FAILED(prompt->Prompt(aRequest))) {
+      return NS_ERROR_FAILURE;
+    }
   }
   return NS_OK;
 }
 
 /* static */ nsTArray<PContentPermissionRequestParent*>
 nsContentPermissionUtils::GetContentPermissionRequestParentById(const TabId& aTabId)
 {
   nsTArray<PContentPermissionRequestParent*> parentArray;
--- a/dom/base/nsDOMMutationObserver.cpp
+++ b/dom/base/nsDOMMutationObserver.cpp
@@ -2,26 +2,28 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDOMMutationObserver.h"
 
 #include "mozilla/OwningNonNull.h"
-#include "nsError.h"
-#include "nsIScriptGlobalObject.h"
-#include "nsContentUtils.h"
-#include "nsThreadUtils.h"
-#include "nsIDOMMutationEvent.h"
-#include "nsTextFragment.h"
-#include "nsServiceManagerUtils.h"
+
 #include "mozilla/dom/Animation.h"
 #include "mozilla/dom/KeyframeEffect.h"
 
+#include "nsContentUtils.h"
+#include "nsError.h"
+#include "nsIDOMMutationEvent.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsServiceManagerUtils.h"
+#include "nsTextFragment.h"
+#include "nsThreadUtils.h"
+
 using mozilla::dom::Animation;
 
 nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>*
   nsDOMMutationObserver::sScheduledMutationObservers = nullptr;
 
 nsDOMMutationObserver* nsDOMMutationObserver::sCurrentObserver = nullptr;
 
 uint32_t nsDOMMutationObserver::sMutationLevel = 0;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -9228,16 +9228,18 @@ DispatchCustomEventWithFlush(nsINode* aT
 static void
 DispatchFullScreenChange(nsIDocument* aTarget)
 {
   DispatchCustomEventWithFlush(
     aTarget, NS_LITERAL_STRING("mozfullscreenchange"),
     /* Bubbles */ true, /* OnlyChrome */ false);
 }
 
+static void ClearPendingFullscreenRequests(nsIDocument* aDoc);
+
 void
 nsDocument::OnPageHide(bool aPersisted,
                        EventTarget* aDispatchStartTarget)
 {
   // Send out notifications that our <link> elements are detached,
   // but only if this is not a full unload.
   Element* root = GetRootElement();
   if (aPersisted && root) {
@@ -9292,16 +9294,17 @@ nsDocument::OnPageHide(bool aPersisted,
 
   mVisible = false;
 
   UpdateVisibilityState();
 
   EnumerateExternalResources(NotifyPageHide, &aPersisted);
   EnumerateActivityObservers(NotifyActivityChanged, nullptr);
 
+  ClearPendingFullscreenRequests(this);
   if (IsFullScreenDoc()) {
     // If this document was fullscreen, we should exit fullscreen in this
     // doctree branch. This ensures that if the user navigates while in
     // fullscreen mode we don't leave its still visible ancestor documents
     // in fullscreen mode. So exit fullscreen in the document's fullscreen
     // root document, as this will exit fullscreen in all the root's
     // descendant documents. Note that documents are removed from the
     // doctree by the time OnPageHide() is called, so we must store a
@@ -11003,57 +11006,28 @@ nsDocument::MozCancelFullScreen()
 }
 
 void
 nsIDocument::MozCancelFullScreen()
 {
   RestorePreviousFullScreenState();
 }
 
-// Runnable to set window full-screen mode. Used as a script runner
-// to ensure we only call nsGlobalWindow::SetFullScreen() when it's safe to
-// run script. nsGlobalWindow::SetFullScreen() dispatches a synchronous event
-// (handled in chome code) which is unsafe to run if this is called in
-// Element::UnbindFromTree().
-class nsSetWindowFullScreen : public nsRunnable {
-public:
-  nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue, gfx::VRHMDInfo* aHMD = nullptr)
-    : mDoc(aDoc), mValue(aValue), mHMD(aHMD) {}
-
-  NS_IMETHOD Run()
-  {
-    if (mDoc->GetWindow()) {
-      mDoc->GetWindow()->SetFullscreenInternal(
-        nsPIDOMWindow::eForFullscreenAPI, mValue, mHMD);
-    }
-    return NS_OK;
-  }
-
-private:
-  nsCOMPtr<nsIDocument> mDoc;
-  bool mValue;
-  nsRefPtr<gfx::VRHMDInfo> mHMD;
-};
-
-static void
-SetWindowFullScreen(nsIDocument* aDoc, bool aValue, gfx::VRHMDInfo *aVRHMD = nullptr)
-{
-  nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue, aVRHMD));
-}
-
 static void
 AskWindowToExitFullscreen(nsIDocument* aDoc)
 {
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     nsContentUtils::DispatchEventOnlyToChrome(
       aDoc, ToSupports(aDoc), NS_LITERAL_STRING("MozDOMFullscreen:Exit"),
       /* Bubbles */ true, /* Cancelable */ false,
       /* DefaultAction */ nullptr);
   } else {
-    SetWindowFullScreen(aDoc, false);
+    if (nsPIDOMWindow* win = aDoc->GetWindow()) {
+      win->SetFullscreenInternal(nsPIDOMWindow::eForFullscreenAPI, false);
+    }
   }
 }
 
 class nsCallExitFullscreen : public nsRunnable
 {
 public:
   explicit nsCallExitFullscreen(nsIDocument* aDoc)
     : mDoc(aDoc) {}
@@ -11110,23 +11084,53 @@ nsDocument::IsFullscreenLeaf()
 static bool
 ResetFullScreen(nsIDocument* aDocument, void* aData)
 {
   if (aDocument->IsFullScreenDoc()) {
     NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
         "Should have at most 1 fullscreen subdocument.");
     static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
     NS_ASSERTION(!aDocument->IsFullScreenDoc(), "Should reset full-screen");
-    nsTArray<nsIDocument*>* changed = reinterpret_cast<nsTArray<nsIDocument*>*>(aData);
+    auto changed = reinterpret_cast<nsCOMArray<nsIDocument>*>(aData);
     changed->AppendElement(aDocument);
     aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
   }
   return true;
 }
 
+// Since nsIDocument::ExitFullscreenInDocTree() could be called from
+// Element::UnbindFromTree() where it is not safe to synchronously run
+// script. This runnable is the script part of that function.
+class ExitFullscreenScriptRunnable : public nsRunnable
+{
+public:
+  explicit ExitFullscreenScriptRunnable(nsCOMArray<nsIDocument>&& aDocuments)
+    : mDocuments(Move(aDocuments)) { }
+
+  NS_IMETHOD Run() override
+  {
+    // Dispatch MozDOMFullscreen:Exited to the last document in
+    // the list since we want this event to follow the same path
+    // MozDOMFullscreen:Entered dispatched.
+    nsIDocument* lastDocument = mDocuments[mDocuments.Length() - 1];
+    nsContentUtils::DispatchEventOnlyToChrome(
+      lastDocument, ToSupports(lastDocument),
+      NS_LITERAL_STRING("MozDOMFullscreen:Exited"),
+      /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
+    // Ensure the window exits fullscreen.
+    if (nsPIDOMWindow* win = mDocuments[0]->GetWindow()) {
+      win->SetFullscreenInternal(nsPIDOMWindow::eForForceExitFullscreen, false);
+    }
+    return NS_OK;
+  }
+
+private:
+  nsCOMArray<nsIDocument> mDocuments;
+};
+
 /* static */ void
 nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
 {
   MOZ_ASSERT(aMaybeNotARootDoc);
 
   // Unlock the pointer
   UnlockPointer();
 
@@ -11143,41 +11147,36 @@ nsIDocument::ExitFullscreenInDocTree(nsI
     return;
   }
 
   // Stores a list of documents to which we must dispatch "mozfullscreenchange".
   // We're required by the spec to dispatch the events in leaf-to-root
   // order when exiting fullscreen, but we traverse the doctree in a
   // root-to-leaf order, so we save references to the documents we must
   // dispatch to so that we dispatch in the specified order.
-  nsAutoTArray<nsIDocument*, 8> changed;
+  nsCOMArray<nsIDocument> changed;
 
   // Walk the tree of fullscreen documents, and reset their fullscreen state.
   ResetFullScreen(root, static_cast<void*>(&changed));
 
   // Dispatch "mozfullscreenchange" events. Note this loop is in reverse
   // order so that the events for the leaf document arrives before the root
   // document, as required by the spec.
   for (uint32_t i = 0; i < changed.Length(); ++i) {
     DispatchFullScreenChange(changed[changed.Length() - i - 1]);
   }
 
   NS_ASSERTION(!root->IsFullScreenDoc(),
     "Fullscreen root should no longer be a fullscreen doc...");
 
-  // Dispatch MozDOMFullscreen:Exited to the last document in
-  // the list since we want this event to follow the same path
-  // MozDOMFullscreen:Entered dispatched.
-  nsContentUtils::DispatchEventOnlyToChrome(
-    changed.LastElement(), ToSupports(changed.LastElement()),
-    NS_LITERAL_STRING("MozDOMFullscreen:Exited"),
-    /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
   // Move the top-level window out of fullscreen mode.
   FullscreenRoots::Remove(root);
-  SetWindowFullScreen(root, false);
+
+  nsContentUtils::AddScriptRunner(
+    new ExitFullscreenScriptRunnable(Move(changed)));
 }
 
 bool
 GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
 {
   if (aDoc->IsFullscreenLeaf()) {
     nsIDocument** result = static_cast<nsIDocument**>(aData);
     *result = aDoc;
@@ -11625,17 +11624,18 @@ nsDocument::RequestFullScreen(UniquePtr<
     // If we are not the top level process, dispatch an event to make
     // our parent process go fullscreen first.
     nsContentUtils::DispatchEventOnlyToChrome(
       this, ToSupports(this), NS_LITERAL_STRING("MozDOMFullscreen:Request"),
       /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
   } else {
     // Make the window fullscreen.
     FullscreenRequest* lastRequest = sPendingFullscreenRequests.getLast();
-    SetWindowFullScreen(this, true, lastRequest->mVRHMDDevice);
+    rootWin->SetFullscreenInternal(nsPIDOMWindow::eForFullscreenAPI, true,
+                                   lastRequest->mVRHMDDevice);
   }
 }
 
 /* static */ bool
 nsIDocument::HandlePendingFullscreenRequest(const FullscreenRequest& aRequest,
                                             nsIDocShellTreeItem* aRootShell,
                                             bool* aHandled)
 {
@@ -11645,18 +11645,19 @@ nsIDocument::HandlePendingFullscreenRequ
     return true;
   }
   nsCOMPtr<nsIDocShellTreeItem> rootShell;
   shell->GetRootTreeItem(getter_AddRefs(rootShell));
   if (rootShell != aRootShell) {
     return false;
   }
 
-  doc->ApplyFullscreen(aRequest);
-  *aHandled = true;
+  if (doc->ApplyFullscreen(aRequest)) {
+    *aHandled = true;
+  }
   return true;
 }
 
 /* static */ bool
 nsIDocument::HandlePendingFullscreenRequests(nsIDocument* aDoc)
 {
   if (sPendingFullscreenRequests.isEmpty()) {
     return false;
@@ -11678,22 +11679,51 @@ nsIDocument::HandlePendingFullscreenRequ
       delete thisRequest;
     } else {
       request = request->getNext();
     }
   }
   return handled;
 }
 
-void
+static void
+ClearPendingFullscreenRequests(nsIDocument* aDoc)
+{
+  nsIDocShellTreeItem* shell = aDoc->GetDocShell();
+  if (!shell) {
+    return;
+  }
+
+  FullscreenRequest* request = sPendingFullscreenRequests.getFirst();
+  while (request) {
+    nsIDocument* doc = request->GetDocument();
+    bool shouldRemove = false;
+    for (nsCOMPtr<nsIDocShellTreeItem> docShell = doc->GetDocShell();
+         docShell; docShell->GetParent(getter_AddRefs(docShell))) {
+      if (docShell == shell) {
+        shouldRemove = true;
+        break;
+      }
+    }
+    if (shouldRemove) {
+      FullscreenRequest* thisRequest = request;
+      request = request->getNext();
+      delete thisRequest;
+    } else {
+      request = request->getNext();
+    }
+  }
+}
+
+bool
 nsDocument::ApplyFullscreen(const FullscreenRequest& aRequest)
 {
   Element* elem = aRequest.GetElement();
   if (!FullscreenElementReadyCheck(elem, aRequest.mIsCallerChrome)) {
-    return;
+    return false;
   }
 
   // Stash a reference to any existing fullscreen doc, we'll use this later
   // to detect if the origin which is fullscreen has changed.
   nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this);
 
   // Stores a list of documents which we must dispatch "mozfullscreenchange"
   // too. We're required by the spec to dispatch the events in root-to-leaf
@@ -11779,16 +11809,17 @@ nsDocument::ApplyFullscreen(const Fullsc
   }
 
   // Dispatch "mozfullscreenchange" events. Note this loop is in reverse
   // order so that the events for the root document arrives before the leaf
   // document, as required by the spec.
   for (uint32_t i = 0; i < changed.Length(); ++i) {
     DispatchFullScreenChange(changed[changed.Length() - i - 1]);
   }
+  return true;
 }
 
 NS_IMETHODIMP
 nsDocument::GetMozFullScreenElement(nsIDOMElement **aFullScreenElement)
 {
   ErrorResult rv;
   Element* el = GetMozFullScreenElement(rv);
   if (rv.Failed()) {
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1524,18 +1524,20 @@ protected:
 
   explicit nsDocument(const char* aContentType);
   virtual ~nsDocument();
 
   void EnsureOnloadBlocker();
 
   void NotifyStyleSheetApplicableStateChanged();
 
-  // Apply the fullscreen state to the document, and trigger related events.
-  void ApplyFullscreen(const FullscreenRequest& aRequest);
+  // Apply the fullscreen state to the document, and trigger related
+  // events. It returns false if the fullscreen element ready check
+  // fails and nothing gets changed.
+  bool ApplyFullscreen(const FullscreenRequest& aRequest);
 
   nsTArray<nsIObserver*> mCharSetObservers;
 
   PLDHashTable *mSubDocuments;
 
   // Array of owning references to all children
   nsAttrAndChildArray mChildren;
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -88,16 +88,17 @@
 #include "GeckoProfiler.h"
 
 #include "jsapi.h"
 #include "mozilla/dom/HTMLIFrameElement.h"
 #include "nsSandboxFlags.h"
 #include "mozilla/layers/CompositorChild.h"
 
 #include "mozilla/dom/StructuredCloneUtils.h"
+#include "mozilla/WebBrowserPersistLocalDocument.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::hal;
 using namespace mozilla::dom;
@@ -125,16 +126,17 @@ typedef FrameMetrics::ViewID ViewID;
 
 NS_IMPL_CYCLE_COLLECTION(nsFrameLoader, mDocShell, mMessageManager, mChildMessageManager)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsIFrameLoader)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFrameLoader)
+  NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistable)
 NS_INTERFACE_MAP_END
 
 nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
   : mOwnerContent(aOwner)
   , mAppIdSentToPermissionManager(nsIScriptSecurityManager::NO_APP_ID)
   , mDetachedSubdocViews(nullptr)
   , mRemoteBrowser(nullptr)
   , mChildID(0)
@@ -2842,8 +2844,25 @@ nsFrameLoader::InitializeBrowserAPI()
       }
     }
     nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
     if (browserFrame) {
       browserFrame->InitializeBrowserAPI();
     }
   }
 }
+
+NS_IMETHODIMP
+nsFrameLoader::StartPersistence(nsIWebBrowserPersistDocumentReceiver* aRecv)
+{
+  if (mRemoteBrowser) {
+    return mRemoteBrowser->StartPersistence(aRecv);
+  }
+  if (mDocShell) {
+    nsCOMPtr<nsIDocument> doc = do_GetInterface(mDocShell);
+    NS_ENSURE_STATE(doc);
+    nsCOMPtr<nsIWebBrowserPersistDocument> pdoc =
+      new mozilla::WebBrowserPersistLocalDocument(doc);
+    aRecv->OnDocumentReady(pdoc);
+    return NS_OK;
+  }
+  return NS_ERROR_NO_CONTENT;
+}
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -19,16 +19,17 @@
 #include "nsSize.h"
 #include "nsIURI.h"
 #include "nsAutoPtr.h"
 #include "nsFrameMessageManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Attributes.h"
 #include "nsStubMutationObserver.h"
 #include "Units.h"
+#include "nsIWebBrowserPersistable.h"
 
 class nsIURI;
 class nsSubDocumentFrame;
 class nsView;
 class nsIInProcessContentFrameMessageManager;
 class AutoResetInShow;
 class nsITabParent;
 class nsIDocShellTreeItem;
@@ -48,32 +49,34 @@ class RenderFrameParent;
 } // namespace layout
 } // namespace mozilla
 
 #if defined(MOZ_WIDGET_GTK)
 typedef struct _GtkWidget GtkWidget;
 #endif
 
 class nsFrameLoader final : public nsIFrameLoader,
+                            public nsIWebBrowserPersistable,
                             public nsStubMutationObserver,
                             public mozilla::dom::ipc::MessageManagerCallback
 {
   friend class AutoResetInShow;
   typedef mozilla::dom::PBrowserParent PBrowserParent;
   typedef mozilla::dom::TabParent TabParent;
   typedef mozilla::layout::RenderFrameParent RenderFrameParent;
 
 public:
   static nsFrameLoader* Create(mozilla::dom::Element* aOwner,
                                bool aNetworkCreated);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFrameLoader, nsIFrameLoader)
   NS_DECL_NSIFRAMELOADER
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
+  NS_DECL_NSIWEBBROWSERPERSISTABLE
   nsresult CheckForRecursiveLoad(nsIURI* aURI);
   nsresult ReallyStartLoading();
   void StartDestroy();
   void DestroyDocShell();
   void DestroyComplete();
   nsIDocShell* GetExistingDocShell() { return mDocShell; }
   mozilla::dom::EventTarget* GetTabChildGlobalAsEventTarget();
   nsresult CreateStaticClone(nsIFrameLoader* aDest);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6297,21 +6297,28 @@ public:
                            nsISupports* aTransitionData)
     : mWindow(aWindow)
     , mWidget(aWidget)
     , mScreen(aScreen)
     , mTransitionData(aTransitionData)
     , mDuration(aDuration)
     , mStage(eBeforeToggle)
     , mFullscreen(aFullscreen)
-  {}
+  {
+    MOZ_COUNT_CTOR(FullscreenTransitionTask);
+  }
 
   NS_IMETHOD Run() override;
 
 private:
+  virtual ~FullscreenTransitionTask()
+  {
+    MOZ_COUNT_DTOR(FullscreenTransitionTask);
+  }
+
   enum Stage {
     // BeforeToggle stage happens before we enter or leave fullscreen
     // state. In this stage, the task triggers the pre-toggle fullscreen
     // transition on the widget.
     eBeforeToggle,
     // ToggleFullscreen stage actually executes the fullscreen toggle,
     // and wait for the next paint on the content to continue.
     eToggleFullscreen,
@@ -6353,16 +6360,22 @@ private:
 const char* const
 FullscreenTransitionTask::kPaintedTopic = "fullscreen-painted";
 
 NS_IMETHODIMP
 FullscreenTransitionTask::Run()
 {
   Stage stage = mStage;
   mStage = Stage(mStage + 1);
+  if (MOZ_UNLIKELY(mWidget->Destroyed())) {
+    // If the widget has been destroyed before we get here, don't try to
+    // do anything more. Just let it go and release ourselves.
+    NS_WARNING("The widget to fullscreen has been destroyed");
+    return NS_OK;
+  }
   if (stage == eBeforeToggle) {
     mWidget->PerformFullscreenTransition(nsIWidget::eBeforeFullscreenToggle,
                                          mDuration.mFadeIn, mTransitionData,
                                          this);
   } else if (stage == eToggleFullscreen) {
     if (MOZ_UNLIKELY(mWindow->mFullScreen != mFullscreen)) {
       // This could happen in theory if several fullscreen requests in
       // different direction happen continuously in a short time. We
@@ -6469,19 +6482,26 @@ MakeWidgetFullscreen(nsGlobalWindow* aWi
 }
 
 nsresult
 nsGlobalWindow::SetFullscreenInternal(FullscreenReason aReason,
                                       bool aFullScreen,
                                       gfx::VRHMDInfo* aHMD)
 {
   MOZ_ASSERT(IsOuterWindow());
+  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
+             "Requires safe to run script as it "
+             "may call FinishDOMFullscreenChange");
 
   NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
 
+  MOZ_ASSERT(aReason != eForForceExitFullscreen || !aFullScreen,
+             "FullscreenReason::eForForceExitFullscreen can "
+             "only be used with exiting fullscreen");
+
   // Only chrome can change our fullscreen mode. Otherwise, the state
   // can only be changed for DOM fullscreen.
   if (aReason == eForFullscreenMode && !nsContentUtils::IsCallerChrome()) {
     return NS_OK;
   }
 
   // SetFullScreen needs to be called on the root window, so get that
   // via the DocShell tree, and if we are not already the root,
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -462,17 +462,21 @@ public:
     }
   }
 
   enum FullscreenReason
   {
     // Toggling the fullscreen mode requires trusted context.
     eForFullscreenMode,
     // Fullscreen API is the API provided to untrusted content.
-    eForFullscreenAPI
+    eForFullscreenAPI,
+    // This reason can only be used with exiting fullscreen.
+    // It is otherwise identical to eForFullscreenAPI except it would
+    // suppress the fullscreen transition.
+    eForForceExitFullscreen
   };
 
   /**
    * Moves the top-level window into fullscreen mode if aIsFullScreen is true,
    * otherwise exits fullscreen.
    *
    * If aHMD is not null, the window is made full screen on the given VR HMD
    * device instead of its currrent display.
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -237,16 +237,17 @@ support-files =
   file_nonascii_blob_url.html
   referrerHelper.js
   test_performance_user_timing.js
   img_referrer_testserver.sjs
   file_audioLoop.html
   file_webaudioLoop.html
   file_webaudioLoop2.html
   file_pluginAudio.html
+  noaudio.webm
   referrer_helper.js
   referrer_testserver.sjs
   script_postmessages_fileList.js
   iframe_postMessages.html
 
 [test_anonymousContent_api.html]
 [test_anonymousContent_append_after_reflow.html]
 [test_anonymousContent_insert.html]
@@ -294,16 +295,17 @@ skip-if = buildapp == 'mulet' || buildap
 [test_history_document_open.html]
 [test_history_state_null.html]
 [test_Image_constructor.html]
 [test_innersize_scrollport.html]
 [test_messagemanager_targetchain.html]
 [test_named_frames.html]
 [test_navigator_resolve_identity.html]
 [test_navigator_language.html]
+[test_noAudioNotification.html]
 [test_noAudioNotificationOnMutedElement.html]
 [test_noAudioNotificationOnMutedOrVolume0Element.html]
 [test_noAudioNotificationOnVolume0Element.html]
 [test_openDialogChromeOnly.html]
 [test_open_null_features.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Fails on b2g-desktop, tracked in bug 1011874
 [test_postMessage_solidus.html]
 [test_pluginAudioNotification.html]
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9207017fb60eee73a7789694e7ec41b23cfcd9c6
GIT binary patch
literal 105755
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%0A;&N)`Ed~*Sl
z^<3Zl4^UatoPSwkCnaF!<QSnff`smxx+qerU;1bXOAHJ*ucvxn<4m2v)OF7qub7bY
zjR^u<u6>>Fx%J~Lj*pYFo|qn9RI4goH+4iEcKSRAZ1RKmpUk8mhC|E=zm1cO!t)%a
zGen@~M4kWl%#k-{L4S%XM7S?eO){EypAa0{K$}<9PGtMbT}t(_C0sx#Gga<^a7l3W
zzs%8cAC5Vq(eZKbCx#e3$IXh_ZBd>G!p8_a#O#`resu4m5&S3R)#Q4U{ku3k2$CdJ
zt^Q+Mu~Gj<sy;EzO*s#RtoIWDOsX>#PZ67qemskj^V>8Db;wZRa-m-2o#}5+^o?P+
z_**?kt>Ja!YWQYC8(Ary2XQGgr$_|M2`?53=bJdT?Fstz%!2tN2TN}M)}idwFg?%1
zT%<Q+G^?Aeu&*j!OA~F^N<o4xKbfXb`_Cz80p|7DB6F_TNDUr-coXkHV?aBYwtui6
z2X8UW9e8xRv}#9c>EZr8Xx(2SI?wpgT!HAmEqQ+9Cd*N!^*v>Zv|08iX9=Ah!Mc%g
zf|cxObPrVDYKPLhvKsIF(xQOEyMFp}UY!$LvsAMd4x`N~lF3pWQYx*%>Fv!s2t4(W
z!^${1KgWSD9x8J>4L()gn{)A`js_^LSPen(K+wXcvqA&tW^9_Z3(yxO6_*I#rwR!*
zk#@B5>lKX>I?qSw(IIHYEe62o&EXQ+VOIX09%W*cs^eSTmb71))4Xa=i&5~l|04n(
zB*lF`<#?qKYKHp`ty-C0yAjgb^xIGyWP`+9Iu->xzYHX}1$?^1@#!Q-D+uR+hJ^<a
z-KI}IUmS0d@ET@$YaY}|BHpKCIeTi9h_0JpkWHq0M;a6PG14D5i3)QUglxnr+Otzj
zK|^qN>kI`=@?jmf%P~hYaL8%uO|Tgy|Iki=S6Xk}%fU0t`;J~AkuVd-#e~rW8s?0H
z|1{jceGZLz1PzsDMEsL{x;|JxjY>LS4n|`dai>poBMNl>vZcnl8<tKr65f#MXV#mV
zVepjKfF;Cc!`=_RQ0+1c9g4l8GpPuVf7Kp1>DubKB`0M)dtPk^ZN-V8Q-XuN6`p@Z
zf=h3O+ICQ{8hTr}-!XRFFGzc(#k42%#|B}9DpbXJ8ht&h^$3pP<;6(o8W3^3sV9I{
zZz%Ui=owXePd;(qjT0fy_C;okCS5Zjkmu8FT7&G1cAXtx{%t>7pIsnP2W;Wq=$i|w
zA~girsC!fD{%e8$%r(rB@N)s>=dO-Cwe(`JHSFmDsbH{38@&KI8UNGlL!hM(X1@_d
z^5Bo|fg5@7={Rv@xgDMpuGp+ht~4h{M<C$0*xpJ2JPpfd*O6%Ux=^_kS*jcimPx9V
zXc01c>~a9ztkcbf)bwwKF_<uNkE=l7>??8e!7Mixv{UyAN-t$2P7J^zU9V_1QD<bl
zJtA@l;g0=indksZI+^=OT|$U{l)p~QwzD>LY$jG0%CiMbUB_xv=R(>nyjB*Tv$&O2
zoq`2+d-x=%%)jj{SL~4;hJY58&xg=MzhguW=<#y0Ge^mxT7uQWjMWwm5UJr5<6Saf
z3{zYP0Q`3q4R{-X^q-etW*M73-kBicarm|IhX3n1l1)|GtM#sjzlR8-126*S`+^mR
zFA}suiQJnSqGGFIqi%#cyx9hyo_~{YlhwCijTL4S0ygbwULfAk3#Kg+tMyA>tQ{A&
zc`w%%l&3Cq<Y|ANd<MwRHXQ{=@Gd%w518*(CN6a{yof!rf@K}a>_M<cdga0|3%l`<
zYxCdVp_CNBnJSj(FjYv*X1T%apPvxJr4lQTrt}u)iHh&1e_E6K0)H3kVRgpgPXmwz
zfI0UuK78Lgt0$QMPUlYsHu1}{u<>QfqJ=IW>ng3no}^v49XEczoaEi_Cqs3#MDgGf
zb6Gglv2W8bQ<F7#tWW@9M>wHpGPS|jK?m^E*YeNHdJ0WGgm2I9*rX2*Sf(tuzi5PL
z6|9z8^79aA)9B}(zwJbwwlNm>Or}BH-fda<Hh&eMp5fm=8uFYDBPg`9BNXEXW8-K{
z;nsJVP*E-!N-!B$<WUl~56$PNFz{e<)vH9tGPaJ&t_cwEDw||Fs>7I;H<}0ig|5lP
zYJqNfo-6QJd%qNhxIENTnB@1+P4+t_PmW8YP{{yZmE}P9%CE<jFFzAF?fN~=U(f(5
z@XQX)+Bq<pG$eYB8X8-I^c<YZ-JU?@riMlgX1;)cHwdd8LP4jGt>RXqQ<=-SV%__w
zZikKV>7oK9(a{&6SJ~U!Aax=(N7d@VTJU{a{P(R=eNa3-o$7v!(Yx-Edj|$hqPE*~
zBJsdJ?ny$dXv9UJ>*3_{WRY8O^&fLp$6%4as)8Lt0H9X%Lb;VN?^&}cu%Pd$6G#xm
zLU=&@4iTB}>`CAUTf=4IsJ)@_NDX$E*`z$^{SudM;r&h{$+Is(v#0@g%sQ1C3(HL&
zh!QBVGt_#Fe_~#?^n|1?uAoBRyx@=@gbK#<?8_!l(^KV$8!ua&qyNRq_-Xh2JCmlD
zhE?%m3&jJ?I8lf4fapLc>uwkTsBfz2ZMs}96r=2!nfE?pkDyn69<Nie0qfv;^sb&u
zXHFZ4d(!E1#?6Sxx3(kTi16#=N{$RjJ@^Rl)u1Q(#f|u27a7^F6lQ}}nmB>;{VF}e
zL>R9GFBFu#&SW>?Of7`>m!QS<Aa<T|GhM%B1a<$*Rw*_zYkMrotQ1sF2D9w)@F9a>
z$A%DHuBrp*JX*SW;T9e5Bb_ab+wnLZ<E#8ui|HK)q%k$utD}<XnEHm<APB*dDGt7)
z=HmrHH(NC*dcKX5{_#h;sD(Lt7iHtuTK88&bO)gY#cA$X+iC@m5$Q9XSVqZ@c<e4&
zHnTSFJ<WhrZKv-gyTnHSJTV*!=2p1Ovm?9<qd?uNdwiuZK2lPleVyPs2WZ!I_|Q~8
z@J4OyV%lT!pE@!IzrxFhU7v>=@AnV#bfqK5;TLr430x*kmu%h-+tHQAFb`=>JOd^j
z$RaOS0@c1%Lb4+k6~+>xXOZ~&V&MV-n7)Lt#Eq_fam9nDzpw)Q%T&M=2>-Fc%)<}K
zke*<(3I%9SlkGqmG^qjXhCS=sTgfTi6uwD>s?~(NAdNiy%J2i9I1d!SiXZ9zaFDf6
z$=<f3$s7ULknc}1_JzxtJD!i_9yK$!0Qnt;j2YG2u7QxXUdEZGD}1W<dw!2nO@b8;
zCp>$4^XCFtdBsAR>$J+9pxkH-(l$JWhe6Jy9agrlM8QhA#ROqkTg4Yw<ox7f9M}eN
z%y}5&fgMtD`*psa^7`kr<Rj!O)<sWSK7F>WE`AHM8MoL_XfyV>iPxW%c^hg+#3;zF
zu-@JoDG(L5VaMJu6831vHWNmB3MXEHld~+x&T5sid?9f=V;Jcm;w5`|%uwsp`_~Fb
zy%8!a1fhh+)gNo?^V$K`(9qT<0iFsUwfT&H9ENs-UWX}Smk|hafOxNGSJQZ4;o~ec
ztgyIGuF8N$SFTW?hUhfwK}Zh?wZ#dAsw3Ctw-EbP`VICZW+3}S@M5+!WQbpo4Oy4C
z9*$zESRU~4mn$CUBQ_@>he<MKT`7^l_eW7~$#oe59M~%Nf)WV<;rR9@!~pe*f*j3s
zMH!UC`1u&?bj4qNpc)n7T4maS+r0mX47Nbv-~YG8Yzg-oX$S8)jAx55T7*ONJ1DMr
z(3AKp?lVdIaCrxRzk*qs_QVd?=P<9oW07*9qdyHMYx_=eY{K~|8oN&Q1-Uya5Gvlg
zj)prOs|P`j2261}G@Q2OnB6(x{EAG{55C(v2r0+C;JvD)h_AzYxA3->xyyVM_s6AW
zir(?|_vE-3^%!CSO!=iAgaA!NfaYM*v1QDFlI_bW%ILdeXCZ{8pe;+DH2ajbcyeR|
zG64WVv1z}xEE*)QcUv5h!-zA4;BJhs-yKlG%Pd_Jnj$06V&UQ&NE~QbpjZ%H*}JtB
znCsUjIXE2n&Sh%iw$GY$Grft4+cceNb&VU3ig<0*v>Xm&aYiA==c#8dK7tp|sCu=f
zojphxIKx2b7^0Cb=G@Je*1>Bx4mRI7ZgyZYx6V16B6E%;)tn`iqn?CTn&|r=LrrkM
zP}w}ce+^)aPtTMCJe>_8Fx_Ieyb}6`5BtN17E+X!4lO>Bv`@L3OD3>q0|-%Zm3<-3
zx=Pa1ZeH>H4-7}9gcM@K!iYr3gzcw)31JlFnRl1Qbo4WorBc(kn?zJ<F3UM6#OJ#C
zKOXv${$;q_A>)SY5d&n$(30Phv){IMRFl5cgX15iA{t{z%c(T8E(?B!42h{xOGmFK
zvR$8`bgC;bV1KjZ1oQN|qSvtYQ&b+@|0HeF0dWZ%w0=9eb6}FY-W@H5VKQtY)&Tlp
zDU4y@)^VNxG|K$~kRJ<@Tke&nz$(zM@llxjb3yxj;M*T}-?PvY6+=i?UQb4Ii(=oL
zSjl*Hl#|pFn6RxAsL7!5wCMBk13>1Ezz*M`nkO2p4e^^b3H8NU{jte{?z*9K46Oc|
z%9}Br@|dvK<2XiYmx}5)6#8@PXIref`mqxl5EVoRtkcaF-*7{<_6A3)oC>ACS1D&W
zyx<aPVCNq?cA6T4=CGhivZ##2qOl85H>ps$l2ot%$(FX4-*6b16tS1KfPD)W;nf1!
zx!xb>cXAs+LdqZ$g)cPT;Q+Gm1AEHtUYBJornfO!AjA`*9v!}UsPWCy3kp}dXtI5%
zwQ7HHhlfVMn7{mHrxY2KY-?1hKagIMkh{i{SAEy?Qt_p~Utl${HAM>8n#sMk0)Mf7
z`C4pfrWK*B5BQW3vvTwHe%;b=(TEiPg0<F|RYYI9gZ|1LK657?oQqGUc^b=qDkwm;
zY$huu<^f^^WEk!LE-id_Bqm<4c|c(RZz0L>gN+L-aEF8!vf$R?!7en<ET|xoIQ^~Q
zAHWP2BxS0nQMmS$8$9<XM=PxlY+#jN7)^mpq}g$1{!o?BwDnvjIy6mUh8L-=9SFuJ
zWJt<Djs6FpX!H=re5AW1%zSckgJR=Ho>q$vY&AdWjzHG*lUIJEjD6ks4pOl=m*#6c
zPFw$NnP!2gpKopVC=Eed=aVtz@g0U0r^Jkc$(nki{ab6r^)KOrosRu!-eb)bW<%U*
z!OK72l+X@{{V*jQ58=c+s$A~1kO=g-M9DW2@4$H->#Q$l#knC~rs6qy(Ng>_n+vVR
zV5*U1;z61EIIQn@mcaXqIbvtj_441G592yc!%ANgtU){$Ax#Jk$No%U&RE9eBw2H^
ztH<y6DzSLE02mc?YsVx#@d6$BCMo*(qEjkyQ|3}-@C%yoewWy5LxVs$MGz~lDD3ZO
z#O}$j9X*f{(knJ9Eb-TxlAg!KUh1F&(Ib4aMzbUH(cUcq)bK08Wm+#$oh7NG0aF!?
z-^NHiQ46T$w_61}asg(h6&70<(|+wW;5@-fEel=>4x{=WHU^fMd?!xx7XvSo%4Y7P
z1j}A7fKy_AU2f6buu#sDK2?^G5FxgIOvr@57d=tiOAa%OQ?l*LBvr=s#$qf#x93f1
za9I*e7f58+jWjKXgNH>66jTeuX{J;SKM-$+prmb_VNy){5xLIo?_#@3MBOY=8Yn0w
zSC?{F0SMEGGK`V*z)|IQ6U7Ysi^*6pM<vCI7Mi}m_Du_n!5=a;eJzN^x&}=h&1qM}
z41;rx{vqh#kGow26Qd8KRQANvbDT|lTuN3$K(SnGAP8ZAqT>HIO|ya{kniW4<P+lh
zmmn|U5$6IVi}&+3eP^|xZ*<#z=6BEw_*3Dz?~P8#yZ;L0tNs%qPvF1@$<P1C{Dou>
z;oba2;M-uEZ@EAEck@SQX7`L>xBulg_YM5V^LuKEVV&W|y}ADC8<X~1DP3<GcH!>V
zr7EGQ=+@syK<UEP8D`w~A-y&%*((*1P2IJz$|$`fx3N0~Cn^jb(kr!u3O<Yl6C)!!
zWQehIVQ`{8Dg`I&!@%5)j&9|3cztrCIS+|NPP4ZT`d`Xp+UU)HHPgr@?=3Rml|GHF
zodm)~m?~vlrZWt(O;W0tsBU3@WiMhlz-7z`MM%)MFuevkIgeJ(O?YB_Gk4B%AI4<8
zg}?a$Z<^skRtG3<%tveJRX+<P=kEy3H%gT5GI|+9Fh)6h%HMRQd5~7OxE*o&`x8{#
zx=vvDK+J5wt%($M97VLD-M%1s|2$WU2Q5I3w_?i1`@_@%FNpIr4*zC3_JPuhmkKAt
z4L}d#)jn%#P6h=IeCA%-xXNY4lJ`2~S?oqpt<^6gZ_4zaoC-x5%~;!?oKb83vvCxo
zY@If+YX6p8T^-6a!H4mvZ%M(qUn5zr6)NvTxIR0C3(=(YSdX(3Lc&$DDINC~&q=(=
z&KrP|(e<0$3gyzzRVDx2q&{ds(C!SF^5#!}UOmd^ry@}JT0)6G9UQu&N-RJ3-;>|p
zvh<**a%_VApMWNYE5^t<Vyli)S6YWSO6<`_D(ztIX=}jc8C|yMpTSAzcgurKF5?ur
z=mF=YkdhilQF=nlnzy|YTD^C{v^>L-l12Zg2Oy(5)IeIfZU{-_;lC<-z|n~Q-Y*E^
zg!kspt6#hRP8MPJ^KN}YWC!Td17p!jN?PLwYTpz=)kl!tQ-pUG`MSgo-6|;<FdtuH
zt@npx$E-Q{qJQ@$V|R=LWz)fT^Qr*$Yko#3Gj3g2GTLV#QcR9ho~cE*Y=Y>2jD&5M
z1^}Fh07aHraBy(kxBVsTGj(WEW9Q^L!WRih<UQihH<Tx?eD+vNqLo^=sgYa9lejeC
zK9iZBfy#KNuT5qO3wI{1wu|oc-ESu9G!BwwpE{5yWwn__@98zyH$iZ~LZ^DFC6ikS
ziQh>idoI5yWLSh>wNL-m$Hbri;b$tWCA!}#<}$3Kp0n+@xO@?!>Zkw1-Y&Kad9x9V
z2S46{b=x2<<fE`;ooI}e<G{|sT*b)8R`}hHAB4pZnFX!?XzkLwmVjKGH49_O2X`rz
z7%bK#ayuhGD;h`4zJ?|K=bz+5lM{YvFRiQ%($ZXg-EaWEYl2!680}~D`$tXe*#(r8
zw!T>FP&jI4G!*?yWcS9K^%}EZ9QIBZ8p-ZLgaE<Mn!p4OP<rX^Yx;xcp91kZ`LY37
z>dr1Gf0}8%NY0jT;}5d#au`qM!@#QF;VqVbO<;9VM!NH@I}pU3X3_tg(+c>Kc*GVu
zSIVyZCi4L==fWZ*ufF?Z-1hQ=%(a$15Lxq{2q*E|j1e(2fB^`6{ez<1&-dad#k5w>
zgXG`8?WlJ~N;4xfWN?eJ(aNGHg|=TGBP0J#HLyh2<_PetY3U((V$ry%)>&y2Bylp|
zj+22mlPO;#73R1SNHs#o0|33jy93Z&pjkb7Y}W>WG-aW{#$9gt60e>9l#x@tjGoop
zV$SOl{7ywI+*DN0;y8J<WX?BsJVTV)E|8~8kVPGb;a3}v4>oREzYylYL=vup?x%D#
zmbO%Lu#gjRIyea#V3a*5%<XS^P`U|YFc8*3l$zwS*N-L(mO_PB0BKk|Ck^QzV3>;j
zOZo<xwq6VW@i(MI1xmi7QOvcFRbB23gs>Ii1>Ijz;nDG)1;z3U_D=}9?hhr`NneQ?
zCb+lo*RxSSGC#Q&|CxXFONjw%RMdm}A@0EESoBPcHXWiYHz{!&C6atj7`O=;EQd;!
z;=kZ=(8NM+sA4aEuR&=nLZE|)XBli7*%K=H;>snGFtXAd==)0<_(&}hqN=q1&u0iN
z82w(=Mqb{T$75a!0_9W{;B;I49rJ!T^xiU1J*ydUhfPPgWln$FA_f;uy323`av(^|
zQf(i4pmxTzqt%^^^|F=^@5JS_f|5(ol+vJ9j;9iD<V2A3N$KARrU9++Z{fJBmY&N>
z<Rs+{Q!S<{rEI^)=Ve>g+)i<l;DQDy^dZE)874Fo_xez)(wLpEhz!Hb(fmp<%hyZM
zVQ#4wcI$_wcmH;R^{3c-2*C&(`~)aGkZG2U=ROJ0m9lOfjYGvMmGx8my>11J%2Umk
zo}Zz(23I;26~O5=U`=UK>Sro!zEu$817v@>;QPB=H>pDh>0*W}cbe3bjNWJOO{UfA
zYa)X+3{G5kTjlcpnFdiS&A^W{N8(+{)t?2<EF+{AImoD4<ea$byOEKn^nB%1erKdD
zj8yCEf>HxvUTxe-DP}|B?kIzjny{FKBI|L^xpt$TcT@Re8j9KJI@Uh$rVh?;%Ec#y
z=*a7-!G1;=X{vx3?h(eepn{%6-Yhv7VlwFC)7TC4!54D}C<ro($kf{Amc5Guu<=_n
zlGB1rw?=zHG5SJ_=ftr+(8UA=-+-CYLxD(JIY8F?Ok8Cld@^@DNQIov7A{GxzsEdm
zd}Z(JNkKz)s_VyxY14|&-<19)QF@N{zdy(3Y}~sip4&->pn2zc3?FkNx1)bR57w&%
zt@rM$2L)rTZ#%cwUN#&z^?%GXFNgP=B79!=`nlH$wWXaOWwYZ5e16UXX>S*3<vUR<
zpIHp+Ix=*SydM^N>SH@Qg<gME3m9=EI8}#-9vp@F#><gA3SG5gZKEMbV3wkWQX_GH
zuz}5tsE#c4ny<SG^BGwopaS)Q-+k~XNqTPl?MurDqyZy<dbHcbF^Y0-@{`YXyIoPo
zf2#@5%<UD&BFfR5CTNdDrI=tdlg_1!Z;~H3R<b3x>(qIWh8o*cuJik;)u-Y^MOou^
z=Vmz>&ouPWMH#BXzA@@rQXOkZ%)i50vkK!gR6tg$#y9A`TmK{~Q-y00G;T4ERg1~;
z5C%b{pP~C$9L!;14z>va(Y0N&4xKuX!ky}ggS0<{-rwG-ZIHaFF_lM~5GHL&-ex+O
zKdP<CByU)Mq=gXR{;3bGiSq!m4Y;^Vx>u&L5W4_!BjVvw$2CQ4WM(@RVSb6*|BLya
zPC>>RbwEdz=~iZet*e(6s#B4d=3f;ADF0U85O*{&;k{c$T);%Kx~d-BIe*L{00mB7
zAV_R_mH3zV0b5a8L*$4`b-Bn4D2`P&O(*z+ppI#_Mblz$oP_>sRd4`W`e6eJi;`MR
z&)c*-Ww2%TH~X&u8O7i`wGj{}k}Js9roW_)!SzuXjHr!&>RYn%@O}hzoo|=s8E%j0
zR6!K>P$P5JFjcEvz+V>s(0!<0Iir#Df+&b8&N{6{_=9?K>kW2XeA01T)bjR?V*T%3
z;cPa}_=|o&?XYo1XbGYULN7e~BlEc2@*~!LifgW5{zdz#A2;&|A|=)|T0VU*THSZI
z2fsmLe-?D;U&3`5df=wa5~Ec6V%E6+k3(JTInrZKwLkRM4mC^Nrbwojh2Z67m;jjf
z3jHbFzWzI*UFza^-?u?wH_)YyRr!wOL1M4O&KtN$8>ou9a?K2mtTSx9)%O(R6<gwB
zL(@pkh5AQcZYpe;8vpKe7wghlih!L&e8B|p<7aLamHO5(c+V6B5~7<^<Bxs8>Ycu<
zy^2U4nj5cTaJ~dcZPurD+_~fv(v;aa4Cx8{LI)NB_!MNV;wTg12jybWP_B9!>LqQy
zDtB@`$G!(2nx)l7N}_e0)LQX`+c4=}eRgOEsL7j?<4{8xRZD3Oi|Z^9m#q!4=vt4h
zNJD{pACLQ-jT1Vl%R$}osFlX;ynF`H;y<^a`~OIP74m8&*RZtq93*`NZ2wS<ViI%>
zuyMHH#)4hU3MROERb-}1qR3X<b*_3dL&N7)PPAEv_AaozytVzVIoB!)JQhBy$ogfG
zh}4Y`RkXl~l?QB*3Qs79I1_c|gr%C)$_m*+YS{j0`$cCq5$;;ibR-y-7n0)bOvsV`
zomxugI+w^8_}f)!)0CU_Ycd3DrOw2k@p^&36dyn8%BJ)HEN;QD9%dNbM@!ZN{+&DI
zT1>mq3nc8+b1=qm4$ZkLYfYiiMk>Yo!`yz|Hy`r@ug`@DFZ#jAsvmTlMLLG7AS|a<
z9Fb1Mocn{ey>jLpy^~GeO*!ajXI6+_S3QkY%unZ=<y4E?5!jBK?#rILUE^;q9_ki&
z8~#td`GI=epHD|@UfK~_cq9!VWXx^w<VHj~2PWbe>ME%U0!oTMV|x0p@Xp7#9#oYj
zIZREcSR5D_QId|oX5X7y!(KJ~CG`u>9k6H>oEL@al%?v3qNm@-NnA%#|NLxrI#peH
zqU`hXliVgJ!V4%CcMAj&0w@vur<>Vze^b?g(EZDG8XxdMOM(!WU+w?y-^DNMe=p$5
z58B`4-+meJf1xvX;WEFbE|p-!ZbeC?HwL^e3vH!(eqMHp1senC4h$u1jgQ^Q{FRH2
zBgG6!GkAyrs1}jINql$t$+-p1e-5xw+Mo6sDC7nrHQdQj)4`Vz6mOfNva&bAsEgNy
zE}yHJ{vduac*v*R&G+hOa0g!+`0gx@wnz@rk5=m!@HrP5kw8-hn;V8XWW8`!UG*=X
zMv(|JDG$TZT#-815_8(>;SAiJ_+ejFDYa$bBn}L?6Fh%%Fp1;Xe*tdgrE$I<Lyntr
z1quVzo?W0peN=z43DP1zd1Bp;ogQVXVPSxtD-Us>d<B31HsSJE0?m4!_6;j6M+=hN
z_Z4<B>+=pZD}KjV)z05;u9>O{J?hAg<Ydweg$pS)&d`CbpRFRK>#gmvvGMEDEt*Je
z!L*D&Ff@!!Va{ZaNy~@<aP9jAXM@cI%lkq?9No05TT7mvn3{SrnZgg72>%_&W-Zw~
z<2Zbvw*(>MEP4AeIu=z4jp1^RKSd>*zG6FsDs=(*dHhFm>Jy^#QS8F}l(tEKp6G(+
zavUKQQ(zY_1oS#Et(E}rGEC6CglRIw%(|!!{gDa018!bTI1i?Gk$gZz5p}g5{Mx>Z
zAm8ww)L2#_oD@{op-=S7cyp)|;E{Y<a_vqxkuJ-740U6D0^01di-Pz2gU0;ZbJu$a
zHKiy<R+LvoYUd&rCMNtHbwEA9b0thsZT4#Z6hgIlet{5dWlT^=AT&DlFln|MOOLxY
zO(BL|7FIns-E%SogeBSEriF*iTzQC2lcGJ3A$=Xg@nei3`Y8e_UXXEdWP%uu=(0=4
zfM%s=c>MWm6Q8xGIi@_mpHP#o8XU*-)3+vp|B~Oj4>h?Wt1~WejF%p0i{a)ga4C66
z1^cf=Ek_jvJ-4fZ&4&BBY*-6<WbC<rOedJMIyCfIWk5R|Y%=}VWgbN$QG3QE*3QWR
zkC2@J{yB=Jz&IvC;d<lFE+{vpYI!wJq3;j&nm_bEA`^b%#XKbUkU<*O8qyRb1vH@x
z78#{iN(=_WMF@RK(bC#r$YK|u6T5>!S+b2PWGWI#VD~^Gp*=P{CO1h>+S4flf#kBM
zQDwKpiQJrIF2vYoBkMAq`>m$54j(GqwZ)q|lk-#=)hh?jq$P4rwZAZ!Xj!TLn=R2#
zRRM;-Xd?`e@*J<%Jqdw#f(U2FMnJYCB%TRZN=a-#&5Of{g{x|8_OA~`rre2&?nntf
zH1Y{qY5o4Ll8Vim8h&ZzN`hKe&$wtU_Jd59aosJWmzInKYOQsc37t!&_($(FT|j_i
z(RJ#W&o6ufI3-qJxy4vT<z`wTyk{oa^b}|wShmKg<y!l4@xclwVtZ8;VBWK1+OIg>
z>T5FtkJJ{lK|Bi$0`2OgnvMbcp{5<~^NFHOS>y>XG5xu|LN5u}5^jmz<H>bKf&<a3
zo&a?DZKdS7qHhP_Q=fA&gQ7&LTxSi(ks^>rBD$H=aCHxXa6FMl=Xb)hHxb;!omwJ7
zWE@kJ(AZXYW8hQOo_|^WIqtMYn)F`qY>u<H`3pz<=I{rrPUob46E}Mfmw%2G(~7yU
zINU<u;|5jXPQ7N>zc@}G;hu-r9$&bh;hb(_<ouQ)*EHAV=iSBthn9J>6yJ5vQ20XL
zMWfH)V4WQ~my_fk{a}7Alz&$^8S`O1N?+D92iFu?cG36Nt6CTL+^&ck;w;<h3WX8;
z@pMNm3<Xl2dfU$P^?uG=;th>%?h|4+oz=%aLw9+TT5bfY6q`RM(DDL=wl<i6>17pR
zddA|3#=xEVHfNI+Q+QEEq%#z;MISXKHNrIJi5qJEOttFlAnXJ8J{5`Z0&VD!5s?pG
zBgS%b{?LM9N)<UTB2ajbJwo4RQw1Cz2=mNknoWQ9(`VH4-Sk?S<|r>71_g8H%O}Lw
z2-zUw<%6qw7kUF(%i|kB?r76`RS%-YC#Lh66yNplV@*A3FG~LHl6~8Xq~^_G)MRg;
zYxi-^k$%Qi;>!e<apA5H=G4o<#P?X4BI5#OcKEhv<40I@RHu8pzZvGYL>&C&*OXir
z@t#M0b$<#^h>?cx`doT-SiHr#<j=w_)X-8QP(5X?1y$r0EEYEi1Th96{pYD#O)FUC
zc&t4&L_!#%)69R?5Sv<4?zdqrW&CVTR(wj=P`uA#XR`kr>}N@{jk}vWNh0BjRyNJg
z!CxbRC$7+A3h)%o0t!817&wyPJR+9LNRg!ANilAJ2)x#Po~In>t1;+gFkd66sm7!W
z!L-B^R%=1G8eV;&Up;>*X(I02Q(E9!5<?|d``^#~jKKj*z5xw)!?&^?59S=PU0dVe
z_nV?X(#OA&(1**1`V|a7o@1euOX@B--<_a|Y8yI&fz`<g+jy|ffbG>^<KZp4Q1+!g
zXYJ>4oKE8&S0~I?du?Jf_~9i0bJ%QaZrwby6kZs_GP})uqnROf_@5H^t!jnI(DB+&
zO-iOI%V>_7C+GZ@&@hqc)f{A<Gf2!q3b0vw!+9|tkIj;prIv?Bz(FC9+5{?PKk<f=
z#h^%`vg9!Wuo{atxFCeiBLahcvknbGcO??9ev`_^DqZX=q30#()DrI@AZ741v!D|!
zdh2P~-T<TijaJO6sRzHdY1W}3!2~6%2`Z=q8z?*poSBnV$S;fwryfoK_m*bE(^Noy
z(OLef=Y`y?g-dlB<PwrGS^Qu=xWg~5M1`A9@t93AQ)7nI$C4F1bHu2iL=OvT#1+^%
zlR%SMm2LOO2X4f6p)N9TBu}dD9Iz7>;G?b9ZD1o~ZqqvTW>OMkNAw)^-mOt1_2fFJ
z`;1xr4gEoXy2=&Bo|J3sIp(vM`LvK}?As4Sur6&CfjIP96HKt(AX;%o4bE}uHM&;!
z(@l|)e{>?jEC5o|P0*fyd9I>MW07|5$erV25cOcYG$7}iHtOhAxhPebCLiHdSMX(n
zDp}OC-*n1>jG&yq|DefA9)^|nl;P|S4V1BERgM(wbf_Z1d$+vc8|2F}N!fUnV{=UR
zqBR(SvhXSJuEcoi`b?H0!tNzv=0`z#2q##txAEKYgDcxx^<|X|sRs(<xMFj6NZlBJ
zEk#4MM7f|O=lVRX5|qVUG^>_$WZzl>T>J8xCG+5JEch`-K9CZ(b;G7<_jxh1m<-PO
z_RrK-SaBoG1Q>RC8hf`a3f_QLwgjJL1vN!ix3<&>9Z&XL4p#m%7Y0)4k;{vH^3_s8
z*)TN|G2(@qQ^U>T`OPsi5u`==jFCf6I_>%8h1D}f1@<vEJvMZ>TasH5Q+*!Atq!VC
z%MSQY2e*cQH``c1+;ZMh$+C}S3D1GG6m=)w=VaXylw-;r<`pKy>EX^tINv1I$PUi0
z<f#&$9+>rW$pK(JBuo+ZpU?L6Lz|;+eJ$wTF0O!KbCJ4&PwVsCQYyqeXe1>WRj-gR
z0x`%ZH&T^OA(T9i4h^K7U)KJpn?Cfz9UqAsazPD#aYXf@DA~K`ii8m!J!C*c&ZGkm
z^-x8_gXow!1Fi5N=A2Z+j}w(JN#D6yo|@)f92v4HTh2q>UIdj$xATm~hdC5CM%8S@
z@;m+!jLCp9%%k^KAXw?UVY=H`kZs$cIxyAb@~;O6`a4pp?>C|xh=y_1sb?T;Wgu)R
zk(O$?iBtlX;Ye_s3LmSeVl3!1Q067Ri9@5Wb*J@Bnp>@~EK3n|&8Uc@<5>8Knx0*{
zDH~P^>(yj9Xt=g>(=@7bG`R|2?P8An>|u4ujh*b)Ess$UDEUU0WzjRh#@&=v^5EY0
z85j(7ng_YN7yl>&l|b6xVkUvimRs^AkHnNm%Z%;m0^_UJpQ2ZRmKP9ZOwk#GAmDKi
zD+Vri58a5bN0OVg8Aa5eKsi1Qr<QdY4Z&yLm~Jutirp85`XD@v)x+^Nanf=7=Fgy>
zT9Gu1-7g-5v$;EDMDIk9_RVsAji}OXmY$QN;|O=<oR3K+ACrKnsJ5Td^6XB*$s(Rm
zVUc&-doE&JN%R3zm&bpq)+9djM72psGnih==`||hp^WMUoY%lsovb!HmIN|yoKT!*
ztkqkfqurj&2@5NWIl%E<KJ3{FY7k`Z%!}IP<a+b|e(fBm7ZKKlWv(g$PSc(!K@U+4
zE14o{pkjoA5%PDfP?k)H6--@|Ba~>=h#|E~^xA(%3CQ&`Vm_Nw0!oZlSXxZas^5Cg
z@E^kw=z{yxH+@dUY9X(HNdial_X;+$lV#leH%Tej<1Js&c`i@WPztnuc)HpD5C%X(
z{<knN6<DBlG2B5qR0X_eG;r|WSbK*cU7%*oy6jW7ZQHhO+d5_2b;`DF+qP}nu6nD#
zzH$HV=*b=ZdzSMZvEqr$T<OvN->+FJ)HPTT&)fU&g6>9-+(4BV0aoit;A7$0GmvhI
z8priQO$CvNrSIW+vws%rn1iQX2<FE-b_s|ED>S0w`D2+S15+}kF?>4d>>MJGd@*RA
zb%?+Ya=}tt*;|5P#W#M=_pGEpeHcqxUEtTg;y8sWR?|euA79IkC;qGWGy&-#a&-8<
zkx(hEJz?~eDN;V7BLz?W1FTH0*WE|Ug0;wXY9)-Cwly62fFWYW|D+Y+W+x0A$Mwyx
z%9AuDd@|BJ<Zn%7D?&O!Z~VmZ2p`7QjhL4#N(~9|gsVzo0~BeJ|KObO(^WHoxJA>B
zVeYsVcIB}&%_gdU>pAU%t(4gvQwA9trNylCPQ4%xwZi?5zDWm+_??zFi}D*74u1&<
zkK&2#9lmnjPjMFaB+44@J>Boqbu4q^mdQYhJ%-4}9bpivL#CC#{j)yACmR(HuD0*k
z?4fDkE_?(y`FA{9|4`yctv<>XB6QloG0yL0%NgfcLl%a3K|>#Rj3PqPgs-U9$G_b8
zerR|TrZ~{oNnF|+7dX(~igDvXuvp(pt;#!VVHRq&mk>FBF{dwi=4E|@A*9+VPUg-U
zqYY)g8@OIpoGJ4UWpbo)DrH0Y+?>P5T|NJf#}v0%BAbSjQ9_v|Q`X<TjqNwwP*hWA
zXZs7UNrKgS&fdjU>R%1ae0ay7S}FbE0-b}hHjnX5tpJsgSmeT{To|wD$vGcA7Uj0T
zG^21oTX+)syl^IeLrYp;aL71sVU1^Pl}y8?=snXukYEQ>M{OL>OBxEzteul|v+xq2
zG@nr(i5%lHLc;(zb(Q{~k#{?vXgKoJFCGQmbZ$ZWM{k7=zPpJNdYM8xbkNMderYMw
z)Dt95sc=`9zX4@tQ(dGHScI(MQyV}D%L8c<7{S`98{Q_asL1fpng#9#Duk%(#T7&^
z7+Z6E@K0y3mrT`z9z|{bukhEq`VbJpDg{BPlTQxcC{jwKoJ4}oYhOims`UR@4Iv1v
zAt!`jP==qCG_X>(^;&khggXgNX7dfkMEZ7$K^FYbp773yixC8dLGmP&yfsu91nJrI
zK!$YaFr3ejaJy772qH}9?_)-Tb`^ln<s=99P7S=bIl3LLXt2x~MV+bOSn(s|YWYi@
zChWFw1;=@GFcZ#LEHaclX;FzQwa%P~ipXCM+<m73J`8<&S#`+UvB)i{rlD1NRLJ15
zBP_cqZ$zW%-8(<|D6Q!TI)fE{1~0HioxDz_jUQ9V?thDH&-5!IDDp@91|?z*U5RV>
zV``vMrz>s}TR~qLO~IJ@mygYC*F9hRThUlmzqm*UP8A#6+(Xe)_A{pf_U1rjYMW`b
zIU<5)(=#^{cs=%~=Ohf_iNmnl49bAcRf{-;(iU$I#lp)cDLlk{%>9zSjEZ@i;o#?N
zA?kKB3@>&64&mjU%H)_5Qr>jrMN7=9);zOMUqggZvM;92vv=|@mqK<JPnE-W>RR2v
z*WDerVDssFWj+2t1@4#XDQ74*ZMSju`LHPK{wMG7*jc!O>ia|t!U@}Qc@&+r)|q{u
zNb<~NU#zq$wyH)Ojn+{FH@Zl^2{uG>=0iUj{ct|eYZhum3#ovK7Swwow)IRaH2N}1
zxAlmqtatKi5LyG=eO+#Rfo!wSvQwM+uw3BmevzRy$p+^=mT^&C@h>e#k_aNTb4t1U
zu86y#JO^71jSvbqDk&RdH`&HkLsTOC7dl)bYaaf_&^D@Aa8*5z6S6T4a}!z4l=bAa
ziOEKFlgpTB3Uc1_P^(PaDjD|OQw<`8!~1X!V%j!|50;SG?!NacGv$4HPJ(PstrtjM
zLCHjHi%XuJyS$h<bOPUiJDQ+pg`h2LpSdJ(MZ*8_O2xeD1UY+bL|TZm)m~T0(-&Fg
zW)gBLL81=axzZsIg!OEJ=3lO`sX-@zf2Fm8%NzJb<u^iG8Z1*V%$CMvSL<p1Cmc=J
zS_D<cMb!DP<Qf}i^ya%MNEyl&XONrj^TAaeXF<H(^r{Q;Ge=ksl+!*s%?QN$*T^(L
z(IMh7p_)^JJbDh<hN722{Up<t`(jN6mf3nrhl)a7`M*^Dpd~;~|HHIfxv&f-uViNw
z)$knPk^o2VMGSOMVgm5E1j0qnO(FT2M0sZGj(x?ixAbGQuW<7_*q)(<0}t((kK{zN
zovhGKyg9_7|9i_dv}REIs33tuHh1u@Q$WpTS#Jc%D|!>{L*sPpWSS}6-H}zT)j@Kk
zt<aPjx>Ap?Kg=Mbm8<&M+_g*b!3(@a!Cj99G(_!B@s6g9m7nW?PdBJt*E_Kt%-E)!
zw~tAHdG{@me%Gc8mOphN1_;UEnnO`dL_<j8)U|#=$Yi_u*s({PUj0}cFysblK~wb`
ziOl-Z)B{0%%h}*OPaioOQlY1UbtTo@tq(~q1wu1v%*9kAPzsw8Ap>C52mb+rAOtPT
z4B}w3lem!V(fh$S>c<{RB6zD+$get`!$$TI-d(}8DOsmNFQAY%d4+s%3h}3Q`hGvO
ze5+IGc=gO}#3RFf&3i~^qrz(5qcR9q&Wu>EwV*1>_+4wI2*1iR)T9`*@y{)#J&?M@
zqPraTc5@e6C!P_al~5^Hlr40OQ)xlS-gHoL=0`h%`4*YT)=Q9TC6&_lDFm7EuT<AV
zCGdLxQQcFm=+KX}0IR9%_k`bW3Fzr}c!M;g-Zd#Uozvfdz+d+U8*3#4JeOwl>Ui-V
z0AJcP2&AHIwkGxYr#nP_-}i@Uew=$oN-@XC4*yQucwA?Ch%TxVS7vDx+3QmCG<wLr
zx)pJx!7#V=F@Q!BZ79MMp(~?m(Uo$$WSURal~bJ`wtQuBW07r>%A{sz`KyssDV$O5
zcY>rAeUP^w*)C1p4EY=-eca_FajkHbv+;5JmR{(Xl!!#npj$t`8D>WBKqWg%6LRP(
zBnm^Nmj;j<qyqbu1-pe@yNZc&fMQLEmeO-!GfU=Z=;yHv7!|7s8}mBD=va0&+=Gn5
zfH&wYXYJJXb(i;~6Lsx^oZ~p{p_a4dk)M{vrR`KBczdr^xCI|^ZqUYWif1D&HllIe
zbQjKx)c6j<R)?C?ogMEMm|BxX{{a>uhGKL9`o}A4GTH9i9jIN@E2XA&XpX^z4Y?Ub
z)x5)_&}`b*Aj(+`7K#-bfKNGlYP30An3M7?ilu?fsd{XT$l8$FtkC6m&1P%%jW&v_
z0C`)lR@8>*=H8(JicG&VXr10k*vaEXwcROTE@nW>)85?kZ_aiet_zL9jdnd+mD~g;
z(Xp{dkZzy0!Q)7%07dYVEuN2Hrl~xuHuk2}eveEBQ;D6PQztvEuc`Rz$Le8`u$zxe
zuec38ZcL-t=+e7_c62_XXxtFe)`tln1)4re`A$x9;wC{5Uf8v7(&`>c^!u#jX_7|7
zeW1imu-A3Bgx>l~R=CD(`#bS&q{>{v$P<hK_dQ@fDkc^^i=O7H6r71P0q)P4a&^Hj
zY?x8bZK#v`*wvdM4Zz0=>n*V}B_Nz|Z8&u!VsLjtpJEmgtbzA_1$5ji$hZ-iai+P@
zUlU5P*_9~BTb3#vbXTr3aKNz2?o1!4Eh2{mApk-2YLCpQzENVs>)f`kUxm-}2dj7c
zQptOPsX6*>)B2kCclfaWcvWXKMH7Zys&=gK8JRJ2Ya24VX@J`Z&=A`fT|~$^u*L#K
z0SvX|cMjs@^LLU{Rl<pS*vqa-KT_3-pW3>$1UH-z&4ubxU6x_QvP7U?FjV}|*^eer
zuv#x$)~CHn(x6ZZqV$}ksBFOhVd-D_J9{t5K&JFsr;OZR(wCU?XgW^YTuj%~!h3mG
z<d;=`QXnd){P#{2saEs5oRYz)p7(SJxx~yp+jxHGBvt6V;CDJKI-M{zurGlBSf;U+
z6-Ev#Yep{y4u>fu)NM-f3xiY^)xWXdmS1$l&ANP?qiTf;rbmHtvXa>`&uJ7wxMXj*
zQO}`vi6?+Da{=Eq!iPpJGO=NpN<$)ur8e|2FdlNpg|6&~ZruL(T{BZit8I`afJgi#
zY~OXLBO=rSUqc6esQjifiop+XrCrg!r0R;dV))}gyop3SE}Zd9)m}>Ee|AgowM*8R
zs6LNW_F~~te|3M-5b<!+x8s_34sy6DCQ4DKf=c!mRtbBshj9u+h{|!W0LMyC&~x_(
zN4FlKHGm-MKP(Xs1ReXovBY<PO)dBgXrU;)%4Ge&mo()xU_nwIHn4;^4g8WN!|y|D
zBh7bYs|ZT#r@M`hL`Q3tByv}iNb=}$deY=e3vLEvo-I?q2I_vOj_(J98~;g)W*W;M
zR#JCRjlJfPnf@-nM8y4WjBS>!c!>$?bns1EMc&8DBT!Wu&Bp;d?Lz?=Xsu+=ikyHr
zqOwK1Ex0Qusm#|lQ6*;~+RW-qKd>=Kq8|=(3MLJm*7?8;sZYY$3u9%dr$NM|UulWq
zlOHBM{u4{gnDU(FS!nB&^<a8T+Cwv4_FXyD(xx!D6);mv@w?w6lAA9qJ5LFOSck1Z
zfLs%?gCeg;CX>fgAS%lnAoWRd9idbRCzw|J(Ts!*TRV<vbeH+8u5_snO6aML>7CPZ
zJdy6uvUVMFcpw00FfFmCeu0jwtrvPi|I+Dp*)OT?Ok+?CR94Zq_uaO3?p3*3oNK2L
z=vMbLr%`I>-e3U|0KY@+th7JIvPagek`D5-4=jEz2S<&4suaxMkE;lTWkNlOaVcyP
zaFk>Km8MeP2oSp$)LqX5qk%?zwF@6$um+FLJ?l0q_F7-p>;w&sCvjCA2#gvyqPQ2j
z&85rg@@Uvb*L%UBjTPRO-{lnLGhwq8QEYy{V~Qr~IEMKfqaCvJXiEiPtSs5wTyw33
z^iq0hptP=6g3xFdiFuA4d(jf<bg$^w$}@4I>N7<y6*3r#lt1Zf(`6AurUE(FvO&>V
zhtwUvQY$=2-w#bDIcyu%R^Tk_6Inv@^kvguUVviz!6gZla^Z}{4#K)pi-om0pMSd0
z!#j+HKQwU5-vM(K-2!!H7=)r{*}%~&9TSZ+Jz=>!0Z^#wy44L;PL(%~r6wCJIfaGH
zzOHOq0b!t7({&bONk6@imaeZo$Nqn5ZN>K-AbG+iB5OluV%>rfsw2-w;tS+x;v0De
z0Y2jMp9WOEBE*}-_1ya1lZHd!kT83;`QxZ2ZJOuz(ANEZ5KBiIDypLHCn-%9<$}Bf
z*G5d+nvFJ8-L;ywm*=sZxC{rmfgHm%!KZcw)u~WKz<6L%h&49^T&~SwuZ>GXb#W?o
z4nXeuBlASA;9#(=4;PcN1`H@ST{r9|<c~^VRY%uLu#d4*W+2%F-k-O_ezA#lB+LUE
z>f!`%2kNDNDL+roT#XDi%FhD>k}<D^oD<ec&f50DRct?)FN~=WMm&K7M`eKfcpv*}
zNVi;iU8mHIAlMW<pjcdSJu-nQ;OjawawA~|L3TEYTj*01Ng<wDNmf044CE{;K1SVK
zx<}GZc2Fs#UuQKchvRTwX&C`Yh(+QN)=<lSSxA?PZ;1z23qgpp{ujBYf0nhFf52Q|
z*u6hKJFyIPWIyvXJBOj~oki;<lr~%|ZuUV-l(C<XrGUus1v0|m1}lZsI7)gV>7a6R
z5uN+y)E{-9iy_XdF!xL8(l>*aV1~EFrj<B#luocfS9Ux3c3OpGn)f)mAPzI>GQQ2N
zy;Fy8aaZnjrEiChMfBwy<O0pqOj0#Y?RZh?k%Ha%S~Z+OZ$Q>XYU4>c@F>4V)y?qM
zs5~lZAM{QP@ilk;h$&^8D`Vx=0oVBNCr{v1=7j)3h{NKCa?L_28-bUr$YtFm^{)8W
zx#SOL#k&nY30m0@(<UTJmTIIZ%g1g-FnX^hN3X`(xp~DpS-)E5b@8Y^*3;%B?nyiD
z>)GF!Rz8dNAx#xpse+NbXna+k02(C7Z>s(tqXSRw69YfNKWG;a!&FhtDWRuTrY+<4
z${Q#7;gfHS5Z?0FL2L^_)u<sN0x$4sdX*aN)ut)rH1a6#8-W0G;%V~si+2zJxbU}c
zN|b{&XD0?v2KOc6r)Q2))a-nilC_(9Ko3z-l#{zFp+}-detC%p;7Li4l`lysg@(0U
zA7kZKD557Q21L$jGK329-GK3f@_BM}2;73IgZ_jb3t4$|<9(MdK95hdMYctxaJAx&
ztS|cNal9_X!Z+18uNBd;?Q~<pC;d`?b`y`I3LsRg3USaDAwb|gP^g3;dc$UDu$`_U
zryq;9YGpo=?^!=*IBoTP|6WFp`9*R|)z=v@J+X}K!_H0uz<d$3R}}Em7!3rA#5Dpz
z&w&>HKaiLf3MEJ#1pdFv{%*$6Oc~WT9jwjAyRm((;uv1mI--UYf;D12StJuzFLo>j
zd@dt(FrQoMJm1Os1iiPcmZO9<#`+EyK*3@ih7po%B2-lfCy~DwuL>R6aMwE6SvR~6
zN#h;Pu>IUJegg`}t`wDQ0f#b6sISC>eY?%nmqW5yzAPLrR8VJE<1=9e1Lr&p(5_Ph
zAi8|mSg?aaC3{Egh`Tv&**aFu5Oc6NpSuci8jLoXcS4!&HBqGFck&*PduXyqTt-B<
zzgsh*L9ie=#rlRdsz!~a*#YCRgi=}~i`hVY6TKOXkvuaf9GUK;KiypyW1ZeR05%F(
z0U!@vjVRw^>7Zop7<B)x($a8KwPXen)lT8*XYYUzDcT^MZ_HIkg$E9kwF7XpZZx}c
zI+CC5p&cv-pskNZIEWWa{K?fT_?6MFv)<rbsauUFkP^2_mRf((&P}19UtbZbS<+x}
z-LGEt(Mma?lk#Pt(!1$M6tB6Cr(p7SL`boU<iLOvuG<k0mnT=5(~j37sa%}|k5|??
zOO#tBj=M`;KKK)UJq2i^O3v@Gg1W^xB>ACPNxAlZZo}*QqRKc#Qgsoh<VtNGJH0Nz
z>T;_&FDYNX;-l1$^)t?C(mGuAOuy8pM1jCsb1uazUtH~<&7p7{?g$OW-)>vMl!|V(
zMdl~P3FkIs-SlJw=ta-Y<fy|31a4|@*}I=UFbG1KL+LsF7i|DYwY{UJbg_sKvl8m$
zzlW&O=b3UW=SR95@?aj3dGas!LGb7J@=m~7T+SWMI%&(0Z&F<-fFL=M;pONjB*E#-
zw4L{Nkb;}&62NcNk4ImP&VlmgnMV3TD=h}`^}Wks@6<kn^T$@?5*o;FdINka*~mT}
zSjRn@wnbS`UD=u-+2})w8dd(Z#GKI8nU*rvFQi=2s?1i@m)Cbw6I$FqEiOIte7@>1
zWhcLjz0R6*`HTv&5X&gWYNFp!H2u+J^?U^(vd%!DhMcb*R{zkP?lEmIe^4hkp_*7P
zoUdyhS5w5~FGXD%7p`}Y6{+!J@(lwuRh%)Qq+3KdS`?CUAP40=r7tEi1xq5@VVeH{
zdn*+LNnYsB^gm_3L*yjP_+HJD%<?+=2CBXH;8m0f4#fPm$c;<5%1vH+V9+!#Mt9rd
z*L}*9*11Afw06_SkF4GC+Pc$8AVyG9QsOynpGY{WJnO>4oM?LF%q>qUSNoW8)Tpt}
zvrsVmY$GCFjTY!w)AY=@L*bWIRk9{BtHd}h;in;4^c$)_yb)j9%Lq-@0luMS4Z|M%
z3L0y;_BI6dfD%ai7>JhP7ZKUFtdcNyjiZc+=f-UK^A!BxscZ)qmChs9fE*Hlvif6o
ztR--ql3vT0#|&iWC-Sq}{@}7uVlYBU&hKfX(Tx@KjdHh~m#Wr%hgi8AmH9ocwmnAJ
zGX)~Mk`7v>-xd`8v*m>ev1|BrB^qh!+8J(9Z;r{cqH_xL=JDD$?J(E2X~lZ(YfIg&
zF*gO*P-hr{=SZ@{%8QacD?w^@!zQI9GPHEDtIZtRy?DAwgt-x+GBr{f)}2D)$jU*+
z^1J0`+h_UF#YO7J*y-<&!A62JC`qAr`Abb`_rr3GLHgF`Dqmc1#HWUUVdU@%!^2?@
zbYQ$yC|>huw<OtFTX~j)CBDq3a3pqwPKtt^tC^yi->HEo>otK)9_3rp%TN+-9;9f}
z<x29dS?NZx{l7M8EC(za<l@Ob6Pl(ztB6}G;qJ=1mKpejPf*s=lfQB3@k3Dj(Eta#
z7)AbSu&X_J1K&AntS>ZQ{PGoVohT8a(3Jz)t;5h8yNmS$1khdVlj{kR?2pcp+Bq$#
zfn1SIaS3E?^+OK3@aD7B39}fZ-1K@+CuZ-o>S3kxMu#TM4QpfhBXbn^L8+g@wgI>N
z>3^)isx<-%WP=SiveP<AVHs1U<1l7yQ8kS^Ca>Rniy{+5C@RL7ZlJW&<>)J|2&^aE
zIpnNzf4E*CKvA@kcQKNofUY!xb^o<|$?n*`xd<!PBA+0KzbJkmr~&NoIbHY7bW<ld
z#oaljfrp3wY;Ch@5;IJ#h=ChC?jmX}I0s=Z^M?^8WV?;;w{$eF-^WDOl?N1w4+erk
z11<cwP!l|Yi@oo61Mbkp(BNDGPw@KfAqp38`pk58CCUhOH<YlZ85`%41;t_oVId$`
z7Oc&b8Xk&!GNmnm5M0vRLtU?wv+(p#XC^3Mp#g3>sKOu1v2*k0cT6~-vqv`aZ;~#y
z@HeFl2KaSdm(kr!d&E<_h`OawP2&5sE;(Hu9WfkXJ{+2NsB76g!dAsUVuZ_Rd!zT=
zte`Y?$;$%GMsciiby**wuM;534ueygkk6X#AAThJWW;_ldENJ->n)D=+Ue1Dx+=?9
zW4bpfH5@>jCyPwTgz3U}8(T9p_JobPdM-@9a?O?1Xl&pAP1o*D6{J5S{xqR6>+3({
zrnNDJ;~QYD)`b@2^cLgU8cmQpcMOIAgZ+uC=6c#P1ZQ#d6_hv#S6gw-Rv{AfMx{yu
zwKOu`I7SHiQIzKAGkto70i!v#F;L6sKANEKQ9s`$8|zyoMr{zyJX`I|qQMFUQ#3)M
zjvj+09;LgLxMooyHl&g6n`Hc8iHcZD)4Zx#C1q_f0cJ*vL%z62yKFdNktwC1fFnbq
zS&3kFeVG2HS%Vtvrnz%{b7(ZP<Gzgk43x9PEQ~<Fr8gq4dO0Irm$qFRICZw?N-w93
zhL}q;Ju(+YwbOBvFBr>+O9F*z#Mp)QEQ?XF+Rcbv*thEC&m$pqw&)kOSAs^#VY<qv
zNj40<@u+o%9Y)YQS2L2osO2K-@{(}CR>nvplcwr_YG7sE@2j+hUhWkv8_%Y{%hZ(>
z(lPL&qt?V?D7B%#JRD$%PPcsS&DtQZa8X7>n^$ck4f1Z960rDf6t32b!&GMD>oe<L
zHW>^I%=aicvrIxHCa7V3bnKoO^OSYk^d(RNHX&OriYk=v*Z~2%vLusNO~y+|w17s{
zL`cHiIJg$3uQX8uTVs*DC7&;QI=W`ZcJ$#mn!1^<ot?NAa+NaGhQW;&k)RM+OX70;
z*#Jb<W4Zw4ds_x<K5*0EITbd)W)aDgkkiy5F`x*|5f)N4)ra3&*rcwoO&&~DEB!_@
z91x=U4@38=wx3vK4{XR|ymjnnRrBL7(IBRbzaz*HCBi3b+?8PjUW!<1$mdIVYKrF)
zaEC|Wmo)pFhu=+yj!2r_wgh4iBDj!PqYinMPSqCzR#>j3YjtN6$<-P*{f+6|epe+7
z4F=BrW_#k!OIIf`xaF8SpLHZ>8<0*{jEQC?{nb*#XATa1A#RVxpi8#Frn3(ea9g8X
z{A?aucW7D%aqH9Kk%ux2g}#BW^QVwaVC2@WZRNjz!wse8->XME;7CUk=`nn@BI$W{
z+~$AeWmlWOM{)~QT)|b!f$w1?<{KgVac5PqeM?6e=d<8L#hVx|kUm}iGXKi^UhksB
zrb8(70;&C5@Kg(daLu#0|7ur90S$BsTzXfBV5jcpLOr5Z?HBiyuIcj6$l<#wM`QW1
zKe>XXm4DT13M>X{SHREud95HZhjzG%FJ_yH#JK2k6ft-!&hqYZ+kd(ArMnX^7o;-v
zV@(7XzSg`@PZW4|F>c*3LAW|$LfG*Q9{s|guJ@d;hhO7=v5#xDyBBlW;78%X?DHE3
zh*f3K%i_UZhxg{x=q*hi(FNGrFntUBS&QMKhbvjaU%+1=*nVM7&8__yY)-vRELQw<
zf;$tV<;g^m1f`0dFO+kW#d0ylt6<6=wxO^shE<P8;3%f`{2p`XmyN!jntOY@#zWcW
zCgk&l4`8dHSKb#m)CY}jZUQ`Zk1baC)^XCdF=8X+EsBYJ>6Us%1{j?|TrYtyfofg-
z30lD?(p8Os3b+JPKE6&K-3D&w$a=>MU?K%5#YmkQLhsN^oV1kFsbol(2YD!#$Ecq3
zpoSibYuz6?x(x@Tyo-YF-xkwU$s>y~5L++=Vx&d>@l}|`mtxvdD0Cu(N)&k-u*~ok
zpGopS5mQ}R61DxEwwz7CPeW~ru|Lj>2DK{E0Mz>S{hKgN7!5?-tYfw`rfpz$2A^J4
z=q%MY3iMq|yv{K^a<?Z2tNplpH^FpZkuY$FfOmOqjye4|D1fIxG$F(t=^Be^1WiqB
z3OW{wUm3+7Oeo#?cPzHFXx$5FZ&Hu4XmgFUIGEA9eT94mY}LGjA+J|eUQ3$cYE&Qk
zz0p|}031lhb?cZH*68~eLLeW_OPs}g7zJnJh8@#puQOaw@>elSa-(iE*N?;5pv1_f
z12iX6El(LMYzO)y@mCyU&z<I7FOWYqB0IIeR-{Aby0gvXb>}WbTlndAFaSfV2TXyb
z(QRo>lG*qks7PEo5RB&k7z%*RgXA^bzs&Y$ZU10%dbRQej3X@KO=5zrNY@sSg3!D+
z)O$=>d>NK{<5S;@pNYVnc02+RCxFYEljsBR+{McV*h|9XRfGkQ+STvRtJH_3dK#Y!
z$EoeyO?<tgHf%4yxZy;s)Hoh}K?|Nbv3u;ghaFTK(6H9k&kc@Ri8$To!U19lh~wI{
zp^Kw}Di=fIzR*{g-EK053>oln4M1ZMWG>g)#44lr?r01Us>?X<xB|$R%hgHZFNd(Q
zh{rs$_hW9KKwb-$Kpgn{8H+f77k;=}hT$VQ?5E_GBnVk_cLsoSUZX<3l<yTkYJwf%
zn5<oLI1a)iohdVKK{A+D*cxVR3XK7|gn`i2%Hd>|e>8u-gMx^9kAHm+4Ho5x^FwcZ
z;fo`Qx##i~Z;424P=m(Hsur2gi|DW<3L)x*m2qj&*RNrE^6zKAN3C*5x7ou(TTNE_
zY@Et(MS}#3u<kZ4Q+}!;ML`vNR6n`0yJxImpXUR3CCekH>Lh<Ee+4tWk|9%=#f(45
z0utrWK$~k^j#qT#`Ks=RX;Lc%o2k*Tbi6+u*!I0cGLt(1lgaUy_);R_=dMyb-bqyb
zZVoT05o0tH(kPe}#G48E$;@cF6Pn`6OAWxE-MPyjb?Nn>7<824mZVu5`(K<`{x;$g
zF$)L@YvVwl#}shjp;Odfe|mFNWqtRIQsTkSaIVCSXW1aYfo1MWoRd99XyH2SlqzNR
zX=<qJQEu@|Y4NoQrL=Lt9td{&?%$>u2+OzIigGp#iYWjNq1a{|)3$C4Xeaz+3v3B~
zzMD}I#G=#qk1bU3cR@|Ib`yWBMx>aa@6`Dpa_l*X%g|<hs+cYYNI&k)Bg_+Ey-Bm|
zCawn#*o#U}Jg)4>fNK%C0UU_0^0W3lEwcXgFCjT8ppDq_?;6st-u#qB;1B!--ocyw
z{D~i0dFwomt%x@iSOcno2eTQ+Mh4D68sJB9SKeh?tqzU84Ae8@neuZXceS&vyysEG
zcDUGN?a>lhuT}P3AEXsb)d<vR=TG)>Kcy_OCC{b%)p5z>lXs?(l3BeV4NDs8@)PCg
zypKfPy77hzlv&sV*{8i{(nXPWA1JJ>0who5-jm@4cUR5?A0#E)F&s%`{dLM5If2%r
za_G=oJuV$T0=s@SDhD!V{puF!qVTA^gay#PRk?H>gcuE|Dwg0J^L3I!>s(5d_}gFr
z|Kju2h0AWXn=Q0)WIUc%oz1FZUEG?{auaotHPKYHP4Yb+3iMtJ9kk@N*WiII+!rLK
zN&e&_Hq&(P9PavE5#sCK=nk{w{;{T135axpg1auN%HiHvxgHy2j3n10oWI8r1GcaG
z$)%w#9yn}ZF!*@*O6G2p?9$}ApCV9jl#=>W(5NLW=j*oXvV{|l*Mz5TmAj^u0SybN
z4fnrQ$*iO}TlohS)#fKdC!3}MvVFo{YE;tUCNX+$&b8t^QmIPc)?+Hl>HNhe?hjcE
zX+tt$VfkXoQwW&*(<&9k|7iBxubS>wZng?KO~w-pWT7UcLWH}*L!6^kIi6=J$*0P{
zIUv687+NdH$m`jo^iO>f)pGHLXzs#A&O&L?Ot#YuG`H>sTyvWE27}b$vz&WqrT?-9
zv<kySy1DF+GN%6!%@YkD`H0j%oG<NzXk%@eGza748w+N1@*#zmV|ylW(bu9ZXqsq8
zOlqEE?AMfRhJ&1|!a9SMQ=w2$Td@8oy^ez}6BrzqP;da>jfhZUvlDU};aPuoBH)>_
z24<%w#wWHJVEN(x`UTEKtPHdR(>{7$UD^COu8m~%S@S-A4CpGPBmEA4uNy6H#Wydf
z1i$PB@`G)piFD6ES!7B%3XLUPQ-9tJMvJ9oLs>GrmE*n~%#Csxy0ML8CfJAB6^Qe<
z9xJ?q&b%3aqd+$IbjOzGpd6<eP@jY_I)D{3&!5^R#rwhI!HN>${#t$tJ~M52*atgy
z4Za&jh_IfZ(tw4#`w6*2@4IFmDz=n1vMHlBEE#q{l`DD2wV3fnzNHH=eQ&_z?(5xL
zc1;;ovLF-q_Mc^)@P*I+)AzjnmDT)ngXd^)#<1L-V~zai_J!Iboeumgy!_9+ko6zj
zN$8Q~e_+BL2qp`}^gkUt>mxC))l$wp1fugaoU&llF#-X}v|W%2(u6U8GtPM=L77d%
z<4^a07*Y%iB>4mOgilV<y|2iQ+CoLP*b|Uf4K2P1+45^{5=6=ocMG>*5~#@T_+uDn
zB}ua%q1?U&cj^z&<-?nw*`(f{E%GL2hb!}_3|eM3XifEbJ9*a1JgzLE?k`Rg%)Prw
z;UjaO@MWl^-9}QlzSZs5rX#yuZj&+cfdmiTmdc6xO>qG-zWk$z{094MqxLuLOQ$)A
zKeQA<Sl4tcz!^COtwE^W{b(M0X<ibVNdk_VQaXLnR{snf+zo>*+W`r35n~I@#CkrR
z;M!CAe5?C{u4q*}9?U9ZxYA<l2m_(~$Vk`Go}cuOtaT-0Rsb3p$V0RhtK*9}B9;=j
zN!eO(XV$-3Fxj0RozebHh2zc)vi1`&Cw15#4m6Zn@d3Y$*xqdC4ldQ*bf9zhnV)tp
zZlA8fMZA7rMQ?bT?WLMKh0Z^G1YsQtC!E)=WKs|go+T0M9axJ~8F#UyQj5Rw!_ik^
z0O#RWeKrqvelzdit?Q6}f(_DBw~)UP;{+QuNGhh6cr$L4=U-FbDZadhO5?Pc>n@<u
z9u4?~tL}BxzD~|-j`ju@`0YDOyZ8qG`UzjLVnxbM4xjZpz!DA|8Dc8Fyp2<`HDXeT
z7__5pi4ubN6VNR4NKY^gz0v`e%5LCWlkj1pLxeayx)*jeq}XBa@N)r?4+5bn*C>H{
z{2jpr7i$4==#MrX-5hvJ^pi|-_coCpnLl|^uUq=*@8}O5Zu}T-$QzCSh6HuzC&Tba
zb-^?$1UW}X7)F_6V<AOrH}I5D`Pin8{1u%v%c-_KiJ^U6X9f}yBvOsF1>Gq7WxA89
zPqb0{8E6Z0pZY1hWX<uN$)0a@7%I%s8+$wDS{{<CG3&V$XE3cI74SS^g&NYW#AqBu
zS^SJ<T9GJsf=AQzt-Wx5qaf!Mak?1Gs;(6{q9(-YGv2#5xuFl-QUy5g!LMp`e&GeC
zOf3o3K$5U#s0V!@a=QMw-9?Nbc)mRNdR{Zc{)MmkG^^Dmoe!dCeH4K`E&P-^A$2)@
z>&7_Aos%~-Kz|^|WE&}r{APh&ruwME$u<Dqi80Li&#w(~S|d#u`%%8Ju#I=Twc;tI
zlb}_}?r~Pa2<VREg?pl}<HBtG{fkuNOLV{~jR&DPHy5IRk7iP2UrYk|P#1QCSeZa9
zGC#W0iC99<emi=sd>qh?3BYb(mC~y}OrI$Lc1iPDaB<5sRc#}u`EBAUik>QO`fBeZ
zz(t0Po%2RAl!G^$p-;xn!Oj|elzTqvK{`1gpcq||q}G%P;DH*=sh>BsY}j7qf*Fdw
z*`gXdrA<W6*D!j_Z1S4S@r|NKi}%S<&!+gev<U+jEaU`+usr+^^TEHsWDheIK8!$B
z-6B70<2j<~#^aHYG5S(5AjrPzwPK>vEy!2Mj=K$3Hw=Zj^lnG0cg*UrS>P;xTX-%?
zoQ659BGCYkBmBHoM5;5?ds^V~z#pY8&vF~EQ<B3etQ}<H20|+$1MDWR^2S!mPca?g
zemzg`mQOtL$tbGK*IXQdZ@^=z5Pj<cWtz9r0FcN;IC+Y186;N!UY9%y0vrR2l0xv8
zxrf`H9zz3Z<fdTnlE#l@X3(H@_cJ4+ZQfB0i5X<Uf*rW5XIAk;o9c*=V16RAh`Ay9
zfAOV0H<VCev9-lZ=5&P5fSA`|cyNf-zMbHSWI&#tCn2$ptHl+sI(h(Et^k^93XoZt
zEB^Rrhi#sPB+&~f#F(x8=$!^n5octl>fRv74hc~virhMbCBO&j0d0&<G)4%!P8}zK
zI<w^PCx${%@$mJD<NUob>Ralap|a>~^T7#tB7N&c9M2hYNB{Qt));~L>tF-~^0}l#
z*n98UkNMkI&jMP1vVQVpq#+)X9|&f)i*%IoVaYH3?mFV=FdoN{2B&Ff$7qpu6u2=l
zmyxy03=X&LT4S&|AW<Ddc;L)_&eq^@Qj}+A22$&%g-_=?#HYVYVcE4}gw)DS5<JNr
zJ`$G>0EW+IAu`dKpBsr!j#?xx8wh6iKVHhL;)Wp^nvYk6HSD-BsPb6#7@FrojJh|6
zO_JODEO$ao9ufOW7mXY9U(PIxUBTamfYJoUyRHAuq2u?>$_eXHS<C*t+{-_KHXgZs
z)xFE|LnbWRLoptou4-#m>1<4~^yr2%%e$zeD6DOdR#$3SEAluu*Tg3uXL{zlrQX?~
z(Pb{;tiFtGJpR>iS3o7g`~W)76xE0R2*i8tyiptQX^!S~83S5V(>m<)AKN+WdTs)u
zm%U(g{Z%gp)fwNL+fS))3D3d0hv+WGIv|R1@c`;LTx+uc3tG9;ehsFG!*UNAj?7()
zwb2}n<SU+yaDhQ&^wLp(gpWfr+&fbaNC7v%y!H#gnxZ;FOQNsreoMi&%u7?G-HbG8
zrXOz}iK~2X?w%WJrU_--K~o)UIYnBjgVfBUdIiCbgJFZx|102HVnH<uG!%j-qU;(I
z8<F+=K`-KEObKE0h=yvI0^$?5LA{)=*-(0XruJR01cri^UPm!`T?&9r5P_{w1OE2&
z(`6la#<%20(=#l{7|$a`r|<8)5-;sCBGz7%kfs`QAQa2$dMMSzhSjioNUIPJte{0n
zKr0AWc&750s-uEs<^fV<z$H|2RSAbhhb^;VG#v2M2L%&>q}7Iq=p7=kb+}!zLU;SD
z&;cvt?=@m-)EwtNpBMP_xXm9_LX17zdtdcf^Q$N`l@Ge>81wOYLPh1P3y6NM;J)Ng
zix(%uFD?|0KycwLiPGLOMgUAtaDC-LWQT!$<@n05>N`6Kd@Cfz)4rM-jXq?g*{eo&
zGgv%q6PZu;qTJ;3W)WV!5u3W&Ap2eOK0+BqrsJ>gM-zQji%d{_chVc@V#o+kaY<7G
zrx{FUVCQwoe~9^nGXOekmZFxMk2C|lqS?voVm_7({iQHLl~-!qfy%8lG6lXR%&ffr
z!^8)y^Lt(dywAWJkZ}0rW?{Ot;w02tq<B2I>8`+D{6OXg(w!@QH)qpF{phG|P#YS(
zUWr-c-F$pw(Fz6Dg5`B};?s8l=y(!qp{||nC+q^Wq;|tc+P1o0eWj;1o~)AzLCb$B
zHg!I3BuNceeXd<B+t`ml+GQid*+%){4vA9GT8YnC0)LT)T)kCK<Y)lsf}`qTxdj^c
zyzvm>Ct$lDAW)cg@^fQyObzh@Qqv>(mhfOxtr0aj0d-C6<kTSoX}3ZMFnBYhjb#C`
zCJ$7|Yn_!}Fl^cS0*gsTKcEXc7&9XUvxzHwMk<A^=@MVf{51kmh9oKtEaJs*BVbpM
zNI(79(dwYh%MtS^NbEI;H7Op6LP5;$CT&c4nEqxD?28WFg|L0a0l_P$fmfwwzYjRn
z-6Q#QgsP|#PL>nb5u+LA{9@NGQkFZ@;|%f1vu-{fyv^A#>IK1yC6ekp;H`-vED{%i
zxM~d$OffZRKw-f4K;JFN_Y77^!WXFA0Qn^TU&9_2e7D3H1L9;bCBqr)Y#9TOZ8p#e
z2KFatSmykF6hcAcc;qRhY4_6V;0N(BhdRi++(<8GeSDM71iZ`Wks4M~5&1s5Qnmrg
z2O%l%FHQnVcG?evTvsU8OJ&S1XNUb#pgNK{CvQ9q^nLvw|NF;ef_i$Lv{5cy^L7sF
zH;s7NZk!xNpN8hUdp-D#Y7yXgI=>&o9lZUtGuC%RL#}p(b-8}+g~<mYTX*C1S}=PO
z0#`_R1JN};H-%s=lisSdD=HEwah2nmy|!4r*1d3Z6oP%|mIzR)@Hu7VcL0+1N=&ik
z2;8Opeo&>gR)+2pkq;(j>jjx^No0m-v8OhlfL1Br#()%<HH8#<PM>oqkANyX+281%
zb0i)``-qdCM<D4c6y&5j9?En^PCE|}BQvgZ-x~c5{<pDw$hj>ex}$f-F6DL#7EoT`
zzKE7sFdKf&Q~Jk*DiNILQy*ER_ThXz#>p2);^;j=_3>v+iPnQK0{eDxtoIN_9;G^-
zcyWh2-&CeFM5#P1*+18eAx2(rh*sirRo~ZVC`+*Ty79|sgfd?Jt1jg!JjwMw-fe?>
zI|e?6T!S|8l?C;6#o_DFURaRCTVT5Iad#!7#PEj{B)c&N9%b^b;N5@3nqL(Eci$f@
z?*9%WbNlL!7dYoT{=Yz>!R^61^YNWuXOZ~I4)({Cjb1+e<8XQ!?xiRCsBxkK5wr4^
zkBLKahCEnC&JujL<cgo@UVa^aDdw7${~mYmmns@maO7;DuL0-VOk4%vunOq)&LQi)
z0B86pzqjKzMIn4EMg0zXRAf-d+DfMJ{%laWx)O+j@L}xK@77kBe=ywZ46oYl7coa%
zv(g`REc90iMvm@IuoH#ams|u0Ja=z2MX-+iDTI*%WK+`1rArEp+@7@m{bAwNV^{u!
zl^xwudJ5Q?^aXw6J6Uq5)BF38eM-@IGv8U$5tD2(S}JA;Z_b%sK|b*3TyxUi7#^Mz
z)xj63N>e5*I(+wiu~)H99elQlet>=gbsBS6BRISA?BEt05VQ2xRer90Hbf^qw7z^4
z*(XT_Zmxx^amxA01aOtlSFfn`DQ?znd*fd*_k-$n2CVI1+rY3Uuw~OLt9wl28%4Ou
zHLhV&QK1M?Qbo>Q?t9DkMQaI%sP>SS4{T+9R3CJbew<JC@3W~q-!9aI;V>%Kf1;XR
z{ND%aZ!z1(yhg>LXY%76)yx47);di7Ul<5wmjaXCHi9*AM|Y78-T<Fr_L2K`!7SV2
z7-$%ZhDu%`ggCfKTQ8#$$M6YIj5=rA95L0Jx^E}i?a%T{<ZryrOORgLMUTT|o%C}R
z=kdP{_wRbhkXrQN0(R&s#WM^XodIhKQ=#aQGmE`i1A{AC>!=gGg}&hhyJ-E|rebD&
z+9nVQ-6)$PlL9qk5O{*0lA3t}g#k<^1ha+s76npfa?Xc<$WlLl0GvK<{d@f-P2otR
z-t!ORp4;%5H+MASb7Dnd6kVn)n_p_<MuNj+J!xyiQK{{nonZfLe%e<>&X|w3l%vlP
z!e9ql=vwq<Hbb6Q&;#7=O1VNs+fF+t<2)i@%=W`h!V9KJFZ|mpl<oGi8~0{3<x<ld
zhj#S39(#^gK3a71V!{TVU%Vy7;)&NYq#VhLCF3#z)?wf8y_p_EeRwAa6z+|{65=gC
z3g|Tv2bcXFM;FOmBASrv*wT9GT}749!Q2x`KCD*b{%Ha2N6xAvVXqKKE-fAouCpWO
zkqLLAfZuvz3?ssQ6MSjhfQI!(Uq3*W2FRb)8<m3e>O7~5QJ)nQy@tp=X|$7c|JR`_
zvX@Mm{{Ul?NNI-txNkt0+jr#;d3n&l4&^0TMu!0kTwaEk=cvuj+Ldb5c;H!yA+uXl
z@wk?U2?Or<WODZ&5fJ^k2MD(Os@0$8WvzQI!8}RBCA(dv%Q=wAi2j3Q_v*@isG_j+
zGR#j9o0>ptE~wTvA4G0qPFuE|s=@+uO-ncH50VZZh+5)=9W01qWFh%ZE*H;4UTOR1
zx{k-jTD4*D307qB_GS?vu_^Z4sWiwXIzMY4$6z@n!H-~!_8Vav#vPngAD{4$*MI<N
zp{;YV=nSKY)fdZ<QMmO~m9KFoWP5}xw<!kaD}mHwl43xG<4%tSYv!j_bSBK<dzOFZ
z9C)mq0$Gnv!|2G}BFzS_m~cr{2XF!+cBfX!A3ThjS<pX{kF6}M2DRMcPvj6=P^sor
zUE$=c=u4`Bvv-l^!}91%{t9nhaYCeZ@LbeS)6aHu&dU_m(-2Dx*oO;nK`v+7DNx&W
z0T`VLHJ*fUm&50s`i}db6S4F22?Ae85e8<IZaBq~Nq;Z60y>w|TlfRJ`JunVpTHyU
zuvKz0I7{QMATSC7ZH$U3nKMq9Li6sQuCxk&<Cdi~KC&Js;>!oN^n|&;!RvXX;bZfP
zWexwHqrTR2Qcv-K0~!j`xJ;Ox$=)V?3~r{X*HWxZMjuK1;Z17mzt+W_4Q1;Khuf9@
zU1%rVMGSq{(MYX#TP=q(mf!TEmrr0$_!A2UI?U9)2=pgG0WM%>6F^$^ShNZd?-VdZ
z-!|oCEuS1&e6y3>b!vwC;%+^8#oaYvt?x?fo_gT_79#a_bq6&;8jqsEYq!ItkD^G!
zy}=sg$r2{a?P>4~seJG=^l&YB-c(P>M8UrjEb_o-GGk%fiT-788LQ%o#8UV~j=g97
znPVQ*f=kimrLBAZr$Mmg!!#*Y?63zaZxNV>E7v&8<M6M@#}ZbLyi{8lh(M&(l~4uq
zEC?>3NUkvu2q<7)>3{TeYG3^S>FGS=$l!<jLHew_<)}1g__SVDY2szRi`(s+*6;5f
zxkuT6%=is@27fiV^u5;(eG^%S{D6G^<rSFs;P}4!iu*BmGyw9S{;m7H23*}1eM|fz
zJmT8}e13A?!2zB>WsBawzpZ&2d%u92y%rb4rWbKNcSaV8p`XP{;i%}IpZgz)ik4{x
zqPI!?R&2RTRlyal)zOMD{X@6WTSa?nOiiLo_4s0L%xOb?V|o<0!7~X+@?L61dz%a2
zob~25jXe|-8uM8X(HS=5*LE6fm02B}_Wx5bN+q2x&|;T7j;<d2!bBL!r=O&;_ApP7
zsZ=X%;y|-hGHzqhr3b>r=r|f(fgB$tX_Um@GQC=O7rIa3G2OsEzk?na<AGO2$gNK$
z>1tPfiN=*3^UrmN7VOZunZVJ7{SB0PZcX(ds_J$=<nj(DEpv4mNA&<3Uxrx~&28U}
zXhgqxhWEBQQ;!4BM@Vx9BHy-xd(?;ku+m}W$-fgx=3XsYbFR>G0Z>&fneH~La86jz
zAP_#0uR>2-aWvg<VAB1{Tg3O<_4KmaF`!}fAD$2#7%sYLD%8p-3wI#&5<}j0l3vc?
z;V8eSSG@LwI8Hm6iRn2Oz8pNFMEkSy7c4-Nljp~5e@8Vp2zL{)%0FsD13CRbxJ1KB
zf=iH`ZB7t98K38uuiOsei>mNFqKP6isSTshs3j5RW!-*VnTj7OqJO$DUY*!*vjqRk
z*=c+a(6_DKPokRG^J(oDQg7c;UZ%6-3%Dlj{Zq(fc*6DF{9u>eFpiWLJXGwKQj%~G
zP5NKXPQ1}eRJyCsUZj-0eDO9~HFnUp%T^wFETH_!T)pK=H78{bL(OR@iNAAuY?hP;
z_|H^{G<6V5iN2MD5W=`Ns%Gp7p<g{*xPQCB8ipqYA>DrYu&V>|jk*W{)pnQGHX9WR
zTz<{hZeV9yQmG?xJb@Ya6qz?J%&CnGEfdB^E=e&_Uy3i|7v(He-hx2(`9R6q>M+Wo
z6+s5aAT7oJ+{>K!M^p14T0G-vnp2bAh=$lBt%w!_u0e+V)M^cBhS_2Hi`RR;@&I-j
zK>QnxcRClzg46-AU;+p!F7-{c`^H#<u^2c$6CdhT`$*t`L`COkdycb{Sb<VAfFYZx
zvUCQ~vq6-q97zo|vmEhLeemZWH?cGN?TeASDhL!4{XqO`EOgo-bG%X4-hGyA5h*eq
z#7pm>$7ZHuo!PtX0mG1B|JAhtxFYa&cZT;=y{9uWbEj_SlUj<|d$?%PzHXWZ8}VyP
zHE>wtcyVWgD+g)Ci?7y=n~SD7N7Oh>eY+9{9lAmy0L}k)c7C{k;^Lt-A`^t>(Kx^g
z@DzBP_yWyZ8bluTagFlsl%@)V_p$wB1`kk?*Wz5hlm;jg?BOlyhiWY07GH+7lQ&~N
zQ;Ew7;utGGz*ProO#ec032gGOtjJ;me|7|dxwtI&UvXIjzXToTDwM$EK;EZapcoi8
z*FwsLX5gpQdvYdIM~gR&?h1K~AF0n1=L0!{08zxsErNkQ$*Cobc6(h+{ko30=f~GB
zGyL)IRxn?laVcpF>t&+RIWBf?a#-pynsqsnJ56uiQ12JPc-vZw4BuhbPz$o(`#;W3
z+-u-|=xRLtnGK}|-b$8wpuxat;bWHDsRGu~?tWU-1ol0&3`*BDLb2apdJ>7w!a7d7
zi9t4r&82z1!RVN~2P9R*q@-tCAZ+>0Uc*<?4jp%;=YMSEJ<g^aF9Zk;n_@k#{f|#>
zYId?aH!5=~eg}Jcn}<!8mE&l~jp&;U+eSKqz0N{3DLnnW?UwhwO11vCuk_Djy?C@O
zfPh$SLd@`kz8psXK5QdNMuBR^F6aEJbMk*geHI5cvivcG$L6y=uun!#hHN*#HvHYM
zqAFSrzm|Mot^arfICR@8Xz%KDXPKk;F?2`Bin7R<?Cz+oF$#7GLll+<H{o5!{btxY
z_4+Mj=TBJ{%wSWb`Q~nRe1Cdzhrb1IITGf7oIpGt8*iR#|9VB}{F1FEAHj#)%^>k=
zig&0LwQ3X>%VBeDsxjuUpBo;eML@u3FOYZ-X4rySmhbfdYz^60&|Y!Re?(4sE()US
zm|8XlN94zu{oWS&z5&H|_#03vsGJ+pG-G=jb3b$YI7F$uQ)^k7coMo^wMNGjShJ1I
z;%9yoRNZCTDAJ_bziLfU<VLV_KzEiUiozP_5@vYe?4B-7sD}aFkqtkWIcYLw@LTnN
z)=#i0N$!Flon<fi`U^o>QoLLwgk6WqrZ!gLrP^_z$EIvR4`yj_V-9^=;%cwo;2=|+
zy}X$ShpHob;$uzY9R4a#_gKkVWVk<kCcES?HN&TG+y|-N{UzgN)0JpN?K2sie*bvY
z8kz09e0x{`VR?zjL_(J71l0k(%;)uQ_~i>3Jg>_*j`YzH{svZ=pzvw6YjBVyZYDPy
zHF?n?Wq^=}U{iSKFMp)ulpRl7;JP8OxK~dG60blTpu80vSG4UyGD=UY)|k8g*J<CS
z+g;-w>4i&wB|qxkObaE|twei)F!yhyCw)T|?gCZu3gNo{WINT!ca{HtvGop2x&UpH
zX4|%H+wRl0ZQHhu(>`t6wr$(CpEmZLnf>P5*_iqV6;V-F<Xx4Si9xNR-zrnY(evbb
zI<-_4l(o&FV!rDH=C*&&%B-(izqpd#Gh3EZp3zLvw2g(SWzl{@PxYP)h@7>Dk;rq0
zRxa?xV2uesZfQ7akIe10hk707qGlZj@6iB?emyENok)|Uea*wc=ieW*Bp}S*_l~;S
z43Q6fULxR5N?JFF^p#g!PrMryV=aY<JlS=eyc5>?U#w`nIWs#$Nk8wqzI@nEK1!pZ
z$gnH#5vv(A#6d0c)ttV`Bc2tzr3QartIpp6PT+bp92?ghtSSRZx7putn>^OI>C0nj
zMz+#KskIC4)JzTL5?bleDb~&m=n=2-UR50Xu$>Nf0l3OY>nX9c)PE%{!bsjBOP)Pt
zFHh<lKf_fbr~$9E_2?m>zXWq;w@i-3A=&p;L2G>;$Ow>S%ud+-n+a-19ieV8g7f6d
zP>-0LWXLG<upLZZIhaGC!pH%WYAUz(e}5LRW9PBHdz920r)bLZ4Gh+#@vAL&zta_@
zDoKa_LPq&=X{r!*TjQz&g<GO!eOBaEMW<1g<XuOqMXqHZg{XMb<yvn9aeL3AReCJ?
z!zP|5{O!|aiOd|z8lg<^Dl8G3ltO2+ry<wZO4ToRta?E|h+pF=GEwdS-UO#=X%6;2
zw`qMQ4(>~q!S$nEq!dYch$6?aau=-lcSO#YNg73QR@==cI-hHfxg?VsAYhT!HE_et
z;cVLDIS_|b)bq9wO{T~QD|p@wcWxJ}0T%A3y`Hpmbm}j=VKlq#>C3G+4|A|2>~joH
zZ^d3{m#!K2H?UO$9Nor2VuNZeqC2pjdXzXMi+_zUuR*^~cKNx2QTNKy5L+DWgW1q5
zw2ggBa9`X#gDalAdmUNu2gS$_NI3lIx~qFUL}*O7mpCL&yJ#(PsMDwIWsYAH))^vU
zbS8uwx#`)_6<EVGbtQFItF1s^MvxuMl6;j@dSBSU61KvP<N-{Z83kYZDyq}qZqHX!
zosOwHhf79V%KA%f!{XZODZ3F)tcA<fb#EBMhYk*Ny#b77YsfaJwQm2n`RYjepm}?A
zE~)A&htpAED5U^ftQpn0P2i7IfCIT^gkU#jh(f`ZdFJ4+?mPU8R2L=<o3r26NW&jw
z9Y7-ylZ7>~s$R)5)NKBX(Q2JmmnXy(8)6)8!k~%nUyzqV^=HVL%b~vMdbRTqyRl=$
z>4j%076o_Jel8vMdp+w16CUjO9akghOX?2>JeXGvX%)oY#g*r5x&CC@iclTm))k(U
z9z5yir`KsSX!=Lz!5B$O`<?j~xB`@mNpbs<kE0^tP~pZo&3+?R{QfuOmfKNjz5c)!
zQ3N7{1N!I6r=V!sIl3lz{j3*fc3|mManW0wdQ{me)_kDQ;Ai#Y7)oZ`XDU}$nhbd^
z8^mv>NoeET6OiDrE|pu#Jz=(%OG83M`eSt~_hM3E30D_-DI^^EK;*zR)qm!lHWL(b
z8B;^Swt+Du8z^k5&JH1qAe>QlTUBXEAL(Pc8s0cUnBNCGe6ax-Qp>VFRYJ2!DDM~o
z$8b=w52PfP0c{|5d;Ijao`v=g94&^(0K(lb$pm`JL4;D*+{%?Uwown@*V%V5qEA}#
zVpEtDP1>U;j^ypL9<01MR%qtMx`I(#`ns`)ZH2&a!;{MEG<EO&eHWgP`LzyD)QUN}
zU;2c0%k>8fg2MfnmvSAh&KJ;=_2Cm`l9}o7AF+GejbJ~QZ^gu8iH<fUwklMP!3$5h
z-G>oF@ncJKVokZeEXeA&lkJc3gTZ5U*|lkH({6NR@=_4OySoe8Gue0HvJiM(`;?kH
zxJA72WOh;4U5148<q7`!zVkUfXJX!(eX5c6v;o8E?>cTmqNVq>V%QBKzP5by`r#%#
z+vuAaB#b*PKWSmolM~hDxBnUD2}BI^m*9Vgd2S^6zoiYsB5g@~3)Bdn_}_VP1`K}h
zzFPeze_q_&-rn^kf{y+lHE+^|)>Yn9X%9iG$kRpgdp2=#tO#9nM2h=Vx8Ew3;zcpS
z{`1Dve-!Hw-^e8~=Z$K&|KQv<x)lS(2xG2>0PUVg78f@7wJ}MmMIM5?`}x0G#9k#3
zpdsk=CPkE9zF>ph_L`B(yPS#B(zXimpZx9yf0?%YvI3;j&$E>mB2-$q%mkrEL3V2#
z_m<o61kYRVXUs(s2nFu|aIEhSAW8%Y*`HP0d!yn6M>puZ*fj$)Rf^7E_`makckDD{
z{;S^c1gb#avZ;F6U5dZ)5w_=Pu)M010xJ^F811pq<|nx_z0-eK3I}Xn-lrpIY2bl}
z7AIHpS&C7(0R-oG>Jer@K(P5x`-&Pu9+TlR*x=#h2?#m^A#-5fEADEEvC`n%Y6^Zo
zuS_W>ua0$*n1cC+bMcWEoV<9j0pERurd@qZQAgw_Zfohd_1aw;bLfNE;S1A(C9hF2
zljI`o5_cEvc01+U82ns1ii;O&_Rqg~%{QcOqLKd3$66o_%MvZ@M41&h<ZUH=2t0p#
zR7#FbX?%bttf%=$r%Cfy&`JA0&*%}m>al9sk4Y~ZXdf<!U){Zu<QZHuH^Kn>rfCh3
zhrp93!0F<j!Dmh(cSC@2^}7+9L=%o6Ag=^{*w)L}Yzi$GOpY5qr1NlAmZRD}={A}~
zyi|revyU*}24~(dD2Xu^O`=z|YKq<J>>A^F8Ic6@2VM~t^o;?@LgJc&#Oz+Rzwbnj
zY5}oR(YdGnYQPLQLhuEnpj@Qf*hQzVSB(D#WZDcX{30ESD+&Yq`k@4lG!y-97=5P;
zlc^s!nm-Lz<I-*7p7}}~g3<t0p(5KSKwh2(6m_VQ^J!h0+#sTQ-u3BuoStR@Gtm}1
zu>db>PZQvXXCw15XK;&uLfb679x;x$t)<Becv9T%o5#ZJJKZAVHo0w5t5p4V!OBq?
zr|+0t`XYAANVLVf=sG9bujAl3m>_97ybfj%ZF7huuX4C?YiF{@eZ+}-=^NEGNg;pH
zISCe7@Rx_fI`{BksBSL-9A}iNb?`e@R<$SbHEuZI^eYKMVUI#zKjNW-9m>UGLO^x2
z#{vXity8gdC~`_B4U{4jOp-^Y>H!t<k)O#xbu_UM<L_%rCW}3AFXpNP<|cUidexk8
z+B)avJYfOd7H9pwH)*XeGDJq6hEcrtv<BV+sn1p$m{|04>C&0&0@C#$iK0?Z?X`F^
zvXk-6m~T2S8!l&)6uh84Go5N%*gTD1VZ^)_UL?&6MllMeoReN<TfD$5w#(7Bf0n{n
z1*kg3Bw#UD8k@T#2S54|vmZ}|Co!_%Din#wno*JBWp1I~sKim7BzmRITr>REo}|aJ
za+a6FD{Q!nvp3J_@yIe=;u*~2y3MHy1OL2%ZmW_uOBtUOjQ+SbN^YT-kRs^0o|i-!
z78tlOeqdzI&3@$l^Gm}(-m*p$7030$$I*+=ku0h(ES?yhXez0zsyK3D7|lo=n<`NA
zs1pt>BRBk+z%!y5+f7~$j?Q~G!15q;QjbtSyk`*ahPP%HKUHH&r{}9@@H9ipge{W-
zTIf#`USB0QtUYAd)X>~kesv9@l!?1jGDMwnu4`fTaExSv4Y9&Cd876adf$$?*MP<;
z<D8v+&!A3-U*r8p;wC&zKnv5sr~Me1D!e7(AJG|^=QS@#Yqs0v)+jfecT5P<zil?g
zGHKJop-wf^lq=+@KaHONve)k3ddjO!=j+6FIC|ezRnj$`L*FSFX-ZbVz)g6pZQcNJ
z?}<$+_iA4kMl3BlAp@}wl#)DcVc6uxf_wYf2D8xQ*jzIeed3Ogx6Ec<eP<udZUaO#
z*c%-+e*wR^*~xeS$D^jv%%`K4b2?I<-|nIL5&M}r1q!qd-)f*E#i7)Z`kgDbTv^9U
za!s{_v_*oqcxAgy#6aQ!mm8~^IP)ZY6dpoC<;&j;0;(g_lX-5S#CXeqe(me2-sQC;
zX`S0>by6XVF=vZ*>`!U314n{5Y-&k@K^|hetW>@JN!%$NGcKQ6<!q`Y)LjhW4i54g
zI)1#2Wb!A##8G(2-f((-IqI6RJnZZ=GOeJoM*80U)NOO=h5aC*=nS5vrQY#B;Q7z>
zA_}DRUtykp6SMRc5IYffpSSP5R7j#%tK(e%weWw}5qpS9!)cSK)uzQ<S8C-$zl7?Q
z-I^yMmxp67I!stt#7RzMm^hLv2hKE4l}U6g?0N9~NH-iHr9La+p$rfbFj$bS&A5uD
zAU>Np66a_0IR4^}^Ic7!Sfnl6&>(*)|FwJE>FLRL9eiA)I42Sne+;1XdIseP@PdT>
zK!hzMAerKBjEX*K?AAYO6Vx`-1-E|f{_sUxej)S1y&aS5^Ir|5;Z*EjCIl(S0J_Ft
z${EP)Br>y75@t97%ze46JzIWfXWc5Mh@e9I;Bjww^Q`6Ric}F~V;q~T4Kl8>74W|7
zQ5p<e45)!r6GaSgVF}=YKb_uWfi0WPXEcBEm?lJMf={9N7RD>LGAP`6T%5P#uG9$}
z;}bM&!S4*rm!rz-4qHI{WvA`G;#CfDzM;hxbLUzPHY+^r2?&06<NJ}J8if(C<4;t`
zm2Ss<Sr?A4T0=kS{L6x<S*xKGsFgO?C~WHbxDTX%iT72W=$Z%k(57pkqWYs0w#ClQ
zzSu(w6m1yOXDD2L(n7(gTtDww!xLLKUKD|uRE9rbiTYB=aKXD>yx6z-AtP!SoVA_;
zvN7n(vx|r9D^$R31y0NH;{3Dx2-5~y$D?uUn?l1wgnwDmmfvvo^c*=g%yOJdLx#$@
zNklyG)Bk<q0y*@j$zhol^M#j8m??!+EFnwYDByXa*(>rdr6fh-<UN&`saQ7-b*qUb
zlpj@YqvzA4a||d6@h&z!>HRvcAZgx2+}MB~)<YQX4FPW*2idsrc33hAw$+?YrI@7|
zXA?dO0B$>MOG4lDIXp*z8Ql737LVPETUAzCTH-UB%N7Yda%d9jk=#ojHtH^%O%B=T
zu<j(_<RQ?^W*GZ7%B+#*Tv}j0m#zTVb5^vt+9B2+x1CFzKD;|Hi9(1yF9#fY8VZc4
z3;27QxNz9ioT$b}Tqhvjnzo$fYFc2!`<4n;-O)P6RyDd-wrIO!raYYZORqiyL~D$n
zNK1K%BC!@)rmIkDm^E1aRdVOD-i9!;k!eO%Xs+b28Ue#fPF^QAi(SE6gj%qQj(frk
zdf`oP-zQbC_`QX+4)Y>#_sl@Pde(EmyXKao3Yavz+X?OI4osd~RQ8%LBtEO!Mq^KY
ziNQ`cUZIk1{L?6p5^G|K>g8J3IkLi&{;>J`v>5?3L72SVPqg2&y8k?o`d2K==DMnP
zNrYg}G&BgYfB}^IBoI&k)jE*FVCm579gelXvHOoXUL@t%O|<bps8j~3Kk=zh4g~iJ
z7IFxXuhS_^UmT#r{dUtub^Nz{6pwwA!TSHWW4Yq6n3jIu$$P(YS=ucpnbsQ_)#FLx
z-YQj!{{a4iXfC#;FN`WTvET7vJPU(o%cn@S<%{1Zw!1gJKXREp)mLX}i-U5`c@|jn
z4A8<d;kTqE-IW=54d(y-7|fAChj?8UgM7Asen`&{=kbVYg}wBG;LDGY)bEmZ|84wq
zY18MK#x?1SVi}ZR1Bm3~nf`8P>-iCFa@L(i7v~gXO1=0w6h#7KWpO*{XrtAk>tg1P
z_dS|O{OYs%gBng3m8v?_@Z`hurrCRp*P6TDb~t12ZgBl{G@R&6dk<|8i;AvM!&4C=
zkuNQ`!@X%q=A|3)aSf(Wt&Gq(U=yB>Aqox>Z5>wC`?eu{wd86KgkW~p{GlS5?a2p^
zRMC7avz}}smsrONStr-HoY<GXSba&eeLmVwF<;q)wFCe+)1UZ@+VOfdBdd@&)%_@u
z0(Vzkb*l>pL>8K?AZc~Tp-bFv;5FB<f~Ms9n5b>7aoASj)T)hZ2t4R4;Rg5Q|KazI
zQj}}YR`#hLQR?H53TE$CPYLpP`N{2tHi(3<6fLb~YDBTZc_e-cqLUr61<DXY34+t6
zo#-&NL&bXX^pM8{x~;n3>kBa$#AVb+{*+U1yi1oR;t`LFq?)AZYI~k)S?kzIb_TW{
zT?wo~%sj75i+UBjloG2qg&_?IV+q?i;>52(4f(iwJ8N3Sp!Mrag_=j;FaX_u<4f+@
zc6M7S8Cs=?H0}(NE1Pk(tP1@PI240`Oo1ByUvL0p_z{y>R{!k7unpgHBW7nK8~rI(
z%g6dF_YCaRZJJPU?XaZx;*6Ujx<n5di@pm})u={>PIif^`T3~H^dOcA7jPc5ooz-1
zPho%O^{M0Wx=K~5VZnnaFoa5XNxdOg?Fy$H%$$j5^A>QH^`-$jg2!gxeYWI2%xYKp
zn-GReTT|QMWuZR{ynp>SpmQY94gGIc!~6VxS80pvA%oE>e;qhClfVTRBR{J7Bl^h;
z<TzZ#a_}F)dO8Fs=Ec**OMa1{^3t~|&OVQXbWfl=1^p%YIP3%J&KqvicL{Os+oLUz
zEOU&EP1sQ#{$Z{<VOd+x&@)ajuLdy6DixoKic$|u4?QK@#<U33iENg)>w6$#T_CeU
z^v^$ExiCLwqjnrnVomZHint;DP;31^lEqH@M7g(s2#qSdbSm&Z6eOH7iWOAsFsJzK
z_d+!1Ax4J0v1lWniN-RaF-o&t1ezTkvN13256c(Z{oV>vg@||?RzrCpDjM&@NZElH
zRi!Ff{wPYpQqwOYCAB*_pT%CM-(g)s<PT@k*p+ps?4ANj;f^}&>!0L(3Ol=qW`^^n
zrVij*Tu3E5Ch_*Ao^Y&&(z_L4Udd1fnc<Oz$e5+a2+Ud5#$|7YNrvSF7`m$Ku@*~d
z2mpon&KE3-5auvA%K$Ja%^NM$z^tS=+Ma_vl-O8>-02$P>4+hZ&)NI?5Aei637ONA
zSLWfeQZ6f_>N+LC`pwXVc+V`XOF`&q41oh{2h^3{nGyiRy%Ql;qsbpxt0S9V_<qrt
z;<-~ACBE5Rj!2FF0$Bo=i7pa0lE^`G7sLm#!<Nor<NJ{r)VkbucPQe^246{1OcE@K
z`{u)3^CVmRO5N_|E#x#8!B#SP8>R6mK+6E;n=^L5m0I$&wg{%N*W0w`-z~)#3yftM
zr8h?-V|$qbBc??lV}X!~Ft*5T>T_Ul>yQ_l2j`4_qkRaEKMzD7&Hz*3>kYpTq~=>@
z`Go$<RoLg|vQ(cWfT3G&WAbeS6Edk>v+1FPv8(##nh0a4h(fXb<4(;kyIu!CqOQVp
z@q-pq?HkEXC;EzL6KzFn0eya&8aM)*Q-GN-Z{j{WH1PzNqd$vb0}~hy%pXUOuvVZX
z3rZSpM!@$0%DwPI!{~687Mjmr;U68m=EMHp<fmK`WaLU`N~S_QMkRbjSq*T4QOq=v
zZTiqKQN7?#_Fs~2x`y?Mx7v9ncO`PO(+ge9V@$Sgqc^l%#`X7gJ8E-MttwEIxt=9(
zLzb()nNU>zmY~EH5l?#6Yen%4a~|@g*D1<*kXlF{u}E5YFG^KFpepDh$mw`6D&(Tz
z3b1S)8l9__+PJ(R>jx4){5d705Zx5jx57w{g}8Rp-?;!Pq4EDW95+Lpc*(6xnLnP0
zX*r3Y>4E@9vcJj4#DK$yl`Z2rCZ`*sYe<*H18elTl_krISukVUd(>0J{MC-lUEK5{
zE`!e4Z37?Om(d~JGWZI8p)<V627$|%7Qn1(37cix7tcKkk5z99N>=a?oaJ(P3-3;r
zQtQZH8sX#GO8cr`M?F<C#1C=t2!GVainxw-+zSZJWjs<_Hj}EC48FI#bL)#HrW3R}
zHaa<R9H5%@)+6juqkP13eIvwU!Qxe4f@_R5KJrUCS{^T+1H#}oy1piw!(phet=C^q
zi7q~$7m7i)YD%x<;TIj0N%iu+$Fn2fzA{9OxrUq)<Z>@kUV69bh_XSFj)!ADbHd6N
z=CMtaAaTEiV+#azCc4Bb#}dnrm9_qrz+!oh0w=QB@OdZH#-*fO{SMulI~lW8>GW3O
zD~r-O0~FqthooP9@{@>=dgCV8fsz*5BeM-nQrMk>s4<6YM+Sv+xd{f=uHfC~cynO~
zJpkI!l*`@-m{)YgG(-pW>R4}5cXCE6K?$o>Mdk>oRr*wCP#rc{p?O1UkyYm!<8U3R
z62Ti(sl0rUJF2vYbYhHQhcjD#9|c<zy-Z;f?>`%tyYEe^hhOco2^V3B%?krIZ9{?s
zmE=7!$M<-0&#{B$4<}Tkq;~HptIQQoTaex5|J|&QS}Gixy84)n(IX|J4U7r#eUS<~
zoIB`1J}VEq$9&Pl+RG9dydDDFw*G?@rXZj&Af^BEeezp}gR0Key8i!Mp9-PBp_7wK
zS9qAC<yx`zI=ZE}CT_ga*LF;4o5c!?NldgE!c8M@c+7Ut?9;IP8;T*lHbQF{G1OC}
zlJ9M$tH4-%o)Qn&dTDom)R)w0UL_+z%S)3iE#Hf|QT_xpA!Fd`NhqVo*;7ZrRZJ3T
zJnnM5L&p8lf}V#v9^&)`B~=vaWh#>2bd+yxj(GO&RCq>VSm?(dy8B>ZPB2qXe?8N%
zwK*Zg2mPoaEf2htZ4{(W5H`dvd>ocPr*U`*%VW)4Aj6unIvy>)x3<?P6J0u+n0nGh
z87AzK>LSTi2%AjyFPt5^zaxOv=$I_2)=+CO+Q9dFGC|6)z4I#+vjnT5RaKG6hWZUJ
zYR1bzG^vr7Q{|_{2_cVzw6b;DyOM)z7bd7kBf*TakGkc-<j6zXL_WV3cjKLX6(P<R
z=#y13vNF4?FCM!?x({nk-x&-Qu1eJ-ZSWWf3N#K&b%mi>$SzDAI>X(ZIdf0qi#e`O
z`mnUhH_Ir~FH}>b)}Gkz;Y5X9-4$fg(*N9H;Hj;6n~s2edY;x~wVxep2MvYFtk%L*
zoNjBZy?dGTp$|l*E~ziicq4%l_76aqc`ShUpIb1e!*)Y`bxq48S8WLwe^T)S?gI06
zK@&VD9MQ0QhIQo3;5gW13%3tqmT&V0E6W;le9b+@kb0w~c$>~7=@<721_DrzRmvQq
zZQEMpT`(v<qSM4oXq<vw9|S9=MN<*x01gn<hjJy!U`W7j$c&&t<|WZnRRO#RQnum)
z2Npv`dd}rg3oECaOcbO*#z6%{h@82$D&MvW^FY)+M`4%W=&7qZfpGE3`>}Zgj5IY~
z#Er0XcM^_rp$OYOkLF}+54X>%tZk`oD92mZNLYC^;v%R?ff#n8YodIK&Gt?C-k15o
z$qXEIY+!V=rp5p)3nS0W+J;y>`c7Hq__B0Gy^qLY54lqZ396=&8>rZ1DacX`#^79X
zXZKAfaO9vU?6`~uq{qyJR>Cy}SzAi3=uI(})Y77*G^9Nb&?y;2q&T}ro=W&!iVR*Q
zM)^ZB<dxtT#D&(}%{ro{ggUT<^+;HuGE-J`@!537Nd?Way9=57@ID^K0*PC}p*>cs
zD4vA$_Eb8q8moO}NkWTb;Y{>D>gqWSR<Dhr^iA-P65v+w9hpoTb?OsUy18J8Z5`(Y
zZWRsp72jk|AqsL33oD)u<uM1*3rdg~!x+dJkF*-tu68i$-=2G49frIxUOI1I6Wz(&
ziCu=HL+6{3ZrDD2b4@E8s_fP4`d;$*=K)BFTs1g#Y&%?r<X-#iSn#Hvn^{*sXwdGU
zu``|1wDHw3QO56|vI^FF!5659YXZw0xxxx-_+%_-wUdi#Zt;>;0amrF8d|VxuGstI
zVRgGJQ7(!kJ9cku)tb{Wan)V{%s`l^1=dzAK?%B?L=zx!L`j4!Y2XSf^)S&-_81|9
zCB#u>bzvu@@fe4*cyU5o?9H;FK<DV&fUaaCuu9ro=9AUeTB}UES^9QPgCyCp<PWdN
zi!6g#FF~~Is6(RWT^F!@B`jTzRw;&~7SE|N{L0E&S9X*b6<^N#tT=7&{Ho|1<_6-r
zaf6<tz`nLWxGrhSa15<=P3AnxsFRH}<nqm;dksMR%&R%sDpUs>fSw6&`43MFV$`Ck
z_RQJ8V7gnUd9t|w5`eS2B>Sm&#Ph}9VE|@Vk5jx6-c;n3i3SS=DadwTKlH$4^I!9X
zHf(jtQ2&hgQBK;mX#c(XZuYGE^+u7-Nx9xLc$Zfa;;i@2q@NeEqR4&!w9VMmyH9!>
ztKJU<G6=FLpDb(@Zf2{B_uZ?B@9y0x-6a@wmDuI;s<a7a_B?G?TfwKwv^3xm08R^v
z4b*nbmgg|4VvJ{Bb!rlxH6)jjWwW-r1dQ_NmS+}E)cyamCN84!*<*4qv89F5vk#{;
zVNUHDZ!S+L-DGkCV^o19&!E$qQUz?!*Ad1Sa1z^Gym`stLq^W)<*p;{!^*RB{?zxf
zPhud-J)34EH8^ruUq@B-%K^Bu$aQ-TWX4B#4T{Pm|AP$rAfU4UPEyN5=T<{U{tW~}
z_&+Mnwu3943DbIrjCAZKeN0EWnt6>gp~0%Y72gfBTD2thRQYdcOC}LSzr@WL^Qj()
z@ixSK;#EZZ(RbOoD&ixDK_Ug@4m7`M2?94`S;k>n*^lAtRq9+{mzGAh+_&{upw4{G
zU1E7g8p^gqJ0=QC;?Zaf3^+l56jBK{kmh2e1+oO<K@Or5*RjAR9ow|&&&r(_jJbHK
zoq~c)?4t%YHv24Ytl;gG$)IoFoE^D7fbOKLl=i!6Jn4SlY<A;4Tpg)fqUb%8Q7Oa<
z!IF3!BlY#6DmMbn-69t%JRFsK&W?Hv+d&0ME@_g5n<oa588Q}_whz<mgw*T1*XjsR
z(hXK8IC##Q{kQ+|*i~mC-tX_^mg+U88U`and5B+Xeh1FWX8dGTUgF9!yZ`~y$UOyP
z$H+sO&b`-sIz)*f!`58f#NM*U*jnwsho&mwv<QwVU}H|4oA9Yu-p{!oAtjf3qgt>g
za`5{*szagkYfaX}$`Kezk<LP7y{|$o%M7VtuT~AjZq>!ku4H<5l1jL6uqsTu!$m8z
zd_N8b)yKzG>iA-cy^!X|&zk+>emnTmAZ+8v#yQ6EO6QH|`K!PWOl-<BwC^mA%gI;}
zW4$GD4n(sBWNuQ`(hjR5kgZ?1`K1kDPt~Ivw#!+dqMc}iyVc3T|Kiu2@;7SxKCMgv
zv7p9zQ+&$`cGD{TU>0L#wojZdwB$X?EmkILUThB3DKcNy%Z>qI2+$rigP#Kli`7E2
zu_d=RwRKp-A@GB`dq1A4-tYQvv@K13pha65K5VCFQltrV&ZvLKb0t9K#7O`6UJPqZ
zTYh0}Y_-;JBNNU^HvM$;cFNqS_8?_VSSxULJ{K6eIs2olm%lhnrYY;0t1H4E9QKXj
zK$mo5?VX1;l%aWoH`KFH6*JIBclXYNW8;)dKJ*eyiz0_TB#SZFf1x*uM>@!q;#5ry
zhQoEEOiZtVQxpckZzKN|OBsLPCY#x&KMEjLD6Dy`32)&T352aHWgdHlQGULHM|9nL
zTZn)hqhBGLENO-JWmy5Ipaf|VAZ!|eQAsi+qMZ~p0eV@1px0X&*{53eu4h$K*zaH)
z3=ca?2Nz&n$K$%Mq9Mqg=>GWWry61dMjJ>~sBO!UylC96mYmT3T^S4^oIuIWx36e-
z1HQ;dU!U0dInWQz92AdF>#O8t?mvll1b&^Dufstv5G(%Kxi9V0<V{9${+$^6;dWTK
z=^(hDoslBv_P0TmROop_@XpJ3nT;bZHo7o#Z=h16vV%(i`F69ijP@Z_SG*j3>|np*
zF2|?;*06fDjz8V#kSfHT4g13e1%LeMH=KTougJVh|7G30NQb4lJF~7RH=gk%r#l+#
z639N$)DQx*E`5YZwJEiGODxgj#do2jZ)weR4oVdh27m8LZq+MhGWIj4UfBQvJaf*8
zg>VQl*aumobK@mu7WYPFS>u|#Va|mqjY_Dl=cYk0v^jlP*tN%Y_KJWab;Xg&<B|*Z
z>dMDAIY;Zt{R~U+*iMYvaQ>D5*c*KSCi;~m$0rCCItogcK4OH>gn)ddPI>BI*4Xa>
zm3o?I`OFn479Lh%>vJ^T<Q8<fmTAbmTB^-)DU~B66S)v$PFWjSN1oiAaSnnBrS~x7
z*EZQ7Go>`F{dq^i`4AK%9IyO*)S+`L$W~>u9Q^c(FE?xAB~4)~4<F%r5m4_6%D+Z-
z5}S}|$W`6q%4;RF<es;MS6w&f-;-J3ts=|t95&Kugp%#I8>npfs1z+`{nfwWyu+Fj
zrkcb*^uT9#_q7X>$iEwKLaLA}Bj>&bIu{485cmN$fwqqm;zpE$WL-<vl!XI~uuMT<
z#7=+yrZ#R?g5c=W9N<1*-fXtjtvZ5@4q<~*c*tKdW<_p?M7B!!ZwN;{f`I76E2oaZ
zko8T(QE5cVgHv2C2>Fal&`eGUJvc)rd@kX44NksJk<OIOBr5(ckJS`aaM>8gZ~uJ`
zc4*hd;4^HM6Xfk)cTz152LpetZ~M%8-fWsKR;2@KZOfT~)qMlQp}$C+JqT$0e@l%2
ztD*CWHRtPQnCfTzUOirrcQ*-VbD%jFQ-hs0)L{;z*y$5}htVg$01!@+w#EDtu%_?&
zF#)S=>ama&2i7d49?ZDW1OXuYv!+=I03iWw{`H+w<Mp*lKEr<^RBr?;-S$oRNK*Wf
zu7G!655s}vE$tep6371f(9<7`H^V-O`aGcE`*SJOS1!f*J--|3Lwf0HyRLbB(>XHW
zdSC)2kB^v{7!q_M6thn5hH^rc+aR<<a2rtpAOcQfeD|NS`}w?Sz}b#8iv#zFhs_ZM
z!qiBy0E?~oJ`8&EQh$(|`5xlu^a@IZ4h96kr#U$GY$Y+uJP<wgj1x>(_z3P(w#*lm
zS*J4;-eMilJ&|ZddC)o%lZL7mMq8w<$su_2(m$UKdkj3WrFP>p!Ia9WE#1oWDw)uz
z_i^Jm0@B(3NCisCU7-p6S+A5XdQbWNmliAX^bZcIRfYl`<nTeKxNL1#a#m9?wvX}a
zcaQzRT)LOx{Mk;0o?HIYL}4I_3v~9aLmtJUGT!do3JwYL8b2qi_yLe3)4Gq-Syn8)
zkYcRh_7Hi3VjmUxZMGn3rx3KM^N$$}bz|Z*b-d~bEJc|*peK&g1&5x-qx9>IlHTi3
zHTV#afEi3$UT?_o8lsSF{e032R|mL4%XxL0BvP^o$9ybx{CKcz=P_H$e_jj^Hnv@v
z)qFbyD|N@FaYwf4Ld}F5(WYjBJnu4MH$=Vw^PN)n)vChkEls~eK}Ej&x?QpKw36Z~
zb0A}W-mvs5YOQyr5qw?zJd`w`EAkY+@KnB#HL^MUI<&tbh!9#h&bCL$ve)(JL)~l|
zXl={gz@dbdlM@y4?iE2lu2qK>ihybKPXTFi_kXBZVt4OfJk0vAOlwc($wOyAIAiZy
z!&_{6RSTT)s3wd4@yJxH{cZ~`;~^fVrSx$IjZ3`^!<`Z{){K}S#wmNkloZ}_RCtI_
zE<$e>R?g0q*1v-ny=!9N<3!J}_s=z7dbpdZ9y2ZN11(%DZbf$+_-z$kG>yFWhqB}D
z%yFB+0*Fz+CAHjXbo1`-)N74I;fj(~6lP(j2|gVhwR9{4H(mN^p2A6$jD4Wj6_%eF
zJ)s9FPS}7&TZ2DJSrhzqYNS;pJpsO*a;zL#+nUmP31=_#NJ6CsUx2UB1SZAomrvQm
z(n{RsYf3hinpqWc-Jx;YJe0nR_FIs?b6yE?ibs0bs6$vhH!GEgw9VQ|HmPrVj_GM_
z6#9H49|ewJn#x}ozwce?*B|8UO7oh99IERMW1*L)v#BfGF4zAnXhxQZoI_5f;Nf{J
zkOGqkHMnnCFnurhmayvq-cGn#k>T_svDkIi9r3Poeh3PdlpWc5kE^{Toy5?9C%#@c
z+8Cx&Z({3SD)yM@uqFZ=cOP^CkLec}B--G4`Yk=5E4I9y<yLrAW?GN0b>yx{^R!B$
zr5>+<j9cI_(xai0qO9+5nC5m?_Sftqj8zY5jgTuV2KQh?TOEZwmsN;@DJ)1`)e$v^
zvWz4r)pItw{AXnNUn_PaE^ii7Ct1E6^P|<$)XOxptemB^Jc?6Prtk>6(Pk55$SDr~
z7Bvw2DusWh_pbh`=K6j9yZRywFO|(1a^19ZG?E4+?8s;iP0-QIDDn;k#BZ{9k)45G
zQIyhC)n;I9%Y`Y`>~h>qmTYH+EjjY*6=u4YPCeTE*FdUc*oqkUAMTe(5?nOs0Ru!<
zvsb>5hbUvEo6g5;E=xoGLw2|uyj|wc6OX<p14*q_1plHNQ`w%C<*67H($Pe)S$Tx;
z>r`d6wKM2lWo8LtHipkqhOCN=Ah<3RpQJmqcCHwu;_g_K(%%n7Q9T-TC+E}~>uOT_
zdxslcTd^*PpHinkQ~A?w6r8!j!4KYi^$_Jg`pRkct>uOP%0aAi<=M7ni^Hc<M^*D}
zaPNW__zhs}e9RYETV`%m%xFSyY^h;z6zaj)Lks;Y^$_f5UY5W?Y$2^@lv-;m9P6d_
zf!pBdWh*pT|GDeBX*UmX4)0vO_amzNpeBzrue<;k?lRYwUE<TGheB=q)KX!%qxb(u
zXEX)@UHq@k%m0z9nF4D6aBKSi8BFzSollhsR-*ovzu73Ma>@vHGs_BzF;&Ok*De=P
zaOP9A#T!M-$@>7FX6W%h^?t@#>K3n=Qe~D9ejk+Z(8e$KQDb@C6Cqt)em^qX2{iB3
z%xn}I^tx`3x_&$dKJOeI|Fb{j6ThqVQO3b3;sK|qflPcscql-5!W};3Oak;Jc)Y#&
zQR0*zU6g6*p@}8fvzbH&6+I4p3)d)GB<ocnmZJimV+6O(26ac`-1>@IfGj?G;lF!B
zTlzUCmnIO7lL6}6v+i_Xp@P|ZOiAR^35mTp9f`o96xV&u<;lf>0no3rnSfT&oju_w
zKe?a!!VkX5gt8^*L}T?K0ith<Z92D%uP+?IDA?rb)w!sKwr5DLG^eMvrQIxj+#F@e
z>(3fYZ4x{wq_vuf(9PKqv_uYBc^>)O4i&ZZe`)2L4OBqq=c`jln|o_V*2h{)VWl~%
z?B)3+LBJkge#4)s8ZoIU+e{bh8qozjC>$G7rIGY7eP<5&$<F`S*j$(p!Hw8PORb|g
ztNMXpc-%r&4pK*(5UQ3U7#CtM=^6nnNt<x}5w9*cldUo=#yDIyZ~r9YR<VqE&`n(<
zQ36qhxd7``LOC+yMmV~IqW!si;CnrIlV6tcP(A`hd)vX1=`k9jG`?reUF4x;R@81j
zimzm*qdZULBOJ`&&Qe5smGB<<l*#3uXxOPXcqb64*;S<6Qjl^Atugoy->f7x&>q4o
z#wcF;aCXIWtJq%J&8&yxUOf6pen5SDfMwXbKO5tgZggIRM&Mh7#k3*_w31zLnTz$J
z(_Sen4vA{$d_vQK27vxqT<n93g(`%iBs2Zi%Ze@~tj3hJmHR=%60veM`Tm|#w9WI+
zB+nhO(Chc3?7_X#E!<ut<iiX?d$eT^6dZ9`hRU^gk=+?Ot^5+GPvQKo+|p)umLA#o
zo>L6zl{rL|Dhi2@UtJc#!tr)-jBfQCshT)@1?52~4(JQtTk$=Lh^1JI<Re9qgl3yh
z;dVbSzVxy$f1Wybc;pCSj>KFgB);v}XGrP8_%Zq$pbl^18<d#zHP3R;DvR|u@4ubr
zPUGLixSy_L+9M0N1J*oyr4)8OU(Dm}9@xK^>(udDT0C#hj)Pg=h~2j3(Qc4ZetVZ3
zwl(=KTRtzPuW+~5%`~!!lnIM{FA!Z_=rh{$P9qlXQe(kebXFzlJ*Rg4<zUaXv@g#q
ztJvtIo$=w~-R5l6Ex{tBUOALG-S&IqO@tibiD_VzCzYylH5bub?EQsq+ZH(ahsiaY
zU{qC=)z1l)?J4K$^WH@B98Md0wHHOcg%Is8hsFiMpQ<GtIRi!=Ix~~qYqH4F3Z*VG
zVj;WPGD5tX3dGXr{kyeMeTXl`B>exj4+v30TOoHsm%W5CDXUsGO8a+(+_w}GSzrdn
zxs3?@@afUJ?>Q?T&oVQ>J1;3n^Z#+EB$f+xvA6a&NXyx^Yb4>zQ^Sp(Jt3&invZXe
zsl`7{9~q*FnIuESj6bOc8*U!(dau2yySPBl=yw<f@WwhHM+i0$Y&?IwzDCMNGpF6i
zim;#T1V@+zl~%{Q1ELb34G=*~qZK;`3}lYCgTIkIDy9s>;c%)NF4aPC^{EitnQaG$
zu+XS0Y4Tqu|0sBsj<-vhW&-wqHDxs9Qtq$|_XnsI$VRtNdsw%2n=!g!^~6KAhT!tR
z;gb+lSf)t6zBx=DX}N3R)wuVQ;Et#vn<zs=Ay)TrGf!E9(t%~iNVX5+L(vXd!JV;A
zC*Z+u-e7joTWRW4$!{=MMD3kM8zjC#og)titf7?Rl%}+b4;l&6qRMoXTKZjg!n2dG
zCjL1=S^oIg1p-1jc_0M+7aB+`9St{t9Y<bOk-CszV%=*{$W%Kl$E}j(S1lLt47aCP
zQ_+le(sBY5M|ZHE6Q2-IfZw9dB*{75gQFXks~t)<FBKN(7?;ol4(1x;Vdqi*+QS`c
zW$bet1+<qcT=iC*1u1U%lgyv*hMYlQ|1$beInev2aG{AW;jzsK$bZmHhr04q?5$z_
zOQjv-%4M1=X3o=bujCG;AS4t9Vo*|1fwX3>jJ?sn2wSd#qy77p{;8(h)YRTdxyfO{
zOW_fzq6u|hJBMSXgqcHEi2zn!{TAQZOSU%D+#eZ&lFs~AK!;YvLU1*n02O!Y#jES_
znF8#kz=UW6Puq;TZtRP%%Pqh`Ad<N>KUQlQ3Z1I8&o_~77+$Yi>iHjNF$Mv{{`YW9
zU#GBg{H{M}*a^ZR{C}w(I_S>h!CXn`s<}x)pe@xz1&UC5as%m1aYTt~8_o}L5Asu~
zWv+(?Kav(|J~8Z-i*zwhnfkmaN`e#qZATFq)4HyH3Ne^uKj`;C-!jE+bj^LV6!M%O
zlYo(!MJ``=0yGu;Vu2*iG*5_AgFWbbg%1X*CLPn169gfyPmxE>Mg}yI7qj>uze=d^
zkA+v<korW(ceZqY%5HxXG16_teA){gX*1)owa7-4m^z-~_y$}~B11)yrGb(zC@g4W
zm$#{~Vf%yfO?kdmeP3v3D7h7pLsP*#RxhiAES?AQOfnNKe>}t$FWv7LQ8v6u^km5l
z6=7&i(ius2glpug5Sdla*~SpKKgXnC#5j`*?-#@bHwyw-b`$mTrh+3wQkY(WBdt@m
zNCz0`mf*1HpcI_9sD9A+UI_Sp9YSJ%zQ{w6&9_Tayw5Z}nvg8nP#(8BhZ+y%-gOn7
z>jYDg6JR0LbIR^roj%xTFjaWA;mw&YSXM_f{GvZLAI(ges@`XN?no6@e?fpU-ykdl
z5I>dsW@U}UGiG?gC1!cBq3qVN`<{levlN?OgVeuzSk;|h{^q~y4OtBhraI3Jw8{?_
zR*X99BZyc{LI#iJo-4-V<g*@(+84iDV=>>%{S~^m7o@>kxPhu>vwIzmm6Zw-9c)F`
zfG04QKr6LI?|B|9Yvq3@O!^Vl`fx+Mx1k#M819UG?oWw;C4(v2l!}}vZv!i$<n^CE
zvp$xEiU!#6;e{`bT`WGN6SE^@t<7kyHUR1sIaPka#@MDn3WzTqS=ye{e6&MH5R1us
z_-KqLCw~M8PK*uyQHOH6cv9j9wYri2&T7DMCS=z!femtrK#MGlUw}zFN`<V?jNKO_
zL!6=YpB8OD$@WoB5!G%*K;kY9@989kV3<0iGC>e{SxaJGTS5-~!Px+qz4nPxX}lMS
z{-jaa9YkhRO14c_w?^U$<C@3eZ1g1j0B^0NxRCWRDPv@9=GmajD=EMul-n@AzL0p2
z1-1m*(6%x{%p>I~!7p>zCuz31sEo3$2uC2tB3zu0D9?*R|MfEwqtr}m1Ws!=T<i?f
zQnNX5;Ke2}K{fM9vuW&?^us@Kc}=^XWreG+CK{i0v*2q$)X)-WGHru8QIu&^B2Km$
zI<X{|UlkI3`}X;TXJ4|Q{d_D2HS$onve~k&#B^ViY<B+n1fM+|huRp8qZsybFMT;W
z4BAMVcIfgf-Jat%p5Tk98Rp6sT&r}rs{%4YF1Dd8zDcvlnw<&xS#0E$ewXowTehm^
zzhCeTxgYPH-Q27k_K9J`F5Naly6q*KfDi**S~&1m#>O!os7>tV0?&6y5!am+wwvL(
z6ci6Fr*!eSMI^b`Xq`(MJuF5m;z1NLR2&3VD5cEkRn3O1&0#YU05Prwfy3TmZcR-S
z8E4Rh@L^5}M?^bF#A<GDWpJl3ta3{7R8jgC-&;8gSMl(BkWSIE#bhZArdk-fDx*^d
zs)(=kMtU4nD#C;_=NL)6MA*qUtIdeiz1^|5vT*neyN}KbzZ5#|3SwRO`|17r6!SYz
zWTG?#*NpKb`~Ggt6d=ZNFARQS!Fi>9+z{F|!`O^g&YU(b=#H=sPN#)pQdoYP*g|~T
zV=(uvA}@{o)0uS)7EMl$j2hE%SUh17@LdXQ9t4r5R1U}%Dz4}&r7w0qH4rV28Fa4k
z<5Ey|mV%D;{9C<{sBQ$d499+?XDj1I=kbv+H(4;gQ54269j<cEhK)xDvUZOL7alO|
zNWG8ta`J6@!}~YMy{E59iM`9NZhw~gml7$l3I{>7B<ppEjJgad+lbm){N0%VJaMTt
zBZX*O_bxO?H?RR1>VB|;-i|X9JdA+c1&s#LIl0(IYaZDI5mn4|NnwQo(0y?9qnISM
zMx@hae?^|pj1SspY|U=KGPh(Yl`I3xQtLds%ntbI14^7Q5^U^+e0*%n*FMfe)vsWm
zp`p2nW2$R|Vu4T-r&s7Qpx!EZ%xks<5QKIIo&WTRGo)1C7_(=%r^`X%j(~j*`5zR`
zit788#QqT~5|;@Aru*;T!0uThC{92|Q$bA<i0WhY*uWp69il*)#e2*x=rzGG@`*-B
zmy)7}Ml59jH|wvFe-+>Kj$*%J{X41#eSzwVZx4sgNWGiVjJ+Zp50tf^gTan8hYa>S
zYHAXHkOQpuE9DIUM#gMU0G-?~yu*Kpj?0u`?sH<J`*`+RRs0s!=sm0g%53kvr^h!@
zjT%T$(Ec|aaMkw{Zg1NDhnlUioKCBT$GJR`_8ODwlK4;US708H@?@DXeOS=1ozpMj
zA8WuM!VZlOFT0KXK!%F-h=Pzm{gF_mSEAmCKC^W(&0P5k?hd+O<3tCdhL4t=f>$s8
zoi1nu%gebof~IP-Uxwzw2Pas4C*ZLF4E|~fsBQKkSB1CVTSw>6M0`;w(=i;@(oA0E
zajt)=okh`S9C1V;9&X_le#dcDVW=X+dv}-u=zb<_6sy!H3v?nJmitow>YDN1w~$)V
z>>mklzqBXZJ?tfAs9hP$2OVQw0)nHV_g%o9$aVAcjr>>S|5fIj`aShP{mbP5xU^()
zF9wsizqWG*)+nAu3~!I8QW19K8HA;=DT<9_2!RM-p*@EOqM{^-)>v9RK(_6bEIEU8
zjv0YT=b2dF^E`TpqDrqg;M7s;al+c#Ezak)9XyOcNaC5gSbQ)F=L5AdPEyGT!>99H
zbVIx?G;<pXU82#|pBk|e)=_x=JA7({4}}-*c&`K}3HV4k3yOdu>&wqSO3ZjZbtm_I
z8w@|#zW3iQ+ShgL`Sf#blv2VA9=9pXIm-!Je4tBdmGjZPLaDM}dQ@I006XUMuTTQ-
z!2DDesPId&@2`KsE?YHn6T)OK_UqY-Dzt>?qPL4rVx1QguGx8e#=Y4B_ZT*hP5ANQ
zkoggirapmH51Z|L>v&o!NEwGHTq-}t>GfIA3}_@ap)F`xaX1p01`MjOz02p=fm^nm
zmg=H{(EjOQ<+eNa5cm&QtXeX<sI<5^?LSwKV=5Pup=x*2jc7VbCD6{YcjGIt+p_6k
z$Pkis-d7LlP;RM0gx^{idg75)7*B*nEYND)r*Ma6;AR--%r%tM-MPzU)Z!k+bdZML
z>=0c9V79mCvc8nY2#lHq`S~PvHFUvS$885}g{f0Gyqhlt!2venQCADAY_=tv`QF@l
z#x!T)mx|nFtKn#+c?Jjv>4hF{5V$;gd%lOMWI}QTT)n6JMKK8Z6*xJ1G~`7jnYG%<
zx8xl)Pdo;8O(_naJia#GOPY3EtHe@+-*mH)9lj(oM~dDsGjOKV(q6U;GKTX!uu^YH
z-0BEc(;A?^0B$PY?l6bd3Iw(s;NTd8+M_x*YY~DHDX%6HG1@zybBk9|DFBf3g$BKx
zDVWn~dkNR9eR^^!?E7|c)SXrBxKq4fA1PbCqjeoyEmyx9zhHmmUeZT~L<!V+58L~p
z=Zn{kIGx<!Gr3J-^-6-Ph`qeYjRlvOICl0}ktLOwsli=4rH}<*?D?U@{V-L!x{cUz
zAs=OC@|!8WG)IA)L}ii82GHd71H2X{@f|O;z6(L_{oVxojXKg8Rg1kN5(8Pce2+r5
zkVRnReXOd=M8PfGVV0dy++3(&U?MEr`gBvU>zK_C8yfMjrcI+qt<oGyrDGceaY=K4
z&baak!pHZzc#HDUTp|?cVro_#JAKkh8o=g`l39mdkJNC_IJZN#%sGr2{)!b8@#%9w
zXSE>f>Np2jS7=W&zf$BU-HZ`N{;+v>BrXHN5w|F>WzA~@4wE|)44(xguwnE15W})p
z5zhquh1;qY+mN91NT;M?Lg@ajUPq4@q8M!L9?R>SR7(oRZ8nR!t_Wc$Sw$5%%Wd2&
zM{HN;5z|^UoXikgq-foInHDnpF?U7U7B++pooqJuK3W(VD_nkNa!M`6bkI-^p@sPh
zauhf5LFSHl0VJc~@3M%Qn?W0PUAqscWH?j?ku!_1;!llG(7RyZb~&z%1z`Ntr+N{P
z$juuFG|<9LJhWdBzBW6d*{8*uVf$>*Zyu5Z_^>&AHT<@hYq?g3;~%4@Sek-jTWT!2
zxSsVl|IZQ2R&Ke6ZIRo+XMeD%noa>-W2?<$fQcFRCH*zh)o1GlOK!l35jnS=A2ECP
zar~d%%fNE~tv~)Rk1dszYmnd>I|fZK{6s2?jfmNW*nsA8sLJ9|XkE+7Mex?VFA~&C
z>}QUgJA0=wvBfhfk(M{6Ohn&``&j%QhIRxpf7l4k_j}v4{fvW<>omYB`7z2w9@M&z
zO%;DO?-r&vod>%dChaKGP;tDuqH=peE{M-@@q-j79oaADHy2}gJgSiLvPP_V!$02E
zfqs8tINt}4C|o79yue68Fe{6RSv<R<W1QU}S5SAT+i_*|r^G;glI5rWkswbv)G~WB
zJTkKZ1E%YfOrL@4#uBJrvtPVXD7>@byKRtJy&8U;L%m44E;Gj@U-`a}ZSREi7e6vv
z(Z=bZ)nQzjk^fL&NlqrVP(*Z_9=k*c#1j@wA1VNuX#Qi4VO1yg@bcJK{?66*4HV|X
zY0^BEro&h?cJ5X_rjSEuUD#?;UO=k9dQ6fi$x!7lDprVS%$^gm`i<msj?U#|PK#-E
zHJp{Ws_7}l`blkUN`K#~f35#q4Vf+f6W6t6Y(Rp3m>!fn;4Hahu+Op?2XOH<p0K~V
zu3S^g*^e$WmUbAOa8E4pvwa*|#^aIVWCp0ZQW!4z5|~y%EkJWt-Vb&=g_Ht>e62uO
zu_HBdW!ipnx5ME^%lU@I2v<AkfMuIoj8oGEbi#V@e6VZ>oMI2-&I&8ZnuagK2&1S^
zZ6|uE&2XJ&E-#;+y=pb+XgvhlKB4qqjdbqL)3Rm8Gu9}P?`u?m9|(vkuAqCy`uVv>
zbP;|$>Z2SJ-{;;t|ErRk`F~h>hbCPBWnHsu+qR8WwzbN(ZQHhObCqq|wry8^ckk$P
zZbZ*|(D?&$kdr4P^36xL3>b%dZG~EKWi7F_#;^UOOE0M(tx-cAnR)fC5KNUA0mL|-
zsZDuxZ-B&Ha^n$4bsTqY0hw#wWw7Pml00Mto~A`eR9CywJk_EeOdie)o&BL5xy4Yu
z)-;xJ5Vfhi;Rd>5ylcRQ9bY0oV_%7q=d7_KbI`QipSkzTxDY)H!~-iGirkP<?1&LA
zZ6-<5r8}ZKE4;B!W!tb*zr*sH)=*x3WHo=Ze&34%e<BqITgUi;(isuwFld2Pn+ZKP
zJnP`lmh6ppi;jrLVWtP>)9cCan{Ebrm2zME!{Lk^_c6gmaX!|V2Lstphf*-3adHV%
z^J`^0+Y&e~P(;sMVF4UHbT?P3lm}&Q2isem2g?Z#yRkb^;?uCB&jF3MrjYu*!8#SL
zLQbS<LPYda@fPVK3amp~nn+?Z{)akWLk_N9cf=tB$9bgwMo;B<`d(;ES0F>XOuZ2J
z&sX{ZjG*XT8qyF1(JRg|K+s-aQ9})%^={b-dfHT{1ixGu>dJ;P%P(tqDei>#`%8!W
zIoRM%i9F6BO}DeU+hywN;dBRLO{$)nhpJh3eexS*whvbiXr=b#rr>Wp)9kCt@n52V
zk_8>nYFD<A;6^v?fwqwGe-NyPwqg2FVARWo><6R`8Y8b|0Zs!uRao<O^f)oJebQ4s
zlwV^XAd#2Ru~d1NR?5w}AdI6!q2K4KB0n^RCnY0{SuE_xrh&yb>G-{8XCFm<&pPwp
zm46&ryhUiToDaS9Q)Hm<9G6~%jY=20tqW4OLj)I7Jw`lBz726Nea27Q%7YL?f=!S_
zAmhb>4UQf9=5fkekgERvX?m5^?>?Lt3UaHJjo>8|04*gvJ0@`XYO%ndIL=l!W*jN0
z=`M~90~kmhw)z#BU`zLl^VB9kp08F#^gtAuPT8e!MSwz!m{}F&i1$X{GO%O=MdRvo
z%^r_*+IJnMzB4Nj$_F1za4b}Adb4VaD^cPbA<bLGGME-F)-C+v%$(&D4Qz)m{w$(9
zJk{?i2z*!fn5Ch)+^#8RVhNkx{0Se|kPmO<`IGeFN%;L0G(VcU8O;+;yI*4%d<M_3
z0gpN}ll$ivByXu<zII-YvER4X+rxOKgJ-DvhYmRczt(cqs2VPu!%W&8fA;X!LL8#P
zgnvfAXZNbkm2q<PtlK7Z5&z6{sby-uZP2TDSqxW!0d*Rcoi!q(gmi^C3SR}`^LT6*
zgPYrBic-C+I*&<ytheF_>&Ezv(NW;MqoMREW=F=Saxne5tZf_)iS-W!<e|$SbaD+Y
z{!0xr$8Eh$%2#aD0(PZ=*4n4ql1|z%4*^t_fd8<BClJ^k=;41^BL7wWPvfe;tY1*;
ze^^3?gnN5B;bTcX;ZI0R;w(Rr#mMBOQ4T&X^iDhM9wYOsV48;>nhUP70_UVf@^C3*
zwg^2c5by2!hR@?2ggZEA046lB!j`Xlilq}P&SPfQtB18fD!wNKYLi2x9N%mBjeu#=
z^ZVfH)UAcGThBL>CdYuadG~ErfM-w-?xM(j><N1lF(>v<r0yK!^acT{p^<R=)l_oL
z#A0w&9hw1jR=y8&t0&Hd<y2a2A^MB(?w81H9S93;!`cT1XK&^$LYdaCgwb?UnF2y6
zv&Fr)Py*uvGWgXmn)`$VLxBmzOrR?r7Zcl}I8_K9pOG<n^{oDL)REL3C@BvJtlXS5
z_`_i8;00J(^NrMB&>d`W%n;;f0F`dl*y8R=T2!!d3*4fDRMs&6Q$1AVd+Y-o{kIkv
z!vSh=eI?!5!?0Imyc2k}aa!hzvGJIY2A>5jux)r(q0Wrnb{lK}`SEvYt15%p0j7^F
ztOsi0->q29iS|?sxXReyGXjKK>!}=%+jD<D@%r#t5`<P}{kbe!)FwZJl`=2)+#7}I
z;Y|XMw-;*L!+wKcTdEKEJ?#LBS_;>o{tBd}H`;l%e<RQ<B48oxoOEQNoV{D8hw_Iv
zC45~_Kry1-e@jH~Mek9$3r`%JRWWz81tw2j;~U2im}jEEA@j-K8I^8tEf_qOfg&@D
zRfNTQ=SK)^9)u1<1<@@xPDZf`;&(P?z||w1^7(TLEYZvhwLgap;YVQ7i4W*ATMY^C
zPilIJzv4tzjKy??{3H_k4{JQ&y9w{7QDe$=TEKJU?6pm^0_xsZHOWaM>Rm@D-Xau^
z)5@e4&vivnXAI9ChFLxa>o+5%-KDpH&z#nzIl>RpFS89^JRI#0hbRuMP;hY2gw2aT
z>;@!czcH!Kl9TJ%j}&;XhZnzbj(Dn<zQJbA>2PquHRb*sqT?6R(~c@aDK2mnDR^7h
zwD1auQMu4^pWJHPIFY4E#n}8|XsT1fc(s*VA7)zvUm8}ots!{t2%eKp-;(_s5}4rJ
zSy7kDdeuRI#?n%s1oZ_1Cp{DHqncnR4uk-MU6J5Ge1r7*m3_Ym%PG1)ug19mh3RUv
z506RxurpLl_^BnH9er%ps;_~mG)B5KP@By@Rf~Cb)Y+|RzagK3yZ@0g&EK#hMpOxv
zlYO0%wI&WY6K17E>w;lrl00c}k$M#&>Kcow44zzl(U%O3pe1dR8NA5X(NNWeCalr7
z6q^3X6ucmG1%IASHHw!D(SulZcb#F*ryjH1m^}zstZarUnsS7owo5VeL;$cmvvPbg
z<X`xj6LqTovMa@SFwAH$W<?bO90e>*YMU&sa%Z1vtCVh}mTvNv2hOjl@U_?7)&7o3
z-r^K49ql3zi)kI0mJ)%bA_F*Mbz3f&EBNH;5Kyo&Y8mTO$|qJi(9z|SV`?*lMBmt2
zzK55OB}@cq^P*r)%(P?1C=uu61`cHhyZN+UlHu^q(DJCMEx|clh6ZnMQ!G?aFG3%>
zxyOgqWCW452qGGtT<8qaT9pDz<t`HzD%EHX%^rwTL!gZtbn63Wxs6eE1$vSL7twV+
zDTFV1otwJnq(8=;F`$)qqNLkpqx*wv7o*QYL#<kO9WkL$sNVJH{=Oke94ayUXaMPy
z#{uMhYlp}eF);;a)azu(7#1)D{PTi=C69U1_oK@xlNB!?G+5*E`tb7o|KUUr_Gvmt
zkJPpeXjvg3TPRAAC_gzd1gFNDy1-$<go9m)wby@SMG(<=`Ei;ctKzK!m_`CQQCu%e
z<`}t!BtfsO_1dqq_KSarq!UG4a7MWNvBB)|-7TR{QoIPtoMd@fQny{HKsa-5w#6;7
zmk1m$sY(!$D5w>xo_+Ml3Zkdd&Pvz!{&BueaU~gqEY=qpcl_3Ax{k+Y>~b_b%sz#N
zC(!-;t{xxaF9<Si&O2POLsU`;USWxBk7p*igTz!4Lkch;q2|}qP1G6NAS7H*ifS{_
zOJ3W$MA*ZxoRl}isV*rduR*?s7TZ-H-@s$wE$<EUFG9~osyBI3+qcR~DSRDmhhobW
zCwn7l3L^bKdQAC<vmgYdV!8PL4|+^%%m3f>n8Q?@fqsxZq&aRJpY6-a#{dsN>`(FB
z?T7CtAp8>c8j)=;_&RvTfA|yR3-U$pe(#wN_%rqz^n2)2KM!!|gVF2wi~BORkN64s
zs{gZh^s@>G2ORyX&y1Y`_5|Jm+3yg)?mu0NfVI7wZ~(v!akG;Pp|hBwKRcMsXI3m-
zF?1qcx)8L1!ztFVqaPnSBRcW+UplU4mpdt9r+#+nOx0QF4%6dlG6gSkAbr@_hNt}4
z^8}gsKYem)PMK`jl8$wQ9)5_8!tgSu{%@}|%Ao$U=$7VzI%!gJg3%Rs>FygSphvvJ
zp;YhEk51JKxQ{sKQraNeQ%jd?{+%Wd=7cvyOj%n%RY#9^m8&F-F5fJJ+`HCF35-ey
zWbNxjlk)4xK0l!B?DV=}=Uf%DHnvS2T><hAa(7u$IJ+7ACsEdTNYzQdvP0u<1%o4A
z&s36fX9pN8)K@Cq0t!~NMAJ*dozZq9y!1WcjZUf@y;YurjtM1ROOg_@6galwx-u>%
zflbQ1epHIzzkN<U>}bmMYN{!-@E`V;<qP)1<2__?;@sT#Q~mB(`fVpx{WahZm1hgL
zM01ahFKMeqL+PmHZf487=2r9ExB*?REf<Jpn^Be~mm-Hr7RU&KWU5OlJRq{Ei<SzI
zaUTKi?bI|1iGwuWjw5U=1&^{$$F>5hd5Vj3tIgpOlz}Dwf*44_{Lpfdb@q{CU|1~<
zAGlflkrxWCpseb(SfJnU4N0dZs;%W&j{fFIEcEVd4^O)|b$AOi(ML0vpmk<dw(NLi
zUX9f%|F$yZ{NTwm-sk@;otid8-)sm6amxw7xgIooKY7N}JdzrdmiQWBFR)L7aNBxk
z?w*04^r~kI%#tA<DU)OCf}JEGm!T|TZpg%5MBWZY^L)zu0g*uniyjwzpbmGBt2hi(
z<^}p%9i-jG*F~Hw7<F5P%>3Td_8rXwCX5ubxAXfh6Akw){Rxaj%1}2-W=-=EaHh0i
zOlEPY?UmrOuRt)V2JjHx0UkcSjL8R-skL;@pUxqS!p-k5)eAs3x|`T$?%2>l*Gqo0
zaJAgOCWpV<wAYaKmy4gi0#7#MxrN&!W?NPFd4T8PDvj-UrN?%}1u1n-X_!O-!sa>;
z(8wowbLP@bq}Z=4vQ3u{BoZ9r&)P@8zZ)!s&BNsBxZZ{)=loc}cA@u#I@@quZrIlB
zSC#$kg|Gt8;}ywsL#6|~%{7W4QKv@~93tM8We^fRvN0z>*!3>~QmtfrA2*${uhs^m
z$DH=oU<-$N%2DDISVtR8%u(>Zl}h=oX}>?&yE-@G>TXI&Go+=t`j+7^e(yN7CMd@D
z&{rhN-pM>RF=tN7k~iZ_5r=*WbzeE)#IPja2P9%x3eVEvk@DU9N#l#i1~khq6gBP$
z5kQ4FwYE(o1A~LlyjtkuxiFSi@CB4fH5ht`tqIa11cv0~-|Smmm67cI;tT?HsaXVA
zby@~}+J5TI6y|18KVB~^TQ=hkkeg&Vx_XY6sEh+CH83EcpJDsD?^i+%fCwPL2`O2w
zpYtt9PEgmGZp*)W(^2<=hI#sXzxFl!YAd_;SIoou2xX~P+JSA#Hb;=quF*sINAeKO
z>T*>^rvE^a*51*vlFsdA;y*)2Jkbyf7X}IgC1dz(`_B7;PB|V2;wrEW<iKsS7b=+(
zhbd-#E71wU6*Ko34cZ6%>WIg?is?CQC6AQYvt!X7P)p=G1#ArmL%!v=K)RI5;Rr;8
zr4=*@#z}T-7j!<Qe1C&H6KnjsY4b4%lPre=iS<l^GBdpl9-8-UWt{DnPx`Y&2|%qm
zuoA<-^V=+ZvkLTbppC?Nb`TN4)WdQm3L=|i$hENXVzaf-q;)4`L9g|PV#nCk+N}X4
zjPLj@@SZC8K+Y_H!O{hL8ih$jbJCo#-IUZH^+h(ItIA%|C?Lb~4|nR0&vNLOroX$X
z{+*uOT_+AxFn!*|G%E=J(&1n^ar(u&B~<N!`CTVd?dzh?@{2aCp^J{ik(|)X^HqTX
zSLNt)M{GsiAju`x9G(G%)3muMB}8C|Dx0?d6!5P0ra+Z1WSIt`XXwMMyU=cB)nUI$
z^FK19{HSU#R$x%>XBjC7j$=bDt-ZbO-N{2{=Enu1S7}4s!3eU71LRaA>jxZx!iR6t
zq1t%}ag-Cv2z85wuA=DM_!x%!r}D<@cJk_UvFP*}cy1k)))g~F1%Qm(8SutDo^7ws
z_4mj{goJ;@xbnm(Lso;{q!^b%|KP+joZGm?_8ggl{i8O-?s~a<KPnA<hF!e{KrkTu
z6yPmnhoou6-Ktd!37|SKOLL}7yjLe4aWm()M8`Rd3PK@+v)7_&&vkq|OWOE=FmFJ2
zcp>^R2T+dJ^AE4%fYZiTVEaKHte&We%PJGg4&+{r=3S{ulhHMzHD&XyP>X{={K<pE
zJi$X9-aW`>hks9S%1lF#>*>--+-cKjd-*ph{z_p#V^zB|W!}UV8xoF-h2sO<hbC;Z
zaE9P;>zoq|hT6uZ%H=xTyXIAs=+-5@cpO(6ha|xG1S1(=B2ieD=8#JJ7HA5!`&Ylo
zLBlDAP3ND=olqA878o#^F)e6*H4PEDu&QBlR2DzwsGwjWe+#=8an~6N$`3f&zPX0$
zo8;MbW&T2OX8kBj_NKXX1|;cKA=5>^qe0Gv!SwYH*Q2}yL(x=9bz6QmY&RywQ66kP
z%XqkCFxE(St22mkz~8Q1+qhn=&h`@GIfogm5C`imH3>`NlBntJ4z{wbE9;}tm3M+0
z4!oLa%CCR8vb<f4^#aO>Hj}h`18|9n>k3G$0GP^?FNG~GZKS%pqwmX~^-eNDyM4_b
zr@!=bs~n_#B|m*e32cZKNP3-oup*FY5y%|cwLqO~U_I{4Q>Vt^I+yw8CtERDrp2pe
zVPYV<Ifrziq46phJ`HbWFaw!t#&N%<zG)-p$gmo=x0(zCygXC8dj<P)?LB@6n0zuT
zCaVXN#Sp_);nv!s?%49U_Be8U`x}WCn!?gu#NrI<m6<1%k&1tX(v`D3MB#7Zr^-LG
z-q%}&q4Wna<aK4Zo=huaW?~=<zi0kUCS;6C>&~B%@^<wyT~S@!Cde}<te+G@74=7v
z2gF^qal@dN`9i0@^&dXF@I;w@@oY6m*2Yr#m~al=D_0z0TGFq~0u%q)%tWj|l)m9S
zmbi)0ZNgPQggGR_=vXJ*cq!|4?x@>=Zb+K#>VnMOU3Sk3akwJW1MrblOX-k!8;J#}
zgqLGAJYu`2%73mTXdfe7nBGTSKM?aXJ)1w@E%E0m9lxG>a69gRFY0dcjfmx6M`}^7
z;not6?y~v13dV6&p+iwXf3a`vV98<G09W<(rY~w>pEGUFjdff=hG|hjSK32JVVvo*
zbnr*1DIIO|2U$m3HCL0%?$AT8EuSt%$ftP0c;aAk6`Q8yFEZ`xSs(9tUbr_L7vIy^
z&am3Hi~URU7`IUe@Wm%(-f!L~Qam)!Iw-@{k=TNat-IWV&(yW_7>MgxE@4O$?$ne-
z4RgT%GK}W7%X`L!2S@io9LKFtB3H@r=wiO&<;WhHWtbR{`v-c~0LN4e;8#1p97dBE
z>YgUW;P%T{N8!Wr`silMf@-%+|8qGhc|*>?g;Li_R3(9(&c1rstF;be@vj_P_%~FW
z8k~Aht5<6{O&T8tcvU(6(@^YfG?+6rgPGgtbDf0GC;}?^+-y(4*9+;E+W?I7gHsW_
zQb{2^@|6}p=XM9U>_(&jN9jj}I=D^5UHB*&NCDQtwg8D!RPYD~<xf5Hq~|I^CveS?
zOLILuu`6b&>c?&4o$cn|1_;1Zu|56s^|e6Dg1n?_%}bW;3<>lJN&MYrO#Ss6R1oU3
zah*R%qU?k{piCh&**7M+xH_Gi;D~G-px_tYv;yHA4*-X7iaewX_p${=#k3cz9?BXb
z_1@7_{uHYX{#&wCg42J1`dk>2;s+r6o$bywH+x&3{8cyh=uXcRPIZJ4#iDqD70(_m
z9SEj~;2;aZ=sevFsRl8S{{qxB#y#sz1D`DtH`6F%KLj7{&+Iq>gVKIaysJ|#;#h=r
zvr6_7p$j&)C54);;-H8YH6hds^Ybcp#w<jWNgsnru^Hwjwu0S9@cXu(Ja8{EtJ#GI
zrV6`pV(FO1&6(P0mC*%r?#|B<!yzZLf_$u~Kl<9fPN#&>87A{H53PnLk}CZZ*yH#j
zR=v~4Rk*;^BBacHqv$Sc52hwzITlL&sKn`9YjbIT!AY)jn_edFQZgR9!#m7@dE7uA
zMnt-r((etL$2Q=R!o#g0oz#Dg#M22gZx$6GToY?6A~f1{i4|ZACBtNA+12d)Fzp46
zFm-c;Bky!Jnu!b}>KcxtVjObM%G)hk_B|Y$bpS(!e*J~`mL_^k&bJTD2xkf??Cly=
z$60CmgpYJ{0xA{{{;&HI`Tx2v+j=Jbv8+<F7Jl7k%S-me6k2$*1de_mzghw9=byg2
zG9$oe$ivvZ{}V@Opm;W~z%>;(raIn#S7<h_GNh5D=i39G2-791XehW1YF9L@<8gbQ
z=IQCP@Xw$em;%nM&t9=1EYYFn_2<kYT9jSJvm>oU`vnSm$=FVO>bC(=JlK&Td8UAA
zT|tVwHlbBRyK#Pu7k*?#8a#UOp6=`PsP+Rypfp!Ie>(W5r@Fg=A8e#-)DLChzP@f1
z+2ol(|I)1ho9p{hUPo?&mOlEdBoj0K%6Jp04?;^|3E{BJ<>t^T0o`K=Ty*@l^hI_|
zBL7OtwZWK|y;&~T?-eM;s?|LDFfi*|phC<v76)EjmHdkV;$Nr}&N6K`?X&s81+EO&
zD4{7IbN){Y&ldm){J7UAO<47%&+Zy=nJ_C^5-f`}1H}%t^l+t6kbNYf;XTne?Jarx
zb7<(xPK)zfN+j8N;F4gS(a`%^!-*jm5dK3Viea>Wzm$KE)sw_X504sxh6f^X(3R|v
zjxK>k`ns^xOCGP-%p~5Zc*mzAWw@DK3IgWIy0G?kYl(0ls5S-%)^kZvr*-?0DfS90
zzjF3aY`Snv7e}qWMM-+`o(f}5oE$nvvK48GbsyZ$Sk1=<u8Q@#3tp(pxpD5ZOSpGO
zCxbdw!*vO}P~=SuX~MCPYm!!un;vrEi)c49!?`3dI3|vFJ&z%f>p2p{92i!cI2bk(
z8Kx))Hxi?S5U^u|IfOwnUYNpo^ud(LVv!;e^fjxv;TIvHcXffOis!OPR1{P@1y1z<
z5k$XUV@l+;v1$h5J9lRY^yY?p@DgP83fHy_7=2lVj3Zn8#d_z;zG@uX@<y6}nYZgg
zgB~SDoTs$YAlNGqtv^EL;B45VPAgeDOLizu7$<1r`k6TtMafu^Xi8CU#E<pIQucC`
zO-71s3#xtqr}M`-z0G+@5$!S7xd)i`pnf_~!)E8*wHaP5M#&OaqQ=33bPTDL)K+vO
z<qe{XTH$I237BB3=W5$9)L8~vFH|e1%w*C(pZH)_Y;zavJOr(T+KN9mqVCgTbQaaD
zh8`owio8(JiViPYK`3e$TwtXtE*iHrJTDsbG>vz~JE1GQn!pfn%p%}tQ-yo@-Jwlp
zY|sc?bz9x_#m&1SBz6e4xPX4Fi?U3QOZmhi@yMh2^-`F7!WUB`de3P1?*?sMe-;oJ
zP-sUiH&p3zBD&FvA6d;L@1eLR{YrZ)Loz3ht2nA-tY<K+m835_r`A5@7Sq_S(r4wv
zVMHSelW8)v+CoN0(1lO0H**W4msW2g!86A0`;6wRk;CUqSW9Qvh<qK&P$If6=A#DZ
zd1Yj)_1NM{4o|jAN?Ex`Xx$<WRUe@I&v-WFE@O8E<TWA9bjs3OOkv5;+#)F7Z+sk3
z?XQW^W-Ao%iP=DC4a(NtXfN$SBj$m**QfJJg>4m`S$bJkL-oHs3p{$3`NBh`bqMcU
z(1eAgEX_Jq>D;q^xVa{t306)MHKZ}1W8coO0o!Y=bgoN}S0JMcu;X6){4oUy8vx8Y
z-#F8G7$GZA@boD}Dkyr;5(r9v)57~wwSLizOMvCC<FUp%9DGA<q+pvW6^wz~M?@~?
zYI(g0g7^9rP#H!3V&56!dZ~Y!fmZfuHn-IE1SaBwwoxV8cT+VOZ8Ua|q7h5+j-BKu
z)LFss<;DZ7MKMpNuxyIUjF_CG2|_{Mm0O^n>^Vp!&d=`|w2Xni*l4lq#uOa%B^v+p
z%IPir9QesB|FrF!jRA*iGy{pQeAi-;rUds7?+v)FUPyU*WUhXftMvp}qf&}dGH-|V
z;>&=mMzCA8t{%(eP*xS&h(!C7*Y>}MmDr6Tv(-PU7mV)$INe&i)dGS~{S|d!<E|~2
z*qMc=3lL#XqtZKN>KDQJjce!Q=FEjuPdX`j#Mg#?vJ>Oh$kRAz+-9P$FHH5Y57a;S
z&&PWnSJs#C=87+CA|20psaeT+r5%HKJ)HHJcG_E2wX>!y&_11Y2}g&y8G~Q1-^njq
zl6IFp1abHv(p*5Ry5zmbVf>?td|;v_ORX$=Tt;Z_M2gOh7-;jy?HATjODggPUV|&)
zOH=-?sn~>+{5z`-^j_rfMDyvqL(gy}{hMiqxuknNkk0t?#P@?@xu-Qs^f8zAzDi%S
z7`2Jt|DllUe?^$cxc?)cF`Mc8-iRXBON-AO_Gl17tk)*m|KD=I&|K9fjvY!C()@`a
zULgmrZ;{?~r#+AbHJ>o<r4FQ2;OF2J+x}ZUqfNeJBAkAgQ8VjJ$c02SkWwAl(!$r6
zB5oE@o(}pt%jmfBw&$S{qNDap93Ufwx}qIc2kDNndBx^ppDy;;Y@vag@vAFKqcnw3
z2DD%Ci8PxJA#eV)&5QdCh?E&*I3PtvY<Df*d;6^$hjFe<HqrH?DShZOK}3OOr_?#b
zNVOrB^&v?3sV)};3gAI3ddc11Xg;pm$_IJV#6IfO%F+IJ|7SVOG;7E~<2)r?_rrOJ
z>Z|4ANc>{cJE$v3WOoUodwGVTQ!SH%e5Cxu_&GOr$+|4~-`Z`{pEjWZ$=i=`ZNDI(
zg50_t0~5zO%G=#PLG}m$e!9ukN3eOkvUpfsEjO{fJCC`-CD9tq<DadAyj}pqbbau@
z)@fZ=@m?w98)1aL>!WJ7b0VQVF44CE;Hp#ZRnT1iUBk#!iZQVGQ_Aof09XPLlDUgp
zO2UhF3kOFeSXPR@q+2G9oq>^OctwjCTSPA@Ew;X-lo39IJ4M?V=$ov4WIJ}Z;zh(6
z43&$Tb|=~Pko-2%rC=#!MDESZ1pI}0f8kvb-(NgpTePZoKOEzd9QgZt=*+3rw%(dD
z$PSpZyfybbIB#G06#l3EE7&Zv!*fXm181>`U0NAOh{>~Zv%X?@Lg|;k8!dgN`)q1>
ziAQC$;Y+kP-44y;;8xFJw@kc<57ZUOBfN)KcvND32t8f#L*oB##cGL$qK}4{;Y{xp
z@7L32B~1(5gAqG}SUADl1tOk+G|$4SU{NQX_<^}j25hL6xH+tzg^&NUVavm0Tlxkg
z$emypbDUYWWj<kkyNK(mOgeZt6#f+o648a_@97$F_%TD_$+zvIAo!uCchX(>Xy&>+
z6(GW1_*tA`&$b&P?4sic3&@%n;se@T8wtJI>ywZLS*;*v9QFSqF+4O~l^-qpaB7Jo
zi(GWJlMw^3^|wrT@idbwk}@FDsIcfRAZ#^TnXzDmOSo2L6!cP_KL>soSEszb|E}#P
z!e{dOmPH)jQI9I_KCah)$nZHIwgylNj1(xNU((im_EpgG+0Ym2d$CQ698ZGo$`KrG
z{rVwQ(0p$6;+D~M?7w>`o4ClvZ?xr&WP-g@nsu1ukTN`xhp!toBY%}!|Ep!)p%@^)
z$t(FnSo@6%&_GBi#Q9LM*Q=)DC<&dyfg7$&cnPt$tLqez(4rJM6Q)dtEtU0YOkOnE
zY2PP=Z%U?{g07gQ{R$aTW5CkWBS78c>ibK}uA6Vwr37-P%KmMAx_7(VAb=nBS?fN8
zbOB8}-3CMPqxY%JmckQP>D6jrl9R7B9F)wXvsY!HrBXBh1Pk4Jg^%9DFOkM@LzA2U
z;S7qN->jD|civj7?sqo%^HxO*I@ts)GwU8YA9Q@n9Y3`-8l9#og7nAMgxQ$Pn6gaq
z)G?tLjnKKc$xXDobmhcaV77Gzn0Y)YW}x-X!&oOWR99~!?3-S!Uf8F7-ovT~AwOJe
zgeyGFJPWc~5U2rtiQ?2%PCjvYBi%XoOna1x041Wbc2|>BMkswpCv#{No4tFab!FWh
zwI5MAe9lGuiA7)WB6XCmC3z?Da8oR2GV77akqe)VNxtZXfo8o^wYmmfN4N!HVp(4)
zC&Ez9JVMoA@tX5l%UD#urIeTreEr<7OIA?tjQq4*aK#fYl{G#B02KMaT@&eK+lZgJ
zq{{lD!h2cF!Yd_zxKtwxm1UsoRhxiU0_EzV>VAh#WTG6j-OQDZcl<!?kt?qQq9r^8
z=1J@I-pr9M%C8k1Z+yYW*FfP}KKS$!a{-6D{{WQ54#eJ5J{$eqzp0dU#K_NMp_cJQ
zzf+gw{_zMV0NgjXh+ZqT_O>ykJm?UCw_dZ-z_f)|5`a#SX?Xm?@}yLiL;^-f>;fU^
zic&*@;{K&;;m*GQ)hJ=L#w<@`uKfMSS%_+j%!kT7M7IFt>{i9vm?4%f0~8}&DRdn&
ze_L|?90q2ifuPI@(#?9CJJRG}%6ahEFx=?azfF;XQ18d_;W{cZ$>SCt9b9OP=^>L7
zn$bj%ccGlgVY)#7%6nNQd_Jc3Glzc~$82bdq4ZzvIk{fy*!F*e4U+%$X#QXBi1#5g
zQe<O5&%o{ilPLrd4*hQp67ngcZ>cKx^m`%t+w=_MWI*c1D=D?KA)tZy;sn++4T7iS
z)gPgC^Ofgf#DPq-FaN2pZ8UTh_vgy0CJErOW=?v$@fJ0NyJU!ci%07q^DHa}GXse+
zN3j#2zcsv7C42<KM6#+N0i(JsTICY4A>KK<MTMO+Z|Z8UNZCW6+2ZX~!wOrW2j8e(
zt0JlhVCQ8mngTqFgr6JBnq59SGv&dlyvyVI83GCR4j(J<*E4{YO^O!+nSuUE4I6eq
zCfd2TrMm+YT0jVWjhunHSJ#gYfF};xwN6x|$(q4O?C+BS5@BqV9plb1P(G=~co+)2
zB98k3oa1u2(^T=|0-@Mh^Hby!h;tym63zH6s;tH;XF~u(070TZwN8_iH}ZW5Oh*79
zg!aF?C!CUuWSCnm-D>NEH_axLUvAG!LVjwRO@M)S|AOj^F@KFs|80GxidoWPkB6Rg
zoSz}k%$wiwo!4#u_i365$ypt?9IBWN8Y>3b&(0+2c%7HU=x;~P0OL11ja*-h0Y2Dp
zy;WCo1Q#{RPL!}tERwjW-9ADmU9Z@wGSRUET_7Z#PDU(5DYN@W$&r|=tYw%8daBn8
zC*=Vu>+IT5na^oKwWaWHRB+Egk>@#shy*G7Ojpx<=5&WZo4A)`H&enp%hC=EHZg*t
zINDaPQ!-7^l0i~7BKzS3%Z2Z}nq2iq3f1!p)euN0;cR3N-3>F?#!;7qs-^o&7rTuK
zU<>;#zTWBNEM2*cDw|)UG$G={8OIbImA}Mo{<sl^Ub=U@e@Hw-P{Cv~06a-WVShd1
z%W?onO#_=B)yZt2o-q)=s6P*V!Y>{`Qdd2w9OCShW~Qr4lA_GnLMVUqFhEjC0)6dm
zZ7HYfM@)LAH`y(2%#xy1n0bGU1?-H(<xt1##l>5>^Z5BBVgA=zujsgEBZlD2ZPEcd
zVZcRqzjMP*sLdejBGv-*hBik@@P3RC+alvBgpJZx(+ykX!ey_%@*4y>WxfBs4sI;X
zWXk8pXNi)Yn5uEYAwe9LLd-jdIep$r#m}d{dmE=-OmhkGVRH)Q$D{CTxy;DC$pw}x
z%g0jTGWH}C?gf6FzTB8HNxb`Vc0mV)R~Q4W6f0&gIjn09-677h>Itfzz3m?}Sxnuj
zF3EggVAC2GRMZR~ysi?|5o7_PV8hj9Nj%SL8d=tqI?YWbD;dns9hU155lnt;z-M6$
z9OAmSD{<#I!W5NYr76Y~qC-P`7O<y*NxIPwB^ig*I9sM+865e!EW&M|EaKnX{nvHy
zV?!dbC<ZIg-^cBV%feWD@>L8$byQwJDdeX;16tLaUsG5UGY7}0=F5W8^^jlUUno|>
zgt4G&EO?U23%{YsIqeJ9CUa@dhuH$iJ#Wcsfe6+aH{w;)ss?hX1DE_=qbM=DJ1#%H
zIPO4?`3AJ7Tw@)l55)Z_S!nlWkw=X)Z<rO87PA2SqNWTDA|K$i2x=0Sw^U!^$ZiV_
zHcM%PCa1A|s7T2`jF>7Nb>q0$90xs*8MuBGMyaLk?@COQ5jLO`j1@XZG?QWe;ZIN&
z7rJtYuIexL14Q!5;`RJ+3Ge(EF7{x)?S?5C!^bP_)<C&xC6)Oi;$AS3oJ<zFLtf|v
z%`^?h@U2MBjo~%24xN;azc8F;Ox2U1`M-^kY*Hd5+OtjMHiooV4y^$(D4MT6P0A=d
zyNLc)RM@Y>g(p+}R?#!>r})p=oy_D@{2Y$@eoJ&3?#;KyET!gJSnh01Mnz?Hp5<^;
zYcV7W{zhC2RW>9QBC}@uWi|JCg{%5PrL_4ML2{PEYUI$N70XFZxtnTJ*A#e7JIo|c
zlw7$KRvV?F?Pl`}C>iliR8vVWi|xV1i>mI(Hfk<4#+$D?TwGy{v(tW?oT&(>%Jrlv
z_6$-X{UgCqL|mKd(x=~;R$#nfPB$?}e~lc9FY=Q}VhCcBLyikMQX*ne^;nP+mSfxc
zL3o;~q7>Y03=C~KLn<2p9y+&HK76wQVhn*Qwa$SgZ}om3X#u9)zJ_mw;rOBLItTzP
z|88E!(@Bb-R91XTnrw^1qb1ga_}-^rt^c5e@4x;`$N&A_S<089{L#UssaLRXbNBrY
zAYgNNvGE2@AS~fYVS=t&R^c5LDNj!fYXb_#1L;LS12T7CVsxMmGhn1&4MGC06NV!e
zgDtST{0I5hvlP(Yl0xe3`QZ`8i3)HG>=6<RWf@LU^6k%~^+C~LARNl0(BIf0#NGPU
z8OWM*wSKbxV?@yEl@s>(6%lAMbf7kivWt9zG3Z<lA%?83OM?2r4t~I&;N5|f!ZL2R
zhLGfo=`p_VHQ#-u9>tX+5WqnY*{^MX9f}+r&P7r9D;WqX$eMbIl$ni6XP3wxyy>;0
z0TOeI=zS8$KOu+GrBr&EJ>2Q5d3xUh@39MIk?zs4`={7gY%+zclc3!nuIS`fFXS=P
z1MA5J74Akoxmw8B<kaAu;Dr>k`?>wu^x_h{{_jdov-_)TdnU{mN-XfW<96ksNL0+g
zGO#fGm8`n*Ve}p}bttxgqRU!fd%7h-<|x+Mw$EFg0K-o}#VQPRG;2HBE{uehex*UR
zVP`cPv^`6M7g{xw9mS2&2;4S4+P$;t{%mxSj2#Dl7Lbf>(6g1BBrle)>sxd;HFoa!
zY-{3pp1#b*bLBP5K@A+43t5GEK0TIEw`-wRU?1EHq@E)bi0$29JJuj7#n~h7j#$2z
zgLxp?yvlP~F9)>HT%r>rv5}maxS0AP*kXTv#dn>8DS=Mt00e2UW7F0qks!faGOUTX
z6w9Dxs!$}~J}0_^^k3ObeApxT1yA$y<qu9Uro+xn9mqIt&bK;K$+a&n5@lWLW*U6O
zYICs2%CEx8kOsT&>oKZ6k%Ej1dsC2^RnolBSMlIK4j)X1UC|)ZG!S51mX^AzFT1gM
zvgKV-n$3a`NfK9g)~q6&Rc;e9%r{PGjoPD%gaOl*%&jSm2y!y*(0r&Xnz@Nb-&7fw
z;bi{~^vSNoXa?wAGlMUJYa@{8gt>Q4(O&(v6P|C@bWp6Hj>ib2{49+HcA^O*x*_|D
z=<+w1d5h3p5`Zsake)feAAo<3g+`PTUjW%#hDb5bweK5`3<%|s<gSVaftvNhAGkgV
z9fIrJQ!<w59ArfD?jtQAu6lTV7%JnTaF<#dYX71P+jSN*;3*6x?``mLgyiEYHibT3
zl@V3gAtI}MF4+n8DZ4|)sBT<jfkNc~+6kPKSw}`<X=5iv#m&fkHhPgDOHFPaZeTJ_
zyWBby#N3-pLiv*Aga0zM+>~4dOT{>Ux}wzz?ynl6V86L~bEEy>>rUPC=orpMB9hc)
z*(0V*KIF6}n$j2d=C-e<^7TMG^k&+JLIOJ>rITe9YK+M9$P3UW;q9t%9XBP35Zg|V
z=yM@Bg@r+reRulOpt_s;Zp@8n4tRXu1eq6-W^<Y?a=CQ=y(>EF!{5X>m_g&qvuu?W
z@+%E<UjFb68mVk!l%mD4R9y_O)Tj(NCZnW6R3R3XP3ViVF{tW-XuX=vU;WvH91h3r
zj)vkB%3)~RSOlFwjg^BkB$lGimBXVbGGm4N>+2C56`HrYky|(f<q^;2Oy8}|XX7**
zd=PPHd}Kot>X2L|h0V^Qz$yLjxHOW^vVl6>vX-W&$ltuzgg#ZL|A8pzXLwkfcX=!?
z+R5k}{j`W{sbX0nNC61uq2^Qcst1&LBV@YLt9Z^eMzsndL~&(vx+0o>`^t6t*sK&$
zk{49O3Q@2}D}AKQSyq!9*Mp8*$--8JLKGB+8U^AXwRW2n60Ca43S3|nH8*i7QE_Ox
zf#NY3a*#_4W(Q$d30CJpEWNee849YQCFr|@@bZA5{-=`sB}*KzVW3xPxkx&f$f1%Q
zF9QNx_Z(#Ht(JjF(PqL-EFZ<w$=3W4_Gp*Nd&$AV#j!X)VRnzCB;<+a0@9Lgdbp`D
z8`*<Zj%MtZB5>nd;K(SGdYD*wSoUO)&dBSvmRamY@<^yPr1~9GOIa?pDdS$Yh~pm?
z=Yz#SKDqRG=5=C^!Q+Pcp>dxvpmKI_*vC_mK@&Vn_>hC9a(Ye;8L`A6|BJ6-p<1e{
zG_t9R%<znZdw4vWt%}*eO*sist<gS{yiIO6q)d@i=pFJJGkoS+@#wr`;E+=T-EAhJ
zLH1hImrd>I?*H(E`M)yF|C_O{>Dk+`GSAll_X}}cMws)*Dx7=NcH#8@eJy(C1*5Mt
zVn69##Z`#HjQv?}9Eqj~K%r|DX`z1~*cB03USSSWbyrJCpaKiQBhr7)FaRa^>W4!R
z5C)IJFLG>@xKz6^g954~6P3zMgd<}gd9Zz&AAJ~SpAq5WBKI^9Y*b7L)u7dr4vBn}
z;&44@M~ru2jW&=`gn(mEwjR&dkEi1ZEG<I@DoIN^CP=CQ>FxH1;dk_;W)S|eJEDCq
zFb9?vD}HWuKIJ?MK#Q+9*L>T;wRJ~7&`fwu_HU=+()5u+5k3_gH7#+bzxy`kIBeZr
z@UQ46m;Ol1(K;@|#EOO4U)sKDXEUU93`;>;zi$FYqrU{c5t~9A9}CA{MkbiTs~AI6
z8uWy?I{$Nm##YeoO4*!dog)VKcEd`qb@29^YUwDlvba99HHMA_=EL^K|NbzID8;(}
z^}+3xD7ygUoo%KA^7Tljlh2vs#8xWfXlkEUaDiqInC3x9`>LJ!X)kL4q-pLFF<KOf
z41R!t4W10g{V$s}Mif<oTb<vH)GxbC<Z_y}Y<4*V*Wt<*=t0m2>GnUmf8uNVG`t+b
zdvB8ps=OP(0nk5V2x*q8=5M`pMqxy<rW`}u1t2$U=(U|$cZWm0kli@Di?<?L`|avH
zq3^c|+D+(OJHPIGnQFmDZZybqzG%dkqw%^(-?tZ3%dOoVC-<ZWvG1Yv>iG%5U~oh^
z!?G{R)b8z4kki$USg0lB*UE&op%U>1dvK_8M8((9&})@tyR0-`s!l7a|K2X?B{~4x
zUtH%WhxX=ZpVW(W83mf`5Xe*){qvqvc}8k#S=Cn?gR=P4QQ5AzOS{CCZdzx&T+a@4
zcpJot1FImvA^AMuZkMSNgwj_n>DKO8cUlC-_z|>v6w&vlE3E72GnZVaMOPyut8>oT
zkio8B_Y~#qGP0|FX#LaBQ@mQJY8PZ3x$;6s7~wR`0rBCcJiheK$w-}{E^Y@V;ff)7
z^$6RZ*p+_yw-+HX9(U+YbZI%66t<qtie%HjQ@-!I(Diw8p?=T&#`<odb{h-BKwPYI
zPu_#?nn!HHOGO%3*yOs|eU&qctZ{Tdc3(^+9hx7II9T<qW+9sec#ihgJmbMFt;@pr
zr>;VVNHdn^wo3qfUxRf*tTiPD?*NFwO=6tuy^WOg;zaCXG%t$0CNK?$=w~@q>^RbI
zFu3>?(T&H@y@Z(4NzhtMQv_~h{Hrle1WyIC?#OZV(+&v0$b0`e>{iK!!NhVDfWcX-
zu(8imtuL0v`omLiQhn;HN8DJH#wE$<qlo(%3MqGz+=TVWspjIAXGlTV(UhTU)^y0O
zpu+fuH&tB4-!G|!knEKa_^kw2LH5jpay_^8{%LM&-fS1gZ{Q2bnP+|w%_aUR1=2rB
z*ziIcQDSExOp%8D@p3Dp^H_5uWKt4J$e@rz?vd9^gB-Dhk!RN?%c2eM$V)lmC|F~?
zKux+|t-9lLR$0XttpKNu60P|&%4i)2^)tZ{ZeLPX9x$ad9vS4{9(<wJ>-e%%6n6`9
zC`!y2o(W)4!CjTRs=h<^gIocOf4N$>xnw`yU~G(HaTSI=49ydjHWqUpR!gW6C*eN@
z4LJjvyCA3S6F+medq&nQ>aQ#OCM_DpLdt}#{A!tP|LIGN>G|Aqk;f5`l5pM*XRgsL
z1+D%m_Y>3c9CT*r4DZUkD#YF^Mc${RE=oa`xZ2{pVs52;cq3L|SL;e;vaW620x<FJ
z=RBR1L|qIeR%hzE+chHVL_T3~>g1cpD&pDZDk-NjUih!Pl$L<O3T7mHweA$libXC&
zm14>+UJ@!Bap=)VP$0MU(=hs%cullHI{{QO1Gc9@Z)%DhZn3IO2|McIb$OAi%%mP?
z2XrRfv$TUJzG0&!htpp!{bFI341$yd;Zot4lYVm&_NEVR1Kn!vu30)oq~txe5ok%)
zM2#xq1iBjM*X@r;i)I;9Fu_itOWlF0K7&FsG0M-nN9NVEotj)DfmpeV)zth;;>6%8
zoTtZIo;PY_%s6j)5x$|F9&uoc^sJf;TR6FYy6p^##pV7h)J*(eW+-;jRLxxj{lEWm
z?hdMr&qn-;=eW_`u3{iG%!Pw8f)MQkEl^e1l)*sVXcU>JU}BT@+(s0bJCEGWvYobm
zYV@)(sm>??K*1&chCuuFV>XzxyQJM$m=xh5{$(OFGXDsrMEmr;w^bUzyYH)|@-k4B
z9<&UOAQApvySQ&Co>LxV2m#q@h+_Inmg>WPNVKZdMW9T7(l6kHuK)>5YYAe-J0PCO
z^Hy=oF#n2W$JXgD@i5|bPh1-9U<HyghXH{)F$)wUH-nUSQ_`1MJq|Y)T^^D%@vcI2
zJL@b}hdsZQ{!oOj4ChNLgH4l4pn`gt*n&iV6vw`i=lMZzq@G&1g-MGTfM~$dYSCG!
za=EJmq;YQ*HDg25vFubGe|qT69=k(4<PRYE)RT*)<blW;*{B_X%4T6}SYzi>n7*61
z1_|o=|6ttiX2t-;ZY#IbT+i|TW+lh|xt~(&rd;=kc1DX#o2(?31km^OoiML|X%>4#
zv#p9nFk9AiW9cIu^!Fbpt~{j@9`oJqIc_j+jkWTzFg^U4h3oSEQ5SXv>|A1$Y5L8_
z*ILg@+$%H5J*r}{;O8}Wyuwft8*`~u(r)=fj5xG-6Tyz2xOuvRcpY~ZcgbvW<4Z4_
zZ>;RW{N@D0=`-~$+qI-;zzJ42ElRu<*pyY3aIZMfLsNg)VO3SFC0<W6-aawvVDv?f
zC~oJyQJ{e9mIdEwEx?uw^Ts*Sz=Cs!a^e%G&D|^r;Be!m+hQ@=vtTyEpGrP1?C;fH
zctrL_{S)`JFo%a?fe{F5L$pitwZb7FA=?fiEbkQx=>FQ8AEplLKN~v?VIibIinvbN
zsE}joiwGRNVQcsW{poQDJ{`LzO7+gRM-##!C@vLITYs<IqQr%AZxsY;qk%s^H7>1F
z<A=&VI)7R4mkNU`!%_Lrpv4f=ei#>-N;U*B?}_zC+t+JIjd|0Xq|P5lRH>kE_E?MB
zNh!)}PC9q=jYiv+sXl|JEX6PbB6*cpt#SyH45JdW#R);TReSEplN29vCNr1KY{79k
zJ+>Z4GMTmWC^10u6K|Mm4g$v#9NUE|l_$wbb1MchNY}{}q!<uiG1%WvuXZ@3DT)v*
z2Nt^%BZ7S!?6KU5R)0pb7sryLUp{C3LvV3%4!yjdYFJN^K>#y%3O8sfvFvX_LJ*D9
zYe&0!Lo=PVqom?9nfC+-S`_rPvB}TCqG%&0&B5=NYOLZX+%1YTRgNFzl=N9UOmnu^
z;0G+f36>Blclty{R657JYn#36Uj&e<6`M)AJDW}W5Yfc!Vpnm8>7xl6KNM-<V_X*6
z=fKyv;}yKqkVD#pbnjXp4$g+qb>51S7V-TXD8k=an17ApT>9$l%ZR@{CPYizaWVgl
zclCK1PAKSlIsC2Y64WIEX&B*$0!~au6(&hZA2mvOGlHDUXz;XXpLwB<iX;(tOK$v`
zuY=b{^({fC7}6s*i<#L)8AJk^YOY2Pdh+YY7(!RwKodrP?vZ3qw_Q@b2VMg~#xSvc
zHWW#|(<08($TG)d@~@+q@6Hhh&s&l~(5clYflW(;H)%h-_U;RG-Kq<M!gJ3^na)BS
zfqbDy|IOtPEC&}v<%rSqS*!33xepgJ^I3>CX<(=G;ilz5s~$9JjnXKhm!f&Rcvya$
zA-D*I(iP#;*gne{VJl4gD^g?pH6CaDE&f+V5*s(j3aP<BYM{_L8r+({87sU5p9<t*
zPBkF!4t_1ks1I^0*$uNbo}}!W5hp6e$VQ#6)wyXbw+;E;^{19ABfh|=?o{mregdA}
zjKR2l;PGCF1ZzSI;L<0Kxm8MDBPSnsq=H5X!Ef1z?8%XEPFs1`F)de;CgKmPW(_?I
z`^;|uWFK`cm`8N=PH9OW1;lz;EyQTWn#B`>q_5-1h^IzvsJrSpGyaF=;yiCr7WZZ_
z=hPgwQO<!xI+TXy`!)nx>3Zl`f@{--SW^)bW%5A~SM=t5=^&=M+H>!31n}vN13g>2
zbVXPp#x$`#nqt73lnLl9teZ-gAmU+AXc9QuSPwDuBLWVVM!P|9Y=NjhX{h@7Qjcym
zZ9R$kE=8L#o4m!d#>dV)Wlxk~vG~=0MVm_hJ%_ZT8Gv#g)|#)%g^+SLa0CTKEeDcM
z8OU`oIxk`Glp0|GUOEJlU7*O1;{m#E-fF|9a7Fxl-5=O76bwbBIpn<lw~aWBjYcd-
z3rZRrj6n=!KZ!+ty*vPDqlIzC?TE<ld+C_DX!>W?pDF#35%!kX2w@$@b+^dO7VVsU
zDrk<GRQi&)Gv{|r0OVku_x!!z!Y6~rg2P~EQJ31{qX35dZShf#mO%b{{quX0m(KZQ
zbKjc}nMC#neYZ(VuA?Q`+k$p*!Thy}=&d8_V@uB#H`jztkYJj-mC#fK?bRa{e~dQ>
zcg*aeTqJapoD1M&Ebr?8z-)zdLA%MVPZ`!66h*(Z5k*V-Z2<UjSgH0!xDLb`Phv%l
zSjFTZ0dlA>5*B3_a>3zdLKX_cy72wIZ@fX}WfhnBH|($hx;%jR<ju35N&S%<*Lb`p
z$s!H(46>Q+YpGKm%v3#uiD3>+GvYK4Ve{y^x2WWjFh}rrhs_GXW)UU+Vroe}vf-{{
z*JB}E6S<GF@a7kU+b=rl&bADCvghv=O%F0>pg5SN7GWTOHS8U0N3pn%XU-t}ya{ij
z%{FGH<@XN&S6NRiBj6caAjnqK?1+y!Dhb2wD9g%SfRcYoWsbgyWu*kxTKe<T%p;8}
z+u|x2n)@Cl?FTk5?Pt0v2x^0{v<QHE>~7)JZS>LBxnF(DdqYPr^svV1wqH$)G%byO
zK>OR$xDtE=GfF(1Y|QZ#^_+k)BAad;fy?}S&G{x+nyXqEx}6q#`W&@N3#qrU0FKb0
zwEu_KFvArZy9>_Q=R7?qNrDt#_0iNzlCrRxcC!Z(@xBLkuJbx1_D#px(f`zuLqVY8
zxTv13;HiQdkGo;sp+4{pYaw;^hOP2ERH@t&?2Ife3-hlId(xgYX+T?Lc_Ln>49mXB
zZA99G0GFkhAPJDk5=9*lWD_{^C;)5%!K~6v57U+xm|lvIpDb~Ml1VWXp2d^TGv$88
zWeX=KD55(hNgxq`_t<CxG0ID=3MwjQvg2CZMqqX$<6uZRnq~Wc`nadBN&rTU!!_B)
zo;=yMZ8w=yP3C0mcAA=OO?K^?Y}>YNyLLL?cP`H5`6J%-TkE}e5=54=m1SVUCwRS@
zWE(w^52c9Ac_ccN<1tYRT-~Yscm{~Y6v@kT2lR-jB(KKiXBN}#E9K4v>eA}ijREI<
zmI}vGTBXo*U4v>e>BbLb6Ko<!zl-JizN}u><}9zb%Va54jFyw{pxOl0AygpgVYTbn
zVZD{jBS@KUWw24iL_(wg9^gy(-B5%_vSpX7S^IrB6XWl%ev<fm&cY7?+=&4;!Rm%t
zFBLFc1~#T}G_{5YB5HuK803$}QDcMHPv4#rUKGo2Pfr6lNis%eWJd5doaSTzDw-@`
zzlyLm?`!`~ruS0K>fqHnhrJWrboHL;^%GcKz9TkSaeymMLOq*jBw}k<k=smP#8CEK
zX{O$2@4ONFuB{s1`E(c74&N!rpu5)YWNnEKEIw8muz42FN?W4Nzs;G94+G^{HhpcC
zTX5PI4=@r8mVR8tj61r{uhb%=$@aK^3jNhLmn0uR|C_CbNRXpn?g>HFk_wF8ALxZ^
z)7vQUz;aCVuR;;0G$%n%F+Z{fxtdm8H#M0Lh3@blvQi|^rAxnt4d3AW>^UO$G&X{n
z(wZ{8LDg8|cC^}yLE@CTLr-%dOqXb5LP*L}Lw7jjg%`N(Y?MDCbcof>I|vg-Uaf<N
zucVG)*dE@V#f`T6^g}?Kfa<CY8uXu47C_z19%YBoN%4l@oUh#7z?zs0wP~kYF4G!1
zZj2kC7tQuqkojJQ{@8P}UGisSCYP3?eV`k@Ub9v#dB=PSz?QX)Q@rQxsqu)Sx<z96
z3dnvNsXdP}-^ax&*Y)lM@?jMl3vM@Tg{V7hGe^Cw?hD@!4Bn0j=i!8#M(IjH!Q+N>
zYPG*WGM3`3K^=K;BWQ|i8?{z2ZH1u<{P{QR0X0}m8V7ugoR(1p+flocrxUmeGp&}>
z59ckWxc#L2NKAWBl{DOy!XojN#6E-EK{x|V9gl$JeLYdFcug-a)fwr~pBqDBi^jaC
zdA&DD_SP7jLGlMGW*}JD(yE=<BP_ZJy?Hl98;a4A*O+2qNHw?(&3<$|&%r!X-1Chn
z6v?S{6bF}+!;aEt5FYy^^NFG!Ec5lR*EPW(v${aSh_$`2&4rU1Hbu1f*TIycDUn)W
zDwK+HzIa~nRO2c9U&wk|V%ibP9atRG#_!ufcZgaN=?527re>UmZ^<?q6ZFbV#^v~}
zWTa`e0@3T%WrhLX0M(7O`-*q`1_5I!S0P}!)mKmMdgM4h^G2xY3oS^4Ft|ZvRN?Tx
zsh1;2u%fXNbkB^|W%>YM<^vCtT@d>Gr*=~R6*~X#aZ~8_;bBLtJrQe~ju8l4x!S|!
z|4?-NeU<kJ?@`UebYqwk?MErsYdTvm92GLbr%y7nz_fyZv`?52ERgM7Xzq)5ti%|d
zv>t}Lh9vl#BE{;Z-}G+%&pUo&$pd`by&t%qmD_4YEe2g;Ebr>)64ug>t#2vy-?ULb
z8N4-@xG3%3w?!5<h%K;Q&Cxd3rf;*+3)=MAi}$9VlQ~(>*_u~uavjiCsh9cfF0Xw$
z)NG!<@l=>J>v=_l=JvbFRlI4!4WqPuJFAAG1Hpi1jo(}0k7<iT(vPj!YZ|%tpKJwb
z^M6WVJ%YJKMvH$Pp4B$K()&rQe{byxrb+szmxj(vd#+>EGwQLhT(o8i7o5iSmMlbA
z<^8!?bXACBlC$P~fDUscFKJtJV@tYfmE=a&och}xcBO}Nnd&<rF;=q>0bX#DK(c<K
z>YMjYW&Dt00a&rZHXYJF+h?yv_cH;rfTJ_q>w+Y0m{%|y>eLaZe(Iie76`X$i?FQK
zo}H-I%`_s%x0k};S5_NA8t*IJ)o~(r^q1Fp(bMPn)K6EY@8^9ek?;!7ifUAyo|6CU
z{WD?QSb5+fv3r;R5K)}6U$$;8>9=RbNdO~Ayvp~_4%%dQ48gkF9~*m|XpT3k^T`^a
zi0?Y*?iJUJW(ROOcj~%euHc0WO9a(QO$&RIs|^r$coA@tjK{Mih9#<u6m2YXR-Jzd
ztkGk{ADd8wy(<5=9W-SBndU6)O5b4@Y59k5H*Gl2RZ_oAurtut^r*@1-xy;1I+5n$
z(=v}{JwL>-)Q>ruzwl5W-B{k+G3VS}V&ETfX`{o`oEk><<UZ86)LhGt;?4qL);cb(
zov<pVicgExXB7RD9D0`;A-ms0$tm)ACl&{P_lV+&PgmH_ZGB+JhwvGT##$Fxjv$|5
zQ3hl9FUs@T?UpDV20@*iV(e=dmdY`bh;-NWT%upN2D8vZ8aNLW?9v2N{A0-6?o&V0
ztB|Z04O5XkWfV`%MNHh7ZAh^9FhQn0YFZzP24?{hC+hX2q-C`=h7&9v`~!DC2rk4<
ziuKBBo5zqY#rw+LB}=9t`I*k0|45AqizB$*#><7N^lZglnwAX~=^!SjkYN@2Wlg7a
zqMZ1Ou9EGWNmt!o*U+Y?=?kx$fXKeYQi++YRSX%3?{Hv4Ky_wA>lclL2`3`1PJ6bB
zWY13%F*4}kP5IyZF6D1!|3sIa7q&T$nKJ4b#NQYG66NUz1-^%c_Qg~%%Hv{8W14MS
zHLl2XRnssCmT^Sp1Cxfs5p>`#?qPrNds^%5ZJxa35MkO7_+9kStkq#LsdT6JcN}wX
zm_4cc7BWp&P?l=k`qa;)m$8(-XyJHk5;=f7|D^8YYrz}g05{~Pqhat@>z@7OOvApj
zYDUIV5e;%>-_UH$*>+$zW|k+z3bB%gxFzUOBvA!>MPKtho_INV?(Q)t?Rh7>9(M$%
z9q%P+pSzD7ae5nL7fRQ{N}iqsS47<w$$e7aKge~3DX~r68irDfWi?FPx4-lQL_^!4
zD*+&pr>RZBy?e1DW!2H=K<|^FsM4}4+oNL3KJyvNLtTY#h3@JXQMBo)zjO6{PTIm0
zLqB+)66Ju0QdiO%$()oOZ-coT!tY&sL-bUChtHg&w~Gz72||ypVrg#=!u5D&TV~CF
zS8{o<-YwF3ByC+iQQt)qH{1%x&PG?S_r^yqqIaU8&IW-HC*I6*-oZ?2UCXXKfL#-!
z6;jR!8snTqsh#s^@XD?gJxv;Lr)U>=OWA~imw0$|v~TC5OIB2KjCP=ce(jSAi@)Z1
zjX}Qajtjs*tt?+_s0u`skCtN%nY^^+70v{U-g!&HL}HG;QwSHl0Lvq<XU{BC>RT~=
zc&5B?U1w*%ba~p$uQfz2e*IEu+=iqNX|+pbjn@+19d9XC-z|dj58oOhf}u>H$*GVF
z4dExZ9OMiAGLp8=gSB)bLNEnjM~~8#lZw4$yT88akncbAgc0gOSS@An_GQq;JS9;U
zu|U(zQI>3YdRABc@#4A!!*nGcUFeb|^0W4PGkW<CvE5HNPso(;jfAgTnhGga{%EPE
ztU+!k+?MtV#k5F;8dQOd>VMhJ{|{ONi<%^?SM8$<=BEQgF)?&S?%%IyHYljHlob9x
zU3JaM0Qldsevy`%%4RzGvzQ3$*Tv)YIJ*{HOba_mIffznM6+ZCEjEh71kOQ_E0LFW
z4%*tALqr-2&*Iy&<Som^lj41j-$;M8tD-{LttLe<#m@yTmgpa&os7=#2{QN-8-LlM
zPC!ao(`tLO)$3dGz;lfQ5<kj3m+-_fsC-9#G)8z};9c^q5#lE(yVkZO`*1`C{e%K2
zm|~waSc*R?arl0_+?#zR?vOTnEllNV8V@~9KNB-KzM`;h$Lg%$y(xkvxFlAtX)$b0
z6NLoWKAjx86#3L1EtFF><ZWTR;OLFj0jR9Kk9GoZBg3gN!tc<;lHdg5iMMU(QyJcv
zEix-MDb)%6UxhfPG(Rc#WyI!8ny7jTu9lz($+Oe}Y@iquEDmZ4yND(a){3JDBbWJh
z`5eLCiv5*zy0X!Fw4@#~!mR`Dp+=GB;>tbj4q3mKlECw$_LI8DGi7mwxRB~>kmD_+
zB-29?zIU@l=fb8it0sy{4uX?%51p!IVRPp}&?a+4@2fLh>dc$5Zy@H7LQoLdSRb{2
ze(EC`G(!fvp1c{RNEin)Q=va6GZ0G+(pI_lCjpWU$^$5eKLX9}$F;{ML%M4@5NWHA
zX^`qD9v-pzMN7KhU^q1W3g_Lp89|~(i2O|LJ%z=cSnC%=$JRGv1X{={v{EX5b~AO#
zU4Q9wi?lR(XxgE*`ADCFtd89xarVG%N%5B%fEhU51QXZWOw&Nx@kltPBBa|@F#~?>
zy!mb5I=#)ulqKmoFcMHI3fMv091di$;P=@<!5LB)yxSJBo^X1xstP~gRdLb_y&u;X
z22nO&cdQvVGxHqCHJM2zY9?$9$XFCi-1tZ=D#7>#6d^`i8D%3y($L)`Mz>dN^xM4@
zTD!ZWWhB&>=$ToDz#C@0)&l8|TJ|Y9Tw8_=8qO;HgN=6EdR&ceESUL8r#8eMjmRp0
zl2aqUlDW!L*6|L)#@KJ3Co^1pmqWX3%?->b6BI~{f(}P;{Vt=(RVVb^&C5jE!HH;Z
zBj4pB&UhHMHvQ6o!j-lhjLj0iIM?CMg~RXE?nBBRjhxzpsUPBKFbT(MQIk1h#N-tG
zO^SS0P&*I%`!IgCzWA3P`WnZ%L<W$lp|Tv+asf}eyE@*Iq7ux>R!fFXs_$=kRACAk
zBE)cP#zf2;CebqkYZ}i*@SQ|G7Ek11E?q%~Q^6qR{iCQ?&GL-t=U1AlNWNjmTwK#p
zsUl85`{R|W@M2grln1Sy9&G!VkrxXya6E4a#^mLq4PPrT=^vZ$<N2Z%8e6XXt&n@v
zY^p}|gA?1h4DLTGpDAeM7V_=&+?&ZVwSr!0fB#{(Bp5VLgeVYY7AMs;Q_TE?;ym8s
zi6`LqH$z)D3j+;K%knf5BT{mm83eeqdZFveaR@x!my7<hKLTr#C=>>HR7TY@Hk?uq
z^9YR;sZuxoxiZz6w6-xIquBVSSz4SPmB_5n1cmHjHRaNR%@tChGs{+`9?+ddY$$G-
zPk#0olM|BhB`<D1qg}4Ra^)c2D`_tX7Cgn8WSTvBJu$T(Nf{2qu5-DrFr%fD4=qli
z?WQU4os`ddW=oLMMPgZuH+v9)wB7jw*1>vz$1SB>VmOaA?#~Yl9Xg;eb0uuiGs6yS
zUJ8x@0ir8@y?KTI&5+?8;-6EyK95?wyHXbxmY@jgHhu7iWSEN{tb!H%XH@D-WB*q6
zQjlA5;#8S9Dt3;`Cfq_oxH+>@##m%+cI%0rsLo$B24cvI<D@RW4+eR}5>WgPd@$Z4
z9Y9O<j92!GT~k55)}-SKCk+l^^<U4E2zsydr#Ip7>Zl@YATFf88MHLp?yT`~e_Hyd
za?Hp!#t>sp4yKzqOO33W88HFuKF8nrO9L!m??M%3ymBnbuHlB7IDx#AGAR9l<`sWm
zTT;hM%OZR7XN+XkCx|5|pYI2aBQ)+tHJeA3R5JU4OCmz3271%0Q#MCUQ||D@*G{~d
za77Qq7tcy$R$by>uSqTEe`BT-57Nvds7cUzo?rySq6jiT46BJ1ckpje|0#;-f9Xz_
z|Bvpp-q-KMU^~F=v-X<w6{e8Y-oSX_ngHoJpYpS(MnN@9m_Jls7HYNVouG)KAcgPj
zNWmW6KN=_6_+A5Xn9py3NLoWy!l(Bw2Xvnidj+7m^^^r%dXiYtui|o!ShIJxddN#K
z81F<APb&1dTU`zAXCtGvqu`_jrA4bH>rXlcDM;9qamFcEOM5cjM{F|He)KK;b_5NS
z42rIJd>OmcGqjiecz;cJAHp1x*kT$KU8izweXs4|C-%msdg8@G8K@G(k2%5bI|<Af
z$1>otn@Urx(PO9_90!o@S+$jH)f?L>>VKV(`4z|OEH>RTMUumn4dD?HB?>%x2yw+y
zGHk4D>7!@Z9Ele4L}^l@i@fwFdqf4>ptfW%s6<kN`%y(^J{lXJfv-wSv}AJHwc1@F
zOGdMctxgQNZsP?bVc9$e^WZodpGPVp5CebgAu749Pn`2i*bd9GGhO={{Ra!6c)=Bs
z`-XUQInY@!oLxU_Dc;9mL(A<62SNJ$c0j72OlrX#9~Htz*q&{Oq2RuYZhId)a#GB>
z14qQjyWprpbvxxa972(5p0W)m+tX``;_BQKue=e6Nazx=C|;v8EM*THWdMO@zv`+O
zZDv^R#)=41o0eb?(;)4TrFz8UV~}WJnLfa>%C)qCCwbe@Q0t{YJx9>xubQCq+?BXL
z0D35yi^>dB8c|r6=@6T@v-lYH8CfROQ+5UI?nBWuJn+e=-yQ3U`}Pb?JpkXGLIFeT
zP}2h}<dI)l7!TMBDBWVQ*hVt$*j$t*jyW?n(C{c-Ue6baOkq*}Sqbl9ka$`thxdDY
z#F&L4p5ezjo7>LOt~I4^T7T|t`$bEa+HIweAcT^8N6hA@V~DUt?$mqu3u4DiU;iDa
zMx&c^xe2F#kx$Qhg1*Khba=k1-h5Q7{o2kR=Y(Ax#z8Zq7{(h7D=RTl)Hbh&CuysW
z<=hag6Y?v%cP99c1z7P0%}gl#wZ5h2M14Y;9;a-P?|G#=*;Pk6N8K!4u|$Je9$<3t
zqXmW4fz4lKdd6LUaDCHtY0edyp|K$4<?$gtr{X0J-Zd865+_hKvFA<hQQlqNXb~5$
zOdkHUBSgEt<3*@|HHx6Mso4=ck$2p(Uf;n?F?35P@EooaL;I3;2r~W@_^=y{qu5VL
zA{oF_O?JTETj=O@zXCaEEsId&eF%pD2BdgAgv(Ef<(jX?whc;X<&LOhw!iI<NzB7v
z$!o#Ma~eVXv%5s<+HdO;+&PTdtqvM#4R*Pl4mw6XJ%d>mOh-Fj@jVMA2kMD<Zhm_V
zI%qpW7T~3`;82-Tz&Zz=uvCz*#GRac>{kra&DwHDqT^vYr9x#f^)6j*TzrIvKlmqg
zgKaauCNd6*Ww1s1|GHX~9$?r=rv6>RcQ{(|6JShQg6$!ReB#rBCf#CRn`re#J<+2e
zBKMk8jF~#nt;u3ze;G<Y5HDJTluGYY-M_-wP`V%xQ^B?fECPw5*Ie<3?lTK<Wnma+
zHUd%W!b<V9vf7u%3<)Uo`4hQ_RZ<EBqJHJ5d){6?Ww1Mc|EhzYw?^dN)r+J#k{@zq
zXNz+rDh<81>-^?*sWvUrTZKXD!Kgc)pqrNiaJ-9kzVdbL*6Q{*hKIX+qWYVZH>?^l
zUBb5?U^IfK!ZkjbSi77h!<@>tO}hjPFKTJF5;P8FwQ({K&c_CA85LrN=9CSs9YJ|L
z1LMo3t?=flH4elH3pC$FCz}5KTHI@8MWmRP{Q@&GIb-j&>e;tNIT4T$t2Su@)n8!>
zw;x`UvFKk_U@Wd2FIM|&<Hc38md2oJ@?1?BIdXQXpU?<CuMF%Kz51=l`9{nl!jELn
zp#k%Wdo{;S!bvPJVt8P=FnU)}3IhtUuZZOnErI7Cm0UGx6PexVVfaHKc%Z;w$Zk*K
zAfXL#Jzd*)vKc7-5b{ez*3%@EKevHJ_gO*~7TRlgPOE73t%PH=rN}Ig{+Hw@ICbv3
zOei`&Rr!$IdkB5;<Rbl@y}C+P-#UG68XFjyFNmqY(tQng8H6HWUTuVcI63ZAK|uQ+
zBkF7^bvO-GAP)F1*clK1zXc(+?|OUrAcEB0cUTn|Db}el-hb&!CT)fr(O*}Ap5tUQ
zo(MpUKp*}S)-UzY`|aD1p$WWmKIZCzFQOV9!0Apw*GNXT@?V;h9I6OM|FBYdf<<ea
zE9MzW)cCVN)l~UByr~-mVb*I9s`rvlUYnp&nrlb$MM15v+1c}e3N0_IwOeNCQZ>Jv
zqU8ub2bpg=<OzRPer)cPo`a~~jsc~Tvfl8;qAhx3%)8W{&F>t|<le*qr6xLY8L9c;
z+vk~n0eX6?qeT?O4`7cx0TAVN^g<V^_R``j%@$lQcYeg0#9P7OCLpefd62{={nOlw
i@A`5=nNxmrSGSzghid=daL1ICq~Nv{%QO=q)c*iL+>5>d
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_noAudioNotification.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for video controller in windows</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<pre id="test">
+</pre>
+
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var observer = {
+  observe: function(subject, topic, data) {
+    ok(false, "should not receive audio-playback notification!");
+  }
+};
+
+var observerService = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
+                                   .getService(SpecialPowers.Ci.nsIObserverService);
+
+var video = document.createElement("video");
+video.loop = true;
+video.src = "noaudio.webm";
+
+video.onplay = video.onpause  = function() {
+  // Yield to the event loop a few times to make sure that audio-playback is not dispatched.
+  SimpleTest.executeSoon(function() {
+    SimpleTest.executeSoon(function() {
+      SimpleTest.executeSoon(function() {
+        runTest();
+      });
+    });
+  });
+};
+
+var tests = [
+  function() {
+    SpecialPowers.pushPrefEnv({"set": [["media.useAudioChannelService", true]]}, runTest);
+  },
+
+  function() {
+    observerService.addObserver(observer, "audio-playback", false);
+    ok(true, "Observer set");
+    runTest();
+  },
+
+  function() {
+    video.play();
+  },
+
+  function() {
+    video.pause();
+  },
+
+  function() {
+    observerService.removeObserver(observer, "audio-playback");
+    ok(true, "Observer removed");
+    runTest();
+  }
+];
+
+function runTest() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+runTest();
+
+</script>
+</body>
+</html>
+
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -8,25 +8,28 @@
  * A header for declaring various things that binding implementation headers
  * might need.  The idea is to make binding implementation headers safe to
  * include anywhere without running into include hell like we do with
  * BindingUtils.h
  */
 #ifndef mozilla_dom_BindingDeclarations_h__
 #define mozilla_dom_BindingDeclarations_h__
 
-#include "nsStringGlue.h"
+#include "js/RootingAPI.h"
 #include "js/Value.h"
-#include "js/RootingAPI.h"
+
 #include "mozilla/Maybe.h"
+#include "mozilla/OwningNonNull.h"
+
+#include "mozilla/dom/DOMString.h"
+
+#include "nsAutoPtr.h" // for nsRefPtr member variables
 #include "nsCOMPtr.h"
+#include "nsStringGlue.h"
 #include "nsTArray.h"
-#include "nsAutoPtr.h" // for nsRefPtr member variables
-#include "mozilla/dom/DOMString.h"
-#include "mozilla/OwningNonNull.h"
 
 class nsWrapperCache;
 
 namespace mozilla {
 namespace dom {
 
 // Struct that serves as a base class for all dictionaries.  Particularly useful
 // so we can use IsBaseOf to detect dictionary template arguments.
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -71,8 +71,9 @@ MSG_DEF(MSG_INVALID_RESPONSE_STATUSCODE_
 MSG_DEF(MSG_INVALID_REDIRECT_STATUSCODE_ERROR, 0, JSEXN_RANGEERR, "Invalid redirect status code.")
 MSG_DEF(MSG_INVALID_URL_SCHEME, 2, JSEXN_TYPEERR, "{0} URL {1} must be either http:// or https://.")
 MSG_DEF(MSG_RESPONSE_URL_IS_NULL, 0, JSEXN_TYPEERR, "Cannot set Response.finalURL when Response.url is null.")
 MSG_DEF(MSG_RESPONSE_HAS_VARY_STAR, 0, JSEXN_TYPEERR, "Invalid Response object with a 'Vary: *' header.")
 MSG_DEF(MSG_BAD_FORMDATA, 0, JSEXN_TYPEERR, "Could not parse content as FormData.")
 MSG_DEF(MSG_NO_ACTIVE_WORKER, 1, JSEXN_TYPEERR, "No active worker for scope {0}.")
 MSG_DEF(MSG_NOTIFICATION_PERMISSION_DENIED, 0, JSEXN_TYPEERR, "Permission to show Notification denied.")
 MSG_DEF(MSG_NOTIFICATION_NO_CONSTRUCTOR_IN_SERVICEWORKER, 0, JSEXN_TYPEERR, "Notification constructor cannot be used in ServiceWorkerGlobalScope. Use registration.showNotification() instead.")
+MSG_DEF(MSG_INVALID_SCOPE, 2, JSEXN_TYPEERR, "Invalid scope trying to resolve {0} with base URL {1}.")
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -649,20 +649,16 @@ NS_IMETHODIMP
 BroadcastChannel::Observe(nsISupports* aSubject, const char* aTopic,
                           const char16_t* aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!strcmp(aTopic, "inner-window-destroyed"));
 
   // If the window is destroyed we have to release the reference that we are
   // keeping.
-  if (!mIsKeptAlive) {
-    return NS_OK;
-  }
-
   nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
   NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
 
   uint64_t innerID;
   nsresult rv = wrapper->GetData(&innerID);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (innerID == mInnerID) {
--- a/dom/broadcastchannel/tests/test_broadcastchannel_basic.html
+++ b/dom/broadcastchannel/tests/test_broadcastchannel_basic.html
@@ -47,16 +47,21 @@ function runTest() {
   var ifr = document.createElement("iframe");
   ifr.addEventListener("load", iframeLoaded, false);
   ifr.setAttribute('src', "iframe_broadcastchannel.html");
   div.appendChild(ifr);
 
   function iframeLoaded() {
     bc.postMessage("Hello world from the window!");
   }
+
+  // A leak test
+  var dummyBc = new BroadcastChannel("dont_leak_this");
+  dummyBc.foo = "bar";
+  // don't add message listener!
 }
 
 SimpleTest.waitForExplicitFinish();
 runTest();
 
 </script>
 </body>
 </html>
--- a/dom/browser-element/BrowserElementCopyPaste.js
+++ b/dom/browser-element/BrowserElementCopyPaste.js
@@ -66,17 +66,18 @@ let CopyPasteAssistent = {
         canCut: this._isCommandEnabled("cut"),
         canCopy: this._isCommandEnabled("copy"),
         canPaste: this._isCommandEnabled("paste"),
       },
       zoomFactor: zoomFactor,
       reason: e.reason,
       collapsed: e.collapsed,
       caretVisible: e.caretVisible,
-      selectionVisible: e.selectionVisible
+      selectionVisible: e.selectionVisible,
+      selectionEditable: e.selectionEditable
     };
 
     // Get correct geometry information if we have nested iframe.
     let currentWindow = e.target.defaultView;
     while (currentWindow.realFrameElement) {
       let currentRect = currentWindow.realFrameElement.getBoundingClientRect();
       detail.rect.top += currentRect.top;
       detail.rect.bottom += currentRect.top;
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -447,16 +447,17 @@ BrowserElementParent.prototype = {
   //              command is available, using following code, if (data.commands.canCut) {}.
   //  - zoomFactor: Current zoom factor in child frame.
   //  - reason: The reason causes the state changed. Include "visibilitychange",
   //            "updateposition", "longpressonemptycontent", "taponcaret", "presscaret",
   //            "releasecaret".
   //  - collapsed: Indicate current selection is collapsed or not.
   //  - caretVisible: Indicate the caret visiibility.
   //  - selectionVisible: Indicate current selection is visible or not.
+  //  - selectionEditable: Indicate current selection is editable or not.
   _handleCaretStateChanged: function(data) {
     let evt = this._createEvent('caretstatechanged', data.json,
                                 /* cancelable = */ false);
 
     let self = this;
     function sendDoCommandMsg(cmd) {
       let data = { command: cmd };
       self._sendAsyncMsg('copypaste-do-command', data);
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -1096,17 +1096,17 @@ nsDOMCameraControl::Shutdown()
   }
 }
 
 void
 nsDOMCameraControl::ReleaseAudioChannelAgent()
 {
 #ifdef MOZ_B2G
   if (mAudioChannelAgent) {
-    mAudioChannelAgent->NotifyStoppedPlaying();
+    mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_DONT_NOTIFY);
     mAudioChannelAgent = nullptr;
   }
 #endif
 }
 
 nsresult
 nsDOMCameraControl::NotifyRecordingStatusChange(const nsString& aMsg)
 {
@@ -1132,17 +1132,18 @@ nsDOMCameraControl::NotifyRecordingStatu
       return NS_ERROR_UNEXPECTED;
     }
 
     // Camera app will stop recording when it falls to the background, so no callback is necessary.
     mAudioChannelAgent->Init(mWindow, (int32_t)AudioChannel::Content, nullptr);
     // Video recording doesn't output any sound, so it's not necessary to check canPlay.
     float volume = 0.0;
     bool muted = true;
-    rv = mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+    rv = mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_DONT_NOTIFY,
+                                                  &volume, &muted);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 #endif
   return rv;
 }
 
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -5441,22 +5441,17 @@ CanvasRenderingContext2D::GetBufferProvi
   if (mBufferProvider) {
     return mBufferProvider;
   }
 
   if (!mTarget) {
     return nullptr;
   }
 
-  mBufferProvider = aManager->CreatePersistentBufferProvider(mTarget->GetSize(), mTarget->GetFormat());
-
-  RefPtr<SourceSurface> surf = mTarget->Snapshot();
-
-  mTarget = mBufferProvider->GetDT(IntRect(IntPoint(), mTarget->GetSize()));
-  mTarget->CopySurface(surf, IntRect(IntPoint(), mTarget->GetSize()), IntPoint());
+  mBufferProvider = new PersistentBufferProviderBasic(mTarget);
 
   return mBufferProvider;
 }
 
 already_AddRefed<CanvasLayer>
 CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
                                          CanvasLayer *aOldLayer,
                                          LayerManager *aManager)
--- a/dom/canvas/WebGL1Context.cpp
+++ b/dom/canvas/WebGL1Context.cpp
@@ -15,23 +15,28 @@ namespace mozilla {
 WebGL1Context::Create()
 {
     return new WebGL1Context();
 }
 
 WebGL1Context::WebGL1Context()
     : WebGLContext()
 {
-    mFormatUsage = Move(webgl::FormatUsageAuthority::CreateForWebGL1());
 }
 
 WebGL1Context::~WebGL1Context()
 {
 }
 
+UniquePtr<webgl::FormatUsageAuthority>
+WebGL1Context::CreateFormatUsage() const
+{
+    return webgl::FormatUsageAuthority::CreateForWebGL1();
+}
+
 JSObject*
 WebGL1Context::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLRenderingContextBinding::Wrap(cx, this, givenProto);
 }
 
 bool
 WebGL1Context::ValidateQueryTarget(GLenum target, const char* info)
--- a/dom/canvas/WebGL1Context.h
+++ b/dom/canvas/WebGL1Context.h
@@ -13,16 +13,17 @@ namespace mozilla {
 class WebGL1Context
     : public WebGLContext
 {
 public:
     static WebGL1Context* Create();
 
 private:
     WebGL1Context();
+    virtual UniquePtr<webgl::FormatUsageAuthority> CreateFormatUsage() const override;
 
 public:
     virtual ~WebGL1Context();
 
     virtual bool IsWebGL2() const override {
         return false;
     }
 
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -16,25 +16,29 @@
 
 namespace mozilla {
 
 WebGL2Context::WebGL2Context()
     : WebGLContext()
 {
     MOZ_ASSERT(IsSupported(), "not supposed to create a WebGL2Context"
                               "context when not supported");
-
-    mFormatUsage = Move(webgl::FormatUsageAuthority::CreateForWebGL2());
 }
 
 WebGL2Context::~WebGL2Context()
 {
 
 }
 
+UniquePtr<webgl::FormatUsageAuthority>
+WebGL2Context::CreateFormatUsage() const
+{
+    return webgl::FormatUsageAuthority::CreateForWebGL2();
+}
+
 /*static*/ bool
 WebGL2Context::IsSupported()
 {
     return Preferences::GetBool("webgl.enable-prototype-webgl2", false);
 }
 
 /*static*/ WebGL2Context*
 WebGL2Context::Create()
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -351,16 +351,17 @@ public:
     already_AddRefed<WebGLVertexArrayObject> CreateVertexArray();
     void DeleteVertexArray(WebGLVertexArrayObject* vertexArray);
     bool IsVertexArray(WebGLVertexArrayObject* vertexArray);
     void BindVertexArray(WebGLVertexArrayObject* vertexArray);
 */
 
 private:
     WebGL2Context();
+    virtual UniquePtr<webgl::FormatUsageAuthority> CreateFormatUsage() const override;
 
     virtual bool IsTexParamValid(GLenum pname) const override;
 
     void UpdateBoundQuery(GLenum target, WebGLQuery* query);
 
     // CreateVertexArrayImpl is assumed to be infallible.
     virtual WebGLVertexArray* CreateVertexArrayImpl() override;
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) override;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1482,17 +1482,19 @@ protected:
 
     nsRefPtr<WebGLObserver> mContextObserver;
 
 public:
     // console logging helpers
     void GenerateWarning(const char* fmt, ...);
     void GenerateWarning(const char* fmt, va_list ap);
 
+public:
     UniquePtr<webgl::FormatUsageAuthority> mFormatUsage;
+    virtual UniquePtr<webgl::FormatUsageAuthority> CreateFormatUsage() const = 0;
 
     // Friend list
     friend class WebGLTexture;
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
     friend class WebGLQuery;
     friend class WebGLBuffer;
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -1648,16 +1648,21 @@ FloorPOT(int32_t x)
 }
 
 bool
 WebGLContext::InitAndValidateGL()
 {
     if (!gl)
         return false;
 
+    // Unconditionally create a new format usage authority. This is
+    // important when restoring contexts and extensions need to add
+    // formats back into the authority.
+    mFormatUsage = CreateFormatUsage();
+
     GLenum error = gl->fGetError();
     if (error != LOCAL_GL_NO_ERROR) {
         GenerateWarning("GL error 0x%x occurred during OpenGL context"
                         " initialization, before WebGL initialization!", error);
         return false;
     }
 
     mMinCapability = Preferences::GetBool("webgl.min_capability_mode", false);
--- a/dom/fmradio/FMRadio.cpp
+++ b/dom/fmradio/FMRadio.cpp
@@ -190,17 +190,17 @@ FMRadio::Notify(const FMRadioEventType& 
     case FrequencyChanged:
       DispatchTrustedEvent(NS_LITERAL_STRING("frequencychange"));
       break;
     case EnabledChanged:
       if (Enabled()) {
         DispatchTrustedEvent(NS_LITERAL_STRING("enabled"));
       } else {
         if (mAudioChannelAgentEnabled) {
-          mAudioChannelAgent->NotifyStoppedPlaying();
+          mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
           mAudioChannelAgentEnabled = false;
         }
 
         DispatchTrustedEvent(NS_LITERAL_STRING("disabled"));
       }
       break;
     case RDSEnabledChanged:
       if (RdsEnabled()) {
@@ -452,17 +452,18 @@ FMRadio::DisableRDS()
 
 void
 FMRadio::EnableAudioChannelAgent()
 {
   NS_ENSURE_TRUE_VOID(mAudioChannelAgent);
 
   float volume = 0.0;
   bool muted = true;
-  mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+  mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY,
+                                           &volume, &muted);
   WindowVolumeChanged(volume, muted);
 
   mAudioChannelAgentEnabled = true;
 }
 
 NS_IMETHODIMP
 FMRadio::WindowVolumeChanged(float aVolume, bool aMuted)
 {
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4517,23 +4517,27 @@ HTMLMediaElement::NotifyAudioChannelAgen
   // media playback route.
   WindowAudioCaptureChanged();
 
   // This is needed to pass nsContentUtils::IsCallerChrome().
   // AudioChannel API should not called from content but it can happen that
   // this method has some content JS in its stack.
   AutoNoJSAPI nojsapi;
 
+  // Don't notify playback if this element doesn't have any audio tracks.
+  uint32_t notify = HasAudio() ? nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY :
+                                 nsIAudioChannelAgent::AUDIO_AGENT_DONT_NOTIFY;
+
   if (aPlaying) {
     float volume = 0.0;
     bool muted = true;
-    mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+    mAudioChannelAgent->NotifyStartedPlaying(notify, &volume, &muted);
     WindowVolumeChanged(volume, muted);
   } else {
-    mAudioChannelAgent->NotifyStoppedPlaying();
+    mAudioChannelAgent->NotifyStoppedPlaying(notify);
     mAudioChannelAgent = nullptr;
   }
 }
 
 NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted)
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -10,16 +10,17 @@ include protocol PColorPicker;
 include protocol PContent;
 include protocol PContentBridge;
 include protocol PDocAccessible;
 include protocol PDocumentRenderer;
 include protocol PFilePicker;
 include protocol PIndexedDBPermissionRequest;
 include protocol PRenderFrame;
 include protocol PPluginWidget;
+include protocol PWebBrowserPersistDocument;
 include DOMTypes;
 include JavaScriptTypes;
 include URIParams;
 include BrowserConfiguration;
 
 
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
@@ -103,27 +104,30 @@ prio(normal upto urgent) sync protocol P
 
     manages PColorPicker;
     manages PDocAccessible;
     manages PDocumentRenderer;
     manages PFilePicker;
     manages PIndexedDBPermissionRequest;
     manages PRenderFrame;
     manages PPluginWidget;
+    manages PWebBrowserPersistDocument;
 
 both:
     AsyncMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows,
                  Principal aPrincipal);
 
     /**
      * Create a layout frame (encapsulating a remote layer tree) for
      * the page that is currently loaded in the <browser>.
      */
     PRenderFrame();
 
+    PWebBrowserPersistDocument();
+
 parent:
     /**
      * Tell the parent process a new accessible document has been created.
      * aParentDoc is the accessible document it was created in if any, and
      * aParentAcc is the id of the accessible in that document the new document
      * is a child of.
      */
     PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -29,16 +29,17 @@
 #include "mozilla/layers/APZEventState.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/DoubleTapToZoom.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layout/RenderFrameChild.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/MouseEvents.h"
+#include "mozilla/PWebBrowserPersistDocumentChild.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/unused.h"
 #include "mozIApplication.h"
 #include "nsContentUtils.h"
 #include "nsDocShell.h"
@@ -92,16 +93,17 @@
 #include "nsColorPickerProxy.h"
 #include "nsPresShell.h"
 #include "nsIAppsService.h"
 #include "nsNetUtil.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptError.h"
 #include "mozilla/EventForwards.h"
 #include "nsDeviceContext.h"
+#include "mozilla/WebBrowserPersistDocumentChild.h"
 
 #define BROWSER_ELEMENT_CHILD_SCRIPT \
     NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
 
 #define TABC_LOG(...)
 // #define TABC_LOG(...) printf_stderr("TABC: " __VA_ARGS__)
 
 using namespace mozilla;
@@ -3103,8 +3105,28 @@ JSObject*
 TabChildGlobal::GetGlobalJSObject()
 {
   NS_ENSURE_TRUE(mTabChild, nullptr);
   nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mTabChild->GetGlobal();
   NS_ENSURE_TRUE(ref, nullptr);
   return ref->GetJSObject();
 }
 
+PWebBrowserPersistDocumentChild*
+TabChild::AllocPWebBrowserPersistDocumentChild()
+{
+  return new WebBrowserPersistDocumentChild();
+}
+
+bool
+TabChild::RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor)
+{
+  nsCOMPtr<nsIDocument> doc = GetDocument();
+  static_cast<WebBrowserPersistDocumentChild*>(aActor)->Start(doc);
+  return true;
+}
+
+bool
+TabChild::DeallocPWebBrowserPersistDocumentChild(PWebBrowserPersistDocumentChild* aActor)
+{
+  delete aActor;
+  return true;
+}
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -490,16 +490,20 @@ public:
     bool ParentIsActive()
     {
       return mParentIsActive;
     }
     bool AsyncPanZoomEnabled() { return mAsyncPanZoomEnabled; }
 
     virtual ScreenIntSize GetInnerSize() override;
 
+    virtual PWebBrowserPersistDocumentChild* AllocPWebBrowserPersistDocumentChild() override;
+    virtual bool RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor) override;
+    virtual bool DeallocPWebBrowserPersistDocumentChild(PWebBrowserPersistDocumentChild* aActor) override;
+
 protected:
     virtual ~TabChild();
 
     virtual PRenderFrameChild* AllocPRenderFrameChild() override;
     virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
     virtual bool RecvDestroy() override;
     virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) override;
     virtual bool RecvSetIsDocShellActive(const bool& aIsActive) override;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -90,16 +90,17 @@
 #include "gfxPrefs.h"
 #include "nsILoginManagerPrompter.h"
 #include "nsPIWindowRoot.h"
 #include "nsIAuthPrompt2.h"
 #include "gfxDrawable.h"
 #include "ImageOps.h"
 #include "UnitTransforms.h"
 #include <algorithm>
+#include "mozilla/WebBrowserPersistDocumentParent.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::services;
 using namespace mozilla::widget;
 using namespace mozilla::jsipc;
@@ -251,17 +252,18 @@ namespace mozilla {
 namespace dom {
 
 TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr;
 
 NS_IMPL_ISUPPORTS(TabParent,
                   nsITabParent,
                   nsIAuthPromptProvider,
                   nsISecureBrowserUI,
-                  nsISupportsWeakReference)
+                  nsISupportsWeakReference,
+                  nsIWebBrowserPersistable)
 
 TabParent::TabParent(nsIContentParent* aManager,
                      const TabId& aTabId,
                      const TabContext& aContext,
                      uint32_t aChromeFlags)
   : TabContext(aContext)
   , mFrameElement(nullptr)
   , mRect(0, 0, 0, 0)
@@ -3340,16 +3342,39 @@ TabParent::TakeDragVisualization(RefPtr<
 
 bool
 TabParent::AsyncPanZoomEnabled() const
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   return widget && widget->AsyncPanZoomEnabled();
 }
 
+PWebBrowserPersistDocumentParent*
+TabParent::AllocPWebBrowserPersistDocumentParent()
+{
+  return new WebBrowserPersistDocumentParent();
+}
+
+bool
+TabParent::DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+NS_IMETHODIMP
+TabParent::StartPersistence(nsIWebBrowserPersistDocumentReceiver* aRecv)
+{
+  auto* actor = new WebBrowserPersistDocumentParent();
+  actor->SetOnReady(aRecv);
+  return SendPWebBrowserPersistDocumentConstructor(actor)
+    ? NS_OK : NS_ERROR_FAILURE;
+  // (The actor will be destroyed on constructor failure.)
+}
+
 NS_IMETHODIMP
 FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo)
 {
   nsAuthInformationHolder* holder =
     static_cast<nsAuthInformationHolder*>(aAuthInfo);
 
   if (!net::gNeckoChild->SendOnAuthAvailable(mCallbackId,
                                              holder->User(),
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -17,16 +17,17 @@
 #include "mozilla/dom/File.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDOMEventListener.h"
 #include "nsISecureBrowserUI.h"
 #include "nsITabParent.h"
+#include "nsIWebBrowserPersistable.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsRefreshDriver.h"
 #include "nsWeakReference.h"
 #include "Units.h"
 #include "nsIWidget.h"
 
 class nsFrameLoader;
 class nsIFrameLoader;
@@ -71,16 +72,17 @@ struct StructuredCloneData;
 class TabParent final : public PBrowserParent
                       , public nsIDOMEventListener
                       , public nsITabParent
                       , public nsIAuthPromptProvider
                       , public nsISecureBrowserUI
                       , public nsSupportsWeakReference
                       , public TabContext
                       , public nsAPostRefreshObserver
+                      , public nsIWebBrowserPersistable
 {
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
     typedef mozilla::OwningSerializedStructuredCloneBuffer OwningSerializedStructuredCloneBuffer;
 
     virtual ~TabParent();
 
 public:
     // nsITabParent
@@ -363,16 +365,17 @@ public:
                                       PIndexedDBPermissionRequestParent* aActor)
                                       override;
 
     bool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIAUTHPROMPTPROVIDER
     NS_DECL_NSISECUREBROWSERUI
+    NS_DECL_NSIWEBBROWSERPERSISTABLE
 
     bool HandleQueryContentEvent(mozilla::WidgetQueryContentEvent& aEvent);
     bool SendCompositionEvent(mozilla::WidgetCompositionEvent& event);
     bool SendSelectionEvent(mozilla::WidgetSelectionEvent& event);
 
     static TabParent* GetFrom(nsFrameLoader* aFrameLoader);
     static TabParent* GetFrom(nsIFrameLoader* aFrameLoader);
     static TabParent* GetFrom(nsITabParent* aTabParent);
@@ -425,16 +428,20 @@ public:
                           const uint32_t& aStride, const uint8_t& aFormat,
                           const int32_t& aDragAreaX, const int32_t& aDragAreaY) override;
 
     void AddInitialDnDDataTo(DataTransfer* aDataTransfer);
 
     void TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
                                int32_t& aDragAreaX, int32_t& aDragAreaY);
     layout::RenderFrameParent* GetRenderFrame();
+
+    virtual PWebBrowserPersistDocumentParent* AllocPWebBrowserPersistDocumentParent() override;
+    virtual bool DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor) override;
+
 protected:
     bool ReceiveMessage(const nsString& aMessage,
                         bool aSync,
                         const StructuredCloneData* aCloneData,
                         mozilla::jsipc::CpowHolder* aCpows,
                         nsIPrincipal* aPrincipal,
                         nsTArray<OwningSerializedStructuredCloneBuffer>* aJSONRetVal = nullptr);
 
--- a/dom/locales/en-US/chrome/security/security.properties
+++ b/dom/locales/en-US/chrome/security/security.properties
@@ -11,20 +11,42 @@ CORSMissingAllowOrigin=Cross-Origin Requ
 CORSAllowOriginNotMatchingOrigin=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header 'Access-Control-Allow-Origin' does not match '%2$S').
 CORSMethodNotFound=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: Did not find method in CORS header 'Access-Control-Allow-Methods').
 CORSMissingAllowCredentials=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: expected 'true' in CORS header 'Access-Control-Allow-Credentials').
 CORSPreflightDidNotSucceed=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS preflight channel did not succeed).
 CORSInvalidAllowMethod=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: invalid token '%2$S' in CORS header 'Access-Control-Allow-Methods').
 CORSInvalidAllowHeader=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: invalid token '%2$S' in CORS header 'Access-Control-Allow-Headers').
 CORSMissingAllowHeaderFromPreflight=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: missing token '%2$S' in CORS header 'Access-Control-Allow-Headers' from CORS preflight channel).
 
-# LOCALIZATION NOTE: Do not translate "Strict-Transport-Security" or "HSTS"
-InvalidSTSHeaders=The site specified an invalid Strict-Transport-Security header.
-# LOCALIZATION NOTE: Do not translate "Public-Key-Pins or HPKP"
-InvalidPKPHeaders=The site specified an invalid Public-Key-Pins header.
+# LOCALIZATION NOTE: Do not translate "Strict-Transport-Security", "HSTS", "max-age" or "includeSubDomains"
+STSUnknownError=Strict-Transport-Security: An unknown error occurred processing the header specified by the site.
+STSUntrustworthyConnection=Strict-Transport-Security: The connection to the site is untrustworthy, so the specified header was ignored.
+STSCouldNotParseHeader=Strict-Transport-Security: The site specified a header that could not be parsed successfully.
+STSNoMaxAge=Strict-Transport-Security: The site specified a header that did not include a 'max-age' directive.
+STSMultipleMaxAges=Strict-Transport-Security: The site specified a header that included multiple 'max-age' directives.
+STSInvalidMaxAge=Strict-Transport-Security: The site specified a header that included an invalid 'max-age' directive.
+STSMultipleIncludeSubdomains=Strict-Transport-Security: The site specified a header that included multiple 'includeSubDomains' directives.
+STSInvalidIncludeSubdomains=Strict-Transport-Security: The site specified a header that included an invalid 'includeSubDomains' directive.
+STSCouldNotSaveState=Strict-Transport-Security: An error occurred noting the site as a Strict-Transport-Security host.
+
+# LOCALIZATION NOTE: Do not translate "Public-Key-Pins", "HPKP", "max-age" or "includeSubDomains"
+PKPUnknownError=Public-Key-Pins: An unknown error occurred processing the header specified by the site.
+PKPUntrustworthyConnection=Public-Key-Pins: The connection to the site is untrustworthy, so the specified header was ignored.
+PKPCouldNotParseHeader=Public-Key-Pins: The site specified a header that could not be parsed successfully.
+PKPNoMaxAge=Public-Key-Pins: The site specified a header that did not include a 'max-age' directive.
+PKPMultipleMaxAges=Public-Key-Pins: The site specified a header that included multiple 'max-age' directives.
+PKPInvalidMaxAge=Public-Key-Pins: The site specified a header that included an invalid 'max-age' directive.
+PKPMultipleIncludeSubdomains=Public-Key-Pins: The site specified a header that included multiple 'includeSubDomains' directives.
+PKPInvalidIncludeSubdomains=Public-Key-Pins: The site specified a header that included an invalid 'includeSubDomains' directive.
+PKPInvalidPin=Public-Key-Pins: The site specified a header that included an invalid pin.
+PKPMultipleReportURIs=Public-Key-Pins: The site specified a header that included multiple 'report-uri' directives.
+PKPPinsetDoesNotMatch=Public-Key-Pins: The site specified a header that did not include a matching pin.
+PKPNoBackupPin=Public-Key-Pins: The site specified a header that did not include a backup pin.
+PKPCouldNotSaveState=Public-Key-Pins: An error occurred noting the site as a Public-Key-Pins host.
+
 # LOCALIZATION NOTE: Do not translate "SHA-1"
 SHA1Sig=This site makes use of a SHA-1 Certificate; it's recommended you use certificates with signature algorithms that use hash functions stronger than SHA-1.
 InsecurePasswordsPresentOnPage=Password fields present on an insecure (http://) page. This is a security risk that allows user login credentials to be stolen.
 InsecureFormActionPasswordsPresent=Password fields present in a form with an insecure (http://) form action. This is a security risk that allows user login credentials to be stolen.
 InsecurePasswordsPresentOnIframe=Password fields present on an insecure (http://) iframe. This is a security risk that allows user login credentials to be stolen.
 # LOCALIZATION NOTE: "%1$S" is the URI of the insecure mixed content resource
 LoadingMixedActiveContent2=Loading mixed (insecure) active content "%1$S" on a secure page
 LoadingMixedDisplayContent2=Loading mixed (insecure) display content "%1$S" on a secure page
--- a/dom/media/AudioSink.cpp
+++ b/dom/media/AudioSink.cpp
@@ -53,16 +53,43 @@ AudioSink::DispatchTask(already_AddRefed
 {
   DebugOnly<nsresult> rv = mThread->Dispatch(Move(event), NS_DISPATCH_NORMAL);
   // There isn't much we can do if Dispatch() fails.
   // Just assert it to keep things simple.
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 }
 
 void
+AudioSink::OnAudioQueueEvent()
+{
+  AssertOnAudioThread();
+  if (!mAudioLoopScheduled) {
+    AudioLoop();
+  }
+}
+
+void
+AudioSink::ConnectListener()
+{
+  AssertOnAudioThread();
+  mPushListener = AudioQueue().PushEvent().Connect(
+    mThread, this, &AudioSink::OnAudioQueueEvent);
+  mFinishListener = AudioQueue().FinishEvent().Connect(
+    mThread, this, &AudioSink::OnAudioQueueEvent);
+}
+
+void
+AudioSink::DisconnectListener()
+{
+  AssertOnAudioThread();
+  mPushListener.Disconnect();
+  mFinishListener.Disconnect();
+}
+
+void
 AudioSink::ScheduleNextLoop()
 {
   AssertOnAudioThread();
   if (mAudioLoopScheduled) {
     return;
   }
   mAudioLoopScheduled = true;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
@@ -216,22 +243,16 @@ AudioSink::SetPlaying(bool aPlaying)
     // Wake up the audio loop to play next sample.
     if (aPlaying && !self->mAudioLoopScheduled) {
       self->AudioLoop();
     }
   });
   DispatchTask(r.forget());
 }
 
-void
-AudioSink::NotifyData()
-{
-  ScheduleNextLoopCrossThread();
-}
-
 nsresult
 AudioSink::InitializeAudioStream()
 {
   // AudioStream initialization can block for extended periods in unusual
   // circumstances, so we take care to drop the decoder monitor while
   // initializing.
   RefPtr<AudioStream> audioStream(new AudioStream());
   nsresult rv = audioStream->Init(mInfo.mChannels, mInfo.mRate,
@@ -311,38 +332,40 @@ AudioSink::AudioLoop()
       nsresult rv = InitializeAudioStream();
       if (NS_FAILED(rv)) {
         NS_WARNING("Initializing AudioStream failed.");
         mEndPromise.Reject(rv, __func__);
         SetState(AUDIOSINK_STATE_ERROR);
         break;
       }
       SetState(AUDIOSINK_STATE_PLAYING);
+      ConnectListener();
       break;
     }
 
     case AUDIOSINK_STATE_PLAYING: {
       if (WaitingForAudioToPlay()) {
-        // NotifyData() will schedule next loop.
+        // OnAudioQueueEvent() will schedule next loop.
         break;
       }
       if (!IsPlaybackContinuing()) {
         SetState(AUDIOSINK_STATE_COMPLETE);
         break;
       }
       if (!PlayAudio()) {
         SetState(AUDIOSINK_STATE_COMPLETE);
         break;
       }
       // Schedule next loop to play next sample.
       ScheduleNextLoop();
       break;
     }
 
     case AUDIOSINK_STATE_COMPLETE: {
+      DisconnectListener();
       FinishAudioLoop();
       SetState(AUDIOSINK_STATE_SHUTDOWN);
       break;
     }
 
     case AUDIOSINK_STATE_SHUTDOWN:
       break;
 
--- a/dom/media/AudioSink.h
+++ b/dom/media/AudioSink.h
@@ -48,36 +48,36 @@ public:
   // Shut down the AudioSink's resources.
   void Shutdown();
 
   void SetVolume(double aVolume);
   void SetPlaybackRate(double aPlaybackRate);
   void SetPreservesPitch(bool aPreservesPitch);
   void SetPlaying(bool aPlaying);
 
-  // Wake up the audio loop if it is waiting for data to play or the audio
-  // queue is finished.
-  void NotifyData();
-
 private:
   enum State {
     AUDIOSINK_STATE_INIT,
     AUDIOSINK_STATE_PLAYING,
     AUDIOSINK_STATE_COMPLETE,
     AUDIOSINK_STATE_SHUTDOWN,
     AUDIOSINK_STATE_ERROR
   };
 
   ~AudioSink() {}
 
   void DispatchTask(already_AddRefed<nsIRunnable>&& event);
   void SetState(State aState);
   void ScheduleNextLoop();
   void ScheduleNextLoopCrossThread();
 
+  void OnAudioQueueEvent();
+  void ConnectListener();
+  void DisconnectListener();
+
   // The main loop for the audio thread. Sent to the thread as
   // an nsRunnableMethod. This continually does blocking writes to
   // to audio stream to play audio data.
   void AudioLoop();
 
   // Allocate and initialize mAudioStream.  Returns NS_OK on success.
   nsresult InitializeAudioStream();
 
@@ -170,13 +170,16 @@ private:
 
   dom::AudioChannel mChannel;
 
   bool mStopAudioThread;
 
   bool mPlaying;
 
   MozPromiseHolder<GenericPromise> mEndPromise;
+
+  MediaEventListener mPushListener;
+  MediaEventListener mFinishListener;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/MP3Demuxer.cpp
@@ -451,35 +451,35 @@ MP3TrackDemuxer::GetNextFrame(const Medi
   frame->mOffset = aRange.mStart;
 
   nsAutoPtr<MediaRawDataWriter> frameWriter(frame->CreateWriter());
   if (!frameWriter->SetSize(aRange.Length())) {
     MP3DEMUXER_LOG("GetNext() Exit failed to allocated media buffer");
     return nullptr;
   }
 
-  const uint32_t read = Read(frameWriter->mData, frame->mOffset, frame->mSize);
+  const uint32_t read = Read(frameWriter->Data(), frame->mOffset, frame->Size());
 
   if (read != aRange.Length()) {
-    MP3DEMUXER_LOG("GetNext() Exit read=%u frame->mSize=%u", read, frame->mSize);
+    MP3DEMUXER_LOG("GetNext() Exit read=%u frame->Size()=%u", read, frame->Size());
     return nullptr;
   }
 
   UpdateState(aRange);
 
   frame->mTime = Duration(mFrameIndex - 1).ToMicroseconds();
   frame->mDuration = Duration(1).ToMicroseconds();
 
   MOZ_ASSERT(frame->mTime >= 0);
   MOZ_ASSERT(frame->mDuration > 0);
 
   if (mNumParsedFrames == 1) {
     // First frame parsed, let's read VBR info if available.
     // TODO: read info that helps with seeking (bug 1163667).
-    mParser.ParseVBRHeader(frame->mData, frame->mData + frame->mSize);
+    mParser.ParseVBRHeader(frame->Data(), frame->Data() + frame->Size());
     mFirstFrameOffset = frame->mOffset;
   }
 
   MP3DEMUXER_LOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
               " mFrameIndex=%" PRId64
               " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d "
               "mChannels=%d",
               mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, mSamplesPerFrame,
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -480,44 +480,40 @@ VideoData::Create(const VideoInfo& aInfo
   return v.forget();
 }
 #endif  // MOZ_OMX_DECODER
 
 // Alignment value - 1. 0 means that data isn't aligned.
 // For 32-bytes aligned, use 31U.
 #define RAW_DATA_ALIGNMENT 31U
 
-#define RAW_DATA_DEFAULT_SIZE 4096
-
 MediaRawData::MediaRawData()
   : MediaData(RAW_DATA, 0)
+  , mCrypto(mCryptoInternal)
   , mData(nullptr)
   , mSize(0)
-  , mCrypto(mCryptoInternal)
-  , mBuffer(new MediaByteBuffer())
-  , mPadding(0)
+  , mBuffer(nullptr)
+  , mCapacity(0)
 {
-  unused << mBuffer->SetCapacity(RAW_DATA_DEFAULT_SIZE, fallible);
 }
 
 MediaRawData::MediaRawData(const uint8_t* aData, size_t aSize)
   : MediaData(RAW_DATA, 0)
+  , mCrypto(mCryptoInternal)
   , mData(nullptr)
   , mSize(0)
-  , mCrypto(mCryptoInternal)
-  , mBuffer(new MediaByteBuffer())
-  , mPadding(0)
+  , mBuffer(nullptr)
+  , mCapacity(0)
 {
   if (!EnsureCapacity(aSize)) {
     return;
   }
 
   // We ensure sufficient capacity above so this shouldn't fail.
-  MOZ_ALWAYS_TRUE(mBuffer->AppendElements(aData, aSize, fallible));
-  MOZ_ALWAYS_TRUE(mBuffer->AppendElements(RAW_DATA_ALIGNMENT, fallible));
+  memcpy(mData, aData, aSize);
   mSize = aSize;
 }
 
 already_AddRefed<MediaRawData>
 MediaRawData::Clone() const
 {
   nsRefPtr<MediaRawData> s = new MediaRawData;
   s->mTimecode = mTimecode;
@@ -528,145 +524,140 @@ MediaRawData::Clone() const
   s->mExtraData = mExtraData;
   s->mCryptoInternal = mCryptoInternal;
   s->mTrackInfo = mTrackInfo;
   if (mSize) {
     if (!s->EnsureCapacity(mSize)) {
       return nullptr;
     }
 
-    // We ensure sufficient capacity above so this shouldn't fail.
-    MOZ_ALWAYS_TRUE(s->mBuffer->AppendElements(mData, mSize, fallible));
-    MOZ_ALWAYS_TRUE(s->mBuffer->AppendElements(RAW_DATA_ALIGNMENT, fallible));
+    memcpy(s->mData, mData, mSize);
     s->mSize = mSize;
   }
   return s.forget();
 }
 
+// EnsureCapacity ensures that the buffer is big enough to hold
+// aSize. It doesn't set the mSize. It's up to the caller to adjust it.
 bool
 MediaRawData::EnsureCapacity(size_t aSize)
 {
-  if (mData && mBuffer->Capacity() >= aSize + RAW_DATA_ALIGNMENT * 2) {
+  const size_t sizeNeeded = aSize + RAW_DATA_ALIGNMENT * 2;
+
+  if (mData && mCapacity >= sizeNeeded) {
     return true;
   }
-  if (!mBuffer->SetCapacity(aSize + RAW_DATA_ALIGNMENT * 2, fallible)) {
+  nsAutoArrayPtr<uint8_t> newBuffer;
+  newBuffer = new (fallible) uint8_t[sizeNeeded];
+  if (!newBuffer) {
     return false;
   }
+
   // Find alignment address.
   const uintptr_t alignmask = RAW_DATA_ALIGNMENT;
-  mData = reinterpret_cast<uint8_t*>(
-    (reinterpret_cast<uintptr_t>(mBuffer->Elements()) + alignmask) & ~alignmask);
-  MOZ_ASSERT(uintptr_t(mData) % (RAW_DATA_ALIGNMENT+1) == 0);
-
-  // Shift old data according to new padding.
-  uint32_t oldpadding = int32_t(mPadding);
-  mPadding = mData - mBuffer->Elements();
-  int32_t shift = int32_t(mPadding) - int32_t(oldpadding);
+  uint8_t* newData = reinterpret_cast<uint8_t*>(
+    (reinterpret_cast<uintptr_t>(newBuffer.get()) + alignmask) & ~alignmask);
+  MOZ_ASSERT(uintptr_t(newData) % (RAW_DATA_ALIGNMENT+1) == 0);
+  memcpy(newData, mData, mSize);
 
-  if (shift == 0) {
-    // Nothing to do.
-  } else if (shift > 0) {
-    // We ensure sufficient capacity above so this shouldn't fail.
-    MOZ_ALWAYS_TRUE(mBuffer->InsertElementsAt(oldpadding, shift, fallible));
-  } else {
-    mBuffer->RemoveElementsAt(mPadding, -shift);
-  }
+  mBuffer = newBuffer.forget();
+  mCapacity = sizeNeeded;
+  mData = newData;
+
   return true;
 }
 
 MediaRawData::~MediaRawData()
 {
 }
 
 size_t
 MediaRawData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t size = aMallocSizeOf(this);
-
-  size += mBuffer->ShallowSizeOfIncludingThis(aMallocSizeOf);
+  size += aMallocSizeOf(mBuffer.get());
   return size;
 }
 
 MediaRawDataWriter*
 MediaRawData::CreateWriter()
 {
   return new MediaRawDataWriter(this);
 }
 
 MediaRawDataWriter::MediaRawDataWriter(MediaRawData* aMediaRawData)
-  : mData(nullptr)
-  , mSize(0)
-  , mCrypto(aMediaRawData->mCryptoInternal)
+  : mCrypto(aMediaRawData->mCryptoInternal)
   , mTarget(aMediaRawData)
-  , mBuffer(aMediaRawData->mBuffer.get())
 {
-  if (aMediaRawData->mData) {
-    mData = mBuffer->Elements() + mTarget->mPadding;
-    mSize = mTarget->mSize;
-  }
 }
 
 bool
 MediaRawDataWriter::EnsureSize(size_t aSize)
 {
-  if (aSize <= mSize) {
+  if (aSize <= mTarget->mSize) {
     return true;
   }
   if (!mTarget->EnsureCapacity(aSize)) {
     return false;
   }
-  mData = mBuffer->Elements() + mTarget->mPadding;
   return true;
 }
 
 bool
 MediaRawDataWriter::SetSize(size_t aSize)
 {
   if (aSize > mTarget->mSize && !EnsureSize(aSize)) {
     return false;
   }
 
-  // Pad our buffer. We ensure sufficient capacity above so this shouldn't fail.
-  MOZ_ALWAYS_TRUE(
-    mBuffer->SetLength(aSize + mTarget->mPadding + RAW_DATA_ALIGNMENT,
-                       fallible));
-  mTarget->mSize = mSize = aSize;
+  mTarget->mSize = aSize;
   return true;
 }
 
 bool
 MediaRawDataWriter::Prepend(const uint8_t* aData, size_t aSize)
 {
   if (!EnsureSize(aSize + mTarget->mSize)) {
     return false;
   }
 
-  // We ensure sufficient capacity above so this shouldn't fail.
-  MOZ_ALWAYS_TRUE(mBuffer->InsertElementsAt(mTarget->mPadding, aData, aSize,
-                                            fallible));
+  // Shift the data to the right by aSize to leave room for the new data.
+  memmove(mTarget->mData + aSize, mTarget->mData, mTarget->mSize);
+  memcpy(mTarget->mData, aData, aSize);
+
   mTarget->mSize += aSize;
-  mSize = mTarget->mSize;
   return true;
 }
 
 bool
 MediaRawDataWriter::Replace(const uint8_t* aData, size_t aSize)
 {
+  // If aSize is smaller than our current size, we leave the buffer as is,
+  // only adjusting the reported size.
   if (!EnsureSize(aSize)) {
     return false;
   }
 
-  // We ensure sufficient capacity above so this shouldn't fail.
-  MOZ_ALWAYS_TRUE(mBuffer->ReplaceElementsAt(mTarget->mPadding, mTarget->mSize,
-                                             aData, aSize, fallible));
-  mTarget->mSize = mSize = aSize;
+  memcpy(mTarget->mData, aData, aSize);
+  mTarget->mSize = aSize;
   return true;
 }
 
 void
 MediaRawDataWriter::Clear()
 {
-  mBuffer->RemoveElementsAt(mTarget->mPadding, mTarget->mSize);
-  mTarget->mSize = mSize = 0;
-  mTarget->mData = mData = nullptr;
+  mTarget->mSize = 0;
+  mTarget->mData = nullptr;
+}
+
+uint8_t*
+MediaRawDataWriter::Data()
+{
+  return mTarget->mData;
+}
+
+size_t
+MediaRawDataWriter::Size()
+{
+  return mTarget->Size();
 }
 
 } // namespace mozilla
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -355,51 +355,54 @@ public:
 // up to mSize bytes.
 
 class MediaRawData;
 
 class MediaRawDataWriter
 {
 public:
   // Pointer to data or null if not-yet allocated
-  uint8_t* mData;
+  uint8_t* Data();
   // Writeable size of buffer.
-  size_t mSize;
+  size_t Size();
   // Writeable reference to MediaRawData::mCryptoInternal
   CryptoSample& mCrypto;
 
   // Data manipulation methods. mData and mSize may be updated accordingly.
 
   // Set size of buffer, allocating memory as required.
   // If size is increased, new buffer area is filled with 0.
   bool SetSize(size_t aSize);
   // Add aData at the beginning of buffer.
   bool Prepend(const uint8_t* aData, size_t aSize);
   // Replace current content with aData.
   bool Replace(const uint8_t* aData, size_t aSize);
-  // Clear the memory buffer. Will set mData and mSize to 0.
+  // Clear the memory buffer. Will set target mData and mSize to 0.
   void Clear();
 
 private:
   friend class MediaRawData;
   explicit MediaRawDataWriter(MediaRawData* aMediaRawData);
   bool EnsureSize(size_t aSize);
   MediaRawData* mTarget;
-  nsRefPtr<MediaByteBuffer> mBuffer;
 };
 
 class MediaRawData : public MediaData {
 public:
   MediaRawData();
   MediaRawData(const uint8_t* aData, size_t mSize);
 
   // Pointer to data or null if not-yet allocated
-  const uint8_t* mData;
+  const uint8_t* Data() const { return mData; }
   // Size of buffer.
-  size_t mSize;
+  size_t Size() const { return mSize; }
+  size_t ComputedSizeOfIncludingThis() const
+  {
+    return sizeof(*this) + mCapacity;
+  }
 
   const CryptoSample& mCrypto;
   nsRefPtr<MediaByteBuffer> mExtraData;
 
   nsRefPtr<SharedTrackInfo> mTrackInfo;
 
   // Return a deep copy or nullptr if out of memory.
   virtual already_AddRefed<MediaRawData> Clone() const;
@@ -414,19 +417,21 @@ protected:
 private:
   friend class MediaRawDataWriter;
   // Ensure that the backend buffer can hold aSize data. Will update mData.
   // Will enforce that the start of allocated data is always 32 bytes
   // aligned and that it has sufficient end padding to allow for 32 bytes block
   // read as required by some data decoders.
   // Returns false if memory couldn't be allocated.
   bool EnsureCapacity(size_t aSize);
-  nsRefPtr<MediaByteBuffer> mBuffer;
+  uint8_t* mData;
+  size_t mSize;
+  nsAutoArrayPtr<uint8_t> mBuffer;
+  uint32_t mCapacity;
   CryptoSample mCryptoInternal;
-  uint32_t mPadding;
   MediaRawData(const MediaRawData&); // Not implemented
 };
 
   // MediaByteBuffer is a ref counted infallible TArray.
 class MediaByteBuffer : public nsTArray<uint8_t> {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaByteBuffer);
   MediaByteBuffer() = default;
   explicit MediaByteBuffer(size_t aCapacity) : nsTArray<uint8_t>(aCapacity) {}
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -279,27 +279,20 @@ MediaDecoderStateMachine::MediaDecoderSt
   // Ensure high precision timers are enabled on Windows, otherwise the state
   // machine isn't woken up at reliable intervals to set the next frame,
   // and we drop frames while painting. Note that multiple calls to this
   // function per-process is OK, provided each call is matched by a corresponding
   // timeEndPeriod() call.
   timeBeginPeriod(1);
 #endif
 
-  nsRefPtr<MediaDecoderStateMachine> self = this;
-
-  AudioQueue().AddPopListener(
-    [self] (const MediaData* aSample) {
-      self->OnAudioPopped(aSample->As<AudioData>());
-     }, mTaskQueue);
-
-  VideoQueue().AddPopListener(
-    [self] (const MediaData* aSample) {
-      self->OnVideoPopped(aSample->As<VideoData>());
-    }, mTaskQueue);
+  mAudioQueueListener = AudioQueue().PopEvent().Connect(
+    mTaskQueue, this, &MediaDecoderStateMachine::OnAudioPopped);
+  mVideoQueueListener = VideoQueue().PopEvent().Connect(
+    mTaskQueue, this, &MediaDecoderStateMachine::OnVideoPopped);
 }
 
 MediaDecoderStateMachine::~MediaDecoderStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   MOZ_COUNT_DTOR(MediaDecoderStateMachine);
 
   mReader = nullptr;
@@ -635,19 +628,16 @@ MediaDecoderStateMachine::Push(AudioData
   MOZ_ASSERT(aSample);
   // TODO: Send aSample to MSG and recalculate readystate before pushing,
   // otherwise AdvanceFrame may pop the sample before we have a chance
   // to reach playing.
   AudioQueue().Push(aSample);
   UpdateNextFrameStatus();
   DispatchDecodeTasksIfNeeded();
 
-  if (mAudioSink) {
-    mAudioSink->NotifyData();
-  }
 }
 
 void
 MediaDecoderStateMachine::PushFront(AudioData* aSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(aSample);
 
@@ -679,27 +669,27 @@ MediaDecoderStateMachine::PushFront(Vide
   MOZ_ASSERT(aSample);
 
   aSample->mFrameID = ++mCurrentFrameID;
   VideoQueue().PushFront(aSample);
   UpdateNextFrameStatus();
 }
 
 void
-MediaDecoderStateMachine::OnAudioPopped(const AudioData* aSample)
+MediaDecoderStateMachine::OnAudioPopped(const MediaData* aSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mDecoder->UpdatePlaybackOffset(std::max<int64_t>(0, aSample->mOffset));
   UpdateNextFrameStatus();
   DispatchAudioDecodeTaskIfNeeded();
 }
 
 void
-MediaDecoderStateMachine::OnVideoPopped(const VideoData* aSample)
+MediaDecoderStateMachine::OnVideoPopped(const MediaData* aSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mDecoder->UpdatePlaybackOffset(aSample->mOffset);
   UpdateNextFrameStatus();
   DispatchVideoDecodeTaskIfNeeded();
   // Notify the decode thread that the video queue's buffers may have
   // free'd up space for more frames.
@@ -781,20 +771,16 @@ MediaDecoderStateMachine::OnNotDecoded(M
   switch (mState) {
     case DECODER_STATE_BUFFERING:
     case DECODER_STATE_DECODING: {
       if (MaybeFinishDecodeFirstFrame()) {
         return;
       }
       CheckIfDecodeComplete();
       mDecoder->GetReentrantMonitor().NotifyAll();
-      // Tell AudioSink to wake up for audio queue is finished.
-      if (mAudioSink) {
-        mAudioSink->NotifyData();
-      }
       // Schedule the state machine to notify track ended as soon as possible.
       if (mAudioCaptured) {
         ScheduleStateMachine();
       }
       return;
     }
     case DECODER_STATE_SEEKING: {
       if (!mCurrentSeek.Exists()) {
@@ -2211,18 +2197,20 @@ void
 MediaDecoderStateMachine::FinishShutdown()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   // The reader's listeners hold references to the state machine,
   // creating a cycle which keeps the state machine and its shared
   // thread pools alive. So break it here.
-  AudioQueue().ClearListeners();
-  VideoQueue().ClearListeners();
+
+  // Prevent dangling pointers by disconnecting the listeners.
+  mAudioQueueListener.Disconnect();
+  mVideoQueueListener.Disconnect();
 
   // Disconnect canonicals and mirrors before shutting down our task queue.
   mBuffered.DisconnectIfConnected();
   mEstimatedDuration.DisconnectIfConnected();
   mExplicitDuration.DisconnectIfConnected();
   mPlayState.DisconnectIfConnected();
   mNextPlayState.DisconnectIfConnected();
   mLogicallySeeking.DisconnectIfConnected();
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -86,16 +86,17 @@ hardware (via AudioStream).
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/RollingMean.h"
 #include "mozilla/StateMirroring.h"
 
 #include "nsThreadUtils.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
 #include "MediaDecoderOwner.h"
+#include "MediaEventSource.h"
 #include "MediaMetadataManager.h"
 #include "MediaTimer.h"
 #include "DecodedStream.h"
 #include "ImageContainer.h"
 
 namespace mozilla {
 
 class AudioSegment;
@@ -394,18 +395,18 @@ protected:
 
   // Inserts MediaData* samples into their respective MediaQueues.
   // aSample must not be null.
   void Push(AudioData* aSample);
   void Push(VideoData* aSample);
   void PushFront(AudioData* aSample);
   void PushFront(VideoData* aSample);
 
-  void OnAudioPopped(const AudioData* aSample);
-  void OnVideoPopped(const VideoData* aSample);
+  void OnAudioPopped(const MediaData* aSample);
+  void OnVideoPopped(const MediaData* aSample);
 
   void VolumeChanged();
   void LogicalPlaybackRateChanged();
   void PreservesPitchChanged();
 
   MediaQueue<MediaData>& AudioQueue() { return mAudioQueue; }
   MediaQueue<MediaData>& VideoQueue() { return mVideoQueue; }
 
@@ -1283,16 +1284,19 @@ private:
   // without holding the monitor.
   nsRefPtr<DecodedStream> mDecodedStream;
 
   // Media data resource from the decoder.
   nsRefPtr<MediaResource> mResource;
 
   MozPromiseRequestHolder<GenericPromise> mAudioSinkPromise;
 
+  MediaEventListener mAudioQueueListener;
+  MediaEventListener mVideoQueueListener;
+
 private:
   // The buffered range. Mirrored from the decoder thread.
   Mirror<media::TimeIntervals> mBuffered;
 
   // The duration according to the demuxer's current estimate, mirrored from the main thread.
   Mirror<media::NullableTimeUnit> mEstimatedDuration;
 
   // The duration explicitly set by JS, mirrored from the main thread.
--- a/dom/media/MediaQueue.h
+++ b/dom/media/MediaQueue.h
@@ -3,64 +3,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/. */
 #if !defined(MediaQueue_h_)
 #define MediaQueue_h_
 
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/TaskQueue.h"
-#include "mozilla/UniquePtr.h"
 
 #include "nsDeque.h"
-#include "nsTArray.h"
+#include "MediaEventSource.h"
 
 namespace mozilla {
 
 // Thread and type safe wrapper around nsDeque.
 template <class T>
 class MediaQueueDeallocator : public nsDequeFunctor {
   virtual void* operator() (void* aObject) {
     nsRefPtr<T> releaseMe = dont_AddRef(static_cast<T*>(aObject));
     return nullptr;
   }
 };
 
 template <class T>
 class MediaQueue : private nsDeque {
-  struct Listener {
-    virtual ~Listener() {}
-    virtual void Dispatch(T* aItem) = 0;
-  };
-
-  template<typename Function>
-  class PopListener : public Listener {
-  public:
-    explicit PopListener(const Function& aFunction, TaskQueue* aTarget)
-      : mFunction(aFunction), mTarget(aTarget) {}
-
-    void Dispatch(T* aItem) override {
-      nsRefPtr<T> item = aItem;
-      Function function = mFunction;
-      nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
-        function(item);
-      });
-      mTarget->Dispatch(r.forget());
-    }
-  private:
-    Function mFunction;
-    nsRefPtr<TaskQueue> mTarget;
-  };
-
-  void NotifyPopListeners(T* aItem) {
-    for (auto&& l : mPopListeners) {
-      l->Dispatch(aItem);
-    }
-  }
-
 public:
   MediaQueue()
     : nsDeque(new MediaQueueDeallocator<T>()),
       mReentrantMonitor("mediaqueue"),
       mEndOfStream(false)
   {}
 
   ~MediaQueue() {
@@ -72,30 +41,32 @@ public:
     return nsDeque::GetSize();
   }
 
   inline void Push(T* aItem) {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     MOZ_ASSERT(aItem);
     NS_ADDREF(aItem);
     nsDeque::Push(aItem);
+    mPushEvent.Notify();
   }
 
   inline void PushFront(T* aItem) {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     MOZ_ASSERT(aItem);
     NS_ADDREF(aItem);
     nsDeque::PushFront(aItem);
+    mPushEvent.Notify();
   }
 
   inline already_AddRefed<T> PopFront() {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     nsRefPtr<T> rv = dont_AddRef(static_cast<T*>(nsDeque::PopFront()));
     if (rv) {
-      NotifyPopListeners(rv);
+      mPopEvent.Notify(rv);
     }
     return rv.forget();
   }
 
   inline T* Peek() {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     return static_cast<T*>(nsDeque::Peek());
   }
@@ -125,16 +96,17 @@ public:
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     return mEndOfStream;
   }
 
   // Informs the media queue that it won't be receiving any more items.
   void Finish() {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mEndOfStream = true;
+    mFinishEvent.Notify();
   }
 
   // Returns the approximate number of microseconds of items in the queue.
   int64_t Duration() {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     if (GetSize() == 0) {
       return 0;
     }
@@ -180,31 +152,33 @@ public:
     uint32_t frames = 0;
     for (int32_t i = 0; i < GetSize(); ++i) {
       T* v = static_cast<T*>(ObjectAt(i));
       frames += v->mFrames;
     }
     return frames;
   }
 
-  void ClearListeners() {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    mPopListeners.Clear();
+  MediaEventSource<nsRefPtr<T>>& PopEvent() {
+    return mPopEvent;
   }
 
-  template<typename Function>
-  void AddPopListener(const Function& aFunction, TaskQueue* aTarget) {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    mPopListeners.AppendElement()->reset(
-      new PopListener<Function>(aFunction, aTarget));
+  MediaEventSource<void>& PushEvent() {
+    return mPushEvent;
+  }
+
+  MediaEventSource<void>& FinishEvent() {
+    return mFinishEvent;
   }
 
 private:
   mutable ReentrantMonitor mReentrantMonitor;
-  nsTArray<UniquePtr<Listener>> mPopListeners;
+  MediaEventProducer<nsRefPtr<T>> mPopEvent;
+  MediaEventProducer<void> mPushEvent;
+  MediaEventProducer<void> mFinishEvent;
   // True when we've decoded the last frame of data in the
   // bitstream for which we're queueing frame data.
   bool mEndOfStream;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -655,17 +655,17 @@ CDMProxy::gmp_Decrypt(nsRefPtr<DecryptJo
 
   if (!mCDM) {
     aJob->PostResult(GMPAbortedErr);
     return;
   }
 
   aJob->mId = ++mDecryptionJobCount;
   nsTArray<uint8_t> data;
-  data.AppendElements(aJob->mSample->mData, aJob->mSample->mSize);
+  data.AppendElements(aJob->mSample->Data(), aJob->mSample->Size());
   mCDM->Decrypt(aJob->mId, aJob->mSample->mCrypto, data);
   mDecryptionJobs.AppendElement(aJob.forget());
 }
 
 void
 CDMProxy::gmp_Decrypted(uint32_t aId,
                         GMPErr aResult,
                         const nsTArray<uint8_t>& aDecryptedData)
@@ -696,24 +696,24 @@ CDMProxy::DecryptJob::PostResult(GMPErr 
 {
   nsTArray<uint8_t> empty;
   PostResult(aResult, empty);
 }
 
 void
 CDMProxy::DecryptJob::PostResult(GMPErr aResult, const nsTArray<uint8_t>& aDecryptedData)
 {
-  if (aDecryptedData.Length() != mSample->mSize) {
+  if (aDecryptedData.Length() != mSample->Size()) {
     NS_WARNING("CDM returned incorrect number of decrypted bytes");
   }
   if (GMP_SUCCEEDED(aResult)) {
     nsAutoPtr<MediaRawDataWriter> writer(mSample->CreateWriter());
-    PodCopy(writer->mData,
+    PodCopy(writer->Data(),
             aDecryptedData.Elements(),
-            std::min<size_t>(aDecryptedData.Length(), mSample->mSize));
+            std::min<size_t>(aDecryptedData.Length(), mSample->Size()));
   } else if (aResult == GMPNoKeyErr) {
     NS_WARNING("CDM returned GMPNoKeyErr");
     // We still have the encrypted sample, so we can re-enqueue it to be
     // decrypted again once the key is usable again.
   } else {
     nsAutoCString str("CDM returned decode failure GMPErr=");
     str.AppendInt(aResult);
     NS_WARNING(str.get());
--- a/dom/media/gmp/GMPAudioHost.cpp
+++ b/dom/media/gmp/GMPAudioHost.cpp
@@ -35,17 +35,17 @@ GMPAudioSamplesImpl::GMPAudioSamplesImpl
 GMPAudioSamplesImpl::GMPAudioSamplesImpl(MediaRawData* aSample,
                                          uint32_t aChannels,
                                          uint32_t aRate)
  : mFormat(kGMPAudioEncodedSamples)
  , mTimeStamp(aSample->mTime)
  , mChannels(aChannels)
  , mRate(aRate)
 {
-  mBuffer.AppendElements(aSample->mData, aSample->mSize);
+  mBuffer.AppendElements(aSample->Data(), aSample->Size());
   if (aSample->mCrypto.mValid) {
     mCrypto = new GMPEncryptedBufferDataImpl(aSample->mCrypto);
   }
 }
 
 GMPAudioSamplesImpl::~GMPAudioSamplesImpl()
 {
 }
--- a/dom/media/gtest/TestMP3Demuxer.cpp
+++ b/dom/media/gtest/TestMP3Demuxer.cpp
@@ -204,17 +204,17 @@ TEST_F(MP3DemuxerTest, FrameParsing) {
 
     while (frameData) {
       if (static_cast<int64_t>(target.mSyncOffsets.size()) > numFrames) {
         // Test sync offsets.
         EXPECT_EQ(target.mSyncOffsets[numFrames], frameData->mOffset);
       }
 
       ++numFrames;
-      parsedLength += frameData->mSize;
+      parsedLength += frameData->Size();
 
       const auto& frame = target.mDemuxer->LastFrame();
       const auto& header = frame.Header();
       ASSERT_TRUE(header.IsValid());
 
       numSamples += header.SamplesPerFrame();
 
       EXPECT_EQ(target.mMPEGLayer, header.Layer());
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -425,17 +425,17 @@ TrackBuffersManager::DoEvictData(const T
       if (toEvict < 0) {
         break;
       }
       partialEvict = 0;
     }
     if (frame->mTime >= lowerLimit.ToMicroseconds()) {
       break;
     }
-    partialEvict += sizeof(*frame) + frame->mSize;
+    partialEvict += frame->ComputedSizeOfIncludingThis();
   }
 
   int64_t finalSize = mSizeSourceBuffer - aSizeToEvict;
 
   if (lastKeyFrameIndex > 0) {
     MSE_DEBUG("Step1. Evicting %u bytes prior currentTime",
               aSizeToEvict - toEvict);
     CodedFrameRemoval(
@@ -463,17 +463,17 @@ TrackBuffersManager::DoEvictData(const T
       if (toEvict < 0) {
         break;
       }
       partialEvict = 0;
     }
     if (frame->mTime <= upperLimit.ToMicroseconds()) {
       break;
     }
-    partialEvict += sizeof(*frame) + frame->mSize;
+    partialEvict += frame->ComputedSizeOfIncludingThis();
   }
   if (lastKeyFrameIndex < buffer.Length()) {
     MSE_DEBUG("Step2. Evicting %u bytes from trailing data",
               mSizeSourceBuffer - finalSize);
     CodedFrameRemoval(
       TimeInterval(TimeUnit::FromMicroseconds(buffer[lastKeyFrameIndex]->GetEndTime() + 1),
                    TimeUnit::FromInfinity()));
   }
@@ -1395,17 +1395,17 @@ TrackBuffersManager::ProcessFrames(Track
         sizeNewSamples = 0;
       }
       trackBuffer.mNeedRandomAccessPoint = true;
       needDiscontinuityCheck = true;
       continue;
     }
 
     samplesRange += sampleInterval;
-    sizeNewSamples += sizeof(*sample) + sample->mSize;
+    sizeNewSamples += sample->ComputedSizeOfIncludingThis();
     sample->mTime = sampleInterval.mStart.ToMicroseconds();
     sample->mTimecode = decodeTimestamp.ToMicroseconds();
     sample->mTrackInfo = trackBuffer.mLastInfo;
     samples.AppendElement(sample);
 
     // Steps 11,12,13,14, 15 and 16 will be done in one block in InsertFrames.
 
     // 17. Set last decode timestamp for track buffer to decode timestamp.
@@ -1601,17 +1601,17 @@ TrackBuffersManager::RemoveFrames(const 
     MediaRawData* sample = data[i].get();
     TimeInterval sampleInterval =
       TimeInterval(TimeUnit::FromMicroseconds(sample->mTime),
                    TimeUnit::FromMicroseconds(sample->GetEndTime()));
     removedIntervals += sampleInterval;
     if (sample->mDuration > maxSampleDuration) {
       maxSampleDuration = sample->mDuration;
     }
-    aTrackData.mSizeBuffer -= sizeof(*sample) + sample->mSize;
+    aTrackData.mSizeBuffer -= sample->ComputedSizeOfIncludingThis();
   }
 
   removedIntervals.SetFuzz(TimeUnit::FromMicroseconds(maxSampleDuration));
 
   MSE_DEBUG("Removing frames from:%u (frames:%u) ([%f, %f))",
             firstRemovedIndex.ref(),
             lastRemovedIndex - firstRemovedIndex.ref() + 1,
             removedIntervals.GetStart().ToSeconds(),
--- a/dom/media/platforms/agnostic/OpusDecoder.cpp
+++ b/dom/media/platforms/agnostic/OpusDecoder.cpp
@@ -144,45 +144,45 @@ OpusDataDecoder::DoDecode(MediaRawData* 
   if (mPaddingDiscarded) {
     // Discard padding should be used only on the final packet, so
     // decoding after a padding discard is invalid.
     OPUS_DEBUG("Opus error, discard padding on interstitial packet");
     return -1;
   }
 
   // Maximum value is 63*2880, so there's no chance of overflow.
-  int32_t frames_number = opus_packet_get_nb_frames(aSample->mData,
-                                                    aSample->mSize);
+  int32_t frames_number = opus_packet_get_nb_frames(aSample->Data(),
+                                                    aSample->Size());
   if (frames_number <= 0) {
     OPUS_DEBUG("Invalid packet header: r=%ld length=%ld",
-               frames_number, aSample->mSize);
+               frames_number, aSample->Size());
     return -1;
   }
 
-  int32_t samples = opus_packet_get_samples_per_frame(aSample->mData,
+  int32_t samples = opus_packet_get_samples_per_frame(aSample->Data(),
                                            opus_int32(mOpusParser->mRate));
 
 
   // A valid Opus packet must be between 2.5 and 120 ms long (48kHz).
   int32_t frames = frames_number*samples;
   if (frames < 120 || frames > 5760) {
     OPUS_DEBUG("Invalid packet frames: %ld", frames);
     return -1;
   }
 
   nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
 
   // Decode to the appropriate sample type.
 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
   int ret = opus_multistream_decode_float(mOpusDecoder,
-                                          aSample->mData, aSample->mSize,
+                                          aSample->Data(), aSample->Size(),
                                           buffer, frames, false);
 #else
   int ret = opus_multistream_decode(mOpusDecoder,
-                                    aSample->mData, aSample->mSize,
+                                    aSample->Data(), aSample->Size(),
                                     buffer, frames, false);
 #endif
   if (ret < 0) {
     return -1;
   }
   NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
   CheckedInt64 startTime = aSample->mTime;
 
--- a/dom/media/platforms/agnostic/VPXDecoder.cpp
+++ b/dom/media/platforms/agnostic/VPXDecoder.cpp
@@ -82,25 +82,25 @@ VPXDecoder::Flush()
 int
 VPXDecoder::DoDecodeFrame(MediaRawData* aSample)
 {
 #if defined(DEBUG)
   vpx_codec_stream_info_t si;
   PodZero(&si);
   si.sz = sizeof(si);
   if (mCodec == Codec::VP8) {
-    vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), aSample->mData, aSample->mSize, &si);
+    vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), aSample->Data(), aSample->Size(), &si);
   } else if (mCodec == Codec::VP9) {
-    vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), aSample->mData, aSample->mSize, &si);
+    vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), aSample->Data(), aSample->Size(), &si);
   }
   NS_ASSERTION(bool(si.is_kf) == aSample->mKeyframe,
                "VPX Decode Keyframe error sample->mKeyframe and si.si_kf out of sync");
 #endif
 
-  if (vpx_codec_err_t r = vpx_codec_decode(&mVPX, aSample->mData, aSample->mSize, nullptr, 0)) {
+  if (vpx_codec_err_t r = vpx_codec_decode(&mVPX, aSample->Data(), aSample->Size(), nullptr, 0)) {
     LOG("VPX Decode error: %s", vpx_codec_err_to_string(r));
     return -1;
   }
 
   vpx_image_t      *img;
 
   if ((img = vpx_codec_get_frame(&mVPX, &mIter))) {
     NS_ASSERTION(img->fmt == VPX_IMG_FMT_I420, "WebM image format not I420");
--- a/dom/media/platforms/agnostic/VorbisDecoder.cpp
+++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp
@@ -147,18 +147,18 @@ VorbisDataDecoder::Decode(MediaRawData* 
   } else if (mTaskQueue->IsEmpty()) {
     mCallback->InputExhausted();
   }
 }
 
 int
 VorbisDataDecoder::DoDecode(MediaRawData* aSample)
 {
-  const unsigned char* aData = aSample->mData;
-  size_t aLength = aSample->mSize;
+  const unsigned char* aData = aSample->Data();
+  size_t aLength = aSample->Size();
   int64_t aOffset = aSample->mOffset;
   uint64_t aTstampUsecs = aSample->mTime;
   int64_t aTotalFrames = 0;
 
   MOZ_ASSERT(mPacketCount >= 3);
 
   ogg_packet pkt = InitVorbisPacket(aData, aLength, false, false, -1, mPacketCount++);
   bool first_packet = mPacketCount == 4;
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
@@ -121,23 +121,23 @@ GMPVideoDecoder::CreateFrame(MediaRawDat
   GMPVideoFrame* ftmp = nullptr;
   GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
   if (GMP_FAILED(err)) {
     mCallback->Error();
     return nullptr;
   }
 
   GMPUniquePtr<GMPVideoEncodedFrame> frame(static_cast<GMPVideoEncodedFrame*>(ftmp));
-  err = frame->CreateEmptyFrame(aSample->mSize);
+  err = frame->CreateEmptyFrame(aSample->Size());
   if (GMP_FAILED(err)) {
     mCallback->Error();
     return nullptr;
   }
 
-  memcpy(frame->Buffer(), aSample->mData, frame->Size());
+  memcpy(frame->Buffer(), aSample->Data(), frame->Size());
 
   // Convert 4-byte NAL unit lengths to host-endian 4-byte buffer lengths to
   // suit the GMP API.
   if (mConvertNALUnitLengths) {
     const int kNALLengthSize = 4;
     uint8_t* buf = frame->Buffer();
     while (buf < frame->Buffer() + frame->Size() - kNALLengthSize) {
       uint32_t length = BigEndian::readUint32(buf) + kNALLengthSize;
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -468,28 +468,28 @@ void MediaCodecDataDecoder::DecoderLoop(
 
       if (inputIndex >= 0) {
         jni::Object::LocalRef buffer(frame.GetEnv());
         res = GetInputBuffer(frame.GetEnv(), inputIndex, &buffer);
         HANDLE_DECODER_ERROR();
 
         void* directBuffer = frame.GetEnv()->GetDirectBufferAddress(buffer.Get());
 
-        MOZ_ASSERT(frame.GetEnv()->GetDirectBufferCapacity(buffer.Get()) >= sample->mSize,
+        MOZ_ASSERT(frame.GetEnv()->GetDirectBufferCapacity(buffer.Get()) >= sample->Size(),
           "Decoder buffer is not large enough for sample");
 
         {
           // We're feeding this to the decoder, so remove it from the queue
           MonitorAutoLock lock(mMonitor);
           mQueue.pop();
         }
 
-        PodCopy((uint8_t*)directBuffer, sample->mData, sample->mSize);
+        PodCopy((uint8_t*)directBuffer, sample->Data(), sample->Size());
 
-        res = mDecoder->QueueInputBuffer(inputIndex, 0, sample->mSize,
+        res = mDecoder->QueueInputBuffer(inputIndex, 0, sample->Size(),
                                          sample->mTime, 0);
         HANDLE_DECODER_ERROR();
 
         mDurations.push(media::TimeUnit::FromMicroseconds(sample->mDuration));
         sample = nullptr;
         outputDone = false;
       }
     }
--- a/dom/media/platforms/apple/AppleATDecoder.cpp
+++ b/dom/media/platforms/apple/AppleATDecoder.cpp
@@ -63,17 +63,17 @@ AppleATDecoder::Init()
 nsresult
 AppleATDecoder::Input(MediaRawData* aSample)
 {
   LOG("mp4 input sample %p %lld us %lld pts%s %llu bytes audio",
       aSample,
       aSample->mDuration,
       aSample->mTime,
       aSample->mKeyframe ? " keyframe" : "",
-      (unsigned long long)aSample->mSize);
+      (unsigned long long)aSample->Size());
 
   // Queue a task to perform the actual decoding on a separate thread.
   nsCOMPtr<nsIRunnable> runnable =
       NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
         this,
         &AppleATDecoder::SubmitSample,
         nsRefPtr<MediaRawData>(aSample));
   mTaskQueue->Dispatch(runnable.forget());
@@ -211,17 +211,17 @@ AppleATDecoder::DecodeSample(MediaRawDat
 
   // Descriptions for _decompressed_ audio packets. ignored.
   nsAutoArrayPtr<AudioStreamPacketDescription>
     packets(new AudioStreamPacketDescription[MAX_AUDIO_FRAMES]);
 
   // This API insists on having packets spoon-fed to it from a callback.
   // This structure exists only to pass our state.
   PassthroughUserData userData =
-    { channels, (UInt32)aSample->mSize, aSample->mData };
+    { channels, (UInt32)aSample->Size(), aSample->Data() };
 
   // Decompressed audio buffer
   nsAutoArrayPtr<AudioDataValue> decoded(new AudioDataValue[maxDecodedSamples]);
 
   do {
     AudioBufferList decBuffer;
     decBuffer.mNumberBuffers = 1;
     decBuffer.mBuffers[0].mNumberChannels = channels;
@@ -483,18 +483,18 @@ AppleATDecoder::GetImplicitAACMagicCooki
                                       &mStream);
     if (rv) {
       NS_WARNING("Couldn't open AudioFileStream");
       return NS_ERROR_FAILURE;
     }
   }
 
   OSStatus status = AudioFileStreamParseBytes(mStream,
-                                              adtssample->mSize,
-                                              adtssample->mData,
+                                              adtssample->Size(),
+                                              adtssample->Data(),
                                               0 /* discontinuity */);
   if (status) {
     NS_WARNING("Couldn't parse sample");
   }
 
   if (status || mFileStreamError || mMagicCookie.Length()) {
     // We have decoded a magic cookie or an error occurred as such
     // we won't need the stream any longer.
--- a/dom/media/platforms/apple/AppleVDADecoder.cpp
+++ b/dom/media/platforms/apple/AppleVDADecoder.cpp
@@ -101,17 +101,17 @@ AppleVDADecoder::Shutdown()
 nsresult
 AppleVDADecoder::Input(MediaRawData* aSample)
 {
   LOG("mp4 input sample %p pts %lld duration %lld us%s %d bytes",
       aSample,
       aSample->mTime,
       aSample->mDuration,
       aSample->mKeyframe ? " keyframe" : "",
-      aSample->mSize);
+      aSample->Size());
 
   nsCOMPtr<nsIRunnable> runnable =
       NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
           this,
           &AppleVDADecoder::SubmitFrame,
           nsRefPtr<MediaRawData>(aSample));
   mTaskQueue->Dispatch(runnable.forget());
   return NS_OK;
@@ -302,17 +302,17 @@ AppleVDADecoder::OutputFrame(CVPixelBuff
 
   return NS_OK;
 }
 
 nsresult
 AppleVDADecoder::SubmitFrame(MediaRawData* aSample)
 {
   AutoCFRelease<CFDataRef> block =
-    CFDataCreate(kCFAllocatorDefault, aSample->mData, aSample->mSize);
+    CFDataCreate(kCFAllocatorDefault, aSample->Data(), aSample->Size());
   if (!block) {
     NS_ERROR("Couldn't create CFData");
     return NS_ERROR_FAILURE;
   }
 
   AutoCFRelease<CFNumberRef> pts =
     CFNumberCreate(kCFAllocatorDefault,
                    kCFNumberSInt64Type,
--- a/dom/media/platforms/apple/AppleVTDecoder.cpp
+++ b/dom/media/platforms/apple/AppleVTDecoder.cpp
@@ -79,17 +79,17 @@ AppleVTDecoder::Shutdown()
 nsresult
 AppleVTDecoder::Input(MediaRawData* aSample)
 {
   LOG("mp4 input sample %p pts %lld duration %lld us%s %d bytes",
       aSample,
       aSample->mTime,
       aSample->mDuration,
       aSample->mKeyframe ? " keyframe" : "",
-      aSample->mSize);
+      aSample->Size());
 
 #ifdef LOG_MEDIA_SHA1
   SHA1Sum hash;
   hash.update(aSample->data, aSample->size);
   uint8_t digest_buf[SHA1Sum::kHashSize];
   hash.finish(digest_buf);
   nsAutoCString digest;
   for (size_t i = 0; i < sizeof(digest_buf); i++) {
@@ -210,22 +210,22 @@ AppleVTDecoder::SubmitFrame(MediaRawData
   VTDecodeInfoFlags infoFlags;
   OSStatus rv;
 
   // FIXME: This copies the sample data. I think we can provide
   // a custom block source which reuses the aSample buffer.
   // But note that there may be a problem keeping the samples
   // alive over multiple frames.
   rv = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, // Struct allocator.
-                                          const_cast<uint8_t*>(aSample->mData),
-                                          aSample->mSize,
+                                          const_cast<uint8_t*>(aSample->Data()),
+                                          aSample->Size(),
                                           kCFAllocatorNull, // Block allocator.
                                           NULL, // Block source.
                                           0,    // Data offset.
-                                          aSample->mSize,
+                                          aSample->Size(),
                                           false,
                                           block.receive());
   if (rv != noErr) {
     NS_ERROR("Couldn't create CMBlockBuffer");
     return NS_ERROR_FAILURE;
   }
   CMSampleTimingInfo timestamp = TimingInfoFromSample(aSample);
   rv = CMSampleBufferCreate(kCFAllocatorDefault, block, true, 0, 0, mFormat, 1, 1, &timestamp, 0, NULL, sample.receive());
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
@@ -83,18 +83,18 @@ CopyAndPackAudio(AVFrame* aFrame, uint32
 }
 
 void
 FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
 {
   AVPacket packet;
   av_init_packet(&packet);
 
-  packet.data = const_cast<uint8_t*>(aSample->mData);
-  packet.size = aSample->mSize;
+  packet.data = const_cast<uint8_t*>(aSample->Data());
+  packet.size = aSample->Size();
 
   if (!PrepareFrame()) {
     NS_WARNING("FFmpeg audio decoder failed to allocate frame.");
     mCallback->Error();
     return;
   }
 
   int64_t samplePosition = aSample->mOffset;
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
@@ -66,18 +66,18 @@ FFmpegH264Decoder<LIBAV_VER>::GetPts(con
 }
 
 FFmpegH264Decoder<LIBAV_VER>::DecodeResult
 FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
 {
   AVPacket packet;
   av_init_packet(&packet);
 
-  packet.data = const_cast<uint8_t*>(aSample->mData);
-  packet.size = aSample->mSize;
+  packet.data = const_cast<uint8_t*>(aSample->Data());
+  packet.size = aSample->Size();
   packet.dts = aSample->mTimecode;
   packet.pts = aSample->mTime;
   packet.flags = aSample->mKeyframe ? AV_PKT_FLAG_KEY : 0;
   packet.pos = aSample->mOffset;
 
   if (!PrepareFrame()) {
     NS_WARNING("FFmpeg h264 decoder failed to allocate frame.");
     mCallback->Error();
--- a/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp
@@ -122,18 +122,18 @@ GonkAudioDecoderManager::Input(MediaRawD
 
   mQueueSample.AppendElement(sample);
 
   status_t rv;
   while (mQueueSample.Length()) {
     nsRefPtr<MediaRawData> data = mQueueSample.ElementAt(0);
     {
       MonitorAutoUnlock mon_exit(mMonitor);
-      rv = mDecoder->Input(reinterpret_cast<const uint8_t*>(data->mData),
-                           data->mSize,
+      rv = mDecoder->Input(reinterpret_cast<const uint8_t*>(data->Data()),
+                           data->Size(),
                            data->mTime,
                            0);
     }
     if (rv == OK) {
       mQueueSample.RemoveElementAt(0);
     } else if (rv == -EAGAIN || rv == -ETIMEDOUT) {
       // In most cases, EAGAIN or ETIMEOUT are safe because OMX can't fill
       // buffer on time.
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
@@ -127,18 +127,18 @@ GonkVideoDecoderManager::Input(MediaRawD
 
   mQueueSample.AppendElement(sample);
 
   status_t rv;
   while (mQueueSample.Length()) {
     nsRefPtr<MediaRawData> data = mQueueSample.ElementAt(0);
     {
       MonitorAutoUnlock mon_unlock(mMonitor);
-      rv = mDecoder->Input(reinterpret_cast<const uint8_t*>(data->mData),
-                           data->mSize,
+      rv = mDecoder->Input(reinterpret_cast<const uint8_t*>(data->Data()),
+                           data->Size(),
                            data->mTime,
                            0);
     }
     if (rv == OK) {
       mQueueSample.RemoveElementAt(0);
     } else if (rv == -EAGAIN || rv == -ETIMEDOUT) {
       // In most cases, EAGAIN or ETIMEOUT are safe because OMX can't fill
       // buffer on time.
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
@@ -170,18 +170,18 @@ WMFAudioMFTManager::Init()
   mDecoder = decoder;
 
   return decoder.forget();
 }
 
 HRESULT
 WMFAudioMFTManager::Input(MediaRawData* aSample)
 {
-  return mDecoder->Input(aSample->mData,
-                         uint32_t(aSample->mSize),
+  return mDecoder->Input(aSample->Data(),
+                         uint32_t(aSample->Size()),
                          aSample->mTime);
 }
 
 HRESULT
 WMFAudioMFTManager::UpdateOutputType()
 {
   HRESULT hr;
 
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -267,18 +267,18 @@ WMFVideoMFTManager::InitInternal(bool aF
 HRESULT
 WMFVideoMFTManager::Input(MediaRawData* aSample)
 {
   if (!mDecoder) {
     // This can happen during shutdown.
     return E_FAIL;
   }
   // Forward sample data to the decoder.
-  return mDecoder->Input(aSample->mData,
-                         uint32_t(aSample->mSize),
+  return mDecoder->Input(aSample->Data(),
+                         uint32_t(aSample->Size()),
                          aSample->mTime);
 }
 
 HRESULT
 WMFVideoMFTManager::ConfigureVideoFrameGeometry()
 {
   RefPtr<IMFMediaType> mediaType;
   HRESULT hr = mDecoder->GetOutputMediaType(mediaType);
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -1,50 +1,53 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "AudioContext.h"
 
-#include "nsPIDOMWindow.h"
+#include "blink/PeriodicWave.h"
+
 #include "mozilla/ErrorResult.h"
+#include "mozilla/OwningNonNull.h"
+
 #include "mozilla/dom/AnalyserNode.h"
+#include "mozilla/dom/AudioContextBinding.h"
 #include "mozilla/dom/HTMLMediaElement.h"
-#include "mozilla/dom/AudioContextBinding.h"
 #include "mozilla/dom/OfflineAudioContextBinding.h"
-#include "mozilla/OwningNonNull.h"
-#include "MediaStreamGraph.h"
+#include "mozilla/dom/Promise.h"
+
+#include "AudioBuffer.h"
+#include "AudioBufferSourceNode.h"
 #include "AudioChannelService.h"
 #include "AudioDestinationNode.h"
-#include "AudioBufferSourceNode.h"
-#include "AudioBuffer.h"
+#include "AudioListener.h"
+#include "AudioStream.h"
+#include "BiquadFilterNode.h"
+#include "ChannelMergerNode.h"
+#include "ChannelSplitterNode.h"
+#include "ConvolverNode.h"
+#include "DelayNode.h"
+#include "DynamicsCompressorNode.h"
 #include "GainNode.h"
 #include "MediaElementAudioSourceNode.h"
+#include "MediaStreamAudioDestinationNode.h"
 #include "MediaStreamAudioSourceNode.h"
-#include "DelayNode.h"
+#include "MediaStreamGraph.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsPIDOMWindow.h"
+#include "OscillatorNode.h"
 #include "PannerNode.h"
-#include "AudioListener.h"
-#include "DynamicsCompressorNode.h"
-#include "BiquadFilterNode.h"
+#include "PeriodicWave.h"
 #include "ScriptProcessorNode.h"
 #include "StereoPannerNode.h"
-#include "ChannelMergerNode.h"
-#include "ChannelSplitterNode.h"
-#include "MediaStreamAudioDestinationNode.h"
 #include "WaveShaperNode.h"
-#include "PeriodicWave.h"
-#include "ConvolverNode.h"
-#include "OscillatorNode.h"
-#include "nsNetCID.h"
-#include "blink/PeriodicWave.h"
-#include "nsNetUtil.h"
-#include "AudioStream.h"
-#include "mozilla/dom/Promise.h"
 
 namespace mozilla {
 namespace dom {
 
 // 0 is a special value that MediaStreams use to denote they are not part of a
 // AudioContext.
 static dom::AudioContext::AudioContextId gAudioContextId = 1;
 
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -366,17 +366,17 @@ AudioDestinationNode::SizeOfIncludingThi
 {
   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
 }
 
 void
 AudioDestinationNode::DestroyAudioChannelAgent()
 {
   if (mAudioChannelAgent && !Context()->IsOffline()) {
-    mAudioChannelAgent->NotifyStoppedPlaying();
+    mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
     mAudioChannelAgent = nullptr;
   }
 }
 
 void
 AudioDestinationNode::DestroyMediaStream()
 {
   DestroyAudioChannelAgent();
@@ -600,17 +600,17 @@ AudioDestinationNode::CheckAudioChannelP
 void
 AudioDestinationNode::CreateAudioChannelAgent()
 {
   if (mIsOffline || !UseAudioChannelService()) {
     return;
   }
 
   if (mAudioChannelAgent) {
-    mAudioChannelAgent->NotifyStoppedPlaying();
+    mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
   }
 
   mAudioChannelAgent = new AudioChannelAgent();
   mAudioChannelAgent->InitWithWeakCallback(GetOwner(),
                                            static_cast<int32_t>(mAudioChannel),
                                            this);
 
   // The AudioChannelAgent must start playing immediately in order to avoid
@@ -693,23 +693,24 @@ AudioDestinationNode::InputMuted(bool aM
 {
   MOZ_ASSERT(Context() && !Context()->IsOffline());
 
   if (!mAudioChannelAgent) {
     return;
   }
 
   if (aMuted) {
-    mAudioChannelAgent->NotifyStoppedPlaying();
+    mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
     return;
   }
 
   float volume = 0.0;
   bool muted = true;
-  nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+  nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY,
+                                                         &volume, &muted);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   WindowAudioCaptureChanged();
   WindowVolumeChanged(volume, muted);
 }
 
--- a/dom/media/webm/IntelWebMVideoDecoder.cpp
+++ b/dom/media/webm/IntelWebMVideoDecoder.cpp
@@ -210,17 +210,17 @@ IntelWebMVideoDecoder::Demux(nsRefPtr<VP
   MOZ_ASSERT(mPlatform && mMediaDataDecoder);
 
   aSample = new VP8Sample(tstamp,
                           next_tstamp - tstamp,
                           0,
                           data,
                           length,
                           si.is_kf);
-  if (!aSample->mData) {
+  if (!aSample->Data()) {
     return false;
   }
 
   return true;
 }
 
 bool
 IntelWebMVideoDecoder::Decode()
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -1,56 +1,59 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Notification.h"
+
+#include "mozilla/Move.h"
+#include "mozilla/OwningNonNull.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/unused.h"
+
 #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
 #include "mozilla/dom/BindingUtils.h"
-#include "mozilla/OwningNonNull.h"
+#include "mozilla/dom/NotificationEvent.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseWorkerProxy.h"
-#include "mozilla/Move.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/unused.h"
+#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
+
+#include "nsContentPermissionHelper.h"
 #include "nsContentUtils.h"
+#include "nsDOMJSUtils.h"
+#include "nsGlobalWindow.h"
 #include "nsIAlertsService.h"
 #include "nsIAppsService.h"
 #include "nsIContentPermissionPrompt.h"
 #include "nsIDocument.h"
+#include "nsILoadContext.h"
 #include "nsINotificationStorage.h"
 #include "nsIPermissionManager.h"
+#include "nsIScriptSecurityManager.h"
 #include "nsIServiceWorkerManager.h"
 #include "nsIUUIDGenerator.h"
+#include "nsIXPConnect.h"
+#include "nsNetUtil.h"
+#include "nsProxyRelease.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStructuredCloneContainer.h"
 #include "nsToolkitCompsCID.h"
-#include "nsGlobalWindow.h"
-#include "nsDOMJSUtils.h"
-#include "nsProxyRelease.h"
-#include "nsNetUtil.h"
-#include "nsIScriptSecurityManager.h"
-#include "nsIXPConnect.h"
-#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
-#include "mozilla/dom/NotificationEvent.h"
-#include "mozilla/dom/PermissionMessageUtils.h"
-#include "mozilla/Services.h"
-#include "nsContentPermissionHelper.h"
-#include "nsILoadContext.h"
-#ifdef MOZ_B2G
-#include "nsIDOMDesktopNotification.h"
-#endif
-
 #include "ServiceWorkerManager.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
+#ifdef MOZ_B2G
+#include "nsIDOMDesktopNotification.h"
+#endif
+
 namespace mozilla {
 namespace dom {
 
 using namespace workers;
 
 struct NotificationStrings
 {
   const nsString mID;
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -2418,24 +2418,25 @@ NPError
       nsresult rv = inst->GetOrCreateAudioChannelAgent(getter_AddRefs(agent));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return NPERR_NO_ERROR;
       }
 
       MOZ_ASSERT(agent);
 
       if (isMuted) {
-        rv = agent->NotifyStoppedPlaying();
+        rv = agent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return NPERR_NO_ERROR;
         }
       } else {
         float volume = 0.0;
         bool muted = true;
-        rv = agent->NotifyStartedPlaying(&volume, &muted);
+        rv = agent->NotifyStartedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY,
+                                         &volume, &muted);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return NPERR_NO_ERROR;
         }
 
         rv = inst->WindowVolumeChanged(volume, muted);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return NPERR_NO_ERROR;
         }
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -1,42 +1,45 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Promise.h"
 
-#include "jsfriendapi.h"
 #include "js/Debug.h"
+
+#include "mozilla/Atomics.h"
+#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/OwningNonNull.h"
+#include "mozilla/Preferences.h"
+
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/DOMException.h"
-#include "mozilla/OwningNonNull.h"
+#include "mozilla/dom/MediaStreamError.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
-#include "mozilla/dom/MediaStreamError.h"
-#include "mozilla/Atomics.h"
-#include "mozilla/CycleCollectedJSRuntime.h"
-#include "mozilla/Preferences.h"
+
+#include "jsfriendapi.h"
+#include "nsContentUtils.h"
+#include "nsGlobalWindow.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsJSEnvironment.h"
+#include "nsJSPrincipals.h"
+#include "nsJSUtils.h"
+#include "nsPIDOMWindow.h"
 #include "PromiseCallback.h"
 #include "PromiseDebugging.h"
 #include "PromiseNativeHandler.h"
 #include "PromiseWorkerProxy.h"
-#include "nsContentUtils.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
-#include "nsJSPrincipals.h"
-#include "nsJSUtils.h"
-#include "nsPIDOMWindow.h"
-#include "nsJSEnvironment.h"
-#include "nsIScriptObjectPrincipal.h"
 #include "xpcpublic.h"
-#include "nsGlobalWindow.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 // Generator used by Promise::GetID.
 Atomic<uintptr_t> gIDGenerator(0);
 } // namespace
--- a/dom/push/PushManager.cpp
+++ b/dom/push/PushManager.cpp
@@ -281,26 +281,24 @@ private:
 
 class WorkerUnsubscribeResultCallback final : public nsIUnsubscribeResultCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
   explicit WorkerUnsubscribeResultCallback(PromiseWorkerProxy* aProxy)
     : mProxy(aProxy)
-    , mCallbackCalled(false)
   {
     AssertIsOnMainThread();
   }
 
   NS_IMETHOD
   OnUnsubscribe(nsresult aStatus, bool aSuccess) override
   {
     AssertIsOnMainThread();
-    mCallbackCalled = true;
     if (!mProxy) {
       return NS_OK;
     }
 
     MutexAutoLock lock(mProxy->GetCleanUpLock());
     if (mProxy->IsClean()) {
       return NS_OK;
     }
@@ -309,29 +307,37 @@ public:
     jsapi.Init();
 
     nsRefPtr<UnsubscribeResultRunnable> r =
       new UnsubscribeResultRunnable(mProxy, aStatus, aSuccess);
     if (!r->Dispatch(jsapi.cx())) {
       ReleasePromiseWorkerProxy(mProxy.forget());
     }
 
+    mProxy = nullptr;
     return NS_OK;
   }
 
 private:
   ~WorkerUnsubscribeResultCallback()
   {
-    // Enforces that UnsubscribeRunnable uses the callback for error
-    // reporting once it creates the callback.
-    MOZ_ASSERT(mCallbackCalled);
+    AssertIsOnMainThread();
+    if (mProxy) {
+      MutexAutoLock lock(mProxy->GetCleanUpLock());
+      if (!mProxy->IsClean()) {
+        AutoJSAPI jsapi;
+        jsapi.Init();
+        nsRefPtr<PromiseWorkerProxyControlRunnable> cr =
+          new PromiseWorkerProxyControlRunnable(mProxy->GetWorkerPrivate(), mProxy);
+        cr->Dispatch(jsapi.cx());
+      }
+    }
   }
 
   nsRefPtr<PromiseWorkerProxy> mProxy;
-  DebugOnly<bool> mCallbackCalled;
 };
 
 NS_IMPL_ISUPPORTS(WorkerUnsubscribeResultCallback, nsIUnsubscribeResultCallback)
 
 class UnsubscribeRunnable final : public nsRunnable
 {
 public:
   UnsubscribeRunnable(PromiseWorkerProxy* aProxy,
@@ -439,23 +445,19 @@ public:
   { }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
     nsRefPtr<Promise> promise = proxy->GetWorkerPromise();
     if (NS_SUCCEEDED(mStatus)) {
-      if (mEndpoint.IsEmpty()) {
-        promise->MaybeResolve(JS::NullHandleValue);
-      } else {
-        nsRefPtr<WorkerPushSubscription> sub =
-          new WorkerPushSubscription(mEndpoint, mScope);
-        promise->MaybeResolve(sub);
-      }
+      nsRefPtr<WorkerPushSubscription> sub =
+        new WorkerPushSubscription(mEndpoint, mScope);
+      promise->MaybeResolve(sub);
     } else {
       promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     }
 
     proxy->CleanUp(aCx);
     return true;
   }
 private:
@@ -472,24 +474,22 @@ class GetSubscriptionCallback final : pu
 {
 public:
   NS_DECL_ISUPPORTS
 
   explicit GetSubscriptionCallback(PromiseWorkerProxy* aProxy,
                                    const nsAString& aScope)
     : mProxy(aProxy)
     , mScope(aScope)
-    , mCallbackCalled(false)
   {}
 
   NS_IMETHOD
   OnPushEndpoint(nsresult aStatus, const nsAString& aEndpoint) override
   {
     AssertIsOnMainThread();
-    mCallbackCalled = true;
 
     if (!mProxy) {
       return NS_OK;
     }
 
     MutexAutoLock lock(mProxy->GetCleanUpLock());
     if (mProxy->IsClean()) {
       return NS_OK;
@@ -498,31 +498,40 @@ public:
     AutoJSAPI jsapi;
     jsapi.Init();
 
     nsRefPtr<GetSubscriptionResultRunnable> r =
       new GetSubscriptionResultRunnable(mProxy, aStatus, aEndpoint, mScope);
     if (!r->Dispatch(jsapi.cx())) {
       ReleasePromiseWorkerProxy(mProxy.forget());
     }
+
+    mProxy = nullptr;
     return NS_OK;
   }
 
 protected:
   ~GetSubscriptionCallback()
   {
-    // Enforces that GetSubscriptionRunnable uses the callback for error
-    // reporting once it creates the callback.
-    MOZ_ASSERT(mCallbackCalled);
+    AssertIsOnMainThread();
+    if (mProxy) {
+      MutexAutoLock lock(mProxy->GetCleanUpLock());
+      if (!mProxy->IsClean()) {
+        AutoJSAPI jsapi;
+        jsapi.Init();
+        nsRefPtr<PromiseWorkerProxyControlRunnable> cr =
+          new PromiseWorkerProxyControlRunnable(mProxy->GetWorkerPrivate(), mProxy);
+        cr->Dispatch(jsapi.cx());
+      }
+    }
   }
 
 private:
   nsRefPtr<PromiseWorkerProxy> mProxy;
   nsString mScope;
-  DebugOnly<bool> mCallbackCalled;
 };
 
 NS_IMPL_ISUPPORTS(GetSubscriptionCallback, nsIPushEndpointCallback)
 
 class GetSubscriptionRunnable final : public nsRunnable
 {
 public:
   GetSubscriptionRunnable(PromiseWorkerProxy* aProxy,
--- a/dom/push/test/mochitest.ini
+++ b/dom/push/test/mochitest.ini
@@ -16,10 +16,8 @@ skip-if = os == "android" || toolkit == 
 [test_multiple_register_during_service_activation.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_unregister.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_multiple_register_different_scope.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_try_registering_offline_disabled.html]
 skip-if = os == "android" || toolkit == "gonk"
-[test_push_manager_worker.html]
-skip-if = os == "android" || toolkit == "gonk"
deleted file mode 100644
--- a/dom/push/test/test_push_manager_worker.html
+++ /dev/null
@@ -1,101 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-Bug 1184574: Expose PushManager to workers.
-
-Any copyright is dedicated to the Public Domain.
-http://creativecommons.org/licenses/publicdomain/
-
--->
-<head>
-  <title>Test for Bug 1184574</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
-</head>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1184574">Mozilla Bug 1184574</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-</pre>
-
-<script class="testbody" type="text/javascript">
-
-  var registration;
-
-  function start() {
-    return navigator.serviceWorker.register("worker.js" + "?" + (Math.random()), {scope: "."})
-    .then(swr => { registration = swr; return swr; });
-  }
-
-  function unregisterSW() {
-    return registration.unregister().then(function(result) {
-      ok(result, "Unregister should return true.");
-    }, function(e) {
-      dump("Unregistering the SW failed with " + e + "\n");
-    });
-  }
-
-  function setupPushNotification(swr) {
-    return swr.pushManager.subscribe().then(
-      pushSubscription => {
-        ok(true, "successful registered for push notification");
-        return pushSubscription;
-      }, error => {
-        ok(false, "could not register for push notification");
-      });
-  }
-
-  function getNewEndpointFromWorker(pushSubscription) {
-    return new Promise((resolve, reject) => {
-      var channel = new MessageChannel();
-      channel.port1.onmessage = e => {
-        (e.data.error ? reject : resolve)(e.data);
-      };
-      registration.active.postMessage({
-        endpoint: pushSubscription.endpoint,
-      }, [channel.port2]);
-    }).then(data => {
-      return registration.pushManager.getSubscription().then(
-        pushSubscription => {
-          is(data.endpoint, pushSubscription.endpoint,
-             "Wrong push endpoint in parent");
-          return pushSubscription;
-      });
-    });
-  }
-
-  function unregisterPushNotification(pushSubscription) {
-    return pushSubscription.unsubscribe().then(
-      result => {
-      ok(result, "unsubscribe() on existing subscription should return true.");
-      return pushSubscription;
-    }, error => {
-      ok(false, "unsubscribe() should never fail.");
-    });
-  }
-
-  function runTest() {
-    start()
-    .then(setupPushNotification)
-    .then(getNewEndpointFromWorker)
-    .then(unregisterPushNotification)
-    .then(unregisterSW)
-    .catch(function(e) {
-      ok(false, "Some test failed with error " + e);
-    }).then(SimpleTest.finish);
-  }
-
-  SpecialPowers.pushPrefEnv({"set": [
-    ["dom.push.enabled", true],
-    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true]
-    ]}, runTest);
-  SpecialPowers.addPermission('push', true, document);
-  SimpleTest.waitForExplicitFinish();
-</script>
-</body>
-</html>
--- a/dom/push/test/worker.js
+++ b/dom/push/test/worker.js
@@ -1,46 +1,16 @@
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
-// This worker is used for two types of tests. `handlePush` sends messages to
-// `frame.html`, which verifies that the worker can receive push messages.
-
-// `handleMessage` receives a message from `test_push_manager_worker.html`, and
-// verifies that `PushManager` can be used from the worker.
-
 this.onpush = handlePush;
-this.onmessage = handleMessage;
 
 function handlePush(event) {
 
   self.clients.matchAll().then(function(result) {
     // FIXME(nsm): Bug 1149195 will fix data exposure.
     if (event instanceof PushEvent && !('data' in event)) {
       result[0].postMessage({type: "finished", okay: "yes"});
       return;
     }
     result[0].postMessage({type: "finished", okay: "no"});
   });
 }
-
-function handleMessage(event) {
-  self.registration.pushManager.getSubscription().then(subscription => {
-    if (subscription.endpoint != event.data.endpoint) {
-      throw new Error("Wrong push endpoint in worker");
-    }
-    return subscription.unsubscribe();
-  }).then(result => {
-    if (!result) {
-      throw new Error("Error dropping subscription in worker");
-    }
-    return self.registration.pushManager.getSubscription();
-  }).then(subscription => {
-    if (subscription) {
-      throw new Error("Subscription not dropped in worker");
-    }
-    return self.registration.pushManager.subscribe();
-  }).then(subscription => {
-    event.ports[0].postMessage({endpoint: subscription.endpoint});
-  }).catch(error => {
-    event.ports[0].postMessage({error: error});
-  });
-}
--- a/dom/webidl/CaretStateChangedEvent.webidl
+++ b/dom/webidl/CaretStateChangedEvent.webidl
@@ -14,19 +14,21 @@ enum CaretChangedReason {
 };
 
 dictionary CaretStateChangedEventInit : EventInit {
   boolean collapsed = true;
   DOMRectReadOnly? boundingClientRect = null;
   CaretChangedReason reason = "visibilitychange";
   boolean caretVisible = false;
   boolean selectionVisible = false;
+  boolean selectionEditable = false;
 };
 
 [Constructor(DOMString type, optional CaretStateChangedEventInit eventInit),
  ChromeOnly]
 interface CaretStateChangedEvent : Event {
   readonly attribute boolean collapsed;
   readonly attribute DOMRectReadOnly? boundingClientRect;
   readonly attribute CaretChangedReason reason;
   readonly attribute boolean caretVisible;
   readonly attribute boolean selectionVisible;
+  readonly attribute boolean selectionEditable;
 };
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -126,25 +126,35 @@ ServiceWorkerContainer::Register(const n
   }
 
   // In ServiceWorkerContainer.register() the scope argument is parsed against
   // different base URLs depending on whether it was passed or not.
   nsCOMPtr<nsIURI> scopeURI;
 
   // Step 4. If none passed, parse against script's URL
   if (!aOptions.mScope.WasPassed()) {
-    nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), NS_LITERAL_CSTRING("./"),
-                            nullptr, scriptURI);
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
+    NS_NAMED_LITERAL_STRING(defaultScope, "./");
+    rv = NS_NewURI(getter_AddRefs(scopeURI), defaultScope,
+                   nullptr, scriptURI);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      nsAutoCString spec;
+      scriptURI->GetSpec(spec);
+      aRv.ThrowTypeError(MSG_INVALID_SCOPE, &defaultScope, &spec);
+      return nullptr;
+    }
   } else {
     // Step 5. Parse against entry settings object's base URL.
-    nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aOptions.mScope.Value(),
-                            nullptr, window->GetDocBaseURI());
+    rv = NS_NewURI(getter_AddRefs(scopeURI), aOptions.mScope.Value(),
+                   nullptr, window->GetDocBaseURI());
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      aRv.ThrowTypeError(MSG_INVALID_URL, &aOptions.mScope.Value());
+      nsAutoCString spec;
+      if (window->GetDocBaseURI()) {
+        window->GetDocBaseURI()->GetSpec(spec);
+      }
+      aRv.ThrowTypeError(MSG_INVALID_SCOPE, &aOptions.mScope.Value(), &spec);
       return nullptr;
     }
   }
 
   aRv = swm->Register(window, scopeURI, scriptURI, getter_AddRefs(promise));
   if (aRv.Failed()) {
     return nullptr;
   }
--- a/embedding/browser/nsWebBrowser.cpp
+++ b/embedding/browser/nsWebBrowser.cpp
@@ -1060,17 +1060,17 @@ nsWebBrowser::SaveChannel(nsIChannel* aC
   rv = mPersist->SaveChannel(aChannel, aFile);
   if (NS_FAILED(rv)) {
     mPersist = nullptr;
   }
   return rv;
 }
 
 NS_IMETHODIMP
-nsWebBrowser::SaveDocument(nsIDOMDocument* aDocument,
+nsWebBrowser::SaveDocument(nsISupports* aDocumentish,
                            nsISupports* aFile,
                            nsISupports* aDataPath,
                            const char* aOutputContentType,
                            uint32_t aEncodingFlags,
                            uint32_t aWrapColumn)
 {
   if (mPersist) {
     uint32_t currentState;
@@ -1081,21 +1081,23 @@ nsWebBrowser::SaveDocument(nsIDOMDocumen
       // You can't save again until the last save has completed
       return NS_ERROR_FAILURE;
     }
   }
 
   // Use the specified DOM document, or if none is specified, the one
   // attached to the web browser.
 
-  nsCOMPtr<nsIDOMDocument> doc;
-  if (aDocument) {
-    doc = do_QueryInterface(aDocument);
+  nsCOMPtr<nsISupports> doc;
+  if (aDocumentish) {
+    doc = aDocumentish;
   } else {
-    GetDocument(getter_AddRefs(doc));
+    nsCOMPtr<nsIDOMDocument> domDoc;
+    GetDocument(getter_AddRefs(domDoc));
+    doc = domDoc.forget();
   }
   if (!doc) {
     return NS_ERROR_FAILURE;
   }
 
   // Create a throwaway persistence object to do the work
   nsresult rv;
   mPersist = do_CreateInstance(NS_WEBBROWSERPERSIST_CONTRACTID, &rv);
new file mode 100644
--- /dev/null
+++ b/embedding/components/webbrowserpersist/PWebBrowserPersistDocument.ipdl
@@ -0,0 +1,90 @@
+/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBrowser;
+include protocol PWebBrowserPersistResources;
+include protocol PWebBrowserPersistSerialize;
+
+include InputStreamParams;
+
+namespace mozilla {
+
+// nsIWebBrowserPersistDocument has attributes which can be read
+// synchronously.  To avoid using sync IPC for them, the actor sends
+// this structure from the child to the parent before the parent actor
+// is exposed to XPCOM.
+struct WebBrowserPersistDocumentAttrs {
+  bool isPrivate;
+  nsCString documentURI;
+  nsCString baseURI;
+  nsCString contentType;
+  nsCString characterSet;
+  nsString title;
+  nsString referrer;
+  nsString contentDisposition;
+  uint32_t cacheKey;
+  uint32_t persistFlags;
+};
+
+// IPDL doesn't have tuples, so this gives the pair of strings from
+// nsIWebBrowserPersistURIMap::getURIMapping a name.
+struct WebBrowserPersistURIMapEntry {
+  nsCString mapFrom;
+  nsCString mapTo;
+};
+
+// nsIWebBrowserPersistURIMap is just copied over IPC as one of these,
+// not proxied, to simplify the protocol.
+struct WebBrowserPersistURIMap {
+  WebBrowserPersistURIMapEntry[] mapURIs;
+  nsCString targetBaseURI;
+};
+
+// This remotes nsIWebBrowserPersistDocument and its visitors.  The
+// lifecycle is a little complicated: the initial document is
+// constructed parent->child, but subdocuments are constructed
+// child->parent and then passed back.  Subdocuments aren't subactors,
+// because that would impose a lifetime relationship that doesn't
+// exist in the XPIDL; instead they're all managed by the enclosing
+// PBrowser (== TabParent/TabChild).
+protocol PWebBrowserPersistDocument {
+  manager PBrowser;
+  manages PWebBrowserPersistResources;
+  manages PWebBrowserPersistSerialize;
+
+parent:
+  // The actor isn't exposed to XPCOM until after it gets one of these
+  // two messages; see also the state transition rules.  The message
+  // is either a response to the constructor (if it was parent->child)
+  // or sent after it (if it was child->parent).
+  Attributes(WebBrowserPersistDocumentAttrs aAttrs,
+             OptionalInputStreamParams postData,
+             FileDescriptor[] postFiles);
+  InitFailure(nsresult aStatus);
+
+child:
+  SetPersistFlags(uint32_t aNewFlags);
+  PWebBrowserPersistResources();
+  PWebBrowserPersistSerialize(WebBrowserPersistURIMap aMap,
+                              nsCString aRequestedContentType,
+                              uint32_t aEncoderFlags,
+                              uint32_t aWrapColumn);
+  __delete__();
+
+state START:
+  recv Attributes goto MAIN;
+  recv InitFailure goto FAILED;
+
+state MAIN:
+  send SetPersistFlags goto MAIN;
+  send PWebBrowserPersistResources goto MAIN;
+  send PWebBrowserPersistSerialize goto MAIN;
+  send __delete__;
+
+state FAILED:
+  send __delete__;
+};
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/embedding/components/webbrowserpersist/PWebBrowserPersistResources.ipdl
@@ -0,0 +1,26 @@
+/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PWebBrowserPersistDocument;
+
+namespace mozilla {
+
+// == nsIWebBrowserPersistResourceVisitor
+protocol PWebBrowserPersistResources {
+  manager PWebBrowserPersistDocument;
+
+parent:
+  VisitResource(nsCString aURI);
+
+  // The actor sent here is in the START state; the parent-side
+  // receiver will have to wait for it to enter the MAIN state
+  // before exposing it with a visitDocument call.
+  VisitDocument(PWebBrowserPersistDocument aSubDocument);
+
+  // This reflects the endVisit method.
+  __delete__(nsresult aStatus);
+};
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/embedding/components/webbrowserpersist/PWebBrowserPersistSerialize.ipdl
@@ -0,0 +1,29 @@
+/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PWebBrowserPersistDocument;
+
+namespace mozilla {
+
+// This actor represents both an nsIWebBrowserPersistWriteCompletion
+// and the nsIOutputStream passed with it to the writeContent method.
+protocol PWebBrowserPersistSerialize {
+  manager PWebBrowserPersistDocument;
+
+parent:
+  // This sends the data with no flow control, so the parent could
+  // wind up buffering an arbitrarily large amount of data...  but
+  // it's a serialized DOM that's already in memory as DOM nodes, so
+  // this is at worst just a constant-factor increase in memory usage.
+  // Also, Chromium does the same thing; see
+  // content::RenderViewImpl::didSerializeDataForFrame.
+  WriteData(uint8_t[] aData);
+
+  // This is the onFinish method.
+  __delete__(nsCString aContentType,
+             nsresult aStatus);
+};
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/embedding/components/webbrowserpersist/WebBrowserPersistDocumentChild.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "WebBrowserPersistDocumentChild.h"
+
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "nsIDocument.h"
+#include "nsIInputStream.h"
+#include "WebBrowserPersistLocalDocument.h"
+#include "WebBrowserPersistResourcesChild.h"
+#include "WebBrowserPersistSerializeChild.h"
+
+namespace mozilla {
+
+WebBrowserPersistDocumentChild::WebBrowserPersistDocumentChild()
+{
+}
+
+WebBrowserPersistDocumentChild::~WebBrowserPersistDocumentChild()
+{
+}
+
+void
+WebBrowserPersistDocumentChild::Start(nsIDocument* aDocument)
+{
+    nsRefPtr<WebBrowserPersistLocalDocument> doc;
+    if (aDocument) {
+        doc = new WebBrowserPersistLocalDocument(aDocument);
+    }
+    Start(doc);
+}
+
+void
+WebBrowserPersistDocumentChild::Start(nsIWebBrowserPersistDocument* aDocument)
+{
+    MOZ_ASSERT(!mDocument);
+    if (!aDocument) {
+        SendInitFailure(NS_ERROR_FAILURE);
+        return;
+    }
+
+    WebBrowserPersistDocumentAttrs attrs;
+    nsCOMPtr<nsIInputStream> postDataStream;
+    OptionalInputStreamParams postData;
+    nsTArray<FileDescriptor> postFiles;
+#define ENSURE(e) do {           \
+        nsresult rv = (e);       \
+        if (NS_FAILED(rv)) {     \
+            SendInitFailure(rv); \
+            return;              \
+        }                        \
+    } while(0)
+    ENSURE(aDocument->GetIsPrivate(&(attrs.isPrivate())));
+    ENSURE(aDocument->GetDocumentURI(attrs.documentURI()));
+    ENSURE(aDocument->GetBaseURI(attrs.baseURI()));
+    ENSURE(aDocument->GetContentType(attrs.contentType()));
+    ENSURE(aDocument->GetCharacterSet(attrs.characterSet()));
+    ENSURE(aDocument->GetTitle(attrs.title()));
+    ENSURE(aDocument->GetReferrer(attrs.referrer()));
+    ENSURE(aDocument->GetContentDisposition(attrs.contentDisposition()));
+    ENSURE(aDocument->GetCacheKey(&(attrs.cacheKey())));
+    ENSURE(aDocument->GetPersistFlags(&(attrs.persistFlags())));
+    ENSURE(aDocument->GetPostData(getter_AddRefs(postDataStream)));
+    ipc::SerializeInputStream(postDataStream,
+                              postData,
+                              postFiles);
+#undef ENSURE
+    mDocument = aDocument;
+    SendAttributes(attrs, postData, postFiles);
+}
+
+bool
+WebBrowserPersistDocumentChild::RecvSetPersistFlags(const uint32_t& aNewFlags)
+{
+    mDocument->SetPersistFlags(aNewFlags);
+    return true;
+}
+
+PWebBrowserPersistResourcesChild*
+WebBrowserPersistDocumentChild::AllocPWebBrowserPersistResourcesChild()
+{
+    auto* actor = new WebBrowserPersistResourcesChild();
+    NS_ADDREF(actor);
+    return actor;
+}
+
+bool
+WebBrowserPersistDocumentChild::RecvPWebBrowserPersistResourcesConstructor(PWebBrowserPersistResourcesChild* aActor)
+{
+    nsRefPtr<WebBrowserPersistResourcesChild> visitor =
+        static_cast<WebBrowserPersistResourcesChild*>(aActor);
+    nsresult rv = mDocument->ReadResources(visitor);
+    if (NS_FAILED(rv)) {
+        visitor->EndVisit(mDocument, rv);
+    }
+    return true;
+}
+
+bool
+WebBrowserPersistDocumentChild::DeallocPWebBrowserPersistResourcesChild(PWebBrowserPersistResourcesChild* aActor)
+{
+    auto* castActor =
+        static_cast<WebBrowserPersistResourcesChild*>(aActor);
+    NS_RELEASE(castActor);
+    return true;
+}
+
+PWebBrowserPersistSerializeChild*
+WebBrowserPersistDocumentChild::AllocPWebBrowserPersistSerializeChild(
+            const WebBrowserPersistURIMap& aMap,
+            const nsCString& aRequestedContentType,
+            const uint32_t& aEncoderFlags,
+            const uint32_t& aWrapColumn)
+{
+    auto* actor = new WebBrowserPersistSerializeChild(aMap);
+    NS_ADDREF(actor);
+    return actor;
+}
+
+bool
+WebBrowserPersistDocumentChild::RecvPWebBrowserPersistSerializeConstructor(
+            PWebBrowserPersistSerializeChild* aActor,
+            const WebBrowserPersistURIMap& aMap,
+            const nsCString& aRequestedContentType,
+            const uint32_t& aEncoderFlags,
+            const uint32_t& aWrapColumn)
+{
+    auto* castActor =
+        static_cast<WebBrowserPersistSerializeChild*>(aActor);
+    // This actor performs the roles of: completion, URI map, and output stream.
+    nsresult rv = mDocument->WriteContent(castActor,
+                                          castActor,
+                                          aRequestedContentType,
+                                          aEncoderFlags,
+                                          aWrapColumn,
+                                          castActor);
+    if (NS_FAILED(rv)) {
+        castActor->OnFinish(mDocument, castActor, aRequestedContentType, rv);
+    }
+    return true;
+}
+
+bool
+WebBrowserPersistDocumentChild::DeallocPWebBrowserPersistSerializeChild(PWebBrowserPersistSerializeChild* aActor)
+{
+    auto* castActor =
+        static_cast<WebBrowserPersistSerializeChild*>(aActor);
+    NS_RELEASE(castActor);
+    return true;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/embedding/components/webbrowserpersist/WebBrowserPersistDocumentChild.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WebBrowserPersistDocumentChild_h__
+#define WebBrowserPersistDocumentChild_h__
+
+#include "mozilla/PWebBrowserPersistDocumentChild.h"
+#include "nsCOMPtr.h"
+#include "nsIWebBrowserPersistDocument.h"
+
+class nsIDocument;
+
+namespace mozilla {
+
+class WebBrowserPersistDocumentChild final
+    : public PWebBrowserPersistDocumentChild
+{
+public:
+    WebBrowserPersistDocumentChild();
+    ~WebBrowserPersistDocumentChild();
+
+    // This sends either Attributes or InitFailure and thereby causes
+    // the actor to leave the START state.
+    void Start(nsIWebBrowserPersistDocument* aDocument);
+    void Start(nsIDocument* aDocument);
+
+    virtual bool
+    RecvSetPersistFlags(const uint32_t& aNewFlags) override;
+
+    virtual PWebBrowserPersistResourcesChild*
+    AllocPWebBrowserPersistResourcesChild() override;
+    virtual bool
+    RecvPWebBrowserPersistResourcesConstructor(PWebBrowserPersistResourcesChild* aActor) override;
+    virtual bool
+    DeallocPWebBrowserPersistResourcesChild(PWebBrowserPersistResourcesChild* aActor) override;
+
+    virtual PWebBrowserPersistSerializeChild*
+    AllocPWebBrowserPersistSerializeChild(
+            const WebBrowserPersistURIMap& aMap,
+            const nsCString& aRequestedContentType,
+            const uint32_t& aEncoderFlags,
+            const uint32_t& aWrapColumn) override;
+    virtual bool
+    RecvPWebBrowserPersistSerializeConstructor(
+            PWebBrowserPersistSerializeChild* aActor,
+            const WebBrowserPersistURIMap& aMap,
+            const nsCString& aRequestedContentType,
+            const uint32_t& aEncoderFlags,
+            const uint32_t& aWrapColumn) override;
+    virtual bool
+    DeallocPWebBrowserPersistSerializeChild(PWebBrowserPersistSerializeChild* aActor) override;
+
+private:
+    nsCOMPtr<nsIWebBrowserPersistDocument> mDocument;
+};
+
+} // namespace mozilla
+
+#endif // WebBrowserPersistDocumentChild_h__
new file mode 100644
--- /dev/null
+++ b/embedding/components/webbrowserpersist/WebBrowserPersistDocumentParent.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "WebBrowserPersistDocumentParent.h"
+
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "nsIInputStream.h"
+#include "nsThreadUtils.h"
+#include "WebBrowserPersistResourcesParent.h"
+#include "WebBrowserPersistSerializeParent.h"
+#include "WebBrowserPersistRemoteDocument.h"
+
+namespace mozilla {
+
+WebBrowserPersistDocumentParent::WebBrowserPersistDocumentParent()
+: mReflection(nullptr)
+{
+}
+
+void
+WebBrowserPersistDocumentParent::SetOnReady(nsIWebBrowserPersistDocumentReceiver* aOnReady)
+{
+    MOZ_ASSERT(aOnReady);
+    MOZ_ASSERT(!mOnReady);
+    MOZ_ASSERT(!mReflection);
+    mOnReady = aOnReady;
+}
+
+void
+WebBrowserPersistDocumentParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+    if (mReflection) {
+        mReflection->ActorDestroy();
+        mReflection = nullptr;
+    }
+    if (mOnReady) {
+        mOnReady->OnError(NS_ERROR_FAILURE);
+        mOnReady = nullptr;
+    }
+}
+
+WebBrowserPersistDocumentParent::~WebBrowserPersistDocumentParent()
+{
+    MOZ_RELEASE_ASSERT(!mReflection);
+    MOZ_ASSERT(!mOnReady);
+}
+
+bool
+WebBrowserPersistDocumentParent::RecvAttributes(const Attrs& aAttrs,
+                                                const OptionalInputStreamParams& aPostData,
+                                                nsTArray<FileDescriptor>&& aPostFiles)
+{
+    // Deserialize the postData unconditionally so that fds aren't leaked.
+    nsCOMPtr<nsIInputStream> postData =
+        ipc::DeserializeInputStream(aPostData, aPostFiles);
+    if (!mOnReady || mReflection) {
+        return false;
+    }
+    mReflection = new WebBrowserPersistRemoteDocument(this, aAttrs, postData);
+    nsRefPtr<WebBrowserPersistRemoteDocument> reflection = mReflection;
+    mOnReady->OnDocumentReady(reflection);
+    mOnReady = nullptr;
+    return true;
+}
+
+bool
+WebBrowserPersistDocumentParent::RecvInitFailure(const nsresult& aFailure)
+{
+    if (!mOnReady || mReflection) {
+        return false;
+    }
+    mOnReady->OnError(aFailure);
+    mOnReady = nullptr;
+    // Warning: Send__delete__ deallocates this object.
+    return Send__delete__(this);
+}
+
+PWebBrowserPersistResourcesParent*
+WebBrowserPersistDocumentParent::AllocPWebBrowserPersistResourcesParent()
+{
+    MOZ_CRASH("Don't use this; construct the actor directly and AddRef.");
+    return nullptr;
+}
+
+bool
+WebBrowserPersistDocumentParent::DeallocPWebBrowserPersistResourcesParent(PWebBrowserPersistResourcesParent* aActor)
+{
+    // Turn the ref held by IPC back into an nsRefPtr.
+    nsRefPtr<WebBrowserPersistResourcesParent> actor =
+        already_AddRefed<WebBrowserPersistResourcesParent>(
+            static_cast<WebBrowserPersistResourcesParent*>(aActor));
+    return true;
+}
+
+PWebBrowserPersistSerializeParent*
+WebBrowserPersistDocumentParent::AllocPWebBrowserPersistSerializeParent(
+        const WebBrowserPersistURIMap& aMap,
+        const nsCString& aRequestedContentType,
+        const uint32_t& aEncoderFlags,
+        const uint32_t& aWrapColumn)
+{
+    MOZ_CRASH("Don't use this; construct the actor directly.");
+    return nullptr;
+}
+
+bool
+WebBrowserPersistDocumentParent::DeallocPWebBrowserPersistSerializeParent(PWebBrowserPersistSerializeParent* aActor)
+{
+    delete aActor;
+    return true;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/embedding/components/webbrowserpersist/WebBrowserPersistDocumentParent.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WebBrowserPersistDocumentParent_h__
+#define WebBrowserPersistDocumentParent_h__
+
+#include "mozilla/Maybe.h"
+#include "mozilla/PWebBrowserPersistDocumentParent.h"
+#include "nsCOMPtr.h"
+#include "nsIWebBrowserPersistDocument.h"
+
+// This class is the IPC half of the glue between the
+// nsIWebBrowserPersistDocument interface and a remote document.  When
+// (and if) it receives the Attributes message it constructs an
+// WebBrowserPersistRemoteDocument and releases it into the XPCOM
+// universe; otherwise, it invokes the document receiver's error
+// callback.
+//
+// This object's lifetime is the normal IPC lifetime; on destruction,
+// it calls its XPCOM reflection (if it exists yet) to remove that
+// reference.  Normal deletion occurs when the XPCOM object is being
+// destroyed or after an InitFailure is received and handled.
+//
+// See also: TabParent::StartPersistence.
+
+namespace mozilla {
+
+class WebBrowserPersistRemoteDocument;
+
+class WebBrowserPersistDocumentParent final
+    : public PWebBrowserPersistDocumentParent
+{
+public:
+    WebBrowserPersistDocumentParent();
+    virtual ~WebBrowserPersistDocumentParent();
+
+    // Set a callback to be invoked when the actor leaves the START
+    // state.  This method must be called exactly once while the actor
+    // is still in the START state (or is unconstructed).
+    void SetOnReady(nsIWebBrowserPersistDocumentReceiver* aOnReady);
+
+    using Attrs = WebBrowserPersistDocumentAttrs;
+
+    // IPDL methods:
+    virtual bool
+    RecvAttributes(const Attrs& aAttrs,
+                   const OptionalInputStreamParams& aPostData,
+                   nsTArray<FileDescriptor>&& aPostFiles) override;
+    virtual bool
+    RecvInitFailure(const nsresult& aFailure) override;
+
+    virtual PWebBrowserPersistResourcesParent*
+    AllocPWebBrowserPersistResourcesParent() override;
+    virtual bool
+    DeallocPWebBrowserPersistResourcesParent(PWebBrowserPersistResourcesParent* aActor) override;
+
+    virtual PWebBrowserPersistSerializeParent*
+    AllocPWebBrowserPersistSerializeParent(
+            const WebBrowserPersistURIMap& aMap,
+            const nsCString& aRequestedContentType,
+            const uint32_t& aEncoderFlags,
+            const uint32_t& aWrapColumn) override;
+    virtual bool
+    DeallocPWebBrowserPersistSerializeParent(PWebBrowserPersistSerializeParent* aActor) override;
+
+    virtual void
+    ActorDestroy(ActorDestroyReason aWhy) override;
+private:
+    // This is reset to nullptr when the callback is invoked.
+    nsCOMPtr<nsIWebBrowserPersistDocumentReceiver> mOnReady;
+    WebBrowserPersistRemoteDocument* mReflection;
+};
+
+} // namespace mozilla
+
+#endif // WebBrowserPersistDocumentParent_h__
new file mode 100644
--- /dev/null
+++ b/embedding/components/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
@@ -0,0 +1,1451 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "WebBrowserPersistLocalDocument.h"
+#include "WebBrowserPersistDocumentParent.h"
+
+#include "mozilla/dom/HTMLInputElement.h"
+#include "mozilla/dom/HTMLSharedElement.h"
+#include "mozilla/dom/HTMLSharedObjectElement.h"
+#include "mozilla/dom/TabParent.h"
+#include "nsComponentManagerUtils.h"
+#include "nsContentUtils.h"
+#include "nsContentCID.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsFrameLoader.h"
+#include "nsIComponentRegistrar.h"
+#include "nsIContent.h"
+#include "nsIDOMAttr.h"
+#include "nsIDOMComment.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMHTMLAnchorElement.h"
+#include "nsIDOMHTMLAppletElement.h"
+#include "nsIDOMHTMLAreaElement.h"
+#include "nsIDOMHTMLBaseElement.h"
+#include "nsIDOMHTMLCollection.h"
+#include "nsIDOMHTMLDocument.h"
+#include "nsIDOMHTMLEmbedElement.h"
+#include "nsIDOMHTMLFrameElement.h"
+#include "nsIDOMHTMLIFrameElement.h"
+#include "nsIDOMHTMLImageElement.h"
+#include "nsIDOMHTMLInputElement.h"
+#include "nsIDOMHTMLLinkElement.h"
+#include "nsIDOMHTMLMediaElement.h"
+#include "nsIDOMHTMLObjectElement.h"
+#include "nsIDOMHTMLOptionElement.h"
+#include "nsIDOMHTMLScriptElement.h"
+#include "nsIDOMHTMLSourceElement.h"
+#include "nsIDOMHTMLTextAreaElement.h"
+#include "nsIDOMMozNamedAttrMap.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMNodeFilter.h"
+#include "nsIDOMNodeList.h"
+#include "nsIDOMProcessingInstruction.h"
+#include "nsIDOMTreeWalker.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsIDocShell.h"
+#include "nsIDocument.h"
+#include "nsIDocumentEncoder.h"
+#include "nsILoadContext.h"
+#include "nsIProtocolHandler.h"
+#include "nsISHEntry.h"
+#include "nsISupportsPrimitives.h"
+#include "nsITabParent.h"
+#include "nsIWebBrowserPersist.h"
+#include "nsIWebNavigation.h"
+#include "nsIWebPageDescriptor.h"
+#include "nsNetUtil.h"
+
+namespace mozilla {
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WebBrowserPersistLocalDocument)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WebBrowserPersistLocalDocument)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebBrowserPersistLocalDocument)
+  NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistDocument)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION(WebBrowserPersistLocalDocument, mDocument)
+
+
+WebBrowserPersistLocalDocument::WebBrowserPersistLocalDocument(nsIDocument* aDocument)
+: mDocument(aDocument)
+, mPersistFlags(0)
+{
+    MOZ_ASSERT(mDocument);
+}
+
+WebBrowserPersistLocalDocument::~WebBrowserPersistLocalDocument()
+{
+}
+
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::SetPersistFlags(uint32_t aFlags)
+{
+    mPersistFlags = aFlags;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetPersistFlags(uint32_t* aFlags)
+{
+    *aFlags = mPersistFlags;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetIsPrivate(bool* aIsPrivate)
+{
+    nsCOMPtr<nsILoadContext> privacyContext = mDocument->GetLoadContext();
+    *aIsPrivate = privacyContext && privacyContext->UsePrivateBrowsing();
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetDocumentURI(nsACString& aURISpec)
+{
+    nsCOMPtr<nsIURI> uri = mDocument->GetDocumentURI();
+    if (!uri) {
+        return NS_ERROR_UNEXPECTED;
+    }
+    return uri->GetSpec(aURISpec);
+}
+
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetBaseURI(nsACString& aURISpec)
+{
+    nsCOMPtr<nsIURI> uri = GetBaseURI();
+    if (!uri) {
+        return NS_ERROR_UNEXPECTED;
+    }
+    return uri->GetSpec(aURISpec);
+}
+
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetContentType(nsACString& aContentType)
+{
+    nsAutoString utf16Type;
+    nsresult rv;
+
+    rv = mDocument->GetContentType(utf16Type);
+    NS_ENSURE_SUCCESS(rv, rv);
+    aContentType = NS_ConvertUTF16toUTF8(utf16Type);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetCharacterSet(nsACString& aCharSet)
+{
+    aCharSet = GetCharacterSet();
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetTitle(nsAString& aTitle)
+{
+    nsAutoString titleBuffer;
+    mDocument->GetTitle(titleBuffer);
+    aTitle = titleBuffer;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetReferrer(nsAString& aReferrer)
+{
+    mDocument->GetReferrer(aReferrer);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetContentDisposition(nsAString& aCD)
+{
+    nsCOMPtr<nsIDOMWindow> window = mDocument->GetDefaultView();
+    NS_ENSURE_STATE(window);
+    nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
+    NS_ENSURE_STATE(utils);
+    return utils->GetDocumentMetadata(
+        NS_LITERAL_STRING("content-disposition"), aCD);
+}
+
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetCacheKey(uint32_t* aKey)
+{
+    nsCOMPtr<nsISHEntry> history;
+    nsresult rv = GetHistory(getter_AddRefs(history));
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ENSURE_STATE(history);
+    nsCOMPtr<nsISupports> abstractKey;
+    rv = history->GetCacheKey(getter_AddRefs(abstractKey));
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (!abstractKey) {
+        *aKey = 0;
+        return NS_OK;
+    }
+    nsCOMPtr<nsISupportsPRUint32> u32 = do_QueryInterface(abstractKey);
+    if (NS_WARN_IF(!u32)) {
+        *aKey = 0;
+        return NS_OK;
+    }
+    return u32->GetData(aKey);
+}
+
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetPostData(nsIInputStream** aStream)
+{
+    nsCOMPtr<nsISHEntry> history;
+    nsresult rv = GetHistory(getter_AddRefs(history));
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ENSURE_STATE(history);
+    return history->GetPostData(aStream);
+}
+
+nsresult
+WebBrowserPersistLocalDocument::GetHistory(nsISHEntry** aHistory)
+{
+    nsCOMPtr<nsIDOMWindow> window = mDocument->GetDefaultView();
+    NS_ENSURE_STATE(window);
+    nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
+    NS_ENSURE_STATE(webNav);
+    nsCOMPtr<nsIWebPageDescriptor> desc = do_QueryInterface(webNav);
+    NS_ENSURE_STATE(desc);
+    nsCOMPtr<nsISupports> curDesc;
+    nsresult rv = desc->GetCurrentDescriptor(getter_AddRefs(curDesc));
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ENSURE_STATE(curDesc);
+    nsCOMPtr<nsISHEntry> history = do_QueryInterface(curDesc);
+    history.forget(aHistory);
+    return NS_OK;
+}
+
+const nsCString&
+WebBrowserPersistLocalDocument::GetCharacterSet() const
+{
+    return mDocument->GetDocumentCharacterSet();
+}
+
+uint32_t
+WebBrowserPersistLocalDocument::GetPersistFlags() const
+{
+    return mPersistFlags;
+}
+
+
+already_AddRefed<nsIURI>
+WebBrowserPersistLocalDocument::GetBaseURI() const
+{
+    return mDocument->GetBaseURI();
+}
+
+
+namespace {
+
+// Helper class for ReadResources().
+class ResourceReader final : public nsIWebBrowserPersistDocumentReceiver {
+public:
+    ResourceReader(WebBrowserPersistLocalDocument* aParent,
+                   nsIWebBrowserPersistResourceVisitor* aVisitor);
+    nsresult OnWalkDOMNode(nsIDOMNode* aNode);
+
+    // This is called both to indicate the end of the document walk
+    // and when a subdocument is (maybe asynchronously) sent to the
+    // visitor.  The call to EndVisit needs to happen after both of
+    // those have finished.
+    void DocumentDone(nsresult aStatus);
+
+    NS_DECL_NSIWEBBROWSERPERSISTDOCUMENTRECEIVER
+    NS_DECL_ISUPPORTS
+
+private:
+    nsRefPtr<WebBrowserPersistLocalDocument> mParent;
+    nsCOMPtr<nsIWebBrowserPersistResourceVisitor> mVisitor;
+    nsCOMPtr<nsIURI> mCurrentBaseURI;
+    uint32_t mPersistFlags;
+
+    // The number of DocumentDone calls after which EndVisit will be
+    // called on the visitor.  Counts the main document if it's still
+    // being walked and any outstanding asynchronous subdocument
+    // StartPersistence calls.
+    size_t mOutstandingDocuments;
+    // Collects the status parameters to DocumentDone calls.
+    nsresult mEndStatus;
+
+    nsresult OnWalkURI(const nsACString& aURISpec);
+    nsresult OnWalkURI(nsIURI* aURI);
+    nsresult OnWalkAttribute(nsIDOMNode* aNode,
+                             const char* aAttribute,
+                             const char* aNamespaceURI = "");
+    nsresult OnWalkSubframe(nsIDOMNode* aNode);
+
+    bool IsFlagSet(uint32_t aFlag) const {
+        return mParent->GetPersistFlags() & aFlag;
+    }
+
+    ~ResourceReader();
+
+    using IWBP = nsIWebBrowserPersist;
+};
+
+NS_IMPL_ISUPPORTS(ResourceReader, nsIWebBrowserPersistDocumentReceiver)
+
+ResourceReader::ResourceReader(WebBrowserPersistLocalDocument* aParent,
+                               nsIWebBrowserPersistResourceVisitor* aVisitor)
+: mParent(aParent)
+, mVisitor(aVisitor)
+, mCurrentBaseURI(aParent->GetBaseURI())
+, mPersistFlags(aParent->GetPersistFlags())
+, mOutstandingDocuments(1)
+, mEndStatus(NS_OK)
+{
+    MOZ_ASSERT(mCurrentBaseURI);
+}
+
+ResourceReader::~ResourceReader()
+{
+    MOZ_ASSERT(mOutstandingDocuments == 0);
+}
+
+void
+ResourceReader::DocumentDone(nsresult aStatus)
+{
+    MOZ_ASSERT(mOutstandingDocuments > 0);
+    if (NS_SUCCEEDED(mEndStatus)) {
+        mEndStatus = aStatus;
+    }
+    if (--mOutstandingDocuments == 0) {
+        mVisitor->EndVisit(mParent, mEndStatus);
+    }
+}
+
+nsresult
+ResourceReader::OnWalkSubframe(nsIDOMNode* aNode)
+{
+    nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(aNode);
+    NS_ENSURE_STATE(loaderOwner);
+    nsRefPtr<nsFrameLoader> loader = loaderOwner->GetFrameLoader();
+    NS_ENSURE_STATE(loader);
+
+    ++mOutstandingDocuments;
+    nsresult rv = loader->StartPersistence(this);
+    if (NS_FAILED(rv)) {
+        if (rv == NS_ERROR_NO_CONTENT) {
+            // Just ignore frames with no content document.
+            rv = NS_OK;
+        }
+        // StartPersistence won't eventually call this if it failed,
+        // so this does so (to keep mOutstandingDocuments correct).
+        DocumentDone(rv);
+    }
+    return rv;
+}
+
+NS_IMETHODIMP
+ResourceReader::OnDocumentReady(nsIWebBrowserPersistDocument* aDocument)
+{
+    mVisitor->VisitDocument(mParent, aDocument);
+    DocumentDone(NS_OK);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+ResourceReader::OnError(nsresult aFailure)
+{
+    DocumentDone(aFailure);
+    return NS_OK;
+}
+
+nsresult
+ResourceReader::OnWalkURI(nsIURI* aURI)
+{
+    // Test if this URI should be persisted. By default
+    // we should assume the URI  is persistable.
+    bool doNotPersistURI;
+    nsresult rv = NS_URIChainHasFlags(aURI,
+                                      nsIProtocolHandler::URI_NON_PERSISTABLE,
+                                      &doNotPersistURI);
+    if (NS_SUCCEEDED(rv) && doNotPersistURI) {
+        return NS_OK;
+    }
+
+    nsAutoCString stringURI;
+    rv = aURI->GetSpec(stringURI);
+    NS_ENSURE_SUCCESS(rv, rv);
+    return mVisitor->VisitResource(mParent, stringURI);
+}
+
+nsresult
+ResourceReader::OnWalkURI(const nsACString& aURISpec)
+{
+    nsresult rv;
+    nsCOMPtr<nsIURI> uri;
+
+    rv = NS_NewURI(getter_AddRefs(uri),
+                   aURISpec,
+                   mParent->GetCharacterSet().get(),
+                   mCurrentBaseURI);
+    NS_ENSURE_SUCCESS(rv, rv);
+    return OnWalkURI(uri);
+}
+
+static nsresult
+ExtractAttribute(nsIDOMNode* aNode,
+                 const char* aAttribute,
+                 const char* aNamespaceURI,
+                 nsCString&  aValue)
+{
+    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
+    MOZ_ASSERT(element);
+
+    // Find the named URI attribute on the (element) node and store
+    // a reference to the URI that maps onto a local file name
+
+    nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
+    nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+    NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI);
+    NS_ConvertASCIItoUTF16 attribute(aAttribute);
+    nsCOMPtr<nsIDOMAttr> attr;
+    rv = attrMap->GetNamedItemNS(namespaceURI, attribute, getter_AddRefs(attr));
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (attr) {
+        nsAutoString value;
+        rv = attr->GetValue(value);
+        NS_ENSURE_SUCCESS(rv, rv);
+        aValue = NS_ConvertUTF16toUTF8(value);
+    } else {
+        aValue.Truncate();
+    }
+    return NS_OK;
+}
+
+nsresult
+ResourceReader::OnWalkAttribute(nsIDOMNode* aNode,
+                                const char* aAttribute,
+                                const char* aNamespaceURI)
+{
+    nsAutoCString uriSpec;
+    nsresult rv = ExtractAttribute(aNode, aAttribute, aNamespaceURI, uriSpec);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (uriSpec.IsEmpty()) {
+        return NS_OK;
+    }
+    return OnWalkURI(uriSpec);
+}
+
+static nsresult
+GetXMLStyleSheetLink(nsIDOMProcessingInstruction *aPI, nsAString &aHref)
+{
+    nsresult rv;
+    nsAutoString data;
+    rv = aPI->GetData(data);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+    nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::href, aHref);
+    return NS_OK;
+}
+
+nsresult
+ResourceReader::OnWalkDOMNode(nsIDOMNode* aNode)
+{
+    nsresult rv;
+
+    // Fixup xml-stylesheet processing instructions
+    nsCOMPtr<nsIDOMProcessingInstruction> nodeAsPI = do_QueryInterface(aNode);
+    if (nodeAsPI) {
+        nsAutoString target;
+        rv = nodeAsPI->GetTarget(target);
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (target.EqualsLiteral("xml-stylesheet")) {
+            nsAutoString href;
+            GetXMLStyleSheetLink(nodeAsPI, href);
+            if (!href.IsEmpty()) {
+                return OnWalkURI(NS_ConvertUTF16toUTF8(href));
+            }
+        }
+        return NS_OK;
+    }
+
+    nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+    if (!content) {
+        return NS_OK;
+    }
+
+    // Test the node to see if it's an image, frame, iframe, css, js
+    nsCOMPtr<nsIDOMHTMLImageElement> nodeAsImage = do_QueryInterface(aNode);
+    if (nodeAsImage) {
+        return OnWalkAttribute(aNode, "src");
+    }
+
+    if (content->IsSVGElement(nsGkAtoms::img)) {
+        return OnWalkAttribute(aNode, "href", "http://www.w3.org/1999/xlink");
+    }
+
+    nsCOMPtr<nsIDOMHTMLMediaElement> nodeAsMedia = do_QueryInterface(aNode);
+    if (nodeAsMedia) {
+        return OnWalkAttribute(aNode, "src");
+    }
+    nsCOMPtr<nsIDOMHTMLSourceElement> nodeAsSource = do_QueryInterface(aNode);
+    if (nodeAsSource) {
+        return OnWalkAttribute(aNode, "src");
+    }
+
+    if (content->IsHTMLElement(nsGkAtoms::body)) {
+        return OnWalkAttribute(aNode, "background");
+    }
+
+    if (content->IsHTMLElement(nsGkAtoms::table)) {
+        return OnWalkAttribute(aNode, "background");
+    }
+
+    if (content->IsHTMLElement(nsGkAtoms::tr)) {
+        return OnWalkAttribute(aNode, "background");
+    }
+
+    if (content->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th)) {
+        return OnWalkAttribute(aNode, "background");
+    }
+
+    nsCOMPtr<nsIDOMHTMLScriptElement> nodeAsScript = do_QueryInterface(aNode);
+    if (nodeAsScript) {
+        return OnWalkAttribute(aNode, "src");
+    }
+
+    if (content->IsSVGElement(nsGkAtoms::script)) {
+        return OnWalkAttribute(aNode, "href", "http://www.w3.org/1999/xlink");
+    }
+
+    nsCOMPtr<nsIDOMHTMLEmbedElement> nodeAsEmbed = do_QueryInterface(aNode);
+    if (nodeAsEmbed) {
+        return OnWalkAttribute(aNode, "src");
+    }
+
+    nsCOMPtr<nsIDOMHTMLObjectElement> nodeAsObject = do_QueryInterface(aNode);
+    if (nodeAsObject) {
+        return OnWalkAttribute(aNode, "data");
+    }
+
+    nsCOMPtr<nsIDOMHTMLAppletElement> nodeAsApplet = do_QueryInterface(aNode);
+    if (nodeAsApplet) {
+        // For an applet, relative URIs are resolved relative to the
+        // codebase (which is resolved relative to the base URI).
+        nsCOMPtr<nsIURI> oldBase = mCurrentBaseURI;
+        nsAutoString codebase;
+        rv = nodeAsApplet->GetCodeBase(codebase);
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (!codebase.IsEmpty()) {
+            nsCOMPtr<nsIURI> baseURI;
+            rv = NS_NewURI(getter_AddRefs(baseURI), codebase,
+                           mParent->GetCharacterSet().get(), mCurrentBaseURI);
+            NS_ENSURE_SUCCESS(rv, rv);
+            if (baseURI) {
+                mCurrentBaseURI = baseURI;
+                // Must restore this before returning (or ENSURE'ing).
+            }
+        }
+
+        // We only store 'code' locally if there is no 'archive',
+        // otherwise we assume the archive file(s) contains it (bug 430283).
+        nsAutoCString archiveAttr;
+        rv = ExtractAttribute(aNode, "archive", "", archiveAttr);
+        if (NS_SUCCEEDED(rv)) {
+            if (!archiveAttr.IsEmpty()) {
+                rv = OnWalkURI(archiveAttr);
+            } else {
+                rv = OnWalkAttribute(aNode, "core");
+            }
+        }
+
+        // restore the base URI we really want to have
+        mCurrentBaseURI = oldBase;
+        return rv;
+    }
+
+    nsCOMPtr<nsIDOMHTMLLinkElement> nodeAsLink = do_QueryInterface(aNode);
+    if (nodeAsLink) {
+        // Test if the link has a rel value indicating it to be a stylesheet
+        nsAutoString linkRel;
+        if (NS_SUCCEEDED(nodeAsLink->GetRel(linkRel)) && !linkRel.IsEmpty()) {
+            nsReadingIterator<char16_t> start;
+            nsReadingIterator<char16_t> end;
+            nsReadingIterator<char16_t> current;
+
+            linkRel.BeginReading(start);
+            linkRel.EndReading(end);
+
+            // Walk through space delimited string looking for "stylesheet"
+            for (current = start; current != end; ++current) {
+                // Ignore whitespace
+                if (nsCRT::IsAsciiSpace(*current)) {
+                    continue;
+                }
+
+                // Grab the next space delimited word
+                nsReadingIterator<char16_t> startWord = current;
+                do {
+                    ++current;
+                } while (current != end && !nsCRT::IsAsciiSpace(*current));
+
+                // Store the link for fix up if it says "stylesheet"
+                if (Substring(startWord, current)
+                        .LowerCaseEqualsLiteral("stylesheet")) {
+                    OnWalkAttribute(aNode, "href");
+                    return NS_OK;
+                }
+                if (current == end) {
+                    break;
+                }
+            }
+        }
+        return NS_OK;
+    }
+
+    nsCOMPtr<nsIDOMHTMLFrameElement> nodeAsFrame = do_QueryInterface(aNode);
+    if (nodeAsFrame) {
+        return OnWalkSubframe(aNode);
+    }
+
+    nsCOMPtr<nsIDOMHTMLIFrameElement> nodeAsIFrame = do_QueryInterface(aNode);
+    if (nodeAsIFrame && !(mPersistFlags &
+                          IWBP::PERSIST_FLAGS_IGNORE_IFRAMES)) {
+        return OnWalkSubframe(aNode);
+    }
+
+    nsCOMPtr<nsIDOMHTMLInputElement> nodeAsInput = do_QueryInterface(aNode);
+    if (nodeAsInput) {
+        return OnWalkAttribute(aNode, "src");
+    }
+
+    return NS_OK;
+}
+
+// Helper class for node rewriting in writeContent().
+class PersistNodeFixup final : public nsIDocumentEncoderNodeFixup {
+public:
+    PersistNodeFixup(WebBrowserPersistLocalDocument* aParent,
+                     nsIWebBrowserPersistURIMap* aMap,
+                     nsIURI* aTargetURI);
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIDOCUMENTENCODERNODEFIXUP
+private:
+    virtual ~PersistNodeFixup() { }
+    nsRefPtr<WebBrowserPersistLocalDocument> mParent;
+    nsClassHashtable<nsCStringHashKey, nsCString> mMap;
+    nsCOMPtr<nsIURI> mCurrentBaseURI;
+    nsCOMPtr<nsIURI> mTargetBaseURI;
+
+    bool IsFlagSet(uint32_t aFlag) const {
+        return mParent->GetPersistFlags() & aFlag;
+    }
+
+    nsresult GetNodeToFixup(nsIDOMNode* aNodeIn, nsIDOMNode** aNodeOut);
+    nsresult FixupURI(nsAString& aURI);
+    nsresult FixupAttribute(nsIDOMNode* aNode,
+                            const char* aAttribute,
+                            const char* aNamespaceURI = "");
+    nsresult FixupAnchor(nsIDOMNode* aNode);
+    nsresult FixupXMLStyleSheetLink(nsIDOMProcessingInstruction* aPI,
+                                    const nsAString& aHref);
+
+    using IWBP = nsIWebBrowserPersist;
+};
+
+NS_IMPL_ISUPPORTS(PersistNodeFixup, nsIDocumentEncoderNodeFixup)
+
+PersistNodeFixup::PersistNodeFixup(WebBrowserPersistLocalDocument* aParent,
+                                   nsIWebBrowserPersistURIMap* aMap,
+                                   nsIURI* aTargetURI)
+: mParent(aParent)
+, mCurrentBaseURI(aParent->GetBaseURI())
+, mTargetBaseURI(aTargetURI)
+{
+    if (aMap) {
+        uint32_t mapSize;
+        nsresult rv = aMap->GetNumMappedURIs(&mapSize);
+        MOZ_ASSERT(NS_SUCCEEDED(rv));
+        NS_ENSURE_SUCCESS_VOID(rv);
+        for (uint32_t i = 0; i < mapSize; ++i) {
+            nsAutoCString urlFrom;
+            nsCString* urlTo = new nsCString();
+
+            rv = aMap->GetURIMapping(i, urlFrom, *urlTo);
+            MOZ_ASSERT(NS_SUCCEEDED(rv));
+            if (NS_SUCCEEDED(rv)) {
+                mMap.Put(urlFrom, urlTo);
+            }
+        }
+    }
+}
+
+nsresult
+PersistNodeFixup::GetNodeToFixup(nsIDOMNode *aNodeIn, nsIDOMNode **aNodeOut)
+{
+    // Avoid mixups in FixupNode that could leak objects; this goes
+    // against the usual out parameter convention, but it's a private
+    // method so shouldn't be a problem.
+    MOZ_ASSERT(!*aNodeOut);
+
+    if (!IsFlagSet(IWBP::PERSIST_FLAGS_FIXUP_ORIGINAL_DOM)) {
+        nsresult rv = aNodeIn->CloneNode(false, 1, aNodeOut);
+        NS_ENSURE_SUCCESS(rv, rv);
+    } else {
+        NS_ADDREF(*aNodeOut = aNodeIn);
+    }
+    nsCOMPtr<nsIDOMHTMLElement> element(do_QueryInterface(*aNodeOut));
+    if (element) {
+        // Make sure this is not XHTML
+        nsAutoString namespaceURI;
+        element->GetNamespaceURI(namespaceURI);
+        if (namespaceURI.IsEmpty()) {
+            // This is a tag-soup node.  It may have a _base_href attribute
+            // stuck on it by the parser, but since we're fixing up all URIs
+            // relative to the overall document base that will screw us up.
+            // Just remove the _base_href.
+            element->RemoveAttribute(NS_LITERAL_STRING("_base_href"));
+        }
+    }
+    return NS_OK;
+}
+
+nsresult
+PersistNodeFixup::FixupURI(nsAString &aURI)
+{
+    // get the current location of the file (absolutized)
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI,
+                            mParent->GetCharacterSet().get(), mCurrentBaseURI);
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsAutoCString spec;
+    rv = uri->GetSpec(spec);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    const nsCString* replacement = mMap.Get(spec);
+    if (!replacement) {
+        // Note that most callers ignore this "failure".
+        return NS_ERROR_FAILURE;
+    }
+    if (!replacement->IsEmpty()) {
+        aURI = NS_ConvertUTF8toUTF16(*replacement);
+    }
+    return NS_OK;
+}
+
+nsresult
+PersistNodeFixup::FixupAttribute(nsIDOMNode* aNode,
+                                 const char* aAttribute,
+                                 const char* aNamespaceURI)
+{
+    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
+    MOZ_ASSERT(element);
+
+    nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
+    nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+    NS_ConvertASCIItoUTF16 attribute(aAttribute);
+    NS_ConvertASCIItoUTF16 namespaceURI(aNamespaceURI);
+    nsCOMPtr<nsIDOMAttr> attr;
+    rv = attrMap->GetNamedItemNS(namespaceURI, attribute, getter_AddRefs(attr));
+    if (attr) {
+        nsString uri;
+        attr->GetValue(uri);
+        rv = FixupURI(uri);
+        if (NS_SUCCEEDED(rv)) {
+            attr->SetValue(uri);
+        }
+    }
+
+    return rv;
+}
+
+nsresult
+PersistNodeFixup::FixupAnchor(nsIDOMNode *aNode)
+{
+    if (IsFlagSet(IWBP::PERSIST_FLAGS_DONT_FIXUP_LINKS)) {
+        return NS_OK;
+    }
+
+    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
+    MOZ_ASSERT(element);
+
+    nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
+    nsresult rv = element->GetAttributes(getter_AddRefs(attrMap));
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+    // Make all anchor links absolute so they point off onto the Internet
+    nsString attribute(NS_LITERAL_STRING("href"));
+    nsCOMPtr<nsIDOMAttr> attr;
+    rv = attrMap->GetNamedItem(attribute, getter_AddRefs(attr));
+    if (attr) {
+        nsString oldValue;
+        attr->GetValue(oldValue);
+        NS_ConvertUTF16toUTF8 oldCValue(oldValue);
+
+        // Skip empty values and self-referencing bookmarks
+        if (oldCValue.IsEmpty() || oldCValue.CharAt(0) == '#') {
+            return NS_OK;
+        }
+
+        // if saving file to same location, we don't need to do any fixup
+        bool isEqual;
+        if (mTargetBaseURI &&
+            NS_SUCCEEDED(mCurrentBaseURI->Equals(mTargetBaseURI, &isEqual)) &&
+            isEqual) {
+            return NS_OK;
+        }
+
+        nsCOMPtr<nsIURI> relativeURI;
+        relativeURI = IsFlagSet(IWBP::PERSIST_FLAGS_FIXUP_LINKS_TO_DESTINATION)
+                      ? mTargetBaseURI : mCurrentBaseURI;
+        // Make a new URI to replace the current one
+        nsCOMPtr<nsIURI> newURI;
+        rv = NS_NewURI(getter_AddRefs(newURI), oldCValue,
+                       mParent->GetCharacterSet().get(), relativeURI);
+        if (NS_SUCCEEDED(rv) && newURI)
+        {
+            newURI->SetUserPass(EmptyCString());
+            nsAutoCString uriSpec;
+            newURI->GetSpec(uriSpec);
+            attr->SetValue(NS_ConvertUTF8toUTF16(uriSpec));
+        }
+    }
+
+    return NS_OK;
+}
+
+static void
+AppendXMLAttr(const nsAString& key, const nsAString& aValue, nsAString& aBuffer)
+{
+    if (!aBuffer.IsEmpty()) {
+        aBuffer.Append(' ');
+    }
+    aBuffer.Append(key);
+    aBuffer.AppendLiteral("=\"");
+    for (size_t i = 0; i < aValue.Length(); ++i) {
+        switch (aValue[i]) {
+            case '&':
+                aBuffer.AppendLiteral("&amp;");
+                break;
+            case '<':
+                aBuffer.AppendLiteral("&lt;");
+                break;
+            case '>':
+                aBuffer.AppendLiteral("&gt;");
+                break;
+            case '"':
+                aBuffer.AppendLiteral("&quot;");
+                break;
+            default:
+                aBuffer.Append(aValue[i]);
+                break;
+        }
+    }
+    aBuffer.Append('"');
+}
+
+nsresult
+PersistNodeFixup::FixupXMLStyleSheetLink(nsIDOMProcessingInstruction* aPI,
+                                         const nsAString& aHref)
+{
+    NS_ENSURE_ARG_POINTER(aPI);
+    nsresult rv = NS_OK;
+
+    nsAutoString data;
+    rv = aPI->GetData(data);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+    nsAutoString href;
+    nsContentUtils::GetPseudoAttributeValue(data,
+                                            nsGkAtoms::href,
+                                            href);
+
+    // Construct and set a new data value for the xml-stylesheet
+    if (!aHref.IsEmpty() && !href.IsEmpty())
+    {
+        nsAutoString alternate;
+        nsAutoString charset;
+        nsAutoString title;
+        nsAutoString type;
+        nsAutoString media;
+
+        nsContentUtils::GetPseudoAttributeValue(data,
+                                                nsGkAtoms::alternate,
+                                                alternate);
+        nsContentUtils::GetPseudoAttributeValue(data,
+                                                nsGkAtoms::charset,
+                                                charset);
+        nsContentUtils::GetPseudoAttributeValue(data,
+                                                nsGkAtoms::title,
+                                                title);
+        nsContentUtils::GetPseudoAttributeValue(data,
+                                                nsGkAtoms::type,
+                                                type);
+        nsContentUtils::GetPseudoAttributeValue(data,
+                                                nsGkAtoms::media,
+                                                media);
+
+        nsAutoString newData;
+        AppendXMLAttr(NS_LITERAL_STRING("href"), aHref, newData);
+        if (!title.IsEmpty()) {
+            AppendXMLAttr(NS_LITERAL_STRING("title"), title, newData);
+        }
+        if (!media.IsEmpty()) {
+            AppendXMLAttr(NS_LITERAL_STRING("media"), media, newData);
+        }
+        if (!type.IsEmpty()) {
+            AppendXMLAttr(NS_LITERAL_STRING("type"), type, newData);
+        }
+        if (!charset.IsEmpty()) {
+            AppendXMLAttr(NS_LITERAL_STRING("charset"), charset, newData);
+        }
+        if (!alternate.IsEmpty()) {
+            AppendXMLAttr(NS_LITERAL_STRING("alternate"), alternate, newData);
+        }
+        aPI->SetData(newData);
+    }
+
+    return rv;
+}
+
+NS_IMETHODIMP
+PersistNodeFixup::FixupNode(nsIDOMNode *aNodeIn,
+                            bool *aSerializeCloneKids,
+                            nsIDOMNode **aNodeOut)
+{
+    *aNodeOut = nullptr;
+    *aSerializeCloneKids = false;
+
+    uint16_t type;
+    nsresult rv = aNodeIn->GetNodeType(&type);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (type != nsIDOMNode::ELEMENT_NODE &&
+        type != nsIDOMNode::PROCESSING_INSTRUCTION_NODE) {
+        return NS_OK;
+    }
+
+    // Fixup xml-stylesheet processing instructions
+    nsCOMPtr<nsIDOMProcessingInstruction> nodeAsPI = do_QueryInterface(aNodeIn);
+    if (nodeAsPI) {
+        nsAutoString target;
+        nodeAsPI->GetTarget(target);
+        if (target.EqualsLiteral("xml-stylesheet"))
+        {
+            rv = GetNodeToFixup(aNodeIn, aNodeOut);
+            if (NS_SUCCEEDED(rv) && *aNodeOut) {
+                nsCOMPtr<nsIDOMProcessingInstruction> outNode =
+                    do_QueryInterface(*aNodeOut);
+                nsAutoString href;
+                GetXMLStyleSheetLink(nodeAsPI, href);
+                if (!href.IsEmpty()) {
+                    FixupURI(href);
+                    FixupXMLStyleSheetLink(outNode, href);
+                }
+            }
+        }
+        return NS_OK;
+    }
+
+    // BASE elements are replaced by a comment so relative links are not hosed.
+    if (!IsFlagSet(IWBP::PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS)) {
+        nsCOMPtr<nsIDOMHTMLBaseElement> nodeAsBase = do_QueryInterface(aNodeIn);
+        if (nodeAsBase) {
+            nsCOMPtr<nsIDOMDocument> ownerDocument;
+            auto* base = static_cast<dom::HTMLSharedElement*>(nodeAsBase.get());
+            base->GetOwnerDocument(getter_AddRefs(ownerDocument));
+            if (ownerDocument) {
+                nsAutoString href;
+                base->GetHref(href); // Doesn't matter if this fails
+                nsCOMPtr<nsIDOMComment> comment;
+                nsAutoString commentText;
+                commentText.AssignLiteral(" base ");
+                if (!href.IsEmpty()) {
+                    commentText += NS_LITERAL_STRING("href=\"") + hr