Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sat, 29 Oct 2016 09:11:45 -0400
changeset 320143 16cdd6273c48ea8e7dbc99e343a50b9ca9789715
parent 319963 1561c917ee27c3ea04bd69467e5b8c7c08102f2a (current diff)
parent 320142 2ac285d70ab25253dd2f1822f23163b42e21e4a2 (diff)
child 320144 dc422956242bacfbf88d716f5b967d2c985b913b
push id20749
push userryanvm@gmail.com
push dateSat, 29 Oct 2016 13:21:21 +0000
treeherderfx-team@1b170b39ed6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
Merge inbound to m-c. a=merge
dom/battery/test/marionette/manifest.ini
dom/battery/test/marionette/test_battery_level.js
dom/battery/test/marionette/test_battery_status_charging.js
dom/battery/test/marionette/test_battery_status_discharging.js
dom/battery/test/marionette/test_battery_status_full.js
dom/battery/test/marionette/test_battery_status_not_charging.js
dom/battery/test/marionette/test_battery_status_unknown.js
dom/browser-element/mochitest/test_browserElement_oop_BrowserWindowNamespace.html
dom/media/test/test_eme_persistent_sessions.html
gfx/angle/src/compiler/translator/RenameFunction.h
gfx/angle/src/compiler/translator/depgraph/DependencyGraph.cpp
gfx/angle/src/compiler/translator/depgraph/DependencyGraph.h
gfx/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.cpp
gfx/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h
gfx/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp
gfx/angle/src/compiler/translator/depgraph/DependencyGraphOutput.h
gfx/angle/src/compiler/translator/depgraph/DependencyGraphTraverse.cpp
gfx/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp
gfx/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.h
gfx/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.cpp
gfx/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h
gfx/angle/src/tests/compiler_tests/BuiltInFunctionEmulator_test.cpp
js/src/jit-test/tests/wasm/spec/grow-memory.wast
js/src/jit-test/tests/wasm/spec/grow-memory.wast.js
js/src/jit-test/tests/wasm/spec/nan-propagation.wast
js/src/jit-test/tests/wasm/spec/nan-propagation.wast.js
testing/web-platform/meta/IndexedDB/interfaces.worker.js.ini
testing/web-platform/meta/html/browsers/the-window-object/named-access-on-the-window-object/window-null-names.html.ini
--- a/accessible/atk/UtilInterface.cpp
+++ b/accessible/atk/UtilInterface.cpp
@@ -256,18 +256,24 @@ mai_util_add_key_event_listener(AtkKeySn
   if (MOZ_UNLIKELY(!listener)) {
     return 0;
   }
 
   static guint key = 0;
 
   if (!sKey_listener_list) {
     sKey_listener_list = g_hash_table_new(nullptr, nullptr);
+  }
+
+  // If we have no registered event listeners then we need to (re)install the
+  // key event snooper.
+  if (g_hash_table_size(sKey_listener_list) == 0) {
     sKey_snooper_id = gtk_key_snooper_install(mai_key_snooper, data);
   }
+
   AtkKeySnoopFuncPointer atkKeySnoop;
   atkKeySnoop.func_ptr = listener;
   key++;
   g_hash_table_insert(sKey_listener_list, GUINT_TO_POINTER(key),
                       atkKeySnoop.data);
   return key;
 }
 
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -508,18 +508,25 @@ DocManager::CreateDocOrRootAccessible(ns
         nsCOMPtr<nsITabChild> tabChild = docShell->GetTabChild();
 
         // XXX We may need to handle the case that we don't have a tab child
         // differently.  It may be that this will cause us to fail to notify
         // the parent process about important accessible documents.
         if (tabChild) {
           DocAccessibleChild* ipcDoc = new DocAccessibleChild(docAcc);
           docAcc->SetIPCDoc(ipcDoc);
+
           static_cast<TabChild*>(tabChild.get())->
-            SendPDocAccessibleConstructor(ipcDoc, nullptr, 0);
+            SendPDocAccessibleConstructor(ipcDoc, nullptr, 0,
+#if defined(XP_WIN)
+                                          AccessibleWrap::GetChildIDFor(docAcc)
+#else
+                                          0
+#endif
+                                          );
 
 #if defined(XP_WIN)
           IAccessibleHolder holder(CreateHolderFromAccessible(docAcc));
           ipcDoc->SendCOMProxy(holder);
 #endif
         }
       }
     }
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -417,22 +417,25 @@ NotificationController::WillRefresh(mozi
         continue;
       }
 
       ipcDoc = new DocAccessibleChild(childDoc);
       childDoc->SetIPCDoc(ipcDoc);
       nsCOMPtr<nsITabChild> tabChild =
         do_GetInterface(mDocument->DocumentNode()->GetDocShell());
       if (tabChild) {
+        MOZ_ASSERT(parentIPCDoc);
         static_cast<TabChild*>(tabChild.get())->
-          SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id);
+          SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id,
 #if defined(XP_WIN)
-        MOZ_ASSERT(parentIPCDoc);
-        ipcDoc->SendMsaaID(AccessibleWrap::GetChildIDFor(childDoc));
+                                        AccessibleWrap::GetChildIDFor(childDoc)
+#else
+                                        0
 #endif
+                                        );
       }
     }
   }
 
   mObservingState = eRefreshObserving;
   if (!mDocument)
     return;
 
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -39,16 +39,17 @@
 #include "xpcAccessibleDocument.h"
 
 #ifdef MOZ_ACCESSIBILITY_ATK
 #include "AtkSocketAccessible.h"
 #endif
 
 #ifdef XP_WIN
 #include "mozilla/a11y/Compatibility.h"
+#include "mozilla/dom/ContentChild.h"
 #include "HTMLWin32ObjectAccessible.h"
 #include "mozilla/StaticPtr.h"
 #endif
 
 #ifdef A11Y_LOG
 #include "Logging.h"
 #endif
 
@@ -1259,20 +1260,33 @@ nsAccessibilityService::Init()
 
 #ifdef A11Y_LOG
   logging::CheckEnv();
 #endif
 
   gAccessibilityService = this;
   NS_ADDREF(gAccessibilityService); // will release in Shutdown()
 
-  if (XRE_IsParentProcess())
+  if (XRE_IsParentProcess()) {
     gApplicationAccessible = new ApplicationAccessibleWrap();
-  else
+  } else {
+#if defined(XP_WIN)
+    dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+    MOZ_ASSERT(contentChild);
+    // If we were instantiated by the chrome process, GetMsaaID() will return
+    // a non-zero value and we may safely continue with initialization.
+    if (!contentChild->GetMsaaID()) {
+      // Since we were not instantiated by chrome, we need to synchronously
+      // obtain a MSAA content process id.
+      contentChild->SendGetA11yContentId();
+    }
+#endif // defined(XP_WIN)
+
     gApplicationAccessible = new ApplicationAccessible();
+  }
 
   NS_ADDREF(gApplicationAccessible); // will release in Shutdown()
   gApplicationAccessible->Init();
 
 #ifdef MOZ_CRASHREPORTER
   CrashReporter::
     AnnotateCrashReport(NS_LITERAL_CSTRING("Accessibility"),
                         NS_LITERAL_CSTRING("Active"));
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -455,45 +455,33 @@ DocAccessibleParent::GetXPCAccessible(Pr
   xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
   MOZ_ASSERT(doc);
 
   return doc->GetXPCAccessible(aProxy);
 }
 
 #if defined(XP_WIN)
 /**
- * @param aMsaaID The MSAA ID that was generated by content that the chrome
- *        process should assign to this DocAccessibleParent.
  * @param aCOMProxy COM Proxy to the document in the content process.
  * @param aParentCOMProxy COM Proxy to the OuterDocAccessible that is
  *        the parent of the document. The content process will use this
  *        proxy when traversing up across the content/chrome boundary.
  */
 bool
-DocAccessibleParent::RecvCOMProxy(const int32_t& aMsaaID,
-                                  const IAccessibleHolder& aCOMProxy,
+DocAccessibleParent::RecvCOMProxy(const IAccessibleHolder& aCOMProxy,
                                   IAccessibleHolder* aParentCOMProxy)
 {
-  WrapperFor(this)->SetID(aMsaaID);
-
   RefPtr<IAccessible> ptr(aCOMProxy.Get());
   SetCOMInterface(ptr);
 
   Accessible* outerDoc = OuterDocOfRemoteBrowser();
   IAccessible* rawNative = nullptr;
   if (outerDoc) {
     outerDoc->GetNativeInterface((void**) &rawNative);
   }
 
   aParentCOMProxy->Set(IAccessibleHolder::COMPtrType(rawNative));
   return true;
 }
-
-bool
-DocAccessibleParent::RecvMsaaID(const int32_t& aMsaaID)
-{
-  WrapperFor(this)->SetID(aMsaaID);
-  return true;
-}
 #endif // defined(XP_WIN)
 
 } // a11y
 } // mozilla
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -139,21 +139,18 @@ public:
   const ProxyAccessible* GetAccessible(uintptr_t aID) const
     { return const_cast<DocAccessibleParent*>(this)->GetAccessible(aID); }
 
   size_t ChildDocCount() const { return mChildDocs.Length(); }
   const DocAccessibleParent* ChildDocAt(size_t aIdx) const
     { return mChildDocs[aIdx]; }
 
 #if defined(XP_WIN)
-  virtual bool RecvCOMProxy(const int32_t& aMsaaID,
-                            const IAccessibleHolder& aCOMProxy,
+  virtual bool RecvCOMProxy(const IAccessibleHolder& aCOMProxy,
                             IAccessibleHolder* aParentCOMProxy) override;
-
-  virtual bool RecvMsaaID(const int32_t& aMsaaID) override;
 #endif
 
 private:
 
   class ProxyEntry : public PLDHashEntryHdr
   {
   public:
     explicit ProxyEntry(const void*) : mProxy(nullptr) {}
--- a/accessible/ipc/win/DocAccessibleChild.cpp
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -28,17 +28,16 @@ DocAccessibleChild::DocAccessibleChild(D
 DocAccessibleChild::~DocAccessibleChild()
 {
   MOZ_COUNT_DTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
 }
 
 void
 DocAccessibleChild::SendCOMProxy(const IAccessibleHolder& aProxy)
 {
-  int32_t msaaID = AccessibleWrap::GetChildIDFor(mDoc);
   IAccessibleHolder parentProxy;
-  PDocAccessibleChild::SendCOMProxy(msaaID, aProxy, &parentProxy);
+  PDocAccessibleChild::SendCOMProxy(aProxy, &parentProxy);
   mParentProxy.reset(parentProxy.Release());
 }
 
 } // namespace a11y
 } // namespace mozilla
 
--- a/accessible/ipc/win/PDocAccessible.ipdl
+++ b/accessible/ipc/win/PDocAccessible.ipdl
@@ -58,19 +58,17 @@ parent:
   /*
    * Tell the parent document to bind the existing document as a new child
    * document.
    */
   async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
 
   // For now we'll add the command to send the proxy here. This might move to
   // PDocAccessible constructor in PBrowser.
-  sync COMProxy(int32_t aMsaaID, IAccessibleHolder aDocCOMProxy)
+  sync COMProxy(IAccessibleHolder aDocCOMProxy)
     returns(IAccessibleHolder aParentCOMProxy);
 
-  async MsaaID(int32_t aMsaaID);
-
 child:
   async __delete__();
 };
 
 }
 }
--- a/accessible/ipc/win/ProxyAccessible.cpp
+++ b/accessible/ipc/win/ProxyAccessible.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "Accessible2.h"
 #include "ProxyAccessible.h"
+#include "ia2AccessibleValue.h"
 #include "mozilla/a11y/DocAccessibleParent.h"
 #include "DocAccessible.h"
 #include "mozilla/a11y/DocManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/Unused.h"
 #include "mozilla/a11y/Platform.h"
 #include "RelationType.h"
@@ -44,16 +45,55 @@ ProxyAccessible::GetCOMInterface(void** 
     thisPtr->mCOMProxy = wrap->GetIAccessibleFor(realId, &isDefunct);
   }
 
   RefPtr<IAccessible> addRefed = mCOMProxy;
   addRefed.forget(aOutAccessible);
   return !!mCOMProxy;
 }
 
+/**
+ * Specializations of this template map an IAccessible type to its IID
+ */
+template<typename Interface> struct InterfaceIID {};
+
+template<>
+struct InterfaceIID<IAccessibleValue>
+{
+  static REFIID Value() { return IID_IAccessibleValue; }
+};
+
+template<>
+struct InterfaceIID<IAccessibleText>
+{
+  static REFIID Value() { return IID_IAccessibleText; }
+};
+
+/**
+ * Get the COM proxy for this proxy accessible and QueryInterface it with the
+ * correct IID
+ */
+template<typename Interface>
+static already_AddRefed<Interface>
+QueryInterface(const ProxyAccessible* aProxy)
+{
+  RefPtr<IAccessible> acc;
+  if (!aProxy->GetCOMInterface((void**)getter_AddRefs(acc))) {
+    return nullptr;
+  }
+
+  RefPtr<Interface> acc2;
+  if (FAILED(acc->QueryInterface(InterfaceIID<Interface>::Value(),
+                                 (void**)getter_AddRefs(acc2)))) {
+    return nullptr;
+  }
+
+  return acc2.forget();
+}
+
 void
 ProxyAccessible::Name(nsString& aName) const
 {
   aName.Truncate();
   RefPtr<IAccessible> acc;
   if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
     return;
   }
@@ -272,10 +312,288 @@ ProxyAccessible::Attributes(nsTArray<Att
     return;
   }
 
   ConvertBSTRAttributesToArray(nsDependentString((wchar_t*)attrs,
                                                  attrsWrap.length()),
                                aAttrs);
 }
 
+double
+ProxyAccessible::CurValue()
+{
+  RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
+  if (!acc) {
+    return UnspecifiedNaN<double>();
+  }
+
+  VARIANT currentValue;
+  HRESULT hr = acc->get_currentValue(&currentValue);
+  if (FAILED(hr) || currentValue.vt != VT_R8) {
+    return UnspecifiedNaN<double>();
+  }
+
+  return currentValue.dblVal;
+}
+
+bool
+ProxyAccessible::SetCurValue(double aValue)
+{
+  RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
+  if (!acc) {
+    return false;
+  }
+
+  VARIANT currentValue;
+  VariantInit(&currentValue);
+  currentValue.vt = VT_R8;
+  currentValue.dblVal = aValue;
+  HRESULT hr = acc->setCurrentValue(currentValue);
+  return SUCCEEDED(hr);
+}
+
+double
+ProxyAccessible::MinValue()
+{
+  RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
+  if (!acc) {
+    return UnspecifiedNaN<double>();
+  }
+
+  VARIANT minimumValue;
+  HRESULT hr = acc->get_minimumValue(&minimumValue);
+  if (FAILED(hr) || minimumValue.vt != VT_R8) {
+    return UnspecifiedNaN<double>();
+  }
+
+  return minimumValue.dblVal;
+}
+
+double
+ProxyAccessible::MaxValue()
+{
+  RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
+  if (!acc) {
+    return UnspecifiedNaN<double>();
+  }
+
+  VARIANT maximumValue;
+  HRESULT hr = acc->get_maximumValue(&maximumValue);
+  if (FAILED(hr) || maximumValue.vt != VT_R8) {
+    return UnspecifiedNaN<double>();
+  }
+
+  return maximumValue.dblVal;
+}
+
+static IA2TextBoundaryType
+GetIA2TextBoundary(AccessibleTextBoundary aGeckoBoundaryType)
+{
+  switch (aGeckoBoundaryType) {
+    case nsIAccessibleText::BOUNDARY_CHAR:
+      return IA2_TEXT_BOUNDARY_CHAR;
+    case nsIAccessibleText::BOUNDARY_WORD_START:
+      return IA2_TEXT_BOUNDARY_WORD;
+    case nsIAccessibleText::BOUNDARY_LINE_START:
+      return IA2_TEXT_BOUNDARY_LINE;
+    default:
+      MOZ_RELEASE_ASSERT(false);
+  }
+}
+
+bool
+ProxyAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOffset,
+                               nsString& aText) const
+{
+  RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+  if (!acc) {
+    return false;
+  }
+
+  BSTR result;
+  HRESULT hr = acc->get_text(static_cast<long>(aStartOffset),
+                             static_cast<long>(aEndOffset), &result);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  _bstr_t resultWrap(result, false);
+  aText = (wchar_t*)result;
+
+  return true;
+}
+
+void
+ProxyAccessible::GetTextBeforeOffset(int32_t aOffset,
+                                    AccessibleTextBoundary aBoundaryType,
+                                    nsString& aText, int32_t* aStartOffset,
+                                    int32_t* aEndOffset)
+{
+  RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+  if (!acc) {
+    return;
+  }
+
+  BSTR result;
+  long start, end;
+  HRESULT hr = acc->get_textBeforeOffset(aOffset,
+                                         GetIA2TextBoundary(aBoundaryType),
+                                         &start, &end, &result);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  _bstr_t resultWrap(result, false);
+  *aStartOffset = start;
+  *aEndOffset = end;
+  aText = (wchar_t*)result;
+}
+
+void
+ProxyAccessible::GetTextAfterOffset(int32_t aOffset,
+                                    AccessibleTextBoundary aBoundaryType,
+                                    nsString& aText, int32_t* aStartOffset,
+                                    int32_t* aEndOffset)
+{
+  RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+  if (!acc) {
+    return;
+  }
+
+  BSTR result;
+  long start, end;
+  HRESULT hr = acc->get_textAfterOffset(aOffset,
+                                        GetIA2TextBoundary(aBoundaryType),
+                                        &start, &end, &result);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  _bstr_t resultWrap(result, false);
+  aText = (wchar_t*)result;
+  *aStartOffset = start;
+  *aEndOffset = end;
+}
+
+void
+ProxyAccessible::GetTextAtOffset(int32_t aOffset,
+                                    AccessibleTextBoundary aBoundaryType,
+                                    nsString& aText, int32_t* aStartOffset,
+                                    int32_t* aEndOffset)
+{
+  RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+  if (!acc) {
+    return;
+  }
+
+  BSTR result;
+  long start, end;
+  HRESULT hr = acc->get_textAtOffset(aOffset, GetIA2TextBoundary(aBoundaryType),
+                                     &start, &end, &result);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  _bstr_t resultWrap(result, false);
+  aText = (wchar_t*)result;
+  *aStartOffset = start;
+  *aEndOffset = end;
+}
+
+bool
+ProxyAccessible::AddToSelection(int32_t aStartOffset, int32_t aEndOffset)
+{
+  RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+  if (!acc) {
+    return false;
+  }
+
+  return SUCCEEDED(acc->addSelection(static_cast<long>(aStartOffset),
+                                     static_cast<long>(aEndOffset)));
+}
+
+bool
+ProxyAccessible::RemoveFromSelection(int32_t aSelectionNum)
+{
+  RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+  if (!acc) {
+    return false;
+  }
+
+  return SUCCEEDED(acc->removeSelection(static_cast<long>(aSelectionNum)));
+}
+
+int32_t
+ProxyAccessible::CaretOffset()
+{
+  RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+  if (!acc) {
+    return -1;
+  }
+
+  long offset;
+  HRESULT hr = acc->get_caretOffset(&offset);
+  if (FAILED(hr)) {
+    return -1;
+  }
+
+  return static_cast<int32_t>(offset);
+}
+
+void
+ProxyAccessible::SetCaretOffset(int32_t aOffset)
+{
+  RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+  if (!acc) {
+    return;
+  }
+
+  acc->setCaretOffset(static_cast<long>(aOffset));
+}
+
+/**
+ * aScrollType should be one of the nsIAccessiblescrollType constants.
+ */
+void
+ProxyAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
+                                   uint32_t aScrollType)
+{
+  RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+  if (!acc) {
+    return;
+  }
+
+  acc->scrollSubstringTo(static_cast<long>(aStartOffset),
+                         static_cast<long>(aEndOffset),
+                         static_cast<IA2ScrollType>(aScrollType));
+}
+
+/**
+ * aCoordinateType is one of the nsIAccessibleCoordinateType constants.
+ */
+void
+ProxyAccessible::ScrollSubstringToPoint(int32_t aStartOffset, int32_t aEndOffset,
+                                        uint32_t aCoordinateType, int32_t aX,
+                                        int32_t aY)
+{
+  RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
+  if (!acc) {
+    return;
+  }
+
+  IA2CoordinateType coordType;
+  if (aCoordinateType == nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE) {
+    coordType = IA2_COORDTYPE_SCREEN_RELATIVE;
+  } else if (aCoordinateType == nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE) {
+    coordType = IA2_COORDTYPE_PARENT_RELATIVE;
+  } else {
+    MOZ_RELEASE_ASSERT(false, "unsupported coord type");
+  }
+
+  acc->scrollSubstringToPoint(static_cast<long>(aStartOffset),
+                              static_cast<long>(aEndOffset),
+                              coordType,
+                              static_cast<long>(aX),
+                              static_cast<long>(aY));
+}
+
 } // namespace a11y
 } // namespace mozilla
--- a/accessible/tests/browser/e10s/events.js
+++ b/accessible/tests/browser/e10s/events.js
@@ -63,17 +63,21 @@ function waitForEvent(eventType, expecte
   return new Promise(resolve => {
     let eventObserver = {
       observe(subject, topic, data) {
         if (topic !== 'accessible-event') {
           return;
         }
 
         let event = subject.QueryInterface(nsIAccessibleEvent);
-        Logger.log(eventToString(event));
+        if (Logger.enabled) {
+          // Avoid calling eventToString if the logger isn't enabled in order
+          // to avoid an intermittent crash (bug 1307645).
+          Logger.log(eventToString(event));
+        }
 
         // If event type does not match expected type, skip the event.
         if (event.eventType !== eventType) {
           return;
         }
 
         let acc = event.accessible;
         let id = getAccessibleDOMNodeID(acc);
--- a/accessible/windows/ia2/ia2AccessibleHyperlink.cpp
+++ b/accessible/windows/ia2/ia2AccessibleHyperlink.cpp
@@ -69,18 +69,17 @@ ia2AccessibleHyperlink::get_anchor(long 
   if (!anchor)
     return S_FALSE;
 
   void* instancePtr = nullptr;
   HRESULT result = anchor->QueryInterface(IID_IUnknown, &instancePtr);
   if (FAILED(result))
     return result;
 
-  IUnknown* unknownPtr = static_cast<IUnknown*>(instancePtr);
-  aAnchor->ppunkVal = &unknownPtr;
+  aAnchor->punkVal = static_cast<IUnknown*>(instancePtr);
   aAnchor->vt = VT_UNKNOWN;
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 ia2AccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT* aAnchorTarget)
--- a/accessible/windows/msaa/MsaaIdGenerator.cpp
+++ b/accessible/windows/msaa/MsaaIdGenerator.cpp
@@ -80,36 +80,34 @@ public:
 private:
   MsaaID  mID;
 };
 
 } // namespace detail
 
 constexpr MsaaIdGenerator::MsaaIdGenerator()
   : mIDSet(kNumUniqueIDBits)
-  , mContentProcessID(0)
 {}
 
 uint32_t
 MsaaIdGenerator::GetID()
 {
-  static const uint32_t kContentProcessId = ResolveContentProcessID();
   uint32_t id = mIDSet.GetID();
   MOZ_ASSERT(id <= ((1UL << kNumUniqueIDBits) - 1UL));
-  return detail::BuildMsaaID(id, kContentProcessId);
+  return detail::BuildMsaaID(id, ResolveContentProcessID());
 }
 
 void
 MsaaIdGenerator::ReleaseID(AccessibleWrap* aAccWrap)
 {
   MOZ_ASSERT(aAccWrap);
   uint32_t id = aAccWrap->GetExistingID();
   MOZ_ASSERT(id != AccessibleWrap::kNoID);
   detail::MsaaIDCracker cracked(id);
-  if (cracked.GetContentProcessId() != mContentProcessID) {
+  if (cracked.GetContentProcessId() != ResolveContentProcessID()) {
     // This may happen if chrome holds a proxy whose ID was originally generated
     // by a content process. Since ReleaseID only has meaning in the process
     // that originally generated that ID, we ignore ReleaseID calls for any ID
     // that did not come from the current process.
     MOZ_ASSERT(aAccWrap->IsProxy());
     return;
   }
   mIDSet.ReleaseID(cracked.GetUniqueId());
@@ -121,19 +119,18 @@ MsaaIdGenerator::IsChromeID(uint32_t aID
   detail::MsaaIDCracker cracked(aID);
   return cracked.GetContentProcessId() == 0;
 }
 
 bool
 MsaaIdGenerator::IsIDForThisContentProcess(uint32_t aID)
 {
   MOZ_ASSERT(XRE_IsContentProcess());
-  static const uint32_t kContentProcessId = ResolveContentProcessID();
   detail::MsaaIDCracker cracked(aID);
-  return cracked.GetContentProcessId() == kContentProcessId;
+  return cracked.GetContentProcessId() == ResolveContentProcessID();
 }
 
 bool
 MsaaIdGenerator::IsIDForContentProcess(uint32_t aID,
                                        dom::ContentParentId aIPCContentProcessId)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   detail::MsaaIDCracker cracked(aID);
@@ -153,20 +150,20 @@ MsaaIdGenerator::IsSameContentProcessFor
 uint32_t
 MsaaIdGenerator::ResolveContentProcessID()
 {
   if (XRE_IsParentProcess()) {
     return 0;
   }
 
   dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
-  Unused << contentChild->SendGetA11yContentId(&mContentProcessID);
+  uint32_t result = contentChild->GetMsaaID();
 
-  MOZ_ASSERT(mContentProcessID);
-  return mContentProcessID;
+  MOZ_ASSERT(result);
+  return result;
 }
 
 /**
  * Each dom::ContentParent has a 64-bit ID. This ID is monotonically increasing
  * with each new content process, so those IDs are effectively single-use. OTOH,
  * MSAA requires 32-bit IDs. Since we only allocate kNumContentProcessIDBits for
  * the content process ID component, the MSAA content process ID value must be
  * reusable. sContentParentIdMap holds the current associations between
--- a/accessible/windows/msaa/MsaaIdGenerator.h
+++ b/accessible/windows/msaa/MsaaIdGenerator.h
@@ -43,15 +43,14 @@ public:
   uint32_t GetContentProcessIDFor(dom::ContentParentId aIPCContentProcessID);
   void ReleaseContentProcessIDFor(dom::ContentParentId aIPCContentProcessID);
 
 private:
   uint32_t ResolveContentProcessID();
 
 private:
   IDSet     mIDSet;
-  uint32_t  mContentProcessID;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif // mozilla_a11y_MsaaIdGenerator_h
--- a/accessible/xpcom/xpcAccessibleHyperText.cpp
+++ b/accessible/xpcom/xpcAccessibleHyperText.cpp
@@ -63,23 +63,19 @@ xpcAccessibleHyperText::GetText(int32_t 
   aText.Truncate();
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->TextSubstring(aStartOffset, aEndOffset, aText);
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     nsString text;
     mIntl.AsProxy()->TextSubstring(aStartOffset, aEndOffset, text);
     aText = text;
-#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetTextBeforeOffset(int32_t aOffset,
                                             AccessibleTextBoundary aBoundaryType,
                                             int32_t* aStartOffset,
@@ -93,24 +89,20 @@ xpcAccessibleHyperText::GetTextBeforeOff
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->TextBeforeOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, 
                              aText);
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     nsString text;
     mIntl.AsProxy()->GetTextBeforeOffset(aOffset, aBoundaryType, text,
                                          aStartOffset, aEndOffset);
     aText = text;
-#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetTextAtOffset(int32_t aOffset,
                                         AccessibleTextBoundary aBoundaryType,
                                         int32_t* aStartOffset,
@@ -123,24 +115,20 @@ xpcAccessibleHyperText::GetTextAtOffset(
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->TextAtOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, 
                          aText);
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     nsString text;
     mIntl.AsProxy()->GetTextAtOffset(aOffset, aBoundaryType, text, 
                                      aStartOffset, aEndOffset);
     aText = text;
-#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetTextAfterOffset(int32_t aOffset,
                                            AccessibleTextBoundary aBoundaryType,
                                            int32_t* aStartOffset,
@@ -153,24 +141,20 @@ xpcAccessibleHyperText::GetTextAfterOffs
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->TextAfterOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, 
                             aText);
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     nsString text;
     mIntl.AsProxy()->GetTextAfterOffset(aOffset, aBoundaryType, text, 
                                         aStartOffset, aEndOffset);
     aText = text;
-#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetCharacterAtOffset(int32_t aOffset,
                                              char16_t* aCharacter)
 {
@@ -350,39 +334,31 @@ xpcAccessibleHyperText::GetCaretOffset(i
   *aCaretOffset = -1;
 
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     *aCaretOffset = Intl()->CaretOffset();
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     *aCaretOffset = mIntl.AsProxy()->CaretOffset();
-#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::SetCaretOffset(int32_t aCaretOffset)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->SetCaretOffset(aCaretOffset);
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     mIntl.AsProxy()->SetCaretOffset(aCaretOffset);
-#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetSelectionCount(int32_t* aSelectionCount)
 {
   NS_ENSURE_ARG_POINTER(aSelectionCount);
@@ -468,59 +444,47 @@ NS_IMETHODIMP
 xpcAccessibleHyperText::AddSelection(int32_t aStartOffset, int32_t aEndOffset)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->AddToSelection(aStartOffset, aEndOffset);
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     mIntl.AsProxy()->AddToSelection(aStartOffset, aEndOffset);
-#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::RemoveSelection(int32_t aSelectionNum)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->RemoveFromSelection(aSelectionNum);
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     mIntl.AsProxy()->RemoveFromSelection(aSelectionNum);
-#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::ScrollSubstringTo(int32_t aStartOffset,
                                           int32_t aEndOffset,
                                           uint32_t aScrollType)
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->ScrollSubstringTo(aStartOffset, aEndOffset, aScrollType);
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     mIntl.AsProxy()->ScrollSubstringTo(aStartOffset, aEndOffset, aScrollType);
-#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::ScrollSubstringToPoint(int32_t aStartOffset,
                                                int32_t aEndOffset,
                                                uint32_t aCoordinateType,
@@ -528,22 +492,18 @@ xpcAccessibleHyperText::ScrollSubstringT
 {
   if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   if (mIntl.IsAccessible()) {
     Intl()->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoordinateType,
                                    aX, aY);
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     mIntl.AsProxy()->ScrollSubstringToPoint(aStartOffset, aEndOffset,
                                             aCoordinateType, aX, aY);
-#endif
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetEnclosingRange(nsIAccessibleTextRange** aRange)
 {
   NS_ENSURE_ARG_POINTER(aRange);
--- a/accessible/xpcom/xpcAccessibleValue.cpp
+++ b/accessible/xpcom/xpcAccessibleValue.cpp
@@ -21,21 +21,17 @@ xpcAccessibleValue::GetMaximumValue(doub
 
   if (Intl().IsAccessible() && Intl().AsAccessible()->IsDefunct())
     return NS_ERROR_FAILURE;
 
   double value;
   if (Intl().IsAccessible()) {
     value = Intl().AsAccessible()->MaxValue();
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     value = Intl().AsProxy()->MaxValue();
-#endif
   }
 
   if (!IsNaN(value))
     *aValue = value;
 
   return NS_OK;
 }
 
@@ -50,21 +46,17 @@ xpcAccessibleValue::GetMinimumValue(doub
 
   if (Intl().IsAccessible() && Intl().AsAccessible()->IsDefunct())
     return NS_ERROR_FAILURE;
 
   double value;
   if (Intl().IsAccessible()) {
     value = Intl().AsAccessible()->MinValue();
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     value = Intl().AsProxy()->MinValue();
-#endif
   }
 
   if (!IsNaN(value))
     *aValue = value;
 
   return NS_OK;
 }
 
@@ -79,21 +71,17 @@ xpcAccessibleValue::GetCurrentValue(doub
 
   if (Intl().IsAccessible() && Intl().AsAccessible()->IsDefunct())
     return NS_ERROR_FAILURE;
 
   double value;
   if (Intl().IsAccessible()) {
     value = Intl().AsAccessible()->CurValue();
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     value = Intl().AsProxy()->CurValue();
-#endif
   }
 
   if (!IsNaN(value))
     *aValue = value;
 
   return NS_OK;
 }
 
@@ -104,21 +92,17 @@ xpcAccessibleValue::SetCurrentValue(doub
     return NS_ERROR_FAILURE;
 
   if (Intl().IsAccessible() && Intl().AsAccessible()->IsDefunct())
     return NS_ERROR_FAILURE;
 
   if (Intl().IsAccessible()) {
     Intl().AsAccessible()->SetCurValue(aValue);
   } else {
-#if defined(XP_WIN)
-    return NS_ERROR_NOT_IMPLEMENTED;
-#else
     Intl().AsProxy()->SetCurValue(aValue);
-#endif
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleValue::GetMinimumIncrement(double* aValue)
 {
--- a/addon-sdk/source/app-extension/bootstrap.js
+++ b/addon-sdk/source/app-extension/bootstrap.js
@@ -293,21 +293,18 @@ function loadSandbox(uri) {
       CC: bind(CC, Components), components: Components,
       ChromeWorker: ChromeWorker });
   };
   scriptLoader.loadSubScript(uri, sandbox, 'UTF-8');
   return sandbox;
 }
 
 function unloadSandbox(sandbox) {
-  if ("nukeSandbox" in Cu) {
-    try {
-      Cu.nukeSandbox(sandbox);
-    } catch (e) {}
-  }
+  if (Cu.getClassName(sandbox, true) == "Sandbox")
+    Cu.nukeSandbox(sandbox);
 }
 
 function setTimeout(callback, delay) {
   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   timer.initWithCallback({ notify: callback }, delay,
                          Ci.nsITimer.TYPE_ONE_SHOT);
   return timer;
 }
--- a/addon-sdk/source/lib/sdk/addon/bootstrap.js
+++ b/addon-sdk/source/lib/sdk/addon/bootstrap.js
@@ -152,21 +152,19 @@ Bootstrap.prototype = {
     return new Promise(resolve => {
       const { loader } = this;
       if (loader) {
         this.loader = null;
         unload(loader, reason);
 
         setTimeout(() => {
           for (let uri of Object.keys(loader.sandboxes)) {
-            try {
-              Cu.nukeSandbox(loader.sandboxes[uri]);
-            } catch (e) {
-              // This will throw for shared sandboxes.
-            }
+            let sandbox = loader.sandboxes[uri];
+            if (Cu.getClassName(sandbox, true) == "Sandbox")
+              Cu.nukeSandbox(sandbox);
             delete loader.sandboxes[uri];
             delete loader.modules[uri];
           }
 
           try {
             Cu.nukeSandbox(loader.sharedGlobalSandbox);
           } catch (e) {
             Cu.reportError(e);
--- a/addon-sdk/source/test/addons/l10n-properties/app-extension/bootstrap.js
+++ b/addon-sdk/source/test/addons/l10n-properties/app-extension/bootstrap.js
@@ -275,17 +275,17 @@ function loadSandbox(uri) {
       CC: bind(CC, Components), components: Components,
       ChromeWorker: ChromeWorker });
   };
   scriptLoader.loadSubScript(uri, sandbox, 'UTF-8');
   return sandbox;
 }
 
 function unloadSandbox(sandbox) {
-  if ("nukeSandbox" in Cu)
+  if (Cu.getClassName(sandbox, true) == "Sandbox")
     Cu.nukeSandbox(sandbox);
 }
 
 function setTimeout(callback, delay) {
   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   timer.initWithCallback({ notify: callback }, delay,
                          Ci.nsITimer.TYPE_ONE_SHOT);
   return timer;
--- a/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/bootstrap.js
+++ b/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/bootstrap.js
@@ -275,17 +275,17 @@ function loadSandbox(uri) {
       CC: bind(CC, Components), components: Components,
       ChromeWorker: ChromeWorker });
   };
   scriptLoader.loadSubScript(uri, sandbox, 'UTF-8');
   return sandbox;
 }
 
 function unloadSandbox(sandbox) {
-  if ("nukeSandbox" in Cu)
+  if (Cu.getClassName(sandbox, true) == "Sandbox")
     Cu.nukeSandbox(sandbox);
 }
 
 function setTimeout(callback, delay) {
   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   timer.initWithCallback({ notify: callback }, delay,
                          Ci.nsITimer.TYPE_ONE_SHOT);
   return timer;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1072,21 +1072,21 @@ var gBrowserInit = {
     }, false, true);
 
     gBrowser.addEventListener("InsecureLoginFormsStateChange", function() {
       gIdentityHandler.refreshForInsecureLoginForms();
     });
 
     let uriToLoad = this._getUriToLoad();
     if (uriToLoad && uriToLoad != "about:blank") {
-      if (uriToLoad instanceof Ci.nsISupportsArray) {
-        let count = uriToLoad.Count();
+      if (uriToLoad instanceof Ci.nsIArray) {
+        let count = uriToLoad.length;
         let specs = [];
         for (let i = 0; i < count; i++) {
-          let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString);
+          let urisstring = uriToLoad.queryElementAt(i, Ci.nsISupportsString);
           specs.push(urisstring.data);
         }
 
         // This function throws for certain malformed URIs, so use exception handling
         // so that we don't disrupt startup
         try {
           gBrowser.loadTabs(specs, false, true);
         } catch (e) {}
@@ -1408,17 +1408,17 @@ var gBrowserInit = {
     this.delayedStartupFinished = true;
 
     Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
     TelemetryTimestamps.add("delayedStartupFinished");
   },
 
   // Returns the URI(s) to load at startup.
   _getUriToLoad: function () {
-    // window.arguments[0]: URI to load (string), or an nsISupportsArray of
+    // window.arguments[0]: URI to load (string), or an nsIArray of
     //                      nsISupportsStrings to load, or a xul:tab of
     //                      a tabbrowser, which will be replaced by this
     //                      window (for this case, all other arguments are
     //                      ignored).
     if (!window.arguments || !window.arguments[0])
       return null;
 
     let uri = window.arguments[0];
@@ -4245,20 +4245,20 @@ var XULBrowserWindow = {
   },
 
   forceInitialBrowserRemote: function() {
     let initBrowser =
       document.getAnonymousElementByAttribute(gBrowser, "anonid", "initialBrowser");
     return initBrowser.frameLoader.tabParent;
   },
 
-  forceInitialBrowserNonRemote: function() {
+  forceInitialBrowserNonRemote: function(aOpener) {
     let initBrowser =
       document.getAnonymousElementByAttribute(gBrowser, "anonid", "initialBrowser");
-    gBrowser.updateBrowserRemoteness(initBrowser, false);
+    gBrowser.updateBrowserRemoteness(initBrowser, false, aOpener);
   },
 
   setDefaultStatus: function (status) {
     this.defaultStatus = status;
     this.updateStatusField();
   },
 
   setOverLink: function (url, anchorElt) {
@@ -4896,17 +4896,18 @@ var TabsProgressListener = {
 
 function nsBrowserAccess() { }
 
 nsBrowserAccess.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
 
   _openURIInNewTab: function(aURI, aReferrer, aReferrerPolicy, aIsPrivate,
                              aIsExternal, aForceNotRemote=false,
-                             aUserContextId=Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID) {
+                             aUserContextId=Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID,
+                             aOpener=null) {
     let win, needToFocusWin;
 
     // try the current window.  if we're in a popup, fall back on the most recent browser window
     if (window.toolbar.visible)
       win = window;
     else {
       win = RecentWindow.getMostRecentBrowserWindow({private: aIsPrivate});
       needToFocusWin = true;
@@ -4926,36 +4927,38 @@ nsBrowserAccess.prototype = {
     let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground");
 
     let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", {
                                       referrerURI: aReferrer,
                                       referrerPolicy: aReferrerPolicy,
                                       userContextId: aUserContextId,
                                       fromExternal: aIsExternal,
                                       inBackground: loadInBackground,
-                                      forceNotRemote: aForceNotRemote});
+                                      forceNotRemote: aForceNotRemote,
+                                      opener: aOpener,
+                                      });
     let browser = win.gBrowser.getBrowserForTab(tab);
 
     if (needToFocusWin || (!loadInBackground && aIsExternal))
       win.focus();
 
     return browser;
   },
 
-  openURI: function (aURI, aOpener, aWhere, aContext) {
+  openURI: function (aURI, aOpener, aWhere, aFlags) {
     // This function should only ever be called if we're opening a URI
     // from a non-remote browser window (via nsContentTreeOwner).
     if (aOpener && Cu.isCrossProcessWrapper(aOpener)) {
       Cu.reportError("nsBrowserAccess.openURI was passed a CPOW for aOpener. " +
                      "openURI should only ever be called from non-remote browsers.");
       throw Cr.NS_ERROR_FAILURE;
     }
 
     var newWindow = null;
-    var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
+    var isExternal = !!(aFlags & Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
     if (aOpener && isExternal) {
       Cu.reportError("nsBrowserAccess.openURI did not expect an opener to be " +
                      "passed if the context is OPEN_EXTERNAL.");
       throw Cr.NS_ERROR_FAILURE;
     }
 
     if (isExternal && aURI && aURI.schemeIs("chrome")) {
@@ -4999,19 +5002,21 @@ nsBrowserAccess.prototype = {
         // this means forcing the newly opened browser to be non-remote so that
         // we can hand back the nsIDOMWindow. The XULBrowserWindow.shouldLoadURI
         // will do the job of shuttling off the newly opened browser to run in
         // the right process once it starts loading a URI.
         let forceNotRemote = !!aOpener;
         let userContextId = aOpener && aOpener.document
                               ? aOpener.document.nodePrincipal.originAttributes.userContextId
                               : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
+        let openerWindow = (aFlags & Ci.nsIBrowserDOMWindow.OPEN_NO_OPENER) ? null : aOpener;
         let browser = this._openURIInNewTab(aURI, referrer, referrerPolicy,
                                             isPrivate, isExternal,
-                                            forceNotRemote, userContextId);
+                                            forceNotRemote, userContextId,
+                                            openerWindow);
         if (browser)
           newWindow = browser.contentWindow;
         break;
       default : // OPEN_CURRENTWINDOW or an illegal value
         newWindow = content;
         if (aURI) {
           let loadflags = isExternal ?
                             Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
@@ -5023,23 +5028,23 @@ nsBrowserAccess.prototype = {
                                     });
         }
         if (!gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"))
           window.focus();
     }
     return newWindow;
   },
 
-  openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aContext) {
+  openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aFlags) {
     if (aWhere != Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
       dump("Error: openURIInFrame can only open in new tabs");
       return null;
     }
 
-    var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
+    var isExternal = !!(aFlags & Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
     var userContextId = aParams.openerOriginAttributes &&
                         ("userContextId" in aParams.openerOriginAttributes)
                           ? aParams.openerOriginAttributes.userContextId
                           : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID
 
     let browser = this._openURIInNewTab(aURI, aParams.referrer,
                                         aParams.referrerPolicy,
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -1048,28 +1048,26 @@ function setItemValue(id, value)
 
 function formatNumber(number)
 {
   return (+number).toLocaleString();  // coerce number to a numeric value before calling toLocaleString()
 }
 
 function formatDate(datestr, unknown)
 {
-  // scriptable date formatter, for pretty printing dates
-  var dateService = Components.classes["@mozilla.org/intl/scriptabledateformat;1"]
-                              .getService(Components.interfaces.nsIScriptableDateFormat);
-
   var date = new Date(datestr);
   if (!date.valueOf())
     return unknown;
 
-  return dateService.FormatDateTime("", dateService.dateFormatLong,
-                                    dateService.timeFormatSeconds,
-                                    date.getFullYear(), date.getMonth()+1, date.getDate(),
-                                    date.getHours(), date.getMinutes(), date.getSeconds());
+  const locale = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+                 .getService(Components.interfaces.nsIXULChromeRegistry)
+                 .getSelectedLocale("global", true);
+  const dtOptions = { year: 'numeric', month: 'long', day: 'numeric',
+                      hour: 'numeric', minute: 'numeric', second: 'numeric' };
+  return date.toLocaleString(locale, dtOptions);
 }
 
 function doCopy()
 {
   if (!gClipboardHelper)
     return;
 
   var elem = document.commandDispatcher.focusedElement;
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1498,16 +1498,17 @@
             var aRelatedToCurrent;
             var aAllowMixedContent;
             var aSkipAnimation;
             var aForceNotRemote;
             var aNoReferrer;
             var aUserContextId;
             var aRelatedBrowser;
             var aOriginPrincipal;
+            var aOpener;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aReferrerPolicy       = params.referrerPolicy;
               aCharset              = params.charset;
               aPostData             = params.postData;
@@ -1517,16 +1518,17 @@
               aRelatedToCurrent     = params.relatedToCurrent;
               aAllowMixedContent    = params.allowMixedContent;
               aSkipAnimation        = params.skipAnimation;
               aForceNotRemote       = params.forceNotRemote;
               aNoReferrer           = params.noReferrer;
               aUserContextId        = params.userContextId;
               aRelatedBrowser       = params.relatedBrowser;
               aOriginPrincipal      = params.originPrincipal;
+              aOpener               = params.opener;
             }
 
             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
                          Services.prefs.getBoolPref("browser.tabs.loadInBackground");
             var owner = bgLoad ? null : this.selectedTab;
             var tab = this.addTab(aURI, {
                                   referrerURI: aReferrerURI,
                                   referrerPolicy: aReferrerPolicy,
@@ -1537,17 +1539,18 @@
                                   fromExternal: aFromExternal,
                                   relatedToCurrent: aRelatedToCurrent,
                                   skipAnimation: aSkipAnimation,
                                   allowMixedContent: aAllowMixedContent,
                                   forceNotRemote: aForceNotRemote,
                                   noReferrer: aNoReferrer,
                                   userContextId: aUserContextId,
                                   originPrincipal: aOriginPrincipal,
-                                  relatedBrowser: aRelatedBrowser });
+                                  relatedBrowser: aRelatedBrowser,
+                                  opener: aOpener });
             if (!bgLoad)
               this.selectedTab = tab;
 
             return tab;
          ]]>
         </body>
       </method>
 
@@ -1650,21 +1653,37 @@
               this.selectedBrowser.focus();
           }
         ]]></body>
       </method>
 
       <method name="updateBrowserRemoteness">
         <parameter name="aBrowser"/>
         <parameter name="aShouldBeRemote"/>
+        <parameter name="aOpener"/>
         <body>
           <![CDATA[
             let isRemote = aBrowser.getAttribute("remote") == "true";
-            if (isRemote == aShouldBeRemote)
+
+            // If we are passed an opener, we must be making the browser non-remote, and
+            // if the browser is _currently_ non-remote, we need the openers to match,
+            // because it is already too late to change it.
+            if (aOpener) {
+              if (aShouldBeRemote) {
+                throw new Exception("Cannot set an opener on a browser which should be remote!");
+              }
+              if (!isRemote && aBrowser.contentWindow.opener != aOpener) {
+                throw new Exception("Cannot change opener on an already non-remote browser!");
+              }
+            }
+
+            // Abort if we're not going to change anything
+            if (isRemote == aShouldBeRemote) {
               return false;
+            }
 
             let tab = this.getTabForBrowser(aBrowser);
             let evt = document.createEvent("Events");
             evt.initEvent("BeforeTabRemotenessChange", true, false);
             tab.dispatchEvent(evt);
 
             let wasActive = document.activeElement == aBrowser;
 
@@ -1695,16 +1714,20 @@
             let parent = aBrowser.parentNode;
             parent.removeChild(aBrowser);
             aBrowser.setAttribute("remote", aShouldBeRemote ? "true" : "false");
 
             // NB: This works with the hack in the browser constructor that
             // turns this normal property into a field.
             aBrowser.relatedBrowser = relatedBrowser;
 
+            // Set the opener window on the browser, such that when the frame
+            // loader is created the opener is set correctly.
+            aBrowser.presetOpenerWindow(aOpener);
+
             parent.appendChild(aBrowser);
 
             aBrowser.userTypedValue = oldUserTypedValue;
             if (hadStartedLoad) {
               aBrowser.urlbarChangeTracker.startedLoad();
             }
 
             aBrowser.droppedLinkHandler = droppedLinkHandler;
@@ -1876,16 +1899,23 @@
             if (aParams.userContextId) {
               b.setAttribute("usercontextid", aParams.userContextId);
             }
 
             if (aParams.remote) {
               b.setAttribute("remote", "true");
             }
 
+            if (aParams.opener) {
+              if (aParams.remote) {
+                throw new Exception("Cannot set opener window on a remote browser!");
+              }
+              b.QueryInterface(Ci.nsIFrameLoaderOwner).presetOpenerWindow(aParams.opener);
+            }
+
             if (window.gShowPageResizers && window.windowState == window.STATE_NORMAL) {
               b.setAttribute("showresizer", "true");
             }
 
             if (!aParams.isPreloadBrowser && this.hasAttribute("autocompletepopup")) {
               b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
             }
 
@@ -1976,17 +2006,18 @@
             }
 
             if (!browser) {
               // No preloaded browser found, create one.
               browser = this._createBrowser({permanentKey: aTab.permanentKey,
                                              remote: remote,
                                              uriIsAboutBlank: uriIsAboutBlank,
                                              userContextId: aParams.userContextId,
-                                             relatedBrowser: aParams.relatedBrowser});
+                                             relatedBrowser: aParams.relatedBrowser,
+                                             opener: aParams.opener});
             }
 
             let notificationbox = this.getNotificationBox(browser);
             let uniqueId = this._generateUniquePanelID();
             notificationbox.id = uniqueId;
             aTab.linkedPanel = uniqueId;
             aTab.linkedBrowser = browser;
             aTab.hasBrowser = true;
@@ -2052,16 +2083,17 @@
             var aSkipAnimation;
             var aAllowMixedContent;
             var aForceNotRemote;
             var aNoReferrer;
             var aUserContextId;
             var aEventDetail;
             var aRelatedBrowser;
             var aOriginPrincipal;
+            var aOpener;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aReferrerPolicy       = params.referrerPolicy;
               aCharset              = params.charset;
               aPostData             = params.postData;
@@ -2072,16 +2104,17 @@
               aSkipAnimation        = params.skipAnimation;
               aAllowMixedContent    = params.allowMixedContent;
               aForceNotRemote       = params.forceNotRemote;
               aNoReferrer           = params.noReferrer;
               aUserContextId        = params.userContextId;
               aEventDetail          = params.eventDetail;
               aRelatedBrowser       = params.relatedBrowser;
               aOriginPrincipal      = params.originPrincipal;
+              aOpener               = params.opener;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
@@ -2139,17 +2172,18 @@
             // bug this will be removed so we can leave the tab in its "lazy"
             // state to be exploited for startup optimization.  Note that for
             // now this must occur before "TabOpen" event is fired, as that will
             // trigger SessionStore.jsm to run code that expects the existence
             // of tab.linkedBrowser.
             let browserParams = {
               forceNotRemote: aForceNotRemote,
               userContextId:  aUserContextId,
-              relatedBrowser: aRelatedBrowser
+              relatedBrowser: aRelatedBrowser,
+              opener: aOpener,
             };
             let { usingPreloadedContent } = this._linkBrowserToTab(t, aURI, browserParams);
             let b = t.linkedBrowser;
 
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
             // even if the event listener opens or closes tabs.
             var detail = aEventDetail || {};
@@ -6568,16 +6602,25 @@
       Describes how the tab ended up in this mute state. May be any of:
 
        - undefined: The tabs mute state has never changed.
        - null: The mute state was last changed through the UI.
        - Any string: The ID was changed through an extension API. The string
                      must be the ID of the extension which changed it.
       -->
       <field name="muteReason">undefined</field>
+
+      <property name="userContextId" readonly="true">
+        <getter>
+          return this.hasAttribute("usercontextid")
+                   ? parseInt(this.getAttribute("usercontextid"))
+                   : 0;
+        </getter>
+      </property>
+
       <property name="soundPlaying" readonly="true">
         <getter>
           return this.getAttribute("soundplaying") == "true";
         </getter>
       </property>
 
       <property name="lastAccessed">
         <getter>
--- a/browser/components/contextualidentity/test/browser/browser_eme.js
+++ b/browser/components/contextualidentity/test/browser/browser_eme.js
@@ -86,16 +86,17 @@ function generateKeyInfo(aData) {
 add_task(function* setup() {
   // Make sure userContext is enabled.
   yield new Promise(resolve => {
     SpecialPowers.pushPrefEnv({"set": [
       [ "privacy.userContext.enabled", true ],
       [ "media.mediasource.enabled", true ],
       [ "media.eme.apiVisible", true ],
       [ "media.mediasource.webm.enabled", true ],
+      [ "media.clearkey.persistent-license.enabled", true ],
     ]}, resolve);
   });
 });
 
 add_task(function* test() {
   // Open a tab with the default container.
   let defaultContainer = yield openTabInUserContext(TEST_URL + "empty_file.html", USER_ID_DEFAULT);
 
--- a/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
@@ -176,16 +176,17 @@ function* checkEMEKey(browser, emeSessio
 
 add_task(function* setup() {
   // Make sure userContext is enabled.
   yield SpecialPowers.pushPrefEnv({"set": [
       [ "privacy.userContext.enabled", true ],
       [ "media.mediasource.enabled", true ],
       [ "media.eme.apiVisible", true ],
       [ "media.mediasource.webm.enabled", true ],
+      [ "media.clearkey.persistent-license.enabled", true ],
   ]});
 });
 
 add_task(function* test_EME_forgetThisSite() {
   let tabs = [];
   let emeSessionIds = [];
 
   for (let userContextId of Object.keys(USER_CONTEXTS)) {
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -3,16 +3,18 @@
 "use strict";
 
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
 XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
                                   "resource://gre/modules/MatchPattern.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+                                  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
                                   "resource://gre/modules/PromiseUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 
 var {
@@ -518,18 +520,47 @@ extensions.registerSchemaAPI("tabs", "ad
           if (createProperties.url !== null) {
             url = context.uri.resolve(createProperties.url);
 
             if (!context.checkLoadURL(url, {dontReportErrors: true})) {
               return Promise.reject({message: `Illegal URL: ${url}`});
             }
           }
 
+          if (createProperties.cookieStoreId && !extension.hasPermission("cookies")) {
+            return Promise.reject({message: `No permission for cookieStoreId: ${createProperties.cookieStoreId}`});
+          }
+
+          let options = {};
+          if (createProperties.cookieStoreId) {
+            if (!global.isValidCookieStoreId(createProperties.cookieStoreId)) {
+              return Promise.reject({message: `Illegal cookieStoreId: ${createProperties.cookieStoreId}`});
+            }
+
+            let privateWindow = PrivateBrowsingUtils.isBrowserPrivate(window.gBrowser);
+            if (privateWindow && !global.isPrivateCookieStoreId(createProperties.cookieStoreId)) {
+              return Promise.reject({message: `Illegal to set non-private cookieStorageId in a private window`});
+            }
+
+            if (!privateWindow && global.isPrivateCookieStoreId(createProperties.cookieStoreId)) {
+              return Promise.reject({message: `Illegal to set private cookieStorageId in a non-private window`});
+            }
+
+            if (global.isContainerCookieStoreId(createProperties.cookieStoreId)) {
+              let containerId = global.getContainerForCookieStoreId(createProperties.cookieStoreId);
+              if (!containerId) {
+                return Promise.reject({message: `No cookie store exists with ID ${createProperties.cookieStoreId}`});
+              }
+
+              options.userContextId = containerId;
+            }
+          }
+
           tabListener.initTabReady();
-          let tab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL);
+          let tab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL, options);
 
           let active = true;
           if (createProperties.active !== null) {
             active = createProperties.active;
           }
           if (active) {
             window.gBrowser.selectedTab = tab;
           }
@@ -688,16 +719,21 @@ extensions.registerSchemaAPI("tabs", "ad
 
           if (queryInfo.currentWindow !== null) {
             let eq = window == currentWindow(context);
             if (queryInfo.currentWindow != eq) {
               return false;
             }
           }
 
+          if (queryInfo.cookieStoreId !== null &&
+              tab.cookieStoreId != queryInfo.cookieStoreId) {
+            return false;
+          }
+
           if (pattern && !pattern.matches(Services.io.newURI(tab.url, null, null))) {
             return false;
           }
 
           return true;
         }
 
         let result = [];
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -639,17 +639,17 @@ ExtensionTabManager.prototype = {
       status: TabManager.getStatus(tab),
       incognito: PrivateBrowsingUtils.isBrowserPrivate(browser),
       width: browser.frameLoader.lazyWidth || browser.clientWidth,
       height: browser.frameLoader.lazyHeight || browser.clientHeight,
       audible: tab.soundPlaying,
       mutedInfo,
     };
     if (this.extension.hasPermission("cookies")) {
-      result.cookieStoreId = getCookieStoreIdForTab(result);
+      result.cookieStoreId = getCookieStoreIdForTab(result, tab);
     }
 
     if (this.hasTabPermission(tab)) {
       result.url = browser.currentURI.spec;
       let title = browser.contentTitle || tab.label;
       if (title) {
         result.title = title;
       }
--- a/browser/components/extensions/ext-windows.js
+++ b/browser/components/extensions/ext-windows.js
@@ -115,19 +115,19 @@ extensions.registerSchemaAPI("windows", 
           if (createData.incognito !== null && createData.incognito != incognito) {
             return Promise.reject({message: "`incognito` property must match the incognito state of tab"});
           }
           createData.incognito = incognito;
 
           args.appendElement(tab, /* weak = */ false);
         } else if (createData.url !== null) {
           if (Array.isArray(createData.url)) {
-            let array = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
+            let array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
             for (let url of createData.url) {
-              array.AppendElement(mkstr(url));
+              array.appendElement(mkstr(url), /* weak = */ false);
             }
             args.appendElement(array, /* weak = */ false);
           } else {
             args.appendElement(mkstr(createData.url), /* weak = */ false);
           }
         } else {
           args.appendElement(mkstr(aboutNewTabService.newTabURL), /* weak = */ false);
         }
--- a/browser/components/extensions/schemas/tabs.json
+++ b/browser/components/extensions/schemas/tabs.json
@@ -68,17 +68,18 @@
           "mutedInfo": {"$ref": "MutedInfo", "optional": true, "description": "Current tab muted state and the reason for the last state change."},
           "url": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL the tab is displaying. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
           "title": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The title of the tab. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
           "favIconUrl": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL of the tab's favicon. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission. It may also be an empty string if the tab is loading."},
           "status": {"type": "string", "optional": true, "description": "Either <em>loading</em> or <em>complete</em>."},
           "incognito": {"type": "boolean", "description": "Whether the tab is in an incognito window."},
           "width": {"type": "integer", "optional": true, "description": "The width of the tab in pixels."},
           "height": {"type": "integer", "optional": true, "description": "The height of the tab in pixels."},
-          "sessionId": {"unsupported": true, "type": "string", "optional": true, "description": "The session ID used to uniquely identify a Tab obtained from the $(ref:sessions) API."}
+          "sessionId": {"unsupported": true, "type": "string", "optional": true, "description": "The session ID used to uniquely identify a Tab obtained from the $(ref:sessions) API."},
+          "cookieStoreId": {"type": "string", "description": "The CookieStoreId used for the tab."}
         }
       },
       {
         "id": "ZoomSettingsMode",
         "type": "string",
         "description": "Defines how zoom changes are handled, i.e. which entity is responsible for the actual scaling of the page; defaults to <code>automatic</code>.",
         "enum": [
           {
@@ -386,16 +387,21 @@
                 "description": "Whether the tab should be pinned. Defaults to <var>false</var>"
               },
               "openerTabId": {
                 "unsupported": true,
                 "type": "integer",
                 "minimum": 0,
                 "optional": true,
                 "description": "The ID of the tab that opened this tab. If specified, the opener tab must be in the same window as the newly created tab."
+              },
+              "cookieStoreId": {
+                "type": "string",
+                "optional": true,
+                "description": "The CookieStoreId for the tab that opened this tab."
               }
             }
           },
           {
             "type": "function",
             "name": "callback",
             "optional": true,
             "parameters": [
@@ -509,16 +515,21 @@
                 "optional": true,
                 "description": "The type of window the tabs are in."
               },
               "index": {
                 "type": "integer",
                 "optional": true,
                 "minimum": 0,
                 "description": "The position of the tabs within their windows."
+              },
+              "cookieStoreId": {
+                "type": "string",
+                "optional": true,
+                "description": "The CookieStoreId used for the tab."
               }
             }
           },
           {
             "type": "function",
             "name": "callback",
             "parameters": [
               {
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -78,16 +78,17 @@ tags = webextensions
 [browser_ext_tabs_move_window_multiple.js]
 [browser_ext_tabs_move_window_pinned.js]
 [browser_ext_tabs_onHighlighted.js]
 [browser_ext_tabs_onUpdated.js]
 [browser_ext_tabs_query.js]
 [browser_ext_tabs_reload.js]
 [browser_ext_tabs_reload_bypass_cache.js]
 [browser_ext_tabs_sendMessage.js]
+[browser_ext_tabs_cookieStoreId.js]
 [browser_ext_tabs_update.js]
 [browser_ext_tabs_zoom.js]
 [browser_ext_tabs_update_url.js]
 [browser_ext_topwindowid.js]
 [browser_ext_webNavigation_frameId0.js]
 [browser_ext_webNavigation_getFrames.js]
 [browser_ext_webNavigation_urlbar_transitions.js]
 [browser_ext_windows.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_cookieStoreId.js
@@ -0,0 +1,170 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(function* setup() {
+  // make sure userContext is enabled.
+  return SpecialPowers.pushPrefEnv({"set": [
+    ["privacy.userContext.enabled", true],
+  ]});
+});
+
+add_task(function* () {
+  info("Start testing tabs.create with cookieStoreId");
+
+  let testCases = [
+    // No private window
+    {privateTab: false, cookieStoreId: null, success: true, expectedCookieStoreId: "firefox-default"},
+    {privateTab: false, cookieStoreId: "firefox-default", success: true, expectedCookieStoreId: "firefox-default"},
+    {privateTab: false, cookieStoreId: "firefox-container-1", success: true, expectedCookieStoreId: "firefox-container-1"},
+    {privateTab: false, cookieStoreId: "firefox-container-2", success: true, expectedCookieStoreId: "firefox-container-2"},
+    {privateTab: false, cookieStoreId: "firefox-container-42", failure: "exist"},
+    {privateTab: false, cookieStoreId: "firefox-private", failure: "defaultToPrivate"},
+    {privateTab: false, cookieStoreId: "wow", failure: "illegal"},
+
+    // Private window
+    {privateTab: true, cookieStoreId: null, success: true, expectedCookieStoreId: "firefox-private"},
+    {privateTab: true, cookieStoreId: "firefox-private", success: true, expectedCookieStoreId: "firefox-private"},
+    {privateTab: true, cookieStoreId: "firefox-default", failure: "privateToDefault"},
+    {privateTab: true, cookieStoreId: "firefox-container-1", failure: "privateToDefault"},
+    {privateTab: true, cookieStoreId: "wow", failure: "illegal"},
+  ];
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["tabs", "cookies"],
+    },
+
+    background: function() {
+      function testTab(data, tab) {
+        browser.test.assertTrue(data.success, "we want a success");
+        browser.test.assertTrue(!!tab, "we have a tab");
+        browser.test.assertEq(data.expectedCookieStoreId, tab.cookieStoreId, "tab should have the correct cookieStoreId");
+      }
+
+      function runTest(data) {
+        // Tab Creation
+        browser.tabs.create({windowId: data.privateTab ? this.privateWindowId : this.defaultWindowId,
+                             cookieStoreId: data.cookieStoreId})
+
+        // Tests for tab creation
+        .then((tab) => {
+          testTab(data, tab);
+          return tab;
+        }, (error) => {
+          browser.test.assertTrue(!!data.failure, "we want a failure");
+          if (data.failure == "illegal") {
+            browser.test.assertTrue(/Illegal cookieStoreId/.test(error.message),
+                                    "runtime.lastError should report the expected error message");
+          } else if (data.failure == "defaultToPrivate") {
+            browser.test.assertTrue("Illegal to set private cookieStorageId in a non private window",
+                                    error.message,
+                                    "runtime.lastError should report the expected error message");
+          } else if (data.failure == "privateToDefault") {
+            browser.test.assertTrue("Illegal to set non private cookieStorageId in a private window",
+                                    error.message,
+                                    "runtime.lastError should report the expected error message");
+          } else if (data.failure == "exist") {
+            browser.test.assertTrue(/No cookie store exists/.test(error.message),
+                                    "runtime.lastError should report the expected error message");
+          } else {
+            browser.test.fail("The test is broken");
+          }
+
+          return null;
+        })
+
+        // Tests for tab querying
+        .then((tab) => {
+          if (tab) {
+            return browser.tabs.query({windowId: data.privateTab ? this.privateWindowId : this.defaultWindowId,
+                                       cookieStoreId: data.cookieStoreId})
+                   .then((tabs) => {
+                     browser.test.assertTrue(tabs.length >= 1, "Tab found!");
+                     testTab(data, tabs[0]);
+                     return tab;
+                   });
+          }
+        })
+
+        .then((tab) => {
+          if (tab) {
+            return browser.cookies.getAllCookieStores()
+                   .then(stores => {
+                     let store = stores.find(store => store.id === tab.cookieStoreId);
+                     browser.test.assertTrue(!!store, "We have a store for this tab.");
+                     return tab;
+                   });
+          }
+        })
+
+        .then((tab) => {
+          if (tab) {
+            return browser.tabs.remove(tab.id);
+          }
+        })
+
+        .then(() => {
+          browser.test.sendMessage("test-done");
+        }, () => {
+          browser.test.fail("An exception has ben thrown");
+        });
+      }
+
+      function initialize() {
+        browser.windows.create({incognito: true})
+        .then((win) => {
+          this.privateWindowId = win.id;
+          return browser.windows.create({incognito: false});
+        })
+        .then((win) => {
+          this.defaultWindowId = win.id;
+        })
+        .then(() => {
+          browser.test.sendMessage("ready");
+        });
+      }
+
+      function shutdown() {
+        browser.windows.remove(this.privateWindowId)
+        .then(() => {
+          browser.windows.remove(this.defaultWindowId);
+        })
+        .then(() => {
+          browser.test.sendMessage("gone");
+        });
+      }
+
+      // Waiting for messages
+      browser.test.onMessage.addListener((msg, data) => {
+        if (msg == "be-ready") {
+          initialize();
+        } else if (msg == "test") {
+          runTest(data);
+        } else {
+          browser.test.assertTrue("finish", msg, "Shutting down");
+          shutdown();
+        }
+      });
+    },
+  });
+
+  yield extension.startup();
+
+  info("Tests must be ready...");
+  extension.sendMessage("be-ready");
+  yield extension.awaitMessage("ready");
+  info("Tests are ready to run!");
+
+  for (let test of testCases) {
+    info(`test tab.create with cookieStoreId: "${test.cookieStoreId}"`);
+    extension.sendMessage("test", test);
+    yield extension.awaitMessage("test-done");
+  }
+
+  info("Waiting for shutting down...");
+  extension.sendMessage("finish");
+  yield extension.awaitMessage("gone");
+
+  yield extension.unload();
+});
--- a/browser/components/feeds/FeedWriter.js
+++ b/browser/components/feeds/FeedWriter.js
@@ -257,21 +257,30 @@ FeedWriter.prototype = {
   _parseDate(dateString) {
     // Convert the date into the user's local time zone
     let dateObj = new Date(dateString);
 
     // Make sure the date we're given is valid.
     if (!dateObj.getTime())
       return false;
 
-    let dateService = Cc["@mozilla.org/intl/scriptabledateformat;1"].
-                      getService(Ci.nsIScriptableDateFormat);
-    return dateService.FormatDateTime("", dateService.dateFormatLong, dateService.timeFormatNoSeconds,
-                                      dateObj.getFullYear(), dateObj.getMonth()+1, dateObj.getDate(),
-                                      dateObj.getHours(), dateObj.getMinutes(), dateObj.getSeconds());
+    return this._dateFormatter.format(dateObj);
+  },
+
+  __dateFormatter: null,
+  get _dateFormatter() {
+    if (!this.__dateFormatter) {
+      const locale = Cc["@mozilla.org/chrome/chrome-registry;1"]
+                     .getService(Ci.nsIXULChromeRegistry)
+                     .getSelectedLocale("global", true);
+      const dtOptions = { year: 'numeric', month: 'long', day: 'numeric',
+                          hour: 'numeric', minute: 'numeric' };
+      this.__dateFormatter = new Intl.DateTimeFormat(locale, dtOptions);
+    }
+    return this.__dateFormatter;
   },
 
   /**
    * Returns the feed type.
    */
   __feedType: null,
   _getFeedType() {
     if (this.__feedType != null)
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -196,23 +196,23 @@ function openWindow(parent, url, target,
   var stringArgs = null;
   if (args instanceof Array) // array
     stringArgs = args;
   else if (args) // string
     stringArgs = [args];
 
   if (stringArgs) {
     // put the URIs into argArray
-    var uriArray = Components.classes["@mozilla.org/supports-array;1"]
-                       .createInstance(Components.interfaces.nsISupportsArray);
+    var uriArray = Components.classes["@mozilla.org/array;1"]
+                       .createInstance(Components.interfaces.nsIMutableArray);
     stringArgs.forEach(function (uri) {
       var sstring = Components.classes["@mozilla.org/supports-string;1"]
                               .createInstance(nsISupportsString);
       sstring.data = uri;
-      uriArray.AppendElement(sstring);
+      uriArray.appendElement(sstring, /* weak = */ false);
     });
     argArray.appendElement(uriArray, /*weak =*/ false);
   } else {
     argArray.appendElement(null, /*weak =*/ false);
   }
 
   // Pass these as null to ensure that we always trigger the "single URL"
   // behavior in browser.js's gBrowserInit.onLoad (which handles the window
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -406,18 +406,21 @@ var PlacesOrganizer = {
   },
 
   /**
    * Populates the restore menu with the dates of the backups available.
    */
   populateRestoreMenu: function PO_populateRestoreMenu() {
     let restorePopup = document.getElementById("fileRestorePopup");
 
-    let dateSvc = Cc["@mozilla.org/intl/scriptabledateformat;1"].
-                  getService(Ci.nsIScriptableDateFormat);
+    const locale = Cc["@mozilla.org/chrome/chrome-registry;1"]
+                   .getService(Ci.nsIXULChromeRegistry)
+                   .getSelectedLocale("global", true);
+    const dtOptions = { year: 'numeric', month: 'long', day: 'numeric' };
+    let dateFormatter = new Intl.DateTimeFormat(locale, dtOptions);
 
     // Remove existing menu items.  Last item is the restoreFromFile item.
     while (restorePopup.childNodes.length > 1)
       restorePopup.removeChild(restorePopup.firstChild);
 
     Task.spawn(function* () {
       let backupFiles = yield PlacesBackups.getBackupFiles();
       if (backupFiles.length == 0)
@@ -439,23 +442,17 @@ var PlacesOrganizer = {
                      ")";
         } else {
           sizeInfo = " (" + sizeString + ")";
         }
 
         let backupDate = PlacesBackups.getDateForFile(backupFiles[i]);
         let m = restorePopup.insertBefore(document.createElement("menuitem"),
                                           document.getElementById("restoreFromFile"));
-        m.setAttribute("label",
-                       dateSvc.FormatDate("",
-                                          Ci.nsIScriptableDateFormat.dateFormatLong,
-                                          backupDate.getFullYear(),
-                                          backupDate.getMonth() + 1,
-                                          backupDate.getDate()) +
-                                          sizeInfo);
+        m.setAttribute("label", dateFormatter.format(backupDate) + sizeInfo);
         m.setAttribute("value", OS.Path.basename(backupFiles[i]));
         m.setAttribute("oncommand",
                        "PlacesOrganizer.onRestoreMenuItemClick(this);");
       }
 
       // Add the restoreFromFile item.
       restorePopup.insertBefore(document.createElement("menuseparator"),
                                 document.getElementById("restoreFromFile"));
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -28,25 +28,16 @@ PlacesTreeView.prototype = {
   __xulStore: null,
   get _xulStore() {
     if (!this.__xulStore) {
       this.__xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
     }
     return this.__xulStore;
   },
 
-  __dateService: null,
-  get _dateService() {
-    if (!this.__dateService) {
-      this.__dateService = Cc["@mozilla.org/intl/scriptabledateformat;1"].
-                           getService(Ci.nsIScriptableDateFormat);
-    }
-    return this.__dateService;
-  },
-
   QueryInterface: XPCOMUtils.generateQI(PTV_interfaces),
 
   // Bug 761494:
   // ----------
   // Some addons use methods from nsINavHistoryResultObserver and
   // nsINavHistoryResultTreeViewer, without QIing to these interfaces first.
   // That's not a problem when the view is retrieved through the
   // <tree>.view getter (which returns the wrappedJSObject of this object),
@@ -499,26 +490,46 @@ PlacesTreeView.prototype = {
     // milliseconds from today's midnight.
     // getTimezoneOffset corrects that based on local time, notice midnight
     // can have a different offset during DST-change days.
     let dateObj = new Date();
     let now = dateObj.getTime() - dateObj.getTimezoneOffset() * MS_PER_MINUTE;
     let midnight = now - (now % MS_PER_DAY);
     midnight += new Date(midnight).getTimezoneOffset() * MS_PER_MINUTE;
 
-    let dateFormat = timeMs >= midnight ?
-                      Ci.nsIScriptableDateFormat.dateFormatNone :
-                      Ci.nsIScriptableDateFormat.dateFormatShort;
+    let timeObj = new Date(timeMs);
+    return timeMs >= midnight ? this._todayFormatter.format(timeObj)
+                              : this._dateFormatter.format(timeObj);
+  },
 
-    let timeObj = new Date(timeMs);
-    return (this._dateService.FormatDateTime("", dateFormat,
-      Ci.nsIScriptableDateFormat.timeFormatNoSeconds,
-      timeObj.getFullYear(), timeObj.getMonth() + 1,
-      timeObj.getDate(), timeObj.getHours(),
-      timeObj.getMinutes(), timeObj.getSeconds()));
+  // We use a different formatter for times within the current day,
+  // so we cache both a "today" formatter and a general date formatter.
+  __todayFormatter: null,
+  get _todayFormatter() {
+    if (!this.__todayFormatter) {
+      const locale = Cc["@mozilla.org/chrome/chrome-registry;1"]
+                     .getService(Ci.nsIXULChromeRegistry)
+                     .getSelectedLocale("global", true);
+      const dtOptions = { hour: 'numeric', minute: 'numeric' };
+      this.__todayFormatter = new Intl.DateTimeFormat(locale, dtOptions);
+    }
+    return this.__todayFormatter;
+  },
+
+  __dateFormatter: null,
+  get _dateFormatter() {
+    if (!this.__dateFormatter) {
+      const locale = Cc["@mozilla.org/chrome/chrome-registry;1"]
+                     .getService(Ci.nsIXULChromeRegistry)
+                     .getSelectedLocale("global", true);
+      const dtOptions = { year: '2-digit', month: 'numeric', day: 'numeric',
+                          hour: 'numeric', minute: 'numeric' };
+      this.__dateFormatter = new Intl.DateTimeFormat(locale, dtOptions);
+    }
+    return this.__dateFormatter;
   },
 
   COLUMN_TYPE_UNKNOWN: 0,
   COLUMN_TYPE_TITLE: 1,
   COLUMN_TYPE_URI: 2,
   COLUMN_TYPE_DATE: 3,
   COLUMN_TYPE_VISITCOUNT: 4,
   COLUMN_TYPE_DESCRIPTION: 5,
--- a/browser/components/places/tests/chrome/test_treeview_date.xul
+++ b/browser/components/places/tests/chrome/test_treeview_date.xul
@@ -48,19 +48,16 @@
      * https://bugzilla.mozilla.org/show_bug.cgi?id=435322
      *
      * Ensures that date in places treeviews is correctly formatted.
      */
 
     function runTest() {
       SimpleTest.waitForExplicitFinish();
 
-      let ds = Cc["@mozilla.org/intl/scriptabledateformat;1"].
-               getService(Ci.nsIScriptableDateFormat);
-
       function uri(spec) {
         return Services.io.newURI(spec, null, null);
       }
 
       Task.spawn(function* () {
         yield PlacesTestUtils.clearHistory();
 
         let midnight = new Date();
@@ -102,16 +99,19 @@
         tree.place = queryURI;
 
         // loop through the rows and check formatting
         let treeView = tree.view;
         let rc = treeView.rowCount;
         ok(rc >= 3, "Rows found");
         let columns = tree.columns;
         ok(columns.count > 0, "Columns found");
+        const locale = Cc["@mozilla.org/chrome/chrome-registry;1"]
+                       .getService(Ci.nsIXULChromeRegistry)
+                       .getSelectedLocale("global", true);
         for (let r = 0; r < rc; r++) {
           let node = treeView.nodeForTreeIndex(r);
           ok(node, "Places node found");
           for (let ci = 0; ci < columns.count; ci++) {
             let c = columns.getColumnAt(ci);
             let text = treeView.getCellText(r, c);
             switch (c.element.getAttribute("anonid")) {
               case "title":
@@ -122,33 +122,28 @@
                   is(text, node.title, "Title is correct");
                 break;
               case "url":
                 is(text, node.uri, "Uri is correct");
                 break;
               case "date":
                 let timeObj = new Date(node.time / 1000);
                 // Default is short date format.
-                let dateFormat = Ci.nsIScriptableDateFormat.dateFormatShort;
+                let dtOptions = { year: '2-digit', month: 'numeric', day: 'numeric',
+                                  hour: 'numeric', minute: 'numeric' };
                 // For today's visits we don't show date portion.
                 if (node.uri == "http://at.midnight.com/" ||
-                    node.uri == "http://after.midnight.com/")
-                  dateFormat = Ci.nsIScriptableDateFormat.dateFormatNone;
-                else if (node.uri == "http://before.midnight.com/")
-                  dateFormat = Ci.nsIScriptableDateFormat.dateFormatShort;
-                else {
+                    node.uri == "http://after.midnight.com/") {
+                  dtOptions = { hour: 'numeric', minute: 'numeric' };
+                } else if (node.uri != "http://before.midnight.com/") {
                   // Avoid to test spurious uris, due to how the test works
                   // a redirecting uri could be put in the tree while we test.
                   break;
                 }
-                let timeStr = ds.FormatDateTime("", dateFormat,
-                      Ci.nsIScriptableDateFormat.timeFormatNoSeconds,
-                      timeObj.getFullYear(), timeObj.getMonth() + 1,
-                      timeObj.getDate(), timeObj.getHours(),
-                      timeObj.getMinutes(), timeObj.getSeconds())
+                let timeStr = timeObj.toLocaleString(locale, dtOptions);
 
                 is(text, timeStr, "Date format is correct");
                 break;
               case "visitCount":
                 is(text, 1, "Visit count is correct");
                 break;
             }
           }
--- a/browser/components/preferences/cookies.js
+++ b/browser/components/preferences/cookies.js
@@ -11,18 +11,16 @@ Components.utils.import("resource://gre/
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
                                   "resource://gre/modules/ContextualIdentityService.jsm");
 
 var gCookiesWindow = {
   _cm               : Components.classes["@mozilla.org/cookiemanager;1"]
                                 .getService(Components.interfaces.nsICookieManager),
-  _ds               : Components.classes["@mozilla.org/intl/scriptabledateformat;1"]
-                                .getService(Components.interfaces.nsIScriptableDateFormat),
   _hosts            : {},
   _hostOrder        : [],
   _tree             : null,
   _bundle           : null,
 
   init: function () {
     var os = Components.classes["@mozilla.org/observer-service;1"]
                        .getService(Components.interfaces.nsIObserverService);
@@ -508,24 +506,22 @@ var gCookiesWindow = {
         break;
     }
     this._view._rowCount = hostCount.value;
   },
 
   formatExpiresString: function (aExpires) {
     if (aExpires) {
       var date = new Date(1000 * aExpires);
-      return this._ds.FormatDateTime("", this._ds.dateFormatLong,
-                                     this._ds.timeFormatSeconds,
-                                     date.getFullYear(),
-                                     date.getMonth() + 1,
-                                     date.getDate(),
-                                     date.getHours(),
-                                     date.getMinutes(),
-                                     date.getSeconds());
+      const locale = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+                     .getService(Components.interfaces.nsIXULChromeRegistry)
+                     .getSelectedLocale("global", true);
+      const dtOptions = { year: 'numeric', month: 'long', day: 'numeric',
+                          hour: 'numeric', minute: 'numeric', second: 'numeric' };
+      return date.toLocaleString(locale, dtOptions);
     }
     return this._bundle.getString("expireAtEndOfSession");
   },
 
   _getUserContextString: function(aUserContextId) {
     if (parseInt(aUserContextId) == 0) {
       return this._bundle.getString("defaultUserContextLabel");
     }
--- a/browser/components/preferences/in-content/tests/browser_advanced_update.js
+++ b/browser/components/preferences/in-content/tests/browser_advanced_update.js
@@ -3,18 +3,16 @@
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, manager: Cm, utils: Cu, results: Cr } = Components;
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 
 const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
-const dateFormat = Cc["@mozilla.org/intl/scriptabledateformat;1"]
-                      .getService(Components.interfaces.nsIScriptableDateFormat);
 
 const mockUpdateManager = {
   contractId: "@mozilla.org/updates/update-manager;1",
 
   _mockClassId: uuidGenerator.generateUUID(),
 
   _originalClassId: "",
 
@@ -82,25 +80,22 @@ const mockUpdateManager = {
 };
 
 function resetPreferences() {
   Services.prefs.clearUserPref("browser.search.update");
 }
 
 function formatInstallDate(sec) {
   var date = new Date(sec);
-  return dateFormat.FormatDateTime("",
-    dateFormat.dateFormatLong,
-    dateFormat.timeFormatSeconds,
-    date.getFullYear(),
-    date.getMonth() + 1,
-    date.getDate(),
-    date.getHours(),
-    date.getMinutes(),
-    date.getSeconds());
+  const locale = Cc["@mozilla.org/chrome/chrome-registry;1"]
+                 .getService(Ci.nsIXULChromeRegistry)
+                 .getSelectedLocale("global", true);
+  const dtOptions = { year: 'numeric', month: 'long', day: 'numeric',
+                      hour: 'numeric', minute: 'numeric', second: 'numeric' };
+  return date.toLocaleString(locale, dtOptions);
 }
 
 registerCleanupFunction(resetPreferences);
 
 add_task(function*() {
   yield openPreferencesViaOpenPreferencesAPI("advanced", "updateTab", { leaveOpen: true });
   resetPreferences();
   Services.prefs.setBoolPref("browser.search.update", false);
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -50,14 +50,15 @@ MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3
 ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
 # The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
 MAR_CHANNEL_ID=firefox-mozilla-central
 MOZ_PROFILE_MIGRATOR=1
 MOZ_APP_STATIC_INI=1
 MOZ_WEBGL_CONFORMANT=1
 MOZ_JSDOWNLOADS=1
 MOZ_RUST_MP4PARSE=1
+MOZ_RUST_URLPARSE=1
 
 # Enable checking that add-ons are signed by the trusted root
 MOZ_ADDON_SIGNING=1
 
 # Include the DevTools client, not just the server (which is the default)
 MOZ_DEVTOOLS=all
new file mode 100644
--- /dev/null
+++ b/browser/extensions/aushelper/bootstrap.js
@@ -0,0 +1,129 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+const APP_UPDATE_URL_PREF = "app.update.url";
+const REPLACE_KEY = "%OS_VERSION%";
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/TelemetryLog.jsm");
+
+function startup() {
+  if (Services.appinfo.OS != "WINNT") {
+    return;
+  }
+
+  const regCPUPath = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
+  let wrk;
+  try {
+    wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(Ci.nsIWindowsRegKey);
+    wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE, regCPUPath, wrk.ACCESS_READ);
+  } catch (e) {
+    Cu.reportError("Unable to open registry. Exception: " + e);
+    TelemetryLog.log("AUSHELPER_FATAL_ERROR", [e]);
+  }
+
+  // If any of the following values are successfully retrieved and they don't
+  // match the condition for that value then it is safe to update. Hence why the
+  // following checks are somewhat convoluted. The possible values for the
+  // variable set by each check is as follows:
+  //
+  //          | Match | No Match | Error |
+  // variable |  true |   false  |  null |
+
+  let cpuVendorIDMatch = false;
+  try {
+    let cpuVendorID = wrk.readStringValue("VendorIdentifier");
+    if (cpuVendorID.toLowerCase() == "genuineintel") {
+      cpuVendorIDMatch = true;
+    }
+  } catch (e) {
+    cpuVendorIDMatch = null;
+    Cu.reportError("Error getting CPU vendor indentifier. Exception: " + e);
+    TelemetryLog.log("AUSHELPER_CPU_VENDOR_ID_ERROR", [e]);
+  }
+
+  let cpuIDMatch = false;
+  try {
+    let cpuID = wrk.readStringValue("Identifier");
+    if (cpuID.toLowerCase().indexOf("family 6 model 61 stepping 4") != -1) {
+      cpuIDMatch = true;
+    }
+  } catch (e) {
+    cpuIDMatch = null;
+    Cu.reportError("Error getting CPU indentifier. Exception: " + e);
+    TelemetryLog.log("AUSHELPER_CPU_ID_ERROR", [e]);
+  }
+
+  let microCodeVersions = [0xe, 0x11, 0x12, 0x13, 0x16, 0x18, 0x19];
+  let cpuRevMatch = false;
+  try {
+    let keyNames = ["Update Revision", "Update Signature"];
+    for (let i = 0; i < keyNames.length; ++i) {
+      try {
+        let regVal = wrk.readBinaryValue(keyNames[i]);
+        if (regVal.length == 8) {
+          let hexVal = [];
+          // We are only inyterested in the highest byte and return the little
+          // endian value for it.
+          for (let j = 4; j < 8; j++) {
+            let c = regVal.charCodeAt(j).toString(16);
+            if (c.length == 1) {
+              c = "0" + c;
+            }
+            hexVal.unshift(c);
+          }
+          if (microCodeVersions.indexOf(parseInt(hexVal.join(''))) != -1) {
+            cpuRevMatch = true;
+          }
+          break;
+        }
+      } catch (e) {
+        if (i == keyNames.length - 1) {
+          // The registry key name's value was not successfully queried.
+          cpuRevMatch = null;
+          Cu.reportError("Error getting CPU revision. Exception: " + e);
+          TelemetryLog.log("AUSHELPER_CPU_REV_ERROR", [e]);
+        }
+      }
+    }
+  } catch (ex) {
+    cpuRevMatch = null;
+    Cu.reportError("Error getting CPU revision. Exception: " + ex);
+    TelemetryLog.log("AUSHELPER_CPU_REV_ERROR", [ex]);
+  }
+
+  let resultCode = 3;
+  let newValue = "(unkBug1296630v1)";
+  // The following uses strict equality checks since the values can be true,
+  // false, or null.
+  if (cpuVendorIDMatch === false || cpuIDMatch === false || cpuRevMatch === false) {
+    // Since one of the values is false then the system won't be affected by
+    // bug 1296630 according to the conditions set out in bug 1311515.
+    newValue = "(noBug1296630v1)";
+    resultCode = 0;
+  } else if (cpuVendorIDMatch === null || cpuIDMatch === null || cpuRevMatch === null) {
+    // Since one of the values is null we can't say for sure if the system will
+    // be affected by bug 1296630.
+    newValue = "(errBug1296630v1)";
+    resultCode = 2;
+  } else if (cpuVendorIDMatch === true && cpuIDMatch === true && cpuRevMatch === true) {
+    // Since all of the values are true we can say that the system will be
+    // affected by bug 1296630.
+    newValue = "(yesBug1296630v1)";
+    resultCode = 1;
+  }
+
+  let defaultBranch = Services.prefs.getDefaultBranch("");
+  let curPrefValue = defaultBranch.getCharPref(APP_UPDATE_URL_PREF);
+  let newPrefValue = curPrefValue.replace(REPLACE_KEY + "/", REPLACE_KEY + newValue + "/");
+  defaultBranch.setCharPref(APP_UPDATE_URL_PREF, newPrefValue);
+  TelemetryLog.log("AUSHELPER_RESULT", [resultCode]);
+}
+
+function shutdown() {}
+function install() {}
+function uninstall() {}
new file mode 100644
--- /dev/null
+++ b/browser/extensions/aushelper/install.rdf.in
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+#filter substitution
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>aushelper@mozilla.org</em:id>
+    <em:version>1.0</em:version>
+    <em:type>2</em:type>
+    <em:bootstrap>true</em:bootstrap>
+    <em:multiprocessCompatible>true</em:multiprocessCompatible>
+
+    <!-- Target Application this extension can install into,
+        with minimum and maximum supported versions. -->
+    <em:targetApplication>
+      <Description>
+        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+        <em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
+        <em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+    <!-- Front End MetaData -->
+    <em:name>Application Update Service Helper</em:name>
+    <em:description>Sets value(s) in the update url based on custom checks.</em:description>
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/browser/extensions/aushelper/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
+DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
+
+FINAL_TARGET_FILES.features['aushelper@mozilla.org'] += [
+  'bootstrap.js'
+]
+
+FINAL_TARGET_PP_FILES.features['aushelper@mozilla.org'] += [
+  'install.rdf.in'
+]
--- a/browser/extensions/moz.build
+++ b/browser/extensions/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
+    'aushelper',
     'e10srollout',
     'pdfjs',
     'pocket',
     'webcompat',
 ]
 
 # Only include the following system add-ons if building Aurora or Nightly
 if 'a' in CONFIG['GRE_MILESTONE']:
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -809,17 +809,17 @@ bin/libfreebl_32int64_3.so
 
 #if defined(CLANG_CXX)
 #if defined(MOZ_ASAN) || defined(MOZ_TSAN)
 @BINPATH@/llvm-symbolizer
 #endif
 #endif
 
 #if defined(MOZ_ASAN) && defined(CLANG_CL)
-@BINPATH@/clang_rt.asan_dynamic-i386.dll
+@BINPATH@/clang_rt.asan_dynamic-*.dll
 #endif
 
 
 ; media
 @RESPATH@/gmp-clearkey/0.1/@DLL_PREFIX@clearkey@DLL_SUFFIX@
 @RESPATH@/gmp-clearkey/0.1/clearkey.info
 
 ; gfx
--- a/browser/themes/osx/places/organizer.css
+++ b/browser/themes/osx/places/organizer.css
@@ -56,16 +56,32 @@
 #placesList > treechildren::-moz-tree-twisty(open) {
   list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
 }
 
 #placesList > treechildren::-moz-tree-twisty(open, selected) {
   list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
 }
 
+#placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty {
+  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-rtl");
+}
+
+#placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(selected) {
+  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl");
+}
+
+#placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(open) {
+  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
+}
+
+#placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(open, selected) {
+  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
+}
+
 @media (-moz-mac-yosemite-theme) {
   #placesList > treechildren::-moz-tree-cell-text(selected) {
     color: -moz-dialogtext;
     font-weight: 500;
   }
 
   #placesList > treechildren::-moz-tree-cell-text(selected, focus) {
     color: #fff;
@@ -81,16 +97,32 @@
 
   #placesList > treechildren::-moz-tree-twisty(open, selected) {
     list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
   }
 
   #placesList > treechildren::-moz-tree-twisty(open, selected, focus) {
     list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
   }
+
+  #placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(selected) {
+    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-rtl");
+  }
+
+  #placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(selected, focus) {
+    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl");
+  }
+
+  #placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(open, selected) {
+    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
+  }
+
+  #placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(open, selected, focus) {
+    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
+  }
 }
 
 #placesToolbar {
   padding: 0 4px 3px;
 }
 
 #placesView {
   border-top: none !important;
--- a/browser/themes/osx/places/places.css
+++ b/browser/themes/osx/places/places.css
@@ -74,16 +74,32 @@
 .sidebar-placesTreechildren::-moz-tree-twisty(open) {
   list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
 }
 
 .sidebar-placesTreechildren::-moz-tree-twisty(open, selected) {
   list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
 }
 
+.sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty {
+  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-rtl");
+}
+
+.sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(selected) {
+  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl");
+}
+
+.sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(open) {
+  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
+}
+
+.sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(open, selected) {
+  list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
+}
+
 @media (-moz-mac-yosemite-theme) {
   .sidebar-placesTreechildren::-moz-tree-cell-text(selected) {
     color: -moz-dialogtext;
     font-weight: 500;
   }
 
   .sidebar-placesTreechildren::-moz-tree-cell-text(selected, focus) {
     color: #fff;
@@ -99,16 +115,32 @@
 
   .sidebar-placesTreechildren::-moz-tree-twisty(open, selected) {
     list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
   }
 
   .sidebar-placesTreechildren::-moz-tree-twisty(open, selected, focus) {
     list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
   }
+
+  .sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(selected) {
+    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-rtl");
+  }
+
+  .sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(selected, focus) {
+    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl")
+  }
+
+  .sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(open, selected) {
+    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
+  }
+
+  .sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(open, selected, focus) {
+    list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
+  }
 }
 
 #viewButton {
   -moz-appearance: none;
   padding-bottom: 1px;
   padding-inline-start: 5px;
   padding-inline-end: 0px;
   margin: 0;
--- a/browser/themes/osx/syncedtabs/sidebar.css
+++ b/browser/themes/osx/syncedtabs/sidebar.css
@@ -47,16 +47,32 @@
 .item.client.selected:focus .item-twisty-container {
   background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
 }
 
 .item.client.selected.closed:focus .item-twisty-container {
   background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted");
 }
 
+.item.client .item-twisty-container:dir(rtl) {
+  background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
+}
+
+.item.client.closed .item-twisty-container:dir(rtl) {
+  background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-rtl");
+}
+
+.item.client.selected:focus .item-twisty-container:dir(rtl) {
+  background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
+}
+
+.item.client.selected.closed:focus .item-twisty-container:dir(rtl) {
+  background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl");
+}
+
 @media (-moz-mac-yosemite-theme) {
   .item.selected > .item-title-container {
     color: -moz-dialogtext;
     font-weight: 500;
   }
 
   .item.selected:focus > .item-title-container {
     color: #fff;
--- a/build/autoconf/sanitize.m4
+++ b/build/autoconf/sanitize.m4
@@ -9,18 +9,22 @@ dnl = Use Address Sanitizer
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(address-sanitizer,
 [  --enable-address-sanitizer       Enable Address Sanitizer (default=no)],
     MOZ_ASAN=1,
     MOZ_ASAN= )
 if test -n "$MOZ_ASAN"; then
     MOZ_LLVM_HACKS=1
     if test -n "$CLANG_CL"; then
-        # Look for clang_rt.asan_dynamic-i386.dll
-        MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-i386.dll
+        # Look for the ASan runtime binary
+        if test "$CPU_ARCH" = "x86_64"; then
+          MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-x86_64.dll
+        else
+          MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-i386.dll
+        fi
         # We use MOZ_PATH_PROG in order to get a Windows style path.
         MOZ_PATH_PROG(MOZ_CLANG_RT_ASAN_LIB_PATH, $MOZ_CLANG_RT_ASAN_LIB)
         if test -z "$MOZ_CLANG_RT_ASAN_LIB_PATH"; then
             AC_MSG_ERROR([Couldn't find $MOZ_CLANG_RT_ASAN_LIB.  It should be available in the same location as clang-cl.])
         fi
         AC_SUBST(MOZ_CLANG_RT_ASAN_LIB_PATH)
     fi
     CFLAGS="-fsanitize=address $CFLAGS"
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -239,17 +239,17 @@ class Automation(object):
     # enable non-local connections for the purposes of local testing.  Don't
     # override the user's choice here.  See bug 1049688.
     env.setdefault('MOZ_DISABLE_NONLOCAL_CONNECTIONS', '1')
 
     env['GNOME_DISABLE_CRASH_DIALOG'] = '1'
     env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
 
     # Set WebRTC logging in case it is not set yet
-    env.setdefault('MOZ_LOG', 'signaling:3,mtransport:4,datachannel:4,jsep:4,MediaPipelineFactory:4')
+    env.setdefault('MOZ_LOG', 'signaling:3,mtransport:4,DataChannel:4,jsep:4,MediaPipelineFactory:4')
     env.setdefault('R_LOG_LEVEL', '6')
     env.setdefault('R_LOG_DESTINATION', 'stderr')
     env.setdefault('R_LOG_VERBOSE', '1')
 
     # ASan specific environment stuff
     if self.IS_ASAN and (self.IS_LINUX or self.IS_MAC):
       # Symbolizer support
       llvmsym = os.path.join(xrePath, "llvm-symbolizer")
--- a/build/docs/index.rst
+++ b/build/docs/index.rst
@@ -21,16 +21,17 @@ Important Concepts
    python
    test_manifests
    mozinfo
    preprocessor
    jar-manifests
    defining-binaries
    toolchains
    locales
+   rust
 
 integrated development environment (IDE)
 ========================================
 .. toctree::
    :maxdepth: 1
 
    androideclipse
    cppeclipse
new file mode 100644
--- /dev/null
+++ b/build/docs/rust.rst
@@ -0,0 +1,78 @@
+.. _rust:
+
+==============================
+Including Rust Code in Firefox
+==============================
+
+The build system has support for building and linking Rust crates.
+Rust code is built using ``cargo`` in the typical way, so it is
+straightforward to take an existing Rust crate and integrate it
+into Firefox.
+
+.. important::
+
+   Rust code is not currently enabled by default in Firefox builds.
+   This should change soon (`bug 1283898 <https://bugzilla.mozilla.org/show_bug.cgi?id=1283898>`_),
+   but the option to build without Rust code will likely last a little longer
+   (`bug 1284816 <https://bugzilla.mozilla.org/show_bug.cgi?id=1284816>`_),
+   so Rust code cannot currently be used for required components.
+
+
+Linking Rust Crates into libxul
+===============================
+
+Rust crates that you want to link into libxul should be listed in the
+``dependencies`` section of `toolkit/library/rust/shared/Cargo.toml <https://dxr.mozilla.org/mozilla-central/source/toolkit/library/rust/shared/Cargo.toml>`_.
+This ensures that the Rust code will be linked properly into libxul as well
+as the copy of libxul used for gtests.
+
+Linking Rust Crates into something else
+=======================================
+
+There currently is not any Rust code being linked into binaries other than
+libxul. If you would like to do so, you'll need to create a directory with
+a ``Cargo.toml`` file for your crate, and a ``moz.build`` file that contains:
+
+.. code-block:: python
+
+    RustLibrary('crate_name')
+
+Where *crate_name* matches the name from the ``[package]`` section of your
+``Cargo.toml``. You can refer to `the moz.build file <https://dxr.mozilla.org/mozilla-central/rev/3f4c3a3cabaf94958834d3a8935adfb4a887942d/toolkit/library/rust/moz.build#7>`_ and `the Cargo.toml file <https://dxr.mozilla.org/mozilla-central/rev/3f4c3a3cabaf94958834d3a8935adfb4a887942d/toolkit/library/rust/Cargo.toml>`_ that are used for libxul.
+
+You can then add ``USE_LIBS += ['crate_name']`` to the ``moz.build`` file
+that defines the binary as you would with any other library in the tree.
+
+.. important::
+
+    You cannot link a Rust crate into an intermediate library that will wind
+    up being linked into libxul. The build system enforces that only a single
+    ``RustLibrary`` may be linked into a binary. If you need to do this, you
+    will have to add a ``RustLibrary`` to link to any standalone binaries that
+    link the intermediate library, and also add the Rust crate to the libxul
+    dependencies as in `linking Rust Crates into libxul`_.
+
+Where Should I put my Crate?
+============================
+
+If your crate's canonical home is mozilla-central, you can put it next to the
+other code in the module it belongs to.
+
+If your crate is mirrored into mozilla-central from another repository, and
+will not be actively developed in mozilla-central, you can simply list it
+as a ``crates.io``-style dependency with a version number, and let it be
+vendored into the ``third_party/rust`` directory.
+
+If your crate is mirrored into mozilla-central from another repository, but
+will be actively developed in both locations, you should send mail to the
+dev-builds mailing list to start a discussion on how to meet your needs.
+
+
+Crate dependencies
+==================
+
+All dependencies for in-tree Rust crates are vendored into the
+``third_party/rust`` directory. Currently if you add a dependency on a new
+crate you must run ``mach vendor rust`` to vendor the dependencies into
+that directory. In the future we hope to make it so that you only need to
+vendor the dependencies in order to build your changes in a CI push.
--- a/chrome/nsChromeRegistry.cpp
+++ b/chrome/nsChromeRegistry.cpp
@@ -27,16 +27,20 @@
 #include "nsIObserverService.h"
 #include "nsIPresShell.h"
 #include "nsIScriptError.h"
 #include "nsIWindowMediator.h"
 #include "nsIPrefService.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 
+#ifdef ENABLE_INTL_API
+#include "unicode/uloc.h"
+#endif
+
 nsChromeRegistry* nsChromeRegistry::gChromeRegistry;
 
 // DO NOT use namespace mozilla; it'll break due to a naming conflict between
 // mozilla::TextRange and a TextRange in OSX headers.
 using mozilla::StyleSheet;
 using mozilla::dom::IsChromeURI;
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -641,16 +645,21 @@ nsChromeRegistry::MustLoadURLRemotely(ns
     *aResult = !!(flags & REMOTE_REQUIRED);
   }
   return NS_OK;
 }
 
 bool
 nsChromeRegistry::GetDirectionForLocale(const nsACString& aLocale)
 {
+#ifdef ENABLE_INTL_API
+  nsAutoCString locale(aLocale);
+  SanitizeForBCP47(locale);
+  return uloc_isRightToLeft(locale.get());
+#else
   // first check the intl.uidirection.<locale> preference, and if that is not
   // set, check the same preference but with just the first two characters of
   // the locale. If that isn't set, default to left-to-right.
   nsAutoCString prefString = NS_LITERAL_CSTRING("intl.uidirection.") + aLocale;
   nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (!prefBranch) {
     return false;
   }
@@ -661,16 +670,17 @@ nsChromeRegistry::GetDirectionForLocale(
     int32_t hyphen = prefString.FindChar('-');
     if (hyphen >= 1) {
       nsAutoCString shortPref(Substring(prefString, 0, hyphen));
       prefBranch->GetCharPref(shortPref.get(), getter_Copies(dir));
     }
   }
 
   return dir.EqualsLiteral("rtl");
+#endif
 }
 
 NS_IMETHODIMP_(bool)
 nsChromeRegistry::WrappersEnabled(nsIURI *aURI)
 {
   nsCOMPtr<nsIURL> chromeURL (do_QueryInterface(aURI));
   if (!chromeURL)
     return false;
@@ -704,8 +714,37 @@ nsChromeRegistry::GetSingleton()
   else
     cr = new nsChromeRegistryChrome();
 
   if (NS_FAILED(cr->Init()))
     return nullptr;
 
   return cr.forget();
 }
+
+void
+nsChromeRegistry::SanitizeForBCP47(nsACString& aLocale)
+{
+#ifdef ENABLE_INTL_API
+  // Currently, the only locale code we use that's not BCP47-conformant is
+  // "ja-JP-mac" on OS X, but let's try to be more general than just
+  // hard-coding that here.
+  const int32_t LANG_TAG_CAPACITY = 128;
+  char langTag[LANG_TAG_CAPACITY];
+  nsAutoCString locale(aLocale);
+  UErrorCode err = U_ZERO_ERROR;
+  // This is a fail-safe method that will set langTag to "und" if it cannot
+  // match any part of the input locale code.
+  int32_t len = uloc_toLanguageTag(locale.get(), langTag, LANG_TAG_CAPACITY,
+                                   false, &err);
+  if (U_SUCCESS(err) && len > 0) {
+    aLocale.Assign(langTag, len);
+  }
+#else
+  // This is only really needed for Intl API purposes, AFAIK,
+  // so probably won't be used in a non-ENABLE_INTL_API build.
+  // But let's fix up the single anomalous code we actually ship,
+  // just in case:
+  if (aLocale.EqualsLiteral("ja-JP-mac")) {
+    aLocale.AssignLiteral("ja-JP");
+  }
+#endif
+}
--- a/chrome/nsChromeRegistry.h
+++ b/chrome/nsChromeRegistry.h
@@ -96,16 +96,18 @@ protected:
   nsresult SelectLocaleFromPref(nsIPrefBranch* prefs);
 
   static nsresult RefreshWindow(nsPIDOMWindowOuter* aWindow);
   static nsresult GetProviderAndPath(nsIURL* aChromeURL,
                                      nsACString& aProvider, nsACString& aPath);
 
   bool GetDirectionForLocale(const nsACString& aLocale);
 
+  void SanitizeForBCP47(nsACString& aLocale);
+
 public:
   static already_AddRefed<nsChromeRegistry> GetSingleton();
 
   struct ManifestProcessingContext
   {
     ManifestProcessingContext(NSLocationType aType, mozilla::FileLocation &aFile)
       : mType(aType)
       , mFile(aFile)
--- a/chrome/nsChromeRegistryChrome.cpp
+++ b/chrome/nsChromeRegistryChrome.cpp
@@ -219,40 +219,45 @@ getUILangCountry(nsACString& aUILang)
 }
 
 NS_IMETHODIMP
 nsChromeRegistryChrome::IsLocaleRTL(const nsACString& package, bool *aResult)
 {
   *aResult = false;
 
   nsAutoCString locale;
-  GetSelectedLocale(package, locale);
+  GetSelectedLocale(package, false, locale);
   if (locale.Length() < 2)
     return NS_OK;
 
   *aResult = GetDirectionForLocale(locale);
   return NS_OK;
 }
 
 nsresult
 nsChromeRegistryChrome::GetSelectedLocale(const nsACString& aPackage,
+                                          bool aAsBCP47,
                                           nsACString& aLocale)
 {
   nsCString realpackage;
   nsresult rv = OverrideLocalePackage(aPackage, realpackage);
   if (NS_FAILED(rv))
     return rv;
   PackageEntry* entry;
   if (!mPackagesHash.Get(realpackage, &entry))
     return NS_ERROR_FILE_NOT_FOUND;
 
   aLocale = entry->locales.GetSelected(mSelectedLocale, nsProviderArray::LOCALE);
   if (aLocale.IsEmpty())
     return NS_ERROR_FAILURE;
 
+  if (aAsBCP47) {
+    SanitizeForBCP47(aLocale);
+  }
+
   return NS_OK;
 }
 
 nsresult
 nsChromeRegistryChrome::OverrideLocalePackage(const nsACString& aPackage,
                                               nsACString& aOverride)
 {
   const nsACString& pref = NS_LITERAL_CSTRING(PACKAGE_OVERRIDE_BRANCH) + aPackage;
--- a/chrome/nsChromeRegistryChrome.h
+++ b/chrome/nsChromeRegistryChrome.h
@@ -31,16 +31,17 @@ class nsChromeRegistryChrome : public ns
 
   NS_IMETHOD CheckForNewChrome() override;
   NS_IMETHOD CheckForOSAccessibility() override;
   NS_IMETHOD GetLocalesForPackage(const nsACString& aPackage,
                                   nsIUTF8StringEnumerator* *aResult) override;
   NS_IMETHOD IsLocaleRTL(const nsACString& package,
                          bool *aResult) override;
   NS_IMETHOD GetSelectedLocale(const nsACString& aPackage,
+                               bool aAsBCP47,
                                nsACString& aLocale) override;
   NS_IMETHOD Observe(nsISupports *aSubject, const char *aTopic,
                      const char16_t *someData) override;
 
 #ifdef MOZ_XUL
   NS_IMETHOD GetXULOverlays(nsIURI *aURI,
                             nsISimpleEnumerator **_retval) override;
   NS_IMETHOD GetStyleOverlays(nsIURI *aURI,
--- a/chrome/nsChromeRegistryContent.cpp
+++ b/chrome/nsChromeRegistryContent.cpp
@@ -217,23 +217,27 @@ nsChromeRegistryContent::IsLocaleRTL(con
     return NS_ERROR_NOT_AVAILABLE;
   }
   *aResult = GetDirectionForLocale(mLocale);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsChromeRegistryContent::GetSelectedLocale(const nsACString& aPackage,
+                                           bool aAsBCP47,
                                            nsACString& aLocale)
 {
   if (aPackage != nsDependentCString("global")) {
     NS_ERROR("Uh-oh, caller wanted something other than 'some local'");
     return NS_ERROR_NOT_AVAILABLE;
   }
   aLocale = mLocale;
+  if (aAsBCP47) {
+    SanitizeForBCP47(aLocale);
+  }
   return NS_OK;
 }
   
 NS_IMETHODIMP
 nsChromeRegistryContent::Observe(nsISupports* aSubject, const char* aTopic,
                                  const char16_t* aData)
 {
   CONTENT_NOT_IMPLEMENTED();
--- a/chrome/nsChromeRegistryContent.h
+++ b/chrome/nsChromeRegistryContent.h
@@ -28,16 +28,17 @@ class nsChromeRegistryContent : public n
                                   nsIUTF8StringEnumerator* *aResult) override;
   NS_IMETHOD CheckForNewChrome() override;
   NS_IMETHOD CheckForOSAccessibility() override;
   NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
                      const char16_t* aData) override;
   NS_IMETHOD IsLocaleRTL(const nsACString& package,
                          bool *aResult) override;
   NS_IMETHOD GetSelectedLocale(const nsACString& aPackage,
+                               bool aAsBCP47,
                                nsACString& aLocale) override;
   NS_IMETHOD GetStyleOverlays(nsIURI *aChromeURL,
                               nsISimpleEnumerator **aResult) override;
   NS_IMETHOD GetXULOverlays(nsIURI *aChromeURL,
                             nsISimpleEnumerator **aResult) override;
 
   void RegisterPackage(const ChromePackage& aPackage);
   void RegisterOverride(const OverrideMapping& aOverride);
--- a/chrome/nsIChromeRegistry.idl
+++ b/chrome/nsIChromeRegistry.idl
@@ -45,18 +45,23 @@ interface nsIChromeRegistry : nsISupport
   [notxpcom] boolean wrappersEnabled(in nsIURI aURI);
 };
 
 [scriptable, uuid(93251ddf-5e85-4172-ac2a-31780562974f)]
 interface nsIXULChromeRegistry : nsIChromeRegistry
 {
   /* Should be called when locales change to reload all chrome (including XUL). */
   void reloadChrome();
-  
-  ACString getSelectedLocale(in ACString packageName);
+
+  // If the optional asBCP47 parameter is true, the locale code will be
+  // converted to a BCP47 language tag; in particular, this means that
+  // "ja-JP-mac" will be returned as "ja-JP-x-lvariant-mac", which can be
+  // passed to ECMA402 Intl API methods without throwing a RangeError.
+  ACString getSelectedLocale(in ACString packageName,
+                             [optional] in boolean asBCP47);
   
   // Get the direction of the locale via the intl.uidirection.<locale> pref
   boolean isLocaleRTL(in ACString package);
 
   /* Should be called when skins change. Reloads only stylesheets. */
   void refreshSkins();
 
   /**
--- a/devtools/client/responsive.html/browser/tunnel.js
+++ b/devtools/client/responsive.html/browser/tunnel.js
@@ -237,18 +237,18 @@ function tunnelToInnerBrowser(outer, inn
       let { detail } = event;
       event.preventDefault();
       let uri = Services.io.newURI(detail.url, null, null);
       // This API is used mainly because it's near the path used for <a target/> with
       // regular browser tabs (which calls `openURIInFrame`).  The more elaborate APIs
       // that support openers, window features, etc. didn't seem callable from JS and / or
       // this event doesn't give enough info to use them.
       browserWindow.browserDOMWindow
-                   .openURI(uri, null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
-                            Ci.nsIBrowserDOMWindow.OPEN_NEWTAB);
+        .openURI(uri, null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
+                 Ci.nsIBrowserDOMWindow.OPEN_NEW);
     },
 
     stop() {
       let tab = gBrowser.getTabForBrowser(outer);
       let filteredProgressListener = gBrowser._tabFilters.get(tab);
 
       // The browser's state has changed over time while the tunnel was active.  Push the
       // the current state down to the inner browser, so that it follows the content in
--- a/devtools/client/shared/components/reps/rep-utils.js
+++ b/devtools/client/shared/components/reps/rep-utils.js
@@ -43,16 +43,23 @@ define(function (require, exports, modul
   function cropString(text, limit, alternativeText) {
     if (!alternativeText) {
       alternativeText = "\u2026";
     }
 
     // Make sure it's a string.
     text = text + "";
 
+    // Replace all non-printable characters, except of
+    // (horizontal) tab (HT: \x09) and newline (LF: \x0A, CR: \x0D),
+    // with unicode replacement character (u+fffd).
+    // eslint-disable-next-line no-control-regex
+    let re = new RegExp("[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\x9F]", "g");
+    text = text.replace(re, "\ufffd");
+
     // Crop the string only if a limit is actually specified.
     if (!limit || limit <= 0) {
       return text;
     }
 
     // Set the limit at least to the length of the alternative text
     // plus one character of the original text.
     if (limit <= alternativeText.length) {
--- a/devtools/client/shared/components/tabs/tabs.css
+++ b/devtools/client/shared/components/tabs/tabs.css
@@ -37,34 +37,26 @@
 
 .tabs .tab-panel {
   height: 100%;
 }
 
 .tabs .all-tabs-menu  {
   position: absolute;
   top: 0;
-  right: 0;
+  offset-inline-end: 0;
   width: 15px;
   height: 100%;
-  border-style: solid;
-  border-width: 0;
-  border-inline-start-width: 1px;
-  border-color: var(--theme-splitter-color);
+  border-inline-start: 1px solid var(--theme-splitter-color);
   background: url("chrome://devtools/skin/images/dropmarker.svg");
   background-repeat: no-repeat;
   background-position: center;
   background-color: var(--theme-tab-toolbar-background);
 }
 
-.tabs .all-tabs-menu:-moz-locale-dir(rtl) {
-  right: unset;
-  left: 0;
-}
-
 /* Light Theme */
 
 .theme-dark .tabs,
 .theme-light .tabs {
   background: var(--theme-body-background);
 }
 
 .theme-dark .tabs .tabs-navigation,
--- a/devtools/client/shared/components/test/mochitest/test_reps_string.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_string.html
@@ -23,16 +23,17 @@ window.onload = Task.async(function* () 
     const renderedRep = shallowRenderComponent(Rep, { object: getGripStub("testMultiline") });
     is(renderedRep.type, StringRep.rep, `Rep correctly selects ${StringRep.rep.displayName}`);
 
     // Test rendering
     yield testMultiline();
     yield testMultilineOpen();
     yield testMultilineLimit();
     yield testUseQuotes();
+    yield testNonPritableCharacters();
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testMultiline() {
     const renderedComponent = renderComponent(StringRep.rep, { object: getGripStub("testMultiline") });
@@ -46,25 +47,31 @@ window.onload = Task.async(function* () 
 
   function testMultilineOpen() {
     const renderedComponent = renderComponent(StringRep.rep, { object: getGripStub("testMultiline"), member: {open: true} });
     is(renderedComponent.textContent, "\"aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n\"", "String rep has expected text content for multiline string when open");
   }
 
   function testUseQuotes(){
      const renderedComponent = renderComponent(StringRep.rep, { object: getGripStub("testUseQuotes"), useQuotes: false });
-     is(renderedComponent.textContent, "abc","String rep was expected to omit quotes");
+     is(renderedComponent.textContent, "abc", "String rep was expected to omit quotes");
+  }
+
+  function testNonPritableCharacters(){
+     const renderedComponent = renderComponent(StringRep.rep, { object: getGripStub("testNonPritableCharacters"), useQuotes: false });
+     is(renderedComponent.textContent, "a\ufffdb", "String rep was expected to omit non printable characters");
   }
 
   function getGripStub(name) {
     switch (name) {
       case "testMultiline":
-      	 return "aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n";
-	 break;
+        return "aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n";
       case "testUseQuotes":
-	 return "abc";
+        return "abc";
+      case "testNonPritableCharacters":
+        return "a\x01b";
     }
   }
 });
 </script>
 </pre>
 </body>
 </html>
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -26,16 +26,17 @@
 #include "mozilla/LoadInfo.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StartupTimeline.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Unused.h"
 #include "Navigator.h"
 #include "URIUtils.h"
+#include "mozilla/dom/DocGroup.h"
 
 #include "nsIContent.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 
 #include "nsArray.h"
@@ -1537,17 +1538,17 @@ nsDocShell::LoadURI(nsIURI* aURI,
   return InternalLoad(aURI,
                       originalURI,
                       loadReplace,
                       referrer,
                       referrerPolicy,
                       triggeringPrincipal,
                       principalToInherit,
                       flags,
-                      target.get(),
+                      target,
                       nullptr,      // No type hint
                       NullString(), // No forced download
                       postStream,
                       headersStream,
                       loadType,
                       nullptr, // No SHEntry
                       aFirstParty,
                       srcdoc,
@@ -3143,19 +3144,18 @@ nsDocShell::GetName(nsAString& aName)
 NS_IMETHODIMP
 nsDocShell::SetName(const nsAString& aName)
 {
   mName = aName;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::NameEquals(const char16_t* aName, bool* aResult)
-{
-  NS_ENSURE_ARG_POINTER(aName);
+nsDocShell::NameEquals(const nsAString& aName, bool* aResult)
+{
   NS_ENSURE_ARG_POINTER(aResult);
   *aResult = mName.Equals(aName);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent)
 {
@@ -3644,83 +3644,54 @@ ItemIsActive(nsIDocShellTreeItem* aItem)
       return true;
     }
   }
 
   return false;
 }
 
 NS_IMETHODIMP
-nsDocShell::FindItemWithName(const char16_t* aName,
+nsDocShell::FindItemWithName(const nsAString& aName,
                              nsISupports* aRequestor,
                              nsIDocShellTreeItem* aOriginalRequestor,
                              nsIDocShellTreeItem** aResult)
 {
-  NS_ENSURE_ARG(aName);
   NS_ENSURE_ARG_POINTER(aResult);
 
   // If we don't find one, we return NS_OK and a null result
   *aResult = nullptr;
 
-  if (!*aName) {
+  if (aName.IsEmpty()) {
     return NS_OK;
   }
 
   if (aRequestor) {
     // If aRequestor is not null we don't need to check special names, so
     // just hand straight off to the search by actual name function.
     return DoFindItemWithName(aName, aRequestor, aOriginalRequestor, aResult);
   } else {
     // This is the entry point into the target-finding algorithm.  Check
     // for special names.  This should only be done once, hence the check
     // for a null aRequestor.
 
     nsCOMPtr<nsIDocShellTreeItem> foundItem;
-    nsDependentString name(aName);
-    if (name.LowerCaseEqualsLiteral("_self")) {
+    if (aName.LowerCaseEqualsLiteral("_self")) {
       foundItem = this;
-    } else if (name.LowerCaseEqualsLiteral("_blank")) {
+    } else if (aName.LowerCaseEqualsLiteral("_blank")) {
       // Just return null.  Caller must handle creating a new window with
       // a blank name himself.
       return NS_OK;
-    } else if (name.LowerCaseEqualsLiteral("_parent")) {
+    } else if (aName.LowerCaseEqualsLiteral("_parent")) {
       GetSameTypeParent(getter_AddRefs(foundItem));
       if (!foundItem) {
         foundItem = this;
       }
-    } else if (name.LowerCaseEqualsLiteral("_top")) {
+    } else if (aName.LowerCaseEqualsLiteral("_top")) {
       GetSameTypeRootTreeItem(getter_AddRefs(foundItem));
       NS_ASSERTION(foundItem, "Must have this; worst case it's us!");
-    }
-    // _main is an IE target which should be case-insensitive but isn't
-    // see bug 217886 for details
-    else if (name.LowerCaseEqualsLiteral("_content") ||
-             name.EqualsLiteral("_main")) {
-      // Must pass our same type root as requestor to the
-      // treeowner to make sure things work right.
-      nsCOMPtr<nsIDocShellTreeItem> root;
-      GetSameTypeRootTreeItem(getter_AddRefs(root));
-      if (mTreeOwner) {
-        NS_ASSERTION(root, "Must have this; worst case it's us!");
-        mTreeOwner->FindItemWithName(aName, root, aOriginalRequestor,
-                                     getter_AddRefs(foundItem));
-      }
-#ifdef DEBUG
-      else {
-        NS_ERROR("Someone isn't setting up the tree owner.  "
-                 "You might like to try that.  "
-                 "Things will.....you know, work.");
-        // Note: _content should always exist.  If we don't have one
-        // hanging off the treeowner, just create a named window....
-        // so don't return here, in case we did that and can now find
-        // it.
-        // XXXbz should we be using |root| instead of creating
-        // a new window?
-      }
-#endif
     } else {
       // Do the search for item by an actual name.
       DoFindItemWithName(aName, aRequestor, aOriginalRequestor,
                          getter_AddRefs(foundItem));
     }
 
     if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
       foundItem = nullptr;
@@ -3743,17 +3714,17 @@ nsDocShell::AssertOriginAttributesMatchP
   if (mItemType == typeChrome) {
     MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0);
   } else {
     MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId);
   }
 }
 
 nsresult
-nsDocShell::DoFindItemWithName(const char16_t* aName,
+nsDocShell::DoFindItemWithName(const nsAString& aName,
                                nsISupports* aRequestor,
                                nsIDocShellTreeItem* aOriginalRequestor,
                                nsIDocShellTreeItem** aResult)
 {
   // First we check our name.
   if (mName.Equals(aName) && ItemIsActive(this) &&
       CanAccessItem(this, aOriginalRequestor)) {
     NS_ADDREF(*aResult = this);
@@ -3784,33 +3755,37 @@ nsDocShell::DoFindItemWithName(const cha
   // docShellTreeOwner to do the search.
   nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem =
     do_QueryInterface(GetAsSupports(mParent));
   if (parentAsTreeItem) {
     if (parentAsTreeItem == reqAsTreeItem) {
       return NS_OK;
     }
 
-    if (parentAsTreeItem->ItemType() == mItemType) {
+    // If we have a same-type parent, respecting browser and app boundaries.
+    // NOTE: Could use GetSameTypeParent if the issues described in bug 1310344 are fixed.
+    if (!GetIsMozBrowserOrApp() && parentAsTreeItem->ItemType() == mItemType) {
       return parentAsTreeItem->FindItemWithName(
         aName,
         static_cast<nsIDocShellTreeItem*>(this),
         aOriginalRequestor,
         aResult);
     }
   }
 
-  // If the parent is null or not of the same type fall through and ask tree
-  // owner.
-
-  // This may fail, but comparing against null serves the same purpose
-  nsCOMPtr<nsIDocShellTreeOwner> reqAsTreeOwner(do_QueryInterface(aRequestor));
-  if (mTreeOwner && mTreeOwner != reqAsTreeOwner) {
-    return mTreeOwner->FindItemWithName(aName, this, aOriginalRequestor,
-                                        aResult);
+  // If we have a null parent or the parent is not of the same type, we need to
+  // give up on finding it in our tree, and start looking in our TabGroup.
+  nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
+  if (window) {
+    RefPtr<mozilla::dom::TabGroup> tabGroup = window->TabGroup();
+    // We don't want to make the request to our TabGroup if they are the ones
+    // which made a request to us.
+    if (tabGroup != aRequestor) {
+      tabGroup->FindItemWithName(aName, this, aOriginalRequestor, aResult);
+    }
   }
 
   return NS_OK;
 }
 
 bool
 nsDocShell::IsSandboxedFrom(nsIDocShell* aTargetDocShell)
 {
@@ -4185,29 +4160,28 @@ nsDocShell::GetChildAt(int32_t aIndex, n
 
   nsIDocumentLoader* child = ChildAt(aIndex);
   NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
 
   return CallQueryInterface(child, aChild);
 }
 
 NS_IMETHODIMP
-nsDocShell::FindChildWithName(const char16_t* aName,
+nsDocShell::FindChildWithName(const nsAString& aName,
                               bool aRecurse, bool aSameType,
                               nsIDocShellTreeItem* aRequestor,
                               nsIDocShellTreeItem* aOriginalRequestor,
                               nsIDocShellTreeItem** aResult)
 {
-  NS_ENSURE_ARG(aName);
   NS_ENSURE_ARG_POINTER(aResult);
 
   // if we don't find one, we return NS_OK and a null result
   *aResult = nullptr;
 
-  if (!*aName) {
+  if (aName.IsEmpty()) {
     return NS_OK;
   }
 
   nsXPIDLString childName;
   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
   while (iter.HasMore()) {
     nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
     NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
@@ -5367,17 +5341,17 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, 
   errorPageUrl.AppendASCII(escapedDescription.get());
 
   nsCOMPtr<nsIURI> errorPageURI;
   rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return InternalLoad(errorPageURI, nullptr, false, nullptr,
                       mozilla::net::RP_Default,
-                      nullptr, nullptr, INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL, nullptr,
+                      nullptr, nullptr, INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL, EmptyString(),
                       nullptr, NullString(), nullptr, nullptr, LOAD_ERROR_PAGE,
                       nullptr, true, NullString(), this, nullptr, nullptr,
                       nullptr);
 }
 
 NS_IMETHODIMP
 nsDocShell::Reload(uint32_t aReloadFlags)
 {
@@ -5447,17 +5421,17 @@ nsDocShell::Reload(uint32_t aReloadFlags
     rv = InternalLoad(mCurrentURI,
                       originalURI,
                       loadReplace,
                       mReferrerURI,
                       mReferrerPolicy,
                       principal,
                       principal,
                       flags,
-                      nullptr,         // No window target
+                      EmptyString(),   // No window target
                       NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
                       NullString(),    // No forced download
                       nullptr,         // No post data
                       nullptr,         // No headers data
                       loadType,        // Load type
                       nullptr,         // No SHEntry
                       true,
                       srcdoc,          // srcdoc argument for iframe
@@ -9589,17 +9563,17 @@ public:
   NS_IMETHOD
   Run() override
   {
     return mDocShell->InternalLoad(mURI, mOriginalURI,
                                    mLoadReplace,
                                    mReferrer,
                                    mReferrerPolicy,
                                    mTriggeringPrincipal, mPrincipalToInherit,
-                                   mFlags, nullptr, mTypeHint.get(),
+                                   mFlags, EmptyString(), mTypeHint.get(),
                                    NullString(), mPostData, mHeadersData,
                                    mLoadType, mSHEntry, mFirstParty,
                                    mSrcdoc, mSourceDocShell, mBaseURI,
                                    nullptr, nullptr);
   }
 
 private:
   // Use IDL strings so .get() returns null by default
@@ -9679,17 +9653,17 @@ NS_IMETHODIMP
 nsDocShell::InternalLoad(nsIURI* aURI,
                          nsIURI* aOriginalURI,
                          bool aLoadReplace,
                          nsIURI* aReferrer,
                          uint32_t aReferrerPolicy,
                          nsIPrincipal* aTriggeringPrincipal,
                          nsIPrincipal* aPrincipalToInherit,
                          uint32_t aFlags,
-                         const char16_t* aWindowTarget,
+                         const nsAString& aWindowTarget,
                          const char* aTypeHint,
                          const nsAString& aFileName,
                          nsIInputStream* aPostData,
                          nsIInputStream* aHeadersData,
                          uint32_t aLoadType,
                          nsISHEntry* aSHEntry,
                          bool aFirstParty,
                          const nsAString& aSrcdoc,
@@ -9752,25 +9726,24 @@ nsDocShell::InternalLoad(nsIURI* aURI,
 
   bool isJavaScript = false;
   if (NS_FAILED(aURI->SchemeIs("javascript", &isJavaScript))) {
     isJavaScript = false;
   }
 
   bool isTargetTopLevelDocShell = false;
   nsCOMPtr<nsIDocShell> targetDocShell;
-  if (aWindowTarget && *aWindowTarget) {
+  if (!aWindowTarget.IsEmpty()) {
     // Locate the target DocShell.
     nsCOMPtr<nsIDocShellTreeItem> targetItem;
-    nsDependentString name(aWindowTarget);
     // Only _self, _parent, and _top are supported in noopener case.
     if (!(aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) ||
-        name.LowerCaseEqualsLiteral("_self") ||
-        name.LowerCaseEqualsLiteral("_parent") ||
-        name.LowerCaseEqualsLiteral("_top")) {
+        aWindowTarget.LowerCaseEqualsLiteral("_self") ||
+        aWindowTarget.LowerCaseEqualsLiteral("_parent") ||
+        aWindowTarget.LowerCaseEqualsLiteral("_top")) {
       rv = FindItemWithName(aWindowTarget, nullptr, this,
                             getter_AddRefs(targetItem));
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     targetDocShell = do_QueryInterface(targetItem);
     if (targetDocShell) {
       // If the targetDocShell and the rootDocShell are the same, then the
@@ -9961,17 +9934,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
     }
   }
 
   //
   // Resolve the window target before going any further...
   // If the load has been targeted to another DocShell, then transfer the
   // load to it...
   //
-  if (aWindowTarget && *aWindowTarget) {
+  if (!aWindowTarget.IsEmpty()) {
     // We've already done our owner-inheriting.  Mask out that bit, so we
     // don't try inheriting an owner from the target window if we came up
     // with a null owner above.
     aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
 
     bool isNewWindow = false;
     if (!targetDocShell) {
       // If the docshell's document is sandboxed, only open a new window
@@ -9986,17 +9959,16 @@ nsDocShell::InternalLoad(nsIURI* aURI,
         if (sandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
           return NS_ERROR_DOM_INVALID_ACCESS_ERR;
         }
       }
 
       nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
       NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
 
-      nsDependentString name(aWindowTarget);
       nsCOMPtr<nsPIDOMWindowOuter> newWin;
       nsAutoCString spec;
       if (aURI) {
         aURI->GetSpec(spec);
       }
       // If we are a noopener load, we just hand the whole thing over to our
       // window.
       if (aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) {
@@ -10031,27 +10003,27 @@ nsDocShell::InternalLoad(nsIURI* aURI,
         loadInfo->SetInheritPrincipal(
           aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
         // Explicit principal because we do not want any guesses as to what the
         // principal to inherit is: it should be aTriggeringPrincipal.
         loadInfo->SetPrincipalIsExplicit(true);
         loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(LOAD_LINK));
 
         rv = win->Open(NS_ConvertUTF8toUTF16(spec),
-                       name, // window name
+                       aWindowTarget, // window name
                        EmptyString(), // Features
                        loadInfo,
                        true, // aForceNoOpener
                        getter_AddRefs(newWin));
         MOZ_ASSERT(!newWin);
         return rv;
       }
 
       rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
-                               name,  // window name
+                               aWindowTarget,  // window name
                                EmptyString(), // Features
                                getter_AddRefs(newWin));
 
       // In some cases the Open call doesn't actually result in a new
       // window being opened.  We can detect these cases by examining the
       // document in |newWin|, if any.
       nsCOMPtr<nsPIDOMWindowOuter> piNewWin = do_QueryInterface(newWin);
       if (piNewWin) {
@@ -10074,17 +10046,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
       rv = targetDocShell->InternalLoad(aURI,
                                         aOriginalURI,
                                         aLoadReplace,
                                         aReferrer,
                                         aReferrerPolicy,
                                         aTriggeringPrincipal,
                                         principalToInherit,
                                         aFlags,
-                                        nullptr,         // No window target
+                                        EmptyString(),   // No window target
                                         aTypeHint,
                                         NullString(),    // No forced download
                                         aPostData,
                                         aHeadersData,
                                         aLoadType,
                                         aSHEntry,
                                         aFirstParty,
                                         aSrcdoc,
@@ -10138,17 +10110,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
 
   rv = CheckLoadingPermissions();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (mFiredUnloadEvent) {
     if (IsOKToLoadURI(aURI)) {
-      NS_PRECONDITION(!aWindowTarget || !*aWindowTarget,
+      NS_PRECONDITION(aWindowTarget.IsEmpty(),
                       "Shouldn't have a window target here!");
 
       // If this is a replace load, make whatever load triggered
       // the unload event also a replace load, so we don't
       // create extra history entries.
       if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
         mLoadType = LOAD_NORMAL_REPLACE;
       }
@@ -12541,17 +12513,17 @@ nsDocShell::LoadHistoryEntry(nsISHEntry*
   rv = InternalLoad(uri,
                     originalURI,
                     loadReplace,
                     referrerURI,
                     referrerPolicy,
                     triggeringPrincipal,
                     principalToInherit,
                     flags,
-                    nullptr,            // No window target
+                    EmptyString(),      // No window target
                     contentType.get(),  // Type hint
                     NullString(),       // No forced file download
                     postData,           // Post data stream
                     nullptr,            // No headers stream
                     aLoadType,          // Load type
                     aEntry,             // SHEntry
                     true,
                     srcdoc,
@@ -14032,17 +14004,17 @@ nsDocShell::OnLinkClickSync(nsIContent* 
                              nullptr,                   // Original URI
                              false,                     // LoadReplace
                              referer,                   // Referer URI
                              refererPolicy,             // Referer policy
                              aContent->NodePrincipal(), // Triggering is our node's
                                                         // principal
                              aContent->NodePrincipal(),
                              flags,
-                             target.get(),              // Window target
+                             target,                    // Window target
                              NS_LossyConvertUTF16toASCII(typeHint).get(),
                              aFileName,                 // Download as file
                              aPostDataStream,           // Post data stream
                              aHeadersDataStream,        // Headers stream
                              LOAD_LINK,                 // Load type
                              nullptr,                   // No SHEntry
                              true,                      // first party site
                              NullString(),              // No srcdoc
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -1032,17 +1032,17 @@ private:
   uint32_t mJSRunToCompletionDepth;
 
   // Whether or not touch events are overridden. Possible values are defined
   // as constants in the nsIDocShell.idl file.
   uint32_t mTouchEventsOverride;
 
   // Separate function to do the actual name (i.e. not _top, _self etc.)
   // searching for FindItemWithName.
-  nsresult DoFindItemWithName(const char16_t* aName,
+  nsresult DoFindItemWithName(const nsAString& aName,
                               nsISupports* aRequestor,
                               nsIDocShellTreeItem* aOriginalRequestor,
                               nsIDocShellTreeItem** aResult);
 
   // Helper assertion to enforce that mInPrivateBrowsing is in sync with
   // OriginAttributes.mPrivateBrowsingId
   void AssertOriginAttributesMatchPrivateBrowsing();
 
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -185,17 +185,17 @@ interface nsIDocShell : nsIDocShellTreeI
   [noscript]void internalLoad(in nsIURI aURI,
                               in nsIURI aOriginalURI,
                               in boolean aLoadReplace,
                               in nsIURI aReferrer,
                               in unsigned long aReferrerPolicy,
                               in nsIPrincipal aTriggeringPrincipal,
                               in nsIPrincipal aPrincipalToInherit,
                               in uint32_t aFlags,
-                              in wstring aWindowTarget,
+                              in AString aWindowTarget,
                               in string aTypeHint,
                               in AString aFileName,
                               in nsIInputStream aPostDataStream,
                               in nsIInputStream aHeadersStream,
                               in unsigned long aLoadFlags,
                               in nsISHEntry aSHEntry,
                               in boolean firstParty,
                               in AString aSrcdoc,
--- a/docshell/base/nsIDocShellTreeItem.idl
+++ b/docshell/base/nsIDocShellTreeItem.idl
@@ -27,17 +27,17 @@ interface nsIDocShellTreeItem : nsISuppo
 
         /**
          * Compares the provided name against the item's name and
          * returns the appropriate result.
          *
          * @return <CODE>PR_TRUE</CODE> if names match;
          *         <CODE>PR_FALSE</CODE> otherwise.
          */
-        boolean nameEquals(in wstring name);
+        boolean nameEquals(in AString name);
 
 	/*
 	Definitions for the item types.
 	*/
 	const long typeChrome=0;            // typeChrome must equal 0
 	const long typeContent=1;           // typeContent must equal 1
 	const long typeContentWrapper=2;    // typeContentWrapper must equal 2
 	const long typeChromeWrapper=3;     // typeChromeWrapper must equal 3
@@ -99,17 +99,17 @@ interface nsIDocShellTreeItem : nsISuppo
 		a child with the specific name.  The parent uses this parameter to ensure
 		a resursive state does not occur by not again asking the requestor to find
 		a shell by the specified name.  Inversely the child uses it to ensure it
 		does not ask its parent to do the search if its parent is the one that
 		asked it to search.  Children also use this to test against the treeOwner;
 	aOriginalRequestor - The original treeitem that made the request, if any.
 		This is used to ensure that we don't run into cross-site issues.
 	*/
-	nsIDocShellTreeItem findItemWithName(in wstring name,
+	nsIDocShellTreeItem findItemWithName(in AString name,
 	                                     in nsISupports aRequestor,
 	                                     in nsIDocShellTreeItem aOriginalRequestor);
 
 	/*
 	The owner of the DocShell Tree.  This interface will be called upon when
 	the docshell has things it needs to tell to the owner of the docshell.
 	Note that docShell tree ownership does not cross tree types.  Meaning
 	setting ownership on a chrome tree does not set ownership on the content 
@@ -167,17 +167,17 @@ interface nsIDocShellTreeItem : nsISuppo
 	aRequestor - This is the docshellTreeItem that is requesting the find.  This
 		parameter is used when recursion is being used to avoid searching the same
 		tree again when a child has asked a parent to search for children.
 	aOriginalRequestor - The original treeitem that made the request, if any.
     	This is used to ensure that we don't run into cross-site issues.
 
 	Note the search is depth first when recursing.
 	*/
-	nsIDocShellTreeItem findChildWithName(in wstring aName,
+	nsIDocShellTreeItem findChildWithName(in AString aName,
 	                                      in boolean aRecurse,
 	                                      in boolean aSameType,
 	                                      in nsIDocShellTreeItem aRequestor,
 	                                      in nsIDocShellTreeItem aOriginalRequestor);
 
   [noscript,nostdcall,notxpcom] nsIDocument getDocument();
   [noscript,nostdcall,notxpcom] nsPIDOMWindowOuter getWindow();
 };
--- a/docshell/base/nsIDocShellTreeOwner.idl
+++ b/docshell/base/nsIDocShellTreeOwner.idl
@@ -11,34 +11,16 @@
  */
 
 interface nsIDocShellTreeItem;
 interface nsITabParent;
 
 [scriptable, uuid(0e3dc4b1-4cea-4a37-af71-79f0afd07574)]
 interface nsIDocShellTreeOwner : nsISupports
 {
-	/*
-	Return the child DocShellTreeItem with the specified name.
-	name - This is the name of the item that is trying to be found.
-	aRequestor - This is the docshellTreeItem that is requesting the find.  This
-	parameter is used to identify when the child is asking its parent to find
-	a child with the specific name.  The parent uses this parameter to ensure
-	a resursive state does not occur by not again asking the requestor for find
-	a shell by the specified name.  Inversely the child uses it to ensure it
-	does not ask its parent to do the search if its parent is the one that
-	asked it to search.
-	aOriginalRequestor - The original treeitem that made the request, if any.
-	This is used to ensure that we don't run into cross-site issues.
-
-	*/
-	nsIDocShellTreeItem findItemWithName(in wstring name, 
-		in nsIDocShellTreeItem aRequestor,
-		in nsIDocShellTreeItem aOriginalRequestor);
-
 	/**
 	 * Called when a content shell is added to the docshell tree.  This is
 	 * _only_ called for "root" content shells (that is, ones whose parent is a
 	 * chrome shell).
 	 *
 	 * @param aContentShell the shell being added.
 	 * @param aPrimary whether the shell is primary.
 	 * @param aTargetable whether the shell can be a target for named window
--- a/docshell/test/chrome/bug364461_window.xul
+++ b/docshell/test/chrome/bug364461_window.xul
@@ -209,17 +209,17 @@
       //  Check that navigation is not blocked after a document is restored
       //  from bfcache
       
       var test7Doc = "data:text/html,<html><head><title>test7</title>" +
                       "</head><body>" +
                       "<iframe src='data:text/html," +
                         "<html><head><title>test7-nested1</title></head>" +
                         "<body>test7-nested1<br/>" +
-                        "<a href=\"data:text/plain,aaa\" target=\"_main\">" +
+                        "<a href=\"data:text/plain,aaa\" target=\"_top\">" +
                           "Click me, hit back, click me again</a>" +
                         "</body></html>'>" +
                       "</iframe>" +
                     "</body></html>";
       
       gExpected = [{type: "pagehide", title: "test6", persisted: true},
                    {type: "load", title: "test7-nested1"},
                    {type: "pageshow", title: "test7-nested1", persisted: false},
new file mode 100644
--- /dev/null
+++ b/dom/base/DocGroup.cpp
@@ -0,0 +1,164 @@
+#include "mozilla/dom/DocGroup.h"
+#include "mozilla/Telemetry.h"
+#include "nsIURI.h"
+#include "nsIEffectiveTLDService.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "nsIDocShell.h"
+
+namespace mozilla {
+namespace dom {
+
+/* static */ void
+DocGroup::GetKey(nsIPrincipal* aPrincipal, nsACString& aKey)
+{
+  aKey.Truncate();
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  if (NS_SUCCEEDED(rv)) {
+    nsCOMPtr<nsIEffectiveTLDService> tldService =
+      do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+    if (tldService) {
+      rv = tldService->GetBaseDomain(uri, 0, aKey);
+      if (NS_FAILED(rv)) {
+        aKey.Truncate();
+      }
+    }
+  }
+}
+
+void
+DocGroup::RemoveDocument(nsIDocument* aDocument)
+{
+  MOZ_ASSERT(mDocuments.Contains(aDocument));
+  mDocuments.RemoveElement(aDocument);
+}
+
+DocGroup::DocGroup(TabGroup* aTabGroup, const nsACString& aKey)
+  : mKey(aKey), mTabGroup(aTabGroup)
+{
+  // This method does not add itself to mTabGroup->mDocGroups as the caller does it for us.
+}
+
+DocGroup::~DocGroup()
+{
+  MOZ_ASSERT(mDocuments.IsEmpty());
+  mTabGroup->mDocGroups.RemoveEntry(mKey);
+}
+
+NS_IMPL_ISUPPORTS(DocGroup, nsISupports)
+
+TabGroup::TabGroup()
+{}
+
+TabGroup::~TabGroup()
+{
+  MOZ_ASSERT(mDocGroups.IsEmpty());
+  MOZ_ASSERT(mWindows.IsEmpty());
+}
+
+static StaticRefPtr<TabGroup> sChromeTabGroup;
+
+TabGroup*
+TabGroup::GetChromeTabGroup()
+{
+  if (!sChromeTabGroup) {
+    sChromeTabGroup = new TabGroup();
+    ClearOnShutdown(&sChromeTabGroup);
+  }
+  return sChromeTabGroup;
+}
+
+already_AddRefed<DocGroup>
+TabGroup::GetDocGroup(const nsACString& aKey)
+{
+  RefPtr<DocGroup> docGroup(mDocGroups.GetEntry(aKey)->mDocGroup);
+  return docGroup.forget();
+}
+
+already_AddRefed<DocGroup>
+TabGroup::AddDocument(const nsACString& aKey, nsIDocument* aDocument)
+{
+  HashEntry* entry = mDocGroups.PutEntry(aKey);
+  RefPtr<DocGroup> docGroup;
+  if (entry->mDocGroup) {
+    docGroup = entry->mDocGroup;
+  } else {
+    docGroup = new DocGroup(this, aKey);
+    entry->mDocGroup = docGroup;
+  }
+
+  // Make sure that the hashtable was updated and now contains the correct value
+  MOZ_ASSERT(RefPtr<DocGroup>(GetDocGroup(aKey)) == docGroup);
+
+  docGroup->mDocuments.AppendElement(aDocument);
+
+  return docGroup.forget();
+}
+
+/* static */ already_AddRefed<TabGroup>
+TabGroup::Join(nsPIDOMWindowOuter* aWindow, TabGroup* aTabGroup)
+{
+  RefPtr<TabGroup> tabGroup = aTabGroup;
+  if (!tabGroup) {
+    tabGroup = new TabGroup();
+  }
+  MOZ_ASSERT(!tabGroup->mWindows.Contains(aWindow));
+  tabGroup->mWindows.AppendElement(aWindow);
+  return tabGroup.forget();
+}
+
+void
+TabGroup::Leave(nsPIDOMWindowOuter* aWindow)
+{
+  MOZ_ASSERT(mWindows.Contains(aWindow));
+  mWindows.RemoveElement(aWindow);
+}
+
+nsresult
+TabGroup::FindItemWithName(const nsAString& aName,
+                           nsIDocShellTreeItem* aRequestor,
+                           nsIDocShellTreeItem* aOriginalRequestor,
+                           nsIDocShellTreeItem** aFoundItem)
+{
+  NS_ENSURE_ARG_POINTER(aFoundItem);
+  *aFoundItem = nullptr;
+
+  MOZ_ASSERT(!aName.LowerCaseEqualsLiteral("_blank") &&
+             !aName.LowerCaseEqualsLiteral("_top") &&
+             !aName.LowerCaseEqualsLiteral("_parent") &&
+             !aName.LowerCaseEqualsLiteral("_self"));
+
+  for (nsPIDOMWindowOuter* outerWindow : mWindows) {
+    // Ignore non-toplevel windows
+    if (outerWindow->GetScriptableParentOrNull()) {
+      continue;
+    }
+
+    nsCOMPtr<nsIDocShellTreeItem> docshell = outerWindow->GetDocShell();
+    if (!docshell) {
+      continue;
+    }
+
+    nsCOMPtr<nsIDocShellTreeItem> root;
+    docshell->GetSameTypeRootTreeItem(getter_AddRefs(root));
+    MOZ_RELEASE_ASSERT(docshell == root);
+    if (root && aRequestor != root) {
+      root->FindItemWithName(aName, this, aOriginalRequestor, aFoundItem);
+      if (*aFoundItem) {
+        break;
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(TabGroup, nsISupports)
+
+TabGroup::HashEntry::HashEntry(const nsACString* aKey)
+  : nsCStringHashKey(aKey), mDocGroup(nullptr)
+{}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/DocGroup.h
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DocGroup_h
+#define DocGroup_h
+
+#include "nsISupports.h"
+#include "nsISupportsImpl.h"
+#include "nsIPrincipal.h"
+#include "nsTHashtable.h"
+#include "nsString.h"
+
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+// Two browsing contexts are considered "related" if they are reachable from one
+// another through window.opener, window.parent, or window.frames. This is the
+// spec concept of a "unit of related browsing contexts"
+//
+// Two browsing contexts are considered "similar-origin" if they can be made to
+// have the same origin by setting document.domain. This is the spec concept of
+// a "unit of similar-origin related browsing contexts"
+//
+// A TabGroup is a set of browsing contexts which are all "related". Within a
+// TabGroup, browsing contexts are broken into "similar-origin" DocGroups. In
+// more detail, a DocGroup is actually a collection of documents, and a
+// TabGroup is a collection of DocGroups. A TabGroup typically will contain
+// (through its DocGroups) the documents from one or more tabs related by
+// window.opener. A DocGroup is a member of exactly one TabGroup.
+
+class TabGroup;
+
+class DocGroup final : public nsISupports
+{
+public:
+  typedef nsTArray<nsIDocument*>::iterator Iterator;
+  friend class TabGroup;
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  static void GetKey(nsIPrincipal* aPrincipal, nsACString& aString);
+  bool MatchesKey(const nsACString& aKey)
+  {
+    return aKey == mKey;
+  }
+  TabGroup* GetTabGroup()
+  {
+    return mTabGroup;
+  }
+  void RemoveDocument(nsIDocument* aWindow);
+
+  // Iterators for iterating over every document within the DocGroup
+  Iterator begin()
+  {
+    return mDocuments.begin();
+  }
+  Iterator end()
+  {
+    return mDocuments.end();
+  }
+
+private:
+  DocGroup(TabGroup* aTabGroup, const nsACString& aKey);
+  ~DocGroup();
+
+  nsCString mKey;
+  RefPtr<TabGroup> mTabGroup;
+  nsTArray<nsIDocument*> mDocuments;
+};
+
+
+class TabGroup final : public nsISupports
+{
+private:
+  class HashEntry : public nsCStringHashKey
+  {
+  public:
+    // NOTE: Weak reference. The DocGroup destructor removes itself from its
+    // owning TabGroup.
+    DocGroup* mDocGroup;
+    explicit HashEntry(const nsACString* aKey);
+  };
+
+  typedef nsTHashtable<HashEntry> DocGroupMap;
+public:
+  typedef DocGroupMap::Iterator Iterator;
+
+  friend class DocGroup;
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  static TabGroup*
+  GetChromeTabGroup();
+
+  TabGroup();
+
+  // Get the docgroup for the corresponding doc group key.
+  // Returns null if the given key hasn't been seen yet.
+  already_AddRefed<DocGroup>
+  GetDocGroup(const nsACString& aKey);
+
+  already_AddRefed<DocGroup>
+  AddDocument(const nsACString& aKey, nsIDocument* aDocument);
+
+  // Join the specified TabGroup, returning a reference to it. If aTabGroup is
+  // nullptr, create a new tabgroup to join.
+  static already_AddRefed<TabGroup>
+  Join(nsPIDOMWindowOuter* aWindow, TabGroup* aTabGroup);
+
+  void Leave(nsPIDOMWindowOuter* aWindow);
+
+  Iterator Iter()
+  {
+    return mDocGroups.Iter();
+  }
+
+
+  // Returns the nsIDocShellTreeItem with the given name, searching each of the
+  // docShell trees which are within this TabGroup. It will pass itself as
+  // aRequestor to each docShellTreeItem which it asks to search for the name,
+  // and will not search the docShellTreeItem which is passed as aRequestor.
+  //
+  // This method is used in order to correctly namespace named windows based on
+  // their unit of related browsing contexts.
+  //
+  // It is illegal to pass in the special case-insensitive names "_blank",
+  // "_self", "_parent" or "_top", as those should be handled elsewhere.
+  nsresult
+  FindItemWithName(const nsAString& aName,
+                   nsIDocShellTreeItem* aRequestor,
+                   nsIDocShellTreeItem* aOriginalRequestor,
+                   nsIDocShellTreeItem** aFoundItem);
+
+private:
+  ~TabGroup();
+  DocGroupMap mDocGroups;
+  nsTArray<nsPIDOMWindowOuter*> mWindows;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // defined(DocGroup_h)
--- a/dom/base/FormData.cpp
+++ b/dom/base/FormData.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FormData.h"
 #include "nsIVariant.h"
 #include "nsIInputStream.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/Directory.h"
 #include "mozilla/dom/HTMLFormElement.h"
 
 #include "MultipartBlobImpl.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 FormData::FormData(nsISupports* aOwner)
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1526,20 +1526,16 @@ Navigator::GetBattery(ErrorResult& aRv)
 
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
   RefPtr<Promise> batteryPromise = Promise::Create(go, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
   mBatteryPromise = batteryPromise;
 
-  // We just initialized mBatteryPromise, so we know this is the first time
-  // this page has accessed navigator.getBattery(). 1 = navigator.getBattery()
-  Telemetry::Accumulate(Telemetry::BATTERY_STATUS_COUNT, 1);
-
   if (!mBatteryManager) {
     mBatteryManager = new battery::BatteryManager(mWindow);
     mBatteryManager->Init();
   }
 
   mBatteryPromise->MaybeResolve(mBatteryManager);
 
   return mBatteryPromise;
--- a/dom/base/PartialSHistory.cpp
+++ b/dom/base/PartialSHistory.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PartialSHistory.h"
 
+#include "nsIWebNavigation.h"
+
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION(PartialSHistory, mOwnerFrameLoader)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(PartialSHistory)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(PartialSHistory)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PartialSHistory)
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -992,16 +992,51 @@ WriteFormData(JSStructuredCloneWriter* a
 
       return true;
     }
   };
   Closure closure(aWriter, aHolder);
   return aFormData->ForEach(Closure::Write, &closure);
 }
 
+JSObject*
+ReadWasmModule(JSContext* aCx,
+               uint32_t aIndex,
+               StructuredCloneHolder* aHolder)
+{
+  MOZ_ASSERT(aHolder);
+  MOZ_ASSERT(aIndex < aHolder->WasmModules().Length());
+  MOZ_ASSERT(aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessSameThread ||
+             aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessDifferentThread);
+
+  RefPtr<JS::WasmModule> wasmModule = aHolder->WasmModules()[aIndex];
+  return wasmModule->createObject(aCx);
+}
+
+bool
+WriteWasmModule(JSStructuredCloneWriter* aWriter,
+                JS::WasmModule* aWasmModule,
+                StructuredCloneHolder* aHolder)
+{
+  MOZ_ASSERT(aWriter);
+  MOZ_ASSERT(aWasmModule);
+  MOZ_ASSERT(aHolder);
+  MOZ_ASSERT(aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessSameThread ||
+             aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessDifferentThread);
+
+  // We store the position of the wasmModule in the array as index.
+  if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_WASM,
+                         aHolder->WasmModules().Length())) {
+    aHolder->WasmModules().AppendElement(aWasmModule);
+    return true;
+  }
+
+  return false;
+}
+
 } // anonymous namespace
 
 JSObject*
 StructuredCloneHolder::CustomReadHandler(JSContext* aCx,
                                          JSStructuredCloneReader* aReader,
                                          uint32_t aTag,
                                          uint32_t aIndex)
 {
@@ -1028,17 +1063,21 @@ StructuredCloneHolder::CustomReadHandler
                mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
 
     // Get the current global object.
     // This can be null.
     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
     // aIndex is the index of the cloned image.
     return ImageBitmap::ReadStructuredClone(aCx, aReader,
                                             parent, GetSurfaces(), aIndex);
-   }
+  }
+
+  if (aTag == SCTAG_DOM_WASM) {
+    return ReadWasmModule(aCx, aIndex, this);
+  }
 
   return ReadFullySerializableObjects(aCx, aReader, aTag);
 }
 
 bool
 StructuredCloneHolder::CustomWriteHandler(JSContext* aCx,
                                           JSStructuredCloneWriter* aWriter,
                                           JS::Handle<JSObject*> aObj)
@@ -1090,16 +1129,26 @@ StructuredCloneHolder::CustomWriteHandle
     ImageBitmap* imageBitmap = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
       return ImageBitmap::WriteStructuredClone(aWriter,
                                                GetSurfaces(),
                                                imageBitmap);
     }
   }
 
+  // See if this is a WasmModule.
+  if ((mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+       mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) &&
+      JS::IsWasmModuleObject(aObj)) {
+    RefPtr<JS::WasmModule> module = JS::GetWasmModule(aObj);
+    MOZ_ASSERT(module);
+
+    return WriteWasmModule(aWriter, module, this);
+  }
+
   return WriteFullySerializableObjects(aCx, aWriter, aObj);
 }
 
 bool
 StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
                                                  JSStructuredCloneReader* aReader,
                                                  uint32_t aTag,
                                                  void* aContent,
--- a/dom/base/StructuredCloneHolder.h
+++ b/dom/base/StructuredCloneHolder.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 mozilla_dom_StructuredCloneHolder_h
 #define mozilla_dom_StructuredCloneHolder_h
 
+#include "jsapi.h"
 #include "js/StructuredClone.h"
 #include "mozilla/Move.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsISupports.h"
 #include "nsTArray.h"
 
 #ifdef DEBUG
@@ -173,25 +174,32 @@ public:
             JSContext* aCx,
             JS::MutableHandle<JS::Value> aValue,
             ErrorResult &aRv);
 
   // Call this method to know if this object is keeping some DOM object alive.
   bool HasClonedDOMObjects() const
   {
     return !mBlobImplArray.IsEmpty() ||
+           !mWasmModuleArray.IsEmpty() ||
            !mClonedSurfaces.IsEmpty();
   }
 
   nsTArray<RefPtr<BlobImpl>>& BlobImpls()
   {
     MOZ_ASSERT(mSupportsCloning, "Blobs cannot be taken/set if cloning is not supported.");
     return mBlobImplArray;
   }
 
+  nsTArray<RefPtr<JS::WasmModule>>& WasmModules()
+  {
+    MOZ_ASSERT(mSupportsCloning, "WasmModules cannot be taken/set if cloning is not supported.");
+    return mWasmModuleArray;
+  }
+
   StructuredCloneScope CloneScope() const
   {
     return mStructuredCloneScope;
   }
 
   // The parent object is set internally just during the Read(). This method
   // can be used by read functions to retrieve it.
   nsISupports* ParentDuringRead() const
@@ -287,16 +295,19 @@ protected:
                       ErrorResult &aRv);
 
   bool mSupportsCloning;
   bool mSupportsTransferring;
 
   // Used for cloning blobs in the structured cloning algorithm.
   nsTArray<RefPtr<BlobImpl>> mBlobImplArray;
 
+  // Used for cloning JS::WasmModules in the structured cloning algorithm.
+  nsTArray<RefPtr<JS::WasmModule>> mWasmModuleArray;
+
   // This is used for sharing the backend of ImageBitmaps.
   // The DataSourceSurface object must be thread-safely reference-counted.
   // The DataSourceSurface object will not be written ever via any ImageBitmap
   // instance, so no race condition will occur.
   nsTArray<RefPtr<gfx::DataSourceSurface>> mClonedSurfaces;
 
   // This raw pointer is only set within ::Read() and is unset by the end.
   nsISupports* MOZ_NON_OWNING_REF mParent;
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -83,16 +83,17 @@ public:
   NS_DECL_NSIREQUEST
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIEVENTTARGET
   using nsIEventTarget::Dispatch;
 
   explicit WebSocketImpl(WebSocket* aWebSocket)
   : mWebSocket(aWebSocket)
   , mIsServerSide(false)
+  , mSecure(false)
   , mOnCloseScheduled(false)
   , mFailed(false)
   , mDisconnectingOrDisconnected(false)
   , mCloseEventWasClean(false)
   , mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL)
   , mScriptLine(0)
   , mScriptColumn(0)
   , mInnerWindowID(0)
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -154,16 +154,17 @@ EXPORTS.mozilla.dom += [
     'BodyUtil.h',
     'BorrowedAttrInfo.h',
     'ChildIterator.h',
     'ChromeNodeList.h',
     'ChromeUtils.h',
     'Comment.h',
     'CustomElementRegistry.h',
     'DirectionalityUtils.h',
+    'DocGroup.h',
     'DocumentFragment.h',
     'DocumentType.h',
     'DOMCursor.h',
     'DOMError.h',
     'DOMException.h',
     'DOMImplementation.h',
     'DOMMatrix.h',
     'DOMParser.h',
@@ -222,16 +223,17 @@ UNIFIED_SOURCES += [
     'BorrowedAttrInfo.cpp',
     'ChildIterator.cpp',
     'ChromeNodeList.cpp',
     'ChromeUtils.cpp',
     'Comment.cpp',
     'Crypto.cpp',
     'CustomElementRegistry.cpp',
     'DirectionalityUtils.cpp',
+    'DocGroup.cpp',
     'DocumentFragment.cpp',
     'DocumentType.cpp',
     'DOMCursor.cpp',
     'DOMError.cpp',
     'DOMException.cpp',
     'DOMImplementation.cpp',
     'DOMMatrix.cpp',
     'DOMParser.cpp',
--- a/dom/base/nsDOMWindowList.cpp
+++ b/dom/base/nsDOMWindowList.cpp
@@ -109,18 +109,17 @@ nsDOMWindowList::NamedItem(const nsAStri
 {
   nsCOMPtr<nsIDocShellTreeItem> item;
 
   *aReturn = nullptr;
 
   EnsureFresh();
 
   if (mDocShellNode) {
-    mDocShellNode->FindChildWithName(PromiseFlatString(aName).get(),
-                                     false, false, nullptr,
+    mDocShellNode->FindChildWithName(aName, false, false, nullptr,
                                      nullptr, getter_AddRefs(item));
 
     nsCOMPtr<nsIScriptGlobalObject> globalObject(do_GetInterface(item));
     if (globalObject) {
       CallQueryInterface(globalObject.get(), aReturn);
     }
   }
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -247,16 +247,18 @@
 #include "mozilla/dom/BoxObject.h"
 #include "gfxPrefs.h"
 #include "nsISupportsPrimitives.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/dom/SVGSVGElement.h"
+#include "mozilla/dom/DocGroup.h"
+
 #include "mozilla/DocLoadingTimelineMarker.h"
 
 #include "nsISpeculativeConnect.h"
 
 #include "mozilla/MediaManager.h"
 #ifdef MOZ_WEBRTC
 #include "IPeerConnection.h"
 #endif // MOZ_WEBRTC
@@ -1357,16 +1359,20 @@ nsIDocument::~nsIDocument()
 {
   MOZ_ASSERT(PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists),
              "must not have media query lists left");
 
   if (mNodeInfoManager) {
     mNodeInfoManager->DropDocumentReference();
   }
 
+  if (mDocGroup) {
+    mDocGroup->RemoveDocument(this);
+  }
+
   UnlinkOriginalDocumentIfStatic();
 }
 
 bool
 nsDocument::IsAboutPage()
 {
   nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
   nsCOMPtr<nsIURI> uri;
@@ -2837,16 +2843,46 @@ nsDocument::SetPrincipal(nsIPrincipal *a
     aNewPrincipal->GetURI(getter_AddRefs(uri));
     bool isHTTPS;
     if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) ||
         isHTTPS) {
       mAllowDNSPrefetch = false;
     }
   }
   mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
+
+#ifdef DEBUG
+  // Validate that the docgroup is set correctly by calling its getter and
+  // triggering its sanity check.
+  //
+  // If we're setting the principal to null, we don't want to perform the check,
+  // as the document is entering an intermediate state where it does not have a
+  // principal. It will be given another real principal shortly which we will
+  // check. It's not unsafe to have a document which has a null principal in the
+  // same docgroup as another document, so this should not be a problem.
+  if (aNewPrincipal) {
+    GetDocGroup();
+  }
+#endif
+}
+
+mozilla::dom::DocGroup*
+nsIDocument::GetDocGroup()
+{
+#ifdef DEBUG
+  // Sanity check that we have an up-to-date and accurate docgroup
+  if (mDocGroup) {
+    nsAutoCString docGroupKey;
+    mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
+    MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey));
+    // XXX: Check that the TabGroup is correct as well!
+  }
+#endif
+
+  return mDocGroup;
 }
 
 NS_IMETHODIMP
 nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
 {
   NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
 
   return NS_OK;
@@ -4243,16 +4279,33 @@ nsDocument::GetScopeObject() const
 }
 
 void
 nsDocument::SetScopeObject(nsIGlobalObject* aGlobal)
 {
   mScopeObject = do_GetWeakReference(aGlobal);
   if (aGlobal) {
     mHasHadScriptHandlingObject = true;
+
+    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
+    if (window) {
+      // We want to get the tabgroup unconditionally, such that we can make
+      // certain that it is cached in the inner window early enough.
+      mozilla::dom::TabGroup* tabgroup = window->TabGroup();
+      // We should already have the principal, and now that we have been added to a
+      // window, we should be able to join a DocGroup!
+      nsAutoCString docGroupKey;
+      mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
+      if (mDocGroup) {
+        MOZ_RELEASE_ASSERT(mDocGroup->MatchesKey(docGroupKey));
+      } else {
+        mDocGroup = tabgroup->AddDocument(docGroupKey, this);
+        MOZ_ASSERT(mDocGroup);
+      }
+    }
   }
 }
 
 static void
 CheckIfContainsEMEContent(nsISupports* aSupports, void* aContainsEME)
 {
   nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aSupports));
   if (domMediaElem) {
@@ -4431,21 +4484,20 @@ nsDocument::SetScriptGlobalObject(nsIScr
     if (os) {
       os->AddObserver(this, "service-worker-get-client", /* ownsWeak */ false);
     }
   }
 
   mScriptGlobalObject = aScriptGlobalObject;
 
   if (aScriptGlobalObject) {
-    mHasHadScriptHandlingObject = true;
-    mHasHadDefaultView = true;
     // Go back to using the docshell for the layout history state
     mLayoutHistoryState = nullptr;
-    mScopeObject = do_GetWeakReference(aScriptGlobalObject);
+    SetScopeObject(aScriptGlobalObject);
+    mHasHadDefaultView = true;
 #ifdef DEBUG
     if (!mWillReparent) {
       // We really shouldn't have a wrapper here but if we do we need to make sure
       // it has the correct parent.
       JSObject *obj = GetWrapperPreserveColor();
       if (obj) {
         JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject();
         NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope,
@@ -4572,18 +4624,17 @@ nsDocument::GetScriptHandlingObjectInter
 }
 void
 nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject)
 {
   NS_ASSERTION(!mScriptGlobalObject ||
                mScriptGlobalObject == aScriptObject,
                "Wrong script object!");
   if (aScriptObject) {
-    mScopeObject = do_GetWeakReference(aScriptObject);
-    mHasHadScriptHandlingObject = true;
+    SetScopeObject(aScriptObject);
     mHasHadDefaultView = false;
   }
 }
 
 bool
 nsDocument::IsTopLevelContentDocument()
 {
   return mIsTopLevelContentDocument;
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -135,30 +135,32 @@ typedef FrameMetrics::ViewID ViewID;
 // of shells can rapidly become huge and run us out of memory.  To solve that,
 // we'd need to re-institute a fixed version of bug 98158.
 #define MAX_DEPTH_CONTENT_FRAMES 10
 
 NS_IMPL_CYCLE_COLLECTION(nsFrameLoader,
                          mDocShell,
                          mMessageManager,
                          mChildMessageManager,
+                         mOpener,
                          mPartialSessionHistory,
                          mGroupedSessionHistory)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsIFrameLoader)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistable)
 NS_INTERFACE_MAP_END
 
-nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
+nsFrameLoader::nsFrameLoader(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetworkCreated)
   : mOwnerContent(aOwner)
   , mDetachedSubdocFrame(nullptr)
+  , mOpener(aOpener)
   , mRemoteBrowser(nullptr)
   , mChildID(0)
   , mEventMode(EVENT_MODE_NORMAL_DISPATCH)
   , mIsPrerendered(false)
   , mDepthTooGreat(false)
   , mIsTopLevelContent(false)
   , mDestroyCalled(false)
   , mNeedsAsyncDestroy(false)
@@ -169,28 +171,30 @@ nsFrameLoader::nsFrameLoader(Element* aO
   , mRemoteBrowserShown(false)
   , mRemoteFrame(false)
   , mClipSubdocument(true)
   , mClampScrollPosition(true)
   , mObservingOwnerContent(false)
   , mVisible(true)
 {
   mRemoteFrame = ShouldUseRemoteProcess();
+  MOZ_ASSERT(!mRemoteFrame || !aOpener,
+             "Cannot pass aOpener for a remote frame!");
 }
 
 nsFrameLoader::~nsFrameLoader()
 {
   if (mMessageManager) {
     mMessageManager->Disconnect();
   }
   MOZ_RELEASE_ASSERT(mDestroyCalled);
 }
 
 nsFrameLoader*
-nsFrameLoader::Create(Element* aOwner, bool aNetworkCreated)
+nsFrameLoader::Create(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetworkCreated)
 {
   NS_ENSURE_TRUE(aOwner, nullptr);
   nsIDocument* doc = aOwner->OwnerDoc();
 
   // We never create nsFrameLoaders for elements in resource documents.
   //
   // We never create nsFrameLoaders for elements in data documents, unless the
   // document is a static document.
@@ -210,17 +214,17 @@ nsFrameLoader::Create(Element* aOwner, b
   // since for a static document we know aOwner will end up in a document and
   // the nsFrameLoader will be used for its docShell.)
   //
   NS_ENSURE_TRUE(!doc->IsResourceDoc() &&
                  ((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) ||
                   doc->IsStaticDocument()),
                  nullptr);
 
-  return new nsFrameLoader(aOwner, aNetworkCreated);
+  return new nsFrameLoader(aOwner, aOpener, aNetworkCreated);
 }
 
 NS_IMETHODIMP
 nsFrameLoader::LoadFrame()
 {
   NS_ENSURE_TRUE(mOwnerContent, NS_ERROR_NOT_INITIALIZED);
 
   nsAutoString src;
@@ -1031,21 +1035,28 @@ nsFrameLoader::Hide()
   NS_ASSERTION(baseWin,
                "Found an nsIDocShell which doesn't implement nsIBaseWindow.");
   baseWin->SetVisibility(false);
   baseWin->SetParentWidget(nullptr);
 }
 
 nsresult
 nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
-                                         RefPtr<nsFrameLoader>& aFirstToSwap,
-                                         RefPtr<nsFrameLoader>& aSecondToSwap)
+                                         nsIFrameLoaderOwner* aThisOwner,
+                                         nsIFrameLoaderOwner* aOtherOwner)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+#ifdef DEBUG
+  RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader();
+  RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader();
+  MOZ_ASSERT(first == this, "aThisOwner must own this");
+  MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther");
+#endif
+
   Element* ourContent = mOwnerContent;
   Element* otherContent = aOther->mOwnerContent;
 
   if (!ourContent || !otherContent) {
     // Can't handle this
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
@@ -1183,17 +1194,22 @@ nsFrameLoader::SwapWithOtherRemoteLoader
   if (ourMessageManager) {
     ourMessageManager->SetCallback(aOther);
   }
   if (otherMessageManager) {
     otherMessageManager->SetCallback(this);
   }
   mMessageManager.swap(aOther->mMessageManager);
 
-  aFirstToSwap.swap(aSecondToSwap);
+  // Perform the actual swap of the internal refptrs. We keep a strong reference
+  // to ourselves to make sure we don't die while we overwrite our reference to
+  // ourself.
+  nsCOMPtr<nsIFrameLoader> kungFuDeathGrip(this);
+  aThisOwner->InternalSetFrameLoader(aOther);
+  aOtherOwner->InternalSetFrameLoader(kungFuDeathGrip);
 
   ourFrameFrame->EndSwapDocShells(otherFrame);
 
   ourShell->BackingScaleFactorChanged();
   otherShell->BackingScaleFactorChanged();
 
   ourDoc->FlushPendingNotifications(Flush_Layout);
   otherDoc->FlushPendingNotifications(Flush_Layout);
@@ -1274,22 +1290,26 @@ private:
   RefPtr<nsDocShell> mOtherDocShell;
   nsCOMPtr<EventTarget> mThisEventTarget;
   nsCOMPtr<EventTarget> mOtherEventTarget;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 nsresult
 nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
-                                   RefPtr<nsFrameLoader>& aFirstToSwap,
-                                   RefPtr<nsFrameLoader>& aSecondToSwap)
+                                   nsIFrameLoaderOwner* aThisOwner,
+                                   nsIFrameLoaderOwner* aOtherOwner)
 {
-  NS_PRECONDITION((aFirstToSwap == this && aSecondToSwap == aOther) ||
-                  (aFirstToSwap == aOther && aSecondToSwap == this),
-                  "Swapping some sort of random loaders?");
+#ifdef DEBUG
+  RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader();
+  RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader();
+  MOZ_ASSERT(first == this, "aThisOwner must own this");
+  MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther");
+#endif
+
   NS_ENSURE_STATE(!mInShow && !aOther->mInShow);
 
   if (IsRemoteFrame() != aOther->IsRemoteFrame()) {
     NS_WARNING("Swapping remote and non-remote frames is not currently supported");
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   Element* ourContent = mOwnerContent;
@@ -1321,17 +1341,17 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
        otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)));
   if (ourFullscreenAllowed != otherFullscreenAllowed) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   // Divert to a separate path for the remaining steps in the remote case
   if (IsRemoteFrame()) {
     MOZ_ASSERT(aOther->IsRemoteFrame());
-    return SwapWithOtherRemoteLoader(aOther, aFirstToSwap, aSecondToSwap);
+    return SwapWithOtherRemoteLoader(aOther, aThisOwner, aOtherOwner);
   }
 
   // Make sure there are no same-origin issues
   bool equal;
   nsresult rv =
     ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal);
   if (NS_FAILED(rv) || !equal) {
     // Security problems loom.  Just bail on it all
@@ -1583,17 +1603,22 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
   if (mMessageManager) {
     mMessageManager->SetCallback(aOther);
   }
   if (aOther->mMessageManager) {
     aOther->mMessageManager->SetCallback(this);
   }
   mMessageManager.swap(aOther->mMessageManager);
 
-  aFirstToSwap.swap(aSecondToSwap);
+  // Perform the actual swap of the internal refptrs. We keep a strong reference
+  // to ourselves to make sure we don't die while we overwrite our reference to
+  // ourself.
+  nsCOMPtr<nsIFrameLoader> kungFuDeathGrip(this);
+  aThisOwner->InternalSetFrameLoader(aOther);
+  aOtherOwner->InternalSetFrameLoader(kungFuDeathGrip);
 
   // Drop any cached content viewers in the two session histories.
   nsCOMPtr<nsISHistoryInternal> ourInternalHistory =
     do_QueryInterface(ourHistory);
   nsCOMPtr<nsISHistoryInternal> otherInternalHistory =
     do_QueryInterface(otherHistory);
   if (ourInternalHistory) {
     ourInternalHistory->EvictAllContentViewers();
@@ -2124,16 +2149,22 @@ nsFrameLoader::MaybeCreateDocShell()
   // Tell the window about the frame that hosts it.
   nsCOMPtr<Element> frame_element = mOwnerContent;
   NS_ASSERTION(frame_element, "frame loader owner element not a DOM element!");
 
   nsCOMPtr<nsPIDOMWindowOuter> win_private(mDocShell->GetWindow());
   nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
   if (win_private) {
     win_private->SetFrameElementInternal(frame_element);
+
+    // Set the opener window if we have one provided here
+    if (mOpener) {
+      win_private->SetOpenerWindow(mOpener, true);
+      mOpener = nullptr;
+    }
   }
 
   // This is kinda whacky, this call doesn't really create anything,
   // but it must be called to make sure things are properly
   // initialized.
   if (NS_FAILED(base_win->Create()) || !win_private) {
     // Do not call Destroy() here. See bug 472312.
     NS_WARNING("Something wrong when creating the docshell for a frameloader!");
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -70,16 +70,17 @@ class nsFrameLoader final : public nsIFr
   friend class AutoResetInShow;
   friend class AutoResetInFrameSwap;
   typedef mozilla::dom::PBrowserParent PBrowserParent;
   typedef mozilla::dom::TabParent TabParent;
   typedef mozilla::layout::RenderFrameParent RenderFrameParent;
 
 public:
   static nsFrameLoader* Create(mozilla::dom::Element* aOwner,
+                               nsPIDOMWindowOuter* aOpener,
                                bool aNetworkCreated);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFrameLoader, nsIFrameLoader)
   NS_DECL_NSIFRAMELOADER
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIWEBBROWSERPERSISTABLE
   nsresult CheckForRecursiveLoad(nsIURI* aURI);
@@ -126,22 +127,22 @@ public:
   void Hide();
 
   nsresult CloneForStatic(nsIFrameLoader* aOriginal);
 
   // The guts of an nsIFrameLoaderOwner::SwapFrameLoader implementation.  A
   // frame loader owner needs to call this, and pass in the two references to
   // nsRefPtrs for frame loaders that need to be swapped.
   nsresult SwapWithOtherLoader(nsFrameLoader* aOther,
-                               RefPtr<nsFrameLoader>& aFirstToSwap,
-                               RefPtr<nsFrameLoader>& aSecondToSwap);
+                               nsIFrameLoaderOwner* aThisOwner,
+                               nsIFrameLoaderOwner* aOtherOwner);
 
   nsresult SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
-                                     RefPtr<nsFrameLoader>& aFirstToSwap,
-                                     RefPtr<nsFrameLoader>& aSecondToSwap);
+                                     nsIFrameLoaderOwner* aThisOwner,
+                                     nsIFrameLoaderOwner* aOtherOwner);
 
   /**
    * Return the primary frame for our owning content, or null if it
    * can't be found.
    */
   nsIFrame* GetPrimaryFrameOfOwningContent() const
   {
     return mOwnerContent ? mOwnerContent->GetPrimaryFrame() : nullptr;
@@ -224,17 +225,19 @@ public:
 
   virtual nsIMessageSender* GetProcessMessageManager() const override;
 
   // public because a callback needs these.
   RefPtr<nsFrameMessageManager> mMessageManager;
   nsCOMPtr<nsIInProcessContentFrameMessageManager> mChildMessageManager;
 
 private:
-  nsFrameLoader(mozilla::dom::Element* aOwner, bool aNetworkCreated);
+  nsFrameLoader(mozilla::dom::Element* aOwner,
+                nsPIDOMWindowOuter* aOpener,
+                bool aNetworkCreated);
   ~nsFrameLoader();
 
   void SetOwnerContent(mozilla::dom::Element* aContent);
 
   bool ShouldUseRemoteProcess();
 
   /**
    * Return true if the frame is a remote frame. Return false otherwise
@@ -350,16 +353,19 @@ private:
   nsWeakFrame mDetachedSubdocFrame;
   // Stores the containing document of the frame corresponding to this
   // frame loader. This is reference is kept valid while the subframe's
   // presentation is detached and stored in mDetachedSubdocFrame. This
   // enables us to detect whether the frame has moved documents during
   // a reframe, so that we know not to restore the presentation.
   nsCOMPtr<nsIDocument> mContainerDocWhileDetached;
 
+  // An opener window which should be used when the docshell is created.
+  nsCOMPtr<nsPIDOMWindowOuter> mOpener;
+
   TabParent* mRemoteBrowser;
   uint64_t mChildID;
 
   // See nsIFrameLoader.idl. EVENT_MODE_NORMAL_DISPATCH automatically
   // forwards some input events to out-of-process content.
   uint32_t mEventMode;
 
   // Holds the last known size of the frame.
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -76,16 +76,17 @@
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/ProcessHangMonitor.h"
 #include "AudioChannelService.h"
 #include "nsAboutProtocolUtils.h"
 #include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE
 #include "PostMessageEvent.h"
+#include "DocGroup.h"
 
 // Interfaces Needed
 #include "nsIFrame.h"
 #include "nsCanvasFrame.h"
 #include "nsIWidget.h"
 #include "nsIWidgetListener.h"
 #include "nsIBaseWindow.h"
 #include "nsIDeviceSensors.h"
@@ -1221,19 +1222,20 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
 #endif
 #ifdef MOZ_B2G
     mNetworkUploadObserverEnabled(false),
     mNetworkDownloadObserverEnabled(false),
 #endif
     mCleanedUp(false),
     mDialogAbuseCount(0),
     mAreDialogsEnabled(true),
-    mCanSkipCCGeneration(0),
-    mStaticConstellation(0),
-    mConstellation(NullCString())
+#ifdef DEBUG
+    mIsValidatingTabGroup(false),
+#endif
+    mCanSkipCCGeneration(0)
 {
   AssertIsOnMainThread();
 
   nsLayoutStatics::AddRef();
 
   // Initialize the PRCList (this).
   PR_INIT_CLIST(this);
 
@@ -1257,21 +1259,16 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
       }
 
       Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
     }
   } else {
     // |this| is an outer window. Outer windows start out frozen and
     // remain frozen until they get an inner window.
     MOZ_ASSERT(IsFrozen());
-
-    // As an outer window, we may be the root of a constellation. This initial
-    // static constellation may be overridden as this window is given a parent
-    // window or an opener.
-    mStaticConstellation = WindowID();
   }
 
   // We could have failed the first time through trying
   // to create the entropy collector, so we should
   // try to get one until we succeed.
 
   gRefCnt++;
 
@@ -1425,16 +1422,21 @@ nsGlobalWindow::~nsGlobalWindow()
     // If our outer window's inner window is this window, null out the
     // outer window's reference to this window that's being deleted.
     nsGlobalWindow *outer = GetOuterWindowInternal();
     if (outer) {
       outer->MaybeClearInnerWindow(this);
     }
   }
 
+  // We don't have to leave the tab group if we are an inner window.
+  if (mTabGroup && IsOuterWindow()) {
+    mTabGroup->Leave(AsOuter());
+  }
+
   // Outer windows are always supposed to call CleanUp before letting themselves
   // be destroyed. And while CleanUp generally seems to be intended to clean up
   // outers, we've historically called it for both. Changing this would probably
   // involve auditing all of the references that inners and outers can have, and
   // separating the handling into CleanUp() and FreeInnerObjects.
   if (IsInnerWindow()) {
     CleanUp();
   } else {
@@ -2851,16 +2853,18 @@ nsGlobalWindow::SetNewDocument(nsIDocume
 
     mInnerWindow->SyncStateFromParentWindow();
   }
 
   // Add an extra ref in case we release mContext during GC.
   nsCOMPtr<nsIScriptContext> kungFuDeathGrip(mContext);
 
   aDocument->SetScriptGlobalObject(newInnerWindow);
+  MOZ_ASSERT(newInnerWindow->mTabGroup,
+             "We must have a TabGroup cached at this point");
 
   if (!aState) {
     if (reUseInnerWindow) {
 
       if (newInnerWindow->mDoc != aDocument) {
         newInnerWindow->mDoc = aDocument;
 
         // The storage objects contain the URL of the window. We have to
@@ -3027,21 +3031,18 @@ nsGlobalWindow::SetDocShell(nsIDocShell*
   MOZ_ASSERT(aDocShell);
 
   if (aDocShell == mDocShell) {
     return;
   }
 
   mDocShell = aDocShell; // Weak Reference
 
-  // Copy over the static constellation from our new parent.
-  nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetParent();
-  if (parentWindow) {
-    mStaticConstellation = Cast(parentWindow)->mStaticConstellation;
-  }
+  nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetScriptableParentOrNull();
+  MOZ_RELEASE_ASSERT(!parentWindow || !mTabGroup || mTabGroup == Cast(parentWindow)->mTabGroup);
 
   NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
 
   if (mFrames) {
     mFrames->SetDocShell(aDocShell);
   }
 
   // Get our enclosing chrome shell and retrieve its global window impl, so
@@ -3136,29 +3137,34 @@ nsGlobalWindow::DetachFromDocShell()
 }
 
 void
 nsGlobalWindow::SetOpenerWindow(nsPIDOMWindowOuter* aOpener,
                                 bool aOriginalOpener)
 {
   FORWARD_TO_OUTER_VOID(SetOpenerWindow, (aOpener, aOriginalOpener));
 
+  nsWeakPtr opener = do_GetWeakReference(aOpener);
+  if (opener == mOpener) {
+    return;
+  }
+
   NS_ASSERTION(!aOriginalOpener || !mSetOpenerWindowCalled,
                "aOriginalOpener is true, but not first call to "
                "SetOpenerWindow!");
   NS_ASSERTION(aOpener || !aOriginalOpener,
                "Shouldn't set mHadOriginalOpener if aOpener is null");
 
-  mOpener = do_GetWeakReference(aOpener);
+  mOpener = opener.forget();
   NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!");
 
-  // Copy over the static constellation from our new opener
-  if (aOpener) {
-    mStaticConstellation = Cast(aOpener)->mStaticConstellation;
-  }
+  // Check that the js visible opener matches!
+  nsPIDOMWindowOuter* contentOpener = GetSanitizedOpener(aOpener);
+  MOZ_RELEASE_ASSERT(!contentOpener || !mTabGroup ||
+    mTabGroup == Cast(contentOpener)->mTabGroup);
 
   if (aOriginalOpener) {
     MOZ_ASSERT(!mHadOriginalOpener,
                "Probably too late to call ComputeIsSecureContext again");
     mHadOriginalOpener = true;
     mOriginalOpenerWasSecureContext =
       aOpener->GetCurrentInnerWindow()->IsSecureContext();
   }
@@ -4721,64 +4727,75 @@ nsGlobalWindow::GetControllers(nsIContro
   ErrorResult rv;
   nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
   controllers.forget(aResult);
 
   return rv.StealNSResult();
 }
 
 nsPIDOMWindowOuter*
-nsGlobalWindow::GetOpenerWindowOuter()
-{
-  MOZ_RELEASE_ASSERT(IsOuterWindow());
-
-  nsCOMPtr<nsPIDOMWindowOuter> opener = do_QueryReferent(mOpener);
-  if (!opener) {
+nsGlobalWindow::GetSanitizedOpener(nsPIDOMWindowOuter* aOpener)
+{
+  if (!aOpener) {
     return nullptr;
   }
 
-  nsGlobalWindow* win = nsGlobalWindow::Cast(opener);
-
-  // First, check if we were called from a privileged chrome script
-  if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
-    // Catch the case where we're chrome but the opener is not...
-    if (GetPrincipal() == nsContentUtils::GetSystemPrincipal() &&
-        win->GetPrincipal() != nsContentUtils::GetSystemPrincipal()) {
-      return nullptr;
-    }
-    return opener;
-  }
+  nsGlobalWindow* win = nsGlobalWindow::Cast(aOpener);
 
   // First, ensure that we're not handing back a chrome window to content:
   if (win->IsChromeWindow()) {
     return nullptr;
   }
 
   // We don't want to reveal the opener if the opener is a mail window,
   // because opener can be used to spoof the contents of a message (bug 105050).
   // So, we look in the opener's root docshell to see if it's a mail window.
-  nsCOMPtr<nsIDocShell> openerDocShell = opener->GetDocShell();
+  nsCOMPtr<nsIDocShell> openerDocShell = aOpener->GetDocShell();
 
   if (openerDocShell) {
     nsCOMPtr<nsIDocShellTreeItem> openerRootItem;
     openerDocShell->GetRootTreeItem(getter_AddRefs(openerRootItem));
     nsCOMPtr<nsIDocShell> openerRootDocShell(do_QueryInterface(openerRootItem));
     if (openerRootDocShell) {
       uint32_t appType;
       nsresult rv = openerRootDocShell->GetAppType(&appType);
       if (NS_SUCCEEDED(rv) && appType != nsIDocShell::APP_TYPE_MAIL) {
-        return opener;
+        return aOpener;
       }
     }
   }
 
   return nullptr;
 }
 
 nsPIDOMWindowOuter*
+nsGlobalWindow::GetOpenerWindowOuter()
+{
+  MOZ_RELEASE_ASSERT(IsOuterWindow());
+
+  nsCOMPtr<nsPIDOMWindowOuter> opener = do_QueryReferent(mOpener);
+
+  if (!opener) {
+    return nullptr;
+  }
+
+  // First, check if we were called from a privileged chrome script
+  if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
+    // Catch the case where we're chrome but the opener is not...
+    if (GetPrincipal() == nsContentUtils::GetSystemPrincipal() &&
+        nsGlobalWindow::Cast(opener)->GetPrincipal() != nsContentUtils::GetSystemPrincipal()) {
+      return nullptr;
+    }
+    return opener;
+  }
+
+  return GetSanitizedOpener(opener);
+}
+
+nsPIDOMWindowOuter*
 nsGlobalWindow::GetOpenerWindow(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetOpenerWindowOuter, (), aError, nullptr);
 }
 
 void
 nsGlobalWindow::GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
                           ErrorResult& aError)
@@ -4792,21 +4809,19 @@ nsGlobalWindow::GetOpener(JSContext* aCx
   }
 
   aError = nsContentUtils::WrapNative(aCx, opener, aRetval);
 }
 
 already_AddRefed<nsPIDOMWindowOuter>
 nsGlobalWindow::GetOpener()
 {
-  FORWARD_TO_INNER(GetOpener, (), nullptr);
-
-  ErrorResult dummy;
-  nsCOMPtr<nsPIDOMWindowOuter> opener = GetOpenerWindow(dummy);
-  dummy.SuppressException();
+  FORWARD_TO_OUTER(GetOpener, (), nullptr);
+
+  nsCOMPtr<nsPIDOMWindowOuter> opener = GetOpenerWindowOuter();
   return opener.forget();
 }
 
 void
 nsGlobalWindow::SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
                           ErrorResult& aError)
 {
   // Check if we were called from a privileged chrome script.  If not, and if
@@ -5979,18 +5994,17 @@ nsGlobalWindow::GetTop(mozilla::ErrorRes
 
 nsPIDOMWindowOuter*
 nsGlobalWindow::GetChildWindow(const nsAString& aName)
 {
   nsCOMPtr<nsIDocShell> docShell(GetDocShell());
   NS_ENSURE_TRUE(docShell, nullptr);
 
   nsCOMPtr<nsIDocShellTreeItem> child;
-  docShell->FindChildWithName(PromiseFlatString(aName).get(),
-                              false, true, nullptr, nullptr,
+  docShell->FindChildWithName(aName, false, true, nullptr, nullptr,
                               getter_AddRefs(child));
 
   return child ? child->GetWindow() : nullptr;
 }
 
 bool
 nsGlobalWindow::DispatchCustomEvent(const nsAString& aEventName)
 {
@@ -6090,17 +6104,17 @@ nsGlobalWindow::WindowExists(const nsASt
     caller = GetCallerDocShellTreeItem();
   }
 
   if (!caller) {
     caller = mDocShell;
   }
 
   nsCOMPtr<nsIDocShellTreeItem> namedItem;
-  mDocShell->FindItemWithName(PromiseFlatString(aName).get(), nullptr, caller,
+  mDocShell->FindItemWithName(aName, nullptr, caller,
                               getter_AddRefs(namedItem));
   return namedItem != nullptr;
 }
 
 already_AddRefed<nsIWidget>
 nsGlobalWindow::GetMainWidget()
 {
   FORWARD_TO_OUTER(GetMainWidget, (), nullptr);
@@ -13622,29 +13636,31 @@ nsGlobalWindow::NotifyActiveVRDisplaysCh
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow,
                                                   nsGlobalWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserDOMWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGroupMessageManagers)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOpenerForInitialContentBrowser)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGlobalChromeWindow,
                                                 nsGlobalWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserDOMWindow)
   if (tmp->mMessageManager) {
     static_cast<nsFrameMessageManager*>(
       tmp->mMessageManager.get())->Disconnect();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
   }
   tmp->DisconnectAndClearGroupMessageManagers();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGroupMessageManagers)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOpenerForInitialContentBrowser)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 // QueryInterface implementation for nsGlobalChromeWindow
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsGlobalChromeWindow)
   NS_INTERFACE_MAP_ENTRY(nsIDOMChromeWindow)
 NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow)
 
 NS_IMPL_ADDREF_INHERITED(nsGlobalChromeWindow, nsGlobalWindow)
@@ -14105,16 +14121,34 @@ nsGlobalWindow::GetGroupMessageManager(c
                                                parent,
                                                MM_CHROME | MM_BROADCASTER);
     myself->mGroupMessageManagers.Put(aGroup, messageManager);
   }
 
   return messageManager;
 }
 
+nsresult
+nsGlobalChromeWindow::SetOpenerForInitialContentBrowser(mozIDOMWindowProxy* aOpenerWindow)
+{
+  MOZ_RELEASE_ASSERT(IsOuterWindow());
+  MOZ_ASSERT(!mOpenerForInitialContentBrowser);
+  mOpenerForInitialContentBrowser = aOpenerWindow;
+  return NS_OK;
+}
+
+nsresult
+nsGlobalChromeWindow::TakeOpenerForInitialContentBrowser(mozIDOMWindowProxy** aOpenerWindow)
+{
+  MOZ_RELEASE_ASSERT(IsOuterWindow());
+  // Intentionally forget our own member
+  mOpenerForInitialContentBrowser.forget(aOpenerWindow);
+  return NS_OK;
+}
+
 // nsGlobalModalWindow implementation
 
 // QueryInterface implementation for nsGlobalModalWindow
 NS_INTERFACE_MAP_BEGIN(nsGlobalModalWindow)
   NS_INTERFACE_MAP_ENTRY(nsIDOMModalContentWindow)
 NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow)
 
 NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
@@ -14588,54 +14622,121 @@ nsGlobalWindow::CheckForDPIChange()
     if (presContext) {
       if (presContext->DeviceContext()->CheckDPIChange()) {
         presContext->UIResolutionChanged();
       }
     }
   }
 }
 
-void
-nsGlobalWindow::GetConstellation(nsACString& aConstellation)
-{
-  FORWARD_TO_INNER_VOID(GetConstellation, (aConstellation));
+mozilla::dom::TabGroup*
+nsGlobalWindow::TabGroupOuter()
+{
+  MOZ_RELEASE_ASSERT(IsOuterWindow());
+
+  // This method is valid both on inner and outer windows, which is a
+  // Outer windows lazily join TabGroups when requested. This is usually done
+  // because a document is getting its NodePrincipal, and asking for the
+  // TabGroup to determine its DocGroup.
+  if (!mTabGroup) {
+    // Get mOpener ourselves, instead of relying on GetOpenerWindowOuter,
+    // because that way we dodge the LegacyIsCallerChromeOrNativeCode() call
+    // which we want to return false.
+    nsCOMPtr<nsPIDOMWindowOuter> piOpener = do_QueryReferent(mOpener);
+    nsPIDOMWindowOuter* opener = GetSanitizedOpener(piOpener);
+    nsPIDOMWindowOuter* parent = GetScriptableParentOrNull();
+    MOZ_ASSERT(!parent || !opener, "Only one of parent and opener may be provided");
+
+    mozilla::dom::TabGroup* toJoin = nullptr;
+    if (GetDocShell()->ItemType() == nsIDocShellTreeItem::typeChrome) {
+      toJoin = TabGroup::GetChromeTabGroup();
+    } else if (opener) {
+      toJoin = opener->TabGroup();
+    } else if (parent) {
+      toJoin = parent->TabGroup();
+    }
+    mTabGroup = mozilla::dom::TabGroup::Join(AsOuter(), toJoin);
+  }
+  MOZ_ASSERT(mTabGroup);
 
 #ifdef DEBUG
-  RefPtr<nsGlobalWindow> outer = GetOuterWindowInternal();
-  MOZ_ASSERT(outer, "We should have an outer window");
-  RefPtr<nsGlobalWindow> top = outer->GetTopInternal();
-  RefPtr<nsPIDOMWindowOuter> opener = outer->GetOpener();
-  MOZ_ASSERT(!top || (top->mStaticConstellation ==
-                      outer->mStaticConstellation));
-  MOZ_ASSERT(!opener || (Cast(opener)->mStaticConstellation ==
-                         outer->mStaticConstellation));
+  // Ensure that we don't recurse forever
+  if (!mIsValidatingTabGroup) {
+    mIsValidatingTabGroup = true;
+    // We only need to do this check if we aren't in the chrome tab group
+    if (GetDocShell()->ItemType() == nsIDocShellTreeItem::typeChrome) {
+      MOZ_ASSERT(mTabGroup == TabGroup::GetChromeTabGroup());
+    } else {
+      // Sanity check that our tabgroup matches our opener or parent.
+      RefPtr<nsPIDOMWindowOuter> parent = GetScriptableParentOrNull();
+      MOZ_ASSERT_IF(parent, parent->TabGroup() == mTabGroup);
+      nsCOMPtr<nsPIDOMWindowOuter> piOpener = do_QueryReferent(mOpener);
+      nsPIDOMWindowOuter* opener = GetSanitizedOpener(piOpener);
+      MOZ_ASSERT_IF(opener && Cast(opener) != this, opener->TabGroup() == mTabGroup);
+    }
+    mIsValidatingTabGroup = false;
+  }
 #endif
 
-  if (mConstellation.IsVoid()) {
-    mConstellation.Truncate();
-    // The dynamic constellation part comes from the eTLD+1 for the principal's URI.
-    nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
-    nsCOMPtr<nsIURI> uri;
-    nsresult rv = principal->GetURI(getter_AddRefs(uri));
-    if (NS_SUCCEEDED(rv)) {
-      nsCOMPtr<nsIEffectiveTLDService> tldService =
-        do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
-      if (tldService) {
-        rv = tldService->GetBaseDomain(uri, 0, mConstellation);
-        if (NS_FAILED(rv)) {
-          mConstellation.Truncate();
-        }
-      }
-    }
-
-    // Get the static constellation from the outer window object.
-    mConstellation.AppendPrintf("^%llu", GetOuterWindowInternal()->mStaticConstellation);
-  }
-
-  aConstellation.Assign(mConstellation);
+  return mTabGroup;
+}
+
+mozilla::dom::TabGroup*
+nsGlobalWindow::TabGroupInner()
+{
+  MOZ_RELEASE_ASSERT(IsInnerWindow());
+
+  // If we don't have a TabGroup yet, try to get it from the outer window and
+  // cache it.
+  if (!mTabGroup) {
+    nsGlobalWindow* outer = GetOuterWindowInternal();
+    // This will never be called without either an outer window, or a cached tab group.
+    // This is because of the following:
+    // * This method is only called on inner windows
+    // * This method is called as a document is attached to it's script global
+    //   by the document
+    // * Inner windows are created in nsGlobalWindow::SetNewDocument, which
+    //   immediately sets a document, which will call this method, causing
+    //   the TabGroup to be cached.
+    MOZ_RELEASE_ASSERT(outer, "Inner window without outer window has no cached tab group!");
+    mTabGroup = outer->TabGroup();
+  }
+  MOZ_ASSERT(mTabGroup);
+
+#ifdef DEBUG
+  nsGlobalWindow* outer = GetOuterWindowInternal();
+  MOZ_ASSERT_IF(outer, outer->TabGroup() == mTabGroup);
+#endif
+
+  return mTabGroup;
+}
+
+template<typename T>
+mozilla::dom::TabGroup*
+nsPIDOMWindow<T>::TabGroup()
+{
+  nsGlobalWindow* globalWindow =
+    static_cast<nsGlobalWindow*>(
+        reinterpret_cast<nsPIDOMWindow<nsISupports>*>(this));
+
+  if (IsInnerWindow()) {
+    return globalWindow->TabGroupInner();
+  }
+  return globalWindow->TabGroupOuter();
+}
+
+template<typename T>
+mozilla::dom::DocGroup*
+nsPIDOMWindow<T>::GetDocGroup()
+{
+  nsIDocument* doc = GetExtantDoc();
+  if (doc) {
+    return doc->GetDocGroup();
+  }
+  return nullptr;
 }
 
 nsGlobalWindow::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
   nsGlobalWindow* aWindow MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
   MOZ_ASSERT(aWindow);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -81,16 +81,17 @@ class nsICSSDeclaration;
 class nsIDocShellTreeOwner;
 class nsIDOMOfflineResourceList;
 class nsIScrollableFrame;
 class nsIControllers;
 class nsIJSID;
 class nsIScriptContext;
 class nsIScriptTimeoutHandler;
 class nsIWebBrowserChrome;
+class mozIDOMWindowProxy;
 
 class nsDOMWindowList;
 class nsScreen;
 class nsHistory;
 class nsGlobalWindowObserver;
 class nsGlobalWindow;
 class nsDOMWindowUtils;
 class nsIIdleService;
@@ -101,31 +102,33 @@ class nsWindowSizes;
 namespace mozilla {
 class DOMEventTargetHelper;
 namespace dom {
 class BarProp;
 struct ChannelPixelLayout;
 class Console;
 class Crypto;
 class CustomElementRegistry;
+class DocGroup;
 class External;
 class Function;
 class Gamepad;
 enum class ImageBitmapFormat : uint32_t;
 class Location;
 class MediaQueryList;
 class MozSelfSupport;
 class Navigator;
 class OwningExternalOrWindowProxy;
 class Promise;
 class PostMessageEvent;
 struct RequestInit;
 class RequestOrUSVString;
 class Selection;
 class SpeechSynthesis;
+class TabGroup;
 class U2F;
 class VRDisplay;
 class VREventObserver;
 class WakeLock;
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 class WindowOrientationObserver;
 #endif
 namespace cache {
@@ -915,16 +918,19 @@ public:
 
   nsresult GetPrompter(nsIPrompt** aPrompt) override;
 protected:
   explicit nsGlobalWindow(nsGlobalWindow *aOuterWindow);
   nsPIDOMWindowOuter* GetOpenerWindowOuter();
   // Initializes the mWasOffline member variable
   void InitWasOffline();
 public:
+  nsPIDOMWindowOuter*
+  GetSanitizedOpener(nsPIDOMWindowOuter* aOpener);
+
   nsPIDOMWindowOuter* GetOpenerWindow(mozilla::ErrorResult& aError);
   void GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
                  mozilla::ErrorResult& aError);
   already_AddRefed<nsPIDOMWindowOuter> GetOpener() override;
   void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
                  mozilla::ErrorResult& aError);
   already_AddRefed<nsPIDOMWindowOuter> GetParentOuter();
   already_AddRefed<nsPIDOMWindowOuter> GetParent(mozilla::ErrorResult& aError);
@@ -1734,19 +1740,23 @@ private:
   void FireOnNewGlobalObject();
 
   void DisconnectEventTargetObjects();
 
   // Called only on outer windows to compute the value that will be returned by
   // IsSecureContext() for the inner window that corresponds to aDocument.
   bool ComputeIsSecureContext(nsIDocument* aDocument);
 
-public:
+  // nsPIDOMWindow<T> should be able to see these helper methods.
+  friend class nsPIDOMWindow<mozIDOMWindowProxy>;
+  friend class nsPIDOMWindow<mozIDOMWindow>;
+  friend class nsPIDOMWindow<nsISupports>;
 
-  void GetConstellation(nsACString& aConstellation);
+  mozilla::dom::TabGroup* TabGroupInner();
+  mozilla::dom::TabGroup* TabGroupOuter();
 
 protected:
   // These members are only used on outer window objects. Make sure
   // you never set any of these on an inner object!
   bool                          mFullScreen : 1;
   bool                          mFullscreenMode : 1;
   bool                          mIsClosed : 1;
   bool                          mInClose : 1;
@@ -1941,27 +1951,30 @@ protected:
   nsAutoPtr<mozilla::dom::WindowOrientationObserver> mOrientationChangeObserver;
 #endif
 
 #ifdef MOZ_WEBSPEECH
   // mSpeechSynthesis is only used on inner windows.
   RefPtr<mozilla::dom::SpeechSynthesis> mSpeechSynthesis;
 #endif
 
+#ifdef DEBUG
+  // This member is used in the debug only assertions in TabGroup()
+  // to catch cyclic parent/opener trees and not overflow the stack.
+  bool mIsValidatingTabGroup;
+#endif
+
   // This is the CC generation the last time we called CanSkip.
   uint32_t mCanSkipCCGeneration;
 
   // The VR Displays for this window
   nsTArray<RefPtr<mozilla::dom::VRDisplay>> mVRDisplays;
 
   nsAutoPtr<mozilla::dom::VREventObserver> mVREventObserver;
 
-  uint64_t mStaticConstellation; // Only used on outer windows
-  nsCString mConstellation; // Only used on inner windows
-
   friend class nsDOMScriptableHelper;
   friend class nsDOMWindowUtils;
   friend class mozilla::dom::PostMessageEvent;
   friend class DesktopNotification;
 
   static WindowByIdTable* sWindowsById;
   static bool sWarnedAboutWindowInternal;
 };
@@ -2048,16 +2061,17 @@ public:
 
   nsCOMPtr<nsIBrowserDOMWindow> mBrowserDOMWindow;
   nsCOMPtr<nsIMessageBroadcaster> mMessageManager;
   nsInterfaceHashtable<nsStringHashKey, nsIMessageBroadcaster> mGroupMessageManagers;
   // A weak pointer to the nsPresShell that we are doing fullscreen for.
   // The pointer being set indicates we've set the IsInFullscreenChange
   // flag on this pres shell.
   nsWeakPtr mFullscreenPresShell;
+  nsCOMPtr<mozIDOMWindowProxy> mOpenerForInitialContentBrowser;
 };
 
 /*
  * nsGlobalModalWindow inherits from nsGlobalWindow. It is the global
  * object created for a modal content windows only (i.e. not modal
  * chrome dialogs).
  */
 class nsGlobalModalWindow : public nsGlobalWindow,
--- a/dom/base/nsHostObjectURI.cpp
+++ b/dom/base/nsHostObjectURI.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsHostObjectURI.h"
 
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
+#include "nsHostObjectProtocolHandler.h"
 
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/URIUtils.h"
 
 static NS_DEFINE_CID(kHOSTOBJECTURICID, NS_HOSTOBJECTURI_CID);
 
 static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
                      NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -118,16 +118,17 @@ class Rule;
 namespace dom {
 class Animation;
 class AnonymousContent;
 class Attr;
 class BoxObject;
 class CDATASection;
 class Comment;
 struct CustomElementDefinition;
+class DocGroup;
 class DocumentFragment;
 class DocumentTimeline;
 class DocumentType;
 class DOMImplementation;
 class DOMStringList;
 class Element;
 struct ElementCreationOptions;
 struct ElementRegistrationOptions;
@@ -2839,16 +2840,18 @@ public:
   bool HasScriptsBlockedBySandbox();
 
   void ReportHasScrollLinkedEffect();
   bool HasScrollLinkedEffect() const
   {
     return mHasScrollLinkedEffect;
   }
 
+  mozilla::dom::DocGroup* GetDocGroup();
+
 protected:
   bool GetUseCounter(mozilla::UseCounter aUseCounter)
   {
     return mUseCounters[aUseCounter];
   }
 
   void SetChildDocumentUseCounter(mozilla::UseCounter aUseCounter)
   {
@@ -3295,16 +3298,18 @@ protected:
   // Flags for whether we've notified our top-level "page" of a use counter
   // for this child document.
   std::bitset<mozilla::eUseCounter_Count> mNotifiedPageForUseCounter;
 
   // Whether the user has interacted with the document or not:
   bool mUserHasInteracted;
 
   mozilla::TimeStamp mPageUnloadingEventTimeStamp;
+
+  RefPtr<mozilla::dom::DocGroup> mDocGroup;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
 
 /**
  * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
  * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
  * object is deleted.
--- a/dom/base/nsIFrameLoader.idl
+++ b/dom/base/nsIFrameLoader.idl
@@ -279,9 +279,19 @@ interface nsIFrameLoaderOwner : nsISuppo
    * iframes.
    */
   readonly attribute mozIApplication parentApplication;
 
   /**
    * Puts the FrameLoaderOwner in prerendering mode.
    */
   void setIsPrerendered();
+
+  /**
+   * This method is used internally by SwapFrameLoaders to set the frame loader
+   * on the target nsFrameLoader.
+   *
+   * Avoid using this method outside of that context, and instead prefer using
+   * SwapFrameLoaders.
+   */
+  [noscript, notxpcom] void
+  internalSetFrameLoader(in nsIFrameLoader aNewFrameLoader);
 };
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -1253,22 +1253,34 @@ nsObjectLoadingContent::GetParentApplica
   if (!aApplication) {
     return NS_ERROR_FAILURE;
   }
 
   *aApplication = nullptr;
   return NS_OK;
 }
 
+void
+nsObjectLoadingContent::PresetOpenerWindow(mozIDOMWindowProxy* aWindow, mozilla::ErrorResult& aRv)
+{
+  aRv.Throw(NS_ERROR_FAILURE);
+}
+
 NS_IMETHODIMP
 nsObjectLoadingContent::SetIsPrerendered()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+void
+nsObjectLoadingContent::InternalSetFrameLoader(nsIFrameLoader* aNewFrameLoader)
+{
+  MOZ_CRASH("You shouldn't be calling this function, it doesn't make any sense on this type.");
+}
+
 NS_IMETHODIMP
 nsObjectLoadingContent::GetActualType(nsACString& aType)
 {
   aType = mContentType;
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -2445,16 +2457,17 @@ nsObjectLoadingContent::LoadObject(bool 
         // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters
         // requires documents have a channel, so this is not a valid state.
         NS_NOTREACHED("Attempting to load a document without a channel");
         mType = eType_Null;
         break;
       }
 
       mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
+                                           /* aOpener = */ nullptr,
                                            mNetworkCreated);
       if (!mFrameLoader) {
         NS_NOTREACHED("nsFrameLoader::Create failed");
         mType = eType_Null;
         break;
       }
 
       rv = mFrameLoader->CheckForRecursiveLoad(mURI);
@@ -2889,17 +2902,17 @@ nsObjectLoadingContent::CreateStaticClon
     aDest->mPrintFrame = thisObj->mPrintFrame;
   } else {
     aDest->mPrintFrame = const_cast<nsObjectLoadingContent*>(this)->GetExistingFrame();
   }
 
   if (mFrameLoader) {
     nsCOMPtr<nsIContent> content =
       do_QueryInterface(static_cast<nsIImageLoadingContent*>(aDest));
-    nsFrameLoader* fl = nsFrameLoader::Create(content->AsElement(), false);
+    nsFrameLoader* fl = nsFrameLoader::Create(content->AsElement(), nullptr, false);
     if (fl) {
       aDest->mFrameLoader = fl;
       mFrameLoader->CreateStaticClone(fl);
     }
   }
 }
 
 NS_IMETHODIMP
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -251,16 +251,18 @@ class nsObjectLoadingContent : public ns
       return runID;
     }
 
     bool IsRewrittenYoutubeEmbed() const
     {
       return mRewrittenYoutubeEmbed;
     }
 
+    void PresetOpenerWindow(mozIDOMWindowProxy* aOpenerWindow, mozilla::ErrorResult& aRv);
+
   protected:
     /**
      * Begins loading the object when called
      *
      * Attributes of |this| QI'd to nsIContent will be inspected, depending on
      * the node type. This function currently assumes it is a <applet>,
      * <object>, or <embed> tag.
      *
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -38,16 +38,18 @@ class nsPIWindowRoot;
 class nsXBLPrototypeHandler;
 struct nsTimeout;
 
 typedef uint32_t SuspendTypes;
 
 namespace mozilla {
 namespace dom {
 class AudioContext;
+class DocGroup;
+class TabGroup;
 class Element;
 class Performance;
 class ServiceWorkerRegistration;
 class CustomElementRegistry;
 } // namespace dom
 } // namespace mozilla
 
 // Popup control state enum. The values in this enum must go from most
@@ -570,16 +572,20 @@ public:
   virtual nsresult SetFullScreen(bool aFullScreen) = 0;
 
   virtual nsresult Focus() = 0;
   virtual nsresult Close() = 0;
 
   virtual nsresult MoveBy(int32_t aXDif, int32_t aYDif) = 0;
   virtual nsresult UpdateCommands(const nsAString& anAction, nsISelection* aSel, int16_t aReason) = 0;
 
+  mozilla::dom::TabGroup* TabGroup();
+
+  mozilla::dom::DocGroup* GetDocGroup();
+
 protected:
   // The nsPIDOMWindow constructor. The aOuterWindow argument should
   // be null if and only if the created window itself is an outer
   // window. In all other cases aOuterWindow should be the outer
   // window for the inner window that is being created.
   explicit nsPIDOMWindow<T>(nsPIDOMWindowOuter *aOuterWindow);
 
   ~nsPIDOMWindow<T>();
@@ -681,16 +687,19 @@ protected:
 
   // the element within the document that is currently focused when this
   // window is active
   nsCOMPtr<nsIContent> mFocusedNode;
 
   // The AudioContexts created for the current document, if any.
   nsTArray<mozilla::dom::AudioContext*> mAudioContexts; // Weak
 
+  // This is present both on outer and inner windows.
+  RefPtr<mozilla::dom::TabGroup> mTabGroup;
+
   // A unique (as long as our 64-bit counter doesn't roll over) id for
   // this window.
   uint64_t mWindowID;
 
   // This is only used by the inner window. Set to true once we've sent
   // the (chrome|content)-document-global-created notification.
   bool mHasNotifiedGlobalCreated;
 
--- a/dom/base/test/test_postMessages.html
+++ b/dom/base/test/test_postMessages.html
@@ -6,29 +6,34 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 
 <body>
 <input id="fileList" type="file"></input>
 <script type="application/javascript;version=1.7">
 
 function setup_tests() {
-  SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true]]}, next);
+  SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
+                                     ["javascript.options.wasm", true]]}, next);
 }
 
 function getType(a) {
   if (a === null || a === undefined)
     return 'null';
 
   if (Array.isArray(a))
     return 'array';
 
   if (typeof a == 'object')
     return 'object';
 
+  if (SpecialPowers.Cu.getJSTestingFunctions().wasmIsSupported() &&
+      a instanceof WebAssembly.Module)
+    return "wasm";
+
   return 'primitive';
 }
 
 function compare(a, b) {
   is (getType(a), getType(b), 'Type matches');
 
   var type = getType(a);
   if (type == 'array') {
@@ -54,46 +59,57 @@ function compare(a, b) {
 
     for (var p in a) {
       compare(a[p], b[p]);
     }
 
     return;
   }
 
+  if (type == 'wasm') {
+    var wasmA = new WebAssembly.Instance(a);
+    ok(wasmA instanceof WebAssembly.Instance, "got an instance");
+
+    var wasmB = new WebAssembly.Instance(b);
+    ok(wasmB instanceof WebAssembly.Instance, "got an instance");
+
+    ok(wasmA.exports.foo() === wasmB.exports.foo(), "Same result!");
+    ok(wasmB.exports.foo() === 42, "We want 42");
+  }
+
   if (type != 'null') {
     is (a.toSource(), b.toSource(), 'Matching using toSource()');
   }
 }
 
 var clonableObjects = [
-  'hello world',
-  123,
-  null,
-  true,
-  new Date(),
-  [ 1, 'test', true, new Date() ],
-  { a: true, b:  null, c: new Date(), d: [ true, false, {} ] },
-  new Blob([123], { type: 'plain/text' }),
-  new ImageData(2, 2),
+  { target: 'all', data: 'hello world' },
+  { target: 'all', data: 123 },
+  { target: 'all', data: null },
+  { target: 'all', data: true },
+  { target: 'all', data: new Date() },
+  { target: 'all', data: [ 1, 'test', true, new Date() ] },
+  { target: 'all', data: { a: true, b:  null, c: new Date(), d: [ true, false, {} ] } },
+  { target: 'all', data: new Blob([123], { type: 'plain/text' }) },
+  { target: 'all', data: new ImageData(2, 2) },
 ];
 
 function create_fileList() {
   var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js");
   var script = SpecialPowers.loadChromeScript(url);
 
   function onOpened(message) {
     var fileList = document.getElementById('fileList');
     SpecialPowers.wrap(fileList).mozSetFileArray([message.file]);
 
     // Just a simple test
     var domFile = fileList.files[0];
     is(domFile.name, "prefs.js", "fileName should be prefs.js");
 
-    clonableObjects.push(fileList.files);
+    clonableObjects.push({ target: 'all', data: fileList.files });
     script.destroy();
     next();
   }
 
   script.addMessageListener("file.opened", onOpened);
   script.sendAsyncMessage("file.open");
 }
 
@@ -110,56 +126,120 @@ function create_directory() {
     var fileList = document.getElementById('fileList');
     SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
 
     fileList.getFilesAndDirectories().then(function(list) {
       // Just a simple test
       is(list.length, 1, "This list has 1 element");
       ok(list[0] instanceof Directory, "We have a directory.");
 
-      clonableObjects.push(list[0]);
+      clonableObjects.push({ target: 'all', data: list[0] });
       script.destroy();
       next();
     });
   }
 
   script.addMessageListener("dir.opened", onOpened);
   script.sendAsyncMessage("dir.open");
 }
 
+function create_wasmModule() {
+  info("Checking if we can play with WebAssembly...");
+
+  if (!SpecialPowers.Cu.getJSTestingFunctions().wasmIsSupported()) {
+    next();
+    return;
+  }
+
+  ok(WebAssembly, "WebAssembly object should exist");
+  ok(WebAssembly.compile, "WebAssembly.compile function should exist");
+
+  const wasmTextToBinary = SpecialPowers.unwrap(SpecialPowers.Cu.getJSTestingFunctions().wasmTextToBinary);
+  const fooModuleCode = wasmTextToBinary(`(module
+    (func $foo (result i32) (i32.const 42))
+    (export "foo" $foo)
+  )`, 'new-format');
+
+  WebAssembly.compile(fooModuleCode).then((m) => {
+    ok(m instanceof WebAssembly.Module, "The WasmModule has been compiled.");
+    clonableObjects.push({ target: 'sameProcess', data: m });
+    next();
+  }, () => {
+    ok(false, "The compilation of the wasmModule failed.");
+  });
+}
+
 function runTests(obj) {
-  ok(('clonableObjects' in obj) &&
+  ok(('clonableObjectsEveryWhere' in obj) &&
+     ('clonableObjectsSameProcess' in obj) &&
      ('transferableObjects' in obj) &&
-     (obj.clonableObjects || obj.transferableObjects), "We must run some test!");
+     (obj.clonableObjectsEveryWhere || obj.clonableObjectsSameProcess || obj.transferableObjects), "We must run some test!");
 
-  // cloning tests
+  // cloning tests - everyWhere
   new Promise(function(resolve, reject) {
-    if (!obj.clonableObjects) {
+    if (!obj.clonableObjectsEveryWhere) {
       resolve();
       return;
     }
 
     var clonableObjectsId = 0;
     function runClonableTest() {
       if (clonableObjectsId >= clonableObjects.length) {
         resolve();
         return;
       }
 
       var object = clonableObjects[clonableObjectsId++];
 
-      obj.send(object, []).then(function(received) {
-        compare(received.data, object);
+      if (object.target != 'all') {
+        runClonableTest();
+        return;
+      }
+
+      obj.send(object.data, []).then(function(received) {
+        compare(received.data, object.data);
         runClonableTest();
       });
     }
 
     runClonableTest();
   })
 
+  // clonable same process
+  .then(function() {
+    return new Promise(function(resolve, reject) {
+      if (!obj.clonableObjectsSameProcess) {
+        resolve();
+        return;
+      }
+
+      var clonableObjectsId = 0;
+      function runClonableTest() {
+        if (clonableObjectsId >= clonableObjects.length) {
+          resolve();
+          return;
+        }
+
+        var object = clonableObjects[clonableObjectsId++];
+
+        if (object.target != 'sameProcess') {
+          runClonableTest();
+          return;
+        }
+
+        obj.send(object.data, []).then(function(received) {
+          compare(received.data, object.data);
+          runClonableTest();
+        });
+      }
+
+      runClonableTest();
+    });
+  })
+
   // transfering tests
   .then(function() {
     if (!obj.transferableObjects) {
       return;
     }
 
     // MessagePort
     return new Promise(function(r, rr) {
@@ -229,17 +309,18 @@ function test_windowToWindow() {
     }
 
     let tmp = resolve;
     resolve = null;
     tmp({ data: e.data, ports: e.ports });
   }
 
   runTests({
-    clonableObjects: true,
+    clonableObjectsEveryWhere: true,
+    clonableObjectsSameProcess: true,
     transferableObjects: true,
     send: function(what, ports) {
       return new Promise(function(r, rr) {
         resolve = r;
 
         try {
           postMessage(what, '*', ports);
         } catch(e) {
@@ -282,17 +363,18 @@ function test_windowToIframeURL(url) {
     resolve = null;
     tmp({ data: e.data, ports: e.ports });
   }
 
   var ifr = document.createElement('iframe');
   ifr.src = url;
   ifr.onload = function() {
     runTests({
-      clonableObjects: true,
+      clonableObjectsEveryWhere: true,
+      clonableObjectsSameProcess: true,
       transferableObjects: true,
       send: function(what, ports) {
         return new Promise(function(r, rr) {
           resolve = r;
           try {
             ifr.contentWindow.postMessage(what, '*', ports);
           } catch(e) {
             resolve = null;
@@ -329,17 +411,18 @@ function test_workers() {
       }
 
       let tmp = resolve;
       resolve = null;
       tmp({ data: e.data, ports: e.ports });
     }
 
     runTests({
-      clonableObjects: true,
+      clonableObjectsEveryWhere: true,
+      clonableObjectsSameProcess: true,
       transferableObjects: true,
       send: function(what, ports) {
         return new Promise(function(r, rr) {
           resolve = r;
           try {
             w.postMessage(what, ports);
           } catch(e) {
             resolve = null;
@@ -372,17 +455,18 @@ function test_broadcastChannel() {
     }
 
     let tmp = resolve;
     resolve = null;
     tmp({ data: e.data, ports: [] });
   }
 
   runTests({
-    clonableObjects: true,
+    clonableObjectsEveryWhere: true,
+    clonableObjectsSameProcess: false,
     transferableObjects: false,
     send: function(what, ports) {
       return new Promise(function(r, rr) {
         if (ports.length) {
           rr();
           return;
         }
 
@@ -417,17 +501,18 @@ function test_broadcastChannel_inWorkers
       }
 
       let tmp = resolve;
       resolve = null;
       tmp({ data: e.data, ports: e.ports });
     }
 
     runTests({
-      clonableObjects: true,
+      clonableObjectsEveryWhere: true,
+      clonableObjectsSameProcess: false,
       transferableObjects: false,
       send: function(what, ports) {
         return new Promise(function(r, rr) {
           if (ports.length) {
             rr();
             return;
           }
 
@@ -458,17 +543,18 @@ function test_messagePort() {
     }
 
     let tmp = resolve;
     resolve = null;
     tmp({ data: e.data, ports: e.ports });
   }
 
   runTests({
-    clonableObjects: true,
+    clonableObjectsEveryWhere: true,
+    clonableObjectsSameProcess: false,
     transferableObjects: true,
     send: function(what, ports) {
       return new Promise(function(r, rr) {
         resolve = r;
         try {
           mc.port1.postMessage(what, ports);
         } catch(e) {
           resolve = null;
@@ -503,17 +589,18 @@ function test_messagePort_inWorkers() {
       }
 
       let tmp = resolve;
       resolve = null;
       tmp({ data: e.data, ports: e.ports });
     }
 
     runTests({
-      clonableObjects: true,
+      clonableObjectsEveryWhere: true,
+      clonableObjectsSameProcess: false,
       transferableObjects: true,
       send: function(what, ports) {
         return new Promise(function(r, rr) {
           resolve = r;
           try {
             mc.port1.postMessage(what, ports);
           } catch(e) {
             resolve = null;
@@ -530,16 +617,17 @@ function test_messagePort_inWorkers() {
   }
 }
 
 var tests = [
   setup_tests,
 
   create_fileList,
   create_directory,
+  create_wasmModule,
 
   test_windowToWindow,
   test_windowToIframe,
   test_windowToCrossOriginIframe,
 
   test_workers,
 
   test_broadcastChannel,
--- a/dom/battery/moz.build
+++ b/dom/battery/moz.build
@@ -12,9 +12,10 @@ EXPORTS.mozilla.dom.battery += [
 SOURCES += [
     'BatteryManager.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
+MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
copy from dom/battery/test/mochitest.ini
copy to dom/battery/test/chrome.ini
--- a/dom/battery/test/mochitest.ini
+++ b/dom/battery/test/chrome.ini
@@ -1,6 +1,3 @@
 [test_battery_basics.html]
-skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
 [test_battery_charging.html]
-skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
 [test_battery_discharging.html]
-skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
deleted file mode 100644
--- a/dom/battery/test/marionette/manifest.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[DEFAULT]
-run-if = buildapp == 'b2g'
-
-[test_battery_level.js]
-[test_battery_status_charging.js]
-[test_battery_status_discharging.js]
-[test_battery_status_full.js]
-[test_battery_status_not_charging.js]
-[test_battery_status_unknown.js]
deleted file mode 100644
--- a/dom/battery/test/marionette/test_battery_level.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-MARIONETTE_TIMEOUT = 10000;
-
-var battery = null;
-
-function verifyInitialState() {
-  window.navigator.getBattery().then(function (b) {
-    battery = b;
-    ok(battery, "battery");
-    is(battery.level, 0.5, "battery.level");
-    runEmulatorCmd("power display", function (result) {
-      is(result.pop(), "OK", "power display successful");
-      ok(result.indexOf("capacity: 50") !== -1, "power capacity");
-      setUp();
-    });
-  });
-}
-
-function unexpectedEvent(event) {
-  ok(false, "Unexpected " + event.type + " event");
-}
-
-function setUp() {
-  battery.onchargingchange = unexpectedEvent;
-  battery.onlevelchange = unexpectedEvent;
-  levelUp();
-}
-
-function changeCapacity(capacity, changeExpected, nextFunction) {
-  log("Changing power capacity to " + capacity);
-  if (changeExpected) {
-    battery.onlevelchange = function (event) {
-      battery.onlevelchange = unexpectedEvent;
-      is(event.type, "levelchange", "event.type");
-      is(battery.level, capacity / 100, "battery.level");
-      nextFunction();
-    };
-    runEmulatorCmd("power capacity " + capacity);
-  }
-  else {
-    runEmulatorCmd("power capacity " + capacity, function () {
-      is(battery.level, capacity / 100, "battery.level");
-      nextFunction();
-    });
-  }
-}
-
-function levelUp() {
-  changeCapacity("90", true, levelDown);
-}
-
-function levelDown() {
-  changeCapacity("10", true, levelSame);
-}
-
-function levelSame() {
-  changeCapacity("10", false, cleanUp);
-}
-
-function cleanUp() {
-  battery.onchargingchange = null;
-  battery.onlevelchange = function () {
-    battery.onlevelchange = null;
-    finish();
-  };
-  runEmulatorCmd("power capacity 50");
-}
-
-verifyInitialState();
deleted file mode 100644
--- a/dom/battery/test/marionette/test_battery_status_charging.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-MARIONETTE_TIMEOUT = 10000;
-
-var battery = null;
-var fromStatus = "charging";
-var fromCharging = true;
-
-function verifyInitialState() {
-  window.navigator.getBattery().then(function (b) {
-    battery = b;
-    ok(battery, "battery");
-    ok(battery.charging, "battery.charging");
-    runEmulatorCmd("power display", function (result) {
-      is(result.pop(), "OK", "power display successful");
-      ok(result.indexOf("status: Charging") !== -1, "power status charging");
-      setUp();
-    });
-  });
-}
-
-function unexpectedEvent(event) {
-  ok(false, "Unexpected " + event.type + " event");
-}
-
-function setUp() {
-  battery.onchargingchange = unexpectedEvent;
-  battery.onlevelchange = unexpectedEvent;
-  toDischarging();
-}
-
-function resetStatus(charging, nextFunction) {
-  log("Resetting power status to " + fromStatus);
-  if (charging !== fromCharging) {
-    battery.onchargingchange = function () {
-      battery.onchargingchange = unexpectedEvent;
-      nextFunction();
-    };
-    runEmulatorCmd("power status " + fromStatus);
-  }
-  else {
-    runEmulatorCmd("power status " + fromStatus, nextFunction);
-  }
-}
-
-function changeStatus(toStatus, toCharging, nextFunction) {
-  log("Changing power status to " + toStatus);
-  if (fromCharging !== toCharging) {
-    battery.onchargingchange = function (event) {
-      battery.onchargingchange = unexpectedEvent;
-      is(event.type, "chargingchange", "event type");
-      is(battery.charging, toCharging, "battery.charging");
-      resetStatus(toCharging, nextFunction);
-    };
-    runEmulatorCmd("power status " + toStatus);
-  }
-  else {
-    runEmulatorCmd("power status " + toStatus, function () {
-      is(battery.charging, toCharging, "battery.charging");
-      resetStatus(toCharging, nextFunction);
-    });
-  }
-}
-
-function toDischarging() {
-  changeStatus("discharging", false, toFull);
-}
-
-function toFull() {
-  changeStatus("full", true, toNotCharging);
-}
-
-function toNotCharging() {
-  changeStatus("not-charging", false, toUnknown);
-}
-
-function toUnknown() {
-  changeStatus("unknown", false, cleanUp);
-}
-
-function cleanUp() {
-  battery.onchargingchange = null;
-  battery.onlevelchange = null;
-  finish();
-}
-
-verifyInitialState();
deleted file mode 100644
--- a/dom/battery/test/marionette/test_battery_status_discharging.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-MARIONETTE_TIMEOUT = 10000;
-
-var battery = null;
-var fromStatus = "discharging";
-var fromCharging = false;
-
-function verifyInitialState() {
-  window.navigator.getBattery().then(function (b) {
-    battery = b;
-    ok(battery, "battery");
-    ok(battery.charging, "battery.charging");
-    runEmulatorCmd("power display", function (result) {
-      is(result.pop(), "OK", "power display successful");
-      ok(result.indexOf("status: Charging") !== -1, "power status charging");
-      setUp();
-    });
-  });
-}
-
-function unexpectedEvent(event) {
-  ok(false, "Unexpected " + event.type + " event");
-}
-
-function setUp() {
-  battery.onchargingchange = function () {
-    battery.onchargingchange = unexpectedEvent;
-    toCharging();
-  };
-  battery.onlevelchange = unexpectedEvent;
-  log("Changing power status to " + fromStatus);
-  runEmulatorCmd("power status " + fromStatus);
-}
-
-function resetStatus(charging, nextFunction) {
-  log("Resetting power status to " + fromStatus);
-  if (charging !== fromCharging) {
-    battery.onchargingchange = function () {
-      battery.onchargingchange = unexpectedEvent;
-      nextFunction();
-    };
-    runEmulatorCmd("power status " + fromStatus);
-  }
-  else {
-    runEmulatorCmd("power status " + fromStatus, nextFunction);
-  }
-}
-
-function changeStatus(toStatus, toCharging, nextFunction) {
-  log("Changing power status to " + toStatus);
-  if (fromCharging !== toCharging) {
-    battery.onchargingchange = function (event) {
-      battery.onchargingchange = unexpectedEvent;
-      is(event.type, "chargingchange", "event type");
-      is(battery.charging, toCharging, "battery.charging");
-      resetStatus(toCharging, nextFunction);
-    };
-    runEmulatorCmd("power status " + toStatus);
-  }
-  else {
-    runEmulatorCmd("power status " + toStatus, function () {
-      is(battery.charging, toCharging, "battery.charging");
-      resetStatus(toCharging, nextFunction);
-    });
-  }
-}
-
-function toCharging() {
-  changeStatus("charging", true, toFull);
-}
-
-function toFull() {
-  changeStatus("full", true, toNotCharging);
-}
-
-function toNotCharging() {
-  changeStatus("not-charging", false, toUnknown);
-}
-
-function toUnknown() {
-  changeStatus("unknown", false, cleanUp);
-}
-
-function cleanUp() {
-  battery.onchargingchange = function () {
-    battery.onchargingchange = null;
-    finish();
-  };
-  battery.onlevelchange = null;
-  log("Resetting power status to charging");
-  runEmulatorCmd("power status charging");
-}
-
-verifyInitialState();
deleted file mode 100644
--- a/dom/battery/test/marionette/test_battery_status_full.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-MARIONETTE_TIMEOUT = 10000;
-
-var battery;
-var fromStatus = "full";
-var fromCharging = true;
-
-function verifyInitialState() {
-  window.navigator.getBattery().then(function (b) {
-    battery = b;
-    ok(battery, "battery");
-    ok(battery.charging, "battery.charging");
-    runEmulatorCmd("power display", function (result) {
-      is(result.pop(), "OK", "power display successful");
-      ok(result.indexOf("status: Charging") !== -1, "power status charging");
-      setUp();
-    });
-  });
-}
-
-function unexpectedEvent(event) {
-  ok(false, "Unexpected " + event.type + " event");
-}
-
-function setUp() {
-  battery.onchargingchange = unexpectedEvent;
-  battery.onlevelchange = unexpectedEvent;
-  log("Changing power status to " + fromStatus);
-  runEmulatorCmd("power status " + fromStatus, toCharging);
-}
-
-function resetStatus(charging, nextFunction) {
-  log("Resetting power status to " + fromStatus);
-  if (charging !== fromCharging) {
-    battery.onchargingchange = function () {
-      battery.onchargingchange = unexpectedEvent;
-      nextFunction();
-    };
-    runEmulatorCmd("power status " + fromStatus);
-  }
-  else {
-    runEmulatorCmd("power status " + fromStatus, nextFunction);
-  }
-}
-
-function changeStatus(toStatus, toCharging, nextFunction) {
-  log("Changing power status to " + toStatus);
-  if (fromCharging !== toCharging) {
-    battery.onchargingchange = function (event) {
-      battery.onchargingchange = unexpectedEvent;
-      is(event.type, "chargingchange", "event type");
-      is(battery.charging, toCharging, "battery.charging");
-      resetStatus(toCharging, nextFunction);
-    };
-    runEmulatorCmd("power status " + toStatus);
-  }
-  else {
-    runEmulatorCmd("power status " + toStatus, function () {
-      is(battery.charging, toCharging, "battery.charging");
-      resetStatus(toCharging, nextFunction);
-    });
-  }
-}
-
-function toCharging() {
-  changeStatus("charging", true, toDischarging);
-}
-
-function toDischarging() {
-  changeStatus("discharging", false, toNotCharging);
-}
-
-function toNotCharging() {
-  changeStatus("not-charging", false, toUnknown);
-}
-
-function toUnknown() {
-  changeStatus("unknown", false, cleanUp);
-}
-
-function cleanUp() {
-  battery.onchargingchange = null;
-  battery.onlevelchange = null;
-  log("Resetting power status to charging");
-  runEmulatorCmd("power status charging", finish);
-}
-
-verifyInitialState();
deleted file mode 100644
--- a/dom/battery/test/marionette/test_battery_status_not_charging.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-MARIONETTE_TIMEOUT = 10000;
-
-var battery;
-var fromStatus = "not-charging";
-var fromCharging = false;
-
-function verifyInitialState() {
-  window.navigator.getBattery().then(function (b) {
-    battery = b;
-    ok(battery, "battery");
-    ok(battery.charging, "battery.charging");
-    runEmulatorCmd("power display", function (result) {
-      is(result.pop(), "OK", "power display successful");
-      ok(result.indexOf("status: Charging") !== -1, "power status charging");
-      setUp();
-    });
-  });
-}
-
-function unexpectedEvent(event) {
-  ok(false, "Unexpected " + event.type + " event");
-}
-
-function setUp() {
-  battery.onchargingchange = function () {
-    battery.onchargingchange = unexpectedEvent;
-    toCharging();
-  };
-  battery.onlevelchange = unexpectedEvent;
-  log("Changing power status to " + fromStatus);
-  runEmulatorCmd("power status " + fromStatus);
-}
-
-function resetStatus(charging, nextFunction) {
-  log("Resetting power status to " + fromStatus);
-  if (charging !== fromCharging) {
-    battery.onchargingchange = function () {
-      battery.onchargingchange = unexpectedEvent;
-      nextFunction();
-    };
-    runEmulatorCmd("power status " + fromStatus);
-  }
-  else {
-    runEmulatorCmd("power status " + fromStatus, nextFunction);
-  }
-}
-
-function changeStatus(toStatus, toCharging, nextFunction) {
-  log("Changing power status to " + toStatus);
-  if (fromCharging !== toCharging) {
-    battery.onchargingchange = function (event) {
-      battery.onchargingchange = unexpectedEvent;
-      is(event.type, "chargingchange", "event type");
-      is(battery.charging, toCharging, "battery.charging");
-      resetStatus(toCharging, nextFunction);
-    };
-    runEmulatorCmd("power status " + toStatus);
-  }
-  else {
-    runEmulatorCmd("power status " + toStatus, function () {
-      is(battery.charging, toCharging, "battery.charging");
-      resetStatus(toCharging, nextFunction);
-    });
-  }
-}
-
-function toCharging() {
-  changeStatus("charging", true, toDischarging);
-}
-
-function toDischarging() {
-  changeStatus("discharging", false, toFull);
-}
-
-function toFull() {
-  changeStatus("full", true, toUnknown);
-}
-
-function toUnknown() {
-  changeStatus("unknown", false, cleanUp);
-}
-
-function cleanUp() {
-  battery.onchargingchange = function () {
-    battery.onchargingchange = null;
-    finish();
-  };
-  battery.onlevelchange = null;
-  log("Resetting power status to charging");
-  runEmulatorCmd("power status charging");
-}
-
-verifyInitialState();
deleted file mode 100644
--- a/dom/battery/test/marionette/test_battery_status_unknown.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-MARIONETTE_TIMEOUT = 10000;
-
-var battery;
-var fromStatus = "unknown";
-var fromCharging = false;
-
-function verifyInitialState() {
-  window.navigator.getBattery().then(function (b) {
-    battery = b;
-    ok(battery, "battery");
-    ok(battery.charging, "battery.charging");
-    runEmulatorCmd("power display", function (result) {
-      is(result.pop(), "OK", "power display successful");
-      ok(result.indexOf("status: Charging") !== -1, "power status charging");
-      setUp();
-    });
-  });
-}
-
-function unexpectedEvent(event) {
-  ok(false, "Unexpected " + event.type + " event");
-}
-
-function setUp() {
-  battery.onchargingchange = function () {
-    battery.onchargingchange = unexpectedEvent;
-    toCharging();
-  };
-  battery.onlevelchange = unexpectedEvent;
-  log("Changing power status to " + fromStatus);
-  runEmulatorCmd("power status " + fromStatus);
-}
-
-function resetStatus(charging, nextFunction) {
-  log("Resetting power status to " + fromStatus);
-  if (charging !== fromCharging) {
-    battery.onchargingchange = function () {
-      battery.onchargingchange = unexpectedEvent;
-      nextFunction();
-    };
-    runEmulatorCmd("power status " + fromStatus);
-  }
-  else {
-    runEmulatorCmd("power status " + fromStatus, nextFunction);
-  }
-}
-
-function changeStatus(toStatus, toCharging, nextFunction) {
-  log("Changing power status to " + toStatus);
-  if (fromCharging !== toCharging) {
-    battery.onchargingchange = function (event) {
-      battery.onchargingchange = unexpectedEvent;
-      is(event.type, "chargingchange", "event type");
-      is(battery.charging, toCharging, "battery.charging");
-      resetStatus(toCharging, nextFunction);
-    };
-    runEmulatorCmd("power status " + toStatus);
-  }
-  else {
-    runEmulatorCmd("power status " + toStatus, function () {
-      is(battery.charging, toCharging, "battery.charging");
-      resetStatus(toCharging, nextFunction);
-    });
-  }
-}
-
-function toCharging() {
-  changeStatus("charging", true, toDischarging);
-}
-
-function toDischarging() {
-  changeStatus("discharging", false, toFull);
-}
-
-function toFull() {
-  changeStatus("full", true, toNotCharging);
-}
-
-function toNotCharging() {
-  changeStatus("not-charging", false, cleanUp);
-}
-
-function cleanUp() {
-  battery.onchargingchange = function () {
-    battery.onchargingchange = null;
-    finish();
-  };
-  battery.onlevelchange = null;
-  log("Resetting power status to charging");
-  runEmulatorCmd("power status charging");
-}
-
-verifyInitialState();
--- a/dom/battery/test/mochitest.ini
+++ b/dom/battery/test/mochitest.ini
@@ -1,6 +1,1 @@
-[test_battery_basics.html]
-skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
-[test_battery_charging.html]
-skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
-[test_battery_discharging.html]
-skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
+[test_battery_unprivileged.html]
--- a/dom/battery/test/test_battery_basics.html
+++ b/dom/battery/test/test_battery_basics.html
@@ -1,14 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test for Battery API</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
--- a/dom/battery/test/test_battery_charging.html
+++ b/dom/battery/test/test_battery_charging.html
@@ -1,22 +1,24 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test for Battery API</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+"use strict";
+
 SimpleTest.waitForExplicitFinish();
 
 /** Test for Battery API **/
 navigator.getBattery().then(function (battery) {
   ok(battery.level >= 0.0 && battery.level <= 1.0, "Battery level " + battery.level + " should be in the range [0.0, 1.0]");
 
   SpecialPowers.pushPrefEnv({"set": [["dom.battery.test.charging", true]]}, function () {
     is(battery.charging, true, "Battery should be charging");
--- a/dom/battery/test/test_battery_discharging.html
+++ b/dom/battery/test/test_battery_discharging.html
@@ -1,22 +1,24 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test for Battery API</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+"use strict";
+
 SimpleTest.waitForExplicitFinish();
 
 /** Test for Battery API **/
 navigator.getBattery().then(function (battery) {
   ok(battery.level >= 0.0 && battery.level <= 1.0, "Battery level " + battery.level + " should be in the range [0.0, 1.0]");
 
   SpecialPowers.pushPrefEnv({"set": [["dom.battery.test.discharging", true]]}, function () {
     is(battery.charging, false, "Battery should be discharging");
new file mode 100644
--- /dev/null
+++ b/dom/battery/test/test_battery_unprivileged.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Battery API</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+"use strict";
+
+/** Test for Battery API **/
+ok(!("getBattery" in navigator), "navigator.getBattery should not exist for unprivileged web content");
+ok(!("battery" in navigator), "navigator.battery should not exist");
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/browser-element/BrowserElementParent.cpp
+++ b/dom/browser-element/BrowserElementParent.cpp
@@ -251,16 +251,17 @@ BrowserElementParent::OpenWindowOOP(TabP
 }
 
 /* static */
 BrowserElementParent::OpenWindowResult
 BrowserElementParent::OpenWindowInProcess(nsPIDOMWindowOuter* aOpenerWindow,
                                           nsIURI* aURI,
                                           const nsAString& aName,
                                           const nsACString& aFeatures,
+                                          bool aForceNoOpener,
                                           mozIDOMWindowProxy** aReturnWindow)
 {
   *aReturnWindow = nullptr;
 
   // If we call window.open from an <iframe> inside an <iframe mozbrowser>,
   // it's as though the top-level document inside the <iframe mozbrowser>
   // called window.open.  (Indeed, in the OOP case, the inner <iframe> lives
   // out-of-process, so we couldn't touch it if we tried.)
@@ -278,16 +279,22 @@ BrowserElementParent::OpenWindowInProces
     CreateIframe(openerFrameElement, aName, /* aRemote = */ false);
   NS_ENSURE_TRUE(popupFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
 
   nsAutoCString spec;
   if (aURI) {
     aURI->GetSpec(spec);
   }
 
+  if (!aForceNoOpener) {
+    ErrorResult res;
+    popupFrameElement->PresetOpenerWindow(aOpenerWindow, res);
+    MOZ_ASSERT(!res.Failed());
+  }
+
   OpenWindowResult opened =
     DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
                             NS_ConvertUTF8toUTF16(spec),
                             aName,
                             NS_ConvertUTF8toUTF16(aFeatures));
 
   if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
     return opened;
--- a/dom/browser-element/BrowserElementParent.h
+++ b/dom/browser-element/BrowserElementParent.h
@@ -113,16 +113,17 @@ public:
    *         frame to a document or whether they called preventDefault to prevent
    *         the platform from handling the open request
    */
   static OpenWindowResult
   OpenWindowInProcess(nsPIDOMWindowOuter* aOpenerWindow,
                       nsIURI* aURI,
                       const nsAString& aName,
                       const nsACString& aFeatures,
+                      bool aForceNoOpener,
                       mozIDOMWindowProxy** aReturnWindow);
 
 private:
   static OpenWindowResult
   DispatchOpenWindowEvent(dom::Element* aOpenerFrameElement,
                           dom::Element* aPopupFrameElement,
                           const nsAString& aURL,
                           const nsAString& aName,
--- a/dom/browser-element/mochitest/browserElement_BrowserWindowNamespace.js
+++ b/dom/browser-element/mochitest/browserElement_BrowserWindowNamespace.js
@@ -30,16 +30,20 @@ function runTest() {
       }
       else {
         ok(true, "Got locationchange to " + e.detail.url);
       }
     });
 
     SimpleTest.executeSoon(function() {
       var iframe2 = document.createElement('iframe');
+      // Make sure that iframe1 and iframe2 are in the same TabGroup by linking
+      // them through opener. Right now this API requires chrome privileges, as
+      // it is on MozFrameLoaderOwner.
+      SpecialPowers.wrap(iframe2).presetOpenerWindow(iframe1.contentWindow);
       iframe2.setAttribute('mozbrowser', 'true');
 
       iframe2.addEventListener('mozbrowseropenwindow', function(e) {
         ok(false, "Got second mozbrowseropenwindow event.");
       });
 
       document.body.appendChild(iframe2);
       iframe2.src = 'file_browserElement_BrowserWindowNamespace.html#2';
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -26,18 +26,16 @@ disabled = disabled for bug 1266035
 tags = audiochannel
 disabled = Disabling some OOP tests for WebIDL scope changes
 [test_browserElement_oop_Auth.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_BackForward.html]
 disabled = Disabling some OOP tests for WebIDL scope changes
 [test_browserElement_oop_BadScreenshot.html]
 disabled = Disabling some OOP tests for WebIDL scope changes
-[test_browserElement_oop_BrowserWindowNamespace.html]
-skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_BrowserWindowResize.html]
 [test_browserElement_oop_Close.html]
 [test_browserElement_oop_CookiesNotThirdParty.html]
 [test_browserElement_oop_CopyPaste.html]
 subsuite = clipboard
 [test_browserElement_oop_DOMRequestError.html]
 disabled = Disabling some OOP tests for WebIDL scope changes
 [test_browserElement_oop_DataURI.html]
deleted file mode 100644
--- a/dom/browser-element/mochitest/test_browserElement_oop_BrowserWindowNamespace.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for Bug 780351</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<script type="application/javascript;version=1.7" src="browserElement_BrowserWindowNamespace.js">
-</script>
-</body>
-</html>
\ No newline at end of file
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -29,17 +29,16 @@ IdentifierHashFunc(const char* name, siz
 }
 
 static int
 ChooseValidatorCompileOptions(const ShBuiltInResources& resources,
                               const mozilla::gl::GLContext* gl)
 {
     int options = SH_VARIABLES |
                   SH_ENFORCE_PACKING_RESTRICTIONS |
-                  SH_INIT_VARYINGS_WITHOUT_STATIC_USE |
                   SH_OBJECT_CODE |
                   SH_LIMIT_CALL_STACK_DEPTH |
                   SH_INIT_GL_POSITION;
 
     if (resources.MaxExpressionComplexity > 0) {
         options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
     }
 
@@ -49,48 +48,39 @@ ChooseValidatorCompileOptions(const ShBu
     // Just do it universally.
     options |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX;
 
     if (gfxPrefs::WebGLAllANGLEOptions()) {
         return options |
                SH_VALIDATE_LOOP_INDEXING |
                SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX |
                SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX |
-               SH_EMULATE_BUILT_IN_FUNCTIONS |
                SH_CLAMP_INDIRECT_ARRAY_BOUNDS |
                SH_UNFOLD_SHORT_CIRCUIT |
                SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS |
+               SH_INIT_OUTPUT_VARIABLES |
                SH_REGENERATE_STRUCT_NAMES;
     }
 
 #ifndef XP_MACOSX
     // We want to do this everywhere, but to do this on Mac, we need
     // to do it only on Mac OSX > 10.6 as this causes the shader
     // compiler in 10.6 to crash
     options |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
 #endif
 
 #ifdef XP_MACOSX
     if (gl->WorkAroundDriverBugs()) {
         // Work around https://bugs.webkit.org/show_bug.cgi?id=124684,
         // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb
         options |= SH_UNFOLD_SHORT_CIRCUIT;
 
-        // Work around bug 665578 and bug 769810
-        if (gl->Vendor() == gl::GLVendor::ATI) {
-            options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
-        }
-
-        // Work around bug 735560
-        if (gl->Vendor() == gl::GLVendor::Intel) {
-            options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
-        }
-
         // Work around that Mac drivers handle struct scopes incorrectly.
         options |= SH_REGENERATE_STRUCT_NAMES;
+        options |= SH_INIT_OUTPUT_VARIABLES;
     }
 #endif
 
     return options;
 }
 
 } // namespace webgl
 
--- a/dom/encoding/FallbackEncoding.cpp
+++ b/dom/encoding/FallbackEncoding.cpp
@@ -64,17 +64,17 @@ FallbackEncoding::Get(nsACString& aFallb
     aFallback = mFallback;
     return;
   }
 
   nsAutoCString locale;
   nsCOMPtr<nsIXULChromeRegistry> registry =
     mozilla::services::GetXULChromeRegistryService();
   if (registry) {
-    registry->GetSelectedLocale(NS_LITERAL_CSTRING("global"), locale);
+    registry->GetSelectedLocale(NS_LITERAL_CSTRING("global"), false, locale);
   }
 
   // Let's lower case the string just in case unofficial language packs
   // don't stick to conventions.
   ToLowerCase(locale); // ASCII lowercasing with CString input!
 
   // Special case Traditional Chinese before throwing away stuff after the
   // language itself. Today we only ship zh-TW, but be defensive about
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -6200,16 +6200,20 @@ HTMLMediaElement::SetOnwaitingforkey(Eve
 {
   EventTarget::SetEventHandler(nsGkAtoms::onwaitingforkey, EmptyString(), aCallback);
 }
 
 void
 HTMLMediaElement::DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
                                     const nsAString& aInitDataType)
 {
+  LOG(LogLevel::Debug,
+      ("%p DispatchEncrypted initDataType='%s'",
+      this, NS_ConvertUTF16toUTF8(aInitDataType).get()));
+
   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
     // Ready state not HAVE_METADATA (yet), don't dispatch encrypted now.
     // Queueing for later dispatch in MetadataLoaded.
     mPendingEncryptedInitData.AddInitData(aInitDataType, aInitData);
     return;
   }
 
   RefPtr<MediaEncryptedEvent> event;
--- a/dom/html/PluginDocument.cpp
+++ b/dom/html/PluginDocument.cpp
@@ -163,17 +163,17 @@ PluginDocument::StartDocumentLoad(const 
                                   bool                aReset,
                                   nsIContentSink*     aSink)
 {
   // do not allow message panes to host full-page plugins
   // returning an error causes helper apps to take over
   nsCOMPtr<nsIDocShellTreeItem> dsti (do_QueryInterface(aContainer));
   if (dsti) {
     bool isMsgPane = false;
-    dsti->NameEquals(u"messagepane", &isMsgPane);
+    dsti->NameEquals(NS_LITERAL_STRING("messagepane"), &isMsgPane);
     if (isMsgPane) {
       return NS_ERROR_FAILURE;
     }
   }
 
   nsresult rv =
     MediaDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer,
                                      aDocListener, aReset, aSink);
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -31,27 +31,29 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericHTMLFrameElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGenericHTMLFrameElement,
                                                   nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOpenerWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserElementAPI)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserElementAudioChannels)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGenericHTMLFrameElement,
                                                 nsGenericHTMLElement)
   if (tmp->mFrameLoader) {
     tmp->mFrameLoader->Destroy();
   }
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOpenerWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserElementAPI)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserElementAudioChannels)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ADDREF_INHERITED(nsGenericHTMLFrameElement, nsGenericHTMLElement)
 NS_IMPL_RELEASE_INHERITED(nsGenericHTMLFrameElement, nsGenericHTMLElement)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsGenericHTMLFrameElement)
@@ -144,17 +146,19 @@ nsGenericHTMLFrameElement::EnsureFrameLo
 {
   if (!IsInComposedDoc() || mFrameLoader || mFrameLoaderCreationDisallowed) {
     // If frame loader is there, we just keep it around, cached
     return;
   }
 
   // Strangely enough, this method doesn't actually ensure that the
   // frameloader exists.  It's more of a best-effort kind of thing.
-  mFrameLoader = nsFrameLoader::Create(this, mNetworkCreated);
+  mFrameLoader = nsFrameLoader::Create(this,
+                                       nsPIDOMWindowOuter::From(mOpenerWindow),
+                                       mNetworkCreated);
   if (mIsPrerendered) {
     mFrameLoader->SetIsPrerendered();
   }
 }
 
 nsresult
 nsGenericHTMLFrameElement::CreateRemoteFrameLoader(nsITabParent* aTabParent)
 {
@@ -208,46 +212,59 @@ nsGenericHTMLFrameElement::GetParentAppl
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 void
+nsGenericHTMLFrameElement::PresetOpenerWindow(mozIDOMWindowProxy* aWindow, ErrorResult& aRv)
+{
+  MOZ_ASSERT(!mFrameLoader);
+  mOpenerWindow = nsPIDOMWindowOuter::From(aWindow);
+}
+
+void
+nsGenericHTMLFrameElement::InternalSetFrameLoader(nsIFrameLoader* aNewFrameLoader)
+{
+  mFrameLoader = static_cast<nsFrameLoader*>(aNewFrameLoader);
+}
+
+void
 nsGenericHTMLFrameElement::SwapFrameLoaders(HTMLIFrameElement& aOtherLoaderOwner,
                                             ErrorResult& rv)
 {
   if (&aOtherLoaderOwner == this) {
     // nothing to do
     return;
   }
 
-  SwapFrameLoaders(aOtherLoaderOwner.mFrameLoader, rv);
+  aOtherLoaderOwner.SwapFrameLoaders(this, rv);
 }
 
 void
 nsGenericHTMLFrameElement::SwapFrameLoaders(nsXULElement& aOtherLoaderOwner,
                                             ErrorResult& rv)
 {
-  aOtherLoaderOwner.SwapFrameLoaders(mFrameLoader, rv);
+  aOtherLoaderOwner.SwapFrameLoaders(this, rv);
 }
 
 void
-nsGenericHTMLFrameElement::SwapFrameLoaders(RefPtr<nsFrameLoader>& aOtherLoader,
+nsGenericHTMLFrameElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoaderOwner,
                                             mozilla::ErrorResult& rv)
 {
-  if (!mFrameLoader || !aOtherLoader) {
+  RefPtr<nsFrameLoader> loader = GetFrameLoader();
+  RefPtr<nsFrameLoader> otherLoader = aOtherLoaderOwner->GetFrameLoader();
+  if (!loader || !otherLoader) {
     rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
     return;
   }
 
-  rv = mFrameLoader->SwapWithOtherLoader(aOtherLoader,
-                                         mFrameLoader,
-                                         aOtherLoader);
+  rv = loader->SwapWithOtherLoader(otherLoader, this, aOtherLoaderOwner);
 }
 
 NS_IMETHODIMP
 nsGenericHTMLFrameElement::SetIsPrerendered()
 {
   MOZ_ASSERT(!mFrameLoader, "Please call SetIsPrerendered before frameLoader is created");
   mIsPrerendered = true;
   return NS_OK;
@@ -431,17 +448,17 @@ nsGenericHTMLFrameElement::CopyInnerTo(E
 {
   nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsIDocument* doc = aDest->OwnerDoc();
   if (doc->IsStaticDocument() && mFrameLoader) {
     nsGenericHTMLFrameElement* dest =
       static_cast<nsGenericHTMLFrameElement*>(aDest);
-    nsFrameLoader* fl = nsFrameLoader::Create(dest, false);
+    nsFrameLoader* fl = nsFrameLoader::Create(dest, nullptr, false);
     NS_ENSURE_STATE(fl);
     dest->mFrameLoader = fl;
     static_cast<nsFrameLoader*>(mFrameLoader.get())->CreateStaticClone(fl);
   }
 
   return rv;
 }
 
--- a/dom/html/nsGenericHTMLFrameElement.h
+++ b/dom/html/nsGenericHTMLFrameElement.h
@@ -75,19 +75,22 @@ public:
                                            nsGenericHTMLElement)
 
   void SwapFrameLoaders(mozilla::dom::HTMLIFrameElement& aOtherLoaderOwner,
                         mozilla::ErrorResult& aError);
 
   void SwapFrameLoaders(nsXULElement& aOtherLoaderOwner,
                         mozilla::ErrorResult& aError);
 
-  void SwapFrameLoaders(RefPtr<nsFrameLoader>& aOtherLoader,
+  void SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoaderOwner,
                         mozilla::ErrorResult& rv);
 
+  void PresetOpenerWindow(mozIDOMWindowProxy* aOpenerWindow,
+                          mozilla::ErrorResult& aRv);
+
   static bool BrowserFramesEnabled();
 
   /**
    * Helper method to map a HTML 'scrolling' attribute value to a nsIScrollable
    * enum value.  scrolling="no" (and its synonyms) maps to
    * nsIScrollable::Scrollbar_Never, and anything else (including nullptr) maps
    * to nsIScrollable::Scrollbar_Auto.
    * @param aValue the attribute value to map or nullptr
@@ -102,16 +105,17 @@ protected:
   // it makes sense.
   void EnsureFrameLoader();
   nsresult LoadSrc();
   nsIDocument* GetContentDocument(nsIPrincipal& aSubjectPrincipal);
   nsresult GetContentDocument(nsIDOMDocument** aContentDocument);
   already_AddRefed<nsPIDOMWindowOuter> GetContentWindow();
 
   RefPtr<nsFrameLoader> mFrameLoader;
+  nsCOMPtr<nsPIDOMWindowOuter> mOpenerWindow;
 
   /**
    * True when the element is created by the parser using the
    * NS_FROM_PARSER_NETWORK flag.
    * If the element is modified, it may lose the flag.
    */
   bool mNetworkCreated;
 
--- a/dom/interfaces/base/nsIBrowserDOMWindow.idl
+++ b/dom/interfaces/base/nsIBrowserDOMWindow.idl
@@ -57,51 +57,64 @@ interface nsIBrowserDOMWindow : nsISuppo
   const short OPEN_NEWTAB        = 3;
   /**
    * Open in an existing content tab based on the URI. If a match can't be
    * found, revert to OPEN_NEWTAB behavior.
    */
   const short OPEN_SWITCHTAB     = 4;
 
   /**
-   * Values for openURI's aContext parameter.  These affect the behavior of
-   * OPEN_DEFAULTWINDOW.
+   * Values for openURI's aFlags parameter. This is a bitflags field.
+   *
+   * The 0x1 bit decides the behavior of OPEN_DEFAULTWINDOW, and the 0x4 bit
+   * controls whether or not to set the window.opener property on the newly
+   * opened window.
+   *
+   * NOTE: The 0x2 bit is ignored for backwards compatibility with addons, as
+   * OPEN_NEW used to have the value 2. The values 0 and 2 are treated
+   * the same way internally.
    */
   /**
+   * internal open new window
+   */
+  const long OPEN_NEW           = 0x0;
+  /**
    * external link (load request from another application, xremote, etc).
    */
-  const short OPEN_EXTERNAL      = 1;
+  const long OPEN_EXTERNAL      = 0x1;
+
   /**
-   * internal open new window
+   * Don't set the window.opener property on the window which is being opened
    */
-  const short OPEN_NEW           = 2; 
+  const long OPEN_NO_OPENER     = 0x4;
 
   /**
    * Load a URI
 
    * @param aURI the URI to open. null is allowed.  If null is passed in, no
    *             load will be done, though the window the load would have
    *             happened in will be returned.
    * @param aWhere see possible values described above.
    * @param aOpener window requesting the open (can be null).
-   * @param aContext the context in which the URI is being opened. This
-   *                 is used only when aWhere == OPEN_DEFAULTWINDOW.
+   * @param aFlags flags which control the behavior of the load. The
+   *               OPEN_EXTERNAL/OPEN_NEW flag is only used when
+   *               aWhere == OPEN_DEFAULTWINDOW.
    * @return the window into which the URI was opened.
   */
   mozIDOMWindowProxy
   openURI(in nsIURI aURI, in mozIDOMWindowProxy aOpener,
-          in short aWhere, in short aContext);
+          in short aWhere, in long aFlags);
 
   /**
    * As above, but return the nsIFrameLoaderOwner for the new window.
    // XXXbz is this the right API?
    // See bug 537428
    */
   nsIFrameLoaderOwner openURIInFrame(in nsIURI aURI, in nsIOpenURIInFrameParams params,
-                                     in short aWhere, in short aContext);
+                                     in short aWhere, in long aFlags);
 
   /**
    * @param  aWindow the window to test.
    * @return whether the window is the main content window for any
    *         currently open tab in this toplevel browser window.
    */
   boolean isTabContentWindow(in nsIDOMWindow aWindow);
 
--- a/dom/interfaces/base/nsIDOMChromeWindow.idl
+++ b/dom/interfaces/base/nsIDOMChromeWindow.idl
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 
 interface nsIBrowserDOMWindow;
 interface nsIDOMElement;
 interface nsIDOMEvent;
 interface nsIMessageBroadcaster;
+interface mozIDOMWindowProxy;
 
 [scriptable, uuid(78bdcb41-1efa-409f-aaba-70842213f80f)]
 interface nsIDOMChromeWindow : nsISupports
 {
   const unsigned short STATE_MAXIMIZED = 1;
   const unsigned short STATE_MINIMIZED = 2;
   const unsigned short STATE_NORMAL = 3;
   const unsigned short STATE_FULLSCREEN = 4;
@@ -58,9 +59,23 @@ interface nsIDOMChromeWindow : nsISuppor
    * while the left mouse button is held down, callers must check this.
    *
    * The optional panel argument should be set when moving a panel.
    *
    * Returns NS_ERROR_NOT_IMPLEMENTED (and thus throws in JS) if the OS
    * doesn't support this.
    */
   void beginWindowMove(in nsIDOMEvent mouseDownEvent, [optional] in nsIDOMElement panel);
+
+  /**
+   * These methods provide a way to specify the opener value for the content in
+   * the window before the content itself is created. This is important in order
+   * to set the DocGroup of a document, as the opener must be set before the
+   * document is created.
+   *
+   * SetOpenerForInitialContentBrowser is used to set which opener will be used,
+   * and TakeOpenerForInitialContentBrowser is used by nsXULElement in order to
+   * take the value set earlier, and null out the value in the
+   * nsIDOMChromeWindow.
+   */
+  void setOpenerForInitialContentBrowser(in mozIDOMWindowProxy aOpener);
+  mozIDOMWindowProxy takeOpenerForInitialContentBrowser();
 };
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -497,16 +497,19 @@ private:
 };
 
 NS_IMPL_ISUPPORTS(BackgroundChildPrimer, nsIIPCBackgroundChildCreateCallback)
 
 ContentChild* ContentChild::sSingleton;
 
 ContentChild::ContentChild()
  : mID(uint64_t(-1))
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ , mMsaaID(0)
+#endif
  , mCanOverrideProcessName(true)
  , mIsAlive(true)
  , mShuttingDown(false)
 {
   // This process is a content process, so it's clearly running in
   // multiprocess mode!
   nsDebugImpl::SetMultiprocessMode("Child");
 }
@@ -654,36 +657,38 @@ NS_IMETHODIMP
 ContentChild::ProvideWindow(mozIDOMWindowProxy* aParent,
                             uint32_t aChromeFlags,
                             bool aCalledFromJS,
                             bool aPositionSpecified,
                             bool aSizeSpecified,
                             nsIURI* aURI,
                             const nsAString& aName,
                             const nsACString& aFeatures,
+                            bool aForceNoOpener,
                             bool* aWindowIsNew,
                             mozIDOMWindowProxy** aReturn)
 {
   return ProvideWindowCommon(nullptr, aParent, false, aChromeFlags,
                              aCalledFromJS, aPositionSpecified,
                              aSizeSpecified, aURI, aName, aFeatures,
-                             aWindowIsNew, aReturn);
+                             aForceNoOpener, aWindowIsNew, aReturn);
 }
 
 nsresult
 ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
                                   mozIDOMWindowProxy* aParent,
                                   bool aIframeMoz,
                                   uint32_t aChromeFlags,
                                   bool aCalledFromJS,
                                   bool aPositionSpecified,
                                   bool aSizeSpecified,
                                   nsIURI* aURI,
                                   const nsAString& aName,
                                   const nsACString& aFeatures,
+                                  bool aForceNoOpener,
                                   bool* aWindowIsNew,
                                   mozIDOMWindowProxy** aReturn)
 {
   *aReturn = nullptr;
 
   nsAutoPtr<IPCTabContext> ipcContext;
   TabId openerTabId = TabId(0);
 
@@ -818,16 +823,27 @@ ContentChild::ProvideWindowCommon(TabChi
   if (opener && (openerShell = opener->GetDocShell())) {
     nsCOMPtr<nsILoadContext> context = do_QueryInterface(openerShell);
     showInfo = ShowInfo(EmptyString(), false,
                         context->UsePrivateBrowsing(), true, false,
                         aTabOpener->mDPI, aTabOpener->mRounding,
                         aTabOpener->mDefaultScale);
   }
 
+  // Set the opener window for this window before we start loading the document
+  // inside of it. We have to do this before loading the remote scripts, because
+  // they can poke at the document and cause the nsDocument to be created before
+  // the openerwindow
+  nsCOMPtr<mozIDOMWindowProxy> windowProxy = do_GetInterface(newChild->WebNavigation());
+  if (!aForceNoOpener && windowProxy && aParent) {
+    nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(windowProxy);
+    nsPIDOMWindowOuter* parent = nsPIDOMWindowOuter::From(aParent);
+    outer->SetOpenerWindow(parent, *aWindowIsNew);
+  }
+
   // Unfortunately we don't get a window unless we've shown the frame.  That's
   // pretty bogus; see bug 763602.
   newChild->DoFakeShow(textureFactoryIdentifier, layersId, renderFrame,
                        showInfo);
 
   for (size_t i = 0; i < frameScripts.Length(); i++) {
     FrameScriptInfo& info = frameScripts[i];
     if (!newChild->RecvLoadRemoteScript(info.url(), info.runInGlobalScope())) {
@@ -2410,23 +2426,28 @@ ContentChild::RecvFlushMemory(const nsSt
     mozilla::services::GetObserverService();
   if (os) {
     os->NotifyObservers(nullptr, "memory-pressure", reason.get());
   }
   return true;
 }
 
 bool
-ContentChild::RecvActivateA11y()
+ContentChild::RecvActivateA11y(const uint32_t& aMsaaID)
 {
 #ifdef ACCESSIBILITY
+#ifdef XP_WIN
+  MOZ_ASSERT(aMsaaID != 0);
+  mMsaaID = aMsaaID;
+#endif // XP_WIN
+
   // Start accessibility in content process if it's running in chrome
   // process.
   GetOrCreateAccService(nsAccessibilityService::eMainProcess);
-#endif
+#endif // ACCESSIBILITY
   return true;
 }
 
 bool
 ContentChild::RecvShutdownA11y()
 {
 #ifdef ACCESSIBILITY
   // Try to shutdown accessibility in content process if it's shutting down in
@@ -3271,16 +3292,23 @@ ContentChild::RecvBlobURLRegistration(co
 
 bool
 ContentChild::RecvBlobURLUnregistration(const nsCString& aURI)
 {
   nsHostObjectProtocolHandler::RemoveDataEntry(aURI);
   return true;
 }
 
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+bool
+ContentChild::SendGetA11yContentId()
+{
+  return PContentChild::SendGetA11yContentId(&mMsaaID);
+}
+#endif // defined(XP_WIN) && defined(ACCESSIBILITY)
 
 void
 ContentChild::CreateGetFilesRequest(const nsAString& aDirectoryPath,
                                     bool aRecursiveFlag,
                                     nsID& aUUID,
                                     GetFilesHelperChild* aChild)
 {
   MOZ_ASSERT(aChild);
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -83,16 +83,17 @@ public:
                       bool aIframeMoz,
                       uint32_t aChromeFlags,
                       bool aCalledFromJS,
                       bool aPositionSpecified,
                       bool aSizeSpecified,
                       nsIURI* aURI,
                       const nsAString& aName,
                       const nsACString& aFeatures,
+                      bool aForceNoOpener,
                       bool* aWindowIsNew,
                       mozIDOMWindowProxy** aReturn);
 
   bool Init(MessageLoop* aIOLoop,
             base::ProcessId aParentPid,
             IPC::Channel* aChannel);
 
   void InitProcessAttributes();
@@ -429,17 +430,17 @@ public:
   virtual bool RecvGeolocationError(const uint16_t& errorCode) override;
 
   virtual bool RecvUpdateDictionaryList(InfallibleTArray<nsString>&& aDictionaries) override;
 
   virtual bool RecvAddPermission(const IPC::Permission& permission) override;
 
   virtual bool RecvFlushMemory(const nsString& reason) override;
 
-  virtual bool RecvActivateA11y() override;
+  virtual bool RecvActivateA11y(const uint32_t& aMsaaID) override;
   virtual bool RecvShutdownA11y() override;
 
   virtual bool RecvGarbageCollect() override;
   virtual bool RecvCycleCollect() override;
 
   virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID,
                            const nsCString& name, const nsCString& UAName,
                            const nsCString& ID, const nsCString& vendor) override;
@@ -550,16 +551,20 @@ public:
                                               const IPC::Principal& aPrincipal) override;
 
   // Get the directory for IndexedDB files. We query the parent for this and
   // cache the value
   nsString &GetIndexedDBPath();
 
   ContentParentId GetID() const { return mID; }
 
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+  uint32_t GetMsaaID() const { return mMsaaID; }
+#endif
+
   bool IsForApp() const { return mIsForApp; }
   bool IsForBrowser() const { return mIsForBrowser; }
 
   virtual PBlobChild*
   SendPBlobConstructor(PBlobChild* actor,
                        const BlobConstructorParams& params) override;
 
   virtual PFileDescriptorSetChild*
@@ -636,16 +641,21 @@ public:
 
   virtual bool
   RecvBlobURLRegistration(const nsCString& aURI, PBlobChild* aBlobChild,
                           const IPC::Principal& aPrincipal) override;
 
   virtual bool
   RecvBlobURLUnregistration(const nsCString& aURI) override;
 
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+  bool
+  SendGetA11yContentId();
+#endif // defined(XP_WIN) && defined(ACCESSIBILITY)
+
   /**
    * Helper function for protocols that use the GPU process when available.
    * Overrides FatalError to just be a warning when communicating with the
    * GPU process since we don't want to crash the content process when the
    * GPU process crashes.
    */
   static void FatalErrorIfNotUsingGPUProcess(const char* const aProtocolName,
                                              const char* const aErrorMsg,
@@ -670,16 +680,24 @@ private:
    * An ID unique to the process containing our corresponding
    * content parent.
    *
    * We expect our content parent to set this ID immediately after opening a
    * channel to us.
    */
   ContentParentId mID;
 
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+  /**
+   * This is an a11y-specific unique id for the content process that is
+   * generated by the chrome process.
+   */
+  uint32_t mMsaaID;
+#endif
+
   AppInfo mAppInfo;
 
   bool mIsForApp;
   bool mIsForBrowser;
   bool mCanOverrideProcessName;
   bool mIsAlive;
   nsString mProcessName;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1351,20 +1351,21 @@ ContentParent::Init()
   }
 
 #ifdef ACCESSIBILITY
   // If accessibility is running in chrome process then start it in content
   // process.
   if (nsIPresShell::IsAccessibilityActive()) {
 #if defined(XP_WIN)
     if (IsVistaOrLater()) {
-      Unused << SendActivateA11y();
+      Unused <<
+        SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
     }
 #else
-    Unused << SendActivateA11y();
+    Unused << SendActivateA11y(0);
 #endif
   }
 #endif
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
   nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
   bool profilerActive = false;
   DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
@@ -2776,20 +2777,21 @@ ContentParent::Observe(nsISupports* aSub
 #endif
 #ifdef ACCESSIBILITY
   else if (aData && !strcmp(aTopic, "a11y-init-or-shutdown")) {
     if (*aData == '1') {
       // Make sure accessibility is running in content process when
       // accessibility gets initiated in chrome process.
 #if defined(XP_WIN)
       if (IsVistaOrLater()) {
-        Unused << SendActivateA11y();
+        Unused <<
+          SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
       }
 #else
-      Unused << SendActivateA11y();
+      Unused << SendActivateA11y(0);
 #endif
     } else {
       // If possible, shut down accessibility in content process when
       // accessibility gets shutdown in chrome process.
       Unused << SendShutdownA11y();
     }
   }
 #endif
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -134,19 +134,21 @@ both:
      */
     async PRenderFrame();
 
 parent:
     /**
      * Tell the parent process a new accessible document has been created.
      * aParentDoc is the accessible document it was created in if any, and
      * aParentAcc is the id of the accessible in that document the new document
-     * is a child of.
+     * is a child of. aMsaaID is the MSAA id for this content process, and
+     * is only valid on Windows. Set to 0 on other platforms.
      */
-    async PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc);
+    async PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc,
+                         uint32_t aMsaaID);
 
     /*
      * Creates a new remoted nsIWidget connection for windowed plugins
      * in e10s mode. This is always initiated from the child in response
      * to windowed plugin creation.
      */
     sync PPluginWidget();
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -500,18 +500,21 @@ child:
 
     async FlushMemory(nsString reason);
 
     async GarbageCollect();
     async CycleCollect();
 
     /**
      * Start accessibility engine in content process.
+     * @param aMsaaID is an a11y-specific unique id for the content process
+     *                that is generated by the chrome process. Only used on
+     *                Windows; pass 0 on other platforms.
      */
-    async ActivateA11y();
+    async ActivateA11y(uint32_t aMsaaID);
 
     /**
      * Shutdown accessibility engine in content process (if not in use).
      */
     async ShutdownA11y();
 
     async AppInfo(nsCString version, nsCString buildID, nsCString name, nsCString UAName,
                   nsCString ID, nsCString vendor);
@@ -1170,17 +1173,17 @@ parent:
      async UnstoreAndBroadcastBlobURLUnregistration(nsCString url);
 
     /**
      * Messages for communicating child Telemetry to the parent process
      */
     async AccumulateChildHistogram(Accumulation[] accumulations);
     async AccumulateChildKeyedHistogram(KeyedAccumulation[] accumulations);
 
-     sync GetA11yContentId() returns (uint32_t aContentId);
+    sync GetA11yContentId() returns (uint32_t aContentId);
 
 both:
      async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
                         Principal aPrincipal, ClonedMessageData aData);
 
     /**
      * Notify `push-subscription-modified` observers in the parent and child.
      */
--- a/dom/ipc/StructuredCloneData.cpp
+++ b/dom/ipc/StructuredCloneData.cpp
@@ -41,16 +41,17 @@ StructuredCloneData::Copy(const Structur
   }
 
   PortIdentifiers().AppendElements(aData.PortIdentifiers());
 
   MOZ_ASSERT(BlobImpls().IsEmpty());
   BlobImpls().AppendElements(aData.BlobImpls());
 
   MOZ_ASSERT(GetSurfaces().IsEmpty());
+  MOZ_ASSERT(WasmModules().IsEmpty());
 
   mInitialized = true;
 
   return true;
 }
 
 void
 StructuredCloneData::Read(JSContext* aCx,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1185,18 +1185,18 @@ TabChild::GetInterface(const nsIID & aII
 }
 
 NS_IMETHODIMP
 TabChild::ProvideWindow(mozIDOMWindowProxy* aParent,
                         uint32_t aChromeFlags,
                         bool aCalledFromJS,
                         bool aPositionSpecified, bool aSizeSpecified,
                         nsIURI* aURI, const nsAString& aName,
-                        const nsACString& aFeatures, bool* aWindowIsNew,
-                        mozIDOMWindowProxy** aReturn)
+                        const nsACString& aFeatures, bool aForceNoOpener,
+                        bool* aWindowIsNew, mozIDOMWindowProxy** aReturn)
 {
     *aReturn = nullptr;
 
     // If aParent is inside an <iframe mozbrowser> or <iframe mozapp> and this
     // isn't a request to open a modal-type window, we're going to create a new
     // <iframe mozbrowser/mozapp> and return its window here.
     nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
     bool iframeMoz = (docshell && docshell->GetIsInMozBrowserOrApp() &&
@@ -1228,16 +1228,17 @@ TabChild::ProvideWindow(mozIDOMWindowPro
                                    iframeMoz,
                                    aChromeFlags,
                                    aCalledFromJS,
                                    aPositionSpecified,
                                    aSizeSpecified,
                                    aURI,
                                    aName,
                                    aFeatures,
+                                   aForceNoOpener,
                                    aWindowIsNew,
                                    aReturn);
 }
 
 void
 TabChild::DestroyWindow()
 {
     nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
@@ -2232,17 +2233,18 @@ TabChild::RecvPasteTransferable(const IP
   NS_ENSURE_SUCCESS(rv, true);
 
   ourDocShell->DoCommandWithParams("cmd_pasteTransferable", params);
   return true;
 }
 
 
 a11y::PDocAccessibleChild*
-TabChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&)
+TabChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&,
+                                   const uint32_t&)
 {
   MOZ_ASSERT(false, "should never call this!");
   return nullptr;
 }
 
 bool
 TabChild::DeallocPDocAccessibleChild(a11y::PDocAccessibleChild* aChild)
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -440,17 +440,18 @@ public:
                                 InfallibleTArray<CpowEntry>&& aCpows,
                                 const IPC::Principal& aPrincipal,
                                 const ClonedMessageData& aData) override;
 
   virtual bool
   RecvSwappedWithOtherRemoteLoader(const IPCTabContext& aContext) override;
 
   virtual PDocAccessibleChild*
-  AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) override;
+  AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&,
+                           const uint32_t&) override;
 
   virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) override;
 
   virtual PDocumentRendererChild*
   AllocPDocumentRendererChild(const nsRect& aDocumentRect,
                               const gfx::Matrix& aTransform,
                               const nsString& aBggcolor,
                               const uint32_t& aRenderFlags,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -98,16 +98,20 @@
 #include "gfxDrawable.h"
 #include "ImageOps.h"
 #include "UnitTransforms.h"
 #include <algorithm>
 #include "mozilla/WebBrowserPersistDocumentParent.h"
 #include "nsIGroupedSHistory.h"
 #include "PartialSHistory.h"
 
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+#include "mozilla/a11y/AccessibleWrap.h"
+#endif
+
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::services;
 using namespace mozilla::widget;
 using namespace mozilla::jsipc;
 using namespace mozilla::gfx;
@@ -1087,17 +1091,17 @@ TabParent::SetDocShell(nsIDocShell *aDoc
 {
   NS_ENSURE_ARG(aDocShell);
   NS_WARNING("No mDocShell member in TabParent so there is no docShell to set");
   return NS_OK;
 }
 
   a11y::PDocAccessibleParent*
 TabParent::AllocPDocAccessibleParent(PDocAccessibleParent* aParent,
-                                     const uint64_t&)
+                                     const uint64_t&, const uint32_t&)
 {
 #ifdef ACCESSIBILITY
   return new a11y::DocAccessibleParent();
 #else
   return nullptr;
 #endif
 }
 
@@ -1108,42 +1112,50 @@ TabParent::DeallocPDocAccessibleParent(P
   delete static_cast<a11y::DocAccessibleParent*>(aParent);
 #endif
   return true;
 }
 
 bool
 TabParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
                                          PDocAccessibleParent* aParentDoc,
-                                         const uint64_t& aParentID)
+                                         const uint64_t& aParentID,
+                                         const uint32_t& aMsaaID)
 {
 #ifdef ACCESSIBILITY
   auto doc = static_cast<a11y::DocAccessibleParent*>(aDoc);
   if (aParentDoc) {
     // A document should never directly be the parent of another document.
     // There should always be an outer doc accessible child of the outer
     // document containing the child.
     MOZ_ASSERT(aParentID);
     if (!aParentID) {
       return false;
     }
 
     auto parentDoc = static_cast<a11y::DocAccessibleParent*>(aParentDoc);
-    return parentDoc->AddChildDoc(doc, aParentID);
+    bool added = parentDoc->AddChildDoc(doc, aParentID);
+#ifdef XP_WIN
+    a11y::WrapperFor(doc)->SetID(aMsaaID);
+#endif
+    return added;
   } else {
     // null aParentDoc means this document is at the top level in the child
     // process.  That means it makes no sense to get an id for an accessible
     // that is its parent.
     MOZ_ASSERT(!aParentID);
     if (aParentID) {
       return false;
     }
 
     doc->SetTopLevel();
     a11y::DocManager::RemoteDocAdded(doc);
+#ifdef XP_WIN
+    a11y::WrapperFor(doc)->SetID(aMsaaID);
+#endif
   }
 #endif
   return true;
 }
 
 a11y::DocAccessibleParent*
 TabParent::GetTopLevelDocAccessible() const
 {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -351,24 +351,26 @@ public:
   virtual bool
   DeallocPColorPickerParent(PColorPickerParent* aColorPicker) override;
 
   virtual PDatePickerParent*
   AllocPDatePickerParent(const nsString& aTitle, const nsString& aInitialDate) override;
   virtual bool DeallocPDatePickerParent(PDatePickerParent* aDatePicker) override;
 
   virtual PDocAccessibleParent*
-  AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&) override;
+  AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&,
+                            const uint32_t&) override;
 
   virtual bool DeallocPDocAccessibleParent(PDocAccessibleParent*) override;
 
   virtual bool
   RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
                                 PDocAccessibleParent* aParentDoc,
-                                const uint64_t& aParentID) override;
+                                const uint64_t& aParentID,
+                                const uint32_t& aMsaaID) override;
 
   /**
    * Return the top level doc accessible parent for this tab.
    */
   a11y::DocAccessibleParent* GetTopLevelDocAccessible() const;
 
   void LoadURL(nsIURI* aURI);
 
--- a/dom/media/MediaPrefs.h
+++ b/dom/media/MediaPrefs.h
@@ -90,16 +90,19 @@ private:
   DECL_MEDIA_PREF("accessibility.monoaudio.enable",           MonoAudio, bool, false);
   DECL_MEDIA_PREF("media.resampling.enabled",                 AudioSinkResampling, bool, false);
   DECL_MEDIA_PREF("media.resampling.rate",                    AudioSinkResampleRate, uint32_t, 48000);
   DECL_MEDIA_PREF("media.forcestereo.enabled",                AudioSinkForceStereo, bool, true);
 
   // VideoSink
   DECL_MEDIA_PREF("media.ruin-av-sync.enabled",               RuinAvSync, bool, false);
 
+  // Encrypted Media Extensions
+  DECL_MEDIA_PREF("media.clearkey.persistent-license.enabled", ClearKeyPersistentLicenseEnabled, bool, false);
+
   // PlatformDecoderModule
   DECL_MEDIA_PREF("media.apple.forcevda",                     AppleForceVDA, bool, false);
   DECL_MEDIA_PREF("media.gmp.insecure.allow",                 GMPAllowInsecure, bool, false);
   DECL_MEDIA_PREF("media.gmp.async-shutdown-timeout",         GMPAsyncShutdownTimeout, uint32_t, GMP_DEFAULT_ASYNC_SHUTDOWN_TIMEOUT);
   DECL_MEDIA_PREF("media.eme.enabled",                        EMEEnabled, bool, false);
   DECL_MEDIA_PREF("media.use-blank-decoder",                  PDMUseBlankDecoder, bool, false);
   DECL_MEDIA_PREF("media.gpu-process-decoder",                PDMUseGPUDecoder, bool, false);
 #ifdef MOZ_GONK_MEDIACODEC
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -498,17 +498,17 @@ MediaKeySession::Close(ErrorResult& aRv)
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
       NS_LITERAL_CSTRING("MediaKeySession.Close() lost reference to CDM"));
     return promise.forget();
   }
   // 4. Let promise be a new promise.
   PromiseId pid = mKeys->StorePromise(promise);
   // 5. Run the following steps in parallel:
-  // 5.1 Let cdm be the CDM instance represented by session's cdm instance value.
+  // 5.1 Let cdm be the CDM instance represented by session's cdm instance value.
   // 5.2 Use cdm to close the session associated with session.
   mKeys->GetCDMProxy()->CloseSession(mSessionId, pid);
 
   EME_LOG("MediaKeySession[%p,'%s'] Close() sent to CDM, promiseId=%d",
           this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);
 
   // Session Closed algorithm is run when CDM causes us to run OnSessionClosed().
 
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -92,18 +92,17 @@ MediaKeySystemAccess::GetConfiguration(M
 }
 
 already_AddRefed<Promise>
 MediaKeySystemAccess::CreateMediaKeys(ErrorResult& aRv)
 {
   RefPtr<MediaKeys> keys(new MediaKeys(mParent,
                                        mKeySystem,
                                        mCDMVersion,
-                                       mConfig.mDistinctiveIdentifier == MediaKeysRequirement::Required,
-                                       mConfig.mPersistentState == MediaKeysRequirement::Required));
+                                       mConfig));
   return keys->Init(aRv);
 }
 
 static bool
 HaveGMPFor(mozIGeckoMediaPluginService* aGMPService,
            const nsCString& aKeySystem,
            const nsCString& aAPI,
            const nsCString& aTag = EmptyCString())
@@ -457,17 +456,19 @@ GetSupportedKeySystems()
       KeySystemConfig clearkey;
       clearkey.mKeySystem = NS_ConvertUTF8toUTF16(kEMEKeySystemClearkey);
       clearkey.mInitDataTypes.AppendElement(NS_LITERAL_STRING("cenc"));
       clearkey.mInitDataTypes.AppendElement(NS_LITERAL_STRING("keyids"));
       clearkey.mInitDataTypes.AppendElement(NS_LITERAL_STRING("webm"));
       clearkey.mPersistentState = KeySystemFeatureSupport::Requestable;
       clearkey.mDistinctiveIdentifier = KeySystemFeatureSupport::Prohibited;
       clearkey.mSessionTypes.AppendElement(MediaKeySessionType::Temporary);
-      clearkey.mSessionTypes.AppendElement(MediaKeySessionType::Persistent_license);
+      if (MediaPrefs::ClearKeyPersistentLicenseEnabled()) {
+        clearkey.mSessionTypes.AppendElement(MediaKeySessionType::Persistent_license);
+      }
 #if defined(XP_WIN)
       // Clearkey CDM uses WMF decoders on Windows.
       if (WMFDecoderModule::HasAAC()) {
         clearkey.mMP4.SetCanDecryptAndDecode(EME_CODEC_AAC);
       } else {
         clearkey.mMP4.SetCanDecrypt(EME_CODEC_AAC);
       }
       if (WMFDecoderModule::HasH264()) {
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -45,24 +45,22 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKe
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeys)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 MediaKeys::MediaKeys(nsPIDOMWindowInner* aParent,
                      const nsAString& aKeySystem,
                      const nsAString& aCDMVersion,
-                     bool aDistinctiveIdentifierRequired,
-                     bool aPersistentStateRequired)
+                     const MediaKeySystemConfiguration& aConfig)
   : mParent(aParent)
   , mKeySystem(aKeySystem)
   , mCDMVersion(aCDMVersion)
   , mCreatePromiseId(0)
-  , mDistinctiveIdentifierRequired(aDistinctiveIdentifierRequired)
-  , mPersistentStateRequired(aPersistentStateRequired)
+  , mConfig(aConfig)
 {
   EME_LOG("MediaKeys[%p] constructed keySystem=%s",
           this, NS_ConvertUTF16toUTF8(mKeySystem).get());
 }
 
 MediaKeys::~MediaKeys()
 {
   Shutdown();
@@ -336,18 +334,18 @@ MediaKeys::Init(ErrorResult& aRv)
     NS_LITERAL_CSTRING("MediaKeys::Init()")));
   if (aRv.Failed()) {
     return nullptr;
   }
 
   mProxy = new GMPCDMProxy(this,
                            mKeySystem,
                            new MediaKeysGMPCrashHelper(this),
-                           mDistinctiveIdentifierRequired,
-                           mPersistentStateRequired);
+                           mConfig.mDistinctiveIdentifier == MediaKeysRequirement::Required,
+                           mConfig.mPersistentState == MediaKeysRequirement::Required);
 
   // Determine principal (at creation time) of the MediaKeys object.
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetParentObject());
   if (!sop) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
                          NS_LITERAL_CSTRING("Couldn't get script principal in MediaKeys::Init"));
     return promise.forget();
   }
@@ -438,21 +436,49 @@ MediaKeys::OnCDMCreated(PromiseId aId, c
 
   MediaKeySystemAccess::NotifyObservers(mParent,
                                         mKeySystem,
                                         MediaKeySystemStatus::Cdm_created);
 
   Telemetry::Accumulate(Telemetry::VIDEO_CDM_CREATED, ToCDMTypeTelemetryEnum(mKeySystem));
 }
 
+static bool
+IsSessionTypeSupported(const MediaKeySessionType aSessionType,
+                       const MediaKeySystemConfiguration& aConfig)
+{
+  if (aSessionType == MediaKeySessionType::Temporary) {
+    // Temporary is always supported.
+    return true;
+  }
+  if (!aConfig.mSessionTypes.WasPassed()) {
+    // No other session types supported.
+    return false;
+  }
+  using MediaKeySessionTypeValues::strings;
+  const char* sessionType = strings[static_cast<uint32_t>(aSessionType)].value;
+  for (const nsString& s : aConfig.mSessionTypes.Value()) {
+    if (s.EqualsASCII(sessionType)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 already_AddRefed<MediaKeySession>
 MediaKeys::CreateSession(JSContext* aCx,
                          MediaKeySessionType aSessionType,
                          ErrorResult& aRv)
 {
+  if (!IsSessionTypeSupported(aSessionType, mConfig)) {
+    EME_LOG("MediaKeys[%p,'%s'] CreateSession() failed, unsupported session type", this);
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
   if (!mProxy) {
     NS_WARNING("Tried to use a MediaKeys which lost its CDM");
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
   EME_LOG("MediaKeys[%p] Creating session", this);
 
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -11,16 +11,17 @@
 #include "nsISupports.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsRefPtrHashtable.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/MediaKeysBinding.h"
+#include "mozilla/dom/MediaKeySystemAccessBinding.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "mozilla/DetailedPromise.h"
 #include "mozilla/WeakPtr.h"
 
 namespace mozilla {
 
 class CDMProxy;
 
@@ -47,18 +48,17 @@ class MediaKeys final : public nsISuppor
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeys)
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MediaKeys)
 
   MediaKeys(nsPIDOMWindowInner* aParentWindow,
             const nsAString& aKeySystem,
             const nsAString& aCDMVersion,
-            bool aDistinctiveIdentifierRequired,
-            bool aPersistentStateRequired);
+            const MediaKeySystemConfiguration& aConfig);
 
   already_AddRefed<DetailedPromise> Init(ErrorResult& aRv);
 
   nsPIDOMWindowInner* GetParentObject() const;
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   nsresult Bind(HTMLMediaElement* aElement);
@@ -152,18 +152,17 @@ private:
   KeySessionHashMap mKeySessions;
   PromiseHashMap mPromises;
   PendingKeySessionsHashMap mPendingSessions;
   PromiseId mCreatePromiseId;
 
   RefPtr<nsIPrincipal> mPrincipal;
   RefPtr<nsIPrincipal> mTopLevelPrincipal;
 
-  const bool mDistinctiveIdentifierRequired;
-  const bool mPersistentStateRequired;
+  const MediaKeySystemConfiguration mConfig;
 
   PendingPromiseIdTokenHashMap mPromiseIdToken;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_mediakeys_h__
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -692,18 +692,16 @@ skip-if = toolkit == 'android' # bug 114
 [test_eme_initDataTypes.html]
 skip-if = toolkit == 'android' # bug 1149374
 [test_eme_missing_pssh.html]
 skip-if = toolkit == 'android' # bug 1149374
 [test_eme_non_mse_fails.html]
 skip-if = toolkit == 'android' # bug 1149374
 [test_eme_request_notifications.html]
 skip-if = toolkit == 'android' # bug 1149374
-[test_eme_persistent_sessions.html]
-skip-if = toolkit == 'android' # bug 1149374
 [test_eme_playback.html]
 skip-if = toolkit == 'android' || toolkit == 'gonk' # android: bug 1149374; gonk: bug 1193351
 [test_eme_requestKeySystemAccess.html]
 skip-if = toolkit == 'android' # bug 1149374
 [test_eme_setMediaKeys_before_attach_MediaSource.html]
 skip-if = toolkit == 'android' # bug 1149374
 [test_eme_stream_capture_blocked_case1.html]
 tags=msg capturestream
--- a/dom/media/test/test_eme_initDataTypes.html
+++ b/dom/media/test/test_eme_initDataTypes.html
@@ -37,40 +37,33 @@ var tests = [
     expectPass: true,
   },
   {
     name: "Two keyIds, persistent session, type before kids",
     initDataType: 'keyids',
     initData: '{"type":"persistent-license", "kids":["LwVHf8JLtPrv2GUXFW2v_A", "0DdtU9od-Bh5L3xbv0Xf_A"]}',
     expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A","0DdtU9od-Bh5L3xbv0Xf_A"],"type":"persistent-license"}',
     sessionType: 'persistent-license',
-    expectPass: true,
+    expectPass: false,
   },
   {
     name: "Invalid keyId",
     initDataType: 'keyids',
     initData: '{"kids":["0"]}',
     sessionType: 'temporary',
     expectPass: false,
   },
   {
     name: "Empty keyId",
     initDataType: 'keyids',
     initData: '{"kids":[""]}',
     sessionType: 'temporary',
     expectPass: false,
   },
   {
-    name: "SessionType in license doesn't match MediaKeySession's sessionType",
-    initDataType: 'keyids',
-    initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"]}',
-    sessionType: 'persistent-license',
-    expectPass: false,
-  },
-  {
     name: "Invalid initData",
     initDataType: 'keyids',
     initData: 'invalid initData',
     sessionType: 'temporary',
     expectPass: false,
   },
   {
     name: "'webm' initDataType",
deleted file mode 100644
--- a/dom/media/test/test_eme_persistent_sessions.html
+++ /dev/null
@@ -1,166 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test Encrypted Media Extensions</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  <script type="text/javascript" src="manifest.js"></script>
-  <script type="text/javascript" src="eme.js"></script>
-</head>
-<body>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-var manager = new MediaTestManager;
-
-function UsableKeyIdsMatch(usableKeyIds, expectedKeyIds) {
-  var hexKeyIds = usableKeyIds.map(function(keyId) {
-    return Base64ToHex(window.btoa(ArrayBufferToString(keyId)));
-  }).sort();
-  var expected = Object.keys(expectedKeyIds).sort();
-  if (expected.length != hexKeyIds.length) {
-    return false;
-  }
-  for (var i = 0; i < hexKeyIds.length; i++) {
-    if (hexKeyIds[i] != expected[i]){
-      return false;
-    }
-  }
-  return true;
-}
-
-function AwaitAllKeysUsable(session, keys, token) {
-  return new Promise(function(resolve, reject) {
-    function check() {
-      var map = session.keyStatuses;
-      var usableKeyIds = [];
-      for (var [key, val] of map.entries()) {
-        is(val, "usable", token + ": key status should be usable");
-        usableKeyIds.push(key);
-      }
-      if (UsableKeyIdsMatch(usableKeyIds, keys)) {
-        session.removeEventListener("keystatuseschange", check);
-        resolve();
-      }
-    }
-    session.addEventListener("keystatuseschange", check);
-    check(); // in case all keys are already usable
-  });
-}
-
-function AwaitAllKeysNotUsable(session, token) {
-  return new Promise(function(resolve, reject) {
-    function check() {
-      var map = session.keyStatuses;
-      if (map.size == 0) {
-        session.removeEventListener("keystatuseschange", check);
-        resolve();
-      }
-    }
-    session.addEventListener("keystatuseschange", check);
-    check(); // in case all keys are already removed
-  });
-}
-
-function startTest(test, token)
-{
-  manager.started(token);
-
-  var recreatedSession; // will have remove() called on it.
-
-  var keySystemAccess;
-
-  var v = SetupEME(test, token,
-    {
-      onsessionupdated: function(session) {
-        Log(token, "Session created");
-        var sessionId;
-
-        // Once the session has loaded and has all its keys usable, close
-        // all sessions without calling remove() on them.
-        AwaitAllKeysUsable(session, test.keys, token)
-        .then(function() {
-          sessionId = session.sessionId;
-          Log(token, "Closing session with id=" + sessionId);
-          return session.close();
-        })
-
-        // Once the session is closed, reload the MediaKeys and reload the session
-        .then(function() {
-          return navigator.requestMediaKeySystemAccess(CLEARKEY_KEYSYSTEM, gCencMediaKeySystemConfig);
-        })
-
-        .then(function(requestedKeySystemAccess) {
-          keySystemAccess = requestedKeySystemAccess;
-          return keySystemAccess.createMediaKeys();
-        })
-
-        .then(function(mediaKeys) {
-          Log(token, "re-created MediaKeys object ok");
-          recreatedSession = mediaKeys.createSession("persistent-license");
-          Log(token, "Created recreatedSession, loading sessionId=" + sessionId);
-          return recreatedSession.load(sessionId);
-        })
-
-        .then(function(suceeded) {
-          if (suceeded) {
-            return Promise.resolve();
-          } else {
-            return Promise.reject("Fail to load recreatedSession, sessionId=" + sessionId);
-          }
-        })
-
-        .then(function() {
-          return AwaitAllKeysUsable(recreatedSession, test.keys, token);
-        })
-
-        .then(function() {
-          Log(token, "re-loaded persistent session, all keys still usable");
-          return Promise.all([AwaitAllKeysNotUsable(recreatedSession, token), recreatedSession.remove()]);
-        })
-
-        .then(function() {
-          Log(token, "removed session, all keys unusable.");
-          // Attempt to recreate the session, the attempt should fail.
-          return keySystemAccess.createMediaKeys();
-        })
-
-        .then(function(mediaKeys) {
-          Log(token, "re-re-created MediaKeys object ok");
-          // Trying to load the removed persistent session should fail.
-          return mediaKeys.createSession("persistent-license").load(sessionId);
-        })
-
-        .then(function(suceeded) {
-          is(suceeded, false, token + " we expect the third session creation to fail, as the session should have been removed.");
-          manager.finished(token);
-        })
-
-        .catch(function(reason) {
-          // Catch rejections if any.
-          ok(false, token + " rejected, reason=" + reason);
-          manager.finished(token);
-        });
-
-      },
-      sessionType: "persistent-license",
-    }
-  );
-
-  LoadTestWithManagedLoadToken(test, v, manager, token,
-                               { onlyLoadFirstFragments:2, noEndOfStream:false });
-}
-
-function beginTest() {
-  manager.runTests(gEMETests.filter(t => t.sessionCount === 1), startTest);
-}
-
-if (!IsMacOSSnowLeopardOrEarlier()) {
-  SimpleTest.waitForExplicitFinish();
-  SetupEMEPref(beginTest);
-} else {
-  todo(false, "Test disabled on this platform.");
-}
-</script>
-</pre>
-</body>
-</html>
--- a/dom/media/test/test_eme_requestKeySystemAccess.html
+++ b/dom/media/test/test_eme_requestKeySystemAccess.html
@@ -233,79 +233,39 @@ var tests = [
     expectedConfig: {
       label: SUPPORTED_LABEL,
       initDataTypes: ['cenc'],
       videoCapabilities: [{contentType: 'video/mp4'}],
     },
     shouldPass: true,
   },
   {
-    name: 'Persistent sessions; persistence required',
-    options: [
-      {
-        label: SUPPORTED_LABEL,
-        initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: 'video/mp4'}],
-        sessionTypes: ['temporary','persistent-license'],
-        persistentState: 'required',
-      }
-    ],
-    expectedConfig: {
-      label: SUPPORTED_LABEL,
-      initDataTypes: ['cenc'],
-      videoCapabilities: [{contentType: 'video/mp4'}],
-      sessionTypes: ['temporary','persistent-license'],
-      persistentState: 'required',
-    },
-    shouldPass: true,
-  },
-  {
-    name: 'Persistent sessions not allowed when persistentState prohibited',
+    name: 'Persistent-license should not be supported by ClearKey',
     options: [
       {
         initDataTypes: ['cenc'],
         videoCapabilities: [{contentType: 'video/mp4'}],
-        sessionTypes: ['temporary','persistent-license'],
-        persistentState: 'not-allowed',
+        sessionTypes: ['persistent-license'],
+        persistentState: 'optional',
       }
     ],
     shouldPass: false,
-  },
-  {
-    name: 'Persistent sessions; should bump optional persistState to required',
-    options: [
-      {
-        label: SUPPORTED_LABEL,
-        initDataTypes: ['cenc'],
-        videoCapabilities: [{contentType: 'video/mp4'}],
-        sessionTypes: ['temporary','persistent-license'],
-        persistentState: 'optional',
-      }
-    ],
-    expectedConfig: {
-      label: SUPPORTED_LABEL,
-      initDataTypes: ['cenc'],
-      videoCapabilities: [{contentType: 'video/mp4'}],
-      sessionTypes: ['temporary','persistent-license'],
-      persistentState: 'required',
-    },
-    shouldPass: true,
-  },
+  },  
   {
     name: 'Persistent-usage-record should not be supported by ClearKey',
     options: [
       {
         initDataTypes: ['cenc'],
         videoCapabilities: [{contentType: 'video/mp4'}],
         sessionTypes: ['persistent-usage-record'],
-        persistentState: 'required',
+        persistentState: 'optional',
       }
     ],
     shouldPass: false,
-  },  
+  },
   {
     name: 'MP4 audio container',
     options: [
       {
         label: SUPPORTED_LABEL,
         initDataTypes: ['cenc'],
         audioCapabilities: [{contentType: 'audio/mp4'}],
       }
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -881,17 +881,17 @@ var interfaceNamesInGlobalScope =
     {name: "SpecialPowers", xbl: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "StereoPannerNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Storage",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "StorageEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "StorageManager",
+    {name: "StorageManager", nightly: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "StyleSheet",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "StyleSheetList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SubtleCrypto",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SVGAElement",
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -122,17 +122,18 @@ partial interface Navigator {
 interface NavigatorGeolocation {
   [Throws, Pref="geo.enabled"]
   readonly attribute Geolocation geolocation;
 };
 Navigator implements NavigatorGeolocation;
 
 // http://www.w3.org/TR/battery-status/#navigatorbattery-interface
 partial interface Navigator {
-  [Throws, Pref="dom.battery.enabled"]
+  // ChromeOnly to prevent web content from fingerprinting users' batteries.
+  [Throws, ChromeOnly, Pref="dom.battery.enabled"]
   Promise<BatteryManager> getBattery();
 };
 
 partial interface Navigator {
   [NewObject, Pref="dom.flyweb.enabled"]
   Promise<FlyWebPublishedServer> publishServer(DOMString name,
                                                optional FlyWebPublishOptions options);
 };
--- a/dom/webidl/XULElement.webidl
+++ b/dom/webidl/XULElement.webidl
@@ -121,16 +121,19 @@ interface XULElement : Element {
 interface MozFrameLoaderOwner {
   [ChromeOnly]
   readonly attribute MozFrameLoader? frameLoader;
 
   [ChromeOnly]
   void setIsPrerendered();
 
   [ChromeOnly, Throws]
+  void presetOpenerWindow(WindowProxy? window);
+
+  [ChromeOnly, Throws]
   void swapFrameLoaders(XULElement aOtherLoaderOwner);
 
   [ChromeOnly, Throws]
   void swapFrameLoaders(HTMLIFrameElement aOtherLoaderOwner);
 };
 
 XULElement implements GlobalEventHandlers;
 XULElement implements TouchEventHandlers;
--- a/dom/workers/test/navigator_worker.js
+++ b/dom/workers/test/navigator_worker.js
@@ -10,39 +10,40 @@ var supportedProps = [
   "appVersion",
   "platform",
   "product",
   "userAgent",
   "onLine",
   "language",
   "languages",
   "hardwareConcurrency",
-  "storage"
+  { name: "storage", nightly: true },
 ];
 
 self.onmessage = function(event) {
   if (!event || !event.data) {
     return;
   }
 
-  startTest(event.data.isB2G);
+  startTest(event.data);
 };
 
-function startTest(isB2G) {
+function startTest(channelData) {
   // Prepare the interface map showing if a propery should exist in this build.
   // For example, if interfaceMap[foo] = true means navigator.foo should exist.
   var interfaceMap = {};
 
   for (var prop of supportedProps) {
     if (typeof(prop) === "string") {
       interfaceMap[prop] = true;
       continue;
     }
 
-    if (prop.b2g === !isB2G) {
+    if (prop.nightly === !channelData.isNightly ||
+        prop.release === !channelData.isRelease) {
       interfaceMap[prop.name] = false;
       continue;
     }
 
     interfaceMap[prop.name] = true;
   }
 
   for (var prop in navigator) {
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -48,19 +48,19 @@ var ecmaGlobals =
     "Object",
     "Promise",
     "Proxy",
     "RangeError",
     "ReferenceError",
     "Reflect",
     "RegExp",
     "Set",
-    {name: "SharedArrayBuffer", nightly: true},
+    {name: "SharedArrayBuffer", release: false},
     {name: "SIMD", nightly: true},
-    {name: "Atomics", nightly: true},
+    {name: "Atomics", release: false},
     "StopIteration",
     "String",
     "Symbol",
     "SyntaxError",
     {name: "TypedObject", nightly: true},
     "TypeError",
     "Uint16Array",
     "Uint32Array",
@@ -191,17 +191,17 @@ var interfaceNamesInGlobalScope =
     "Response",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ServiceWorker",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ServiceWorkerGlobalScope",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ServiceWorkerRegistration",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "StorageManager",
+    {name: "StorageManager", nightly: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SubtleCrypto",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TextDecoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TextEncoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "URL",
--- a/dom/workers/test/test_navigator.html
+++ b/dom/workers/test/test_navigator.html
@@ -49,16 +49,20 @@ Tests of DOM Worker Navigator
        "Mismatched navigator string for " + args.name + "!");
   };
 
   worker.onerror = function(event) {
     ok(false, "Worker had an error: " + event.message);
     SimpleTest.finish();
   }
 
-  worker.postMessage({ 'isB2G': SpecialPowers.isB2G });
+  var version = SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].getService(SpecialPowers.Ci.nsIXULAppInfo).version;
+  var isNightly = version.endsWith("a1");
+  var isRelease = !version.includes("a");
+
+  worker.postMessage({ isNightly, isRelease });
 
   SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -48,19 +48,19 @@ var ecmaGlobals =
     "Object",
     "Promise",
     "Proxy",
     "RangeError",
     "ReferenceError",
     "Reflect",
     "RegExp",
     "Set",
-    {name: "SharedArrayBuffer", nightly: true},
+    {name: "SharedArrayBuffer", release: false},
     {name: "SIMD", nightly: true},
-    {name: "Atomics", nightly: true},
+    {name: "Atomics", release: false},
     "StopIteration",
     "String",
     "Symbol",
     "SyntaxError",
     {name: "TypedObject", nightly: true},
     "TypeError",
     "Uint16Array",
     "Uint32Array",
@@ -174,17 +174,17 @@ var interfaceNamesInGlobalScope =
     "PushSubscriptionOptions",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Request",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Response",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ServiceWorkerRegistration",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "StorageManager",
+    {name: "StorageManager", nightly: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SubtleCrypto",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TextDecoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TextEncoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "XMLHttpRequest",
--- a/dom/xml/XMLDocument.cpp
+++ b/dom/xml/XMLDocument.cpp
@@ -123,35 +123,37 @@ NS_NewDOMDocument(nsIDOMDocument** aInst
     MOZ_ASSERT(aFlavor == DocumentFlavorLegacyGuess);
     rv = NS_NewXMLDocument(getter_AddRefs(d));
   }
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  if (nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aEventObject)) {
-    d->SetScriptHandlingObject(sgo);
-  } else if (aEventObject){
-    d->SetScopeObject(aEventObject);
-  }
-
   if (isHTML) {
     nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(d);
     NS_ASSERTION(htmlDoc, "HTML Document doesn't implement nsIHTMLDocument?");
     htmlDoc->SetCompatibilityMode(eCompatibility_FullStandards);
     htmlDoc->SetIsXHTML(isXHTML);
   }
   nsDocument* doc = static_cast<nsDocument*>(d.get());
   doc->SetLoadedAsData(aLoadedAsData);
   doc->nsDocument::SetDocumentURI(aDocumentURI);
   // Must set the principal first, since SetBaseURI checks it.
   doc->SetPrincipal(aPrincipal);
   doc->SetBaseURI(aBaseURI);
 
+  // We need to set the script handling object after we set the principal such
+  // that the doc group is assigned correctly.
+  if (nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aEventObject)) {
+    d->SetScriptHandlingObject(sgo);
+  } else if (aEventObject){
+    d->SetScopeObject(aEventObject);
+  }
+
   // XMLDocuments and documents "created in memory" get to be UTF-8 by default,
   // unlike the legacy HTML mess
   doc->SetDocumentCharacterSet(NS_LITERAL_CSTRING("UTF-8"));
   
   if (aDoctype) {
     nsCOMPtr<nsIDOMNode> tmpNode;
     rv = doc->AppendChild(aDoctype, getter_AddRefs(tmpNode));
     NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/xslt/xslt/txMozillaXMLOutput.cpp
+++ b/dom/xslt/xslt/txMozillaXMLOutput.cpp
@@ -798,24 +798,27 @@ txMozillaXMLOutput::createResultDocument
                nsIDocument::READYSTATE_UNINITIALIZED, "Bad readyState");
     mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_LOADING);
     nsCOMPtr<nsIDocument> source = do_QueryInterface(aSourceDocument);
     NS_ENSURE_STATE(source);
     bool hasHadScriptObject = false;
     nsIScriptGlobalObject* sgo =
       source->GetScriptHandlingObject(hasHadScriptObject);
     NS_ENSURE_STATE(sgo || !hasHadScriptObject);
-    mDocument->SetScriptHandlingObject(sgo);
 
     mCurrentNode = mDocument;
     mNodeInfoManager = mDocument->NodeInfoManager();
 
     // Reset and set up the document
     URIUtils::ResetWithSource(mDocument, aSourceDocument);
 
+    // Make sure we set the script handling object after resetting with the
+    // source, so that we have the right principal.
+    mDocument->SetScriptHandlingObject(sgo);
+
     // Set the charset
     if (!mOutputFormat.mEncoding.IsEmpty()) {
         nsAutoCString canonicalCharset;
         if (EncodingUtils::FindEncodingForLabel(mOutputFormat.mEncoding,
                                                 canonicalCharset)) {
             mDocument->SetDocumentCharacterSetSource(kCharsetFromOtherComponent);
             mDocument->SetDocumentCharacterSet(canonicalCharset);
         }
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -90,16 +90,17 @@
 #include "rdf.h"
 #include "nsIControllers.h"
 #include "nsAttrValueOrString.h"
 #include "nsAttrValueInlines.h"
 #include "mozilla/Attributes.h"
 #include "nsIController.h"
 #include "nsQueryObject.h"
 #include <algorithm>
+#include "nsIDOMChromeWindow.h"
 
 // The XUL doc interface
 #include "nsIDOMXULDocument.h"
 
 #include "nsReadableUtils.h"
 #include "nsIFrame.h"
 #include "nsNodeInfoManager.h"
 #include "nsXBLBinding.h"
@@ -176,26 +177,27 @@ nsXULElement::~nsXULElement()
 nsXULElement::nsXULSlots::nsXULSlots()
     : nsXULElement::nsDOMSlots()
 {
 }
 
 nsXULElement::nsXULSlots::~nsXULSlots()
 {
     NS_IF_RELEASE(mControllers); // Forces release
-    if (mFrameLoader) {
-        mFrameLoader->Destroy();
+    nsCOMPtr<nsIFrameLoader> frameLoader = do_QueryInterface(mFrameLoaderOrOpener);
+    if (frameLoader) {
+        static_cast<nsFrameLoader*>(frameLoader.get())->Destroy();
     }
 }
 
 void
 nsXULElement::nsXULSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
 {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mFrameLoader");
-    cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIFrameLoader*, mFrameLoader));
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mFrameLoaderOrOpener");
+    cb.NoteXPCOMChild(mFrameLoaderOrOpener);
 }
 
 nsINode::nsSlots*
 nsXULElement::CreateSlots()
 {
     return new nsXULSlots();
 }
 
@@ -891,25 +893,21 @@ nsXULElement::UnbindFromTree(bool aDeep,
     // which owns this content.  That's a cycle, so we break
     // it here.  (It might be better to break this by releasing
     // mDocument in nsGlobalWindow::SetDocShell, but I'm not
     // sure whether that would fix all possible cycles through
     // mControllers.)
     nsXULSlots* slots = static_cast<nsXULSlots*>(GetExistingDOMSlots());
     if (slots) {
         NS_IF_RELEASE(slots->mControllers);
-        if (slots->mFrameLoader) {
-            // This element is being taken out of the document, destroy the
-            // possible frame loader.
-            // XXXbz we really want to only partially destroy the frame
-            // loader... we don't want to tear down the docshell.  Food for
-            // later bug.
-            slots->mFrameLoader->Destroy();
-            slots->mFrameLoader = nullptr;
+        RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+        if (frameLoader) {
+            frameLoader->Destroy();
         }
+        slots->mFrameLoaderOrOpener = nullptr;
     }
 
     nsStyledElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 void
 nsXULElement::RemoveChildAt(uint32_t aIndex, bool aNotify)
 {
@@ -1241,20 +1239,21 @@ nsXULElement::RemoveBroadcaster(const ns
 }
 
 void
 nsXULElement::DestroyContent()
 {
     nsXULSlots* slots = static_cast<nsXULSlots*>(GetExistingDOMSlots());
     if (slots) {
         NS_IF_RELEASE(slots->mControllers);
-        if (slots->mFrameLoader) {
-            slots->mFrameLoader->Destroy();
-            slots->mFrameLoader = nullptr;
+        RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+        if (frameLoader) {
+            frameLoader->Destroy();
         }
+        slots->mFrameLoaderOrOpener = nullptr;
     }
 
     nsStyledElement::DestroyContent();
 }
 
 #ifdef DEBUG
 void
 nsXULElement::List(FILE* out, int32_t aIndent) const
@@ -1571,120 +1570,158 @@ nsXULElement::LoadSrc()
         return NS_OK;
     }
     if (!IsInUncomposedDoc() ||
         !OwnerDoc()->GetRootElement() ||
         OwnerDoc()->GetRootElement()->
             NodeInfo()->Equals(nsGkAtoms::overlay, kNameSpaceID_XUL)) {
         return NS_OK;
     }
-    nsXULSlots* slots = static_cast<nsXULSlots*>(Slots());
-    if (!slots->mFrameLoader) {
+    RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+    if (!frameLoader) {
+        // Check if we have an opener we need to be setting
+        nsXULSlots* slots = static_cast<nsXULSlots*>(Slots());
+        nsCOMPtr<nsPIDOMWindowOuter> opener = do_QueryInterface(slots->mFrameLoaderOrOpener);
+        if (!opener) {
+            // If we are a content-primary xul-browser, we want to take the opener property!
+            nsCOMPtr<nsIDOMChromeWindow> chromeWindow = do_QueryInterface(OwnerDoc()->GetWindow());
+            if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+                            NS_LITERAL_STRING("content-primary"), eIgnoreCase) &&
+                chromeWindow) {
+                nsCOMPtr<mozIDOMWindowProxy> wp;
+                chromeWindow->TakeOpenerForInitialContentBrowser(getter_AddRefs(wp));
+                opener = nsPIDOMWindowOuter::From(wp);
+            }
+        }
+
         // false as the last parameter so that xul:iframe/browser/editor
         // session history handling works like dynamic html:iframes.
         // Usually xul elements are used in chrome, which doesn't have
         // session history at all.
-        slots->mFrameLoader = nsFrameLoader::Create(this, false);
-        NS_ENSURE_TRUE(slots->mFrameLoader, NS_OK);
+        frameLoader = nsFrameLoader::Create(this, opener, false);
+        slots->mFrameLoaderOrOpener = static_cast<nsIFrameLoader*>(frameLoader);
+        NS_ENSURE_TRUE(frameLoader, NS_OK);
 
         (new AsyncEventDispatcher(this,
                                   NS_LITERAL_STRING("XULFrameLoaderCreated"),
                                   /* aBubbles */ true))->RunDOMEventWhenSafe();
 
         if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::prerendered,
                         NS_LITERAL_STRING("true"), eIgnoreCase)) {
-            nsresult rv = slots->mFrameLoader->SetIsPrerendered();
+            nsresult rv = frameLoader->SetIsPrerendered();
             NS_ENSURE_SUCCESS(rv,rv);
         }
     }
 
-    return slots->mFrameLoader->LoadFrame();
+    return frameLoader->LoadFrame();
 }
 
 nsresult
 nsXULElement::GetFrameLoaderXPCOM(nsIFrameLoader **aFrameLoader)
 {
     *aFrameLoader = GetFrameLoader().take();
     return NS_OK;
 }
 
 already_AddRefed<nsFrameLoader>
 nsXULElement::GetFrameLoader()
 {
     nsXULSlots* slots = static_cast<nsXULSlots*>(GetExistingSlots());
     if (!slots)
         return nullptr;
 
-    RefPtr<nsFrameLoader> loader = slots->mFrameLoader;
-    return loader.forget();
+    nsCOMPtr<nsIFrameLoader> loader = do_QueryInterface(slots->mFrameLoaderOrOpener);
+    return already_AddRefed<nsFrameLoader>(static_cast<nsFrameLoader*>(loader.forget().take()));
 }
 
 nsresult
 nsXULElement::GetParentApplication(mozIApplication** aApplication)
 {
     if (!aApplication) {
         return NS_ERROR_FAILURE;
     }
 
     *aApplication = nullptr;
     return NS_OK;
 }
 
+void
+nsXULElement::PresetOpenerWindow(mozIDOMWindowProxy* aWindow, ErrorResult& aRv)
+{
+    nsXULSlots* slots = static_cast<nsXULSlots*>(Slots());
+    MOZ_ASSERT(!slots->mFrameLoaderOrOpener, "A frameLoader or opener is present when calling PresetOpenerWindow");
+
+    slots->mFrameLoaderOrOpener = aWindow;
+}
+
 nsresult
 nsXULElement::SetIsPrerendered()
 {
   return SetAttr(kNameSpaceID_None, nsGkAtoms::prerendered, nullptr,
                  NS_LITERAL_STRING("true"), true);
 }
 
 void
+nsXULElement::InternalSetFrameLoader(nsIFrameLoader* aNewFrameLoader)
+{
+    nsXULSlots* slots = static_cast<nsXULSlots*>(GetExistingDOMSlots());
+    MOZ_ASSERT(slots);
+
+    slots->mFrameLoaderOrOpener = aNewFrameLoader;
+}
+
+void
 nsXULElement::SwapFrameLoaders(HTMLIFrameElement& aOtherLoaderOwner,
                                ErrorResult& rv)
 {
-    nsXULSlots *ourSlots = static_cast<nsXULSlots*>(GetExistingDOMSlots());
-    if (!ourSlots) {
+    if (!GetExistingDOMSlots()) {
         rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
         return;
     }
 
-    aOtherLoaderOwner.SwapFrameLoaders(ourSlots->mFrameLoader, rv);
+    nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(static_cast<nsIDOMXULElement*>(this));
+    aOtherLoaderOwner.SwapFrameLoaders(flo, rv);
 }
 
 void
 nsXULElement::SwapFrameLoaders(nsXULElement& aOtherLoaderOwner,
                                ErrorResult& rv)
 {
     if (&aOtherLoaderOwner == this) {
         // nothing to do
         return;
     }
 
-    nsXULSlots *otherSlots =
-        static_cast<nsXULSlots*>(aOtherLoaderOwner.GetExistingDOMSlots());
-    if (!otherSlots) {
+    if (!GetExistingDOMSlots()) {
         rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
         return;
     }
 
-    SwapFrameLoaders(otherSlots->mFrameLoader, rv);
+    nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(static_cast<nsIDOMXULElement*>(this));
+    aOtherLoaderOwner.SwapFrameLoaders(flo, rv);
 }
 
 void
-nsXULElement::SwapFrameLoaders(RefPtr<nsFrameLoader>& aOtherLoader,
+nsXULElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoaderOwner,
                                mozilla::ErrorResult& rv)
 {
-    nsXULSlots *ourSlots = static_cast<nsXULSlots*>(GetExistingDOMSlots());
-    if (!ourSlots || !ourSlots->mFrameLoader || !aOtherLoader) {
+    if (!GetExistingDOMSlots()) {
         rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
         return;
     }
 
-    rv = ourSlots->mFrameLoader->SwapWithOtherLoader(aOtherLoader,
-                                                     ourSlots->mFrameLoader,
-                                                     aOtherLoader);
+    RefPtr<nsFrameLoader> loader = GetFrameLoader();
+    RefPtr<nsFrameLoader> otherLoader = aOtherLoaderOwner->GetFrameLoader();
+    if (!loader || !otherLoader) {
+        rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+        return;
+    }
+
+    nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(static_cast<nsIDOMXULElement*>(this));
+    rv = loader->SwapWithOtherLoader(otherLoader, flo, aOtherLoaderOwner);
 }
 
 NS_IMETHODIMP
 nsXULElement::GetParentTree(nsIDOMXULMultiSelectControlElement** aTreeElement)
 {
     for (nsIContent* current = GetParent(); current;
          current = current->GetParent()) {
         if (current->NodeInfo()->Equals(nsGkAtoms::listbox,
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -407,16 +407,17 @@ public:
     // nsIDOMXULElement
     NS_DECL_NSIDOMXULELEMENT
 
     virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
     virtual mozilla::EventStates IntrinsicState() const override;
 
     nsresult GetFrameLoaderXPCOM(nsIFrameLoader** aFrameLoader);
     nsresult GetParentApplication(mozIApplication** aApplication);
+    void PresetOpenerWindow(mozIDOMWindowProxy* aWindow, ErrorResult& aRv);
     nsresult SetIsPrerendered();
 
     virtual void RecompileScriptEventListeners() override;
 
     // This function should ONLY be used by BindToTree implementations.
     // The function exists solely because XUL elements store the binding
     // parent as a member instead of in the slots, as Element does.
     void SetXULBindingParent(nsIContent* aBindingParent)
@@ -569,21 +570,22 @@ public:
                              const nsAString& aValue);
     already_AddRefed<nsINodeList>
       GetElementsByAttributeNS(const nsAString& aNamespaceURI,
                                const nsAString& aAttribute,
                                const nsAString& aValue,
                                mozilla::ErrorResult& rv);
     // Style() inherited from nsStyledElement
     already_AddRefed<nsFrameLoader> GetFrameLoader();
+    void InternalSetFrameLoader(nsIFrameLoader* aNewFrameLoader);
     void SwapFrameLoaders(mozilla::dom::HTMLIFrameElement& aOtherLoaderOwner,
                           mozilla::ErrorResult& rv);
     void SwapFrameLoaders(nsXULElement& aOtherLoaderOwner,
                           mozilla::ErrorResult& rv);
-    void SwapFrameLoaders(RefPtr<nsFrameLoader>& aOtherLoader,
+    void SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoaderOwner,
                           mozilla::ErrorResult& rv);
 
     nsINode* GetScopeChainParent() const override
     {
         // For XUL, the parent is the parent element, if any
         Element* parent = GetParentElement();
         return parent ? parent : nsStyledElement::GetScopeChainParent();
     }
@@ -610,17 +612,17 @@ protected:
     class nsXULSlots : public mozilla::dom::Element::nsDOMSlots
     {
     public:
         nsXULSlots();
         virtual ~nsXULSlots();
 
         void Traverse(nsCycleCollectionTraversalCallback &cb);
 
-        RefPtr<nsFrameLoader> mFrameLoader;
+        nsCOMPtr<nsISupports> mFrameLoaderOrOpener;
     };
 
     virtual nsINode::nsSlots* CreateSlots() override;
 
     nsresult LoadSrc();
 
     /**
      * The nearest enclosing content node with a binding
--- a/dom/xul/nsXULPrototypeCache.cpp
+++ b/dom/xul/nsXULPrototypeCache.cpp
@@ -482,17 +482,17 @@ nsXULPrototypeCache::BeginCaching(nsIURI
     // all subsequent packages of cached chrome URIs....
     nsAutoCString package;
     rv = aURI->GetHost(package);
     if (NS_FAILED(rv))
         return rv;
     nsCOMPtr<nsIXULChromeRegistry> chromeReg
         = do_GetService(NS_CHROMEREGISTRY_CONTRACTID, &rv);
     nsAutoCString locale;
-    rv = chromeReg->GetSelectedLocale(package, locale);
+    rv = chromeReg->GetSelectedLocale(package, false, locale);
     if (NS_FAILED(rv))
         return rv;
 
     nsAutoCString fileChromePath, fileLocale;
 
     UniquePtr<char[]> buf;
     uint32_t len, amtRead;
     nsCOMPtr<nsIObjectInputStream> objectInput;
--- a/editor/composer/nsEditorSpellCheck.cpp
+++ b/editor/composer/nsEditorSpellCheck.cpp
@@ -917,17 +917,17 @@ nsEditorSpellCheck::DictionaryFetched(Di
   // As next fallback, try the current locale.
   if (NS_FAILED(rv)) {
     nsCOMPtr<nsIXULChromeRegistry> packageRegistry =
       mozilla::services::GetXULChromeRegistryService();
 
     if (packageRegistry) {
       nsAutoCString utf8DictName;
       rv2 = packageRegistry->GetSelectedLocale(NS_LITERAL_CSTRING("global"),
-                                               utf8DictName);
+                                               false, utf8DictName);
       dictName.Assign(EmptyString());
       AppendUTF8toUTF16(utf8DictName, dictName);
 #ifdef DEBUG_DICT
       printf("***** Trying locale |%s|\n",
              NS_ConvertUTF16toUTF8(dictName).get());
 #endif
       rv = TryDictionary (dictName, dictList, DICT_COMPARE_CASE_INSENSITIVE);
     }
--- a/embedding/browser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/nsDocShellTreeOwner.cpp
@@ -180,92 +180,16 @@ nsDocShellTreeOwner::GetInterface(const 
 
   return NS_NOINTERFACE;
 }
 
 //*****************************************************************************
 // nsDocShellTreeOwner::nsIDocShellTreeOwner
 //*****************************************************************************
 
-NS_IMETHODIMP
-nsDocShellTreeOwner::FindItemWithName(const char16_t* aName,
-                                      nsIDocShellTreeItem* aRequestor,
-                                      nsIDocShellTreeItem* aOriginalRequestor,
-                                      nsIDocShellTreeItem** aFoundItem)
-{
-  NS_ENSURE_ARG(aName);
-  NS_ENSURE_ARG_POINTER(aFoundItem);
-
-  // if we don't find one, we return NS_OK and a null result
-  *aFoundItem = nullptr;
-  nsresult rv;
-
-  nsAutoString name(aName);
-
-  if (!mWebBrowser) {
-    return NS_OK; // stymied
-  }
-
-  /* special cases */
-  if (name.IsEmpty()) {
-    return NS_OK;
-  }
-  if (name.LowerCaseEqualsLiteral("_blank")) {
-    return NS_OK;
-  }
-  // _main is an IE target which should be case-insensitive but isn't
-  // see bug 217886 for details
-  // XXXbz what if our browser isn't targetable?  We need to handle that somehow.
-  if (name.LowerCaseEqualsLiteral("_content") || name.EqualsLiteral("_main")) {
-    nsCOMPtr<nsIDocShell> foundItem = mWebBrowser->mDocShell;
-    foundItem.forget(aFoundItem);
-    return NS_OK;
-  }
-
-  if (!SameCOMIdentity(aRequestor, mWebBrowser->mDocShell)) {
-    // This isn't a request coming up from our kid, so check with said kid
-    nsISupports* thisSupports = static_cast<nsIDocShellTreeOwner*>(this);
-    rv = mWebBrowser->mDocShell->FindItemWithName(aName, thisSupports,
-                                                  aOriginalRequestor, aFoundItem);
-    if (NS_FAILED(rv) || *aFoundItem) {
-      return rv;
-    }
-  }
-
-  // next, if we have a parent and it isn't the requestor, ask it
-  if (mTreeOwner) {
-    nsCOMPtr<nsIDocShellTreeOwner> reqAsTreeOwner(do_QueryInterface(aRequestor));
-    if (mTreeOwner != reqAsTreeOwner)
-      return mTreeOwner->FindItemWithName(aName, mWebBrowser->mDocShell,
-                                          aOriginalRequestor, aFoundItem);
-    return NS_OK;
-  }
-
-  // finally, failing everything else, search all windows
-  return FindItemWithNameAcrossWindows(aName, aRequestor, aOriginalRequestor,
-                                       aFoundItem);
-}
-
-nsresult
-nsDocShellTreeOwner::FindItemWithNameAcrossWindows(
-    const char16_t* aName,
-    nsIDocShellTreeItem* aRequestor,
-    nsIDocShellTreeItem* aOriginalRequestor,
-    nsIDocShellTreeItem** aFoundItem)
-{
-  // search for the item across the list of top-level windows
-  nsCOMPtr<nsPIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
-  if (!wwatch) {
-    return NS_OK;
-  }
-
-  return wwatch->FindItemWithName(aName, aRequestor, aOriginalRequestor,
-                                  aFoundItem);
-}
-
 void
 nsDocShellTreeOwner::EnsurePrompter()
 {
   if (mPrompter) {
     return;
   }
 
   nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
--- a/embedding/browser/nsWebBrowser.cpp
+++ b/embedding/browser/nsWebBrowser.cpp
@@ -419,19 +419,18 @@ nsWebBrowser::SetName(const nsAString& a
   } else {
     mInitInfo->name = aName;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWebBrowser::NameEquals(const char16_t* aName, bool* aResult)
+nsWebBrowser::NameEquals(const nsAString& aName, bool* aResult)
 {
-  NS_ENSURE_ARG_POINTER(aName);
   NS_ENSURE_ARG_POINTER(aResult);
   if (mDocShell) {
     return mDocShell->NameEquals(aName, aResult);
   } else {
     *aResult = mInitInfo->name.Equals(aName);
   }
 
   return NS_OK;
@@ -514,17 +513,17 @@ nsWebBrowser::GetSameTypeRootTreeItem(ns
     NS_ENSURE_SUCCESS((*aRootTreeItem)->GetSameTypeParent(getter_AddRefs(parent)),
                       NS_ERROR_FAILURE);
   }
   NS_ADDREF(*aRootTreeItem);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWebBrowser::FindItemWithName(const char16_t* aName,
+nsWebBrowser::FindItemWithName(const nsAString& aName,
                                nsISupports* aRequestor,
                                nsIDocShellTreeItem* aOriginalRequestor,
                                nsIDocShellTreeItem** aResult)
 {
   NS_ENSURE_STATE(mDocShell);
   NS_ASSERTION(mDocShellTreeOwner,
                "This should always be set when in this situation");
 
@@ -594,17 +593,17 @@ nsWebBrowser::RemoveChild(nsIDocShellTre
 
 NS_IMETHODIMP
 nsWebBrowser::GetChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild)
 {
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
-nsWebBrowser::FindChildWithName(const char16_t* aName,
+nsWebBrowser::FindChildWithName(const nsAString& aName,
                                 bool aRecurse,
                                 bool aSameType,
                                 nsIDocShellTreeItem* aRequestor,
                                 nsIDocShellTreeItem* aOriginalRequestor,
                                 nsIDocShellTreeItem** aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
 
--- a/embedding/components/windowwatcher/nsIWindowWatcher.idl
+++ b/embedding/components/windowwatcher/nsIWindowWatcher.idl
@@ -143,17 +143,17 @@ interface nsIWindowWatcher : nsISupports
       @param aCurrentWindow a starting point in the window hierarchy to
                             begin the search.  If null, each toplevel window
                             will be searched.
 
       Note: This method will search all open windows for any window or
       frame with the given window name. Make sure you understand the
       security implications of this before using this method!
   */
-  mozIDOMWindowProxy getWindowByName(in wstring aTargetName,
+  mozIDOMWindowProxy getWindowByName(in AString aTargetName,
                                      in mozIDOMWindowProxy aCurrentWindow);
 
   /** The Watcher serves as a global storage facility for the current active
       (frontmost non-floating-palette-type) window, storing and returning
       it on demand. Users must keep this attribute current, including after
       the topmost window is closed. This attribute obviously can return null
       if no windows are open, but should otherwise always return a valid
       window.
--- a/embedding/components/windowwatcher/nsPIWindowWatcher.idl
+++ b/embedding/components/windowwatcher/nsPIWindowWatcher.idl
@@ -134,13 +134,13 @@ interface nsPIWindowWatcher : nsISupport
    * @return the tree item with aName as the name, or null if there
    *         isn't one.  "Special" names, like _self, _top, etc, will be
    *         treated specially only if aRequestor is null; in that case they
    *         will be resolved relative to the first window the windowwatcher
    *         knows about.
    * @see findItemWithName methods on nsIDocShellTreeItem and
    *      nsIDocShellTreeOwner
    */
-  nsIDocShellTreeItem findItemWithName(in wstring aName,
+  nsIDocShellTreeItem findItemWithName(in AString aName,
                                        in nsIDocShellTreeItem aRequestor,
                                        in nsIDocShellTreeItem aOriginalRequestor);
 };
 
--- a/embedding/components/windowwatcher/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp
@@ -60,18 +60,20 @@
 #include "nsContentUtils.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsSandboxFlags.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/DOMStorage.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/DocGroup.h"
 #include "nsIXULWindow.h"
 #include "nsIXULBrowserWindow.h"
+#include "nsGlobalWindow.h"
 
 #ifdef USEWEAKREFS
 #include "nsIWeakReference.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -478,16 +480,17 @@ nsWindowWatcher::OpenWindowWithoutParent
 }
 
 nsresult
 nsWindowWatcher::CreateChromeWindow(const nsACString& aFeatures,
                                     nsIWebBrowserChrome* aParentChrome,
                                     uint32_t aChromeFlags,
                                     uint32_t aContextFlags,
                                     nsITabParent* aOpeningTabParent,
+                                    mozIDOMWindowProxy* aOpener,
                                     nsIWebBrowserChrome** aResult)
 {
   nsCOMPtr<nsIWindowCreator2> windowCreator2(do_QueryInterface(mWindowCreator));
   if (NS_WARN_IF(!windowCreator2)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   // B2G multi-screen support. mozDisplayId is returned from the
@@ -496,17 +499,17 @@ nsWindowWatcher::CreateChromeWindow(cons
   int retval = WinHasOption(aFeatures, "mozDisplayId", 0, nullptr);
   windowCreator2->SetScreenId(retval);
 #endif
 
   bool cancel = false;
   nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
   nsresult rv =
     windowCreator2->CreateChromeWindow2(aParentChrome, aChromeFlags, aContextFlags,
-                                        aOpeningTabParent, &cancel,
+                                        aOpeningTabParent, aOpener, &cancel,
                                         getter_AddRefs(newWindowChrome));
 
   if (NS_SUCCEEDED(rv) && cancel) {
     newWindowChrome = nullptr;
     return NS_ERROR_ABORT;
   }
 
   newWindowChrome.forget(aResult);
@@ -616,17 +619,17 @@ nsWindowWatcher::OpenWindowWithTabParent
   // A content process has asked for a new window, which implies
   // that the new window will need to be remote.
   chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
 
   nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
   nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
 
   CreateChromeWindow(aFeatures, parentChrome, chromeFlags, contextFlags,
-                     aOpeningTabParent, getter_AddRefs(newWindowChrome));
+                     aOpeningTabParent, nullptr, getter_AddRefs(newWindowChrome));
 
   if (NS_WARN_IF(!newWindowChrome)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsCOMPtr<nsIDocShellTreeItem> chromeTreeItem = do_GetInterface(newWindowChrome);
   if (NS_WARN_IF(!chromeTreeItem)) {
     return NS_ERROR_UNEXPECTED;
@@ -875,18 +878,18 @@ nsWindowWatcher::OpenWindowInternal(mozI
         provider = nsContentUtils::GetWindowProviderForContentProcess();
       }
 
       if (provider) {
         nsCOMPtr<mozIDOMWindowProxy> newWindow;
         rv = provider->ProvideWindow(aParent, chromeFlags, aCalledFromJS,
                                      sizeSpec.PositionSpecified(),
                                      sizeSpec.SizeSpecified(),
-                                     uriToLoad, name, features, &windowIsNew,
-                                     getter_AddRefs(newWindow));
+                                     uriToLoad, name, features, aForceNoOpener,
+                                     &windowIsNew, getter_AddRefs(newWindow));
 
         if (NS_SUCCEEDED(rv)) {
           GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
           if (windowIsNew && newDocShellItem) {
             // Make sure to stop any loads happening in this window that the
             // window provider might have started.  Otherwise if our caller
             // manipulates the window it just opened and then the load
             // completes their stuff will get blown away.
@@ -990,31 +993,33 @@ nsWindowWatcher::OpenWindowInternal(mozI
           popupConditions = !isCallerChrome;
         }
 
         if (popupConditions) {
           contextFlags |=
             nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT;
         }
 
+        mozIDOMWindowProxy* openerWindow = aForceNoOpener ? nullptr : aParent;
         rv = CreateChromeWindow(features, parentChrome, chromeFlags, contextFlags,
-                                nullptr, getter_AddRefs(newChrome));
+                                nullptr, openerWindow, getter_AddRefs(newChrome));
 
       } else {
         rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags,
                                                 getter_AddRefs(newChrome));
       }
 
       if (newChrome) {
         nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(newChrome);
         if (xulWin) {
           nsCOMPtr<nsIXULBrowserWindow> xulBrowserWin;
           xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin));
           if (xulBrowserWin) {
-            xulBrowserWin->ForceInitialBrowserNonRemote();
+            nsPIDOMWindowOuter* openerWindow = aForceNoOpener ? nullptr : parentWindow.get();
+            xulBrowserWin->ForceInitialBrowserNonRemote(openerWindow);
           }
         }
         /* It might be a chrome nsXULWindow, in which case it won't have
             an nsIDOMWindow (primary content shell). But in that case, it'll
             be able to hand over an nsIDocShellTreeItem directly. */
         nsCOMPtr<nsPIDOMWindowOuter> newWindow(do_GetInterface(newChrome));
         if (newWindow) {
           GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
@@ -1629,17 +1634,17 @@ nsWindowWatcher::GetChromeForWindow(mozI
     }
     *aResult = info->mChrome;
     NS_IF_ADDREF(*aResult);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWindowWatcher::GetWindowByName(const char16_t* aTargetName,
+nsWindowWatcher::GetWindowByName(const nsAString& aTargetName,
                                  mozIDOMWindowProxy* aCurrentWindow,
                                  mozIDOMWindowProxy** aResult)
 {
   if (!aResult) {
     return NS_ERROR_INVALID_ARG;
   }
 
   *aResult = nullptr;
@@ -2046,81 +2051,39 @@ nsWindowWatcher::WinHasOption(const nsAC
   return found;
 }
 
 /* try to find an nsIDocShellTreeItem with the given name in any
    known open window. a failure to find the item will not
    necessarily return a failure method value. check aFoundItem.
 */
 NS_IMETHODIMP
-nsWindowWatcher::FindItemWithName(const char16_t* aName,
+nsWindowWatcher::FindItemWithName(const nsAString& aName,
                                   nsIDocShellTreeItem* aRequestor,
                                   nsIDocShellTreeItem* aOriginalRequestor,
                                   nsIDocShellTreeItem** aFoundItem)
 {
-  *aFoundItem = 0;
-
-  /* special cases */
-  if (!aName || !*aName) {
+  *aFoundItem = nullptr;
+  if (aName.IsEmpty()) {
     return NS_OK;
   }
 
-  nsDependentString name(aName);
-
-  nsCOMPtr<nsISimpleEnumerator> windows;
-  GetWindowEnumerator(getter_AddRefs(windows));
-  if (!windows) {
-    return NS_ERROR_FAILURE;
+  if (aName.LowerCaseEqualsLiteral("_blank") ||
+      aName.LowerCaseEqualsLiteral("_top") ||
+      aName.LowerCaseEqualsLiteral("_parent") ||
+      aName.LowerCaseEqualsLiteral("_self")) {
+    return NS_OK;
   }
 
-  bool more;
-  nsresult rv = NS_OK;
-
-  do {
-    windows->HasMoreElements(&more);
-    if (!more) {
-      break;
-    }
-    nsCOMPtr<nsISupports> nextSupWindow;
-    windows->GetNext(getter_AddRefs(nextSupWindow));
-    nsCOMPtr<mozIDOMWindowProxy> nextWindow(do_QueryInterface(nextSupWindow));
-    if (nextWindow) {
-      nsCOMPtr<nsIDocShellTreeItem> treeItem;
-      GetWindowTreeItem(nextWindow, getter_AddRefs(treeItem));
-      if (treeItem) {
-        // Get the root tree item of same type, since roots are the only
-        // things that call into the treeowner to look for named items.
-        nsCOMPtr<nsIDocShellTreeItem> root;
-        treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
-        NS_ASSERTION(root, "Must have root tree item of same type");
-        // Make sure not to call back into aRequestor
-        if (root != aRequestor) {
-          // Get the tree owner so we can pass it in as the requestor so
-          // the child knows not to call back up, since we're walking
-          // all windows already.
-          nsCOMPtr<nsIDocShellTreeOwner> rootOwner;
-          // Note: if we have no aRequestor, then we want to also look for
-          // "special" window names, so pass a null requestor.  This will mean
-          // that the treeitem calls back up to us, effectively (with a
-          // non-null aRequestor), so break the loop immediately after the
-          // call in that case.
-          if (aRequestor) {
-            root->GetTreeOwner(getter_AddRefs(rootOwner));
-          }
-          rv = root->FindItemWithName(aName, rootOwner, aOriginalRequestor,
-                                      aFoundItem);
-          if (NS_FAILED(rv) || *aFoundItem || !aRequestor) {
-            break;
-          }
-        }
-      }
-    }
-  } while (1);
-
-  return rv;
+  // If we are looking for an item and we don't have a docshell we are checking
+  // on, let's just look in the chrome tab group!
+  return TabGroup::GetChromeTabGroup()->FindItemWithName(aName,
+                                                         aRequestor,
+                                                         aOriginalRequestor,
+                                                         aFoundItem);
 }
 
 already_AddRefed<nsIDocShellTreeItem>
 nsWindowWatcher::GetCallerTreeItem(nsIDocShellTreeItem* aParentItem)
 {
   nsCOMPtr<nsIWebNavigation> callerWebNav = do_GetInterface(GetEntryGlobal());
   nsCOMPtr<nsIDocShellTreeItem> callerItem = do_QueryInterface(callerWebNav);
   if (!callerItem) {
@@ -2144,24 +2107,22 @@ nsWindowWatcher::SafeGetWindowByName(con
     }
   }
 
   nsCOMPtr<nsIDocShellTreeItem> startItem;
   GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
 
   nsCOMPtr<nsIDocShellTreeItem> callerItem = GetCallerTreeItem(startItem);
 
-  const nsAFlatString& flatName = PromiseFlatString(aName);
-
   nsCOMPtr<nsIDocShellTreeItem> foundItem;
   if (startItem) {
-    startItem->FindItemWithName(flatName.get(), nullptr, callerItem,
+    startItem->FindItemWithName(aName, nullptr, callerItem,
                                 getter_AddRefs(foundItem));
   } else {
-    FindItemWithName(flatName.get(), nullptr, callerItem,
+    FindItemWithName(aName, nullptr, callerItem,
                      getter_AddRefs(foundItem));
   }
 
   return foundItem ? foundItem->GetWindow() : nullptr;
 }
 
 /* Fetch the nsIDOMWindow corresponding to the given nsIDocShellTreeItem.
    This forces the creation of a script context, if one has not already
@@ -2177,38 +2138,39 @@ nsWindowWatcher::ReadyOpenedDocShellItem
 {
   nsresult rv = NS_ERROR_FAILURE;
 
   NS_ENSURE_ARG(aOpenedWindow);
 
   *aOpenedWindow = 0;
   nsCOMPtr<nsPIDOMWindowOuter> piOpenedWindow = aOpenedItem->GetWindow();
   if (piOpenedWindow) {
-    if (aParent) {
-      if (!aForceNoOpener) {
-        piOpenedWindow->SetOpenerWindow(aParent, aWindowIsNew); // damnit
-      }
+    if (!aForceNoOpener) {
+      piOpenedWindow->SetOpenerWindow(aParent, aWindowIsNew); // damnit
+    } else if (aParent && aParent != piOpenedWindow) {
+      MOZ_ASSERT(piOpenedWindow->TabGroup() != aParent->TabGroup(),
+                 "If we're forcing no opener, they should be in different tab groups");
+    }
 
-      if (aWindowIsNew) {
+    if (aWindowIsNew) {
 #ifdef DEBUG
-        // Assert that we're not loading things right now.  If we are, when
-        // that load completes it will clobber whatever principals we set up
-        // on this new window!
-        nsCOMPtr<nsIDocumentLoader> docloader = do_QueryInterface(aOpenedItem);
-        NS_ASSERTION(docloader, "How can we not have a docloader here?");
+      // Assert that we're not loading things right now.  If we are, when
+      // that load completes it will clobber whatever principals we set up
+      // on this new window!
+      nsCOMPtr<nsIDocumentLoader> docloader = do_QueryInterface(aOpenedItem);
+      NS_ASSERTION(docloader, "How can we not have a docloader here?");
 
-        nsCOMPtr<nsIChannel> chan;
-        docloader->GetDocumentChannel(getter_AddRefs(chan));
-        NS_ASSERTION(!chan, "Why is there a document channel?");
+      nsCOMPtr<nsIChannel> chan;
+      docloader->GetDocumentChannel(getter_AddRefs(chan));
+      NS_ASSERTION(!chan, "Why is there a document channel?");
 #endif
 
-        nsCOMPtr<nsIDocument> doc = piOpenedWindow->GetExtantDoc();
-        if (doc) {
-          doc->SetIsInitialDocument(true);
-        }
+      nsCOMPtr<nsIDocument> doc = piOpenedWindow->GetExtantDoc();
+      if (doc) {
+        doc->SetIsInitialDocument(true);
       }
     }
     rv = CallQueryInterface(piOpenedWindow, aOpenedWindow);
   }
   return rv;
 }
 
 // static
--- a/embedding/components/windowwatcher/nsWindowWatcher.h
+++ b/embedding/components/windowwatcher/nsWindowWatcher.h
@@ -125,16 +125,17 @@ protected:
                                  nsIDocShellTreeOwner** aResult);
 
 private:
   nsresult CreateChromeWindow(const nsACString& aFeatures,
                               nsIWebBrowserChrome* aParentChrome,
                               uint32_t aChromeFlags,
                               uint32_t aContextFlags,
                               nsITabParent* aOpeningTabParent,
+                              mozIDOMWindowProxy* aOpener,
                               nsIWebBrowserChrome** aResult);
 
   void MaybeDisablePersistence(const nsACString& aFeatures,
                                nsIDocShellTreeOwner* aTreeOwner);
 
   static uint32_t CalculateChromeFlagsHelper(uint32_t aInitialFlags,
                                              const nsACString& aFeatures,
                                              bool &presenceFlag,
--- a/embedding/nsIWindowCreator2.idl
+++ b/embedding/nsIWindowCreator2.idl
@@ -15,16 +15,17 @@
  * @status
  */
 
 #include "nsIWindowCreator.idl"
 
 interface nsITabParent;
 interface nsIURI;
 interface nsIWebBrowserChrome;
+interface mozIDOMWindowProxy;
 
 [scriptable, uuid(b6c44689-f97e-4f32-a723-29eeddfbdc53)]
 
 interface nsIWindowCreator2 : nsIWindowCreator {
 
   /**
    * Definitions for contextFlags
    */
@@ -37,26 +38,29 @@ interface nsIWindowCreator2 : nsIWindowC
       @param parent Parent window, if any. Null if not. The newly created
                     window should be made a child/dependent window of
                     the parent, if any (and if the concept applies
                     to the underlying OS).
       @param chromeFlags Chrome features from nsIWebBrowserChrome
       @param contextFlags Flags about the context of the window being created.
       @param aOpeningTab The TabParent that is trying to open this new chrome
                          window. Can be nullptr.
+      @param aOpener The window which is trying to open this new chrome window.
+                     Can be nullptr
       @param cancel Return |true| to reject window creation. If true the
                     implementation has determined the window should not
                     be created at all. The caller should not default
                     to any possible backup scheme for creating the window.
       @return the new window. Will be null if canceled or an error occurred.
   */
   nsIWebBrowserChrome createChromeWindow2(in nsIWebBrowserChrome parent,
                                           in uint32_t chromeFlags,
                                           in uint32_t contextFlags,
                                           in nsITabParent aOpeningTab,
+                                          in mozIDOMWindowProxy aOpener,
                                           out boolean cancel);
 
   /**
    * B2G multi-screen support. When open another top-level window on b2g,
    * a screen ID is needed for identifying which screen this window is
    * opened to.
    * @param aScreenId Differentiate screens of windows. It is platform-
    *                  specific due to the hardware limitation for now.
--- a/embedding/nsIWindowProvider.idl
+++ b/embedding/nsIWindowProvider.idl
@@ -93,10 +93,11 @@ interface nsIWindowProvider : nsISupport
   mozIDOMWindowProxy provideWindow(in mozIDOMWindowProxy aParent,
                                    in unsigned long aChromeFlags,
                                    in boolean aCalledFromJS,
                                    in boolean aPositionSpecified,
                                    in boolean aSizeSpecified,
                                    in nsIURI aURI,
                                    in AString aName,
                                    in AUTF8String aFeatures,
+                                   in boolean aForceNoOpener,
                                    out boolean aWindowIsNew);
 };
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -43,16 +43,17 @@ typedef _FcPattern FcPattern;
 
 struct ID3D11Texture2D;
 struct ID3D11Device;
 struct ID2D1Device;
 struct IDWriteRenderingParams;
 struct IDWriteFontFace;
 
 class GrContext;
+class SkCanvas;
 struct gfxFontStyle;
 
 struct CGContext;
 typedef struct CGContext *CGContextRef;
 
 namespace mozilla {
 
 namespace gfx {
@@ -1461,16 +1462,20 @@ public:
    * drawing is distributed over number of tiles which may each hold an
    * individual offset. The tiles in the set must each have the same backend
    * and format.
    */
   static already_AddRefed<DrawTarget> CreateTiledDrawTarget(const TileSet& aTileSet);
 
   static bool DoesBackendSupportDataDrawtarget(BackendType aType);
 
+#ifdef USE_SKIA
+  static already_AddRefed<DrawTarget> CreateDrawTargetWithSkCanvas(SkCanvas* aCanvas);
+#endif
+
 #ifdef XP_DARWIN
   static already_AddRefed<GlyphRenderingOptions>
     CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor);
 #endif
 
 #ifdef WIN32
   static already_AddRefed<DrawTarget> CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat);
 
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -268,17 +268,17 @@ DrawTargetSkia::~DrawTargetSkia()
   }
 #endif
 }
 
 already_AddRefed<SourceSurface>
 DrawTargetSkia::Snapshot()
 {
   RefPtr<SourceSurfaceSkia> snapshot = mSnapshot;
-  if (!snapshot) {
+  if (mSurface && !snapshot) {
     snapshot = new SourceSurfaceSkia();
     sk_sp<SkImage> image;
     // If the surface is raster, making a snapshot may trigger a pixel copy.
     // Instead, try to directly make a raster image referencing the surface pixels.
     SkPixmap pixmap;
     if (mSurface->peekPixels(&pixmap)) {
       image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
     } else {
@@ -1562,16 +1562,20 @@ DrawTargetSkia::CreateSimilarDrawTarget(
     // Try to create a GPU draw target first if we're currently using the GPU.
     // Mark the DT as cached so that shadow DTs, extracted subrects, and similar can be reused.
     if (target->InitWithGrContext(mGrContext.get(), aSize, aFormat, true)) {
       return target.forget();
     }
     // Otherwise, just fall back to a software draw target.
   }
 #endif
+  // Check that our SkCanvas isn't backed by vector storage such as PDF.  If it
+  // is then we want similar storage to avoid losing fidelity.
+  MOZ_ASSERT(mCanvas->imageInfo().colorType() != kUnknown_SkColorType,
+             "Not backed by pixels - we need to handle PDF backed SkCanvas");
   if (!target->Init(aSize, aFormat)) {
     return nullptr;
   }
   return target.forget();
 }
 
 bool
 DrawTargetSkia::UsingSkiaGPU() const
@@ -1749,16 +1753,40 @@ DrawTargetSkia::Init(const IntSize &aSiz
   mCanvas = sk_ref_sp(mSurface->getCanvas());
 
   if (info.isOpaque()) {
     mCanvas->clear(SK_ColorBLACK);
   }
   return true;
 }
 
+bool
+DrawTargetSkia::Init(SkCanvas* aCanvas)
+{
+  mCanvas = sk_ref_sp(aCanvas);
+
+  SkImageInfo imageInfo = mCanvas->imageInfo();
+
+  // If the canvas is backed by pixels we clear it to be on the safe side.  If
+  // it's not (for example, for PDF output) we don't.
+  bool isBackedByPixels = imageInfo.colorType() != kUnknown_SkColorType;
+  if (isBackedByPixels) {
+    // Note for PDF backed SkCanvas |alphaType == kUnknown_SkAlphaType|.
+    SkColor clearColor = imageInfo.isOpaque() ? SK_ColorBLACK : SK_ColorTRANSPARENT;
+    mCanvas->clear(clearColor);
+  }
+
+  SkISize size = mCanvas->getBaseLayerSize();
+  mSize.width = size.width();
+  mSize.height = size.height();
+  mFormat = SkiaColorTypeToGfxFormat(imageInfo.colorType(),
+                                     imageInfo.alphaType());
+  return true;
+}
+
 #ifdef USE_SKIA_GPU
 /** Indicating a DT should be cached means that space will be reserved in Skia's cache
  * for the render target at creation time, with any unused resources exceeding the cache
  * limits being purged. When the DT is freed, it will then be guaranteed to be kept around
  * for subsequent allocations until it gets incidentally purged.
  *
  * If it is not marked as cached, no space will be purged to make room for the render
  * target in the cache. When the DT is freed, If there is space within the resource limits
@@ -1830,17 +1858,17 @@ DrawTargetSkia::SetTransform(const Matri
   mCanvas->setMatrix(mat);
   mTransform = aTransform;
 }
 
 void*
 DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType)
 {
 #ifdef USE_SKIA_GPU
-  if (aType == NativeSurfaceType::OPENGL_TEXTURE) {
+  if (aType == NativeSurfaceType::OPENGL_TEXTURE && mSurface) {
     GrBackendObject handle = mSurface->getTextureHandle(SkSurface::kFlushRead_BackendHandleAccess);
     if (handle) {
       return (void*)(uintptr_t)reinterpret_cast<GrGLTextureInfo *>(handle)->fID;
     }
   }
 #endif
   return nullptr;
 }
@@ -2081,17 +2109,19 @@ DrawTargetSkia::CreateFilter(FilterType 
 void
 DrawTargetSkia::MarkChanged()
 {
   if (mSnapshot) {
     mSnapshot->DrawTargetWillChange();
     mSnapshot = nullptr;
 
     // Handle copying of any image snapshots bound to the surface.
-    mSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
+    if (mSurface) {
+      mSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
+    }
   }
 }
 
 void
 DrawTargetSkia::SnapshotDestroyed()
 {
   mSnapshot = nullptr;
 }
--- a/gfx/2d/DrawTargetSkia.h
+++ b/gfx/2d/DrawTargetSkia.h
@@ -127,16 +127,17 @@ public:
   virtual already_AddRefed<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
   virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
   virtual void SetTransform(const Matrix &aTransform) override;
   virtual void *GetNativeSurface(NativeSurfaceType aType) override;
   virtual void DetachAllSnapshots() override { MarkChanged(); }
 
   bool Init(const IntSize &aSize, SurfaceFormat aFormat);
   bool Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false);
+  bool Init(SkCanvas* aCanvas);
 
 #ifdef USE_SKIA_GPU
   bool InitWithGrContext(GrContext* aGrContext,
                          const IntSize &aSize,
                          SurfaceFormat aFormat,
                          bool aCached);
   virtual bool
     InitWithGrContext(GrContext* aGrContext,
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -699,16 +699,28 @@ Factory::CreateDrawTargetSkiaWithGrConte
   if (!newTarget->InitWithGrContext(aGrContext, aSize, aFormat)) {
     return nullptr;
   }
   return newTarget.forget();
 }
 
 #endif // USE_SKIA_GPU
 
+#ifdef USE_SKIA
+already_AddRefed<DrawTarget>
+Factory::CreateDrawTargetWithSkCanvas(SkCanvas* aCanvas)
+{
+  RefPtr<DrawTargetSkia> newTarget = new DrawTargetSkia();
+  if (!newTarget->Init(aCanvas)) {
+    return nullptr;
+  }
+  return newTarget.forget();
+}
+#endif
+
 void
 Factory::PurgeAllCaches()
 {
 }
 
 already_AddRefed<DrawTarget>
 Factory::CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
 {
--- a/gfx/angle/BUILD.gn
+++ b/gfx/angle/BUILD.gn
@@ -492,33 +492,37 @@ util_gypi = exec_script("//build/gypi_to
 
 config("angle_util_config") {
   include_dirs = [ "util" ]
   if (is_linux && use_x11) {
     libs = [ "X11" ]
   }
 }
 
-static_library("angle_util") {
+shared_library("angle_util") {
   sources = rebase_path(util_gypi.util_sources, ".", "util")
 
   if (is_win) {
     sources += rebase_path(util_gypi.util_win32_sources, ".", "util")
   }
 
   if (is_linux) {
     sources += rebase_path(util_gypi.util_linux_sources, ".", "util")
     libs = [
       "rt",
       "dl",
     ]
   }
 
   if (is_mac) {
     sources += rebase_path(util_gypi.util_osx_sources, ".", "util")
+    libs = [
+      "AppKit.framework",
+      "QuartzCore.framework",
+    ]
   }
 
   if (use_x11) {
     sources += rebase_path(util_gypi.util_x11_sources, ".", "util")
   }
 
   if (is_android) {
     # To prevent linux sources filtering on android
@@ -533,26 +537,34 @@ static_library("angle_util") {
 
   if (use_ozone) {
     sources += rebase_path(util_gypi.util_ozone_sources, ".", "util")
   }
 
   defines = [
     "GL_GLEXT_PROTOTYPES",
     "EGL_EGLEXT_PROTOTYPES",
+    "LIBANGLE_UTIL_IMPLEMENTATION",
   ]
 
   configs += [
     ":debug_annotations_config",
     ":extra_warnings",
   ]
 
   public_configs = [
     ":angle_util_config",
     ":internal_config",
   ]
+  if (is_mac && !is_component_build) {
+    ldflags = [
+      "-install_name",
+      "@rpath/lib${target_name}.dylib",
+    ]
+    public_configs += [ ":shared_library_public_config" ]
+  }
 
   deps = [
     ":angle_common",
     ":libEGL",
     ":libGLESv2",
   ]
 }
--- a/gfx/angle/include/GLSLANG/ShaderLang.h
+++ b/gfx/angle/include/GLSLANG/ShaderLang.h
@@ -44,49 +44,28 @@ typedef unsigned int GLenum;
 }
 
 // Must be included after GLenum proxy typedef
 // Note: make sure to increment ANGLE_SH_VERSION when changing ShaderVars.h
 #include "ShaderVars.h"
 
 // Version number for shader translation API.
 // It is incremented every time the API changes.
-#define ANGLE_SH_VERSION 155
+#define ANGLE_SH_VERSION 161
 
 typedef enum {
     SH_GLES2_SPEC,
     SH_WEBGL_SPEC,
 
     SH_GLES3_SPEC,
     SH_WEBGL2_SPEC,
 
     SH_GLES3_1_SPEC,
     SH_WEBGL3_SPEC,
 
-    // The CSS Shaders spec is a subset of the WebGL spec.
-    //