Merge mozilla-central into holly
authorMike Conley <mconley@mozilla.com>
Mon, 02 Dec 2013 18:46:14 -0500
changeset 173987 aec0f57a9f89097270d12593289429ed789dcfd7
parent 173886 cc2ea69b6acd6e6d0f4e87660031c445fbd0b0f5 (current diff)
parent 173986 492fbc8095b807132f96446fcc6a8db62faf9ff8 (diff)
child 174015 1153ef40e4d7cf4f6ec4b22d4c32b40031401bfc
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone28.0a1
Merge mozilla-central into holly
CLOBBER
browser/base/content/tabbrowser.xml
dom/bluetooth/BluetoothSocket.h
layout/style/nsCSSKeywordList.h
layout/style/nsCSSProps.cpp
modules/libpref/src/init/all.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Another Windows WebIDL clobber needed due to bug 928195
+More Windows webidl changes
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -434,16 +434,21 @@ pref("services.push.requestTimeout", 100
 pref("services.push.udp.wakeupEnabled", true);
 
 // NetworkStats
 #ifdef MOZ_WIDGET_GONK
 pref("dom.mozNetworkStats.enabled", true);
 pref("dom.webapps.firstRunWithSIM", true);
 #endif
 
+#ifdef MOZ_B2G_RIL
+// SingleVariant
+pref("dom.mozApps.single_variant_sourcedir", "/persist/svoperapps");
+#endif
+
 // WebSettings
 pref("dom.mozSettings.enabled", true);
 pref("dom.navigator-property.disable.mozSettings", false);
 pref("dom.mozPermissionSettings.enabled", true);
 
 // controls if we want camera support
 pref("device.camera.enabled", true);
 pref("media.realtime_decoder.enabled", true);
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "cf23b263b4bbf1024210350f24c7e6eb5872a4be", 
+    "revision": "c2506884aba22b968d3ab1ad6c28f0782b22a17f", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -702,19 +702,25 @@
 
             onLocationChange: function (aWebProgress, aRequest, aLocation,
                                         aFlags) {
               // OnLocationChange is called for both the top-level content
               // and the subframes.
               let topLevel = aWebProgress.isTopLevel;
 
               if (topLevel) {
-                // The document loaded correctly, clear the value if we should
+                // If userTypedClear > 0, the document loaded correctly and we should be
+                // clearing the user typed value. We also need to clear the typed value
+                // if the document failed to load, to make sure the urlbar reflects the
+                // failed URI (particularly for SSL errors). However, don't clear the value
+                // if the error page's URI is about:blank, because that causes complete
+                // loss of urlbar contents for invalid URI errors (see bug 867957).
                 if (this.mBrowser.userTypedClear > 0 ||
-                    (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE))
+                    ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
+                     aLocation.spec != "about:blank"))
                   this.mBrowser.userTypedValue = null;
 
                 // Clear out the missing plugins list since it's related to the
                 // previous location.
                 this.mBrowser.missingPlugins = null;
 
                 if (this.mTabBrowser.isFindBarInitialized(this.mTab)) {
                   let findBar = this.mTabBrowser.getFindBar(this.mTab);
--- a/config/system-headers
+++ b/config/system-headers
@@ -1126,8 +1126,9 @@ unicode/uclean.h
 unicode/ucol.h
 unicode/udat.h
 unicode/udatpg.h
 unicode/uenum.h
 unicode/unum.h
 unicode/ustring.h
 unicode/utypes.h
 #endif
+libutil.h
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/942979.html
@@ -0,0 +1,42 @@
+<html>
+<head></head>
+<body>
+<script>
+var QNBABBAH=document.createElement('ROTFL');
+
+QNBABBAH.addEventListener('DOMNodeRemoved',function EventHandlerEventHandlerIMQFGDGL()
+{
+         QNBABBAH.normalize();
+}, false);
+
+var MBHOMIBK=document.createElement('whatever');
+QNBABBAH.appendChild(MBHOMIBK);
+
+var BKDKEDBI=document.createElement('whatever');
+MBHOMIBK.appendChild(BKDKEDBI);
+
+var QQFOJBAM=document.createElement('whatever');
+BKDKEDBI.appendChild(QQFOJBAM); 
+
+var JNCNIPON=document.createElement('whatever');
+QQFOJBAM.appendChild(JNCNIPON);
+var a=document.createTextNode('AAAA');
+
+JNCNIPON.addEventListener('DOMNodeInserted',function EventHandlerEventHandlerGFNKONKK()
+{
+   JNCNIPON.insertAdjacentHTML('afterbegin','.');
+
+}, false);
+
+JNCNIPON.appendChild(a);
+
+var CACEQGDA=document.createElement('whatever2');
+
+CACEQGDA.addEventListener('DOMSubtreeModified',function EventHandlerEventHandlerMOOLBGNC(){
+
+}, false);
+
+JNCNIPON.innerHTML = 'ALBERTDAJEDUPY';
+</script>
+</body>
+</html>
--- a/content/base/crashtests/crashtests.list
+++ b/content/base/crashtests/crashtests.list
@@ -141,8 +141,9 @@ load 847127.html
 load 845093-1.html
 load 845093-2.html
 load 849727.html
 load 849732.html
 load 849601.html
 skip-if(Android) load 851353-1.html
 load 863950.html
 load 864448.html
+load 942979.html
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -643,16 +643,19 @@ public:
                                              ErrorResult& aError);
   Attr* GetAttributeNodeNS(const nsAString& aNamespaceURI,
                            const nsAString& aLocalName);
   already_AddRefed<Attr> SetAttributeNodeNS(Attr& aNewAttr,
                                             ErrorResult& aError);
 
   already_AddRefed<DOMRectList> GetClientRects();
   already_AddRefed<DOMRect> GetBoundingClientRect();
+
+  already_AddRefed<ShadowRoot> CreateShadowRoot(ErrorResult& aError);
+
   void ScrollIntoView()
   {
     ScrollIntoView(true);
   }
   void ScrollIntoView(bool aTop);
   int32_t ScrollTop()
   {
     nsIScrollableFrame* sf = GetScrollFrame();
@@ -1074,18 +1077,18 @@ protected:
                                    const nsAString& aLocalName);
 
   inline void RegisterFreezableElement();
   inline void UnregisterFreezableElement();
 
   /**
    * Add/remove this element to the documents id cache
    */
-  inline void AddToIdTable(nsIAtom* aId);
-  inline void RemoveFromIdTable();
+  void AddToIdTable(nsIAtom* aId);
+  void RemoveFromIdTable();
 
   /**
    * Functions to carry out event default actions for links of all types
    * (HTML links, XLinks, SVG "XLinks", etc.)
    */
 
   /**
    * Check that we meet the conditions to handle a link event
@@ -1125,18 +1128,16 @@ private:
    * Get this element's client area rect in app units.
    * @return the frame's client area
    */
   nsRect GetClientAreaRect();
 
   nsIScrollableFrame* GetScrollFrame(nsIFrame **aStyledFrame = nullptr,
                                      bool aFlushLayout = true);
 
-  void GetMarkup(bool aIncludeSelf, nsAString& aMarkup);
-
   // Data members
   nsEventStates mState;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(Element, NS_ELEMENT_IID)
 
 inline bool
 Element::HasAttr(int32_t aNameSpaceID, nsIAtom* aName) const
--- a/content/base/public/ElementInlines.h
+++ b/content/base/public/ElementInlines.h
@@ -9,43 +9,16 @@
 
 #include "mozilla/dom/Element.h"
 #include "nsIDocument.h"
 
 namespace mozilla {
 namespace dom {
 
 inline void
-Element::AddToIdTable(nsIAtom* aId)
-{
-  NS_ASSERTION(HasID(), "Node doesn't have an ID?");
-  nsIDocument* doc = GetCurrentDoc();
-  if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) {
-    doc->AddToIdTable(this, aId);
-  }
-}
-
-inline void
-Element::RemoveFromIdTable()
-{
-  if (HasID()) {
-    nsIDocument* doc = GetCurrentDoc();
-    if (doc) {
-      nsIAtom* id = DoGetID();
-      // id can be null during mutation events evilness. Also, XUL elements
-      // loose their proto attributes during cc-unlink, so this can happen
-      // during cc-unlink too.
-      if (id) {
-        doc->RemoveFromIdTable(this, DoGetID());
-      }
-    }
-  }
-}
-
-inline void
 Element::MozRequestPointerLock()
 {
   OwnerDoc()->RequestPointerLock(this);
 }
 
 inline void
 Element::RegisterFreezableElement()
 {
--- a/content/base/public/FragmentOrElement.h
+++ b/content/base/public/FragmentOrElement.h
@@ -155,16 +155,17 @@ private:
 
 /**
  * A generic base class for DOM elements, implementing many nsIContent,
  * nsIDOMNode and nsIDOMElement methods.
  */
 namespace mozilla {
 namespace dom {
 
+class ShadowRoot;
 class UndoManager;
 
 class FragmentOrElement : public nsIContent
 {
 public:
   FragmentOrElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   virtual ~FragmentOrElement();
 
@@ -198,16 +199,19 @@ public:
   virtual nsresult AppendText(const PRUnichar* aBuffer, uint32_t aLength,
                               bool aNotify) MOZ_OVERRIDE;
   virtual bool TextIsOnlyWhitespace() MOZ_OVERRIDE;
   virtual void AppendTextTo(nsAString& aResult) MOZ_OVERRIDE;
   virtual nsIContent *GetBindingParent() const MOZ_OVERRIDE;
   virtual nsXBLBinding *GetXBLBinding() const MOZ_OVERRIDE;
   virtual void SetXBLBinding(nsXBLBinding* aBinding,
                              nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
+  virtual ShadowRoot *GetShadowRoot() const MOZ_OVERRIDE;
+  virtual ShadowRoot *GetContainingShadow() const MOZ_OVERRIDE;
+  virtual void SetShadowRoot(ShadowRoot* aBinding) MOZ_OVERRIDE;
   virtual nsIContent *GetXBLInsertionParent() const;
   virtual void SetXBLInsertionParent(nsIContent* aContent);
   virtual bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE;
 
   virtual void DestroyContent() MOZ_OVERRIDE;
   virtual void SaveSubtreeState() MOZ_OVERRIDE;
 
   virtual const nsAttrValue* DoGetClasses() const MOZ_OVERRIDE;
@@ -350,27 +354,40 @@ public:
     nsRefPtr<nsContentList> mChildrenList;
 
     /**
      * An object implementing the .classList property for this element.
      */
     nsRefPtr<nsDOMTokenList> mClassList;
 
     /**
+     * ShadowRoot bound to the element.
+     */
+    nsRefPtr<ShadowRoot> mShadowRoot;
+
+    /**
+     * The root ShadowRoot of this element if it is in a shadow tree.
+     */
+    nsRefPtr<ShadowRoot> mContainingShadow;
+
+    /**
      * XBL binding installed on the element.
      */
     nsRefPtr<nsXBLBinding> mXBLBinding;
 
     /**
      * XBL binding installed on the lement.
      */
     nsCOMPtr<nsIContent> mXBLInsertionParent;
   };
 
 protected:
+  void GetMarkup(bool aIncludeSelf, nsAString& aMarkup);
+  void SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError);
+
   // Override from nsINode
   virtual nsINode::nsSlots* CreateSlots() MOZ_OVERRIDE;
 
   nsDOMSlots *DOMSlots()
   {
     return static_cast<nsDOMSlots*>(Slots());
   }
 
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -22,16 +22,21 @@
 #include "js/RootingAPI.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/TimeStamp.h"
 #include "nsContentListDeclarations.h"
 #include "nsMathUtils.h"
 #include "Units.h"
 
+#if defined(XP_WIN)
+// Undefine LoadImage to prevent naming conflict with Windows.
+#undef LoadImage
+#endif
+
 class imgICache;
 class imgIContainer;
 class imgINotificationObserver;
 class imgIRequest;
 class imgLoader;
 class imgRequestProxy;
 class nsAutoScriptBlockerSuppressNodeRemoved;
 class nsEventListenerManager;
@@ -1836,16 +1841,28 @@ public:
    * Returns true if the doc tree branch which contains aDoc contains any
    * plugins which we don't control event dispatch for, i.e. do any plugins
    * in the same tab as this document receive key events outside of our
    * control? This always returns false on MacOSX.
    */
   static bool HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc);
 
   /**
+   * Fire mutation events for changes caused by parsing directly into a
+   * context node.
+   *
+   * @param aDoc the document of the node
+   * @param aDest the destination node that got stuff appended to it
+   * @param aOldChildCount the number of children the node had before parsing
+   */
+  static void FireMutationEventsForDirectParsing(nsIDocument* aDoc,
+                                                 nsIContent* aDest,
+                                                 int32_t aOldChildCount);
+
+  /**
    * Returns true if the content is in a document and contains a plugin
    * which we don't control event dispatch for, i.e. do any plugins in this
    * doc tree receive key events outside of our control? This always returns
    * false on MacOSX.
    */
   static bool HasPluginWithUncontrolledEventDispatch(nsIContent* aContent);
 
   /**
@@ -2088,16 +2105,27 @@ public:
                                   const nsAString& aFeature,
                                   const nsAString& aVersion);
 
   /**
    * Return true if the browser.dom.window.dump.enabled pref is set.
    */
   static bool DOMWindowDumpEnabled();
 
+  /**
+   * Returns whether a content is an insertion point for XBL
+   * bindings or web components ShadowRoot. In web components,
+   * this corresponds to a <content> element that participates
+   * in node distribution. In XBL this corresponds to an
+   * <xbl:children> element in anonymous content.
+   *
+   * @param aContent The content to test for being an insertion point.
+   */
+  static bool IsContentInsertionPoint(const nsIContent* aContent);
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -16,31 +16,34 @@ class nsIURI;
 class nsRuleWalker;
 class nsAttrValue;
 class nsAttrName;
 class nsTextFragment;
 class nsIFrame;
 class nsXBLBinding;
 
 namespace mozilla {
+namespace dom {
+class ShadowRoot;
+} // namespace dom
 namespace widget {
 struct IMEState;
 } // namespace widget
 } // namespace mozilla
 
 enum nsLinkState {
   eLinkState_Unvisited  = 1,
   eLinkState_Visited    = 2,
   eLinkState_NotLink    = 3 
 };
 
 // IID for the nsIContent interface
 #define NS_ICONTENT_IID \
-{ 0x34117ca3, 0x45d0, 0x479e, \
-  { 0x91, 0x30, 0x54, 0x49, 0xa9, 0x5f, 0x25, 0x99 } }
+{ 0x4b05faf2, 0x12e0, 0x4f56, \
+  { 0xb5, 0x2e, 0x3e, 0xb6, 0xad, 0x9c, 0x6e, 0xbe } }
 
 /**
  * A node of content in a document's content model. This interface
  * is supported by all content objects.
  */
 class nsIContent : public nsINode {
 public:
   typedef mozilla::widget::IMEState IMEState;
@@ -621,16 +624,40 @@ public:
    * @param aOldBindingManager The old binding manager that contains
    *                           this content if this content was adopted
    *                           to another document.
    */
   virtual void SetXBLBinding(nsXBLBinding* aBinding,
                              nsBindingManager* aOldBindingManager = nullptr) = 0;
 
   /**
+   * Sets the ShadowRoot binding for this element. The contents of the
+   * binding is rendered in place of this node's children.
+   *
+   * @param aShadowRoot The ShadowRoot to be bound to this element.
+   */
+  virtual void SetShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot) = 0;
+
+  /**
+   * Gets the ShadowRoot binding for this element.
+   *
+   * @return The ShadowRoot currently bound to this element.
+   */
+  virtual mozilla::dom::ShadowRoot *GetShadowRoot() const = 0;
+
+  /**
+   * Gets the root of the node tree for this content if it is in a shadow tree.
+   * This method is called |GetContainingShadow| instead of |GetRootShadowRoot|
+   * to avoid confusion with |GetShadowRoot|.
+   *
+   * @return The ShadowRoot that is the root of the node tree.
+   */
+  virtual mozilla::dom::ShadowRoot *GetContainingShadow() const = 0;
+
+  /**
    * Gets the insertion parent element of the XBL binding.
    * The insertion parent is our one true parent in the transformed DOM.
    *
    * @return the insertion parent element.
    */
   virtual nsIContent *GetXBLInsertionParent() const = 0;
 
   /**
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -108,16 +108,19 @@ enum {
   NODE_MAY_BE_IN_BINDING_MNGR =           NODE_FLAG_BIT(6),
 
   NODE_IS_EDITABLE =                      NODE_FLAG_BIT(7),
 
   // For all Element nodes, NODE_MAY_HAVE_CLASS is guaranteed to be set if the
   // node in fact has a class, but may be set even if it doesn't.
   NODE_MAY_HAVE_CLASS =                   NODE_FLAG_BIT(8),
 
+  // Whether the node participates in a shadow tree.
+  NODE_IS_IN_SHADOW_TREE =                NODE_FLAG_BIT(9),
+
   // Node has an :empty or :-moz-only-whitespace selector
   NODE_HAS_EMPTY_SELECTOR =               NODE_FLAG_BIT(10),
 
   // A child of the node has a selector such that any insertion,
   // removal, or appending of children requires restyling the parent.
   NODE_HAS_SLOW_SELECTOR =                NODE_FLAG_BIT(11),
 
   // A child of the node has a :first-child, :-moz-first-node,
--- a/content/base/src/ChildIterator.cpp
+++ b/content/base/src/ChildIterator.cpp
@@ -1,66 +1,120 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et 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 "ChildIterator.h"
+#include "nsContentUtils.h"
 #include "mozilla/dom/XBLChildrenElement.h"
+#include "mozilla/dom/HTMLContentElement.h"
 
 namespace mozilla {
 namespace dom {
 
+class MatchedNodes {
+public:
+  MatchedNodes(HTMLContentElement* aInsertionPoint)
+    : mIsContentElement(true), mContentElement(aInsertionPoint) {}
+
+  MatchedNodes(XBLChildrenElement* aInsertionPoint)
+    : mIsContentElement(false), mChildrenElement(aInsertionPoint) {}
+
+  uint32_t Length() const
+  {
+    return mIsContentElement ? mContentElement->MatchedNodes().Length()
+                             : mChildrenElement->mInsertedChildren.Length();
+  }
+
+  nsIContent* operator[](int32_t aIndex) const
+  {
+    return mIsContentElement ? mContentElement->MatchedNodes()[aIndex]
+                             : mChildrenElement->mInsertedChildren[aIndex];
+  }
+
+  bool IsEmpty() const
+  {
+    return mIsContentElement ? mContentElement->MatchedNodes().IsEmpty()
+                             : mChildrenElement->mInsertedChildren.IsEmpty();
+  }
+protected:
+  bool mIsContentElement;
+  union {
+    HTMLContentElement* mContentElement;
+    XBLChildrenElement* mChildrenElement;
+  };
+};
+
+static inline MatchedNodes
+GetMatchedNodesForPoint(nsIContent* aContent)
+{
+  if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+    // XBL case
+    return MatchedNodes(static_cast<XBLChildrenElement*>(aContent));
+  }
+
+  // Web components case
+  MOZ_ASSERT(aContent->IsHTML(nsGkAtoms::content));
+  return MatchedNodes(static_cast<HTMLContentElement*>(aContent));
+}
+
 nsIContent*
 ExplicitChildIterator::GetNextChild()
 {
   // If we're already in the inserted-children array, look there first
   if (mIndexInInserted) {
     MOZ_ASSERT(mChild);
-    MOZ_ASSERT(mChild->IsActiveChildrenElement());
+    MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild));
     MOZ_ASSERT(!mDefaultChild);
 
-    XBLChildrenElement* point = static_cast<XBLChildrenElement*>(mChild);
-    if (mIndexInInserted < point->mInsertedChildren.Length()) {
-      return point->mInsertedChildren[mIndexInInserted++];
+    MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
+    if (mIndexInInserted < assignedChildren.Length()) {
+      return assignedChildren[mIndexInInserted++];
     }
     mIndexInInserted = 0;
     mChild = mChild->GetNextSibling();
   } else if (mDefaultChild) {
     // If we're already in default content, check if there are more nodes there
     MOZ_ASSERT(mChild);
-    MOZ_ASSERT(mChild->IsActiveChildrenElement());
+    MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild));
 
     mDefaultChild = mDefaultChild->GetNextSibling();
     if (mDefaultChild) {
       return mDefaultChild;
     }
 
     mChild = mChild->GetNextSibling();
   } else if (mIsFirst) {  // at the beginning of the child list
     mChild = mParent->GetFirstChild();
     mIsFirst = false;
   } else if (mChild) { // in the middle of the child list
     mChild = mChild->GetNextSibling();
   }
 
-  // Iterate until we find a non-<children>, or a <children> with content.
-  while (mChild && mChild->IsActiveChildrenElement()) {
-    XBLChildrenElement* point = static_cast<XBLChildrenElement*>(mChild);
-    if (!point->mInsertedChildren.IsEmpty()) {
+  // Iterate until we find a non-insertion point, or an insertion point with
+  // content.
+  while (mChild && nsContentUtils::IsContentInsertionPoint(mChild)) {
+    MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
+    if (!assignedChildren.IsEmpty()) {
+      // Iterate through elements projected on insertion point.
       mIndexInInserted = 1;
-      return point->mInsertedChildren[0];
+      return assignedChildren[0];
     }
 
+    // Insertion points inside fallback/default content
+    // are considered inactive and do not get assigned nodes.
     mDefaultChild = mChild->GetFirstChild();
     if (mDefaultChild) {
       return mDefaultChild;
     }
 
+    // If we have an insertion point with no assigned nodes and
+    // no default content, move on to the next node.
     mChild = mChild->GetNextSibling();
   }
 
   return mChild;
 }
 
 FlattenedChildIterator::FlattenedChildIterator(nsIContent* aParent)
   : ExplicitChildIterator(aParent), mXBLInvolved(false)
@@ -104,19 +158,19 @@ nsIContent* FlattenedChildIterator::Get(
 }
 
 nsIContent* FlattenedChildIterator::GetPreviousChild()
 {
   // If we're already in the inserted-children array, look there first
   if (mIndexInInserted) {
     // NB: mIndexInInserted points one past the last returned child so we need
     // to look *two* indices back in order to return the previous child.
-    XBLChildrenElement* point = static_cast<XBLChildrenElement*>(mChild);
+    MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
     if (--mIndexInInserted) {
-      return point->mInsertedChildren[mIndexInInserted - 1];
+      return assignedChildren[mIndexInInserted - 1];
     }
     mChild = mChild->GetPreviousSibling();
   } else if (mDefaultChild) {
     // If we're already in default content, check if there are more nodes there
     mDefaultChild = mDefaultChild->GetPreviousSibling();
     if (mDefaultChild) {
       return mDefaultChild;
     }
@@ -125,22 +179,23 @@ nsIContent* FlattenedChildIterator::GetP
   } else if (mIsFirst) { // at the beginning of the child list
     return nullptr;
   } else if (mChild) { // in the middle of the child list
     mChild = mChild->GetPreviousSibling();
   } else { // at the end of the child list
     mChild = mParent->GetLastChild();
   }
 
-  // Iterate until we find a non-<children>, or a <children> with content.
-  while (mChild && mChild->IsActiveChildrenElement()) {
-    XBLChildrenElement* point = static_cast<XBLChildrenElement*>(mChild);
-    if (!point->mInsertedChildren.IsEmpty()) {
-      mIndexInInserted = point->InsertedChildrenLength();
-      return point->mInsertedChildren[mIndexInInserted - 1];
+  // Iterate until we find a non-insertion point, or an insertion point with
+  // content.
+  while (mChild && nsContentUtils::IsContentInsertionPoint(mChild)) {
+    MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
+    if (!assignedChildren.IsEmpty()) {
+      mIndexInInserted = assignedChildren.Length();
+      return assignedChildren[mIndexInInserted - 1];
     }
 
     mDefaultChild = mChild->GetLastChild();
     if (mDefaultChild) {
       return mDefaultChild;
     }
 
     mChild = mChild->GetPreviousSibling();
--- a/content/base/src/ChildIterator.h
+++ b/content/base/src/ChildIterator.h
@@ -34,16 +34,34 @@ public:
       mDefaultChild(nullptr),
       mIndexInInserted(0),
       mIsFirst(true)
   {
   }
 
   nsIContent* GetNextChild();
 
+  // Looks for aChildToFind respecting insertion points until aChildToFind
+  // or aBound is found. If aBound is nullptr then the seek is unbounded. Returns
+  // whether aChildToFind was found as an explicit child prior to encountering
+  // aBound.
+  bool Seek(nsIContent* aChildToFind, nsIContent* aBound = nullptr)
+  {
+    // It would be nice to assert that we find aChildToFind, but bz thinks that
+    // we might not find aChildToFind when called from ContentInserted
+    // if first-letter frames are about.
+
+    nsIContent* child;
+    do {
+      child = GetNextChild();
+    } while (child && child != aChildToFind && child != aBound);
+
+    return child == aChildToFind;
+  }
+
 protected:
   // The parent of the children being iterated. For the FlattenedChildIterator,
   // if there is a binding attached to the original parent, mParent points to
   // the <xbl:content> element for the binding.
   nsIContent* mParent;
 
   // The current child. When we encounter an <xbl:children> insertion point,
   // mChild remains as the insertion point whose content we're iterating (and
@@ -79,29 +97,16 @@ public:
   // child of the node, default content for an <xbl:children> element or
   // an inserted child for an <xbl:children> element.
   nsIContent* Get();
 
   // The inverse of GetNextChild. Properly steps in and out of <xbl:children>
   // elements.
   nsIContent* GetPreviousChild();
 
-  // Looks for aChildToFind respecting XBL insertion points.
-  void Seek(nsIContent* aChildToFind)
-  {
-    // It would be nice to assert that we find aChildToFind, but bz thinks that
-    // we might not find aChildToFind when called from ContentInserted
-    // if first-letter frames are about.
-
-    nsIContent* child;
-    do {
-      child = GetNextChild();
-    } while (child && child != aChildToFind);
-  }
-
   bool XBLInvolved() { return mXBLInvolved; }
 
 private:
   // For certain optimizations, nsCSSFrameConstructor needs to know if the
   // child list of the element that we're iterating matches its .childNodes.
   bool mXBLInvolved;
 };
 
--- a/content/base/src/DocumentFragment.h
+++ b/content/base/src/DocumentFragment.h
@@ -16,20 +16,19 @@ class nsIAtom;
 class nsAString;
 class nsIDocument;
 class nsIContent;
 
 namespace mozilla {
 namespace dom {
 
 class Element;
-class HTMLTemplateElement;
 
-class DocumentFragment MOZ_FINAL : public FragmentOrElement,
-                                   public nsIDOMDocumentFragment
+class DocumentFragment : public FragmentOrElement,
+                         public nsIDOMDocumentFragment
 {
 private:
   void Init()
   {
     NS_ABORT_IF_FALSE(mNodeInfo->NodeType() ==
                       nsIDOMNode::DOCUMENT_FRAGMENT_NODE &&
                       mNodeInfo->Equals(nsGkAtoms::documentFragmentNodeName,
                                         kNameSpaceID_None),
@@ -122,36 +121,36 @@ public:
     return;
   }
 
   virtual Element* GetNameSpaceElement()
   {
     return nullptr;
   }
 
-  HTMLTemplateElement* GetHost() const
+  nsIContent* GetHost() const
   {
     return mHost;
   }
 
-  void SetHost(HTMLTemplateElement* aHost)
+  void SetHost(nsIContent* aHost)
   {
     mHost = aHost;
   }
 
   static already_AddRefed<DocumentFragment>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
 
 #ifdef DEBUG
   virtual void List(FILE* out, int32_t aIndent) const MOZ_OVERRIDE;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const MOZ_OVERRIDE;
 #endif
 
 protected:
   nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
-  mozilla::dom::HTMLTemplateElement* mHost; // Weak
+  nsIContent* mHost; // Weak
 };
 
 } // namespace dom
 } // namespace mozilla
 
 
 #endif // mozilla_dom_DocumentFragment_h__
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -13,17 +13,16 @@
 #include "mozilla/dom/ElementInlines.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/Attr.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIAtom.h"
 #include "nsIContentInlines.h"
 #include "nsINodeInfo.h"
-#include "nsIDocumentEncoder.h"
 #include "nsIDocumentInlines.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMDocument.h"
 #include "nsIContentIterator.h"
 #include "nsEventListenerManager.h"
 #include "nsFocusManager.h"
 #include "nsILinkHandler.h"
 #include "nsIScriptGlobalObject.h"
@@ -97,17 +96,16 @@
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIScrollableFrame.h"
 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleProcessorData.h"
 #include "nsAsyncDOMEvent.h"
 #include "nsTextNode.h"
-#include "mozilla/dom/HTMLTemplateElement.h"
 
 #ifdef MOZ_XUL
 #include "nsIXULDocument.h"
 #endif /* MOZ_XUL */
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCCUncollectableMarker.h"
 
@@ -118,20 +116,20 @@
 #include "nsDOMMutationObserver.h"
 #include "nsSVGFeatures.h"
 #include "nsWrapperCacheInlines.h"
 #include "xpcpublic.h"
 #include "nsIScriptError.h"
 #include "mozilla/Telemetry.h"
 
 #include "mozilla/CORSMode.h"
+#include "mozilla/dom/ShadowRoot.h"
 
 #include "nsStyledElement.h"
 #include "nsXBLService.h"
-#include "nsContentCID.h"
 #include "nsITextControlElement.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/dom/DocumentFragment.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMETHODIMP
@@ -681,16 +679,106 @@ Element::GetClientRects()
           nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder,
           nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
   return rectList.forget();
 }
 
 
 //----------------------------------------------------------------------
 
+void
+Element::AddToIdTable(nsIAtom* aId)
+{
+  NS_ASSERTION(HasID(), "Node doesn't have an ID?");
+  if (HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    ShadowRoot* containingShadow = GetContainingShadow();
+    containingShadow->AddToIdTable(this, aId);
+  } else {
+    nsIDocument* doc = GetCurrentDoc();
+    if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) {
+      doc->AddToIdTable(this, aId);
+    }
+  }
+}
+
+void
+Element::RemoveFromIdTable()
+{
+  if (HasID()) {
+    if (HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+      ShadowRoot* containingShadow = GetContainingShadow();
+      // Check for containingShadow because it may have
+      // been deleted during unlinking.
+      if (containingShadow) {
+        containingShadow->RemoveFromIdTable(this, DoGetID());
+      }
+    } else {
+      nsIDocument* doc = GetCurrentDoc();
+      if (doc) {
+        nsIAtom* id = DoGetID();
+        // id can be null during mutation events evilness. Also, XUL elements
+        // loose their proto attributes during cc-unlink, so this can happen
+        // during cc-unlink too.
+        if (id) {
+          doc->RemoveFromIdTable(this, DoGetID());
+        }
+      }
+    }
+  }
+}
+
+already_AddRefed<ShadowRoot>
+Element::CreateShadowRoot(ErrorResult& aError)
+{
+  nsAutoScriptBlocker scriptBlocker;
+
+  nsCOMPtr<nsINodeInfo> nodeInfo;
+  nodeInfo = mNodeInfo->NodeInfoManager()->GetNodeInfo(
+    nsGkAtoms::documentFragmentNodeName, nullptr, kNameSpaceID_None,
+    nsIDOMNode::DOCUMENT_FRAGMENT_NODE);
+
+  nsRefPtr<nsXBLDocumentInfo> docInfo = new nsXBLDocumentInfo(OwnerDoc());
+
+  nsXBLPrototypeBinding* protoBinding = new nsXBLPrototypeBinding();
+  aError = protoBinding->Init(NS_LITERAL_CSTRING("shadowroot"),
+                              docInfo, this, true);
+  if (aError.Failed()) {
+    delete protoBinding;
+    return nullptr;
+  }
+
+  // Unlike for XBL, false is the default for inheriting style.
+  protoBinding->SetInheritsStyle(false);
+
+  // Calling SetPrototypeBinding takes ownership of protoBinding.
+  docInfo->SetPrototypeBinding(NS_LITERAL_CSTRING("shadowroot"), protoBinding);
+
+  nsRefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget(),
+                                                   protoBinding);
+  SetShadowRoot(shadowRoot);
+
+  // xblBinding takes ownership of docInfo.
+  nsRefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding);
+  shadowRoot->SetAssociatedBinding(xblBinding);
+  xblBinding->SetBoundElement(this);
+
+  SetXBLBinding(xblBinding);
+
+  // Recreate the frame for the bound content because binding a ShadowRoot
+  // changes how things are rendered.
+  nsIDocument* doc = GetCurrentDoc();
+  if (doc) {
+    nsIPresShell *shell = doc->GetShell();
+    if (shell) {
+      shell->RecreateFramesFor(this);
+    }
+  }
+
+  return shadowRoot.forget();
+}
 
 void
 Element::GetAttribute(const nsAString& aName, DOMString& aReturn)
 {
   const nsAttrValue* val =
     mAttrsAndChildren.GetAttr(aName,
                               IsHTML() && IsInHTMLDocument() ?
                                 eIgnoreCase : eCaseMatters);
@@ -992,16 +1080,23 @@ Element::BindToTree(nsIDocument* aDocume
                "non-native anonymous parent!");
   if (aParent) {
     if (aParent->IsInNativeAnonymousSubtree()) {
       SetFlags(NODE_IS_IN_ANONYMOUS_SUBTREE);
     }
     if (aParent->HasFlag(NODE_CHROME_ONLY_ACCESS)) {
       SetFlags(NODE_CHROME_ONLY_ACCESS);
     }
+    if (aParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+      SetFlags(NODE_IS_IN_SHADOW_TREE);
+    }
+    ShadowRoot* parentContainingShadow = aParent->GetContainingShadow();
+    if (parentContainingShadow) {
+      DOMSlots()->mContainingShadow = parentContainingShadow;
+    }
   }
 
   bool hadForceXBL = HasFlag(NODE_FORCE_XBL_BINDINGS);
 
   // Now set the parent and set the "Force attach xbl" flag if needed.
   if (aParent) {
     if (!GetParent()) {
       NS_ADDREF(aParent);
@@ -1085,18 +1180,18 @@ Element::BindToTree(nsIDocument* aDocume
        child = child->GetNextSibling()) {
     rv = child->BindToTree(aDocument, this, aBindingParent,
                            aCompileEventHandlers);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsNodeUtils::ParentChainChanged(this);
 
-  if (aDocument && HasID() && !aBindingParent) {
-    aDocument->AddToIdTable(this, DoGetID());
+  if (HasID()) {
+    AddToIdTable(DoGetID());
   }
 
   if (MayHaveStyle() && !IsXUL()) {
     // XXXbz if we already have a style attr parsed, this won't do
     // anything... need to fix that.
     // If MayHaveStyle() is true, we must be an nsStyledElement
     static_cast<nsStyledElement*>(this)->ReparseStyleAttribute(false);
   }
@@ -1205,29 +1300,30 @@ Element::UnbindFromTree(bool aDeep, bool
     DeleteProperty(nsGkAtoms::transitionsOfAfterProperty);
     DeleteProperty(nsGkAtoms::transitionsProperty);
     DeleteProperty(nsGkAtoms::animationsOfBeforeProperty);
     DeleteProperty(nsGkAtoms::animationsOfAfterProperty);
     DeleteProperty(nsGkAtoms::animationsProperty);
   }
 
   // Unset this since that's what the old code effectively did.
-  UnsetFlags(NODE_FORCE_XBL_BINDINGS);
+  UnsetFlags(NODE_FORCE_XBL_BINDINGS | NODE_IS_IN_SHADOW_TREE);
   
 #ifdef MOZ_XUL
   nsXULElement* xulElem = nsXULElement::FromContent(this);
   if (xulElem) {
     xulElem->SetXULBindingParent(nullptr);
   }
   else
 #endif
   {
     nsDOMSlots *slots = GetExistingDOMSlots();
     if (slots) {
       slots->mBindingParent = nullptr;
+      slots->mContainingShadow = nullptr;
     }
   }
 
   // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree, 
   //  because it has to happen after unsetting the parent pointer, but before
   //  recursively unbinding the kids.
   if (IsHTML()) {
     ResetDir(this);
@@ -2474,803 +2570,27 @@ Element::MozRequestFullScreen()
     return;
   }
 
   OwnerDoc()->AsyncRequestFullScreen(this);
 
   return;
 }
 
-// Try to keep the size of StringBuilder close to a jemalloc bucket size.
-#define STRING_BUFFER_UNITS 1020
-
-namespace {
-
-// We put StringBuilder in the anonymous namespace to prevent anything outside
-// this file from accidentally being linked against it.
-
-class StringBuilder
-{
-private:
-  class Unit
-  {
-  public:
-    Unit() : mAtom(nullptr), mType(eUnknown), mLength(0)
-    {
-      MOZ_COUNT_CTOR(StringBuilder::Unit);
-    }
-    ~Unit()
-    {
-      if (mType == eString || mType == eStringWithEncode) {
-        delete mString;
-      }
-      MOZ_COUNT_DTOR(StringBuilder::Unit);
-    }
-
-    enum Type
-    {
-      eUnknown,
-      eAtom,
-      eString,
-      eStringWithEncode,
-      eLiteral,
-      eTextFragment,
-      eTextFragmentWithEncode,
-    };
-
-    union
-    {
-      nsIAtom*              mAtom;
-      const char*           mLiteral;
-      nsAutoString*         mString;
-      const nsTextFragment* mTextFragment;
-    };
-    Type     mType;
-    uint32_t mLength;
-  };
-public:
-  StringBuilder() : mLast(MOZ_THIS_IN_INITIALIZER_LIST()), mLength(0)
-  {
-    MOZ_COUNT_CTOR(StringBuilder);
-  }
-
-  ~StringBuilder()
-  {
-    MOZ_COUNT_DTOR(StringBuilder);
-  }
-
-  void Append(nsIAtom* aAtom)
-  {
-    Unit* u = AddUnit();
-    u->mAtom = aAtom;
-    u->mType = Unit::eAtom;
-    uint32_t len = aAtom->GetLength();
-    u->mLength = len;
-    mLength += len;
-  }
-
-  template<int N>
-  void Append(const char (&aLiteral)[N])
-  {
-    Unit* u = AddUnit();
-    u->mLiteral = aLiteral;
-    u->mType = Unit::eLiteral;
-    uint32_t len = N - 1;
-    u->mLength = len;
-    mLength += len;
-  }
-
-  template<int N>
-  void Append(char (&aLiteral)[N])
-  {
-    Unit* u = AddUnit();
-    u->mLiteral = aLiteral;
-    u->mType = Unit::eLiteral;
-    uint32_t len = N - 1;
-    u->mLength = len;
-    mLength += len;
-  }
-
-  void Append(const nsAString& aString)
-  {
-    Unit* u = AddUnit();
-    u->mString = new nsAutoString(aString);
-    u->mType = Unit::eString;
-    uint32_t len = aString.Length();
-    u->mLength = len;
-    mLength += len;
-  }
-
-  void Append(nsAutoString* aString)
-  {
-    Unit* u = AddUnit();
-    u->mString = aString;
-    u->mType = Unit::eString;
-    uint32_t len = aString->Length();
-    u->mLength = len;
-    mLength += len;
-  }
-
-  void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen)
-  {
-    Unit* u = AddUnit();
-    u->mString = aString;
-    u->mType = Unit::eStringWithEncode;
-    u->mLength = aLen;
-    mLength += aLen;
-  }
-
-  void Append(const nsTextFragment* aTextFragment)
-  {
-    Unit* u = AddUnit();
-    u->mTextFragment = aTextFragment;
-    u->mType = Unit::eTextFragment;
-    uint32_t len = aTextFragment->GetLength();
-    u->mLength = len;
-    mLength += len;
-  }
-
-  void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen)
-  {
-    Unit* u = AddUnit();
-    u->mTextFragment = aTextFragment;
-    u->mType = Unit::eTextFragmentWithEncode;
-    u->mLength = aLen;
-    mLength += aLen;
-  }
-
-  bool ToString(nsAString& aOut)
-  {
-    if (!aOut.SetCapacity(mLength, fallible_t())) {
-      return false;
-    }
-
-    for (StringBuilder* current = this; current; current = current->mNext) {
-      uint32_t len = current->mUnits.Length();
-      for (uint32_t i = 0; i < len; ++i) {
-        Unit& u = current->mUnits[i];
-        switch (u.mType) {
-          case Unit::eAtom:
-            aOut.Append(nsDependentAtomString(u.mAtom));
-            break;
-          case Unit::eString:
-            aOut.Append(*(u.mString));
-            break;
-          case Unit::eStringWithEncode:
-            EncodeAttrString(*(u.mString), aOut);
-            break;
-          case Unit::eLiteral:
-            aOut.AppendASCII(u.mLiteral, u.mLength);
-            break;
-          case Unit::eTextFragment:
-            u.mTextFragment->AppendTo(aOut);
-            break;
-          case Unit::eTextFragmentWithEncode:
-            EncodeTextFragment(u.mTextFragment, aOut);
-            break;
-          default:
-            MOZ_CRASH("Unknown unit type?");
-        }
-      }
-    }
-    return true;
-  }
-private:
-  Unit* AddUnit()
-  {
-    if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
-      new StringBuilder(this);
-    }
-    return mLast->mUnits.AppendElement();
-  }
-
-  StringBuilder(StringBuilder* aFirst)
-  : mLast(nullptr), mLength(0)
-  {
-    MOZ_COUNT_CTOR(StringBuilder);
-    aFirst->mLast->mNext = this;
-    aFirst->mLast = this;
-  }
-
-  void EncodeAttrString(const nsAutoString& aValue, nsAString& aOut)
-  {
-    const PRUnichar* c = aValue.BeginReading();
-    const PRUnichar* end = aValue.EndReading();
-    while (c < end) {
-      switch (*c) {
-      case '"':
-        aOut.AppendLiteral("&quot;");
-        break;
-      case '&':
-        aOut.AppendLiteral("&amp;");
-        break;
-      case 0x00A0:
-        aOut.AppendLiteral("&nbsp;");
-        break;
-      default:
-        aOut.Append(*c);
-        break;
-      }
-      ++c;
-    }
-  }
-
-  void EncodeTextFragment(const nsTextFragment* aValue, nsAString& aOut)
-  {
-    uint32_t len = aValue->GetLength();
-    if (aValue->Is2b()) {
-      const PRUnichar* data = aValue->Get2b();
-      for (uint32_t i = 0; i < len; ++i) {
-        const PRUnichar c = data[i];
-        switch (c) {
-          case '<':
-            aOut.AppendLiteral("&lt;");
-            break;
-          case '>':
-            aOut.AppendLiteral("&gt;");
-            break;
-          case '&':
-            aOut.AppendLiteral("&amp;");
-            break;
-          case 0x00A0:
-            aOut.AppendLiteral("&nbsp;");
-            break;
-          default:
-            aOut.Append(c);
-            break;
-        }
-      }
-    } else {
-      const char* data = aValue->Get1b();
-      for (uint32_t i = 0; i < len; ++i) {
-        const unsigned char c = data[i];
-        switch (c) {
-          case '<':
-            aOut.AppendLiteral("&lt;");
-            break;
-          case '>':
-            aOut.AppendLiteral("&gt;");
-            break;
-          case '&':
-            aOut.AppendLiteral("&amp;");
-            break;
-          case 0x00A0:
-            aOut.AppendLiteral("&nbsp;");
-            break;
-          default:
-            aOut.Append(c);
-            break;
-        }
-      }
-    }
-  }
-
-  nsAutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
-  nsAutoPtr<StringBuilder>                mNext;
-  StringBuilder*                          mLast;
-  // mLength is used only in the first StringBuilder object in the linked list.
-  uint32_t                                mLength;
-};
-
-} // anonymous namespace
-
-static void
-AppendEncodedCharacters(const nsTextFragment* aText, StringBuilder& aBuilder)
-{
-  uint32_t extraSpaceNeeded = 0;
-  uint32_t len = aText->GetLength();
-  if (aText->Is2b()) {
-    const PRUnichar* data = aText->Get2b();
-    for (uint32_t i = 0; i < len; ++i) {
-      const PRUnichar c = data[i];
-      switch (c) {
-        case '<':
-          extraSpaceNeeded += ArrayLength("&lt;") - 2;
-          break;
-        case '>':
-          extraSpaceNeeded += ArrayLength("&gt;") - 2;
-          break;
-        case '&':
-          extraSpaceNeeded += ArrayLength("&amp;") - 2;
-          break;
-        case 0x00A0:
-          extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
-          break;
-        default:
-          break;
-      }
-    }
-  } else {
-    const char* data = aText->Get1b();
-    for (uint32_t i = 0; i < len; ++i) {
-      const unsigned char c = data[i];
-      switch (c) {
-        case '<':
-          extraSpaceNeeded += ArrayLength("&lt;") - 2;
-          break;
-        case '>':
-          extraSpaceNeeded += ArrayLength("&gt;") - 2;
-          break;
-        case '&':
-          extraSpaceNeeded += ArrayLength("&amp;") - 2;
-          break;
-        case 0x00A0:
-          extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
-          break;
-        default:
-          break;
-      }
-    }
-  }
-
-  if (extraSpaceNeeded) {
-    aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
-  } else {
-    aBuilder.Append(aText);
-  }
-}
-
-static void
-AppendEncodedAttributeValue(nsAutoString* aValue, StringBuilder& aBuilder)
-{
-  const PRUnichar* c = aValue->BeginReading();
-  const PRUnichar* end = aValue->EndReading();
-
-  uint32_t extraSpaceNeeded = 0;
-  while (c < end) {
-    switch (*c) {
-      case '"':
-        extraSpaceNeeded += ArrayLength("&quot;") - 2;
-        break;
-      case '&':
-        extraSpaceNeeded += ArrayLength("&amp;") - 2;
-        break;
-      case 0x00A0:
-        extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
-        break;
-      default:
-        break;
-    }
-    ++c;
-  }
-
-  if (extraSpaceNeeded) {
-    aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
-  } else {
-    aBuilder.Append(aValue);
-  }
-}
-
-static void
-StartElement(Element* aContent, StringBuilder& aBuilder)
-{
-  nsIAtom* localName = aContent->Tag();
-  int32_t tagNS = aContent->GetNameSpaceID();
-
-  aBuilder.Append("<");
-  if (aContent->IsHTML() || aContent->IsSVG() || aContent->IsMathML()) {
-    aBuilder.Append(localName);
-  } else {
-    aBuilder.Append(aContent->NodeName());
-  }
-
-  int32_t count = aContent->GetAttrCount();
-  for (int32_t i = count; i > 0;) {
-    --i;
-    const nsAttrName* name = aContent->GetAttrNameAt(i);
-    int32_t attNs = name->NamespaceID();
-    nsIAtom* attName = name->LocalName();
-
-    // Filter out any attribute starting with [-|_]moz
-    nsDependentAtomString attrNameStr(attName);
-    if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
-        StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
-      continue;
-    }
-
-    nsAutoString* attValue = new nsAutoString();
-    aContent->GetAttr(attNs, attName, *attValue);
-
-    // Filter out special case of <br type="_moz*"> used by the editor.
-    // Bug 16988.  Yuck.
-    if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
-        attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
-        StringBeginsWith(*attValue, NS_LITERAL_STRING("_moz"))) {
-      delete attValue;
-      continue;
-    }
-    
-    if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
-        (attNs == kNameSpaceID_XMLNS &&
-         attName == nsGkAtoms::xmlns)) {
-      aBuilder.Append(" ");
-    } else if (attNs == kNameSpaceID_XML) {
-      aBuilder.Append(" xml:");
-    } else if (attNs == kNameSpaceID_XMLNS) {
-      aBuilder.Append(" xmlns:");
-    } else if (attNs == kNameSpaceID_XLink) {
-      aBuilder.Append(" xlink:");
-    } else {
-      nsIAtom* prefix = name->GetPrefix();
-      if (prefix) {
-        aBuilder.Append(" ");
-        aBuilder.Append(prefix);
-        aBuilder.Append(":");
-      }
-    }
-
-    aBuilder.Append(attName);
-    aBuilder.Append("=\"");
-    AppendEncodedAttributeValue(attValue, aBuilder);
-    aBuilder.Append("\"");
-  }
-
-  aBuilder.Append(">");
-
-  /*
-  // Per HTML spec we should append one \n if the first child of
-  // pre/textarea/listing is a textnode and starts with a \n.
-  // But because browsers haven't traditionally had that behavior,
-  // we're not changing our behavior either - yet.
-  if (aContent->IsHTML()) {
-    if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
-        localName == nsGkAtoms::listing) {
-      nsIContent* fc = aContent->GetFirstChild();
-      if (fc &&
-          (fc->NodeType() == nsIDOMNode::TEXT_NODE ||
-           fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
-        const nsTextFragment* text = fc->GetText();
-        if (text && text->GetLength() && text->CharAt(0) == PRUnichar('\n')) {
-          aBuilder.Append("\n");
-        }
-      }
-    }
-  }*/
-}
-
-static inline bool
-ShouldEscape(nsIContent* aParent)
-{
-  if (!aParent || !aParent->IsHTML()) {
-    return true;
-  }
-
-  static const nsIAtom* nonEscapingElements[] = {
-    nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
-    nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
-    nsGkAtoms::plaintext, 
-    // Per the current spec noscript should be escaped in case
-    // scripts are disabled or if document doesn't have
-    // browsing context. However the latter seems to be a spec bug
-    // and Gecko hasn't traditionally done the former.
-    nsGkAtoms::noscript    
-  };
-  static mozilla::BloomFilter<12, nsIAtom> sFilter;
-  static bool sInitialized = false;
-  if (!sInitialized) {
-    sInitialized = true;
-    for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
-      sFilter.add(nonEscapingElements[i]);
-    }
-  }
-
-  nsIAtom* tag = aParent->Tag();
-  if (sFilter.mightContain(tag)) {
-    for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
-      if (tag == nonEscapingElements[i]) {
-        return false;
-      }
-    }
-  }
-  return true;
-}
-
-static inline bool
-IsVoidTag(Element* aElement)
-{
-  if (!aElement->IsHTML()) {
-    return false;
-  }
-
-  static const nsIAtom* voidElements[] = {
-    nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
-    nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
-    nsGkAtoms::command, nsGkAtoms::embed, nsGkAtoms::frame,
-    nsGkAtoms::hr, nsGkAtoms::img, nsGkAtoms::input,
-    nsGkAtoms::keygen, nsGkAtoms::link, nsGkAtoms::meta,
-    nsGkAtoms::param, nsGkAtoms::source, nsGkAtoms::track,
-    nsGkAtoms::wbr
-  };
-
-  static mozilla::BloomFilter<12, nsIAtom> sFilter;
-  static bool sInitialized = false;
-  if (!sInitialized) {
-    sInitialized = true;
-    for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
-      sFilter.add(voidElements[i]);
-    }
-  }
-  
-  nsIAtom* tag = aElement->Tag();
-  if (sFilter.mightContain(tag)) {
-    for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
-      if (tag == voidElements[i]) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-static bool
-Serialize(Element* aRoot, bool aDescendentsOnly, nsAString& aOut)
-{
-  nsINode* current = aDescendentsOnly ?
-    nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
-
-  if (!current) {
-    return true;
-  }
-
-  StringBuilder builder;
-  nsIContent* next;
-  while (true) {
-    bool isVoid = false;
-    switch (current->NodeType()) {
-      case nsIDOMNode::ELEMENT_NODE: {
-        Element* elem = current->AsElement();
-        StartElement(elem, builder);
-        isVoid = IsVoidTag(elem);
-        if (!isVoid &&
-            (next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
-          current = next;
-          continue;
-        }
-        break;
-      }
-
-      case nsIDOMNode::TEXT_NODE:
-      case nsIDOMNode::CDATA_SECTION_NODE: {
-        const nsTextFragment* text = static_cast<nsIContent*>(current)->GetText();
-        nsIContent* parent = current->GetParent();
-        if (ShouldEscape(parent)) {
-          AppendEncodedCharacters(text, builder);
-        } else {
-          builder.Append(text);
-        }
-        break;
-      }
-
-      case nsIDOMNode::COMMENT_NODE: {
-        builder.Append("<!--");
-        builder.Append(static_cast<nsIContent*>(current)->GetText());
-        builder.Append("-->");
-        break;
-      }
-
-      case nsIDOMNode::DOCUMENT_TYPE_NODE: {
-        builder.Append("<!DOCTYPE ");
-        builder.Append(current->NodeName());
-        builder.Append(">");
-        break;
-      }
-
-      case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: {
-        builder.Append("<?");
-        builder.Append(current->NodeName());
-        builder.Append(" ");
-        builder.Append(static_cast<nsIContent*>(current)->GetText());
-        builder.Append(">");
-        break;
-      }
-    }
-
-    while (true) {
-      if (!isVoid && current->NodeType() == nsIDOMNode::ELEMENT_NODE) {
-        builder.Append("</");
-        nsIContent* elem = static_cast<nsIContent*>(current);
-        if (elem->IsHTML() || elem->IsSVG() || elem->IsMathML()) {
-          builder.Append(elem->Tag());
-        } else {
-          builder.Append(current->NodeName());
-        }
-        builder.Append(">");
-      }
-      isVoid = false;
-
-      if (current == aRoot) {
-        return builder.ToString(aOut);
-      }
-
-      if ((next = current->GetNextSibling())) {
-        current = next;
-        break;
-      }
-
-      current = current->GetParentNode();
-
-      // Template case, if we are in a template's content, then the parent
-      // should be the host template element.
-      if (current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
-        DocumentFragment* frag = static_cast<DocumentFragment*>(current);
-        HTMLTemplateElement* fragHost = frag->GetHost();
-        if (fragHost) {
-          current = fragHost;
-        }
-      }
-
-      if (aDescendentsOnly && current == aRoot) {
-        return builder.ToString(aOut);
-      }
-    }
-  }
-}
-
-void
-Element::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
-{
-  aMarkup.Truncate();
-
-  nsIDocument* doc = OwnerDoc();
-  if (IsInHTMLDocument()) {
-    Serialize(this, !aIncludeSelf, aMarkup);
-    return;
-  }
-
-  nsAutoString contentType;
-  doc->GetContentType(contentType);
-  bool tryToCacheEncoder = !aIncludeSelf;
-
-  nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
-  if (!docEncoder) {
-    docEncoder =
-      do_CreateInstance(PromiseFlatCString(
-        nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
-        NS_ConvertUTF16toUTF8(contentType)
-      ).get());
-  }
-  if (!docEncoder) {
-    // This could be some type for which we create a synthetic document.  Try
-    // again as XML
-    contentType.AssignLiteral("application/xml");
-    docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
-    // Don't try to cache the encoder since it would point to a different
-    // contentType once it has been reinitialized.
-    tryToCacheEncoder = false;
-  }
-
-  NS_ENSURE_TRUE_VOID(docEncoder);
-
-  uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
-                   // Output DOM-standard newlines
-                   nsIDocumentEncoder::OutputLFLineBreak |
-                   // Don't do linebreaking that's not present in
-                   // the source
-                   nsIDocumentEncoder::OutputRaw |
-                   // Only check for mozdirty when necessary (bug 599983)
-                   nsIDocumentEncoder::OutputIgnoreMozDirty;
-
-  if (IsEditable()) {
-    nsIEditor* editor = GetEditorInternal();
-    if (editor && editor->OutputsMozDirty()) {
-      flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
-    }
-  }
-
-  DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-
-  if (aIncludeSelf) {
-    docEncoder->SetNativeNode(this);
-  } else {
-    docEncoder->SetNativeContainerNode(this);
-  }
-  rv = docEncoder->EncodeToString(aMarkup);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-  if (tryToCacheEncoder) {
-    doc->SetCachedEncoder(docEncoder.forget());
-  }
-}
-
-/**
- * Fire mutation events for changes caused by parsing directly into a
- * context node.
- *
- * @param aDoc the document of the node
- * @param aDest the destination node that got stuff appended to it
- * @param aOldChildCount the number of children the node had before parsing
- */
-static void
-FireMutationEventsForDirectParsing(nsIDocument* aDoc, nsIContent* aDest,
-                                   int32_t aOldChildCount)
-{
-  // Fire mutation events. Optimize for the case when there are no listeners
-  int32_t newChildCount = aDest->GetChildCount();
-  if (newChildCount && nsContentUtils::
-        HasMutationListeners(aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
-    nsAutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
-    NS_ASSERTION(newChildCount - aOldChildCount >= 0,
-                 "What, some unexpected dom mutation has happened?");
-    childNodes.SetCapacity(newChildCount - aOldChildCount);
-    for (nsIContent* child = aDest->GetFirstChild();
-         child;
-         child = child->GetNextSibling()) {
-      childNodes.AppendElement(child);
-    }
-    Element::FireNodeInserted(aDoc, aDest, childNodes);
-  }
-}
-
 NS_IMETHODIMP
 Element::GetInnerHTML(nsAString& aInnerHTML)
 {
   GetMarkup(false, aInnerHTML);
   return NS_OK;
 }
 
 void
 Element::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError)
 {
-  FragmentOrElement* target = this;
-  // Handle template case.
-  if (nsNodeUtils::IsTemplateElement(target)) {
-    DocumentFragment* frag =
-      static_cast<HTMLTemplateElement*>(target)->Content();
-    MOZ_ASSERT(frag);
-    target = frag;
-  }
-
-  nsIDocument* doc = target->OwnerDoc();
-
-  // Batch possible DOMSubtreeModified events.
-  mozAutoSubtreeModified subtree(doc, nullptr);
-
-  target->FireNodeRemovedForChildren();
-
-  // Needed when innerHTML is used in combination with contenteditable
-  mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
-
-  // Remove childnodes.
-  uint32_t childCount = target->GetChildCount();
-  nsAutoMutationBatch mb(target, true, false);
-  for (uint32_t i = 0; i < childCount; ++i) {
-    target->RemoveChildAt(0, true);
-  }
-  mb.RemovalDone();
-
-  nsAutoScriptLoaderDisabler sld(doc);
-
-  if (doc->IsHTML()) {
-    int32_t oldChildCount = target->GetChildCount();
-    aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
-                                               target,
-                                               Tag(),
-                                               GetNameSpaceID(),
-                                               doc->GetCompatibilityMode() ==
-                                                 eCompatibility_NavQuirks,
-                                               true);
-    mb.NodesAdded();
-    // HTML5 parser has notified, but not fired mutation events.
-    FireMutationEventsForDirectParsing(doc, target, oldChildCount);
-  } else {
-    nsCOMPtr<nsIDOMDocumentFragment> df;
-    aError = nsContentUtils::CreateContextualFragment(target, aInnerHTML,
-                                                      true,
-                                                      getter_AddRefs(df));
-    nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
-    if (!aError.Failed()) {
-      // Suppress assertion about node removal mutation events that can't have
-      // listeners anyway, because no one has had the chance to register mutation
-      // listeners on the fragment that comes from the parser.
-      nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
-
-      static_cast<nsINode*>(target)->AppendChild(*fragment, aError);
-      mb.NodesAdded();
-    }
-  }
+  SetInnerHTMLInternal(aInnerHTML, aError);
 }
 
 void
 Element::GetOuterHTML(nsAString& aOuterHTML)
 {
   GetMarkup(true, aOuterHTML);
 }
 
@@ -3400,17 +2720,18 @@ Element::InsertAdjacentHTML(const nsAStr
     aError = nsContentUtils::ParseFragmentHTML(aText,
                                                destination,
                                                contextLocal,
                                                contextNs,
                                                doc->GetCompatibilityMode() ==
                                                  eCompatibility_NavQuirks,
                                                true);
     // HTML5 parser has notified, but not fired mutation events.
-    FireMutationEventsForDirectParsing(doc, destination, oldChildCount);
+    nsContentUtils::FireMutationEventsForDirectParsing(doc, destination,
+                                                       oldChildCount);
     return;
   }
 
   // couldn't parse directly
   nsCOMPtr<nsIDOMDocumentFragment> df;
   aError = nsContentUtils::CreateContextualFragment(destination,
                                                     aText,
                                                     true,
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -16,16 +16,17 @@
 
 #include "mozilla/dom/FragmentOrElement.h"
 
 #include "mozilla/dom/Attr.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIAtom.h"
 #include "nsINodeInfo.h"
 #include "nsIDocumentInlines.h"
+#include "nsIDocumentEncoder.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMDocument.h"
 #include "nsIContentIterator.h"
 #include "nsEventListenerManager.h"
 #include "nsFocusManager.h"
 #include "nsILinkHandler.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIURL.h"
@@ -68,16 +69,18 @@
 #include "nsBindingManager.h"
 #include "nsXBLBinding.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIBoxObject.h"
 #include "nsSVGUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsGkAtoms.h"
 #include "nsContentUtils.h"
+#include "nsTextFragment.h"
+#include "nsContentCID.h"
 
 #include "nsIDOMEventListener.h"
 #include "nsIWebNavigation.h"
 #include "nsIBaseWindow.h"
 #include "nsIWidget.h"
 
 #include "js/GCAPI.h"
 
@@ -115,16 +118,19 @@
 #include "nsWrapperCacheInlines.h"
 #include "nsCycleCollector.h"
 #include "xpcpublic.h"
 #include "nsIScriptError.h"
 #include "mozilla/Telemetry.h"
 
 #include "mozilla/CORSMode.h"
 
+#include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/dom/HTMLTemplateElement.h"
+
 #include "nsStyledElement.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 int32_t nsIContent::sTabFocusModel = eTabFocus_any;
 bool nsIContent::sTabFocusModelAppliesToXUL = false;
 uint32_t nsMutationGuard::sMutationCount = 0;
@@ -541,16 +547,22 @@ FragmentOrElement::nsDOMSlots::Traverse(
   }
 
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLBinding");
   cb.NoteNativeChild(mXBLBinding, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
 
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
   cb.NoteXPCOMChild(mXBLInsertionParent.get());
 
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mShadowRoot");
+  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
+  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
+
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
   cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
 
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
   cb.NoteXPCOMChild(mClassList.get());
 }
 
 void
@@ -561,16 +573,18 @@ FragmentOrElement::nsDOMSlots::Unlink(bo
   if (mAttributeMap) {
     mAttributeMap->DropReference();
     mAttributeMap = nullptr;
   }
   if (aIsXUL)
     NS_IF_RELEASE(mControllers);
   mXBLBinding = nullptr;
   mXBLInsertionParent = nullptr;
+  mShadowRoot = nullptr;
+  mContainingShadow = nullptr;
   mChildrenList = nullptr;
   mUndoManager = nullptr;
   if (mClassList) {
     mClassList->DropReference();
     mClassList = nullptr;
   }
 }
 
@@ -955,16 +969,43 @@ FragmentOrElement::GetXBLInsertionParent
     if (slots) {
       return slots->mXBLInsertionParent;
     }
   }
 
   return nullptr;
 }
 
+ShadowRoot*
+FragmentOrElement::GetShadowRoot() const
+{
+  nsDOMSlots *slots = GetExistingDOMSlots();
+  if (slots) {
+    return slots->mShadowRoot;
+  }
+  return nullptr;
+}
+
+ShadowRoot*
+FragmentOrElement::GetContainingShadow() const
+{
+  nsDOMSlots *slots = GetExistingDOMSlots();
+  if (slots) {
+    return slots->mContainingShadow;
+  }
+  return nullptr;
+}
+
+void
+FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot)
+{
+  nsDOMSlots *slots = DOMSlots();
+  slots->mShadowRoot = aShadowRoot;
+}
+
 void
 FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
 {
   nsDOMSlots *slots = DOMSlots();
   if (aContent) {
     SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
   }
   slots->mXBLInsertionParent = aContent;
@@ -1860,16 +1901,781 @@ FragmentOrElement::GetChildArray(uint32_
 }
 
 int32_t
 FragmentOrElement::IndexOf(const nsINode* aPossibleChild) const
 {
   return mAttrsAndChildren.IndexOfChild(aPossibleChild);
 }
 
+// Try to keep the size of StringBuilder close to a jemalloc bucket size.
+#define STRING_BUFFER_UNITS 1020
+
+namespace {
+
+// We put StringBuilder in the anonymous namespace to prevent anything outside
+// this file from accidentally being linked against it.
+
+class StringBuilder
+{
+private:
+  class Unit
+  {
+  public:
+    Unit() : mAtom(nullptr), mType(eUnknown), mLength(0)
+    {
+      MOZ_COUNT_CTOR(StringBuilder::Unit);
+    }
+    ~Unit()
+    {
+      if (mType == eString || mType == eStringWithEncode) {
+        delete mString;
+      }
+      MOZ_COUNT_DTOR(StringBuilder::Unit);
+    }
+
+    enum Type
+    {
+      eUnknown,
+      eAtom,
+      eString,
+      eStringWithEncode,
+      eLiteral,
+      eTextFragment,
+      eTextFragmentWithEncode,
+    };
+
+    union
+    {
+      nsIAtom*              mAtom;
+      const char*           mLiteral;
+      nsAutoString*         mString;
+      const nsTextFragment* mTextFragment;
+    };
+    Type     mType;
+    uint32_t mLength;
+  };
+public:
+  StringBuilder() : mLast(MOZ_THIS_IN_INITIALIZER_LIST()), mLength(0)
+  {
+    MOZ_COUNT_CTOR(StringBuilder);
+  }
+
+  ~StringBuilder()
+  {
+    MOZ_COUNT_DTOR(StringBuilder);
+  }
+
+  void Append(nsIAtom* aAtom)
+  {
+    Unit* u = AddUnit();
+    u->mAtom = aAtom;
+    u->mType = Unit::eAtom;
+    uint32_t len = aAtom->GetLength();
+    u->mLength = len;
+    mLength += len;
+  }
+
+  template<int N>
+  void Append(const char (&aLiteral)[N])
+  {
+    Unit* u = AddUnit();
+    u->mLiteral = aLiteral;
+    u->mType = Unit::eLiteral;
+    uint32_t len = N - 1;
+    u->mLength = len;
+    mLength += len;
+  }
+
+  template<int N>
+  void Append(char (&aLiteral)[N])
+  {
+    Unit* u = AddUnit();
+    u->mLiteral = aLiteral;
+    u->mType = Unit::eLiteral;
+    uint32_t len = N - 1;
+    u->mLength = len;
+    mLength += len;
+  }
+
+  void Append(const nsAString& aString)
+  {
+    Unit* u = AddUnit();
+    u->mString = new nsAutoString(aString);
+    u->mType = Unit::eString;
+    uint32_t len = aString.Length();
+    u->mLength = len;
+    mLength += len;
+  }
+
+  void Append(nsAutoString* aString)
+  {
+    Unit* u = AddUnit();
+    u->mString = aString;
+    u->mType = Unit::eString;
+    uint32_t len = aString->Length();
+    u->mLength = len;
+    mLength += len;
+  }
+
+  void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen)
+  {
+    Unit* u = AddUnit();
+    u->mString = aString;
+    u->mType = Unit::eStringWithEncode;
+    u->mLength = aLen;
+    mLength += aLen;
+  }
+
+  void Append(const nsTextFragment* aTextFragment)
+  {
+    Unit* u = AddUnit();
+    u->mTextFragment = aTextFragment;
+    u->mType = Unit::eTextFragment;
+    uint32_t len = aTextFragment->GetLength();
+    u->mLength = len;
+    mLength += len;
+  }
+
+  void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen)
+  {
+    Unit* u = AddUnit();
+    u->mTextFragment = aTextFragment;
+    u->mType = Unit::eTextFragmentWithEncode;
+    u->mLength = aLen;
+    mLength += aLen;
+  }
+
+  bool ToString(nsAString& aOut)
+  {
+    if (!aOut.SetCapacity(mLength, fallible_t())) {
+      return false;
+    }
+
+    for (StringBuilder* current = this; current; current = current->mNext) {
+      uint32_t len = current->mUnits.Length();
+      for (uint32_t i = 0; i < len; ++i) {
+        Unit& u = current->mUnits[i];
+        switch (u.mType) {
+          case Unit::eAtom:
+            aOut.Append(nsDependentAtomString(u.mAtom));
+            break;
+          case Unit::eString:
+            aOut.Append(*(u.mString));
+            break;
+          case Unit::eStringWithEncode:
+            EncodeAttrString(*(u.mString), aOut);
+            break;
+          case Unit::eLiteral:
+            aOut.AppendASCII(u.mLiteral, u.mLength);
+            break;
+          case Unit::eTextFragment:
+            u.mTextFragment->AppendTo(aOut);
+            break;
+          case Unit::eTextFragmentWithEncode:
+            EncodeTextFragment(u.mTextFragment, aOut);
+            break;
+          default:
+            MOZ_CRASH("Unknown unit type?");
+        }
+      }
+    }
+    return true;
+  }
+private:
+  Unit* AddUnit()
+  {
+    if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
+      new StringBuilder(this);
+    }
+    return mLast->mUnits.AppendElement();
+  }
+
+  StringBuilder(StringBuilder* aFirst)
+  : mLast(nullptr), mLength(0)
+  {
+    MOZ_COUNT_CTOR(StringBuilder);
+    aFirst->mLast->mNext = this;
+    aFirst->mLast = this;
+  }
+
+  void EncodeAttrString(const nsAutoString& aValue, nsAString& aOut)
+  {
+    const PRUnichar* c = aValue.BeginReading();
+    const PRUnichar* end = aValue.EndReading();
+    while (c < end) {
+      switch (*c) {
+      case '"':
+        aOut.AppendLiteral("&quot;");
+        break;
+      case '&':
+        aOut.AppendLiteral("&amp;");
+        break;
+      case 0x00A0:
+        aOut.AppendLiteral("&nbsp;");
+        break;
+      default:
+        aOut.Append(*c);
+        break;
+      }
+      ++c;
+    }
+  }
+
+  void EncodeTextFragment(const nsTextFragment* aValue, nsAString& aOut)
+  {
+    uint32_t len = aValue->GetLength();
+    if (aValue->Is2b()) {
+      const PRUnichar* data = aValue->Get2b();
+      for (uint32_t i = 0; i < len; ++i) {
+        const PRUnichar c = data[i];
+        switch (c) {
+          case '<':
+            aOut.AppendLiteral("&lt;");
+            break;
+          case '>':
+            aOut.AppendLiteral("&gt;");
+            break;
+          case '&':
+            aOut.AppendLiteral("&amp;");
+            break;
+          case 0x00A0:
+            aOut.AppendLiteral("&nbsp;");
+            break;
+          default:
+            aOut.Append(c);
+            break;
+        }
+      }
+    } else {
+      const char* data = aValue->Get1b();
+      for (uint32_t i = 0; i < len; ++i) {
+        const unsigned char c = data[i];
+        switch (c) {
+          case '<':
+            aOut.AppendLiteral("&lt;");
+            break;
+          case '>':
+            aOut.AppendLiteral("&gt;");
+            break;
+          case '&':
+            aOut.AppendLiteral("&amp;");
+            break;
+          case 0x00A0:
+            aOut.AppendLiteral("&nbsp;");
+            break;
+          default:
+            aOut.Append(c);
+            break;
+        }
+      }
+    }
+  }
+
+  nsAutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
+  nsAutoPtr<StringBuilder>                mNext;
+  StringBuilder*                          mLast;
+  // mLength is used only in the first StringBuilder object in the linked list.
+  uint32_t                                mLength;
+};
+
+} // anonymous namespace
+
+static void
+AppendEncodedCharacters(const nsTextFragment* aText, StringBuilder& aBuilder)
+{
+  uint32_t extraSpaceNeeded = 0;
+  uint32_t len = aText->GetLength();
+  if (aText->Is2b()) {
+    const PRUnichar* data = aText->Get2b();
+    for (uint32_t i = 0; i < len; ++i) {
+      const PRUnichar c = data[i];
+      switch (c) {
+        case '<':
+          extraSpaceNeeded += ArrayLength("&lt;") - 2;
+          break;
+        case '>':
+          extraSpaceNeeded += ArrayLength("&gt;") - 2;
+          break;
+        case '&':
+          extraSpaceNeeded += ArrayLength("&amp;") - 2;
+          break;
+        case 0x00A0:
+          extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
+          break;
+        default:
+          break;
+      }
+    }
+  } else {
+    const char* data = aText->Get1b();
+    for (uint32_t i = 0; i < len; ++i) {
+      const unsigned char c = data[i];
+      switch (c) {
+        case '<':
+          extraSpaceNeeded += ArrayLength("&lt;") - 2;
+          break;
+        case '>':
+          extraSpaceNeeded += ArrayLength("&gt;") - 2;
+          break;
+        case '&':
+          extraSpaceNeeded += ArrayLength("&amp;") - 2;
+          break;
+        case 0x00A0:
+          extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
+          break;
+        default:
+          break;
+      }
+    }
+  }
+
+  if (extraSpaceNeeded) {
+    aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
+  } else {
+    aBuilder.Append(aText);
+  }
+}
+
+static void
+AppendEncodedAttributeValue(nsAutoString* aValue, StringBuilder& aBuilder)
+{
+  const PRUnichar* c = aValue->BeginReading();
+  const PRUnichar* end = aValue->EndReading();
+
+  uint32_t extraSpaceNeeded = 0;
+  while (c < end) {
+    switch (*c) {
+      case '"':
+        extraSpaceNeeded += ArrayLength("&quot;") - 2;
+        break;
+      case '&':
+        extraSpaceNeeded += ArrayLength("&amp;") - 2;
+        break;
+      case 0x00A0:
+        extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
+        break;
+      default:
+        break;
+    }
+    ++c;
+  }
+
+  if (extraSpaceNeeded) {
+    aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
+  } else {
+    aBuilder.Append(aValue);
+  }
+}
+
+static void
+StartElement(Element* aContent, StringBuilder& aBuilder)
+{
+  nsIAtom* localName = aContent->Tag();
+  int32_t tagNS = aContent->GetNameSpaceID();
+
+  aBuilder.Append("<");
+  if (aContent->IsHTML() || aContent->IsSVG() || aContent->IsMathML()) {
+    aBuilder.Append(localName);
+  } else {
+    aBuilder.Append(aContent->NodeName());
+  }
+
+  int32_t count = aContent->GetAttrCount();
+  for (int32_t i = count; i > 0;) {
+    --i;
+    const nsAttrName* name = aContent->GetAttrNameAt(i);
+    int32_t attNs = name->NamespaceID();
+    nsIAtom* attName = name->LocalName();
+
+    // Filter out any attribute starting with [-|_]moz
+    nsDependentAtomString attrNameStr(attName);
+    if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
+        StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
+      continue;
+    }
+
+    nsAutoString* attValue = new nsAutoString();
+    aContent->GetAttr(attNs, attName, *attValue);
+
+    // Filter out special case of <br type="_moz*"> used by the editor.
+    // Bug 16988.  Yuck.
+    if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
+        attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
+        StringBeginsWith(*attValue, NS_LITERAL_STRING("_moz"))) {
+      delete attValue;
+      continue;
+    }
+    
+    if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
+        (attNs == kNameSpaceID_XMLNS &&
+         attName == nsGkAtoms::xmlns)) {
+      aBuilder.Append(" ");
+    } else if (attNs == kNameSpaceID_XML) {
+      aBuilder.Append(" xml:");
+    } else if (attNs == kNameSpaceID_XMLNS) {
+      aBuilder.Append(" xmlns:");
+    } else if (attNs == kNameSpaceID_XLink) {
+      aBuilder.Append(" xlink:");
+    } else {
+      nsIAtom* prefix = name->GetPrefix();
+      if (prefix) {
+        aBuilder.Append(" ");
+        aBuilder.Append(prefix);
+        aBuilder.Append(":");
+      }
+    }
+
+    aBuilder.Append(attName);
+    aBuilder.Append("=\"");
+    AppendEncodedAttributeValue(attValue, aBuilder);
+    aBuilder.Append("\"");
+  }
+
+  aBuilder.Append(">");
+
+  /*
+  // Per HTML spec we should append one \n if the first child of
+  // pre/textarea/listing is a textnode and starts with a \n.
+  // But because browsers haven't traditionally had that behavior,
+  // we're not changing our behavior either - yet.
+  if (aContent->IsHTML()) {
+    if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
+        localName == nsGkAtoms::listing) {
+      nsIContent* fc = aContent->GetFirstChild();
+      if (fc &&
+          (fc->NodeType() == nsIDOMNode::TEXT_NODE ||
+           fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
+        const nsTextFragment* text = fc->GetText();
+        if (text && text->GetLength() && text->CharAt(0) == PRUnichar('\n')) {
+          aBuilder.Append("\n");
+        }
+      }
+    }
+  }*/
+}
+
+static inline bool
+ShouldEscape(nsIContent* aParent)
+{
+  if (!aParent || !aParent->IsHTML()) {
+    return true;
+  }
+
+  static const nsIAtom* nonEscapingElements[] = {
+    nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
+    nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
+    nsGkAtoms::plaintext, 
+    // Per the current spec noscript should be escaped in case
+    // scripts are disabled or if document doesn't have
+    // browsing context. However the latter seems to be a spec bug
+    // and Gecko hasn't traditionally done the former.
+    nsGkAtoms::noscript    
+  };
+  static mozilla::BloomFilter<12, nsIAtom> sFilter;
+  static bool sInitialized = false;
+  if (!sInitialized) {
+    sInitialized = true;
+    for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
+      sFilter.add(nonEscapingElements[i]);
+    }
+  }
+
+  nsIAtom* tag = aParent->Tag();
+  if (sFilter.mightContain(tag)) {
+    for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
+      if (tag == nonEscapingElements[i]) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+static inline bool
+IsVoidTag(Element* aElement)
+{
+  if (!aElement->IsHTML()) {
+    return false;
+  }
+
+  static const nsIAtom* voidElements[] = {
+    nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
+    nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
+    nsGkAtoms::command, nsGkAtoms::embed, nsGkAtoms::frame,
+    nsGkAtoms::hr, nsGkAtoms::img, nsGkAtoms::input,
+    nsGkAtoms::keygen, nsGkAtoms::link, nsGkAtoms::meta,
+    nsGkAtoms::param, nsGkAtoms::source, nsGkAtoms::track,
+    nsGkAtoms::wbr
+  };
+
+  static mozilla::BloomFilter<12, nsIAtom> sFilter;
+  static bool sInitialized = false;
+  if (!sInitialized) {
+    sInitialized = true;
+    for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
+      sFilter.add(voidElements[i]);
+    }
+  }
+  
+  nsIAtom* tag = aElement->Tag();
+  if (sFilter.mightContain(tag)) {
+    for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
+      if (tag == voidElements[i]) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+static bool
+Serialize(FragmentOrElement* aRoot, bool aDescendentsOnly, nsAString& aOut)
+{
+  nsINode* current = aDescendentsOnly ?
+    nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
+
+  if (!current) {
+    return true;
+  }
+
+  StringBuilder builder;
+  nsIContent* next;
+  while (true) {
+    bool isVoid = false;
+    switch (current->NodeType()) {
+      case nsIDOMNode::ELEMENT_NODE: {
+        Element* elem = current->AsElement();
+        StartElement(elem, builder);
+        isVoid = IsVoidTag(elem);
+        if (!isVoid &&
+            (next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
+          current = next;
+          continue;
+        }
+        break;
+      }
+
+      case nsIDOMNode::TEXT_NODE:
+      case nsIDOMNode::CDATA_SECTION_NODE: {
+        const nsTextFragment* text = static_cast<nsIContent*>(current)->GetText();
+        nsIContent* parent = current->GetParent();
+        if (ShouldEscape(parent)) {
+          AppendEncodedCharacters(text, builder);
+        } else {
+          builder.Append(text);
+        }
+        break;
+      }
+
+      case nsIDOMNode::COMMENT_NODE: {
+        builder.Append("<!--");
+        builder.Append(static_cast<nsIContent*>(current)->GetText());
+        builder.Append("-->");
+        break;
+      }
+
+      case nsIDOMNode::DOCUMENT_TYPE_NODE: {
+        builder.Append("<!DOCTYPE ");
+        builder.Append(current->NodeName());
+        builder.Append(">");
+        break;
+      }
+
+      case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: {
+        builder.Append("<?");
+        builder.Append(current->NodeName());
+        builder.Append(" ");
+        builder.Append(static_cast<nsIContent*>(current)->GetText());
+        builder.Append(">");
+        break;
+      }
+    }
+
+    while (true) {
+      if (!isVoid && current->NodeType() == nsIDOMNode::ELEMENT_NODE) {
+        builder.Append("</");
+        nsIContent* elem = static_cast<nsIContent*>(current);
+        if (elem->IsHTML() || elem->IsSVG() || elem->IsMathML()) {
+          builder.Append(elem->Tag());
+        } else {
+          builder.Append(current->NodeName());
+        }
+        builder.Append(">");
+      }
+      isVoid = false;
+
+      if (current == aRoot) {
+        return builder.ToString(aOut);
+      }
+
+      if ((next = current->GetNextSibling())) {
+        current = next;
+        break;
+      }
+
+      current = current->GetParentNode();
+
+      // Template case, if we are in a template's content, then the parent
+      // should be the host template element.
+      if (current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
+        DocumentFragment* frag = static_cast<DocumentFragment*>(current);
+        nsIContent* fragHost = frag->GetHost();
+        if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
+          current = fragHost;
+        }
+      }
+
+      if (aDescendentsOnly && current == aRoot) {
+        return builder.ToString(aOut);
+      }
+    }
+  }
+}
+
+void
+FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
+{
+  aMarkup.Truncate();
+
+  nsIDocument* doc = OwnerDoc();
+  if (IsInHTMLDocument()) {
+    Serialize(this, !aIncludeSelf, aMarkup);
+    return;
+  }
+
+  nsAutoString contentType;
+  doc->GetContentType(contentType);
+  bool tryToCacheEncoder = !aIncludeSelf;
+
+  nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
+  if (!docEncoder) {
+    docEncoder =
+      do_CreateInstance(PromiseFlatCString(
+        nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
+        NS_ConvertUTF16toUTF8(contentType)
+      ).get());
+  }
+  if (!docEncoder) {
+    // This could be some type for which we create a synthetic document.  Try
+    // again as XML
+    contentType.AssignLiteral("application/xml");
+    docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
+    // Don't try to cache the encoder since it would point to a different
+    // contentType once it has been reinitialized.
+    tryToCacheEncoder = false;
+  }
+
+  NS_ENSURE_TRUE_VOID(docEncoder);
+
+  uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
+                   // Output DOM-standard newlines
+                   nsIDocumentEncoder::OutputLFLineBreak |
+                   // Don't do linebreaking that's not present in
+                   // the source
+                   nsIDocumentEncoder::OutputRaw |
+                   // Only check for mozdirty when necessary (bug 599983)
+                   nsIDocumentEncoder::OutputIgnoreMozDirty;
+
+  if (IsEditable()) {
+    nsCOMPtr<Element> elem = do_QueryInterface(this);
+    nsIEditor* editor = elem ? elem->GetEditorInternal() : nullptr;
+    if (editor && editor->OutputsMozDirty()) {
+      flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
+    }
+  }
+
+  DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  if (aIncludeSelf) {
+    docEncoder->SetNativeNode(this);
+  } else {
+    docEncoder->SetNativeContainerNode(this);
+  }
+  rv = docEncoder->EncodeToString(aMarkup);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  if (tryToCacheEncoder) {
+    doc->SetCachedEncoder(docEncoder.forget());
+  }
+}
+
+void
+FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError)
+{
+  FragmentOrElement* target = this;
+  // Handle template case.
+  if (nsNodeUtils::IsTemplateElement(target)) {
+    DocumentFragment* frag =
+      static_cast<HTMLTemplateElement*>(target)->Content();
+    MOZ_ASSERT(frag);
+    target = frag;
+  }
+
+  nsIDocument* doc = target->OwnerDoc();
+
+  // Batch possible DOMSubtreeModified events.
+  mozAutoSubtreeModified subtree(doc, nullptr);
+
+  target->FireNodeRemovedForChildren();
+
+  // Needed when innerHTML is used in combination with contenteditable
+  mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
+
+  // Remove childnodes.
+  uint32_t childCount = target->GetChildCount();
+  nsAutoMutationBatch mb(target, true, false);
+  for (uint32_t i = 0; i < childCount; ++i) {
+    target->RemoveChildAt(0, true);
+  }
+  mb.RemovalDone();
+
+  nsAutoScriptLoaderDisabler sld(doc);
+
+  nsIAtom* contextLocalName = Tag();
+  int32_t contextNameSpaceID = GetNameSpaceID();
+
+  ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
+  if (shadowRoot) {
+    // Fix up the context to be the host of the ShadowRoot.
+    contextLocalName = shadowRoot->GetHost()->Tag();
+    contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
+  }
+
+  if (doc->IsHTML()) {
+    int32_t oldChildCount = target->GetChildCount();
+    aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
+                                               target,
+                                               contextLocalName,
+                                               contextNameSpaceID,
+                                               doc->GetCompatibilityMode() ==
+                                                 eCompatibility_NavQuirks,
+                                               true);
+    mb.NodesAdded();
+    // HTML5 parser has notified, but not fired mutation events.
+    nsContentUtils::FireMutationEventsForDirectParsing(doc, target,
+                                                       oldChildCount);
+  } else {
+    nsCOMPtr<nsIDOMDocumentFragment> df;
+    aError = nsContentUtils::CreateContextualFragment(target, aInnerHTML,
+                                                      true,
+                                                      getter_AddRefs(df));
+    nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
+    if (!aError.Failed()) {
+      // Suppress assertion about node removal mutation events that can't have
+      // listeners anyway, because no one has had the chance to register mutation
+      // listeners on the fragment that comes from the parser.
+      nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
+
+      static_cast<nsINode*>(target)->AppendChild(*fragment, aError);
+      mb.NodesAdded();
+    }
+  }
+}
+
 nsINode::nsSlots*
 FragmentOrElement::CreateSlots()
 {
   return new nsDOMSlots();
 }
 
 void
 FragmentOrElement::FireNodeRemovedForChildren()
new file mode 100644
--- /dev/null
+++ b/content/base/src/ShadowRoot.cpp
@@ -0,0 +1,590 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/dom/ShadowRootBinding.h"
+#include "mozilla/dom/DocumentFragment.h"
+#include "ChildIterator.h"
+#include "nsContentUtils.h"
+#include "nsDOMClassInfoID.h"
+#include "nsIDOMHTMLElement.h"
+#include "nsIStyleSheetLinkingElement.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLContentElement.h"
+#include "nsXBLPrototypeBinding.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+static PLDHashOperator
+IdentifierMapEntryTraverse(nsIdentifierMapEntry *aEntry, void *aArg)
+{
+  nsCycleCollectionTraversalCallback *cb =
+    static_cast<nsCycleCollectionTraversalCallback*>(aArg);
+  aEntry->Traverse(cb);
+  return PL_DHASH_NEXT;
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot,
+                                                  DocumentFragment)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHost)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetList)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAssociatedBinding)
+  tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ShadowRoot,
+                                                DocumentFragment)
+  if (tmp->mHost) {
+    tmp->mHost->RemoveMutationObserver(tmp);
+  }
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mHost)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mStyleSheetList)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAssociatedBinding)
+  tmp->mIdentifierMap.Clear();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+DOMCI_DATA(ShadowRoot, ShadowRoot)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ShadowRoot)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
+  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
+NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
+
+NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
+NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
+
+ShadowRoot::ShadowRoot(nsIContent* aContent,
+                       already_AddRefed<nsINodeInfo> aNodeInfo,
+                       nsXBLPrototypeBinding* aProtoBinding)
+  : DocumentFragment(aNodeInfo), mHost(aContent),
+    mProtoBinding(aProtoBinding), mInsertionPointChanged(false)
+{
+  SetHost(aContent);
+  SetFlags(NODE_IS_IN_SHADOW_TREE);
+  // ShadowRoot isn't really in the document but it behaves like it is.
+  SetInDocument();
+  DOMSlots()->mBindingParent = aContent;
+  DOMSlots()->mContainingShadow = this;
+
+  // Add the ShadowRoot as a mutation observer on the host to watch
+  // for mutations because the insertion points in this ShadowRoot
+  // may need to be updated when the host children are modified.
+  mHost->AddMutationObserver(this);
+}
+
+ShadowRoot::~ShadowRoot()
+{
+  if (mHost) {
+    // mHost may have been unlinked.
+    mHost->RemoveMutationObserver(this);
+  }
+
+  ClearInDocument();
+  SetHost(nullptr);
+}
+
+JSObject*
+ShadowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return mozilla::dom::ShadowRootBinding::Wrap(aCx, aScope, this);
+}
+
+ShadowRoot*
+ShadowRoot::FromNode(nsINode* aNode)
+{
+  if (aNode->HasFlag(NODE_IS_IN_SHADOW_TREE) && !aNode->GetParentNode()) {
+    MOZ_ASSERT(aNode->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE,
+               "ShadowRoot is a document fragment.");
+    return static_cast<ShadowRoot*>(aNode);
+  }
+
+  return nullptr;
+}
+
+void
+ShadowRoot::Restyle()
+{
+  mProtoBinding->FlushSkinSheets();
+
+  nsIPresShell* shell = OwnerDoc()->GetShell();
+  if (shell) {
+    OwnerDoc()->BeginUpdate(UPDATE_STYLE);
+    shell->RestyleShadowRoot(this);
+    OwnerDoc()->EndUpdate(UPDATE_STYLE);
+  }
+}
+
+void
+ShadowRoot::InsertSheet(nsCSSStyleSheet* aSheet,
+                        nsIContent* aLinkingContent)
+{
+  nsCOMPtr<nsIStyleSheetLinkingElement>
+    linkingElement = do_QueryInterface(aLinkingContent);
+  MOZ_ASSERT(linkingElement, "The only styles in a ShadowRoot should come "
+                             "from <style>.");
+
+  linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet
+
+  nsTArray<nsRefPtr<nsCSSStyleSheet> >* sheets =
+    mProtoBinding->GetOrCreateStyleSheets();
+  MOZ_ASSERT(sheets, "Style sheets array should never be null.");
+
+  // Find the correct position to insert into the style sheet list (must
+  // be in tree order).
+  for (uint32_t i = 0; i <= sheets->Length(); i++) {
+    if (i == sheets->Length()) {
+      sheets->AppendElement(aSheet);
+      break;
+    }
+
+    nsINode* sheetOwnerNode = sheets->ElementAt(i)->GetOwnerNode();
+    if (nsContentUtils::PositionIsBefore(aLinkingContent, sheetOwnerNode)) {
+      sheets->InsertElementAt(i, aSheet);
+      break;
+    }
+  }
+
+  Restyle();
+}
+
+void
+ShadowRoot::RemoveSheet(nsCSSStyleSheet* aSheet)
+{
+  nsTArray<nsRefPtr<nsCSSStyleSheet> >* sheets =
+    mProtoBinding->GetOrCreateStyleSheets();
+  MOZ_ASSERT(sheets, "Style sheets array should never be null.");
+
+  DebugOnly<bool> found = sheets->RemoveElement(aSheet);
+  MOZ_ASSERT(found, "Trying to remove a sheet from a ShadowRoot "
+                    "that does not exist.");
+
+  Restyle();
+}
+
+Element*
+ShadowRoot::GetElementById(const nsAString& aElementId)
+{
+  nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
+  return entry ? entry->GetIdElement() : nullptr;
+}
+
+already_AddRefed<nsContentList>
+ShadowRoot::GetElementsByTagName(const nsAString& aTagName)
+{
+  return NS_GetContentList(this, kNameSpaceID_Unknown, aTagName);
+}
+
+already_AddRefed<nsContentList>
+ShadowRoot::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
+                                   const nsAString& aLocalName)
+{
+  int32_t nameSpaceId = kNameSpaceID_Wildcard;
+
+  if (!aNamespaceURI.EqualsLiteral("*")) {
+    nsresult rv =
+      nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
+                                                            nameSpaceId);
+    NS_ENSURE_SUCCESS(rv, nullptr);
+  }
+
+  NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
+
+  return NS_GetContentList(this, nameSpaceId, aLocalName);
+}
+
+void
+ShadowRoot::AddToIdTable(Element* aElement, nsIAtom* aId)
+{
+  nsIdentifierMapEntry *entry =
+    mIdentifierMap.PutEntry(nsDependentAtomString(aId));
+  if (entry) {
+    entry->AddIdElement(aElement);
+  }
+}
+
+void
+ShadowRoot::RemoveFromIdTable(Element* aElement, nsIAtom* aId)
+{
+  nsIdentifierMapEntry *entry =
+    mIdentifierMap.GetEntry(nsDependentAtomString(aId));
+  if (entry) {
+    entry->RemoveIdElement(aElement);
+    if (entry->IsEmpty()) {
+      mIdentifierMap.RawRemoveEntry(entry);
+    }
+  }
+}
+
+already_AddRefed<nsContentList>
+ShadowRoot::GetElementsByClassName(const nsAString& aClasses)
+{
+  return nsContentUtils::GetElementsByClassName(this, aClasses);
+}
+
+bool
+ShadowRoot::PrefEnabled()
+{
+  return Preferences::GetBool("dom.webcomponents.enabled", false);
+}
+
+class TreeOrderComparator {
+public:
+  bool Equals(nsINode* aElem1, nsINode* aElem2) const {
+    return aElem1 == aElem2;
+  }
+  bool LessThan(nsINode* aElem1, nsINode* aElem2) const {
+    return nsContentUtils::PositionIsBefore(aElem1, aElem2);
+  }
+};
+
+void
+ShadowRoot::AddInsertionPoint(HTMLContentElement* aInsertionPoint)
+{
+  TreeOrderComparator comparator;
+  mInsertionPoints.InsertElementSorted(aInsertionPoint, comparator);
+}
+
+void
+ShadowRoot::RemoveInsertionPoint(HTMLContentElement* aInsertionPoint)
+{
+  mInsertionPoints.RemoveElement(aInsertionPoint);
+}
+
+void
+ShadowRoot::DistributeSingleNode(nsIContent* aContent)
+{
+  // Find the insertion point to which the content belongs.
+  HTMLContentElement* insertionPoint = nullptr;
+  for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
+    if (mInsertionPoints[i]->Match(aContent)) {
+      // Matching may cause the insertion point to drop fallback content.
+      if (mInsertionPoints[i]->MatchedNodes().IsEmpty() &&
+          static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) {
+        // This match will cause the insertion point to drop all fallback
+        // content and used matched nodes instead. Give up on the optimization
+        // and just distribute all nodes.
+        DistributeAllNodes();
+        return;
+      }
+      insertionPoint = mInsertionPoints[i];
+      break;
+    }
+  }
+
+  // Find the index into the insertion point.
+  if (insertionPoint) {
+    nsCOMArray<nsIContent>& matchedNodes = insertionPoint->MatchedNodes();
+    // Find the appropriate position in the matched node list for the
+    // newly distributed content.
+    bool isIndexFound = false;
+    ExplicitChildIterator childIterator(GetHost());
+    for (uint32_t i = 0; i < matchedNodes.Length(); i++) {
+      // Seek through the host's explicit children until the inserted content
+      // is found or when the current matched node is reached.
+      if (childIterator.Seek(aContent, matchedNodes[i])) {
+        // aContent was found before the current matched node.
+        matchedNodes.InsertElementAt(i, aContent);
+        isIndexFound = true;
+        break;
+      }
+    }
+
+    if (!isIndexFound) {
+      // We have still not found an index in the insertion point,
+      // thus it must be at the end.
+      MOZ_ASSERT(childIterator.Seek(aContent),
+                 "Trying to match a node that is not a candidate to be matched");
+      matchedNodes.AppendElement(aContent);
+    }
+
+    // Handle the case where the parent of the insertion point has a ShadowRoot.
+    // The node distributed into the insertion point must be reprojected to the
+    // insertion points of the parent's ShadowRoot.
+    ShadowRoot* parentShadow = insertionPoint->GetParent()->GetShadowRoot();
+    if (parentShadow) {
+      parentShadow->DistributeSingleNode(aContent);
+    }
+  }
+}
+
+void
+ShadowRoot::RemoveDistributedNode(nsIContent* aContent)
+{
+  // Find insertion point containing the content and remove the node.
+  for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
+    if (mInsertionPoints[i]->MatchedNodes().Contains(aContent)) {
+      // Removing the matched node may cause the insertion point to use
+      // fallback content.
+      if (mInsertionPoints[i]->MatchedNodes().Length() == 1 &&
+          static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) {
+        // Removing the matched node will cause fallback content to be
+        // used instead. Give up optimization and distribute all nodes.
+        DistributeAllNodes();
+        return;
+      }
+
+      mInsertionPoints[i]->MatchedNodes().RemoveElement(aContent);
+
+      // Handle the case where the parent of the insertion point has a ShadowRoot.
+      // The removed node needs to be removed from the insertion points of the
+      // parent's ShadowRoot.
+      ShadowRoot* parentShadow = mInsertionPoints[i]->GetParent()->GetShadowRoot();
+      if (parentShadow) {
+        parentShadow->RemoveDistributedNode(aContent);
+      }
+
+      break;
+    }
+  }
+}
+
+void
+ShadowRoot::DistributeAllNodes()
+{
+  // Create node pool.
+  nsTArray<nsIContent*> nodePool;
+  ExplicitChildIterator childIterator(GetHost());
+  for (nsIContent* content = childIterator.GetNextChild();
+       content;
+       content = childIterator.GetNextChild()) {
+    nodePool.AppendElement(content);
+  }
+
+  for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
+    mInsertionPoints[i]->ClearMatchedNodes();
+    // Assign matching nodes from node pool.
+    for (uint32_t j = 0; j < nodePool.Length(); j++) {
+      if (mInsertionPoints[i]->Match(nodePool[j])) {
+        mInsertionPoints[i]->MatchedNodes().AppendElement(nodePool[j]);
+        nodePool[j]->SetXBLInsertionParent(mInsertionPoints[i]);
+        nodePool.RemoveElementAt(j--);
+      }
+    }
+  }
+
+  // Distribute nodes in outer ShadowRoot for the case of an insertion point being
+  // distributed into an outer ShadowRoot.
+  for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
+    nsIContent* insertionParent = mInsertionPoints[i]->GetParent();
+    MOZ_ASSERT(insertionParent, "The only way for an insertion point to be in the"
+                                "mInsertionPoints array is to be a descendant of a"
+                                "ShadowRoot, in which case, it should have a parent");
+
+    // If the parent of the insertion point has as ShadowRoot, the nodes distributed
+    // to the insertion point must be reprojected to the insertion points of the
+    // parent's ShadowRoot.
+    ShadowRoot* parentShadow = insertionParent->GetShadowRoot();
+    if (parentShadow) {
+      parentShadow->DistributeAllNodes();
+    }
+  }
+}
+
+void
+ShadowRoot::GetInnerHTML(nsAString& aInnerHTML)
+{
+  GetMarkup(false, aInnerHTML);
+}
+
+void
+ShadowRoot::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError)
+{
+  SetInnerHTMLInternal(aInnerHTML, aError);
+}
+
+bool
+ShadowRoot::ApplyAuthorStyles()
+{
+  return mProtoBinding->InheritsStyle();
+}
+
+void
+ShadowRoot::SetApplyAuthorStyles(bool aApplyAuthorStyles)
+{
+  mProtoBinding->SetInheritsStyle(aApplyAuthorStyles);
+
+  nsIPresShell* shell = OwnerDoc()->GetShell();
+  if (shell) {
+    OwnerDoc()->BeginUpdate(UPDATE_STYLE);
+    shell->RestyleShadowRoot(this);
+    OwnerDoc()->EndUpdate(UPDATE_STYLE);
+  }
+}
+
+nsIDOMStyleSheetList*
+ShadowRoot::StyleSheets()
+{
+  if (!mStyleSheetList) {
+    mStyleSheetList = new ShadowRootStyleSheetList(this);
+  }
+
+  return mStyleSheetList;
+}
+
+/**
+ * Returns whether the web components pool population algorithm
+ * on the host would contain |aContent|. This function ignores
+ * insertion points in the pool, thus should only be used to
+ * test nodes that have not yet been distributed.
+ */
+static bool
+IsPooledNode(nsIContent* aContent, nsIContent* aContainer, nsIContent* aHost)
+{
+  if (nsContentUtils::IsContentInsertionPoint(aContent)) {
+    // Insertion points never end up in the pool.
+    return false;
+  }
+
+  if (aContainer->IsHTML(nsGkAtoms::content)) {
+    // Fallback content will end up in pool if its parent is a child of the host.
+    HTMLContentElement* content = static_cast<HTMLContentElement*>(aContainer);
+    return content->IsInsertionPoint() && content->MatchedNodes().IsEmpty() &&
+           aContainer->GetParentNode() == aHost;
+  }
+
+  if (aContainer == aHost) {
+    // Any other child nodes of the host will end up in the pool.
+    return true;
+  }
+
+  return false;
+}
+
+void
+ShadowRoot::AttributeChanged(nsIDocument* aDocument,
+                             Element* aElement,
+                             int32_t aNameSpaceID,
+                             nsIAtom* aAttribute,
+                             int32_t aModType)
+{
+  // Watch for attribute changes to nodes in the pool because
+  // insertion points can select on attributes.
+  if (!IsPooledNode(aElement, aElement->GetParent(), mHost)) {
+    return;
+  }
+
+  // Attributes may change insertion point matching, find its new distribution.
+  RemoveDistributedNode(aElement);
+  DistributeSingleNode(aElement);
+}
+
+void
+ShadowRoot::ContentAppended(nsIDocument* aDocument,
+                            nsIContent* aContainer,
+                            nsIContent* aFirstNewContent,
+                            int32_t aNewIndexInContainer)
+{
+  if (mInsertionPointChanged) {
+    DistributeAllNodes();
+    mInsertionPointChanged = false;
+    return;
+  }
+
+  // Watch for new nodes added to the pool because the node
+  // may need to be added to an insertion point.
+  nsIContent* currentChild = aFirstNewContent;
+  while (currentChild) {
+    if (IsPooledNode(currentChild, aContainer, mHost)) {
+      DistributeSingleNode(currentChild);
+    }
+    currentChild = currentChild->GetNextSibling();
+  }
+}
+
+void
+ShadowRoot::ContentInserted(nsIDocument* aDocument,
+                            nsIContent* aContainer,
+                            nsIContent* aChild,
+                            int32_t aIndexInContainer)
+{
+  if (mInsertionPointChanged) {
+    DistributeAllNodes();
+    mInsertionPointChanged = false;
+    return;
+  }
+
+  // Watch for new nodes added to the pool because the node
+  // may need to be added to an insertion point.
+  if (IsPooledNode(aChild, aContainer, mHost)) {
+    DistributeSingleNode(aChild);
+  }
+}
+
+void
+ShadowRoot::ContentRemoved(nsIDocument* aDocument,
+                           nsIContent* aContainer,
+                           nsIContent* aChild,
+                           int32_t aIndexInContainer,
+                           nsIContent* aPreviousSibling)
+{
+  if (mInsertionPointChanged) {
+    DistributeAllNodes();
+    mInsertionPointChanged = false;
+    return;
+  }
+
+  // Watch for node that is removed from the pool because
+  // it may need to be removed from an insertion point.
+  if (IsPooledNode(aChild, aContainer, mHost)) {
+    RemoveDistributedNode(aChild);
+  }
+}
+
+NS_IMPL_CYCLE_COLLECTION_1(ShadowRootStyleSheetList, mShadowRoot)
+
+NS_INTERFACE_TABLE_HEAD(ShadowRootStyleSheetList)
+  NS_INTERFACE_TABLE1(ShadowRootStyleSheetList, nsIDOMStyleSheetList)
+  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(ShadowRootStyleSheetList)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StyleSheetList)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(ShadowRootStyleSheetList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(ShadowRootStyleSheetList)
+
+ShadowRootStyleSheetList::ShadowRootStyleSheetList(ShadowRoot* aShadowRoot)
+  : mShadowRoot(aShadowRoot)
+{
+  MOZ_COUNT_CTOR(ShadowRootStyleSheetList);
+}
+
+ShadowRootStyleSheetList::~ShadowRootStyleSheetList()
+{
+  MOZ_COUNT_DTOR(ShadowRootStyleSheetList);
+}
+
+NS_IMETHODIMP
+ShadowRootStyleSheetList::Item(uint32_t aIndex, nsIDOMStyleSheet** aReturn)
+{
+  nsTArray<nsRefPtr<nsCSSStyleSheet> >* sheets =
+    mShadowRoot->mProtoBinding->GetStyleSheets();
+
+  if (sheets) {
+    NS_IF_ADDREF(*aReturn = sheets->SafeElementAt(aIndex));
+  } else {
+    *aReturn = nullptr;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ShadowRootStyleSheetList::GetLength(uint32_t* aLength)
+{
+  nsTArray<nsRefPtr<nsCSSStyleSheet> >* sheets =
+    mShadowRoot->mProtoBinding->GetStyleSheets();
+
+  if (sheets) {
+    *aLength = sheets->Length();
+  } else {
+    *aLength = 0;
+  }
+
+  return NS_OK;
+}
+
new file mode 100644
--- /dev/null
+++ b/content/base/src/ShadowRoot.h
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_shadowroot_h__
+#define mozilla_dom_shadowroot_h__
+
+#include "mozilla/dom/DocumentFragment.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsTHashtable.h"
+#include "nsDocument.h"
+
+class nsIAtom;
+class nsIContent;
+class nsIDocument;
+class nsINodeInfo;
+class nsPIDOMWindow;
+class nsXBLPrototypeBinding;
+class nsTagNameMapEntry;
+
+namespace mozilla {
+namespace dom {
+
+class Element;
+class HTMLContentElement;
+class ShadowRootStyleSheetList;
+
+class ShadowRoot : public DocumentFragment,
+                   public nsStubMutationObserver
+{
+  friend class ShadowRootStyleSheetList;
+public:
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ShadowRoot,
+                                           DocumentFragment)
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
+
+  ShadowRoot(nsIContent* aContent, already_AddRefed<nsINodeInfo> aNodeInfo,
+             nsXBLPrototypeBinding* aProtoBinding);
+  virtual ~ShadowRoot();
+
+  void AddToIdTable(Element* aElement, nsIAtom* aId);
+  void RemoveFromIdTable(Element* aElement, nsIAtom* aId);
+  static bool PrefEnabled();
+  void InsertSheet(nsCSSStyleSheet* aSheet, nsIContent* aLinkingContent);
+  void RemoveSheet(nsCSSStyleSheet* aSheet);
+  bool ApplyAuthorStyles();
+  void SetApplyAuthorStyles(bool aApplyAuthorStyles);
+  nsIDOMStyleSheetList* StyleSheets();
+
+  /**
+   * Distributes a single explicit child of the host to the content
+   * insertion points in this ShadowRoot.
+   */
+  void DistributeSingleNode(nsIContent* aContent);
+
+  /**
+   * Removes a single explicit child of the host from the content
+   * insertion points in this ShadowRoot.
+   */
+  void RemoveDistributedNode(nsIContent* aContent);
+
+  /**
+   * Distributes all the explicit children of the host to the content
+   * insertion points in this ShadowRoot.
+   */
+  void DistributeAllNodes();
+
+  void AddInsertionPoint(HTMLContentElement* aInsertionPoint);
+  void RemoveInsertionPoint(HTMLContentElement* aInsertionPoint);
+
+  void SetInsertionPointChanged() { mInsertionPointChanged = true; }
+
+  void SetAssociatedBinding(nsXBLBinding* aBinding)
+  {
+    mAssociatedBinding = aBinding;
+  }
+
+  nsISupports* GetParentObject() const
+  {
+    return mHost;
+  }
+
+  nsIContent* GetHost() { return mHost; }
+
+  JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  static ShadowRoot* FromNode(nsINode* aNode);
+
+  // WebIDL methods.
+  Element* GetElementById(const nsAString& aElementId);
+  already_AddRefed<nsContentList>
+    GetElementsByTagName(const nsAString& aNamespaceURI);
+  already_AddRefed<nsContentList>
+    GetElementsByTagNameNS(const nsAString& aNamespaceURI,
+                           const nsAString& aLocalName);
+  already_AddRefed<nsContentList>
+    GetElementsByClassName(const nsAString& aClasses);
+  void GetInnerHTML(nsAString& aInnerHTML);
+  void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
+protected:
+  void Restyle();
+
+  nsCOMPtr<nsIContent> mHost;
+
+  // An array of content insertion points that are a descendant of the ShadowRoot
+  // sorted in tree order. Insertion points are responsible for notifying
+  // the ShadowRoot when they are removed or added as a descendant. The insertion
+  // points are kept alive by the parent node, thus weak references are held
+  // by the array.
+  nsTArray<HTMLContentElement*> mInsertionPoints;
+
+  nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
+  nsXBLPrototypeBinding* mProtoBinding;
+
+  // It is necessary to hold a reference to the associated nsXBLBinding
+  // because the binding holds a reference on the nsXBLDocumentInfo that
+  // owns |mProtoBinding|.
+  nsRefPtr<nsXBLBinding> mAssociatedBinding;
+
+  nsRefPtr<ShadowRootStyleSheetList> mStyleSheetList;
+
+  // A boolean that indicates that an insertion point was added or removed
+  // from this ShadowRoot and that the nodes need to be redistributed into
+  // the insertion points. After this flag is set, nodes will be distributed
+  // on the next mutation event.
+  bool mInsertionPointChanged;
+};
+
+class ShadowRootStyleSheetList : public nsIDOMStyleSheetList
+{
+public:
+  ShadowRootStyleSheetList(ShadowRoot* aShadowRoot);
+  virtual ~ShadowRootStyleSheetList();
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(ShadowRootStyleSheetList)
+
+  // nsIDOMStyleSheetList
+  NS_DECL_NSIDOMSTYLESHEETLIST
+
+protected:
+  nsRefPtr<ShadowRoot> mShadowRoot;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_shadowroot_h__
+
--- a/content/base/src/moz.build
+++ b/content/base/src/moz.build
@@ -58,16 +58,17 @@ EXPORTS.mozilla.dom += [
     'DocumentFragment.h',
     'DocumentType.h',
     'DOMImplementation.h',
     'DOMParser.h',
     'DOMRect.h',
     'EventSource.h',
     'Link.h',
     'NodeIterator.h',
+    'ShadowRoot.h',
     'Text.h',
     'TreeWalker.h',
 ]
 
 UNIFIED_SOURCES += [
     'Attr.cpp',
     'ChildIterator.cpp',
     'Comment.cpp',
@@ -144,16 +145,17 @@ UNIFIED_SOURCES += [
     'nsTextNode.cpp',
     'nsTraversal.cpp',
     'nsTreeSanitizer.cpp',
     'nsViewportInfo.cpp',
     'nsXHTMLContentSerializer.cpp',
     'nsXMLContentSerializer.cpp',
     'nsXMLHttpRequest.cpp',
     'nsXMLNameSpaceMap.cpp',
+    'ShadowRoot.cpp',
     'Text.cpp',
     'ThirdPartyUtil.cpp',
     'TreeWalker.cpp',
     'WebSocket.cpp',
 ]
 
 # These files cannot be built in unified mode because they use FORCE_PR_LOG
 SOURCES += [
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -22,25 +22,23 @@
 #include "nsNetUtil.h"
 #include "nsIHttpChannel.h"
 #include "nsIContent.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsViewManager.h"
 #include "nsIAtom.h"
 #include "nsGkAtoms.h"
-#include "nsIDOMWindow.h"
 #include "nsNetCID.h"
 #include "nsIOfflineCacheUpdate.h"
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIApplicationCacheChannel.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsICookieService.h"
-#include "nsIPrompt.h"
 #include "nsContentUtils.h"
 #include "nsNodeInfoManager.h"
 #include "nsIAppShell.h"
 #include "nsIWidget.h"
 #include "nsWidgetsCID.h"
 #include "nsIDOMNode.h"
 #include "mozAutoDocUpdate.h"
 #include "nsIWebNavigation.h"
@@ -285,18 +283,17 @@ nsContentSink::ProcessHeaderData(nsIAtom
   nsresult rv = NS_OK;
   // necko doesn't process headers coming in from the parser
 
   mDocument->SetHeaderData(aHeader, aValue);
 
   if (aHeader == nsGkAtoms::setcookie) {
     // Note: Necko already handles cookies set via the channel.  We can't just
     // call SetCookie on the channel because we want to do some security checks
-    // here and want to use the prompt associated to our current window, not
-    // the window where the channel was dispatched.
+    // here.
     nsCOMPtr<nsICookieService> cookieServ =
       do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     // Get a URI from the document principal
 
@@ -304,29 +301,23 @@ nsContentSink::ProcessHeaderData(nsIAtom
     // by SetDomain
 
     // Note that a non-codebase principal (eg the system principal) will return
     // a null URI.
     nsCOMPtr<nsIURI> codebaseURI;
     rv = mDocument->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
     NS_ENSURE_TRUE(codebaseURI, rv);
 
-    nsCOMPtr<nsIPrompt> prompt;
-    nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mDocument->GetWindow());
-    if (window) {
-      window->GetPrompter(getter_AddRefs(prompt));
-    }
-
     nsCOMPtr<nsIChannel> channel;
     if (mParser) {
       mParser->GetChannel(getter_AddRefs(channel));
     }
 
     rv = cookieServ->SetCookieString(codebaseURI,
-                                     prompt,
+                                     nullptr,
                                      NS_ConvertUTF16toUTF8(aValue).get(),
                                      channel);
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
   else if (aHeader == nsGkAtoms::msthemecompatible) {
     // Disable theming for the presshell if the value is no.
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -31,17 +31,19 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/Base64.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
+#include "mozilla/dom/HTMLContentElement.h"
 #include "mozilla/dom/TextDecoder.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/MutationEvent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Selection.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/Util.h"
 #include "nsAString.h"
@@ -4324,19 +4326,27 @@ nsContentUtils::IsInSameAnonymousTree(co
      * nodes in the same anonymous subtree as it will have a null
      * bindingParent.
      *
      * XXXbz strictly speaking, that's not true for attribute nodes.
      */
     return aContent->GetBindingParent() == nullptr;
   }
 
-  return static_cast<const nsIContent*>(aNode)->GetBindingParent() ==
-         aContent->GetBindingParent();
- 
+  const nsIContent* nodeAsContent = static_cast<const nsIContent*>(aNode);
+
+  // For nodes in a shadow tree, it is insufficient to simply compare
+  // the binding parent because a node may host multiple ShadowRoots,
+  // thus nodes in different shadow tree may have the same binding parent.
+  if (aNode->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    return nodeAsContent->GetContainingShadow() ==
+      aContent->GetContainingShadow();
+  }
+
+  return nodeAsContent->GetBindingParent() == aContent->GetBindingParent();
 }
 
 class AnonymousContentDestroyer : public nsRunnable {
 public:
   AnonymousContentDestroyer(nsCOMPtr<nsIContent>* aContent) {
     mContent.swap(*aContent);
     mParent = mContent->GetParent();
     mDoc = mContent->OwnerDoc();
@@ -6391,16 +6401,39 @@ nsContentUtils::HasPluginWithUncontrolle
   return false;
 #endif
   bool result = false;
   CheckForWindowedPlugins(aContent, &result);
   return result;
 }
 
 /* static */
+void
+nsContentUtils::FireMutationEventsForDirectParsing(nsIDocument* aDoc,
+                                                   nsIContent* aDest,
+                                                   int32_t aOldChildCount)
+{
+  // Fire mutation events. Optimize for the case when there are no listeners
+  int32_t newChildCount = aDest->GetChildCount();
+  if (newChildCount && nsContentUtils::
+        HasMutationListeners(aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
+    nsAutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
+    NS_ASSERTION(newChildCount - aOldChildCount >= 0,
+                 "What, some unexpected dom mutation has happened?");
+    childNodes.SetCapacity(newChildCount - aOldChildCount);
+    for (nsIContent* child = aDest->GetFirstChild();
+         child;
+         child = child->GetNextSibling()) {
+      childNodes.AppendElement(child);
+    }
+    FragmentOrElement::FireNodeInserted(aDoc, aDest, childNodes);
+  }
+}
+
+/* static */
 nsIDocument*
 nsContentUtils::GetFullscreenAncestor(nsIDocument* aDoc)
 {
   nsIDocument* doc = aDoc;
   while (doc) {
     if (doc->IsFullScreenDoc()) {
       return doc;
     }
@@ -6553,16 +6586,32 @@ nsContentUtils::InternalIsSupported(nsIS
            nsSVGFeatures::HasFeature(aObject, aFeature);
   }
 
   // Otherwise, we claim to support everything
   return true;
 }
 
 bool
+nsContentUtils::IsContentInsertionPoint(const nsIContent* aContent)
+{
+  // Check if the content is a XBL insertion point.
+  if (aContent->IsActiveChildrenElement()) {
+    return true;
+  }
+
+  // Check if the content is a web components content insertion point.
+  if (aContent->IsHTML(nsGkAtoms::content)) {
+    return static_cast<const HTMLContentElement*>(aContent)->IsInsertionPoint();
+  }
+
+  return false;
+}
+
+bool
 nsContentUtils::DOMWindowDumpEnabled()
 {
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
   // In optimized builds we check a pref that controls if we should
   // enable output from dump() or not, in debug builds it's always
   // enabled.
   return nsContentUtils::sDOMWindowDumpEnabled;
 #else
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -167,16 +167,17 @@
 #include "nsRefreshDriver.h"
 
 // FOR CSP (autogenerated by xpidl)
 #include "nsIContentSecurityPolicy.h"
 #include "nsCSPService.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsHTMLCSSStyleSheet.h"
 #include "mozilla/dom/DOMImplementation.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/Comment.h"
 #include "nsTextNode.h"
 #include "mozilla/dom/Link.h"
 #include "mozilla/dom/HTMLElementBinding.h"
 #include "nsXULAppAPI.h"
 #include "nsDOMTouchEvent.h"
 #include "mozilla/dom/Touch.h"
 #include "DictionaryHelpers.h"
@@ -428,20 +429,20 @@ nsIdentifierMapEntry::SetImageElement(El
   mImageElement = aElement;
   Element* newElement = GetImageIdElement();
   if (oldElement != newElement) {
     FireChangeCallbacks(oldElement, newElement, true);
   }
 }
 
 void
-nsIdentifierMapEntry::AddNameElement(nsIDocument* aDocument, Element* aElement)
+nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement)
 {
   if (!mNameContentList) {
-    mNameContentList = new nsSimpleContentList(aDocument);
+    mNameContentList = new nsSimpleContentList(aNode);
   }
 
   mNameContentList->AppendElement(aElement);
 }
 
 void
 nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
 {
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -129,17 +129,17 @@ public:
   }
   nsIdentifierMapEntry(const nsIdentifierMapEntry& aOther) :
     nsStringHashKey(&aOther.GetKey())
   {
     NS_ERROR("Should never be called");
   }
   ~nsIdentifierMapEntry();
 
-  void AddNameElement(nsIDocument* aDocument, Element* aElement);
+  void AddNameElement(nsINode* aDocument, Element* aElement);
   void RemoveNameElement(Element* aElement);
   bool IsEmpty();
   nsBaseContentList* GetNameContentList() {
     return mNameContentList;
   }
   bool HasNameElement() const {
     return mNameContentList && mNameContentList->Length() != 0;
   }
@@ -274,32 +274,16 @@ public:
   NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED
   NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED
 
   // nsIMutationObserver
   NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
 
   nsIStyleSheet* GetItemAt(uint32_t aIndex);
 
-  static nsDOMStyleSheetList* FromSupports(nsISupports* aSupports)
-  {
-    nsIDOMStyleSheetList* list = static_cast<nsIDOMStyleSheetList*>(aSupports);
-#ifdef DEBUG
-    {
-      nsCOMPtr<nsIDOMStyleSheetList> list_qi = do_QueryInterface(aSupports);
-
-      // If this assertion fires the QI implementation for the object in
-      // question doesn't use the nsIDOMStyleSheetList pointer as the
-      // nsISupports pointer. That must be fixed, or we'll crash...
-      NS_ASSERTION(list_qi == list, "Uh, fix QI!");
-    }
-#endif
-    return static_cast<nsDOMStyleSheetList*>(list);
-  }
-
 protected:
   int32_t       mLength;
   nsIDocument*  mDocument;
 };
 
 class nsOnloadBlocker MOZ_FINAL : public nsIRequest
 {
 public:
--- a/content/base/src/nsDocumentEncoder.cpp
+++ b/content/base/src/nsDocumentEncoder.cpp
@@ -44,16 +44,17 @@
 #include "nsContentUtils.h"
 #include "nsNodeUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsReadableUtils.h"
 #include "nsTArray.h"
 #include "nsIFrame.h"
 #include "nsStringBuffer.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "nsIEditor.h"
 #include "nsIHTMLEditor.h"
 #include "nsIDocShell.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsresult NS_NewDomSelection(nsISelection **aDomSelection);
@@ -103,17 +104,26 @@ protected:
 
   nsresult FlushText(nsAString& aString, bool aForce);
 
   bool IsVisibleNode(nsINode* aNode)
   {
     NS_PRECONDITION(aNode, "");
 
     if (mFlags & SkipInvisibleContent) {
-      nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+      // Treat the visibility of the ShadowRoot as if it were
+      // the host content.
+      nsCOMPtr<nsIContent> content;
+      ShadowRoot* shadowRoot = ShadowRoot::FromNode(aNode);
+      if (shadowRoot) {
+        content = shadowRoot->GetHost();
+      } else {
+        content = do_QueryInterface(aNode);
+      }
+
       if (content) {
         nsIFrame* frame = content->GetPrimaryFrame();
         if (!frame) {
           if (aNode->IsNodeOfType(nsINode::eTEXT)) {
             // We have already checked that our parent is visible.
             return true;
           }
           return false;
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -8,16 +8,17 @@
  * nsIDOMCDATASection, and nsIDOMProcessingInstruction nodes.
  */
 
 #include "mozilla/DebugOnly.h"
 
 #include "nsGenericDOMDataNode.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "nsIDocument.h"
 #include "nsEventListenerManager.h"
 #include "nsIDOMDocument.h"
 #include "nsReadableUtils.h"
 #include "mozilla/MutationEvent.h"
 #include "nsINameSpaceManager.h"
 #include "nsIURI.h"
 #include "nsIDOMEvent.h"
@@ -475,16 +476,23 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
                  "non-native anonymous parent!");
     DataSlots()->mBindingParent = aBindingParent; // Weak, so no addref happens.
     if (aParent->IsInNativeAnonymousSubtree()) {
       SetFlags(NODE_IS_IN_ANONYMOUS_SUBTREE);
     }
     if (aParent->HasFlag(NODE_CHROME_ONLY_ACCESS)) {
       SetFlags(NODE_CHROME_ONLY_ACCESS);
     }
+    if (aParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+      SetFlags(NODE_IS_IN_SHADOW_TREE);
+    }
+    ShadowRoot* parentContainingShadow = aParent->GetContainingShadow();
+    if (parentContainingShadow) {
+      DataSlots()->mContainingShadow = parentContainingShadow;
+    }
   }
 
   // Set parent
   if (aParent) {
     if (!GetParent()) {
       NS_ADDREF(aParent);
     }
     mParent = aParent;
@@ -526,17 +534,20 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
   return NS_OK;
 }
 
 void
 nsGenericDOMDataNode::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   // Unset frame flags; if we need them again later, they'll get set again.
   UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
-             NS_REFRAME_IF_WHITESPACE);
+             NS_REFRAME_IF_WHITESPACE |
+             // Also unset the shadow tree flag because it can
+             // no longer be a descendant of a ShadowRoot.
+             NODE_IS_IN_SHADOW_TREE);
   
   nsIDocument *document = GetCurrentDoc();
   if (document) {
     // Notify XBL- & nsIAnonymousContentCreator-generated
     // anonymous content that the document is changing.
     // This is needed to update the insertion point.
     document->BindingManager()->RemovedFromDocument(this, document);
   }
@@ -552,16 +563,17 @@ nsGenericDOMDataNode::UnbindFromTree(boo
   ClearInDocument();
 
   // Begin keeping track of our subtree root.
   SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot());
 
   nsDataSlots *slots = GetExistingDataSlots();
   if (slots) {
     slots->mBindingParent = nullptr;
+    slots->mContainingShadow = nullptr;
   }
 
   nsNodeUtils::ParentChainChanged(this);
 }
 
 already_AddRefed<nsINodeList>
 nsGenericDOMDataNode::GetChildren(uint32_t aFilter)
 {
@@ -640,16 +652,34 @@ nsGenericDOMDataNode::RemoveChildAt(uint
 
 nsIContent *
 nsGenericDOMDataNode::GetBindingParent() const
 {
   nsDataSlots *slots = GetExistingDataSlots();
   return slots ? slots->mBindingParent : nullptr;
 }
 
+ShadowRoot *
+nsGenericDOMDataNode::GetShadowRoot() const
+{
+  return nullptr;
+}
+
+ShadowRoot *
+nsGenericDOMDataNode::GetContainingShadow() const
+{
+  nsDataSlots *slots = GetExistingDataSlots();
+  return slots ? slots->mContainingShadow : nullptr;
+}
+
+void
+nsGenericDOMDataNode::SetShadowRoot(ShadowRoot* aShadowRoot)
+{
+}
+
 nsXBLBinding *
 nsGenericDOMDataNode::GetXBLBinding() const
 {
   return nullptr;
 }
 
 void
 nsGenericDOMDataNode::SetXBLBinding(nsXBLBinding* aBinding,
@@ -720,27 +750,36 @@ nsGenericDOMDataNode::IsLink(nsIURI** aU
 }
 
 nsINode::nsSlots*
 nsGenericDOMDataNode::CreateSlots()
 {
   return new nsDataSlots();
 }
 
+nsGenericDOMDataNode::nsDataSlots::nsDataSlots()
+  : nsINode::nsSlots(), mBindingParent(nullptr)
+{
+}
+
 void
 nsGenericDOMDataNode::nsDataSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
 {
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
   cb.NoteXPCOMChild(mXBLInsertionParent.get());
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
+  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
 }
 
 void
 nsGenericDOMDataNode::nsDataSlots::Unlink()
 {
   mXBLInsertionParent = nullptr;
+  mContainingShadow = nullptr;
 }
 
 //----------------------------------------------------------------------
 
 // Implementation of the nsIDOMText interface
 
 nsresult
 nsGenericDOMDataNode::SplitData(uint32_t aOffset, nsIContent** aReturn,
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -153,16 +153,19 @@ public:
   virtual void List(FILE* out, int32_t aIndent) const MOZ_OVERRIDE;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const MOZ_OVERRIDE;
 #endif
 
   virtual nsIContent *GetBindingParent() const MOZ_OVERRIDE;
   virtual nsXBLBinding *GetXBLBinding() const MOZ_OVERRIDE;
   virtual void SetXBLBinding(nsXBLBinding* aBinding,
                              nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
+  virtual mozilla::dom::ShadowRoot *GetContainingShadow() const MOZ_OVERRIDE;
+  virtual mozilla::dom::ShadowRoot *GetShadowRoot() const MOZ_OVERRIDE;
+  virtual void SetShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot) MOZ_OVERRIDE;
   virtual nsIContent *GetXBLInsertionParent() const;
   virtual void SetXBLInsertionParent(nsIContent* aContent);
   virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
   virtual bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE;
 
   virtual nsIAtom* DoGetID() const MOZ_OVERRIDE;
   virtual const nsAttrValue* DoGetClasses() const MOZ_OVERRIDE;
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) MOZ_OVERRIDE;
@@ -236,35 +239,36 @@ protected:
    * through the DOM. Rather than burn actual slots in the content
    * objects for each of these instance variables, we put them off
    * in a side structure that's only allocated when the content is
    * accessed through the DOM.
    */
   class nsDataSlots : public nsINode::nsSlots
   {
   public:
-    nsDataSlots()
-      : nsINode::nsSlots(),
-        mBindingParent(nullptr)
-    {
-    }
+    nsDataSlots();
 
     void Traverse(nsCycleCollectionTraversalCallback &cb);
     void Unlink();
 
     /**
      * The nearest enclosing content node with a binding that created us.
      * @see nsIContent::GetBindingParent
      */
     nsIContent* mBindingParent;  // [Weak]
 
     /**
      * @see nsIContent::GetXBLInsertionParent
      */
     nsCOMPtr<nsIContent> mXBLInsertionParent;
+
+    /**
+     * @see nsIContent::GetContainingShadow
+     */
+    nsRefPtr<mozilla::dom::ShadowRoot> mContainingShadow;
   };
 
   // Override from nsINode
   virtual nsINode::nsSlots* CreateSlots() MOZ_OVERRIDE;
 
   nsDataSlots* DataSlots()
   {
     return static_cast<nsDataSlots*>(Slots());
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1468,20 +1468,19 @@ GK_ATOM(onrepeatEvent, "onrepeatEvent")
 GK_ATOM(repeatCount, "repeatCount")
 GK_ATOM(repeatDur, "repeatDur")
 GK_ATOM(repeatEvent, "repeatEvent")
 GK_ATOM(restart, "restart")
 GK_ATOM(to, "to")
 GK_ATOM(XML, "XML")
 
 // internal MathML attributes: different from columnalign_, columnlines_,
-// fontstyle_, rowalign_ and rowlines_
+// rowalign_ and rowlines_
 GK_ATOM(_moz_math_columnalign_, "_moz-math-columnalign")
 GK_ATOM(_moz_math_columnline_, "_moz-math-columnline")
-GK_ATOM(_moz_math_fontstyle_, "_moz-math-font-style")
 GK_ATOM(_moz_math_rowalign_, "_moz-math-rowalign")
 GK_ATOM(_moz_math_rowline_, "_moz-math-rowline")
 
 GK_ATOM(abs_, "abs")
 GK_ATOM(accent_, "accent")
 GK_ATOM(accentunder_, "accentunder")
 GK_ATOM(actiontype_, "actiontype")
 GK_ATOM(alignmentscope_, "alignmentscope")
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -92,16 +92,17 @@
 #include "nsXBLBinding.h"
 #include "nsXBLPrototypeBinding.h"
 #include "prprf.h"
 #include "xpcpublic.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsCSSParser.h"
 #include "HTMLLegendElement.h"
 #include "nsWrapperCacheInlines.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "WrapperFactory.h"
 #include "DocumentType.h"
 #include <algorithm>
 #include "nsDOMEvent.h"
 #include "nsGlobalWindow.h"
 #include "nsDOMMutationObserver.h"
 
 using namespace mozilla;
@@ -233,16 +234,25 @@ nsINode::GetTextEditorRootContent(nsIEdi
     return rootContent;
   }
   return nullptr;
 }
 
 static nsIContent* GetRootForContentSubtree(nsIContent* aContent)
 {
   NS_ENSURE_TRUE(aContent, nullptr);
+
+  // Special case for ShadowRoot because the ShadowRoot itself is
+  // the root. This is necessary to prevent selection from crossing
+  // the ShadowRoot boundary.
+  ShadowRoot* containingShadow = aContent->GetContainingShadow();
+  if (containingShadow) {
+    return containingShadow;
+  }
+
   nsIContent* stop = aContent->GetBindingParent();
   while (aContent) {
     nsIContent* parent = aContent->GetParent();
     if (parent == stop) {
       break;
     }
     aContent = parent;
   }
@@ -301,18 +311,27 @@ nsINode::GetSelectionRootContent(nsIPres
       if (!content)
         return nullptr;
     }
   }
 
   // This node might be in another subtree, if so, we should find this subtree's
   // root.  Otherwise, we can return the content simply.
   NS_ENSURE_TRUE(content, nullptr);
-  return nsContentUtils::IsInSameAnonymousTree(this, content) ?
-           content : GetRootForContentSubtree(static_cast<nsIContent*>(this));
+  if (!nsContentUtils::IsInSameAnonymousTree(this, content)) {
+    content = GetRootForContentSubtree(static_cast<nsIContent*>(this));
+    // Fixup for ShadowRoot because the ShadowRoot itself does not have a frame.
+    // Use the host as the root.
+    ShadowRoot* shadowRoot = ShadowRoot::FromNode(content);
+    if (shadowRoot) {
+      content = shadowRoot->GetHost();
+    }
+  }
+
+  return content;
 }
 
 nsINodeList*
 nsINode::ChildNodes()
 {
   nsSlots* slots = Slots();
   if (!slots->mChildNodes) {
     slots->mChildNodes = new nsChildContentList(this);
@@ -515,18 +534,21 @@ nsINode::Normalize()
   mozAutoSubtreeModified subtree(doc, nullptr);
 
   // Fire all DOMNodeRemoved events. Optimize the common case of there being
   // no listeners
   bool hasRemoveListeners = nsContentUtils::
       HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED);
   if (hasRemoveListeners) {
     for (uint32_t i = 0; i < nodes.Length(); ++i) {
-      nsContentUtils::MaybeFireNodeRemoved(nodes[i], nodes[i]->GetParentNode(),
-                                           doc);
+      nsINode* parentNode = nodes[i]->GetParentNode();
+      if (parentNode) { // Node may have already been removed.
+        nsContentUtils::MaybeFireNodeRemoved(nodes[i], parentNode,
+                                             doc);
+      }
     }
   }
 
   mozAutoDocUpdate batch(doc, UPDATE_CONTENT_MODEL, true);
 
   // Merge and remove all nodes
   nsAutoString tmpStr;
   for (uint32_t i = 0; i < nodes.Length(); ++i) {
@@ -1546,17 +1568,21 @@ bool IsAllowedAsChild(nsIContent* aNewCh
              "can't be parents!");
 
   // A common case is that aNewChild has no kids, in which case
   // aParent can't be a descendant of aNewChild unless they're
   // actually equal to each other.  Fast-path that case, since aParent
   // could be pretty deep in the DOM tree.
   if (aNewChild == aParent ||
       ((aNewChild->GetFirstChild() ||
-        aNewChild->Tag() == nsGkAtoms::_template) &&
+        // HTML template elements and ShadowRoot hosts need
+        // to be checked to ensure that they are not inserted into
+        // the hosted content.
+        aNewChild->Tag() == nsGkAtoms::_template ||
+        aNewChild->GetShadowRoot()) &&
        nsContentUtils::ContentIsHostIncludingDescendantOf(aParent,
                                                           aNewChild))) {
     return false;
   }
 
   // The allowed child nodes differ for documents and elements
   switch (aNewChild->NodeType()) {
   case nsIDOMNode::COMMENT_NODE :
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -43,16 +43,21 @@
 #include "nsLayoutUtils.h"
 #include "nsIContentPolicy.h"
 #include "nsEventDispatcher.h"
 #include "nsSVGEffects.h"
 
 #include "mozAutoDocUpdate.h"
 #include "mozilla/dom/Element.h"
 
+#if defined(XP_WIN)
+// Undefine LoadImage to prevent naming conflict with Windows.
+#undef LoadImage
+#endif
+
 using namespace mozilla;
 
 #ifdef DEBUG_chb
 static void PrintReqURL(imgIRequest* req) {
   if (!req) {
     printf("(null req)\n");
     return;
   }
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -27,16 +27,17 @@
 #include "nsGenericHTMLElement.h"
 #include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsObjectLoadingContent.h"
 #include "nsDOMMutationObserver.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
+#include "mozilla/dom/ShadowRoot.h"
 
 using namespace mozilla::dom;
 using mozilla::AutoJSContext;
 
 // This macro expects the ownerDocument of content_ to be in scope as
 // |nsIDocument* doc|
 #define IMPL_MUTATION_NOTIFICATION(func_, content_, params_)      \
   PR_BEGIN_MACRO                                                  \
@@ -53,17 +54,22 @@ using mozilla::AutoJSContext;
     nsINode::nsSlots* slots = node->GetExistingSlots();           \
     if (slots && !slots->mMutationObservers.IsEmpty()) {          \
       /* No need to explicitly notify the first observer first    \
          since that'll happen anyway. */                          \
       NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(                         \
         slots->mMutationObservers, nsIMutationObserver,           \
         func_, params_);                                          \
     }                                                             \
-    node = node->GetParentNode();                                 \
+    ShadowRoot* shadow = ShadowRoot::FromNode(node);              \
+    if (shadow) {                                                 \
+      node = shadow->GetHost();                                   \
+    } else {                                                      \
+      node = node->GetParentNode();                               \
+    }                                                             \
   } while (node);                                                 \
   if (needsEnterLeave) {                                          \
     nsDOMMutationObserver::LeaveMutationHandling();               \
   }                                                               \
   PR_END_MACRO
 
 void
 nsNodeUtils::CharacterDataWillChange(nsIContent* aContent,
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -25,16 +25,17 @@
 #include "nsGenericDOMDataNode.h"
 #include "nsLayoutUtils.h"
 #include "nsTextFrame.h"
 #include "nsFontFaceList.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DocumentType.h"
 #include "mozilla/dom/RangeBinding.h"
 #include "mozilla/dom/DOMRect.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Likely.h"
 #include "nsCSSFrameConstructor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 JSObject*
@@ -1071,16 +1072,22 @@ nsRange::IsValidBoundary(nsINode* aNode)
 
   if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
     nsIContent* content = static_cast<nsIContent*>(aNode);
     if (content->Tag() == nsGkAtoms::documentTypeNodeName) {
       return nullptr;
     }
 
     if (!mMaySpanAnonymousSubtrees) {
+      // If the node is in a shadow tree then the ShadowRoot is the root.
+      ShadowRoot* containingShadow = content->GetContainingShadow();
+      if (containingShadow) {
+        return containingShadow;
+      }
+
       // If the node has a binding parent, that should be the root.
       // XXXbz maybe only for native anonymous content?
       nsINode* root = content->GetBindingParent();
       if (root) {
         return root;
       }
     }
   }
--- a/content/base/src/nsStyleLinkElement.cpp
+++ b/content/base/src/nsStyleLinkElement.cpp
@@ -9,16 +9,17 @@
  * be subclassed by various content nodes that want to load
  * stylesheets (<style>, <link>, processing instructions, etc).
  */
 
 #include "nsStyleLinkElement.h"
 
 #include "mozilla/css/Loader.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "nsCSSStyleSheet.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMStyleSheet.h"
 #include "nsNetUtil.h"
 #include "nsUnicharUtils.h"
@@ -171,27 +172,28 @@ uint32_t nsStyleLinkElement::ParseLinkTy
   return linkMask;
 }
 
 NS_IMETHODIMP
 nsStyleLinkElement::UpdateStyleSheet(nsICSSLoaderObserver* aObserver,
                                      bool* aWillNotify,
                                      bool* aIsAlternate)
 {
-  return DoUpdateStyleSheet(nullptr, aObserver, aWillNotify, aIsAlternate,
-                            false);
+  return DoUpdateStyleSheet(nullptr, nullptr, aObserver, aWillNotify,
+                            aIsAlternate, false);
 }
 
 nsresult
 nsStyleLinkElement::UpdateStyleSheetInternal(nsIDocument *aOldDocument,
+                                             ShadowRoot *aOldShadowRoot,
                                              bool aForceUpdate)
 {
   bool notify, alternate;
-  return DoUpdateStyleSheet(aOldDocument, nullptr, &notify, &alternate,
-                            aForceUpdate);
+  return DoUpdateStyleSheet(aOldDocument, aOldShadowRoot, nullptr, &notify,
+                            &alternate, aForceUpdate);
 }
 
 static bool
 IsScopedStyleElement(nsIContent* aContent)
 {
   // This is quicker than, say, QIing aContent to nsStyleLinkElement
   // and then calling its virtual GetStyleSheetInfo method to find out
   // if it is scoped.
@@ -267,45 +269,60 @@ GetScopeElement(nsIStyleSheet* aSheet)
   if (!cssStyleSheet) {
     return nullptr;
   }
 
   return cssStyleSheet->GetScopeElement();
 }
 
 nsresult
-nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
+nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
+                                       ShadowRoot* aOldShadowRoot,
                                        nsICSSLoaderObserver* aObserver,
                                        bool* aWillNotify,
                                        bool* aIsAlternate,
                                        bool aForceUpdate)
 {
   *aWillNotify = false;
 
   nsCOMPtr<nsIContent> thisContent;
   CallQueryInterface(this, getter_AddRefs(thisContent));
 
+  // All instances of nsStyleLinkElement should implement nsIContent.
+  NS_ENSURE_TRUE(thisContent, NS_ERROR_FAILURE);
+
+  // Check for a ShadowRoot because link elements are inert in a
+  // ShadowRoot.
+  ShadowRoot* containingShadow = thisContent->GetContainingShadow();
+  if (thisContent->IsHTML(nsGkAtoms::link) &&
+      (aOldShadowRoot || containingShadow)) {
+    return NS_OK;
+  }
+
   Element* oldScopeElement = GetScopeElement(mStyleSheet);
 
   if (mStyleSheet && aOldDocument) {
     // We're removing the link element from the document, unload the
     // stylesheet.  We want to do this even if updates are disabled, since
     // otherwise a sheet with a stale linking element pointer will be hanging
     // around -- not good!
-    aOldDocument->BeginUpdate(UPDATE_STYLE);
-    aOldDocument->RemoveStyleSheet(mStyleSheet);
-    aOldDocument->EndUpdate(UPDATE_STYLE);
+    if (aOldShadowRoot) {
+      aOldShadowRoot->RemoveSheet(mStyleSheet);
+    } else {
+      aOldDocument->BeginUpdate(UPDATE_STYLE);
+      aOldDocument->RemoveStyleSheet(mStyleSheet);
+      aOldDocument->EndUpdate(UPDATE_STYLE);
+    }
+
     nsStyleLinkElement::SetStyleSheet(nullptr);
     if (oldScopeElement) {
       UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
     }
   }
 
-  NS_ENSURE_TRUE(thisContent, NS_ERROR_FAILURE);
-
   // When static documents are created, stylesheets are cloned manually.
   if (mDontLoadStyle || !mUpdatesEnabled ||
       thisContent->OwnerDoc()->IsStaticDocument()) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIDocument> doc = thisContent->GetDocument();
 
@@ -323,19 +340,25 @@ nsStyleLinkElement::DoUpdateStyleSheet(n
       nsresult rv = oldURI->Equals(uri, &equal);
       if (NS_SUCCEEDED(rv) && equal) {
         return NS_OK; // We already loaded this stylesheet
       }
     }
   }
 
   if (mStyleSheet) {
-    doc->BeginUpdate(UPDATE_STYLE);
-    doc->RemoveStyleSheet(mStyleSheet);
-    doc->EndUpdate(UPDATE_STYLE);
+    if (thisContent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+      ShadowRoot* containingShadow = thisContent->GetContainingShadow();
+      containingShadow->RemoveSheet(mStyleSheet);
+    } else {
+      doc->BeginUpdate(UPDATE_STYLE);
+      doc->RemoveStyleSheet(mStyleSheet);
+      doc->EndUpdate(UPDATE_STYLE);
+    }
+
     nsStyleLinkElement::SetStyleSheet(nullptr);
   }
 
   if (!uri && !isInline) {
     return NS_OK; // If href is empty and this is not inline style then just bail
   }
 
   nsAutoString title, type, media;
@@ -415,23 +438,32 @@ nsStyleLinkElement::UpdateStyleSheetScop
                                nullptr;
 
   if (oldScopeElement == newScopeElement) {
     return;
   }
 
   nsIDocument* document = thisContent->GetOwnerDocument();
 
-  document->BeginUpdate(UPDATE_STYLE);
-  document->RemoveStyleSheet(mStyleSheet);
+  if (thisContent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    ShadowRoot* containingShadow = thisContent->GetContainingShadow();
+    containingShadow->RemoveSheet(mStyleSheet);
+
+    mStyleSheet->SetScopeElement(newScopeElement);
 
-  mStyleSheet->SetScopeElement(newScopeElement);
+    containingShadow->InsertSheet(mStyleSheet, thisContent);
+  } else {
+    document->BeginUpdate(UPDATE_STYLE);
+    document->RemoveStyleSheet(mStyleSheet);
 
-  document->AddStyleSheet(mStyleSheet);
-  document->EndUpdate(UPDATE_STYLE);
+    mStyleSheet->SetScopeElement(newScopeElement);
+
+    document->AddStyleSheet(mStyleSheet);
+    document->EndUpdate(UPDATE_STYLE);
+  }
 
   if (oldScopeElement) {
     UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
   }
   if (newScopeElement) {
     SetIsElementInStyleScopeFlagOnSubtree(newScopeElement);
   }
 }
--- a/content/base/src/nsStyleLinkElement.h
+++ b/content/base/src/nsStyleLinkElement.h
@@ -24,16 +24,22 @@
 #define DNS_PREFETCH  0x00000002
 #define STYLESHEET    0x00000004
 #define NEXT          0x00000008
 #define ALTERNATE     0x00000010
 
 class nsIDocument;
 class nsIURI;
 
+namespace mozilla {
+namespace dom {
+class ShadowRoot;
+} // namespace dom
+} // namespace mozilla
+
 class nsStyleLinkElement : public nsIStyleSheetLinkingElement
 {
 public:
   nsStyleLinkElement();
   virtual ~nsStyleLinkElement();
 
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) MOZ_OVERRIDE = 0;
 
@@ -48,28 +54,32 @@ public:
                               bool* aIsAlternate) MOZ_OVERRIDE;
   NS_IMETHOD SetEnableUpdates(bool aEnableUpdates) MOZ_OVERRIDE;
   NS_IMETHOD GetCharset(nsAString& aCharset) MOZ_OVERRIDE;
 
   virtual void OverrideBaseURI(nsIURI* aNewBaseURI) MOZ_OVERRIDE;
   virtual void SetLineNumber(uint32_t aLineNumber) MOZ_OVERRIDE;
 
   static uint32_t ParseLinkTypes(const nsAString& aTypes);
-  
-  void UpdateStyleSheetInternal() { UpdateStyleSheetInternal(nullptr); }
+
+  void UpdateStyleSheetInternal()
+  {
+    UpdateStyleSheetInternal(nullptr, nullptr);
+  }
 protected:
   /**
    * @param aOldDocument should be non-null only if we're updating because we
    *                     removed the node from the document.
    * @param aForceUpdate true will force the update even if the URI has not
    *                     changed.  This should be used in cases when something
    *                     about the content that affects the resulting sheet
    *                     changed but the URI may not have changed.
    */
   nsresult UpdateStyleSheetInternal(nsIDocument *aOldDocument,
+                                    mozilla::dom::ShadowRoot *aOldShadowRoot,
                                     bool aForceUpdate = false);
 
   void UpdateStyleSheetScopedness(bool aIsNowScoped);
 
   virtual already_AddRefed<nsIURI> GetStyleSheetURL(bool* aIsInline) = 0;
   virtual void GetStyleSheetInfo(nsAString& aTitle,
                                  nsAString& aType,
                                  nsAString& aMedia,
@@ -85,22 +95,27 @@ protected:
   // CC methods
   void Unlink();
   void Traverse(nsCycleCollectionTraversalCallback &cb);
 
 private:
   /**
    * @param aOldDocument should be non-null only if we're updating because we
    *                     removed the node from the document.
+   * @param aOldShadowRoot The ShadowRoot that used to contain the style.
+   *                     Passed as a parameter because on an update, the node
+   *                     is removed from the tree before the sheet is removed
+   *                     from the ShadowRoot.
    * @param aForceUpdate true will force the update even if the URI has not
    *                     changed.  This should be used in cases when something
    *                     about the content that affects the resulting sheet
    *                     changed but the URI may not have changed.
    */
-  nsresult DoUpdateStyleSheet(nsIDocument *aOldDocument,
+  nsresult DoUpdateStyleSheet(nsIDocument* aOldDocument,
+                              mozilla::dom::ShadowRoot* aOldShadowRoot,
                               nsICSSLoaderObserver* aObserver,
                               bool* aWillNotify,
                               bool* aIsAlternate,
                               bool aForceUpdate);
 
   nsRefPtr<nsCSSStyleSheet> mStyleSheet;
 protected:
   bool mDontLoadStyle;
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -1897,43 +1897,46 @@ WebGLContext::GetUniform(JSContext* cx, 
         GLfloat fv[16] = { GLfloat(0) };
         gl->fGetUniformfv(progname, location->Location(), fv);
         if (unitSize == 1) {
             return JS::DoubleValue(fv[0]);
         } else {
             JSObject* obj = Float32Array::Create(cx, this, unitSize, fv);
             if (!obj) {
                 ErrorOutOfMemory("getUniform: out of memory");
+                return JS::NullValue();
             }
             return JS::ObjectOrNullValue(obj);
         }
     } else if (baseType == LOCAL_GL_INT) {
         GLint iv[16] = { 0 };
         gl->fGetUniformiv(progname, location->Location(), iv);
         if (unitSize == 1) {
             return JS::Int32Value(iv[0]);
         } else {
             JSObject* obj = Int32Array::Create(cx, this, unitSize, iv);
             if (!obj) {
                 ErrorOutOfMemory("getUniform: out of memory");
+                return JS::NullValue();
             }
             return JS::ObjectOrNullValue(obj);
         }
     } else if (baseType == LOCAL_GL_BOOL) {
         GLint iv[16] = { 0 };
         gl->fGetUniformiv(progname, location->Location(), iv);
         if (unitSize == 1) {
             return JS::BooleanValue(iv[0] ? true : false);
         } else {
             JS::Value uv[16];
             for (int k = 0; k < unitSize; k++)
                 uv[k] = JS::BooleanValue(iv[k] ? true : false);
             JSObject* obj = JS_NewArrayObject(cx, unitSize, uv);
             if (!obj) {
                 ErrorOutOfMemory("getUniform: out of memory");
+                return JS::NullValue();
             }
             return JS::ObjectOrNullValue(obj);
         }
     }
 
     // Else preserving behavior, but I'm not sure this is correct per spec
     return JS::UndefinedValue();
 }
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/HTMLContentElement.cpp
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/HTMLContentElement.h"
+#include "mozilla/dom/HTMLContentElementBinding.h"
+#include "mozilla/dom/NodeListBinding.h"
+#include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/css/StyleRule.h"
+#include "nsGkAtoms.h"
+#include "nsStyleConsts.h"
+#include "nsIAtom.h"
+#include "nsCSSRuleProcessor.h"
+#include "nsRuleData.h"
+#include "nsRuleProcessorData.h"
+#include "nsRuleWalker.h"
+#include "nsCSSParser.h"
+
+NS_IMPL_NS_NEW_HTML_ELEMENT(Content)
+
+using namespace mozilla::dom;
+
+HTMLContentElement::HTMLContentElement(already_AddRefed<nsINodeInfo> aNodeInfo)
+  : nsGenericHTMLElement(aNodeInfo), mValidSelector(true), mIsInsertionPoint(false)
+{
+  SetIsDOMBinding();
+}
+
+HTMLContentElement::~HTMLContentElement()
+{
+}
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(HTMLContentElement,
+                                     nsGenericHTMLElement,
+                                     mMatchedNodes)
+
+NS_IMPL_ADDREF_INHERITED(HTMLContentElement, Element)
+NS_IMPL_RELEASE_INHERITED(HTMLContentElement, Element)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLContentElement)
+NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
+
+NS_IMPL_ELEMENT_CLONE(HTMLContentElement)
+
+JSObject*
+HTMLContentElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aScope)
+{
+  return HTMLContentElementBinding::Wrap(aCx, aScope, this);
+}
+
+nsresult
+HTMLContentElement::BindToTree(nsIDocument* aDocument,
+                               nsIContent* aParent,
+                               nsIContent* aBindingParent,
+                               bool aCompileEventHandlers)
+{
+  nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
+                                                 aBindingParent,
+                                                 aCompileEventHandlers);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  ShadowRoot* containingShadow = GetContainingShadow();
+  if (containingShadow) {
+    nsINode* parentNode = nsINode::GetParentNode();
+    while (parentNode && parentNode != containingShadow) {
+      if (parentNode->IsElement() &&
+          parentNode->AsElement()->IsHTML(nsGkAtoms::content)) {
+        // Content element in fallback content is not an insertion point.
+        return NS_OK;
+      }
+      parentNode = parentNode->GetParentNode();
+    }
+
+    // If the content element is being inserted into a ShadowRoot,
+    // add this element to the list of insertion points.
+    mIsInsertionPoint = true;
+    containingShadow->AddInsertionPoint(this);
+    containingShadow->SetInsertionPointChanged();
+  }
+
+  return NS_OK;
+}
+
+void
+HTMLContentElement::UnbindFromTree(bool aDeep, bool aNullParent)
+{
+  if (mIsInsertionPoint) {
+    ShadowRoot* containingShadow = GetContainingShadow();
+    // Make sure that containingShadow exists, it may have been nulled
+    // during unlinking in which case the ShadowRoot is going away.
+    if (containingShadow) {
+      containingShadow->RemoveInsertionPoint(this);
+
+      // Remove all the assigned nodes now that the
+      // insertion point now that the insertion point is
+      // no longer a descendant of a ShadowRoot.
+      ClearMatchedNodes();
+      containingShadow->SetInsertionPointChanged();
+    }
+
+    mIsInsertionPoint = false;
+  }
+
+  nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
+}
+
+void
+HTMLContentElement::ClearMatchedNodes()
+{
+  for (uint32_t i = 0; i < mMatchedNodes.Length(); i++) {
+    mMatchedNodes[i]->SetXBLInsertionParent(nullptr);
+  }
+  mMatchedNodes.Clear();
+}
+
+static bool
+IsValidContentSelectors(nsCSSSelector* aSelector)
+{
+  nsCSSSelector* currentSelector = aSelector;
+  while (currentSelector) {
+    // Blacklist invalid selector fragments.
+    if (currentSelector->IsPseudoElement() ||
+        currentSelector->mPseudoClassList ||
+        currentSelector->mNegations ||
+        currentSelector->mOperator) {
+      return false;
+    }
+
+    currentSelector = currentSelector->mNext;
+  }
+
+  return true;
+}
+
+nsresult
+HTMLContentElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                            nsIAtom* aPrefix, const nsAString& aValue,
+                            bool aNotify)
+{
+  nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
+                                              aValue, aNotify);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::select) {
+    // Select attribute was updated, the insertion point may match different
+    // elements.
+    nsIDocument* doc = OwnerDoc();
+    nsCSSParser parser(doc->CSSLoader());
+
+    mValidSelector = true;
+    mSelectorList = nullptr;
+
+    nsresult rv = parser.ParseSelectorString(aValue,
+                                             doc->GetDocumentURI(),
+                                             // Bug 11240
+                                             0, // XXX get the line number!
+                                             getter_Transfers(mSelectorList));
+
+    // We don't want to return an exception if parsing failed because
+    // the spec does not define it as an exception case.
+    if (NS_SUCCEEDED(rv)) {
+      // Ensure that all the selectors are valid
+      nsCSSSelectorList* selectors = mSelectorList;
+      while (selectors) {
+        if (!IsValidContentSelectors(selectors->mSelectors)) {
+          // If we have an invalid selector, we can not match anything.
+          mValidSelector = false;
+          mSelectorList = nullptr;
+          break;
+        }
+        selectors = selectors->mNext;
+      }
+    }
+
+    ShadowRoot* containingShadow = GetContainingShadow();
+    if (containingShadow) {
+      containingShadow->DistributeAllNodes();
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+HTMLContentElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
+                              bool aNotify)
+{
+  nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID,
+                                                aAttribute, aNotify);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::select) {
+    // The select attribute was removed. This insertion point becomes
+    // a universal selector.
+    mValidSelector = true;
+    mSelectorList = nullptr;
+
+    ShadowRoot* containingShadow = GetContainingShadow();
+    if (containingShadow) {
+      containingShadow->DistributeAllNodes();
+    }
+  }
+
+  return NS_OK;
+}
+
+bool
+HTMLContentElement::Match(nsIContent* aContent)
+{
+  if (!mValidSelector) {
+    return false;
+  }
+
+  if (mSelectorList) {
+    nsIDocument* doc = OwnerDoc();
+    ShadowRoot* containingShadow = GetContainingShadow();
+    nsIContent* host = containingShadow->GetHost();
+
+    TreeMatchContext matchingContext(false, nsRuleWalker::eRelevantLinkUnvisited,
+                                     doc, TreeMatchContext::eNeverMatchVisited);
+    doc->FlushPendingLinkUpdates();
+    matchingContext.SetHasSpecifiedScope();
+    matchingContext.AddScopeElement(host->AsElement());
+
+    if (!aContent->IsElement()) {
+      return false;
+    }
+
+    return nsCSSRuleProcessor::SelectorListMatches(aContent->AsElement(),
+                                                   matchingContext,
+                                                   mSelectorList);
+  }
+
+  return true;
+}
+
+already_AddRefed<DistributedContentList>
+HTMLContentElement::GetDistributedNodes()
+{
+  nsRefPtr<DistributedContentList> list = new DistributedContentList(this);
+  return list.forget();
+}
+
+NS_IMPL_CYCLE_COLLECTION_2(DistributedContentList, mParent, mDistributedNodes)
+
+NS_INTERFACE_TABLE_HEAD(DistributedContentList)
+  NS_INTERFACE_TABLE1(DistributedContentList, nsINodeList)
+  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(DistributedContentList)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DistributedContentList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DistributedContentList)
+
+DistributedContentList::DistributedContentList(HTMLContentElement* aHostElement)
+  : mParent(aHostElement)
+{
+  MOZ_COUNT_CTOR(DistributedContentList);
+  SetIsDOMBinding();
+
+  if (aHostElement->IsInsertionPoint()) {
+    if (aHostElement->MatchedNodes().IsEmpty()) {
+      // Fallback content.
+      nsINode* contentNode = aHostElement;
+      for (nsIContent* content = contentNode->GetFirstChild();
+           content;
+           content = content->GetNextSibling()) {
+        mDistributedNodes.AppendElement(content);
+      }
+    } else {
+      mDistributedNodes.AppendElements(aHostElement->MatchedNodes());
+    }
+  }
+}
+
+DistributedContentList::~DistributedContentList()
+{
+  MOZ_COUNT_DTOR(DistributedContentList);
+}
+
+nsIContent*
+DistributedContentList::Item(uint32_t aIndex)
+{
+  return mDistributedNodes.SafeElementAt(aIndex);
+}
+
+NS_IMETHODIMP
+DistributedContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
+{
+  nsIContent* item = Item(aIndex);
+  if (!item) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return CallQueryInterface(item, aReturn);
+}
+
+uint32_t
+DistributedContentList::Length() const
+{
+  return mDistributedNodes.Length();
+}
+
+NS_IMETHODIMP
+DistributedContentList::GetLength(uint32_t* aLength)
+{
+  *aLength = Length();
+  return NS_OK;
+}
+
+int32_t
+DistributedContentList::IndexOf(nsIContent* aContent)
+{
+  return mDistributedNodes.IndexOf(aContent);
+}
+
+JSObject*
+DistributedContentList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return NodeListBinding::Wrap(aCx, aScope, this);
+}
+
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/HTMLContentElement.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_HTMLContentElement_h__
+#define mozilla_dom_HTMLContentElement_h__
+
+#include "nsINodeList.h"
+#include "nsGenericHTMLElement.h"
+
+class nsCSSSelectorList;
+
+namespace mozilla {
+namespace dom {
+
+class DistributedContentList;
+
+class HTMLContentElement MOZ_FINAL : public nsGenericHTMLElement
+{
+public:
+  HTMLContentElement(already_AddRefed<nsINodeInfo> aNodeInfo);
+  virtual ~HTMLContentElement();
+
+  // nsISupports
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLContentElement,
+                                           nsGenericHTMLElement)
+
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+
+  virtual nsIDOMNode* AsDOMNode() { return this; }
+
+  virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+                              nsIContent* aBindingParent,
+                              bool aCompileEventHandlers);
+
+  virtual void UnbindFromTree(bool aDeep = true,
+                              bool aNullParent = true);
+
+  /**
+   * Returns whether if the selector of this insertion point
+   * matches the provided content.
+   */
+  bool Match(nsIContent* aContent);
+  bool IsInsertionPoint() const { return mIsInsertionPoint; }
+  nsCOMArray<nsIContent>& MatchedNodes() { return mMatchedNodes; }
+  void ClearMatchedNodes();
+
+  virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                           nsIAtom* aPrefix, const nsAString& aValue,
+                           bool aNotify);
+
+  virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
+                             bool aNotify);
+
+  // WebIDL methods.
+  already_AddRefed<DistributedContentList> GetDistributedNodes();
+  void GetSelect(nsAString& aSelect)
+  {
+    Element::GetAttr(kNameSpaceID_None, nsGkAtoms::select, aSelect);
+  }
+  void SetSelect(const nsAString& aSelect)
+  {
+    Element::SetAttr(kNameSpaceID_None, nsGkAtoms::select, aSelect, true);
+  }
+
+protected:
+  virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  /**
+   * An array of nodes from the ShadowRoot host that match the
+   * content insertion selector.
+   */
+  nsCOMArray<nsIContent> mMatchedNodes;
+
+  nsAutoPtr<nsCSSSelectorList> mSelectorList;
+  bool mValidSelector;
+  bool mIsInsertionPoint;
+};
+
+class DistributedContentList : public nsINodeList
+{
+public:
+  DistributedContentList(HTMLContentElement* aHostElement);
+  virtual ~DistributedContentList();
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(DistributedContentList)
+
+  // nsIDOMNodeList
+  NS_DECL_NSIDOMNODELIST
+
+  // nsINodeList
+  virtual nsIContent* Item(uint32_t aIndex);
+  virtual int32_t IndexOf(nsIContent* aContent);
+  virtual nsINode* GetParentObject() { return mParent; }
+  virtual uint32_t Length() const;
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+protected:
+  nsRefPtr<HTMLContentElement> mParent;
+  nsCOMArray<nsIContent> mDistributedNodes;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_HTMLContentElement_h__
+
--- a/content/html/content/src/HTMLLinkElement.cpp
+++ b/content/html/content/src/HTMLLinkElement.cpp
@@ -126,18 +126,19 @@ HTMLLinkElement::BindToTree(nsIDocument*
                             bool aCompileEventHandlers)
 {
   Link::ResetLinkState(false, Link::ElementHasHref());
 
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
-  
-  if (aDocument) {
+
+  // Link must be inert in ShadowRoot.
+  if (aDocument && !GetContainingShadow()) {
     aDocument->RegisterPendingLinkUpdate(this);
   }
 
   void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal;
   nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, update));
 
   CreateAndDispatchEvent(aDocument, NS_LITERAL_STRING("DOMLinkAdded"));
 
@@ -161,22 +162,29 @@ HTMLLinkElement::UnbindFromTree(bool aDe
 {
   // If this link is ever reinserted into a document, it might
   // be under a different xml:base, so forget the cached state now.
   Link::ResetLinkState(false, Link::ElementHasHref());
 
   // Once we have XPCOMGC we shouldn't need to call UnbindFromTree during Unlink
   // and so this messy event dispatch can go away.
   nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
-  if (oldDoc) {
+
+  // Check for a ShadowRoot because link elements are inert in a
+  // ShadowRoot.
+  ShadowRoot* oldShadowRoot = GetBindingParent() ?
+    GetBindingParent()->GetShadowRoot() : nullptr;
+
+  if (oldDoc && !oldShadowRoot) {
     oldDoc->UnregisterPendingLinkUpdate(this);
   }
   CreateAndDispatchEvent(oldDoc, NS_LITERAL_STRING("DOMLinkRemoved"));
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
-  UpdateStyleSheetInternal(oldDoc);
+
+  UpdateStyleSheetInternal(oldDoc, oldShadowRoot);
 }
 
 bool
 HTMLLinkElement::ParseAttribute(int32_t aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult)
 {
@@ -243,17 +251,17 @@ HTMLLinkElement::SetAttr(int32_t aNameSp
        aName == nsGkAtoms::media ||
        aName == nsGkAtoms::type)) {
     bool dropSheet = false;
     if (aName == nsGkAtoms::rel && GetSheet()) {
       uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(aValue);
       dropSheet = !(linkTypes & STYLESHEET);          
     }
     
-    UpdateStyleSheetInternal(nullptr,
+    UpdateStyleSheetInternal(nullptr, nullptr,
                              dropSheet ||
                              (aName == nsGkAtoms::title ||
                               aName == nsGkAtoms::media ||
                               aName == nsGkAtoms::type));
   }
 
   return rv;
 }
@@ -267,17 +275,17 @@ HTMLLinkElement::UnsetAttr(int32_t aName
   // Since removing href or rel makes us no longer link to a
   // stylesheet, force updates for those too.
   if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None &&
       (aAttribute == nsGkAtoms::href ||
        aAttribute == nsGkAtoms::rel ||
        aAttribute == nsGkAtoms::title ||
        aAttribute == nsGkAtoms::media ||
        aAttribute == nsGkAtoms::type)) {
-    UpdateStyleSheetInternal(nullptr, true);
+    UpdateStyleSheetInternal(nullptr, nullptr, true);
   }
 
   // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not unset until UnsetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
--- a/content/html/content/src/HTMLStyleElement.cpp
+++ b/content/html/content/src/HTMLStyleElement.cpp
@@ -128,17 +128,17 @@ HTMLStyleElement::ContentRemoved(nsIDocu
 {
   ContentChanged(aChild);
 }
 
 void
 HTMLStyleElement::ContentChanged(nsIContent* aContent)
 {
   if (nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
-    UpdateStyleSheetInternal(nullptr);
+    UpdateStyleSheetInternal(nullptr, nullptr);
   }
 }
 
 nsresult
 HTMLStyleElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                              nsIContent* aBindingParent,
                              bool aCompileEventHandlers)
 {
@@ -152,33 +152,33 @@ HTMLStyleElement::BindToTree(nsIDocument
 
   return rv;  
 }
 
 void
 HTMLStyleElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
-
+  ShadowRoot* oldShadow = GetContainingShadow();
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
-  UpdateStyleSheetInternal(oldDoc);
+  UpdateStyleSheetInternal(oldDoc, oldShadow);
 }
 
 nsresult
 HTMLStyleElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                           nsIAtom* aPrefix, const nsAString& aValue,
                           bool aNotify)
 {
   nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
                                               aValue, aNotify);
   if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::title ||
         aName == nsGkAtoms::media ||
         aName == nsGkAtoms::type) {
-      UpdateStyleSheetInternal(nullptr, true);
+      UpdateStyleSheetInternal(nullptr, nullptr, true);
     } else if (aName == nsGkAtoms::scoped) {
       UpdateStyleSheetScopedness(true);
     }
   }
 
   return rv;
 }
 
@@ -187,17 +187,17 @@ HTMLStyleElement::UnsetAttr(int32_t aNam
                             bool aNotify)
 {
   nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute,
                                                 aNotify);
   if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::title ||
         aAttribute == nsGkAtoms::media ||
         aAttribute == nsGkAtoms::type) {
-      UpdateStyleSheetInternal(nullptr, true);
+      UpdateStyleSheetInternal(nullptr, nullptr, true);
     } else if (aAttribute == nsGkAtoms::scoped) {
       UpdateStyleSheetScopedness(false);
     }
   }
 
   return rv;
 }
 
@@ -213,17 +213,17 @@ HTMLStyleElement::SetInnerHTML(const nsA
                                ErrorResult& aError)
 {
   SetEnableUpdates(false);
 
   aError = nsContentUtils::SetNodeTextContent(this, aInnerHTML, true);
 
   SetEnableUpdates(true);
 
-  UpdateStyleSheetInternal(nullptr);
+  UpdateStyleSheetInternal(nullptr, nullptr);
 }
 
 already_AddRefed<nsIURI>
 HTMLStyleElement::GetStyleSheetURL(bool* aIsInline)
 {
   *aIsInline = true;
   return nullptr;
 }
--- a/content/html/content/src/moz.build
+++ b/content/html/content/src/moz.build
@@ -11,16 +11,17 @@ EXPORTS += [
 ]
 
 EXPORTS.mozilla.dom += [
     'HTMLAnchorElement.h',
     'HTMLAreaElement.h',
     'HTMLBodyElement.h',
     'HTMLBRElement.h',
     'HTMLButtonElement.h',
+    'HTMLContentElement.h',
     'HTMLDataElement.h',
     'HTMLDataListElement.h',
     'HTMLDivElement.h',
     'HTMLFieldSetElement.h',
     'HTMLFontElement.h',
     'HTMLFormControlsCollection.h',
     'HTMLFormElement.h',
     'HTMLFrameElement.h',
@@ -78,16 +79,17 @@ EXPORTS.mozilla.dom += [
 UNIFIED_SOURCES += [
     'HTMLAnchorElement.cpp',
     'HTMLAreaElement.cpp',
     'HTMLAudioElement.cpp',
     'HTMLBodyElement.cpp',
     'HTMLBRElement.cpp',
     'HTMLButtonElement.cpp',
     'HTMLCanvasElement.cpp',
+    'HTMLContentElement.cpp',
     'HTMLDataElement.cpp',
     'HTMLDataListElement.cpp',
     'HTMLDivElement.cpp',
     'HTMLElement.cpp',
     'HTMLFieldSetElement.cpp',
     'HTMLFontElement.cpp',
     'HTMLFormControlsCollection.cpp',
     'HTMLFormElement.cpp',
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -1746,16 +1746,17 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(SharedObj
 
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Anchor)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Area)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Audio)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(BR)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Body)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Button)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Canvas)
+NS_DECLARE_NS_NEW_HTML_ELEMENT(Content)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Mod)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Data)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(DataList)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Div)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(FieldSet)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Font)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Form)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Frame)
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -73,17 +73,16 @@
 #include "nsIContentViewer.h"
 #include "nsIWyciwygChannel.h"
 #include "nsIScriptElement.h"
 #include "nsIScriptError.h"
 #include "nsIMutableArray.h"
 #include "nsArrayUtils.h"
 #include "nsIEffectiveTLDService.h"
 
-#include "nsIPrompt.h"
 //AHMED 12-2
 #include "nsBidiUtils.h"
 
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/FallbackEncoding.h"
 #include "nsIEditingSession.h"
 #include "nsIEditor.h"
 #include "nsNodeInfoManager.h"
@@ -1228,35 +1227,29 @@ nsHTMLDocument::SetCookie(const nsAStrin
   if (mSandboxFlags & SANDBOXED_ORIGIN) {
     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   // not having a cookie service isn't an error
   nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
   if (service && mDocumentURI) {
-    nsCOMPtr<nsIPrompt> prompt;
-    nsCOMPtr<nsPIDOMWindow> window = GetWindow();
-    if (window) {
-      window->GetPrompter(getter_AddRefs(prompt));
-    }
-
     // The for getting the URI matches nsNavigator::GetCookieEnabled
     nsCOMPtr<nsIURI> codebaseURI;
     NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
 
     if (!codebaseURI) {
       // Document's principal is not a codebase (may be system), so
       // can't set cookies
 
       return;
     }
 
     NS_ConvertUTF16toUTF8 cookie(aCookie);
-    service->SetCookieString(codebaseURI, prompt, cookie.get(), mChannel);
+    service->SetCookieString(codebaseURI, nullptr, cookie.get(), mChannel);
   }
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::Open(const nsAString& aContentTypeOrUrl,
                      const nsAString& aReplaceOrName,
                      const nsAString& aFeatures,
                      JSContext* cx, uint8_t aOptionalArgCount,
--- a/content/mathml/content/src/nsMathMLElement.cpp
+++ b/content/mathml/content/src/nsMathMLElement.cpp
@@ -162,16 +162,19 @@ static Element::MappedAttributeEntry sMt
   { nullptr }
 };
 
 static Element::MappedAttributeEntry sTokenStyles[] = {
   { &nsGkAtoms::mathsize_ },
   { &nsGkAtoms::fontsize_ },
   { &nsGkAtoms::color },
   { &nsGkAtoms::fontfamily_ },
+  { &nsGkAtoms::fontstyle_ },
+  { &nsGkAtoms::fontweight_ },
+  { &nsGkAtoms::mathvariant_},
   { nullptr }
 };
 
 static Element::MappedAttributeEntry sEnvironmentStyles[] = {
   { &nsGkAtoms::scriptlevel_ },
   { &nsGkAtoms::scriptminsize_ },
   { &nsGkAtoms::scriptsizemultiplier_ },
   { &nsGkAtoms::background },
@@ -638,16 +641,120 @@ nsMathMLElement::MapMathMLAttributesInto
       WarnDeprecated(nsGkAtoms::fontfamily_->GetUTF16String(),
                      nsGkAtoms::mathvariant_->GetUTF16String(),
                      aData->mPresContext->Document());
     }
     if (value && value->Type() == nsAttrValue::eString &&
         fontFamily->GetUnit() == eCSSUnit_Null) {
       fontFamily->SetStringValue(value->GetStringValue(), eCSSUnit_Families);
     }
+
+    // fontstyle
+    //
+    // "Specified the font style to use for the token. Deprecated in favor of
+    //  mathvariant."
+    //
+    // values: "normal" | "italic"
+    // default:	normal (except on <mi>)
+    //
+    // Note that the font-style property is reset in layout/style/ when
+    // -moz-math-variant is specified.
+    nsCSSValue* fontStyle = aData->ValueForFontStyle();
+    value = aAttributes->GetAttr(nsGkAtoms::fontstyle_);
+    if (value) {
+      WarnDeprecated(nsGkAtoms::fontstyle_->GetUTF16String(),
+                       nsGkAtoms::mathvariant_->GetUTF16String(),
+                       aData->mPresContext->Document());
+      if (value->Type() == nsAttrValue::eString &&
+          fontStyle->GetUnit() == eCSSUnit_Null) {
+        nsAutoString str(value->GetStringValue());
+        str.CompressWhitespace();
+        if (str.EqualsASCII("normal")) {
+          fontStyle->SetIntValue(NS_STYLE_FONT_STYLE_NORMAL,
+                                eCSSUnit_Enumerated);
+        } else if (str.EqualsASCII("italic")) {
+          fontStyle->SetIntValue(NS_STYLE_FONT_STYLE_ITALIC,
+                                eCSSUnit_Enumerated);
+        }
+      }
+    }
+
+    // fontweight
+    //
+    // "Specified the font weight for the token. Deprecated in favor of
+    // mathvariant."
+    //
+    // values: "normal" | "bold"
+    // default: normal
+    //
+    // Note that the font-weight property is reset in layout/style/ when
+    // -moz-math-variant is specified.
+    nsCSSValue* fontWeight = aData->ValueForFontWeight();
+    value = aAttributes->GetAttr(nsGkAtoms::fontweight_);
+    if (value) {
+      WarnDeprecated(nsGkAtoms::fontweight_->GetUTF16String(),
+                       nsGkAtoms::mathvariant_->GetUTF16String(),
+                       aData->mPresContext->Document());
+      if (value->Type() == nsAttrValue::eString &&
+          fontWeight->GetUnit() == eCSSUnit_Null) {
+        nsAutoString str(value->GetStringValue());
+        str.CompressWhitespace();
+        if (str.EqualsASCII("normal")) {
+          fontWeight->SetIntValue(NS_STYLE_FONT_WEIGHT_NORMAL,
+                                 eCSSUnit_Enumerated);
+        } else if (str.EqualsASCII("bold")) {
+          fontWeight->SetIntValue(NS_STYLE_FONT_WEIGHT_BOLD,
+                                  eCSSUnit_Enumerated);
+        }
+      }
+    }
+
+    // mathvariant
+    //
+    // "Specifies the logical class of the token. Note that this class is more
+    // than styling, it typically conveys semantic intent;"
+    //
+    // values: "normal" | "bold" | "italic" | "bold-italic" | "double-struck" |
+    // "bold-fraktur" | "script" | "bold-script" | "fraktur" | "sans-serif" |
+    // "bold-sans-serif" | "sans-serif-italic" | "sans-serif-bold-italic" |
+    // "monospace" | "initial" | "tailed" | "looped" | "stretched"
+    // default: normal (except on <mi>)
+    //
+    nsCSSValue* mathVariant = aData->ValueForMathVariant();
+    value = aAttributes->GetAttr(nsGkAtoms::mathvariant_);
+    if (value && value->Type() == nsAttrValue::eString &&
+        mathVariant->GetUnit() == eCSSUnit_Null) {
+      nsAutoString str(value->GetStringValue());
+      str.CompressWhitespace();
+      static const char sizes[19][23] = {
+        "normal", "bold", "italic", "bold-italic", "script", "bold-script",
+        "fraktur", "double-struck", "bold-fraktur", "sans-serif",
+        "bold-sans-serif", "sans-serif-italic", "sans-serif-bold-italic",
+        "monospace", "initial", "tailed", "looped", "stretched"
+      };
+      static const int32_t values[NS_ARRAY_LENGTH(sizes)] = {
+        NS_MATHML_MATHVARIANT_NORMAL, NS_MATHML_MATHVARIANT_BOLD,
+        NS_MATHML_MATHVARIANT_ITALIC, NS_MATHML_MATHVARIANT_BOLD_ITALIC,
+        NS_MATHML_MATHVARIANT_SCRIPT, NS_MATHML_MATHVARIANT_BOLD_SCRIPT,
+        NS_MATHML_MATHVARIANT_FRAKTUR, NS_MATHML_MATHVARIANT_DOUBLE_STRUCK,
+        NS_MATHML_MATHVARIANT_BOLD_FRAKTUR, NS_MATHML_MATHVARIANT_SANS_SERIF,
+        NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF,
+        NS_MATHML_MATHVARIANT_SANS_SERIF_ITALIC,
+        NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC,
+        NS_MATHML_MATHVARIANT_MONOSPACE, NS_MATHML_MATHVARIANT_INITIAL,
+        NS_MATHML_MATHVARIANT_TAILED, NS_MATHML_MATHVARIANT_LOOPED,
+        NS_MATHML_MATHVARIANT_STRETCHED
+      };
+      for (uint32_t i = 0; i < ArrayLength(sizes); ++i) {
+        if (str.EqualsASCII(sizes[i])) {
+          mathVariant->SetIntValue(values[i], eCSSUnit_Enumerated);
+          break;
+        }
+      }
+    }
   }
 
   // mathbackground
   // 
   // "Specifies the background color to be used to fill in the bounding box of
   // the element and its children. The default, 'transparent', lets the
   // background color, if any, used in the current rendering context to show
   // through."
@@ -714,17 +821,28 @@ nsMathMLElement::MapMathMLAttributesInto
     nsCSSValue* colorValue = aData->ValueForColor();
     if (value && value->GetColorValue(color) &&
         colorValue->GetUnit() == eCSSUnit_Null) {
       colorValue->SetColorValue(color);
     }
   }
 
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)) {
-    // width: value
+    // width
+    //
+    // "Specifies the desired width of the entire table and is intended for
+    // visual user agents. When the value is a percentage value, the value is
+    // relative to the horizontal space a MathML renderer has available for the
+    // math element. When the value is "auto", the MathML renderer should
+    // calculate the table width from its contents using whatever layout
+    // algorithm it chooses. "
+    //
+    // values: "auto" | length
+    // default: auto
+    //
     nsCSSValue* width = aData->ValueForWidth();
     if (width->GetUnit() == eCSSUnit_Null) {
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
       // This does not handle auto and unitless values
       if (value && value->Type() == nsAttrValue::eString) {
         ParseNumericValue(value->GetStringValue(), *width, 0,
                           aData->mPresContext->Document());
       }
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -156,16 +156,18 @@ AudioStream::~AudioStream()
 {
 #ifdef PR_LOGGING
   gAudioStreamLog = PR_NewLogModule("AudioStream");
 #endif
   PrefChanged(PREF_VOLUME_SCALE, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   PrefChanged(PREF_CUBEB_LATENCY, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
+
+  InitPreferredSampleRate();
 }
 
 /*static*/ void AudioStream::ShutdownLibrary()
 {
   Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
 
   StaticMutexAutoLock lock(sMutex);
@@ -260,38 +262,26 @@ int64_t AudioStream::GetWritten()
       cubeb_get_max_channel_count(cubebContext,
                                   &maxNumberOfChannels) == CUBEB_OK) {
     return static_cast<int>(maxNumberOfChannels);
   }
 
   return 0;
 }
 
-/*static*/ int AudioStream::PreferredSampleRate()
+/*static */ void AudioStream::InitPreferredSampleRate()
 {
-  const int fallbackSampleRate = 44100;
-  StaticMutexAutoLock lock(sMutex);
-  if (sPreferredSampleRate != 0) {
-    return sPreferredSampleRate;
-  }
-
-  cubeb* cubebContext = GetCubebContextUnlocked();
-  if (!cubebContext) {
-    sPreferredSampleRate = fallbackSampleRate;
-  }
   // Get the preferred samplerate for this platform, or fallback to something
   // sensible if we fail. We cache the value, because this might be accessed
   // often, and the complexity of the function call below depends on the
   // backend used.
-  if (cubeb_get_preferred_sample_rate(cubebContext,
+  if (cubeb_get_preferred_sample_rate(GetCubebContext(),
                                       &sPreferredSampleRate) != CUBEB_OK) {
-    sPreferredSampleRate = fallbackSampleRate;
+    sPreferredSampleRate = 44100;
   }
-
-  return sPreferredSampleRate;
 }
 
 static void SetUint16LE(uint8_t* aDest, uint16_t aValue)
 {
   aDest[0] = aValue & 0xFF;
   aDest[1] = aValue >> 8;
 }
 
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -174,19 +174,23 @@ public:
 
   // Shutdown Audio Library. Some Audio backends require shutting down the
   // library after using it.
   static void ShutdownLibrary();
 
   // Returns the maximum number of channels supported by the audio hardware.
   static int MaxNumberOfChannels();
 
+  static void InitPreferredSampleRate();
   // Returns the samplerate the systems prefer, because it is the
   // samplerate the hardware/mixer supports.
-  static int PreferredSampleRate();
+  static int PreferredSampleRate() {
+    MOZ_ASSERT(sPreferredSampleRate);
+    return sPreferredSampleRate;
+  }
 
   AudioStream();
   ~AudioStream();
 
   enum LatencyRequest {
     HighLatency,
     LowLatency
   };
@@ -368,24 +372,24 @@ private:
                  // cubeb, after which StateCallback will indicate drain
                  // completion.
     DRAINED,     // StateCallback has indicated that the drain is complete.
     ERRORED      // Stream disabled due to an internal error.
   };
 
   StreamState mState;
 
-  // This mutex protects the static members below.
-  static StaticMutex sMutex;
-  static cubeb* sCubebContext;
-
   // Prefered samplerate, in Hz (characteristic of the
   // hardware/mixer/platform/API used).
   static uint32_t sPreferredSampleRate;
 
+  // This mutex protects the static members below
+  static StaticMutex sMutex;
+  static cubeb* sCubebContext;
+
   static double sVolumeScale;
   static uint32_t sCubebLatency;
   static bool sCubebLatencyPrefSet;
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -447,17 +447,17 @@ ChannelMediaResource::OnStopRequest(nsIR
 
   return NS_OK;
 }
 
 nsresult
 ChannelMediaResource::OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew,
                                         uint32_t aFlags)
 {
-  mChannel = aNew;
+  mChannel = new nsMainThreadPtrHolder<nsIChannel>(aNew);
   SetupChannelHeaders();
   return NS_OK;
 }
 
 struct CopySegmentClosure {
   nsCOMPtr<nsIPrincipal> mPrincipal;
   ChannelMediaResource*  mResource;
 };
@@ -496,17 +496,17 @@ ChannelMediaResource::OnDataAvailable(ns
 
   {
     MutexAutoLock lock(mLock);
     mChannelStatistics->AddBytes(aCount);
   }
 
   CopySegmentClosure closure;
   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-  if (secMan && mChannel) {
+  if (secMan && mChannel.get()) {
     secMan->GetChannelPrincipal(mChannel, getter_AddRefs(closure.mPrincipal));
   }
   closure.mResource = this;
 
   uint32_t count = aCount;
   while (count > 0) {
     uint32_t read;
     nsresult rv = aStream->ReadSegments(CopySegmentToCache, &closure, count,
@@ -528,31 +528,31 @@ nsresult ChannelMediaResource::Open(nsIS
     mChannelStatistics = new MediaChannelStatistics();
   }
 
   nsresult rv = mCacheStream.Init();
   if (NS_FAILED(rv))
     return rv;
   NS_ASSERTION(mOffset == 0, "Who set mOffset already?");
 
-  if (!mChannel) {
+  if (!mChannel.get()) {
     // When we're a clone, the decoder might ask us to Open even though
     // we haven't established an mChannel (because we might not need one)
     NS_ASSERTION(!aStreamListener,
                  "Should have already been given a channel if we're to return a stream listener");
     return NS_OK;
   }
 
   return OpenChannel(aStreamListener);
 }
 
 nsresult ChannelMediaResource::OpenChannel(nsIStreamListener** aStreamListener)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-  NS_ENSURE_TRUE(mChannel, NS_ERROR_NULL_POINTER);
+  NS_ENSURE_TRUE(mChannel.get(), NS_ERROR_NULL_POINTER);
   NS_ASSERTION(!mListener, "Listener should have been removed by now");
 
   if (aStreamListener) {
     *aStreamListener = nullptr;
   }
 
   if (mByteRange.IsNull()) {
     // We're not making a byte range request, so set the content length,
@@ -708,17 +708,17 @@ void ChannelMediaResource::CloseChannel(
     mChannelStatistics->Stop();
   }
 
   if (mListener) {
     mListener->Revoke();
     mListener = nullptr;
   }
 
-  if (mChannel) {
+  if (mChannel.get()) {
     if (mSuspendCount > 0) {
       // Resume the channel before we cancel it
       PossiblyResume();
     }
     // The status we use here won't be passed to the decoder, since
     // we've already revoked the listener. It can however be passed
     // to nsDocumentViewer::LoadComplete if our channel is the one
     // that kicked off creation of a video document. We don't want that
@@ -806,17 +806,17 @@ void ChannelMediaResource::Suspend(bool 
     return;
   }
   dom::HTMLMediaElement* element = owner->GetMediaElement();
   if (!element) {
     // Shutting down; do nothing.
     return;
   }
 
-  if (mChannel) {
+  if (mChannel.get()) {
     if (aCloseImmediately && mCacheStream.IsTransportSeekable()) {
       // Kill off our channel right now, but don't tell anyone about it.
       mIgnoreClose = true;
       CloseChannel();
       element->DownloadSuspended();
     } else if (mSuspendCount == 0) {
       {
         MutexAutoLock lock(mLock);
@@ -844,17 +844,17 @@ void ChannelMediaResource::Resume()
   if (!element) {
     // Shutting down; do nothing.
     return;
   }
 
   NS_ASSERTION(mSuspendCount > 0, "Resume without previous Suspend!");
   --mSuspendCount;
   if (mSuspendCount == 0) {
-    if (mChannel) {
+    if (mChannel.get()) {
       // Just wake up our existing channel
       {
         MutexAutoLock lock(mLock);
         mChannelStatistics->Start();
       }
       // if an error occurs after Resume, assume it's because the server
       // timed out the connection and we should reopen it.
       mReopenOnError = true;
@@ -892,22 +892,24 @@ ChannelMediaResource::RecreateChannel()
   dom::HTMLMediaElement* element = owner->GetMediaElement();
   if (!element) {
     // The decoder is being shut down, so don't bother opening a new channel
     return NS_OK;
   }
   nsCOMPtr<nsILoadGroup> loadGroup = element->GetDocumentLoadGroup();
   NS_ENSURE_TRUE(loadGroup, NS_ERROR_NULL_POINTER);
 
-  nsresult rv = NS_NewChannel(getter_AddRefs(mChannel),
+  nsCOMPtr<nsIChannel> channel;
+  nsresult rv = NS_NewChannel(getter_AddRefs(channel),
                               mURI,
                               nullptr,
                               loadGroup,
                               nullptr,
                               loadFlags);
+  mChannel = new nsMainThreadPtrHolder<nsIChannel>(channel);
 
   // We have cached the Content-Type, which should not change. Give a hint to
   // the channel to avoid a sniffing failure, which would be expected because we
   // are probably seeking in the middle of the bitstream, and sniffing relies
   // on the presence of a magic number at the beginning of the stream.
   NS_ASSERTION(!GetContentType().IsEmpty(),
       "When recreating a channel, we should know the Content-Type.");
   mChannel->SetContentType(GetContentType());
@@ -985,17 +987,17 @@ ChannelMediaResource::CacheClientSeek(in
     --mSuspendCount;
   }
 
   mOffset = aOffset;
 
   if (mSuspendCount > 0) {
     // Close the existing channel to force the channel to be recreated at
     // the correct offset upon resume.
-    if (mChannel) {
+    if (mChannel.get()) {
       mIgnoreClose = true;
       CloseChannel();
     }
     return NS_OK;
   }
 
   nsresult rv = RecreateChannel();
   if (NS_FAILED(rv))
@@ -1335,31 +1337,31 @@ nsresult FileMediaResource::Open(nsIStre
 }
 
 nsresult FileMediaResource::Close()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   // Since mChennel is only accessed by main thread, there is no necessary to
   // take the lock.
-  if (mChannel) {
+  if (mChannel.get()) {
     mChannel->Cancel(NS_ERROR_PARSED_DATA_CACHED);
     mChannel = nullptr;
   }
 
   return NS_OK;
 }
 
 already_AddRefed<nsIPrincipal> FileMediaResource::GetCurrentPrincipal()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   nsCOMPtr<nsIPrincipal> principal;
   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-  if (!secMan || !mChannel)
+  if (!secMan || !mChannel.get())
     return nullptr;
   secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal));
   return principal.forget();
 }
 
 bool FileMediaResource::CanClone()
 {
   return true;
@@ -1518,17 +1520,17 @@ MediaResource::Create(MediaDecoder* aDec
     resource = new ChannelMediaResource(aDecoder, aChannel, uri, contentType);
   }
   return resource.forget();
 }
 
 void BaseMediaResource::MoveLoadsToBackground() {
   NS_ASSERTION(!mLoadInBackground, "Why are you calling this more than once?");
   mLoadInBackground = true;
-  if (!mChannel) {
+  if (!mChannel.get()) {
     // No channel, resource is probably already loaded.
     return;
   }
 
   MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
   if (!owner) {
     NS_WARNING("Null owner in MediaResource::MoveLoadsToBackground()");
     return;
--- a/content/media/MediaResource.h
+++ b/content/media/MediaResource.h
@@ -8,16 +8,17 @@
 
 #include "mozilla/Mutex.h"
 #include "nsIChannel.h"
 #include "nsIURI.h"
 #include "nsIStreamingProtocolController.h"
 #include "nsIStreamListener.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
+#include "nsProxyRelease.h"
 #include "MediaCache.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TimeStamp.h"
 #include "nsThreadUtils.h"
 
 // For HTTP seeking, if number of bytes needing to be
 // seeked forward is less than this value then a read is
 // done rather than a byte range request.
@@ -390,27 +391,27 @@ public:
   }
 
 protected:
   virtual ~MediaResource() {};
 };
 
 class BaseMediaResource : public MediaResource {
 public:
-  virtual nsIURI* URI() const { return mURI; }
+  virtual nsIURI* URI() const { return const_cast<nsIURI*>(mURI.get()); }
   virtual void MoveLoadsToBackground();
 
 protected:
   BaseMediaResource(MediaDecoder* aDecoder,
                     nsIChannel* aChannel,
                     nsIURI* aURI,
                     const nsACString& aContentType) :
     mDecoder(aDecoder),
-    mChannel(aChannel),
-    mURI(aURI),
+    mChannel(new nsMainThreadPtrHolder<nsIChannel>(aChannel)),
+    mURI(new nsMainThreadPtrHolder<nsIURI>(aURI)),
     mContentType(aContentType),
     mLoadInBackground(false)
   {
     MOZ_COUNT_CTOR(BaseMediaResource);
     NS_ASSERTION(!mContentType.IsEmpty(), "Must know content type");
   }
   virtual ~BaseMediaResource()
   {
@@ -433,21 +434,21 @@ protected:
 
   // This is not an nsCOMPointer to prevent a circular reference
   // between the decoder to the media stream object. The stream never
   // outlives the lifetime of the decoder.
   MediaDecoder* mDecoder;
 
   // Channel used to download the media data. Must be accessed
   // from the main thread only.
-  nsCOMPtr<nsIChannel> mChannel;
+  nsMainThreadPtrHandle<nsIChannel> mChannel;
 
   // URI in case the stream needs to be re-opened. Access from
   // main thread only.
-  nsCOMPtr<nsIURI> mURI;
+  nsMainThreadPtrHandle<nsIURI> mURI;
 
   // Content-Type of the channel. This is copied from the nsIChannel when the
   // MediaResource is created. This is constant, so accessing from any thread
   // is safe.
   const nsAutoCString mContentType;
 
   // True if MoveLoadsToBackground() has been called, i.e. the load event
   // has been fired, and all channel loads will be in the background.
--- a/content/media/VideoSegment.cpp
+++ b/content/media/VideoSegment.cpp
@@ -27,16 +27,17 @@ VideoFrame::SetNull() {
   mIntrinsicSize = gfxIntSize(0, 0);
 }
 
 void
 VideoFrame::TakeFrom(VideoFrame* aFrame)
 {
   mImage = aFrame->mImage.forget();
   mIntrinsicSize = aFrame->mIntrinsicSize;
+  mForceBlack = aFrame->GetForceBlack();
 }
 
 VideoChunk::VideoChunk()
 {}
 
 VideoChunk::~VideoChunk()
 {}
 
--- a/content/media/VideoSegment.h
+++ b/content/media/VideoSegment.h
@@ -33,17 +33,17 @@ public:
            ((mForceBlack && aFrame.mForceBlack) || mImage == aFrame.mImage);
   }
   bool operator!=(const VideoFrame& aFrame) const
   {
     return !operator==(aFrame);
   }
 
   Image* GetImage() const { return mImage; }
-  void SetForceBlack(bool aForceBlack) { mForceBlack = true; }
+  void SetForceBlack(bool aForceBlack) { mForceBlack = aForceBlack; }
   bool GetForceBlack() const { return mForceBlack; }
   const gfxIntSize& GetIntrinsicSize() const { return mIntrinsicSize; }
   void SetNull();
   void TakeFrom(VideoFrame* aFrame);
 
 protected:
   // mImage can be null to indicate "no video" (aka "empty frame"). It can
   // still have an intrinsic size in this case.
--- a/content/media/encoder/EncodedFrameContainer.h
+++ b/content/media/encoder/EncodedFrameContainer.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef EncodedFrameContainer_H_
 #define EncodedFrameContainer_H_
 
+#include "nsAutoPtr.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
 class EncodedFrame;
 
 /*
  * This container is used to carry video or audio encoded data from encoder to muxer.
--- a/content/media/encoder/OpusTrackEncoder.cpp
+++ b/content/media/encoder/OpusTrackEncoder.cpp
@@ -111,43 +111,42 @@ SerializeOpusCommentHeader(const nsCStri
   }
 }
 
 }  // Anonymous namespace.
 
 OpusTrackEncoder::OpusTrackEncoder()
   : AudioTrackEncoder()
   , mEncoder(nullptr)
-  , mSourceSegment(new AudioSegment())
   , mLookahead(0)
   , mResampler(nullptr)
 {
 }
 
 OpusTrackEncoder::~OpusTrackEncoder()
 {
   if (mEncoder) {
     opus_encoder_destroy(mEncoder);
   }
   if (mResampler) {
     speex_resampler_destroy(mResampler);
+    mResampler = nullptr;
   }
-
 }
 
 nsresult
 OpusTrackEncoder::Init(int aChannels, int aSamplingRate)
 {
   // This monitor is used to wake up other methods that are waiting for encoder
   // to be completely initialized.
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   // This version of encoder API only support 1 or 2 channels,
   // So set the mChannels less or equal 2 and
   // let InterleaveTrackData downmix pcm data.
-  mChannels = aChannels > 2 ? 2 : aChannels;
+  mChannels = aChannels > MAX_CHANNELS ? MAX_CHANNELS : aChannels;
 
   if (aChannels <= 0) {
     return NS_ERROR_FAILURE;
   }
   // The granule position is required to be incremented at a rate of 48KHz, and
   // it is simply calculated as |granulepos = samples * (48000/source_rate)|,
   // that is, the source sampling rate must divide 48000 evenly.
   // If this constraint is not satisfied, we resample the input to 48kHz.
@@ -190,22 +189,22 @@ OpusTrackEncoder::GetPacketDuration()
 }
 
 already_AddRefed<TrackMetadataBase>
 OpusTrackEncoder::GetMetadata()
 {
   {
     // Wait if mEncoder is not initialized.
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    while (!mCanceled && !mEncoder) {
+    while (!mCanceled && !mInitialized) {
       mReentrantMonitor.Wait();
     }
   }
 
-  if (mCanceled || mDoneEncoding) {
+  if (mCanceled || mEncodingComplete) {
     return nullptr;
   }
 
   nsRefPtr<OpusMetadata> meta = new OpusMetadata();
 
   mLookahead = 0;
   int error = opus_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&mLookahead));
   if (error != OPUS_OK) {
@@ -233,39 +232,40 @@ OpusTrackEncoder::GetEncodedTrack(Encode
 {
   {
     // Move all the samples from mRawSegment to mSourceSegment. We only hold
     // the monitor in this block.
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
     // Wait if mEncoder is not initialized, or when not enough raw data, but is
     // not the end of stream nor is being canceled.
-    while (!mCanceled && (!mEncoder || (mRawSegment->GetDuration() +
-           mSourceSegment->GetDuration() < GetPacketDuration() &&
+    while (!mCanceled && (!mInitialized || (mRawSegment.GetDuration() +
+           mSourceSegment.GetDuration() < GetPacketDuration() &&
            !mEndOfStream))) {
       mReentrantMonitor.Wait();
     }
 
-    if (mCanceled || mDoneEncoding) {
+    if (mCanceled || mEncodingComplete) {
       return NS_ERROR_FAILURE;
     }
 
-    mSourceSegment->AppendFrom(mRawSegment);
+    mSourceSegment.AppendFrom(&mRawSegment);
 
     // Pad |mLookahead| samples to the end of source stream to prevent lost of
     // original data, the pcm duration will be calculated at rate 48K later.
-    if (mEndOfStream) {
-      mSourceSegment->AppendNullData(mLookahead);
+    if (mEndOfStream && !mEosSetInEncoder) {
+      mEosSetInEncoder = true;
+      mSourceSegment.AppendNullData(mLookahead);
     }
   }
 
   // Start encoding data.
   nsAutoTArray<AudioDataValue, 9600> pcm;
   pcm.SetLength(GetPacketDuration() * mChannels);
-  AudioSegment::ChunkIterator iter(*mSourceSegment);
+  AudioSegment::ChunkIterator iter(mSourceSegment);
   int frameCopied = 0;
   while (!iter.IsEnded() && frameCopied < GetPacketDuration()) {
     AudioChunk chunk = *iter;
 
     // Chunk to the required frame size.
     int frameToCopy = chunk.GetDuration();
     if (frameCopied + frameToCopy > GetPacketDuration()) {
       frameToCopy = GetPacketDuration() - frameCopied;
@@ -314,22 +314,22 @@ OpusTrackEncoder::GetEncodedTrack(Encode
   } else {
     // The ogg time stamping and pre-skip is always timed at 48000.
     audiodata->SetDuration(frameCopied * (kOpusSamplingRate / mSamplingRate));
   }
 
   // Remove the raw data which has been pulled to pcm buffer.
   // The value of frameCopied should equal to (or smaller than, if eos)
   // GetPacketDuration().
-  mSourceSegment->RemoveLeading(frameCopied);
+  mSourceSegment.RemoveLeading(frameCopied);
 
   // Has reached the end of input stream and all queued data has pulled for
   // encoding.
-  if (mSourceSegment->GetDuration() == 0 && mEndOfStream) {
-    mDoneEncoding = true;
+  if (mSourceSegment.GetDuration() == 0 && mEndOfStream) {
+    mEncodingComplete = true;
     LOG("[Opus] Done encoding.");
   }
 
   // Append null data to pcm buffer if the leftover data is not enough for
   // opus encoder.
   if (frameCopied < GetPacketDuration() && mEndOfStream) {
     memset(pcm.Elements() + frameCopied * mChannels, 0,
            (GetPacketDuration()-frameCopied)*mChannels*sizeof(AudioDataValue));
@@ -348,17 +348,17 @@ OpusTrackEncoder::GetEncodedTrack(Encode
   result = opus_encode_float(mEncoder, pcmBuf, GetPacketDuration(),
                              frameData.Elements(), MAX_DATA_BYTES);
 #endif
   frameData.SetLength(result >= 0 ? result : 0);
 
   if (result < 0) {
     LOG("[Opus] Fail to encode data! Result: %s.", opus_strerror(result));
   }
-  if (mDoneEncoding) {
+  if (mEncodingComplete) {
     if (mResampler) {
       speex_resampler_destroy(mResampler);
       mResampler = nullptr;
     }
   }
 
   audiodata->SetFrameData(&frameData);
   aData.AppendEncodedFrame(audiodata);
--- a/content/media/encoder/OpusTrackEncoder.h
+++ b/content/media/encoder/OpusTrackEncoder.h
@@ -32,38 +32,39 @@ public:
   OpusTrackEncoder();
   virtual ~OpusTrackEncoder();
 
   already_AddRefed<TrackMetadataBase> GetMetadata() MOZ_OVERRIDE;
 
   nsresult GetEncodedTrack(EncodedFrameContainer& aData) MOZ_OVERRIDE;
 
 protected:
-  int GetPacketDuration() MOZ_OVERRIDE;
+  int GetPacketDuration();
 
   nsresult Init(int aChannels, int aSamplingRate) MOZ_OVERRIDE;
 
 private:
   /**
    * Get the samplerate of the data to be fed to the Opus encoder. This might be
    * different from the intput samplerate if resampling occurs.
    */
   int GetOutputSampleRate();
 
   /**
    * The Opus encoder from libopus.
    */
   OpusEncoder* mEncoder;
 
   /**
-   * A local segment queue which stores the raw segments. Opus encoder only
-   * takes GetPacketDuration() samples from mSourceSegment in every encoding
-   * cycle, thus it needs to store the raw track data.
+   * A local segment queue which takes the raw data out from mRawSegment in the
+   * call of GetEncodedTrack(). Opus encoder only accepts GetPacketDuration()
+   * samples from mSourceSegment every encoding cycle, thus it needs to be
+   * global in order to store the leftover segments taken from mRawSegment.
    */
-  nsAutoPtr<AudioSegment> mSourceSegment;
+  AudioSegment mSourceSegment;
 
   /**
    * Total samples of delay added by codec, can be queried by the encoder. From
    * the perspective of decoding, real data begins this many samples late, so
    * the encoder needs to append this many null samples to the end of stream,
    * in order to align the time of input and output.
    */
   int mLookahead;
--- a/content/media/encoder/TrackEncoder.cpp
+++ b/content/media/encoder/TrackEncoder.cpp
@@ -1,125 +1,107 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "TrackEncoder.h"
+#include "AudioChannelFormat.h"
 #include "MediaStreamGraph.h"
-#include "AudioChannelFormat.h"
+#include "VideoUtils.h"
 
 #undef LOG
 #ifdef MOZ_WIDGET_GONK
 #include <android/log.h>
-#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediakEncoder", ## args);
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaEncoder", ## args);
 #else
 #define LOG(args, ...)
 #endif
 
 namespace mozilla {
 
-static const int  DEFAULT_CHANNELS = 1;
-static const int  DEFAULT_SAMPLING_RATE = 16000;
+static const int DEFAULT_CHANNELS = 1;
+static const int DEFAULT_SAMPLING_RATE = 16000;
+static const int DEFAULT_FRAME_WIDTH = 640;
+static const int DEFAULT_FRAME_HEIGHT = 480;
+static const int DEFAULT_TRACK_RATE = USECS_PER_S;
 
 void
 AudioTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
                                             TrackID aID,
                                             TrackRate aTrackRate,
                                             TrackTicks aTrackOffset,
                                             uint32_t aTrackEvents,
                                             const MediaSegment& aQueuedMedia)
 {
   if (mCanceled) {
     return;
   }
 
-  AudioSegment* audio = const_cast<AudioSegment*>
-                        (static_cast<const AudioSegment*>(&aQueuedMedia));
+  const AudioSegment& audio = static_cast<const AudioSegment&>(aQueuedMedia);
 
   // Check and initialize parameters for codec encoder.
   if (!mInitialized) {
-    AudioSegment::ChunkIterator iter(*audio);
+    AudioSegment::ChunkIterator iter(const_cast<AudioSegment&>(audio));
     while (!iter.IsEnded()) {
       AudioChunk chunk = *iter;
 
       // The number of channels is determined by the first non-null chunk, and
       // thus the audio encoder is initialized at this time.
       if (!chunk.IsNull()) {
         nsresult rv = Init(chunk.mChannelData.Length(), aTrackRate);
         if (NS_FAILED(rv)) {
           LOG("[AudioTrackEncoder]: Fail to initialize the encoder!");
           NotifyCancel();
         }
         break;
-      } else {
-        mSilentDuration += chunk.mDuration;
       }
+
       iter.Next();
     }
   }
 
   // Append and consume this raw segment.
-  if (mInitialized) {
-    AppendAudioSegment(audio);
-  }
+  AppendAudioSegment(audio);
+
 
   // The stream has stopped and reached the end of track.
   if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) {
     LOG("[AudioTrackEncoder]: Receive TRACK_EVENT_ENDED .");
     NotifyEndOfStream();
   }
 }
 
 void
-AudioTrackEncoder::NotifyRemoved(MediaStreamGraph* aGraph)
-{
-  // In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
-  LOG("[AudioTrackEncoder]: NotifyRemoved.");
-  NotifyEndOfStream();
-}
-
-void
 AudioTrackEncoder::NotifyEndOfStream()
 {
-  // If source audio chunks are completely silent till the end of encoding,
-  // initialize the encoder with default channel counts and sampling rate, and
-  // append this many null data to the segment of track encoder.
+  // If source audio track is completely silent till the end of encoding,
+  // initialize the encoder with default channel counts and sampling rate.
   if (!mCanceled && !mInitialized) {
     Init(DEFAULT_CHANNELS, DEFAULT_SAMPLING_RATE);
-    mRawSegment->AppendNullData(mSilentDuration);
-    mSilentDuration = 0;
   }
 
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   mEndOfStream = true;
   mReentrantMonitor.NotifyAll();
 }
 
 nsresult
-AudioTrackEncoder::AppendAudioSegment(MediaSegment* aSegment)
+AudioTrackEncoder::AppendAudioSegment(const AudioSegment& aSegment)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
-  AudioSegment* audio = static_cast<AudioSegment*>(aSegment);
-  AudioSegment::ChunkIterator iter(*audio);
-
-  // Append this many null data to our queued segment if there is a complete
-  // silence before the audio track encoder has initialized.
-  if (mSilentDuration > 0) {
-    mRawSegment->AppendNullData(mSilentDuration);
-    mSilentDuration = 0;
-  }
-
+  AudioSegment::ChunkIterator iter(const_cast<AudioSegment&>(aSegment));
   while (!iter.IsEnded()) {
     AudioChunk chunk = *iter;
     // Append and consume both non-null and null chunks.
-    mRawSegment->AppendAndConsumeChunk(&chunk);
+    mRawSegment.AppendAndConsumeChunk(&chunk);
     iter.Next();
   }
-  if (mRawSegment->GetDuration() >= GetPacketDuration()) {
+
+  if (mRawSegment.GetDuration() >= GetPacketDuration()) {
     mReentrantMonitor.NotifyAll();
   }
 
   return NS_OK;
 }
 
 static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */
 static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES] = {0};
@@ -140,9 +122,108 @@ AudioTrackEncoder::InterleaveTrackData(A
                          aChunk.mVolume, mChannels, aOutput);
   } else {
     InterleaveAndConvertBuffer(aChunk.mChannelData.Elements(),
                                aChunk.mBufferFormat, aDuration, aChunk.mVolume,
                                mChannels, aOutput);
   }
 }
 
+void
+VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
+                                            TrackID aID,
+                                            TrackRate aTrackRate,
+                                            TrackTicks aTrackOffset,
+                                            uint32_t aTrackEvents,
+                                            const MediaSegment& aQueuedMedia)
+{
+  if (mCanceled) {
+    return;
+  }
+
+  const VideoSegment& video = static_cast<const VideoSegment&>(aQueuedMedia);
+
+   // Check and initialize parameters for codec encoder.
+  if (!mInitialized) {
+    VideoSegment::ChunkIterator iter(const_cast<VideoSegment&>(video));
+    while (!iter.IsEnded()) {
+      VideoChunk chunk = *iter;
+      if (!chunk.IsNull()) {
+        gfxIntSize imgsize = chunk.mFrame.GetImage()->GetSize();
+        int width = (imgsize.width + 1) / 2 * 2;
+        int height = (imgsize.height + 1) / 2 * 2;
+        nsresult rv = Init(width, height, aTrackRate);
+        if (NS_FAILED(rv)) {
+          LOG("[VideoTrackEncoder]: Fail to initialize the encoder!");
+          NotifyCancel();
+        }
+        break;
+      }
+
+      iter.Next();
+    }
+  }
+
+  AppendVideoSegment(video);
+
+  // The stream has stopped and reached the end of track.
+  if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) {
+    LOG("[VideoTrackEncoder]: Receive TRACK_EVENT_ENDED .");
+    NotifyEndOfStream();
+  }
+
 }
+
+nsresult
+VideoTrackEncoder::AppendVideoSegment(const VideoSegment& aSegment)
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  // Append all video segments from MediaStreamGraph, including null an
+  // non-null frames.
+  VideoSegment::ChunkIterator iter(const_cast<VideoSegment&>(aSegment));
+  while (!iter.IsEnded()) {
+    VideoChunk chunk = *iter;
+    nsRefPtr<layers::Image> image = chunk.mFrame.GetImage();
+    mRawSegment.AppendFrame(image.forget(), chunk.GetDuration(),
+                            chunk.mFrame.GetIntrinsicSize());
+    iter.Next();
+  }
+
+  if (mRawSegment.GetDuration() > 0) {
+    mReentrantMonitor.NotifyAll();
+  }
+
+  return NS_OK;
+}
+
+void
+VideoTrackEncoder::NotifyEndOfStream()
+{
+  // If source video track is muted till the end of encoding, initialize the
+  // encoder with default frame width, frame height, and track rate.
+  if (!mCanceled && !mInitialized) {
+    Init(DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT, DEFAULT_TRACK_RATE);
+  }
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  mEndOfStream = true;
+  mReentrantMonitor.NotifyAll();
+}
+
+void
+VideoTrackEncoder::CreateMutedFrame(nsTArray<uint8_t>* aOutputBuffer)
+{
+  NS_ENSURE_TRUE_VOID(aOutputBuffer);
+
+  // Supports YUV420 image format only.
+  int yPlaneLen = mFrameWidth * mFrameHeight;
+  int cbcrPlaneLen = yPlaneLen / 2;
+  int frameLen = yPlaneLen + cbcrPlaneLen;
+
+  aOutputBuffer->SetLength(frameLen);
+  // Fill Y plane.
+  memset(aOutputBuffer->Elements(), 0x10, yPlaneLen);
+  // Fill Cb/Cr planes.
+  memset(aOutputBuffer->Elements() + yPlaneLen, 0x80, cbcrPlaneLen);
+}
+
+}
--- a/content/media/encoder/TrackEncoder.h
+++ b/content/media/encoder/TrackEncoder.h
@@ -4,19 +4,20 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TrackEncoder_h_
 #define TrackEncoder_h_
 
 #include "mozilla/ReentrantMonitor.h"
 
 #include "AudioSegment.h"
+#include "EncodedFrameContainer.h"
 #include "StreamBuffer.h"
 #include "TrackMetadataBase.h"
-#include "EncodedFrameContainer.h"
+#include "VideoSegment.h"
 
 namespace mozilla {
 
 class MediaStreamGraph;
 
 /**
  * Base class of AudioTrackEncoder and VideoTrackEncoder. Lifetimes managed by
  * MediaEncoder. Most methods can only be called on the MediaEncoder's thread,
@@ -25,116 +26,163 @@ class MediaStreamGraph;
  * NotifyQueuedTrackChanges is called on subclasses of this class from the
  * MediaStreamGraph thread, and AppendAudioSegment/AppendVideoSegment is then
  * called to store media data in the TrackEncoder. Later on, GetEncodedTrack is
  * called on MediaEncoder's thread to encode and retrieve the encoded data.
  */
 class TrackEncoder
 {
 public:
-  TrackEncoder() {}
+  TrackEncoder()
+    : mReentrantMonitor("media.TrackEncoder")
+    , mEncodingComplete(false)
+    , mEosSetInEncoder(false)
+    , mInitialized(false)
+    , mEndOfStream(false)
+    , mCanceled(false)
+  {}
+
   virtual ~TrackEncoder() {}
 
   /**
    * Notified by the same callbcak of MediaEncoder when it has received a track
    * change from MediaStreamGraph. Called on the MediaStreamGraph thread.
    */
   virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                         TrackRate aTrackRate,
                                         TrackTicks aTrackOffset,
                                         uint32_t aTrackEvents,
                                         const MediaSegment& aQueuedMedia) = 0;
 
   /**
    * Notified by the same callback of MediaEncoder when it has been removed from
    * MediaStreamGraph. Called on the MediaStreamGraph thread.
    */
-  virtual void NotifyRemoved(MediaStreamGraph* aGraph) = 0;
+  void NotifyRemoved(MediaStreamGraph* aGraph) { NotifyEndOfStream(); }
 
   /**
-   * Creates and sets up meta data for a specific codec
+   * Creates and sets up meta data for a specific codec, called on the worker
+   * thread.
    */
   virtual already_AddRefed<TrackMetadataBase> GetMetadata() = 0;
 
   /**
-   * Encodes raw segments. Result data is returned in aData.
+   * Encodes raw segments. Result data is returned in aData, and called on the
+   * worker thread.
    */
   virtual nsresult GetEncodedTrack(EncodedFrameContainer& aData) = 0;
-};
 
-class AudioTrackEncoder : public TrackEncoder
-{
-public:
-  AudioTrackEncoder()
-    : TrackEncoder()
-    , mChannels(0)
-    , mSamplingRate(0)
-    , mInitialized(false)
-    , mDoneEncoding(false)
-    , mReentrantMonitor("media.AudioEncoder")
-    , mRawSegment(new AudioSegment())
-    , mEndOfStream(false)
-    , mCanceled(false)
-    , mSilentDuration(0)
-  {}
-
-  void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
-                                TrackRate aTrackRate,
-                                TrackTicks aTrackOffset,
-                                uint32_t aTrackEvents,
-                                const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
-
-  void NotifyRemoved(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
-
-  bool IsEncodingComplete()
-  {
-    return mDoneEncoding;
-  }
+  /**
+   * True if the track encoder has encoded all source segments coming from
+   * MediaStreamGraph. Call on the worker thread.
+   */
+  bool IsEncodingComplete() { return mEncodingComplete; }
 
   /**
    * Notifies from MediaEncoder to cancel the encoding, and wakes up
    * mReentrantMonitor if encoder is waiting on it.
    */
   void NotifyCancel()
   {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mCanceled = true;
     mReentrantMonitor.NotifyAll();
   }
 
 protected:
   /**
+   * Notifies track encoder that we have reached the end of source stream, and
+   * wakes up mReentrantMonitor if encoder is waiting for any source data.
+   */
+  virtual void NotifyEndOfStream() = 0;
+
+  /**
+   * A ReentrantMonitor to protect the pushing and pulling of mRawSegment which
+   * is declared in its subclasses, and the following flags: mInitialized,
+   * EndOfStream and mCanceled. The control of protection is managed by its
+   * subclasses.
+   */
+  ReentrantMonitor mReentrantMonitor;
+
+  /**
+   * True if the track encoder has encoded all source data.
+   */
+  bool mEncodingComplete;
+
+  /**
+   * True if flag of EOS or any form of indicating EOS has set in the codec-
+   * encoder.
+   */
+  bool mEosSetInEncoder;
+
+  /**
+   * True if the track encoder has initialized successfully, protected by
+   * mReentrantMonitor.
+   */
+  bool mInitialized;
+
+  /**
+   * True if the TrackEncoder has received an event of TRACK_EVENT_ENDED from
+   * MediaStreamGraph, or the MediaEncoder is removed from its source stream,
+   * protected by mReentrantMonitor.
+   */
+  bool mEndOfStream;
+
+  /**
+   * True if a cancellation of encoding is sent from MediaEncoder, protected by
+   * mReentrantMonitor.
+   */
+  bool mCanceled;
+};
+
+class AudioTrackEncoder : public TrackEncoder
+{
+public:
+  AudioTrackEncoder()
+    : TrackEncoder()
+    , mChannels(0)
+    , mSamplingRate(0)
+  {}
+
+  void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
+                                TrackRate aTrackRate,
+                                TrackTicks aTrackOffset,
+                                uint32_t aTrackEvents,
+                                const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
+
+protected:
+  /**
    * Number of samples per channel in a pcm buffer. This is also the value of
    * frame size required by audio encoder, and mReentrantMonitor will be
    * notified when at least this much data has been added to mRawSegment.
    */
-  virtual int GetPacketDuration() = 0;
+  virtual int GetPacketDuration() { return 0; }
 
   /**
    * Initializes the audio encoder. The call of this method is delayed until we
    * have received the first valid track from MediaStreamGraph, and the
    * mReentrantMonitor will be notified if other methods is waiting for encoder
    * to be completely initialized. This method is called on the MediaStreamGraph
    * thread.
    */
   virtual nsresult Init(int aChannels, int aSamplingRate) = 0;
 
   /**
    * Appends and consumes track data from aSegment, this method is called on
    * the MediaStreamGraph thread. mReentrantMonitor will be notified when at
    * least GetPacketDuration() data has been added to mRawSegment, wake up other
    * method which is waiting for more data from mRawSegment.
    */
-  nsresult AppendAudioSegment(MediaSegment* aSegment);
+  nsresult AppendAudioSegment(const AudioSegment& aSegment);
 
   /**
    * Notifies the audio encoder that we have reached the end of source stream,
    * and wakes up mReentrantMonitor if encoder is waiting for more track data.
    */
-  void NotifyEndOfStream();
+  virtual void NotifyEndOfStream() MOZ_OVERRIDE;
 
   /**
    * Interleaves the track data and stores the result into aOutput. Might need
    * to up-mix or down-mix the channel data if the channels number of this chunk
    * is different from mChannels. The channel data from aChunk might be modified
    * by up-mixing.
    */
   void InterleaveTrackData(AudioChunk& aChunk, int32_t aDuration,
@@ -142,49 +190,105 @@ protected:
 
   /**
    * The number of channels are used for processing PCM data in the audio encoder.
    * This value comes from the first valid audio chunk. If encoder can't support
    * the channels in the chunk, downmix PCM stream can be performed.
    * This value also be used to initialize the audio encoder.
    */
   int mChannels;
-  int mSamplingRate;
-  bool mInitialized;
-  bool mDoneEncoding;
 
   /**
-   * A ReentrantMonitor to protect the pushing and pulling of mRawSegment.
+   * The sampling rate of source audio data.
    */
-  ReentrantMonitor mReentrantMonitor;
+  int mSamplingRate;
 
   /**
    * A segment queue of audio track data, protected by mReentrantMonitor.
    */
-  nsAutoPtr<AudioSegment> mRawSegment;
-
-  /**
-   * True if we have received an event of TRACK_EVENT_ENDED from MediaStreamGraph,
-   * or the MediaEncoder is removed from its source stream, protected by
-   * mReentrantMonitor.
-   */
-  bool mEndOfStream;
-
-  /**
-   * True if a cancellation of encoding is sent from MediaEncoder, protected by
-   * mReentrantMonitor.
-   */
-  bool mCanceled;
-
-  /**
-   * The total duration of null chunks we have received from MediaStreamGraph
-   * before initializing the audio track encoder.
-   */
-  TrackTicks mSilentDuration;
+  AudioSegment mRawSegment;
 };
 
 class VideoTrackEncoder : public TrackEncoder
 {
+public:
+  VideoTrackEncoder()
+    : TrackEncoder()
+    , mFrameWidth(0)
+    , mFrameHeight(0)
+    , mTrackRate(0)
+    , mTotalFrameDuration(0)
+  {}
 
+  /**
+   * Notified by the same callbcak of MediaEncoder when it has received a track
+   * change from MediaStreamGraph. Called on the MediaStreamGraph thread.
+   */
+  void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
+                                TrackRate aTrackRate,
+                                TrackTicks aTrackOffset,
+                                uint32_t aTrackEvents,
+                                const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
+
+protected:
+  /**
+   * Initialized the video encoder. In order to collect the value of width and
+   * height of source frames, this initialization is delayed until we have
+   * received the first valid video frame from MediaStreamGraph;
+   * mReentrantMonitor will be notified after it has successfully initialized,
+   * and this method is called on the MediaStramGraph thread.
+   */
+  virtual nsresult Init(int aWidth, int aHeight, TrackRate aTrackRate) = 0;
+
+  /**
+   * Appends source video frames to mRawSegment. We only append the source chunk
+   * if it is unique to mLastChunk. Called on the MediaStreamGraph thread.
+   */
+  nsresult AppendVideoSegment(const VideoSegment& aSegment);
+
+  /**
+   * Tells the video track encoder that we've reached the end of source stream,
+   * and wakes up mReentrantMonitor if encoder is waiting for more track data.
+   * Called on the MediaStreamGraph thread.
+   */
+  virtual void NotifyEndOfStream() MOZ_OVERRIDE;
+
+  /**
+   * Create a buffer of black image in format of YUV:420. Called on the worker
+   * thread.
+   */
+  void CreateMutedFrame(nsTArray<uint8_t>* aOutputBuffer);
+
+  /**
+   * The width of source video frame, ceiled if the source width is odd.
+   */
+  int mFrameWidth;
+
+  /**
+   * The height of source video frame, ceiled if the source height is odd.
+   */
+  int mFrameHeight;
+
+  /**
+   * The track rate of source video.
+   */
+  TrackRate mTrackRate;
+
+  /**
+   * The total duration of frames in encoded video in TrackTicks, kept track of
+   * in subclasses.
+   */
+  TrackTicks mTotalFrameDuration;
+
+  /**
+   * The last unique frame we've sent to track encoder, kept track of in
+   * subclasses.
+   */
+  VideoFrame mLastFrame;
+
+  /**
+   * A segment queue of audio track data, protected by mReentrantMonitor.
+   */
+  VideoSegment mRawSegment;
 };
 
 }
 #endif
--- a/content/media/fmp4/demuxer/audio_decoder_config.cc
+++ b/content/media/fmp4/demuxer/audio_decoder_config.cc
@@ -1,15 +1,16 @@
 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "mp4_demuxer/audio_decoder_config.h"
 
 #include <sstream>
+#include <string.h>
 
 namespace mp4_demuxer {
 
 static int SampleFormatToBitsPerChannel(SampleFormat sample_format) {
   switch (sample_format) {
     case kUnknownSampleFormat:
       return 0;
     case kSampleFormatU8:
--- a/content/media/fmp4/demuxer/basictypes.h
+++ b/content/media/fmp4/demuxer/basictypes.h
@@ -1,16 +1,17 @@
 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef MEDIA_MP4_BASIC_TYPES_H_
 #define MEDIA_MP4_BASIC_TYPES_H_
 
 #include <iostream>
+#include <limits>
 #include <stdint.h>
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* GetDemuxerLog();
 #endif
 
 namespace mp4_demuxer {
--- a/content/media/fmp4/demuxer/video_decoder_config.cc
+++ b/content/media/fmp4/demuxer/video_decoder_config.cc
@@ -1,15 +1,16 @@
 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "mp4_demuxer/video_decoder_config.h"
 
 #include <sstream>
+#include <string.h>
 
 namespace mp4_demuxer {
 
 VideoDecoderConfig::VideoDecoderConfig()
     : codec_(kUnknownVideoCodec),
       profile_(VIDEO_CODEC_PROFILE_UNKNOWN),
       format_(VideoFrameFormat::INVALID),
       is_encrypted_(false) {
--- a/content/media/fmp4/wmf/MFTDecoder.h
+++ b/content/media/fmp4/wmf/MFTDecoder.h
@@ -2,17 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(MFTDecoder_h_)
 #define MFTDecoder_h_
 
-#include "wmf.h"
+#include "WMF.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsIThread.h"
 
 namespace mozilla {
 
 class MFTDecoder {
 public:
--- a/content/media/fmp4/wmf/WMFAudioDecoder.h
+++ b/content/media/fmp4/wmf/WMFAudioDecoder.h
@@ -2,17 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(WMFAudioDecoder_h_)
 #define WMFAudioDecoder_h_
 
-#include "wmf.h"
+#include "WMF.h"
 #include "MP4Reader.h"
 #include "MFTDecoder.h"
 
 namespace mozilla {
 
 class WMFAudioDecoder : public MediaDataDecoder {
 public:
   WMFAudioDecoder();
--- a/content/media/fmp4/wmf/WMFDecoderModule.cpp
+++ b/content/media/fmp4/wmf/WMFDecoderModule.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "wmf.h"
+#include "WMF.h"
 #include "WMFDecoderModule.h"
 #include "WMFDecoder.h"
 #include "WMFVideoDecoder.h"
 #include "WMFAudioDecoder.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/DebugOnly.h"
 
 namespace mozilla {
--- a/content/media/fmp4/wmf/WMFVideoDecoder.h
+++ b/content/media/fmp4/wmf/WMFVideoDecoder.h
@@ -2,17 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(WMFVideoDecoder_h_)
 #define WMFVideoDecoder_h_
 
-#include "wmf.h"
+#include "WMF.h"
 #include "MP4Reader.h"
 #include "MFTDecoder.h"
 #include "nsRect.h"
 
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 
@@ -70,9 +70,9 @@ private:
   const bool mDXVAEnabled;
   bool mUseHwAccel;
 };
 
 
 
 } // namespace mozilla
 
-#endif
\ No newline at end of file
+#endif
--- a/content/media/wmf/WMF.h
+++ b/content/media/wmf/WMF.h
@@ -32,17 +32,17 @@ which makes Windows Media Foundation una
 #include <initguid.h>
 #include <d3d9.h>
 #include <dxva2api.h>
 #include <wmcodecdsp.h>
 #include <codecapi.h>
 
 // Some SDK versions don't define the AAC decoder CLSID.
 #ifndef CLSID_CMSAACDecMFT
-extern const CLSID CLSID_CMSAACDecMFT;
+extern "C" const CLSID CLSID_CMSAACDecMFT;
 #define WMF_MUST_DEFINE_AAC_MFT_CLSID
 #endif
 
 namespace mozilla {
 namespace wmf {
 
 // Loads/Unloads all the DLLs in which the WMF functions are located.
 // The DLLs must be loaded before any of the WMF functions below will work.
--- a/content/svg/content/src/SVGStyleElement.cpp
+++ b/content/svg/content/src/SVGStyleElement.cpp
@@ -78,33 +78,33 @@ SVGStyleElement::BindToTree(nsIDocument*
 
   return rv;
 }
 
 void
 SVGStyleElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
-
+  ShadowRoot* oldShadow = GetShadowRoot();
   SVGStyleElementBase::UnbindFromTree(aDeep, aNullParent);
-  UpdateStyleSheetInternal(oldDoc);
+  UpdateStyleSheetInternal(oldDoc, oldShadow);
 }
 
 nsresult
 SVGStyleElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                          nsIAtom* aPrefix, const nsAString& aValue,
                          bool aNotify)
 {
   nsresult rv = SVGStyleElementBase::SetAttr(aNameSpaceID, aName, aPrefix,
                                              aValue, aNotify);
   if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::title ||
         aName == nsGkAtoms::media ||
         aName == nsGkAtoms::type) {
-      UpdateStyleSheetInternal(nullptr, true);
+      UpdateStyleSheetInternal(nullptr, nullptr, true);
     } else if (aName == nsGkAtoms::scoped) {
       UpdateStyleSheetScopedness(true);
     }
   }
 
   return rv;
 }
 
@@ -113,17 +113,17 @@ SVGStyleElement::UnsetAttr(int32_t aName
                            bool aNotify)
 {
   nsresult rv = SVGStyleElementBase::UnsetAttr(aNameSpaceID, aAttribute,
                                                aNotify);
   if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::title ||
         aAttribute == nsGkAtoms::media ||
         aAttribute == nsGkAtoms::type) {
-      UpdateStyleSheetInternal(nullptr, true);
+      UpdateStyleSheetInternal(nullptr, nullptr, true);
     } else if (aAttribute == nsGkAtoms::scoped) {
       UpdateStyleSheetScopedness(false);
     }
   }
 
   return rv;
 }
 
@@ -181,17 +181,17 @@ SVGStyleElement::ContentRemoved(nsIDocum
 {
   ContentChanged(aChild);
 }
 
 void
 SVGStyleElement::ContentChanged(nsIContent* aContent)
 {
   if (nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
-    UpdateStyleSheetInternal(nullptr);
+    UpdateStyleSheetInternal(nullptr, nullptr);
   }
 }
 
 //----------------------------------------------------------------------
 
 void
 SVGStyleElement::GetXmlspace(nsAString & aXmlspace)
 {
--- a/content/svg/content/test/test_animLengthReadonly.xhtml
+++ b/content/svg/content/test/test_animLengthReadonly.xhtml
@@ -208,21 +208,14 @@ function checkDiffValue(animLength)
 
   // convertToSpecifiedUnits
   animLength.baseVal.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX);
   is(animLength.animVal.value, origValue,
     "animated animVal.value changed after calling "
     + "baseValue.convertToSpecifiedUnits");
 }
 
-var animate = document.getElementById('animate');
-if (animate && animate.targetElement) {
-  window.addEventListener("load", main, false);
-} else {
-  // SMIL not enabled: skip tests but don't report 'todo' either
-  ok(true);
-  SimpleTest.finish();
-}
+window.addEventListener("load", main, false);
 ]]>
 </script>
 </pre>
 </body>
 </html>
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -53,16 +53,17 @@
 #include "nsNodeUtils.h"
 #include "nsJSUtils.h"
 
 // Nasty hack.  Maybe we could move some of the classinfo utility methods
 // (e.g. WrapNative) over to nsContentUtils?
 #include "nsDOMClassInfo.h"
 
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // Helper classes
 
 /***********************************************************************/
 //
@@ -158,16 +159,26 @@ nsXBLBinding::nsXBLBinding(nsXBLPrototyp
   , mUsingXBLScope(false)
   , mPrototypeBinding(aBinding)
 {
   NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
   // Grab a ref to the document info so the prototype binding won't die
   NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
 }
 
+// Constructor used by web components.
+nsXBLBinding::nsXBLBinding(ShadowRoot* aShadowRoot, nsXBLPrototypeBinding* aBinding)
+  : mMarkedForDeath(false),
+    mPrototypeBinding(aBinding),
+    mContent(aShadowRoot)
+{
+  NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
+  // Grab a ref to the document info so the prototype binding won't die
+  NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
+}
 
 nsXBLBinding::~nsXBLBinding(void)
 {
   if (mContent) {
     nsXBLBinding::UninstallAnonymousContent(mContent->OwnerDoc(), mContent);
   }
   nsXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo();
   NS_RELEASE(info);
@@ -268,16 +279,23 @@ nsXBLBinding::InstallAnonymousContent(ns
 #endif
   }
 }
 
 void
 nsXBLBinding::UninstallAnonymousContent(nsIDocument* aDocument,
                                         nsIContent* aAnonParent)
 {
+  if (aAnonParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    // It is unnecessary to uninstall anonymous content in a shadow tree
+    // because the ShadowRoot itself is a DocumentFragment and does not
+    // need any additional cleanup.
+    return;
+  }
+
   nsAutoScriptBlocker scriptBlocker;
   // Hold a strong ref while doing this, just in case.
   nsCOMPtr<nsIContent> anonParent = aAnonParent;
 #ifdef MOZ_XUL
   nsCOMPtr<nsIXULDocument> xuldoc =
     do_QueryInterface(aDocument);
 #endif
   for (nsIContent* child = aAnonParent->GetFirstChild();
--- a/content/xbl/src/nsXBLBinding.h
+++ b/content/xbl/src/nsXBLBinding.h
@@ -21,30 +21,32 @@ class nsXBLPrototypeBinding;
 class nsIContent;
 class nsIAtom;
 class nsIDocument;
 class nsIScriptContext;
 
 namespace mozilla {
 namespace dom {
 
+class ShadowRoot;
 class XBLChildrenElement;
 
-}
-}
+} // namespace dom
+} // namespace mozilla
 
 class nsAnonymousContentList;
 
 // *********************************************************************/
 // The XBLBinding class
 
 class nsXBLBinding
 {
 public:
   nsXBLBinding(nsXBLPrototypeBinding* aProtoBinding);
+  nsXBLBinding(mozilla::dom::ShadowRoot* aShadowRoot, nsXBLPrototypeBinding* aProtoBinding);
   ~nsXBLBinding();
 
   /**
    * XBLBindings are refcounted.  They are held onto in 3 ways:
    * 1. The binding manager's binding table holds onto all bindings that are
    *    currently attached to a content node.
    * 2. Bindings hold onto their base binding.  This is important since
    *    the base binding itself may not be attached to anything.
--- a/content/xbl/src/nsXBLPrototypeBinding.cpp
+++ b/content/xbl/src/nsXBLPrototypeBinding.cpp
@@ -592,16 +592,26 @@ nsXBLPrototypeBinding::GetRuleProcessor(
   if (mResources) {
     return mResources->mRuleProcessor;
   }
   
   return nullptr;
 }
 
 nsXBLPrototypeResources::sheet_array_type*
+nsXBLPrototypeBinding::GetOrCreateStyleSheets()
+{
+  if (!mResources) {
+    mResources = new nsXBLPrototypeResources(this);
+  }
+
+  return &mResources->mStyleSheetList;
+}
+
+nsXBLPrototypeResources::sheet_array_type*
 nsXBLPrototypeBinding::GetStyleSheets()
 {
   if (mResources) {
     return &mResources->mStyleSheetList;
   }
 
   return nullptr;
 }
--- a/content/xbl/src/nsXBLPrototypeBinding.h
+++ b/content/xbl/src/nsXBLPrototypeBinding.h
@@ -52,16 +52,17 @@ public:
 
   nsresult BindingAttached(nsIContent* aBoundElement);
   nsresult BindingDetached(nsIContent* aBoundElement);
 
   bool LoadResources();
   nsresult AddResource(nsIAtom* aResourceType, const nsAString& aSrc);
 
   bool InheritsStyle() const { return mInheritStyle; }
+  void SetInheritsStyle(bool aInheritStyle) { mInheritStyle = aInheritStyle; }
 
   nsXBLPrototypeHandler* GetPrototypeHandlers() { return mPrototypeHandler; }
   void SetPrototypeHandlers(nsXBLPrototypeHandler* aHandler) { mPrototypeHandler = aHandler; }
 
   nsXBLProtoImplAnonymousMethod* GetConstructor();
   nsresult SetConstructor(nsXBLProtoImplAnonymousMethod* aConstructor);
   nsXBLProtoImplAnonymousMethod* GetDestructor();
   nsresult SetDestructor(nsXBLProtoImplAnonymousMethod* aDestructor);
@@ -111,16 +112,17 @@ public:
   nsXBLPrototypeBinding* GetBasePrototype() { return mBaseBinding; }
 
   nsXBLDocumentInfo* XBLDocumentInfo() const { return mXBLDocInfoWeak; }
   bool IsChrome() { return mXBLDocInfoWeak->IsChrome(); }
   
   void SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent);
 
   nsIStyleRuleProcessor* GetRuleProcessor();
+  nsXBLPrototypeResources::sheet_array_type* GetOrCreateStyleSheets();
   nsXBLPrototypeResources::sheet_array_type* GetStyleSheets();
   
   bool HasStyleSheets() {
     return mResources && mResources->mStyleSheetList.Length() > 0;
   }
 
   nsresult FlushSkinSheets();
 
--- a/content/xml/content/src/XMLStylesheetProcessingInstruction.cpp
+++ b/content/xml/content/src/XMLStylesheetProcessingInstruction.cpp
@@ -67,28 +67,28 @@ XMLStylesheetProcessingInstruction::Bind
 }
 
 void
 XMLStylesheetProcessingInstruction::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
 
   ProcessingInstruction::UnbindFromTree(aDeep, aNullParent);
-  UpdateStyleSheetInternal(oldDoc);
+  UpdateStyleSheetInternal(oldDoc, nullptr);
 }
 
 // nsIDOMNode
 
 void
 XMLStylesheetProcessingInstruction::SetNodeValueInternal(const nsAString& aNodeValue,
                                                          ErrorResult& aError)
 {
   nsGenericDOMDataNode::SetNodeValueInternal(aNodeValue, aError);
   if (!aError.Failed()) {
-    UpdateStyleSheetInternal(nullptr, true);
+    UpdateStyleSheetInternal(nullptr, nullptr, true);
   }
 }
 
 // nsStyleLinkElement
 
 NS_IMETHODIMP
 XMLStylesheetProcessingInstruction::GetCharset(nsAString& aCharset)
 {
--- a/content/xml/content/src/XMLStylesheetProcessingInstruction.h
+++ b/content/xml/content/src/XMLStylesheetProcessingInstruction.h
@@ -64,17 +64,17 @@ public:
   NS_IMETHOD GetCharset(nsAString& aCharset) MOZ_OVERRIDE;
 
   virtual void SetData(const nsAString& aData, mozilla::ErrorResult& rv) MOZ_OVERRIDE
   {
     nsGenericDOMDataNode::SetData(aData, rv);
     if (rv.Failed()) {
       return;
     }
-    UpdateStyleSheetInternal(nullptr, true);
+    UpdateStyleSheetInternal(nullptr, nullptr, true);
   }
   using ProcessingInstruction::SetData; // Prevent hiding overloaded virtual function.
 
 protected:
   nsCOMPtr<nsIURI> mOverriddenBaseURI;
 
   already_AddRefed<nsIURI> GetStyleSheetURL(bool* aIsInline) MOZ_OVERRIDE;
   void GetStyleSheetInfo(nsAString& aTitle,
--- a/dom/apps/src/OperatorApps.jsm
+++ b/dom/apps/src/OperatorApps.jsm
@@ -23,25 +23,29 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "@mozilla.org/ril/content-helper;1",
                                    "nsIIccProvider");
 #endif
 
 function debug(aMsg) {
   //dump("-*-*- OperatorApps.jsm : " + aMsg + "\n");
 }
 
+// Single Variant source dir will be set in PREF_SINGLE_VARIANT_DIR
+// preference.
+// if PREF_SINGLE_VARIANT_DIR does not exist or has not value, it will use (as
+// single variant source) the value of
+// DIRECTORY_NAME + "/" + SINGLE_VARIANT_SOURCE_DIR value instead.
+// SINGLE_VARIANT_CONF_FILE will be stored on Single Variant Source.
+// Apps will be stored on an app per directory basis, hanging from
+// SINGLE_VARIANT_SOURCE_DIR
 const DIRECTORY_NAME = "webappsDir";
-
-// The files will be stored on DIRECTORY_NAME + "/" + SINGLE_VARIANT_SOURCE_DIR
-// SINGLE_VARIANT_CONF_FILE will be stored on SINGLE_VARIANT_SOURCE_DIR
-// Apps will be stored on a app per directory basis, hanging from
-// SINGLE_VARIANT_SOURCE_DIR
 const SINGLE_VARIANT_SOURCE_DIR = "svoperapps";
 const SINGLE_VARIANT_CONF_FILE  = "singlevariantconf.json";
 const PREF_FIRST_RUN_WITH_SIM   = "dom.webapps.firstRunWithSIM";
+const PREF_SINGLE_VARIANT_DIR   = "dom.mozApps.single_variant_sourcedir";
 const METADATA                  = "metadata.json";
 const UPDATEMANIFEST            = "update.webapp";
 const MANIFEST                  = "manifest.webapp";
 const APPLICATION_ZIP           = "application.zip";
 
 function isFirstRunWithSIM() {
   try {
     if (Services.prefs.prefHasUserValue(PREF_FIRST_RUN_WITH_SIM)) {
@@ -49,16 +53,19 @@ function isFirstRunWithSIM() {
     }
   } catch(e) {
     debug ("Error getting pref. " + e);
   }
   return true;
 }
 
 #ifdef MOZ_B2G_RIL
+let File = OS.File;
+let Path = OS.Path;
+
 let iccListener = {
   notifyStkCommand: function() {},
 
   notifyStkSessionEnd: function() {},
 
   notifyCardStateChanged: function() {},
 
   notifyIccInfoChanged: function() {
@@ -83,58 +90,142 @@ this.OperatorAppsRegistry = {
 
   _baseDirectory: null,
 
   init: function() {
     debug("init");
 #ifdef MOZ_B2G_RIL
     if (isFirstRunWithSIM()) {
       debug("First Run with SIM");
-      // TODO: Bug 927709 - OperatorApps for multi-sim
-      // In Multi-sim, there is more than one client in iccProvider. Each
-      // client represents a icc service. To maintain the backward compatibility
-      // with single sim, we always use client 0 for now. Adding support for
-      // multiple sim will be addressed in bug 927709, if needed.
-      let clientId = 0;
-      let iccInfo = iccProvider.getIccInfo(clientId);
-      let mcc = 0;
-      let mnc = 0;
-      if (iccInfo && iccInfo.mcc) {
-        mcc = iccInfo.mcc;
-      }
-      if (iccInfo && iccInfo.mnc) {
-        mnc = iccInfo.mnc;
-      }
-      if (mcc && mnc) {
-        this._installOperatorApps(mcc, mnc);
-      } else {
-        iccProvider.registerIccMsg(clientId, iccListener);
-      }
+      Task.spawn(function() {
+        try {
+          yield this._initializeSourceDir();
+          // TODO: Bug 927709 - OperatorApps for multi-sim
+          // In Multi-sim, there is more than one client in iccProvider. Each
+          // client represents a icc service. To maintain the backward
+          // compatibility with single sim, we always use client 0 for now.
+          // Adding support for multiple sim will be addressed in bug 927709, if
+          // needed.
+          let clientId = 0;
+          let iccInfo = iccProvider.getIccInfo(clientId);
+          let mcc = 0;
+          let mnc = 0;
+          if (iccInfo && iccInfo.mcc) {
+            mcc = iccInfo.mcc;
+          }
+          if (iccInfo && iccInfo.mnc) {
+            mnc = iccInfo.mnc;
+          }
+          if (mcc && mnc) {
+            this._installOperatorApps(mcc, mnc);
+          } else {
+            iccProvider.registerIccMsg(clientId, iccListener);
+          }
+        } catch (e) {
+          debug("Error Initializing OperatorApps. " + e);
+        }
+      }.bind(this));
     } else {
       debug("No First Run with SIM");
     }
 #endif
   },
 
+  _copyDirectory: function(aOrg, aDst) {
+    debug("copying " + aOrg + " to " + aDst);
+    return aDst && Task.spawn(function() {
+      try {
+        let orgInfo = yield File.stat(aOrg);
+        if (!orgInfo.isDir) {
+          return;
+        }
+
+        let dirDstExists = yield File.exists(aDst);
+        if (!dirDstExists) {
+          yield File.makeDir(aDst);
+        }
+        let iterator = new File.DirectoryIterator(aOrg);
+        if (!iterator) {
+          debug("No iterator over: " + aOrg);
+          return;
+        }
+        try {
+          while (true) {
+            let entry;
+            try {
+              entry = yield iterator.next();
+            } catch (ex if ex == StopIteration) {
+              break;
+            }
+
+            if (!entry.isDir) {
+              yield File.copy(entry.path, Path.join(aDst, entry.name));
+            } else {
+              yield this._copyDirectory(entry.path,
+                                        Path.join(aDst, entry.name));
+            }
+          }
+        } finally {
+          iterator.close();
+        }
+      } catch (e) {
+        debug("Error copying " + aOrg + " to " + aDst + ". " + e);
+      }
+    }.bind(this));
+  },
+
+  _initializeSourceDir: function() {
+    return Task.spawn(function() {
+      let svFinalDirName;
+      try {
+        svFinalDirName = Services.prefs.getCharPref(PREF_SINGLE_VARIANT_DIR);
+      } catch(e) {
+        debug ("Error getting pref. " + e);
+        this.appsDir = FileUtils.getFile(DIRECTORY_NAME,
+                                         [SINGLE_VARIANT_SOURCE_DIR]).path;
+        return;
+      }
+      // If SINGLE_VARIANT_CONF_FILE is in PREF_SINGLE_VARIANT_DIR return
+      // PREF_SINGLE_VARIANT_DIR as sourceDir, else go to
+      // DIRECTORY_NAME + SINGLE_VARIANT_SOURCE_DIR and move all apps (and
+      // configuration file) to PREF_SINGLE_VARIANT_DIR and return
+      // PREF_SINGLE_VARIANT_DIR as sourceDir.
+      let existsDir = yield File.exists(svFinalDirName);
+      if (!existsDir) {
+        yield File.makeDir(svFinalDirName, {ignoreExisting: true});
+      }
+
+      let existsSvIndex = yield File.exists(Path.join(svFinalDirName,
+                                            SINGLE_VARIANT_CONF_FILE));
+      if (!existsSvIndex) {
+        let svSourceDirName = FileUtils.getFile(DIRECTORY_NAME,
+                                              [SINGLE_VARIANT_SOURCE_DIR]).path;
+        yield this._copyDirectory(svSourceDirName, svFinalDirName);
+        debug("removing directory:" + svSourceDirName);
+        File.removeDir(svSourceDirName, {
+          ignoreAbsent: true,
+          ignorePermissions: true
+        });
+      }
+      this.appsDir = svFinalDirName;
+    }.bind(this));
+  },
+
   set appsDir(aDir) {
     debug("appsDir SET: " + aDir);
     if (aDir) {
       this._baseDirectory = Cc["@mozilla.org/file/local;1"]
           .createInstance(Ci.nsILocalFile);
       this._baseDirectory.initWithPath(aDir);
     } else {
       this._baseDirectory = null;
     }
   },
 
   get appsDir() {
-    if (!this._baseDirectory) {
-      this._baseDirectory = FileUtils.getFile(DIRECTORY_NAME,
-                                              [SINGLE_VARIANT_SOURCE_DIR]);
-    }
     return this._baseDirectory;
   },
 
   eraseVariantAppsNotInList: function(aIdsApp) {
     if (!aIdsApp || !Array.isArray(aIdsApp)) {
       aIdsApp = [ ];
     }
 
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -4,16 +4,38 @@
 
 "use strict";
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
+// Possible errors thrown by the signature verifier.
+const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
+const SEC_ERROR_EXPIRED_CERTIFICATE = (SEC_ERROR_BASE + 11);
+
+// We need this to decide if we should accept or not files signed with expired
+// certificates.
+function buildIDToTime() {
+  let platformBuildID =
+    Cc["@mozilla.org/xre/app-info;1"]
+      .getService(Ci.nsIXULAppInfo).platformBuildID;
+  let platformBuildIDDate = new Date();
+  platformBuildIDDate.setUTCFullYear(platformBuildID.substr(0,4),
+                                      platformBuildID.substr(4,2) - 1,
+                                      platformBuildID.substr(6,2));
+  platformBuildIDDate.setUTCHours(platformBuildID.substr(8,2),
+                                  platformBuildID.substr(10,2),
+                                  platformBuildID.substr(12,2));
+  return platformBuildIDDate.getTime();
+}
+
+const PLATFORM_BUILD_ID_TIME = buildIDToTime();
+
 this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import('resource://gre/modules/ActivitiesService.jsm');
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
@@ -33,16 +55,20 @@ XPCOMUtils.defineLazyGetter(this, "libcu
 #endif
 
 function debug(aMsg) {
 #ifdef MOZ_DEBUG
   dump("-*- Webapps.jsm : " + aMsg + "\n");
 #endif
 }
 
+function getNSPRErrorCode(err) {
+  return -1 * ((err) & 0xffff);
+}
+
 function supportUseCurrentProfile() {
   return Services.prefs.getBoolPref("dom.webapps.useCurrentProfile");
 }
 
 function supportSystemMessages() {
   return Services.prefs.getBoolPref("dom.sysmsg.enabled");
 }
 
@@ -2480,16 +2506,20 @@ onInstallSuccessAck: function onInstallS
 
     return Task.spawn((function*() {
       yield this._ensureSufficientStorage(aNewApp);
 
       let fullPackagePath = aManifest.fullPackagePath();
 
       // Check if it's a local file install (we've downloaded/sideloaded the
       // package already or it did exist on the build).
+      // Note that this variable also controls whether files signed with expired
+      // certificates are accepted or not. If isLocalFileInstall is true and the
+      // device date is earlier than the build generation date, then the signature
+      // will be accepted even if the certificate is expired.
       let isLocalFileInstall =
         Services.io.extractScheme(fullPackagePath) === 'file';
 
       debug("About to download " + fullPackagePath);
 
       let requestChannel = this._getRequestChannel(fullPackagePath,
                                                    isLocalFileInstall,
                                                    oldApp,
@@ -2832,17 +2862,18 @@ onInstallSuccessAck: function onInstallS
   },
 
   _openAndReadPackage: function(aZipFile, aOldApp, aNewApp, aIsLocalFileInstall,
                                 aIsUpdate, aManifest, aRequestChannel, aHash) {
     return Task.spawn((function*() {
       let zipReader, isSigned, newManifest;
 
       try {
-        [zipReader, isSigned] = yield this._openPackage(aZipFile, aOldApp);
+        [zipReader, isSigned] = yield this._openPackage(aZipFile, aOldApp,
+                                                        aIsLocalFileInstall);
         newManifest = yield this._readPackage(aOldApp, aNewApp,
                 aIsLocalFileInstall, aIsUpdate, aManifest, aRequestChannel,
                 aHash, zipReader, isSigned);
       } catch (e) {
         debug("package open/read error: " + e);
         // Something bad happened when opening/reading the package.
         // Unrecoverable error, don't bug the user.
         // Apps with installState 'pending' does not produce any
@@ -2863,41 +2894,55 @@ onInstallSuccessAck: function onInstallS
         }
       }
 
       return newManifest;
 
     }).bind(this));
   },
 
-  _openPackage: function(aZipFile, aApp) {
+  _openPackage: function(aZipFile, aApp, aIsLocalFileInstall) {
     return Task.spawn((function*() {
       let certDb;
       try {
         certDb = Cc["@mozilla.org/security/x509certdb;1"]
                    .getService(Ci.nsIX509CertDB);
       } catch (e) {
         debug("nsIX509CertDB error: " + e);
         // unrecoverable error, don't bug the user
         aApp.downloadAvailable = false;
         throw "CERTDB_ERROR";
       }
 
       let [result, zipReader] = yield this._openSignedPackage(aZipFile, certDb);
 
+      // We cannot really know if the system date is correct or
+      // not. What we can know is if it's after the build date or not,
+      // and assume the build date is correct (which we cannot
+      // really know either).
+      let isLaterThanBuildTime = Date.now() > PLATFORM_BUILD_ID_TIME;
+
       let isSigned;
 
       if (Components.isSuccessCode(result)) {
         isSigned = true;
       } else if (result == Cr.NS_ERROR_FILE_CORRUPTED) {
         throw "APP_PACKAGE_CORRUPTED";
-      } else if (result != Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED) {
+      } else if ((!aIsLocalFileInstall || isLaterThanBuildTime) &&
+                 (result != Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED)) {
         throw "INVALID_SIGNATURE";
       } else {
-        isSigned = false;
+        // If it's a localFileInstall and the validation failed
+        // because of a expired certificate, just assume it was valid
+        // and that the error occurred because the system time has not
+        // been set yet.
+        isSigned = (aIsLocalFileInstall &&
+                    (getNSPRErrorCode(result) ==
+                     SEC_ERROR_EXPIRED_CERTIFICATE));
+
         zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
                       .createInstance(Ci.nsIZipReader);
         zipReader.open(aZipFile);
       }
 
       return [zipReader, isSigned];
 
     }).bind(this));
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -4273,19 +4273,20 @@ nsStringArraySH::GetProperty(nsIXPConnec
 
 
 // StyleSheetList helper
 
 nsISupports*
 nsStyleSheetListSH::GetItemAt(nsISupports *aNative, uint32_t aIndex,
                               nsWrapperCache **aCache, nsresult *rv)
 {
-  nsDOMStyleSheetList* list = nsDOMStyleSheetList::FromSupports(aNative);
-
-  return list->GetItemAt(aIndex);
+  nsIDOMStyleSheetList* list = static_cast<nsIDOMStyleSheetList*>(aNative);
+  nsCOMPtr<nsIDOMStyleSheet> sheet;
+  list->Item(aIndex, getter_AddRefs(sheet));
+  return sheet;
 }
 
 
 // CSSRuleList scriptable helper
 
 nsISupports*
 nsCSSRuleListSH::GetItemAt(nsISupports *aNative, uint32_t aIndex,
                            nsWrapperCache **aCache, nsresult *aResult)
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -959,16 +959,22 @@ DOMInterfaces = {
 'Screen': {
     'nativeType': 'nsScreen',
 },
 
 'ScrollAreaEvent': {
     'nativeType': 'nsDOMScrollAreaEvent',
 },
 
+'ShadowRoot': {
+    'resultNotAddRefed': [
+        'styleSheets'
+    ]
+},
+
 'SharedWorker': {
     'nativeType': 'mozilla::dom::workers::SharedWorker',
     'headerFile': 'mozilla/dom/workers/bindings/SharedWorker.h',
     'implicitJSContext': [ 'constructor' ],
 },
 
 'SharedWorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6024,30 +6024,31 @@ class CGMemberJITInfo(CGThing):
     """
     def __init__(self, descriptor, member):
         self.member = member
         self.descriptor = descriptor
 
     def declare(self):
         return ""
 
-    def defineJitInfo(self, infoName, opName, opType, infallible, constant,
-                      pure, hasSlot, slotIndex, returnTypes, args):
+    def defineJitInfo(self, infoName, opName, opType, infallible, movable,
+                      aliasSet, hasSlot, slotIndex, returnTypes, args):
         """
+        aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit.
+
         args is None if we don't want to output argTypes for some
         reason (e.g. we have overloads or we're not a method) and
         otherwise an iterable of the arguments for this method.
         """
-        assert(not constant or pure) # constants are always pure
-        assert(not hasSlot or pure) # Things with slots had better be pure
+        assert(not movable or aliasSet != "AliasEverything") # Can't move write-aliasing things
+        assert(not hasSlot or movable) # Things with slots had better be movable
         protoID = "prototypes::id::%s" % self.descriptor.name
         depth = "PrototypeTraits<%s>::Depth" % protoID
         failstr = toStringBool(infallible)
-        conststr = toStringBool(constant)
-        purestr = toStringBool(pure)
+        movablestr = toStringBool(movable)
         slotStr = toStringBool(hasSlot)
         returnType = reduce(CGMemberJITInfo.getSingleReturnType, returnTypes,
                             "")
         if args is not None:
             argTypes = "%s_argTypes" % infoName
             args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
             args.append("JSJitInfo::ArgTypeListEnd")
             argTypesDecl = (
@@ -6059,63 +6060,70 @@ class CGMemberJITInfo(CGThing):
         return ("\n"
                 "%s"
                 "static const JSJitInfo %s = {\n"
                 "  { %s },\n"
                 "  %s,\n"
                 "  %s,\n"
                 "  JSJitInfo::%s,\n"
                 "  %s,  /* isInfallible. False in setters. */\n"
-                "  %s,  /* isConstant. Only relevant for getters. */\n"
-                "  %s,  /* isPure.  Not relevant for setters. */\n"
+                "  %s,  /* isMovable.  Not relevant for setters. */\n"
+                "  JSJitInfo::%s,  /* aliasSet.  Not relevant for setters. */\n"
                 "  %s,  /* hasSlot.  Only relevant for getters. */\n"
                 "  %d,  /* Reserved slot index, if we're stored in a slot, else 0. */\n"
                 "  %s,  /* returnType.  Not relevant for setters. */\n"
                 "  %s,  /* argTypes.  Only relevant for methods */\n"
                 "  nullptr /* parallelNative */\n"
                 "};\n" % (argTypesDecl, infoName, opName, protoID, depth,
-                          opType, failstr, conststr, purestr, slotStr,
+                          opType, failstr, movablestr, aliasSet, slotStr,
                           slotIndex, returnType, argTypes))
 
     def define(self):
         if self.member.isAttr():
             getterinfo = ("%s_getterinfo" % self.member.identifier.name)
             # We need the cast here because JSJitGetterOp has a "void* self"
             # while we have the right type.
             getter = ("(JSJitGetterOp)get_%s" % self.member.identifier.name)
             getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
             getterconst = (self.member.getExtendedAttribute("SameObject") or
                            self.member.getExtendedAttribute("Constant"))
             getterpure = getterconst or self.member.getExtendedAttribute("Pure")
-            assert (getterinfal or (not getterconst and not getterpure))
+            if getterconst:
+                aliasSet = "AliasNone"
+            elif getterpure:
+                aliasSet = "AliasDOMSets"
+            else:
+                aliasSet = "AliasEverything"
+            movable = getterpure and getterinfal
 
             getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
             isInSlot = self.member.getExtendedAttribute("StoreInSlot")
             if isInSlot:
                 slotIndex = INSTANCE_RESERVED_SLOTS + self.member.slotIndex;
                 if slotIndex >= 16 : # JS engine currently allows 16 fixed slots
                     raise TypeError("Make sure we can actually have this many "
                                     "fixed slots, please!")
             else:
                 slotIndex = 0
 
             result = self.defineJitInfo(getterinfo, getter, "Getter",
-                                        getterinfal, getterconst, getterpure,
+                                        getterinfal, movable, aliasSet,
                                         isInSlot, slotIndex,
                                         [self.member.type], None)
             if (not self.member.readonly or
                 self.member.getExtendedAttribute("PutForwards") is not None or
                 self.member.getExtendedAttribute("Replaceable") is not None):
                 setterinfo = ("%s_setterinfo" % self.member.identifier.name)
                 # Actually a JSJitSetterOp, but JSJitGetterOp is first in the
                 # union.
                 setter = ("(JSJitGetterOp)set_%s" % self.member.identifier.name)
                 # Setters are always fallible, since they have to do a typed unwrap.
                 result += self.defineJitInfo(setterinfo, setter, "Setter",
-                                             False, False, False, False, 0,
+                                             False, False, "AliasEverything",
+                                             False, 0,
                                              [BuiltinTypes[IDLBuiltinType.Types.void]],
                                              None)
             return result
         if self.member.isMethod():
             methodinfo = ("%s_methodinfo" % self.member.identifier.name)
             name = CppKeywords.checkMethodName(self.member.identifier.name)
             # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union.
             method = ("(JSJitGetterOp)%s" % name)
@@ -6141,18 +6149,22 @@ class CGMemberJITInfo(CGThing):
                 else:
                     methodInfal = "infallible" in self.descriptor.getExtendedAttributes(self.member)
                 # For now, only bother to output args if we're pure
                 if methodPure:
                     args = sig[1]
                 else:
                     args = None
 
+            if args:
+                aliasSet = "AliasDOMSets"
+            else:
+                aliasSet = "AliasEverything"
             result = self.defineJitInfo(methodinfo, method, "Method",
-                                        methodInfal, False, methodPure, False, 0,
+                                        methodInfal, False, aliasSet, False, 0,
                                         [s[0] for s in sigs], args)
             return result
         raise TypeError("Illegal member type to CGPropertyJITInfo")
 
     @staticmethod
     def getJSReturnTypeTag(t):
         if t.nullable():
             # Sometimes it might return null, sometimes not
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -2682,22 +2682,18 @@ class IDLAttribute(IDLInterfaceMember):
         if identifier == "TreatNonCallableAsNull":
             raise WebIDLError("TreatNonCallableAsNull cannot be specified on attributes",
                               [attr.location, self.location])
         elif identifier == "SetterThrows" and self.readonly:
             raise WebIDLError("Readonly attributes must not be flagged as "
                               "[SetterThrows]",
                               [self.location])
         elif (((identifier == "Throws" or identifier == "GetterThrows") and
-               (self.getExtendedAttribute("Pure") or
-                self.getExtendedAttribute("StoreInSlot") or
-                self.getExtendedAttribute("SameObject") or
-                self.getExtendedAttribute("Constant"))) or
-              ((identifier == "Pure" or identifier == "SameObject" or
-                identifier == "Constant" or identifier == "StoreInSlot") and
+               self.getExtendedAttribute("StoreInSlot")) or
+              (identifier == "StoreInSlot" and
                (self.getExtendedAttribute("Throws") or
                 self.getExtendedAttribute("GetterThrows")))):
             raise WebIDLError("Throwing things can't be [Pure] or [Constant] "
                               "or [SameObject] or [StoreInSlot]",
                               [attr.location])
         elif identifier == "LenientThis":
             if not attr.noArguments():
                 raise WebIDLError("[LenientThis] must take no arguments",
@@ -3281,20 +3277,16 @@ class IDLMethod(IDLInterfaceMember, IDLS
                               "[Unforgeable]",
                               [attr.location, self.location])
         elif identifier == "SameObject":
             raise WebIDLError("Methods must not be flagged as [SameObject]",
                               [attr.location, self.location]);
         elif identifier == "Constant":
             raise WebIDLError("Methods must not be flagged as [Constant]",
                               [attr.location, self.location]);
-        elif ((identifier == "Pure" and self.getExtendedAttribute("Throws")) or
-              (identifier == "Throws" and self.getExtendedAttribute("Pure"))):
-            raise WebIDLError("Throwing methods can't be [Pure]",
-                              [attr.location]);
         elif identifier == "PutForwards":
             raise WebIDLError("Only attributes support [PutForwards]",
                               [attr.location, self.location])
         elif identifier == "LenientFloat":
             # This is called before we've done overload resolution
             assert len(self.signatures()) == 1
             sig = self.signatures()[0]
             if not sig[0].isVoid():
deleted file mode 100644
--- a/dom/bluetooth/BluetoothSocket.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
-/* 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_dom_bluetooth_BluetoothSocket_h
-#define mozilla_dom_bluetooth_BluetoothSocket_h
-
-#include "BluetoothCommon.h"
-#include "mozilla/ipc/UnixSocket.h"
-
-BEGIN_BLUETOOTH_NAMESPACE
-
-class BluetoothSocketObserver;
-
-class BluetoothSocket : public mozilla::ipc::UnixSocketConsumer
-{
-public:
-  BluetoothSocket(BluetoothSocketObserver* aObserver,
-                  BluetoothSocketType aType,
-                  bool aAuth,
-                  bool aEncrypt);
-
-  bool Connect(const nsACString& aDeviceAddress, int aChannel);
-  bool Listen(int aChannel);
-  inline void Disconnect()
-  {
-    CloseSocket();
-  }
-
-  virtual void OnConnectSuccess() MOZ_OVERRIDE;
-  virtual void OnConnectError() MOZ_OVERRIDE;
-  virtual void OnDisconnect() MOZ_OVERRIDE;
-  virtual void ReceiveSocketData(
-    nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage) MOZ_OVERRIDE;
-
-  inline void GetAddress(nsAString& aDeviceAddress)
-  {
-    GetSocketAddr(aDeviceAddress);
-  }
-
-private:
-  BluetoothSocketObserver* mObserver;
-  BluetoothSocketType mType;
-  bool mAuth;
-  bool mEncrypt;
-};
-
-END_BLUETOOTH_NAMESPACE
-
-#endif
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
@@ -0,0 +1,1526 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* 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 "base/basictypes.h"
+#include "BluetoothOppManager.h"
+
+#include "BluetoothService.h"
+#include "BluetoothSocket.h"
+#include "BluetoothUtils.h"
+#include "BluetoothUuid.h"
+#include "ObexBase.h"
+
+#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "nsAutoPtr.h"
+#include "nsCExternalHandlerService.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIDOMFile.h"
+#include "nsIFile.h"
+#include "nsIInputStream.h"
+#include "nsIMIMEService.h"
+#include "nsIOutputStream.h"
+#include "nsIVolumeService.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+
+#define TARGET_SUBDIR "Download/Bluetooth/"
+
+USING_BLUETOOTH_NAMESPACE
+using namespace mozilla;
+using namespace mozilla::ipc;
+
+namespace {
+// Sending system message "bluetooth-opp-update-progress" every 50kb
+static const uint32_t kUpdateProgressBase = 50 * 1024;
+
+/*
+ * The format of the header of an PUT request is
+ * [opcode:1][packet length:2][headerId:1][header length:2]
+ */
+static const uint32_t kPutRequestHeaderSize = 6;
+
+StaticRefPtr<BluetoothOppManager> sBluetoothOppManager;
+static bool sInShutdown = false;
+}
+
+class mozilla::dom::bluetooth::SendFileBatch {
+public:
+  SendFileBatch(const nsAString& aDeviceAddress, BlobParent* aActor)
+    : mDeviceAddress(aDeviceAddress)
+  {
+    mBlobs.AppendElement(aActor->GetBlob().get());
+  }
+
+  nsString mDeviceAddress;
+  nsCOMArray<nsIDOMBlob> mBlobs;
+};
+
+NS_IMETHODIMP
+BluetoothOppManager::Observe(nsISupports* aSubject,
+                             const char* aTopic,
+                             const PRUnichar* aData)
+{
+  MOZ_ASSERT(sBluetoothOppManager);
+
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    HandleShutdown();
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(false, "BluetoothOppManager got unexpected topic!");
+  return NS_ERROR_UNEXPECTED;
+}
+
+class SendSocketDataTask : public nsRunnable
+{
+public:
+  SendSocketDataTask(uint8_t* aStream, uint32_t aSize)
+    : mStream(aStream)
+    , mSize(aSize)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    sBluetoothOppManager->SendPutRequest(mStream, mSize);
+
+    return NS_OK;
+  }
+
+private:
+  nsAutoArrayPtr<uint8_t> mStream;
+  uint32_t mSize;
+};
+
+class ReadFileTask : public nsRunnable
+{
+public:
+  ReadFileTask(nsIInputStream* aInputStream,
+               uint32_t aRemoteMaxPacketSize) : mInputStream(aInputStream)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    mAvailablePacketSize = aRemoteMaxPacketSize - kPutRequestHeaderSize;
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    uint32_t numRead;
+    nsAutoArrayPtr<char> buf(new char[mAvailablePacketSize]);
+
+    // function inputstream->Read() only works on non-main thread
+    nsresult rv = mInputStream->Read(buf, mAvailablePacketSize, &numRead);
+    if (NS_FAILED(rv)) {
+      // Needs error handling here
+      BT_WARNING("Failed to read from input stream");
+      return NS_ERROR_FAILURE;
+    }
+
+    if (numRead > 0) {
+      sBluetoothOppManager->CheckPutFinal(numRead);
+
+      nsRefPtr<SendSocketDataTask> task =
+        new SendSocketDataTask((uint8_t*)buf.forget(), numRead);
+      if (NS_FAILED(NS_DispatchToMainThread(task))) {
+        BT_WARNING("Failed to dispatch to main thread!");
+        return NS_ERROR_FAILURE;
+      }
+    }
+
+    return NS_OK;
+  };
+
+private:
+  nsCOMPtr<nsIInputStream> mInputStream;
+  uint32_t mAvailablePacketSize;
+};
+
+class CloseSocketTask : public Task
+{
+public:
+  CloseSocketTask(BluetoothSocket* aSocket) : mSocket(aSocket)
+  {
+    MOZ_ASSERT(aSocket);
+  }
+
+  void Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mSocket->CloseDroidSocket();
+  }
+
+private:
+  nsRefPtr<BluetoothSocket> mSocket;
+};
+
+BluetoothOppManager::BluetoothOppManager() : mConnected(false)
+                                           , mRemoteObexVersion(0)
+                                           , mRemoteConnectionFlags(0)
+                                           , mRemoteMaxPacketLength(0)
+                                           , mLastCommand(0)
+                                           , mPacketLength(0)
+                                           , mPacketReceivedLength(0)
+                                           , mBodySegmentLength(0)
+                                           , mAbortFlag(false)
+                                           , mNewFileFlag(false)
+                                           , mPutFinalFlag(false)
+                                           , mSendTransferCompleteFlag(false)
+                                           , mSuccessFlag(false)
+                                           , mIsServer(true)
+                                           , mWaitingForConfirmationFlag(false)
+                                           , mFileLength(0)
+                                           , mSentFileLength(0)
+                                           , mWaitingToSendPutFinal(false)
+                                           , mCurrentBlobIndex(-1)
+{
+  mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
+}
+
+BluetoothOppManager::~BluetoothOppManager()
+{
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  NS_ENSURE_TRUE_VOID(obs);
+  if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
+    BT_WARNING("Failed to remove shutdown observer!");
+  }
+}
+
+bool
+BluetoothOppManager::Init()
+{
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  NS_ENSURE_TRUE(obs, false);
+  if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
+    BT_WARNING("Failed to add shutdown observer!");
+    return false;
+  }
+
+  /**
+   * We don't start listening here as BluetoothServiceBluedroid calls Listen()
+   * immediately when BT stops.
+   *
+   * If we start listening here, the listening fails when device boots up since
+   * Listen() is called again and restarts server socket. The restart causes
+   * absence of read events when device boots up.
+   */
+
+  return true;
+}
+
+//static
+BluetoothOppManager*
+BluetoothOppManager::Get()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // If sBluetoothOppManager already exists, exit early
+  if (sBluetoothOppManager) {
+    return sBluetoothOppManager;
+  }
+
+  // If we're in shutdown, don't create a new instance
+  NS_ENSURE_FALSE(sInShutdown, nullptr);
+
+  // Create a new instance, register, and return
+  BluetoothOppManager *manager = new BluetoothOppManager();
+  NS_ENSURE_TRUE(manager->Init(), nullptr);
+
+  sBluetoothOppManager = manager;
+  return sBluetoothOppManager;
+}
+
+void
+BluetoothOppManager::ConnectInternal(const nsAString& aDeviceAddress)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Stop listening because currently we only support one connection at a time.
+  if (mServerSocket) {
+    mServerSocket->Disconnect();
+    mServerSocket = nullptr;
+  }
+
+  mIsServer = false;
+
+  BluetoothService* bs = BluetoothService::Get();
+  if (!bs || sInShutdown || mSocket) {
+    OnSocketConnectError(mSocket);
+    return;
+  }
+
+  mSocket =
+    new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
+  mSocket->Connect(aDeviceAddress, -1);
+}
+
+void
+BluetoothOppManager::HandleShutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  sInShutdown = true;
+
+  if (mSocket) {
+    mSocket->Disconnect();
+    mSocket = nullptr;
+  }
+  if (mServerSocket) {
+    mServerSocket->Disconnect();
+    mServerSocket = nullptr;
+  }
+  sBluetoothOppManager = nullptr;
+}
+
+bool
+BluetoothOppManager::Listen()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mSocket) {
+    BT_WARNING("mSocket exists. Failed to listen.");
+    return false;
+  }
+
+  /**
+   * Restart server socket since its underlying fd becomes invalid when
+   * BT stops; otherwise no more read events would be received even if
+   * BT restarts.
+   */
+  if (mServerSocket) {
+    mServerSocket->Disconnect();
+    mServerSocket = nullptr;
+  }
+
+  mServerSocket =
+    new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
+
+  if (!mServerSocket->Listen(BluetoothReservedChannels::CHANNEL_OPUSH)) {
+    BT_WARNING("[OPP] Can't listen on RFCOMM socket!");
+    mServerSocket = nullptr;
+    return false;
+  }
+
+  mIsServer = true;
+
+  return true;
+}
+
+void
+BluetoothOppManager::StartSendingNextFile()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(!IsConnected());
+  MOZ_ASSERT(!mBatches.IsEmpty());
+  MOZ_ASSERT(mBatches[0].mBlobs.Length() > mCurrentBlobIndex + 1);
+
+  mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex];
+
+  // Before sending content, we have to send a header including
+  // information such as file name, file length and content type.
+  ExtractBlobHeaders();
+  StartFileTransfer();
+
+  if (mCurrentBlobIndex == 0) {
+    // We may have more than one file waiting for transferring, but only one
+    // CONNECT request would be sent. Therefore check if this is the very first
+    // file at the head of queue.
+    SendConnectRequest();
+  } else {
+    SendPutHeaderRequest(mFileName, mFileLength);
+    AfterFirstPut();
+  }
+}
+
+bool
+BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
+                              BlobParent* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  AppendBlobToSend(aDeviceAddress, aActor);
+  if (!mSocket) {
+    ProcessNextBatch();
+  }
+
+  return true;
+}
+
+void
+BluetoothOppManager::AppendBlobToSend(const nsAString& aDeviceAddress,
+                                      BlobParent* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  int indexTail = mBatches.Length() - 1;
+
+  /**
+   * Create a new batch if
+   * - mBatches is empty, or
+   * - aDeviceAddress differs from mDeviceAddress of the last batch
+   */
+  if (mBatches.IsEmpty() ||
+      aDeviceAddress != mBatches[indexTail].mDeviceAddress) {
+    SendFileBatch batch(aDeviceAddress, aActor);
+    mBatches.AppendElement(batch);
+  } else {
+    mBatches[indexTail].mBlobs.AppendElement(aActor->GetBlob().get());
+  }
+}
+
+void
+BluetoothOppManager::DiscardBlobsToSend()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(!mBatches.IsEmpty());
+  MOZ_ASSERT(!mIsServer);
+
+  int length = (int) mBatches[0].mBlobs.Length();
+  while (length > mCurrentBlobIndex + 1) {
+    mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex];
+
+    BT_LOGR("%s: idx %d", __FUNCTION__, mCurrentBlobIndex);
+    ExtractBlobHeaders();
+    StartFileTransfer();
+    FileTransferComplete();
+  }
+}
+
+bool
+BluetoothOppManager::ProcessNextBatch()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Remove the processed batch.
+  // A batch is processed if we've incremented mCurrentBlobIndex for it.
+  if (mCurrentBlobIndex >= 0) {
+    ClearQueue();
+    mBatches.RemoveElementAt(0);
+    BT_LOGR("%s: REMOVE. %d remaining", __FUNCTION__, mBatches.Length());
+  }
+
+  // Process the next batch
+  if (!mBatches.IsEmpty()) {
+    ConnectInternal(mBatches[0].mDeviceAddress);
+    return true;
+  }
+
+  // No more batch to process
+  return false;
+}
+
+void
+BluetoothOppManager::ClearQueue()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(!mIsServer);
+  MOZ_ASSERT(!mBatches.IsEmpty());
+  MOZ_ASSERT(!mBatches[0].mBlobs.IsEmpty());
+
+  mCurrentBlobIndex = -1;
+  mBlob = nullptr;
+  mBatches[0].mBlobs.Clear();
+}
+
+bool
+BluetoothOppManager::StopSendingFile()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mIsServer) {
+    mAbortFlag = true;
+  } else if (mSocket) {
+    mSocket->Disconnect();
+  } else {
+    BT_WARNING("%s: No ongoing file transfer to stop", __FUNCTION__);
+  }
+
+  return true;
+}
+
+bool
+BluetoothOppManager::ConfirmReceivingFile(bool aConfirm)
+{
+  NS_ENSURE_TRUE(mConnected, false);
+  NS_ENSURE_TRUE(mWaitingForConfirmationFlag, false);
+
+  MOZ_ASSERT(mPacketReceivedLength == 0);
+
+  mWaitingForConfirmationFlag = false;
+
+  // For the first packet of first file
+  bool success = false;
+  if (aConfirm) {
+    StartFileTransfer();
+    if (CreateFile()) {
+      success = WriteToFile(mBodySegment.get(), mBodySegmentLength);
+    }
+  }
+
+  if (success && mPutFinalFlag) {
+    mSuccessFlag = true;
+    FileTransferComplete();
+    NotifyAboutFileChange();
+  }
+
+  ReplyToPut(mPutFinalFlag, success);
+  return true;
+}
+
+void
+BluetoothOppManager::AfterFirstPut()
+{
+  mUpdateProgressCounter = 1;
+  mPutFinalFlag = false;
+  mPacketReceivedLength = 0;
+  mSentFileLength = 0;
+  mWaitingToSendPutFinal = false;
+  mSuccessFlag = false;
+  mBodySegmentLength = 0;
+}
+
+void
+BluetoothOppManager::AfterOppConnected()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mConnected = true;
+  mAbortFlag = false;
+  mWaitingForConfirmationFlag = true;
+  AfterFirstPut();
+  // Get a mount lock to prevent the sdcard from being shared with
+  // the PC while we're doing a OPP file transfer. After OPP transaction
+  // were done, the mount lock will be freed.
+  if (!AcquireSdcardMountLock()) {
+    // If we fail to get a mount lock, abort this transaction
+    // Directly sending disconnect-request is better than abort-request
+    BT_WARNING("BluetoothOPPManager couldn't get a mount lock!");
+
+    MOZ_ASSERT(mSocket);
+    mSocket->Disconnect();
+  }
+}
+
+void
+BluetoothOppManager::AfterOppDisconnected()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mConnected = false;
+  mLastCommand = 0;
+  mPacketReceivedLength = 0;
+  mDsFile = nullptr;
+
+  // We can't reset mSuccessFlag here since this function may be called
+  // before we send system message of transfer complete
+  // mSuccessFlag = false;
+
+  if (mInputStream) {
+    mInputStream->Close();
+    mInputStream = nullptr;
+  }
+
+  if (mOutputStream) {
+    mOutputStream->Close();
+    mOutputStream = nullptr;
+  }
+
+  if (mReadFileThread) {
+    mReadFileThread->Shutdown();
+    mReadFileThread = nullptr;
+  }
+  // Release the Mount lock if file transfer completed
+  if (mMountLock) {
+    // The mount lock will be implicitly unlocked
+    mMountLock = nullptr;
+  }
+}
+
+void
+BluetoothOppManager::DeleteReceivedFile()
+{
+  if (mOutputStream) {
+    mOutputStream->Close();
+    mOutputStream = nullptr;
+  }
+
+  if (mDsFile && mDsFile->mFile) {
+    mDsFile->mFile->Remove(false);
+    mDsFile = nullptr;
+  }
+}
+
+bool
+BluetoothOppManager::CreateFile()
+{
+  MOZ_ASSERT(mPacketReceivedLength == mPacketLength);
+
+  nsString path;
+  path.AssignLiteral(TARGET_SUBDIR);
+  path.Append(mFileName);
+
+  mDsFile = DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644);
+  NS_ENSURE_TRUE(mDsFile, false);
+
+  nsCOMPtr<nsIFile> f;
+  mDsFile->mFile->Clone(getter_AddRefs(f));
+
+  /*
+   * The function CreateUnique() may create a file with a different file
+   * name from the original mFileName. Therefore we have to retrieve
+   * the file name again.
+   */
+  f->GetLeafName(mFileName);
+
+  NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f);
+  NS_ENSURE_TRUE(mOutputStream, false);
+
+  return true;
+}
+
+bool
+BluetoothOppManager::WriteToFile(const uint8_t* aData, int aDataLength)
+{
+  NS_ENSURE_TRUE(mOutputStream, false);
+
+  uint32_t wrote = 0;
+  mOutputStream->Write((const char*)aData, aDataLength, &wrote);
+  NS_ENSURE_TRUE(aDataLength == (int) wrote, false);
+
+  return true;
+}
+
+// Virtual function of class SocketConsumer
+void
+BluetoothOppManager::ExtractPacketHeaders(const ObexHeaderSet& aHeader)
+{
+  if (aHeader.Has(ObexHeaderId::Name)) {
+    aHeader.GetName(mFileName);
+  }
+
+  if (aHeader.Has(ObexHeaderId::Type)) {
+    aHeader.GetContentType(mContentType);
+  }
+
+  if (aHeader.Has(ObexHeaderId::Length)) {
+    aHeader.GetLength(&mFileLength);
+  }
+
+  if (aHeader.Has(ObexHeaderId::Body) ||
+      aHeader.Has(ObexHeaderId::EndOfBody)) {
+    uint8_t* bodyPtr;
+    aHeader.GetBody(&bodyPtr);
+    mBodySegment = bodyPtr;
+
+    aHeader.GetBodyLength(&mBodySegmentLength);
+  }
+}
+
+bool
+BluetoothOppManager::ExtractBlobHeaders()
+{
+  RetrieveSentFileName();
+
+  nsresult rv = mBlob->GetType(mContentType);
+  if (NS_FAILED(rv)) {
+    BT_WARNING("Can't get content type");
+    SendDisconnectRequest();
+    return false;
+  }
+
+  uint64_t fileLength;
+  rv = mBlob->GetSize(&fileLength);
+  if (NS_FAILED(rv)) {
+    BT_WARNING("Can't get file size");
+    SendDisconnectRequest();
+    return false;
+  }
+
+  // Currently we keep the size of files which were sent/received via
+  // Bluetooth not exceed UINT32_MAX because the Length header in OBEX
+  // is only 4-byte long. Although it is possible to transfer a file
+  // larger than UINT32_MAX, it needs to parse another OBEX Header
+  // and I would like to leave it as a feature.
+  if (fileLength > (uint64_t)UINT32_MAX) {
+    BT_WARNING("The file size is too large for now");
+    SendDisconnectRequest();
+    return false;
+  }
+
+  mFileLength = fileLength;
+  rv = NS_NewThread(getter_AddRefs(mReadFileThread));
+  if (NS_FAILED(rv)) {
+    BT_WARNING("Can't create thread");
+    SendDisconnectRequest();
+    return false;
+  }
+
+  return true;
+}
+
+void
+BluetoothOppManager::RetrieveSentFileName()
+{
+  mFileName.Truncate();
+
+  nsCOMPtr<nsIDOMFile> file = do_QueryInterface(mBlob);
+  if (file) {
+    file->GetName(mFileName);
+  }
+
+  /**
+   * We try our best to get the file extention to avoid interoperability issues.
+   * However, once we found that we are unable to get suitable extension or
+   * information about the content type, sending a pre-defined file name without
+   * extension would be fine.
+   */
+  if (mFileName.IsEmpty()) {
+    mFileName.AssignLiteral("Unknown");
+  }
+
+  int32_t offset = mFileName.RFindChar('/');
+  if (offset != kNotFound) {
+    mFileName = Substring(mFileName, offset + 1);
+  }
+
+  offset = mFileName.RFindChar('.');
+  if (offset == kNotFound) {
+    nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
+
+    if (mimeSvc) {
+      nsString mimeType;
+      mBlob->GetType(mimeType);
+
+      nsCString extension;
+      nsresult rv =
+        mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType),
+                                     EmptyCString(),
+                                     extension);
+      if (NS_SUCCEEDED(rv)) {
+        mFileName.AppendLiteral(".");
+        AppendUTF8toUTF16(extension, mFileName);
+      }
+    }
+  }
+}
+
+bool
+BluetoothOppManager::IsReservedChar(PRUnichar c)
+{
+  return (c < 0x0020 ||
+          c == PRUnichar('?') || c == PRUnichar('|') || c == PRUnichar('<') ||
+          c == PRUnichar('>') || c == PRUnichar('"') || c == PRUnichar(':') ||
+          c == PRUnichar('/') || c == PRUnichar('*') || c == PRUnichar('\\'));
+}
+
+void
+BluetoothOppManager::ValidateFileName()
+{
+  int length = mFileName.Length();
+
+  for (int i = 0; i < length; ++i) {
+    // Replace reserved char of fat file system with '_'
+    if (IsReservedChar(mFileName.CharAt(i))) {
+      mFileName.Replace(i, 1, PRUnichar('_'));
+    }
+  }
+}
+
+bool
+BluetoothOppManager::ComposePacket(uint8_t aOpCode, UnixSocketRawData* aMessage)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aMessage);
+
+  int frameHeaderLength = 0;
+
+  // See if this is the first part of each Put packet
+  if (mPacketReceivedLength == 0) {
+    // Section 3.3.3 "Put", IrOBEX 1.2
+    // [opcode:1][length:2][Headers:var]
+    frameHeaderLength = 3;
+
+    mPacketLength = ((((int)aMessage->mData[1]) << 8) | aMessage->mData[2]) -
+                      frameHeaderLength;
+    /**
+     * A PUT request from remote devices may be divided into multiple parts.
+     * In other words, one request may need to be received multiple times,
+     * so here we keep a variable mPacketLeftLength to indicate if current
+     * PUT request is done.
+     */
+    mReceivedDataBuffer = new uint8_t[mPacketLength];
+    mPutFinalFlag = (aOpCode == ObexRequestCode::PutFinal);
+  }
+
+  int dataLength = aMessage->mSize - frameHeaderLength;
+
+  // Check length before memcpy to prevent from memory pollution
+  if (dataLength < 0 ||
+      mPacketReceivedLength + dataLength > mPacketLength) {
+    BT_LOGR("%s: Received packet size is unreasonable", __FUNCTION__);
+
+    ReplyToPut(mPutFinalFlag, false);
+    DeleteReceivedFile();
+    FileTransferComplete();
+
+    return false;
+  }
+
+  memcpy(mReceivedDataBuffer.get() + mPacketReceivedLength,
+         &aMessage->mData[frameHeaderLength], dataLength);
+
+  mPacketReceivedLength += dataLength;
+
+  return (mPacketReceivedLength == mPacketLength);
+}
+
+void
+BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  uint8_t opCode;
+  int receivedLength = aMessage->mSize;
+
+  if (mPacketReceivedLength > 0) {
+    opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
+  } else {
+    opCode = aMessage->mData[0];
+
+    // When there's a Put packet right after a PutFinal packet,
+    // which means it's the start point of a new file.
+    if (mPutFinalFlag &&
+        (opCode == ObexRequestCode::Put ||
+         opCode == ObexRequestCode::PutFinal)) {
+      mNewFileFlag = true;
+      AfterFirstPut();
+    }
+  }
+
+  ObexHeaderSet pktHeaders(opCode);
+  if (opCode == ObexRequestCode::Connect) {
+    // Section 3.3.1 "Connect", IrOBEX 1.2
+    // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
+    // [Headers:var]
+    ParseHeaders(&aMessage->mData[7],
+                 receivedLength - 7,
+                 &pktHeaders);
+    ReplyToConnect();
+    AfterOppConnected();
+  } else if (opCode == ObexRequestCode::Abort) {
+    // Section 3.3.5 "Abort", IrOBEX 1.2
+    // [opcode:1][length:2][Headers:var]
+    ParseHeaders(&aMessage->mData[3],
+                receivedLength - 3,
+                &pktHeaders);
+    ReplyToDisconnectOrAbort();
+    DeleteReceivedFile();
+  } else if (opCode == ObexRequestCode::Disconnect) {
+    // Section 3.3.2 "Disconnect", IrOBEX 1.2
+    // [opcode:1][length:2][Headers:var]
+    ParseHeaders(&aMessage->mData[3],
+                receivedLength - 3,
+                &pktHeaders);
+    ReplyToDisconnectOrAbort();
+    AfterOppDisconnected();
+    FileTransferComplete();
+  } else if (opCode == ObexRequestCode::Put ||
+             opCode == ObexRequestCode::PutFinal) {
+    if (!ComposePacket(opCode, aMessage)) {
+      return;
+    }
+
+    // A Put packet is received completely
+    ParseHeaders(mReceivedDataBuffer.get(), mPacketReceivedLength, &pktHeaders);
+    ExtractPacketHeaders(pktHeaders);
+    ValidateFileName();
+
+    mPacketReceivedLength = 0;
+
+    // When we cancel the transfer, delete the file and notify completion
+    if (mAbortFlag) {
+      ReplyToPut(mPutFinalFlag, false);
+      mSentFileLength += mBodySegmentLength;
+      DeleteReceivedFile();
+      FileTransferComplete();
+      return;
+    }
+
+    // Wait until get confirmation from user, then create file and write to it
+    if (mWaitingForConfirmationFlag) {
+      ReceivingFileConfirmation();
+      mSentFileLength += mBodySegmentLength;
+      return;
+    }
+
+    // Already get confirmation from user, create a new file if needed and
+    // write to output stream
+    if (mNewFileFlag) {
+      StartFileTransfer();
+      if (!CreateFile()) {
+        ReplyToPut(mPutFinalFlag, false);
+        return;
+      }
+      mNewFileFlag = false;
+    }
+
+    if (!WriteToFile(mBodySegment.get(), mBodySegmentLength)) {
+      ReplyToPut(mPutFinalFlag, false);
+      return;
+    }
+
+    ReplyToPut(mPutFinalFlag, true);
+
+    // Send progress update
+    mSentFileLength += mBodySegmentLength;
+    if (mSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) {
+      UpdateProgress();
+      mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
+    }
+
+    // Success to receive a file and notify completion
+    if (mPutFinalFlag) {
+      mSuccessFlag = true;
+      FileTransferComplete();
+      NotifyAboutFileChange();
+    }
+  } else if (opCode == ObexRequestCode::Get ||
+             opCode == ObexRequestCode::GetFinal ||
+             opCode == ObexRequestCode::SetPath) {
+    ReplyError(ObexResponseCode::BadRequest);
+    BT_WARNING("Unsupported ObexRequestCode");
+  } else {
+    ReplyError(ObexResponseCode::NotImplemented);
+    BT_WARNING("Unrecognized ObexRequestCode");
+  }
+}
+
+void
+BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  uint8_t opCode = aMessage->mData[0];
+
+  // Check response code and send out system message as finished if the response
+  // code is somehow incorrect.
+  uint8_t expectedOpCode = ObexResponseCode::Success;
+  if (mLastCommand == ObexRequestCode::Put) {
+    expectedOpCode = ObexResponseCode::Continue;
+  }
+
+  if (opCode != expectedOpCode) {
+    if (mLastCommand == ObexRequestCode::Put ||
+        mLastCommand == ObexRequestCode::Abort ||
+        mLastCommand == ObexRequestCode::PutFinal) {
+      SendDisconnectRequest();
+    }
+    nsAutoCString str;
+    str += "[OPP] 0x";
+    str.AppendInt(mLastCommand, 16);
+    str += " failed";
+    BT_WARNING(str.get());
+    FileTransferComplete();
+    return;
+  }
+
+  if (mLastCommand == ObexRequestCode::PutFinal) {
+    mSuccessFlag = true;
+    FileTransferComplete();
+
+    if (mInputStream) {
+      mInputStream->Close();
+      mInputStream = nullptr;
+    }
+
+    if (mCurrentBlobIndex + 1 == (int) mBatches[0].mBlobs.Length()) {
+      SendDisconnectRequest();
+    } else {
+      StartSendingNextFile();
+    }
+  } else if (mLastCommand == ObexRequestCode::Abort) {
+    SendDisconnectRequest();
+    FileTransferComplete();
+  } else if (mLastCommand == ObexRequestCode::Disconnect) {
+    AfterOppDisconnected();
+    // Most devices will directly terminate connection after receiving
+    // Disconnect request, so we make a delay here. If the socket hasn't been
+    // disconnected, we will close it.
+    if (mSocket) {
+      MessageLoop::current()->
+        PostDelayedTask(FROM_HERE, new CloseSocketTask(mSocket), 1000);
+    }
+  } else if (mLastCommand == ObexRequestCode::Connect) {
+    MOZ_ASSERT(!mFileName.IsEmpty());
+    MOZ_ASSERT(mBlob);
+
+    AfterOppConnected();
+
+    // Keep remote information
+    mRemoteObexVersion = aMessage->mData[3];
+    mRemoteConnectionFlags = aMessage->mData[4];
+    mRemoteMaxPacketLength =
+      (((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
+
+    SendPutHeaderRequest(mFileName, mFileLength);
+  } else if (mLastCommand == ObexRequestCode::Put) {
+    if (mWaitingToSendPutFinal) {
+      SendPutFinalRequest();
+      return;
+    }
+
+    if (kUpdateProgressBase * mUpdateProgressCounter < mSentFileLength) {
+      UpdateProgress();
+      mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
+    }
+
+    nsresult rv;
+    if (!mInputStream) {
+      rv = mBlob->GetInternalStream(getter_AddRefs(mInputStream));
+      if (NS_FAILED(rv)) {
+        BT_WARNING("Can't get internal stream of blob");
+        SendDisconnectRequest();
+        return;
+      }
+    }
+
+    nsRefPtr<ReadFileTask> task = new ReadFileTask(mInputStream,
+                                                   mRemoteMaxPacketLength);
+    rv = mReadFileThread->Dispatch(task, NS_DISPATCH_NORMAL);
+    if (NS_FAILED(rv)) {
+      BT_WARNING("Cannot dispatch read file task!");
+      SendDisconnectRequest();
+    }
+  } else {
+    BT_WARNING("Unhandled ObexRequestCode");
+  }
+}
+
+// Virtual function of class SocketConsumer
+void
+BluetoothOppManager::ReceiveSocketData(BluetoothSocket* aSocket,
+                                       nsAutoPtr<UnixSocketRawData>& aMessage)
+{
+  if (mIsServer) {
+    ServerDataHandler(aMessage);
+  } else {
+    ClientDataHandler(aMessage);
+  }
+}
+
+void
+BluetoothOppManager::SendConnectRequest()
+{
+  if (mConnected) return;
+
+  // Section 3.3.1 "Connect", IrOBEX 1.2
+  // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
+  // [Headers:var]
+  uint8_t req[255];
+  int index = 7;
+
+  req[3] = 0x10; // version=1.0
+  req[4] = 0x00; // flag=0x00
+  req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
+  req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH;
+
+  SendObexData(req, ObexRequestCode::Connect, index);
+}
+
+void
+BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName,
+                                          int aFileSize)
+{
+  if (!mConnected) return;
+
+  uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
+
+  int len = aFileName.Length();
+  uint8_t* fileName = new uint8_t[(len + 1) * 2];
+  const PRUnichar* fileNamePtr = aFileName.BeginReading();
+
+  for (int i = 0; i < len; i++) {
+    fileName[i * 2] = (uint8_t)(fileNamePtr[i] >> 8);
+    fileName[i * 2 + 1] = (uint8_t)fileNamePtr[i];
+  }
+
+  fileName[len * 2] = 0x00;
+  fileName[len * 2 + 1] = 0x00;
+
+  int index = 3;
+  index += AppendHeaderName(&req[index], (char*)fileName, (len + 1) * 2);
+  index += AppendHeaderLength(&req[index], aFileSize);
+
+  SendObexData(req, ObexRequestCode::Put, index);
+
+  delete [] fileName;
+  delete [] req;
+}
+
+void
+BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
+                                    int aFileBodyLength)
+{
+  int packetLeftSpace = mRemoteMaxPacketLength - kPutRequestHeaderSize;
+
+  if (!mConnected) return;
+  if (aFileBodyLength > packetLeftSpace) {
+    BT_WARNING("Not allowed such a small MaxPacketLength value");
+    return;
+  }
+
+  // Section 3.3.3 "Put", IrOBEX 1.2
+  // [opcode:1][length:2][Headers:var]
+  uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
+
+  int index = 3;
+  index += AppendHeaderBody(&req[index], aFileBody, aFileBodyLength);
+
+  SendObexData(req, ObexRequestCode::Put, index);
+  delete [] req;
+
+  mSentFileLength += aFileBodyLength;
+}
+
+void
+BluetoothOppManager::SendPutFinalRequest()
+{
+  if (!mConnected) return;
+
+  /**
+   * Section 2.2.9, "End-of-Body", IrObex 1.2
+   * End-of-Body is used to identify the last chunk of the object body.
+   * For most platforms, a PutFinal packet is sent with an zero length
+   * End-of-Body header.
+   */
+
+  // [opcode:1][length:2]
+  int index = 3;
+  uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
+  index += AppendHeaderEndOfBody(&req[index]);
+
+  SendObexData(req, ObexRequestCode::PutFinal, index);
+  delete [] req;
+
+  mWaitingToSendPutFinal = false;
+}
+
+void
+BluetoothOppManager::SendDisconnectRequest()
+{
+  if (!mConnected) return;
+
+  // Section 3.3.2 "Disconnect", IrOBEX 1.2
+  // [opcode:1][length:2][Headers:var]
+  uint8_t req[255];
+  int index = 3;
+
+  SendObexData(req, ObexRequestCode::Disconnect, index);
+}
+
+void
+BluetoothOppManager::CheckPutFinal(uint32_t aNumRead)
+{
+  if (mSentFileLength + aNumRead >= mFileLength) {
+    mWaitingToSendPutFinal = true;
+  }
+}
+
+bool
+BluetoothOppManager::IsConnected()
+{
+  return (mConnected && !mSendTransferCompleteFlag);
+}
+
+void
+BluetoothOppManager::GetAddress(nsAString& aDeviceAddress)
+{
+  return mSocket->GetAddress(aDeviceAddress);
+}
+
+void
+BluetoothOppManager::ReplyToConnect()
+{
+  if (mConnected) return;
+
+  // Section 3.3.1 "Connect", IrOBEX 1.2
+  // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
+  // [Headers:var]
+  uint8_t req[255];
+  int index = 7;
+
+  req[3] = 0x10; // version=1.0
+  req[4] = 0x00; // flag=0x00
+  req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
+  req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH;
+
+  SendObexData(req, ObexResponseCode::Success, index);
+}
+
+void
+BluetoothOppManager::ReplyToDisconnectOrAbort()
+{
+  if (!mConnected) return;
+
+  // Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2
+  // The format of response packet of "Disconnect" and "Abort" are the same
+  // [opcode:1][length:2][Headers:var]
+  uint8_t req[255];
+  int index = 3;
+
+  SendObexData(req, ObexResponseCode::Success, index);
+}
+
+void
+BluetoothOppManager::ReplyToPut(bool aFinal, bool aContinue)
+{
+  if (!mConnected) return;
+
+  // Section 3.3.2 "Disconnect", IrOBEX 1.2
+  // [opcode:1][length:2][Headers:var]
+  uint8_t req[255];
+  int index = 3;
+  uint8_t opcode;
+
+  if (aContinue) {
+    opcode = (aFinal)? ObexResponseCode::Success :
+                       ObexResponseCode::Continue;
+  } else {
+    opcode = (aFinal)? ObexResponseCode::Unauthorized :
+                       ObexResponseCode::Unauthorized & (~FINAL_BIT);
+  }
+
+  SendObexData(req, opcode, index);
+}
+
+void
+BluetoothOppManager::ReplyError(uint8_t aError)
+{
+  if (!mConnected) return;
+
+  // Section 3.2 "Response Format", IrOBEX 1.2
+  // [opcode:1][length:2][Headers:var]
+  uint8_t req[255];
+  int index = 3;
+
+  SendObexData(req, aError, index);
+}
+
+void
+BluetoothOppManager::SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize)
+{
+  SetObexPacketInfo(aData, aOpcode, aSize);
+
+  if (!mIsServer) {
+    mLastCommand = aOpcode;
+  }
+
+  UnixSocketRawData* s = new UnixSocketRawData(aSize);
+  memcpy(s->mData, aData, s->mSize);
+  mSocket->SendDroidSocketData(s);
+}
+
+void
+BluetoothOppManager::FileTransferComplete()
+{
+  if (mSendTransferCompleteFlag) {
+    return;
+  }
+
+  nsString type, name;
+  BluetoothValue v;
+  InfallibleTArray<BluetoothNamedValue> parameters;
+  type.AssignLiteral("bluetooth-opp-transfer-complete");
+
+  name.AssignLiteral("address");
+  v = mDeviceAddress;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("success");
+  v = mSuccessFlag;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("received");
+  v = mIsServer;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("fileName");
+  v = mFileName;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("fileLength");
+  v = mSentFileLength;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("contentType");
+  v = mContentType;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  if (!BroadcastSystemMessage(type, parameters)) {
+    BT_WARNING("Failed to broadcast [bluetooth-opp-transfer-complete]");
+    return;
+  }
+
+  mSendTransferCompleteFlag = true;
+}
+
+void
+BluetoothOppManager::StartFileTransfer()
+{
+  nsString type, name;
+  BluetoothValue v;
+  InfallibleTArray<BluetoothNamedValue> parameters;
+  type.AssignLiteral("bluetooth-opp-transfer-start");
+
+  name.AssignLiteral("address");
+  v = mDeviceAddress;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("received");
+  v = mIsServer;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("fileName");
+  v = mFileName;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("fileLength");
+  v = mFileLength;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("contentType");
+  v = mContentType;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  if (!BroadcastSystemMessage(type, parameters)) {
+    BT_WARNING("Failed to broadcast [bluetooth-opp-transfer-start]");
+    return;
+  }
+
+  mSendTransferCompleteFlag = false;
+}
+
+void
+BluetoothOppManager::UpdateProgress()
+{
+  nsString type, name;
+  BluetoothValue v;
+  InfallibleTArray<BluetoothNamedValue> parameters;
+  type.AssignLiteral("bluetooth-opp-update-progress");
+
+  name.AssignLiteral("address");
+  v = mDeviceAddress;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("received");
+  v = mIsServer;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("processedLength");
+  v = mSentFileLength;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("fileLength");
+  v = mFileLength;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  if (!BroadcastSystemMessage(type, parameters)) {
+    BT_WARNING("Failed to broadcast [bluetooth-opp-update-progress]");
+    return;
+  }
+}
+
+void
+BluetoothOppManager::ReceivingFileConfirmation()
+{
+  nsString type, name;
+  BluetoothValue v;
+  InfallibleTArray<BluetoothNamedValue> parameters;
+  type.AssignLiteral("bluetooth-opp-receiving-file-confirmation");
+
+  name.AssignLiteral("address");
+  v = mDeviceAddress;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("fileName");
+  v = mFileName;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("fileLength");
+  v = mFileLength;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("contentType");
+  v = mContentType;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  if (!BroadcastSystemMessage(type, parameters)) {
+    BT_WARNING("Failed to send [bluetooth-opp-receiving-file-confirmation]");
+    return;
+  }
+}
+
+void
+BluetoothOppManager::NotifyAboutFileChange()
+{
+  NS_NAMED_LITERAL_STRING(data, "modified");
+
+  nsCOMPtr<nsIObserverService> obs =
+    mozilla::services::GetObserverService();
+  NS_ENSURE_TRUE_VOID(obs);
+
+  obs->NotifyObservers(mDsFile, "file-watcher-notify", data.get());
+}
+
+void
+BluetoothOppManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
+{
+  BT_LOGR("%s: [%s]", __FUNCTION__, (mIsServer)? "server" : "client");
+  MOZ_ASSERT(aSocket);
+
+  /**
+   * If the created connection is an inbound connection, close server socket
+   * because currently only one file-transfer session is allowed. After that,
+   * we need to make sure that server socket would be nulled out.
+   * As for outbound connections, we just notify the controller that it's done.
+   */
+  if (aSocket == mServerSocket) {
+    MOZ_ASSERT(!mSocket);
+    mServerSocket.swap(mSocket);
+  }
+
+  // Cache device address since we can't get socket address when a remote
+  // device disconnect with us.
+  mSocket->GetAddress(mDeviceAddress);
+
+  // Start sending file if we connect as a client
+  if (!mIsServer) {
+    StartSendingNextFile();
+  }
+}
+
+void
+BluetoothOppManager::OnSocketConnectError(BluetoothSocket* aSocket)
+{
+  BT_LOGR("%s: [%s]", __FUNCTION__, (mIsServer)? "server" : "client");
+
+  mServerSocket = nullptr;
+  mSocket = nullptr;
+
+  if (!mIsServer) {
+    // Inform gaia of remaining blobs' sending failure
+    DiscardBlobsToSend();
+  }
+
+  // Listen as a server if there's no more batch to process
+  if (!ProcessNextBatch()) {
+    Listen();
+  }
+}
+
+void
+BluetoothOppManager::OnSocketDisconnect(BluetoothSocket* aSocket)
+{
+  MOZ_ASSERT(aSocket);
+  if (aSocket != mSocket) {
+    // Do nothing when a listening server socket is closed.
+    return;
+  }
+  BT_LOGR("%s: [%s]", __FUNCTION__, (mIsServer) ? "client" : "server");
+
+  /**
+   * It is valid for a bluetooth device which is transfering file via OPP
+   * closing socket without sending OBEX disconnect request first. So we
+   * delete the broken file when we failed to receive a file from the remote,
+   * and notify the transfer has been completed (but failed). We also call
+   * AfterOppDisconnected here to ensure all variables will be cleaned.
+   */
+  if (!mSuccessFlag) {
+    if (mIsServer) {
+      DeleteReceivedFile();
+    }
+
+    FileTransferComplete();
+    if (!mIsServer) {
+      // Inform gaia of remaining blobs' sending failure
+      DiscardBlobsToSend();
+    }
+  }
+
+  AfterOppDisconnected();
+  mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
+  mSuccessFlag = false;
+
+  mSocket = nullptr;
+  // Listen as a server if there's no more batch to process
+  if (!ProcessNextBatch()) {
+    Listen();
+  }
+}
+
+NS_IMPL_ISUPPORTS1(BluetoothOppManager, nsIObserver)
+
+bool
+BluetoothOppManager::AcquireSdcardMountLock()
+{
+  nsCOMPtr<nsIVolumeService> volumeSrv =
+    do_GetService(NS_VOLUMESERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(volumeSrv, false);
+  nsresult rv;
+  rv = volumeSrv->CreateMountLock(NS_LITERAL_STRING("sdcard"),
+                                  getter_AddRefs(mMountLock));
+  NS_ENSURE_SUCCESS(rv, false);
+  return true;
+}
+
+void
+BluetoothOppManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
+                                         const nsAString& aServiceUuid,
+                                         int aChannel)
+{
+  MOZ_ASSERT(false);
+}
+
+void
+BluetoothOppManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
+{
+  MOZ_ASSERT(false);
+}
+
+void
+BluetoothOppManager::Connect(const nsAString& aDeviceAddress,
+                             BluetoothProfileController* aController)
+{
+  MOZ_ASSERT(false);
+}
+
+void
+BluetoothOppManager::Disconnect(BluetoothProfileController* aController)
+{
+  MOZ_ASSERT(false);
+}
+
+void
+BluetoothOppManager::OnConnect(const nsAString& aErrorStr)
+{
+  MOZ_ASSERT(false);
+}
+
+void
+BluetoothOppManager::OnDisconnect(const nsAString& aErrorStr)
+{
+  MOZ_ASSERT(false);
+}
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.h
@@ -0,0 +1,226 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* 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_dom_bluetooth_bluetoothoppmanager_h__
+#define mozilla_dom_bluetooth_bluetoothoppmanager_h__
+
+#include "BluetoothCommon.h"
+#include "BluetoothProfileManagerBase.h"
+#include "BluetoothSocketObserver.h"
+#include "DeviceStorage.h"
+#include "mozilla/dom/ipc/Blob.h"
+#include "mozilla/ipc/UnixSocket.h"
+#include "nsCOMArray.h"
+
+class nsIOutputStream;
+class nsIInputStream;
+class nsIVolumeMountLock;
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothSocket;
+class ObexHeaderSet;
+class SendFileBatch;
+
+class BluetoothOppManager : public BluetoothSocketObserver
+                          , public BluetoothProfileManagerBase
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+  BT_DECL_PROFILE_MGR_BASE
+  virtual void GetName(nsACString& aName)
+  {
+    aName.AssignLiteral("OPP");
+  }
+
+  /*
+   * Channel of reserved services are fixed values, please check
+   * function add_reserved_service_records() in
+   * external/bluetooth/bluez/src/adapter.c for more information.
+   */
+  static const int DEFAULT_OPP_CHANNEL = 10;
+  static const int MAX_PACKET_LENGTH = 0xFFFE;
+
+  ~BluetoothOppManager();
+  static BluetoothOppManager* Get();
+  void ClientDataHandler(mozilla::ipc::UnixSocketRawData* aMessage);
+  void ServerDataHandler(mozilla::ipc::UnixSocketRawData* aMessage);
+
+  bool Listen();
+
+  bool SendFile(const nsAString& aDeviceAddress, BlobParent* aActor);
+  bool StopSendingFile();
+  bool ConfirmReceivingFile(bool aConfirm);
+
+  void SendConnectRequest();
+  void SendPutHeaderRequest(const nsAString& aFileName, int aFileSize);
+  void SendPutRequest(uint8_t* aFileBody, int aFileBodyLength);
+  void SendPutFinalRequest();
+  void SendDisconnectRequest();
+
+  void ExtractPacketHeaders(const ObexHeaderSet& aHeader);
+  bool ExtractBlobHeaders();
+  void CheckPutFinal(uint32_t aNumRead);
+
+  // The following functions are inherited from BluetoothSocketObserver
+  void ReceiveSocketData(
+    BluetoothSocket* aSocket,
+    nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage) MOZ_OVERRIDE;
+  virtual void OnSocketConnectSuccess(BluetoothSocket* aSocket) MOZ_OVERRIDE;
+  virtual void OnSocketConnectError(BluetoothSocket* aSocket) MOZ_OVERRIDE;
+  virtual void OnSocketDisconnect(BluetoothSocket* aSocket) MOZ_OVERRIDE;
+
+private:
+  BluetoothOppManager();
+  bool Init();
+  void HandleShutdown();
+
+  void StartFileTransfer();
+  void StartSendingNextFile();
+  void FileTransferComplete();
+  void UpdateProgress();
+  void ReceivingFileConfirmation();
+  bool CreateFile();
+  bool WriteToFile(const uint8_t* aData, int aDataLength);
+  void DeleteReceivedFile();
+  void ReplyToConnect();
+  void ReplyToDisconnectOrAbort();
+  void ReplyToPut(bool aFinal, bool aContinue);
+  void ReplyError(uint8_t aError);
+  void AfterOppConnected();
+  void AfterFirstPut();
+  void AfterOppDisconnected();
+  void ValidateFileName();
+  bool IsReservedChar(PRUnichar c);
+  void ClearQueue();
+  void RetrieveSentFileName();
+  void NotifyAboutFileChange();
+  bool AcquireSdcardMountLock();
+  void SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize);
+  void AppendBlobToSend(const nsAString& aDeviceAddress, BlobParent* aActor);
+  void DiscardBlobsToSend();
+  bool ProcessNextBatch();
+  void ConnectInternal(const nsAString& aDeviceAddress);
+
+  /**
+   * Usually we won't get a full PUT packet in one operation, which means that
+   * a packet may be devided into several parts and BluetoothOppManager should
+   * be in charge of assembling.
+   *
+   * @return true if a packet has been fully received.
+   *         false if the received length exceeds/not reaches the expected
+   *         length.
+   */
+  bool ComposePacket(uint8_t aOpCode, UnixSocketRawData* aMessage);
+
+  /**
+   * OBEX session status.
+   * Set when OBEX session is established.
+   */
+  bool mConnected;
+  nsString mDeviceAddress;
+
+  /**
+   * Remote information
+   */
+  uint8_t mRemoteObexVersion;
+  uint8_t mRemoteConnectionFlags;
+  int mRemoteMaxPacketLength;
+
+  /**
+   * For sending files, we decide our next action based on current command and
+   * previous one.
+   * For receiving files, we don't need previous command and it is set to 0
+   * as a default value.
+   */
+  int mLastCommand;
+
+  int mPacketLength;
+  int mPacketReceivedLength;
+  int mBodySegmentLength;
+  int mUpdateProgressCounter;
+
+  /**
+   * When it is true and the target service on target device couldn't be found,
+   * refreshing SDP records is necessary.
+   */
+  bool mNeedsUpdatingSdpRecords;
+
+  /**
+   * Set when StopSendingFile() is called.
+   */
+  bool mAbortFlag;
+
+  /**
+   * Set when receiving the first PUT packet of a new file
+   */
+  bool mNewFileFlag;
+
+  /**
+   * Set when receiving a PutFinal packet
+   */
+  bool mPutFinalFlag;
+
+  /**
+   * Set when FileTransferComplete() is called
+   */
+  bool mSendTransferCompleteFlag;
+
+  /**
+   * Set when a transfer is successfully completed.
+   */
+  bool mSuccessFlag;
+
+  /**
+   * True: Receive file (Server)
+   * False: Send file (Client)
+   */
+  bool mIsServer;
+
+  /**
+   * Set when receiving the first PUT packet and wait for
+   * ConfirmReceivingFile() to be called.
+   */
+  bool mWaitingForConfirmationFlag;
+
+  nsString mFileName;
+  nsString mContentType;
+  uint32_t mFileLength;
+  uint32_t mSentFileLength;
+  bool mWaitingToSendPutFinal;
+
+  nsAutoArrayPtr<uint8_t> mBodySegment;
+  nsAutoArrayPtr<uint8_t> mReceivedDataBuffer;
+
+  int mCurrentBlobIndex;
+  nsCOMPtr<nsIDOMBlob> mBlob;
+  nsTArray<SendFileBatch> mBatches;
+
+  /**
+   * A seperate member thread is required because our read calls can block
+   * execution, which is not allowed to happen on the IOThread.
+   */
+  nsCOMPtr<nsIThread> mReadFileThread;
+  nsCOMPtr<nsIOutputStream> mOutputStream;
+  nsCOMPtr<nsIInputStream> mInputStream;
+  nsCOMPtr<nsIVolumeMountLock> mMountLock;
+  nsRefPtr<DeviceStorageFile> mDsFile;
+
+  // If a connection has been established, mSocket will be the socket
+  // communicating with the remote socket. We maintain the invariant that if
+  // mSocket is non-null, mServerSocket must be null (and vice versa).
+  nsRefPtr<BluetoothSocket> mSocket;
+
+  // Server sockets. Once an inbound connection is established, it will hand
+  // over the ownership to mSocket, and get a new server socket while Listen()
+  // is called.
+  nsRefPtr<BluetoothSocket> mServerSocket;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.cpp
@@ -0,0 +1,734 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* 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 "BluetoothSocket.h"
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sock.h>
+#include <sys/socket.h>
+
+#include "base/message_loop.h"
+#include "BluetoothServiceBluedroid.h"
+#include "BluetoothSocketObserver.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/RefPtr.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
+#define FIRST_SOCKET_INFO_MSG_LENGTH 4
+#define TOTAL_SOCKET_INFO_LENGTH 20
+
+using namespace mozilla::ipc;
+USING_BLUETOOTH_NAMESPACE
+
+static const size_t MAX_READ_SIZE = 1 << 16;
+static const uint8_t UUID_OBEX_OBJECT_PUSH[] = {
+  0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+  0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+};
+static const btsock_interface_t* sBluetoothSocketInterface = nullptr;
+
+// helper functions
+static bool
+EnsureBluetoothSocketHalLoad()
+{
+  if (sBluetoothSocketInterface) {
+    return true;
+  }
+
+  const bt_interface_t* btInf = GetBluetoothInterface();
+  NS_ENSURE_TRUE(btInf, false);
+
+  sBluetoothSocketInterface =
+    (btsock_interface_t *) btInf->get_profile_interface(BT_PROFILE_SOCKETS_ID);
+  NS_ENSURE_TRUE(sBluetoothSocketInterface, false);
+
+  return true;
+}
+
+static int16_t
+ReadInt16(const uint8_t* aData, size_t* aOffset)
+{
+  int16_t value = (aData[*aOffset + 1] << 8) | aData[*aOffset];
+
+  *aOffset += 2;
+  return value;
+}
+
+static int32_t
+ReadInt32(const uint8_t* aData, size_t* aOffset)
+{
+  int32_t value = (aData[*aOffset + 3] << 24) |
+                  (aData[*aOffset + 2] << 16) |
+                  (aData[*aOffset + 1] << 8) |
+                  aData[*aOffset];
+  *aOffset += 4;
+  return value;
+}
+
+static void
+ReadBdAddress(const uint8_t* aData, size_t* aOffset, nsAString& aDeviceAddress)
+{
+  char bdstr[18];
+  sprintf(bdstr, "%02x:%02x:%02x:%02x:%02x:%02x",
+          aData[*aOffset], aData[*aOffset + 1], aData[*aOffset + 2],
+          aData[*aOffset + 3], aData[*aOffset + 4], aData[*aOffset + 5]);
+
+  aDeviceAddress.AssignLiteral(bdstr);
+  *aOffset += 6;
+}
+
+class mozilla::dom::bluetooth::DroidSocketImpl
+    : public MessageLoopForIO::Watcher
+{
+public:
+  DroidSocketImpl(BluetoothSocket* aConsumer, int aFd)
+    : mConsumer(aConsumer)
+    , mIOLoop(nullptr)
+    , mFd(aFd)
+    , mShuttingDownOnIOThread(false)
+  {
+  }
+
+  ~DroidSocketImpl()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  void QueueWriteData(UnixSocketRawData* aData)
+  {
+    mOutgoingQ.AppendElement(aData);
+    OnFileCanWriteWithoutBlocking(mFd);
+  }
+
+  bool IsShutdownOnMainThread()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mConsumer == nullptr;
+  }
+
+  bool IsShutdownOnIOThread()
+  {
+    return mShuttingDownOnIOThread;
+  }
+
+  void ShutdownOnMainThread()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(!IsShutdownOnMainThread());
+    mConsumer = nullptr;
+  }
+
+  void ShutdownOnIOThread()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(!mShuttingDownOnIOThread);
+
+    mReadWatcher.StopWatchingFileDescriptor();
+    mWriteWatcher.StopWatchingFileDescriptor();
+
+    mShuttingDownOnIOThread = true;
+  }
+
+  void SetUpIO(bool aWrite)
+  {
+    MOZ_ASSERT(!mIOLoop);
+    MOZ_ASSERT(mFd >= 0);
+    mIOLoop = MessageLoopForIO::current();
+
+    // Set up a read watch
+    mIOLoop->WatchFileDescriptor(mFd,
+                                 true,
+                                 MessageLoopForIO::WATCH_READ,
+                                 &mReadWatcher,
+                                 this);
+
+    if (aWrite) {
+      // Set up a write watch
+      mIOLoop->WatchFileDescriptor(mFd.get(),
+                                   false,
+                                   MessageLoopForIO::WATCH_WRITE,
+                                   &mWriteWatcher,
+                                   this);
+    }
+  }
+
+  void ConnectClientFd()
+  {
+    // Stop current read watch
+    mReadWatcher.StopWatchingFileDescriptor();
+    mIOLoop = nullptr;
+
+    // Restart read & write watch on client fd
+    SetUpIO(true);
+  }
+
+  /**
+   * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
+   * directly from main thread. All non-main-thread accesses should happen with
+   * mImpl as container.
+   */
+  RefPtr<BluetoothSocket> mConsumer;
+
+private:
+  /**
+   * libevent triggered functions that reads data from socket when available and
+   * guarenteed non-blocking. Only to be called on IO thread.
+   *
+   * @param aFd [in] File descriptor to read from
+   */
+  virtual void OnFileCanReadWithoutBlocking(int aFd);
+
+  /**
+   * libevent or developer triggered functions that writes data to socket when
+   * available and guarenteed non-blocking. Only to be called on IO thread.
+   *
+   * @param aFd [in] File descriptor to read from
+   */
+  virtual void OnFileCanWriteWithoutBlocking(int aFd);
+
+  /**
+   * Read message to get data and client fd wrapped in message header
+   *
+   * @param aFd     [in]  File descriptor to read message from
+   * @param aBuffer [out] Data buffer read
+   * @param aLength [out] Number of bytes read
+   */
+  ssize_t ReadMsg(int aFd, void *aBuffer, size_t aLength);
+
+  /**
+   * IO Loop pointer. Must be initalized and called from IO thread only.
+   */
+  MessageLoopForIO* mIOLoop;
+
+  /**
+   * Raw data queue. Must be pushed/popped from IO thread only.
+   */
+  typedef nsTArray<UnixSocketRawData* > UnixSocketRawDataQueue;
+  UnixSocketRawDataQueue mOutgoingQ;
+
+  /**
+   * Read watcher for libevent. Only to be accessed on IO Thread.
+   */
+  MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
+
+  /**
+   * Write watcher for libevent. Only to be accessed on IO Thread.
+   */
+  MessageLoopForIO::FileDescriptorWatcher mWriteWatcher;
+
+  /**
+   * File descriptor to read from/write to. Connection happens on user provided
+   * thread. Read/write/close happens on IO thread.
+   */
+  mozilla::ScopedClose mFd;
+
+  /**
+   * If true, do not requeue whatever task we're running
+   */
+  bool mShuttingDownOnIOThread;
+};
+
+template<class T>
+class DeleteInstanceRunnable : public nsRunnable
+{
+public:
+  DeleteInstanceRunnable(T* aInstance)
+  : mInstance(aInstance)
+  { }
+
+  NS_IMETHOD Run()
+  {
+    delete mInstance;
+
+    return NS_OK;
+  }
+
+private:
+  T* mInstance;
+};
+
+class RequestClosingSocketTask : public nsRunnable
+{
+public:
+  RequestClosingSocketTask(DroidSocketImpl* aImpl) : mImpl(aImpl)
+  {
+    MOZ_ASSERT(aImpl);
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mImpl->IsShutdownOnMainThread()) {
+      NS_WARNING("CloseSocket has already been called!");
+      // Since we've already explicitly closed and the close happened before
+      // this, this isn't really an error. Since we've warned, return OK.
+      return NS_OK;
+    }
+
+    // Start from here, same handling flow as calling CloseSocket() from
+    // upper layer
+    mImpl->mConsumer->CloseDroidSocket();
+    return NS_OK;
+  }
+private:
+  DroidSocketImpl* mImpl;
+};
+
+class ShutdownSocketTask : public Task {
+  virtual void Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    // At this point, there should be no new events on the IO thread after this
+    // one with the possible exception of a SocketAcceptTask that
+    // ShutdownOnIOThread will cancel for us. We are now fully shut down, so we
+    // can send a message to the main thread that will delete mImpl safely knowing
+    // that no more tasks reference it.
+    mImpl->ShutdownOnIOThread();
+
+    nsRefPtr<nsIRunnable> t(new DeleteInstanceRunnable<
+                                  mozilla::dom::bluetooth::DroidSocketImpl>(mImpl));
+    nsresult rv = NS_DispatchToMainThread(t);
+    NS_ENSURE_SUCCESS_VOID(rv);
+  }
+
+  DroidSocketImpl* mImpl;
+
+public:
+  ShutdownSocketTask(DroidSocketImpl* aImpl) : mImpl(aImpl) { }
+};
+
+class SocketReceiveTask : public nsRunnable
+{
+public:
+  SocketReceiveTask(DroidSocketImpl* aImpl, UnixSocketRawData* aData) :
+    mImpl(aImpl),
+    mRawData(aData)
+  {
+    MOZ_ASSERT(aImpl);
+    MOZ_ASSERT(aData);
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    if (mImpl->IsShutdownOnMainThread()) {
+      NS_WARNING("mConsumer is null, aborting receive!");
+      // Since we've already explicitly closed and the close happened before
+      // this, this isn't really an error. Since we've warned, return OK.
+      return NS_OK;
+    }
+
+    MOZ_ASSERT(mImpl->mConsumer);
+    mImpl->mConsumer->ReceiveSocketData(mRawData);
+    return NS_OK;
+  }
+private:
+  DroidSocketImpl* mImpl;
+  nsAutoPtr<UnixSocketRawData> mRawData;
+};
+
+class SocketSendTask : public Task
+{
+public:
+  SocketSendTask(BluetoothSocket* aConsumer, DroidSocketImpl* aImpl,
+                 UnixSocketRawData* aData)
+    : mConsumer(aConsumer),
+      mImpl(aImpl),
+      mData(aData)
+  {
+    MOZ_ASSERT(aConsumer);
+    MOZ_ASSERT(aImpl);
+    MOZ_ASSERT(aData);
+  }
+
+  void
+  Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(!mImpl->IsShutdownOnIOThread());
+
+    mImpl->QueueWriteData(mData);
+  }
+
+private:
+  nsRefPtr<BluetoothSocket> mConsumer;
+  DroidSocketImpl* mImpl;
+  UnixSocketRawData* mData;
+};
+
+class SocketSetUpIOTask : public Task
+{
+  virtual void Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    mImpl->SetUpIO(mWrite);
+  }
+
+  DroidSocketImpl* mImpl;
+  bool mWrite;
+public:
+  SocketSetUpIOTask(DroidSocketImpl* aImpl, bool aWrite)
+  : mImpl(aImpl), mWrite(aWrite) { }
+};
+
+class SocketConnectClientFdTask : public Task
+{
+  virtual void Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    mImpl->ConnectClientFd();
+  }
+
+  DroidSocketImpl* mImpl;
+public:
+  SocketConnectClientFdTask(DroidSocketImpl* aImpl) : mImpl(aImpl) { }
+};
+
+ssize_t
+DroidSocketImpl::ReadMsg(int aFd, void *aBuffer, size_t aLength)
+{
+  ssize_t ret;
+  struct msghdr msg;
+  struct iovec iv;
+  struct cmsghdr cmsgbuf[2 * sizeof(cmsghdr) + 0x100];
+
+  memset(&msg, 0, sizeof(msg));
+  memset(&iv, 0, sizeof(iv));
+
+  iv.iov_base = (unsigned char *)aBuffer;
+  iv.iov_len = aLength;
+
+  msg.msg_iov = &iv;
+  msg.msg_iovlen = 1;
+  msg.msg_control = cmsgbuf;
+  msg.msg_controllen = sizeof(cmsgbuf);
+
+  ret = recvmsg(mFd.get(), &msg, MSG_NOSIGNAL);
+  if (ret < 0 && errno == EPIPE) {
+    // Treat this as an end of stream
+    return 0;
+  }
+
+  NS_ENSURE_FALSE(ret < 0, -1);
+  NS_ENSURE_FALSE(msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE), -1);
+
+  // Extract client fd from message header
+  for (struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg);
+       cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
+    if (cmsgptr->cmsg_level != SOL_SOCKET) {
+      continue;
+    }
+    if (cmsgptr->cmsg_type == SCM_RIGHTS) {
+      int *pDescriptors = (int *)CMSG_DATA(cmsgptr);
+      // Overwrite fd with client fd
+      mFd.reset(pDescriptors[0]);
+      break;
+    }
+  }
+
+  return ret;
+}
+
+void
+DroidSocketImpl::OnFileCanReadWithoutBlocking(int aFd)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!mShuttingDownOnIOThread);
+
+  // Read all of the incoming data.
+  while (true) {
+    nsAutoPtr<UnixSocketRawData> incoming(new UnixSocketRawData(MAX_READ_SIZE));
+
+    ssize_t ret;
+    if (!mConsumer->IsWaitingForClientFd()) {
+      ret = read(aFd, incoming->mData, incoming->mSize);
+    } else {
+      ret = ReadMsg(aFd, incoming->mData, incoming->mSize);
+    }
+
+    if (ret <= 0) {
+      if (ret == -1) {
+        if (errno == EINTR) {
+          continue; // retry system call when interrupted
+        }
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+          return; // no data available: return and re-poll
+        }
+
+        BT_WARNING("Cannot read from network");
+        // else fall through to error handling on other errno's
+      }
+
+      // We're done with our descriptors. Ensure that spurious events don't
+      // cause us to end up back here.
+      mReadWatcher.StopWatchingFileDescriptor();
+      mWriteWatcher.StopWatchingFileDescriptor();
+      nsRefPtr<RequestClosingSocketTask> t = new RequestClosingSocketTask(this);
+      NS_DispatchToMainThread(t);
+      return;
+    }
+
+    incoming->mSize = ret;
+    nsRefPtr<SocketReceiveTask> t =
+      new SocketReceiveTask(this, incoming.forget());
+    NS_DispatchToMainThread(t);
+
+    // If ret is less than MAX_READ_SIZE, there's no
+    // more data in the socket for us to read now.
+    if (ret < ssize_t(MAX_READ_SIZE)) {
+      return;
+    }
+  }
+
+  MOZ_CRASH("We returned early");
+}
+
+void
+DroidSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!mShuttingDownOnIOThread);
+  MOZ_ASSERT(aFd >= 0);
+
+  // Try to write the bytes of mCurrentRilRawData.  If all were written, continue.
+  //
+  // Otherwise, save the byte position of the next byte to write
+  // within mCurrentWriteOffset, and request another write when the
+  // system won't block.
+  //
+  while (true) {
+    UnixSocketRawData* data;
+    if (mOutgoingQ.IsEmpty()) {
+      return;
+    }
+    data = mOutgoingQ.ElementAt(0);
+    const uint8_t *toWrite;
+    toWrite = data->mData;
+
+    while (data->mCurrentWriteOffset < data->mSize) {
+      ssize_t write_amount = data->mSize - data->mCurrentWriteOffset;
+      ssize_t written;
+      written = write (aFd, toWrite + data->mCurrentWriteOffset,
+                       write_amount);
+      if (written > 0) {
+        data->mCurrentWriteOffset += written;
+      }
+      if (written != write_amount) {
+        break;
+      }
+    }
+
+    if (data->mCurrentWriteOffset != data->mSize) {
+      MessageLoopForIO::current()->WatchFileDescriptor(
+        aFd,
+        false,
+        MessageLoopForIO::WATCH_WRITE,
+        &mWriteWatcher,
+        this);
+      return;
+    }
+    mOutgoingQ.RemoveElementAt(0);
+    delete data;
+  }
+}
+
+BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
+                                 BluetoothSocketType aType,
+                                 bool aAuth,
+                                 bool aEncrypt)
+  : mObserver(aObserver)
+  , mImpl(nullptr)
+  , mAuth(aAuth)
+  , mEncrypt(aEncrypt)
+  , mReceivedSocketInfoLength(0)
+{
+  MOZ_ASSERT(aObserver);
+
+  EnsureBluetoothSocketHalLoad();
+  mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
+}
+
+void
+BluetoothSocket::CloseDroidSocket()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mImpl) {
+    return;
+  }
+
+  // From this point on, we consider mImpl as being deleted.
+  // We sever the relationship here so any future calls to listen or connect
+  // will create a new implementation.
+  mImpl->ShutdownOnMainThread();
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new ShutdownSocketTask(mImpl));
+  mImpl = nullptr;
+
+  OnDisconnect();
+}
+
+bool
+BluetoothSocket::CreateDroidSocket(int aFd)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_FALSE(mImpl, false);
+
+  mImpl = new DroidSocketImpl(this, aFd);
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new SocketSetUpIOTask(mImpl, !mIsServer));
+
+  return true;
+}
+
+bool
+BluetoothSocket::Connect(const nsAString& aDeviceAddress, int aChannel)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!aDeviceAddress.IsEmpty());
+  NS_ENSURE_TRUE(sBluetoothSocketInterface, false);
+
+  bt_bdaddr_t remoteBdAddress;
+  StringToBdAddressType(aDeviceAddress, &remoteBdAddress);
+
+  // TODO: uuid as argument
+  int fd;
+  NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
+    sBluetoothSocketInterface->connect((bt_bdaddr_t *) &remoteBdAddress,
+                                       (btsock_type_t) BTSOCK_RFCOMM,
+                                       UUID_OBEX_OBJECT_PUSH,
+                                       aChannel, &fd, (mAuth << 1) | mEncrypt),
+    false);
+  NS_ENSURE_TRUE(fd >= 0, false);
+
+  mIsServer = false;
+  return CreateDroidSocket(fd);
+}
+
+bool
+BluetoothSocket::Listen(int aChannel)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_TRUE(sBluetoothSocketInterface, false);
+
+  // TODO: uuid and service name as arguments
+  nsAutoCString serviceName("OBEX Object Push");
+  int fd;
+  NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
+    sBluetoothSocketInterface->listen((btsock_type_t) BTSOCK_RFCOMM,
+                                      serviceName.get(),
+                                      UUID_OBEX_OBJECT_PUSH,
+                                      aChannel, &fd, (mAuth << 1) | mEncrypt),
+    false);
+  NS_ENSURE_TRUE(fd >= 0, false);
+
+  mIsServer = true;
+  return CreateDroidSocket(fd);
+}
+
+bool
+BluetoothSocket::SendDroidSocketData(UnixSocketRawData* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_TRUE(mImpl, false);
+
+  MOZ_ASSERT(!mImpl->IsShutdownOnMainThread());
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new SocketSendTask(this, mImpl, aData));
+  return true;
+}
+
+bool
+BluetoothSocket::IsWaitingForClientFd()
+{
+  return (mIsServer &&
+          mReceivedSocketInfoLength == FIRST_SOCKET_INFO_MSG_LENGTH);
+}
+
+bool
+BluetoothSocket::ReceiveSocketInfo(nsAutoPtr<UnixSocketRawData>& aMessage)
+{
+  /**
+   * 2 socket info messages (20 bytes) to receive at the beginning:
+   * - 1st message: [channel:4]
+   * - 2nd message: [size:2][bd address:6][channel:4][connection status:4]
+   */
+  if (mReceivedSocketInfoLength >= TOTAL_SOCKET_INFO_LENGTH) {
+    // We've got both socket info messages
+    return false;
+  }
+  mReceivedSocketInfoLength += aMessage->mSize;
+
+  size_t offset = 0;
+  if (mReceivedSocketInfoLength == FIRST_SOCKET_INFO_MSG_LENGTH) {
+    // 1st message: [channel:4]
+    int32_t channel = ReadInt32(aMessage->mData, &offset);
+
+    BT_LOGR("%s: channel %d", __FUNCTION__, channel);
+  } else if (mReceivedSocketInfoLength == TOTAL_SOCKET_INFO_LENGTH) {
+    // 2nd message: [size:2][bd address:6][channel:4][connection status:4]
+    int16_t size = ReadInt16(aMessage->mData, &offset);
+    ReadBdAddress(aMessage->mData, &offset, mDeviceAddress);
+    int32_t channel = ReadInt32(aMessage->mData, &offset);
+    int32_t connectionStatus = ReadInt32(aMessage->mData, &offset);
+
+    BT_LOGR("%s: size %d channel %d remote addr %s status %d", __FUNCTION__,
+      size, channel, NS_ConvertUTF16toUTF8(mDeviceAddress).get(), connectionStatus);
+
+    if (connectionStatus != 0) {
+      OnConnectError();
+      return true;
+    }
+
+    if (mIsServer) {
+      // Connect client fd on IO thread
+      XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                       new SocketConnectClientFdTask(mImpl));
+    }
+    OnConnectSuccess();
+  }
+
+  return true;
+}
+
+void
+BluetoothSocket::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage)
+{
+  if (ReceiveSocketInfo(aMessage)) {
+    return;
+  }
+
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mObserver);
+  mObserver->ReceiveSocketData(this, aMessage);
+}
+
+void
+BluetoothSocket::OnConnectSuccess()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mObserver);
+  mObserver->OnSocketConnectSuccess(this);
+}
+
+void
+BluetoothSocket::OnConnectError()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mObserver);
+  mObserver->OnSocketConnectError(this);
+}
+
+void
+BluetoothSocket::OnDisconnect()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mObserver);
+  mObserver->OnSocketDisconnect(this);
+}
+
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.h
@@ -0,0 +1,87 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* 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_dom_bluetooth_BluetoothSocket_h
+#define mozilla_dom_bluetooth_BluetoothSocket_h
+
+#include "BluetoothCommon.h"
+#include "mozilla/ipc/UnixSocket.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothSocketObserver;
+class DroidSocketImpl;
+
+class BluetoothSocket : public mozilla::ipc::UnixSocketConsumer
+{
+public:
+  BluetoothSocket(BluetoothSocketObserver* aObserver,
+                  BluetoothSocketType aType,
+                  bool aAuth,
+                  bool aEncrypt);
+
+  /**
+   * Connect to remote server as a client.
+   *
+   * The steps are as following:
+   * 1) BluetoothSocket acquires fd from bluedroid, and creates
+   *    a DroidSocketImpl to watch read/write of the fd.
+   * 2) DroidSocketImpl receives first 2 messages to get socket info.
+   * 3) Obex client session starts.
+   */
+  bool Connect(const nsAString& aDeviceAddress, int aChannel);
+
+  /**
+   * Listen to incoming connection as a server.
+   *
+   * The steps are as following:
+   * 1) BluetoothSocket acquires fd from bluedroid, and creates
+   *    a DroidSocketImpl to watch read of the fd. DroidSocketImpl
+   *    receives the 1st message immediately.
+   * 2) When there's incoming connection, DroidSocketImpl receives
+   *    2nd message to get socket info and client fd.
+   * 3) DroidSocketImpl stops watching read of original fd and
+   *    starts to watch read/write of client fd.
+   * 4) Obex server session starts.
+   */
+  bool Listen(int aChannel);
+
+  inline void Disconnect()
+  {
+    CloseDroidSocket();
+  }
+
+  virtual void OnConnectSuccess() MOZ_OVERRIDE;
+  virtual void OnConnectError() MOZ_OVERRIDE;
+  virtual void OnDisconnect() MOZ_OVERRIDE;
+  virtual void ReceiveSocketData(
+    nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage) MOZ_OVERRIDE;
+
+  inline void GetAddress(nsAString& aDeviceAddress)
+  {
+    aDeviceAddress = mDeviceAddress;
+  }
+
+  void CloseDroidSocket();
+  bool IsWaitingForClientFd();
+  bool SendDroidSocketData(mozilla::ipc::UnixSocketRawData* aData);
+
+private:
+  BluetoothSocketObserver* mObserver;
+  DroidSocketImpl* mImpl;
+  nsString mDeviceAddress;
+  bool mAuth;
+  bool mEncrypt;
+  bool mIsServer;
+  int mReceivedSocketInfoLength;
+
+  bool CreateDroidSocket(int aFd);
+  bool ReceiveSocketInfo(nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage);
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
--- a/dom/bluetooth/bluedroid/gonk/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/gonk/BluetoothServiceBluedroid.cpp
@@ -15,18 +15,19 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 
 #include "BluetoothServiceBluedroid.h"
 
 #include <hardware/hardware.h>
 
-#include "bluedroid/BluetoothA2dpManager.h"
-#include "bluedroid/BluetoothHfpManager.h"
+#include "BluetoothA2dpManager.h"
+#include "BluetoothHfpManager.h"
+#include "BluetoothOppManager.h"
 #include "BluetoothProfileController.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothUtils.h"
 #include "BluetoothUuid.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
@@ -110,16 +111,22 @@ public:
     // Try to fire event 'AdapterAdded' to fit the original behaviour when
     // we used BlueZ as backend.
     BluetoothService* bs = BluetoothService::Get();
     NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
 
     bs->AdapterAddedReceived();
     bs->TryFiringAdapterAdded();
 
+    // Trigger BluetoothOppManager to listen
+    BluetoothOppManager* opp = BluetoothOppManager::Get();
+    if (!opp || !opp->Listen()) {
+      BT_LOGR("%s: Fail to start BluetoothOppManager listening", __FUNCTION__);
+    }
+
     return NS_OK;
   }
 };
 
 /**
  *  Static callback functions
  */
 static void
@@ -1285,32 +1292,75 @@ BluetoothServiceBluedroid::Disconnect(
 }
 
 void
 BluetoothServiceBluedroid::SendF