Bug 1434342 P4 Make ServiceWorker operate on an abstract Inner interface that ServiceWorkerInfo implements. r=asuth
authorBen Kelly <ben@wanderview.com>
Wed, 31 Jan 2018 09:10:26 -0800
changeset 401846 1cf205e9e3b09713928d85d05c7f13c2f070bda3
parent 401845 6444014a8f9044938eb6a5bece8697b3b6614c40
child 401847 edfc00999a3afac47595e1389f35d8b7903739b6
push id33357
push usershindli@mozilla.com
push dateWed, 31 Jan 2018 22:32:55 +0000
treeherdermozilla-central@c783229694e5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1434342
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1434342 P4 Make ServiceWorker operate on an abstract Inner interface that ServiceWorkerInfo implements. r=asuth
dom/serviceworkers/ServiceWorker.cpp
dom/serviceworkers/ServiceWorker.h
dom/serviceworkers/ServiceWorkerInfo.cpp
dom/serviceworkers/ServiceWorkerInfo.h
--- a/dom/serviceworkers/ServiceWorker.cpp
+++ b/dom/serviceworkers/ServiceWorker.cpp
@@ -61,32 +61,33 @@ ServiceWorker::Create(nsIGlobalObject* a
     return ref.forget();
   }
 
   ref = new ServiceWorker(aOwner, aDescriptor, info);
   return ref.forget();
 }
 
 ServiceWorker::ServiceWorker(nsIGlobalObject* aGlobal,
-                             ServiceWorkerInfo* aInfo)
+                             const ServiceWorkerDescriptor& aDescriptor,
+                             ServiceWorker::Inner* aInner)
   : DOMEventTargetHelper(aGlobal)
   , mDescriptor(aDescriptor)
-  , mInfo(aInfo)
+  , mInner(aInner)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aInfo);
+  MOZ_DIAGNOSTIC_ASSERT(mInner);
 
   // This will update our state too.
-  mInfo->AppendWorker(this);
+  mInner->AddServiceWorker(this);
 }
 
 ServiceWorker::~ServiceWorker()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  mInfo->RemoveWorker(this);
+  mInner->RemoveServiceWorker(this);
 }
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorker, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(ServiceWorker, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorker)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
@@ -125,39 +126,13 @@ ServiceWorker::PostMessage(JSContext* aC
                            const Sequence<JSObject*>& aTransferable,
                            ErrorResult& aRv)
 {
   if (State() == ServiceWorkerState::Redundant) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetParentObject());
-  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;
-  }
-
-  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,
-                                        ClientInfoAndState(clientInfo.ref().ToIPC(),
-                                                           clientState.ref().ToIPC()));
+  mInner->PostMessage(GetParentObject(), aCx, aMessage, aTransferable, aRv);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/serviceworkers/ServiceWorker.h
+++ b/dom/serviceworkers/ServiceWorker.h
@@ -4,34 +4,66 @@
  * 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_serviceworker_h__
 #define mozilla_dom_serviceworker_h__
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/BindingDeclarations.h"
-#include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState.
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 
+#ifdef XP_WIN
+#undef PostMessage
+#endif
+
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
-class ServiceWorkerInfo;
-class ServiceWorkerManager;
-class SharedWorker;
-
 bool
 ServiceWorkerVisible(JSContext* aCx, JSObject* aObj);
 
 class ServiceWorker final : public DOMEventTargetHelper
 {
 public:
+  // Abstract interface for the internal representation of the
+  // ServiceWorker object.
+  class Inner
+  {
+  public:
+    // This will be called when a DOM ServiceWorker object is
+    // created and takes a strong ref to the Inner object.
+    // RemoveServiceWorker() is guaranteed to be called on the
+    // current thread before the ServiceWorker is destroyed.
+    //
+    // In addition, the Inner object should check to see if
+    // the ServiceWorker's state is correct.  If not, it should
+    // be updated automatically by calling SetState().  This is
+    // necessary to handle race conditions where the DOM
+    // ServiceWorker object is created while the state is being
+    // updated in another process.
+    virtual void
+    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(nsIGlobalObject* aGlobal,
+                JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                const Sequence<JSObject*>& aTransferable,
+                ErrorResult& aRv) = 0;
+
+    NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+  };
+
   NS_DECL_ISUPPORTS_INHERITED
 
   IMPL_EVENT_HANDLER(statechange)
   IMPL_EVENT_HANDLER(error)
 
   static already_AddRefed<ServiceWorker>
   Create(nsIGlobalObject* aOwner, const ServiceWorkerDescriptor& aDescriptor);
 
@@ -42,32 +74,28 @@ public:
   State() const;
 
   void
   SetState(ServiceWorkerState aState);
 
   void
   GetScriptURL(nsString& aURL) const;
 
-#ifdef XP_WIN
-#undef PostMessage
-#endif
-
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Sequence<JSObject*>& aTransferable, ErrorResult& aRv);
 
 private:
   ServiceWorker(nsIGlobalObject* aWindow,
                 const ServiceWorkerDescriptor& aDescriptor,
-                ServiceWorkerInfo* aInfo);
+                Inner* aInner);
 
   // This class is reference-counted and will be destroyed from Release().
   ~ServiceWorker();
 
   ServiceWorkerDescriptor mDescriptor;
