Backout rev 509957c7a9f9 (bug 1456986). r=me a=ryanvm
authorBen Kelly <ben@wanderview.com>
Tue, 08 May 2018 07:33:38 -0700
changeset 473222 10812be17bab3794c4f6d97b2128f6a0d8e156ea
parent 473221 cb82afabee422ebabb14e7f44c45ee70343fe987
child 473223 711a838a2065ec76590139d0e950e9569fb0e787
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme, ryanvm
bugs1456986
milestone61.0
Backout rev 509957c7a9f9 (bug 1456986). r=me a=ryanvm
dom/serviceworkers/ServiceWorker.cpp
dom/serviceworkers/ServiceWorker.h
dom/serviceworkers/ServiceWorkerInfo.cpp
dom/serviceworkers/ServiceWorkerInfo.h
dom/serviceworkers/ServiceWorkerPrivate.cpp
dom/serviceworkers/ServiceWorkerPrivate.h
--- a/dom/serviceworkers/ServiceWorker.cpp
+++ b/dom/serviceworkers/ServiceWorker.cpp
@@ -12,17 +12,16 @@
 #include "ServiceWorkerPrivate.h"
 
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/ClientIPCTypes.h"
 #include "mozilla/dom/ClientState.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 #include "mozilla/dom/WorkerPrivate.h"
-#include "mozilla/dom/ipc/StructuredCloneData.h"
 
 #ifdef XP_WIN
 #undef PostMessage
 #endif
 
 using mozilla::ErrorResult;
 using namespace mozilla::dom;
 
@@ -143,52 +142,17 @@ ServiceWorker::PostMessage(JSContext* aC
                            const Sequence<JSObject*>& aTransferable,
                            ErrorResult& aRv)
 {
   if (State() == ServiceWorkerState::Redundant) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  nsPIDOMWindowInner* window = GetOwner();
-  if (NS_WARN_IF(!window || !window->GetExtantDoc())) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-
-  auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
-  if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
-    ServiceWorkerManager::LocalizeAndReportToAllClients(
-      mDescriptor.Scope(), "ServiceWorkerPostMessageStorageError",
-      nsTArray<nsString> { NS_ConvertUTF8toUTF16(mDescriptor.Scope()) });
-    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return;
-  }
-
-  Maybe<ClientInfo> clientInfo = window->GetClientInfo();
-  Maybe<ClientState> clientState = window->GetClientState();
-  if (NS_WARN_IF(clientInfo.isNothing() || clientState.isNothing())) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-
-  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
-  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
-                                                          &transferable);
-  if (aRv.Failed()) {
-    return;
-  }
-
-  ipc::StructuredCloneData data;
-  data.Write(aCx, aMessage, transferable, aRv);
-  if (aRv.Failed()) {
-    return;
-  }
-
-  mInner->PostMessage(Move(data), clientInfo.ref(), clientState.ref());
+  mInner->PostMessage(GetParentObject(), aCx, aMessage, aTransferable, aRv);
 }
 
 
 const ServiceWorkerDescriptor&
 ServiceWorker::Descriptor() const
 {
   return mDescriptor;
 }
--- a/dom/serviceworkers/ServiceWorker.h
+++ b/dom/serviceworkers/ServiceWorker.h
@@ -15,20 +15,16 @@
 #undef PostMessage
 #endif
 
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
-namespace ipc {
-class StructuredCloneData;
-} // namespace ipc
-
 #define NS_DOM_SERVICEWORKER_IID \
   {0xd42e0611, 0x3647, 0x4319, {0xae, 0x05, 0x19, 0x89, 0x59, 0xba, 0x99, 0x5e}}
 
 bool
 ServiceWorkerVisible(JSContext* aCx, JSObject* aObj);
 
 class ServiceWorker final : public DOMEventTargetHelper
 {
@@ -53,19 +49,20 @@ public:
     AddServiceWorker(ServiceWorker* aWorker) = 0;
 
     // This is called when the DOM ServiceWorker object is
     // destroyed and drops its ref to the Inner object.
     virtual void
     RemoveServiceWorker(ServiceWorker* aWorker) = 0;
 
     virtual void
-    PostMessage(ipc::StructuredCloneData&& aData,
-                const ClientInfo& aClientInfo,
-                const ClientState& aClientState) = 0;
+    PostMessage(nsIGlobalObject* aGlobal,
+                JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                const Sequence<JSObject*>& aTransferable,
+                ErrorResult& aRv) = 0;
 
     NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
   };
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_SERVICEWORKER_IID)
   NS_DECL_ISUPPORTS_INHERITED
 
   IMPL_EVENT_HANDLER(statechange)
