Browse Source

Console and serviceworkers backports

Ian Neal 7 months ago
parent
commit
df67e4530b
100 changed files with 24885 additions and 162 deletions
  1. 411 0
      mozilla-release/patches/1293277-1-59a1.patch
  2. 79 0
      mozilla-release/patches/1293277-2-59a1.patch
  3. 124 0
      mozilla-release/patches/1293277-3-59a1.patch
  4. 856 0
      mozilla-release/patches/1293277-4-59a1.patch
  5. 3110 0
      mozilla-release/patches/1293277-5-59a1.patch
  6. 802 0
      mozilla-release/patches/1293277-6-59a1.patch
  7. 407 0
      mozilla-release/patches/1293277-7no8-59a1.patch
  8. 26 0
      mozilla-release/patches/1293277-9-59a1.patch
  9. 2 2
      mozilla-release/patches/1340901-87a1.patch
  10. 4 4
      mozilla-release/patches/1393609-60a1.patch
  11. 31 0
      mozilla-release/patches/1415056-1-59a1.patch
  12. 33 0
      mozilla-release/patches/1415056-2-59a1.patch
  13. 507 0
      mozilla-release/patches/1418376-59a1.patch
  14. 341 0
      mozilla-release/patches/1419536-1-59a1.patch
  15. 342 0
      mozilla-release/patches/1419536-2-59a1.patch
  16. 752 0
      mozilla-release/patches/1419536-3-59a1.patch
  17. 441 0
      mozilla-release/patches/1419536-4-59a1.patch
  18. 36 0
      mozilla-release/patches/1419536-5-59a1.patch
  19. 126 0
      mozilla-release/patches/1420221-59a1.patch
  20. 5 5
      mozilla-release/patches/1420594-1-59a1.patch
  21. 149 0
      mozilla-release/patches/1420743-1-59a1.patch
  22. 30 0
      mozilla-release/patches/1420743-2-59a1.patch
  23. 30 0
      mozilla-release/patches/1422584-59a1.patch
  24. 139 0
      mozilla-release/patches/1422983-59a1.patch
  25. 703 0
      mozilla-release/patches/1423328-59a1.patch
  26. 729 0
      mozilla-release/patches/1423412-1-59a1.patch
  27. 139 0
      mozilla-release/patches/1423412-2-59a1.patch
  28. 49 0
      mozilla-release/patches/1423412-3-59a1.patch
  29. 573 0
      mozilla-release/patches/1423412-4no5-59a1.patch
  30. 188 0
      mozilla-release/patches/1423913-1-59a1.patch
  31. 185 0
      mozilla-release/patches/1423913-2-59a1.patch
  32. 98 0
      mozilla-release/patches/1423913-3-59a1.patch
  33. 400 0
      mozilla-release/patches/1424338-1-59a1.patch
  34. 330 0
      mozilla-release/patches/1424338-2-59a1.patch
  35. 746 0
      mozilla-release/patches/1424338-3-59a1.patch
  36. 968 0
      mozilla-release/patches/1424338-4-59a1.patch
  37. 294 0
      mozilla-release/patches/1424338-5-59a1.patch
  38. 509 0
      mozilla-release/patches/1424338-6-59a1.patch
  39. 105 0
      mozilla-release/patches/1424338-7-59a1.patch
  40. 395 0
      mozilla-release/patches/1424452-59a1.patch
  41. 55 0
      mozilla-release/patches/1424943-59a1.patch
  42. 196 0
      mozilla-release/patches/1425316-1-59a1.patch
  43. 134 0
      mozilla-release/patches/1425316-2-59a1.patch
  44. 146 0
      mozilla-release/patches/1425316-3-59a1.patch
  45. 270 0
      mozilla-release/patches/1425316-4-59a1.patch
  46. 138 0
      mozilla-release/patches/1425316-5-59a1.patch
  47. 107 0
      mozilla-release/patches/1425316-6-59a1.patch
  48. 4 4
      mozilla-release/patches/1425574-1-59a1.patch
  49. 38 0
      mozilla-release/patches/1425614-1-59a1.patch
  50. 51 0
      mozilla-release/patches/1425614-2-59a1.patch
  51. 47 0
      mozilla-release/patches/1425704-59a1.patch
  52. 193 0
      mozilla-release/patches/1425965-1-59a1.patch
  53. 209 0
      mozilla-release/patches/1425965-2-59a1.patch
  54. 210 0
      mozilla-release/patches/1425965-3-59a1.patch
  55. 507 0
      mozilla-release/patches/1425965-4-59a1.patch
  56. 108 0
      mozilla-release/patches/1425965-5-59a1.patch
  57. 115 0
      mozilla-release/patches/1425965-6-59a1.patch
  58. 35 0
      mozilla-release/patches/1425965-7-59a1.patch
  59. 172 0
      mozilla-release/patches/1425975-01-59a1.patch
  60. 194 0
      mozilla-release/patches/1425975-02-59a1.patch
  61. 130 0
      mozilla-release/patches/1425975-03-59a1.patch
  62. 59 0
      mozilla-release/patches/1425975-04-59a1.patch
  63. 44 0
      mozilla-release/patches/1425975-05-59a1.patch
  64. 273 0
      mozilla-release/patches/1425975-06-59a1.patch
  65. 478 0
      mozilla-release/patches/1425975-07no08-59a1.patch
  66. 95 0
      mozilla-release/patches/1425975-09-59a1.patch
  67. 84 0
      mozilla-release/patches/1425975-10-59a1.patch
  68. 77 0
      mozilla-release/patches/1425975-11-59a1.patch
  69. 52 0
      mozilla-release/patches/1425975-12-59a1.patch
  70. 155 0
      mozilla-release/patches/1425975-13-59a1.patch
  71. 109 0
      mozilla-release/patches/1425975-14-59a1.patch
  72. 182 0
      mozilla-release/patches/1425975-15-59a1.patch
  73. 248 0
      mozilla-release/patches/1425975-16-59a1.patch
  74. 134 0
      mozilla-release/patches/1425975-17-59a1.patch
  75. 33 0
      mozilla-release/patches/1425975-18-59a1.patch
  76. 66 0
      mozilla-release/patches/1425975-19-59a1.patch
  77. 51 0
      mozilla-release/patches/1426250-1-59a1.patch
  78. 44 0
      mozilla-release/patches/1426250-2-59a1.patch
  79. 406 0
      mozilla-release/patches/1426250-3-59a1.patch
  80. 151 0
      mozilla-release/patches/1426253-1-59a1.patch
  81. 155 0
      mozilla-release/patches/1426253-2-59a1.patch
  82. 254 0
      mozilla-release/patches/1426253-3-59a1.patch
  83. 113 0
      mozilla-release/patches/1426253-4-59a1.patch
  84. 56 0
      mozilla-release/patches/1428650-59a1.patch
  85. 45 0
      mozilla-release/patches/1428652-1-59a1.patch
  86. 165 0
      mozilla-release/patches/1428652-2-59a1.patch
  87. 29 0
      mozilla-release/patches/1428652-3-59a1.patch
  88. 48 0
      mozilla-release/patches/1428725-59a1.patch
  89. 598 0
      mozilla-release/patches/1429174-1-59a1.patch
  90. 147 0
      mozilla-release/patches/1429174-2-59a1.patch
  91. 275 0
      mozilla-release/patches/1429174-3-59a1.patch
  92. 1038 0
      mozilla-release/patches/1431105-59a1.patch
  93. 22 22
      mozilla-release/patches/1434474-60a1.patch
  94. 22 22
      mozilla-release/patches/1439960-1-61a1.patch
  95. 4 4
      mozilla-release/patches/1441246-61a1.patch
  96. 11 11
      mozilla-release/patches/1444905-61a1.patch
  97. 4 4
      mozilla-release/patches/1447210-2-61a1.patch
  98. 145 0
      mozilla-release/patches/1461181-62a1.patch
  99. 246 20
      mozilla-release/patches/1465060-1-std-62a1.patch
  100. 514 64
      mozilla-release/patches/1465585-3-std-62a1.patch

+ 411 - 0
mozilla-release/patches/1293277-1-59a1.patch

@@ -0,0 +1,411 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513111486 18000
+# Node ID e3fb8bd8b10dc5a0a75238a9a3284e4ca5ea2fa3
+# Parent  cc1272b428ef650617c59a004e34a04934a7ed21
+Bug 1293277 P1 Capture StorageAccess when snapshoting ClientSource state. r=baku
+
+diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
+--- a/dom/base/nsContentUtils.h
++++ b/dom/base/nsContentUtils.h
+@@ -2996,16 +2996,18 @@ public:
+     eDeny = 0,
+     // Allow access to the storage, but only if it is secure to do so in a
+     // private browsing context.
+     ePrivateBrowsing = 1,
+     // Allow access to the storage, but only persist it for the current session
+     eSessionScoped = 2,
+     // Allow access to the storage
+     eAllow = 3,
++    // Keep this at the end.  Used for serialization, but not a valid value.
++    eNumValues = 4,
+   };
+ 
+   /*
+    * Checks if storage for the given window is permitted by a combination of
+    * the user's preferences, and whether the window is a third-party iframe.
+    *
+    * This logic is intended to be shared between the different forms of
+    * persistent storage which are available to web pages. Cookies don't use
+diff --git a/dom/clients/manager/ClientIPCTypes.ipdlh b/dom/clients/manager/ClientIPCTypes.ipdlh
+--- a/dom/clients/manager/ClientIPCTypes.ipdlh
++++ b/dom/clients/manager/ClientIPCTypes.ipdlh
+@@ -5,16 +5,17 @@
+ include protocol PClientSource;
+ include DOMTypes;
+ include PBackgroundSharedTypes;
+ include IPCServiceWorkerDescriptor;
+ include ProtocolTypes;
+ using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+ using ClientType from "mozilla/dom/ClientIPCUtils.h";
+ using FrameType from "mozilla/dom/ClientIPCUtils.h";
++using nsContentUtils::StorageAccess from "mozilla/dom/ClientIPCUtils.h";
+ using VisibilityState from "mozilla/dom/ClientIPCUtils.h";
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ struct ClientSourceConstructorArgs
+ {
+   nsID id;
+@@ -32,21 +33,23 @@ struct IPCClientInfo
+   nsCString url;
+   FrameType frameType;
+ };
+ 
+ struct IPCClientWindowState
+ {
+   VisibilityState visibilityState;
+   TimeStamp lastFocusTime;
++  StorageAccess storageAccess;
+   bool focused;
+ };
+ 
+ struct IPCClientWorkerState
+ {
++  StorageAccess storageAccess;
+ };
+ 
+ union IPCClientState
+ {
+   IPCClientWindowState;
+   IPCClientWorkerState;
+ };
+ 
+diff --git a/dom/clients/manager/ClientIPCUtils.h b/dom/clients/manager/ClientIPCUtils.h
+--- a/dom/clients/manager/ClientIPCUtils.h
++++ b/dom/clients/manager/ClientIPCUtils.h
+@@ -9,16 +9,17 @@
+ #include "ipc/IPCMessageUtils.h"
+ 
+ // Fix X11 header brain damage that conflicts with FrameType::None
+ #undef None
+ 
+ #include "mozilla/dom/ClientBinding.h"
+ #include "mozilla/dom/ClientsBinding.h"
+ #include "mozilla/dom/DocumentBinding.h"
++#include "nsContentUtils.h"
+ 
+ namespace IPC {
+   template<>
+   struct ParamTraits<mozilla::dom::ClientType> :
+     public ContiguousEnumSerializer<mozilla::dom::ClientType,
+                                     mozilla::dom::ClientType::Window,
+                                     mozilla::dom::ClientType::EndGuard_>
+   {};
+@@ -31,11 +32,18 @@ namespace IPC {
+   {};
+ 
+   template<>
+   struct ParamTraits<mozilla::dom::VisibilityState> :
+     public ContiguousEnumSerializer<mozilla::dom::VisibilityState,
+                                     mozilla::dom::VisibilityState::Hidden,
+                                     mozilla::dom::VisibilityState::EndGuard_>
+   {};
++
++  template<>
++  struct ParamTraits<nsContentUtils::StorageAccess> :
++    public ContiguousEnumSerializer<nsContentUtils::StorageAccess,
++                                    nsContentUtils::StorageAccess::eDeny,
++                                    nsContentUtils::StorageAccess::eNumValues>
++  {};
+ } // namespace IPC
+ 
+ #endif // _mozilla_dom_ClientIPCUtils_h
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -73,34 +73,40 @@ nsresult
+ ClientSource::SnapshotWindowState(ClientState* aStateOut)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+ 
+   nsPIDOMWindowInner* window = GetInnerWindow();
+   if (!window || !window->IsCurrentInnerWindow() ||
+       !window->HasActiveDocument()) {
+     *aStateOut = ClientState(ClientWindowState(VisibilityState::Hidden,
+-                                               TimeStamp(), false));
++                                               TimeStamp(),
++                                               nsContentUtils::StorageAccess::eDeny,
++                                               false));
+     return NS_OK;
+   }
+ 
+   nsIDocument* doc = window->GetExtantDoc();
+   if (NS_WARN_IF(!doc)) {
+     return NS_ERROR_UNEXPECTED;
+   }
+ 
+   ErrorResult rv;
+   bool focused = doc->HasFocus(rv);
+   if (NS_WARN_IF(rv.Failed())) {
+     rv.SuppressException();
+     return rv.StealNSResult();
+   }
+ 
++  nsContentUtils::StorageAccess storage =
++    nsContentUtils::StorageAllowedForDocument(doc);
++
+   *aStateOut = ClientState(ClientWindowState(doc->VisibilityState(),
+-                                             doc->LastFocusTime(), focused));
++                                             doc->LastFocusTime(), storage,
++                                             focused));
+ 
+   return NS_OK;
+ }
+ 
+ WorkerPrivate*
+ ClientSource::GetWorkerPrivate() const
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientSource);
+@@ -584,17 +590,28 @@ ClientSource::SnapshotState(ClientState*
+     MaybeCreateInitialDocument();
+     nsresult rv = SnapshotWindowState(aStateOut);
+     if (NS_FAILED(rv)) {
+       return rv;
+     }
+     return NS_OK;
+   }
+ 
+-  *aStateOut = ClientState(ClientWorkerState());
++  WorkerPrivate* workerPrivate = GetWorkerPrivate();
++  if (!workerPrivate) {
++    return NS_ERROR_DOM_INVALID_STATE_ERR;
++  }
++
++  // Workers only keep a boolean for storage access at the moment.
++  // Map this back to eAllow or eDeny for now.
++  nsContentUtils::StorageAccess storage =
++    workerPrivate->IsStorageAllowed() ? nsContentUtils::StorageAccess::eAllow
++                                      : nsContentUtils::StorageAccess::eDeny;
++
++  *aStateOut = ClientState(ClientWorkerState(storage));
+   return NS_OK;
+ }
+ 
+ nsISerialEventTarget*
+ ClientSource::EventTarget() const
+ {
+   return mEventTarget;
+ }
+diff --git a/dom/clients/manager/ClientState.cpp b/dom/clients/manager/ClientState.cpp
+--- a/dom/clients/manager/ClientState.cpp
++++ b/dom/clients/manager/ClientState.cpp
+@@ -8,19 +8,20 @@
+ 
+ #include "mozilla/dom/ClientIPCTypes.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ ClientWindowState::ClientWindowState(mozilla::dom::VisibilityState aVisibilityState,
+                                      const TimeStamp& aLastFocusTime,
++                                     nsContentUtils::StorageAccess aStorageAccess,
+                                      bool aFocused)
+   : mData(MakeUnique<IPCClientWindowState>(aVisibilityState, aLastFocusTime,
+-                                           aFocused))
++                                           aStorageAccess, aFocused))
+ {
+ }
+ 
+ ClientWindowState::ClientWindowState(const IPCClientWindowState& aData)
+   : mData(MakeUnique<IPCClientWindowState>(aData))
+ {
+ }
+ 
+@@ -67,24 +68,30 @@ ClientWindowState::LastFocusTime() const
+ }
+ 
+ bool
+ ClientWindowState::Focused() const
+ {
+   return mData->focused();
+ }
+ 
++nsContentUtils::StorageAccess
++ClientWindowState::GetStorageAccess() const
++{
++  return mData->storageAccess();
++}
++
+ const IPCClientWindowState&
+ ClientWindowState::ToIPC() const
+ {
+   return *mData;
+ }
+ 
+-ClientWorkerState::ClientWorkerState()
+-  : mData(MakeUnique<IPCClientWorkerState>())
++ClientWorkerState::ClientWorkerState(nsContentUtils::StorageAccess aStorageAccess)
++  : mData(MakeUnique<IPCClientWorkerState>(aStorageAccess))
+ {
+ }
+ 
+ ClientWorkerState::ClientWorkerState(const IPCClientWorkerState& aData)
+   : mData(MakeUnique<IPCClientWorkerState>(aData))
+ {
+ }
+ 
+@@ -113,16 +120,22 @@ ClientWorkerState::operator=(ClientWorke
+   mData = Move(aRight.mData);
+   return *this;
+ }
+ 
+ ClientWorkerState::~ClientWorkerState()
+ {
+ }
+ 
++nsContentUtils::StorageAccess
++ClientWorkerState::GetStorageAccess() const
++{
++  return mData->storageAccess();
++}
++
+ const IPCClientWorkerState&
+ ClientWorkerState::ToIPC() const
+ {
+   return *mData;
+ }
+ 
+ ClientState::ClientState()
+ {
+@@ -197,16 +210,26 @@ ClientState::IsWorkerState() const
+ }
+ 
+ const ClientWorkerState&
+ ClientState::AsWorkerState() const
+ {
+   return mData.ref().as<ClientWorkerState>();
+ }
+ 
++nsContentUtils::StorageAccess
++ClientState::GetStorageAccess() const
++{
++  if (IsWindowState()) {
++    return AsWindowState().GetStorageAccess();
++  }
++
++  return AsWorkerState().GetStorageAccess();
++}
++
+ const IPCClientState
+ ClientState::ToIPC() const
+ {
+   if (IsWindowState()) {
+     return IPCClientState(AsWindowState().ToIPC());
+   }
+ 
+   return IPCClientState(AsWorkerState().ToIPC());
+diff --git a/dom/clients/manager/ClientState.h b/dom/clients/manager/ClientState.h
+--- a/dom/clients/manager/ClientState.h
++++ b/dom/clients/manager/ClientState.h
+@@ -6,16 +6,17 @@
+ 
+ #ifndef _mozilla_dom_ClientState_h
+ #define _mozilla_dom_ClientState_h
+ 
+ #include "mozilla/dom/DocumentBinding.h"
+ #include "mozilla/Maybe.h"
+ #include "mozilla/TimeStamp.h"
+ #include "mozilla/UniquePtr.h"
++#include "nsContentUtils.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ class IPCClientState;
+ class IPCClientWindowState;
+ class IPCClientWorkerState;
+ 
+@@ -24,16 +25,17 @@ class IPCClientWorkerState;
+ // is not live updated.
+ class ClientWindowState final
+ {
+   UniquePtr<IPCClientWindowState> mData;
+ 
+ public:
+   ClientWindowState(mozilla::dom::VisibilityState aVisibilityState,
+                     const TimeStamp& aLastFocusTime,
++                    nsContentUtils::StorageAccess aStorageAccess,
+                     bool aFocused);
+ 
+   explicit ClientWindowState(const IPCClientWindowState& aData);
+ 
+   ClientWindowState(const ClientWindowState& aRight);
+   ClientWindowState(ClientWindowState&& aRight);
+ 
+   ClientWindowState&
+@@ -48,46 +50,52 @@ public:
+   VisibilityState() const;
+ 
+   const TimeStamp&
+   LastFocusTime() const;
+ 
+   bool
+   Focused() const;
+ 
++  nsContentUtils::StorageAccess
++  GetStorageAccess() const;
++
+   const IPCClientWindowState&
+   ToIPC() const;
+ };
+ 
+ // This class defines the mutable worker state we support querying
+ // through the ClientManagerService.  It is a snapshot of the state and
+ // is not live updated.  Right now, we don't actually providate any
+ // worker specific state values, but we may in the future.  This
+ // class also services as a placeholder that the state is referring
+ // to a worker in ClientState.
+ class ClientWorkerState final
+ {
+   UniquePtr<IPCClientWorkerState> mData;
+ 
+ public:
+-  ClientWorkerState();
++  explicit ClientWorkerState(nsContentUtils::StorageAccess aStorageAccess);
+ 
+   explicit ClientWorkerState(const IPCClientWorkerState& aData);
+ 
+   ClientWorkerState(const ClientWorkerState& aRight);
+   ClientWorkerState(ClientWorkerState&& aRight);
+ 
+   ClientWorkerState&
+   operator=(const ClientWorkerState& aRight);
+ 
+   ClientWorkerState&
+   operator=(ClientWorkerState&& aRight);
+ 
+   ~ClientWorkerState();
+ 
++  nsContentUtils::StorageAccess
++  GetStorageAccess() const;
++
+   const IPCClientWorkerState&
+   ToIPC() const;
+ };
+ 
+ // This is a union of the various types of mutable state we support
+ // querying in ClientManagerService.  Right now it can contain either
+ // window or worker states.
+ class ClientState final
+@@ -123,16 +131,19 @@ public:
+   AsWindowState() const;
+ 
+   bool
+   IsWorkerState() const;
+ 
+   const ClientWorkerState&
+   AsWorkerState() const;
+ 
++  nsContentUtils::StorageAccess
++  GetStorageAccess() const;
++
+   const IPCClientState
+   ToIPC() const;
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientState_h

+ 79 - 0
mozilla-release/patches/1293277-2-59a1.patch

@@ -0,0 +1,79 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513111487 18000
+# Node ID 3f23acc150062cfe3715024196c1ca1c318f9c2a
+# Parent  f629397e0fe46466846d0c74700663e7a7febbc3
+Bug 1293277 P2 Cleanup test_openWindow.html mochitest service worker a bit. r=baku
+
+diff --git a/dom/workers/test/serviceworkers/openWindow_worker.js b/dom/workers/test/serviceworkers/openWindow_worker.js
+--- a/dom/workers/test/serviceworkers/openWindow_worker.js
++++ b/dom/workers/test/serviceworkers/openWindow_worker.js
+@@ -55,37 +55,37 @@ onmessage = function(event) {
+   if (event.data == "testNoPopup") {
+     client = event.source;
+ 
+     var results = [];
+     var promises = [];
+     promises.push(testForUrl("about:blank", "TypeError", null, results));
+     promises.push(testForUrl("http://example.com", "InvalidAccessError", null, results));
+     promises.push(testForUrl("_._*`InvalidURL", "InvalidAccessError", null, results));
+-    Promise.all(promises).then(function(e) {
++    event.waitUntil(Promise.all(promises).then(function(e) {
+       client.postMessage(results);
+-    });
++    }));
+   }
+   if (event.data == "NEW_WINDOW") {
+     window_count += 1;
+     if (window_count == expected_window_count) {
+       resolve_got_all_windows();
+     }
+   }
+ 
+   if (event.data == "CHECK_NUMBER_OF_WINDOWS") {
+-    got_all_windows.then(function() {
++    event.waitUntil(got_all_windows.then(function() {
+       return clients.matchAll();
+     }).then(function(cl) {
+       event.source.postMessage({result: cl.length == expected_window_count,
+                                 message: "The number of windows is correct."});
+       for (i = 0; i < cl.length; i++) {
+         cl[i].postMessage("CLOSE");
+       }
+-    });
++    }));
+   }
+ }
+ 
+ onnotificationclick = function(e) {
+   var results = [];
+   var promises = [];
+ 
+   var redirect = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/redirect.sjs?"
+@@ -96,21 +96,21 @@ onnotificationclick = function(e) {
+ 
+   promises.push(testForUrl("about:blank", "TypeError", null, results));
+   promises.push(testForUrl(different_origin, null, null, results));
+   promises.push(testForUrl(same_origin, null, {url: same_origin}, results));
+   promises.push(testForUrl("open_window/client.html", null, {url: same_origin}, results));
+ 
+   // redirect tests
+   promises.push(testForUrl(redirect + "open_window/client.html", null,
+-			   {url: same_origin}, results));
++                           {url: same_origin}, results));
+   promises.push(testForUrl(redirect + different_origin, null, null, results));
+ 
+   promises.push(testForUrl(redirect_xorigin + "open_window/client.html", null,
+-			   null, results));
++                           null, results));
+   promises.push(testForUrl(redirect_xorigin + same_origin, null,
+-			   {url: same_origin}, results));
++                           {url: same_origin}, results));
+ 
+-  Promise.all(promises).then(function(e) {
++  e.waitUntil(Promise.all(promises).then(function(e) {
+     client.postMessage(results);
+-  });
++  }));
+ }
+ 

+ 124 - 0
mozilla-release/patches/1293277-3-59a1.patch

@@ -0,0 +1,124 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513111487 18000
+# Node ID 6d09b4467c62da66ce870de1a90d310cc7abb858
+# Parent  32ae3d3ea376163f854b27352211cbaa2009a57e
+Bug 1293277 P3 Remove the dom.serviceWorkers.openWindow.enabled pref. r=baku
+
+diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
+--- a/browser/app/profile/firefox.js
++++ b/browser/app/profile/firefox.js
+@@ -1557,17 +1557,16 @@ pref("reader.parse-node-limit", 0);
+ 
+ // On desktop, we want the URLs to be included here for ease of debugging,
+ // and because (normally) these errors are not persisted anywhere.
+ pref("reader.errors.includeURLs", true);
+ 
+ pref("view_source.tab", true);
+ 
+ pref("dom.serviceWorkers.enabled", true);
+-pref("dom.serviceWorkers.openWindow.enabled", true);
+ 
+ // Enable Push API.
+ pref("dom.push.enabled", true);
+ 
+ // These are the thumbnail width/height set in about:newtab.
+ // If you change this, ENSURE IT IS THE SAME SIZE SET
+ // by about:newtab. These values are in CSS pixels.
+ pref("toolkit.pageThumbs.minWidth", 280);
+diff --git a/dom/webidl/Clients.webidl b/dom/webidl/Clients.webidl
+--- a/dom/webidl/Clients.webidl
++++ b/dom/webidl/Clients.webidl
+@@ -10,18 +10,17 @@
+ 
+ [Exposed=ServiceWorker]
+ interface Clients {
+   // The objects returned will be new instances every time
+   [NewObject]
+   Promise<any> get(DOMString id);
+   [NewObject]
+   Promise<sequence<Client>> matchAll(optional ClientQueryOptions options);
+-  [NewObject,
+-   Func="mozilla::dom::ServiceWorkerGlobalScope::OpenWindowEnabled"]
++  [NewObject]
+   Promise<WindowClient?> openWindow(USVString url);
+   [NewObject]
+   Promise<void> claim();
+ };
+ 
+ dictionary ClientQueryOptions {
+   boolean includeUncontrolled = false;
+   ClientType type = "window";
+diff --git a/dom/workers/WorkerPrefs.h b/dom/workers/WorkerPrefs.h
+--- a/dom/workers/WorkerPrefs.h
++++ b/dom/workers/WorkerPrefs.h
+@@ -28,17 +28,16 @@ WORKER_SIMPLE_PREF("canvas.imagebitmap_e
+ WORKER_SIMPLE_PREF("dom.caches.enabled", DOMCachesEnabled, DOM_CACHES)
+ WORKER_SIMPLE_PREF("dom.caches.testing.enabled", DOMCachesTestingEnabled, DOM_CACHES_TESTING)
+ WORKER_SIMPLE_PREF("dom.performance.enable_user_timing_logging", PerformanceLoggingEnabled, PERFORMANCE_LOGGING_ENABLED)
+ WORKER_SIMPLE_PREF("dom.webnotifications.enabled", DOMWorkerNotificationEnabled, DOM_WORKERNOTIFICATION)
+ WORKER_SIMPLE_PREF("dom.webnotifications.serviceworker.enabled", DOMServiceWorkerNotificationEnabled, DOM_SERVICEWORKERNOTIFICATION)
+ WORKER_SIMPLE_PREF("dom.webnotifications.requireinteraction.enabled", DOMWorkerNotificationRIEnabled, DOM_WORKERNOTIFICATIONRI)
+ WORKER_SIMPLE_PREF("dom.serviceWorkers.enabled", ServiceWorkersEnabled, SERVICEWORKERS_ENABLED)
+ WORKER_SIMPLE_PREF("dom.serviceWorkers.testing.enabled", ServiceWorkersTestingEnabled, SERVICEWORKERS_TESTING_ENABLED)
+-WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, OPEN_WINDOW_ENABLED)
+ WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
+ WORKER_SIMPLE_PREF("dom.promise_rejection_events.enabled", PromiseRejectionEventsEnabled, PROMISE_REJECTION_EVENTS_ENABLED)
+ WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
+ WORKER_SIMPLE_PREF("dom.streams.enabled", StreamsEnabled, STREAMS_ENABLED)
+ WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
+ WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
+ WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)
+ WORKER_SIMPLE_PREF("dom.netinfo.enabled", NetworkInformationEnabled, NETWORKINFORMATION_ENABLED)
+diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp
+--- a/dom/workers/WorkerScope.cpp
++++ b/dom/workers/WorkerScope.cpp
+@@ -844,25 +844,16 @@ ServiceWorkerGlobalScope::SkipWaiting(Er
+   RefPtr<WorkerScopeSkipWaitingRunnable> runnable =
+     new WorkerScopeSkipWaitingRunnable(promiseProxy,
+                                        NS_ConvertUTF16toUTF8(mScope));
+ 
+   MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
+   return promise.forget();
+ }
+ 
+-bool
+-ServiceWorkerGlobalScope::OpenWindowEnabled(JSContext* aCx, JSObject* aObj)
+-{
+-  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+-  MOZ_ASSERT(worker);
+-  worker->AssertIsOnWorkerThread();
+-  return worker->OpenWindowEnabled();
+-}
+-
+ WorkerDebuggerGlobalScope::WorkerDebuggerGlobalScope(
+                                                   WorkerPrivate* aWorkerPrivate)
+ : mWorkerPrivate(aWorkerPrivate)
+ , mSerialEventTarget(aWorkerPrivate->HybridEventTarget())
+ {
+   mWorkerPrivate->AssertIsOnWorkerThread();
+ 
+   // We should always have an event target when the global is created.
+diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h
+--- a/dom/workers/WorkerScope.h
++++ b/dom/workers/WorkerScope.h
+@@ -295,19 +295,16 @@ public:
+   IMPL_EVENT_HANDLER(notificationclose)
+ 
+   ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsACString& aScope);
+ 
+   virtual bool
+   WrapGlobalObject(JSContext* aCx,
+                    JS::MutableHandle<JSObject*> aReflector) override;
+ 
+-  static bool
+-  OpenWindowEnabled(JSContext* aCx, JSObject* aObj);
+-
+   void
+   GetScope(nsString& aScope) const
+   {
+     aScope = mScope;
+   }
+ 
+   workers::ServiceWorkerClients*
+   Clients();

+ 856 - 0
mozilla-release/patches/1293277-4-59a1.patch

@@ -0,0 +1,856 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513111487 18000
+# Node ID f7b0cd514d46f242222aa50e498b85f89b7e668a
+# Parent  3f8fc0abb71072f9b999a6e7beda7db3a58a2f7d
+Bug 1293277 P4 Add Client and Clients DOM classes, but don't hook them into bindings yet. r=baku
+
+diff --git a/dom/clients/api/Client.cpp b/dom/clients/api/Client.cpp
+new file mode 100644
+--- /dev/null
++++ b/dom/clients/api/Client.cpp
+@@ -0,0 +1,249 @@
++/* -*- 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 "Client.h"
++
++#include "ClientDOMUtil.h"
++#include "mozilla/dom/ClientHandle.h"
++#include "mozilla/dom/ClientIPCTypes.h"
++#include "mozilla/dom/ClientManager.h"
++#include "mozilla/dom/ClientState.h"
++#include "mozilla/dom/Promise.h"
++#include "mozilla/dom/WorkerPrivate.h"
++#include "mozilla/dom/WorkerScope.h"
++#include "nsIGlobalObject.h"
++
++namespace mozilla {
++namespace dom {
++
++using mozilla::dom::workers::Closing;
++using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
++using mozilla::dom::workers::WorkerHolderToken;
++using mozilla::dom::workers::WorkerPrivate;
++using mozilla::dom::ipc::StructuredCloneData;
++
++NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla::dom::Client);
++NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::dom::Client);
++NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(mozilla::dom::Client, mGlobal);
++
++NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(mozilla::dom::Client)
++  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
++  NS_INTERFACE_MAP_ENTRY(nsISupports)
++NS_INTERFACE_MAP_END
++
++void
++Client::EnsureHandle()
++{
++  NS_ASSERT_OWNINGTHREAD(mozilla::dom::Client);
++  if (!mHandle) {
++    mHandle = ClientManager::CreateHandle(ClientInfo(mData->info()),
++                                          mGlobal->EventTargetFor(TaskCategory::Other));
++  }
++}
++
++Client::Client(nsIGlobalObject* aGlobal, const ClientInfoAndState& aData)
++  : mGlobal(aGlobal)
++  , mData(MakeUnique<ClientInfoAndState>(aData))
++{
++  MOZ_DIAGNOSTIC_ASSERT(mGlobal);
++}
++
++TimeStamp
++Client::CreationTime() const
++{
++  return mData->info().creationTime();
++}
++
++TimeStamp
++Client::LastFocusTime() const
++{
++  if (mData->info().type() != ClientType::Window) {
++    return TimeStamp();
++  }
++  return mData->state().get_IPCClientWindowState().lastFocusTime();
++}
++
++nsContentUtils::StorageAccess
++Client::GetStorageAccess() const
++{
++  ClientState state(ClientState::FromIPC(mData->state()));
++  return state.GetStorageAccess();
++}
++
++JSObject*
++Client::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
++{
++#if 0
++  // TODO: Enable when bindings are updated to point to this class.
++  if (mData->info().type() == ClientType::Window) {
++    return WindowClientBinding::Wrap(aCx, this, aGivenProto);
++  }
++  return ClientBinding::Wrap(aCx, this, aGivenProto);
++#endif
++  return nullptr;
++}
++
++nsIGlobalObject*
++Client::GetParentObject() const
++{
++  return mGlobal;
++}
++
++void
++Client::GetUrl(nsAString& aUrlOut) const
++{
++  CopyUTF8toUTF16(mData->info().url(), aUrlOut);
++}
++
++void
++Client::GetId(nsAString& aIdOut) const
++{
++  char buf[NSID_LENGTH];
++  mData->info().id().ToProvidedString(buf);
++  NS_ConvertASCIItoUTF16 uuid(buf);
++
++  // Remove {} and the null terminator
++  aIdOut.Assign(Substring(uuid, 1, NSID_LENGTH - 3));
++}
++
++ClientType
++Client::Type() const
++{
++  return mData->info().type();
++}
++
++FrameType
++Client::GetFrameType() const
++{
++  return mData->info().frameType();
++}
++
++void
++Client::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
++                    const Sequence<JSObject*>& aTransferable,
++                    ErrorResult& aRv)
++{
++  MOZ_ASSERT(!NS_IsMainThread());
++  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate->IsServiceWorker());
++  workerPrivate->AssertIsOnWorkerThread();
++
++  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
++  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
++                                                          &transferable);
++  if (aRv.Failed()) {
++    return;
++  }
++
++  StructuredCloneData data;
++  data.Write(aCx, aMessage, transferable, aRv);
++  if (aRv.Failed()) {
++    return;
++  }
++
++  EnsureHandle();
++  mHandle->PostMessage(data, workerPrivate->GetServiceWorkerDescriptor());
++}
++
++VisibilityState
++Client::GetVisibilityState() const
++{
++  return mData->state().get_IPCClientWindowState().visibilityState();
++}
++
++bool
++Client::Focused() const
++{
++  return mData->state().get_IPCClientWindowState().focused();
++}
++
++already_AddRefed<Promise>
++Client::Focus(ErrorResult& aRv)
++{
++  MOZ_ASSERT(!NS_IsMainThread());
++  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate->IsServiceWorker());
++  workerPrivate->AssertIsOnWorkerThread();
++
++  RefPtr<Promise> outerPromise = Promise::Create(mGlobal, aRv);
++  if (aRv.Failed()) {
++    return outerPromise.forget();
++  }
++
++  if (!workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
++    outerPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
++    return outerPromise.forget();
++  }
++
++  // Hold the worker thread alive while we perform the async operation
++  // and also avoid invoking callbacks if the worker starts shutting
++  // down.
++  RefPtr<WorkerHolderToken> token =
++    WorkerHolderToken::Create(GetCurrentThreadWorkerPrivate(), Closing);
++
++  EnsureHandle();
++  RefPtr<ClientStatePromise> innerPromise = mHandle->Focus();
++  RefPtr<Client> self = this;
++
++  innerPromise->Then(mGlobal->EventTargetFor(TaskCategory::Other), __func__,
++    [self, token, outerPromise] (const ClientState& aResult) {
++      if (token->IsShuttingDown()) {
++        return;
++      }
++      RefPtr<Client> newClient =
++        new Client(self->mGlobal, ClientInfoAndState(self->mData->info(), aResult.ToIPC()));
++      outerPromise->MaybeResolve(newClient);
++    }, [self, token, outerPromise] (nsresult aResult) {
++      if (token->IsShuttingDown()) {
++        return;
++      }
++      outerPromise->MaybeReject(aResult);
++    });
++
++  return outerPromise.forget();
++}
++
++already_AddRefed<Promise>
++Client::Navigate(const nsAString& aURL, ErrorResult& aRv)
++{
++  MOZ_ASSERT(!NS_IsMainThread());
++  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate->IsServiceWorker());
++  workerPrivate->AssertIsOnWorkerThread();
++
++  RefPtr<Promise> outerPromise = Promise::Create(mGlobal, aRv);
++  if (aRv.Failed()) {
++    return outerPromise.forget();
++  }
++
++  ClientNavigateArgs args(mData->info(), NS_ConvertUTF16toUTF8(aURL),
++                          workerPrivate->GetLocationInfo().mHref);
++  RefPtr<Client> self = this;
++
++  StartClientManagerOp(&ClientManager::Navigate, args,
++    mGlobal->EventTargetFor(TaskCategory::Other),
++    [self, outerPromise] (const ClientOpResult& aResult) {
++      if (aResult.type() != ClientOpResult::TClientInfoAndState) {
++        outerPromise->MaybeResolve(JS::NullHandleValue);
++        return;
++      }
++      RefPtr<Client> newClient =
++        new Client(self->mGlobal, aResult.get_ClientInfoAndState());
++      outerPromise->MaybeResolve(newClient);
++    }, [self, outerPromise] (nsresult aResult) {
++      // TODO: Improve this error in bug 1412856.  Ideally we should throw
++      //       the TypeError in the child process and pass it back to here.
++      outerPromise->MaybeReject(NS_ERROR_TYPE_ERR);
++    });
++
++  return outerPromise.forget();
++}
++
++} // namespace dom
++} // namespace mozilla
+diff --git a/dom/clients/api/Client.h b/dom/clients/api/Client.h
+new file mode 100644
+--- /dev/null
++++ b/dom/clients/api/Client.h
+@@ -0,0 +1,99 @@
++/* -*- 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 _mozilla_dom_Client_h
++#define _mozilla_dom_Client_h
++
++#include "mozilla/dom/ClientBinding.h"
++#include "nsCOMPtr.h"
++#include "nsContentUtils.h"
++#include "nsISupports.h"
++#include "nsWrapperCache.h"
++
++class nsIGlobalObject;
++
++namespace mozilla {
++
++class ErrorResult;
++
++namespace dom {
++
++class ClientHandle;
++class ClientInfoAndState;
++class Promise;
++
++template <typename t> class Sequence;
++
++class Client final : public nsISupports
++                   , public nsWrapperCache
++{
++  nsCOMPtr<nsIGlobalObject> mGlobal;
++  UniquePtr<ClientInfoAndState> mData;
++  RefPtr<ClientHandle> mHandle;
++
++  ~Client() = default;
++
++  void
++  EnsureHandle();
++
++public:
++  Client(nsIGlobalObject* aGlobal, const ClientInfoAndState& aData);
++
++  TimeStamp
++  CreationTime() const;
++
++  TimeStamp
++  LastFocusTime() const;
++
++  nsContentUtils::StorageAccess
++  GetStorageAccess() const;
++
++  // nsWrapperCache interface methods
++  JSObject*
++  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
++
++  // DOM bindings methods
++  nsIGlobalObject*
++  GetParentObject() const;
++
++  // Client Bindings
++  void
++  GetUrl(nsAString& aUrlOut) const;
++
++  void
++  GetId(nsAString& aIdOut) const;
++
++  ClientType
++  Type() const;
++
++  FrameType
++  GetFrameType() const;
++
++  // WindowClient bindings
++  VisibilityState
++  GetVisibilityState() const;
++
++  bool
++  Focused() const;
++
++  already_AddRefed<Promise>
++  Focus(ErrorResult& aRv);
++
++  already_AddRefed<Promise>
++  Navigate(const nsAString& aURL, ErrorResult& aRv);
++
++  void
++  PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
++              const Sequence<JSObject*>& aTransferrable,
++              ErrorResult& aRv);
++
++  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
++  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(mozilla::dom::Client)
++};
++
++} // namespace dom
++} // namespace mozilla
++
++#endif // _mozilla_dom_Client_h
+diff --git a/dom/clients/api/ClientDOMUtil.h b/dom/clients/api/ClientDOMUtil.h
+new file mode 100644
+--- /dev/null
++++ b/dom/clients/api/ClientDOMUtil.h
+@@ -0,0 +1,54 @@
++/* -*- 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 _mozilla_dom_ClientDOMUtil_h
++#define _mozilla_dom_ClientDOMUtil_h
++
++#include "mozilla/dom/ClientIPCTypes.h"
++#include "mozilla/dom/ClientOpPromise.h"
++#include "mozilla/dom/WorkerPrivate.h"
++#include "mozilla/dom/workers/bindings/WorkerHolderToken.h"
++
++class nsIGlobalObject;
++
++namespace mozilla {
++namespace dom {
++
++// Utility method to properly execute a ClientManager operation.  It
++// will properly hold a worker thread alive and avoid executing callbacks
++// if the thread is shutting down.
++template<typename Func, typename Arg, typename Resolve, typename Reject>
++void
++StartClientManagerOp(Func aFunc, const Arg& aArg, nsISerialEventTarget* aTarget,
++                     Resolve aResolve, Reject aReject)
++{
++  using mozilla::dom::workers::Closing;
++  using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
++  using mozilla::dom::workers::WorkerHolderToken;
++
++  RefPtr<WorkerHolderToken> token;
++  if (!NS_IsMainThread()) {
++    token = WorkerHolderToken::Create(GetCurrentThreadWorkerPrivate(), Closing);
++  }
++
++  RefPtr<ClientOpPromise> promise = aFunc(aArg, aTarget);
++  promise->Then(aTarget, __func__,
++    [aResolve, token](const ClientOpResult& aResult) {
++      if (token && token->IsShuttingDown()) {
++        return;
++      }
++      aResolve(aResult);
++    }, [aReject, token](nsresult aResult) {
++      if (token && token->IsShuttingDown()) {
++        return;
++      }
++      aReject(aResult);
++    });
++}
++
++} // namespace dom
++} // namespace mozilla
++
++#endif // _mozilla_dom_ClientDOMUtil_h
+diff --git a/dom/clients/api/Clients.cpp b/dom/clients/api/Clients.cpp
+new file mode 100644
+--- /dev/null
++++ b/dom/clients/api/Clients.cpp
+@@ -0,0 +1,289 @@
++/* -*- 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 "Clients.h"
++
++#include "ClientDOMUtil.h"
++#include "mozilla/dom/ClientIPCTypes.h"
++#include "mozilla/dom/ClientManager.h"
++#include "mozilla/dom/ClientsBinding.h"
++#include "mozilla/dom/Promise.h"
++#include "mozilla/dom/ServiceWorkerDescriptor.h"
++#include "mozilla/dom/WorkerPrivate.h"
++#include "mozilla/dom/workers/ServiceWorkerManager.h"
++#include "mozilla/SystemGroup.h"
++#include "nsIGlobalObject.h"
++#include "nsString.h"
++
++namespace mozilla {
++namespace dom {
++
++using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
++using mozilla::dom::workers::WorkerPrivate;
++using mozilla::dom::workers::ServiceWorkerManager;
++using mozilla::ipc::PrincipalInfo;
++
++NS_IMPL_CYCLE_COLLECTING_ADDREF(Clients);
++NS_IMPL_CYCLE_COLLECTING_RELEASE(Clients);
++NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Clients, mGlobal);
++
++NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Clients)
++  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
++  NS_INTERFACE_MAP_ENTRY(nsISupports)
++NS_INTERFACE_MAP_END
++
++Clients::Clients(nsIGlobalObject* aGlobal)
++  : mGlobal(aGlobal)
++{
++  MOZ_DIAGNOSTIC_ASSERT(mGlobal);
++}
++
++JSObject*
++Clients::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
++{
++  // TODO: Enable when bindings are updated to point to this class.
++#if 0
++  return ClientsBinding::Wrap(aCx, this, aGivenProto);
++#endif
++  return nullptr;
++}
++
++nsIGlobalObject*
++Clients::GetParentObject() const
++{
++  return mGlobal;
++}
++
++already_AddRefed<Promise>
++Clients::Get(const nsAString& aClientID, ErrorResult& aRv)
++{
++  MOZ_ASSERT(!NS_IsMainThread());
++  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate->IsServiceWorker());
++  workerPrivate->AssertIsOnWorkerThread();
++
++  RefPtr<Promise> outerPromise = Promise::Create(mGlobal, aRv);
++  if (aRv.Failed()) {
++    return outerPromise.forget();
++  }
++
++  nsID id;
++  if (!id.Parse(NS_ConvertUTF16toUTF8(aClientID).get())) {
++    // Invalid ID means we will definitely not find a match, so just
++    // resolve with undefined indicating "not found".
++    outerPromise->MaybeResolveWithUndefined();
++    return outerPromise.forget();
++  }
++
++  const PrincipalInfo& principalInfo = workerPrivate->GetPrincipalInfo();
++  nsCOMPtr<nsISerialEventTarget> target =
++    mGlobal->EventTargetFor(TaskCategory::Other);
++
++  RefPtr<ClientOpPromise> innerPromise =
++    ClientManager::GetInfoAndState(ClientGetInfoAndStateArgs(id, principalInfo),
++                                   target);
++
++  nsCOMPtr<nsIGlobalObject> global = mGlobal;
++  nsCString scope = workerPrivate->ServiceWorkerScope();
++
++  innerPromise->Then(target, __func__,
++    [outerPromise, global, scope] (const ClientOpResult& aResult) {
++      RefPtr<Client> client = new Client(global, aResult.get_ClientInfoAndState());
++      if (client->GetStorageAccess() == nsContentUtils::StorageAccess::eAllow) {
++        outerPromise->MaybeResolve(Move(client));
++        return;
++      }
++      nsCOMPtr<nsIRunnable> r =
++        NS_NewRunnableFunction("Clients::MatchAll() storage denied",
++        [scope] {
++          ServiceWorkerManager::LocalizeAndReportToAllClients(
++            scope, "ServiceWorkerGetClientStorageError", nsTArray<nsString>());
++        });
++      SystemGroup::Dispatch(TaskCategory::Other, r.forget());
++      outerPromise->MaybeResolveWithUndefined();
++    }, [outerPromise] (nsresult aResult) {
++      outerPromise->MaybeResolveWithUndefined();
++    });
++
++  return outerPromise.forget();
++}
++
++namespace {
++
++class MatchAllComparator final
++{
++public:
++  bool
++  LessThan(Client* aLeft, Client* aRight) const
++  {
++    TimeStamp leftFocusTime = aLeft->LastFocusTime();
++    TimeStamp rightFocusTime = aRight->LastFocusTime();
++    // If the focus times are the same, then default to creation order.
++    // MatchAll should return oldest Clients first.
++    if (leftFocusTime == rightFocusTime) {
++      return aLeft->CreationTime() < aRight->CreationTime();
++    }
++
++    // Otherwise compare focus times.  We reverse the logic here so
++    // that the most recently focused window is first in the list.
++    if (!leftFocusTime.IsNull() && rightFocusTime.IsNull()) {
++      return true;
++    }
++    if (leftFocusTime.IsNull() && !rightFocusTime.IsNull()) {
++      return false;
++    }
++    return leftFocusTime > rightFocusTime;
++  }
++
++  bool
++  Equals(Client* aLeft, Client* aRight) const
++  {
++    return aLeft->LastFocusTime() == aRight->LastFocusTime() &&
++           aLeft->CreationTime() == aRight->CreationTime();
++  }
++};
++
++} // anonymous namespace
++
++already_AddRefed<Promise>
++Clients::MatchAll(const ClientQueryOptions& aOptions, ErrorResult& aRv)
++{
++  MOZ_ASSERT(!NS_IsMainThread());
++  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate->IsServiceWorker());
++  workerPrivate->AssertIsOnWorkerThread();
++
++  RefPtr<Promise> outerPromise = Promise::Create(mGlobal, aRv);
++  if (aRv.Failed()) {
++    return outerPromise.forget();
++  }
++
++  nsCOMPtr<nsIGlobalObject> global = mGlobal;
++  nsCString scope = workerPrivate->ServiceWorkerScope();
++
++  ClientMatchAllArgs args(workerPrivate->GetServiceWorkerDescriptor().ToIPC(),
++                          aOptions.mType,
++                          aOptions.mIncludeUncontrolled);
++  StartClientManagerOp(&ClientManager::MatchAll, args,
++    mGlobal->EventTargetFor(TaskCategory::Other),
++    [outerPromise, global, scope] (const ClientOpResult& aResult) {
++      nsTArray<RefPtr<Client>> clientList;
++      bool storageDenied = false;
++      for (const ClientInfoAndState& value : aResult.get_ClientList().values()) {
++        RefPtr<Client> client = new Client(global, value);
++        if (client->GetStorageAccess() != nsContentUtils::StorageAccess::eAllow) {
++          storageDenied = true;
++          continue;
++        }
++        clientList.AppendElement(Move(client));
++      }
++      if (storageDenied) {
++        nsCOMPtr<nsIRunnable> r =
++          NS_NewRunnableFunction("Clients::MatchAll() storage denied",
++          [scope] {
++            ServiceWorkerManager::LocalizeAndReportToAllClients(
++              scope, "ServiceWorkerGetClientStorageError", nsTArray<nsString>());
++          });
++        SystemGroup::Dispatch(TaskCategory::Other, r.forget());
++      }
++      clientList.Sort(MatchAllComparator());
++      outerPromise->MaybeResolve(clientList);
++    }, [outerPromise] (nsresult aResult) {
++      outerPromise->MaybeReject(aResult);
++    });
++
++  return outerPromise.forget();
++}
++
++already_AddRefed<Promise>
++Clients::OpenWindow(const nsAString& aURL, ErrorResult& aRv)
++{
++  MOZ_ASSERT(!NS_IsMainThread());
++  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate->IsServiceWorker());
++  workerPrivate->AssertIsOnWorkerThread();
++
++  RefPtr<Promise> outerPromise = Promise::Create(mGlobal, aRv);
++  if (aRv.Failed()) {
++    return outerPromise.forget();
++  }
++
++  if (aURL.EqualsLiteral("about:blank")) {
++    // TODO: Improve this error in bug 1412856.
++    outerPromise->MaybeReject(NS_ERROR_DOM_TYPE_ERR);
++    return outerPromise.forget();
++  }
++
++  if (!workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
++    outerPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
++    return outerPromise.forget();
++  }
++
++  const PrincipalInfo& principalInfo = workerPrivate->GetPrincipalInfo();
++  nsCString baseURL = workerPrivate->GetLocationInfo().mHref;
++  ClientOpenWindowArgs args(principalInfo, NS_ConvertUTF16toUTF8(aURL),
++                            baseURL);
++
++  nsCOMPtr<nsIGlobalObject> global = mGlobal;
++
++  StartClientManagerOp(&ClientManager::OpenWindow, args,
++    mGlobal->EventTargetFor(TaskCategory::Other),
++    [outerPromise, global] (const ClientOpResult& aResult) {
++      if (aResult.type() != ClientOpResult::TClientInfoAndState) {
++        outerPromise->MaybeResolve(JS::NullHandleValue);
++        return;
++      }
++      RefPtr<Client> client =
++        new Client(global, aResult.get_ClientInfoAndState());
++      outerPromise->MaybeResolve(client);
++    }, [outerPromise] (nsresult aResult) {
++      // TODO: Improve this error in bug 1412856.  Ideally we should throw
++      //       the TypeError in the child process and pass it back to here.
++      outerPromise->MaybeReject(NS_ERROR_TYPE_ERR);
++    });
++
++  return outerPromise.forget();
++}
++
++already_AddRefed<Promise>
++Clients::Claim(ErrorResult& aRv)
++{
++  MOZ_ASSERT(!NS_IsMainThread());
++  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
++  MOZ_DIAGNOSTIC_ASSERT(workerPrivate->IsServiceWorker());
++  workerPrivate->AssertIsOnWorkerThread();
++
++  RefPtr<Promise> outerPromise = Promise::Create(mGlobal, aRv);
++  if (aRv.Failed()) {
++    return outerPromise.forget();
++  }
++
++  const ServiceWorkerDescriptor& serviceWorker =
++    workerPrivate->GetServiceWorkerDescriptor();
++
++  if (serviceWorker.State() != ServiceWorkerState::Activating &&
++      serviceWorker.State() != ServiceWorkerState::Activated) {
++    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
++    return outerPromise.forget();
++  }
++
++  StartClientManagerOp(&ClientManager::Claim, ClientClaimArgs(serviceWorker.ToIPC()),
++    mGlobal->EventTargetFor(TaskCategory::Other),
++    [outerPromise] (const ClientOpResult& aResult) {
++      outerPromise->MaybeResolveWithUndefined();
++    }, [outerPromise] (nsresult aResult) {
++      outerPromise->MaybeReject(aResult);
++    });
++
++  return outerPromise.forget();
++}
++
++} // namespace dom
++} // namespace mozilla
+diff --git a/dom/clients/api/Clients.h b/dom/clients/api/Clients.h
+new file mode 100644
+--- /dev/null
++++ b/dom/clients/api/Clients.h
+@@ -0,0 +1,61 @@
++/* -*- 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 _mozilla_dom_Clients_h
++#define _mozilla_dom_Clients_h
++
++#include "nsCOMPtr.h"
++#include "nsISupports.h"
++#include "nsWrapperCache.h"
++
++class nsIGlobalObject;
++
++namespace mozilla {
++
++class ErrorResult;
++
++namespace dom {
++
++struct ClientQueryOptions;
++class Promise;
++
++class Clients final : public nsISupports
++                    , public nsWrapperCache
++{
++  nsCOMPtr<nsIGlobalObject> mGlobal;
++
++  ~Clients() = default;
++
++public:
++  explicit Clients(nsIGlobalObject* aGlobal);
++
++  // nsWrapperCache interface methods
++  JSObject*
++  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
++
++  // DOM bindings methods
++  nsIGlobalObject*
++  GetParentObject() const;
++
++  already_AddRefed<Promise>
++  Get(const nsAString& aClientID, ErrorResult& aRv);
++
++  already_AddRefed<Promise>
++  MatchAll(const ClientQueryOptions& aOptions, ErrorResult& aRv);
++
++  already_AddRefed<Promise>
++  OpenWindow(const nsAString& aURL, ErrorResult& aRv);
++
++  already_AddRefed<Promise>
++  Claim(ErrorResult& aRv);
++
++  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
++  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Clients)
++};
++
++} // namespace dom
++} // namespace mozilla
++
++#endif // _mozilla_dom_Clients_h
+diff --git a/dom/clients/api/moz.build b/dom/clients/api/moz.build
+new file mode 100644
+--- /dev/null
++++ b/dom/clients/api/moz.build
+@@ -0,0 +1,32 @@
++# -*- 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/.
++
++EXPORTS.mozilla.dom += [
++  'Client.h',
++  'Clients.h',
++]
++
++UNIFIED_SOURCES += [
++  'Client.cpp',
++  'Clients.cpp',
++]
++
++include('/ipc/chromium/chromium-config.mozbuild')
++
++LOCAL_INCLUDES += [
++  '/dom/workers',
++]
++
++FINAL_LIBRARY = 'xul'
++
++MOCHITEST_MANIFESTS += [
++]
++
++BROWSER_CHROME_MANIFESTS += [
++]
++
++XPCSHELL_TESTS_MANIFESTS += [
++]
+diff --git a/dom/clients/moz.build b/dom/clients/moz.build
+--- a/dom/clients/moz.build
++++ b/dom/clients/moz.build
+@@ -1,9 +1,10 @@
+ # -*- 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 += [
++  'api',
+   'manager',
+ ]
+diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp
+--- a/dom/indexedDB/ActorsParent.cpp
++++ b/dom/indexedDB/ActorsParent.cpp
+@@ -134,16 +134,17 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLAT
+                                           PRFileDesc,
+                                           PR_Close);
+ 
+ namespace dom {
+ namespace indexedDB {
+ 
+ using namespace mozilla::dom::quota;
+ using namespace mozilla::ipc;
++using mozilla::dom::quota::Client;
+ 
+ namespace {
+ 
+ class ConnectionPool;
+ class Cursor;
+ class Database;
+ struct DatabaseActorInfo;
+ class DatabaseFile;

+ 3110 - 0
mozilla-release/patches/1293277-5-59a1.patch

@@ -0,0 +1,3110 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513111487 18000
+# Node ID 63938d2d5492022b7d3808d11a87faf83742ed6b
+# Parent  326a1ff52d1ac22dbd066731eb3b326f490d74d5
+Bug 1293277 P5 Switch bindings over to new Client and Clients classes. r=baku
+
+diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf
+--- a/dom/bindings/Bindings.conf
++++ b/dom/bindings/Bindings.conf
+@@ -148,26 +148,16 @@ DOMInterfaces = {
+     'concrete': False,
+ },
+ 
+ 'ChromeWorker': {
+     'headerFile': 'mozilla/dom/WorkerPrivate.h',
+     'nativeType': 'mozilla::dom::workers::ChromeWorkerPrivate',
+ },
+ 
+-'Client': {
+-    'nativeType': 'mozilla::dom::workers::ServiceWorkerClient',
+-    'headerFile': 'mozilla/dom/workers/bindings/ServiceWorkerClient.h',
+-},
+-
+-'Clients': {
+-    'nativeType': 'mozilla::dom::workers::ServiceWorkerClients',
+-    'headerFile': 'mozilla/dom/workers/bindings/ServiceWorkerClients.h',
+-},
+-
+ 'console': {
+     'nativeType': 'mozilla::dom::Console',
+ },
+ 
+ 'ConvolverNode': {
+     'implicitJSContext': [ 'buffer' ],
+ },
+ 
+@@ -1116,18 +1106,17 @@ DOMInterfaces = {
+     'nativeType': 'mozilla::extensions::WebExtensionContentScript',
+ },
+ 
+ 'WebExtensionPolicy': {
+     'nativeType': 'mozilla::extensions::WebExtensionPolicy',
+ },
+ 
+ 'WindowClient': {
+-    'nativeType': 'mozilla::dom::workers::ServiceWorkerWindowClient',
+-    'headerFile': 'mozilla/dom/workers/bindings/ServiceWorkerWindowClient.h',
++    'nativeType': 'mozilla::dom::Client',
+ },
+ 
+ 'WebGLActiveInfo': {
+     'nativeType': 'mozilla::WebGLActiveInfo',
+     'headerFile': 'WebGLActiveInfo.h'
+ },
+ 
+ 'WebGLBuffer': {
+diff --git a/dom/clients/api/Client.cpp b/dom/clients/api/Client.cpp
+--- a/dom/clients/api/Client.cpp
++++ b/dom/clients/api/Client.cpp
+@@ -71,24 +71,20 @@ Client::GetStorageAccess() const
+ {
+   ClientState state(ClientState::FromIPC(mData->state()));
+   return state.GetStorageAccess();
+ }
+ 
+ JSObject*
+ Client::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+ {
+-#if 0
+-  // TODO: Enable when bindings are updated to point to this class.
+   if (mData->info().type() == ClientType::Window) {
+     return WindowClientBinding::Wrap(aCx, this, aGivenProto);
+   }
+   return ClientBinding::Wrap(aCx, this, aGivenProto);
+-#endif
+-  return nullptr;
+ }
+ 
+ nsIGlobalObject*
+ Client::GetParentObject() const
+ {
+   return mGlobal;
+ }
+ 
+diff --git a/dom/clients/api/Clients.cpp b/dom/clients/api/Clients.cpp
+--- a/dom/clients/api/Clients.cpp
++++ b/dom/clients/api/Clients.cpp
+@@ -39,21 +39,17 @@ Clients::Clients(nsIGlobalObject* aGloba
+   : mGlobal(aGlobal)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(mGlobal);
+ }
+ 
+ JSObject*
+ Clients::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+ {
+-  // TODO: Enable when bindings are updated to point to this class.
+-#if 0
+   return ClientsBinding::Wrap(aCx, this, aGivenProto);
+-#endif
+-  return nullptr;
+ }
+ 
+ nsIGlobalObject*
+ Clients::GetParentObject() const
+ {
+   return mGlobal;
+ }
+ 
+diff --git a/dom/webidl/Client.webidl b/dom/webidl/Client.webidl
+--- a/dom/webidl/Client.webidl
++++ b/dom/webidl/Client.webidl
+@@ -8,30 +8,32 @@
+  *
+  */
+ 
+ [Exposed=ServiceWorker]
+ interface Client {
+   readonly attribute USVString url;
+ 
+   // Remove frameType in bug 1290936
++  [BinaryName="GetFrameType"]
+   readonly attribute FrameType frameType;
+ 
+   readonly attribute ClientType type;
+   readonly attribute DOMString id;
+ 
+   // Implement reserved in bug 1264177
+   // readonly attribute boolean reserved;
+ 
+   [Throws]
+   void postMessage(any message, optional sequence<object> transfer = []);
+ };
+ 
+ [Exposed=ServiceWorker]
+ interface WindowClient : Client {
++  [BinaryName="GetVisibilityState"]
+   readonly attribute VisibilityState visibilityState;
+   readonly attribute boolean focused;
+ 
+   // Implement ancestorOrigins in bug 1264180
+   // [SameObject] readonly attribute FrozenArray<USVString> ancestorOrigins;
+ 
+   [Throws, NewObject]
+   Promise<WindowClient> focus();
+diff --git a/dom/webidl/ServiceWorkerGlobalScope.webidl b/dom/webidl/ServiceWorkerGlobalScope.webidl
+--- a/dom/webidl/ServiceWorkerGlobalScope.webidl
++++ b/dom/webidl/ServiceWorkerGlobalScope.webidl
+@@ -10,17 +10,18 @@
+  *
+  * You are granted a license to use, reproduce and create derivative works of
+  * this document.
+  */
+ 
+ [Global=(Worker,ServiceWorker),
+  Exposed=ServiceWorker]
+ interface ServiceWorkerGlobalScope : WorkerGlobalScope {
+-  [SameObject] readonly attribute Clients clients;
++  [SameObject, BinaryName="GetClients"]
++  readonly attribute Clients clients;
+   [SameObject] readonly attribute ServiceWorkerRegistration registration;
+ 
+   [Throws, NewObject]
+   Promise<void> skipWaiting();
+ 
+   attribute EventHandler oninstall;
+   attribute EventHandler onactivate;
+ 
+diff --git a/dom/workers/ServiceWorker.cpp b/dom/workers/ServiceWorker.cpp
+--- a/dom/workers/ServiceWorker.cpp
++++ b/dom/workers/ServiceWorker.cpp
+@@ -3,22 +3,23 @@
+ /* 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 "ServiceWorker.h"
+ 
+ #include "nsIDocument.h"
+ #include "nsPIDOMWindow.h"
+-#include "ServiceWorkerClient.h"
+ #include "ServiceWorkerManager.h"
+ #include "ServiceWorkerPrivate.h"
+ #include "WorkerPrivate.h"
+ 
+ #include "mozilla/Preferences.h"
++#include "mozilla/dom/ClientIPCTypes.h"
++#include "mozilla/dom/ClientState.h"
+ #include "mozilla/dom/Promise.h"
+ #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
+ 
+ #ifdef XP_WIN
+ #undef PostMessage
+ #endif
+ 
+ using mozilla::ErrorResult;
+@@ -97,16 +98,24 @@ ServiceWorker::PostMessage(JSContext* aC
+   if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
+     ServiceWorkerManager::LocalizeAndReportToAllClients(
+       mInfo->Scope(), "ServiceWorkerPostMessageStorageError",
+       nsTArray<nsString> { NS_ConvertUTF8toUTF16(mInfo->Scope()) });
+     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+     return;
+   }
+ 
+-  UniquePtr<ServiceWorkerClientInfo> clientInfo(new ServiceWorkerClientInfo(window->GetExtantDoc()));
++  Maybe<ClientInfo> clientInfo = window->GetClientInfo();
++  Maybe<ClientState> clientState = window->GetClientState();
++  if (clientInfo.isNothing() || clientState.isNothing()) {
++    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
++    return;
++  }
++
+   ServiceWorkerPrivate* workerPrivate = mInfo->WorkerPrivate();
+-  aRv = workerPrivate->SendMessageEvent(aCx, aMessage, aTransferable, Move(clientInfo));
++  aRv = workerPrivate->SendMessageEvent(aCx, aMessage, aTransferable,
++                                        ClientInfoAndState(clientInfo.ref().ToIPC(),
++                                                           clientState.ref().ToIPC()));
+ }
+ 
+ } // namespace workers
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/workers/ServiceWorkerClient.cpp b/dom/workers/ServiceWorkerClient.cpp
+deleted file mode 100644
+--- a/dom/workers/ServiceWorkerClient.cpp
++++ /dev/null
+@@ -1,286 +0,0 @@
+-/* -*- 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 "ServiceWorkerClient.h"
+-#include "ServiceWorkerContainer.h"
+-
+-#include "mozilla/dom/MessageEvent.h"
+-#include "mozilla/dom/Navigator.h"
+-#include "nsGlobalWindow.h"
+-#include "nsIBrowserDOMWindow.h"
+-#include "nsIDocument.h"
+-#include "ServiceWorker.h"
+-#include "ServiceWorkerPrivate.h"
+-#include "WorkerPrivate.h"
+-
+-using namespace mozilla;
+-using namespace mozilla::dom;
+-using namespace mozilla::dom::workers;
+-
+-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ServiceWorkerClient, mOwner)
+-
+-NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClient)
+-NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClient)
+-
+-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerClient)
+-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+-  NS_INTERFACE_MAP_ENTRY(nsISupports)
+-NS_INTERFACE_MAP_END
+-
+-ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc, uint32_t aOrdinal)
+-  : mType(ClientType::Window)
+-  , mOrdinal(aOrdinal)
+-  , mWindowId(0)
+-  , mFrameType(FrameType::None)
+-{
+-  MOZ_ASSERT(aDoc);
+-  nsresult rv = aDoc->GetOrCreateId(mClientId);
+-  if (NS_FAILED(rv)) {
+-    NS_WARNING("Failed to get the UUID of the document.");
+-  }
+-
+-  RefPtr<nsGlobalWindow> innerWindow = nsGlobalWindow::Cast(aDoc->GetInnerWindow());
+-  if (innerWindow) {
+-    // XXXcatalinb: The inner window can be null if the document is navigating
+-    // and was detached.
+-    mWindowId = innerWindow->WindowID();
+-  }
+-
+-  nsCOMPtr<nsIURI> originalURI = aDoc->GetOriginalURI();
+-  if (originalURI) {
+-    nsAutoCString spec;
+-    originalURI->GetSpec(spec);
+-    CopyUTF8toUTF16(spec, mUrl);
+-  }
+-  mVisibilityState = aDoc->VisibilityState();
+-
+-  mLastFocusTime = aDoc->LastFocusTime();
+-
+-  ErrorResult result;
+-  mFocused = aDoc->HasFocus(result);
+-  if (result.Failed()) {
+-    NS_WARNING("Failed to get focus information.");
+-  }
+-
+-  MOZ_ASSERT_IF(mLastFocusTime.IsNull(), !mFocused);
+-  MOZ_ASSERT_IF(mFocused, !mLastFocusTime.IsNull());
+-
+-  RefPtr<nsGlobalWindow> outerWindow = nsGlobalWindow::Cast(aDoc->GetWindow());
+-  if (!outerWindow) {
+-    MOZ_ASSERT(mFrameType == FrameType::None);
+-  } else if (!outerWindow->IsTopLevelWindow()) {
+-    mFrameType = FrameType::Nested;
+-  } else if (outerWindow->HadOriginalOpener()) {
+-    mFrameType = FrameType::Auxiliary;
+-  } else {
+-    mFrameType = FrameType::Top_level;
+-  }
+-}
+-
+-bool
+-ServiceWorkerClientInfo::operator<(const ServiceWorkerClientInfo& aRight) const
+-{
+-  // Note: the mLastFocusTime comparisons are reversed because we need to
+-  // put most recently focused values first.  The mOrdinal comparison is
+-  // normal, though, because otherwise we want normal creation order.
+-
+-  if (mLastFocusTime == aRight.mLastFocusTime) {
+-    return mOrdinal < aRight.mOrdinal;
+-  }
+-
+-  if (mLastFocusTime.IsNull()) {
+-    return false;
+-  }
+-
+-  if (aRight.mLastFocusTime.IsNull()) {
+-    return true;
+-  }
+-
+-  return mLastFocusTime > aRight.mLastFocusTime;
+-}
+-
+-bool
+-ServiceWorkerClientInfo::operator==(const ServiceWorkerClientInfo& aRight) const
+-{
+-  return mLastFocusTime == aRight.mLastFocusTime &&
+-         mOrdinal == aRight.mOrdinal &&
+-         mClientId == aRight.mClientId;
+-}
+-
+-JSObject*
+-ServiceWorkerClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+-{
+-  return ClientBinding::Wrap(aCx, this, aGivenProto);
+-}
+-
+-ClientType
+-ServiceWorkerClient::Type() const
+-{
+-  return mType;
+-}
+-
+-namespace {
+-
+-class ServiceWorkerClientPostMessageRunnable final
+-  : public Runnable
+-  , public StructuredCloneHolder
+-{
+-  const uint64_t mSourceID;
+-  const nsCString mSourceScope;
+-  const uint64_t mWindowId;
+-
+-public:
+-  ServiceWorkerClientPostMessageRunnable(uint64_t aSourceID,
+-                                         const nsACString& aSourceScope,
+-                                         uint64_t aWindowId)
+-    : mozilla::Runnable("ServiceWorkerClientPostMessageRunnable")
+-    , StructuredCloneHolder(CloningSupported,
+-                            TransferringSupported,
+-                            StructuredCloneScope::SameProcessDifferentThread)
+-    , mSourceID(aSourceID)
+-    , mSourceScope(aSourceScope)
+-    , mWindowId(aWindowId)
+-  {}
+-
+-  NS_IMETHOD
+-  Run() override
+-  {
+-    AssertIsOnMainThread();
+-    nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
+-    if (!window) {
+-      return NS_ERROR_FAILURE;
+-    }
+-
+-    dom::Navigator* navigator = window->Navigator();
+-    if (!navigator) {
+-      return NS_ERROR_FAILURE;
+-    }
+-
+-    RefPtr<ServiceWorkerContainer> container = navigator->ServiceWorker();
+-    AutoJSAPI jsapi;
+-    if (NS_WARN_IF(!jsapi.Init(window))) {
+-      return NS_ERROR_FAILURE;
+-    }
+-    JSContext* cx = jsapi.cx();
+-
+-    return DispatchDOMEvent(cx, window->AsInner(), container);
+-  }
+-
+-private:
+-  NS_IMETHOD
+-  DispatchDOMEvent(JSContext* aCx, nsPIDOMWindowInner* aWindow,
+-                   ServiceWorkerContainer* aTargetContainer)
+-  {
+-    AssertIsOnMainThread();
+-
+-    MOZ_ASSERT(aTargetContainer->GetParentObject(),
+-               "How come we don't have a window here?!");
+-
+-    JS::Rooted<JS::Value> messageData(aCx);
+-    ErrorResult rv;
+-    Read(aTargetContainer->GetParentObject(), aCx, &messageData, rv);
+-    if (NS_WARN_IF(rv.Failed())) {
+-      xpc::Throw(aCx, rv.StealNSResult());
+-      return NS_ERROR_FAILURE;
+-    }
+-
+-    RootedDictionary<MessageEventInit> init(aCx);
+-
+-    nsCOMPtr<nsIPrincipal> principal = aTargetContainer->GetParentObject()->PrincipalOrNull();
+-    NS_WARNING_ASSERTION(principal, "Why is the principal null here?");
+-
+-    bool isNullPrincipal = false;
+-    bool isSystemPrincipal = false;
+-    if (principal) {
+-      isNullPrincipal = principal->GetIsNullPrincipal();
+-      MOZ_ASSERT(!isNullPrincipal);
+-      isSystemPrincipal = principal->GetIsSystemPrincipal();
+-      MOZ_ASSERT(!isSystemPrincipal);
+-    }
+-
+-    init.mData = messageData;
+-    nsAutoCString origin;
+-    if (principal && !isNullPrincipal && !isSystemPrincipal) {
+-      principal->GetOrigin(origin);
+-    }
+-    init.mOrigin = NS_ConvertUTF8toUTF16(origin);
+-
+-
+-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+-    if (swm) {
+-      RefPtr<ServiceWorkerRegistrationInfo> reg =
+-        swm->GetRegistration(principal, mSourceScope);
+-      if (reg) {
+-        RefPtr<ServiceWorkerInfo> serviceWorker = reg->GetByID(mSourceID);
+-        if (serviceWorker) {
+-          init.mSource.SetValue().SetAsServiceWorker() =
+-            serviceWorker->GetOrCreateInstance(aWindow);
+-        }
+-      }
+-    }
+-
+-    if (!TakeTransferredPortsAsSequence(init.mPorts)) {
+-      return NS_ERROR_OUT_OF_MEMORY;
+-    }
+-
+-    RefPtr<MessageEvent> event =
+-      MessageEvent::Constructor(aTargetContainer, NS_LITERAL_STRING("message"),
+-                                init);
+-
+-    event->SetTrusted(true);
+-    bool status = false;
+-    aTargetContainer->DispatchEvent(static_cast<dom::Event*>(event.get()),
+-                                    &status);
+-
+-    if (!status) {
+-      return NS_ERROR_FAILURE;
+-    }
+-
+-    return NS_OK;
+-  }
+-};
+-
+-} // namespace
+-
+-void
+-ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+-                                 const Sequence<JSObject*>& aTransferable,
+-                                 ErrorResult& aRv)
+-{
+-  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+-  MOZ_ASSERT(workerPrivate);
+-  workerPrivate->AssertIsOnWorkerThread();
+-
+-  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
+-
+-  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
+-                                                          &transferable);
+-  if (NS_WARN_IF(aRv.Failed())) {
+-    return;
+-  }
+-
+-  // At the moment we only expose Client on ServiceWorker globals.
+-  MOZ_ASSERT(workerPrivate->IsServiceWorker());
+-  uint32_t serviceWorkerID = workerPrivate->ServiceWorkerID();
+-  nsCString scope = workerPrivate->ServiceWorkerScope();
+-
+-  RefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
+-    new ServiceWorkerClientPostMessageRunnable(serviceWorkerID, scope,
+-                                               mWindowId);
+-
+-  runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy().denySharedArrayBuffer(),
+-                  aRv);
+-  if (NS_WARN_IF(aRv.Failed())) {
+-    return;
+-  }
+-
+-  aRv = workerPrivate->DispatchToMainThread(runnable.forget());
+-  if (NS_WARN_IF(aRv.Failed())) {
+-    return;
+-  }
+-}
+-
+diff --git a/dom/workers/ServiceWorkerClient.h b/dom/workers/ServiceWorkerClient.h
+deleted file mode 100644
+--- a/dom/workers/ServiceWorkerClient.h
++++ /dev/null
+@@ -1,128 +0,0 @@
+-/* -*- 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 mozilla_dom_workers_serviceworkerclient_h
+-#define mozilla_dom_workers_serviceworkerclient_h
+-
+-#include "nsCOMPtr.h"
+-#include "nsWrapperCache.h"
+-#include "mozilla/ErrorResult.h"
+-#include "mozilla/dom/BindingDeclarations.h"
+-#include "mozilla/dom/ClientBinding.h"
+-
+-class nsIDocument;
+-
+-namespace mozilla {
+-namespace dom {
+-namespace workers {
+-
+-class ServiceWorkerClient;
+-class ServiceWorkerWindowClient;
+-
+-// Used as a container object for information needed to create
+-// client objects.
+-class ServiceWorkerClientInfo final
+-{
+-  friend class ServiceWorkerClient;
+-  friend class ServiceWorkerWindowClient;
+-
+-public:
+-  explicit ServiceWorkerClientInfo(nsIDocument* aDoc, uint32_t aOrdinal = 0);
+-
+-  const nsString& ClientId() const
+-  {
+-    return mClientId;
+-  }
+-
+-  bool operator<(const ServiceWorkerClientInfo& aRight) const;
+-  bool operator==(const ServiceWorkerClientInfo& aRight) const;
+-
+-private:
+-  const mozilla::dom::ClientType mType;
+-  const uint32_t mOrdinal;
+-  nsString mClientId;
+-  uint64_t mWindowId;
+-  nsString mUrl;
+-
+-  // Window Clients
+-  VisibilityState mVisibilityState;
+-  FrameType mFrameType;
+-  TimeStamp mLastFocusTime;
+-  bool mFocused;
+-};
+-
+-class ServiceWorkerClient : public nsISupports,
+-                            public nsWrapperCache
+-{
+-public:
+-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ServiceWorkerClient)
+-
+-  ServiceWorkerClient(nsISupports* aOwner,
+-                      const ServiceWorkerClientInfo& aClientInfo)
+-    : mOwner(aOwner)
+-    , mType(aClientInfo.mType)
+-    , mId(aClientInfo.mClientId)
+-    , mUrl(aClientInfo.mUrl)
+-    , mWindowId(aClientInfo.mWindowId)
+-    , mFrameType(aClientInfo.mFrameType)
+-  {
+-    MOZ_ASSERT(aOwner);
+-  }
+-
+-  nsISupports*
+-  GetParentObject() const
+-  {
+-    return mOwner;
+-  }
+-
+-  void GetId(nsString& aRetval) const
+-  {
+-    aRetval = mId;
+-  }
+-
+-  void
+-  GetUrl(nsAString& aUrl) const
+-  {
+-    aUrl.Assign(mUrl);
+-  }
+-
+-  mozilla::dom::FrameType
+-  FrameType() const
+-  {
+-    return mFrameType;
+-  }
+-
+-  mozilla::dom::ClientType
+-  Type() const;
+-
+-  void
+-  PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+-              const Sequence<JSObject*>& aTransferable, ErrorResult& aRv);
+-
+-  JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+-
+-protected:
+-  virtual ~ServiceWorkerClient()
+-  { }
+-
+-private:
+-  nsCOMPtr<nsISupports> mOwner;
+-  const ClientType mType;
+-  nsString mId;
+-  nsString mUrl;
+-
+-protected:
+-  uint64_t mWindowId;
+-  mozilla::dom::FrameType mFrameType;
+-};
+-
+-} // namespace workers
+-} // namespace dom
+-} // namespace mozilla
+-
+-#endif // mozilla_dom_workers_serviceworkerclient_h
+diff --git a/dom/workers/ServiceWorkerClients.cpp b/dom/workers/ServiceWorkerClients.cpp
+deleted file mode 100644
+--- a/dom/workers/ServiceWorkerClients.cpp
++++ /dev/null
+@@ -1,941 +0,0 @@
+-/* -*- 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 "ServiceWorkerClients.h"
+-
+-#include "mozilla/dom/Promise.h"
+-#include "mozilla/dom/PromiseWorkerProxy.h"
+-#include "mozilla/JSObjectHolder.h"
+-
+-#include "ServiceWorkerClient.h"
+-#include "ServiceWorkerManager.h"
+-#include "ServiceWorkerPrivate.h"
+-#include "ServiceWorkerWindowClient.h"
+-
+-#include "WorkerPrivate.h"
+-#include "WorkerRunnable.h"
+-#include "WorkerScope.h"
+-
+-#include "nsContentUtils.h"
+-#include "nsIBrowserDOMWindow.h"
+-#include "nsIDocShell.h"
+-#include "nsIDOMChromeWindow.h"
+-#include "nsIDOMWindow.h"
+-#include "nsIWebNavigation.h"
+-#include "nsIWebProgress.h"
+-#include "nsIWebProgressListener.h"
+-#include "nsIWindowMediator.h"
+-#include "nsIWindowWatcher.h"
+-#include "nsNetUtil.h"
+-#include "nsPIWindowWatcher.h"
+-#include "nsWindowWatcher.h"
+-#include "nsWeakReference.h"
+-
+-#ifdef MOZ_WIDGET_ANDROID
+-#include "FennecJNIWrappers.h"
+-#endif
+-
+-using namespace mozilla;
+-using namespace mozilla::dom;
+-using namespace mozilla::dom::workers;
+-
+-NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClients)
+-NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClients)
+-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ServiceWorkerClients, mWorkerScope)
+-
+-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerClients)
+-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+-  NS_INTERFACE_MAP_ENTRY(nsISupports)
+-NS_INTERFACE_MAP_END
+-
+-ServiceWorkerClients::ServiceWorkerClients(ServiceWorkerGlobalScope* aWorkerScope)
+-  : mWorkerScope(aWorkerScope)
+-{
+-  MOZ_ASSERT(mWorkerScope);
+-}
+-
+-JSObject*
+-ServiceWorkerClients::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+-{
+-  return ClientsBinding::Wrap(aCx, this, aGivenProto);
+-}
+-
+-namespace {
+-
+-class GetRunnable final : public Runnable
+-{
+-  class ResolvePromiseWorkerRunnable final : public WorkerRunnable
+-  {
+-    RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-    UniquePtr<ServiceWorkerClientInfo> mValue;
+-    nsresult mRv;
+-
+-  public:
+-    ResolvePromiseWorkerRunnable(WorkerPrivate* aWorkerPrivate,
+-                                 PromiseWorkerProxy* aPromiseProxy,
+-                                 UniquePtr<ServiceWorkerClientInfo>&& aValue,
+-                                 nsresult aRv)
+-      : WorkerRunnable(aWorkerPrivate),
+-        mPromiseProxy(aPromiseProxy),
+-        mValue(Move(aValue)),
+-        mRv(Move(aRv))
+-    {
+-      AssertIsOnMainThread();
+-    }
+-
+-    bool
+-    WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+-    {
+-      MOZ_ASSERT(aWorkerPrivate);
+-      aWorkerPrivate->AssertIsOnWorkerThread();
+-
+-      Promise* promise = mPromiseProxy->WorkerPromise();
+-      MOZ_ASSERT(promise);
+-
+-      if (NS_FAILED(mRv)) {
+-        promise->MaybeReject(mRv);
+-      } else if (mValue) {
+-        RefPtr<ServiceWorkerWindowClient> windowClient =
+-          new ServiceWorkerWindowClient(promise->GetParentObject(), *mValue);
+-        promise->MaybeResolve(windowClient.get());
+-      } else {
+-        promise->MaybeResolveWithUndefined();
+-      }
+-      mPromiseProxy->CleanUp();
+-      return true;
+-    }
+-  };
+-
+-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-  nsString mClientId;
+-public:
+-  GetRunnable(PromiseWorkerProxy* aPromiseProxy, const nsAString& aClientId)
+-    : mozilla::Runnable("GetRunnable")
+-    , mPromiseProxy(aPromiseProxy)
+-    , mClientId(aClientId)
+-  {
+-  }
+-
+-  NS_IMETHOD
+-  Run() override
+-  {
+-    AssertIsOnMainThread();
+-
+-    MutexAutoLock lock(mPromiseProxy->Lock());
+-    if (mPromiseProxy->CleanedUp()) {
+-      return NS_OK;
+-    }
+-
+-    WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
+-    MOZ_ASSERT(workerPrivate);
+-
+-    UniquePtr<ServiceWorkerClientInfo> result;
+-    ErrorResult rv;
+-
+-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+-    if (!swm) {
+-      rv = NS_ERROR_FAILURE;
+-    } else {
+-      result = swm->GetClient(workerPrivate->GetPrincipal(), mClientId, rv);
+-    }
+-
+-    RefPtr<ResolvePromiseWorkerRunnable> r =
+-      new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(),
+-                                       mPromiseProxy, Move(result),
+-                                       rv.StealNSResult());
+-    rv.SuppressException();
+-
+-    r->Dispatch();
+-    return NS_OK;
+-  }
+-};
+-
+-class MatchAllRunnable final : public Runnable
+-{
+-  class ResolvePromiseWorkerRunnable final : public WorkerRunnable
+-  {
+-    RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-    nsTArray<ServiceWorkerClientInfo> mValue;
+-
+-  public:
+-    ResolvePromiseWorkerRunnable(WorkerPrivate* aWorkerPrivate,
+-                                 PromiseWorkerProxy* aPromiseProxy,
+-                                 nsTArray<ServiceWorkerClientInfo>& aValue)
+-      : WorkerRunnable(aWorkerPrivate),
+-        mPromiseProxy(aPromiseProxy)
+-    {
+-      AssertIsOnMainThread();
+-      mValue.SwapElements(aValue);
+-    }
+-
+-    bool
+-    WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+-    {
+-      MOZ_ASSERT(aWorkerPrivate);
+-      aWorkerPrivate->AssertIsOnWorkerThread();
+-
+-      Promise* promise = mPromiseProxy->WorkerPromise();
+-      MOZ_ASSERT(promise);
+-
+-      nsTArray<RefPtr<ServiceWorkerClient>> ret;
+-      for (size_t i = 0; i < mValue.Length(); i++) {
+-        ret.AppendElement(RefPtr<ServiceWorkerClient>(
+-              new ServiceWorkerWindowClient(promise->GetParentObject(),
+-                                            mValue.ElementAt(i))));
+-      }
+-
+-      promise->MaybeResolve(ret);
+-      mPromiseProxy->CleanUp();
+-      return true;
+-    }
+-  };
+-
+-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-  const nsCString mScope;
+-  const uint64_t mServiceWorkerID;
+-  const bool mIncludeUncontrolled;
+-public:
+-  MatchAllRunnable(PromiseWorkerProxy* aPromiseProxy,
+-                   const nsCString& aScope,
+-                   uint64_t aServiceWorkerID,
+-                   bool aIncludeUncontrolled)
+-    : mozilla::Runnable("MatchAllRunnable")
+-    , mPromiseProxy(aPromiseProxy)
+-    , mScope(aScope)
+-    , mServiceWorkerID(aServiceWorkerID)
+-    , mIncludeUncontrolled(aIncludeUncontrolled)
+-  {
+-    MOZ_ASSERT(mPromiseProxy);
+-  }
+-
+-  NS_IMETHOD
+-  Run() override
+-  {
+-    AssertIsOnMainThread();
+-
+-    MutexAutoLock lock(mPromiseProxy->Lock());
+-    if (mPromiseProxy->CleanedUp()) {
+-      return NS_OK;
+-    }
+-
+-    nsTArray<ServiceWorkerClientInfo> result;
+-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+-    if (swm) {
+-      swm->GetAllClients(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(),
+-                         mScope, mServiceWorkerID, mIncludeUncontrolled,
+-                         result);
+-    }
+-    RefPtr<ResolvePromiseWorkerRunnable> r =
+-      new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(),
+-                                       mPromiseProxy, result);
+-
+-    r->Dispatch();
+-    return NS_OK;
+-  }
+-};
+-
+-class ResolveClaimRunnable final : public WorkerRunnable
+-{
+-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-  nsresult mResult;
+-
+-public:
+-  ResolveClaimRunnable(WorkerPrivate* aWorkerPrivate,
+-                       PromiseWorkerProxy* aPromiseProxy,
+-                       nsresult aResult)
+-    : WorkerRunnable(aWorkerPrivate)
+-    , mPromiseProxy(aPromiseProxy)
+-    , mResult(aResult)
+-  {
+-    AssertIsOnMainThread();
+-  }
+-
+-  bool
+-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+-  {
+-    MOZ_ASSERT(aWorkerPrivate);
+-    aWorkerPrivate->AssertIsOnWorkerThread();
+-
+-    RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
+-    MOZ_ASSERT(promise);
+-
+-    if (NS_SUCCEEDED(mResult)) {
+-      promise->MaybeResolveWithUndefined();
+-    } else {
+-      promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+-    }
+-
+-    mPromiseProxy->CleanUp();
+-    return true;
+-  }
+-};
+-
+-class ClaimRunnable final : public Runnable
+-{
+-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-  nsCString mScope;
+-  uint64_t mServiceWorkerID;
+-
+-public:
+-  ClaimRunnable(PromiseWorkerProxy* aPromiseProxy, const nsCString& aScope)
+-    : mozilla::Runnable("ClaimRunnable")
+-    , mPromiseProxy(aPromiseProxy)
+-    , mScope(aScope)
+-    // Safe to call GetWorkerPrivate() since we are being called on the worker
+-    // thread via script (so no clean up has occured yet).
+-    , mServiceWorkerID(aPromiseProxy->GetWorkerPrivate()->ServiceWorkerID())
+-  {
+-    MOZ_ASSERT(aPromiseProxy);
+-  }
+-
+-  NS_IMETHOD
+-  Run() override
+-  {
+-    MutexAutoLock lock(mPromiseProxy->Lock());
+-    if (mPromiseProxy->CleanedUp()) {
+-      return NS_OK;
+-    }
+-
+-    WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
+-    MOZ_ASSERT(workerPrivate);
+-
+-    nsresult rv = NS_OK;
+-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+-    if (!swm) {
+-      // browser shutdown
+-      rv = NS_ERROR_FAILURE;
+-    } else {
+-      rv = swm->ClaimClients(workerPrivate->GetPrincipal(), mScope,
+-                             mServiceWorkerID);
+-    }
+-
+-    RefPtr<ResolveClaimRunnable> r =
+-      new ResolveClaimRunnable(workerPrivate, mPromiseProxy, rv);
+-
+-    r->Dispatch();
+-    return NS_OK;
+-  }
+-};
+-
+-class ResolveOpenWindowRunnable final : public WorkerRunnable
+-{
+-public:
+-  ResolveOpenWindowRunnable(PromiseWorkerProxy* aPromiseProxy,
+-                            UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+-                            const nsresult aStatus)
+-  : WorkerRunnable(aPromiseProxy->GetWorkerPrivate())
+-  , mPromiseProxy(aPromiseProxy)
+-  , mClientInfo(Move(aClientInfo))
+-  , mStatus(aStatus)
+-  {
+-    AssertIsOnMainThread();
+-    MOZ_ASSERT(aPromiseProxy);
+-  }
+-
+-  bool
+-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+-  {
+-    MOZ_ASSERT(aWorkerPrivate);
+-    aWorkerPrivate->AssertIsOnWorkerThread();
+-
+-    Promise* promise = mPromiseProxy->WorkerPromise();
+-    if (NS_WARN_IF(NS_FAILED(mStatus))) {
+-      promise->MaybeReject(mStatus);
+-    } else if (mClientInfo) {
+-      RefPtr<ServiceWorkerWindowClient> client =
+-        new ServiceWorkerWindowClient(promise->GetParentObject(),
+-                                      *mClientInfo);
+-      promise->MaybeResolve(client);
+-    } else {
+-      promise->MaybeResolve(JS::NullHandleValue);
+-    }
+-
+-    mPromiseProxy->CleanUp();
+-    return true;
+-  }
+-
+-private:
+-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-  UniquePtr<ServiceWorkerClientInfo> mClientInfo;
+-  const nsresult mStatus;
+-};
+-
+-class WebProgressListener final : public nsIWebProgressListener,
+-                                  public nsSupportsWeakReference
+-{
+-public:
+-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(WebProgressListener, nsIWebProgressListener)
+-
+-  WebProgressListener(PromiseWorkerProxy* aPromiseProxy,
+-                      ServiceWorkerPrivate* aServiceWorkerPrivate,
+-                      nsPIDOMWindowOuter* aWindow,
+-                      nsIURI* aBaseURI)
+-  : mPromiseProxy(aPromiseProxy)
+-  , mServiceWorkerPrivate(aServiceWorkerPrivate)
+-  , mWindow(aWindow)
+-  , mBaseURI(aBaseURI)
+-  {
+-    MOZ_ASSERT(aPromiseProxy);
+-    MOZ_ASSERT(aServiceWorkerPrivate);
+-    MOZ_ASSERT(aWindow);
+-    MOZ_ASSERT(aWindow->IsOuterWindow());
+-    MOZ_ASSERT(aBaseURI);
+-    AssertIsOnMainThread();
+-
+-    mServiceWorkerPrivate->StoreISupports(static_cast<nsIWebProgressListener*>(this));
+-  }
+-
+-  NS_IMETHOD
+-  OnStateChange(nsIWebProgress* aWebProgress,
+-                nsIRequest* aRequest,
+-                uint32_t aStateFlags, nsresult aStatus) override
+-  {
+-    if (!(aStateFlags & STATE_IS_DOCUMENT) ||
+-         !(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) {
+-      return NS_OK;
+-    }
+-
+-    // Our caller keeps a strong reference, so it is safe to remove the listener
+-    // from ServiceWorkerPrivate.
+-    mServiceWorkerPrivate->RemoveISupports(static_cast<nsIWebProgressListener*>(this));
+-    aWebProgress->RemoveProgressListener(this);
+-
+-    MutexAutoLock lock(mPromiseProxy->Lock());
+-    if (mPromiseProxy->CleanedUp()) {
+-      return NS_OK;
+-    }
+-
+-    nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
+-    UniquePtr<ServiceWorkerClientInfo> clientInfo;
+-    if (doc) {
+-      // Check same origin.
+-      nsCOMPtr<nsIScriptSecurityManager> securityManager =
+-        nsContentUtils::GetSecurityManager();
+-      nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(),
+-                                                        mBaseURI, false);
+-      if (NS_SUCCEEDED(rv)) {
+-        clientInfo.reset(new ServiceWorkerClientInfo(doc));
+-      }
+-    }
+-
+-    RefPtr<ResolveOpenWindowRunnable> r =
+-      new ResolveOpenWindowRunnable(mPromiseProxy,
+-                                    Move(clientInfo),
+-                                    NS_OK);
+-    r->Dispatch();
+-
+-    return NS_OK;
+-  }
+-
+-  NS_IMETHOD
+-  OnProgressChange(nsIWebProgress* aWebProgress,
+-                   nsIRequest* aRequest,
+-                   int32_t aCurSelfProgress,
+-                   int32_t aMaxSelfProgress,
+-                   int32_t aCurTotalProgress,
+-                   int32_t aMaxTotalProgress) override
+-  {
+-    MOZ_ASSERT(false, "Unexpected notification.");
+-    return NS_OK;
+-  }
+-
+-  NS_IMETHOD
+-  OnLocationChange(nsIWebProgress* aWebProgress,
+-                   nsIRequest* aRequest,
+-                   nsIURI* aLocation,
+-                   uint32_t aFlags) override
+-  {
+-    MOZ_ASSERT(false, "Unexpected notification.");
+-    return NS_OK;
+-  }
+-
+-  NS_IMETHOD
+-  OnStatusChange(nsIWebProgress* aWebProgress,
+-                 nsIRequest* aRequest,
+-                 nsresult aStatus, const char16_t* aMessage) override
+-  {
+-    MOZ_ASSERT(false, "Unexpected notification.");
+-    return NS_OK;
+-  }
+-
+-  NS_IMETHOD
+-  OnSecurityChange(nsIWebProgress* aWebProgress,
+-                   nsIRequest* aRequest,
+-                   uint32_t aState) override
+-  {
+-    MOZ_ASSERT(false, "Unexpected notification.");
+-    return NS_OK;
+-  }
+-
+-private:
+-  ~WebProgressListener()
+-  { }
+-
+-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-  RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
+-  nsCOMPtr<nsPIDOMWindowOuter> mWindow;
+-  nsCOMPtr<nsIURI> mBaseURI;
+-};
+-
+-NS_IMPL_CYCLE_COLLECTING_ADDREF(WebProgressListener)
+-NS_IMPL_CYCLE_COLLECTING_RELEASE(WebProgressListener)
+-NS_IMPL_CYCLE_COLLECTION(WebProgressListener, mPromiseProxy,
+-                         mServiceWorkerPrivate, mWindow)
+-
+-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebProgressListener)
+-  NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
+-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+-NS_INTERFACE_MAP_END
+-
+-class OpenWindowRunnable final : public Runnable
+-                               , public nsIObserver
+-                               , public nsSupportsWeakReference
+-{
+-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-  nsString mUrl;
+-  nsString mScope;
+-
+-public:
+-  NS_DECL_ISUPPORTS_INHERITED
+-  // Note: |OpenWindowRunnable| cannot be cycle collected because it inherits
+-  // thread safe reference counting from |mozilla::Runnable|. On Fennec, we
+-  // might use |ServiceWorkerPrivate::StoreISupports| to keep this object alive
+-  // while waiting for an event from the observer service. As such, to avoid
+-  // creating a cycle that will leak, |OpenWindowRunnable| must not hold a strong
+-  // reference to |ServiceWorkerPrivate|.
+-
+-  OpenWindowRunnable(PromiseWorkerProxy* aPromiseProxy,
+-                     const nsAString& aUrl,
+-                     const nsAString& aScope)
+-    : mozilla::Runnable("OpenWindowRunnable")
+-    , mPromiseProxy(aPromiseProxy)
+-    , mUrl(aUrl)
+-    , mScope(aScope)
+-  {
+-    MOZ_ASSERT(aPromiseProxy);
+-    MOZ_ASSERT(aPromiseProxy->GetWorkerPrivate());
+-    aPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
+-  }
+-
+-  NS_IMETHOD
+-  Observe(nsISupports* aSubject, const char* aTopic, const char16_t* /* aData */) override
+-  {
+-    AssertIsOnMainThread();
+-
+-    nsCString topic(aTopic);
+-    if (!topic.EqualsLiteral("BrowserChrome:Ready")) {
+-      MOZ_ASSERT(false, "Unexpected topic.");
+-      return NS_ERROR_FAILURE;
+-    }
+-
+-    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+-    NS_ENSURE_STATE(os);
+-    os->RemoveObserver(this, "BrowserChrome:Ready");
+-
+-    RefPtr<ServiceWorkerPrivate> swp = GetServiceWorkerPrivate();
+-    NS_ENSURE_STATE(swp);
+-
+-    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
+-    swp->RemoveISupports(static_cast<nsIObserver*>(this));
+-
+-    return NS_OK;
+-  }
+-
+-  NS_IMETHOD
+-  Run() override
+-  {
+-    AssertIsOnMainThread();
+-
+-    MutexAutoLock lock(mPromiseProxy->Lock());
+-    if (mPromiseProxy->CleanedUp()) {
+-      return NS_OK;
+-    }
+-
+-#ifdef MOZ_WIDGET_ANDROID
+-    // This fires an intent that will start launching Fennec and foreground it,
+-    // if necessary.
+-    if (jni::IsFennec()) {
+-      java::GeckoApp::LaunchOrBringToFront();
+-    }
+-#endif
+-
+-    nsCOMPtr<nsPIDOMWindowOuter> window;
+-    nsresult rv = OpenWindow(getter_AddRefs(window));
+-    if (NS_SUCCEEDED(rv)) {
+-      MOZ_ASSERT(window);
+-
+-      rv = nsContentUtils::DispatchFocusChromeEvent(window);
+-      if (NS_WARN_IF(NS_FAILED(rv))) {
+-        return rv;
+-      }
+-
+-      WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
+-      MOZ_ASSERT(workerPrivate);
+-
+-      WorkerPrivate::LocationInfo& info = workerPrivate->GetLocationInfo();
+-      nsCOMPtr<nsIURI> baseURI;
+-      nsresult rv = NS_NewURI(getter_AddRefs(baseURI), info.mHref);
+-      if (NS_WARN_IF(NS_FAILED(rv))) {
+-        return NS_ERROR_FAILURE;
+-      }
+-
+-      nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
+-      nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
+-
+-      if (!webProgress) {
+-        return NS_ERROR_FAILURE;
+-      }
+-
+-      RefPtr<ServiceWorkerPrivate> swp = GetServiceWorkerPrivate();
+-      NS_ENSURE_STATE(swp);
+-
+-      nsCOMPtr<nsIWebProgressListener> listener =
+-        new WebProgressListener(mPromiseProxy, swp, window, baseURI);
+-
+-      rv = webProgress->AddProgressListener(listener,
+-                                            nsIWebProgress::NOTIFY_STATE_DOCUMENT);
+-      MOZ_ASSERT(NS_SUCCEEDED(rv));
+-      return NS_OK;
+-    }
+-#ifdef MOZ_WIDGET_ANDROID
+-    else if (rv == NS_ERROR_NOT_AVAILABLE && jni::IsFennec()) {
+-      // We couldn't get a browser window, so Fennec must not be running.
+-      // Send an Intent to launch Fennec and wait for "BrowserChrome:Ready"
+-      // to try opening a window again.
+-      RefPtr<ServiceWorkerPrivate> swp = GetServiceWorkerPrivate();
+-      NS_ENSURE_STATE(swp);
+-
+-      nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+-      NS_ENSURE_STATE(os);
+-
+-      rv = os->AddObserver(this, "BrowserChrome:Ready", /* weakRef */ true);
+-      NS_ENSURE_SUCCESS(rv, rv);
+-
+-      swp->StoreISupports(static_cast<nsIObserver*>(this));
+-
+-      return NS_OK;
+-    }
+-#endif
+-
+-    RefPtr<ResolveOpenWindowRunnable> resolveRunnable =
+-      new ResolveOpenWindowRunnable(mPromiseProxy, nullptr, rv);
+-
+-    Unused << NS_WARN_IF(!resolveRunnable->Dispatch());
+-
+-    return NS_OK;
+-  }
+-
+-private:
+-  ~OpenWindowRunnable()
+-  { }
+-
+-  ServiceWorkerPrivate*
+-  GetServiceWorkerPrivate() const
+-  {
+-    AssertIsOnMainThread();
+-
+-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+-    if (!swm) {
+-      // browser shutdown
+-      return nullptr;
+-    }
+-
+-    WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
+-    MOZ_ASSERT(workerPrivate);
+-
+-    nsCOMPtr<nsIPrincipal> principal = workerPrivate->GetPrincipal();
+-    MOZ_DIAGNOSTIC_ASSERT(principal);
+-
+-    RefPtr<ServiceWorkerRegistrationInfo> registration =
+-      swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope));
+-    if (NS_WARN_IF(!registration)) {
+-      return nullptr;
+-    }
+-
+-    RefPtr<ServiceWorkerInfo> serviceWorkerInfo =
+-      registration->GetServiceWorkerInfoById(workerPrivate->ServiceWorkerID());
+-    if (NS_WARN_IF(!serviceWorkerInfo)) {
+-      return nullptr;
+-    }
+-
+-    return serviceWorkerInfo->WorkerPrivate();
+-  }
+-
+-  nsresult
+-  OpenWindow(nsPIDOMWindowOuter** aWindow)
+-  {
+-    MOZ_DIAGNOSTIC_ASSERT(aWindow);
+-    WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
+-
+-    nsCOMPtr<nsIPrincipal> triggeringPrincipal = workerPrivate->GetPrincipal();
+-    MOZ_DIAGNOSTIC_ASSERT(triggeringPrincipal);
+-
+-    // [[1. Let url be the result of parsing url with entry settings object's API
+-    //   base URL.]]
+-    nsCOMPtr<nsIURI> uri;
+-    WorkerPrivate::LocationInfo& info = workerPrivate->GetLocationInfo();
+-
+-    nsCOMPtr<nsIURI> baseURI;
+-    nsresult rv = NS_NewURI(getter_AddRefs(baseURI), info.mHref);
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return NS_ERROR_TYPE_ERR;
+-    }
+-
+-    rv = NS_NewURI(getter_AddRefs(uri), mUrl, nullptr, baseURI);
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return NS_ERROR_TYPE_ERR;
+-    }
+-
+-    // [[6.1 Open Window]]
+-    nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID,
+-                                                   &rv);
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return rv;
+-    }
+-
+-    if (XRE_IsContentProcess()) {
+-
+-      // Let's create a sandbox in order to have a valid JSContext and correctly
+-      // propagate the SubjectPrincipal.
+-      AutoJSAPI jsapi;
+-      jsapi.Init();
+-
+-      JSContext* cx = jsapi.cx();
+-
+-      nsIXPConnect* xpc = nsContentUtils::XPConnect();
+-      MOZ_ASSERT(xpc, "This should never be null!");
+-
+-      JS::Rooted<JSObject*> sandbox(cx);
+-      rv = xpc->CreateSandbox(cx, triggeringPrincipal, sandbox.address());
+-      if (NS_WARN_IF(NS_FAILED(rv))) {
+-        return rv;
+-      }
+-
+-      JSAutoCompartment ac(cx, sandbox);
+-
+-      // ContentProcess
+-      nsCOMPtr<nsIWindowWatcher> wwatch =
+-        do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+-      if (NS_WARN_IF(NS_FAILED(rv))) {
+-        return rv;
+-      }
+-      nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
+-      NS_ENSURE_STATE(pwwatch);
+-
+-      nsCString spec;
+-      rv = uri->GetSpec(spec);
+-      if (NS_WARN_IF(NS_FAILED(rv))) {
+-        return rv;
+-      }
+-
+-      nsCOMPtr<mozIDOMWindowProxy> newWindow;
+-      rv = pwwatch->OpenWindow2(nullptr,
+-                                spec.get(),
+-                                nullptr,
+-                                nullptr,
+-                                false, false, true, nullptr,
+-                                // Not a spammy popup; we got permission, we swear!
+-                                /* aIsPopupSpam = */ false,
+-                                // Don't force noopener.  We're not passing in an
+-                                // opener anyway, and we _do_ want the returned
+-                                // window.
+-                                /* aForceNoOpener = */ false,
+-                                /* aLoadInfp = */ nullptr,
+-                                getter_AddRefs(newWindow));
+-      if (NS_WARN_IF(NS_FAILED(rv))) {
+-        return rv;
+-      }
+-      nsCOMPtr<nsPIDOMWindowOuter> pwindow = nsPIDOMWindowOuter::From(newWindow);
+-      pwindow.forget(aWindow);
+-      MOZ_DIAGNOSTIC_ASSERT(*aWindow);
+-      return NS_OK;
+-    }
+-
+-    // Find the most recent browser window and open a new tab in it.
+-    nsCOMPtr<nsPIDOMWindowOuter> browserWindow =
+-      nsContentUtils::GetMostRecentNonPBWindow();
+-    if (!browserWindow) {
+-      // It is possible to be running without a browser window on Mac OS, so
+-      // we need to open a new chrome window.
+-      // TODO(catalinb): open new chrome window. Bug 1218080
+-      return NS_ERROR_NOT_AVAILABLE;
+-    }
+-
+-    nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(browserWindow);
+-    if (NS_WARN_IF(!chromeWin)) {
+-      return NS_ERROR_FAILURE;
+-    }
+-
+-    nsCOMPtr<nsIBrowserDOMWindow> bwin;
+-    chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
+-
+-    if (NS_WARN_IF(!bwin)) {
+-      return NS_ERROR_FAILURE;
+-    }
+-
+-    nsCOMPtr<mozIDOMWindowProxy> win;
+-    rv = bwin->OpenURI(uri, nullptr,
+-                       nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
+-                       nsIBrowserDOMWindow::OPEN_NEW,
+-                       triggeringPrincipal,
+-                       getter_AddRefs(win));
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return rv;
+-    }
+-    NS_ENSURE_STATE(win);
+-
+-    nsCOMPtr<nsPIDOMWindowOuter> pWin = nsPIDOMWindowOuter::From(win);
+-    pWin.forget(aWindow);
+-    MOZ_DIAGNOSTIC_ASSERT(*aWindow);
+-
+-    return NS_OK;
+-  }
+-};
+-
+-NS_IMPL_ADDREF_INHERITED(OpenWindowRunnable, Runnable)                                    \
+-NS_IMPL_RELEASE_INHERITED(OpenWindowRunnable, Runnable)
+-
+-NS_INTERFACE_MAP_BEGIN(OpenWindowRunnable)
+-NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+-NS_INTERFACE_MAP_ENTRY(nsIObserver)
+-NS_INTERFACE_MAP_ENTRY(nsIRunnable)
+-NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
+-NS_INTERFACE_MAP_END
+-
+-} // namespace
+-
+-already_AddRefed<Promise>
+-ServiceWorkerClients::Get(const nsAString& aClientId, ErrorResult& aRv)
+-{
+-  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+-  MOZ_ASSERT(workerPrivate);
+-  workerPrivate->AssertIsOnWorkerThread();
+-
+-  RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
+-  if (NS_WARN_IF(aRv.Failed())) {
+-    return nullptr;
+-  }
+-
+-  RefPtr<PromiseWorkerProxy> promiseProxy =
+-    PromiseWorkerProxy::Create(workerPrivate, promise);
+-  if (!promiseProxy) {
+-    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+-    return promise.forget();
+-  }
+-
+-  RefPtr<GetRunnable> r =
+-    new GetRunnable(promiseProxy, aClientId);
+-  MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
+-  return promise.forget();
+-}
+-
+-already_AddRefed<Promise>
+-ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions,
+-                               ErrorResult& aRv)
+-{
+-  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+-  MOZ_ASSERT(workerPrivate);
+-  workerPrivate->AssertIsOnWorkerThread();
+-
+-  nsString scope;
+-  mWorkerScope->GetScope(scope);
+-
+-  if (aOptions.mType != ClientType::Window) {
+-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+-    return nullptr;
+-  }
+-
+-  RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
+-  if (NS_WARN_IF(aRv.Failed())) {
+-    return nullptr;
+-  }
+-
+-  RefPtr<PromiseWorkerProxy> promiseProxy =
+-    PromiseWorkerProxy::Create(workerPrivate, promise);
+-  if (!promiseProxy) {
+-    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+-    return promise.forget();
+-  }
+-
+-  RefPtr<MatchAllRunnable> r =
+-    new MatchAllRunnable(promiseProxy,
+-                         NS_ConvertUTF16toUTF8(scope),
+-                         workerPrivate->ServiceWorkerID(),
+-                         aOptions.mIncludeUncontrolled);
+-  MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
+-  return promise.forget();
+-}
+-
+-already_AddRefed<Promise>
+-ServiceWorkerClients::OpenWindow(const nsAString& aUrl,
+-                                 ErrorResult& aRv)
+-{
+-  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+-  MOZ_ASSERT(workerPrivate);
+-
+-  RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
+-  if (NS_WARN_IF(aRv.Failed())) {
+-    return nullptr;
+-  }
+-
+-  if (aUrl.EqualsLiteral("about:blank")) {
+-    promise->MaybeReject(NS_ERROR_TYPE_ERR);
+-    return promise.forget();
+-  }
+-
+-  // [[4. If this algorithm is not allowed to show a popup ..]]
+-  // In Gecko the service worker is allowed to show a popup only if the user
+-  // just clicked on a notification.
+-  if (!workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
+-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+-    return promise.forget();
+-  }
+-
+-  RefPtr<PromiseWorkerProxy> promiseProxy =
+-    PromiseWorkerProxy::Create(workerPrivate, promise);
+-
+-  if (!promiseProxy) {
+-    return nullptr;
+-  }
+-
+-  nsString scope;
+-  mWorkerScope->GetScope(scope);
+-
+-  RefPtr<OpenWindowRunnable> r = new OpenWindowRunnable(promiseProxy,
+-                                                        aUrl, scope);
+-  MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
+-
+-  return promise.forget();
+-}
+-
+-already_AddRefed<Promise>
+-ServiceWorkerClients::Claim(ErrorResult& aRv)
+-{
+-  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+-  MOZ_ASSERT(workerPrivate);
+-
+-  RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
+-  if (NS_WARN_IF(aRv.Failed())) {
+-    return nullptr;
+-  }
+-
+-  RefPtr<PromiseWorkerProxy> promiseProxy =
+-    PromiseWorkerProxy::Create(workerPrivate, promise);
+-  if (!promiseProxy) {
+-    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+-    return promise.forget();
+-  }
+-
+-  nsString scope;
+-  mWorkerScope->GetScope(scope);
+-
+-  RefPtr<ClaimRunnable> runnable =
+-    new ClaimRunnable(promiseProxy, NS_ConvertUTF16toUTF8(scope));
+-
+-  MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(runnable.forget()));
+-  return promise.forget();
+-}
+diff --git a/dom/workers/ServiceWorkerClients.h b/dom/workers/ServiceWorkerClients.h
+deleted file mode 100644
+--- a/dom/workers/ServiceWorkerClients.h
++++ /dev/null
+@@ -1,64 +0,0 @@
+-/* -*- 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 mozilla_dom_workers_serviceworkerclients_h
+-#define mozilla_dom_workers_serviceworkerclients_h
+-
+-#include "nsWrapperCache.h"
+-
+-#include "mozilla/dom/WorkerScope.h"
+-#include "mozilla/dom/BindingDeclarations.h"
+-#include "mozilla/dom/ClientsBinding.h"
+-#include "mozilla/ErrorResult.h"
+-
+-namespace mozilla {
+-namespace dom {
+-namespace workers {
+-
+-class ServiceWorkerClients final : public nsISupports,
+-                                   public nsWrapperCache
+-{
+-public:
+-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ServiceWorkerClients)
+-
+-  explicit ServiceWorkerClients(ServiceWorkerGlobalScope* aWorkerScope);
+-
+-  already_AddRefed<Promise>
+-  Get(const nsAString& aClientId, ErrorResult& aRv);
+-
+-  already_AddRefed<Promise>
+-  MatchAll(const ClientQueryOptions& aOptions, ErrorResult& aRv);
+-
+-  already_AddRefed<Promise>
+-  OpenWindow(const nsAString& aUrl, ErrorResult& aRv);
+-
+-  already_AddRefed<Promise>
+-  Claim(ErrorResult& aRv);
+-
+-  JSObject*
+-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+-
+-  ServiceWorkerGlobalScope*
+-  GetParentObject() const
+-  {
+-    return mWorkerScope;
+-  }
+-
+-private:
+-  ~ServiceWorkerClients()
+-  {
+-  }
+-
+-  RefPtr<ServiceWorkerGlobalScope> mWorkerScope;
+-};
+-
+-} // namespace workers
+-} // namespace dom
+-} // namespace mozilla
+-
+-#endif // mozilla_dom_workers_serviceworkerclients_h
+diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp
+--- a/dom/workers/ServiceWorkerEvents.cpp
++++ b/dom/workers/ServiceWorkerEvents.cpp
+@@ -19,23 +19,23 @@
+ #include "nsContentUtils.h"
+ #include "nsComponentManagerUtils.h"
+ #include "nsServiceManagerUtils.h"
+ #include "nsStreamUtils.h"
+ #include "nsNetCID.h"
+ #include "nsNetUtil.h"
+ #include "nsSerializationHelper.h"
+ #include "nsQueryObject.h"
+-#include "ServiceWorkerClient.h"
+ #include "ServiceWorkerManager.h"
+ 
+ #include "mozilla/ErrorResult.h"
+ #include "mozilla/LoadInfo.h"
+ #include "mozilla/Preferences.h"
+ #include "mozilla/dom/BodyUtil.h"
++#include "mozilla/dom/Client.h"
+ #include "mozilla/dom/FetchEventBinding.h"
+ #include "mozilla/dom/MessagePort.h"
+ #include "mozilla/dom/PromiseNativeHandler.h"
+ #include "mozilla/dom/PushEventBinding.h"
+ #include "mozilla/dom/PushMessageDataBinding.h"
+ #include "mozilla/dom/PushUtil.h"
+ #include "mozilla/dom/Request.h"
+ #include "mozilla/dom/TypedArray.h"
+diff --git a/dom/workers/ServiceWorkerEvents.h b/dom/workers/ServiceWorkerEvents.h
+--- a/dom/workers/ServiceWorkerEvents.h
++++ b/dom/workers/ServiceWorkerEvents.h
+@@ -20,16 +20,17 @@
+ #include "nsProxyRelease.h"
+ #include "nsContentUtils.h"
+ 
+ class nsIInterceptedChannel;
+ 
+ namespace mozilla {
+ namespace dom {
+ class Blob;
++class Client;
+ class MessagePort;
+ class Request;
+ class ResponseOrPromise;
+ 
+ struct PushEventInit;
+ } // namespace dom
+ } // namespace mozilla
+ 
+@@ -264,17 +265,17 @@ public:
+   }
+ };
+ 
+ class ExtendableMessageEvent final : public ExtendableEvent
+ {
+   JS::Heap<JS::Value> mData;
+   nsString mOrigin;
+   nsString mLastEventId;
+-  RefPtr<ServiceWorkerClient> mClient;
++  RefPtr<Client> mClient;
+   RefPtr<ServiceWorker> mServiceWorker;
+   RefPtr<MessagePort> mMessagePort;
+   nsTArray<RefPtr<MessagePort>> mPorts;
+ 
+ protected:
+   explicit ExtendableMessageEvent(EventTarget* aOwner);
+   ~ExtendableMessageEvent();
+ 
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -63,17 +63,16 @@
+ #include "nsGlobalWindow.h"
+ #include "nsNetUtil.h"
+ #include "nsProxyRelease.h"
+ #include "nsQueryObject.h"
+ #include "nsTArray.h"
+ 
+ #include "RuntimeService.h"
+ #include "ServiceWorker.h"
+-#include "ServiceWorkerClient.h"
+ #include "ServiceWorkerContainer.h"
+ #include "ServiceWorkerInfo.h"
+ #include "ServiceWorkerJobQueue.h"
+ #include "ServiceWorkerManagerChild.h"
+ #include "ServiceWorkerPrivate.h"
+ #include "ServiceWorkerRegisterJob.h"
+ #include "ServiceWorkerRegistrar.h"
+ #include "ServiceWorkerRegistration.h"
+@@ -3191,178 +3190,16 @@ FireControllerChangeOnDocument(nsIDocume
+   container->ControllerChanged(result);
+   if (result.Failed()) {
+     NS_WARNING("Failed to dispatch controllerchange event");
+   }
+ }
+ 
+ } // anonymous namespace
+ 
+-UniquePtr<ServiceWorkerClientInfo>
+-ServiceWorkerManager::GetClient(nsIPrincipal* aPrincipal,
+-                                const nsAString& aClientId,
+-                                ErrorResult& aRv)
+-{
+-  AssertIsOnMainThread();
+-  UniquePtr<ServiceWorkerClientInfo> clientInfo;
+-  nsCOMPtr<nsISupportsInterfacePointer> ifptr =
+-    do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
+-  if (NS_WARN_IF(!ifptr)) {
+-    return clientInfo;
+-  }
+-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+-  if (NS_WARN_IF(!obs)) {
+-    return clientInfo;
+-  }
+-
+-  nsresult rv = obs->NotifyObservers(ifptr, "service-worker-get-client",
+-                                     PromiseFlatString(aClientId).get());
+-  if (NS_WARN_IF(NS_FAILED(rv))) {
+-    return clientInfo;
+-  }
+-
+-  nsCOMPtr<nsISupports> ptr;
+-  ifptr->GetData(getter_AddRefs(ptr));
+-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
+-  if (NS_WARN_IF(!doc || !doc->GetInnerWindow())) {
+-    return clientInfo;
+-  }
+-
+-  bool equals = false;
+-  aPrincipal->Equals(doc->NodePrincipal(), &equals);
+-  if (!equals) {
+-    return clientInfo;
+-  }
+-
+-  if (!IsFromAuthenticatedOrigin(doc)) {
+-    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+-    return clientInfo;
+-  }
+-
+-  // Don't let service worker see 3rd party iframes that are denied storage
+-  // access.  We don't want these to communicate.
+-  auto storageAccess =
+-    nsContentUtils::StorageAllowedForWindow(doc->GetInnerWindow());
+-  if (storageAccess != nsContentUtils::StorageAccess::eAllow) {
+-    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+-                                    NS_LITERAL_CSTRING("Service Workers"), doc,
+-                                    nsContentUtils::eDOM_PROPERTIES,
+-                                    "ServiceWorkerGetClientStorageError");
+-    return clientInfo;
+-  }
+-
+-  clientInfo.reset(new ServiceWorkerClientInfo(doc));
+-  return clientInfo;
+-}
+-
+-void
+-ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal,
+-                                    const nsCString& aScope,
+-                                    uint64_t aServiceWorkerID,
+-                                    bool aIncludeUncontrolled,
+-                                    nsTArray<ServiceWorkerClientInfo>& aDocuments)
+-{
+-  AssertIsOnMainThread();
+-  MOZ_ASSERT(aPrincipal);
+-
+-  RefPtr<ServiceWorkerRegistrationInfo> registration =
+-    GetRegistration(aPrincipal, aScope);
+-
+-  if (!registration) {
+-    // The registration was removed, leave the array empty.
+-    return;
+-  }
+-
+-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+-  if (NS_WARN_IF(!obs)) {
+-    return;
+-  }
+-
+-  nsCOMPtr<nsISimpleEnumerator> enumerator;
+-  nsresult rv = obs->EnumerateObservers("service-worker-get-client",
+-                                        getter_AddRefs(enumerator));
+-  if (NS_WARN_IF(NS_FAILED(rv))) {
+-    return;
+-  }
+-
+-  // Get a list of Client documents out of the observer service
+-  AutoTArray<nsCOMPtr<nsIDocument>, 32> docList;
+-  bool loop = true;
+-  while (NS_SUCCEEDED(enumerator->HasMoreElements(&loop)) && loop) {
+-    nsCOMPtr<nsISupports> ptr;
+-    rv = enumerator->GetNext(getter_AddRefs(ptr));
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      continue;
+-    }
+-
+-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
+-    if (!doc || !doc->GetWindow() || !doc->GetInnerWindow()) {
+-      continue;
+-    }
+-
+-    bool equals = false;
+-    Unused << aPrincipal->Equals(doc->NodePrincipal(), &equals);
+-    if (!equals) {
+-      continue;
+-    }
+-
+-    // Treat http windows with devtools opened as secure if the correct devtools
+-    // setting is enabled.
+-    if (!doc->GetWindow()->GetServiceWorkersTestingEnabled() &&
+-        !Preferences::GetBool("dom.serviceWorkers.testing.enabled") &&
+-        !IsFromAuthenticatedOrigin(doc)) {
+-      continue;
+-    }
+-
+-    // Don't let service worker find 3rd party iframes that are denied storage
+-    // access.  We don't want these to communicate.
+-    auto storageAccess =
+-      nsContentUtils::StorageAllowedForWindow(doc->GetInnerWindow());
+-    if (storageAccess != nsContentUtils::StorageAccess::eAllow) {
+-      nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+-                                      NS_LITERAL_CSTRING("Service Workers"),
+-                                      doc, nsContentUtils::eDOM_PROPERTIES,
+-                                      "ServiceWorkerGetClientStorageError");
+-      continue;
+-    }
+-
+-    // If we are only returning controlled Clients then skip any documents
+-    // that are for different registrations.  We also skip service workers
+-    // that don't match the ID of our calling service worker.  We should
+-    // only return Clients controlled by that precise service worker.
+-    if (!aIncludeUncontrolled) {
+-      ServiceWorkerRegistrationInfo* reg = mControlledDocuments.GetWeak(doc);
+-      if (!reg || reg->mScope != aScope || !reg->GetActive() ||
+-          reg->GetActive()->ID() != aServiceWorkerID) {
+-        continue;
+-      }
+-    }
+-
+-    if (!aIncludeUncontrolled && !mControlledDocuments.Contains(doc)) {
+-      continue;
+-    }
+-
+-    docList.AppendElement(doc.forget());
+-  }
+-
+-  // The observer service gives us the list in reverse creation order.
+-  // We need to maintain creation order, so reverse the list before
+-  // processing.
+-  docList.Reverse();
+-
+-  // Finally convert to the list of ServiceWorkerClientInfo objects.
+-  uint32_t ordinal = 0;
+-  for (uint32_t i = 0; i < docList.Length(); ++i) {
+-    aDocuments.AppendElement(ServiceWorkerClientInfo(docList[i], ordinal));
+-    ordinal += 1;
+-  }
+-
+-  aDocuments.Sort();
+-}
+-
+ void
+ ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,
+                                        ServiceWorkerRegistrationInfo* aWorkerRegistration)
+ {
+   MOZ_ASSERT(aWorkerRegistration);
+   MOZ_ASSERT(aWorkerRegistration->GetActive());
+ 
+   // Same origin check
+@@ -3386,56 +3223,16 @@ ServiceWorkerManager::MaybeClaimClient(n
+   if (controllingRegistration) {
+     StopControllingADocument(controllingRegistration);
+   }
+ 
+   StartControllingADocument(aWorkerRegistration, aDocument, NS_LITERAL_STRING(""));
+   FireControllerChangeOnDocument(aDocument);
+ }
+ 
+-nsresult
+-ServiceWorkerManager::ClaimClients(nsIPrincipal* aPrincipal,
+-                                   const nsCString& aScope, uint64_t aId)
+-{
+-  RefPtr<ServiceWorkerRegistrationInfo> registration =
+-    GetRegistration(aPrincipal, aScope);
+-
+-  if (!registration || !registration->GetActive() ||
+-      !(registration->GetActive()->ID() == aId)) {
+-    // The worker is not active.
+-    return NS_ERROR_DOM_INVALID_STATE_ERR;
+-  }
+-
+-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+-  if (NS_WARN_IF(!obs)) {
+-    return NS_ERROR_FAILURE;
+-  }
+-
+-  nsCOMPtr<nsISimpleEnumerator> enumerator;
+-  nsresult rv = obs->EnumerateObservers("service-worker-get-client",
+-                                        getter_AddRefs(enumerator));
+-  if (NS_WARN_IF(NS_FAILED(rv))) {
+-    return rv;
+-  }
+-
+-  bool loop = true;
+-  while (NS_SUCCEEDED(enumerator->HasMoreElements(&loop)) && loop) {
+-    nsCOMPtr<nsISupports> ptr;
+-    rv = enumerator->GetNext(getter_AddRefs(ptr));
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      continue;
+-    }
+-
+-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
+-    MaybeClaimClient(doc, registration);
+-  }
+-
+-  return NS_OK;
+-}
+-
+ void
+ ServiceWorkerManager::SetSkipWaitingFlag(nsIPrincipal* aPrincipal,
+                                          const nsCString& aScope,
+                                          uint64_t aServiceWorkerID)
+ {
+   RefPtr<ServiceWorkerRegistrationInfo> registration =
+     GetRegistration(aPrincipal, aScope);
+   if (NS_WARN_IF(!registration)) {
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -41,17 +41,16 @@ class OriginAttributes;
+ 
+ namespace dom {
+ 
+ class ServiceWorkerRegistrar;
+ class ServiceWorkerRegistrationListener;
+ 
+ namespace workers {
+ 
+-class ServiceWorkerClientInfo;
+ class ServiceWorkerInfo;
+ class ServiceWorkerJobQueue;
+ class ServiceWorkerManagerChild;
+ class ServiceWorkerPrivate;
+ 
+ class ServiceWorkerUpdateFinishCallback
+ {
+ protected:
+@@ -268,35 +267,20 @@ public:
+               const nsString& aMessage,
+               const nsString& aFilename,
+               const nsString& aLine,
+               uint32_t aLineNumber,
+               uint32_t aColumnNumber,
+               uint32_t aFlags,
+               JSExnType aExnType);
+ 
+-  UniquePtr<ServiceWorkerClientInfo>
+-  GetClient(nsIPrincipal* aPrincipal,
+-            const nsAString& aClientId,
+-            ErrorResult& aRv);
+-
+-  void
+-  GetAllClients(nsIPrincipal* aPrincipal,
+-                const nsCString& aScope,
+-                uint64_t aServiceWorkerID,
+-                bool aIncludeUncontrolled,
+-                nsTArray<ServiceWorkerClientInfo>& aDocuments);
+-
+   void
+   MaybeClaimClient(nsIDocument* aDocument,
+                    ServiceWorkerRegistrationInfo* aWorkerRegistration);
+ 
+-  nsresult
+-  ClaimClients(nsIPrincipal* aPrincipal, const nsCString& aScope, uint64_t aId);
+-
+   void
+   SetSkipWaitingFlag(nsIPrincipal* aPrincipal, const nsCString& aScope,
+                      uint64_t aServiceWorkerID);
+ 
+   static already_AddRefed<ServiceWorkerManager>
+   GetInstance();
+ 
+   void
+diff --git a/dom/workers/ServiceWorkerManagerService.h b/dom/workers/ServiceWorkerManagerService.h
+--- a/dom/workers/ServiceWorkerManagerService.h
++++ b/dom/workers/ServiceWorkerManagerService.h
+@@ -4,16 +4,17 @@
+  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+  * You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ #ifndef mozilla_dom_ServiceWorkerManagerService_h
+ #define mozilla_dom_ServiceWorkerManagerService_h
+ 
+ #include "nsISupportsImpl.h"
+ #include "nsHashKeys.h"
++#include "nsTArray.h"
+ #include "nsTHashtable.h"
+ 
+ namespace mozilla {
+ 
+ class OriginAttributes;
+ 
+ namespace ipc {
+ class PrincipalInfo;
+diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp
+--- a/dom/workers/ServiceWorkerPrivate.cpp
++++ b/dom/workers/ServiceWorkerPrivate.cpp
+@@ -2,17 +2,16 @@
+ /* 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 "ServiceWorkerPrivate.h"
+ 
+ #include "ServiceWorkerManager.h"
+-#include "ServiceWorkerWindowClient.h"
+ #include "nsContentUtils.h"
+ #include "nsIHttpChannelInternal.h"
+ #include "nsIHttpHeaderVisitor.h"
+ #include "nsINamed.h"
+ #include "nsINetworkInterceptController.h"
+ #include "nsIPushErrorReporter.h"
+ #include "nsISupportsImpl.h"
+ #include "nsITimedChannel.h"
+@@ -20,30 +19,34 @@
+ #include "nsNetUtil.h"
+ #include "nsProxyRelease.h"
+ #include "nsQueryObject.h"
+ #include "nsStreamUtils.h"
+ #include "nsStringStream.h"
+ #include "WorkerRunnable.h"
+ #include "WorkerScope.h"
+ #include "mozilla/Assertions.h"
++#include "mozilla/dom/Client.h"
++#include "mozilla/dom/ClientIPCTypes.h"
+ #include "mozilla/dom/FetchUtil.h"
+ #include "mozilla/dom/IndexedDatabaseManager.h"
+ #include "mozilla/dom/InternalHeaders.h"
+ #include "mozilla/dom/NotificationEvent.h"
+ #include "mozilla/dom/PromiseNativeHandler.h"
+ #include "mozilla/dom/PushEventBinding.h"
+ #include "mozilla/dom/RequestBinding.h"
+ #include "mozilla/Unused.h"
+ 
+ using namespace mozilla;
+ using namespace mozilla::dom;
+ 
+ BEGIN_WORKERS_NAMESPACE
+ 
++using mozilla::ipc::PrincipalInfo;
++
+ NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(ServiceWorkerPrivate)
+ NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(ServiceWorkerPrivate)
+ NS_IMPL_CYCLE_COLLECTION(ServiceWorkerPrivate, mSupportsArray)
+ 
+ NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(ServiceWorkerPrivate, AddRef)
+ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ServiceWorkerPrivate, Release)
+ 
+ // Tracks the "dom.serviceWorkers.disable_open_click_delay" preference.  Modified
+@@ -478,32 +481,31 @@ public:
+       result.SuppressException();
+       return NS_ERROR_XPC_JS_THREW_EXCEPTION;
+     }
+ 
+     return NS_OK;
+   }
+ };
+ 
+-class SendMesssageEventRunnable final : public ExtendableEventWorkerRunnable
+-                                      , public StructuredCloneHolder
++class SendMessageEventRunnable final : public ExtendableEventWorkerRunnable
++                                     , public StructuredCloneHolder
+ {
+-  UniquePtr<ServiceWorkerClientInfo> mEventSource;
++  const ClientInfoAndState mClientInfoAndState;
+ 
+ public:
+-  SendMesssageEventRunnable(WorkerPrivate*  aWorkerPrivate,
+-                            KeepAliveToken* aKeepAliveToken,
+-                            UniquePtr<ServiceWorkerClientInfo>&& aEventSource)
++  SendMessageEventRunnable(WorkerPrivate*  aWorkerPrivate,
++                           KeepAliveToken* aKeepAliveToken,
++                           const ClientInfoAndState& aClientInfoAndState)
+     : ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken)
+     , StructuredCloneHolder(CloningSupported, TransferringSupported,
+                             StructuredCloneScope::SameProcessDifferentThread)
+-    , mEventSource(Move(aEventSource))
++    , mClientInfoAndState(aClientInfoAndState)
+   {
+     AssertIsOnMainThread();
+-    MOZ_ASSERT(mEventSource);
+   }
+ 
+   bool
+   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+   {
+     JS::Rooted<JS::Value> messageData(aCx);
+     nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope();
+     ErrorResult rv;
+@@ -512,26 +514,25 @@ public:
+       return true;
+     }
+ 
+     Sequence<OwningNonNull<MessagePort>> ports;
+     if (!TakeTransferredPortsAsSequence(ports)) {
+       return true;
+     }
+ 
+-    RefPtr<ServiceWorkerClient> client = new ServiceWorkerWindowClient(sgo,
+-                                                                       *mEventSource);
+     RootedDictionary<ExtendableMessageEventInit> init(aCx);
+ 
+     init.mBubbles = false;
+     init.mCancelable = false;
+ 
+     init.mData = messageData;
+     init.mPorts = ports;
+-    init.mSource.SetValue().SetAsClient() = client;
++    init.mSource.SetValue().SetAsClient() =
++      new Client(sgo, mClientInfoAndState);
+ 
+     RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
+     RefPtr<ExtendableMessageEvent> extendableEvent =
+       ExtendableMessageEvent::Constructor(target, NS_LITERAL_STRING("message"),
+                                           init, rv);
+     if (NS_WARN_IF(rv.Failed())) {
+       rv.SuppressException();
+       return false;
+@@ -547,17 +548,17 @@ public:
+ };
+ 
+ } // anonymous namespace
+ 
+ nsresult
+ ServiceWorkerPrivate::SendMessageEvent(JSContext* aCx,
+                                        JS::Handle<JS::Value> aMessage,
+                                        const Sequence<JSObject*>& aTransferable,
+-                                       UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
++                                       const ClientInfoAndState& aClientInfoAndState)
+ {
+   AssertIsOnMainThread();
+ 
+   ErrorResult rv(SpawnWorkerIfNeeded(MessageEvent, nullptr));
+   if (NS_WARN_IF(rv.Failed())) {
+     return rv.StealNSResult();
+   }
+ 
+@@ -565,18 +566,18 @@ ServiceWorkerPrivate::SendMessageEvent(J
+ 
+   rv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
+                                                          &transferable);
+   if (NS_WARN_IF(rv.Failed())) {
+     return rv.StealNSResult();
+   }
+ 
+   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
+-  RefPtr<SendMesssageEventRunnable> runnable =
+-    new SendMesssageEventRunnable(mWorkerPrivate, token, Move(aClientInfo));
++  RefPtr<SendMessageEventRunnable> runnable =
++    new SendMessageEventRunnable(mWorkerPrivate, token, aClientInfoAndState);
+ 
+   runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy(), rv);
+   if (NS_WARN_IF(rv.Failed())) {
+     return rv.StealNSResult();
+   }
+ 
+   if (!runnable->Dispatch()) {
+     return NS_ERROR_FAILURE;
+diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h
+--- a/dom/workers/ServiceWorkerPrivate.h
++++ b/dom/workers/ServiceWorkerPrivate.h
+@@ -13,16 +13,19 @@
+ 
+ #define NOTIFICATION_CLICK_EVENT_NAME "notificationclick"
+ #define NOTIFICATION_CLOSE_EVENT_NAME "notificationclose"
+ 
+ class nsIInterceptedChannel;
+ 
+ namespace mozilla {
+ namespace dom {
++
++class ClientInfoAndState;
++
+ namespace workers {
+ 
+ class ServiceWorkerInfo;
+ class ServiceWorkerRegistrationInfo;
+ class KeepAliveToken;
+ 
+ class LifeCycleEventCallback : public Runnable
+ {
+@@ -80,17 +83,17 @@ protected:
+   NS_DECL_OWNINGTHREAD
+ 
+ public:
+   explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo);
+ 
+   nsresult
+   SendMessageEvent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                    const Sequence<JSObject*>& aTransferable,
+-                   UniquePtr<ServiceWorkerClientInfo>&& aClientInfo);
++                   const ClientInfoAndState& aClientInfoAndState);
+ 
+   // This is used to validate the worker script and continue the installation
+   // process.
+   nsresult
+   CheckScriptEvaluation(LifeCycleEventCallback* aCallback);
+ 
+   nsresult
+   SendLifeCycleEvent(const nsAString& aEventType,
+diff --git a/dom/workers/ServiceWorkerWindowClient.cpp b/dom/workers/ServiceWorkerWindowClient.cpp
+deleted file mode 100644
+--- a/dom/workers/ServiceWorkerWindowClient.cpp
++++ /dev/null
+@@ -1,555 +0,0 @@
+-/* -*- 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 "ServiceWorkerWindowClient.h"
+-
+-#include "js/Value.h"
+-#include "mozilla/Mutex.h"
+-#include "mozilla/dom/ClientBinding.h"
+-#include "mozilla/dom/Promise.h"
+-#include "mozilla/dom/PromiseWorkerProxy.h"
+-#include "mozilla/UniquePtr.h"
+-#include "nsContentUtils.h"
+-#include "nsGlobalWindow.h"
+-#include "nsIDocShell.h"
+-#include "nsIDocShellLoadInfo.h"
+-#include "nsIDocument.h"
+-#include "nsIGlobalObject.h"
+-#include "nsIPrincipal.h"
+-#include "nsIScriptSecurityManager.h"
+-#include "nsIWebNavigation.h"
+-#include "nsIWebProgress.h"
+-#include "nsIWebProgressListener.h"
+-#include "nsString.h"
+-#include "nsWeakReference.h"
+-#include "ServiceWorker.h"
+-#include "ServiceWorkerInfo.h"
+-#include "ServiceWorkerManager.h"
+-#include "WorkerPrivate.h"
+-#include "WorkerScope.h"
+-
+-using namespace mozilla;
+-using namespace mozilla::dom;
+-using namespace mozilla::dom::workers;
+-
+-using mozilla::UniquePtr;
+-
+-JSObject*
+-ServiceWorkerWindowClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+-{
+-  return WindowClientBinding::Wrap(aCx, this, aGivenProto);
+-}
+-
+-namespace {
+-
+-class ResolveOrRejectPromiseRunnable final : public WorkerRunnable
+-{
+-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-  UniquePtr<ServiceWorkerClientInfo> mClientInfo;
+-  nsresult mRv;
+-
+-public:
+-  // Passing a null clientInfo will resolve the promise with a null value.
+-  ResolveOrRejectPromiseRunnable(
+-    WorkerPrivate* aWorkerPrivate, PromiseWorkerProxy* aPromiseProxy,
+-    UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
+-    : WorkerRunnable(aWorkerPrivate)
+-    , mPromiseProxy(aPromiseProxy)
+-    , mClientInfo(Move(aClientInfo))
+-    , mRv(NS_OK)
+-  {
+-    AssertIsOnMainThread();
+-  }
+-
+-  // Reject the promise with passed nsresult.
+-  ResolveOrRejectPromiseRunnable(WorkerPrivate* aWorkerPrivate,
+-                                 PromiseWorkerProxy* aPromiseProxy,
+-                                 nsresult aRv)
+-    : WorkerRunnable(aWorkerPrivate)
+-    , mPromiseProxy(aPromiseProxy)
+-    , mClientInfo(nullptr)
+-    , mRv(aRv)
+-  {
+-    MOZ_ASSERT(NS_FAILED(aRv));
+-    AssertIsOnMainThread();
+-  }
+-
+-  bool
+-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+-  {
+-    MOZ_ASSERT(aWorkerPrivate);
+-    aWorkerPrivate->AssertIsOnWorkerThread();
+-
+-    RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
+-    MOZ_ASSERT(promise);
+-
+-    if (NS_WARN_IF(NS_FAILED(mRv))) {
+-      promise->MaybeReject(mRv);
+-    } else if (mClientInfo) {
+-      RefPtr<ServiceWorkerWindowClient> client =
+-        new ServiceWorkerWindowClient(promise->GetParentObject(), *mClientInfo);
+-      promise->MaybeResolve(client);
+-    } else {
+-      promise->MaybeResolve(JS::NullHandleValue);
+-    }
+-
+-    // Release the reference on the worker thread.
+-    mPromiseProxy->CleanUp();
+-
+-    return true;
+-  }
+-};
+-
+-class ClientFocusRunnable final : public Runnable
+-{
+-  uint64_t mWindowId;
+-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-
+-public:
+-  ClientFocusRunnable(uint64_t aWindowId, PromiseWorkerProxy* aPromiseProxy)
+-    : mozilla::Runnable("ClientFocusRunnable")
+-    , mWindowId(aWindowId)
+-    , mPromiseProxy(aPromiseProxy)
+-  {
+-    MOZ_ASSERT(mPromiseProxy);
+-  }
+-
+-  NS_IMETHOD
+-  Run() override
+-  {
+-    AssertIsOnMainThread();
+-    nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
+-    UniquePtr<ServiceWorkerClientInfo> clientInfo;
+-
+-    if (window) {
+-      nsCOMPtr<nsIDocument> doc = window->GetDocument();
+-      if (doc) {
+-        nsContentUtils::DispatchFocusChromeEvent(window->GetOuterWindow());
+-        clientInfo.reset(new ServiceWorkerClientInfo(doc));
+-      }
+-    }
+-
+-    DispatchResult(Move(clientInfo));
+-    return NS_OK;
+-  }
+-
+-private:
+-  void
+-  DispatchResult(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
+-  {
+-    AssertIsOnMainThread();
+-    MutexAutoLock lock(mPromiseProxy->Lock());
+-    if (mPromiseProxy->CleanedUp()) {
+-      return;
+-    }
+-
+-    RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable;
+-    if (aClientInfo) {
+-      resolveRunnable = new ResolveOrRejectPromiseRunnable(
+-        mPromiseProxy->GetWorkerPrivate(), mPromiseProxy, Move(aClientInfo));
+-    } else {
+-      resolveRunnable = new ResolveOrRejectPromiseRunnable(
+-        mPromiseProxy->GetWorkerPrivate(), mPromiseProxy,
+-        NS_ERROR_DOM_INVALID_ACCESS_ERR);
+-    }
+-
+-    resolveRunnable->Dispatch();
+-  }
+-};
+-
+-} // namespace
+-
+-already_AddRefed<Promise>
+-ServiceWorkerWindowClient::Focus(ErrorResult& aRv) const
+-{
+-  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+-  MOZ_ASSERT(workerPrivate);
+-  workerPrivate->AssertIsOnWorkerThread();
+-
+-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
+-  MOZ_ASSERT(global);
+-
+-  RefPtr<Promise> promise = Promise::Create(global, aRv);
+-  if (NS_WARN_IF(aRv.Failed())) {
+-    return nullptr;
+-  }
+-
+-  if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
+-    RefPtr<PromiseWorkerProxy> promiseProxy =
+-      PromiseWorkerProxy::Create(workerPrivate, promise);
+-    if (promiseProxy) {
+-      RefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
+-                                                              promiseProxy);
+-      MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
+-    } else {
+-      promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+-    }
+-
+-  } else {
+-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+-  }
+-
+-  return promise.forget();
+-}
+-
+-class WebProgressListener final : public nsIWebProgressListener,
+-                                  public nsSupportsWeakReference
+-{
+-public:
+-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(WebProgressListener,
+-                                           nsIWebProgressListener)
+-
+-  WebProgressListener(PromiseWorkerProxy* aPromiseProxy,
+-                      ServiceWorkerPrivate* aServiceWorkerPrivate,
+-                      nsPIDOMWindowOuter* aWindow, nsIURI* aBaseURI)
+-    : mPromiseProxy(aPromiseProxy)
+-    , mServiceWorkerPrivate(aServiceWorkerPrivate)
+-    , mWindow(aWindow)
+-    , mBaseURI(aBaseURI)
+-  {
+-    MOZ_ASSERT(aPromiseProxy);
+-    MOZ_ASSERT(aServiceWorkerPrivate);
+-    MOZ_ASSERT(aWindow);
+-    MOZ_ASSERT(aWindow->IsOuterWindow());
+-    MOZ_ASSERT(aBaseURI);
+-    AssertIsOnMainThread();
+-
+-    mServiceWorkerPrivate->StoreISupports(static_cast<nsIWebProgressListener*>(this));
+-  }
+-
+-  NS_IMETHOD
+-  OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+-                uint32_t aStateFlags, nsresult aStatus) override
+-  {
+-    if (!(aStateFlags & STATE_IS_DOCUMENT) ||
+-        !(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) {
+-      return NS_OK;
+-    }
+-
+-    // This is safe because our caller holds a strong ref.
+-    mServiceWorkerPrivate->RemoveISupports(static_cast<nsIWebProgressListener*>(this));
+-    aWebProgress->RemoveProgressListener(this);
+-
+-    WorkerPrivate* workerPrivate;
+-
+-    {
+-      MutexAutoLock lock(mPromiseProxy->Lock());
+-      if (mPromiseProxy->CleanedUp()) {
+-        return NS_OK;
+-      }
+-
+-      workerPrivate = mPromiseProxy->GetWorkerPrivate();
+-    }
+-
+-    nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
+-
+-    RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable;
+-    UniquePtr<ServiceWorkerClientInfo> clientInfo;
+-    if (!doc) {
+-      resolveRunnable = new ResolveOrRejectPromiseRunnable(
+-        workerPrivate, mPromiseProxy, NS_ERROR_TYPE_ERR);
+-      resolveRunnable->Dispatch();
+-
+-      return NS_OK;
+-    }
+-
+-    // Check same origin.
+-    nsCOMPtr<nsIScriptSecurityManager> securityManager =
+-      nsContentUtils::GetSecurityManager();
+-    nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(),
+-                                                      mBaseURI, false);
+-
+-    if (NS_SUCCEEDED(rv)) {
+-      nsContentUtils::DispatchFocusChromeEvent(mWindow->GetOuterWindow());
+-      clientInfo.reset(new ServiceWorkerClientInfo(doc));
+-    }
+-
+-    resolveRunnable = new ResolveOrRejectPromiseRunnable(
+-      workerPrivate, mPromiseProxy, Move(clientInfo));
+-    resolveRunnable->Dispatch();
+-
+-    return NS_OK;
+-  }
+-
+-  NS_IMETHOD
+-  OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+-                   int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
+-                   int32_t aCurTotalProgress,
+-                   int32_t aMaxTotalProgress) override
+-  {
+-    MOZ_CRASH("Unexpected notification.");
+-    return NS_OK;
+-  }
+-
+-  NS_IMETHOD
+-  OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+-                   nsIURI* aLocation, uint32_t aFlags) override
+-  {
+-    MOZ_CRASH("Unexpected notification.");
+-    return NS_OK;
+-  }
+-
+-  NS_IMETHOD
+-  OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+-                 nsresult aStatus, const char16_t* aMessage) override
+-  {
+-    MOZ_CRASH("Unexpected notification.");
+-    return NS_OK;
+-  }
+-
+-  NS_IMETHOD
+-  OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+-                   uint32_t aState) override
+-  {
+-    MOZ_CRASH("Unexpected notification.");
+-    return NS_OK;
+-  }
+-
+-private:
+-  ~WebProgressListener() {}
+-
+-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-  RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
+-  nsCOMPtr<nsPIDOMWindowOuter> mWindow;
+-  nsCOMPtr<nsIURI> mBaseURI;
+-};
+-
+-NS_IMPL_CYCLE_COLLECTING_ADDREF(WebProgressListener)
+-NS_IMPL_CYCLE_COLLECTING_RELEASE(WebProgressListener)
+-NS_IMPL_CYCLE_COLLECTION(WebProgressListener, mPromiseProxy,
+-                         mServiceWorkerPrivate, mWindow)
+-
+-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebProgressListener)
+-  NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
+-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+-NS_INTERFACE_MAP_END
+-
+-class ClientNavigateRunnable final : public Runnable
+-{
+-  uint64_t mWindowId;
+-  nsString mUrl;
+-  nsCString mBaseUrl;
+-  nsString mScope;
+-  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+-  MOZ_INIT_OUTSIDE_CTOR WorkerPrivate* mWorkerPrivate;
+-
+-public:
+-  ClientNavigateRunnable(uint64_t aWindowId,
+-                         const nsAString& aUrl,
+-                         const nsAString& aScope,
+-                         PromiseWorkerProxy* aPromiseProxy)
+-    : mozilla::Runnable("ClientNavigateRunnable")
+-    , mWindowId(aWindowId)
+-    , mUrl(aUrl)
+-    , mScope(aScope)
+-    , mPromiseProxy(aPromiseProxy)
+-    , mWorkerPrivate(nullptr)
+-  {
+-    MOZ_ASSERT(aPromiseProxy);
+-    MOZ_ASSERT(aPromiseProxy->GetWorkerPrivate());
+-    aPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
+-  }
+-
+-  NS_IMETHOD
+-  Run() override
+-  {
+-    AssertIsOnMainThread();
+-
+-    nsCOMPtr<nsIPrincipal> principal;
+-
+-    {
+-      MutexAutoLock lock(mPromiseProxy->Lock());
+-      if (mPromiseProxy->CleanedUp()) {
+-        return NS_OK;
+-      }
+-
+-      mWorkerPrivate = mPromiseProxy->GetWorkerPrivate();
+-      WorkerPrivate::LocationInfo& info = mWorkerPrivate->GetLocationInfo();
+-      mBaseUrl = info.mHref;
+-      principal = mWorkerPrivate->GetPrincipal();
+-      MOZ_DIAGNOSTIC_ASSERT(principal);
+-    }
+-
+-    nsCOMPtr<nsIURI> baseUrl;
+-    nsCOMPtr<nsIURI> url;
+-    nsresult rv = ParseUrl(getter_AddRefs(baseUrl), getter_AddRefs(url));
+-
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return RejectPromise(NS_ERROR_TYPE_ERR);
+-    }
+-
+-    nsGlobalWindow* window;
+-    rv = Navigate(url, principal, &window);
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return RejectPromise(rv);
+-    }
+-
+-    nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
+-    nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
+-    if (NS_WARN_IF(!webProgress)) {
+-      return NS_ERROR_FAILURE;
+-    }
+-
+-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+-    if (!swm) {
+-      return NS_ERROR_FAILURE;
+-    }
+-
+-    RefPtr<ServiceWorkerRegistrationInfo> registration =
+-      swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope));
+-    if (NS_WARN_IF(!registration)) {
+-      return NS_ERROR_FAILURE;
+-    }
+-    RefPtr<ServiceWorkerInfo> serviceWorkerInfo =
+-      registration->GetServiceWorkerInfoById(mWorkerPrivate->ServiceWorkerID());
+-    if (NS_WARN_IF(!serviceWorkerInfo)) {
+-      return NS_ERROR_FAILURE;
+-    }
+-
+-    nsCOMPtr<nsIWebProgressListener> listener =
+-      new WebProgressListener(mPromiseProxy, serviceWorkerInfo->WorkerPrivate(),
+-                              window->GetOuterWindow(), baseUrl);
+-
+-    rv = webProgress->AddProgressListener(
+-      listener, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
+-
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return RejectPromise(rv);
+-    }
+-
+-    return NS_OK;
+-  }
+-
+-private:
+-  nsresult
+-  RejectPromise(nsresult aRv)
+-  {
+-    MOZ_ASSERT(mWorkerPrivate);
+-    RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
+-      new ResolveOrRejectPromiseRunnable(mWorkerPrivate, mPromiseProxy, aRv);
+-
+-    resolveRunnable->Dispatch();
+-    return NS_OK;
+-  }
+-
+-  nsresult
+-  ResolvePromise(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
+-  {
+-    MOZ_ASSERT(mWorkerPrivate);
+-    RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
+-      new ResolveOrRejectPromiseRunnable(mWorkerPrivate, mPromiseProxy,
+-                                         Move(aClientInfo));
+-
+-    resolveRunnable->Dispatch();
+-    return NS_OK;
+-  }
+-
+-  nsresult
+-  ParseUrl(nsIURI** aBaseUrl, nsIURI** aUrl)
+-  {
+-    MOZ_ASSERT(aBaseUrl);
+-    MOZ_ASSERT(aUrl);
+-    AssertIsOnMainThread();
+-
+-    nsCOMPtr<nsIURI> baseUrl;
+-    nsresult rv = NS_NewURI(getter_AddRefs(baseUrl), mBaseUrl);
+-    NS_ENSURE_SUCCESS(rv, rv);
+-
+-    nsCOMPtr<nsIURI> url;
+-    rv = NS_NewURI(getter_AddRefs(url), mUrl, nullptr, baseUrl);
+-    NS_ENSURE_SUCCESS(rv, rv);
+-
+-    baseUrl.forget(aBaseUrl);
+-    url.forget(aUrl);
+-
+-    return NS_OK;
+-  }
+-
+-  nsresult
+-  Navigate(nsIURI* aUrl, nsIPrincipal* aPrincipal, nsGlobalWindow** aWindow)
+-  {
+-    MOZ_ASSERT(aWindow);
+-
+-    nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
+-    if (NS_WARN_IF(!window)) {
+-      return NS_ERROR_TYPE_ERR;
+-    }
+-
+-    nsCOMPtr<nsIDocument> doc = window->GetDocument();
+-    if (NS_WARN_IF(!doc)) {
+-      return NS_ERROR_TYPE_ERR;
+-    }
+-
+-    if (NS_WARN_IF(!doc->IsActive())) {
+-      return NS_ERROR_TYPE_ERR;
+-    }
+-
+-    nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
+-    if (NS_WARN_IF(!docShell)) {
+-      return NS_ERROR_TYPE_ERR;
+-    }
+-
+-    nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
+-    nsresult rv = docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return NS_ERROR_TYPE_ERR;
+-    }
+-
+-    loadInfo->SetTriggeringPrincipal(aPrincipal);
+-    loadInfo->SetReferrerPolicy(doc->GetReferrerPolicy());
+-    loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContent);
+-    loadInfo->SetSourceDocShell(docShell);
+-    rv =
+-      docShell->LoadURI(aUrl, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, true);
+-
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return NS_ERROR_TYPE_ERR;
+-    }
+-
+-    *aWindow = window;
+-    return NS_OK;
+-  }
+-};
+-
+-already_AddRefed<Promise>
+-ServiceWorkerWindowClient::Navigate(const nsAString& aUrl, ErrorResult& aRv)
+-{
+-  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+-  MOZ_ASSERT(workerPrivate);
+-  workerPrivate->AssertIsOnWorkerThread();
+-
+-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
+-  MOZ_ASSERT(global);
+-
+-  RefPtr<Promise> promise = Promise::Create(global, aRv);
+-  if (NS_WARN_IF(aRv.Failed())) {
+-    return nullptr;
+-  }
+-
+-  if (aUrl.EqualsLiteral("about:blank")) {
+-    promise->MaybeReject(NS_ERROR_TYPE_ERR);
+-    return promise.forget();
+-  }
+-
+-  ServiceWorkerGlobalScope* globalScope =
+-    static_cast<ServiceWorkerGlobalScope*>(workerPrivate->GlobalScope());
+-  nsString scope;
+-  globalScope->GetScope(scope);
+-
+-  RefPtr<PromiseWorkerProxy> promiseProxy =
+-    PromiseWorkerProxy::Create(workerPrivate, promise);
+-  if (promiseProxy) {
+-    RefPtr<ClientNavigateRunnable> r =
+-      new ClientNavigateRunnable(mWindowId, aUrl, scope, promiseProxy);
+-    MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
+-  } else {
+-    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+-  }
+-
+-  return promise.forget();
+-}
+diff --git a/dom/workers/ServiceWorkerWindowClient.h b/dom/workers/ServiceWorkerWindowClient.h
+deleted file mode 100644
+--- a/dom/workers/ServiceWorkerWindowClient.h
++++ /dev/null
+@@ -1,64 +0,0 @@
+-/* -*- 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 mozilla_dom_workers_serviceworkerwindowclient_h
+-#define mozilla_dom_workers_serviceworkerwindowclient_h
+-
+-#include "ServiceWorkerClient.h"
+-
+-namespace mozilla {
+-namespace dom {
+-
+-class Promise;
+-
+-namespace workers {
+-
+-class ServiceWorkerWindowClient final : public ServiceWorkerClient
+-{
+-public:
+-  ServiceWorkerWindowClient(nsISupports* aOwner,
+-                            const ServiceWorkerClientInfo& aClientInfo)
+-    : ServiceWorkerClient(aOwner, aClientInfo),
+-      mVisibilityState(aClientInfo.mVisibilityState),
+-      mFocused(aClientInfo.mFocused)
+-  {
+-  }
+-
+-  virtual JSObject*
+-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+-
+-  mozilla::dom::VisibilityState
+-  VisibilityState() const
+-  {
+-    return mVisibilityState;
+-  }
+-
+-  bool
+-  Focused() const
+-  {
+-    return mFocused;
+-  }
+-
+-  already_AddRefed<Promise>
+-  Focus(ErrorResult& aRv) const;
+-
+-  already_AddRefed<Promise>
+-  Navigate(const nsAString& aUrl,  ErrorResult& aRv);
+-
+-private:
+-  ~ServiceWorkerWindowClient()
+-  { }
+-
+-  mozilla::dom::VisibilityState mVisibilityState;
+-  bool mFocused;
+-};
+-
+-} // namespace workers
+-} // namespace dom
+-} // namespace mozilla
+-
+-#endif // mozilla_dom_workers_serviceworkerwindowclient_h
+diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp
+--- a/dom/workers/WorkerScope.cpp
++++ b/dom/workers/WorkerScope.cpp
+@@ -4,16 +4,17 @@
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ #include "WorkerScope.h"
+ 
+ #include "jsapi.h"
+ #include "mozilla/EventListenerManager.h"
+ #include "mozilla/dom/BindingDeclarations.h"
++#include "mozilla/dom/Clients.h"
+ #include "mozilla/dom/Console.h"
+ #include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h"
+ #include "mozilla/dom/Fetch.h"
+ #include "mozilla/dom/FunctionBinding.h"
+ #include "mozilla/dom/IDBFactory.h"
+ #include "mozilla/dom/ImageBitmap.h"
+ #include "mozilla/dom/ImageBitmapBinding.h"
+ #include "mozilla/dom/Performance.h"
+@@ -39,17 +40,16 @@
+ #endif
+ 
+ #include "Crypto.h"
+ #include "Principal.h"
+ #include "RuntimeService.h"
+ #include "ScriptLoader.h"
+ #include "WorkerPrivate.h"
+ #include "WorkerRunnable.h"
+-#include "ServiceWorkerClients.h"
+ #include "ServiceWorkerManager.h"
+ #include "ServiceWorkerRegistration.h"
+ 
+ #ifdef XP_WIN
+ #undef PostMessage
+ #endif
+ 
+ extern already_AddRefed<nsIScriptTimeoutHandler>
+@@ -630,24 +630,25 @@ ServiceWorkerGlobalScope::WrapGlobalObje
+   JS::CompartmentOptions options;
+   mWorkerPrivate->CopyJSCompartmentOptions(options);
+ 
+   return ServiceWorkerGlobalScopeBinding::Wrap(aCx, this, this, options,
+                                                GetWorkerPrincipal(),
+                                                true, aReflector);
+ }
+ 
+-ServiceWorkerClients*
+-ServiceWorkerGlobalScope::Clients()
++already_AddRefed<Clients>
++ServiceWorkerGlobalScope::GetClients()
+ {
+   if (!mClients) {
+-    mClients = new ServiceWorkerClients(this);
++    mClients = new Clients(this);
+   }
+ 
+-  return mClients;
++  RefPtr<Clients> ref = mClients;
++  return ref.forget();
+ }
+ 
+ ServiceWorkerRegistration*
+ ServiceWorkerGlobalScope::Registration()
+ {
+   if (!mRegistration) {
+     mRegistration =
+       ServiceWorkerRegistration::CreateForWorker(mWorkerPrivate, mScope);
+diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h
+--- a/dom/workers/WorkerScope.h
++++ b/dom/workers/WorkerScope.h
+@@ -14,16 +14,17 @@
+ #include "nsWeakReference.h"
+ #include "mozilla/dom/ImageBitmapSource.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ class AnyCallback;
+ struct ChannelPixelLayout;
++class Clients;
+ class Console;
+ class Crypto;
+ class Function;
+ class IDBFactory;
+ enum class ImageBitmapFormat : uint8_t;
+ class Performance;
+ class Promise;
+ class RequestOrUSVString;
+@@ -35,17 +36,16 @@ enum class CallerType : uint32_t;
+ namespace cache {
+ 
+ class CacheStorage;
+ 
+ } // namespace cache
+ 
+ namespace workers {
+ 
+-class ServiceWorkerClients;
+ class WorkerPrivate;
+ 
+ } // namespace workers
+ 
+ class WorkerGlobalScope : public DOMEventTargetHelper,
+                           public nsIGlobalObject,
+                           public nsSupportsWeakReference
+ {
+@@ -277,17 +277,17 @@ public:
+   Close(JSContext* aCx);
+ 
+   IMPL_EVENT_HANDLER(connect)
+ };
+ 
+ class ServiceWorkerGlobalScope final : public WorkerGlobalScope
+ {
+   const nsString mScope;
+-  RefPtr<workers::ServiceWorkerClients> mClients;
++  RefPtr<Clients> mClients;
+   RefPtr<ServiceWorkerRegistration> mRegistration;
+ 
+   ~ServiceWorkerGlobalScope();
+ 
+ public:
+   NS_DECL_ISUPPORTS_INHERITED
+   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerGlobalScope,
+                                            WorkerGlobalScope)
+@@ -301,18 +301,18 @@ public:
+                    JS::MutableHandle<JSObject*> aReflector) override;
+ 
+   void
+   GetScope(nsString& aScope) const
+   {
+     aScope = mScope;
+   }
+ 
+-  workers::ServiceWorkerClients*
+-  Clients();
++  already_AddRefed<Clients>
++  GetClients();
+ 
+   ServiceWorkerRegistration*
+   Registration();
+ 
+   already_AddRefed<Promise>
+   SkipWaiting(ErrorResult& aRv);
+ 
+   IMPL_EVENT_HANDLER(activate)
+diff --git a/dom/workers/moz.build b/dom/workers/moz.build
+--- a/dom/workers/moz.build
++++ b/dom/workers/moz.build
+@@ -32,19 +32,16 @@ EXPORTS.mozilla.dom.workers += [
+     'ServiceWorkerRegistrationInfo.h',
+     'WorkerDebuggerManager.h',
+     'Workers.h',
+ ]
+ 
+ # Stuff needed for the bindings, not really public though.
+ EXPORTS.mozilla.dom.workers.bindings += [
+     'ServiceWorker.h',
+-    'ServiceWorkerClient.h',
+-    'ServiceWorkerClients.h',
+-    'ServiceWorkerWindowClient.h',
+     'SharedWorker.h',
+     'WorkerHolder.h',
+     'WorkerHolderToken.h',
+ ]
+ 
+ XPIDL_MODULE = 'dom_workers'
+ 
+ XPIDL_SOURCES += [
+@@ -55,18 +52,16 @@ XPIDL_SOURCES += [
+ UNIFIED_SOURCES += [
+     'ChromeWorkerScope.cpp',
+     'FileReaderSync.cpp',
+     'Principal.cpp',
+     'RegisterBindings.cpp',
+     'RuntimeService.cpp',
+     'ScriptLoader.cpp',
+     'ServiceWorker.cpp',
+-    'ServiceWorkerClient.cpp',
+-    'ServiceWorkerClients.cpp',
+     'ServiceWorkerContainer.cpp',
+     'ServiceWorkerDescriptor.cpp',
+     'ServiceWorkerEvents.cpp',
+     'ServiceWorkerInfo.cpp',
+     'ServiceWorkerJob.cpp',
+     'ServiceWorkerJobQueue.cpp',
+     'ServiceWorkerManager.cpp',
+     'ServiceWorkerManagerChild.cpp',
+@@ -77,17 +72,16 @@ UNIFIED_SOURCES += [
+     'ServiceWorkerRegistrar.cpp',
+     'ServiceWorkerRegistration.cpp',
+     'ServiceWorkerRegistrationInfo.cpp',
+     'ServiceWorkerScriptCache.cpp',
+     'ServiceWorkerUnregisterJob.cpp',
+     'ServiceWorkerUpdateJob.cpp',
+     'ServiceWorkerUpdaterChild.cpp',
+     'ServiceWorkerUpdaterParent.cpp',
+-    'ServiceWorkerWindowClient.cpp',
+     'SharedWorker.cpp',
+     'WorkerDebuggerManager.cpp',
+     'WorkerHolder.cpp',
+     'WorkerHolderToken.cpp',
+     'WorkerLocation.cpp',
+     'WorkerNavigator.cpp',
+     'WorkerPrivate.cpp',
+     'WorkerRunnable.cpp',

+ 802 - 0
mozilla-release/patches/1293277-6-59a1.patch

@@ -0,0 +1,802 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513111488 18000
+# Node ID baa02d97591ea55e6b9ee1867061c3e9e0324021
+# Parent  67e07652637165e46c9d5ed312634d52a62bd303
+Bug 1293277 P6 Use the ClientInfo.Id() value for FetchEvent.clientId. r=baku
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -15156,28 +15156,23 @@ nsDocShell::ChannelIntercepted(nsIInterc
+   nsCOMPtr<nsIDocument> doc;
+ 
+   bool isSubresourceLoad = !nsContentUtils::IsNonSubresourceRequest(channel);
+   if (isSubresourceLoad) {
+     doc = GetDocument();
+     if (!doc) {
+       return NS_ERROR_NOT_AVAILABLE;
+     }
+-  } else {
+-    // For top-level navigations, save a document ID which will be passed to
+-    // the FetchEvent as the clientId later on.
+-    rv = nsIDocument::GenerateDocumentId(mInterceptedDocumentId);
+-    NS_ENSURE_SUCCESS(rv, rv);
+   }
+ 
+   bool isReload = mLoadType & LOAD_CMD_RELOAD;
+ 
+   ErrorResult error;
+-  swm->DispatchFetchEvent(mOriginAttributes, doc, mInterceptedDocumentId,
+-                          aChannel, isReload, isSubresourceLoad, error);
++  swm->DispatchFetchEvent(mOriginAttributes, doc, aChannel, isReload,
++                          isSubresourceLoad, error);
+   if (NS_WARN_IF(error.Failed())) {
+     return error.StealNSResult();
+   }
+ 
+   return NS_OK;
+ }
+ 
+ bool
+diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
+--- a/docshell/base/nsDocShell.h
++++ b/docshell/base/nsDocShell.h
+@@ -366,21 +366,16 @@ public:
+   const mozilla::OriginAttributes&
+   GetOriginAttributes()
+   {
+     return mOriginAttributes;
+   }
+ 
+   nsresult SetOriginAttributes(const mozilla::OriginAttributes& aAttrs);
+ 
+-  void GetInterceptedDocumentId(nsAString& aId)
+-  {
+-    aId = mInterceptedDocumentId;
+-  }
+-
+ private:
+   // An observed docshell wrapper is created when recording markers is enabled.
+   mozilla::UniquePtr<mozilla::ObservedDocShell> mObserved;
+ 
+   // It is necessary to allow adding a timeline marker wherever a docshell
+   // instance is available. This operation happens frequently and needs to
+   // be very fast, so instead of using a Map or having to search for some
+   // docshell-specific markers storage, a pointer to an `ObservedDocShell` is
+@@ -1142,18 +1137,16 @@ protected:
+ 
+   // This represents the state of private browsing in the docshell.
+   // Currently treated as a binary value: 1 - in private mode, 0 - not private mode
+   // On content docshells mPrivateBrowsingId == mOriginAttributes.mPrivateBrowsingId
+   // On chrome docshells this value will be set, but not have the corresponding
+   // origin attribute set.
+   uint32_t mPrivateBrowsingId;
+ 
+-  nsString mInterceptedDocumentId;
+-
+   // This represents the CSS display-mode we are currently using.
+   // It can be any of the following values from nsIDocShell.idl:
+   //
+   // DISPLAY_MODE_BROWSER = 0
+   // DISPLAY_MODE_MINIMAL_UI = 1
+   // DISPLAY_MODE_STANDALONE = 2
+   // DISPLAY_MODE_FULLSCREEN = 3
+   //
+diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
+--- a/dom/base/nsDocument.cpp
++++ b/dom/base/nsDocument.cpp
+@@ -1707,17 +1707,16 @@ NS_INTERFACE_TABLE_HEAD(nsDocument)
+     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentXBL)
+     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal)
+     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMEventTarget)
+     NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget)
+     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference)
+     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
+     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
+     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
+-    NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIObserver)
+     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMXPathEvaluator)
+   NS_INTERFACE_TABLE_END
+   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument)
+ NS_INTERFACE_MAP_END
+ 
+ 
+ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
+ NS_IMETHODIMP_(MozExternalRefCountType)
+@@ -4915,38 +4914,16 @@ nsDocument::SetScriptGlobalObject(nsIScr
+         // of bfcache.  Clear our state to allow that to happen.  Only
+         // clear this flag if we are actually controlled, though, so pages
+         // that were force reloaded don't become controlled when they
+         // come out of bfcache.
+         mMaybeServiceWorkerControlled = false;
+       }
+       swm->MaybeStopControlling(this);
+     }
+-
+-    // Remove ourself from the list of clients.  We only register
+-    // content principal documents in this list.
+-    if (!nsContentUtils::IsSystemPrincipal(GetPrincipal()) &&
+-        !GetPrincipal()->GetIsNullPrincipal()) {
+-      nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+-      if (os) {
+-        os->RemoveObserver(this, "service-worker-get-client");
+-      }
+-    }
+-
+-  } else if (!mScriptGlobalObject && aScriptGlobalObject &&
+-             mDocumentContainer && GetChannel() &&
+-             !nsContentUtils::IsSystemPrincipal(GetPrincipal()) &&
+-             !GetPrincipal()->GetIsNullPrincipal()) {
+-    // This document is being activated.  Register it in the list of
+-    // clients.  We only do this for content principal documents
+-    // since we can never observe system or null principals.
+-    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+-    if (os) {
+-      os->AddObserver(this, "service-worker-get-client", /* ownsWeak */ false);
+-    }
+   }
+ 
+   // BlockOnload() might be called before mScriptGlobalObject is set.
+   // We may need to add the blocker once mScriptGlobalObject is set.
+   bool needOnloadBlocker = !mScriptGlobalObject && aScriptGlobalObject;
+ 
+   mScriptGlobalObject = aScriptGlobalObject;
+ 
+@@ -5057,25 +5034,17 @@ nsDocument::SetScriptGlobalObject(nsIScr
+     // If we are shift-reloaded, don't associate with a ServiceWorker.
+     if (IsForceReloadType(loadType)) {
+       NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
+       return;
+     }
+ 
+     nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
+     if (swm) {
+-      // If this document is being resurrected from the bfcache, then we may
+-      // already have a document ID.  In that case reuse the same ID.  Otherwise
+-      // get our document ID from the docshell.
+-      nsString documentId(GetId());
+-      if (documentId.IsEmpty()) {
+-        static_cast<nsDocShell*>(docShell.get())->GetInterceptedDocumentId(documentId);
+-      }
+-
+-      swm->MaybeStartControlling(this, documentId);
++      swm->MaybeStartControlling(this);
+       mMaybeServiceWorkerControlled = true;
+     }
+   }
+ }
+ 
+ nsIScriptGlobalObject*
+ nsDocument::GetScriptHandlingObjectInternal() const
+ {
+@@ -12206,42 +12175,16 @@ nsIDocument::GetPointerLockElement()
+     do_QueryReferent(EventStateManager::sPointerLockedDoc);
+   if (pointerLockedDoc != this) {
+     return nullptr;
+   }
+ 
+   return pointerLockedElement;
+ }
+ 
+-nsresult
+-nsDocument::Observe(nsISupports *aSubject,
+-                    const char *aTopic,
+-                    const char16_t *aData)
+-{
+-  if (strcmp("service-worker-get-client", aTopic) == 0) {
+-    // No need to generate the ID if it doesn't exist here.  The ID being
+-    // requested must already be generated in order to passed in as
+-    // aSubject.
+-    nsString clientId = GetId();
+-    if (!clientId.IsEmpty() && clientId.Equals(aData)) {
+-      nsCOMPtr<nsISupportsInterfacePointer> ifptr = do_QueryInterface(aSubject);
+-      if (ifptr) {
+-#ifdef DEBUG
+-        nsCOMPtr<nsISupports> value;
+-        MOZ_ALWAYS_SUCCEEDS(ifptr->GetData(getter_AddRefs(value)));
+-        MOZ_ASSERT(!value);
+-#endif
+-        ifptr->SetData(static_cast<nsIDocument*>(this));
+-        ifptr->SetDataIID(&NS_GET_IID(nsIDocument));
+-      }
+-    }
+-  }
+-  return NS_OK;
+-}
+-
+ void
+ nsDocument::UpdateVisibilityState()
+ {
+   dom::VisibilityState oldState = mVisibilityState;
+   mVisibilityState = GetVisibilityState();
+   if (oldState != mVisibilityState) {
+     nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
+                                          NS_LITERAL_STRING("visibilitychange"),
+@@ -12848,59 +12791,16 @@ nsIDocument::CreateHTMLElement(nsIAtom* 
+   DebugOnly<nsresult> rv = NS_NewHTMLElement(getter_AddRefs(element),
+                                              nodeInfo.forget(),
+                                              mozilla::dom::NOT_FROM_PARSER);
+ 
+   MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail");
+   return element.forget();
+ }
+ 
+-/* static */
+-nsresult
+-nsIDocument::GenerateDocumentId(nsAString& aId)
+-{
+-  nsID id;
+-  nsresult rv = nsContentUtils::GenerateUUIDInPlace(id);
+-  if (NS_WARN_IF(NS_FAILED(rv))) {
+-    return rv;
+-  }
+-
+-  // Build a string in {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} format
+-  char buffer[NSID_LENGTH];
+-  id.ToProvidedString(buffer);
+-  NS_ConvertASCIItoUTF16 uuid(buffer);
+-
+-  // Remove {} and the null terminator
+-  aId.Assign(Substring(uuid, 1, NSID_LENGTH - 3));
+-  return NS_OK;
+-}
+-
+-nsresult
+-nsIDocument::GetOrCreateId(nsAString& aId)
+-{
+-  if (mId.IsEmpty()) {
+-    nsresult rv = GenerateDocumentId(mId);
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return rv;
+-    }
+-  }
+-
+-  aId = mId;
+-  return NS_OK;
+-}
+-
+-void
+-nsIDocument::SetId(const nsAString& aId)
+-{
+-  // The ID should only be set one time, but we may get the same value
+-  // more than once if the document is controlled coming out of bfcache.
+-  MOZ_ASSERT_IF(mId != aId, mId.IsEmpty());
+-  mId = aId;
+-}
+-
+ bool
+ MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
+ {
+   nsCOMArray<nsIDocument>* documents =
+     static_cast<nsCOMArray<nsIDocument>*>(aData);
+   if (aDoc) {
+     aDoc->SetIsInSyncOperation(true);
+     if (nsCOMPtr<nsPIDOMWindowInner> window = aDoc->GetInnerWindow()) {
+diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h
+--- a/dom/base/nsDocument.h
++++ b/dom/base/nsDocument.h
+@@ -320,17 +320,16 @@ protected:
+ class nsDocument : public nsIDocument,
+                    public nsIDOMDocument,
+                    public nsIDOMDocumentXBL,
+                    public nsSupportsWeakReference,
+                    public nsIScriptObjectPrincipal,
+                    public nsIRadioGroupContainer,
+                    public nsIApplicationCacheContainer,
+                    public nsStubMutationObserver,
+-                   public nsIObserver,
+                    public nsIDOMXPathEvaluator
+ {
+   friend class nsIDocument;
+ 
+ public:
+   typedef mozilla::dom::Element Element;
+   typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
+ 
+@@ -663,19 +662,16 @@ public:
+     GetExistingListenerManager() const override;
+ 
+   // nsIScriptObjectPrincipal
+   virtual nsIPrincipal* GetPrincipal() override;
+ 
+   // nsIApplicationCacheContainer
+   NS_DECL_NSIAPPLICATIONCACHECONTAINER
+ 
+-  // nsIObserver
+-  NS_DECL_NSIOBSERVER
+-
+   NS_DECL_NSIDOMXPATHEVALUATOR
+ 
+   virtual nsresult Init();
+ 
+   virtual already_AddRefed<Element> CreateElem(const nsAString& aName,
+                                                nsIAtom* aPrefix,
+                                                int32_t aNamespaceID,
+                                                const nsAString* aIs = nullptr) override;
+diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h
+--- a/dom/base/nsIDocument.h
++++ b/dom/base/nsIDocument.h
+@@ -1049,20 +1049,16 @@ public:
+    * inserted anonymous content (in other words, the clone of the aElement
+    * that was passed to InsertAnonymousContent).
+    */
+   Element* GetAnonRootIfInAnonymousContentContainer(nsINode* aNode) const;
+   nsTArray<RefPtr<mozilla::dom::AnonymousContent>>& GetAnonymousContents() {
+     return mAnonymousContents;
+   }
+ 
+-  static nsresult GenerateDocumentId(nsAString& aId);
+-  nsresult GetOrCreateId(nsAString& aId);
+-  void SetId(const nsAString& aId);
+-
+   mozilla::TimeStamp GetPageUnloadingEventTimeStamp() const
+   {
+     if (!mParentDocument) {
+       return mPageUnloadingEventTimeStamp;
+     }
+ 
+     mozilla::TimeStamp parentTimeStamp(mParentDocument->GetPageUnloadingEventTimeStamp());
+     if (parentTimeStamp.IsNull()) {
+@@ -3273,21 +3269,16 @@ protected:
+ 
+   mozilla::dom::XPathEvaluator* XPathEvaluator();
+ 
+   void HandleRebuildUserFontSet() {
+     mPostedFlushUserFontSet = false;
+     FlushUserFontSet();
+   }
+ 
+-  const nsString& GetId() const
+-  {
+-    return mId;
+-  }
+-
+   // Update our frame request callback scheduling state, if needed.  This will
+   // schedule or unschedule them, if necessary, and update
+   // mFrameRequestCallbacksScheduled.  aOldShell should only be passed when
+   // mPresShell is becoming null; in that case it will be used to get hold of
+   // the relevant refresh driver.
+   void UpdateFrameRequestCallbackSchedulingState(nsIPresShell* aOldShell = nullptr);
+ 
+   // Helper for GetScrollingElement/IsScrollingElement.
+@@ -3631,17 +3622,16 @@ protected:
+   uint32_t mSandboxFlags;
+ 
+   nsCString mContentLanguage;
+ 
+   // The channel that got passed to nsDocument::StartDocumentLoad(), if any.
+   nsCOMPtr<nsIChannel> mChannel;
+ private:
+   nsCString mContentType;
+-  nsString mId;
+ protected:
+ 
+   // The document's security info
+   nsCOMPtr<nsISupports> mSecurityInfo;
+ 
+   // The channel that failed to load and resulted in an error page.
+   // This only applies to error pages. Might be null.
+   nsCOMPtr<nsIChannel> mFailedChannel;
+diff --git a/dom/interfaces/base/nsIServiceWorkerManager.idl b/dom/interfaces/base/nsIServiceWorkerManager.idl
+--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
++++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
+@@ -143,17 +143,17 @@ interface nsIServiceWorkerManager : nsIS
+                                                               in DOMString aScope);
+ 
+   /**
+    * Call this to request that document `aDoc` be controlled by a ServiceWorker
+    * if a registration exists for it's scope.
+    *
+    * This MUST only be called once per document!
+    */
+-  [notxpcom,nostdcall] void MaybeStartControlling(in nsIDocument aDoc, in DOMString aDocumentId);
++  [notxpcom,nostdcall] void MaybeStartControlling(in nsIDocument aDoc);
+ 
+   /**
+    * Documents that have called MaybeStartControlling() should call this when
+    * they are destroyed. This function may be called multiple times, and is
+    * idempotent.
+    */
+   [notxpcom,nostdcall] void MaybeStopControlling(in nsIDocument aDoc);
+ 
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -2296,26 +2296,25 @@ ServiceWorkerManager::MaybeRemoveRegistr
+     if (entry.Data()->mOrderedScopes.IsEmpty() &&
+         entry.Data()->mJobQueues.Count() == 0) {
+       entry.Remove();
+     }
+   }
+ }
+ 
+ void
+-ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc,
+-                                            const nsAString& aDocumentId)
++ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc)
+ {
+   AssertIsOnMainThread();
+   MOZ_ASSERT(aDoc);
+   RefPtr<ServiceWorkerRegistrationInfo> registration =
+     GetServiceWorkerRegistrationInfo(aDoc);
+   if (registration) {
+     MOZ_ASSERT(!mControlledDocuments.Contains(aDoc));
+-    StartControllingADocument(registration, aDoc, aDocumentId);
++    StartControllingADocument(registration, aDoc);
+   }
+ }
+ 
+ void
+ ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
+ {
+   AssertIsOnMainThread();
+   MOZ_ASSERT(aDoc);
+@@ -2347,34 +2346,30 @@ ServiceWorkerManager::MaybeCheckNavigati
+   mControlledDocuments.Get(aDoc, getter_AddRefs(registration));
+   if (registration) {
+     registration->MaybeScheduleUpdate();
+   }
+ }
+ 
+ RefPtr<GenericPromise>
+ ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
+-                                                nsIDocument* aDoc,
+-                                                const nsAString& aDocumentId)
++                                                nsIDocument* aDoc)
+ {
+   MOZ_ASSERT(aRegistration);
+   MOZ_ASSERT(aDoc);
+ 
+ #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+   auto storageAllowed = nsContentUtils::StorageAllowedForDocument(aDoc);
+   MOZ_DIAGNOSTIC_ASSERT(storageAllowed == nsContentUtils::StorageAccess::eAllow);
+ #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ 
+   RefPtr<GenericPromise> ref = GenericPromise::CreateAndResolve(true, __func__);
+ 
+   aRegistration->StartControllingADocument();
+   mControlledDocuments.Put(aDoc, aRegistration);
+-  if (!aDocumentId.IsEmpty()) {
+-    aDoc->SetId(aDocumentId);
+-  }
+ 
+   // Mark the document's ClientSource as controlled using the ClientHandle
+   // interface.  While we could get at the ClientSource directly from the
+   // document here, our goal is to move ServiceWorkerManager to a separate
+   // process.  Using the ClientHandle supports this remote operation.
+   ServiceWorkerInfo* activeWorker = aRegistration->GetActive();
+   nsPIDOMWindowInner* innerWindow = aDoc->GetInnerWindow();
+   if (activeWorker && innerWindow) {
+@@ -2558,30 +2553,27 @@ ServiceWorkerManager::GetServiceWorkerFo
+ 
+ namespace {
+ 
+ class ContinueDispatchFetchEventRunnable : public Runnable
+ {
+   RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
+   nsCOMPtr<nsIInterceptedChannel> mChannel;
+   nsCOMPtr<nsILoadGroup> mLoadGroup;
+-  nsString mDocumentId;
+   bool mIsReload;
+ public:
+   ContinueDispatchFetchEventRunnable(
+     ServiceWorkerPrivate* aServiceWorkerPrivate,
+     nsIInterceptedChannel* aChannel,
+     nsILoadGroup* aLoadGroup,
+-    const nsAString& aDocumentId,
+     bool aIsReload)
+     : Runnable("dom::workers::ContinueDispatchFetchEventRunnable")
+     , mServiceWorkerPrivate(aServiceWorkerPrivate)
+     , mChannel(aChannel)
+     , mLoadGroup(aLoadGroup)
+-    , mDocumentId(aDocumentId)
+     , mIsReload(aIsReload)
+   {
+     MOZ_ASSERT(aServiceWorkerPrivate);
+     MOZ_ASSERT(aChannel);
+   }
+ 
+   void
+   HandleError()
+@@ -2612,70 +2604,72 @@ public:
+     // if that happens.
+     nsresult status;
+     rv = channel->GetStatus(&status);
+     if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(status))) {
+       HandleError();
+       return NS_OK;
+     }
+ 
+-    rv = mServiceWorkerPrivate->SendFetchEvent(mChannel, mLoadGroup,
+-                                               mDocumentId, mIsReload);
++    nsString clientId;
++    nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
++    if (loadInfo) {
++      Maybe<ClientInfo> clientInfo = loadInfo->GetClientInfo();
++      if (clientInfo.isSome()) {
++        char buf[NSID_LENGTH];
++        clientInfo.ref().Id().ToProvidedString(buf);
++        CopyUTF8toUTF16(nsDependentCString(buf), clientId);
++      }
++    }
++
++    rv = mServiceWorkerPrivate->SendFetchEvent(mChannel, mLoadGroup, clientId,
++                                               mIsReload);
+     if (NS_WARN_IF(NS_FAILED(rv))) {
+       HandleError();
+     }
+ 
+     return NS_OK;
+   }
+ };
+ 
+ } // anonymous namespace
+ 
+ void
+ ServiceWorkerManager::DispatchFetchEvent(const OriginAttributes& aOriginAttributes,
+                                          nsIDocument* aDoc,
+-                                         const nsAString& aDocumentIdForTopLevelNavigation,
+                                          nsIInterceptedChannel* aChannel,
+                                          bool aIsReload,
+                                          bool aIsSubresourceLoad,
+                                          ErrorResult& aRv)
+ {
+   MOZ_ASSERT(aChannel);
+   AssertIsOnMainThread();
+ 
+   RefPtr<ServiceWorkerInfo> serviceWorker;
+   nsCOMPtr<nsILoadGroup> loadGroup;
+-  nsAutoString documentId;
+ 
+   if (aIsSubresourceLoad) {
+     MOZ_ASSERT(aDoc);
+ 
+     serviceWorker = GetActiveWorkerInfoForDocument(aDoc);
+     if (!serviceWorker) {
+       aRv.Throw(NS_ERROR_FAILURE);
+       return;
+     }
+ 
+     loadGroup = aDoc->GetDocumentLoadGroup();
+-    nsresult rv = aDoc->GetOrCreateId(documentId);
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return;
+-    }
+   } else {
+     nsCOMPtr<nsIChannel> internalChannel;
+     aRv = aChannel->GetChannel(getter_AddRefs(internalChannel));
+     if (NS_WARN_IF(aRv.Failed())) {
+       return;
+     }
+ 
+     internalChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+ 
+-    // TODO: Use aDocumentIdForTopLevelNavigation for potentialClientId, pending
+-    // the spec change.
+-
+     nsCOMPtr<nsIURI> uri;
+     aRv = aChannel->GetSecureUpgradedChannelURI(getter_AddRefs(uri));
+     if (NS_WARN_IF(aRv.Failed())) {
+       return;
+     }
+ 
+     // non-subresource request means the URI contains the principal
+     nsCOMPtr<nsIPrincipal> principal =
+@@ -2745,18 +2739,17 @@ ServiceWorkerManager::DispatchFetchEvent
+   if (NS_WARN_IF(aRv.Failed())) {
+     return;
+   }
+ 
+   MOZ_DIAGNOSTIC_ASSERT(serviceWorker);
+ 
+   nsCOMPtr<nsIRunnable> continueRunnable =
+     new ContinueDispatchFetchEventRunnable(serviceWorker->WorkerPrivate(),
+-                                           aChannel, loadGroup,
+-                                           documentId, aIsReload);
++                                           aChannel, loadGroup, aIsReload);
+ 
+   // When this service worker was registered, we also sent down the permissions
+   // for the runnable. They should have arrived by now, but we still need to
+   // wait for them if they have not.
+   nsCOMPtr<nsIRunnable> permissionsRunnable = NS_NewRunnableFunction(
+     "dom::workers::ServiceWorkerManager::DispatchFetchEvent", [=]() {
+       nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
+       MOZ_ALWAYS_SUCCEEDS(permMgr->WhenPermissionsAvailable(serviceWorker->Principal(),
+@@ -3219,17 +3212,17 @@ ServiceWorkerManager::MaybeClaimClient(n
+         aWorkerRegistration == controllingRegistration) {
+     return;
+   }
+ 
+   if (controllingRegistration) {
+     StopControllingADocument(controllingRegistration);
+   }
+ 
+-  StartControllingADocument(aWorkerRegistration, aDocument, NS_LITERAL_STRING(""));
++  StartControllingADocument(aWorkerRegistration, aDocument);
+   FireControllerChangeOnDocument(aDocument);
+ }
+ 
+ void
+ ServiceWorkerManager::SetSkipWaitingFlag(nsIPrincipal* aPrincipal,
+                                          const nsCString& aScope,
+                                          uint64_t aServiceWorkerID)
+ {
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -145,17 +145,16 @@ public:
+   // reasons without having to worry about shutdown races with the worker.
+   bool
+   MayHaveActiveServiceWorkerInstance(ContentParent* aContent,
+                                      nsIPrincipal* aPrincipal);
+ 
+   void
+   DispatchFetchEvent(const OriginAttributes& aOriginAttributes,
+                      nsIDocument* aDoc,
+-                     const nsAString& aDocumentIdForTopLevelNavigation,
+                      nsIInterceptedChannel* aChannel,
+                      bool aIsReload,
+                      bool aIsSubresourceLoad,
+                      ErrorResult& aRv);
+ 
+   void
+   Update(nsIPrincipal* aPrincipal,
+          const nsACString& aScope,
+@@ -372,18 +371,17 @@ private:
+   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
+                                             WhichServiceWorker aWhichOnes);
+ 
+   void
+   NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+   RefPtr<GenericPromise>
+   StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
+-                            nsIDocument* aDoc,
+-                            const nsAString& aDocumentId);
++                            nsIDocument* aDoc);
+ 
+   void
+   StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+   already_AddRefed<ServiceWorkerRegistrationInfo>
+   GetServiceWorkerRegistrationInfo(nsPIDOMWindowInner* aWindow);
+ 
+   already_AddRefed<ServiceWorkerRegistrationInfo>
+diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp
+--- a/dom/workers/ServiceWorkerPrivate.cpp
++++ b/dom/workers/ServiceWorkerPrivate.cpp
+@@ -1344,24 +1344,24 @@ class FetchEventRunnable : public Extend
+ public:
+   FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
+                      KeepAliveToken* aKeepAliveToken,
+                      nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
+                      // CSP checks might require the worker script spec
+                      // later on.
+                      const nsACString& aScriptSpec,
+                      nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
+-                     const nsAString& aDocumentId,
++                     const nsAString& aClientId,
+                      bool aIsReload,
+                      bool aMarkLaunchServiceWorkerEnd)
+     : ExtendableFunctionalEventWorkerRunnable(
+         aWorkerPrivate, aKeepAliveToken, aRegistration)
+     , mInterceptedChannel(aChannel)
+     , mScriptSpec(aScriptSpec)
+-    , mClientId(aDocumentId)
++    , mClientId(aClientId)
+     , mIsReload(aIsReload)
+     , mMarkLaunchServiceWorkerEnd(aMarkLaunchServiceWorkerEnd)
+     , mCacheMode(RequestCache::Default)
+     , mRequestMode(RequestMode::No_cors)
+     , mRequestRedirect(RequestRedirect::Follow)
+     // By default we set it to same-origin since normal HTTP fetches always
+     // send credentials to same-origin websites unless explicitly forbidden.
+     , mRequestCredentials(RequestCredentials::Same_origin)
+@@ -1613,17 +1613,21 @@ private:
+ 
+     MOZ_ASSERT_IF(internalReq->IsNavigationRequest(),
+                   request->Redirect() == RequestRedirect::Manual);
+ 
+     RootedDictionary<FetchEventInit> init(aCx);
+     init.mRequest = request;
+     init.mBubbles = false;
+     init.mCancelable = true;
+-    if (!mClientId.IsEmpty()) {
++    // Only expose the FetchEvent.clientId on subresource requests for now.
++    // Once we implement .resultingClientId and .targetClientId we can then
++    // start exposing .clientId on non-subresource requests as well.  See
++    // bug 1264177.
++    if (!mClientId.IsEmpty() && !internalReq->IsNavigationRequest()) {
+       init.mClientId = mClientId;
+     }
+     init.mIsReload = mIsReload;
+     RefPtr<FetchEvent> event =
+       FetchEvent::Constructor(globalObj, NS_LITERAL_STRING("fetch"), init, result);
+     if (NS_WARN_IF(result.Failed())) {
+       result.SuppressException();
+       return false;
+@@ -1659,18 +1663,17 @@ private:
+ 
+ NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor)
+ 
+ } // anonymous namespace
+ 
+ nsresult
+ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
+                                      nsILoadGroup* aLoadGroup,
+-                                     const nsAString& aDocumentId,
+-                                     bool aIsReload)
++                                     const nsAString& aClientId, bool aIsReload)
+ {
+   AssertIsOnMainThread();
+ 
+   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+   if (NS_WARN_IF(!mInfo || !swm)) {
+     return NS_ERROR_FAILURE;
+   }
+ 
+@@ -1737,17 +1740,17 @@ ServiceWorkerPrivate::SendFetchEvent(nsI
+       "ServiceWorkerRegistrationInfoProxy", registration, false));
+ 
+   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
+ 
+ 
+   RefPtr<FetchEventRunnable> r =
+     new FetchEventRunnable(mWorkerPrivate, token, handle,
+                            mInfo->ScriptSpec(), regInfo,
+-                           aDocumentId, aIsReload, newWorkerCreated);
++                           aClientId, aIsReload, newWorkerCreated);
+   rv = r->Init();
+   if (NS_WARN_IF(NS_FAILED(rv))) {
+     return rv;
+   }
+ 
+   if (mInfo->State() == ServiceWorkerState::Activating) {
+     mPendingFunctionalEvents.AppendElement(r.forget());
+     return NS_OK;
+@@ -1886,17 +1889,17 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede
+   jsapi.Init();
+   ErrorResult error;
+   NS_ConvertUTF8toUTF16 scriptSpec(mInfo->ScriptSpec());
+ 
+   mWorkerPrivate = WorkerPrivate::Constructor(jsapi.cx(),
+                                               scriptSpec,
+                                               false, WorkerTypeService,
+                                               VoidString(),
+-                                              mInfo->Scope(),
++                                              EmptyCString(),
+                                               &info, error);
+   if (NS_WARN_IF(error.Failed())) {
+     return error.StealNSResult();
+   }
+ 
+   RenewKeepAliveToken(aWhy);
+ 
+   if (aNewWorkerCreated) {
+diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h
+--- a/dom/workers/ServiceWorkerPrivate.h
++++ b/dom/workers/ServiceWorkerPrivate.h
+@@ -117,20 +117,18 @@ public:
+                         const nsAString& aBody,
+                         const nsAString& aTag,
+                         const nsAString& aIcon,
+                         const nsAString& aData,
+                         const nsAString& aBehavior,
+                         const nsAString& aScope);
+ 
+   nsresult
+-  SendFetchEvent(nsIInterceptedChannel* aChannel,
+-                 nsILoadGroup* aLoadGroup,
+-                 const nsAString& aDocumentId,
+-                 bool aIsReload);
++  SendFetchEvent(nsIInterceptedChannel* aChannel, nsILoadGroup* aLoadGroup,
++                 const nsAString& aClientId, bool aIsReload);
+ 
+   void
+   StoreISupports(nsISupports* aSupports);
+ 
+   void
+   RemoveISupports(nsISupports* aSupports);
+ 
+   // This will terminate the current running worker thread and drop the

+ 407 - 0
mozilla-release/patches/1293277-7no8-59a1.patch

@@ -0,0 +1,407 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513111488 18000
+# Node ID 3a2388a414a4792d0f0bf598df9f8d0b2b391ee6
+# Parent  d7fcad674dddc084a8d9aebe89ff983d0b88c0bd
+Bug 1293277 P7 Make ServiceWorkerManager use ClientHandle::Control() for claiming and ClientSource::SetController() fire the controller change event. r=baku
+
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -356,16 +356,33 @@ ClientSource::SetController(const Servic
+   NS_ASSERT_OWNINGTHREAD(ClientSource);
+ 
+   if (mController.isSome() && mController.ref() == aServiceWorker) {
+     return;
+   }
+ 
+   mController.reset();
+   mController.emplace(aServiceWorker);
++
++  RefPtr<ServiceWorkerContainer> swc;
++  nsPIDOMWindowInner* window = GetInnerWindow();
++  if (window) {
++    RefPtr<Navigator> navigator =
++      static_cast<Navigator*>(window->GetNavigator());
++    if (navigator) {
++      swc = navigator->ServiceWorker();
++    }
++  }
++
++  // TODO: Also self.navigator.serviceWorker on workers when its exposed there
++
++  if (swc && nsContentUtils::IsSafeToRunScript()) {
++    IgnoredErrorResult ignored;
++    swc->ControllerChanged(ignored);
++  }
+ }
+ 
+ RefPtr<ClientOpPromise>
+ ClientSource::Control(const ClientControlledArgs& aArgs)
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientSource);
+ 
+   SetController(ServiceWorkerDescriptor(aArgs.serviceWorker()));
+@@ -550,21 +567,49 @@ ClientSource::PostMessage(const ClientPo
+ 
+   ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__);
+   return ref.forget();
+ }
+ 
+ RefPtr<ClientOpPromise>
+ ClientSource::Claim(const ClientClaimArgs& aArgs)
+ {
+-  SetController(ServiceWorkerDescriptor(aArgs.serviceWorker()));
++  RefPtr<ClientOpPromise> ref;
++
++  ServiceWorkerDescriptor swd(aArgs.serviceWorker());
+ 
+-  RefPtr<ClientOpPromise> ref =
+-    ClientOpPromise::CreateAndResolve(NS_OK, __func__);
++  // Today the ServiceWorkerManager maintains its own list of
++  // nsIDocument objects controlled by each service worker.  We
++  // need to try to update that data structure for now.  If we
++  // can't, however, then simply mark the Client as controlled.
++  // In the future this will be enough for the SWM as well since
++  // it will eventually hold ClientHandle objects instead of
++  // nsIDocuments.
++  nsPIDOMWindowInner* innerWindow = GetInnerWindow();
++  nsIDocument* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
++  RefPtr<ServiceWorkerManager> swm = doc ? ServiceWorkerManager::GetInstance()
++                                         : nullptr;
++  if (!swm || !doc) {
++    SetController(swd);
++    ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__);
++    return ref.forget();
++  }
+ 
++  RefPtr<ClientOpPromise::Private> outerPromise =
++    new ClientOpPromise::Private(__func__);
++
++  RefPtr<GenericPromise> p = swm->MaybeClaimClient(doc, swd);
++  p->Then(mEventTarget, __func__,
++    [outerPromise] (bool aResult) {
++      outerPromise->Resolve(NS_OK, __func__);
++    }, [outerPromise] (nsresult aResult) {
++      outerPromise->Reject(aResult, __func__);
++    });
++
++  ref = outerPromise;
+   return ref.forget();
+ }
+ 
+ RefPtr<ClientOpPromise>
+ ClientSource::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs)
+ {
+   RefPtr<ClientOpPromise> ref;
+ 
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -2833,32 +2833,52 @@ ServiceWorkerManager::GetDocumentRegistr
+ NS_IMETHODIMP
+ ServiceWorkerManager::GetDocumentController(nsPIDOMWindowInner* aWindow,
+                                             nsISupports** aServiceWorker)
+ {
+   if (NS_WARN_IF(!aWindow)) {
+     return NS_ERROR_DOM_INVALID_STATE_ERR;
+   }
+ 
++  Maybe<ServiceWorkerDescriptor> controller = aWindow->GetController();
++  if (controller.isNothing()) {
++    return NS_ERROR_DOM_INVALID_STATE_ERR;
++  }
++
+   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
+-  if (!doc) {
++  if (NS_WARN_IF(!doc)) {
+     return NS_ERROR_DOM_INVALID_STATE_ERR;
+   }
+ 
+-  RefPtr<ServiceWorkerRegistrationInfo> registration;
+-  nsresult rv = GetDocumentRegistration(doc, getter_AddRefs(registration));
++  nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
++  if (NS_WARN_IF(!principal)) {
++    return NS_ERROR_DOM_INVALID_STATE_ERR;
++  }
++
++  nsAutoCString scopeKey;
++  nsresult rv = PrincipalToScopeKey(principal, scopeKey);
+   if (NS_WARN_IF(NS_FAILED(rv))) {
+     return rv;
+   }
+ 
+-  MOZ_ASSERT(registration->GetActive());
+-  RefPtr<ServiceWorker> serviceWorker =
+-    registration->GetActive()->GetOrCreateInstance(aWindow);
+-
++  RefPtr<ServiceWorkerRegistrationInfo> registration =
++    GetRegistration(scopeKey, controller.ref().Scope());
++  if (NS_WARN_IF(!registration)) {
++    return NS_ERROR_DOM_INVALID_STATE_ERR;
++  }
++
++  RefPtr<ServiceWorkerInfo> active = registration->GetActive();
++  if (NS_WARN_IF(!active) ||
++      NS_WARN_IF(active->Descriptor().Id() != controller.ref().Id())) {
++    return NS_ERROR_DOM_INVALID_STATE_ERR;
++  }
++
++  RefPtr<ServiceWorker> serviceWorker = active->GetOrCreateInstance(aWindow);
+   serviceWorker.forget(aServiceWorker);
++
+   return NS_OK;
+ }
+ 
+ NS_IMETHODIMP
+ ServiceWorkerManager::GetInstalling(nsPIDOMWindowInner* aWindow,
+                                     const nsAString& aScope,
+                                     nsISupports** aServiceWorker)
+ {
+@@ -3153,77 +3173,75 @@ ServiceWorkerManager::UpdateInternal(nsI
+                                registration->GetUpdateViaCache());
+ 
+   RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback);
+   job->AppendResultCallback(cb);
+ 
+   queue->ScheduleJob(job);
+ }
+ 
+-namespace {
+-
+-static void
+-FireControllerChangeOnDocument(nsIDocument* aDocument)
+-{
+-  AssertIsOnMainThread();
+-  MOZ_ASSERT(aDocument);
+-
+-  nsCOMPtr<nsPIDOMWindowInner> w = aDocument->GetInnerWindow();
+-  if (!w) {
+-    NS_WARNING("Failed to dispatch controllerchange event");
+-    return;
+-  }
+-
+-  auto* window = nsGlobalWindow::Cast(w.get());
+-  dom::Navigator* navigator = window->Navigator();
+-  if (!navigator) {
+-    return;
+-  }
+-
+-  RefPtr<ServiceWorkerContainer> container = navigator->ServiceWorker();
+-  ErrorResult result;
+-  container->ControllerChanged(result);
+-  if (result.Failed()) {
+-    NS_WARNING("Failed to dispatch controllerchange event");
+-  }
+-}
+-
+-} // anonymous namespace
+-
+-void
++already_AddRefed<GenericPromise>
+ ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,
+                                        ServiceWorkerRegistrationInfo* aWorkerRegistration)
+ {
+   MOZ_ASSERT(aWorkerRegistration);
+   MOZ_ASSERT(aWorkerRegistration->GetActive());
+ 
++  RefPtr<GenericPromise> ref;
++
+   // Same origin check
+   if (!aWorkerRegistration->mPrincipal->Equals(aDocument->NodePrincipal())) {
+-    return;
++    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_SECURITY_ERR, __func__);
++    return ref.forget();
+   }
+ 
+   // The registration that should be controlling the client
+   RefPtr<ServiceWorkerRegistrationInfo> matchingRegistration =
+     GetServiceWorkerRegistrationInfo(aDocument);
+ 
+   // The registration currently controlling the client
+   RefPtr<ServiceWorkerRegistrationInfo> controllingRegistration;
+   GetDocumentRegistration(aDocument, getter_AddRefs(controllingRegistration));
+ 
+   if (aWorkerRegistration != matchingRegistration ||
+-        aWorkerRegistration == controllingRegistration) {
+-    return;
++      aWorkerRegistration == controllingRegistration) {
++    ref = GenericPromise::CreateAndResolve(true, __func__);
++    return ref.forget();
+   }
+ 
+   if (controllingRegistration) {
+     StopControllingADocument(controllingRegistration);
+   }
+ 
+-  StartControllingADocument(aWorkerRegistration, aDocument);
+-  FireControllerChangeOnDocument(aDocument);
++  ref = StartControllingADocument(aWorkerRegistration, aDocument);
++  return ref.forget();
++}
++
++already_AddRefed<GenericPromise>
++ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDoc,
++                                       const ServiceWorkerDescriptor& aServiceWorker)
++{
++  RefPtr<GenericPromise> ref;
++
++  nsCOMPtr<nsIPrincipal> principal =
++    PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
++  if (!principal) {
++    ref = GenericPromise::CreateAndResolve(false, __func__);
++    return ref.forget();
++  }
++
++  RefPtr<ServiceWorkerRegistrationInfo> registration =
++    GetRegistration(principal, aServiceWorker.Scope());
++  if (!registration) {
++    ref = GenericPromise::CreateAndResolve(false, __func__);
++    return ref.forget();
++  }
++
++  ref = MaybeClaimClient(aDoc, registration);
++  return ref.forget();
+ }
+ 
+ void
+ ServiceWorkerManager::SetSkipWaitingFlag(nsIPrincipal* aPrincipal,
+                                          const nsCString& aScope,
+                                          uint64_t aServiceWorkerID)
+ {
+   RefPtr<ServiceWorkerRegistrationInfo> registration =
+@@ -3242,38 +3260,52 @@ ServiceWorkerManager::SetSkipWaitingFlag
+   worker->SetSkipWaitingFlag();
+ 
+   if (worker->State() == ServiceWorkerState::Installed) {
+     registration->TryToActivateAsync();
+   }
+ }
+ 
+ void
+-ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
++ServiceWorkerManager::UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration)
+ {
+   AssertIsOnMainThread();
+ 
+-  AutoTArray<nsCOMPtr<nsIDocument>, 16> documents;
++  RefPtr<ServiceWorkerInfo> activeWorker = aRegistration->GetActive();
++  MOZ_DIAGNOSTIC_ASSERT(activeWorker);
++
++  AutoTArray<nsCOMPtr<nsPIDOMWindowInner>, 16> innerWindows;
+   for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
+     if (iter.UserData() != aRegistration) {
+       continue;
+     }
+ 
+     nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
+     if (NS_WARN_IF(!doc)) {
+       continue;
+     }
+ 
+-    documents.AppendElement(doc);
++    nsPIDOMWindowInner* innerWindow = doc->GetInnerWindow();
++    if (NS_WARN_IF(!innerWindow)) {
++      continue;
++    }
++
++    innerWindows.AppendElement(innerWindow);
+   }
+ 
+   // Fire event after iterating mControlledDocuments is done to prevent
+   // modification by reentering from the event handlers during iteration.
+-  for (auto& doc : documents) {
+-    FireControllerChangeOnDocument(doc);
++  for (auto& innerWindow : innerWindows) {
++    Maybe<ClientInfo> clientInfo = innerWindow->GetClientInfo();
++    if (clientInfo.isSome()) {
++      RefPtr<ClientHandle> clientHandle =
++        ClientManager::CreateHandle(clientInfo.ref(),
++                                    innerWindow->EventTargetFor(TaskCategory::Other));
++      clientHandle->Control(activeWorker->Descriptor());
++    }
+   }
+ }
+ 
+ already_AddRefed<ServiceWorkerRegistrationInfo>
+ ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal,
+                                       const nsACString& aScope) const
+ {
+   MOZ_ASSERT(aPrincipal);
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -266,20 +266,24 @@ public:
+               const nsString& aMessage,
+               const nsString& aFilename,
+               const nsString& aLine,
+               uint32_t aLineNumber,
+               uint32_t aColumnNumber,
+               uint32_t aFlags,
+               JSExnType aExnType);
+ 
+-  void
++  already_AddRefed<GenericPromise>
+   MaybeClaimClient(nsIDocument* aDocument,
+                    ServiceWorkerRegistrationInfo* aWorkerRegistration);
+ 
++  already_AddRefed<GenericPromise>
++  MaybeClaimClient(nsIDocument* aDoc,
++                   const ServiceWorkerDescriptor& aServiceWorker);
++
+   void
+   SetSkipWaitingFlag(nsIPrincipal* aPrincipal, const nsCString& aScope,
+                      uint64_t aServiceWorkerID);
+ 
+   static already_AddRefed<ServiceWorkerManager>
+   GetInstance();
+ 
+   void
+@@ -418,17 +422,17 @@ private:
+   void
+   QueueFireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration,
+                                              const nsAString& aName);
+ 
+   void
+   FireUpdateFoundOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+   void
+-  FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration);
++  UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+   void
+   StorePendingReadyPromise(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
+                            Promise* aPromise);
+ 
+   bool
+   CheckReadyPromise(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
+                     Promise* aPromise);
+diff --git a/dom/workers/ServiceWorkerRegistrationInfo.cpp b/dom/workers/ServiceWorkerRegistrationInfo.cpp
+--- a/dom/workers/ServiceWorkerRegistrationInfo.cpp
++++ b/dom/workers/ServiceWorkerRegistrationInfo.cpp
+@@ -269,23 +269,18 @@ ServiceWorkerRegistrationInfo::Activate(
+     return;
+   }
+ 
+   TransitionWaitingToActive();
+ 
+   // FIXME(nsm): Unlink appcache if there is one.
+ 
+   // "Queue a task to fire a simple event named controllerchange..."
+-  nsCOMPtr<nsIRunnable> controllerChangeRunnable =
+-    NewRunnableMethod<RefPtr<ServiceWorkerRegistrationInfo>>(
+-      "dom::workers::ServiceWorkerManager::FireControllerChange",
+-      swm,
+-      &ServiceWorkerManager::FireControllerChange,
+-      this);
+-  NS_DispatchToMainThread(controllerChangeRunnable);
++  MOZ_DIAGNOSTIC_ASSERT(mActiveWorker);
++  swm->UpdateClientControllers(this);
+ 
+   nsCOMPtr<nsIRunnable> failRunnable = NewRunnableMethod<bool>(
+     "dom::workers::ServiceWorkerRegistrationInfo::FinishActivate",
+     this,
+     &ServiceWorkerRegistrationInfo::FinishActivate,
+     false /* success */);
+ 
+   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle(

+ 26 - 0
mozilla-release/patches/1293277-9-59a1.patch

@@ -0,0 +1,26 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513111488 18000
+# Node ID ec647e2200163d7e37f22c7c9fb471250beb5540
+# Parent  976c21cebdc08b6ec7e51e9e43917ca849dd2d60
+Bug 1293277 P9 Disable browser_service_workers_push_service.js on non-e10s due to races. r=jryans
+
+diff --git a/devtools/client/aboutdebugging/test/browser.ini b/devtools/client/aboutdebugging/test/browser.ini
+--- a/devtools/client/aboutdebugging/test/browser.ini
++++ b/devtools/client/aboutdebugging/test/browser.ini
+@@ -42,14 +42,15 @@ tags = webextensions
+ [browser_service_workers.js]
+ [browser_service_workers_fetch_flag.js]
+ skip-if = os == 'mac' # bug 1333759
+ [browser_service_workers_multi_content_process.js]
+ skip-if = !e10s # This test is only valid in e10s
+ [browser_service_workers_not_compatible.js]
+ [browser_service_workers_push.js]
+ [browser_service_workers_push_service.js]
++skip-if = !e10s # Bug 1424895
+ [browser_service_workers_start.js]
+ [browser_service_workers_status.js]
+ [browser_service_workers_timeout.js]
+ skip-if = true # Bug 1232931
+ [browser_service_workers_unregister.js]
+ [browser_tabs.js]

+ 2 - 2
mozilla-release/patches/1340901-87a1.patch

@@ -3,7 +3,7 @@
 # Date 1611682034 0
 #      Tue Jan 26 17:27:14 2021 +0000
 # Node ID 2252ab087d8039acf6a5231155270dbef28205db
-# Parent  905c4bd465611986cdd62cdf44e3a5bcc6246200
+# Parent  ea4892894d8fa3d194e57d60573d6745c46a00f8
 Bug 1340901 - Update Snappy to version 1.1.8. r=dom-workers-and-storage-reviewers,asuth
 
 Add a static assertion in IndexedDB to detect future updates.
@@ -26,12 +26,12 @@ diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp
 +
  using namespace mozilla::dom::quota;
  using namespace mozilla::ipc;
+ using mozilla::dom::quota::Client;
  
  namespace {
  
  class ConnectionPool;
  class Cursor;
- class Database;
 diff --git a/other-licenses/snappy/README b/other-licenses/snappy/README
 --- a/other-licenses/snappy/README
 +++ b/other-licenses/snappy/README

+ 4 - 4
mozilla-release/patches/1393609-60a1.patch

@@ -2,7 +2,7 @@
 # User ewhite7 <ewhite7@myseneca.ca>
 # Date 1515206033 18000
 # Node ID 1684292955256a9392ed2ef153e3e51d10d706a5
-# Parent  c4b9b71f90dc0f06b83e39d732ae4f8adc7b1393
+# Parent  c694d3f0dff422c094bc05906303ed74e6bfd9a3
 Bug 1393609 - Add a test for line-height in console.log custom style; r=nchevobbe.
 
 MozReview-Commit-ID: BD2fjv6u4b2
@@ -166,13 +166,13 @@ diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/c
    },
    "groupId": null,
    "exceptionDocURL": null,
-@@ -2538,18 +2538,18 @@ stubPackets.set(`console.log(%cfoobar)`,
-     "counter": null,
+@@ -2573,18 +2573,18 @@ stubPackets.set(`console.log(%cfoobar)`,
      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
      "functionName": "triggerPacket",
      "groupName": "",
      "level": "log",
      "lineNumber": 2,
+     "prefix": "",
      "private": false,
      "styles": [
 -      "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
@@ -187,7 +187,7 @@ diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/c
    }
  });
  
-@@ -2656,17 +2656,17 @@ stubPackets.set(`console.groupEnd(%cfoo%
+@@ -2695,17 +2695,17 @@ stubPackets.set(`console.groupEnd(%cfoo%
  stubPackets.set(`console.dir({C, M, Y, K})`, {
    "from": "server1.conn0.child1/consoleActor2",
    "type": "consoleAPICall",

+ 31 - 0
mozilla-release/patches/1415056-1-59a1.patch

@@ -0,0 +1,31 @@
+# HG changeset patch
+# User Gijs Kruitbosch <gijskruitbosch@gmail.com>
+# Date 1512389710 0
+# Node ID 8f0234c0ffb70c97ce628b25d9cd3e0ae84deacf
+# Parent  745dc6d3cc2dbf2cb9e2b235b575f57de6aca566
+Bug 1415056 - re-enable animations in the palette in customize mode, r=jaws
+
+MozReview-Commit-ID: DXYBPCdXMBG
+
+diff --git a/browser/components/customizableui/CustomizeMode.jsm b/browser/components/customizableui/CustomizeMode.jsm
+--- a/browser/components/customizableui/CustomizeMode.jsm
++++ b/browser/components/customizableui/CustomizeMode.jsm
+@@ -1795,17 +1795,17 @@ CustomizeMode.prototype = {
+     }
+ 
+     // Do nothing if the widget is not allowed to move to the target area.
+     if (targetArea.id != kPaletteId &&
+         !CustomizableUI.canWidgetMoveToArea(draggedItemId, targetArea.id)) {
+       return;
+     }
+ 
+-    let targetAreaType = CustomizableUI.getAreaType(targetArea.id);
++    let targetAreaType = CustomizableUI.getPlaceForItem(targetArea);
+     let targetNode = this._getDragOverNode(aEvent, targetArea, targetAreaType, draggedItemId);
+ 
+     // We need to determine the place that the widget is being dropped in
+     // the target.
+     let dragOverItem, dragValue;
+     if (targetNode == targetArea.customizationTarget) {
+       // We'll assume if the user is dragging directly over the target, that
+       // they're attempting to append a child to that target.

+ 33 - 0
mozilla-release/patches/1415056-2-59a1.patch

@@ -0,0 +1,33 @@
+# HG changeset patch
+# User Gijs Kruitbosch <gijskruitbosch@gmail.com>
+# Date 1512390699 0
+# Node ID 7ed8bedfc83cf65bb55097def9052d705b497294
+# Parent  d06ac6c5957ea9dcec4fb452208a5df8dc9961d4
+Bug 1415056 - reverse direction of horizontal shift in RTL when shifting palette items in customize mode, r=jaws
+
+This also fixes the vertical offset because we take the correct branch in the yDiff if block lower down.
+
+MozReview-Commit-ID: KbHkRx1575X
+
+diff --git a/browser/components/customizableui/DragPositionManager.jsm b/browser/components/customizableui/DragPositionManager.jsm
+--- a/browser/components/customizableui/DragPositionManager.jsm
++++ b/browser/components/customizableui/DragPositionManager.jsm
+@@ -275,17 +275,17 @@ AreaPositionManager.prototype = {
+       }
+     } else {
+       // We don't have a sibling whose position we can use. First, let's see
+       // if we're also the first item (which complicates things):
+       let firstNode = this._firstInRow(aNode);
+       if (aNode == firstNode) {
+         // Maybe we stored the horizontal distance between non-wide nodes,
+         // if not, we'll use the width of the incoming node as a proxy:
+-        xDiff = this._horizontalDistance || aSize.width;
++        xDiff = this._horizontalDistance || (this._dir == "ltr" ? 1 : -1) * aSize.width;
+       } else {
+         // If not, we should be able to get the distance to the previous node
+         // and use the inverse, unless there's no room for another node (ie we
+         // are the last node and there's no room for another one)
+         xDiff = this._moveNextBasedOnPrevious(aNode, nodeBounds, firstNode);
+       }
+     }
+ 

+ 507 - 0
mozilla-release/patches/1418376-59a1.patch

@@ -0,0 +1,507 @@
+# HG changeset patch
+# User Tom Tung <shes050117@gmail.com>
+# Date 1511918995 -28800
+# Node ID eae544da26b6ea225e3f402c1bd62085cf1c7ec1
+# Parent  cc14dcfc8219f59012368671e0614e2c13c8c838
+Bug 1418376: Report to the console if a service worker is blocked because the storage is not fully allow. r=bkelly
+
+diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties
+--- a/dom/locales/en-US/chrome/dom/dom.properties
++++ b/dom/locales/en-US/chrome/dom/dom.properties
+@@ -221,16 +221,24 @@ InterceptedNonResponseWithURL=Failed to load ‘%1$S’. A ServiceWorker passed a promise to FetchEvent.respondWith() that resolved with non-Response value ‘%2$S’.
+ # LOCALIZATION NOTE: Do not translate "mozImageSmoothingEnabled", or "imageSmoothingEnabled"
+ PrefixedImageSmoothingEnabledWarning=Use of mozImageSmoothingEnabled is deprecated. Please use the unprefixed imageSmoothingEnabled property instead.
+ # LOCALIZATION NOTE: Do not translate "ServiceWorker", "Service-Worker-Allowed" or "HTTP". %1$S and %2$S are URLs.
+ ServiceWorkerScopePathMismatch=Failed to register a ServiceWorker: The path of the provided scope ‘%1$S’ is not under the max scope allowed ‘%2$S’. Adjust the scope, move the Service Worker script, or use the Service-Worker-Allowed HTTP header to allow the scope.
+ # LOCALIZATION NOTE: Do not translate "ServiceWorker". %1$S is a URL representing the scope of the ServiceWorker, %2$S is a stringified numeric HTTP status code like "404" and %3$S is a URL.
+ ServiceWorkerRegisterNetworkError=Failed to register/update a ServiceWorker for scope ‘%1$S’: Load failed with status %2$S for script ‘%3$S’.
+ # LOCALIZATION NOTE: Do not translate "ServiceWorker". %1$S is a URL representing the scope of the ServiceWorker, %2$S is a MIME Media Type like "text/plain" and %3$S is a URL.
+ ServiceWorkerRegisterMimeTypeError=Failed to register/update a ServiceWorker for scope ‘%1$S’: Bad Content-Type of ‘%2$S’ received for script ‘%3$S’.  Must be ‘text/javascript’, ‘application/x-javascript’, or ‘application/javascript’.
++# LOCALIZATION NOTE: Do not translate "ServiceWorker". %S is a URL representing the scope of the ServiceWorker.
++ServiceWorkerRegisterStorageError=Failed to register/update a ServiceWorker for scope ‘%S’: Storage access is restricted in this context due to user settings or private browsing mode.
++# LOCALIZATION NOTE: Do not translate "ServiceWorker".
++ServiceWorkerGetRegistrationStorageError=Failed to get service worker registration(s): Storage access is restricted in this context due to user settings or private browsing mode.
++# LOCALIZATION NOTE: Do not translate "ServiceWorker".
++ServiceWorkerGetClientStorageError=Failed to get service worker’s client(s): Storage access is restricted in this context due to user settings or private browsing mode.
++# LOCALIZATION NOTE: Do not translate "ServiceWorker". %S is a URL representing the scope of the ServiceWorker.
++ServiceWorkerPostMessageStorageError=The ServiceWorker for scope ‘%S’ fail to postMessage because storage access is restricted in this context due to user settings or private browsing mode.
+ # LOCALIZATION NOTE: Do not translate "ServiceWorker". %1$S is a URL representing the scope of the ServiceWorker.
+ ServiceWorkerGraceTimeoutTermination=Terminating ServiceWorker for scope ‘%1$S’ with pending waitUntil/respondWith promises because of grace timeout.
+ # LOCALIZATION NOTE (ServiceWorkerNoFetchHandler): Do not translate "Fetch".
+ ServiceWorkerNoFetchHandler=Fetch event handlers must be added during the worker script’s initial evaluation.
+ ExecCommandCutCopyDeniedNotInputDriven=document.execCommand(‘cut’/‘copy’) was denied because it was not called from inside a short running user-generated event handler.
+ ManifestShouldBeObject=Manifest should be an object.
+ ManifestScopeURLInvalid=The scope URL is invalid.
+ ManifestScopeNotSameOrigin=The scope URL must be same origin as document.
+diff --git a/dom/workers/ServiceWorker.cpp b/dom/workers/ServiceWorker.cpp
+--- a/dom/workers/ServiceWorker.cpp
++++ b/dom/workers/ServiceWorker.cpp
+@@ -90,16 +90,19 @@ ServiceWorker::PostMessage(JSContext* aC
+   if (!window || !window->GetExtantDoc()) {
+     NS_WARNING("Trying to call post message from an invalid dom object.");
+     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+     return;
+   }
+ 
+   auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
+   if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
++    ServiceWorkerManager::LocalizeAndReportToAllClients(
++      mInfo->Scope(), "ServiceWorkerPostMessageStorageError",
++      nsTArray<nsString> { NS_ConvertUTF8toUTF16(mInfo->Scope()) });
+     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+     return;
+   }
+ 
+   UniquePtr<ServiceWorkerClientInfo> clientInfo(new ServiceWorkerClientInfo(window->GetExtantDoc()));
+   ServiceWorkerPrivate* workerPrivate = mInfo->WorkerPrivate();
+   aRv = workerPrivate->SendMessageEvent(aCx, aMessage, aTransferable, Move(clientInfo));
+ }
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -725,26 +725,33 @@ ServiceWorkerManager::Register(mozIDOMWi
+   AssertIsOnMainThread();
+ 
+   if (NS_WARN_IF(!aWindow)) {
+     return NS_ERROR_DOM_INVALID_STATE_ERR;
+   }
+ 
+   auto* window = nsPIDOMWindowInner::From(aWindow);
+ 
++  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
++  MOZ_ASSERT(doc);
++
+   // Don't allow a service worker to be registered if storage is restricted
+   // for the window.
+   auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
+   if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
++    NS_ConvertUTF8toUTF16 reportScope(aScopeURI->GetSpecOrDefault());
++    const char16_t* param[] = { reportScope.get() };
++    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
++                                    NS_LITERAL_CSTRING("Service Workers"), doc,
++                                    nsContentUtils::eDOM_PROPERTIES,
++                                    "ServiceWorkerRegisterStorageError", param,
++                                    1);
+     return NS_ERROR_DOM_SECURITY_ERR;
+   }
+ 
+-  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+-  MOZ_ASSERT(doc);
+-
+   // Don't allow service workers to register when the *document* is chrome.
+   if (NS_WARN_IF(nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()))) {
+     return NS_ERROR_DOM_SECURITY_ERR;
+   }
+ 
+   nsCOMPtr<nsPIDOMWindowOuter> outerWindow = window->GetOuterWindow();
+   bool serviceWorkersTestingEnabled =
+     outerWindow->GetServiceWorkersTestingEnabled();
+@@ -973,16 +980,21 @@ ServiceWorkerManager::GetRegistrations(m
+   auto* window = nsPIDOMWindowInner::From(aWindow);
+ 
+   // Don't allow a service worker to access service worker registrations
+   // from a window with storage disabled.  If these windows can access
+   // the registration it increases the chance they can bypass the storage
+   // block via postMessage(), etc.
+   auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
+   if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
++    nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
++    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
++                                    NS_LITERAL_CSTRING("Service Workers"), doc,
++                                    nsContentUtils::eDOM_PROPERTIES,
++                                    "ServiceWorkerGetRegistrationStorageError");
+     return NS_ERROR_DOM_SECURITY_ERR;
+   }
+ 
+   // Don't allow service workers to register when the *document* is chrome for
+   // now.
+   MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(window->GetExtantDoc()->NodePrincipal()));
+ 
+   nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
+@@ -1091,16 +1103,21 @@ ServiceWorkerManager::GetRegistration(mo
+   auto* window = nsPIDOMWindowInner::From(aWindow);
+ 
+   // Don't allow a service worker to access service worker registrations
+   // from a window with storage disabled.  If these windows can access
+   // the registration it increases the chance they can bypass the storage
+   // block via postMessage(), etc.
+   auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
+   if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
++    nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
++    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
++                                    NS_LITERAL_CSTRING("Service Workers"), doc,
++                                    nsContentUtils::eDOM_PROPERTIES,
++                                    "ServiceWorkerGetRegistrationStorageError");
+     return NS_ERROR_DOM_SECURITY_ERR;
+   }
+ 
+   // Don't allow service workers to register when the *document* is chrome for
+   // now.
+   MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(window->GetExtantDoc()->NodePrincipal()));
+ 
+   nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
+@@ -3183,16 +3200,17 @@ FireControllerChangeOnDocument(nsIDocume
+ 
+ } // anonymous namespace
+ 
+ UniquePtr<ServiceWorkerClientInfo>
+ ServiceWorkerManager::GetClient(nsIPrincipal* aPrincipal,
+                                 const nsAString& aClientId,
+                                 ErrorResult& aRv)
+ {
++  AssertIsOnMainThread();
+   UniquePtr<ServiceWorkerClientInfo> clientInfo;
+   nsCOMPtr<nsISupportsInterfacePointer> ifptr =
+     do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
+   if (NS_WARN_IF(!ifptr)) {
+     return clientInfo;
+   }
+   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+   if (NS_WARN_IF(!obs)) {
+@@ -3223,30 +3241,35 @@ ServiceWorkerManager::GetClient(nsIPrinc
+     return clientInfo;
+   }
+ 
+   // Don't let service worker see 3rd party iframes that are denied storage
+   // access.  We don't want these to communicate.
+   auto storageAccess =
+     nsContentUtils::StorageAllowedForWindow(doc->GetInnerWindow());
+   if (storageAccess != nsContentUtils::StorageAccess::eAllow) {
++    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
++                                    NS_LITERAL_CSTRING("Service Workers"), doc,
++                                    nsContentUtils::eDOM_PROPERTIES,
++                                    "ServiceWorkerGetClientStorageError");
+     return clientInfo;
+   }
+ 
+   clientInfo.reset(new ServiceWorkerClientInfo(doc));
+   return clientInfo;
+ }
+ 
+ void
+ ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal,
+                                     const nsCString& aScope,
+                                     uint64_t aServiceWorkerID,
+                                     bool aIncludeUncontrolled,
+                                     nsTArray<ServiceWorkerClientInfo>& aDocuments)
+ {
++  AssertIsOnMainThread();
+   MOZ_ASSERT(aPrincipal);
+ 
+   RefPtr<ServiceWorkerRegistrationInfo> registration =
+     GetRegistration(aPrincipal, aScope);
+ 
+   if (!registration) {
+     // The registration was removed, leave the array empty.
+     return;
+@@ -3293,16 +3316,20 @@ ServiceWorkerManager::GetAllClients(nsIP
+       continue;
+     }
+ 
+     // Don't let service worker find 3rd party iframes that are denied storage
+     // access.  We don't want these to communicate.
+     auto storageAccess =
+       nsContentUtils::StorageAllowedForWindow(doc->GetInnerWindow());
+     if (storageAccess != nsContentUtils::StorageAccess::eAllow) {
++      nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
++                                      NS_LITERAL_CSTRING("Service Workers"),
++                                      doc, nsContentUtils::eDOM_PROPERTIES,
++                                      "ServiceWorkerGetClientStorageError");
+       continue;
+     }
+ 
+     // If we are only returning controlled Clients then skip any documents
+     // that are for different registrations.  We also skip service workers
+     // that don't match the ID of our calling service worker.  We should
+     // only return Clients controlled by that precise service worker.
+     if (!aIncludeUncontrolled) {
+diff --git a/dom/workers/test/serviceworkers/error_reporting_helpers.js b/dom/workers/test/serviceworkers/error_reporting_helpers.js
+--- a/dom/workers/test/serviceworkers/error_reporting_helpers.js
++++ b/dom/workers/test/serviceworkers/error_reporting_helpers.js
+@@ -32,19 +32,23 @@ let localizer =
+  * @return {Object} Promise/handle to pass to wait_for_expected_message.
+  */
+ function expect_console_message(/* msgId, args, ... */) {
+   let expectations = [];
+   // process repeated paired arguments of: msgId, args
+   for (let i = 0; i < arguments.length; i += 2) {
+     let msgId = arguments[i];
+     let args = arguments[i + 1];
+-    expectations.push({
+-      errorMessage: localizer.formatStringFromName(msgId, args, args.length)
+-    });
++    if (args.length === 0) {
++      expectations.push({errorMessage: localizer.GetStringFromName(msgId)});
++    } else {
++      expectations.push({
++        errorMessage: localizer.formatStringFromName(msgId, args, args.length)
++      });
++    }
+   }
+   return new Promise(resolve => {
+     SimpleTest.monitorConsole(resolve, expectations);
+   });
+ }
+ let expect_console_messages = expect_console_message;
+ 
+ /**
+diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini
+--- a/dom/workers/test/serviceworkers/mochitest.ini
++++ b/dom/workers/test/serviceworkers/mochitest.ini
+@@ -217,16 +217,17 @@ support-files =
+   async_waituntil_worker.js
+   lazy_worker.js
+   nofetch_handler_worker.js
+   service_worker.js
+   service_worker_client.html
+   utils.js
+   bug1290951_worker_main.sjs
+   bug1290951_worker_imported.sjs
++  sw_storage_not_allow.js
+ 
+ [test_bug1151916.html]
+ [test_bug1240436.html]
+ [test_bug1408734.html]
+ [test_claim.html]
+ [test_claim_oninstall.html]
+ [test_controller.html]
+ [test_cookie_fetch.html]
+diff --git a/dom/workers/test/serviceworkers/sw_storage_not_allow.js b/dom/workers/test/serviceworkers/sw_storage_not_allow.js
+new file mode 100644
+--- /dev/null
++++ b/dom/workers/test/serviceworkers/sw_storage_not_allow.js
+@@ -0,0 +1,21 @@
++let clientId;
++addEventListener('fetch', function(event) {
++  if (event.request.url.includes('getClients')) {
++    // Excepted to fail since the storage access is not allowed.
++    self.clients.matchAll();
++  } else if (event.request.url.includes('getClient-stage1')) {
++    self.clients.matchAll().then(function(clients) {
++      clientId = clients[0].id;
++    });
++  } else if (event.request.url.includes('getClient-stage2')) {
++    // Excepted to fail since the storage access is not allowed.
++    self.clients.get(clientId);
++  }
++});
++
++addEventListener('message', function(event) {
++  if (event.data === 'claim') {
++    event.waitUntil(clients.claim());
++  }
++});
++
+diff --git a/dom/workers/test/serviceworkers/test_error_reporting.html b/dom/workers/test/serviceworkers/test_error_reporting.html
+--- a/dom/workers/test/serviceworkers/test_error_reporting.html
++++ b/dom/workers/test/serviceworkers/test_error_reporting.html
+@@ -1,15 +1,16 @@
+ <!DOCTYPE HTML>
+ <html>
+ <head>
+   <title>Test Error Reporting of Service Worker Failures</title>
+   <script src="/tests/SimpleTest/SimpleTest.js"></script>
+   <script src="/tests/SimpleTest/SpawnTask.js"></script>
+   <script src="error_reporting_helpers.js"></script>
++  <script src="utils.js"></script>
+   <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+ </head>
+ <body>
+ 
+ <script type="text/javascript">
+ "use strict";
+ 
+@@ -65,12 +66,191 @@ add_task(async function register_bad_mim
+   // consume the expected rejection so it doesn't get thrown at us.
+   await navigator.serviceWorker.register("sw_bad_mime_type.js", { scope: "bad_mime_type/" })
+     .then(
+       () => { ok(false, "should have rejected"); },
+       (e) => { ok(e.name === "SecurityError", "bad MIME type failed as expected"); });
+ 
+   await wait_for_expected_message(expectedMessage);
+ });
++
++async function notAllowStorageAccess() {
++  return SpecialPowers.pushPrefEnv({"set": [
++    ["network.cookie.lifetimePolicy",
++     SpecialPowers.Ci.nsICookieService.ACCEPT_SESSION],
++  ]});
++}
++
++async function allowStorageAccess() {
++  return SpecialPowers.pushPrefEnv({"set": [
++    ["network.cookie.lifetimePolicy",
++     SpecialPowers.Ci.nsICookieService.ACCEPT_NORMALLY],
++  ]});
++}
++
++/**
++ * Ensure an error is logged when the storage is not allowed and the content
++ * script is trying to register a service worker.
++ */
++add_task(async function register_storage_error() {
++  let expectedMessage = expect_console_message(
++    "ServiceWorkerRegisterStorageError",
++    [make_absolute_url("storage_not_allow/")]);
++
++  await notAllowStorageAccess();
++
++  // consume the expected rejection so it doesn't get thrown at us.
++  await navigator.serviceWorker.register("sw_storage_not_allow.js",
++                                         { scope: "storage_not_allow/" })
++    .then(
++      () => { ok(false, "should have rejected"); },
++      (e) => { ok(e.name === "SecurityError",
++                  "storage access failed as expected."); });
++
++  await wait_for_expected_message(expectedMessage);
++
++  await allowStorageAccess();
++});
++
++/**
++ * Ensure an error is logged when the storage is not allowed and the content
++ * script is trying to get the service worker registration.
++ */
++add_task(async function get_registration_storage_error() {
++  let expectedMessage =
++    expect_console_message("ServiceWorkerGetRegistrationStorageError", []);
++
++  await notAllowStorageAccess();
++
++  // consume the expected rejection so it doesn't get thrown at us.
++  await navigator.serviceWorker.getRegistration()
++    .then(
++      () => { ok(false, "should have rejected"); },
++      (e) => { ok(e.name === "SecurityError",
++                  "storage access failed as expected."); });
++
++  await wait_for_expected_message(expectedMessage);
++
++  await allowStorageAccess();
++});
++
++/**
++ * Ensure an error is logged when the storage is not allowed and the content
++ * script is trying to get the service worker registrations.
++ */
++add_task(async function get_registrations_storage_error() {
++  let expectedMessage =
++    expect_console_message("ServiceWorkerGetRegistrationStorageError", []);
++
++  await notAllowStorageAccess();
++
++  // consume the expected rejection so it doesn't get thrown at us.
++  await navigator.serviceWorker.getRegistrations()
++    .then(
++      () => { ok(false, "should have rejected"); },
++      (e) => { ok(e.name === "SecurityError",
++                  "storage access failed as expected."); });
++
++  await wait_for_expected_message(expectedMessage);
++
++  await allowStorageAccess();
++});
++
++/**
++ * Ensure an error is logged when the storage is not allowed and the content
++ * script is trying to post a message to the service worker.
++ */
++add_task(async function postMessage_storage_error() {
++  let expectedMessage = expect_console_message(
++    "ServiceWorkerPostMessageStorageError",
++    [make_absolute_url("storage_not_allow/")]);
++
++  let registration;
++  // consume the expected rejection so it doesn't get thrown at us.
++  await navigator.serviceWorker.register("sw_storage_not_allow.js",
++                                         { scope: "storage_not_allow/" })
++    .then(reg => { registration = reg; })
++    .then(() => notAllowStorageAccess())
++    .then(() => registration.installing ||
++                registration.waiting ||
++                registration.active)
++    .then(worker =>  worker.postMessage('ha'))
++    .then(
++      () => { ok(false, "should have rejected"); },
++      (e) => { ok(e.name === "SecurityError",
++                  "storage access failed as expected."); });
++
++  await wait_for_expected_message(expectedMessage);
++
++  await registration.unregister();
++  await allowStorageAccess();
++});
++
++/**
++ * Ensure an error is logged when the storage is not allowed and the service
++ * worker is trying to get its client.
++ */
++add_task(async function get_client_storage_error() {
++  let expectedMessage =
++    expect_console_message("ServiceWorkerGetClientStorageError", []);
++
++  await SpecialPowers.pushPrefEnv({"set": [
++    // Make the test pass the IsOriginPotentiallyTrustworthy.
++    ["dom.securecontext.whitelist", "mochi.test"]
++  ]});
++
++  let registration, sw;
++  // consume the expected rejection so it doesn't get thrown at us.
++  await navigator.serviceWorker.register("sw_storage_not_allow.js",
++                                         { scope: "test_error_reporting.html" })
++    .then(reg => { registration = reg; })
++    .then(() => registration.installing ||
++                registration.waiting ||
++                registration.active)
++    .then(worker => { sw = worker; })
++    .then(() => waitForState(sw, 'activated'))
++    .then(() => sw.postMessage('claim'))
++    .then(() => waitForControlled(window))
++    // Get the client's ID in the stage 1
++    .then(() => fetch("getClient-stage1"))
++    .then(() => notAllowStorageAccess())
++    // Trigger the clients.get() in the stage 2
++    .then(() => fetch("getClient-stage2"))
++    .catch(e => ok(false, "fail due to:" + e));
++
++  await wait_for_expected_message(expectedMessage);
++
++  await registration.unregister();
++  await allowStorageAccess();
++});
++
++/**
++ * Ensure an error is logged when the storage is not allowed and the service
++ * worker is trying to get its clients.
++ */
++add_task(async function get_clients_storage_error() {
++  let expectedMessage =
++    expect_console_message("ServiceWorkerGetClientStorageError", []);
++
++  let registration, sw;
++  // consume the expected rejection so it doesn't get thrown at us.
++  await navigator.serviceWorker.register("sw_storage_not_allow.js",
++                                         { scope: "test_error_reporting.html" })
++    .then(reg => { registration = reg; })
++    .then(() => registration.installing ||
++                registration.waiting ||
++                registration.active)
++    .then(worker => { sw = worker; })
++    .then(() => waitForState(sw, 'activated'))
++    .then(() => sw.postMessage('claim'))
++    .then(() => waitForControlled(window))
++    .then(() => notAllowStorageAccess())
++    .then(() => fetch("getClients"))
++    .catch(e => ok(false, "fail due to:" + e));
++
++  await wait_for_expected_message(expectedMessage);
++
++  await registration.unregister();
++  await allowStorageAccess();
++});
+ </script>
+-
+ </body>
+ </html>

+ 341 - 0
mozilla-release/patches/1419536-1-59a1.patch

@@ -0,0 +1,341 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1511295184 18000
+# Node ID 0f356a5c4dea9aa3cc0f806f74338beb484b9f12
+# Parent  9abed7c0efb306f68ac6ae01dd81dcf5a7751819
+Bug 1419536 P1 Add a helper to handle Client objects properly on nsIChannels. r=baku
+
+diff --git a/dom/clients/manager/ClientChannelHelper.cpp b/dom/clients/manager/ClientChannelHelper.cpp
+new file mode 100644
+--- /dev/null
++++ b/dom/clients/manager/ClientChannelHelper.cpp
+@@ -0,0 +1,256 @@
++/* -*- 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 "ClientChannelHelper.h"
++
++#include "ClientManager.h"
++#include "ClientSource.h"
++#include "MainThreadUtils.h"
++#include "mozilla/dom/ServiceWorkerDescriptor.h"
++#include "mozilla/ipc/BackgroundUtils.h"
++#include "nsContentUtils.h"
++#include "nsIAsyncVerifyRedirectCallback.h"
++#include "nsIChannel.h"
++#include "nsIChannelEventSink.h"
++#include "nsIDocShell.h"
++#include "nsIInterfaceRequestor.h"
++#include "nsIInterfaceRequestorUtils.h"
++
++namespace mozilla {
++namespace dom {
++
++using mozilla::ipc::PrincipalInfoToPrincipal;
++
++namespace {
++
++class ClientChannelHelper final : public nsIInterfaceRequestor
++                                , public nsIChannelEventSink
++{
++  nsCOMPtr<nsIInterfaceRequestor> mOuter;
++  nsCOMPtr<nsISerialEventTarget> mEventTarget;
++
++  ~ClientChannelHelper() = default;
++
++  NS_IMETHOD
++  GetInterface(const nsIID& aIID, void** aResultOut) override
++  {
++    if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
++      *aResultOut = static_cast<nsIChannelEventSink*>(this);
++      NS_ADDREF_THIS();
++      return NS_OK;
++    }
++
++    if (mOuter) {
++      return mOuter->GetInterface(aIID, aResultOut);
++    }
++
++    return NS_ERROR_NO_INTERFACE;
++  }
++
++  NS_IMETHOD
++  AsyncOnChannelRedirect(nsIChannel* aOldChannel,
++                         nsIChannel* aNewChannel,
++                         uint32_t aFlags,
++                         nsIAsyncVerifyRedirectCallback *aCallback) override
++  {
++    MOZ_ASSERT(NS_IsMainThread());
++
++    nsCOMPtr<nsILoadInfo> oldLoadInfo;
++    nsresult rv = aOldChannel->GetLoadInfo(getter_AddRefs(oldLoadInfo));
++    NS_ENSURE_SUCCESS(rv, rv);
++
++    nsCOMPtr<nsILoadInfo> newLoadInfo;
++    rv = aNewChannel->GetLoadInfo(getter_AddRefs(newLoadInfo));
++    NS_ENSURE_SUCCESS(rv, rv);
++
++    rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
++    if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_DOM_BAD_URI)) {
++      return rv;
++    }
++
++    UniquePtr<ClientSource> reservedClient = oldLoadInfo->TakeReservedClientSource();
++
++    // If its a same-origin redirect we just move our reserved client to the
++    // new channel.
++    if (NS_SUCCEEDED(rv)) {
++      if (reservedClient) {
++        newLoadInfo->GiveReservedClientSource(Move(reservedClient));
++      }
++
++      // It seems sometimes necko passes two channels with the same LoadInfo.
++      // We only need to move the reserved/initial ClientInfo over if we
++      // actually have a different LoadInfo.
++      else if (oldLoadInfo != newLoadInfo) {
++        const Maybe<ClientInfo>& reservedClientInfo =
++          oldLoadInfo->GetReservedClientInfo();
++
++        const Maybe<ClientInfo>& initialClientInfo =
++          oldLoadInfo->GetInitialClientInfo();
++
++        MOZ_DIAGNOSTIC_ASSERT(reservedClientInfo.isNothing() ||
++                              initialClientInfo.isNothing());
++
++        if (reservedClientInfo.isSome()) {
++          newLoadInfo->SetReservedClientInfo(reservedClientInfo.ref());
++        }
++
++        if (initialClientInfo.isSome()) {
++          newLoadInfo->SetInitialClientInfo(initialClientInfo.ref());
++        }
++      }
++
++      // Make sure we keep the service worker controller on same-origin
++      // internal redirects.
++      if (oldLoadInfo != newLoadInfo &&
++          aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
++        const Maybe<ServiceWorkerDescriptor>& controller = oldLoadInfo->GetController();
++        if (controller.isSome()) {
++          newLoadInfo->SetController(controller.ref());
++        }
++      }
++    }
++
++    // If it's a cross-origin redirect then we discard the old reserved client
++    // and create a new one.
++    else {
++      // If CheckSameOrigin() worked, then the security manager must exist.
++      nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
++      MOZ_DIAGNOSTIC_ASSERT(ssm);
++
++      nsCOMPtr<nsIPrincipal> principal;
++      rv = ssm->GetChannelResultPrincipal(aNewChannel, getter_AddRefs(principal));
++      NS_ENSURE_SUCCESS(rv, rv);
++
++      // Create the new ClientSource.  This should only happen for window
++      // Clients since support cross-origin redirects are blocked by the
++      // same-origin security policy.
++      reservedClient.reset();
++      reservedClient = ClientManager::CreateSource(ClientType::Window,
++                                                   mEventTarget, principal);
++
++      newLoadInfo->GiveReservedClientSource(Move(reservedClient));
++    }
++
++    nsCOMPtr<nsIChannelEventSink> outerSink = do_GetInterface(mOuter);
++    if (outerSink) {
++      return outerSink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags,
++                                               aCallback);
++    }
++
++    aCallback->OnRedirectVerifyCallback(NS_OK);
++    return NS_OK;
++  }
++
++public:
++  ClientChannelHelper(nsIInterfaceRequestor* aOuter,
++                      nsISerialEventTarget* aEventTarget)
++    : mOuter(aOuter)
++    , mEventTarget(aEventTarget)
++  {
++  }
++
++  NS_DECL_ISUPPORTS
++};
++
++NS_IMPL_ISUPPORTS(ClientChannelHelper, nsIInterfaceRequestor,
++                                       nsIChannelEventSink);
++
++} // anonymous namespace
++
++nsresult
++AddClientChannelHelper(nsIChannel* aChannel,
++                       Maybe<ClientInfo>&& aReservedClientInfo,
++                       Maybe<ClientInfo>&& aInitialClientInfo,
++                       nsISerialEventTarget* aEventTarget)
++{
++  MOZ_ASSERT(NS_IsMainThread());
++
++  Maybe<ClientInfo> initialClientInfo(Move(aInitialClientInfo));
++  Maybe<ClientInfo> reservedClientInfo(Move(aReservedClientInfo));
++  MOZ_DIAGNOSTIC_ASSERT(reservedClientInfo.isNothing() ||
++                        initialClientInfo.isNothing());
++
++  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
++  NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
++
++  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
++  NS_ENSURE_TRUE(ssm, NS_ERROR_FAILURE);
++
++  nsCOMPtr<nsIPrincipal> channelPrincipal;
++  nsresult rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(channelPrincipal));
++  NS_ENSURE_SUCCESS(rv, rv);
++
++  // Only allow the initial ClientInfo to be set if the current channel
++  // principal matches.
++  if (initialClientInfo.isSome()) {
++    nsCOMPtr<nsIPrincipal> initialPrincipal =
++      PrincipalInfoToPrincipal(initialClientInfo.ref().PrincipalInfo(), nullptr);
++
++    bool equals = false;
++    rv = initialPrincipal ? initialPrincipal->Equals(channelPrincipal, &equals)
++                          : NS_ERROR_FAILURE;
++    if (NS_FAILED(rv) || !equals) {
++      initialClientInfo.reset();
++    }
++  }
++
++  // Only allow the reserved ClientInfo to be set if the current channel
++  // principal matches.
++  if (reservedClientInfo.isSome()) {
++    nsCOMPtr<nsIPrincipal> reservedPrincipal =
++      PrincipalInfoToPrincipal(reservedClientInfo.ref().PrincipalInfo(), nullptr);
++
++    bool equals = false;
++    rv = reservedPrincipal ? reservedPrincipal->Equals(channelPrincipal, &equals)
++                           : NS_ERROR_FAILURE;
++    if (NS_FAILED(rv) || !equals) {
++      reservedClientInfo.reset();
++    }
++  }
++
++  nsCOMPtr<nsIInterfaceRequestor> outerCallbacks;
++  rv = aChannel->GetNotificationCallbacks(getter_AddRefs(outerCallbacks));
++  NS_ENSURE_SUCCESS(rv, rv);
++
++  UniquePtr<ClientSource> reservedClient;
++  if (initialClientInfo.isNothing() && reservedClientInfo.isNothing()) {
++    // Wait to reserve the client until we are reasonably sure this method
++    // will succeed.  We should only follow this path for window clients.
++    // Workers should always provide a reserved ClientInfo since their
++    // ClientSource object is owned by a different thread.
++    reservedClient = ClientManager::CreateSource(ClientType::Window,
++                                                 aEventTarget,
++                                                 channelPrincipal);
++    NS_ENSURE_TRUE(reservedClient, NS_ERROR_FAILURE);
++  }
++
++  RefPtr<ClientChannelHelper> helper =
++    new ClientChannelHelper(outerCallbacks, aEventTarget);
++
++  // Only set the callbacks helper if we are able to reserve the client
++  // successfully.
++  rv = aChannel->SetNotificationCallbacks(helper);
++  NS_ENSURE_SUCCESS(rv, rv);
++
++  // Finally preserve the various client values on the nsILoadInfo once the
++  // redirect helper has been added to the channel.
++  if (reservedClient) {
++    loadInfo->GiveReservedClientSource(Move(reservedClient));
++  }
++
++  if (initialClientInfo.isSome()) {
++    loadInfo->SetInitialClientInfo(initialClientInfo.ref());
++  }
++
++  if (reservedClientInfo.isSome()) {
++    loadInfo->SetReservedClientInfo(reservedClientInfo.ref());
++  }
++
++  return NS_OK;
++}
++
++} // namespace dom
++} // namespace mozilla
+diff --git a/dom/clients/manager/ClientChannelHelper.h b/dom/clients/manager/ClientChannelHelper.h
+new file mode 100644
+--- /dev/null
++++ b/dom/clients/manager/ClientChannelHelper.h
+@@ -0,0 +1,34 @@
++/* -*- 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 _mozilla_dom_ClientChannelHelper_h
++#define _mozilla_dom_ClientChannelHelper_h
++
++#include "mozilla/Maybe.h"
++#include "nsError.h"
++
++class nsIChannel;
++class nsISerialEventTarget;
++
++namespace mozilla {
++namespace dom {
++
++class ClientInfo;
++
++// Attach a redirect listener to the given nsIChannel that will manage
++// the various client values on the channel's LoadInfo.  This will
++// properly handle creating a new ClientSource on cross-origin redirect
++// and propagate the current reserved/initial client on same-origin
++// redirect.
++nsresult
++AddClientChannelHelper(nsIChannel* aChannel,
++                       Maybe<ClientInfo>&& aReservedClientInfo,
++                       Maybe<ClientInfo>&& aInitialClientInfo,
++                       nsISerialEventTarget* aEventTarget);
++
++} // namespace dom
++} // namespace mozilla
++
++#endif // _mozilla_dom_ClientChannelHelper_h
+diff --git a/dom/clients/manager/moz.build b/dom/clients/manager/moz.build
+--- a/dom/clients/manager/moz.build
++++ b/dom/clients/manager/moz.build
+@@ -1,28 +1,30 @@
+ # -*- 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/.
+ 
+ EXPORTS.mozilla.dom += [
++  'ClientChannelHelper.h',
+   'ClientHandle.h',
+   'ClientInfo.h',
+   'ClientIPCUtils.h',
+   'ClientManager.h',
+   'ClientManagerActors.h',
+   'ClientOpenWindowOpActors.h',
+   'ClientOpPromise.h',
+   'ClientSource.h',
+   'ClientState.h',
+   'ClientThing.h',
+ ]
+ 
+ UNIFIED_SOURCES += [
++  'ClientChannelHelper.cpp',
+   'ClientHandle.cpp',
+   'ClientHandleChild.cpp',
+   'ClientHandleOpChild.cpp',
+   'ClientHandleOpParent.cpp',
+   'ClientHandleParent.cpp',
+   'ClientInfo.cpp',
+   'ClientManager.cpp',
+   'ClientManagerActors.cpp',

+ 342 - 0
mozilla-release/patches/1419536-2-59a1.patch

@@ -0,0 +1,342 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1511295184 18000
+# Node ID 061c5b3ee4d4a7874f4496835cdf1b7c29875a67
+# Parent  849aecdb450be335dc81495166b909df67b3464c
+Bug 1419536 P2 Create a ClientSource for the WorkerPrivate and mark it execution ready after the script loads. r=baku
+
+diff --git a/dom/webidl/Clients.webidl b/dom/webidl/Clients.webidl
+--- a/dom/webidl/Clients.webidl
++++ b/dom/webidl/Clients.webidl
+@@ -26,11 +26,13 @@ dictionary ClientQueryOptions {
+   boolean includeUncontrolled = false;
+   ClientType type = "window";
+ };
+ 
+ enum ClientType {
+   "window",
+   "worker",
+   "sharedworker",
++  // https://github.com/w3c/ServiceWorker/issues/1036
++  "serviceworker",
+   "all"
+ };
+ 
+diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp
+--- a/dom/workers/ScriptLoader.cpp
++++ b/dom/workers/ScriptLoader.cpp
+@@ -45,16 +45,18 @@
+ #include "mozilla/LoadContext.h"
+ #include "mozilla/Maybe.h"
+ #include "mozilla/ipc/BackgroundUtils.h"
+ #include "mozilla/dom/CacheBinding.h"
+ #include "mozilla/dom/cache/CacheTypes.h"
+ #include "mozilla/dom/cache/Cache.h"
+ #include "mozilla/dom/cache/CacheStorage.h"
+ #include "mozilla/dom/ChannelInfo.h"
++#include "mozilla/dom/ClientChannelHelper.h"
++#include "mozilla/dom/ClientInfo.h"
+ #include "mozilla/dom/Exceptions.h"
+ #include "mozilla/dom/InternalResponse.h"
+ #include "mozilla/dom/nsCSPService.h"
+ #include "mozilla/dom/nsCSPUtils.h"
+ #include "mozilla/dom/Promise.h"
+ #include "mozilla/dom/PromiseNativeHandler.h"
+ #include "mozilla/dom/Response.h"
+ #include "mozilla/dom/ScriptLoader.h"
+@@ -254,16 +256,17 @@ struct ScriptLoadInfo
+   // resolution.
+   RefPtr<Promise> mCachePromise;
+ 
+   // The reader stream the cache entry should be filled from, for those cases
+   // when we're going to have an mCachePromise.
+   nsCOMPtr<nsIInputStream> mCacheReadStream;
+ 
+   nsCOMPtr<nsIChannel> mChannel;
++  Maybe<ClientInfo> mReservedClientInfo;
+   char16_t* mScriptTextBuf;
+   size_t mScriptTextLength;
+ 
+   nsresult mLoadResult;
+   bool mLoadingFinished;
+   bool mExecutionScheduled;
+   bool mExecutionResult;
+ 
+@@ -984,16 +987,27 @@ private:
+     // We don't care about progress so just use the simple stream loader for
+     // OnStreamComplete notification only.
+     nsCOMPtr<nsIStreamLoader> loader;
+     rv = NS_NewStreamLoader(getter_AddRefs(loader), listener);
+     if (NS_WARN_IF(NS_FAILED(rv))) {
+       return rv;
+     }
+ 
++    if (IsMainWorkerScript()) {
++      MOZ_DIAGNOSTIC_ASSERT(loadInfo.mReservedClientInfo.isSome());
++      rv = AddClientChannelHelper(channel,
++                                  Move(loadInfo.mReservedClientInfo),
++                                  Maybe<ClientInfo>(),
++                                  mWorkerPrivate->HybridEventTarget());
++      if (NS_WARN_IF(NS_FAILED(rv))) {
++        return rv;
++      }
++    }
++
+     if (loadInfo.mCacheStatus != ScriptLoadInfo::ToBeCached) {
+       rv = channel->AsyncOpen2(loader);
+       if (NS_WARN_IF(NS_FAILED(rv))) {
+         return rv;
+       }
+     } else {
+       nsCOMPtr<nsIOutputStream> writer;
+ 
+@@ -1964,16 +1978,22 @@ ScriptExecutorRunnable::WorkerRun(JSCont
+                                     loadInfo.mLoadResult, loadInfo.mURL);
+       // Top level scripts only!
+       if (mIsWorkerScript) {
+         aWorkerPrivate->MaybeDispatchLoadFailedRunnable();
+       }
+       return true;
+     }
+ 
++    // If this is a top level script that succeeded, then mark the
++    // Client execution ready.
++    if (mIsWorkerScript) {
++      aWorkerPrivate->ExecutionReady();
++    }
++
+     NS_ConvertUTF16toUTF8 filename(loadInfo.mURL);
+ 
+     JS::CompileOptions options(aCx);
+     options.setFileAndLine(filename.get(), 1)
+            .setNoScriptRval(true);
+ 
+     MOZ_ASSERT(loadInfo.mMutedErrorFlag.isSome());
+     options.setMutedErrors(loadInfo.mMutedErrorFlag.valueOr(true));
+@@ -2258,16 +2278,20 @@ LoadMainScript(WorkerPrivate* aWorkerPri
+                ErrorResult& aRv)
+ {
+   nsTArray<ScriptLoadInfo> loadInfos;
+ 
+   ScriptLoadInfo* info = loadInfos.AppendElement();
+   info->mURL = aScriptURL;
+   info->mLoadFlags = aWorkerPrivate->GetLoadFlags();
+ 
++  // We are loading the main script, so the worker's Client must be
++  // reserved.
++  info->mReservedClientInfo.emplace(aWorkerPrivate->GetClientInfo());
++
+   LoadAllScripts(aWorkerPrivate, loadInfos, true, aWorkerScriptType, aRv);
+ }
+ 
+ void
+ Load(WorkerPrivate* aWorkerPrivate,
+      const nsTArray<nsString>& aScriptURLs, WorkerScriptType aWorkerScriptType,
+      ErrorResult& aRv)
+ {
+diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
+--- a/dom/workers/WorkerPrivate.cpp
++++ b/dom/workers/WorkerPrivate.cpp
+@@ -42,16 +42,18 @@
+ #include "mozilla/Assertions.h"
+ #include "mozilla/Attributes.h"
+ #include "mozilla/ContentEvents.h"
+ #include "mozilla/EventDispatcher.h"
+ #include "mozilla/Likely.h"
+ #include "mozilla/LoadContext.h"
+ #include "mozilla/Move.h"
+ #include "mozilla/dom/BindingUtils.h"
++#include "mozilla/dom/ClientManager.h"
++#include "mozilla/dom/ClientSource.h"
+ #include "mozilla/dom/Console.h"
+ #include "mozilla/dom/DocGroup.h"
+ #include "mozilla/dom/ErrorEvent.h"
+ #include "mozilla/dom/ErrorEventBinding.h"
+ #include "mozilla/dom/Exceptions.h"
+ #include "mozilla/dom/ExtendableMessageEventBinding.h"
+ #include "mozilla/dom/FunctionBinding.h"
+ #include "mozilla/dom/IndexedDatabaseManager.h"
+@@ -593,16 +595,18 @@ private:
+   // run we have not yet done our load so don't know things like our final
+   // principal and whatnot.
+ 
+   virtual bool
+   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+   {
+     aWorkerPrivate->AssertIsOnWorkerThread();
+ 
++    aWorkerPrivate->EnsureClientSource();
++
+     ErrorResult rv;
+     scriptloader::LoadMainScript(aWorkerPrivate, mScriptURL, WorkerScript, rv);
+     rv.WouldReportJSException();
+     // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
+     // return false and don't SetWorkerScriptExecutedSuccessfully() in that
+     // case, but don't throw anything on aCx.  The idea is to not dispatch error
+     // events if our load is canceled with that error code.
+     if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
+@@ -663,16 +667,18 @@ private:
+ 
+     WorkerDebuggerGlobalScope* globalScope =
+       aWorkerPrivate->CreateDebuggerGlobalScope(aCx);
+     if (!globalScope) {
+       NS_WARNING("Failed to make global!");
+       return false;
+     }
+ 
++    aWorkerPrivate->EnsureClientSource();
++
+     JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
+ 
+     ErrorResult rv;
+     JSAutoCompartment ac(aCx, global);
+     scriptloader::LoadMainScript(aWorkerPrivate, mScriptURL,
+                                  DebuggerScript, rv);
+     rv.WouldReportJSException();
+     // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
+@@ -5123,16 +5129,18 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
+       }
+ 
+       // If we're supposed to die then we should exit the loop.
+       if (currentStatus == Killing) {
+         // Flush uncaught rejections immediately, without
+         // waiting for a next tick.
+         PromiseDebugging::FlushUncaughtRejections();
+ 
++        mClientSource = nullptr;
++
+         ShutdownGCTimers();
+ 
+         DisableMemoryReporter();
+ 
+         {
+           MutexAutoLock lock(mMutex);
+ 
+           mStatus = Dead;
+@@ -5285,16 +5293,60 @@ WorkerPrivate::ControlEventTarget()
+ 
+ nsISerialEventTarget*
+ WorkerPrivate::HybridEventTarget()
+ {
+   return mWorkerHybridEventTarget;
+ }
+ 
+ void
++WorkerPrivate::EnsureClientSource()
++{
++  AssertIsOnWorkerThread();
++
++  if (mClientSource) {
++    return;
++  }
++
++  ClientType type;
++  switch(Type()) {
++    case WorkerTypeDedicated:
++      type = ClientType::Worker;
++      break;
++    case WorkerTypeShared:
++      type = ClientType::Sharedworker;
++      break;
++    case WorkerTypeService:
++      type = ClientType::Serviceworker;
++      break;
++    default:
++      MOZ_CRASH("unknown worker type!");
++  }
++
++  mClientSource = ClientManager::CreateSource(type, mWorkerHybridEventTarget,
++                                              GetPrincipalInfo());
++}
++
++const ClientInfo&
++WorkerPrivate::GetClientInfo() const
++{
++  AssertIsOnWorkerThread();
++  MOZ_DIAGNOSTIC_ASSERT(mClientSource);
++  return mClientSource->Info();
++}
++
++void
++WorkerPrivate::ExecutionReady()
++{
++  AssertIsOnWorkerThread();
++  MOZ_DIAGNOSTIC_ASSERT(mClientSource);
++  mClientSource->WorkerExecutionReady(this);
++}
++
++void
+ WorkerPrivate::InitializeGCTimers()
+ {
+   AssertIsOnWorkerThread();
+ 
+   // We need a timer for GC. The basic plan is to run a non-shrinking GC
+   // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
+   // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
+   // run a shrinking GC. If the worker receives more messages then the short
+diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
+--- a/dom/workers/WorkerPrivate.h
++++ b/dom/workers/WorkerPrivate.h
+@@ -58,16 +58,18 @@ template<class T> class nsMainThreadPtrH
+ 
+ namespace JS {
+ struct RuntimeStats;
+ } // namespace JS
+ 
+ namespace mozilla {
+ class ThrottledEventQueue;
+ namespace dom {
++class ClientInfo;
++class ClientSource;
+ class Function;
+ class MessagePort;
+ class MessagePortIdentifier;
+ class PromiseNativeHandler;
+ class StructuredCloneHolder;
+ class WorkerDebuggerGlobalScope;
+ class WorkerGlobalScope;
+ struct WorkerOptions;
+@@ -1055,16 +1057,17 @@ class WorkerPrivate : public WorkerPriva
+   // fired on the main thread if the worker script fails to load
+   nsCOMPtr<nsIRunnable> mLoadFailedRunnable;
+ 
+   JS::UniqueChars mDefaultLocale; // nulled during worker JSContext init
+   TimeStamp mKillTime;
+   uint32_t mErrorHandlerRecursionCount;
+   uint32_t mNextTimeoutId;
+   Status mStatus;
++  UniquePtr<ClientSource> mClientSource;
+   bool mFrozen;
+   bool mTimerRunning;
+   bool mRunningExpiredTimeouts;
+   bool mPendingEventQueueClearing;
+   bool mCancelAllPendingRunnables;
+   bool mPeriodicGCTimerRunning;
+   bool mIdleGCTimerRunning;
+   bool mWorkerScriptExecutedSuccessfully;
+@@ -1489,16 +1492,25 @@ public:
+   // Get an event target that will attempt to dispatch a normal WorkerRunnable,
+   // but if that fails will then fall back to a control runnable.
+   nsISerialEventTarget*
+   HybridEventTarget();
+ 
+   void
+   DumpCrashInformation(nsACString& aString);
+ 
++  void
++  EnsureClientSource();
++
++  const ClientInfo&
++  GetClientInfo() const;
++
++  void
++  ExecutionReady();
++
+ private:
+   WorkerPrivate(WorkerPrivate* aParent,
+                 const nsAString& aScriptURL, bool aIsChromeWorker,
+                 WorkerType aWorkerType, const nsAString& aWorkerName,
+                 const nsACString& aServiceWorkerScope,
+                 WorkerLoadInfo& aLoadInfo);
+ 
+   bool

+ 752 - 0
mozilla-release/patches/1419536-3-59a1.patch

@@ -0,0 +1,752 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1511295185 18000
+# Node ID 3e2118a29baf7cb034e4c6d59e57eb67c8f40c37
+# Parent  df29a83db0ca5308cd5a5dc3d628ecb2511f6025
+Bug 1419536 P3 Handle creating and activating the ClientSource in nsDocShell and nsGlobalWindow. r=baku r=freesamael
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -8,16 +8,21 @@
+ 
+ #include <algorithm>
+ 
+ #include "mozilla/ArrayUtils.h"
+ #include "mozilla/Attributes.h"
+ #include "mozilla/AutoRestore.h"
+ #include "mozilla/BasePrincipal.h"
+ #include "mozilla/Casting.h"
++#include "mozilla/dom/ClientChannelHelper.h"
++#include "mozilla/dom/ClientHandle.h"
++#include "mozilla/dom/ClientInfo.h"
++#include "mozilla/dom/ClientManager.h"
++#include "mozilla/dom/ClientSource.h"
+ #include "mozilla/dom/ContentChild.h"
+ #include "mozilla/dom/Element.h"
+ #include "mozilla/dom/HTMLAnchorElement.h"
+ #include "mozilla/dom/TabChild.h"
+ #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+ #include "mozilla/dom/ScreenOrientation.h"
+ #include "mozilla/dom/ToJSValue.h"
+ #include "mozilla/dom/PermissionMessageUtils.h"
+@@ -3368,16 +3373,90 @@ nsDocShell::GetParent(nsIDocShellTreeIte
+ already_AddRefed<nsDocShell>
+ nsDocShell::GetParentDocshell()
+ {
+   nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
+   return docshell.forget().downcast<nsDocShell>();
+ }
+ 
+ void
++nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal)
++{
++  // If there is an existing document then there is no need to create
++  // a client for a future initial about:blank document.
++  if (mScriptGlobal && mScriptGlobal->GetExtantDoc()) {
++    MOZ_DIAGNOSTIC_ASSERT(
++      mScriptGlobal->GetCurrentInnerWindowInternal()->GetClientInfo().isSome());
++    MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
++    return;
++  }
++
++  // Don't recreate the initial client source.  We call this multiple times
++  // when DoChannelLoad() is called before CreateAboutBlankContentViewer.
++  if (mInitialClientSource) {
++    return;
++  }
++
++  // Don't pre-allocate the client when we are sandboxed.  The inherited
++  // principal does not take sandboxing into account.
++  // TODO: Refactor sandboxing principal code out so we can use it here.
++  if (!aPrincipal && (mSandboxFlags & SANDBOXED_ORIGIN)) {
++    return;
++  }
++
++  nsIPrincipal* principal = aPrincipal ? aPrincipal
++                                       : GetInheritedPrincipal(false);
++
++  // Sometimes there is no principal available when we are called from
++  // CreateAboutBlankContentViewer.  For example, sometimes the principal
++  // is only extracted from the load context after the document is created
++  // in nsDocument::ResetToURI().  Ideally we would do something similar
++  // here, but for now lets just avoid the issue by not preallocating the
++  // client.
++  if (!principal) {
++    return;
++  }
++
++  nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
++  if (!win) {
++    return;
++  }
++
++  mInitialClientSource =
++    ClientManager::CreateSource(ClientType::Window,
++                                win->EventTargetFor(TaskCategory::Other),
++                                principal);
++
++  // Mark the initial client as execution ready, but owned by the docshell.
++  // If the client is actually used this will cause ClientSource to force
++  // the creation of the initial about:blank by calling nsDocShell::GetDocument().
++  mInitialClientSource->DocShellExecutionReady(this);
++}
++
++Maybe<ClientInfo>
++nsDocShell::GetInitialClientInfo() const
++{
++  if (mInitialClientSource) {
++    Maybe<ClientInfo> result;
++    result.emplace(mInitialClientSource->Info());
++    return Move(result);
++  }
++
++  nsGlobalWindow* innerWindow =
++    mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
++  nsIDocument* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
++
++  if (!doc || !doc->IsInitialDocument()) {
++    return Maybe<ClientInfo>();
++  }
++
++  return innerWindow->GetClientInfo();
++}
++
++void
+ nsDocShell::RecomputeCanExecuteScripts()
+ {
+   bool old = mCanExecuteScripts;
+   RefPtr<nsDocShell> parent = GetParentDocshell();
+ 
+   // If we have no tree owner, that means that we've been detached from the
+   // docshell tree (this is distinct from having no parent dochshell, which
+   // is the case for root docshells). It would be nice to simply disallow
+@@ -5876,16 +5955,19 @@ nsDocShell::Destroy()
+       const char* msg = mItemType == typeContent ?
+         NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
+       serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
+     }
+   }
+ 
+   mIsBeingDestroyed = true;
+ 
++  // Brak the cycle with the initial client, if present.
++  mInitialClientSource.reset();
++
+   // Make sure we don't record profile timeline markers anymore
+   SetRecordProfileTimelineMarkers(false);
+ 
+   // Remove our pref observers
+   if (mObserveErrorPages) {
+     mObserveErrorPages = false;
+   }
+ 
+@@ -7687,16 +7769,20 @@ nsDocShell::EndPageLoad(nsIWebProgress* 
+         internalLoadGroup->OnEndPageLoad(aChannel);
+       }
+     }
+   }
+ 
+   // Timing is picked up by the window, we don't need it anymore
+   mTiming = nullptr;
+ 
++  // Make sure to discard the initial client if we never created the initial
++  // about:blank document.
++  mInitialClientSource.reset();
++
+   // clean up reload state for meta charset
+   if (eCharsetReloadRequested == mCharsetReloadState) {
+     mCharsetReloadState = eCharsetReloadStopOrigional;
+   } else {
+     mCharsetReloadState = eCharsetReloadInit;
+   }
+ 
+   // Save a pointer to the currently-loading history entry.
+@@ -8183,16 +8269,19 @@ nsDocShell::CreateAboutBlankContentViewe
+       if (aPrincipal) {
+         principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
+       } else {
+         principal = NullPrincipal::CreateWithInheritedAttributes(this);
+       }
+     } else {
+       principal = aPrincipal;
+     }
++
++    MaybeCreateInitialClientSource(principal);
++
+     // generate (about:blank) document to load
+     blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal, this);
+     if (blankDoc) {
+       // Hack: set the base URI manually, since this document never
+       // got Reset() with a channel.
+       blankDoc->SetBaseURI(aBaseURI);
+ 
+       // Copy our sandbox flags to the document. These are immutable
+@@ -11633,16 +11722,34 @@ nsDocShell::DoChannelLoad(nsIChannel* aC
+ 
+   uint32_t openFlags = 0;
+   if (mLoadType == LOAD_LINK) {
+     openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
+   }
+   if (!mAllowContentRetargeting) {
+     openFlags |= nsIURILoader::DONT_RETARGET;
+   }
++
++  nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
++  NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
++
++  MaybeCreateInitialClientSource();
++
++  // Since we are loading a document we need to make sure the proper reserved
++  // and initial client data is stored on the nsILoadInfo.  The
++  // ClientChannelHelper does this and ensures that it is propagated properly
++  // on redirects.  We pass no reserved client here so that the helper will
++  // create the reserved ClientSource if necessary.
++  Maybe<ClientInfo> noReservedClient;
++  rv = AddClientChannelHelper(aChannel,
++                              Move(noReservedClient),
++                              GetInitialClientInfo(),
++                              win->EventTargetFor(TaskCategory::Other));
++  NS_ENSURE_SUCCESS(rv, rv);
++
+   rv = aURILoader->OpenURI(aChannel, openFlags, this);
+   NS_ENSURE_SUCCESS(rv, rv);
+ 
+   // We're about to load a new page and it may take time before necko
+   // gives back any data, so main thread might have a chance to process a
+   // collector slice
+   nsJSContext::MaybeRunNextCollectorSlice(this, JS::gcreason::DOCSHELL);
+ 
+@@ -15043,16 +15150,22 @@ nsDocShell::InFrameSwap()
+     if (shell->mInFrameSwap) {
+       return true;
+     }
+     shell = shell->GetParentDocshell();
+   } while (shell);
+   return false;
+ }
+ 
++UniquePtr<ClientSource>
++nsDocShell::TakeInitialClientSource()
++{
++  return Move(mInitialClientSource);
++}
++
+ NS_IMETHODIMP
+ nsDocShell::IssueWarning(uint32_t aWarning, bool aAsError)
+ {
+   if (mContentViewer) {
+     nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
+     if (doc) {
+       doc->WarnOnceAbout(nsIDocument::DeprecatedOperations(aWarning), aAsError);
+     }
+diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
+--- a/docshell/base/nsDocShell.h
++++ b/docshell/base/nsDocShell.h
+@@ -14,16 +14,17 @@
+ #include "nsIBaseWindow.h"
+ #include "nsINetworkInterceptController.h"
+ #include "nsIScrollable.h"
+ #include "nsITextScroll.h"
+ #include "nsIContentViewerContainer.h"
+ #include "nsIDOMStorageManager.h"
+ #include "nsDocLoader.h"
+ #include "mozilla/BasePrincipal.h"
++#include "mozilla/Maybe.h"
+ #include "mozilla/Move.h"
+ #include "mozilla/UniquePtr.h"
+ #include "mozilla/WeakPtr.h"
+ #include "mozilla/TimeStamp.h"
+ #include "GeckoProfiler.h"
+ #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+ #include "mozilla/LinkedList.h"
+ #include "jsapi.h"
+@@ -63,16 +64,18 @@
+ #include "Units.h"
+ #include "nsIDeprecationWarner.h"
+ 
+ namespace mozilla {
+ class Encoding;
+ class HTMLEditor;
+ enum class TaskCategory;
+ namespace dom {
++class ClientInfo;
++class ClientSource;
+ class EventTarget;
+ typedef uint32_t ScreenOrientationInternal;
+ } // namespace dom
+ } // namespace mozilla
+ 
+ class nsDocShell;
+ class nsDOMNavigationTiming;
+ class nsGlobalWindow;
+@@ -822,16 +825,36 @@ protected:
+ 
+   nsIChannel* GetCurrentDocChannel();
+ 
+   bool ShouldBlockLoadingForBackButton();
+ 
+   // Convenience method for getting our parent docshell. Can return null
+   already_AddRefed<nsDocShell> GetParentDocshell();
+ 
++  // Possibly create a ClientSource object to represent an initial about:blank
++  // window that has not been allocated yet.  Normally we try not to create
++  // this about:blank window until something calls GetDocument().  We still need
++  // the ClientSource to exist for this conceptual window, though.
++  //
++  // The ClientSource is created with the given principal if specified.  If
++  // the principal is not provided we will attempt to inherit it when we
++  // are sure it will match what the real about:blank window principal
++  // would have been.  There are some corner cases where we cannot easily
++  // determine the correct principal and will not create the ClientSource.
++  // In these cases the initial about:blank will appear to not exist until
++  // its real document and window are created.
++  void MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal = nullptr);
++
++  // Return the ClientInfo for the initial about:blank window, if it exists
++  // or we have speculatively created a ClientSource in
++  // MaybeCreateInitialClientSource().  This can return a ClientInfo object
++  // even if GetExtantDoc() returns nullptr.
++  mozilla::Maybe<mozilla::dom::ClientInfo> GetInitialClientInfo() const;
++
+ protected:
+   nsresult GetCurScrollPos(int32_t aScrollOrientation, int32_t* aCurPos);
+   nsresult SetCurScrollPosEx(int32_t aCurHorizontalPos,
+                              int32_t aCurVerticalPos);
+ 
+   // Override the parent setter from nsDocLoader
+   virtual nsresult SetDocLoaderParent(nsDocLoader* aLoader) override;
+ 
+@@ -1145,16 +1168,18 @@ private:
+   nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal;
+   nsTObserverArray<nsWeakPtr> mPrivacyObservers;
+   nsTObserverArray<nsWeakPtr> mReflowObservers;
+   nsTObserverArray<nsWeakPtr> mScrollObservers;
+   nsCString mOriginalUriString;
+   nsWeakPtr mOpener;
+   mozilla::OriginAttributes mOriginAttributes;
+ 
++  mozilla::UniquePtr<mozilla::dom::ClientSource> mInitialClientSource;
++
+   // A depth count of how many times NotifyRunToCompletionStart
+   // has been called without a matching NotifyRunToCompletionStop.
+   uint32_t mJSRunToCompletionDepth;
+ 
+   // Whether or not touch events are overridden. Possible values are defined
+   // as constants in the nsIDocShell.idl file.
+   uint32_t mTouchEventsOverride;
+ 
+diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl
+--- a/docshell/base/nsIDocShell.idl
++++ b/docshell/base/nsIDocShell.idl
+@@ -7,34 +7,39 @@
+ #include "domstubs.idl"
+ #include "nsIDocShellTreeItem.idl"
+ #include "nsIRequest.idl"
+ 
+ %{ C++
+ #include "js/TypeDecls.h"
+ #include "mozilla/Maybe.h"
+ #include "mozilla/NotNull.h"
++#include "mozilla/UniquePtr.h"
+ #include "nsCOMPtr.h"
+ #include "nsIURI.h"
+ class nsPresContext;
+ class nsIPresShell;
+ namespace mozilla {
+ class Encoding;
+ class HTMLEditor;
++namespace dom {
++class ClientSource;
++} // namespace dom
+ }
+ %}
+ 
+ /**
+  * The nsIDocShell interface.
+  */
+ 
+ [ptr] native nsPresContext(nsPresContext);
+ [ptr] native nsIPresShell(nsIPresShell);
+ [ref] native MaybeURI(mozilla::Maybe<nsCOMPtr<nsIURI>>);
+ [ref] native Encoding(const mozilla::Encoding*);
++      native UniqueClientSource(mozilla::UniquePtr<mozilla::dom::ClientSource>);
+ 
+ interface nsIURI;
+ interface nsIChannel;
+ interface nsIContentViewer;
+ interface nsIDOMEventTarget;
+ interface nsIDocShellLoadInfo;
+ interface nsIEditor;
+ interface nsIEditingSession;
+@@ -1155,16 +1160,27 @@ interface nsIDocShell : nsIDocShellTreeI
+    */
+   attribute boolean useTrackingProtection;
+ 
+  /**
+   * Fire a dummy location change event asynchronously.
+   */
+   [noscript] void dispatchLocationChangeEvent();
+ 
++  /**
++   * Take ownership of the ClientSource representing an initial about:blank
++   * document that was never needed.  As an optimization we avoid creating
++   * this document if no code calls GetDocument(), but we still need a
++   * ClientSource object to represent the about:blank window.  This may return
++   * nullptr; for example if the docshell has created a real window and document
++   * already.
++   */
++  [noscript, nostdcall, notxpcom]
++  UniqueClientSource TakeInitialClientSource();
++
+ %{C++
+   /**
+    * These methods call nsDocShell::GetHTMLEditorInternal() and
+    * nsDocShell::SetHTMLEditorInternal() with static_cast.
+    */
+   mozilla::HTMLEditor* GetHTMLEditor();
+   nsresult SetHTMLEditor(mozilla::HTMLEditor* aHTMLEditor);
+ %}
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -12,16 +12,18 @@
+ 
+ // Local Includes
+ #include "Navigator.h"
+ #include "nsContentSecurityManager.h"
+ #include "nsScreen.h"
+ #include "nsHistory.h"
+ #include "nsDOMNavigationTiming.h"
+ #include "nsIDOMStorageManager.h"
++#include "mozilla/dom/ClientManager.h"
++#include "mozilla/dom/ClientSource.h"
+ #include "mozilla/dom/LocalStorage.h"
+ #include "mozilla/dom/Storage.h"
+ #include "mozilla/dom/IdleRequest.h"
+ #include "mozilla/dom/Performance.h"
+ #include "mozilla/dom/StorageEvent.h"
+ #include "mozilla/dom/StorageEventBinding.h"
+ #include "mozilla/dom/StorageNotifierService.h"
+ #include "mozilla/dom/StorageUtils.h"
+@@ -2129,16 +2131,19 @@ nsGlobalWindow::FreeInnerObjects()
+   DisableGamepadUpdates();
+   mHasGamepad = false;
+   mGamepads.Clear();
+   DisableVRUpdates();
+   mHasVREvents = false;
+   mHasVRDisplayActivateEvents = false;
+   mVRDisplays.Clear();
+ 
++  // This breaks a cycle between the window and the ClientSource object.
++  mClientSource.reset();
++
+   if (mTabChild) {
+     while (mBeforeUnloadListenerCount-- > 0) {
+       mTabChild->BeforeUnloadRemoved();
+     }
+   }
+ }
+ 
+ //*****************************************************************************
+@@ -2404,16 +2409,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
+   tmp->UnlinkHostObjectURIs();
+ 
+   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
+ 
+   // Here the IdleRequest list would've been unlinked, but we rely on
+   // that IdleRequest objects have been traced and will remove
+   // themselves while unlinking.
+ 
++  tmp->mClientSource.reset();
++
+   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPromises)
+ 
+   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+ 
+ #ifdef DEBUG
+ void
+ nsGlobalWindow::RiskyUnlink()
+@@ -3239,24 +3246,32 @@ nsGlobalWindow::SetNewDocument(nsIDocume
+         newInnerWindow->mDoc = aDocument;
+ 
+         // The storage objects contain the URL of the window. We have to
+         // recreate them when the innerWindow is reused.
+         newInnerWindow->mLocalStorage = nullptr;
+         newInnerWindow->mSessionStorage = nullptr;
+ 
+         newInnerWindow->ClearDocumentDependentSlots(cx);
++
++        // When replacing an initial about:blank document we call
++        // ExecutionReady again to update the client creation URL.
++        rv = newInnerWindow->ExecutionReady();
++        NS_ENSURE_SUCCESS(rv, rv);
+       }
+     } else {
+       newInnerWindow->InnerSetNewDocument(cx, aDocument);
+ 
+       // Initialize DOM classes etc on the inner window.
+       JS::Rooted<JSObject*> obj(cx, newInnerGlobal);
+       rv = kungFuDeathGrip->InitClasses(obj);
+       NS_ENSURE_SUCCESS(rv, rv);
++
++      rv = newInnerWindow->ExecutionReady();
++      NS_ENSURE_SUCCESS(rv, rv);
+     }
+ 
+     // If the document comes from a JAR, check if the channel was determined
+     // to be unsafe. If so, permanently disable script on the compartment by
+     // calling Block() and throwing away the key.
+     nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aDocument->GetChannel());
+     if (jarChannel && jarChannel->GetIsUnsafe()) {
+       xpc::Scriptability::Get(newInnerGlobal).Block();
+@@ -3420,16 +3435,78 @@ nsGlobalWindow::InnerSetNewDocument(JSCo
+ 
+   Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
+                         mMutationBits ? 1 : 0);
+ 
+   // Clear our mutation bitfield.
+   mMutationBits = 0;
+ }
+ 
++nsresult
++nsGlobalWindow::EnsureClientSource()
++{
++  MOZ_DIAGNOSTIC_ASSERT(mDoc);
++
++  nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
++  nsCOMPtr<nsILoadInfo> loadInfo = channel ? channel->GetLoadInfo() : nullptr;
++
++  // Try to get the reserved client from the LoadInfo.  A Client is
++  // reserved at the start of the channel load if there is not an
++  // initial about:blank document that will be reused.  It is also
++  // created if the channel load encounters a cross-origin redirect.
++  if (loadInfo) {
++    UniquePtr<ClientSource> reservedClient = loadInfo->TakeReservedClientSource();
++    if (reservedClient) {
++      mClientSource.reset();
++      mClientSource = Move(reservedClient);
++    }
++  }
++
++  // We don't have a LoadInfo reserved client, but maybe we should
++  // be inheriting an initial one from the docshell.  This means
++  // that the docshell started the channel load before creating the
++  // initial about:blank document.  This is an optimization, though,
++  // and it created an initial Client as a placeholder for the document.
++  // In this case we want to inherit this placeholder Client here.
++  if (!mClientSource) {
++    nsIDocShell* docshell = GetDocShell();
++    if (docshell) {
++      mClientSource = docshell->TakeInitialClientSource();
++    }
++  }
++ 
++  // If we don't have a reserved client or an initial client, then create
++  // one now.  This can happen in certain cases where we avoid preallocating
++  // the client in the docshell.  This mainly occurs in situations where
++  // the principal is not clearly inherited from the parent; e.g. sandboxed
++  // iframes, window.open(), etc.
++  if (!mClientSource) {
++    mClientSource = ClientManager::CreateSource(ClientType::Window,
++                                                EventTargetFor(TaskCategory::Other),
++                                                mDoc->NodePrincipal());
++    if (NS_WARN_IF(!mClientSource)) {
++      return NS_ERROR_FAILURE;
++    }
++  }
++
++  return NS_OK;
++}
++
++nsresult
++nsGlobalWindow::ExecutionReady()
++{
++  nsresult rv = EnsureClientSource();
++  NS_ENSURE_SUCCESS(rv, rv);
++
++  rv = mClientSource->WindowExecutionReady(AsInner());
++  NS_ENSURE_SUCCESS(rv, rv);
++
++  return NS_OK;
++}
++
+ void
+ nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell)
+ {
+   NS_ASSERTION(IsOuterWindow(), "Uh, SetDocShell() called on inner window!");
+   MOZ_ASSERT(aDocShell);
+ 
+   if (aDocShell == mDocShell) {
+     return;
+@@ -4283,16 +4360,22 @@ nsPIDOMWindowInner::Thaw()
+ }
+ 
+ void
+ nsPIDOMWindowInner::SyncStateFromParentWindow()
+ {
+   nsGlobalWindow::Cast(this)->SyncStateFromParentWindow();
+ }
+ 
++Maybe<ClientInfo>
++nsPIDOMWindowInner::GetClientInfo() const
++{
++  return Move(nsGlobalWindow::Cast(this)->GetClientInfo());
++}
++
+ void
+ nsGlobalWindow::UpdateTopInnerWindow()
+ {
+   if (!IsInnerWindow() || AsInner()->IsTopInnerWindow()) {
+     return;
+   }
+ 
+   AsInner()->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
+@@ -12459,16 +12542,27 @@ nsGlobalWindow::CallOnChildren(Method aM
+     if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) {
+       continue;
+     }
+ 
+     (inner->*aMethod)();
+   }
+ }
+ 
++Maybe<ClientInfo>
++nsGlobalWindow::GetClientInfo() const
++{
++  MOZ_ASSERT(NS_IsMainThread());
++  Maybe<ClientInfo> clientInfo;
++  if (mClientSource) {
++    clientInfo.emplace(mClientSource->Info());
++  }
++  return Move(clientInfo);
++}
++
+ nsresult
+ nsGlobalWindow::FireDelayedDOMEvents()
+ {
+   FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED);
+ 
+   if (mApplicationCache) {
+     static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
+   }
+diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h
+--- a/dom/base/nsGlobalWindow.h
++++ b/dom/base/nsGlobalWindow.h
+@@ -106,16 +106,17 @@ class IdleRequestExecutor;
+ 
+ namespace mozilla {
+ class AbstractThread;
+ class DOMEventTargetHelper;
+ class ThrottledEventQueue;
+ namespace dom {
+ class BarProp;
+ struct ChannelPixelLayout;
++class ClientSource;
+ class Console;
+ class Crypto;
+ class CustomElementRegistry;
+ class DocGroup;
+ class External;
+ class Function;
+ class Gamepad;
+ enum class ImageBitmapFormat : uint8_t;
+@@ -397,16 +398,18 @@ public:
+   void Suspend();
+   void Resume();
+   virtual bool IsSuspended() const override;
+   void Freeze();
+   void Thaw();
+   virtual bool IsFrozen() const override;
+   void SyncStateFromParentWindow();
+ 
++  mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
++
+   virtual nsresult FireDelayedDOMEvents() override;
+ 
+   // Outer windows only.
+   virtual bool WouldReuseInnerWindow(nsIDocument* aNewDocument) override;
+ 
+   virtual void SetDocShell(nsIDocShell* aDocShell) override;
+   virtual void DetachFromDocShell() override;
+   virtual nsresult SetNewDocument(nsIDocument *aDocument,
+@@ -1462,16 +1465,19 @@ protected:
+ 
+   void FreeInnerObjects();
+   nsGlobalWindow *CallerInnerWindow();
+ 
+   // Only to be called on an inner window.
+   // aDocument must not be null.
+   void InnerSetNewDocument(JSContext* aCx, nsIDocument* aDocument);
+ 
++  nsresult EnsureClientSource();
++  nsresult ExecutionReady();
++
+   // Inner windows only.
+   nsresult DefineArgumentsProperty(nsIArray *aArguments);
+ 
+   // Get the parent, returns null if this is a toplevel window
+   nsPIDOMWindowOuter* GetParentInternal();
+ 
+ public:
+   // popup tracking
+@@ -2032,16 +2038,18 @@ protected:
+   // When non-zero, the document should receive a vrdisplayactivate event
+   // after loading.  The value is the ID of the VRDisplay that content should
+   // begin presentation on.
+   uint32_t mAutoActivateVRDisplayID; // Outer windows only
+   int64_t mBeforeUnloadListenerCount; // Inner windows only
+ 
+   RefPtr<mozilla::dom::IntlUtils> mIntlUtils;
+ 
++  mozilla::UniquePtr<mozilla::dom::ClientSource> mClientSource;
++
+   nsTArray<RefPtr<mozilla::dom::Promise>> mPendingPromises; // Inner windows only
+ 
+   friend class nsDOMScriptableHelper;
+   friend class nsDOMWindowUtils;
+   friend class mozilla::dom::PostMessageEvent;
+   friend class DesktopNotification;
+   friend class mozilla::dom::TimeoutManager;
+   friend class IdleRequestExecutor;
+diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h
+--- a/dom/base/nsPIDOMWindow.h
++++ b/dom/base/nsPIDOMWindow.h
+@@ -40,16 +40,17 @@ class nsPIWindowRoot;
+ class nsXBLPrototypeHandler;
+ 
+ typedef uint32_t SuspendTypes;
+ 
+ namespace mozilla {
+ class ThrottledEventQueue;
+ namespace dom {
+ class AudioContext;
++class ClientInfo;
+ class DocGroup;
+ class TabGroup;
+ class Element;
+ class Performance;
+ class ServiceWorkerRegistration;
+ class Timeout;
+ class TimeoutManager;
+ class CustomElementRegistry;
+@@ -930,16 +931,18 @@ public:
+ 
+   // Increase/Decrease the number of open WebSockets.
+   void UpdateWebSocketCount(int32_t aDelta);
+ 
+   // Return true if there are any open WebSockets that could block
+   // timeout-throttling.
+   bool HasOpenWebSockets() const;
+ 
++  mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
++
+ protected:
+   void CreatePerformanceObjectIfNeeded();
+ };
+ 
+ NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
+ 
+ // NB: It's very very important that these two classes have identical vtables
+ // and memory layout!

+ 441 - 0
mozilla-release/patches/1419536-4-59a1.patch

@@ -0,0 +1,441 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1511295185 18000
+# Node ID 61d93d8a46679c63f56b0ad4719834e87a7a7f72
+# Parent  e91820ef7d982cd096558634056b19ab53b6c57c
+Bug 1419536 P4 Allow ClientSource objects to be frozen while in bfcache. r=baku
+
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -3440,56 +3440,69 @@ nsGlobalWindow::InnerSetNewDocument(JSCo
+   mMutationBits = 0;
+ }
+ 
+ nsresult
+ nsGlobalWindow::EnsureClientSource()
+ {
+   MOZ_DIAGNOSTIC_ASSERT(mDoc);
+ 
++  bool newClientSource = false;
++
+   nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
+   nsCOMPtr<nsILoadInfo> loadInfo = channel ? channel->GetLoadInfo() : nullptr;
+ 
+   // Try to get the reserved client from the LoadInfo.  A Client is
+   // reserved at the start of the channel load if there is not an
+   // initial about:blank document that will be reused.  It is also
+   // created if the channel load encounters a cross-origin redirect.
+   if (loadInfo) {
+     UniquePtr<ClientSource> reservedClient = loadInfo->TakeReservedClientSource();
+     if (reservedClient) {
+       mClientSource.reset();
+       mClientSource = Move(reservedClient);
++      newClientSource = true;
+     }
+   }
+ 
+   // We don't have a LoadInfo reserved client, but maybe we should
+   // be inheriting an initial one from the docshell.  This means
+   // that the docshell started the channel load before creating the
+   // initial about:blank document.  This is an optimization, though,
+   // and it created an initial Client as a placeholder for the document.
+   // In this case we want to inherit this placeholder Client here.
+   if (!mClientSource) {
+     nsIDocShell* docshell = GetDocShell();
+     if (docshell) {
+       mClientSource = docshell->TakeInitialClientSource();
++      if (mClientSource) {
++        newClientSource = true;
++      }
+     }
+   }
+  
+   // If we don't have a reserved client or an initial client, then create
+   // one now.  This can happen in certain cases where we avoid preallocating
+   // the client in the docshell.  This mainly occurs in situations where
+   // the principal is not clearly inherited from the parent; e.g. sandboxed
+   // iframes, window.open(), etc.
+   if (!mClientSource) {
+     mClientSource = ClientManager::CreateSource(ClientType::Window,
+                                                 EventTargetFor(TaskCategory::Other),
+                                                 mDoc->NodePrincipal());
+     if (NS_WARN_IF(!mClientSource)) {
+       return NS_ERROR_FAILURE;
+     }
++    newClientSource = true;
++  }
++
++  // Its possible that we got a client just after being frozen in
++  // the bfcache.  In that case freeze the client immediately.
++  if (newClientSource && IsFrozen()) {
++    mClientSource->Freeze();
+   }
+ 
+   return NS_OK;
+ }
+ 
+ nsresult
+ nsGlobalWindow::ExecutionReady()
+ {
+@@ -12405,16 +12418,19 @@ nsGlobalWindow::FreezeInternal()
+   MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
+   if (mFreezeDepth != 1) {
+     return;
+   }
+ 
+   mozilla::dom::workers::FreezeWorkersForWindow(AsInner());
+ 
+   mTimeoutManager->Freeze();
++  if (mClientSource) {
++    mClientSource->Freeze();
++  }
+ 
+   NotifyDOMWindowFrozen(this);
+ }
+ 
+ void
+ nsGlobalWindow::Thaw()
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+@@ -12434,16 +12450,19 @@ nsGlobalWindow::ThawInternal()
+ 
+   MOZ_ASSERT(mFreezeDepth != 0);
+   mFreezeDepth -= 1;
+   MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
+   if (mFreezeDepth != 0) {
+     return;
+   }
+ 
++  if (mClientSource) {
++    mClientSource->Thaw();
++  }
+   mTimeoutManager->Thaw();
+ 
+   mozilla::dom::workers::ThawWorkersForWindow(AsInner());
+ 
+   NotifyDOMWindowThawed(this);
+ }
+ 
+ bool
+diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp
+--- a/dom/clients/manager/ClientManagerService.cpp
++++ b/dom/clients/manager/ClientManagerService.cpp
+@@ -121,17 +121,18 @@ ClientManagerService::FindSource(const n
+   AssertIsOnBackgroundThread();
+ 
+   auto entry = mSourceTable.Lookup(aID);
+   if (!entry) {
+     return nullptr;
+   }
+ 
+   ClientSourceParent* source = entry.Data();
+-  if (!MatchPrincipalInfo(source->Info().PrincipalInfo(), aPrincipalInfo)) {
++  if (source->IsFrozen() ||
++      !MatchPrincipalInfo(source->Info().PrincipalInfo(), aPrincipalInfo)) {
+     return nullptr;
+   }
+ 
+   return source;
+ }
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -230,16 +230,32 @@ ClientSource::DocShellExecutionReady(nsI
+ 
+   ClientSourceExecutionReadyArgs args(NS_LITERAL_CSTRING("about:blank"),
+                                       frameType);
+   ExecutionReady(args);
+ 
+   return NS_OK;
+ }
+ 
++void
++ClientSource::Freeze()
++{
++  MaybeExecute([](PClientSourceChild* aActor) {
++    aActor->SendFreeze();
++  });
++}
++
++void
++ClientSource::Thaw()
++{
++  MaybeExecute([](PClientSourceChild* aActor) {
++    aActor->SendThaw();
++  });
++}
++
+ const ClientInfo&
+ ClientSource::Info() const
+ {
+   return mClientInfo;
+ }
+ 
+ nsISerialEventTarget*
+ ClientSource::EventTarget() const
+diff --git a/dom/clients/manager/ClientSource.h b/dom/clients/manager/ClientSource.h
+--- a/dom/clients/manager/ClientSource.h
++++ b/dom/clients/manager/ClientSource.h
+@@ -77,16 +77,22 @@ public:
+   WorkerExecutionReady(mozilla::dom::workers::WorkerPrivate* aWorkerPrivate);
+ 
+   nsresult
+   WindowExecutionReady(nsPIDOMWindowInner* aInnerWindow);
+ 
+   nsresult
+   DocShellExecutionReady(nsIDocShell* aDocShell);
+ 
++  void
++  Freeze();
++
++  void
++  Thaw();
++
+   const ClientInfo&
+   Info() const;
+ 
+   nsISerialEventTarget*
+   EventTarget() const;
+ };
+ 
+ } // namespace dom
+diff --git a/dom/clients/manager/ClientSourceParent.cpp b/dom/clients/manager/ClientSourceParent.cpp
+--- a/dom/clients/manager/ClientSourceParent.cpp
++++ b/dom/clients/manager/ClientSourceParent.cpp
+@@ -106,16 +106,40 @@ ClientSourceParent::RecvExecutionReady(c
+ 
+   for (ClientHandleParent* handle : mHandleList) {
+     Unused << handle->SendExecutionReady(mClientInfo.ToIPC());
+   }
+ 
+   return IPC_OK();
+ };
+ 
++IPCResult
++ClientSourceParent::RecvFreeze()
++{
++  MOZ_DIAGNOSTIC_ASSERT(!mFrozen);
++  mFrozen = true;
++
++  // Frozen clients should not be observable.  Act as if the client has
++  // been destroyed.
++  nsTArray<ClientHandleParent*> handleList(mHandleList);
++  for (ClientHandleParent* handle : handleList) {
++    Unused << ClientHandleParent::Send__delete__(handle);
++  }
++
++  return IPC_OK();
++}
++
++IPCResult
++ClientSourceParent::RecvThaw()
++{
++  MOZ_DIAGNOSTIC_ASSERT(mFrozen);
++  mFrozen = false;
++  return IPC_OK();
++}
++
+ void
+ ClientSourceParent::ActorDestroy(ActorDestroyReason aReason)
+ {
+   DebugOnly<bool> removed = mService->RemoveSource(this);
+   MOZ_ASSERT(removed);
+ 
+   nsTArray<ClientHandleParent*> handleList(mHandleList);
+   for (ClientHandleParent* handle : handleList) {
+@@ -139,16 +163,17 @@ ClientSourceParent::DeallocPClientSource
+   delete aActor;
+   return true;
+ }
+ 
+ ClientSourceParent::ClientSourceParent(const ClientSourceConstructorArgs& aArgs)
+   : mClientInfo(aArgs.id(), aArgs.type(), aArgs.principalInfo(), aArgs.creationTime())
+   , mService(ClientManagerService::GetOrCreateInstance())
+   , mExecutionReady(false)
++  , mFrozen(false)
+ {
+ }
+ 
+ ClientSourceParent::~ClientSourceParent()
+ {
+   MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty());
+ }
+ 
+@@ -173,20 +198,27 @@ ClientSourceParent::Init()
+ }
+ 
+ const ClientInfo&
+ ClientSourceParent::Info() const
+ {
+   return mClientInfo;
+ }
+ 
++bool
++ClientSourceParent::IsFrozen() const
++{
++  return mFrozen;
++}
++
+ void
+ ClientSourceParent::AttachHandle(ClientHandleParent* aClientHandle)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(aClientHandle);
++  MOZ_DIAGNOSTIC_ASSERT(!mFrozen);
+   MOZ_ASSERT(!mHandleList.Contains(aClientHandle));
+   mHandleList.AppendElement(aClientHandle);
+ }
+ 
+ void
+ ClientSourceParent::DetachHandle(ClientHandleParent* aClientHandle)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(aClientHandle);
+diff --git a/dom/clients/manager/ClientSourceParent.h b/dom/clients/manager/ClientSourceParent.h
+--- a/dom/clients/manager/ClientSourceParent.h
++++ b/dom/clients/manager/ClientSourceParent.h
+@@ -16,27 +16,34 @@ class ClientHandleParent;
+ class ClientManagerService;
+ 
+ class ClientSourceParent final : public PClientSourceParent
+ {
+   ClientInfo mClientInfo;
+   RefPtr<ClientManagerService> mService;
+   nsTArray<ClientHandleParent*> mHandleList;
+   bool mExecutionReady;
++  bool mFrozen;
+ 
+   void
+   KillInvalidChild();
+ 
+   // PClientSourceParent
+   mozilla::ipc::IPCResult
+   RecvTeardown() override;
+ 
+   mozilla::ipc::IPCResult
+   RecvExecutionReady(const ClientSourceExecutionReadyArgs& aArgs) override;
+ 
++  mozilla::ipc::IPCResult
++  RecvFreeze() override;
++
++  mozilla::ipc::IPCResult
++  RecvThaw() override;
++
+   void
+   ActorDestroy(ActorDestroyReason aReason) override;
+ 
+   PClientSourceOpParent*
+   AllocPClientSourceOpParent(const ClientOpConstructorArgs& aArgs) override;
+ 
+   bool
+   DeallocPClientSourceOpParent(PClientSourceOpParent* aActor) override;
+@@ -46,16 +53,19 @@ public:
+   ~ClientSourceParent();
+ 
+   void
+   Init();
+ 
+   const ClientInfo&
+   Info() const;
+ 
++  bool
++  IsFrozen() const;
++
+   void
+   AttachHandle(ClientHandleParent* aClientSource);
+ 
+   void
+   DetachHandle(ClientHandleParent* aClientSource);
+ };
+ 
+ } // namespace dom
+diff --git a/dom/clients/manager/PClientSource.ipdl b/dom/clients/manager/PClientSource.ipdl
+--- a/dom/clients/manager/PClientSource.ipdl
++++ b/dom/clients/manager/PClientSource.ipdl
+@@ -17,16 +17,18 @@ sync protocol PClientSource
+ {
+   manager PClientManager;
+ 
+   manages PClientSourceOp;
+ 
+ parent:
+   async Teardown();
+   async ExecutionReady(ClientSourceExecutionReadyArgs aArgs);
++  async Freeze();
++  async Thaw();
+ 
+ child:
+   async PClientSourceOp(ClientOpConstructorArgs aArgs);
+ 
+   async __delete__();
+ };
+ 
+ } // namespace dom
+diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
+--- a/dom/workers/WorkerPrivate.cpp
++++ b/dom/workers/WorkerPrivate.cpp
+@@ -5318,16 +5318,19 @@ WorkerPrivate::EnsureClientSource()
+       type = ClientType::Serviceworker;
+       break;
+     default:
+       MOZ_CRASH("unknown worker type!");
+   }
+ 
+   mClientSource = ClientManager::CreateSource(type, mWorkerHybridEventTarget,
+                                               GetPrincipalInfo());
++  if (mFrozen) {
++    mClientSource->Freeze();
++  }
+ }
+ 
+ const ClientInfo&
+ WorkerPrivate::GetClientInfo() const
+ {
+   AssertIsOnWorkerThread();
+   MOZ_DIAGNOSTIC_ASSERT(mClientSource);
+   return mClientSource->Info();
+@@ -5675,16 +5678,20 @@ WorkerPrivate::ClearDebuggerEventQueue()
+ 
+ bool
+ WorkerPrivate::FreezeInternal()
+ {
+   AssertIsOnWorkerThread();
+ 
+   NS_ASSERTION(!mFrozen, "Already frozen!");
+ 
++  if (mClientSource) {
++    mClientSource->Freeze();
++  }
++
+   mFrozen = true;
+ 
+   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
+     mChildWorkers[index]->Freeze(nullptr);
+   }
+ 
+   return true;
+ }
+@@ -5696,16 +5703,21 @@ WorkerPrivate::ThawInternal()
+ 
+   NS_ASSERTION(mFrozen, "Not yet frozen!");
+ 
+   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
+     mChildWorkers[index]->Thaw(nullptr);
+   }
+ 
+   mFrozen = false;
++
++  if (mClientSource) {
++    mClientSource->Thaw();
++  }
++
+   return true;
+ }
+ 
+ void
+ WorkerPrivate::TraverseTimeouts(nsCycleCollectionTraversalCallback& cb)
+ {
+   for (uint32_t i = 0; i < mTimeouts.Length(); ++i) {
+     TimeoutInfo* tmp = mTimeouts[i];

+ 36 - 0
mozilla-release/patches/1419536-5-59a1.patch

@@ -0,0 +1,36 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1511295185 18000
+# Node ID dbbca05aa64a14580d3ab23950e148019a097218
+# Parent  6babb535fca120de522cf786de787d05038e0c5b
+Bug 1419536 P5 Set ClientInfo on the LoadInfo for document owned network requests. r=baku
+
+diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp
+--- a/netwerk/base/LoadInfo.cpp
++++ b/netwerk/base/LoadInfo.cpp
+@@ -104,16 +104,25 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
+   // if the load is sandboxed, we can not also inherit the principal
+   if (mSecurityFlags & nsILoadInfo::SEC_SANDBOXED) {
+     mForceInheritPrincipalDropped =
+       (mSecurityFlags & nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL);
+     mSecurityFlags &= ~nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
+   }
+ 
+   if (aLoadingContext) {
++    // Ensure that all network requests for a window client have the ClientInfo
++    // properly set.
++    // TODO: The ClientInfo is not set properly for worker initiated requests yet.
++    nsCOMPtr<nsPIDOMWindowInner> contextInner =
++      aLoadingContext->OwnerDoc()->GetInnerWindow();
++    if (contextInner) {
++      mClientInfo = contextInner->GetClientInfo();
++    }
++
+     nsCOMPtr<nsPIDOMWindowOuter> contextOuter = aLoadingContext->OwnerDoc()->GetWindow();
+     if (contextOuter) {
+       ComputeIsThirdPartyContext(contextOuter);
+       mOuterWindowID = contextOuter->WindowID();
+       nsCOMPtr<nsPIDOMWindowOuter> parent = contextOuter->GetScriptableParent();
+       mParentOuterWindowID = parent ? parent->WindowID() : mOuterWindowID;
+       mTopOuterWindowID = FindTopOuterWindowID(contextOuter);
+     }

+ 126 - 0
mozilla-release/patches/1420221-59a1.patch

@@ -0,0 +1,126 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1511812097 18000
+# Node ID afefcc7ecca06203a0ea46f4f6949ab509911c93
+# Parent  c4ab0ad57f756a32aa6717c492bb2f3e27c3ecb7
+Bug 1420221 Make workers handle the case when the ClientSource cannot be created during shutdown. r=baku
+
+diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
+--- a/dom/workers/WorkerPrivate.cpp
++++ b/dom/workers/WorkerPrivate.cpp
+@@ -595,17 +595,19 @@ private:
+   // run we have not yet done our load so don't know things like our final
+   // principal and whatnot.
+ 
+   virtual bool
+   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+   {
+     aWorkerPrivate->AssertIsOnWorkerThread();
+ 
+-    aWorkerPrivate->EnsureClientSource();
++    if (NS_WARN_IF(!aWorkerPrivate->EnsureClientSource())) {
++      return false;
++    }
+ 
+     ErrorResult rv;
+     scriptloader::LoadMainScript(aWorkerPrivate, mScriptURL, WorkerScript, rv);
+     rv.WouldReportJSException();
+     // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
+     // return false and don't SetWorkerScriptExecutedSuccessfully() in that
+     // case, but don't throw anything on aCx.  The idea is to not dispatch error
+     // events if our load is canceled with that error code.
+@@ -667,17 +669,19 @@ private:
+ 
+     WorkerDebuggerGlobalScope* globalScope =
+       aWorkerPrivate->CreateDebuggerGlobalScope(aCx);
+     if (!globalScope) {
+       NS_WARNING("Failed to make global!");
+       return false;
+     }
+ 
+-    aWorkerPrivate->EnsureClientSource();
++    if (NS_WARN_IF(!aWorkerPrivate->EnsureClientSource())) {
++      return false;
++    }
+ 
+     JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
+ 
+     ErrorResult rv;
+     JSAutoCompartment ac(aCx, global);
+     scriptloader::LoadMainScript(aWorkerPrivate, mScriptURL,
+                                  DebuggerScript, rv);
+     rv.WouldReportJSException();
+@@ -5292,23 +5296,23 @@ WorkerPrivate::ControlEventTarget()
+ }
+ 
+ nsISerialEventTarget*
+ WorkerPrivate::HybridEventTarget()
+ {
+   return mWorkerHybridEventTarget;
+ }
+ 
+-void
++bool
+ WorkerPrivate::EnsureClientSource()
+ {
+   AssertIsOnWorkerThread();
+ 
+   if (mClientSource) {
+-    return;
++    return true;
+   }
+ 
+   ClientType type;
+   switch(Type()) {
+     case WorkerTypeDedicated:
+       type = ClientType::Worker;
+       break;
+     case WorkerTypeShared:
+@@ -5318,19 +5322,25 @@ WorkerPrivate::EnsureClientSource()
+       type = ClientType::Serviceworker;
+       break;
+     default:
+       MOZ_CRASH("unknown worker type!");
+   }
+ 
+   mClientSource = ClientManager::CreateSource(type, mWorkerHybridEventTarget,
+                                               GetPrincipalInfo());
++  if (!mClientSource) {
++    return false;
++  }
++
+   if (mFrozen) {
+     mClientSource->Freeze();
+   }
++
++  return true;
+ }
+ 
+ const ClientInfo&
+ WorkerPrivate::GetClientInfo() const
+ {
+   AssertIsOnWorkerThread();
+   MOZ_DIAGNOSTIC_ASSERT(mClientSource);
+   return mClientSource->Info();
+diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
+--- a/dom/workers/WorkerPrivate.h
++++ b/dom/workers/WorkerPrivate.h
+@@ -1492,17 +1492,17 @@ public:
+   // Get an event target that will attempt to dispatch a normal WorkerRunnable,
+   // but if that fails will then fall back to a control runnable.
+   nsISerialEventTarget*
+   HybridEventTarget();
+ 
+   void
+   DumpCrashInformation(nsACString& aString);
+ 
+-  void
++  bool
+   EnsureClientSource();
+ 
+   const ClientInfo&
+   GetClientInfo() const;
+ 
+   void
+   ExecutionReady();
+ 

+ 5 - 5
mozilla-release/patches/1420594-1-59a1.patch

@@ -2,7 +2,7 @@
 # User Ben Kelly <ben@wanderview.com>
 # Date 1512399092 18000
 # Node ID 7ba9a363b3d2ae4bb0460fafc7279196754211b0
-# Parent  91afe90c9fb99829041f8fb64609a956791c4d9c
+# Parent  c1842f75058956c2af320f3fb5cdcf6a2a1c0ef9
 Bug 1420594 P1 Make ClientManagerService track active ClientManagerParent actors. r=baku
 
 diff --git a/dom/clients/manager/ClientManagerActors.cpp b/dom/clients/manager/ClientManagerActors.cpp
@@ -111,9 +111,9 @@ diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/
  // static
  already_AddRefed<ClientManagerService>
  ClientManagerService::GetOrCreateInstance()
-@@ -128,10 +129,28 @@ ClientManagerService::FindSource(const n
-   ClientSourceParent* source = entry.Data();
-   if (!MatchPrincipalInfo(source->Info().PrincipalInfo(), aPrincipalInfo)) {
+@@ -129,10 +130,28 @@ ClientManagerService::FindSource(const n
+   if (source->IsFrozen() ||
+       !MatchPrincipalInfo(source->Info().PrincipalInfo(), aPrincipalInfo)) {
      return nullptr;
    }
  
@@ -199,7 +199,7 @@ diff --git a/dom/clients/manager/ClientManagerService.h b/dom/clients/manager/Cl
 diff --git a/ipc/glue/BackgroundParentImpl.cpp b/ipc/glue/BackgroundParentImpl.cpp
 --- a/ipc/glue/BackgroundParentImpl.cpp
 +++ b/ipc/glue/BackgroundParentImpl.cpp
-@@ -976,16 +976,23 @@ BackgroundParentImpl::AllocPClientManage
+@@ -975,16 +975,23 @@ BackgroundParentImpl::AllocPClientManage
  }
  
  bool

+ 149 - 0
mozilla-release/patches/1420743-1-59a1.patch

@@ -0,0 +1,149 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1511889040 18000
+# Node ID 2319e3c47580aadcecdb404cc490752f75da744c
+# Parent  de34b5d2a9760c73a12615abf8a34991146bde7b
+Bug 1420743 P1 Do a better job of clearing docshell's mInitialClientSource at the end of page load. r=baku
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -7734,16 +7734,21 @@ nsDocShell::OnSecurityChange(nsIWebProgr
+ nsresult
+ nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
+                         nsIChannel* aChannel, nsresult aStatus)
+ {
+   if (!aChannel) {
+     return NS_ERROR_NULL_POINTER;
+   }
+ 
++  // Make sure to discard the initial client if we never created the initial
++  // about:blank document.  Do this before possibly returning from the method
++  // due to an error.
++  mInitialClientSource.reset();
++
+   nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
+   if (reporter) {
+     nsCOMPtr<nsILoadGroup> loadGroup;
+     aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+     if (loadGroup) {
+       reporter->FlushConsoleReports(loadGroup);
+     } else {
+       reporter->FlushConsoleReports(GetDocument());
+@@ -7769,20 +7774,16 @@ nsDocShell::EndPageLoad(nsIWebProgress* 
+         internalLoadGroup->OnEndPageLoad(aChannel);
+       }
+     }
+   }
+ 
+   // Timing is picked up by the window, we don't need it anymore
+   mTiming = nullptr;
+ 
+-  // Make sure to discard the initial client if we never created the initial
+-  // about:blank document.
+-  mInitialClientSource.reset();
+-
+   // clean up reload state for meta charset
+   if (eCharsetReloadRequested == mCharsetReloadState) {
+     mCharsetReloadState = eCharsetReloadStopOrigional;
+   } else {
+     mCharsetReloadState = eCharsetReloadInit;
+   }
+ 
+   // Save a pointer to the currently-loading history entry.
+@@ -11723,16 +11724,21 @@ nsDocShell::DoChannelLoad(nsIChannel* aC
+   uint32_t openFlags = 0;
+   if (mLoadType == LOAD_LINK) {
+     openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
+   }
+   if (!mAllowContentRetargeting) {
+     openFlags |= nsIURILoader::DONT_RETARGET;
+   }
+ 
++  // If anything fails here, make sure to clear our initial ClientSource.
++  auto cleanupInitialClient = MakeScopeExit([&] {
++    mInitialClientSource.reset();
++  });
++
+   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+   NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
+ 
+   MaybeCreateInitialClientSource();
+ 
+   // Since we are loading a document we need to make sure the proper reserved
+   // and initial client data is stored on the nsILoadInfo.  The
+   // ClientChannelHelper does this and ensures that it is propagated properly
+@@ -11748,16 +11754,19 @@ nsDocShell::DoChannelLoad(nsIChannel* aC
+   rv = aURILoader->OpenURI(aChannel, openFlags, this);
+   NS_ENSURE_SUCCESS(rv, rv);
+ 
+   // We're about to load a new page and it may take time before necko
+   // gives back any data, so main thread might have a chance to process a
+   // collector slice
+   nsJSContext::MaybeRunNextCollectorSlice(this, JS::gcreason::DOCSHELL);
+ 
++  // Success.  Keep the initial ClientSource if it exists.
++  cleanupInitialClient.release();
++
+   return NS_OK;
+ }
+ 
+ nsresult
+ nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
+                            nsACString& aNewHash, uint32_t aLoadType)
+ {
+   if (!mCurrentURI) {
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -3445,16 +3445,24 @@ nsGlobalWindow::EnsureClientSource()
+ {
+   MOZ_DIAGNOSTIC_ASSERT(mDoc);
+ 
+   bool newClientSource = false;
+ 
+   nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
+   nsCOMPtr<nsILoadInfo> loadInfo = channel ? channel->GetLoadInfo() : nullptr;
+ 
++  // Take the initial client source from the docshell immediately.  Even if we
++  // don't end up using it here we should consume it.
++  UniquePtr<ClientSource> initialClientSource;
++  nsIDocShell* docshell = GetDocShell();
++  if (docshell) {
++    initialClientSource = docshell->TakeInitialClientSource();
++  }
++
+   // Try to get the reserved client from the LoadInfo.  A Client is
+   // reserved at the start of the channel load if there is not an
+   // initial about:blank document that will be reused.  It is also
+   // created if the channel load encounters a cross-origin redirect.
+   if (loadInfo) {
+     UniquePtr<ClientSource> reservedClient = loadInfo->TakeReservedClientSource();
+     if (reservedClient) {
+       mClientSource.reset();
+@@ -3465,22 +3473,19 @@ nsGlobalWindow::EnsureClientSource()
+ 
+   // We don't have a LoadInfo reserved client, but maybe we should
+   // be inheriting an initial one from the docshell.  This means
+   // that the docshell started the channel load before creating the
+   // initial about:blank document.  This is an optimization, though,
+   // and it created an initial Client as a placeholder for the document.
+   // In this case we want to inherit this placeholder Client here.
+   if (!mClientSource) {
+-    nsIDocShell* docshell = GetDocShell();
+-    if (docshell) {
+-      mClientSource = docshell->TakeInitialClientSource();
+-      if (mClientSource) {
+-        newClientSource = true;
+-      }
++    mClientSource = Move(initialClientSource);
++    if (mClientSource) {
++      newClientSource = true;
+     }
+   }
+  
+   // If we don't have a reserved client or an initial client, then create
+   // one now.  This can happen in certain cases where we avoid preallocating
+   // the client in the docshell.  This mainly occurs in situations where
+   // the principal is not clearly inherited from the parent; e.g. sandboxed
+   // iframes, window.open(), etc.

+ 30 - 0
mozilla-release/patches/1420743-2-59a1.patch

@@ -0,0 +1,30 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1511889040 18000
+# Node ID e7b3e385157a43ad065f36ccf3d07327dfc170a3
+# Parent  b6a4d2d3483112e98dbb62c27334e00552dc8ac9
+Bug 1420743 P2 Check the inner window for an existing document instead of the outer window in nsDocShell::EnsureInitialClientSource(). r=baku
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -3377,17 +3377,18 @@ nsDocShell::GetParentDocshell()
+   return docshell.forget().downcast<nsDocShell>();
+ }
+ 
+ void
+ nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal)
+ {
+   // If there is an existing document then there is no need to create
+   // a client for a future initial about:blank document.
+-  if (mScriptGlobal && mScriptGlobal->GetExtantDoc()) {
++  if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindowInternal() &&
++      mScriptGlobal->GetCurrentInnerWindowInternal()->GetExtantDoc()) {
+     MOZ_DIAGNOSTIC_ASSERT(
+       mScriptGlobal->GetCurrentInnerWindowInternal()->GetClientInfo().isSome());
+     MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
+     return;
+   }
+ 
+   // Don't recreate the initial client source.  We call this multiple times
+   // when DoChannelLoad() is called before CreateAboutBlankContentViewer.

+ 30 - 0
mozilla-release/patches/1422584-59a1.patch

@@ -0,0 +1,30 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512492311 18000
+# Node ID e18c1ff30e404e6376b00d57102d773c51de77de
+# Parent  bfb0d78aeebe22cfbabf068a469c2990d42681b1
+Bug 1422584 Handle nullptr ClientSource in nsDocShell::MaybeCreateInitialClientSource(). r=baku
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -3421,16 +3421,19 @@ nsDocShell::MaybeCreateInitialClientSour
+   if (!win) {
+     return;
+   }
+ 
+   mInitialClientSource =
+     ClientManager::CreateSource(ClientType::Window,
+                                 win->EventTargetFor(TaskCategory::Other),
+                                 principal);
++  if (NS_WARN_IF(!mInitialClientSource)) {
++    return;
++  }
+ 
+   // Mark the initial client as execution ready, but owned by the docshell.
+   // If the client is actually used this will cause ClientSource to force
+   // the creation of the initial about:blank by calling nsDocShell::GetDocument().
+   mInitialClientSource->DocShellExecutionReady(this);
+ }
+ 
+ Maybe<ClientInfo>

+ 139 - 0
mozilla-release/patches/1422983-59a1.patch

@@ -0,0 +1,139 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512443613 18000
+# Node ID 46463dab3129e5c56a96d34f1c275d14149d32bd
+# Parent  af9c1a9a51da51a3034c5166a6ffa86afd16ba02
+Bug 1422983 Cycle collect the ClientSource object when owned by an nsGlobalWindowInner or nsDocShell object. r=mccr8
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -948,16 +948,17 @@ nsDocShell::DestroyChildren()
+ 
+   nsDocLoader::DestroyChildren();
+ }
+ 
+ NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDocShell,
+                                    nsDocLoader,
+                                    mSessionStorageManager,
+                                    mScriptGlobal,
++                                   mInitialClientSource,
+                                    mChromeEventHandler)
+ 
+ NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
+ NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
+ 
+ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell)
+   NS_INTERFACE_MAP_ENTRY(nsIDocShell)
+   NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -2290,16 +2290,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
+ 
+   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
+   for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
+     cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
+   }
+ 
+   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
+ 
++  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClientSource)
++
+   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
+ 
+   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
+   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
+ 
+   // Traverse stuff from nsPIDOMWindow
+   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
+   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
+@@ -2409,17 +2411,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
+   tmp->UnlinkHostObjectURIs();
+ 
+   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
+ 
+   // Here the IdleRequest list would've been unlinked, but we rely on
+   // that IdleRequest objects have been traced and will remove
+   // themselves while unlinking.
+ 
+-  tmp->mClientSource.reset();
++  NS_IMPL_CYCLE_COLLECTION_UNLINK(mClientSource)
+ 
+   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPromises)
+ 
+   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+ 
+ #ifdef DEBUG
+ void
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -258,10 +258,26 @@ ClientSource::Info() const
+ }
+ 
+ nsISerialEventTarget*
+ ClientSource::EventTarget() const
+ {
+   return mEventTarget;
+ }
+ 
++void
++ClientSource::Traverse(nsCycleCollectionTraversalCallback& aCallback,
++                       const char* aName,
++                       uint32_t aFlags)
++{
++  if (mOwner.is<RefPtr<nsPIDOMWindowInner>>()) {
++    ImplCycleCollectionTraverse(aCallback,
++                                mOwner.as<RefPtr<nsPIDOMWindowInner>>(),
++                                aName, aFlags);
++  } else if (mOwner.is<nsCOMPtr<nsIDocShell>>()) {
++    ImplCycleCollectionTraverse(aCallback,
++                                mOwner.as<nsCOMPtr<nsIDocShell>>(),
++                                aName, aFlags);
++  }
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientSource.h b/dom/clients/manager/ClientSource.h
+--- a/dom/clients/manager/ClientSource.h
++++ b/dom/clients/manager/ClientSource.h
+@@ -88,14 +88,36 @@ public:
+   void
+   Thaw();
+ 
+   const ClientInfo&
+   Info() const;
+ 
+   nsISerialEventTarget*
+   EventTarget() const;
++
++  void
++  Traverse(nsCycleCollectionTraversalCallback& aCallback,
++           const char* aName,
++           uint32_t aFlags);
+ };
+ 
++inline void
++ImplCycleCollectionUnlink(UniquePtr<ClientSource>& aField)
++{
++  aField.reset();
++}
++
++inline void
++ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
++                            UniquePtr<ClientSource>& aField,
++                            const char* aName,
++                            uint32_t aFlags)
++{
++  if (aField) {
++    aField->Traverse(aCallback, aName, aFlags);
++  }
++}
++
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientSource_h

+ 703 - 0
mozilla-release/patches/1423328-59a1.patch

@@ -0,0 +1,703 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512503650 18000
+# Node ID c0c7f12df75a4cd3c15f904606a017af8bcb9ba7
+# Parent  70e780d3f9bd1791f08002d891c143fde7cc9740
+Bug 1423328 Allow a caller to set a ClientSource controlled by a service worker using a ClientHandle. r=baku
+
+diff --git a/dom/clients/manager/ClientHandle.cpp b/dom/clients/manager/ClientHandle.cpp
+--- a/dom/clients/manager/ClientHandle.cpp
++++ b/dom/clients/manager/ClientHandle.cpp
+@@ -5,16 +5,17 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ #include "ClientHandle.h"
+ 
+ #include "ClientHandleChild.h"
+ #include "ClientHandleOpChild.h"
+ #include "ClientManager.h"
+ #include "mozilla/dom/PClientManagerChild.h"
++#include "mozilla/dom/ServiceWorkerDescriptor.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ ClientHandle::~ClientHandle()
+ {
+   Shutdown();
+ }
+@@ -96,10 +97,30 @@ ClientHandle::ExecutionReady(const Clien
+ }
+ 
+ const ClientInfo&
+ ClientHandle::Info() const
+ {
+   return mClientInfo;
+ }
+ 
++RefPtr<GenericPromise>
++ClientHandle::Control(const ServiceWorkerDescriptor& aServiceWorker)
++{
++  RefPtr<GenericPromise::Private> outerPromise =
++    new GenericPromise::Private(__func__);
++
++  RefPtr<ClientOpPromise> innerPromise =
++    StartOp(ClientControlledArgs(aServiceWorker.ToIPC()));
++
++  innerPromise->Then(mSerialEventTarget, __func__,
++    [outerPromise](const ClientOpResult& aResult) {
++      outerPromise->Resolve(true, __func__);
++    },
++    [outerPromise](const ClientOpResult& aResult) {
++      outerPromise->Reject(aResult.get_nsresult(), __func__);
++    });
++
++  return outerPromise.forget();
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientHandle.h b/dom/clients/manager/ClientHandle.h
+--- a/dom/clients/manager/ClientHandle.h
++++ b/dom/clients/manager/ClientHandle.h
+@@ -4,29 +4,31 @@
+  * 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_ClientHandle_h
+ #define _mozilla_dom_ClientHandle_h
+ 
+ #include "mozilla/dom/ClientInfo.h"
+ #include "mozilla/dom/ClientOpPromise.h"
+ #include "mozilla/dom/ClientThing.h"
++#include "mozilla/MozPromise.h"
+ 
+ #ifdef XP_WIN
+ #undef PostMessage
+ #endif
+ 
+ namespace mozilla {
+ 
+ namespace dom {
+ 
+ class ClientManager;
+ class ClientHandleChild;
+ class ClientOpConstructorArgs;
+ class PClientManagerChild;
++class ServiceWorkerDescriptor;
+ 
+ // The ClientHandle allows code to take a simple ClientInfo struct and
+ // convert it into a live actor-backed object attached to a particular
+ // ClientSource somewhere in the browser.  If the ClientSource is
+ // destroyed then the ClientHandle will simply begin to reject operations.
+ // We do not currently provide a way to be notified when the ClientSource
+ // is destroyed, but this could be added in the future.
+ class ClientHandle final : public ClientThing<ClientHandleChild>
+@@ -57,15 +59,21 @@ class ClientHandle final : public Client
+ 
+   void
+   Activate(PClientManagerChild* aActor);
+ 
+ public:
+   const ClientInfo&
+   Info() const;
+ 
++  // Mark the ClientSource attached to this handle as controlled by the
++  // given service worker.  The promise will resolve true if the ClientSource
++  // is successfully marked or reject if the operation could not be completed.
++  RefPtr<GenericPromise>
++  Control(const ServiceWorkerDescriptor& aServiceWorker);
++
+   NS_INLINE_DECL_REFCOUNTING(ClientHandle);
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientHandle_h
+diff --git a/dom/clients/manager/ClientHandleOpParent.cpp b/dom/clients/manager/ClientHandleOpParent.cpp
+--- a/dom/clients/manager/ClientHandleOpParent.cpp
++++ b/dom/clients/manager/ClientHandleOpParent.cpp
+@@ -1,23 +1,54 @@
+ /* -*- 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 "ClientHandleOpParent.h"
+ 
++#include "ClientHandleParent.h"
++#include "ClientSourceParent.h"
++
+ namespace mozilla {
+ namespace dom {
+ 
++ClientSourceParent*
++ClientHandleOpParent::GetSource() const
++{
++  auto handle = static_cast<ClientHandleParent*>(Manager());
++  return handle->GetSource();
++}
++
+ void
+ ClientHandleOpParent::ActorDestroy(ActorDestroyReason aReason)
+ {
++  mPromiseRequestHolder.DisconnectIfExists();
+ }
+ 
+ void
+ ClientHandleOpParent::Init(const ClientOpConstructorArgs& aArgs)
+ {
++  ClientSourceParent* source = GetSource();
++  if (!source) {
++    Unused << PClientHandleOpParent::Send__delete__(this, NS_ERROR_DOM_ABORT_ERR);
++    return;
++  }
++
++  RefPtr<ClientOpPromise> p = source->StartOp(aArgs);
++
++  // Capturing 'this' is safe here because we disconnect the promise in
++  // ActorDestroy() which ensures neither lambda is called if the actor
++  // is destroyed before the source operation completes.
++  p->Then(GetCurrentThreadSerialEventTarget(), __func__,
++    [this] (const ClientOpResult& aResult) {
++      mPromiseRequestHolder.Complete();
++      Unused << PClientHandleOpParent::Send__delete__(this, aResult);
++    },
++    [this] (nsresult aRv) {
++      mPromiseRequestHolder.Complete();
++      Unused << PClientHandleOpParent::Send__delete__(this, aRv);
++    })->Track(mPromiseRequestHolder);
+ }
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientHandleOpParent.h b/dom/clients/manager/ClientHandleOpParent.h
+--- a/dom/clients/manager/ClientHandleOpParent.h
++++ b/dom/clients/manager/ClientHandleOpParent.h
+@@ -1,23 +1,31 @@
+ /* -*- 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 _mozilla_dom_ClientHandleOpParent_h
+ #define _mozilla_dom_ClientHandleOpParent_h
+ 
++#include "ClientOpPromise.h"
+ #include "mozilla/dom/PClientHandleOpParent.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
++class ClientSourceParent;
++
+ class ClientHandleOpParent final : public PClientHandleOpParent
+ {
++  MozPromiseRequestHolder<ClientOpPromise> mPromiseRequestHolder;
++
++  ClientSourceParent*
++  GetSource() const;
++
+   // PClientHandleOpParent interface
+   void
+   ActorDestroy(ActorDestroyReason aReason) override;
+ 
+ public:
+   ClientHandleOpParent() = default;
+   ~ClientHandleOpParent() = default;
+ 
+diff --git a/dom/clients/manager/ClientIPCTypes.ipdlh b/dom/clients/manager/ClientIPCTypes.ipdlh
+--- a/dom/clients/manager/ClientIPCTypes.ipdlh
++++ b/dom/clients/manager/ClientIPCTypes.ipdlh
+@@ -51,22 +51,28 @@ union IPCClientState
+ };
+ 
+ struct ClientSourceExecutionReadyArgs
+ {
+   nsCString url;
+   FrameType frameType;
+ };
+ 
++struct ClientControlledArgs
++{
++  IPCServiceWorkerDescriptor serviceWorker;
++};
++
+ struct ClientOpenWindowArgs
+ {
+ };
+ 
+-struct ClientOpConstructorArgs
++union ClientOpConstructorArgs
+ {
++  ClientControlledArgs;
+ };
+ 
+ struct ClientNavigateOpConstructorArgs
+ {
+ };
+ 
+ union ClientOpResult
+ {
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -252,16 +252,47 @@ ClientSource::Thaw()
+ }
+ 
+ const ClientInfo&
+ ClientSource::Info() const
+ {
+   return mClientInfo;
+ }
+ 
++void
++ClientSource::SetController(const ServiceWorkerDescriptor& aServiceWorker)
++{
++  NS_ASSERT_OWNINGTHREAD(ClientSource);
++
++  if (mController.isSome() && mController.ref() == aServiceWorker) {
++    return;
++  }
++
++  mController.reset();
++  mController.emplace(aServiceWorker);
++}
++
++RefPtr<ClientOpPromise>
++ClientSource::Control(const ClientControlledArgs& aArgs)
++{
++  NS_ASSERT_OWNINGTHREAD(ClientSource);
++
++  SetController(ServiceWorkerDescriptor(aArgs.serviceWorker()));
++
++  RefPtr<ClientOpPromise> ref =
++    ClientOpPromise::CreateAndResolve(NS_OK, __func__);
++  return ref.forget();
++}
++
++const Maybe<ServiceWorkerDescriptor>&
++ClientSource::GetController() const
++{
++  return mController;
++}
++
+ nsISerialEventTarget*
+ ClientSource::EventTarget() const
+ {
+   return mEventTarget;
+ }
+ 
+ void
+ ClientSource::Traverse(nsCycleCollectionTraversalCallback& aCallback,
+diff --git a/dom/clients/manager/ClientSource.h b/dom/clients/manager/ClientSource.h
+--- a/dom/clients/manager/ClientSource.h
++++ b/dom/clients/manager/ClientSource.h
+@@ -2,24 +2,29 @@
+ /* 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 _mozilla_dom_ClientSource_h
+ #define _mozilla_dom_ClientSource_h
+ 
+ #include "mozilla/dom/ClientInfo.h"
++#include "mozilla/dom/ClientOpPromise.h"
+ #include "mozilla/dom/ClientThing.h"
++#include "mozilla/dom/ServiceWorkerDescriptor.h"
++#include "mozilla/Variant.h"
+ 
+ class nsIDocShell;
++class nsISerialEventTarget;
+ class nsPIDOMWindowInner;
+ 
+ namespace mozilla {
+ namespace dom {
+ 
++class ClientControlledArgs;
+ class ClientManager;
+ class ClientSourceChild;
+ class ClientSourceConstructorArgs;
+ class ClientSourceExecutionReadyArgs;
+ class PClientManagerChild;
+ 
+ namespace workers {
+ class WorkerPrivate;
+@@ -41,16 +46,17 @@ class ClientSource final : public Client
+   nsCOMPtr<nsISerialEventTarget> mEventTarget;
+ 
+   Variant<Nothing,
+           RefPtr<nsPIDOMWindowInner>,
+           nsCOMPtr<nsIDocShell>,
+           mozilla::dom::workers::WorkerPrivate*> mOwner;
+ 
+   ClientInfo mClientInfo;
++  Maybe<ServiceWorkerDescriptor> mController;
+ 
+   void
+   Shutdown();
+ 
+   void
+   ExecutionReady(const ClientSourceExecutionReadyArgs& aArgs);
+ 
+   mozilla::dom::workers::WorkerPrivate*
+@@ -86,16 +92,37 @@ public:
+   Freeze();
+ 
+   void
+   Thaw();
+ 
+   const ClientInfo&
+   Info() const;
+ 
++  // Synchronously mark the ClientSource as controlled by the given service
++  // worker.  This can happen as a result of a remote operation or directly
++  // by local code.  For example, if a client's initial network load is
++  // intercepted by a controlling service worker then this should be called
++  // immediately.
++  //
++  // Note, there is no way to clear the controlling service worker because
++  // the specification does not allow that operation.
++  void
++  SetController(const ServiceWorkerDescriptor& aServiceWorker);
++
++  // Mark the ClientSource as controlled using the remote operation arguments.
++  // This will in turn call SetController().
++  RefPtr<ClientOpPromise>
++  Control(const ClientControlledArgs& aArgs);
++
++  // Get the ClientSource's current controlling service worker, if one has
++  // been set.
++  const Maybe<ServiceWorkerDescriptor>&
++  GetController() const;
++
+   nsISerialEventTarget*
+   EventTarget() const;
+ 
+   void
+   Traverse(nsCycleCollectionTraversalCallback& aCallback,
+            const char* aName,
+            uint32_t aFlags);
+ };
+diff --git a/dom/clients/manager/ClientSourceChild.cpp b/dom/clients/manager/ClientSourceChild.cpp
+--- a/dom/clients/manager/ClientSourceChild.cpp
++++ b/dom/clients/manager/ClientSourceChild.cpp
+@@ -1,18 +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 "ClientSourceChild.h"
+ 
++#include "ClientSource.h"
+ #include "ClientSourceOpChild.h"
+-#include "ClientThing.h"
+ #include "mozilla/dom/ClientIPCTypes.h"
+ #include "mozilla/Unused.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ using mozilla::ipc::IPCResult;
+ 
+@@ -56,27 +56,33 @@ ClientSourceChild::ClientSourceChild(con
+ {
+ }
+ 
+ void
+ ClientSourceChild::SetOwner(ClientThing<ClientSourceChild>* aThing)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(aThing);
+   MOZ_DIAGNOSTIC_ASSERT(!mSource);
+-  mSource = aThing;
++  mSource = static_cast<ClientSource*>(aThing);
+ }
+ 
+ void
+ ClientSourceChild::RevokeOwner(ClientThing<ClientSourceChild>* aThing)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(mSource);
+-  MOZ_DIAGNOSTIC_ASSERT(mSource == aThing);
++  MOZ_DIAGNOSTIC_ASSERT(mSource == static_cast<ClientSource*>(aThing));
+   mSource = nullptr;
+ }
+ 
++ClientSource*
++ClientSourceChild::GetSource() const
++{
++  return mSource;
++}
++
+ void
+ ClientSourceChild::MaybeStartTeardown()
+ {
+   if (mTeardownStarted) {
+     return;
+   }
+   mTeardownStarted = true;
+   Unused << SendTeardown();
+diff --git a/dom/clients/manager/ClientSourceChild.h b/dom/clients/manager/ClientSourceChild.h
+--- a/dom/clients/manager/ClientSourceChild.h
++++ b/dom/clients/manager/ClientSourceChild.h
+@@ -6,22 +6,23 @@
+ #ifndef _mozilla_dom_ClientSourceChild_h
+ #define _mozilla_dom_ClientSourceChild_h
+ 
+ #include "mozilla/dom/PClientSourceChild.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
++class ClientSource;
+ class ClientSourceConstructorArgs;
+ template <typename ActorType> class ClientThing;
+ 
+ class ClientSourceChild final : public PClientSourceChild
+ {
+-  ClientThing<ClientSourceChild>* mSource;
++  ClientSource* mSource;
+   bool mTeardownStarted;
+ 
+   // PClientSourceChild interface
+   void
+   ActorDestroy(ActorDestroyReason aReason) override;
+ 
+   PClientSourceOpChild*
+   AllocPClientSourceOpChild(const ClientOpConstructorArgs& aArgs) override;
+@@ -37,16 +38,19 @@ public:
+   explicit ClientSourceChild(const ClientSourceConstructorArgs& aArgs);
+ 
+   void
+   SetOwner(ClientThing<ClientSourceChild>* aThing);
+ 
+   void
+   RevokeOwner(ClientThing<ClientSourceChild>* aThing);
+ 
++  ClientSource*
++  GetSource() const;
++
+   void
+   MaybeStartTeardown();
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientSourceChild_h
+diff --git a/dom/clients/manager/ClientSourceOpChild.cpp b/dom/clients/manager/ClientSourceOpChild.cpp
+--- a/dom/clients/manager/ClientSourceOpChild.cpp
++++ b/dom/clients/manager/ClientSourceOpChild.cpp
+@@ -1,23 +1,91 @@
+ /* -*- 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 "ClientSourceOpChild.h"
+ 
++#include "ClientSource.h"
++#include "ClientSourceChild.h"
++#include "mozilla/Unused.h"
++
+ namespace mozilla {
+ namespace dom {
+ 
++ClientSource*
++ClientSourceOpChild::GetSource() const
++{
++  auto actor = static_cast<ClientSourceChild*>(Manager());
++  return actor->GetSource();
++}
++
++template<typename Method, typename Args>
++void
++ClientSourceOpChild::DoSourceOp(Method aMethod, const Args& aArgs)
++{
++  RefPtr<ClientOpPromise> promise;
++  nsCOMPtr<nsISerialEventTarget> target;
++
++  // Some ClientSource operations can cause the ClientSource to be destroyed.
++  // This means we should reference the ClientSource pointer for the minimum
++  // possible to start the operation.  Use an extra block scope here to help
++  // enforce this and prevent accidental usage later in the method.
++  {
++    ClientSource* source = GetSource();
++    if (!source) {
++      Unused << PClientSourceOpChild::Send__delete__(this, NS_ERROR_DOM_ABORT_ERR);
++      return;
++    }
++
++    target = source->EventTarget();
++
++    // This may cause the ClientSource object to be destroyed.  Do not
++    // use the source variable after this call.
++    promise = (source->*aMethod)(aArgs);
++  }
++
++  // The ClientSource methods are required to always return a promise.  If
++  // they encounter an error they should just immediately resolve or reject
++  // the promise as appropriate.
++  MOZ_DIAGNOSTIC_ASSERT(promise);
++
++  // Capture 'this' is safe here because we disconnect the promise
++  // ActorDestroy() which ensures nethier lambda is called if the
++  // actor is destroyed before the source operation completes.
++  promise->Then(target, __func__,
++    [this, aArgs] (const mozilla::dom::ClientOpResult& aResult) {
++      mPromiseRequestHolder.Complete();
++      Unused << PClientSourceOpChild::Send__delete__(this, aResult);
++    },
++    [this] (nsresult aRv) {
++      mPromiseRequestHolder.Complete();
++      Unused << PClientSourceOpChild::Send__delete__(this, aRv);
++    })->Track(mPromiseRequestHolder);
++}
++
+ void
+ ClientSourceOpChild::ActorDestroy(ActorDestroyReason aReason)
+ {
++  mPromiseRequestHolder.DisconnectIfExists();
+ }
+ 
+ void
+ ClientSourceOpChild::Init(const ClientOpConstructorArgs& aArgs)
+ {
++  switch (aArgs.type()) {
++    case ClientOpConstructorArgs::TClientControlledArgs:
++    {
++      DoSourceOp(&ClientSource::Control, aArgs.get_ClientControlledArgs());
++      break;
++    }
++    default:
++    {
++      MOZ_ASSERT_UNREACHABLE("unknown client operation!");
++      break;
++    }
++  }
+ }
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientSourceOpChild.h b/dom/clients/manager/ClientSourceOpChild.h
+--- a/dom/clients/manager/ClientSourceOpChild.h
++++ b/dom/clients/manager/ClientSourceOpChild.h
+@@ -7,18 +7,29 @@
+ #define _mozilla_dom_ClientSourceOpChild_h
+ 
+ #include "mozilla/dom/PClientSourceOpChild.h"
+ #include "ClientOpPromise.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
++class ClientSource;
++
+ class ClientSourceOpChild final : public PClientSourceOpChild
+ {
++  MozPromiseRequestHolder<ClientOpPromise> mPromiseRequestHolder;
++
++  ClientSource*
++  GetSource() const;
++
++  template <typename Method, typename Args>
++  void
++  DoSourceOp(Method aMethod, const Args& aArgs);
++
+   // PClientSourceOpChild interface
+   void
+   ActorDestroy(ActorDestroyReason aReason) override;
+ 
+ public:
+   ClientSourceOpChild() = default;
+   ~ClientSourceOpChild() = default;
+ 
+diff --git a/dom/clients/manager/ClientSourceParent.cpp b/dom/clients/manager/ClientSourceParent.cpp
+--- a/dom/clients/manager/ClientSourceParent.cpp
++++ b/dom/clients/manager/ClientSourceParent.cpp
+@@ -221,10 +221,30 @@ ClientSourceParent::AttachHandle(ClientH
+ void
+ ClientSourceParent::DetachHandle(ClientHandleParent* aClientHandle)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(aClientHandle);
+   MOZ_ASSERT(mHandleList.Contains(aClientHandle));
+   mHandleList.RemoveElement(aClientHandle);
+ }
+ 
++RefPtr<ClientOpPromise>
++ClientSourceParent::StartOp(const ClientOpConstructorArgs& aArgs)
++{
++  RefPtr<ClientOpPromise::Private> promise =
++    new ClientOpPromise::Private(__func__);
++
++  // If we are being controlled, remember that data before propagating
++  // on to the ClientSource.
++  if (aArgs.type() == ClientOpConstructorArgs::TClientControlledArgs) {
++    mController.reset();
++    mController.emplace(aArgs.get_ClientControlledArgs().serviceWorker());
++  }
++
++  // Constructor failure will reject the promise via ActorDestroy().
++  ClientSourceOpParent* actor = new ClientSourceOpParent(aArgs, promise);
++  Unused << SendPClientSourceOpConstructor(actor, aArgs);
++
++  return promise.forget();
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientSourceParent.h b/dom/clients/manager/ClientSourceParent.h
+--- a/dom/clients/manager/ClientSourceParent.h
++++ b/dom/clients/manager/ClientSourceParent.h
+@@ -2,27 +2,30 @@
+ /* 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 _mozilla_dom_ClientSourceParent_h
+ #define _mozilla_dom_ClientSourceParent_h
+ 
+ #include "ClientInfo.h"
++#include "ClientOpPromise.h"
+ #include "mozilla/dom/PClientSourceParent.h"
++#include "mozilla/dom/ServiceWorkerDescriptor.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ class ClientHandleParent;
+ class ClientManagerService;
+ 
+ class ClientSourceParent final : public PClientSourceParent
+ {
+   ClientInfo mClientInfo;
++  Maybe<ServiceWorkerDescriptor> mController;
+   RefPtr<ClientManagerService> mService;
+   nsTArray<ClientHandleParent*> mHandleList;
+   bool mExecutionReady;
+   bool mFrozen;
+ 
+   void
+   KillInvalidChild();
+ 
+@@ -61,14 +64,17 @@ public:
+   bool
+   IsFrozen() const;
+ 
+   void
+   AttachHandle(ClientHandleParent* aClientSource);
+ 
+   void
+   DetachHandle(ClientHandleParent* aClientSource);
++
++  RefPtr<ClientOpPromise>
++  StartOp(const ClientOpConstructorArgs& aArgs);
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientSourceParent_h

+ 729 - 0
mozilla-release/patches/1423412-1-59a1.patch

@@ -0,0 +1,729 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512524722 18000
+# Node ID 699fd0f2b04666c7b043f0350cf56f0bc07189ac
+# Parent  b87e12c54cdea798e6705b885650bfcb5c542d33
+Bug 1423412 P1 Actually mark window/worker ClientSource objects controlled when loaded with a controlling service worker. r=baku r=jld
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -3429,16 +3429,42 @@ nsDocShell::MaybeCreateInitialClientSour
+   if (NS_WARN_IF(!mInitialClientSource)) {
+     return;
+   }
+ 
+   // Mark the initial client as execution ready, but owned by the docshell.
+   // If the client is actually used this will cause ClientSource to force
+   // the creation of the initial about:blank by calling nsDocShell::GetDocument().
+   mInitialClientSource->DocShellExecutionReady(this);
++
++  // Next, check to see if the parent is controlled.
++  nsCOMPtr<nsIDocShell> parent = GetParentDocshell();
++  nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
++  nsPIDOMWindowInner* parentInner =
++    parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
++  if (!parentInner) {
++    return;
++  }
++
++  Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
++  if (controller.isNothing()) {
++    return;
++  }
++
++  // If the parent is controlled then propagate that controller to the
++  // initial about:blank client as well.  This will set the controller
++  // in the ClientManagerService in the parent.
++  RefPtr<ClientHandle> handle =
++    ClientManager::CreateHandle(mInitialClientSource->Info(),
++                                parentInner->EventTargetFor(TaskCategory::Other));
++  handle->Control(controller.ref());
++
++  // Also mark the ClientSource as controlled directly in case script
++  // immediately accesses navigator.serviceWorker.controller.
++  mInitialClientSource->SetController(controller.ref());
+ }
+ 
+ Maybe<ClientInfo>
+ nsDocShell::GetInitialClientInfo() const
+ {
+   if (mInitialClientSource) {
+     Maybe<ClientInfo> result;
+     result.emplace(mInitialClientSource->Info());
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -3486,26 +3486,44 @@ nsGlobalWindow::EnsureClientSource()
+     }
+   }
+  
+   // If we don't have a reserved client or an initial client, then create
+   // one now.  This can happen in certain cases where we avoid preallocating
+   // the client in the docshell.  This mainly occurs in situations where
+   // the principal is not clearly inherited from the parent; e.g. sandboxed
+   // iframes, window.open(), etc.
++  // TODO: We may not be marking initial about:blank documents created
++  //       this way as controlled by a service worker properly.  The
++  //       controller should be coming from the same place as the inheritted
++  //       principal.  We do this in docshell, but as mentioned we aren't
++  //       smart enough to handle all cases yet.  For example, a
++  //       window.open() with new URL should inherit the controller from
++  //       the opener, but we probably don't handle that yet.
+   if (!mClientSource) {
+     mClientSource = ClientManager::CreateSource(ClientType::Window,
+                                                 EventTargetFor(TaskCategory::Other),
+                                                 mDoc->NodePrincipal());
+     if (NS_WARN_IF(!mClientSource)) {
+       return NS_ERROR_FAILURE;
+     }
+     newClientSource = true;
+   }
+ 
++  // The load may have started controlling the Client as well.  If
++  // so, mark it as controlled immediately here.  The actor may
++  // or may not have been notified by the parent side about being
++  // controlled yet.
++  if (loadInfo) {
++    const Maybe<ServiceWorkerDescriptor> controller = loadInfo->GetController();
++    if (controller.isSome()) {
++      mClientSource->SetController(controller.ref());
++    }
++  }
++
+   // Its possible that we got a client just after being frozen in
+   // the bfcache.  In that case freeze the client immediately.
+   if (newClientSource && IsFrozen()) {
+     mClientSource->Freeze();
+   }
+ 
+   return NS_OK;
+ }
+@@ -4386,16 +4404,22 @@ nsPIDOMWindowInner::SyncStateFromParentW
+ }
+ 
+ Maybe<ClientInfo>
+ nsPIDOMWindowInner::GetClientInfo() const
+ {
+   return Move(nsGlobalWindow::Cast(this)->GetClientInfo());
+ }
+ 
++Maybe<ServiceWorkerDescriptor>
++nsPIDOMWindowInner::GetController() const
++{
++  return Move(nsGlobalWindow::Cast(this)->GetController());
++}
++
+ void
+ nsGlobalWindow::UpdateTopInnerWindow()
+ {
+   if (!IsInnerWindow() || AsInner()->IsTopInnerWindow()) {
+     return;
+   }
+ 
+   AsInner()->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
+@@ -12579,16 +12603,27 @@ nsGlobalWindow::GetClientInfo() const
+   MOZ_ASSERT(NS_IsMainThread());
+   Maybe<ClientInfo> clientInfo;
+   if (mClientSource) {
+     clientInfo.emplace(mClientSource->Info());
+   }
+   return Move(clientInfo);
+ }
+ 
++Maybe<ServiceWorkerDescriptor>
++nsGlobalWindow::GetController() const
++{
++  MOZ_ASSERT(NS_IsMainThread());
++  Maybe<ServiceWorkerDescriptor> controller;
++  if (mClientSource) {
++    controller = mClientSource->GetController();
++  }
++  return Move(controller);
++}
++
+ nsresult
+ nsGlobalWindow::FireDelayedDOMEvents()
+ {
+   FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED);
+ 
+   if (mApplicationCache) {
+     static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
+   }
+diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h
+--- a/dom/base/nsGlobalWindow.h
++++ b/dom/base/nsGlobalWindow.h
+@@ -399,16 +399,17 @@ public:
+   void Resume();
+   virtual bool IsSuspended() const override;
+   void Freeze();
+   void Thaw();
+   virtual bool IsFrozen() const override;
+   void SyncStateFromParentWindow();
+ 
+   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
++  mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
+ 
+   virtual nsresult FireDelayedDOMEvents() override;
+ 
+   // Outer windows only.
+   virtual bool WouldReuseInnerWindow(nsIDocument* aNewDocument) override;
+ 
+   virtual void SetDocShell(nsIDocShell* aDocShell) override;
+   virtual void DetachFromDocShell() override;
+diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h
+--- a/dom/base/nsPIDOMWindow.h
++++ b/dom/base/nsPIDOMWindow.h
+@@ -45,16 +45,17 @@ namespace mozilla {
+ class ThrottledEventQueue;
+ namespace dom {
+ class AudioContext;
+ class ClientInfo;
+ class DocGroup;
+ class TabGroup;
+ class Element;
+ class Performance;
++class ServiceWorkerDescriptor;
+ class ServiceWorkerRegistration;
+ class Timeout;
+ class TimeoutManager;
+ class CustomElementRegistry;
+ enum class CallerType : uint32_t;
+ } // namespace dom
+ } // namespace mozilla
+ 
+@@ -932,16 +933,17 @@ public:
+   // Increase/Decrease the number of open WebSockets.
+   void UpdateWebSocketCount(int32_t aDelta);
+ 
+   // Return true if there are any open WebSockets that could block
+   // timeout-throttling.
+   bool HasOpenWebSockets() const;
+ 
+   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
++  mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
+ 
+ protected:
+   void CreatePerformanceObjectIfNeeded();
+ };
+ 
+ NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
+ 
+ // NB: It's very very important that these two classes have identical vtables
+diff --git a/dom/clients/manager/ClientManager.h b/dom/clients/manager/ClientManager.h
+--- a/dom/clients/manager/ClientManager.h
++++ b/dom/clients/manager/ClientManager.h
+@@ -32,16 +32,17 @@ class WorkerPrivate;
+ // The ClientManager provides a per-thread singleton interface workering
+ // with the client subsystem.  It allows globals to create ClientSource
+ // objects.  It allows other parts of the system to attach to this globals
+ // by creating ClientHandle objects.  The ClientManager also provides
+ // methods for querying the list of clients active in the system.
+ class ClientManager final : public ClientThing<ClientManagerChild>
+ {
+   friend class ClientManagerChild;
++  friend class ClientSource;
+ 
+   ClientManager();
+   ~ClientManager();
+ 
+   // Utility method to trigger a shutdown of the ClientManager.  This
+   // is called in various error conditions or when the last reference
+   // is dropped.
+   void
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -253,16 +253,27 @@ ClientSource::Thaw()
+ 
+ const ClientInfo&
+ ClientSource::Info() const
+ {
+   return mClientInfo;
+ }
+ 
+ void
++ClientSource::WorkerSyncPing(WorkerPrivate* aWorkerPrivate)
++{
++  NS_ASSERT_OWNINGTHREAD(ClientSource);
++  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
++  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate == mManager->GetWorkerPrivate());
++  aWorkerPrivate->AssertIsOnWorkerThread();
++  MOZ_DIAGNOSTIC_ASSERT(GetActor());
++  GetActor()->SendWorkerSyncPing();
++}
++
++void
+ ClientSource::SetController(const ServiceWorkerDescriptor& aServiceWorker)
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientSource);
+ 
+   if (mController.isSome() && mController.ref() == aServiceWorker) {
+     return;
+   }
+ 
+diff --git a/dom/clients/manager/ClientSource.h b/dom/clients/manager/ClientSource.h
+--- a/dom/clients/manager/ClientSource.h
++++ b/dom/clients/manager/ClientSource.h
+@@ -92,16 +92,23 @@ public:
+   Freeze();
+ 
+   void
+   Thaw();
+ 
+   const ClientInfo&
+   Info() const;
+ 
++  // Trigger a synchronous IPC ping to the parent process to confirm that
++  // the ClientSource actor has been created.  This should only be used
++  // by the WorkerPrivate startup code to deal with a ClientHandle::Control()
++  // call racing on the main thread.  Do not call this in other circumstances!
++  void
++  WorkerSyncPing(mozilla::dom::workers::WorkerPrivate* aWorkerPrivate);
++
+   // Synchronously mark the ClientSource as controlled by the given service
+   // worker.  This can happen as a result of a remote operation or directly
+   // by local code.  For example, if a client's initial network load is
+   // intercepted by a controlling service worker then this should be called
+   // immediately.
+   //
+   // Note, there is no way to clear the controlling service worker because
+   // the specification does not allow that operation.
+diff --git a/dom/clients/manager/ClientSourceParent.cpp b/dom/clients/manager/ClientSourceParent.cpp
+--- a/dom/clients/manager/ClientSourceParent.cpp
++++ b/dom/clients/manager/ClientSourceParent.cpp
+@@ -15,16 +15,17 @@
+ #include "mozilla/dom/PClientManagerParent.h"
+ #include "mozilla/ipc/BackgroundParent.h"
+ #include "mozilla/SystemGroup.h"
+ #include "mozilla/Unused.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
++using mozilla::ipc::AssertIsOnBackgroundThread;
+ using mozilla::ipc::BackgroundParent;
+ using mozilla::ipc::IPCResult;
+ using mozilla::ipc::PrincipalInfo;
+ 
+ namespace {
+ 
+ // It would be nice to use a lambda instead of this class, but we cannot
+ // move capture in lambdas yet and ContentParent cannot be AddRef'd off
+@@ -77,16 +78,25 @@ ClientSourceParent::KillInvalidChild()
+   // typically mean someone sent us bogus data over the IPC link.  We can't
+   // trust that process any more.  We have to do this on the main thread, so
+   // there is a small window of time before we kill the process.  This is why
+   // we start the actor destruction immediately above.
+   nsCOMPtr<nsIRunnable> r = new KillContentParentRunnable(Move(process));
+   MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
+ }
+ 
++mozilla::ipc::IPCResult
++ClientSourceParent::RecvWorkerSyncPing()
++{
++  AssertIsOnBackgroundThread();
++  // Do nothing here.  This is purely a sync message allowing the child to
++  // confirm that the actor has been created on the parent process.
++  return IPC_OK();
++}
++
+ IPCResult
+ ClientSourceParent::RecvTeardown()
+ {
+   Unused << Send__delete__(this);
+   return IPC_OK();
+ }
+ 
+ IPCResult
+diff --git a/dom/clients/manager/ClientSourceParent.h b/dom/clients/manager/ClientSourceParent.h
+--- a/dom/clients/manager/ClientSourceParent.h
++++ b/dom/clients/manager/ClientSourceParent.h
+@@ -26,16 +26,19 @@ class ClientSourceParent final : public 
+   bool mExecutionReady;
+   bool mFrozen;
+ 
+   void
+   KillInvalidChild();
+ 
+   // PClientSourceParent
+   mozilla::ipc::IPCResult
++  RecvWorkerSyncPing() override;
++
++  mozilla::ipc::IPCResult
+   RecvTeardown() override;
+ 
+   mozilla::ipc::IPCResult
+   RecvExecutionReady(const ClientSourceExecutionReadyArgs& aArgs) override;
+ 
+   mozilla::ipc::IPCResult
+   RecvFreeze() override;
+ 
+diff --git a/dom/clients/manager/PClientSource.ipdl b/dom/clients/manager/PClientSource.ipdl
+--- a/dom/clients/manager/PClientSource.ipdl
++++ b/dom/clients/manager/PClientSource.ipdl
+@@ -15,16 +15,17 @@ namespace dom {
+ 
+ sync protocol PClientSource
+ {
+   manager PClientManager;
+ 
+   manages PClientSourceOp;
+ 
+ parent:
++  sync WorkerSyncPing();
+   async Teardown();
+   async ExecutionReady(ClientSourceExecutionReadyArgs aArgs);
+   async Freeze();
+   async Thaw();
+ 
+ child:
+   async PClientSourceOp(ClientOpConstructorArgs aArgs);
+ 
+diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp
+--- a/dom/workers/ScriptLoader.cpp
++++ b/dom/workers/ScriptLoader.cpp
+@@ -565,16 +565,17 @@ class ScriptLoaderRunnable final : publi
+   friend class CachePromiseHandler;
+   friend class CacheScriptLoader;
+   friend class LoaderListener;
+ 
+   WorkerPrivate* mWorkerPrivate;
+   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
+   nsTArray<ScriptLoadInfo> mLoadInfos;
+   RefPtr<CacheCreator> mCacheCreator;
++  Maybe<ServiceWorkerDescriptor> mController;
+   bool mIsMainScript;
+   WorkerScriptType mWorkerScriptType;
+   bool mCanceled;
+   bool mCanceledMainThread;
+   ErrorResult& mRv;
+ 
+ public:
+   NS_DECL_THREADSAFE_ISUPPORTS
+@@ -1213,16 +1214,20 @@ private:
+ 
+       mWorkerPrivate->SetReferrerPolicyFromHeaderValue(tRPHeaderCValue);
+ 
+       WorkerPrivate* parent = mWorkerPrivate->GetParent();
+       if (parent) {
+         // XHR Params Allowed
+         mWorkerPrivate->SetXHRParamsAllowed(parent->XHRParamsAllowed());
+       }
++
++      if (chanLoadInfo) {
++        mController = chanLoadInfo->GetController();
++      }
+     }
+ 
+     return NS_OK;
+   }
+ 
+   void
+   DataReceivedFromCache(uint32_t aIndex, const uint8_t* aString,
+                         uint32_t aStringLen,
+@@ -1979,18 +1984,21 @@ ScriptExecutorRunnable::WorkerRun(JSCont
+       // Top level scripts only!
+       if (mIsWorkerScript) {
+         aWorkerPrivate->MaybeDispatchLoadFailedRunnable();
+       }
+       return true;
+     }
+ 
+     // If this is a top level script that succeeded, then mark the
+-    // Client execution ready.
++    // Client execution ready and possible controlled by a service worker.
+     if (mIsWorkerScript) {
++      if (mScriptLoader.mController.isSome()) {
++        aWorkerPrivate->Control(mScriptLoader.mController.ref());
++      }
+       aWorkerPrivate->ExecutionReady();
+     }
+ 
+     NS_ConvertUTF16toUTF8 filename(loadInfo.mURL);
+ 
+     JS::CompileOptions options(aCx);
+     options.setFileAndLine(filename.get(), 1)
+            .setNoScriptRval(true);
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -32,16 +32,18 @@
+ 
+ #include "mozilla/BasePrincipal.h"
+ #include "mozilla/ClearOnShutdown.h"
+ #include "mozilla/ErrorNames.h"
+ #include "mozilla/LoadContext.h"
+ #include "mozilla/SystemGroup.h"
+ #include "mozilla/Telemetry.h"
+ #include "mozilla/dom/BindingUtils.h"
++#include "mozilla/dom/ClientHandle.h"
++#include "mozilla/dom/ClientManager.h"
+ #include "mozilla/dom/ContentParent.h"
+ #include "mozilla/dom/ErrorEvent.h"
+ #include "mozilla/dom/Headers.h"
+ #include "mozilla/dom/InternalHeaders.h"
+ #include "mozilla/dom/Navigator.h"
+ #include "mozilla/dom/NotificationEvent.h"
+ #include "mozilla/dom/PromiseNativeHandler.h"
+ #include "mozilla/dom/PromiseWindowProxy.h"
+@@ -2327,35 +2329,57 @@ ServiceWorkerManager::MaybeCheckNavigati
+   //    algorithm.
+   RefPtr<ServiceWorkerRegistrationInfo> registration;
+   mControlledDocuments.Get(aDoc, getter_AddRefs(registration));
+   if (registration) {
+     registration->MaybeScheduleUpdate();
+   }
+ }
+ 
+-void
++RefPtr<GenericPromise>
+ ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
+                                                 nsIDocument* aDoc,
+                                                 const nsAString& aDocumentId)
+ {
+   MOZ_ASSERT(aRegistration);
+   MOZ_ASSERT(aDoc);
+ 
+ #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+   auto storageAllowed = nsContentUtils::StorageAllowedForDocument(aDoc);
+   MOZ_DIAGNOSTIC_ASSERT(storageAllowed == nsContentUtils::StorageAccess::eAllow);
+ #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ 
++  RefPtr<GenericPromise> ref = GenericPromise::CreateAndResolve(true, __func__);
++
+   aRegistration->StartControllingADocument();
+   mControlledDocuments.Put(aDoc, aRegistration);
+   if (!aDocumentId.IsEmpty()) {
+     aDoc->SetId(aDocumentId);
+   }
++
++  // Mark the document's ClientSource as controlled using the ClientHandle
++  // interface.  While we could get at the ClientSource directly from the
++  // document here, our goal is to move ServiceWorkerManager to a separate
++  // process.  Using the ClientHandle supports this remote operation.
++  ServiceWorkerInfo* activeWorker = aRegistration->GetActive();
++  nsPIDOMWindowInner* innerWindow = aDoc->GetInnerWindow();
++  if (activeWorker && innerWindow) {
++    Maybe<ClientInfo> clientInfo = innerWindow->GetClientInfo();
++    if (clientInfo.isSome()) {
++      RefPtr<ClientHandle> clientHandle =
++        ClientManager::CreateHandle(clientInfo.ref(),
++                                    SystemGroup::EventTargetFor(TaskCategory::Other));
++      if (clientHandle) {
++        ref = Move(clientHandle->Control(activeWorker->Descriptor()));
++      }
++    }
++  }
++
+   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
++  return Move(ref);
+ }
+ 
+ void
+ ServiceWorkerManager::StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration)
+ {
+   aRegistration->StopControllingADocument();
+   if (aRegistration->IsControllingDocuments() || !aRegistration->IsIdle()) {
+     return;
+@@ -2656,16 +2680,58 @@ ServiceWorkerManager::DispatchFetchEvent
+     // before we get to this point.  Therefore we must handle a nullptr
+     // active worker here.
+     serviceWorker = registration->GetActive();
+     if (!serviceWorker) {
+       aRv.Throw(NS_ERROR_FAILURE);
+       return;
+     }
+ 
++    // If there is a reserved client it should be marked as controlled before
++    // the FetchEvent is dispatched.
++    nsCOMPtr<nsILoadInfo> loadInfo = internalChannel->GetLoadInfo();
++    if (loadInfo) {
++      Maybe<ClientInfo> clientInfo = loadInfo->GetReservedClientInfo();
++
++      // Also override the initial about:blank controller since the real
++      // network load may be intercepted by a different service worker.  If
++      // the intial about:blank has a controller here its simply been
++      // inherited from its parent.
++      if (clientInfo.isNothing()) {
++        clientInfo = loadInfo->GetInitialClientInfo();
++
++        // TODO: We need to handle the case where the initial about:blank is
++        //       controlled, but the final document load is not.  Right now
++        //       the spec does not really say what to do.  There currently
++        //       is no way for the controller to be cleared from a client in
++        //       the spec or our implementation.  We may want to force a
++        //       new inner window to be created instead of reusing the
++        //       initial about:blank global.  See bug 1419620 and the spec
++        //       issue here: https://github.com/w3c/ServiceWorker/issues/1232
++      }
++
++      if (clientInfo.isSome()) {
++        // First, attempt to mark the reserved client controlled directly.  This
++        // will update the controlled status in the ClientManagerService in the
++        // parent.  It will also eventually propagate back to the ClientSource.
++        RefPtr<ClientHandle> clientHandle =
++          ClientManager::CreateHandle(clientInfo.ref(),
++                                      SystemGroup::EventTargetFor(TaskCategory::Other));
++        if (clientHandle) {
++          clientHandle->Control(serviceWorker->Descriptor());
++        }
++      }
++
++      // But we also note the reserved state on the LoadInfo.  This allows the
++      // ClientSource to be updated immediately after the nsIChannel starts.
++      // This is necessary to have the correct controller in place for immediate
++      // follow-on requests.
++      loadInfo->SetController(serviceWorker->Descriptor());
++    }
++
+     AddNavigationInterception(serviceWorker->Scope(), aChannel);
+   }
+ 
+   if (NS_WARN_IF(aRv.Failed())) {
+     return;
+   }
+ 
+   MOZ_DIAGNOSTIC_ASSERT(serviceWorker);
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -10,16 +10,17 @@
+ #include "nsIServiceWorkerManager.h"
+ #include "nsCOMPtr.h"
+ 
+ #include "ipc/IPCMessageUtils.h"
+ #include "mozilla/Attributes.h"
+ #include "mozilla/AutoRestore.h"
+ #include "mozilla/ConsoleReportCollector.h"
+ #include "mozilla/LinkedList.h"
++#include "mozilla/MozPromise.h"
+ #include "mozilla/Preferences.h"
+ #include "mozilla/TypedEnumBits.h"
+ #include "mozilla/UniquePtr.h"
+ #include "mozilla/WeakPtr.h"
+ #include "mozilla/dom/BindingUtils.h"
+ #include "mozilla/dom/Promise.h"
+ #include "mozilla/dom/ServiceWorkerCommon.h"
+ #include "mozilla/dom/ServiceWorkerRegistrar.h"
+@@ -385,17 +386,17 @@ private:
+                                             WhichServiceWorker aWhichOne);
+   void
+   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
+                                             WhichServiceWorker aWhichOnes);
+ 
+   void
+   NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+-  void
++  RefPtr<GenericPromise>
+   StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
+                             nsIDocument* aDoc,
+                             const nsAString& aDocumentId);
+ 
+   void
+   StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+   already_AddRefed<ServiceWorkerRegistrationInfo>
+diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
+--- a/dom/workers/WorkerPrivate.cpp
++++ b/dom/workers/WorkerPrivate.cpp
+@@ -5330,28 +5330,61 @@ WorkerPrivate::EnsureClientSource()
+   if (!mClientSource) {
+     return false;
+   }
+ 
+   if (mFrozen) {
+     mClientSource->Freeze();
+   }
+ 
++  // Shortly after the client is reserved we will try loading the main script
++  // for the worker.  This may get intercepted by the ServiceWorkerManager
++  // which will then try to create a ClientHandle.  Its actually possible for
++  // the main thread to create this ClientHandle before our IPC message creating
++  // the ClientSource completes.  To avoid this race we synchronously ping our
++  // parent Client actor here.  This ensure the worker ClientSource is created
++  // in the parent before the main thread might try reaching it with a
++  // ClientHandle.
++  //
++  // An alternative solution would have been to handle the out-of-order operations
++  // on the parent side.  We could have created a small window where we allow
++  // ClientHandle objects to exist without a ClientSource.  We would then time
++  // out these handles if they stayed orphaned for too long.  This approach would
++  // be much more complex, but also avoid this extra bit of latency when starting
++  // workers.
++  //
++  // Note, we only have to do this for workers that can be controlled by a
++  // service worker.  So avoid the sync overhead here if we are starting a
++  // service worker or a chrome worker.
++  if (Type() != WorkerTypeService && !IsChromeWorker()) {
++    mClientSource->WorkerSyncPing(this);
++  }
++
+   return true;
+ }
+ 
+ const ClientInfo&
+ WorkerPrivate::GetClientInfo() const
+ {
+   AssertIsOnWorkerThread();
+   MOZ_DIAGNOSTIC_ASSERT(mClientSource);
+   return mClientSource->Info();
+ }
+ 
+ void
++WorkerPrivate::Control(const ServiceWorkerDescriptor& aServiceWorker)
++{
++  AssertIsOnWorkerThread();
++  MOZ_DIAGNOSTIC_ASSERT(mClientSource);
++  MOZ_DIAGNOSTIC_ASSERT(!IsChromeWorker());
++  MOZ_DIAGNOSTIC_ASSERT(Type() != WorkerTypeService);
++  mClientSource->SetController(aServiceWorker);
++}
++
++void
+ WorkerPrivate::ExecutionReady()
+ {
+   AssertIsOnWorkerThread();
+   MOZ_DIAGNOSTIC_ASSERT(mClientSource);
+   mClientSource->WorkerExecutionReady(this);
+ }
+ 
+ void
+diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
+--- a/dom/workers/WorkerPrivate.h
++++ b/dom/workers/WorkerPrivate.h
+@@ -1499,16 +1499,19 @@ public:
+ 
+   bool
+   EnsureClientSource();
+ 
+   const ClientInfo&
+   GetClientInfo() const;
+ 
+   void
++  Control(const ServiceWorkerDescriptor& aServiceWorker);
++
++  void
+   ExecutionReady();
+ 
+ private:
+   WorkerPrivate(WorkerPrivate* aParent,
+                 const nsAString& aScriptURL, bool aIsChromeWorker,
+                 WorkerType aWorkerType, const nsAString& aWorkerName,
+                 const nsACString& aServiceWorkerScope,
+                 WorkerLoadInfo& aLoadInfo);
+diff --git a/ipc/ipdl/sync-messages.ini b/ipc/ipdl/sync-messages.ini
+--- a/ipc/ipdl/sync-messages.ini
++++ b/ipc/ipdl/sync-messages.ini
+@@ -1010,8 +1010,10 @@ description =
+ [PHandlerService::ExistsForProtocol]
+ description = bug 1382323
+ [PHandlerService::Exists]
+ description =
+ [PHandlerService::GetTypeFromExtension]
+ description =
+ [PLayerTransaction::ShutdownSync]
+ description = bug 1363126
++[PClientSource::WorkerSyncPing]
++description = Synchronous ping allowing worker thread to confirm actor is created. Necessary to avoid racing with ClientHandle actors on main thread.

+ 139 - 0
mozilla-release/patches/1423412-2-59a1.patch

@@ -0,0 +1,139 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512524723 18000
+# Node ID 09942818c67041082051e9bd25aee3f7899546ba
+# Parent  f92cf67d2d9d065c89425e5c3952271dc6e57a49
+Bug 1423412 P2 Copy the service worker controller across redirects by default and clear it explicitly for non-subresource redirects. r=baku
+
+diff --git a/dom/clients/manager/ClientChannelHelper.cpp b/dom/clients/manager/ClientChannelHelper.cpp
+--- a/dom/clients/manager/ClientChannelHelper.cpp
++++ b/dom/clients/manager/ClientChannelHelper.cpp
+@@ -96,26 +96,16 @@ class ClientChannelHelper final : public
+         if (reservedClientInfo.isSome()) {
+           newLoadInfo->SetReservedClientInfo(reservedClientInfo.ref());
+         }
+ 
+         if (initialClientInfo.isSome()) {
+           newLoadInfo->SetInitialClientInfo(initialClientInfo.ref());
+         }
+       }
+-
+-      // Make sure we keep the service worker controller on same-origin
+-      // internal redirects.
+-      if (oldLoadInfo != newLoadInfo &&
+-          aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
+-        const Maybe<ServiceWorkerDescriptor>& controller = oldLoadInfo->GetController();
+-        if (controller.isSome()) {
+-          newLoadInfo->SetController(controller.ref());
+-        }
+-      }
+     }
+ 
+     // If it's a cross-origin redirect then we discard the old reserved client
+     // and create a new one.
+     else {
+       // If CheckSameOrigin() worked, then the security manager must exist.
+       nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+       MOZ_DIAGNOSTIC_ASSERT(ssm);
+@@ -129,16 +119,30 @@ class ClientChannelHelper final : public
+       // same-origin security policy.
+       reservedClient.reset();
+       reservedClient = ClientManager::CreateSource(ClientType::Window,
+                                                    mEventTarget, principal);
+ 
+       newLoadInfo->GiveReservedClientSource(Move(reservedClient));
+     }
+ 
++    // Normally we keep the controller across channel redirects, but we must
++    // clear it when a non-subresource load redirects.  Only do this for real
++    // redirects, however.
++    //
++    // There is an open spec question about what to do in this case for
++    // worker script redirects.  For now we clear the controller as that
++    // seems most sane. See:
++    //
++    //  https://github.com/w3c/ServiceWorker/issues/1239
++    //
++    if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
++      newLoadInfo->ClearController();
++    }
++
+     nsCOMPtr<nsIChannelEventSink> outerSink = do_GetInterface(mOuter);
+     if (outerSink) {
+       return outerSink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags,
+                                                aCallback);
+     }
+ 
+     aCallback->OnRedirectVerifyCallback(NS_OK);
+     return NS_OK;
+diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp
+--- a/netwerk/base/LoadInfo.cpp
++++ b/netwerk/base/LoadInfo.cpp
+@@ -305,17 +305,17 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
+   , mTriggeringPrincipal(rhs.mTriggeringPrincipal)
+   , mPrincipalToInherit(rhs.mPrincipalToInherit)
+   , mSandboxedLoadingPrincipal(rhs.mSandboxedLoadingPrincipal)
+   , mResultPrincipalURI(rhs.mResultPrincipalURI)
+   , mClientInfo(rhs.mClientInfo)
+   // mReservedClientSource must be handled specially during redirect
+   // mReservedClientInfo must be handled specially during redirect
+   // mInitialClientInfo must be handled specially during redirect
+-  // mController must be handled specially during redirect
++  , mController(rhs.mController)
+   , mLoadingContext(rhs.mLoadingContext)
+   , mContextForTopLevelLoad(rhs.mContextForTopLevelLoad)
+   , mSecurityFlags(rhs.mSecurityFlags)
+   , mInternalContentPolicyType(rhs.mInternalContentPolicyType)
+   , mTainting(rhs.mTainting)
+   , mUpgradeInsecureRequests(rhs.mUpgradeInsecureRequests)
+   , mVerifySignedContent(rhs.mVerifySignedContent)
+   , mEnforceSRI(rhs.mEnforceSRI)
+@@ -1167,16 +1167,22 @@ LoadInfo::GetInitialClientInfo()
+ }
+ 
+ void
+ LoadInfo::SetController(const ServiceWorkerDescriptor& aServiceWorker)
+ {
+   mController.emplace(aServiceWorker);
+ }
+ 
++void
++LoadInfo::ClearController()
++{
++  mController.reset();
++}
++
+ const Maybe<ServiceWorkerDescriptor>&
+ LoadInfo::GetController()
+ {
+   return mController;
+ }
+ 
+ } // namespace net
+ } // namespace mozilla
+diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl
+--- a/netwerk/base/nsILoadInfo.idl
++++ b/netwerk/base/nsILoadInfo.idl
+@@ -911,14 +911,22 @@ interface nsILoadInfo : nsISupports
+    * the first service worker interception occurs.  For subresource requests
+    * it may be set by the source client if its already controlled by a
+    * service worker.
+    */
+   [noscript, nostdcall, notxpcom]
+   void SetController(in const_ServiceWorkerDescriptorRef aServiceWorker);
+ 
+   /**
++   * Clear the service worker controller for this channel.  This should only
++   * be used for window navigation redirects.  By default we want to keep
++   * the controller in all other cases.
++   */
++  [noscript, nostdcall, notxpcom]
++  void ClearController();
++
++  /**
+    * Get the service worker controlling this network request, if one has
+    * been set.
+    */
+   [noscript, nostdcall, notxpcom]
+   const_MaybeServiceWorkerDescriptorRef GetController();
+ };

+ 49 - 0
mozilla-release/patches/1423412-3-59a1.patch

@@ -0,0 +1,49 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512524723 18000
+# Node ID 9a5aac891c5f144811b17168353e55641a31e10c
+# Parent  34d52c89ddddd09746d9c100ef8ce1976a690754
+Bug 1423412 P3 Handle the case where an iframe starts with an inherited controller and then ends up uncontrolled. r=baku
+
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -3512,16 +3512,38 @@ nsGlobalWindow::EnsureClientSource()
+   // so, mark it as controlled immediately here.  The actor may
+   // or may not have been notified by the parent side about being
+   // controlled yet.
+   if (loadInfo) {
+     const Maybe<ServiceWorkerDescriptor> controller = loadInfo->GetController();
+     if (controller.isSome()) {
+       mClientSource->SetController(controller.ref());
+     }
++
++    // We also have to handle the case where te initial about:blank is
++    // controlled due to inheritting the service worker from its parent,
++    // but the actual nsIChannel load is not covered by any service worker.
++    // In this case we want the final page to be uncontrolled.  There is
++    // an open spec issue about how exactly this should be handled, but for
++    // now we just force creation of a new ClientSource to clear the
++    // controller.
++    //
++    //  https://github.com/w3c/ServiceWorker/issues/1232
++    //
++    else if (mClientSource->GetController().isSome()) {
++      mClientSource.reset();
++      mClientSource =
++        ClientManager::CreateSource(ClientType::Window,
++                                    EventTargetFor(TaskCategory::Other),
++                                    mDoc->NodePrincipal());
++      if (NS_WARN_IF(!mClientSource)) {
++        return NS_ERROR_FAILURE;
++      }
++      newClientSource = true;
++    }
+   }
+ 
+   // Its possible that we got a client just after being frozen in
+   // the bfcache.  In that case freeze the client immediately.
+   if (newClientSource && IsFrozen()) {
+     mClientSource->Freeze();
+   }
+ 

+ 573 - 0
mozilla-release/patches/1423412-4no5-59a1.patch

@@ -0,0 +1,573 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512524723 18000
+# Node ID bd99f4732647f01441e5ca93d11b27e98592838b
+# Parent  b81d8e28ed76da935c7469b58bf9c4a6f1749dca
+Bug 1423412 P4 Add a ClientManager::GetInfoAndState() that retrieves the ClientInfo and ClientState for a given ID. r=baku
+
+diff --git a/dom/clients/manager/ClientIPCTypes.ipdlh b/dom/clients/manager/ClientIPCTypes.ipdlh
+--- a/dom/clients/manager/ClientIPCTypes.ipdlh
++++ b/dom/clients/manager/ClientIPCTypes.ipdlh
+@@ -45,39 +45,53 @@ struct IPCClientWorkerState
+ };
+ 
+ union IPCClientState
+ {
+   IPCClientWindowState;
+   IPCClientWorkerState;
+ };
+ 
++struct ClientInfoAndState
++{
++  IPCClientInfo info;
++  IPCClientState state;
++};
++
+ struct ClientSourceExecutionReadyArgs
+ {
+   nsCString url;
+   FrameType frameType;
+ };
+ 
+ struct ClientControlledArgs
+ {
+   IPCServiceWorkerDescriptor serviceWorker;
+ };
+ 
++struct ClientGetInfoAndStateArgs
++{
++  nsID id;
++  PrincipalInfo principalInfo;
++};
++
+ struct ClientOpenWindowArgs
+ {
+ };
+ 
+ union ClientOpConstructorArgs
+ {
+   ClientControlledArgs;
++  ClientGetInfoAndStateArgs;
+ };
+ 
+ struct ClientNavigateOpConstructorArgs
+ {
+ };
+ 
+ union ClientOpResult
+ {
+   nsresult;
++  ClientInfoAndState;
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientManager.cpp
+--- a/dom/clients/manager/ClientManager.cpp
++++ b/dom/clients/manager/ClientManager.cpp
+@@ -240,10 +240,19 @@ ClientManager::CreateSource(ClientType a
+ already_AddRefed<ClientHandle>
+ ClientManager::CreateHandle(const ClientInfo& aClientInfo,
+                             nsISerialEventTarget* aSerialEventTarget)
+ {
+   RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
+   return mgr->CreateHandleInternal(aClientInfo, aSerialEventTarget);
+ }
+ 
++// static
++RefPtr<ClientOpPromise>
++ClientManager::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs,
++                               nsISerialEventTarget* aSerialEventTarget)
++{
++  RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
++  return mgr->StartOp(aArgs, aSerialEventTarget);
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientManager.h b/dom/clients/manager/ClientManager.h
+--- a/dom/clients/manager/ClientManager.h
++++ b/dom/clients/manager/ClientManager.h
+@@ -13,16 +13,17 @@ class nsIPrincipal;
+ 
+ namespace mozilla {
+ namespace ipc {
+ class PBackgroundChild;
+ class PrincipalInfo;
+ } // namespace ipc
+ namespace dom {
+ 
++class ClientGetInfoAndStateArgs;
+ class ClientHandle;
+ class ClientInfo;
+ class ClientManagerChild;
+ class ClientOpConstructorArgs;
+ class ClientSource;
+ enum class ClientType : uint8_t;
+ 
+ namespace workers {
+@@ -88,15 +89,19 @@ public:
+   static UniquePtr<ClientSource>
+   CreateSource(ClientType aType, nsISerialEventTarget* aEventTarget,
+                const mozilla::ipc::PrincipalInfo& aPrincipal);
+ 
+   static already_AddRefed<ClientHandle>
+   CreateHandle(const ClientInfo& aClientInfo,
+                nsISerialEventTarget* aSerialEventTarget);
+ 
++  static RefPtr<ClientOpPromise>
++  GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs,
++                  nsISerialEventTarget* aSerialEventTarget);
++
+   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManager)
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientManager_h
+diff --git a/dom/clients/manager/ClientManagerOpParent.cpp b/dom/clients/manager/ClientManagerOpParent.cpp
+--- a/dom/clients/manager/ClientManagerOpParent.cpp
++++ b/dom/clients/manager/ClientManagerOpParent.cpp
+@@ -6,26 +6,61 @@
+ 
+ #include "ClientManagerOpParent.h"
+ 
+ #include "ClientManagerService.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
++template <typename Method, typename... Args>
++void
++ClientManagerOpParent::DoServiceOp(Method aMethod, Args&&... aArgs)
++{
++  // Note, we need perfect forarding of the template type in order
++  // to allow already_AddRefed<> to be passed as an arg.
++  RefPtr<ClientOpPromise> p = (mService->*aMethod)(Forward<Args>(aArgs)...);
++
++  // Capturing `this` is safe here because we disconnect the promise in
++  // ActorDestroy() which ensures neither lambda is called if the actor
++  // is destroyed before the source operation completes.
++  p->Then(GetCurrentThreadSerialEventTarget(), __func__,
++    [this] (const mozilla::dom::ClientOpResult& aResult) {
++      mPromiseRequestHolder.Complete();
++      Unused << PClientManagerOpParent::Send__delete__(this, aResult);
++    }, [this] (nsresult aRv) {
++      mPromiseRequestHolder.Complete();
++      Unused << PClientManagerOpParent::Send__delete__(this, aRv);
++    })->Track(mPromiseRequestHolder);
++}
++
+ void
+ ClientManagerOpParent::ActorDestroy(ActorDestroyReason aReason)
+ {
++  mPromiseRequestHolder.DisconnectIfExists();
+ }
+ 
+ ClientManagerOpParent::ClientManagerOpParent(ClientManagerService* aService)
+   : mService(aService)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(mService);
+ }
+ 
+ void
+ ClientManagerOpParent::Init(const ClientOpConstructorArgs& aArgs)
+ {
++  switch (aArgs.type()) {
++    case ClientOpConstructorArgs::TClientGetInfoAndStateArgs:
++    {
++      DoServiceOp(&ClientManagerService::GetInfoAndState,
++                  aArgs.get_ClientGetInfoAndStateArgs());
++      break;
++    }
++    default:
++    {
++      MOZ_ASSERT_UNREACHABLE("Unknown Client operation!");
++      break;
++    }
++  }
+ }
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientManagerOpParent.h b/dom/clients/manager/ClientManagerOpParent.h
+--- a/dom/clients/manager/ClientManagerOpParent.h
++++ b/dom/clients/manager/ClientManagerOpParent.h
+@@ -2,25 +2,31 @@
+ /* 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 _mozilla_dom_ClientManagerOpParent_h
+ #define _mozilla_dom_ClientManagerOpParent_h
+ 
+ #include "mozilla/dom/PClientManagerOpParent.h"
++#include "ClientOpPromise.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ class ClientManagerService;
+ 
+ class ClientManagerOpParent final : public PClientManagerOpParent
+ {
+   RefPtr<ClientManagerService> mService;
++  MozPromiseRequestHolder<ClientOpPromise> mPromiseRequestHolder;
++
++  template <typename Method, typename... Args>
++  void
++  DoServiceOp(Method aMethod, Args&&... aArgs);
+ 
+   // PClientManagerOpParent interface
+   void
+   ActorDestroy(ActorDestroyReason aReason) override;
+ 
+ public:
+   explicit ClientManagerOpParent(ClientManagerService* aService);
+   ~ClientManagerOpParent() = default;
+diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp
+--- a/dom/clients/manager/ClientManagerService.cpp
++++ b/dom/clients/manager/ClientManagerService.cpp
+@@ -285,10 +285,24 @@ void
+ ClientManagerService::RemoveManager(ClientManagerParent* aManager)
+ {
+   AssertIsOnBackgroundThread();
+   MOZ_DIAGNOSTIC_ASSERT(aManager);
+   DebugOnly<bool> removed = mManagerList.RemoveElement(aManager);
+   MOZ_ASSERT(removed);
+ }
+ 
++RefPtr<ClientOpPromise>
++ClientManagerService::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs)
++{
++  RefPtr<ClientOpPromise> ref;
++
++  ClientSourceParent* source = FindSource(aArgs.id(), aArgs.principalInfo());
++  if (!source || !source->ExecutionReady()) {
++    ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
++    return ref.forget();
++  }
++
++  return source->StartOp(aArgs);
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientManagerService.h b/dom/clients/manager/ClientManagerService.h
+--- a/dom/clients/manager/ClientManagerService.h
++++ b/dom/clients/manager/ClientManagerService.h
+@@ -1,18 +1,17 @@
+ /* -*- 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 _mozilla_dom_ClientManagerService_h
+ #define _mozilla_dom_ClientManagerService_h
+ 
+-#include "mozilla/ipc/PBackgroundSharedTypes.h"
+-#include "mozilla/MozPromise.h"
++#include "ClientOpPromise.h"
+ #include "nsDataHashtable.h"
+ 
+ namespace mozilla {
+ 
+ namespace dom {
+ 
+ class ClientManagerParent;
+ class ClientSourceParent;
+@@ -55,15 +54,18 @@ public:
+              const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
+ 
+   void
+   AddManager(ClientManagerParent* aManager);
+ 
+   void
+   RemoveManager(ClientManagerParent* aManager);
+ 
++  RefPtr<ClientOpPromise>
++  GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs);
++
+   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManagerService)
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientManagerService_h
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -4,16 +4,17 @@
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ #include "ClientSource.h"
+ 
+ #include "ClientManager.h"
+ #include "ClientManagerChild.h"
+ #include "ClientSourceChild.h"
++#include "ClientState.h"
+ #include "ClientValidation.h"
+ #include "mozilla/dom/ClientIPCTypes.h"
+ #include "mozilla/dom/WorkerPrivate.h"
+ #include "nsIDocShell.h"
+ #include "nsPIDOMWindow.h"
+ 
+ namespace mozilla {
+ namespace dom {
+@@ -49,16 +50,47 @@ ClientSource::ExecutionReady(const Clien
+ 
+   mClientInfo.SetURL(aArgs.url());
+   mClientInfo.SetFrameType(aArgs.frameType());
+   MaybeExecute([aArgs](PClientSourceChild* aActor) {
+     aActor->SendExecutionReady(aArgs);
+   });
+ }
+ 
++nsresult
++ClientSource::SnapshotWindowState(ClientState* aStateOut)
++{
++  MOZ_ASSERT(NS_IsMainThread());
++
++  nsPIDOMWindowInner* window = GetInnerWindow();
++  if (!window || !window->IsCurrentInnerWindow() ||
++      !window->HasActiveDocument()) {
++    *aStateOut = ClientState(ClientWindowState(VisibilityState::Hidden,
++                                               TimeStamp(), false));
++    return NS_OK;
++  }
++
++  nsIDocument* doc = window->GetExtantDoc();
++  if (NS_WARN_IF(!doc)) {
++    return NS_ERROR_UNEXPECTED;
++  }
++
++  ErrorResult rv;
++  bool focused = doc->HasFocus(rv);
++  if (NS_WARN_IF(rv.Failed())) {
++    rv.SuppressException();
++    return rv.StealNSResult();
++  }
++
++  *aStateOut = ClientState(ClientWindowState(doc->VisibilityState(),
++                                             doc->LastFocusTime(), focused));
++
++  return NS_OK;
++}
++
+ WorkerPrivate*
+ ClientSource::GetWorkerPrivate() const
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientSource);
+   if (!mOwner.is<WorkerPrivate*>()) {
+     return nullptr;
+   }
+   return mOwner.as<WorkerPrivate*>();
+@@ -69,16 +101,28 @@ ClientSource::GetDocShell() const
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientSource);
+   if (!mOwner.is<nsCOMPtr<nsIDocShell>>()) {
+     return nullptr;
+   }
+   return mOwner.as<nsCOMPtr<nsIDocShell>>();
+ }
+ 
++void
++ClientSource::MaybeCreateInitialDocument()
++{
++  nsIDocShell* docshell = GetDocShell();
++  if (docshell) {
++    // Force the create of the initial document if it does not exist yet.
++    Unused << docshell->GetDocument();
++
++    MOZ_DIAGNOSTIC_ASSERT(GetInnerWindow());
++  }
++}
++
+ ClientSource::ClientSource(ClientManager* aManager,
+                            nsISerialEventTarget* aEventTarget,
+                            const ClientSourceConstructorArgs& aArgs)
+   : mManager(aManager)
+   , mEventTarget(aEventTarget)
+   , mOwner(AsVariant(Nothing()))
+   , mClientInfo(aArgs.id(), aArgs.type(), aArgs.principalInfo(), aArgs.creationTime())
+ {
+@@ -294,16 +338,52 @@ ClientSource::Control(const ClientContro
+ }
+ 
+ const Maybe<ServiceWorkerDescriptor>&
+ ClientSource::GetController() const
+ {
+   return mController;
+ }
+ 
++RefPtr<ClientOpPromise>
++ClientSource::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs)
++{
++  RefPtr<ClientOpPromise> ref;
++
++  ClientState state;
++  nsresult rv = SnapshotState(&state);
++  if (NS_FAILED(rv)) {
++    ref = ClientOpPromise::CreateAndReject(rv, __func__);
++    return ref.forget();
++  }
++
++  ref = ClientOpPromise::CreateAndResolve(ClientInfoAndState(mClientInfo.ToIPC(),
++                                                             state.ToIPC()), __func__);
++  return ref.forget();
++}
++
++nsresult
++ClientSource::SnapshotState(ClientState* aStateOut)
++{
++  NS_ASSERT_OWNINGTHREAD(ClientSource);
++  MOZ_DIAGNOSTIC_ASSERT(aStateOut);
++
++  if (mClientInfo.Type() == ClientType::Window) {
++    MaybeCreateInitialDocument();
++    nsresult rv = SnapshotWindowState(aStateOut);
++    if (NS_FAILED(rv)) {
++      return rv;
++    }
++    return NS_OK;
++  }
++
++  *aStateOut = ClientState(ClientWorkerState());
++  return NS_OK;
++}
++
+ nsISerialEventTarget*
+ ClientSource::EventTarget() const
+ {
+   return mEventTarget;
+ }
+ 
+ void
+ ClientSource::Traverse(nsCycleCollectionTraversalCallback& aCallback,
+diff --git a/dom/clients/manager/ClientSource.h b/dom/clients/manager/ClientSource.h
+--- a/dom/clients/manager/ClientSource.h
++++ b/dom/clients/manager/ClientSource.h
+@@ -60,16 +60,22 @@ class ClientSource final : public Client
+   ExecutionReady(const ClientSourceExecutionReadyArgs& aArgs);
+ 
+   mozilla::dom::workers::WorkerPrivate*
+   GetWorkerPrivate() const;
+ 
+   nsIDocShell*
+   GetDocShell() const;
+ 
++  void
++  MaybeCreateInitialDocument();
++
++  nsresult
++  SnapshotWindowState(ClientState* aStateOut);
++
+   // Private methods called by ClientManager
+   ClientSource(ClientManager* aManager,
+                nsISerialEventTarget* aEventTarget,
+                const ClientSourceConstructorArgs& aArgs);
+ 
+   void
+   Activate(PClientManagerChild* aActor);
+ 
+@@ -120,16 +126,22 @@ public:
+   RefPtr<ClientOpPromise>
+   Control(const ClientControlledArgs& aArgs);
+ 
+   // Get the ClientSource's current controlling service worker, if one has
+   // been set.
+   const Maybe<ServiceWorkerDescriptor>&
+   GetController() const;
+ 
++  RefPtr<ClientOpPromise>
++  GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs);
++
++  nsresult
++  SnapshotState(ClientState* aStateOut);
++
+   nsISerialEventTarget*
+   EventTarget() const;
+ 
+   void
+   Traverse(nsCycleCollectionTraversalCallback& aCallback,
+            const char* aName,
+            uint32_t aFlags);
+ };
+diff --git a/dom/clients/manager/ClientSourceOpChild.cpp b/dom/clients/manager/ClientSourceOpChild.cpp
+--- a/dom/clients/manager/ClientSourceOpChild.cpp
++++ b/dom/clients/manager/ClientSourceOpChild.cpp
+@@ -74,16 +74,22 @@ void
+ ClientSourceOpChild::Init(const ClientOpConstructorArgs& aArgs)
+ {
+   switch (aArgs.type()) {
+     case ClientOpConstructorArgs::TClientControlledArgs:
+     {
+       DoSourceOp(&ClientSource::Control, aArgs.get_ClientControlledArgs());
+       break;
+     }
++    case ClientOpConstructorArgs::TClientGetInfoAndStateArgs:
++    {
++      DoSourceOp(&ClientSource::GetInfoAndState,
++                 aArgs.get_ClientGetInfoAndStateArgs());
++      break;
++    }
+     default:
+     {
+       MOZ_ASSERT_UNREACHABLE("unknown client operation!");
+       break;
+     }
+   }
+ }
+ 
+diff --git a/dom/clients/manager/ClientSourceParent.cpp b/dom/clients/manager/ClientSourceParent.cpp
+--- a/dom/clients/manager/ClientSourceParent.cpp
++++ b/dom/clients/manager/ClientSourceParent.cpp
+@@ -214,16 +214,22 @@ ClientSourceParent::Info() const
+ }
+ 
+ bool
+ ClientSourceParent::IsFrozen() const
+ {
+   return mFrozen;
+ }
+ 
++bool
++ClientSourceParent::ExecutionReady() const
++{
++  return mExecutionReady;
++}
++
+ void
+ ClientSourceParent::AttachHandle(ClientHandleParent* aClientHandle)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(aClientHandle);
+   MOZ_DIAGNOSTIC_ASSERT(!mFrozen);
+   MOZ_ASSERT(!mHandleList.Contains(aClientHandle));
+   mHandleList.AppendElement(aClientHandle);
+ }
+diff --git a/dom/clients/manager/ClientSourceParent.h b/dom/clients/manager/ClientSourceParent.h
+--- a/dom/clients/manager/ClientSourceParent.h
++++ b/dom/clients/manager/ClientSourceParent.h
+@@ -62,16 +62,19 @@ public:
+   Init();
+ 
+   const ClientInfo&
+   Info() const;
+ 
+   bool
+   IsFrozen() const;
+ 
++  bool
++  ExecutionReady() const;
++
+   void
+   AttachHandle(ClientHandleParent* aClientSource);
+ 
+   void
+   DetachHandle(ClientHandleParent* aClientSource);
+ 
+   RefPtr<ClientOpPromise>
+   StartOp(const ClientOpConstructorArgs& aArgs);

+ 188 - 0
mozilla-release/patches/1423913-1-59a1.patch

@@ -0,0 +1,188 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512751926 18000
+# Node ID 79b9a988f0fd3d33be9d308b7f8aa58ccf1bf216
+# Parent  9ddb3f1c51b8184e3497f163d9bd09232969cd02
+Bug 1423913 P1 Make ClientManager::CreateSource() and CreateHandle() infallible.  Errors result in a detached object instead of nullptr.  r=baku
+
+diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientManager.cpp
+--- a/dom/clients/manager/ClientManager.cpp
++++ b/dom/clients/manager/ClientManager.cpp
+@@ -98,46 +98,58 @@ ClientManager::Shutdown()
+ 
+ UniquePtr<ClientSource>
+ ClientManager::CreateSourceInternal(ClientType aType,
+                                     nsISerialEventTarget* aEventTarget,
+                                     const PrincipalInfo& aPrincipal)
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientManager);
+ 
+-  if (IsShutdown()) {
+-    return nullptr;
+-  }
+-
+   nsID id;
+   nsresult rv = nsContentUtils::GenerateUUIDInPlace(id);
++  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+   if (NS_WARN_IF(NS_FAILED(rv))) {
+-    return nullptr;
++    // If we can't even get a UUID, at least make sure not to use a garbage
++    // value.  Instead return a shutdown ClientSource with a zero'd id.
++    // This should be exceptionally rare, if it happens at all.
++    id.Clear();
++    ClientSourceConstructorArgs args(id, aType, aPrincipal, TimeStamp::Now());
++    UniquePtr<ClientSource> source(new ClientSource(this, aEventTarget, args));
++    source->Shutdown();
++    return Move(source);
+   }
+ 
+   ClientSourceConstructorArgs args(id, aType, aPrincipal, TimeStamp::Now());
+   UniquePtr<ClientSource> source(new ClientSource(this, aEventTarget, args));
++
++  if (IsShutdown()) {
++    source->Shutdown();
++    return Move(source);
++  }
++
+   source->Activate(GetActor());
+ 
+   return Move(source);
+ }
+ 
+ already_AddRefed<ClientHandle>
+ ClientManager::CreateHandleInternal(const ClientInfo& aClientInfo,
+                                     nsISerialEventTarget* aSerialEventTarget)
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientManager);
+   MOZ_DIAGNOSTIC_ASSERT(aSerialEventTarget);
+ 
++  RefPtr<ClientHandle> handle = new ClientHandle(this, aSerialEventTarget,
++                                                 aClientInfo);
++
+   if (IsShutdown()) {
+-    return nullptr;
++    handle->Shutdown();
++    return handle.forget();
+   }
+ 
+-  RefPtr<ClientHandle> handle = new ClientHandle(this, aSerialEventTarget,
+-                                                 aClientInfo);
+   handle->Activate(GetActor());
+ 
+   return handle.forget();
+ }
+ 
+ already_AddRefed<ClientOpPromise>
+ ClientManager::StartOp(const ClientOpConstructorArgs& aArgs,
+                        nsISerialEventTarget* aSerialEventTarget)
+@@ -215,17 +227,17 @@ ClientManager::CreateSource(ClientType a
+                             nsIPrincipal* aPrincipal)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   MOZ_ASSERT(aPrincipal);
+ 
+   PrincipalInfo principalInfo;
+   nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
+   if (NS_WARN_IF(NS_FAILED(rv))) {
+-    return nullptr;
++    MOZ_CRASH("ClientManager::CreateSource() cannot serialize bad principal");
+   }
+ 
+   RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
+   return mgr->CreateSourceInternal(aType, aEventTarget, principalInfo);
+ }
+ 
+ // static
+ UniquePtr<ClientSource>
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -177,16 +177,20 @@ ClientSource::GetInnerWindow() const
+ }
+ 
+ void
+ ClientSource::WorkerExecutionReady(WorkerPrivate* aWorkerPrivate)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
+   aWorkerPrivate->AssertIsOnWorkerThread();
+ 
++  if (IsShutdown()) {
++    return;
++  }
++
+   // Its safe to store the WorkerPrivate* here because the ClientSource
+   // is explicitly destroyed by WorkerPrivate before exiting its run loop.
+   MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>());
+   mOwner = AsVariant(aWorkerPrivate);
+ 
+   ClientSourceExecutionReadyArgs args(
+     aWorkerPrivate->GetLocationInfo().mHref,
+     FrameType::None);
+@@ -197,16 +201,20 @@ ClientSource::WorkerExecutionReady(Worke
+ nsresult
+ ClientSource::WindowExecutionReady(nsPIDOMWindowInner* aInnerWindow)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   MOZ_DIAGNOSTIC_ASSERT(aInnerWindow);
+   MOZ_DIAGNOSTIC_ASSERT(aInnerWindow->IsCurrentInnerWindow());
+   MOZ_DIAGNOSTIC_ASSERT(aInnerWindow->HasActiveDocument());
+ 
++  if (IsShutdown()) {
++    return NS_OK;
++  }
++
+   nsIDocument* doc = aInnerWindow->GetExtantDoc();
+   if (NS_WARN_IF(!doc)) {
+     return NS_ERROR_UNEXPECTED;
+   }
+ 
+   // Don't use nsAutoCString here since IPC requires a full nsCString anyway.
+   nsCString spec;
+ 
+@@ -248,16 +256,20 @@ ClientSource::WindowExecutionReady(nsPID
+ }
+ 
+ nsresult
+ ClientSource::DocShellExecutionReady(nsIDocShell* aDocShell)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   MOZ_DIAGNOSTIC_ASSERT(aDocShell);
+ 
++  if (IsShutdown()) {
++    return NS_OK;
++  }
++
+   nsPIDOMWindowOuter* outer = aDocShell->GetWindow();
+   if (NS_WARN_IF(!outer)) {
+     return NS_ERROR_UNEXPECTED;
+   }
+ 
+   // TODO: dedupe this with WindowExecutionReady
+   FrameType frameType = FrameType::Top_level;
+   if (!outer->IsTopLevelWindow()) {
+@@ -301,19 +313,25 @@ ClientSource::Info() const
+   return mClientInfo;
+ }
+ 
+ void
+ ClientSource::WorkerSyncPing(WorkerPrivate* aWorkerPrivate)
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientSource);
+   MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
++
++  if (IsShutdown()) {
++    return;
++  }
++
+   MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate == mManager->GetWorkerPrivate());
+   aWorkerPrivate->AssertIsOnWorkerThread();
+   MOZ_DIAGNOSTIC_ASSERT(GetActor());
++
+   GetActor()->SendWorkerSyncPing();
+ }
+ 
+ void
+ ClientSource::SetController(const ServiceWorkerDescriptor& aServiceWorker)
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientSource);
+ 

+ 185 - 0
mozilla-release/patches/1423913-2-59a1.patch

@@ -0,0 +1,185 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512751927 18000
+# Node ID f6b07d14b67bfaeafe0d23a5a743a1465957d831
+# Parent  cbbb560087b9e1a83cfba180ed5bf34a5b6f0af8
+Bug 1423913 P2 Make callers expect infallble CreateSource() and CreateHandle(). r=baku
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -3421,19 +3421,17 @@ nsDocShell::MaybeCreateInitialClientSour
+   if (!win) {
+     return;
+   }
+ 
+   mInitialClientSource =
+     ClientManager::CreateSource(ClientType::Window,
+                                 win->EventTargetFor(TaskCategory::Other),
+                                 principal);
+-  if (NS_WARN_IF(!mInitialClientSource)) {
+-    return;
+-  }
++  MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource);
+ 
+   // Mark the initial client as execution ready, but owned by the docshell.
+   // If the client is actually used this will cause ClientSource to force
+   // the creation of the initial about:blank by calling nsDocShell::GetDocument().
+   mInitialClientSource->DocShellExecutionReady(this);
+ 
+   // Next, check to see if the parent is controlled.
+   nsCOMPtr<nsIDocShell> parent = GetParentDocshell();
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -3497,19 +3497,17 @@ nsGlobalWindow::EnsureClientSource()
+   //       principal.  We do this in docshell, but as mentioned we aren't
+   //       smart enough to handle all cases yet.  For example, a
+   //       window.open() with new URL should inherit the controller from
+   //       the opener, but we probably don't handle that yet.
+   if (!mClientSource) {
+     mClientSource = ClientManager::CreateSource(ClientType::Window,
+                                                 EventTargetFor(TaskCategory::Other),
+                                                 mDoc->NodePrincipal());
+-    if (NS_WARN_IF(!mClientSource)) {
+-      return NS_ERROR_FAILURE;
+-    }
++    MOZ_DIAGNOSTIC_ASSERT(mClientSource);
+     newClientSource = true;
+   }
+ 
+   // The load may have started controlling the Client as well.  If
+   // so, mark it as controlled immediately here.  The actor may
+   // or may not have been notified by the parent side about being
+   // controlled yet.
+   if (loadInfo) {
+@@ -3529,19 +3527,17 @@ nsGlobalWindow::EnsureClientSource()
+     //  https://github.com/w3c/ServiceWorker/issues/1232
+     //
+     else if (mClientSource->GetController().isSome()) {
+       mClientSource.reset();
+       mClientSource =
+         ClientManager::CreateSource(ClientType::Window,
+                                     EventTargetFor(TaskCategory::Other),
+                                     mDoc->NodePrincipal());
+-      if (NS_WARN_IF(!mClientSource)) {
+-        return NS_ERROR_FAILURE;
+-      }
++      MOZ_DIAGNOSTIC_ASSERT(mClientSource);
+       newClientSource = true;
+     }
+   }
+ 
+   // Its possible that we got a client just after being frozen in
+   // the bfcache.  In that case freeze the client immediately.
+   if (newClientSource && IsFrozen()) {
+     mClientSource->Freeze();
+diff --git a/dom/clients/manager/ClientChannelHelper.cpp b/dom/clients/manager/ClientChannelHelper.cpp
+--- a/dom/clients/manager/ClientChannelHelper.cpp
++++ b/dom/clients/manager/ClientChannelHelper.cpp
+@@ -115,16 +115,17 @@ class ClientChannelHelper final : public
+       NS_ENSURE_SUCCESS(rv, rv);
+ 
+       // Create the new ClientSource.  This should only happen for window
+       // Clients since support cross-origin redirects are blocked by the
+       // same-origin security policy.
+       reservedClient.reset();
+       reservedClient = ClientManager::CreateSource(ClientType::Window,
+                                                    mEventTarget, principal);
++      MOZ_DIAGNOSTIC_ASSERT(reservedClient);
+ 
+       newLoadInfo->GiveReservedClientSource(Move(reservedClient));
+     }
+ 
+     // Normally we keep the controller across channel redirects, but we must
+     // clear it when a non-subresource load redirects.  Only do this for real
+     // redirects, however.
+     //
+@@ -223,17 +224,17 @@ AddClientChannelHelper(nsIChannel* aChan
+   if (initialClientInfo.isNothing() && reservedClientInfo.isNothing()) {
+     // Wait to reserve the client until we are reasonably sure this method
+     // will succeed.  We should only follow this path for window clients.
+     // Workers should always provide a reserved ClientInfo since their
+     // ClientSource object is owned by a different thread.
+     reservedClient = ClientManager::CreateSource(ClientType::Window,
+                                                  aEventTarget,
+                                                  channelPrincipal);
+-    NS_ENSURE_TRUE(reservedClient, NS_ERROR_FAILURE);
++    MOZ_DIAGNOSTIC_ASSERT(reservedClient);
+   }
+ 
+   RefPtr<ClientChannelHelper> helper =
+     new ClientChannelHelper(outerCallbacks, aEventTarget);
+ 
+   // Only set the callbacks helper if we are able to reserve the client
+   // successfully.
+   rv = aChannel->SetNotificationCallbacks(helper);
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -2362,19 +2362,17 @@ ServiceWorkerManager::StartControllingAD
+   ServiceWorkerInfo* activeWorker = aRegistration->GetActive();
+   nsPIDOMWindowInner* innerWindow = aDoc->GetInnerWindow();
+   if (activeWorker && innerWindow) {
+     Maybe<ClientInfo> clientInfo = innerWindow->GetClientInfo();
+     if (clientInfo.isSome()) {
+       RefPtr<ClientHandle> clientHandle =
+         ClientManager::CreateHandle(clientInfo.ref(),
+                                     SystemGroup::EventTargetFor(TaskCategory::Other));
+-      if (clientHandle) {
+-        ref = Move(clientHandle->Control(activeWorker->Descriptor()));
+-      }
++      ref = Move(clientHandle->Control(activeWorker->Descriptor()));
+     }
+   }
+ 
+   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
+   return Move(ref);
+ }
+ 
+ void
+@@ -2710,19 +2708,17 @@ ServiceWorkerManager::DispatchFetchEvent
+ 
+       if (clientInfo.isSome()) {
+         // First, attempt to mark the reserved client controlled directly.  This
+         // will update the controlled status in the ClientManagerService in the
+         // parent.  It will also eventually propagate back to the ClientSource.
+         RefPtr<ClientHandle> clientHandle =
+           ClientManager::CreateHandle(clientInfo.ref(),
+                                       SystemGroup::EventTargetFor(TaskCategory::Other));
+-        if (clientHandle) {
+-          clientHandle->Control(serviceWorker->Descriptor());
+-        }
++        clientHandle->Control(serviceWorker->Descriptor());
+       }
+ 
+       // But we also note the reserved state on the LoadInfo.  This allows the
+       // ClientSource to be updated immediately after the nsIChannel starts.
+       // This is necessary to have the correct controller in place for immediate
+       // follow-on requests.
+       loadInfo->SetController(serviceWorker->Descriptor());
+     }
+diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
+--- a/dom/workers/WorkerPrivate.cpp
++++ b/dom/workers/WorkerPrivate.cpp
+@@ -5322,19 +5322,17 @@ WorkerPrivate::EnsureClientSource()
+       type = ClientType::Serviceworker;
+       break;
+     default:
+       MOZ_CRASH("unknown worker type!");
+   }
+ 
+   mClientSource = ClientManager::CreateSource(type, mWorkerHybridEventTarget,
+                                               GetPrincipalInfo());
+-  if (!mClientSource) {
+-    return false;
+-  }
++  MOZ_DIAGNOSTIC_ASSERT(mClientSource);
+ 
+   if (mFrozen) {
+     mClientSource->Freeze();
+   }
+ 
+   // Shortly after the client is reserved we will try loading the main script
+   // for the worker.  This may get intercepted by the ServiceWorkerManager
+   // which will then try to create a ClientHandle.  Its actually possible for

+ 98 - 0
mozilla-release/patches/1423913-3-59a1.patch

@@ -0,0 +1,98 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512751927 18000
+# Node ID c71de7f98d08b1772f7dc54d56828c34f86ef16e
+# Parent  89619781ee4b54e7694e003dd9fae8c959313df2
+Bug 1423913 P3 Don't register more shutdown handle if we create more than one ClientManagerService instance. r=baku
+
+diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp
+--- a/dom/clients/manager/ClientManagerService.cpp
++++ b/dom/clients/manager/ClientManagerService.cpp
+@@ -19,16 +19,17 @@ namespace dom {
+ 
+ using mozilla::ipc::AssertIsOnBackgroundThread;
+ using mozilla::ipc::ContentPrincipalInfo;
+ using mozilla::ipc::PrincipalInfo;
+ 
+ namespace {
+ 
+ ClientManagerService* sClientManagerServiceInstance = nullptr;
++bool sClientManagerServiceShutdownRegistered = false;
+ 
+ bool
+ MatchPrincipalInfo(const PrincipalInfo& aLeft, const PrincipalInfo& aRight)
+ {
+   if (aLeft.type() != aRight.type()) {
+     return false;
+   }
+ 
+@@ -141,45 +142,56 @@ OnShutdown()
+ 
+ } // anonymous namespace
+ 
+ ClientManagerService::ClientManagerService()
+   : mShutdown(false)
+ {
+   AssertIsOnBackgroundThread();
+ 
+-  // While the ClientManagerService will be gracefully terminated as windows
+-  // and workers are naturally killed, this can cause us to do extra work
+-  // relatively late in the shutdown process.  To avoid this we eagerly begin
+-  // shutdown at the first sign it has begun.  Since we handle normal shutdown
+-  // gracefully we don't really need to block anything here.  We just begin
+-  // destroying our IPC actors immediately.
+-  OnShutdown()->Then(GetCurrentThreadSerialEventTarget(), __func__,
+-    [] () {
+-      RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance();
+-      if (svc) {
+-        svc->Shutdown();
+-      }
+-    });
++  // Only register one shutdown handler at a time.  If a previous service
++  // instance did this, but shutdown has not come, then we can avoid
++  // doing it again.
++  if (!sClientManagerServiceShutdownRegistered) {
++    sClientManagerServiceShutdownRegistered = true;
++
++    // While the ClientManagerService will be gracefully terminated as windows
++    // and workers are naturally killed, this can cause us to do extra work
++    // relatively late in the shutdown process.  To avoid this we eagerly begin
++    // shutdown at the first sign it has begun.  Since we handle normal shutdown
++    // gracefully we don't really need to block anything here.  We just begin
++    // destroying our IPC actors immediately.
++    OnShutdown()->Then(GetCurrentThreadSerialEventTarget(), __func__,
++      [] () {
++        // Look up the latest service instance, if it exists.  This may
++        // be different from the instance that registered the shutdown
++        // handler.
++        RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance();
++        if (svc) {
++          svc->Shutdown();
++        }
++      });
++  }
+ }
+ 
+ ClientManagerService::~ClientManagerService()
+ {
+   AssertIsOnBackgroundThread();
+   MOZ_DIAGNOSTIC_ASSERT(mSourceTable.Count() == 0);
+   MOZ_DIAGNOSTIC_ASSERT(mManagerList.IsEmpty());
+ 
+   MOZ_DIAGNOSTIC_ASSERT(sClientManagerServiceInstance == this);
+   sClientManagerServiceInstance = nullptr;
+ }
+ 
+ void
+ ClientManagerService::Shutdown()
+ {
+   AssertIsOnBackgroundThread();
++  MOZ_DIAGNOSTIC_ASSERT(sClientManagerServiceShutdownRegistered);
+ 
+   // If many ClientManagerService are created and destroyed quickly we can
+   // in theory get more than one shutdown listener calling us.
+   if (mShutdown) {
+     return;
+   }
+   mShutdown = true;
+ 

+ 400 - 0
mozilla-release/patches/1424338-1-59a1.patch

@@ -0,0 +1,400 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512762402 18000
+# Node ID 74a13c7c99270e99af3b4da27a887979d5d710ee
+# Parent  3a4d695eeb5d1f139dc5c2a4d5028831fda126fb
+Bug 1424338 P1 Implement ClientManager::MatchAll() to support clients.matchAll() WebAPI. r=baku
+
+diff --git a/dom/clients/manager/ClientIPCTypes.ipdlh b/dom/clients/manager/ClientIPCTypes.ipdlh
+--- a/dom/clients/manager/ClientIPCTypes.ipdlh
++++ b/dom/clients/manager/ClientIPCTypes.ipdlh
+@@ -62,36 +62,56 @@ struct ClientSourceExecutionReadyArgs
+   FrameType frameType;
+ };
+ 
+ struct ClientControlledArgs
+ {
+   IPCServiceWorkerDescriptor serviceWorker;
+ };
+ 
++union ClientEndPoint
++{
++  IPCClientInfo;
++  IPCServiceWorkerDescriptor;
++};
++
++struct ClientMatchAllArgs
++{
++  ClientEndPoint endpoint;
++  ClientType type;
++  bool includeUncontrolled;
++};
++
+ struct ClientGetInfoAndStateArgs
+ {
+   nsID id;
+   PrincipalInfo principalInfo;
+ };
+ 
+ struct ClientOpenWindowArgs
+ {
+ };
+ 
+ union ClientOpConstructorArgs
+ {
+   ClientControlledArgs;
++  ClientMatchAllArgs;
+   ClientGetInfoAndStateArgs;
+ };
+ 
++struct ClientList
++{
++  ClientInfoAndState[] values;
++};
++
+ struct ClientNavigateOpConstructorArgs
+ {
+ };
+ 
+ union ClientOpResult
+ {
+   nsresult;
+   ClientInfoAndState;
++  ClientList;
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientManager.cpp
+--- a/dom/clients/manager/ClientManager.cpp
++++ b/dom/clients/manager/ClientManager.cpp
+@@ -254,16 +254,25 @@ ClientManager::CreateHandle(const Client
+                             nsISerialEventTarget* aSerialEventTarget)
+ {
+   RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
+   return mgr->CreateHandleInternal(aClientInfo, aSerialEventTarget);
+ }
+ 
+ // static
+ RefPtr<ClientOpPromise>
++ClientManager::MatchAll(const ClientMatchAllArgs& aArgs,
++                        nsISerialEventTarget* aSerialEventTarget)
++{
++  RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
++  return mgr->StartOp(aArgs, aSerialEventTarget);
++}
++
++// static
++RefPtr<ClientOpPromise>
+ ClientManager::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs,
+                                nsISerialEventTarget* aSerialEventTarget)
+ {
+   RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
+   return mgr->StartOp(aArgs, aSerialEventTarget);
+ }
+ 
+ } // namespace dom
+diff --git a/dom/clients/manager/ClientManager.h b/dom/clients/manager/ClientManager.h
+--- a/dom/clients/manager/ClientManager.h
++++ b/dom/clients/manager/ClientManager.h
+@@ -17,16 +17,17 @@ class PBackgroundChild;
+ class PrincipalInfo;
+ } // namespace ipc
+ namespace dom {
+ 
+ class ClientGetInfoAndStateArgs;
+ class ClientHandle;
+ class ClientInfo;
+ class ClientManagerChild;
++class ClientMatchAllArgs;
+ class ClientOpConstructorArgs;
+ class ClientSource;
+ enum class ClientType : uint8_t;
+ 
+ namespace workers {
+ class WorkerPrivate;
+ } // workers namespace
+ 
+@@ -90,16 +91,19 @@ public:
+   CreateSource(ClientType aType, nsISerialEventTarget* aEventTarget,
+                const mozilla::ipc::PrincipalInfo& aPrincipal);
+ 
+   static already_AddRefed<ClientHandle>
+   CreateHandle(const ClientInfo& aClientInfo,
+                nsISerialEventTarget* aSerialEventTarget);
+ 
+   static RefPtr<ClientOpPromise>
++  MatchAll(const ClientMatchAllArgs& aArgs, nsISerialEventTarget* aTarget);
++
++  static RefPtr<ClientOpPromise>
+   GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs,
+                   nsISerialEventTarget* aSerialEventTarget);
+ 
+   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManager)
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientManagerOpParent.cpp b/dom/clients/manager/ClientManagerOpParent.cpp
+--- a/dom/clients/manager/ClientManagerOpParent.cpp
++++ b/dom/clients/manager/ClientManagerOpParent.cpp
+@@ -43,16 +43,22 @@ ClientManagerOpParent::ClientManagerOpPa
+ {
+   MOZ_DIAGNOSTIC_ASSERT(mService);
+ }
+ 
+ void
+ ClientManagerOpParent::Init(const ClientOpConstructorArgs& aArgs)
+ {
+   switch (aArgs.type()) {
++    case ClientOpConstructorArgs::TClientMatchAllArgs:
++    {
++      DoServiceOp(&ClientManagerService::MatchAll,
++                  aArgs.get_ClientMatchAllArgs());
++      break;
++    }
+     case ClientOpConstructorArgs::TClientGetInfoAndStateArgs:
+     {
+       DoServiceOp(&ClientManagerService::GetInfoAndState,
+                   aArgs.get_ClientGetInfoAndStateArgs());
+       break;
+     }
+     default:
+     {
+diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp
+--- a/dom/clients/manager/ClientManagerService.cpp
++++ b/dom/clients/manager/ClientManagerService.cpp
+@@ -297,16 +297,161 @@ void
+ ClientManagerService::RemoveManager(ClientManagerParent* aManager)
+ {
+   AssertIsOnBackgroundThread();
+   MOZ_DIAGNOSTIC_ASSERT(aManager);
+   DebugOnly<bool> removed = mManagerList.RemoveElement(aManager);
+   MOZ_ASSERT(removed);
+ }
+ 
++namespace
++{
++
++class PromiseListHolder final
++{
++  RefPtr<ClientOpPromise::Private> mResultPromise;
++  nsTArray<RefPtr<ClientOpPromise>> mPromiseList;
++  nsTArray<ClientInfoAndState> mResultList;
++  uint32_t mOutstandingPromiseCount;
++
++  void
++  ProcessSuccess(const ClientInfoAndState& aResult)
++  {
++    mResultList.AppendElement(aResult);
++    ProcessCompletion();
++  }
++
++  void
++  ProcessCompletion()
++  {
++    MOZ_DIAGNOSTIC_ASSERT(mOutstandingPromiseCount > 0);
++    mOutstandingPromiseCount -= 1;
++    MaybeFinish();
++  }
++
++  ~PromiseListHolder() = default;
++public:
++  PromiseListHolder()
++    : mResultPromise(new ClientOpPromise::Private(__func__))
++    , mOutstandingPromiseCount(0)
++  {
++  }
++
++  already_AddRefed<ClientOpPromise>
++  GetResultPromise()
++  {
++    RefPtr<PromiseListHolder> kungFuDeathGrip = this;
++    mResultPromise->Then(
++      GetCurrentThreadSerialEventTarget(), __func__,
++      [kungFuDeathGrip] (const ClientOpResult& aResult) { },
++      [kungFuDeathGrip] (nsresult aResult) { });
++
++    RefPtr<ClientOpPromise> ref = mResultPromise;
++    return ref.forget();
++  }
++
++  void
++  AddPromise(RefPtr<ClientOpPromise>&& aPromise)
++  {
++    mPromiseList.AppendElement(Move(aPromise));
++    MOZ_DIAGNOSTIC_ASSERT(mPromiseList.LastElement());
++    mOutstandingPromiseCount += 1;
++
++    RefPtr<PromiseListHolder> self(this);
++    mPromiseList.LastElement()->Then(
++      GetCurrentThreadSerialEventTarget(), __func__,
++      [self] (const ClientOpResult& aResult) {
++        // TODO: This is pretty clunky.  Try to figure out a better
++        //       wait for MatchAll() and Claim() to share this code
++        //       even though they expect different return values.
++        if (aResult.type() == ClientOpResult::TClientInfoAndState) {
++          self->ProcessSuccess(aResult.get_ClientInfoAndState());
++        } else {
++          self->ProcessCompletion();
++        }
++      }, [self] (nsresult aResult) {
++        self->ProcessCompletion();
++      });
++  }
++
++  void
++  MaybeFinish()
++  {
++    if (!mOutstandingPromiseCount) {
++      mResultPromise->Resolve(mResultList, __func__);
++    }
++  }
++
++  NS_INLINE_DECL_REFCOUNTING(PromiseListHolder)
++};
++
++} // anonymous namespace
++
++RefPtr<ClientOpPromise>
++ClientManagerService::MatchAll(const ClientMatchAllArgs& aArgs)
++{
++  AssertIsOnBackgroundThread();
++
++  const ClientEndPoint& endpoint = aArgs.endpoint();
++
++  const PrincipalInfo& principalInfo =
++    endpoint.type() == ClientEndPoint::TIPCClientInfo
++      ? endpoint.get_IPCClientInfo().principalInfo()
++      : endpoint.get_IPCServiceWorkerDescriptor().principalInfo();
++
++  RefPtr<PromiseListHolder> promiseList = new PromiseListHolder();
++
++  for (auto iter = mSourceTable.Iter(); !iter.Done(); iter.Next()) {
++    ClientSourceParent* source = iter.UserData();
++    MOZ_DIAGNOSTIC_ASSERT(source);
++
++    if (source->IsFrozen() || !source->ExecutionReady()) {
++      continue;
++    }
++
++    if (aArgs.type() != ClientType::All &&
++        source->Info().Type() != aArgs.type()) {
++      continue;
++    }
++
++    if (!MatchPrincipalInfo(source->Info().PrincipalInfo(), principalInfo)) {
++      continue;
++    }
++
++    if (!aArgs.includeUncontrolled()) {
++      if (endpoint.type() != ClientEndPoint::TIPCServiceWorkerDescriptor) {
++        continue;
++      }
++
++      const Maybe<ServiceWorkerDescriptor>& controller =
++        source->GetController();
++      if (controller.isNothing()) {
++        continue;
++      }
++
++      const IPCServiceWorkerDescriptor& serviceWorker =
++        endpoint.get_IPCServiceWorkerDescriptor();
++
++      if(controller.ref().Id() != serviceWorker.id() ||
++         controller.ref().Scope() != serviceWorker.scope()) {
++        continue;
++      }
++    }
++
++    promiseList->AddPromise(
++      source->StartOp(Move(ClientGetInfoAndStateArgs(source->Info().Id(),
++                                                     source->Info().PrincipalInfo()))));
++  }
++
++  // Maybe finish the promise now in case we didn't find any matching clients.
++  promiseList->MaybeFinish();
++
++  return promiseList->GetResultPromise();
++}
++
+ RefPtr<ClientOpPromise>
+ ClientManagerService::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs)
+ {
+   RefPtr<ClientOpPromise> ref;
+ 
+   ClientSourceParent* source = FindSource(aArgs.id(), aArgs.principalInfo());
+   if (!source || !source->ExecutionReady()) {
+     ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+diff --git a/dom/clients/manager/ClientManagerService.h b/dom/clients/manager/ClientManagerService.h
+--- a/dom/clients/manager/ClientManagerService.h
++++ b/dom/clients/manager/ClientManagerService.h
+@@ -55,16 +55,19 @@ public:
+ 
+   void
+   AddManager(ClientManagerParent* aManager);
+ 
+   void
+   RemoveManager(ClientManagerParent* aManager);
+ 
+   RefPtr<ClientOpPromise>
++  MatchAll(const ClientMatchAllArgs& aArgs);
++
++  RefPtr<ClientOpPromise>
+   GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs);
+ 
+   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManagerService)
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+diff --git a/dom/clients/manager/ClientSourceParent.cpp b/dom/clients/manager/ClientSourceParent.cpp
+--- a/dom/clients/manager/ClientSourceParent.cpp
++++ b/dom/clients/manager/ClientSourceParent.cpp
+@@ -220,16 +220,22 @@ ClientSourceParent::IsFrozen() const
+ }
+ 
+ bool
+ ClientSourceParent::ExecutionReady() const
+ {
+   return mExecutionReady;
+ }
+ 
++const Maybe<ServiceWorkerDescriptor>&
++ClientSourceParent::GetController() const
++{
++  return mController;
++}
++
+ void
+ ClientSourceParent::AttachHandle(ClientHandleParent* aClientHandle)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(aClientHandle);
+   MOZ_DIAGNOSTIC_ASSERT(!mFrozen);
+   MOZ_ASSERT(!mHandleList.Contains(aClientHandle));
+   mHandleList.AppendElement(aClientHandle);
+ }
+diff --git a/dom/clients/manager/ClientSourceParent.h b/dom/clients/manager/ClientSourceParent.h
+--- a/dom/clients/manager/ClientSourceParent.h
++++ b/dom/clients/manager/ClientSourceParent.h
+@@ -65,16 +65,19 @@ public:
+   Info() const;
+ 
+   bool
+   IsFrozen() const;
+ 
+   bool
+   ExecutionReady() const;
+ 
++  const Maybe<ServiceWorkerDescriptor>&
++  GetController() const;
++
+   void
+   AttachHandle(ClientHandleParent* aClientSource);
+ 
+   void
+   DetachHandle(ClientHandleParent* aClientSource);
+ 
+   RefPtr<ClientOpPromise>
+   StartOp(const ClientOpConstructorArgs& aArgs);

+ 330 - 0
mozilla-release/patches/1424338-2-59a1.patch

@@ -0,0 +1,330 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512762402 18000
+# Node ID b73ceb5d7db6111e9de30c84010030c624bcbd31
+# Parent  d8ece6bd6c5620097a6201e823a2833bd2afde8d
+Bug 1424338 P2 Add ClientManager::Claim() message to matching ClientSource objects. r=baku
+
+diff --git a/dom/clients/manager/ClientIPCTypes.ipdlh b/dom/clients/manager/ClientIPCTypes.ipdlh
+--- a/dom/clients/manager/ClientIPCTypes.ipdlh
++++ b/dom/clients/manager/ClientIPCTypes.ipdlh
+@@ -75,30 +75,36 @@ union ClientEndPoint
+ 
+ struct ClientMatchAllArgs
+ {
+   ClientEndPoint endpoint;
+   ClientType type;
+   bool includeUncontrolled;
+ };
+ 
++struct ClientClaimArgs
++{
++  IPCServiceWorkerDescriptor serviceWorker;
++};
++
+ struct ClientGetInfoAndStateArgs
+ {
+   nsID id;
+   PrincipalInfo principalInfo;
+ };
+ 
+ struct ClientOpenWindowArgs
+ {
+ };
+ 
+ union ClientOpConstructorArgs
+ {
+   ClientControlledArgs;
+   ClientMatchAllArgs;
++  ClientClaimArgs;
+   ClientGetInfoAndStateArgs;
+ };
+ 
+ struct ClientList
+ {
+   ClientInfoAndState[] values;
+ };
+ 
+diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientManager.cpp
+--- a/dom/clients/manager/ClientManager.cpp
++++ b/dom/clients/manager/ClientManager.cpp
+@@ -263,16 +263,25 @@ ClientManager::MatchAll(const ClientMatc
+                         nsISerialEventTarget* aSerialEventTarget)
+ {
+   RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
+   return mgr->StartOp(aArgs, aSerialEventTarget);
+ }
+ 
+ // static
+ RefPtr<ClientOpPromise>
++ClientManager::Claim(const ClientClaimArgs& aArgs,
++                     nsISerialEventTarget* aSerialEventTarget)
++{
++  RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
++  return mgr->StartOp(aArgs, aSerialEventTarget);
++}
++
++// static
++RefPtr<ClientOpPromise>
+ ClientManager::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs,
+                                nsISerialEventTarget* aSerialEventTarget)
+ {
+   RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
+   return mgr->StartOp(aArgs, aSerialEventTarget);
+ }
+ 
+ } // namespace dom
+diff --git a/dom/clients/manager/ClientManager.h b/dom/clients/manager/ClientManager.h
+--- a/dom/clients/manager/ClientManager.h
++++ b/dom/clients/manager/ClientManager.h
+@@ -13,16 +13,17 @@ class nsIPrincipal;
+ 
+ namespace mozilla {
+ namespace ipc {
+ class PBackgroundChild;
+ class PrincipalInfo;
+ } // namespace ipc
+ namespace dom {
+ 
++class ClientClaimArgs;
+ class ClientGetInfoAndStateArgs;
+ class ClientHandle;
+ class ClientInfo;
+ class ClientManagerChild;
+ class ClientMatchAllArgs;
+ class ClientOpConstructorArgs;
+ class ClientSource;
+ enum class ClientType : uint8_t;
+@@ -94,16 +95,19 @@ public:
+   static already_AddRefed<ClientHandle>
+   CreateHandle(const ClientInfo& aClientInfo,
+                nsISerialEventTarget* aSerialEventTarget);
+ 
+   static RefPtr<ClientOpPromise>
+   MatchAll(const ClientMatchAllArgs& aArgs, nsISerialEventTarget* aTarget);
+ 
+   static RefPtr<ClientOpPromise>
++  Claim(const ClientClaimArgs& aArgs, nsISerialEventTarget* aSerialEventTarget);
++
++  static RefPtr<ClientOpPromise>
+   GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs,
+                   nsISerialEventTarget* aSerialEventTarget);
+ 
+   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManager)
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientManagerOpParent.cpp b/dom/clients/manager/ClientManagerOpParent.cpp
+--- a/dom/clients/manager/ClientManagerOpParent.cpp
++++ b/dom/clients/manager/ClientManagerOpParent.cpp
+@@ -49,16 +49,21 @@ ClientManagerOpParent::Init(const Client
+ {
+   switch (aArgs.type()) {
+     case ClientOpConstructorArgs::TClientMatchAllArgs:
+     {
+       DoServiceOp(&ClientManagerService::MatchAll,
+                   aArgs.get_ClientMatchAllArgs());
+       break;
+     }
++    case ClientOpConstructorArgs::TClientClaimArgs:
++    {
++      DoServiceOp(&ClientManagerService::Claim, aArgs.get_ClientClaimArgs());
++      break;
++    }
+     case ClientOpConstructorArgs::TClientGetInfoAndStateArgs:
+     {
+       DoServiceOp(&ClientManagerService::GetInfoAndState,
+                   aArgs.get_ClientGetInfoAndStateArgs());
+       break;
+     }
+     default:
+     {
+diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp
+--- a/dom/clients/manager/ClientManagerService.cpp
++++ b/dom/clients/manager/ClientManagerService.cpp
+@@ -443,16 +443,64 @@ ClientManagerService::MatchAll(const Cli
+ 
+   // Maybe finish the promise now in case we didn't find any matching clients.
+   promiseList->MaybeFinish();
+ 
+   return promiseList->GetResultPromise();
+ }
+ 
+ RefPtr<ClientOpPromise>
++ClientManagerService::Claim(const ClientClaimArgs& aArgs)
++{
++  AssertIsOnBackgroundThread();
++
++  const IPCServiceWorkerDescriptor& serviceWorker = aArgs.serviceWorker();
++  const PrincipalInfo& principalInfo = serviceWorker.principalInfo();
++
++  RefPtr<PromiseListHolder> promiseList = new PromiseListHolder();
++
++  for (auto iter = mSourceTable.Iter(); !iter.Done(); iter.Next()) {
++    ClientSourceParent* source = iter.UserData();
++    MOZ_DIAGNOSTIC_ASSERT(source);
++
++    if (source->IsFrozen()) {
++      continue;
++    }
++
++    if (!MatchPrincipalInfo(source->Info().PrincipalInfo(), principalInfo)) {
++      continue;
++    }
++
++    const Maybe<ServiceWorkerDescriptor>& controller = source->GetController();
++    if (controller.isSome() &&
++        controller.ref().Scope() == serviceWorker.scope() &&
++        controller.ref().Id() == serviceWorker.id()) {
++      continue;
++    }
++
++    // TODO: This logic to determine if a service worker should control
++    //       a particular client should be moved to the ServiceWorkerManager.
++    //       This can't happen until the SWM is moved to the parent process,
++    //       though.
++    if (!source->ExecutionReady() ||
++        source->Info().Type() == ClientType::Serviceworker ||
++        source->Info().URL().Find(serviceWorker.scope()) != 0) {
++      continue;
++    }
++
++    promiseList->AddPromise(source->StartOp(aArgs));
++  }
++
++  // Maybe finish the promise now in case we didn't find any matching clients.
++  promiseList->MaybeFinish();
++
++  return promiseList->GetResultPromise();
++}
++
++RefPtr<ClientOpPromise>
+ ClientManagerService::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs)
+ {
+   RefPtr<ClientOpPromise> ref;
+ 
+   ClientSourceParent* source = FindSource(aArgs.id(), aArgs.principalInfo());
+   if (!source || !source->ExecutionReady()) {
+     ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+     return ref.forget();
+diff --git a/dom/clients/manager/ClientManagerService.h b/dom/clients/manager/ClientManagerService.h
+--- a/dom/clients/manager/ClientManagerService.h
++++ b/dom/clients/manager/ClientManagerService.h
+@@ -58,16 +58,19 @@ public:
+ 
+   void
+   RemoveManager(ClientManagerParent* aManager);
+ 
+   RefPtr<ClientOpPromise>
+   MatchAll(const ClientMatchAllArgs& aArgs);
+ 
+   RefPtr<ClientOpPromise>
++  Claim(const ClientClaimArgs& aArgs);
++
++  RefPtr<ClientOpPromise>
+   GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs);
+ 
+   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManagerService)
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -357,16 +357,27 @@ ClientSource::Control(const ClientContro
+ 
+ const Maybe<ServiceWorkerDescriptor>&
+ ClientSource::GetController() const
+ {
+   return mController;
+ }
+ 
+ RefPtr<ClientOpPromise>
++ClientSource::Claim(const ClientClaimArgs& aArgs)
++{
++  SetController(ServiceWorkerDescriptor(aArgs.serviceWorker()));
++
++  RefPtr<ClientOpPromise> ref =
++    ClientOpPromise::CreateAndResolve(NS_OK, __func__);
++
++  return ref.forget();
++}
++
++RefPtr<ClientOpPromise>
+ ClientSource::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs)
+ {
+   RefPtr<ClientOpPromise> ref;
+ 
+   ClientState state;
+   nsresult rv = SnapshotState(&state);
+   if (NS_FAILED(rv)) {
+     ref = ClientOpPromise::CreateAndReject(rv, __func__);
+diff --git a/dom/clients/manager/ClientSource.h b/dom/clients/manager/ClientSource.h
+--- a/dom/clients/manager/ClientSource.h
++++ b/dom/clients/manager/ClientSource.h
+@@ -14,16 +14,17 @@
+ 
+ class nsIDocShell;
+ class nsISerialEventTarget;
+ class nsPIDOMWindowInner;
+ 
+ namespace mozilla {
+ namespace dom {
+ 
++class ClientClaimArgs;
+ class ClientControlledArgs;
+ class ClientManager;
+ class ClientSourceChild;
+ class ClientSourceConstructorArgs;
+ class ClientSourceExecutionReadyArgs;
+ class PClientManagerChild;
+ 
+ namespace workers {
+@@ -127,16 +128,19 @@ public:
+   Control(const ClientControlledArgs& aArgs);
+ 
+   // Get the ClientSource's current controlling service worker, if one has
+   // been set.
+   const Maybe<ServiceWorkerDescriptor>&
+   GetController() const;
+ 
+   RefPtr<ClientOpPromise>
++  Claim(const ClientClaimArgs& aArgs);
++
++  RefPtr<ClientOpPromise>
+   GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs);
+ 
+   nsresult
+   SnapshotState(ClientState* aStateOut);
+ 
+   nsISerialEventTarget*
+   EventTarget() const;
+ 
+diff --git a/dom/clients/manager/ClientSourceOpChild.cpp b/dom/clients/manager/ClientSourceOpChild.cpp
+--- a/dom/clients/manager/ClientSourceOpChild.cpp
++++ b/dom/clients/manager/ClientSourceOpChild.cpp
+@@ -74,16 +74,21 @@ void
+ ClientSourceOpChild::Init(const ClientOpConstructorArgs& aArgs)
+ {
+   switch (aArgs.type()) {
+     case ClientOpConstructorArgs::TClientControlledArgs:
+     {
+       DoSourceOp(&ClientSource::Control, aArgs.get_ClientControlledArgs());
+       break;
+     }
++    case ClientOpConstructorArgs::TClientClaimArgs:
++    {
++      DoSourceOp(&ClientSource::Claim, aArgs.get_ClientClaimArgs());
++      break;
++    }
+     case ClientOpConstructorArgs::TClientGetInfoAndStateArgs:
+     {
+       DoSourceOp(&ClientSource::GetInfoAndState,
+                  aArgs.get_ClientGetInfoAndStateArgs());
+       break;
+     }
+     default:
+     {

+ 746 - 0
mozilla-release/patches/1424338-3-59a1.patch

@@ -0,0 +1,746 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512762402 18000
+# Node ID e6225232364ee363cf97b2a981eaab94d8bccc27
+# Parent  32634176d49e9fb9b0d0dec0cef3f369be3fb23c
+Bug 1424338 P3 Add ClientManager::Navigate() method. r=baku
+
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -14,16 +14,17 @@
+ #include "Navigator.h"
+ #include "nsContentSecurityManager.h"
+ #include "nsScreen.h"
+ #include "nsHistory.h"
+ #include "nsDOMNavigationTiming.h"
+ #include "nsIDOMStorageManager.h"
+ #include "mozilla/dom/ClientManager.h"
+ #include "mozilla/dom/ClientSource.h"
++#include "mozilla/dom/ClientState.h"
+ #include "mozilla/dom/LocalStorage.h"
+ #include "mozilla/dom/Storage.h"
+ #include "mozilla/dom/IdleRequest.h"
+ #include "mozilla/dom/Performance.h"
+ #include "mozilla/dom/StorageEvent.h"
+ #include "mozilla/dom/StorageEventBinding.h"
+ #include "mozilla/dom/StorageNotifierService.h"
+ #include "mozilla/dom/StorageUtils.h"
+@@ -4422,16 +4423,22 @@ nsPIDOMWindowInner::SyncStateFromParentW
+ }
+ 
+ Maybe<ClientInfo>
+ nsPIDOMWindowInner::GetClientInfo() const
+ {
+   return Move(nsGlobalWindow::Cast(this)->GetClientInfo());
+ }
+ 
++Maybe<ClientState>
++nsPIDOMWindowInner::GetClientState() const
++{
++  return Move(nsGlobalWindow::Cast(this)->GetClientState());
++}
++
+ Maybe<ServiceWorkerDescriptor>
+ nsPIDOMWindowInner::GetController() const
+ {
+   return Move(nsGlobalWindow::Cast(this)->GetController());
+ }
+ 
+ void
+ nsGlobalWindow::UpdateTopInnerWindow()
+@@ -12621,16 +12628,31 @@ nsGlobalWindow::GetClientInfo() const
+   MOZ_ASSERT(NS_IsMainThread());
+   Maybe<ClientInfo> clientInfo;
+   if (mClientSource) {
+     clientInfo.emplace(mClientSource->Info());
+   }
+   return Move(clientInfo);
+ }
+ 
++Maybe<ClientState>
++nsGlobalWindow::GetClientState() const
++{
++  MOZ_ASSERT(NS_IsMainThread());
++  Maybe<ClientState> clientState;
++  if (mClientSource) {
++    ClientState state;
++    nsresult rv = mClientSource->SnapshotState(&state);
++    if (NS_SUCCEEDED(rv)) {
++      clientState.emplace(state);
++    }
++  }
++  return Move(clientState);
++}
++
+ Maybe<ServiceWorkerDescriptor>
+ nsGlobalWindow::GetController() const
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   Maybe<ServiceWorkerDescriptor> controller;
+   if (mClientSource) {
+     controller = mClientSource->GetController();
+   }
+diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h
+--- a/dom/base/nsGlobalWindow.h
++++ b/dom/base/nsGlobalWindow.h
+@@ -399,16 +399,17 @@ public:
+   void Resume();
+   virtual bool IsSuspended() const override;
+   void Freeze();
+   void Thaw();
+   virtual bool IsFrozen() const override;
+   void SyncStateFromParentWindow();
+ 
+   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
++  mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
+   mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
+ 
+   virtual nsresult FireDelayedDOMEvents() override;
+ 
+   // Outer windows only.
+   virtual bool WouldReuseInnerWindow(nsIDocument* aNewDocument) override;
+ 
+   virtual void SetDocShell(nsIDocShell* aDocShell) override;
+diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h
+--- a/dom/base/nsPIDOMWindow.h
++++ b/dom/base/nsPIDOMWindow.h
+@@ -41,16 +41,17 @@ class nsXBLPrototypeHandler;
+ 
+ typedef uint32_t SuspendTypes;
+ 
+ namespace mozilla {
+ class ThrottledEventQueue;
+ namespace dom {
+ class AudioContext;
+ class ClientInfo;
++class ClientState;
+ class DocGroup;
+ class TabGroup;
+ class Element;
+ class Performance;
+ class ServiceWorkerDescriptor;
+ class ServiceWorkerRegistration;
+ class Timeout;
+ class TimeoutManager;
+@@ -933,16 +934,17 @@ public:
+   // Increase/Decrease the number of open WebSockets.
+   void UpdateWebSocketCount(int32_t aDelta);
+ 
+   // Return true if there are any open WebSockets that could block
+   // timeout-throttling.
+   bool HasOpenWebSockets() const;
+ 
+   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
++  mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
+   mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
+ 
+ protected:
+   void CreatePerformanceObjectIfNeeded();
+ };
+ 
+ NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
+ 
+diff --git a/dom/clients/manager/ClientIPCTypes.ipdlh b/dom/clients/manager/ClientIPCTypes.ipdlh
+--- a/dom/clients/manager/ClientIPCTypes.ipdlh
++++ b/dom/clients/manager/ClientIPCTypes.ipdlh
+@@ -62,16 +62,23 @@ struct ClientSourceExecutionReadyArgs
+   FrameType frameType;
+ };
+ 
+ struct ClientControlledArgs
+ {
+   IPCServiceWorkerDescriptor serviceWorker;
+ };
+ 
++struct ClientNavigateArgs
++{
++  IPCClientInfo target;
++  nsCString url;
++  nsCString baseURL;
++};
++
+ union ClientEndPoint
+ {
+   IPCClientInfo;
+   IPCServiceWorkerDescriptor;
+ };
+ 
+ struct ClientMatchAllArgs
+ {
+@@ -93,28 +100,32 @@ struct ClientGetInfoAndStateArgs
+ 
+ struct ClientOpenWindowArgs
+ {
+ };
+ 
+ union ClientOpConstructorArgs
+ {
+   ClientControlledArgs;
++  ClientNavigateArgs;
+   ClientMatchAllArgs;
+   ClientClaimArgs;
+   ClientGetInfoAndStateArgs;
+ };
+ 
+ struct ClientList
+ {
+   ClientInfoAndState[] values;
+ };
+ 
+ struct ClientNavigateOpConstructorArgs
+ {
++  PClientSource target;
++  nsCString url;
++  nsCString baseURL;
+ };
+ 
+ union ClientOpResult
+ {
+   nsresult;
+   ClientInfoAndState;
+   ClientList;
+ };
+diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientManager.cpp
+--- a/dom/clients/manager/ClientManager.cpp
++++ b/dom/clients/manager/ClientManager.cpp
+@@ -279,10 +279,19 @@ ClientManager::Claim(const ClientClaimAr
+ RefPtr<ClientOpPromise>
+ ClientManager::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs,
+                                nsISerialEventTarget* aSerialEventTarget)
+ {
+   RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
+   return mgr->StartOp(aArgs, aSerialEventTarget);
+ }
+ 
++// static
++RefPtr<ClientOpPromise>
++ClientManager::Navigate(const ClientNavigateArgs& aArgs,
++                        nsISerialEventTarget* aSerialEventTarget)
++{
++  RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
++  return mgr->StartOp(aArgs, aSerialEventTarget);
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientManager.h b/dom/clients/manager/ClientManager.h
+--- a/dom/clients/manager/ClientManager.h
++++ b/dom/clients/manager/ClientManager.h
+@@ -19,16 +19,17 @@ class PrincipalInfo;
+ namespace dom {
+ 
+ class ClientClaimArgs;
+ class ClientGetInfoAndStateArgs;
+ class ClientHandle;
+ class ClientInfo;
+ class ClientManagerChild;
+ class ClientMatchAllArgs;
++class ClientNavigateArgs;
+ class ClientOpConstructorArgs;
+ class ClientSource;
+ enum class ClientType : uint8_t;
+ 
+ namespace workers {
+ class WorkerPrivate;
+ } // workers namespace
+ 
+@@ -101,15 +102,19 @@ public:
+ 
+   static RefPtr<ClientOpPromise>
+   Claim(const ClientClaimArgs& aArgs, nsISerialEventTarget* aSerialEventTarget);
+ 
+   static RefPtr<ClientOpPromise>
+   GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs,
+                   nsISerialEventTarget* aSerialEventTarget);
+ 
++  static RefPtr<ClientOpPromise>
++  Navigate(const ClientNavigateArgs& aArgs,
++           nsISerialEventTarget* aSerialEventTarget);
++
+   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManager)
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientManager_h
+diff --git a/dom/clients/manager/ClientManagerOpParent.cpp b/dom/clients/manager/ClientManagerOpParent.cpp
+--- a/dom/clients/manager/ClientManagerOpParent.cpp
++++ b/dom/clients/manager/ClientManagerOpParent.cpp
+@@ -43,16 +43,22 @@ ClientManagerOpParent::ClientManagerOpPa
+ {
+   MOZ_DIAGNOSTIC_ASSERT(mService);
+ }
+ 
+ void
+ ClientManagerOpParent::Init(const ClientOpConstructorArgs& aArgs)
+ {
+   switch (aArgs.type()) {
++    case ClientOpConstructorArgs::TClientNavigateArgs:
++    {
++      DoServiceOp(&ClientManagerService::Navigate,
++                  aArgs.get_ClientNavigateArgs());
++      break;
++    }
+     case ClientOpConstructorArgs::TClientMatchAllArgs:
+     {
+       DoServiceOp(&ClientManagerService::MatchAll,
+                   aArgs.get_ClientMatchAllArgs());
+       break;
+     }
+     case ClientOpConstructorArgs::TClientClaimArgs:
+     {
+diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp
+--- a/dom/clients/manager/ClientManagerService.cpp
++++ b/dom/clients/manager/ClientManagerService.cpp
+@@ -2,16 +2,17 @@
+ /* 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 "ClientManagerService.h"
+ 
+ #include "ClientManagerParent.h"
++#include "ClientNavigateOpParent.h"
+ #include "ClientSourceParent.h"
+ #include "mozilla/ipc/BackgroundParent.h"
+ #include "mozilla/ipc/PBackgroundSharedTypes.h"
+ #include "mozilla/ClearOnShutdown.h"
+ #include "mozilla/SystemGroup.h"
+ #include "nsIAsyncShutdown.h"
+ 
+ namespace mozilla {
+@@ -297,16 +298,57 @@ void
+ ClientManagerService::RemoveManager(ClientManagerParent* aManager)
+ {
+   AssertIsOnBackgroundThread();
+   MOZ_DIAGNOSTIC_ASSERT(aManager);
+   DebugOnly<bool> removed = mManagerList.RemoveElement(aManager);
+   MOZ_ASSERT(removed);
+ }
+ 
++RefPtr<ClientOpPromise>
++ClientManagerService::Navigate(const ClientNavigateArgs& aArgs)
++{
++  RefPtr<ClientOpPromise> ref;
++
++  ClientSourceParent* source = FindSource(aArgs.target().id(),
++                                          aArgs.target().principalInfo());
++  if (!source) {
++    ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
++    return ref.forget();
++  }
++
++  PClientManagerParent* manager = source->Manager();
++  MOZ_DIAGNOSTIC_ASSERT(manager);
++
++  ClientNavigateOpConstructorArgs args;
++  args.url() = aArgs.url();
++  args.baseURL() = aArgs.baseURL();
++
++  // This is safe to do because the ClientSourceChild cannot directly delete
++  // itself.  Instead it sends a Teardown message to the parent which then
++  // calls delete.  That means we can be sure that we are not racing with
++  // source destruction here.
++  args.targetParent() = source;
++
++  RefPtr<ClientOpPromise::Private> promise =
++    new ClientOpPromise::Private(__func__);
++
++  ClientNavigateOpParent* op = new ClientNavigateOpParent(args, promise);
++  PClientNavigateOpParent* result =
++    manager->SendPClientNavigateOpConstructor(op, args);
++  if (!result) {
++    promise->Reject(NS_ERROR_FAILURE, __func__);
++    ref = promise;
++    return ref.forget();
++  }
++
++  ref = promise;
++  return ref.forget();
++}
++
+ namespace
+ {
+ 
+ class PromiseListHolder final
+ {
+   RefPtr<ClientOpPromise::Private> mResultPromise;
+   nsTArray<RefPtr<ClientOpPromise>> mPromiseList;
+   nsTArray<ClientInfoAndState> mResultList;
+diff --git a/dom/clients/manager/ClientManagerService.h b/dom/clients/manager/ClientManagerService.h
+--- a/dom/clients/manager/ClientManagerService.h
++++ b/dom/clients/manager/ClientManagerService.h
+@@ -55,16 +55,19 @@ public:
+ 
+   void
+   AddManager(ClientManagerParent* aManager);
+ 
+   void
+   RemoveManager(ClientManagerParent* aManager);
+ 
+   RefPtr<ClientOpPromise>
++  Navigate(const ClientNavigateArgs& aArgs);
++
++  RefPtr<ClientOpPromise>
+   MatchAll(const ClientMatchAllArgs& aArgs);
+ 
+   RefPtr<ClientOpPromise>
+   Claim(const ClientClaimArgs& aArgs);
+ 
+   RefPtr<ClientOpPromise>
+   GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs);
+ 
+diff --git a/dom/clients/manager/ClientNavigateOpChild.cpp b/dom/clients/manager/ClientNavigateOpChild.cpp
+--- a/dom/clients/manager/ClientNavigateOpChild.cpp
++++ b/dom/clients/manager/ClientNavigateOpChild.cpp
+@@ -1,23 +1,290 @@
+ /* -*- 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 "ClientNavigateOpChild.h"
+ 
++#include "ClientState.h"
++#include "mozilla/Unused.h"
++#include "nsIDocShell.h"
++#include "nsIDocShellLoadInfo.h"
++#include "nsIWebNavigation.h"
++#include "nsIWebProgress.h"
++#include "nsIWebProgressListener.h"
++#include "nsNetUtil.h"
++#include "nsPIDOMWindow.h"
++
+ namespace mozilla {
+ namespace dom {
+ 
++namespace {
++
++class NavigateLoadListener final : public nsIWebProgressListener
++                                 , public nsSupportsWeakReference
++{
++  RefPtr<ClientOpPromise::Private> mPromise;
++  RefPtr<nsPIDOMWindowOuter> mOuterWindow;
++  nsCOMPtr<nsIURI> mBaseURL;
++
++  ~NavigateLoadListener() = default;
++
++public:
++  NavigateLoadListener(ClientOpPromise::Private* aPromise,
++                       nsPIDOMWindowOuter* aOuterWindow,
++                       nsIURI* aBaseURL)
++    : mPromise(aPromise)
++    , mOuterWindow(aOuterWindow)
++    , mBaseURL(aBaseURL)
++  {
++    MOZ_DIAGNOSTIC_ASSERT(mPromise);
++    MOZ_DIAGNOSTIC_ASSERT(mOuterWindow);
++    MOZ_DIAGNOSTIC_ASSERT(mBaseURL);
++  }
++
++  NS_IMETHOD
++  OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
++                uint32_t aStateFlags, nsresult aResult) override
++  {
++    if (!(aStateFlags & STATE_IS_DOCUMENT) ||
++        !(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) {
++      return NS_OK;
++    }
++
++    aWebProgress->RemoveProgressListener(this);
++
++    nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
++    if (!channel) {
++      mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
++      return NS_OK;
++    }
++
++    nsCOMPtr<nsIURI> channelURL;
++    nsresult rv = NS_GetFinalChannelURI(channel, getter_AddRefs(channelURL));
++    if (NS_FAILED(rv)) {
++      mPromise->Reject(rv, __func__);
++      return NS_OK;
++    }
++
++    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
++    MOZ_DIAGNOSTIC_ASSERT(ssm);
++
++    // If the resulting window is not same origin, then resolve immediately
++    // without returning any information about the new Client.  This is
++    // step 6.10 in the Client.navigate(url) spec.
++    rv = ssm->CheckSameOriginURI(mBaseURL, channelURL, false);
++    if (NS_FAILED(rv)) {
++      mPromise->Resolve(NS_OK, __func__);
++      return NS_OK;
++    }
++
++    nsPIDOMWindowInner* innerWindow = mOuterWindow->GetCurrentInnerWindow();
++    MOZ_DIAGNOSTIC_ASSERT(innerWindow);
++
++    Maybe<ClientInfo> clientInfo = innerWindow->GetClientInfo();
++    MOZ_DIAGNOSTIC_ASSERT(clientInfo.isSome());
++
++    Maybe<ClientState> clientState = innerWindow->GetClientState();
++    MOZ_DIAGNOSTIC_ASSERT(clientState.isSome());
++
++    // Otherwise, if the new window is same-origin we want to return a
++    // ClientInfoAndState object so we can provide a Client snapshot
++    // to the caller.  This is step 6.11 and 6.12 in the Client.navigate(url)
++    // spec.
++    mPromise->Resolve(ClientInfoAndState(clientInfo.ref().ToIPC(),
++                                         clientState.ref().ToIPC()), __func__);
++
++    return NS_OK;
++  }
++
++  NS_IMETHOD
++  OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
++                   int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
++                   int32_t aCurTotalProgress, int32_t aMaxTotalProgress) override
++  {
++    MOZ_CRASH("Unexpected notification.");
++    return NS_OK;
++  }
++
++  NS_IMETHOD
++  OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
++                   nsIURI* aLocation, uint32_t aFlags) override
++  {
++    MOZ_CRASH("Unexpected notification.");
++    return NS_OK;
++  }
++
++  NS_IMETHOD
++  OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
++                 nsresult aStatus, const char16_t* aMessage) override
++  {
++    MOZ_CRASH("Unexpected notification.");
++    return NS_OK;
++  }
++
++  NS_IMETHOD
++  OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
++                   uint32_t aState) override
++  {
++    MOZ_CRASH("Unexpected notification.");
++    return NS_OK;
++  }
++
++  NS_DECL_ISUPPORTS
++};
++
++NS_IMPL_ISUPPORTS(NavigateLoadListener, nsIWebProgressListener,
++                                        nsISupportsWeakReference);
++
++} // anonymous namespace
++
++already_AddRefed<ClientOpPromise>
++ClientNavigateOpChild::DoNavigate(const ClientNavigateOpConstructorArgs& aArgs)
++{
++  RefPtr<ClientOpPromise> ref;
++  nsCOMPtr<nsPIDOMWindowInner> window;
++
++  // Navigating the target client window will result in the original
++  // ClientSource being destroyed.  To avoid potential UAF mistakes
++  // we use a small scope to access the ClientSource object.  Once
++  // we have a strong reference to the window object we should not
++  // access the ClientSource again.
++  {
++    ClientSourceChild* targetActor =
++      static_cast<ClientSourceChild*>(aArgs.targetChild());
++    MOZ_DIAGNOSTIC_ASSERT(targetActor);
++
++    ClientSource* target = targetActor->GetSource();
++    if (!target) {
++      ref = ClientOpPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
++                                             __func__);
++      return ref.forget();
++    }
++
++    window = target->GetInnerWindow();
++    if (!window) {
++      ref = ClientOpPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
++                                             __func__);
++      return ref.forget();
++    }
++  }
++
++  MOZ_ASSERT(NS_IsMainThread());
++
++  mSerialEventTarget = window->EventTargetFor(TaskCategory::Other);
++
++  // In theory we could do the URL work before paying the IPC overhead
++  // cost, but in practice its easier to do it here.  The ClientHandle
++  // may be off-main-thread while this method is guaranteed to always
++  // be main thread.
++  nsCOMPtr<nsIURI> baseURL;
++  nsresult rv = NS_NewURI(getter_AddRefs(baseURL), aArgs.baseURL());
++  if (NS_FAILED(rv)) {
++    ref = ClientOpPromise::CreateAndReject(rv, __func__);
++    return ref.forget();
++  }
++
++  nsCOMPtr<nsIURI> url;
++  rv = NS_NewURI(getter_AddRefs(url), aArgs.url(), nullptr, baseURL);
++  if (NS_FAILED(rv)) {
++    ref = ClientOpPromise::CreateAndReject(rv, __func__);
++    return ref.forget();
++  }
++
++  if (url->GetSpecOrDefault().EqualsLiteral("about:blank")) {
++    ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
++    return ref.forget();
++  }
++
++  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
++  if (!doc || !doc->IsActive()) {
++    ref = ClientOpPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
++    return ref.forget();
++  }
++
++  nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
++  if (!principal) {
++    ref = ClientOpPromise::CreateAndReject(rv, __func__);
++    return ref.forget();
++  }
++
++  nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
++  nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
++  if (!docShell || !webProgress) {
++    ref = ClientOpPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
++    return ref.forget();
++  }
++
++  nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
++  rv = docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
++  if (NS_FAILED(rv)) {
++    ref = ClientOpPromise::CreateAndReject(rv, __func__);
++    return ref.forget();
++  }
++
++  loadInfo->SetTriggeringPrincipal(principal);
++  loadInfo->SetReferrerPolicy(doc->GetReferrerPolicy());
++  loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContent);
++  loadInfo->SetSourceDocShell(docShell);
++  rv = docShell->LoadURI(url, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, true);
++  if (NS_FAILED(rv)) {
++    ref = ClientOpPromise::CreateAndReject(rv, __func__);
++    return ref.forget();
++  }
++
++  RefPtr<ClientOpPromise::Private> promise =
++    new ClientOpPromise::Private(__func__);
++
++  nsCOMPtr<nsIWebProgressListener> listener =
++    new NavigateLoadListener(promise, window->GetOuterWindow(), baseURL);
++
++  rv = webProgress->AddProgressListener(listener,
++                                        nsIWebProgress::NOTIFY_STATE_DOCUMENT);
++  if (NS_FAILED(rv)) {
++    promise->Reject(rv, __func__);
++    ref = promise;
++    return ref.forget();
++  }
++
++  ref = promise.get();
++
++  ref->Then(mSerialEventTarget, __func__,
++    [listener] (const ClientOpResult& aResult) { },
++    [listener] (nsresult aResult) { });
++
++  return ref.forget();
++}
++
+ void
+ ClientNavigateOpChild::ActorDestroy(ActorDestroyReason aReason)
+ {
++  mPromiseRequestHolder.DisconnectIfExists();
+ }
+ 
+ void
+ ClientNavigateOpChild::Init(const ClientNavigateOpConstructorArgs& aArgs)
+ {
++  RefPtr<ClientOpPromise> promise = DoNavigate(aArgs);
++
++  // Normally we get the event target from the window in DoNavigate().  If a
++  // failure occurred, though, we may need to fall back to the current thread
++  // target.
++  if (!mSerialEventTarget) {
++    mSerialEventTarget = GetCurrentThreadSerialEventTarget();
++  }
++
++  // Capturing `this` is safe here since we clear the mPromiseRequestHolder in
++  // ActorDestroy.
++  promise->Then(mSerialEventTarget, __func__,
++    [this] (const ClientOpResult& aResult) {
++      mPromiseRequestHolder.Complete();
++      PClientNavigateOpChild::Send__delete__(this, aResult);
++    }, [this] (nsresult aResult) {
++      mPromiseRequestHolder.Complete();
++      PClientNavigateOpChild::Send__delete__(this, aResult);
++  })->Track(mPromiseRequestHolder);
+ }
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientNavigateOpChild.h b/dom/clients/manager/ClientNavigateOpChild.h
+--- a/dom/clients/manager/ClientNavigateOpChild.h
++++ b/dom/clients/manager/ClientNavigateOpChild.h
+@@ -2,22 +2,29 @@
+ /* 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 _mozilla_dom_ClientNavigateOpChild_h
+ #define _mozilla_dom_ClientNavigateOpChild_h
+ 
+ #include "mozilla/dom/PClientNavigateOpChild.h"
++#include "ClientOpPromise.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ class ClientNavigateOpChild final : public PClientNavigateOpChild
+ {
++  MozPromiseRequestHolder<ClientOpPromise> mPromiseRequestHolder;
++  nsCOMPtr<nsISerialEventTarget> mSerialEventTarget;
++
++  already_AddRefed<ClientOpPromise>
++  DoNavigate(const ClientNavigateOpConstructorArgs& aArgs);
++
+   // PClientNavigateOpChild interface
+   void
+   ActorDestroy(ActorDestroyReason aReason) override;
+ 
+ public:
+   ClientNavigateOpChild() = default;
+   ~ClientNavigateOpChild() = default;
+ 
+diff --git a/dom/clients/manager/ClientNavigateOpParent.cpp b/dom/clients/manager/ClientNavigateOpParent.cpp
+--- a/dom/clients/manager/ClientNavigateOpParent.cpp
++++ b/dom/clients/manager/ClientNavigateOpParent.cpp
+@@ -9,16 +9,20 @@
+ namespace mozilla {
+ namespace dom {
+ 
+ using mozilla::ipc::IPCResult;
+ 
+ void
+ ClientNavigateOpParent::ActorDestroy(ActorDestroyReason aReason)
+ {
++  if (mPromise) {
++    mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
++    mPromise = nullptr;
++  }
+ }
+ 
+ IPCResult
+ ClientNavigateOpParent::Recv__delete__(const ClientOpResult& aResult)
+ {
+   if (aResult.type() == ClientOpResult::Tnsresult &&
+       NS_FAILED(aResult.get_nsresult())) {
+     mPromise->Reject(aResult.get_nsresult(), __func__);

+ 968 - 0
mozilla-release/patches/1424338-4-59a1.patch

@@ -0,0 +1,968 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512762403 18000
+# Node ID 0e4e71faf362f2a045361b23edbd673b13a8de7a
+# Parent  05b1e9cf4c301c1d138f179e6d93f2e35643b37a
+Bug 1424338 P4 Implement ClientManager::OpenWindow(). r=baku
+
+diff --git a/dom/clients/manager/ClientIPCTypes.ipdlh b/dom/clients/manager/ClientIPCTypes.ipdlh
+--- a/dom/clients/manager/ClientIPCTypes.ipdlh
++++ b/dom/clients/manager/ClientIPCTypes.ipdlh
+@@ -95,25 +95,29 @@ struct ClientClaimArgs
+ struct ClientGetInfoAndStateArgs
+ {
+   nsID id;
+   PrincipalInfo principalInfo;
+ };
+ 
+ struct ClientOpenWindowArgs
+ {
++  PrincipalInfo principalInfo;
++  nsCString url;
++  nsCString baseURL;
+ };
+ 
+ union ClientOpConstructorArgs
+ {
+   ClientControlledArgs;
+   ClientNavigateArgs;
+   ClientMatchAllArgs;
+   ClientClaimArgs;
+   ClientGetInfoAndStateArgs;
++  ClientOpenWindowArgs;
+ };
+ 
+ struct ClientList
+ {
+   ClientInfoAndState[] values;
+ };
+ 
+ struct ClientNavigateOpConstructorArgs
+diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientManager.cpp
+--- a/dom/clients/manager/ClientManager.cpp
++++ b/dom/clients/manager/ClientManager.cpp
+@@ -288,10 +288,19 @@ ClientManager::GetInfoAndState(const Cli
+ RefPtr<ClientOpPromise>
+ ClientManager::Navigate(const ClientNavigateArgs& aArgs,
+                         nsISerialEventTarget* aSerialEventTarget)
+ {
+   RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
+   return mgr->StartOp(aArgs, aSerialEventTarget);
+ }
+ 
++// static
++RefPtr<ClientOpPromise>
++ClientManager::OpenWindow(const ClientOpenWindowArgs& aArgs,
++                          nsISerialEventTarget* aSerialEventTarget)
++{
++  RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
++  return mgr->StartOp(aArgs, aSerialEventTarget);
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientManager.h b/dom/clients/manager/ClientManager.h
+--- a/dom/clients/manager/ClientManager.h
++++ b/dom/clients/manager/ClientManager.h
+@@ -21,16 +21,17 @@ namespace dom {
+ class ClientClaimArgs;
+ class ClientGetInfoAndStateArgs;
+ class ClientHandle;
+ class ClientInfo;
+ class ClientManagerChild;
+ class ClientMatchAllArgs;
+ class ClientNavigateArgs;
+ class ClientOpConstructorArgs;
++class ClientOpenWindowArgs;
+ class ClientSource;
+ enum class ClientType : uint8_t;
+ 
+ namespace workers {
+ class WorkerPrivate;
+ } // workers namespace
+ 
+ // The ClientManager provides a per-thread singleton interface workering
+@@ -106,15 +107,19 @@ public:
+   static RefPtr<ClientOpPromise>
+   GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs,
+                   nsISerialEventTarget* aSerialEventTarget);
+ 
+   static RefPtr<ClientOpPromise>
+   Navigate(const ClientNavigateArgs& aArgs,
+            nsISerialEventTarget* aSerialEventTarget);
+ 
++  static RefPtr<ClientOpPromise>
++  OpenWindow(const ClientOpenWindowArgs& aArgs,
++             nsISerialEventTarget* aSerialEventTarget);
++
+   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManager)
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientManager_h
+diff --git a/dom/clients/manager/ClientManagerOpParent.cpp b/dom/clients/manager/ClientManagerOpParent.cpp
+--- a/dom/clients/manager/ClientManagerOpParent.cpp
++++ b/dom/clients/manager/ClientManagerOpParent.cpp
+@@ -2,20 +2,23 @@
+ /* 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 "ClientManagerOpParent.h"
+ 
+ #include "ClientManagerService.h"
++#include "mozilla/ipc/BackgroundParent.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
++using mozilla::ipc::BackgroundParent;
++
+ template <typename Method, typename... Args>
+ void
+ ClientManagerOpParent::DoServiceOp(Method aMethod, Args&&... aArgs)
+ {
+   // Note, we need perfect forarding of the template type in order
+   // to allow already_AddRefed<> to be passed as an arg.
+   RefPtr<ClientOpPromise> p = (mService->*aMethod)(Forward<Args>(aArgs)...);
+ 
+@@ -66,16 +69,24 @@ ClientManagerOpParent::Init(const Client
+       break;
+     }
+     case ClientOpConstructorArgs::TClientGetInfoAndStateArgs:
+     {
+       DoServiceOp(&ClientManagerService::GetInfoAndState,
+                   aArgs.get_ClientGetInfoAndStateArgs());
+       break;
+     }
++    case ClientOpConstructorArgs::TClientOpenWindowArgs:
++    {
++      RefPtr<ContentParent> contentParent =
++        BackgroundParent::GetContentParent(Manager()->Manager());
++      DoServiceOp(&ClientManagerService::OpenWindow,
++                  aArgs.get_ClientOpenWindowArgs(), contentParent.forget());
++      break;
++    }
+     default:
+     {
+       MOZ_ASSERT_UNREACHABLE("Unknown Client operation!");
+       break;
+     }
+   }
+ }
+ 
+diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp
+--- a/dom/clients/manager/ClientManagerService.cpp
++++ b/dom/clients/manager/ClientManagerService.cpp
+@@ -3,22 +3,27 @@
+ /* 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 "ClientManagerService.h"
+ 
+ #include "ClientManagerParent.h"
+ #include "ClientNavigateOpParent.h"
++#include "ClientOpenWindowOpParent.h"
++#include "ClientOpenWindowUtils.h"
+ #include "ClientSourceParent.h"
++#include "mozilla/dom/ContentParent.h"
+ #include "mozilla/ipc/BackgroundParent.h"
+ #include "mozilla/ipc/PBackgroundSharedTypes.h"
+ #include "mozilla/ClearOnShutdown.h"
+ #include "mozilla/SystemGroup.h"
+ #include "nsIAsyncShutdown.h"
++#include "nsIXULRuntime.h"
++#include "nsProxyRelease.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ using mozilla::ipc::AssertIsOnBackgroundThread;
+ using mozilla::ipc::ContentPrincipalInfo;
+ using mozilla::ipc::PrincipalInfo;
+ 
+@@ -546,10 +551,95 @@ ClientManagerService::GetInfoAndState(co
+   if (!source || !source->ExecutionReady()) {
+     ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+     return ref.forget();
+   }
+ 
+   return source->StartOp(aArgs);
+ }
+ 
++namespace {
++
++class OpenWindowRunnable final : public Runnable
++{
++  RefPtr<ClientOpPromise::Private> mPromise;
++  const ClientOpenWindowArgs mArgs;
++  RefPtr<ContentParent> mSourceProcess;
++
++  ~OpenWindowRunnable()
++  {
++    NS_ReleaseOnMainThreadSystemGroup(mSourceProcess.forget());
++  }
++
++public:
++  OpenWindowRunnable(ClientOpPromise::Private* aPromise,
++                     const ClientOpenWindowArgs& aArgs,
++                     already_AddRefed<ContentParent> aSourceProcess)
++    : Runnable("ClientManagerService::OpenWindowRunnable")
++    , mPromise(aPromise)
++    , mArgs(aArgs)
++    , mSourceProcess(aSourceProcess)
++  {
++    MOZ_DIAGNOSTIC_ASSERT(mPromise);
++  }
++
++  NS_IMETHOD
++  Run() override
++  {
++    MOZ_ASSERT(NS_IsMainThread());
++
++    if (!BrowserTabsRemoteAutostart()) {
++      RefPtr<ClientOpPromise> p = ClientOpenWindowInCurrentProcess(mArgs);
++      p->ChainTo(mPromise.forget(), __func__);
++      return NS_OK;
++    }
++
++    RefPtr<ContentParent> targetProcess;
++
++    // Possibly try to open the window in the same process that called
++    // openWindow().  This is a temporary compat setting until the
++    // multi-e10s service worker refactor is complete.
++    if (Preferences::GetBool("dom.clients.openwindow_favors_same_process",
++                             false)) {
++      targetProcess = mSourceProcess;
++    }
++
++    // Otherwise, use our normal remote process selection mechanism for
++    // opening the window.  This will start a process if one is not
++    // present.
++    if (!targetProcess) {
++      targetProcess =
++        ContentParent::GetNewOrUsedBrowserProcess(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE),
++                                                  ContentParent::GetInitialProcessPriority(nullptr),
++                                                  nullptr);
++    }
++
++    ClientOpenWindowOpParent* actor =
++      new ClientOpenWindowOpParent(mArgs, mPromise);
++
++    // If this fails the actor will be automatically destroyed which will
++    // reject the promise.
++    Unused << targetProcess->SendPClientOpenWindowOpConstructor(actor, mArgs);
++
++    return NS_OK;
++  }
++};
++
++} // anonymous namespace
++
++RefPtr<ClientOpPromise>
++ClientManagerService::OpenWindow(const ClientOpenWindowArgs& aArgs,
++                                 already_AddRefed<ContentParent> aSourceProcess)
++{
++  RefPtr<ClientOpPromise::Private> promise =
++    new ClientOpPromise::Private(__func__);
++
++  nsCOMPtr<nsIRunnable> r = new OpenWindowRunnable(promise, aArgs,
++                                                   Move(aSourceProcess));
++  MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other,
++                                            r.forget()));
++
++  RefPtr<ClientOpPromise> ref = promise;
++  return ref.forget();
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientManagerService.h b/dom/clients/manager/ClientManagerService.h
+--- a/dom/clients/manager/ClientManagerService.h
++++ b/dom/clients/manager/ClientManagerService.h
+@@ -66,15 +66,19 @@ public:
+   MatchAll(const ClientMatchAllArgs& aArgs);
+ 
+   RefPtr<ClientOpPromise>
+   Claim(const ClientClaimArgs& aArgs);
+ 
+   RefPtr<ClientOpPromise>
+   GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs);
+ 
++  RefPtr<ClientOpPromise>
++  OpenWindow(const ClientOpenWindowArgs& aArgs,
++             already_AddRefed<ContentParent> aSourceProcess);
++
+   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManagerService)
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientManagerService_h
+diff --git a/dom/clients/manager/ClientOpenWindowOpChild.cpp b/dom/clients/manager/ClientOpenWindowOpChild.cpp
+--- a/dom/clients/manager/ClientOpenWindowOpChild.cpp
++++ b/dom/clients/manager/ClientOpenWindowOpChild.cpp
+@@ -1,23 +1,43 @@
+ /* -*- 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 "ClientOpenWindowOpChild.h"
++#include "ClientOpenWindowUtils.h"
++#include "mozilla/SystemGroup.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
++already_AddRefed<ClientOpPromise>
++ClientOpenWindowOpChild::DoOpenWindow(const ClientOpenWindowArgs& aArgs)
++{
++  RefPtr<ClientOpPromise> ref =
++    ClientOpenWindowInCurrentProcess(aArgs);
++  return ref.forget();
++}
++
+ void
+ ClientOpenWindowOpChild::ActorDestroy(ActorDestroyReason aReason)
+ {
++  mPromiseRequestHolder.DisconnectIfExists();
+ }
+ 
+ void
+ ClientOpenWindowOpChild::Init(const ClientOpenWindowArgs& aArgs)
+ {
++  RefPtr<ClientOpPromise> promise = DoOpenWindow(aArgs);
++  promise->Then(SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
++    [this] (const ClientOpResult& aResult) {
++      mPromiseRequestHolder.Complete();
++      PClientOpenWindowOpChild::Send__delete__(this, aResult);
++    }, [this] (nsresult aResult) {
++      mPromiseRequestHolder.Complete();
++      PClientOpenWindowOpChild::Send__delete__(this, aResult);
++  })->Track(mPromiseRequestHolder);
+ }
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientOpenWindowOpChild.h b/dom/clients/manager/ClientOpenWindowOpChild.h
+--- a/dom/clients/manager/ClientOpenWindowOpChild.h
++++ b/dom/clients/manager/ClientOpenWindowOpChild.h
+@@ -2,22 +2,28 @@
+ /* vim: set ts=8 sts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ #ifndef _mozilla_dom_ClientOpenWindowOpChild_h
+ #define _mozilla_dom_ClientOpenWindowOpChild_h
+ 
+ #include "mozilla/dom/PClientOpenWindowOpChild.h"
++#include "ClientOpPromise.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ class ClientOpenWindowOpChild final : public PClientOpenWindowOpChild
+ {
++  MozPromiseRequestHolder<ClientOpPromise> mPromiseRequestHolder;
++
++  already_AddRefed<ClientOpPromise>
++  DoOpenWindow(const ClientOpenWindowArgs& aArgs);
++
+   // PClientOpenWindowOpChild interface
+   void
+   ActorDestroy(ActorDestroyReason aReason) override;
+ 
+ public:
+   ClientOpenWindowOpChild() = default;
+   ~ClientOpenWindowOpChild() = default;
+ 
+diff --git a/dom/clients/manager/ClientOpenWindowUtils.cpp b/dom/clients/manager/ClientOpenWindowUtils.cpp
+new file mode 100644
+--- /dev/null
++++ b/dom/clients/manager/ClientOpenWindowUtils.cpp
+@@ -0,0 +1,463 @@
++/* -*- 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 "ClientOpenWindowUtils.h"
++
++#include "ClientInfo.h"
++#include "ClientState.h"
++#include "nsContentUtils.h"
++#include "nsIBrowserDOMWindow.h"
++#include "nsIDocShell.h"
++#include "nsIDOMChromeWindow.h"
++#include "nsIURI.h"
++#include "nsIWebProgress.h"
++#include "nsIWebProgressListener.h"
++#include "nsIWindowWatcher.h"
++#include "nsNetUtil.h"
++#include "nsPIDOMWindow.h"
++#include "nsPIWindowWatcher.h"
++
++#ifdef MOZ_WIDGET_ANDROID
++#include "FennecJNIWrappers.h"
++#endif
++
++namespace mozilla {
++namespace dom {
++
++namespace {
++
++class WebProgressListener final : public nsIWebProgressListener
++                                , public nsSupportsWeakReference
++{
++public:
++  NS_DECL_ISUPPORTS
++
++  WebProgressListener(nsPIDOMWindowOuter* aWindow,
++                      nsIURI* aBaseURI,
++                      already_AddRefed<ClientOpPromise::Private> aPromise)
++  : mPromise(aPromise)
++  , mWindow(aWindow)
++  , mBaseURI(aBaseURI)
++  {
++    MOZ_ASSERT(aWindow);
++    MOZ_ASSERT(aBaseURI);
++    MOZ_ASSERT(NS_IsMainThread());
++  }
++
++  NS_IMETHOD
++  OnStateChange(nsIWebProgress* aWebProgress,
++                nsIRequest* aRequest,
++                uint32_t aStateFlags, nsresult aStatus) override
++  {
++    if (!(aStateFlags & STATE_IS_DOCUMENT) ||
++         !(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) {
++      return NS_OK;
++    }
++
++    // Our caller keeps a strong reference, so it is safe to remove the listener
++    // from ServiceWorkerPrivate.
++    aWebProgress->RemoveProgressListener(this);
++
++    nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
++    if (NS_WARN_IF(!doc)) {
++      mPromise->Reject(NS_ERROR_FAILURE, __func__);
++      mPromise = nullptr;
++      return NS_OK;
++    }
++
++    // Check same origin.
++    nsCOMPtr<nsIScriptSecurityManager> securityManager =
++      nsContentUtils::GetSecurityManager();
++    nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(),
++                                                      mBaseURI, false);
++    if (NS_FAILED(rv)) {
++      mPromise->Resolve(NS_OK, __func__);
++      mPromise = nullptr;
++      return NS_OK;
++    }
++
++    nsPIDOMWindowInner* innerWindow = doc->GetInnerWindow();
++    if (NS_WARN_IF(!innerWindow)) {
++      mPromise->Reject(NS_ERROR_FAILURE, __func__);
++      mPromise = nullptr;
++      return NS_OK;
++    }
++
++    Maybe<ClientInfo> info = innerWindow->GetClientInfo();
++    Maybe<ClientState> state = innerWindow->GetClientState();
++
++    if (NS_WARN_IF(info.isNothing() || state.isNothing())) {
++      mPromise->Reject(NS_ERROR_FAILURE, __func__);
++      mPromise = nullptr;
++      return NS_OK;
++    }
++
++    mPromise->Resolve(ClientInfoAndState(info.ref().ToIPC(), state.ref().ToIPC()),
++                      __func__);
++    mPromise = nullptr;
++
++    return NS_OK;
++  }
++
++  NS_IMETHOD
++  OnProgressChange(nsIWebProgress* aWebProgress,
++                   nsIRequest* aRequest,
++                   int32_t aCurSelfProgress,
++                   int32_t aMaxSelfProgress,
++                   int32_t aCurTotalProgress,
++                   int32_t aMaxTotalProgress) override
++  {
++    MOZ_ASSERT(false, "Unexpected notification.");
++    return NS_OK;
++  }
++
++  NS_IMETHOD
++  OnLocationChange(nsIWebProgress* aWebProgress,
++                   nsIRequest* aRequest,
++                   nsIURI* aLocation,
++                   uint32_t aFlags) override
++  {
++    MOZ_ASSERT(false, "Unexpected notification.");
++    return NS_OK;
++  }
++
++  NS_IMETHOD
++  OnStatusChange(nsIWebProgress* aWebProgress,
++                 nsIRequest* aRequest,
++                 nsresult aStatus, const char16_t* aMessage) override
++  {
++    MOZ_ASSERT(false, "Unexpected notification.");
++    return NS_OK;
++  }
++
++  NS_IMETHOD
++  OnSecurityChange(nsIWebProgress* aWebProgress,
++                   nsIRequest* aRequest,
++                   uint32_t aState) override
++  {
++    MOZ_ASSERT(false, "Unexpected notification.");
++    return NS_OK;
++  }
++
++private:
++  ~WebProgressListener()
++  {
++    if (mPromise) {
++      mPromise->Reject(NS_ERROR_ABORT, __func__);
++      mPromise = nullptr;
++    }
++  }
++
++  RefPtr<ClientOpPromise::Private> mPromise;
++  // TODO: make window a weak ref and stop cycle collecting
++  nsCOMPtr<nsPIDOMWindowOuter> mWindow;
++  nsCOMPtr<nsIURI> mBaseURI;
++};
++
++NS_IMPL_ISUPPORTS(WebProgressListener, nsIWebProgressListener,
++                                       nsISupportsWeakReference);
++
++nsresult
++OpenWindow(const ClientOpenWindowArgs& aArgs,
++           nsPIDOMWindowOuter** aWindow)
++{
++  MOZ_DIAGNOSTIC_ASSERT(aWindow);
++
++  // [[1. Let url be the result of parsing url with entry settings object's API
++  //   base URL.]]
++  nsCOMPtr<nsIURI> uri;
++
++  nsCOMPtr<nsIURI> baseURI;
++  nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aArgs.baseURL());
++  if (NS_WARN_IF(NS_FAILED(rv))) {
++    return NS_ERROR_TYPE_ERR;
++  }
++
++  rv = NS_NewURI(getter_AddRefs(uri), aArgs.url(), nullptr, baseURI);
++  if (NS_WARN_IF(NS_FAILED(rv))) {
++    return NS_ERROR_TYPE_ERR;
++  }
++
++  nsCOMPtr<nsIPrincipal> principal =
++    PrincipalInfoToPrincipal(aArgs.principalInfo());
++  MOZ_DIAGNOSTIC_ASSERT(principal);
++
++  // [[6.1 Open Window]]
++  if (XRE_IsContentProcess()) {
++
++    // Let's create a sandbox in order to have a valid JSContext and correctly
++    // propagate the SubjectPrincipal.
++    AutoJSAPI jsapi;
++    jsapi.Init();
++
++    JSContext* cx = jsapi.cx();
++
++    nsIXPConnect* xpc = nsContentUtils::XPConnect();
++    MOZ_DIAGNOSTIC_ASSERT(xpc);
++
++    JS::Rooted<JSObject*> sandbox(cx);
++    rv = xpc->CreateSandbox(cx, principal, sandbox.address());
++    if (NS_WARN_IF(NS_FAILED(rv))) {
++      return NS_ERROR_TYPE_ERR;
++    }
++
++    JSAutoCompartment ac(cx, sandbox);
++
++    // ContentProcess
++    nsCOMPtr<nsIWindowWatcher> wwatch =
++      do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
++    if (NS_WARN_IF(NS_FAILED(rv))) {
++      return rv;
++    }
++    nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
++    NS_ENSURE_STATE(pwwatch);
++
++    nsCString spec;
++    rv = uri->GetSpec(spec);
++    if (NS_WARN_IF(NS_FAILED(rv))) {
++      return rv;
++    }
++
++    nsCOMPtr<mozIDOMWindowProxy> newWindow;
++    rv = pwwatch->OpenWindow2(nullptr,
++                              spec.get(),
++                              nullptr,
++                              nullptr,
++                              false, false, true, nullptr,
++                              // Not a spammy popup; we got permission, we swear!
++                              /* aIsPopupSpam = */ false,
++                              // Don't force noopener.  We're not passing in an
++                              // opener anyway, and we _do_ want the returned
++                              // window.
++                              /* aForceNoOpener = */ false,
++                              /* aLoadInfp = */ nullptr,
++                              getter_AddRefs(newWindow));
++    if (NS_WARN_IF(NS_FAILED(rv))) {
++      return rv;
++    }
++    nsCOMPtr<nsPIDOMWindowOuter> pwindow = nsPIDOMWindowOuter::From(newWindow);
++    pwindow.forget(aWindow);
++    MOZ_DIAGNOSTIC_ASSERT(*aWindow);
++    return NS_OK;
++  }
++
++  // Find the most recent browser window and open a new tab in it.
++  nsCOMPtr<nsPIDOMWindowOuter> browserWindow =
++    nsContentUtils::GetMostRecentNonPBWindow();
++  if (!browserWindow) {
++    // It is possible to be running without a browser window on Mac OS, so
++    // we need to open a new chrome window.
++    // TODO(catalinb): open new chrome window. Bug 1218080
++    return NS_ERROR_NOT_AVAILABLE;
++  }
++
++  nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(browserWindow);
++  if (NS_WARN_IF(!chromeWin)) {
++    return NS_ERROR_FAILURE;
++  }
++
++  nsCOMPtr<nsIBrowserDOMWindow> bwin;
++  chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
++
++  if (NS_WARN_IF(!bwin)) {
++    return NS_ERROR_FAILURE;
++  }
++
++  nsCOMPtr<mozIDOMWindowProxy> win;
++  rv = bwin->OpenURI(uri, nullptr,
++                     nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
++                     nsIBrowserDOMWindow::OPEN_NEW,
++                     principal,
++                     getter_AddRefs(win));
++  if (NS_WARN_IF(NS_FAILED(rv))) {
++    return rv;
++  }
++  NS_ENSURE_STATE(win);
++
++  nsCOMPtr<nsPIDOMWindowOuter> pWin = nsPIDOMWindowOuter::From(win);
++  pWin.forget(aWindow);
++  MOZ_DIAGNOSTIC_ASSERT(*aWindow);
++
++  return NS_OK;
++}
++
++void
++WaitForLoad(const ClientOpenWindowArgs& aArgs,
++            nsPIDOMWindowOuter* aOuterWindow,
++            ClientOpPromise::Private* aPromise)
++{
++  MOZ_DIAGNOSTIC_ASSERT(aOuterWindow);
++
++  RefPtr<ClientOpPromise::Private> promise = aPromise;
++
++  nsresult rv = nsContentUtils::DispatchFocusChromeEvent(aOuterWindow);
++  if (NS_WARN_IF(NS_FAILED(rv))) {
++    promise->Reject(rv, __func__);
++    return;
++  }
++
++  nsCOMPtr<nsIURI> baseURI;
++  rv = NS_NewURI(getter_AddRefs(baseURI), aArgs.baseURL());
++  if (NS_WARN_IF(NS_FAILED(rv))) {
++    promise->Reject(rv, __func__);
++    return;
++  }
++
++  nsCOMPtr<nsIDocShell> docShell = aOuterWindow->GetDocShell();
++  nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
++
++  if (NS_WARN_IF(!webProgress)) {
++    promise->Reject(NS_ERROR_FAILURE, __func__);
++    return;
++  }
++
++  RefPtr<ClientOpPromise> ref = promise;
++
++  RefPtr<WebProgressListener> listener =
++    new WebProgressListener(aOuterWindow, baseURI, promise.forget());
++
++
++  rv = webProgress->AddProgressListener(listener,
++                                        nsIWebProgress::NOTIFY_STATE_DOCUMENT);
++  if (NS_WARN_IF(NS_FAILED(rv))) {
++    promise->Reject(rv, __func__);
++    return;
++  }
++
++  // Hold the listener alive until the promise settles
++  ref->Then(aOuterWindow->EventTargetFor(TaskCategory::Other), __func__,
++    [listener] (const ClientOpResult& aResult) { },
++    [listener] (nsresult aResult) { });
++}
++
++#ifdef MOZ_WIDGET_ANDROID
++
++class LaunchObserver final : public nsIObserver
++{
++  RefPtr<GenericPromise::Private> mPromise;
++
++  LaunchObserver()
++    : mPromise(new GenericPromise::Private(__func__))
++  {
++  }
++
++  ~LaunchObserver() = default;
++
++  NS_IMETHOD
++  Observe(nsISupports* aSubject, const char* aTopic, const char16_t * aData) override
++  {
++    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
++    if (os) {
++      os->RemoveObserver(this, "BrowserChrome:Ready");
++    }
++    mPromise->Resolve(true, __func__);
++    return NS_OK;
++  }
++
++public:
++  static already_AddRefed<LaunchObserver>
++  Create()
++  {
++    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
++    if (NS_WARN_IF(!os)) {
++      return nullptr;
++    }
++
++    RefPtr<LaunchObserver> ref = new LaunchObserver();
++
++    nsresult rv = os->AddObserver(ref, "BrowserChrome:Ready", /* weakRef */ false);
++    if (NS_WARN_IF(NS_FAILED(rv))) {
++      return nullptr;
++    }
++
++    return ref.forget();
++  }
++
++  void
++  Cancel()
++  {
++    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
++    if (os) {
++      os->RemoveObserver(this, "BrowserChrome:Ready");
++    }
++    mPromise->Reject(NS_ERROR_ABORT, __func__);
++  }
++
++  GenericPromise*
++  Promise()
++  {
++    return mPromise;
++  }
++
++  NS_DECL_ISUPPORTS
++};
++
++NS_IMPL_ISUPPORTS(LaunchObserver, nsIObserver);
++
++#endif // MOZ_WIDGET_ANDROID
++
++} // anonymous namespace
++
++already_AddRefed<ClientOpPromise>
++ClientOpenWindowInCurrentProcess(const ClientOpenWindowArgs& aArgs)
++{
++  RefPtr<ClientOpPromise::Private> promise =
++    new ClientOpPromise::Private(__func__);
++  RefPtr<ClientOpPromise> ref = promise;
++
++#ifdef MOZ_WIDGET_ANDROID
++  // This fires an intent that will start launching Fennec and foreground it,
++  // if necessary.  We create an observer so that we can determine when
++  // the launch has completed.
++  RefPtr<LaunchObserver> launchObserver = LaunchObserver::Create();
++  java::GeckoApp::LaunchOrBringToFront();
++#endif // MOZ_WIDGET_ANDROID
++
++  nsCOMPtr<nsPIDOMWindowOuter> outerWindow;
++  nsresult rv = OpenWindow(aArgs, getter_AddRefs(outerWindow));
++
++#ifdef MOZ_WIDGET_ANDROID
++  // If we get the NOT_AVAILABLE error that means the browser is still
++  // launching on android.  Use the observer we created above to wait
++  // until the launch completes and then try to open the window again.
++  if (rv == NS_ERROR_NOT_AVAILABLE && launchObserver) {
++    RefPtr<GenericPromise> p = launchObserver->Promise();
++    p->Then(outerWindow->EventTargetFor(TaskCategory::Other), __func__,
++      [aArgs, promise] (bool aResult) {
++        nsCOMPtr<nsPIDOMWindowOuter> outerWindow;
++        nsresult rv = OpenWindow(aArgs, getter_AddRefs(outerWindow));
++        if (NS_WARN_IF(NS_FAILED(rv))) {
++          promise->Reject(rv, __func__);
++        }
++
++        WaitForLoad(aArgs, outerWindow, promise);
++      }, [promise] (nsresult aResult) {
++        promise->Reject(aResult, __func__);
++      });
++    return ref.forget();
++  }
++
++  // If we didn't get the NOT_AVAILABLE error then there is no need
++  // wait for the browser to launch.  Cancel the observer so that it
++  // will release.
++  if (launchObserver) {
++    launchObserver->Cancel();
++  }
++#endif // MOZ_WIDGET_ANDROID
++
++  if (NS_WARN_IF(NS_FAILED(rv))) {
++    promise->Reject(rv, __func__);
++    return ref.forget();
++  }
++
++  MOZ_DIAGNOSTIC_ASSERT(outerWindow);
++  WaitForLoad(aArgs, outerWindow, promise);
++
++  return ref.forget();
++}
++
++} // namespace dom
++} // namespace mozilla
+diff --git a/dom/clients/manager/ClientOpenWindowUtils.h b/dom/clients/manager/ClientOpenWindowUtils.h
+new file mode 100644
+--- /dev/null
++++ b/dom/clients/manager/ClientOpenWindowUtils.h
+@@ -0,0 +1,21 @@
++/* -*- 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 _mozilla_dom_ClientOpenWindowUtils_h
++#define _mozilla_dom_ClientOpenWindowUtils_h
++
++#include "ClientOpPromise.h"
++#include "mozilla/dom/ClientIPCTypes.h"
++
++namespace mozilla {
++namespace dom {
++
++already_AddRefed<ClientOpPromise>
++ClientOpenWindowInCurrentProcess(const ClientOpenWindowArgs &aArgs);
++
++} // namespace dom
++} // namespace mozilla
++
++#endif // _mozilla_dom_ClientOpenWindowUtils_h
+diff --git a/dom/clients/manager/moz.build b/dom/clients/manager/moz.build
+--- a/dom/clients/manager/moz.build
++++ b/dom/clients/manager/moz.build
+@@ -33,16 +33,17 @@ UNIFIED_SOURCES += [
+   'ClientManagerOpParent.cpp',
+   'ClientManagerParent.cpp',
+   'ClientManagerService.cpp',
+   'ClientNavigateOpChild.cpp',
+   'ClientNavigateOpParent.cpp',
+   'ClientOpenWindowOpActors.cpp',
+   'ClientOpenWindowOpChild.cpp',
+   'ClientOpenWindowOpParent.cpp',
++  'ClientOpenWindowUtils.cpp',
+   'ClientPrefs.cpp',
+   'ClientSource.cpp',
+   'ClientSourceChild.cpp',
+   'ClientSourceOpChild.cpp',
+   'ClientSourceOpParent.cpp',
+   'ClientSourceParent.cpp',
+   'ClientState.cpp',
+   'ClientValidation.cpp',
+diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h
+--- a/dom/ipc/ContentParent.h
++++ b/dom/ipc/ContentParent.h
+@@ -629,16 +629,18 @@ public:
+   void OnCompositorDeviceReset() override;
+ 
+   virtual PClientOpenWindowOpParent*
+   AllocPClientOpenWindowOpParent(const ClientOpenWindowArgs& aArgs) override;
+ 
+   virtual bool
+   DeallocPClientOpenWindowOpParent(PClientOpenWindowOpParent* aActor) override;
+ 
++  static hal::ProcessPriority GetInitialProcessPriority(Element* aFrameElement);
++
+   // Control the priority of the IPC messages for input events.
+   void SetInputPriorityEventEnabled(bool aEnabled);
+   bool IsInputPriorityEventEnabled()
+   {
+     return mIsInputPriorityEventEnabled;
+   }
+ 
+   static bool IsInputEventQueueSupported();
+@@ -662,18 +664,16 @@ private:
+    * removed from this list, but will still be in the sContentParents list for
+    * the GetAll/GetAllEvenIfDead APIs.
+    */
+   static nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>* sBrowserContentParents;
+   static nsTArray<ContentParent*>* sPrivateContent;
+   static nsDataHashtable<nsUint32HashKey, ContentParent*> *sJSPluginContentParents;
+   static StaticAutoPtr<LinkedList<ContentParent> > sContentParents;
+ 
+-  static hal::ProcessPriority GetInitialProcessPriority(Element* aFrameElement);
+-
+   static ContentBridgeParent* CreateContentBridgeParent(const TabContext& aContext,
+                                                         const hal::ProcessPriority& aPriority,
+                                                         const TabId& aOpenerTabId,
+                                                         const TabId& aTabId);
+ 
+   // Hide the raw constructor methods since we don't want client code
+   // using them.
+   virtual PBrowserParent* SendPBrowserConstructor(
+diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
+--- a/modules/libpref/init/all.js
++++ b/modules/libpref/init/all.js
+@@ -5930,16 +5930,23 @@ pref("layers.advanced.columnRule-layers"
+ pref("layers.advanced.image-layers", 2);
+ pref("layers.advanced.outline-layers", 2);
+ pref("layers.advanced.solid-color", false);
+ pref("layers.advanced.table", false);
+ 
+ // Enable lowercased response header name
+ pref("dom.xhr.lowercase_header.enabled", false);
+ 
++// Control whether clients.openWindow() opens windows in the same process
++// that called the API vs following our normal multi-process selection
++// algorithm.  Restricting openWindow to same process improves service worker
++// web compat in the short term.  Once the SW multi-e10s refactor is complete
++// this can be removed.
++pref("dom.clients.openwindow_favors_same_process", true);
++
+ // When a crash happens, whether to include heap regions of the crash context
+ // in the minidump. Enabled by default on nightly and aurora.
+ #ifdef RELEASE_OR_BETA
+ pref("toolkit.crashreporter.include_context_heap", false);
+ #else
+ pref("toolkit.crashreporter.include_context_heap", true);
+ #endif
+ 

+ 294 - 0
mozilla-release/patches/1424338-5-59a1.patch

@@ -0,0 +1,294 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512762403 18000
+# Node ID 156315ff3c4c6b48e14eba799b182519baf8dae1
+# Parent  292eec18ec99655e8e110424f0ad4dda51261fd9
+Bug 1424338 P5 Implement ClientHandle::Focus(). r=baku
+
+diff --git a/dom/clients/manager/ClientHandle.cpp b/dom/clients/manager/ClientHandle.cpp
+--- a/dom/clients/manager/ClientHandle.cpp
++++ b/dom/clients/manager/ClientHandle.cpp
+@@ -4,16 +4,17 @@
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ #include "ClientHandle.h"
+ 
+ #include "ClientHandleChild.h"
+ #include "ClientHandleOpChild.h"
+ #include "ClientManager.h"
++#include "ClientState.h"
+ #include "mozilla/dom/PClientManagerChild.h"
+ #include "mozilla/dom/ServiceWorkerDescriptor.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ ClientHandle::~ClientHandle()
+ {
+@@ -117,10 +118,29 @@ ClientHandle::Control(const ServiceWorke
+     },
+     [outerPromise](const ClientOpResult& aResult) {
+       outerPromise->Reject(aResult.get_nsresult(), __func__);
+     });
+ 
+   return outerPromise.forget();
+ }
+ 
++RefPtr<ClientStatePromise>
++ClientHandle::Focus()
++{
++  RefPtr<ClientStatePromise::Private> outerPromise =
++    new ClientStatePromise::Private(__func__);
++
++  RefPtr<ClientOpPromise> innerPromise = StartOp(ClientFocusArgs());
++
++  innerPromise->Then(mSerialEventTarget, __func__,
++    [outerPromise](const ClientOpResult& aResult) {
++      outerPromise->Resolve(ClientState::FromIPC(aResult.get_IPCClientState()), __func__);
++    }, [outerPromise](const ClientOpResult& aResult) {
++      outerPromise->Reject(aResult.get_nsresult(), __func__);
++    });
++
++  RefPtr<ClientStatePromise> ref = outerPromise.get();
++  return ref.forget();
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientHandle.h b/dom/clients/manager/ClientHandle.h
+--- a/dom/clients/manager/ClientHandle.h
++++ b/dom/clients/manager/ClientHandle.h
+@@ -65,15 +65,21 @@ public:
+   Info() const;
+ 
+   // Mark the ClientSource attached to this handle as controlled by the
+   // given service worker.  The promise will resolve true if the ClientSource
+   // is successfully marked or reject if the operation could not be completed.
+   RefPtr<GenericPromise>
+   Control(const ServiceWorkerDescriptor& aServiceWorker);
+ 
++  // Focus the Client if possible.  If successful the promise will resolve with
++  // a new ClientState snapshot after focus has completed.  If focusing fails
++  // for any reason then the promise will reject.
++  RefPtr<ClientStatePromise>
++  Focus();
++
+   NS_INLINE_DECL_REFCOUNTING(ClientHandle);
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientHandle_h
+diff --git a/dom/clients/manager/ClientIPCTypes.ipdlh b/dom/clients/manager/ClientIPCTypes.ipdlh
+--- a/dom/clients/manager/ClientIPCTypes.ipdlh
++++ b/dom/clients/manager/ClientIPCTypes.ipdlh
+@@ -62,16 +62,20 @@ struct ClientSourceExecutionReadyArgs
+   FrameType frameType;
+ };
+ 
+ struct ClientControlledArgs
+ {
+   IPCServiceWorkerDescriptor serviceWorker;
+ };
+ 
++struct ClientFocusArgs
++{
++};
++
+ struct ClientNavigateArgs
+ {
+   IPCClientInfo target;
+   nsCString url;
+   nsCString baseURL;
+ };
+ 
+ union ClientEndPoint
+@@ -103,16 +107,17 @@ struct ClientOpenWindowArgs
+   PrincipalInfo principalInfo;
+   nsCString url;
+   nsCString baseURL;
+ };
+ 
+ union ClientOpConstructorArgs
+ {
+   ClientControlledArgs;
++  ClientFocusArgs;
+   ClientNavigateArgs;
+   ClientMatchAllArgs;
+   ClientClaimArgs;
+   ClientGetInfoAndStateArgs;
+   ClientOpenWindowArgs;
+ };
+ 
+ struct ClientList
+@@ -125,14 +130,15 @@ struct ClientNavigateOpConstructorArgs
+   PClientSource target;
+   nsCString url;
+   nsCString baseURL;
+ };
+ 
+ union ClientOpResult
+ {
+   nsresult;
++  IPCClientState;
+   ClientInfoAndState;
+   ClientList;
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -8,16 +8,17 @@
+ 
+ #include "ClientManager.h"
+ #include "ClientManagerChild.h"
+ #include "ClientSourceChild.h"
+ #include "ClientState.h"
+ #include "ClientValidation.h"
+ #include "mozilla/dom/ClientIPCTypes.h"
+ #include "mozilla/dom/WorkerPrivate.h"
++#include "nsContentUtils.h"
+ #include "nsIDocShell.h"
+ #include "nsPIDOMWindow.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ using mozilla::dom::workers::WorkerPrivate;
+ using mozilla::ipc::PrincipalInfo;
+@@ -357,16 +358,65 @@ ClientSource::Control(const ClientContro
+ 
+ const Maybe<ServiceWorkerDescriptor>&
+ ClientSource::GetController() const
+ {
+   return mController;
+ }
+ 
+ RefPtr<ClientOpPromise>
++ClientSource::Focus(const ClientFocusArgs& aArgs)
++{
++  NS_ASSERT_OWNINGTHREAD(ClientSource);
++
++  RefPtr<ClientOpPromise> ref;
++
++  if (mClientInfo.Type() != ClientType::Window) {
++    ref = ClientOpPromise::CreateAndReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
++                                           __func__);
++    return ref.forget();
++  }
++  nsPIDOMWindowOuter* outer = nullptr;
++
++  nsPIDOMWindowInner* inner = GetInnerWindow();
++  if (inner) {
++    outer = inner->GetOuterWindow();
++  } else {
++    nsIDocShell* docshell = GetDocShell();
++    if (docshell) {
++      outer = docshell->GetWindow();
++    }
++  }
++
++  if (!outer) {
++    ref = ClientOpPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
++                                           __func__);
++    return ref.forget();
++  }
++
++  MOZ_ASSERT(NS_IsMainThread());
++
++  nsresult rv = nsContentUtils::DispatchFocusChromeEvent(outer);
++  if (NS_FAILED(rv)) {
++    ref = ClientOpPromise::CreateAndReject(rv, __func__);
++    return ref.forget();
++  }
++
++  ClientState state;
++  rv = SnapshotState(&state);
++  if (NS_FAILED(rv)) {
++    ref = ClientOpPromise::CreateAndReject(rv, __func__);
++    return ref.forget();
++  }
++
++  ref = ClientOpPromise::CreateAndResolve(state.ToIPC(), __func__);
++  return ref.forget();
++}
++
++RefPtr<ClientOpPromise>
+ ClientSource::Claim(const ClientClaimArgs& aArgs)
+ {
+   SetController(ServiceWorkerDescriptor(aArgs.serviceWorker()));
+ 
+   RefPtr<ClientOpPromise> ref =
+     ClientOpPromise::CreateAndResolve(NS_OK, __func__);
+ 
+   return ref.forget();
+diff --git a/dom/clients/manager/ClientSource.h b/dom/clients/manager/ClientSource.h
+--- a/dom/clients/manager/ClientSource.h
++++ b/dom/clients/manager/ClientSource.h
+@@ -16,16 +16,17 @@ class nsIDocShell;
+ class nsISerialEventTarget;
+ class nsPIDOMWindowInner;
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ class ClientClaimArgs;
+ class ClientControlledArgs;
++class ClientFocusArgs;
+ class ClientManager;
+ class ClientSourceChild;
+ class ClientSourceConstructorArgs;
+ class ClientSourceExecutionReadyArgs;
+ class PClientManagerChild;
+ 
+ namespace workers {
+ class WorkerPrivate;
+@@ -128,16 +129,19 @@ public:
+   Control(const ClientControlledArgs& aArgs);
+ 
+   // Get the ClientSource's current controlling service worker, if one has
+   // been set.
+   const Maybe<ServiceWorkerDescriptor>&
+   GetController() const;
+ 
+   RefPtr<ClientOpPromise>
++  Focus(const ClientFocusArgs& aArgs);
++
++  RefPtr<ClientOpPromise>
+   Claim(const ClientClaimArgs& aArgs);
+ 
+   RefPtr<ClientOpPromise>
+   GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs);
+ 
+   nsresult
+   SnapshotState(ClientState* aStateOut);
+ 
+diff --git a/dom/clients/manager/ClientSourceOpChild.cpp b/dom/clients/manager/ClientSourceOpChild.cpp
+--- a/dom/clients/manager/ClientSourceOpChild.cpp
++++ b/dom/clients/manager/ClientSourceOpChild.cpp
+@@ -74,16 +74,21 @@ void
+ ClientSourceOpChild::Init(const ClientOpConstructorArgs& aArgs)
+ {
+   switch (aArgs.type()) {
+     case ClientOpConstructorArgs::TClientControlledArgs:
+     {
+       DoSourceOp(&ClientSource::Control, aArgs.get_ClientControlledArgs());
+       break;
+     }
++    case ClientOpConstructorArgs::TClientFocusArgs:
++    {
++      DoSourceOp(&ClientSource::Focus, aArgs.get_ClientFocusArgs());
++      break;
++    }
+     case ClientOpConstructorArgs::TClientClaimArgs:
+     {
+       DoSourceOp(&ClientSource::Claim, aArgs.get_ClientClaimArgs());
+       break;
+     }
+     case ClientOpConstructorArgs::TClientGetInfoAndStateArgs:
+     {
+       DoSourceOp(&ClientSource::GetInfoAndState,

+ 509 - 0
mozilla-release/patches/1424338-6-59a1.patch

@@ -0,0 +1,509 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512762403 18000
+# Node ID 3d35790aaf573a84e737b336c815cde017cdb882
+# Parent  cc506d7ead55ad04984013672d139b0067c82799
+Bug 1424338 P6 Implement ClientHandle::PostMessage() r=baku
+
+diff --git a/dom/clients/manager/ClientHandle.cpp b/dom/clients/manager/ClientHandle.cpp
+--- a/dom/clients/manager/ClientHandle.cpp
++++ b/dom/clients/manager/ClientHandle.cpp
+@@ -7,20 +7,23 @@
+ #include "ClientHandle.h"
+ 
+ #include "ClientHandleChild.h"
+ #include "ClientHandleOpChild.h"
+ #include "ClientManager.h"
+ #include "ClientState.h"
+ #include "mozilla/dom/PClientManagerChild.h"
+ #include "mozilla/dom/ServiceWorkerDescriptor.h"
++#include "mozilla/dom/ipc/StructuredCloneData.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
++using mozilla::dom::ipc::StructuredCloneData;
++
+ ClientHandle::~ClientHandle()
+ {
+   Shutdown();
+ }
+ 
+ void
+ ClientHandle::Shutdown()
+ {
+@@ -137,10 +140,45 @@ ClientHandle::Focus()
+     }, [outerPromise](const ClientOpResult& aResult) {
+       outerPromise->Reject(aResult.get_nsresult(), __func__);
+     });
+ 
+   RefPtr<ClientStatePromise> ref = outerPromise.get();
+   return ref.forget();
+ }
+ 
++RefPtr<GenericPromise>
++ClientHandle::PostMessage(StructuredCloneData& aData,
++                          const ServiceWorkerDescriptor& aSource)
++{
++  RefPtr<GenericPromise> ref;
++
++  if (IsShutdown()) {
++    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
++    return ref.forget();
++  }
++
++  ClientPostMessageArgs args;
++  args.serviceWorker() = aSource.ToIPC();
++
++  if (!aData.BuildClonedMessageDataForBackgroundChild(GetActor()->Manager()->Manager(),
++                                                      args.clonedData())) {
++    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
++    return ref.forget();
++  }
++
++  RefPtr<GenericPromise::Private> outerPromise =
++    new GenericPromise::Private(__func__);
++
++  RefPtr<ClientOpPromise> innerPromise = StartOp(args);
++  innerPromise->Then(mSerialEventTarget, __func__,
++    [outerPromise](const ClientOpResult& aResult) {
++      outerPromise->Resolve(true, __func__);
++    }, [outerPromise](const ClientOpResult& aResult) {
++      outerPromise->Reject(aResult.get_nsresult(), __func__);
++    });
++
++  ref = outerPromise.get();
++  return ref.forget();
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientHandle.h b/dom/clients/manager/ClientHandle.h
+--- a/dom/clients/manager/ClientHandle.h
++++ b/dom/clients/manager/ClientHandle.h
+@@ -20,16 +20,20 @@ namespace mozilla {
+ namespace dom {
+ 
+ class ClientManager;
+ class ClientHandleChild;
+ class ClientOpConstructorArgs;
+ class PClientManagerChild;
+ class ServiceWorkerDescriptor;
+ 
++namespace ipc {
++class StructuredCloneData;
++}
++
+ // The ClientHandle allows code to take a simple ClientInfo struct and
+ // convert it into a live actor-backed object attached to a particular
+ // ClientSource somewhere in the browser.  If the ClientSource is
+ // destroyed then the ClientHandle will simply begin to reject operations.
+ // We do not currently provide a way to be notified when the ClientSource
+ // is destroyed, but this could be added in the future.
+ class ClientHandle final : public ClientThing<ClientHandleChild>
+ {
+@@ -71,15 +75,25 @@ public:
+   Control(const ServiceWorkerDescriptor& aServiceWorker);
+ 
+   // Focus the Client if possible.  If successful the promise will resolve with
+   // a new ClientState snapshot after focus has completed.  If focusing fails
+   // for any reason then the promise will reject.
+   RefPtr<ClientStatePromise>
+   Focus();
+ 
++  // Send a postMessage() call to the target Client.  Currently this only
++  // supports sending from a ServiceWorker source and the MessageEvent is
++  // dispatched to the Client's navigator.serviceWorker event target.  The
++  // returned promise will resolve if the MessageEvent is dispatched or if
++  // it triggers an error handled in the Client's context.  Other errors
++  // will result in the promise rejecting.
++  RefPtr<GenericPromise>
++  PostMessage(ipc::StructuredCloneData& aData,
++              const ServiceWorkerDescriptor& aSource);
++
+   NS_INLINE_DECL_REFCOUNTING(ClientHandle);
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientHandle_h
+diff --git a/dom/clients/manager/ClientHandleOpParent.cpp b/dom/clients/manager/ClientHandleOpParent.cpp
+--- a/dom/clients/manager/ClientHandleOpParent.cpp
++++ b/dom/clients/manager/ClientHandleOpParent.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 "ClientHandleOpParent.h"
+ 
+ #include "ClientHandleParent.h"
+ #include "ClientSourceParent.h"
++#include "mozilla/dom/PClientManagerParent.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ ClientSourceParent*
+ ClientHandleOpParent::GetSource() const
+ {
+   auto handle = static_cast<ClientHandleParent*>(Manager());
+@@ -29,17 +30,43 @@ void
+ ClientHandleOpParent::Init(const ClientOpConstructorArgs& aArgs)
+ {
+   ClientSourceParent* source = GetSource();
+   if (!source) {
+     Unused << PClientHandleOpParent::Send__delete__(this, NS_ERROR_DOM_ABORT_ERR);
+     return;
+   }
+ 
+-  RefPtr<ClientOpPromise> p = source->StartOp(aArgs);
++  RefPtr<ClientOpPromise> p;
++
++  // ClientPostMessageArgs can contain PBlob actors.  This means we
++  // can't just forward the args from one PBackground manager to
++  // another.  Instead, unpack the structured clone data and repack
++  // it into a new set of arguments.
++  if (aArgs.type() == ClientOpConstructorArgs::TClientPostMessageArgs) {
++    const ClientPostMessageArgs& orig = aArgs.get_ClientPostMessageArgs();
++
++    ClientPostMessageArgs rebuild;
++    rebuild.serviceWorker() = orig.serviceWorker();
++
++    StructuredCloneData data;
++    data.BorrowFromClonedMessageDataForBackgroundParent(orig.clonedData());
++    if (!data.BuildClonedMessageDataForBackgroundParent(source->Manager()->Manager(),
++                                                        rebuild.clonedData())) {
++      Unused << PClientHandleOpParent::Send__delete__(this, NS_ERROR_DOM_ABORT_ERR);
++      return;
++    }
++
++    p = source->StartOp(rebuild);
++  }
++
++  // Other argument types can just be forwarded straight through.
++  else {
++    p = source->StartOp(aArgs);
++  }
+ 
+   // Capturing 'this' is safe here because we disconnect the promise in
+   // ActorDestroy() which ensures neither lambda is called if the actor
+   // is destroyed before the source operation completes.
+   p->Then(GetCurrentThreadSerialEventTarget(), __func__,
+     [this] (const ClientOpResult& aResult) {
+       mPromiseRequestHolder.Complete();
+       Unused << PClientHandleOpParent::Send__delete__(this, aResult);
+diff --git a/dom/clients/manager/ClientIPCTypes.ipdlh b/dom/clients/manager/ClientIPCTypes.ipdlh
+--- a/dom/clients/manager/ClientIPCTypes.ipdlh
++++ b/dom/clients/manager/ClientIPCTypes.ipdlh
+@@ -79,16 +79,22 @@ struct ClientNavigateArgs
+ };
+ 
+ union ClientEndPoint
+ {
+   IPCClientInfo;
+   IPCServiceWorkerDescriptor;
+ };
+ 
++struct ClientPostMessageArgs
++{
++  ClonedMessageData clonedData;
++  IPCServiceWorkerDescriptor serviceWorker;
++};
++
+ struct ClientMatchAllArgs
+ {
+   ClientEndPoint endpoint;
+   ClientType type;
+   bool includeUncontrolled;
+ };
+ 
+ struct ClientClaimArgs
+@@ -109,16 +115,17 @@ struct ClientOpenWindowArgs
+   nsCString baseURL;
+ };
+ 
+ union ClientOpConstructorArgs
+ {
+   ClientControlledArgs;
+   ClientFocusArgs;
+   ClientNavigateArgs;
++  ClientPostMessageArgs;
+   ClientMatchAllArgs;
+   ClientClaimArgs;
+   ClientGetInfoAndStateArgs;
+   ClientOpenWindowArgs;
+ };
+ 
+ struct ClientList
+ {
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -7,26 +7,39 @@
+ #include "ClientSource.h"
+ 
+ #include "ClientManager.h"
+ #include "ClientManagerChild.h"
+ #include "ClientSourceChild.h"
+ #include "ClientState.h"
+ #include "ClientValidation.h"
+ #include "mozilla/dom/ClientIPCTypes.h"
++#include "mozilla/dom/ipc/StructuredCloneData.h"
++#include "mozilla/dom/MessageEvent.h"
++#include "mozilla/dom/MessageEventBinding.h"
++#include "mozilla/dom/Navigator.h"
+ #include "mozilla/dom/WorkerPrivate.h"
++#include "mozilla/dom/WorkerScope.h"
++#include "mozilla/dom/ServiceWorkerContainer.h"
++#include "mozilla/dom/workers/ServiceWorkerManager.h"
++#include "mozilla/dom/workers/bindings/ServiceWorker.h"
+ #include "nsContentUtils.h"
+ #include "nsIDocShell.h"
+ #include "nsPIDOMWindow.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
++using mozilla::dom::ipc::StructuredCloneData;
++using mozilla::dom::workers::ServiceWorkerInfo;
++using mozilla::dom::workers::ServiceWorkerManager;
++using mozilla::dom::workers::ServiceWorkerRegistrationInfo;
+ using mozilla::dom::workers::WorkerPrivate;
+ using mozilla::ipc::PrincipalInfo;
++using mozilla::ipc::PrincipalInfoToPrincipal;
+ 
+ void
+ ClientSource::Shutdown()
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientSource);
+   if (IsShutdown()) {
+     return;
+   }
+@@ -407,16 +420,138 @@ ClientSource::Focus(const ClientFocusArg
+     return ref.forget();
+   }
+ 
+   ref = ClientOpPromise::CreateAndResolve(state.ToIPC(), __func__);
+   return ref.forget();
+ }
+ 
+ RefPtr<ClientOpPromise>
++ClientSource::PostMessage(const ClientPostMessageArgs& aArgs)
++{
++  NS_ASSERT_OWNINGTHREAD(ClientSource);
++  RefPtr<ClientOpPromise> ref;
++
++  ServiceWorkerDescriptor source(aArgs.serviceWorker());
++  const PrincipalInfo& principalInfo = source.PrincipalInfo();
++
++  StructuredCloneData clonedData;
++  clonedData.BorrowFromClonedMessageDataForBackgroundChild(aArgs.clonedData());
++
++  // Currently we only support firing these messages on window Clients.
++  // Once we expose ServiceWorkerContainer and the ServiceWorker on Worker
++  // threads then this will need to change.  See bug 1113522.
++  if (mClientInfo.Type() != ClientType::Window) {
++    ref = ClientOpPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__);
++    return ref.forget();
++  }
++
++  MOZ_ASSERT(NS_IsMainThread());
++
++  RefPtr<ServiceWorkerContainer> target;
++  nsCOMPtr<nsIGlobalObject> globalObject;
++
++  // We don't need to force the creation of the about:blank document
++  // here because there is no postMessage listener.  If a listener
++  // was registered then the document will already be created.
++  nsPIDOMWindowInner* window = GetInnerWindow();
++  if (window) {
++    globalObject = do_QueryInterface(window);
++    RefPtr<Navigator> navigator =
++      static_cast<Navigator*>(window->GetNavigator());
++    if (navigator) {
++      target = navigator->ServiceWorker();
++    }
++  }
++
++  if (NS_WARN_IF(!target)) {
++    ref = ClientOpPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
++                                           __func__);
++    return ref.forget();
++  }
++
++  // If AutoJSAPI::Init() fails then either global is nullptr or not
++  // in a usable state.
++  AutoJSAPI jsapi;
++  if (!jsapi.Init(globalObject)) {
++    ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__);
++    return ref.forget();
++  }
++
++  JSContext* cx = jsapi.cx();
++
++  ErrorResult result;
++  JS::Rooted<JS::Value> messageData(cx);
++  clonedData.Read(cx, &messageData, result);
++  if (result.MaybeSetPendingException(cx)) {
++    // We reported the error in the current window context.  Resolve
++    // promise instead of rejecting.
++    ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__);
++    return ref.forget();
++  }
++
++  RootedDictionary<MessageEventInit> init(cx);
++
++  init.mData = messageData;
++  if (!clonedData.TakeTransferredPortsAsSequence(init.mPorts)) {
++    // Report the error in the current window context and resolve the
++    // promise instead of rejecting.
++    xpc::Throw(cx, NS_ERROR_OUT_OF_MEMORY);
++    ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__);
++    return ref.forget();
++  }
++
++  nsresult rv = NS_OK;
++  nsCOMPtr<nsIPrincipal> principal =
++    PrincipalInfoToPrincipal(principalInfo, &rv);
++  if (NS_FAILED(rv) || !principal) {
++    ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
++    return ref.forget();
++  }
++
++  nsAutoCString origin;
++  rv = principal->GetOriginNoSuffix(origin);
++  if (NS_SUCCEEDED(rv)) {
++    CopyUTF8toUTF16(origin, init.mOrigin);
++  }
++
++  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
++  if (!swm) {
++    // Shutting down. Just don't deliver this message.
++    ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
++    return ref.forget();
++  }
++
++  RefPtr<ServiceWorkerRegistrationInfo> reg =
++    swm->GetRegistration(principal, source.Scope());
++  if (reg) {
++    RefPtr<ServiceWorkerInfo> serviceWorker = reg->GetByID(source.Id());
++    if (serviceWorker) {
++      init.mSource.SetValue().SetAsServiceWorker() =
++        serviceWorker->GetOrCreateInstance(GetInnerWindow());
++    }
++  }
++
++  RefPtr<MessageEvent> event =
++    MessageEvent::Constructor(target, NS_LITERAL_STRING("message"), init);
++  event->SetTrusted(true);
++
++  bool preventDefaultCalled = false;
++  rv = target->DispatchEvent(static_cast<dom::Event*>(event.get()),
++                             &preventDefaultCalled);
++  if (NS_FAILED(rv)) {
++    ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
++    return ref.forget();
++  }
++
++  ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__);
++  return ref.forget();
++}
++
++RefPtr<ClientOpPromise>
+ ClientSource::Claim(const ClientClaimArgs& aArgs)
+ {
+   SetController(ServiceWorkerDescriptor(aArgs.serviceWorker()));
+ 
+   RefPtr<ClientOpPromise> ref =
+     ClientOpPromise::CreateAndResolve(NS_OK, __func__);
+ 
+   return ref.forget();
+diff --git a/dom/clients/manager/ClientSource.h b/dom/clients/manager/ClientSource.h
+--- a/dom/clients/manager/ClientSource.h
++++ b/dom/clients/manager/ClientSource.h
+@@ -7,30 +7,38 @@
+ #define _mozilla_dom_ClientSource_h
+ 
+ #include "mozilla/dom/ClientInfo.h"
+ #include "mozilla/dom/ClientOpPromise.h"
+ #include "mozilla/dom/ClientThing.h"
+ #include "mozilla/dom/ServiceWorkerDescriptor.h"
+ #include "mozilla/Variant.h"
+ 
++#ifdef XP_WIN
++#undef PostMessage
++#endif
++
+ class nsIDocShell;
+ class nsISerialEventTarget;
+ class nsPIDOMWindowInner;
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ class ClientClaimArgs;
+ class ClientControlledArgs;
+ class ClientFocusArgs;
++class ClientGetInfoAndStateArgs;
+ class ClientManager;
++class ClientPostMessageArgs;
+ class ClientSourceChild;
+ class ClientSourceConstructorArgs;
+ class ClientSourceExecutionReadyArgs;
++class ClientState;
++class ClientWindowState;
+ class PClientManagerChild;
+ 
+ namespace workers {
+ class WorkerPrivate;
+ } // workers namespace
+ 
+ // ClientSource is an RAII style class that is designed to be held via
+ // a UniquePtr<>.  When created ClientSource will register the existence
+@@ -132,16 +140,19 @@ public:
+   // been set.
+   const Maybe<ServiceWorkerDescriptor>&
+   GetController() const;
+ 
+   RefPtr<ClientOpPromise>
+   Focus(const ClientFocusArgs& aArgs);
+ 
+   RefPtr<ClientOpPromise>
++  PostMessage(const ClientPostMessageArgs& aArgs);
++
++  RefPtr<ClientOpPromise>
+   Claim(const ClientClaimArgs& aArgs);
+ 
+   RefPtr<ClientOpPromise>
+   GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs);
+ 
+   nsresult
+   SnapshotState(ClientState* aStateOut);
+ 
+diff --git a/dom/clients/manager/ClientSourceOpChild.cpp b/dom/clients/manager/ClientSourceOpChild.cpp
+--- a/dom/clients/manager/ClientSourceOpChild.cpp
++++ b/dom/clients/manager/ClientSourceOpChild.cpp
+@@ -79,16 +79,21 @@ ClientSourceOpChild::Init(const ClientOp
+       DoSourceOp(&ClientSource::Control, aArgs.get_ClientControlledArgs());
+       break;
+     }
+     case ClientOpConstructorArgs::TClientFocusArgs:
+     {
+       DoSourceOp(&ClientSource::Focus, aArgs.get_ClientFocusArgs());
+       break;
+     }
++    case ClientOpConstructorArgs::TClientPostMessageArgs:
++    {
++      DoSourceOp(&ClientSource::PostMessage, aArgs.get_ClientPostMessageArgs());
++      break;
++    }
+     case ClientOpConstructorArgs::TClientClaimArgs:
+     {
+       DoSourceOp(&ClientSource::Claim, aArgs.get_ClientClaimArgs());
+       break;
+     }
+     case ClientOpConstructorArgs::TClientGetInfoAndStateArgs:
+     {
+       DoSourceOp(&ClientSource::GetInfoAndState,

+ 105 - 0
mozilla-release/patches/1424338-7-59a1.patch

@@ -0,0 +1,105 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1512762403 18000
+# Node ID 22b5172a00a377f2cd6b2bff9920af845d602922
+# Parent  1f0aed28227e986a7705041c3928c866f271ea23
+Bug 1424338 P7 Remove the ClientEndPoint union type for now. r=baku
+
+diff --git a/dom/clients/manager/ClientIPCTypes.ipdlh b/dom/clients/manager/ClientIPCTypes.ipdlh
+--- a/dom/clients/manager/ClientIPCTypes.ipdlh
++++ b/dom/clients/manager/ClientIPCTypes.ipdlh
+@@ -73,31 +73,25 @@ struct ClientFocusArgs
+ 
+ struct ClientNavigateArgs
+ {
+   IPCClientInfo target;
+   nsCString url;
+   nsCString baseURL;
+ };
+ 
+-union ClientEndPoint
+-{
+-  IPCClientInfo;
+-  IPCServiceWorkerDescriptor;
+-};
+-
+ struct ClientPostMessageArgs
+ {
+   ClonedMessageData clonedData;
+   IPCServiceWorkerDescriptor serviceWorker;
+ };
+ 
+ struct ClientMatchAllArgs
+ {
+-  ClientEndPoint endpoint;
++  IPCServiceWorkerDescriptor serviceWorker;
+   ClientType type;
+   bool includeUncontrolled;
+ };
+ 
+ struct ClientClaimArgs
+ {
+   IPCServiceWorkerDescriptor serviceWorker;
+ };
+diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp
+--- a/dom/clients/manager/ClientManagerService.cpp
++++ b/dom/clients/manager/ClientManagerService.cpp
+@@ -432,22 +432,18 @@ public:
+ 
+ } // anonymous namespace
+ 
+ RefPtr<ClientOpPromise>
+ ClientManagerService::MatchAll(const ClientMatchAllArgs& aArgs)
+ {
+   AssertIsOnBackgroundThread();
+ 
+-  const ClientEndPoint& endpoint = aArgs.endpoint();
+-
+-  const PrincipalInfo& principalInfo =
+-    endpoint.type() == ClientEndPoint::TIPCClientInfo
+-      ? endpoint.get_IPCClientInfo().principalInfo()
+-      : endpoint.get_IPCServiceWorkerDescriptor().principalInfo();
++  ServiceWorkerDescriptor swd(aArgs.serviceWorker());
++  const PrincipalInfo& principalInfo = swd.PrincipalInfo();
+ 
+   RefPtr<PromiseListHolder> promiseList = new PromiseListHolder();
+ 
+   for (auto iter = mSourceTable.Iter(); !iter.Done(); iter.Next()) {
+     ClientSourceParent* source = iter.UserData();
+     MOZ_DIAGNOSTIC_ASSERT(source);
+ 
+     if (source->IsFrozen() || !source->ExecutionReady()) {
+@@ -459,31 +455,24 @@ ClientManagerService::MatchAll(const Cli
+       continue;
+     }
+ 
+     if (!MatchPrincipalInfo(source->Info().PrincipalInfo(), principalInfo)) {
+       continue;
+     }
+ 
+     if (!aArgs.includeUncontrolled()) {
+-      if (endpoint.type() != ClientEndPoint::TIPCServiceWorkerDescriptor) {
+-        continue;
+-      }
+-
+       const Maybe<ServiceWorkerDescriptor>& controller =
+         source->GetController();
+       if (controller.isNothing()) {
+         continue;
+       }
+ 
+-      const IPCServiceWorkerDescriptor& serviceWorker =
+-        endpoint.get_IPCServiceWorkerDescriptor();
+-
+-      if(controller.ref().Id() != serviceWorker.id() ||
+-         controller.ref().Scope() != serviceWorker.scope()) {
++      if(controller.ref().Id() != swd.Id() ||
++         controller.ref().Scope() != swd.Scope()) {
+         continue;
+       }
+     }
+ 
+     promiseList->AddPromise(
+       source->StartOp(Move(ClientGetInfoAndStateArgs(source->Info().Id(),
+                                                      source->Info().PrincipalInfo()))));
+   }

+ 395 - 0
mozilla-release/patches/1424452-59a1.patch

@@ -0,0 +1,395 @@
+# HG changeset patch
+# User Gijs Kruitbosch <gijskruitbosch@gmail.com>
+# Date 1512823040 0
+# Node ID eb65296afea1c4f65949440cea58f8018a02533f
+# Parent  e8e8c02d83451f7022d1fa36e87c8c1e739988b4
+Bug 1424452 - stop having 2 different values for the 'panel' area type, r=jaws
+
+MozReview-Commit-ID: 3spDhRhlOTF
+
+diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css
+--- a/browser/base/content/browser.css
++++ b/browser/base/content/browser.css
+@@ -340,17 +340,17 @@ toolbarpaletteitem {
+ 
+ /* Rules to help integrate SDK widgets */
+ toolbaritem[sdkstylewidget="true"] > toolbarbutton,
+ toolbarpaletteitem > toolbaritem[sdkstylewidget="true"] > iframe,
+ toolbarpaletteitem > toolbaritem[sdkstylewidget="true"] > .toolbarbutton-text {
+   display: none;
+ }
+ 
+-toolbarpaletteitem:-moz-any([place="palette"], [place="panel"]) > toolbaritem[sdkstylewidget="true"] > toolbarbutton {
++toolbarpaletteitem:-moz-any([place="palette"], [place="menu-panel"]) > toolbaritem[sdkstylewidget="true"] > toolbarbutton {
+   display: -moz-box;
+ }
+ 
+ toolbarpaletteitem > toolbaritem[sdkstylewidget="true"][cui-areatype="toolbar"] > .toolbarbutton-text {
+   display: -moz-box;
+ }
+ 
+ .webextension-browser-action > .toolbarbutton-badge-stack > .toolbarbutton-icon {
+@@ -439,17 +439,17 @@ toolbarbutton.webextension-menuitem > .t
+ 
+ toolbarpaletteitem[removable="false"] {
+   opacity: 0.5;
+   cursor: default;
+ }
+ 
+ %ifndef XP_MACOSX
+ toolbarpaletteitem[place="palette"],
+-toolbarpaletteitem[place="panel"],
++toolbarpaletteitem[place="menu-panel"],
+ toolbarpaletteitem[place="toolbar"] {
+   -moz-user-focus: normal;
+ }
+ %endif
+ 
+ #bookmarks-toolbar-placeholder,
+ toolbarpaletteitem > #personal-bookmarks > #PlacesToolbar,
+ #personal-bookmarks[cui-areatype="menu-panel"] > #PlacesToolbar,
+diff --git a/browser/components/customizableui/CustomizableUI.jsm b/browser/components/customizableui/CustomizableUI.jsm
+--- a/browser/components/customizableui/CustomizableUI.jsm
++++ b/browser/components/customizableui/CustomizableUI.jsm
+@@ -873,17 +873,17 @@ var CustomizableUIInternal = {
+   },
+ 
+   ensureButtonContextMenu(aNode, aAreaNode, forcePanel) {
+     const kPanelItemContextMenu = "customizationPanelItemContextMenu";
+ 
+     let currentContextMenu = aNode.getAttribute("context") ||
+                              aNode.getAttribute("contextmenu");
+     let contextMenuForPlace =
+-      (forcePanel || "panel" == CustomizableUI.getPlaceForItem(aAreaNode)) ?
++      (forcePanel || "menu-panel" == CustomizableUI.getPlaceForItem(aAreaNode)) ?
+       kPanelItemContextMenu :
+       null;
+     if (contextMenuForPlace && !currentContextMenu) {
+       aNode.setAttribute("context", contextMenuForPlace);
+     } else if (currentContextMenu == kPanelItemContextMenu &&
+                contextMenuForPlace != kPanelItemContextMenu) {
+       aNode.removeAttribute("context");
+       aNode.removeAttribute("contextmenu");
+@@ -3811,17 +3811,17 @@ var CustomizableUI = {
+    */
+   getPlaceForItem(aElement) {
+     let place;
+     let node = aElement;
+     while (node && !place) {
+       if (node.localName == "toolbar")
+         place = "toolbar";
+       else if (node.id == CustomizableUI.AREA_PANEL || node.id == CustomizableUI.AREA_FIXED_OVERFLOW_PANEL)
+-        place = "panel";
++        place = "menu-panel";
+       else if (node.id == "customization-palette")
+         place = "palette";
+ 
+       node = node.parentNode;
+     }
+     return place;
+   },
+ 
+diff --git a/browser/components/customizableui/CustomizeMode.jsm b/browser/components/customizableui/CustomizeMode.jsm
+--- a/browser/components/customizableui/CustomizeMode.jsm
++++ b/browser/components/customizableui/CustomizeMode.jsm
+@@ -965,17 +965,17 @@ CustomizeMode.prototype = {
+ 
+     let contextMenuAttrName = "";
+     if (aNode.getAttribute("context")) {
+       contextMenuAttrName = "context";
+     } else if (aNode.getAttribute("contextmenu")) {
+       contextMenuAttrName = "contextmenu";
+     }
+     let currentContextMenu = aNode.getAttribute(contextMenuAttrName);
+-    let contextMenuForPlace = aPlace == "panel" ?
++    let contextMenuForPlace = aPlace == "menu-panel" ?
+                                 kPanelItemContextMenu :
+                                 kPaletteItemContextMenu;
+     if (aPlace != "toolbar") {
+       wrapper.setAttribute("context", contextMenuForPlace);
+     }
+     // Only keep track of the menu if it is non-default.
+     if (currentContextMenu &&
+         currentContextMenu != contextMenuForPlace) {
+@@ -1049,17 +1049,17 @@ CustomizeMode.prototype = {
+     }
+ 
+     let wrappedContext = toolbarItem.getAttribute("wrapped-context");
+     if (wrappedContext) {
+       let contextAttrName = toolbarItem.getAttribute("wrapped-contextAttrName");
+       toolbarItem.setAttribute(contextAttrName, wrappedContext);
+       toolbarItem.removeAttribute("wrapped-contextAttrName");
+       toolbarItem.removeAttribute("wrapped-context");
+-    } else if (place == "panel") {
++    } else if (place == "menu-panel") {
+       toolbarItem.setAttribute("context", kPanelItemContextMenu);
+     }
+ 
+     if (aWrapper.parentNode) {
+       aWrapper.parentNode.replaceChild(toolbarItem, aWrapper);
+     }
+     return toolbarItem;
+   },
+@@ -1741,17 +1741,17 @@ CustomizeMode.prototype = {
+       // For automated tests, we sometimes start exiting customization mode
+       // before this fires, which leaves us with placeholders inserted after
+       // we've exited. So we need to check that we are indeed customizing.
+       if (this._customizing && !this._transitioning) {
+         item.hidden = true;
+         this._showPanelCustomizationPlaceholders();
+         DragPositionManager.start(this.window);
+         let canUsePrevSibling = placeForItem == "toolbar" ||
+-                                (placeForItem == "panel" && gPhotonStructure);
++                                (placeForItem == "menu-panel" && gPhotonStructure);
+         if (item.nextSibling) {
+           this._setDragActive(item.nextSibling, "before", draggedItem.id, placeForItem);
+           this._dragOverItem = item.nextSibling;
+         } else if (canUsePrevSibling && item.previousSibling) {
+           this._setDragActive(item.previousSibling, "after", draggedItem.id, placeForItem);
+           this._dragOverItem = item.previousSibling;
+         }
+       }
+@@ -2121,21 +2121,16 @@ CustomizeMode.prototype = {
+            mozSourceNode.ownerGlobal != this.window;
+   },
+ 
+   _setDragActive(aItem, aValue, aDraggedItemId, aAreaType) {
+     if (!aItem) {
+       return;
+     }
+ 
+-    // getPlaceForItem and getAreaType return different things. Hack-around
+-    // rather than having to update every. single. consumer. (and break add-ons)
+-    if (aAreaType == "panel") {
+-      aAreaType = "menu-panel";
+-    }
+     if (aItem.getAttribute("dragover") != aValue) {
+       aItem.setAttribute("dragover", aValue);
+ 
+       let window = aItem.ownerGlobal;
+       let draggedItem = window.document.getElementById(aDraggedItemId);
+       if (aAreaType == "palette" || (aAreaType == "menu-panel" && !gPhotonStructure)) {
+         this._setGridDragActive(aItem, draggedItem, aValue);
+       } else {
+diff --git a/browser/themes/shared/customizableui/customizeMode.inc.css b/browser/themes/shared/customizableui/customizeMode.inc.css
+--- a/browser/themes/shared/customizableui/customizeMode.inc.css
++++ b/browser/themes/shared/customizableui/customizeMode.inc.css
+@@ -278,49 +278,49 @@
+                     linear-gradient(to bottom, #3e86ce, #3878ba);
+   background-position: center top, left center, left top, left top, left top;
+   background-repeat: no-repeat, no-repeat, repeat, repeat, no-repeat;
+   background-size: auto 12px, 12px 100%, auto, auto, auto;
+   background-attachment: scroll, scroll, fixed, fixed, scroll;
+ }
+ %endif
+ 
+-#widget-overflow-fixed-list > toolbarpaletteitem[place="panel"],
++#widget-overflow-fixed-list > toolbarpaletteitem[place="menu-panel"],
+ toolbarpaletteitem[place="toolbar"] {
+   transition: border-width 250ms ease-in-out;
+ }
+ 
+ toolbarpaletteitem[mousedown] {
+   cursor: -moz-grabbing;
+ }
+ 
+ .panel-customization-placeholder,
+ toolbarpaletteitem[place="palette"],
+-toolbarpaletteitem[place="panel"] {
++toolbarpaletteitem[place="menu-panel"] {
+   transition: transform var(--drag-drop-transition-duration) ease-in-out;
+ }
+ 
+ #customization-palette {
+   transition: opacity .3s ease-in-out;
+   opacity: 0;
+ }
+ 
+ #customization-palette[showing="true"] {
+   opacity: 1;
+ }
+ 
+ toolbarpaletteitem toolbarbutton[disabled] {
+   color: inherit !important;
+ }
+ 
+-#widget-overflow-fixed-list > toolbarpaletteitem[notransition][place="panel"],
++#widget-overflow-fixed-list > toolbarpaletteitem[notransition][place="menu-panel"],
+ toolbarpaletteitem[notransition].panel-customization-placeholder,
+ toolbarpaletteitem[notransition][place="toolbar"],
+ toolbarpaletteitem[notransition][place="palette"],
+-toolbarpaletteitem[notransition][place="panel"] {
++toolbarpaletteitem[notransition][place="menu-panel"] {
+   transition: none;
+ }
+ 
+ toolbarpaletteitem > toolbarbutton > .toolbarbutton-icon,
+ toolbarpaletteitem > toolbarbutton > .toolbarbutton-badge-stack > .toolbarbutton-icon,
+ toolbarpaletteitem > toolbaritem.panel-wide-item,
+ toolbarpaletteitem > toolbarbutton[type="menu-button"] {
+   transition: transform var(--drag-drop-transition-duration) cubic-bezier(.6, 2, .75, 1.5) !important;
+@@ -338,33 +338,33 @@ toolbarpaletteitem[mousedown] > toolbarb
+ 
+ /* Override the toolkit styling for items being dragged over. */
+ toolbarpaletteitem[place="toolbar"] {
+   border-left-width: 0;
+   border-right-width: 0;
+   margin-right: 0;
+   margin-left: 0;
+ }
+-#widget-overflow-fixed-list > toolbarpaletteitem[place="panel"] {
++#widget-overflow-fixed-list > toolbarpaletteitem[place="menu-panel"] {
+   border-top: 0px solid transparent;
+   border-bottom: 0px solid transparent;
+ }
+ 
+ #customization-palette:not([hidden]) {
+   margin-bottom: 20px;
+ }
+ 
+ toolbarpaletteitem[place="palette"]:-moz-focusring,
+-toolbarpaletteitem[place="panel"]:-moz-focusring,
++toolbarpaletteitem[place="menu-panel"]:-moz-focusring,
+ toolbarpaletteitem[place="toolbar"]:-moz-focusring {
+   outline-width: 0;
+ }
+ 
+ toolbarpaletteitem[place="palette"]:not([mousedown="true"]):-moz-focusring,
+-toolbarpaletteitem[place="panel"]:not([mousedown="true"]):-moz-focusring,
++toolbarpaletteitem[place="menu-panel"]:not([mousedown="true"]):-moz-focusring,
+ toolbarpaletteitem[place="toolbar"]:not([mousedown="true"]):-moz-focusring {
+   /* Delay adding the focusring back until after the transform transition completes. */
+   transition: outline-width .01s linear var(--drag-drop-transition-duration);
+   outline: 1px dotted;
+   -moz-outline-radius: 2.5px;
+ }
+ 
+ toolbarpaletteitem[place="toolbar"]:not([mousedown="true"]):-moz-focusring {
+diff --git a/browser/themes/shared/customizableui/panelUI.inc.css b/browser/themes/shared/customizableui/panelUI.inc.css
+--- a/browser/themes/shared/customizableui/panelUI.inc.css
++++ b/browser/themes/shared/customizableui/panelUI.inc.css
+@@ -295,18 +295,18 @@ panel[photon] > .panel-arrowcontainer > 
+   margin: 2px 0 0 !important;
+ }
+ 
+ .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-multiline-text {
+   text-align: center;
+   margin: -1px 0 0;
+ }
+ 
+-#wrapper-edit-controls:-moz-any([place="palette"],[place="panel"]) > #edit-controls,
+-#wrapper-zoom-controls:-moz-any([place="palette"],[place="panel"]) > #zoom-controls {
++#wrapper-edit-controls:-moz-any([place="palette"],[place="menu-panel"]) > #edit-controls,
++#wrapper-zoom-controls:-moz-any([place="palette"],[place="menu-panel"]) > #zoom-controls {
+   margin-inline-start: 0;
+ }
+ 
+ #PanelUI-contents {
+   max-width: @menuPanelWidth@;
+ }
+ 
+ #BMB_bookmarksPopup,
+@@ -408,25 +408,25 @@ photonpanelmultiview .panel-subview-body
+   -moz-box-orient: vertical;
+   width: calc(@menuPanelButtonWidth@);
+   height: calc(51px + 2.2em);
+ }
+ 
+ /* In order to have button labels constrained appropriately, items inside the toolbarpaletteitem
+  * should have a min-width set so they abide by the width set above (which they do outside of
+  * customize mode because they're in a flexed container) */
+-:root:not([photon-structure]) toolbarpaletteitem[place="panel"]:not([haswideitem=true]) > .toolbarbutton-1 {
++:root:not([photon-structure]) toolbarpaletteitem[place="menu-panel"]:not([haswideitem=true]) > .toolbarbutton-1 {
+   min-width: 0.01px;
+ }
+ 
+ /* Help SDK buttons fit in. */
+ toolbarpaletteitem[place="palette"] > toolbarbutton[constrain-size="true"] > .toolbarbutton-icon,
+ toolbarpaletteitem[place="palette"] > toolbarbutton[constrain-size="true"] > .toolbarbutton-badge-stack > .toolbarbutton-icon,
+ toolbarpaletteitem[place="palette"] > toolbaritem[sdkstylewidget="true"] > .toolbarbutton-1 > .toolbarbutton-icon,
+-toolbarpaletteitem[place="panel"] > toolbaritem[sdkstylewidget="true"] > .toolbarbutton-1 > .toolbarbutton-icon,
++toolbarpaletteitem[place="menu-panel"] > toolbaritem[sdkstylewidget="true"] > .toolbarbutton-1 > .toolbarbutton-icon,
+ toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton-icon,
+ toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton-badge-stack > .toolbarbutton-icon {
+   height: @panelPaletteIconSize@;
+   width: @panelPaletteIconSize@;
+ }
+ 
+ .customization-palette .toolbarbutton-1 {
+   -moz-appearance: none;
+@@ -1266,17 +1266,17 @@ panelview:not([mainView]) .subviewbutton
+ 
+ .cui-widget-panelview .subviewbutton:not(.panel-subview-footer) {
+   margin-left: 4px;
+   margin-right: 4px;
+ }
+ 
+ /* START photon adjustments */
+ 
+-.widget-overflow-list > toolbarpaletteitem[place=panel] .toolbarbutton-1,
++.widget-overflow-list > toolbarpaletteitem[place="menu-panel"] .toolbarbutton-1,
+ photonpanelmultiview .widget-overflow-list .toolbarbutton-1,
+ photonpanelmultiview .PanelUI-subView .subviewbutton,
+ photonpanelmultiview .cui-widget-panelview .subviewbutton:not(.panel-subview-footer),
+ photonpanelmultiview .subview-subheader {
+   border-radius: 0;
+   border-width: 0;
+   margin: 0;
+   padding: 4px 12px;
+@@ -1602,17 +1602,17 @@ toolbarpaletteitem[place="palette"] > .t
+ #bookmarks-menu-button[cui-areatype="menu-panel"] > .toolbarbutton-menu-dropmarker,
+ #bookmarks-menu-button[overflowedItem] > .toolbarbutton-menu-dropmarker,
+ toolbarpaletteitem[place="palette"] > .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker,
+ #bookmarks-menu-button[cui-areatype="menu-panel"] > .toolbarbutton-menubutton-dropmarker {
+   display: none;
+ }
+ 
+ #search-container[cui-areatype="menu-panel"],
+-#wrapper-search-container[place="panel"] {
++#wrapper-search-container[place="menu-panel"] {
+   width: @menuPanelWidth@;
+ }
+ 
+ #search-container[cui-areatype="menu-panel"] {
+   margin-top: 6px;
+   margin-bottom: 6px;
+ }
+ 
+@@ -1635,17 +1635,17 @@ toolbarpaletteitem[place="palette"] > #s
+ }
+ 
+ /* Make direct siblings overlap borders: */
+ .toolbaritem-combined-buttons + .toolbaritem-combined-buttons@inAnyPanel@ {
+   border-top-color: transparent !important;
+ }
+ 
+ .toolbaritem-combined-buttons + .toolbaritem-combined-buttons@inAnyPanel@,
+-toolbarpaletteitem[haswideitem][place="panel"] + toolbarpaletteitem[haswideitem][place="panel"] {
++toolbarpaletteitem[haswideitem][place="menu-panel"] + toolbarpaletteitem[haswideitem][place="menu-panel"] {
+   margin-top: -1px;
+ }
+ %endif
+ 
+ .toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton {
+   border: 0;
+   margin: 0;
+   -moz-box-flex: 1;
+@@ -1661,17 +1661,17 @@ toolbarpaletteitem[haswideitem][place="p
+   padding-top: 4px;
+   padding-bottom: 4px;
+ %endif
+   -moz-box-orient: horizontal;
+ }
+ 
+ %ifdef MOZ_PHOTON_THEME
+ /* In customize mode, extend the buttons *only* in the panel, just to make them not look stupid */
+-toolbarpaletteitem[place=panel] > .toolbaritem-combined-buttons > toolbarbutton {
++toolbarpaletteitem[place="menu-panel"] > .toolbaritem-combined-buttons > toolbarbutton {
+   min-width: calc(@menuPanelButtonWidth@ - 1px);
+   max-width: calc(@menuPanelButtonWidth@ - 1px);
+ }
+ %else
+ #edit-controls@inAnyPanel@ > #copy-button,
+ #zoom-controls@inAnyPanel@ > #zoom-reset-button {
+   /* reduce the width with 2px for this button to compensate for two separators
+      of 1px. */

+ 55 - 0
mozilla-release/patches/1424943-59a1.patch

@@ -0,0 +1,55 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513177106 18000
+# Node ID c885e730dded79619acf357346b4954ef24a79b0
+# Parent  a6ef349582d9f68b7e131f92485dfed3dc3920b9
+Bug 1424943 Fix test_error_reporting.html not to race network vs Clients API. r=tt
+
+diff --git a/dom/workers/test/serviceworkers/sw_storage_not_allow.js b/dom/workers/test/serviceworkers/sw_storage_not_allow.js
+--- a/dom/workers/test/serviceworkers/sw_storage_not_allow.js
++++ b/dom/workers/test/serviceworkers/sw_storage_not_allow.js
+@@ -1,21 +1,34 @@
+ let clientId;
+ addEventListener('fetch', function(event) {
+-  if (event.request.url.includes('getClients')) {
+-    // Excepted to fail since the storage access is not allowed.
+-    self.clients.matchAll();
+-  } else if (event.request.url.includes('getClient-stage1')) {
+-    self.clients.matchAll().then(function(clients) {
++  event.respondWith(async function() {
++    if (event.request.url.includes('getClients')) {
++      // Expected to fail since the storage access is not allowed.
++      try {
++        await self.clients.matchAll();
++      } catch (e) {
++        // expected failure
++      }
++    } else if (event.request.url.includes('getClient-stage1')) {
++      let clients = await self.clients.matchAll();
+       clientId = clients[0].id;
+-    });
+-  } else if (event.request.url.includes('getClient-stage2')) {
+-    // Excepted to fail since the storage access is not allowed.
+-    self.clients.get(clientId);
+-  }
++    } else if (event.request.url.includes('getClient-stage2')) {
++      // Expected to fail since the storage access is not allowed.
++      try {
++        await self.clients.get(clientId);
++      } catch(e) {
++        // expected failure
++      }
++    }
++
++    // Pass through the network request once our various Clients API
++    // promises have completed.
++    return await fetch(event.request);
++  }());
+ });
+ 
+ addEventListener('message', function(event) {
+   if (event.data === 'claim') {
+     event.waitUntil(clients.claim());
+   }
+ });
+ 

+ 196 - 0
mozilla-release/patches/1425316-1-59a1.patch

@@ -0,0 +1,196 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513695887 18000
+# Node ID 56b3aab1d763d41b4672b06cc1034f6f5895357d
+# Parent  b8380e17e90aa8c45d3c0cc76429aa1d0a585d8b
+Bug 1425316 P1 Expose a chrome-only Window.shouldReportForServiceWorkerScope() method that initially forwards to ServiceWorkerManager. r=asuth
+
+diff --git a/devtools/server/actors/webconsole/listeners.js b/devtools/server/actors/webconsole/listeners.js
+--- a/devtools/server/actors/webconsole/listeners.js
++++ b/devtools/server/actors/webconsole/listeners.js
+@@ -278,17 +278,17 @@ ConsoleAPIListener.prototype =
+     let workerType = WebConsoleUtils.getWorkerType(message);
+ 
+     if (this.window && workerType === "ServiceWorker") {
+       // For messages from Service Workers, message.ID is the
+       // scope, which can be used to determine whether it's controlling
+       // a window.
+       let scope = message.ID;
+ 
+-      if (!swm.shouldReportToWindow(this.window, scope)) {
++      if (!this.window.shouldReportForServiceWorkerScope(scope)) {
+         return false;
+       }
+     }
+ 
+     if (this.window && !workerType) {
+       let msgWindow = Services.wm.getCurrentInnerWindowWithId(message.innerID);
+       if (!msgWindow || !isWindowIncluded(this.window, msgWindow)) {
+         // Not the same window!
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -78,16 +78,17 @@
+ #include "mozilla/Sprintf.h"
+ #include "mozilla/Unused.h"
+ 
+ // Other Classes
+ #include "mozilla/dom/BarProps.h"
+ #include "nsContentCID.h"
+ #include "nsLayoutStatics.h"
+ #include "nsCCUncollectableMarker.h"
++#include "mozilla/dom/workers/ServiceWorkerManager.h"
+ #include "mozilla/dom/workers/Workers.h"
+ #include "mozilla/dom/ToJSValue.h"
+ #include "nsJSPrincipals.h"
+ #include "mozilla/Attributes.h"
+ #include "mozilla/Debug.h"
+ #include "mozilla/EventListenerManager.h"
+ #include "mozilla/EventStates.h"
+ #include "mozilla/MouseEvents.h"
+@@ -4435,16 +4436,31 @@ nsPIDOMWindowInner::GetClientState() con
+ }
+ 
+ Maybe<ServiceWorkerDescriptor>
+ nsPIDOMWindowInner::GetController() const
+ {
+   return Move(nsGlobalWindow::Cast(this)->GetController());
+ }
+ 
++bool
++nsGlobalWindow::ShouldReportForServiceWorkerScope(const nsAString& aScope)
++{
++  RefPtr<workers::ServiceWorkerManager> swm =
++    workers::ServiceWorkerManager::GetInstance();
++  NS_ENSURE_TRUE(swm, false);
++
++  bool aResult = false;
++  nsresult rv = swm->ShouldReportToWindow(GetOuterWindowInternal(),
++                                          NS_ConvertUTF16toUTF8(aScope), &aResult);
++  NS_ENSURE_SUCCESS(rv, false);
++
++  return aResult;
++}
++
+ void
+ nsGlobalWindow::UpdateTopInnerWindow()
+ {
+   if (!IsInnerWindow() || AsInner()->IsTopInnerWindow()) {
+     return;
+   }
+ 
+   AsInner()->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
+diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h
+--- a/dom/base/nsGlobalWindow.h
++++ b/dom/base/nsGlobalWindow.h
+@@ -1314,16 +1314,18 @@ public:
+                     JS::MutableHandle<JS::Value> aRetval,
+                     mozilla::ErrorResult& aError);
+ 
+   already_AddRefed<nsWindowRoot> GetWindowRootOuter();
+   already_AddRefed<nsWindowRoot> GetWindowRoot(mozilla::ErrorResult& aError);
+ 
+   mozilla::dom::Performance* GetPerformance();
+ 
++  bool ShouldReportForServiceWorkerScope(const nsAString& aScope);
++
+   void UpdateTopInnerWindow();
+ 
+   virtual bool IsInSyncOperation() override
+   {
+     return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
+   }
+ 
+ protected:
+diff --git a/dom/interfaces/base/nsIServiceWorkerManager.idl b/dom/interfaces/base/nsIServiceWorkerManager.idl
+--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
++++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
+@@ -229,15 +229,13 @@ interface nsIServiceWorkerManager : nsIS
+                                      [optional] in uint32_t aDataLength,
+                                      [optional, array, size_is(aDataLength)] in uint8_t aDataBytes);
+   void sendPushSubscriptionChangeEvent(in ACString aOriginAttributes,
+                                        in ACString scope);
+ 
+   void addListener(in nsIServiceWorkerManagerListener aListener);
+ 
+   void removeListener(in nsIServiceWorkerManagerListener aListener);
+-
+-  bool shouldReportToWindow(in mozIDOMWindowProxy aWindow, in ACString aScope);
+ };
+ 
+ %{ C++
+ #define SERVICEWORKERMANAGER_CONTRACTID "@mozilla.org/serviceworkers/manager;1"
+ %}
+diff --git a/dom/webidl/Window.webidl b/dom/webidl/Window.webidl
+--- a/dom/webidl/Window.webidl
++++ b/dom/webidl/Window.webidl
+@@ -336,16 +336,23 @@ partial interface Window {
+ 
+   [Throws, ChromeOnly] any getInterface(IID iid);
+ 
+   /**
+    * Same as nsIDOMWindow.windowRoot, useful for event listener targeting.
+    */
+   [ChromeOnly, Throws]
+   readonly attribute WindowRoot? windowRoot;
++
++  /**
++   * ChromeOnly method to determine if a particular window should see console
++   * reports from service workers of the given scope.
++   */
++  [ChromeOnly]
++  boolean shouldReportForServiceWorkerScope(USVString aScope);
+ };
+ 
+ Window implements TouchEventHandlers;
+ 
+ Window implements OnErrorEventHandlerForWindow;
+ 
+ #if defined(MOZ_WIDGET_ANDROID)
+ // https://compat.spec.whatwg.org/#windoworientation-interface
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -3626,17 +3626,17 @@ ServiceWorkerManager::RemoveListener(nsI
+     return NS_ERROR_INVALID_ARG;
+   }
+ 
+   mListeners.RemoveElement(aListener);
+ 
+   return NS_OK;
+ }
+ 
+-NS_IMETHODIMP
++nsresult
+ ServiceWorkerManager::ShouldReportToWindow(mozIDOMWindowProxy* aWindow,
+                                            const nsACString& aScope,
+                                            bool* aResult)
+ {
+   AssertIsOnMainThread();
+   MOZ_ASSERT(aResult);
+ 
+   *aResult = false;
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -319,16 +319,20 @@ public:
+   NotifyUnregister(nsIPrincipal* aPrincipal, const nsAString& aScope);
+ 
+   void
+   WorkerIsIdle(ServiceWorkerInfo* aWorker);
+ 
+   void
+   CheckPendingReadyPromises();
+ 
++  nsresult
++  ShouldReportToWindow(mozIDOMWindowProxy* aWindow, const nsACString& aScope,
++                       bool* aResult);
++
+ private:
+   ServiceWorkerManager();
+   ~ServiceWorkerManager();
+ 
+   void
+   Init(ServiceWorkerRegistrar* aRegistrar);
+ 
+   void

+ 134 - 0
mozilla-release/patches/1425316-2-59a1.patch

@@ -0,0 +1,134 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513695888 18000
+# Node ID 81fe3f741dc336b2ce1df1c5d9718ee5b179a24a
+# Parent  1bf366f04bc32a49f72379eea9a49a61779817d5
+Bug 1425316 P2 Improve nsGlobalWindowInner::CallOnChildren() to take variable arguments and allow iteration to be aborted. r=asuth
+
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -12593,27 +12593,29 @@ nsGlobalWindow::SyncStateFromParentWindo
+ 
+   // Now apply only the number of Suspend() calls to reach the target
+   // suspend count after applying the Freeze() calls.
+   for (uint32_t i = 0; i < (parentSuspendDepth - parentFreezeDepth); ++i) {
+     Suspend();
+   }
+ }
+ 
+-template<typename Method>
+-void
+-nsGlobalWindow::CallOnChildren(Method aMethod)
++template<typename Method, typename... Args>
++nsGlobalWindow::CallState
++nsGlobalWindow::CallOnChildren(Method aMethod, Args& ...aArgs)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   MOZ_ASSERT(IsInnerWindow());
+   MOZ_ASSERT(AsInner()->IsCurrentInnerWindow());
+ 
++  CallState state = CallState::Continue;
++
+   nsCOMPtr<nsIDocShell> docShell = GetDocShell();
+   if (!docShell) {
+-    return;
++    return state;
+   }
+ 
+   int32_t childCount = 0;
+   docShell->GetChildCount(&childCount);
+ 
+   for (int32_t i = 0; i < childCount; ++i) {
+     nsCOMPtr<nsIDocShellTreeItem> childShell;
+     docShell->GetChildAt(i, getter_AddRefs(childShell));
+@@ -12629,18 +12631,29 @@ nsGlobalWindow::CallOnChildren(Method aM
+ 
+     // This is a bit hackish. Only freeze/suspend windows which are truly our
+     // subwindows.
+     nsCOMPtr<Element> frame = pWin->GetFrameElementInternal();
+     if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) {
+       continue;
+     }
+ 
+-    (inner->*aMethod)();
+-  }
++    // Call the child method using our helper CallChild() template method.
++    // This allows us to handle both void returning methods and methods
++    // that return CallState explicitly.  For void returning methods we
++    // assume CallState::Continue.
++    typedef decltype((inner->*aMethod)(aArgs...)) returnType;
++    state = CallChild<returnType>(inner, aMethod, aArgs...);
++
++    if (state == CallState::Stop) {
++      return state;
++    }
++  }
++
++  return state;
+ }
+ 
+ Maybe<ClientInfo>
+ nsGlobalWindow::GetClientInfo() const
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   Maybe<ClientInfo> clientInfo;
+   if (mClientSource) {
+diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h
+--- a/dom/base/nsGlobalWindow.h
++++ b/dom/base/nsGlobalWindow.h
+@@ -1566,18 +1566,52 @@ private:
+                         bool aDoJSFixups,
+                         bool aNavigate,
+                         nsIArray *argv,
+                         nsISupports *aExtraArgument,
+                         nsIDocShellLoadInfo* aLoadInfo,
+                         bool aForceNoOpener,
+                         nsPIDOMWindowOuter **aReturn);
+ 
+-  template<typename Method>
+-  void CallOnChildren(Method aMethod);
++  // A type that methods called by CallOnChildren can return.  If Stop
++  // is returned then CallOnChildren will stop calling further children.
++  // If Continue is returned then CallOnChildren will keep calling further
++  // children.
++  enum class CallState
++  {
++    Continue,
++    Stop,
++  };
++
++  // Call the given method on the immediate children of this window.  The
++  // CallState returned by the last child method invocation is returned or
++  // CallState::Continue if the method returns void.
++  template<typename Method, typename... Args>
++  CallState CallOnChildren(Method aMethod, Args& ...aArgs);
++
++  // Helper to convert a void returning child method into an implicit
++  // CallState::Continue value.
++  template<typename Return, typename Method, typename... Args>
++  typename std::enable_if<std::is_void<Return>::value,
++                          nsGlobalWindow::CallState>::type
++  CallChild(nsGlobalWindow* aWindow, Method aMethod, Args& ...aArgs)
++  {
++    (aWindow->*aMethod)(aArgs...);
++    return nsGlobalWindow::CallState::Continue;
++  }
++
++  // Helper that passes through the CallState value from a child method.
++  template<typename Return, typename Method, typename... Args>
++  typename std::enable_if<std::is_same<Return,
++                                       nsGlobalWindow::CallState>::value,
++                          nsGlobalWindow::CallState>::type
++  CallChild(nsGlobalWindow* aWindow, Method aMethod, Args& ...aArgs)
++  {
++    return (aWindow->*aMethod)(aArgs...);
++  }
+ 
+   void FreezeInternal();
+   void ThawInternal();
+ 
+ public:
+   // Timeout Functions
+   // |interval| is in milliseconds.
+   int32_t SetTimeoutOrInterval(JSContext* aCx,

+ 146 - 0
mozilla-release/patches/1425316-3-59a1.patch

@@ -0,0 +1,146 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513695888 18000
+# Node ID ca80e585ab6d3e85030c583d2139167e8c112ff6
+# Parent  2ead4fc25786ba837330bd4cedee1aa729303bd8
+Bug 1425316 P3 Make nsGlobalWindowInner::ShouldReportForServiceWorkerScope() handle checks for controlled windows. r=asuth
+
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -4439,28 +4439,63 @@ Maybe<ServiceWorkerDescriptor>
+ nsPIDOMWindowInner::GetController() const
+ {
+   return Move(nsGlobalWindow::Cast(this)->GetController());
+ }
+ 
+ bool
+ nsGlobalWindow::ShouldReportForServiceWorkerScope(const nsAString& aScope)
+ {
++  bool result = false;
++
++  nsPIDOMWindowOuter* topOuter = GetScriptableTop();
++  NS_ENSURE_TRUE(topOuter, false);
++
++  nsGlobalWindow* topInner =
++    nsGlobalWindow::Cast(topOuter->GetCurrentInnerWindow());
++  NS_ENSURE_TRUE(topInner, false);
++
++  topInner->ShouldReportForServiceWorkerScopeInternal(NS_ConvertUTF16toUTF8(aScope),
++                                                      &result);
++  if (result) {
++    return true;
++  }
++
+   RefPtr<workers::ServiceWorkerManager> swm =
+     workers::ServiceWorkerManager::GetInstance();
+   NS_ENSURE_TRUE(swm, false);
+ 
+   bool aResult = false;
+-  nsresult rv = swm->ShouldReportToWindow(GetOuterWindowInternal(),
++  nsresult rv = swm->ShouldReportToWindow(topOuter,
+                                           NS_ConvertUTF16toUTF8(aScope), &aResult);
+   NS_ENSURE_SUCCESS(rv, false);
+ 
+   return aResult;
+ }
+ 
++nsGlobalWindow::CallState
++nsGlobalWindow::ShouldReportForServiceWorkerScopeInternal(const nsACString& aScope,
++                                                          bool* aResultOut)
++{
++  MOZ_DIAGNOSTIC_ASSERT(aResultOut);
++
++  // First check to see if this window is controlled.  If so, then we have
++  // found a match and are done.
++  const Maybe<ServiceWorkerDescriptor> swd = GetController();
++  if (swd.isSome() && swd.ref().Scope() == aScope) {
++    *aResultOut = true;
++    return CallState::Stop;
++  }
++
++  // The current window doesn't care about this service worker, but maybe
++  // one of our child frames does.
++  return CallOnChildren(&nsGlobalWindow::ShouldReportForServiceWorkerScopeInternal,
++                        aScope, aResultOut);
++}
++
+ void
+ nsGlobalWindow::UpdateTopInnerWindow()
+ {
+   if (!IsInnerWindow() || AsInner()->IsTopInnerWindow()) {
+     return;
+   }
+ 
+   AsInner()->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
+diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h
+--- a/dom/base/nsGlobalWindow.h
++++ b/dom/base/nsGlobalWindow.h
+@@ -1606,16 +1606,20 @@ private:
+   CallChild(nsGlobalWindow* aWindow, Method aMethod, Args& ...aArgs)
+   {
+     return (aWindow->*aMethod)(aArgs...);
+   }
+ 
+   void FreezeInternal();
+   void ThawInternal();
+ 
++  CallState ShouldReportForServiceWorkerScopeInternal(const nsACString& aScope,
++                                                      bool* aResultOut);
++
++
+ public:
+   // Timeout Functions
+   // |interval| is in milliseconds.
+   int32_t SetTimeoutOrInterval(JSContext* aCx,
+                                mozilla::dom::Function& aFunction,
+                                int32_t aTimeout,
+                                const mozilla::dom::Sequence<JS::Value>& aArguments,
+                                bool aIsInterval, mozilla::ErrorResult& aError);
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -3713,43 +3713,16 @@ ServiceWorkerManager::ShouldReportToWind
+       // Match.  We should report to this window.
+       if (outer && winId == outer->WindowID()) {
+         *aResult = true;
+         return NS_OK;
+       }
+     }
+   }
+ 
+-  // Next examine controlled documents to see if the windows match.
+-  for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
+-    ServiceWorkerRegistrationInfo* reg = iter.UserData();
+-    MOZ_ASSERT(reg);
+-    if (!reg->mScope.Equals(aScope)) {
+-      continue;
+-    }
+-
+-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
+-    if (!doc || !doc->IsCurrentActiveDocument()) {
+-      continue;
+-    }
+-
+-    nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
+-    if (!win) {
+-      continue;
+-    }
+-
+-    win = win->GetScriptableTop();
+-
+-    // Match.  We should report to this window.
+-    if (win && winId == win->WindowID()) {
+-      *aResult = true;
+-      return NS_OK;
+-    }
+-  }
+-
+   // No match.  We should not report to this window.
+   return NS_OK;
+ }
+ 
+ NS_IMETHODIMP
+ ServiceWorkerManager::Observe(nsISupports* aSubject,
+                               const char* aTopic,
+                               const char16_t* aData)

+ 270 - 0
mozilla-release/patches/1425316-4-59a1.patch

@@ -0,0 +1,270 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513695888 18000
+# Node ID c08edd442321e8d3b1c04ca25489e6f1634abbeb
+# Parent  003675998a8d6997d1032beb9d5969bc60c98175
+Bug 1425316 P4 Note that a ClientSource has called register() for a SW scope and use it to match window console reports. r=asuth
+
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -4436,16 +4436,22 @@ nsPIDOMWindowInner::GetClientState() con
+ }
+ 
+ Maybe<ServiceWorkerDescriptor>
+ nsPIDOMWindowInner::GetController() const
+ {
+   return Move(nsGlobalWindow::Cast(this)->GetController());
+ }
+ 
++void
++nsPIDOMWindowInner::NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope)
++{
++  nsGlobalWindow::Cast(this)->NoteCalledRegisterForServiceWorkerScope(aScope);
++}
++
+ bool
+ nsGlobalWindow::ShouldReportForServiceWorkerScope(const nsAString& aScope)
+ {
+   bool result = false;
+ 
+   nsPIDOMWindowOuter* topOuter = GetScriptableTop();
+   NS_ENSURE_TRUE(topOuter, false);
+ 
+@@ -4480,23 +4486,41 @@ nsGlobalWindow::ShouldReportForServiceWo
+   // First check to see if this window is controlled.  If so, then we have
+   // found a match and are done.
+   const Maybe<ServiceWorkerDescriptor> swd = GetController();
+   if (swd.isSome() && swd.ref().Scope() == aScope) {
+     *aResultOut = true;
+     return CallState::Stop;
+   }
+ 
++  // Next, check to see if this window has called navigator.serviceWorker.register()
++  // for this scope.  If so, then treat this as a match so console reports
++  // appear in the devtools console.
++  if (mClientSource && mClientSource->CalledRegisterForServiceWorkerScope(aScope)) {
++    *aResultOut = true;
++    return CallState::Stop;
++  }
++
+   // The current window doesn't care about this service worker, but maybe
+   // one of our child frames does.
+   return CallOnChildren(&nsGlobalWindow::ShouldReportForServiceWorkerScopeInternal,
+                         aScope, aResultOut);
+ }
+ 
+ void
++nsGlobalWindow::NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope)
++{
++  if (!mClientSource) {
++    return;
++  }
++
++  mClientSource->NoteCalledRegisterForServiceWorkerScope(aScope);
++}
++
++void
+ nsGlobalWindow::UpdateTopInnerWindow()
+ {
+   if (!IsInnerWindow() || AsInner()->IsTopInnerWindow()) {
+     return;
+   }
+ 
+   AsInner()->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
+ }
+diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h
+--- a/dom/base/nsGlobalWindow.h
++++ b/dom/base/nsGlobalWindow.h
+@@ -402,16 +402,18 @@ public:
+   void Thaw();
+   virtual bool IsFrozen() const override;
+   void SyncStateFromParentWindow();
+ 
+   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
+   mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
+   mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
+ 
++  void NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope);
++
+   virtual nsresult FireDelayedDOMEvents() override;
+ 
+   // Outer windows only.
+   virtual bool WouldReuseInnerWindow(nsIDocument* aNewDocument) override;
+ 
+   virtual void SetDocShell(nsIDocShell* aDocShell) override;
+   virtual void DetachFromDocShell() override;
+   virtual nsresult SetNewDocument(nsIDocument *aDocument,
+diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h
+--- a/dom/base/nsPIDOMWindow.h
++++ b/dom/base/nsPIDOMWindow.h
+@@ -937,16 +937,18 @@ public:
+   // Return true if there are any open WebSockets that could block
+   // timeout-throttling.
+   bool HasOpenWebSockets() const;
+ 
+   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
+   mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
+   mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
+ 
++  void NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope);
++
+ protected:
+   void CreatePerformanceObjectIfNeeded();
+ };
+ 
+ NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
+ 
+ // NB: It's very very important that these two classes have identical vtables
+ // and memory layout!
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -672,10 +672,25 @@ ClientSource::Traverse(nsCycleCollection
+                                 aName, aFlags);
+   } else if (mOwner.is<nsCOMPtr<nsIDocShell>>()) {
+     ImplCycleCollectionTraverse(aCallback,
+                                 mOwner.as<nsCOMPtr<nsIDocShell>>(),
+                                 aName, aFlags);
+   }
+ }
+ 
++void
++ClientSource::NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope)
++{
++  if (mRegisteringScopeList.Contains(aScope)) {
++    return;
++  }
++  mRegisteringScopeList.AppendElement(aScope);
++}
++
++bool
++ClientSource::CalledRegisterForServiceWorkerScope(const nsACString& aScope)
++{
++  return mRegisteringScopeList.Contains(aScope);
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientSource.h b/dom/clients/manager/ClientSource.h
+--- a/dom/clients/manager/ClientSource.h
++++ b/dom/clients/manager/ClientSource.h
+@@ -58,16 +58,22 @@ class ClientSource final : public Client
+   Variant<Nothing,
+           RefPtr<nsPIDOMWindowInner>,
+           nsCOMPtr<nsIDocShell>,
+           mozilla::dom::workers::WorkerPrivate*> mOwner;
+ 
+   ClientInfo mClientInfo;
+   Maybe<ServiceWorkerDescriptor> mController;
+ 
++  // Contained a de-duplicated list of ServiceWorker scope strings
++  // for which this client has called navigator.serviceWorker.register().
++  // Typically there will be either be zero or one scope strings, but
++  // there could be more.  We keep this list until the client is closed.
++  AutoTArray<nsCString, 1> mRegisteringScopeList;
++
+   void
+   Shutdown();
+ 
+   void
+   ExecutionReady(const ClientSourceExecutionReadyArgs& aArgs);
+ 
+   mozilla::dom::workers::WorkerPrivate*
+   GetWorkerPrivate() const;
+@@ -158,16 +164,22 @@ public:
+ 
+   nsISerialEventTarget*
+   EventTarget() const;
+ 
+   void
+   Traverse(nsCycleCollectionTraversalCallback& aCallback,
+            const char* aName,
+            uint32_t aFlags);
++
++  void
++  NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope);
++
++  bool
++  CalledRegisterForServiceWorkerScope(const nsACString& aScope);
+ };
+ 
+ inline void
+ ImplCycleCollectionUnlink(UniquePtr<ClientSource>& aField)
+ {
+   aField.reset();
+ }
+ 
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -833,16 +833,17 @@ ServiceWorkerManager::Register(mozIDOMWi
+   }
+ 
+   nsAutoCString scopeKey;
+   rv = PrincipalToScopeKey(documentPrincipal, scopeKey);
+   if (NS_WARN_IF(NS_FAILED(rv))) {
+     return rv;
+   }
+ 
++  window->NoteCalledRegisterForServiceWorkerScope(cleanedScope);
+   AddRegisteringDocument(cleanedScope, doc);
+ 
+   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
+                                                             cleanedScope);
+ 
+   RefPtr<ServiceWorkerResolveWindowPromiseOnRegisterCallback> cb =
+     new ServiceWorkerResolveWindowPromiseOnRegisterCallback(window, promise);
+ 
+@@ -3645,51 +3646,16 @@ ServiceWorkerManager::ShouldReportToWind
+   nsCOMPtr<nsPIDOMWindowOuter> targetWin = nsPIDOMWindowOuter::From(aWindow);
+   if (NS_WARN_IF(!targetWin)) {
+     return NS_OK;
+   }
+ 
+   targetWin = targetWin->GetScriptableTop();
+   uint64_t winId = targetWin->WindowID();
+ 
+-  // Check our weak registering document references first.  This way we clear
+-  // out as many dead weak references as possible when this method is called.
+-  WeakDocumentList* list = mRegisteringDocuments.Get(aScope);
+-  if (list) {
+-    for (int32_t i = list->Length() - 1; i >= 0; --i) {
+-      nsCOMPtr<nsIDocument> doc = do_QueryReferent(list->ElementAt(i));
+-      if (!doc) {
+-        list->RemoveElementAt(i);
+-        continue;
+-      }
+-
+-      if (!doc->IsCurrentActiveDocument()) {
+-        continue;
+-      }
+-
+-      nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
+-      if (!win) {
+-        continue;
+-      }
+-
+-      win = win->GetScriptableTop();
+-
+-      // Match.  We should report to this window.
+-      if (win && winId == win->WindowID()) {
+-        *aResult = true;
+-        return NS_OK;
+-      }
+-    }
+-
+-    if (list->IsEmpty()) {
+-      list = nullptr;
+-      mRegisteringDocuments.Remove(aScope);
+-    }
+-  }
+-
+   // Examine any windows performing a navigation that we are currently
+   // intercepting.
+   InterceptionList* intList = mNavigationInterceptions.Get(aScope);
+   if (intList) {
+     for (uint32_t i = 0; i < intList->Length(); ++i) {
+       nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
+ 
+       nsCOMPtr<nsIChannel> inner;

+ 138 - 0
mozilla-release/patches/1425316-5-59a1.patch

@@ -0,0 +1,138 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513695889 18000
+# Node ID 8c2c417bbe76fe6f49f2e55503232b5cd54fe16f
+# Parent  ebff0951b29a2df5802be90520eaf01f2b88ebdb
+Bug 1425316 P5 Match service worker console reports against currently loading navigation channel. r=asuth
+
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -41,16 +41,17 @@
+ #include "nsISizeOfEventTarget.h"
+ #include "nsDOMJSUtils.h"
+ #include "nsArrayUtils.h"
+ #include "nsIDOMWindowCollection.h"
+ #include "nsDOMWindowList.h"
+ #include "mozilla/dom/WakeLock.h"
+ #include "mozilla/dom/power/PowerManagerService.h"
+ #include "nsIDocShellTreeOwner.h"
++#include "nsIDocumentLoader.h"
+ #include "nsIInterfaceRequestorUtils.h"
+ #include "nsIPermissionManager.h"
+ #include "nsIScriptContext.h"
+ #include "nsIScriptTimeoutHandler.h"
+ #include "nsITimeoutHandler.h"
+ #include "nsIController.h"
+ #include "nsScriptNameSpaceManager.h"
+ #include "nsISlowScriptDebug.h"
+@@ -78,17 +79,16 @@
+ #include "mozilla/Sprintf.h"
+ #include "mozilla/Unused.h"
+ 
+ // Other Classes
+ #include "mozilla/dom/BarProps.h"
+ #include "nsContentCID.h"
+ #include "nsLayoutStatics.h"
+ #include "nsCCUncollectableMarker.h"
+-#include "mozilla/dom/workers/ServiceWorkerManager.h"
+ #include "mozilla/dom/workers/Workers.h"
+ #include "mozilla/dom/ToJSValue.h"
+ #include "nsJSPrincipals.h"
+ #include "mozilla/Attributes.h"
+ #include "mozilla/Debug.h"
+ #include "mozilla/EventListenerManager.h"
+ #include "mozilla/EventStates.h"
+ #include "mozilla/MouseEvents.h"
+@@ -4450,30 +4450,17 @@ nsGlobalWindow::ShouldReportForServiceWo
+   NS_ENSURE_TRUE(topOuter, false);
+ 
+   nsGlobalWindow* topInner =
+     nsGlobalWindow::Cast(topOuter->GetCurrentInnerWindow());
+   NS_ENSURE_TRUE(topInner, false);
+ 
+   topInner->ShouldReportForServiceWorkerScopeInternal(NS_ConvertUTF16toUTF8(aScope),
+                                                       &result);
+-  if (result) {
+-    return true;
+-  }
+-
+-  RefPtr<workers::ServiceWorkerManager> swm =
+-    workers::ServiceWorkerManager::GetInstance();
+-  NS_ENSURE_TRUE(swm, false);
+-
+-  bool aResult = false;
+-  nsresult rv = swm->ShouldReportToWindow(topOuter,
+-                                          NS_ConvertUTF16toUTF8(aScope), &aResult);
+-  NS_ENSURE_SUCCESS(rv, false);
+-
+-  return aResult;
++  return result;
+ }
+ 
+ nsGlobalWindow::CallState
+ nsGlobalWindow::ShouldReportForServiceWorkerScopeInternal(const nsACString& aScope,
+                                                           bool* aResultOut)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(aResultOut);
+ 
+@@ -4488,16 +4475,59 @@ nsGlobalWindow::ShouldReportForServiceWo
+   // Next, check to see if this window has called navigator.serviceWorker.register()
+   // for this scope.  If so, then treat this as a match so console reports
+   // appear in the devtools console.
+   if (mClientSource && mClientSource->CalledRegisterForServiceWorkerScope(aScope)) {
+     *aResultOut = true;
+     return CallState::Stop;
+   }
+ 
++  // Finally check the current docshell nsILoadGroup to see if there are any
++  // outstanding navigation requests.  If so, match the scope against the
++  // channel's URL.  We want to show console reports during the FetchEvent
++  // intercepting the navigation itself.
++  nsCOMPtr<nsIDocumentLoader> loader(do_QueryInterface(GetDocShell()));
++  if (loader) {
++    nsCOMPtr<nsILoadGroup> loadgroup;
++    Unused << loader->GetLoadGroup(getter_AddRefs(loadgroup));
++    if (loadgroup) {
++      nsCOMPtr<nsISimpleEnumerator> iter;
++      Unused << loadgroup->GetRequests(getter_AddRefs(iter));
++      if (iter) {
++        nsCOMPtr<nsISupports> tmp;
++        bool hasMore = true;
++        // Check each network request in the load group.
++        while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
++          iter->GetNext(getter_AddRefs(tmp));
++          nsCOMPtr<nsIChannel> loadingChannel(do_QueryInterface(tmp));
++          // Ignore subresource requests.  Logging for a subresource
++          // FetchEvent should be handled above since the client is
++          // already controlled.
++          if (!loadingChannel ||
++              !nsContentUtils::IsNonSubresourceRequest(loadingChannel)) {
++            continue;
++          }
++          nsCOMPtr<nsIURI> loadingURL;
++          Unused << loadingChannel->GetURI(getter_AddRefs(loadingURL));
++          if (!loadingURL) {
++            continue;
++          }
++          nsAutoCString loadingSpec;
++          Unused << loadingURL->GetSpec(loadingSpec);
++          // Perform a simple substring comparison to match the scope
++          // against the channel URL.
++          if (StringBeginsWith(loadingSpec, aScope)) {
++            *aResultOut = true;
++            return CallState::Stop;
++          }
++        }
++      }
++    }
++  }
++
+   // The current window doesn't care about this service worker, but maybe
+   // one of our child frames does.
+   return CallOnChildren(&nsGlobalWindow::ShouldReportForServiceWorkerScopeInternal,
+                         aScope, aResultOut);
+ }
+ 
+ void
+ nsGlobalWindow::NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope)

+ 107 - 0
mozilla-release/patches/1425316-6-59a1.patch

@@ -0,0 +1,107 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513695889 18000
+# Node ID 1e2457625325ac08b5820c186d82a8f1bdc7b20f
+# Parent  624263b006912c4fd30750ee55b1d1b3657634e5
+Bug 1425316 P6 Remove ServiceWorkerManager::ShouldReportToWindow(). r=asuth
+
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -3627,72 +3627,16 @@ ServiceWorkerManager::RemoveListener(nsI
+     return NS_ERROR_INVALID_ARG;
+   }
+ 
+   mListeners.RemoveElement(aListener);
+ 
+   return NS_OK;
+ }
+ 
+-nsresult
+-ServiceWorkerManager::ShouldReportToWindow(mozIDOMWindowProxy* aWindow,
+-                                           const nsACString& aScope,
+-                                           bool* aResult)
+-{
+-  AssertIsOnMainThread();
+-  MOZ_ASSERT(aResult);
+-
+-  *aResult = false;
+-
+-  // Get the inner window ID to compare to our document windows below.
+-  nsCOMPtr<nsPIDOMWindowOuter> targetWin = nsPIDOMWindowOuter::From(aWindow);
+-  if (NS_WARN_IF(!targetWin)) {
+-    return NS_OK;
+-  }
+-
+-  targetWin = targetWin->GetScriptableTop();
+-  uint64_t winId = targetWin->WindowID();
+-
+-  // Examine any windows performing a navigation that we are currently
+-  // intercepting.
+-  InterceptionList* intList = mNavigationInterceptions.Get(aScope);
+-  if (intList) {
+-    for (uint32_t i = 0; i < intList->Length(); ++i) {
+-      nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
+-
+-      nsCOMPtr<nsIChannel> inner;
+-      nsresult rv = channel->GetChannel(getter_AddRefs(inner));
+-      if (NS_WARN_IF(NS_FAILED(rv))) {
+-        continue;
+-      }
+-
+-      uint64_t id = nsContentUtils::GetInnerWindowID(inner);
+-      if (id == 0) {
+-        continue;
+-      }
+-
+-      nsCOMPtr<nsPIDOMWindowInner> win = nsGlobalWindow::GetInnerWindowWithId(id)->AsInner();
+-      if (!win) {
+-        continue;
+-      }
+-
+-      nsCOMPtr<nsPIDOMWindowOuter> outer = win->GetScriptableTop();
+-
+-      // Match.  We should report to this window.
+-      if (outer && winId == outer->WindowID()) {
+-        *aResult = true;
+-        return NS_OK;
+-      }
+-    }
+-  }
+-
+-  // No match.  We should not report to this window.
+-  return NS_OK;
+-}
+-
+ NS_IMETHODIMP
+ ServiceWorkerManager::Observe(nsISupports* aSubject,
+                               const char* aTopic,
+                               const char16_t* aData)
+ {
+   if (strcmp(aTopic, PURGE_SESSION_HISTORY) == 0) {
+     MOZ_ASSERT(XRE_IsParentProcess());
+     RemoveAll();
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -319,20 +319,16 @@ public:
+   NotifyUnregister(nsIPrincipal* aPrincipal, const nsAString& aScope);
+ 
+   void
+   WorkerIsIdle(ServiceWorkerInfo* aWorker);
+ 
+   void
+   CheckPendingReadyPromises();
+ 
+-  nsresult
+-  ShouldReportToWindow(mozIDOMWindowProxy* aWindow, const nsACString& aScope,
+-                       bool* aResult);
+-
+ private:
+   ServiceWorkerManager();
+   ~ServiceWorkerManager();
+ 
+   void
+   Init(ServiceWorkerRegistrar* aRegistrar);
+ 
+   void

+ 4 - 4
mozilla-release/patches/1425574-1-59a1.patch

@@ -2,15 +2,15 @@
 # User Andrea Marchesini <amarchesini@mozilla.com>
 # Date 1515089983 -3600
 # Node ID e6a832b492dd99ce7d41dca8b6f797b150558344
-# Parent  8445cc9c1806b1e4876ba4a9f760d34b92175535
+# Parent  21d487a16caca68dd554d82a0f70d4a189c44389
 Bug 1425574 - Fill the feature gap between Console.jsm and Console API - part 1 - Console.createInstance(), r=smaug
 
 diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf
 --- a/dom/bindings/Bindings.conf
 +++ b/dom/bindings/Bindings.conf
-@@ -162,16 +162,20 @@ DOMInterfaces = {
-     'nativeType': 'mozilla::dom::workers::ServiceWorkerClients',
-     'headerFile': 'mozilla/dom/workers/bindings/ServiceWorkerClients.h',
+@@ -152,16 +152,20 @@ DOMInterfaces = {
+     'headerFile': 'mozilla/dom/WorkerPrivate.h',
+     'nativeType': 'mozilla::dom::workers::ChromeWorkerPrivate',
  },
  
  'console': {

+ 38 - 0
mozilla-release/patches/1425614-1-59a1.patch

@@ -0,0 +1,38 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513633330 18000
+# Node ID 926123cda34755f06f7b5be385ac94a721aaf2a2
+# Parent  36bcea224d06c54d69408f4253b0a250debae700
+Bug 1425614 P1 Make ClientThing::MaybeExecute() call an optional failure callback. r=asuth
+
+diff --git a/dom/clients/manager/ClientThing.h b/dom/clients/manager/ClientThing.h
+--- a/dom/clients/manager/ClientThing.h
++++ b/dom/clients/manager/ClientThing.h
+@@ -44,23 +44,25 @@ protected:
+   IsShutdown() const
+   {
+     return mShutdown;
+   }
+ 
+   // Conditionally execute the given callable based on the current state.
+   template<typename Callable>
+   void
+-  MaybeExecute(const Callable& aCallable)
++  MaybeExecute(const Callable& aSuccess,
++               const std::function<void()>& aFailure = []{})
+   {
+     if (mShutdown) {
++      aFailure();
+       return;
+     }
+     MOZ_DIAGNOSTIC_ASSERT(mActor);
+-    aCallable(mActor);
++    aSuccess(mActor);
+   }
+ 
+   // Attach activate the thing by attaching its underlying IPC actor.  This
+   // will make the thing register as the actor's owner as well.  The actor
+   // must call RevokeActor() to clear this weak back reference before its
+   // destroyed.
+   void
+   ActivateThing(ActorType* aActor)

+ 51 - 0
mozilla-release/patches/1425614-2-59a1.patch

@@ -0,0 +1,51 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513633330 18000
+# Node ID 10b24604cf6b3bafb2a6ba39b1e2a34281ebbe9e
+# Parent  3ec838c182ee0666ec921a1391a56932965d31e1
+Bug 1425614 P2 Make ClientManager and ClientHandle operation promises reject if called on a worker thread already shutting down. r=asuth
+
+diff --git a/dom/clients/manager/ClientHandle.cpp b/dom/clients/manager/ClientHandle.cpp
+--- a/dom/clients/manager/ClientHandle.cpp
++++ b/dom/clients/manager/ClientHandle.cpp
+@@ -52,16 +52,18 @@ ClientHandle::StartOp(const ClientOpCons
+                 [kungFuGrip] (nsresult) { });
+ 
+   MaybeExecute([aArgs, promise] (ClientHandleChild* aActor) {
+     ClientHandleOpChild* actor = new ClientHandleOpChild(aArgs, promise);
+     if (!aActor->SendPClientHandleOpConstructor(actor, aArgs)) {
+       // Constructor failure will reject promise via ActorDestroy()
+       return;
+     }
++  }, [promise] {
++    promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
+   });
+ 
+   RefPtr<ClientOpPromise> ref = promise.get();
+   return ref.forget();
+ }
+ 
+ ClientHandle::ClientHandle(ClientManager* aManager,
+                            nsISerialEventTarget* aSerialEventTarget,
+diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientManager.cpp
+--- a/dom/clients/manager/ClientManager.cpp
++++ b/dom/clients/manager/ClientManager.cpp
+@@ -166,16 +166,18 @@ ClientManager::StartOp(const ClientOpCon
+                 [kungFuGrip] (nsresult) { });
+ 
+   MaybeExecute([aArgs, promise] (ClientManagerChild* aActor) {
+     ClientManagerOpChild* actor = new ClientManagerOpChild(aArgs, promise);
+     if (!aActor->SendPClientManagerOpConstructor(actor, aArgs)) {
+       // Constructor failure will reject promise via ActorDestroy()
+       return;
+     }
++  }, [promise] {
++    promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
+   });
+ 
+   RefPtr<ClientOpPromise> ref = promise.get();
+   return ref.forget();
+ }
+ 
+ // static
+ already_AddRefed<ClientManager>

+ 47 - 0
mozilla-release/patches/1425704-59a1.patch

@@ -0,0 +1,47 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513616118 18000
+# Node ID 332dc60a05c2be6783adbb42312c77367e5d49b9
+# Parent  0195d038b2bb6dd66c51c287318fc75bc0230142
+Bug 1425704 Fix nullptr deref in android-only Clients.openWindow() when browser is not running. r=asuth
+
+diff --git a/dom/clients/manager/ClientOpenWindowUtils.cpp b/dom/clients/manager/ClientOpenWindowUtils.cpp
+--- a/dom/clients/manager/ClientOpenWindowUtils.cpp
++++ b/dom/clients/manager/ClientOpenWindowUtils.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 "ClientOpenWindowUtils.h"
+ 
+ #include "ClientInfo.h"
+ #include "ClientState.h"
++#include "mozilla/SystemGroup.h"
+ #include "nsContentUtils.h"
+ #include "nsIBrowserDOMWindow.h"
+ #include "nsIDocShell.h"
+ #include "nsIDOMChromeWindow.h"
+ #include "nsIURI.h"
+ #include "nsIWebProgress.h"
+ #include "nsIWebProgressListener.h"
+ #include "nsIWindowWatcher.h"
+@@ -420,17 +421,17 @@ ClientOpenWindowInCurrentProcess(const C
+   nsresult rv = OpenWindow(aArgs, getter_AddRefs(outerWindow));
+ 
+ #ifdef MOZ_WIDGET_ANDROID
+   // If we get the NOT_AVAILABLE error that means the browser is still
+   // launching on android.  Use the observer we created above to wait
+   // until the launch completes and then try to open the window again.
+   if (rv == NS_ERROR_NOT_AVAILABLE && launchObserver) {
+     RefPtr<GenericPromise> p = launchObserver->Promise();
+-    p->Then(outerWindow->EventTargetFor(TaskCategory::Other), __func__,
++    p->Then(SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
+       [aArgs, promise] (bool aResult) {
+         nsCOMPtr<nsPIDOMWindowOuter> outerWindow;
+         nsresult rv = OpenWindow(aArgs, getter_AddRefs(outerWindow));
+         if (NS_WARN_IF(NS_FAILED(rv))) {
+           promise->Reject(rv, __func__);
+         }
+ 
+         WaitForLoad(aArgs, outerWindow, promise);

+ 193 - 0
mozilla-release/patches/1425965-1-59a1.patch

@@ -0,0 +1,193 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515777363 18000
+# Node ID bbc20dc8387908c28ad7c60341c99b6d17ae5e6b
+# Parent  ddb1c786f7940274b410dafffbbce8fde828b10e
+Bug 1425965 P1 Make ServiceWorkerManager::ReportToAllClients use ConsoleUtils::ReportForServiceWorkerScope. r=asuth
+
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -34,16 +34,17 @@
+ #include "mozilla/ClearOnShutdown.h"
+ #include "mozilla/ErrorNames.h"
+ #include "mozilla/LoadContext.h"
+ #include "mozilla/SystemGroup.h"
+ #include "mozilla/Telemetry.h"
+ #include "mozilla/dom/BindingUtils.h"
+ #include "mozilla/dom/ClientHandle.h"
+ #include "mozilla/dom/ClientManager.h"
++#include "mozilla/dom/ConsoleUtils.h"
+ #include "mozilla/dom/ContentParent.h"
+ #include "mozilla/dom/ErrorEvent.h"
+ #include "mozilla/dom/Headers.h"
+ #include "mozilla/dom/InternalHeaders.h"
+ #include "mozilla/dom/Navigator.h"
+ #include "mozilla/dom/NotificationEvent.h"
+ #include "mozilla/dom/PromiseNativeHandler.h"
+ #include "mozilla/dom/PromiseWindowProxy.h"
+@@ -1720,158 +1721,22 @@ void
+ ServiceWorkerManager::ReportToAllClients(const nsCString& aScope,
+                                          const nsString& aMessage,
+                                          const nsString& aFilename,
+                                          const nsString& aLine,
+                                          uint32_t aLineNumber,
+                                          uint32_t aColumnNumber,
+                                          uint32_t aFlags)
+ {
+-  nsCOMPtr<nsIURI> uri;
+-  nsresult rv;
+-
+-  if (!aFilename.IsEmpty()) {
+-    rv = NS_NewURI(getter_AddRefs(uri), aFilename);
+-    if (NS_WARN_IF(NS_FAILED(rv))) {
+-      return;
+-    }
+-  }
+-
+-  AutoTArray<uint64_t, 16> windows;
+-
+-  // Report errors to every controlled document.
+-  for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
+-    ServiceWorkerRegistrationInfo* reg = iter.UserData();
+-    MOZ_ASSERT(reg);
+-    if (!reg->mScope.Equals(aScope)) {
+-      continue;
+-    }
+-
+-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
+-    if (!doc || !doc->IsCurrentActiveDocument() || !doc->GetWindow()) {
+-      continue;
+-    }
+-
+-    windows.AppendElement(doc->InnerWindowID());
+-
+-    nsContentUtils::ReportToConsoleNonLocalized(aMessage,
+-                                                aFlags,
+-                                                NS_LITERAL_CSTRING("Service Workers"),
+-                                                doc,
+-                                                uri,
+-                                                aLine,
+-                                                aLineNumber,
+-                                                aColumnNumber,
+-                                                nsContentUtils::eOMIT_LOCATION);
+-  }
+-
+-  // Report to any documents that have called .register() for this scope.  They
+-  // may not be controlled, but will still want to see error reports.
+-  WeakDocumentList* regList = mRegisteringDocuments.Get(aScope);
+-  if (regList) {
+-    for (int32_t i = regList->Length() - 1; i >= 0; --i) {
+-      nsCOMPtr<nsIDocument> doc = do_QueryReferent(regList->ElementAt(i));
+-      if (!doc) {
+-        regList->RemoveElementAt(i);
+-        continue;
+-      }
+-
+-      if (!doc->IsCurrentActiveDocument()) {
+-        continue;
+-      }
+-
+-      uint64_t innerWindowId = doc->InnerWindowID();
+-      if (windows.Contains(innerWindowId)) {
+-        continue;
+-      }
+-
+-      windows.AppendElement(innerWindowId);
+-
+-      nsContentUtils::ReportToConsoleNonLocalized(aMessage,
+-                                                  aFlags,
+-                                                  NS_LITERAL_CSTRING("Service Workers"),
+-                                                  doc,
+-                                                  uri,
+-                                                  aLine,
+-                                                  aLineNumber,
+-                                                  aColumnNumber,
+-                                                  nsContentUtils::eOMIT_LOCATION);
+-    }
+-
+-    if (regList->IsEmpty()) {
+-      regList = nullptr;
+-      mRegisteringDocuments.Remove(aScope);
+-    }
+-  }
+-
+-  InterceptionList* intList = mNavigationInterceptions.Get(aScope);
+-  if (intList) {
+-    nsIConsoleService* consoleService = nullptr;
+-    for (uint32_t i = 0; i < intList->Length(); ++i) {
+-      nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
+-
+-      nsCOMPtr<nsIChannel> inner;
+-      rv = channel->GetChannel(getter_AddRefs(inner));
+-      if (NS_WARN_IF(NS_FAILED(rv))) {
+-        continue;
+-      }
+-
+-      uint64_t innerWindowId = nsContentUtils::GetInnerWindowID(inner);
+-      if (innerWindowId == 0 || windows.Contains(innerWindowId)) {
+-        continue;
+-      }
+-
+-      windows.AppendElement(innerWindowId);
+-
+-      // Unfortunately the nsContentUtils helpers don't provide a convenient
+-      // way to log to a window ID without a document.  Use console service
+-      // directly.
+-      nsCOMPtr<nsIScriptError> errorObject =
+-        do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
+-      if (NS_WARN_IF(NS_FAILED(rv))) {
+-        return;
+-      }
+-
+-      rv = errorObject->InitWithWindowID(aMessage,
+-                                         aFilename,
+-                                         aLine,
+-                                         aLineNumber,
+-                                         aColumnNumber,
+-                                         aFlags,
+-                                         NS_LITERAL_CSTRING("Service Workers"),
+-                                         innerWindowId);
+-      if (NS_WARN_IF(NS_FAILED(rv))) {
+-        return;
+-      }
+-
+-      if (!consoleService) {
+-        rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &consoleService);
+-        if (NS_WARN_IF(NS_FAILED(rv))) {
+-          return;
+-        }
+-      }
+-
+-      consoleService->LogMessage(errorObject);
+-    }
+-  }
+-
+-  // If there are no documents to report to, at least report something to the
+-  // browser console.
+-  if (windows.IsEmpty()) {
+-    nsContentUtils::ReportToConsoleNonLocalized(aMessage,
+-                                                aFlags,
+-                                                NS_LITERAL_CSTRING("Service Workers"),
+-                                                nullptr,  // document
+-                                                uri,
+-                                                aLine,
+-                                                aLineNumber,
+-                                                aColumnNumber,
+-                                                nsContentUtils::eOMIT_LOCATION);
+-    return;
+-  }
++  ConsoleUtils::ReportForServiceWorkerScope(NS_ConvertUTF8toUTF16(aScope),
++                                            aMessage,
++                                            aFilename,
++                                            aLineNumber,
++                                            aColumnNumber,
++                                            ConsoleUtils::eError);
+ }
+ 
+ /* static */
+ void
+ ServiceWorkerManager::LocalizeAndReportToAllClients(
+                                           const nsCString& aScope,
+                                           const char* aStringKey,
+                                           const nsTArray<nsString>& aParamArray,

+ 209 - 0
mozilla-release/patches/1425965-2-59a1.patch

@@ -0,0 +1,209 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515777364 18000
+# Node ID 57b875846062f8dcb318f9edfc7d2d3a5321e7fe
+# Parent  9de31aa12fea036c7e5e4abac39c4912ba110187
+Bug 1425965 P2 Add nsIConsoleReportCollector::FlushReportsToConsoleForServiceWorkerScope(). r=baku
+
+diff --git a/dom/console/ConsoleReportCollector.cpp b/dom/console/ConsoleReportCollector.cpp
+--- a/dom/console/ConsoleReportCollector.cpp
++++ b/dom/console/ConsoleReportCollector.cpp
+@@ -1,22 +1,25 @@
+ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+ /* vim: set ts=8 sts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ #include "mozilla/ConsoleReportCollector.h"
+ 
++#include "ConsoleUtils.h"
+ #include "nsIConsoleService.h"
+ #include "nsIScriptError.h"
+ #include "nsNetUtil.h"
+ 
+ namespace mozilla {
+ 
++using mozilla::dom::ConsoleUtils;
++
+ NS_IMPL_ISUPPORTS(ConsoleReportCollector, nsIConsoleReportCollector)
+ 
+ ConsoleReportCollector::ConsoleReportCollector()
+   : mMutex("mozilla::ConsoleReportCollector")
+ {
+ }
+ 
+ void
+@@ -90,16 +93,73 @@ ConsoleReportCollector::FlushReportsToCo
+                                               uri,
+                                               EmptyString(),
+                                               report.mLineNumber,
+                                               report.mColumnNumber);
+   }
+ }
+ 
+ void
++ConsoleReportCollector::FlushReportsToConsoleForServiceWorkerScope(const nsACString& aScope,
++                                                                   ReportAction aAction)
++{
++  nsTArray<PendingReport> reports;
++
++  {
++    MutexAutoLock lock(mMutex);
++    if (aAction == ReportAction::Forget) {
++      mPendingReports.SwapElements(reports);
++    } else {
++      reports = mPendingReports;
++    }
++  }
++
++  for (uint32_t i = 0; i < reports.Length(); ++i) {
++    PendingReport& report = reports[i];
++
++    nsAutoString errorText;
++    nsresult rv;
++    if (!report.mStringParams.IsEmpty()) {
++      rv = nsContentUtils::FormatLocalizedString(report.mPropertiesFile,
++                                                 report.mMessageName.get(),
++                                                 report.mStringParams,
++                                                 errorText);
++    } else {
++      rv = nsContentUtils::GetLocalizedString(report.mPropertiesFile,
++                                              report.mMessageName.get(),
++                                              errorText);
++    }
++    if (NS_WARN_IF(NS_FAILED(rv))) {
++      continue;
++    }
++
++    ConsoleUtils::Level level = ConsoleUtils::eLog;
++    switch (report.mErrorFlags) {
++      case nsIScriptError::errorFlag:
++      case nsIScriptError::exceptionFlag:
++        level = ConsoleUtils::eError;
++        break;
++      case nsIScriptError::warningFlag:
++        level = ConsoleUtils::eWarning;
++        break;
++      default:
++        // default to log otherwise
++        break;
++    }
++
++    ConsoleUtils::ReportForServiceWorkerScope(NS_ConvertUTF8toUTF16(aScope),
++                                              errorText,
++                                              NS_ConvertUTF8toUTF16(report.mSourceFileURI),
++                                              report.mLineNumber,
++                                              report.mColumnNumber,
++                                              level);
++  }
++}
++
++void
+ ConsoleReportCollector::FlushConsoleReports(nsIDocument* aDocument,
+                                             ReportAction aAction)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+ 
+   FlushReportsToConsole(aDocument ? aDocument->InnerWindowID() : 0, aAction);
+ }
+ 
+diff --git a/dom/console/ConsoleReportCollector.h b/dom/console/ConsoleReportCollector.h
+--- a/dom/console/ConsoleReportCollector.h
++++ b/dom/console/ConsoleReportCollector.h
+@@ -26,16 +26,20 @@ public:
+                    const nsACString& aMessageName,
+                    const nsTArray<nsString>& aStringParams) override;
+ 
+   void
+   FlushReportsToConsole(uint64_t aInnerWindowID,
+                         ReportAction aAction = ReportAction::Forget) override;
+ 
+   void
++  FlushReportsToConsoleForServiceWorkerScope(const nsACString& aScope,
++                                             ReportAction aAction = ReportAction::Forget) override;
++
++  void
+   FlushConsoleReports(nsIDocument* aDocument,
+                       ReportAction aAction = ReportAction::Forget) override;
+ 
+   void
+   FlushConsoleReports(nsILoadGroup* aLoadGroup,
+                       ReportAction aAction = ReportAction::Forget) override;
+ 
+   void
+diff --git a/dom/console/nsIConsoleReportCollector.h b/dom/console/nsIConsoleReportCollector.h
+--- a/dom/console/nsIConsoleReportCollector.h
++++ b/dom/console/nsIConsoleReportCollector.h
+@@ -77,16 +77,20 @@ public:
+   //
+   // aInnerWindowID A inner window ID representing where to flush the reports.
+   // aAction        An action to determine whether to reserve the pending
+   //                reports. Defalut action is to forget the report.
+   virtual void
+   FlushReportsToConsole(uint64_t aInnerWindowID,
+                         ReportAction aAction = ReportAction::Forget) = 0;
+ 
++  virtual void
++  FlushReportsToConsoleForServiceWorkerScope(const nsACString& aScope,
++                                             ReportAction aAction = ReportAction::Forget) = 0;
++
+   // Flush all pending reports to the console.  Main thread only.
+   //
+   // aDocument      An optional document representing where to flush the
+   //                reports.  If provided, then the corresponding window's
+   //                web console will get the reports.  Otherwise the reports
+   //                go to the browser console.
+   // aAction        An action to determine whether to reserve the pending
+   //                reports. Defalut action is to forget the report.
+diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp
+--- a/netwerk/protocol/http/HttpBaseChannel.cpp
++++ b/netwerk/protocol/http/HttpBaseChannel.cpp
+@@ -2965,16 +2965,23 @@ HttpBaseChannel::AddConsoleReport(uint32
+ void
+ HttpBaseChannel::FlushReportsToConsole(uint64_t aInnerWindowID,
+                                        ReportAction aAction)
+ {
+   mReportCollector->FlushReportsToConsole(aInnerWindowID, aAction);
+ }
+ 
+ void
++HttpBaseChannel::FlushReportsToConsoleForServiceWorkerScope(const nsACString& aScope,
++                                                            ReportAction aAction)
++{
++  mReportCollector->FlushReportsToConsoleForServiceWorkerScope(aScope, aAction);
++}
++
++void
+ HttpBaseChannel::FlushConsoleReports(nsIDocument* aDocument,
+                                      ReportAction aAction)
+ {
+   mReportCollector->FlushConsoleReports(aDocument, aAction);
+ }
+ 
+ void
+ HttpBaseChannel::FlushConsoleReports(nsILoadGroup* aLoadGroup,
+diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h
+--- a/netwerk/protocol/http/HttpBaseChannel.h
++++ b/netwerk/protocol/http/HttpBaseChannel.h
+@@ -293,16 +293,20 @@ public:
+                    const nsACString& aMessageName,
+                    const nsTArray<nsString>& aStringParams) override;
+ 
+   void
+   FlushReportsToConsole(uint64_t aInnerWindowID,
+                         ReportAction aAction = ReportAction::Forget) override;
+ 
+   void
++  FlushReportsToConsoleForServiceWorkerScope(const nsACString& aScope,
++                                             ReportAction aAction = ReportAction::Forget) override;
++
++  void
+   FlushConsoleReports(nsIDocument* aDocument,
+                       ReportAction aAction = ReportAction::Forget) override;
+ 
+   void
+   FlushConsoleReports(nsILoadGroup* aLoadGroup,
+                       ReportAction aAction = ReportAction::Forget) override;
+ 
+   void

+ 210 - 0
mozilla-release/patches/1425965-3-59a1.patch

@@ -0,0 +1,210 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515777364 18000
+# Node ID 79280bcea4451e8f64d329f6ee3c46bc1321dff9
+# Parent  392509ed05f342d1cbd6c1dc58500ead3a8da702
+Bug 1425965 P3 Make ServiceWorkerManager::FlushReportsToAllClients() use FlushReportsToConsoleForServiceWorkerScope(). r=asuth
+
+diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp
+--- a/dom/fetch/Fetch.cpp
++++ b/dom/fetch/Fetch.cpp
+@@ -33,17 +33,16 @@
+ #include "mozilla/dom/Headers.h"
+ #include "mozilla/dom/MutableBlobStreamListener.h"
+ #include "mozilla/dom/Promise.h"
+ #include "mozilla/dom/PromiseWorkerProxy.h"
+ #include "mozilla/dom/Request.h"
+ #include "mozilla/dom/Response.h"
+ #include "mozilla/dom/ScriptSettings.h"
+ #include "mozilla/dom/URLSearchParams.h"
+-#include "mozilla/dom/workers/ServiceWorkerManager.h"
+ 
+ #include "BodyExtractor.h"
+ #include "FetchObserver.h"
+ #include "InternalRequest.h"
+ #include "InternalResponse.h"
+ 
+ #include "WorkerPrivate.h"
+ #include "WorkerRunnable.h"
+@@ -857,23 +856,17 @@ WorkerFetchResolver::FlushConsoleReport(
+   workers::WorkerPrivate* worker = mPromiseProxy->GetWorkerPrivate();
+   if (!worker) {
+     mReporter->FlushReportsToConsole(0);
+     return;
+   }
+ 
+   if (worker->IsServiceWorker()) {
+     // Flush to service worker
+-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+-    if (!swm) {
+-      mReporter->FlushReportsToConsole(0);
+-      return;
+-    }
+-
+-    swm->FlushReportsToAllClients(worker->ServiceWorkerScope(), mReporter);
++    mReporter->FlushReportsToConsoleForServiceWorkerScope(worker->ServiceWorkerScope());
+     return;
+   }
+ 
+   if (worker->IsSharedWorker()) {
+     // Flush to shared worker
+     worker->FlushReportsToSharedWorkers(mReporter);
+     return;
+   }
+diff --git a/dom/fetch/FetchConsumer.cpp b/dom/fetch/FetchConsumer.cpp
+--- a/dom/fetch/FetchConsumer.cpp
++++ b/dom/fetch/FetchConsumer.cpp
+@@ -2,16 +2,17 @@
+ /* 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 "Fetch.h"
+ #include "FetchConsumer.h"
+ 
++#include "mozilla/ipc/PBackgroundSharedTypes.h"
+ #include "nsIInputStreamPump.h"
+ #include "nsProxyRelease.h"
+ #include "WorkerPrivate.h"
+ #include "WorkerRunnable.h"
+ #include "WorkerScope.h"
+ #include "Workers.h"
+ 
+ namespace mozilla {
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -1760,108 +1760,16 @@ ServiceWorkerManager::LocalizeAndReportT
+                             aFilename, aLine, aLineNumber, aColumnNumber,
+                             aFlags);
+   } else {
+     NS_WARNING("Failed to format and therefore report localized error.");
+   }
+ }
+ 
+ void
+-ServiceWorkerManager::FlushReportsToAllClients(const nsACString& aScope,
+-                                               nsIConsoleReportCollector* aReporter)
+-{
+-  AutoTArray<uint64_t, 16> windows;
+-
+-  // Report errors to every controlled document.
+-  for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
+-    ServiceWorkerRegistrationInfo* reg = iter.UserData();
+-    MOZ_ASSERT(reg);
+-    if (!reg->mScope.Equals(aScope)) {
+-      continue;
+-    }
+-
+-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
+-    if (!doc || !doc->IsCurrentActiveDocument() || !doc->GetWindow()) {
+-      continue;
+-    }
+-
+-    uint64_t innerWindowId = doc->InnerWindowID();
+-    windows.AppendElement(innerWindowId);
+-
+-    aReporter->FlushReportsToConsole(
+-      innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
+-  }
+-
+-  // Report to any documents that have called .register() for this scope.  They
+-  // may not be controlled, but will still want to see error reports.
+-  WeakDocumentList* regList = mRegisteringDocuments.Get(aScope);
+-  if (regList) {
+-    for (int32_t i = regList->Length() - 1; i >= 0; --i) {
+-      nsCOMPtr<nsIDocument> doc = do_QueryReferent(regList->ElementAt(i));
+-      if (!doc) {
+-        regList->RemoveElementAt(i);
+-        continue;
+-      }
+-
+-      if (!doc->IsCurrentActiveDocument()) {
+-        continue;
+-      }
+-
+-      uint64_t innerWindowId = doc->InnerWindowID();
+-      if (windows.Contains(innerWindowId)) {
+-        continue;
+-      }
+-
+-      windows.AppendElement(innerWindowId);
+-
+-      aReporter->FlushReportsToConsole(
+-        innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
+-    }
+-
+-    if (regList->IsEmpty()) {
+-      regList = nullptr;
+-      mRegisteringDocuments.Remove(aScope);
+-    }
+-  }
+-
+-  nsresult rv;
+-  InterceptionList* intList = mNavigationInterceptions.Get(aScope);
+-  if (intList) {
+-    for (uint32_t i = 0; i < intList->Length(); ++i) {
+-      nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
+-
+-      nsCOMPtr<nsIChannel> inner;
+-      rv = channel->GetChannel(getter_AddRefs(inner));
+-      if (NS_WARN_IF(NS_FAILED(rv))) {
+-        continue;
+-      }
+-
+-      uint64_t innerWindowId = nsContentUtils::GetInnerWindowID(inner);
+-      if (innerWindowId == 0 || windows.Contains(innerWindowId)) {
+-        continue;
+-      }
+-
+-      windows.AppendElement(innerWindowId);
+-
+-      aReporter->FlushReportsToConsole(
+-        innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
+-    }
+-  }
+-
+-  // If there are no documents to report to, at least report something to the
+-  // browser console.
+-  if (windows.IsEmpty()) {
+-    aReporter->FlushReportsToConsole(0);
+-    return;
+-  }
+-
+-  aReporter->ClearConsoleReports();
+-}
+-
+-void
+ ServiceWorkerManager::HandleError(JSContext* aCx,
+                                   nsIPrincipal* aPrincipal,
+                                   const nsCString& aScope,
+                                   const nsString& aWorkerURL,
+                                   const nsString& aMessage,
+                                   const nsString& aFilename,
+                                   const nsString& aLine,
+                                   uint32_t aLineNumber,
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -260,20 +260,16 @@ public:
+                                 const char* aStringKey,
+                                 const nsTArray<nsString>& aParamArray,
+                                 uint32_t aFlags = 0x0,
+                                 const nsString& aFilename = EmptyString(),
+                                 const nsString& aLine = EmptyString(),
+                                 uint32_t aLineNumber = 0,
+                                 uint32_t aColumnNumber = 0);
+ 
+-  void
+-  FlushReportsToAllClients(const nsACString& aScope,
+-                           nsIConsoleReportCollector* aReporter);
+-
+   // Always consumes the error by reporting to consoles of all controlled
+   // documents.
+   void
+   HandleError(JSContext* aCx,
+               nsIPrincipal* aPrincipal,
+               const nsCString& aScope,
+               const nsString& aWorkerURL,
+               const nsString& aMessage,

+ 507 - 0
mozilla-release/patches/1425965-4-59a1.patch

@@ -0,0 +1,507 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515777365 18000
+# Node ID 4027039552fea211647109c3d45b4215d759bd52
+# Parent  6f132aee4f2a1aa7a39572e9fbc0ecdd2ad3a87a
+Bug 1425965 P4 Remove mControlledDocument, mRegisteringDocuments, and mNavigationIntercepts from ServiceWorkerManager. r=asuth
+
+diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
+--- a/dom/base/nsDocument.cpp
++++ b/dom/base/nsDocument.cpp
+@@ -4898,34 +4898,29 @@ nsDocument::SetScriptGlobalObject(nsIScr
+     // Also make sure to remove our onload blocker now if we haven't done it yet
+     if (mOnloadBlockCount != 0) {
+       nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
+       if (loadGroup) {
+         loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
+       }
+     }
+ 
+-    using mozilla::dom::workers::ServiceWorkerManager;
+-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+-    if (swm) {
+-      ErrorResult error;
+-      if (GetController().isSome()) {
+-        imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this);
+-        if (loader) {
+-          loader->ClearCacheForControlledDocument(this);
+-        }
+-
+-        // We may become controlled again if this document comes back out
+-        // of bfcache.  Clear our state to allow that to happen.  Only
+-        // clear this flag if we are actually controlled, though, so pages
+-        // that were force reloaded don't become controlled when they
+-        // come out of bfcache.
+-        mMaybeServiceWorkerControlled = false;
+-      }
+-      swm->MaybeStopControlling(this);
++    ErrorResult error;
++    if (GetController().isSome()) {
++      imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this);
++      if (loader) {
++        loader->ClearCacheForControlledDocument(this);
++      }
++
++      // We may become controlled again if this document comes back out
++      // of bfcache.  Clear our state to allow that to happen.  Only
++      // clear this flag if we are actually controlled, though, so pages
++      // that were force reloaded don't become controlled when they
++      // come out of bfcache.
++      mMaybeServiceWorkerControlled = false;
+     }
+   }
+ 
+   // BlockOnload() might be called before mScriptGlobalObject is set.
+   // We may need to add the blocker once mScriptGlobalObject is set.
+   bool needOnloadBlocker = !mScriptGlobalObject && aScriptGlobalObject;
+ 
+   mScriptGlobalObject = aScriptGlobalObject;
+@@ -5035,21 +5030,17 @@ nsDocument::SetScriptGlobalObject(nsIScr
+     docShell->GetLoadType(&loadType);
+ 
+     // If we are shift-reloaded, don't associate with a ServiceWorker.
+     if (IsForceReloadType(loadType)) {
+       NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
+       return;
+     }
+ 
+-    nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
+-    if (swm) {
+-      swm->MaybeStartControlling(this);
+-      mMaybeServiceWorkerControlled = true;
+-    }
++    mMaybeServiceWorkerControlled = true;
+   }
+ }
+ 
+ nsIScriptGlobalObject*
+ nsDocument::GetScriptHandlingObjectInternal() const
+ {
+   MOZ_ASSERT(!mScriptGlobalObject,
+              "Do not call this when mScriptGlobalObject is set!");
+diff --git a/dom/interfaces/base/nsIServiceWorkerManager.idl b/dom/interfaces/base/nsIServiceWorkerManager.idl
+--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
++++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
+@@ -148,34 +148,19 @@ interface nsIServiceWorkerManager : nsIS
+   nsISupports getReadyPromise(in mozIDOMWindow aWindow);
+ 
+   // Remove ready pending Promise
+   void removeReadyPromise(in mozIDOMWindow aWindow);
+ 
+   nsIServiceWorkerRegistrationInfo getRegistrationByPrincipal(in nsIPrincipal aPrincipal,
+                                                               in DOMString aScope);
+ 
+-  /**
+-   * Call this to request that document `aDoc` be controlled by a ServiceWorker
+-   * if a registration exists for it's scope.
+-   *
+-   * This MUST only be called once per document!
+-   */
+-  [notxpcom,nostdcall] void MaybeStartControlling(in nsIDocument aDoc);
+-
+   [notxpcom, nostdcall] bool StartControlling(in const_ClientInfoRef aClientInfo,
+                                               in const_ServiceWorkerDescriptorRef aServiceWorker);
+ 
+-  /**
+-   * Documents that have called MaybeStartControlling() should call this when
+-   * they are destroyed. This function may be called multiple times, and is
+-   * idempotent.
+-   */
+-  [notxpcom,nostdcall] void MaybeStopControlling(in nsIDocument aDoc);
+-
+   /*
+    * Returns a ServiceWorker.
+    * window is the window of the caller. scope is the registration's scope and must be
+    * a valid entry that window is allowed to load, otherwise this will return nullptr.
+    * These are only meant to be called from ServiceWorkerRegistration instances.
+    */
+   [noscript] nsISupports GetInstalling(in nsPIDOMWindowInner aWindow, in DOMString aScope);
+   [noscript] nsISupports GetWaiting(in nsPIDOMWindowInner aWindow, in DOMString aScope);
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -904,17 +904,16 @@ ServiceWorkerManager::Register(mozIDOMWi
+ 
+   nsAutoCString scopeKey;
+   rv = PrincipalToScopeKey(documentPrincipal, scopeKey);
+   if (NS_WARN_IF(NS_FAILED(rv))) {
+     return rv;
+   }
+ 
+   window->NoteCalledRegisterForServiceWorkerScope(cleanedScope);
+-  AddRegisteringDocument(cleanedScope, doc);
+ 
+   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
+                                                             cleanedScope);
+ 
+   RefPtr<ServiceWorkerResolveWindowPromiseOnRegisterCallback> cb =
+     new ServiceWorkerResolveWindowPromiseOnRegisterCallback(window, promise);
+ 
+   nsCOMPtr<nsILoadGroup> docLoadGroup = doc->GetDocumentLoadGroup();
+@@ -2123,28 +2122,16 @@ ServiceWorkerManager::RemoveScopeAndRegi
+         reg->mPrincipal->Equals(aRegistration->mPrincipal)) {
+       MOZ_DIAGNOSTIC_ASSERT(false,
+                             "controlled client when removing registration");
+       iter.Remove();
+       break;
+     }
+   }
+ 
+-  // Registration lifecycle is managed via mControlledClients now.  Do not
+-  // assert on on mControlledDocuments as races may cause this to still be
+-  // set when the registration is destroyed.
+-  for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
+-    ServiceWorkerRegistrationInfo* reg = iter.UserData();
+-    if (reg->mScope.Equals(aRegistration->mScope) &&
+-        reg->mPrincipal->Equals(aRegistration->mPrincipal)) {
+-      iter.Remove();
+-      break;
+-    }
+-  }
+-
+   RefPtr<ServiceWorkerRegistrationInfo> info;
+   data->mInfos.Remove(aRegistration->mScope, getter_AddRefs(info));
+   data->mOrderedScopes.RemoveElement(aRegistration->mScope);
+   swm->NotifyListenersOnUnregister(info);
+ 
+   swm->MaybeRemoveRegistrationInfo(scopeKey);
+   swm->NotifyServiceWorkerRegistrationRemoved(aRegistration);
+ }
+@@ -2155,30 +2142,16 @@ ServiceWorkerManager::MaybeRemoveRegistr
+   if (auto entry = mRegistrationInfos.Lookup(aScopeKey)) {
+     if (entry.Data()->mOrderedScopes.IsEmpty() &&
+         entry.Data()->mJobQueues.Count() == 0) {
+       entry.Remove();
+     }
+   }
+ }
+ 
+-void
+-ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc)
+-{
+-  AssertIsOnMainThread();
+-  MOZ_ASSERT(aDoc);
+-  RefPtr<ServiceWorkerRegistrationInfo> registration =
+-    GetServiceWorkerRegistrationInfo(aDoc);
+-  if (registration && registration->GetActive() &&
+-      aDoc->GetSandboxFlags() == 0) {
+-    MOZ_ASSERT(!mControlledDocuments.Contains(aDoc));
+-    StartControllingADocument(registration, aDoc);
+-  }
+-}
+-
+ bool
+ ServiceWorkerManager::StartControlling(const ClientInfo& aClientInfo,
+                                        const ServiceWorkerDescriptor& aServiceWorker)
+ {
+   AssertIsOnMainThread();
+ 
+   nsCOMPtr<nsIPrincipal> principal =
+     PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
+@@ -2194,24 +2167,16 @@ ServiceWorkerManager::StartControlling(c
+   NS_ENSURE_TRUE(registration, false);
+ 
+   StartControllingClient(aClientInfo, registration);
+ 
+   return true;
+ }
+ 
+ void
+-ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
+-{
+-  AssertIsOnMainThread();
+-  MOZ_ASSERT(aDoc);
+-  mControlledDocuments.Remove(aDoc);
+-}
+-
+-void
+ ServiceWorkerManager::MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo)
+ {
+   AssertIsOnMainThread();
+   // We perform these success path navigation update steps when the
+   // document tells us its more or less done loading.  This avoids
+   // slowing down page load and also lets pages consistently get
+   // updatefound events when they fire.
+   //
+@@ -2221,26 +2186,16 @@ ServiceWorkerManager::MaybeCheckNavigati
+   //    algorithm.
+   ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
+   if (data && data->mRegistrationInfo) {
+     data->mRegistrationInfo->MaybeScheduleUpdate();
+   }
+ }
+ 
+ void
+-ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
+-                                                nsIDocument* aDoc)
+-{
+-  MOZ_ASSERT(aRegistration);
+-  MOZ_ASSERT(aDoc);
+-
+-  mControlledDocuments.Put(aDoc, aRegistration);
+-}
+-
+-void
+ ServiceWorkerManager::StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration)
+ {
+   aRegistration->StopControllingClient();
+   if (aRegistration->IsControllingClients() || !aRegistration->IsIdle()) {
+     return;
+   }
+ 
+   if (aRegistration->mPendingUninstall) {
+@@ -2573,18 +2528,16 @@ ServiceWorkerManager::DispatchFetchEvent
+       }
+ 
+       // But we also note the reserved state on the LoadInfo.  This allows the
+       // ClientSource to be updated immediately after the nsIChannel starts.
+       // This is necessary to have the correct controller in place for immediate
+       // follow-on requests.
+       loadInfo->SetController(serviceWorker->Descriptor());
+     }
+-
+-    AddNavigationInterception(serviceWorker->Scope(), aChannel);
+   }
+ 
+   if (NS_WARN_IF(aRv.Failed())) {
+     return;
+   }
+ 
+   MOZ_DIAGNOSTIC_ASSERT(serviceWorker);
+ 
+@@ -3035,17 +2988,16 @@ ServiceWorkerManager::MaybeClaimClient(n
+                         getter_AddRefs(controllingRegistration));
+ 
+   if (aWorkerRegistration != matchingRegistration ||
+       aWorkerRegistration == controllingRegistration) {
+     ref = GenericPromise::CreateAndResolve(true, __func__);
+     return ref.forget();
+   }
+ 
+-  StartControllingADocument(aWorkerRegistration, aDocument);
+   ref = StartControllingClient(clientInfo.ref(), aWorkerRegistration);
+   return ref.forget();
+ }
+ 
+ already_AddRefed<GenericPromise>
+ ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDoc,
+                                        const ServiceWorkerDescriptor& aServiceWorker)
+ {
+@@ -3545,112 +3497,16 @@ ServiceWorkerManager::NotifyListenersOnU
+                                         nsIServiceWorkerRegistrationInfo* aInfo)
+ {
+   nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners);
+   for (size_t index = 0; index < listeners.Length(); ++index) {
+     listeners[index]->OnUnregister(aInfo);
+   }
+ }
+ 
+-void
+-ServiceWorkerManager::AddRegisteringDocument(const nsACString& aScope,
+-                                             nsIDocument* aDoc)
+-{
+-  AssertIsOnMainThread();
+-  MOZ_ASSERT(!aScope.IsEmpty());
+-  MOZ_ASSERT(aDoc);
+-
+-  WeakDocumentList* list = mRegisteringDocuments.LookupOrAdd(aScope);
+-  MOZ_ASSERT(list);
+-
+-  for (int32_t i = list->Length() - 1; i >= 0; --i) {
+-    nsCOMPtr<nsIDocument> existing = do_QueryReferent(list->ElementAt(i));
+-    if (!existing) {
+-      list->RemoveElementAt(i);
+-      continue;
+-    }
+-    if (existing == aDoc) {
+-      return;
+-    }
+-  }
+-
+-  list->AppendElement(do_GetWeakReference(aDoc));
+-}
+-
+-class ServiceWorkerManager::InterceptionReleaseHandle final : public nsISupports
+-{
+-  const nsCString mScope;
+-
+-  // Weak reference to channel is safe, because the channel holds a
+-  // reference to this object.  Also, the pointer is only used for
+-  // comparison purposes.
+-  nsIInterceptedChannel* mChannel;
+-
+-  ~InterceptionReleaseHandle()
+-  {
+-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+-    if (swm) {
+-      swm->RemoveNavigationInterception(mScope, mChannel);
+-    }
+-  }
+-
+-public:
+-  InterceptionReleaseHandle(const nsACString& aScope,
+-                            nsIInterceptedChannel* aChannel)
+-    : mScope(aScope)
+-    , mChannel(aChannel)
+-  {
+-    AssertIsOnMainThread();
+-    MOZ_ASSERT(!aScope.IsEmpty());
+-    MOZ_ASSERT(mChannel);
+-  }
+-
+-  NS_DECL_ISUPPORTS
+-};
+-
+-NS_IMPL_ISUPPORTS0(ServiceWorkerManager::InterceptionReleaseHandle);
+-
+-void
+-ServiceWorkerManager::AddNavigationInterception(const nsACString& aScope,
+-                                                nsIInterceptedChannel* aChannel)
+-{
+-  AssertIsOnMainThread();
+-  MOZ_ASSERT(!aScope.IsEmpty());
+-  MOZ_ASSERT(aChannel);
+-
+-  InterceptionList* list =
+-    mNavigationInterceptions.LookupOrAdd(aScope);
+-  MOZ_ASSERT(list);
+-  MOZ_ASSERT(!list->Contains(aChannel));
+-
+-  nsCOMPtr<nsISupports> releaseHandle =
+-    new InterceptionReleaseHandle(aScope, aChannel);
+-  aChannel->SetReleaseHandle(releaseHandle);
+-
+-  list->AppendElement(aChannel);
+-}
+-
+-void
+-ServiceWorkerManager::RemoveNavigationInterception(const nsACString& aScope,
+-                                                   nsIInterceptedChannel* aChannel)
+-{
+-  AssertIsOnMainThread();
+-  MOZ_ASSERT(aChannel);
+-  InterceptionList* list =
+-    mNavigationInterceptions.Get(aScope);
+-  if (list) {
+-    MOZ_ALWAYS_TRUE(list->RemoveElement(aChannel));
+-    MOZ_ASSERT(!list->Contains(aChannel));
+-    if (list->IsEmpty()) {
+-      list = nullptr;
+-      mNavigationInterceptions.Remove(aScope);
+-    }
+-  }
+-}
+-
+ class UpdateTimerCallback final : public nsITimerCallback
+                                 , public nsINamed
+ {
+   nsCOMPtr<nsIPrincipal> mPrincipal;
+   const nsCString mScope;
+ 
+   ~UpdateTimerCallback()
+   {
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -99,50 +99,31 @@ public:
+   NS_DECL_NSISERVICEWORKERMANAGER
+   NS_DECL_NSIOBSERVER
+ 
+   struct RegistrationDataPerPrincipal;
+   nsClassHashtable<nsCStringHashKey, RegistrationDataPerPrincipal> mRegistrationInfos;
+ 
+   nsTObserverArray<ServiceWorkerRegistrationListener*> mServiceWorkerRegistrationListeners;
+ 
+-  nsRefPtrHashtable<nsISupportsHashKey, ServiceWorkerRegistrationInfo> mControlledDocuments;
+-
+   struct ControlledClientData
+   {
+     RefPtr<ClientHandle> mClientHandle;
+     RefPtr<ServiceWorkerRegistrationInfo> mRegistrationInfo;
+ 
+     ControlledClientData(ClientHandle* aClientHandle,
+                          ServiceWorkerRegistrationInfo* aRegistrationInfo)
+       : mClientHandle(aClientHandle)
+       , mRegistrationInfo(aRegistrationInfo)
+     {
+     }
+   };
+ 
+   nsClassHashtable<nsIDHashKey, ControlledClientData> mControlledClients;
+ 
+-  // Track all documents that have attempted to register a service worker for a
+-  // given scope.
+-  typedef nsTArray<nsCOMPtr<nsIWeakReference>> WeakDocumentList;
+-  nsClassHashtable<nsCStringHashKey, WeakDocumentList> mRegisteringDocuments;
+-
+-  // Track all intercepted navigation channels for a given scope.  Channels are
+-  // placed in the appropriate list before dispatch the FetchEvent to the worker
+-  // thread and removed once FetchEvent processing dispatches back to the main
+-  // thread.
+-  //
+-  // Note: Its safe to use weak references here because a RAII-style callback
+-  //       is registered with the channel before its added to this list.  We
+-  //       are guaranteed the callback will fire before and remove the ref
+-  //       from this list before the channel is destroyed.
+-  typedef nsTArray<nsIInterceptedChannel*> InterceptionList;
+-  nsClassHashtable<nsCStringHashKey, InterceptionList> mNavigationInterceptions;
+-
+   bool
+   IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI);
+ 
+   // Return true if the given content process could potentially be executing
+   // service worker code with the given principal.  At the current time, this
+   // just means that we have any registration for the origin, regardless of
+   // scope.  This is a very weak guarantee but is the best we can do when push
+   // notifications can currently spin up a service worker in content processes
+@@ -390,20 +371,16 @@ private:
+   void
+   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
+                                             WhichServiceWorker aWhichOnes);
+ 
+   void
+   NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+   void
+-  StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
+-                            nsIDocument* aDoc);
+-
+-  void
+   StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+   already_AddRefed<ServiceWorkerRegistrationInfo>
+   GetServiceWorkerRegistrationInfo(nsPIDOMWindowInner* aWindow);
+ 
+   already_AddRefed<ServiceWorkerRegistrationInfo>
+   GetServiceWorkerRegistrationInfo(nsIDocument* aDoc);
+ 
+@@ -480,29 +457,16 @@ private:
+ 
+   void
+   NotifyListenersOnRegister(nsIServiceWorkerRegistrationInfo* aRegistration);
+ 
+   void
+   NotifyListenersOnUnregister(nsIServiceWorkerRegistrationInfo* aRegistration);
+ 
+   void
+-  AddRegisteringDocument(const nsACString& aScope, nsIDocument* aDoc);
+-
+-  class InterceptionReleaseHandle;
+-
+-  void
+-  AddNavigationInterception(const nsACString& aScope,
+-                            nsIInterceptedChannel* aChannel);
+-
+-  void
+-  RemoveNavigationInterception(const nsACString& aScope,
+-                               nsIInterceptedChannel* aChannel);
+-
+-  void
+   ScheduleUpdateTimer(nsIPrincipal* aPrincipal, const nsACString& aScope);
+ 
+   void
+   UpdateTimerFired(nsIPrincipal* aPrincipal, const nsACString& aScope);
+ 
+   void
+   MaybeSendUnregister(nsIPrincipal* aPrincipal, const nsACString& aScope);
+ 

+ 108 - 0
mozilla-release/patches/1425965-5-59a1.patch

@@ -0,0 +1,108 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515777365 18000
+# Node ID 241b9e419ca4a960144320842839f4ae39be6493
+# Parent  a854d4e09e55f738b537d3af6d426d813ec210e5
+Bug 1425965 P5 Make SimpleTest.monitorConsole() and SpecialPowers.registerConsoleListener() observe dom/console events. r=baku
+
+diff --git a/testing/specialpowers/content/specialpowersAPI.js b/testing/specialpowers/content/specialpowersAPI.js
+--- a/testing/specialpowers/content/specialpowersAPI.js
++++ b/testing/specialpowers/content/specialpowersAPI.js
+@@ -302,57 +302,74 @@ SpecialPowersHandler.prototype = {
+ // tidy, XPCOM-hiding way.  Messages that are nsIScriptError objects
+ // have their properties exposed in detail.  It also auto-unregisters
+ // itself when it receives a "sentinel" message.
+ function SPConsoleListener(callback) {
+   this.callback = callback;
+ }
+ 
+ SPConsoleListener.prototype = {
+-  observe(msg) {
++  // Overload the observe method for both nsIConsoleListener and nsIObserver.
++  // The topic will be null for nsIConsoleListener.
++  observe(msg, topic) {
+     let m = { message: msg.message,
+               errorMessage: null,
+               sourceName: null,
+               sourceLine: null,
+               lineNumber: null,
+               columnNumber: null,
+               category: null,
+               windowID: null,
+               isScriptError: false,
++              isConsoleEvent: false,
+               isWarning: false,
+               isException: false,
+               isStrict: false };
+     if (msg instanceof Ci.nsIScriptError) {
+       m.errorMessage  = msg.errorMessage;
+       m.sourceName    = msg.sourceName;
+       m.sourceLine    = msg.sourceLine;
+       m.lineNumber    = msg.lineNumber;
+       m.columnNumber  = msg.columnNumber;
+       m.category      = msg.category;
+       m.windowID      = msg.outerWindowID;
+       m.innerWindowID = msg.innerWindowID;
+       m.isScriptError = true;
+       m.isWarning     = ((msg.flags & Ci.nsIScriptError.warningFlag) === 1);
+       m.isException   = ((msg.flags & Ci.nsIScriptError.exceptionFlag) === 1);
+       m.isStrict      = ((msg.flags & Ci.nsIScriptError.strictFlag) === 1);
++    } else if (topic === "console-api-log-event") {
++      // This is a dom/console event.
++      let unwrapped = msg.wrappedJSObject;
++      m.errorMessage   = unwrapped.arguments[0];
++      m.sourceName     = unwrapped.filename;
++      m.lineNumber     = unwrapped.lineNumber;
++      m.columnNumber   = unwrapped.columnNumber;
++      m.windowID       = unwrapped.ID;
++      m.innerWindowID  = unwrapped.innerID;
++      m.isConsoleEvent = true;
++      m.isWarning      = unwrapped.level === "warning";
+     }
+ 
+     Object.freeze(m);
+ 
+     // Run in a separate runnable since console listeners aren't
+     // supposed to touch content and this one might.
+     Services.tm.dispatchToMainThread(() => {
+       this.callback.call(undefined, m);
+     });
+ 
+-    if (!m.isScriptError && m.message === "SENTINEL")
++    if (!m.isScriptError && !m.isConsoleEvent && m.message === "SENTINEL") {
++      Services.obs.removeObserver(this, "console-api-log-event");
+       Services.console.unregisterListener(this);
++    }
+   },
+ 
+-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleListener])
++  QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleListener,
++                                         Ci.nsIObserver])
+ };
+ 
+ function wrapCallback(cb) {
+   return function SpecialPowersCallbackWrapper() {
+     var args = Array.prototype.map.call(arguments, wrapIfUnwrapped);
+     return cb.apply(this, args);
+   }
+ }
+@@ -1419,16 +1436,19 @@ SpecialPowersAPI.prototype = {
+   // call to postConsoleSentinel; when the callback receives the
+   // sentinel it will unregister itself (_after_ calling the
+   // callback).  SimpleTest.expectConsoleMessages does this for you.
+   // If you register more than one console listener, a call to
+   // postConsoleSentinel will zap all of them.
+   registerConsoleListener(callback) {
+     let listener = new SPConsoleListener(callback);
+     Services.console.registerListener(listener);
++
++    // listen for dom/console events as well
++    Services.obs.addObserver(listener, "console-api-log-event");
+   },
+   postConsoleSentinel() {
+     Services.console.logStringMessage("SENTINEL");
+   },
+   resetConsole() {
+     Services.console.reset();
+   },
+ 

+ 115 - 0
mozilla-release/patches/1425965-6-59a1.patch

@@ -0,0 +1,115 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515777366 18000
+# Node ID 9972008ac10510753a6adda9af0f21b33ee861e8
+# Parent  be03f8b1739fb16f203784040ca327f2a4248a90
+Bug 1425965 P6 Change test_fetch_integrity.html not to expect exact window ID values in console messages. r=asuth
+
+diff --git a/dom/workers/test/serviceworkers/test_fetch_integrity.html b/dom/workers/test/serviceworkers/test_fetch_integrity.html
+--- a/dom/workers/test/serviceworkers/test_fetch_integrity.html
++++ b/dom/workers/test/serviceworkers/test_fetch_integrity.html
+@@ -15,25 +15,23 @@
+ "use strict";
+ 
+ let security_localizer =
+   stringBundleService.createBundle("chrome://global/locale/security/security.properties");
+ 
+ function expect_security_console_message(/* msgId, args, ... */) {
+   let expectations = [];
+   // process repeated paired arguments of: msgId, args
+-  for (let i = 0; i < arguments.length; i += 4) {
++  for (let i = 0; i < arguments.length; i += 3) {
+     let msgId = arguments[i];
+     let args = arguments[i + 1];
+     let filename = arguments[i + 2];
+-    let windowId = arguments[i + 3];
+     expectations.push({
+       errorMessage: security_localizer.formatStringFromName(msgId, args, args.length),
+       sourceName: filename,
+-      windowID: windowId
+     });
+   }
+   return new Promise(resolve => {
+     SimpleTest.monitorConsole(resolve, expectations);
+   });
+ }
+ 
+ // (This doesn't really need to be its own task, but it allows the actual test
+@@ -55,38 +53,31 @@ add_task(async function test_integrity_s
+   await waitForState(worker, 'activated');
+   worker.postMessage('claim');
+   await waitForControlled(window);
+ 
+   info("Test for mNavigationInterceptions.")
+   // The client_win will reload to another URL after opening filename2.
+   let client_win = window.open(filename2);
+ 
+-  // XXX windowID should be innerWindowID
+-  let mainWindowID = SpecialPowers.getDOMWindowUtils(window).outerWindowID;
+-  let clientWindowID = SpecialPowers.getDOMWindowUtils(client_win).outerWindowID;
+   let expectedMessage = expect_security_console_message(
+     "MalformedIntegrityHash",
+     ["abc"],
+     filename,
+-    mainWindowID,
+     "NoValidMetadata",
+     [""],
+     filename,
+-    mainWindowID
+   );
+   let expectedMessage2 = expect_security_console_message(
+     "MalformedIntegrityHash",
+     ["abc"],
+     filename,
+-    clientWindowID,
+     "NoValidMetadata",
+     [""],
+     filename,
+-    clientWindowID
+   );
+ 
+   info("Test for mControlledDocuments and report error message to console.");
+   // The fetch will succeed because the integrity value is invalid and we are
+   // looking for the console message regarding the bad integrity value.
+   await fetch("fail.html");
+ 
+   await wait_for_expected_message(expectedMessage);
+@@ -122,38 +113,31 @@ add_task(async function test_integrity_s
+         resolve();
+       } else {
+         reject();
+       }
+     }
+   });
+   await waitForBothConnected;
+ 
+-  // XXX windowID should be innerWindowID
+-  let mainWindowID = SpecialPowers.getDOMWindowUtils(window).outerWindowID;
+-  let clientWindowID = SpecialPowers.getDOMWindowUtils(client_win).outerWindowID;
+   let expectedMessage = expect_security_console_message(
+     "MalformedIntegrityHash",
+     ["abc"],
+     filename,
+-    mainWindowID,
+     "NoValidMetadata",
+     [""],
+     filename,
+-    mainWindowID
+   );
+   let expectedMessage2 = expect_security_console_message(
+     "MalformedIntegrityHash",
+     ["abc"],
+     filename,
+-    clientWindowID,
+     "NoValidMetadata",
+     [""],
+     filename,
+-    clientWindowID
+   );
+ 
+   info("Start to fetch a URL with wrong integrity.")
+   sharedWorker.port.start();
+   sharedWorker.port.postMessage("StartFetchWithWrongIntegrity");
+ 
+   let waitForSRIFailed = new Promise((resolve) => {
+     sharedWorker.port.onmessage = function (e) {

+ 35 - 0
mozilla-release/patches/1425965-7-59a1.patch

@@ -0,0 +1,35 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515777366 18000
+# Node ID d16fe1a6931e5dadaeddafd626ef5bc0bb8bf8ea
+# Parent  0298e5af5fc702c3885a79bdbdd24b648405fdcc
+Bug 1425965 P7 Fix dom/push/test/test_error_reporting.html to expect new console reporting mechanism. r=asuth
+
+diff --git a/dom/push/test/test_error_reporting.html b/dom/push/test/test_error_reporting.html
+--- a/dom/push/test/test_error_reporting.html
++++ b/dom/push/test/test_error_reporting.html
+@@ -74,20 +74,22 @@ http://creativecommons.org/licenses/publ
+     reason = await waitForDeliveryError({ type: "rejection" });
+     is(reason, SpecialPowers.Ci.nsIPushErrorReporter.DELIVERY_UNHANDLED_REJECTION,
+       "Should report unhandled rejections");
+   });
+ 
+   add_task(async function reportDecryptionError() {
+     var message = await new Promise(resolve => {
+       SpecialPowers.registerConsoleListener(message => {
+-        if (!message.isScriptError) {
++        if (!message.isScriptError && !message.isConsoleEvent) {
+           return;
+         }
+-        if (message.innerWindowID == controlledFrame.innerWindowId()) {
++        const scope = "http://mochi.test:8888/tests/dom/push/test/";
++        if (message.innerWindowID === "ServiceWorker" &&
++            message.windowID === scope) {
+           SpecialPowers.postConsoleSentinel();
+           resolve(message);
+         }
+       });
+ 
+       var principal = SpecialPowers.wrap(document).nodePrincipal;
+       pushNotifier.notifyError(registration.scope, principal, "Push error",
+         SpecialPowers.Ci.nsIScriptError.errorFlag);

+ 172 - 0
mozilla-release/patches/1425975-01-59a1.patch

@@ -0,0 +1,172 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172220 18000
+# Node ID 21042078d8070e0e704ebacfc7abd20968d42fda
+# Parent  c3cc65e4bc2570dd52af78aabc30e47c6d9167ed
+Bug 1425975 P1 Add ClientHandle::OnDetach() which returns a MozPromise that resolves on actor destruction. r=asuth
+
+diff --git a/dom/clients/manager/ClientHandle.cpp b/dom/clients/manager/ClientHandle.cpp
+--- a/dom/clients/manager/ClientHandle.cpp
++++ b/dom/clients/manager/ClientHandle.cpp
+@@ -60,16 +60,26 @@ ClientHandle::StartOp(const ClientOpCons
+   }, [promise] {
+     promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
+   });
+ 
+   RefPtr<ClientOpPromise> ref = promise.get();
+   return ref.forget();
+ }
+ 
++void
++ClientHandle::OnShutdownThing()
++{
++  NS_ASSERT_OWNINGTHREAD(ClientHandle);
++  if (!mDetachPromise) {
++    return;
++  }
++  mDetachPromise->Resolve(true, __func__);
++}
++
+ ClientHandle::ClientHandle(ClientManager* aManager,
+                            nsISerialEventTarget* aSerialEventTarget,
+                            const ClientInfo& aClientInfo)
+   : mManager(aManager)
+   , mSerialEventTarget(aSerialEventTarget)
+   , mClientInfo(aClientInfo)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(mManager);
+@@ -177,10 +187,26 @@ ClientHandle::PostMessage(StructuredClon
+     }, [outerPromise](const ClientOpResult& aResult) {
+       outerPromise->Reject(aResult.get_nsresult(), __func__);
+     });
+ 
+   ref = outerPromise.get();
+   return ref.forget();
+ }
+ 
++RefPtr<GenericPromise>
++ClientHandle::OnDetach()
++{
++  NS_ASSERT_OWNINGTHREAD(ClientSource);
++
++  if (!mDetachPromise) {
++    mDetachPromise = new GenericPromise::Private(__func__);
++    if (IsShutdown()) {
++      mDetachPromise->Resolve(true, __func__);
++    }
++  }
++
++  RefPtr<GenericPromise> ref(mDetachPromise);
++  return Move(ref);
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientHandle.h b/dom/clients/manager/ClientHandle.h
+--- a/dom/clients/manager/ClientHandle.h
++++ b/dom/clients/manager/ClientHandle.h
+@@ -37,26 +37,31 @@ class StructuredCloneData;
+ // is destroyed, but this could be added in the future.
+ class ClientHandle final : public ClientThing<ClientHandleChild>
+ {
+   friend class ClientManager;
+   friend class ClientHandleChild;
+ 
+   RefPtr<ClientManager> mManager;
+   nsCOMPtr<nsISerialEventTarget> mSerialEventTarget;
++  RefPtr<GenericPromise::Private> mDetachPromise;
+   ClientInfo mClientInfo;
+ 
+   ~ClientHandle();
+ 
+   void
+   Shutdown();
+ 
+   already_AddRefed<ClientOpPromise>
+   StartOp(const ClientOpConstructorArgs& aArgs);
+ 
++  // ClientThing interface
++  void
++  OnShutdownThing() override;
++
+   // Private methods called by ClientHandleChild
+   void
+   ExecutionReady(const ClientInfo& aClientInfo);
+ 
+   // Private methods called by ClientManager
+   ClientHandle(ClientManager* aManager,
+                nsISerialEventTarget* aSerialEventTarget,
+                const ClientInfo& aClientInfo);
+@@ -85,15 +90,26 @@ public:
+   // dispatched to the Client's navigator.serviceWorker event target.  The
+   // returned promise will resolve if the MessageEvent is dispatched or if
+   // it triggers an error handled in the Client's context.  Other errors
+   // will result in the promise rejecting.
+   RefPtr<GenericPromise>
+   PostMessage(ipc::StructuredCloneData& aData,
+               const ServiceWorkerDescriptor& aSource);
+ 
++  // Return a Promise that resolves when the ClientHandle object is detached
++  // from its remote actors.  This will happen if the ClientSource is destroyed
++  // and triggers the cleanup of the handle actors.  It will also naturally
++  // happen when the ClientHandle is de-referenced and tears down its own
++  // actors.
++  //
++  // Note: This method can only be called on the ClientHandle owning thread,
++  //       but the MozPromise lets you Then() to another thread.
++  RefPtr<GenericPromise>
++  OnDetach();
++
+   NS_INLINE_DECL_REFCOUNTING(ClientHandle);
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientHandle_h
+diff --git a/dom/clients/manager/ClientThing.h b/dom/clients/manager/ClientThing.h
+--- a/dom/clients/manager/ClientThing.h
++++ b/dom/clients/manager/ClientThing.h
+@@ -85,31 +85,42 @@ protected:
+ 
+     // If we are shutdown before the actor, then clear the weak references
+     // between the actor and the thing.
+     if (mActor) {
+       mActor->RevokeOwner(this);
+       mActor->MaybeStartTeardown();
+       mActor = nullptr;
+     }
++
++    OnShutdownThing();
++  }
++
++  // Allow extending classes to take action when shutdown.
++  virtual void
++  OnShutdownThing()
++  {
++    // by default do nothing
+   }
+ 
+ public:
+   // Clear the weak references between the thing and its IPC actor.
+   void
+   RevokeActor(ActorType* aActor)
+   {
+     MOZ_DIAGNOSTIC_ASSERT(mActor);
+     MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
+     mActor->RevokeOwner(this);
+     mActor = nullptr;
+ 
+     // Also consider the ClientThing shutdown.  We simply set the flag
+     // instead of calling ShutdownThing() to avoid calling MaybeStartTeardown()
+     // on the destroyed actor.
+     mShutdown = true;
++
++    OnShutdownThing();
+   }
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientThing_h

+ 194 - 0
mozilla-release/patches/1425975-02-59a1.patch

@@ -0,0 +1,194 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172220 18000
+# Node ID ef7cf1394d0fd6fbd9e97bed3cc705cc439107a5
+# Parent  92298e0b576f0716d79cbb10ff47455781d9a38a
+Bug 1425975 P2 Add ServiceWorkerManager mControlledClients to track controlled ClientHandle references. r=asuth
+
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -309,16 +309,51 @@ ServiceWorkerManager::Init(ServiceWorker
+     MaybeStartShutdown();
+     return;
+   }
+ 
+   mActor = static_cast<ServiceWorkerManagerChild*>(actor);
+ }
+ 
+ void
++ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo,
++                                             ServiceWorkerRegistrationInfo* aRegistrationInfo)
++{
++  RefPtr<ClientHandle> clientHandle =
++    ClientManager::CreateHandle(aClientInfo,
++                                SystemGroup::EventTargetFor(TaskCategory::Other));
++
++  auto entry = mControlledClients.LookupForAdd(aClientInfo.Id());
++  if (entry) {
++    entry.Data()->mRegistrationInfo = aRegistrationInfo;
++  } else {
++    entry.OrInsert([&] {
++      return new ControlledClientData(clientHandle, aRegistrationInfo);
++    });
++
++    RefPtr<ServiceWorkerManager> self(this);
++    clientHandle->OnDetach()->Then(
++      SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
++      [self = Move(self), aClientInfo] {
++        self->StopControllingClient(aClientInfo);
++      });
++  }
++}
++
++void
++ServiceWorkerManager::StopControllingClient(const ClientInfo& aClientInfo)
++{
++  auto entry = mControlledClients.Lookup(aClientInfo.Id());
++  if (!entry) {
++    return;
++  }
++  entry.Remove();
++}
++
++void
+ ServiceWorkerManager::MaybeStartShutdown()
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+ 
+   if (mShuttingDown) {
+     return;
+   }
+ 
+@@ -2357,33 +2392,45 @@ ServiceWorkerManager::StartControllingAD
+   MOZ_ASSERT(aRegistration);
+   MOZ_ASSERT(aDoc);
+ 
+ #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+   auto storageAllowed = nsContentUtils::StorageAllowedForDocument(aDoc);
+   MOZ_DIAGNOSTIC_ASSERT(storageAllowed == nsContentUtils::StorageAccess::eAllow);
+ #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ 
+-  RefPtr<GenericPromise> ref = GenericPromise::CreateAndResolve(true, __func__);
++  RefPtr<GenericPromise> ref;
++
++  ServiceWorkerInfo* activeWorker = aRegistration->GetActive();
++  if (NS_WARN_IF(!activeWorker)) {
++    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
++                                          __func__);
++    return ref.forget();
++  }
++
++  Maybe<ClientInfo> clientInfo = aDoc->GetClientInfo();
++  if (NS_WARN_IF(clientInfo.isNothing())) {
++    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
++                                          __func__);
++    return ref.forget();
++  }
+ 
+   aRegistration->StartControllingADocument();
+   mControlledDocuments.Put(aDoc, aRegistration);
+ 
++  StartControllingClient(clientInfo.ref(), aRegistration);
++
+   // Mark the document's ClientSource as controlled using the ClientHandle
+   // interface.  While we could get at the ClientSource directly from the
+   // document here, our goal is to move ServiceWorkerManager to a separate
+   // process.  Using the ClientHandle supports this remote operation.
+-  ServiceWorkerInfo* activeWorker = aRegistration->GetActive();
+-  Maybe<ClientInfo> clientInfo = aDoc->GetClientInfo();
+-  if (activeWorker && clientInfo.isSome()) {
+-    RefPtr<ClientHandle> clientHandle =
+-      ClientManager::CreateHandle(clientInfo.ref(),
+-                                  SystemGroup::EventTargetFor(TaskCategory::Other));
+-    ref = Move(clientHandle->Control(activeWorker->Descriptor()));
+-  }
++  RefPtr<ClientHandle> clientHandle =
++    ClientManager::CreateHandle(clientInfo.ref(),
++                                SystemGroup::EventTargetFor(TaskCategory::Other));
++  ref = Move(clientHandle->Control(activeWorker->Descriptor()));
+ 
+   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
+   return Move(ref);
+ }
+ 
+ void
+ ServiceWorkerManager::StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration)
+ {
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -16,16 +16,17 @@
+ #include "mozilla/ConsoleReportCollector.h"
+ #include "mozilla/LinkedList.h"
+ #include "mozilla/MozPromise.h"
+ #include "mozilla/Preferences.h"
+ #include "mozilla/TypedEnumBits.h"
+ #include "mozilla/UniquePtr.h"
+ #include "mozilla/WeakPtr.h"
+ #include "mozilla/dom/BindingUtils.h"
++#include "mozilla/dom/ClientHandle.h"
+ #include "mozilla/dom/Promise.h"
+ #include "mozilla/dom/ServiceWorkerCommon.h"
+ #include "mozilla/dom/ServiceWorkerRegistrar.h"
+ #include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
+ #include "mozilla/dom/workers/ServiceWorkerRegistrationInfo.h"
+ #include "mozilla/ipc/BackgroundUtils.h"
+ #include "nsClassHashtable.h"
+ #include "nsDataHashtable.h"
+@@ -100,16 +101,31 @@ public:
+ 
+   struct RegistrationDataPerPrincipal;
+   nsClassHashtable<nsCStringHashKey, RegistrationDataPerPrincipal> mRegistrationInfos;
+ 
+   nsTObserverArray<ServiceWorkerRegistrationListener*> mServiceWorkerRegistrationListeners;
+ 
+   nsRefPtrHashtable<nsISupportsHashKey, ServiceWorkerRegistrationInfo> mControlledDocuments;
+ 
++  struct ControlledClientData
++  {
++    RefPtr<ClientHandle> mClientHandle;
++    RefPtr<ServiceWorkerRegistrationInfo> mRegistrationInfo;
++
++    ControlledClientData(ClientHandle* aClientHandle,
++                         ServiceWorkerRegistrationInfo* aRegistrationInfo)
++      : mClientHandle(aClientHandle)
++      , mRegistrationInfo(aRegistrationInfo)
++    {
++    }
++  };
++
++  nsClassHashtable<nsIDHashKey, ControlledClientData> mControlledClients;
++
+   // Track all documents that have attempted to register a service worker for a
+   // given scope.
+   typedef nsTArray<nsCOMPtr<nsIWeakReference>> WeakDocumentList;
+   nsClassHashtable<nsCStringHashKey, WeakDocumentList> mRegisteringDocuments;
+ 
+   // Track all intercepted navigation channels for a given scope.  Channels are
+   // placed in the appropriate list before dispatch the FetchEvent to the worker
+   // thread and removed once FetchEvent processing dispatches back to the main
+@@ -324,16 +340,23 @@ public:
+ private:
+   ServiceWorkerManager();
+   ~ServiceWorkerManager();
+ 
+   void
+   Init(ServiceWorkerRegistrar* aRegistrar);
+ 
+   void
++  StartControllingClient(const ClientInfo& aClientInfo,
++                         ServiceWorkerRegistrationInfo* aRegistrationInfo);
++
++  void
++  StopControllingClient(const ClientInfo& aClientInfo);
++
++  void
+   MaybeStartShutdown();
+ 
+   already_AddRefed<ServiceWorkerJobQueue>
+   GetOrCreateJobQueue(const nsACString& aOriginSuffix,
+                       const nsACString& aScope);
+ 
+   void
+   MaybeRemoveRegistrationInfo(const nsACString& aScopeKey);

+ 130 - 0
mozilla-release/patches/1425975-03-59a1.patch

@@ -0,0 +1,130 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172220 18000
+# Node ID 818fd73be280cca813edeea710596876ce91f234
+# Parent  622bc2fa12206d667cca9c30a25892158a2aac69
+Bug 1425975 P3 Refactor ServiceWorkerManager::GetDocumentRegistration() to GetClientRegistration(). r=asuth
+
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -1462,18 +1462,23 @@ ServiceWorkerManager::GetActiveWorkerInf
+   return registration->GetActive();
+ }
+ 
+ ServiceWorkerInfo*
+ ServiceWorkerManager::GetActiveWorkerInfoForDocument(nsIDocument* aDocument)
+ {
+   AssertIsOnMainThread();
+ 
++  Maybe<ClientInfo> clientInfo(aDocument->GetClientInfo());
++  if (clientInfo.isNothing()) {
++    return nullptr;
++  }
++
+   RefPtr<ServiceWorkerRegistrationInfo> registration;
+-  GetDocumentRegistration(aDocument, getter_AddRefs(registration));
++  GetClientRegistration(clientInfo.ref(), getter_AddRefs(registration));
+ 
+   if (!registration) {
+     return nullptr;
+   }
+ 
+   return registration->GetActive();
+ }
+ 
+@@ -2827,30 +2832,31 @@ ServiceWorkerManager::IsAvailable(nsIPri
+   MOZ_ASSERT(aURI);
+ 
+   RefPtr<ServiceWorkerRegistrationInfo> registration =
+     GetServiceWorkerRegistrationInfo(aPrincipal, aURI);
+   return registration && registration->GetActive();
+ }
+ 
+ nsresult
+-ServiceWorkerManager::GetDocumentRegistration(nsIDocument* aDoc,
+-                                              ServiceWorkerRegistrationInfo** aRegistrationInfo)
++ServiceWorkerManager::GetClientRegistration(const ClientInfo& aClientInfo,
++                                            ServiceWorkerRegistrationInfo** aRegistrationInfo)
+ {
+-  RefPtr<ServiceWorkerRegistrationInfo> registration;
+-  if (!mControlledDocuments.Get(aDoc, getter_AddRefs(registration))) {
++  ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
++  if (!data || !data->mRegistrationInfo) {
+     return NS_ERROR_NOT_AVAILABLE;
+   }
+ 
+   // If the document is controlled, the current worker MUST be non-null.
+-  if (!registration->GetActive()) {
++  if (!data->mRegistrationInfo->GetActive()) {
+     return NS_ERROR_NOT_AVAILABLE;
+   }
+ 
+-  registration.forget(aRegistrationInfo);
++  RefPtr<ServiceWorkerRegistrationInfo> ref = data->mRegistrationInfo;
++  ref.forget(aRegistrationInfo);
+   return NS_OK;
+ }
+ 
+ /*
+  * The .controller is for the registration associated with the document when
+  * the document was loaded.
+  */
+ NS_IMETHODIMP
+@@ -3211,23 +3217,31 @@ ServiceWorkerManager::MaybeClaimClient(n
+   RefPtr<GenericPromise> ref;
+ 
+   // Same origin check
+   if (!aWorkerRegistration->mPrincipal->Equals(aDocument->NodePrincipal())) {
+     ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_SECURITY_ERR, __func__);
+     return ref.forget();
+   }
+ 
++  Maybe<ClientInfo> clientInfo(aDocument->GetClientInfo());
++  if (NS_WARN_IF(clientInfo.isNothing())) {
++    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
++                                          __func__);
++    return ref.forget();
++  }
++
+   // The registration that should be controlling the client
+   RefPtr<ServiceWorkerRegistrationInfo> matchingRegistration =
+     GetServiceWorkerRegistrationInfo(aDocument);
+ 
+   // The registration currently controlling the client
+   RefPtr<ServiceWorkerRegistrationInfo> controllingRegistration;
+-  GetDocumentRegistration(aDocument, getter_AddRefs(controllingRegistration));
++  GetClientRegistration(clientInfo.ref(),
++                        getter_AddRefs(controllingRegistration));
+ 
+   if (aWorkerRegistration != matchingRegistration ||
+       aWorkerRegistration == controllingRegistration) {
+     ref = GenericPromise::CreateAndResolve(true, __func__);
+     return ref.forget();
+   }
+ 
+   if (controllingRegistration) {
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -367,18 +367,18 @@ private:
+ 
+   void
+   AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+   nsresult
+   Update(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+   nsresult
+-  GetDocumentRegistration(nsIDocument* aDoc,
+-                          ServiceWorkerRegistrationInfo** aRegistrationInfo);
++  GetClientRegistration(const ClientInfo& aClientInfo,
++                        ServiceWorkerRegistrationInfo** aRegistrationInfo);
+ 
+   nsresult
+   GetServiceWorkerForScope(nsPIDOMWindowInner* aWindow,
+                            const nsAString& aScope,
+                            WhichServiceWorker aWhichWorker,
+                            nsISupports** aServiceWorker);
+ 
+   ServiceWorkerInfo*

+ 59 - 0
mozilla-release/patches/1425975-04-59a1.patch

@@ -0,0 +1,59 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172220 18000
+# Node ID d432082aabbd1aaf685263eb265748e2ae043d01
+# Parent  a7f6a9b1069014b44185f7409d4e2250d09c7063
+Bug 1425975 P4 Make ServiceWorkerManager::UpdateClientControllers use mControlledClients. r=asuth
+
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -3304,41 +3304,29 @@ ServiceWorkerManager::SetSkipWaitingFlag
+ void
+ ServiceWorkerManager::UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration)
+ {
+   AssertIsOnMainThread();
+ 
+   RefPtr<ServiceWorkerInfo> activeWorker = aRegistration->GetActive();
+   MOZ_DIAGNOSTIC_ASSERT(activeWorker);
+ 
+-  AutoTArray<nsCOMPtr<nsIDocument>, 16> docList;
+-  for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
+-    if (iter.UserData() != aRegistration) {
+-      continue;
+-    }
+-
+-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
+-    if (NS_WARN_IF(!doc)) {
++  AutoTArray<RefPtr<ClientHandle>, 16> handleList;
++  for (auto iter = mControlledClients.Iter(); !iter.Done(); iter.Next()) {
++    if (iter.UserData()->mRegistrationInfo != aRegistration) {
+       continue;
+     }
+ 
+-    docList.AppendElement(doc.forget());
++    handleList.AppendElement(iter.UserData()->mClientHandle);
+   }
+ 
+-  // Fire event after iterating mControlledDocuments is done to prevent
++  // Fire event after iterating mControlledClients is done to prevent
+   // modification by reentering from the event handlers during iteration.
+-  for (auto& doc : docList) {
+-    Maybe<ClientInfo> clientInfo = doc->GetClientInfo();
+-    if (clientInfo.isNothing()) {
+-      continue;
+-    }
+-    RefPtr<ClientHandle> clientHandle =
+-      ClientManager::CreateHandle(clientInfo.ref(),
+-                                  SystemGroup::EventTargetFor(TaskCategory::Other));
+-    clientHandle->Control(activeWorker->Descriptor());
++  for (auto& handle : handleList) {
++    handle->Control(activeWorker->Descriptor());
+   }
+ }
+ 
+ already_AddRefed<ServiceWorkerRegistrationInfo>
+ ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal,
+                                       const nsACString& aScope) const
+ {
+   MOZ_ASSERT(aPrincipal);

+ 44 - 0
mozilla-release/patches/1425975-05-59a1.patch

@@ -0,0 +1,44 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172220 18000
+# Node ID 560f15a8009772f7b237c666e28fac92198f2c9b
+# Parent  7d35e8422edd2bee46e4ea842b6ee8fa47ee8ead
+Bug 1425975 P5 Make ServiceWorkerManager::RemoveRegistration assert there is no controlled document. r=asuth
+
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -2304,27 +2304,28 @@ ServiceWorkerManager::RemoveScopeAndRegi
+     return;
+   }
+ 
+   if (auto entry = data->mUpdateTimers.Lookup(aRegistration->mScope)) {
+     entry.Data()->Cancel();
+     entry.Remove();
+   }
+ 
+-  // The registration should generally only be removed if there are no controlled
+-  // documents, but mControlledDocuments can contain references to potentially
+-  // controlled docs.  This happens when the service worker is not active yet.
+-  // We must purge these references since we are evicting the registration.
++  // Verify there are no controlled documents for the purged registration.
++#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+   for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
+     ServiceWorkerRegistrationInfo* reg = iter.UserData();
+-    MOZ_ASSERT(reg);
+     if (reg->mScope.Equals(aRegistration->mScope)) {
++      MOZ_DIAGNOSTIC_ASSERT(false,
++                            "controlled document when removing registration");
+       iter.Remove();
++      break;
+     }
+   }
++#endif
+ 
+   RefPtr<ServiceWorkerRegistrationInfo> info;
+   data->mInfos.Remove(aRegistration->mScope, getter_AddRefs(info));
+   data->mOrderedScopes.RemoveElement(aRegistration->mScope);
+   swm->NotifyListenersOnUnregister(info);
+ 
+   swm->MaybeRemoveRegistrationInfo(scopeKey);
+   swm->NotifyServiceWorkerRegistrationRemoved(aRegistration);

+ 273 - 0
mozilla-release/patches/1425975-06-59a1.patch

@@ -0,0 +1,273 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172221 18000
+# Node ID 834f38d73b6f71650ea891e2151fd155f845dc5a
+# Parent  f853e9d56c091813cbbf0a16b5d05423bff7f568
+Bug 1425975 P6 Rename some service worker methods to not reference documents. r=asuth
+
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -1613,17 +1613,17 @@ ServiceWorkerManager::WorkerIsIdle(Servi
+   if (!reg) {
+     return;
+   }
+ 
+   if (reg->GetActive() != aWorker) {
+     return;
+   }
+ 
+-  if (!reg->IsControllingDocuments() && reg->mPendingUninstall) {
++  if (!reg->IsControllingClients() && reg->mPendingUninstall) {
+     RemoveRegistration(reg);
+     return;
+   }
+ 
+   reg->TryToActivateAsync();
+ }
+ 
+ already_AddRefed<ServiceWorkerJobQueue>
+@@ -2361,17 +2361,17 @@ ServiceWorkerManager::MaybeStopControlli
+   AssertIsOnMainThread();
+   MOZ_ASSERT(aDoc);
+   RefPtr<ServiceWorkerRegistrationInfo> registration;
+   mControlledDocuments.Remove(aDoc, getter_AddRefs(registration));
+   // A document which was uncontrolled does not maintain that state itself, so
+   // it will always call MaybeStopControlling() even if there isn't an
+   // associated registration. So this check is required.
+   if (registration) {
+-    StopControllingADocument(registration);
++    StopControllingRegistration(registration);
+   }
+ }
+ 
+ void
+ ServiceWorkerManager::MaybeCheckNavigationUpdate(nsIDocument* aDoc)
+ {
+   AssertIsOnMainThread();
+   MOZ_ASSERT(aDoc);
+@@ -2414,17 +2414,17 @@ ServiceWorkerManager::StartControllingAD
+ 
+   Maybe<ClientInfo> clientInfo = aDoc->GetClientInfo();
+   if (NS_WARN_IF(clientInfo.isNothing())) {
+     ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                                           __func__);
+     return ref.forget();
+   }
+ 
+-  aRegistration->StartControllingADocument();
++  aRegistration->StartControllingClient();
+   mControlledDocuments.Put(aDoc, aRegistration);
+ 
+   StartControllingClient(clientInfo.ref(), aRegistration);
+ 
+   // Mark the document's ClientSource as controlled using the ClientHandle
+   // interface.  While we could get at the ClientSource directly from the
+   // document here, our goal is to move ServiceWorkerManager to a separate
+   // process.  Using the ClientHandle supports this remote operation.
+@@ -2433,20 +2433,20 @@ ServiceWorkerManager::StartControllingAD
+                                 SystemGroup::EventTargetFor(TaskCategory::Other));
+   ref = Move(clientHandle->Control(activeWorker->Descriptor()));
+ 
+   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
+   return Move(ref);
+ }
+ 
+ void
+-ServiceWorkerManager::StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration)
++ServiceWorkerManager::StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration)
+ {
+-  aRegistration->StopControllingADocument();
+-  if (aRegistration->IsControllingDocuments() || !aRegistration->IsIdle()) {
++  aRegistration->StopControllingClient();
++  if (aRegistration->IsControllingClients() || !aRegistration->IsIdle()) {
+     return;
+   }
+ 
+   if (aRegistration->mPendingUninstall) {
+     RemoveRegistration(aRegistration);
+     return;
+   }
+ 
+@@ -3241,17 +3241,17 @@ ServiceWorkerManager::MaybeClaimClient(n
+ 
+   if (aWorkerRegistration != matchingRegistration ||
+       aWorkerRegistration == controllingRegistration) {
+     ref = GenericPromise::CreateAndResolve(true, __func__);
+     return ref.forget();
+   }
+ 
+   if (controllingRegistration) {
+-    StopControllingADocument(controllingRegistration);
++    StopControllingRegistration(controllingRegistration);
+   }
+ 
+   ref = StartControllingADocument(aWorkerRegistration, aDocument);
+   return ref.forget();
+ }
+ 
+ already_AddRefed<GenericPromise>
+ ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDoc,
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -398,17 +398,17 @@ private:
+   void
+   NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+   RefPtr<GenericPromise>
+   StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
+                             nsIDocument* aDoc);
+ 
+   void
+-  StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration);
++  StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+   already_AddRefed<ServiceWorkerRegistrationInfo>
+   GetServiceWorkerRegistrationInfo(nsPIDOMWindowInner* aWindow);
+ 
+   already_AddRefed<ServiceWorkerRegistrationInfo>
+   GetServiceWorkerRegistrationInfo(nsIDocument* aDoc);
+ 
+   already_AddRefed<ServiceWorkerRegistrationInfo>
+diff --git a/dom/workers/ServiceWorkerRegistrationInfo.cpp b/dom/workers/ServiceWorkerRegistrationInfo.cpp
+--- a/dom/workers/ServiceWorkerRegistrationInfo.cpp
++++ b/dom/workers/ServiceWorkerRegistrationInfo.cpp
+@@ -76,32 +76,30 @@ ServiceWorkerRegistrationInfo::Clear()
+ 
+   NotifyChromeRegistrationListeners();
+ }
+ 
+ ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
+     const nsACString& aScope,
+     nsIPrincipal* aPrincipal,
+     ServiceWorkerUpdateViaCache aUpdateViaCache)
+-  : mControlledDocumentsCounter(0)
++  : mControlledClientsCounter(0)
+   , mUpdateState(NoUpdate)
+   , mCreationTime(PR_Now())
+   , mCreationTimeStamp(TimeStamp::Now())
+   , mLastUpdateTime(0)
+   , mUpdateViaCache(aUpdateViaCache)
+   , mScope(aScope)
+   , mPrincipal(aPrincipal)
+   , mPendingUninstall(false)
+ {}
+ 
+ ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
+ {
+-  if (IsControllingDocuments()) {
+-    NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
+-  }
++  MOZ_DIAGNOSTIC_ASSERT(!IsControllingClients());
+ }
+ 
+ NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo, nsIServiceWorkerRegistrationInfo)
+ 
+ NS_IMETHODIMP
+ ServiceWorkerRegistrationInfo::GetPrincipal(nsIPrincipal** aPrincipal)
+ {
+   AssertIsOnMainThread();
+@@ -243,17 +241,17 @@ ServiceWorkerRegistrationInfo::TryToActi
+ 
+ /*
+  * TryToActivate should not be called directly, use TryToActivateAsync instead.
+  */
+ void
+ ServiceWorkerRegistrationInfo::TryToActivate()
+ {
+   AssertIsOnMainThread();
+-  bool controlling = IsControllingDocuments();
++  bool controlling = IsControllingClients();
+   bool skipWaiting = mWaitingWorker && mWaitingWorker->SkipWaitingFlag();
+   bool idle = IsIdle();
+   if (idle && (!controlling || skipWaiting)) {
+     Activate();
+   }
+ }
+ 
+ void
+diff --git a/dom/workers/ServiceWorkerRegistrationInfo.h b/dom/workers/ServiceWorkerRegistrationInfo.h
+--- a/dom/workers/ServiceWorkerRegistrationInfo.h
++++ b/dom/workers/ServiceWorkerRegistrationInfo.h
+@@ -12,17 +12,17 @@
+ 
+ namespace mozilla {
+ namespace dom {
+ namespace workers {
+ 
+ class ServiceWorkerRegistrationInfo final
+   : public nsIServiceWorkerRegistrationInfo
+ {
+-  uint32_t mControlledDocumentsCounter;
++  uint32_t mControlledClientsCounter;
+ 
+   enum
+   {
+     NoUpdate,
+     NeedTimeCheckAndUpdate,
+     NeedUpdate
+   } mUpdateState;
+ 
+@@ -74,32 +74,32 @@ public:
+ 
+     return newest.forget();
+   }
+ 
+   already_AddRefed<ServiceWorkerInfo>
+   GetServiceWorkerInfoById(uint64_t aId);
+ 
+   void
+-  StartControllingADocument()
++  StartControllingClient()
+   {
+-    ++mControlledDocumentsCounter;
++    ++mControlledClientsCounter;
+   }
+ 
+   void
+-  StopControllingADocument()
++  StopControllingClient()
+   {
+-    MOZ_ASSERT(mControlledDocumentsCounter);
+-    --mControlledDocumentsCounter;
++    MOZ_ASSERT(mControlledClientsCounter);
++    --mControlledClientsCounter;
+   }
+ 
+   bool
+-  IsControllingDocuments() const
++  IsControllingClients() const
+   {
+-    return mActiveWorker && mControlledDocumentsCounter;
++    return mActiveWorker && mControlledClientsCounter;
+   }
+ 
+   void
+   Clear();
+ 
+   void
+   TryToActivateAsync();
+ 
+diff --git a/dom/workers/ServiceWorkerUnregisterJob.cpp b/dom/workers/ServiceWorkerUnregisterJob.cpp
+--- a/dom/workers/ServiceWorkerUnregisterJob.cpp
++++ b/dom/workers/ServiceWorkerUnregisterJob.cpp
+@@ -133,17 +133,17 @@ ServiceWorkerUnregisterJob::Unregister()
+   // "Set registration's uninstalling flag."
+   registration->mPendingUninstall = true;
+ 
+   // "Resolve promise with true"
+   mResult = true;
+   InvokeResultCallbacks(NS_OK);
+ 
+   // "If no service worker client is using registration..."
+-  if (!registration->IsControllingDocuments() && registration->IsIdle()) {
++  if (!registration->IsControllingClients() && registration->IsIdle()) {
+     // "Invoke [[Clear Registration]]..."
+     swm->RemoveRegistration(registration);
+   }
+ 
+   Finish(NS_OK);
+ }
+ 
+ } // namespace workers

+ 478 - 0
mozilla-release/patches/1425975-07no08-59a1.patch

@@ -0,0 +1,478 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172221 18000
+# Node ID 841e106b28101d1762d8c41740f433d19e7def92
+# Parent  679687cc3e08a1afd166dc8083f6d0e6c982a662
+Bug 1425975 P7 Use the mControlledClients list to drive controller start and stop logic. r=asuth
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -148,16 +148,17 @@
+ #include "nsIStructuredCloneContainer.h"
+ #include "nsISupportsPrimitives.h"
+ #ifdef MOZ_PLACES
+ #include "nsIFaviconService.h"
+ #include "mozIPlacesPendingOperation.h"
+ #include "mozIAsyncFavicons.h"
+ #endif
+ #include "nsINetworkPredictor.h"
++#include "nsIServiceWorkerManager.h"
+ 
+ // Editor-related
+ #include "nsIEditingSession.h"
+ 
+ #include "nsPIDOMWindow.h"
+ #include "nsGlobalWindow.h"
+ #include "nsPIWindowRoot.h"
+ #include "nsICachingChannel.h"
+@@ -3442,23 +3443,37 @@ nsDocShell::MaybeCreateInitialClientSour
+     return;
+   }
+ 
+   Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
+   if (controller.isNothing()) {
+     return;
+   }
+ 
++  nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
++  if (!swm) {
++    return;
++  }
++
+   // If the parent is controlled then propagate that controller to the
+   // initial about:blank client as well.  This will set the controller
+   // in the ClientManagerService in the parent.
+-  RefPtr<ClientHandle> handle =
+-    ClientManager::CreateHandle(mInitialClientSource->Info(),
+-                                parentInner->EventTargetFor(TaskCategory::Other));
+-  handle->Control(controller.ref());
++  //
++  // Note: If the registration is missing from the SWM we avoid setting
++  //       the controller on the client.  We can do this synchronously
++  //       for now since SWM is in the child process.  In the future
++  //       when SWM is in the parent process we will probably have to
++  //       always set the initial client source and then somehow clear
++  //       it if we find the registration is acutally gone.  Its also
++  //       possible this race only occurs in cases where the resulting
++  //       window is no longer exposed.  For example, in theory the SW
++  //       should not go away if our parent window is controlled.
++  if (!swm->StartControlling(mInitialClientSource->Info(), controller.ref())) {
++    return;
++  }
+ 
+   // Also mark the ClientSource as controlled directly in case script
+   // immediately accesses navigator.serviceWorker.controller.
+   mInitialClientSource->SetController(controller.ref());
+ }
+ 
+ Maybe<ClientInfo>
+ nsDocShell::GetInitialClientInfo() const
+diff --git a/dom/interfaces/base/nsIServiceWorkerManager.idl b/dom/interfaces/base/nsIServiceWorkerManager.idl
+--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
++++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
+@@ -10,16 +10,27 @@ interface mozIDOMWindow;
+ interface nsPIDOMWindowInner;
+ interface mozIDOMWindowProxy;
+ interface nsIArray;
+ interface nsIDocument;
+ interface nsIInterceptedChannel;
+ interface nsIPrincipal;
+ interface nsIRunnable;
+ interface nsIURI;
++%{C++
++namespace mozilla {
++namespace dom {
++class ClientInfo;
++class ServiceWorkerDescriptor;
++} // namespace dom
++} // namespace mozilla
++%}
++
++[ref] native const_ClientInfoRef(const mozilla::dom::ClientInfo);
++[ref] native const_ServiceWorkerDescriptorRef(const mozilla::dom::ServiceWorkerDescriptor);
+ 
+ [scriptable, uuid(52ee2c9d-ee87-4caf-9588-23ae77ff8798)]
+ interface nsIServiceWorkerUnregisterCallback : nsISupports
+ {
+   // aState is true if the unregistration succeded.
+   // It's false if this ServiceWorkerRegistration doesn't exist.
+   void unregisterSucceeded(in bool aState);
+   void unregisterFailed();
+@@ -145,16 +156,19 @@ interface nsIServiceWorkerManager : nsIS
+   /**
+    * Call this to request that document `aDoc` be controlled by a ServiceWorker
+    * if a registration exists for it's scope.
+    *
+    * This MUST only be called once per document!
+    */
+   [notxpcom,nostdcall] void MaybeStartControlling(in nsIDocument aDoc);
+ 
++  [notxpcom, nostdcall] bool StartControlling(in const_ClientInfoRef aClientInfo,
++                                              in const_ServiceWorkerDescriptorRef aServiceWorker);
++
+   /**
+    * Documents that have called MaybeStartControlling() should call this when
+    * they are destroyed. This function may be called multiple times, and is
+    * idempotent.
+    */
+   [notxpcom,nostdcall] void MaybeStopControlling(in nsIDocument aDoc);
+ 
+   /*
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -308,49 +308,83 @@ ServiceWorkerManager::Init(ServiceWorker
+   if (!actor) {
+     MaybeStartShutdown();
+     return;
+   }
+ 
+   mActor = static_cast<ServiceWorkerManagerChild*>(actor);
+ }
+ 
+-void
++RefPtr<GenericPromise>
+ ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo,
+                                              ServiceWorkerRegistrationInfo* aRegistrationInfo)
+ {
+-  RefPtr<ClientHandle> clientHandle =
+-    ClientManager::CreateHandle(aClientInfo,
+-                                SystemGroup::EventTargetFor(TaskCategory::Other));
++  MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive());
++
++  RefPtr<GenericPromise> ref;
++
++  const ServiceWorkerDescriptor& active =
++    aRegistrationInfo->GetActive()->Descriptor();
+ 
+   auto entry = mControlledClients.LookupForAdd(aClientInfo.Id());
+   if (entry) {
++    RefPtr<ServiceWorkerRegistrationInfo> old =
++      entry.Data()->mRegistrationInfo.forget();
++
++    ref = Move(entry.Data()->mClientHandle->Control(active));
+     entry.Data()->mRegistrationInfo = aRegistrationInfo;
+-  } else {
+-    entry.OrInsert([&] {
+-      return new ControlledClientData(clientHandle, aRegistrationInfo);
++
++    if (old != aRegistrationInfo) {
++      StopControllingRegistration(old);
++      aRegistrationInfo->StartControllingClient();
++    }
++
++    Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
++
++    return Move(ref);
++  }
++
++  RefPtr<ClientHandle> clientHandle =
++    ClientManager::CreateHandle(aClientInfo,
++                                SystemGroup::EventTargetFor(TaskCategory::Other));
++
++  ref = Move(clientHandle->Control(active));
++
++  aRegistrationInfo->StartControllingClient();
++
++  entry.OrInsert([&] {
++    return new ControlledClientData(clientHandle, aRegistrationInfo);
++  });
++
++  RefPtr<ServiceWorkerManager> self(this);
++  clientHandle->OnDetach()->Then(
++    SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
++    [self = Move(self), aClientInfo] {
++      self->StopControllingClient(aClientInfo);
+     });
+ 
+-    RefPtr<ServiceWorkerManager> self(this);
+-    clientHandle->OnDetach()->Then(
+-      SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
+-      [self = Move(self), aClientInfo] {
+-        self->StopControllingClient(aClientInfo);
+-      });
+-  }
++  Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
++
++  return Move(ref);
+ }
+ 
+ void
+ ServiceWorkerManager::StopControllingClient(const ClientInfo& aClientInfo)
+ {
+   auto entry = mControlledClients.Lookup(aClientInfo.Id());
+   if (!entry) {
+     return;
+   }
++
++  RefPtr<ServiceWorkerRegistrationInfo> reg =
++    entry.Data()->mRegistrationInfo.forget();
++
+   entry.Remove();
++
++  StopControllingRegistration(reg);
+ }
+ 
+ void
+ ServiceWorkerManager::MaybeStartShutdown()
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+ 
+   if (mShuttingDown) {
+@@ -2304,28 +2338,37 @@ ServiceWorkerManager::RemoveScopeAndRegi
+     return;
+   }
+ 
+   if (auto entry = data->mUpdateTimers.Lookup(aRegistration->mScope)) {
+     entry.Data()->Cancel();
+     entry.Remove();
+   }
+ 
+-  // Verify there are no controlled documents for the purged registration.
+-#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+-  for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
+-    ServiceWorkerRegistrationInfo* reg = iter.UserData();
++  // Verify there are no controlled clients for the purged registration.
++  for (auto iter = swm->mControlledClients.Iter(); !iter.Done(); iter.Next()) {
++    auto& reg = iter.UserData()->mRegistrationInfo;
+     if (reg->mScope.Equals(aRegistration->mScope)) {
+       MOZ_DIAGNOSTIC_ASSERT(false,
+-                            "controlled document when removing registration");
++                            "controlled client when removing registration");
+       iter.Remove();
+       break;
+     }
+   }
+-#endif
++
++  // Registration lifecycle is managed via mControlledClients now.  Do not
++  // assert on on mControlledDocuments as races may cause this to still be
++  // set when the registration is destroyed.
++  for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
++    ServiceWorkerRegistrationInfo* reg = iter.UserData();
++    if (reg->mScope.Equals(aRegistration->mScope)) {
++      iter.Remove();
++      break;
++    }
++  }
+ 
+   RefPtr<ServiceWorkerRegistrationInfo> info;
+   data->mInfos.Remove(aRegistration->mScope, getter_AddRefs(info));
+   data->mOrderedScopes.RemoveElement(aRegistration->mScope);
+   swm->NotifyListenersOnUnregister(info);
+ 
+   swm->MaybeRemoveRegistrationInfo(scopeKey);
+   swm->NotifyServiceWorkerRegistrationRemoved(aRegistration);
+@@ -2344,35 +2387,53 @@ ServiceWorkerManager::MaybeRemoveRegistr
+ 
+ void
+ ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc)
+ {
+   AssertIsOnMainThread();
+   MOZ_ASSERT(aDoc);
+   RefPtr<ServiceWorkerRegistrationInfo> registration =
+     GetServiceWorkerRegistrationInfo(aDoc);
+-  if (registration) {
++  if (registration && registration->GetActive() &&
++      aDoc->GetSandboxFlags() == 0) {
+     MOZ_ASSERT(!mControlledDocuments.Contains(aDoc));
+     StartControllingADocument(registration, aDoc);
+   }
+ }
+ 
++bool
++ServiceWorkerManager::StartControlling(const ClientInfo& aClientInfo,
++                                       const ServiceWorkerDescriptor& aServiceWorker)
++{
++  AssertIsOnMainThread();
++
++  nsCOMPtr<nsIPrincipal> principal =
++    PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
++  NS_ENSURE_TRUE(principal, false);
++
++  nsCOMPtr<nsIURI> scope;
++  nsresult rv =
++    NS_NewURI(getter_AddRefs(scope), aServiceWorker.Scope(), nullptr, nullptr);
++  NS_ENSURE_SUCCESS(rv, false);
++
++  RefPtr<ServiceWorkerRegistrationInfo> registration =
++    GetServiceWorkerRegistrationInfo(principal, scope);
++  NS_ENSURE_TRUE(registration, false);
++
++  StartControllingClient(aClientInfo, registration);
++
++  return true;
++}
++
+ void
+ ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
+ {
+   AssertIsOnMainThread();
+   MOZ_ASSERT(aDoc);
+-  RefPtr<ServiceWorkerRegistrationInfo> registration;
+-  mControlledDocuments.Remove(aDoc, getter_AddRefs(registration));
+-  // A document which was uncontrolled does not maintain that state itself, so
+-  // it will always call MaybeStopControlling() even if there isn't an
+-  // associated registration. So this check is required.
+-  if (registration) {
+-    StopControllingRegistration(registration);
+-  }
++  mControlledDocuments.Remove(aDoc);
+ }
+ 
+ void
+ ServiceWorkerManager::MaybeCheckNavigationUpdate(nsIDocument* aDoc)
+ {
+   AssertIsOnMainThread();
+   MOZ_ASSERT(aDoc);
+   // We perform these success path navigation update steps when the
+@@ -2386,60 +2447,24 @@ ServiceWorkerManager::MaybeCheckNavigati
+   //    algorithm.
+   RefPtr<ServiceWorkerRegistrationInfo> registration;
+   mControlledDocuments.Get(aDoc, getter_AddRefs(registration));
+   if (registration) {
+     registration->MaybeScheduleUpdate();
+   }
+ }
+ 
+-RefPtr<GenericPromise>
++void
+ ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
+                                                 nsIDocument* aDoc)
+ {
+   MOZ_ASSERT(aRegistration);
+   MOZ_ASSERT(aDoc);
+ 
+-#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+-  auto storageAllowed = nsContentUtils::StorageAllowedForDocument(aDoc);
+-  MOZ_DIAGNOSTIC_ASSERT(storageAllowed == nsContentUtils::StorageAccess::eAllow);
+-#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
+-
+-  RefPtr<GenericPromise> ref;
+-
+-  ServiceWorkerInfo* activeWorker = aRegistration->GetActive();
+-  if (NS_WARN_IF(!activeWorker)) {
+-    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+-                                          __func__);
+-    return ref.forget();
+-  }
+-
+-  Maybe<ClientInfo> clientInfo = aDoc->GetClientInfo();
+-  if (NS_WARN_IF(clientInfo.isNothing())) {
+-    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+-                                          __func__);
+-    return ref.forget();
+-  }
+-
+-  aRegistration->StartControllingClient();
+   mControlledDocuments.Put(aDoc, aRegistration);
+-
+-  StartControllingClient(clientInfo.ref(), aRegistration);
+-
+-  // Mark the document's ClientSource as controlled using the ClientHandle
+-  // interface.  While we could get at the ClientSource directly from the
+-  // document here, our goal is to move ServiceWorkerManager to a separate
+-  // process.  Using the ClientHandle supports this remote operation.
+-  RefPtr<ClientHandle> clientHandle =
+-    ClientManager::CreateHandle(clientInfo.ref(),
+-                                SystemGroup::EventTargetFor(TaskCategory::Other));
+-  ref = Move(clientHandle->Control(activeWorker->Descriptor()));
+-
+-  Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
+-  return Move(ref);
+ }
+ 
+ void
+ ServiceWorkerManager::StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration)
+ {
+   aRegistration->StopControllingClient();
+   if (aRegistration->IsControllingClients() || !aRegistration->IsIdle()) {
+     return;
+@@ -2766,20 +2791,17 @@ ServiceWorkerManager::DispatchFetchEvent
+         //       initial about:blank global.  See bug 1419620 and the spec
+         //       issue here: https://github.com/w3c/ServiceWorker/issues/1232
+       }
+ 
+       if (clientInfo.isSome()) {
+         // First, attempt to mark the reserved client controlled directly.  This
+         // will update the controlled status in the ClientManagerService in the
+         // parent.  It will also eventually propagate back to the ClientSource.
+-        RefPtr<ClientHandle> clientHandle =
+-          ClientManager::CreateHandle(clientInfo.ref(),
+-                                      SystemGroup::EventTargetFor(TaskCategory::Other));
+-        clientHandle->Control(serviceWorker->Descriptor());
++        StartControllingClient(clientInfo.ref(), registration);
+       }
+ 
+       // But we also note the reserved state on the LoadInfo.  This allows the
+       // ClientSource to be updated immediately after the nsIChannel starts.
+       // This is necessary to have the correct controller in place for immediate
+       // follow-on requests.
+       loadInfo->SetController(serviceWorker->Descriptor());
+     }
+@@ -3240,21 +3262,18 @@ ServiceWorkerManager::MaybeClaimClient(n
+                         getter_AddRefs(controllingRegistration));
+ 
+   if (aWorkerRegistration != matchingRegistration ||
+       aWorkerRegistration == controllingRegistration) {
+     ref = GenericPromise::CreateAndResolve(true, __func__);
+     return ref.forget();
+   }
+ 
+-  if (controllingRegistration) {
+-    StopControllingRegistration(controllingRegistration);
+-  }
+-
+-  ref = StartControllingADocument(aWorkerRegistration, aDocument);
++  StartControllingADocument(aWorkerRegistration, aDocument);
++  ref = StartControllingClient(clientInfo.ref(), aWorkerRegistration);
+   return ref.forget();
+ }
+ 
+ already_AddRefed<GenericPromise>
+ ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDoc,
+                                        const ServiceWorkerDescriptor& aServiceWorker)
+ {
+   RefPtr<GenericPromise> ref;
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -339,17 +339,17 @@ public:
+ 
+ private:
+   ServiceWorkerManager();
+   ~ServiceWorkerManager();
+ 
+   void
+   Init(ServiceWorkerRegistrar* aRegistrar);
+ 
+-  void
++  RefPtr<GenericPromise>
+   StartControllingClient(const ClientInfo& aClientInfo,
+                          ServiceWorkerRegistrationInfo* aRegistrationInfo);
+ 
+   void
+   StopControllingClient(const ClientInfo& aClientInfo);
+ 
+   void
+   MaybeStartShutdown();
+@@ -393,17 +393,17 @@ private:
+                                             WhichServiceWorker aWhichOne);
+   void
+   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
+                                             WhichServiceWorker aWhichOnes);
+ 
+   void
+   NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+-  RefPtr<GenericPromise>
++  void
+   StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
+                             nsIDocument* aDoc);
+ 
+   void
+   StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration);
+ 
+   already_AddRefed<ServiceWorkerRegistrationInfo>
+   GetServiceWorkerRegistrationInfo(nsPIDOMWindowInner* aWindow);

+ 95 - 0
mozilla-release/patches/1425975-09-59a1.patch

@@ -0,0 +1,95 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172221 18000
+# Node ID 8449669c2adc5012c05ea09d279d4b57e6df5096
+# Parent  2c6d9ef596243039d0e8b94f210ef85ae231902f
+Bug 1425975 P9 Refactor MaybeCheckNavigationUpdate() to take a ClientInfo instead of a document. r=asuth
+
+diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
+--- a/dom/base/nsDocument.cpp
++++ b/dom/base/nsDocument.cpp
+@@ -5430,17 +5430,20 @@ nsDocument::DispatchContentLoadedEvents(
+                                         NS_LITERAL_STRING("MozApplicationManifest"),
+                                         true, true);
+   }
+ 
+   if (mMaybeServiceWorkerControlled) {
+     using mozilla::dom::workers::ServiceWorkerManager;
+     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+     if (swm) {
+-      swm->MaybeCheckNavigationUpdate(this);
++      Maybe<ClientInfo> clientInfo = GetClientInfo();
++      if (clientInfo.isSome()) {
++        swm->MaybeCheckNavigationUpdate(clientInfo.ref());
++      }
+     }
+   }
+ 
+   UnblockOnload(true);
+ }
+ 
+ void
+ nsDocument::EndLoad()
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -2427,33 +2427,31 @@ void
+ ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
+ {
+   AssertIsOnMainThread();
+   MOZ_ASSERT(aDoc);
+   mControlledDocuments.Remove(aDoc);
+ }
+ 
+ void
+-ServiceWorkerManager::MaybeCheckNavigationUpdate(nsIDocument* aDoc)
++ServiceWorkerManager::MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo)
+ {
+   AssertIsOnMainThread();
+-  MOZ_ASSERT(aDoc);
+   // We perform these success path navigation update steps when the
+   // document tells us its more or less done loading.  This avoids
+   // slowing down page load and also lets pages consistently get
+   // updatefound events when they fire.
+   //
+   // 9.8.20 If respondWithEntered is false, then:
+   // 9.8.22 Else: (respondWith was entered and succeeded)
+   //    If request is a non-subresource request, then: Invoke Soft Update
+   //    algorithm.
+-  RefPtr<ServiceWorkerRegistrationInfo> registration;
+-  mControlledDocuments.Get(aDoc, getter_AddRefs(registration));
+-  if (registration) {
+-    registration->MaybeScheduleUpdate();
++  ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
++  if (data && data->mRegistrationInfo) {
++    data->mRegistrationInfo->MaybeScheduleUpdate();
+   }
+ }
+ 
+ void
+ ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
+                                                 nsIDocument* aDoc)
+ {
+   MOZ_ASSERT(aRegistration);
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -315,17 +315,17 @@ public:
+   AddRegistrationEventListener(const nsAString& aScope,
+                                ServiceWorkerRegistrationListener* aListener);
+ 
+   NS_IMETHOD
+   RemoveRegistrationEventListener(const nsAString& aScope,
+                                   ServiceWorkerRegistrationListener* aListener);
+ 
+   void
+-  MaybeCheckNavigationUpdate(nsIDocument* aDoc);
++  MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo);
+ 
+   nsresult
+   SendPushEvent(const nsACString& aOriginAttributes,
+                 const nsACString& aScope,
+                 const nsAString& aMessageId,
+                 const Maybe<nsTArray<uint8_t>>& aData);
+ 
+   nsresult

+ 84 - 0
mozilla-release/patches/1425975-10-59a1.patch

@@ -0,0 +1,84 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172221 18000
+# Node ID 8e4ffe879be7ec092b58657f8b06fe78b96c4437
+# Parent  a3165eddf7570d539c943884057c570d272fb8d6
+Bug 1425975 P10 Fix the test_skip_waiting.html mochitest to properly wait for active worker state. r=asuth
+
+diff --git a/dom/workers/test/serviceworkers/skip_waiting_scope/index.html b/dom/workers/test/serviceworkers/skip_waiting_scope/index.html
+--- a/dom/workers/test/serviceworkers/skip_waiting_scope/index.html
++++ b/dom/workers/test/serviceworkers/skip_waiting_scope/index.html
+@@ -14,20 +14,16 @@
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <script class="testbody" type="text/javascript">
+ 
+   if (!parent) {
+     info("skip_waiting_scope/index.html shouldn't be launched directly!");
+   }
+ 
+-  navigator.serviceWorker.ready.then(function() {
+-    parent.postMessage("READY", "*");
+-  });
+-
+   navigator.serviceWorker.oncontrollerchange = function() {
+     parent.postMessage({
+       event: "controllerchange",
+       controllerScriptURL: navigator.serviceWorker.controller &&
+                            navigator.serviceWorker.controller.scriptURL
+     }, "*");
+   }
+ 
+diff --git a/dom/workers/test/serviceworkers/test_skip_waiting.html b/dom/workers/test/serviceworkers/test_skip_waiting.html
+--- a/dom/workers/test/serviceworkers/test_skip_waiting.html
++++ b/dom/workers/test/serviceworkers/test_skip_waiting.html
+@@ -8,45 +8,36 @@
+   <title>Bug 1131352 - Add ServiceWorkerGlobalScope skipWaiting()</title>
+   <script type="text/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"></pre>
++<script src="utils.js"></script>
+ <script class="testbody" type="text/javascript">
+   var registration, iframe, content;
+ 
+   function start() {
+     return navigator.serviceWorker.register("worker.js",
+                                             {scope: "./skip_waiting_scope/"});
+   }
+ 
+-  function waitForActivated(swr) {
++  async function waitForActivated(swr) {
+     registration = swr;
+-    var promise = new Promise(function(resolve, reject) {
+-      window.onmessage = function(e) {
+-        if (e.data === "READY") {
+-          ok(true, "Active worker is activated now");
+-          resolve();
+-        } else {
+-          ok(false, "Wrong value. Somenting went wrong");
+-          resolve();
+-        }
+-      }
+-    });
++    await waitForState(registration.installing, "activated")
+ 
+     iframe = document.createElement("iframe");
+     iframe.setAttribute("src", "skip_waiting_scope/index.html");
+ 
+     content = document.getElementById("content");
+     content.appendChild(iframe);
+ 
+-    return promise;
++    await new Promise(resolve => iframe.onload = resolve);
+   }
+ 
+   function checkWhetherItSkippedWaiting() {
+     var promise = new Promise(function(resolve, reject) {
+       window.onmessage = function (evt) {
+         if (evt.data.event === "controllerchange") {
+           ok(evt.data.controllerScriptURL.match("skip_waiting_installed_worker"),
+              "The controller changed after skiping the waiting step");

+ 77 - 0
mozilla-release/patches/1425975-11-59a1.patch

@@ -0,0 +1,77 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172222 18000
+# Node ID 86acf7e4d09daf2b392eba2784bc92591cab00d5
+# Parent  cda047412b4371d43f44df0bd1dc6a6ffce8e7d9
+Bug 1425975 P11 Fix test_workerupdatefound.html not to frame loading against SW activation and updatefound events. r=asuth
+
+diff --git a/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
+--- a/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
++++ b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
+@@ -8,24 +8,25 @@
+   <title>Bug 1131327 - Test ServiceWorkerRegistration.onupdatefound on ServiceWorker</title>
+   <script type="text/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"></div>
+ <pre id="test"></pre>
++<script src="utils.js"></script>
+ <script class="testbody" type="text/javascript">
+   var registration;
+   var promise;
+ 
+-  function start() {
+-    return navigator.serviceWorker.register("worker_updatefoundevent.js",
+-                                            { scope: "./updatefoundevent.html" })
+-      .then((swr) => registration = swr);
++  async function start() {
++    registration = await navigator.serviceWorker.register("worker_updatefoundevent.js",
++                                                          { scope: "./updatefoundevent.html" })
++    await waitForState(registration.installing, 'activated');
+   }
+ 
+   function startWaitForUpdateFound() {
+     registration.onupdatefound = function(e) {
+     }
+ 
+     promise = new Promise(function(resolve, reject) {
+       window.onmessage = function(e) {
+diff --git a/dom/workers/test/serviceworkers/worker_updatefoundevent.js b/dom/workers/test/serviceworkers/worker_updatefoundevent.js
+--- a/dom/workers/test/serviceworkers/worker_updatefoundevent.js
++++ b/dom/workers/test/serviceworkers/worker_updatefoundevent.js
+@@ -1,23 +1,18 @@
+ /**
+  * Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/
+  */
+ 
+-onactivate = function(e) {
+-  e.waitUntil(new Promise(function(resolve, reject) {
+-    registration.onupdatefound = function(e) {
+-      clients.matchAll().then(function(clients) {
+-        if (!clients.length) {
+-          reject("No clients found");
+-        }
++registration.onupdatefound = function(e) {
++  clients.matchAll().then(function(clients) {
++    if (!clients.length) {
++      reject("No clients found");
++    }
+ 
+-        if (registration.scope.match(/updatefoundevent\.html$/)) {
+-          clients[0].postMessage("finish");
+-          resolve();
+-        } else {
+-          dump("Scope did not match");
+-        }
+-      }, reject);
++    if (registration.scope.match(/updatefoundevent\.html$/)) {
++      clients[0].postMessage("finish");
++    } else {
++      dump("Scope did not match");
+     }
+-  }));
++  });
+ }

+ 52 - 0
mozilla-release/patches/1425975-12-59a1.patch

@@ -0,0 +1,52 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172222 18000
+# Node ID d8406786b4192ef8fbbd4b56549f99ed02fbf040
+# Parent  553d59d90721656958bd8bfde4af36f898d8f8e7
+Bug 1425975 P12 Don't mark an initial about:blank client as controlled if its sandboxed. r=asuth
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -3396,17 +3396,17 @@ nsDocShell::MaybeCreateInitialClientSour
+   // when DoChannelLoad() is called before CreateAboutBlankContentViewer.
+   if (mInitialClientSource) {
+     return;
+   }
+ 
+   // Don't pre-allocate the client when we are sandboxed.  The inherited
+   // principal does not take sandboxing into account.
+   // TODO: Refactor sandboxing principal code out so we can use it here.
+-  if (!aPrincipal && (mSandboxFlags & SANDBOXED_ORIGIN)) {
++  if (!aPrincipal && mSandboxFlags) {
+     return;
+   }
+ 
+   nsIPrincipal* principal = aPrincipal ? aPrincipal
+                                        : GetInheritedPrincipal(false);
+ 
+   // Sometimes there is no principal available when we are called from
+   // CreateAboutBlankContentViewer.  For example, sometimes the principal
+@@ -3438,18 +3438,21 @@ nsDocShell::MaybeCreateInitialClientSour
+   nsCOMPtr<nsIDocShell> parent = GetParentDocshell();
+   nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
+   nsPIDOMWindowInner* parentInner =
+     parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
+   if (!parentInner) {
+     return;
+   }
+ 
++  // We're done if there is no parent controller.  Also, don't inherit
++  // the controller if we're sandboxed.  This matches our behavior in
++  // ShouldPrepareForIntercept(),
+   Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
+-  if (controller.isNothing()) {
++  if (controller.isNothing() || mSandboxFlags) {
+     return;
+   }
+ 
+   nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
+   if (!swm) {
+     return;
+   }
+ 

+ 155 - 0
mozilla-release/patches/1425975-13-59a1.patch

@@ -0,0 +1,155 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172222 18000
+# Node ID 42f1a8bfebf4c0ec070634195ec0c3faa4368a58
+# Parent  1570383720ba3821e1de1d1ff2f0ad6d68a902e9
+Bug 1425975 P13 Check for a different final document principal and reset the ClientSource when it happens. r=asuth
+
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -3483,41 +3483,74 @@ nsGlobalWindow::EnsureClientSource()
+   // In this case we want to inherit this placeholder Client here.
+   if (!mClientSource) {
+     mClientSource = Move(initialClientSource);
+     if (mClientSource) {
+       newClientSource = true;
+     }
+   }
+  
++  // Verify the final ClientSource principal matches the final document
++  // principal.  The ClientChannelHelper handles things like network
++  // redirects, but there are other ways the document principal can change.
++  // For example, if something sets the nsIChannel.owner property, then
++  // the final channel principal can be anything.  Unfortunately there is
++  // no good way to detect this until after the channel completes loading.
++  //
++  // For now we handle this just by reseting the ClientSource.  This will
++  // result in a new ClientSource with the correct principal being created.
++  // To APIs like ServiceWorker and Clients API it will look like there was
++  // an initial content page created that was then immediately replaced.
++  // This is pretty close to what we are actually doing.
++  if (mClientSource) {
++    nsCOMPtr<nsIPrincipal> clientPrincipal(mClientSource->Info().GetPrincipal());
++    if (!clientPrincipal || !clientPrincipal->Equals(mDoc->NodePrincipal())) {
++      mClientSource.reset();
++    }
++  }
++
+   // If we don't have a reserved client or an initial client, then create
+   // one now.  This can happen in certain cases where we avoid preallocating
+   // the client in the docshell.  This mainly occurs in situations where
+   // the principal is not clearly inherited from the parent; e.g. sandboxed
+   // iframes, window.open(), etc.
++  //
++  // We also do this late ClientSource creation if the final document ended
++  // up with a different principal.
++  //
+   // TODO: We may not be marking initial about:blank documents created
+   //       this way as controlled by a service worker properly.  The
+   //       controller should be coming from the same place as the inheritted
+   //       principal.  We do this in docshell, but as mentioned we aren't
+   //       smart enough to handle all cases yet.  For example, a
+   //       window.open() with new URL should inherit the controller from
+   //       the opener, but we probably don't handle that yet.
+   if (!mClientSource) {
+     mClientSource = ClientManager::CreateSource(ClientType::Window,
+                                                 EventTargetFor(TaskCategory::Other),
+                                                 mDoc->NodePrincipal());
+     MOZ_DIAGNOSTIC_ASSERT(mClientSource);
+     newClientSource = true;
++
++    // Note, we don't apply the loadinfo controller below if we create
++    // the ClientSource here.
+   }
+ 
+   // The load may have started controlling the Client as well.  If
+   // so, mark it as controlled immediately here.  The actor may
+   // or may not have been notified by the parent side about being
+   // controlled yet.
+-  if (loadInfo) {
++  //
++  // Note: We should be careful not to control a client that was created late.
++  //       These clients were not seen by the ServiceWorkerManager when it
++  //       marked the LoadInfo controlled and it won't know about them.  Its
++  //       also possible we are creating the client late due to the final
++  //       principal changing and these clients should definitely not be
++  //       controlled by a service worker with a different principal.
++  else if (loadInfo) {
+     const Maybe<ServiceWorkerDescriptor> controller = loadInfo->GetController();
+     if (controller.isSome()) {
+       mClientSource->SetController(controller.ref());
+     }
+ 
+     // We also have to handle the case where te initial about:blank is
+     // controlled due to inheritting the service worker from its parent,
+     // but the actual nsIChannel load is not covered by any service worker.
+diff --git a/dom/clients/manager/ClientInfo.cpp b/dom/clients/manager/ClientInfo.cpp
+--- a/dom/clients/manager/ClientInfo.cpp
++++ b/dom/clients/manager/ClientInfo.cpp
+@@ -2,21 +2,23 @@
+ /* 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 "ClientInfo.h"
+ 
+ #include "mozilla/dom/ClientIPCTypes.h"
++#include "mozilla/ipc/BackgroundUtils.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ using mozilla::ipc::PrincipalInfo;
++using mozilla::ipc::PrincipalInfoToPrincipal;
+ 
+ ClientInfo::ClientInfo(const nsID& aId,
+                        ClientType aType,
+                        const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
+                        const TimeStamp& aCreationTime)
+   : mData(MakeUnique<IPCClientInfo>(aId, aType, aPrincipalInfo, aCreationTime,
+                                     EmptyCString(),
+                                     mozilla::dom::FrameType::None))
+@@ -133,10 +135,18 @@ ClientInfo::IsPrivateBrowsing() const
+     default:
+     {
+       // clients should never be expanded principals
+       MOZ_CRASH("unexpected principal type!");
+     }
+   }
+ }
+ 
++nsCOMPtr<nsIPrincipal>
++ClientInfo::GetPrincipal() const
++{
++  MOZ_ASSERT(NS_IsMainThread());
++  nsCOMPtr<nsIPrincipal> ref = PrincipalInfoToPrincipal(PrincipalInfo());
++  return Move(ref);
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientInfo.h b/dom/clients/manager/ClientInfo.h
+--- a/dom/clients/manager/ClientInfo.h
++++ b/dom/clients/manager/ClientInfo.h
+@@ -90,14 +90,19 @@ public:
+ 
+   // Convert to the ipdl generated type.
+   const IPCClientInfo&
+   ToIPC() const;
+ 
+   // Determine if the client is in private browsing mode.
+   bool
+   IsPrivateBrowsing() const;
++
++  // Get a main-thread nsIPrincipal for the client.  This may return nullptr
++  // if the PrincipalInfo() fails to deserialize for some reason.
++  nsCOMPtr<nsIPrincipal>
++  GetPrincipal() const;
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientInfo_h

+ 109 - 0
mozilla-release/patches/1425975-14-59a1.patch

@@ -0,0 +1,109 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172222 18000
+# Node ID e7b92985e941f7f1b9b562fba75b0b764b1ce6a9
+# Parent  9a14e1e8544c7bb1d640516454a4518e0311af5e
+Bug 1425975 P14 Assert that storage is allowed when a ClientSource is both execution ready and controlled. r=asuth
+
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -201,16 +201,24 @@ ClientSource::WorkerExecutionReady(Worke
+ {
+   MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
+   aWorkerPrivate->AssertIsOnWorkerThread();
+ 
+   if (IsShutdown()) {
+     return;
+   }
+ 
++  // A client without access to storage should never be controlled by
++  // a service worker.  Check this here in case we were controlled before
++  // execution ready.  We can't reliably determine what our storage policy
++  // is before execution ready, unfortunately.
++  if (mController.isSome()) {
++    MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate->IsStorageAllowed());
++  }
++
+   // Its safe to store the WorkerPrivate* here because the ClientSource
+   // is explicitly destroyed by WorkerPrivate before exiting its run loop.
+   MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>());
+   mOwner = AsVariant(aWorkerPrivate);
+ 
+   ClientSourceExecutionReadyArgs args(
+     aWorkerPrivate->GetLocationInfo().mHref,
+     FrameType::None);
+@@ -230,16 +238,25 @@ ClientSource::WindowExecutionReady(nsPID
+     return NS_OK;
+   }
+ 
+   nsIDocument* doc = aInnerWindow->GetExtantDoc();
+   if (NS_WARN_IF(!doc)) {
+     return NS_ERROR_UNEXPECTED;
+   }
+ 
++  // A client without access to storage should never be controlled by
++  // a service worker.  Check this here in case we were controlled before
++  // execution ready.  We can't reliably determine what our storage policy
++  // is before execution ready, unfortunately.
++  if (mController.isSome()) {
++    MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::StorageAllowedForWindow(aInnerWindow) ==
++                          nsContentUtils::StorageAccess::eAllow);
++  }
++
+   // Don't use nsAutoCString here since IPC requires a full nsCString anyway.
+   nsCString spec;
+ 
+   nsIURI* uri = doc->GetOriginalURI();
+   if (uri) {
+     nsresult rv = uri->GetSpec(spec);
+     if (NS_WARN_IF(NS_FAILED(rv))) {
+       return rv;
+@@ -285,16 +302,20 @@ ClientSource::DocShellExecutionReady(nsI
+     return NS_OK;
+   }
+ 
+   nsPIDOMWindowOuter* outer = aDocShell->GetWindow();
+   if (NS_WARN_IF(!outer)) {
+     return NS_ERROR_UNEXPECTED;
+   }
+ 
++  // Note: We don't assert storage access for a controlled client.  If
++  // the about:blank actually gets used then WindowExecutionReady() will
++  // get called which asserts storage access.
++
+   // TODO: dedupe this with WindowExecutionReady
+   FrameType frameType = FrameType::Top_level;
+   if (!outer->IsTopLevelWindow()) {
+     frameType = FrameType::Nested;
+   } else if(outer->HadOriginalOpener()) {
+     frameType = FrameType::Auxiliary;
+   }
+ 
+@@ -355,16 +376,26 @@ ClientSource::SetController(const Servic
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientSource);
+ 
+   // A client in private browsing mode should never be controlled by
+   // a service worker.  The principal origin attributes should guarantee
+   // this invariant.
+   MOZ_DIAGNOSTIC_ASSERT(!mClientInfo.IsPrivateBrowsing());
+ 
++  // A client without access to storage should never be controlled a
++  // a service worker.  If we are already execution ready with a real
++  // window or worker, then verify assert the storage policy is correct.
++  if (GetInnerWindow()) {
++    MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::StorageAllowedForWindow(GetInnerWindow()) ==
++                          nsContentUtils::StorageAccess::eAllow);
++  } else if (GetWorkerPrivate()) {
++    MOZ_DIAGNOSTIC_ASSERT(GetWorkerPrivate()->IsStorageAllowed());
++  }
++
+   if (mController.isSome() && mController.ref() == aServiceWorker) {
+     return;
+   }
+ 
+   mController.reset();
+   mController.emplace(aServiceWorker);
+ 
+   RefPtr<ServiceWorkerContainer> swc;

+ 182 - 0
mozilla-release/patches/1425975-15-59a1.patch

@@ -0,0 +1,182 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172222 18000
+# Node ID 93e07d0261cb5b1025545263c22ac024ffa911c9
+# Parent  fe7a7e5af90168b677fca294e4f4dcf40009ed68
+Bug 1425975 P15 Add session lifetime policy checks to test_third_party_iframe.html. r=asuth
+
+diff --git a/dom/workers/test/serviceworkers/test_third_party_iframes.html b/dom/workers/test/serviceworkers/test_third_party_iframes.html
+--- a/dom/workers/test/serviceworkers/test_third_party_iframes.html
++++ b/dom/workers/test/serviceworkers/test_third_party_iframes.html
+@@ -77,19 +77,20 @@ function runTest(aExpectedResponses) {
+       ok(false, "Expected " + expected.status + " got " + status);
+     }
+     responsesIndex++;
+   };
+ }
+ 
+ // Verify that we can register and intercept a 3rd party iframe with
+ // the given cookie policy.
+-function testShouldIntercept(policy, done) {
++function testShouldIntercept(behavior, lifetime, done) {
+   SpecialPowers.pushPrefEnv({"set": [
+-      ["network.cookie.cookieBehavior", policy]
++      ["network.cookie.cookieBehavior", behavior],
++      ["network.cookie.lifetimePolicy", lifetime],
+   ]}, function() {
+     runTest([{
+       status: "ok"
+     }, {
+       status: "registrationdone",
+       next: function() {
+         iframe.addEventListener("load", testIframeLoaded);
+         iframe.src = origin + basePath + "iframe1.html";
+@@ -116,19 +117,20 @@ function testShouldIntercept(policy, don
+         done();
+       }
+     }]);
+   });
+ }
+ 
+ // Verify that we cannot register a service worker in a 3rd party
+ // iframe with the given cookie policy.
+-function testShouldNotRegister(policy, done) {
++function testShouldNotRegister(behavior, lifetime, done) {
+   SpecialPowers.pushPrefEnv({"set": [
+-      ["network.cookie.cookieBehavior", policy]
++      ["network.cookie.cookieBehavior", behavior],
++      ["network.cookie.lifetimePolicy", lifetime],
+   ]}, function() {
+     runTest([{
+       status: "registrationfailed",
+       next: function() {
+         iframe.addEventListener("load", testIframeLoaded);
+         iframe.src = origin + basePath + "iframe1.html";
+       }
+     }, {
+@@ -147,28 +149,30 @@ function testShouldNotRegister(policy, d
+       }
+     }]);
+   });
+ }
+ 
+ // Verify that if a service worker is already registered a 3rd
+ // party iframe will still not be intercepted with the given cookie
+ // policy.
+-function testShouldNotIntercept(policy, done) {
++function testShouldNotIntercept(behavior, lifetime, done) {
+   SpecialPowers.pushPrefEnv({"set": [
+-      ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT]
++    ["network.cookie.cookieBehavior", BEHAVIOR_ACCEPT],
++    ["network.cookie.lifetimePolicy", LIFETIME_EXPIRE],
+   ]}, function() {
+     runTest([{
+       status: "ok"
+     }, {
+       status: "registrationdone",
+       next: function() {
+         iframe.addEventListener("load", testIframeLoaded);
+         SpecialPowers.pushPrefEnv({"set": [
+-            ["network.cookie.cookieBehavior", policy],
++            ["network.cookie.cookieBehavior", behavior],
++            ["network.cookie.lifetimePolicy", lifetime],
+           ]}, function() {
+             iframe.src = origin + basePath + "iframe1.html";
+           });
+       }
+     }, {
+       status: "networkresponse",
+     }, {
+       status: "worker-networkresponse",
+@@ -181,17 +185,18 @@ function testShouldNotIntercept(policy, 
+         iframe.src = thirdPartyOrigin + basePath + "unregister.html";
+       }
+     }, {
+       status: "uncontrolled",
+     }, {
+       status: "getregistrationfailed",
+       next: function() {
+         SpecialPowers.pushPrefEnv({"set": [
+-            ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT],
++            ["network.cookie.cookieBehavior", BEHAVIOR_ACCEPT],
++            ["network.cookie.lifetimePolicy", LIFETIME_EXPIRE],
+           ]}, function() {
+             iframe.src = thirdPartyOrigin + basePath + "unregister.html";
+           });
+       }
+     }, {
+       status: "controlled",
+     }, {
+       status: "unregistrationdone",
+@@ -199,41 +204,59 @@ function testShouldNotIntercept(policy, 
+         window.onmessage = null;
+         ok(true, "Test finished successfully");
+         done();
+       }
+     }]);
+   });
+ }
+ 
+-const COOKIE_BEHAVIOR_ACCEPT        = 0;
+-const COOKIE_BEHAVIOR_REJECTFOREIGN = 1;
+-const COOKIE_BEHAVIOR_REJECT        = 2;
+-const COOKIE_BEHAVIOR_LIMITFOREIGN  = 3;
++const BEHAVIOR_ACCEPT        = 0;
++const BEHAVIOR_REJECTFOREIGN = 1;
++const BEHAVIOR_REJECT        = 2;
++const BEHAVIOR_LIMITFOREIGN  = 3;
++
++const LIFETIME_EXPIRE        = 0;
++const LIFETIME_SESSION       = 2;
+ 
+ let steps = [() => {
+   SpecialPowers.pushPrefEnv({"set": [
+     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+     ["dom.serviceWorkers.enabled", true],
+     ["dom.serviceWorkers.testing.enabled", true],
+     ["browser.dom.window.dump.enabled", true],
+-    ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT]
++    ["network.cookie.cookieBehavior", BEHAVIOR_ACCEPT],
++    ["network.cookie.lifetimePolicy", LIFETIME_EXPIRE],
+   ]}, next);
+ }, () => {
+-  testShouldIntercept(COOKIE_BEHAVIOR_ACCEPT, next);
++  testShouldIntercept(BEHAVIOR_ACCEPT, LIFETIME_EXPIRE, next);
++}, () => {
++  testShouldNotRegister(BEHAVIOR_REJECTFOREIGN, LIFETIME_EXPIRE, next);
++}, () => {
++  testShouldNotIntercept(BEHAVIOR_REJECTFOREIGN, LIFETIME_EXPIRE, next);
+ }, () => {
+-  testShouldNotRegister(COOKIE_BEHAVIOR_REJECTFOREIGN, next);
++  testShouldNotRegister(BEHAVIOR_REJECT, LIFETIME_EXPIRE, next);
++}, () => {
++  testShouldNotIntercept(BEHAVIOR_REJECT, LIFETIME_EXPIRE, next);
+ }, () => {
+-  testShouldNotIntercept(COOKIE_BEHAVIOR_REJECTFOREIGN, next);
++  testShouldNotRegister(BEHAVIOR_LIMITFOREIGN, LIFETIME_EXPIRE, next);
++}, () => {
++  testShouldNotIntercept(BEHAVIOR_LIMITFOREIGN, LIFETIME_EXPIRE, next);
+ }, () => {
+-  testShouldNotRegister(COOKIE_BEHAVIOR_REJECT, next);
++  testShouldNotIntercept(BEHAVIOR_ACCEPT, LIFETIME_SESSION, next);
+ }, () => {
+-  testShouldNotIntercept(COOKIE_BEHAVIOR_REJECT, next);
++  testShouldNotRegister(BEHAVIOR_REJECTFOREIGN, LIFETIME_SESSION, next);
++}, () => {
++  testShouldNotIntercept(BEHAVIOR_REJECTFOREIGN, LIFETIME_SESSION, next);
+ }, () => {
+-  testShouldNotRegister(COOKIE_BEHAVIOR_LIMITFOREIGN, next);
++  testShouldNotRegister(BEHAVIOR_REJECT, LIFETIME_SESSION, next);
++}, () => {
++  testShouldNotIntercept(BEHAVIOR_REJECT, LIFETIME_SESSION, next);
+ }, () => {
+-  testShouldNotIntercept(COOKIE_BEHAVIOR_LIMITFOREIGN, next);
++  testShouldNotRegister(BEHAVIOR_LIMITFOREIGN, LIFETIME_SESSION, next);
++}, () => {
++  testShouldNotIntercept(BEHAVIOR_LIMITFOREIGN, LIFETIME_SESSION, next);
+ }];
+ 
+ </script>
+ </pre>
+ </body>
+ </html>

+ 248 - 0
mozilla-release/patches/1425975-16-59a1.patch

@@ -0,0 +1,248 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172222 18000
+# Node ID 5b1109ca1c1f887a793748f1e115152a42122151
+# Parent  54b5f539a81dc4b0824b1b2ab4539fedf06766c3
+Bug 1425975 P16 Make nsDocShell check for session cookie lifetime policy before allowing service worker intercept. r=asuth
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -3438,21 +3438,20 @@ nsDocShell::MaybeCreateInitialClientSour
+   nsCOMPtr<nsIDocShell> parent = GetParentDocshell();
+   nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
+   nsPIDOMWindowInner* parentInner =
+     parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
+   if (!parentInner) {
+     return;
+   }
+ 
+-  // We're done if there is no parent controller.  Also, don't inherit
+-  // the controller if we're sandboxed.  This matches our behavior in
+-  // ShouldPrepareForIntercept(),
++  // We're done if there is no parent controller or if this docshell
++  // is not permitted to control for some reason.
+   Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
+-  if (controller.isNothing() || mSandboxFlags) {
++  if (controller.isNothing() || !ServiceWorkerAllowedToControlWindow(nullptr)) {
+     return;
+   }
+ 
+   nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
+   if (!swm) {
+     return;
+   }
+ 
+@@ -14888,16 +14887,64 @@ nsDocShell::CanSetOriginAttributes()
+         return false;
+       }
+     }
+   }
+ 
+   return true;
+ }
+ 
++bool
++nsDocShell::ServiceWorkerAllowedToControlWindow(nsIURI* aURI)
++{
++  // NOTE: Ideally this method would call one of the
++  //       nsContentUtils::StorageAllowed*() methods to determine if the
++  //       interception is allowed.  Unfortunately we cannot safely do this
++  //       before the first window loads in the child process because the
++  //       permission manager might not have all its data yet.  Therefore,
++  //       we use this somewhat lame alternate implementation here.  Once
++  //       interception is moved to the parent process we should switch
++  //       to calling nsContentUtils::StorageAllowed*().  See bug 1428130.
++
++  if (UsePrivateBrowsing() || mSandboxFlags) {
++    return false;
++  }
++
++  uint32_t cookieBehavior = nsContentUtils::CookiesBehavior();
++  uint32_t lifetimePolicy = nsContentUtils::CookiesLifetimePolicy();
++  if (cookieBehavior == nsICookieService::BEHAVIOR_REJECT ||
++      lifetimePolicy == nsICookieService::ACCEPT_SESSION) {
++    return false;
++  }
++
++  if (!aURI || cookieBehavior == nsICookieService::BEHAVIOR_ACCEPT) {
++    return true;
++  }
++
++  nsCOMPtr<nsIDocShellTreeItem> parent;
++  GetSameTypeParent(getter_AddRefs(parent));
++  nsCOMPtr<nsPIDOMWindowOuter> parentWindow = parent ? parent->GetWindow()
++                                                     : nullptr;
++  if (parentWindow) {
++    nsresult rv = NS_OK;
++    nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
++      do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
++    if (thirdPartyUtil) {
++      bool isThirdPartyURI = true;
++      rv = thirdPartyUtil->IsThirdPartyWindow(parentWindow, aURI,
++                                              &isThirdPartyURI);
++      if (NS_SUCCEEDED(rv) && isThirdPartyURI) {
++        return false;
++      }
++    }
++  }
++
++  return true;
++}
++
+ nsresult
+ nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs)
+ {
+   if (!CanSetOriginAttributes()) {
+     return NS_ERROR_FAILURE;
+   }
+ 
+   AssertOriginAttributesMatchPrivateBrowsing();
+@@ -15090,73 +15137,49 @@ nsDocShell::MaybeNotifyKeywordSearchLoad
+ #endif
+ }
+ 
+ NS_IMETHODIMP
+ nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNonSubresourceRequest,
+                                       bool* aShouldIntercept)
+ {
+   *aShouldIntercept = false;
+-  // No in private browsing
+-  if (UsePrivateBrowsing()) {
+-    return NS_OK;
+-  }
+-
+-  if (mSandboxFlags) {
+-    // If we're sandboxed, don't intercept.
+-    return NS_OK;
+-  }
+-
+-  uint32_t cookieBehavior = nsContentUtils::CookiesBehavior();
+-  if (cookieBehavior == nsICookieService::BEHAVIOR_REJECT) {
+-    // If cookies are disabled, don't intercept.
+-    return NS_OK;
+-  }
+-
+-  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+-  if (!swm) {
+-    return NS_OK;
+-  }
+-
++
++  // For subresource requests we base our decision solely on the client's
++  // controller value.  Any settings that would have blocked service worker
++  // access should have been set before the initial navigation created the
++  // window.
+   if (!aIsNonSubresourceRequest) {
+     nsCOMPtr<nsIDocument> doc = GetDocument();
+     if (!doc) {
+       return NS_ERROR_NOT_AVAILABLE;
+     }
+ 
+     ErrorResult rv;
+     *aShouldIntercept = doc->GetController().isSome();
+     if (NS_WARN_IF(rv.Failed())) {
+       return rv.StealNSResult();
+     }
+ 
+     return NS_OK;
+   }
+ 
+-  // If the user has set a cookie policy that restricts cookies, then
+-  // avoid intercepting 3rd party iframes.
+-  if (cookieBehavior != nsICookieService::BEHAVIOR_ACCEPT) {
+-    nsCOMPtr<nsIDocShellTreeItem> parent;
+-    GetSameTypeParent(getter_AddRefs(parent));
+-    nsCOMPtr<nsPIDOMWindowOuter> parentWindow = parent ? parent->GetWindow()
+-                                                       : nullptr;
+-    if (parentWindow) {
+-      nsresult rv = NS_OK;
+-      nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
+-        do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
+-      NS_ENSURE_SUCCESS(rv, rv);
+-
+-      bool isThirdPartyURI = true;
+-      rv = thirdPartyUtil->IsThirdPartyWindow(parentWindow, aURI, &isThirdPartyURI);
+-      if (NS_SUCCEEDED(rv) && isThirdPartyURI) {
+-        return NS_OK;
+-      }
+-    }
+-  }
+-
++  // For navigations, first check to see if we are allowed to control a
++  // window with the given URL.
++  if (!ServiceWorkerAllowedToControlWindow(aURI)) {
++    return NS_OK;
++  }
++
++  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
++  if (!swm) {
++    return NS_OK;
++  }
++
++  // We're allowed to control a window, so check with the ServiceWorkerManager
++  // for a matching service worker.
+   nsCOMPtr<nsIPrincipal> principal =
+     BasePrincipal::CreateCodebasePrincipal(aURI, mOriginAttributes);
+   *aShouldIntercept = swm->IsAvailable(principal, aURI);
+   return NS_OK;
+ }
+ 
+ NS_IMETHODIMP
+ nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel)
+diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
+--- a/docshell/base/nsDocShell.h
++++ b/docshell/base/nsDocShell.h
+@@ -357,16 +357,27 @@ public:
+   void SetAncestorOuterWindowIDs(nsTArray<uint64_t>&& aAncestorOuterWindowIDs)
+   {
+     mAncestorOuterWindowIDs = mozilla::Move(aAncestorOuterWindowIDs);
+   }
+ 
+ private:
+   bool CanSetOriginAttributes();
+ 
++  // Determine if a service worker is allowed to control a window in this
++  // docshell with the given URL.  If there are any reasons it should not,
++  // this will return false.  If true is returned then the window *may* be
++  // controlled.  The caller must still consult either the parent controller
++  // or the ServiceWorkerManager to determine if a service worker should
++  // actually control the window.
++  //
++  // A nullptr URL is considered to be an about:blank window and will not
++  // trigger 3rd party iframe checks.
++  bool ServiceWorkerAllowedToControlWindow(nsIURI* aURI);
++
+ public:
+   const mozilla::OriginAttributes&
+   GetOriginAttributes()
+   {
+     return mOriginAttributes;
+   }
+ 
+   nsresult SetOriginAttributes(const mozilla::OriginAttributes& aAttrs);
+diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
+--- a/dom/base/nsContentUtils.h
++++ b/dom/base/nsContentUtils.h
+@@ -2978,16 +2978,21 @@ public:
+ 
+   static bool IsNonSubresourceRequest(nsIChannel* aChannel);
+ 
+   static uint32_t CookiesBehavior()
+   {
+     return sCookiesBehavior;
+   }
+ 
++  static uint32_t CookiesLifetimePolicy()
++  {
++    return sCookiesLifetimePolicy;
++  }
++
+   // The order of these entries matters, as we use std::min for total ordering
+   // of permissions. Private Browsing is considered to be more limiting
+   // then session scoping
+   enum class StorageAccess {
+     // Don't allow access to the storage
+     eDeny = 0,
+     // Allow access to the storage, but only if it is secure to do so in a
+     // private browsing context.

+ 134 - 0
mozilla-release/patches/1425975-17-59a1.patch

@@ -0,0 +1,134 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515172223 18000
+# Node ID 14123832068511d74fc6e766feee1c5e77e28ebd
+# Parent  21a9dce1e778a7af71c4dac83c20a6b2d23b0d8e
+Bug 1425975 P17 Make web extension tests wait for service worker to activate to avoid races. r=kmag
+
+diff --git a/toolkit/components/extensions/test/mochitest/head.js b/toolkit/components/extensions/test/mochitest/head.js
+--- a/toolkit/components/extensions/test/mochitest/head.js
++++ b/toolkit/components/extensions/test/mochitest/head.js
+@@ -51,16 +51,31 @@ let Assert = {
+ function waitForLoad(win) {
+   return new Promise(resolve => {
+     win.addEventListener("load", function() {
+       resolve();
+     }, {capture: true, once: true});
+   });
+ }
+ 
++/* exported waitForState */
++
++function waitForState(sw, state) {
++  return new Promise(resolve => {
++    if (sw.state === state) {
++      return resolve();
++    }
++    sw.addEventListener("statechange", function onStateChange() {
++      if (sw.state === state) {
++        sw.removeEventListener("statechange", onStateChange);
++        resolve();
++      }
++    });
++  });
++}
+ /* exported loadChromeScript */
+ function loadChromeScript(fn) {
+   let wrapper = `
+ const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+ (${fn.toString()})();`;
+ 
+   return SpecialPowers.loadChromeScript(new Function(wrapper));
+ }
+diff --git a/toolkit/components/extensions/test/mochitest/test_ext_webrequest_background_events.html b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_background_events.html
+--- a/toolkit/components/extensions/test/mochitest/test_ext_webrequest_background_events.html
++++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_background_events.html
+@@ -55,16 +55,17 @@ add_task(async function test_webRequest_
+           {urls: ["https://example.com/*"]}
+         );
+       }
+     },
+   });
+ 
+   await extension.startup();
+   let registration = await navigator.serviceWorker.register("webrequest_worker.js", {scope: "."});
++  await waitForState(registration.installing, "activated");
+   await extension.awaitMessage("done");
+   await registration.unregister();
+   await extension.unload();
+ });
+ 
+ add_task(async function test_webRequest_background_events() {
+   let extension = ExtensionTestUtils.loadExtension({
+     manifest: {
+diff --git a/toolkit/components/extensions/test/mochitest/test_ext_webrequest_filter.html b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_filter.html
+--- a/toolkit/components/extensions/test/mochitest/test_ext_webrequest_filter.html
++++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_filter.html
+@@ -85,21 +85,23 @@ add_task(async function test_webRequest_
+ 
+   // We should not get events for a new window load.
+   let newWindow = window.open("file_image_good.png", "_blank", "width=100,height=100");
+   await waitForLoad(newWindow);
+   newWindow.close();
+ 
+   // We should not get background events.
+   let registration = await navigator.serviceWorker.register("webrequest_worker.js?test0", {scope: "."});
++  await waitForState(registration.installing, "activated");
+ 
+   // We should get events for the reload.
+   testWindow.location = "file_image_bad.png";
+   await extension.awaitMessage("done");
+ 
++  testWindow.location = "about:blank";
+   await registration.unregister();
+   await extension.unload();
+ });
+ 
+ add_task(async function test_webRequest_filter_tab() {
+   await SpecialPowers.pushPrefEnv({
+     set: [["dom.serviceWorkers.testing.enabled", true]],
+   });
+@@ -133,21 +135,23 @@ add_task(async function test_webRequest_
+     // We should not get events for a new window load.
+     let newWindow = window.open(img, "_blank", "width=100,height=100");
+     await waitForLoad(newWindow);
+     newWindow.close();
+   }
+ 
+   // We should not get background events.
+   let registration = await navigator.serviceWorker.register("webrequest_worker.js?test1", {scope: "."});
++  await waitForState(registration.installing, "activated");
+ 
+   // We should get events for the reload.
+   testWindow.location = img;
+   await extension.awaitMessage("done");
+ 
++  testWindow.location = "about:blank";
+   await registration.unregister();
+   await extension.unload();
+ });
+ 
+ 
+ add_task(async function test_webRequest_filter_background() {
+   await SpecialPowers.pushPrefEnv({
+     set: [["dom.serviceWorkers.testing.enabled", true]],
+@@ -181,17 +185,19 @@ add_task(async function test_webRequest_
+   extension.sendMessage("set-expected", {expect, origin: location.href});
+   await extension.awaitMessage("continue");
+ 
+   // We should not get events for a window.
+   testWindow.location = "file_image_bad.png";
+ 
+   // We should get events for the background page.
+   let registration = await navigator.serviceWorker.register(SimpleTest.getTestFileURL("webrequest_worker.js?test2"), {scope: "."});
++  await waitForState(registration.installing, "activated");
+   await extension.awaitMessage("done");
++  testWindow.location = "about:blank";
+   await registration.unregister();
+ 
+   await extension.unload();
+ });
+ 
+ add_task(async function teardown() {
+   testWindow.close();
+ });

+ 33 - 0
mozilla-release/patches/1425975-18-59a1.patch

@@ -0,0 +1,33 @@
+# HG changeset patch
+# User Sebastian Hengst <archaeopteryx@coole-files.de>
+# Date 1515193852 -7200
+# Node ID b84fe2ad1ca27fc30c2e3f609b8f766185652560
+# Parent  a130c2c1923221429ac686e1a577b438cfb5a2aa
+Bug 1425975 - Fix merge bustage. r=bustage-fix a=bustage-fix
+
+diff --git a/docshell/base/nsDocShell.h.1425975-18.later b/docshell/base/nsDocShell.h.1425975-18.later
+new file mode 100644
+--- /dev/null
++++ b/docshell/base/nsDocShell.h.1425975-18.later
+@@ -0,0 +1,21 @@
++--- nsDocShell.h
+++++ nsDocShell.h
++@@ -347,18 +347,16 @@ public:
++    *
++    * This method steals the data from the passed-in array.
++    */
++   void SetAncestorOuterWindowIDs(nsTArray<uint64_t>&& aAncestorOuterWindowIDs)
++   {
++     mAncestorOuterWindowIDs = mozilla::Move(aAncestorOuterWindowIDs);
++   }
++ 
++-  bool CanSetOriginAttributes();
++-
++   // Determine if a service worker is allowed to control a window in this
++   // docshell with the given URL.  If there are any reasons it should not,
++   // this will return false.  If true is returned then the window *may* be
++   // controlled.  The caller must still consult either the parent controller
++   // or the ServiceWorkerManager to determine if a service worker should
++   // actually control the window.
++   //
++   // A nullptr URL is considered to be an about:blank window and will not

+ 66 - 0
mozilla-release/patches/1425975-19-59a1.patch

@@ -0,0 +1,66 @@
+# HG changeset patch
+# User Sebastian Hengst <archaeopteryx@coole-files.de>
+# Date 1515196306 -7200
+# Node ID 9099a6ed993f0113c47c0d9e800bf0ff6e1a1dc1
+# Parent  ac43086e18c70dfce9591a4f06ecfa00ae429e8e
+Bug 1425975 - Follow-up: Move method to correct position. r=merge-fix a=merge-fix
+
+diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
+--- a/docshell/base/nsDocShell.h
++++ b/docshell/base/nsDocShell.h
+@@ -357,27 +357,16 @@ public:
+   void SetAncestorOuterWindowIDs(nsTArray<uint64_t>&& aAncestorOuterWindowIDs)
+   {
+     mAncestorOuterWindowIDs = mozilla::Move(aAncestorOuterWindowIDs);
+   }
+ 
+ private:
+   bool CanSetOriginAttributes();
+ 
+-  // Determine if a service worker is allowed to control a window in this
+-  // docshell with the given URL.  If there are any reasons it should not,
+-  // this will return false.  If true is returned then the window *may* be
+-  // controlled.  The caller must still consult either the parent controller
+-  // or the ServiceWorkerManager to determine if a service worker should
+-  // actually control the window.
+-  //
+-  // A nullptr URL is considered to be an about:blank window and will not
+-  // trigger 3rd party iframe checks.
+-  bool ServiceWorkerAllowedToControlWindow(nsIURI* aURI);
+-
+ public:
+   const mozilla::OriginAttributes&
+   GetOriginAttributes()
+   {
+     return mOriginAttributes;
+   }
+ 
+   nsresult SetOriginAttributes(const mozilla::OriginAttributes& aAttrs);
+@@ -845,16 +834,27 @@ protected:
+   // the principal is not provided we will attempt to inherit it when we
+   // are sure it will match what the real about:blank window principal
+   // would have been.  There are some corner cases where we cannot easily
+   // determine the correct principal and will not create the ClientSource.
+   // In these cases the initial about:blank will appear to not exist until
+   // its real document and window are created.
+   void MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal = nullptr);
+ 
++  // Determine if a service worker is allowed to control a window in this
++  // docshell with the given URL.  If there are any reasons it should not,
++  // this will return false.  If true is returned then the window *may* be
++  // controlled.  The caller must still consult either the parent controller
++  // or the ServiceWorkerManager to determine if a service worker should
++  // actually control the window.
++  //
++  // A nullptr URL is considered to be an about:blank window and will not
++  // trigger 3rd party iframe checks.
++  bool ServiceWorkerAllowedToControlWindow(nsIURI* aURI);
++
+   // Return the ClientInfo for the initial about:blank window, if it exists
+   // or we have speculatively created a ClientSource in
+   // MaybeCreateInitialClientSource().  This can return a ClientInfo object
+   // even if GetExtantDoc() returns nullptr.
+   mozilla::Maybe<mozilla::dom::ClientInfo> GetInitialClientInfo() const;
+ 
+ protected:
+   nsresult GetCurScrollPos(int32_t aScrollOrientation, int32_t* aCurPos);

+ 51 - 0
mozilla-release/patches/1426250-1-59a1.patch

@@ -0,0 +1,51 @@
+# HG changeset patch
+# User Gijs Kruitbosch <gijskruitbosch@gmail.com>
+# Date 1515187842 0
+# Node ID 462b35675a119a778f35aed836aa88b606f0babd
+# Parent  4613d73eede2733fa52f109cde033a0da87ad8ba
+Bug 1426250 - allow EventUtils to dispatch drag/mouse events through the DOM only, r=enndeakin
+
+MozReview-Commit-ID: HmdRdTQ05pm
+
+diff --git a/testing/mochitest/tests/SimpleTest/EventUtils.js b/testing/mochitest/tests/SimpleTest/EventUtils.js
+--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
++++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
+@@ -206,16 +206,20 @@ function sendDragEvent(aEvent, aTarget, 
+   var relatedTargetArg = aEvent.relatedTarget || null;
+   var dataTransfer     = aEvent.dataTransfer  || null;
+ 
+   event.initDragEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg,
+                       screenXArg, screenYArg, clientXArg, clientYArg,
+                       ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
+                       buttonArg, relatedTargetArg, dataTransfer);
+ 
++  if (aEvent._domDispatchOnly) {
++    return aTarget.dispatchEvent(event);
++  }
++
+   var utils = _getDOMWindowUtils(aWindow);
+   return utils.dispatchDOMEventViaPresShell(aTarget, event, true);
+ }
+ 
+ /**
+  * Send the char aChar to the focused element.  This method handles casing of
+  * chars (sends the right charcode, and sends a shift key for uppercase chars).
+  * No other modifiers are handled at this point.
+@@ -2129,16 +2133,17 @@ function createDragEventObject(aType, aD
+   // nsContentUtils::SetDataTransferInEvent for actual impl).
+   dataTransfer.dropEffect = aDataTransfer.dropEffect;
+ 
+   return Object.assign({
+     type: aType,
+     screenX: destScreenX, screenY: destScreenY,
+     clientX: destClientX, clientY: destClientY,
+     dataTransfer: dataTransfer,
++    _domDispatchOnly: aDragEvent._domDispatchOnly,
+   }, aDragEvent);
+ }
+ 
+ /**
+  * Emulate a event sequence of dragstart, dragenter, and dragover.
+  *
+  * @param aSrcElement   The element to use to start the drag.
+  * @param aDestElement  The element to fire the dragover, dragenter events

+ 44 - 0
mozilla-release/patches/1426250-2-59a1.patch

@@ -0,0 +1,44 @@
+# HG changeset patch
+# User Gijs Kruitbosch <gijskruitbosch@gmail.com>
+# Date 1515187913 0
+# Node ID 5551470e71c346c115b9aef08b7a75e9d8607e8e
+# Parent  04c9c98098ccf6b9a4b88095c25dca253e1f5d47
+Bug 1426250 - allow changing log preference at runtime for CUI.jsm, r=jaws
+
+MozReview-Commit-ID: 9kYBC27wowg
+
+diff --git a/browser/components/customizableui/CustomizableUI.jsm b/browser/components/customizableui/CustomizableUI.jsm
+--- a/browser/components/customizableui/CustomizableUI.jsm
++++ b/browser/components/customizableui/CustomizableUI.jsm
+@@ -159,22 +159,29 @@ var gUIStateBeforeReset = {
+   drawInTitlebar: null,
+   currentTheme: null,
+   uiDensity: null,
+   autoTouchMode: null,
+ };
+ 
+ var gDefaultPanelPlacements = null;
+ 
++XPCOMUtils.defineLazyPreferenceGetter(this, "gDebuggingEnabled", kPrefCustomizationDebug, false,
++  (pref, oldVal, newVal) => {
++    if (typeof log != "undefined") {
++      log.maxLogLevel = newVal ? "all" : "log";
++    }
++  }
++);
++
+ XPCOMUtils.defineLazyGetter(this, "log", () => {
+   let scope = {};
+   ChromeUtils.import("resource://gre/modules/Console.jsm", scope);
+-  let debug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
+   let consoleOptions = {
+-    maxLogLevel: debug ? "all" : "log",
++    maxLogLevel: gDebuggingEnabled ? "all" : "log",
+     prefix: "CustomizableUI",
+   };
+   return new scope.ConsoleAPI(consoleOptions);
+ });
+ 
+ var CustomizableUIInternal = {
+   initialize() {
+     log.debug("Initializing");

+ 406 - 0
mozilla-release/patches/1426250-3-59a1.patch

@@ -0,0 +1,406 @@
+# HG changeset patch
+# User Gijs Kruitbosch <gijskruitbosch@gmail.com>
+# Date 1515188127 0
+# Node ID 21ce1e95d697cbc1bdaa45d124e55642ff5b2fd3
+# Parent  6f29e2a7b9a2d50ef649139ae1d8bd3223768bd0
+Bug 1426250 - make tests actually pass valid mouse coordinates when testing Customize Mode, r=jaws
+
+MozReview-Commit-ID: IglKedBqrWQ
+
+diff --git a/browser/components/customizableui/CustomizeMode.jsm b/browser/components/customizableui/CustomizeMode.jsm
+--- a/browser/components/customizableui/CustomizeMode.jsm
++++ b/browser/components/customizableui/CustomizeMode.jsm
+@@ -2301,20 +2301,16 @@ CustomizeMode.prototype = {
+       aElement = aElement.parentNode;
+     }
+ 
+     return null;
+   },
+ 
+   _getDragOverNode(aEvent, aAreaElement, aAreaType, aDraggedItemId) {
+     let expectedParent = aAreaElement.customizationTarget || aAreaElement;
+-    // Our tests are stupid. Cope:
+-    if (!aEvent.clientX && !aEvent.clientY) {
+-      return aEvent.target;
+-    }
+     // Offset the drag event's position with the offset to the center of
+     // the thing we're dragging
+     let dragX = aEvent.clientX - this._dragOffset.x;
+     let dragY = aEvent.clientY - this._dragOffset.y;
+ 
+     // Ensure this is within the container
+     let boundsContainer = expectedParent;
+     // NB: because the panel UI itself is inside a scrolling container, we need
+diff --git a/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js b/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
+--- a/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
++++ b/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
+@@ -10,53 +10,60 @@ const kPanel = CustomizableUI.AREA_PANEL
+ const kToolbar = CustomizableUI.AREA_NAVBAR;
+ const kVisiblePalette = "customization-palette";
+ const kPlaceholderClass = "panel-customization-placeholder";
+ 
+ function checkWrapper(id) {
+   is(document.querySelectorAll("#wrapper-" + id).length, 1, "There should be exactly 1 wrapper for " + id + " in the customizing window.");
+ }
+ 
+-async function ensureVisibleIfInPalette(node) {
+-    if (node.parentNode.parentNode == gNavToolbox.palette) {
+-      node.scrollIntoView();
+-      window.QueryInterface(Ci.nsIInterfaceRequestor);
+-      let dwu = window.getInterface(Ci.nsIDOMWindowUtils);
+-      await BrowserTestUtils.waitForCondition(() => {
+-        let nodeBounds = dwu.getBoundsWithoutFlushing(node);
+-        let paletteBounds = dwu.getBoundsWithoutFlushing(gNavToolbox.palette);
+-        return nodeBounds.top >= paletteBounds.top && nodeBounds.bottom <= paletteBounds.bottom;
+-      });
++async function ensureVisible(node) {
++  let isInPalette = node.parentNode.parentNode == gNavToolbox.palette;
++  if (isInPalette) {
++    node.scrollIntoView();
++  }
++  window.QueryInterface(Ci.nsIInterfaceRequestor);
++  let dwu = window.getInterface(Ci.nsIDOMWindowUtils);
++  await BrowserTestUtils.waitForCondition(() => {
++    let nodeBounds = dwu.getBoundsWithoutFlushing(node);
++    if (isInPalette) {
++      let paletteBounds = dwu.getBoundsWithoutFlushing(gNavToolbox.palette);
++      if (!(nodeBounds.top >= paletteBounds.top && nodeBounds.bottom <= paletteBounds.bottom)) {
++        return false;
++      }
+     }
++    return nodeBounds.height && nodeBounds.width;
++  });
+ }
+ 
+ var move = {
+   "drag": async function(id, target) {
+     let targetNode = document.getElementById(target);
+     if (targetNode.customizationTarget) {
+       targetNode = targetNode.customizationTarget;
+     }
+     let nodeToMove = document.getElementById(id);
+-    await ensureVisibleIfInPalette(nodeToMove);
+-    simulateItemDrag(nodeToMove, targetNode);
++    await ensureVisible(nodeToMove);
++
++    simulateItemDrag(nodeToMove, targetNode, "end");
+   },
+   "dragToItem": async function(id, target) {
+     let targetNode = document.getElementById(target);
+     if (targetNode.customizationTarget) {
+       targetNode = targetNode.customizationTarget;
+     }
+     let items = targetNode.querySelectorAll("toolbarpaletteitem:not(." + kPlaceholderClass + ")");
+     if (target == kPanel) {
+       targetNode = items[items.length - 1];
+     } else {
+       targetNode = items[0];
+     }
+     let nodeToMove = document.getElementById(id);
+-    await ensureVisibleIfInPalette(nodeToMove);
+-    simulateItemDrag(nodeToMove, targetNode);
++    await ensureVisible(nodeToMove);
++    simulateItemDrag(nodeToMove, targetNode, "start");
+   },
+   "API": function(id, target) {
+     if (target == kVisiblePalette) {
+       return CustomizableUI.removeWidgetFromArea(id);
+     }
+     return CustomizableUI.addWidgetToArea(id, target, null);
+   }
+ };
+@@ -146,16 +153,21 @@ async function checkPanel(id, method) {
+ }
+ 
+ async function checkPalette(id, method) {
+   // Move back to palette:
+   await move[method](id, kVisiblePalette);
+   ok(CustomizableUI.inDefaultState, "Should end in default state");
+   let visibleChildren = gCustomizeMode.visiblePalette.children;
+   let expectedChild = method == "dragToItem" ? visibleChildren[0] : visibleChildren[visibleChildren.length - 1];
++  // Items dragged to the end of the palette should be the final item. That they're the penultimate
++  // item when dragged is tracked in bug 1395950. Once that's fixed, this hack can be removed.
++  if (method == "drag") {
++    expectedChild = expectedChild.previousElementSibling;
++  }
+   is(expectedChild.firstChild.id, id, "Widget " + id + " was moved using " + method + " and should now be wrapped in palette in customizing window.");
+   if (id == kXULWidgetId) {
+     ok(otherWin.gNavToolbox.palette.querySelector("#" + id), "Widget " + id + " should be in invisible palette in other window.");
+   }
+   checkWrapper(id);
+ }
+ 
+ // This test needs a XUL button that's in the palette by default. No such
+diff --git a/browser/components/customizableui/test/browser_878452_drag_to_panel.js b/browser/components/customizableui/test/browser_878452_drag_to_panel.js
+--- a/browser/components/customizableui/test/browser_878452_drag_to_panel.js
++++ b/browser/components/customizableui/test/browser_878452_drag_to_panel.js
+@@ -10,17 +10,17 @@ add_task(async function() {
+   await startCustomizing();
+   let btn = document.getElementById("feed-button");
+   let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
+ 
+   let lastButtonIndex = placements.length - 1;
+   let lastButton = placements[lastButtonIndex];
+   let placementsAfterInsert = placements.slice(0, lastButtonIndex).concat(["feed-button", lastButton]);
+   let lastButtonNode = document.getElementById(lastButton);
+-  simulateItemDrag(btn, lastButtonNode);
++  simulateItemDrag(btn, lastButtonNode, "start");
+   assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterInsert);
+   ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+   let palette = document.getElementById("customization-palette");
+   simulateItemDrag(btn, palette);
+   ok(CustomizableUI.inDefaultState, "Should be in default state again.");
+ });
+ 
+ // Dragging an item from the palette to the panel itself should also work.
+@@ -55,12 +55,13 @@ add_task(async function() {
+   simulateItemDrag(btn, panel);
+   assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
+   ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
+   let palette = document.getElementById("customization-palette");
+   simulateItemDrag(btn, palette);
+   assertAreaPlacements(panel.id, []);
+ });
+ 
+-add_task(async function asyncCleanup() {
++registerCleanupFunction(async function asyncCleanup() {
++  CustomizableUI.destroyWidget("cui-panel-item-to-drag-to");
+   await endCustomizing();
+   await resetCustomization();
+ });
+diff --git a/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js b/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js
+--- a/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js
++++ b/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js
+@@ -13,23 +13,24 @@ add_task(async function() {
+   skippedItem = document.createElement("toolbarbutton");
+   skippedItem.id = "test-skipintoolbarset-item";
+   skippedItem.setAttribute("label", "Test");
+   skippedItem.setAttribute("skipintoolbarset", "true");
+   skippedItem.setAttribute("removable", "true");
+   navbar.customizationTarget.appendChild(skippedItem);
+   let downloadsButton = document.getElementById("downloads-button");
+   await startCustomizing();
++  await waitForElementShown(skippedItem);
+   ok(CustomizableUI.inDefaultState, "Should still be in default state");
+-  simulateItemDrag(skippedItem, downloadsButton);
++  simulateItemDrag(skippedItem, downloadsButton, "start");
+   ok(CustomizableUI.inDefaultState, "Should still be in default state");
+   let skippedItemWrapper = skippedItem.parentNode;
+   is(skippedItemWrapper.nextSibling && skippedItemWrapper.nextSibling.id,
+      downloadsButton.parentNode.id, "Should be next to downloads button");
+-  simulateItemDrag(downloadsButton, skippedItem);
++  simulateItemDrag(downloadsButton, skippedItem, "start");
+   let downloadWrapper = downloadsButton.parentNode;
+   is(downloadWrapper.nextSibling && downloadWrapper.nextSibling.id,
+      skippedItem.parentNode.id, "Should be next to skipintoolbarset item");
+   ok(CustomizableUI.inDefaultState, "Should still be in default state");
+ });
+ 
+ add_task(async function asyncCleanup() {
+   await endCustomizing();
+diff --git a/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js b/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
+--- a/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
++++ b/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
+@@ -40,17 +40,17 @@ add_task(async function() {
+   // Make sure we have some hidden items at the end of the nav-bar.
+   navbar.insertItem(hidden1.id);
+   navbar.insertItem(hidden2.id);
+ 
+   // Drag an item and drop it onto the nav-bar customization target, but
+   // not over a particular item.
+   await startCustomizing();
+   let downloadsButton = document.getElementById("downloads-button");
+-  simulateItemDrag(downloadsButton, navbar.customizationTarget);
++  simulateItemDrag(downloadsButton, navbar.customizationTarget, "end");
+ 
+   await endCustomizing();
+ 
+   is(downloadsButton.previousSibling.id, lastVisible.id,
+      "The downloads button should be placed after the last visible item.");
+ 
+   await resetCustomization();
+ });
+diff --git a/browser/components/customizableui/test/browser_newtab_button_customizemode.js.1426250-3.later b/browser/components/customizableui/test/browser_newtab_button_customizemode.js.1426250-3.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/customizableui/test/browser_newtab_button_customizemode.js.1426250-3.later
+@@ -0,0 +1,79 @@
++--- browser_newtab_button_customizemode.js
+++++ browser_newtab_button_customizemode.js
++@@ -27,46 +27,49 @@ function assertNewTabButton(which) {
++   }
++ }
++ 
++ /**
++  * Add and remove items *after* the new tab button in customize mode.
++  */
++ add_task(async function addremove_after_newtab_customizemode() {
++   await startCustomizing();
++-  simulateItemDrag(document.getElementById("home-button"),
++-                   kGlobalNewTabButton.parentNode.nextElementSibling);
+++  await waitForElementShown(kGlobalNewTabButton);
+++  simulateItemDrag(document.getElementById("home-button"), kGlobalNewTabButton, "end");
++   ok(gBrowser.tabContainer.hasAttribute("hasadjacentnewtabbutton"),
++     "tabs should have the adjacent newtab attribute");
++   await endCustomizing();
++   assertNewTabButton("inner");
++ 
++   await startCustomizing();
++-  simulateItemDrag(document.getElementById("home-button"),
++-    document.getElementById("stop-reload-button").parentNode.nextElementSibling);
+++  let dropTarget = document.getElementById("stop-reload-button");
+++  await waitForElementShown(dropTarget);
+++  simulateItemDrag(document.getElementById("home-button"), dropTarget, "end");
++   ok(gBrowser.tabContainer.hasAttribute("hasadjacentnewtabbutton"),
++     "tabs should still have the adjacent newtab attribute");
++   await endCustomizing();
++   assertNewTabButton("inner");
++   ok(CustomizableUI.inDefaultState, "Should be in default state");
++ });
++ 
++ /**
++  * Add and remove items *before* the new tab button in customize mode.
++  */
++ add_task(async function addremove_before_newtab_customizemode() {
++   await startCustomizing();
++-  simulateItemDrag(document.getElementById("home-button"), kGlobalNewTabButton);
+++  await waitForElementShown(kGlobalNewTabButton);
+++  simulateItemDrag(document.getElementById("home-button"), kGlobalNewTabButton, "start");
++   ok(!gBrowser.tabContainer.hasAttribute("hasadjacentnewtabbutton"),
++     "tabs should no longer have the adjacent newtab attribute");
++   await endCustomizing();
++   assertNewTabButton("global");
++   await startCustomizing();
++-  simulateItemDrag(document.getElementById("home-button"),
++-    document.getElementById("stop-reload-button").parentNode.nextElementSibling);
+++  let dropTarget = document.getElementById("stop-reload-button");
+++  await waitForElementShown(dropTarget);
+++  simulateItemDrag(document.getElementById("home-button"), dropTarget, "end");
++   ok(gBrowser.tabContainer.hasAttribute("hasadjacentnewtabbutton"),
++     "tabs should have the adjacent newtab attribute again");
++   await endCustomizing();
++   assertNewTabButton("inner");
++   ok(CustomizableUI.inDefaultState, "Should be in default state");
++ });
++ 
++ /**
++@@ -105,17 +108,18 @@ add_task(async function addremove_before
++   ok(CustomizableUI.inDefaultState, "Should be in default state");
++ });
++ 
++ /**
++   * Reset to defaults in customize mode to see if that doesn't break things.
++   */
++ add_task(async function reset_before_newtab_customizemode() {
++   await startCustomizing();
++-  simulateItemDrag(document.getElementById("home-button"), kGlobalNewTabButton);
+++  await waitForElementShown(kGlobalNewTabButton);
+++  simulateItemDrag(document.getElementById("home-button"), kGlobalNewTabButton, "start");
++   ok(!gBrowser.tabContainer.hasAttribute("hasadjacentnewtabbutton"),
++     "tabs should no longer have the adjacent newtab attribute");
++   await endCustomizing();
++   assertNewTabButton("global");
++   await startCustomizing();
++   await gCustomizeMode.reset();
++   ok(gBrowser.tabContainer.hasAttribute("hasadjacentnewtabbutton"),
++     "tabs should have the adjacent newtab attribute again");
+diff --git a/browser/components/customizableui/test/browser_remove_customized_specials.js.1426250-3.later b/browser/components/customizableui/test/browser_remove_customized_specials.js.1426250-3.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/customizableui/test/browser_remove_customized_specials.js.1426250-3.later
+@@ -0,0 +1,25 @@
++--- browser_remove_customized_specials.js
+++++ browser_remove_customized_specials.js
++@@ -10,19 +10,20 @@ add_task(async function() {
++   await startCustomizing();
++   CustomizableUI.addWidgetToArea("spring", "nav-bar", 5);
++   await gCustomizeMode.reset();
++   let springs = document.querySelectorAll("#nav-bar toolbarspring");
++   let lastSpring = springs[springs.length - 1];
++   let expectedPlacements = CustomizableUI.getWidgetIdsInArea("nav-bar");
++   info("Placements before drag: " + expectedPlacements.join(","));
++   let lastItem = document.getElementById(expectedPlacements[expectedPlacements.length - 1]);
++-  simulateItemDrag(lastSpring, lastItem);
+++  await waitForElementShown(lastItem);
+++  simulateItemDrag(lastSpring, lastItem, "end");
++   expectedPlacements.splice(expectedPlacements.indexOf(lastSpring.id), 1);
++-  expectedPlacements.splice(expectedPlacements.length - 1, 0, lastSpring.id);
+++  expectedPlacements.push(lastSpring.id);
++   let actualPlacements = CustomizableUI.getWidgetIdsInArea("nav-bar");
++   // Log these separately because Assert.deepEqual truncates the stringified versions...
++   info("Actual placements: " + actualPlacements.join(","));
++   info("Expected placements: " + expectedPlacements.join(","));
++   Assert.deepEqual(expectedPlacements, actualPlacements, "Should be able to move spring");
++   await gCustomizeMode.reset();
++   await endCustomizing();
++ });
+diff --git a/browser/components/customizableui/test/head.js b/browser/components/customizableui/test/head.js
+--- a/browser/components/customizableui/test/head.js
++++ b/browser/components/customizableui/test/head.js
+@@ -190,18 +190,32 @@ function todoAssertAreaPlacements(areaId
+   todo(isPassing, "The area placements for " + areaId +
+                   " should equal the expected placements.");
+ }
+ 
+ function getAreaWidgetIds(areaId) {
+   return CustomizableUI.getWidgetIdsInArea(areaId);
+ }
+ 
+-function simulateItemDrag(aToDrag, aTarget) {
+-  synthesizeDrop(aToDrag.parentNode, aTarget);
++function simulateItemDrag(aToDrag, aTarget, aEvent = {}) {
++  let ev = aEvent;
++  if (ev == "end" || ev == "start") {
++    let win = aTarget.ownerGlobal;
++    win.QueryInterface(Ci.nsIInterfaceRequestor);
++    const dwu = win.getInterface(Ci.nsIDOMWindowUtils);
++    let bounds = dwu.getBoundsWithoutFlushing(aTarget);
++    if (ev == "end") {
++      ev = {clientX: bounds.right - 2, clientY: bounds.bottom - 2};
++    } else {
++      ev = {clientX: bounds.left + 2, clientY: bounds.top + 2};
++    }
++  }
++  ev._domDispatchOnly = true;
++  synthesizeDrop(aToDrag.parentNode, aTarget, null, null,
++                 aToDrag.ownerGlobal, aTarget.ownerGlobal, ev);
+ }
+ 
+ function endCustomizing(aWindow = window) {
+   if (aWindow.document.documentElement.getAttribute("customizing") != "true") {
+     return true;
+   }
+   return new Promise(resolve => {
+     function onCustomizationEnds() {
+diff --git a/browser/components/customizableui/test/head.js.1426250-3.later b/browser/components/customizableui/test/head.js.1426250-3.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/customizableui/test/head.js.1426250-3.later
+@@ -0,0 +1,27 @@
++--- head.js
+++++ head.js
++@@ -492,17 +506,21 @@ function checkContextMenu(aContextMenu, 
++       is(menuItemDisabled, !aExpectedEntries[i][1], "disabled state for " + selector);
++     } catch (e) {
++       ok(false, "Exception when checking context menu: " + e);
++     }
++   }
++ }
++ 
++ function waitForOverflowButtonShown(win = window) {
+++  let ov = win.document.getElementById("nav-bar-overflow-button");
+++  let icon = win.document.getAnonymousElementByAttribute(ov, "class", "toolbarbutton-icon");
+++  return waitForElementShown(icon);
+++}
+++function waitForElementShown(element) {
+++  let win = element.ownerGlobal;
++   let dwu = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
++   return BrowserTestUtils.waitForCondition(() => {
++     info("Waiting for overflow button to have non-0 size");
++-    let ov = win.document.getElementById("nav-bar-overflow-button");
++-    let icon = win.document.getAnonymousElementByAttribute(ov, "class", "toolbarbutton-icon");
++-    let bounds = dwu.getBoundsWithoutFlushing(icon);
+++    let bounds = dwu.getBoundsWithoutFlushing(element);
++     return bounds.width > 0 && bounds.height > 0;
++   });
++ }

+ 151 - 0
mozilla-release/patches/1426253-1-59a1.patch

@@ -0,0 +1,151 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513785198 18000
+# Node ID c7fb331fc914d60e9847e1e6bb69468355d0bb93
+# Parent  b8380e17e90aa8c45d3c0cc76429aa1d0a585d8b
+Bug 1426253 P1 Expose nsIDocument GetClientInfo(), GetClientState(), and GetController(). r=asuth
+
+diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
+--- a/dom/base/nsDocument.cpp
++++ b/dom/base/nsDocument.cpp
+@@ -209,29 +209,32 @@
+ 
+ #include "imgILoader.h"
+ #include "imgRequestProxy.h"
+ #include "nsWrapperCacheInlines.h"
+ #include "nsSandboxFlags.h"
+ #include "mozilla/dom/AnimatableBinding.h"
+ #include "mozilla/dom/AnonymousContent.h"
+ #include "mozilla/dom/BindingUtils.h"
++#include "mozilla/dom/ClientInfo.h"
++#include "mozilla/dom/ClientState.h"
+ #include "mozilla/dom/DocumentFragment.h"
+ #include "mozilla/dom/DocumentTimeline.h"
+ #include "mozilla/dom/Event.h"
+ #include "mozilla/dom/HTMLBodyElement.h"
+ #include "mozilla/dom/HTMLInputElement.h"
+ #include "mozilla/dom/ImageTracker.h"
+ #include "mozilla/dom/MediaQueryList.h"
+ #include "mozilla/dom/NodeFilterBinding.h"
+ #include "mozilla/OwningNonNull.h"
+ #include "mozilla/dom/TabChild.h"
+ #include "mozilla/dom/WebComponentsBinding.h"
+ #include "mozilla/dom/CustomElementRegistryBinding.h"
+ #include "mozilla/dom/CustomElementRegistry.h"
++#include "mozilla/dom/ServiceWorkerDescriptor.h"
+ #include "mozilla/dom/TimeoutManager.h"
+ #include "mozilla/ExtensionPolicyService.h"
+ #include "nsFrame.h"
+ #include "nsDOMCaretPosition.h"
+ #include "nsViewportInfo.h"
+ #include "mozilla/StaticPtr.h"
+ #include "nsITextControlElement.h"
+ #include "nsIDOMNSEditableElement.h"
+@@ -5664,16 +5667,46 @@ nsIDocument::GetAnonRootIfInAnonymousCon
+       return child->IsElement() ? child->AsElement() : nullptr;
+     }
+     child = parent;
+     parent = child->GetParentNode();
+   }
+   return nullptr;
+ }
+ 
++Maybe<ClientInfo>
++nsIDocument::GetClientInfo() const
++{
++  nsPIDOMWindowInner* inner = GetInnerWindow();
++  if (inner) {
++    return Move(inner->GetClientInfo());
++  }
++  return Move(Maybe<ClientInfo>());
++}
++
++Maybe<ClientState>
++nsIDocument::GetClientState() const
++{
++  nsPIDOMWindowInner* inner = GetInnerWindow();
++  if (inner) {
++    return Move(inner->GetClientState());
++  }
++  return Move(Maybe<ClientState>());
++}
++
++Maybe<ServiceWorkerDescriptor>
++nsIDocument::GetController() const
++{
++  nsPIDOMWindowInner* inner = GetInnerWindow();
++  if (inner) {
++    return Move(inner->GetController());
++  }
++  return Move(Maybe<ServiceWorkerDescriptor>());
++}
++
+ //
+ // nsIDOMDocument interface
+ //
+ DocumentType*
+ nsIDocument::GetDoctype() const
+ {
+   for (nsIContent* child = GetFirstChild();
+        child;
+diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h
+--- a/dom/base/nsIDocument.h
++++ b/dom/base/nsIDocument.h
+@@ -124,16 +124,18 @@ class ImageLoader;
+ class Rule;
+ } // namespace css
+ 
+ namespace dom {
+ class Animation;
+ class AnonymousContent;
+ class Attr;
+ class BoxObject;
++class ClientInfo;
++class ClientState;
+ class CDATASection;
+ class Comment;
+ struct CustomElementDefinition;
+ class DocGroup;
+ class DocumentFragment;
+ class DocumentTimeline;
+ class DocumentType;
+ class DOMImplementation;
+@@ -157,16 +159,17 @@ class NodeFilter;
+ class NodeIterator;
+ enum class OrientationType : uint8_t;
+ class ProcessingInstruction;
+ class Promise;
+ class ResizeObserver;
+ class ResizeObserverController;
+ class ScriptLoader;
+ class Selection;
++class ServiceWorkerDescriptor;
+ class StyleSheetList;
+ class SVGDocument;
+ class SVGSVGElement;
+ class Touch;
+ class TouchList;
+ class TreeWalker;
+ class XPathEvaluator;
+ class XPathExpression;
+@@ -1084,16 +1087,20 @@ public:
+    */
+   virtual void ScheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) = 0;
+   // Unschedule an element scheduled by ScheduleFrameRequestCallback (e.g. for when it is destroyed)
+   virtual void UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) = 0;
+ 
+   // Resolve all SVG pres attrs scheduled in ScheduleSVGForPresAttrEvaluation
+   virtual void ResolveScheduledSVGPresAttrs() = 0;
+ 
++  mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
++  mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
++  mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
++
+ protected:
+   virtual Element *GetRootElementInternal() const = 0;
+ 
+   void SetPageUnloadingEventTimeStamp()
+   {
+     MOZ_ASSERT(!mPageUnloadingEventTimeStamp);
+     mPageUnloadingEventTimeStamp = mozilla::TimeStamp::NowLoRes();
+   }

+ 155 - 0
mozilla-release/patches/1426253-2-59a1.patch

@@ -0,0 +1,155 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513785198 18000
+# Node ID 5b676a0c9383eaa52ea203500daada8e3051d367
+# Parent  970726601fc228517f31e3c2422ca8b9c36f1d14
+Bug 1426253 P2 Use nsIDocument::GetClientInfo() where possible. r=asuth
+
+diff --git a/dom/clients/manager/ClientOpenWindowUtils.cpp b/dom/clients/manager/ClientOpenWindowUtils.cpp
+--- a/dom/clients/manager/ClientOpenWindowUtils.cpp
++++ b/dom/clients/manager/ClientOpenWindowUtils.cpp
+@@ -75,25 +75,18 @@ public:
+     nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(),
+                                                       mBaseURI, false);
+     if (NS_FAILED(rv)) {
+       mPromise->Resolve(NS_OK, __func__);
+       mPromise = nullptr;
+       return NS_OK;
+     }
+ 
+-    nsPIDOMWindowInner* innerWindow = doc->GetInnerWindow();
+-    if (NS_WARN_IF(!innerWindow)) {
+-      mPromise->Reject(NS_ERROR_FAILURE, __func__);
+-      mPromise = nullptr;
+-      return NS_OK;
+-    }
+-
+-    Maybe<ClientInfo> info = innerWindow->GetClientInfo();
+-    Maybe<ClientState> state = innerWindow->GetClientState();
++    Maybe<ClientInfo> info(doc->GetClientInfo());
++    Maybe<ClientState> state(doc->GetClientState());
+ 
+     if (NS_WARN_IF(info.isNothing() || state.isNothing())) {
+       mPromise->Reject(NS_ERROR_FAILURE, __func__);
+       mPromise = nullptr;
+       return NS_OK;
+     }
+ 
+     mPromise->Resolve(ClientInfoAndState(info.ref().ToIPC(), state.ref().ToIPC()),
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -2366,25 +2366,22 @@ ServiceWorkerManager::StartControllingAD
+   aRegistration->StartControllingADocument();
+   mControlledDocuments.Put(aDoc, aRegistration);
+ 
+   // Mark the document's ClientSource as controlled using the ClientHandle
+   // interface.  While we could get at the ClientSource directly from the
+   // document here, our goal is to move ServiceWorkerManager to a separate
+   // process.  Using the ClientHandle supports this remote operation.
+   ServiceWorkerInfo* activeWorker = aRegistration->GetActive();
+-  nsPIDOMWindowInner* innerWindow = aDoc->GetInnerWindow();
+-  if (activeWorker && innerWindow) {
+-    Maybe<ClientInfo> clientInfo = innerWindow->GetClientInfo();
+-    if (clientInfo.isSome()) {
+-      RefPtr<ClientHandle> clientHandle =
+-        ClientManager::CreateHandle(clientInfo.ref(),
+-                                    SystemGroup::EventTargetFor(TaskCategory::Other));
+-      ref = Move(clientHandle->Control(activeWorker->Descriptor()));
+-    }
++  Maybe<ClientInfo> clientInfo = aDoc->GetClientInfo();
++  if (activeWorker && clientInfo.isSome()) {
++    RefPtr<ClientHandle> clientHandle =
++      ClientManager::CreateHandle(clientInfo.ref(),
++                                  SystemGroup::EventTargetFor(TaskCategory::Other));
++    ref = Move(clientHandle->Control(activeWorker->Descriptor()));
+   }
+ 
+   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
+   return Move(ref);
+ }
+ 
+ void
+ ServiceWorkerManager::StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration)
+@@ -3267,45 +3264,41 @@ ServiceWorkerManager::SetSkipWaitingFlag
+ void
+ ServiceWorkerManager::UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration)
+ {
+   AssertIsOnMainThread();
+ 
+   RefPtr<ServiceWorkerInfo> activeWorker = aRegistration->GetActive();
+   MOZ_DIAGNOSTIC_ASSERT(activeWorker);
+ 
+-  AutoTArray<nsCOMPtr<nsPIDOMWindowInner>, 16> innerWindows;
++  AutoTArray<nsCOMPtr<nsIDocument>, 16> docList;
+   for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
+     if (iter.UserData() != aRegistration) {
+       continue;
+     }
+ 
+     nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
+     if (NS_WARN_IF(!doc)) {
+       continue;
+     }
+ 
+-    nsPIDOMWindowInner* innerWindow = doc->GetInnerWindow();
+-    if (NS_WARN_IF(!innerWindow)) {
+-      continue;
+-    }
+-
+-    innerWindows.AppendElement(innerWindow);
++    docList.AppendElement(doc.forget());
+   }
+ 
+   // Fire event after iterating mControlledDocuments is done to prevent
+   // modification by reentering from the event handlers during iteration.
+-  for (auto& innerWindow : innerWindows) {
+-    Maybe<ClientInfo> clientInfo = innerWindow->GetClientInfo();
+-    if (clientInfo.isSome()) {
+-      RefPtr<ClientHandle> clientHandle =
+-        ClientManager::CreateHandle(clientInfo.ref(),
+-                                    innerWindow->EventTargetFor(TaskCategory::Other));
+-      clientHandle->Control(activeWorker->Descriptor());
++  for (auto& doc : docList) {
++    Maybe<ClientInfo> clientInfo = doc->GetClientInfo();
++    if (clientInfo.isNothing()) {
++      continue;
+     }
++    RefPtr<ClientHandle> clientHandle =
++      ClientManager::CreateHandle(clientInfo.ref(),
++                                  SystemGroup::EventTargetFor(TaskCategory::Other));
++    clientHandle->Control(activeWorker->Descriptor());
+   }
+ }
+ 
+ already_AddRefed<ServiceWorkerRegistrationInfo>
+ ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal,
+                                       const nsACString& aScope) const
+ {
+   MOZ_ASSERT(aPrincipal);
+diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp
+--- a/netwerk/base/LoadInfo.cpp
++++ b/netwerk/base/LoadInfo.cpp
+@@ -107,21 +107,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
+       (mSecurityFlags & nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL);
+     mSecurityFlags &= ~nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
+   }
+ 
+   if (aLoadingContext) {
+     // Ensure that all network requests for a window client have the ClientInfo
+     // properly set.
+     // TODO: The ClientInfo is not set properly for worker initiated requests yet.
+-    nsCOMPtr<nsPIDOMWindowInner> contextInner =
+-      aLoadingContext->OwnerDoc()->GetInnerWindow();
+-    if (contextInner) {
+-      mClientInfo = contextInner->GetClientInfo();
+-    }
++    mClientInfo = aLoadingContext->OwnerDoc()->GetClientInfo();
+ 
+     nsCOMPtr<nsPIDOMWindowOuter> contextOuter = aLoadingContext->OwnerDoc()->GetWindow();
+     if (contextOuter) {
+       ComputeIsThirdPartyContext(contextOuter);
+       mOuterWindowID = contextOuter->WindowID();
+       nsCOMPtr<nsPIDOMWindowOuter> parent = contextOuter->GetScriptableParent();
+       mParentOuterWindowID = parent ? parent->WindowID() : mOuterWindowID;
+       mTopOuterWindowID = FindTopOuterWindowID(contextOuter);

+ 254 - 0
mozilla-release/patches/1426253-3-59a1.patch

@@ -0,0 +1,254 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513785198 18000
+# Node ID 83c725dad7a9cefe2a9bc82b1886141ad536ca36
+# Parent  17b9bd634bd49b53aeb1da19760e53c6b477b821
+Bug 1426253 P3 Use the window/document GetController() method. r=asuth
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -15100,17 +15100,17 @@ nsDocShell::ShouldPrepareForIntercept(ns
+ 
+   if (!aIsNonSubresourceRequest) {
+     nsCOMPtr<nsIDocument> doc = GetDocument();
+     if (!doc) {
+       return NS_ERROR_NOT_AVAILABLE;
+     }
+ 
+     ErrorResult rv;
+-    *aShouldIntercept = swm->IsControlled(doc, rv);
++    *aShouldIntercept = doc->GetController().isSome();
+     if (NS_WARN_IF(rv.Failed())) {
+       return rv.StealNSResult();
+     }
+ 
+     return NS_OK;
+   }
+ 
+   // If the user has set a cookie policy that restricts cookies, then
+diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp
+--- a/dom/base/nsContentSink.cpp
++++ b/dom/base/nsContentSink.cpp
+@@ -43,16 +43,17 @@
+ #include "nsWidgetsCID.h"
+ #include "nsIDOMNode.h"
+ #include "mozAutoDocUpdate.h"
+ #include "nsIWebNavigation.h"
+ #include "nsGenericHTMLElement.h"
+ #include "nsHTMLDNSPrefetch.h"
+ #include "nsIObserverService.h"
+ #include "mozilla/Preferences.h"
++#include "mozilla/dom/ServiceWorkerDescriptor.h"
+ #include "mozilla/dom/ScriptLoader.h"
+ #include "nsParserConstants.h"
+ #include "nsSandboxFlags.h"
+ #include "Link.h"
+ 
+ using namespace mozilla;
+ using namespace mozilla::dom;
+ 
+@@ -1084,17 +1085,17 @@ nsContentSink::ProcessOfflineManifest(co
+   // Don't bother processing offline manifest for documents
+   // without a docshell
+   if (!mDocShell) {
+     return;
+   }
+ 
+   // If this document has been interecepted, let's skip the processing of the
+   // manifest.
+-  if (nsContentUtils::IsControlledByServiceWorker(mDocument)) {
++  if (mDocument->GetController().isSome()) {
+     return;
+   }
+ 
+   // If the docshell's in private browsing mode, we don't want to do any
+   // manifest processing.
+   nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(mDocShell);
+   if (loadContext->UsePrivateBrowsing()) {
+     return;
+diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
+--- a/dom/base/nsContentUtils.cpp
++++ b/dom/base/nsContentUtils.cpp
+@@ -2044,46 +2044,24 @@ nsContentUtils::ParseLegacyFontSize(cons
+       value = 3 + value;
+     }
+   }
+ 
+   return clamped(value, 1, 7);
+ }
+ 
+ /* static */
+-bool
+-nsContentUtils::IsControlledByServiceWorker(nsIDocument* aDocument)
+-{
+-  if (nsContentUtils::IsInPrivateBrowsing(aDocument)) {
+-    return false;
+-  }
+-
+-  RefPtr<workers::ServiceWorkerManager> swm =
+-    workers::ServiceWorkerManager::GetInstance();
+-  MOZ_ASSERT(swm);
+-
+-  ErrorResult rv;
+-  bool controlled = swm->IsControlled(aDocument, rv);
+-  if (NS_WARN_IF(rv.Failed())) {
+-    rv.SuppressException();
+-    return false;
+-  }
+-
+-  return controlled;
+-}
+-
+-/* static */
+ void
+ nsContentUtils::GetOfflineAppManifest(nsIDocument *aDocument, nsIURI **aURI)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   MOZ_ASSERT(aDocument);
+   *aURI = nullptr;
+ 
+-  if (IsControlledByServiceWorker(aDocument)) {
++  if (aDocument->GetController().isSome()) {
+     return;
+   }
+ 
+   Element* docElement = aDocument->GetRootElement();
+   if (!docElement) {
+     return;
+   }
+ 
+diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
+--- a/dom/base/nsContentUtils.h
++++ b/dom/base/nsContentUtils.h
+@@ -2431,21 +2431,16 @@ public:
+    * Returns true if the <style scoped> enabling pref is true.
+    */
+   static bool IsScopedStylePrefEnabled()
+   {
+     return sIsScopedStyleEnabled;
+   }
+ 
+   /**
+-   * Return true if this doc is controlled by a ServiceWorker.
+-   */
+-  static bool IsControlledByServiceWorker(nsIDocument* aDocument);
+-
+-  /**
+    * Fire mutation events for changes caused by parsing directly into a
+    * context node.
+    *
+    * @param aDoc the document of the node
+    * @param aDest the destination node that got stuff appended to it
+    * @param aOldChildCount the number of children the node had before parsing
+    */
+   static void FireMutationEventsForDirectParsing(nsIDocument* aDoc,
+diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
+--- a/dom/base/nsDocument.cpp
++++ b/dom/base/nsDocument.cpp
+@@ -4902,17 +4902,17 @@ nsDocument::SetScriptGlobalObject(nsIScr
+         loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
+       }
+     }
+ 
+     using mozilla::dom::workers::ServiceWorkerManager;
+     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+     if (swm) {
+       ErrorResult error;
+-      if (swm->IsControlled(this, error)) {
++      if (GetController().isSome()) {
+         imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this);
+         if (loader) {
+           loader->ClearCacheForControlledDocument(this);
+         }
+ 
+         // We may become controlled again if this document comes back out
+         // of bfcache.  Clear our state to allow that to happen.  Only
+         // clear this flag if we are actually controlled, though, so pages
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -2778,38 +2778,16 @@ ServiceWorkerManager::IsAvailable(nsIPri
+   MOZ_ASSERT(aPrincipal);
+   MOZ_ASSERT(aURI);
+ 
+   RefPtr<ServiceWorkerRegistrationInfo> registration =
+     GetServiceWorkerRegistrationInfo(aPrincipal, aURI);
+   return registration && registration->GetActive();
+ }
+ 
+-bool
+-ServiceWorkerManager::IsControlled(nsIDocument* aDoc, ErrorResult& aRv)
+-{
+-  MOZ_ASSERT(aDoc);
+-
+-  if (nsContentUtils::IsInPrivateBrowsing(aDoc)) {
+-    // Handle the case where a service worker was previously registered in
+-    // a non-private window (bug 1255621).
+-    return false;
+-  }
+-
+-  RefPtr<ServiceWorkerRegistrationInfo> registration;
+-  nsresult rv = GetDocumentRegistration(aDoc, getter_AddRefs(registration));
+-  if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE)) {
+-    // It's OK to ignore the case where we don't have a registration.
+-    aRv.Throw(rv);
+-    return false;
+-  }
+-
+-  return !!registration;
+-}
+-
+ nsresult
+ ServiceWorkerManager::GetDocumentRegistration(nsIDocument* aDoc,
+                                               ServiceWorkerRegistrationInfo** aRegistrationInfo)
+ {
+   RefPtr<ServiceWorkerRegistrationInfo> registration;
+   if (!mControlledDocuments.Get(aDoc, getter_AddRefs(registration))) {
+     return NS_ERROR_NOT_AVAILABLE;
+   }
+diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
+--- a/dom/workers/ServiceWorkerManager.h
++++ b/dom/workers/ServiceWorkerManager.h
+@@ -120,19 +120,16 @@ public:
+   //       are guaranteed the callback will fire before and remove the ref
+   //       from this list before the channel is destroyed.
+   typedef nsTArray<nsIInterceptedChannel*> InterceptionList;
+   nsClassHashtable<nsCStringHashKey, InterceptionList> mNavigationInterceptions;
+ 
+   bool
+   IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI);
+ 
+-  bool
+-  IsControlled(nsIDocument* aDocument, ErrorResult& aRv);
+-
+   // Return true if the given content process could potentially be executing
+   // service worker code with the given principal.  At the current time, this
+   // just means that we have any registration for the origin, regardless of
+   // scope.  This is a very weak guarantee but is the best we can do when push
+   // notifications can currently spin up a service worker in content processes
+   // without our involvement in the parent process.
+   //
+   // In the future when there is only a single ServiceWorkerManager in the
+diff --git a/image/ImageCacheKey.cpp b/image/ImageCacheKey.cpp
+--- a/image/ImageCacheKey.cpp
++++ b/image/ImageCacheKey.cpp
+@@ -163,17 +163,17 @@ ImageCacheKey::GetControlledDocumentToke
+   // For non-controlled documents, we just return null.  For controlled
+   // documents, we cast the pointer into a void* to avoid dereferencing
+   // it (since we only use it for comparisons), and return it.
+   void* pointer = nullptr;
+   using dom::workers::ServiceWorkerManager;
+   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+   if (aDocument && swm) {
+     ErrorResult rv;
+-    if (swm->IsControlled(aDocument, rv)) {
++    if (aDocument->GetController().isSome()) {
+       pointer = aDocument;
+     }
+   }
+   return pointer;
+ }
+ 
+ } // namespace image
+ } // namespace mozilla

+ 113 - 0
mozilla-release/patches/1426253-4-59a1.patch

@@ -0,0 +1,113 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1513785199 18000
+# Node ID 661c1ca56b09907f19299040eb7baa6209728997
+# Parent  55791f68a19027ab685b1dbb0a6902f29a37f129
+Bug 1426253 P4 Assert that ClientSource::SetController() is never called on a client in private browsing mode. r=asuth
+
+diff --git a/dom/clients/manager/ClientInfo.cpp b/dom/clients/manager/ClientInfo.cpp
+--- a/dom/clients/manager/ClientInfo.cpp
++++ b/dom/clients/manager/ClientInfo.cpp
+@@ -6,16 +6,18 @@
+ 
+ #include "ClientInfo.h"
+ 
+ #include "mozilla/dom/ClientIPCTypes.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
++using mozilla::ipc::PrincipalInfo;
++
+ ClientInfo::ClientInfo(const nsID& aId,
+                        ClientType aType,
+                        const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
+                        const TimeStamp& aCreationTime)
+   : mData(MakeUnique<IPCClientInfo>(aId, aType, aPrincipalInfo, aCreationTime,
+                                     EmptyCString(),
+                                     mozilla::dom::FrameType::None))
+ {
+@@ -105,10 +107,36 @@ ClientInfo::SetFrameType(mozilla::dom::F
+ }
+ 
+ const IPCClientInfo&
+ ClientInfo::ToIPC() const
+ {
+   return *mData;
+ }
+ 
++bool
++ClientInfo::IsPrivateBrowsing() const
++{
++  switch(PrincipalInfo().type()) {
++    case PrincipalInfo::TContentPrincipalInfo:
++    {
++      auto& p = PrincipalInfo().get_ContentPrincipalInfo();
++      return p.attrs().mPrivateBrowsingId != 0;
++    }
++    case PrincipalInfo::TSystemPrincipalInfo:
++    {
++      return false;
++    }
++    case PrincipalInfo::TNullPrincipalInfo:
++    {
++      auto& p = PrincipalInfo().get_NullPrincipalInfo();
++      return p.attrs().mPrivateBrowsingId != 0;
++    }
++    default:
++    {
++      // clients should never be expanded principals
++      MOZ_CRASH("unexpected principal type!");
++    }
++  }
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientInfo.h b/dom/clients/manager/ClientInfo.h
+--- a/dom/clients/manager/ClientInfo.h
++++ b/dom/clients/manager/ClientInfo.h
+@@ -86,14 +86,18 @@ public:
+   // Set the frame type for the global.  This should only happen once the
+   // global has become execution ready.
+   void
+   SetFrameType(mozilla::dom::FrameType aFrameType);
+ 
+   // Convert to the ipdl generated type.
+   const IPCClientInfo&
+   ToIPC() const;
++
++  // Determine if the client is in private browsing mode.
++  bool
++  IsPrivateBrowsing() const;
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif // _mozilla_dom_ClientInfo_h
+diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp
+--- a/dom/clients/manager/ClientSource.cpp
++++ b/dom/clients/manager/ClientSource.cpp
+@@ -350,16 +350,21 @@ ClientSource::WorkerSyncPing(WorkerPriva
+   GetActor()->SendWorkerSyncPing();
+ }
+ 
+ void
+ ClientSource::SetController(const ServiceWorkerDescriptor& aServiceWorker)
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientSource);
+ 
++  // A client in private browsing mode should never be controlled by
++  // a service worker.  The principal origin attributes should guarantee
++  // this invariant.
++  MOZ_DIAGNOSTIC_ASSERT(!mClientInfo.IsPrivateBrowsing());
++
+   if (mController.isSome() && mController.ref() == aServiceWorker) {
+     return;
+   }
+ 
+   mController.reset();
+   mController.emplace(aServiceWorker);
+ 
+   RefPtr<ServiceWorkerContainer> swc;

+ 56 - 0
mozilla-release/patches/1428650-59a1.patch

@@ -0,0 +1,56 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515525898 18000
+# Node ID 82d983031ec77367bd48ba80d948272b06f85aea
+# Parent  8a427b714496e25bac99331ff89b49f180b2b3a0
+Bug 1428650 Make ServiceWorkerManager::MaybeClaimClient() handle inconsistent child-process SWM state. r=asuth
+
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -3227,18 +3227,18 @@ ServiceWorkerManager::UpdateInternal(nsI
+ 
+   queue->ScheduleJob(job);
+ }
+ 
+ already_AddRefed<GenericPromise>
+ ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,
+                                        ServiceWorkerRegistrationInfo* aWorkerRegistration)
+ {
+-  MOZ_ASSERT(aWorkerRegistration);
+-  MOZ_ASSERT(aWorkerRegistration->GetActive());
++  MOZ_DIAGNOSTIC_ASSERT(aWorkerRegistration);
++  MOZ_DIAGNOSTIC_ASSERT(aWorkerRegistration->GetActive());
+ 
+   RefPtr<GenericPromise> ref;
+ 
+   // Same origin check
+   if (!aWorkerRegistration->mPrincipal->Equals(aDocument->NodePrincipal())) {
+     ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_SECURITY_ERR, __func__);
+     return ref.forget();
+   }
+@@ -3280,17 +3280,23 @@ ServiceWorkerManager::MaybeClaimClient(n
+     PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
+   if (!principal) {
+     ref = GenericPromise::CreateAndResolve(false, __func__);
+     return ref.forget();
+   }
+ 
+   RefPtr<ServiceWorkerRegistrationInfo> registration =
+     GetRegistration(principal, aServiceWorker.Scope());
+-  if (!registration) {
++
++  // While ServiceWorkerManager is distributed across child processes its
++  // possible for us to sometimes get a claim for a new worker that has
++  // not propagated to this process yet.  For now, simply note that we
++  // are done.  The fix for this is to move the SWM to the parent process
++  // so there are no consistency errors.
++  if (NS_WARN_IF(!registration) || NS_WARN_IF(!registration->GetActive())) {
+     ref = GenericPromise::CreateAndResolve(false, __func__);
+     return ref.forget();
+   }
+ 
+   ref = MaybeClaimClient(aDoc, registration);
+   return ref.forget();
+ }
+ 

+ 45 - 0
mozilla-release/patches/1428652-1-59a1.patch

@@ -0,0 +1,45 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515527704 18000
+# Node ID 0d553d8df00975df74d7deb793cbbaf74129fff3
+# Parent  8a427b714496e25bac99331ff89b49f180b2b3a0
+Bug 1428652 P1 Make service worker registration removal take container principals into account. r=asuth
+
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -2341,30 +2341,32 @@ ServiceWorkerManager::RemoveScopeAndRegi
+   if (auto entry = data->mUpdateTimers.Lookup(aRegistration->mScope)) {
+     entry.Data()->Cancel();
+     entry.Remove();
+   }
+ 
+   // Verify there are no controlled clients for the purged registration.
+   for (auto iter = swm->mControlledClients.Iter(); !iter.Done(); iter.Next()) {
+     auto& reg = iter.UserData()->mRegistrationInfo;
+-    if (reg->mScope.Equals(aRegistration->mScope)) {
++    if (reg->mScope.Equals(aRegistration->mScope) &&
++        reg->mPrincipal->Equals(aRegistration->mPrincipal)) {
+       MOZ_DIAGNOSTIC_ASSERT(false,
+                             "controlled client when removing registration");
+       iter.Remove();
+       break;
+     }
+   }
+ 
+   // Registration lifecycle is managed via mControlledClients now.  Do not
+   // assert on on mControlledDocuments as races may cause this to still be
+   // set when the registration is destroyed.
+   for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
+     ServiceWorkerRegistrationInfo* reg = iter.UserData();
+-    if (reg->mScope.Equals(aRegistration->mScope)) {
++    if (reg->mScope.Equals(aRegistration->mScope) &&
++        reg->mPrincipal->Equals(aRegistration->mPrincipal)) {
+       iter.Remove();
+       break;
+     }
+   }
+ 
+   RefPtr<ServiceWorkerRegistrationInfo> info;
+   data->mInfos.Remove(aRegistration->mScope, getter_AddRefs(info));
+   data->mOrderedScopes.RemoveElement(aRegistration->mScope);

+ 165 - 0
mozilla-release/patches/1428652-2-59a1.patch

@@ -0,0 +1,165 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515527705 18000
+# Node ID 56c7e33c6c64e189c01c2abeea9b1a36a9bdb745
+# Parent  2b02405f3c10c6c8ca3d20ff6eb9f52bd3291d15
+Bug 1428652 P2 Verify that unregistering a service worker in one container does not break a SW with same scope in different container. r=asuth
+
+diff --git a/dom/workers/test/serviceworkers/browser.ini b/dom/workers/test/serviceworkers/browser.ini
+--- a/dom/workers/test/serviceworkers/browser.ini
++++ b/dom/workers/test/serviceworkers/browser.ini
+@@ -12,10 +12,11 @@ support-files =
+   force_refresh_browser_worker.js
+   empty.html
+   utils.js
+ 
+ [browser_devtools_serviceworker_interception.js]
+ [browser_force_refresh.js]
+ [browser_download.js]
+ [browser_download_canceled.js]
++[browser_unregister_with_containers.js]
+ [browser_userContextId_openWindow.js]
+ skip-if = !e10s
+diff --git a/dom/workers/test/serviceworkers/browser_unregister_with_containers.js b/dom/workers/test/serviceworkers/browser_unregister_with_containers.js
+new file mode 100644
+--- /dev/null
++++ b/dom/workers/test/serviceworkers/browser_unregister_with_containers.js
+@@ -0,0 +1,138 @@
++"use strict";
++
++const { interfaces: Ci } = Components;
++
++const BASE_URI = "http://mochi.test:8888/browser/dom/workers/test/serviceworkers/";
++const PAGE_URI = BASE_URI + "empty.html";
++const SCOPE = PAGE_URI + "?unregister_with_containers";
++const SW_SCRIPT = BASE_URI + "empty.js";
++
++function doRegister(browser) {
++  return ContentTask.spawn(browser, { script: SW_SCRIPT, scope: SCOPE },
++    async function(opts) {
++      let reg = await content.navigator.serviceWorker.register(opts.script,
++                                                               { scope: opts.scope });
++      let worker = reg.installing || reg.waiting || reg.active;
++      await new Promise(resolve => {
++        if (worker.state === "activated") {
++          resolve();
++          return;
++        }
++        worker.addEventListener("statechange", function onStateChange() {
++          if (worker.state === "activated") {
++            worker.removeEventListener("statechange", onStateChange);
++            resolve();
++          }
++        });
++      });
++    }
++  );
++}
++
++function doUnregister(browser) {
++  return ContentTask.spawn(browser, SCOPE, async function(uri) {
++    let reg = await content.navigator.serviceWorker.getRegistration(uri);
++    let worker = reg.active;
++    await reg.unregister();
++    await new Promise(resolve => {
++      if (worker.state === "redundant") {
++        resolve();
++        return;
++      }
++      worker.addEventListener("statechange", function onStateChange() {
++        if (worker.state === "redundant") {
++          worker.removeEventListener("statechange", onStateChange);
++          resolve();
++        }
++      });
++    });
++  });
++}
++
++function isControlled(browser) {
++  return ContentTask.spawn(browser, null, function() {
++    return !!content.navigator.serviceWorker.controller;
++  });
++}
++
++async function checkControlled(browser) {
++  let controlled = await isControlled(browser);
++  ok(controlled, "window should be controlled");
++}
++
++async function checkUncontrolled(browser) {
++  let controlled = await isControlled(browser);
++  ok(!controller, "window should not be controlled");
++}
++
++add_task(async function test() {
++  await SpecialPowers.pushPrefEnv({"set": [
++    // Avoid service worker propagation races by disabling multi-e10s for now.
++    // This can be removed after the e10s refactor is complete.
++    ["dom.ipc.processCount", 1],
++    ["dom.serviceWorkers.enabled", true],
++    ["dom.serviceWorkers.testing.enabled", true],
++  ]});
++
++  // Setup service workers in two different contexts with the same scope.
++  let containerTab1 = BrowserTestUtils.addTab(gBrowser, PAGE_URI, { userContextId: 1 });
++  let containerBrowser1 = gBrowser.getBrowserForTab(containerTab1);
++  await BrowserTestUtils.browserLoaded(containerBrowser1);
++
++  let containerTab2 = BrowserTestUtils.addTab(gBrowser, PAGE_URI, { userContextId: 2 });
++  let containerBrowser2 = gBrowser.getBrowserForTab(containerTab2);
++  await BrowserTestUtils.browserLoaded(containerBrowser2);
++
++  await doRegister(containerBrowser1);
++  await doRegister(containerBrowser2);
++
++  await checkUncontrolled(containerBrowser1);
++  await checkUncontrolled(containerBrowser2);
++
++  // Close the tabs we used to register the service workers.  These are not
++  // controlled.
++  await BrowserTestUtils.removeTab(containerTab1);
++  await BrowserTestUtils.removeTab(containerTab2);
++
++  // Open a controlled tab in each container.
++  containerTab1 = BrowserTestUtils.addTab(gBrowser, SCOPE, { userContextId: 1 });
++  containerBrowser1 = gBrowser.getBrowserForTab(containerTab1);
++  await BrowserTestUtils.browserLoaded(containerBrowser1);
++
++  containerTab2 = BrowserTestUtils.addTab(gBrowser, SCOPE, { userContextId: 2 });
++  containerBrowser2 = gBrowser.getBrowserForTab(containerTab2);
++  await BrowserTestUtils.browserLoaded(containerBrowser2);
++
++  await checkControlled(containerBrowser1);
++  await checkControlled(containerBrowser2);
++
++  // Remove the first container's controlled tab
++  await BrowserTestUtils.removeTab(containerTab1);
++
++  // Create a new uncontrolled tab for the first container and use it to
++  // unregister the service worker.
++  containerTab1 = BrowserTestUtils.addTab(gBrowser, PAGE_URI, { userContextId: 1 });
++  containerBrowser1 = gBrowser.getBrowserForTab(containerTab1);
++  await BrowserTestUtils.browserLoaded(containerBrowser1);
++  await doUnregister(containerBrowser1);
++
++  await checkUncontrolled(containerBrowser1);
++  await checkControlled(containerBrowser2);
++
++  // Remove the second container's controlled tab
++  await BrowserTestUtils.removeTab(containerTab2);
++
++  // Create a new uncontrolled tab for the second container and use it to
++  // unregister the service worker.
++  containerTab2 = BrowserTestUtils.addTab(gBrowser, PAGE_URI, { userContextId: 2 });
++  containerBrowser2 = gBrowser.getBrowserForTab(containerTab2);
++  await BrowserTestUtils.browserLoaded(containerBrowser2);
++  await doUnregister(containerBrowser2);
++
++  await checkUncontrolled(containerBrowser1);
++  await checkUncontrolled(containerBrowser2);
++
++  // Close the two tabs we used to unregister the service worker.
++  await BrowserTestUtils.removeTab(containerTab1);
++  await BrowserTestUtils.removeTab(containerTab2);
++});

+ 29 - 0
mozilla-release/patches/1428652-3-59a1.patch

@@ -0,0 +1,29 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1515530374 18000
+# Node ID 48c2c76a78739a40c5c94c3c15d643a37e256deb
+# Parent  cdd40209806e5b59eb55a23d32e28eba97a7e6f7
+Bug 1428652 P3 Fix typo in browser_unregister_with_containers.js. r=me
+
+diff --git a/dom/workers/test/serviceworkers/browser_unregister_with_containers.js b/dom/workers/test/serviceworkers/browser_unregister_with_containers.js
+--- a/dom/workers/test/serviceworkers/browser_unregister_with_containers.js
++++ b/dom/workers/test/serviceworkers/browser_unregister_with_containers.js
+@@ -57,17 +57,17 @@ function isControlled(browser) {
+ 
+ async function checkControlled(browser) {
+   let controlled = await isControlled(browser);
+   ok(controlled, "window should be controlled");
+ }
+ 
+ async function checkUncontrolled(browser) {
+   let controlled = await isControlled(browser);
+-  ok(!controller, "window should not be controlled");
++  ok(!controlled, "window should not be controlled");
+ }
+ 
+ add_task(async function test() {
+   await SpecialPowers.pushPrefEnv({"set": [
+     // Avoid service worker propagation races by disabling multi-e10s for now.
+     // This can be removed after the e10s refactor is complete.
+     ["dom.ipc.processCount", 1],
+     ["dom.serviceWorkers.enabled", true],

+ 48 - 0
mozilla-release/patches/1428725-59a1.patch

@@ -0,0 +1,48 @@
+# HG changeset patch
+# User Andrea Marchesini <amarchesini@mozilla.com>
+# Date 1515583853 -3600
+# Node ID 11c801abda3c68b438668e2d5f267f5c6371e584
+# Parent  2e6cbd43ab8b11f13d3f2daff1cc0ab1f29c617c
+Bug 1428725 - Fix a crash in ConsoleData when StructuredCloneHolder fails to write data, r=smaug
+
+diff --git a/dom/console/Console.cpp b/dom/console/Console.cpp
+--- a/dom/console/Console.cpp
++++ b/dom/console/Console.cpp
+@@ -516,16 +516,19 @@ public:
+   ConsoleCallDataRunnable(Console* aConsole,
+                           ConsoleCallData* aCallData)
+     : ConsoleRunnable(aConsole)
+     , mCallData(aCallData)
+   {
+     MOZ_ASSERT(aCallData);
+     mWorkerPrivate->AssertIsOnWorkerThread();
+     mCallData->AssertIsOnOwningThread();
++
++    // Marking this CallData as in use.
++    mCallData->mStatus = ConsoleCallData::eInUse;
+   }
+ 
+ private:
+   ~ConsoleCallDataRunnable()
+   {
+     MOZ_ASSERT(!mCallData);
+   }
+ 
+@@ -553,17 +556,16 @@ private:
+     }
+ 
+     JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
+ 
+     if (NS_WARN_IF(!Write(aCx, value))) {
+       return false;
+     }
+ 
+-    mCallData->mStatus = ConsoleCallData::eInUse;
+     return true;
+   }
+ 
+   void
+   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
+              nsPIDOMWindowInner* aInnerWindow) override
+   {
+     AssertIsOnMainThread();

+ 598 - 0
mozilla-release/patches/1429174-1-59a1.patch

@@ -0,0 +1,598 @@
+# HG changeset patch
+# User Andrea Marchesini <amarchesini@mozilla.com>
+# Date 1515618397 -3600
+# Node ID 8c42fc215b1be8456a1c4bb0a1491d9c2bee0ff4
+# Parent  071728187e73cd7fa8caa28b7b08af22210ce38e
+Bug 1429174 - Introducing ConsoleUtils for logging messages to console, r=bkelly
+
+diff --git a/dom/console/Console.cpp b/dom/console/Console.cpp
+--- a/dom/console/Console.cpp
++++ b/dom/console/Console.cpp
+@@ -2,16 +2,17 @@
+ /* vim: set ts=8 sts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ #include "mozilla/dom/Console.h"
+ #include "mozilla/dom/ConsoleInstance.h"
+ #include "mozilla/dom/ConsoleBinding.h"
++#include "ConsoleCommon.h"
+ 
+ #include "mozilla/dom/BlobBinding.h"
+ #include "mozilla/dom/Exceptions.h"
+ #include "mozilla/dom/File.h"
+ #include "mozilla/dom/FunctionBinding.h"
+ #include "mozilla/dom/Performance.h"
+ #include "mozilla/dom/ScriptSettings.h"
+ #include "mozilla/dom/StructuredCloneHolder.h"
+@@ -291,34 +292,16 @@ public:
+ private:
+   ~ConsoleCallData()
+   {
+     AssertIsOnOwningThread();
+     MOZ_ASSERT(mStatus != eInUse);
+   }
+ };
+ 
+-// This class is used to clear any exception at the end of this method.
+-class ClearException
+-{
+-public:
+-  explicit ClearException(JSContext* aCx)
+-    : mCx(aCx)
+-  {
+-  }
+-
+-  ~ClearException()
+-  {
+-    JS_ClearPendingException(mCx);
+-  }
+-
+-private:
+-  JSContext* mCx;
+-};
+-
+ class ConsoleRunnable : public WorkerProxyToMainThreadRunnable
+                       , public StructuredCloneHolderBase
+ {
+ public:
+   explicit ConsoleRunnable(Console* aConsole)
+     : WorkerProxyToMainThreadRunnable(GetCurrentThreadWorkerPrivate())
+     , mConsole(aConsole)
+   {}
+@@ -533,17 +516,17 @@ private:
+   }
+ 
+   bool
+   PreDispatch(JSContext* aCx) override
+   {
+     mWorkerPrivate->AssertIsOnWorkerThread();
+     mCallData->AssertIsOnOwningThread();
+ 
+-    ClearException ce(aCx);
++    ConsoleCommon::ClearException ce(aCx);
+ 
+     JS::Rooted<JSObject*> arguments(aCx,
+       JS_NewArrayObject(aCx, mCallData->mCopiedArguments.Length()));
+     if (NS_WARN_IF(!arguments)) {
+       return false;
+     }
+ 
+     JS::Rooted<JS::Value> arg(aCx);
+@@ -620,17 +603,17 @@ private:
+     mCallData = nullptr;
+   }
+ 
+   void
+   ProcessCallData(JSContext* aCx)
+   {
+     AssertIsOnMainThread();
+ 
+-    ClearException ce(aCx);
++    ConsoleCommon::ClearException ce(aCx);
+ 
+     JS::Rooted<JS::Value> argumentsValue(aCx);
+     if (!Read(aCx, &argumentsValue)) {
+       return;
+     }
+ 
+     MOZ_ASSERT(argumentsValue.isObject());
+ 
+@@ -678,17 +661,17 @@ public:
+   {
+     MOZ_ASSERT(aConsole);
+   }
+ 
+ private:
+   bool
+   PreDispatch(JSContext* aCx) override
+   {
+-    ClearException ce(aCx);
++    ConsoleCommon::ClearException ce(aCx);
+ 
+     JS::Rooted<JSObject*> arguments(aCx,
+       JS_NewArrayObject(aCx, mArguments.Length()));
+     if (NS_WARN_IF(!arguments)) {
+       return false;
+     }
+ 
+     JS::Rooted<JS::Value> arg(aCx);
+@@ -710,17 +693,17 @@ private:
+   }
+ 
+   void
+   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
+              nsPIDOMWindowInner* aInnerWindow) override
+   {
+     AssertIsOnMainThread();
+ 
+-    ClearException ce(aCx);
++    ConsoleCommon::ClearException ce(aCx);
+ 
+     // Now we could have the correct window (if we are not window-less).
+     mClonedData.mParent = aInnerWindow;
+ 
+     JS::Rooted<JS::Value> argumentsValue(aCx);
+     bool ok = Read(aCx, &argumentsValue);
+     mClonedData.mParent = nullptr;
+ 
+@@ -1012,17 +995,17 @@ Console::StringMethod(const GlobalObject
+                                 aMethodString);
+ }
+ 
+ void
+ Console::StringMethodInternal(JSContext* aCx, const nsAString& aLabel,
+                               MethodName aMethodName,
+                               const nsAString& aMethodString)
+ {
+-  ClearException ce(aCx);
++  ConsoleCommon::ClearException ce(aCx);
+ 
+   Sequence<JS::Value> data;
+   SequenceRooter<JS::Value> rooter(aCx, &data);
+ 
+   JS::Rooted<JS::Value> value(aCx);
+   if (!dom::ToJSValue(aCx, aLabel, &value)) {
+     return;
+   }
+@@ -1035,17 +1018,17 @@ Console::StringMethodInternal(JSContext*
+ }
+ 
+ /* static */ void
+ Console::TimeStamp(const GlobalObject& aGlobal,
+                    const JS::Handle<JS::Value> aData)
+ {
+   JSContext* cx = aGlobal.Context();
+ 
+-  ClearException ce(cx);
++  ConsoleCommon::ClearException ce(cx);
+ 
+   Sequence<JS::Value> data;
+   SequenceRooter<JS::Value> rooter(cx, &data);
+ 
+   if (aData.isString() && !data.AppendElement(aData, fallible)) {
+     return;
+   }
+ 
+@@ -1111,17 +1094,17 @@ Console::ProfileMethodInternal(JSContext
+     // Here we are in a worker thread.
+     RefPtr<ConsoleProfileRunnable> runnable =
+       new ConsoleProfileRunnable(this, aMethodName, aAction, aData);
+ 
+     runnable->Dispatch(aCx);
+     return;
+   }
+ 
+-  ClearException ce(aCx);
++  ConsoleCommon::ClearException ce(aCx);
+ 
+   RootedDictionary<ConsoleProfileEvent> event(aCx);
+   event.mAction = aAction;
+ 
+   event.mArguments.Construct();
+   Sequence<JS::Value>& sequence = event.mArguments.Value();
+ 
+   for (uint32_t i = 0; i < aData.Length(); ++i) {
+@@ -1244,17 +1227,17 @@ Console::MethodInternal(JSContext* aCx, 
+   if (!ShouldProceed(aMethodName)) {
+     return;
+   }
+ 
+   AssertIsOnOwningThread();
+ 
+   RefPtr<ConsoleCallData> callData(new ConsoleCallData());
+ 
+-  ClearException ce(aCx);
++  ConsoleCommon::ClearException ce(aCx);
+ 
+   if (NS_WARN_IF(!callData->Initialize(aCx, aMethodName, aMethodString,
+                                        aData, this))) {
+     return;
+   }
+ 
+   OriginAttributes oa;
+ 
+@@ -1499,17 +1482,17 @@ Console::PopulateConsoleNotificationInTh
+ 
+   JS::Rooted<JSObject*> targetScope(aCx, aTargetScope);
+ 
+   ConsoleStackEntry frame;
+   if (aData->mTopStackFrame) {
+     frame = *aData->mTopStackFrame;
+   }
+ 
+-  ClearException ce(aCx);
++  ConsoleCommon::ClearException ce(aCx);
+   RootedDictionary<ConsoleEvent> event(aCx);
+ 
+   event.mAddonId = aData->mAddonId;
+ 
+   event.mID.Construct();
+   event.mInnerID.Construct();
+ 
+   if (aData->mIDType == ConsoleCallData::eString) {
+@@ -2157,17 +2140,17 @@ Console::ArgumentsToValueList(const Sequ
+ }
+ 
+ uint32_t
+ Console::IncreaseCounter(JSContext* aCx, const Sequence<JS::Value>& aArguments,
+                          nsAString& aCountLabel)
+ {
+   AssertIsOnOwningThread();
+ 
+-  ClearException ce(aCx);
++  ConsoleCommon::ClearException ce(aCx);
+ 
+   MOZ_ASSERT(!aArguments.IsEmpty());
+ 
+   JS::Rooted<JS::Value> labelValue(aCx, aArguments[0]);
+   JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, labelValue));
+   if (!jsString) {
+     return 0; // We cannot continue.
+   }
+@@ -2193,17 +2176,17 @@ Console::IncreaseCounter(JSContext* aCx,
+   }
+   return entry.Data();
+ }
+ 
+ JS::Value
+ Console::CreateCounterValue(JSContext* aCx, const nsAString& aCountLabel,
+                             uint32_t aCountValue) const
+ {
+-  ClearException ce(aCx);
++  ConsoleCommon::ClearException ce(aCx);
+ 
+   if (aCountValue == MAX_PAGE_COUNTERS) {
+     RootedDictionary<ConsoleCounterError> error(aCx);
+ 
+     JS::Rooted<JS::Value> value(aCx);
+     if (!ToJSValue(aCx, error, &value)) {
+       return JS::UndefinedValue();
+     }
+diff --git a/dom/console/ConsoleCommon.h b/dom/console/ConsoleCommon.h
+new file mode 100644
+--- /dev/null
++++ b/dom/console/ConsoleCommon.h
+@@ -0,0 +1,38 @@
++/* -*- 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 mozilla_dom_ConsoleCommon_h
++#define mozilla_dom_ConsoleCommon_h
++
++#include "nsString.h"
++
++namespace mozilla {
++namespace dom {
++namespace ConsoleCommon {
++
++// This class is used to clear any exception at the end of this method.
++class MOZ_RAII ClearException
++{
++public:
++  explicit ClearException(JSContext* aCx)
++    : mCx(aCx)
++  {
++  }
++
++  ~ClearException()
++  {
++    JS_ClearPendingException(mCx);
++  }
++
++private:
++  JSContext* mCx;
++};
++
++} // namespace ConsoleCommon
++} // namespace dom
++} // namespace mozilla
++
++#endif /* mozilla_dom_ConsoleCommon_h */
+diff --git a/dom/console/ConsoleInstance.cpp b/dom/console/ConsoleInstance.cpp
+--- a/dom/console/ConsoleInstance.cpp
++++ b/dom/console/ConsoleInstance.cpp
+@@ -1,16 +1,17 @@
+ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+ /* vim: set ts=8 sts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ #include "mozilla/dom/ConsoleInstance.h"
+ #include "mozilla/dom/ConsoleBinding.h"
++#include "ConsoleCommon.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ConsoleInstance, mConsole)
+ 
+ NS_IMPL_CYCLE_COLLECTING_ADDREF(ConsoleInstance)
+ NS_IMPL_CYCLE_COLLECTING_RELEASE(ConsoleInstance)
+@@ -127,17 +128,17 @@ ConsoleInstance::TimeEnd(JSContext* aCx,
+ {
+   mConsole->StringMethodInternal(aCx, aLabel, Console::MethodTimeEnd,
+                                  NS_LITERAL_STRING("timeEnd"));
+ }
+ 
+ void
+ ConsoleInstance::TimeStamp(JSContext* aCx, const JS::Handle<JS::Value> aData)
+ {
+-  ClearException ce(aCx);
++  ConsoleCommon::ClearException ce(aCx);
+ 
+   Sequence<JS::Value> data;
+   SequenceRooter<JS::Value> rooter(aCx, &data);
+ 
+   if (aData.isString() && !data.AppendElement(aData, fallible)) {
+     return;
+   }
+ 
+diff --git a/dom/console/ConsoleUtils.cpp b/dom/console/ConsoleUtils.cpp
+new file mode 100644
+--- /dev/null
++++ b/dom/console/ConsoleUtils.cpp
+@@ -0,0 +1,152 @@
++/* -*- 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 "ConsoleUtils.h"
++#include "ConsoleCommon.h"
++
++#include "mozilla/ClearOnShutdown.h"
++#include "NullPrincipal.h"
++
++namespace mozilla {
++namespace dom {
++
++namespace {
++
++StaticRefPtr<ConsoleUtils> gConsoleUtilsService;
++
++}
++
++/* static */ ConsoleUtils*
++ConsoleUtils::GetOrCreate()
++{
++  if (!gConsoleUtilsService) {
++    MOZ_ASSERT(NS_IsMainThread());
++
++    gConsoleUtilsService = new ConsoleUtils();
++    ClearOnShutdown(&gConsoleUtilsService);
++  }
++
++  return gConsoleUtilsService;
++}
++
++ConsoleUtils::ConsoleUtils() = default;
++ConsoleUtils::~ConsoleUtils() = default;
++
++/* static */ void
++ConsoleUtils::ReportForServiceWorkerScope(const nsAString& aScope,
++                                          const nsAString& aMessage,
++                                          const nsAString& aFilename,
++                                          uint32_t aLineNumber,
++                                          uint32_t aColumnNumber)
++{
++  MOZ_ASSERT(NS_IsMainThread());
++
++  RefPtr<ConsoleUtils> service = ConsoleUtils::GetOrCreate();
++  if (NS_WARN_IF(!service)) {
++    return;
++  }
++
++  service->ReportForServiceWorkerScopeInternal(aScope, aMessage, aFilename,
++                                               aLineNumber, aColumnNumber);
++}
++
++void
++ConsoleUtils::ReportForServiceWorkerScopeInternal(const nsAString& aScope,
++                                                  const nsAString& aMessage,
++                                                  const nsAString& aFilename,
++                                                  uint32_t aLineNumber,
++                                                  uint32_t aColumnNumber)
++{
++  MOZ_ASSERT(NS_IsMainThread());
++
++  AutoJSAPI jsapi;
++  jsapi.Init();
++
++  JSContext* cx = jsapi.cx();
++
++  ConsoleCommon::ClearException ce(cx);
++  JS::Rooted<JSObject*> global(cx, GetOrCreateSandbox(cx));
++  if (NS_WARN_IF(!global)) {
++    return;
++  }
++
++  // The GetOrCreateSandbox call returns a proxy to the actual sandbox object.
++  // We don't need a proxy here.
++  global = js::UncheckedUnwrap(global);
++
++  JSAutoCompartment ac(cx, global);
++
++  RootedDictionary<ConsoleEvent> event(cx);
++
++  event.mID.Construct();
++  event.mID.Value().SetAsString() = aScope;
++
++  event.mInnerID.Construct();
++  event.mInnerID.Value().SetAsString() = NS_LITERAL_STRING("ServiceWorker");
++
++  event.mLevel = NS_LITERAL_STRING("log");
++  event.mFilename = aFilename;
++  event.mLineNumber = aLineNumber;
++  event.mColumnNumber = aColumnNumber;
++  event.mTimeStamp = JS_Now() / PR_USEC_PER_MSEC;
++
++  JS::Rooted<JS::Value> messageValue(cx);
++  if (!dom::ToJSValue(cx, aMessage, &messageValue)) {
++    return;
++  }
++
++  event.mArguments.Construct();
++  if (!event.mArguments.Value().AppendElement(messageValue, fallible)) {
++    return;
++  }
++
++  nsCOMPtr<nsIConsoleAPIStorage> storage =
++    do_GetService("@mozilla.org/consoleAPI-storage;1");
++
++  if (NS_WARN_IF(!storage)) {
++    return;
++  }
++
++  JS::Rooted<JS::Value> eventValue(cx);
++  if (!ToJSValue(cx, event, &eventValue)) {
++    return;
++  }
++
++  // This is a legacy property.
++  JS::Rooted<JSObject*> eventObj(cx, &eventValue.toObject());
++  if (NS_WARN_IF(!JS_DefineProperty(cx, eventObj, "wrappedJSObject", eventObj,
++                                    JSPROP_ENUMERATE))) {
++    return;
++  }
++
++  storage->RecordEvent(NS_LITERAL_STRING("ServiceWorker"), aScope, eventValue);
++}
++
++JSObject*
++ConsoleUtils::GetOrCreateSandbox(JSContext* aCx)
++{
++  AssertIsOnMainThread();
++
++  if (!mSandbox) {
++    nsIXPConnect* xpc = nsContentUtils::XPConnect();
++    MOZ_ASSERT(xpc, "This should never be null!");
++
++    RefPtr<NullPrincipal> nullPrincipal = NullPrincipal::Create();
++
++    JS::Rooted<JSObject*> sandbox(aCx);
++    nsresult rv = xpc->CreateSandbox(aCx, nullPrincipal, sandbox.address());
++    if (NS_WARN_IF(NS_FAILED(rv))) {
++      return nullptr;
++    }
++
++    mSandbox = new JSObjectHolder(aCx, sandbox);
++  }
++
++  return mSandbox->GetJSObject();
++}
++
++} // namespace dom
++} // namespace mozilla
+diff --git a/dom/console/ConsoleUtils.h b/dom/console/ConsoleUtils.h
+new file mode 100644
+--- /dev/null
++++ b/dom/console/ConsoleUtils.h
+@@ -0,0 +1,53 @@
++/* -*- 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 mozilla_dom_ConsoleUtils_h
++#define mozilla_dom_ConsoleUtils_h
++
++#include "mozilla/JSObjectHolder.h"
++#include "nsISupportsImpl.h"
++#include "nsString.h"
++
++namespace mozilla {
++namespace dom {
++
++class ConsoleUtils final
++{
++public:
++  NS_INLINE_DECL_REFCOUNTING(ConsoleUtils)
++
++  // Main-thread only, reports a console message from a ServiceWorker.
++  static void
++  ReportForServiceWorkerScope(const nsAString& aScope,
++                              const nsAString& aMessage,
++                              const nsAString& aFilename,
++                              uint32_t aLineNumber,
++                              uint32_t aColumnNumber);
++
++private:
++  ConsoleUtils();
++  ~ConsoleUtils();
++
++  static ConsoleUtils*
++  GetOrCreate();
++
++  JSObject*
++  GetOrCreateSandbox(JSContext* aCx);
++
++  void
++  ReportForServiceWorkerScopeInternal(const nsAString& aScope,
++                                      const nsAString& aMessage,
++                                      const nsAString& aFilename,
++                                      uint32_t aLineNumber,
++                                      uint32_t aColumnNumber);
++
++  RefPtr<JSObjectHolder> mSandbox;
++};
++
++} // namespace dom
++} // namespace mozilla
++
++#endif /* mozilla_dom_ConsoleUtils_h */
+diff --git a/dom/console/moz.build b/dom/console/moz.build
+--- a/dom/console/moz.build
++++ b/dom/console/moz.build
+@@ -19,22 +19,24 @@ EXPORTS += [
+ 
+ EXPORTS.mozilla += [
+     'ConsoleReportCollector.h',
+ ]
+ 
+ EXPORTS.mozilla.dom += [
+     'Console.h',
+     'ConsoleInstance.h',
++    'ConsoleUtils.h',
+ ]
+ 
+ UNIFIED_SOURCES += [
+     'Console.cpp',
+     'ConsoleInstance.cpp',
+     'ConsoleReportCollector.cpp',
++    'ConsoleUtils.cpp',
+ ]
+ 
+ EXTRA_COMPONENTS += [
+     'ConsoleAPI.manifest',
+     'ConsoleAPIStorage.js',
+ ]
+ 
+ LOCAL_INCLUDES += [

+ 147 - 0
mozilla-release/patches/1429174-2-59a1.patch

@@ -0,0 +1,147 @@
+# HG changeset patch
+# User Andrea Marchesini <amarchesini@mozilla.com>
+# Date 1515618397 -3600
+# Node ID 75a5873327a61af0923c83720a1f677d832e2b9c
+# Parent  69ebae08cd9008aec593f6e136b43ae0e9940eb5
+Bug 1429174 - Introducing ConsoleUtils for logging messages to console - tests, r=bkelly
+
+diff --git a/dom/console/ConsoleInstance.cpp b/dom/console/ConsoleInstance.cpp
+--- a/dom/console/ConsoleInstance.cpp
++++ b/dom/console/ConsoleInstance.cpp
+@@ -2,16 +2,17 @@
+ /* vim: set ts=8 sts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ #include "mozilla/dom/ConsoleInstance.h"
+ #include "mozilla/dom/ConsoleBinding.h"
+ #include "ConsoleCommon.h"
++#include "ConsoleUtils.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ConsoleInstance, mConsole)
+ 
+ NS_IMPL_CYCLE_COLLECTING_ADDREF(ConsoleInstance)
+ NS_IMPL_CYCLE_COLLECTING_RELEASE(ConsoleInstance)
+@@ -180,10 +181,23 @@ ConsoleInstance::Count(JSContext* aCx, c
+ void
+ ConsoleInstance::Clear(JSContext* aCx)
+ {
+   const Sequence<JS::Value> data;
+   mConsole->MethodInternal(aCx, Console::MethodClear,
+                            NS_LITERAL_STRING("clear"), data);
+ }
+ 
++void
++ConsoleInstance::ReportForServiceWorkerScope(const nsAString& aScope,
++                                             const nsAString& aMessage,
++                                             const nsAString& aFilename,
++                                             uint32_t aLineNumber,
++                                             uint32_t aColumnNumber)
++{
++  if (NS_IsMainThread()) {
++    ConsoleUtils::ReportForServiceWorkerScope(aScope, aMessage, aFilename,
++                                              aLineNumber, aColumnNumber);
++  }
++}
++
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/console/ConsoleInstance.h b/dom/console/ConsoleInstance.h
+--- a/dom/console/ConsoleInstance.h
++++ b/dom/console/ConsoleInstance.h
+@@ -89,16 +89,23 @@ public:
+   Assert(JSContext* aCx, bool aCondition, const Sequence<JS::Value>& aData);
+ 
+   void
+   Count(JSContext* aCx, const nsAString& aLabel);
+ 
+   void
+   Clear(JSContext* aCx);
+ 
++  // For testing only.
++  void ReportForServiceWorkerScope(const nsAString& aScope,
++                                   const nsAString& aMessage,
++                                   const nsAString& aFilename,
++                                   uint32_t aLineNumber,
++                                   uint32_t aColumnNumber);
++
+ private:
+   ~ConsoleInstance();
+ 
+   RefPtr<Console> mConsole;
+ };
+ 
+ } // dom namespace
+ } // mozilla namespace
+diff --git a/dom/console/tests/xpcshell/test_reportForServiceWorkerScope.js b/dom/console/tests/xpcshell/test_reportForServiceWorkerScope.js
+new file mode 100644
+--- /dev/null
++++ b/dom/console/tests/xpcshell/test_reportForServiceWorkerScope.js
+@@ -0,0 +1,33 @@
++/* Any copyright is dedicated to the Public Domain.
++   http://creativecommons.org/publicdomain/zero/1.0/ */
++
++Components.utils.import("resource://gre/modules/Services.jsm");
++
++add_task(async function() {
++  let p = new Promise(resolve => {
++    function consoleListener() {
++      Services.obs.addObserver(this, "console-api-log-event");
++    }
++
++    consoleListener.prototype  = {
++      observe: function(aSubject, aTopic, aData) {
++        let obj = aSubject.wrappedJSObject;
++        Assert.ok(obj.arguments[0] === "Hello world!", "Message received!");
++        Assert.ok(obj.ID === "scope", "The ID is the scope");
++        Assert.ok(obj.innerID === "ServiceWorker", "The innerID is ServiceWorker");
++        Assert.ok(obj.filename === "filename", "The filename matches");
++        Assert.ok(obj.lineNumber === 42, "The lineNumber matches");
++        Assert.ok(obj.columnNumber === 24, "The columnNumber matches");
++
++        Services.obs.removeObserver(this, "console-api-log-event");
++        resolve();
++      }
++    };
++
++    new consoleListener();
++  });
++
++  let ci = console.createInstance();
++  ci.reportForServiceWorkerScope("scope", "Hello world!", "filename", 42, 24);
++  await p;
++});
+diff --git a/dom/console/tests/xpcshell/xpcshell.ini b/dom/console/tests/xpcshell/xpcshell.ini
+--- a/dom/console/tests/xpcshell/xpcshell.ini
++++ b/dom/console/tests/xpcshell/xpcshell.ini
+@@ -1,5 +1,6 @@
+ [DEFAULT]
+ head =
+ support-files =
+ 
+ [test_basic.js]
++[test_reportForServiceWorkerScope.js]
+diff --git a/dom/webidl/Console.webidl b/dom/webidl/Console.webidl
+--- a/dom/webidl/Console.webidl
++++ b/dom/webidl/Console.webidl
+@@ -181,8 +181,16 @@ dictionary ConsoleInstanceOptions {
+   ConsoleLogLevel maxLogLevel;
+ 
+   // String pref name which contains the level to use for maxLogLevel. If the
+   // pref doesn't exist, gets removed or it is used in workers, the maxLogLevel
+   // will default to the value passed to this constructor (or "all" if it wasn't
+   // specified).
+   DOMString maxLogLevelPref = "";
+ };
++
++// this interface is just for testing
++partial interface ConsoleInstance {
++  [ChromeOnly]
++  void reportForServiceWorkerScope(DOMString scope, DOMString message,
++                                   DOMString filename, unsigned long lineNumber,
++                                   unsigned long columnNumber);
++};

+ 275 - 0
mozilla-release/patches/1429174-3-59a1.patch

@@ -0,0 +1,275 @@
+# HG changeset patch
+# User Andrea Marchesini <amarchesini@mozilla.com>
+# Date 1515618398 -3600
+# Node ID 6e74b98b535425ea96ecbf65a391e9b6f3f51bc4
+# Parent  a17e571e9e1548ff34429c895a217b1e886156f4
+Bug 1429174 - Introducing ConsoleUtils for logging messages to console - message level, r=bkelly
+
+diff --git a/dom/console/ConsoleInstance.cpp b/dom/console/ConsoleInstance.cpp
+--- a/dom/console/ConsoleInstance.cpp
++++ b/dom/console/ConsoleInstance.cpp
+@@ -42,16 +42,33 @@ PrefToValue(const nsCString& aPref)
+   if (NS_WARN_IF(index < 0)) {
+     return ConsoleLogLevel::All;
+   }
+ 
+   MOZ_ASSERT(index < (int)ConsoleLogLevel::EndGuard_);
+   return static_cast<ConsoleLogLevel>(index);
+ }
+ 
++ConsoleUtils::Level
++WebIDLevelToConsoleUtilsLevel(ConsoleLevel aLevel)
++{
++  switch (aLevel) {
++    case ConsoleLevel::Log:
++      return ConsoleUtils::eLog;
++    case ConsoleLevel::Warning:
++      return ConsoleUtils::eWarning;
++    case ConsoleLevel::Error:
++      return ConsoleUtils::eError;
++    default:
++      break;
++  }
++
++  return ConsoleUtils::eLog;
++}
++
+ } // anonymous
+ 
+ ConsoleInstance::ConsoleInstance(const ConsoleInstanceOptions& aOptions)
+   : mConsole(new Console(nullptr))
+ {
+   mConsole->mConsoleID = aOptions.mConsoleID;
+   mConsole->mPassedInnerID = aOptions.mInnerID;
+ 
+@@ -186,18 +203,22 @@ ConsoleInstance::Clear(JSContext* aCx)
+                            NS_LITERAL_STRING("clear"), data);
+ }
+ 
+ void
+ ConsoleInstance::ReportForServiceWorkerScope(const nsAString& aScope,
+                                              const nsAString& aMessage,
+                                              const nsAString& aFilename,
+                                              uint32_t aLineNumber,
+-                                             uint32_t aColumnNumber)
++                                             uint32_t aColumnNumber,
++                                             ConsoleLevel aLevel)
+ {
+-  if (NS_IsMainThread()) {
+-    ConsoleUtils::ReportForServiceWorkerScope(aScope, aMessage, aFilename,
+-                                              aLineNumber, aColumnNumber);
++  if (!NS_IsMainThread()) {
++    return;
+   }
++
++  ConsoleUtils::ReportForServiceWorkerScope(aScope, aMessage, aFilename,
++                                            aLineNumber, aColumnNumber,
++                                            WebIDLevelToConsoleUtilsLevel(aLevel));
+ }
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/console/ConsoleInstance.h b/dom/console/ConsoleInstance.h
+--- a/dom/console/ConsoleInstance.h
++++ b/dom/console/ConsoleInstance.h
+@@ -94,17 +94,18 @@ public:
+   void
+   Clear(JSContext* aCx);
+ 
+   // For testing only.
+   void ReportForServiceWorkerScope(const nsAString& aScope,
+                                    const nsAString& aMessage,
+                                    const nsAString& aFilename,
+                                    uint32_t aLineNumber,
+-                                   uint32_t aColumnNumber);
++                                   uint32_t aColumnNumber,
++                                   ConsoleLevel aLevel);
+ 
+ private:
+   ~ConsoleInstance();
+ 
+   RefPtr<Console> mConsole;
+ };
+ 
+ } // dom namespace
+diff --git a/dom/console/ConsoleUtils.cpp b/dom/console/ConsoleUtils.cpp
+--- a/dom/console/ConsoleUtils.cpp
++++ b/dom/console/ConsoleUtils.cpp
+@@ -35,35 +35,38 @@ ConsoleUtils::GetOrCreate()
+ ConsoleUtils::ConsoleUtils() = default;
+ ConsoleUtils::~ConsoleUtils() = default;
+ 
+ /* static */ void
+ ConsoleUtils::ReportForServiceWorkerScope(const nsAString& aScope,
+                                           const nsAString& aMessage,
+                                           const nsAString& aFilename,
+                                           uint32_t aLineNumber,
+-                                          uint32_t aColumnNumber)
++                                          uint32_t aColumnNumber,
++                                          Level aLevel)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+ 
+   RefPtr<ConsoleUtils> service = ConsoleUtils::GetOrCreate();
+   if (NS_WARN_IF(!service)) {
+     return;
+   }
+ 
+   service->ReportForServiceWorkerScopeInternal(aScope, aMessage, aFilename,
+-                                               aLineNumber, aColumnNumber);
++                                               aLineNumber, aColumnNumber,
++                                               aLevel);
+ }
+ 
+ void
+ ConsoleUtils::ReportForServiceWorkerScopeInternal(const nsAString& aScope,
+                                                   const nsAString& aMessage,
+                                                   const nsAString& aFilename,
+                                                   uint32_t aLineNumber,
+-                                                  uint32_t aColumnNumber)
++                                                  uint32_t aColumnNumber,
++                                                  Level aLevel)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+ 
+   AutoJSAPI jsapi;
+   jsapi.Init();
+ 
+   JSContext* cx = jsapi.cx();
+ 
+@@ -82,17 +85,30 @@ ConsoleUtils::ReportForServiceWorkerScop
+   RootedDictionary<ConsoleEvent> event(cx);
+ 
+   event.mID.Construct();
+   event.mID.Value().SetAsString() = aScope;
+ 
+   event.mInnerID.Construct();
+   event.mInnerID.Value().SetAsString() = NS_LITERAL_STRING("ServiceWorker");
+ 
+-  event.mLevel = NS_LITERAL_STRING("log");
++  switch (aLevel) {
++    case eLog:
++      event.mLevel = NS_LITERAL_STRING("log");
++      break;
++
++    case eWarning:
++      event.mLevel = NS_LITERAL_STRING("warn");
++      break;
++
++    case eError:
++      event.mLevel = NS_LITERAL_STRING("error");
++      break;
++  }
++
+   event.mFilename = aFilename;
+   event.mLineNumber = aLineNumber;
+   event.mColumnNumber = aColumnNumber;
+   event.mTimeStamp = JS_Now() / PR_USEC_PER_MSEC;
+ 
+   JS::Rooted<JS::Value> messageValue(cx);
+   if (!dom::ToJSValue(cx, aMessage, &messageValue)) {
+     return;
+diff --git a/dom/console/ConsoleUtils.h b/dom/console/ConsoleUtils.h
+--- a/dom/console/ConsoleUtils.h
++++ b/dom/console/ConsoleUtils.h
+@@ -14,40 +14,48 @@
+ namespace mozilla {
+ namespace dom {
+ 
+ class ConsoleUtils final
+ {
+ public:
+   NS_INLINE_DECL_REFCOUNTING(ConsoleUtils)
+ 
++  enum Level {
++    eLog,
++    eWarning,
++    eError,
++  };
++
+   // Main-thread only, reports a console message from a ServiceWorker.
+   static void
+   ReportForServiceWorkerScope(const nsAString& aScope,
+                               const nsAString& aMessage,
+                               const nsAString& aFilename,
+                               uint32_t aLineNumber,
+-                              uint32_t aColumnNumber);
++                              uint32_t aColumnNumber,
++                              Level aLevel);
+ 
+ private:
+   ConsoleUtils();
+   ~ConsoleUtils();
+ 
+   static ConsoleUtils*
+   GetOrCreate();
+ 
+   JSObject*
+   GetOrCreateSandbox(JSContext* aCx);
+ 
+   void
+   ReportForServiceWorkerScopeInternal(const nsAString& aScope,
+                                       const nsAString& aMessage,
+                                       const nsAString& aFilename,
+                                       uint32_t aLineNumber,
+-                                      uint32_t aColumnNumber);
++                                      uint32_t aColumnNumber,
++                                      Level aLevel);
+ 
+   RefPtr<JSObjectHolder> mSandbox;
+ };
+ 
+ } // namespace dom
+ } // namespace mozilla
+ 
+ #endif /* mozilla_dom_ConsoleUtils_h */
+diff --git a/dom/console/tests/xpcshell/test_reportForServiceWorkerScope.js b/dom/console/tests/xpcshell/test_reportForServiceWorkerScope.js
+--- a/dom/console/tests/xpcshell/test_reportForServiceWorkerScope.js
++++ b/dom/console/tests/xpcshell/test_reportForServiceWorkerScope.js
+@@ -13,21 +13,22 @@ add_task(async function() {
+       observe: function(aSubject, aTopic, aData) {
+         let obj = aSubject.wrappedJSObject;
+         Assert.ok(obj.arguments[0] === "Hello world!", "Message received!");
+         Assert.ok(obj.ID === "scope", "The ID is the scope");
+         Assert.ok(obj.innerID === "ServiceWorker", "The innerID is ServiceWorker");
+         Assert.ok(obj.filename === "filename", "The filename matches");
+         Assert.ok(obj.lineNumber === 42, "The lineNumber matches");
+         Assert.ok(obj.columnNumber === 24, "The columnNumber matches");
++        Assert.ok(obj.level === "error", "The level is correct");
+ 
+         Services.obs.removeObserver(this, "console-api-log-event");
+         resolve();
+       }
+     };
+ 
+     new consoleListener();
+   });
+ 
+   let ci = console.createInstance();
+-  ci.reportForServiceWorkerScope("scope", "Hello world!", "filename", 42, 24);
++  ci.reportForServiceWorkerScope("scope", "Hello world!", "filename", 42, 24, "error");
+   await p;
+ });
+diff --git a/dom/webidl/Console.webidl b/dom/webidl/Console.webidl
+--- a/dom/webidl/Console.webidl
++++ b/dom/webidl/Console.webidl
+@@ -182,15 +182,18 @@ dictionary ConsoleInstanceOptions {
+ 
+   // String pref name which contains the level to use for maxLogLevel. If the
+   // pref doesn't exist, gets removed or it is used in workers, the maxLogLevel
+   // will default to the value passed to this constructor (or "all" if it wasn't
+   // specified).
+   DOMString maxLogLevelPref = "";
+ };
+ 
++enum ConsoleLevel { "log", "warning", "error" };
++
+ // this interface is just for testing
+ partial interface ConsoleInstance {
+   [ChromeOnly]
+   void reportForServiceWorkerScope(DOMString scope, DOMString message,
+                                    DOMString filename, unsigned long lineNumber,
+-                                   unsigned long columnNumber);
++                                   unsigned long columnNumber,
++                                   ConsoleLevel level);
+ };

+ 1038 - 0
mozilla-release/patches/1431105-59a1.patch

@@ -0,0 +1,1038 @@
+# HG changeset patch
+# User Andrea Marchesini <amarchesini@mozilla.com>
+# Date 1516209576 28800
+# Node ID 6405716d757df0ee49f6beec764086422529b6a2
+# Parent  e891ed4471d9b29c3b3b86780a5e473612af3187
+Bug 1431105 - Prefix in Console when used by JSM, r=bgrins, r=smaug
+
+diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
+--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
++++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
+@@ -1502,16 +1502,17 @@ stubPackets.set("console.log('foobar', '
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924471,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -1528,16 +1529,17 @@ stubPackets.set("console.log(undefined)"
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924479,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -1552,16 +1554,17 @@ stubPackets.set("console.warn('danger, w
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "warn",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924487,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -1578,16 +1581,17 @@ stubPackets.set("console.log(NaN)", {
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924495,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -1604,16 +1608,17 @@ stubPackets.set("console.log(null)", {
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924501,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -1628,16 +1633,17 @@ stubPackets.set("console.log('鼬')", {
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924506,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -1650,16 +1656,17 @@ stubPackets.set("console.clear()", {
+     "arguments": [],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "clear",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924512,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -1677,16 +1684,17 @@ stubPackets.set("console.count('bar')", 
+       "count": 1,
+       "label": "bar"
+     },
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "count",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924515,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -1724,16 +1732,17 @@ stubPackets.set("console.assert(false, {
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "assert",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924521,
+     "timer": null,
+     "stacktrace": [
+       {
+         "columnNumber": 27,
+         "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+@@ -1756,16 +1765,17 @@ stubPackets.set("console.log('hello \nfr
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924528,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -1780,16 +1790,17 @@ stubPackets.set("console.log('úṇĩçödê țĕșť')", {
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924586,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -1816,16 +1827,17 @@ stubPackets.set("console.dirxml(window)"
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "dirxml",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924596,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -1858,16 +1870,17 @@ stubPackets.set("console.log('myarray', 
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924604,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -1892,16 +1905,17 @@ stubPackets.set("console.log('myregex', 
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924610,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -1933,16 +1947,17 @@ stubPackets.set("console.table(['red', '
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "table",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924612,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -1993,16 +2008,17 @@ stubPackets.set("console.log('myobject',
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924614,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -2017,16 +2033,17 @@ stubPackets.set("console.debug('debug me
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "debug",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924621,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -2041,16 +2058,17 @@ stubPackets.set("console.info('info mess
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "info",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924625,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -2065,16 +2083,17 @@ stubPackets.set("console.error('error me
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "error",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924628,
+     "timer": null,
+     "stacktrace": [
+       {
+         "columnNumber": 27,
+         "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+@@ -2120,16 +2139,17 @@ stubPackets.set("console.log('mymap')", 
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 5,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924631,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -2161,16 +2181,17 @@ stubPackets.set("console.log('myset')", 
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 2,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924746,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -2183,16 +2204,17 @@ stubPackets.set("console.trace()", {
+     "arguments": [],
+     "columnNumber": 3,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "testStacktraceFiltering",
+     "groupName": "",
+     "level": "trace",
+     "lineNumber": 3,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924752,
+     "timer": null,
+     "stacktrace": [
+       {
+         "columnNumber": 3,
+         "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+         "functionName": "testStacktraceFiltering",
+@@ -2227,16 +2249,17 @@ stubPackets.set("console.time('bar')", {
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "time",
+     "lineNumber": 2,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924757,
+     "timer": {
+       "name": "bar"
+     },
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+@@ -2253,16 +2276,17 @@ stubPackets.set("timerAlreadyExists", {
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "time",
+     "lineNumber": 3,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924758,
+     "timer": {
+       "error": "timerAlreadyExists",
+       "name": "bar"
+     },
+     "workerType": "none",
+     "styles": [],
+@@ -2280,16 +2304,17 @@ stubPackets.set("console.timeEnd('bar')"
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "timeEnd",
+     "lineNumber": 4,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924759,
+     "timer": {
+       "duration": 1.2149999999999181,
+       "name": "bar"
+     },
+     "workerType": "none",
+     "styles": [],
+@@ -2307,16 +2332,17 @@ stubPackets.set("timerDoesntExist", {
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "timeEnd",
+     "lineNumber": 5,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924759,
+     "timer": {
+       "error": "timerDoesntExist",
+       "name": "bar"
+     },
+     "workerType": "none",
+     "styles": [],
+@@ -2334,16 +2360,17 @@ stubPackets.set("console.table('bar')", 
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "table",
+     "lineNumber": 2,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924801,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2375,16 +2402,17 @@ stubPackets.set("console.table(['a', 'b'
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "table",
+     "lineNumber": 2,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924859,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2399,16 +2427,17 @@ stubPackets.set("console.group('bar')", 
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "bar",
+     "level": "group",
+     "lineNumber": 2,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924863,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -2421,16 +2450,17 @@ stubPackets.set("console.groupEnd('bar')
+     "arguments": [],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "bar",
+     "level": "groupEnd",
+     "lineNumber": 3,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924864,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2445,16 +2475,17 @@ stubPackets.set("console.groupCollapsed(
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "foo",
+     "level": "groupCollapsed",
+     "lineNumber": 2,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924870,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -2467,16 +2498,17 @@ stubPackets.set("console.groupEnd('foo')
+     "arguments": [],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "foo",
+     "level": "groupEnd",
+     "lineNumber": 3,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924871,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2489,16 +2521,17 @@ stubPackets.set("console.group()", {
+     "arguments": [],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "group",
+     "lineNumber": 2,
++    "prefix": "",
+     "private": false,
+     "styles": [],
+     "timeStamp": 1502884924878,
+     "timer": null,
+     "workerType": "none",
+     "category": "webdev"
+   }
+ });
+@@ -2511,16 +2544,17 @@ stubPackets.set("console.groupEnd()", {
+     "arguments": [],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "groupEnd",
+     "lineNumber": 3,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924879,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2536,16 +2570,17 @@ stubPackets.set("console.log(%cfoobar)",
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "log",
+     "lineNumber": 2,
++    "prefix": "",
+     "private": false,
+     "styles": [
+       "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
+       "color:red;background:url('http://example.com/test')"
+     ],
+     "timeStamp": 1502884924883,
+     "timer": null,
+     "workerType": "none",
+@@ -2564,16 +2599,17 @@ stubPackets.set("console.group(%cfoo%cba
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "foo bar",
+     "level": "group",
+     "lineNumber": 2,
++    "prefix": "",
+     "private": false,
+     "styles": [
+       "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
+       "color:red;background:url('http://example.com/test')"
+     ],
+     "timeStamp": 1502884924887,
+     "timer": null,
+     "workerType": "none",
+@@ -2589,16 +2625,17 @@ stubPackets.set("console.groupEnd(%cfoo%
+     "arguments": [],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "foo bar",
+     "level": "groupEnd",
+     "lineNumber": 6,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924887,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2614,16 +2651,17 @@ stubPackets.set("console.groupCollapsed(
+     ],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "foo baz",
+     "level": "groupCollapsed",
+     "lineNumber": 2,
++    "prefix": "",
+     "private": false,
+     "styles": [
+       "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
+       "color:red;background:url('http://example.com/test')"
+     ],
+     "timeStamp": 1502884924892,
+     "timer": null,
+     "workerType": "none",
+@@ -2639,16 +2677,17 @@ stubPackets.set("console.groupEnd(%cfoo%
+     "arguments": [],
+     "columnNumber": 1,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "foo baz",
+     "level": "groupEnd",
+     "lineNumber": 6,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924893,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2704,16 +2743,17 @@ stubPackets.set("console.dir({C, M, Y, K
+     ],
+     "columnNumber": 27,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "dir",
+     "lineNumber": 1,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1502884924899,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2731,16 +2771,17 @@ stubPackets.set("console.count | default
+       "count": 1,
+       "label": "default"
+     },
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "count",
+     "lineNumber": 2,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1511365913333,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2758,16 +2799,17 @@ stubPackets.set("console.count | default
+       "count": 2,
+       "label": "default"
+     },
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "count",
+     "lineNumber": 3,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1511365913334,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2785,16 +2827,17 @@ stubPackets.set("console.count | test co
+       "count": 1,
+       "label": "test counter"
+     },
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "count",
+     "lineNumber": 4,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1511365913334,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2812,16 +2855,17 @@ stubPackets.set("console.count | test co
+       "count": 2,
+       "label": "test counter"
+     },
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "count",
+     "lineNumber": 5,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1511365913334,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2839,16 +2883,17 @@ stubPackets.set("console.count | default
+       "count": 3,
+       "label": "default"
+     },
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "count",
+     "lineNumber": 6,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1511365913334,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2861,16 +2906,17 @@ stubPackets.set("console.count | clear",
+     "arguments": [],
+     "columnNumber": 5,
+     "counter": null,
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "clear",
+     "lineNumber": 7,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1511365913334,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2888,16 +2934,17 @@ stubPackets.set("console.count | default
+       "count": 4,
+       "label": "default"
+     },
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "count",
+     "lineNumber": 8,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1511365913335,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+@@ -2915,16 +2962,17 @@ stubPackets.set("console.count | test co
+       "count": 3,
+       "label": "test counter"
+     },
+     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
+     "functionName": "triggerPacket",
+     "groupName": "",
+     "level": "count",
+     "lineNumber": 9,
++    "prefix": "",
+     "private": false,
+     "timeStamp": 1511365913335,
+     "timer": null,
+     "workerType": "none",
+     "styles": [],
+     "category": "webdev"
+   }
+ });
+diff --git a/dom/console/Console.cpp b/dom/console/Console.cpp
+--- a/dom/console/Console.cpp
++++ b/dom/console/Console.cpp
+@@ -1506,16 +1506,17 @@ Console::PopulateConsoleNotificationInTh
+     // mConsoleEventNotifier.
+     event.mID.Value().SetAsUnsignedLongLong() = 0;
+     event.mInnerID.Value().SetAsUnsignedLongLong() = 0;
+   }
+ 
+   event.mConsoleID = mConsoleID;
+   event.mLevel = aData->mMethodString;
+   event.mFilename = frame.mFilename;
++  event.mPrefix = mPrefix;
+ 
+   nsCOMPtr<nsIURI> filenameURI;
+   nsAutoCString pass;
+   if (NS_IsMainThread() &&
+       NS_SUCCEEDED(NS_NewURI(getter_AddRefs(filenameURI), frame.mFilename)) &&
+       NS_SUCCEEDED(filenameURI->GetPassword(pass)) && !pass.IsEmpty()) {
+     nsCOMPtr<nsISensitiveInfoHiddenURI> safeURI = do_QueryInterface(filenameURI);
+     nsAutoCString spec;
+@@ -2571,18 +2572,18 @@ Console::MaybeExecuteDumpFunction(JSCont
+     return;
+   }
+ 
+   nsAutoString message;
+   message.AssignLiteral("console.");
+   message.Append(aMethodName);
+   message.AppendLiteral(": ");
+ 
+-  if (!mDumpPrefix.IsEmpty()) {
+-    message.Append(mDumpPrefix);
++  if (!mPrefix.IsEmpty()) {
++    message.Append(mPrefix);
+     message.AppendLiteral(": ");
+   }
+ 
+   for (uint32_t i = 0; i < aData.Length(); ++i) {
+     JS::Rooted<JS::Value> v(aCx, aData[i]);
+     JS::Rooted<JSString*> jsString(aCx, JS_ValueToSource(aCx, v));
+     if (!jsString) {
+       continue;
+@@ -2609,18 +2610,18 @@ Console::MaybeExecuteDumpFunctionForTrac
+ {
+   if (!aStack || (!mDumpFunction && !mDumpToStdout)) {
+     return;
+   }
+ 
+   nsAutoString message;
+   message.AssignLiteral("console.trace:\n");
+ 
+-  if (!mDumpPrefix.IsEmpty()) {
+-    message.Append(mDumpPrefix);
++  if (!mPrefix.IsEmpty()) {
++    message.Append(mPrefix);
+     message.AppendLiteral(": ");
+   }
+ 
+   nsCOMPtr<nsIStackFrame> stack(aStack);
+ 
+   while (stack) {
+     nsAutoString filename;
+     stack->GetFilename(aCx, filename);
+diff --git a/dom/console/Console.h b/dom/console/Console.h
+--- a/dom/console/Console.h
++++ b/dom/console/Console.h
+@@ -439,17 +439,17 @@ private:
+   uint64_t mOuterID;
+   uint64_t mInnerID;
+ 
+   // Set only by ConsoleInstance:
+   nsString mConsoleID;
+   nsString mPassedInnerID;
+   RefPtr<ConsoleInstanceDumpCallback> mDumpFunction;
+   bool mDumpToStdout;
+-  nsString mDumpPrefix;
++  nsString mPrefix;
+   bool mChromeInstance;
+   ConsoleLogLevel mMaxLogLevel;
+ 
+   enum {
+     eUnknown,
+     eInitialized,
+     eShuttingDown
+   } mStatus;
+diff --git a/dom/console/ConsoleInstance.cpp b/dom/console/ConsoleInstance.cpp
+--- a/dom/console/ConsoleInstance.cpp
++++ b/dom/console/ConsoleInstance.cpp
+@@ -74,17 +74,17 @@ ConsoleInstance::ConsoleInstance(const C
+ 
+   if (aOptions.mDump.WasPassed()) {
+     mConsole->mDumpFunction = &aOptions.mDump.Value();
+   } else {
+     // For historical reasons, ConsoleInstance prints messages on stdout.
+     mConsole->mDumpToStdout = true;
+   }
+ 
+-  mConsole->mDumpPrefix = aOptions.mPrefix;
++  mConsole->mPrefix = aOptions.mPrefix;
+ 
+   // Let's inform that this is a custom instance.
+   mConsole->mChromeInstance = true;
+ 
+   if (aOptions.mMaxLogLevel.WasPassed()) {
+     mConsole->mMaxLogLevel = aOptions.mMaxLogLevel.Value();
+   }
+ 
+diff --git a/dom/console/tests/test_jsm.xul b/dom/console/tests/test_jsm.xul
+--- a/dom/console/tests/test_jsm.xul
++++ b/dom/console/tests/test_jsm.xul
+@@ -33,29 +33,32 @@ consoleListener.prototype  = {
+ 
+   observe: function(aSubject, aTopic, aData) {
+     if (aTopic == "console-api-log-event") {
+       var obj = aSubject.wrappedJSObject;
+       if (obj.innerID == JSM) {
+         is(obj.ID, "jsm", "ID and InnerID are correctly set.");
+         is(obj.arguments[0], "Hello world!", "Message matches");
+         is(obj.consoleID, "", "No consoleID for console API");
++        is(obj.prefix, "", "prefix is empty by default");
+ 
+         // We want to see 2 messages from this innerID, the first is generated
+         // by console.log, the second one from createInstance().log();
+         ++this.count;
+       } else if (obj.innerID == "CUSTOM INNER") {
+         is(obj.ID, "jsm", "ID and InnerID are correctly set.");
+         is(obj.arguments[0], "Hello world!", "Message matches");
+         is(obj.consoleID, "wow", "consoleID is set by consoleInstance");
++        is(obj.prefix, "_PREFIX_", "prefix is set by consoleInstance");
+         ++this.count;
+       } else if (obj.innerID == "LEVEL") {
+         // Nothing special... just we don't want to see 'invisible' messages.
+         is(obj.ID, "jsm", "ID and InnerID are correctly set.");
+         is(obj.arguments[0], "Hello world!", "Message matches");
++        is(obj.prefix, "", "prefix is empty by default");
+         ++this.count;
+       }
+ 
+       if (this.count == 4) {
+         is(dumpCalled, 2, "Dump has been called!");
+         SpecialPowers.removeObserver(this, "console-api-log-event");
+         SimpleTest.finish();
+       }
+diff --git a/dom/webidl/Console.webidl b/dom/webidl/Console.webidl
+--- a/dom/webidl/Console.webidl
++++ b/dom/webidl/Console.webidl
+@@ -73,16 +73,17 @@ dictionary ConsoleEvent {
+   // lazily.  Note that we're not making this whole thing an interface because
+   // consumers expect to see own properties on it, which would mean making the
+   // props unforgeable, which means lots of JSFunction allocations.  Maybe we
+   // should fix those consumers, of course....
+   // sequence<ConsoleStackEntry> stacktrace;
+   DOMString groupName = "";
+   any timer = null;
+   any counter = null;
++  DOMString prefix = "";
+ };
+ 
+ // Event for profile operations
+ dictionary ConsoleProfileEvent {
+   DOMString action = "";
+   sequence<any> arguments;
+ };
+ 

+ 22 - 22
mozilla-release/patches/1434474-60a1.patch

@@ -2,7 +2,7 @@
 # User Emilio Cobos Alvarez <emilio@crisal.io>
 # Date 1517393556 -3600
 # Node ID 6d21bdf7eab331e98bac5d4fa25faf6f41af647f
-# Parent  739e5a9c6cfa748215e13481c2f3db1a1f814863
+# Parent  4289e3ea78859099eae1b595376582e945fdeb66
 Bug 1434474: There's no need to rebuild font / counter styles / font feature values off a runnable. r=bholley
 
 Everything that needs them up-to-date will call flush appropriately, there
@@ -16,7 +16,7 @@ MozReview-Commit-ID: BVsxXxhtcKL
 diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
 --- a/dom/base/nsDocument.cpp
 +++ b/dom/base/nsDocument.cpp
-@@ -1448,17 +1448,16 @@ nsIDocument::nsIDocument()
+@@ -1451,17 +1451,16 @@ nsIDocument::nsIDocument()
      mBFCacheDisallowed(false),
      mHasHadDefaultView(false),
      mStyleSheetChangeEventsEnabled(false),
@@ -34,7 +34,7 @@ diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
      mDidCallBeginLoad(false),
      mBufferingCSPViolations(false),
      mAllowPaymentRequest(false),
-@@ -4036,18 +4035,21 @@ nsDocument::CreateShell(nsPresContext* a
+@@ -3954,18 +3953,21 @@ nsDocument::CreateShell(nsPresContext* a
  
    MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p with PressShell %p and DocShell %p",
                                                  this, shell.get(), docShell.get()));
@@ -58,7 +58,7 @@ diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
  nsIDocument::UpdateFrameRequestCallbackSchedulingState(nsIPresShell* aOldShell)
  {
    // If the condition for shouldBeScheduled changes to depend on some other
-@@ -4133,18 +4135,20 @@ nsDocument::DeleteShell()
+@@ -4051,18 +4053,20 @@ nsDocument::DeleteShell()
    }
  
    // When our shell goes away, request that all our images be immediately
@@ -81,7 +81,7 @@ diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
    nsIPresShell* oldShell = mPresShell;
    mPresShell = nullptr;
    UpdateFrameRequestCallbackSchedulingState(oldShell);
-@@ -13028,17 +13032,17 @@ gfxUserFontSet*
+@@ -12867,17 +12871,17 @@ gfxUserFontSet*
  nsIDocument::GetUserFontSet(bool aFlushUserFontSet)
  {
    // We want to initialize the user font set lazily the first time the
@@ -100,7 +100,7 @@ diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
    mGetUserFontSetCalled = true;
    if (mFontFaceSetDirty && aFlushUserFontSet) {
      // If this assertion fails, and there have actually been changes to
-@@ -13065,82 +13069,63 @@ void
+@@ -12904,82 +12908,63 @@ void
  nsIDocument::FlushUserFontSet()
  {
    if (!mGetUserFontSetCalled) {
@@ -224,7 +224,7 @@ diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
 diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h
 --- a/dom/base/nsIDocument.h
 +++ b/dom/base/nsIDocument.h
-@@ -3048,17 +3048,17 @@ public:
+@@ -3051,17 +3051,17 @@ public:
  
      if (weakNode) {
        mBlockedTrackingNodes.AppendElement(weakNode);
@@ -243,7 +243,7 @@ diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h
    bool DidFireDOMContentLoaded() const { return mDidFireDOMContentLoaded; }
  
    void SetDocumentUseCounter(mozilla::UseCounter aUseCounter)
-@@ -3275,21 +3275,16 @@ protected:
+@@ -3274,21 +3274,16 @@ protected:
  
    nsCString GetContentTypeInternal() const
    {
@@ -257,15 +257,15 @@ diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h
 -    FlushUserFontSet();
 -  }
 -
-   const nsString& GetId() const
-   {
-     return mId;
-   }
- 
    // Update our frame request callback scheduling state, if needed.  This will
    // schedule or unschedule them, if necessary, and update
    // mFrameRequestCallbacksScheduled.  aOldShell should only be passed when
-@@ -3531,19 +3526,16 @@ protected:
+   // mPresShell is becoming null; in that case it will be used to get hold of
+   // the relevant refresh driver.
+   void UpdateFrameRequestCallbackSchedulingState(nsIPresShell* aOldShell = nullptr);
+ 
+   // Helper for GetScrollingElement/IsScrollingElement.
+@@ -3533,19 +3528,16 @@ protected:
    bool mHasDisplayDocument : 1;
  
    // Is the current mFontFaceSet valid?
@@ -306,7 +306,7 @@ diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
  #include "nsNameSpaceManager.h"  // for Pref-related rule management (bugs 22963,20760,31816)
  #include "nsFrame.h"
  #include "FrameLayerBuilder.h"
-@@ -4515,21 +4516,22 @@ PresShell::ReconstructFrames()
+@@ -4528,21 +4529,22 @@ PresShell::ReconstructFrames()
  void
  nsIPresShell::RestyleForCSSRuleChanges()
  {
@@ -335,7 +335,7 @@ diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
 diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp
 --- a/layout/base/nsPresContext.cpp
 +++ b/layout/base/nsPresContext.cpp
-@@ -304,19 +304,17 @@ nsPresContext::nsPresContext(nsIDocument
+@@ -305,19 +305,17 @@ nsPresContext::nsPresContext(nsIDocument
      mPendingMediaFeatureValuesChanged(false),
      mPrefChangePendingNeedsReflow(false),
      mIsEmulatingMedia(false),
@@ -355,7 +355,7 @@ diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp
      mPaintFlashing(false),
      mPaintFlashingInitialized(false),
      mHasWarnedAboutPositionedTableParts(false),
-@@ -1974,19 +1972,26 @@ nsPresContext::RebuildAllStyleData(nsCha
+@@ -1980,19 +1978,26 @@ nsPresContext::RebuildAllStyleData(nsCha
    mUsesExChUnits = false;
    if (mShell->StyleSet()->IsGecko()) {
  #ifdef MOZ_OLD_STYLE
@@ -385,7 +385,7 @@ diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp
  nsPresContext::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
                                              nsRestyleHint aRestyleHint)
  {
-@@ -2322,37 +2327,24 @@ nsPresContext::FlushCounterStyles()
+@@ -2328,37 +2333,24 @@ nsPresContext::FlushCounterStyles()
        RefreshDriver()->AddPostRefreshObserver(
          new CounterStyleCleaner(RefreshDriver(), mCounterStyleManager));
      }
@@ -425,7 +425,7 @@ diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp
    if (mMissingFonts) {
      mMissingFonts->Flush();
    }
-@@ -3083,38 +3075,16 @@ nsPresContext::FlushFontFeatureValues()
+@@ -3089,38 +3081,16 @@ nsPresContext::FlushFontFeatureValues()
  
    if (mFontFeatureValuesDirty) {
      StyleSetHandle styleSet = mShell->StyleSet();
@@ -467,7 +467,7 @@ diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp
 diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h
 --- a/layout/base/nsPresContext.h
 +++ b/layout/base/nsPresContext.h
-@@ -946,33 +946,36 @@ public:
+@@ -959,33 +959,36 @@ public:
      mPaintFlashing = aPaintFlashing;
      mPaintFlashingInitialized = true;
    }
@@ -507,7 +507,7 @@ diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h
  
    // Mark an area as invalidated, associated with a given transaction id (allocated
    // by nsRefreshDriver::GetTransactionId).
-@@ -1213,26 +1216,16 @@ public:
+@@ -1226,26 +1229,16 @@ public:
    void InvalidatePaintedLayers();
  
  protected:
@@ -534,7 +534,7 @@ diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h
    // Creates a one-shot timer with the given aCallback & aDelay.
    // Returns a refcounted pointer to the timer (or nullptr on failure).
    already_AddRefed<nsITimer> CreateTimer(nsTimerCallbackFunc aCallback,
-@@ -1412,23 +1405,19 @@ protected:
+@@ -1426,23 +1419,19 @@ protected:
    // Does the associated document use ex or ch units?
    unsigned              mUsesExChUnits : 1;
  

+ 22 - 22
mozilla-release/patches/1439960-1-61a1.patch

@@ -2,7 +2,7 @@
 # User Ryan Hunt <rhunt@eqrion.net>
 # Date 1519226989 21600
 # Node ID 62432cc896eefa5fda52fdf00b7390cf1f2318cd
-# Parent  df6e42d222192cab8ea09953d196c40566961def
+# Parent  7dcbc01a90a72af4626f9fcccbb025ce0925111e
 Remove DisplayItemLayer and corresponding layers.advanced prefs. (bug 1439960, r=mstange)
 
 MozReview-Commit-ID: FAWTC1Llu31
@@ -478,7 +478,7 @@ diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build
 diff --git a/gfx/layers/wr/WebRenderLayerManager.cpp b/gfx/layers/wr/WebRenderLayerManager.cpp
 --- a/gfx/layers/wr/WebRenderLayerManager.cpp
 +++ b/gfx/layers/wr/WebRenderLayerManager.cpp
-@@ -324,18 +324,16 @@ WebRenderLayerManager::EndTransactionWit
+@@ -321,18 +321,16 @@ WebRenderLayerManager::EndTransactionWit
    {
      AUTO_PROFILER_TRACING("Paint", "ForwardDPTransaction");
      WrBridge()->EndTransaction(contentSize, dl, resourceUpdates, size.ToUnknownSize(),
@@ -500,7 +500,7 @@ diff --git a/gfx/layers/wr/WebRenderLayerManager.cpp b/gfx/layers/wr/WebRenderLa
 diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h
 --- a/gfx/thebes/gfxPrefs.h
 +++ b/gfx/thebes/gfxPrefs.h
-@@ -536,28 +536,18 @@ private:
+@@ -539,28 +539,18 @@ private:
    DECL_GFX_PREF(Once, "image.multithreaded_decoding.idle_timeout", ImageMTDecodingIdleTimeout, int32_t, -1);
    DECL_GFX_PREF(Live, "image.webp.enabled",                    ImageWebPEnabled, bool, false);
  
@@ -912,7 +912,7 @@ diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFra
 diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp
 --- a/layout/generic/nsImageFrame.cpp
 +++ b/layout/generic/nsImageFrame.cpp
-@@ -1621,18 +1621,17 @@ nsDisplayImage::GetDestRect() const
+@@ -1624,18 +1624,17 @@ nsDisplayImage::GetDestRect() const
    return imageFrame->PredictedDestRect(frameContentBox);
  }
  
@@ -935,7 +935,7 @@ diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp
 diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp
 --- a/layout/painting/nsDisplayList.cpp
 +++ b/layout/painting/nsDisplayList.cpp
-@@ -3182,22 +3182,19 @@ bool
+@@ -3173,22 +3173,19 @@ bool
  nsDisplayItem::ShouldUseAdvancedLayer(LayerManager* aManager, PrefFunc aFunc) const
  {
    return CanUseAdvancedLayer(aManager) ? aFunc() : false;
@@ -961,7 +961,7 @@ diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.c
  {
    for (const ActiveScrolledRoot* asr =
           ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR);
-@@ -3246,53 +3243,30 @@ nsDisplayItem::IntersectClip(nsDisplayLi
+@@ -3237,53 +3234,30 @@ nsDisplayItem::IntersectClip(nsDisplayLi
  nsRect
  nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) const
  {
@@ -1016,7 +1016,7 @@ diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.c
  already_AddRefed<Layer>
  nsDisplaySolidColor::BuildLayer(nsDisplayListBuilder* aBuilder,
                                  LayerManager* aManager,
-@@ -3990,21 +3964,16 @@ static void CheckForBorderItem(nsDisplay
+@@ -3981,21 +3955,16 @@ static void CheckForBorderItem(nsDisplay
  LayerState
  nsDisplayBackgroundImage::GetLayerState(nsDisplayListBuilder* aBuilder,
                                          LayerManager* aManager,
@@ -1038,7 +1038,7 @@ diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.c
    }
  
    if (CanOptimizeToImageLayer(aManager, aBuilder)) {
-@@ -4042,20 +4011,16 @@ nsDisplayBackgroundImage::GetLayerState(
+@@ -4033,20 +4002,16 @@ nsDisplayBackgroundImage::GetLayerState(
    return LAYER_NONE;
  }
  
@@ -1059,7 +1059,7 @@ diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.c
        return nullptr;
    }
    RefPtr<ImageContainer> imageContainer = GetContainer(aManager, aBuilder);
-@@ -4078,17 +4043,17 @@ nsDisplayBackgroundImage::CanBuildWebRen
+@@ -4069,17 +4034,17 @@ nsDisplayBackgroundImage::CanBuildWebRen
  bool
  nsDisplayBackgroundImage::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                                    mozilla::wr::IpcResourceUpdateQueue& aResources,
@@ -1078,7 +1078,7 @@ diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.c
    }
    CheckForBorderItem(this, mImageFlags);
    nsCSSRendering::PaintBGParams params =
-@@ -4678,18 +4643,17 @@ nsDisplayBackgroundColor::CanApplyOpacit
+@@ -4669,18 +4634,17 @@ nsDisplayBackgroundColor::CanApplyOpacit
  }
  
  LayerState
@@ -1098,7 +1098,7 @@ diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.c
  already_AddRefed<Layer>
  nsDisplayBackgroundColor::BuildLayer(nsDisplayListBuilder* aBuilder,
                                       LayerManager* aManager,
-@@ -4903,72 +4867,48 @@ nsDisplayOutline::Paint(nsDisplayListBui
+@@ -4894,72 +4858,48 @@ nsDisplayOutline::Paint(nsDisplayListBui
  
    nsPoint offset = ToReferenceFrame();
    nsCSSRendering::PaintOutline(mFrame->PresContext(), *aCtx, mFrame,
@@ -1192,7 +1192,7 @@ diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.c
  {
    const nsStyleOutline* outline = mFrame->StyleOutline();
    nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
-@@ -5375,36 +5315,16 @@ nsDisplayCaret::CreateWebRenderCommands(
+@@ -5366,36 +5306,16 @@ nsDisplayCaret::CreateWebRenderCommands(
      aBuilder.PushRect(hook,
                        hook,
                        !BackfaceIsHidden(),
@@ -1229,7 +1229,7 @@ diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.c
  
    mBounds = CalculateBounds<nsRect>(*mFrame->StyleBorder());
  }
-@@ -5526,35 +5446,31 @@ already_AddRefed<Layer>
+@@ -5517,35 +5437,31 @@ already_AddRefed<Layer>
  nsDisplayBorder::BuildLayer(nsDisplayListBuilder* aBuilder,
                              LayerManager* aManager,
                              const ContainerLayerParameters& aContainerParameters)
@@ -1280,7 +1280,7 @@ diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.c
                                           const StackingContextHelper& aSc,
                                           mozilla::layers::WebRenderLayerManager* aManager,
                                           nsDisplayListBuilder* aDisplayListBuilder)
-@@ -5807,24 +5723,16 @@ nsDisplayBoxShadowOuter::ComputeVisibili
+@@ -5798,24 +5714,16 @@ nsDisplayBoxShadowOuter::ComputeVisibili
    if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion)) {
      return false;
    }
@@ -1305,7 +1305,7 @@ diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.c
      return false;
    }
  
-@@ -6008,24 +5916,16 @@ nsDisplayBoxShadowInner::CanCreateWebRen
+@@ -5999,24 +5907,16 @@ nsDisplayBoxShadowInner::CanCreateWebRen
    // input buttons.
    if (nativeTheme) {
      return false;
@@ -1540,7 +1540,7 @@ diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp
 diff --git a/layout/xul/nsImageBoxFrame.cpp b/layout/xul/nsImageBoxFrame.cpp
 --- a/layout/xul/nsImageBoxFrame.cpp
 +++ b/layout/xul/nsImageBoxFrame.cpp
-@@ -539,36 +539,16 @@ void nsDisplayXULImage::Paint(nsDisplayL
+@@ -540,36 +540,16 @@ void nsDisplayXULImage::Paint(nsDisplayL
      flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
  
    ImgDrawResult result = static_cast<nsImageBoxFrame*>(mFrame)->
@@ -1608,7 +1608,7 @@ diff --git a/layout/xul/nsImageBoxFrame.h b/layout/xul/nsImageBoxFrame.h
 diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
 --- a/modules/libpref/init/all.js
 +++ b/modules/libpref/init/all.js
-@@ -5954,34 +5954,16 @@ pref("fuzzing.enabled", false);
+@@ -5960,34 +5960,16 @@ pref("fuzzing.enabled", false);
  #if defined(XP_WIN)
  pref("layers.mlgpu.enabled", true);
  
@@ -1638,8 +1638,8 @@ diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
  // Enable lowercased response header name
  pref("dom.xhr.lowercase_header.enabled", false);
  
- // When a crash happens, whether to include heap regions of the crash context
- // in the minidump. Enabled by default on nightly and aurora.
- #ifdef RELEASE_OR_BETA
- pref("toolkit.crashreporter.include_context_heap", false);
- #else
+ // Control whether clients.openWindow() opens windows in the same process
+ // that called the API vs following our normal multi-process selection
+ // algorithm.  Restricting openWindow to same process improves service worker
+ // web compat in the short term.  Once the SW multi-e10s refactor is complete
+ // this can be removed.

+ 4 - 4
mozilla-release/patches/1441246-61a1.patch

@@ -2,7 +2,7 @@
 # User Dragana Damjanovic <dd.mozilla@gmail.com>
 # Date 1524217620 -10800
 # Node ID ccbbd0a711734c30808a2e934b5788d0907d405b
-# Parent  a92510adac7ac0bbadce80f6629eee70dcba53c0
+# Parent  06a30ad6e5a71c5ba28197cc381850e811ad9bd2
 Bug 1441246 - Move preload code from nsStyleLinkElement to HTMLLinkElement. r=smaug
 
 diff --git a/dom/base/Link.cpp b/dom/base/Link.cpp
@@ -127,11 +127,11 @@ diff --git a/dom/base/Link.cpp b/dom/base/Link.cpp
 diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp
 --- a/dom/base/nsContentSink.cpp
 +++ b/dom/base/nsContentSink.cpp
-@@ -47,16 +47,17 @@
- #include "nsGenericHTMLElement.h"
+@@ -48,16 +48,17 @@
  #include "nsHTMLDNSPrefetch.h"
  #include "nsIObserverService.h"
  #include "mozilla/Preferences.h"
+ #include "mozilla/dom/ServiceWorkerDescriptor.h"
  #include "mozilla/dom/ScriptLoader.h"
  #include "nsParserConstants.h"
  #include "nsSandboxFlags.h"
@@ -145,7 +145,7 @@ diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp
  
  NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentSink)
  NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentSink)
-@@ -880,18 +881,18 @@ nsContentSink::PrefetchPreloadHref(const
+@@ -881,18 +882,18 @@ nsContentSink::PrefetchPreloadHref(const
          if (policyType == nsIContentPolicy::TYPE_INVALID) {
            // Ignore preload with a wrong or empty as attribute.
            return;

+ 11 - 11
mozilla-release/patches/1444905-61a1.patch

@@ -2,7 +2,7 @@
 # User Emilio Cobos Alvarez <emilio@crisal.io>
 # Date 1520863662 -3600
 # Node ID 543c4f3183a2812d33b75226cecafa04bb35c541
-# Parent  1415a44b9a83389ccbf0d6af8ec5576e6eaa0516
+# Parent  9403b15a9b967e476e518286e06a982dd0bd5d54
 Bug 1444905: Remove scoped style support from the old style system. r=xidorn,smaug
 
 Summary: It uses two node bits that can be better suited for something else.
@@ -203,15 +203,15 @@ diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
 -  }
 -
 -  /**
-    * Return true if this doc is controlled by a ServiceWorker.
-    */
-   static bool IsControlledByServiceWorker(nsIDocument* aDocument);
- 
-   /**
     * Fire mutation events for changes caused by parsing directly into a
     * context node.
     *
-@@ -3479,17 +3471,16 @@ private:
+    * @param aDoc the document of the node
+    * @param aDest the destination node that got stuff appended to it
+    * @param aOldChildCount the number of children the node had before parsing
+    */
+   static void FireMutationEventsForDirectParsing(nsIDocument* aDoc,
+@@ -3481,17 +3473,16 @@ private:
    static bool sRequestIdleCallbackEnabled;
    static bool sLowerNetworkPriority;
    static bool sTailingEnabled;
@@ -232,7 +232,7 @@ diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
 diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
 --- a/dom/base/nsDocument.cpp
 +++ b/dom/base/nsDocument.cpp
-@@ -1457,17 +1457,16 @@ nsIDocument::nsIDocument()
+@@ -1460,17 +1460,16 @@ nsIDocument::nsIDocument()
      mHasScrollLinkedEffect(false),
      mFrameRequestCallbacksScheduled(false),
      mIsTopLevelContentDocument(false),
@@ -250,7 +250,7 @@ diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
  #else
      mDummy(0),
  #endif
-@@ -13606,28 +13605,16 @@ nsDocument::IsThirdParty()
+@@ -13533,28 +13532,16 @@ nsDocument::IsThirdParty()
      return mIsThirdParty.value();
    }
  
@@ -282,7 +282,7 @@ diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
 diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h
 --- a/dom/base/nsIDocument.h
 +++ b/dom/base/nsIDocument.h
-@@ -3570,20 +3570,16 @@ protected:
+@@ -3568,20 +3568,16 @@ protected:
  
    // True if dom.webcomponents.shadowdom.enabled pref is set when document is
    // created.
@@ -5106,7 +5106,7 @@ diff --git a/layout/style/nsStyleSet.h b/layout/style/nsStyleSet.h
 diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
 --- a/modules/libpref/init/all.js
 +++ b/modules/libpref/init/all.js
-@@ -3090,22 +3090,16 @@ pref("layout.css.prefixes.gradients", tr
+@@ -3095,22 +3095,16 @@ pref("layout.css.prefixes.gradients", tr
  // Are webkit-prefixed properties & property-values supported?
  pref("layout.css.prefixes.webkit", true);
  

+ 4 - 4
mozilla-release/patches/1447210-2-61a1.patch

@@ -2,13 +2,13 @@
 # User Andrea Marchesini <amarchesini@mozilla.com>
 # Date 1521876061 -3600
 # Node ID 2acdab79292680b9764ad907d4ca67f9aa8bb49d
-# Parent  ef603ab3c4a4d68670913d1e8061c0b91ebc331f
+# Parent  ff13ba38a779c0990519041995101e6bebe7b76f
 Bug 1447210 - Warning messages if console level pref doesn't exist, r=bgrins
 
 diff --git a/dom/console/ConsoleInstance.cpp b/dom/console/ConsoleInstance.cpp
 --- a/dom/console/ConsoleInstance.cpp
 +++ b/dom/console/ConsoleInstance.cpp
-@@ -17,32 +17,43 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(Console
+@@ -19,32 +19,43 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(Console
  
  NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ConsoleInstance)
    NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -53,8 +53,8 @@ diff --git a/dom/console/ConsoleInstance.cpp b/dom/console/ConsoleInstance.cpp
    return static_cast<ConsoleLogLevel>(index);
  }
  
- } // anonymous
-@@ -66,17 +77,17 @@ ConsoleInstance::ConsoleInstance(const C
+ ConsoleUtils::Level
+@@ -85,17 +96,17 @@ ConsoleInstance::ConsoleInstance(const C
    mConsole->mChromeInstance = true;
  
    if (aOptions.mMaxLogLevel.WasPassed()) {

+ 145 - 0
mozilla-release/patches/1461181-62a1.patch

@@ -0,0 +1,145 @@
+# HG changeset patch
+# User Ben Kelly <ben@wanderview.com>
+# Date 1526919227 25200
+# Node ID d5ec7b9c344ae2475ff6c411c3776c7c33353ec5
+# Parent  a89976be25c1dfc643b5067084d34cae0ece1e60
+Bug 1461181 Don't call ServiceWorkerManager::StartControllingClient() if there is no active worker. r=asuth
+
+diff --git a/dom/serviceworkers/ServiceWorkerManager.cpp.1461181.later b/dom/serviceworkers/ServiceWorkerManager.cpp.1461181.later
+new file mode 100644
+--- /dev/null
++++ b/dom/serviceworkers/ServiceWorkerManager.cpp.1461181.later
+@@ -0,0 +1,66 @@
++--- ServiceWorkerManager.cpp
+++++ ServiceWorkerManager.cpp
++@@ -307,17 +307,17 @@ ServiceWorkerManager::Init(ServiceWorker
++ 
++   mActor = static_cast<ServiceWorkerManagerChild*>(actor);
++ }
++ 
++ RefPtr<GenericPromise>
++ ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo,
++                                              ServiceWorkerRegistrationInfo* aRegistrationInfo)
++ {
++-  MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive());
+++  MOZ_RELEASE_ASSERT(aRegistrationInfo->GetActive());
++ 
++   RefPtr<GenericPromise> ref;
++ 
++   const ServiceWorkerDescriptor& active =
++     aRegistrationInfo->GetActive()->Descriptor();
++ 
++   auto entry = mControlledClients.LookupForAdd(aClientInfo.Id());
++   if (entry) {
++@@ -1877,16 +1877,17 @@ ServiceWorkerManager::StartControlling(c
++   nsCOMPtr<nsIURI> scope;
++   nsresult rv =
++     NS_NewURI(getter_AddRefs(scope), aServiceWorker.Scope(), nullptr, nullptr);
++   NS_ENSURE_SUCCESS(rv, false);
++ 
++   RefPtr<ServiceWorkerRegistrationInfo> registration =
++     GetServiceWorkerRegistrationInfo(principal, scope);
++   NS_ENSURE_TRUE(registration, false);
+++  NS_ENSURE_TRUE(registration->GetActive(), false);
++ 
++   StartControllingClient(aClientInfo, registration);
++ 
++   return true;
++ }
++ 
++ void
++ ServiceWorkerManager::MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo)
++@@ -2552,20 +2553,25 @@ ServiceWorkerManager::UpdateInternal(nsI
++   queue->ScheduleJob(job);
++ }
++ 
++ already_AddRefed<GenericPromise>
++ ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,
++                                        ServiceWorkerRegistrationInfo* aWorkerRegistration)
++ {
++   MOZ_DIAGNOSTIC_ASSERT(aWorkerRegistration);
++-  MOZ_DIAGNOSTIC_ASSERT(aWorkerRegistration->GetActive());
++ 
++   RefPtr<GenericPromise> ref;
++ 
+++  if (!aWorkerRegistration->GetActive()) {
+++    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+++                                          __func__);
+++    return ref.forget();
+++  }
+++
++   // Same origin check
++   if (!aWorkerRegistration->Principal()->Equals(aDocument->NodePrincipal())) {
++     ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_SECURITY_ERR, __func__);
++     return ref.forget();
++   }
++ 
++   Maybe<ClientInfo> clientInfo(aDocument->GetClientInfo());
++   if (NS_WARN_IF(clientInfo.isNothing())) {
+diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
+--- a/dom/workers/ServiceWorkerManager.cpp
++++ b/dom/workers/ServiceWorkerManager.cpp
+@@ -307,17 +307,17 @@ ServiceWorkerManager::Init(ServiceWorker
+ 
+   mActor = static_cast<ServiceWorkerManagerChild*>(actor);
+ }
+ 
+ RefPtr<GenericPromise>
+ ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo,
+                                              ServiceWorkerRegistrationInfo* aRegistrationInfo)
+ {
+-  MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive());
++  MOZ_RELEASE_ASSERT(aRegistrationInfo->GetActive());
+ 
+   RefPtr<GenericPromise> ref;
+ 
+   const ServiceWorkerDescriptor& active =
+     aRegistrationInfo->GetActive()->Descriptor();
+ 
+   auto entry = mControlledClients.LookupForAdd(aClientInfo.Id());
+   if (entry) {
+@@ -2152,16 +2152,17 @@ ServiceWorkerManager::StartControlling(c
+   nsCOMPtr<nsIURI> scope;
+   nsresult rv =
+     NS_NewURI(getter_AddRefs(scope), aServiceWorker.Scope(), nullptr, nullptr);
+   NS_ENSURE_SUCCESS(rv, false);
+ 
+   RefPtr<ServiceWorkerRegistrationInfo> registration =
+     GetServiceWorkerRegistrationInfo(principal, scope);
+   NS_ENSURE_TRUE(registration, false);
++  NS_ENSURE_TRUE(registration->GetActive(), false);
+ 
+   StartControllingClient(aClientInfo, registration);
+ 
+   return true;
+ }
+ 
+ void
+ ServiceWorkerManager::MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo)
+@@ -2948,20 +2949,25 @@ ServiceWorkerManager::UpdateInternal(nsI
+   queue->ScheduleJob(job);
+ }
+ 
+ already_AddRefed<GenericPromise>
+ ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,
+                                        ServiceWorkerRegistrationInfo* aWorkerRegistration)
+ {
+   MOZ_DIAGNOSTIC_ASSERT(aWorkerRegistration);
+-  MOZ_DIAGNOSTIC_ASSERT(aWorkerRegistration->GetActive());
+ 
+   RefPtr<GenericPromise> ref;
+ 
++  if (!aWorkerRegistration->GetActive()) {
++    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
++                                          __func__);
++    return ref.forget();
++  }
++
+   // Same origin check
+   if (!aWorkerRegistration->mPrincipal->Equals(aDocument->NodePrincipal())) {
+     ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_SECURITY_ERR, __func__);
+     return ref.forget();
+   }
+ 
+   Maybe<ClientInfo> clientInfo(aDocument->GetClientInfo());
+   if (NS_WARN_IF(clientInfo.isNothing())) {

+ 246 - 20
mozilla-release/patches/1465060-1-std-62a1.patch

@@ -2,7 +2,7 @@
 # User Miko Mynttinen <mikokm@gmail.com>
 # Date 1527868747 -7200
 # Node ID a0d11b55d5957a488b41420c4f6cc178df7cd2e7
-# Parent  c5418379227eca67caf387159afbba9ec3e6013d
+# Parent  5dc997a8ba29c97bb16163afe843ff7488892fa7
 Bug 1465060 - Part 1: Fix warnings for std::move() use r=froydnj
 
 MozReview-Commit-ID: HpdFXqQdIOO
@@ -56,7 +56,26 @@ diff --git a/devtools/shared/heapsnapshot/DeserializedNode.cpp b/devtools/shared
 diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
 --- a/docshell/base/nsDocShell.cpp
 +++ b/docshell/base/nsDocShell.cpp
-@@ -14794,20 +14794,20 @@ nsDocShell::NotifyJSRunToCompletionStart
+@@ -3479,17 +3479,17 @@ nsDocShell::MaybeCreateInitialClientSour
+ }
+ 
+ Maybe<ClientInfo>
+ nsDocShell::GetInitialClientInfo() const
+ {
+   if (mInitialClientSource) {
+     Maybe<ClientInfo> result;
+     result.emplace(mInitialClientSource->Info());
+-    return std::move(result);
++    return result;
+   }
+ 
+   nsGlobalWindow* innerWindow =
+     mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
+   nsIDocument* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
+ 
+   if (!doc || !doc->IsInitialDocument()) {
+     return Maybe<ClientInfo>();
+@@ -15073,20 +15073,20 @@ nsDocShell::NotifyJSRunToCompletionStart
                                           const uint32_t aLineNumber,
                                           JS::Handle<JS::Value> aAsyncStack,
                                           const char* aAsyncCause)
@@ -242,10 +261,95 @@ diff --git a/dom/base/nsContentPermissionHelper.cpp b/dom/base/nsContentPermissi
  {
    auto it = ContentPermissionRequestChildMap().find(aChild);
    MOZ_ASSERT(it != ContentPermissionRequestChildMap().end());
+diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
+--- a/dom/base/nsDocument.cpp
++++ b/dom/base/nsDocument.cpp
+@@ -5670,39 +5670,39 @@ nsIDocument::GetAnonRootIfInAnonymousCon
+   return nullptr;
+ }
+ 
+ Maybe<ClientInfo>
+ nsIDocument::GetClientInfo() const
+ {
+   nsPIDOMWindowInner* inner = GetInnerWindow();
+   if (inner) {
+-    return std::move(inner->GetClientInfo());
+-  }
+-  return std::move(Maybe<ClientInfo>());
++    return inner->GetClientInfo();
++  }
++  return Maybe<ClientInfo>();
+ }
+ 
+ Maybe<ClientState>
+ nsIDocument::GetClientState() const
+ {
+   nsPIDOMWindowInner* inner = GetInnerWindow();
+   if (inner) {
+-    return std::move(inner->GetClientState());
+-  }
+-  return std::move(Maybe<ClientState>());
++    return inner->GetClientState();
++  }
++  return Maybe<ClientState>();
+ }
+ 
+ Maybe<ServiceWorkerDescriptor>
+ nsIDocument::GetController() const
+ {
+   nsPIDOMWindowInner* inner = GetInnerWindow();
+   if (inner) {
+-    return std::move(inner->GetController());
+-  }
+-  return std::move(Maybe<ServiceWorkerDescriptor>());
++    return inner->GetController();
++  }
++  return Maybe<ServiceWorkerDescriptor>();
+ }
+ 
+ //
+ // nsIDOMDocument interface
+ //
+ DocumentType*
+ nsIDocument::GetDoctype() const
+ {
 diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
 --- a/dom/base/nsGlobalWindow.cpp
 +++ b/dom/base/nsGlobalWindow.cpp
-@@ -11634,17 +11634,17 @@ nsGlobalWindow::ShowSlowScriptDialog(con
+@@ -4454,29 +4454,29 @@ void
+ nsPIDOMWindowInner::SyncStateFromParentWindow()
+ {
+   nsGlobalWindow::Cast(this)->SyncStateFromParentWindow();
+ }
+ 
+ Maybe<ClientInfo>
+ nsPIDOMWindowInner::GetClientInfo() const
+ {
+-  return std::move(nsGlobalWindow::Cast(this)->GetClientInfo());
++  return nsGlobalWindow::Cast(this)->GetClientInfo();
+ }
+ 
+ Maybe<ClientState>
+ nsPIDOMWindowInner::GetClientState() const
+ {
+-  return std::move(nsGlobalWindow::Cast(this)->GetClientState());
++  return nsGlobalWindow::Cast(this)->GetClientState();
+ }
+ 
+ Maybe<ServiceWorkerDescriptor>
+ nsPIDOMWindowInner::GetController() const
+ {
+-  return std::move(nsGlobalWindow::Cast(this)->GetController());
++  return nsGlobalWindow::Cast(this)->GetController();
+ }
+ 
+ void
+ nsPIDOMWindowInner::NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope)
+ {
+   nsGlobalWindow::Cast(this)->NoteCalledRegisterForServiceWorkerScope(aScope);
+ }
+ 
+@@ -11924,17 +11924,17 @@ nsGlobalWindow::ShowSlowScriptDialog(con
    auto getString = [&] (const char* name,
                          nsContentUtils::PropertiesFile propFile = nsContentUtils::eDOM_PROPERTIES) {
      nsAutoString result;
@@ -264,6 +368,53 @@ diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
    // Get localizable strings
  
    nsAutoString title, checkboxMsg, debugButton, msg;
+@@ -12778,43 +12778,43 @@ nsGlobalWindow::CallOnChildren(Method aM
+ Maybe<ClientInfo>
+ nsGlobalWindow::GetClientInfo() const
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   Maybe<ClientInfo> clientInfo;
+   if (mClientSource) {
+     clientInfo.emplace(mClientSource->Info());
+   }
+-  return std::move(clientInfo);
++  return clientInfo;
+ }
+ 
+ Maybe<ClientState>
+ nsGlobalWindow::GetClientState() const
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   Maybe<ClientState> clientState;
+   if (mClientSource) {
+     ClientState state;
+     nsresult rv = mClientSource->SnapshotState(&state);
+     if (NS_SUCCEEDED(rv)) {
+       clientState.emplace(state);
+     }
+   }
+-  return std::move(clientState);
++  return clientState;
+ }
+ 
+ Maybe<ServiceWorkerDescriptor>
+ nsGlobalWindow::GetController() const
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   Maybe<ServiceWorkerDescriptor> controller;
+   if (mClientSource) {
+     controller = mClientSource->GetController();
+   }
+-  return std::move(controller);
++  return controller;
+ }
+ 
+ nsresult
+ nsGlobalWindow::FireDelayedDOMEvents()
+ {
+   FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED);
+ 
+   if (mApplicationCache) {
 diff --git a/dom/canvas/ImageBitmap.cpp b/dom/canvas/ImageBitmap.cpp
 --- a/dom/canvas/ImageBitmap.cpp
 +++ b/dom/canvas/ImageBitmap.cpp
@@ -368,16 +519,67 @@ diff --git a/dom/canvas/WebGLTextureUpload.cpp b/dom/canvas/WebGLTextureUpload.c
                         GLsizei depth, GLint border, const webgl::PackingInfo& pi,
                         const TexImageSource& src)
  {
+diff --git a/dom/clients/manager/ClientHandle.cpp b/dom/clients/manager/ClientHandle.cpp
+--- a/dom/clients/manager/ClientHandle.cpp
++++ b/dom/clients/manager/ClientHandle.cpp
+@@ -200,13 +200,13 @@ ClientHandle::OnDetach()
+   if (!mDetachPromise) {
+     mDetachPromise = new GenericPromise::Private(__func__);
+     if (IsShutdown()) {
+       mDetachPromise->Resolve(true, __func__);
+     }
+   }
+ 
+   RefPtr<GenericPromise> ref(mDetachPromise);
+-  return std::move(ref);
++  return ref;
+ }
+ 
+ } // namespace dom
+ } // namespace mozilla
+diff --git a/dom/clients/manager/ClientInfo.cpp b/dom/clients/manager/ClientInfo.cpp
+--- a/dom/clients/manager/ClientInfo.cpp
++++ b/dom/clients/manager/ClientInfo.cpp
+@@ -140,13 +140,13 @@ ClientInfo::IsPrivateBrowsing() const
+   }
+ }
+ 
+ nsCOMPtr<nsIPrincipal>
+ ClientInfo::GetPrincipal() const
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   nsCOMPtr<nsIPrincipal> ref = PrincipalInfoToPrincipal(PrincipalInfo());
+-  return std::move(ref);
++  return ref;
+ }
+ 
+ } // namespace dom
+ } // namespace mozilla
 diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientManager.cpp
 --- a/dom/clients/manager/ClientManager.cpp
 +++ b/dom/clients/manager/ClientManager.cpp
-@@ -112,17 +112,17 @@ ClientManager::CreateSourceInternal(Clie
+@@ -109,30 +109,30 @@ ClientManager::CreateSourceInternal(Clie
    if (NS_WARN_IF(NS_FAILED(rv))) {
-     return nullptr;
+     // If we can't even get a UUID, at least make sure not to use a garbage
+     // value.  Instead return a shutdown ClientSource with a zero'd id.
+     // This should be exceptionally rare, if it happens at all.
+     id.Clear();
+     ClientSourceConstructorArgs args(id, aType, aPrincipal, TimeStamp::Now());
+     UniquePtr<ClientSource> source(new ClientSource(this, aEventTarget, args));
+     source->Shutdown();
+-    return std::move(source);
++    return source;
    }
  
    ClientSourceConstructorArgs args(id, aType, aPrincipal, TimeStamp::Now());
    UniquePtr<ClientSource> source(new ClientSource(this, aEventTarget, args));
+ 
+   if (IsShutdown()) {
+     source->Shutdown();
+-    return std::move(source);
++    return source;
+   }
+ 
    source->Activate(GetActor());
  
 -  return std::move(source);
@@ -390,10 +592,34 @@ diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientM
  {
    NS_ASSERT_OWNINGTHREAD(ClientManager);
    MOZ_DIAGNOSTIC_ASSERT(aSerialEventTarget);
+diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp
+--- a/dom/clients/manager/ClientManagerService.cpp
++++ b/dom/clients/manager/ClientManagerService.cpp
+@@ -468,18 +468,18 @@ ClientManagerService::MatchAll(const Cli
+ 
+       if(controller.ref().Id() != swd.Id() ||
+          controller.ref().Scope() != swd.Scope()) {
+         continue;
+       }
+     }
+ 
+     promiseList->AddPromise(
+-      source->StartOp(std::move(ClientGetInfoAndStateArgs(source->Info().Id(),
+-                                                          source->Info().PrincipalInfo()))));
++      source->StartOp(ClientGetInfoAndStateArgs(source->Info().Id(),
++                                                source->Info().PrincipalInfo())));
+   }
+ 
+   // Maybe finish the promise now in case we didn't find any matching clients.
+   promiseList->MaybeFinish();
+ 
+   return promiseList->GetResultPromise();
+ }
+ 
 diff --git a/dom/console/Console.cpp b/dom/console/Console.cpp
 --- a/dom/console/Console.cpp
 +++ b/dom/console/Console.cpp
-@@ -2459,36 +2459,36 @@ Console::MonotonicTimer(JSContext* aCx, 
+@@ -2506,36 +2506,36 @@ Console::MonotonicTimer(JSContext* aCx, 
          return false;
        }
  
@@ -437,7 +663,7 @@ diff --git a/dom/console/Console.cpp b/dom/console/Console.cpp
 diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp
 --- a/dom/events/EventListenerManager.cpp
 +++ b/dom/events/EventListenerManager.cpp
-@@ -1232,19 +1232,19 @@ EventListenerManager::HandleEventInterna
+@@ -1228,19 +1228,19 @@ EventListenerManager::HandleEventInterna
                docShell = nsContentUtils::GetDocShellForEventTarget(mTarget);
                if (docShell) {
                  if (timelines && timelines->HasConsumer(docShell)) {
@@ -525,7 +751,7 @@ diff --git a/dom/geolocation/nsGeolocation.cpp b/dom/geolocation/nsGeolocation.c
 diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
 --- a/dom/ipc/ContentParent.cpp
 +++ b/dom/ipc/ContentParent.cpp
-@@ -4231,18 +4231,18 @@ ContentParent::RecvNotifyTabDestroying(c
+@@ -4310,18 +4310,18 @@ ContentParent::RecvNotifyTabDestroying(c
  {
    NotifyTabDestroying(aTabId, aCpId);
    return IPC_OK();
@@ -661,7 +887,7 @@ diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp
 diff --git a/dom/media/eme/MediaKeySystemAccess.cpp b/dom/media/eme/MediaKeySystemAccess.cpp
 --- a/dom/media/eme/MediaKeySystemAccess.cpp
 +++ b/dom/media/eme/MediaKeySystemAccess.cpp
-@@ -765,17 +765,17 @@ GetSupportedCapabilities(
+@@ -757,17 +757,17 @@ GetSupportedCapabilities(
      if (!supportedCapabilities.AppendElement(capabilities, mozilla::fallible)) {
        NS_WARNING("GetSupportedCapabilities: Malloc failure");
        return Sequence<MediaKeySystemMediaCapability>();
@@ -819,7 +1045,7 @@ diff --git a/dom/media/fake-cdm/cdm-test-storage.cpp b/dom/media/fake-cdm/cdm-te
 diff --git a/dom/media/gmp/ChromiumCDMParent.cpp b/dom/media/gmp/ChromiumCDMParent.cpp
 --- a/dom/media/gmp/ChromiumCDMParent.cpp
 +++ b/dom/media/gmp/ChromiumCDMParent.cpp
-@@ -1101,17 +1101,17 @@ ChromiumCDMParent::RecvDrainComplete()
+@@ -1073,17 +1073,17 @@ ChromiumCDMParent::RecvDrainComplete()
  {
    if (mIsShutdown) {
      MOZ_ASSERT(mDecodePromise.IsEmpty());
@@ -841,7 +1067,7 @@ diff --git a/dom/media/gmp/ChromiumCDMParent.cpp b/dom/media/gmp/ChromiumCDMPare
 diff --git a/dom/media/gmp/GMPParent.cpp b/dom/media/gmp/GMPParent.cpp
 --- a/dom/media/gmp/GMPParent.cpp
 +++ b/dom/media/gmp/GMPParent.cpp
-@@ -949,17 +949,17 @@ GMPParent::GetGMPContentParent(UniquePtr
+@@ -946,17 +946,17 @@ GMPParent::GetGMPContentParent(UniquePtr
      }
    }
  }
@@ -929,7 +1155,7 @@ diff --git a/dom/media/ipc/VideoDecoderManagerParent.cpp b/dom/media/ipc/VideoDe
 diff --git a/dom/media/platforms/apple/AppleVTDecoder.cpp b/dom/media/platforms/apple/AppleVTDecoder.cpp
 --- a/dom/media/platforms/apple/AppleVTDecoder.cpp
 +++ b/dom/media/platforms/apple/AppleVTDecoder.cpp
-@@ -252,17 +252,17 @@ AppleVTDecoder::ProcessDrain()
+@@ -248,17 +248,17 @@ AppleVTDecoder::ProcessDrain()
    AssertOnTaskQueueThread();
    nsresult rv = WaitForAsynchronousFrames();
    if (NS_FAILED(rv)) {
@@ -1658,7 +1884,7 @@ diff --git a/gfx/layers/wr/WebRenderCommandBuilder.cpp b/gfx/layers/wr/WebRender
 diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp
 --- a/gfx/thebes/gfxFont.cpp
 +++ b/gfx/thebes/gfxFont.cpp
-@@ -2526,17 +2526,17 @@ gfxFont::Measure(const gfxTextRun *aText
+@@ -2514,17 +2514,17 @@ gfxFont::Measure(const gfxTextRun *aText
  {
      // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
      // and the underlying cairo font may be antialiased,
@@ -1680,7 +1906,7 @@ diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp
 diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp
 --- a/gfx/thebes/gfxPlatform.cpp
 +++ b/gfx/thebes/gfxPlatform.cpp
-@@ -1825,17 +1825,17 @@ gfxPlatform::GetBackendPrefs()
+@@ -1818,17 +1818,17 @@ gfxPlatform::GetBackendPrefs()
    data.mContentBitmask = BackendTypeBit(BackendType::CAIRO);
  #ifdef USE_SKIA
    data.mCanvasBitmask |= BackendTypeBit(BackendType::SKIA);
@@ -1702,7 +1928,7 @@ diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp
 diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp
 --- a/gfx/thebes/gfxPlatformMac.cpp
 +++ b/gfx/thebes/gfxPlatformMac.cpp
-@@ -91,17 +91,17 @@ gfxPlatformMac::GetBackendPrefs()
+@@ -94,17 +94,17 @@ gfxPlatformMac::GetBackendPrefs()
  {
    BackendPrefsData data;
  
@@ -1911,7 +2137,7 @@ diff --git a/image/test/gtest/TestSurfacePipeIntegration.cpp b/image/test/gtest/
 diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
 --- a/js/src/builtin/TestingFunctions.cpp
 +++ b/js/src/builtin/TestingFunctions.cpp
-@@ -3386,17 +3386,17 @@ struct FindPathHandler {
+@@ -3384,17 +3384,17 @@ struct FindPathHandler {
          if (!first)
              return true;
  
@@ -2054,7 +2280,7 @@ diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h
 diff --git a/js/src/vm/CodeCoverage.cpp b/js/src/vm/CodeCoverage.cpp
 --- a/js/src/vm/CodeCoverage.cpp
 +++ b/js/src/vm/CodeCoverage.cpp
-@@ -517,17 +517,17 @@ LCovCompartment::lookupOrAdd(JSCompartme
+@@ -518,17 +518,17 @@ LCovCompartment::lookupOrAdd(JSCompartme
  
      char* source_name = js_strdup(name);
      if (!source_name) {
@@ -2188,7 +2414,7 @@ diff --git a/js/xpconnect/loader/URLPreloader.cpp b/js/xpconnect/loader/URLPrelo
 diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
 --- a/layout/base/PresShell.cpp
 +++ b/layout/base/PresShell.cpp
-@@ -6233,17 +6233,17 @@ PresShell::Paint(nsView*         aViewTo
+@@ -6227,17 +6227,17 @@ PresShell::Paint(nsView*         aViewTo
        bool computeInvalidRect = computeInvalidFunc ||
                                  (layerManager->GetBackendType() == LayersBackend::LAYERS_BASIC);
  
@@ -2752,7 +2978,7 @@ new file mode 100644
 diff --git a/mfbt/tests/TestUniquePtr.cpp b/mfbt/tests/TestUniquePtr.cpp
 --- a/mfbt/tests/TestUniquePtr.cpp
 +++ b/mfbt/tests/TestUniquePtr.cpp
-@@ -74,17 +74,17 @@ ReturnUniqueA()
+@@ -70,17 +70,17 @@ ReturnUniqueA()
  {
    return UniqueA(new B);
  }
@@ -2771,7 +2997,7 @@ diff --git a/mfbt/tests/TestUniquePtr.cpp b/mfbt/tests/TestUniquePtr.cpp
    // Make sure UniquePtr will use its deleter's pointer type if it defines one.
    typedef int* Ptr;
    struct Deleter {
-@@ -367,17 +367,17 @@ SetMallocedInt(UniquePtr<int, FreeSignat
+@@ -363,17 +363,17 @@ SetMallocedInt(UniquePtr<int, FreeSignat
  }
  
  static UniquePtr<int, FreeSignature>

File diff suppressed because it is too large
+ 514 - 64
mozilla-release/patches/1465585-3-std-62a1.patch


Some files were not shown because too many files changed in this diff