Bug 1434342 P5 Support caching the ServiceWorker DOM instance on the global. r=asuth
authorBen Kelly <ben@wanderview.com>
Wed, 31 Jan 2018 09:10:26 -0800
changeset 401847 edfc00999a3afac47595e1389f35d8b7903739b6
parent 401846 1cf205e9e3b09713928d85d05c7f13c2f070bda3
child 401848 6b5ed759f753d1f204be3826ab56bb33bb2d2611
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 P5 Support caching the ServiceWorker DOM instance on the global. r=asuth
dom/base/nsGlobalWindowInner.cpp
dom/base/nsGlobalWindowInner.h
dom/base/nsIGlobalObject.cpp
dom/base/nsIGlobalObject.h
dom/base/nsPIDOMWindow.h
dom/serviceworkers/ServiceWorker.cpp
dom/serviceworkers/ServiceWorker.h
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -241,16 +241,17 @@
 #include "mozilla/dom/PrimitiveConversions.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
+#include "mozilla/dom/ServiceWorker.h"
 #include "mozilla/dom/ServiceWorkerRegistration.h"
 #include "mozilla/dom/U2F.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/Worklet.h"
 #ifdef HAVE_SIDEBAR
 #include "mozilla/dom/ExternalBinding.h"
 #endif
 
@@ -2390,16 +2391,22 @@ nsPIDOMWindowInner::GetClientState() con
 }
 
 Maybe<ServiceWorkerDescriptor>
 nsPIDOMWindowInner::GetController() const
 {
   return Move(nsGlobalWindowInner::Cast(this)->GetController());
 }
 
+RefPtr<mozilla::dom::ServiceWorker>
+nsPIDOMWindowInner::GetOrCreateServiceWorker(const mozilla::dom::ServiceWorkerDescriptor& aDescriptor)
+{
+  return Move(nsGlobalWindowInner::Cast(this)->GetOrCreateServiceWorker(aDescriptor));
+}
+
 void
 nsPIDOMWindowInner::NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope)
 {
   nsGlobalWindowInner::Cast(this)->NoteCalledRegisterForServiceWorkerScope(aScope);
 }
 
 bool
 nsGlobalWindowInner::ShouldReportForServiceWorkerScope(const nsAString& aScope)
@@ -6366,16 +6373,49 @@ nsGlobalWindowInner::GetController() con
   MOZ_ASSERT(NS_IsMainThread());
   Maybe<ServiceWorkerDescriptor> controller;
   if (mClientSource) {
     controller = mClientSource->GetController();
   }
   return Move(controller);
 }
 
+RefPtr<ServiceWorker>
+nsGlobalWindowInner::GetOrCreateServiceWorker(const ServiceWorkerDescriptor& aDescriptor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  RefPtr<ServiceWorker> ref;
+  for (auto sw : mServiceWorkerList) {
+    if (sw->MatchesDescriptor(aDescriptor)) {
+      ref = sw;
+      return ref.forget();
+    }
+  }
+  ref = ServiceWorker::Create(this, aDescriptor);
+  return ref.forget();
+}
+
+void
+nsGlobalWindowInner::AddServiceWorker(ServiceWorker* aServiceWorker)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_DIAGNOSTIC_ASSERT(aServiceWorker);
+  MOZ_ASSERT(!mServiceWorkerList.Contains(aServiceWorker));
+  mServiceWorkerList.AppendElement(aServiceWorker);
+}
+
+void
+nsGlobalWindowInner::RemoveServiceWorker(ServiceWorker* aServiceWorker)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_DIAGNOSTIC_ASSERT(aServiceWorker);
+  MOZ_ASSERT(mServiceWorkerList.Contains(aServiceWorker));
+  mServiceWorkerList.RemoveElement(aServiceWorker);
+}
+
 nsresult
 nsGlobalWindowInner::FireDelayedDOMEvents()
 {
   if (mApplicationCache) {
     static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
   }
 
   // Fires an offline status event if the offline status has changed
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -346,16 +346,25 @@ public:
   void Thaw();
   virtual bool IsFrozen() const override;
   void SyncStateFromParentWindow();
 
   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const override;
   mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
   mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const override;
 
+  virtual RefPtr<mozilla::dom::ServiceWorker>
+  GetOrCreateServiceWorker(const mozilla::dom::ServiceWorkerDescriptor& aDescriptor) override;
+
+  virtual void
+  AddServiceWorker(mozilla::dom::ServiceWorker* aServiceWorker) override;
+
+  virtual void
+  RemoveServiceWorker(mozilla::dom::ServiceWorker* aServiceWorker) override;
+
   void NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope);
 
   virtual nsresult FireDelayedDOMEvents() override;
 
   virtual nsresult SetNewDocument(nsIDocument *aDocument,
                                   nsISupports *aState,
                                   bool aForceReuseInnerWindow) override;
 