--- a/dom/serviceworkers/ServiceWorkerInfo.cpp
+++ b/dom/serviceworkers/ServiceWorkerInfo.cpp
@@ -251,23 +251,48 @@ ServiceWorkerInfo::RemoveServiceWorker(S
     workerURL.Equals(NS_ConvertUTF8toUTF16(mDescriptor.ScriptURL())));
 #endif
 
   DebugOnly<bool> removed = mInstances.RemoveElement(aWorker);
   MOZ_ASSERT(removed);
 }
 
 void
-ServiceWorkerInfo::PostMessage(ipc::StructuredCloneData&& aData,
-                               const ClientInfo& aClientInfo,
-                               const ClientState& aClientState)
+ServiceWorkerInfo::PostMessage(nsIGlobalObject* aGlobal,
+                               JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                               const Sequence<JSObject*>& aTransferable,
+                               ErrorResult& aRv)
 {
-  mServiceWorkerPrivate->SendMessageEvent(Move(aData),
-                                          ClientInfoAndState(aClientInfo.ToIPC(),
-                                                             aClientState.ToIPC()));
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
+  if (NS_WARN_IF(!window || !window->GetExtantDoc())) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
+  if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
+    ServiceWorkerManager::LocalizeAndReportToAllClients(
+      Scope(), "ServiceWorkerPostMessageStorageError",
+      nsTArray<nsString> { NS_ConvertUTF8toUTF16(Scope()) });
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  Maybe<ClientInfo> clientInfo = window->GetClientInfo();
+  Maybe<ClientState> clientState = window->GetClientState();
+  if (NS_WARN_IF(clientInfo.isNothing() || clientState.isNothing())) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  aRv = mServiceWorkerPrivate->SendMessageEvent(aCx, aMessage, aTransferable,
+                                                ClientInfoAndState(clientInfo.ref().ToIPC(),
+                                                                   clientState.ref().ToIPC()));
 }
 
 void
 ServiceWorkerInfo::UpdateInstalledTime()
 {
   MOZ_ASSERT(State() == ServiceWorkerState::Installed);
   MOZ_ASSERT(mInstalledTime == 0);
 
--- a/dom/serviceworkers/ServiceWorkerInfo.h
+++ b/dom/serviceworkers/ServiceWorkerInfo.h
@@ -12,17 +12,16 @@
 #include "mozilla/dom/WorkerCommon.h"
 #include "mozilla/OriginAttributes.h"
 #include "nsIServiceWorkerManager.h"
 #include "ServiceWorker.h"
 
 namespace mozilla {
 namespace dom {
 
-class ClientInfoAndState;
 class ServiceWorkerPrivate;
 
 /*
  * Wherever the spec treats a worker instance and a description of said worker
  * as the same thing; i.e. "Resolve foo with
  * _GetNewestWorker(serviceWorkerRegistration)", we represent the description
  * by this class and spawn a ServiceWorker in the right global when required.
  */
@@ -81,19 +80,20 @@ private:
   // ServiceWorker::Inner implementation
   virtual void
   AddServiceWorker(ServiceWorker* aWorker) override;
 
   virtual void
   RemoveServiceWorker(ServiceWorker* aWorker) override;
 
   virtual void
-  PostMessage(ipc::StructuredCloneData&& aData,
-              const ClientInfo& aClientInfo,
-              const ClientState& aClientState) override;
+  PostMessage(nsIGlobalObject* aGlobal,
+              JSContext* aCx, JS::Handle<JS::Value> aMessage,
+              const Sequence<JSObject*>& aTransferable,
+              ErrorResult& aRv) override;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVICEWORKERINFO
 
   class ServiceWorkerPrivate*
   WorkerPrivate() const
   {
--- a/dom/serviceworkers/ServiceWorkerPrivate.cpp
+++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp
@@ -18,33 +18,30 @@
 #include "nsITimedChannel.h"
 #include "nsIUploadChannel2.h"
 #include "nsNetUtil.h"
 #include "nsProxyRelease.h"
 #include "nsQueryObject.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 #include "mozilla/Assertions.h"
-#include "mozilla/JSObjectHolder.h"
 #include "mozilla/dom/Client.h"
 #include "mozilla/dom/ClientIPCTypes.h"
 #include "mozilla/dom/DOMPrefs.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/dom/StructuredCloneHolder.h"
 #include "mozilla/dom/WorkerDebugger.h"
 #include "mozilla/dom/WorkerRef.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/dom/WorkerScope.h"
-#include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/Unused.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace mozilla {
 namespace dom {
 
@@ -210,34 +207,16 @@ ServiceWorkerPrivate::CheckScriptEvaluat
                                                                    aScriptEvaluationCallback);
   if (NS_WARN_IF(!r->Dispatch())) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
-JSObject*
-ServiceWorkerPrivate::GetOrCreateSandbox(JSContext* aCx)
-{
-  AssertIsOnMainThread();
-
-  if (!mSandbox) {
-    nsIXPConnect* xpc = nsContentUtils::XPConnect();
-
-    JS::Rooted<JSObject*> sandbox(aCx);
-    nsresult rv = xpc->CreateSandbox(aCx, mInfo->Principal(), sandbox.address());
-    NS_ENSURE_SUCCESS(rv, nullptr);
-
-    mSandbox = new JSObjectHolder(aCx, sandbox);
-  }
-
-  return mSandbox->GetJSObject();
-}
-
 namespace {
 
 enum ExtendableEventResult {
     Rejected = 0,
     Resolved
 };
 
 class ExtendableEventCallback {
@@ -516,45 +495,45 @@ public:
       return NS_ERROR_XPC_JS_THREW_EXCEPTION;
     }
 
     return NS_OK;
   }
 };
 
 class SendMessageEventRunnable final : public ExtendableEventWorkerRunnable
+                                     , public StructuredCloneHolder
 {
-  StructuredCloneHolder mData;
   const ClientInfoAndState mClientInfoAndState;
 
 public:
   SendMessageEventRunnable(WorkerPrivate*  aWorkerPrivate,
                            KeepAliveToken* aKeepAliveToken,
-                           StructuredCloneHolder&& aData,
                            const ClientInfoAndState& aClientInfoAndState)
     : ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken)
-    , mData(Move(aData))
+    , StructuredCloneHolder(CloningSupported, TransferringSupported,
+                            StructuredCloneScope::SameProcessDifferentThread)
     , mClientInfoAndState(aClientInfoAndState)
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     JS::Rooted<JS::Value> messageData(aCx);
     nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope();
     ErrorResult rv;
-    mData.Read(sgo, aCx, &messageData, rv);
+    Read(sgo, aCx, &messageData, rv);
     if (NS_WARN_IF(rv.Failed())) {
       return true;
     }
 
     Sequence<OwningNonNull<MessagePort>> ports;
-    if (!mData.TakeTransferredPortsAsSequence(ports)) {
+    if (!TakeTransferredPortsAsSequence(ports)) {
       return true;
     }
 
     RootedDictionary<ExtendableMessageEventInit> init(aCx);
 
     init.mBubbles = false;
     init.mCancelable = false;
 
@@ -579,92 +558,45 @@ public:
                                                              extendableEvent,
                                                              nullptr));
   }
 };
 
 } // anonymous namespace
 
 nsresult