-  const RefPtr<ServiceWorkerInfo> mInfo;
+  const RefPtr<Inner> mInner;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_serviceworker_h__
--- a/dom/serviceworkers/ServiceWorkerInfo.cpp
+++ b/dom/serviceworkers/ServiceWorkerInfo.cpp
@@ -108,45 +108,16 @@ ServiceWorkerInfo::AttachDebugger()
 }
 
 NS_IMETHODIMP
 ServiceWorkerInfo::DetachDebugger()
 {
   return mServiceWorkerPrivate->DetachDebugger();
 }
 
-void
-ServiceWorkerInfo::AppendWorker(ServiceWorker* aWorker)
-{
-  MOZ_ASSERT(aWorker);
-#ifdef DEBUG
-  nsAutoString workerURL;
-  aWorker->GetScriptURL(workerURL);
-  MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mDescriptor.ScriptURL())));
-#endif
-  MOZ_ASSERT(!mInstances.Contains(aWorker));
-
-  mInstances.AppendElement(aWorker);
-  aWorker->SetState(State());
-}
-
-void
-ServiceWorkerInfo::RemoveWorker(ServiceWorker* aWorker)
-{
-  MOZ_ASSERT(aWorker);
-#ifdef DEBUG
-  nsAutoString workerURL;
-  aWorker->GetScriptURL(workerURL);
-  MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mDescriptor.ScriptURL())));
-#endif
-  MOZ_ASSERT(mInstances.Contains(aWorker));
-
-  mInstances.RemoveElement(aWorker);
-}
-
 namespace {
 
 class ChangeStateUpdater final : public Runnable
 {
 public:
   ChangeStateUpdater(const nsTArray<ServiceWorker*>& aInstances,
                      ServiceWorkerState aState)
     : Runnable("dom::ChangeStateUpdater")
@@ -248,16 +219,82 @@ ServiceWorkerInfo::~ServiceWorkerInfo()
 static uint64_t gServiceWorkerInfoCurrentID = 0;
 
 uint64_t
 ServiceWorkerInfo::GetNextID() const
 {
   return ++gServiceWorkerInfoCurrentID;
 }
 
+void
+ServiceWorkerInfo::AddServiceWorker(ServiceWorker* aWorker)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aWorker);
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+  nsAutoString workerURL;
+  aWorker->GetScriptURL(workerURL);
+  MOZ_DIAGNOSTIC_ASSERT(
+    workerURL.Equals(NS_ConvertUTF8toUTF16(mDescriptor.ScriptURL())));
+#endif
+  MOZ_ASSERT(!mInstances.Contains(aWorker));
+
+  mInstances.AppendElement(aWorker);
+  aWorker->SetState(State());
+}
+
+void
+ServiceWorkerInfo::RemoveServiceWorker(ServiceWorker* aWorker)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aWorker);
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+  nsAutoString workerURL;
+  aWorker->GetScriptURL(workerURL);
+  MOZ_DIAGNOSTIC_ASSERT(
+    workerURL.Equals(NS_ConvertUTF8toUTF16(mDescriptor.ScriptURL())));
+#endif
+  MOZ_ASSERT(mInstances.Contains(aWorker));
+
+  mInstances.RemoveElement(aWorker);
+}
+
+void
+ServiceWorkerInfo::PostMessage(nsIGlobalObject* aGlobal,
+                               JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                               const Sequence<JSObject*>& aTransferable,
+                               ErrorResult& aRv)
+{
+  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()));
+}
+
 already_AddRefed<ServiceWorker>
 ServiceWorkerInfo::GetOrCreateInstance(nsPIDOMWindowInner* aWindow)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
 
   RefPtr<ServiceWorker> ref;
 
--- a/dom/serviceworkers/ServiceWorkerInfo.h
+++ b/dom/serviceworkers/ServiceWorkerInfo.h
@@ -7,21 +7,21 @@
 #ifndef mozilla_dom_serviceworkerinfo_h
 #define mozilla_dom_serviceworkerinfo_h
 
 #include "MainThreadUtils.h"
 #include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState
 #include "mozilla/dom/WorkerCommon.h"
 #include "mozilla/OriginAttributes.h"
 #include "nsIServiceWorkerManager.h"
+#include "ServiceWorker.h"
 
 namespace mozilla {
 namespace dom {
 
-class ServiceWorker;
 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.
  */
@@ -70,16 +70,29 @@ private:
 
   ~ServiceWorkerInfo();
 
   // Generates a unique id for the service worker, with zero being treated as
   // invalid.
   uint64_t
   GetNextID() const;
 
+  // ServiceWorker::Inner implementation
+  virtual void
+  AddServiceWorker(ServiceWorker* aWorker) override;
+
+  virtual void
+  RemoveServiceWorker(ServiceWorker* aWorker) override;
+
+  virtual void
+  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
   {
     MOZ_ASSERT(mServiceWorkerPrivate);
@@ -180,22 +193,16 @@ public:
   bool
   HandlesFetch() const
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_DIAGNOSTIC_ASSERT(mHandlesFetch != Unknown);
     return mHandlesFetch != Disabled;
   }
 
-  void
-  AppendWorker(ServiceWorker* aWorker);
-
-  void
-  RemoveWorker(ServiceWorker* aWorker);
-
   already_AddRefed<ServiceWorker>
   GetOrCreateInstance(nsPIDOMWindowInner* aWindow);
 
   void
   UpdateInstalledTime();
 
   void
   UpdateActivatedTime();