@@ -1437,16 +1446,20 @@ protected:
   RefPtr<mozilla::dom::VREventObserver> mVREventObserver;
 
   int64_t mBeforeUnloadListenerCount;
 
   RefPtr<mozilla::dom::IntlUtils> mIntlUtils;
 
   mozilla::UniquePtr<mozilla::dom::ClientSource> mClientSource;
 
+  // Weak references added by AddServiceWorker() and cleared by
+  // RemoveServiceWorker() when the ServiceWorker is destroyed.
+  nsTArray<mozilla::dom::ServiceWorker*> mServiceWorkerList;
+
   nsTArray<RefPtr<mozilla::dom::Promise>> mPendingPromises;
 
   static InnerWindowByIdTable* sInnerWindowsById;
 
   // Members in the mChromeFields member should only be used in chrome windows.
   // All accesses to this field should be guarded by a check of mIsChrome.
   struct ChromeFields {
     ChromeFields()
--- a/dom/base/nsIGlobalObject.cpp
+++ b/dom/base/nsIGlobalObject.cpp
@@ -1,21 +1,24 @@
 /* -*- 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 "nsIGlobalObject.h"
+
+#include "mozilla/dom/ServiceWorker.h"
 #include "nsContentUtils.h"
 #include "nsThreadUtils.h"
 #include "nsHostObjectProtocolHandler.h"
 
 using mozilla::Maybe;
 using mozilla::dom::ClientInfo;
+using mozilla::dom::ServiceWorker;
 using mozilla::dom::ServiceWorkerDescriptor;
 
 nsIGlobalObject::~nsIGlobalObject()
 {
   UnlinkHostObjectURIs();
 }
 
 nsIPrincipal*
@@ -127,8 +130,27 @@ nsIGlobalObject::GetClientInfo() const
 
 Maybe<ServiceWorkerDescriptor>
 nsIGlobalObject::GetController() const
 {
   // By default globals do not have a service worker controller.  Only real
   // window and worker globals can currently be controlled as a client.
   return Maybe<ServiceWorkerDescriptor>();
 }
+
+RefPtr<ServiceWorker>
+nsIGlobalObject::GetOrCreateServiceWorker(const ServiceWorkerDescriptor& aDescriptor)
+{
+  MOZ_DIAGNOSTIC_ASSERT(false, "this global should not have any service workers");
+  return nullptr;
+}
+
+void
+nsIGlobalObject::AddServiceWorker(ServiceWorker* aServiceWorker)
+{
+  MOZ_DIAGNOSTIC_ASSERT(false, "this global should not have any service workers");
+}
+
+void
+nsIGlobalObject::RemoveServiceWorker(ServiceWorker* aServiceWorker)
+{
+  MOZ_DIAGNOSTIC_ASSERT(false, "this global should not have any service workers");
+}
--- a/dom/base/nsIGlobalObject.h
+++ b/dom/base/nsIGlobalObject.h
@@ -19,16 +19,22 @@
 // Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
 #define NS_IGLOBALOBJECT_IID \
 { 0x11afa8be, 0xd997, 0x4e07, \
 { 0xa6, 0xa3, 0x6f, 0x87, 0x2e, 0xc3, 0xee, 0x7f } }
 
 class nsCycleCollectionTraversalCallback;
 class nsIPrincipal;
 
+namespace mozilla {
+namespace dom {
+class ServiceWorker;
+} // namespace dom
+} // namespace mozilla
+
 class nsIGlobalObject : public nsISupports,
                         public mozilla::dom::DispatcherTrait
 {
   nsTArray<nsCString> mHostObjectURIs;
   bool mIsDying;
 
 protected:
   nsIGlobalObject()
@@ -81,16 +87,31 @@ public:
   virtual bool IsInSyncOperation() { return false; }
 
   virtual mozilla::Maybe<mozilla::dom::ClientInfo>
   GetClientInfo() const;
 
   virtual mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor>
   GetController() const;
 
+  // Get the DOM object for the given descriptor or attempt to create one.
+  // Creation can still fail and return nullptr during shutdown, etc.
+  virtual RefPtr<mozilla::dom::ServiceWorker>
+  GetOrCreateServiceWorker(const mozilla::dom::ServiceWorkerDescriptor& aDescriptor);
+
+  // These methods allow the ServiceWorker instances to note their existence
+  // so that the global can use weak references to them.  The global should
+  // not hold a strong reference to the ServiceWorker.
+  virtual void
+  AddServiceWorker(mozilla::dom::ServiceWorker* aServiceWorker);
+
+  // This method must be called by the ServiceWorker before it is destroyed.
+  virtual void
+  RemoveServiceWorker(mozilla::dom::ServiceWorker* aServiceWorker);
+
 protected:
   virtual ~nsIGlobalObject();
 
   void
   StartDying()
   {
     mIsDying = true;
   }
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -48,16 +48,17 @@ namespace dom {
 class AudioContext;
 class ClientInfo;
 class ClientState;
 class DocGroup;
 class TabGroup;
 class Element;
 class Navigator;
 class Performance;
+class ServiceWorker;
 class ServiceWorkerDescriptor;
 class ServiceWorkerRegistration;
 class Timeout;
 class TimeoutManager;
 class CustomElementRegistry;
 enum class CallerType : uint32_t;
 } // namespace dom
 } // namespace mozilla
@@ -329,16 +330,19 @@ 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;
 
+  RefPtr<mozilla::dom::ServiceWorker>
+  GetOrCreateServiceWorker(const mozilla::dom::ServiceWorkerDescriptor& aDescriptor);
+
   void NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope);
 
   mozilla::dom::TabGroup* TabGroup();
 
   virtual nsPIDOMWindowOuter* GetPrivateRoot() = 0;
 
   virtual mozilla::dom::CustomElementRegistry* CustomElements() = 0;
 
--- a/dom/serviceworkers/ServiceWorker.cpp
+++ b/dom/serviceworkers/ServiceWorker.cpp
@@ -129,10 +129,21 @@ ServiceWorker::PostMessage(JSContext* aC
   if (State() == ServiceWorkerState::Redundant) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   mInner->PostMessage(GetParentObject(), aCx, aMessage, aTransferable, aRv);
 }
 
+bool
+ServiceWorker::MatchesDescriptor(const ServiceWorkerDescriptor& aDescriptor) const
+{
+  // Compare everything in the descriptor except the state.  That is mutable
+  // and may not exactly match.
+  return mDescriptor.PrincipalInfo() == aDescriptor.PrincipalInfo() &&
+         mDescriptor.Scope() == aDescriptor.Scope() &&
+         mDescriptor.ScriptURL() == aDescriptor.ScriptURL() &&
+         mDescriptor.Id() == aDescriptor.Id();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/serviceworkers/ServiceWorker.h
+++ b/dom/serviceworkers/ServiceWorker.h
@@ -78,16 +78,19 @@ public:
 
   void
   GetScriptURL(nsString& aURL) const;
 
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Sequence<JSObject*>& aTransferable, ErrorResult& aRv);
 
+  bool
+  MatchesDescriptor(const ServiceWorkerDescriptor& aDescriptor) const;
+
 private:
   ServiceWorker(nsIGlobalObject* aWindow,
                 const ServiceWorkerDescriptor& aDescriptor,
                 Inner* aInner);
 
   // This class is reference-counted and will be destroyed from Release().
   ~ServiceWorker();