-ServiceWorkerPrivate::SendMessageEvent(ipc::StructuredCloneData&& aData,
+ServiceWorkerPrivate::SendMessageEvent(JSContext* aCx,
+                                       JS::Handle<JS::Value> aMessage,
+                                       const Sequence<JSObject*>& aTransferable,
                                        const ClientInfoAndState& aClientInfoAndState)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  ErrorResult rv;
-
-  // Ideally we would simply move the StructuredCloneData to the
-  // SendMessageEventRunnable, but we cannot because it uses non-threadsafe
-  // ref-counting.  The following gnarly code unpacks the IPC-friendly
-  // StructuredCloneData and re-packs it into the thread-friendly
-  // StructuredCloneHolder.  In the future we should remove this and make
-  // it easier to simple move the data to the other thread.  See bug 1458936.
-
-  AutoSafeJSContext cx;
-  JSObject* sandbox = GetOrCreateSandbox(cx);
-  NS_ENSURE_TRUE(sandbox, NS_ERROR_FAILURE);
-
-  JS::Rooted<JSObject*> global(cx, sandbox);
-  NS_ENSURE_TRUE(sandbox, NS_ERROR_FAILURE);
-
-  // The CreateSandbox call returns a proxy to the actual sandbox object.  We
-  // don't need a proxy here.
-  global = js::UncheckedUnwrap(global);
-
-  JSAutoCompartment ac(cx, global);
-
-  JS::Rooted<JS::Value> messageData(cx);
-  aData.Read(cx, &messageData, rv);
-  if (rv.Failed()) {
+  ErrorResult rv(SpawnWorkerIfNeeded(MessageEvent));
+  if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
-  Sequence<OwningNonNull<MessagePort>> ports;
-  if (!aData.TakeTransferredPortsAsSequence(ports)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, ports.Length()));
-  NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
-
-  for (uint32_t i = 0; i < ports.Length(); ++i) {
-    JS::Rooted<JS::Value> value(cx);
-    if (!GetOrCreateDOMReflector(cx, ports[i], &value)) {
-      JS_ClearPendingException(cx);
-      return NS_ERROR_FAILURE;
-    }
+  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedHandleValue);
 
-    if (!JS_DefineElement(cx, array, i, value, JSPROP_ENUMERATE)) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  JS::Rooted<JS::Value> transferable(cx);
-  transferable.setObject(*array);
-
-  StructuredCloneHolder holder(StructuredCloneHolder::CloningSupported,
-                               StructuredCloneHolder::TransferringSupported,
-                               JS::StructuredCloneScope::SameProcessDifferentThread);
-  holder.Write(cx, messageData, transferable, JS::CloneDataPolicy(), rv);
-  if (rv.Failed()) {
-    return rv.StealNSResult();
-  }
-
-  // Now that the re-packing is complete, send a runnable to the service worker
-  // thread.
-
-  rv = SpawnWorkerIfNeeded(MessageEvent);
+  rv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
+                                                         &transferable);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
   RefPtr<SendMessageEventRunnable> runnable =
