Merge old head via |hg debugsetparents ace95bf5bd7b d55b99e80107|. CLOSED TREE DONTBUILD a=release
authorffxbld <release@mozilla.com>
Mon, 23 Feb 2015 10:21:24 -0500
changeset 247770 f9000ce9110111c97b11856fcdf1465b9f1d50ff
parent 247769 ace95bf5bd7bc2e14daf9d46679f14d38d313467 (diff)
parent 240867 d55b99e8010728b0c802e75e967d2a853122dd30 (current diff)
child 247771 187c73d085e5020ce0ae36096092f6680f5c8a6f
push id7677
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 18:11:24 +0000
treeherdermozilla-aurora@f531d838c055 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrelease
milestone38.0a1
Merge old head via |hg debugsetparents ace95bf5bd7b d55b99e80107|. CLOSED TREE DONTBUILD a=release
--- a/.hgtags
+++ b/.hgtags
@@ -97,40 +97,22 @@ 8d3810543edccf4fbe458178b88dd4a6e420b010
 ad0ae007aa9e03cd74e9005cd6652e544139b3b5 FIREFOX_AURORA_25_BASE
 2520866d58740851d862c7c59246a4e3f8b4a176 FIREFOX_AURORA_26_BASE
 05025f4889a0bf4dc99ce0c244c750adc002f015 FIREFOX_AURORA_27_BASE
 9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
 ba2cc1eda988a1614d8986ae145d28e1268409b9 FIREFOX_AURORA_29_BASE
 83c9853e136451474dfa6d1aaa60a7fca7d2d83a FIREFOX_AURORA_30_BASE
 cfde3603b0206e119abea76fdd6e134b634348f1 FIREFOX_AURORA_31_BASE
 16f3cac5e8fe471e12f76d6a94a477b14e78df7c FIREFOX_AURORA_32_BASE
-f11b164d75442617c4f046177d2ab913ed03a318 FIREFOX_BETA_31_BASE
-f11b164d75442617c4f046177d2ab913ed03a318 FIREFOX_AURORA_31_END
-dc2fd26b301375f15c935f00fe6283d3e3bc1efc B2G_2_0_20140609_MERGEDAY
-d69cd84b6824e018e0906cab0464e11e97a9bdca FIREFOX_BETA_32_BASE
-d69cd84b6824e018e0906cab0464e11e97a9bdca FIREFOX_BETA_32_BASE
-0000000000000000000000000000000000000000 FIREFOX_BETA_32_BASE
-0000000000000000000000000000000000000000 FIREFOX_BETA_32_BASE
-ac396ad5a32d60ae5b7eebe5416fdd46e9e12be1 FIREFOX_BETA_32_BASE
 dc23164ba2a289a8b22902e30990c77d9677c214 FIREFOX_AURORA_33_BASE
-a104ddcd4cdbf950f1755dfaf5a278d53570655f FIREFOX_AURORA_32_END
-114b010b6bf1a0efee03f003e54ed6fa00909972 FIREFOX_BETA_33_BASE
 c360f3d1c00d73b0c1fb0a2c0da525cb55e58b83 FIREFOX_AURORA_34_BASE
-9f1aad8e807cc283aafbc14caa3d4775e8d0535c FIREFOX_AURORA_33_END
-5b8210dcf52a795646bf0c8a32082a2ed7c4f537 B2G_2_1_20140902_MERGEDAY
-e85828ce78a80e2ccda98972d69d5f750335c4ab FIREFOX_BETA_34_BASE
 cec1a116c4f9a3e887d52e9a26e8bbec200fe162 FIREFOX_AURORA_35_BASE
-2608561c091ae83cc85e38740feffa5bfc6b5ed4 FIREFOX_AURORA_34_END
-390a34a40ea4e7f4d24b3ed83778e0f408411fcc FIREFOX_BETA_35_BASE
 ca89fe55717059e4e43040d16d260765ffa9dca7 FIREFOX_AURORA_36_BASE
 6047f510fb73c7dbe9866066fb01ddda3c170c9c FIREFOX_AURORA_37_BASE
 ca89fe55717059e4e43040d16d260765ffa9dca7 FIREFOX_AURORA_36_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_36_BASE
 6047f510fb73c7dbe9866066fb01ddda3c170c9c FIREFOX_AURORA_37_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_37_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_36_BASE
 b297a6727acfd21e757ddd38cd61894812666265 FIREFOX_AURORA_36_BASE
-0cf828669d5a0911b6f2b83d501eeef5bdf9905e FIREFOX_AURORA_35_END
-75177371cb85baaa9d623f56d849a5c21d18040f FIREFOX_BETA_36_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_37_BASE
 2c951493eef5b50b8085ef78ffe0d7902ff3d593 FIREFOX_AURORA_37_BASE
-1bc9beda018a42bdd5f63fc9fc46facf0c6f37ec FIREFOX_AURORA_36_END
-030fa1665346dfa94d1f72a1c7830644664ecf08 FIREFOX_BETA_37_BASE
+98086da94ccdc88f6de86774ce3d1fa258dc7c44 FIREFOX_AURORA_38_BASE
--- a/AUTHORS
+++ b/AUTHORS
@@ -9,16 +9,17 @@ contribution to Mozilla, see http://www.
 <1010mozilla@Ostermiller.com>
 Aaron Boodman <aa@google.com>
 Aaron Kaluszka <ask@swva.net>
 Aaron Leventhal <aaronleventhal@moonset.net>
 Aaron Nowack <anowack@mimiru.net>
 Aaron Reed <aaronr@us.ibm.com>
 Aaron Spangler <aaron@spangler.ods.org>
 Aaron Train <aaron.train@gmail.com>
+Abdelrhman Ahmed <a.ahmed1026@gmail.com>
 Achim Hasenmueller <achimha@innotek.de>
 ActiveState Tool Corp.
 Adam Barth <hk9565@gmail.com>
 Adam Christian <adam.christian@gmail.com>
 Adam Hauner
 Adam Lock <adamlock@netscape.com>
 Adam L. Peller
 Adam Souzis <adam@souzis.com>
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,10 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1121297 - Converted VolatileBuffer's CPP tests to GTests
+Bug 1119335 - (DOMString or sequence<DOMString> or ConstrainDOMStringParameters)
+needs binding flush (Bug 1103153).
new file mode 100644
--- /dev/null
+++ b/GNUmakefile
@@ -0,0 +1,14 @@
+# This Makefile is used as a shim to aid people with muscle memory
+# so that they can type "make".
+#
+# This file and all of its targets should not be used by anything important.
+
+all: build
+
+build:
+	./mach build
+
+clean:
+	./mach clobber
+
+.PHONY: all build clean
--- a/README.txt
+++ b/README.txt
@@ -21,8 +21,9 @@ are accessible on Google Groups, or news
 You can download nightly development builds from the Mozilla FTP server.
 Keep in mind that nightly builds, which are used by Mozilla developers for
 testing, may be buggy. Firefox nightlies, for example, can be found at:
 
     ftp://ftp.mozilla.org/pub/firefox/nightly/latest-trunk/
             - or -
     http://nightly.mozilla.org/
 
+
--- a/accessible/atk/AccessibleWrap.cpp
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -956,16 +956,50 @@ UpdateAtkRelation(RelationType aType, Ac
 }
 
 AtkRelationSet *
 refRelationSetCB(AtkObject *aAtkObj)
 {
   AtkRelationSet* relation_set =
     ATK_OBJECT_CLASS(parent_class)->ref_relation_set(aAtkObj);
 
+  const AtkRelationType typeMap[] = {
+#define RELATIONTYPE(gecko, s, atk, m, i) atk,
+#include "RelationTypeMap.h"
+#undef RELATIONTYPE
+  };
+
+  if (ProxyAccessible* proxy = GetProxy(aAtkObj)) {
+    nsTArray<RelationType> types;
+    nsTArray<nsTArray<ProxyAccessible*>> targetSets;
+    proxy->Relations(&types, &targetSets);
+
+    size_t relationCount = types.Length();
+    for (size_t i = 0; i < relationCount; i++) {
+      if (typeMap[static_cast<uint32_t>(types[i])] == ATK_RELATION_NULL)
+        continue;
+
+      size_t targetCount = targetSets[i].Length();
+      nsAutoTArray<AtkObject*, 5> wrappers;
+      for (size_t j = 0; j < targetCount; j++)
+        wrappers.AppendElement(GetWrapperFor(targetSets[i][j]));
+
+      AtkRelationType atkType = typeMap[static_cast<uint32_t>(types[i])];
+      AtkRelation* atkRelation =
+        atk_relation_set_get_relation_by_type(relation_set, atkType);
+      if (atkRelation)
+        atk_relation_set_remove(relation_set, atkRelation);
+
+      atkRelation = atk_relation_new(wrappers.Elements(), wrappers.Length(),
+                                     atkType);
+      atk_relation_set_add(relation_set, atkRelation);
+      g_object_unref(atkRelation);
+    }
+  }
+
   AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
   if (!accWrap)
     return relation_set;
 
 #define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
   UpdateAtkRelation(RelationType::geckoType, accWrap, atkType, relation_set);
 
 #include "RelationTypeMap.h"
@@ -1014,25 +1048,30 @@ GetProxy(AtkObject* aObj)
 
 AtkObject*
 GetWrapperFor(ProxyAccessible* aProxy)
 {
   return reinterpret_cast<AtkObject*>(aProxy->GetWrapper() & ~IS_PROXY);
 }
 
 static uint16_t
-GetInterfacesForProxy(ProxyAccessible* aProxy)
+GetInterfacesForProxy(ProxyAccessible* aProxy, uint32_t aInterfaces)
 {
-  return MAI_INTERFACE_COMPONENT;
+  uint16_t interfaces = 1 << MAI_INTERFACE_COMPONENT;
+  if (aInterfaces & Interfaces::HYPERTEXT)
+    interfaces |= (1 << MAI_INTERFACE_HYPERTEXT) | (1 << MAI_INTERFACE_TEXT)
+        | (1 << MAI_INTERFACE_EDITABLE_TEXT);
+
+  return interfaces;
 }
 
 void
-a11y::ProxyCreated(ProxyAccessible* aProxy)
+a11y::ProxyCreated(ProxyAccessible* aProxy, uint32_t aInterfaces)
 {
-  GType type = GetMaiAtkType(GetInterfacesForProxy(aProxy));
+  GType type = GetMaiAtkType(GetInterfacesForProxy(aProxy, aInterfaces));
   NS_ASSERTION(type, "why don't we have a type!");
 
   AtkObject* obj =
     reinterpret_cast<AtkObject *>
     (g_object_new(type, nullptr));
   if (!obj)
     return;
 
--- a/accessible/atk/AccessibleWrap.h
+++ b/accessible/atk/AccessibleWrap.h
@@ -47,21 +47,21 @@ class MaiHyperlink;
  */
 class AccessibleWrap : public Accessible
 {
 public:
   AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~AccessibleWrap();
   void ShutdownAtkObject();
 
-  virtual void Shutdown();
+  virtual void Shutdown() MOZ_OVERRIDE;
 
   // return the atk object for this AccessibleWrap
   virtual void GetNativeInterface(void** aOutAccessible) MOZ_OVERRIDE;
-  virtual nsresult HandleAccEvent(AccEvent* aEvent);
+  virtual nsresult HandleAccEvent(AccEvent* aEvent) MOZ_OVERRIDE;
 
   AtkObject * GetAtkObject(void);
   static AtkObject* GetAtkObject(Accessible* aAccessible);
 
   bool IsValidObject();
 
   // get/set the MaiHyperlink object for this AccessibleWrap
   MaiHyperlink* GetMaiHyperlink(bool aCreate = true);
--- a/accessible/atk/ApplicationAccessibleWrap.h
+++ b/accessible/atk/ApplicationAccessibleWrap.h
@@ -14,19 +14,19 @@ namespace a11y {
 
 class ApplicationAccessibleWrap: public ApplicationAccessible
 {
 public:
   ApplicationAccessibleWrap();
   virtual ~ApplicationAccessibleWrap();
 
   // Accessible
-  virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
+  virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) MOZ_OVERRIDE;
   virtual bool InsertChildAt(uint32_t aIdx, Accessible* aChild) MOZ_OVERRIDE;
-  virtual bool RemoveChild(Accessible* aChild);
+  virtual bool RemoveChild(Accessible* aChild) MOZ_OVERRIDE;
 
   /**
    * Return the atk object for app root accessible.
    */
   virtual void GetNativeInterface(void** aOutAccessible) MOZ_OVERRIDE;
 };
 
 } // namespace a11y
--- a/accessible/atk/AtkSocketAccessible.h
+++ b/accessible/atk/AtkSocketAccessible.h
@@ -40,17 +40,17 @@ public:
    * True if the current Atk version supports AtkSocket and it was correctly
    * loaded.
    */
   static bool gCanEmbed;
 
   AtkSocketAccessible(nsIContent* aContent, DocAccessible* aDoc,
                       const nsCString& aPlugId);
 
-  virtual void Shutdown();
+  virtual void Shutdown() MOZ_OVERRIDE;
 
   virtual void GetNativeInterface(void** aOutAccessible) MOZ_OVERRIDE;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/atk/nsMaiInterfaceText.cpp
+++ b/accessible/atk/nsMaiInterfaceText.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InterfaceInitFuncs.h"
 
 #include "Accessible-inl.h"
 #include "HyperTextAccessible-inl.h"
 #include "nsMai.h"
+#include "ProxyAccessible.h"
 
 #include "nsIAccessibleTypes.h"
 #include "nsIPersistentProperties2.h"
 #include "nsISimpleEnumerator.h"
 
 #include "mozilla/Likely.h"
 
 using namespace mozilla;
@@ -105,31 +106,33 @@ ConvertTexttoAsterisks(AccessibleWrap* a
 }
 
 extern "C" {
 
 static gchar*
 getTextCB(AtkText *aText, gint aStartOffset, gint aEndOffset)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
-  if (!accWrap)
-    return nullptr;
+  nsAutoString autoStr;
+  if (accWrap) {
+    HyperTextAccessible* text = accWrap->AsHyperText();
+    if (!text || !text->IsTextRole())
+      return nullptr;
 
-  HyperTextAccessible* text = accWrap->AsHyperText();
-  if (!text || !text->IsTextRole())
-    return nullptr;
-
-    nsAutoString autoStr;
     text->TextSubstring(aStartOffset, aEndOffset, autoStr);
 
     ConvertTexttoAsterisks(accWrap, autoStr);
-    NS_ConvertUTF16toUTF8 cautoStr(autoStr);
+  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+    proxy->TextSubstring(aStartOffset, aEndOffset, autoStr);
+  }
 
-    //copy and return, libspi will free it.
-    return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
+  NS_ConvertUTF16toUTF8 cautoStr(autoStr);
+
+  //copy and return, libspi will free it.
+  return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
 }
 
 static gchar*
 getTextAfterOffsetCB(AtkText *aText, gint aOffset,
                      AtkTextBoundary aBoundaryType,
                      gint *aStartOffset, gint *aEndOffset)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -361,16 +361,26 @@ static nsRoleMapEntry sWAIRoleMaps[] =
     kUseMapRole,
     eNoValue,
     eClickAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates,
     eARIACheckableBool
   },
+  { // none
+    &nsGkAtoms::none,
+    roles::NOTHING,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
   { // note
     &nsGkAtoms::note_,
     roles::NOTE,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     kGenericAccType,
@@ -706,17 +716,17 @@ static const AttrCharacteristics gWAIUni
   {&nsGkAtoms::aria_controls,          ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {&nsGkAtoms::aria_describedby,       ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {&nsGkAtoms::aria_disabled,          ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
   {&nsGkAtoms::aria_dropeffect,                         ATTR_VALTOKEN | ATTR_GLOBAL },
   {&nsGkAtoms::aria_expanded,          ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
   {&nsGkAtoms::aria_flowto,            ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {&nsGkAtoms::aria_grabbed,                            ATTR_VALTOKEN | ATTR_GLOBAL },
   {&nsGkAtoms::aria_haspopup,          ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
-  {&nsGkAtoms::aria_hidden,   ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL },
+  {&nsGkAtoms::aria_hidden,            ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL }, /* handled special way */
   {&nsGkAtoms::aria_invalid,           ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
   {&nsGkAtoms::aria_label,             ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {&nsGkAtoms::aria_labelledby,        ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
   {&nsGkAtoms::aria_level,             ATTR_BYPASSOBJ                               }, /* handled via groupPosition */
   {&nsGkAtoms::aria_live,                               ATTR_VALTOKEN | ATTR_GLOBAL },
   {&nsGkAtoms::aria_modal,             ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
   {&nsGkAtoms::aria_multiline,         ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
   {&nsGkAtoms::aria_multiselectable,   ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
@@ -793,16 +803,25 @@ aria::AttrCharacteristicsFor(nsIAtom* aA
 {
   for (uint32_t i = 0; i < ArrayLength(gWAIUnivAttrMap); i++)
     if (*gWAIUnivAttrMap[i].attributeName == aAtom)
       return gWAIUnivAttrMap[i].characteristics;
 
   return 0;
 }
 
+bool
+aria::HasDefinedARIAHidden(nsIContent* aContent)
+{
+  return aContent &&
+    nsAccUtils::HasDefinedARIAToken(aContent, nsGkAtoms::aria_hidden) &&
+    !aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_hidden,
+                           nsGkAtoms::_false, eCaseMatters);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // AttrIterator class
 
 bool
 AttrIterator::Next(nsAString& aAttrName, nsAString& aAttrValue)
 {
   while (mAttrIdx < mAttrCount) {
     const nsAttrName* attr = mContent->GetAttrNameAt(mAttrIdx);
--- a/accessible/base/ARIAMap.h
+++ b/accessible/base/ARIAMap.h
@@ -222,16 +222,21 @@ uint64_t UniversalStatesFor(mozilla::dom
  * Get the ARIA attribute characteristics for a given ARIA attribute.
  *
  * @param aAtom  ARIA attribute
  * @return       A bitflag representing the attribute characteristics
  *               (see above for possible bit masks, prefixed "ATTR_")
  */
 uint8_t AttrCharacteristicsFor(nsIAtom* aAtom);
 
+/**
+ * Return true if the element has defined aria-hidden.
+ */
+bool HasDefinedARIAHidden(nsIContent* aContent);
+
  /**
   * Represents a simple enumerator for iterating through ARIA attributes
   * exposed as object attributes on a given accessible.
   */
 class AttrIterator
 {
 public:
   explicit AttrIterator(nsIContent* aContent) :
--- a/accessible/base/AccCollector.h
+++ b/accessible/base/AccCollector.h
@@ -72,24 +72,24 @@ private:
  * vice versa.
  */
 class EmbeddedObjCollector MOZ_FINAL : public AccCollector
 {
 public:
   virtual ~EmbeddedObjCollector() { }
 
 public:
-  virtual int32_t GetIndexAt(Accessible* aAccessible);
+  virtual int32_t GetIndexAt(Accessible* aAccessible) MOZ_OVERRIDE;
 
 protected:
   // Make sure it's used by Accessible class only.
   explicit EmbeddedObjCollector(Accessible* aRoot) :
     AccCollector(aRoot, filters::GetEmbeddedObject) { }
 
-  virtual void AppendObject(Accessible* aAccessible);
+  virtual void AppendObject(Accessible* aAccessible) MOZ_OVERRIDE;
 
   friend class Accessible;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/base/AccEvent.h
+++ b/accessible/base/AccEvent.h
@@ -149,17 +149,17 @@ public:
 
   AccStateChangeEvent(Accessible* aAccessible, uint64_t aState) :
     AccEvent(::nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible,
              eAutoDetect, eCoalesceStateChange), mState(aState)
     { mIsEnabled = (mAccessible->State() & mState) != 0; }
 
   // AccEvent
   static const EventGroup kEventGroup = eStateChangeEvent;
-  virtual unsigned int GetEventGroups() const
+  virtual unsigned int GetEventGroups() const MOZ_OVERRIDE
   {
     return AccEvent::GetEventGroups() | (1U << eStateChangeEvent);
   }
 
   // AccStateChangeEvent
   uint64_t GetState() const { return mState; }
   bool IsStateEnabled() const { return mIsEnabled; }
 
@@ -178,17 +178,17 @@ class AccTextChangeEvent: public AccEven
 {
 public:
   AccTextChangeEvent(Accessible* aAccessible, int32_t aStart,
                      const nsAString& aModifiedText, bool aIsInserted,
                      EIsFromUserInput aIsFromUserInput = eAutoDetect);
 
   // AccEvent
   static const EventGroup kEventGroup = eTextChangeEvent;
-  virtual unsigned int GetEventGroups() const
+  virtual unsigned int GetEventGroups() const MOZ_OVERRIDE
   {
     return AccEvent::GetEventGroups() | (1U << eTextChangeEvent);
   }
 
   // AccTextChangeEvent
   int32_t GetStartOffset() const { return mStart; }
   uint32_t GetLength() const { return mModifiedText.Length(); }
   bool IsTextInserted() const { return mIsInserted; }
@@ -218,17 +218,17 @@ public:
     // Don't coalesce these since they are coalesced by reorder event. Coalesce
     // contained text change events.
     mParent = mAccessible->Parent();
   }
   virtual ~AccMutationEvent() { }
 
   // Event
   static const EventGroup kEventGroup = eMutationEvent;
-  virtual unsigned int GetEventGroups() const
+  virtual unsigned int GetEventGroups() const MOZ_OVERRIDE
   {
     return AccEvent::GetEventGroups() | (1U << eMutationEvent);
   }
 
   // MutationEvent
   bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; }
   bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; }
 
@@ -248,17 +248,17 @@ protected:
  */
 class AccHideEvent: public AccMutationEvent
 {
 public:
   AccHideEvent(Accessible* aTarget, nsINode* aTargetNode);
 
   // Event
   static const EventGroup kEventGroup = eHideEvent;
-  virtual unsigned int GetEventGroups() const
+  virtual unsigned int GetEventGroups() const MOZ_OVERRIDE
   {
     return AccMutationEvent::GetEventGroups() | (1U << eHideEvent);
   }
 
   // AccHideEvent
   Accessible* TargetParent() const { return mParent; }
   Accessible* TargetNextSibling() const { return mNextSibling; }
   Accessible* TargetPrevSibling() const { return mPrevSibling; }
@@ -276,17 +276,17 @@ protected:
  */
 class AccShowEvent: public AccMutationEvent
 {
 public:
   AccShowEvent(Accessible* aTarget, nsINode* aTargetNode);
 
   // Event
   static const EventGroup kEventGroup = eShowEvent;
-  virtual unsigned int GetEventGroups() const
+  virtual unsigned int GetEventGroups() const MOZ_OVERRIDE
   {
     return AccMutationEvent::GetEventGroups() | (1U << eShowEvent);
   }
 };
 
 
 /**
  * Class for reorder accessible event. Takes care about
@@ -296,17 +296,17 @@ class AccReorderEvent : public AccEvent
 public:
   explicit AccReorderEvent(Accessible* aTarget) :
     AccEvent(::nsIAccessibleEvent::EVENT_REORDER, aTarget,
              eAutoDetect, eCoalesceReorder) { }
   virtual ~AccReorderEvent() { }
 
   // Event
   static const EventGroup kEventGroup = eReorderEvent;
-  virtual unsigned int GetEventGroups() const
+  virtual unsigned int GetEventGroups() const MOZ_OVERRIDE
   {
     return AccEvent::GetEventGroups() | (1U << eReorderEvent);
   }
 
   /**
    * Get connected with mutation event.
    */
   void AddSubMutationEvent(AccMutationEvent* aEvent)
@@ -349,17 +349,17 @@ public:
                     EIsFromUserInput aIsFromUserInput = eAutoDetect) :
     AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible,
              aIsFromUserInput),
     mCaretOffset(aCaretOffset) { }
   virtual ~AccCaretMoveEvent() { }
 
   // AccEvent
   static const EventGroup kEventGroup = eCaretMoveEvent;
-  virtual unsigned int GetEventGroups() const
+  virtual unsigned int GetEventGroups() const MOZ_OVERRIDE
   {
     return AccEvent::GetEventGroups() | (1U << eCaretMoveEvent);
   }
 
   // AccCaretMoveEvent
   int32_t GetCaretOffset() const { return mCaretOffset; }
 
 private:
@@ -375,17 +375,17 @@ class AccTextSelChangeEvent : public Acc
 public:
   AccTextSelChangeEvent(HyperTextAccessible* aTarget,
                         dom::Selection* aSelection,
                         int32_t aReason);
   virtual ~AccTextSelChangeEvent();
 
   // AccEvent
   static const EventGroup kEventGroup = eTextSelChangeEvent;
-  virtual unsigned int GetEventGroups() const
+  virtual unsigned int GetEventGroups() const MOZ_OVERRIDE
   {
     return AccEvent::GetEventGroups() | (1U << eTextSelChangeEvent);
   }
 
   // AccTextSelChangeEvent
 
   /**
    * Return true if the text selection change wasn't caused by pure caret move.
@@ -414,17 +414,17 @@ public:
 
   AccSelChangeEvent(Accessible* aWidget, Accessible* aItem,
                     SelChangeType aSelChangeType);
 
   virtual ~AccSelChangeEvent() { }
 
   // AccEvent
   static const EventGroup kEventGroup = eSelectionChangeEvent;
-  virtual unsigned int GetEventGroups() const
+  virtual unsigned int GetEventGroups() const MOZ_OVERRIDE
   {
     return AccEvent::GetEventGroups() | (1U << eSelectionChangeEvent);
   }
 
   // AccSelChangeEvent
   Accessible* Widget() const { return mWidget; }
 
 private:
@@ -444,17 +444,17 @@ private:
 class AccTableChangeEvent : public AccEvent
 {
 public:
   AccTableChangeEvent(Accessible* aAccessible, uint32_t aEventType,
                       int32_t aRowOrColIndex, int32_t aNumRowsOrCols);
 
   // AccEvent
   static const EventGroup kEventGroup = eTableChangeEvent;
-  virtual unsigned int GetEventGroups() const
+  virtual unsigned int GetEventGroups() const MOZ_OVERRIDE
   {
     return AccEvent::GetEventGroups() | (1U << eTableChangeEvent);
   }
 
   // AccTableChangeEvent
   uint32_t GetIndex() const { return mRowOrColIndex; }
   uint32_t GetCount() const { return mNumRowsOrCols; }
 
@@ -474,17 +474,17 @@ public:
                    int32_t aOldStart, int32_t aOldEnd,
                    int16_t aReason,
                    EIsFromUserInput aIsFromUserInput = eFromUserInput);
 
   virtual ~AccVCChangeEvent() { }
 
   // AccEvent
   static const EventGroup kEventGroup = eVirtualCursorChangeEvent;
-  virtual unsigned int GetEventGroups() const
+  virtual unsigned int GetEventGroups() const MOZ_OVERRIDE
   {
     return AccEvent::GetEventGroups() | (1U << eVirtualCursorChangeEvent);
   }
 
   // AccTableChangeEvent
   Accessible* OldAccessible() const { return mOldAccessible; }
   int32_t OldStartOffset() const { return mOldStart; }
   int32_t OldEndOffset() const { return mOldEnd; }
@@ -504,17 +504,17 @@ class AccObjectAttrChangedEvent: public 
 {
 public:
   AccObjectAttrChangedEvent(Accessible* aAccessible, nsIAtom* aAttribute) :
     AccEvent(::nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED, aAccessible),
     mAttribute(aAttribute) { }
 
   // AccEvent
   static const EventGroup kEventGroup = eObjectAttrChangedEvent;
-  virtual unsigned int GetEventGroups() const
+  virtual unsigned int GetEventGroups() const MOZ_OVERRIDE
   {
     return AccEvent::GetEventGroups() | (1U << eObjectAttrChangedEvent);
   }
 
   // AccObjectAttrChangedEvent
   nsIAtom* GetAttribute() const { return mAttribute; }
 
 private:
--- a/accessible/base/AccGroupInfo.cpp
+++ b/accessible/base/AccGroupInfo.cpp
@@ -59,20 +59,20 @@ AccGroupInfo::Update()
     }
 
     // Skip subset.
     if (siblingLevel > level)
       continue;
 
     // If the previous item in the group has calculated group information then
     // build group information for this item based on found one.
-    if (sibling->mGroupInfo) {
-      mPosInSet += sibling->mGroupInfo->mPosInSet;
-      mParent = sibling->mGroupInfo->mParent;
-      mSetSize = sibling->mGroupInfo->mSetSize;
+    if (sibling->mBits.groupInfo) {
+      mPosInSet += sibling->mBits.groupInfo->mPosInSet;
+      mParent = sibling->mBits.groupInfo->mParent;
+      mSetSize = sibling->mBits.groupInfo->mSetSize;
       return;
     }
 
     mPosInSet++;
   }
 
   // Compute set size.
   mSetSize = mPosInSet;
@@ -96,19 +96,19 @@ AccGroupInfo::Update()
       break;
 
     // Skip subset.
     if (siblingLevel > level)
       continue;
 
     // If the next item in the group has calculated group information then
     // build group information for this item based on found one.
-    if (sibling->mGroupInfo) {
-      mParent = sibling->mGroupInfo->mParent;
-      mSetSize = sibling->mGroupInfo->mSetSize;
+    if (sibling->mBits.groupInfo) {
+      mParent = sibling->mBits.groupInfo->mParent;
+      mSetSize = sibling->mBits.groupInfo->mSetSize;
       return;
     }
 
     mSetSize++;
   }
 
   if (mParent)
     return;
--- a/accessible/base/AccIterator.cpp
+++ b/accessible/base/AccIterator.cpp
@@ -327,16 +327,74 @@ Accessible*
 IDRefsIterator::Next()
 {
   nsIContent* nextElm = NextElem();
   return nextElm ? mDoc->GetAccessible(nextElm) : nullptr;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
+// ARIAOwnedByIterator
+////////////////////////////////////////////////////////////////////////////////
+
+ARIAOwnedByIterator::ARIAOwnedByIterator(const Accessible* aDependent) :
+  RelatedAccIterator(aDependent->Document(), aDependent->GetContent(),
+                     nsGkAtoms::aria_owns), mDependent(aDependent)
+{
+}
+
+Accessible*
+ARIAOwnedByIterator::Next()
+{
+  Accessible* owner = RelatedAccIterator::Next();
+  Accessible* cur = owner;
+  while (cur) {
+    if (cur == mDependent)
+      return Next(); // owner cannot be a child of dependent.
+
+    if (cur->IsDoc())
+      break; // don't cross document boundaries
+
+    cur = cur->Parent();
+  }
+
+  return owner;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// ARIAOwnsIterator
+////////////////////////////////////////////////////////////////////////////////
+
+ARIAOwnsIterator::ARIAOwnsIterator(const Accessible* aOwner) :
+  mIter(aOwner->Document(), aOwner->GetContent(), nsGkAtoms::aria_owns),
+  mOwner(aOwner)
+{
+}
+
+Accessible*
+ARIAOwnsIterator::Next()
+{
+  Accessible* child = mIter.Next();
+  const Accessible* cur = mOwner;
+  while (cur) {
+    if (cur == child)
+      return Next(); // cannot own its own parent
+
+    if (cur->IsDoc())
+      break; // don't cross document boundaries
+
+    cur = cur->Parent();
+  }
+
+  return child;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
 // SingleAccIterator
 ////////////////////////////////////////////////////////////////////////////////
 
 Accessible*
 SingleAccIterator::Next()
 {
   nsRefPtr<Accessible> nextAcc;
   mAcc.swap(nextAcc);
--- a/accessible/base/AccIterator.h
+++ b/accessible/base/AccIterator.h
@@ -38,17 +38,17 @@ class AccIterator : public AccIterable
 public:
   AccIterator(Accessible* aRoot, filters::FilterFuncPtr aFilterFunc);
   virtual ~AccIterator();
 
   /**
    * Return next accessible complying with filter function. Return the first
    * accessible for the first time.
    */
-  virtual Accessible* Next();
+  virtual Accessible* Next() MOZ_OVERRIDE;
 
 private:
   AccIterator();
   AccIterator(const AccIterator&);
   AccIterator& operator =(const AccIterator&);
 
   struct IteratorState
   {
@@ -84,17 +84,17 @@ public:
   RelatedAccIterator(DocAccessible* aDocument, nsIContent* aDependentContent,
                      nsIAtom* aRelAttr);
 
   virtual ~RelatedAccIterator() { }
 
   /**
    * Return next related accessible for the given dependent accessible.
    */
-  virtual Accessible* Next();
+  virtual Accessible* Next() MOZ_OVERRIDE;
 
 private:
   RelatedAccIterator();
   RelatedAccIterator(const RelatedAccIterator&);
   RelatedAccIterator& operator = (const RelatedAccIterator&);
 
   DocAccessible* mDocument;
   nsIAtom* mRelAttr;
@@ -118,17 +118,17 @@ public:
   HTMLLabelIterator(DocAccessible* aDocument, const Accessible* aAccessible,
                     LabelFilter aFilter = eAllLabels);
 
   virtual ~HTMLLabelIterator() { }
 
   /**
    * Return next label accessible associated with the given element.
    */
-  virtual Accessible* Next();
+  virtual Accessible* Next() MOZ_OVERRIDE;
 
 private:
   HTMLLabelIterator();
   HTMLLabelIterator(const HTMLLabelIterator&);
   HTMLLabelIterator& operator = (const HTMLLabelIterator&);
 
   RelatedAccIterator mRelIter;
   // XXX: replace it on weak reference (bug 678429), it's safe to use raw
@@ -145,17 +145,17 @@ class HTMLOutputIterator : public AccIte
 {
 public:
   HTMLOutputIterator(DocAccessible* aDocument, nsIContent* aElement);
   virtual ~HTMLOutputIterator() { }
 
   /**
    * Return next output accessible associated with the given element.
    */
-  virtual Accessible* Next();
+  virtual Accessible* Next() MOZ_OVERRIDE;
 
 private:
   HTMLOutputIterator();
   HTMLOutputIterator(const HTMLOutputIterator&);
   HTMLOutputIterator& operator = (const HTMLOutputIterator&);
 
   RelatedAccIterator mRelIter;
 };
@@ -168,17 +168,17 @@ class XULLabelIterator : public AccItera
 {
 public:
   XULLabelIterator(DocAccessible* aDocument, nsIContent* aElement);
   virtual ~XULLabelIterator() { }
 
   /**
    * Return next label accessible associated with the given element.
    */
-  virtual Accessible* Next();
+  virtual Accessible* Next() MOZ_OVERRIDE;
 
 private:
   XULLabelIterator();
   XULLabelIterator(const XULLabelIterator&);
   XULLabelIterator& operator = (const XULLabelIterator&);
 
   RelatedAccIterator mRelIter;
 };
@@ -191,17 +191,17 @@ class XULDescriptionIterator : public Ac
 {
 public:
   XULDescriptionIterator(DocAccessible* aDocument, nsIContent* aElement);
   virtual ~XULDescriptionIterator() { }
 
   /**
    * Return next description accessible associated with the given element.
    */
-  virtual Accessible* Next();
+  virtual Accessible* Next() MOZ_OVERRIDE;
 
 private:
   XULDescriptionIterator();
   XULDescriptionIterator(const XULDescriptionIterator&);
   XULDescriptionIterator& operator = (const XULDescriptionIterator&);
 
   RelatedAccIterator mRelIter;
 };
@@ -229,40 +229,82 @@ public:
   nsIContent* NextElem();
 
   /**
    * Return the element with the given ID.
    */
   nsIContent* GetElem(const nsDependentSubstring& aID);
 
   // AccIterable
-  virtual Accessible* Next();
+  virtual Accessible* Next() MOZ_OVERRIDE;
 
 private:
   IDRefsIterator();
   IDRefsIterator(const IDRefsIterator&);
   IDRefsIterator operator = (const IDRefsIterator&);
 
   nsString mIDs;
   nsIContent* mContent;
   DocAccessible* mDoc;
   nsAString::index_type mCurrIdx;
 };
 
+
+/**
+ * Iterates over related accessible referred by aria-owns.
+ */
+class ARIAOwnedByIterator MOZ_FINAL : public RelatedAccIterator
+{
+public:
+  explicit ARIAOwnedByIterator(const Accessible* aDependent);
+  virtual ~ARIAOwnedByIterator() { }
+
+  virtual Accessible* Next() MOZ_OVERRIDE;
+
+private:
+  ARIAOwnedByIterator() = delete;
+  ARIAOwnedByIterator(const ARIAOwnedByIterator&) = delete;
+  ARIAOwnedByIterator& operator = (const ARIAOwnedByIterator&) = delete;
+
+  const Accessible* mDependent;
+};
+
+
+/**
+ * Iterates over related accessible referred by aria-owns.
+ */
+class ARIAOwnsIterator MOZ_FINAL : public AccIterable
+{
+public:
+  explicit ARIAOwnsIterator(const Accessible* aOwner);
+  virtual ~ARIAOwnsIterator() { }
+
+  virtual Accessible* Next() MOZ_OVERRIDE;
+
+private:
+  ARIAOwnsIterator() = delete;
+  ARIAOwnsIterator(const ARIAOwnsIterator&) = delete;
+  ARIAOwnsIterator& operator = (const ARIAOwnsIterator&) = delete;
+
+  IDRefsIterator mIter;
+  const Accessible* mOwner;
+};
+
+
 /**
  * Iterator that points to a single accessible returning it on the first call
  * to Next().
  */
 class SingleAccIterator : public AccIterable
 {
 public:
   explicit SingleAccIterator(Accessible* aTarget): mAcc(aTarget) { }
   virtual ~SingleAccIterator() { }
 
-  virtual Accessible* Next();
+  virtual Accessible* Next() MOZ_OVERRIDE;
 
 private:
   SingleAccIterator();
   SingleAccIterator(const SingleAccIterator&);
   SingleAccIterator& operator = (const SingleAccIterator&);
 
   nsRefPtr<Accessible> mAcc;
 };
@@ -273,17 +315,17 @@ private:
  */
 class ItemIterator : public AccIterable
 {
 public:
   explicit ItemIterator(Accessible* aItemContainer) :
     mContainer(aItemContainer), mAnchor(nullptr) { }
   virtual ~ItemIterator() { }
 
-  virtual Accessible* Next();
+  virtual Accessible* Next() MOZ_OVERRIDE;
 
 private:
   ItemIterator() = delete;
   ItemIterator(const ItemIterator&) = delete;
   ItemIterator& operator = (const ItemIterator&) = delete;
 
   Accessible* mContainer;
   Accessible* mAnchor;
@@ -295,17 +337,17 @@ private:
  */
 class XULTreeItemIterator : public AccIterable
 {
 public:
   XULTreeItemIterator(XULTreeAccessible* aXULTree, nsITreeView* aTreeView,
                       int32_t aRowIdx);
   virtual ~XULTreeItemIterator() { }
 
-  virtual Accessible* Next();
+  virtual Accessible* Next() MOZ_OVERRIDE;
 
 private:
   XULTreeItemIterator() = delete;
   XULTreeItemIterator(const XULTreeItemIterator&) = delete;
   XULTreeItemIterator& operator = (const XULTreeItemIterator&) = delete;
 
   XULTreeAccessible* mXULTree;
   nsITreeView* mTreeView;
--- a/accessible/base/AccTypes.h
+++ b/accessible/base/AccTypes.h
@@ -47,16 +47,17 @@ enum AccType {
 
   /**
    * Other accessible types.
    */
   eApplicationType,
   eHTMLOptGroupType,
   eImageMapType,
   eMenuPopupType,
+  eProxyType,
   eProgressType,
   eRootType,
   eXULLabelType,
   eXULListItemType,
   eXULTabpanelsType,
   eXULTreeType,
 
   eLastAccType = eXULTreeType
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -544,10 +544,10 @@ DocManager::RemoteDocAdded(DocAccessible
   if (!sRemoteDocuments) {
     sRemoteDocuments = new nsTArray<DocAccessibleParent*>;
     ClearOnShutdown(&sRemoteDocuments);
   }
 
   MOZ_ASSERT(!sRemoteDocuments->Contains(aDoc),
       "How did we already have the doc!");
   sRemoteDocuments->AppendElement(aDoc);
-  ProxyCreated(aDoc);
+  ProxyCreated(aDoc, 0);
 }
--- a/accessible/base/FocusManager.cpp
+++ b/accessible/base/FocusManager.cpp
@@ -319,19 +319,17 @@ FocusManager::ProcessFocusEvent(AccEvent
           continue;
         }
       }
 
       // If no required context role then check aria-owns relation.
       if (!tryOwnsParent)
         break;
 
-      RelatedAccIterator iter(child->Document(), child->GetContent(),
-                              nsGkAtoms::aria_owns);
-      parent = iter.Next();
+      parent = ARIAOwnedByIterator(child).Next();
       tryOwnsParent = false;
     }
 
     if (ARIAMenubar != mActiveARIAMenubar) {
       // Leaving ARIA menu. Fire menu_end event on current menubar.
       if (mActiveARIAMenubar) {
         nsRefPtr<AccEvent> menuEndEvent =
           new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
--- a/accessible/base/NotificationController.h
+++ b/accessible/base/NotificationController.h
@@ -59,17 +59,17 @@ class TNotification : public Notificatio
 {
 public:
   typedef void (Class::*Callback)(Arg*);
 
   TNotification(Class* aInstance, Callback aCallback, Arg* aArg) :
     mInstance(aInstance), mCallback(aCallback), mArg(aArg) { }
   virtual ~TNotification() { mInstance = nullptr; }
 
-  virtual void Process()
+  virtual void Process() MOZ_OVERRIDE
   {
     (mInstance->*mCallback)(mArg);
 
     mInstance = nullptr;
     mCallback = nullptr;
     mArg = nullptr;
   }
 
@@ -86,18 +86,18 @@ private:
  * Used to process notifications from core for the document accessible.
  */
 class NotificationController MOZ_FINAL : public EventQueue,
                                          public nsARefreshObserver
 {
 public:
   NotificationController(DocAccessible* aDocument, nsIPresShell* aPresShell);
 
-  NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
-  NS_IMETHOD_(MozExternalRefCountType) Release(void);
+  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) MOZ_OVERRIDE;
+  NS_IMETHOD_(MozExternalRefCountType) Release(void) MOZ_OVERRIDE;
 
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController)
 
   /**
    * Shutdown the notification controller.
    */
   void Shutdown();
 
@@ -198,17 +198,17 @@ protected:
    */
   bool IsUpdatePending();
 
 private:
   NotificationController(const NotificationController&);
   NotificationController& operator = (const NotificationController&);
 
   // nsARefreshObserver
-  virtual void WillRefresh(mozilla::TimeStamp aTime);
+  virtual void WillRefresh(mozilla::TimeStamp aTime) MOZ_OVERRIDE;
 
 private:
   /**
    * Indicates whether we're waiting on an event queue processing from our
    * notification controller to flush events.
    */
   enum eObservingState {
     eNotObservingRefresh,
--- a/accessible/base/Platform.h
+++ b/accessible/base/Platform.h
@@ -50,17 +50,17 @@ void PlatformInit();
  * Note this is called before internal accessibility support is shutdown.
  */
 void PlatformShutdown();
 
 /**
  * called when a new ProxyAccessible is created, so the platform may setup a
  * wrapper for it, or take other action.
  */
-void ProxyCreated(ProxyAccessible*);
+void ProxyCreated(ProxyAccessible* aProxy, uint32_t aInterfaces);
 
 /**
  * Called just before a ProxyAccessible is destroyed so its wrapper can be
  * disposed of and other action taken.
  */
 void ProxyDestroyed(ProxyAccessible*);
 
 /**
--- a/accessible/base/RelationType.h
+++ b/accessible/base/RelationType.h
@@ -2,22 +2,20 @@
 /* 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_relationtype_h_
 #define mozilla_a11y_relationtype_h_
 
-#include "mozilla/TypedEnum.h"
-
 namespace mozilla {
 namespace a11y {
 
-MOZ_BEGIN_ENUM_CLASS(RelationType)
+enum class RelationType {
 
   /**
    * This object is labelled by a target object.
    */
   LABELLED_BY = 0x00,
 
   /**
    * This object is label for a target object.
@@ -126,14 +124,14 @@ MOZ_BEGIN_ENUM_CLASS(RelationType)
 
   /**
    * The target object is the containing application object.
    */
   CONTAINING_APPLICATION = 0x14,
 
   LAST = CONTAINING_APPLICATION
 
-MOZ_END_ENUM_CLASS(RelationType)
+};
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/base/TextAttrs.h
+++ b/accessible/base/TextAttrs.h
@@ -125,17 +125,17 @@ protected:
   template<class T>
   class TTextAttr : public TextAttr
   {
   public:
     explicit TTextAttr(bool aGetRootValue) : mGetRootValue(aGetRootValue) {}
 
     // TextAttr
     virtual void Expose(nsIPersistentProperties* aAttributes,
-                        bool aIncludeDefAttrValue)
+                        bool aIncludeDefAttrValue) MOZ_OVERRIDE
     {
       if (mGetRootValue) {
         if (mIsRootDefined)
           ExposeValue(aAttributes, mRootNativeValue);
         return;
       }
 
       if (mIsDefined) {
@@ -143,17 +143,17 @@ protected:
           ExposeValue(aAttributes, mNativeValue);
         return;
       }
 
       if (aIncludeDefAttrValue && mIsRootDefined)
         ExposeValue(aAttributes, mRootNativeValue);
     }
 
-    virtual bool Equal(Accessible* aAccessible)
+    virtual bool Equal(Accessible* aAccessible) MOZ_OVERRIDE
     {
       T nativeValue;
       bool isDefined = GetValueFor(aAccessible, &nativeValue);
 
       if (!mIsDefined && !isDefined)
         return true;
 
       if (mIsDefined && isDefined)
@@ -198,19 +198,19 @@ protected:
   public:
     LangTextAttr(HyperTextAccessible* aRoot, nsIContent* aRootElm,
                  nsIContent* aElm);
     virtual ~LangTextAttr();
 
   protected:
 
     // TextAttr
-    virtual bool GetValueFor(Accessible* aAccessible, nsString* aValue);
+    virtual bool GetValueFor(Accessible* aAccessible, nsString* aValue) MOZ_OVERRIDE;
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const nsString& aValue);
+                             const nsString& aValue) MOZ_OVERRIDE;
 
   private:
     nsCOMPtr<nsIContent> mRootContent;
   };
 
 
   /**
    * Class is used for the 'invalid' text attribute. Note, it calculated
@@ -229,19 +229,19 @@ protected:
     enum {
       eFalse,
       eGrammar,
       eSpelling,
       eTrue
     };
 
     // TextAttr
-    virtual bool GetValueFor(Accessible* aAccessible, uint32_t* aValue);
+    virtual bool GetValueFor(Accessible* aAccessible, uint32_t* aValue) MOZ_OVERRIDE;
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const uint32_t& aValue);
+                             const uint32_t& aValue) MOZ_OVERRIDE;
 
   private:
     bool GetValue(nsIContent* aElm, uint32_t* aValue);
     nsIContent* mRootElm;
   };
 
 
   /**
@@ -251,19 +251,20 @@ protected:
   {
   public:
     BGColorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
     virtual ~BGColorTextAttr() { }
 
   protected:
 
     // TextAttr
-    virtual bool GetValueFor(Accessible* aAccessible, nscolor* aValue);
+    virtual bool GetValueFor(Accessible* aAccessible, nscolor* aValue)
+      MOZ_OVERRIDE;
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const nscolor& aValue);
+                             const nscolor& aValue) MOZ_OVERRIDE;
 
   private:
     bool GetColor(nsIFrame* aFrame, nscolor* aColor);
     nsIFrame* mRootFrame;
   };
 
 
   /**
@@ -273,37 +274,39 @@ protected:
   {
   public:
     ColorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
     virtual ~ColorTextAttr() { }
 
   protected:
 
     // TTextAttr
-    virtual bool GetValueFor(Accessible* aAccessible, nscolor* aValue);
+    virtual bool GetValueFor(Accessible* aAccessible, nscolor* aValue)
+      MOZ_OVERRIDE;
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const nscolor& aValue);
+                             const nscolor& aValue) MOZ_OVERRIDE;
   };
 
 
   /**
    * Class is used for the work with "font-family" text attribute.
    */
   class FontFamilyTextAttr : public TTextAttr<nsString>
   {
   public:
     FontFamilyTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
     virtual ~FontFamilyTextAttr() { }
 
   protected:
 
     // TTextAttr
-    virtual bool GetValueFor(Accessible* aAccessible, nsString* aValue);
+    virtual bool GetValueFor(Accessible* aAccessible, nsString* aValue)
+      MOZ_OVERRIDE;
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const nsString& aValue);
+                             const nsString& aValue) MOZ_OVERRIDE;
 
   private:
 
     bool GetFontFamily(nsIFrame* aFrame, nsString& aFamily);
   };
 
 
   /**
@@ -313,19 +316,20 @@ protected:
   {
   public:
     FontSizeTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
     virtual ~FontSizeTextAttr() { }
 
   protected:
 
     // TTextAttr
-    virtual bool GetValueFor(Accessible* aAccessible, nscoord* aValue);
+    virtual bool GetValueFor(Accessible* aAccessible, nscoord* aValue)
+      MOZ_OVERRIDE;
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const nscoord& aValue);
+                             const nscoord& aValue) MOZ_OVERRIDE;
 
   private:
     nsDeviceContext* mDC;
   };
 
 
   /**
    * Class is used for the work with "font-style" text attribute.
@@ -334,37 +338,39 @@ protected:
   {
   public:
     FontStyleTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
     virtual ~FontStyleTextAttr() { }
 
   protected:
 
     // TTextAttr
-    virtual bool GetValueFor(Accessible* aContent, nscoord* aValue);
+    virtual bool GetValueFor(Accessible* aContent, nscoord* aValue)
+      MOZ_OVERRIDE;
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const nscoord& aValue);
+                             const nscoord& aValue) MOZ_OVERRIDE;
   };
 
 
   /**
    * Class is used for the work with "font-weight" text attribute.
    */
   class FontWeightTextAttr : public TTextAttr<int32_t>
   {
   public:
     FontWeightTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
     virtual ~FontWeightTextAttr() { }
 
   protected:
 
     // TTextAttr
-    virtual bool GetValueFor(Accessible* aAccessible, int32_t* aValue);
+    virtual bool GetValueFor(Accessible* aAccessible, int32_t* aValue)
+      MOZ_OVERRIDE;
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const int32_t& aValue);
+                             const int32_t& aValue) MOZ_OVERRIDE;
 
   private:
     int32_t GetFontWeight(nsIFrame* aFrame);
   };
 
   /**
    * Class is used for the work with 'auto-generated' text attribute.
    */
@@ -372,19 +378,20 @@ protected:
   {
   public:
     AutoGeneratedTextAttr(HyperTextAccessible* aHyperTextAcc,
                           Accessible* aAccessible);
     virtual ~AutoGeneratedTextAttr() { }
 
   protected:
     // TextAttr
-    virtual bool GetValueFor(Accessible* aAccessible, bool* aValue);
+    virtual bool GetValueFor(Accessible* aAccessible, bool* aValue)
+      MOZ_OVERRIDE;
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const bool& aValue);
+                             const bool& aValue) MOZ_OVERRIDE;
   };
 
 
   /**
    * TextDecorTextAttr class is used for the work with
    * "text-line-through-style", "text-line-through-color",
    * "text-underline-style" and "text-underline-color" text attributes.
    */
@@ -423,19 +430,20 @@ protected:
   {
   public:
     TextDecorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
     virtual ~TextDecorTextAttr() { }
 
   protected:
 
     // TextAttr
-    virtual bool GetValueFor(Accessible* aAccessible, TextDecorValue* aValue);
+    virtual bool GetValueFor(Accessible* aAccessible, TextDecorValue* aValue)
+      MOZ_OVERRIDE;
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const TextDecorValue& aValue);
+                             const TextDecorValue& aValue) MOZ_OVERRIDE;
   };
 
   /**
    * Class is used for the work with "text-position" text attribute.
    */
 
   enum TextPosValue {
     eTextPosNone = 0,
@@ -448,19 +456,20 @@ protected:
   {
   public:
     TextPosTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
     virtual ~TextPosTextAttr() { }
 
   protected:
 
     // TextAttr
-    virtual bool GetValueFor(Accessible* aAccessible, TextPosValue* aValue);
+    virtual bool GetValueFor(Accessible* aAccessible, TextPosValue* aValue)
+      MOZ_OVERRIDE;
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const TextPosValue& aValue);
+                             const TextPosValue& aValue) MOZ_OVERRIDE;
 
   private:
     TextPosValue GetTextPosValue(nsIFrame* aFrame) const;
   };
 
 }; // TextAttrMgr
 
 } // namespace a11y
--- a/accessible/base/nsAccCache.h
+++ b/accessible/base/nsAccCache.h
@@ -15,22 +15,33 @@
 /**
  * Shutdown and removes the accessible from cache.
  */
 template <class T>
 static PLDHashOperator
 ClearCacheEntry(const void* aKey, nsRefPtr<T>& aAccessible, void* aUserArg)
 {
   NS_ASSERTION(aAccessible, "Calling ClearCacheEntry with a nullptr pointer!");
-  if (aAccessible)
+  if (aAccessible && !aAccessible->IsDefunct())
     aAccessible->Shutdown();
 
   return PL_DHASH_REMOVE;
 }
 
+template <class T>
+static PLDHashOperator
+UnbindCacheEntryFromDocument(const void* aKey, nsRefPtr<T>& aAccessible,
+                             void* aUserArg)
+{
+  MOZ_ASSERT(aAccessible && !aAccessible->IsDefunct());
+  aAccessible->Document()->UnbindFromDocument(aAccessible);
+
+  return PL_DHASH_REMOVE;
+}
+
 /**
  * Clear the cache and shutdown the accessibles.
  */
 
 template <class T>
 static void
 ClearCache(nsRefPtrHashtable<nsPtrHashKey<const void>, T>& aCache)
 {
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -957,69 +957,70 @@ nsAccessibilityService::GetOrCreateAcces
     return newAcc;
   }
 
   nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aNode);
 
   // If the element is focusable or global ARIA attribute is applied to it or
   // it is referenced by ARIA relationship then treat role="presentation" on
   // the element as the role is not there.
-  if (roleMapEntry && roleMapEntry->Is(nsGkAtoms::presentation)) {
+  if (roleMapEntry &&
+      (roleMapEntry->Is(nsGkAtoms::presentation) ||
+       roleMapEntry->Is(nsGkAtoms::none))) {
     if (!MustBeAccessible(content, document))
       return nullptr;
 
     roleMapEntry = nullptr;
   }
 
   if (!newAcc && isHTML) {  // HTML accessibles
-    if (roleMapEntry) {
-      // Create pure ARIA grid/treegrid related accessibles if they weren't used
-      // on accessible HTML table elements.
+    bool isARIATableOrCell = roleMapEntry &&
+      (roleMapEntry->accTypes & (eTableCell | eTable));
+
+    if (!isARIATableOrCell ||
+        frame->AccessibleType() == eHTMLTableCellType ||
+        frame->AccessibleType() == eHTMLTableType) {
+      // Prefer to use markup (mostly tag name, perhaps attributes) to decide if
+      // and what kind of accessible to create,
+      newAcc = CreateHTMLAccessibleByMarkup(frame, content, aContext);
+      if (!newAcc) // try by frame accessible type.
+        newAcc = CreateAccessibleByFrameType(frame, content, aContext);
+    }
+
+    // In case of ARIA grids use grid-specific classes if it's not native table
+    // based.
+    if (isARIATableOrCell && (!newAcc || newAcc->IsGenericHyperText())) {
       if ((roleMapEntry->accTypes & eTableCell)) {
-        if (aContext->IsTableRow() &&
-            (frame->AccessibleType() != eHTMLTableCellType ||
-             aContext->GetContent() != content->GetParent())) {
+        if (aContext->IsTableRow())
           newAcc = new ARIAGridCellAccessibleWrap(content, document);
-        }
 
-      } else if ((roleMapEntry->IsOfType(eTable)) &&
-                 frame->AccessibleType() != eHTMLTableType) {
+      } else if (roleMapEntry->IsOfType(eTable)) {
         newAcc = new ARIAGridAccessibleWrap(content, document);
       }
     }
 
-    if (!newAcc) {
-      // Prefer to use markup (mostly tag name, perhaps attributes) to decide if
-      // and what kind of accessible to create.
-      newAcc = CreateHTMLAccessibleByMarkup(frame, content, aContext);
-
-      // Try using frame to do it.
-      if (!newAcc)
-        newAcc = CreateAccessibleByFrameType(frame, content, aContext);
-
-      // If table has strong ARIA role then all table descendants shouldn't
-      // expose their native roles.
-      if (!roleMapEntry && newAcc && aContext->HasStrongARIARole()) {
-        if (frame->AccessibleType() == eHTMLTableRowType) {
-          nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
-          if (!contextRoleMap->IsOfType(eTable))
-            roleMapEntry = &aria::gEmptyRoleMap;
-
-        } else if (frame->AccessibleType() == eHTMLTableCellType &&
-                   aContext->ARIARoleMap() == &aria::gEmptyRoleMap) {
+    // If table has strong ARIA role then all table descendants shouldn't
+    // expose their native roles.
+    if (!roleMapEntry && newAcc && aContext->HasStrongARIARole()) {
+      if (frame->AccessibleType() == eHTMLTableRowType) {
+        nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
+        if (!contextRoleMap->IsOfType(eTable))
           roleMapEntry = &aria::gEmptyRoleMap;
 
-        } else if (content->Tag() == nsGkAtoms::dt ||
-                   content->Tag() == nsGkAtoms::li ||
-                   content->Tag() == nsGkAtoms::dd ||
-                   frame->AccessibleType() == eHTMLLiType) {
-          nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
-          if (!contextRoleMap->IsOfType(eList))
-            roleMapEntry = &aria::gEmptyRoleMap;
-        }
+      } else if (frame->AccessibleType() == eHTMLTableCellType &&
+                 aContext->ARIARoleMap() == &aria::gEmptyRoleMap) {
+        roleMapEntry = &aria::gEmptyRoleMap;
+
+      } else if (content->Tag() == nsGkAtoms::dt ||
+                 content->Tag() == nsGkAtoms::li ||
+                 content->Tag() == nsGkAtoms::dd ||
+                 frame->AccessibleType() == eHTMLLiType) {
+        nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
+        if (!contextRoleMap->IsOfType(eList))
+          roleMapEntry = &aria::gEmptyRoleMap;
       }
     }
   }
 
   // Accessible XBL types and deck stuff are used in XUL only currently.
   if (!newAcc && content->IsXUL()) {
     // No accessible for not selected deck panel and its children.
     if (!aContext->IsXULTabpanels()) {
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -6,16 +6,17 @@
 #ifndef __nsAccessibilityService_h__
 #define __nsAccessibilityService_h__
 
 #include "nsIAccessibilityService.h"
 
 #include "mozilla/a11y/DocManager.h"
 #include "mozilla/a11y/FocusManager.h"
 #include "mozilla/a11y/SelectionManager.h"
+#include "mozilla/Preferences.h"
 
 #include "nsIObserver.h"
 
 class nsImageFrame;
 class nsPluginFrame;
 class nsITreeView;
 
 namespace mozilla {
@@ -246,22 +247,21 @@ GetAccService()
 }
 
 /**
  * Return true if we're in a content process and not B2G.
  */
 inline bool
 IPCAccessibilityActive()
 {
-  // Don't allow IPC accessibility to ride the 37 train.
-  return false;
 #ifdef MOZ_B2G
   return false;
 #else
-  return XRE_GetProcessType() == GeckoProcessType_Content;
+  return XRE_GetProcessType() == GeckoProcessType_Content &&
+    mozilla::Preferences::GetBool("accessibility.ipc_architecture.enabled", true);
 #endif
 }
 
 /**
  * Map nsIAccessibleEvents constants to strings. Used by
  * nsIAccessibleRetrieval::getStringEventType() method.
  */
 static const char kEventTypeNames[][40] = {
--- a/accessible/base/nsAccessiblePivot.cpp
+++ b/accessible/base/nsAccessiblePivot.cpp
@@ -887,21 +887,17 @@ RuleCache::ApplyFilter(Accessible* aAcce
         (state & states::OFFSCREEN))
       return NS_OK;
 
     if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
         !(state & states::FOCUSABLE))
       return NS_OK;
 
     if (nsIAccessibleTraversalRule::PREFILTER_ARIA_HIDDEN & mPreFilter) {
-      nsIContent* content = aAccessible->GetContent();
-      if (content &&
-          nsAccUtils::HasDefinedARIAToken(content, nsGkAtoms::aria_hidden) &&
-          !content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_hidden,
-                                nsGkAtoms::_false, eCaseMatters)) {
+      if (aAccessible->IsARIAHidden()) {
         *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
         return NS_OK;
       }
     }
 
     if ((nsIAccessibleTraversalRule::PREFILTER_TRANSPARENT & mPreFilter) &&
         !(state & states::OPAQUE1)) {
       nsIFrame* frame = aAccessible->GetFrame();
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -144,17 +144,17 @@ nsCoreUtils::DispatchTouchEvent(uint32_t
   if (!dom::TouchEvent::PrefEnabled())
     return;
 
   WidgetTouchEvent event(true, aEventType, aRootWidget);
 
   event.time = PR_IntervalNow();
 
   // XXX: Touch has an identifier of -1 to hint that it is synthesized.
-  nsRefPtr<dom::Touch> t = new dom::Touch(-1, nsIntPoint(aX, aY),
+  nsRefPtr<dom::Touch> t = new dom::Touch(-1, LayoutDeviceIntPoint(aX, aY),
                                           nsIntPoint(1, 1), 0.0f, 1.0f);
   t->SetTarget(aContent);
   event.touches.AppendElement(t);
   nsEventStatus status = nsEventStatus_eIgnore;
   aPresShell->HandleEventWithTarget(&event, aFrame, aContent, &status);
 }
 
 uint32_t
--- a/accessible/generic/ARIAGridAccessible.cpp
+++ b/accessible/generic/ARIAGridAccessible.cpp
@@ -599,10 +599,17 @@ ARIAGridCellAccessible::NativeAttributes
   }
 
   int32_t rowIdx = RowIndexFor(thisRow);
 
   nsAutoString stringIdx;
   stringIdx.AppendInt(rowIdx * colCount + colIdx);
   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx);
 
+#ifdef DEBUG
+  nsAutoString unused;
+  attributes->SetStringProperty(NS_LITERAL_CSTRING("cppclass"),
+                                NS_LITERAL_STRING("ARIAGridCellAccessible"),
+                                unused);
+#endif
+
   return attributes.forget();
 }
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -104,34 +104,17 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(Accessib
 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(Accessible, LastRelease())
 
 Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc) :
   mContent(aContent), mDoc(aDoc),
   mParent(nullptr), mIndexInParent(-1), mChildrenFlags(eChildrenUninitialized),
   mStateFlags(0), mContextFlags(0), mType(0), mGenericTypes(0),
   mIndexOfEmbeddedChild(-1), mRoleMapEntry(nullptr)
 {
-#ifdef NS_DEBUG_X
-   {
-     nsCOMPtr<nsIPresShell> shell(do_QueryReferent(aShell));
-     printf(">>> %p Created Acc - DOM: %p  PS: %p",
-            (void*)static_cast<nsIAccessible*>(this), (void*)aNode,
-            (void*)shell.get());
-    nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
-    if (content) {
-      printf(" Con: %s@%p",
-             NS_ConvertUTF16toUTF8(content->NodeInfo()->QualifiedName()).get(),
-             (void *)content.get());
-      nsAutoString buf;
-      Name(buf);
-      printf(" Name:[%s]", NS_ConvertUTF16toUTF8(buf).get());
-     }
-     printf("\n");
-   }
-#endif
+  mBits.groupInfo = nullptr;
 }
 
 Accessible::~Accessible()
 {
   NS_ASSERTION(!mDoc, "LastRelease was never called!?!");
 }
 
 ENameValueFlag
@@ -885,16 +868,21 @@ Accessible::Attributes()
   }
 
   // Expose object attributes from ARIA attributes.
   aria::AttrIterator attribIter(mContent);
   nsAutoString name, value;
   while(attribIter.Next(name, value))
     attributes->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused);
 
+  if (IsARIAHidden()) {
+    nsAccUtils::SetAccAttr(attributes, nsGkAtoms::hidden,
+                           NS_LITERAL_STRING("true"));
+  }
+
   // If there is no aria-live attribute then expose default value of 'live'
   // object attribute used for ARIA role of this accessible.
   if (mRoleMapEntry) {
     nsAutoString live;
     nsAccUtils::GetAccAttr(attributes, nsGkAtoms::live, live);
     if (live.IsEmpty()) {
       if (nsAccUtils::GetLiveAttrValue(mRoleMapEntry->liveAttRule, live))
         nsAccUtils::SetAccAttr(attributes, nsGkAtoms::live, live);
@@ -1273,17 +1261,17 @@ Accessible::Value(nsString& aValue)
     return;
   }
 
   // Value of combobox is a text of current or selected item.
   if (mRoleMapEntry->Is(nsGkAtoms::combobox)) {
     Accessible* option = CurrentItem();
     if (!option) {
       Accessible* listbox = nullptr;
-      IDRefsIterator iter(mDoc, mContent, nsGkAtoms::aria_owns);
+      ARIAOwnsIterator iter(this);
       while ((listbox = iter.Next()) && !listbox->IsListControl());
 
       if (!listbox) {
         uint32_t childCount = ChildCount();
         for (uint32_t idx = 0; idx < childCount; idx++) {
           Accessible* child = mChildren.ElementAt(idx);
           if (child->IsListControl())
             listbox = child;
@@ -1551,18 +1539,17 @@ Accessible::RelationByType(RelationType 
           mContent->IsXUL())
         rel.AppendIter(new IDRefsIterator(mDoc, mContent,
                                           nsGkAtoms::control));
 
       return rel;
     }
 
     case RelationType::NODE_CHILD_OF: {
-      Relation rel(new RelatedAccIterator(Document(), mContent,
-                                          nsGkAtoms::aria_owns));
+      Relation rel(new ARIAOwnedByIterator(this));
 
       // This is an ARIA tree or treegrid that doesn't use owns, so we need to
       // get the parent the hard way.
       if (mRoleMapEntry && (mRoleMapEntry->role == roles::OUTLINEITEM ||
                             mRoleMapEntry->role == roles::LISTITEM ||
                             mRoleMapEntry->role == roles::ROW)) {
         rel.AppendTarget(GetGroupInfo()->ConceptualParent());
       }
@@ -1582,17 +1569,17 @@ Accessible::RelationByType(RelationType 
             rel.AppendTarget(Parent());
         }
       }
 
       return rel;
     }
 
     case RelationType::NODE_PARENT_OF: {
-      Relation rel(new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_owns));
+      Relation rel(new ARIAOwnsIterator(this));
 
       // ARIA tree or treegrid can do the hierarchy by @aria-level, ARIA trees
       // also can be organized by groups.
       if (mRoleMapEntry &&
           (mRoleMapEntry->role == roles::OUTLINEITEM ||
            mRoleMapEntry->role == roles::LISTITEM ||
            mRoleMapEntry->role == roles::ROW ||
            mRoleMapEntry->role == roles::OUTLINE ||
@@ -1721,17 +1708,17 @@ void
 Accessible::DoCommand(nsIContent *aContent, uint32_t aActionIndex)
 {
   class Runnable MOZ_FINAL : public nsRunnable
   {
   public:
     Runnable(Accessible* aAcc, nsIContent* aContent, uint32_t aIdx) :
       mAcc(aAcc), mContent(aContent), mIdx(aIdx) { }
 
-    NS_IMETHOD Run()
+    NS_IMETHOD Run() MOZ_OVERRIDE
     {
       if (mAcc)
         mAcc->DispatchClickEvent(mContent, mIdx);
 
       return NS_OK;
     }
 
     void Revoke()
@@ -1933,29 +1920,36 @@ Accessible::BindToParent(Accessible* aPa
   AssertInMutatingSubtree();
 #endif
 
   // Note: this is currently only used for richlistitems and their children.
   if (mParent->HasNameDependentParent() || mParent->IsXULListItem())
     mContextFlags |= eHasNameDependentParent;
   else
     mContextFlags &= ~eHasNameDependentParent;
+
+  if (mParent->IsARIAHidden() || aria::HasDefinedARIAHidden(mContent))
+    SetARIAHidden(true);
 }
 
 // Accessible protected
 void
 Accessible::UnbindFromParent()
 {
 #ifdef DEBUG
   AssertInMutatingSubtree();
 #endif
   mParent = nullptr;
   mIndexInParent = -1;
   mIndexOfEmbeddedChild = -1;
-  mGroupInfo = nullptr;
+  if (IsProxy())
+    MOZ_CRASH("this should never be called on proxy wrappers");
+
+  delete mBits.groupInfo;
+  mBits.groupInfo = nullptr;
   mContextFlags &= ~eHasNameDependentParent;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible public methods
 
 RootAccessible*
 Accessible::RootAccessible() const
@@ -2386,16 +2380,30 @@ Accessible::ContainerWidget() const
       // Don't cross DOM document boundaries.
       if (parent->IsDoc())
         break;
     }
   }
   return nullptr;
 }
 
+void
+Accessible::SetARIAHidden(bool aIsDefined)
+{
+  if (aIsDefined)
+    mContextFlags |= eARIAHidden;
+  else
+    mContextFlags &= ~eARIAHidden;
+
+  uint32_t length = mChildren.Length();
+  for (uint32_t i = 0; i < length; i++) {
+    mChildren[i]->SetARIAHidden(aIsDefined);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible protected methods
 
 void
 Accessible::LastRelease()
 {
   // First cleanup if needed...
   if (mDoc) {
@@ -2521,27 +2529,30 @@ Accessible::GetActionRule() const
     return eExpandAction;
 
   return eNoAction;
 }
 
 AccGroupInfo*
 Accessible::GetGroupInfo()
 {
-  if (mGroupInfo){
+  if (IsProxy())
+    MOZ_CRASH("This should never be called on proxy wrappers");
+
+  if (mBits.groupInfo){
     if (HasDirtyGroupInfo()) {
-      mGroupInfo->Update();
+      mBits.groupInfo->Update();
       SetDirtyGroupInfo(false);
     }
 
-    return mGroupInfo;
+    return mBits.groupInfo;
   }
 
-  mGroupInfo = AccGroupInfo::CreateGroupInfo(this);
-  return mGroupInfo;
+  mBits.groupInfo = AccGroupInfo::CreateGroupInfo(this);
+  return mBits.groupInfo;
 }
 
 void
 Accessible::InvalidateChildrenGroupInfo()
 {
   uint32_t length = mChildren.Length();
   for (uint32_t i = 0; i < length; i++) {
     Accessible* child = mChildren[i];
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -34,16 +34,17 @@ class AccGroupInfo;
 class ApplicationAccessible;
 class DocAccessible;
 class EmbeddedObjCollector;
 class HTMLImageMapAccessible;
 class HTMLLIAccessible;
 class HyperTextAccessible;
 class ImageAccessible;
 class KeyBinding;
+class ProxyAccessible;
 class Relation;
 class RootAccessible;
 class TableAccessible;
 class TableCellAccessible;
 class TextLeafAccessible;
 class XULLabelAccessible;
 class XULTreeAccessible;
 
@@ -573,16 +574,17 @@ public:
 
   bool IsButton() const { return HasGenericType(eButton); }
 
   bool IsCombobox() const { return HasGenericType(eCombobox); }
 
   bool IsDoc() const { return HasGenericType(eDocument); }
   DocAccessible* AsDoc();
 
+  bool IsGenericHyperText() const { return mType == eHyperTextType; }
   bool IsHyperText() const { return HasGenericType(eHyperText); }
   HyperTextAccessible* AsHyperText();
 
   bool IsHTMLBr() const { return mType == eHTMLBRType; }
   bool IsHTMLCombobox() const { return mType == eHTMLComboboxType; }
   bool IsHTMLFileInput() const { return mType == eHTMLFileInputType; }
 
   bool IsHTMLListItem() const { return mType == eHTMLLiType; }
@@ -602,16 +604,23 @@ public:
   bool IsList() const { return HasGenericType(eList); }
 
   bool IsListControl() const { return HasGenericType(eListControl); }
 
   bool IsMenuButton() const { return HasGenericType(eMenuButton); }
 
   bool IsMenuPopup() const { return mType == eMenuPopupType; }
 
+  bool IsProxy() const { return mType == eProxyType; }
+  ProxyAccessible* Proxy() const
+  {
+    MOZ_ASSERT(IsProxy());
+    return mBits.proxy;
+  }
+
   bool IsProgress() const { return mType == eProgressType; }
 
   bool IsRoot() const { return mType == eRootType; }
   a11y::RootAccessible* AsRoot();
 
   bool IsSelect() const { return HasGenericType(eSelect); }
 
   bool IsTable() const { return HasGenericType(eTable); }
@@ -881,16 +890,23 @@ public:
 
   /**
    * Return true if this accessible has a parent whose name depends on this
    * accessible.
    */
   bool HasNameDependentParent() const
     { return mContextFlags & eHasNameDependentParent; }
 
+  /**
+   * Return true if aria-hidden="true" is applied to the accessible or inherited
+   * from the parent.
+   */
+  bool IsARIAHidden() const { return mContextFlags & eARIAHidden; }
+  void SetARIAHidden(bool aIsDefined);
+
 protected:
 
   virtual ~Accessible();
 
   /**
    * Return the accessible name provided by native markup. It doesn't take
    * into account ARIA markup used to specify the name.
    */
@@ -967,18 +983,19 @@ protected:
     eLastStateFlag = eSurvivingInUpdate
   };
 
   /**
    * Flags used for contextual information about the accessible.
    */
   enum ContextFlags {
     eHasNameDependentParent = 1 << 0, // Parent's name depends on this accessible.
+    eARIAHidden = 1 << 1,
 
-    eLastContextFlag = eHasNameDependentParent
+    eLastContextFlag = eARIAHidden
   };
 
 protected:
 
   //////////////////////////////////////////////////////////////////////////////
   // Miscellaneous helpers
 
   /**
@@ -1074,17 +1091,17 @@ protected:
   DocAccessible* mDoc;
 
   nsRefPtr<Accessible> mParent;
   nsTArray<nsRefPtr<Accessible> > mChildren;
   int32_t mIndexInParent;
 
   static const uint8_t kChildrenFlagsBits = 2;
   static const uint8_t kStateFlagsBits = 9;
-  static const uint8_t kContextFlagsBits = 1;
+  static const uint8_t kContextFlagsBits = 2;
   static const uint8_t kTypeBits = 6;
   static const uint8_t kGenericTypesBits = 13;
 
   /**
    * Keep in sync with ChildrenFlags, StateFlags, ContextFlags, and AccTypes.
    */
   uint32_t mChildrenFlags : kChildrenFlagsBits;
   uint32_t mStateFlags : kStateFlagsBits;
@@ -1098,17 +1115,21 @@ protected:
   friend class DocAccessible;
   friend class xpcAccessible;
   friend class AutoTreeMutation;
 
   nsAutoPtr<mozilla::a11y::EmbeddedObjCollector> mEmbeddedObjCollector;
   int32_t mIndexOfEmbeddedChild;
   friend class EmbeddedObjCollector;
 
-  nsAutoPtr<AccGroupInfo> mGroupInfo;
+  union
+  {
+    AccGroupInfo* groupInfo;
+    ProxyAccessible* proxy;
+  } mBits;
   friend class AccGroupInfo;
 
   /**
    * Non-null indicates author-supplied role; possibly state & value as well
    */
   nsRoleMapEntry* mRoleMapEntry;
 
 private:
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -950,16 +950,32 @@ DocAccessible::ARIAAttributeChanged(Acce
   if (!(attrFlags & ATTR_BYPASSOBJ)) {
     nsRefPtr<AccEvent> event =
       new AccObjectAttrChangedEvent(aAccessible, aAttribute);
     FireDelayedEvent(event);
   }
 
   nsIContent* elm = aAccessible->GetContent();
 
+  // Update aria-hidden flag for the whole subtree iff aria-hidden is changed
+  // on the root, i.e. ignore any affiliated aria-hidden changes in the subtree
+  // of top aria-hidden.
+  if (aAttribute == nsGkAtoms::aria_hidden) {
+    bool isDefined = aria::HasDefinedARIAHidden(elm);
+    if (isDefined != aAccessible->IsARIAHidden() &&
+        !aAccessible->Parent()->IsARIAHidden()) {
+      aAccessible->SetARIAHidden(isDefined);
+
+      nsRefPtr<AccEvent> event =
+        new AccObjectAttrChangedEvent(aAccessible, aAttribute);
+      FireDelayedEvent(event);
+    }
+    return;
+  }
+
   if (aAttribute == nsGkAtoms::aria_checked ||
       (aAccessible->IsButton() &&
        aAttribute == nsGkAtoms::aria_pressed)) {
     const uint64_t kState = (aAttribute == nsGkAtoms::aria_checked) ?
                             states::CHECKED : states::PRESSED;
     nsRefPtr<AccEvent> event = new AccStateChangeEvent(aAccessible, kState);
     FireDelayedEvent(event);
 
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -45,16 +45,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // HyperTextAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HyperTextAccessible::
   HyperTextAccessible(nsIContent* aNode, DocAccessible* aDoc) :
   AccessibleWrap(aNode, aDoc)
 {
+  mType = eHyperTextType;
   mGenericTypes |= eHyperText;
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(HyperTextAccessible, Accessible)
 
 role
 HyperTextAccessible::NativeRole()
 {
@@ -505,17 +506,17 @@ HyperTextAccessible::FindOffset(uint32_t
 
   const bool kIsJumpLinesOk = true; // okay to jump lines
   const bool kIsScrollViewAStop = false; // do not stop at scroll views
   const bool kIsKeyboardSelect = true; // is keyboard selection
   const bool kIsVisualBidi = false; // use visual order for bidi text
   nsPeekOffsetStruct pos(aAmount, aDirection, innerContentOffset,
                          nsPoint(0, 0), kIsJumpLinesOk, kIsScrollViewAStop,
                          kIsKeyboardSelect, kIsVisualBidi,
-                         aWordMovementType);
+                         false, aWordMovementType);
   nsresult rv = frameAtOffset->PeekOffset(&pos);
 
   // PeekOffset fails on last/first lines of the text in certain cases.
   if (NS_FAILED(rv) && aAmount == eSelectLine) {
     pos.mAmount = (aDirection == eDirNext) ? eSelectEndLine : eSelectBeginLine;
     frameAtOffset->PeekOffset(&pos);
   }
   if (!pos.mResultContent) {
@@ -1339,17 +1340,17 @@ HyperTextAccessible::GetCaretRect(nsIWid
   // on the widget.
   *aWidget = frame->GetNearestWidget(offset);
   NS_ENSURE_TRUE(*aWidget, nsIntRect());
   rect.MoveBy(offset);
 
   nsIntRect caretRect;
   caretRect = rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel());
   // ((content screen origin) - (content offset in the widget)) = widget origin on the screen
-  caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset());
+  caretRect.MoveBy((*aWidget)->WidgetToScreenOffsetUntyped() - (*aWidget)->GetClientOffset());
 
   // Correct for character size, so that caret always matches the size of
   // the character. This is important for font size transitions, and is
   // necessary because the Gecko caret uses the previous character's size as
   // the user moves forward in the text by character.
   nsIntRect charRect = CharBounds(CaretOffset(),
                                   nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE);
   if (!charRect.IsEmpty()) {
--- a/accessible/html/HTMLSelectAccessible.cpp
+++ b/accessible/html/HTMLSelectAccessible.cpp
@@ -376,16 +376,27 @@ void
 HTMLComboboxAccessible::InvalidateChildren()
 {
   AccessibleWrap::InvalidateChildren();
 
   if (mListAccessible)
     mListAccessible->InvalidateChildren();
 }
 
+bool
+HTMLComboboxAccessible::RemoveChild(Accessible* aChild)
+{
+  MOZ_ASSERT(aChild == mListAccessible);
+  if (AccessibleWrap::RemoveChild(aChild)) {
+    mListAccessible = nullptr;
+    return true;
+  }
+  return false;
+}
+
 void
 HTMLComboboxAccessible::CacheChildren()
 {
   nsIComboboxControlFrame* comboFrame = do_QueryFrame(GetFrame());
   if (!comboFrame)
     return;
 
   nsIFrame* listFrame = comboFrame->GetDropDown();
@@ -404,22 +415,20 @@ HTMLComboboxAccessible::CacheChildren()
     // tree for combobox.
     mListAccessible->EnsureChildren();
   }
 }
 
 void
 HTMLComboboxAccessible::Shutdown()
 {
-  AccessibleWrap::Shutdown();
+  MOZ_ASSERT(mDoc->IsDefunct() || !mListAccessible);
+  mListAccessible = nullptr;
 
-  if (mListAccessible) {
-    mListAccessible->Shutdown();
-    mListAccessible = nullptr;
-  }
+  AccessibleWrap::Shutdown();
 }
 
 uint64_t
 HTMLComboboxAccessible::NativeState()
 {
   // As a HTMLComboboxAccessible we can have the following states:
   // FOCUSED, FOCUSABLE, HASPOPUP, EXPANDED, COLLAPSED
   // Get focus status from base class
--- a/accessible/html/HTMLSelectAccessible.h
+++ b/accessible/html/HTMLSelectAccessible.h
@@ -169,16 +169,17 @@ public:
 
   // Accessible
   virtual void Shutdown() MOZ_OVERRIDE;
   virtual void Description(nsString& aDescription) MOZ_OVERRIDE;
   virtual void Value(nsString& aValue) MOZ_OVERRIDE;
   virtual a11y::role NativeRole() MOZ_OVERRIDE;
   virtual uint64_t NativeState() MOZ_OVERRIDE;
   virtual void InvalidateChildren() MOZ_OVERRIDE;
+  virtual bool RemoveChild(Accessible* aChild) MOZ_OVERRIDE;
 
   // ActionAccessible
   virtual uint8_t ActionCount() MOZ_OVERRIDE;
   virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) MOZ_OVERRIDE;
   virtual bool DoAction(uint8_t aIndex) MOZ_OVERRIDE;
 
   // Widgets
   virtual bool IsWidget() const MOZ_OVERRIDE;
--- a/accessible/html/HTMLTableAccessible.cpp
+++ b/accessible/html/HTMLTableAccessible.cpp
@@ -43,16 +43,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTableCellAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLTableCellAccessible::
   HTMLTableCellAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   HyperTextAccessibleWrap(aContent, aDoc)
 {
+  mType = eHTMLTableCellType;
   mGenericTypes |= eTableCell;
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableCellAccessible, HyperTextAccessible)
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTableCellAccessible: Accessible implementation
 
@@ -124,16 +125,23 @@ HTMLTableCellAccessible::NativeAttribute
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::abbr, abbrText);
 
   // axis attribute
   nsAutoString axisText;
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::axis, axisText);
   if (!axisText.IsEmpty())
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::axis, axisText);
 
+#ifdef DEBUG
+  nsAutoString unused;
+  attributes->SetStringProperty(NS_LITERAL_CSTRING("cppclass"),
+                                NS_LITERAL_STRING("HTMLTableCellAccessible"),
+                                unused);
+#endif
+
   return attributes.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTableCellAccessible: TableCellAccessible implementation
 
 TableAccessible*
 HTMLTableCellAccessible::Table() const
--- a/accessible/interfaces/nsIAccessible.idl
+++ b/accessible/interfaces/nsIAccessible.idl
@@ -147,16 +147,18 @@ interface nsIAccessible : nsISupports
    *                 constants)
    * @param aExtraState - the second bit field
    *                      (see nsIAccessibleStates::EXT_STATE_* constants)
    */
   void getState(out unsigned long aState, out unsigned long aExtraState);
 
   /**
    * Help text associated with node
+   *
+   * @note As of now, this just returns empty string.
    */
   readonly attribute AString help;
 
   /**
    * Focused accessible child of node
    */
   readonly attribute nsIAccessible focusedChild;
 
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/DocAccessibleChild.cpp
@@ -2,93 +2,132 @@
 /* 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/. */
 
 #include "DocAccessibleChild.h"
 
 #include "Accessible-inl.h"
+#include "ProxyAccessible.h"
+#include "Relation.h"
 
 #include "nsIPersistentProperties2.h"
 #include "nsISimpleEnumerator.h"
 
 namespace mozilla {
 namespace a11y {
 
-void
+static uint32_t
+InterfacesFor(Accessible* aAcc)
+{
+  uint32_t interfaces = 0;
+  if (aAcc->IsHyperText() && aAcc->AsHyperText()->IsTextRole())
+    interfaces |= Interfaces::HYPERTEXT;
+
+  return interfaces;
+}
+
+static void
 SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree)
 {
   uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID());
   uint32_t role = aRoot->Role();
   uint32_t childCount = aRoot->ChildCount();
+  uint32_t interfaces = InterfacesFor(aRoot);
 
   // OuterDocAccessibles are special because we don't want to serialize the
   // child doc here, we'll call PDocAccessibleConstructor in
   // NotificationController.
   if (childCount == 1 && aRoot->GetChildAt(0)->IsDoc())
     childCount = 0;
 
-  aTree.AppendElement(AccessibleData(id, role, childCount));
+  aTree.AppendElement(AccessibleData(id, role, childCount, interfaces));
   for (uint32_t i = 0; i < childCount; i++)
     SerializeTree(aRoot->GetChildAt(i), aTree);
 }
 
+Accessible*
+DocAccessibleChild::IdToAccessible(const uint64_t& aID)
+{
+  return mDoc->GetAccessibleByUniqueID(reinterpret_cast<void*>(aID));
+}
+
+HyperTextAccessible*
+DocAccessibleChild::IdToHyperTextAccessible(const uint64_t& aID)
+{
+  Accessible* acc = IdToAccessible(aID);
+  MOZ_ASSERT(!acc || acc->IsHyperText());
+  return acc ? acc->AsHyperText() : nullptr;
+}
+
 void
 DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent)
 {
   Accessible* parent = aShowEvent->Parent();
   uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID());
   uint32_t idxInParent = aShowEvent->GetAccessible()->IndexInParent();
   nsTArray<AccessibleData> shownTree;
   ShowEventData data(parentID, idxInParent, shownTree);
   SerializeTree(aShowEvent->GetAccessible(), data.NewTree());
   SendShowEvent(data);
 }
 
 bool
 DocAccessibleChild::RecvState(const uint64_t& aID, uint64_t* aState)
 {
-  Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID);
+  Accessible* acc = IdToAccessible(aID);
   if (!acc) {
     *aState = states::DEFUNCT;
     return true;
   }
 
   *aState = acc->State();
 
   return true;
 }
 
 bool
 DocAccessibleChild::RecvName(const uint64_t& aID, nsString* aName)
 {
-  Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID);
+  Accessible* acc = IdToAccessible(aID);
   if (!acc)
     return true;
 
   acc->Name(*aName);
   return true;
 }
 
 bool
+DocAccessibleChild::RecvValue(const uint64_t& aID, nsString* aValue)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (!acc) {
+    return true;
+  }
+
+  acc->Value(*aValue);
+  return true;
+}
+
+bool
 DocAccessibleChild::RecvDescription(const uint64_t& aID, nsString* aDesc)
 {
-  Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID);
+  Accessible* acc = IdToAccessible(aID);
   if (!acc)
     return true;
 
   acc->Description(*aDesc);
   return true;
 }
 
 bool
 DocAccessibleChild::RecvAttributes(const uint64_t& aID, nsTArray<Attribute>* aAttributes)
 {
-  Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID);
+  Accessible* acc = IdToAccessible(aID);
   if (!acc)
     return true;
 
   nsCOMPtr<nsIPersistentProperties> props = acc->Attributes();
   if (!props)
     return true;
 
   nsCOMPtr<nsISimpleEnumerator> propEnum;
@@ -112,10 +151,130 @@ DocAccessibleChild::RecvAttributes(const
     rv = propElem->GetValue(value);
     NS_ENSURE_SUCCESS(rv, false);
 
     aAttributes->AppendElement(Attribute(name, value));
     }
 
   return true;
 }
+
+bool
+DocAccessibleChild::RecvRelationByType(const uint64_t& aID,
+                                       const uint32_t& aType,
+                                       nsTArray<uint64_t>* aTargets)
+{
+  Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID);
+  if (!acc)
+    return false;
+
+  auto type = static_cast<RelationType>(aType);
+  Relation rel = acc->RelationByType(type);
+  while (Accessible* target = rel.Next())
+    aTargets->AppendElement(reinterpret_cast<uintptr_t>(target));
+
+  return true;
+}
+
+static void
+AddRelation(Accessible* aAcc, RelationType aType,
+            nsTArray<RelationTargets>* aTargets)
+{
+  Relation rel = aAcc->RelationByType(aType);
+  nsTArray<uint64_t> targets;
+  while (Accessible* target = rel.Next())
+    targets.AppendElement(reinterpret_cast<uintptr_t>(target));
+
+  if (!targets.IsEmpty()) {
+    RelationTargets* newRelation =
+      aTargets->AppendElement(RelationTargets(static_cast<uint32_t>(aType),
+                                              nsTArray<uint64_t>()));
+    newRelation->Targets().SwapElements(targets);
+  }
+}
+
+bool
+DocAccessibleChild::RecvRelations(const uint64_t& aID,
+                                  nsTArray<RelationTargets>* aRelations)
+{
+  Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID);
+  if (!aID)
+    return false;
+
+#define RELATIONTYPE(gecko, s, a, m, i) AddRelation(acc, RelationType::gecko, aRelations);
+
+#include "RelationTypeMap.h"
+#undef RELATIONTYPE
+
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvTextSubstring(const uint64_t& aID,
+                                      const int32_t& aStartOffset,
+                                      const int32_t& aEndOffset,
+                                      nsString* aText)
+{
+  HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+  if (!acc) {
+    return true;
+  }
+
+  acc->TextSubstring(aStartOffset, aEndOffset, *aText);
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvGetTextAfterOffset(const uint64_t& aID,
+                                           const int32_t& aOffset,
+                                           const int32_t& aBoundaryType,
+                                           nsString* aText,
+                                           int32_t* aStartOffset,
+                                           int32_t* aEndOffset)
+{
+  *aStartOffset = 0;
+  *aEndOffset = 0;
+  HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+  if (acc) {
+    acc->TextAfterOffset(aOffset, aBoundaryType,
+                         aStartOffset, aEndOffset, *aText);
+  }
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvGetTextAtOffset(const uint64_t& aID,
+                                        const int32_t& aOffset,
+                                        const int32_t& aBoundaryType,
+                                        nsString* aText,
+                                        int32_t* aStartOffset,
+                                        int32_t* aEndOffset)
+{
+  *aStartOffset = 0;
+  *aEndOffset = 0;
+  HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+  if (acc) {
+    acc->TextAtOffset(aOffset, aBoundaryType,
+                      aStartOffset, aEndOffset, *aText);
+  }
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvGetTextBeforeOffset(const uint64_t& aID,
+                                            const int32_t& aOffset,
+                                            const int32_t& aBoundaryType,
+                                            nsString* aText,
+                                            int32_t* aStartOffset,
+                                            int32_t* aEndOffset)
+{
+  *aStartOffset = 0;
+  *aEndOffset = 0;
+  HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
+  if (acc) {
+    acc->TextBeforeOffset(aOffset, aBoundaryType,
+                          aStartOffset, aEndOffset, *aText);
+  }
+  return true;
+}
+
 }
 }
--- a/accessible/ipc/DocAccessibleChild.h
+++ b/accessible/ipc/DocAccessibleChild.h
@@ -8,16 +8,19 @@
 #define mozilla_a11y_DocAccessibleChild_h
 
 #include "mozilla/a11y/DocAccessible.h"
 #include "mozilla/a11y/PDocAccessibleChild.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 namespace a11y {
+class Accessible;
+class HyperTextAccessible;
+
 class AccShowEvent;
 
   /*
    * These objects handle content side communication for an accessible document,
    * and their lifetime is the same as the document they represent.
    */
 class DocAccessibleChild : public PDocAccessibleChild
 {
@@ -26,34 +29,65 @@ public:
     mDoc(aDoc)
   { MOZ_COUNT_CTOR(DocAccessibleChild); }
   ~DocAccessibleChild()
   {
     mDoc->SetIPCDoc(nullptr);
     MOZ_COUNT_DTOR(DocAccessibleChild);
   }
 
+  Accessible* IdToAccessible(const uint64_t& aID);
+  HyperTextAccessible* IdToHyperTextAccessible(const uint64_t& aID);
+
   void ShowEvent(AccShowEvent* aShowEvent);
 
   /*
    * Return the state for the accessible with given ID.
    */
   virtual bool RecvState(const uint64_t& aID, uint64_t* aState) MOZ_OVERRIDE;
 
   /*
    * Get the name for the accessible with given id.
    */
   virtual bool RecvName(const uint64_t& aID, nsString* aName) MOZ_OVERRIDE;
 
+  virtual bool RecvValue(const uint64_t& aID, nsString* aValue) MOZ_OVERRIDE;
+  
   /*
    * Get the description for the accessible with given id.
    */
   virtual bool RecvDescription(const uint64_t& aID, nsString* aDesc) MOZ_OVERRIDE;
+  virtual bool RecvRelationByType(const uint64_t& aID, const uint32_t& aType,
+                                  nsTArray<uint64_t>* aTargets) MOZ_OVERRIDE;
+  virtual bool RecvRelations(const uint64_t& aID,
+                             nsTArray<RelationTargets>* aRelations)
+    MOZ_OVERRIDE;
 
-  virtual bool RecvAttributes(const uint64_t& aID, nsTArray<Attribute> *aAttributes) MOZ_OVERRIDE;
+  virtual bool RecvAttributes(const uint64_t& aID,
+                              nsTArray<Attribute> *aAttributes) MOZ_OVERRIDE;
+  virtual bool RecvTextSubstring(const uint64_t& aID,
+                                 const int32_t& aStartOffset,
+                                 const int32_t& aEndOffset, nsString* aText)
+    MOZ_OVERRIDE;
+
+  virtual bool RecvGetTextAfterOffset(const uint64_t& aID,
+                                      const int32_t& aOffset,
+                                      const int32_t& aBoundaryType,
+                                      nsString* aText, int32_t* aStartOffset,
+                                      int32_t* aEndOffset) MOZ_OVERRIDE;
+  virtual bool RecvGetTextAtOffset(const uint64_t& aID,
+                                   const int32_t& aOffset,
+                                   const int32_t& aBoundaryType,
+                                   nsString* aText, int32_t* aStartOffset,
+                                   int32_t* aEndOffset) MOZ_OVERRIDE;
+  virtual bool RecvGetTextBeforeOffset(const uint64_t& aID,
+                                       const int32_t& aOffset,
+                                       const int32_t& aBoundaryType,
+                                       nsString* aText, int32_t* aStartOffset,
+                                       int32_t* aEndOffset) MOZ_OVERRIDE;
 
 private:
   DocAccessible* mDoc;
 };
 
 }
 }
 
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -2,23 +2,27 @@
 /* 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/. */
 
 #include "DocAccessibleParent.h"
 #include "nsAutoPtr.h"
 #include "mozilla/a11y/Platform.h"
+#include "ProxyAccessible.h"
 
 namespace mozilla {
 namespace a11y {
 
 bool
 DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
 {
+  if (mShutdown)
+    return true;
+
   if (aData.NewTree().IsEmpty()) {
     NS_ERROR("no children being added");
     return false;
   }
 
   ProxyAccessible* parent = nullptr;
   if (aData.ID()) {
     ProxyEntry* e = mAccessibles.GetEntry(aData.ID());
@@ -45,17 +49,17 @@ DocAccessibleParent::RecvShowEvent(const
   MOZ_ASSERT(consumed == aData.NewTree().Length());
 #ifdef DEBUG
   for (uint32_t i = 0; i < consumed; i++) {
     uint64_t id = aData.NewTree()[i].ID();
     MOZ_ASSERT(mAccessibles.GetEntry(id));
   }
 #endif
 
-  return consumed;
+  return consumed != 0;
 }
 
 uint32_t
 DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
                                 const nsTArray<a11y::AccessibleData>& aNewTree,
                                 uint32_t aIdx, uint32_t aIdxInParent)
 {
   if (aNewTree.Length() <= aIdx) {
@@ -69,17 +73,17 @@ DocAccessibleParent::AddSubtree(ProxyAcc
     return 0;
   }
 
   auto role = static_cast<a11y::role>(newChild.Role());
   ProxyAccessible* newProxy =
     new ProxyAccessible(newChild.ID(), aParent, this, role);
   aParent->AddChildAt(aIdxInParent, newProxy);
   mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
-  ProxyCreated(newProxy);
+  ProxyCreated(newProxy, newChild.Interfaces());
 
   uint32_t accessibles = 1;
   uint32_t kids = newChild.ChildrenCount();
   for (uint32_t i = 0; i < kids; i++) {
     uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i);
     if (!consumed)
       return 0;
 
@@ -89,16 +93,19 @@ DocAccessibleParent::AddSubtree(ProxyAcc
   MOZ_ASSERT(newProxy->ChildrenCount() == kids);
 
   return accessibles;
 }
 
 bool
 DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID)
 {
+  if (mShutdown)
+    return true;
+
   ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
   if (!rootEntry) {
     NS_ERROR("invalid root being removed!");
     return true;
   }
 
   ProxyAccessible* root = rootEntry->mProxy;
   if (!root) {
@@ -137,32 +144,38 @@ DocAccessibleParent::AddChildDoc(DocAcce
   ProxyAccessible* outerDoc = mAccessibles.GetEntry(aParentID)->mProxy;
   if (!outerDoc)
     return false;
 
   aChildDoc->mParent = outerDoc;
   outerDoc->SetChildDoc(aChildDoc);
   mChildDocs.AppendElement(aChildDoc);
   aChildDoc->mParentDoc = this;
-  ProxyCreated(aChildDoc);
+  ProxyCreated(aChildDoc, 0);
   return true;
 }
 
 PLDHashOperator
 DocAccessibleParent::ShutdownAccessibles(ProxyEntry* entry, void*)
 {
   ProxyDestroyed(entry->mProxy);
-  return PL_DHASH_NEXT;
+  return PL_DHASH_REMOVE;
 }
 
 void
-DocAccessibleParent::ActorDestroy(ActorDestroyReason aWhy)
+DocAccessibleParent::Destroy()
 {
-  MOZ_ASSERT(mChildDocs.IsEmpty(),
-      "why wheren't the child docs destroyed already?");
+  NS_ASSERTION(mChildDocs.IsEmpty(),
+               "why weren't the child docs destroyed already?");
+  MOZ_ASSERT(!mShutdown);
+  mShutdown = true;
+
+  uint32_t childDocCount = mChildDocs.Length();
+  for (uint32_t i = childDocCount - 1; i < childDocCount; i--)
+    mChildDocs[i]->Destroy();
 
   mAccessibles.EnumerateEntries(ShutdownAccessibles, nullptr);
   ProxyDestroyed(this);
   mParentDoc ? mParentDoc->RemoveChildDoc(this)
     : GetAccService()->RemoteDocShutdown(this);
 }
 }
 }
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -21,17 +21,17 @@ namespace a11y {
  * These objects live in the main process and comunicate with and represent
  * an accessible document in a content process.
  */
 class DocAccessibleParent : public ProxyAccessible,
     public PDocAccessibleParent
 {
 public:
   DocAccessibleParent() :
-    ProxyAccessible(this), mParentDoc(nullptr)
+    ProxyAccessible(this), mParentDoc(nullptr), mShutdown(false)
   { MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible); }
   ~DocAccessibleParent()
   {
     MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
     MOZ_ASSERT(mChildDocs.Length() == 0);
     MOZ_ASSERT(!mParentDoc);
   }
 
@@ -40,17 +40,22 @@ public:
    * process it is firing an event.
    */
   virtual bool RecvEvent(const uint64_t& aID, const uint32_t& aType)
     MOZ_OVERRIDE;
 
   virtual bool RecvShowEvent(const ShowEventData& aData) MOZ_OVERRIDE;
   virtual bool RecvHideEvent(const uint64_t& aRootID) MOZ_OVERRIDE;
 
-  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+  void Destroy();
+  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE
+  {
+    if (!mShutdown)
+      Destroy();
+  }
 
   /*
    * Return the main processes representation of the parent document (if any)
    * of the document this object represents.
    */
   DocAccessibleParent* Parent() const { return mParentDoc; }
 
   /*
@@ -72,16 +77,25 @@ public:
   }
 
   void RemoveAccessible(ProxyAccessible* aAccessible)
   {
     MOZ_ASSERT(mAccessibles.GetEntry(aAccessible->ID()));
     mAccessibles.RemoveEntry(aAccessible->ID());
   }
 
+  /**
+   * Return the accessible for given id.
+   */
+  ProxyAccessible* GetAccessible(uintptr_t aID) const
+  {
+    ProxyEntry* e = mAccessibles.GetEntry(aID);
+    return e ? e->mProxy : nullptr;
+  }
+
 private:
 
   class ProxyEntry : public PLDHashEntryHdr
   {
   public:
     explicit ProxyEntry(const void*) : mProxy(nullptr) {}
     ProxyEntry(ProxyEntry&& aOther) :
       mProxy(aOther.mProxy) { aOther.mProxy = nullptr; }
@@ -110,14 +124,15 @@ private:
   nsTArray<DocAccessibleParent*> mChildDocs;
   DocAccessibleParent* mParentDoc;
 
   /*
    * Conceptually this is a map from IDs to proxies, but we store the ID in the
    * proxy object so we can't use a real map.
    */
   nsTHashtable<ProxyEntry> mAccessibles;
+  bool mShutdown;
 };
 
 }
 }
 
 #endif
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -9,47 +9,71 @@ include protocol PContent;
 namespace mozilla {
 namespace a11y {
 
 struct AccessibleData
 {
   uint64_t ID;
   uint32_t Role;
   uint32_t ChildrenCount;
+  uint32_t Interfaces;
 };
 
 struct ShowEventData
 {
   uint64_t ID;
   uint32_t Idx;
   AccessibleData[] NewTree;
 };
 
 struct Attribute
 {
   nsCString Name;
   nsString Value;
 };
 
+struct RelationTargets
+{
+  uint32_t Type;
+  uint64_t[] Targets;
+};
+
 prio(normal upto high) sync protocol PDocAccessible
 {
   manager PContent;
 
 parent:
   __delete__();
 
   /*
    * Notify the parent process the document in the child process is firing an
    * event.
    */
   Event(uint64_t aID, uint32_t type);
   ShowEvent(ShowEventData data);
   HideEvent(uint64_t aRootID);
 
 child:
+  // Accessible
   prio(high) sync State(uint64_t aID) returns(uint64_t states);
   prio(high) sync Name(uint64_t aID) returns(nsString name);
+  prio(high) sync Value(uint64_t aID) returns(nsString value);
   prio(high) sync Description(uint64_t aID) returns(nsString desc);
   prio(high) sync Attributes(uint64_t aID) returns(Attribute[] attributes);
+  prio(high) sync RelationByType(uint64_t aID, uint32_t aRelationType)
+    returns(uint64_t[] targets);
+  prio(high) sync Relations(uint64_t aID) returns(RelationTargets[] relations);
+
+  // AccessibleText
+
+  // TextSubstring is getText in IDL.
+  prio(high) sync TextSubstring(uint64_t aID, int32_t aStartOffset, int32_t
+                                aEndOffset) returns(nsString aText);
+  prio(high) sync GetTextAfterOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
+    returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
+  prio(high) sync GetTextAtOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
+    returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
+  prio(high) sync GetTextBeforeOffset(uint64_t aID, int32_t aOffset, int32_t aBoundaryType)
+    returns(nsString aText, int32_t aStartOffset, int32_t aEndOffset);
 };
 
 }
 }
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/ProxyAccessible.cpp
@@ -3,31 +3,38 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ProxyAccessible.h"
 #include "DocAccessibleParent.h"
 #include "mozilla/unused.h"
 #include "mozilla/a11y/Platform.h"
+#include "RelationType.h"
+#include "mozilla/a11y/Role.h"
 
 namespace mozilla {
 namespace a11y {
 
 void
 ProxyAccessible::Shutdown()
 {
-  MOZ_ASSERT(!mOuterDoc);
+  NS_ASSERTION(!mOuterDoc, "Why do we still have a child doc?");
 
   // XXX Ideally  this wouldn't be necessary, but it seems OuterDoc accessibles
   // can be destroyed before the doc they own.
   if (!mOuterDoc) {
     uint32_t childCount = mChildren.Length();
     for (uint32_t idx = 0; idx < childCount; idx++)
       mChildren[idx]->Shutdown();
+  } else {
+    if (mChildren.Length() != 1)
+      MOZ_CRASH("outer doc doesn't own adoc!");
+
+    static_cast<DocAccessibleParent*>(mChildren[0])->Destroy();
   }
 
   mChildren.Clear();
   ProxyDestroyed(this);
   mDoc->RemoveAccessible(this);
 }
 
 void
@@ -39,35 +46,145 @@ ProxyAccessible::SetChildDoc(DocAccessib
     mOuterDoc = true;
   } else {
     MOZ_ASSERT(mChildren.Length() == 1);
     mChildren.Clear();
     mOuterDoc = false;
   }
 }
 
+bool
+ProxyAccessible::MustPruneChildren() const
+{
+  // this is the equivalent to nsAccUtils::MustPrune for proxies and should be
+  // kept in sync with that.
+  if (mRole != roles::MENUITEM && mRole != roles::COMBOBOX_OPTION
+      && mRole != roles::OPTION && mRole != roles::ENTRY
+      && mRole != roles::FLAT_EQUATION && mRole != roles::PASSWORD_TEXT
+      && mRole != roles::PUSHBUTTON && mRole != roles::TOGGLE_BUTTON
+      && mRole != roles::GRAPHIC && mRole != roles::SLIDER
+      && mRole != roles::PROGRESSBAR && mRole != roles::SEPARATOR)
+    return false;
+
+  if (mChildren.Length() != 1)
+    return false;
+
+  return mChildren[0]->Role() == roles::TEXT_LEAF
+    || mChildren[0]->Role() == roles::STATICTEXT;
+}
+
 uint64_t
 ProxyAccessible::State() const
 {
   uint64_t state = 0;
   unused << mDoc->SendState(mID, &state);
   return state;
 }
 
 void
 ProxyAccessible::Name(nsString& aName) const
 {
   unused << mDoc->SendName(mID, &aName);
 }
 
 void
+ProxyAccessible::Value(nsString& aValue) const
+{
+  unused << mDoc->SendValue(mID, &aValue);
+}
+
+void
 ProxyAccessible::Description(nsString& aDesc) const
 {
   unused << mDoc->SendDescription(mID, &aDesc);
 }
 
 void
 ProxyAccessible::Attributes(nsTArray<Attribute> *aAttrs) const
 {
   unused << mDoc->SendAttributes(mID, aAttrs);
 }
+
+nsTArray<ProxyAccessible*>
+ProxyAccessible::RelationByType(RelationType aType) const
+{
+  nsTArray<uint64_t> targetIDs;
+  unused << mDoc->SendRelationByType(mID, static_cast<uint32_t>(aType),
+                                     &targetIDs);
+
+  size_t targetCount = targetIDs.Length();
+  nsTArray<ProxyAccessible*> targets(targetCount);
+  for (size_t i = 0; i < targetCount; i++)
+    if (ProxyAccessible* proxy = mDoc->GetAccessible(targetIDs[i]))
+      targets.AppendElement(proxy);
+
+  return Move(targets);
+}
+
+void
+ProxyAccessible::Relations(nsTArray<RelationType>* aTypes,
+                           nsTArray<nsTArray<ProxyAccessible*>>* aTargetSets)
+  const
+{
+  nsTArray<RelationTargets> ipcRelations;
+  unused << mDoc->SendRelations(mID, &ipcRelations);
+
+  size_t relationCount = ipcRelations.Length();
+  aTypes->SetCapacity(relationCount);
+  aTargetSets->SetCapacity(relationCount);
+  for (size_t i = 0; i < relationCount; i++) {
+    uint32_t type = ipcRelations[i].Type();
+    if (type > static_cast<uint32_t>(RelationType::LAST))
+      continue;
+
+    size_t targetCount = ipcRelations[i].Targets().Length();
+    nsTArray<ProxyAccessible*> targets(targetCount);
+    for (size_t j = 0; j < targetCount; j++)
+      if (ProxyAccessible* proxy = mDoc->GetAccessible(ipcRelations[i].Targets()[j]))
+        targets.AppendElement(proxy);
+
+    if (targets.IsEmpty())
+      continue;
+
+    aTargetSets->AppendElement(Move(targets));
+    aTypes->AppendElement(static_cast<RelationType>(type));
+  }
+}
+
+void
+ProxyAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOfset,
+                               nsString& aText) const
+{
+  unused << mDoc->SendTextSubstring(mID, aStartOffset, aEndOfset, &aText);
+}
+
+void
+ProxyAccessible::GetTextAfterOffset(int32_t aOffset,
+                                    AccessibleTextBoundary aBoundaryType,
+                                    nsString& aText, int32_t* aStartOffset,
+                                    int32_t* aEndOffset)
+{
+  unused << mDoc->SendGetTextAfterOffset(mID, aOffset, aBoundaryType,
+                                         &aText, aStartOffset, aEndOffset);
+}
+
+void
+ProxyAccessible::GetTextAtOffset(int32_t aOffset,
+                                 AccessibleTextBoundary aBoundaryType,
+                                 nsString& aText, int32_t* aStartOffset,
+                                 int32_t* aEndOffset)
+{
+  unused << mDoc->SendGetTextAtOffset(mID, aOffset, aBoundaryType,
+                                      &aText, aStartOffset, aEndOffset);
+}
+
+void
+ProxyAccessible::GetTextBeforeOffset(int32_t aOffset,
+                                     AccessibleTextBoundary aBoundaryType,
+                                     nsString& aText, int32_t* aStartOffset,
+                                     int32_t* aEndOffset)
+{
+  unused << mDoc->SendGetTextBeforeOffset(mID, aOffset, aBoundaryType,
+                                          &aText, aStartOffset, aEndOffset);
+}
+
 }
 }
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -3,24 +3,26 @@
 /* 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_ProxyAccessible_h
 #define mozilla_a11y_ProxyAccessible_h
 
 #include "mozilla/a11y/Role.h"
+#include "nsIAccessibleText.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 namespace a11y {
 
 class Attribute;
 class DocAccessibleParent;
+enum class RelationType;
 
 class ProxyAccessible
 {
 public:
 
   ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
                   DocAccessibleParent* aDoc, role aRole) :
      mParent(aParent), mDoc(aDoc), mWrapper(0), mID(aID), mRole(aRole),
@@ -33,16 +35,18 @@ public:
     MOZ_COUNT_DTOR(ProxyAccessible);
     MOZ_ASSERT(!mWrapper);
   }
 
   void AddChildAt(uint32_t aIdx, ProxyAccessible* aChild)
   { mChildren.InsertElementAt(aIdx, aChild); }
 
   uint32_t ChildrenCount() const { return mChildren.Length(); }
+  ProxyAccessible* ChildAt(uint32_t aIdx) const { return mChildren[aIdx]; }
+  bool MustPruneChildren() const;
 
   void Shutdown();
 
   void SetChildDoc(DocAccessibleParent*);
 
   /**
    * Remove The given child.
    */
@@ -64,27 +68,61 @@ public:
    */
   uint64_t State() const;
 
   /*
    * Set aName to the name of the proxied accessible.
    */
   void Name(nsString& aName) const;
 
+  /*
+   * Set aValue to the value of the proxied accessible.
+   */
+  void Value(nsString& aValue) const;
+
   /**
    * Set aDesc to the description of the proxied accessible.
    */
   void Description(nsString& aDesc) const;
 
   /**
    * Get the set of attributes on the proxied accessible.
    */
   void Attributes(nsTArray<Attribute> *aAttrs) const;
 
   /**
+   * Return set of targets of given relation type.
+   */
+  nsTArray<ProxyAccessible*> RelationByType(RelationType aType) const;
+
+  /**
+   * Get all relations for this accessible.
+   */
+  void Relations(nsTArray<RelationType>* aTypes,
+                 nsTArray<nsTArray<ProxyAccessible*>>* aTargetSets) const;
+
+  /**
+   * Get the text between the given offsets.
+   */
+  void TextSubstring(int32_t aStartOffset, int32_t aEndOfset,
+                     nsString& aText) const;
+
+  void GetTextAfterOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType,
+                          nsString& aText, int32_t* aStartOffset,
+                          int32_t* aEndOffset);
+
+  void GetTextAtOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType,
+                       nsString& aText, int32_t* aStartOffset,
+                       int32_t* aEndOffset);
+
+  void GetTextBeforeOffset(int32_t aOffset, AccessibleTextBoundary aBoundaryType,
+                           nsString& aText, int32_t* aStartOffset,
+                           int32_t* aEndOffset);
+
+  /**
    * Allow the platform to store a pointers worth of data on us.
    */
   uintptr_t GetWrapper() const { return mWrapper; }
   void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
 
   /*
    * Return the ID of the accessible being proxied.
    */
@@ -103,12 +141,17 @@ private:
   nsTArray<ProxyAccessible*> mChildren;
   DocAccessibleParent* mDoc;
   uintptr_t mWrapper;
   uint64_t mID;
   role mRole : 31;
   bool mOuterDoc : 1;
 };
 
+enum Interfaces
+{
+  HYPERTEXT = 1
+};
+
 }
 }
 
 #endif
--- a/accessible/jsat/ContentControl.jsm
+++ b/accessible/jsat/ContentControl.jsm
@@ -282,18 +282,17 @@ this.ContentControl.prototype = {
 
     if (elem.tagName === 'INPUT' && elem.type === 'range') {
       elem[aStepUp ? 'stepDown' : 'stepUp']();
       let evt = this.document.createEvent('UIEvent');
       evt.initEvent('change', true, true);
       elem.dispatchEvent(evt);
     } else {
       let evt = this.document.createEvent('KeyboardEvent');
-      let keycode = aStepUp ? content.KeyEvent.DOM_VK_DOWN :
-        content.KeyEvent.DOM_VK_UP;
+      let keycode = aStepUp ? evt.DOM_VK_DOWN : evt.DOM_VK_UP;
       evt.initKeyEvent(
         "keypress", false, true, null, false, false, false, false, keycode, 0);
       elem.dispatchEvent(evt);
     }
 
     return true;
   },
 
--- a/accessible/jsat/EventManager.jsm
+++ b/accessible/jsat/EventManager.jsm
@@ -181,16 +181,24 @@ this.EventManager.prototype = {
         } else if (state.contains(States.SELECTED)) {
           this.present(
             Presentation.
               actionInvoked(aEvent.accessible,
                             event.isEnabled ? 'select' : 'unselect'));
         }
         break;
       }
+      case Events.NAME_CHANGE:
+      {
+        let acc = aEvent.accessible;
+        if (acc === this.contentControl.vc.position) {
+          this.present(Presentation.nameChanged(acc));
+        }
+        break;
+      }
       case Events.SCROLLING_START:
       {
         this.contentControl.autoMove(aEvent.accessible);
         break;
       }
       case Events.TEXT_CARET_MOVED:
       {
         let acc = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText);
--- a/accessible/jsat/Presentation.jsm
+++ b/accessible/jsat/Presentation.jsm
@@ -70,16 +70,22 @@ Presenter.prototype = {
 
   /**
    * Selection has changed. TODO.
    * @param {nsIAccessible} aObject the object that has been selected.
    */
   selectionChanged: function selectionChanged(aObject) {}, // jshint ignore:line
 
   /**
+   * Name has changed.
+   * @param {nsIAccessible} aAccessible the object whose value has changed.
+   */
+  nameChanged: function nameChanged(aAccessible) {}, // jshint ignore: line
+
+  /**
    * Value has changed.
    * @param {nsIAccessible} aAccessible the object whose value has changed.
    */
   valueChanged: function valueChanged(aAccessible) {}, // jshint ignore:line
 
   /**
    * The tab, or the tab's document state has changed.
    * @param {nsIAccessible} aDocObj the tab document accessible that has had its
@@ -309,27 +315,29 @@ AndroidPresenter.prototype.pivotChanged 
       details: androidEvents
     };
   };
 
 AndroidPresenter.prototype.actionInvoked =
   function AndroidPresenter_actionInvoked(aObject, aActionName) {
     let state = Utils.getState(aObject);
 
-    // Checkable objects will have a state changed event we will use instead.
-    if (state.contains(States.CHECKABLE)) {
-      return null;
+    // Checkable objects use TalkBack's text derived from the event state,
+    // so we don't populate the text here.
+    let text = '';
+    if (!state.contains(States.CHECKABLE)) {
+      text = Utils.localize(UtteranceGenerator.genForAction(aObject,
+        aActionName));
     }
 
     return {
       type: this.type,
       details: [{
         eventType: this.ANDROID_VIEW_CLICKED,
-        text: Utils.localize(UtteranceGenerator.genForAction(aObject,
-          aActionName)),
+        text: text,
         checked: state.contains(States.CHECKED)
       }]
     };
   };
 
 AndroidPresenter.prototype.tabSelected =
   function AndroidPresenter_tabSelected(aDocContext, aVCContext) {
     // Send a pivot change message with the full context utterance for this doc.
@@ -509,16 +517,27 @@ B2GPresenter.prototype.pivotChanged =
           reason: this.pivotChangedReasons[aReason],
           isUserInput: aIsUserInput,
           hints: aContext.interactionHints
         }
       }
     };
   };
 
+B2GPresenter.prototype.nameChanged =
+  function B2GPresenter_nameChanged(aAccessible) {
+    return {
+      type: this.type,
+      details: {
+        eventType: 'name-change',
+        data: aAccessible.name
+      }
+    };
+  };
+
 B2GPresenter.prototype.valueChanged =
   function B2GPresenter_valueChanged(aAccessible) {
 
     // the editable value changes are handled in the text changed presenter
     if (Utils.getState(aAccessible).contains(States.EDITABLE)) {
       return null;
     }
 
@@ -684,16 +703,20 @@ this.Presentation = { // jshint ignore:l
 
   textSelectionChanged: function textSelectionChanged(aText, aStart, aEnd,
                                                       aOldStart, aOldEnd,
                                                       aIsFromUserInput) {
     return [p.textSelectionChanged(aText, aStart, aEnd, aOldStart, aOldEnd, // jshint ignore:line
       aIsFromUserInput) for each (p in this.presenters)]; // jshint ignore:line
   },
 
+  nameChanged: function nameChanged(aAccessible) {
+    return [ p.nameChanged(aAccessible) for (p of this.presenters) ]; // jshint ignore:line
+  },
+
   valueChanged: function valueChanged(aAccessible) {
     return [ p.valueChanged(aAccessible) for (p of this.presenters) ]; // jshint ignore:line
   },
 
   tabStateChanged: function Presentation_tabStateChanged(aDocObj, aPageState) {
     return [p.tabStateChanged(aDocObj, aPageState) // jshint ignore:line
       for each (p in this.presenters)]; // jshint ignore:line
   },
--- a/accessible/jsat/TraversalRules.jsm
+++ b/accessible/jsat/TraversalRules.jsm
@@ -97,17 +97,18 @@ var gSimpleTraversalRoles =
    Roles.HEADER,
    Roles.HEADING,
    Roles.SLIDER,
    Roles.SPINBUTTON,
    Roles.OPTION,
    Roles.LISTITEM,
    Roles.GRID_CELL,
    Roles.COLUMNHEADER,
-   Roles.ROWHEADER];
+   Roles.ROWHEADER,
+   Roles.STATUSBAR];
 
 var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) {
   // An object is simple, if it either has a single child lineage,
   // or has a flat subtree.
   function isSingleLineage(acc) {
     for (let child = acc; child; child = child.firstChild) {
       if (Utils.visibleChildCount(child) > 1) {
         return false;
@@ -146,16 +147,17 @@ var gSimpleMatchFunc = function gSimpleM
     return Utils.isListItemDecorator(aAccessible) ?
       Filters.IGNORE : Filters.MATCH;
   case Roles.GRAPHIC:
     return TraversalRules._shouldSkipImage(aAccessible);
   case Roles.HEADER:
   case Roles.HEADING:
   case Roles.COLUMNHEADER:
   case Roles.ROWHEADER:
+  case Roles.STATUSBAR:
     if ((aAccessible.childCount > 0 || aAccessible.name) &&
         (isSingleLineage(aAccessible) || isFlatSubtree(aAccessible))) {
       return Filters.MATCH | Filters.IGNORE_SUBTREE;
     }
     return Filters.IGNORE;
   case Roles.GRID_CELL:
     return isSingleLineage(aAccessible) || isFlatSubtree(aAccessible) ?
       Filters.MATCH | Filters.IGNORE_SUBTREE : Filters.IGNORE;
--- a/accessible/mac/Platform.mm
+++ b/accessible/mac/Platform.mm
@@ -29,17 +29,17 @@ PlatformInit()
 }
 
 void
 PlatformShutdown()
 {
 }
 
 void
-ProxyCreated(ProxyAccessible*)
+ProxyCreated(ProxyAccessible*, uint32_t)
 {
 }
 
 void
 ProxyDestroyed(ProxyAccessible*)
 {
 }
 
--- a/accessible/other/Platform.cpp
+++ b/accessible/other/Platform.cpp
@@ -15,17 +15,17 @@ a11y::PlatformInit()
 }
 
 void
 a11y::PlatformShutdown()
 {
 }
 
 void
-a11y::ProxyCreated(ProxyAccessible*)
+a11y::ProxyCreated(ProxyAccessible*, uint32_t)
 {
 }
 
 void
 a11y::ProxyDestroyed(ProxyAccessible*)
 {
 }
 
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -208,17 +208,17 @@ const DONOTFAIL_IF_NO_INTERFACE = 2;
 function getAccessible(aAccOrElmOrID, aInterfaces, aElmObj, aDoNotFailIf)
 {
   if (!aAccOrElmOrID)
     return null;
 
   var elm = null;
 
   if (aAccOrElmOrID instanceof nsIAccessible) {
-    elm = aAccOrElmOrID.DOMNode;
+    try { elm = aAccOrElmOrID.DOMNode; } catch(e) { }
 
   } else if (aAccOrElmOrID instanceof nsIDOMNode) {
     elm = aAccOrElmOrID;
 
   } else {
     elm = document.getElementById(aAccOrElmOrID);
     if (!elm) {
       ok(false, "Can't get DOM element for " + aAccOrElmOrID);
@@ -695,18 +695,19 @@ function prettyName(aIdentifier)
 
       msg += prettyName(aIdentifier[idx]);
     }
     return msg;
   }
 
   if (aIdentifier instanceof nsIAccessible) {
     var acc = getAccessible(aIdentifier);
-    var msg = "[" + getNodePrettyName(acc.DOMNode);
+    var msg = "[";
     try {
+      msg += getNodePrettyName(acc.DOMNode);
       msg += ", role: " + roleToString(acc.role);
       if (acc.name)
         msg += ", name: '" + shortenString(acc.name) + "'";
     } catch (e) {
       msg += "defunct";
     }
 
     if (acc)
--- a/accessible/tests/mochitest/elm/test_canvas.html
+++ b/accessible/tests/mochitest/elm/test_canvas.html
@@ -13,42 +13,41 @@
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../layout.js"></script>
 
   <script type="application/javascript">
-  SpecialPowers.setBoolPref("canvas.hitregions.enabled", true);
-
     function doTest()
     {
       var canv = document.getElementById("c");
       var context = canv.getContext('2d');
       var element = document.getElementById("showA");
       context.beginPath();
       context.rect(10, 10, 150, 100);
       context.addHitRegion({control: element});
       var input = getAccessible("showA");
       var input = getAccessible("showA");
       var [cnvX, cnvY, cnvWidth, cnvHeight] = getBoundsForDOMElm(canv);
       var [accX, accY, accWidth, accHeight] = getBounds(input);
       is(accX, cnvX + 10, "accX should be 10 and not " + accX);
       is(accY, cnvY + 10, "accY should be 10 and not " + accY);
       is(accWidth, 150, "accWidth should be 150 and not " + accWidth);
       is(accHeight, 100, "accHeight should be 100 and not " + accHeight);
-
-      SpecialPowers.setBoolPref("canvas.hitregions.enabled", false);
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
-  </script>
+    addA11yLoadEvent(function() {
+      SpecialPowers.pushPrefEnv({"set": [['canvas.hitregions.enabled', true]]}, doTest);
+    });
+
+    </script>
 </head>
 <body>
 
   <canvas id="c">
     <input id="showA" type="checkbox"><label for="showA"> Show As </label>
   </canvas>
 
 </body>
--- a/accessible/tests/mochitest/events/test_aria_objattr.html
+++ b/accessible/tests/mochitest/events/test_aria_objattr.html
@@ -7,16 +7,18 @@
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
+          src="../attributes.js"></script>
+  <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
 
     /**
      * Do tests.
      */
     var gQueue = null;
@@ -35,25 +37,43 @@
       };
 
       this.getID = function updateAttribute_getID()
       {
         return aAttr + " for " + aID + " " + aValue;
       };
     }
 
+    function updateARIAHidden(aID, aIsDefined, aChildId)
+    {
+      this.__proto__ = new updateAttribute(aID, "aria-hidden",
+                                           aIsDefined ? "true" : "false");
+
+      this.finalCheck = function updateARIAHidden()
+      {
+        if (aIsDefined) {
+          testAttrs(aID, {"hidden" : "true"}, true);
+          testAttrs(aChildId, {"hidden" : "true"}, true);
+        } else {
+          testAbsentAttrs(aID, { "hidden": "true"});
+          testAbsentAttrs(aChildId, { "hidden": "true"});
+        }
+      }
+    }
+
     // Debug stuff.
     // gA11yEventDumpID = "eventdump";
     //gA11yEventDumpToConsole = true;
 
     function doTests()
     {
       gQueue = new eventQueue();
 
-      gQueue.push(new updateAttribute("hideable", "aria-hidden", "true"));
+      gQueue.push(new updateARIAHidden("hideable", true, "hideable_child"));
+      gQueue.push(new updateARIAHidden("hideable", false, "hideable_child"));
 
       gQueue.push(new updateAttribute("sortable", "aria-sort", "ascending"));
 
       // For experimental ARIA extensions
       gQueue.push(new updateAttribute("custom", "aria-blah", "true"));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
@@ -84,15 +104,15 @@
   </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
   <div id="eventdump"></div>
 
-  <div id="hideable"><div>Hi</div><div>there</div></div>
+  <div id="hideable"><div id="hideable_child">Hi</div><div>there</div></div>
 
   <div id="sortable" role="columnheader" aria-sort="none">aria-sort</div>
 
   <div id="custom" role="custom" aria-blah="false">Fat free cheese</div>
 </body>
 </html>
--- a/accessible/tests/mochitest/events/test_docload.html
+++ b/accessible/tests/mochitest/events/test_docload.html
@@ -10,16 +10,35 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../states.js"></script>
+
+  <script type="application/javascript">
+    // Front end stuff sometimes likes to stuff things in the hidden window(s)
+    // in which case there's accessibles for that content.
+    Components.utils.import("resource://gre/modules/Services.jsm");
+
+    // Force the creation of an accessible for the hidden window's document.
+    var doc = Services.appShell.hiddenDOMWindow.document;
+    gAccRetrieval.getAccessibleFor(doc);
+
+    // The private hidden window will be lazily created that's why we need to do
+    // it here *before* loading '../events.js' or else we'll have a duplicate
+    // reorder event.
+    var privateDoc = Services.appShell.hiddenPrivateDOMWindow.document;
+
+    // Force the creation of an accessible for the private hidden window's doc.
+    gAccRetrieval.getAccessibleFor(privateDoc);
+  </script>
+
   <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
     ////////////////////////////////////////////////////////////////////////////
     // Invokers
 
     function changeIframeSrc(aIdentifier, aURL)
@@ -174,16 +193,22 @@
         var accTree = {
           role: ROLE_APP_ROOT,
           children: [
             {
               role: ROLE_CHROME_WINDOW
             },
             {
               role: ROLE_CHROME_WINDOW
+            },
+            {
+              role: ROLE_CHROME_WINDOW
+            },
+            {
+              role: ROLE_CHROME_WINDOW
             }
           ]
         };
 
         testAccessibleTree(this.mRootAcc, accTree);
 
         var dlgDoc = this.mDialog.document;
         ok(isAccessibleInCache(dlgDoc),
--- a/accessible/tests/mochitest/hittest/test_canvas_hitregion.html
+++ b/accessible/tests/mochitest/hittest/test_canvas_hitregion.html
@@ -8,18 +8,16 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../layout.js"></script>
 
   <script type="application/javascript">
-    SpecialPowers.setBoolPref("canvas.hitregions.enabled", true);
-
     function redrawCheckbox(context, element, x, y)
     {
       context.save();
       context.font = '10px sans-serif';
       context.textAlign = 'left';
       context.textBaseline = 'middle';
       var metrics = context.measureText(element.parentNode.textContent);
       context.beginPath();
@@ -62,23 +60,23 @@
                           ". Found: " + prettyName(hitAcc));
 
       tgtY = hitY+75;
       hitAcc = docAcc.getDeepestChildAtPoint(tgtX, tgtY);
       // test that we don't hit the region associated with the shadow dom checkbox
       is(hitAcc, hitcanvas, "Hit match at " + tgtX + "," + tgtY +
                           ". Found: " + prettyName(hitAcc));
 
-      SpecialPowers.setBoolPref("canvas.hitregions.enabled", false);
       SimpleTest.finish();
     }
-
     SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
-  </script>
+    addA11yLoadEvent(function() {
+      SpecialPowers.pushPrefEnv({"set": [['canvas.hitregions.enabled', true]]}, doTest);
+    });
+ </script>
 </head>
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=966591"
      title="nsIAccessible::childAtPoint() for canvas hit regions from browser tests">Mozilla Bug 966591</a>
 
   <canvas id="hitcanvas">
--- a/accessible/tests/mochitest/jsat/doc_content_integration.html
+++ b/accessible/tests/mochitest/jsat/doc_content_integration.html
@@ -35,16 +35,20 @@
     function ariaShowIframe() {
       document.getElementById('iframe').setAttribute('aria-hidden', false);
     }
 
     function ariaHideIframe() {
       document.getElementById('iframe').setAttribute('aria-hidden', true);
     }
 
+    function renameFruit() {
+      document.getElementById('fruit').setAttribute('aria-label', 'banana');
+    }
+
   </script>
   <style>
     #windows {
       position: relative;
       width: 320px;
       height: 480px;
     }
 
@@ -79,10 +83,11 @@
       <h1>This is an alert!</h1>
       <p>Do you agree?</p>
       <button onclick="setTimeout(hideAlert, 500)">Yes</button>
       <button onclick="hideAlert()">No</button>
     </div>
     <div id="appframe"></div>
   </div>
   <button id="home">Home</button>
+  <button id="fruit" aria-label="apple"></button>
 </body>
 </html>
--- a/accessible/tests/mochitest/jsat/doc_traversal.html
+++ b/accessible/tests/mochitest/jsat/doc_traversal.html
@@ -136,10 +136,12 @@
     </tfoot>
     <tbody>
       <tr>
         <td>Dirt</td>
         <td>Messy Stuff</td>
       </tr>
     </tbody>
   </table>
+  <div id="statusbar-1" role="status">Last sync:<span>2 days ago</span></div>
+  <div aria-label="Last sync: 30min ago" id="statusbar-2" role="status"></div>
 </body>
 </html>
--- a/accessible/tests/mochitest/jsat/jsatcommon.js
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -612,16 +612,25 @@ function ExpectedCheckAction(aChecked, a
   }, [{
     eventType: AndroidEvent.VIEW_CLICKED,
     checked: aChecked
   }], aOptions);
 }
 
 ExpectedCheckAction.prototype = Object.create(ExpectedPresent.prototype);
 
+function ExpectedNameChange(aName, aOptions) {
+  ExpectedPresent.call(this, {
+    eventType: 'name-change',
+    data: aName
+  }, null, aOptions);
+}
+
+ExpectedNameChange.prototype = Object.create(ExpectedPresent.prototype);
+
 function ExpectedValueChange(aValue, aOptions) {
   ExpectedPresent.call(this, {
     eventType: 'value-change',
     data: [aValue]
   }, null, aOptions);
 }
 
 ExpectedValueChange.prototype = Object.create(ExpectedPresent.prototype);
--- a/accessible/tests/mochitest/jsat/test_content_integration.html
+++ b/accessible/tests/mochitest/jsat/test_content_integration.html
@@ -43,39 +43,43 @@
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['many option', {'string': 'stateNotChecked'},
             {'string': 'checkbutton'}, {'string': 'listStart'},
             {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
 
           // check checkbox
           [ContentMessages.activateCurrent(),
            new ExpectedClickAction({ no_android: true }),
-           new ExpectedCheckAction(true, { android_todo: true })],
+           new ExpectedCheckAction(true)],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['much range', {'string': 'label'}])],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['much range', '5', {'string': 'slider'}])],
           [ContentMessages.moveOrAdjustUp(), new ExpectedValueChange('6')],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+          [ContentMessages.simpleMoveNext,
+           new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
 
           // Simple traversal backward
           [ContentMessages.simpleMovePrevious,
+           new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+          [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['much range', '6', {'string': 'slider'}, 'such app'])],
           [ContentMessages.moveOrAdjustDown(), new ExpectedValueChange('5')],
           [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['much range', {'string': 'label'}])],
           [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['many option', {'string': 'stateChecked'},
             {'string': 'checkbutton'}, {'string': 'listStart'},
             {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
           // uncheck checkbox
           [ContentMessages.activateCurrent(),
            new ExpectedClickAction({ no_android: true }),
-           new ExpectedCheckAction(false, { android_todo: true })],
+           new ExpectedCheckAction(false)],
           [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['wow', {'string': 'headingLevel', 'args': [1]}])],
           [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
           [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['Phone status bar'])],
 
           [ContentMessages.simpleMoveNext,
@@ -83,17 +87,17 @@
           // Moving to the absolute last item from an embedded document
           // fails. Bug 972035.
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(
             ['wow', {'string': 'headingLevel', 'args': [1]}, 'such app'])],
           // Move from an inner frame to the last element in the parent doc
           [ContentMessages.simpleMoveLast,
             new ExpectedCursorChange(
-              ['Home', {'string': 'pushbutton'}], { b2g_todo: true })],
+              ['apple', {'string': 'pushbutton'}], { b2g_todo: true })],
 
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
           [ContentMessages.moveOrAdjustDown('FormElement'),
            new ExpectedCursorChange(['Back', {"string": "pushbutton"}])],
           [ContentMessages.moveOrAdjustDown('FormElement'),
@@ -131,16 +135,27 @@
              {'string': 'checkbutton'}, {'string': 'listStart'},
              {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
           [ContentMessages.simpleMoveFirst,
             new ExpectedCursorChange(['Phone status bar'], { b2g_todo: true })],
 
           // Reset cursors
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
+          // Current virtual cursor's position's name changes
+          [ContentMessages.simpleMoveNext,
+           new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
+          [ContentMessages.focusSelector('button#fruit', false),
+           new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
+          [doc.defaultView.renameFruit, new ExpectedNameChange('banana')],
+
+          // Blur button and reset cursor
+          [ContentMessages.focusSelector('button#fruit', true), null],
+          [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
           // Move cursor with focus in outside document
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
           [ContentMessages.focusSelector('button#home', false),
            new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
 
           // Blur button and reset cursor
           [ContentMessages.focusSelector('button#home', true), null],
@@ -202,24 +217,24 @@
            new ExpectedCursorChange(
              ["wow", {"string": "headingLevel","args": [1]}, "such app"])],
           [doc.defaultView.ariaShowBack],
           [ContentMessages.focusSelector('button#back', true), null],
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           // Open dialog in outer doc, while cursor is also in outer doc
           [ContentMessages.simpleMoveLast,
-           new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+           new ExpectedCursorChange(['banana', {'string': 'pushbutton'}])],
           [doc.defaultView.showAlert,
             new ExpectedCursorChange(['This is an alert!',
               {'string': 'headingLevel', 'args': [1]},
               {'string': 'dialog'}])],
 
           [doc.defaultView.hideAlert,
-           new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+           new ExpectedCursorChange(['banana', {'string': 'pushbutton'}])],
 
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           // Open dialog in outer doc, while cursor is in inner frame
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
@@ -248,16 +263,18 @@
            new ExpectedCursorChange(['This is an alert!',
             {'string': 'headingLevel', 'args': [1]}, {'string': 'dialog'}])],
 
           [function hideAlertAndFocusHomeButton() {
             doc.defaultView.hideAlert();
             doc.querySelector('button#home').focus();
           }, new ExpectedCursorChange(['Home', {'string': 'pushbutton'},
             'Traversal Rule test document'])],
+          [ContentMessages.simpleMoveNext,
+            new ExpectedCursorChange(['banana', {'string': 'pushbutton'}])]
           [ContentMessages.simpleMoveNext, new ExpectedNoMove()]
         ]);
 
         addA11yLoadEvent(function() {
           contentTest.start(function () {
             closeBrowserWindow();
             SimpleTest.finish();
           });
--- a/accessible/tests/mochitest/jsat/test_output.html
+++ b/accessible/tests/mochitest/jsat/test_output.html
@@ -472,16 +472,27 @@ https://bugzilla.mozilla.org/show_bug.cg
         }, {
           accOrElmOrID: "labelled-combobox",
           expectedUtterance: [[{"string": "stateCollapsed"},
             {"string": "stateHasPopup"}, {"string": "combobox"}, "Never",
             "Intervals"], ["Intervals", "Never", {"string": "stateCollapsed"},
             {"string": "stateHasPopup"}, {"string": "combobox"}]],
           expectedBraille: [[{"string": "comboboxAbbr"}, "Never", "Intervals"],
             ["Intervals", "Never", {"string": "comboboxAbbr"}]]
+        }, {
+          accOrElmOrID: "statusbar-1",
+          expectedUtterance: [["Last sync:", "2 days ago"],
+                              ["Last sync:", "2 days ago"]],
+          expectedBraille: [["Last sync:", "2 days ago"],
+                            ["Last sync:", "2 days ago"]]
+        }, {
+          accOrElmOrID: "statusbar-2",
+          expectedUtterance: [["Last sync: 30min ago"],
+                              ["Last sync: 30min ago"]],
+          expectedBraille: [["Last sync: 30min ago"], ["Last sync: 30min ago"]]
         }];
 
         // Test all possible utterance order preference values.
         tests.forEach(function run(test) {
           var utteranceOrderValues = [0, 1];
           utteranceOrderValues.forEach(
             function testUtteranceOrder(utteranceOrder) {
               SpecialPowers.setIntPref(PREF_UTTERANCE_ORDER, utteranceOrder);
@@ -627,11 +638,13 @@ https://bugzilla.mozilla.org/show_bug.cg
         <option id="combobox-option" value="30">30 min</option>
         <option value="null">Manual</option>
       </select>
       <select id="labelled-combobox" aria-label="Intervals">
         <option value="15">Every 15 min</option>
         <option value="30">Every 30 min</option>
         <option value="null" selected>Never</option>
       </select>
+      <div id="statusbar-1" role="status">Last sync:<span>2 days ago</span></div>
+      <div aria-label="Last sync: 30min ago" id="statusbar-2" role="status"></div>
     </div>
   </body>
 </html>
--- a/accessible/tests/mochitest/jsat/test_traversal.html
+++ b/accessible/tests/mochitest/jsat/test_traversal.html
@@ -117,17 +117,17 @@
                               '4. Standard Lisp', 'link-0', ' Lisp',
                               'checkbox-1-5', ' LeLisp', '• JavaScript',
                               'heading-5', 'image-2', 'image-3',
                               'Not actually an image', 'link-1', 'anchor-1',
                               'link-2', 'anchor-2', 'link-3', '3', '1', '4',
                               '1', 'Sunday', 'M', 'Week 1', '3', '4', '7', '2',
                               '5 8', 'gridcell4', 'Just an innocuous separator',
                               'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
-                              'Dirt', 'Messy Stuff']);
+                              'Dirt', 'Messy Stuff', 'statusbar-1', 'statusbar-2']);
 
       gQueue.invoke();
     }
 
     SimpleTest.waitForExplicitFinish();
     addLoadEvent(function () {
       /* We open a new browser because we need to test with a top-level content
          document. */
--- a/accessible/tests/mochitest/relations/test_general.html
+++ b/accessible/tests/mochitest/relations/test_general.html
@@ -69,16 +69,25 @@
       testRelation("descr2", RELATION_DESCRIPTION_FOR, "checkbox5");
       testRelation("descr3", RELATION_DESCRIPTION_FOR, "checkbox5");
       testRelation("checkbox5", RELATION_DESCRIBED_BY, ["descr2", "descr3"]);
 
       // aria_owns, multiple relations
       testRelation("treeitem1", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
 
+      // aria-owns, bad relations
+      testRelation("ariaowns_container", RELATION_NODE_CHILD_OF, null);
+      testRelation("ariaowns_self", RELATION_NODE_CHILD_OF, null);
+      testRelation("ariaowns_uncle", RELATION_NODE_CHILD_OF, "ariaowns_self");
+
+      testRelation("ariaowns_container", RELATION_NODE_PARENT_OF, null);
+      testRelation("ariaowns_self", RELATION_NODE_PARENT_OF, "ariaowns_uncle");
+      testRelation("ariaowns_uncle", RELATION_NODE_PARENT_OF, null);
+
       // 'node child of' relation for outlineitem role
       testRelation("treeitem3", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem4", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem5", RELATION_NODE_CHILD_OF, "treeitem4");
       testRelation("treeitem6", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem7", RELATION_NODE_CHILD_OF, "treeitem6");
       testRelation("tree2_ti1", RELATION_NODE_CHILD_OF, "tree2");
       testRelation("tree2_ti1a", RELATION_NODE_CHILD_OF, "tree2_ti1");
@@ -305,16 +314,22 @@
     <div role="treeitem" id="treeitem4" aria-level="1">Green</div>
     <div role="treeitem" id="treeitem5" aria-level="2">Light green</div>
     <div role="treeitem" id="treeitem6" aria-level="1">Green2</div>
     <div role="group">
       <div role="treeitem" id="treeitem7">Super light green</div>
     </div>
   </div>
 
+  <div id="ariaowns_container">
+    <div id="ariaowns_self"
+         aria-owns="aria_ownscontainer ariaowns_self ariaowns_uncle"></div>
+  </div>
+  <div id="ariaowns_uncle"></div>
+
   <div aria-owns="simplegrid-ownrow" role="grid" id="simplegrid">
     <div role="row" id="simplegrid-row1" aria-level="1">
       <div role="gridcell">cell 1,1</div>
       <div role="gridcell">cell 1,2</div>
     </div>
     <div role="row" id="simplegrid-row2" aria-level="1">
       <div role="gridcell">cell 2,1</div>
       <div role="gridcell">cell 2,2</div>
--- a/accessible/tests/mochitest/states.js
+++ b/accessible/tests/mochitest/states.js
@@ -193,28 +193,39 @@ function testStatesInSubtree(aAccOrElmOr
   if (children) {
     for (var i = 0; i < children.length; i++) {
       var childAcc = children.queryElementAt(i, nsIAccessible);
       testStatesInSubtree(childAcc, aState, aExtraState, aAbsentState);
     }
   }
 }
 
+/**
+ * Fails if no defunct state on the accessible.
+ */
+function testIsDefunct(aAccessible, aTestName)
+{
+  var id = prettyName(aAccessible) + (aTestName ? " [" + aTestName + "]" : "");
+  var [state, extraState] = getStates(aAccessible);
+  isState(extraState & EXT_STATE_DEFUNCT, EXT_STATE_DEFUNCT, true,
+          "no defuct state for " + id + "!");
+}
+
 function getStringStates(aAccOrElmOrID)
 {
   var [state, extraState] = getStates(aAccOrElmOrID);
   return statesToString(state, extraState);
 }
 
 function getStates(aAccOrElmOrID)
 {
   var acc = getAccessible(aAccOrElmOrID);
   if (!acc)
     return [0, 0];
-  
+
   var state = {}, extraState = {};
   acc.getState(state, extraState);
 
   return [state.value, extraState.value];
 }
 
 /**
  * Return true if the accessible has given states.
--- a/accessible/tests/mochitest/table/test_headers_ariagrid.html
+++ b/accessible/tests/mochitest/table/test_headers_ariagrid.html
@@ -57,31 +57,52 @@
       testHeaderCells(headerInfoMap);
 
 
       //////////////////////////////////////////////////////////////////////////
       // column and row headers from markup for crazy grid.
 
       headerInfoMap = [
         {
-          // not focusable cell (nsARIAGridCellAccessible is used)
+          // not focusable cell (ARIAGridCellAccessible is used)
           cell: "table2_dc_1",
           rowHeaderCells: [],
           columnHeaderCells: [ "table2_ch_1" ]
         },
         {
-          // focusable cell (nsARIAGridCellAccessible is used)
+          // focusable cell (ARIAGridCellAccessible is used)
           cell: "table2_dc_2",
           rowHeaderCells: [],
           columnHeaderCells: [ "table2_ch_2" ]
         }
       ];
 
       testHeaderCells(headerInfoMap);
 
+
+      //////////////////////////////////////////////////////////////////////////
+      // column and row headers from markup for one more crazy grid.
+
+      headerInfoMap = [
+        {
+          // ARIAGridCellAccessible is used
+          cell: "t3_dc_1",
+          rowHeaderCells: [ "t3_rh_1" ],
+          columnHeaderCells: [ ]
+        },
+        {
+          // ARIAGridCellAccessible is used (inside rowgroup)
+          cell: "t3_dc_2",
+          rowHeaderCells: [ "t3_rh_2" ],
+          columnHeaderCells: [ ]
+        }
+      ];
+
+      testHeaderCells(headerInfoMap);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 
@@ -130,10 +151,35 @@
         <tr>
           <td id="table2_dc_1" role="gridcell">cell1</td>
           <td id="table2_dc_2" role="gridcell" tabindex="-1">cell2</td>
         </tr>
       </table>
     </div>
   </div>
 
+  <div role="grid">
+    <table role="presentation">
+      <tbody role="presentation">
+        <tr role="row">
+          <th id="t3_rh_1" role="rowheader">Row 1</th>
+          <td id="t3_dc_1" role="gridcell" tabindex="-1">
+            Apple Inc.
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <div role="rowgroup" tabindex="0">
+      <table role="presentation">
+        <tbody role="presentation">
+          <tr role="row">
+            <th id="t3_rh_2" role="rowheader">Row 2</th>
+            <td id="t3_dc_2" role="gridcell" tabindex="-1">
+              Apple-Shmapple Inc.
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </div>
+
 </body>
 </html>
--- a/accessible/tests/mochitest/tree/test_aria_presentation.html
+++ b/accessible/tests/mochitest/tree/test_aria_presentation.html
@@ -16,122 +16,168 @@
           src="../role.js"></script>
 
   <script type="application/javascript">
   function doTest()
   {
     // Presentation role don't allow accessible.
     var tree =
       { SECTION: [ // container
-        { TEXT_LEAF: [ ] } // child text of presentation node
+        { TEXT_LEAF: [ ] }, // child text of 'presentation' node
+        { TEXT_LEAF: [ ] } // child text of 'none' node
       ] };
     testAccessibleTree("div_cnt", tree);
 
-    // Focusable element, presentation role is ignored.
+    // Focusable element, 'presentation' and 'none' roles are ignored.
     tree =
       { SECTION: [ // container
-        { PUSHBUTTON: [ // button
+        { PUSHBUTTON: [ // button having 'presentation' role
+          { TEXT_LEAF: [ ] }
+        ] },
+        { PUSHBUTTON: [ // button having 'none' role
           { TEXT_LEAF: [ ] }
         ] }
       ] };
     testAccessibleTree("btn_cnt", tree);
 
     // Presentation table, no table structure is exposed.
     tree =
       { SECTION: [ // container
-        { TEXT_CONTAINER: [ // td generic accessible
+        { TEXT_CONTAINER: [ // td generic accessible inside 'presentation' table
+          { TEXT_LEAF: [ ] } // cell text
+        ] },
+        { TEXT_CONTAINER: [ // td generic accessible inside 'none' table
           { TEXT_LEAF: [ ] } // cell text
         ] }
       ] };
     testAccessibleTree("tbl_cnt", tree);
 
-    // Focusable table, presentation role is ignored.
+    // Focusable table, 'presentation' and 'none' roles are ignored.
     tree =
       { SECTION: [ // container
-        { TABLE: [ // table
+        { TABLE: [ // table having 'presentation' role
           { ROW: [ // tr
-            { CELL: [ //td
+            { CELL: [ // td
+              { TEXT_LEAF: [ ] }
+            ] }
+          ] }
+        ] },
+        { TABLE: [ // table having 'none' role
+          { ROW: [ // tr
+            { CELL: [ // td
               { TEXT_LEAF: [ ] }
             ] }
           ] }
         ] }
       ] };
     testAccessibleTree("tblfocusable_cnt", tree);
 
     // Presentation list, expose generic accesisble for list items.
     tree =
       { SECTION: [ // container
-        { PARAGRAPH: [ // li generic accessible
+        { PARAGRAPH: [ // li generic accessible inside 'presentation' role
+          { TEXT_LEAF: [ ] } // li text
+        ] },
+        { PARAGRAPH: [ // li generic accessible inside 'none' role
           { TEXT_LEAF: [ ] } // li text
         ] }
       ] };
     testAccessibleTree("list_cnt", tree);
 
-    // Has ARIA globals or referred by ARIA relationship.
+    // Has ARIA globals or referred by ARIA relationship, role='presentation'
+    // and role='none' are ignored.
     tree =
       { SECTION: [ // container
         { LABEL: [ // label, has aria-owns
           { TEXT_LEAF: [ ] }
         ] },
         { TEXT_LEAF: [ ] },
         { LABEL: [ // label, referenced by aria-owns
           { TEXT_LEAF: [ ] }
+        ] },
+        { TEXT_LEAF: [ ] },
+        { LABEL: [ // label, has aria-owns
+          { TEXT_LEAF: [ ] }
+        ] },
+        { TEXT_LEAF: [ ] },
+        { LABEL: [ // label, referenced by aria-owns
+          { TEXT_LEAF: [ ] }
         ] }
       ] };
     testAccessibleTree("airaglobalprop_cnt", tree);
 
     SimpleTest.finish();
   }
 
   SimpleTest.waitForExplicitFinish();
   addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=548291"
      title="Accessible tree of ARIA image maps">
-    Mozilla Bug 548291
+    Bug 548291
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=666504"
      title="Ignore role presentation on focusable elements">
-    Mozilla Bug 666504
+    Bug 666504
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=971212"
+     title="Implement ARIA role=none">
+    Bug 971212
   </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <div id="div_cnt"><div role="presentation">text</div></div>
+  <div id="div_cnt"><div role="presentation">t</div><div role="none">t</div></div>
 
-  <div id="btn_cnt"><button role="presentation">btn</button></div>
+  <div id="btn_cnt"><button role="presentation">btn</button><button role="none">btn</button></div>
 
   <div id="tbl_cnt">
     <table role="presentation">
       <tr>
         <td>cell</td>
       </tr>
     </table>
+    <table role="none">
+      <tr>
+        <td>cell</td>
+      </tr>
+    </table>
   </div>
 
   <div id="tblfocusable_cnt">
     <table role="presentation" tabindex="0">
       <tr>
         <td>cell</td>
       </tr>
     </table>
+    <table role="none" tabindex="0">
+      <tr>
+        <td>cell</td>
+      </tr>
+    </table>
   </div>
 
   <div id="list_cnt">
     <ul role="presentation">
       <li>item</li>
     </ul>
+    <ul role="none">
+      <li>item</li>
+    </ul>
   </div>
 
   <div id="airaglobalprop_cnt">
     <label role="presentation" aria-owns="ariaowned">has aria-owns</label>
     <label role="presentation" id="ariaowned">referred by aria-owns</label>
+    <label role="none" aria-owns="ariaowned2">has aria-owns</label>
+    <label role="none" id="ariaowned2">referred by aria-owns</label>
   </div>
 
 </body>
 </html>
--- a/accessible/tests/mochitest/tree/test_txtctrl.xul
+++ b/accessible/tests/mochitest/tree/test_txtctrl.xul
@@ -35,33 +35,42 @@
       // default textbox
       testAccessibleTree("txc", accTree);
 
       // multiline
       testAccessibleTree("txc_multiline", accTree);
 
       //////////////////////////////////////////////////////////////////////////
       // search textbox
+      accTree =
+        { SECTION: [
+          { ENTRY: [ { TEXT_LEAF: [] } ] },
+          { MENUPOPUP: [] }
+        ] };
+      testAccessibleTree("txc_search", accTree);
+
+      //////////////////////////////////////////////////////////////////////////
+      // search textbox with search button
 
       if (MAC) {
         accTree =
           { SECTION: [
             { ENTRY: [ { TEXT_LEAF: [] } ] },
             { MENUPOPUP: [] }
           ] };
       } else {
         accTree =
           { SECTION: [
             { ENTRY: [ { TEXT_LEAF: [] } ] },
             { PUSHBUTTON: [] },
             { MENUPOPUP: [] }
           ] };
       }
 
-      testAccessibleTree("txc_search", accTree);
+      testAccessibleTree("txc_search_searchbutton", accTree);
 
       //////////////////////////////////////////////////////////////////////////
       // number textbox
 
       accTree =
         { SECTION: [
           { ENTRY: [ { TEXT_LEAF: [] } ] },
           { MENUPOPUP: [] },
@@ -190,16 +199,17 @@
       </div>
       <pre id="test">
       </pre>
     </body>
 
     <vbox flex="1">
       <textbox id="txc" value="hello"/>
       <textbox id="txc_search" type="search" value="hello"/>
+      <textbox id="txc_search_searchbutton" searchbutton="true" type="search" value="hello"/>
       <textbox id="txc_number" type="number" value="44"/>
       <textbox id="txc_password" type="password" value="hello"/>
       <textbox id="txc_multiline" multiline="true" value="hello"/>
       <textbox id="txc_autocomplete" type="autocomplete" value="hello"/>
     </vbox>
   </hbox>
 
 </window>
--- a/accessible/tests/mochitest/treeupdate/a11y.ini
+++ b/accessible/tests/mochitest/treeupdate/a11y.ini
@@ -19,11 +19,12 @@ skip-if = buildapp == "mulet"
 [test_list.html]
 [test_list_editabledoc.html]
 [test_listbox.xul]
 [test_menu.xul]
 [test_menubutton.xul]
 [test_optgroup.html]
 [test_recreation.html]
 [test_select.html]
+[test_shutdown.xul]
 [test_textleaf.html]
 [test_visibility.html]
 [test_whitespace.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_shutdown.xul
@@ -0,0 +1,132 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Accessible XUL tree hierarchy tests">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+  <script type="application/javascript"
+          src="../treeview.js" />
+
+  <script type="application/javascript"
+          src="../common.js" />
+  <script type="application/javascript"
+          src="../role.js" />
+  <script type="application/javascript"
+          src="../states.js" />
+  <script type="application/javascript"
+          src="../events.js" />
+
+  <script type="application/javascript">
+  <![CDATA[
+    function setXULTreeView(aTreeID, aTreeView)
+    {
+      this.treeNode = getNode(aTreeID);
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, this.treeNode)
+      ];
+
+      this.invoke = function loadXULTree_invoke()
+      {
+        this.treeNode.view = aTreeView;
+      };
+
+      this.getID = function loadXULTree_getID()
+      {
+        return "Load XUL tree " + prettyName(aTreeID);
+      };
+    }
+
+    function removeTree(aID)
+    {
+      this.tree = getAccessible(aID);
+      this.lastItem = null;
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, document)
+      ];
+
+      this.invoke = function invoke()
+      {
+        this.lastItem = getAccessible(aID).lastChild;
+        this.lastCell = this.lastItem.lastChild;
+        getNode(aID).parentNode.removeChild(getNode(aID));
+      };
+
+      this.check = function check(aEvent)
+      {
+        testIsDefunct(this.tree, aID);
+        testIsDefunct(this.lastItem, "last item of " + aID);
+        if (this.lastCell) {
+          testIsDefunct(this.lastCell, "last item cell of " + aID);
+        }
+      };
+
+      this.getID = function getID()
+      {
+        return "Remove tree from DOM";
+      };
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Test
+
+    // gA11yEventDumpID = "debug";
+    var gQueue = null;
+
+    function doTest()
+    {
+      gQueue = new eventQueue();
+
+      gQueue.push(new setXULTreeView("tree", new nsTreeTreeView()));
+      gQueue.push(new removeTree("tree"));
+
+      gQueue.push(new setXULTreeView("treetable", new nsTreeTreeView()));
+      gQueue.push(new removeTree("treetable"));
+
+      gQueue.invoke(); // Will call SimpleTest.finish()
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  ]]>
+  </script>
+
+  <hbox flex="1" style="overflow: auto;">
+    <body xmlns="http://www.w3.org/1999/xhtml">
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
+         title="Reorganize implementation of XUL tree accessibility">
+        Bug 503727
+      </a><br/>
+      <p id="display"></p>
+      <div id="content" style="display: none">
+      </div>
+      <pre id="test">
+      </pre>
+    </body>
+
+    <vbox flex="1">
+      <tree id="tree" flex="1">
+        <treecols>
+          <treecol id="col" flex="1" primary="true" label="column"/>
+        </treecols>
+        <treechildren/>
+      </tree>
+
+      <tree id="treetable" flex="1">
+        <treecols>
+          <treecol id="col1" flex="1" primary="true" label="column"/>
+          <treecol id="col2" flex="1" label="column 2"/>
+        </treecols>
+        <treechildren/>
+      </tree>
+    </vbox>
+  </hbox>
+
+</window>
--- a/accessible/tests/mochitest/treeview.js
+++ b/accessible/tests/mochitest/treeview.js
@@ -55,17 +55,17 @@ function nsTableTreeView(aRowCount)
 
   for (var idx = 0; idx < aRowCount; idx++)
     this.mData.push(new treeItem("row" + String(idx) + "_"));
 }
 
 function nsTreeTreeView()
 {
   this.__proto__ = new nsTreeView();
-  
+
   this.mData = [
     new treeItem("row1"),
     new treeItem("row2_", true, [new treeItem("row2.1_"), new treeItem("row2.2_")]),
     new treeItem("row3_", false, [new treeItem("row3.1_"), new treeItem("row3.2_")]),
     new treeItem("row4")
   ];
 }
 
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -9,16 +9,17 @@
 
 #include "Compatibility.h"
 #include "DocAccessible-inl.h"
 #include "EnumVariant.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 #include "nsIAccessibleEvent.h"
 #include "nsWinUtils.h"
+#include "mozilla/a11y/ProxyAccessible.h"
 #include "ServiceProvider.h"
 #include "Relation.h"
 #include "Role.h"
 #include "RootAccessible.h"
 #include "sdnAccessible.h"
 #include "States.h"
 
 #ifdef A11Y_LOG
@@ -75,17 +76,23 @@ AccessibleWrap::QueryInterface(REFIID ii
 {
   A11Y_TRYBLOCK_BEGIN
 
   if (!ppv)
     return E_INVALIDARG;
 
   *ppv = nullptr;
 
-  if (IID_IUnknown == iid || IID_IDispatch == iid || IID_IAccessible == iid)
+  if (IID_IUnknown == iid)
+    *ppv = static_cast<IAccessible*>(this);
+
+  if (!*ppv && IsProxy())
+    return E_NOINTERFACE;
+
+  if (IID_IDispatch == iid || IID_IAccessible == iid)
     *ppv = static_cast<IAccessible*>(this);
   else if (IID_IEnumVARIANT == iid) {
     // Don't support this interface for leaf elements.
     if (!HasChildren() || nsAccUtils::MustPrune(this))
       return E_NOINTERFACE;
 
     *ppv = static_cast<IEnumVARIANT*>(new ChildrenEnumVariant(this));
   } else if (IID_IServiceProvider == iid)
@@ -142,16 +149,26 @@ AccessibleWrap::get_accParent( IDispatch
   if (!ppdispParent)
     return E_INVALIDARG;
 
   *ppdispParent = nullptr;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  if (IsProxy()) {
+    ProxyAccessible* proxy = Proxy();
+    ProxyAccessible* parent = proxy->Parent();
+    if (!parent)
+      return S_FALSE;
+
+    *ppdispParent = NativeAccessible(WrapperFor(parent));
+    return S_OK;
+  }
+
   DocAccessible* doc = AsDoc();
   if (doc) {
     // Return window system accessible object for root document and tab document
     // accessibles.
     if (!doc->ParentDocument() ||
         (nsWinUtils::IsWindowEmulationStarted() &&
          nsCoreUtils::IsTabDocument(doc->DocumentNode()))) {
       HWND hwnd = static_cast<HWND>(doc->GetNativeWindow());
@@ -181,16 +198,25 @@ AccessibleWrap::get_accChildCount( long 
   if (!pcountChildren)
     return E_INVALIDARG;
 
   *pcountChildren = 0;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  if (IsProxy()) {
+    ProxyAccessible* proxy = Proxy();
+    if (proxy->MustPruneChildren())
+      return S_OK;
+
+    *pcountChildren = proxy->ChildrenCount();
+    return S_OK;
+  }
+
   if (nsAccUtils::MustPrune(this))
     return S_OK;
 
   *pcountChildren = ChildCount();
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
@@ -245,17 +271,20 @@ AccessibleWrap::get_accName(
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   nsAutoString name;
-  xpAccessible->Name(name);
+  if (xpAccessible->IsProxy())
+    xpAccessible->Proxy()->Name(name);
+  else
+    xpAccessible->Name(name);
 
   // The name was not provided, e.g. no alt attribute for an image. A screen
   // reader may choose to invent its own accessible name, e.g. from an image src
   // attribute. Refer to eNoNameOnPurpose return value.
   if (name.IsVoid())
     return S_FALSE;
 
   *pszName = ::SysAllocStringLen(name.get(), name.Length());
@@ -284,16 +313,20 @@ AccessibleWrap::get_accValue(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (IsProxy())
+    return E_NOTIMPL;
+
   if (xpAccessible->NativeRole() == roles::PASSWORD_TEXT)
     return E_ACCESSDENIED;
 
   nsAutoString value;
   xpAccessible->Value(value);
 
   // See bug 438784: need to expose URL on doc's value attribute. For this,
   // reverting part of fix for bug 425693 to make this MSAA method behave
@@ -326,17 +359,20 @@ AccessibleWrap::get_accDescription(VARIA
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   nsAutoString description;
-  xpAccessible->Description(description);
+  if (IsProxy())
+    xpAccessible->Proxy()->Description(description);
+  else
+    xpAccessible->Description(description);
 
   *pszDescription = ::SysAllocStringLen(description.get(),
                                         description.Length());
   return *pszDescription ? S_OK : E_OUTOFMEMORY;
 
   A11Y_TRYBLOCK_END
 }
 
@@ -357,22 +393,28 @@ AccessibleWrap::get_accRole(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  a11y::role geckoRole;
+  if (xpAccessible->IsProxy()) {
+    geckoRole = xpAccessible->Proxy()->Role();
+  } else {
 #ifdef DEBUG
-  NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible),
-               "Does not support Text when it should");
+    NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible),
+                 "Does not support Text when it should");
 #endif
 
-  a11y::role geckoRole = xpAccessible->Role();
+    geckoRole = xpAccessible->Role();
+  }
+
   uint32_t msaaRole = 0;
 
 #define ROLE(_geckoRole, stringRole, atkRole, macRole, \
              _msaaRole, ia2Role, nameRule) \
   case roles::_geckoRole: \
     msaaRole = _msaaRole; \
     break;
 
@@ -382,29 +424,39 @@ AccessibleWrap::get_accRole(
       MOZ_CRASH("Unknown role.");
   };
 
 #undef ROLE
 
   // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call the MSAA role
   // a ROLE_OUTLINEITEM for consistency and compatibility.
   // We need this because ARIA has a role of "row" for both grid and treegrid
-  if (geckoRole == roles::ROW) {
-    Accessible* xpParent = Parent();
-    if (xpParent && xpParent->Role() == roles::TREE_TABLE)
-      msaaRole = ROLE_SYSTEM_OUTLINEITEM;
+  if (xpAccessible->IsProxy()) {
+      if (geckoRole == roles::ROW
+          && xpAccessible->Proxy()->Parent()->Role() == roles::TREE_TABLE)
+        msaaRole = ROLE_SYSTEM_OUTLINEITEM;
+  } else {
+    if (geckoRole == roles::ROW) {
+      Accessible* xpParent = Parent();
+      if (xpParent && xpParent->Role() == roles::TREE_TABLE)
+        msaaRole = ROLE_SYSTEM_OUTLINEITEM;
+    }
   }
   
   // -- Try enumerated role
   if (msaaRole != USE_ROLE_STRING) {
     pvarRole->vt = VT_I4;
     pvarRole->lVal = msaaRole;  // Normal enumerated role
     return S_OK;
   }
 
+  // XXX bug 798492 make this work with proxies?
+  if (IsProxy())
+  return E_FAIL;
+
   // -- Try BSTR role
   // Could not map to known enumerated MSAA role like ROLE_BUTTON
   // Use BSTR role to expose role attribute or tag name + namespace
   nsIContent *content = xpAccessible->GetContent();
   if (!content)
     return E_FAIL;
 
   if (content->IsElement()) {
@@ -465,18 +517,24 @@ AccessibleWrap::get_accState(
   // MSAA only has 31 states and the lowest 31 bits of our state bit mask
   // are the same states as MSAA.
   // Note: we map the following Gecko states to different MSAA states:
   //   REQUIRED -> ALERT_LOW
   //   ALERT -> ALERT_MEDIUM
   //   INVALID -> ALERT_HIGH
   //   CHECKABLE -> MARQUEED
 
+  uint64_t state;
+  if (xpAccessible->IsProxy())
+    state = xpAccessible->Proxy()->State();
+  else
+    state = State();
+
   uint32_t msaaState = 0;
-  nsAccUtils::To32States(xpAccessible->State(), &msaaState, nullptr);
+  nsAccUtils::To32States(state, &msaaState, nullptr);
   pvarState->lVal = msaaState;
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
 
 STDMETHODIMP
@@ -529,16 +587,20 @@ AccessibleWrap::get_accKeyboardShortcut(
 
   Accessible* acc = GetXPAccessibleFor(varChild);
   if (!acc)
     return E_INVALIDARG;
 
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (acc->IsProxy())
+    return E_NOTIMPL;
+
   KeyBinding keyBinding = acc->AccessKey();
   if (keyBinding.IsEmpty())
     keyBinding = acc->KeyboardShortcut();
 
   nsAutoString shortcut;
   keyBinding.ToString(shortcut);
 
   *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(),
@@ -563,16 +625,20 @@ AccessibleWrap::get_accFocus(
   //              and does not contain a child that has the keyboard focus.
   // VT_I4:       lVal is CHILDID_SELF. The object itself has the keyboard focus.
   // VT_I4:       lVal contains the child ID of the child element with the keyboard focus.
   // VT_DISPATCH: pdispVal member is the address of the IDispatch interface
   //              for the child object with the keyboard focus.
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (IsProxy())
+    return E_NOTIMPL;
+
   // Return the current IAccessible child that has focus
   Accessible* focusedAccessible = FocusedChild();
   if (focusedAccessible == this) {
     pvarChild->vt = VT_I4;
     pvarChild->lVal = CHILDID_SELF;
   }
   else if (focusedAccessible) {
     pvarChild->vt = VT_DISPATCH;
@@ -725,16 +791,20 @@ AccessibleWrap::get_accSelection(VARIANT
     return E_INVALIDARG;
 
   VariantInit(pvarChildren);
   pvarChildren->vt = VT_EMPTY;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (IsProxy())
+    return E_NOTIMPL;
+
   if (IsSelect()) {
     nsAutoTArray<Accessible*, 10> selectedItems;
     SelectedItems(&selectedItems);
 
     // 1) Create and initialize the enumeration
     nsRefPtr<AccessibleEnumerator> pEnum = new AccessibleEnumerator(selectedItems);
     pvarChildren->vt = VT_UNKNOWN;    // this must be VT_UNKNOWN for an IEnumVARIANT
     NS_ADDREF(pvarChildren->punkVal = pEnum);
@@ -761,16 +831,20 @@ AccessibleWrap::get_accDefaultAction(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (xpAccessible->IsProxy())
+    return E_NOTIMPL;
+
   nsAutoString defaultAction;
   xpAccessible->ActionNameAt(0, defaultAction);
   *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(),
                                           defaultAction.Length());
   return *pszDefaultAction ? S_OK : E_OUTOFMEMORY;
 
   A11Y_TRYBLOCK_END
 }
@@ -788,16 +862,20 @@ AccessibleWrap::accSelect(
   // currently only handle focus and selection
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (xpAccessible->IsProxy())
+    return E_NOTIMPL;
+
   if (flagsSelect & (SELFLAG_TAKEFOCUS|SELFLAG_TAKESELECTION|SELFLAG_REMOVESELECTION))
   {
     if (flagsSelect & SELFLAG_TAKEFOCUS)
       xpAccessible->TakeFocus();
 
     if (flagsSelect & SELFLAG_TAKESELECTION)
       xpAccessible->TakeSelection();
 
@@ -841,16 +919,20 @@ AccessibleWrap::accLocation(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (xpAccessible->IsProxy())
+    return E_NOTIMPL;
+
   nsIntRect rect = xpAccessible->Bounds();
   *pxLeft = rect.x;
   *pyTop = rect.y;
   *pcxWidth = rect.width;
   *pcyHeight = rect.height;
   return S_OK;
 
   A11Y_TRYBLOCK_END
@@ -874,16 +956,20 @@ AccessibleWrap::accNavigate(
 
   Accessible* accessible = GetXPAccessibleFor(varStart);
   if (!accessible)
     return E_INVALIDARG;
 
   if (accessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (IsProxy())
+    return E_NOTIMPL;
+
   Accessible* navAccessible = nullptr;
   Maybe<RelationType> xpRelation;
 
 #define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \
   case msaaType: \
     xpRelation.emplace(RelationType::geckoType); \
     break;
 
@@ -945,16 +1031,20 @@ AccessibleWrap::accHitTest(
   if (!pvarChild)
     return E_INVALIDARG;
 
   VariantInit(pvarChild);
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (IsProxy())
+    return E_NOTIMPL;
+
   Accessible* accessible = ChildAtPoint(xLeft, yTop, eDirectChild);
 
   // if we got a child
   if (accessible) {
     // if the child is us
     if (accessible == this) {
       pvarChild->vt = VT_I4;
       pvarChild->lVal = CHILDID_SELF;
@@ -983,16 +1073,20 @@ AccessibleWrap::accDoDefaultAction(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (xpAccessible->IsProxy())
+    return E_NOTIMPL;
+
   return xpAccessible->DoAction(0) ? S_OK : E_INVALIDARG;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 AccessibleWrap::put_accName(
       /* [optional][in] */ VARIANT varChild,
@@ -1130,17 +1224,17 @@ AccessibleWrap::HandleAccEvent(AccEvent*
     cnt->Tag()->ToString(tag);
     nsIAtom* aid = cnt->GetID();
     if (aid)
       aid->ToUTF8String(id);
   }
 
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::ePlatforms)) {
-    printf("\n\nMSAA event: event: %d, target: %s@id='%s', childid: %d, hwnd: %d\n\n",
+    printf("\n\nMSAA event: event: %d, target: %s@id='%s', childid: %d, hwnd: %p\n\n",
            eventType, NS_ConvertUTF16toUTF8(tag).get(), id.get(),
            childID, hWnd);
   }
 #endif
 
   // Fire MSAA event for client area window.
   ::NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
 
@@ -1225,16 +1319,28 @@ AccessibleWrap::GetXPAccessibleFor(const
 {
   if (aVarChild.vt != VT_I4)
     return nullptr;
 
   // if its us real easy - this seems to always be the case
   if (aVarChild.lVal == CHILDID_SELF)
     return this;
 
+  if (IsProxy()) {
+    if (Proxy()->MustPruneChildren())
+      return nullptr;
+
+    if (aVarChild.lVal > 0)
+      return WrapperFor(Proxy()->ChildAt(aVarChild.lVal - 1));
+
+    // XXX Don't implement negative child ids for now because annoying, and
+    // doesn't seem to be speced.
+    return nullptr;
+  }
+
   if (nsAccUtils::MustPrune(this))
     return nullptr;
 
   // If lVal negative then it is treated as child ID and we should look for
   // accessible through whole accessible subtree including subdocuments.
   // Otherwise we treat lVal as index in parent.
 
   if (aVarChild.lVal < 0) {
--- a/accessible/windows/msaa/AccessibleWrap.h
+++ b/accessible/windows/msaa/AccessibleWrap.h
@@ -9,16 +9,17 @@
 
 #include "nsCOMPtr.h"
 #include "Accessible.h"
 #include "Accessible2.h"
 #include "ia2Accessible.h"
 #include "ia2AccessibleComponent.h"
 #include "ia2AccessibleHyperlink.h"
 #include "ia2AccessibleValue.h"
+#include "mozilla/a11y/ProxyAccessible.h"
 
 #ifdef __GNUC__
 // Inheriting from both XPCOM and MSCOM interfaces causes a lot of warnings
 // about virtual functions being hidden by each other. This is done by
 // design, so silence the warning.
 #pragma GCC diagnostic ignored "-Woverloaded-virtual"
 #endif
 
@@ -204,16 +205,21 @@ protected:
     NAVRELATION_DESCRIPTION_FOR = 0x100f,
     NAVRELATION_NODE_PARENT_OF = 0x1010,
     NAVRELATION_CONTAINING_DOCUMENT = 0x1011,
     NAVRELATION_CONTAINING_TAB_PANE = 0x1012,
     NAVRELATION_CONTAINING_APPLICATION = 0x1014
   };
 };
 
+static inline AccessibleWrap*
+WrapperFor(ProxyAccessible* aProxy)
+{
+  return reinterpret_cast<AccessibleWrap*>(aProxy->GetWrapper());
+}
 } // namespace a11y
 } // namespace mozilla
 
 #ifdef XP_WIN
 // Undo the windows.h damage
 #undef GetMessage
 #undef CreateEvent
 #undef GetClassName
--- a/accessible/windows/msaa/IUnknownImpl.h
+++ b/accessible/windows/msaa/IUnknownImpl.h
@@ -15,16 +15,22 @@
 // Avoid warning C4509 like "nonstandard extension used:
 // 'AccessibleWrap::[acc_getName]' uses SEH and 'name' has destructor.
 // At this point we're catching a crash which is of much greater
 // importance than the missing dereference for the nsCOMPtr<>
 #ifdef _MSC_VER
 #pragma warning( disable : 4509 )
 #endif
 
+#ifdef __GNUC__
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+#else
+#define ATTRIBUTE_UNUSED
+#endif
+
 namespace mozilla {
 namespace a11y {
 
 class AutoRefCnt
 {
 public:
   AutoRefCnt() : mValue(0) {}
 
@@ -73,17 +79,17 @@ virtual HRESULT STDMETHODCALLTYPE QueryI
 STDMETHODIMP                                                                   \
 Class::QueryInterface(REFIID aIID, void** aInstancePtr)                        \
 {                                                                              \
   A11Y_TRYBLOCK_BEGIN                                                          \
   if (!aInstancePtr)                                                           \
     return E_INVALIDARG;                                                       \
   *aInstancePtr = nullptr;                                                     \
                                                                                \
-  HRESULT hr = E_NOINTERFACE;
+  HRESULT hr ATTRIBUTE_UNUSED = E_NOINTERFACE;
 
 #define IMPL_IUNKNOWN_QUERY_TAIL                                               \
   return hr;                                                                   \
   A11Y_TRYBLOCK_END                                                            \
 }
 
 #define IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(Member)                            \
   return Member->QueryInterface(aIID, aInstancePtr);                           \
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Platform.h"
 
 #include "AccEvent.h"
 #include "Compatibility.h"
 #include "HyperTextAccessibleWrap.h"
 #include "nsWinUtils.h"
+#include "mozilla/a11y/ProxyAccessible.h"
 
 #include "mozilla/ClearOnShutdown.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 void
 a11y::PlatformInit()
@@ -29,22 +30,46 @@ a11y::PlatformInit()
 void
 a11y::PlatformShutdown()
 {
   ::DestroyCaret();
 
   nsWinUtils::ShutdownWindowEmulation();
 }
 
+class ProxyAccessibleWrap : public AccessibleWrap
+{
+  public:
+  ProxyAccessibleWrap(ProxyAccessible* aProxy) :
+    AccessibleWrap(nullptr, nullptr)
+  {
+    mType = eProxyType;
+    mBits.proxy = aProxy;
+  }
+
+  virtual void Shutdown() MOZ_OVERRIDE
+  {
+    mBits.proxy = nullptr;
+  }
+};
+
 void
-a11y::ProxyCreated(ProxyAccessible*)
+a11y::ProxyCreated(ProxyAccessible* aProxy, uint32_t)
 {
+  ProxyAccessibleWrap* wrapper = new ProxyAccessibleWrap(aProxy);
+  wrapper->AddRef();
+  aProxy->SetWrapper(reinterpret_cast<uintptr_t>(wrapper));
 }
 
 void
-a11y::ProxyDestroyed(ProxyAccessible*)
+a11y::ProxyDestroyed(ProxyAccessible* aProxy)
 {
+  ProxyAccessibleWrap* wrapper =
+    reinterpret_cast<ProxyAccessibleWrap*>(aProxy->GetWrapper());
+  wrapper->Shutdown();
+  aProxy->SetWrapper(0);
+  wrapper->Release();
 }
 
 void
 a11y::ProxyEvent(ProxyAccessible*, uint32_t)
 {
 }
--- a/accessible/xpcom/xpcAccessible.h
+++ b/accessible/xpcom/xpcAccessible.h
@@ -19,70 +19,78 @@ class Accessible;
 /**
  * XPCOM nsIAccessible interface implementation, used by xpcAccessibleGeneric
  * class.
  */
 class xpcAccessible : public nsIAccessible
 {
 public:
   // nsIAccessible
-  NS_IMETHOD GetParent(nsIAccessible** aParent) MOZ_FINAL;
-  NS_IMETHOD GetNextSibling(nsIAccessible** aNextSibling) MOZ_FINAL;
-  NS_IMETHOD GetPreviousSibling(nsIAccessible** aPreviousSibling) MOZ_FINAL;
-  NS_IMETHOD GetFirstChild(nsIAccessible** aFirstChild) MOZ_FINAL;
-  NS_IMETHOD GetLastChild(nsIAccessible** aLastChild) MOZ_FINAL;
-  NS_IMETHOD GetChildCount(int32_t* aChildCount) MOZ_FINAL;
-  NS_IMETHOD GetChildAt(int32_t aChildIndex, nsIAccessible** aChild) MOZ_FINAL;
-  NS_IMETHOD GetChildren(nsIArray** aChildren) MOZ_FINAL;
-  NS_IMETHOD GetIndexInParent(int32_t* aIndexInParent) MOZ_FINAL;
+  NS_IMETHOD GetParent(nsIAccessible** aParent) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetNextSibling(nsIAccessible** aNextSibling) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetPreviousSibling(nsIAccessible** aPreviousSibling)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetFirstChild(nsIAccessible** aFirstChild) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetLastChild(nsIAccessible** aLastChild) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetChildCount(int32_t* aChildCount) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetChildAt(int32_t aChildIndex, nsIAccessible** aChild)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetChildren(nsIArray** aChildren) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetIndexInParent(int32_t* aIndexInParent) MOZ_FINAL MOZ_OVERRIDE;
 
-  NS_IMETHOD GetDOMNode(nsIDOMNode** aDOMNode) MOZ_FINAL;
-  NS_IMETHOD GetDocument(nsIAccessibleDocument** aDocument) MOZ_FINAL;
-  NS_IMETHOD GetRootDocument(nsIAccessibleDocument** aRootDocument) MOZ_FINAL;
+  NS_IMETHOD GetDOMNode(nsIDOMNode** aDOMNode) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetDocument(nsIAccessibleDocument** aDocument) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetRootDocument(nsIAccessibleDocument** aRootDocument)
+    MOZ_FINAL MOZ_OVERRIDE;
 
-  NS_IMETHOD GetRole(uint32_t* aRole) MOZ_FINAL;
-  NS_IMETHOD GetState(uint32_t* aState, uint32_t* aExtraState) MOZ_FINAL;
+  NS_IMETHOD GetRole(uint32_t* aRole) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetState(uint32_t* aState, uint32_t* aExtraState)
+    MOZ_FINAL MOZ_OVERRIDE;
 
-  NS_IMETHOD GetDescription(nsAString& aDescription) MOZ_FINAL;
-  NS_IMETHOD GetName(nsAString& aName) MOZ_FINAL;
-  NS_IMETHOD GetLanguage(nsAString& aLanguage) MOZ_FINAL;
-  NS_IMETHOD GetValue(nsAString& aValue) MOZ_FINAL;
-  NS_IMETHOD GetHelp(nsAString& aHelp) MOZ_FINAL;
+  NS_IMETHOD GetDescription(nsAString& aDescription) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetName(nsAString& aName) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetLanguage(nsAString& aLanguage) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetValue(nsAString& aValue) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetHelp(nsAString& aHelp) MOZ_FINAL MOZ_OVERRIDE;
 
-  NS_IMETHOD GetAccessKey(nsAString& aAccessKey) MOZ_FINAL;
-  NS_IMETHOD GetKeyboardShortcut(nsAString& aKeyBinding) MOZ_FINAL;
+  NS_IMETHOD GetAccessKey(nsAString& aAccessKey) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetKeyboardShortcut(nsAString& aKeyBinding) MOZ_FINAL MOZ_OVERRIDE;
 
-  NS_IMETHOD GetAttributes(nsIPersistentProperties** aAttributes) MOZ_FINAL;
+  NS_IMETHOD GetAttributes(nsIPersistentProperties** aAttributes)
+    MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetBounds(int32_t* aX, int32_t* aY,
-                       int32_t* aWidth, int32_t* aHeight) MOZ_FINAL;
+                       int32_t* aWidth, int32_t* aHeight) MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GroupPosition(int32_t* aGroupLevel, int32_t* aSimilarItemsInGroup,
-                           int32_t* aPositionInGroup) MOZ_FINAL;
+                           int32_t* aPositionInGroup) MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetRelationByType(uint32_t aType,
-                               nsIAccessibleRelation** aRelation) MOZ_FINAL;
-  NS_IMETHOD GetRelations(nsIArray** aRelations) MOZ_FINAL;
+                               nsIAccessibleRelation** aRelation)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetRelations(nsIArray** aRelations) MOZ_FINAL MOZ_OVERRIDE;
 
-  NS_IMETHOD GetFocusedChild(nsIAccessible** aChild) MOZ_FINAL;
+  NS_IMETHOD GetFocusedChild(nsIAccessible** aChild) MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetChildAtPoint(int32_t aX, int32_t aY,
-                             nsIAccessible** aAccessible) MOZ_FINAL;
+                             nsIAccessible** aAccessible) MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetDeepestChildAtPoint(int32_t aX, int32_t aY,
-                                    nsIAccessible** aAccessible) MOZ_FINAL;
+                                    nsIAccessible** aAccessible)
+    MOZ_FINAL MOZ_OVERRIDE;
 
-  NS_IMETHOD SetSelected(bool aSelect) MOZ_FINAL;
-  NS_IMETHOD ExtendSelection() MOZ_FINAL;
-  NS_IMETHOD TakeSelection() MOZ_FINAL;
-  NS_IMETHOD TakeFocus() MOZ_FINAL;
+  NS_IMETHOD SetSelected(bool aSelect) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD ExtendSelection() MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD TakeSelection() MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD TakeFocus() MOZ_FINAL MOZ_OVERRIDE;
 
-  NS_IMETHOD GetActionCount(uint8_t* aActionCount) MOZ_FINAL;
-  NS_IMETHOD GetActionName(uint8_t aIndex, nsAString& aName) MOZ_FINAL;
-  NS_IMETHOD GetActionDescription(uint8_t aIndex, nsAString& aDescription) MOZ_FINAL;
-  NS_IMETHOD DoAction(uint8_t aIndex) MOZ_FINAL;
+  NS_IMETHOD GetActionCount(uint8_t* aActionCount) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetActionName(uint8_t aIndex, nsAString& aName) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetActionDescription(uint8_t aIndex, nsAString& aDescription)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD DoAction(uint8_t aIndex) MOZ_FINAL MOZ_OVERRIDE;
 
-  NS_IMETHOD ScrollTo(uint32_t aHow) MOZ_FINAL;
+  NS_IMETHOD ScrollTo(uint32_t aHow) MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD ScrollToPoint(uint32_t aCoordinateType,
-                           int32_t aX, int32_t aY) MOZ_FINAL;
+                           int32_t aX, int32_t aY) MOZ_FINAL MOZ_OVERRIDE;
 
 protected:
   xpcAccessible() { }
   virtual ~xpcAccessible() {}
 
 private:
   Accessible* Intl();
 
--- a/accessible/xpcom/xpcAccessibleApplication.h
+++ b/accessible/xpcom/xpcAccessibleApplication.h
@@ -22,20 +22,20 @@ class xpcAccessibleApplication : public 
 {
 public:
   explicit xpcAccessibleApplication(Accessible* aIntl) :
     xpcAccessibleGeneric(aIntl) { }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessibleApplication
-  NS_IMETHOD GetAppName(nsAString& aName) MOZ_FINAL;
-  NS_IMETHOD GetAppVersion(nsAString& aVersion) MOZ_FINAL;
-  NS_IMETHOD GetPlatformName(nsAString& aName) MOZ_FINAL;
-  NS_IMETHOD GetPlatformVersion(nsAString& aVersion) MOZ_FINAL;
+  NS_IMETHOD GetAppName(nsAString& aName) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetAppVersion(nsAString& aVersion) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetPlatformName(nsAString& aName) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetPlatformVersion(nsAString& aVersion) MOZ_FINAL MOZ_OVERRIDE;
 
 protected:
   virtual ~xpcAccessibleApplication() {}
 
 private:
   ApplicationAccessible* Intl() { return mIntl->AsApplication(); }
 
   xpcAccessibleApplication(const xpcAccessibleApplication&) = delete;
--- a/accessible/xpcom/xpcAccessibleDocument.h
+++ b/accessible/xpcom/xpcAccessibleDocument.h
@@ -27,27 +27,30 @@ public:
   explicit xpcAccessibleDocument(DocAccessible* aIntl) :
     xpcAccessibleHyperText(aIntl), mCache(kDefaultCacheLength) { }
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(xpcAccessibleDocument,
                                            xpcAccessibleGeneric)
 
   // nsIAccessibleDocument
-  NS_IMETHOD GetURL(nsAString& aURL) MOZ_FINAL;
-  NS_IMETHOD GetTitle(nsAString& aTitle) MOZ_FINAL;
-  NS_IMETHOD GetMimeType(nsAString& aType) MOZ_FINAL;
-  NS_IMETHOD GetDocType(nsAString& aType) MOZ_FINAL;
-  NS_IMETHOD GetDOMDocument(nsIDOMDocument** aDOMDocument) MOZ_FINAL;
-  NS_IMETHOD GetWindow(nsIDOMWindow** aDOMWindow) MOZ_FINAL;
-  NS_IMETHOD GetParentDocument(nsIAccessibleDocument** aDocument) MOZ_FINAL;
-  NS_IMETHOD GetChildDocumentCount(uint32_t* aCount) MOZ_FINAL;
+  NS_IMETHOD GetURL(nsAString& aURL) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetTitle(nsAString& aTitle) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetMimeType(nsAString& aType) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetDocType(nsAString& aType) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetDOMDocument(nsIDOMDocument** aDOMDocument) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetWindow(nsIDOMWindow** aDOMWindow) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetParentDocument(nsIAccessibleDocument** aDocument)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetChildDocumentCount(uint32_t* aCount) MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetChildDocumentAt(uint32_t aIndex,
-                                nsIAccessibleDocument** aDocument) MOZ_FINAL;
-  NS_IMETHOD GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor) MOZ_FINAL;
+                                nsIAccessibleDocument** aDocument)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor)
+    MOZ_FINAL MOZ_OVERRIDE;
 
   /**
    * Return XPCOM wrapper for the internal accessible.
    */
   xpcAccessibleGeneric* GetAccessible(Accessible* aAccessible);
 
   virtual void Shutdown() MOZ_OVERRIDE;
 
--- a/accessible/xpcom/xpcAccessibleGeneric.h
+++ b/accessible/xpcom/xpcAccessibleGeneric.h
@@ -36,17 +36,17 @@ public:
     if (mIntl->IsLink())
       mSupportedIfaces |= eHyperLink;
   }
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(xpcAccessibleGeneric, nsIAccessible)
 
   // nsIAccessible
-  virtual Accessible* ToInternalAccessible() const MOZ_FINAL;
+  virtual Accessible* ToInternalAccessible() const MOZ_FINAL MOZ_OVERRIDE;
 
   // xpcAccessibleGeneric
   virtual void Shutdown();
 
 protected:
   virtual ~xpcAccessibleGeneric() {}
 
   Accessible* mIntl;
--- a/accessible/xpcom/xpcAccessibleHyperLink.h
+++ b/accessible/xpcom/xpcAccessibleHyperLink.h
@@ -18,22 +18,23 @@ class Accessible;
 
 /**
  * XPCOM nsIAccessibleHyperLink implementation, used by xpcAccessibleGeneric
  * class.
  */
 class xpcAccessibleHyperLink : public nsIAccessibleHyperLink
 {
 public:
-  NS_IMETHOD GetAnchorCount(int32_t* aAnchorCount) MOZ_FINAL;
-  NS_IMETHOD GetStartIndex(int32_t* aStartIndex) MOZ_FINAL;
-  NS_IMETHOD GetEndIndex(int32_t* aEndIndex) MOZ_FINAL;
-  NS_IMETHOD GetURI(int32_t aIndex, nsIURI** aURI) MOZ_FINAL;
-  NS_IMETHOD GetAnchor(int32_t aIndex, nsIAccessible** aAccessible) MOZ_FINAL;
-  NS_IMETHOD GetValid(bool* aValid) MOZ_FINAL;
+  NS_IMETHOD GetAnchorCount(int32_t* aAnchorCount) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetStartIndex(int32_t* aStartIndex) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetEndIndex(int32_t* aEndIndex) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetURI(int32_t aIndex, nsIURI** aURI) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetAnchor(int32_t aIndex, nsIAccessible** aAccessible)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetValid(bool* aValid) MOZ_FINAL MOZ_OVERRIDE;
 
 protected:
   xpcAccessibleHyperLink() { }
   virtual ~xpcAccessibleHyperLink() {}
 
 private:
   xpcAccessibleHyperLink(const xpcAccessibleHyperLink&) = delete;
   xpcAccessibleHyperLink& operator =(const xpcAccessibleHyperLink&) = delete;
--- a/accessible/xpcom/xpcAccessibleImage.h
+++ b/accessible/xpcom/xpcAccessibleImage.h
@@ -19,18 +19,18 @@ class xpcAccessibleImage : public xpcAcc
 {
 public:
   explicit xpcAccessibleImage(Accessible* aIntl) :
     xpcAccessibleGeneric(aIntl) { }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD GetImagePosition(uint32_t aCoordType,
-                              int32_t* aX, int32_t* aY) MOZ_FINAL;
-  NS_IMETHOD GetImageSize(int32_t* aWidth, int32_t* aHeight) MOZ_FINAL;
+                              int32_t* aX, int32_t* aY) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetImageSize(int32_t* aWidth, int32_t* aHeight) MOZ_FINAL MOZ_OVERRIDE;
 
 protected:
   virtual ~xpcAccessibleImage() {}
 
 private:
   ImageAccessible* Intl() { return mIntl->AsImage(); }
 
   xpcAccessibleImage(const xpcAccessibleImage&) = delete;
--- a/accessible/xpcom/xpcAccessibleSelectable.h
+++ b/accessible/xpcom/xpcAccessibleSelectable.h
@@ -20,24 +20,27 @@ class Accessible;
 /**
  * XPCOM nsIAccessibleSelectable inteface implementation, used by
  * xpcAccessibleGeneric class.
  */
 class xpcAccessibleSelectable : public nsIAccessibleSelectable
 {
 public:
   // nsIAccessibleSelectable
-  NS_IMETHOD GetSelectedItems(nsIArray** aSelectedItems) MOZ_FINAL;
-  NS_IMETHOD GetSelectedItemCount(uint32_t* aSelectedItemCount) MOZ_FINAL;
-  NS_IMETHOD GetSelectedItemAt(uint32_t aIndex, nsIAccessible** aItem) MOZ_FINAL;
-  NS_IMETHOD IsItemSelected(uint32_t aIndex, bool* aIsSelected) MOZ_FINAL;
-  NS_IMETHOD AddItemToSelection(uint32_t aIndex) MOZ_FINAL;
-  NS_IMETHOD RemoveItemFromSelection(uint32_t aIndex) MOZ_FINAL;
-  NS_IMETHOD SelectAll(bool* aIsMultiSelect) MOZ_FINAL;
-  NS_IMETHOD UnselectAll() MOZ_FINAL;
+  NS_IMETHOD GetSelectedItems(nsIArray** aSelectedItems) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetSelectedItemCount(uint32_t* aSelectedItemCount)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetSelectedItemAt(uint32_t aIndex, nsIAccessible** aItem)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD IsItemSelected(uint32_t aIndex, bool* aIsSelected)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD AddItemToSelection(uint32_t aIndex) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD RemoveItemFromSelection(uint32_t aIndex) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD SelectAll(bool* aIsMultiSelect) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD UnselectAll() MOZ_FINAL MOZ_OVERRIDE;
 
 protected:
   xpcAccessibleSelectable() { }
   virtual ~xpcAccessibleSelectable() {}
 
 private:
   xpcAccessibleSelectable(const xpcAccessibleSelectable&) = delete;
   xpcAccessibleSelectable& operator =(const xpcAccessibleSelectable&) = delete;
--- a/accessible/xpcom/xpcAccessibleTable.h
+++ b/accessible/xpcom/xpcAccessibleTable.h
@@ -21,52 +21,65 @@ class xpcAccessibleTable : public xpcAcc
 {
 public:
   explicit xpcAccessibleTable(Accessible* aIntl) :
     xpcAccessibleGeneric(aIntl) { }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessibleTable
-  NS_IMETHOD GetCaption(nsIAccessible** aCaption) MOZ_FINAL;
-  NS_IMETHOD GetSummary(nsAString& aSummary) MOZ_FINAL;
-  NS_IMETHOD GetColumnCount(int32_t* aColumnCount) MOZ_FINAL;
-  NS_IMETHOD GetRowCount(int32_t* aRowCount) MOZ_FINAL;
+  NS_IMETHOD GetCaption(nsIAccessible** aCaption) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetSummary(nsAString& aSummary) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetColumnCount(int32_t* aColumnCount) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetRowCount(int32_t* aRowCount) MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetCellAt(int32_t aRowIndex, int32_t aColumnIndex,
-                       nsIAccessible** aCell) MOZ_FINAL;
+                       nsIAccessible** aCell) MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetCellIndexAt(int32_t aRowIndex, int32_t aColumnIndex,
-                            int32_t* aCellIndex) MOZ_FINAL;
-  NS_IMETHOD GetColumnIndexAt(int32_t aCellIndex, int32_t* aColumnIndex) MOZ_FINAL;
-  NS_IMETHOD GetRowIndexAt(int32_t aCellIndex, int32_t* aRowIndex) MOZ_FINAL;
+                            int32_t* aCellIndex) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetColumnIndexAt(int32_t aCellIndex, int32_t* aColumnIndex)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetRowIndexAt(int32_t aCellIndex, int32_t* aRowIndex)
+    MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetRowAndColumnIndicesAt(int32_t aCellIndex, int32_t* aRowIndex,
-                                      int32_t* aColumnIndex) MOZ_FINAL;
+                                      int32_t* aColumnIndex)
+    MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetColumnExtentAt(int32_t row, int32_t column,
-                               int32_t* aColumnExtent) MOZ_FINAL;
+                               int32_t* aColumnExtent) MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetRowExtentAt(int32_t row, int32_t column,
-                            int32_t* aRowExtent) MOZ_FINAL;
-  NS_IMETHOD GetColumnDescription(int32_t aColIdx, nsAString& aDescription) MOZ_FINAL;
-  NS_IMETHOD GetRowDescription(int32_t aRowIdx, nsAString& aDescription) MOZ_FINAL;
-  NS_IMETHOD IsColumnSelected(int32_t aColIdx, bool* _retval) MOZ_FINAL;
-  NS_IMETHOD IsRowSelected(int32_t aRowIdx, bool* _retval) MOZ_FINAL;
-  NS_IMETHOD IsCellSelected(int32_t aRowIdx, int32_t aColIdx, bool* _retval) MOZ_FINAL;
-  NS_IMETHOD GetSelectedCellCount(uint32_t* aSelectedCellCount) MOZ_FINAL;
-  NS_IMETHOD GetSelectedColumnCount(uint32_t* aSelectedColumnCount) MOZ_FINAL;
-  NS_IMETHOD GetSelectedRowCount(uint32_t* aSelectedRowCount) MOZ_FINAL;
-  NS_IMETHOD GetSelectedCells(nsIArray** aSelectedCell) MOZ_FINAL;
+                            int32_t* aRowExtent) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetColumnDescription(int32_t aColIdx, nsAString& aDescription)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetRowDescription(int32_t aRowIdx, nsAString& aDescription)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD IsColumnSelected(int32_t aColIdx, bool* _retval)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD IsRowSelected(int32_t aRowIdx, bool* _retval)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD IsCellSelected(int32_t aRowIdx, int32_t aColIdx, bool* _retval)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetSelectedCellCount(uint32_t* aSelectedCellCount)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetSelectedColumnCount(uint32_t* aSelectedColumnCount)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetSelectedRowCount(uint32_t* aSelectedRowCount)
+    MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetSelectedCells(nsIArray** aSelectedCell) MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetSelectedCellIndices(uint32_t* aCellsArraySize,
-                                    int32_t** aCellsArray) MOZ_FINAL;
+                                    int32_t** aCellsArray)
+    MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetSelectedColumnIndices(uint32_t* aColsArraySize,
-                                      int32_t** aColsArray) MOZ_FINAL;
+                                      int32_t** aColsArray)
+    MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetSelectedRowIndices(uint32_t* aRowsArraySize,
-                                   int32_t** aRowsArray) MOZ_FINAL;
-  NS_IMETHOD SelectColumn(int32_t aColIdx) MOZ_FINAL;
-  NS_IMETHOD SelectRow(int32_t aRowIdx) MOZ_FINAL;
-  NS_IMETHOD UnselectColumn(int32_t aColIdx) MOZ_FINAL;
-  NS_IMETHOD UnselectRow(int32_t aRowIdx) MOZ_FINAL;
-  NS_IMETHOD IsProbablyForLayout(bool* aIsForLayout) MOZ_FINAL;
+                                   int32_t** aRowsArray) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD SelectColumn(int32_t aColIdx) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD SelectRow(int32_t aRowIdx) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD UnselectColumn(int32_t aColIdx) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD UnselectRow(int32_t aRowIdx) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD IsProbablyForLayout(bool* aIsForLayout) MOZ_FINAL MOZ_OVERRIDE;
 
 protected:
   virtual ~xpcAccessibleTable() {}
 
 private:
   TableAccessible* Intl() { return mIntl->AsTable(); }
 
   xpcAccessibleTable(const xpcAccessibleTable&) = delete;
--- a/accessible/xpcom/xpcAccessibleTableCell.h
+++ b/accessible/xpcom/xpcAccessibleTableCell.h
@@ -22,24 +22,24 @@ class xpcAccessibleTableCell : public xp
 {
 public:
   explicit xpcAccessibleTableCell(Accessible* aIntl) :
     xpcAccessibleHyperText(aIntl) { }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessibleTableCell
-  NS_IMETHOD GetTable(nsIAccessibleTable** aTable) MOZ_FINAL;
-  NS_IMETHOD GetColumnIndex(int32_t* aColIdx) MOZ_FINAL;
-  NS_IMETHOD GetRowIndex(int32_t* aRowIdx) MOZ_FINAL;
-  NS_IMETHOD GetColumnExtent(int32_t* aExtent) MOZ_FINAL;
-  NS_IMETHOD GetRowExtent(int32_t* aExtent) MOZ_FINAL;
-  NS_IMETHOD GetColumnHeaderCells(nsIArray** aHeaderCells) MOZ_FINAL;
-  NS_IMETHOD GetRowHeaderCells(nsIArray** aHeaderCells) MOZ_FINAL;
-  NS_IMETHOD IsSelected(bool* aSelected) MOZ_FINAL;
+  NS_IMETHOD GetTable(nsIAccessibleTable** aTable) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetColumnIndex(int32_t* aColIdx) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetRowIndex(int32_t* aRowIdx) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetColumnExtent(int32_t* aExtent) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetRowExtent(int32_t* aExtent) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetColumnHeaderCells(nsIArray** aHeaderCells) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD GetRowHeaderCells(nsIArray** aHeaderCells) MOZ_FINAL MOZ_OVERRIDE;
+  NS_IMETHOD IsSelected(bool* aSelected) MOZ_FINAL MOZ_OVERRIDE;
 
 protected:
   virtual ~xpcAccessibleTableCell() {}
 
 private:
   TableCellAccessible* Intl() { return mIntl->AsTableCell(); }
 
   xpcAccessibleTableCell(const xpcAccessibleTableCell&) = delete;
--- a/accessible/xul/XULFormControlAccessible.h
+++ b/accessible/xul/XULFormControlAccessible.h
@@ -169,18 +169,18 @@ public:
  * Used for XUL toolbarbutton element.
  */
 class XULToolbarButtonAccessible : public XULButtonAccessible
 {
 public:
   XULToolbarButtonAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // Accessible
-  virtual void GetPositionAndSizeInternal(int32_t *aPosInSet,
-                                          int32_t *aSetSize);
+  virtual void GetPositionAndSizeInternal(int32_t* aPosInSet,
+                                          int32_t* aSetSize) MOZ_OVERRIDE;
 
   // nsXULToolbarButtonAccessible
   static bool IsSeparator(Accessible* aAccessible);
 };
 
 /**
  * Used for XUL toolbar element.
  */
--- a/accessible/xul/XULTreeAccessible.cpp
+++ b/accessible/xul/XULTreeAccessible.cpp
@@ -139,21 +139,18 @@ XULTreeAccessible::Value(nsString& aValu
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeAccessible: Accessible implementation
 
 void
 XULTreeAccessible::Shutdown()
 {
-  // XXX: we don't remove accessible from document cache if shutdown wasn't
-  // initiated by document destroying. Note, we can't remove accessible from
-  // document cache here while document is going to be shutdown. Note, this is
-  // not unique place where we have similar problem.
-  ClearCache(mAccessibleCache);
+  if (!mDoc->IsDefunct())
+    mAccessibleCache.Enumerate(UnbindCacheEntryFromDocument<Accessible>, nullptr);
 
   mTree = nullptr;
   mTreeView = nullptr;
 
   AccessibleWrap::Shutdown();
 }
 
 role
@@ -547,17 +544,18 @@ XULTreeAccessible::GetTreeItemAccessible
 
 void
 XULTreeAccessible::InvalidateCache(int32_t aRow, int32_t aCount)
 {
   if (IsDefunct())
     return;
 
   if (!mTreeView) {
-    ClearCache(mAccessibleCache);
+    mAccessibleCache.Enumerate(UnbindCacheEntryFromDocument<Accessible>,
+                               nullptr);
     return;
   }
 
   // Do not invalidate the cache if rows have been inserted.
   if (aCount > 0)
     return;
 
   DocAccessible* document = Document();
@@ -605,17 +603,18 @@ XULTreeAccessible::InvalidateCache(int32
 void
 XULTreeAccessible::TreeViewInvalidated(int32_t aStartRow, int32_t aEndRow,
                                        int32_t aStartCol, int32_t aEndCol)
 {
   if (IsDefunct())
     return;
 
   if (!mTreeView) {
-    ClearCache(mAccessibleCache);
+    mAccessibleCache.Enumerate(UnbindCacheEntryFromDocument<Accessible>,
+                               nullptr);
     return;
   }
 
   int32_t endRow = aEndRow;
 
   nsresult rv;
   if (endRow == -1) {
     int32_t rowCount = 0;
@@ -664,17 +663,19 @@ XULTreeAccessible::TreeViewChanged(nsITr
 
   // Fire reorder event on tree accessible on accessible tree (do not fire
   // show/hide events on tree items because it can be expensive to fire them for
   // each tree item.
   nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this);
   Document()->FireDelayedEvent(reorderEvent);
 
   // Clear cache.
-  ClearCache(mAccessibleCache);
+  mAccessibleCache.Enumerate(UnbindCacheEntryFromDocument<Accessible>,
+                             nullptr);
+
   mTreeView = aView;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeAccessible: protected implementation
 
 already_AddRefed<Accessible>
 XULTreeAccessible::CreateTreeItemAccessible(int32_t aRow) const
--- a/accessible/xul/XULTreeAccessible.h
+++ b/accessible/xul/XULTreeAccessible.h
@@ -257,17 +257,18 @@ class XULTreeColumAccessible : public XU
 {
 public:
   XULTreeColumAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
 protected:
 
   // Accessible
   virtual Accessible* GetSiblingAtOffset(int32_t aOffset,
-                                         nsresult *aError = nullptr) const;
+                                         nsresult* aError = nullptr) const
+    MOZ_OVERRIDE;
 };
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible downcasting method
 
 inline XULTreeAccessible*
 Accessible::AsXULTree()
--- a/accessible/xul/XULTreeGridAccessible.cpp
+++ b/accessible/xul/XULTreeGridAccessible.cpp
@@ -270,17 +270,21 @@ NS_IMPL_RELEASE_INHERITED(XULTreeGridRow
                           XULTreeItemAccessibleBase)
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridRowAccessible: Accessible implementation
 
 void
 XULTreeGridRowAccessible::Shutdown()
 {
-  ClearCache(mAccessibleCache);
+  if (!mDoc->IsDefunct()) {
+    mAccessibleCache.Enumerate(UnbindCacheEntryFromDocument<XULTreeGridCellAccessible>,
+                               nullptr);
+  }
+
   XULTreeItemAccessibleBase::Shutdown();
 }
 
 role
 XULTreeGridRowAccessible::NativeRole()
 {
   return roles::ROW;
 }
--- a/addon-sdk/moz.build
+++ b/addon-sdk/moz.build
@@ -4,29 +4,26 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-HAS_MISC_RULE = True
-
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 JETPACK_PACKAGE_MANIFESTS += ['source/test/jetpack-package.ini']
 JETPACK_ADDON_MANIFESTS += ['source/test/addons/jetpack-addon.ini']
 
 EXTRA_JS_MODULES.sdk += [
     'source/app-extension/bootstrap.js',
 ]
 
 EXTRA_JS_MODULES.sdk.system += [
     'source/modules/system/Startup.js',
-    'source/modules/system/XulApp.js',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != "gonk":
     EXTRA_JS_MODULES.commonjs.method.test += [
         'source/lib/method/test/browser.js',
         'source/lib/method/test/common.js',
     ]
 
@@ -34,32 +31,32 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != "gonk
         'source/lib/sdk/deprecated/api-utils.js',
         'source/lib/sdk/deprecated/cortex.js',
         'source/lib/sdk/deprecated/errors.js',
         'source/lib/sdk/deprecated/events.js',
         'source/lib/sdk/deprecated/light-traits.js',
         'source/lib/sdk/deprecated/list.js',
         'source/lib/sdk/deprecated/memory.js',
         'source/lib/sdk/deprecated/symbiont.js',
+        'source/lib/sdk/deprecated/sync-worker.js',
         'source/lib/sdk/deprecated/traits-worker.js',
         'source/lib/sdk/deprecated/traits.js',
         'source/lib/sdk/deprecated/unit-test-finder.js',
         'source/lib/sdk/deprecated/unit-test.js',
         'source/lib/sdk/deprecated/window-utils.js',
     ]
 
     EXTRA_JS_MODULES.commonjs.sdk.frame += [
         'source/lib/sdk/frame/hidden-frame.js',
         'source/lib/sdk/frame/utils.js',
     ]
 
     EXTRA_JS_MODULES.commonjs.sdk.panel += [
         'source/lib/sdk/panel/events.js',
         'source/lib/sdk/panel/utils.js',
-        'source/lib/sdk/panel/window.js',
     ]
 
     EXTRA_JS_MODULES.commonjs.sdk.places += [
         'source/lib/sdk/places/bookmarks.js',
         'source/lib/sdk/places/contract.js',
         'source/lib/sdk/places/events.js',
         'source/lib/sdk/places/favicon.js',
         'source/lib/sdk/places/history.js',
@@ -94,16 +91,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != "gonk
         'source/lib/sdk/test/memory.js',
         'source/lib/sdk/test/options.js',
         'source/lib/sdk/test/runner.js',
         'source/lib/sdk/test/tmp-file.js',
         'source/lib/sdk/test/utils.js',
     ]
 
     EXTRA_JS_MODULES.commonjs.sdk.ui += [
+        'source/lib/sdk/ui/component.js',
         'source/lib/sdk/ui/frame.js',
         'source/lib/sdk/ui/id.js',
         'source/lib/sdk/ui/sidebar.js',
         'source/lib/sdk/ui/state.js',
         'source/lib/sdk/ui/toolbar.js',
     ]
 
     EXTRA_JS_MODULES.commonjs.sdk.ui.button += [
@@ -128,23 +126,23 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != "gonk
         'source/lib/sdk/window/namespace.js',
         'source/lib/sdk/window/utils.js',
     ]
 
     EXTRA_JS_MODULES.commonjs.sdk.windows += [
         'source/lib/sdk/windows/dom.js',
         'source/lib/sdk/windows/fennec.js',
         'source/lib/sdk/windows/firefox.js',
-        'source/lib/sdk/windows/loader.js',
         'source/lib/sdk/windows/observer.js',
         'source/lib/sdk/windows/tabs-fennec.js',
         'source/lib/sdk/windows/tabs-firefox.js',
     ]
 
 EXTRA_JS_MODULES.commonjs += [
+    'source/lib/index.js',
     'source/lib/test.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.dev += [
     'source/lib/dev/debuggee.js',
     'source/lib/dev/frame-script.js',
     'source/lib/dev/panel.js',
     'source/lib/dev/ports.js',
@@ -168,34 +166,38 @@ EXTRA_JS_MODULES.commonjs.diffpatcher.te
     'source/lib/diffpatcher/test/common.js',
     'source/lib/diffpatcher/test/diff.js',
     'source/lib/diffpatcher/test/index.js',
     'source/lib/diffpatcher/test/patch.js',
     'source/lib/diffpatcher/test/tap.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.framescript += [
+    'source/lib/framescript/context-menu.js',
     'source/lib/framescript/contextmenu-events.js',
     'source/lib/framescript/FrameScriptManager.jsm',
     'source/lib/framescript/LoaderHelper.jsm',
+    'source/lib/framescript/manager.js',
     'source/lib/framescript/tab-events.js',
+    'source/lib/framescript/util.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.method += [
     'source/lib/method/core.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.node += [
     'source/lib/node/os.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk += [
     'source/lib/sdk/base64.js',
     'source/lib/sdk/clipboard.js',
     'source/lib/sdk/context-menu.js',
+    'source/lib/sdk/context-menu@2.js',
     'source/lib/sdk/hotkeys.js',
     'source/lib/sdk/indexed-db.js',
     'source/lib/sdk/l10n.js',
     'source/lib/sdk/messaging.js',
     'source/lib/sdk/notifications.js',
     'source/lib/sdk/page-mod.js',
     'source/lib/sdk/page-worker.js',
     'source/lib/sdk/panel.js',
@@ -213,16 +215,17 @@ EXTRA_JS_MODULES.commonjs.sdk += [
     'source/lib/sdk/timers.js',
     'source/lib/sdk/ui.js',
     'source/lib/sdk/url.js',
     'source/lib/sdk/widget.js',
     'source/lib/sdk/windows.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.addon += [
+    'source/lib/sdk/addon/bootstrap.js',
     'source/lib/sdk/addon/events.js',
     'source/lib/sdk/addon/host.js',
     'source/lib/sdk/addon/installer.js',
     'source/lib/sdk/addon/manager.js',
     'source/lib/sdk/addon/runner.js',
     'source/lib/sdk/addon/window.js',
 ]
 
@@ -241,20 +244,25 @@ EXTRA_JS_MODULES.commonjs.sdk.content +=
     'source/lib/sdk/content/context-menu.js',
     'source/lib/sdk/content/events.js',
     'source/lib/sdk/content/loader.js',
     'source/lib/sdk/content/mod.js',
     'source/lib/sdk/content/sandbox.js',
     'source/lib/sdk/content/thumbnail.js',
     'source/lib/sdk/content/utils.js',
     'source/lib/sdk/content/worker-child.js',
-    'source/lib/sdk/content/worker-parent.js',
     'source/lib/sdk/content/worker.js',
 ]
 
+EXTRA_JS_MODULES.commonjs.sdk['context-menu'] += [
+    'source/lib/sdk/context-menu/context.js',
+    'source/lib/sdk/context-menu/core.js',
+    'source/lib/sdk/context-menu/readers.js',
+]
+
 EXTRA_JS_MODULES.commonjs.sdk.core += [
     'source/lib/sdk/core/disposable.js',
     'source/lib/sdk/core/heritage.js',
     'source/lib/sdk/core/namespace.js',
     'source/lib/sdk/core/observer.js',
     'source/lib/sdk/core/promise.js',
     'source/lib/sdk/core/reference.js',
 ]
@@ -390,16 +398,17 @@ EXTRA_JS_MODULES.commonjs.sdk.system += 
     'source/lib/sdk/system/child_process.js',
     'source/lib/sdk/system/environment.js',
     'source/lib/sdk/system/events.js',
     'source/lib/sdk/system/globals.js',
     'source/lib/sdk/system/process.js',
     'source/lib/sdk/system/runtime.js',
     'source/lib/sdk/system/unload.js',
     'source/lib/sdk/system/xul-app.js',
+    'source/lib/sdk/system/xul-app.jsm',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.system.child_process += [
     'source/lib/sdk/system/child_process/subprocess.js',
     'source/lib/sdk/system/child_process/subprocess_worker_unix.js',
     'source/lib/sdk/system/child_process/subprocess_worker_win.js',
 ]
 
@@ -421,30 +430,34 @@ EXTRA_JS_MODULES.commonjs.sdk.ui.state +
     'source/lib/sdk/ui/state/events.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.ui.toolbar += [
     'source/lib/sdk/ui/toolbar/model.js',
     'source/lib/sdk/ui/toolbar/view.js',
 ]
 
+EXTRA_JS_MODULES.commonjs.sdk.uri += [
+    'source/lib/sdk/uri/resource.js',
+]
+
 EXTRA_JS_MODULES.commonjs.sdk.url += [
     'source/lib/sdk/url/utils.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.util += [
     'source/lib/sdk/util/array.js',
+    'source/lib/sdk/util/bond.js',
     'source/lib/sdk/util/collection.js',
     'source/lib/sdk/util/contract.js',
     'source/lib/sdk/util/deprecate.js',
     'source/lib/sdk/util/dispatcher.js',
     'source/lib/sdk/util/list.js',
     'source/lib/sdk/util/match-pattern.js',
     'source/lib/sdk/util/object.js',
-    'source/lib/sdk/util/registry.js',
     'source/lib/sdk/util/rules.js',
     'source/lib/sdk/util/sequence.js',
     'source/lib/sdk/util/uuid.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.view += [
     'source/lib/sdk/view/core.js',
 ]
--- a/addon-sdk/mozbuild.template
+++ b/addon-sdk/mozbuild.template
@@ -9,10 +9,9 @@ JETPACK_PACKAGE_MANIFESTS += ['source/te
 JETPACK_ADDON_MANIFESTS += ['source/test/addons/jetpack-addon.ini']
 
 EXTRA_JS_MODULES.sdk += [
     'source/app-extension/bootstrap.js',
 ]
 
 EXTRA_JS_MODULES.sdk.system += [
     'source/modules/system/Startup.js',
-    'source/modules/system/XulApp.js',
 ]
--- a/addon-sdk/source/.gitignore
+++ b/addon-sdk/source/.gitignore
@@ -3,18 +3,18 @@ python-lib/cuddlefish/app-extension/comp
 testdocs.tgz
 jetpack-sdk-docs.tgz
 .test_tmp/
 doc/dev-guide/
 doc/index.html
 doc/modules/
 doc/status.md5
 packages/*
+node_modules
 
 # Python
 *.pyc
 
 # OSX
 *.DS_Store
 
 # Windows
 *Thumbs.db
-
--- a/addon-sdk/source/.hgignore
+++ b/addon-sdk/source/.hgignore
@@ -1,14 +1,15 @@
 syntax: glob
 local.json
 python-lib/cuddlefish/app-extension/components/jetpack.xpt
 testdocs.tgz
 jetpack-sdk-docs.tgz
 .test_tmp
 jetpack-sdk-docs
+node_modules
 
 # These should really be in a global .hgignore, but such a thing
 # seems ridiculously confusing to set up, so we'll include some
 # common intermediate files here.
 *.pyc
 *~
 *.DS_Store
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/.jpmignore
@@ -0,0 +1,17 @@
+local.json
+mapping.json
+CONTRIBUTING.md
+@addon-sdk.xpi
+.*
+app-extension/
+bin/
+modules/
+node_modules/
+examples/
+
+# Python
+python-lib/
+*.pyc
+
+# Windows
+*Thumbs.db
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/.travis.yml
@@ -0,0 +1,23 @@
+sudo: false
+language: node_js
+node_js:
+  - "0.10"
+
+notifications:
+  irc: "irc.mozilla.org#jetpack"
+
+before_install:
+  - "export DISPLAY=:99.0"
+  - "sh -e /etc/init.d/xvfb start"
+  - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 -extension RANDR"
+
+before_script:
+  - npm install mozilla-download -g
+  - npm install jpm -g
+  - cd ..
+  - mozilla-download --branch nightly -c prerelease --host ftp.mozilla.org firefox
+  - export JPM_FIREFOX_BINARY=$TRAVIS_BUILD_DIR/../firefox/firefox
+  - cd $TRAVIS_BUILD_DIR
+
+script:
+  - npm test
deleted file mode 100644
--- a/addon-sdk/source/README
+++ /dev/null
@@ -1,41 +0,0 @@
-Add-on SDK README
-==================
-
-Before proceeding, please make sure you've installed Python 2.5,
-2.6, or 2.7 (if it's not already on your system):
-
-  http://python.org/download/
-
-Note that Python 3 is not supported.
-
-For Windows users, MozillaBuild (https://wiki.mozilla.org/MozillaBuild)
-will install the correct version of Python and the MSYS package, which
-will make it easier to work with the SDK.
-
-To get started, first enter the same directory that this README file
-is in (the SDK's root directory) using a shell program. On Unix systems
-or on Windows with MSYS, you can execute the following command:
-
-  source bin/activate
-
-Windows users using cmd.exe should instead run:
-
-  bin\activate.bat
-
-Then go to https://developer.mozilla.org/en-US/Add-ons/SDK/
-to browse the SDK documentation.
-
-If you get an error when running cfx or have any other problems getting
-started, see the "Troubleshooting" guide at:
-https://developer.mozilla.org/en-US/Add-ons/SDK/Tutorials/Troubleshooting
-
-Bugs
--------
-
-* file a bug: https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK
-
-
-Style Guidelines
---------------------
-
-* https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/README.md
@@ -0,0 +1,31 @@
+# Mozilla Add-on SDK [![Build Status](https://travis-ci.org/mozilla/addon-sdk.png)](https://travis-ci.org/mozilla/addon-sdk)
+
+Using the Add-on SDK you can create Firefox add-ons using standard Web technologies: JavaScript, HTML, and CSS. The SDK includes JavaScript APIs which you can use to create add-ons, and tools for creating, running, testing, and packaging add-ons.
+
+If you find a problem, please [report the bug here](https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK).
+
+## Developing Add-ons
+
+These resources should provide some help:
+
+* [Add-on SDK Documentation](https://developer.mozilla.org/en-US/Add-ons/SDK)
+* [Community Developed Modules](https://github.com/mozilla/addon-sdk/wiki/Community-developed-modules)
+* [Jetpack FAQ](https://wiki.mozilla.org/Jetpack/FAQ)
+* [StackOverflow Questions](http://stackoverflow.com/questions/tagged/firefox-addon-sdk)
+* [Mailing List](https://wiki.mozilla.org/Jetpack#Mailing_list)
+* #jetpack on irc.mozilla.org
+
+## Contributing Code
+
+Please read these two guides if you wish to contribute some patches to the addon-sdk:
+
+* [Contribute Guide](https://github.com/mozilla/addon-sdk/wiki/Contribute)
+* [Style Guide](https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide)
+
+## Issues
+
+We use [bugzilla](https://bugzilla.mozilla.org/) as our issue tracker, here are some useful links:
+
+* [File a bug](https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK)
+* [Open bugs](https://bugzilla.mozilla.org/buglist.cgi?bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&columnlist=bug_severity%2Cpriority%2Cassigned_to%2Cbug_status%2Ctarget_milestone%2Cresolution%2Cshort_desc%2Cchangeddate&product=Add-on%20SDK&query_format=advanced&order=priority)
+* [Good first bugs](https://bugzilla.mozilla.org/buglist.cgi?status_whiteboard=[good+first+bug]&&resolution=---&product=Add-on+SDK)
--- a/addon-sdk/source/app-extension/bootstrap.js
+++ b/addon-sdk/source/app-extension/bootstrap.js
@@ -45,17 +45,25 @@ function setResourceSubstitution(domain,
   resourceHandler.setSubstitution(domain, uri);
 }
 
 // Utility function that synchronously reads local resource from the given
 // `uri` and returns content string.
 function readURI(uri) {
   let ioservice = Cc['@mozilla.org/network/io-service;1'].
     getService(Ci.nsIIOService);
-  let channel = ioservice.newChannel(uri, 'UTF-8', null);
+
+  let channel = ioservice.newChannel2(uri,
+                                      'UTF-8',
+                                      null,
+                                      null,      // aLoadingNode
+                                      systemPrincipal,
+                                      null,      // aTriggeringPrincipal
+                                      Ci.nsILoadInfo.SEC_NORMAL,
+                                      Ci.nsIContentPolicy.TYPE_OTHER);
   let stream = channel.open();
 
   let cstream = Cc['@mozilla.org/intl/converter-input-stream;1'].
     createInstance(Ci.nsIConverterInputStream);
   cstream.init(stream, 'UTF-8', 0, 0);
 
   let str = {};
   let data = '';
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/bin/jpm-test.js
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+var BLACKLIST = [];
+var readParam = require("./node-scripts/utils").readParam;
+var path = require("path");
+var Mocha = require("mocha");
+var mocha = new Mocha({
+  ui: "bdd",
+  reporter: "spec",
+  timeout: 900000
+});
+
+var type = readParam("type");
+
+[
+  (!type || type == "modules") && require.resolve("../bin/node-scripts/test.modules"),
+  (!type || type == "addons") && require.resolve("../bin/node-scripts/test.addons"),
+  (!type || type == "examples") && require.resolve("../bin/node-scripts/test.examples"),
+].sort().forEach(function(filepath) {
+  filepath && mocha.addFile(filepath);
+})
+
+mocha.run(function (failures) {
+  process.exit(failures);
+});
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/bin/node-scripts/test.addons.js
@@ -0,0 +1,48 @@
+/* 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";
+
+var utils = require("./utils");
+var path = require("path");
+var fs = require("fs");
+var jpm = utils.run;
+var readParam = utils.readParam;
+
+var addonsPath = path.join(__dirname, "..", "..", "test", "addons");
+
+var binary = process.env.JPM_FIREFOX_BINARY || "nightly";
+var filterPattern = readParam("filter");
+
+describe("jpm test sdk addons", function () {
+  fs.readdirSync(addonsPath)
+  .filter(fileFilter.bind(null, addonsPath))
+  .forEach(function (file) {
+    it(file, function (done) {
+      var addonPath = path.join(addonsPath, file);
+      process.chdir(addonPath);
+
+      var options = { cwd: addonPath, env: { JPM_FIREFOX_BINARY: binary }};
+      if (process.env.DISPLAY) {
+        options.env.DISPLAY = process.env.DISPLAY;
+      }
+      if (/^e10s/.test(file)) {
+        options.e10s = true;
+      }
+
+      jpm("run", options).then(done).catch(done);
+    });
+  });
+});
+
+function fileFilter(root, file) {
+  var matcher = filterPattern && new RegExp(filterPattern);
+  if (/^(l10n|simple-prefs|page-mod-debugger)/.test(file)) {
+    return false;
+  }
+  if (matcher && !matcher.test(file)) {
+    return false;
+  }
+  var stat = fs.statSync(path.join(root, file))
+  return (stat && stat.isDirectory());
+}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/bin/node-scripts/test.examples.js
@@ -0,0 +1,45 @@
+/* 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";
+
+var utils = require("./utils");
+var path = require("path");
+var fs = require("fs");
+var jpm = utils.run;
+var readParam = utils.readParam;
+
+var examplesPath = path.join(__dirname, "..", "..", "examples");
+
+var binary = process.env.JPM_FIREFOX_BINARY || "nightly";
+var filterPattern = readParam("filter");
+
+describe("jpm test sdk examples", function () {
+  fs.readdirSync(examplesPath)
+  .filter(fileFilter.bind(null, examplesPath))
+  .forEach(function (file) {
+    it(file, function (done) {
+      var addonPath = path.join(examplesPath, file);
+      process.chdir(addonPath);
+
+      var options = { cwd: addonPath, env: { JPM_FIREFOX_BINARY: binary }};
+      if (process.env.DISPLAY) {
+        options.env.DISPLAY = process.env.DISPLAY;
+      }
+
+      jpm("test", options).then(done);
+    });
+  });
+});
+
+function fileFilter(root, file) {
+  var matcher = filterPattern && new RegExp(filterPattern);
+  if (/^(reading-data)/.test(file)) {
+    return false;
+  }
+  if (matcher && !matcher.test(file)) {
+    return false;
+  }
+  var stat = fs.statSync(path.join(root, file))
+  return (stat && stat.isDirectory());
+}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/bin/node-scripts/test.modules.js
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+var utils = require("./utils");
+var readParam = utils.readParam;
+var path = require("path");
+var fs = require("fs");
+var jpm = utils.run;
+var sdk = path.join(__dirname, "..", "..");
+var binary = process.env.JPM_FIREFOX_BINARY || "nightly";
+
+var filterPattern = readParam("filter");
+
+describe("jpm test sdk modules", function () {
+  it("SDK Modules", function (done) {
+    process.chdir(sdk);
+
+    var options = { cwd: sdk, env: { JPM_FIREFOX_BINARY: binary } };
+    if (process.env.DISPLAY) {
+      options.env.DISPLAY = process.env.DISPLAY;
+    }
+    options.filter = filterPattern;
+
+    jpm("test", options, process).then(done);
+  });
+});
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/bin/node-scripts/utils.js
@@ -0,0 +1,70 @@
+/* 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";
+
+var _ = require("lodash");
+var path = require("path");
+var child_process = require("child_process");
+var jpm = require.resolve("../../node_modules/jpm/bin/jpm");
+var Promise = require("promise");
+var chai = require("chai");
+var expect = chai.expect;
+var assert = chai.assert;
+var DEFAULT_PROCESS = process;
+
+var sdk = path.join(__dirname, "..", "..");
+var prefsPath = path.join(sdk, "test", "preferences", "test-preferences.js");
+var e10sPrefsPath = path.join(sdk, "test", "preferences", "test-e10s-preferences.js");
+
+function spawn (cmd, options) {
+  options = options || {};
+  var env = _.extend({}, options.env, process.env);
+  var e10s = options.e10s || false;
+
+  return child_process.spawn("node", [
+    jpm, cmd, "-v",
+    "--prefs", e10s ? e10sPrefsPath : prefsPath,
+    "-o", sdk,
+    "-f", options.filter || ""
+  ], {
+    cwd: options.cwd || tmpOutputDir,
+    env: env
+  });
+}
+exports.spawn = spawn;
+
+function run (cmd, options, p) {
+  return new Promise(function(resolve) {
+    var output = [];
+    var proc = spawn(cmd, options);
+    proc.stderr.pipe(process.stderr);
+    proc.stdout.on("data", function (data) {
+      output.push(data);
+    });
+    if (p) {
+      proc.stdout.pipe(p.stdout);
+    }
+    proc.on("close", function(code) {
+      var out = output.join("");
+      var noTests = /No tests were run/.test(out);
+      var hasSuccess = /All tests passed!/.test(out);
+      var hasFailure = /There were test failures\.\.\./.test(out);
+      if (noTests || hasFailure || !hasSuccess || code != 0) {
+        DEFAULT_PROCESS.stdout.write(out);
+      }
+      expect(code).to.equal(hasFailure ? 1 : 0);
+      expect(hasFailure).to.equal(false);
+      expect(hasSuccess).to.equal(true);
+      expect(noTests).to.equal(false);
+      resolve();
+    });
+  });
+}
+exports.run = run;
+
+function readParam(name) {
+  var index = process.argv.indexOf("--" + name)
+  return index >= 0 && process.argv[index + 1]
+}
+exports.readParam = readParam;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/bootstrap.js
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+// Note that this file is temporary workaroud until JPM is smart enough
+// to cover it on it's own.
+
+const { utils: Cu } = Components;
+const rootURI = __SCRIPT_URI_SPEC__.replace("bootstrap.js", "");
+const { require } = Cu.import(`${rootURI}/lib/toolkit/require.js`, {});
+const { Bootstrap } = require(`${rootURI}/lib/sdk/addon/bootstrap.js`);
+const { startup, shutdown, install, uninstall } = new Bootstrap(rootURI);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/examples/actor-repl/test/test-main.js
@@ -0,0 +1,10 @@
+/* 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";
+
+exports.testMain = function(assert) {
+  assert.pass("TODO: Write some tests.");
+};
+
+require("sdk/test").run(exports);
--- a/addon-sdk/source/examples/annotator/package.json
+++ b/addon-sdk/source/examples/annotator/package.json
@@ -1,9 +1,11 @@
 {
-    "license": "MPL 2.0",
-    "name": "annotator",
-    "contributors": [],
-    "author": "Will Bamberg",
-    "keywords": [],
-    "id": "anonid0-annotator",
-    "description": "Add notes to Web pages"
+  "license": "MPL 2.0",
+  "name": "annotator",
+  "contributors": [],
+  "author": "Will Bamberg",
+  "keywords": [],
+  "version": "0.1.1",
+  "id": "anonid0-annotator@jetpack",
+  "description": "Add notes to Web pages",
+  "main": "./lib/main.js"
 }
--- a/addon-sdk/source/examples/annotator/tests/test-main.js
+++ b/addon-sdk/source/examples/annotator/tests/test-main.js
@@ -1,7 +1,10 @@
 /* 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";
 
-exports.testMain = function(test) {
-  test.pass("TODO: Write some tests.");
+exports.testMain = function(assert) {
+  assert.pass("TODO: Write some tests.");
 };
+
+require("sdk/test").run(exports);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/examples/debug-client/test/test-main.js
@@ -0,0 +1,10 @@
+/* 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";
+
+exports.testMain = function(assert) {
+  assert.pass("TODO: Write some tests.");
+};
+
+require("sdk/test").run(exports);
--- a/addon-sdk/source/examples/library-detector/package.json
+++ b/addon-sdk/source/examples/library-detector/package.json
@@ -1,9 +1,10 @@
 {
-    "name": "library-detector-sdk",
-    "license": "MPL 2.0",
-    "author": "",
-    "version": "0.1",
-    "title": "library-detector-sdk",
-    "id": "jid1-R4rSVNkBANnvGQ",
-    "description": "a basic add-on"
+  "name": "library-detector-sdk",
+  "license": "MPL 2.0",
+  "author": "",
+  "version": "0.1.1",
+  "title": "library-detector-sdk",
+  "id": "jid1-R4rSVNkBANnvGQ@jetpack",
+  "description": "a basic add-on",
+  "main": "./lib/main.js"
 }
--- a/addon-sdk/source/examples/library-detector/test/test-main.js
+++ b/addon-sdk/source/examples/library-detector/test/test-main.js
@@ -1,7 +1,10 @@
 /* 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";
 
-exports.testMain = function(test) {
-  test.pass("TODO: Write some tests.");
+exports.testMain = function(assert) {
+  assert.pass("TODO: Write some tests.");
 };
+
+require("sdk/test").run(exports);
--- a/addon-sdk/source/examples/toolbar-api/package.json
+++ b/addon-sdk/source/examples/toolbar-api/package.json
@@ -1,12 +1,12 @@
 {
   "name": "toolbar-api",
   "title": "Toolbar API",
   "main": "./lib/main.js",
   "description": "a toolbar api example",
   "author": "",
   "license": "MPL 2.0",
-  "version": "0.1",
+  "version": "0.1.1",
   "engines": {
     "firefox": ">=27.0 <=30.0"
   }
 }
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/examples/toolbar-api/test/test-main.js
@@ -0,0 +1,10 @@
+/* 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";
+
+exports.testMain = function(assert) {
+  assert.pass("TODO: Write some tests.");
+};
+
+require("sdk/test").run(exports);
--- a/addon-sdk/source/examples/ui-button-apis/package.json
+++ b/addon-sdk/source/examples/ui-button-apis/package.json
@@ -1,9 +1,10 @@
 {
   "name": "ui-button-apis",
   "title": "Australis Button API Examples",
   "id": "ui-button-apis@mozilla.org",
   "description": "A Button API example",
   "author": "jeff@canuckistani.ca (Jeff Griffiths | @canuckistani)",
   "license": "MPL 2.0",
-  "version": "0.1"
+  "version": "0.1.1",
+  "main": "./lib/main.js"
 }
--- a/addon-sdk/source/examples/ui-button-apis/tests/test-main.js
+++ b/addon-sdk/source/examples/ui-button-apis/tests/test-main.js
@@ -1,14 +1,22 @@
 /* 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";
 
-var { actionButton, toggleButton, icon } = require("main");
+try {
+  // CFX use case..
+  var { actionButton, toggleButton, icon } = require("main");
+}
+catch (e) {
+  // JPM use case..
+  let mainURI = "../lib/main";
+  var { actionButton, toggleButton, icon } = require(mainURI);
+}
 var self = require("sdk/self");
 
 exports.testActionButton = function(assert) {
   assert.equal(actionButton.id, "test-action-button", "action button id is correct");
   assert.equal(actionButton.label, "Action Button", "action button label is correct");
   assert.equal(actionButton.icon, icon, "action button icon is correct");
 }
 
--- a/addon-sdk/source/lib/dev/utils.js
+++ b/addon-sdk/source/lib/dev/utils.js
@@ -11,28 +11,30 @@ const { devtools } = Cu.import("resource
 const { getActiveTab } = require("../sdk/tabs/utils");
 const { getMostRecentBrowserWindow } = require("../sdk/window/utils");
 
 const targetFor = target => {
   target = target || getActiveTab(getMostRecentBrowserWindow());
   return devtools.TargetFactory.forTab(target);
 };
 
+const getId = id => ((id.prototype && id.prototype.id) || id.id || id);
+
 const getCurrentPanel = toolbox => toolbox.getCurrentPanel();
 exports.getCurrentPanel = getCurrentPanel;
 
 const openToolbox = (id, tab) => {
-  id = id.prototype.id || id.id || id;
+  id = getId(id);
   return gDevTools.showToolbox(targetFor(tab), id);
 };
 exports.openToolbox = openToolbox;
 
 const closeToolbox = tab => gDevTools.closeToolbox(targetFor(tab));
 exports.closeToolbox = closeToolbox;
 
 const getToolbox = tab => gDevTools.getToolbox(targetFor(tab));
 exports.getToolbox = getToolbox;
 
 const openToolboxPanel = (id, tab) => {
-  id = id.prototype.id || id.id || id;
+  id = getId(id);
   return gDevTools.showToolbox(targetFor(tab), id).then(getCurrentPanel);
 };
 exports.openToolboxPanel = openToolboxPanel;
--- a/addon-sdk/source/lib/dev/volcan.js
+++ b/addon-sdk/source/lib/dev/volcan.js
@@ -1,8 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 !function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.volcan=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
 "use strict";
 
 var Client = _dereq_("../client").Client;
 
 function connect(port) {
   var client = new Client();
   return client.connect(port);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/framescript/context-menu.js
@@ -0,0 +1,215 @@
+/* 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 { query, constant, cache } = require("sdk/lang/functional");
+const { pairs, each, map, object } = require("sdk/util/sequence");
+const { nodeToMessageManager } = require("./util");
+
+// Decorator function that takes `f` function and returns one that attempts
+// to run `f` with given arguments. In case of exception error is logged
+// and `fallback` is returned instead.
+const Try = (fn, fallback=null) => (...args) => {
+  try {
+    return fn(...args);
+  } catch(error) {
+    console.error(error);
+    return fallback;
+  }
+};
+
+// Decorator funciton that takes `f` function and returns one that returns
+// JSON cloned result of whatever `f` returns for given arguments.
+const JSONReturn = f => (...args) => JSON.parse(JSON.stringify(f(...args)));
+
+const Null = constant(null);
+
+// Table of readers mapped to field names they're going to be reading.
+const readers = Object.create(null);
+// Read function takes "contextmenu" event target `node` and returns table of
+// read field names mapped to appropriate values. Read uses above defined read
+// table to read data for all registered readers.
+const read = node =>
+  object(...map(([id, read]) => [id, read(node, id)], pairs(readers)));
+
+// Table of built-in readers, each takes a descriptor and returns a reader:
+// descriptor -> node -> JSON
+const parsers = Object.create(null)
+// Function takes a descriptor of the remotely defined reader and parsese it
+// to construct a local reader that's going to read out data from context menu
+// target.
+const parse = descriptor => {
+  const parser = parsers[descriptor.category];
+  if (!parser) {
+    console.error("Unknown reader descriptor was received", descriptor, `"${descriptor.category}"`);
+    return Null
+  }
+  return Try(parser(descriptor));
+}
+
+// TODO: Test how chrome's mediaType behaves to try and match it's behavior.
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const SVG_NS = "http://www.w3.org/2000/svg";
+
+// Firefox always creates a HTMLVideoElement when loading an ogg file
+// directly. If the media is actually audio, be smarter and provide a
+// context menu with audio operations.
+// Source: https://github.com/mozilla/gecko-dev/blob/28c2fca3753c5371643843fc2f2f205146b083b7/browser/base/content/nsContextMenu.js#L632-L637
+const isVideoLoadingAudio = node =>
+  node.readyState >= node.HAVE_METADATA &&
+    (node.videoWidth == 0 || node.videoHeight == 0)
+
+const isVideo = node =>
+  node instanceof node.ownerDocument.defaultView.HTMLVideoElement &&
+  !isVideoLoadingAudio(node);
+
+const isAudio = node => {
+  const {HTMLVideoElement, HTMLAudioElement} = node.ownerDocument.defaultView;
+  return node instanceof HTMLAudioElement ? true :
+         node instanceof HTMLVideoElement ? isVideoLoadingAudio(node) :
+         false;
+};
+
+const isImage = ({namespaceURI, localName}) =>
+  namespaceURI === HTML_NS && localName === "img" ? true :
+  namespaceURI === XUL_NS && localName === "image" ? true :
+  namespaceURI === SVG_NS && localName === "image" ? true :
+  false;
+
+parsers["reader/MediaType()"] = constant(node =>
+  isImage(node) ? "image" :
+  isAudio(node) ? "audio" :
+  isVideo(node) ? "video" :
+  null);
+
+
+const readLink = node =>
+  node.namespaceURI === HTML_NS && node.localName === "a" ? node.href :
+  readLink(node.parentNode);
+
+parsers["reader/LinkURL()"] = constant(node =>
+  node.matches("a, a *") ? readLink(node) : null);
+
+// Reader that reads out `true` if "contextmenu" `event.target` matches
+// `descriptor.selector` and `false` if it does not.
+parsers["reader/SelectorMatch()"] = ({selector}) =>
+  node => node.matches(selector);
+
+// Accessing `selectionStart` and `selectionEnd` properties on non
+// editable input nodes throw exceptions, there for we need this util
+// function to guard us against them.
+const getInputSelection = node => {
+  try {
+    if ("selectionStart" in node && "selectionEnd" in node) {
+      const {selectionStart, selectionEnd} = node;
+      return {selectionStart, selectionEnd}
+    }
+  }
+  catch(_) {}
+
+  return null;
+}
+
+// Selection reader does not really cares about descriptor so it is
+// a constant function returning selection reader. Selection reader
+// returns string of the selected text or `null` if there is no selection.
+parsers["reader/Selection()"] = constant(node => {
+  const selection = node.ownerDocument.getSelection();
+  if (!selection.isCollapsed) {
+    return selection.toString();
+  }
+  // If target node is editable (text, input, textarea, etc..) document does
+  // not really handles selections there. There for we fallback to checking
+  // `selectionStart` `selectionEnd` properties and if they are present we
+  // extract selections manually from the `node.value`.
+  else {
+    const selection = getInputSelection(node);
+    const isSelected = selection &&
+                       Number.isInteger(selection.selectionStart) &&
+                       Number.isInteger(selection.selectionEnd) &&
+                       selection.selectionStart !== selection.selectionEnd;
+    return  isSelected ? node.value.substring(selection.selectionStart,
+                                              selection.selectionEnd) :
+            null;
+  }
+});
+
+// Query reader just reads out properties from the node, so we just use `query`
+// utility function.
+parsers["reader/Query()"] = ({path}) => JSONReturn(query(path));
+// Attribute reader just reads attribute of the event target node.
+parsers["reader/Attribute()"] = ({name}) => node => node.getAttribute(name);
+
+// Extractor reader defines generates a reader out of serialized function, who's
+// return value is JSON cloned. Note: We do know source will evaluate to function
+// as that's what we serialized on the other end, it's also ok if generated function
+// is going to throw as registered readers are wrapped in try catch to avoid breakting
+// unrelated readers.
+parsers["reader/Extractor()"] = ({source}) =>
+  JSONReturn(new Function("return (" + source + ")")());
+
+// If the context-menu target node or any of its ancestors is one of these,
+// Firefox uses a tailored context menu, and so the page context doesn't apply.
+// There for `reader/isPage()` will read `false` in that case otherwise it's going
+// to read `true`.
+const nonPageElements = ["a", "applet", "area", "button", "canvas", "object",
+                         "embed", "img", "input", "map", "video", "audio", "menu",
+                         "option", "select", "textarea", "[contenteditable=true]"];
+const nonPageSelector = nonPageElements.
+                          concat(nonPageElements.map(tag => `${tag} *`)).
+                          join(", ");
+
+// Note: isPageContext implementation could have actually used SelectorMatch reader,
+// but old implementation was also checked for collapsed selection there for to keep
+// the behavior same we end up implementing a new reader.
+parsers["reader/isPage()"] = constant(node =>
+  node.ownerDocument.defaultView.getSelection().isCollapsed &&
+  !node.matches(nonPageSelector));
+
+// Reads `true` if node is in an iframe otherwise returns true.
+parsers["reader/isFrame()"] = constant(node =>
+  !!node.ownerDocument.defaultView.frameElement);
+
+parsers["reader/isEditable()"] = constant(node => {
+  const selection = getInputSelection(node);
+  return selection ? !node.readOnly && !node.disabled : node.isContentEditable;
+});
+
+
+// TODO: Add some reader to read out tab id.
+
+const onReadersUpdate = message => {
+  each(([id, descriptor]) => {
+    if (descriptor) {
+      readers[id] = parse(descriptor);
+    }
+    else {
+      delete readers[id];
+    }
+  }, pairs(message.data));
+};
+exports.onReadersUpdate = onReadersUpdate;
+
+
+const onContextMenu = event => {
+  if (!event.defaultPrevented) {
+    const manager = nodeToMessageManager(event.target);
+    manager.sendSyncMessage("sdk/context-menu/read", read(event.target), readers);
+  }
+};
+exports.onContextMenu = onContextMenu;
+
+
+const onContentFrame = (frame) => {
+  // Listen for contextmenu events in on this frame.
+  frame.addEventListener("contextmenu", onContextMenu);
+  // Listen to registered reader changes and update registry.
+  frame.addMessageListener("sdk/context-menu/readers", onReadersUpdate);
+
+  // Request table of readers (if this is loaded in a new process some table
+  // changes may be missed, this is way to sync up).
+  frame.sendAsyncMessage("sdk/context-menu/readers?");
+};
+exports.onContentFrame = onContentFrame;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/framescript/manager.js
@@ -0,0 +1,26 @@
+/* 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": "unstable"
+};
+
+const mime = "application/javascript";
+const requireURI = module.uri.replace("framescript/manager.js",
+                                      "toolkit/require.js");
+
+const requireLoadURI = `data:${mime},this["Components"].utils.import("${requireURI}")`
+
+// Loads module with given `id` into given `messageManager` via shared module loader. If `init`
+// string is passed, will call module export with that name and pass frame script environment
+// of the `messageManager` into it. Since module will load only once per process (which is
+// once for chrome proces & second for content process) it is useful to have an init function
+// to setup event listeners on each content frame.
+const loadModule = (messageManager, id, allowDelayed, init) => {
+  const moduleLoadURI = `${requireLoadURI}.require("${id}")`
+  const uri = init ? `${moduleLoadURI}.${init}(this)` : moduleLoadURI;
+  messageManager.loadFrameScript(uri, allowDelayed);
+};
+exports.loadModule = loadModule;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/framescript/util.js
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+module.metadata = {
+  "stability": "unstable"
+};
+
+
+const { Ci } = require("chrome");
+
+const windowToMessageManager = window =>
+  window.
+    QueryInterface(Ci.nsIInterfaceRequestor).
+    getInterface(Ci.nsIDocShell).
+    sameTypeRootTreeItem.
+    QueryInterface(Ci.nsIDocShell).
+    QueryInterface(Ci.nsIInterfaceRequestor).
+    getInterface(Ci.nsIContentFrameMessageManager);
+exports.windowToMessageManager = windowToMessageManager;
+
+const nodeToMessageManager = node =>
+  windowToMessageManager(node.ownerDocument.defaultView);
+exports.nodeToMessageManager = nodeToMessageManager;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/index.js
@@ -0,0 +1,3 @@
+/* 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/. */
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/addon/bootstrap.js
@@ -0,0 +1,160 @@
+/* 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 { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
+const { Task: { spawn } } = require("resource://gre/modules/Task.jsm");
+const { readURI } = require("sdk/net/url");
+const { mount, unmount } = require("sdk/uri/resource");
+const { setTimeout } = require("sdk/timers");
+const { Loader, Require, Module, main, unload } = require("toolkit/loader");
+const prefs = require("sdk/preferences/service");
+
+// load below now, so that it can be used by sdk/addon/runner
+// see bug https://bugzilla.mozilla.org/show_bug.cgi?id=1042239
+const Startup = Cu.import("resource://gre/modules/sdk/system/Startup.js", {});
+
+const REASON = [ "unknown", "startup", "shutdown", "enable", "disable",
+                 "install", "uninstall", "upgrade", "downgrade" ];
+
+const UUID_PATTERN = /^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/;
+// Takes add-on ID and normalizes it to a domain name so that add-on
+// can be mapped to resource://domain/
+const readDomain = id =>
+  // If only `@` character is the first one, than just substract it,
+  // otherwise fallback to legacy normalization code path. Note: `.`
+  // is valid character for resource substitutaiton & we intend to
+  // make add-on URIs intuitive, so it's best to just stick to an
+  // add-on author typed input.
+  id.lastIndexOf("@") === 0 ? id.substr(1).toLowerCase() :
+  id.toLowerCase().
+     replace(/@/g, "-at-").
+     replace(/\./g, "-dot-").
+     replace(UUID_PATTERN, "$1");
+
+const readPaths = id => {
+  const base = `extensions.modules.${id}.path.`;
+  const domain = readDomain(id);
+  return prefs.keys(base).reduce((paths, key) => {
+    const value = prefs.get(key);
+    const name = key.replace(base, "");
+    const path = name.split(".").join("/");
+    const prefix = path.length ? `${path}/` : path;
+    const uri = value.endsWith("/") ? value : `${value}/`;
+    const root = `extensions.modules.${domain}.commonjs.path.${name}`;
+
+    mount(root, uri);
+
+    paths[prefix] = `resource://${root}/`;
+    return paths;
+  }, {});
+};
+
+const Bootstrap = function(mountURI) {
+  this.mountURI = mountURI;
+  this.install = this.install.bind(this);
+  this.uninstall = this.uninstall.bind(this);
+  this.startup = this.startup.bind(this);
+  this.shutdown = this.shutdown.bind(this);
+};
+Bootstrap.prototype = {
+  constructor: Bootstrap,
+  mount(domain, rootURI) {
+    mount(domain, rootURI);
+    this.domain = domain;
+  },
+  unmount() {
+    if (this.domain) {
+      unmount(this.domain);
+      this.domain = null;
+    }
+  },
+  install(addon, reason) {
+  },
+  uninstall(addon, reason) {
+    const {id} = addon;
+
+    prefs.reset(`extensions.${id}.sdk.domain`);
+    prefs.reset(`extensions.${id}.sdk.version`);
+    prefs.reset(`extensions.${id}.sdk.rootURI`);
+    prefs.reset(`extensions.${id}.sdk.baseURI`);
+    prefs.reset(`extensions.${id}.sdk.load.reason`);
+
+  },
+  startup(addon, reasonCode) {
+    const { id, version, resourceURI: {spec: addonURI} } = addon;
+    const rootURI = this.mountURI || addonURI;
+    const reason = REASON[reasonCode];
+
+    spawn(function*() {
+      const metadata = JSON.parse(yield readURI(`${rootURI}package.json`));
+      const domain = readDomain(id);
+      const baseURI = `resource://${domain}/`;
+
+      this.mount(domain, rootURI);
+
+      prefs.set(`extensions.${id}.sdk.domain`, domain);
+      prefs.set(`extensions.${id}.sdk.version`, version);
+      prefs.set(`extensions.${id}.sdk.rootURI`, rootURI);
+      prefs.set(`extensions.${id}.sdk.baseURI`, baseURI);
+      prefs.set(`extensions.${id}.sdk.load.reason`, reason);
+
+      const command = prefs.get(`extensions.${id}.sdk.load.command`);
+
+      const loader = Loader({
+        id,
+        isNative: true,
+        checkCompatibility: true,
+        prefixURI: baseURI,
+        rootURI: baseURI,
+        name: metadata.name,
+        paths: Object.assign({
+          "": "resource://gre/modules/commonjs/",
+          "devtools/": "resource://gre/modules/devtools/",
+          "./": baseURI
+        }, readPaths(id)),
+        manifest: metadata,
+        metadata: metadata,
+        modules: {
+          "@test/options": {}
+        },
+        noQuit: prefs.get(`extensions.${id}.sdk.test.no-quit`, false)
+      });
+      this.loader = loader;
+
+      const module = Module("package.json", `${baseURI}package.json`);
+      const require = Require(loader, module);
+      const main = command === "test" ? "sdk/test/runner" : null;
+      const prefsURI = `${baseURI}defaults/preferences/prefs.js`;
+
+      const { startup } = require("sdk/addon/runner");
+      startup(reason, {loader, main, prefsURI});
+    }.bind(this)).catch(error => {
+      console.error(`Failed to start ${id} addon`, error);
+      throw error;
+    });
+  },
+  shutdown(addon, code) {
+    const { loader, domain } = this;
+
+    this.unmount();
+    this.unload(REASON[code]);
+  },
+  unload(reason) {
+    const {loader} = this;
+    if (loader) {
+      this.loader = null;
+      unload(loader, reason);
+      setTimeout(() => {
+        for (let uri of Object.keys(loader.sandboxes)) {
+          Cu.nukeSandbox(loader.sandboxes[uri]);
+          delete loader.sandboxes[uri];
+          delete loader.modules[uri];
+        }
+      }, 1000);
+    }
+  }
+};
+exports.Bootstrap = Bootstrap;
--- a/addon-sdk/source/lib/sdk/clipboard.js
+++ b/addon-sdk/source/lib/sdk/clipboard.js
@@ -4,17 +4,18 @@
 
 "use strict";
 
 module.metadata = {
   "stability": "stable",
   "engines": {
     // TODO Fennec Support 789757
     "Firefox": "*",
-    "SeaMonkey": "*"
+    "SeaMonkey": "*",
+    "Thunderbird": "*"
   }
 };
 
 const { Cc, Ci } = require("chrome");
 const { DataURL } = require("./url");
 const errors = require("./deprecated/errors");
 const apiUtils = require("./deprecated/api-utils");
 /*
@@ -119,36 +120,34 @@ exports.set = function(aData, aDataType)
                     "(couldn't create a Transferable object).");
   // Bug 769440: Starting with FF16, transferable have to be inited
   if ("init" in xferable)
     xferable.init(null);
 
   switch (flavor) {
     case "text/html":
       // add text/html flavor
-      let (str = Cc["@mozilla.org/supports-string;1"].
-                 createInstance(Ci.nsISupportsString))
-      {
-        str.data = options.data;
-        xferable.addDataFlavor(flavor);
-        xferable.setTransferData(flavor, str, str.data.length * 2);
-      }
+      let str = Cc["@mozilla.org/supports-string;1"].
+                 createInstance(Ci.nsISupportsString);
+
+      str.data = options.data;
+      xferable.addDataFlavor(flavor);
+      xferable.setTransferData(flavor, str, str.data.length * 2);
 
       // add a text/unicode flavor (html converted to plain text)
-      let (str = Cc["@mozilla.org/supports-string;1"].
-                 createInstance(Ci.nsISupportsString),
-           converter = Cc["@mozilla.org/feed-textconstruct;1"].
-                       createInstance(Ci.nsIFeedTextConstruct))
-      {
-        converter.type = "html";
-        converter.text = options.data;
-        str.data = converter.plainText();
-        xferable.addDataFlavor("text/unicode");
-        xferable.setTransferData("text/unicode", str, str.data.length * 2);
-      }
+      str = Cc["@mozilla.org/supports-string;1"].
+               createInstance(Ci.nsISupportsString);
+      let converter = Cc["@mozilla.org/feed-textconstruct;1"].
+                     createInstance(Ci.nsIFeedTextConstruct);
+
+      converter.type = "html";
+      converter.text = options.data;
+      str.data = converter.plainText();
+      xferable.addDataFlavor("text/unicode");
+      xferable.setTransferData("text/unicode", str, str.data.length * 2);
       break;
 
     // Set images to the clipboard is not straightforward, to have an idea how
     // it works on platform side, see:
     // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsCopySupport.cpp?rev=7857c5bff017#530
     case "image/png":
       let image = options.data;
 
--- a/addon-sdk/source/lib/sdk/content/sandbox.js
+++ b/addon-sdk/source/lib/sdk/content/sandbox.js
@@ -188,17 +188,18 @@ const WorkerSandbox = Class({
 
     // Handle messages send by this script:
     setListeners(this, console);
 
     // Inject `addon` global into target document if document is trusted,
     // `addon` in document is equivalent to `self` in content script.
     if (requiresAddonGlobal(worker)) {
       Object.defineProperty(getUnsafeWindow(window), 'addon', {
-          value: content.self
+          value: content.self,
+          configurable: true
         }
       );
     }
 
     // Inject our `console` into target document if worker doesn't have a tab
     // (e.g Panel, PageWorker, Widget).
     // `worker.tab` can't be used because bug 804935.
     if (!getTabForContentWindow(window)) {
--- a/addon-sdk/source/lib/sdk/content/worker-child.js
+++ b/addon-sdk/source/lib/sdk/content/worker-child.js
@@ -32,17 +32,27 @@ const WorkerChild = Class({
     this.observe = this.observe.bind(this);
 
     for (let topic in EVENTS)
       observe(topic, this.observe);
 
     this.receive = this.receive.bind(this);
     this.manager.addMessageListener('sdk/worker/message', this.receive);
 
-    this.sandbox = WorkerSandbox(this, getByInnerId(this.window));
+    let window = getByInnerId(this.window);
+    this.sandbox = WorkerSandbox(this, window);
+
+    if (options.currentReadyState != "complete" &&
+        window.document.readyState == "complete") {
+      // If we attempted to attach the worker before the document was loaded but
+      // it has now completed loading then the parent should reasonably expect
+      // to see a pageshow event.
+      this.sandbox.emitSync("pageshow");
+      this.send("pageshow");
+    }
   },
   // messages
   receive({ data: { id, args }}) {
     if (id !== this.id)
       return;
     this.sandbox.emit(...args);
     if (args[0] === 'detach')
       this.destroy(args[1]);
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/content/worker-parent.js
+++ /dev/null
@@ -1,184 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-module.metadata = {
-  "stability": "unstable"
-};
-
-const { emit } = require('../event/core');
-const { omit } = require('../util/object');
-const { Class } = require('../core/heritage');
-const { method } = require('../lang/functional');
-const { getInnerId } = require('../window/utils');
-const { EventTarget } = require('../event/target');
-const { when, ensure } = require('../system/unload');
-const { getTabForWindow } = require('../tabs/helpers');
-const { getTabForContentWindow, getBrowserForTab } = require('../tabs/utils');
-const { isPrivate } = require('../private-browsing/utils');
-const { getFrameElement } = require('../window/utils');
-const { attach, detach, destroy } = require('./utils');
-const { on: observe } = require('../system/events');
-const { uuid } = require('../util/uuid');
-const { Ci, Cc } = require('chrome');
-
-const ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"].
-  getService(Ci.nsIMessageBroadcaster);
-
-// null-out cycles in .modules to make @loader/options JSONable
-const ADDON = omit(require('@loader/options'), ['modules', 'globals']);
-
-const workers = new WeakMap();
-let modelFor = (worker) => workers.get(worker);
-
-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.";
-
-const ERR_FROZEN = "The page is currently hidden and can no longer be used " +
-                   "until it is visible again.";
-
-// a handle for communication between content script and addon code
-const Worker = Class({
-  implements: [EventTarget],
-  initialize(options = {}) {
-
-    let model = {
-      inited: false,
-      earlyEvents: [],        // fired before worker was inited
-      frozen: true,           // document is in BFcache, let it go
-      options,
-    };
-    workers.set(this, model);
-
-    ensure(this, 'destroy');
-    this.on('detach', this.detach);
-    EventTarget.prototype.initialize.call(this, options);
-
-    this.receive = this.receive.bind(this);
-
-    model.observe = ({ subject }) => {
-      let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
-      if (model.window && getInnerId(model.window) === id)
-        this.detach();
-    }
-
-    observe('inner-window-destroyed', model.observe);
-
-    this.port = EventTarget();
-    this.port.emit = this.send.bind(this, 'event');
-    this.postMessage = this.send.bind(this, 'message');
-
-    if ('window' in options)
-      attach(this, options.window);
-  },
-  // messages
-  receive({ data: { id, args }}) {
-    let model = modelFor(this);
-    if (id !== model.id || !model.childWorker)
-      return;
-    if (args[0] === 'event')
-      emit(this.port, ...args.slice(1))
-    else
-      emit(this, ...args);
-  },
-  send(...args) {
-    let model = modelFor(this);
-    if (!model.inited) {
-      model.earlyEvents.push(args);
-      return;
-    }
-    if (!model.childWorker && args[0] !== 'detach')
-      throw new Error(ERR_DESTROYED);
-    if (model.frozen && args[0] !== 'detach')
-      throw new Error(ERR_FROZEN);
-    try {
-      model.manager.sendAsyncMessage('sdk/worker/message', { id: model.id, args });
-    } catch (e) {
-      //
-    }
-  },
-  // properties
-  get url() {
-    let { window } = modelFor(this);
-    return window && window.document.location.href;
-  },
-  get contentURL() {
-    let { window } = modelFor(this);
-    return window && window.document.URL;
-  },
-  get tab() {
-    let { window } = modelFor(this);
-    return window && getTabForWindow(window);
-  },
-  toString: () => '[object Worker]',
-  // methods
-  attach: method(attach),
-  detach: method(detach),
-  destroy: method(destroy),
-})
-exports.Worker = Worker;
-
-attach.define(Worker, function(worker, window) {
-  let model = modelFor(worker);
-
-  model.window = window;
-  model.options.window = getInnerId(window);
-  model.id = model.options.id = String(uuid());
-
-  let tab = getTabForContentWindow(window);
-  if (tab) {
-    model.manager = getBrowserForTab(tab).messageManager;
-  } else {
-    model.manager = getFrameElement(window.top).frameLoader.messageManager;
-  }
-
-  model.manager.addMessageListener('sdk/worker/event', worker.receive);
-  model.manager.addMessageListener('sdk/worker/attach', attach);
-
-  model.manager.sendAsyncMessage('sdk/worker/create', {
-    options: model.options,
-    addon: ADDON
-  });
-
-  function attach({ data }) {
-    if (data.id !== model.id)
-      return;
-    model.manager.removeMessageListener('sdk/worker/attach', attach);
-    model.childWorker = true;
-
-    worker.on('pageshow', () => model.frozen = false);
-    worker.on('pagehide', () => model.frozen = true);
-
-    model.inited = true;
-    model.frozen = false;
-
-    model.earlyEvents.forEach(args => worker.send(...args));
-    emit(worker, 'attach', window);
-  }
-})
-
-// unload and release the child worker, release window reference
-detach.define(Worker, function(worker, reason) {
-  let model = modelFor(worker);
-  worker.send('detach', reason);
-  if (!model.childWorker)
-    return;
-
-  model.childWorker = null;
-  model.earlyEvents = [];
-  model.window = null;
-  emit(worker, 'detach');
-  model.manager.removeMessageListener('sdk/worker/event', this.receive);
-})
-
-isPrivate.define(Worker, ({ tab }) => isPrivate(tab));
-
-// unlod worker, release references
-destroy.define(Worker, function(worker, reason) {
-  detach(worker, reason);
-  modelFor(worker).inited = true;
-})
-
-// unload Loaders used for creating WorkerChild instances in each process
-when(() => ppmm.broadcastAsyncMessage('sdk/loader/unload', { data: ADDON }));
--- a/addon-sdk/source/lib/sdk/content/worker.js
+++ b/addon-sdk/source/lib/sdk/content/worker.js
@@ -2,285 +2,184 @@
  * 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": "unstable"
 };
 
+const { emit } = require('../event/core');
+const { omit } = require('../util/object');
 const { Class } = require('../core/heritage');
-const { EventTarget } = require('../event/target');
-const { on, off, emit, setListeners } = require('../event/core');
-const {
-  attach, detach, destroy
-} = require('./utils');
 const { method } = require('../lang/functional');
-const { Ci, Cu, Cc } = require('chrome');
-const unload = require('../system/unload');
-const events = require('../system/events');
-const { getInnerId } = require("../window/utils");
-const { WorkerSandbox } = require('./sandbox');
+const { getInnerId } = require('../window/utils');
+const { EventTarget } = require('../event/target');
+const { when, ensure } = require('../system/unload');
 const { getTabForWindow } = require('../tabs/helpers');
+const { getTabForContentWindow, getBrowserForTab } = require('../tabs/utils');
 const { isPrivate } = require('../private-browsing/utils');
+const { getFrameElement } = require('../window/utils');
+const { attach, detach, destroy } = require('./utils');
+const { on: observe } = require('../system/events');
+const { uuid } = require('../util/uuid');
+const { Ci, Cc } = require('chrome');
 
-// A weak map of workers to hold private attributes that
-// should not be exposed
+const ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"].
+  getService(Ci.nsIMessageBroadcaster);
+
+// null-out cycles in .modules to make @loader/options JSONable
+const ADDON = omit(require('@loader/options'), ['modules', 'globals']);
+
 const workers = new WeakMap();
-
 let modelFor = (worker) => workers.get(worker);
 
-const ERR_DESTROYED =
-  "Couldn't find the worker to receive this message. " +
+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.";
 
 const ERR_FROZEN = "The page is currently hidden and can no longer be used " +
                    "until it is visible again.";
 
-/**
- * Message-passing facility for communication between code running
- * in the content and add-on process.
- * @see https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/content_worker
- */
+// a handle for communication between content script and addon code
 const Worker = Class({
   implements: [EventTarget],
-  initialize: function WorkerConstructor (options) {
-    // Save model in weak map to not expose properties
-    let model = createModel();
+  initialize(options = {}) {
+
+    let model = {
+      inited: false,
+      earlyEvents: [],        // fired before worker was inited
+      frozen: true,           // document is in BFcache, let it go
+      options,
+    };
     workers.set(this, model);
 
-    options = options || {};
+    ensure(this, 'destroy');
+    this.on('detach', this.detach);
+    EventTarget.prototype.initialize.call(this, options);
 
-    if ('contentScriptFile' in options)
-      this.contentScriptFile = options.contentScriptFile;
-    if ('contentScriptOptions' in options)
-      this.contentScriptOptions = options.contentScriptOptions;
-    if ('contentScript' in options)
-      this.contentScript = options.contentScript;
-    if ('injectInDocument' in options)
-      this.injectInDocument = !!options.injectInDocument;
+    this.receive = this.receive.bind(this);
 
-    setListeners(this, options);
-
-    unload.ensure(this, "destroy");
+    model.observe = ({ subject }) => {
+      let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
+      if (model.window && getInnerId(model.window) === id)
+        this.detach();
+    }
 
-    // Ensure that worker.port is initialized for contentWorker to be able
-    // to send events during worker initialization.
-    this.port = createPort(this);
+    observe('inner-window-destroyed', model.observe);
 
-    model.documentUnload = documentUnload.bind(this);
-    model.pageShow = pageShow.bind(this);
-    model.pageHide = pageHide.bind(this);
+    this.port = EventTarget();
+    this.port.emit = this.send.bind(this, 'event');
+    this.postMessage = this.send.bind(this, 'message');
 
     if ('window' in options)
       attach(this, options.window);
   },
-
-  /**
-   * Sends a message to the worker's global scope. Method takes single
-   * argument, which represents data to be sent to the worker. The data may
-   * be any primitive type value or `JSON`. Call of this method asynchronously
-   * emits `message` event with data value in the global scope of this
-   * symbiont.
-   *
-   * `message` event listeners can be set either by calling
-   * `self.on` with a first argument string `"message"` or by
-   * implementing `onMessage` function in the global scope of this worker.
-   * @param {Number|String|JSON} data
-   */
-  postMessage: function (...data) {
+  // messages
+  receive({ data: { id, args }}) {
     let model = modelFor(this);
-    let args = ['message'].concat(data);
+    if (id !== model.id || !model.childWorker)
+      return;
+    if (args[0] === 'event')
+      emit(this.port, ...args.slice(1))
+    else
+      emit(this, ...args);
+  },
+  send(...args) {
+    let model = modelFor(this);
     if (!model.inited) {
       model.earlyEvents.push(args);
       return;
     }
-    processMessage.apply(null, [this].concat(args));
-  },
-
-  get url () {
-    let model = modelFor(this);
-    // model.window will be null after detach
-    return model.window ? model.window.document.location.href : null;
-  },
-
-  get contentURL () {
-    let model = modelFor(this);
-    return model.window ? model.window.document.URL : null;
+    if (!model.childWorker && args[0] !== 'detach')
+      throw new Error(ERR_DESTROYED);
+    if (model.frozen && args[0] !== 'detach')
+      throw new Error(ERR_FROZEN);
+    try {
+      model.manager.sendAsyncMessage('sdk/worker/message', { id: model.id, args });
+    } catch (e) {
+      //
+    }
   },
-
-  get tab () {
-    let model = modelFor(this);
-    // model.window will be null after detach
-    if (model.window)
-      return getTabForWindow(model.window);
-    return null;
+  // properties
+  get url() {
+    let { window } = modelFor(this);
+    return window && window.document.location.href;
   },
-
-  // Implemented to provide some of the previous features of exposing sandbox
-  // so that Worker can be extended
-  getSandbox: function () {
-    return modelFor(this).contentWorker;
+  get contentURL() {
+    let { window } = modelFor(this);
+    return window && window.document.URL;
   },
-
-  toString: function () { return '[object Worker]'; },
+  get tab() {
+    let { window } = modelFor(this);
+    return window && getTabForWindow(window);
+  },
+  toString: () => '[object Worker]',
+  // methods
   attach: method(attach),
   detach: method(detach),
-  destroy: method(destroy)
-});
+  destroy: method(destroy),
+})
 exports.Worker = Worker;
 
-attach.define(Worker, function (worker, window) {
-  let model = modelFor(worker);
-  model.window = window;
-  // Track document unload to destroy this worker.
-  // We can't watch for unload event on page's window object as it
-  // prevents bfcache from working:
-  // https://developer.mozilla.org/En/Working_with_BFCache
-  model.windowID = getInnerId(model.window);
-  events.on("inner-window-destroyed", model.documentUnload);
-
-  // will set model.contentWorker pointing to the private API:
-  model.contentWorker = WorkerSandbox(worker, model.window);
-
-  // Listen to pagehide event in order to freeze the content script
-  // while the document is frozen in bfcache:
-  model.window.addEventListener("pageshow", model.pageShow, true);
-  model.window.addEventListener("pagehide", model.pageHide, true);
-
-  // Mainly enable worker.port.emit to send event to the content worker
-  model.inited = true;
-  model.frozen = false;
-
-  // Fire off `attach` event
-  emit(worker, 'attach', window);
-
-  // Process all events and messages that were fired before the
-  // worker was initialized.
-  model.earlyEvents.forEach(args => processMessage.apply(null, [worker].concat(args)));
-});
-
-/**
- * Remove all internal references to the attached document
- * Tells _port to unload itself and removes all the references from itself.
- */
-detach.define(Worker, function (worker, reason) {
+attach.define(Worker, function(worker, window) {
   let model = modelFor(worker);
 
-  // maybe unloaded before content side is created
-  if (model.contentWorker) {
-    model.contentWorker.destroy(reason);
+  model.window = window;
+  model.options.window = getInnerId(window);
+  model.options.currentReadyState = window.document.readyState;
+  model.id = model.options.id = String(uuid());
+
+  let tab = getTabForContentWindow(window);
+  if (tab) {
+    model.manager = getBrowserForTab(tab).messageManager;
+  } else {
+    model.manager = getFrameElement(window.top).frameLoader.messageManager;
   }
 
-  model.contentWorker = null;
-  if (model.window) {
-    model.window.removeEventListener("pageshow", model.pageShow, true);
-    model.window.removeEventListener("pagehide", model.pageHide, true);
+  model.manager.addMessageListener('sdk/worker/event', worker.receive);
+  model.manager.addMessageListener('sdk/worker/attach', attach);
+
+  model.manager.sendAsyncMessage('sdk/worker/create', {
+    options: model.options,
+    addon: ADDON
+  });
+
+  function attach({ data }) {
+    if (data.id !== model.id)
+      return;
+    model.manager.removeMessageListener('sdk/worker/attach', attach);
+    model.childWorker = true;
+
+    worker.on('pageshow', () => model.frozen = false);
+    worker.on('pagehide', () => model.frozen = true);
+
+    model.inited = true;
+    model.frozen = false;
+
+    model.earlyEvents.forEach(args => worker.send(...args));
+    emit(worker, 'attach', window);
   }
+})
+
+// unload and release the child worker, release window reference
+detach.define(Worker, function(worker, reason) {
+  let model = modelFor(worker);
+  worker.send('detach', reason);
+  if (!model.childWorker)
+    return;
+
+  model.childWorker = null;
+  model.earlyEvents = [];
   model.window = null;
-  // This method may be called multiple times,
-  // avoid dispatching `detach` event more than once
-  if (model.windowID) {
-    model.windowID = null;
-    events.off("inner-window-destroyed", model.documentUnload);
-    model.earlyEvents.length = 0;
-    emit(worker, 'detach');
-  }
-  model.inited = false;
-});
+  emit(worker, 'detach');
+  model.manager.removeMessageListener('sdk/worker/event', this.receive);
+})
 
 isPrivate.define(Worker, ({ tab }) => isPrivate(tab));
 
-/**
- * Tells content worker to unload itself and
- * removes all the references from itself.
- */
-destroy.define(Worker, function (worker, reason) {
+// unlod worker, release references
+destroy.define(Worker, function(worker, reason) {
   detach(worker, reason);
   modelFor(worker).inited = true;
-  // Specifying no type or listener removes all listeners
-  // from target
-  off(worker);
-  off(worker.port);
-});
-
-/**
- * Events fired by workers
- */
-function documentUnload ({ subject, data }) {
-  let model = modelFor(this);
-  let innerWinID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
-  if (innerWinID != model.windowID) return false;
-  detach(this);
-  return true;
-}
-
-function pageShow () {
-  let model = modelFor(this);
-  model.contentWorker.emitSync('pageshow');
-  emit(this, 'pageshow');
-  model.frozen = false;
-}
-
-function pageHide () {
-  let model = modelFor(this);
-  model.contentWorker.emitSync('pagehide');
-  emit(this, 'pagehide');
-  model.frozen = true;
-}
-
-/**
- * Fired from postMessage and emitEventToContent, or from the earlyMessage
- * queue when fired before the content is loaded. Sends arguments to
- * contentWorker if able
- */
+})
 
-function processMessage (worker, ...args) {
-  let model = modelFor(worker) || {};
-  if (!model.contentWorker)
-    throw new Error(ERR_DESTROYED);
-  if (model.frozen)
-    throw new Error(ERR_FROZEN);
-  model.contentWorker.emit.apply(null, args);
-}
-
-function createModel () {
-  return {
-    // List of messages fired before worker is initialized
-    earlyEvents: [],
-    // Is worker connected to the content worker sandbox ?
-    inited: false,
-    // Is worker being frozen? i.e related document is frozen in bfcache.
-    // Content script should not be reachable if frozen.
-    frozen: true,
-    /**
-     * Reference to the content side of the worker.
-     * @type {WorkerGlobalScope}
-     */
-    contentWorker: null,
-    /**
-     * Reference to the window that is accessible from
-     * the content scripts.
-     * @type {Object}
-     */
-    window: null
-  };
-}
-
-function createPort (worker) {
-  let port = EventTarget();
-  port.emit = emitEventToContent.bind(null, worker);
-  return port;
-}
-
-/**
- * Emit a custom event to the content script,
- * i.e. emit this event on `self.port`
- */
-function emitEventToContent (worker, ...eventArgs) {
-  let model = modelFor(worker);
-  let args = ['event'].concat(eventArgs);
-  if (!model.inited) {
-    model.earlyEvents.push(args);
-    return;
-  }
-  processMessage.apply(null, [worker].concat(args));
-}
+// unload Loaders used for creating WorkerChild instances in each process
+when(() => ppmm.broadcastAsyncMessage('sdk/loader/unload', { data: ADDON }));
--- a/addon-sdk/source/lib/sdk/context-menu.js
+++ b/addon-sdk/source/lib/sdk/context-menu.js
@@ -16,17 +16,16 @@ const { Class, mix } = require("./core/h
 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");
 const { isBrowser, getInnerId } = require("./window/utils");
 const { Ci, Cc, Cu } = require("chrome");
 const { MatchPattern } = require("./util/match-pattern");
-const { Worker } = require("./content/worker");
 const { EventTarget } = require("./event/target");
 const { emit } = require('./event/core');
 const { when } = require('./system/unload');
 const { contract: loaderContract } = require('./content/loader');
 const { omit } = require('./util/object');
 const self = require('./self')
 
 // null-out cycles in .modules to make @loader/options JSONable
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/context-menu/context.js
@@ -0,0 +1,147 @@
+/* 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 { Class } = require("../core/heritage");
+const { extend } = require("../util/object");
+const { MatchPattern } = require("../util/match-pattern");
+const readers = require("./readers");
+
+// Context class is required to implement a single `isCurrent(target)` method
+// that must return boolean value indicating weather given target matches a
+// context or not. Most context implementations below will have an associated
+// reader that way context implementation can setup a reader to extract necessary
+// information to make decision if target is matching a context.
+const Context = Class({
+  isRequired: false,
+  isCurrent(target) {
+    throw Error("Context class must implement isCurrent(target) method");
+  },
+  get required() {
+    Object.defineProperty(this, "required", {
+      value: Object.assign(Object.create(Object.getPrototypeOf(this)),
+                           this,
+                           {isRequired: true})
+    });
+    return this.required;
+  }
+});
+Context.required = function(...params) {
+  return Object.assign(new this(...params), {isRequired: true});
+};
+exports.Context = Context;
+
+
+// Next few context implementations use an associated reader to extract info
+// from the context target and story it to a private symbol associtaed with
+// a context implementation. That way name collisions are avoided while required
+// information is still carried along.
+const isPage = Symbol("context/page?")
+const PageContext = Class({
+  extends: Context,
+  read: {[isPage]: new readers.isPage()},
+  isCurrent: target => target[isPage]
+});
+exports.Page = PageContext;
+
+const isFrame = Symbol("context/frame?");
+const FrameContext = Class({
+  extends: Context,
+  read: {[isFrame]: new readers.isFrame()},
+  isCurrent: target => target[isFrame]
+});
+exports.Frame = FrameContext;
+
+const selection = Symbol("context/selection")
+const SelectionContext = Class({
+  read: {[selection]: new readers.Selection()},
+  isCurrent: target => !!target[selection]
+});
+exports.Selection = SelectionContext;
+
+const link = Symbol("context/link");
+const LinkContext = Class({
+  extends: Context,
+  read: {[link]: new readers.LinkURL()},
+  isCurrent: target => !!target[link]
+});
+exports.Link = LinkContext;
+
+const isEditable = Symbol("context/editable?")
+const EditableContext = Class({
+  extends: Context,
+  read: {[isEditable]: new readers.isEditable()},
+  isCurrent: target => target[isEditable]
+});
+exports.Editable = EditableContext;
+
+
+const mediaType = Symbol("context/mediaType")
+
+const ImageContext = Class({
+  extends: Context,
+  read: {[mediaType]: new readers.MediaType()},
+  isCurrent: target => target[mediaType] === "image"
+});
+exports.Image = ImageContext;
+
+
+const VideoContext = Class({
+  extends: Context,
+  read: {[mediaType]: new readers.MediaType()},
+  isCurrent: target => target[mediaType] === "video"
+});
+exports.Video = VideoContext;
+
+
+const AudioContext = Class({
+  extends: Context,
+  read: {[mediaType]: new readers.MediaType()},
+  isCurrent: target => target[mediaType] === "audio"
+});
+exports.Audio = AudioContext;
+
+const isSelectorMatch = Symbol("context/selector/mathches?")
+const SelectorContext = Class({
+  extends: Context,
+  initialize(selector) {
+    this.selector = selector;
+    // Each instance of selector context will need to store read
+    // data into different field, so that case with multilpe selector
+    // contexts won't cause a conflicts.
+    this[isSelectorMatch] = Symbol(selector);
+    this.read = {[this[isSelectorMatch]]: new readers.SelectorMatch(selector)};
+  },
+  isCurrent(target) {
+    return target[this[isSelectorMatch]];
+  }
+});
+exports.Selector = SelectorContext;
+
+const url = Symbol("context/url");
+const URLContext = Class({
+  extends: Context,
+  initialize(pattern) {
+    this.pattern = new MatchPattern(pattern);
+  },
+  read: {[url]: new readers.PageURL()},
+  isCurrent(target) {
+    return this.pattern.test(target[url]);
+  }
+});
+exports.URL = URLContext;
+
+var PredicateContext = Class({
+  extends: Context,
+  initialize(isMatch) {
+    if (typeof(isMatch) !== "function") {
+      throw TypeError("Predicate context mus be passed a function");
+    }
+
+    this.isMatch = isMatch
+  },
+  isCurrent(target) {
+    return this.isMatch(target);
+  }
+});
+exports.Predicate = PredicateContext;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/context-menu/core.js
@@ -0,0 +1,384 @@
+/* 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 Contexts = require("./context");
+const Readers = require("./readers");
+const Component = require("../ui/component");
+const { Class } = require("../core/heritage");
+const { map, filter, object, reduce, keys, symbols,
+        pairs, values, each, some, isEvery, count } = require("../util/sequence");
+const { loadModule } = require("framescript/manager");
+const { Cu, Cc, Ci } = require("chrome");
+const prefs = require("sdk/preferences/service");
+
+const globalMessageManager = Cc["@mozilla.org/globalmessagemanager;1"]
+                              .getService(Ci.nsIMessageListenerManager);
+const preferencesService = Cc["@mozilla.org/preferences-service;1"].
+                            getService(Ci.nsIPrefService).
+                            getBranch(null);
+
+
+const readTable = Symbol("context-menu/read-table");
+const nameTable = Symbol("context-menu/name-table");
+const onContext = Symbol("context-menu/on-context");
+const isMatching = Symbol("context-menu/matching-handler?");
+
+exports.onContext = onContext;
+exports.readTable = readTable;
+exports.nameTable = nameTable;
+
+
+const propagateOnContext = (item, data) =>
+  each(child => child[onContext](data), item.state.children);
+
+const isContextMatch = item => !item[isMatching] || item[isMatching]();
+
+// For whatever reason addWeakMessageListener does not seems to work as our
+// instance seems to dropped even though it's alive. This is simple workaround
+// to avoid dead object excetptions.
+const WeakMessageListener = function(receiver, handler="receiveMessage") {
+  this.receiver = receiver
+  this.handler = handler
+};
+WeakMessageListener.prototype = {
+  constructor: WeakMessageListener,
+  receiveMessage(message) {
+    if (Cu.isDeadWrapper(this.receiver)) {
+      message.target.messageManager.removeMessageListener(message.name, this);
+    }
+    else {
+      this.receiver[this.handler](message);
+    }
+  }
+};
+
+const OVERFLOW_THRESH = "extensions.addon-sdk.context-menu.overflowThreshold";
+const onMessage = Symbol("context-menu/message-listener");
+const onPreferceChange = Symbol("context-menu/preference-change");
+const ContextMenuExtension = Class({
+  extends: Component,
+  initialize: Component,
+  setup() {
+    const messageListener = new WeakMessageListener(this, onMessage);
+    loadModule(globalMessageManager, "framescript/context-menu", true, "onContentFrame");
+    globalMessageManager.addMessageListener("sdk/context-menu/read", messageListener);
+    globalMessageManager.addMessageListener("sdk/context-menu/readers?", messageListener);
+
+    preferencesService.addObserver(OVERFLOW_THRESH, this, false);
+  },
+  observe(_, __, name) {
+    if (name === OVERFLOW_THRESH) {
+      const overflowThreshold = prefs.get(OVERFLOW_THRESH, 10);
+      this[Component.patch]({overflowThreshold});
+    }
+  },
+  [onMessage]({name, data, target}) {
+    if (name === "sdk/context-menu/read")
+      this[onContext]({target, data});
+    if (name === "sdk/context-menu/readers?")
+      target.messageManager.sendAsyncMessage("sdk/context-menu/readers",
+                                             JSON.parse(JSON.stringify(this.state.readers)));
+  },
+  [Component.initial](options={}, children) {
+    const element = options.element || null;
+    const target = options.target || null;
+    const readers = Object.create(null);
+    const users = Object.create(null);
+    const registry = new WeakSet();
+    const overflowThreshold = prefs.get(OVERFLOW_THRESH, 10);
+
+    return { target, children: [], readers, users, element,
+             registry, overflowThreshold };
+  },
+  [Component.isUpdated](before, after) {
+    // Update only if target changed, since there is no point in re-rendering
+    // when children are. Also new items added won't be in sync with a latest
+    // context target so we should really just render before drawing context
+    // menu.
+    return before.target !== after.target;
+  },
+  [Component.render]({element, children, overflowThreshold}) {
+    if (!element) return null;
+
+    const items = children.filter(isContextMatch);
+    const body = items.length === 0 ? items :
+                 items.length < overflowThreshold ? [new Separator(),
+                                                     ...items] :
+                 [{tagName: "menu",
+                   className: "sdk-context-menu-overflow-menu",
+                   label: "Add-ons",
+                   accesskey: "A",
+                   children: [{tagName: "menupopup",
+                               children: items}]}];
+    return {
+      element: element,
+      tagName: "menugroup",
+      style: "-moz-box-orient: vertical;",
+      className: "sdk-context-menu-extension",
+      children: body
+    }
+  },
+  // Adds / remove child to it's own list.
+  add(item) {
+    this[Component.patch]({children: this.state.children.concat(item)});
+  },
+  remove(item) {
+    this[Component.patch]({
+      children: this.state.children.filter(x => x !== item)
+    });
+  },
+  register(item) {
+    const { users, registry } = this.state;
+    if (registry.has(item)) return;
+    registry.add(item);
+
+    // Each (ContextHandler) item has a readTable that is a
+    // map of keys to readers extracting them from the content.
+    // During the registraction we update intrnal record of unique
+    // readers and users per reader. Most context will have a reader
+    // shared across all instances there for map of users per reader
+    // is stored separately from the reader so that removing reader
+    // will occur only when no users remain.
+    const table = item[readTable];
+    // Context readers store data in private symbols so we need to
+    // collect both table keys and private symbols.
+    const names = [...keys(table), ...symbols(table)];
+    const readers = map(name => table[name], names);
+    // Create delta for registered readers that will be merged into
+    // internal readers table.
+    const added = filter(x => !users[x.id], readers);
+    const delta = object(...map(x => [x.id, x], added));
+
+    const update = reduce((update, reader) => {
+      const n = update[reader.id] || 0;
+      update[reader.id] = n + 1;
+      return update;
+    }, Object.assign({}, users), readers);
+
+    // Patch current state with a changes that registered item caused.
+    this[Component.patch]({users: update,
+                           readers: Object.assign(this.state.readers, delta)});
+
+    if (count(added)) {
+      globalMessageManager.broadcastAsyncMessage("sdk/context-menu/readers",
+                                                 JSON.parse(JSON.stringify(delta)));
+    }
+  },
+  unregister(item) {
+    const { users, registry } = this.state;
+    if (!registry.has(item)) return;
+    registry.delete(item);
+
+    const table = item[readTable];
+    const names = [...keys(table), ...symbols(table)];
+    const readers = map(name => table[name], names);
+    const update = reduce((update, reader) => {
+      update[reader.id] = update[reader.id] - 1;
+      return update;
+    }, Object.assign({}, users), readers);
+    const removed = filter(id => !update[id], keys(update));
+    const delta = object(...map(x => [x, null], removed));
+
+    this[Component.patch]({users: update,
+                           readers: Object.assign(this.state.readers, delta)});
+
+    if (count(removed)) {
+      globalMessageManager.broadcastAsyncMessage("sdk/context-menu/readers",
+                                                 JSON.parse(JSON.stringify(delta)));
+    }
+  },
+
+  [onContext]({data, target}) {
+    propagateOnContext(this, data);
+    const document = target.ownerDocument;
+    const element = document.getElementById("contentAreaContextMenu");
+
+    this[Component.patch]({target: data, element: element});
+  }
+});this,
+exports.ContextMenuExtension = ContextMenuExtension;
+
+// Takes an item options and
+const makeReadTable = ({context, read}) => {
+  // Result of this function is a tuple of all readers &
+  // name, reader id pairs.
+
+  // Filter down to contexts that have a reader associated.
+  const contexts = filter(context => context.read, context);
+  // Merge all contexts read maps to a single hash, note that there should be
+  // no name collisions as context implementations expect to use private
+  // symbols for storing it's read data.
+  return Object.assign({}, ...map(({read}) => read, contexts), read);
+}
+
+const readTarget = (nameTable, data) =>
+  object(...map(([name, id]) => [name, data[id]], nameTable))
+
+const ContextHandler = Class({
+  extends: Component,
+  initialize: Component,
+  get context() {
+    return this.state.options.context;
+  },
+  get read() {
+    return this.state.options.read;
+  },
+  [Component.initial](options) {
+    return {
+      table: makeReadTable(options),
+      requiredContext: filter(context => context.isRequired, options.context),
+      optionalContext: filter(context => !context.isRequired, options.context)
+    }
+  },
+  [isMatching]() {
+    const {target, requiredContext, optionalContext} = this.state;
+    return isEvery(context => context.isCurrent(target), requiredContext) &&
+            (count(optionalContext) === 0 ||
+             some(context => context.isCurrent(target), optionalContext));
+  },
+  setup() {
+    const table = makeReadTable(this.state.options);
+    this[readTable] = table;
+    this[nameTable] = [...map(symbol => [symbol, table[symbol].id], symbols(table)),
+                       ...map(name => [name, table[name].id], keys(table))];
+
+
+    contextMenu.register(this);
+
+    each(child => contextMenu.remove(child), this.state.children);
+    contextMenu.add(this);
+  },
+  dispose() {
+    contextMenu.remove(this);
+
+    each(child => contextMenu.unregister(child), this.state.children);
+    contextMenu.unregister(this);
+  },
+  // Internal `Symbol("onContext")` method is invoked when "contextmenu" event
+  // occurs in content process. Context handles with children delegate to each
+  // child and patch it's internal state to reflect new contextmenu target.
+  [onContext](data) {
+    propagateOnContext(this, data);
+    this[Component.patch]({target: readTarget(this[nameTable], data)});
+  }
+});
+const isContextHandler = item => item instanceof ContextHandler;
+
+exports.ContextHandler = ContextHandler;
+
+const Menu = Class({
+  extends: ContextHandler,
+  [isMatching]() {
+    return ContextHandler.prototype[isMatching].call(this) &&
+           this.state.children.filter(isContextHandler)
+                              .some(isContextMatch);
+  },
+  [Component.render]({children, options}) {
+    const items = children.filter(isContextMatch);
+    return {tagName: "menu",
+            className: "sdk-context-menu menu-iconic",
+            label: options.label,
+            accesskey: options.accesskey,
+            image: options.icon,
+            children: [{tagName: "menupopup",
+                        children: items}]};
+  }
+});
+exports.Menu = Menu;
+
+const onCommand = Symbol("context-menu/item/onCommand");
+const Item = Class({
+  extends: ContextHandler,
+  get onClick() {
+    return this.state.options.onClick;
+  },
+  [Component.render]({options}) {
+    const {label, icon, accesskey} = options;
+    return {tagName: "menuitem",
+            className: "sdk-context-menu-item menuitem-iconic",
+            label,
+            accesskey,
+            image: icon,
+            oncommand: this};
+  },
+  handleEvent(event) {
+    if (this.onClick)
+      this.onClick(this.state.target);
+  }
+});
+exports.Item = Item;
+
+var Separator = Class({
+  extends: Component,
+  initialize: Component,
+  [Component.render]() {
+    return {tagName: "menuseparator",
+            className: "sdk-context-menu-separator"}
+  },
+  [onContext]() {
+
+  }
+});
+exports.Separator = Separator;
+
+exports.Contexts = Contexts;
+exports.Readers = Readers;
+
+const createElement = (vnode, {document}) => {
+   const node = vnode.namespace ?
+              document.createElementNS(vnode.namespace, vnode.tagName) :
+              document.createElement(vnode.tagName);
+
+   node.setAttribute("data-component-path", vnode[Component.path]);
+
+   each(([key, value]) => {
+     if (key === "tagName") {
+       return;
+     }
+     if (key === "children") {
+       return;
+     }
+
+     if (key.startsWith("on")) {
+       node.addEventListener(key.substr(2), value)
+       return;
+     }
+
+     if (typeof(value) !== "object" &&
+         typeof(value) !== "function" &&
+         value !== void(0) &&
+         value !== null)
+    {
+       if (key === "className") {
+         node[key] = value;
+       }
+       else {
+         node.setAttribute(key, value);
+       }
+       return;
+     }
+   }, pairs(vnode));
+
+  each(child => node.appendChild(createElement(child, {document})), vnode.children);
+  return node;
+};
+
+const htmlWriter = tree => {
+  if (tree !== null) {
+    const root = tree.element;
+    const node = createElement(tree, {document: root.ownerDocument});
+    const before = root.querySelector("[data-component-path='/']");
+    if (before) {
+      root.replaceChild(node, before);
+    } else {
+      root.appendChild(node);
+    }
+  }
+};
+
+
+const contextMenu = ContextMenuExtension();
+exports.contextMenu = contextMenu;
+Component.mount(contextMenu, htmlWriter);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/context-menu/readers.js
@@ -0,0 +1,112 @@
+/* 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 { Class } = require("../core/heritage");
+const { extend } = require("../util/object");
+const { memoize, method, identity } = require("../lang/functional");
+
+const serializeCategory = ({type}) => ({ category: `reader/${type}()` });
+
+const Reader = Class({
+  initialize() {
+    this.id = `reader/${this.type}()`
+  },
+  toJSON() {
+    return serializeCategory(this);
+  }
+});
+
+
+const MediaTypeReader = Class({ extends: Reader, type: "MediaType" });
+exports.MediaType = MediaTypeReader;
+
+const LinkURLReader = Class({ extends: Reader, type: "LinkURL" });
+exports.LinkURL = LinkURLReader;
+
+const SelectionReader = Class({ extends: Reader, type: "Selection" });
+exports.Selection = SelectionReader;
+
+const isPageReader = Class({ extends: Reader, type: "isPage" });
+exports.isPage = isPageReader;
+
+const isFrameReader = Class({ extends: Reader, type: "isFrame" });
+exports.isFrame = isFrameReader;
+
+const isEditable = Class({ extends: Reader, type: "isEditable"});
+exports.isEditable = isEditable;
+
+
+
+const ParameterizedReader = Class({
+  extends: Reader,
+  readParameter: function(value) {
+    return value;
+  },
+  toJSON: function() {
+    var json = serializeCategory(this);
+    json[this.parameter] = this[this.parameter];
+    return json;
+  },
+  initialize(...params) {
+    if (params.length) {
+      this[this.parameter] = this.readParameter(...params);
+    }
+    this.id = `reader/${this.type}(${JSON.stringify(this[this.parameter])})`;
+  }
+});
+exports.ParameterizedReader = ParameterizedReader;
+
+
+const QueryReader = Class({
+  extends: ParameterizedReader,
+  type: "Query",
+  parameter: "path"
+});
+exports.Query = QueryReader;
+
+
+const AttributeReader = Class({
+  extends: ParameterizedReader,
+  type: "Attribute",
+  parameter: "name"
+});
+exports.Attribute = AttributeReader;
+
+const SrcURLReader = Class({
+  extends: AttributeReader,
+  name: "src",
+});
+exports.SrcURL = SrcURLReader;
+
+const PageURLReader = Class({
+  extends: QueryReader,
+  path: "ownerDocument.URL",
+});
+exports.PageURL = PageURLReader;
+
+const SelectorMatchReader = Class({
+  extends: ParameterizedReader,
+  type: "SelectorMatch",
+  parameter: "selector"
+});
+exports.SelectorMatch = SelectorMatchReader;
+
+const extractors = new WeakMap();
+extractors.id = 0;
+
+
+var Extractor = Class({
+  extends: ParameterizedReader,
+  type: "Extractor",
+  parameter: "source",
+  initialize: function(f) {
+    this[this.parameter] = String(f);
+    if (!extractors.has(f)) {
+      extractors.id = extractors.id + 1;
+      extractors.set(f, extractors.id);
+    }
+
+    this.id = `reader/${this.type}.for(${extractors.get(f)})`
+  }
+});
+exports.Extractor = Extractor;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/context-menu@2.js
@@ -0,0 +1,32 @@
+/* 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 shared = require("toolkit/require");
+const { Item, Separator, Menu, Contexts, Readers } = shared.require("sdk/context-menu/core");
+const { setupDisposable, disposeDisposable, Disposable } = require("sdk/core/disposable")
+const { Class } = require("sdk/core/heritage")
+
+const makeDisposable = Type => Class({
+  extends: Type,
+  implements: [Disposable],
+  initialize: Type.prototype.initialize,
+  setup(...params) {
+    Type.prototype.setup.call(this, ...params);
+    setupDisposable(this);
+  },
+  dispose(...params) {
+    disposeDisposable(this);
+    Type.prototype.dispose.call(this, ...params);
+  }
+});
+
+exports.Separator = Separator;
+exports.Contexts = Contexts;
+exports.Readers = Readers;
+
+// Subclass Item & Menu shared classes so their items
+// will be unloaded when add-on is unloaded.
+exports.Item = makeDisposable(Item);
+exports.Menu = makeDisposable(Menu);
--- a/addon-sdk/source/lib/sdk/core/disposable.js
+++ b/addon-sdk/source/lib/sdk/core/disposable.js
@@ -47,21 +47,23 @@ const setup = method("disposable/setup")
 exports.setup = setup;
 setup.define(Object, (object, ...args) => object.setup(...args));
 
 
 // Set's up disposable instance.
 const setupDisposable = disposable => {
   subscribe(disposable, addonUnloadTopic, isWeak(disposable));
 };
+exports.setupDisposable = setupDisposable;
 
 // Tears down disposable instance.
 const disposeDisposable = disposable => {
   unsubscribe(disposable, addonUnloadTopic);
 };
+exports.disposeDisposable = disposeDisposable;
 
 // Base type that takes care of disposing it's instances on add-on unload.
 // Also makes sure to remove unload listener if it's already being disposed.
 const Disposable = Class({
   implements: [Observer],
   initialize: function(...args) {
     // First setup instance before initializing it's disposal. If instance
     // fails to initialize then there is no instance to be disposed at the
@@ -124,9 +126,8 @@ downgrade.define(Disposable, dispose);
 upgrade.define(Disposable, dispose);
 uninstall.define(Disposable, dispose);
 
 // If application is shut down no dispose is invoked as undo-ing
 // changes made by instance is likely to just waste of resources &
 // increase shutdown time. Although specefic components may choose
 // to implement shutdown handler that does something better.
 shutdown.define(Disposable, disposable => {});
-
--- a/addon-sdk/source/lib/sdk/core/observer.js
+++ b/addon-sdk/source/lib/sdk/core/observer.js
@@ -36,20 +36,19 @@ exports.unsubscribe = unsubscribe;
 // This is wrapper class that takes a `delegate` and produces
 // instance of `nsIObserver` which will delegate to a given
 // object when observer notification occurs.
 const ObserverDelegee = Class({
   initialize: function(delegate) {
     this.delegate = delegate;
   },
   QueryInterface: function(iid) {
-    const isObserver = iid.equals(Ci.nsIObserver);
-    const isWeakReference = iid.equals(Ci.nsISupportsWeakReference);
-
-    if (!isObserver && !isWeakReference)
+    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) {
     observe(this.delegate, subject, topic, data);
   }
 });
--- a/addon-sdk/source/lib/sdk/deprecated/cortex.js
+++ b/addon-sdk/source/lib/sdk/deprecated/cortex.js
@@ -1,12 +1,11 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
 "use strict";
 
 module.metadata = {
   "stability": "deprecated"
 };
 
 const getOwnIdentifiers = x => [...Object.getOwnPropertyNames(x),
                                 ...Object.getOwnPropertySymbols(x)];
--- a/addon-sdk/source/lib/sdk/deprecated/events.js
+++ b/addon-sdk/source/lib/sdk/deprecated/events.js
@@ -1,12 +1,11 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
 "use strict";
 
 module.metadata = {
   "stability": "deprecated"
 };
 
 const ERROR_TYPE = 'error',
       UNCAUGHT_ERROR = 'An error event was dispatched for which there was'
--- a/addon-sdk/source/lib/sdk/deprecated/events/assembler.js
+++ b/addon-sdk/source/lib/sdk/deprecated/events/assembler.js
@@ -1,39 +1,43 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { Trait } = require("../light-traits");
+const { Class } = require("../../core/heritage");
 const { removeListener, on } = require("../../dom/events");
 
 /**
  * Trait may be used for building objects / composing traits that wish to handle
  * multiple dom events from multiple event targets in one place. Event targets
  * can be added / removed by calling `observe / ignore` methods. Composer should
  * provide array of event types it wishes to handle as property
  * `supportedEventsTypes` and function for handling all those events as
  * `handleEvent` property.
  */
-exports.DOMEventAssembler = Trait({
+exports.DOMEventAssembler = Class({
   /**
    * Function that is supposed to handle all the supported events (that are
    * present in the `supportedEventsTypes`) from all the observed
    * `eventTargets`.
    * @param {Event} event
    *    Event being dispatched.
    */
-  handleEvent: Trait.required,
+  handleEvent() {
+    throw new TypeError("Instance of DOMEventAssembler must implement `handleEvent` method");
+  },
   /**
    * Array of supported event names.
    * @type {String[]}
    */
-  supportedEventsTypes: Trait.required,
+  get supportedEventsTypes() {
+    throw new TypeError("Instance of DOMEventAssembler must implement `handleEvent` field");
+  },
   /**
    * Adds `eventTarget` to the list of observed `eventTarget`s. Listeners for
    * supported events will be registered on the given `eventTarget`.
    * @param {EventTarget} eventTarget
    */
   observe: function observe(eventTarget) {
     this.supportedEventsTypes.forEach(function(eventType) {
       on(eventTarget, eventType, this);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/deprecated/sync-worker.js
@@ -0,0 +1,297 @@
+/* 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/. */
+
+/**
+ *
+ * `deprecated/sync-worker` was previously `content/worker`, that was
+ * incompatible with e10s. we are in the process of switching to the new
+ * asynchronous `Worker`, which behaves slightly differently in some edge
+ * cases, so we are keeping this one around for a short period.
+ * try to switch to the new one as soon as possible..
+ *
+ */
+
+"use strict";
+
+module.metadata = {
+  "stability": "unstable"
+};
+
+const { Class } = require('../core/heritage');
+const { EventTarget } = require('../event/target');
+const { on, off, emit, setListeners } = require('../event/core');
+const {
+  attach, detach, destroy
+} = require('../content/utils');
+const { method } = require('../lang/functional');
+const { Ci, Cu, Cc } = require('chrome');
+const unload = require('../system/unload');
+const events = require('../system/events');
+const { getInnerId } = require("../window/utils");
+const { WorkerSandbox } = require('../content/sandbox');
+const { getTabForWindow } = require('../tabs/helpers');
+const { isPrivate } = require('../private-browsing/utils');
+
+// A weak map of workers to hold private attributes that
+// should not be exposed
+const workers = new WeakMap();
+
+let modelFor = (worker) => workers.get(worker);
+
+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.";
+
+const ERR_FROZEN = "The page is currently hidden and can no longer be used " +
+                   "until it is visible again.";
+
+/**
+ * Message-passing facility for communication between code running
+ * in the content and add-on process.
+ * @see https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/content_worker
+ */
+const Worker = Class({
+  implements: [EventTarget],
+  initialize: function WorkerConstructor (options) {
+    // Save model in weak map to not expose properties
+    let model = createModel();
+    workers.set(this, model);
+
+    options = options || {};
+
+    if ('contentScriptFile' in options)
+      this.contentScriptFile = options.contentScriptFile;
+    if ('contentScriptOptions' in options)
+      this.contentScriptOptions = options.contentScriptOptions;
+    if ('contentScript' in options)
+      this.contentScript = options.contentScript;
+    if ('injectInDocument' in options)
+      this.injectInDocument = !!options.injectInDocument;
+
+    setListeners(this, options);
+
+    unload.ensure(this, "destroy");
+
+    // Ensure that worker.port is initialized for contentWorker to be able
+    // to send events during worker initialization.
+    this.port = createPort(this);
+
+    model.documentUnload = documentUnload.bind(this);
+    model.pageShow = pageShow.bind(this);
+    model.pageHide = pageHide.bind(this);
+
+    if ('window' in options)
+      attach(this, options.window);
+  },
+
+  /**
+   * Sends a message to the worker's global scope. Method takes single
+   * argument, which represents data to be sent to the worker. The data may
+   * be any primitive type value or `JSON`. Call of this method asynchronously
+   * emits `message` event with data value in the global scope of this
+   * symbiont.
+   *
+   * `message` event listeners can be set either by calling
+   * `self.on` with a first argument string `"message"` or by
+   * implementing `onMessage` function in the global scope of this worker.
+   * @param {Number|String|JSON} data
+   */
+  postMessage: function (...data) {
+    let model = modelFor(this);
+    let args = ['message'].concat(data);
+    if (!model.inited) {
+      model.earlyEvents.push(args);
+      return;
+    }
+    processMessage.apply(null, [this].concat(args));
+  },
+
+  get url () {
+    let model = modelFor(this);
+    // model.window will be null after detach
+    return model.window ? model.window.document.location.href : null;
+  },
+
+  get contentURL () {
+    let model = modelFor(this);
+    return model.window ? model.window.document.URL : null;
+  },
+
+  get tab () {
+    let model = modelFor(this);
+    // model.window will be null after detach
+    if (model.window)
+      return getTabForWindow(model.window);
+    return null;
+  },
+
+  // Implemented to provide some of the previous features of exposing sandbox
+  // so that Worker can be extended
+  getSandbox: function () {
+    return modelFor(this).contentWorker;
+  },
+
+  toString: function () { return '[object Worker]'; },
+  attach: method(attach),
+  detach: method(detach),
+  destroy: method(destroy)
+});
+exports.Worker = Worker;
+
+attach.define(Worker, function (worker, window) {
+  let model = modelFor(worker);
+  model.window = window;
+  // Track document unload to destroy this worker.
+  // We can't watch for unload event on page's window object as it
+  // prevents bfcache from working:
+  // https://developer.mozilla.org/En/Working_with_BFCache
+  model.windowID = getInnerId(model.window);
+  events.on("inner-window-destroyed", model.documentUnload);
+
+  // will set model.contentWorker pointing to the private API:
+  model.contentWorker = WorkerSandbox(worker, model.window);
+
+  // Listen to pagehide event in order to freeze the content script
+  // while the document is frozen in bfcache:
+  model.window.addEventListener("pageshow", model.pageShow, true);
+  model.window.addEventListener("pagehide", model.pageHide, true);
+
+  // Mainly enable worker.port.emit to send event to the content worker
+  model.inited = true;
+  model.frozen = false;
+
+  // Fire off `attach` event
+  emit(worker, 'attach', window);
+
+  // Process all events and messages that were fired before the
+  // worker was initialized.
+  model.earlyEvents.forEach(args => processMessage.apply(null, [worker].concat(args)));
+});
+
+/**
+ * Remove all internal references to the attached document
+ * Tells _port to unload itself and removes all the references from itself.
+ */
+detach.define(Worker, function (worker, reason) {
+  let model = modelFor(worker);
+
+  // maybe unloaded before content side is created
+  if (model.contentWorker) {
+    model.contentWorker.destroy(reason);
+  }
+
+  model.contentWorker = null;
+  if (model.window) {
+    model.window.removeEventListener("pageshow", model.pageShow, true);
+    model.window.removeEventListener("pagehide", model.pageHide, true);
+  }
+  model.window = null;
+  // This method may be called multiple times,
+  // avoid dispatching `detach` event more than once
+  if (model.windowID) {
+    model.windowID = null;
+    events.off("inner-window-destroyed", model.documentUnload);
+    model.earlyEvents.length = 0;
+    emit(worker, 'detach');
+  }
+  model.inited = false;
+});
+
+isPrivate.define(Worker, ({ tab }) => isPrivate(tab));
+
+/**
+ * Tells content worker to unload itself and
+ * removes all the references from itself.
+ */
+destroy.define(Worker, function (worker, reason) {
+  detach(worker, reason);
+  modelFor(worker).inited = true;
+  // Specifying no type or listener removes all listeners
+  // from target
+  off(worker);
+  off(worker.port);
+});
+
+/**
+ * Events fired by workers
+ */
+function documentUnload ({ subject, data }) {
+  let model = modelFor(this);
+  let innerWinID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
+  if (innerWinID != model.windowID) return false;
+  detach(this);
+  return true;
+}
+
+function pageShow () {
+  let model = modelFor(this);
+  model.contentWorker.emitSync('pageshow');
+  emit(this, 'pageshow');
+  model.frozen = false;
+}
+
+function pageHide () {
+  let model = modelFor(this);
+  model.contentWorker.emitSync('pagehide');
+  emit(this, 'pagehide');
+  model.frozen = true;
+}
+
+/**
+ * Fired from postMessage and emitEventToContent, or from the earlyMessage
+ * queue when fired before the content is loaded. Sends arguments to
+ * contentWorker if able
+ */
+
+function processMessage (worker, ...args) {
+  let model = modelFor(worker) || {};
+  if (!model.contentWorker)
+    throw new Error(ERR_DESTROYED);
+  if (model.frozen)
+    throw new Error(ERR_FROZEN);
+  model.contentWorker.emit.apply(null, args);
+}
+
+function createModel () {
+  return {
+    // List of messages fired before worker is initialized
+    earlyEvents: [],
+    // Is worker connected to the content worker sandbox ?
+    inited: false,
+    // Is worker being frozen? i.e related document is frozen in bfcache.
+    // Content script should not be reachable if frozen.
+    frozen: true,
+    /**
+     * Reference to the content side of the worker.
+     * @type {WorkerGlobalScope}
+     */
+    contentWorker: null,
+    /**
+     * Reference to the window that is accessible from
+     * the content scripts.
+     * @type {Object}
+     */
+    window: null
+  };
+}
+
+function createPort (worker) {
+  let port = EventTarget();
+  port.emit = emitEventToContent.bind(null, worker);
+  return port;
+}
+
+/**
+ * Emit a custom event to the content script,
+ * i.e. emit this event on `self.port`
+ */
+function emitEventToContent (worker, ...eventArgs) {
+  let model = modelFor(worker);
+  let args = ['event'].concat(eventArgs);
+  if (!model.inited) {
+    model.earlyEvents.push(args);
+    return;
+  }
+  processMessage.apply(null, [worker].concat(args));
+}
--- a/addon-sdk/source/lib/sdk/deprecated/traits-worker.js
+++ b/addon-sdk/source/lib/sdk/deprecated/traits-worker.js
@@ -233,17 +233,18 @@ const WorkerSandbox = EventEmitter.compo
       }
     });
 
     // Inject `addon` global into target document if document is trusted,
     // `addon` in document is equivalent to `self` in content script.
     if (worker._injectInDocument) {
       let win = window.wrappedJSObject ? window.wrappedJSObject : window;
       Object.defineProperty(win, "addon", {
-          value: content.self
+          value: content.self,
+          configurable: true
         }
       );
     }
 
     // Inject our `console` into target document if worker doesn't have a tab
     // (e.g Panel, PageWorker, Widget).
     // `worker.tab` can't be used because bug 804935.
     if (!getTabForContentWindow(window)) {
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test.js
@@ -5,17 +5,17 @@
 
 module.metadata = {
   "stability": "deprecated"
 };
 
 const memory = require("./memory");
 const timer = require("../timers");
 const cfxArgs = require("../test/options");
-const { getTabs, closeTab, getURI } = require("../tabs/utils");
+const { getTabs, closeTab, getURI, getTabId, getSelectedTab } = require("../tabs/utils");
 const { windows, isBrowser, getMostRecentBrowserWindow } = require("../window/utils");
 const { defer, all, Debugging: PromiseDebugging, resolve } = require("../core/promise");
 const { getInnerId } = require("../window/utils");
 const { cleanUI } = require("../test/utils")
 
 const findAndRunTests = function findAndRunTests(options) {
   var TestFinder = require("./unit-test-finder").TestFinder;
   var finder = new TestFinder({
@@ -30,20 +30,26 @@ const findAndRunTests = function findAnd
       stopOnError: options.stopOnError,
       onDone: options.onDone
     });
   });
 };
 exports.findAndRunTests = findAndRunTests;
 
 let runnerWindows = new WeakMap();
+let runnerTabs = new WeakMap();
 
 const TestRunner = function TestRunner(options) {
   options = options || {};
-  runnerWindows.set(this, getInnerId(getMostRecentBrowserWindow()));
+
+  // remember the id's for the open window and tab
+  let window = getMostRecentBrowserWindow();
+  runnerWindows.set(this, getInnerId(window));
+  runnerTabs.set(this, getTabId(getSelectedTab(window)));
+
   this.fs = options.fs;
   this.console = options.console || console;
   memory.track(this);
   this.passed = 0;
   this.failed = 0;
   this.testRunSummary = [];
   this.expectFailNesting = 0;
   this.done = TestRunner.prototype.done.bind(this);
@@ -313,25 +319,36 @@ TestRunner.prototype = {
       return promise;
     });
 
     PromiseDebugging.flushUncaughtErrors();
 
     return all(winPromises).then(() => {
       let browserWins = wins.filter(isBrowser);
       let tabs = browserWins.reduce((tabs, window) => tabs.concat(getTabs(window)), []);
-
-      if (wins.length != 1 || getInnerId(wins[0]) !== runnerWindows.get(this))
-        this.fail("Should not be any unexpected windows open");
-
+      let newTabID = getTabId(getSelectedTab(wins[0]));
+      let oldTabID = runnerTabs.get(this);
       let hasMoreTabsOpen = browserWins.length && tabs.length != 1;
-      if (hasMoreTabsOpen)
+      let failure = false;
+
+      if (wins.length != 1 || getInnerId(wins[0]) !== runnerWindows.get(this)) {
+        failure = true;
+        this.fail("Should not be any unexpected windows open");
+      }
+      else if (hasMoreTabsOpen) {
+        failure = true;
         this.fail("Should not be any unexpected tabs open");
+      }
+      else if (oldTabID != newTabID) {
+        failure = true;
+        runnerTabs.set(this, newTabID);
+        this.fail("Should not be any new tabs left open, old id: " + oldTabID + " new id: " + newTabID);
+      }
 
-      if (hasMoreTabsOpen || wins.length != 1) {
+      if (failure) {
         console.log("Windows open:");
         for (let win of wins) {
           if (isBrowser(win)) {
             tabs = getTabs(win);
             console.log(win.location + " - " + tabs.map(getURI).join(", "));
           }
           else {
             console.log(win.location);
@@ -351,17 +368,17 @@ TestRunner.prototype = {
       });
 
       if (this.onDone !== null) {
         let onDone = this.onDone;
         this.onDone = null;
         timer.setTimeout(_ => onDone(this));
       }
     }).
-    catch(e => console.exception(e));
+    catch(console.exception);
   },
 
   // Set of assertion functions to wait for an assertion to become true
   // These functions take the same arguments as the TestRunner.assert* methods.
   waitUntil: function waitUntil() {
     return this._waitUntil(this.assert, arguments);
   },
 
--- a/addon-sdk/source/lib/sdk/io/data.js
+++ b/addon-sdk/source/lib/sdk/io/data.js
@@ -10,16 +10,17 @@ module.metadata = {
 
 const { Cc, Ci, Cu } = require("chrome");
 const base64 = require("../base64");
 const IOService = Cc["@mozilla.org/network/io-service;1"].
   getService(Ci.nsIIOService);
 
 const { deprecateFunction } = require('../util/deprecate');
 const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm");
+const { Services } = Cu.import("resource://gre/modules/Services.jsm");
 const FaviconService = Cc["@mozilla.org/browser/favicon-service;1"].
                           getService(Ci.nsIFaviconService);
 
 const PNG_B64 = "data:image/png;base64,";
 const DEF_FAVICON_URI = "chrome://mozapps/skin/places/defaultFavicon.png";
 let   DEF_FAVICON = null;
 
 /**
@@ -46,17 +47,24 @@ function getFaviconURIForLocation(uri) {
 exports.getFaviconURIForLocation = getFaviconURIForLocation;
 
 /**
  * Takes chrome URI and returns content under that URI.
  * @param {String} chromeURI
  * @returns {String}
  */
 function getChromeURIContent(chromeURI) {
-  let channel = IOService.newChannel(chromeURI, null, null);
+  let channel = IOService.newChannel2(chromeURI,
+                                      null,
+                                      null,
+                                      null,      // aLoadingNode
+                                      Services.scriptSecurityManager.getSystemPrincipal(),
+                                      null,      // aTriggeringPrincipal
+                                      Ci.nsILoadInfo.SEC_NORMAL,
+                                      Ci.nsIContentPolicy.TYPE_OTHER);
   let input = channel.open();
   let stream = Cc["@mozilla.org/binaryinputstream;1"].
                 createInstance(Ci.nsIBinaryInputStream);
   stream.setInputStream(input);
   let content = stream.readBytes(input.available());
   stream.close();
   input.close();
   return content;
--- a/addon-sdk/source/lib/sdk/io/stream.js
+++ b/addon-sdk/source/lib/sdk/io/stream.js
@@ -187,18 +187,20 @@ const InputStream = Class({
   },
   pause: function pause() {
     this.paused = true;
     nsIInputStreamPump(this).suspend();
     emit(this, "paused");
   },
   resume: function resume() {
     this.paused = false;
-    nsIInputStreamPump(this).resume();
-    emit(this, "resume");
+    if (nsIInputStreamPump(this).isPending()) {
+        nsIInputStreamPump(this).resume();
+        emit(this, "resume");
+    }
   },
   close: function close() {
     this.readable = false;
     nsIInputStreamPump(this).cancel(Cr.NS_OK);
     nsIBinaryInputStream(this).close();
     nsIAsyncInputStream(this).close();
   },
   destroy: function destroy() {
--- a/addon-sdk/source/lib/sdk/keyboard/observer.js
+++ b/addon-sdk/source/lib/sdk/keyboard/observer.js
@@ -3,55 +3,55 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-const { Trait } = require("../deprecated/light-traits");
-const { EventEmitterTrait: EventEmitter } = require("../deprecated/events");
+const { Class } = require("../core/heritage");
+const { EventTarget } = require("../event/target");
+const { emit } = require("../event/core");
 const { DOMEventAssembler } = require("../deprecated/events/assembler");
 const { browserWindowIterator } = require('../deprecated/window-utils');
 const { isBrowser } = require('../window/utils');
 const { observer: windowObserver } = require("../windows/observer");
 
 // Event emitter objects used to register listeners and emit events on them
 // when they occur.
-const observer = Trait.compose(DOMEventAssembler, EventEmitter).create({
-  /**
-   * Method is implemented by `EventEmitter` and is used just for emitting
-   * events on registered listeners.
-   */
-  _emit: Trait.required,
+const Observer = Class({
+  implements: [DOMEventAssembler, EventTarget],
+  initialize() {
+    // Adding each opened window to a list of observed windows.
+    windowObserver.on("open", window => {
+      if (isBrowser(window))
+        this.observe(window);
+    });
+
+    // Removing each closed window form the list of observed windows.
+    windowObserver.on("close", window => {
+      if (isBrowser(window))
+        this.ignore(window);
+    });
+
+    // Making observer aware of already opened windows.
+    for (let window of browserWindowIterator()) {
+      this.observe(window);
+    }
+  },
   /**
    * Events that are supported and emitted by the module.
    */
   supportedEventsTypes: [ "keydown", "keyup", "keypress" ],
   /**
    * Function handles all the supported events on all the windows that are
    * observed. Method is used to proxy events to the listeners registered on
    * this event emitter.
    * @param {Event} event
    *    Keyboard event being emitted.
    */
-  handleEvent: function handleEvent(event) {
-    this._emit(event.type, event, event.target.ownerDocument.defaultView);
+  handleEvent(event) {
+    emit(this, event.type, event, event.target.ownerDocument.defaultView);
   }
 });
 
-// Adding each opened window to a list of observed windows.
-windowObserver.on("open", function onOpen(window) {
-  if (isBrowser(window))
-    observer.observe(window);
-});
-// Removing each closed window form the list of observed windows.
-windowObserver.on("close", function onClose(window) {
-  if (isBrowser(window))
-    observer.ignore(window);
-});
-
-// Making observer aware of already opened windows.
-for (let window of browserWindowIterator())
-  observer.observe(window);
-
-exports.observer = observer;
+exports.observer = new Observer();
--- a/addon-sdk/source/lib/sdk/l10n/locale.js
+++ b/addon-sdk/source/lib/sdk/l10n/locale.js
@@ -45,17 +45,18 @@ function getPreferedLocales(caseSensitve
   // `general.useragent.locale` is a special 'localized' value, like:
   // "chrome://global/locale/intl.properties"
   let browserUiLocale = prefs.getLocalized(PREF_SELECTED_LOCALE, "") ||
                         prefs.get(PREF_SELECTED_LOCALE, "");
   if (browserUiLocale)
     addLocale(browserUiLocale);
 
   // Third priority is the list of locales used for web content
-  let contentLocales = prefs.get(PREF_ACCEPT_LANGUAGES, "");
+  let contentLocales = prefs.getLocalized(PREF_ACCEPT_LANGUAGES, "") ||
+                       prefs.get(PREF_ACCEPT_LANGUAGES, "");
   if (contentLocales) {
     // This list is a string of locales seperated by commas.
     // There is spaces after commas, so strip each item
     for (let locale of contentLocales.split(","))
       addLocale(locale.replace(/(^\s+)|(\s+$)/g, ""));
   }
 
   // Finally, we ensure that en-US is the final fallback if it wasn't added
--- a/addon-sdk/source/lib/sdk/lang/type.js
+++ b/addon-sdk/source/lib/sdk/lang/type.js
@@ -103,16 +103,28 @@ exports.isFunction = isFunction;
  *    isObject(null) // false
  */
 function isObject(value) {
     return typeof value === "object" && value !== null;
 }
 exports.isObject = isObject;
 
 /**
+ * Detect whether a value is a generator.
+ *
+ * @param aValue
+ *        The value to identify.
+ * @return A boolean indicating whether the value is a generator.
+ */
+function isGenerator(aValue) {
+  return !!(aValue && aValue.isGenerator && aValue.isGenerator());
+}
+exports.isGenerator = isGenerator;
+
+/**
  * Returns true if `value` is an Array.
  * @examples
  *    isArray([1, 2, 3])  // true
  *    isArray({ 0: 'foo', length: 1 }) // false
  */
 var isArray = Array.isArray || function isArray(value) {
   Object.prototype.toString.call(value) === "[object Array]";
 }
--- a/addon-sdk/source/lib/sdk/loader/cuddlefish.js
+++ b/addon-sdk/source/lib/sdk/loader/cuddlefish.js
@@ -1,102 +1,52 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-'use strict';
+"use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
 // This module is manually loaded by bootstrap.js in a sandbox and immediatly
 // put in module cache so that it is never loaded in any other way.
 
 /* Workarounds to include dependencies in the manifest
 require('chrome')                  // Otherwise CFX will complain about Components
 require('toolkit/loader')          // Otherwise CFX will stip out loader.js
 require('sdk/addon/runner')        // Otherwise CFX will stip out addon/runner.js
-require('sdk/system/xul-app')      // Otherwise CFX will stip out sdk/system/xul-app
 */
 
 const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
 
 // `loadSandbox` is exposed by bootstrap.js
 const loaderURI = module.uri.replace("sdk/loader/cuddlefish.js",
                                      "toolkit/loader.js");
 const xulappURI = module.uri.replace("loader/cuddlefish.js",
-                                     "system/xul-app.js");
+                                     "system/xul-app.jsm");
 // We need to keep a reference to the sandbox in order to unload it in
 // bootstrap.js
 
 const loaderSandbox = loadSandbox(loaderURI);
 const loaderModule = loaderSandbox.exports;
 
-const xulappSandbox = loadSandbox(xulappURI);
-const xulappModule = xulappSandbox.exports;
+const { incompatibility } = Cu.import(xulappURI, {}).XulApp;
 
 const { override, load } = loaderModule;
 
-/**
- * Ensure the current application satisfied the requirements specified in the
- * module given. If not, an exception related to the incompatibility is
- * returned; `null` otherwise.
- *
- * @param {Object} module
- *  The module to check
- * @returns {Error}
- */
-function incompatibility(module) {
-  let { metadata, id } = module;
-
-  // if metadata or engines are not specified we assume compatibility is not
-  // an issue.
-  if (!metadata || !("engines" in metadata))
-    return null;
-
-  let { engines } = metadata;
-
-  if (engines === null || typeof(engines) !== "object")
-    return new Error("Malformed engines' property in metadata");
-
-  let applications = Object.keys(engines);
-
-  let versionRange;
-  applications.forEach(function(name) {
-    if (xulappModule.is(name)) {
-      versionRange = engines[name];
-      // Continue iteration. We want to ensure the module doesn't
-      // contain a typo in the applications' name or some unknown
-      // application - `is` function throws an exception in that case.
-    }
-  });
-
-  if (typeof(versionRange) === "string") {
-    if (xulappModule.satisfiesVersion(versionRange))
-      return null;
-
-    return new Error("Unsupported Application version: The module " + id +
-            " currently supports only version " + versionRange + " of " +
-            xulappModule.name + ".");
-  }
-
-  return new Error("Unsupported Application: The module " + id +
-            " currently supports only " + applications.join(", ") + ".")
-}
-
 function CuddlefishLoader(options) {
   let { manifest } = options;
 
   options = override(options, {
     // Put `api-utils/loader` and `api-utils/cuddlefish` loaded as JSM to module
     // cache to avoid subsequent loads via `require`.
     modules: override({
       'toolkit/loader': loaderModule,
-      'sdk/loader/cuddlefish': exports,
-      'sdk/system/xul-app': xulappModule
+      'sdk/loader/cuddlefish': exports
     }, options.modules),
     resolve: function resolve(id, requirer) {
       let entry = requirer && requirer in manifest && manifest[requirer];
       let uri = null;
 
       // If manifest entry for this requirement is present we follow manifest.
       // Note: Standard library modules like 'panel' will be present in
       // manifest unless they were moved to platform.
--- a/addon-sdk/source/lib/sdk/net/url.js
+++ b/addon-sdk/source/lib/sdk/net/url.js
@@ -3,21 +3,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
-const { Cu, components } = require("chrome");
+const { Ci, Cu, components } = require("chrome");
+
 const { defer } = require("../core/promise");
 const { merge } = require("../util/object");
 
 const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 /**
  * Reads a URI and returns a promise.
  *
  * @param uri {string} The URI to read
  * @param [options] {object} This parameter can have any or all of the following
  * fields: `charset`. By default the `charset` is set to 'UTF-8'.
  *
@@ -28,22 +30,29 @@ const { NetUtil } = Cu.import("resource:
  *  let promise = readURI('resource://gre/modules/NetUtil.jsm', {
  *    charset: 'US-ASCII'
  *  });
  */
 function readURI(uri, options) {
   options = options || {};
   let charset = options.charset || 'UTF-8';
 
-  let channel = NetUtil.newChannel(uri, charset, null);
+  let channel = NetUtil.newChannel2(uri,
+                                    charset,
+                                    null,
+                                    null,      // aLoadingNode
+                                    Services.scriptSecurityManager.getSystemPrincipal(),
+                                    null,      // aTriggeringPrincipal
+                                    Ci.nsILoadInfo.SEC_NORMAL,
+                                    Ci.nsIContentPolicy.TYPE_OTHER);
 
   let { promise, resolve, reject } = defer();
 
   try {
-    NetUtil.asyncFetch(channel, function (stream, result) {
+    NetUtil.asyncFetch2(channel, function (stream, result) {
       if (components.isSuccessCode(result)) {
         let count = stream.available();
         let data = NetUtil.readInputStreamToString(stream, count, { charset : charset });
 
         resolve(data);
       } else {
         reject("Failed to read: '" + uri + "' (Error Code: " + result + ")");
       }
@@ -69,17 +78,24 @@ exports.readURI = readURI;
  * @returns {string} The content of the URI given.
  *
  * @example
  *  let data = readURISync('resource://gre/modules/NetUtil.jsm');
  */
 function readURISync(uri, charset) {
   charset = typeof charset === "string" ? charset : "UTF-8";
 
-  let channel = NetUtil.newChannel(uri, charset, null);
+  let channel = NetUtil.newChannel2(uri,
+                                    charset,
+                                    null,
+                                    null,      // aLoadingNode
+                                    Services.scriptSecurityManager.getSystemPrincipal(),
+                                    null,      // aTriggeringPrincipal
+                                    Ci.nsILoadInfo.SEC_NORMAL,
+                                    Ci.nsIContentPolicy.TYPE_OTHER);
   let stream = channel.open();
 
   let count = stream.available();
   let data = NetUtil.readInputStreamToString(stream, count, { charset : charset });
 
   stream.close();
 
   return data;
--- a/addon-sdk/source/lib/sdk/page-mod.js
+++ b/addon-sdk/source/lib/sdk/page-mod.js
@@ -13,17 +13,16 @@ const { contract } = require('./util/con
 const { getAttachEventType, WorkerHost } = require('./content/utils');
 const { Class } = require('./core/heritage');
 const { Disposable } = require('./core/disposable');
 const { WeakReference } = require('./core/reference');
 const { Worker } = require('./content/worker');
 const { EventTarget } = require('./event/target');
 const { on, emit, once, setListeners } = require('./event/core');
 const { on: domOn, removeListener: domOff } = require('./dom/events');
-const { pipe } = require('./event/utils');
 const { isRegExp, isUndefined } = require('./lang/type');
 const { merge } = require('./util/object');
 const { windowIterator } = require('./deprecated/window-utils');
 const { isBrowser, getFrames } = require('./window/utils');
 const { getTabs, getTabContentWindow, getTabForContentWindow,
         getURI: getTabURI } = require('./tabs/utils');
 const { ignoreWindow } = require('./private-browsing/utils');
 const { Style } = require("./stylesheet/style");
@@ -109,17 +108,16 @@ const modContract = contract(merge({}, l
  * PageMod constructor (exported below).
  * @constructor
  */
 const PageMod = Class({
   implements: [
     modContract.properties(modelFor),
     EventTarget,
     Disposable,
-    WeakReference
   ],
   extends: WorkerHost(workerFor),
   setup: function PageMod(options) {
     let mod = this;
     let model = modContract(options);
     models.set(this, model);
 
     // Set listeners on {PageMod} itself, not the underlying worker,
@@ -186,16 +184,20 @@ function onContentWindow({ subject: docu
       onContent(pagemod, window);
   }
 }
 
 function applyOnExistingDocuments (mod) {
   getTabs().forEach(tab => {
     // Fake a newly created document
     let window = getTabContentWindow(tab);
+    // on startup with e10s, contentWindow might not exist yet,
+    // in which case we will get notified by "document-element-inserted".
+    if (!window || !window.frames)
+      return;
     let uri = getTabURI(tab);
     if (has(mod.attachTo, "top") && modMatchesURI(mod, uri))
       onContent(mod, window);
     if (has(mod.attachTo, "frame"))
       getFrames(window).
         filter(iframe => modMatchesURI(mod, iframe.location.href)).
         forEach(frame => onContent(mod, frame));
   });
@@ -208,21 +210,25 @@ function createWorker (mod, window) {
     contentScriptFile: mod.contentScriptFile,
     contentScriptOptions: mod.contentScriptOptions,
     // Bug 980468: Syntax errors from scripts can happen before the worker
     // can set up an error handler. They are per-mod rather than per-worker
     // so are best handled at the mod level.
     onError: (e) => emit(mod, 'error', e)
   });
   workers.set(mod, worker);
-  pipe(worker, mod);
-  emit(mod, 'attach', worker);
-  once(worker, 'detach', function detach() {
-    worker.destroy();
-  });
+  worker.on('*', (event, ...args) => {
+    // worker's "attach" event passes a window as the argument
+    // page-mod's "attach" event needs a worker
+    if (event === 'attach')
+      emit(mod, event, worker)
+    else
+      emit(mod, event, ...args);
+  })
+  once(worker, 'detach', () => worker.destroy());
 }
 
 function onContent (mod, window) {
   // not registered yet
   if (!pagemods.has(mod))
     return;
 
   let isTopDocument = window.top === window;
@@ -251,16 +257,30 @@ function onContent (mod, window) {
   }
 
   let eventName = getAttachEventType(mod) || 'load';
   domOn(window, eventName, function onReady (e) {
     if (e.target.defaultView !== window)
       return;
     domOff(window, eventName, onReady, true);
     createWorker(mod, window);
+
+    // Attaching is asynchronous so if the document is already loaded we will
+    // miss the pageshow event so send a synthetic one.
+    if (window.document.readyState == "complete") {
+      mod.on('attach', worker => {
+        try {
+          worker.send('pageshow');
+          emit(worker, 'pageshow');
+        }
+        catch (e) {
+          // This can fail if an earlier attach listener destroyed the worker
+        }
+      });
+    }
   }, true);
 }
 
 function isMatchingAttachState (mod, window) {
   let state = window.document.readyState;
   return 'start' === mod.contentScriptWhen ||
       // Is `load` event already dispatched?
       'complete' === state ||
--- a/addon-sdk/source/lib/sdk/page-mod/match-pattern.js
+++ b/addon-sdk/source/lib/sdk/page-mod/match-pattern.js
@@ -1,5 +1,10 @@
+/* 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 { deprecateUsage } = require("../util/deprecate");
 
 deprecateUsage("Module 'sdk/page-mod/match-pattern' is deprecated use 'sdk/util/match-pattern' instead");
 
 module.exports = require("../util/match-pattern");
--- a/addon-sdk/source/lib/sdk/page-worker.js
+++ b/addon-sdk/source/lib/sdk/page-worker.js
@@ -23,19 +23,19 @@ const { getParentWindow } = require('./w
 const { create: makeFrame, getDocShell } = require('./frame/utils');
 const { contract } = require('./util/contract');
 const { contract: loaderContract } = require('./content/loader');
 const { has } = require('./util/array');
 const { Rules } = require('./util/rules');
 const { merge } = require('./util/object');
 const { data } = require('./self');
 
-const views = WeakMap();
-const workers = WeakMap();
-const pages = WeakMap();
+const views = new WeakMap();
+const workers = new WeakMap();
+const pages = new WeakMap();
 
 const readyEventNames = [
   'DOMContentLoaded',
   'document-element-inserted',
   'load'
 ];
 
 function workerFor(page) workers.get(page)
@@ -131,17 +131,17 @@ const Page = Class({
   get contentURL() { return viewFor(this).getAttribute("data-src") },
   set contentURL(value) {
     if (!isValidURL(this, value)) return;
     let view = viewFor(this);
     let contentURL = pageContract({ contentURL: value }).contentURL;
 
     // page-worker doesn't have a model like other APIs, so to be consitent
     // with the behavior "what you set is what you get", we need to store
-    // the original `contentURL` given. 
+    // the original `contentURL` given.
     // Even if XUL elements doesn't support `dataset`, properties, to
     // indicate that is a custom attribute the syntax "data-*" is used.
     view.setAttribute('data-src', contentURL);
     view.setAttribute('src', data.url(contentURL));
   },
   dispose: function () {
     if (isDisposed(this)) return;
     let view = viewFor(this);
--- a/addon-sdk/source/lib/sdk/panel.js
+++ b/addon-sdk/source/lib/sdk/panel.js
@@ -10,22 +10,20 @@ module.metadata = {
   "engines": {
     "Firefox": "*",
     "SeaMonkey": "*"
   }
 };
 
 const { Ci } = require("chrome");
 const { setTimeout } = require('./timers');
-const { isPrivateBrowsingSupported } = require('./self');
-const { isWindowPBSupported } = require('./private-browsing/utils');
 const { Class } = require("./core/heritage");
 const { merge } = require("./util/object");
 const { WorkerHost } = require("./content/utils");
-const { Worker } = require("./content/worker");
+const { Worker } = require("./deprecated/sync-worker");
 const { Disposable } = require("./core/disposable");
 const { WeakReference } = require('./core/reference');
 const { contract: loaderContract } = require("./content/loader");
 const { contract } = require("./util/contract");
 const { on, off, emit, setListeners } = require("./event/core");
 const { EventTarget } = require("./event/target");
 const domPanel = require("./panel/utils");
 const { events } = require("./panel/events");
@@ -150,17 +148,17 @@ const Panel = Class({
 
     // Setup view
     let view = domPanel.make();
     panels.set(view, this);
     views.set(this, view);
 
     // Load panel content.
     domPanel.setURL(view, model.contentURL);
-    
+
     // Allow context menu
     domPanel.allowContextMenu(view, model.contextMenu);
 
     setupAutoHide(this);
 
     // Setup listeners.
     setListeners(this, options);
     let worker = new Worker(stripListeners(options));
@@ -190,25 +188,25 @@ const Panel = Class({
   get height() modelFor(this).height,
   set height(value) this.resize(this.width, value),
 
   /* Public API: Panel.focus */
   get focus() modelFor(this).focus,
 
   /* Public API: Panel.position */
   get position() modelFor(this).position,
-  
+
   /* Public API: Panel.contextMenu */
   get contextMenu() modelFor(this).contextMenu,
   set contextMenu(allow) {
     let model = modelFor(this);
     model.contextMenu = panelContract({ contextMenu: allow }).contextMenu;
     domPanel.allowContextMenu(viewFor(this), model.contextMenu);
   },
-    
+
   get contentURL() modelFor(this).contentURL,
   set contentURL(value) {
     let model = modelFor(this);
     model.contentURL = panelContract({ contentURL: value }).contentURL;
     domPanel.setURL(viewFor(this), model.contentURL);
     // Detach worker so that messages send will be queued until it's
     // reatached once panel content is ready.
     workerFor(this).detach();
--- a/addon-sdk/source/lib/sdk/panel/utils.js
+++ b/addon-sdk/source/lib/sdk/panel/utils.js
@@ -208,17 +208,17 @@ function shimDefaultStyle(panel) {
       if (node) node.style.padding = 0;
   });
 }
 
 function show(panel, options, anchor) {
   // Prevent the panel from getting focus when showing up
   // if focus is set to false
   panel.setAttribute("noautofocus", !options.focus);
-  
+
   let window = anchor && getOwnerBrowserWindow(anchor);
   let { document } = window ? window : getMostRecentBrowserWindow();
   attach(panel, document);
 
   open(panel, options, anchor);
 }
 exports.show = show
 
@@ -281,18 +281,17 @@ function make(document) {
     }
   }
 
   function onContentLoad({target, type}) {
     if (target === getContentDocument(panel))
       events.emit(type, { subject: panel });
   }
 
-  function onContentChange({subject, type}) {
-    let document = subject;
+  function onContentChange({subject: document, type}) {
     if (document === getContentDocument(panel) && document.defaultView)
       events.emit(type, { subject: panel });
   }
 
   function onPanelStateChange({type}) {
     events.emit(type, { subject: panel })
   }
 
@@ -406,16 +405,16 @@ exports.getContentDocument = getContentD
 
 function setURL(panel, url) {
   getContentFrame(panel).setAttribute("src", url ? data.url(url) : url);
 }
 
 exports.setURL = setURL;
 
 function allowContextMenu(panel, allow) {
-  if(allow) {
+  if (allow) {
     panel.setAttribute("context", "contentAreaContextMenu");
-  } 
+  }
   else {
     panel.removeAttribute("context");
   }
 }
 exports.allowContextMenu = allowContextMenu;
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/panel/window.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-'use strict';
-
-// The panel module currently supports only Firefox.
-// See: https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps
-module.metadata = {
-  'stability': 'unstable',
-  'engines': {
-    'Firefox': '*'
-  }
-};
-
-const { getMostRecentBrowserWindow, windows: getWindows } = require('../window/utils');
-const { ignoreWindow } = require('../private-browsing/utils');
-const { isPrivateBrowsingSupported } = require('../self');
-
-function getWindow(anchor) {
-  let window;
-  let windows = getWindows("navigator:browser", {
-    includePrivate: isPrivateBrowsingSupported
-  });
-
-  if (anchor) {
-    let anchorWindow = anchor.ownerDocument.defaultView.top;
-    let anchorDocument = anchorWindow.document;
-
-    // loop thru supported windows
-    for (let enumWindow of windows) {
-      // Check if the anchor is in this browser window.
-      if (enumWindow == anchorWindow) {
-        window = anchorWindow;
-        break;
-      }
-
-      // Check if the anchor is in a browser tab in this browser window.
-      try {
-        let browser = enumWindow.gBrowser.getBrowserForDocument(anchorDocument);
-        if (browser) {
-          window = enumWindow;
-          break;
-        }
-      }
-      catch (e) {
-      }
-
-      // Look in other subdocuments (sidebar, etc.)?
-    }
-  }
-
-  // If we didn't find the anchor's window (or we have no anchor),
-  // return the most recent browser window.
-  if (!window)
-    window = getMostRecentBrowserWindow();
-
-  // if the window is not supported, then it should be ignored
-  if (ignoreWindow(window)) {
-  	return null;
-  }
-
-  return window;
-}
-exports.getWindow = getWindow;
--- a/addon-sdk/source/lib/sdk/places/bookmarks.js
+++ b/addon-sdk/source/lib/sdk/places/bookmarks.js
@@ -2,17 +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";
 
 module.metadata = {
   "stability": "unstable",
   "engines": {
-    "Firefox": "*"
+    "Firefox": "*",
+    "SeaMonkey": "*"
   }
 };
 
 /*
  * Requiring hosts so they can subscribe to client messages
  */
 require('./host/host-bookmarks');
 require('./host/host-tags');
--- a/addon-sdk/source/lib/sdk/places/events.js
+++ b/addon-sdk/source/lib/sdk/places/events.js
@@ -2,17 +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';
 
 module.metadata = {
   'stability': 'experimental',
   'engines': {
-    'Firefox': '*'
+    'Firefox': '*',
+    "SeaMonkey": '*'
   }
 };
 
 const { Cc, Ci } = require('chrome');
 const { Unknown } = require('../platform/xpcom');
 const { Class } = require('../core/heritage');
 const { merge } = require('../util/object');
 const bookmarkService = Cc['@mozilla.org/browser/nav-bookmarks-service;1']
--- a/addon-sdk/source/lib/sdk/places/favicon.js
+++ b/addon-sdk/source/lib/sdk/places/favicon.js
@@ -2,17 +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";
 
 module.metadata = {
   "stability": "unstable",
   "engines": {
-    "Firefox": "*"
+    "Firefox": "*",
+    "SeaMonkey": "*"
   }
 };
 
 const { Cc, Ci, Cu } = require("chrome");
 const { defer, reject } = require("../core/promise");
 const FaviconService = Cc["@mozilla.org/browser/favicon-service;1"].
                           getService(Ci.nsIFaviconService);
 const AsyncFavicons = FaviconService.QueryInterface(Ci.mozIAsyncFavicons);
--- a/addon-sdk/source/lib/sdk/places/history.js
+++ b/addon-sdk/source/lib/sdk/places/history.js
@@ -2,17 +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";
 
 module.metadata = {
   "stability": "unstable",
   "engines": {
-    "Firefox": "*"
+    "Firefox": "*",
+    "SeaMonkey": "*"
   }
 };
 
 /*
  * Requiring hosts so they can subscribe to client messages
  */
 require('./host/host-bookmarks');
 require('./host/host-tags');
--- a/addon-sdk/source/lib/sdk/places/host/host-bookmarks.js
+++ b/addon-sdk/source/lib/sdk/places/host/host-bookmarks.js
@@ -2,17 +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";
 
 module.metadata = {
   "stability": "experimental",
   "engines": {
-    "Firefox": "*"
+    "Firefox": "*",
+    "SeaMonkey": "*"
   }
 };
 
 const { Cc, Ci } = require('chrome');
 const browserHistory = Cc["@mozilla.org/browser/nav-history-service;1"].
                        getService(Ci.nsIBrowserHistory);
 const asyncHistory = Cc["@mozilla.org/browser/history;1"].
                      getService(Ci.mozIAsyncHistory);
--- a/addon-sdk/source/lib/sdk/places/host/host-query.js
+++ b/addon-sdk/source/lib/sdk/places/host/host-query.js
@@ -2,17 +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";
 
 module.metadata = {
   "stability": "experimental",
   "engines": {
-    "Firefox": "*"
+    "Firefox": "*",
+    "SeaMonkey": "*"
   }
 };
 
 const { Cc, Ci } = require('chrome');
 const { defer, all, resolve } = require('../../core/promise');
 const { safeMerge, omit } = require('../../util/object');
 const historyService = Cc['@mozilla.org/browser/nav-history-service;1']
                      .getService(Ci.nsINavHistoryService);
--- a/addon-sdk/source/lib/sdk/places/host/host-tags.js
+++ b/addon-sdk/source/lib/sdk/places/host/host-tags.js
@@ -2,17 +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";
 
 module.metadata = {
   "stability": "experimental",
   "engines": {
-    "Firefox": "*"
+    "Firefox": "*",
+    "SeaMonkey": "*"
   }
 };
 
 const { Cc, Ci } = require('chrome');
 const taggingService = Cc["@mozilla.org/browser/tagging-service;1"].
                        getService(Ci.nsITaggingService);
 const ios = Cc['@mozilla.org/network/io-service;1'].
             getService(Ci.nsIIOService);
--- a/addon-sdk/source/lib/sdk/places/utils.js
+++ b/addon-sdk/source/lib/sdk/places/utils.js
@@ -2,17 +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';
 
 module.metadata = {
   "stability": "experimental",
   "engines": {
-    "Firefox": "*"
+    "Firefox": "*",
+    "SeaMonkey": "*"
   }
 };
 
 const { Cc, Ci } = require('chrome');
 const { Class } = require('../core/heritage');
 const { method } = require('../lang/functional');
 const { defer, promised, all } = require('../core/promise');
 const { send } = require('../addon/events');
--- a/addon-sdk/source/lib/sdk/preferences/native-options.js
+++ b/addon-sdk/source/lib/sdk/preferences/native-options.js
@@ -118,17 +118,18 @@ function injectOptions({ preferences, pr
     }
 
     let setting = document.createElement('setting');
     setting.setAttribute('pref-name', name);
     setting.setAttribute('data-jetpack-id', id);
     setting.setAttribute('pref', 'extensions.' + preferencesBranch + '.' + name);
     setting.setAttribute('type', type);
     setting.setAttribute('title', title);
-    setting.setAttribute('desc', description);
+    if (description)
+      setting.setAttribute('desc', description);
 
     if (type === 'file' || type === 'directory') {
       setting.setAttribute('fullpath', 'true');
     }
     else if (type === 'control') {
       let button = document.createElement('button');
       button.setAttribute('pref-name', name);
       button.setAttribute('data-jetpack-id', id);
--- a/addon-sdk/source/lib/sdk/preferences/service.js
+++ b/addon-sdk/source/lib/sdk/preferences/service.js
@@ -16,126 +16,88 @@ const MIN_INT = -0x80000000;
 
 const {Cc,Ci,Cr} = require("chrome");
 
 const prefService = Cc["@mozilla.org/preferences-service;1"].
                 getService(Ci.nsIPrefService);
 const prefSvc = prefService.getBranch(null);
 const defaultBranch = prefService.getDefaultBranch(null);
 
-function Branch(branchName) {
-  function getPrefKeys() {
-    return keys(branchName).map(function(key) {
-      return key.replace(branchName, "");
-    });
-  }
+const { Preferences } = require("resource://gre/modules/Preferences.jsm");
+const prefs = new Preferences({});
+
+const branchKeys = branchName =>
+  keys(branchName).map($ => $.replace(branchName, ""));
 
-  return Proxy.create({
-    get: function(receiver, pref) {
-      return get(branchName + pref);
-    },
-    set: function(receiver, pref, val) {
-      set(branchName + pref, val);
-    },
-    delete: function(pref) {
-      reset(branchName + pref);
-      return true;
-    },
-    has: function hasPrefKey(pref) {
-      return has(branchName + pref)
-    },
-    getPropertyDescriptor: function(name) {
+const Branch = function(branchName) {
+  return new Proxy(Branch.prototype, {
+    getOwnPropertyDescriptor(target, name, receiver) {
       return {
-        value: get(branchName + name)
+        configurable: true,
+        enumerable: true,
+        writable: false,
+        value: this.get(target, name, receiver)
       };
     },
-    enumerate: getPrefKeys,
-    keys: getPrefKeys
-  }, Branch.prototype);
+    enumerate(target) {
+      return branchKeys(branchName)[Symbol.iterator]();
+    },
+    ownKeys(target) {
+      return branchKeys(branchName);
+    },
+    get(target, name, receiver) {
+      return get(`${branchName}${name}`);
+    },
+    set(target, name, value, receiver) {
+      set(`${branchName}${name}`, value);
+    },
+    has(target, name) {
+      return this.hasOwn(target, name);
+    },
+    hasOwn(target, name) {
+      return has(`${branchName}${name}`);
+    },
+    deleteProperty(target, name) {
+      reset(`${branchName}${name}`);
+      return true;
+    }
+  });
 }
 
+
 function get(name, defaultValue) {
-  switch (prefSvc.getPrefType(name)) {
-  case Ci.nsIPrefBranch.PREF_STRING:
-    return prefSvc.getComplexValue(name, Ci.nsISupportsString).data;
-
-  case Ci.nsIPrefBranch.PREF_INT:
-    return prefSvc.getIntPref(name);
-
-  case Ci.nsIPrefBranch.PREF_BOOL:
-    return prefSvc.getBoolPref(name);
-
-  case Ci.nsIPrefBranch.PREF_INVALID:
-    return defaultValue;
-
-  default:
-    // This should never happen.
-    throw new Error("Error getting pref " + name +
-                    "; its value's type is " +
-                    prefSvc.getPrefType(name) +
-                    ", which I don't know " +
-                    "how to handle.");
-  }
+  return prefs.get(name, defaultValue);
 }
 exports.get = get;
 
+
 function set(name, value) {
   var prefType;
   if (typeof value != "undefined" && value != null)
     prefType = value.constructor.name;
 
   switch (prefType) {
-  case "String":
-    {
-      var string = Cc["@mozilla.org/supports-string;1"].
-                   createInstance(Ci.nsISupportsString);
-      string.data = value;
-      prefSvc.setComplexValue(name, Ci.nsISupportsString, string);
-    }
-    break;
-
   case "Number":
-    // We throw if the number is outside the range or not an integer, since
-    // the result will not be what the consumer wanted to store.
-    if (value > MAX_INT || value < MIN_INT)
-      throw new Error("you cannot set the " + name +
-                      " pref to the number " + value +
-                      ", as number pref values must be in the signed " +
-                      "32-bit integer range -(2^31) to 2^31-1.  " +
-                      "To store numbers outside that range, store " +
-                      "them as strings.");
     if (value % 1 != 0)
       throw new Error("cannot store non-integer number: " + value);
-    prefSvc.setIntPref(name, value);
-    break;
+  }
 
-  case "Boolean":
-    prefSvc.setBoolPref(name, value);
-    break;
-
-  default:
-    throw new Error("can't set pref " + name + " to value '" + value +
-                    "'; it isn't a string, integer, or boolean");
-  }
+  prefs.set(name, value);
 }
 exports.set = set;
 
-function has(name) {
-  return (prefSvc.getPrefType(name) != Ci.nsIPrefBranch.PREF_INVALID);
-}
+const has = prefs.has.bind(prefs)
 exports.has = has;
 
 function keys(root) {
   return prefSvc.getChildList(root);
 }
 exports.keys = keys;
 
-function isSet(name) {
-  return (has(name) && prefSvc.prefHasUserValue(name));
-}
+const isSet = prefs.isSet.bind(prefs);
 exports.isSet = isSet;
 
 function reset(name) {
   try {
     prefSvc.clearUserPref(name);
   }
   catch (e) {
     // The pref service throws NS_ERROR_UNEXPECTED when the caller tries
--- a/addon-sdk/source/lib/sdk/preferences/utils.js
+++ b/addon-sdk/source/lib/sdk/preferences/utils.js
@@ -3,48 +3,40 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
 const { openTab, getBrowserForTab, getTabId } = require("sdk/tabs/utils");
-const { defer, all } = require("sdk/core/promise");
 const { on, off } = require("sdk/system/events");
-const { setTimeout } = require("sdk/timers");
 const { getMostRecentBrowserWindow } = require('../window/utils');
 
-const open = function open({ id }) {
-  let showing = defer();
-  let loaded = defer();
-  let result = { id: id };
-  let tab = openTab(getMostRecentBrowserWindow(), "about:addons", {
-    inBackground: true
-  });
+// Opens about:addons in a new tab, then displays the inline
+// preferences of the provided add-on
+const open = ({ id }) => new Promise((resolve, reject) => {
+  // opening the about:addons page in a new tab
+  let tab = openTab(getMostRecentBrowserWindow(), "about:addons");
   let browser = getBrowserForTab(tab);
 
+  // waiting for the about:addons page to load
   browser.addEventListener("load", function onPageLoad() {
     browser.removeEventListener("load", onPageLoad, true);
     let window = browser.contentWindow;
 
     // wait for the add-on's "addon-options-displayed"
     on("addon-options-displayed", function onPrefDisplayed({ subject: doc, data }) {
       if (data === id) {
         off("addon-options-displayed", onPrefDisplayed);
-        result.tabId = getTabId(tab);
-        result.document = doc;
-        loaded.resolve();
+        resolve({
+          id: id,
+          tabId: getTabId(tab),
+          "document": doc
+        });
       }
     }, true);
 
     // display the add-on inline preferences page
     window.gViewController.commands.cmd_showItemDetails.doCommand({ id: id }, true);
-    let { node } = window.gViewController.viewObjects.detail;
-    node.addEventListener("ViewChanged", function whenViewChanges() {
-      node.removeEventListener("ViewChanged", whenViewChanges, false);
-      showing.resolve();
-    }, false);
   }, true);
-
-  return all([ showing.promise, loaded.promise ]).then(_ => result);
-}
+});
 exports.open = open;
--- a/addon-sdk/source/lib/sdk/self.js
+++ b/addon-sdk/source/lib/sdk/self.js
@@ -16,26 +16,29 @@ const { readURISync } = require('./net/u
 const id = options.id;
 
 const readPref = key => get("extensions." + id + ".sdk." + key);
 
 const name = readPref("name") || options.name;
 const version = readPref("version") || options.version;
 const loadReason = readPref("load.reason") || options.loadReason;
 const rootURI = readPref("rootURI") || options.rootURI || "";
-const baseURI = readPref("baseURI") || options.prefixURI + name + "/";
+const baseURI = readPref("baseURI") || options.prefixURI + name + "/"
 const addonDataURI = baseURI + "data/";
 const metadata = options.metadata || {};
 const permissions = metadata.permissions || {};
 const isPacked = rootURI && rootURI.indexOf("jar:") === 0;
 
 const uri = (path="") =>
   path.contains(":") ? path : addonDataURI + path.replace(/^\.\//, "");
 
-let { preferencesBranch } = options;
+let preferencesBranch = ("preferences-branch" in metadata)
+                            ? metadata["preferences-branch"]
+                            : options.preferencesBranch
+
 if (/[^\w{@}.-]/.test(preferencesBranch)) {
   preferencesBranch = id;
   console.warn("Ignoring preferences-branch (not a valid branch name)");
 }
 
 // Some XPCOM APIs require valid URIs as an argument for certain operations
 // (see `nsILoginManager` for example). This property represents add-on
 // associated unique URI string that can be used for that.
--- a/addon-sdk/source/lib/sdk/system/child_process.js
+++ b/addon-sdk/source/lib/sdk/system/child_process.js
@@ -15,17 +15,17 @@ let { on, emit, off } = require('../even
 let { Class } = require('../core/heritage');
 let { platform } = require('../system');
 let { isFunction, isArray } = require('../lang/type');
 let { delay } = require('../lang/functional');
 let { merge } = require('../util/object');
 let { setTimeout, clearTimeout } = require('../timers');
 let isWindows = platform.indexOf('win') === 0;
 
-let processes = WeakMap();
+let processes = new WeakMap();
 
 
 /**
  * The `Child` class wraps a subprocess command, exposes
  * the stdio streams, and methods to manipulate the subprocess
  */
 let Child = Class({
   implements: [EventTarget],
--- a/addon-sdk/source/lib/sdk/system/xul-app.js
+++ b/addon-sdk/source/lib/sdk/system/xul-app.js
@@ -2,12 +2,11 @@
  * 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": "experimental"
 };
 
-var { Cu } = require("chrome");
-var { XulApp } = Cu.import("resource://gre/modules/sdk/system/XulApp.js", {});
+const { XulApp } = require("./xul-app.jsm");
 
 Object.keys(XulApp).forEach(k => exports[k] = XulApp[k]);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/system/xul-app.jsm
@@ -0,0 +1,241 @@
+/* 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";
+
+this.EXPORTED_SYMBOLS = [ "XulApp" ];
+
+var { classes: Cc, interfaces: Ci } = Components;
+
+var exports = {};
+this.XulApp = exports;
+
+var appInfo;
+
+// NOTE: below is required to avoid failing xpcshell tests,
+//       which do not implement nsIXULAppInfo
+// See Bug 1114752 https://bugzilla.mozilla.org/show_bug.cgi?id=1114752
+try {
+ appInfo = Cc["@mozilla.org/xre/app-info;1"]
+              .getService(Ci.nsIXULAppInfo);
+}
+catch (e) {
+  // xpcshell test case
+  appInfo = {};
+}
+var vc = Cc["@mozilla.org/xpcom/version-comparator;1"]
+         .getService(Ci.nsIVersionComparator);
+
+var ID = exports.ID = appInfo.ID;
+var name = exports.name = appInfo.name;
+var version = exports.version = appInfo.version;
+var platformVersion = exports.platformVersion = appInfo.platformVersion;
+
+// The following mapping of application names to GUIDs was taken from:
+//
+//   https://addons.mozilla.org/en-US/firefox/pages/appversions
+//
+// Using the GUID instead of the app's name is preferable because sometimes
+// re-branded versions of a product have different names: for instance,
+// Firefox, Minefield, Iceweasel, and Shiretoko all have the same
+// GUID.
+
+var ids = exports.ids = {
+  Firefox: "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
+  Mozilla: "{86c18b42-e466-45a9-ae7a-9b95ba6f5640}",
+  SeaMonkey: "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}",
+  Fennec: "{aa3c5121-dab2-40e2-81ca-7ea25febc110}",
+  Thunderbird: "{3550f703-e582-4d05-9a08-453d09bdfdc6}"
+};
+
+function is(name) {
+  if (!(name in ids))
+    throw new Error("Unkown Mozilla Application: " + name);
+  return ID == ids[name];
+};
+exports.is = is;
+
+function isOneOf(names) {
+  for (var i = 0; i < names.length; i++)
+    if (is(names[i]))
+      return true;
+  return false;
+};
+exports.isOneOf = isOneOf;
+
+/**
+ * Use this to check whether the given version (e.g. xulApp.platformVersion)
+ * is in the given range. Versions must be in version comparator-compatible
+ * format. See MDC for details:
+ * https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIVersionComparator
+ */
+var versionInRange = exports.versionInRange =
+function versionInRange(version, lowInclusive, highExclusive) {
+  return (vc.compare(version, lowInclusive) >= 0) &&
+         (vc.compare(version, highExclusive) < 0);
+}
+
+const reVersionRange = /^((?:<|>)?=?)?\s*((?:\d+[\S]*)|\*)(?:\s+((?:<|>)=?)?(\d+[\S]+))?$/;
+const reOnlyInifinity = /^[<>]?=?\s*[*x]$/;
+const reSubInfinity = /\.[*x]/g;
+const reHyphenRange = /^(\d+.*?)\s*-\s*(\d+.*?)$/;
+const reRangeSeparator = /\s*\|\|\s*/;
+
+const compares = {
+  "=": function (c) { return c === 0 },
+  ">=": function (c) { return c >= 0 },
+  "<=": function (c) { return c <= 0},
+  "<": function (c) { return c < 0 },
+  ">": function (c) { return c > 0 }
+}
+
+function normalizeRange(range) {
+    return range
+        .replace(reOnlyInifinity, "")
+        .replace(reSubInfinity, ".*")
+        .replace(reHyphenRange, ">=$1 <=$2")
+}
+
+/**
+ * Compare the versions given, using the comparison operator provided.
+ * Internal use only.
+ *
+ * @example
+ *  compareVersion("1.2", "<=", "1.*") // true
+ *
+ * @param {String} version
+ *  A version to compare
+ *
+ * @param {String} comparison
+ *  The comparison operator
+ *
+ * @param {String} compareVersion
+ *  A version to compare
+ */
+function compareVersion(version, comparison, compareVersion) {
+  let hasWildcard = compareVersion.indexOf("*") !== -1;
+
+  comparison = comparison || "=";
+
+  if (hasWildcard) {
+    switch (comparison) {
+      case "=":
+        let zeroVersion = compareVersion.replace(reSubInfinity, ".0");
+        return versionInRange(version, zeroVersion, compareVersion);
+      case ">=":
+        compareVersion = compareVersion.replace(reSubInfinity, ".0");
+        break;
+    }
+  }
+
+  let compare = compares[comparison];
+
+  return typeof compare === "function" && compare(vc.compare(version, compareVersion));
+}
+
+/**
+ * Returns `true` if `version` satisfies the `versionRange` given.
+ * If only an argument is passed, is used as `versionRange` and compared against
+ * `xulApp.platformVersion`.
+ *
+ * `versionRange` is either a string which has one or more space-separated
+ * descriptors, or a range like "fromVersion - toVersion".
+ * Version range descriptors may be any of the following styles:
+ *
+ * - "version" Must match `version` exactly
+ * - "=version" Same as just `version`
+ * - ">version" Must be greater than `version`
+ * - ">=version" Must be greater or equal than `version`
+ * - "<version" Must be less than `version`
+ * - "<=version" Must be less or equal than `version`
+ * - "1.2.x" or "1.2.*" See 'X version ranges' below
+ * - "*" or "" (just an empty string) Matches any version
+ * - "version1 - version2" Same as ">=version1 <=version2"
+ * - "range1 || range2" Passes if either `range1` or `range2` are satisfied
+ *
+ * For example, these are all valid:
+ * - "1.0.0 - 2.9999.9999"
+ * - ">=1.0.2 <2.1.2"
+ * - ">1.0.2 <=2.3.4"
+ * - "2.0.1"
+ * - "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0"