merge m-c to fig
authorMargaret Leibovic <margaret.leibovic@gmail.com>
Sun, 04 Aug 2013 15:14:01 -0700
changeset 156403 2c24acf4816b28a62c3871555c6e3c3e33bdb736
parent 156402 4c3421eb1d4358150b151a9f3b74bddf1d8a0e7d (current diff)
parent 153497 0a63cd911b4f8c065c3dd344f59adfe5e9b57849 (diff)
child 156404 9972d89f09bbf393b238cb2bdd86cba47026801c
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone25.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 fig
CLOBBER
browser/components/sessionstore/src/Makefile.in
browser/metro/base/content/flyouts/aboutFlyout.js
browser/metro/base/content/flyouts/flyoutUI.js
browser/metro/base/content/flyouts/prefsFlyout.js
browser/metro/base/content/flyouts/syncFlyout.js
browser/metro/components/Makefile.in
browser/themes/linux/webapps-16.png
browser/themes/linux/webapps-64.png
browser/themes/osx/webapps-16.png
browser/themes/osx/webapps-16@2x.png
browser/themes/osx/webapps-64.png
browser/themes/windows/webapps-16.png
browser/themes/windows/webapps-64.png
content/events/test/test_bug716822.html
dom/apps/src/Makefile.in
dom/identity/Makefile.in
dom/indexedDB/nsIIDBCursor.idl
dom/indexedDB/nsIIDBCursorWithValue.idl
dom/indexedDB/nsIIDBDatabase.idl
dom/indexedDB/nsIIDBIndex.idl
dom/indexedDB/nsIIDBObjectStore.idl
dom/indexedDB/nsIIDBOpenDBRequest.idl
dom/indexedDB/nsIIDBRequest.idl
dom/indexedDB/nsIIDBTransaction.idl
dom/interfaces/html/nsIDOMHTMLDListElement.idl
dom/interfaces/html/nsIDOMHTMLDataListElement.idl
dom/interfaces/html/nsIDOMHTMLFontElement.idl
dom/interfaces/html/nsIDOMHTMLHeadingElement.idl
dom/interfaces/html/nsIDOMHTMLLabelElement.idl
dom/interfaces/html/nsIDOMHTMLLegendElement.idl
dom/interfaces/html/nsIDOMHTMLMeterElement.idl
dom/interfaces/html/nsIDOMHTMLModElement.idl
dom/interfaces/html/nsIDOMHTMLOutputElement.idl
dom/interfaces/html/nsIDOMHTMLParamElement.idl
dom/interfaces/html/nsIDOMHTMLProgressElement.idl
dom/interfaces/html/nsIDOMHTMLTableColElement.idl
dom/interfaces/html/nsIDOMHTMLTableRowElement.idl
dom/interfaces/html/nsIDOMHTMLTableSectionElement.idl
dom/interfaces/html/nsIDOMHTMLUnknownElement.idl
dom/payment/Makefile.in
dom/phonenumberutils/Makefile.in
dom/wappush/src/Makefile.in
gfx/tests/TestColorNames.cpp
gfx/tests/TestRect.cpp
gfx/tests/TestRegion.cpp
gfx/tests/gfxColorManagementTest.cmtest
gfx/tests/gfxColorManagementTest.cpp
gfx/tests/gfxFontSelectionTest.cpp
gfx/tests/gfxFontSelectionTests.h
gfx/tests/gfxSurfaceRefCountTest.cpp
gfx/tests/gfxTextRunPerfTest.cpp
gfx/tests/gfxWordCacheTest.cpp
gfx/tests/per-word-runs.h
gfx/tests/testprofiles/DELL2407WFP-2B283C91.icc
gfx/tests/testprofiles/G22LWk-2489A79.icc
gfx/tests/testprofiles/MBP20080419-1.icc
gfx/tests/testprofiles/PhLCD17a.icm
gfx/tests/testprofiles/identity.icc
gfx/tests/testprofiles/murphy.icc
gfx/tests/testprofiles/sRGB_IEC61966-2-1_noBPC.icc
gfx/tests/testprofiles/sRGB_IEC61966-2-1_withBPC.icc
gfx/tests/testprofiles/sRGB_v4_ICC_preference.icc
layout/base/nsStyleConsts.h
layout/generic/nsTextFrameThebes.cpp
layout/tools/recording/Makefile.in
media/libsoundtouch/Makefile.in
mfbt/MSStdInt.h
mfbt/StandardInteger.h
mobile/android/base/BrowserApp.java
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/Makefile.in
mobile/android/base/Tabs.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/resources/values/arrays.xml
mobile/android/base/resources/values/dimens.xml
mobile/android/base/strings.xml.in
mobile/android/base/tests/BaseTest.java.in
mobile/android/base/tests/robocop.ini
mobile/android/base/tests/testAddSearchEngine.java.in
mobile/android/base/tests/testShareLink.java.in
mobile/android/chrome/content/browser.js
mobile/android/components/AlertsService.js
mobile/android/themes/core/images/addons-amo-hdpi.png
netwerk/base/src/nsIOThreadPool.cpp
netwerk/base/src/nsIOThreadPool.h
netwerk/protocol/app/Makefile.in
netwerk/test/unit/test_bug722299.js
security/patches/bug-658222-false-start.patch
toolkit/components/captivedetect/Makefile.in
toolkit/components/contentprefs/tests/unit_ipc/contentPrefs_childipc.js
toolkit/components/contentprefs/tests/unit_ipc/head_contentPrefs.js
toolkit/components/contentprefs/tests/unit_ipc/test_contentPrefs_parentipc.js
toolkit/components/jsdownloads/src/Makefile.in
toolkit/components/osfile/_PromiseWorker.jsm
toolkit/components/osfile/osfile_async_front.jsm
toolkit/components/osfile/osfile_async_worker.js
toolkit/components/osfile/osfile_shared_allthreads.jsm
toolkit/components/osfile/osfile_shared_front.jsm
toolkit/components/osfile/osfile_unix_allthreads.jsm
toolkit/components/osfile/osfile_unix_back.jsm
toolkit/components/osfile/osfile_unix_front.jsm
toolkit/components/osfile/osfile_win_allthreads.jsm
toolkit/components/osfile/osfile_win_back.jsm
toolkit/components/osfile/osfile_win_front.jsm
toolkit/components/osfile/ospath_unix_back.jsm
toolkit/components/osfile/ospath_win_back.jsm
toolkit/crashreporter/breakpad-patches/12-sht-arm-exidx-define.patch
toolkit/devtools/apps/Makefile.in
toolkit/mozapps/downloads/Makefile.in
toolkit/mozapps/extensions/test/addons/test_bug463819_1/install.rdf
toolkit/mozapps/extensions/test/addons/test_bug463819_2/install.rdf
toolkit/mozapps/extensions/test/addons/test_bug463819_3/install.rdf
toolkit/mozapps/extensions/test/addons/test_bug463819_4/install.rdf
toolkit/mozapps/extensions/test/addons/test_bug463819_5/install.rdf
toolkit/mozapps/extensions/test/addons/test_bug463819_6/install.rdf
toolkit/mozapps/extensions/test/addons/test_bug463819_7/install.rdf
toolkit/mozapps/extensions/test/addons/test_bug463819_8/install.rdf
toolkit/mozapps/extensions/test/addons/test_bug463819_9/install.rdf
toolkit/mozapps/plugins/Makefile.in
toolkit/webapps/Makefile.in
--- a/CLOBBER
+++ b/CLOBBER
@@ -12,10 +12,9 @@
 #          O               O
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
-Bug 895173: Rename context menu id's to "home_" for home context menu.
-Android resource changes
+Removal of XPIDL for bug 893117 requires a clobber to make sure interfaces aren't generated.
--- a/accessible/public/msaa/Makefile.in
+++ b/accessible/public/msaa/Makefile.in
@@ -34,18 +34,16 @@ MIDL_GENERATED_FILES = \
 	ISimpleDOMDocument.h \
 	ISimpleDOMDocument_p.c \
 	ISimpleDOMDocument_i.c \
 	ISimpleDOMText.h \
 	ISimpleDOMText_p.c \
 	ISimpleDOMText_i.c \
 	$(NULL)
 
-SRCDIR_CSRCS	= $(addprefix $(srcdir)/,$(CSRCS))
-
 OS_LIBS = $(call EXPAND_LIBNAME,kernel32 rpcns4 rpcrt4 oleaut32)
 
 $(MIDL_GENERATED_FILES): done_gen
 
 done_gen: ISimpleDOMNode.idl \
           ISimpleDOMDocument.idl \
           ISimpleDOMText.idl
 
--- a/accessible/src/base/ARIAMap.cpp
+++ b/accessible/src/base/ARIAMap.cpp
@@ -614,17 +614,17 @@ static nsRoleMapEntry sWAIRoleMaps[] =
     &nsGkAtoms::treegrid,
     roles::TREE_TABLE,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     eSelect | eTable,
     kNoReqStates,
-    eARIAReadonly,
+    eARIAReadonlyOrEditable,
     eARIAMultiSelectable,
     eFocusableUntilDisabled
   },
   { // treeitem
     &nsGkAtoms::treeitem,
     roles::OUTLINEITEM,
     kUseMapRole,
     eNoValue,
--- a/accessible/src/base/ARIAStateMap.cpp
+++ b/accessible/src/base/ARIAStateMap.cpp
@@ -361,34 +361,29 @@ MapEnumType(dom::Element* aElement, uint
 
   *aState |= aData.mDefaultState;
 }
 
 static void
 MapTokenType(dom::Element* aElement, uint64_t* aState,
              const TokenTypeData& aData)
 {
-  if (aElement->HasAttr(kNameSpaceID_None, aData.mAttrName)) {
+  if (nsAccUtils::HasDefinedARIAToken(aElement, aData.mAttrName)) {
     if ((aData.mType & eMixedType) &&
         aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
                               nsGkAtoms::mixed, eCaseMatters)) {
       *aState |= aData.mPermanentState | states::MIXED;
       return;
     }
 
     if (aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
                               nsGkAtoms::_false, eCaseMatters)) {
       *aState |= aData.mPermanentState | aData.mFalseState;
       return;
     }
 
-    if (!aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
-                               nsGkAtoms::_undefined, eCaseMatters) &&
-        !aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
-                               nsGkAtoms::_empty, eCaseMatters)) {
-      *aState |= aData.mPermanentState | aData.mTrueState;
-    }
+    *aState |= aData.mPermanentState | aData.mTrueState;
     return;
   }
 
   if (aData.mType & eDefinedIfAbsent)
     *aState |= aData.mPermanentState | aData.mFalseState;
 }
--- a/accessible/src/base/ARIAStateMap.h
+++ b/accessible/src/base/ARIAStateMap.h
@@ -2,17 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _mozilla_a11y_aria_ARIAStateMap_h_
 #define _mozilla_a11y_aria_ARIAStateMap_h_
 
-#include "mozilla/StandardInteger.h"
+#include <stdint.h>
 
 namespace mozilla {
 
 namespace dom {
 class Element;
 }
 
 namespace a11y {
--- a/accessible/src/base/EventQueue.cpp
+++ b/accessible/src/base/EventQueue.cpp
@@ -298,17 +298,17 @@ EventQueue::CoalesceSelChangeEvents(AccS
       aTailEvent->mPackedEvent = aThisEvent;
       return;
     }
 
     if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
         aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
       aTailEvent->mEventRule = AccEvent::eDoNotEmit;
       aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
-      aThisEvent->mPackedEvent = aThisEvent;
+      aThisEvent->mPackedEvent = aTailEvent;
       return;
     }
   }
 
   // Unpack the packed selection change event because we've got one
   // more selection add/remove.
   if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
     if (aThisEvent->mPackedEvent) {
@@ -467,16 +467,39 @@ EventQueue::ProcessEventQueue()
           hyperText->GetSelectionCount(&selectionCount);
           if (selectionCount)
             nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
                                     hyperText);
         }
         continue;
       }
 
+      // Fire selected state change events in support to selection events.
+      if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) {
+        nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
+                                true, event->mIsFromUserInput);
+
+      } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_REMOVE) {
+        nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
+                                false, event->mIsFromUserInput);
+
+      } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
+        AccSelChangeEvent* selChangeEvent = downcast_accEvent(event);
+        nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
+                                (selChangeEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd),
+                                event->mIsFromUserInput);
+
+        if (selChangeEvent->mPackedEvent) {
+          nsEventShell::FireEvent(selChangeEvent->mPackedEvent->mAccessible,
+                                  states::SELECTED,
+                                  (selChangeEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd),
+                                  selChangeEvent->mPackedEvent->mIsFromUserInput);
+        }
+      }
+
       nsEventShell::FireEvent(event);
 
       // Fire text change events.
       AccMutationEvent* mutationEvent = downcast_accEvent(event);
       if (mutationEvent) {
         if (mutationEvent->mTextChangeEvent)
           nsEventShell::FireEvent(mutationEvent->mTextChangeEvent);
       }
--- a/accessible/src/base/Filters.h
+++ b/accessible/src/base/Filters.h
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_Filters_h__
 #define mozilla_a11y_Filters_h__
 
-#include "mozilla/StandardInteger.h"
+#include <stdint.h>
 
 /**
  * Predefined filters used for nsAccIterator and nsAccCollector.
  */
 namespace mozilla {
 namespace a11y {
 
 class Accessible;
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -43,16 +43,18 @@ NotificationController::~NotificationCon
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationCollector: AddRef/Release and cycle collection
 
 NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(NotificationController)
 NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(NotificationController)
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationController)
+
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationController)
   if (tmp->mDocument)
     tmp->Shutdown();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentInsertions)
--- a/accessible/src/base/RoleAsserts.cpp
+++ b/accessible/src/base/RoleAsserts.cpp
@@ -7,13 +7,13 @@
 #include "nsIAccessibleRole.h"
 #include "Role.h"
 
 #include "mozilla/Assertions.h"
 
 using namespace mozilla::a11y;
 
 #define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \
-  MOZ_STATIC_ASSERT(static_cast<uint32_t>(roles::geckoRole) \
-                    == static_cast<uint32_t>(nsIAccessibleRole::ROLE_ ## geckoRole), \
-                    "internal and xpcom roles differ!");
+  static_assert(static_cast<uint32_t>(roles::geckoRole) \
+                == static_cast<uint32_t>(nsIAccessibleRole::ROLE_ ## geckoRole), \
+                "internal and xpcom roles differ!");
 #include "RoleMap.h"
 #undef ROLE
--- a/accessible/src/base/States.h
+++ b/accessible/src/base/States.h
@@ -2,17 +2,17 @@
 /* vim: set expandtab shiftwidth=2 tabstop=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/. */
 
 #ifndef _states_h_
 #define _states_h_
 
-#include "mozilla/StandardInteger.h"
+#include <stdint.h>
 
 namespace mozilla {
 namespace a11y {
 namespace states {
 
   /**
    * The object is disabled, opposite to enabled and sensitive.
    */
--- a/accessible/src/base/nsCoreUtils.cpp
+++ b/accessible/src/base/nsCoreUtils.cpp
@@ -36,16 +36,18 @@
 
 #include "nsComponentManagerUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "mozilla/dom/Element.h"
 
 #include "nsITreeBoxObject.h"
 #include "nsITreeColumns.h"
 
+using namespace mozilla;
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsCoreUtils
 ////////////////////////////////////////////////////////////////////////////////
 
 bool
 nsCoreUtils::HasClickListener(nsIContent *aContent)
 {
   NS_ENSURE_TRUE(aContent, false);
@@ -125,17 +127,17 @@ nsCoreUtils::DispatchClickEvent(nsITreeB
 void
 nsCoreUtils::DispatchMouseEvent(uint32_t aEventType, int32_t aX, int32_t aY,
                                 nsIContent *aContent, nsIFrame *aFrame,
                                 nsIPresShell *aPresShell, nsIWidget *aRootWidget)
 {
   nsMouseEvent event(true, aEventType, aRootWidget,
                      nsMouseEvent::eReal, nsMouseEvent::eNormal);
 
-  event.refPoint = nsIntPoint(aX, aY);
+  event.refPoint = LayoutDeviceIntPoint(aX, aY);
 
   event.clickCount = 1;
   event.button = nsMouseEvent::eLeftButton;
   event.time = PR_IntervalNow();
   event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
 
   nsEventStatus status = nsEventStatus_eIgnore;
   aPresShell->HandleEventWithTarget(&event, aFrame, aContent, &status);
--- a/accessible/src/base/nsEventShell.h
+++ b/accessible/src/base/nsEventShell.h
@@ -31,16 +31,30 @@ public:
    * @param  aEventType   [in] the event type
    * @param  aAccessible  [in] the event target
    */
   static void FireEvent(uint32_t aEventType,
                         mozilla::a11y::Accessible* aAccessible,
                         mozilla::a11y::EIsFromUserInput aIsFromUserInput = mozilla::a11y::eAutoDetect);
 
   /**
+   * Fire state change event.
+   */
+  static void FireEvent(mozilla::a11y::Accessible* aTarget, uint64_t aState,
+                        bool aIsEnabled, bool aIsFromUserInput)
+  {
+    nsRefPtr<mozilla::a11y::AccStateChangeEvent> stateChangeEvent =
+      new mozilla::a11y::AccStateChangeEvent(aTarget, aState, aIsEnabled,
+                                             (aIsFromUserInput ?
+                                               mozilla::a11y::eFromUserInput :
+                                               mozilla::a11y::eNoUserInput));
+    FireEvent(stateChangeEvent);
+  }
+
+  /**
    * Append 'event-from-input' object attribute if the accessible event has
    * been fired just now for the given node.
    *
    * @param  aNode        [in] the DOM node
    * @param  aAttributes  [in, out] the attributes
    */
   static void GetEventAttributes(nsINode *aNode,
                                  nsIPersistentProperties *aAttributes);
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -835,17 +835,17 @@ Accessible::ChildAtPoint(int32_t aX, int
   nsIWidget* rootWidget = rootFrame->GetView()->GetNearestWidget(nullptr);
   NS_ENSURE_TRUE(rootWidget, nullptr);
 
   nsIntRect rootRect;
   rootWidget->GetScreenBounds(rootRect);
 
   nsMouseEvent dummyEvent(true, NS_MOUSE_MOVE, rootWidget,
                           nsMouseEvent::eSynthesized);
-  dummyEvent.refPoint = nsIntPoint(aX - rootRect.x, aY - rootRect.y);
+  dummyEvent.refPoint = LayoutDeviceIntPoint(aX - rootRect.x, aY - rootRect.y);
 
   nsIFrame* popupFrame = nsLayoutUtils::
     GetPopupFrameForEventCoordinates(accDocument->PresContext()->GetRootPresContext(),
                                      &dummyEvent);
   if (popupFrame) {
     // If 'this' accessible is not inside the popup then ignore the popup when
     // searching an accessible at point.
     DocAccessible* popupDoc =
@@ -3348,24 +3348,24 @@ Accessible::GetLevelInternal()
   }
 
   return level;
 }
 
 void
 Accessible::StaticAsserts() const
 {
-  MOZ_STATIC_ASSERT(eLastChildrenFlag <= (2 << kChildrenFlagsBits) - 1,
-                    "Accessible::mChildrenFlags was oversized by eLastChildrenFlag!");
-  MOZ_STATIC_ASSERT(eLastStateFlag <= (2 << kStateFlagsBits) - 1,
-                    "Accessible::mStateFlags was oversized by eLastStateFlag!");
-  MOZ_STATIC_ASSERT(eLastAccType <= (2 << kTypeBits) - 1,
-                    "Accessible::mType was oversized by eLastAccType!");
-  MOZ_STATIC_ASSERT(eLastAccGenericType <= (2 << kGenericTypesBits) - 1,
-                    "Accessible::mGenericType was oversized by eLastAccGenericType!");
+  static_assert(eLastChildrenFlag <= (2 << kChildrenFlagsBits) - 1,
+                "Accessible::mChildrenFlags was oversized by eLastChildrenFlag!");
+  static_assert(eLastStateFlag <= (2 << kStateFlagsBits) - 1,
+                "Accessible::mStateFlags was oversized by eLastStateFlag!");
+  static_assert(eLastAccType <= (2 << kTypeBits) - 1,
+                "Accessible::mType was oversized by eLastAccType!");
+  static_assert(eLastAccGenericType <= (2 << kGenericTypesBits) - 1,
+                "Accessible::mGenericType was oversized by eLastAccGenericType!");
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // KeyBinding class
 
 void
 KeyBinding::ToPlatformFormat(nsAString& aValue) const
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -718,16 +718,25 @@ public:
   bool HasOwnContent() const
     { return mContent && !(mStateFlags & eSharedNode); }
 
   /**
   * Return true if the accessible has a numeric value.
   */
   bool HasNumericValue() const;
 
+  /**
+   * Return true if the accessible state change is processed by handling proper
+   * DOM UI event, if otherwise then false. For example, HTMLCheckboxAccessible
+   * process nsIDocumentObserver::ContentStateChanged instead
+   * 'CheckboxStateChange' event.
+   */
+  bool NeedsDOMUIEvent() const
+    { return !(mStateFlags & eIgnoreDOMUIEvent); }
+
 protected:
 
   /**
    * Return the accessible name provided by native markup. It doesn't take
    * into account ARIA markup used to specify the name.
    */
   virtual mozilla::a11y::ENameValueFlag NativeName(nsString& aName);
 
@@ -784,16 +793,17 @@ protected:
    * @note keep these flags in sync with ChildrenFlags
    */
   enum StateFlags {
     eIsDefunct = 1 << 0, // accessible is defunct
     eIsNotInDocument = 1 << 1, // accessible is not in document
     eSharedNode = 1 << 2, // accessible shares DOM node from another accessible
     eNotNodeMapEntry = 1 << 3, // accessible shouldn't be in document node map
     eHasNumericValue = 1 << 4, // accessible has a numeric value
+    eIgnoreDOMUIEvent = 1 << 5, // don't process DOM UI events for a11y events
 
     eLastStateFlag = eHasNumericValue
   };
 
 protected:
 
   //////////////////////////////////////////////////////////////////////////////
   // Miscellaneous helpers
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -98,16 +98,18 @@ DocAccessible::~DocAccessible()
 {
   NS_ASSERTION(!mPresShell, "LastRelease was never called!?!");
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(DocAccessible)
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible, Accessible)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationController)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVirtualCursor)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildDocuments)
   tmp->mDependentIDsHash.EnumerateRead(CycleCollectorTraverseDepIDsEntry, &cb);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessibleCache)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorJumpElm)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -1151,17 +1153,23 @@ DocAccessible::ContentStateChanged(nsIDo
     Accessible* widget = accessible->ContainerWidget();
     if (widget && widget->IsSelect()) {
       AccSelChangeEvent::SelChangeType selChangeType =
         aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED) ?
           AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
       nsRefPtr<AccEvent> event =
         new AccSelChangeEvent(widget, accessible, selChangeType);
       FireDelayedEvent(event);
+      return;
     }
+
+    nsRefPtr<AccEvent> event =
+      new AccStateChangeEvent(accessible, states::CHECKED,
+                              aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED));
+    FireDelayedEvent(event);
   }
 
   if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
     nsRefPtr<AccEvent> event =
       new AccStateChangeEvent(accessible, states::INVALID, true);
     FireDelayedEvent(event);
   }
 
@@ -1825,20 +1833,26 @@ DocAccessible::UpdateTree(Accessible* aC
 }
 
 uint32_t
 DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
                                   AccReorderEvent* aReorderEvent)
 {
   uint32_t updateFlags = eAccessible;
 
+  // If a focused node has been shown then it could mean its frame was recreated
+  // while the node stays focused and we need to fire focus event on
+  // the accessible we just created. If the queue contains a focus event for
+  // this node already then it will be suppressed by this one.
+  Accessible* focusedAcc = nullptr;
+
   nsINode* node = aChild->GetNode();
   if (aIsInsert) {
     // Create accessible tree for shown accessible.
-    CacheChildrenInSubtree(aChild);
+    CacheChildrenInSubtree(aChild, &focusedAcc);
 
   } else {
     // Fire menupopup end event before hide event if a menu goes away.
 
     // XXX: We don't look into children of hidden subtree to find hiding
     // menupopup (as we did prior bug 570275) because we don't do that when
     // menu is showing (and that's impossible until bug 606924 is fixed).
     // Nevertheless we should do this at least because layout coalesces
@@ -1865,57 +1879,59 @@ DocAccessible::UpdateTreeInternal(Access
       // Fire EVENT_MENUPOPUP_START if ARIA menu appears.
       FireDelayedEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START, aChild);
 
     } else if (ariaRole == roles::ALERT) {
       // Fire EVENT_ALERT if ARIA alert appears.
       updateFlags = eAlertAccessible;
       FireDelayedEvent(nsIAccessibleEvent::EVENT_ALERT, aChild);
     }
-
-    // If focused node has been shown then it means its frame was recreated
-    // while it's focused. Fire focus event on new focused accessible. If
-    // the queue contains focus event for this node then it's suppressed by
-    // this one.
-    // XXX: do we really want to send focus to focused DOM node not taking into
-    // account active item?
-    if (FocusMgr()->IsFocused(aChild))
-      FocusMgr()->DispatchFocusEvent(this, aChild);
-
   } else {
     // Update the tree for content removal.
     // The accessible parent may differ from container accessible if
     // the parent doesn't have own DOM node like list accessible for HTML
     // selects.
     Accessible* parent = aChild->Parent();
     NS_ASSERTION(parent, "No accessible parent?!");
     if (parent)
       parent->RemoveChild(aChild);
 
     UncacheChildrenInSubtree(aChild);
   }
 
+  // XXX: do we really want to send focus to focused DOM node not taking into
+  // account active item?
+  if (focusedAcc)
+    FocusMgr()->DispatchFocusEvent(this, focusedAcc);
+
   return updateFlags;
 }
 
 void
-DocAccessible::CacheChildrenInSubtree(Accessible* aRoot)
+DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
+                                      Accessible** aFocusedAcc)
 {
+  // If the accessible is focused then report a focus event after all related
+  // mutation events.
+  if (aFocusedAcc && !*aFocusedAcc &&
+      FocusMgr()->HasDOMFocus(aRoot->GetContent()))
+    *aFocusedAcc = aRoot;
+
   aRoot->EnsureChildren();
 
   // Make sure we create accessible tree defined in DOM only, i.e. if accessible
   // provides specific tree (like XUL trees) then tree creation is handled by
   // this accessible.
   uint32_t count = aRoot->ContentChildCount();
   for (uint32_t idx = 0; idx < count; idx++) {
     Accessible* child = aRoot->ContentChildAt(idx);
     NS_ASSERTION(child, "Illicit tree change while tree is created!");
     // Don't cross document boundaries.
     if (child && child->IsContent())
-      CacheChildrenInSubtree(child);
+      CacheChildrenInSubtree(child, aFocusedAcc);
   }
 
   // Fire document load complete on ARIA documents.
   // XXX: we should delay an event if the ARIA document has aria-busy.
   if (aRoot->HasARIARole() && !aRoot->IsDoc()) {
     a11y::role role = aRoot->ARIARole();
     if (role == roles::DIALOG || role == roles::DOCUMENT)
       FireDelayedEvent(nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE, aRoot);
--- a/accessible/src/generic/DocAccessible.h
+++ b/accessible/src/generic/DocAccessible.h
@@ -444,18 +444,23 @@ protected:
     eAlertAccessible = 2
   };
 
   uint32_t UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
                               AccReorderEvent* aReorderEvent);
 
   /**
    * Create accessible tree.
+   *
+   * @param aRoot       [in] a root of subtree to create
+   * @param aFocusedAcc [in, optional] a focused accessible under created
+   *                      subtree if any
    */
-  void CacheChildrenInSubtree(Accessible* aRoot);
+  void CacheChildrenInSubtree(Accessible* aRoot,
+                              Accessible** aFocusedAcc = nullptr);
 
   /**
    * Remove accessibles in subtree from node to accessible map.
    */
   void UncacheChildrenInSubtree(Accessible* aRoot);
 
   /**
    * Shutdown any cached accessible in the subtree.
--- a/accessible/src/generic/FormControlAccessible.h
+++ b/accessible/src/generic/FormControlAccessible.h
@@ -16,17 +16,19 @@ namespace a11y {
   */
 template<int Max>
 class ProgressMeterAccessible : public LeafAccessible
 {
 public:
   ProgressMeterAccessible(nsIContent* aContent, DocAccessible* aDoc) :
     LeafAccessible(aContent, aDoc)
   {
-    mStateFlags |= eHasNumericValue;
+    // Ignore 'ValueChange' DOM event in lieu of @value attribute change
+    // notifications.
+    mStateFlags |= eHasNumericValue | eIgnoreDOMUIEvent;
     mType = eProgressType;
   }
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIACCESSIBLEVALUE
 
   // Accessible
   virtual void Value(nsString& aValue);
--- a/accessible/src/generic/RootAccessible.cpp
+++ b/accessible/src/generic/RootAccessible.cpp
@@ -305,47 +305,44 @@ RootAccessible::ProcessDOMEvent(nsIDOMEv
       HandleTreeInvalidatedEvent(aDOMEvent, treeAcc);
       return;
     }
   }
 #endif
 
   if (eventType.EqualsLiteral("RadioStateChange")) {
     uint64_t state = accessible->State();
-
-    // radiogroup in prefWindow is exposed as a list,
-    // and panebutton is exposed as XULListitem in A11y.
-    // XULListitemAccessible::GetStateInternal uses STATE_SELECTED in this case,
-    // so we need to check states::SELECTED also.
     bool isEnabled = (state & (states::CHECKED | states::SELECTED)) != 0;
 
-    nsRefPtr<AccEvent> accEvent =
-      new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
-    nsEventShell::FireEvent(accEvent);
+    if (accessible->NeedsDOMUIEvent()) {
+      nsRefPtr<AccEvent> accEvent =
+        new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
+      nsEventShell::FireEvent(accEvent);
+    }
 
     if (isEnabled) {
       FocusMgr()->ActiveItemChanged(accessible);
 #ifdef A11Y_LOG
       if (logging::IsEnabled(logging::eFocus))
         logging::ActiveItemChangeCausedBy("RadioStateChange", accessible);
 #endif
     }
 
     return;
   }
 
   if (eventType.EqualsLiteral("CheckboxStateChange")) {
-    uint64_t state = accessible->State();
-
-    bool isEnabled = !!(state & states::CHECKED);
+    if (accessible->NeedsDOMUIEvent()) {
+      uint64_t state = accessible->State();
+      bool isEnabled = !!(state & states::CHECKED);
 
-    nsRefPtr<AccEvent> accEvent =
-      new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
-
-    nsEventShell::FireEvent(accEvent);
+      nsRefPtr<AccEvent> accEvent =
+        new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
+      nsEventShell::FireEvent(accEvent);
+    }
     return;
   }
 
   Accessible* treeItemAcc = nullptr;
 #ifdef MOZ_XUL
   // If it's a tree element, need the currently selected item.
   if (treeAcc) {
     treeItemAcc = accessible->CurrentItem();
@@ -450,24 +447,20 @@ RootAccessible::ProcessDOMEvent(nsIDOMEv
                             accessible, eFromUserInput);
 
     FocusMgr()->ActiveItemChanged(nullptr);
 #ifdef A11Y_LOG
     if (logging::IsEnabled(logging::eFocus))
       logging::ActiveItemChangeCausedBy("DOMMenuBarInactive", accessible);
 #endif
   }
-  else if (eventType.EqualsLiteral("ValueChange")) {
-
-    //We don't process 'ValueChange' events for progress meters since we listen
-    //@value attribute change for them.
-    if (!accessible->IsProgress()) {
-      targetDocument->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
-                                       accessible);
-    }
+  else if (accessible->NeedsDOMUIEvent() &&
+           eventType.EqualsLiteral("ValueChange")) {
+     targetDocument->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
+                                      accessible);
   }
 #ifdef DEBUG_DRAGDROPSTART
   else if (eventType.EqualsLiteral("mouseover")) {
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_DRAGDROP_START,
                             accessible);
   }
 #endif
 }
--- a/accessible/src/generic/TableCellAccessible.h
+++ b/accessible/src/generic/TableCellAccessible.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_TableCellAccessible_h__
 #define mozilla_a11y_TableCellAccessible_h__
 
 #include "nsTArray.h"
-#include "mozilla/StandardInteger.h"
+#include <stdint.h>
 
 namespace mozilla {
 namespace a11y {
 
 class Accessible;
 class TableAccessible;
 
 /**
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -34,22 +34,16 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLCheckboxAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
-HTMLCheckboxAccessible::
-  HTMLCheckboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
-  LeafAccessible(aContent, aDoc)
-{
-}
-
 role
 HTMLCheckboxAccessible::NativeRole()
 {
   return roles::CHECKBUTTON;
 }
 
 uint8_t
 HTMLCheckboxAccessible::ActionCount()
@@ -114,22 +108,16 @@ HTMLCheckboxAccessible::IsWidget() const
   return true;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLRadioButtonAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
-HTMLRadioButtonAccessible::
-  HTMLRadioButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
-  RadioButtonAccessible(aContent, aDoc)
-{
-}
-
 uint64_t
 HTMLRadioButtonAccessible::NativeState()
 {
   uint64_t state = AccessibleWrap::NativeState();
 
   state |= states::CHECKABLE;
 
   HTMLInputElement* input = HTMLInputElement::FromContent(mContent);
--- a/accessible/src/html/HTMLFormControlAccessible.h
+++ b/accessible/src/html/HTMLFormControlAccessible.h
@@ -21,17 +21,23 @@ typedef ProgressMeterAccessible<1> HTMLP
  * Accessible for HTML input@type="checkbox".
  */
 class HTMLCheckboxAccessible : public LeafAccessible
 {
 
 public:
   enum { eAction_Click = 0 };
 
-  HTMLCheckboxAccessible(nsIContent* aContent, DocAccessible* aDoc);
+  HTMLCheckboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+    LeafAccessible(aContent, aDoc)
+  {
+    // Ignore "CheckboxStateChange" DOM event in lieu of document observer
+    // state change notification.
+    mStateFlags |= eIgnoreDOMUIEvent;
+  }
 
   // nsIAccessible
   NS_IMETHOD GetActionName(uint8_t aIndex, nsAString& aName);
   NS_IMETHOD DoAction(uint8_t index);
 
   // Accessible
   virtual mozilla::a11y::role NativeRole();
   virtual uint64_t NativeState();
@@ -46,17 +52,23 @@ public:
 
 /**
  * Accessible for HTML input@type="radio" element.
  */
 class HTMLRadioButtonAccessible : public RadioButtonAccessible
 {
 
 public:
-  HTMLRadioButtonAccessible(nsIContent* aContent, DocAccessible* aDoc);
+  HTMLRadioButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+    RadioButtonAccessible(aContent, aDoc)
+  {
+    // Ignore "RadioStateChange" DOM event in lieu of document observer
+    // state change notification.
+    mStateFlags |= eIgnoreDOMUIEvent;
+  }
 
   // Accessible
   virtual uint64_t NativeState();
   virtual void GetPositionAndSizeInternal(int32_t *aPosInSet,
                                           int32_t *aSetSize);
 };
 
 
--- a/accessible/src/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/src/windows/msaa/AccessibleWrap.cpp
@@ -1612,18 +1612,18 @@ AccessibleWrap::HandleAccEvent(AccEvent*
   // when running in metro mode. This confuses input focus tracking
   // in metro's UIA implementation.
   if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
     return NS_OK;
   }
 
   uint32_t eventType = aEvent->GetEventType();
 
-  MOZ_STATIC_ASSERT(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY,
-                    "MSAA event map skewed");
+  static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY,
+                "MSAA event map skewed");
 
   NS_ENSURE_TRUE(eventType > 0 && eventType < ArrayLength(gWinEventMap), NS_ERROR_FAILURE);
 
   uint32_t winEvent = gWinEventMap[eventType];
   if (!winEvent)
     return NS_OK;
 
   // Means we're not active.
--- a/accessible/src/windows/msaa/Compatibility.h
+++ b/accessible/src/windows/msaa/Compatibility.h
@@ -2,17 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef COMPATIBILITY_MANAGER_H
 #define COMPATIBILITY_MANAGER_H
 
-#include "mozilla/StandardInteger.h"
+#include <stdint.h>
 
 namespace mozilla {
 namespace a11y {
 
 /**
  * Used to get compatibility modes. Note, modes are computed at accessibility
  * start up time and aren't changed during lifetime.
  */
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -503,69 +503,74 @@ function eventQueue(aEventType)
             ok(false,
                "Unique type " +
                eventQueue.getEventTypeAsString(checker) + " event was handled.");
           }
         }
       }
     }
 
-    var matchedChecker = null;
+    var hasMatchedCheckers = false;
     for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
       var eventSeq = this.mScenarios[scnIdx];
 
       // Check if handled event matches expected sync event.
       var nextChecker = this.getNextExpectedEvent(eventSeq);
       if (nextChecker) {
         if (eventQueue.compareEvents(nextChecker, aEvent)) {
-          matchedChecker = nextChecker;
-          matchedChecker.wasCaught++;
-          break;
+          this.processMatchedChecker(aEvent, nextChecker, scnIdx, eventSeq.idx);
+          hasMatchedCheckers = true;
+          continue;
         }
       }
 
       // Check if handled event matches any expected async events.
       for (idx = 0; idx < eventSeq.length; idx++) {
         if (!eventSeq[idx].unexpected && eventSeq[idx].async) {
           if (eventQueue.compareEvents(eventSeq[idx], aEvent)) {
-            matchedChecker = eventSeq[idx];
-            matchedChecker.wasCaught++;
+            this.processMatchedChecker(aEvent, eventSeq[idx], scnIdx, idx);
+            hasMatchedCheckers = true;
             break;
           }
         }
       }
     }
 
-    // Call 'check' functions on invoker's side.
-    if (matchedChecker) {
-      if ("check" in matchedChecker)
-        matchedChecker.check(aEvent);
-
+    if (hasMatchedCheckers) {
       var invoker = this.getInvoker();
       if ("check" in invoker)
         invoker.check(aEvent);
     }
 
-    // Dump handled event.
-    eventQueue.logEvent(aEvent, matchedChecker, this.areExpectedEventsLeft(),
-                        this.mNextInvokerStatus);
-
     // If we don't have more events to wait then schedule next invoker.
-    if (!this.areExpectedEventsLeft() &&
+    if (this.hasMatchedScenario() &&
         (this.mNextInvokerStatus == kInvokerNotScheduled)) {
       this.processNextInvokerInTimeout();
       return;
     }
 
     // If we have scheduled a next invoker then cancel in case of match.
-    if ((this.mNextInvokerStatus == kInvokerPending) && matchedChecker)
+    if ((this.mNextInvokerStatus == kInvokerPending) && hasMatchedCheckers)
       this.mNextInvokerStatus = kInvokerCanceled;
   }
 
   // Helpers
+  this.processMatchedChecker =
+    function eventQueue_function(aEvent, aMatchedChecker, aScenarioIdx, aEventIdx)
+  {
+    aMatchedChecker.wasCaught++;
+
+    if ("check" in aMatchedChecker)
+      aMatchedChecker.check(aEvent);
+
+    eventQueue.logEvent(aEvent, aMatchedChecker, aScenarioIdx, aEventIdx,
+                        this.areExpectedEventsLeft(),
+                        this.mNextInvokerStatus);
+  }
+
   this.getNextExpectedEvent =
     function eventQueue_getNextExpectedEvent(aEventSeq)
   {
     if (!("idx" in aEventSeq))
       aEventSeq.idx = 0;
 
     while (aEventSeq.idx < aEventSeq.length &&
            (aEventSeq[aEventSeq.idx].unexpected ||
@@ -630,16 +635,26 @@ function eventQueue(aEventType)
           break;
       }
       if (idx == eventSeq.length)
         return true;
     }
 
     return false;
   }
+  
+  this.hasMatchedScenario =
+    function eventQueue_hasMatchedScenario()
+  {
+    for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
+      if (!this.areExpectedEventsLeft(this.mScenarios[scnIdx]))
+        return true;
+    }
+    return false;
+  }
 
   this.getInvoker = function eventQueue_getInvoker()
   {
     return this.mInvokers[this.mIndex];
   }
 
   this.getNextInvoker = function eventQueue_getNextInvoker()
   {
@@ -853,16 +868,17 @@ eventQueue.isSameEvent = function eventQ
   // target, thus we should filter text change and state change events since
   // they may occur on the same element because of complex changes.
   return this.compareEvents(aChecker, aEvent) &&
     !(aEvent instanceof nsIAccessibleTextChangeEvent) &&
     !(aEvent instanceof nsIAccessibleStateChangeEvent);
 }
 
 eventQueue.logEvent = function eventQueue_logEvent(aOrigEvent, aMatchedChecker,
+                                                   aScenarioIdx, aEventIdx,
                                                    aAreExpectedEventsLeft,
                                                    aInvokerStatus)
 {
   if (!gLogger.isEnabled()) // debug stuff
     return;
 
   // Dump DOM event information. Skip a11y event since it is dumped by
   // gA11yEventObserver.
@@ -892,17 +908,18 @@ eventQueue.logEvent = function eventQueu
   if (!aMatchedChecker)
     return;
 
   var msg = "EQ: ";
   var emphText = "matched ";
 
   var currType = eventQueue.getEventTypeAsString(aMatchedChecker);
   var currTargetDescr = eventQueue.getEventTargetDescr(aMatchedChecker);
-  var consoleMsg = "*****\nEQ matched: " + currType + "\n*****";
+  var consoleMsg = "*****\nScenario " + aScenarioIdx + 
+    ", event " + aEventIdx + " matched: " + currType + "\n*****";
   gLogger.logToConsole(consoleMsg);
 
   msg += " event, type: " + currType + ", target: " + currTargetDescr;
 
   gLogger.logToDOM(msg, true, emphText);
 }
 
 
@@ -1722,17 +1739,18 @@ function stateChangeChecker(aState, aIsE
     var unxpdExtraState = aIsEnabled ? 0 : (aIsExtraState ? aState : 0);
     testStates(event.accessible, state, extraState, unxpdState, unxpdExtraState);
   }
 
   this.match = function stateChangeChecker_match(aEvent)
   {
     if (aEvent instanceof nsIAccessibleStateChangeEvent) {
       var scEvent = aEvent.QueryInterface(nsIAccessibleStateChangeEvent);
-      return aEvent.accessible = this.target && scEvent.state == aState;
+      return (aEvent.accessible == getAccessible(this.target)) &&
+        (scEvent.state == aState);
     }
     return false;
   }
 }
 
 function asyncStateChangeChecker(aState, aIsExtraState, aIsEnabled,
                                  aTargetOrFunc, aTargetFuncArg)
 {
@@ -1767,16 +1785,69 @@ function expandedStateChecker(aIsEnabled
       "Wrong state of statechange event state");
 
     testStates(event.accessible,
                (aIsEnabled ? STATE_EXPANDED : STATE_COLLAPSED));
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// Event sequances (array of predefined checkers)
+
+/**
+ * Event seq for single selection change.
+ */
+function selChangeSeq(aUnselectedID, aSelectedID)
+{
+  if (!aUnselectedID) {
+    return [
+      new stateChangeChecker(STATE_SELECTED, false, true, aSelectedID),
+      new invokerChecker(EVENT_SELECTION, aSelectedID)
+    ];
+  }
+
+  // Return two possible scenarios: depending on widget type when selection is
+  // moved the the order of items that get selected and unselected may vary. 
+  return [
+    [
+      new stateChangeChecker(STATE_SELECTED, false, false, aUnselectedID),
+      new stateChangeChecker(STATE_SELECTED, false, true, aSelectedID),
+      new invokerChecker(EVENT_SELECTION, aSelectedID)
+    ],
+    [
+      new stateChangeChecker(STATE_SELECTED, false, true, aSelectedID),
+      new stateChangeChecker(STATE_SELECTED, false, false, aUnselectedID),
+      new invokerChecker(EVENT_SELECTION, aSelectedID)
+    ]
+  ];
+}
+
+/**
+ * Event seq for item removed form the selection.
+ */
+function selRemoveSeq(aUnselectedID)
+{
+  return [
+    new stateChangeChecker(STATE_SELECTED, false, false, aUnselectedID),
+    new invokerChecker(EVENT_SELECTION_REMOVE, aUnselectedID)
+  ];
+}
+
+/**
+ * Event seq for item added to the selection.
+ */
+function selAddSeq(aSelectedID)
+{
+  return [
+    new stateChangeChecker(STATE_SELECTED, false, true, aSelectedID),
+    new invokerChecker(EVENT_SELECTION_ADD, aSelectedID)
+  ];
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // Private implementation details.
 ////////////////////////////////////////////////////////////////////////////////
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // General
 
 var gA11yEventListeners = {};
@@ -2038,23 +2109,30 @@ function sequenceItem(aProcessor, aEvent
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Event queue invokers
 
 /**
  * Invoker base class for prepare an action.
  */
-function synthAction(aNodeOrID, aCheckerOrEventSeq)
+function synthAction(aNodeOrID, aEventsObj)
 {
   this.DOMNode = getNode(aNodeOrID);
 
-  if (aCheckerOrEventSeq) {
-    if (aCheckerOrEventSeq instanceof Array) {
-      this.eventSeq = aCheckerOrEventSeq;
+  if (aEventsObj) {
+    var scenarios = null;
+    if (aEventsObj instanceof Array) {
+      if (aEventsObj[0] instanceof Array)
+        scenarios = aEventsObj; // scenarios
+      else
+        scenarios = [ aEventsObj ]; // event sequance
     } else {
-      this.eventSeq = [ aCheckerOrEventSeq ];
+      scenarios = [ [ aEventsObj ] ]; // a single checker object
     }
+
+    for (var i = 0; i < scenarios.length; i++)
+      defineScenario(this, scenarios[i]);
   }
 
   this.getID = function synthAction_getID()
     { return prettyName(aNodeOrID) + " action"; }
 }
--- a/accessible/tests/mochitest/events/test_selection.html
+++ b/accessible/tests/mochitest/events/test_selection.html
@@ -33,56 +33,61 @@
     function doTests()
     {
       gQueue = new eventQueue();
 
       // open combobox
       gQueue.push(new synthClick("combobox",
                                  new invokerChecker(EVENT_FOCUS, "cb1_item1")));
       gQueue.push(new synthDownKey("cb1_item1",
-                                   new invokerChecker(EVENT_SELECTION, "cb1_item2")));
+                                   selChangeSeq("cb1_item1", "cb1_item2")));
 
       // closed combobox
       gQueue.push(new synthEscapeKey("combobox",
                                      new invokerChecker(EVENT_FOCUS, "combobox")));
       gQueue.push(new synthDownKey("cb1_item2",
-                                   new invokerChecker(EVENT_SELECTION, "cb1_item3")));
+                                   selChangeSeq("cb1_item2", "cb1_item3")));
 
       // listbox
       gQueue.push(new synthClick("lb1_item1",
                                  new invokerChecker(EVENT_SELECTION, "lb1_item1")));
       gQueue.push(new synthDownKey("lb1_item1",
-                                   new invokerChecker(EVENT_SELECTION, "lb1_item2")));
+                                   selChangeSeq("lb1_item1", "lb1_item2")));
 
       // multiselectable listbox
       gQueue.push(new synthClick("lb2_item1",
-                                 new invokerChecker(EVENT_SELECTION, "lb2_item1")));
+                                 selChangeSeq(null, "lb2_item1")));
       gQueue.push(new synthDownKey("lb2_item1",
-                                   new invokerChecker(EVENT_SELECTION_ADD, "lb2_item2"),
+                                   selAddSeq("lb2_item2"),
                                    { shiftKey: true }));
       gQueue.push(new synthUpKey("lb2_item2",
-                                 new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item2"),
+                                 selRemoveSeq("lb2_item2"),
                                  { shiftKey: true }));
       gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
-                               new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item1")));
+                               selRemoveSeq("lb2_item1")));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=414302"
      title="Incorrect selection events in HTML, XUL and ARIA">
-    Mozilla Bug 414302
+    Bug 414302
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=810268"
+     title="There's no way to know unselected item when selection in single selection was changed">
+    Bug 810268
   </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <select id="combobox">
--- a/accessible/tests/mochitest/events/test_selection_aria.html
+++ b/accessible/tests/mochitest/events/test_selection_aria.html
@@ -31,17 +31,17 @@
 
       this.eventSeq = [
         new invokerChecker(EVENT_SELECTION, aItemID)
       ];
 
       this.invoke = function selectItem_invoke() {
         var itemNode = this.selectNode.querySelector("*[aria-selected='true']");
         if (itemNode)
-          itemNode.removeAttribute("aria-selected", "true");
+          itemNode.removeAttribute("aria-selected");
 
         this.itemNode.setAttribute("aria-selected", "true");
       }
 
       this.getID = function selectItem_getID()
       {
         return "select item " + prettyName(aItemID);
       }
--- a/accessible/tests/mochitest/events/test_statechange.html
+++ b/accessible/tests/mochitest/events/test_statechange.html
@@ -67,16 +67,36 @@
         testStates(aNodeOrID, STATE_INVALID);
       };
 
       this.getID = function invalidInput_getID() {
         return prettyName(aNodeOrID) + " became invalid";
       };
     }
 
+    function changeCheckInput(aID, aIsChecked)
+    {
+      this.DOMNode = getNode(aID);
+
+      this.eventSeq = [
+        new stateChangeChecker(STATE_CHECKED, false, aIsChecked, this.DOMNode)
+      ];
+
+      this.invoke = function changeCheckInput_invoke()
+      {
+        this.DOMNode.checked = aIsChecked;
+      }
+
+      this.getID = function changeCheckInput_getID()
+      {
+        return "change checked state to '" + aIsChecked + "' for " +
+          prettyName(aID);
+      }
+    }
+
     function stateChangeOnFileInput(aID, aAttr, aValue,
                                     aState, aIsExtraState, aIsEnabled)
     {
       this.fileControlNode = getNode(aID);
       this.fileControl = getAccessible(this.fileControlNode);
       this.browseButton = this.fileControl.firstChild;
       // No state change events on the label.
 
@@ -149,16 +169,22 @@
 
       // Test delayed editable state change
       var doc = document.getElementById("iframe").contentDocument;
       gQueue.push(new makeEditableDoc(doc));
 
       // invalid state change
       gQueue.push(new invalidInput("email"));
 
+      // checked state change
+      gQueue.push(new changeCheckInput("checkbox", true));
+      gQueue.push(new changeCheckInput("checkbox", false));
+      gQueue.push(new changeCheckInput("radio", true));
+      gQueue.push(new changeCheckInput("radio", false));
+
       // file input inherited state changes
       gQueue.push(new stateChangeOnFileInput("file", "aria-busy", "true",
                                              STATE_BUSY, false, true));
       gQueue.push(new stateChangeOnFileInput("file", "aria-required", "true",
                                              STATE_REQUIRED, false, true));
       gQueue.push(new stateChangeOnFileInput("file", "aria-invalid", "true",
                                              STATE_INVALID, false, true));
 
@@ -175,39 +201,47 @@
   </script>
 </head>
 
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=564471"
      title="Make state change events async">
-    Mozilla Bug 564471
-  </a><br>
+    Bug 564471
+  </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=555728"
      title="Fire a11y event based on HTML5 constraint validation">
-    Mozilla Bug 555728
+    Bug 555728
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=699017"
      title="File input control should be propogate states to descendants">
-    Mozilla Bug 699017
+    Bug 699017
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=788389"
+     title="Fire statechange event whenever checked state is changed not depending on focused state">
+    Bug 788389
   </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="testContainer">
     <iframe id="iframe"></iframe>
   </div>
 
   <input id="email" type='email'>
 
+  <input id="checkbox" type="checkbox">
+  <input id="radio" type="radio">
+
   <input id="file" type="file">
 
   <div id="div"></div>
 
   <div id="eventdump"></div>
 </body>
 </html>
--- a/accessible/tests/mochitest/states/test_aria.html
+++ b/accessible/tests/mochitest/states/test_aria.html
@@ -136,16 +136,47 @@
                  STATE_READONLY, 0);
       testStates("aria_grid_readonly_rowheader_inherited", STATE_READONLY, 0,
                  0, EXT_STATE_EDITABLE);
       testStates("aria_grid_readonly_cell_editable", 0, EXT_STATE_EDITABLE,
                  STATE_READONLY, 0);
       testStates("aria_grid_readonly_cell_inherited", STATE_READONLY, 0,
                  0, EXT_STATE_EDITABLE);
 
+      // readonly/editable on treegrid and gridcell
+      testStates("aria_treegrid_default", 0, EXT_STATE_EDITABLE,
+                 STATE_READONLY, 0);
+      testStates("aria_treegrid_default_colheader_readonly", STATE_READONLY, 0,
+                 0, EXT_STATE_EDITABLE);
+      testStates("aria_treegrid_default_colheader_inherited", 0, EXT_STATE_EDITABLE,
+                 STATE_READONLY, 0);
+      testStates("aria_treegrid_default_rowheader_readonly", STATE_READONLY, 0,
+                 0, EXT_STATE_EDITABLE);
+      testStates("aria_treegrid_default_rowheader_inherited", 0, EXT_STATE_EDITABLE,
+                 STATE_READONLY, 0);
+      testStates("aria_treegrid_default_cell_readonly", STATE_READONLY, 0,
+                 0, EXT_STATE_EDITABLE);
+      testStates("aria_treegrid_default_cell_inherited", 0, EXT_STATE_EDITABLE,
+                 STATE_READONLY, 0);
+
+      testStates("aria_treegrid_readonly", STATE_READONLY, 0,
+                 0, EXT_STATE_EDITABLE);
+      testStates("aria_treegrid_readonly_colheader_editable", 0, EXT_STATE_EDITABLE,
+                 STATE_READONLY, 0);
+      testStates("aria_treegrid_readonly_colheader_inherited", STATE_READONLY, 0,
+                 0, EXT_STATE_EDITABLE);
+      testStates("aria_treegrid_readonly_rowheader_editable", 0, EXT_STATE_EDITABLE,
+                 STATE_READONLY, 0);
+      testStates("aria_treegrid_readonly_rowheader_inherited", STATE_READONLY, 0,
+                 0, EXT_STATE_EDITABLE);
+      testStates("aria_treegrid_readonly_cell_editable", 0, EXT_STATE_EDITABLE,
+                 STATE_READONLY, 0);
+      testStates("aria_treegrid_readonly_cell_inherited", STATE_READONLY, 0,
+                 0, EXT_STATE_EDITABLE);
+
       // aria-selectable
       testStates("aria_selectable_listitem", STATE_SELECTABLE | STATE_SELECTED);
 
       // active state caused by aria-activedescendant
       testStates("as_item1", 0, EXT_STATE_ACTIVE);
       testStates("as_item2", 0, 0, 0, EXT_STATE_ACTIVE);
 
       // universal ARIA properties inherited from file input control
@@ -268,16 +299,21 @@
     Mozilla Bug 740851
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=762876"
      title="fix default horizontal / vertical state of role=scrollbar and ensure only one of horizontal / vertical states is exposed">
     Mozilla Bug 762876
   </a>
   <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=892091"
+     title="ARIA treegrid should be editable by default">
+    Bug 892091
+  </a>
+  <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=835121"
      title="ARIA grid should be editable by default">
     Mozilla Bug 835121
   </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
@@ -354,16 +390,58 @@
     <div role="row">
       <div id="aria_grid_readonly_cell_editable"
            role="gridcell" aria-readonly="false">gridcell1</div>
       <div id="aria_grid_readonly_cell_inherited"
            role="gridcell">gridcell2</div>
     </div>
   </div>
 
+  <div id="aria_treegrid_default" role="grid">
+    <div role="row">
+      <div id="aria_treegrid_default_colheader_readonly"
+           role="columnheader" aria-readonly="true">colheader1</div>
+      <div id="aria_treegrid_default_colheader_inherited"
+           role="columnheader">colheader2</div>
+    </div>
+    <div role="row">
+      <div id="aria_treegrid_default_rowheader_readonly"
+           role="rowheader" aria-readonly="true">rowheader1</div>
+      <div id="aria_treegrid_default_rowheader_inherited"
+           role="rowheader">rowheader2</div>
+    </div>
+    <div role="row">
+      <div id="aria_treegrid_default_cell_readonly"
+           role="gridcell" aria-readonly="true">gridcell1</div>
+      <div id="aria_treegrid_default_cell_inherited"
+           role="gridcell">gridcell2</div>
+    </div>
+  </div>
+
+  <div id="aria_treegrid_readonly" role="grid" aria-readonly="true">
+    <div role="row">
+      <div id="aria_treegrid_readonly_colheader_editable"
+           role="columnheader" aria-readonly="false">colheader1</div>
+      <div id="aria_treegrid_readonly_colheader_inherited"
+           role="columnheader">colheader2</div>
+    </div>
+    <div role="row">
+      <div id="aria_treegrid_readonly_rowheader_editable"
+           role="rowheader" aria-readonly="false">rowheader1</div>
+      <div id="aria_treegrid_readonly_rowheader_inherited"
+           role="rowheader">rowheader2</div>
+    </div>
+    <div role="row">
+      <div id="aria_treegrid_readonly_cell_editable"
+           role="gridcell" aria-readonly="false">gridcell1</div>
+      <div id="aria_treegrid_readonly_cell_inherited"
+           role="gridcell">gridcell2</div>
+    </div>
+  </div>
+
   <div role="listbox">
     <div id="aria_selectable_listitem" role="option" aria-selected="true">Item1</div>
   </div>
 
   <!-- Test that aria-disabled state gets propagated to all descendants -->
   <div id="group" role="group" aria-disabled="true">
     <button>hi</button>
     <div tabindex="0" role="listbox" aria-activedescendant="item1">
--- a/accessible/tests/mochitest/test_aria_token_attrs.html
+++ b/accessible/tests/mochitest/test_aria_token_attrs.html
@@ -32,18 +32,18 @@ https://bugzilla.mozilla.org/show_bug.cg
       testStates("button_pressed_false", STATE_CHECKABLE, 0, STATE_PRESSED);
       testStates("button_pressed_empty", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
       testStates("button_pressed_undefined", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
       testStates("button_pressed_absent", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
 
       // test (checkbox) checkable and checked states
       testStates("checkbox_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
       testStates("checkbox_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
-      testStates("checkbox_checked_empty", 0 , 0, STATE_CHECKABLE | STATE_CHECKED);
-      testStates("checkbox_checked_undefined", 0, 0, STATE_CHECKABLE | STATE_CHECKED);
+      testStates("checkbox_checked_empty", STATE_CHECKABLE , 0, STATE_CHECKED);
+      testStates("checkbox_checked_undefined", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("checkbox_checked_absent", STATE_CHECKABLE, 0, STATE_CHECKED);
 
       // test native checkbox checked state and aria-checked state (if conflict, native wins)
       testStates("native_checkbox_nativechecked_ariatrue", (STATE_CHECKABLE | STATE_CHECKED));
       testStates("native_checkbox_nativechecked_ariafalse", (STATE_CHECKABLE | STATE_CHECKED));
       testStates("native_checkbox_nativechecked_ariaempty", (STATE_CHECKABLE | STATE_CHECKED));
       testStates("native_checkbox_nativechecked_ariaundefined", (STATE_CHECKABLE | STATE_CHECKED));
       testStates("native_checkbox_nativechecked_ariaabsent", (STATE_CHECKABLE | STATE_CHECKED));
@@ -101,39 +101,39 @@ https://bugzilla.mozilla.org/show_bug.cg
       testStates("menuitem_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("menuitem_checked_empty", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
       testStates("menuitem_checked_undefined", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
       testStates("menuitem_checked_absent", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
 
       // test (menuitemradio) checkable and checked states
       testStates("menuitemradio_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
       testStates("menuitemradio_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
-      testStates("menuitemradio_checked_empty", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
-      testStates("menuitemradio_checked_undefined", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
+      testStates("menuitemradio_checked_empty", STATE_CHECKABLE, 0, STATE_CHECKED);
+      testStates("menuitemradio_checked_undefined", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("menuitemradio_checked_absent", STATE_CHECKABLE, 0, STATE_CHECKED);
 
       // test (radio) checkable and checked states
       testStates("radio_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
       testStates("radio_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
-      testStates("radio_checked_empty", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
-      testStates("radio_checked_undefined", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
+      testStates("radio_checked_empty", STATE_CHECKABLE, 0, STATE_CHECKED);
+      testStates("radio_checked_undefined", STATE_CHECKABLE, 0, STATE_CHECKED);
       testStates("radio_checked_absent", STATE_CHECKABLE, 0, STATE_CHECKED);
 
       // test (textbox) multiline states
       testStates("textbox_multiline_true", 0, EXT_STATE_MULTI_LINE);
       testStates("textbox_multiline_false", 0, EXT_STATE_SINGLE_LINE);
-      testStates("textbox_multiline_empty", 0, 0, 0, EXT_STATE_SINGLE_LINE | EXT_STATE_MULTI_LINE);
-      testStates("textbox_multiline_undefined", 0, 0, 0, EXT_STATE_SINGLE_LINE | EXT_STATE_MULTI_LINE);
+      testStates("textbox_multiline_empty", 0, EXT_STATE_SINGLE_LINE);
+      testStates("textbox_multiline_undefined", 0, EXT_STATE_SINGLE_LINE);
       testStates("textbox_multiline_absent", 0, EXT_STATE_SINGLE_LINE);
 
       // test (textbox) readonly states
       testStates("textbox_readonly_true", STATE_READONLY);
       testStates("textbox_readonly_false", 0, EXT_STATE_EDITABLE, STATE_READONLY);
-      testStates("textbox_readonly_empty", 0, 0, STATE_READONLY, EXT_STATE_EDITABLE);
-      testStates("textbox_readonly_undefined", 0, 0, STATE_READONLY, EXT_STATE_EDITABLE);
+      testStates("textbox_readonly_empty", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+      testStates("textbox_readonly_undefined", 0, EXT_STATE_EDITABLE, STATE_READONLY);
       testStates("textbox_readonly_absent", 0, EXT_STATE_EDITABLE, STATE_READONLY);
 
       // test native textbox readonly state and aria-readonly state (if conflict, native wins)
       testStates("native_textbox_nativereadonly_ariatrue", STATE_READONLY);
       testStates("native_textbox_nativereadonly_ariafalse", STATE_READONLY);
       testStates("native_textbox_nativereadonly_ariaempty", STATE_READONLY);
       testStates("native_textbox_nativereadonly_ariaundefined", STATE_READONLY);
       testStates("native_textbox_nativereadonly_ariaabsent", STATE_READONLY);
@@ -142,18 +142,18 @@ https://bugzilla.mozilla.org/show_bug.cg
       testStates("native_textbox_nativeeditable_ariafalse", 0, 0, STATE_READONLY);
       testStates("native_textbox_nativeeditable_ariaempty", 0, 0, STATE_READONLY);
       testStates("native_textbox_nativeeditable_ariaundefined", 0, 0, STATE_READONLY);
       testStates("native_textbox_nativeeditable_ariaabsent", 0, 0, STATE_READONLY);
 
       // test (treeitem) selectable and selected states
       testStates("treeitem_selected_true", (STATE_SELECTABLE | STATE_SELECTED));
       testStates("treeitem_selected_false", STATE_SELECTABLE, 0, STATE_SELECTED);
-      testStates("treeitem_selected_empty", 0, 0, (STATE_SELECTABLE | STATE_SELECTED));
-      testStates("treeitem_selected_undefined", 0, 0, (STATE_SELECTABLE | STATE_SELECTED));
+      testStates("treeitem_selected_empty", STATE_SELECTABLE, 0, STATE_SELECTED);
+      testStates("treeitem_selected_undefined", STATE_SELECTABLE, 0, STATE_SELECTED);
       testStates("treeitem_selected_absent", STATE_SELECTABLE, 0, STATE_SELECTED);
 
       // test (treeitem) haspopup states
       testStates("treeitem_haspopup_true", STATE_HASPOPUP);
       testStates("treeitem_haspopup_false", 0, 0, STATE_HASPOPUP);
       testStates("treeitem_haspopup_empty", 0, 0, STATE_HASPOPUP);
       testStates("treeitem_haspopup_undefined", 0, 0, STATE_HASPOPUP);
       testStates("treeitem_haspopup_absent", 0, 0, STATE_HASPOPUP);
--- a/addon-sdk/source/app-extension/bootstrap.js
+++ b/addon-sdk/source/app-extension/bootstrap.js
@@ -215,18 +215,16 @@ function startup(data, reasonCode) {
       loadReason: reason,
 
       prefixURI: prefixURI,
       // Add-on URI.
       rootURI: rootURI,
       // options used by system module.
       // File to write 'OK' or 'FAIL' (exit code emulation).
       resultFile: options.resultFile,
-      // File to write stdout.
-      logFile: options.logFile,
       // Arguments passed as --static-args
       staticArgs: options.staticArgs,
 
       // Arguments related to test runner.
       modules: {
         '@test/options': {
           allTestModules: options.allTestModules,
           iterations: options.iterations,
--- a/addon-sdk/source/app-extension/install.rdf
+++ b/addon-sdk/source/app-extension/install.rdf
@@ -12,18 +12,18 @@
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:unpack>false</em:unpack>
 
     <!-- Firefox -->
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
-        <em:minVersion>19.0</em:minVersion>
-        <em:maxVersion>22.0a1</em:maxVersion>
+        <em:minVersion>21.0</em:minVersion>
+        <em:maxVersion>25.0a1</em:maxVersion>
       </Description>
     </em:targetApplication>
 
     <!-- Front End MetaData -->
     <em:name>Test App</em:name>
     <em:description>Harness for tests.</em:description>
     <em:creator>Mozilla Corporation</em:creator>
     <em:homepageURL></em:homepageURL>
--- a/addon-sdk/source/doc/dev-guide-source/cfx-tool.md
+++ b/addon-sdk/source/doc/dev-guide-source/cfx-tool.md
@@ -635,25 +635,24 @@ of `updateURL`.
 
 Note that as the [add-on documentation](https://developer.mozilla.org/en/extension_versioning,_update_and_compatibility#Securing_Updates)
 explains, you should make sure the update procedure for your add-on is secure,
 and this usually involves using HTTPS for the links.
 
 So if we run the following command:
 
 <pre>
-  cfx xpi --update-link https://example.com/addon/latest
-          --update-url https://example.com/addon/update_rdf
+  cfx xpi --update-link https://example.com/addon/latest/pluginName.xpi --update-url https://example.com/addon/update_rdf/pluginName.update.rdf
 </pre>
 
 `cfx` will create two files:
 
 * an XPI file which embeds
-`https://example.com/addon/update_rdf` as the value of `updateURL`
-* an RDF file which embeds `https://example.com/addon/latest` as the value of
+`https://example.com/addon/update_rdf/pluginName.update.rdf` as the value of `updateURL`
+* an RDF file which embeds `https://example.com/addon/latest/pluginName.xpi` as the value of
 `updateLink`.
 
 ### Supported Options ###
 
 As with `cfx run` you can point `cfx` at a different `package.json` file using
 the `--pkgdir` option. You can also embed arguments in the XPI using the
 `--static-args` option: if you do this the arguments will be passed to your
 add-on whenever it is run.
--- a/addon-sdk/source/doc/module-source/sdk/self.md
+++ b/addon-sdk/source/doc/module-source/sdk/self.md
@@ -8,16 +8,23 @@ The `self` module provides access to dat
 as a whole. It also provides access to the
 [Program ID](dev-guide/guides/program-id.html), a value which is
 unique for each add-on.
 
 Note that the `self` module is completely different from the global `self`
 object accessible to content scripts, which is used by a content script to
 [communicate with the add-on code](dev-guide/guides/content-scripts/using-port.html).
 
+<api name="uri">
+@property {string}
+This property represents an add-on associated unique URI string.
+This URI can be used for APIs which require a valid URI string, such as the
+[passwords](modules/sdk/passwords.html) module.
+</api>
+
 <api name="id">
 @property {string}
 This property is a printable string that is unique for each add-on. It comes
 from the `id` property set in the `package.json` file in the main package
 (i.e. the package in which you run `cfx xpi`). While not generally of use to
 add-on code directly, it can be used by internal API code to index local
 storage and other resources that are associated with a particular add-on.
 Eventually, this ID will be unspoofable (see
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/doc/module-source/sdk/test/utils.md
@@ -0,0 +1,90 @@
+<!-- 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/. -->
+
+The `test/utils` module provides additional helper methods to be used in
+the CommonJS Unit Testing test suite.
+
+## Before and After
+
+Helper functions `before()` and `after()` are available for running a function
+before or after each test in a suite. They're useful when you need to
+guarantee a particular state before running a test, and to clean up
+after your test.
+
+    let { before, after } = require('sdk/test/utils');
+    let { search } = require('sdk/places/bookmarks');
+
+    exports.testCountBookmarks = function (assert, done) {
+      search().on('end', function (results) {
+        assert.equal(results, 0, 'should be no bookmarks');
+        done();
+      });
+    };
+
+    before(exports, function (name, assert) {
+      removeAllBookmarks();
+    });
+
+    require('sdk/test').run(exports);
+
+Both `before` and `after` may be asynchronous. To make them asynchronous,
+pass a third argument `done`, which is a function to call when you have
+finished:
+
+    let { before, after } = require('sdk/test/utils');
+    let { search } = require('sdk/places/bookmarks');
+
+    exports.testCountBookmarks = function (assert, done) {
+      search().on('end', function (results) {
+        assert.equal(results, 0, 'should be no bookmarks');
+        done();
+      });
+    };
+
+    before(exports, function (name, assert, done) {
+      removeAllBookmarksAsync(function () {
+        done();
+      });
+    });
+
+    require('sdk/test').run(exports);
+
+<api name="before">
+@function
+  Runs `beforeFn` before each test in the file. May be asynchronous
+  if `beforeFn` accepts a third argument, which is a callback.
+
+ @param exports {Object}
+    A test file's `exports` object
+ @param beforeFn {Function}
+    The function to be called before each test. It has two arguments,
+    or three if it is asynchronous:
+
+   * the first argument is the test's name as a `String`.
+   * the second argument is the `assert` object for the test.
+   * the third, optional, argument is a callback. If the callback is
+    defined, then the `beforeFn` is considered asynchronous, and the
+    callback must be invoked before the test runs.
+
+</api>
+
+<api name="after">
+@function
+  Runs `afterFn` after each test in the file. May be asynchronous
+  if `afterFn` accepts a third argument, which is a callback.
+
+ @param exports {Object}
+    A test file's `exports` object
+ @param afterFn {Function}
+    The function to be called after each test. It has two arguments,
+    or three if it is asynchronous:
+
+   * the first argument is the test's name as a `String`.
+   * the second argument is the `assert` object for the test.
+   * the third, optional, argument is a callback. If the callback is
+    defined, then the `afterFn` is considered asynchronous, and the
+    callback must be invoked before the next test runs.
+
+</api>
+
--- a/addon-sdk/source/doc/module-source/sdk/util/array.md
+++ b/addon-sdk/source/doc/module-source/sdk/util/array.md
@@ -1,13 +1,13 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
-The `util/array` module provides simple helper functions for working with 
+The `util/array` module provides simple helper functions for working with
 arrays.
 
 <api name="has">
 @function
 Returns `true` if the given [`Array`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) contains the element or `false` otherwise.
 A simplified version of `array.indexOf(element) >= 0`.
 
     let { has } = require('sdk/util/array');
@@ -24,17 +24,17 @@ A simplified version of `array.indexOf(e
   The element to search for in the array.
 
 @returns {boolean}
   A boolean indicating whether or not the element was found in the array.
 </api>
 
 <api name="hasAny">
 @function
-Returns `true` if the given [`Array`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) contains any of the elements in the 
+Returns `true` if the given [`Array`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) contains any of the elements in the
 `elements` array, or `false` otherwise.
 
     let { hasAny } = require('sdk/util/array');
     let a = ['rock', 'roll', 100];
 
     hasAny(a, ['rock', 'bright', 'light']); // true
     hasAny(a, ['rush', 'coil', 'jet']); // false
 
@@ -79,17 +79,17 @@ does not add the element and returns `fa
 If the given [array](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) contains the given element, this function
 removes the element from the array and returns `true`. Otherwise, this function
 does not alter the array and returns `false`.
 
     let { remove } = require('sdk/util/array');
     let a = ['alice', 'bob', 'carol'];
 
     remove(a, 'dave'); // false
-    remove(a, 'bob'); // true 
+    remove(a, 'bob'); // true
     remove(a, 'bob'); // false
 
     console.log(a); // ['alice', 'carol']
 
 @param array {array}
   The array to remove the element from.
 
 @param element {*}
@@ -149,8 +149,29 @@ Iterates over an [iterator](https://deve
 
 @param iterator {iterator}
   The [`Iterator`](https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Iterators_and_Generators#Iterators) object over which to iterate and place results into an array.
 
 @returns {array}
   The iterator's results in an array.
 </api>
 
+<api name="find">
+@function
+Iterates over given `array` and applies given `predicate` function until
+`predicate(element)` is `true`. If such element is found it's retured back
+otherwise third optional `fallback` argument is returned back. If fallback
+is not provided returns `undefined`.
+
+    let { find } = require('sdk/util/array');
+    let isOdd = (x) => x % 2;
+    find([2, 4, 5, 7, 8, 9], isOdd);   // => 5
+    find([2, 4, 6, 8], isOdd);         // => undefiend
+    find([2, 4, 6, 8], isOdd, null);   // => null
+
+    fromIterator(i) // ['otoro', 'unagi', 'keon']
+
+@param iterator {iterator}
+  The [`Iterator`](https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Iterators_and_Generators#Iterators) object over which to iterate and place results into an array.
+
+@returns {array}
+  The iterator's results in an array.
+</api>
--- a/addon-sdk/source/lib/sdk/addon/installer.js
+++ b/addon-sdk/source/lib/sdk/addon/installer.js
@@ -1,14 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 module.metadata = {
-  "stability": "experimental"
+  "stability": "experimental",
+  "engines": {
+    // TODO Fennec Support in bug 894515
+    "Firefox": "*"
+  }
 };
 
 const { Cc, Ci, Cu } = require("chrome");
 const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm");
 const { defer } = require("../core/promise");
 const { setTimeout } = require("../timers");
 
 /**
--- a/addon-sdk/source/lib/sdk/clipboard.js
+++ b/addon-sdk/source/lib/sdk/clipboard.js
@@ -2,17 +2,21 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
-  "stability": "stable"
+  "stability": "stable",
+  "engines": {
+    // TODO Fennec Support 789757
+    "Firefox": "*"
+  }
 };
 
 const { Cc, Ci } = require("chrome");
 const { DataURL } = require("./url");
 const errors = require("./deprecated/errors");
 const apiUtils = require("./deprecated/api-utils");
 /*
 While these data flavors resemble Internet media types, they do
--- a/addon-sdk/source/lib/sdk/console/plain-text.js
+++ b/addon-sdk/source/lib/sdk/console/plain-text.js
@@ -3,134 +3,77 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-const { Cc, Ci } = require("chrome");
+const { Cc, Ci, Cu, Cr } = require("chrome");
 const self = require("../self");
-const traceback = require("./traceback")
 const prefs = require("../preferences/service");
 const { merge } = require("../util/object");
-const { partial } = require("../lang/functional");
+const { ConsoleAPI } = Cu.import("resource://gre/modules/devtools/Console.jsm");
 
-const LEVELS = {
-  "all": Number.MIN_VALUE,
-  "debug": 20000,
-  "info": 30000,
-  "warn": 40000,
-  "error": 50000,
-  "off": Number.MAX_VALUE,
-};
 const DEFAULT_LOG_LEVEL = "error";
 const ADDON_LOG_LEVEL_PREF = "extensions." + self.id + ".sdk.console.logLevel";
 const SDK_LOG_LEVEL_PREF = "extensions.sdk.console.logLevel";
 
-let logLevel;
-
+let logLevel = DEFAULT_LOG_LEVEL;
 function setLogLevel() {
-  logLevel = prefs.get(ADDON_LOG_LEVEL_PREF, prefs.get(SDK_LOG_LEVEL_PREF,
-                                                       DEFAULT_LOG_LEVEL));
+  logLevel = prefs.get(ADDON_LOG_LEVEL_PREF, 
+                           prefs.get(SDK_LOG_LEVEL_PREF,
+                                     DEFAULT_LOG_LEVEL));
 }
 setLogLevel();
 
 let logLevelObserver = {
+  QueryInterface: function(iid) {
+    if (!iid.equals(Ci.nsIObserver) &&
+        !iid.equals(Ci.nsISupportsWeakReference) &&
+        !iid.equals(Ci.nsISupports))
+      throw Cr.NS_ERROR_NO_INTERFACE;
+    return this;
+  },
   observe: function(subject, topic, data) {
     setLogLevel();
   }
 };
 let branch = Cc["@mozilla.org/preferences-service;1"].
              getService(Ci.nsIPrefService).
              getBranch(null);
-branch.addObserver(ADDON_LOG_LEVEL_PREF, logLevelObserver, false);
-branch.addObserver(SDK_LOG_LEVEL_PREF, logLevelObserver, false);
-
-function stringify(arg) {
-  try {
-    return String(arg);
-  }
-  catch(ex) {
-    return "<toString() error>";
-  }
-}
-
-function stringifyArgs(args) {
-  return Array.map(args, stringify).join(" ");
-}
-
-function message(print, level) {
-  if (LEVELS[level] < LEVELS[logLevel])
-    return;
-
-  let args = Array.slice(arguments, 2);
-
-  print(level + ": " + self.name + ": " + stringifyArgs(args) + "\n", level);
-}
-
-function errorMessage(print, e) {
-  // Some platform exception doesn't have name nor message but
-  // can be stringified to a meaningfull message
-  var fullString = ("An exception occurred.\n" +
-                   (e.name ? e.name + ": " : "") +
-                   (e.message ? e.message : e.toString()) + "\n" +
-                   (e.fileName ? traceback.sourceURI(e.fileName) + " " +
-                                 e.lineNumber + "\n"
-                               : "") +
-                   traceback.format(e));
-
-  message(print, "error", fullString);
-}
-
-function traceMessage(print) {
-  var stack = traceback.get();
-  stack.splice(-1, 1);
-
-  message(print, "info", traceback.format(stack));
-}
+branch.addObserver(ADDON_LOG_LEVEL_PREF, logLevelObserver, true);
+branch.addObserver(SDK_LOG_LEVEL_PREF, logLevelObserver, true);
 
 function PlainTextConsole(print) {
-  if (!print)
-    print = dump;
-
-  if (print === dump) {
-    // If we're just using dump(), auto-enable preferences so
-    // that the developer actually sees the console output.
-    var prefs = Cc["@mozilla.org/preferences-service;1"]
-                .getService(Ci.nsIPrefBranch);
-    prefs.setBoolPref("browser.dom.window.dump.enabled", true);
-  }
 
-  merge(this, {
-    log: partial(message, print, "info"),
-    info: partial(message, print, "info"),
-    warn: partial(message, print, "warn"),
-    error: partial(message, print, "error"),
-    debug: partial(message, print, "debug"),
-    exception: partial(errorMessage, print),
-    trace: partial(traceMessage, print),
+  let consoleOptions = {
+    prefix: self.name + ": ",
+    maxLogLevel: logLevel,
+    dump: print
+  };
+  let console = new ConsoleAPI(consoleOptions);
 
-    dir: function dir() {},
-    group: function group() {},
-    groupCollapsed: function groupCollapsed() {},
-    groupEnd: function groupEnd() {},
-    time: function time() {},
-    timeEnd: function timeEnd() {}
+  // As we freeze the console object, we can't modify this property afterward
+  Object.defineProperty(console, "maxLogLevel", {
+    get: function() {
+      return logLevel;
+    }
   });
 
   // We defined the `__exposedProps__` in our console chrome object.
   // Although it seems redundant, because we use `createObjectIn` too, in
   // worker.js, we are following what `ConsoleAPI` does. See:
   // http://mxr.mozilla.org/mozilla-central/source/dom/base/ConsoleAPI.js#132
   //
   // Meanwhile we're investigating with the platform team if `__exposedProps__`
   // are needed, or are just a left-over.
 
-  this.__exposedProps__ = Object.keys(this).reduce(function(exposed, prop) {
+  console.__exposedProps__ = Object.keys(ConsoleAPI.prototype).reduce(function(exposed, prop) {
     exposed[prop] = "r";
     return exposed;
   }, {});
 
-  Object.freeze(this);
+  Object.freeze(console);
+  return console;
 };
 exports.PlainTextConsole = PlainTextConsole;
--- a/addon-sdk/source/lib/sdk/context-menu.js
+++ b/addon-sdk/source/lib/sdk/context-menu.js
@@ -1,15 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
-  "stability": "stable"
+  "stability": "stable",
+  "engines": {
+    // TODO Fennec support Bug 788334
+    "Firefox": "*"
+  }
 };
 
 const { Class, mix } = require("./core/heritage");
 const { addCollectionProperty } = require("./util/collection");
 const { ns } = require("./core/namespace");
 const { validateOptions, getTypeOf } = require("./deprecated/api-utils");
 const { URL, isValidURI } = require("./url");
 const { WindowTracker, browserWindowIterator } = require("./deprecated/window-utils");
--- a/addon-sdk/source/lib/sdk/indexed-db.js
+++ b/addon-sdk/source/lib/sdk/indexed-db.js
@@ -48,15 +48,8 @@ let principal = Cc["@mozilla.org/scripts
 exports.indexedDB = Object.freeze({
   open: indexedDB.openForPrincipal.bind(indexedDB, principal),
   deleteDatabase: indexedDB.deleteForPrincipal.bind(indexedDB, principal),
   cmp: indexedDB.cmp
 });
 
 exports.IDBKeyRange = IDBKeyRange;
 exports.DOMException = Ci.nsIDOMDOMException;
-exports.IDBCursor = Ci.nsIIDBCursor;
-exports.IDBTransaction = Ci.nsIIDBTransaction;
-exports.IDBOpenDBRequest = Ci.nsIIDBOpenDBRequest;
-exports.IDBDatabase = Ci.nsIIDBDatabase;
-exports.IDBIndex = Ci.nsIIDBIndex;
-exports.IDBObjectStore = Ci.nsIIDBObjectStore;
-exports.IDBRequest = Ci.nsIIDBRequest;
--- a/addon-sdk/source/lib/sdk/io/buffer.js
+++ b/addon-sdk/source/lib/sdk/io/buffer.js
@@ -4,79 +4,257 @@
  */
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 
-const { Cc, Ci, CC } = require("chrome");
-const { Class } = require("../core/heritage");
+const { Cu } = require("chrome");
+const { TextEncoder, TextDecoder } = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
 
-const Transcoder = CC("@mozilla.org/intl/scriptableunicodeconverter",
-                      "nsIScriptableUnicodeConverter");
+exports.TextEncoder = TextEncoder;
+exports.TextDecoder = TextDecoder;
+
 
-var Buffer = Class({
-  initialize: function initialize(subject, encoding) {
-    subject = subject ? subject.valueOf() : 0;
-    let length = typeof subject === "number" ? subject : 0;
-    this.encoding = encoding || "utf-8";
-    this.valueOf(Array.isArray(subject) ? subject : new Array(length));
+function Buffer(subject, encoding) {
+    var type = typeof(subject);
+    switch (type) {
+      case "number":
+        // Create typed array of the given size if number.
+        return Uint8Array(subject > 0 ? Math.floor(subject) : 0);
+      case "string":
+        // If string encode it and use buffer for the returned Uint8Array
+        // to create a local patched version that acts like node buffer.
+        encoding = encoding || "utf8";
+        return Uint8Array(TextEncoder(encoding).encode(subject).buffer);
+      case "object":
+        // If array or alike just make a copy with a local patched prototype.
+        return Uint8Array(subject);
+     default:
+        throw new TypeError("must start with number, buffer, array or string");
+    }
+}
+exports.Buffer = Buffer;
+
+// Tests if `value` is a Buffer.
+Buffer.isBuffer = value => value instanceof Buffer
+
+// Returns true if the encoding is a valid encoding argument & false otherwise
+Buffer.isEncoding = encoding => !!ENCODINGS[String(encoding).toLowerCase()]
+
+// Gives the actual byte length of a string. encoding defaults to 'utf8'.
+// This is not the same as String.prototype.length since that returns the
+// number of characters in a string.
+Buffer.byteLength = (value, encoding = "utf8") =>
+  TextEncoder(encoding).encode(value).byteLength
 
-    if (typeof subject === "string") this.write(subject);
-  },
-  get length() {
-    return this.valueOf().length;
-  },
-  get: function get(index) {
-    return this.valueOf()[index];
-  },
-  set: function set(index, value) {
-    return this.valueOf()[index] = value;
-  },
-  valueOf: function valueOf(value) {
-    Object.defineProperty(this, "valueOf", {
-      value: Array.prototype.valueOf.bind(value),
-      configurable: false,
-      writable: false,
-      enumerable: false
-    });
+// Direct copy of the nodejs's buffer implementation:
+// https://github.com/joyent/node/blob/b255f4c10a80343f9ce1cee56d0288361429e214/lib/buffer.js#L146-L177
+Buffer.concat = function(list, length) {
+  if (!Array.isArray(list))
+    throw new TypeError('Usage: Buffer.concat(list[, length])');
+
+  if (typeof length === 'undefined') {
+    length = 0;
+    for (var i = 0; i < list.length; i++)
+      length += list[i].length;
+  } else {
+    length = ~~length;
+  }
+
+  if (length < 0)
+    length = 0;
+
+  if (list.length === 0)
+    return new Buffer(0);
+  else if (list.length === 1)
+    return list[0];
+
+  if (length < 0)
+    throw new RangeError('length is not a positive number');
+
+  var buffer = new Buffer(length);
+  var pos = 0;
+  for (var i = 0; i < list.length; i++) {
+    var buf = list[i];
+    buf.copy(buffer, pos);
+    pos += buf.length;
+  }
+
+  return buffer;
+};
+
+// Node buffer is very much like Uint8Array although it has bunch of methods
+// that typically can be used in combination with `DataView` while preserving
+// access by index. Since in SDK echo module has it's own set of bult-ins it
+// ok to patch ours to make it nodejs Buffer compatible.
+Buffer.prototype = Uint8Array.prototype;
+Object.defineProperties(Buffer.prototype, {
+  view: {
+    get: function() this._view || (this._view = DataView(this.buffer))
   },
-  toString: function toString(encoding, start, end) {
-    let bytes = this.valueOf().slice(start || 0, end || this.length);
-    let transcoder = Transcoder();
-    transcoder.charset = String(encoding || this.encoding).toUpperCase();
-    return transcoder.convertFromByteArray(bytes, this.length);
+  toString: {
+    value: function(encoding, start, end) {
+      encoding = !!encoding ? (encoding + '').toLowerCase() : "utf8";
+      start = Math.max(0, ~~start);
+      end = Math.min(this.length, end === void(0) ? this.length : ~~end);
+      return TextDecoder(encoding).decode(this.subarray(start, end));
+    }
+  },
+  toJSON: {
+    value: function() ({ type: "Buffer", data: Array.slice(this, 0) })
   },
-  toJSON: function toJSON() {
-    return this.toString()
+  get: {
+    value: function(offset) this[offset]
+  },
+  set: {
+    value: function(offset, value) this[offset] = value
+  },
+  copy: {
+    value: function(target, offset, start, end)
+      Uint8Array.set(target, this.subarray(start, end), offset)
+  },
+  slice: {
+    value: Buffer.prototype.subarray
   },
-  write: function write(string, offset, encoding) {
-    offset = Math.max(offset || 0, 0);
-    let value = this.valueOf();
-    let transcoder = Transcoder();
-    transcoder.charset = String(encoding || this.encoding).toUpperCase();
-    let bytes = transcoder.convertToByteArray(string, {});
-    value.splice.apply(value, [
-      offset,
-      Math.min(value.length - offset, bytes.length, bytes)
-    ].concat(bytes));
-    return bytes;
+  write: {
+    value: function(string, offset, length, encoding = "utf8") {
+      if (typeof(offset) === "string")
+        ([offset, length, encoding]) = [0, null, offset];
+      else if (typeof(length) === "string")
+        ([length, encoding]) = [null, length];
+
+      offset = ~~offset;
+      length = length || this.length - offset;
+      let buffer = TextEncoder(encoding).encode(string);
+      let result = Math.min(buffer.length, length);
+      if (buffer.length !== length)
+        buffer = buffer.subarray(0, length);
+      Uint8Array.set(this, buffer, offset);
+      return result;
+    }
   },
-  slice: function slice(start, end) {
-    return new Buffer(this.valueOf().slice(start, end));
-  },
-  copy: function copy(target, offset, start, end) {
-    offset = Math.max(offset || 0, 0);
-    target = target.valueOf();
-    let bytes = this.valueOf();
-    bytes.slice(Math.max(start || 0, 0), end);
-    target.splice.apply(target, [
-      offset,
-      Math.min(target.length - offset, bytes.length),
-    ].concat(bytes));
+  fill: {
+    value: function fill(value, start, end) {
+      value = value || 0;
+      start = start || 0;
+      end = end || this.length;
+
+      if (typeof(value) === "string")
+        value = value.charCodeAt(0);
+      if (typeof(value) !== "number" || isNaN(value))
+        throw TypeError("value is not a number");
+      if (end < start)
+        throw new RangeError("end < start");
+
+      // Fill 0 bytes; we're done
+      if (end === start)
+        return 0;
+      if (this.length == 0)
+        return 0;
+
+      if (start < 0 || start >= this.length)
+        throw RangeError("start out of bounds");
+
+      if (end < 0 || end > this.length)
+        throw RangeError("end out of bounds");
+
+      let index = start;
+      while (index < end) this[index++] = value;
+    }
   }
 });
-Buffer.isBuffer = function isBuffer(buffer) {
-  return buffer instanceof Buffer
-};
-exports.Buffer = Buffer;
+
+// Define nodejs Buffer's getter and setter functions that just proxy
+// to internal DataView's equivalent methods.
+[["readUInt16LE", "getUint16", true],
+ ["readUInt16BE", "getUint16", false],
+ ["readInt16LE", "getInt16", true],
+ ["readInt16BE", "getInt16", false],
+ ["readUInt32LE", "getInt32", true],
+ ["readUInt32BE", "getInt32", false],
+ ["readInt32LE", "getInt32", true],
+ ["readInt32BE", "getInt32", false],
+ ["readFloatLE", "getFloat32", true],
+ ["readFloatBE", "getFloat32", false],
+ ["readDoubleLE", "getFloat64", true],
+ ["readDoubleBE", "getFloat64", false],
+ ["readUInt8", "getUint8"],
+ ["readInt8", "getInt8"]].forEach(([alias, name, littleEndian]) => {
+  Object.defineProperty(Buffer.prototype, alias, {
+    value: function(offset) this.view[name](offset, littleEndian)
+  });
+});
+
+[["writeUInt16LE", "setUint16", true],
+ ["writeUInt16BE", "setUint16", false],
+ ["writeInt16LE", "setInt16", true],
+ ["writeInt16BE", "setInt16", false],
+ ["writeUInt32LE", "setUint32", true],
+ ["writeUInt32BE", "setUint32", false],
+ ["writeInt32LE", "setInt32", true],
+ ["writeInt32BE", "setInt32", false],
+ ["writeFloatLE", "setFloat32", true],
+ ["writeFloatBE", "setFloat32", false],
+ ["writeDoubleLE", "setFloat64", true],
+ ["writeDoubleBE", "setFloat64", false],
+ ["writeUInt8", "setUint8"],
+ ["writeInt8", "setInt8"]].forEach(([alias, name, littleEndian]) => {
+  Object.defineProperty(Buffer.prototype, alias, {
+    value: function(value, offset) this.view[name](offset, value, littleEndian)
+  });
+});
+
+
+// List of supported encodings taken from:
+// http://mxr.mozilla.org/mozilla-central/source/dom/encoding/labelsencodings.properties
+const ENCODINGS = { "unicode-1-1-utf-8": 1, "utf-8": 1, "utf8": 1,
+  "866": 1, "cp866": 1, "csibm866": 1, "ibm866": 1, "csisolatin2": 1,
+  "iso-8859-2": 1, "iso-ir-101": 1, "iso8859-2": 1, "iso88592": 1,
+  "iso_8859-2": 1, "iso_8859-2:1987": 1, "l2": 1, "latin2": 1, "csisolatin3": 1,
+  "iso-8859-3": 1, "iso-ir-109": 1, "iso8859-3": 1, "iso88593": 1,
+  "iso_8859-3": 1, "iso_8859-3:1988": 1, "l3": 1, "latin3": 1, "csisolatin4": 1,
+  "iso-8859-4": 1, "iso-ir-110": 1, "iso8859-4": 1, "iso88594": 1,
+  "iso_8859-4": 1, "iso_8859-4:1988": 1, "l4": 1, "latin4": 1,
+  "csisolatincyrillic": 1, "cyrillic": 1, "iso-8859-5": 1, "iso-ir-144": 1,
+  "iso8859-5": 1, "iso88595": 1, "iso_8859-5": 1, "iso_8859-5:1988": 1,
+  "arabic": 1, "asmo-708": 1, "csiso88596e": 1, "csiso88596i": 1,
+  "csisolatinarabic": 1, "ecma-114": 1, "iso-8859-6": 1, "iso-8859-6-e": 1,
+  "iso-8859-6-i": 1, "iso-ir-127": 1, "iso8859-6": 1, "iso88596": 1,
+  "iso_8859-6": 1, "iso_8859-6:1987": 1, "csisolatingreek": 1, "ecma-118": 1,
+  "elot_928": 1, "greek": 1, "greek8": 1, "iso-8859-7": 1, "iso-ir-126": 1,
+  "iso8859-7": 1, "iso88597": 1, "iso_8859-7": 1, "iso_8859-7:1987": 1,
+  "sun_eu_greek": 1, "csiso88598e": 1, "csisolatinhebrew": 1, "hebrew": 1,
+  "iso-8859-8": 1, "iso-8859-8-e": 1, "iso-ir-138": 1, "iso8859-8": 1,
+  "iso88598": 1, "iso_8859-8": 1, "iso_8859-8:1988": 1, "visual": 1,
+  "csiso88598i": 1, "iso-8859-8-i": 1, "logical": 1, "csisolatin6": 1,
+  "iso-8859-10": 1, "iso-ir-157": 1, "iso8859-10": 1, "iso885910": 1,
+  "l6": 1, "latin6": 1, "iso-8859-13": 1, "iso8859-13": 1, "iso885913": 1,
+  "iso-8859-14": 1, "iso8859-14": 1, "iso885914": 1, "csisolatin9": 1,
+  "iso-8859-15": 1, "iso8859-15": 1, "iso885915": 1, "iso_8859-15": 1,
+  "l9": 1, "iso-8859-16": 1, "cskoi8r": 1, "koi": 1, "koi8": 1, "koi8-r": 1,
+  "koi8_r": 1, "koi8-u": 1, "csmacintosh": 1, "mac": 1, "macintosh": 1,
+  "x-mac-roman": 1, "dos-874": 1, "iso-8859-11": 1, "iso8859-11": 1,
+  "iso885911": 1, "tis-620": 1, "windows-874": 1, "cp1250": 1,
+  "windows-1250": 1, "x-cp1250": 1, "cp1251": 1, "windows-1251": 1,
+  "x-cp1251": 1, "ansi_x3.4-1968": 1, "ascii": 1, "cp1252": 1, "cp819": 1,
+  "csisolatin1": 1, "ibm819": 1, "iso-8859-1": 1, "iso-ir-100": 1,
+  "iso8859-1": 1, "iso88591": 1, "iso_8859-1": 1, "iso_8859-1:1987": 1,
+  "l1": 1, "latin1": 1, "us-ascii": 1, "windows-1252": 1, "x-cp1252": 1,
+  "cp1253": 1, "windows-1253": 1, "x-cp1253": 1, "cp1254": 1, "csisolatin5": 1,
+  "iso-8859-9": 1, "iso-ir-148": 1, "iso8859-9": 1, "iso88599": 1,
+  "iso_8859-9": 1, "iso_8859-9:1989": 1, "l5": 1, "latin5": 1,
+  "windows-1254": 1, "x-cp1254": 1, "cp1255": 1, "windows-1255": 1,
+  "x-cp1255": 1, "cp1256": 1, "windows-1256": 1, "x-cp1256": 1, "cp1257": 1,
+  "windows-1257": 1, "x-cp1257": 1, "cp1258": 1, "windows-1258": 1,
+  "x-cp1258": 1, "x-mac-cyrillic": 1, "x-mac-ukrainian": 1, "chinese": 1,
+  "csgb2312": 1, "csiso58gb231280": 1, "gb2312": 1, "gb_2312": 1,
+  "gb_2312-80": 1, "gbk": 1, "iso-ir-58": 1, "x-gbk": 1, "gb18030": 1,
+  "hz-gb-2312": 1, "big5": 1, "big5-hkscs": 1, "cn-big5": 1, "csbig5": 1,
+  "x-x-big5": 1, "cseucpkdfmtjapanese": 1, "euc-jp": 1, "x-euc-jp": 1,
+  "csiso2022jp": 1, "iso-2022-jp": 1, "csshiftjis": 1, "ms_kanji": 1,
+  "shift-jis": 1, "shift_jis": 1, "sjis": 1, "windows-31j": 1, "x-sjis": 1,
+  "cseuckr": 1, "csksc56011987": 1, "euc-kr": 1, "iso-ir-149": 1, "korean": 1,
+  "ks_c_5601-1987": 1, "ks_c_5601-1989": 1, "ksc5601": 1, "ksc_5601": 1,
+  "windows-949": 1, "csiso2022kr": 1, "iso-2022-kr": 1, "utf-16": 1,
+  "utf-16le": 1, "utf-16be": 1, "x-user-defined": 1 };
--- a/addon-sdk/source/lib/sdk/io/fs.js
+++ b/addon-sdk/source/lib/sdk/io/fs.js
@@ -7,20 +7,22 @@
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Cc, Ci, CC } = require("chrome");
 
 const { setTimeout } = require("../timers");
 const { Stream, InputStream, OutputStream } = require("./stream");
+const { emit, on } = require("../event/core");
 const { Buffer } = require("./buffer");
 const { ns } = require("../core/namespace");
 const { Class } = require("../core/heritage");
 
+
 const nsILocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile",
                         "initWithPath");
 const FileOutputStream = CC("@mozilla.org/network/file-output-stream;1",
                             "nsIFileOutputStream", "init");
 const FileInputStream = CC("@mozilla.org/network/file-input-stream;1",
                            "nsIFileInputStream", "init");
 const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
                              "nsIBinaryInputStream", "setInputStream");
@@ -28,16 +30,18 @@ const BinaryOutputStream = CC("@mozilla.
                               "nsIBinaryOutputStream", "setOutputStream");
 const StreamPump = CC("@mozilla.org/network/input-stream-pump;1",
                       "nsIInputStreamPump", "init");
 
 const { createOutputTransport, createInputTransport } =
   Cc["@mozilla.org/network/stream-transport-service;1"].
   getService(Ci.nsIStreamTransportService);
 
+const { OPEN_UNBUFFERED } = Ci.nsITransport;
+
 
 const { REOPEN_ON_REWIND, DEFER_OPEN } = Ci.nsIFileInputStream;
 const { DIRECTORY_TYPE, NORMAL_FILE_TYPE } = Ci.nsIFile;
 const { NS_SEEK_SET, NS_SEEK_CUR, NS_SEEK_END } = Ci.nsISeekableStream;
 
 const FILE_PERMISSION = parseInt("0666", 8);
 const PR_UINT32_MAX = 0xfffffff;
 // Values taken from:
@@ -152,24 +156,26 @@ const ReadStream = Class({
       input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
     // We use `nsIStreamTransportService` service to transform blocking
     // file input stream into a fully asynchronous stream that can be written
     // without blocking the main thread.
     let transport = createInputTransport(input, position, length, false);
     // Open an input stream on a transport. We don"t pass flags to guarantee
     // non-blocking stream semantics. Also we use defaults for segment size &
     // count.
-    let asyncInputStream = transport.openInputStream(null, 0, 0);
-    let binaryInputStream = BinaryInputStream(asyncInputStream);
-    nsIBinaryInputStream(fd, binaryInputStream);
-    let pump = StreamPump(asyncInputStream, position, length, 0, 0, false);
+    InputStream.prototype.initialize.call(this, {
+      asyncInputStream: transport.openInputStream(null, 0, 0)
+    });
 
-    InputStream.prototype.initialize.call(this, {
-      input: binaryInputStream, pump: pump
+    // Close file descriptor on end and destroy the stream.
+    on(this, "end", _ => {
+      this.destroy();
+      emit(this, "close");
     });
+
     this.read();
   },
   destroy: function() {
     closeSync(this.fd);
     InputStream.prototype.destroy.call(this);
   }
 });
 exports.ReadStream = ReadStream;
@@ -206,31 +212,30 @@ const WriteStream = Class({
       output.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
     // We use `nsIStreamTransportService` service to transform blocking
     // file output stream into a fully asynchronous stream that can be written
     // without blocking the main thread.
     let transport = createOutputTransport(output, position, -1, false);
     // Open an output stream on a transport. We don"t pass flags to guarantee
     // non-blocking stream semantics. Also we use defaults for segment size &
     // count.
-    let asyncOutputStream = transport.openOutputStream(null, 0, 0);
-    // Finally we create a non-blocking binary output stream. This will allows
-    // us to write buffers as byte arrays without any further transcoding.
-    let binaryOutputStream = BinaryOutputStream(asyncOutputStream);
-    nsIBinaryOutputStream(fd, binaryOutputStream);
+    OutputStream.prototype.initialize.call(this, {
+      asyncOutputStream: transport.openOutputStream(OPEN_UNBUFFERED, 0, 0),
+      output: output
+    });
 
-    // Storing output stream so that it can beaccessed later.
-    OutputStream.prototype.initialize.call(this, {
-      output: binaryOutputStream,
-      asyncOutputStream: asyncOutputStream
+    // For write streams "finish" basically means close.
+    on(this, "finish", _ => {
+       this.destroy();
+       emit(this, "close");
     });
   },
   destroy: function() {
+    OutputStream.prototype.destroy.call(this);
     closeSync(this.fd);
-    OutputStream.prototype.destroy.call(this);
   }
 });
 exports.WriteStream = WriteStream;
 exports.createWriteStream = function createWriteStream(path, options) {
   return new WriteStream(path, options);
 };
 
 const Stats = Class({
@@ -360,17 +365,17 @@ exports.truncate = truncate;
 
 function ftruncate(fd, length, callback) {
   write(fd, new Buffer(length), 0, length, 0, function(error) {
     callback(error);
   });
 }
 exports.ftruncate = ftruncate;
 
-function ftruncateSync(fd, length) {
+function ftruncateSync(fd, length = 0) {
   writeSync(fd, new Buffer(length), 0, length, 0);
 }
 exports.ftruncateSync = ftruncateSync;
 
 function chownSync(path, uid, gid) {
   throw Error("Not implemented yet!!");
 }
 exports.chownSync = chownSync;
@@ -629,16 +634,18 @@ exports.close = close;
 
 /**
  * Synchronous open(2).
  */
 function openSync(path, flags, mode) {
   let [ fd, flags, mode, file ] =
       [ { path: path }, Flags(flags), Mode(mode), nsILocalFile(path) ];
 
+  nsIFile(fd, file);
+
   // If trying to open file for just read that does not exists
   // need to throw exception as node does.
   if (!file.exists() && !isWritable(flags))
     throw FSError("open", "ENOENT", 34, path);
 
   // If we want to open file in read mode we initialize input stream.
   if (isReadable(flags)) {
     let input = FileInputStream(file, flags, mode, DEFER_OPEN);
@@ -670,17 +677,19 @@ function writeSync(fd, buffer, offset, l
   if (length + offset > buffer.length) {
     throw Error("Length is extends beyond buffer");
   }
   else if (length + offset !== buffer.length) {
     buffer = buffer.slice(offset, offset + length);
   }
   let writeStream = new WriteStream(fd, { position: position,
                                           length: length });
-  let output = nsIBinaryOutputStream(fd);
+
+  let output = BinaryOutputStream(nsIFileOutputStream(fd));
+  nsIBinaryOutputStream(fd, output);
   // We write content as a byte array as this will avoid any transcoding
   // if content was a buffer.
   output.writeByteArray(buffer.valueOf(), buffer.length);
   output.flush();
 };
 exports.writeSync = writeSync;
 
 /**
@@ -731,20 +740,24 @@ function readSync(fd, buffer, offset, le
   // Setting a stream position, unless it"s `-1` which means current position.
   if (position >= 0)
     input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
   // We use `nsIStreamTransportService` service to transform blocking
   // file input stream into a fully asynchronous stream that can be written
   // without blocking the main thread.
   let binaryInputStream = BinaryInputStream(input);
   let count = length === ALL ? binaryInputStream.available() : length;
-  var bytes = binaryInputStream.readByteArray(count);
-  buffer.copy.call(bytes, buffer, offset);
+  if (offset === 0) binaryInputStream.readArrayBuffer(count, buffer.buffer);
+  else {
+    let chunk = new Buffer(count);
+    binaryInputStream.readArrayBuffer(count, chunk.buffer);
+    chunk.copy(buffer, offset);
+  }
 
-  return bytes;
+  return buffer.slice(offset, offset + count);
 };
 exports.readSync = readSync;
 
 /**
  * Read data from the file specified by `fd`.
  *
  * `buffer` is the buffer that the data will be written to.
  * `offset` is offset within the buffer where writing will start.
@@ -754,19 +767,19 @@ exports.readSync = readSync;
  * `position` is an integer specifying where to begin reading from in the file.
  * If `position` is `null`, data will be read from the current file position.
  *
  * The callback is given the three arguments, `(error, bytesRead, buffer)`.
  */
 function read(fd, buffer, offset, length, position, callback) {
   let bytesRead = 0;
   let readStream = new ReadStream(fd, { position: position, length: length });
-  readStream.on("data", function onData(chunck) {
-      chunck.copy(buffer, offset + bytesRead);
-      bytesRead += chunck.length;
+  readStream.on("data", function onData(data) {
+      data.copy(buffer, offset + bytesRead);
+      bytesRead += data.length;
   });
   readStream.on("end", function onEnd() {
     callback(null, bytesRead, buffer);
     readStream.destroy();
   });
 };
 exports.read = read;
 
@@ -776,44 +789,52 @@ exports.read = read;
  * contents of the file.
  */
 function readFile(path, encoding, callback) {
   if (isFunction(encoding)) {
     callback = encoding
     encoding = null
   }
 
-  let buffer = new Buffer();
+  let buffer = null;
   try {
     let readStream = new ReadStream(path);
-    readStream.on("data", function(chunck) {
-      chunck.copy(buffer, buffer.length);
+    readStream.on("data", function(data) {
+      if (!buffer) buffer = data;
+      else {
+        let bufferred = buffer
+        buffer = new Buffer(buffer.length + data.length);
+        bufferred.copy(buffer, 0);
+        data.copy(buffer, bufferred.length);
+      }
     });
     readStream.on("error", function onError(error) {
       callback(error);
-      readStream.destroy();
     });
     readStream.on("end", function onEnd() {
+      // Note: Need to destroy before invoking a callback
+      // so that file descriptor is released.
+      readStream.destroy();
       callback(null, buffer);
-      readStream.destroy();
     });
   } catch (error) {
     setTimeout(callback, 0, error);
   }
 };
 exports.readFile = readFile;
 
 /**
  * Synchronous version of `fs.readFile`. Returns the contents of the path.
  * If encoding is specified then this function returns a string.
  * Otherwise it returns a buffer.
  */
 function readFileSync(path, encoding) {
-  let buffer = new Buffer();
   let fd = openSync(path, "r");
+  let size = fstatSync(fd).size;
+  let buffer = new Buffer(size);
   try {
     readSync(fd, buffer, 0, ALL, 0);
   }
   finally {
     closeSync(fd);
   }
   return buffer;
 };
@@ -828,23 +849,26 @@ function writeFile(path, content, encodi
     if (isFunction(encoding)) {
       callback = encoding
       encoding = null
     }
     if (isString(content))
       content = new Buffer(content, encoding);
 
     let writeStream = new WriteStream(path);
-    writeStream.on("error", function onError(error) {
+    let error = null;
+
+    writeStream.end(content, function() {
+      writeStream.destroy();
       callback(error);
-      writeStream.destroy();
     });
-    writeStream.write(content, function onDrain() {
+
+    writeStream.on("error", function onError(reason) {
+      error = reason;
       writeStream.destroy();
-      callback(null);
     });
   } catch (error) {
     callback(error);
   }
 };
 exports.writeFile = writeFile;
 
 /**
--- a/addon-sdk/source/lib/sdk/io/stream.js
+++ b/addon-sdk/source/lib/sdk/io/stream.js
@@ -3,109 +3,55 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
+const { CC, Cc, Ci, Cu, Cr, components } = require("chrome");
 const { EventTarget } = require("../event/target");
 const { emit } = require("../event/core");
 const { Buffer } = require("./buffer");
 const { Class } = require("../core/heritage");
 const { setTimeout } = require("../timers");
-const { ns } = require("../core/namespace");
+
+
+const MultiplexInputStream = CC("@mozilla.org/io/multiplex-input-stream;1",
+                                "nsIMultiplexInputStream");
+const AsyncStreamCopier = CC("@mozilla.org/network/async-stream-copier;1",
+                             "nsIAsyncStreamCopier", "init");
+const StringInputStream = CC("@mozilla.org/io/string-input-stream;1",
+                             "nsIStringInputStream");
+const ArrayBufferInputStream = CC("@mozilla.org/io/arraybuffer-input-stream;1",
+                                  "nsIArrayBufferInputStream");
 
-function isFunction(value) typeof value === "function"
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+                             "nsIBinaryInputStream", "setInputStream");
+const InputStreamPump = CC("@mozilla.org/network/input-stream-pump;1",
+                           "nsIInputStreamPump", "init");
+
+const threadManager = Cc["@mozilla.org/thread-manager;1"].
+                      getService(Ci.nsIThreadManager);
+
+const eventTarget = Cc["@mozilla.org/network/socket-transport-service;1"].
+                    getService(Ci.nsIEventTarget);
+
+let isFunction = value => typeof(value) === "function"
 
 function accessor() {
   let map = new WeakMap();
-  return function(fd, value) {
-    if (value === null) map.delete(fd);
-    if (value !== undefined) map.set(fd, value);
-    return map.get(fd);
+  return function(target, value) {
+    if (value)
+      map.set(target, value);
+    return map.get(target);
   }
 }
 
-let nsIInputStreamPump = accessor();
-let nsIAsyncOutputStream = accessor();
-let nsIInputStream = accessor();
-let nsIOutputStream = accessor();
-
-
-/**
- * Utility function / hack that we use to figure if output stream is closed.
- */
-function isClosed(stream) {
-  // We assume that stream is not closed.
-  let isClosed = false;
-  stream.asyncWait({
-    // If `onClose` callback is called before outer function returns
-    // (synchronously) `isClosed` will be set to `true` identifying
-    // that stream is closed.
-    onOutputStreamReady: function onClose() isClosed = true
-
-  // `WAIT_CLOSURE_ONLY` flag overrides the default behavior, causing the
-  // `onOutputStreamReady` notification to be suppressed until the stream
-  // becomes closed.
-  }, stream.WAIT_CLOSURE_ONLY, 0, null);
-  return isClosed;
-}
-/**
- * Utility function takes output `stream`, `onDrain`, `onClose` callbacks and
- * calls one of this callbacks depending on stream state. It is guaranteed
- * that only one called will be called and it will be called asynchronously.
- * @param {nsIAsyncOutputStream} stream
- * @param {Function} onDrain
- *    callback that is called when stream becomes writable.
- * @param {Function} onClose
- *    callback that is called when stream becomes closed.
- */
-function onStateChange(stream, target) {
-  let isAsync = false;
-  stream.asyncWait({
-    onOutputStreamReady: function onOutputStreamReady() {
-      // If `isAsync` was not yet set to `true` by the last line we know that
-      // `onOutputStreamReady` was called synchronously. In such case we just
-      // defer execution until next turn of event loop.
-      if (!isAsync)
-        return setTimeout(onOutputStreamReady, 0);
-
-      // As it"s not clear what is a state of the stream (TODO: Is there really
-      // no better way ?) we employ hack (see details in `isClosed`) to verify
-      // if stream is closed.
-      emit(target, isClosed(stream) ? "close" : "drain");
-    }
-  }, 0, 0, null);
-  isAsync = true;
-}
-
-function pump(stream) {
-  let input = nsIInputStream(stream);
-  nsIInputStreamPump(stream).asyncRead({
-    onStartRequest: function onStartRequest() {
-      emit(stream, "start");
-    },
-    onDataAvailable: function onDataAvailable(req, c, is, offset, count) {
-      try {
-        let bytes = input.readByteArray(count);
-        emit(stream, "data", new Buffer(bytes, stream.encoding));
-      } catch (error) {
-        emit(stream, "error", error);
-        stream.readable = false;
-      }
-    },
-    onStopRequest: function onStopRequest() {
-      stream.readable = false;
-      emit(stream, "end");
-    }
-  }, null);
-}
-
 const Stream = Class({
   extends: EventTarget,
   initialize: function() {
     this.readable = false;
     this.writable = false;
     this.encoding = null;
   },
   setEncoding: function setEncoding(encoding) {
@@ -115,17 +61,18 @@ const Stream = Class({
     let source = this;
     function onData(chunk) {
       if (target.writable) {
         if (false === target.write(chunk))
           source.pause();
       }
     }
     function onDrain() {
-      if (source.readable) source.resume();
+      if (source.readable)
+        source.resume();
     }
     function onEnd() {
       target.end();
     }
     function onPause() {
       source.pause();
     }
     function onResume() {
@@ -171,154 +118,320 @@ const Stream = Class({
     emit(this, "resume");
   },
   destroySoon: function destroySoon() {
     this.destroy();
   }
 });
 exports.Stream = Stream;
 
+
+let nsIStreamListener = accessor();
+let nsIInputStreamPump = accessor();
+let nsIAsyncInputStream = accessor();
+let nsIBinaryInputStream = accessor();
+
+const StreamListener = Class({
+  initialize: function(stream) {
+    this.stream = stream;
+  },
+
+  // Next three methods are part of `nsIStreamListener` interface and are
+  // invoked by `nsIInputStreamPump.asyncRead`.
+  onDataAvailable: function(request, context, input, offset, count) {
+    let stream = this.stream;
+    let buffer = new ArrayBuffer(count);
+    nsIBinaryInputStream(stream).readArrayBuffer(count, buffer);
+    emit(stream, "data", new Buffer(buffer, stream.encoding));
+  },
+
+  // Next two methods implement `nsIRequestObserver` interface and are invoked
+  // by `nsIInputStreamPump.asyncRead`.
+  onStartRequest: function() {},
+  // Called to signify the end of an asynchronous request. We only care to
+  // discover errors.
+  onStopRequest: function(request, context, status) {
+    let stream = this.stream;
+    stream.readable = false;
+    if (!components.isSuccessCode(status))
+      emit(stream, "error", status);
+    else
+      emit(stream, "end");
+  }
+});
+
+
 const InputStream = Class({
   extends: Stream,
+  readable: false,
+  paused: false,
   initialize: function initialize(options) {
-    let { input, pump } = options;
+    let { asyncInputStream } = options;
 
     this.readable = true;
-    this.paused = false;
-    nsIInputStream(this, input);
-    nsIInputStreamPump(this, pump);
+
+    let binaryInputStream = new BinaryInputStream(asyncInputStream);
+    let inputStreamPump = new InputStreamPump(asyncInputStream,
+                                              -1, -1, 0, 0, false);
+    let streamListener = new StreamListener(this);
+
+    nsIAsyncInputStream(this, asyncInputStream);
+    nsIInputStreamPump(this, inputStreamPump);
+    nsIBinaryInputStream(this, binaryInputStream);
+    nsIStreamListener(this, streamListener);
+
+    this.asyncInputStream = asyncInputStream;
+    this.inputStreamPump = inputStreamPump;
+    this.binaryInputStream = binaryInputStream;
   },
   get status() nsIInputStreamPump(this).status,
-  read: function() pump(this),
+  read: function() {
+    nsIInputStreamPump(this).asyncRead(nsIStreamListener(this), null);
+  },
   pause: function pause() {
     this.paused = true;
     nsIInputStreamPump(this).suspend();
     emit(this, "paused");
   },
   resume: function resume() {
     this.paused = false;
     nsIInputStreamPump(this).resume();
     emit(this, "resume");
   },
-  destroy: function destroy() {
+  close: function close() {
     this.readable = false;
-    try {
-      emit(this, "close", null);
-      nsIInputStreamPump(this).cancel(null);
-      nsIInputStreamPump(this, null);
+    nsIInputStreamPump(this).cancel(Cr.NS_OK);
+    nsIBinaryInputStream(this).close();
+    nsIAsyncInputStream(this).close();
+  },
+  destroy: function destroy() {
+    this.close();
 
-      nsIInputStream(this).close();
-      nsIInputStream(this, null);
-    } catch (error) {
-      emit(this, "error", error);
-    }
+    nsIInputStreamPump(this);
+    nsIAsyncInputStream(this);
+    nsIBinaryInputStream(this);
+    nsIStreamListener(this);
   }
 });
 exports.InputStream = InputStream;
 
+
+
+let nsIRequestObserver = accessor();
+let nsIAsyncOutputStream = accessor();
+let nsIAsyncStreamCopier = accessor();
+let nsIMultiplexInputStream = accessor();
+
+const RequestObserver = Class({
+  initialize: function(stream) {
+    this.stream = stream;
+  },
+  // Method is part of `nsIRequestObserver` interface that is
+  // invoked by `nsIAsyncStreamCopier.asyncCopy`.
+  onStartRequest: function() {},
+  // Method is part of `nsIRequestObserver` interface that is
+  // invoked by `nsIAsyncStreamCopier.asyncCopy`.
+  onStopRequest: function(request, context, status) {
+    let stream = this.stream;
+    stream.drained = true;
+
+    // Remove copied chunk.
+    let multiplexInputStream = nsIMultiplexInputStream(stream);
+    multiplexInputStream.removeStream(0);
+
+    // If there was an error report.
+    if (!components.isSuccessCode(status))
+      emit(stream, "error", status);
+
+    // If there more chunks in queue then flush them.
+    else if (multiplexInputStream.count)
+      stream.flush();
+
+    // If stream is still writable notify that queue has drained.
+    else if (stream.writable)
+      emit(stream, "drain");
+
+    // If stream is no longer writable close it.
+    else {
+      nsIAsyncStreamCopier(stream).cancel(Cr.NS_OK);
+      nsIMultiplexInputStream(stream).close();
+      nsIAsyncOutputStream(stream).close();
+      nsIAsyncOutputStream(stream).flush();
+    }
+  }
+});
+
+const OutputStreamCallback = Class({
+  initialize: function(stream) {
+    this.stream = stream;
+  },
+  // Method is part of `nsIOutputStreamCallback` interface that
+  // is invoked by `nsIAsyncOutputStream.asyncWait`. It is registered
+  // with `WAIT_CLOSURE_ONLY` flag that overrides the default behavior,
+  // causing the `onOutputStreamReady` notification to be suppressed until
+  // the stream becomes closed.
+  onOutputStreamReady: function(nsIAsyncOutputStream) {
+    emit(this.stream, "finish");
+  }
+});
+
 const OutputStream = Class({
   extends: Stream,
+  writable: false,
+  drained: true,
+  get bufferSize() {
+    let multiplexInputStream = nsIMultiplexInputStream(this);
+    return multiplexInputStream && multiplexInputStream.available();
+  },
   initialize: function initialize(options) {
-    let { output, asyncOutputStream } = options;
+    let { asyncOutputStream, output } = options;
+    this.writable = true;
+
+    // Ensure that `nsIAsyncOutputStream` was provided.
+    asyncOutputStream.QueryInterface(Ci.nsIAsyncOutputStream);
 
-    this.writable = true;
-    nsIOutputStream(this, output);
+    // Create a `nsIMultiplexInputStream` and `nsIAsyncStreamCopier`. Former
+    // is used to queue written data chunks that `asyncStreamCopier` will
+    // asynchronously drain into `asyncOutputStream`.
+    let multiplexInputStream = MultiplexInputStream();
+    let asyncStreamCopier = AsyncStreamCopier(multiplexInputStream,
+                                              output || asyncOutputStream,
+                                              eventTarget,
+                                              // nsIMultiplexInputStream
+                                              // implemnts .readSegments()
+                                              true,
+                                              // nsIOutputStream may or
+                                              // may not implemnet
+                                              // .writeSegments().
+                                              false,
+                                              // Use default buffer size.
+                                              null,
+                                              // Should not close an input.
+                                              false,
+                                              // Should not close an output.
+                                              false);
+
+    // Create `requestObserver` implementing `nsIRequestObserver` interface
+    // in the constructor that's gonna be reused across several flushes.
+    let requestObserver = RequestObserver(this);
+
+
+    // Create observer that implements `nsIOutputStreamCallback` and register
+    // using `WAIT_CLOSURE_ONLY` flag. That way it will be notfied once
+    // `nsIAsyncOutputStream` is closed.
+    asyncOutputStream.asyncWait(OutputStreamCallback(this),
+                                asyncOutputStream.WAIT_CLOSURE_ONLY,
+                                0,
+                                threadManager.currentThread);
+
+    nsIRequestObserver(this, requestObserver);
     nsIAsyncOutputStream(this, asyncOutputStream);
+    nsIMultiplexInputStream(this, multiplexInputStream);
+    nsIAsyncStreamCopier(this, asyncStreamCopier);
+
+    this.asyncOutputStream = asyncOutputStream;
+    this.multiplexInputStream = multiplexInputStream;
+    this.asyncStreamCopier = asyncStreamCopier;
   },
   write: function write(content, encoding, callback) {
-    let output = nsIOutputStream(this);
-    let asyncOutputStream = nsIAsyncOutputStream(this);
-
     if (isFunction(encoding)) {
       callback = encoding;
       encoding = callback;
     }
 
-    // Flag indicating whether or not content has been flushed to the kernel
-    // buffer.
-    let isWritten = false;
     // If stream is not writable we throw an error.
-    if (!this.writable)
-      throw Error("stream not writable");
+    if (!this.writable) throw Error("stream is not writable");
+
+    let chunk = null;
 
-    try {
-      // If content is not a buffer then we create one out of it.
-      if (!Buffer.isBuffer(content))
-        content = new Buffer(content, encoding);
+    // If content is not a buffer then we create one out of it.
+    if (Buffer.isBuffer(content)) {
+      chunk = new ArrayBufferInputStream();
+      chunk.setData(content.buffer, 0, content.length);
+    }
+    else {
+      chunk = new StringInputStream();
+      chunk.setData(content, content.length);
+    }
 
-      // We write content as a byte array as this will avoid any transcoding
-      // if content was a buffer.
-      output.writeByteArray(content.valueOf(), content.length);
-      output.flush();
+    if (callback)
+      this.once("drain", callback);
+
+    // Queue up chunk to be copied to output sync.
+    nsIMultiplexInputStream(this).appendStream(chunk);
+    this.flush();
 
-      if (callback) this.once("drain", callback);
-      onStateChange(asyncOutputStream, this);
-      return true;
-    } catch (error) {
-      // If errors occur we emit appropriate event.
-      emit(this, "error", error);
+    return this.drained;
+  },
+  flush: function() {
+    if (this.drained) {
+      this.drained = false;
+      nsIAsyncStreamCopier(this).asyncCopy(nsIRequestObserver(this), null);
     }
   },
-  flush: function flush() {
-    nsIOutputStream(this).flush();
-  },
   end: function end(content, encoding, callback) {
     if (isFunction(content)) {
       callback = content
       content = callback
     }
     if (isFunction(encoding)) {
       callback = encoding
       encoding = callback
     }
 
-    // Setting a listener to "close" event if passed.
+    // Setting a listener to "finish" event if passed.
     if (isFunction(callback))
-      this.once("close", callback);
+      this.once("finish", callback);
 
-    // If content is passed then we defer closing until we finish with writing.
+
     if (content)
-      this.write(content, encoding, end.bind(this));
-    // If we don"t write anything, then we close an outputStream.
-    else
-      nsIOutputStream(this).close();
+      this.write(content, encoding);
+    this.writable = false;
+
+    // Close `asyncOutputStream` only if output has drained. If it's
+    // not drained than `asyncStreamCopier` is busy writing, so let
+    // it finish. Note that since `this.writable` is false copier will
+    // close `asyncOutputStream` once output drains.
+    if (this.drained)
+      nsIAsyncOutputStream(this).close();
   },
-  destroy: function destroy(callback) {
-    try {
-      this.end(callback);
-      nsIOutputStream(this, null);
-      nsIAsyncOutputStream(this, null);
-    } catch (error) {
-      emit(this, "error", error);
-    }
+  destroy: function destroy() {
+    nsIAsyncOutputStream(this).close();
+    nsIAsyncOutputStream(this);
+    nsIMultiplexInputStream(this);
+    nsIAsyncStreamCopier(this);
+    nsIRequestObserver(this);
   }
 });
 exports.OutputStream = OutputStream;
 
 const DuplexStream = Class({
   extends: Stream,
+  implements: [InputStream, OutputStream],
+  allowHalfOpen: true,
   initialize: function initialize(options) {
-    let { input, output, pump } = options;
+    options = options || {};
+    let { readable, writable, allowHalfOpen } = options;
+
+    InputStream.prototype.initialize.call(this, options);
+    OutputStream.prototype.initialize.call(this, options);
+
+    if (readable === false)
+      this.readable = false;
 
-    this.writable = true;
-    this.readable = true;
-    this.encoding = null;
+    if (writable === false)
+      this.writable = false;
+
+    if (allowHalfOpen === false)
+      this.allowHalfOpen = false;
 
-    nsIInputStream(this, input);
-    nsIOutputStream(this, output);
-    nsIInputStreamPump(this, pump);
+    // If in a half open state and it's disabled enforce end.
+    this.once("end", () => {
+      if (!this.allowHalfOpen && (!this.readable || !this.writable))
+        this.end();
+    });
   },
-  read: InputStream.prototype.read,
-  pause: InputStream.prototype.pause,
-  resume: InputStream.prototype.resume,
-
-  write: OutputStream.prototype.write,
-  flush: OutputStream.prototype.flush,
-  end: OutputStream.prototype.end,
-
   destroy: function destroy(error) {
-    if (error)
-      emit(this, "error", error);
     InputStream.prototype.destroy.call(this);
     OutputStream.prototype.destroy.call(this);
   }
 });
 exports.DuplexStream = DuplexStream;
--- a/addon-sdk/source/lib/sdk/lang/functional.js
+++ b/addon-sdk/source/lib/sdk/lang/functional.js
@@ -7,17 +7,17 @@
 // those goes to him.
 
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-const { setTimeout } = require("../timers");
+const { setImmediate, setTimeout } = require("../timers");
 const { deprecateFunction } = require("../util/deprecate");
 
 /**
  * Takes `lambda` function and returns a method. When returned method is
  * invoked it calls wrapped `lambda` and passes `this` as a first argument
  * and given argument as rest.
  */
 function method(lambda) {
@@ -25,23 +25,22 @@ function method(lambda) {
     return lambda.apply(null, [this].concat(Array.slice(arguments)));
   }
 }
 exports.method = method;
 
 /**
  * Takes a function and returns a wrapped one instead, calling which will call
  * original function in the next turn of event loop. This is basically utility
- * to do `setTimeout(function() { ... }, 0)`, with a difference that returned
+ * to do `setImmediate(function() { ... })`, with a difference that returned
  * function is reused, instead of creating a new one each time. This also allows
  * to use this functions as event listeners.
  */
 function defer(f) {
-  return function deferred()
-    setTimeout(invoke, 0, f, arguments, this);
+  return function deferred() setImmediate(invoke, f, arguments, this);
 }
 exports.defer = defer;
 // Exporting `remit` alias as `defer` may conflict with promises.
 exports.remit = defer;
 
 /*
  * Takes a funtion and returns a wrapped function that returns `this`
  */
--- a/addon-sdk/source/lib/sdk/panel.js
+++ b/addon-sdk/source/lib/sdk/panel.js
@@ -66,17 +66,16 @@ let models = new WeakMap();
 let views = new WeakMap();
 let workers = new WeakMap();
 
 function viewFor(panel) views.get(panel)
 function modelFor(panel) models.get(panel)
 function panelFor(view) panels.get(view)
 function workerFor(panel) workers.get(panel)
 
-getActiveView.define(Panel, viewFor);
 
 // Utility function takes `panel` instance and makes sure it will be
 // automatically hidden as soon as other panel is shown.
 let setupAutoHide = new function() {
   let refs = new WeakMap();
 
   return function setupAutoHide(panel) {
     // Create system event listener that reacts to any panel showing and
@@ -228,16 +227,19 @@ const Panel = Class({
 
     domPanel.resize(view, model.width, model.height);
 
     return this;
   }
 });
 exports.Panel = Panel;
 
+// Note must be defined only after value to `Panel` is assigned.
+getActiveView.define(Panel, viewFor);
+
 // Filter panel events to only panels that are create by this module.
 let panelEvents = filter(events, function({target}) panelFor(target));
 
 // Panel events emitted after panel has being shown.
 let shows = filter(panelEvents, function({type}) type === "popupshown");
 
 // Panel events emitted after panel became hidden.
 let hides = filter(panelEvents, function({type}) type === "popuphidden");
--- a/addon-sdk/source/lib/sdk/system.js
+++ b/addon-sdk/source/lib/sdk/system.js
@@ -63,29 +63,21 @@ exports.exit = function exit(code) {
     stream.write(status, status.length);
     stream.flush();
     stream.close();
   }
 
   appStartup.quit(code ? E_ATTEMPT : E_FORCE);
 };
 
-exports.stdout = new function() {
-  let write = dump
-  if ('logFile' in options && options.logFile) {
-    let mode = PR_WRONLY | PR_CREATE_FILE | PR_APPEND;
-    let stream = openFile(options.logFile, mode);
-    write = function write(data) {
-      let text = String(data);
-      stream.write(text, text.length);
-      stream.flush();
-    }
-  }
-  return Object.freeze({ write: write });
-};
+// Adapter for nodejs's stdout & stderr:
+// http://nodejs.org/api/process.html#process_process_stdout
+let stdout = Object.freeze({ write: dump, end: dump });
+exports.stdout = stdout;
+exports.stderr = stdout;
 
 /**
  * Returns a path of the system's or application's special directory / file
  * associated with a given `id`. For list of possible `id`s please see:
  * https://developer.mozilla.org/en-US/docs/Code_snippets/File_I_O#Getting_files_in_special_directories
  * http://mxr.mozilla.org/mozilla-central/source/xpcom/io/nsAppDirectoryServiceDefs.h
  * @example
  *
@@ -129,17 +121,17 @@ exports.build = appInfo.appBuildID;
  * The XUL application's UUID.
  * This has traditionally been in the form
  * `{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}` but for some applications it may
  * be: "appname@vendor.tld".
  */
 exports.id = appInfo.ID;
 
 /**
- * The name of the application. 
+ * The name of the application.
  */
 exports.name = appInfo.name;
 
 /**
  * The XUL application's version, for example "0.8.0+" or "3.7a1pre".
  */
 exports.version = appInfo.version;
 
--- a/addon-sdk/source/lib/sdk/system/globals.js
+++ b/addon-sdk/source/lib/sdk/system/globals.js
@@ -17,30 +17,17 @@ let consoleService = Cc['@mozilla.org/co
                      QueryInterface(Ci.nsIConsoleService);
 
 // On windows dump does not writes into stdout so cfx can't read thous dumps.
 // To workaround this issue we write to a special file from which cfx will
 // read and print to the console.
 // For more details see: bug-673383
 exports.dump = stdout.write;
 
-// Bug 718230: We need to send console messages to stdout and JS Console
-function forsakenConsoleDump(msg, level) {
-  stdout.write(msg);
-
-  if (level === 'error') {
-    let error = ScriptError();
-    msg = msg.replace(/^error: /, '');
-    error.init(msg, null, null, 0, 0, 0, 'Add-on SDK');
-    consoleService.logMessage(error);
-  }
-  else
-    consoleService.logStringMessage(msg);
-};
-exports.console = new PlainTextConsole(forsakenConsoleDump);
+exports.console = new PlainTextConsole();
 
 // Provide CommonJS `define` to allow authoring modules in a format that can be
 // loaded both into jetpack and into browser via AMD loaders.
 Object.defineProperty(exports, 'define', {
   // `define` is provided as a lazy getter that binds below defined `define`
   // function to the module scope, so that require, exports and module
   // variables remain accessible.
   configurable: true,
--- a/addon-sdk/source/lib/sdk/tab/events.js
+++ b/addon-sdk/source/lib/sdk/tab/events.js
@@ -12,16 +12,17 @@ module.metadata = {
   "stability": "experimental"
 };
 
 const { Ci } = require("chrome");
 const { windows, isInteractive } = require("../window/utils");
 const { events } = require("../browser/events");
 const { open } = require("../event/dom");
 const { filter, map, merge, expand } = require("../event/utils");
+const isFennec = require("sdk/system/xul-app").is("Fennec");
 
 // Module provides event stream (in nodejs style) that emits data events
 // for all the tab events that happen in running firefox. At the moment
 // it does it by registering listeners on all browser windows and then
 // forwarding events when they occur to a stream. This will become obsolete
 // once Bug 843901 is fixed, and we'll just leverage observer notifications.
 
 // Set of tab events that this module going to aggregate and expose.
@@ -52,9 +53,18 @@ let eventsFromFuture = expand(futureWind
 // tab events for them too.
 let interactiveWindows = windows("navigator:browser", { includePrivate: true }).
                          filter(isInteractive);
 let eventsFromInteractive = merge(interactiveWindows.map(tabEventsFor));
 
 
 // Finally merge stream of tab events from future windows and current windows
 // to cover all tab events on all windows that will open.
-exports.events = merge([eventsFromInteractive, eventsFromFuture]);
+let allEvents = merge([eventsFromInteractive, eventsFromFuture]);
+
+// Map events to Fennec format if necessary
+exports.events = map(allEvents, function (event) {
+  return !isFennec ? event : {
+    type: event.type,
+    target: event.target.ownerDocument.defaultView.BrowserApp
+            .getTabForBrowser(event.target)
+  };
+});
--- a/addon-sdk/source/lib/sdk/tabs/tab-fennec.js
+++ b/addon-sdk/source/lib/sdk/tabs/tab-fennec.js
@@ -49,17 +49,19 @@ const Tab = Class({
   get title() getTabTitle(tabNS(this).tab),
   set title(title) setTabTitle(tabNS(this).tab, title),
 
   /**
    * Location of the page currently loaded in this tab.
    * Changing this property will loads page under under the specified location.
    * @type {String}
    */
-  get url() getTabURL(tabNS(this).tab),
+  get url() {
+    return tabNS(this).closed ? undefined : getTabURL(tabNS(this).tab);
+  },
   set url(url) setTabURL(tabNS(this).tab, url),
 
   /**
    * URI of the favicon for the page currently loaded in this tab.
    * @type {String}
    */
   get favicon() {
     /*
@@ -90,16 +92,18 @@ const Tab = Class({
   },
 
   /**
    * The index of the tab relative to other tabs in the application window.
    * Changing this property will change order of the actual position of the tab.
    * @type {Number}
    */
   get index() {
+    if (tabNS(this).closed) return undefined;
+
     let tabs = tabNS(this).window.BrowserApp.tabs;
     let tab = tabNS(this).tab;
     for (var i = tabs.length; i >= 0; i--) {
       if (tabs[i] === tab)
         return i;
     }
     return null;
   },
@@ -146,18 +150,21 @@ const Tab = Class({
   activate: function activate() {
     activateTab(tabNS(this).tab, tabNS(this).window);
   },
 
   /**
    * Close the tab
    */
   close: function close(callback) {
-    if (callback)
-      this.once(EVENTS.close.name, callback);
+    let tab = this;
+    this.once(EVENTS.close.name, function () {
+      tabNS(tab).closed = true;
+      if (callback) callback();
+    });
 
     closeTab(tabNS(this).tab);
   },
 
   /**
    * Reload the tab
    */
   reload: function reload() {
--- a/addon-sdk/source/lib/sdk/tabs/tabs-firefox.js
+++ b/addon-sdk/source/lib/sdk/tabs/tabs-firefox.js
@@ -21,20 +21,19 @@ function newTabWindow(options) {
 }
 
 Object.defineProperties(tabs, {
   open: { value: function open(options) {
     if (options.inNewWindow) {
         newTabWindow(options);
         return undefined;
     }
-    // Open in active window if new window was not required.
 
     let activeWindow = windows.activeWindow;
-    let privateState = !!options.isPrivate;
+    let privateState = (supportPrivateTabs && (options.isPrivate || isPrivate(activeWindow))) || false;
 
     // if the active window is in the state that we need then use it
     if (activeWindow && (!supportPrivateTabs || privateState === isPrivate(activeWindow))) {
       activeWindow.tabs.open(options);
     }
     else {
       // find a window in the state that we need
       let window = getWindow(privateState);
--- a/addon-sdk/source/lib/sdk/test/harness.js
+++ b/addon-sdk/source/lib/sdk/test/harness.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
-const { Cc,Ci } = require("chrome");
+const { Cc, Ci, Cu } = require("chrome");
 const { Loader } = require('./loader');
 const { serializeStack, parseStack  } = require("toolkit/loader");
 const { setTimeout } = require('../timers');
 const memory = require('../deprecated/memory');
 const { PlainTextConsole } = require("../console/plain-text");
 const { when: unload } = require("../system/unload");
 const { format, fromException }  = require("../console/traceback");
 const system = require("../system");
@@ -142,19 +142,21 @@ function dictDiff(last, curr) {
   return diff;
 }
 
 function reportMemoryUsage() {
   memory.gc();
 
   var mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
             .getService(Ci.nsIMemoryReporterManager);
+
   var reporters = mgr.enumerateReporters();
   if (reporters.hasMoreElements())
     print("\n");
+
   while (reporters.hasMoreElements()) {
     var reporter = reporters.getNext();
     reporter.QueryInterface(Ci.nsIMemoryReporter);
     print(reporter.description + ": " + reporter.memoryUsed + "\n");
   }
 
   var weakrefs = [info.weakref.get()
                   for each (info in memory.getObjects())];
@@ -162,36 +164,34 @@ function reportMemoryUsage() {
   print("Tracked memory objects in testing sandbox: " +
         weakrefs.length + "\n");
 }
 
 var gWeakrefInfo;
 
 function checkMemory() {
   memory.gc();
-  setTimeout(function () {
-    memory.gc();
-    setTimeout(function () {
-      let leaks = getPotentialLeaks();
-      let compartmentURLs = Object.keys(leaks.compartments).filter(function(url) {
-        return !(url in startLeaks.compartments);
-      });
+  Cu.schedulePreciseGC(function () {
+    let leaks = getPotentialLeaks();
+
+    let compartmentURLs = Object.keys(leaks.compartments).filter(function(url) {
+      return !(url in startLeaks.compartments);
+    });
 
-      let windowURLs = Object.keys(leaks.windows).filter(function(url) {
-        return !(url in startLeaks.windows);
-      });
+    let windowURLs = Object.keys(leaks.windows).filter(function(url) {
+      return !(url in startLeaks.windows);
+    });
 
-      for (let url of compartmentURLs)
-        console.warn("LEAKED", leaks.compartments[url]);
+    for (let url of compartmentURLs)
+      console.warn("LEAKED", leaks.compartments[url]);
 
-      for (let url of windowURLs)
-        console.warn("LEAKED", leaks.windows[url]);
+    for (let url of windowURLs)
+      console.warn("LEAKED", leaks.windows[url]);
 
-      showResults();
-    });
+    showResults();
   });
 }
 
 function showResults() {
   if (gWeakrefInfo) {
     gWeakrefInfo.forEach(
       function(info) {
         var ref = info.weakref.get();
@@ -293,16 +293,17 @@ function getPotentialLeaks() {
   let uri = ioService.newURI("chrome://global/content/", "UTF-8", null);
   let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
                   getService(Ci.nsIChromeRegistry);
   uri = chromeReg.convertChromeURL(uri);
   let spec = uri.spec;
   let pos = spec.indexOf("!/");
   WHITELIST_BASE_URLS.push(spec.substring(0, pos + 2));
 
+  let zoneRegExp = new RegExp("^explicit/js-non-window/zones/zone[^/]+/compartment\\((.+)\\)");
   let compartmentRegexp = new RegExp("^explicit/js-non-window/compartments/non-window-global/compartment\\((.+)\\)/");
   let compartmentDetails = new RegExp("^([^,]+)(?:, (.+?))?(?: \\(from: (.*)\\))?$");
   let windowRegexp = new RegExp("^explicit/window-objects/top\\((.*)\\)/active");
   let windowDetails = new RegExp("^(.*), id=.*$");
 
   function isPossibleLeak(item) {
     if (!item.location)
       return false;
@@ -313,18 +314,19 @@ function getPotentialLeaks() {
     }
 
     return true;
   }
 
   let compartments = {};
   let windows = {};
   function logReporter(process, path, kind, units, amount, description) {
-    let matches = compartmentRegexp.exec(path);
-    if (matches) {
+    let matches;
+
+    if ((matches = compartmentRegexp.exec(path)) || (matches = zoneRegExp.exec(path))) {
       if (matches[1] in compartments)
         return;
 
       let details = compartmentDetails.exec(matches[1]);
       if (!details) {
         console.error("Unable to parse compartment detail " + matches[1]);
         return;
       }
@@ -571,17 +573,17 @@ var runTests = exports.runTests = functi
     print("Running tests on " + system.name + " " + system.version +
           "/Gecko " + system.platformVersion + " (" +
           system.id + ") under " +
           system.platform + "/" + system.architecture + ".\n");
 
     if (options.parseable)
       testConsole = new TestRunnerTinderboxConsole(options);
     else
-      testConsole = new TestRunnerConsole(new PlainTextConsole(print), options);
+      testConsole = new TestRunnerConsole(new PlainTextConsole(), options);
 
     loader = Loader(module, {
       console: testConsole,
       global: {} // useful for storing things like coverage testing.
     });
 
     // Load these before getting initial leak stats as they will still be in
     // memory when we check later
--- a/addon-sdk/source/lib/sdk/timers.js
+++ b/addon-sdk/source/lib/sdk/timers.js
@@ -2,52 +2,104 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 module.metadata = {
   "stability": "stable"
 };
 
-const { CC, Ci } = require('chrome');
-const { when: unload } = require('./system/unload');
+const { CC, Cc, Ci } = require("chrome");
+const { when: unload } = require("./system/unload");
 
 const { TYPE_ONE_SHOT, TYPE_REPEATING_SLACK } = Ci.nsITimer;
-const Timer = CC('@mozilla.org/timer;1', 'nsITimer');
+const Timer = CC("@mozilla.org/timer;1", "nsITimer");
 const timers = Object.create(null);
+const threadManager = Cc["@mozilla.org/thread-manager;1"].
+                      getService(Ci.nsIThreadManager);
+const prefBranch = Cc["@mozilla.org/preferences-service;1"].
+                    getService(Ci.nsIPrefService).
+                    QueryInterface(Ci.nsIPrefBranch);
+
+let MIN_DELAY = 4;
+// Try to get min timeout delay used by browser.
+try { MIN_DELAY = prefBranch.getIntPref("dom.min_timeout_value"); } finally {}
+
 
 // Last timer id.
 let lastID = 0;
 
 // Sets typer either by timeout or by interval
 // depending on a given type.
-function setTimer(type, callback, delay) {
+function setTimer(type, callback, delay, ...args) {
   let id = ++ lastID;
   let timer = timers[id] = Timer();
-  let args = Array.slice(arguments, 3);
   timer.initWithCallback({
     notify: function notify() {
       try {
         if (type === TYPE_ONE_SHOT)
           delete timers[id];
         callback.apply(null, args);
       }
       catch(error) {
         console.exception(error);
       }
     }
-  }, delay || 0, type);
+  }, Math.max(delay || MIN_DELAY), type);
   return id;
 }
 
 function unsetTimer(id) {
   let timer = timers[id];
   delete timers[id];
-  if (timer)
-    timer.cancel();
+  if (timer) timer.cancel();
 }
 
+let immediates = new Map();
+
+let dispatcher = _ => {
+  // Allow scheduling of a new dispatch loop.
+  dispatcher.scheduled = false;
+  // Take a snapshot of timer `id`'s that have being present before
+  // starting a dispatch loop, in order to ignore timers registered
+  // in side effect to dispatch while also skipping immediates that
+  // were removed in side effect.
+  let ids = [id for ([id] of immediates)];
+  for (let id of ids) {
+    let immediate = immediates.get(id);
+    if (immediate) {
+      immediates.delete(id);
+      try { immediate(); }
+      catch (error) { console.exception(error); }
+    }
+  }
+}
+
+function setImmediate(callback, ...params) {
+  let id = ++ lastID;
+  // register new immediate timer with curried params.
+  immediates.set(id, _ => callback.apply(callback, params));
+  // if dispatch loop is not scheduled schedule one. Own scheduler
+  if (!dispatcher.scheduled) {
+    dispatcher.scheduled = true;
+    threadManager.currentThread.dispatch(dispatcher,
+                                         Ci.nsIThread.DISPATCH_NORMAL);
+  }
+  return id;
+}
+
+function clearImmediate(id) {
+  immediates.delete(id);
+}
+
+// Bind timers so that toString-ing them looks same as on native timers.
+exports.setImmediate = setImmediate.bind(null);
+exports.clearImmediate = clearImmediate.bind(null);
 exports.setTimeout = setTimer.bind(null, TYPE_ONE_SHOT);
 exports.setInterval = setTimer.bind(null, TYPE_REPEATING_SLACK);
 exports.clearTimeout = unsetTimer.bind(null);
 exports.clearInterval = unsetTimer.bind(null);
 
-unload(function() { Object.keys(timers).forEach(unsetTimer) });
+// all timers are cleared out on unload.
+unload(function() {
+  immediates.clear();
+  Object.keys(timers).forEach(unsetTimer)
+});
--- a/addon-sdk/source/lib/sdk/util/array.js
+++ b/addon-sdk/source/lib/sdk/util/array.js
@@ -96,18 +96,19 @@ function fromIterator(iterator) {
   else {
     for (let item of iterator)
       array.push(item);
   }
   return array;
 }
 exports.fromIterator = fromIterator;
 
-function find(array, predicate) {
+function find(array, predicate, fallback) {
   var index = 0;
   var count = array.length;
   while (index < count) {
     var value = array[index];
     if (predicate(value)) return value;
     else index = index + 1;
   }
+  return fallback;
 }
 exports.find = find;
--- a/addon-sdk/source/lib/sdk/window/utils.js
+++ b/addon-sdk/source/lib/sdk/window/utils.js
@@ -13,16 +13,18 @@ const observers = require('../deprecated
 const { defer } = require('sdk/core/promise');
 
 const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1'].
                        getService(Ci.nsIWindowWatcher);
 const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
                         getService(Ci.nsIAppShellService);
 const WM = Cc['@mozilla.org/appshell/window-mediator;1'].
            getService(Ci.nsIWindowMediator);
+const io = Cc['@mozilla.org/network/io-service;1'].
+           getService(Ci.nsIIOService);
 
 const BROWSER = 'navigator:browser',
       URI_BROWSER = 'chrome://browser/content/browser.xul',
       NAME = '_blank',
       FEATURES = 'chrome,all,dialog=no,non-private';
 
 function isWindowPrivate(win) {
   if (!win)
@@ -179,28 +181,31 @@ function serializeFeatures(options) {
  * @params {nsIDOMWindow} options.parent
  *    Used as parent for the created window.
  * @params {String} options.name
  *    Optional name that is assigned to the window.
  * @params {Object} options.features
  *    Map of key, values like: `{ width: 10, height: 15, chrome: true, private: true }`.
  */
 function open(uri, options) {
-  options = options || {};
+  uri = uri || URI_BROWSER;
+  options = options || {}
+
+  if (['chrome', 'resource', 'data'].indexOf(io.newURI(uri, null, null).scheme) < 0)
+    throw new Error('only chrome, resource and data uris are allowed');
+
   let newWindow = windowWatcher.
     openWindow(options.parent || null,
-               uri || URI_BROWSER,
+               uri,
                options.name || null,
                serializeFeatures(options.features || {}),
                options.args || null);
 
   return newWindow;
 }
-
-
 exports.open = open;
 
 function onFocus(window) {
   let { resolve, promise } = defer();
 
   if (isFocused(window)) {
     resolve(window);
   }
--- a/addon-sdk/source/python-lib/cuddlefish/options_xul.py
+++ b/addon-sdk/source/python-lib/cuddlefish/options_xul.py
@@ -47,16 +47,19 @@ def validate_prefs(options):
 
 def parse_options(options, jetpack_id):
     doc = Document()
     root = doc.createElement("vbox")
     root.setAttribute("xmlns", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")
     doc.appendChild(root)
 
     for pref in options:
+        if ("hidden" in pref and pref["hidden"] == True):
+            continue;
+
         setting = doc.createElement("setting")
         setting.setAttribute("pref-name", pref["name"])
         setting.setAttribute("data-jetpack-id", jetpack_id)
         setting.setAttribute("pref", "extensions." + jetpack_id + "." + pref["name"])
         setting.setAttribute("type", pref["type"])
         setting.setAttribute("title", pref["title"])
 
         if ("description" in pref):
--- a/addon-sdk/source/python-lib/cuddlefish/runner.py
+++ b/addon-sdk/source/python-lib/cuddlefish/runner.py
@@ -25,17 +25,17 @@ FILTER_ONLY_CONSOLE_FROM_ADB = re.compil
 
 # Used to detect the currently running test
 PARSEABLE_TEST_NAME = re.compile(r'TEST-START \| ([^\n]+)\n')
 
 # Maximum time we'll wait for tests to finish, in seconds.
 # The purpose of this timeout is to recover from infinite loops.  It should be
 # longer than the amount of time any test run takes, including those on slow
 # machines running slow (debug) versions of Firefox.
-RUN_TIMEOUT = 45 * 60 # 45 minutes
+RUN_TIMEOUT = 1.5 * 60 * 60 # 1.5 Hour
 
 # Maximum time we'll wait for tests to emit output, in seconds.
 # The purpose of this timeout is to recover from hangs.  It should be longer
 # than the amount of time any test takes to report results.
 OUTPUT_TIMEOUT = 60 # one minute
 
 def follow_file(filename):
     """
@@ -491,19 +491,16 @@ def run_app(harness_root_dir, manifest_r
         fileno,logfile = tempfile.mkstemp(prefix="harness-log-")
         os.close(fileno)
     logfile_tail = follow_file(logfile)
     atexit.register(maybe_remove_logfile)
 
     logfile = os.path.abspath(os.path.expanduser(logfile))
     maybe_remove_logfile()
 
-    if app_type != "fennec-on-device":
-        harness_options['logFile'] = logfile
-
     env = {}
     env.update(os.environ)
     env['MOZ_NO_REMOTE'] = '1'
     env['XPCOM_DEBUG_BREAK'] = 'stack'
     env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1'
     env.update(extra_environment)
     if norun:
         cmdargs.append("-no-remote")
--- a/addon-sdk/source/python-lib/mozrunner/winprocess.py
+++ b/addon-sdk/source/python-lib/mozrunner/winprocess.py
@@ -325,23 +325,19 @@ GetExitCodeProcessProto = WINFUNCTYPE(BO
 GetExitCodeProcessFlags = ((1, "hProcess"),
                            (2, "lpExitCode"))
 GetExitCodeProcess = GetExitCodeProcessProto(
     ("GetExitCodeProcess", windll.kernel32),
     GetExitCodeProcessFlags)
 GetExitCodeProcess.errcheck = ErrCheckBool
 
 def CanCreateJobObject():
-    currentProc = GetCurrentProcess()
-    if IsProcessInJob(currentProc):
-        jobinfo = QueryInformationJobObject(HANDLE(0), 'JobObjectExtendedLimitInformation')
-        limitflags = jobinfo['BasicLimitInformation']['LimitFlags']
-        return bool(limitflags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) or bool(limitflags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)
-    else:
-        return True
+    # Running firefox in a job (from cfx) hangs on sites using flash plugin
+    # so job creation is turned off for now. (see Bug 768651).
+    return False
 
 ### testing functions
 
 def parent():
     print 'Starting parent'
     currentProc = GetCurrentProcess()
     if IsProcessInJob(currentProc):
         print >> sys.stderr, "You should not be in a job object to test"
--- a/addon-sdk/source/test/addons/content-permissions/main.js
+++ b/addon-sdk/source/test/addons/content-permissions/main.js
@@ -1,20 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const xulApp = require("sdk/system/xul-app");
 const { PageMod } = require("sdk/page-mod");
 const tabs = require("sdk/tabs");
+const { startServerAsync } = require("sdk/test/httpd");
+
+const serverPort = 8099;
 
 exports.testCrossDomainIframe = function(assert, done) {
-  let serverPort = 8099;
-  let server = require("sdk/test/httpd").startServerAsync(serverPort);
+  let server = startServerAsync(serverPort);
   server.registerPathHandler("/iframe", function handle(request, response) {
     response.write("<html><body>foo</body></html>");
   });
 
   let pageMod = PageMod({
     include: "about:*",
     contentScript: "new " + function ContentScriptScope() {
       self.on("message", function (url) {
@@ -26,29 +27,33 @@ exports.testCrossDomainIframe = function
         iframe.setAttribute("src", url);
         document.documentElement.appendChild(iframe);
       });
     },
     onAttach: function(w) {
       w.on("message", function (body) {
         assert.equal(body, "foo", "received iframe html content");
         pageMod.destroy();
-        w.tab.close();
-        server.stop(done);
+        w.tab.close(function() {
+          server.stop(done);
+        });
       });
+
       w.postMessage("http://localhost:8099/iframe");
     }
   });
 
-  tabs.open("about:credits");
+  tabs.open({
+    url: "about:home",
+    inBackground: true
+  });
 };
 
 exports.testCrossDomainXHR = function(assert, done) {
-  let serverPort = 8099;
-  let server = require("sdk/test/httpd").startServerAsync(serverPort);
+  let server = startServerAsync(serverPort);
   server.registerPathHandler("/xhr", function handle(request, response) {
     response.write("foo");
   });
 
   let pageMod = PageMod({
     include: "about:*",
     contentScript: "new " + function ContentScriptScope() {
       self.on("message", function (url) {
@@ -60,27 +65,24 @@ exports.testCrossDomainXHR = function(as
         };
         request.send(null);
       });
     },
     onAttach: function(w) {
       w.on("message", function (body) {
         assert.equal(body, "foo", "received XHR content");
         pageMod.destroy();
-        w.tab.close();
-        server.stop(done);
+        w.tab.close(function() {
+          server.stop(done);
+        });
       });
+
       w.postMessage("http://localhost:8099/xhr");
     }
   });
 
-  tabs.open("about:credits");
+  tabs.open({
+    url: "about:home",
+    inBackground: true
+  });
 };
 
-if (!xulApp.versionInRange(xulApp.platformVersion, "17.0a2", "*")) {
-  module.exports = {
-    "test Unsupported Application": function Unsupported (assert) {
-      assert.pass("This firefox version doesn't support cross-domain-content permission.");
-    }
-  };
-}
-
 require("sdk/test/runner").runTestsFromModule(module);
--- a/addon-sdk/source/test/addons/layout-change/main.js
+++ b/addon-sdk/source/test/addons/layout-change/main.js
@@ -74,18 +74,20 @@ exports["test compatibility"] = function
                require("sdk/console/traceback"), "sdk/console/traceback -> traceback");
 
   assert.equal(require("unload"),
                require("sdk/system/unload"), "sdk/system/unload -> unload");
 
   assert.equal(require("hotkeys"),
                require("sdk/hotkeys"), "sdk/hotkeys -> hotkeys");
 
-  assert.equal(require("clipboard"),
-               require("sdk/clipboard"), "sdk/clipboard -> clipboard");
+  if (app.is("Firefox")) {
+    assert.equal(require("clipboard"),
+                 require("sdk/clipboard"), "sdk/clipboard -> clipboard");
+  }
 
   assert.equal(require("windows"),
                require("sdk/windows"), "sdk/windows -> windows");
 
   assert.equal(require("page-worker"),
                require("sdk/page-worker"), "sdk/page-worker -> page-worker");
 
   assert.equal(require("timer"),
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/main/main.js
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+'use strict';
+
+const { setTimeout } = require('sdk/timers');
+
+let mainStarted = false;
+
+exports.main = function main(options, callbacks) {
+  mainStarted = true;
+
+  let tests = {};
+
+  tests.testMainArguments = function(assert) {
+  	assert.ok(!!options, 'options argument provided to main');
+    assert.ok('loadReason' in options, 'loadReason is in options provided by main');
+    assert.equal(typeof callbacks.print, 'function', 'callbacks.print is a function');
+    assert.equal(typeof callbacks.quit, 'function', 'callbacks.quit is a function');
+    assert.equal(options.loadReason, 'install', 'options.loadReason is install');
+  }
+
+  require('sdk/test/runner').runTestsFromModule({exports: tests});
+}
+
+// this causes a fail if main does not start
+setTimeout(function() {
+  if (mainStarted)
+  	return;
+
+  // main didn't start, fail..
+  require("sdk/test/runner").runTestsFromModule({exports: {
+  	testFail: function(assert) assert.fail('Main did not start..')
+  }});
+}, 500);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/main/package.json
@@ -0,0 +1,3 @@
+{
+  "id": "test-main"
+}
--- a/addon-sdk/source/test/addons/private-browsing-supported/main.js
+++ b/addon-sdk/source/test/addons/private-browsing-supported/main.js
@@ -1,24 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 const { merge } = require('sdk/util/object');
-const app = require("sdk/system/xul-app");
+const app = require('sdk/system/xul-app');
 const { isGlobalPBSupported } = require('sdk/private-browsing/utils');
 
 merge(module.exports,
   require('./test-tabs'),
   require('./test-page-mod'),
   require('./test-selection'),
   require('./test-panel'),
   require('./test-private-browsing'),
   isGlobalPBSupported ? require('./test-global-private-browsing') : {}
 );
 
 // Doesn't make sense to test window-utils and windows on fennec,
 // as there is only one window which is never private
-if (!app.is("Fennec"))
+if (!app.is('Fennec'))
   merge(module.exports, require('./test-windows'));
 
 require('sdk/test/runner').runTestsFromModule(module);
--- a/addon-sdk/source/test/addons/private-browsing-supported/test-tabs.js
+++ b/addon-sdk/source/test/addons/private-browsing-supported/test-tabs.js
@@ -1,15 +1,17 @@
 'use strict';
 
 const tabs = require('sdk/tabs');
 const { is } = require('sdk/system/xul-app');
 const { isPrivate } = require('sdk/private-browsing');
 const pbUtils = require('sdk/private-browsing/utils');
 const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
+const { promise: windowPromise, close, focus } = require('sdk/window/helpers');
+const { getMostRecentBrowserWindow } = require('sdk/window/utils');
 
 exports.testPrivateTabsAreListed = function (assert, done) {
   let originalTabCount = tabs.length;
 
   tabs.open({
     url: 'about:blank',
     isPrivate: true,
     onOpen: function(tab) {
@@ -21,12 +23,91 @@ exports.testPrivateTabsAreListed = funct
                      'New private window\'s tab are visible in tabs list');
       }
       else {
       // Global case, openDialog didn't opened a private window/tab
         assert.ok(!isPrivate(tab), "tab isn't private");
         assert.equal(tabs.length, originalTabCount + 1,
                      'New non-private window\'s tab is visible in tabs list');
       }
+
       tab.close(done);
     }
   });
 }
+
+exports.testOpenTabWithPrivateActiveWindowNoIsPrivateOption = function(assert, done) {
+  let window = getMostRecentBrowserWindow().OpenBrowserWindow({ private: true });
+
+  windowPromise(window, 'load').then(focus).then(function (window) {
+    assert.ok(isPrivate(window), 'new window is private');
+
+    tabs.open({
+      url: 'about:blank',
+      onOpen: function(tab) {
+        assert.ok(isPrivate(tab), 'new tab is private');
+        assert.ok(isPrivate(getOwnerWindow(tab)), 'new tab window is private');
+        assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the private window are the same');
+
+        close(window).then(done, assert.fail);
+      }
+    })
+  }, assert.fail).then(null, assert.fail);
+}
+
+exports.testOpenTabWithNonPrivateActiveWindowNoIsPrivateOption = function(assert, done) {
+  let window = getMostRecentBrowserWindow().OpenBrowserWindow({ private: false });
+
+  windowPromise(window, 'load').then(focus).then(function (window) {
+    assert.equal(isPrivate(window), false, 'new window is not private');
+
+    tabs.open({
+      url: 'about:blank',
+      onOpen: function(tab) {
+        assert.equal(isPrivate(tab), false, 'new tab is not private');
+        assert.equal(isPrivate(getOwnerWindow(tab)), false, 'new tab window is not private');
+        assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the new window are the same');
+
+        close(window).then(done, assert.fail);
+      }
+    })
+  }, assert.fail).then(null, assert.fail);
+}
+
+exports.testOpenTabWithPrivateActiveWindowWithIsPrivateOptionTrue = function(assert, done) {
+  let window = getMostRecentBrowserWindow().OpenBrowserWindow({ private: true });
+
+  windowPromise(window, 'load').then(focus).then(function (window) {
+    assert.ok(isPrivate(window), 'new window is private');
+
+    tabs.open({
+      url: 'about:blank',
+      isPrivate: true,
+      onOpen: function(tab) {
+        assert.ok(isPrivate(tab), 'new tab is private');
+        assert.ok(isPrivate(getOwnerWindow(tab)), 'new tab window is private');
+        assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the private window are the same');
+
+        close(window).then(done, assert.fail);
+      }
+    })
+  }, assert.fail).then(null, assert.fail);
+}
+
+exports.testOpenTabWithNonPrivateActiveWindowWithIsPrivateOptionFalse = function(assert, done) {
+  let window = getMostRecentBrowserWindow().OpenBrowserWindow({ private: false });
+
+  windowPromise(window, 'load').then(focus).then(function (window) {
+    assert.equal(isPrivate(window), false, 'new window is not private');
+
+    tabs.open({
+      url: 'about:blank',
+      isPrivate: false,
+      onOpen: function(tab) {
+        assert.equal(isPrivate(tab), false, 'new tab is not private');
+        assert.equal(isPrivate(getOwnerWindow(tab)), false, 'new tab window is not private');
+        assert.strictEqual(getOwnerWindow(tab), window, 'the tab window and the new window are the same');
+
+        close(window).then(done, assert.fail);
+      }
+    })
+  }, assert.fail).then(null, assert.fail);
+}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/simple-prefs/lib/main.js
@@ -0,0 +1,82 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+'use strict';
+
+const { Cu } = require('chrome');
+const sp = require('sdk/simple-prefs');
+const app = require('sdk/system/xul-app');
+const self = require('sdk/self');
+const tabs = require('sdk/tabs');
+
+const { AddonManager } = Cu.import('resource://gre/modules/AddonManager.jsm', {});
+
+exports.testDefaultValues = function (assert) {
+  assert.equal(sp.prefs.myHiddenInt, 5, 'myHiddenInt default is 5');
+  assert.equal(sp.prefs.myInteger, 8, 'myInteger default is 8');
+  assert.equal(sp.prefs.somePreference, 'TEST', 'somePreference default is correct');
+}
+
+exports.testOptionsType = function(assert, done) {
+  AddonManager.getAddonByID(self.id, function(aAddon) {
+    assert.equal(aAddon.optionsType, AddonManager.OPTIONS_TYPE_INLINE, 'options type is inline');
+    done();
+  });
+}
+
+if (app.is('Firefox')) {
+  exports.testAOM = function(assert, done) {
+      tabs.open({
+      	url: 'about:addons',
+      	onReady: function(tab) {
+          tab.attach({
+          	contentScript: 'AddonManager.getAddonByID("' + self.id + '", function(aAddon) {\n' +
+          		             'unsafeWindow.gViewController.viewObjects.detail.node.addEventListener("ViewChanged", function whenViewChanges() {\n' +
+          		               'unsafeWindow.gViewController.viewObjects.detail.node.removeEventListener("ViewChanged", whenViewChanges, false);\n' +
+          		               'setTimeout(function() {\n' + // TODO: figure out why this is necessary..
+                                 'self.postMessage({\n' +
+                                   'somePreference: getAttributes(unsafeWindow.document.querySelector("setting[title=\'some-title\']")),\n' +
+                                   'myInteger: getAttributes(unsafeWindow.document.querySelector("setting[title=\'my-int\']")),\n' +
+                                   'myHiddenInt: getAttributes(unsafeWindow.document.querySelector("setting[title=\'hidden-int\']"))\n' +
+                                 '});\n' +
+          		               '}, 250);\n' +
+          		             '}, false);\n' +
+                             'unsafeWindow.gViewController.commands.cmd_showItemDetails.doCommand(aAddon, true);\n' +
+                           '});\n' + 
+                           'function getAttributes(ele) {\n' +
+                             'if (!ele) return {};\n' +
+                             'return {\n' +
+                               'pref: ele.getAttribute("pref"),\n' +
+                               'type: ele.getAttribute("type"),\n' +
+                               'title: ele.getAttribute("title"),\n' +
+                               'desc: ele.getAttribute("desc")\n' +
+                             '}\n' +
+                           '}\n',
+            onMessage: function(msg) {
+              // test somePreference
+              assert.equal(msg.somePreference.type, 'string', 'some pref is a string');
+              assert.equal(msg.somePreference.pref, 'extensions.'+self.id+'.somePreference', 'somePreference path is correct');
+              assert.equal(msg.somePreference.title, 'some-title', 'somePreference title is correct');
+              assert.equal(msg.somePreference.desc, 'Some short description for the preference', 'somePreference description is correct');
+
+              // test myInteger
+              assert.equal(msg.myInteger.type, 'integer', 'myInteger is a int');
+              assert.equal(msg.myInteger.pref, 'extensions.'+self.id+'.myInteger', 'extensions.test-simple-prefs.myInteger');
+              assert.equal(msg.myInteger.title, 'my-int', 'myInteger title is correct');
+              assert.equal(msg.myInteger.desc, 'How many of them we have.', 'myInteger desc is correct');
+
+              // test myHiddenInt
+              assert.equal(msg.myHiddenInt.type, undefined, 'myHiddenInt was not displayed');
+              assert.equal(msg.myHiddenInt.pref, undefined, 'myHiddenInt was not displayed');
+              assert.equal(msg.myHiddenInt.title, undefined, 'myHiddenInt was not displayed');
+              assert.equal(msg.myHiddenInt.desc, undefined, 'myHiddenInt was not displayed');
+
+              tab.close(done);
+            }
+          });
+      	}
+      });
+  }
+}
+
+require('sdk/test/runner').runTestsFromModule(module);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/simple-prefs/package.json
@@ -0,0 +1,23 @@
+{
+  "id": "test-simple-prefs",
+    "preferences": [{
+        "name": "somePreference",
+        "title": "some-title",
+        "description": "Some short description for the preference",
+        "type": "string",
+        "value": "TEST"
+    },
+    {
+        "description": "How many of them we have.",
+        "name": "myInteger",
+        "type": "integer",
+        "value": 8,
+        "title": "my-int"
+    }, {
+        "name": "myHiddenInt",
+        "type": "integer",
+        "hidden": true,
+        "value": 5,
+        "title": "hidden-int"
+    }]
+}
--- a/addon-sdk/source/test/places-helper.js
+++ b/addon-sdk/source/test/places-helper.js
@@ -14,16 +14,17 @@ const tagsrv = Cc['@mozilla.org/browser/
               getService(Ci.nsITaggingService);
 const asyncHistory = Cc['@mozilla.org/browser/history;1'].
               getService(Ci.mozIAsyncHistory);
 const { send } = require('sdk/addon/events');
 const { setTimeout } = require('sdk/timers');
 const { newURI } = require('sdk/url/utils');
 const { defer, all } = require('sdk/core/promise');
 const { once } = require('sdk/system/events');
+const { set } = require('sdk/preferences/service');
 const {
   Bookmark, Group, Separator,
   save, search,
   MENU, TOOLBAR, UNSORTED
 } = require('sdk/places/bookmarks');
 
 function invalidResolve (assert) {
   return function (e) {
@@ -40,22 +41,35 @@ function invalidReject (assert) {
 exports.invalidReject = invalidReject;
 
 // Removes all children of group
 function clearBookmarks (group) {
   group
    ? bmsrv.removeFolderChildren(group.id)
    : clearAllBookmarks();
 }
-exports.clearBookmarks = clearBookmarks;
 
 function clearAllBookmarks () {
   [MENU, TOOLBAR, UNSORTED].forEach(clearBookmarks);
 }
-exports.clearAllBookmarks = clearAllBookmarks;
+
+function clearHistory (done) {
+  hsrv.removeAllPages();
+  once('places-expiration-finished', done);
+}
+
+// Cleans bookmarks and history and disables maintanance
+function resetPlaces (done) {
+  // Set last maintenance to current time to prevent
+  // Places DB maintenance occuring and locking DB
+  set('places.database.lastMaintenance', Math.floor(Date.now() / 1000));
+  clearAllBookmarks();
+  clearHistory(done);
+}
+exports.resetPlaces = resetPlaces;
 
 function compareWithHost (assert, item) {
   let id = item.id;
   let type = item.type === 'group' ? bmsrv.TYPE_FOLDER : bmsrv['TYPE_' + item.type.toUpperCase()];
   let url = item.url && !item.url.endsWith('/') ? item.url + '/' : item.url;
 
   if (type === bmsrv.TYPE_BOOKMARK) {
     assert.equal(url, bmsrv.getBookmarkURI(id).spec.toString(), 'Matches host url');
@@ -100,22 +114,16 @@ function createVisit (url) {
   place.visits = [{
     transitionType: hsrv.TRANSITION_LINK,
     visitDate: +(new Date()) * 1000,
     referredURI: undefined
   }];
   return place;
 }
 
-function clearHistory (done) {
-  hsrv.removeAllPages();
-  once('places-expiration-finished', done);
-}
-exports.clearHistory = clearHistory;
-
 function createBookmark (data) {
   data = data || {};
   let item = {
     title: data.title || 'Moz',
     url: data.url || (!data.type || data.type === 'bookmark' ?
       'http://moz.com/' :
       undefined),
     tags: data.tags || (!data.type || data.type === 'bookmark' ?
--- a/addon-sdk/source/test/tabs/test-fennec-tabs.js
+++ b/addon-sdk/source/test/tabs/test-fennec-tabs.js
@@ -106,22 +106,21 @@ exports.testTabProperties = function(tes
   let tabsLen = tabs.length;
   tabs.open({
     url: url,
     onReady: function(tab) {
       test.assertEqual(tab.title, "foo", "title of the new tab matches");
       test.assertEqual(tab.url, url, "URL of the new tab matches");
       test.assert(tab.favicon, "favicon of the new tab is not empty");
       // TODO: remove need for this test by implementing the favicon feature
-      // Poors man deepEqual with JSON.stringify...
-      test.assertEqual(JSON.stringify(messages),
-                       JSON.stringify(['tab.favicon is deprecated, and ' +
-                          'currently favicon helpers are not yet supported ' +
-                          'by Fennec']),
-                       "favicon logs an error for now");
+      test.assertEqual(messages[0].msg,
+        "tab.favicon is deprecated, and " +
+        "currently favicon helpers are not yet supported " +
+        "by Fennec",
+        "favicon logs an error for now");
       test.assertEqual(tab.style, null, "style of the new tab matches");
       test.assertEqual(tab.index, tabsLen, "index of the new tab matches");
       test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
       test.assertNotEqual(tab.id, null, "a tab object always has an id property");
 
       tab.close(function() {
         loader.unload();
 
--- a/addon-sdk/source/test/test-addon-installer.js
+++ b/addon-sdk/source/test/test-addon-installer.js
@@ -128,17 +128,9 @@ exports["test Update"] = function (asser
   function next() {
     events = [];
     AddonInstaller.install(ADDON_PATH).then(onInstalled, onFailure);
   }
 
   next();
 }
 
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass("Skipping this test until Fennec support is implemented.");
-    }
-  }
-}
-
 require("test").run(exports);
--- a/addon-sdk/source/test/test-addon-page.js
+++ b/addon-sdk/source/test/test-addon-page.js
@@ -1,14 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 'use strict';
 
+module.metadata = {
+  engines: {
+    'Firefox': '*'
+  }
+};
+
 const { isTabOpen, activateTab, openTab,
         closeTab, getURI } = require('sdk/tabs/utils');
 const windows = require('sdk/deprecated/window-utils');
 const { LoaderWithHookedConsole } = require('sdk/test/loader');
 const { setTimeout } = require('sdk/timers');
 const { is } = require('sdk/system/xul-app');
 const tabs = require('sdk/tabs');
 const isAustralis = "gCustomizeMode" in windows.activeBrowserWindow;
--- a/addon-sdk/source/test/test-array.js
+++ b/addon-sdk/source/test/test-array.js
@@ -80,8 +80,16 @@ exports.testUnique = function(test) {
 
   function compareArray (a, b) {
     test.assertEqual(a.length, b.length);
     for (let i = 0; i < a.length; i++) {
       test.assertEqual(a[i], b[i]);
     }
   }
 };
+
+exports.testFind = function(test) {
+  let isOdd = (x) => x % 2;
+  test.assertEqual(array.find([2, 4, 5, 7, 8, 9], isOdd), 5);
+  test.assertEqual(array.find([2, 4, 6, 8], isOdd), undefined);
+  test.assertEqual(array.find([2, 4, 6, 8], isOdd, null), null);
+};
+
--- a/addon-sdk/source/test/test-browser-events.js
+++ b/addon-sdk/source/test/test-browser-events.js
@@ -87,19 +87,9 @@ exports["test browser events ignore othe
       done();
     }
   });
 
   // Open window and close it to trigger observers.
   let window = open("data:text/html,not a browser");
 };
 
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See bug 793071");
-    }
-  }
-}
-
 require("test").run(exports);
--- a/addon-sdk/source/test/test-clipboard.js
+++ b/addon-sdk/source/test/test-clipboard.js
@@ -1,14 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+require("sdk/clipboard");
+
 const { Cc, Ci } = require("chrome");
 
 const imageTools = Cc["@mozilla.org/image/tools;1"].
                     getService(Ci.imgITools);
 
 const io = Cc["@mozilla.org/network/io-service;1"].
                     getService(Ci.nsIIOService);
 
@@ -213,21 +215,9 @@ exports["test Set Image Type Wrong Data"
 
   var wrongPNG = "data:image/png" + base64jpeg.substr(15);
 
   assert.throws(function () {
     clip.set(wrongPNG, flavor);
   }, "Unable to decode data given in a valid image.");
 };
 
-// TODO: Test error cases.
-
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See bug 789757");
-    }
-  }
-}
-
 require("test").run(exports)
--- a/addon-sdk/source/test/test-content-script.js
+++ b/addon-sdk/source/test/test-content-script.js
@@ -1,57 +1,71 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const hiddenFrames = require("sdk/frame/hidden-frame");
-
+const { create: makeFrame } = require("sdk/frame/utils");
+const { window } = require("sdk/addon/window");
 const { Loader } = require('sdk/test/loader');
+const { URL } = require("sdk/url");
+const testURI = require("sdk/self").data.url("test.html");
+const testHost = URL(testURI).scheme + '://' + URL(testURI).host;
 
 /*
  * Utility function that allow to easily run a proxy test with a clean
  * new HTML document. See first unit test for usage.
  */
 function createProxyTest(html, callback) {
   return function (assert, done) {
-    let url = 'data:text/html;charset=utf-8,' + encodeURI(html);
-
-    let hiddenFrame = hiddenFrames.add(hiddenFrames.HiddenFrame({
-      onReady: function () {
+    let url = 'data:text/html;charset=utf-8,' + encodeURIComponent(html);
+    let principalLoaded = false;
 
-        function onDOMReady() {
-          hiddenFrame.element.removeEventListener("DOMContentLoaded", onDOMReady,
-                                                  false);
+    let element = makeFrame(window.document, {
+      nodeName: "iframe",
+      type: "content",
+      allowJavascript: true,
+      allowPlugins: true,
+      allowAuth: true,
+      uri: testURI
+    });
 
-          let xrayWindow = hiddenFrame.element.contentWindow;
-          let rawWindow = xrayWindow.wrappedJSObject;
+    element.addEventListener("DOMContentLoaded", onDOMReady, false);
+
+    function onDOMReady() {
+      // Reload frame after getting principal from `testURI`
+      if (!principalLoaded) {
+        element.setAttribute("src", url);
+        principalLoaded = true;
+        return;
+      }
 
-          let isDone = false;
-          let helper = {
-            xrayWindow: xrayWindow,
-            rawWindow: rawWindow,
-            createWorker: function (contentScript) {
-              return createWorker(assert, xrayWindow, contentScript, helper.done);
-            },
-            done: function () {
-              if (isDone)
-                return;
-              isDone = true;
-              hiddenFrames.remove(hiddenFrame);
-              done();
-            }
-          }
-          callback(helper, assert);
+      assert.equal(element.getAttribute("src"), url, "correct URL loaded");
+      element.removeEventListener("DOMContentLoaded", onDOMReady,
+                                                  false);
+      let xrayWindow = element.contentWindow;
+      let rawWindow = xrayWindow.wrappedJSObject;
+
+      let isDone = false;
+      let helper = {
+        xrayWindow: xrayWindow,
+        rawWindow: rawWindow,
+        createWorker: function (contentScript) {
+          return createWorker(assert, xrayWindow, contentScript, helper.done);
+        },
+        done: function () {
+          if (isDone)
+            return;
+          isDone = true;
+          element.parentNode.removeChild(element);
+          done();
         }
-
-        hiddenFrame.element.addEventListener("DOMContentLoaded", onDOMReady, false);
-        hiddenFrame.element.setAttribute("src", url);
-
-      }
-    }));
+      };
+      callback(helper, assert);
+    }
   };
 }
 
 function createWorker(assert, xrayWindow, contentScript, done) {
   let loader = Loader(module);
   let Worker = loader.require("sdk/content/worker").Worker;
   let worker = Worker({
     window: xrayWindow,
@@ -160,19 +174,19 @@ exports["test postMessage"] = createProx
   // Listen without proxies, to check that it will work in regular case
   // simulate listening from a web document.
   ifWindow.addEventListener("message", function listener(event) {
     ifWindow.removeEventListener("message", listener, false);
     // As we are in system principal, event is an XrayWrapper
     // xrays use current compartments when calling postMessage method.
     // Whereas js proxies was using postMessage method compartment,
     // not the caller one.
-    assert.equal(event.source, helper.xrayWindow,
-                 "event.source is the top window");
-    assert.equal(event.origin, "null", "origin is null");
+    assert.strictEqual(event.source, helper.xrayWindow,
+                      "event.source is the top window");
+    assert.equal(event.origin, testHost, "origin matches testHost");
 
     assert.equal(event.data, "{\"foo\":\"bar\\n \\\"escaped\\\".\"}",
                      "message data is correct");
 
     helper.done();
   }, false);
 
   helper.createWorker(
@@ -211,38 +225,40 @@ exports["test Object Listener"] = create
     }
   );
 
 });
 
 exports["test Object Listener 2"] = createProxyTest("", function (helper) {
 
   helper.createWorker(
-    'new ' + function ContentScriptScope() {
+    ('new ' + function ContentScriptScope() {
+      // variable replaced with `testHost`
+      let testHost = "TOKEN";
       // Verify object as DOM event listener
       let myMessageListener = {
         called: false,
         handleEvent: function(event) {
           window.removeEventListener("message", myMessageListener, true);
 
           assert(this == myMessageListener, "`this` is the original object");
           assert(!this.called, "called only once");
           this.called = true;
           assert(event.target == document.defaultView, "event.target is the wrapped window");
           assert(event.source == document.defaultView, "event.source is the wrapped window");
-          assert(event.origin == "null", "origin is null");
+          assert(event.origin == testHost, "origin matches testHost");
           assert(event.data == "ok", "message data is correct");
           done();
         }
       };
 
       window.addEventListener("message", myMessageListener, true);
       document.defaultView.postMessage("ok", '*');
     }
-  );
+  ).replace("TOKEN", testHost));
 
 });
 
 let html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' +
              '<input id="input2" type="checkbox" />';
 
 /* Disable test to keep tree green until Bug 756214 is fixed.
 exports.testStringOverload = createProxyTest(html, function (helper, test) {
@@ -825,19 +841,9 @@ exports["test MutationObvserver"] = crea
 
       // Modify the DOM
       link.setAttribute("href", "bar");
     }
   );
 
 });
 
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See bug 806813");
-    }
-  }
-}
-
 require("test").run(exports);
--- a/addon-sdk/source/test/test-content-symbiont.js
+++ b/addon-sdk/source/test/test-content-symbiont.js
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Cc, Ci } = require('chrome');
 const { Symbiont } = require('sdk/content/symbiont');
 const self = require('sdk/self');
 const { close } = require('sdk/window/helpers');
+const app = require("sdk/system/xul-app");
 
 function makeWindow() {
   let content =
     '<?xml version="1.0"?>' +
     '<window ' +
     'xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">' +
     '<iframe id="content" type="content"/>' +
     '</window>';
@@ -61,16 +62,21 @@ exports['test:constructing symbiont && v
     "start",
     "contentScriptWhen is as specified in options."
   );
 
   contentSymbiont.destroy();
 };
 
 exports["test:communication with worker global scope"] = function(assert, done) {
+  if (app.is('Fennec')) {
+    assert.pass('Test skipped on Fennec');
+    done();
+  }
+
   let window = makeWindow();
   let contentSymbiont;
 
   function onMessage1(message) {
     assert.equal(message, 1, "Program gets message via onMessage.");
     contentSymbiont.removeListener('message', onMessage1);
     contentSymbiont.on('message', onMessage2);
     contentSymbiont.postMessage(2);
@@ -172,19 +178,9 @@ exports["test:`addon` is not available w
   worker.port.on('cs-to-addon', function (hasAddon) {
     assert.equal(hasAddon, false,
       "`addon` is not available");
     worker.destroy();
     done();
   });
 };
 
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See bug 806815");
-    }
-  }
-}
-
 require("test").run(exports);
--- a/addon-sdk/source/test/test-content-worker.js
+++ b/addon-sdk/source/test/test-content-worker.js
@@ -1,14 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+// Skipping due to window creation being unsupported in Fennec
+module.metadata = {
+  engines: {
+    'Firefox': '*'
+  }
+};
+
 const { Cc, Ci } = require("chrome");
 const { setTimeout } = require("sdk/timers");
 const { LoaderWithHookedConsole } = require("sdk/test/loader");
 const { Worker } = require("sdk/content/worker");
 const { close } = require("sdk/window/helpers");
 
 const DEFAULT_CONTENT_URL = "data:text/html;charset=utf-8,foo";
 
@@ -685,19 +692,9 @@ exports["test:global postMessage"] = Wor
     });
 
     assert.equal(worker.url, window.location.href,
                      "worker.url works");
     worker.postMessage("hi!");
   }
 );
 
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See bug 806817");
-    }
-  }
-}
-
 require("test").run(exports);
--- a/addon-sdk/source/test/test-context-menu.js
+++ b/addon-sdk/source/test/test-context-menu.js
@@ -2,16 +2,18 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  'use strict';
 
 let { Cc, Ci } = require("chrome");
 
+require("sdk/context-menu");
+
 const { Loader } = require('sdk/test/loader');
 const timer = require("sdk/timers");
 const { merge } = require("sdk/util/object");
 
 // These should match the same constants in the module.
 const ITEM_CLASS = "addon-context-menu-item";
 const SEPARATOR_CLASS = "addon-context-menu-separator";
 const OVERFLOW_THRESH_DEFAULT = 10;
@@ -3131,26 +3133,16 @@ exports.testSelectionInOuterFrameNoMatch
       test.checkMenu(items, items, []);
       test.done();
     });
   });
 };
 
 // NO TESTS BELOW THIS LINE! ///////////////////////////////////////////////////
 
-// Run only a dummy test if context-menu doesn't support the host app.
-if (!require("sdk/system/xul-app").is("Firefox")) {
-  module.exports = {
-    testAppNotSupported: function (test) {
-      test.pass("context-menu does not support this application.");
-    }
-  };
-}
-
-
 // This makes it easier to run tests by handling things like opening the menu,
 // opening new windows, making assertions, etc.  Methods on |test| can be called
 // on instances of this class.  Don't forget to call done() to end the test!
 // WARNING: This looks up items in popups by comparing labels, so don't give two
 // items the same label.
 function TestHelper(test) {
   // default waitUntilDone timeout is 10s, which is too short on the win7
   // buildslave
--- a/addon-sdk/source/test/test-fs.js
+++ b/addon-sdk/source/test/test-fs.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 const { pathFor } = require("sdk/system");
 const fs = require("sdk/io/fs");
 const url = require("sdk/url");
 const path = require("sdk/fs/path");
 const { Buffer } = require("sdk/io/buffer");
+const { is } = require("sdk/system/xul-app");
 
 // Use profile directory to list / read / write files.
 const profilePath = pathFor("ProfD");
 const fileNameInProfile = "compatibility.ini";
 const dirNameInProfile = "extensions";
 const filePathInProfile = path.join(profilePath, fileNameInProfile);
 const dirPathInProfile = path.join(profilePath, dirNameInProfile);
 const mkdirPath = path.join(profilePath, "sdk-fixture-mkdir");
@@ -21,29 +22,28 @@ const writePath = path.join(profilePath,
 const unlinkPath = path.join(profilePath, "sdk-fixture-unlink");
 const truncatePath = path.join(profilePath, "sdk-fixture-truncate");
 const renameFromPath = path.join(profilePath, "sdk-fixture-rename-from");
 const renameToPath = path.join(profilePath, "sdk-fixture-rename-to");
 
 const profileEntries = [
   "compatibility.ini",
   "extensions",
-  "extensions.ini",
   "prefs.js"
   // There are likely to be a lot more files but we can't really
   // on consistent list so we limit to this.
 ];
 
-exports["test readir"] = function(assert, end) {
+exports["test readdir"] = function(assert, end) {
   var async = false;
   fs.readdir(profilePath, function(error, entries) {
     assert.ok(async, "readdir is async");
     assert.ok(!error, "there is no error when reading directory");
     assert.ok(profileEntries.length <= entries.length,
-              "got et least number of entries we expect");
+              "got at least number of entries we expect");
     assert.ok(profileEntries.every(function(entry) {
                 return entries.indexOf(entry) >= 0;
               }), "all profiles are present");
     end();
   });
 
   async = true;
 };
@@ -62,23 +62,23 @@ exports["test readdir error"] = function
 
   async = true;
 };
 
 exports["test readdirSync"] = function(assert) {
   var async = false;
   var entries = fs.readdirSync(profilePath);
   assert.ok(profileEntries.length <= entries.length,
-            "got et least number of entries we expect");
+            "got at least number of entries we expect");
   assert.ok(profileEntries.every(function(entry) {
     return entries.indexOf(entry) >= 0;
   }), "all profiles are present");
 };
 
-exports["test readirSync error"] = function(assert) {
+exports["test readdirSync error"] = function(assert) {
   var async = false;
   var path = profilePath + "-does-not-exists";
   try {
     fs.readdirSync(path);
     assert.fail(Error("No error was thrown"));
   } catch (error) {
     assert.equal(error.message, "ENOENT, readdir " + path);
     assert.equal(error.code, "ENOENT", "error has a code");
@@ -87,16 +87,17 @@ exports["test readirSync error"] = funct
   }
 };
 
 exports["test readFile"] = function(assert, end) {
   let async = false;
   fs.readFile(filePathInProfile, function(error, content) {
     assert.ok(async, "readFile is async");
     assert.ok(!error, "error is falsy");
+
     assert.ok(Buffer.isBuffer(content), "readFile returns buffer");
     assert.ok(typeof(content.length) === "number", "buffer has length");
     assert.ok(content.toString().indexOf("[Compatibility]") >= 0,
               "content contains expected data");
     end();
   });
   async = true;
 };
@@ -333,17 +334,16 @@ exports["test fs.truncateSync fs.unlinkS
 
 
 exports["test fs.truncate"] = function(assert, end) {
   let path = truncatePath;
   if (!fs.existsSync(path)) {
     let async = false;
     fs.truncate(path, 0, function(error) {
       assert.ok(async, "truncate is async");
-      console.log(error);
       assert.ok(!error, "no error");
       assert.equal(fs.existsSync(path), true, "file was created");
       fs.unlinkSync(path);
       assert.equal(fs.existsSync(path), false, "file was removed");
       end();
     })
     async = true;
   }
@@ -454,9 +454,33 @@ exports["test fs.writeFile"] = function(
     fs.unlinkSync(path);
     assert.ok(!fs.exists(path), "file was removed");
 
     end();
   });
   async = true;
 };
 
+exports["test fs.writeFile (with large files)"] = function(assert, end) {
+  let path = writePath;
+  let content = "";
+
+  for (var i = 0; i < 100000; i++) {
+    content += "buffer\n";
+  }
+
+  var async = false;
+  fs.writeFile(path, content, function(error) {
+    assert.ok(async, "fs write is async");
+    assert.ok(!error, "error is falsy");
+    assert.ok(fs.existsSync(path), "file was created");
+    assert.equal(fs.readFileSync(path).toString(),
+                 content,
+                 "contet was written");
+    fs.unlinkSync(path);
+    assert.ok(!fs.exists(path), "file was removed");
+
+    end();
+  });
+  async = true;
+};
+
 require("test").run(exports);
--- a/addon-sdk/source/test/test-indexed-db.js
+++ b/addon-sdk/source/test/test-indexed-db.js
@@ -3,33 +3,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 let xulApp = require("sdk/system/xul-app");
 if (xulApp.versionInRange(xulApp.platformVersion, "16.0a1", "*")) {
 new function tests() {
 
-const { indexedDB, IDBKeyRange, DOMException, IDBCursor, IDBTransaction,
-        IDBOpenDBRequest, IDBDatabase, IDBIndex, IDBObjectStore, IDBRequest
+const { indexedDB, IDBKeyRange, DOMException
       } = require("sdk/indexed-db");
 
 exports["test indexedDB is frozen"] = function(assert){
   let original = indexedDB.open;
   let f = function(){};
   assert.throws(function(){indexedDB.open = f});
   assert.equal(indexedDB.open,original);
   assert.notEqual(indexedDB.open,f);
 
 };
 
 exports["test db variables"] = function(assert) {
-  [ indexedDB, IDBKeyRange, DOMException, IDBCursor, IDBTransaction,
-    IDBOpenDBRequest, IDBOpenDBRequest, IDBDatabase, IDBIndex,
-    IDBObjectStore, IDBRequest
+  [ indexedDB, IDBKeyRange, DOMException
   ].forEach(function(value) {
     assert.notEqual(typeof(value), "undefined", "variable is defined");
   });
 }
 
 exports["test open"] = function(assert, done) {
   let request = indexedDB.open("MyTestDatabase");
   request.onerror = function(event) {
--- a/addon-sdk/source/test/test-observer-service.js
+++ b/addon-sdk/source/test/test-observer-service.js
@@ -17,23 +17,23 @@ exports.testUnloadAndErrorLogging = func
   var badCb = function(subject, data) {
     throw new Error("foo");
   };
   sbobsvc.add("blarg", cb);
   observers.notify("blarg", "yo yo");
   test.assertEqual(timesCalled, 1);
   sbobsvc.add("narg", badCb);
   observers.notify("narg", "yo yo");
-  var lines = messages[0].split("\n");
-  test.assertEqual(lines[0], "error: " + require("sdk/self").name + ": An exception occurred.");
-  test.assertEqual(lines[0], "error: " + require("sdk/self").name + ": An exception occurred.");
-  test.assertEqual(lines[1], "Error: foo");
+
+  test.assertEqual(messages[0], "console.error: " + require("sdk/self").name + ": \n");
+  var lines = messages[1].split("\n");
+  test.assertEqual(lines[0], "  Message: Error: foo");
+  test.assertEqual(lines[1], "  Stack:");
   // Keep in mind to update "18" to the line of "throw new Error("foo")"
-  test.assertEqual(lines[2], module.uri + " 18");
-  test.assertEqual(lines[3], "Traceback (most recent call last):");
+  test.assert(lines[2].indexOf(module.uri + ":18") != -1);
 
   loader.unload();
   observers.notify("blarg", "yo yo");
   test.assertEqual(timesCalled, 1);
 };
 
 exports.testObserverService = function(test) {
   var ios = Cc['@mozilla.org/network/io-service;1']
--- a/addon-sdk/source/test/test-page-mod.js
+++ b/addon-sdk/source/test/test-page-mod.js
@@ -14,16 +14,18 @@ const windowUtils = require('sdk/depreca
 const { getTabContentWindow, getActiveTab, setTabURL, openTab, closeTab } = require('sdk/tabs/utils');
 const xulApp = require("sdk/system/xul-app");
 const { data, isPrivateBrowsingSupported } = require('sdk/self');
 const { isPrivate } = require('sdk/private-browsing');
 const { openWebpage } = require('./private-browsing/helper');
 const { isTabPBSupported, isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
 const promise = require("sdk/core/promise");
 const { pb } = require('./private-browsing/helper');
+const { URL } = require("sdk/url");
+const testPageURI = require("sdk/self").data.url("test.html");
 
 /* XXX This can be used to delay closing the test Firefox instance for interactive
  * testing or visual inspection. This test is registered first so that it runs
  * the last. */
 exports.delay = function(test) {
   if (false) {
     test.waitUntilDone(60000);
     timer.setTimeout(function() {test.done();}, 4000);
@@ -114,26 +116,26 @@ exports.testPageModIncludes = function(t
       // so we attach it on 'start'.
       contentScriptWhen: 'start',
       onAttach: function(worker) {
         worker.postMessage(this.include[0]);
       }
     };
   }
 
-  testPageMod(test, "about:buildconfig", [
+  testPageMod(test, testPageURI, [
       createPageModTest("*", false),
       createPageModTest("*.google.com", false),
-      createPageModTest("about:*", true),
-      createPageModTest("about:", false),
-      createPageModTest("about:buildconfig", true)
+      createPageModTest("resource:*", true),
+      createPageModTest("resource:", false),
+      createPageModTest(testPageURI, true)
     ],
     function (win, done) {
-      test.waitUntil(function () win.localStorage["about:buildconfig"],
-                     "about:buildconfig page-mod to be executed")
+      test.waitUntil(function () win.localStorage[testPageURI],
+                     testPageURI + " page-mod to be executed")
           .then(function () {
             asserts.forEach(function(fn) {
               fn(test, win);
             });
             done();
           });
     }
     );
--- a/addon-sdk/source/test/test-page-worker.js
+++ b/addon-sdk/source/test/test-page-worker.js
@@ -2,16 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Loader } = require('sdk/test/loader');
 const Pages = require("sdk/page-worker");
 const Page = Pages.Page;
+const { URL } = require("sdk/url");
+const testURI = require("sdk/self").data.url("test.html");
 
 const ERR_DESTROYED =
   "Couldn't find the worker to receive this message. " +
   "The script may not be initialized yet, or may already have been unloaded.";
 
 exports.testSimplePageCreation = function(assert, done) {
   let page = new Page({
     contentScript: "self.postMessage(window.location.href)",
@@ -151,24 +153,33 @@ exports.testValidateOptions = function(a
     "Validation correctly denied a non-function onMessage."
   );
 
   assert.pass("Options validation is working.");
 }
 
 exports.testContentAndAllowGettersAndSetters = function(assert, done) {
   let content = "data:text/html;charset=utf-8,<script>window.localStorage.allowScript=3;</script>";
+
+  // Load up the page with testURI initially for the resource:// principal,
+  // then load the actual data:* content, as data:* URIs no longer
+  // have localStorage
   let page = Page({
-    contentURL: content,
-    contentScript: "self.postMessage(window.localStorage.allowScript)",
+    contentURL: testURI,
+    contentScript: "if (window.location.href==='"+testURI+"')" +
+      "  self.postMessage('reload');" +
+      "else " +
+      "  self.postMessage(window.localStorage.allowScript)",
     contentScriptWhen: "end",
     onMessage: step0
   });
 
   function step0(message) {
+    if (message === 'reload')
+      return page.contentURL = content;
     assert.equal(message, "3",
                      "Correct value expected for allowScript - 3");
     assert.equal(page.contentURL, content,
                      "Correct content expected");
     page.removeListener('message', step0);
     page.on('message', step1);
     page.allow = { script: false };
     page.contentURL = content =
--- a/addon-sdk/source/test/test-places-bookmarks.js
+++ b/addon-sdk/source/test/test-places-bookmarks.js
@@ -20,19 +20,19 @@ const { defer: async } = require('sdk/la
 const { before, after } = require('sdk/test/utils');
 
 const {
   Bookmark, Group, Separator,
   save, search, remove,
   MENU, TOOLBAR, UNSORTED
 } = require('sdk/places/bookmarks');
 const {
-  invalidResolve, invalidReject, clearBookmarks, createTree,
-  compareWithHost, clearAllBookmarks, createBookmark, createBookmarkItem,
-  createBookmarkTree, addVisits
+  invalidResolve, invalidReject, createTree,
+  compareWithHost, createBookmark, createBookmarkItem,
+  createBookmarkTree, addVisits, resetPlaces
 } = require('./places-helper');
 const { promisedEmitter } = require('sdk/places/utils');
 const bmsrv = Cc['@mozilla.org/browser/nav-bookmarks-service;1'].
                     getService(Ci.nsINavBookmarksService);
 const tagsrv = Cc['@mozilla.org/browser/tagging-service;1'].
                     getService(Ci.nsITaggingService);
 
 exports.testDefaultFolders = function (assert) {
@@ -518,67 +518,73 @@ exports.testRemoveAllChildren = function
 };
 
 exports.testResolution = function (assert, done) {
   let firstSave, secondSave;
   createBookmarkItem().then((item) => {
     firstSave = item;
     assert.ok(item.updated, 'bookmark has updated time');
     item.title = 'my title';
-    save(item).on('data', (item) => {
-      secondSave = item;
-      assert.ok(firstSave.updated < secondSave.updated, 'snapshots have different update times');
-      firstSave.title = 'updated title';
-      save(firstSave, { resolve: (mine, theirs) => {
-        assert.equal(mine.title, 'updated title', 'correct data for my object');
-        assert.equal(theirs.title, 'my title', 'correct data for their object');
-        assert.equal(mine.url, theirs.url, 'other data is equal');
-        assert.equal(mine.group, theirs.group, 'other data is equal');
-        assert.ok(mine !== firstSave, 'instance is not passed in');
-        assert.ok(theirs !== secondSave, 'instance is not passed in');
-        assert.equal(mine.toString(), '[object Object]', 'serialized objects');
-        assert.equal(theirs.toString(), '[object Object]', 'serialized objects');
-        mine.title = 'a new title';
-        return mine;
-      }}).on('end', (results) => {
-        let result = results[0];
-        assert.equal(result.title, 'a new title', 'resolve handles results');
-        done();
-      });
-    });
+    // Ensure delay so a different save time is set
+    return delayed(item);
+  }).then(saveP)
+  .then(items => {
+    let item = items[0];
+    secondSave = item;
+    assert.ok(firstSave.updated < secondSave.updated, 'snapshots have different update times');
+    firstSave.title = 'updated title';
+    return saveP(firstSave, { resolve: (mine, theirs) => {
+      assert.equal(mine.title, 'updated title', 'correct data for my object');
+      assert.equal(theirs.title, 'my title', 'correct data for their object');
+      assert.equal(mine.url, theirs.url, 'other data is equal');
+      assert.equal(mine.group, theirs.group, 'other data is equal');
+      assert.ok(mine !== firstSave, 'instance is not passed in');
+      assert.ok(theirs !== secondSave, 'instance is not passed in');
+      assert.equal(mine.toString(), '[object Object]', 'serialized objects');
+      assert.equal(theirs.toString(), '[object Object]', 'serialized objects');
+      mine.title = 'a new title';
+      return mine;
+    }});
+  }).then((results) => {
+    let result = results[0];
+    assert.equal(result.title, 'a new title', 'resolve handles results');
+    done();
   });
 };
 
 /*
  * Same as the resolution test, but with the 'unsaved' snapshot
  */
 exports.testResolutionMapping = function (assert, done) {
   let bookmark = Bookmark({ title: 'moz', url: 'http://bookmarks4life.com/' });
-  save(bookmark).on('end', (saved) => {
-    saved = saved[0];
+  let saved;
+  saveP(bookmark).then(data => {
+    saved = data[0];
     saved.title = 'updated title';
-    save(saved).on('end', () => {
-      bookmark.title = 'conflicting title';
-      save(bookmark, { resolve: (mine, theirs) => {
-        assert.equal(mine.title, 'conflicting title', 'correct data for my object');
-        assert.equal(theirs.title, 'updated title', 'correct data for their object');
-        assert.equal(mine.url, theirs.url, 'other data is equal');
-        assert.equal(mine.group, theirs.group, 'other data is equal');
-        assert.ok(mine !== bookmark, 'instance is not passed in');
-        assert.ok(theirs !== saved, 'instance is not passed in');
-        assert.equal(mine.toString(), '[object Object]', 'serialized objects');
-        assert.equal(theirs.toString(), '[object Object]', 'serialized objects');
-        mine.title = 'a new title';
-        return mine;
-      }}).on('end', (results) => {
-        let result = results[0];
-        assert.equal(result.title, 'a new title', 'resolve handles results');
-        done();
-      });
-    });
+    // Ensure a delay for different updated times
+    return delayed(saved);
+  }).then(saveP)
+  .then(() => {
+    bookmark.title = 'conflicting title';
+    return saveP(bookmark, { resolve: (mine, theirs) => {
+      assert.equal(mine.title, 'conflicting title', 'correct data for my object');
+      assert.equal(theirs.title, 'updated title', 'correct data for their object');
+      assert.equal(mine.url, theirs.url, 'other data is equal');
+      assert.equal(mine.group, theirs.group, 'other data is equal');
+      assert.ok(mine !== bookmark, 'instance is not passed in');
+      assert.ok(theirs !== saved, 'instance is not passed in');
+      assert.equal(mine.toString(), '[object Object]', 'serialized objects');
+      assert.equal(theirs.toString(), '[object Object]', 'serialized objects');
+      mine.title = 'a new title';
+      return mine;
+    }});
+  }).then((results) => {
+    let result = results[0];
+    assert.equal(result.title, 'a new title', 'resolve handles results');
+    done();
   });
 };
 
 exports.testUpdateTags = function (assert, done) {
   createBookmarkItem({ tags: ['spidermonkey'] }).then(bookmark => {
     bookmark.tags.add('jagermonkey');
     bookmark.tags.add('ionmonkey');
     bookmark.tags.delete('spidermonkey');
@@ -936,24 +942,26 @@ exports.testCheckSaveOrder = function (a
   saveP(bookmarks).then(results => {
     for (let i = 0; i < bookmarks.length; i++)
       assert.equal(results[i].url, bookmarks[i].url,
         'correct ordering of bookmark results');
     done();
   });
 };
 
-before(exports, name => {
-  clearAllBookmarks();
-});
-
-after(exports, name => {
-  clearAllBookmarks();
-});
+before(exports, (name, assert, done) => resetPlaces(done));
+after(exports, (name, assert, done) => resetPlaces(done));
 
 function saveP () {
   return promisedEmitter(save.apply(null, Array.slice(arguments)));
 }
 
 function searchP () {
   return promisedEmitter(search.apply(null, Array.slice(arguments)));
 }
+
+function delayed (value, ms) {
+  let { promise, resolve } = defer();
+  setTimeout(() => resolve(value), ms || 10);
+  return promise;
+}
+
 require('test').run(exports);
--- a/addon-sdk/source/test/test-places-favicon.js
+++ b/addon-sdk/source/test/test-places-favicon.js
@@ -14,17 +14,17 @@ const { Cc, Ci, Cu } = require('chrome')
 const { getFavicon } = require('sdk/places/favicon');
 const tabs = require('sdk/tabs');
 const open = tabs.open;
 const port = 8099;
 const host = 'http://localhost:' + port;
 const { onFaviconChange, serve, binFavicon } = require('./favicon-helpers');
 const { once } = require('sdk/system/events');
 const { defer } = require('sdk/core/promise');
-const { clearHistory } = require('./places-helper');
+const { resetPlaces } = require('./places-helper');
 const faviconService = Cc["@mozilla.org/browser/favicon-service;1"].
                          getService(Ci.nsIFaviconService);
 
 exports.testStringGetFaviconCallbackSuccess = function (assert, done) {
   let name = 'callbacksuccess'
   let srv = makeServer(name);
   let url = host + '/' + name + '.html';
   let favicon = host + '/' + name + '.ico';
@@ -176,15 +176,15 @@ function waitAndExpire (url) {
     });
     faviconService.expireAllFavicons();
   });
   return deferred.promise;
 }
 
 function complete(tab, srv, done) {
   tab.close(function () {
-    clearHistory(() => {
+    resetPlaces(() => {
       srv.stop(done);
     });
   });
 }
 
 require("test").run(exports);
--- a/addon-sdk/source/test/test-places-history.js
+++ b/addon-sdk/source/test/test-places-history.js
@@ -9,26 +9,25 @@ module.metadata = {
   }
 };
 
 const { Cc, Ci } = require('chrome');
 const { defer, all } = require('sdk/core/promise');
 const { has } = require('sdk/util/array');
 const { setTimeout } = require('sdk/timers');
 const { before, after } = require('sdk/test/utils');
+const { set } = require('sdk/preferences/service');
 const {
   search 
 } = require('sdk/places/history');
 const {
-  invalidResolve, invalidReject, clearBookmarks, createTree,
-  compareWithHost, clearAllBookmarks, addVisits, clearHistory
+  invalidResolve, invalidReject, createTree,
+  compareWithHost, addVisits, resetPlaces
 } = require('./places-helper');
 const { promisedEmitter } = require('sdk/places/utils');
-const hsrv = Cc['@mozilla.org/browser/nav-history-service;1'].
-              getService(Ci.nsINavHistoryService);
 
 exports.testEmptyQuery = function (assert, done) {
   let within = toBeWithin();
   addVisits([
     'http://simplequery-1.com', 'http://simplequery-2.com'
   ]).then(searchP).then(results => {
     assert.equal(results.length, 2, 'Correct number of entries returned');
     assert.equal(results[0].url, 'http://simplequery-1.com/',
@@ -234,21 +233,16 @@ exports.testEmitters = function (assert,
 function toBeWithin (range) {
   range = range || 2000;
   var current = new Date() * 1000; // convert to microseconds
   return compared => { 
     return compared - current < range;
   };
 }
 
-function clear (done) {
-  clearAllBookmarks();
-  clearHistory(done);
-}
-
 function searchP () {
   return promisedEmitter(search.apply(null, Array.slice(arguments)));
 }
 
-before(exports, (name, assert, done) => clear(done));
-after(exports, (name, assert, done) => clear(done));
+before(exports, (name, assert, done) => resetPlaces(done));
+after(exports, (name, assert, done) => resetPlaces(done));
 
 require('test').run(exports);
--- a/addon-sdk/source/test/test-places-host.js
+++ b/addon-sdk/source/test/test-places-host.js
@@ -9,32 +9,33 @@ module.metadata = {
   }
 };
 
 const { Cc, Ci } = require('chrome');
 const { defer, all } = require('sdk/core/promise');
 const { setTimeout } = require('sdk/timers');
 const { newURI } = require('sdk/url/utils');
 const { send } = require('sdk/addon/events');
+const { set } = require('sdk/preferences/service');
+const { before, after } = require('sdk/test/utils');
 
 require('sdk/places/host/host-bookmarks');
 require('sdk/places/host/host-tags');
 require('sdk/places/host/host-query');
 const {
-  invalidResolve, invalidReject, clearBookmarks, createTree,
-  compareWithHost, clearAllBookmarks, createBookmark, createBookmarkTree
+  invalidResolve, invalidReject, createTree,
+  compareWithHost, createBookmark, createBookmarkTree, resetPlaces
 } = require('./places-helper');
 
 const bmsrv = Cc['@mozilla.org/browser/nav-bookmarks-service;1'].
                     getService(Ci.nsINavBookmarksService);
 const hsrv = Cc['@mozilla.org/browser/nav-history-service;1'].
               getService(Ci.nsINavHistoryService);
 const tagsrv = Cc['@mozilla.org/browser/tagging-service;1'].
               getService(Ci.nsITaggingService);
-clearAllBookmarks();
 
 exports.testBookmarksCreate = function (assert, done) {
   let items = [{
     title: 'my title',
     url: 'http://moz.com',
     tags: ['some', 'tags', 'yeah'],
     type: 'bookmark'
   }, {
@@ -46,17 +47,16 @@ exports.testBookmarksCreate = function (
     group: bmsrv.unfiledBookmarksFolder
   }];
   
   all(items.map(function (item) {
     return send('sdk-places-bookmarks-create', item).then(function (data) {
       compareWithHost(assert, data);
     }, invalidReject(assert));
   })).then(function () {
-    clearAllBookmarks();
     done();
   }, invalidReject(assert));
 };
 
 exports.testBookmarksCreateFail = function (assert, done) {
   let items = [{
     title: 'my title',
     url: 'not-a-url',
@@ -67,17 +67,16 @@ exports.testBookmarksCreateFail = functi
   }, {
     group: bmsrv.unfiledBookmarksFolder
   }];
   all(items.map(function (item) {
     return send('sdk-places-bookmarks-create', item).then(null, function (reason) {
       assert.ok(reason, 'bookmark create should fail');
     });
   })).then(function () {
-    clearAllBookmarks();
     done();
   });
 };
 
 exports.testBookmarkLastUpdated = function (assert, done) {
   let timestamp;
   let item;
   createBookmark().then(function (data) {
@@ -89,33 +88,31 @@ exports.testBookmarkLastUpdated = functi
     item.title = 'updated mozilla';
     return send('sdk-places-bookmarks-save', item).then(function (data) {
       let deferred = defer();
       setTimeout(function () deferred.resolve(data), 100);
       return deferred.promise;
     });
   }).then(function (data) {
     assert.ok(data.updated > timestamp, 'time has elapsed and updated the updated property');
-    clearAllBookmarks();
     done();
   });
 };
 
 exports.testBookmarkRemove = function (assert, done) {
   let id;
   createBookmark().then(function (data) {
     id = data.id;
     compareWithHost(assert, data); // ensure bookmark exists
     bmsrv.getItemTitle(id); // does not throw an error
     return send('sdk-places-bookmarks-remove', data);
   }).then(function () {
     assert.throws(function () {
       bmsrv.getItemTitle(id);
     }, 'item should no longer exist');
-    clearAllBookmarks();
     done();
   }, console.error);
 };
 
 exports.testBookmarkGet = function (assert, done) {
   let bookmark;
   createBookmark().then(function (data) {
     bookmark = data;
@@ -128,17 +125,16 @@ exports.testBookmarkGet = function (asse
             'correctly fetched tag ' + tag);
         }
         assert.equal(bookmark.tags.length, data.tags.length,
           'same amount of tags');
       }
       else
         assert.equal(bookmark[prop], data[prop], 'correctly fetched ' + prop);
     });
-    clearAllBookmarks();
     done();
   });
 };
 
 exports.testTagsTag = function (assert, done) {
   let url;
   createBookmark().then(function (data) {
     url = data.url;
@@ -146,17 +142,16 @@ exports.testTagsTag = function (assert, 
       url: data.url, tags: ['mozzerella', 'foxfire']
     });
   }).then(function () {
     let tags = tagsrv.getTagsForURI(newURI(url));
     assert.ok(~tags.indexOf('mozzerella'), 'first tag found');
     assert.ok(~tags.indexOf('foxfire'), 'second tag found');
     assert.ok(~tags.indexOf('firefox'), 'default tag found');
     assert.equal(tags.length, 3, 'no extra tags');
-    clearAllBookmarks();
     done();
   });
 };
 
 exports.testTagsUntag = function (assert, done) {
   let item;
   createBookmark({tags: ['tag1', 'tag2', 'tag3']}).then(function (data) {
     item = data;
@@ -166,49 +161,46 @@ exports.testTagsUntag = function (assert
     });
   }).then(function () {
     let tags = tagsrv.getTagsForURI(newURI(item.url));
     assert.ok(~tags.indexOf('tag1'), 'first tag persisted');
     assert.ok(~tags.indexOf('tag3'), 'second tag persisted');
     assert.ok(!~tags.indexOf('firefox'), 'first tag removed');
     assert.ok(!~tags.indexOf('tag2'), 'second tag removed');
     assert.equal(tags.length, 2, 'no extra tags');
-    clearAllBookmarks();
     done();
   });
 };
 
 exports.testTagsGetURLsByTag = function (assert, done) {
   let item;
   createBookmark().then(function (data) {
     item = data;
     return send('sdk-places-tags-get-urls-by-tag', {
       tag: 'firefox'
     });
   }).then(function(urls) {
     assert.equal(item.url, urls[0], 'returned correct url');
     assert.equal(urls.length, 1, 'returned only one url');
-    clearAllBookmarks();
     done();
   });
 };
 
 exports.testTagsGetTagsByURL = function (assert, done) {
   let item;
   createBookmark({ tags: ['firefox', 'mozilla', 'metal']}).then(function (data) {
     item = data;
     return send('sdk-places-tags-get-tags-by-url', {
       url: data.url,
     });
   }).then(function(tags) {
     assert.ok(~tags.indexOf('firefox'), 'returned first tag');
     assert.ok(~tags.indexOf('mozilla'), 'returned second tag');
     assert.ok(~tags.indexOf('metal'), 'returned third tag');
     assert.equal(tags.length, 3, 'returned all tags');
-    clearAllBookmarks();
     done();
   });
 };
 
 exports.testHostQuery = function (assert, done) {
   all([
     createBookmark({ url: 'http://firefox.com', tags: ['firefox', 'mozilla'] }),
     createBookmark({ url: 'http://mozilla.com', tags: ['mozilla'] }),
@@ -223,17 +215,16 @@ exports.testHostQuery = function (assert
     assert.equal(results[0].url, 'http://mozilla.com/', 'is sorted by URI asc');
     return send('sdk-places-query', {
       queries: { tags: ['mozilla'] }, 
       options: { sortingMode: 5, queryType: 1 } // sort by URI descending, bookmarks only
     });
   }).then(results => {
     assert.equal(results.length, 2, 'should only return two');
     assert.equal(results[0].url, 'http://firefox.com/', 'is sorted by URI desc');
-    clearAllBookmarks();
     done();
   });
 };
 
 exports.testHostMultiQuery = function (assert, done) {
   all([
     createBookmark({ url: 'http://firefox.com', tags: ['firefox', 'mozilla'] }),
     createBookmark({ url: 'http://mozilla.com', tags: ['mozilla'] }),
@@ -248,37 +239,38 @@ exports.testHostMultiQuery = function (a
     assert.equal(results[0].url, 'http://firefox.com/', 'should match URL or tag');
     assert.equal(results[1].url, 'http://thunderbird.com/', 'should match URL or tag');
     return send('sdk-places-query', {
       queries: [{ tags: ['firefox'], url: 'http://mozilla.com/' }],
       options: { sortingMode: 5, queryType: 1 } // sort by URI descending, bookmarks only
     });
   }).then(results => {
     assert.equal(results.length, 0, 'query props should be AND\'d');
-    clearAllBookmarks();
     done();
   });
 };
 
 exports.testGetAllBookmarks = function (assert, done) {
   createBookmarkTree().then(() => { 
     return send('sdk-places-bookmarks-get-all', {});
   }).then(res => {
     assert.equal(res.length, 8, 'all bookmarks returned');
-    clearAllBookmarks();
     done();
   }, console.error);
 };
 
 exports.testGetAllChildren = function (assert, done) {
   createBookmarkTree().then(results => {
     return send('sdk-places-bookmarks-get-children', {
       id: results.filter(({title}) => title === 'mozgroup')[0].id
     });
   }).then(results => {
     assert.equal(results.length, 5,
       'should return all children and folders at a single depth');
-    clearAllBookmarks();
     done();
   });
 };
 
+
+before(exports, (name, assert, done) => resetPlaces(done));
+after(exports, (name, assert, done) => resetPlaces(done));
+
 require('test').run(exports);
--- a/addon-sdk/source/test/test-plain-text-console.js
+++ b/addon-sdk/source/test/test-plain-text-console.js
@@ -31,121 +31,134 @@ exports.testPlainTextConsole = function(
   prefs.reset(ADDON_LOG_LEVEL_PREF);
 
   var Console = require("sdk/console/plain-text").PlainTextConsole;
   var con = new Console(print);
 
   test.pass("PlainTextConsole instantiates");
 
   con.log('testing', 1, [2, 3, 4]);
-  test.assertEqual(lastPrint(), "info: " + name + ": testing 1 2,3,4\n",
+  test.assertEqual(lastPrint(), "console.log: " + name + ": testing, 1, Array [2,3,4]\n",
                    "PlainTextConsole.log() must work.");
 
   con.info('testing', 1, [2, 3, 4]);
-  test.assertEqual(lastPrint(), "info: " + name + ": testing 1 2,3,4\n",
+  test.assertEqual(lastPrint(), "console.info: " + name + ": testing, 1, Array [2,3,4]\n",
                    "PlainTextConsole.info() must work.");
 
   con.warn('testing', 1, [2, 3, 4]);
-  test.assertEqual(lastPrint(), "warn: " + name + ": testing 1 2,3,4\n",
+  test.assertEqual(lastPrint(), "console.warn: " + name + ": testing, 1, Array [2,3,4]\n",
                    "PlainTextConsole.warn() must work.");
 
   con.error('testing', 1, [2, 3, 4]);
-  test.assertEqual(lastPrint(), "error: " + name + ": testing 1 2,3,4\n",
+  test.assertEqual(prints[0], "console.error: " + name + ": \n",
                    "PlainTextConsole.error() must work.");
+  test.assertEqual(prints[1], "  testing\n")
+  test.assertEqual(prints[2], "  1\n")
+  test.assertEqual(prints[3], "Array\n    - 0 = 2\n    - 1 = 3\n    - 2 = 4\n    - length = 3\n");
+  prints = [];
 
   con.debug('testing', 1, [2, 3, 4]);
-  test.assertEqual(lastPrint(), "debug: " + name + ": testing 1 2,3,4\n",
+  test.assertEqual(prints[0], "console.debug: " + name + ": \n",
                    "PlainTextConsole.debug() must work.");
+  test.assertEqual(prints[1], "  testing\n")
+  test.assertEqual(prints[2], "  1\n")
+  test.assertEqual(prints[3], "Array\n    - 0 = 2\n    - 1 = 3\n    - 2 = 4\n    - length = 3\n");
+  prints = [];
 
   con.log('testing', undefined);
-  test.assertEqual(lastPrint(), "info: " + name + ": testing undefined\n",
+  test.assertEqual(lastPrint(), "console.log: " + name + ": testing, undefined\n",
                    "PlainTextConsole.log() must stringify undefined.");
 
   con.log('testing', null);
-  test.assertEqual(lastPrint(), "info: " + name + ": testing null\n",
+  test.assertEqual(lastPrint(), "console.log: " + name + ": testing, null\n",
                    "PlainTextConsole.log() must stringify null.");
 
+  // TODO: Fix console.jsm to detect custom toString.
   con.log("testing", { toString: function() "obj.toString()" });
-  test.assertEqual(lastPrint(), "info: " + name + ": testing obj.toString()\n",
-                   "PlainTextConsole.log() must stringify custom toString.");
+  test.assertEqual(lastPrint(), "console.log: " + name + ": testing, {}\n",
+                   "PlainTextConsole.log() doesn't printify custom toString.");
 
   con.log("testing", { toString: function() { throw "fail!"; } });
-  test.assertEqual(lastPrint(), "info: " + name + ": testing <toString() error>\n",
+  test.assertEqual(lastPrint(), "console.log: " + name + ": testing, {}\n",
                    "PlainTextConsole.log() must stringify custom bad toString.");
 
+  
   con.exception(new Error("blah"));
 
-  var tbLines = prints[0].split("\n");
-  test.assertEqual(tbLines[0], "error: " + name + ": An exception occurred.");
-  test.assertEqual(tbLines[1], "Error: blah");
-  test.assertEqual(tbLines[2], module.uri + " 74");
-  test.assertEqual(tbLines[3], "Traceback (most recent call last):");
+  
+  test.assertEqual(prints[0], "console.error: " + name + ": \n");
+  let tbLines = prints[1].split("\n");
+  test.assertEqual(tbLines[0], "  Message: Error: blah");
+  test.assertEqual(tbLines[1], "  Stack:");
+  test.assert(prints[1].indexOf(module.uri + ":84") !== -1);
+  prints = []
 
-  prints = [];
   try {
     loadSubScript("invalid-url", {});
     test.fail("successed in calling loadSubScript with invalid-url");
   }
   catch(e) {
     con.exception(e);
   }
-  var tbLines = prints[0].split("\n");
-  test.assertEqual(tbLines[0], "error: " + name + ": An exception occurred.");
-  test.assertEqual(tbLines[1], "Error creating URI (invalid URL scheme?)");
-  test.assertEqual(tbLines[2], "Traceback (most recent call last):");
+  test.assertEqual(prints[0], "console.error: " + name + ": \n");
+  test.assertEqual(prints[1], "  Error creating URI (invalid URL scheme?)\n");
+  prints = [];
 
+  con.trace();
+  let tbLines = prints[0].split("\n");
+  test.assertEqual(tbLines[0], "console.trace: " + name + ": ");
+  test.assert(tbLines[1].indexOf("_ain-text-console.js 105") == 0);
   prints = [];
-  con.trace();
-  tbLines = prints[0].split("\n");
-  test.assertEqual(tbLines[0], "info: " + name + ": Traceback (most recent call last):");
-  test.assertEqual(tbLines[tbLines.length - 4].trim(), "con.trace();");
 
   // Whether or not console methods should print at the various log levels,
   // structured as a hash of levels, each of which contains a hash of methods,
   // each of whose value is whether or not it should print, i.e.:
   // { [level]: { [method]: [prints?], ... }, ... }.
   let levels = {
     all:   { debug: true,  log: true,  info: true,  warn: true,  error: true  },
     debug: { debug: true,  log: true,  info: true,  warn: true,  error: true  },
     info:  { debug: false, log: true,  info: true,  warn: true,  error: true  },
     warn:  { debug: false, log: false, info: false, warn: true,  error: true  },
     error: { debug: false, log: false, info: false, warn: false, error: true  },
     off:   { debug: false, log: false, info: false, warn: false, error: false },
   };
 
   // The messages we use to test the various methods, as a hash of methods.
   let messages = {
-    debug: "debug: " + name + ": \n",
-    log: "info: " + name + ": \n",
-    info: "info: " + name + ": \n",
-    warn: "warn: " + name + ": \n",
-    error: "error: " + name + ": \n",
+    debug: "console.debug: " + name + ": \n  \n",
+    log: "console.log: " + name + ": \n",
+    info: "console.info: " + name + ": \n",
+    warn: "console.warn: " + name + ": \n",
+    error: "console.error: " + name + ": \n  \n",
   };
 
   for (let level in levels) {
     let methods = levels[level];
     for (let method in methods) {
       // We have to reset the log level pref each time we run the test
       // because the test runner relies on the console to print test output,
       // and test results would not get printed to the console for some
       // values of the pref.
       prefs.set(SDK_LOG_LEVEL_PREF, level);
       con[method]("");
       prefs.set(SDK_LOG_LEVEL_PREF, "all");
-      test.assertEqual(lastPrint(), (methods[method] ? messages[method] : null),
+      test.assertEqual(prints.join(""), 
+                       (methods[method] ? messages[method] : ""),
                        "at log level '" + level + "', " + method + "() " +
                        (methods[method] ? "prints" : "doesn't print"));
+      prints = [];
     }
   }
 
   prefs.set(SDK_LOG_LEVEL_PREF, "off");
   prefs.set(ADDON_LOG_LEVEL_PREF, "all");
   con.debug("");
-  test.assertEqual(lastPrint(), messages["debug"],
+  test.assertEqual(prints.join(""), messages["debug"],
                    "addon log level 'all' overrides SDK log level 'off'");
+  prints = [];
 
   prefs.set(SDK_LOG_LEVEL_PREF, "all");
   prefs.set(ADDON_LOG_LEVEL_PREF, "off");
   con.error("");
   prefs.reset(ADDON_LOG_LEVEL_PREF);
   test.assertEqual(lastPrint(), null,
                    "addon log level 'off' overrides SDK log level 'all'");
 
--- a/addon-sdk/source/test/test-system-events.js
+++ b/addon-sdk/source/test/test-system-events.js
@@ -5,16 +5,18 @@
 const events = require("sdk/system/events");
 const self = require("sdk/self");
 const { Cc, Ci, Cu } = require("chrome");
 const { setTimeout } = require("sdk/timers");
 const { Loader, LoaderWithHookedConsole2 } = require("sdk/test/loader");
 const nsIObserverService = Cc["@mozilla.org/observer-service;1"].
                            getService(Ci.nsIObserverService);
 
+let isConsoleEvent = (topic) =>
+  !!~["console-api-log-event", "console-storage-cache-event"].indexOf(topic)
 
 exports["test basic"] = function(assert) {
   let type = Date.now().toString(32);
 
   let timesCalled = 0;
   function handler(subject, data) { timesCalled++; };
 
   events.on(type, handler);
@@ -43,20 +45,20 @@ exports["test error reporting"] = functi
   let lineNumber;
   try { brokenHandler() } catch (error) { lineNumber = error.lineNumber }
 
   let errorType = Date.now().toString(32);
 
   events.on(errorType, brokenHandler);
   events.emit(errorType, { data: "yo yo" });
 
-  assert.equal(messages.length, 1, "Got an exception");
-  let text = messages[0];
-  assert.ok(text.indexOf(self.name + ": An exception occurred.") >= 0,
-            "error is logged");
+  assert.equal(messages.length, 2, "Got an exception");
+  assert.equal(messages[0], "console.error: " + self.name + ": \n",
+               "error is logged");
+  let text = messages[1];
   assert.ok(text.indexOf("Error: foo") >= 0, "error message is logged");
   assert.ok(text.indexOf(module.uri) >= 0, "module uri is logged");
   assert.ok(text.indexOf(lineNumber) >= 0, "error line is logged");
 
   events.off(errorType, brokenHandler);
 
   loader.unload();
 };
@@ -99,16 +101,19 @@ exports["test handle nsIObserverService 
 
   let type = Date.now().toString(32);
   let timesCalled = 0;
   let lastSubject = null;
   let lastData = null;
   let lastType = null;
 
   function handler({ subject, data, type }) {
+    // Ignores internal console events
+    if (isConsoleEvent(type))
+      return;
     timesCalled++;
     lastSubject = subject;
     lastData = data;
     lastType = type;
   };
 
   events.on(type, handler);
   nsIObserverService.notifyObservers(uri, type, "some data");
@@ -163,33 +168,35 @@ exports["test emit to nsIObserverService
   let lastTopic = null;
 
   var topic = Date.now().toString(32)
   let nsIObserver = {
     QueryInterface: function() {
       return nsIObserver;
     },
     observe: function(subject, topic, data) {
+      // Ignores internal console events
+      if (isConsoleEvent(topic))
+        return;
       timesCalled = timesCalled + 1;
       lastSubject = subject;
       lastData = data;
       lastTopic = topic;
     }
   };
 
   nsIObserverService.addObserver(nsIObserver, topic, false);
 
   events.emit(topic, { subject: uri, data: "some data" });
 
   assert.equal(timesCalled, 1, "emit notifies observers");
   assert.equal(lastTopic, topic, "event type is notification topic");
   assert.equal(lastSubject.wrappedJSObject.object, uri,
                "event.subject is notification subject");
   assert.equal(lastData, "some data", "event.data is notification data");
-
   function customSubject() {}
   function customData() {}
   events.emit(topic, { subject: customSubject, data: customData });
 
   assert.equal(timesCalled, 2, "emit notifies observers");
   assert.equal(lastTopic, topic, "event.type is notification");
   assert.equal(lastSubject.wrappedJSObject.object, customSubject,
                "event.subject is notification subject");
@@ -201,20 +208,21 @@ exports["test emit to nsIObserverService
 
   assert.equal(timesCalled, 2, "removed observers no longer invoked");
 
   nsIObserverService.addObserver(nsIObserver, "*", false);
 
   events.emit(topic, { data: "data again" });
 
   assert.equal(timesCalled, 3, "emit notifies * observers");
+
   assert.equal(lastTopic, topic, "event.type is notification");
   assert.equal(lastSubject, null,
                "event.subject is notification subject");
   assert.equal(lastData, "data again", "event.data is notification data");
 
   nsIObserverService.removeObserver(nsIObserver, "*");
-
+  
   events.emit(topic, { data: "last data" });
   assert.equal(timesCalled, 3, "removed observers no longer invoked");
 }
 
 require("test").run(exports);
--- a/addon-sdk/source/test/test-tab-events.js
+++ b/addon-sdk/source/test/test-tab-events.js
@@ -5,171 +5,234 @@
 "use strict";
 
 const { Loader } = require("sdk/test/loader");
 const utils = require("sdk/tabs/utils");
 const { open, close } = require("sdk/window/helpers");
 const { getMostRecentBrowserWindow } = require("sdk/window/utils");
 const { events } = require("sdk/tab/events");
 const { on, off } = require("sdk/event/core");
-const { resolve } = require("sdk/core/promise");
+const { resolve, defer } = require("sdk/core/promise");
 
 let isFennec = require("sdk/system/xul-app").is("Fennec");
 
-function test(scenario, currentWindow) {
-  let useActiveWindow = isFennec || currentWindow;
+function test(options) {
   return function(assert, done) {
-    let actual = [];
-    function handler(event) actual.push(event)
+    let tabEvents = [];
+    let tabs = [];
+    let { promise, resolve: resolveP } = defer();
+    let win = isFennec ? resolve(getMostRecentBrowserWindow()) :
+      open(null, {
+        features: { private: true, toolbar:true, chrome: true }
+      });
+    let window = null;
 
-    let win = useActiveWindow ? resolve(getMostRecentBrowserWindow()) :
-              open(null, {
-                features: { private: true, toolbar:true, chrome: true }
-              });
-    let window = null;
+    // Firefox events are fired sync; Fennec events async
+    // this normalizes the tests
+    function handler (event) {
+      tabEvents.push(event);
+      runIfReady();
+    }
+
+    function runIfReady () {
+      let releventEvents = getRelatedEvents(tabEvents, tabs);
+      if (options.readyWhen(releventEvents))
+        options.end({
+          tabs: tabs,
+          events: releventEvents,
+          assert: assert,
+          done: resolveP
+        });
+    }
 
     win.then(function(w) {
       window = w;
       on(events, "data", handler);
-      return scenario(assert, window, actual);
+      options.start({ tabs: tabs, window: window });
+
+      // Execute here for synchronous FF events, as the handlers
+      // were called before tabs were pushed to `tabs`
+      runIfReady(); 
+      return promise;
     }).then(function() {
       off(events, "data", handler);
-      return useActiveWindow ? null : close(window);
+      return isFennec ? null : close(window);
     }).then(done, assert.fail);
-  }
+  };
 }
 
-exports["test current window"] = test(function(assert, window, events) {
-  // Just making sure that tab events work for already opened tabs not only
-  // for new windows.
-  let tab = utils.openTab(window, 'data:text/plain,open');
-  utils.closeTab(tab);
-
-  let [open, select, close] = events;
-
-  assert.equal(open.type, "TabOpen");
-  assert.equal(open.target, tab);
-
-  assert.equal(select.type, "TabSelect");
-  assert.equal(select.target, tab);
+// Just making sure that tab events work for already opened tabs not only
+// for new windows.
+exports["test current window"] = test({
+  readyWhen: events => events.length === 3,
+  start: ({ tabs, window }) => {
+    let tab = utils.openTab(window, 'data:text/plain,open');
+    tabs.push(tab);
+    utils.closeTab(tab);
+  },
+  end: ({ tabs, events, assert, done }) => {
+    let [open, select, close] = events;
+    let tab = tabs[0];
 
-  assert.equal(close.type, "TabClose");
-  assert.equal(close.target, tab);
-});
+    assert.equal(open.type, "TabOpen");
+    assert.equal(open.target, tab);
 
-exports["test open"] = test(function(assert, window, events) {
-  let tab = utils.openTab(window, 'data:text/plain,open');
-  let [open, select] = events;
+    assert.equal(select.type, "TabSelect");
+    assert.equal(select.target, tab);
 
-  assert.equal(open.type, "TabOpen");
-  assert.equal(open.target, tab);
-
-  assert.equal(select.type, "TabSelect");
-  assert.equal(select.target, tab);
+    assert.equal(close.type, "TabClose");
+    assert.equal(close.target, tab);
+    done();
+  }
 });
 
-exports["test open -> close"] = test(function(assert, window, events) {
-  // First tab is useless we just open it so that closing second tab won't
-  // close window on some platforms.
-  let _ = utils.openTab(window, 'daat:text/plain,ignore');
-  let tab = utils.openTab(window, 'data:text/plain,open-close');
-  utils.closeTab(tab);
-
-  let [_open, _select, open, select, close] = events;
+exports["test open"] = test({
+  readyWhen: events => events.length === 2,
+  start: ({ tabs, window }) => {
+    tabs.push(utils.openTab(window, 'data:text/plain,open'));
+  },
+  end: ({ tabs, events, assert, done }) => {
+    let [open, select] = events;
+    let tab = tabs[0];
 
-  assert.equal(open.type, "TabOpen");
-  assert.equal(open.target, tab);
+    assert.equal(open.type, "TabOpen");
+    assert.equal(open.target, tab);
 
-  assert.equal(select.type, "TabSelect");
-  assert.equal(select.target, tab);
-
-  assert.equal(close.type, "TabClose");
-  assert.equal(close.target, tab);
+    assert.equal(select.type, "TabSelect");
+    assert.equal(select.target, tab);
+    done();
+  }
 });
 
-exports["test open -> open -> select"] = test(function(assert, window, events) {
-  let tab1 = utils.openTab(window, 'data:text/plain,Tab-1');
-  let tab2 = utils.openTab(window, 'data:text/plain,Tab-2');
-  utils.activateTab(tab1, window);
-
-  let [open1, select1, open2, select2, select3] = events;
-
-  // Open first tab
-  assert.equal(open1.type, "TabOpen", "first tab opened")
-  assert.equal(open1.target, tab1, "event.target is first tab")
-
-  assert.equal(select1.type, "TabSelect", "first tab seleceted")
-  assert.equal(select1.target, tab1, "event.target is first tab")
-
-
-  // Open second tab
-  assert.equal(open2.type, "TabOpen", "second tab opened");
-  assert.equal(open2.target, tab2, "event.target is second tab");
-
-  assert.equal(select2.type, "TabSelect", "second tab seleceted");
-  assert.equal(select2.target, tab2, "event.target is second tab");
+exports["test open -> close"] = test({
+  readyWhen: events => events.length === 3,
+  start: ({ tabs, window }) => {
+    // First tab is useless we just open it so that closing second tab won't
+    // close window on some platforms.
+    utils.openTab(window, 'data:text/plain,ignore');
+    let tab = utils.openTab(window, 'data:text/plain,open-close');
+    tabs.push(tab);
+    utils.closeTab(tab);
+  },
+  end: ({ tabs, events, assert, done }) => {
+    let [open, select, close] = events;
+    let tab = tabs[0];
 
-  // Select first tab
-  assert.equal(select3.type, "TabSelect", "tab seleceted");
-  assert.equal(select3.target, tab1, "event.target is first tab");
-});
-
-exports["test open -> pin -> unpin"] = test(function(assert, window, events) {
-  let tab = utils.openTab(window, 'data:text/plain,pin-unpin');
-  utils.pin(tab);
-  utils.unpin(tab);
-
-  let [open, select, move, pin, unpin] = events;
-
-  assert.equal(open.type, "TabOpen");
-  assert.equal(open.target, tab);
+    assert.equal(open.type, "TabOpen");
+    assert.equal(open.target, tab);
 
-  assert.equal(select.type, "TabSelect");
-  assert.equal(select.target, tab);
+    assert.equal(select.type, "TabSelect");
+    assert.equal(select.target, tab);
 
-  if (isFennec) {
-    assert.pass("Tab pin / unpin is not supported by Fennec");
-  }
-  else {
-    assert.equal(move.type, "TabMove");
-    assert.equal(move.target, tab);
-
-    assert.equal(pin.type, "TabPinned");
-    assert.equal(pin.target, tab);
-
-    assert.equal(unpin.type, "TabUnpinned");
-    assert.equal(unpin.target, tab);
+    assert.equal(close.type, "TabClose");
+    assert.equal(close.target, tab);
+    done();
   }
 });
 
-exports["test open -> open -> move "] = test(function(assert, window, events) {
-  let tab1 = utils.openTab(window, 'data:text/plain,Tab-1');
-  let tab2 = utils.openTab(window, 'data:text/plain,Tab-2');
-  utils.move(tab1, 2);
-
-  let [open1, select1, open2, select2, move] = events;
-
-  // Open first tab
-  assert.equal(open1.type, "TabOpen", "first tab opened");
-  assert.equal(open1.target, tab1, "event.target is first tab");
-
-  assert.equal(select1.type, "TabSelect", "first tab seleceted")
-  assert.equal(select1.target, tab1, "event.target is first tab");
-
+exports["test open -> open -> select"] = test({
+  readyWhen: events => events.length === 5,
+  start: ({tabs, window}) => {
+    tabs.push(utils.openTab(window, 'data:text/plain,Tab-1'));
+    tabs.push(utils.openTab(window, 'data:text/plain,Tab-2'));
+    utils.activateTab(tabs[0], window);
+  },
+  end: ({ tabs, events, assert, done }) => {
+    let [ tab1, tab2 ] = tabs;
+    let tab1Events = 0;
+    getRelatedEvents(events, tab1).map(event => {
+      tab1Events++;
+      if (tab1Events === 1)
+        assert.equal(event.type, "TabOpen", "first tab opened");
+      else
+        assert.equal(event.type, "TabSelect", "first tab selected");
+      assert.equal(event.target, tab1);
+    });
+    assert.equal(tab1Events, 3, "first tab has 3 events");
 
-  // Open second tab
-  assert.equal(open2.type, "TabOpen", "second tab opened");
-  assert.equal(open2.target, tab2, "event.target is second tab");
-
-  assert.equal(select2.type, "TabSelect", "second tab seleceted");
-  assert.equal(select2.target, tab2, "event.target is second tab");
-
-  if (isFennec) {
-    assert.pass("Tab index changes not supported on Fennec yet")
-  }
-  else {
-    // Move first tab
-    assert.equal(move.type, "TabMove", "tab moved");
-    assert.equal(move.target, tab1, "event.target is first tab");
+    let tab2Opened;
+    getRelatedEvents(events, tab2).map(event => {
+      if (!tab2Opened)
+        assert.equal(event.type, "TabOpen", "second tab opened");
+      else
+        assert.equal(event.type, "TabSelect", "second tab selected");
+      tab2Opened = true;
+      assert.equal(event.target, tab2);
+    });
+    done();
   }
 });
 
-require("test").run(exports);
+exports["test open -> pin -> unpin"] = test({
+  readyWhen: events => events.length === (isFennec ? 2 : 5),
+  start: ({ tabs, window }) => {
+    tabs.push(utils.openTab(window, 'data:text/plain,pin-unpin'));
+    utils.pin(tabs[0]);
+    utils.unpin(tabs[0]);
+  },
+  end: ({ tabs, events, assert, done }) => {
+    let [open, select, move, pin, unpin] = events;
+    let tab = tabs[0];
+
+    assert.equal(open.type, "TabOpen");
+    assert.equal(open.target, tab);
+
+    assert.equal(select.type, "TabSelect");
+    assert.equal(select.target, tab);
+
+    if (isFennec) {
+      assert.pass("Tab pin / unpin is not supported by Fennec");
+    }
+    else {
+      assert.equal(move.type, "TabMove");
+      assert.equal(move.target, tab);
+
+      assert.equal(pin.type, "TabPinned");
+      assert.equal(pin.target, tab);
+
+      assert.equal(unpin.type, "TabUnpinned");
+      assert.equal(unpin.target, tab);
+    }
+    done();
+  }
+});
+
+exports["test open -> open -> move "] = test({
+  readyWhen: events => events.length === (isFennec ? 4 : 5),
+  start: ({tabs, window}) => {
+    tabs.push(utils.openTab(window, 'data:text/plain,Tab-1'));
+    tabs.push(utils.openTab(window, 'data:text/plain,Tab-2'));
+    utils.move(tabs[0], 2);
+  },
+  end: ({ tabs, events, assert, done }) => {
+    let [ tab1, tab2 ] = tabs;
+    let tab1Events = 0;
+    getRelatedEvents(events, tab1).map(event => {
+      tab1Events++;
+      if (tab1Events === 1)
+        assert.equal(event.type, "TabOpen", "first tab opened");
+      else if (tab1Events === 2)
+        assert.equal(event.type, "TabSelect", "first tab selected");
+      else if (tab1Events === 3 && isFennec)
+        assert.equal(event.type, "TabMove", "first tab moved");
+      assert.equal(event.target, tab1);
+    });
+    assert.equal(tab1Events, isFennec ? 2 : 3,
+      "correct number of events for first tab");
+
+    let tab2Events = 0;
+    getRelatedEvents(events, tab2).map(event => {
+      tab2Events++;
+      if (tab2Events === 1)
+        assert.equal(event.type, "TabOpen", "second tab opened");
+      else
+        assert.equal(event.type, "TabSelect", "second tab selected");
+      assert.equal(event.target, tab2);
+    });
+    done();
+  }
+});
+
+function getRelatedEvents (events, tabs) {
+  return events.filter(({target}) => ~([].concat(tabs)).indexOf(target));
+}
+
+require("sdk/test").run(exports);
--- a/addon-sdk/source/test/test-tab-observer.js
+++ b/addon-sdk/source/test/test-tab-observer.js
@@ -1,14 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+// TODO Fennec support in Bug #894525
+module.metadata = {
+  "engines": {
+    "Firefox": "*"
+  }
+}
+
 const { openTab, closeTab } = require("sdk/tabs/utils");
 const { Loader } = require("sdk/test/loader");
 const { setTimeout } = require("sdk/timers");
 
 exports["test unload tab observer"] = function(assert, done) {
   let loader = Loader(module);
 
   let window = loader.require("sdk/deprecated/window-utils").activeBrowserWindow;
@@ -31,20 +38,10 @@ exports["test unload tab observer"] = fu
   // Enqueuing asserts to make sure that assertion is not performed early.
   setTimeout(function () {
     assert.equal(1, opened, "observer open was called before unload only");
     assert.equal(1, closed, "observer close was called before unload only");
     done();
   }, 0);
 };
 
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented."
-        );
-    }
-  }
-}
-
 require("test").run(exports);
 
--- a/addon-sdk/source/test/test-tab-utils.js
+++ b/addon-sdk/source/test/test-tab-utils.js
@@ -1,57 +1,50 @@
 'use strict';
 
 const { getTabs } = require('sdk/tabs/utils');
 const { isGlobalPBSupported, isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
 const { browserWindows } = require('sdk/windows');
 const tabs = require('sdk/tabs');
 const { pb } = require('./private-browsing/helper');
 const { isPrivate } = require('sdk/private-browsing');
-const { openTab, closeTab, getTabContentWindow } = require('sdk/tabs/utils');
+const { openTab, closeTab, getTabContentWindow, getOwnerWindow } = require('sdk/tabs/utils');
 const { open, close } = require('sdk/window/helpers');
 const { windows } = require('sdk/window/utils');
 const { getMostRecentBrowserWindow } = require('sdk/window/utils');
 const { fromIterator } = require('sdk/util/array');
 
-if (isGlobalPBSupported) {
+if (isWindowPBSupported) {
   exports.testGetTabs = function(assert, done) {
-    pb.once('start', function() {
-      tabs.open({
-        url: 'about:blank',
-        inNewWindow: true,
-        onOpen: function(tab) {
-          assert.equal(getTabs().length, 2, 'there are two tabs');
-          assert.equal(browserWindows.length, 2, 'there are two windows');
-          pb.once('stop', function() {
-            done();
-          });
-          pb.deactivate();
-        }
-      });
-    });
-    pb.activate();
-  };
-}
-else if (isWindowPBSupported) {
-  exports.testGetTabs = function(assert, done) {
+    let tabCount = getTabs().length;
+    let windowCount = browserWindows.length;
+
     open(null, {
         features: {
         private: true,
         toolbar: true,
         chrome: true
       }
     }).then(function(window) {
       assert.ok(isPrivate(window), 'new tab is private');
-      assert.equal(getTabs().length, 1, 'there is one tab found');
-      assert.equal(browserWindows.length, 1, 'there is one window found');
+
+      assert.equal(getTabs().length, tabCount, 'there are no new tabs found');
+      getTabs().forEach(function(tab) {
+        assert.equal(isPrivate(tab), false, 'all found tabs are not private');
+        assert.equal(isPrivate(getOwnerWindow(tab)), false, 'all found tabs are not private');
+        assert.equal(isPrivate(getTabContentWindow(tab)), false, 'all found tabs are not private');
+      });
+
+      assert.equal(browserWindows.length, windowCount, 'there are no new windows found');
       fromIterator(browserWindows).forEach(function(window) {
-        assert.ok(!isPrivate(window), 'all found windows are not private');
+        assert.equal(isPrivate(window), false, 'all found windows are not private');
       });
+
       assert.equal(windows(null, {includePrivate: true}).length, 2, 'there are really two windows');
+
       close(window).then(done);
     });
   };
 }
 else if (isTabPBSupported) {
   exports.testGetTabs = function(assert, done) {
     let startTabCount = getTabs().length;
     let tab = openTab(getMostRecentBrowserWindow(), 'about:blank', {
@@ -66,12 +59,9 @@ else if (isTabPBSupported) {
                  'the last tab is the opened tab');
     assert.equal(browserWindows.length, 1, 'there is only one window');
     closeTab(tab);
 
     done();
   };
 }
 
-// Test disabled because of bug 855771
-module.exports = {};
-
 require('test').run(exports);
--- a/addon-sdk/source/test/test-tab.js
+++ b/addon-sdk/source/test/test-tab.js
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 const tabs = require("sdk/tabs"); // From addon-kit
 const windowUtils = require("sdk/deprecated/window-utils");
 const { getTabForWindow } = require('sdk/tabs/helpers');
+const app = require("sdk/system/xul-app");
 
 // The primary test tab
 var primaryTab;
 
 // We have an auxiliary tab to test background tabs.
 var auxTab;
 
 // The window for the outer iframe in the primary test page
@@ -117,29 +118,22 @@ exports["test behavior on close"] = func
       assert.equal(tab.url, "about:mozilla", "Tab has the expected url");
       // if another test ends before closing a tab then index != 1 here
       assert.ok(tab.index >= 1, "Tab has the expected index, a value greater than 0");
       tab.close(function () {
         assert.equal(tab.url, undefined,
                      "After being closed, tab attributes are undefined (url)");
         assert.equal(tab.index, undefined,
                      "After being closed, tab attributes are undefined (index)");
-        // Ensure that we can call destroy multiple times without throwing
-        tab.destroy();
-        tab.destroy();
+        if (app.is("Firefox")) {
+          // Ensure that we can call destroy multiple times without throwing;
+          // Fennec doesn't use this internal utility
+          tab.destroy();
+          tab.destroy();
+        }
 
         done();
       });
     }
   });
 };
 
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See Bug 809362");
-    }
-  }
-}
-
 require("test").run(exports);
--- a/addon-sdk/source/test/test-timer.js
+++ b/addon-sdk/source/test/test-timer.js
@@ -1,131 +1,177 @@
 /* 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/. */
 
 var timer = require("sdk/timers");
 const { Loader } = require("sdk/test/loader");
 
-exports.testSetTimeout = function(test) {
+exports.testSetTimeout = function(assert, end) {
   timer.setTimeout(function() {
-    test.pass("testSetTimeout passed");
-    test.done();
+    assert.pass("testSetTimeout passed");
+    end();
   }, 1);
-  test.waitUntilDone();
 };
 
-exports.testParamedSetTimeout = function(test) {
+exports.testParamedSetTimeout = function(assert, end) {
   let params = [1, 'foo', { bar: 'test' }, null, undefined];
   timer.setTimeout.apply(null, [function() {
-    test.assertEqual(arguments.length, params.length);
+    assert.equal(arguments.length, params.length);
     for (let i = 0, ii = params.length; i < ii; i++)
-      test.assertEqual(params[i], arguments[i]);
-    test.done();
+      assert.equal(params[i], arguments[i]);
+    end();
   }, 1].concat(params));
-  test.waitUntilDone();
 };
 
-exports.testClearTimeout = function(test) {
+exports.testClearTimeout = function(assert, end) {
   var myFunc = function myFunc() {
-    test.fail("myFunc() should not be called in testClearTimeout");
+    assert.fail("myFunc() should not be called in testClearTimeout");
   };
   var id = timer.setTimeout(myFunc, 1);
   timer.setTimeout(function() {
-    test.pass("testClearTimeout passed");
-    test.done();
+    assert.pass("testClearTimeout passed");
+    end();
   }, 2);
   timer.clearTimeout(id);
-  test.waitUntilDone();
 };
 
-exports.testParamedClearTimeout = function(test) {
+exports.testParamedClearTimeout = function(assert, end) {
   let params = [1, 'foo', { bar: 'test' }, null, undefined];
   var myFunc = function myFunc() {
-    test.fail("myFunc() should not be called in testClearTimeout");
+    assert.fail("myFunc() should not be called in testClearTimeout");
   };
   var id = timer.setTimeout(myFunc, 1);
   timer.setTimeout.apply(null, [function() {
-    test.assertEqual(arguments.length, params.length);
+    assert.equal(arguments.length, params.length);
     for (let i = 0, ii = params.length; i < ii; i++)
-      test.assertEqual(params[i], arguments[i]);
-    test.done();
+      assert.equal(params[i], arguments[i]);
+    end();
   }, 1].concat(params));
   timer.clearTimeout(id);
-  test.waitUntilDone();
 };
 
-exports.testSetInterval = function (test) {
+exports.testSetInterval = function (assert, end) {
   var count = 0;
   var id = timer.setInterval(function () {
     count++;
     if (count >= 5) {
       timer.clearInterval(id);
-      test.pass("testSetInterval passed");
-      test.done();
+      assert.pass("testSetInterval passed");
+      end();
     }
   }, 1);
-  test.waitUntilDone();
 };
 
-exports.testParamedSetInerval = function(test) {
+exports.testParamedSetInerval = function(assert, end) {
   let params = [1, 'foo', { bar: 'test' }, null, undefined];
   let count = 0;
   let id = timer.setInterval.apply(null, [function() {
     count ++;
     if (count < 5) {
-      test.assertEqual(arguments.length, params.length);
+      assert.equal(arguments.length, params.length);
       for (let i = 0, ii = params.length; i < ii; i++)
-        test.assertEqual(params[i], arguments[i]);
+        assert.equal(params[i], arguments[i]);
     } else {
       timer.clearInterval(id);
-      test.done();
+      end();
     }
   }, 1].concat(params));
-  test.waitUntilDone();
 };
 
-exports.testClearInterval = function (test) {
+exports.testClearInterval = function (assert, end) {
   timer.clearInterval(timer.setInterval(function () {
-    test.fail("setInterval callback should not be called");
+    assert.fail("setInterval callback should not be called");
   }, 1));
   var id = timer.setInterval(function () {
     timer.clearInterval(id);
-    test.pass("testClearInterval passed");
-    test.done();
+    assert.pass("testClearInterval passed");
+    end();
   }, 2);
-  test.waitUntilDone();
 };
 
-exports.testParamedClearInterval = function(test) {
+exports.testParamedClearInterval = function(assert, end) {
   timer.clearInterval(timer.setInterval(function () {
-    test.fail("setInterval callback should not be called");
+    assert.fail("setInterval callback should not be called");
   }, 1, timer, {}, null));
 
   let id = timer.setInterval(function() {
     timer.clearInterval(id);
-    test.assertEqual(3, arguments.length);
-    test.done();
+    assert.equal(3, arguments.length);
+    end();
   }, 2, undefined, 'test', {});
-  test.waitUntilDone();
 };
 
 
-exports.testUnload = function(test) {
+exports.testImmediate = function(assert, end) {
+  let actual = [];
+  let ticks = 0;
+  timer.setImmediate(function(...params) {
+    actual.push(params);
+    assert.equal(ticks, 1, "is a next tick");
+    assert.deepEqual(actual, [["start", "immediates"]]);
+  }, "start", "immediates");
+
+  timer.setImmediate(function(...params) {
+    actual.push(params);
+    assert.deepEqual(actual, [["start", "immediates"],
+                                  ["added"]]);
+    assert.equal(ticks, 1, "is a next tick");
+    timer.setImmediate(function(...params) {
+      actual.push(params);
+      assert.equal(ticks, 2, "is second tick");
+      assert.deepEqual(actual, [["start", "immediates"],
+                                    ["added"],
+                                    [],
+                                    ["last", "immediate", "handler"],
+                                    ["side-effect"]]);
+      end();
+    }, "side-effect");
+  }, "added");
+
+  timer.setImmediate(function(...params) {
+    actual.push(params);
+    assert.equal(ticks, 1, "is a next tick");
+    assert.deepEqual(actual, [["start", "immediates"],
+                              ["added"],
+                              []]);
+    timer.clearImmediate(removeID);
+  });
+
+  function removed() {
+    assert.fail("should be removed");
+  }
+  let removeID = timer.setImmediate(removed);
+
+  timer.setImmediate(function(...params) {
+    actual.push(params);
+    assert.equal(ticks, 1, "is a next tick");
+    assert.deepEqual(actual, [["start", "immediates"],
+                              ["added"],
+                              [],
+                              ["last", "immediate", "handler"]]);
+    ticks = ticks + 1;
+  }, "last", "immediate", "handler");
+
+
+  ticks = ticks + 1;
+};
+
+exports.testUnload = function(assert, end) {
   var loader = Loader(module);
   var sbtimer = loader.require("sdk/timers");
 
   var myFunc = function myFunc() {
-    test.fail("myFunc() should not be called in testUnload");
+    assert.fail("myFunc() should not be called in testUnload");
   };
 
   sbtimer.setTimeout(myFunc, 1);
   sbtimer.setTimeout(myFunc, 1, 'foo', 4, {}, undefined);
   sbtimer.setInterval(myFunc, 1);
   sbtimer.setInterval(myFunc, 1, {}, null, 'bar', undefined, 87);
   loader.unload();
   timer.setTimeout(function() {
-    test.pass("timer testUnload passed");
-    test.done();
+    assert.pass("timer testUnload passed");
+    end();
   }, 2);
-  test.waitUntilDone();
 };
 
+require("test").run(exports);
\ No newline at end of file
--- a/addon-sdk/source/test/test-window-events.js
+++ b/addon-sdk/source/test/test-window-events.js
@@ -1,13 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
 
-"use strict";
+// Opening new windows in Fennec causes issues
+module.metadata = {
+  engines: {
+    'Firefox': '*'
+  }
+};
 
 const { Loader } = require("sdk/test/loader");
 const { open, getMostRecentBrowserWindow, getOuterId } = require("sdk/window/utils");
 
 exports["test browser events"] = function(assert, done) {
   let loader = Loader(module);
   let { events } = loader.require("sdk/window/events");
   let { on, off } = loader.require("sdk/event/core");
@@ -38,19 +44,9 @@ exports["test browser events"] = functio
       done();
     }
   });
 
   // Open window and close it to trigger observers.
   let window = open();
 };
 
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See bug 793071");
-    }
-  }
-}
-
-require("test").run(exports);
+require("sdk/test").run(exports);
--- a/addon-sdk/source/test/test-window-loader.js
+++ b/addon-sdk/source/test/test-window-loader.js
@@ -1,13 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
+// Opening new windows in Fennec causes issues
+module.metadata = {
+  engines: {
+    'Firefox': '*'
+  }
+};
+
 const { WindowLoader } = require('sdk/windows/loader'),
       { Trait } = require('sdk/deprecated/traits');
 
 const Loader = Trait.compose(
   WindowLoader,
   {
     constructor: function Loader(options) {
       this._onLoad = options.onLoad;
@@ -112,19 +119,8 @@ exports['test create loader from opened 
         }
       });
     },
     onUnload: function(window) {
       onUnloadCalled = true;
     }
   });
 };
-
-if (require("sdk/system/xul-app").is("Fennec")) {
-
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (test) {
-        test.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See bug 809409");
-    }
-  }
-}
--- a/addon-sdk/source/test/test-window-observer.js
+++ b/addon-sdk/source/test/test-window-observer.js
@@ -1,13 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
+// Opening new windows in Fennec causes issues
+module.metadata = {
+  engines: {
+    'Firefox': '*'
+  }
+};
+
 const { Loader } = require("sdk/test/loader");
 const { open, close } = require("sdk/window/helpers");
 const { browserWindows: windows } = require("sdk/windows");
 const { isBrowser } = require('sdk/window/utils');
 
 exports["test unload window observer"] = function(assert, done) {
   // Hacky way to be able to create unloadable modules via makeSandboxedLoader.
   let loader = Loader(module);
@@ -37,19 +44,9 @@ exports["test unload window observer"] =
     then(function() {
       // Enqueuing asserts to make sure that assertion is not performed early.
       assert.equal(1, opened, "observer open was called before unload only");
       assert.equal(windowsOpen + 1, closed, "observer close was called before unload only");
     }).
     then(done, assert.fail);
 };
 
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See bug 793071");
-    }
-  }
-}
-
 require("test").run(exports);
--- a/addon-sdk/source/test/test-window-utils-private-browsing.js
+++ b/addon-sdk/source/test/test-window-utils-private-browsing.js
@@ -1,13 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
+// Fennec support tracked in bug #809412
+module.metadata = {
+  'engines': {
+    'Firefox': '*'
+  }
+};
+
 const windowUtils = require('sdk/deprecated/window-utils');
 const { Cc, Ci } = require('chrome');
 const { isWindowPBSupported } = require('sdk/private-browsing/utils');
 const { getFrames, getWindowTitle, onFocus, isWindowPrivate } = require('sdk/window/utils');
 const { open, close, focus } = require('sdk/window/helpers');
 const WM = Cc['@mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator);
 const { isPrivate } = require('sdk/private-browsing');
 const { fromIterator: toArray } = require('sdk/util/array');
@@ -209,19 +216,9 @@ exports.testWindowIteratorIgnoresPrivate
       assert.ok(toArray(windowUtils.windowIterator()).indexOf(window) > -1,
                 "window is in windowIterator()"); 
     }
 
     close(window).then(done);
   });
 };
 
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See bug 809412");
-    }
-  }
-}
-
 require("test").run(exports);
--- a/addon-sdk/source/test/test-window-utils.js
+++ b/addon-sdk/source/test/test-window-utils.js
@@ -1,13 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
+module.metadata = {
+  engines: {
+    'Firefox': '*'
+  }
+};
+
 const windowUtils = require("sdk/deprecated/window-utils");
 const timer = require("sdk/timers");
 const { Cc, Ci } = require("chrome");
 const { Loader } = require("sdk/test/loader");
 const { open, getFrames, getWindowTitle, onFocus } = require('sdk/window/utils');
 const { close } = require('sdk/window/helpers');
 const { fromIterator: toArray } = require('sdk/util/array');
 
@@ -286,19 +292,9 @@ exports.testWindowIterator = function(as
     assert.ok(toArray(windowUtils.windowIterator()).indexOf(window) !== -1,
               "window is now in windowIterator()");
 
     // Wait for the window unload before ending test
     close(window).then(done);
   }, false);
 };
 
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See bug 809412");
-    }
-  }
-}
-
 require("test").run(exports);
--- a/addon-sdk/source/test/test-window-utils2.js
+++ b/addon-sdk/source/test/test-window-utils2.js
@@ -1,13 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
+// Opening new windows in Fennec causes issues
+module.metadata = {
+  engines: {
+    'Firefox': '*'
+  }
+};
+
 const { Ci } = require('chrome');
 const { open, backgroundify, windows, isBrowser,
         getXULWindow, getBaseWindow, getToplevelWindow, getMostRecentWindow,
         getMostRecentBrowserWindow } = require('sdk/window/utils');
 const { close } = require('sdk/window/helpers');
 const windowUtils = require('sdk/deprecated/window-utils');
 
 exports['test get nsIBaseWindow from nsIDomWindow'] = function(assert) {
@@ -56,16 +63,44 @@ exports['test new top window with option
   assert.equal(window.innerHeight, 100, 'height is set');
   assert.equal(window.innerWidth, 200, 'height is set');
   assert.equal(window.toolbar.visible, true, 'toolbar was set');
 
   // Wait for the window unload before ending test
   close(window).then(done);
 };
 
+exports['test new top window with various URIs'] = function(assert, done) {
+  let msg = 'only chrome, resource and data uris are allowed';
+  assert.throws(function () {
+    open('foo');
+  }, msg);
+  assert.throws(function () {
+    open('http://foo');
+  }, msg);
+  assert.throws(function () {
+    open('https://foo');
+  }, msg); 
+  assert.throws(function () {
+    open('ftp://foo');
+  }, msg);
+  assert.throws(function () {
+    open('//foo');
+  }, msg);
+
+  let chromeWindow = open('chrome://foo/content/');
+  assert.ok(~windows().indexOf(chromeWindow), 'chrome URI works');
+  
+  let resourceWindow = open('resource://foo');
+  assert.ok(~windows().indexOf(resourceWindow), 'resource URI works');
+
+  // Wait for the window unload before ending test
+  close(chromeWindow).then(close.bind(null, resourceWindow)).then(done);
+};
+
 exports.testBackgroundify = function(assert, done) {
   let window = open('data:text/html;charset=utf-8,backgroundy');
   assert.ok(~windows().indexOf(window),
             'window is in the list of windows');
   let backgroundy = backgroundify(window);
   assert.equal(backgroundy, window, 'backgroundify returs give window back');
   assert.ok(!~windows().indexOf(window),
             'backgroundifyied window is in the list of windows');
--- a/b2g/app/Makefile.in
+++ b/b2g/app/Makefile.in
@@ -25,17 +25,17 @@ LIBS += \
   -lEGL \
   -lhardware_legacy \
   -lhardware \
   -lcutils \
   $(DEPTH)/media/libpng/$(LIB_PREFIX)mozpng.$(LIB_SUFFIX) \
   $(DEPTH)/widget/gonk/libdisplay/$(LIB_PREFIX)display.$(LIB_SUFFIX) \
   $(MOZ_ZLIB_LIBS) \
   $(NULL)
-ifeq (17,$(ANDROID_VERSION))
+ifeq (18,$(ANDROID_VERSION))
 LIBS += \
   -lgui \
   -lsuspend \
   $(NULL)
 endif
 OS_LDFLAGS += -Wl,--export-dynamic
 LOCAL_INCLUDES += -I$(topsrcdir)/widget/gonk/libdisplay
 endif
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -374,20 +374,21 @@ pref("browser.link.open_newwindow.restri
 pref("dom.mozBrowserFramesEnabled", true);
 
 // Enable a (virtually) unlimited number of mozbrowser processes.
 // We'll run out of PIDs on UNIX-y systems before we hit this limit.
 pref("dom.ipc.processCount", 100000);
 
 pref("dom.ipc.browser_frames.oop_by_default", false);
 
-// WebSMS
+// SMS/MMS
 pref("dom.sms.enabled", true);
 pref("dom.sms.strict7BitEncoding", false); // Disabled by default.
 pref("dom.sms.requestStatusReport", true); // Enabled by default.
+pref("dom.mms.requestStatusReport", true); // Enabled by default.
 
 // WebContacts
 pref("dom.mozContacts.enabled", true);
 pref("dom.navigator-property.disable.mozContacts", false);
 pref("dom.global-constructor.disable.mozContact", false);
 
 // Shortnumber matching needed for e.g. Brazil:
 // 01187654321 can be found with 87654321
--- a/b2g/chrome/content/dbg-browser-actors.js
+++ b/b2g/chrome/content/dbg-browser-actors.js
@@ -116,15 +116,15 @@ Object.defineProperty(ContentTabActor.pr
 Object.defineProperty(ContentTabActor.prototype, "url", {
   get: function() {
     return this.browser.document.documentURI;
   },
   enumerable: true,
   configurable: false
 });
 
-Object.defineProperty(ContentTabActor.prototype, "contentWindow", {
+Object.defineProperty(ContentTabActor.prototype, "window", {
   get: function() {
     return this.browser;
   },
   enumerable: true,
   configurable: false
 });
--- a/b2g/chrome/content/payment.js
+++ b/b2g/chrome/content/payment.js
@@ -4,64 +4,125 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This JS shim contains the callbacks to fire DOMRequest events for
 // navigator.pay API within the payment processor's scope.
 
 "use strict";
 
-dump("======================= payment.js ======================= \n");
+let _DEBUG = false;
+function _debug(s) { dump("== Payment flow == " + s + "\n"); }
+_debug("Frame script injected");
 
 let { classes: Cc, interfaces: Ci, utils: Cu }  = Components;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
 #ifdef MOZ_B2G_RIL
-XPCOMUtils.defineLazyServiceGetter(this, "mobileConnection",
+XPCOMUtils.defineLazyServiceGetter(this, "iccProvider",
                                    "@mozilla.org/ril/content-helper;1",
-                                   "nsIMobileConnectionProvider");
+                                   "nsIIccProvider");
+
+XPCOMUtils.defineLazyServiceGetter(this, "smsService",
+                                   "@mozilla.org/sms/smsservice;1",
+                                   "nsISmsService");
+
+const kSilentSmsReceivedTopic = "silent-sms-received";
+
+const MOBILEMESSAGECALLBACK_CID =
+  Components.ID("{b484d8c9-6be4-4f94-ab60-c9c7ebcc853d}");
+
+// In order to send messages through nsISmsService, we need to implement
+// nsIMobileMessageCallback, as the WebSMS API implementation is not usable
+// from JS.
+function SilentSmsRequest() {
+}
+SilentSmsRequest.prototype = {
+  __exposedProps__: {
+    onsuccess: 'rw',
+    onerror: 'rw'
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileMessageCallback]),
+
+  classID: MOBILEMESSAGECALLBACK_CID,
+
+  set onsuccess(aSuccessCallback) {
+    this._onsuccess = aSuccessCallback;
+  },
+
+  set onerror(aErrorCallback) {
+    this._onerror = aErrorCallback;
+  },
+
+  notifyMessageSent: function notifyMessageSent(aMessage) {
+    if (_DEBUG) {
+      _debug("Silent message successfully sent");
+    }
+    this._onsuccess(aMessage);
+  },
+
+  notifySendMessageFailed: function notifySendMessageFailed(aError) {
+    if (_DEBUG) {
+      _debug("Error sending silent message " + aError);
+    }
+    this._onerror(aError);
+  }
+};
 #endif
 
-
 const kClosePaymentFlowEvent = "close-payment-flow-dialog";
 
-let _requestId;
+let gRequestId;
+
+let gBrowser = Services.wm.getMostRecentWindow("navigator:browser");
 
 let PaymentProvider = {
-
+#ifdef MOZ_B2G_RIL
   __exposedProps__: {
     paymentSuccess: 'r',
     paymentFailed: 'r',
-    iccIds: 'r'
+    iccIds: 'r',
+    mcc: 'r',
+    mnc: 'r',
+    sendSilentSms: 'r',
+    observeSilentSms: 'r',
+    removeSilentSmsObserver: 'r'
   },
+#else
+  __exposedProps__: {
+    paymentSuccess: 'r',
+    paymentFailed: 'r'
+  },
+#endif
 
   _closePaymentFlowDialog: function _closePaymentFlowDialog(aCallback) {
     // After receiving the payment provider confirmation about the
     // successful or failed payment flow, we notify the UI to close the
     // payment flow dialog and return to the caller application.
     let id = kClosePaymentFlowEvent + "-" + uuidgen.generateUUID().toString();
 
-    let browser = Services.wm.getMostRecentWindow("navigator:browser");
-    let content = browser.getContentWindow();
+    let content = gBrowser.getContentWindow();
     if (!content) {
       return;
     }
 
     let detail = {
       type: kClosePaymentFlowEvent,
       id: id,
-      requestId: _requestId
+      requestId: gRequestId
     };
 
     // In order to avoid race conditions, we wait for the UI to notify that
     // it has successfully closed the payment flow and has recovered the
     // caller app, before notifying the parent process to fire the success
     // or error event over the DOMRequest.
     content.addEventListener("mozContentEvent",
                              function closePaymentFlowReturn(evt) {
@@ -72,54 +133,181 @@ let PaymentProvider = {
       content.removeEventListener("mozContentEvent",
                                   closePaymentFlowReturn);
 
       let glue = Cc["@mozilla.org/payment/ui-glue;1"]
                    .createInstance(Ci.nsIPaymentUIGlue);
       glue.cleanup();
     });
 
-    browser.shell.sendChromeEvent(detail);
+    gBrowser.shell.sendChromeEvent(detail);
+
+#ifdef MOZ_B2G_RIL
+    this._cleanUp();
+#endif
   },
 
   paymentSuccess: function paymentSuccess(aResult) {
+    if (_DEBUG) {
+      _debug("paymentSuccess " + aResult);
+    }
+
     PaymentProvider._closePaymentFlowDialog(function notifySuccess() {
-      if (!_requestId) {
+      if (!gRequestId) {
         return;
       }
       cpmm.sendAsyncMessage("Payment:Success", { result: aResult,
-                                                 requestId: _requestId });
+                                                 requestId: gRequestId });
     });
   },
 
   paymentFailed: function paymentFailed(aErrorMsg) {
+    if (_DEBUG) {
+      _debug("paymentFailed " + aErrorMsg);
+    }
+
     PaymentProvider._closePaymentFlowDialog(function notifyError() {
-      if (!_requestId) {
+      if (!gRequestId) {
         return;
       }
       cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aErrorMsg,
-                                                requestId: _requestId });
+                                                requestId: gRequestId });
     });
   },
 
+#ifdef MOZ_B2G_RIL
+  // Until bug 814629 is done, we only have support for a single SIM, so we
+  // can only provide information for a single ICC. However, we return an array
+  // so the payment provider facing API won't need to change once we support
+  // multiple SIMs.
+
   get iccIds() {
-#ifdef MOZ_B2G_RIL
-    // Until bug 814629 is done, we only have support for a single SIM, so we
-    // can only provide a single ICC ID. However, we return an array so the
-    // payment provider facing API won't need to change once we support
-    // multiple SIMs.
-    return [mobileConnection.iccInfo.iccid];
-#else
-    return null;
-#endif
+    return [iccProvider.iccInfo.iccid];
+  },
+
+  get mcc() {
+    return [iccProvider.iccInfo.mcc];
+  },
+
+  get mnc() {
+    return [iccProvider.iccInfo.mnc];
+  },
+
+  _silentNumbers: null,
+  _silentSmsObservers: null,
+
+  sendSilentSms: function sendSilentSms(aNumber, aMessage) {
+    if (_DEBUG) {
+      _debug("Sending silent message " + aNumber + " - " + aMessage);
+    }
+
+    let request = new SilentSmsRequest();
+    smsService.send(aNumber, aMessage, true, request);
+    return request;
+  },
+
+  observeSilentSms: function observeSilentSms(aNumber, aCallback) {
+    if (_DEBUG) {
+      _debug("observeSilentSms " + aNumber);
+    }
+
+    if (!this._silentSmsObservers) {
+      this._silentSmsObservers = {};
+      this._silentNumbers = [];
+      Services.obs.addObserver(this._onSilentSms.bind(this),
+                               kSilentSmsReceivedTopic,
+                               false);
+    }
+
+    if (!this._silentSmsObservers[aNumber]) {
+      this._silentSmsObservers[aNumber] = [];
+      this._silentNumbers.push(aNumber);
+      smsService.addSilentNumber(aNumber);
+    }
+
+    if (this._silentSmsObservers[aNumber].indexOf(aCallback) == -1) {
+      this._silentSmsObservers[aNumber].push(aCallback);
+    }
   },
 
+  removeSilentSmsObserver: function removeSilentSmsObserver(aNumber, aCallback) {
+    if (_DEBUG) {
+      _debug("removeSilentSmsObserver " + aNumber);
+    }
+
+    if (!this._silentSmsObservers || !this._silentSmsObservers[aNumber]) {
+      if (_DEBUG) {
+        _debug("No observers for " + aNumber);
+      }
+      return;
+    }
+
+    let index = this._silentSmsObservers[aNumber].indexOf(aCallback);
+    if (index != -1) {
+      this._silentSmsObservers[aNumber].splice(index, 1);
+      if (this._silentSmsObservers[aNumber].length == 0) {
+        this._silentSmsObservers[aNumber] = null;
+        this._silentNumbers.splice(this._silentNumbers.indexOf(aNumber), 1);
+        smsService.removeSilentNumber(aNumber);
+      }
+    } else if (_DEBUG) {
+      _debug("No callback found for " + aNumber);
+    }
+  },
+
+  _onSilentSms: function _onSilentSms(aSubject, aTopic, aData) {
+    if (_DEBUG) {
+      _debug("Got silent message! " + aSubject.sender + " - " + aSubject.body);
+    }
+
+    let number = aSubject.sender;
+    if (!number || this._silentNumbers.indexOf(number) == -1) {
+      if (_DEBUG) {
+        _debug("No observers for " + number);
+      }
+      return;
+    }
+
+    this._silentSmsObservers[number].forEach(function(callback) {
+      callback(aSubject);
+    });
+  },
+
+  _cleanUp: function _cleanUp() {
+    if (_DEBUG) {
+      _debug("Cleaning up!");
+    }
+
+    if (!this._silentNumbers) {
+      return;
+    }
+
+    while (this._silentNumbers.length) {
+      let number = this._silentNumbers.pop();
+      smsService.removeSilentNumber(number);
+    }
+    this._silentNumbers = null;
+    this._silentSmsObservers = null;
+    Services.obs.removeObserver(this._onSilentSms, kSilentSmsReceivedTopic);
+  }
+#endif
 };
 
 // We save the identifier of the DOM request, so we can dispatch the results
 // of the payment flow to the appropriate content process.
 addMessageListener("Payment:LoadShim", function receiveMessage(aMessage) {
-  _requestId = aMessage.json.requestId;
+  gRequestId = aMessage.json.requestId;
 });
 
 addEventListener("DOMWindowCreated", function(e) {
   content.wrappedJSObject.mozPaymentProvider = PaymentProvider;
 });
+
+#ifdef MOZ_B2G_RIL
+// If the trusted dialog is not closed via paymentSuccess or paymentFailed
+// a mozContentEvent with type 'cancel' is sent from the UI. We need to listen
+// for this event to clean up the silent sms observers if any exists.
+gBrowser.getContentWindow().addEventListener("mozContentEvent", function(e) {
+  if (e.detail.type === "cancel") {
+    PaymentProvider._cleanUp();
+  }
+});
+#endif
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -128,16 +128,21 @@ SettingsListener.observe('language.curre
       Services.prefs.setBoolPref('dom.sms.strict7BitEncoding', value);
   });
 
   SettingsListener.observe('ril.sms.requestStatusReport.enabled', false,
     function(value) {
       Services.prefs.setBoolPref('dom.sms.requestStatusReport', value);
   });
 
+  SettingsListener.observe('ril.mms.requestStatusReport.enabled', false,
+    function(value) {
+      Services.prefs.setBoolPref('dom.mms.requestStatusReport', value);
+  });
+
   SettingsListener.observe('ril.cellbroadcast.disabled', false,
     function(value) {
       Services.prefs.setBoolPref('ril.cellbroadcast.disabled', value);
   });
 
   SettingsListener.observe('ril.radio.disabled', false,
     function(value) {
       Services.prefs.setBoolPref('ril.radio.disabled', value);
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -550,16 +550,18 @@ var shell = {
 
   openAppForSystemMessage: function shell_openAppForSystemMessage(msg) {
     let origin = Services.io.newURI(msg.manifest, null, null).prePath;
     this.sendChromeEvent({
       type: 'open-app',
       url: msg.uri,
       manifestURL: msg.manifest,
       isActivity: (msg.type == 'activity'),
+      onlyShowApp: msg.onlyShowApp,
+      showApp: msg.showApp,
       target: msg.target,
       expectingSystemMessage: true,
       extra: msg.extra
     });
   },
 
   receiveMessage: function shell_receiveMessage(message) {
     var activities = { 'content-handler': { name: 'view', response: null },
@@ -972,16 +974,17 @@ let RemoteDebugger = {
     this._promptDone = true;
   },
 
   // Start the debugger server.
   start: function debugger_start() {
     if (!DebuggerServer.initialized) {
       // Ask for remote connections.
       DebuggerServer.init(this.prompt.bind(this));
+      DebuggerServer.chromeWindowType = "navigator:browser";
       DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webbrowser.js");
       // Until we implement unix domain socket, we enable content actors
       // only on development devices
       if (Services.prefs.getBoolPref("devtools.debugger.enable-content-actors")) {
         DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/script.js");
         DebuggerServer.addGlobalActor(DebuggerServer.ChromeDebuggerActor, "chromeDebugger");
         DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webconsole.js");
         DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/gcli.js");
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -15,16 +15,20 @@ component {88b3eb21-d072-4e3b-886d-f89d8
 contract @mozilla.org/updates/update-prompt;1 {88b3eb21-d072-4e3b-886d-f89d8c49fe59}
 #endif
 
 # MozKeyboard.js
 component {397a7fdf-2254-47be-b74e-76625a1a66d5} MozKeyboard.js
 contract @mozilla.org/b2g-keyboard;1 {397a7fdf-2254-47be-b74e-76625a1a66d5}
 category JavaScript-navigator-property mozKeyboard @mozilla.org/b2g-keyboard;1
 
+component {5c7f4ce1-a946-4adc-89e6-c908226341a0} MozKeyboard.js
+contract @mozilla.org/b2g-inputmethod;1 {5c7f4ce1-a946-4adc-89e6-c908226341a0}
+category JavaScript-navigator-property mozInputMethod @mozilla.org/b2g-inputmethod;1
+
 # DirectoryProvider.js
 component {9181eb7c-6f87-11e1-90b1-4f59d80dd2e5} DirectoryProvider.js
 contract @mozilla.org/browser/directory-provider;1 {9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}
 category xpcom-directory-providers browser-directory-provider @mozilla.org/browser/directory-provider;1
 
 # ActivitiesGlue.js
 component {70a83123-7467-4389-a309-3e81c74ad002} ActivitiesGlue.js
 contract @mozilla.org/dom/activities/ui-glue;1 {70a83123-7467-4389-a309-3e81c74ad002}
--- a/b2g/components/Keyboard.jsm
+++ b/b2g/components/Keyboard.jsm
@@ -16,17 +16,18 @@ Cu.import("resource://gre/modules/XPCOMU
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
   "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageBroadcaster");
 
 let Keyboard = {
   _messageManager: null,
   _messageNames: [
     'SetValue', 'RemoveFocus', 'SetSelectedOption', 'SetSelectedOptions',
-    'SetSelectionRange', 'ReplaceSurroundingText'
+    'SetSelectionRange', 'ReplaceSurroundingText', 'ShowInputMethodPicker',
+    'SwitchToNextInputMethod', 'HideInputMethod'
   ],
 
   get messageManager() {
     if (this._messageManager && !Cu.isDeadWrapper(this._messageManager))
       return this._messageManager;
 
     throw Error('no message manager set');
   },
@@ -115,16 +116,22 @@ let Keyboard = {
         this.setSelectedOption(msg);
         break;
       case 'Keyboard:SetSelectionRange':
         this.setSelectionRange(msg);
         break;
       case 'Keyboard:ReplaceSurroundingText':
         this.replaceSurroundingText(msg);
         break;
+      case 'Keyboard:SwitchToNextInputMethod':
+        this.switchToNextInputMethod();
+        break;
+      case 'Keyboard:ShowInputMethodPicker':
+        this.showInputMethodPicker();
+        break;
     }
   },
 
   handleFormsInput: function keyboardHandleFormsInput(msg) {
     this.messageManager = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
                              .frameLoader.messageManager;
 
     ppmm.broadcastAsyncMessage('Keyboard:FocusChange', msg.data);
@@ -155,12 +162,26 @@ let Keyboard = {
 
   removeFocus: function keyboardRemoveFocus() {
     this.messageManager.sendAsyncMessage('Forms:Select:Blur', {});
   },
 
   replaceSurroundingText: function keyboardReplaceSurroundingText(msg) {
     this.messageManager.sendAsyncMessage('Forms:ReplaceSurroundingText',
                                          msg.data);
+  },
+
+  showInputMethodPicker: function keyboardShowInputMethodPicker() {
+    let browser = Services.wm.getMostRecentWindow("navigator:browser");
+    browser.shell.sendChromeEvent({
+      type: "input-method-show-picker"
+    });
+  },
+
+  switchToNextInputMethod: function keyboardSwitchToNextInputMethod() {
+    let browser = Services.wm.getMostRecentWindow("navigator:browser");
+    browser.shell.sendChromeEvent({
+      type: "input-method-switch-to-next"
+    });
   }
 };
 
 Keyboard.init();
--- a/b2g/components/MozKeyboard.js
+++ b/b2g/components/MozKeyboard.js
@@ -76,17 +76,17 @@ MozKeyboard.prototype = {
   sendKey: function mozKeyboardSendKey(keyCode, charCode) {
     charCode = (charCode == undefined) ? keyCode : charCode;
 
     let mainThread = tm.mainThread;
     let utils = this._utils;
 
     function send(type) {
       mainThread.dispatch(function() {
-	utils.sendKeyEvent(type, keyCode, charCode, null);
+	      utils.sendKeyEvent(type, keyCode, charCode, null);
       }, mainThread.DISPATCH_NORMAL);
     }
 
     send("keydown");
     send("keypress");
     send("keyup");
   },
 
@@ -192,9 +192,90 @@ MozKeyboard.prototype = {
 
   observe: function mozKeyboardObserve(subject, topic, data) {
     let wId = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
     if (wId == this.innerWindowID)
       this.uninit();
   }
 };
 
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MozKeyboard]);
+/**
+ * ==============================================
+ * InputMethodManager
+ * ==============================================
+ */
+function MozInputMethodManager() { }
+
+MozInputMethodManager.prototype = {
+  classID: Components.ID("{7e9d7280-ef86-11e2-b778-0800200c9a66}"),
+
+  QueryInterface: XPCOMUtils.generateQI([
+    Ci.nsIInputMethodManager
+  ]),
+
+  classInfo: XPCOMUtils.generateCI({
+    "classID": Components.ID("{7e9d7280-ef86-11e2-b778-0800200c9a66}"),
+    "contractID": "@mozilla.org/b2g-imm;1",
+    "interfaces": [Ci.nsIInputMethodManager],
+    "flags": Ci.nsIClassInfo.DOM_OBJECT,
+    "classDescription": "B2G Input Method Manager"
+  }),
+
+  showAll: function() {
+    cpmm.sendAsyncMessage('Keyboard:ShowInputMethodPicker', {});
+  },
+
+  next: function() {
+    cpmm.sendAsyncMessage('Keyboard:SwitchToNextInputMethod', {});
+  },
+
+  supportsSwitching: function() {
+    return true;
+  },
+
+  hide: function() {
+    cpmm.sendAsyncMessage('Keyboard:RemoveFocus', {});
+  }
+};
+
+/**
+ * ==============================================
+ * InputMethod
+ * ==============================================
+ */
+function MozInputMethod() { }
+
+MozInputMethod.prototype = {
+  classID: Components.ID("{5c7f4ce1-a946-4adc-89e6-c908226341a0}"),
+
+  QueryInterface: XPCOMUtils.generateQI([
+    Ci.nsIInputMethod,
+    Ci.nsIDOMGlobalPropertyInitializer
+  ]),
+
+  classInfo: XPCOMUtils.generateCI({
+    "classID": Components.ID("{5c7f4ce1-a946-4adc-89e6-c908226341a0}"),
+    "contractID": "@mozilla.org/b2g-inputmethod;1",
+    "interfaces": [Ci.nsIInputMethod],
+    "flags": Ci.nsIClassInfo.DOM_OBJECT,
+    "classDescription": "B2G Input Method"
+  }),
+
+  init: function mozInputMethodInit(win) {
+    let principal = win.document.nodePrincipal;
+    let perm = Services.perms
+               .testExactPermissionFromPrincipal(principal, "keyboard");
+    if (perm != Ci.nsIPermissionManager.ALLOW_ACTION) {
+      dump("No permission to use the keyboard API for " +
+           principal.origin + "\n");
+      return null;
+    }
+
+    this._mgmt = new MozInputMethodManager();
+  },
+
+  get mgmt() {
+    return this._mgmt;
+  }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
+  [MozKeyboard, MozInputMethodManager, MozInputMethod]);
--- a/b2g/components/b2g.idl
+++ b/b2g/components/b2g.idl
@@ -71,8 +71,41 @@ interface nsIB2GKeyboard : nsISupports
    * beginning of the current selection range. Defaults to 0.
    * @param afterLength The number of characters to be deleted after the
    * beginning of the current selection range. Defaults to 0.
    */
   void replaceSurroundingText(in DOMString text,
                               [optional] in long beforeLength,
                               [optional] in long afterLength);
 };
+
+// Manages the list of IMEs, enables/disables IME and switches to an IME.
+[scriptable, uuid(e51a6fa0-ef85-11e2-b778-0800200c9a66)]
+interface nsIInputMethodManager : nsISupports
+{
+  // Ask the OS to show a list of available IMEs for users to switch from.
+  // OS should ignore this request if the app is currently not the active one.
+  void showAll();
+
+  // Ask the OS to switch away from the current active Keyboard app.
+  // OS should ignore this request if the app is currently not the active one.
+  void next();
+
+  // To know if the OS supports IME switching or not.
+  // Use case: let the keyboard app knows if it is necessary to show the "IME switching"
+  // (globe) button. We have a use case that when there is only one IME enabled, we
+  // should not show the globe icon.
+  boolean supportsSwitching();
+
+  // Ask the OS to hide the current active Keyboard app. (was: |removeFocus()|)
+  // OS should ignore this request if the app is currently not the active one.
+  // The OS will void the current input context (if it exists).
+  // This method belong to |mgmt| because we would like to allow Keyboard to access to
+  // this method w/o a input context.
+  void hide();
+};
+
+[scriptable, uuid(5c7f4ce1-a946-4adc-89e6-c908226341a0)]
+interface nsIInputMethod : nsISupports
+{
+  // Input Method Manager contain a few global methods expose to apps
+  readonly attribute nsIInputMethodManager mgmt;
+};
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "878cc221e0fdadb4d42dc110945533104f6dd572", 
+    "revision": "7669b3265def0eed0473acd938897704007afaf3", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/leo/releng-leo.tt
+++ b/b2g/config/leo/releng-leo.tt
@@ -1,12 +1,12 @@
 [
 {
-"size": 117247732,
-"digest": "16e74278e4e9b0d710df77d68af1677c91823dccfc611ab00ee617298a63787f9f9892bd1a41eccb8d45fb18d61bfda0dbd1de88f1861c14b4b44da3b94a4eca",
+"size": 114839868,
+"digest": "4754612c52330f4d25a250eb52adbf245950e3411a46f45836950b96fdff99e37e3563667d9a0069a7097f3900af3710d9e94010a26bf2e7e1a27c97f37ef447",
 "algorithm": "sha512",
 "filename": "backup-leo.tar.xz"
 },
 {
 "size": 1570553,
 "digest": "ea03de74df73b05e939c314cd15c54aac7b5488a407b7cc4f5f263f3049a1f69642c567dd35c43d0bc3f0d599d0385a26ab2dd947a6b18f9044e4918b382eea7",
 "algorithm": "sha512",
 "filename": "Adreno200-AU_LINUX_ANDROID_ICS_CHOCO_CS.04.00.03.06.001.zip"
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -522,16 +522,17 @@
 @BINPATH@/components/Activities.manifest
 @BINPATH@/components/ActivityOptions.js
 @BINPATH@/components/ActivityProxy.js
 @BINPATH@/components/ActivityRequestHandler.js
 @BINPATH@/components/ActivityWrapper.js
 @BINPATH@/components/ActivityMessageConfigurator.js
 
 @BINPATH@/components/TCPSocket.js
+@BINPATH@/components/TCPServerSocket.js
 @BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
 @BINPATH@/components/AppProtocolHandler.js
 @BINPATH@/components/AppProtocolHandler.manifest
 
 @BINPATH@/components/Payment.js
 @BINPATH@/components/PaymentFlowInfo.js
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1374187152000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1375219877000">
   <emItems>
       <emItem  blockID="i350" id="sqlmoz@facebook.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i58" id="webmaster@buzzzzvideos.info">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
@@ -130,16 +130,20 @@
       <emItem  blockID="i97" id="support3_en@adobe122.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i382" id="{6926c7f7-6006-42d1-b046-eba1b3010315}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i429" id="{B40794A0-7477-4335-95C5-8CB9BBC5C4A5}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i11" id="yslow@yahoo-inc.com">
                         <versionRange  minVersion="2.0.5" maxVersion="2.0.5">
                       <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="3.5.7" maxVersion="*" />
                           </targetApplication>
                     </versionRange>
                   </emItem>
       <emItem  blockID="i62" id="jid0-EcdqvFOgWLKHNJPuqAnawlykCGZ@jetpack">
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -62,16 +62,23 @@ pref("extensions.hotfix.certs.1.sha1Fing
 
 // Disable add-ons that are not installed by the user in all scopes by default.
 // See the SCOPE constants in AddonManager.jsm for values to use here.
 pref("extensions.autoDisableScopes", 15);
 
 // Dictionary download preference
 pref("browser.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/firefox/dictionaries/");
 
+// At startup, should we check to see if the installation
+// date is older than some threshold
+pref("app.update.checkInstallTime", true);
+
+// The number of days a binary is permitted to be old without checking is defined in
+// firefox-branding.js (app.update.checkInstallTime.days)
+
 // The minimum delay in seconds for the timer to fire.
 // default=2 minutes
 pref("app.update.timerMinimumDelay", 120);
 
 // App-specific update preferences
 
 // The interval to check for updates (app.update.interval) is defined in
 // firefox-branding.js
@@ -218,18 +225,16 @@ pref("general.skins.selectedSkin", "clas
 
 pref("general.smoothScroll", true);
 #ifdef UNIX_BUT_NOT_MAC
 pref("general.autoScroll", false);
 #else
 pref("general.autoScroll", true);
 #endif
 
-pref("general.useragent.complexOverride.moodle", false); // bug 797703
-
 // At startup, check if we're the default browser and prompt user if not.
 pref("browser.shell.checkDefaultBrowser", true);
 pref("browser.shell.shortcutFavicons",true);
 
 // 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
 // The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore
 pref("browser.startup.page",                1);
 pref("browser.startup.homepage",            "chrome://branding/locale/browserconfig.properties");
@@ -328,16 +333,20 @@ pref("browser.download.manager.flashCoun
 pref("browser.download.manager.addToRecentDocs", true);
 pref("browser.download.manager.quitBehavior", 0);
 pref("browser.download.manager.scanWhenDone", true);
 pref("browser.download.manager.resumeOnWakeDelay", 10000);
 
 // This allows disabling the Downloads Panel in favor of the old interface.
 pref("browser.download.useToolkitUI", false);
 
+// This allows disabling the animated notifications shown by
+// the Downloads Indicator when a download starts or completes.
+pref("browser.download.animateNotifications", true);
+
 // This records whether or not the panel has been shown at least once.
 pref("browser.download.panel.shown", false);
 
 // This records whether or not at least one session with the Downloads Panel
 // enabled has been completed already.
 pref("browser.download.panel.firstSessionCompleted", false);
 
 // search engines URL
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -605,21 +605,23 @@ SocialShare = {
     let panel = this.panel;
     if (!SocialUI.enabled || this.iframe)
       return;
     this.panel.hidden = false;
     // create and initialize the panel for this window
     let iframe = document.createElement("iframe");
     iframe.setAttribute("type", "content");
     iframe.setAttribute("class", "social-share-frame");
+    iframe.setAttribute("context", "contentAreaContextMenu");
+    iframe.setAttribute("tooltip", "aHTMLTooltip");
     iframe.setAttribute("flex", "1");
     panel.appendChild(iframe);
     this.populateProviderMenu();
   },
-  
+
   getSelectedProvider: function() {
     let provider;
     let lastProviderOrigin = this.iframe && this.iframe.getAttribute("origin");
     if (lastProviderOrigin) {
       provider = Social._getProviderFromOrigin(lastProviderOrigin);
     }
     if (!provider)
       provider = Social.provider || Social.defaultProvider;
@@ -1361,16 +1363,21 @@ SocialSidebar = {
       if (Social.provider.errorState == "frameworker-error") {
         SocialSidebar.setSidebarErrorMessage();
         return;
       }
 
       // Make sure the right sidebar URL is loaded
       if (sbrowser.getAttribute("src") != Social.provider.sidebarURL) {
         sbrowser.setAttribute("src", Social.provider.sidebarURL);
+        PopupNotifications.locationChange(sbrowser);
+      }
+
+      // if the document has not loaded, delay until it is
+      if (sbrowser.contentDocument.readyState != "complete") {
         sbrowser.addEventListener("load", SocialSidebar._loadListener, true);
       } else {
         this.setSidebarVisibilityState(true);
       }
     }
   },
 
   _loadListener: function SocialSidebar_loadListener() {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3006,18 +3006,17 @@ const DOMLinkHandler = {
           }
           break;
         case "search":
           if (!searchAdded) {
             var type = link.type && link.type.toLowerCase();
             type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
 
             if (type == "application/opensearchdescription+xml" && link.title &&
-                /^(?:https?|ftp):/i.test(link.href) &&
-                !PrivateBrowsingUtils.isWindowPrivate(window)) {
+                /^(?:https?|ftp):/i.test(link.href)) {
               var engine = { title: link.title, href: link.href };
               BrowserSearch.addEngine(engine, link.ownerDocument);
               searchAdded = true;
             }
           }
           break;
       }
     }
@@ -3936,18 +3935,20 @@ var XULBrowserWindow = {
         this.reloadCommand.removeAttribute("disabled");
       }
 
       if (gURLBar) {
         URLBarSetURI(aLocationURI);
 
         // Update starring UI
         BookmarkingUI.updateStarState();
-        SocialMark.updateMarkState();
-        SocialShare.update();
+        if (SocialUI.enabled) {
+          SocialMark.updateMarkState();
+          SocialShare.update();
+        }
       }
 
       // Show or hide browser chrome based on the whitelist
       if (this.hideChromeForLocation(location)) {
         document.documentElement.setAttribute("disablechrome", "true");
       } else {
         let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
         if (ss.getTabValue(gBrowser.selectedTab, "appOrigin"))
@@ -6400,17 +6401,17 @@ var gIdentityHandler = {
       gNavigatorBundle.getString("identity.encrypted2");
     this._encryptionLabel[this.IDENTITY_MODE_IDENTIFIED] =
       gNavigatorBundle.getString("identity.encrypted2");
     this._encryptionLabel[this.IDENTITY_MODE_UNKNOWN] =
       gNavigatorBundle.getString("identity.unencrypted");
     this._encryptionLabel[this.IDENTITY_MODE_MIXED_DISPLAY_LOADED] =
       gNavigatorBundle.getString("identity.mixed_display_loaded");
     this._encryptionLabel[this.IDENTITY_MODE_MIXED_ACTIVE_LOADED] =
-      gNavigatorBundle.getString("identity.mixed_active_loaded");
+      gNavigatorBundle.getString("identity.mixed_active_loaded2");
     this._encryptionLabel[this.IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED] =
       gNavigatorBundle.getString("identity.mixed_display_loaded_active_blocked");
     return this._encryptionLabel;
   },
   get _identityPopup () {
     delete this._identityPopup;
     return this._identityPopup = document.getElementById("identity-popup");
   },
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -581,20 +581,18 @@
                 // cancelled a pending load which would have cleared
                 // its anchor scroll detection temporary increment.
                 if (aWebProgress.isTopLevel)
                   this.mBrowser.userTypedClear += 2;
 
                 if (this._shouldShowProgress(aRequest)) {
                   if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
                     this.mTab.setAttribute("busy", "true");
-                    if (!gMultiProcessBrowser) {
-                      if (!(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
-                        this.mTabBrowser.setTabTitleLoading(this.mTab);
-                    }
+                    if (!(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
+                      this.mTabBrowser.setTabTitleLoading(this.mTab);
                   }
 
                   if (this.mTab.selected)
                     this.mTabBrowser.mIsBusy = true;
                 }
               }
               else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
                        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
@@ -696,20 +694,19 @@
                     findBar.close();
 
                   // fix bug 253793 - turn off highlight when page changes
                   findBar.getElement("highlight").checked = false;
                 }
 
                 // Don't clear the favicon if this onLocationChange was
                 // triggered by a pushState or a replaceState.  See bug 550565.
-                if (!gMultiProcessBrowser) {
-                  if (aWebProgress.isLoadingDocument &&
-                      !(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE))
-                    this.mBrowser.mIconURL = null;
+                if (aWebProgress.isLoadingDocument &&
+                    !(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE)) {
+                  this.mBrowser.mIconURL = null;
                 }
 
                 let autocomplete = this.mTabBrowser._placesAutocomplete;
                 if (this.mBrowser.registeredOpenURI) {
                   autocomplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
                   delete this.mBrowser.registeredOpenURI;
                 }
                 // Tabs in private windows aren't registered as "Open" so
@@ -825,40 +822,33 @@
           ]]>
         </body>
       </method>
 
       <method name="useDefaultIcon">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
-            // Bug 691610 - e10s support for useDefaultIcon
-            if (gMultiProcessBrowser)
-              return;
-
             var browser = this.getBrowserForTab(aTab);
-            var docURIObject = browser.contentDocument.documentURIObject;
+            var documentURI = browser.documentURI;
             var icon = null;
-            if (browser.contentDocument instanceof ImageDocument) {
+
+            if (browser.imageDocument) {
               if (Services.prefs.getBoolPref("browser.chrome.site_icons")) {
                 let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size");
-                try {
-                  let req = browser.contentDocument.imageRequest;
-                  if (req &&
-                      req.image &&
-                      req.image.width <= sz &&
-                      req.image.height <= sz)
-                    icon = browser.currentURI;
-                } catch (e) { }
+                if (browser.imageDocument.width <= sz &&
+                    browser.imageDocument.height <= sz) {
+                  icon = browser.currentURI;
+                }
               }
             }
             // Use documentURIObject in the check for shouldLoadFavIcon so that we
             // do the right thing with about:-style error pages.  Bug 453442
-            else if (this.shouldLoadFavIcon(docURIObject)) {
-              let url = docURIObject.prePath + "/favicon.ico";
+            else if (this.shouldLoadFavIcon(documentURI)) {
+              let url = documentURI.prePath + "/favicon.ico";
               if (!this.isFailedIcon(url))
                 icon = url;
             }
             this.setIcon(aTab, icon);
           ]]>
         </body>
       </method>
 
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -217,17 +217,16 @@ MOCHITEST_BROWSER_FILES = \
                  browser_minimize.js \
                  browser_offlineQuotaNotification.js \
                  browser_overflowScroll.js \
                  browser_page_style_menu.js \
                  browser_pageInfo_plugins.js \
                  browser_pageInfo.js \
                  browser_pinnedTabs.js \
                  browser_plainTextLinks.js \
-                 browser_pluginCrashCommentAndURL.js \
                  browser_pluginnotification.js \
                  browser_pluginplaypreview.js \
                  browser_pluginplaypreview2.js \
                  browser_plugins_added_dynamically.js \
                  browser_popupUI.js \
                  browser_private_browsing_window.js \
                  browser_private_no_prompt.js \
                  browser_relatedTabs.js \
@@ -316,17 +315,16 @@ MOCHITEST_BROWSER_FILES = \
                  plugin_clickToPlayDeny.html \
                  plugin_hidden_to_visible.html \
                  plugin_test.html \
                  plugin_test2.html \
                  plugin_test3.html \
                  plugin_two_types.html \
                  plugin_data_url.html \
                  plugin_unknown.html \
-                 pluginCrashCommentAndURL.html \
                  POSTSearchEngine.xml \
                  print_postdata.sjs \
                  redirect_bug623155.sjs \
                  test_bug435035.html \
                  test_bug462673.html \
                  test_bug628179.html \
                  test_bug839103.html \
                  test_wyciwyg_copying.html \
@@ -354,9 +352,16 @@ MOCHITEST_BROWSER_FILES += \
 endif
 
 ifdef MOZ_DATA_REPORTING
 MOCHITEST_BROWSER_FILES += \
   browser_datareporting_notification.js \
   $(NULL)
 endif
 
+ifdef MOZ_CRASHREPORTER
+MOCHITEST_BROWSER_FILES += \
+  browser_pluginCrashCommentAndURL.js \
+  pluginCrashCommentAndURL.html \
+  $(NULL)
+endif
+
 include $(topsrcdir)/config/rules.mk
--- a/browser/base/content/test/browser_locationBarCommand.js
+++ b/browser/base/content/test/browser_locationBarCommand.js
@@ -27,28 +27,30 @@ saveURL = function() {
   runShiftLeftClickTest();
 }
 function runAltLeftClickTest() {
   info("Running test: Alt left click");
   triggerCommand(true, { altKey: true });
 }
 
 function runShiftLeftClickTest() {
-  let listener = new WindowListener(getBrowserURL(), function(aWindow) {
+  let listener = new BrowserWindowListener(getBrowserURL(), function(aWindow) {
     Services.wm.removeListener(listener);
     addPageShowListener(aWindow.gBrowser.selectedBrowser, function() {
       executeSoon(function () {
         info("URL should be loaded in a new window");
         is(gURLBar.value, "", "Urlbar reverted to original value");       
         is(gFocusManager.focusedElement, null, "There should be no focused element");
         is(gFocusManager.focusedWindow, aWindow.gBrowser.contentWindow, "Content window should be focused");
         is(aWindow.gURLBar.value, TEST_VALUE, "New URL is loaded in new window");
 
         aWindow.close();
-        runNextTest();
+
+        // Continue testing when the original window has focus again.
+        whenWindowActivated(window, runNextTest);
       });
     }, "http://example.com/");
   });
   Services.wm.addListener(listener);
 
   info("Running test: Shift left click");
   triggerCommand(true, { shiftKey: true });
 }
@@ -170,32 +172,42 @@ function addPageShowListener(browser, cb
     info("pageshow: " + browser.currentURI.spec);
     if (expectedURL && browser.currentURI.spec != expectedURL)
       return; // ignore pageshows for non-expected URLs
     browser.removeEventListener("pageshow", pageShowListener, false);
     cb();
   });
 }
 
-function WindowListener(aURL, aCallback) {
+function whenWindowActivated(win, cb) {
+  if (Services.focus.activeWindow == win) {
+    executeSoon(cb);
+    return;
+  }
+
+  win.addEventListener("activate", function onActivate() {
+    win.removeEventListener("activate", onActivate);
+    executeSoon(cb);
+  });
+}
+
+function BrowserWindowListener(aURL, aCallback) {
   this.callback = aCallback;
   this.url = aURL;
 }
-WindowListener.prototype = {
+BrowserWindowListener.prototype = {
   onOpenWindow: function(aXULWindow) {
-    var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+    let cb = () => this.callback(domwindow);
+    let domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIDOMWindow);
-    var self = this;
-    domwindow.addEventListener("load", function() {
-      domwindow.removeEventListener("load", arguments.callee, false);
 
-      if (domwindow.document.location.href != self.url)
-        return;
+    let numWait = 2;
+    function maybeRunCallback() {
+      if (--numWait == 0)
+        cb();
+    }
 
-      // Allow other window load listeners to execute before passing to callback
-      executeSoon(function() {
-        self.callback(domwindow);
-      });
-    }, false);
+    whenWindowActivated(domwindow, maybeRunCallback);
+    whenDelayedStartupFinished(domwindow, maybeRunCallback);
   },
   onCloseWindow: function(aXULWindow) {},
   onWindowTitleChange: function(aXULWindow, aNewTitle) {}
 }
--- a/browser/base/content/test/browser_save_link-perwindowpb.js
+++ b/browser/base/content/test/browser_save_link-perwindowpb.js
@@ -72,21 +72,26 @@ function triggerSave(aWindow, aCallback)
 
 function test() {
   waitForExplicitFinish();
 
   var windowsToClose = [];
   var gNumSet = 0;
   function testOnWindow(options, callback) {
     var win = OpenBrowserWindow(options);
-    win.addEventListener("load", function onLoad() {
-      win.removeEventListener("load", onLoad, false);
-      windowsToClose.push(win);
-      executeSoon(function() callback(win));
-    }, false);
+    whenDelayedStartupFinished(win, () => callback(win));
+  }
+
+  function whenDelayedStartupFinished(aWindow, aCallback) {
+    Services.obs.addObserver(function observer(aSubject, aTopic) {
+      if (aWindow == aSubject) {
+        Services.obs.removeObserver(observer, aTopic);
+        executeSoon(aCallback);
+      }
+    }, "browser-delayed-startup-finished", false);
   }
 
   mockTransferRegisterer.register();
 
   registerCleanupFunction(function () {
     mockTransferRegisterer.unregister();
     MockFilePicker.cleanup();
     windowsToClose.forEach(function(win) {
--- a/browser/base/content/test/browser_tab_dragdrop.js
+++ b/browser/base/content/test/browser_tab_dragdrop.js
@@ -56,19 +56,17 @@ function test()
     var t = tabs[1];
     var b = gBrowser.getBrowserForTab(t);
     gBrowser.selectedTab = t;
     b.addEventListener("load", function() {
       b.removeEventListener("load", arguments.callee, true);
 
       executeSoon(function () {
         var win = gBrowser.replaceTabWithWindow(t);
-        win.addEventListener("load", function () {
-          win.removeEventListener("load", arguments.callee, true);
-
+        whenDelayedStartupFinished(win, function () {
           // Verify that the original window now only has the initial tab left in it.
           is(gBrowser.tabs[0], tabs[0], "tab0");
           is(gBrowser.getBrowserForTab(gBrowser.tabs[0]).contentWindow.location, "about:blank", "tab0 uri");
 
           executeSoon(function () {
             win.gBrowser.addEventListener("pageshow", function () {
               win.gBrowser.removeEventListener("pageshow", arguments.callee, false);
               executeSoon(function () {
@@ -77,17 +75,17 @@ function test()
                 var doc = b.docShell.contentViewer.DOMDocument.wrappedJSObject;
                 clickTest(doc, win);
                 win.close();
                 finish();
               });
             }, false);
             win.gBrowser.goBack();
           });
-        }, true);
+        });
       });
     }, true);
     b.loadURI("about:blank");
 
   }
 
   var loads = 0;
   function waitForLoad(event, tab, listenerContainer) {
@@ -106,15 +104,15 @@ function test()
     var listenerContainer = { listener: null }
     listenerContainer.listener = function (event) { return f(event, arg, listenerContainer); };
     return listenerContainer.listener;
   }
   for (var i = 1; i < tabs.length; ++i) {
     gBrowser.getBrowserForTab(tabs[i]).addEventListener("load", fn(waitForLoad,i), true);
   }
 
-  setLocation(1, "data:text/html,<title>tab1</title><body>tab1<iframe>");
-  setLocation(2, "data:text/plain,tab2");
-  setLocation(3, "data:text/html,<title>tab3</title><body>tab3<iframe>");
-  setLocation(4, "data:text/html,<body onload='clicks=0' onclick='++clicks'>"+embed);
+  setLocation(1, "data:text/html;charset=utf-8,<title>tab1</title><body>tab1<iframe>");
+  setLocation(2, "data:text/plain;charset=utf-8,tab2");
+  setLocation(3, "data:text/html;charset=utf-8,<title>tab3</title><body>tab3<iframe>");
+  setLocation(4, "data:text/html;charset=utf-8,<body onload='clicks=0' onclick='++clicks'>"+embed);
   gBrowser.selectedTab = tabs[3];
 
 }
--- a/browser/base/content/test/social/browser_addons.js
+++ b/browser/base/content/test/social/browser_addons.js
@@ -286,43 +286,41 @@ var tests = {
     // point should be upgraded
     let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
     addTab(activationURL, function(tab) {
       let doc = tab.linkedBrowser.contentDocument;
       let installFrom = doc.nodePrincipal.origin;
       Services.prefs.setCharPref("social.whitelist", installFrom);
       Social.installProvider(doc, manifest2, function(addonManifest) {
         SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
+          is(provider.manifest.version, 1, "manifest version is 1");
           Social.enabled = true;
-          checkSocialUI();
-          is(Social.provider.manifest.version, 1, "manifest version is 1")
-          // watch for the provider-update and tell the worker to update
+
+          // watch for the provider-update and test the new version
           SocialService.registerProviderListener(function providerListener(topic, data) {
             if (topic != "provider-update")
               return;
             SocialService.unregisterProviderListener(providerListener);
-            observeProviderSet(function() {
-              Services.prefs.clearUserPref("social.whitelist");
-              executeSoon(function() {
-                is(Social.provider.manifest.version, 2, "manifest version is 2");
-                Social.uninstallProvider(addonManifest.origin);
-                gBrowser.removeTab(tab);
-                next();
-              })
-            });
+            Services.prefs.clearUserPref("social.whitelist");
+            let provider = Social._getProviderFromOrigin(addonManifest.origin);
+            is(provider.manifest.version, 2, "manifest version is 2");
+            Social.uninstallProvider(addonManifest.origin);
+            gBrowser.removeTab(tab);
+            next();
           });
-          let port = Social.provider.getWorkerPort();
-          port.postMessage({topic: "worker.update", data: true});
+
+          let port = provider.getWorkerPort();
+          port.onmessage = function (e) {
+            let topic = e.data.topic;
+            switch (topic) {
+              case "got-sidebar-message":
+                ok(true, "got the sidebar message from provider 1");
+                port.postMessage({topic: "worker.update", data: true});
+                break;
+            }
+          };
+          port.postMessage({topic: "test-init"});
+
         });
       });
     });
   }
 }
-
-
-function observeProviderSet(cb) {
-  Services.obs.addObserver(function providerSet(subject, topic, data) {
-    Services.obs.removeObserver(providerSet, "social:provider-set");
-    info("social:provider-set observer was notified");
-    // executeSoon to let the browser UI observers run first
-    executeSoon(cb);
-  }, "social:provider-set", false);
-}
\ No newline at end of file
--- a/browser/base/content/test/social/browser_social_toolbar.js
+++ b/browser/base/content/test/social/browser_social_toolbar.js
@@ -119,28 +119,32 @@ var tests = {
     Social.provider.setAmbientNotification(ambience3);
     
     try {
       Social.provider.setAmbientNotification(ambience4);
     } catch(e) {}
     let numIcons = Object.keys(Social.provider.ambientNotificationIcons).length;
     ok(numIcons == 3, "prevent adding more than 3 ambient notification icons");
 
-    let statusIcon = document.getElementById("social-provider-button").nextSibling;
+    let mButton = document.getElementById("social-mark-button");
+    let pButton = document.getElementById("social-provider-button");
     waitForCondition(function() {
-      statusIcon = document.getElementById("social-provider-button").nextSibling;
-      return !!statusIcon;
+      // wait for a new button to be inserted inbetween the provider and mark
+      // button
+      return pButton.nextSibling != mButton;
     }, function () {
+      let statusIcon = pButton.nextSibling;
       let badge = statusIcon.getAttribute("badge");
       is(badge, "42", "status value is correct");
       // If there is a counter, the aria-label should reflect it.
       is(statusIcon.getAttribute("aria-label"), "Test Ambient 1 \u2046 (42)");
 
       ambience.counter = 0;
       Social.provider.setAmbientNotification(ambience);
+      statusIcon = pButton.nextSibling;
       badge = statusIcon.getAttribute("badge");
       is(badge, "", "status value is correct");
       // If there is no counter, the aria-label should be the same as the label
       is(statusIcon.getAttribute("aria-label"), "Test Ambient 1 \u2046");
 
       // The menu bar isn't as easy to instrument on Mac.
       if (navigator.platform.contains("Mac")) {
         next();
index 6aba01cd3ef128d834a9c3c978c431ab4c5bb3d0..e7dede528826f2ac090a5133dd6318a2fe67c73d
GIT binary patch
literal 2345
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
P00000NkvXXu0mjfS~_EQ
index 4ae88040c1ec0045f4579706987516d4bd467769..c019a8393b0064f8b79ecd666b790fdf798d5011
GIT binary patch
literal 993957
zc$|D(2V4_p|38*Enp`fIy+c9>BM?Z~dk6}mfZ$%Zaqm4YVJV;@N)`7)Eh<{Uz3Qra
z)II7}#4RX8RQUg%3y!}3zAyFDN}t^K``hF0AZ@^?@fhw>oHpLzg<;m47Gf>uuibU*
z;<bzCE7P$yqo%ChfBE5yw-29vr6_E6Owy#4W#=Egt8IAotQy07UB^x=+<p$e|J?BW
zD28c$drw}t`Q+cvs_N_Coy9O`kBGGFReLU6yZz!#<#i1EMHi7cy<p?MT_;apyKrMF
z#_$={ahQ3@rftiXZ$ES@3nO&y&QT-N7p*DF%UHcT1tWEYL+=4;GjlW2rfgn;VNSpB
zo%znuah-=!<A#_<Q&<}(4}&PYb7)-B+%W^pSSz(e>nfLr8zMr}u~v2hwTnchbr#uX
zVYap`c56E`2I{5s>^*$;<|F4PfO<Z@5uxz|7o2+d;5JfE<La+-Yd5s;?58(R7Gqc|
zagYyJ8#`*{>B@#zmoQA};UwZJ!-nMVy>#>PWeig(6k?9Zv(s4fuPX|6VpwafMylZ`
ze3OUvpU`pHL9CV6kPrz6uJCe*=scwaYvrm_x$-z1aaS*y->edh>5416=tz>IQt4Bt
zBJ~I-u9Jaukh??&c1P;D={)(N!32jtD5g88r<FT9i&+u={@$QoYq`5Y?qbxbT4rJ`
z+O$!qIjvipKtX$rqedxmi5Z@OwRUzfxG1Eq6VI<7X2NE>>OuqERE{2F-@n-@#xQrc
zh!8(_jYqp)Ypb3u#W261pg=!Qt-jaDo@3_aVVI}4r=Op<t4rH{@j?D67^d^{^$+y(
za%&qG8tT;kCZ_X=i3srZ^9%{~4~&ezi@62_2m1T?`Wa&)10s5sU^;)jF(v^1M}TkO
zz;PHB{)=0%S5TO@zkhJM=pnN}y+of7LvV=E=-qwjL{P6?xWBibA*OF?3R2H2v3rly
zft`IxP>;vcx$`+36E@y&<ZukLYKi^Jm$=~e=g)VSd$qBmza99YiT-N1Fw7dh9n{$L
z?duVVVUpVK-!^@T#jr(9KYd-qu)9BhUu%Um{QP|*`5)hNvBy7u|H%d`{`q?d{lHH@
zeY`V<*)07b&n_E~kH7Cr^XVlu65r0d@Wtn*rmt@|N425#vE_Qiqz>$86xoA*X(Ld&
zwz{$zYkl+9-A9!#-&NIAQwuQi{N7!AkDa@E`(eeK>bkm5H8mgKP;)Tu`of%brMpjF
zx_R&E%Xc-OzI^%A&`^6f3**n7Fn-RGP5Vz=y!qhStE!q$jZKY>O<!tDGB9F9m!!Vt
zWhLcD&RxIv^i_53r$(yDrm?a9T{^~235!S`oV9Sn_QPjy+<*3_y1Mp@smZ#rq1KFX
z!aRKQJ0_c!tlNI*?2QK?2ZE*G^mB~~6OVM1#SKXvl3TcD+rcx}(Q$44r_Vsz)K~|{
zGKng&XP1$47O&j0_te$DpS^}-IREnH>z7X^jLQ>f4Sj}8$y~U6WBIX5cb>d>@usG}
z&Qw49Q{7z*6KV-{T!#VUO!JqnD+TnwZ(h6iqNcXCx(WoW!7$QO;Tjx~FmguD;+31B
z$X&hi*I#!YzW-49;^oWg4;aSg^3-~-jzcD;FIc*6+y0XmFI~HK_R_W2mG|#ItgM1_
zoG<YVP3)I8E@#p5jXMq=U9t1R{-fugT)%K}_SL&@&<6-d53R2ue6)F9;mXZB_Y{<G
z+qS*@;KBWePF<-)=Uk26!_B$th-sM%maN*mV?)WJqLu54xBk9+-ycsvKp<$_+gX*=
zfAoy3{3WY4mTouATadqK+0HE`#~)xA2Nw|%sh3n8+-K~}?EEDw)|af@us$O@Yx&ZM
z4X1BG6yYoliQ`<QB69H98JY7J7p>Y*Y??S>ih2IdLsv0OhVy&~0v8E&@jV8OnP$$(
zFDzOyYV?paYU8G|^Au*y!BrH_6LOq~#daSsa?-4f+yyhb_USor@!H(dBNQeU;UX@M
zlelC;bZkoB!HL~xrKgP_&?#xsjI1r-J_U#CAY|cUoZ}uGlhD<#-RKF^(&D0$yY$QN
zXvTz6g7DXpECT1~e8OUrdh{KXHn>Z2@9tyAWMF(T?(WRzvw6}UQ62#iu?Z<XdQDFo
zk~S2ow}|A32nuH?g4M1Df1|-YIH^OA<T)70CrAm2i&P{}s`e6b`5b-Qz#NPqI2@&n
zOW-&mawTL^7k5z}W^2dh3b-b`1t}5<#Jm>ym^BMZ3Te$|F*jq7NDDJ_7i`>e`1Iem
zQ6d%f>k=Oq+o?<H_(f&cp1$6{zGMSRq)wp*Zx^r7c1b;kQOkGT_*nno#ObmENTd)K
z*8n%2Ur4*8UZb*B?YUXg)L7H-pezLv$wwhp>fD^%e4`S3jh(yh;O!bnq_3aOtxm^C
zH=)=;rErjHecC4XpP0A)_p?<fS3VvuF=HY%S2|2ARB|L*-<Yn0r{x#_e(d%~lqruk
zfvwx~aE(Ny<Zwh9@93^WXB2EY_}7DH^>F_AgbCxb_&fnAl5sc!l@}!Iw1SPhPv3m}
zs*0+{-a<2AaU~)t$>DH$3SCI2)bY7X*X=(2*Nr>ZE?<2Nsq~8zE|RlJ^Z|*JPjuHI
z<0qQe?b?5&ysUi7+1nV#@DP)PScE>nSL%X0bRRf!<cy3p>*nOopS$q_hA~Nk#ih?l
zu0-P%7T-H{eBVJs7i5ncI+8j7*K3(1mkR^}0U*eojZsb!6Jiqj=cJ`{>^yQa05EZh
zR?O$hL=-1jpmg!lBu57L2F0}v2yC004**P#LgOswxq<``9ZF}NzoVl{qw(_h_lw9t
z=R8qpcO}=E<WQu9D^O_M1!}oa<PfiOiJXqk1-MA;0p<~yNEwGGRq5I%i6neyNAIXH
zpg$9LBp3{Whs!x6NpZwnfmE&IlcYlBA2keJ&NbpVgU?Yx12=JgA(0`KB<ZHpMhu2L
zVlb2x!<Nr>?5Z%6zkrAej)*OZb8ra@gDhe)1sn#0%@B=nV6)*;KFk0HHx6lZbqNN)
zGdM~<lObZTwL&&26evJJjf9jd7494}#%RM}`D)lqoWXV$5^OWsLZNe!OOy_fLJX!(
z%jK~+;?ABd0+87xH^E6HkwFSE*lYolVlZT0EEYkCNWPnp%@aZj{lZ{N2!_ys#l%^T
zd^W-0bJ;mqi<S(wT*|>Irai}rDdccjts#Z1Tm8Z#P0SXAKtM2BS%Vu|rvGByrgck{
zK&@0Nt<p`UQ9ElEVuYiUi;G$zlSrJrV!97@a&}eE#kd-+tEY#Hqf#c8I2b!-p13gF
z!^=aRh4GwSJOh06&Kjj$DpPrNUi7%Bq4dHqk4%hk(RsoM;pOI}QYsuYo}K1DhU9sF
z#!HWq$K7Di8+{Erty-nlx*B6Ao&EyJQ>OO-Bf9AfNnU<_{@(6djmBB;8x%j_*N2cC
zUrbJrJZhaDCKZ1_f1`)92Bl`Fl%8W&{S6)WfdP))y}i7>eFOabee^C)PCEbi)YSg{
zhUe|Re)rTA6Be%b_VMupvHX0!+_X-vKJ5n&AK0%;k0H~uXLg6~tuyMOO8NN02aN8{
zPR{z6KEuZk=nx(io6s&og<<+2gUP6*uh+Y2HEw}%UHf*8_BI;yZZ2@Sr_s}31ZrQ(
zuMddl<m73HZXXlo<Kd=KDbeL#MlXW_MDRxf=(KKu!Eqr$;SpYXwS$g6_w)`jcpC%!
zeF4SO%{e$U8b+%Cqt035AqRXfFJE7S!7~ssNWDGXJbeNJ{QZ0a{5*8d3Us}}7!~Jj
z2nG${13q5v?mmIQ6&PXAYv^+?PamIfgTd%ef56wM_X&v!fU(r*t5#x|2ON9p^`3e!
z(81T_C-E^DgM58_13f*pAfCIYAw=)xVe|?L_NE!y`1^q=e0&1*9y&+J9lfVNrEg{Q
z@{aWbCDA8Z1{;07eWDDWTJQ~ojHh1jr8g$}8{ym=%<LN&Vl)K%dAOJ`T;XX9^wE3z
z>GeV0MsHtlldpY%kGIj^;NfJ(aCc8HKYtKSZwT~(!PA@aX%XNT>}zm$%fQ-rc!mZU
zy*#~)kwFH^X!5p=^z-v{hvaeh^fY)-dMochlfmB4U<iaBq|3&vS~*jmxQE_508DBy
zczGK1K3<v}%vS8~>E~nivWEkMmyf$J53{qExq16|QeG{LK0aPLNs9$oYa6bElbgG{
zn?}yrjE$ftTibt~Z2!xbVfxNX&nvI(P7j6w*XlnG?S|g|F=0NQA`%Li);}Ew-){QH
z*UPLCV|?ttA5Z?;^dDc1ud8?X*fbw6`p;APt9lH}ay<QCkDFc%6Vp<LHU9f~Q;rCo
z+u!>y$4&2K=<-qj?YOBa1D*fZ%bS{BgN7mh{kW+q0mCx?*Lfj^?fqZpe`46v|8)*S
z<j4PYUXNjQ|LeRC!z%yR`8y2z<A0t1fnf{(*ZD#Wi~nEeaS&K9|NC);E!f}u-_NHZ
z|FB;Dx8vswI()kSm*d8y=I|@}_w$9{qSxm4e>>jrJ^t;a+y6iKHs$x+YatC;`X6rk
zxGYB9`g?k`R)mf?{*$BXPECr|Fn>xx>o#KF0c$TjumAeBzVh;>k)aBv4ffA3D_e#{
z7Z4d0tXB{%eq5S10d02UiqYSjn*Z5`*-<xPgZXIsVE4ARuIgP?B_?`Y`})<(%F2r8
zFJ8TR{rb(j_wTDeeypzfSX+DdJ|=$u{Q09t_wL-fb?3q32Y2t?fB59-^U7DR-@UK;
z@X=IjT~kHnVpbG&3e#4<czEOLrSlj5x_I^0?K^iLJh=bx*^`QjmoMJFdHWuHytcNk
z?qf|Im4VrmQ0K2xcWthpr>G;CGgWct>Ys=9?cROx{JD#l|GIkp#?3o-?>%_<_~|p)
zs9wE+t3TG%)YjF1qUx|(3bQLYPgT5pL%nVB=2qpC>lC#gbFH|4@sC~GN=mluJaqEZ
z>9gl9T)GT6x9|LY@4=(T&yaE;MO8KM*Vj`GoKLm2)ZNNAAXUxd$2BiMRK2OZOYOk|
zUtT%3b^V$(#hZ3+J#ggc@e>FNM7m~?=n+Wt90Ym`d*H_!SSTAlfBD={SM$CSgu6#o
zx4c*Xp!VbYmv>9bFavdc-}<FXmK5#UziaoN{Ra*mK6?D*sWbmXs{r-hzH3JN^!YRO
zrN!sEs(X(b8qBr!RTXdRYpW{H|Bl(**uOI0w7{Wg$MzjN%XjVBfAG+eV<%{^E7xcx
zA3l0oQHgY<s#;cCu)chu8e4p>udn-9Ut{{zvZ}hSruy;QyVN$U#q)!|<<H5=rLz3<
zHg4Hkx_xK)?$QH?jvPOI?$VX(Alu)__#hpK_vY=p>JPB`)_($BztZb<1N^<Ap8DMC
z!>5K%?_btcQPdX9?#Sjs^Xv?2mgnp_>o;!P1h6}H>^*Yg>^FV?e(=!Zh?mHK@2Wn~
z4n+%jqq)iU%cl<?8mKR=KYwoc+yJY3)m>^cW_<bk>a3|#rcRzTDS!3qHS5-I*jTco
zY}aw5`T0u#d;1$|MI{YNi%QkoHXzjWCZh2(@=sIa*Uw+-Yd_V#KXQE&7PMo{{MqBi
zP8~IB($uxfSFBvQs<?E=mcz6`Pa}g~w*(&;^f^L(S5@`R005fR#A^HkJq--e__?9>
zQ%&uy^Tk+;&4p9OqzxTH4K|EfSZLmmm%VlS){^ae574M*EwWld5gEV|Ko-zXU%oVE
zH(50{W;EG<Zm6&S@Zm|xdMsp3)~G@K`}gbHXYlBeg9nTlH+#;irAxP$@7{X=!iaYK
zHITD8xM?qel;8c-IHwu$%jc@vI`H{gEVO8B|GvF?cJJ1$_rPx5dk&s5W7h0-`Ni9J
zqG&pD?Br>1)Rph~g3`_+BMk{=Y|LrGz)>w58@_z3dHb=FT8;UY<frxMmXe&5m@uqs
zVsg(B6DMcR+q`M()@|S<5cJpy8q^}^Lt4&PbTrjawRWI6fG$LU^>ww+K*p6=WXY^P
z$%&ofyTrxD#>FT09XW2!vZ6KXHf-FywRC6sp8bc9f-z}Ci{qa*YfkHJxxMqXF~6zh
z*UulSYTs8>Ajqj*I>xn+X&V(8)iE}q|ImyT;PO?g*KOETvTb|W?)?z-r_O+XXdxe3
zGLkl?MRBD0SCA46`LVA0<*n;0Fu%zO?V}^ZBSJ#jB}OOo8!$YhuxMFc*2>lEEn@CH
z2$10NE7!r8_d!U|8#%nHnyP78TT^2R`TV9<4PQRj)qSWvLQ%`G=+W&X!$SfCA_78V
zBNBV}>6=loxN!c6+!d?VY}mA=bmy*S)XQ`n(WrE`(@w9hf?`eU-Z-zR4GMMuep^@d
zq@)Oo85$oN<PQ_7kAKIAgl^q?^h%puupn(r(Xy5QK)(1-<hK^&D%1+<LEy&5`Aw}G
z0r-90$GVD2YAF`bvt6LC5zW6H+INZXoSf2S)V#cmS)=DIUAAKNdO+UsGcvgA(UWIT
zp<W`}QZ;R=-Xjwuk0GI3H`afw|5W$!0YERoVpC##40@fb&ZB*1R-cZYIwd4e8#=rv
zl~+h(Z!FpN1N$mq-*3i7xeB$f_WfJbFTi_Xk@-!nAWv)FzOAcxd435N*WTda>gMEP
zNE$tRYD!$Eln#lV5<8F0UsSlX=%3IRnxUcJS&9oX2<l|jmhuWVncvj%%g6fK4|Ua*
z)UQ~4xTl+w+Re#1B5BNk#OUalcI^{8bn3q#zu+hAJ<Zt7rRy6uN{WwF@9F;Z9o)X|
zBUHlo6;vVCDcY!2E0t<bU*n*`edF4;>lYQ>KCbiZc?${_FD+WWYF+W>t=r4@95{09
z)ESH57JI*->sNCY*Ffw0^vQxgugSi#?(O@RZ>f?(EY#moDU~YS-L;;5h71}udSs7?
z_6bR*IeCD-<R|pQ$4;I(cj+pm%Kd+$o1o@efBMuweX_BDxA;<1TmAAnwHOQY)5yeP
zslv%IGCp<S;Gx4tj*X0ppOTTCw_qWFFI$PgOCb>tA3b^c8+fx_-Zq0{byU4Qz|#p%
zHRd(hHhg^h1i-@$E@FW|Bz1By_U_#`b-<vZBSuXel|D2hJ9qxVMN5``i*ZQvqfm>W
z3PYAZu(Zn87SYYM)@UI3WJ}w{)R^03+gM$RByZ~>;c@vQiA>q9OV3`t`wbW}qGxnO
z&ra!Cx${xHG>0N2(4ix!EJ2C4<PV7cnhqCeaJ9AOdOHxk0kw7$lx5q-#=4g!i?L`2
zHHXcSOT;qI_>`{Qd-mx+q<2JULKH-N&b*&PrkoC$GvC6oS$0bqg?TEy-s;mQs=*53
zXEa(hHKjLNeW@+Pq8<4H4o4{zOCuAKlDqWi-M@E8a7cLKXmjR&_;ok!*9%~JI%=Li
z`_}Z4{B;>%8yepH1#8Uw3gUmMd9oObk>fa_l?p@-9XoVPNa@}ywb$@Lorg@DWy+ZI
z&9k)a%Ph7(_s#ZCEaqtrN`!CvWYy3>ea`!0_4&ghELy?g;w%yjuZf9`>y+HBXTO0%
zM~;~=btYx;>c7+gluXTq%YuxCfVwQO3<#h5+4jrlFLhJ_79+yhxR}l13%w$vVgpjT
z_UsEj9W#E)jM?cw8U3eVr;T#wTPnRq^CLZA)XfE{vCo+x`6ueL)u-x(SbHgf<3PaS
zi35BT4(iA*J^J(?Jbd)H$<t^3!{k5N<RY@kU1XDIv`t>ayj6qXsd}rry3G3YPd4@S
zFsGv6Y-p%kh{f|+4nRVXT(MXpSGxB}>E1haP}<0`lcvoy{T%c^%=4`rJhbTW=FPqP
z51v#$e*x1!(AdJi>+4MrMKD&^w5<PB4>MUqUEKn#gOJHq0vC(Tb61L`{?XuyUf_xm
z;EEX(ZLdW?*$aY*&R^6=?>?Y?@$%V=zk%%Wlc%phC9B$cQ>|5ft+~#oroIL`K&y|y
z4OOYW?(G7s6`w`;kQ`tWQObz6?PEJ8B_Zz&A3c7`^w~e`_s^d7qte~J|KtX2DECn5
zcv}U!Wz{mkk~QyZYTrZSg|0x=+JLGb7GNC-mfS&t<6MHn^^OaVY9F5%(4}|(!NW$4
zn}ovmpF!3f3C-~fQTFi3v%euMZr=VIxGG*$R+($9&^$xcu;0IVUsF~6whFbM4`|n^
zt)7S3$r!jpkPn5&5NwVrGBh$KE+HYgS3l&n@0LW7N2lX=-H_YRNb=y)wI`>V<M7^t
zipNi$Rhw!}H9T11-oJeLy1MG)+xKtZK(&EArmhb75=1!84=_S7viTgDUvNZpY{$sX
z|6$8-`Dv*y;5vFxLuqvO;+2~x&jaC=>$iX%rnVQ^HJmpuDqp;K4TgO4<~0OrHIUWT
z&cl*8Pz01dIPT!)>gwzlgp3&#+@p8D)M2B(homJ;eo_uvl*O87j~~1D2R(gVxpwmw
z82A3e7uDtuf|?H>UZaT~wllOVym;{v)+1o7$ipxWg?HjWTq-m!?p{6tp^@$4BK4uY
zB9c*HHYYM2D$VL`x8xcfD`3w%XCGWWe)`Pm^B4cRdh_P3+jsBXd;HE+ZS&y+wD70a
z&njL~m2F<UsH}X2rv8t$?{l#(D6xqY2}{RPJL^2X1HhyKdW~0tU%MeFy?=_CpVXv-
z28}1kiBPqlJva|CT)KJ<G=<&x;k$RHD*NiUj~+dFQt^~})&{hF2BQ&-vb8n27$%|c
zWSN(UqvK1RTs@3_L19sWDo3NATk6ko^G#6*)UBW>vh7LQwy2V$RCsXi&ok%$y7br8
zYu6z?Zr{28hI+?+`{cocCqVoZDt*P1iZ?Lo!L*c%;R4W;&(iRPA_p0=F6G`<u5{J=
z2M(cw$5P0eHMLYS6g^<x;}(zJpjCz3zjW&S+4C1*P6u_b-?;I_^p^4F@!t>rz7MBR
z>>fO!UfDpz)`FsZGoH-m3OQ_%)=h6zIEkf7SJ%YuP&#2ym;^=tCq0`7Y-C*8qZZ0*
zH-PfNh0}kYIdk^>#Y>kjU%7ep>6<syE6&6FcW>XhgDmmr@4FR`p1pZrReK}{!??gn
za0Fbr3T$ieiC0BMMkge9r>h&vk$<SU#<D1UTW2m@I=Fv#+3%(MZajQ+<@nL#f1Wyh
z7U_EN($kl(sF$24_ikUmc7vXz@4zJU^wsOCj}>z<D<LNxPh|7CQmxVn1yKmf%cQP7
zk)osip(v_67TZGTf=7Y$Z1KvqQ1`Z7esKN5$%99Z9y<ZFXU?8I_keoAgTT3d_3CBt
z@r^roZiDL|K70NCV-;{Z5b5}Ef+JSQd5+CN)UE^S)fPo3Paa`O(1OAr*4<IwOnMsn
z$l47Xx1GLw@%+Jk2M!+k<LL3@Cm@{eR#a9zt-N~a?74Fn&}x74#-;1m?>v0-_Cxht
z3{z&{!=ZcQxLTrcf&``Y>}=6<<k)_RQ$SBtfW8?QBA4Fmpyr)8y`hlqC)<zzd7-R)
z_rCoH4;}vF=+R@Ru0MNt;ljz&r%#?a4NitkzJ}J{$1fp<fKrr+4`Vw*^Cbj252KqG
zRH1KL4(}fsItk_Hchj!2EPg<VZg-sc4aVo?tJaq6{(U$6&t1Fr9N2&S__5<>F5Lg?
z&!flBA3X*-qkA@&u0j#M_3Q<N5HRvGA%NI8?xrwkg?b)8G$JOpW2fXEDe=*5y+hkW
z8KNTyDjyiO*_+Lj$2OuKy=+D4f$iJ2@B97teY?tc?b)+$|DivQoj!iTbci?%?gdj^
zxOC#=xqFW)U(<w=EIf&=RLC9mjxs(+?CKNVKF-5M@8ag+Z}jOjei|J;%~nO8gd&90
z0m8!@==QSX_r0+4Zv3ru>vsAE(yjyh4(&N;+Q;4xo;`Hr=!r8Ijvl{w?ODakcgUpT
zEIgUR5r~zt0I3LOF`+clOQrVE!baxPb=Wvak#9CdZiE74Aw+)M2>oS#aq)p|#Vc1A
z!{T1DWm_pw?kp=SFW<kL+C}W%vlmE@o;-8-*zr>p$fU@JS$HUm%@;|fYFy~5fVomC
zQ>q-DoL$}Hy3qx%*`&yeC~zQHX~y0A4=l~jU%U0-!Nt^a&C1p5))ynrty{O1mV&C~
z<vVul+yf@wcj)MuqX&<ixc#*9^*cnEjSqzyAeKY-VzGr<7;)rEwMOd_8E2{LGwDFJ
z5H3So_&376<?A+XD?Yw%alzUpMeA0sS__oLn>KAODcJ(FWx%|9`_8gm`;HyoyZ7MH
zGfyhM^RdLBpp#N6C&XMW4CXK;I=OTX8lE<C45~V&Z$3mp^Mi2Lp8VMx4sI_1S(eeV
ztX#Ev?fMNHii<avly2Howy9*x&TZQdK?d#Gwg2#$N6%ipZH^e26@5gmDknETK8M3|
zb(0{IdiUu$U<j0)3E!RgozddO9c8-;7Vkc^KQlWgZ+<}meYkwZs#UAktXa2?T2F4;
zwqaxWq5UU!?>V$%=k5cS9z1=46jWs5ofUi>_X_Cjs8q+qcsV<Ih6c6scXnytr#DJP
z7_VqAX6Mdp4pf?Q6BK|QknIo@3)6vf0h~fuftP`G#mbe`Dss)H&FglbEjzfaeE+u6
zvST;yKdF2TjH*l=^~u4c&Ld3e(63t*o8#ai(Mi?Lu|1*Q3`#>a4*ZA$=RYYehTXem
zwh1`rESoof;lhGNzzVcQ%T_O2wtVfD(>o4rF4?hX)0Q3AZvOq~`3qo_0V5w62|g(@
zy3XoufRRoqfDKU*(5)McSc8!#>CpK`iEOtLWgb|TR&#guOyJDOnwK*#Z{C9Zg<#;K
z#f61ym#toTWLH`F`i-UK>o@Pb1_kvw=xF80&cHFb2_GbolfEI*2HZ^~mhvHXWE#KD
zP~yOqBWYKnXl<rM8Hp&7Wl<z9m^OXpY|6ZJ;lk`Wxq0*E&0mn8zpQZK!opRDHyzrz
zdi|E|tBd#ib?x?p3e>0sTwv6s;~k_N6-(+MaaNKNL@0Og2<+B7b>NWU&9U<{VY6Ln
zC3h~FG-cZK8K%`a3v)BGva{#p%%7iUo=0pxcwlSMinYJ}wqiZZLAUNdeFkiVFax*d
z;$k!2UVuBnIH~U}<M1@Vr&6jrbm@~i$Pzd|@hxuVqcsG=ZAIx*r%X@ZzG{JaetLF#
z?)*7aHh0dPoSbdPk1j7PT3Nh)?bb8rFW<QL_<auDEUa`e0K{`0*|-ziQ4l8P@WF*r
zCkM5Ek6yh7L1+92A6?>*6W7p{1?tK&YNl!F{<VdL=2^2TlgXT(o{>Ri5`{;$FDxir
zzG3;+!>}z}zx#v&I$0KO#gmzETag4O2@~Fp5OU-~kwoF<pw?<bdJazePDh7Kesk!r
zKzAs?puo3vMedTamASL0O`kp^XV&c56qR8zo9FDzgSx(S?ebMH@?E@k_W{gOO3;lb
zHsMwTD--X=BKTr(95~Pc>RQB*?^IMeo!a>Y7S}CbiEN3uwv?{=ZF_-f!i>pNrXsc(
zGiT14H9IE<Z754N<}E)0ZRzTrhq;)QhMk4mLb5yXvhl7gwm<|#o-#z_I&i=bA~Q{d
zeD*UFa@*Ez3#N^oG=9RQNt35cnL2eE&@H4VpsiaMlpj8J^z4;8w}D3}%*L%1N&LBZ
zI236|zEFwUf?Ct3Uo*>;>5$(USr*gL`V^r8xdyd?O`BIuoib|r=<(ynjYmw>WMW1-
zN|fAvWt;XM-1*0uE4LoON+F$t+oZzMGzYaHj@VIyT7k;7Ywu=~2~(z_oXwhpjMiM6
zXclNTP=zO@4I4RP<fzf3$BZ3IjU&cqPn|i-l==I?^71`pYfl2lJPcE3;H~9soErqo
z8CM{nn;l1s!d$M5?b@Rk%>;QhdD;vV3E4RoeVVDjZs^&^jIl$8P98RF_y}MdMU7_V
zOoTKn+PxPh;?mv6PG7z<53>@(m}y7wHNkN@Gmdeya4&+*3zZ_GcJUS>NT{*nCr_O*
z%VId>pC3#}tA*4+`;jAt3>}&_Y#2417-1SaacXX9`A%3dmTx_J`trs3Fd$^$Hhc#^
z$$UJKU~3!&KoHg;83_9Jhm3_Ph&(g9S(Wb$7K-J4`=t&TJ#g%x!Gnh&mWiXs<?h^G
zR#LKU^Rm@{oWA&EK0QX-z&fIsi(7L@57_w#wn(BD>P8Mq=xkwu!biKtq6bZr|E-1t
z&y=CP`}9rim)gJofB^#s4jP=6Ij$6n#fD8=i}HUxeEQ4+7>~hLHW0-89NfprPr@fz
z1l!F~8Iu+pp3u2V_nv*A*decgv8G#!Hbj6$8JcEs{~lCNQJ>yG)VE*1)YJjvsbx?C
zi`Q-3l%Kcqk29x$LnKVcZG|wK3UlxP6(|H#s$Ag|l@{(A*wI1)t{*;XOtUIrF<?O^
zqm@}ap?jCE-MaS$B5to<z%*if_6BGsE7ugSoRhoek7FhI&?5*la1c0Kxe$*Nx%ej!
z);lR;J0+!b>j@z|XebzJEck=Alm#AXkw3a`_oU9rDJlKCcI(!?dygJHhYp=IVfmWk
zH7g32=a?7#e)RZ4%u2yC<8~r25?3+@N5wN%7(3h$6snDi>zI^+Jb{cv%a5#NVaTTK
zG&3IExKB*(mkcCbx(x2$t5?5K)7NY$T9H06BYoBWg9i%0&tM)2&Xq~CaVs__6K^jT
zkLfnr#}L>)u47^{x-Cj;fV?otLI8fCb(j<z8`mK|IjKi-Vq&juojWIY?Kz}>=GNs)
zW{=FtTv4|F&?3xACIIPWIL?;wp$-!;wj0^nwsU&8g-5q-7aI>=1{3w|KM*`Ha`ect
zY2(rcQL|=4MbF5Z(K)7F`{W@BgW?l=bu=Zgl2W?&?w7r`AU$=`l!aT$_m?lmFb7C6
zCkoOmANS;Q<vvN>_0gdbQ8BR{Iwe6a(6SF19A^kJ#`%QKoIV4(u*p2VW0#&o$Br8}
zoQmbd#dYWaQgrLpcf!1xgNDtVxo~TFSs_HG(1hEuKyW!H3nq>X+=?$S;ZY)A-@uUY
z$Y^966gy!20bSia^qvNzH`GyR^U#noC#UCSn#Olej%nAfefwBgGQ0HXKX5|#0W*eY
zEZ??u^sktek_8Qkg^O6+Jlsm|la5C!csd`S;IIf}l}^a7D02MW5VjGrY!d1R2%Itv
z?Hrj97uB|HbTlkkeY%h65}Puh|BReX+ZHXsthjt=K75#vtW>aiVj?rZ^D}Xsl2C}f
zgK5*yqIc`jt#{uDS2uT~=a|uB#*LpqH=G$WXN_(j85U=XU`4fyOCCI?OWW9_9z(NM
zY+5^bDQ2Z&n{j)kn&9d<N&vMIk{Ng;2PYhH7Plj^3kct#Q(|(LfWS~Et&6K$Xxi`*
zqehPfcreb?@dMij1t*7uhK5DNBzI0qh=`0!>N$PE+D$WyFe?tkp&g$?;wqBDZFmqd
zxp){~C1Hs}0%*D0$HgZk^$iR4)5k=|3?4LO=&<2X^2dxFKcPo>KukbDRA6A+h=>lc
z@g2iL<NHjVHDk%Typ(0&NkIl~FW}=2kU(G;jL5@-Spr<;=noPjbH{Z|NJ6*&dqK4t
zFmUjYv|&i-(Fr{#wfFH23HJ|(j7?}C7aABju2+}AdCQCXuK-XO3R|%?ELRd|;XK}a
zJP`J6q1rbfC^RB68mwT!1Sj_G*MGpE!AM%vGddfM{;{zU5rbz=?d=yZe9-V=GZ!x(
zvl6pnOEYnM*Dz-(u9QmV;lW~(6ypTn-`777f+DIJG8z2VvlopC1$Agz+OVD}okzq3
z`lU>sF*(Y2!1!^)C*}M)z5Oc8N&;%zN&9vT^&!(C@z^FD0}B5{Z%<sJ4AXiCh9KDX
zG;C*DRD_#K0}mNGx<|Otx99Y6v5AAmOd2_D?t;utYoN?Q9<=c5%2RR$?D@EcbO`cP
zt7AMoWk$b%ATTBpGcLYUBFJe0?1K=g0jzQQkUld;4j(>h*qBK(%yZ{-SqESsS1T7j
z&R4KWTmyNai%s-Vt6gBh^)mYW1PBr)c1}h`xF>?_(|5qk)bYcHQUmM<rj4G^&ormo
zdd!+oK?zQ91}`Z2Y-lJ#iAE&gLe-S1I2^7(>>uYH<>KVx>aK4F4~vLw8`B=d1@xrk
z6a?QRC24loeuMi1Mc)Bwqk2qC?^TRhw^qT>h!GS{aWQE=?!tHL<c+pew;xbQPzzS4
zgrrVgQaTRv@7=XuPiPtTy$7Uq9X+crXCr3KbpW7N(18R>lFP+;^KfUb*2REUP_3(*
zhXpM#7_uXhhKubG5h!!@9hR8Vvr7uq#Xg~1|FpCzJ$ah}N=9Vkn07vH<grANF=(r#
zL6At^zWxD0zEbbd@E|`Qf2mj^)kP$C?v#*_*fF+iw*kY(b>o+Sy&MQwGROklQ>zc6
zhoEKz4=80m9*(3?!Q;vJ0-;c=lIxRWp-x7JCU)yHaCjoI6)u-U0@$($+z|#GOi1Ad
z4(=e}ih2C+Sjdr37wK1*(VOW!p-?OqhDV1-`?ZTp>fU!yELn<K;|?5{Gf<t)!%_0{
z*c_gq8AIdb?CR=h!UZe<(U4L;pU2~e=oQAW=nkEG^bKrnw*zVttS`0%Tu(rER8lyb
z<l{s$2pWeSozM>(@CjUmbI?oFv@l}5UwBM>a<W(J9hjY+EhZ7M(&=d$tzIn%rAo*Z
zDa|<X9Y~>6DcU>e30Ve?qgSrkd@yoE`%Y16%yuVcYm1R87-f(ltmPsRv<@x~Xwa;g
z2m<W@Qn_=upFoKIj|6ACc^Q3!BijcsG23!<`7cUwI?f<ToQuocyj-QYTm%z$Yq*%p
z7m9e|I9@u=7IQ^98DHk=;bHI(4RmUap^IDCVweqA&N1Vy`M8rvB*tNd6VC%JKvS92
zB^{O?7d3hasda|%HwL)#ZEP^x-IzVPu$5fQMl$1&J=Q#&Sb$rz2z>@l@?=s8U*w=x
zJ8E3?o=STgAlrwvvBhj`a0hfd9`zWcua%t4$1!K1;W`P0PEx5%u5fU2a$wq^OAcTy
z(Is{yT(cY(h=kw<Tn_ssM3zV_mMP_IJ0#9wtd%YOL8d~?0Z^{G5X!2Azy$;gDZ=Fm
zr3!`}jd>JnjYNmfN@Zg3trcIj9B;!SgfgM6<r60{Ye1uHTHABvN->lJu;#CL8v?2V
zX=nM!S@;MBA8BDvO66i9xd;r#7l_3yJBmKHy@=TW24=Ep(~5xn5=(?Uk_`zAL~M>g
z3=5=~Y-O^1_Bv)|YfHgrTi7w#XtgvcTL37J!?ZKmQ0VYJM%kLNY@2lW?@KJ(wh8<3
z&(?pu8hGi&!-{*<%@((BSH7rt`UJfjSaEWH+18R(mRq&I?>=<w^tGFh9zT2e^zMDk
z?$OoKb&ID{Gq_V{&Ym@6mN|3o{KZA9H|^Ma^vspJkDpM_vF|Sj-aN8AYsldKsiOw<
zLq&ev#4!_06U?*nbJi4X*!Da6v9X8GU%afik6sQ;p^7M~zy|&M2-cdquxLU`azcmp
z9s5xUa;V0rU-nKNFl6}HNi#F&Eh*mg`{9!puit<E`t|b{PbthUrHCpj+&~rCY*;yA
zK6*DWvZ8cGm)N!uVIfiBQEj7R=tcnz4FUBXICSL1nVIu{UA=iHNO9-Ei+5FTURF{C
z6E>_^m07fDQ&Hin6&ofL(C-HRIwB%0I4~@@lV4D9NN9LC)XH}4QPY7Y)}zmWp`#|u
zFy|~<QCzzJ$f>{XK7I9}rs~zh;w5W~H?O8jTCU!_U_)kcVL?h67E2ZO2?+G}@re#I
z!1U(p2c0%JG#qsWm<SSKbniQO#P}(*v*s^e@mopxp;MP{KYmqJ_vz!S3#+%5Y~Q|h
z=gzGgelOj;cEyCE-?8}O0e-&TUY=0$bUJr;D16?iHvk~W2jV4l>E37HuyFvFw`ke=
zjoWs^>U;Ou8}wpNO~t-ld&>9iE8V_*Px<d9n>Q5|P}{JulHnoVUV5XOtFx;XDxr&;
z&fV1uihFQmOk5{=66lpWFm2?x$u#7;-*)dmc;e#iCm`bIuZ>^d-#tYA@ypSJCoY`X
zw{>Sp@e1^QU{GEMqu$-<?4(iabPf(G)W%#q{X(Of4cHAibx_)<2~%gM&t0&1`MTeB
z?ET}!xx0^ERM&lJq?*{DD=!{9ck%Mozi*#9cBpJ?@rnX!GZy@Euu<>k?5a^YgoJ5i
zQhIgw3=a*rOhTRLX|q?~fe4q%%3Zj0)%ud(_a8fZ{^sLXAL_rFn~27myVvhNdiL(^
z-K(dMY}v4!qBdb61+f6C(K{%#u7MJ<NFY+UdU>|<ryJzAAsLnv1Uroa6BMm3-dc9x
z_}Ra1J%0VMp)sS0_4#e(yXwzh-aNi_{?M+H4J(U^G1k}su%=2Omr69DM!A<nNx8cO
z>rvB$J`VC)=K3B0J9^>_)13K>maW;it^C08b2n~1dGqlzdiC(r=f<X{uXWFFo!MVn
zvTA<HdMvcJht|<SArp%oTs2a@LZ)`nYBY2=_C<3{R1D0nU|s|}X41^`x%s~?U%R<<
z*P#>VZ{2zFrsgaC@}a4zbz|MLYlru2D251Fi-n~)IXcLtVj*9m;;>19O081r9bxc*
z0m<7J#=^+x*q>k_?DLnbTL0UQJxBh$aN{9@or_?prdEyBkFOowTben6T8%}Nw0Ba`
zs5~z*o6VIwC=D)Qj-F0V&Tcxr!3QQ-3oa~XeFqL34dNnPu<d^Ic;fFTuRngx`G#Bd
z`23OW^FUhk-e9O2EFg5?lc>vzlq#KzT!v!7(aFWl!_(Uz0k@B*_xiqrhK-#JzzY#@
z`GFIEUc7ko@rw_ibDJ3G?be2ucaQGbS+cPRaHB#T<PssD%V85DHb<&3y2)h{F+CtS
zYF%}nMn4$2EdqD%-GA_KNU@9@h%tnVZZAEosQmD$F}LZL#-_&V7q<@YD%)JRl3Iba
z4O2=*0v?Y|2o-GDew7NTL?l-UMD#-l7o8pfN5;f<M3tZqRH*S&XJ_RWSb$F+JbLp%
z#aqaVJg@>p*o{B;m26&%-Xe?%fGG#|7%@Rg2mxTK#3G?UDB%gjQl*Lp_74t&?Wz+9
z+$(j^u+bBzQQ3I~D16IzZU5uIiF?oARM&lO%xh}%<<-M0Ck~X9maLecQiQb&m5TXX
zl7RU`%@ImOLZO4JfX`8gxqOjSfuO<201aDVGJ4uKWW-nqia85V2yZRl{@boShtJ%5
zRrR4Bz3f+a>%!54B|C~YPN0@zv5|7HFdLAalsX~g8sEu9B9y7bTrTeibVyj+_VJ0y
zuqqD#YfPSLo}0gT`C7=1(oLni4jw;u{qdXH1`xY-<IA&04wUa$0N6{g4q-9@k0fAf
z(xwg80W^>6#&xErFuJL$9Gz+Ka0E`jOEYZLgekK!a`FpTtlhM&ykz5+vb{%6UApt+
zT@5lytFI6L*te~;cw=D^fU5*tHiw1ty*os7bmWT_9B9&{f@=Yn(BOItI9kGc_ovN0
zJ0o}Dk`?PVZ`-tXeaW8PhfZ9$^5FTqy07z^S~gtVzqO=f@dWButfLc{gvDdw8V{XT
z&Es-Nwvb>eUB7|Le}K~)K6=$??4;>bX70jY0lc()`__`uoqLa*Jb&YUWmP?(+rQsa
zws~!F5mktZR3Z+6GjWlO?;>!Pa7iW1lmt)10d$IM5J}`pM=e4RrqRKj&}+v{o@vU;
zD_FX6{pM}u`%j!defGl5d$%v$eDng~A;j$)pOuyr7EvjHF9wq{nIZv;=OmQzMO-n#
z74w}r7JQ>X3_-4S^Dz2Z@VoTvH*m-Zz&Fo<Lb`JOZ>8n?|M>I#UpMYPc>3ttZ6JVv
z%x!AXaBjomLW)|9SxZR5#AI@CEe|3GKs6kK&7(Nh@OO&qEr7|6ezK1qc(;!S>sat-
zrO%nSsA$!O-%5AxKXUTi<?FZaKYDia^36xkrW+b_n(P{@GK=PeeXO_ukMnS`8U+l8
z<d8<Sj{Ju23jvSt-3`7$p;7G+KG=uGp9>Wm@VA%mJ$(H1g)2AjK6w21gUWXw>OP@2
zQL~#`G=6QkHvy?&!)D_Q6VnbCAqMoL!)$`$gmGyb{Rn(7pTN+_b{!H^5XJB@lV<?M
z;-#xLlx#2Cd+7M7^Pt1u51&@NeEq(<rvB5XhOZP<#+HqZU%!0*@~$LhF~$H#Fc>_7
z$rN&+jG4)39xPul%zq;iB4^XDwS-2*#3dv{83Uq8)6LlyqHQ~OA2|Bwxr<kB+_?`-
zFJ8T=`cP9-S4VxaZTS4Tp@FKmZm6#)#O#<jm&FiJOlt*~n@(bEifAbkak$M+k8XB4
zsz2e;v7I`1>(zfSq{DPm_Pj;Fvw7=|oqG-)1C6dCBJ?)zt2ghetExYItgim>p}HEq
zqEi1F{Voj_#b~AEg8Nk{D=5-ds8I40>CN5==3Bh)ZlGUBLeYy}nL*7zuVBe4`i-YD
z<eu|?UA=MpZ}cN370+M1e)Hz_+t)8DUz#dy-@dE)_<Rv8hfI!%Vb5gpIH;#kWD67s
z91dyX1afKH@xMDM0BW_xNkd1BoiZ~cH~-faYtf5-zwg?IA_2XyPQQlo_{lTsxz)30
z)V)@Z?%jLvwDR4@7gPbpY|Q}MF&IpyOw3cW<>_RKh3ubh(poY;8od>Xy3J^do0g+T
z#oJ23QGbA1=<T1Iw=F-x@#w+*dw2f2a{aICw{JhFc=ztvLJa$bVqgLjqXo-Z&BSr3
ziDb}J-^plS{cOT`FkxRXA+(@O$p2NVeqFqz%))l+-1$qFuUxx+<0dfPxqa)#)k`N&
zpS*neud8<}s({S44MWKgniv>PF>P?6=MO7#P0jv_u~-oz&<8~zdU1meb#$i&A}}jG
zD|hLyOMfdnaQN7tr%ofj%YXfK<;u0Ie_g(C?(m_*r%#_befj>ImlgS#Z7YVH!3UqI
zc}%84K(ftb80TN?*j#(U+J27?P%R^O&O);}Ju5qB-in>O_Z>QV488Yp_AGUdeg6Eh
zb4SY$9Xq)9&p-Y+aq9My*A)veyA}+FkziAd7Hk0zcjmD($u|EqqX%t9`Zcvc6d#}&
z%B|58N6$!~IcwT1M3%RubmOkQ2M!)O^2gER)CuzB$v;Ytp8Bn1%l49O`}Q0-dhT}R
z^97iF8-`RVVlaTsTI4`d?3N^fexM%y$>79ue^d+<A>TCXf;O8GU`J}`NL0IlZE5kc
z?Pa@m@7cHiz`=uu4<Fk9+p3a%+jgznynfBP4cqn}z4Yk$t9h7R%hpVVzcHO*P09$0
z-J2wvx&CQMOORNiA1ViWI}}VgaL|;#sXz#{=@~_Ha`Lxq-@fDbvhrQKcJD6Rx@hr|
zlHX3Q+_+-t(xSDcdr#h~cs37f@e7m5^)=uW!<Jyc96+(HNkVJ>*_<d)v~-|2IwRjf
zShhp`y3c@=9)rg88j%Xq#>8nesjSSbtis=@66>wowo#>=RSR<S<`=FjJy5i`V8OyA
z#ia)?-hVz1!x$8^walBr;B#5fng|oyiYGUdwltfW<JoLah!mtB#3iI0-DLar=$JA-
zse9j4^jhzvX)|WcrqX|1x1o3gwUNJR(~@-bglKVD?t;9$>^ZsXig%v4_P8PsGcj!m
z1%ppeEEDKPidn>W@BIiCwM9FKOoK0q78uY_)};&|(XIdBzJtJ#QzlNCN=>uMSZ-d!
zU%g`Pj2Sa$QAKmi=^5D+mA7d5=6z=#SLR}{Ei;9z&LV~j112>Vv~_4Ke4I=26eJxk
zuHV8%BKtNFT3!o7{u>D4IeT(i@6^F*W5<peH=dekYg+Yd(K4!tTQFhb#7P-brcRqN
zbM}nMIk^R^b{x6)Fc-9ICGp@hH3S>zD1y&sD{-79!rhvy<$tQEhPpzV^xHT$aLBMx
z<HrpEWycO3He#e{l+EZ-Qwyj?-2AB{M^2l7o(@c$JbBF2^tpu_cVB&!i?x6<iSro_
z6w{7J-wvZ#EudR-SUd?u+8|o<cP;54MKJ?b4T+$Kyl$44CdN%2o;oZwb>QG3LrrPo
zp+imc=H=z(m`98oGBj=Yh|y!l4Vh>%<*e9p`f)Cn$!rB(l!@%g6hnvO;XI0D{+oi5
zZ{s^S5$y$NeDBw@-_YTsN2m4gmfE{dYJbxJ$=Ct2=VWJPWlWlu)-Sa`dN?_JK>zV0
zrxmO{d~bUW_%Mr!iA=1P1Yy8kmC$_Bzv*SewJ?ER5G_uY4I~UPg~hdP8xauMZ`jCT
zJ(};-n|h1;Qf8BhnmuvGNc4k0z&@yNkA8zD&t0?o%Htd?o7s{Ola?@{faG6Pf`Tcd
zcO7?6Z<?u{ua;g<`V1O6b^v;Kk(|=S+*LGn=8PHBrkaQK>D{?AumcTj0cn{lOE2A+
z3kjFSY{8Zam~@Chx*?5h>5^iGHh_rqe6;#NA8>Q`f@KYeG>)(ndIa=K?K>@@ZA@&3
z_)ZClNlD#OM@^YLX`HEdAF2Z$-w{3J>>A%UrT_H8ji>(trZ(BkRt$!SiL(h>DWEcu
zI87xGek&%xr-FqD1!FT`XoN3)KSCGUebDT%P;_^`U2GiHLELT3gbDrnv`^?56CV?U
z?k99kj*0EmWqR(KqkmfzYpsMrf|FbbL@uowR1$$&%;BXstLDxVA#VYf4!B^i3s5+q
zH`{zWhR6H(2Zo15w2SK6HL*+TaMQ@7wz2)p5zNRawEDqtJcKeW+jj|wT4ckFz;Kny
zSxgm!DWurHkZdu@<|<Vz^h-Uyaq0LLe?i?sT!DHps)Je+-6tX_Bq2B;Has{aG%O;b
zU8lr;os+{t%^}RNa7c@eq2aN!(`FWyou3Oqk%r6ogh<Te6AU((2?_|whn(iKSiGMq
zGD<Hx;OP|`v3ZImGKI2JSPw0%#`-{mAG)g*7!(s87Zep37~t>c7mS`TK(q#hbssWi
zN`C28iwRr7c#5-ee=#2_vpq|pV4?fh6w6kiAdzkum!gASWZAoc&C}>26iK8JzL9a>
z8Yd6*I7IJd@b>ocG5PYM{Jo6^qY>Q#3yFvchzM(InvnO~mAP0BQv`ZAF!&k~2N>ZG
z&VoMz`j^vux1pNXOyopAW$}_~!~DbADO?;?jv5Vmuz;S5>P?>3UV(m;o)zTjWrP;k
zA<91}B5mB9;uCYRT&6Y7(wP|6gp6lmns7qsDEK+EED?xya=GOlN1cbeR`071_jZ)~
zDwTSb+EL?#ZkW5KyKw?tU0g%lK@gv|kw$}mSjS13YmUst<}%v|KzN=+EM;&RGDOqT
zohzbAU{z`HornJq9xX50+rvdHk+{k!1*W7_GIWz&<K*P#tI>oyYP8-iZXv<JAs%|4
z;Ep3qE052m%Mp$%8Ai5(!;u;}ID?^3P)u7KmpD68D356;HJ2vA4>}h&jn>oKC=iIm
z5~WmTl3OVhCS|$<>FVpC3JEes1{(u)IzvFp&{@ll0Z+?JCST5DFg$z`RV<col)GB$
zY;+IM^0}^p=Jx!9N7(Eo`eB&P!_(l#=L>`iF{nmKDOskJbZ~c<g-5u@xqA9(UG&i@
zgJ&$=gEA=-iYvt+30L<JJ}ae57?cY~Le6CK3<8R@Zys}g5D`29-7D3!0d*cBTpq=5
zAux(WQn5H)LW#MKB7s2d+s!r5MeE_KcGf4R44n4s7ND{RDjvmXiE{~ohirORFEo0a
znO072CbD&NJfM)EzHy0ZE(KVR)=H;_9OLo%3V}o@6qtnq2cbaVCU;Q}_H*}BX+69h
zoD2z_`%hlHubE70V*J9B62`zNPlhv}$Ab;inxht_H#>=<Gl}Lhalbz-qi^pz!UurN
z*I7&PO#+I~bCL0Q65Rl;kI})&-OEAiAD@&ud2tyK*#VJbI-|9Otz=3RB1ecnGt-(S
zB>shl;wqp>0FQ$9lr#Djg(N9zr*>EHco3QpzC1IR2savB6~N=+;G7hj(05Wnc@8vP
zYc4}%X0+imG;9V#*9it-3B_cxaon9ApDcd*hfY%3exMW>(8<Y}Px6(W+vx?a0vT7X
z7V*<LMB6|=C%MYW-PO$_vR$V>6X(5ve#^*YTDg&CMr%^VVmPu?ykIWF3_~e;&LsX1
zCMzyojQF(qkgFhlJT(eOfA8p!Krxq`!y(i@zB0t2@qxj(W1oqOsT?eeY0cnBOblCr
zh{49q3~LU9M=^1h7{>|R;omq+FtZ7oO{a2n3-gU~@%55(C=%0Bq&0^lpg1gv4-{Yr
zt%rv`C_Fm8&%{N!7G*fFb6MfcY=#v<nwVAsC=RWp!fzxQ76LPuO~3d^6F`zUDV#_N
zNRUOc<WiADp>p<@=>tNeI`o-T0Jy(oGGXQ6Lh^IwGOXBan1fVICWW_V{}4a(o9HY)
zUu1y>Y_+qJ6r@9|XOkQ+K0@~M(gyj5M8x%(vLp|i!?czsaB~^9Y$=mTQUKY;>xX}T
zHVH%ukH(b&u$x**I+A>D7D@1gXtY!Zd-{6$`gV!yJNY0&wPo}Hd2<=oY_S;f0B6H^
z*pg#GwpiNq4fYov9~CL7Or?f2Fq0T3on%S*VnFrx)9LgEL;JYY>99Gp&S18d5=>5j
zkij7NJTQbpVupHR{t1~jf(cODSe`RVU}Ym6=a4LCo=_yycp05sJ-njAI}Df#fHoOS
zJDlqn80%tYVB{QzHSUW%=Fa&U(ezDL6DOV93ZXi4Nm44#CRvV9Ze85;I!9-B_rUP@
zLDYN@G97g1DSgEG3@f<=RQHs^P}0hRg-n_W0c_%AaIJV6ifu!xNQX?4rQ{3sfv)Z<
zmDV*pC_G_k2_P{tm^OR{LpB$TNVuCBElG}$sqCjGf8hO)kD|r1=fI-D=CENO#yS4t
z5RJ2o%t7O7@DA@hU;$KLK!g&{&0*L&`ii(LsE%q0Kd1}L80;)`bAZPD259EAgv6Gx
zA>X+aX@(DR)IK4OYA2;csnK}^x9v78AIo6cf{a2z4ub`wn2f<^hy=30&X87Y7&Gm@
zyBh)!p_-5>Ni?d7Kt`5>Q<zqvb`(gI9-ex?=<X%?5b9t+KF%QI3!(Zrdc}3*vtUj}
zN`DjZ8=M)0w3f(3W;UkG0P$1`59e5=qg){M*1P%##r0l@S+!|xX4(lMaY)e|2FA~1
zwBfcLC?Y(?3Q!A_wr{o~4G6=IOFB%EigY&HQNiW%oxEc;e6g3(N#_&VF{QvFoS1>U
z582JmWc(uJ4+|Q^aTX&hfx;LEl?NoE6@sKAT){`<Fk1)(k_~e>`fYAJ2T@oUgt$`e
zq4$eOSVV`7os_{~NqJceD<+xAuqPbDrw6k<dGuJDVX5wr8JHN2N)kE6ZYl8+c#u35
zn`2_*94J=?XP#E1(7GA|!{ZjiG!D6;fu=|1Gg|UUsj*`Rj+8LNevbrY0346SVM%G@
zf>&BAg^n&7XI;2In{6WKH#eZjx_NkOR2q%WJ2<Kk@&&Y)fS1`>42+w>KyREeZ22BS
zq8T=W#3UIkOiF7{aau|^JOxJ<qXF;0j4lu<9m6F;wS%+X&)?x!48U*(pUKH%SV@iP
z3_BTH>&Pb3X(TggEoR|n7DbvYmb4|+Y`K7frGm-fsl=XgHcuqefvTQMFe^rD$XYNp
z!%B&#qb|>|l5sN`E#wRdDPlJZXC~XQq2RN4ETP5U6ojW#%p=5R0;H3A_$y)57OS1y
zy$!sjV7_#wy+VbPu7sSI$*|(HG8nDc3|s|H(i0%2J-Rg`CPXX~j)^JK3jGdB82w06
zD<cSJ6M?fyg;Wf-LBDa(#naEB2)t}&+B2A3l3+Lx6vKuDtXzg2*FnS}#S~z+AxQ;a
z!Dq>2VmZJ;7Lr1Nh?Ee782<3k&jNrxQoc;5(HY#91E7i72Hm;j^B4*UahP{7Jda_|
z<TIo!Hj)xL7DPEl%O)Zr*SIVeU&tbaQU^XjaUF#crL*2cvH}z~G11MLR)izNg<vol
zT+V!2M4tIO4k&2`Ag()$#nCv5g<4m!oJFYlVui-dpje4vt+JT*E}>3hhFl_^$7mxW
z2qA;TQNtEPWY8FMEQaC;71~Z3AzK4u93QHmNb2D1p;?Vt*+7xEll1BkY-FcH7iF0k
zm>80wqrrnAmb&2-4Fe5u4u}C>l=>+M(nZDRIB;NDR%+bb$Tgrz2AJ6=g)3+CS@Ri8
zLJ^=9DWkY7CR$60Oj?j!k`VV`OT9h#awiFgFP5vE-9+o?3~$YF<}!KUMTVNpW3+6S
zpkcAl4VDc0QkvY(Wngfyl!r!E^d!s4O}rknYQ;!rs+?fCQSew`Hon+Vz$c;fNR({!
zWAA=3E};^1UkHG*U@-<DsA+6P{~)D+Ya&~4VRl!$h>J0+*1tfU;NVv17|=&-xGo)Z
zCYCi^X(FH(5ZQ2bE4GHmj#B&a`Oq?3Kx0=riS0LHR`xJFwQ^v<Fi&tG%jba$HCos7
z=Bv?@foyh5Le0cw?O^0gCtIM0Wg2m-&6w3M&~d=tn)!?tBvTL`g5y75X2o=%2pbMp
z#iE!tQa2uWu_c?$1<R?WEq}wT?OOn|4a5ymz_8YM7%3b|0YR}S!k#5#u%Yj8Oi(^}
zEUDVewkN>kGL_iA1hcWnTEM8>5`{62Gt?X?E_{k%&t@q3Bo7Ta87wPwp9#}uGgvJ6
z3{%AhKZ;2txnL`1Yv01IRVx#?91u`>%466N3?+1O>i^jL>VT-S_WwIf!UWwlG)&&<
zgrU2;6e$HoRKTv8p#-tJ1G~Gs6J5Kz6B8AM-#G*Bvc2#7uKe@;on7QIbI$X5`aI{{
zGORL!?qlIZ1%$xY7DhB1V-vU!ZES!?h$|5+ULP^2go%i1ZwMv`I>;f$7Uf`Mxe><9
zP))_dQv=2{km*e<jX@5xwKFk5@Pw5Jfr#KqL<-E?R3MwBX$6&F=m=Q0rdNZCPi=&^
z4RPWa+SO3WhA_}9ZOsi0Ncw9K0^k;BKmfjip#nMe?5*Gl0W(<K*-&d}x@(VpM3!Kx
zGNHk$8(wr|X>CS8Z~&LM3Lye<!7&BoCmPknm})|`W1wEPR0k`IaX=87b+~ysm0(FV
zwzV{&sg3kaOf5}~3=kp-zYZbkm<IaR*U~6}Z8uON(M%xlc$%J-ttS|0=wM_>GX(<;
zj7?0<4QXTwk)*!~A?n8I!%VB^Ks5w;pJqliq>ckZa4M*HQ&U4zW11GC(F}=XfTq6%
zj6rY)`c!CLj3r1-1S?<z2V?43FvtTynb8et_J)QUBYk6<A%#k48)X2^Niv|24QRlq
z<<v<uqLn2TlzetJaGh;nL;+usDF#3tI}v?638(AYHU&7VsS|)b%<PP)mQ^&0fdQ35
z)rVw(=C&gg9c3a=odJacn8HHGm`5WKfL(N*_8}D515%`d!TP|_sg5=l(?O1<0ez7v
zx?TqnLr4ht9RmA7m}xYcfj${_0q%r@a7PdmA_0NKhz1mWG8hZKH5fxPF`xos`cyIj
z5Q9B|Bb`9Zb$t=o8L&VG{E^921B!-fMxjDn0}||S00tm%#7}?&1dPxP0b>jhL_@)V
z7@(*TiUARHYwL#)ujs-HQRA`t`Up}@!h#g7ug2qm7b3)aAZQUpOVA=3f*x4I{&HQ~
z@}G~^AUH5-xY9KaICr?2^%S9i%<tmtN_TOvcXoDCyXZSQy3k#n+}xe$w-H0Qqd^Dj
zFZh^Ou=;SWbkcW*t2B3K7dPiC2+kH%6HaP3M-BsYpgVeaxH_;LT^&3e7>@2>jmqSp
zJe2L>>@*5Nm}>BI5TUwyxH{`LNqFuq0FaJ#ba1dQw=)I{5m$yM1MVRDhXu+#9rU#b
zmTtpD)Xq4>42<1{U~F799%@e<+r!=6RqcW}tLZo}*xpWKYibA9`p#~iEL5xvh)e8R
zkg4*r(;)gxgzRNz=Ea1=^*mf%G@hm$5yQ(9P;hf~adC2@LwGy2t=i7o0iMocqGJD`
zsFduYK@~N*5*N0MyA#V!%^}#aP!5^R+=7I-ySjRMdNHJYHj4=;d3w0%DAC&}!A%pm
zmW7nOeS%}s^ZSh)J7?u4ji09-gUMzxI2?wvy%)=!xe=ked%5birdV7qhYiR9YVMGl
zb30prD-W<fgXc2%a^KMS%))`y)0b~Qa{lT#rI!=Klf~u&Rya<4u9@>XMBmfR3qr8i
z95reLD6*I=NYKTZP6yP~c5GX?E`u9NUTlHPKOrTnX!y7V8xNkp`{KiMHG{!og1Vi-
z<ZuD+PE7hL#MZ%`!C>-OY&KUY;q!SYB+Bs6QFPQe=m8Ejc3xm<>I~LQ0-0ZUa!!eM
z=JH()w_kj2X>DzYar0oHLZN`iVQ}~iHghF{@$^tL2^=<utMV4Bg(d<%%4VWE4ML%z
z)LJ_Wd!5jLq})FuC2w%m%=HH^J^k3CX*K>bh9^-)BxGd-2$>u{#{*?9MGzAW(~2eJ
za0NoCP$UwI1Y8yi)tMMjRnzqVRjs|bgQHGzj!^C&nby63?c7brZ#RCeXf-^Mo<DlU
z*$Wd3RUDMT=a?-)oEe@>s75ZR=L2LiADK|ZVar(1Z`#d6chovq+B?9-Gd$r67>!Kr
zK4AQUEvN5&XsK+az1e)LxwYlPi4nd$z88<T7%}$nXs0NYE963N0m@UOWHxXJz$l~%
z7)5|i)sCigCwMP2)MrF${(vzHwlzF#s&2)6{j6yvwH)sw;{l61*8?U!pkcTuPb3!r
zC4(bW1}wyC=hH<OUgeHvaO1$OjbM06UjLd!TN)mIu4%=<$Nv+X7DfQOdXX0*Zom=Q
z91fSq_ZJI90+fp?gi0a!4gI`baAzl{a=O~d1mpluU{a}Xcv5!1irE`a-1}JDifM)f
zKUYeDNm<VG5hG6)lvu#yL-zPwu2e0gih#vY$RqGfcQ>^Q&KYV`?PTGsODrH|@TIEI
zgsfuC)U`)$y>F>)rNFE7PWJHTdvQE$Y~~_H@B&JZSoi{g9I_`<sD)|~4bbK4LI@@Y
z7Z<g&t&<a==>qp;J-wJ*KsF?{ThF0WSM0m?8eT^BwdL#f9{xNYm&-I`&PH6>kXaNJ
z2*e7gOn51&NT?8tgg`&g?R9q2xL|;#Tr6GNww0MIR3PyQO6XcJxOV=wpB{gyYbCcl
zpV>tP(jLnlrO!ePJw0`s+%f?l{151>g{ESWSS}F@+c?(Ib#pCu(Q|cmS9_R&5N2`U
zndgwmv~I=a(^en7^B!I;b)zgw0;1cC$(#jpnim@y3aCIORHIfro<t@R3q=a41hD&^
zs*YVd*Y6wPAX%V%NH;7#qhRQ`rMoXY{W7MNG~1iUWwAY-Y-S=JFgtOfVwDPiV1FK8
zC6kNgGKH)IU1&C~tG$~W-0NiOZfk;@_CBFuF=;t{)sxp8z10Z#C4DXCFhO_JW(H!Z
zql*H%J~5HrZ~(BJQY4Y8Wd<DxtKD6IBHZ8>x>p;wx~q8=s-V!Qq^^a7YUXTexc3p#
zei_DQd3ZV5FsCCHAeW$kXQ3=UI6#Jq!~y^#luI=-Y6sFl85%dbyKZ|H-WVYgOJ#C}
zZ%A-xWNLa|AI-QWJI+4(3`i4Ba#<h_Y^NiZI$eOQr;z&#Bs{2WnNaBC11OU_QdYZT
z+}zaej=I|<_yVy^rhqq+goTGi#-(Nzl$B3izURVI9qp+M4%eM+GZnFNVd%^!l899T
z;1+zP0EUQM0*On%69=wb;qC-jhFkJtc)hHj?pBwm<h0Z-g#(9I%~-Yn;?vK%%em6L
zJebb(sffAG+4xdEk0*jp0td+usKiIDBMkn4t_)+O+}#D<*ug;sBAG(v7Z4m8rn_`F
zF(o6Xuxv!_%+>oZJo*So<Bu^owoK*}M4tgE1Jm(kY5_?o6id7zfCf;ev{TkiU>;g`
z-T4X*Pbh(>rr|Zyv2pPUNxI8Wdks+6&Rn(Ur+aUjb<c16JcR5#4bkIrP&Lm`%oqCU
z!cHs^`^rPa?X-n$w4t&!?k3Qe+fOvYQ|$@KDXHo-ll1hAF4^6C_ph8bXVtETThBj!
z1zDKT@}==O9|6n*0B$*tDr=)ImO$M|AbIL9o(;4DjA;z%!|QV4wN(+Z@RCtY7SdJS
z#V#wSNALb4YNs#ReCYh0=kLFKg->s7Y5w@~-laYO0`IJg7&Q+OsRbC_Bg{o2fl?q;
z>cr>(<Jkd5Eb|EnjY>%El9Q{+rsw2jXJvKE>0VSata8Hq)jQy;R-VJBM!tFd==P-(
zD_H=B3E~13sChV@FlsR((h3ln7GOAcFffqC-Th|_RX}KTQbtZ*UXCW)F()s#TUJ(f
zUSXes>e{Ib*K9v{@&bGS*Nv;^PafR9I&KO=1(Ki=F-VP4f$m}Kc$vyupp=xic~QGP
zyu3WM9wy(d0mO(&?vj@eFw{^G0MfN9yt<>`U~S#h`71VT-*fQDu|o%TZ`-tV?jYw$
zh#n8+tAW+^K$NRRS}|54mgo>D9Zlk?@i5gvz$;??ffSi}-ShKu0fu8vZeDiR%#6&g
zIuLcugc<W!EMK{L?aHMK=T9A5DVT)ddGKlX00S?R2^0dER;+E4;X43Sl-9%4OBV=I
zCA^a;Be#3^{DO9X+^!jE@MM2CcoF$fO?BO*iIb;Knlxc_`S3xZt`iW%ON|0^qgWm)
zlc@O^u}JeB;+K$sY7)0)2eDig7#^3J-K%#=QE@J?c6LrqRz^w^ybUA0OSk;O;*x>z
zqLAUkh72ez?%Bm<0)peB0;nhw4~5Yulm86S9vMIDNz$e#kl~@8x)c`|^(raqSq4G6
zWhBSPMn}i!?sV;%lV4ESv#3|kg6_H9a=Wmd$02wp7Ego<H7HRAtS*&_M88!PyG>Q?
za!BN=pzy@BqT=2~MLi4pLP4_95@RF7!{N<tQL*t!Y0#rHbJJ3jlhWb@?f}AzhX(OL
zVT$KSQJ$Yv3*1WkYmb6*K!|oR{6a$Wa&mhX1CZYR`t<~wN=t|ihYz0(2n-4iPDqRZ
zNHNjz5H(Z@AZQ#O@F5gBFHhhLy$U1v86{NO(dZyLJao~)0ny<do0tagJ^(<aLyAkX
zyQIWLh6eiis^BvoeZu|1;sQbg0|Wg1eEoct&dy^HJe$o|_=~kD4j4oz)c$Oe5XpC|
zGuk5%&XH`czqdR(4uItJ=+(P-|6YZKKwHsaL4GQwTqX^aN<j)v^Y#OEAw01!lR3ML
zK?qzh;RGtUKw*415$dFoe+Qxhopq2#4YN5w5=&Kqk#TXlhYNNu>{Zw!uWNE#M2Npi
zDU*ogQndgl<ohQ`H9~>}%sxU7_b~{9s`(^^A5fV@AQ5YXM3IOu5sQHb03fYy673*S
zJCIQB=MxeZ6C0nLmYJ0evsOk1lm|W=moMj`YA%)+!r^H+L^dB(Rqn1fbqK;y^KtMM
z6MT(8BP4+|3@R;wu!BH?Uj)*)dqEtI1oaQoJybS1wQG7tdRlTqLa@J&96l;n3i~pB
zSPX{7i^%ct^kld>xr|2ioq=S$2nni0+i(OTzCa|Awd2sF-v!c5-T=t8Z9+l$KFSz)
z$7^bGVq!vkOjLLfkVb%VKssf3an&9aQ1`gGb6}C`;^saYF<<~3rJtCu;));=Q2{Fi
zfw#E)k1V8Z!vSk6P=bYl7)gkUjfshh2n`PK^^<}Mi~+o#=i#Py#eupVl&?-$@C(2Y
z;A=euJUPfN624l12*KnFCt-nDDHi{1qqe-?2379P)7`TPM3RR_$3=!mM1+Th1PA*0
zD1AU>?&&GzFkyuZi+N`cN3epSyEu;qj!?;mIawnh@ueWsNca*EJl}=U1<!Y_Rk%}t
zFl-L2OTwdr!!^OyK>>cM0J)3<IB@66**f}8PL5#Vsd3Z;6@D#(uqyc&p0r#*Q1Mj~
z;rA4yZ8JTueVG9@<;hVheFJ@zV9xdn0>Q2eJs)pS+`_Wf)lJBEZ(k#8=y+ELHz&Gv
z4T5pvRr0Ys$TTnp49W6e8T=Q|((_R$)!r;0F+&m+;tPWs1iH6EE|I}ijw{e(8~d(w
zx^7Q{Lw9ip^qD&PLMaa#TchJ15w{_T+m!QjFp?E=g+l4==jSC!R%v~lyuFnQnaqz5
zD}5eY3~woFTSPj$)9IcrbQh-@1mRZm2`DOp)^6ipEzuJR<WNHn?ZMH$v;%p?M+Om;
z0m{HkA0?2+NeQnBlK99xxKasNWHNNC3Wx!J*e-NuX8_?*#V3flsD#52s|9+%KcN^n
zk)K>3s{n$SN!uLhJC3_MA0#8S0viw<5Uf_J6;4XIufGqdi@c>m6!m87?4{dZ0QzEZ
z?H!$60g7i8p8}s`E%Onez>%aNbCHEISk!^yPAZl_DE|&t(TT)^c~7ngiV0Nt#&|1%
zD*<uDVxf=JN8$}PM|9mF7WnaXgx3ZFC{`8U6qQN&P(5HlEf&duB?K@|a3EzuD6Py;
z+O~xI4kge?*|J#gAgDiuj~u>12LLHR+7m<3!Eyplr@%n&?kq6_UXulY&?-J6Q1kIZ
zevCkbYIp=GSZ9Dl1AxE^NumRy=_3ATAg)Gg6d>XUdMgwvMO$W<h}9x%z^Vjzi*9>{
z0dL^*WCO1NIM70zI=;Rf1&baq4Wm3QNSFetNUYK6%m$4A4gl?=603Y=9I?~~5LYV*
zI*maDB0vy}0fL%M1s>_aU@$>U+B-TqwgF&dU{cZJ3M6U(peK-lOkeq1@=PxVSf6k`
zl`^THOy;eXJL-soX#p%#Wc)V%*-V~012DpHb)@TPkJ2fLC<KZ?#XOOYegJS`oq9}v
zrVreU$xujwJz0KgDOL^)ua!GM^?=z^s1ga4z}H16Sb?)WVZH$=s!cald}CCBdh=vr
zzB~}>N2cWQ;G`mvi^Xjq#=n37^~&Y2Xptf^wX_n-*Cv2UBmgyrhm`5b2Bj0jp^ZAX
zl5Yv4Smo~zd`TW4SHkyd`bh*Ll}yu4-J~ORUDf3;QA-g;g|r;3gUlpSA0Ls-g$36B
z44_3G-J#8S0BKkxnhIoqZ9!aI5Q<8bz}r9y7iil_8|g@ch=6GXItEV-NMn70R<wXR
zqEgF@fy1&rg**-u<a)XTAP&+7hQq7iBN9*_a``-|w=_L50F<OsctxEw5E8F!CoX~+
zq>XVVL#2`6fOt|_rPN9R^dBIV`^52C0s)W1;-f4&oD}Q<aTX*l)1YKfJqHA<{Lw%j
zSEA+V2^6yLv<;;ozC<7x+Bp}MOY~GS-)gCapHeE7i&9W`1(-#FQaqhOLWN-qQYj#-
zDn|{Z91%|{lKKE^m-F<1t~ENU<!uv=P92bAxmJRcg@E;MxfBB|Ej14F@L<86LpG1g
zbb?f2f_4TI7zUMSPzqOq%7Cze(Ndskuu>B8!L;z5v_rcx+Gq=X!5Ul=5gZ`*mv~FP
zRZ=i%P<-9optjf?2@B4|7)SS7-Smi&^0g?L3&Ph2RR|+M4%F~S5>QBq<RWo9c?%u+
z@AQ3Ca=8yXD%@A5mFPSKXx$yv5l8u+a1wwu3&Ms|uSlUmu@Vu=FGux2+EnxOQ86ef
z_})6&Re&}OA*gE|Z8R(}nk5JnY9xpsn2O|TiII{o)RC5<ZJTwFG@}w04r9SGMFUo*
zARs}$s%WRyreqyCq1cDbmrJCz5=tBQT(DXJ`BnpHV7!362s|E;S0JuIF(B>nFe07?
zOoyNbfy63g`t4$ak!lpj6?%*1H4<a7w~iKvA?NAlJXcph%?YjM>nTFSqfvqo1h9Yy
zC?S4oK&TpUg@b8Qq!yCFIG$Xg6wAaSX%!%)BaSi|0uXk5Hy1~DSW;H=al9lH<=3GY
zA<!5W$O59ZQpXauE#cz8IG%hINTNod=m5t8PZ$DBwwtTl7#Kh0d;@4h6<9fNgc2a1
zOcy>fQMrz#3gC=wsR?)ukXJEEY@~#|fSL^^86MoeYbS&U>w(}1l@>*WfKHGQ&`}B7
z=~RJ*4xDL3zQCSJX_Xi+k^wS8HJb$MDvl>8Y5*A~s~pIL%Kc=OD5wcgM6TswL%{S2
zsf+?t)KZK@p%Lk+!1N|If>pFc#9;%6U`sh*h8zd9Qo+Xp&llC8dVJJRixT)8DNh)!
zQUe;5fW{~pMyyneKq(16z=?#xe2_CpAlTR_$hIEi5DXg_91GOStwC{0l^DKcjo>2{
z$ihW}HX=13p<<+xI+3AV;UnUqpvXo!Og6*A6F#Sl2}A%4Ncgp=u?&PD$mT+x#4lP1
zR!bsL1sGooG6x1MH$;fMQe-4lNL5NWYYD;R3TvndkOi%d2*<ky=%E573FSUP{veWN
zYCxe%GFqyqRLaysJ(0*4kdR9Kg}ws55L66&CJW3-6QC-!e7sZu<PeWS5lJOV<cCH{
z1wJx~29T%*LLdR<LOp>Plv^fJ1@H~A%3lZ;N?Z<;>E+6t_%jzAiMSfYaD;$_M4aLu
zFOUn%btI~#h(s&YlM0k-Ax0_F2x$U2U!@R91Ypt5VtRT_LNJ~vU_%5JSW-z9is6bY
zQ4&WOkrp6ONCA~fQ0JA40e65B@OnjvKrRq^L&pG>J{9D2SPO9BBHxug8G4z9k5>R|
z3%TO}GpN*G6~a@f0ikL!NgxD+1qun6GDs?ccc9G2+drV9jSebQ`pE<WSfhGOflPs*
z<H2|5@vBfoT!CW5;4ds!;K$bpD}ar`tR_H|TKJYfxkMtD$|HH+<?W=n5ugNszVGHd
z6=rh{9|uwc7e=AfryRve_&gCPV#`5wsUrgnu9f1%3ISY#B03^6R4Rd4h6m<R<}@G|
zuoWmC02|zL6cK1qj8agE;uTye5J?R<B7?{gi{$WqethXD2|+3q3c-rCTtEY>FmL$Y
zM0gI3$zV-yQxb`%ka79G0x2*WhR3f!iGXh<@Fzcwuu`PQ6G$|Cj6hZ^!HWaE1p>Ji
z5E2Wyr~p=_P~A-UC@ZdpPlRg<pgzDHR*Z7_qfrpJQn0qt0s%=ra4%6P7R%MZCkZmh
ztw5|181f+*0VtJtIz=&O0<OSHn+*4nflG=(jmHNQHLngNODV|n<$$SCU`c@$E9Aav
zfhmwpAO>0jG{9<%3n~>4#%w5=8k&u-kIJ|*-Mbt)V^Qc_VAY@%R*7oBOkwU1Wbjdd
zr6}B67D$0+fMVEl5Iq*0c!>dlN&#0SmTBAai&%r|aYTL<VDxCQK1fhXS)7lQ50+8-
zB7yEq2*^(JKrO(k;N!)?alRp7g2Hh58l9QQTojE6P=d+ASD@oyjM&86PtNBlK(_RT
zyCFcCOvZdDK1^(iRG>&O9U1TiDj%sd4i@ee!fH{i*jN$57pYW2?*MuGeh_0}TL2pH
zWMEk&;DAiR84Y|i!atx=P$?V*TpsV;MHL_xs>MXOoZtXX>k&O01{mZZ6T)qEFi!$i
zg5)9uw&RI_fw@vqw2zOZ0#K+0MFByiNDl@f*G7U5ROPi8(X;0O5^!q-%*5jHD3<3F
z;a?@F0*vYl{Uo9YF`x(r6GiZ}7RL)d6v_^I9Z|~TOBEcxXdH^;D<v|1wV)cf30|NA
zp9iZ2T3DUy3<4ki<N)Uk7=?&pEJ^^SPym>SMgcW~WGn*-fG?71csLo{6ITOZ-L%YM
zS(26^SfU+6Hv#i`!ilJ!NUG9+fv}4dcq*8x!8kFfqLmtv0gxR!qn9xP2I7ba7^w!~
z0k=u`s5cCMiEfigDCJ1X0Tsl%0@O7c)KCT|bX`Tk^)f}^XgraCH;2QCa5!*4Fp1-U
z2l$98`IQ2UkXM1~Nl`$~h=)pm4DEx>@Cb1^LLeaqR8udsO=OIe3zD)N90<hKe60W@
zhMSxm6c(u<0D)8T_+XU^qL#xjKnw^g5h4-6Q;6<NU>Q751eeMcD1igkM_j4A4z9%f
z#WJq27Nv@zicyX$ybc5*fq?`ADvnBrUICK;a3el&dIFCN7FlxMSUye}9K-|Dg9#T<
z1$u{d2FJpP3{Z)y5Pf)o4%x`c0|g4F@pNxc6)1e=BGg+d84pbm00M#_<nd$@xVJ^-
z^OzoH1R4cTMo6m=19*`Rg+?%ThW-H;#y)(hln-ht5pO)G#zYD*m<!?*C{zmuqiiR#
zA&mk#UxScI2pJ4EGB)vK!pN6vQBY#Qxnvw4!waZD@t{H!^7!Qf5|8U{Ndu_*8xbQC
zf<Tt_sSZqdmPrZ>LV#z4y!k*{J)WGaMR6P@pD(K5lUW`%CdNi>fEy7rs4J3zA<fVp
zE&#z8xa&NbPmrKs9_5L^(w@b%H#Rk-k^uxl+J+e5DP$vxk+Gu(<Pe8~DvCD&1iAzO
zR)gLQdt<7OK2YX1#GC{G^npb1&CG7_BrYFrsm$UVLW2QDj0rRn)c{c53Hl<iFPUmc
zGckAaVt~#dh`3XM5m1hoqp1m*Vgx$l$@>vg9a#hi=z_lXZVV>idLkcqA(QRyU}|Ej
zPcR@u77pvWk^x!J*U*@3VnughaG3Bsgodx-nWLb<^mMZ`F)=bWG$N8IRCqxT=@??s
zHVUDTX;gxtF|ddY-Py&(P2*t(3tM|TGb6Gwg#d<-5PbuLa28?03#@>4s0fKnrWqI+
z5ey9}6n%XP#n6yIFft(0$Rr9C_C<)KiwK2;fc|6y1H3+!3NQa5ku($vf>8DGcmv(#
zJ)~O*o&<ZfU*ZGQPee$hU#{|5f|TiAR_ptpysY;BxBuUMZaBIuA+PU9&<6L;2;rJ^
zBD76h`TA?kE$~f{ZSQ>rhpn%U&F(EF{{7(pu^A`Nx!Lk(FF$T=+B;m-@fFPo2BW8k
z`R#k1|LbPjW7n5I#s1zNP4j7I0fWWi2}F3&C{FKx=Rq%I)u+D&Z)>+l+Cg+Y$$&yL
zG&H19$@&DG-rqy~MKkDi?Vn+{wdUm#9FYw2yQQ^_jkT4z35`s|{r5Vc41D$vMgOJk
z4Kuc|b#!s_@bGYRq1#y+Q;5HPfABwSxT2^3F*<C!L)@6CK=+P%k&xwLYeL5VH!29c
z^k1Qa_H{*&4}1Ved_qEMw2#2u#*p|Qdx81V{}vs##xY4<@_UyK7+f(VC&3?eG$H+m
z3?%;Po38(1JEzr-nL20T=GAqB^P(iqCjXHJDD(dp;oI7Wt;@C^IC=Z}wuQBQ;-yYT
zgn#Y>j7$GBI%q9-&fa?V>GS;)YbW%L5!g}xu@lC;{Wsu2`veo$x5o49#`FkcnCt(;
zFdXphUxIIMEpJXQ8IdY={AWRcne@M#{x|L0^W*bMBUqMX+~31v7ysMnptZc%H#$RX
z{|^*^UGf9qTbr(}Q>RLu%nbfc0hk{leQV3hvnxiVDc!B8e;0$3e*}E%m;1+;4$n||
z*&6*#7A*bOT~F7(UfDf&aJq`+VDi@?822v+-p}prx=E!eDz>B9Uq%4q-%2~bZ`Z1N
z#e1{qe~C}I_`~2^TUTXADp-zYl)uQq3w|Jc>*tbS3B$qkFXT`Dk?^gpHv{<Yw#I*+
z04!eoSp3#mEGH{N(x3YO${!5h`X$WHltTEk_z(Nh@U5*!Ee-YmtQ5yz|MB>(+2lVJ
zzoZl3TW?eT%=(5u+WB;?rO%%Qe?@1&gB7a&AH&n$bPj$?;UD7<>Kypi)*FB5f4GO8
zh2IkWNBF6o1>f4b?T_&Hb{_s$hu_0H{+OcY4_nLN-{TMKJb17w{XPEq&cttV|1G?8
zC#oOd_x~+^@6Lp8ZQb)*{LP(<|H<Ij@Otk%7r({-*Z88&g>P*g@@xF!&c<K&Yy2Nt
zb^lSj`)mADosHjO^eg;NosIuZ*b&~bv*BA?Q#;}-J0HKSBYv08#~<Gje?aHs*LTDp
z+4=YzJK|65eEeU{K%Jg`T3b(c#9!L^_-Fo);~(sZKfCkscXh-c)%o}<I^qxSeEi8B
z@%wZ>etAdy$j-;l>xeJzeEh(U_=cSh|JAu8KGG>WKONh<U++Qe>TLWYzk)}`bT<B^
zU*mV;=0{uW@@ss@PKN*XYxhHtTb+x4>o@S7Jp)buEq-F>;{R?3j`F!P;otp!3wA?i
z;xGOkJd)9w_))*dH~4X<{eIUT{m~hK8J&k;{Ri*}+G#s*f3*Dy+JVl(U-5_VNK_}m
zw+Q|izq7Vr|8VymwuDZBZxQ_&K60Wn@K^mQJmTF6A@JGd&+(CEKOVp4FW?cIAASn%
z9_6p_k)A&oz9sB0;StR4AB#W#ui+8I{^gH^zeW2yd?fw{#=sZJ-@+q$6Mh_i@!wy0
zhryEf|0wu+(%)Z$jKS+W{Gh8Kj#(P~>6H(^sGne9BzpEE;IDc7lS`nnM6$7ESmVDB
ze_w8GLj79<V6a4riIrpWzkWLGNuZOB=|9Hz$o!WR@uNtue}oV8PcgQ1bmwONt9j^7
z9M6+({ZH`q2xKD*dpEW?<?g=+e<51R^>DN@0si;5_;`JqnXNNJpp0wyx8Qe2`-oVs
z_7+C}ApS5olFk9RQoqQx|4J%uo)Z-)=K=GZ(e(epwbvj47@FI;Fa^q>n3{M0JN&Z|
zaiKmUwwt}BG5Md|0fE862yk%ch*hESz0UoQ=q-nPB!>G*(SPWD?H)iiwWfP;CB9(^
zne#sUFYvD>WhF)U%XnVSwq}O^!1~`UKr^#(@<gS+Vey$INB?Ut_`0i4*Te{aIiKNT
zXKwV5w6AMe-~x~Vu0$0Qo0eO1<G(_0Ij7Cfj1TvN(%V@;_<wxo0bqb;YVGLG7AXUx
zlDhVo_Mhj1mdoP`yQRbg`-r)oPPXQT{|S7X4*(~GFYoXTjZMuioO0zqvck8sV|(SM
z$A|mN1uQp5Yg3v5;Xk|i2S$OBxh>tDEmZi2#ie#Ds5tPC=cLy=M)u4}PlyOqinyLY
z_r_Eb{y)9(27@I46<FFidvX8=y61Hk&u;h!n^f<QPwm}3D>W`WKq*GOTpX-S4GsPy
z-FHv{@B%A)XAibOrV5IPOYNFhJng{azc570{oNCb^0U$sqC))SLJmN;HZvrX{@Z)c
z^x!>Yrj~YecP1)Q_y$G9CZ%WP^`E}&V&m_a`1a!brpYDw*_kPEks*Fc36JIBY;Oh8
z@&Dy*U+o+iP>s#4;j5fbq0Gm>?FF@6vkSEomaN});3R1Kwydikr|yxHm64hl8yOm)
zl8bmOPZviU3wYNX{(rpn5BLFzY-nO(W3PK%xlpQ91q6q-y?`_&H8m|QH8mwUF(EEG
zGAua2*IOpya~ST<4z`x2hLr#LuD>=8^l$`yGEMi|5GPj;1{)QKByy#<%Gb}|KOn&0
zA3l^%DVK@`JPy;-&Dqh`%G?Bi6LJ5I*ZnCABm;_}v6+Rntv%ftzETf9p`F8pP4{#*
zPj^>mx`UmyrJ0E#)qq6!H*UH7Ef092KAB20GBGo^w6fN{JHSEr6nuL-TN`Uj3o{dA
zLn>JxVE=1^|BeUUEu|y_GDY{w7NfRiNTX3HWCMVV$6^0%x&MF*j2_?vhsWayx&{t#
zI4t&GywUdmP%F$ur3i*@3=SzhW9f9NK{dQI(5!pe;J!Wk^69Wo+i&7K@Vfx~X;D~^
zR|3p{=zefSB0M5)%<WTDq6>H8vh2Qni}b*d-o<4l@ge^7)U<Ru@hYN+SR-V_1|etX
zmiG0Fjg5r8Vc*}g_3@I9K60M@$KU^DBj@Fp<^ty6!wvf6mFD&a$Ab_AUzFRoqzpj_
z55VWrfn|NcG0_eRTLKsa$0$7R(RK_^51Zw*9jEI0B*q7TW5^3Fw;($Y91jD>teg&?
z_vmok#)Gbzh2$6KmlWpGp)O)e`gAWW%J0DL-+cWKTkoR&-|^t~I;FH%LIn8j31u%R
z35{<*o}Zf?lmL!h!SS)aW&ZGUcX0fof3GAzaLfRcj!E~D;H0+xddeXMi7DXN0vzWR
zmW3s@e?Fu*CpxBWtlq+2eInx9`y9(H4FEbroWb#}g8Z<^Hh!>3UVdN@)E78*DCi&B
zP9GayIv^qFJDni~0nu%Auq8dS!(+j*4LBZE(kDKqjc4q_{GyQfHu~5jeam9odB8p?
zE{cw9qlu&Cm+EBHeO^|O7}_=#CoTg<Y#WP9?_L-j*4}?u-y)qa0^V?oO8Unqwf8xj
zT@n=1MhEvczc{I#Cf+J9I}qxY1CD)=WK1@akMu!uz`wajG4c(eBk@QG;tziJMM}Uo
z-H}412z-(czRd@p6k@1IFYwtP_lwo_X*>R<M-ScTCvY@C`Qtuq2v6Jl^ubx;P@Dk#
zC~%QDZ=4h-L+Cga&KKu{3k09aaEhPD#&*DmSWkbZ(+}wnsKS29i2rz-{+_?!X-l#S
zbI*)x{MG0_zvDUoJ3k#YfRq9aeDBHbDF4x6yR3epaS^vI^E~C~FeK*h^oxIhe}caV
z{=JI7@k<Z<b^HzR-&Mp9@K&Vbs5kg2)bUZ;frE}R><2$(NH+NQF6h^%L%&}kb}t@p
z^UF6-6ZzHAAEOa1>z7Z_%jiq=BDw%wgg*a`mj6&o*h$#U*xlIe*d5qY2pzi*yC1t3
zdjz`{yR(C>{@g!)w!Kc?@6y6={z%vPND<ASW=nIS1=1X8ZnVf>=+P``JX$Et1AJrq
zv)wx4b(EzLNdy0W*Xkc*we?E^-xeYRbrO{7G+c}f{*C{)kH$IRc(}0Nc&!xr^7kHu
zAVMI)51|tn1Q~%x2#3et`6GCM?_}WLpkH}i?w?4I-=SAWIoUd|gT4w!572#AisYB&
z57J#e8_=ik;F7`~1!Z(Tic08yV5-WehZW~?SoG|oBD(IYQhG^#X@1Fo{5%c<=fSoV
z{Vu+3{<Jvr%V%X#2;%(;<gBy5e3srHK~|L_h^6qC&pbg+G@pkc+r+v3O9r&(d<^C^
zf|Pa_@Z0{P_=DVZtF`qV$XCRP2=Z-0YisjDuuQ23^lu=@J~&4?=jP>iN80X%#DEmt
zKBI$q8qVXoWeb?{b6aCt;}AZA{5<9V+<x;nj)=kh{C{gaLecRBiZcY$nu(awV^dSR
zeg8i-Ha4|24>3kC5!t=6^ZK&~h&X%>uXQ{E%MXmD8jP8AX1|f~u-E)6Imgd!7<%`Z
zZSTtmlWI3l-nHw_eb2rjG096kqn2Nk>=<_U4Lfhhg41~qKFu2zebs%}w9Wc_`n>}m
zqnjMVMkU%Fy;kNH!5fWvdfhLYOfK4gdc5b+U3Kq<7CWEK$ZueIynjw_aa<l3y7+Oj
z$|JF%{;1LLklS;P61qJvd*g9t-+TI=+|whk*nio+p^PwoXTSA>GRPCVer<jw8J8S$
za?{bS-7Rx2UDFOGQmxmHt6CXrmQaRkO|wV~-?Sh@;(unptMPDJufi5LuVdW?%(yi4
zew3PMReILsDce(0My>oZRkm*B=~Mjs+YDS9@TR9E>u?jtYtQ+O-my2kapj$1Wmm1V
z7T;D|(QP(bojG;Qv9Lmg9WPlBT9*|v(x)paY9;+eVq|jD!&$DYUY{_}+VI{rjWKBA
z^UJ#;eeWJ#lsgnhx;=e&iv_WS!FBF3YiGL4_TK$^4!o*fQagR~sI(IPP36;!v9EHy
zzVwJ>(#Z=)Eu3?CWXi6&Ss^F(vIxG!?B_-}Y~ie*#=K3dty~oUcH_#6!gS3z-`&Nz
zWAgN`2s|soAHF(0r#6iIP_dd%NqgroUp(OGonD5UE*WmRBbxd!-#xMALj8^9ma5)G
zbM8gGt#7J1y5iFGqzVRez?Mh;<MjI<6j#mJ+U>)xwX3)HxD_ur?lkt({ynpY;$4k3
zWziS<8U{$c*PpvL<pyzf+WC-e@j16E*Pg+=DH?p{CV_OyWB|8tiXLmbXJZp{-n{Fo
zc{5FvF;4ULe|R_EAA3|RKYg{!%O;zXE{so)x&3^ea7G%hOYo+f1U}+yO55XllydN;
zu=-jq@mi5^zHI**$Fc#Nf{!(xK6CCc^}Us^?GQ!xWNYs;aYkPUBvl5QPNN#noI1dn
zHD_Yb(C6pI&G*QZHl2ArxT~I#+sDDH21L~=E8~kVkyhJ3C>ebp<9c+})h9=Db}d=5
zqH-8%$QFn4h-8#u>r#7WmfN(R8>z05MZ;42ZS^a3n3ZwqX5+A}^F2?MO&?8p;d$Tq
zMv=)*j%z=8>6Vvbhcl-hsj$mN5bj9ks?W)nA6V^jUQzVyo6|J?Lwkns`{D&bb!qJJ
zO>3^&Op*xuUD-c%r+AqLu~|{q$D})!O)q*r!mjt;A*_p*zUGXOYj@5rGBVC>@LZsZ
zTPeF`dl1_y9ym$%&gIno`pZR!KP2#K-k-qNF*Y|XGv!X|X|X7f?phu`JP*Hi@I}2?
zhVP9g`%|Qix5kgd&AL4%{nAmiUzgl3J%`?j_0Ks~5zw?1cR$Va+}xvglS1ys?+0ak
zEoSbcZl&sehq32=I+Ze{VRhiU(?^>cHjO!snQ>=hJ==Wt(y9LB;iI%+XmH%Yq5i_X
z<DA2Cu%<RA2Hf1y)#@zH{F;^G`uW{Q)45MAd@trK=$nPF$?mgl;DT{A51O{%;xn@<
z3{NF{)OJZ&U{O%`W?%k`v7ZBl{V$w79z58U5wqTG--ai93LIp!zM6h8KUBpD@fmA{
zPFVPL#SP)>6;|UX6euU}dv~<U)+<5TqX&GDhGyBmd=_tUI!}DzR^mbS>5uPwU3P6q
zTeuV#Jw7zDqBZN{!yV2~8jh}N9$sN{uqN^0$eB%_lDqE^-#b*6V@hA$PpwBjQd4(*
zxlf&0;gsFZ)hvo_%tNj{!GE-jwXxXozPI$$sLS0S-f6O#abU`n-5Y0AJsUS|W8BL4
zxhd{7F|;o6^K<taKlW|%LB2hF6|>0wTg6xZ;Hi`Btvp}deW0-4wjyB0#=0(poO`>?
zdyuv*J$--Lu=`&RvX`A2);jphxeSL>W_4D>d-f)Mc=}Vl!3eL!;o(CaN7oF<-($lb
z#Lf9IWkur|lR?h+y1R^AYA}(28<Y{UM|`Mz-Q*#@Pv^0}OwFUtHDqpm5yeTcJuU1u
zPB?4s_DiolVzN)IDc>X%f9jr}AA0Uv!4@`Sg>a7b$-pUI$!7NJx=l7-e{ul*r=DM^
zxww@h_pIM4OSIxSPu{Wo=-t$VFPC*|?&Vv{W*wP!;bqQ{Ro#i#M{0JQ7OtSp9OeG{
z!L|{x_y*3?8nn;Z;~pGK(!qqy(_`FU_!`%`4H`CDzV_mdtAiIlF{n#MwTx41yEf-s
zyU7cCUioag*~{FIkq&IJ@q4dP`MC?3S9-2+HytH>zs$N`Lt|Y{Ub)0IZOFteL&LUg
zJ-MwB?cQI?RVJ6<x@?g@o{S@ncR4#gY}M%2@P>J_avHZj_ga0)qv%^=<NK3g_Z^y3
zeC`E1U8>W6dvxJ@ZX|lKZ)MG)J@Z%XDKp;lpwVY6vuCTt@)v1o=S)~RPX}~8H{#xQ
z(_Y?hkh@<)uyr9u2OGU+QB$g$m*ZbJZy7pOJ${(Y*y=4ANna;zN_uzv5J&E!7nj*5
zh@iLm+E0Wjr}uo_@^y~Ij%6E8t#|3;S>c!_{oL9$*{0_8wfr}C_RPnqlip@rCvK)p
zOj-AF?ahe68LIOEa~>x;<X;}R!#t^ZQPq^B*Sl%F;DQmi&fX&pKVUIq@~25|=0_&2
zQ)ET|gjwIPsP>k{&<BNu)x^&!7o+=oU0<NsIqJ(YladL>7JZ0@tb%8=UyP`|Vi4_X
zJ$1jsF~!*4UuM3FR$e~-v0iU!&~U}j(!%1|c7lFuuJl;f5*>4Y?VxM=k+{8~S&Pyi
z&9V<0E1og^wcXsSc4wAfv8{YqA9N-5(woi2g+{$<b9PX&r3Z?7i{;KW>o$(NdGO`Z
zyB{yC8@^eZIfc~c@Yk7J_5@yxjbE{yvdd|-e?}ilzem$|8BbW}dtm%mTWV9l!+i~e
z(&<FO^hKU2IWKZ4o->_p@7QvjR+EOkHJ`Pt*H7itmXWc$m|=N2iB^Y=_kEz>8K;RU
zt};1&%;aiG{dDnAyy#*c#?p=F)imVTz`l>dv=fK9M$i1zWn#{p9y{H~_Bxf=KY}xN
z*){VwrIo`I1}tcte*ILx>#01NpQ3uzxa;gwZfELea8xUg-WT%@;t3ZfR`92kjb47p
zZd-%z%;l3RX~!n?eYqq$`N)}IbY)$1L8~D1cHNcH>x-(x9ZuLKYo~r<HXVH0MQHc6
z*Z85`SsRDv7VMo+x~G-7W!3J(i~YRU&nR+?=@&5jGi%<BJE_~EmTXS6dmPa}!I1ob
zvVMLm2fy>EyW7BF_PZDNjK)m9Dtt{mXEhW%TpqXRh%`|iU~=>H(yOZ<R@UBl-|ZCF
z?;>Yn^A43^^0}x*q6MF@fftWQx=n5AK2ZDS<Kd52V(#mM(g%}pzLwgf?B1dg;fT+4
zj}`k??NFY<J~}mNXA>n&F~YNcMaq}Dty`aOjUm37zzKAx7{`9vY(|Ryx-v2K^^6`D
zozeyld38vhh<W5^KE3YD38Qms*E~KDnG=?6yd}2U49B;8BKT4magMy{_TiL*$6XT7
zG*pf|P+5J~4;fvTTDtLWMZe*9=UQ=hQ|C?Ppk5Os#!t&m9h>Sh{U*jk_|x<e7gLh*
z_6gXDt;G{tEuZrXcCB-pvu^MDfR%yiJFf0?&CeP9xOhrBo)ek%W|i{3xcKw@X@mSL
zj;`D7f5&9kfe^oXz9Q3qujDRvQ7`PAdoI<hxmDQ4ySuBd4p>t%{YigQH@{;~lTYjE
zTdkT~xGS3(c#dFv@<2qI?dlt6`wnq@z2|<??TRu%sy9E{O?*tabN;sHUR66<gWhkC
z81MSkJke*bI8W8zvOo6z6v-=t8GUXn_%eUQw&X2O`*9ULZ3<*tGJVq=ZKtK~o-lTV
z-uxFEkIU9*hd5susLGd|-s5p3b;EUP@Z%6im1soE_EDQ%2Ta*|Eo|zhFE5TBSbC4<
zQS@rivK~>D^5&n^O^Jp#Sv3QOdfwhPF{#y!rSi=heORWQK7YuOeAX9}JLmKFjcdg-
zS9{&qFp4{cPxz<|<uz(K>E@OJL#RFDGT!n&J@Ij<3)p>SvdC%t+3OSi0s@qS*BObN
z0;XRw+I{ry*}xI@dVU|@#~$o?bEkPIC*)j6^p_Lj@|jbL5$~P%Ru!K%t}mPtaemQ)
z=Ea+}V{+eQMX5-i2kuGEv=BF4vB1u`uQ`6+bLf|$+*q5|q`bHniXQh~9y?ZbK$3+T
z&3OLCJFj8I9zEWrHDRRY8+!6f!5_Lk88|+va<MbbZlUzZ*r%uy(JMFXV~NpypE6mo
zO~&x0cd&y+A1oth8IKvR)L2Ka&@(%35H0C?Heg&?zZ>Yc8L>T8seuL4(<>~CoPD2s
zn{=^mRqcoYW){Qk({rCsoIijtIod@jdt!94s%a~>a%HufXTarmP3y|Xuiv|^Pxj#H
zJ=RZoIDVbgz@sYF#o}{`o^!@@f4f4VPjN`!(LlWrSaHUZ8x~^ioxkf){lmpZ+sByp
zdETp)@ww#Yy@v`La-?D|PH*i8{>r%2NqbKQXByWo%ZVB8Ke7Dr#xyrxzugh111?!v
znoY$K<`ER87VqwarVr-Y8k^MjNZIp+nYm^5i{MGt2OfF&uF6Wg_Ox#K;jIJnc5EH8
za*NhZdwc5J!grxB;)1r$nc+Ho_6_GI^Qf~A`ZDs$S{`m+5j;IB&3)mtp;xC7dVS0A
z;J#f{bh076Zw3pU7H57awRP~xBV(s|Tq;{SHI`Hqu|8bzl8ftixzA0(_2vh|64Ewj
z_cP-pD*F#{$6seXt#9mZR8kkHsIG1jzn)wsuvyS!m8qfNnW7Y1d&Gpkp!=A!F`tiw
zx*GR6e8hP~<<+dMweMdqym6ZNaLz;3v*&l}r;c6!`c3(hccphz9-lh>kaC5Pu6N)6
zlIe%A`#EPeokZ71P;PHhOw4h7K`DOSe01obkcZrg!>0q+XPgNvRWEzLM_)fVHf!FI
zpyRz3k6zlmYAn%GWPYN{jHdeVeARoS5$i)Dy7FcvoU3r&@HphdDdnf*TWbcMPd2?|
zy~<w1ak_1ox?0rmo^@j8p)UUM+4UQHJeyrK|4hKaO-HwANMwO+=Hl!H+lH3vUl$D6
zJk)*#KQ-tbhIXLIswCso^aXd6BdIq}w;(+VcnKox>KUbLh9uvZcIeQld)?~4W%}DK
z`d~ZO#>Dq=%fQio3lH?1<yVB+-1Tt(J;moX=6$ef8rCh{W9caNmdwfnl7Zy|ye&4~
zx|O({JH;*H!?1fUy+e_)K@X3|pQT%V8LAyH-+SltBwnCa_GI!R&&A5!yUjn3-F5b~
zjl%C!X7?)J6Y}OmuIuXp_a^hgcZ;&E#+g_ix!MwS*~O%4f|KmP;0?!z7lyxVuK4<8
zWA>`XhTQXP;_{kmp7_F>rM*uS4%y^1E8S)Y)x;=a!p%EJLta$!?;bE+ez`F;;e*YZ
z)jPc#&lc_Ib-MR^_isPl(=NZ(z*&ELR>Isb&FA>ROST-nQgqvH?9TqTDQj{!u5-~`
zfA>7(@jR|V)<t>sT#w<IdkCU_Uek;pTH7UuroFqHw&Q;G$zjgRH-+ac@{GPbBg^e_
zYDn{eYa}ajzAt;&)d}k<b?Lj^>qkBar%rJ^yCMbct&LCW7ACB{sP+i1E}$^hkx#D>
z?y{O<i#av-wPmoJE0Hr^#B4F}M<-uix!p7@G$R_9IDvX_iOba)A)7Vvn!r}+OzlPz
zsbHhu=c8S|c`H6Vm_4CSkAvhp325W&$!Kfh`GzO;cSP?K8WXR{Zj7BMkBVL+%fH&M
z-z3dxzG3+D&H0(b!v<}jH`n(vx^%Vqv(0kR*5*?$6bo;hnKe_sKkJ#zV>Ugo_vb0Y
zry5y?7BR#F*M2R$-2BF6TwliLwB3uf?mdF*i+X)2so(2sR$I3JJZ{A6Zw-yxZMrxf
zrA}m0Pxkq?#~9bW&ggDx)V$pC<=c&uyiaA0PMbcsQDJ1G9XNf|;Zt{#dfnL>YPqZC
z^nNcw;ix6D71if^t@AfZz531PCM$~J`S9~fub<9O%bOUI<g_nIwEERsy)>Fcf7-77
z0&BA8?OK_G{kfoAmY}qU==^?JkYnG`M8}7BAJ-jR7kt(9@YH?tQU<>ZxI`GFn%g>b
z!{;YYj-~G%Fig0URNq>#bw=c#jc4R~u`|Yhqee7N-*$A!w<|uifwi8=ZwHkhnRrB4
z^<wbxuG^i%5=Eqm`0Mkg86@@SmgIY2O+LdpHe}97LEWoox7K^zn=6Tqi9LUBabNnb
zRf~dz%L5K3bpg}It`YY72OFR4JD4x3D?Tu2P?o^D-eYgBOXHyP=bshFy9{?}QBzlC
zPTEWH8*3RIa^a(+<Fcf=K`|z04jv*Um6&dPUVmntRd6gJY31cm%kvXWj(N>qeeBhP
zUHvlOxB5;t(yIy{-|)6--<ur~df5I8yPpcqq<p=<xQre3@XPK?gWRoc2F+jS*VHwk
z`Ci!2OWlxpG2K=Z*)iwL&N)-AW~$uxnb|cDqG@BQubtDpz1W+1W%+29JQ9;Xtg6*4
zDaD<i&zLpltCIMxwmM>A&9g3~ZTIgcZ@v3EWZSV9??|GlHbO$Y2I=u~nBJj6jU;o)
z0*6ET%d*lZUdjuYn)q~4w8gWhuYIyT>$O=YINv@l-Tvgty3-?q3nSjIT&w4uY&v}B
z<B{*4=*{WBV>tI}Yev%9CxhtvD-Movs0x~Upg*(r*jkl$v1!omq^^A(%q&Y5tr0z{
z$vd!Zh*5IjW52R@)kPxW<nV*~LFVpzgqvi$EH12gyHD=b<ltHLWj1@jf$8Isk27l%
zhDBfBT7(M^G@QC<-KZc+>&P|t4u83Y&mCd08f(~vvZ?Pxy_%8BuJyV%HS^j~`L;79
zvuZBg3Lkg<{Qlt7Yuk?dW}mO9>^?Qr@iR6tan767i$X?Sc{#-~Ik0fzl*97<W(D1v
z{VJC%np-Dwe-?hTkEY1F=eX%d%eTb7dvR$);fl;!x@&>Aj}7hyX4eu&JgSGcbUmL}
z#klEBxMh$~-DAN6izOJ;KmEn!%9=Gpvq+!!(;nbY-<o~#)dJHs*6*HJWAAELn%&*q
z+jPOK?T3z+^r)$0T`ha0v2=_`<L~^uW%AH9itCZjEE1k=n6}p3<qa}9bVLdLk-(>U
zQ}5{ymYznnchWX)9DK+jbj12w&kL$%{xrB$`C!D>*V;MF-Y29l9d`SEFjw|?wn1e#
z0WIQJ^fg|ej@{YCA<^*Dc)fv%4KX7&hul14bQtsMZMo}?O@x88`fZW%bGv@<C|^`O
z%jn*u5jT#14p_3X?BgLOmpAw71-;TeFG9EOFnjp5${?cW{-Y~5Y;i8AaE+OqAG&Vw
zv+XfTg?C-{jHp`|HL-~&?Iz5VgbO?x#(p>xzz}aQ8H{g8dOKw3j*$^@PY3k7>5A4L
zsA(J~KmDnGN6{M7^%;SiB*@lneXk`oyjbvQWH`Rt&Tal*zUX&VNsoI=j<XXu4HRBZ
z)D%_B^ootopN@|+LVLXKb&Ib)H+WI_?oaM(EXWaOw<}HGx!qb-sePzgTJ8NNs!>~V
z;%VZry_+8w_E~WD>XYaUg4+Dkfl1wa@)tK;nIT-)HM}ut=1Qb{;<G1rn3(c}Zm$z|
z&bQj#-0F2dwX9iwVdrh~8~l^8gY>?wv+SAipx_CS(VS>4+|bqZz}dq&>-m0~m)r~4
zb0_RMmKof%_0#!-P5jLp9MV}6ZjZcE{jBlU)0c*-LxZyp`jHkouAaE}skLWFL*-=h
zncX$#JYTR@ZhYvupStbB?GYnqjyu_JP{ZqC1&NJB=Pu84a6KNUvIZP_+HdRU56Yix
zZ)ZH*L>uCJ`Qoj$Zk`KEtM~V#BhT+g%<lJU*n*3d{HuP9mS7J?;E2&A^4Q4{t5$PL
z#m=&&n{6&ntMl1gvc~+|fo(q9$G<l_`uO(Us!^vtY#!U)aAv|iS)6h2E!i(zGnZkH
zCLOLD5O{Y+bk?hnk>=O=ZZpOw2Yj<i65Lq)rZQvY$m2A&OP9sBqu17+AjTW)jx3mQ
zb6HyasKEQX9EZI*z-aX=tXr$y6SXAGT4@v*YQ99gKsxQD!F7+>+guKqYVFSADYcER
zs+^RU()fkO(l;ihhK+-)3%pFzUyH-`H9o($(SMJ*MV%nw@v*$dK4&LABLytgOi$C>
zl6`0Vh+FIon}pu`SLPia{;lx%w2{lQu^RP?-QC`6>&RXfcjGs8U&l^TVo6gkVdj({
z>9#Bgt6iREf4sOX#ACe0#CaPJR}PyHzC^m(o{ChOV<jKM$;FK+t270kZ+DgQ-&hM0
z^bBcB?Mv4suD!N(T*|3|JI_v9$Z4+h)!&(ZF8|V+XP>4~d-O}VwtVjTr&A6D_&8E5
zoFDtS&Uleo-2CFGd77$M|Hey?*77}Ss}o<1_3~I4_T1ZXeU0x%^|0a7Pm<%}23#?s
zS<jnG3TIk5u6I+GGziBr=6rIwpqZUmzR4`$?9-k{Ha)#f8m#KSZFe`{$ErQ7%aYlP
zeGb07nL2#ue&Y4B$c?5I-OBE-cC!oC-@g8X^S4ozcKv+|24BkZ?adA<NGwY^5!P$3
z#s1#P>jR$M>BGx=x_9ox3ke%Vp6Y|6c(SJcs`q`uy4+pl+EjANWPH@M@n;qVbD~b|
z%dUL0zdmHDhkDC-K~DvzzwgQ0uA2xmqr5o$2j5nV8|>tA=WS%jjlBob!W%Ag&t$FW
zUp>i}bmURrB}EL2$<5y5%?HoxI;3m=_z`cM$UXRcVb0;(jWz1)7u_ji*)LN9dX}p^
z3d02(_FTSx(~`f>*1SZxBm3Rr<NfL3cIT(cjNUslCo>)P;GS8>({>A_9zlx=n)xHi
z)|tWc3!I0fRb_m@Y9Dn;Go1A#&*l8t;mc2-{QMw$(RfeUqqxB%m$ux#KBw!pMdwzQ
zC9HZ<QotMZZ1g6xWuI;u{4{gPt7Us5*Nd8tjC30(shSrayp|sD7JsawW`JN|&;CZ}
zIUaG*?xCSJk2Y^oO*o^(bX%QvQ=s{Ba=2w3&%V_$J;`d3u&U=u2fnRcRAo%T#$u)b
zb&J@Bb-ae`P7Ck-qTkEA2d=L1iqR~7b?cRI$-ZU!haGp&?)x^yikD5^J1lkev&!eC
z#`iKaS3g**S@AaHjnjUqOHOD`>XwzIK1IjI;4x;C+0i}i`cN!Sa0|rG7H>C9jZP^^
zSx3B`q7prwzxo5)WYzk;H2;9n!YQP&4cyJ1Zk4$^p17%*&ac?{pJxz0%--N49Qb+V
zxckR?*g4!ADNe6_CVsxh<H;N}|BEr<YjX>am*4AD;4x+X=ez?iZ<SOGr}W5tJ8r0C
zXv3i1e)&xzdyMa~WlG08(s$9A1iBlcDfesB<hLOk>Gy}ty#J~`*m{xv-IvC*LSu&A
zAA();!Sucdu_S7ZUN6zS&6~4~zF3^wv*F9CS#v)xYdn{tpT1)AyxP*UDyLH>4Ohd)
z9C-A&mfd&Pr1eW0w?6hx$l0JbzG2$_LD=3^6MgfHw5e*=-O6VzPP}LPZ0`#;+54nS
zzUXsk5HYa6+n8x<c?R8jJt;2EGPuPP6h7I%CGld*d4Y*>)to0Stc*KOI4AXzve35`
zs~dMu$TV_HxUNsUu6Ih$>)pwv-kY(=+ZkO3vbw*ar7Z6@-OOeN)zspe>%6RoiTY9d
zxMw0`OBY`!-e;68xmkJ8a1?8Oc5!~}mc5e)D!<Ng<Eau3C)Hbq+8?02_P+i2HF73#
z`;2}cZYpgLjyUsdJKy%qSmf)?arS{-sy3W^dIw*>u6*F0$z!K!d)1w}=bADsW8UU9
zXP+OrH^gLEnpy1rJu=>g&*QJ1AGor1%n*w;+oHI}Uuh}If)S*P=}lhS7k*tEzv;p5
z2~CZ{IbXdm$4`5?G4$|-So%V*wS7<4RcD;<WqyWB)N*f6)B6^Asn3|%cQejKVY#CG
zhN@{{ZX=E_O<U+IxK;G%$d;vI>t{bj`n%1UHEPY*9!<lSEV`X==bL6juTM`dEb$Yv
z$D6LPYmG{%Z*F+{y!qOzS;rPdT|aZVj`F(uFiGn8ro51wr^i^0E2`Njwjb<zORk=H
z;Kd5Z<Rb%LU!T~c$V#nFS$(-;PM2XxiIX$V-m<YPS~)VX-|GJT*PPuwV&kZLMu}n1
z`M3K&ocyui-RWs>*c%SkpYWMGr|8u1H{3$)O3u|AiS#Esd(JuSc=%96<Kah9`xhtS
z`{|DfzE!e$$EL6?gUrQqb0RLUKe}`){g$m^)8a?UfVcC@jF+5`=bI!p=3Vp~*=N?S
zWUqqJgzzPy+kzabUwUNw`7PXFn?lsAA@+Z>99xjCyoG(r9h7%aMc9=ej&2smJiQ#a
zx5xaKF*hZ|#Rn(W9!xtHxU=QXpby=BEk<~six_&Odd&X=5kT(0GoOJi+T#kdA|qkl
z{k7(Mj_KD?%PA3YP{zby<CYwO6*-YT?D6S&R;mpC&5u4KYN>jwmirpE>&@mWV=Y|=
zhrqBC!5rKSs~@H_Lm|8$T}ReF^Q;AAoXxurKJt_CiO1og(fDZuKMe`6&#HBe0Vzi7
z9Ayq4y?Kf?s*Xih3)lC;E}HfI(Dzu7zAjP>_uPg<>W+s^E)@qv?PxC*?-eCq-dmSR
zDSvSFw?DBj7~$5pM!c!T0x@zcXd-Y+{oNVm-BKTZP*gS(U9~!|hFL64ItgPCpZEpB
zNWW{sop1tu1!ly~GXH)DO(WbZY9KiW>QgWeNkB})z!Xenpz*O!giP3q$%R9VYOh=*
zd5p=7RJtyzr)5*--o55|>KL;N0R?_mw?OzX^zn?pDf4uBzKKre=+>=Z&mgy+RO7xl
zYEkJt%Z7$xssur3wG1|3(!RMfQsY&!KeWw>#)|Q2yGnAjNK@?k-cmi7IBOnBKf+xv
zgF%>7jGuo1cT+Ns-)(4#pw=5gU<VTj6lv-G2rEov&;cyuE2lwnGuRn}C?%t$uG~3F
zLjDacF_rGwkyPWz@Fpi3H{Y<K-xKH#{Io~c5{BzSNjp~v|8ELzP5O_v2Um&)k~ZpA
zC5itbwh<jjSQ;5L+rIhLxC||N(G!o^PunyC&rX$EDXy>ZJgNJb4ZtU0j}a2I`z#>O
zTQ!F`a6oNUtq~3PEWI65F?5s0L)&^XqkKXSMI1nR+(*4P=tu&BMKYYCJ?4i03%N4{
zuFKQ#ZtmM~2d~>6OqU<&92-BPS%fC{ouU_FW4u?LAc)^G*U3B(a`z1ZeS!-YV#zTz
zcPUWdw(;#@gckw27Pd7`a|1P1H0(<MNfV5kNjWiR`)I)2hNB6J0vC9Q`%=uHpC2Jh
z|6Wd6-f<y&Bm&kGk{l7g5~HD=eU!X-a-Kmj`VlwMh=j1;UkgClzUV&b_yAs3siCzM
zL`YxOw$kjJz?JjFGe39FHA*-vZ!E6IeXmf_UIVf#F?b1A-(lu0yV6>fM|%=PW9V|q
zClAj$r?2^NurW>t4o5a6jVs@QqgUBTrNAHHtx<6@+%F1$J<CJNPnf2ej8}bGVf;Um
zr%3MAST;x~ZgCe<m+e}e17XD@b)LN!1H?jRJATE?84lEJ%~rO)JKWSgb_IpFfjA#!
zFoUa(B&>p33Bg58f!7olm8@R0H42q<lr%F0B<-JAP>{pz_HBpT?9(5IxWMOUKt%ih
z2=p`vnxUbUO5$P-+L*(TZ7Tkm_}&y}So8)<^+tR1#@AL9wGc`>;r|7S1g2}tcv!QQ
zCnx1yPT`i)4EV`F_czO$cHs9~#|qJ6L?qr=kPW%FP*q^2=<@q?m}c`t*CgjIS|{Z&
zGYr8pj^e!VYiox34oa=*_Ekdem>(jy+po95;ahZy#1x5k7roY%!97I=k<4#agmM}n
zv+XwUbGC+|2k!R=bz8^1G9Lv<v1}g(uxL9ZH;mXJK>Dx%yiH^DRhk@E;Mf;$Mf4g_
z-?0;yrmbl79}i|b$!CBrs{FHk+2mQEA5;qLq)oy8J{uHHSZl_rskCPFo!7m?3Z7OD
zKR#^w)J8z3xKdfFUP!YIv)c#Vhb#yiZ<f3mKBkU}J$$s`>dF9!{uiB73tjM6cij~g
z7MQ9Cv(7qpXTyglkP4^Y%6VeB*w($>rj8IddqGWW&Wu59U``;!=K6BY)UoOF5;%Jt
zI!M+gjI-2;Y?rh3(3J*b?%w+X#zxF^bf2~DCt(0e<XXIXEk)??HQlnCd(wu-Ssphi
zcwyiP!A)z?&pJbsrH#L&hJXIVBDC0zK59<;7m_YF$xJLTNIu)-F?UTdB@9dNSDI8X
zSCu}>GweNIjvFX~y_JpotbPwMGc-Xi8Y8UrqfPlerdnClsm?}f-XHPkd59wem#0ia
z7<m#Hu{U)Y5RslH!Rae$!)pa!xKn=1IKSLhAAL_K8(t-qB(PgtoLB6jH*Y1?!}ik&
za8ioVD=$@StPt^x^?{WT1hw3w5_yq0$MYLrUR{+yih>qQM9`XDA}#J1qXui_vD=79
zb<HczyYSmz>a2yYq25j#%uBvtUz~t#XX4=sUYO$i{}yT){|6@7L|;6r=RX{vIfTpE
zh>@d|3+-EHSu?xGHn(@`!Ugzw)olbuUNG%`{}Q|G1!up!*aU1rl~V|+rvQel<G^c=
zZDCX;v_U9|YL)g?>M>2*wOrHEfAr&GY<X51R8lXO$L<$4E}Cg!=mOfLPz6!3HIIz{
z2UwK&s5G{;KaUa6zh1L(g@k?alxmORM=jRSn*Tj<Bz4OyHnKm=DmVqo4mM+X^N!W&
zBY$$}mbs@<D+>a%KkE(MGk(ry2Ej}=HdePzVFkBXJK{IaLS~`3{A_dU^{hjjH{C-*
z{{KXAUNOD*+z?vz(yjEO>nCpjm=N^M$3w{1lT;L2Z&K=4y-tR<vLZCn!I_TB+$Qqf
zPTXawwP!5(9&Tr~m-r4%f(*IQM1v=Z{0Cw7=BGjI73_Ci*EqCMC4cL)p$;pb=XVjB
z8|ckK6IWH+GPfs&<t<az`99~R-US#G$IMPv^|li4e2ylh5mmCimW`0B3NJ?KrV5LF
zv}jZUlg6ETj!~+<6(88bShBXpHn?eKbZBWDge-M=y*|OF%U3B0TuKXG^hpg|5E|Nc
z!S0P6#bOBB+;yLuAizbPc?=}|3iBoxANbS<tf<sN%<UqXqJL=%J|WUDF4F<&e5apL
zAvm*z;=#DXblEXU59GV0mC7$EPImbRh`maN=?V`fRKcc%WQLXKJYc&{Mr-LyrD*PO
z<7cjML{)15ivJi-$gF1WXE?kMR6yg{oB633sC&vP&+zMP&0fX-BioxTA<MKLOCyJ>
z*cR=4z&!t7@LWq%eBAkV#h~)zjdyMIMJa|7G;<D#|4k{kZ&`Bq@PuvzYmbe3Z94Wq
z7(f7524A(n;NgAS8BVh`Moln_df&;VZb)hH%U$So&nT!SZg$M9+Ct?TDbU`w|0%a!
za2To9Pc-iaxgK6`f_oo%td*o&ye*#X7Ji98o)qz>hbnkF9#;xG0_9-^rl0sV?-{KJ
z7^68fd(tYLVzGr?bM5(j5@%XzqO1h7(x$C;YYK(F*~=P&P}I5a$#lDgFP$QqL4I55
zqpeQLMyzD)Pv&yvb6bPjRPuQ!a~3A3G+txHcEdQ&!6`7@zwVm>%ql!HoDC|jed0ci
z-{j7+Qx33o;{NXV%V)HySt#4lmqH9)^a!b7Xq3^m5D%*LU6VSE9{Zp0!WT5RddYmm
zAp|qFUD|1HlEW6Mye4D=jD@b0vy%3MeC{R>7<}|djyx0OwZ(&<+`g?wxjL##k0%`Q
z@6Ie{E3q^a1tT~nDrWm#=nRMiy|L~Y50O)W<+wUZeUR%C<|}10TLaWakeov}Ny$Sa
zVzPHhw`sv2mcNS}w7awLZgqv|GrwQgZ{L1s@G8+9g5&f5D*9+UGv_+z2LWu+9LtR1
z(qOEJdwf8|wo95kU!;wUpW(M(AY0_66Z?wr4`{+-7-bvqoUdt()LM!Hy|Jc&XTZ54
zmHEE@s_kP^PdM}s1o2P2km3;>`_Xp)IEi%~7mpWoIazN8k9(K$k2$E+u?b#54#^^s
z46OQx@Okld^hRJZXZ3T<10<bprfS1+eo;>H4u)=bTqesupWE|KUB-g_9Kjr4UOulz
zEs<7?$QY!A<JRCq-Q7b)bOHf4f?3qjCgbM`Wvssge*u~++^AzF$wLAhf2<hw>msLf
zktCi5S9iFdUX?kkLz`Xz#B#xJrJnKZiuYJ0^*<H*?NH#FkC-a$nwjc4p}UYOLgm*1
zW*ZJnd5`O|;d+Z&5mD_Y<=jcXC^F@H&BeVmN)`u%G`K+7ANW|9c+W(&N+v@|@=gcu
zZ&gWAWs+XKy@ot1T%Ho*c8ld0$4yg4JO<iQ1ls5Ue9^00ZkXQTYkIlPSMo|c$ncRQ
zn@j{ZmSfw;DuDf3g{>na02K^Z=!z>Z4v&I-&S3jnANvmoQS;n)5eG<fx#<-7{803&
zM-k!anPX_l<LxEb4K$rK7u$2$f005WNXq!LOaA$hkga4>0ThZmuoalOZMzC5W`PCP
z*twy!Bkxpq$}ENcQZGpV7C~L7a)7{BH$ugk-td<GuWPqnq^8m*?~-G?T_m$*oImW_
zsf&s2l+<Dd^}0QoK1A1y>w=PdOeSmSn<#3sXjKs%#1gg8h@xt{(w@bJkWSka%+az=
z8d5(Xau+j(exAkg!<|&pfC}3TPU$8MC~B$n(OL+0uLgH<)O8Gl3&2qL6uqO$gx-NE
z)k6td3=<~g1gU-J*t}e4hhv34ZR>J7lu{Z5Sr1I|r_B-!lF^hVxj*;g*b!?RANeeC
z6TTC`0z?ZbaO2V!M4d7ic@UP<`d9#y2m4JPmPigKATmL%P4&iLs~eZ9mH6n5N|sZl
z23%Y%<^-QJUUqQ#DO)>kM8q68xjCKZX>Hb6!I?+1+()M-+(T#`{ja|8h5pjI<-VM~
zT2?6*ksXjSqd`Y;(m;S)%Qf+NN@5wV4yRmc{fjNfEBHp7pXbUG`?wJR#m`N?3mA(k
z#sQh@Fe@KixLxqR;#gY^d}mo0@vwXDQftK*OI?`?blj(>WMasK8H1vfX!F{y3=J=W
zYeVZXa4v+6h&;PyJlO<r(^c$4U&bu=o`EQK`Uz^P@Zt*wO1cLox!5`4K`Snu<#nKf
zkxjmx#AHphTkg??I374(C@^=9P5hx9KN+Vi)=IZnu;{^pjK$f{Z8rHJ2WCctIK1-@
zTIe7_V-uY|={iBg7km6`JU^ba*Z>ZxYhX}&9D*}#&YS0>TT6X8eYBqosVKh}KimMD
z*o;w+ZT}Dn_YrD0L<cW|*(y?*N!azXtHz(@sWJh6Ob~Kgm=WSKlM$&_;f(DV!)Y1x
z-MDOYNgCu&Zx-v}>@R-}EV0IxFJC64r}K0=nhzxH3lj+-5Z@&`!R0VdB%2p>Of!$O
zBIutq-8Z9jw8}5euj?dC#UcrW@bm7eUO^{mPdn`LbVaiH^NE#GYC1S#>$)K!vTM)z
zSkFFaetotU3E*uF_kzRAIR4I(6Yf9BU#Y0Im~D)h(kE8JzuB6q+|C5=2SAt21?rCT
zi8xUHqDQ)e)%AGNPZ!fABuoenGG)UHtM4ktkQ+yRr4;DWeZ*rIflO`&PhU|QN1Ktj
z^3fi6?S@UJ1PEP<FTB$!Bel2tG18F)+B~X4Gmj;2_r@bnBpoULbB6#RD3G4Pkx(X`
zH`Ntw8&p-p06^Fv>nHKt|7vBghITtR$(8sn*0EtVJm^XSnt}g#U>A~>a~3ywg91>j
zXTjKU!92)UD(U(D=11#GNm3VP9=tt;XTJdM5){d*tR2QzOEkpNO>_T9y~@%Zi*m_|
z#*dHlqA%b8iz*%%5ip{8vU+y(S==n~^`npp!N0P-SBkitIty!XSdjW~bBYF6Sc~gV
z1&Tz=sXH<a{goXDqK%lS*wecUu89d?APhsZb*=LIx{aHu{rx0T-;?X(NfZ2p8-+7O
z^x9PKy@`ShBX5<&F1$1Xdk$@*!*wl61Rz#QfT)s|vt2<4U4ijud(Kif_Fe{yTBJ1A
zUVo~i4JGpXFnX-4p(oQDCZG?6v{$`p6k~2^X0^ILsc%3-v02J7F7?>>Qq~kGc2Kt-
z)LZHKGh%-FZbko0i;esH#-Nk&2R2NX21nA@_4zQNR1T;~hJxzpJcDq5fyG*R3cXfe
zzf(qgnmKEN_fT)RV*YgU!ZBhXwAY0zv<AJ&PBg+-yclg3lGSQ-o6@HLX4TsE9M|KU
zf@8((uj>x9vtYB0SC=n!v09&js*X70<4<y3-OO<+)EigS_EL<K(lSPoC9@#Bn+HFg
z@@DNL3Ew|w3s2i2lFvZiMtu=DL|el;3ZI%|OF{@fl`LUqmH!+I1*<gB797`{$VwkO
zpJct3(06@Nhd_VDZ+ZUxM^7c?c+Ab0#mc<P@e7>8H(`uBL382K@Cfj`8{8D2I8i~M
zhw)s0R^UJL@kX&z8T5m!EvoCH({C}jLt%nrGg}1mY&9j@Zij2?0=02n!P63jAY+o7
zW(RkHT8pL>Y?4!Yd=eNNQ4(@l7Yk}Ne=VT^KRc+7WEix!)^4P95>pZCUdu9IX%?Yz
zY!9s}jQiue!L@8A&mAly0DQk6Abl6wG%XhAYbL{RvL+pZ(?c<>dB%$UD>xp0OTZyz
z&Od@8J9M;I>YxBf#n#+nX1Ya<Z@I3JN+>^q>T<%VhP9O^!$b|8ouB(BPE?XF*(eAM
z`zoI{jaiLAg7LvTWHe<f_b-9<?eY_{Q3a2Z1?=ulicY0+f9a{M*W93|cOvJXEZ(&p
zUvCbmPmZt@`n(s!tILPDbQ0)4RQ%OJz8b%{(B=-{ryuYvRY8rAdcamrAFX=|C`cGB
zB|iFUDtF_B^$3D+<}VvS^G11(8SP;!i<xjf_g7mpvJ$>sZS1Gj<dwmr7#6aP6va40
zPf*9EhvJ0?ZBw=Ik!ojs>0T2DjPXP#EyOVtT1Re~R7-Y2|6f4Gk|M)^W`E<7QJbVZ
z?BQ{ylTdV?ZcA$Ye@wxeGI`xPupF$f-F{RUp0yizv<_kL+IvG-dW6(nC7JL{^FzcH
zqyUJ}x3T%A0|ci~v15Oc4>=4ziIi+We4DO_dw?5%3O3Y|2hB`qW$_-l{SLT4O|@#8
zh8QNWDN&{3(i7e-2HN!1Q*iPou`+yXd2m9`^vSDn|1~}hxXvY9u|{qSf7h?A{e-+t
z>*u(xB|ys{t~wAf3`N%S`yG_q0Z)NoDiQIf;(q8r8OKh`hO`@DS{>3c*t=-lOx5F+
zz9qqaC4?BV39C{a<P%0<i@Cjie)6Hg!O4{lhM{SapzgALyu+ljGCmTrPM`y_{G;JQ
zu5^{N$Z^@!eK~Q-Hq$*%OsPr3x`^IOI%*?XNo2?OXFYGFyY7p2PyMXrIzAdKjQGeh
zQ<1n^=MljYzESh_UY=`rrfSb-VbrLl^_5>Ja%PdJ@cK)fFFx)m3Mn>ix0H)78;KDr
zLJ3nYw(>l;+5O%p?dNeC;#U4|K`{5PA-jW}BBfxL%TxxZEjxBx>4mF+#1xw3Ku$Zi
z7`tqd2_NiiyS!O!X&o_hIPjP|BZKe&&?FahT~kIy9BTDEh1a6WGZ)CVT)?zkPkUhm
zFppWrwESNXeh{Z^ob{4){oA61WVCPfQr^H}96vaGVYK{VXo%DW6Bvg|?#HFLSATA#
zAU;%V_AgRP8ahdaT`ete2!RXbCk)1q-gPhd(5R^;Euy7uMjTb>xfKMLMZpqsX2$Tg
zt#sVQlBr+Zf``9v751x%%6g#?D{ho_&AK&o#dQ(uK=LlBKu!U!knft0tb?_;T22o#
z90z8x1sZ<Z{?1TmoDp>v9wnP$wkYi+=j^^4U6Z_*IsMCcrFO&|_~xDa6SN10*{>oj
zQ^l3U!cPzZi!Q+k_W+zH?8ZaI^($=bBgNOxu5(@nE?#C<q~UICwH72DaAqhO9pHrk
zw}Da|nL}sDd?U`U$O()<3otn5NoE?eV2*%dF+!q}_MKV^7Z*YbGbd6ID+$msJd6Dd
z8wy8*b#NZ1Nx1O*&y2noQdW>Pu6^@ygyx5x@KqCL+**avh8cN(whvSAb{ICc-UcsD
zr>Rt9BI})049y!&k4+&{+c!GNiRWR<udOofO1VV4hCZ#Ua7NU$q7aU2fIrTMn5(hA
z*RitMZX{9s2i>Y>n<>pvB{D*uh8h{7l)RqyMJKv%awqL&^s?w@>8UE<@Z|TJK^z2i
z#Ne_8e&931sCDBm5Dy4II`7w}TlEhlCb_Y}iP^3x&2BzQsLNhhD6JVE*Vc^&cE5gS
z^7*bGK**(*=&LOZHoXp7zLY0MT`^Y=H>maH94M~?h6y?MX?m@jFV&0{4n@gL;!{dq
z$KmZGABVKeejepO=V>Bg!=;a8Vb2{8Mlu#C`K#T+p>pZCktNN~VyakwT{ZHdTA!1g
ztT7;Ef%cG4n7Kkt$Z7^y!Er>HjoiW@C8XUxospXCrZeN>_4;uog`<^)E|R><x~v(`
zh=}v93i6Dw#V6OETB7W*uMX_f^w_Q!Sr}5d$noxUl?UZTLV0$`8(OcrDu0_%vd1^5
ztDw)WbhKe^BTsovi?)AslQ|{68u;*!nibJKB1OLIm}mD(@^d{Q1V8Bi0IByZdu>r|
zT8%;=EA1fUHWI84+{+I{_-$yvcZQ2?sq{Z>-NABzogH+-nwXis6)*21fHchx3!#{K
zY8N6h0R*u0+eOA8eD6m1L6UP-BhvGJ(F-2kdHsG}mxE3@j*#RXYab*B0{z1}-X{c=
zEGH)>uw4XA1X_u3V>}j|&9c8Y32;pnR@~t1tPTQ?%r*Nb0_I)1fQlNrA~P)Nk?$ez
z*NdrQs)r>f3|C8Od_Wk-R@l?9tY7qtyTsnq=4<y3B3}W6#DP2B>CdSbO#FX29rbP6
z<yIR=NrtG&j5$puy@7s5D^((M;<4UlS53t`^)QRn(iH{3@n(v|?cal{;eX(vz$E1N
zNZ04<gzNXl3v+sFsgx-3FKZ<UEE*`Hx1NsRd*=+rnqxcvao4v9?0awWE6iujv6oJ{
z_<r;=e6T=ub4>?XuD;KNy$tP#+FkdO@ovGvBVp;bC?g}RVW#orC6-xU6(XkqFgAnL
zO!e+(DP^x<AT{i9Onm3FbTKczYqJ))7e4i93vxU;sO{F;uJ~9%9Xs&Gw1haYs_e-0
zr&JiNY-sE_#khw+C|6#6%W+h)gGCD4#gczRt=wMr%XCeq$Ov4|>HU|1{%S{+aAv>1
z_ttZe#F1ZKK_6jYQ%E+Fy_$NL3;V#U+aaf_s(@4M@cjk6SFRv?#0Z&qsW~ViXMag4
zlmH>L%?kxSXIMzfs%Jiq!;4#Kc~AXu$>L?pI>pO~Xv1he=CSq-mWCZSIrJGv@IIG6
znyZo{j%$PuA|#8uN%#Q?I6fXxJy~_KV*JDSMnKw9(lI8kxp)?dX))+Ut3WOPP0$ZY
zJtm){jU>x7ZXOc8=(3{bBUl}E_|?Sq<?@9?#i!Si9gkT}={Pt_7=+0TsdY3vhiqr`
z2Z8dJByh!~c8sqvx-;1*0jQ`8^tCY!6A<*X+Pw{*qE;L2uLegR7<AH~C-iC(CFL;G
zqfV`q{zXecBru}_gAKgt?HA$Rqi+R9bRy^CK^&%#6Fb?1186R_Gs<8`Shf?&lgITG
z5}?<C;Ziy54<Blhb%1xXk%JQ+V_kg^Z&-7Jfv!7@h=XD2xvZYIIRFBE&o*e}-(Apt
zhBr$uQr7K(wzWTFYm@EUha%Rk(CFg{79R7He=+A{H^$$XA%KHf6I&E?PCfb8q3~8$
z<h#>41NNz+{}A6wZ-qy`$19Z=+|!baB1JI<57q-R@NHu!Ud5}Is#b|^p4aPdRSq_`
zw`10hSm+>n3XD9E2OhD?O0ALl0Dic5q5+N`-szGuHdeEG#Z4K;b_2pSZ~DKUQyy*m
z4^lPPIW4TX$u3;;<Zvm@a)IjeIDT0Wg-+B{qFtE?d#u0DX5(_e!ayPYwLL6ga>Lyn
zhG;^Ym@f+DkNIHyJY;Dv&dUAsBkrko3<n+wz*Hkh|0J0Um3VP{I~e0j**XQk!^CMg
z&hp$NZ9$A)%o=+1BUWMm7~($R7|bt0@<H*bc@oKik7YCvP#COM1Sj#vSmNB8BjYY-
z-cP)Dh!es!M-Lpo()^WP6cW=yGBuO8M!2o~n$g4@rZ%C~C{#RYB}YK2txojf(8;9B
zrH1t@d3NBzEBQY8J`0Vn+N~H<-~ovDky8;WPtL~demyV%v9q28faBASlQ9f#n&T&K
z<)nbrONvz4=n3H&uf!+ZHq5zV=!x?<+@Zcw3K-!tz{jSb{#U?GoB<&JV4TJ5t6Efs
z+%a*}^P~hv5ly;y4%pE?EuTzQx8fKFUZ+BgbuR@qKueW7fD(-FZX+rsfCa>5$wj`P
zS}2jzl5Dr6@<4=wc^8_}#f@ovM!M+0(zweD*Eoi~eZCm0=Sne@U&;klV}}kOX&z|O
zh9{kIO3e0wjXjzrWL^obxWvb|gz1xegsd^u?u8!=ID_Nk#;bebQgPi-G}w#$F=B!%
zYX)k*1;v(XDi3+iXlby^Iea$Qdr%S<7@$5?qk{UIOrx87R;=<abJD9I6~<ktX=$*;
z8zAlkEYzVOV5Vf2L+Bz{cE#^?N-T1Fg>d{7YcXthrj*K#;-#Ur8~lPKKMFJQE+O%K
zC#{TZqm+>ft-TM}XDe=3yP6kyfwmLlVI^+9ODzOz)fs`srgG-8#wI4fW#m!;k{b8b
zWTUJtR>^IQOj`5%YYSQ{h@~j%+vI(Jtp8DlK;Y)U5>iNm`h7Gs;p%iiX6JR@1)l1H
z#Bxv$2iA&BA}i=H{6xpy=Z#4K^YzBv0~$X>!~z9l@+u=#ioR{^r+bctdnw1;EUtYr
z<i;G+1UurW*LnME=N}!~DLb9@onrf(0z_|pe3Hu7?En?z0&g<E@@FHDf4rW-RM3mc
z*Bm>Xx$0+Xwl&UA+f=BOhOBwFUf0G80C^=@%3QYniSiDxO>H(%pMKt~RV1mIH`aZ~
z%99XeYnX+T3C->E;Ip36yCd)Gm;t2)U~c4I6+NOLyC0c7rn4zVlhYw>ffwF<XGTpv
zjEUrV_XA`+f7q$b{%Htl#v|J^JyZQnyADG1ODsXtL0?cW`UIFpm;oFufg7tZ89YWh
zFZEKoxEe3d!vZKFsRcb;wQGiYGB3sAzFj|W-$B1PTn&mEJiC3!QDk78wm{r2sTtKY
zOZ%8+NX^-FM;&OeKk&2;RxTD2<C~x2b*a}qtj;1GyT*Wo+hPY*e8;Ii7`;}BZJajB
z3+c=It1C>kRKL7C<s(J?i9CMJDzs_S+owi%TxZv~S>GBFf<6!W;HZG#=9+4BGBvH~
zFGUpdjsT;RsNR*m+H}EZ+A@_BuO~21F|C(Idcgy@2tpbf=|!<_&L>ql+|kVA0U^Rm
zqP)|`W1(m@U(^$y>D#s71$CcqLz-g5r?blX6lfYIGqx&q!s$PWkOvGyqZREvE3ljZ
z6Mm?rS#17fo|SV`f^-7Kqgc@&y(2RnTG67P_vZPk{cq^gT>I)ssr;F(DHkbPh~=Ar
zg;mn*0kCgEZ54VDpPSwX*SvLBb&ODa8_4n@&{#;2P4H{w!VO}ym04qMZ*T;B!yrzb
zb+4~kFteB@Q-j~vKNT&%d;+taStMg;HSG!~%h5?`oVm*;LsjKlH(jNa|4ozmRaPJ&
z+|3snPeH{q<o3s0XngK)H7ENnYFC6!`cr)^l87_*(;o2Y|6m&|^XnHfj^s47F}a1A
zC)&dckhzk?1`fYq>tbk#;7woSB-E%{F58eZtEaV`{8wY^(lT_03yL}qB9$-cg`cVM
z9B8G6Io-Noq;Uvl*=?tQb_P{-$UGIOF36tcM->GR^|0B0`fz;>A2x}+6QE>Y*pI+~
z8#%w)U6n>`068~z4gk7xU9L~P<$FDhb}gzv!ai?;K!EX8^U_NHPJVx5Q=aGj-&Sat
zC*RDJJnD=@-!1ZtYm|p`5s%YLKU_Nxjbh+9?=$IKt8P#JhUI+Ax4N0tn|z{!0Hk~l
z+?$Rc=(SsQDuWMq-Rt#K-rae0|6QxvC!#Uu2N5aToi02Gmd!#VnrX-}B~|&k33X_p
z2r;w#$k0-1WzT@a&IFD6gD>jHnW7>VifR!>KU0H*BG-aZ7U#9kYy7x7Rd>-f(D;v3
z66vtN=?KWW_IkX(HU<0BtZ-m!Nx7eZu_qJkS2iygs^5o~6Jrc{lT5sHSmXeMW~Pq#
zw=Vou38wF$7Gw+4W|w{*RNs1H9wb19XjhMO!e`o96|L+A6{1*`{tVx`M9`BComF1R
zZ~FS+LQ(_j9@zFkHNL##eHy3>PZd8oZnp^u!@TA2Y@~%~*$UqvBQ785L*&*Q#X9=C
zeL7R)fKC=el3cS{x0O_w`Vd~;p=J|;`HXB1t<A*8>sr&OvcFt3fGEmXc^yOu)R@pM
zJd0Qe6^d2w@}D$bg`{1XCz=g$UV%}WoCvkOM4!cVuUh3G#s$Ju<De6Ci?HRDT@=_J
z7@86wTzYkeHHH24Q%wX1iqm-6%?Tnzl+hw&h20_Ma(Pr0A8v_=xOJ%IC|onq+NL^B
zTIOS#Cn~57rT3>gEw`9$cU*pWm*WSc06<QHhr8`pkB$y5$fQ5L)+9n$y5A4nz1`Vn
ze%h09#|7Stwc3mpVx)J}{*~?(F=mU=V8z?wQ)Y6ho;xORts)9g03Dc#U}H;kPnA=<
zmds*y^s^G0G2|4);s;sGt0aRxETMp9cpkBw2{xOok<%P289RJjIigKcf`PJK*%M6k
z@nyZ6Lo``Uza&54?WH)-*IWBWQVV-c!1|`)QDGS2JuCJczGjZq9y0kEOarPnTy{yG
zK*0Wx<wi~<x;H-pzt%Yv#^lH|q@7^i^^t{L4L8!t`(8J_XdRoH6X|1VxZ?NNrNYJL
zBnG6*BbSw#zLc}+DqD5eWdk_PAE|e1v1+d8h+Q7c-5|m+mrcQ6m_zO(4@|w+cffo6
zYD4~cB5GCi2$QafY}S|A@~-Fq7Ty`hAv$Gur*r}}nt^0k;4MWz5mYj(>e$ZVNO`)3
zN%Tbq&7&0MkQV1u+R-_vRP^b>Jgs$WDMFvXoh(JC{-L@R3HN7IXn`=tsg`pA#%UeN
zZY_cZ*iu^b!>b$0L+Z4D>VCY3CJh}&6L-KV;s%xFXj?jI=s&4h6+N1_vl))O0hf&w
z1h0#&GhnHu2HD`lMRouxCHuF^!E1xfiZWAJ+QYgtfkQ^i{JNhvKP|mCiw<{v2W|>K
z(FWGSPw-5nzBLHl%b`4R(I&B&!_1*UjPVM{9X>(wN~Qx?8!M1=^lK?>CZkECFN9KI
zeQit==G31#)f(H(yq8JhE(^Ed)^tI-Y(GMC;D~~8?(!b@wU&$N^qWEpN5#{JVJm2|
zjAHu={R+I7y@27hk7OT<NX-hTQ*u%65>fd?a1r2TqSv)$OAjy1kc&MFa{Q~me|(Pp
zQ8HSA@s#YPBT&;KV$@PIs~0K{*qztd=smF@pZPdfr@u^>EWw%PJQlAXO?fNL*AOgJ
zqKIw!baWP;(n?%Cxzg_nJ&!uBEjx0ayF}(ArvjV{YlF0aV)Q>%iU4%MK3>PcfKNp`
z*r1RFDq0!Z0he_LPqtDz%W9W0M&Tf7N}j$U1|*r&#!(LDwMucUTZgR;F{3tQ-M+fR
zO5FczvvVSLAqQ?zteQd&O63GfHI}vdhPFW;bVI5UQk9_`t`aaF&_l+XsEBXqK(eHL
z(N47888>Jnoz*G76n;xVVBWY4jb)iPviIc$i4nh3$}WyvUkX?m?1?mjjmOglCCSgf
zp&O4ZO`Cl=iWE{Tbi9=A5efjXIHu2M9lLNc-D6{<>OvTE5&Ci1--*q?aL*CioRy6M
z^iL1gi+3;mnIi$sLDDC%?P1|YAP<0eUN|RKFkVMj%GQcXY#nkuIf_6c=9UxlegJgc
z$hQ><KG)URIW2|CgaxihwpF*B=rf^Yw+~h=XvxEHzF236;K>1jcvqE;mxR{rgkoxS
z@3Y8f$bF(@`>HT3unZOF(*b@leu9~xoaKiwY`$G$1C*2;F%!F*FU0yeUJ2awfC}h0
z-_UjyIF`^m?ZgXu<~yl4#ucbu{f&1gUkj}%`twnrEZC*lC2O`7A<QyjlKSe{!lbIo
zn}H7aWQ4(Gd=(HB1R&DR9wsLQz{h0Ms$=ZYv7qHc-v>{MxS0xOL~%0z2Lasv-T0rl
zG_<BeSg({T7htCe%=A}|e=M`hBI_z=Pw4sc6CX45#^94yxQ|^MODv>-!y2Kqm$weP
z;?7bC01Rn}hUO!}fdNj$``L7*xC$$!=RyX2;7+gMSIkTqwd_wucm$E1$q7#2J4nce
z`MiDK)0dKEi-sC)0HxemK6K`RGfcW7yX&{UKtdXlHb((ZyabaHuGits&*$c`as8!R
z!u9(h2+iwH-)~6U>^HKR8_qKjwF&Hrm6lJ``NgT<BwIwIr3T*o-QW<2l(T%RtBj?O
zuM-6L?lft05FRyDU}@^ATWjhrjeLt5vk)+|-=kT40op1BoSty}q-NVgw3+(A`XfYc
zvcge+V{oRnn8utI_p7An?2tm%I{x6TiOcxdv!(Rm5VQ?1^*xN=Ynu|LTKzm$^`gV_
zTzV_s)!}H%&j=A-4P3*;vr1!5>^Q*{`a7BP|1YxNgAFIe*l909wWFy#2JiPrhNUMU
zO&l$nBLu~;T98_3hwIoKzEJNno1B;=rWHI=$b0S$_KM^Rn<p^kvO-QzMU_JE`j|x;
z%4VY;*%XW!xSfLi>bdM*fBbD@-Kp~i++lRjQ|kMfLn;8;2n~i}1^$HiKv=v+$JK{@
z*#CaI1tn})X@)YFpPR|rYaxioz>RxVo>(obU87SMOAbpRpO>b?hW-`L=Y?D)QefQt
z-I8_fX$Gkp9H~a@g|RFLw?<Vs8dQ#L5G`udy@p}GPf`(2HF)lz``@sZ1ZP7<&I=)g
zQ*xA@vlTv03S)5Fg5-1Uw4Z$V7M92bO>>J#(!ciC#@fcz+SpZ)jgn2;mpT4{uK_EY
zVikHS*iIeUUY`@gYc&RCRh%sn)Q=i%Ny<V=hWh0Vmh9k$<B^_u&sH<}7D@$nmJG|*
zd&wdLwFh@mflw*h%^{Dq*sUPmYal37lwU`GI=Z2=Bxa+_0d0?)g5)qnNl5n(*XN{u
zlxQP1k%QgK$rZfiwt31M!|nBlhuiBf54YGP<5B6Rf-p_@EgVR?QNz6(u*WyVPU~*X
zoZma$Z?J1_Y7TCYl~Dq`DtkEAlieR~G3s3A$7^0OGQe4_N5$#D?VGRZ+=s$^wxQ0V
zCYjwtth?%JQAU(&;CY&*qdTj?uyweElA-Ik65EGp(4&Il_TJZmfxgS}k4dpfF;=@{
z-;|ldl@OPeGkk&MBx73kV9Se8k6Vz7Y?TX?05>h*Pt&?O!P_1<5s9z}|4eZrb7MD8
zZK#}ResgtW=W;4HvbmSh2D46#MlWw{(`{i>RMG1AF$39r*^Q1Tq)xf{x=&<CpG+5B
z+|sFDYAXgMCOMjmxU!(rhrj<;16?xjD*bRrNli;J|2Gky-BF%K@4l#J5=pw(iN{_f
z5c~eTSg7*-mV~9r>aP$ZVf?ZVt@Ni|(5QTpBk^DfY{{MwK7Qy9>vd?SrxZWF%0UM_
z3yaCdTxvStTr5i~3n|6*%&Z<?kC~k)#!fxQZgTam;HOrGtlv_G7+R+kQSqN)>if?&
zm`*@H6EyAZ0UC}BT)uB+Y_dB+<p#d+yWNaYi_Y_|&I9|BhnLTK>=`e>Y`wmO<B%-_
z##&#0kRhXmqv*YhR*45+$sl}dt|av@MN*=Z2*Jc5T5>WzPS;N?;hor1soOUo(DPNS
zsT$dw0261OAI1%8!0&?iVy~-;F7C8l=%npEUr1{czb$DW5Aq^-yiuW>l*|N+5bM}|
zu4>Kw-S_lsTA{ub(Qh5%GtlQQ#o~=D6!)I{?)Gr*QUud1%TIr)=}2-YhRNY=2vMNr
zijqIb@FPusmlpJ$bK^`LfO+y;eWx($d^Ds>hGe+)Z7wL{2}`<)>-JUH*_J^&w?Fd8
zJU)_240-_5Mk)`o8z)?cJ^QF43{!j2@yH3B^-mwUIMM_Uwmgp8d#!u)i0Qw{Zjz8C
z>||ixMDQ(IO<?epbDF7|BFek4pw9UE3nI=iZB3<L8F&?GFREzDp>QC7ETALA5hUjh
zmXho+T1ow{VGIVPtHs8*RQ;K^thz5wKl=YNnx^vltNv~UD81$;gM)!_qkP{e&^Cy)
z64w@BY*<hP_XytYiVLyu(**V%1QO!1P_Ve65sXn<e6j^#84d!i=s33=+NVE=5wsJq
zVn4^D&vpB>K~$PwY^K9X7@ZZNU!Mk-nboBPXtZOtwe5<~!M(OF=*6ZLSs(SIy%C!S
znqVKIMu^&YNryICauTpTKO}_NPC-w3^35YbxSCIp%z^m5cM-|q3EWU>ClZD`(XLnE
zX}G54ilXzd?_zC;VRkq8>A^)Qn+neBqrQuuJIJ)&y_zS!T0KUegOkQPF^`t)%<!W*
zt#%VE|97Iu+3+=hhOIWiU;bf`lU|<L{rZ8<Enx<L?(>bray@TFZ$kf8daE3=`2+OD
zxI+Mt)0$n|x`BzGy#g;;sS7k-P~NHWjnk^o<_J9hT7GK7X0KGo$Ia^yqdqJWVZJrw
z={++f0s7~Tv^(=Z)1-SQ^640X+2}M`_I1vKIJK2+J(T6nsvj}qek;;f2&9$GHKgm8
z^Z=g1V=Y9g9%P%W2i(Gu;<IL_e>45>A*^j-L3vOczmx=P??fz(UPlcat`g4^9UWTv
zz57|c55Eu^3RVCrnKiFz60MZ<2>t?1*3(i$wz}c_IBa`&-*#{@yY6|)-RPzVdy!#2
zVD)>!SL`T`*Zu4sEDF^)bwH>4KRRg1Z~Zi!uw@L!jCxj^ds}rK4|1XwZ<3ON?wVTH
z161^~XoCPm+SoUnMRr}g%GM-gNPnRfS}p)!;7AT0{G(Jn<NCkudrA1rp&FEgc2_^_
zjsy$q8Sn55xjT&)xFl_Wz{G2^y!RT@7->swOM|1}{J?i_8!Hc<zo>JMn42!5iQG4S
z9RQW$PLhHm$N97WBomcMjvrY&vTKFL%1R7<KS<XDmO@`oI=gQ$rP|0eVvp7zesJiO
z;Yeyea>i-LMp<<@0(aC&Yy1~e@XuL(${j~^^I2BM3d06FQM3cuNvV;yS^f=_uM+Q_
zVsku9M>`eOJ?TN<x3W4UBb<ijpEw)+%8X}hu{h(#f%=-W%b|Kgz4}d$6At1kYYpSL
zf5OxS=$IuLKM!xbL*Q#2Q7JEcImPfWq}_BYXW@qrKc1uJKe2<2J5KXWK@aj4NAO-t
z=xhfFbm@Gwc29V*`eiNg%P>Zq9MC&Kg_c9iMbw0a3VA9qNjkh0%ab-*)8_irFh4it
z#s0?mE?R=zx+>H4gR8O!bWGEuWoS$RTvw2nX$JSu)qSJ9x|6z#Um_sl$nrus-W$a-
zOdna0oF>43FN8nO&GUoCPUkb}^Ap_1<oU)|DZj3fmL>A4AukXOfy!qQCm&IV0F4BA
zr4Yna_l07hqDJOBr&!gJ!LYDMc1!pEj#p;&he+PH$y&YLdNk4>7>J2rbr|!FlEjux
z$)?7Lfr%LhMk1H(3RbrRz*aj|!+nL+x&eK*io3|Pp~UtWXO4^DzN2B&YVOG%w<Luu
zXvO#Cfo;;FaZH=1bw>c1sQ}xbi7*<fDg2Ih+HcT1zB8nshYwy<!8pAYXZIZ+n^NrC
zI>5?`v|efOjy@kT;kjysaaelpYAg}8Rh#F+-M+8^-}*M6v~fwPJSQMmXE440cYFlW
zs&9U>vy|#b0aYjmcN$9J9XVAAF81_VO~D&u-ndY8I_eWjs<4o7i;H#}pYdFXgE%1e
zCqK{E5fGXDd=Vkt?ga<G!7$ngq%5Fa@TD6vx8(#GDbAQP6{eQ>C@nVK2ssl<iW10?
zv|rc8lcVYz=I{7}DIOH;*g9A4v|T7-@>_QAg357$V*wyRj&cwDd`hWi$>zpKs@$d3
z;RMHOY3rS%H*xi<^rc$cW{+qZJ0qVVNmj4_1+k#~fGvy~<2d66i3b5bpc?U*tR9ys
zz41g8e<o959KYCV9J*2-9FCex5$UG45+%HNmgPW+CI{ESr|lvToQa1!Rfi&tQfSty
zb93Zs@il(O#V^Toate?Rn8MLv1%geSx~Ks5ixxR8zuA_C-KHDZIjXVuTSsgQPJh0F
zh#3E1DN4zGvoW}aPSi94*%$j{TjJ`)^6v6|6u)TgLM34wyl?aNbyJ&GKAMEiy)AP~
z<?uy0>IZM7G<+hm7Kk|qCk$_v=z#*>^A$)QdZ@9OoQilaAG=nbUKy685)<cNpMu^s
zZ83#Y4P)cb11(qGU9A)!Y*7e%4EOsP?;zyUqkEOa3u45mb~0TbJW^|GQE+(|J%puI
zFL6jQ#Jgr~nXOTp?xthm2IZ`)v(x16+r!Ag<7k~Nx;m=8%QtHiX(o1W6qCC17|A;_
z)j#zK@EkfX3*rnlD84_<K7ommhAYW1T5dQF`ENj}6EImBVdJ(Y3y8ES{JaM;exCtz
z66f|)XTDX%xsH8-cn3!u@oX6_hTRA{Wr_b$_&F#P?LKG?M<)1Nn9r^A#NCoE4Ow5}
zR%6u`?#G;G7afprxv{Pk5JcSx$w?ENElp~1x@_YrwV0i_u-1`%TK6t-RMBv*wxC+u
z=ds>kV7TV15P@hp@v_KDbV@bGYH3!ZYv;5rUn`ktPQM!>lx2uifhMFsC>mh_;a^tZ
zyrbpU>RQm1yqcZzj~d}5T0knwcW~#HIod=u*X;=Hi&TIrQ|eY#q$O`~AjnPOvBk}o
znwPj!BiW<#GHwbQHUuEG4*S}AQ&E8@DtQ+@8BE9#cf!3)<R;C&XtN*_`Q%;4pm&zO
z*r({gM{|dRmA)F6noq}>{is*d8I6`vY2g<1aNNh}9MgCfgrT2?^1B0{&7@)h*-h-v
z)Lh6t9PyIP-K=+QB;A=YO-k!LQp%P_Wl|`2LdA1uTesYRgU@srlsm6=0CjfK^|L;{
zp~}ZEE#@HCBPZufKHTo{V<t#dc=?pL+1NMLz?Z3&b1U-v08SZJS=<xwqlQPN?mX}i
z;nVg8B`;aV99$r&`yBcp_hY~RVG&Svmm@yzsT;pwU}tcvFfM=?0q-*ij+<OEc3OWg
zS6PXzImDOAom}(xcyTeMOO0@4niu=ho6I7TDd*@XrCVc|vT~75g@tSF;Oa~HNIO6!
zOIc8gpFt#*9!^0wO;&C+Yg#)$d+YkeRzXH0^xq)dgH~x%H>=JBmN|tjwls<J@gc0J
zeyyQJAD^PGirAy=b;<g|yR{LkGL~5YFX~k+^xa>D+BIb9Z;1RBhP;uRUtgr!>f7g;
z8K4-MpPXiHI8xYtoeDRD;}0#2lYjUD#L|?kg}n!5X8$oSj3ayqDqVz2Wx~f5en|eU
zkf%B<^j})k*A^*Nj`ebqH+u1VZy{n$gH=Pr4h_@l*2u5PD7wJag~G4?a1okbf_UIT
z%0{<ylarQ?&MR?)EE71vBWBlx>h64VO<pLPAmXOk17+Ttj^;+8`OFJ}3=EFgTS9j*
zFDPA+xloIe$dL2}9XU1bjk~eT($LrJEe}7AXH+-1t6SVhJ54J?v9h}-q;EWxvuNX<
z_QJJ?B6vpa26~q}lf8E?;><(9_t4d;Oz7a%y|znzm6L_FU3c?aN<fBtVW^l;x#^4&
zgYaEf-9A3AU3;-kXCeEb3bbRxMzND&%LUv6yTlbo!E{5cO*D3%hevM<s+cK(!mTk%
zs7!ousLvs^j+5a=Y=F=%{Wswi2jPI%+#66tz%~`D^uj-`C>j4s5{Ag@O78?cK;;AI
zc`tz_Pa<A>RaH?uCfUifaFEmz>9L7*QX4vw{EM+5h<aVO*+gO6C@uC>uxP;Ac6X4R
zJAC(0Ql>eM#@O60*-^!2dF7uc>G~-gk5{rDg<^BQiMM{PkqVJ;X}#JfH#Q;u78811
zv^ca3td}JXzn=^*n^buA+CiID-UKOyMsZn~(=0U>zCvp#L)&0|Crw4YjbLI=p=5Yi
z=ugdML*z+?JL0;c5Avuas&88@+6-?JMH|fRbXj6ulThFPUv<=-168*g&IK-OC@#gS
z<~<fG@2*{=MmKu2RQ$e02PIw94&>}>Fs>pz;XN?yEUdy%3$WBsh927gQgIWV!0!8I
zN3DZ7IK?NAK(}-K%Y#267D-zXT9IH&=cT53Q~KV-U(pI-=HjT{qj7-hZ;5BoVnB!P
zRI<t-65>`wa;NEb&xlTFFhlG1sEOg^e?{#yYd?Sj{yp~*N#W@|TgBF}vfBcLlkPwU
zUiOiUjd`vDGtT&g%}&L+yQFYSiTZz8!zB4C<s1`_VuZGyPe>lUUZ<h~Cq@ukp}hbd
zl5?ibe<8D#h0>1~)f27D=q3sCjD0xsFhQqq=vM~AaxbGZ=q!Xr1>rOTnl>97O2$hx
zk$ldSeP4BPKY^sk^xJWp4mtd_U;@EZb~<Hf-EJip4(Y9v`Q)f)oaIk)bsQQY`z4UA
zPAs1;s|>IW;Ib*jnV?#X7CW-rvp6wGotqG16`!{1zp9h{40Wbm1cT71An!C(=t`_@
zz4Wm7OD$^CX-p50Zx3>%_?-TN1~2WMlAJX|_n(3gH+#mBxw-+{D)7Cd(z8SvN%4rn
zxCIq4{0u3*i6XcmdrI?mfvs2>KgCyhkru#I@Ty&*76O)oLhs+56Nl{jgO7UZ-uR8%
zgco$4<lZC%slJ4u*YLWy_+?Y=T)a;cqxlnGTxXRip1xipb53R#_$9x*pM;(rCztu~
z;UqsS16pSnDBnEVYQToCO`)<mYx@&Q{vEGII}ohg_dj?{raw57+<H{R515HsDb1LO
zNzhncsquN$8LI$_5_-0nQD*)i(LuA$Dua7Hc_a;5?WM39D_p7R%F9GWS56Xca1K<*
z;n0kJ9TZ35&>-KZ)FF&?n*$e+Iqbj}@38EkIGsaFXF9y8F$%*9M<aq|mOBnoK*2sS
zb43z^fw?=x=<kN=+wYF{@{c>n13{Fo;>6zm#lfvGs#JjY8pdh1JciPQJh3tO=moym
z5G)JC2aN{XCl~w+DGU&#Zdkw?QnU!>Dgamh(0K=;4&bRj*LB61GJpHOdOvgR$niBQ
zVTa6zaInJ~85-t^gK1}ygODA^yWYRlnL2|SalWf61sUUKm1+~Pi7j-x6-qexpSz~D
z50z}guFWu~E2%O<ckUVOv=^<O%r{}rau1G0QYJaT6AVs}UawH)uGSUZU&At5kt~X}
zwe>t$0Qgru?j`U@<Z(&@n*UT^S@HxsN~P}mw{PH3GzeP58F?A>xzvyhW-5Tpcf<OA
zlMHkcb{<qJCm=0wYSvH2@Kz6+nv~;usWovM@#YIOd-@uc?#1ixeJ~6gn8#+?S|=tw
zbj42F=%|V_Q}eWm+-nO!km9~a9pw;|)_H_YG7lX*Y)x^*YC|zD{b_kTnvDxMd<te1
zU6%;>>O{2euv)>@u!3U)y-=;Zl3nlp!5X>6oXDcciLP?+7lw|SKp6zoq7Q!b{WV?y
zPV)%JQ+JSAz5?|f3IYc_JlkQ)SCh{YJ3(S)E_~+w`NXR0OpM1NQx~2jP4`9wn6Ep-
zW*m;Mq%Picg{p1=nKsMer|BSp>C9;gd7>N5TL<*hu<IhPh*Y(r$-a`Vb;HkB(8qH2
zg2XZ|+EDTAMZq?TfW#2HUIw!!lk7?mv5+6i7_3zq>aCziH`Qw?<r~MdR*LrLF#}0|
z=YCUjfD{Ji(`4_r&8&{7w0Od5&sTRTBwf=B==BeNfkR2eSAQyu7X7olHC<et_?VN_
zH@u4TV7KBhwlL&z*5)^VE7JSy%zIWGMh%V?#2@J<!@bLl=uA3)QfV!E$c3}huPg7u
zk5zk*L7hR1p8yCz_rIAF@5@N4@gR=yKB69eqF?e71GB%Rx0AW8%rsmdkNL0+MJ-2`
zXMCzJ1HI?xPmm=yx_@NHk?)w~JeA0!5o^2S!xPOsEEG@y&V>kKk5`)yp_I0(<xGZi
zb-A|i56pXV4zjOCJrij`8*<xb!g0BKW5?Na;G)QjIqb2)TsLqC;}Qm>D0Qc%(F!BX
z{B43gAcn`X@7WLr%4)kztrj9KRt0vyif7>KyHH+0j`6oHd=y=GB;`D<{R!<UlF>O3
z)xI_Ixwp{Tm5Es3a0Dl4TxB_;B{(*vIFEgtV8N3EFfclIF@nAnT_~a3WaI$z0CZ?q
zqVAD_f`Vxlaqpc<|2-Z_QU?znr{xZ-h3C&U-wOCGe&&`^PwsOau+8rN`&I-;^cOT9
zMnx+owKzM6(-@}%o7UC7rMxTQZ5g5o#<F-g5R8_)JaGJZu+ug&RLI9+Wh$V+F`>L(
zAn!aPzOQ{XKDgbtl_Lk(40RUo@}s<7m2+EeASeb&+@B-?^5vDmLv08)>C68%phGJ?
z@8WA8{z@@gF1lfPE(5ZE$Bi*6aBg@LFh?n=d54!b@PWo!E1;E}f&w2pJ#mbsh5^m1
zlEF%_AXJNKZc%;Bl<H)rEAJ1HP_fqf*Rou91YkFVRtL7nU)D(*DR^$4GscPaP)UFS
z3`_h_XiM^UZZuQH{{{s%(CDPopH`W*0Bk3ldMF6xbZPh=IgB;+Jj`E50iOvZM(h)D
zIMzv;`6eL15NG;PH~a7FVpYzSAnVzthqo@D2D#P{la`#uJhaAb_s~<X(-o;AtU^5C
z+kUPjGCsJ}ixtf)e>i9EsOi4EMP?D{IpJ}c?Ff<yZ;4!;|6KiOK2R=*%cC#}e|$Xa
z2yF<hSPU9F*a3_pp^@0MYLFbIfnx#XrRnTGG$MkRM|0ADr1nPe)%q6<AQ%o!Ev><f
zjTj}+iOM2$EX7&^<QKziZs<g;@j&3mOqTpVo|@!V@`p*>hX-j-9lJj^&4OPX-VxjW
z;Z$O<8qDy(8+b*Y)rk5(j@R-;ELMAfZ~fHsOjI*rTW!~OT17sT9<7r;BcwL8v*}bs
zq5iLCX^LU?-`8~riDyH}$<x=AZuBr=&(R6lCZsOfy@0xN(l&`eL3=-wiHNaVzns`_
z<M#S6e&15B+v%(OeM+<VdMn>=sb%*1Y2R<Djz5Q^)%N<TUvH>Y_WGNE$bqPJI{T}L
zWfQBPIrgTP>*9+*X50kH*f+rAaf4fnZ&NoyCpeoN#*C<gsz6vTV8V@jX7qRlim}II
zIhgDO&f2rU2Vg5II<>rs8~!>KMdw!#`)HhZEHq7R-CvjiLz6TL`9x;rF0e?Cp=mdB
zFc0f{4!qiE;4?Bo0Cgj5*r?}NFQf$?0(LW27Uh^f;CdL!{58+>K?F94=6_)I3_H)L
z%E^&8Loyj{f~&P`>$QpWG39{MIj5~grWWU)d>#3p4_;sxE;#CD$56^0U+Wl!ykt1w
zLkib~xL+8!zAe-x!vMOk;|At@#Jx$WV8^)F8X{LF`o^jfWk4`TCzt$YzsjGHNOY0R
zyH0=9Vddn<kh?*wFS3jL8A7Ao4^0J`85K<Nff>fVuL|G1YqW)+!;gt=uq=A2rnD^i
zJyQDhXX#Al_`UC_#lo^)FjIcdR=kA5Dx!hy+!%tMcGV4C`-!l>Uih6TP#YUDVe(#n
z)EoWtY<yOHRTvheNWJd{7eN5>G+>@9%eR^vvJN+zA^$uTjhVF0%ASo??jStvgC`TL
zGSwAbL9_njKBQb?x6ENOJ}ix{wb7chF#9b-z<{^LSTc(D7&<*j+i56B9}~h4(cp}?
z((a=$?!qx0hYZBgb{|lb<U$*(VM@;CQpz8>{M<fd2!X-@sQ8G%tFJ$eTLL>PQ8n@Z
zZhpb6cV^6puKIy!ga)dMTw>h1$+ojG;iX)Y2Nlzmdfqu+Me3uwc0=6a@r93oT%Hr|
zL+S4;YtQMAY}9L6LFI)Yq1hx^l{&yXaJ`pBsV_p@@QKkY9P^pXt3nO0<o|8g7W5|g
z$yyf<qz!H_<WW74C{%S13TXumpczp@n7C0P=j$E;>!#$>o{Yck!yh~Dw_Kg^y|B@6
zn($Ac=@Qa3L-h-gf)q|IXg7Ak6%AcbOb*mWOptco7(JX^`Y6!T&(2F&!S3Rboj^lH
z6bjpTn=5y!VH%;>xE34j&!HH-a+%pF7aEGHS6Okyp_Ivsit|XwC&pc5x5g7#iBAKv
zvduRu1GSR(R)knEC&_EKEE)y9h3De==Z~e#P_|cSiSgLrm4N?hNu!o1LU^3U@~)(#
zhJylymjWLV$2OjI%M4km)t|(@No!50g26M(vy=>ds4e2d1RH5f{gps$87aAcJ)%0k
z=+V~Vk+sR9P2(~p55EN*ID<Vf`6l3B7M8HQz_~UZ40d(15Xw3jl3bx8n7-?pb`Up>
z)$rq3gSVEFUXb3w*AH7D(N(JwqLJkcuPGMJh8L#@0ghr@b2RS|SzPz{`~##E{KA#r
z!5`N_ejNiO3Z6SPhhF^4&pVvDN$#m9A>5;>9;*Gl!Zr76x;4~zQ*a2f3(Z`+>O;nZ
z!tlo?<YplY){!F2)Qa@Py!KM+;HDL#R7eLA%q)QL2;ozEMf@w?>Z{)dC`Xj9Fq-z6
z)!B~$*5}-op;@n2Wop793b`!@xY>hM4XSxmrFDBXyj;Fs6;vy(L62*|C}}cSg1;o!
z{Ty&9cF$OweMJ7gw-(hd*o8%944yJF^X$@}?YE`NU?zCy-NpYH$fN0&pPAE<Xd?NG
zYy##Qs0gi3aqnf73DPFSZTQ_#*U^9%Q)|4h#||0y9?ANP<!If{{GIw)2M#=BD~Bln
z4x9UcvyA@zJ(=OI!zbhG3)7ZmKzT1mx_SJ@p{YJI>4=&^<lJ_iL0TDY{3dudf9z-f
zc|>~DXoYvXyTqiOB~5R;LDQtzse;A8L(>9!JqWCv&j|t@Y$BVKCB_ee1y;mK$MX@@
z=Rh>}zoPG#XIT6>ZYzI8t%h*PLsm~#-sp3->pOI!m}ET-npb7Dt<d{k3qLJN<-st2
zksc#JX{ID+`?p*wb562{u9d@WjP1F`|8uM}+76T3?SnF?V7f~E8q_DMBljVh6o-}p
zV4ik4YO^g_>M$$}$(Su-@5-+23|Lf_?k&D9di^Df#L9tK6mt?^>>K#VhZxO7DYKdr
z4A#4;o&DDjf~G~2WO8R~5(Lz<;zeKcoQVzKtBK)%(yWp>;h|W(VnIU@ZdFTq@FT?6
zvloRR>OXIExW*d7DTw@4;UFL1OR_}Z_MyjW-jUfrciMdgYt@5-EIJs5%vId<?31sq
zW$T2qs`t0SVQhL`DBX6e<T%*=uH*(N8sYs{xjF``CL%9JWdpg~N<Z4t$a5mxWYemK
zH+X%<QhU`%yx-Xe_&cNkG>lk?5v;vEbf5ncZoW99AyyHC+NA8uZN;^;z8KWbi7Z_Z
zEkiRztz2XVTLjY$l+QnqhH~d0R}~|DNQ|ZJi&m|ymq+_^e1TeV1x3b~n_!JlAHi*@
z>uu^jR<n5^WaO>JDstY?oS!%%UexPOxEEF@p-wAwF%Vs<<XaUH$|&Z;0toB0fX)H(
zx#DaB&9S>FZVpza(>7McaHbC44!A2;H#M_=C)AG~)ZH^cRyu~4_Ecqca0(=wD$fe^
z7}xl0eJ|Sr=6Yd^L^0QXZ}0dPoEhG)4Ymn{892Va0d7%|rd<URQ@5JZR{MnK*e;N-
z#7RPdzoB?bzF^-j-OjYAdrgDuk9I?y(u@l5iS(+_W}m?oUe9&1Uh!TchrUxuyGzn8
z)S`2PjBVN{J!fF^>qSlbdO7-T{vGU1Jm4)Z?>V$IwX3NffhtuB73K6!(hQsq7v?<9
z!pnf7U^F^50_Ok+b9shJ4L1-g=BAMHmM`Ih6Vt1?^dFtjHa_!sKfs(gBK2(1x`&0)
z-ujK7-!`DQ<njGDr0>z*D7NAhOC07)7zmrpiYw9^&sm6(;X4Ibezm&&Uxi*NTTVr^
zcb6Ny%^94YnyEQ|0%rXFL>@^v<Qz3Jfh6UXA;LIG1psB0hfT`e6S`vqvtiyK>3oS?
zL6lY2KPxIAY)mc$pj~#?p}rJ+U|3>q9FmlIte6-m<J!GG7g|q)41C~DF>0moZ(@u*
z^(%R@&NVm>)OvG{F^Gn^>_HHSuvPbO<md}BI*DM4eT_QGiuFd=C@k=GhF>+<aP!vH
z`I!`0B9WJCgr<)iKGy)pb6hGpJ9U{-GnTCLNk;v$kWX}ecV)QXB!%t|)p=on5PJ?v
zZ<#@6SvJ{2h1+4sDqHbGi*ojVhxNs`bBFwHJs|oN|1M)$SIWyqb*_b|>VOP8E4SzV
zeKWlW85BUqx!h2Xmtt#4WT?^LWJ|t&1RLyZ4y8t{eRRWw2waAj1SW+ys$Okzb}D2t
zZ0mc;Ox2O;(V`6NEK0J<1=!ZfSup}?{_|Ux5MWWBB4Ex?I+>o<$oIEKD?nE-iI+uB
zGV!0Doj~sT!PpmGTIhbNo1<Jm-@OgmbO(%iwb@WH9sN!i-v~0JmMQs<)920Rk0m3<
z$NCzefhKdDcakrOf?k3&^w)Yhjz0;y*_1nW?UW#BpRki*8UA5QSflQ!!Ui&@ZfQF+
z?0qLr-TmuL1e*z0B!6-JnLAPPE>njIuBxz!agu_T;Qsf;`-_r5Yv|H0+l4iEN*S3p
zbt38{cQ58$SOg;rW_z;8&{Sj9msxrKF!#Bue;dcQ9TuP-X~7E55*&1Ujxnpf-{Fy5
zmY=z&qS;?W)nfaWc-a=tNwI7wLO~sf{#Avz$*3%O+2<+X9F0p$G)KLd_0XQJf8C?O
zF<kRjWveb+s<91VhwP4&ZB>4ZR}<lsCdr40>K0l--m&n59{|dm(=xCs`>-%P5t7NK
z#H;f6W_;ynAG%-T^Hieh-nxzD^+3MgeDzwr%I)t!io^;(>(u60yhE^Us=PRSqeMf<
zx60J3r->S1IRr3EnWDWk2g+Nwf1Vj@@g{8%ExX6!n+~4fc$O5`m-5kfgXJ?x+S9gs
zNI(e}-r77ikEC;<YsKr8R;y6kzP-+#auPv<h0-W7aeta^{{a3mVd&+2nnwh(7e5gA
zHH*Z?Pji)FbJ#uYOwVR^8|>jj=mZ*=JDw*#H?eLMpMVh7j3ty$Pc?Dd>2v!V+WE+{
zTZ0?NpCmi5OmnCK1l&n3+^XtgT;3|Lk>M2I^J#nEhovU=-61Q<%?<+4op{?yGTr?w
z6|!qI@A7>CkMV!dL^md#!6B@+$pS#=7oHR|0lddUk3bw%J;aLO>AmvnOCyV!MCJ@E
zn8k<uZI*WqZ^6!c%4t7`JF&jHH|Q=t5e40HYRt^-)&ApEjp|gR!j`iws%;X{ISUkp
ze#R`nUMxp%Ghi|8+w7%_IQ$Q)L@PJ=R5um6ut`og>o)R!^ISp3Z-dSmR{BE^S2U3r
zCvudB{G)wOsWY<jmBasAaqO2%Rxrzsz`4egjQ#2ckLchB`o6-5roP~<wzJm922dQ(
zFT`ef?%}8u^=ioV>JlZ*|9#worv#0^vo(wOf<c6eM7L1-Rf#WQufa_caUQi~AM2GE
zo;vZQ*m~rc=e}QkyWM=mreid=NPnK$&R@C6@CC|RY*$Cgz8G4NvJ+B+czo3zJN(It
zjBa+@*l7LsEbJ+;JUjfn<yrM6((>+L!9rl*t5^xs;cE&8`cJDv_d+CT@QK9W6=te6
zj!WW^V{1GEb<c=w%%Y+n;IsW*OsJ*A&*XEScI<uuHS<x_>vz6uq+J42jVPy$m|;sp
z>2^;PZf^S~b6+@=z%HAz3D#-nggFA32yB{UBqKa>m~k03ufLoAMK}xJy1lio)s){N
zN^CcsxIpgpqLSvV+{mFp`A9p({oB1{XOfgya4i*l-H)$A%|P9XeU^__{wS`E2OQbK
z-7~cq|19s>d>W1A-DrEWpG#;@RqzD9u2DA?xRdK>TvK8+L#sF5;7Y!VDxMeapZEW4
z-9qnL!9KTvGr$8q5mtkM6Y9Z^>UkO`8mQ5itjNiy^MWA`sXL11Q|brXzREox!V`_m
z;5I|vocl|=m-<%5e(hkaktU$ipj9{XlX~2`$fzTv_S==iX%rgYd`SzJUA`#eBi7NY
zMMJk({0b*N;uEnSKUG@LKZXA6m6Lg_sBLR!Hho}k9^%@8m^V2)JvQI{7CSr~g*O!6
zhGC#iDPT-k<~Y%?Qq?zi*+^u*1wgo*2LFfuHU!TPZ4iZHbu3rBxzyQDDeHA|Zs2H;
zLL<;13)Q-p%n(-iskugz$`pB5*^&)E4bAkxs(2`9Rjkyr1lRv8j&VL@{|37q=IQtf
zp^<Tm49l<p#auA@xd*9VXngzCDEDK6kdFF0-1!XF$k=b_w+SQK0p8?d8}QZ-;Nu_!
z>IH0v?(G5xd=xB*K^zaGfXi0EtW8$@#}XFQc$bDL2`1ers1IW1*1!OKfz=0EL#q_p
zxK8c$ahuuzp>M-rj3jJ+yST%63F!q9Evc@ORpL4l^^~n-?Seua(9q@bJpX{s+0cj_
z`idn+%2b+a-A38Hr$49rW(DL`<RPKSh^{Ro=xBLhx3Jm~6&4<*#iay?=v}P7q{H5P
zhz&`D!M08;!>B`)mhU&e%nAs-h*yl++a>@lq1~2DR9*Yc@_=<BZuLiwaL^oxprqAM
zspF&%(fJ@3Jxli?^o1MyUgnm5RZuJYN14{5Ykp!gI<g1pl24&jf*qni5#dlu^!zWf
zc=vc$eTG6a-TKAS5Uqr`;O*P#{~4Kz9qYE+$K(RT!X>)pM1r5^k}Xc;KVPwgyx{n!
zBDjdIiF2v{Z~P*_CGr-tg<Tl>2`5r5)E&CLvscJdJuGIa)<`-zd&JOW@B%Q()>upH
z^bbbhPw2p)i_qPes~ce4!J63KWSd}4YhYbbGW0r=tJEVhCZnrn^D{hgRGs(yAb0E|
zlPQ13v5IekK@E7@$_b`?#)N%AAu|e(|0UftN1jE}3&LPI$8J!tXsyHSQDW8qVQ2R7
z;hv+BQz)Lqw_8zD{QM-2Y$){uJ%UGvz6AHt+P{AhKA6yk^=@^!eKD-@MEyvswzIrm
zoe$fM_dwd*26&GHeHb+tkpzw0RSET{udp5iVBIFCZ3g@kYb4I>Ec${7{J%8V%#F!s
z+E0GJGWPU%jUFJ;j_i1FXZo^GOrcCGSzkNhE^ns47fE!YOef4UkU%bR*FJeQCT|!*
zkrzzRjvYOz9<G!Wn$_MzD>;#%z@dN!E!aJ))5ACszv}d(2$WErp|10@n9jyQT5S<b
z$LNetSnRjiTZFI8`59^NK87|ZtpP@I(yKnpTbny$88fwsap#1AIR_<f-MuvLN-)7_
z$zywdMxqBezaibFo}m10W(SpS-963TY}6Lfr+Rhmo)fnhriiPp=pUY?_dfb=d<h=G
zL0zeHHx0vKt!WzWZ^kd18pvxegm3&fmFG5yLc><)tt}V}8#nTPm%YI~awT(hZgg2w
z;yub@E)d$(*ap{Tk6yx5bQUh|M7oTAWJPJFex&{dyCj(B*m9gCZ@6!sE-vUGi|Z+0
zR?xITx9fuu0U9;2E%swkQ3o3e6_t!q(1badO^kh%s)FS~qyAorrtgv(EvfV(^Sm9;
zY<j`R^0F=dqxc%ils2?@O4cfVfWXuMmccK}j%2)-PVnNRCwJaBxt8^s4&eTiKMOm}
zaawG+3wus4DWvuLx)M_%5*Q-U>Y)~};Q0_*$DqYx2j79eNJW3YSfOqY7Cu10=OdTB
zh{%Pyd~Mjv;?6nEXbLaKfGgJ^v4Ks^Ma=nqMHfgErKM~$!?ez?S4Y}bmDcqnj)&y(
zs{=Q3it%cXuLL#1T}jo?q?}(DsUV2fHX%E@4Ef9OP}Ec2rcJ9Ft9l{fmy};cd?oFO
zD{6{?YG)?)V;^9Ef$s#1tvnFZBNeBL-kU)G3x|vpNpihki%g$tMeV!JH+=Oq`LhNa
zCvxwufy8ph?@fxyJlD8IKPagl=|t{M0!6o;5OQ*^C?c5P8GAnwC$EI1F8t8QPA8Ae
z#Dvs;KBEJ&g<ywbDl&fFbeHb0jU}Q4;;~}yQQlvvze2}}cZ{mFy*3T)!)2iNpZ{Y$
z++?+>+UWsXhXtc~GzVM)F~1AxvYydap!`C{3yQ&apJ9T{h<j{yhj0sQ1pCLCw%06?
z#Xi+Ma0jLgUs?Jl)>kU63<>%^zE(pK|2jaAAQa=H#-!K41%$C~a@j%7V_HlEi@x*w
z4oR0gd}fvhx?ZR(9YSG=0-uNjC@BSRw^eaKST|~;%lJu4(5(UklS2a3xf`6f+#K@(
z5#tRgg**RXMHCN=PUJGMWdtr1m}ouY&50#>tjEhmG-e{Lz3p+`b;ayZ{meX|ooyB9
z|0}@x*{fO)`i3=TvpnkDF!MJ<$V%**lhU|uyx&ib1OQQI^f3hBA^~<wcO&A|7F$Hp
z3F1;J^aPOu8EV{M(F(m)yfV*<Y7hc|P;3iFl&_U()h-z6DiK1m+RC5lJRm9B%Pe@A
zfpx=^Gk(Cg_-@(1*@DeKVY1n~Fc?wFk(6=;UVE=DrNFa`A!S>^oTzR{wav!vw(0Hr
z1v&UWJb+j8HpGec1g{Z;G;T1z??2j}O+0Cv2)#G-#o%xS^EanAPunEMD?a@+kkm8C
zSAlRE-^~!tfy@P$Guh3S7B20W9g7|!Z%p}_KLNeT2RUf}dY){GIzIP>YP#<<&Ero%
zzR8bG(@|4~o<LN7!|-_fBbLke@G+L){}H3JlmVDGw=^gNS1)F)31+g+ICEaKb>M|f
zhcq9bd7b4FPEUazMtD28_BsSuc?0F6Tu-W{oL^WP$=y))?U{3y*tq-0zk?i*M%6@3
zLKkKv15m3CJ~>OG73pq!1_CDd)YCRD15tp@O<gwpEohAjFMq!PK)ZSdXAHzxg!)UE
zILH)+(Rm=lA*hH*{BwtOrXKm*Ggd+BXyp1A&XP<X7Q=B*ps>I{L-yMK^cGc)gTIIA
zJ?FM%uj_T7SkMvZiu@p=^gTF>KWq_kCH`jJ7c3Kb+Iv)>ZiXUmzj5r9PAU61Wd}&i
za=;)S0@E7>WIZPKe6M^=0<EHa<WlDxpKQT|5{lyy#I#aYsbF<gr&4v!-YOqyvu}>y
z(|^}BkW`44E|n9-&|K4$Q_9kuIZ8^EZIxMdKss1y7NU=|8;%Bk95l6?^H^=%;y@wT
zW`2Hah}7N`I|Kk7>C>wOO*U<Ru9MK7C2xX6I}%|6whw4h+ob~jpUaG5cn{ZHc2TDn
zlI+ZkLXc~V@Wg@eUsy6|MeOUU=ppCF5~{R5%mS2a?JY<WSYKJpR5o9=sc?5i@24J_
z$y|3|a~hCwG7wF>F4Et?7h~8_rID?*g_me@O*WeN9Pzhy=%^kj@8E1YkNy3uhv9VR
zG7?27Unc9@di5OrQ~2c`CYq9>ziI|Gv)%n{0aY8UPKtvT&o@fazg!HIoR8OA%&%Gf
zT9kweB<7whR4fZ8Uo$p23@fb>+2yuKD(9g?eUlFUU~l-`cApY9@@(IX<b$9wzS!2z
z^hQOcLd>kGT3vaU4JaU)Dy=7Hw4GNwZo<34xEmylTMotztD_Y3RZ|iH9)^s`lSjo4
zRvxz)J7Wn;CFEA~uV@-F_8F7>w89kvD$fT}eX~R;7H_Kp&<98X-6qiZ%<IAvub!1l
zAcE}njPJ&Le7o;oNP}vZ=V`yzluX};?JL^21NYitsoZe^tGZT0lm4AJS|Rhk6WJ_6
zh>^>6fkpL`AH)a{w^9IUUruqp-Ghsibf8{x*F$$<$f*cskXkf`;e9(pUFt;P;NT9z
z=T!_2kgmbEEo=Ww<3GXRj3{dTP?u*jqn4T08x{=qI~AJ;OI#n)+GQU8@UfAm<RmrS
zRQ5!}9bxe7<LP_=bC0OrKgNwwu80cx{Ziu@;G$WX8pM9BoIEa{#rOYjLNth60Ti@(
z1gt^8!VJ8P8p)+`Fh^PG!#pn^c&AxeZzF5&8CZE;CK+0WjeGa7el`)S0oST?zc#x{
zpJA=rqzwL#fD5{z7zhBr)7U19D199zRa=7`ypEU0)<m|4QQWV!INKOKwEt(kMweL_
zdr}9-J~YNFH7*NT>xWTO#9Ak>E`CrD2zR`~Uay?F<Nc4ROKoA)y3;q#XkN2;u)813
z*s%P*swFjM`%I(pcwIiiW`H8b!)BR{*#l1Kk=6_Afy>llImYX@XnN}Op;$K+#8~Ih
zbw9Khgd%`WAS$mb=8W@>!kakKljoQCrTw&61G>733TPZ}W63H-oWKKytvPDFFmGOA
zCu*Liz2117E_a`XuKa2LgXQ+~iZP*?W$LMHa*V}NG^Iw3&ljd!Iyc$)FPqt#VLc{+
z9bSq+C%Zk+<z!Y!x1aL`?bM_!l`vIJD3qptZd)_puLp$R6clfWCSHj3>V+@Lx;k@L
z)fxx+jz=X>O}+|4{vUT{t&vv*XrteJxzs_+ETn<z0x35