-    new SendMessageEventRunnable(mWorkerPrivate, token, Move(holder),
-                                 aClientInfoAndState);
+    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;
   }
 
   return NS_OK;
 }
 
 namespace {
--- a/dom/serviceworkers/ServiceWorkerPrivate.h
+++ b/dom/serviceworkers/ServiceWorkerPrivate.h
@@ -11,30 +11,23 @@
 #include "mozilla/dom/WorkerPrivate.h"
 
 #define NOTIFICATION_CLICK_EVENT_NAME "notificationclick"
 #define NOTIFICATION_CLOSE_EVENT_NAME "notificationclose"
 
 class nsIInterceptedChannel;
 
 namespace mozilla {
-
-class JSObjectHolder;
-
 namespace dom {
 
 class ClientInfoAndState;
 class KeepAliveToken;
 class ServiceWorkerInfo;
 class ServiceWorkerRegistrationInfo;
 
-namespace ipc {
-class StructuredCloneData;
-} // namespace ipc
-
 class LifeCycleEventCallback : public Runnable
 {
 public:
   LifeCycleEventCallback() : Runnable("dom::LifeCycleEventCallback") {}
 
   // Called on the worker thread.
   virtual void
   SetResult(bool aResult) = 0;
@@ -84,17 +77,18 @@ public:
 protected:
   nsCycleCollectingAutoRefCnt mRefCnt;
   NS_DECL_OWNINGTHREAD
 
 public:
   explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo);
 
   nsresult
-  SendMessageEvent(ipc::StructuredCloneData&& aData,
+  SendMessageEvent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                   const Sequence<JSObject*>& aTransferable,
                    const ClientInfoAndState& aClientInfoAndState);
 
   // This is used to validate the worker script and continue the installation
   // process.
   nsresult
   CheckScriptEvaluation(LifeCycleEventCallback* aCallback);
 
   nsresult
@@ -200,40 +194,32 @@ private:
                       bool* aNewWorkerCreated = nullptr,
                       nsILoadGroup* aLoadGroup = nullptr);
 
   ~ServiceWorkerPrivate();
 
   already_AddRefed<KeepAliveToken>
   CreateEventKeepAliveToken();
 
-  JSObject*
-  GetOrCreateSandbox(JSContext* aCx);
-
   // The info object owns us. It is possible to outlive it for a brief period
   // of time if there are pending waitUntil promises, in which case it
   // will be null and |SpawnWorkerIfNeeded| will always fail.
   ServiceWorkerInfo* MOZ_NON_OWNING_REF mInfo;
 
   // The WorkerPrivate object can only be closed by this class or by the
   // RuntimeService class if gecko is shutting down. Closing the worker
   // multiple times is OK, since the second attempt will be a no-op.
   RefPtr<WorkerPrivate> mWorkerPrivate;
 
   nsCOMPtr<nsITimer> mIdleWorkerTimer;
 
   // We keep a token for |dom.serviceWorkers.idle_timeout| seconds to give the
   // worker a grace period after each event.
   RefPtr<KeepAliveToken> mIdleKeepAliveToken;
 
-  // Sandbox global used to re-pack structured clone data before sending
-  // to the service worker thread.  Ideally we would remove this and just
-  // make StructuredCloneData thread safe enough to pass to the worker thread.
-  RefPtr<JSObjectHolder> mSandbox;
-
   uint64_t mDebuggerCount;
 
   uint64_t mTokenCount;
 
   // Meant for keeping objects alive while handling requests from the worker
   // on the main thread. Access to this array is provided through
   // |StoreISupports| and |RemoveISupports|. Note that the array is also
   // cleared whenever the worker is terminated.