Bug 1269154 - Get rid of WorkerFeature: WorkerHolder, r=khuey
☠☠ backed out by e6da60a09547 ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 22 Jun 2016 17:24:35 +0200
changeset 302420 1c5d78c7ba432f923296cf6d664e345782cb67b1
parent 302419 fd26cef5ff88205d6cb3896996c45f36ec305d8b
child 302421 0c7c1f35ab4cbc5a4d3313d7daf085fba2c133e0
push id30360
push usercbook@mozilla.com
push dateThu, 23 Jun 2016 12:57:36 +0000
treeherdermozilla-central@d1102663db10 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1269154
milestone50.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 1269154 - Get rid of WorkerFeature: WorkerHolder, r=khuey
dom/base/FileReader.cpp
dom/base/FileReader.h
dom/base/WebSocket.cpp
dom/bindings/BindingUtils.cpp
dom/broadcastchannel/BroadcastChannel.cpp
dom/broadcastchannel/BroadcastChannel.h
dom/cache/ActorChild.cpp
dom/cache/ActorChild.h
dom/cache/Cache.cpp
dom/cache/CacheChild.cpp
dom/cache/CacheChild.h
dom/cache/CacheOpChild.cpp
dom/cache/CacheOpChild.h
dom/cache/CacheStorage.cpp
dom/cache/CacheStorage.h
dom/cache/CacheStorageChild.cpp
dom/cache/CacheStorageChild.h
dom/cache/CacheStreamControlChild.cpp
dom/cache/CacheWorkerHolder.cpp
dom/cache/CacheWorkerHolder.h
dom/cache/Connection.h
dom/cache/Feature.cpp
dom/cache/Feature.h
dom/cache/moz.build
dom/canvas/WebGLContextLossHandler.cpp
dom/canvas/WebGLContextLossHandler.h
dom/console/Console.cpp
dom/fetch/Fetch.cpp
dom/fetch/Fetch.h
dom/indexedDB/ActorsChild.cpp
dom/indexedDB/IDBRequest.cpp
dom/indexedDB/IDBRequest.h
dom/indexedDB/IDBTransaction.cpp
dom/indexedDB/IDBTransaction.h
dom/messagechannel/MessagePort.cpp
dom/messagechannel/MessagePort.h
dom/notification/Notification.cpp
dom/notification/Notification.h
dom/promise/Promise.cpp
dom/promise/Promise.h
dom/promise/PromiseWorkerProxy.h
dom/workers/ScriptLoader.cpp
dom/workers/ServiceWorkerPrivate.cpp
dom/workers/ServiceWorkerRegistration.cpp
dom/workers/ServiceWorkerRegistration.h
dom/workers/WorkerFeature.h
dom/workers/WorkerHolder.cpp
dom/workers/WorkerHolder.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/XMLHttpRequest.cpp
dom/workers/XMLHttpRequest.h
dom/workers/moz.build
dom/workers/test/bug1020226_worker.js
ipc/glue/SendStreamChild.cpp
--- a/dom/base/FileReader.cpp
+++ b/dom/base/FileReader.cpp
@@ -599,17 +599,17 @@ NS_IMETHODIMP
 FileReader::OnInputStreamReady(nsIAsyncInputStream* aStream)
 {
   if (mReadyState != LOADING || aStream != mAsyncStream) {
     return NS_OK;
   }
 
   // We use this class to decrease the busy counter at the end of this method.
   // In theory we can do it immediatelly but, for debugging reasons, we want to
-  // be 100% sure we have a feature when OnLoadEnd() is called.
+  // be 100% sure we have a workerHolder when OnLoadEnd() is called.
   FileReaderDecreaseBusyCounter RAII(this);
 
   uint64_t aCount;
   nsresult rv = aStream->Available(&aCount);
 
   if (NS_SUCCEEDED(rv) && aCount) {
     rv = DoReadData(aCount);
   }
@@ -696,29 +696,29 @@ FileReader::Abort(ErrorResult& aRv)
   DispatchProgressEvent(NS_LITERAL_STRING(ABORT_STR));
   DispatchProgressEvent(NS_LITERAL_STRING(LOADEND_STR));
 }
 
 nsresult
 FileReader::IncreaseBusyCounter()
 {
   if (mWorkerPrivate && mBusyCount++ == 0 &&
-      !mWorkerPrivate->AddFeature(this)) {
+      !HoldWorker(mWorkerPrivate)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 void
 FileReader::DecreaseBusyCounter()
 {
   MOZ_ASSERT_IF(mWorkerPrivate, mBusyCount);
   if (mWorkerPrivate && --mBusyCount == 0) {
-    mWorkerPrivate->RemoveFeature(this);
+    ReleaseWorker();
   }
 }
 
 bool
 FileReader::Notify(Status aStatus)
 {
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
@@ -737,16 +737,16 @@ FileReader::Shutdown()
   mResultArrayBuffer = nullptr;
 
   if (mAsyncStream) {
     mAsyncStream->Close();
     mAsyncStream = nullptr;
   }
 
   if (mWorkerPrivate && mBusyCount != 0) {
-    mWorkerPrivate->RemoveFeature(this);
+    ReleaseWorker();
     mWorkerPrivate = nullptr;
     mBusyCount = 0;
   }
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/base/FileReader.h
+++ b/dom/base/FileReader.h
@@ -10,17 +10,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 
 #include "nsIAsyncInputStream.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsWeakReference.h"
-#include "WorkerFeature.h"
+#include "WorkerHolder.h"
 
 #define NS_PROGRESS_EVENT_INTERVAL 50
 
 class nsITimer;
 class nsIEventTarget;
 
 namespace mozilla {
 namespace dom {
@@ -36,17 +36,17 @@ extern const uint64_t kUnknownSize;
 
 class FileReaderDecreaseBusyCounter;
 
 class FileReader final : public DOMEventTargetHelper,
                          public nsIInterfaceRequestor,
                          public nsSupportsWeakReference,
                          public nsIInputStreamCallback,
                          public nsITimerCallback,
-                         public workers::WorkerFeature
+                         public workers::WorkerHolder
 {
   friend class FileReaderDecreaseBusyCounter;
 
 public:
   FileReader(nsIGlobalObject* aGlobal,
              workers::WorkerPrivate* aWorkerPrivate);
 
   NS_DECL_ISUPPORTS_INHERITED
@@ -101,17 +101,17 @@ public:
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(loadend)
 
   void ReadAsBinaryString(Blob& aBlob, ErrorResult& aRv)
   {
     ReadFileContent(aBlob, EmptyString(), FILE_AS_BINARY, aRv);
   }
 
-  // WorkerFeature
+  // WorkerHolder
   bool Notify(workers::Status) override;
 
 private:
   virtual ~FileReader();
 
   // This must be in sync with dom/webidl/FileReader.webidl
   enum eReadyState {
     EMPTY = 0,
@@ -185,16 +185,16 @@ private:
 
   uint64_t mTotal;
   uint64_t mTransferred;
 
   nsCOMPtr<nsIEventTarget> mTarget;
 
   uint64_t mBusyCount;
 
-  // Kept alive with a WorkerFeature.
+  // Kept alive with a WorkerHolder.
   workers::WorkerPrivate* mWorkerPrivate;
 };
 
 } // dom namespace
 } // mozilla namespace
 
 #endif // mozilla_dom_FileReader_h
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -94,17 +94,17 @@ public:
   , mDisconnectingOrDisconnected(false)
   , mCloseEventWasClean(false)
   , mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL)
   , mScriptLine(0)
   , mScriptColumn(0)
   , mInnerWindowID(0)
   , mWorkerPrivate(nullptr)
 #ifdef DEBUG
-  , mHasFeatureRegistered(false)
+  , mHasWorkerHolderRegistered(false)
 #endif
   , mIsMainThread(true)
   , mMutex("WebSocketImpl::mMutex")
   , mWorkerShuttingDown(false)
   {
     if (!NS_IsMainThread()) {
       mWorkerPrivate = GetCurrentThreadWorkerPrivate();
       MOZ_ASSERT(mWorkerPrivate);
@@ -161,18 +161,18 @@ public:
   // 2nd half of ScheduleConnectionCloseEvents, run in its own event.
   void DispatchConnectionCloseEvents();
 
   nsresult UpdateURI();
 
   void AddRefObject();
   void ReleaseObject();
 
-  bool RegisterFeature();
-  void UnregisterFeature();
+  bool RegisterWorkerHolder();
+  void UnregisterWorkerHolder();
 
   nsresult CancelInternal();
 
   RefPtr<WebSocket> mWebSocket;
 
   nsCOMPtr<nsIWebSocketChannel> mChannel;
 
   bool mIsServerSide; // True if we're implementing the server side of a
@@ -208,34 +208,34 @@ public:
   //   be the same as the Web Socket owner window.
   // These attributes are used for error reporting.
   nsCString mScriptFile;
   uint32_t mScriptLine;
   uint32_t mScriptColumn;
   uint64_t mInnerWindowID;
 
   WorkerPrivate* mWorkerPrivate;
-  nsAutoPtr<WorkerFeature> mWorkerFeature;
+  nsAutoPtr<WorkerHolder> mWorkerHolder;
 
 #ifdef DEBUG
   // This is protected by mutex.
-  bool mHasFeatureRegistered;
-
-  bool HasFeatureRegistered()
+  bool mHasWorkerHolderRegistered;
+
+  bool HasWorkerHolderRegistered()
   {
     MOZ_ASSERT(mWebSocket);
     MutexAutoLock lock(mWebSocket->mMutex);
-    return mHasFeatureRegistered;
+    return mHasWorkerHolderRegistered;
   }
 
-  void SetHasFeatureRegistered(bool aValue)
+  void SetHasWorkerHolderRegistered(bool aValue)
   {
     MOZ_ASSERT(mWebSocket);
     MutexAutoLock lock(mWebSocket->mMutex);
-    mHasFeatureRegistered = aValue;
+    mHasWorkerHolderRegistered = aValue;
   }
 #endif
 
   nsWeakPtr mWeakLoadGroup;
 
   bool mIsMainThread;
 
   // This mutex protects mWorkerShuttingDown.
@@ -484,17 +484,17 @@ WebSocketImpl::CloseConnection(uint16_t 
   AssertIsOnTargetThread();
 
   if (mDisconnectingOrDisconnected) {
     return NS_OK;
   }
 
   // If this method is called because the worker is going away, we will not
   // receive the OnStop() method and we have to disconnect the WebSocket and
-  // release the WorkerFeature.
+  // release the WorkerHolder.
   MaybeDisconnect md(this);
 
   uint16_t readyState = mWebSocket->ReadyState();
   if (readyState == WebSocket::CLOSING ||
       readyState == WebSocket::CLOSED) {
     return NS_OK;
   }
 
@@ -605,17 +605,17 @@ WebSocketImpl::Disconnect()
 {
   if (mDisconnectingOrDisconnected) {
     return;
   }
 
   AssertIsOnTargetThread();
 
   // Disconnect can be called from some control event (such as Notify() of
-  // WorkerFeature). This will be schedulated before any other sync/async
+  // WorkerHolder). This will be schedulated before any other sync/async
   // runnable. In order to prevent some double Disconnect() calls, we use this
   // boolean.
   mDisconnectingOrDisconnected = true;
 
   // DisconnectInternal touches observers and nsILoadGroup and it must run on
   // the main thread.
 
   if (NS_IsMainThread()) {
@@ -635,18 +635,18 @@ WebSocketImpl::Disconnect()
   RefPtr<WebSocketImpl> kungfuDeathGrip = this;
 
   NS_ReleaseOnMainThread(mChannel.forget());
   NS_ReleaseOnMainThread(mService.forget());
 
   mWebSocket->DontKeepAliveAnyMore();
   mWebSocket->mImpl = nullptr;
 
-  if (mWorkerPrivate && mWorkerFeature) {
-    UnregisterFeature();
+  if (mWorkerPrivate && mWorkerHolder) {
+    UnregisterWorkerHolder();
   }
 
   // We want to release the WebSocket in the correct thread.
   mWebSocket = nullptr;
 }
 
 void
 WebSocketImpl::DisconnectInternal()
@@ -1257,19 +1257,19 @@ WebSocket::ConstructorCommon(const Globa
 
   bool connectionFailed = true;
 
   if (NS_IsMainThread()) {
     webSocket->mImpl->Init(aGlobal.Context(), principal, !!aTransportProvider,
                            aUrl, protocolArray, EmptyCString(),
                            0, 0, aRv, &connectionFailed);
   } else {
-    // In workers we have to keep the worker alive using a feature in order to
-    // dispatch messages correctly.
-    if (!webSocket->mImpl->RegisterFeature()) {
+    // In workers we have to keep the worker alive using a workerHolder in order
+    // to dispatch messages correctly.
+    if (!webSocket->mImpl->RegisterWorkerHolder()) {
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
 
     unsigned lineno, column;
     JS::AutoFilename file;
     if (!JS::DescribeScriptedCaller(aGlobal.Context(), &file, &lineno,
                                     &column)) {
@@ -2199,20 +2199,20 @@ WebSocket::DontKeepAliveAnyMore()
     mImpl->ReleaseObject();
   }
 
   mCheckMustKeepAlive = false;
 }
 
 namespace {
 
-class WebSocketWorkerFeature final : public WorkerFeature
+class WebSocketWorkerHolder final : public WorkerHolder
 {
 public:
-  explicit WebSocketWorkerFeature(WebSocketImpl* aWebSocketImpl)
+  explicit WebSocketWorkerHolder(WebSocketImpl* aWebSocketImpl)
     : mWebSocketImpl(aWebSocketImpl)
   {
   }
 
   bool Notify(Status aStatus) override
   {
     MOZ_ASSERT(aStatus > workers::Running);
 
@@ -2245,49 +2245,49 @@ WebSocketImpl::AddRefObject()
 void
 WebSocketImpl::ReleaseObject()
 {
   AssertIsOnTargetThread();
   Release();
 }
 
 bool
-WebSocketImpl::RegisterFeature()
+WebSocketImpl::RegisterWorkerHolder()
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(!mWorkerFeature);
-  mWorkerFeature = new WebSocketWorkerFeature(this);
-
-  if (!mWorkerPrivate->AddFeature(mWorkerFeature)) {
-    NS_WARNING("Failed to register a feature.");
-    mWorkerFeature = nullptr;
+  MOZ_ASSERT(!mWorkerHolder);
+  mWorkerHolder = new WebSocketWorkerHolder(this);
+
+  if (NS_WARN_IF(!mWorkerHolder->HoldWorker(mWorkerPrivate))) {
+    mWorkerHolder = nullptr;
     return false;
   }
 
 #ifdef DEBUG
-  SetHasFeatureRegistered(true);
+  SetHasWorkerHolderRegistered(true);
 #endif
 
   return true;
 }
 
 void
-WebSocketImpl::UnregisterFeature()
+WebSocketImpl::UnregisterWorkerHolder()
 {
   MOZ_ASSERT(mDisconnectingOrDisconnected);
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(mWorkerFeature);
-
-  mWorkerPrivate->RemoveFeature(mWorkerFeature);
-  mWorkerFeature = nullptr;
+  MOZ_ASSERT(mWorkerHolder);
+
+  // The DTOR of this WorkerHolder will release the worker for us.
+  mWorkerHolder = nullptr;
+
   mWorkerPrivate = nullptr;
 
 #ifdef DEBUG
-  SetHasFeatureRegistered(false);
+  SetHasWorkerHolderRegistered(false);
 #endif
 }
 
 nsresult
 WebSocketImpl::UpdateURI()
 {
   AssertIsOnTargetThread();
 
@@ -2837,17 +2837,17 @@ WebSocketImpl::Dispatch(already_AddRefed
   MutexAutoLock lock(mMutex);
   if (mWorkerShuttingDown) {
     return NS_OK;
   }
 
   MOZ_ASSERT(mWorkerPrivate);
 
 #ifdef DEBUG
-  MOZ_ASSERT(HasFeatureRegistered());
+  MOZ_ASSERT(HasWorkerHolderRegistered());
 #endif
 
   // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
   // runnable.
   RefPtr<WorkerRunnableDispatcher> event =
     new WorkerRunnableDispatcher(this, mWorkerPrivate, event_ref.forget());
 
   if (!event->Dispatch()) {
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3293,39 +3293,39 @@ SetDocumentAndPageUseCounter(JSContext* 
   }
 }
 
 namespace {
 
 // This runnable is used to write a deprecation message from a worker to the
 // console running on the main-thread.
 class DeprecationWarningRunnable final : public Runnable
-                                       , public WorkerFeature
+                                       , public WorkerHolder
 {
   WorkerPrivate* mWorkerPrivate;
   nsIDocument::DeprecatedOperations mOperation;
 
 public:
   DeprecationWarningRunnable(WorkerPrivate* aWorkerPrivate,
                              nsIDocument::DeprecatedOperations aOperation)
     : mWorkerPrivate(aWorkerPrivate)
     , mOperation(aOperation)
   {
     MOZ_ASSERT(aWorkerPrivate);
   }
 
   void
   Dispatch()
   {
-    if (NS_WARN_IF(!mWorkerPrivate->AddFeature(this))) {
+    if (NS_WARN_IF(!HoldWorker(mWorkerPrivate))) {
       return;
     }
 
     if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) {
-      mWorkerPrivate->RemoveFeature(this);
+      ReleaseWorker();
       return;
     }
   }
 
   virtual bool
   Notify(Status aStatus) override
   {
     // We don't care about the notification. We just want to keep the
@@ -3346,22 +3346,22 @@ private:
       wp = wp->GetParent();
     }
 
     nsPIDOMWindowInner* window = wp->GetWindow();
     if (window && window->GetExtantDoc()) {
       window->GetExtantDoc()->WarnOnceAbout(mOperation);
     }
 
-    ReleaseWorker();
+    ReleaseWorkerHolder();
     return NS_OK;
   }
 
   void
-  ReleaseWorker()
+  ReleaseWorkerHolder()
   {
     class ReleaseRunnable final : public MainThreadWorkerRunnable
     {
       RefPtr<DeprecationWarningRunnable> mRunnable;
 
     public:
       ReleaseRunnable(WorkerPrivate* aWorkerPrivate,
                       DeprecationWarningRunnable* aRunnable)
@@ -3370,17 +3370,17 @@ private:
       {}
 
       virtual bool
       WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
       {
         MOZ_ASSERT(aWorkerPrivate);
         aWorkerPrivate->AssertIsOnWorkerThread();
 
-        aWorkerPrivate->RemoveFeature(mRunnable);
+        mRunnable->ReleaseWorker();
         return true;
       }
     };
 
     RefPtr<ReleaseRunnable> runnable =
       new ReleaseRunnable(mWorkerPrivate, this);
     NS_WARN_IF(!runnable->Dispatch());
   }
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -261,65 +261,65 @@ public:
 private:
   ~TeardownRunnable() {}
 
   RefPtr<BroadcastChannelChild> mActor;
 };
 
 NS_IMPL_ISUPPORTS(TeardownRunnable, nsICancelableRunnable, nsIRunnable)
 
-class BroadcastChannelFeature final : public workers::WorkerFeature
+class BroadcastChannelWorkerHolder final : public workers::WorkerHolder
 {
   BroadcastChannel* mChannel;
 
 public:
-  explicit BroadcastChannelFeature(BroadcastChannel* aChannel)
+  explicit BroadcastChannelWorkerHolder(BroadcastChannel* aChannel)
     : mChannel(aChannel)
   {
-    MOZ_COUNT_CTOR(BroadcastChannelFeature);
+    MOZ_COUNT_CTOR(BroadcastChannelWorkerHolder);
   }
 
   virtual bool Notify(workers::Status aStatus) override
   {
     if (aStatus >= Closing) {
       mChannel->Shutdown();
     }
 
     return true;
   }
 
 private:
-  ~BroadcastChannelFeature()
+  ~BroadcastChannelWorkerHolder()
   {
-    MOZ_COUNT_DTOR(BroadcastChannelFeature);
+    MOZ_COUNT_DTOR(BroadcastChannelWorkerHolder);
   }
 };
 
 } // namespace
 
 BroadcastChannel::BroadcastChannel(nsPIDOMWindowInner* aWindow,
                                    const PrincipalInfo& aPrincipalInfo,
                                    const nsACString& aOrigin,
                                    const nsAString& aChannel)
   : DOMEventTargetHelper(aWindow)
-  , mWorkerFeature(nullptr)
+  , mWorkerHolder(nullptr)
   , mPrincipalInfo(new PrincipalInfo(aPrincipalInfo))
   , mOrigin(aOrigin)
   , mChannel(aChannel)
   , mIsKeptAlive(false)
   , mInnerID(0)
   , mState(StateActive)
 {
   // Window can be null in workers
 }
 
 BroadcastChannel::~BroadcastChannel()
 {
   Shutdown();
-  MOZ_ASSERT(!mWorkerFeature);
+  MOZ_ASSERT(!mWorkerHolder);
 }
 
 JSObject*
 BroadcastChannel::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return BroadcastChannelBinding::Wrap(aCx, this, aGivenProto);
 }
 
@@ -401,20 +401,19 @@ BroadcastChannel::Constructor(const Glob
     bc->mInnerID = window->WindowID();
 
     // Register as observer for inner-window-destroyed.
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(bc, "inner-window-destroyed", false);
     }
   } else {
-    bc->mWorkerFeature = new BroadcastChannelFeature(bc);
-    if (NS_WARN_IF(!workerPrivate->AddFeature(bc->mWorkerFeature))) {
-      NS_WARNING("Failed to register the BroadcastChannel worker feature.");
-      bc->mWorkerFeature = nullptr;
+    bc->mWorkerHolder = new BroadcastChannelWorkerHolder(bc);
+    if (NS_WARN_IF(!bc->mWorkerHolder->HoldWorker(workerPrivate))) {
+      bc->mWorkerHolder = nullptr;
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
   }
 
   return bc.forget();
 }
 
@@ -523,21 +522,18 @@ BroadcastChannel::ActorCreated(PBackgrou
   }
 }
 
 void
 BroadcastChannel::Shutdown()
 {
   mState = StateClosed;
 
-  if (mWorkerFeature) {
-    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-    workerPrivate->RemoveFeature(mWorkerFeature);
-    mWorkerFeature = nullptr;
-  }
+  // The DTOR of this WorkerHolder will release the worker for us.
+  mWorkerHolder = nullptr;
 
   if (mActor) {
     mActor->SetParent(nullptr);
 
     RefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor);
     NS_DispatchToCurrentThread(runnable);
 
     mActor = nullptr;
--- a/dom/broadcastchannel/BroadcastChannel.h
+++ b/dom/broadcastchannel/BroadcastChannel.h
@@ -21,17 +21,17 @@ namespace mozilla {
 
 namespace ipc {
 class PrincipalInfo;
 } // namespace ipc
 
 namespace dom {
 
 namespace workers {
-class WorkerFeature;
+class WorkerHolder;
 } // namespace workers
 
 class BroadcastChannelChild;
 class BroadcastChannelMessage;
 
 class BroadcastChannel final
   : public DOMEventTargetHelper
   , public nsIIPCBackgroundChildCreateCallback
@@ -105,17 +105,17 @@ private:
     return mIsKeptAlive;
   }
 
   void RemoveDocFromBFCache();
 
   RefPtr<BroadcastChannelChild> mActor;
   nsTArray<RefPtr<BroadcastChannelMessage>> mPendingMessages;
 
-  nsAutoPtr<workers::WorkerFeature> mWorkerFeature;
+  nsAutoPtr<workers::WorkerHolder> mWorkerHolder;
 
   nsAutoPtr<PrincipalInfo> mPrincipalInfo;
 
   nsCString mOrigin;
   nsString mChannel;
 
   bool mIsKeptAlive;
 
--- a/dom/cache/ActorChild.cpp
+++ b/dom/cache/ActorChild.cpp
@@ -1,66 +1,66 @@
 /* -*- 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/cache/ActorChild.h"
 
-#include "mozilla/dom/cache/Feature.h"
+#include "mozilla/dom/cache/CacheWorkerHolder.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 void
-ActorChild::SetFeature(Feature* aFeature)
+ActorChild::SetWorkerHolder(CacheWorkerHolder* aWorkerHolder)
 {
   // Some of the Cache actors can have multiple DOM objects associated with
-  // them.  In this case the feature will be added multiple times.  This is
-  // permitted, but the feature should be the same each time.
-  if (mFeature) {
-    MOZ_ASSERT(mFeature == aFeature);
+  // them.  In this case the workerHolder will be added multiple times.  This is
+  // permitted, but the workerHolder should be the same each time.
+  if (mWorkerHolder) {
+    MOZ_ASSERT(mWorkerHolder == aWorkerHolder);
     return;
   }
 
-  mFeature = aFeature;
-  if (mFeature) {
-    mFeature->AddActor(this);
+  mWorkerHolder = aWorkerHolder;
+  if (mWorkerHolder) {
+    mWorkerHolder->AddActor(this);
   }
 }
 
 void
-ActorChild::RemoveFeature()
+ActorChild::RemoveWorkerHolder()
 {
-  MOZ_ASSERT_IF(!NS_IsMainThread(), mFeature);
-  if (mFeature) {
-    mFeature->RemoveActor(this);
-    mFeature = nullptr;
+  MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerHolder);
+  if (mWorkerHolder) {
+    mWorkerHolder->RemoveActor(this);
+    mWorkerHolder = nullptr;
   }
 }
 
-Feature*
-ActorChild::GetFeature() const
+CacheWorkerHolder*
+ActorChild::GetWorkerHolder() const
 {
-  return mFeature;
+  return mWorkerHolder;
 }
 
 bool
-ActorChild::FeatureNotified() const
+ActorChild::WorkerHolderNotified() const
 {
-  return mFeature && mFeature->Notified();
+  return mWorkerHolder && mWorkerHolder->Notified();
 }
 
 ActorChild::ActorChild()
 {
 }
 
 ActorChild::~ActorChild()
 {
-  MOZ_ASSERT(!mFeature);
+  MOZ_ASSERT(!mWorkerHolder);
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/ActorChild.h
+++ b/dom/cache/ActorChild.h
@@ -8,41 +8,41 @@
 #define mozilla_dom_cache_ActioChild_h
 
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
-class Feature;
+class CacheWorkerHolder;
 
 class ActorChild
 {
 public:
   virtual void
   StartDestroy() = 0;
 
   void
-  SetFeature(Feature* aFeature);
+  SetWorkerHolder(CacheWorkerHolder* aWorkerHolder);
 
   void
-  RemoveFeature();
+  RemoveWorkerHolder();
 
-  Feature*
-  GetFeature() const;
+  CacheWorkerHolder*
+  GetWorkerHolder() const;
 
   bool
-  FeatureNotified() const;
+  WorkerHolderNotified() const;
 
 protected:
   ActorChild();
   ~ActorChild();
 
 private:
-  RefPtr<Feature> mFeature;
+  RefPtr<CacheWorkerHolder> mWorkerHolder;
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_ActioChild_h
--- a/dom/cache/Cache.cpp
+++ b/dom/cache/Cache.cpp
@@ -10,17 +10,17 @@
 #include "mozilla/dom/InternalResponse.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/CacheBinding.h"
 #include "mozilla/dom/cache/AutoUtils.h"
 #include "mozilla/dom/cache/CacheChild.h"
-#include "mozilla/dom/cache/Feature.h"
+#include "mozilla/dom/cache/CacheWorkerHolder.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 #include "nsIGlobalObject.h"
 
 namespace mozilla {
 namespace dom {
@@ -77,43 +77,43 @@ IsValidPutRequestMethod(const RequestOrU
     return true;
   }
   return IsValidPutRequestMethod(aRequest.GetAsRequest(), aRv);
 }
 
 } // namespace
 
 // Helper class to wait for Add()/AddAll() fetch requests to complete and
-// then perform a PutAll() with the responses.  This class holds a Feature
+// then perform a PutAll() with the responses.  This class holds a WorkerHolder
 // to keep the Worker thread alive.  This is mainly to ensure that Add/AddAll
 // act the same as other Cache operations that directly create a CacheOpChild
 // actor.
 class Cache::FetchHandler final : public PromiseNativeHandler
 {
 public:
-  FetchHandler(Feature* aFeature, Cache* aCache,
+  FetchHandler(CacheWorkerHolder* aWorkerHolder, Cache* aCache,
                nsTArray<RefPtr<Request>>&& aRequestList, Promise* aPromise)
-    : mFeature(aFeature)
+    : mWorkerHolder(aWorkerHolder)
     , mCache(aCache)
     , mRequestList(Move(aRequestList))
     , mPromise(aPromise)
   {
-    MOZ_ASSERT_IF(!NS_IsMainThread(), mFeature);
+    MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerHolder);
     MOZ_ASSERT(mCache);
     MOZ_ASSERT(mPromise);
   }
 
   virtual void
   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
     NS_ASSERT_OWNINGTHREAD(FetchHandler);
 
     // Stop holding the worker alive when we leave this method.
-    RefPtr<Feature> feature;
-    feature.swap(mFeature);
+    RefPtr<CacheWorkerHolder> workerHolder;
+    workerHolder.swap(mWorkerHolder);
 
     // Promise::All() passed an array of fetch() Promises should give us
     // an Array of Response objects.  The following code unwraps these
     // JS values back to an nsTArray<RefPtr<Response>>.
 
     AutoTArray<RefPtr<Response>, 256> responseList;
     responseList.SetCapacity(mRequestList.Length());
 
@@ -212,17 +212,17 @@ private:
   void
   Fail()
   {
     ErrorResult rv;
     rv.ThrowTypeError<MSG_FETCH_FAILED>();
     mPromise->MaybeReject(rv);
   }
 
-  RefPtr<Feature> mFeature;
+  RefPtr<CacheWorkerHolder> mWorkerHolder;
   RefPtr<Cache> mCache;
   nsTArray<RefPtr<Request>> mRequestList;
   RefPtr<Promise> mPromise;
 
   NS_DECL_ISUPPORTS
 };
 
 NS_IMPL_ISUPPORTS0(Cache::FetchHandler)
@@ -609,18 +609,19 @@ Cache::AddAll(const GlobalObject& aGloba
     fetchList.AppendElement(Move(fetch));
   }
 
   RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  RefPtr<FetchHandler> handler = new FetchHandler(mActor->GetFeature(), this,
-                                                    Move(aRequestList), promise);
+  RefPtr<FetchHandler> handler =
+    new FetchHandler(mActor->GetWorkerHolder(), this,
+                     Move(aRequestList), promise);
 
   RefPtr<Promise> fetchPromise = Promise::All(aGlobal, fetchList, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
   fetchPromise->AppendNativeHandler(handler);
 
   return promise.forget();
--- a/dom/cache/CacheChild.cpp
+++ b/dom/cache/CacheChild.cpp
@@ -65,17 +65,17 @@ CacheChild::ClearListener()
 }
 
 void
 CacheChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
                       nsISupports* aParent, const CacheOpArgs& aArgs)
 {
   mNumChildActors += 1;
   MOZ_ALWAYS_TRUE(SendPCacheOpConstructor(
-    new CacheOpChild(GetFeature(), aGlobal, aParent, aPromise), aArgs));
+    new CacheOpChild(GetWorkerHolder(), aGlobal, aParent, aPromise), aArgs));
 }
 
 void
 CacheChild::StartDestroyFromListener()
 {
   NS_ASSERT_OWNINGTHREAD(CacheChild);
 
   // The listener should be held alive by any async operations, so if it
@@ -98,17 +98,17 @@ CacheChild::StartDestroy()
   // have been explicitly locked by someone using us on the stack.
   if (mNumChildActors || mLocked) {
     mDelayedDestroy = true;
     return;
   }
 
   RefPtr<Cache> listener = mListener;
 
-  // StartDestroy() can get called from either Cache or the Feature.
+  // StartDestroy() can get called from either Cache or the WorkerHolder.
   // Theoretically we can get double called if the right race happens.  Handle
   // that by just ignoring the second StartDestroy() call.
   if (!listener) {
     return;
   }
 
   listener->DestroyInternal(this);
 
@@ -125,17 +125,17 @@ CacheChild::ActorDestroy(ActorDestroyRea
   NS_ASSERT_OWNINGTHREAD(CacheChild);
   RefPtr<Cache> listener = mListener;
   if (listener) {
     listener->DestroyInternal(this);
     // Cache listener should call ClearListener() in DestroyInternal()
     MOZ_ASSERT(!mListener);
   }
 
-  RemoveFeature();
+  RemoveWorkerHolder();
 }
 
 PCacheOpChild*
 CacheChild::AllocPCacheOpChild(const CacheOpArgs& aOpArgs)
 {
   MOZ_CRASH("CacheOpChild should be manually constructed.");
   return nullptr;
 }
--- a/dom/cache/CacheChild.h
+++ b/dom/cache/CacheChild.h
@@ -60,17 +60,17 @@ public:
             nsISupports* aParent, const CacheOpArgs& aArgs);
 
   // Our parent Listener object has gone out of scope and is being destroyed.
   void StartDestroyFromListener();
 
 private:
   // ActorChild methods
 
-  // Feature is trying to destroy due to worker shutdown.
+  // WorkerHolder is trying to destroy due to worker shutdown.
   virtual void StartDestroy() override;
 
   // PCacheChild methods
   virtual void
   ActorDestroy(ActorDestroyReason aReason) override;
 
   virtual PCacheOpChild*
   AllocPCacheOpChild(const CacheOpArgs& aOpArgs) override;
--- a/dom/cache/CacheOpChild.cpp
+++ b/dom/cache/CacheOpChild.cpp
@@ -17,64 +17,70 @@ namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::ipc::PBackgroundChild;
 
 namespace {
 
 void
-AddFeatureToStreamChild(const CacheReadStream& aReadStream, Feature* aFeature)
+AddWorkerHolderToStreamChild(const CacheReadStream& aReadStream,
+                             CacheWorkerHolder* aWorkerHolder)
 {
-  MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
+  MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerHolder);
   CacheStreamControlChild* cacheControl =
     static_cast<CacheStreamControlChild*>(aReadStream.controlChild());
   if (cacheControl) {
-    cacheControl->SetFeature(aFeature);
+    cacheControl->SetWorkerHolder(aWorkerHolder);
   }
 }
 
 void
-AddFeatureToStreamChild(const CacheResponse& aResponse, Feature* aFeature)
+AddWorkerHolderToStreamChild(const CacheResponse& aResponse,
+                             CacheWorkerHolder* aWorkerHolder)
 {
-  MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
+  MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerHolder);
 
   if (aResponse.body().type() == CacheReadStreamOrVoid::Tvoid_t) {
     return;
   }
 
-  AddFeatureToStreamChild(aResponse.body().get_CacheReadStream(), aFeature);
+  AddWorkerHolderToStreamChild(aResponse.body().get_CacheReadStream(),
+                               aWorkerHolder);
 }
 
 void
-AddFeatureToStreamChild(const CacheRequest& aRequest, Feature* aFeature)
+AddWorkerHolderToStreamChild(const CacheRequest& aRequest,
+                             CacheWorkerHolder* aWorkerHolder)
 {
-  MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
+  MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerHolder);
 
   if (aRequest.body().type() == CacheReadStreamOrVoid::Tvoid_t) {
     return;
   }
 
-  AddFeatureToStreamChild(aRequest.body().get_CacheReadStream(), aFeature);
+  AddWorkerHolderToStreamChild(aRequest.body().get_CacheReadStream(),
+                               aWorkerHolder);
 }
 
 } // namespace
 
-CacheOpChild::CacheOpChild(Feature* aFeature, nsIGlobalObject* aGlobal,
+CacheOpChild::CacheOpChild(CacheWorkerHolder* aWorkerHolder,
+                           nsIGlobalObject* aGlobal,
                            nsISupports* aParent, Promise* aPromise)
   : mGlobal(aGlobal)
   , mParent(aParent)
   , mPromise(aPromise)
 {
   MOZ_ASSERT(mGlobal);
   MOZ_ASSERT(mParent);
   MOZ_ASSERT(mPromise);
 
-  MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
-  SetFeature(aFeature);
+  MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerHolder);
+  SetWorkerHolder(aWorkerHolder);
 }
 
 CacheOpChild::~CacheOpChild()
 {
   NS_ASSERT_OWNINGTHREAD(CacheOpChild);
   MOZ_ASSERT(!mPromise);
 }
 
@@ -85,17 +91,17 @@ CacheOpChild::ActorDestroy(ActorDestroyR
 
   // If the actor was terminated for some unknown reason, then indicate the
   // operation is dead.
   if (mPromise) {
     mPromise->MaybeReject(NS_ERROR_FAILURE);
     mPromise = nullptr;
   }
 
-  RemoveFeature();
+  RemoveWorkerHolder();
 }
 
 bool
 CacheOpChild::Recv__delete__(const ErrorResult& aRv,
                              const CacheOpResult& aResult)
 {
   NS_ASSERT_OWNINGTHREAD(CacheOpChild);
 
@@ -144,17 +150,17 @@ CacheOpChild::Recv__delete__(const Error
     {
       mPromise->MaybeResolve(aResult.get_StorageHasResult().success());
       break;
     }
     case CacheOpResult::TStorageOpenResult:
     {
       auto actor = static_cast<CacheChild*>(
         aResult.get_StorageOpenResult().actorChild());
-      actor->SetFeature(GetFeature());
+      actor->SetWorkerHolder(GetWorkerHolder());
       RefPtr<Cache> cache = new Cache(mGlobal, actor);
       mPromise->MaybeResolve(cache);
       break;
     }
     case CacheOpResult::TStorageDeleteResult:
     {
       mPromise->MaybeResolve(aResult.get_StorageDeleteResult().success());
       break;
@@ -173,18 +179,18 @@ CacheOpChild::Recv__delete__(const Error
   return true;
 }
 
 void
 CacheOpChild::StartDestroy()
 {
   NS_ASSERT_OWNINGTHREAD(CacheOpChild);
 
-  // Do not cancel on-going operations when Feature calls this.  Instead, keep
-  // the Worker alive until we are done.
+  // Do not cancel on-going operations when WorkerHolder calls this.  Instead,
+  // keep the Worker alive until we are done.
 }
 
 nsIGlobalObject*
 CacheOpChild::GetGlobalObject() const
 {
   return mGlobal;
 }
 
@@ -207,44 +213,44 @@ CacheOpChild::HandleResponse(const Cache
 {
   if (aResponseOrVoid.type() == CacheResponseOrVoid::Tvoid_t) {
     mPromise->MaybeResolve(JS::UndefinedHandleValue);
     return;
   }
 
   const CacheResponse& cacheResponse = aResponseOrVoid.get_CacheResponse();
 
-  AddFeatureToStreamChild(cacheResponse, GetFeature());
+  AddWorkerHolderToStreamChild(cacheResponse, GetWorkerHolder());
   RefPtr<Response> response = ToResponse(cacheResponse);
 
   mPromise->MaybeResolve(response);
 }
 
 void
 CacheOpChild::HandleResponseList(const nsTArray<CacheResponse>& aResponseList)
 {
   AutoTArray<RefPtr<Response>, 256> responses;
   responses.SetCapacity(aResponseList.Length());
 
   for (uint32_t i = 0; i < aResponseList.Length(); ++i) {
-    AddFeatureToStreamChild(aResponseList[i], GetFeature());
+    AddWorkerHolderToStreamChild(aResponseList[i], GetWorkerHolder());
     responses.AppendElement(ToResponse(aResponseList[i]));
   }
 
   mPromise->MaybeResolve(responses);
 }
 
 void
 CacheOpChild::HandleRequestList(const nsTArray<CacheRequest>& aRequestList)
 {
   AutoTArray<RefPtr<Request>, 256> requests;
   requests.SetCapacity(aRequestList.Length());
 
   for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
-    AddFeatureToStreamChild(aRequestList[i], GetFeature());
+    AddWorkerHolderToStreamChild(aRequestList[i], GetWorkerHolder());
     requests.AppendElement(ToRequest(aRequestList[i]));
   }
 
   mPromise->MaybeResolve(requests);
 }
 
 } // namespace cache
 } // namespace dom
--- a/dom/cache/CacheOpChild.h
+++ b/dom/cache/CacheOpChild.h
@@ -26,17 +26,17 @@ class CacheOpChild final : public PCache
                          , public TypeUtils
 {
   friend class CacheChild;
   friend class CacheStorageChild;
 
 private:
   // This class must be constructed by CacheChild or CacheStorageChild using
   // their ExecuteOp() factory method.
-  CacheOpChild(Feature* aFeature, nsIGlobalObject* aGlobal,
+  CacheOpChild(CacheWorkerHolder* aWorkerHolder, nsIGlobalObject* aGlobal,
                nsISupports* aParent, Promise* aPromise);
   ~CacheOpChild();
 
   // PCacheOpChild methods
   virtual void
   ActorDestroy(ActorDestroyReason aReason) override;
 
   virtual bool
--- a/dom/cache/CacheStorage.cpp
+++ b/dom/cache/CacheStorage.cpp
@@ -9,17 +9,17 @@
 #include "mozilla/unused.h"
 #include "mozilla/dom/CacheStorageBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/cache/AutoUtils.h"
 #include "mozilla/dom/cache/Cache.h"
 #include "mozilla/dom/cache/CacheChild.h"
 #include "mozilla/dom/cache/CacheStorageChild.h"
-#include "mozilla/dom/cache/Feature.h"
+#include "mozilla/dom/cache/CacheWorkerHolder.h"
 #include "mozilla/dom/cache/PCacheChild.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/dom/cache/TypeUtils.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "nsContentUtils.h"
@@ -197,18 +197,19 @@ CacheStorage::CreateOnWorker(Namespace a
   }
 
   if (aWorkerPrivate->IsInPrivateBrowsing()) {
     NS_WARNING("CacheStorage not supported during private browsing.");
     RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR);
     return ref.forget();
   }
 
-  RefPtr<Feature> feature = Feature::Create(aWorkerPrivate);
-  if (!feature) {
+  RefPtr<CacheWorkerHolder> workerHolder =
+    CacheWorkerHolder::Create(aWorkerPrivate);
+  if (!workerHolder) {
     NS_WARNING("Worker thread is shutting down.");
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   const PrincipalInfo& principalInfo = aWorkerPrivate->GetPrincipalInfo();
 
   // We have a number of cases where we want to skip the https scheme
@@ -231,17 +232,17 @@ CacheStorage::CreateOnWorker(Namespace a
 
   if (!IsTrusted(principalInfo, testingEnabled)) {
     NS_WARNING("CacheStorage not supported on untrusted origins.");
     RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR);
     return ref.forget();
   }
 
   RefPtr<CacheStorage> ref = new CacheStorage(aNamespace, aGlobal,
-                                                principalInfo, feature);
+                                              principalInfo, workerHolder);
   return ref.forget();
 }
 
 // static
 bool
 CacheStorage::DefineCaches(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -271,21 +272,22 @@ CacheStorage::DefineCaches(JSContext* aC
   if (NS_WARN_IF(!ToJSValue(aCx, storage, &caches))) {
     return false;
   }
 
   return JS_DefineProperty(aCx, aGlobal, "caches", caches, JSPROP_ENUMERATE);
 }
 
 CacheStorage::CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal,
-                           const PrincipalInfo& aPrincipalInfo, Feature* aFeature)
+                           const PrincipalInfo& aPrincipalInfo,
+                           CacheWorkerHolder* aWorkerHolder)
   : mNamespace(aNamespace)
   , mGlobal(aGlobal)
   , mPrincipalInfo(MakeUnique<PrincipalInfo>(aPrincipalInfo))
-  , mFeature(aFeature)
+  , mWorkerHolder(aWorkerHolder)
   , mActor(nullptr)
   , mStatus(NS_OK)
 {
   MOZ_ASSERT(mGlobal);
 
   // If the PBackground actor is already initialized then we can
   // immediately use it
   PBackgroundChild* actor = BackgroundChild::GetForCurrentThread();
@@ -504,50 +506,50 @@ CacheStorage::WrapObject(JSContext* aCon
 }
 
 void
 CacheStorage::ActorCreated(PBackgroundChild* aActor)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
   MOZ_ASSERT(aActor);
 
-  if (NS_WARN_IF(mFeature && mFeature->Notified())) {
+  if (NS_WARN_IF(mWorkerHolder && mWorkerHolder->Notified())) {
     ActorFailed();
     return;
   }
 
-  // Feature ownership is passed to the CacheStorageChild actor and any actors
-  // it may create.  The Feature will keep the worker thread alive until the
-  // actors can gracefully shutdown.
-  CacheStorageChild* newActor = new CacheStorageChild(this, mFeature);
+  // WorkerHolder ownership is passed to the CacheStorageChild actor and any
+  // actors it may create.  The WorkerHolder will keep the worker thread alive
+  // until the actors can gracefully shutdown.
+  CacheStorageChild* newActor = new CacheStorageChild(this, mWorkerHolder);
   PCacheStorageChild* constructedActor =
     aActor->SendPCacheStorageConstructor(newActor, mNamespace, *mPrincipalInfo);
 
   if (NS_WARN_IF(!constructedActor)) {
     ActorFailed();
     return;
   }
 
-  mFeature = nullptr;
+  mWorkerHolder = nullptr;
 
   MOZ_ASSERT(constructedActor == newActor);
   mActor = newActor;
 
   MaybeRunPendingRequests();
   MOZ_ASSERT(mPendingRequests.IsEmpty());
 }
 
 void
 CacheStorage::ActorFailed()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
   MOZ_ASSERT(!NS_FAILED(mStatus));
 
   mStatus = NS_ERROR_UNEXPECTED;
-  mFeature = nullptr;
+  mWorkerHolder = nullptr;
 
   for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) {
     nsAutoPtr<Entry> entry(mPendingRequests[i].forget());
     entry->mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
   }
   mPendingRequests.Clear();
 }
 
--- a/dom/cache/CacheStorage.h
+++ b/dom/cache/CacheStorage.h
@@ -33,17 +33,17 @@ class Promise;
 
 namespace workers {
   class WorkerPrivate;
 } // namespace workers
 
 namespace cache {
 
 class CacheStorageChild;
-class Feature;
+class CacheWorkerHolder;
 
 class CacheStorage final : public nsIIPCBackgroundChildCreateCallback
                          , public nsWrapperCache
                          , public TypeUtils
 {
   typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
 
 public:
@@ -92,26 +92,27 @@ public:
   virtual void AssertOwningThread() const override;
 #endif
 
   virtual mozilla::ipc::PBackgroundChild*
   GetIPCManager() override;
 
 private:
   CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal,
-               const mozilla::ipc::PrincipalInfo& aPrincipalInfo, Feature* aFeature);
+               const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
+               CacheWorkerHolder* aWorkerHolder);
   explicit CacheStorage(nsresult aFailureResult);
   ~CacheStorage();
 
   void MaybeRunPendingRequests();
 
   const Namespace mNamespace;
   nsCOMPtr<nsIGlobalObject> mGlobal;
   UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
-  RefPtr<Feature> mFeature;
+  RefPtr<CacheWorkerHolder> mWorkerHolder;
 
   // weak ref cleared in DestroyInternal
   CacheStorageChild* mActor;
 
   struct Entry;
   nsTArray<nsAutoPtr<Entry>> mPendingRequests;
 
   nsresult mStatus;
--- a/dom/cache/CacheStorageChild.cpp
+++ b/dom/cache/CacheStorageChild.cpp
@@ -17,25 +17,26 @@ namespace cache {
 
 // declared in ActorUtils.h
 void
 DeallocPCacheStorageChild(PCacheStorageChild* aActor)
 {
   delete aActor;
 }
 
-CacheStorageChild::CacheStorageChild(CacheStorage* aListener, Feature* aFeature)
+CacheStorageChild::CacheStorageChild(CacheStorage* aListener,
+                                     CacheWorkerHolder* aWorkerHolder)
   : mListener(aListener)
   , mNumChildActors(0)
   , mDelayedDestroy(false)
 {
   MOZ_COUNT_CTOR(cache::CacheStorageChild);
   MOZ_ASSERT(mListener);
 
-  SetFeature(aFeature);
+  SetWorkerHolder(aWorkerHolder);
 }
 
 CacheStorageChild::~CacheStorageChild()
 {
   MOZ_COUNT_DTOR(cache::CacheStorageChild);
   NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
   MOZ_ASSERT(!mListener);
 }
@@ -49,17 +50,17 @@ CacheStorageChild::ClearListener()
 }
 
 void
 CacheStorageChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
                              nsISupports* aParent, const CacheOpArgs& aArgs)
 {
   mNumChildActors += 1;
   Unused << SendPCacheOpConstructor(
-    new CacheOpChild(GetFeature(), aGlobal, aParent, aPromise), aArgs);
+    new CacheOpChild(GetWorkerHolder(), aGlobal, aParent, aPromise), aArgs);
 }
 
 void
 CacheStorageChild::StartDestroyFromListener()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
 
   // The listener should be held alive by any async operations, so if it
@@ -81,17 +82,18 @@ CacheStorageChild::StartDestroy()
   // method when the last child actor is gone.
   if (mNumChildActors) {
     mDelayedDestroy = true;
     return;
   }
 
   RefPtr<CacheStorage> listener = mListener;
 
-  // StartDestroy() can get called from either CacheStorage or the Feature.
+  // StartDestroy() can get called from either CacheStorage or the
+  // CacheWorkerHolder.
   // Theoretically we can get double called if the right race happens.  Handle
   // that by just ignoring the second StartDestroy() call.
   if (!listener) {
     return;
   }
 
   listener->DestroyInternal(this);
 
@@ -108,17 +110,17 @@ CacheStorageChild::ActorDestroy(ActorDes
   NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
   RefPtr<CacheStorage> listener = mListener;
   if (listener) {
     listener->DestroyInternal(this);
     // CacheStorage listener should call ClearListener() in DestroyInternal()
     MOZ_ASSERT(!mListener);
   }
 
-  RemoveFeature();
+  RemoveWorkerHolder();
 }
 
 PCacheOpChild*
 CacheStorageChild::AllocPCacheOpChild(const CacheOpArgs& aOpArgs)
 {
   MOZ_CRASH("CacheOpChild should be manually constructed.");
   return nullptr;
 }
--- a/dom/cache/CacheStorageChild.h
+++ b/dom/cache/CacheStorageChild.h
@@ -17,24 +17,24 @@ namespace mozilla {
 namespace dom {
 
 class Promise;
 
 namespace cache {
 
 class CacheOpArgs;
 class CacheStorage;
+class CacheWorkerHolder;
 class PCacheChild;
-class Feature;
 
 class CacheStorageChild final : public PCacheStorageChild
                               , public ActorChild
 {
 public:
-  CacheStorageChild(CacheStorage* aListener, Feature* aFeature);
+  CacheStorageChild(CacheStorage* aListener, CacheWorkerHolder* aWorkerHolder);
   ~CacheStorageChild();
 
   // Must be called by the associated CacheStorage listener in its
   // DestroyInternal() method.  Also, CacheStorage must call
   // SendDestroyFromListener() on the actor in its destructor to trigger
   // ActorDestroy() if it has not been called yet.
   void ClearListener();
 
@@ -43,17 +43,17 @@ public:
             nsISupports* aParent, const CacheOpArgs& aArgs);
 
   // Our parent Listener object has gone out of scope and is being destroyed.
   void StartDestroyFromListener();
 
 private:
   // ActorChild methods
 
-  // Feature is trying to destroy due to worker shutdown.
+  // CacheWorkerHolder is trying to destroy due to worker shutdown.
   virtual void StartDestroy() override;
 
   // PCacheStorageChild methods
   virtual void ActorDestroy(ActorDestroyReason aReason) override;
 
   virtual PCacheOpChild*
   AllocPCacheOpChild(const CacheOpArgs& aOpArgs) override;
 
--- a/dom/cache/CacheStreamControlChild.cpp
+++ b/dom/cache/CacheStreamControlChild.cpp
@@ -53,18 +53,18 @@ CacheStreamControlChild::~CacheStreamCon
   MOZ_COUNT_DTOR(cache::CacheStreamControlChild);
 }
 
 void
 CacheStreamControlChild::StartDestroy()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
   // This can get called twice under some circumstances.  For example, if the
-  // actor is added to a Feature that has already been notified and the Cache
-  // actor has no mListener.
+  // actor is added to a CacheWorkerHolder that has already been notified and
+  // the Cache actor has no mListener.
   if (mDestroyStarted) {
     return;
   }
   mDestroyStarted = true;
 
   // If any of the streams have started to be read, then wait for them to close
   // naturally.
   if (HasEverBeenRead()) {
@@ -130,17 +130,17 @@ CacheStreamControlChild::AssertOwningThr
 }
 #endif
 
 void
 CacheStreamControlChild::ActorDestroy(ActorDestroyReason aReason)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
   CloseAllReadStreamsWithoutReporting();
-  RemoveFeature();
+  RemoveWorkerHolder();
 }
 
 bool
 CacheStreamControlChild::RecvClose(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
   CloseReadStreams(aId);
   return true;
rename from dom/cache/Feature.cpp
rename to dom/cache/CacheWorkerHolder.cpp
--- a/dom/cache/Feature.cpp
+++ b/dom/cache/CacheWorkerHolder.cpp
@@ -1,82 +1,83 @@
 /* -*- 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/cache/Feature.h"
+#include "mozilla/dom/cache/CacheWorkerHolder.h"
 
 #include "mozilla/dom/cache/ActorChild.h"
 #include "WorkerPrivate.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::dom::workers::Terminating;
 using mozilla::dom::workers::Status;
 using mozilla::dom::workers::WorkerPrivate;
 
 // static
-already_AddRefed<Feature>
-Feature::Create(WorkerPrivate* aWorkerPrivate)
+already_AddRefed<CacheWorkerHolder>
+CacheWorkerHolder::Create(WorkerPrivate* aWorkerPrivate)
 {
   MOZ_ASSERT(aWorkerPrivate);
 
-  RefPtr<Feature> feature = new Feature(aWorkerPrivate);
+  RefPtr<CacheWorkerHolder> workerHolder =
+    new CacheWorkerHolder(aWorkerPrivate);
 
-  if (!aWorkerPrivate->AddFeature(feature)) {
+  if (NS_WARN_IF(!workerHolder->HoldWorker(aWorkerPrivate))) {
     return nullptr;
   }
 
-  return feature.forget();
+  return workerHolder.forget();
 }
 
 void
-Feature::AddActor(ActorChild* aActor)
+CacheWorkerHolder::AddActor(ActorChild* aActor)
 {
-  NS_ASSERT_OWNINGTHREAD(Feature);
+  NS_ASSERT_OWNINGTHREAD(CacheWorkerHolder);
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(!mActorList.Contains(aActor));
 
   mActorList.AppendElement(aActor);
 
   // Allow an actor to be added after we've entered the Notifying case.  We
   // can't stop the actor creation from racing with out destruction of the
   // other actors and we need to wait for this extra one to close as well.
   // Signal it should destroy itself right away.
   if (mNotified) {
     aActor->StartDestroy();
   }
 }
 
 void
-Feature::RemoveActor(ActorChild* aActor)
+CacheWorkerHolder::RemoveActor(ActorChild* aActor)
 {
-  NS_ASSERT_OWNINGTHREAD(Feature);
+  NS_ASSERT_OWNINGTHREAD(CacheWorkerHolder);
   MOZ_ASSERT(aActor);
 
   DebugOnly<bool> removed = mActorList.RemoveElement(aActor);
 
   MOZ_ASSERT(removed);
   MOZ_ASSERT(!mActorList.Contains(aActor));
 }
 
 bool
-Feature::Notified() const
+CacheWorkerHolder::Notified() const
 {
   return mNotified;
 }
 
 bool
-Feature::Notify(Status aStatus)
+CacheWorkerHolder::Notify(Status aStatus)
 {
-  NS_ASSERT_OWNINGTHREAD(Feature);
+  NS_ASSERT_OWNINGTHREAD(CacheWorkerHolder);
 
   // When the service worker thread is stopped we will get Terminating,
   // but nothing higher than that.  We must shut things down at Terminating.
   if (aStatus < Terminating || mNotified) {
     return true;
   }
 
   mNotified = true;
@@ -85,26 +86,24 @@ Feature::Notify(Status aStatus)
   // into RemoveActor() once the actor is destroyed.
   for (uint32_t i = 0; i < mActorList.Length(); ++i) {
     mActorList[i]->StartDestroy();
   }
 
   return true;
 }
 
-Feature::Feature(WorkerPrivate* aWorkerPrivate)
+CacheWorkerHolder::CacheWorkerHolder(WorkerPrivate* aWorkerPrivate)
   : mWorkerPrivate(aWorkerPrivate)
   , mNotified(false)
 {
   MOZ_ASSERT(mWorkerPrivate);
 }
 
-Feature::~Feature()
+CacheWorkerHolder::~CacheWorkerHolder()
 {
-  NS_ASSERT_OWNINGTHREAD(Feature);
+  NS_ASSERT_OWNINGTHREAD(CacheWorkerHolder);
   MOZ_ASSERT(mActorList.IsEmpty());
-
-  mWorkerPrivate->RemoveFeature(this);
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
rename from dom/cache/Feature.h
rename to dom/cache/CacheWorkerHolder.h
--- a/dom/cache/Feature.h
+++ b/dom/cache/CacheWorkerHolder.h
@@ -1,54 +1,55 @@
 /* -*- 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_cache_Feature_h
-#define mozilla_dom_cache_Feature_h
+#ifndef mozilla_dom_cache_CacheWorkerHolder_h
+#define mozilla_dom_cache_CacheWorkerHolder_h
 
 #include "nsISupportsImpl.h"
 #include "nsTArray.h"
-#include "WorkerFeature.h"
+#include "WorkerHolder.h"
 
 namespace mozilla {
 
 namespace workers {
 class WorkerPrivate;
 } // namespace workers
 
 namespace dom {
 namespace cache {
 
 class ActorChild;
 
-class Feature final : public workers::WorkerFeature
+class CacheWorkerHolder final : public workers::WorkerHolder
 {
 public:
-  static already_AddRefed<Feature> Create(workers::WorkerPrivate* aWorkerPrivate);
+  static already_AddRefed<CacheWorkerHolder>
+  Create(workers::WorkerPrivate* aWorkerPrivate);
 
   void AddActor(ActorChild* aActor);
   void RemoveActor(ActorChild* aActor);
 
   bool Notified() const;
 
-  // WorkerFeature methods
+  // WorkerHolder methods
   virtual bool Notify(workers::Status aStatus) override;
 
 private:
-  explicit Feature(workers::WorkerPrivate *aWorkerPrivate);
-  ~Feature();
+  explicit CacheWorkerHolder(workers::WorkerPrivate *aWorkerPrivate);
+  ~CacheWorkerHolder();
 
   workers::WorkerPrivate* mWorkerPrivate;
   nsTArray<ActorChild*> mActorList;
   bool mNotified;
 
 public:
-  NS_INLINE_DECL_REFCOUNTING(mozilla::dom::cache::Feature)
+  NS_INLINE_DECL_REFCOUNTING(mozilla::dom::cache::CacheWorkerHolder)
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_cache_Feature_h
+#endif // mozilla_dom_cache_CacheWorkerHolder_h
--- a/dom/cache/Connection.h
+++ b/dom/cache/Connection.h
@@ -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/. */
 
 #ifndef mozilla_dom_cache_Connection_h
 #define mozilla_dom_cache_Connection_h
 
 #include "mozIStorageConnection.h"
+#include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 class Connection final : public mozIStorageConnection
 {
 public:
--- a/dom/cache/moz.build
+++ b/dom/cache/moz.build
@@ -14,21 +14,21 @@ EXPORTS.mozilla.dom.cache += [
     'CacheOpChild.h',
     'CacheOpParent.h',
     'CacheParent.h',
     'CacheStorage.h',
     'CacheStorageChild.h',
     'CacheStorageParent.h',
     'CacheStreamControlChild.h',
     'CacheStreamControlParent.h',
+    'CacheWorkerHolder.h',
     'Connection.h',
     'Context.h',
     'DBAction.h',
     'DBSchema.h',
-    'Feature.h',
     'FileUtils.h',
     'IPCUtils.h',
     'Manager.h',
     'ManagerId.h',
     'PrincipalVerifier.h',
     'QuotaClient.h',
     'ReadStream.h',
     'SavedTypes.h',
@@ -47,21 +47,21 @@ UNIFIED_SOURCES += [
     'CacheOpChild.cpp',
     'CacheOpParent.cpp',
     'CacheParent.cpp',
     'CacheStorage.cpp',
     'CacheStorageChild.cpp',
     'CacheStorageParent.cpp',
     'CacheStreamControlChild.cpp',
     'CacheStreamControlParent.cpp',
+    'CacheWorkerHolder.cpp',
     'Connection.cpp',
     'Context.cpp',
     'DBAction.cpp',
     'DBSchema.cpp',
-    'Feature.cpp',
     'FileUtils.cpp',
     'Manager.cpp',
     'ManagerId.cpp',
     'PrincipalVerifier.cpp',
     'QuotaClient.cpp',
     'ReadStream.cpp',
     'StreamControl.cpp',
     'StreamList.cpp',
--- a/dom/canvas/WebGLContextLossHandler.cpp
+++ b/dom/canvas/WebGLContextLossHandler.cpp
@@ -102,17 +102,17 @@ ContextLossWorkerRunnable::Cancel()
 // -------------------------------------------------------------------
 
 WebGLContextLossHandler::WebGLContextLossHandler(WebGLContext* webgl)
     : mWeakWebGL(webgl)
     , mTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
     , mIsTimerRunning(false)
     , mShouldRunTimerAgain(false)
     , mIsDisabled(false)
-    , mFeatureAdded(false)
+    , mWorkerHolderAdded(false)
 #ifdef DEBUG
     , mThread(NS_GetCurrentThread())
 #endif
 {
 }
 
 WebGLContextLossHandler::~WebGLContextLossHandler()
 {
@@ -181,19 +181,19 @@ WebGLContextLossHandler::RunTimer()
         return;
     }
 
     if (!NS_IsMainThread()) {
         dom::workers::WorkerPrivate* workerPrivate =
             dom::workers::GetCurrentThreadWorkerPrivate();
         nsCOMPtr<nsIEventTarget> target = workerPrivate->GetEventTarget();
         mTimer->SetTarget(new ContextLossWorkerEventTarget(target));
-        if (!mFeatureAdded) {
-            workerPrivate->AddFeature(this);
-            mFeatureAdded = true;
+        if (!mWorkerHolderAdded) {
+            HoldWorker(workerPrivate);
+            mWorkerHolderAdded = true;
         }
     }
 
     StartTimer(1000);
 
     mIsTimerRunning = true;
     mShouldRunTimerAgain = false;
 }
@@ -201,22 +201,22 @@ WebGLContextLossHandler::RunTimer()
 void
 WebGLContextLossHandler::DisableTimer()
 {
     if (mIsDisabled)
         return;
 
     mIsDisabled = true;
 
-    if (mFeatureAdded) {
+    if (mWorkerHolderAdded) {
         dom::workers::WorkerPrivate* workerPrivate =
             dom::workers::GetCurrentThreadWorkerPrivate();
         MOZ_RELEASE_ASSERT(workerPrivate, "GFX: No private worker created.");
-        workerPrivate->RemoveFeature(this);
-        mFeatureAdded = false;
+        ReleaseWorker();
+        mWorkerHolderAdded = false;
     }
 
     // We can't just Cancel() the timer, as sometimes we end up
     // receiving a callback after calling Cancel(). This could cause us
     // to receive the callback after object destruction.
 
     // Instead, we let the timer finish, but ignore it.
 
--- a/dom/canvas/WebGLContextLossHandler.h
+++ b/dom/canvas/WebGLContextLossHandler.h
@@ -5,32 +5,32 @@
 
 #ifndef WEBGL_CONTEXT_LOSS_HANDLER_H_
 #define WEBGL_CONTEXT_LOSS_HANDLER_H_
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/WeakPtr.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsImpl.h"
-#include "WorkerFeature.h"
+#include "WorkerHolder.h"
 
 class nsIThread;
 class nsITimer;
 
 namespace mozilla {
 class WebGLContext;
 
-class WebGLContextLossHandler : public dom::workers::WorkerFeature
+class WebGLContextLossHandler : public dom::workers::WorkerHolder
 {
     WeakPtr<WebGLContext> mWeakWebGL;
     nsCOMPtr<nsITimer> mTimer;
     bool mIsTimerRunning;
     bool mShouldRunTimerAgain;
     bool mIsDisabled;
-    bool mFeatureAdded;
+    bool mWorkerHolderAdded;
 #ifdef DEBUG
     nsIThread* mThread;
 #endif
 
 public:
     NS_INLINE_DECL_REFCOUNTING(WebGLContextLossHandler)
 
     explicit WebGLContextLossHandler(WebGLContext* webgl);
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -309,17 +309,17 @@ public:
     JS_ClearPendingException(mCx);
   }
 
 private:
   JSContext* mCx;
 };
 
 class ConsoleRunnable : public Runnable
-                      , public WorkerFeature
+                      , public WorkerHolder
                       , public StructuredCloneHolderBase
 {
 public:
   explicit ConsoleRunnable(Console* aConsole)
     : mWorkerPrivate(GetCurrentThreadWorkerPrivate())
     , mConsole(aConsole)
   {
     MOZ_ASSERT(mWorkerPrivate);
@@ -377,17 +377,17 @@ private:
   DispatchInternal(JSContext* aCx)
   {
     mWorkerPrivate->AssertIsOnWorkerThread();
 
     if (NS_WARN_IF(!PreDispatch(aCx))) {
       return false;
     }
 
-    if (NS_WARN_IF(!mWorkerPrivate->AddFeature(this))) {
+    if (NS_WARN_IF(!HoldWorker(mWorkerPrivate))) {
       return false;
     }
 
     if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) {
       return false;
     }
 
     return true;
@@ -422,17 +422,17 @@ private:
       WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override
       {
         MOZ_ASSERT(aWorkerPrivate);
         aWorkerPrivate->AssertIsOnWorkerThread();
 
         mRunnable->ReleaseData();
         mRunnable->mConsole = nullptr;
 
-        aWorkerPrivate->RemoveFeature(mRunnable);
+        mRunnable->ReleaseWorker();
         return true;
       }
 
     private:
       ~ConsoleReleaseRunnable()
       {}
     };
 
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -224,17 +224,17 @@ FetchRequest(nsIGlobalObject* aGlobal, c
     Telemetry::Accumulate(Telemetry::FETCH_IS_MAINTHREAD, 0);
 
     if (worker->IsServiceWorker()) {
       r->SetSkipServiceWorker();
     }
 
     RefPtr<WorkerFetchResolver> resolver = WorkerFetchResolver::Create(worker, p);
     if (!resolver) {
-      NS_WARNING("Could not add WorkerFetchResolver feature to worker");
+      NS_WARNING("Could not add WorkerFetchResolver workerHolder to worker");
       aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
       return nullptr;
     }
 
     RefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(resolver, r);
     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(run));
   }
 
@@ -400,17 +400,17 @@ WorkerFetchResolver::OnResponseEnd()
 
   RefPtr<WorkerFetchResponseEndRunnable> r =
     new WorkerFetchResponseEndRunnable(mPromiseProxy);
 
   if (!r->Dispatch()) {
     RefPtr<WorkerFetchResponseEndControlRunnable> cr =
       new WorkerFetchResponseEndControlRunnable(mPromiseProxy);
     // This can fail if the worker thread is canceled or killed causing
-    // the PromiseWorkerProxy to give up its WorkerFeature immediately,
+    // the PromiseWorkerProxy to give up its WorkerHolder immediately,
     // allowing the worker thread to become Dead.
     if (!cr->Dispatch()) {
       NS_WARNING("Failed to dispatch WorkerFetchResponseEndControlRunnable");
     }
   }
 }
 
 namespace {
@@ -748,17 +748,17 @@ public:
     if (mFetchBody->mWorkerPrivate) {
       RefPtr<ContinueConsumeBodyRunnable<Derived>> r =
         new ContinueConsumeBodyRunnable<Derived>(mFetchBody,
                                         aStatus,
                                         aResultLength,
                                         nonconstResult);
       if (!r->Dispatch()) {
         // XXXcatalinb: The worker is shutting down, the pump will be canceled
-        // by FetchBodyFeature::Notify.
+        // by FetchBodyWorkerHolder::Notify.
         NS_WARNING("Could not dispatch ConsumeBodyRunnable");
         // Return failure so that aResult is freed.
         return NS_ERROR_FAILURE;
       }
     } else {
       mFetchBody->ContinueConsumeBody(aStatus, aResultLength, nonconstResult);
     }
 
@@ -814,46 +814,46 @@ public:
   {
     mBody->CancelPump();
     return true;
   }
 };
 } // namespace
 
 template <class Derived>
-class FetchBodyFeature final : public workers::WorkerFeature
+class FetchBodyWorkerHolder final : public workers::WorkerHolder
 {
-  // This is addrefed before the feature is created, and is released in ContinueConsumeBody()
-  // so we can hold a rawptr.
+  // This is addrefed before the workerHolder is created, and is released in
+  // ContinueConsumeBody() so we can hold a rawptr.
   FetchBody<Derived>* mBody;
   bool mWasNotified;
 
 public:
-  explicit FetchBodyFeature(FetchBody<Derived>* aBody)
+  explicit FetchBodyWorkerHolder(FetchBody<Derived>* aBody)
     : mBody(aBody)
     , mWasNotified(false)
   { }
 
-  ~FetchBodyFeature()
+  ~FetchBodyWorkerHolder()
   { }
 
   bool Notify(workers::Status aStatus) override
   {
     MOZ_ASSERT(aStatus > workers::Running);
     if (!mWasNotified) {
       mWasNotified = true;
       mBody->ContinueConsumeBody(NS_BINDING_ABORTED, 0, nullptr);
     }
     return true;
   }
 };
 
 template <class Derived>
 FetchBody<Derived>::FetchBody()
-  : mFeature(nullptr)
+  : mWorkerHolder(nullptr)
   , mBodyUsed(false)
 #ifdef DEBUG
   , mReadDone(false)
 #endif
 {
   if (!NS_IsMainThread()) {
     mWorkerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(mWorkerPrivate);
@@ -870,75 +870,75 @@ FetchBody<Response>::FetchBody();
 
 template <class Derived>
 FetchBody<Derived>::~FetchBody()
 {
 }
 
 // Returns true if addref succeeded.
 // Always succeeds on main thread.
-// May fail on worker if RegisterFeature() fails. In that case, it will release
-// the object before returning false.
+// May fail on worker if RegisterWorkerHolder() fails. In that case, it will
+// release the object before returning false.
 template <class Derived>
 bool
 FetchBody<Derived>::AddRefObject()
 {
   AssertIsOnTargetThread();
   DerivedClass()->AddRef();
 
-  if (mWorkerPrivate && !mFeature) {
-    if (!RegisterFeature()) {
+  if (mWorkerPrivate && !mWorkerHolder) {
+    if (!RegisterWorkerHolder()) {
       ReleaseObject();
       return false;
     }
   }
   return true;
 }
 
 template <class Derived>
 void
 FetchBody<Derived>::ReleaseObject()
 {
   AssertIsOnTargetThread();
 
-  if (mWorkerPrivate && mFeature) {
-    UnregisterFeature();
+  if (mWorkerPrivate && mWorkerHolder) {
+    UnregisterWorkerHolder();
   }
 
   DerivedClass()->Release();
 }
 
 template <class Derived>
 bool
-FetchBody<Derived>::RegisterFeature()
+FetchBody<Derived>::RegisterWorkerHolder()
 {
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(!mFeature);
-  mFeature = new FetchBodyFeature<Derived>(this);
+  MOZ_ASSERT(!mWorkerHolder);
+  mWorkerHolder = new FetchBodyWorkerHolder<Derived>(this);
 
-  if (!mWorkerPrivate->AddFeature(mFeature)) {
-    NS_WARNING("Failed to add feature");
-    mFeature = nullptr;
+  if (!mWorkerHolder->HoldWorker(mWorkerPrivate)) {
+    NS_WARNING("Failed to add workerHolder");
+    mWorkerHolder = nullptr;
     return false;
   }
 
   return true;
 }
 
 template <class Derived>
 void
-FetchBody<Derived>::UnregisterFeature()
+FetchBody<Derived>::UnregisterWorkerHolder()
 {
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(mFeature);
+  MOZ_ASSERT(mWorkerHolder);
 
-  mWorkerPrivate->RemoveFeature(mFeature);
-  mFeature = nullptr;
+  mWorkerHolder->ReleaseWorker();
+  mWorkerHolder = nullptr;
 }
 
 template <class Derived>
 void
 FetchBody<Derived>::CancelPump()
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(mConsumeBodyPump);
@@ -947,17 +947,17 @@ FetchBody<Derived>::CancelPump()
 
 // Return value is used by ConsumeBody to bubble the error code up to WebIDL so
 // mConsumePromise doesn't have to be rejected on early exit.
 template <class Derived>
 nsresult
 FetchBody<Derived>::BeginConsumeBody()
 {
   AssertIsOnTargetThread();
-  MOZ_ASSERT(!mFeature);
+  MOZ_ASSERT(!mWorkerHolder);
   MOZ_ASSERT(mConsumePromise);
 
   // The FetchBody is not thread-safe refcounted. We addref it here and release
   // it once the stream read is finished.
   if (!AddRefObject()) {
     return NS_ERROR_FAILURE;
   }
 
@@ -1031,17 +1031,17 @@ template <class Derived>
 void
 FetchBody<Derived>::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength, uint8_t* aResult)
 {
   AssertIsOnTargetThread();
   // Just a precaution to ensure ContinueConsumeBody is not called out of
   // sync with a body read.
   MOZ_ASSERT(mBodyUsed);
   MOZ_ASSERT(!mReadDone);
-  MOZ_ASSERT_IF(mWorkerPrivate, mFeature);
+  MOZ_ASSERT_IF(mWorkerPrivate, mWorkerHolder);
 #ifdef DEBUG
   mReadDone = true;
 #endif
 
   AutoFreeBuffer autoFree(aResult);
 
   MOZ_ASSERT(mConsumePromise);
   RefPtr<Promise> localPromise = mConsumePromise.forget();
--- a/dom/fetch/Fetch.h
+++ b/dom/fetch/Fetch.h
@@ -15,17 +15,17 @@
 #include "nsError.h"
 #include "nsProxyRelease.h"
 #include "nsString.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/RequestBinding.h"
-#include "mozilla/dom/workers/bindings/WorkerFeature.h"
+#include "mozilla/dom/workers/bindings/WorkerHolder.h"
 
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
 class InternalRequest;
@@ -58,17 +58,17 @@ ExtractByteStreamFromBody(const OwningAr
  * Non-owning version.
  */
 nsresult
 ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
                           nsCString& aContentType,
                           uint64_t& aContentLength);
 
-template <class Derived> class FetchBodyFeature;
+template <class Derived> class FetchBodyWorkerHolder;
 
 /*
  * FetchBody's body consumption uses nsIInputStreamPump to read from the
  * underlying stream to a block of memory, which is then adopted by
  * ContinueConsumeBody() and converted to the right type based on the JS
  * function called.
  *
  * Use of the nsIInputStreamPump complicates things on the worker thread.
@@ -150,17 +150,17 @@ public:
     mBodyUsed = true;
   }
 
   // Always set whenever the FetchBody is created on the worker thread.
   workers::WorkerPrivate* mWorkerPrivate;
 
   // Set when consuming the body is attempted on a worker.
   // Unset when consumption is done/aborted.
-  nsAutoPtr<workers::WorkerFeature> mFeature;
+  nsAutoPtr<workers::WorkerHolder> mWorkerHolder;
 
 protected:
   FetchBody();
 
   virtual ~FetchBody();
 
   void
   SetMimeType();
@@ -188,20 +188,20 @@ private:
 
   bool
   AddRefObject();
 
   void
   ReleaseObject();
 
   bool
-  RegisterFeature();
+  RegisterWorkerHolder();
 
   void
-  UnregisterFeature();
+  UnregisterWorkerHolder();
 
   bool
   IsOnTargetThread()
   {
     return NS_IsMainThread() == !mWorkerPrivate;
   }
 
   void
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -898,17 +898,17 @@ protected:
   ~WorkerPermissionRequestChildProcessActor()
   {}
 
   virtual bool
   Recv__delete__(const uint32_t& aPermission) override;
 };
 
 class WorkerPermissionChallenge final : public Runnable
-                                      , public WorkerFeature
+                                      , public WorkerHolder
 {
 public:
   WorkerPermissionChallenge(WorkerPrivate* aWorkerPrivate,
                             BackgroundFactoryRequestChild* aActor,
                             IDBFactory* aFactory,
                             const PrincipalInfo& aPrincipalInfo)
     : mWorkerPrivate(aWorkerPrivate)
     , mActor(aActor)
@@ -958,17 +958,17 @@ public:
 
     RefPtr<IDBFactory> factory;
     mFactory.swap(factory);
 
     mActor->SendPermissionRetry();
     mActor = nullptr;
 
     mWorkerPrivate->AssertIsOnWorkerThread();
-    mWorkerPrivate->RemoveFeature(this);
+    ReleaseWorker();
   }
 
 private:
   bool
   RunInternal()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -1410,17 +1410,17 @@ BackgroundFactoryRequestChild::RecvPermi
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(workerPrivate);
     workerPrivate->AssertIsOnWorkerThread();
 
     RefPtr<WorkerPermissionChallenge> challenge =
       new WorkerPermissionChallenge(workerPrivate, this, mFactory,
                                     aPrincipalInfo);
 
-    if (NS_WARN_IF(!workerPrivate->AddFeature(challenge))) {
+    if (NS_WARN_IF(!challenge->HoldWorker(workerPrivate))) {
       return false;
     }
 
     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(challenge));
     return true;
   }
 
   nsresult rv;
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -25,17 +25,17 @@
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsIScriptContext.h"
 #include "nsJSUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsString.h"
 #include "ReportInternalError.h"
-#include "WorkerFeature.h"
+#include "WorkerHolder.h"
 #include "WorkerPrivate.h"
 
 // Include this last to avoid path problems on Windows.
 #include "ActorsChild.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -456,55 +456,51 @@ IDBRequest::PreHandleEvent(EventChainPre
 {
   AssertIsOnOwningThread();
 
   aVisitor.mCanHandle = true;
   aVisitor.mParentTarget = mTransaction;
   return NS_OK;
 }
 
-class IDBOpenDBRequest::WorkerFeature final
-  : public mozilla::dom::workers::WorkerFeature
+class IDBOpenDBRequest::WorkerHolder final
+  : public mozilla::dom::workers::WorkerHolder
 {
   WorkerPrivate* mWorkerPrivate;
 #ifdef DEBUG
   // This is only here so that assertions work in the destructor even if
-  // NoteAddFeatureFailed was called.
+  // NoteAddWorkerHolderFailed was called.
   WorkerPrivate* mWorkerPrivateDEBUG;
 #endif
 
 public:
   explicit
-  WorkerFeature(WorkerPrivate* aWorkerPrivate)
+  WorkerHolder(WorkerPrivate* aWorkerPrivate)
     : mWorkerPrivate(aWorkerPrivate)
 #ifdef DEBUG
     , mWorkerPrivateDEBUG(aWorkerPrivate)
 #endif
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
-    MOZ_COUNT_CTOR(IDBOpenDBRequest::WorkerFeature);
+    MOZ_COUNT_CTOR(IDBOpenDBRequest::WorkerHolder);
   }
 
-  ~WorkerFeature()
+  ~WorkerHolder()
   {
 #ifdef DEBUG
     mWorkerPrivateDEBUG->AssertIsOnWorkerThread();
 #endif
 
-    MOZ_COUNT_DTOR(IDBOpenDBRequest::WorkerFeature);
-
-    if (mWorkerPrivate) {
-      mWorkerPrivate->RemoveFeature(this);
-    }
+    MOZ_COUNT_DTOR(IDBOpenDBRequest::WorkerHolder);
   }
 
   void
-  NoteAddFeatureFailed()
+  NoteAddWorkerHolderFailed()
   {
     MOZ_ASSERT(mWorkerPrivate);
     mWorkerPrivate->AssertIsOnWorkerThread();
 
     mWorkerPrivate = nullptr;
   }
 
 private:
@@ -572,23 +568,23 @@ IDBOpenDBRequest::CreateForJS(JSContext*
   request->SetScriptOwner(aScriptOwner);
 
   if (!NS_IsMainThread()) {
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(workerPrivate);
 
     workerPrivate->AssertIsOnWorkerThread();
 
-    nsAutoPtr<WorkerFeature> feature(new WorkerFeature(workerPrivate));
-    if (NS_WARN_IF(!workerPrivate->AddFeature(feature))) {
-      feature->NoteAddFeatureFailed();
+    nsAutoPtr<WorkerHolder> workerHolder(new WorkerHolder(workerPrivate));
+    if (NS_WARN_IF(!workerHolder->HoldWorker(workerPrivate))) {
+      workerHolder->NoteAddWorkerHolderFailed();
       return nullptr;
     }
 
-    request->mWorkerFeature = Move(feature);
+    request->mWorkerHolder = Move(workerHolder);
   }
 
   return request.forget();
 }
 
 void
 IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction)
 {
@@ -598,21 +594,21 @@ IDBOpenDBRequest::SetTransaction(IDBTran
 
   mTransaction = aTransaction;
 }
 
 void
 IDBOpenDBRequest::NoteComplete()
 {
   AssertIsOnOwningThread();
-  MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerFeature);
+  MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerHolder);
 
-  // If we have a WorkerFeature installed on the worker then nulling this out
+  // If we have a WorkerHolder installed on the worker then nulling this out
   // will uninstall it from the worker.
-  mWorkerFeature = nullptr;
+  mWorkerHolder = nullptr;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest,
                                                   IDBRequest)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -645,17 +641,17 @@ IDBOpenDBRequest::WrapObject(JSContext* 
 {
   AssertIsOnOwningThread();
 
   return IDBOpenDBRequestBinding::Wrap(aCx, this, aGivenProto);
 }
 
 bool
 IDBOpenDBRequest::
-WorkerFeature::Notify(Status aStatus)
+WorkerHolder::Notify(Status aStatus)
 {
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(aStatus > Running);
 
   // There's nothing we can really do here at the moment...
   NS_WARNING("Worker closing but IndexedDB is waiting to open a database!");
 
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -221,22 +221,22 @@ public:
 protected:
   ResultCallback()
   { }
 };
 
 class IDBOpenDBRequest final
   : public IDBRequest
 {
-  class WorkerFeature;
+  class WorkerHolder;
 
   // Only touched on the owning thread.
   RefPtr<IDBFactory> mFactory;
 
-  nsAutoPtr<WorkerFeature> mWorkerFeature;
+  nsAutoPtr<WorkerHolder> mWorkerHolder;
 
   const bool mFileHandleDisabled;
 
 public:
   static already_AddRefed<IDBOpenDBRequest>
   CreateForWindow(JSContext* aCx,
                   IDBFactory* aFactory,
                   nsPIDOMWindowInner* aOwner,
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -17,58 +17,56 @@
 #include "mozilla/dom/DOMStringList.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "nsAutoPtr.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsTHashtable.h"
 #include "ProfilerHelpers.h"
 #include "ReportInternalError.h"
-#include "WorkerFeature.h"
+#include "WorkerHolder.h"
 #include "WorkerPrivate.h"
 
 // Include this last to avoid path problems on Windows.
 #include "ActorsChild.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::dom::workers;
 using namespace mozilla::ipc;
 
-class IDBTransaction::WorkerFeature final
-  : public mozilla::dom::workers::WorkerFeature
+class IDBTransaction::WorkerHolder final
+  : public mozilla::dom::workers::WorkerHolder
 {
   WorkerPrivate* mWorkerPrivate;
 
   // The IDBTransaction owns this object so we only need a weak reference back
   // to it.
   IDBTransaction* mTransaction;
 
 public:
-  WorkerFeature(WorkerPrivate* aWorkerPrivate, IDBTransaction* aTransaction)
+  WorkerHolder(WorkerPrivate* aWorkerPrivate, IDBTransaction* aTransaction)
     : mWorkerPrivate(aWorkerPrivate)
     , mTransaction(aTransaction)
   {
     MOZ_ASSERT(aWorkerPrivate);
     MOZ_ASSERT(aTransaction);
     aWorkerPrivate->AssertIsOnWorkerThread();
     aTransaction->AssertIsOnOwningThread();
 
-    MOZ_COUNT_CTOR(IDBTransaction::WorkerFeature);
+    MOZ_COUNT_CTOR(IDBTransaction::WorkerHolder);
   }
 
-  ~WorkerFeature()
+  ~WorkerHolder()
   {
     mWorkerPrivate->AssertIsOnWorkerThread();
 
-    MOZ_COUNT_DTOR(IDBTransaction::WorkerFeature);
-
-    mWorkerPrivate->RemoveFeature(this);
+    MOZ_COUNT_DTOR(IDBTransaction::WorkerHolder);
   }
 
 private:
   virtual bool
   Notify(Status aStatus) override;
 };
 
 IDBTransaction::IDBTransaction(IDBDatabase* aDatabase,
@@ -236,18 +234,18 @@ IDBTransaction::Create(JSContext* aCx, I
   transaction->mRegistered = true;
 
   if (!NS_IsMainThread()) {
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(workerPrivate);
 
     workerPrivate->AssertIsOnWorkerThread();
 
-    transaction->mWorkerFeature = new WorkerFeature(workerPrivate, transaction);
-    MOZ_ALWAYS_TRUE(workerPrivate->AddFeature(transaction->mWorkerFeature));
+    transaction->mWorkerHolder = new WorkerHolder(workerPrivate, transaction);
+    MOZ_ALWAYS_TRUE(transaction->mWorkerHolder->HoldWorker(workerPrivate));
   }
 
   return transaction.forget();
 }
 
 // static
 IDBTransaction*
 IDBTransaction::GetCurrent()
@@ -781,18 +779,18 @@ IDBTransaction::FireCompleteOrAbortEvent
   MOZ_ASSERT(!mFiredCompleteOrAbort);
 
   mReadyState = DONE;
 
 #ifdef DEBUG
   mFiredCompleteOrAbort = true;
 #endif
 
-  // Make sure we drop the WorkerFeature when this function completes.
-  nsAutoPtr<WorkerFeature> workerFeature = Move(mWorkerFeature);
+  // Make sure we drop the WorkerHolder when this function completes.
+  nsAutoPtr<WorkerHolder> workerHolder = Move(mWorkerHolder);
 
   nsCOMPtr<nsIDOMEvent> event;
   if (NS_SUCCEEDED(aResult)) {
     event = CreateGenericEvent(this,
                                nsDependentString(kCompleteEventType),
                                eDoesNotBubble,
                                eNotCancelable);
     MOZ_ASSERT(event);
@@ -1025,17 +1023,17 @@ IDBTransaction::Run()
     SendCommit();
   }
 
   return NS_OK;
 }
 
 bool
 IDBTransaction::
-WorkerFeature::Notify(Status aStatus)
+WorkerHolder::Notify(Status aStatus)
 {
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(aStatus > Running);
 
   if (mTransaction && aStatus > Terminating) {
     mTransaction->AssertIsOnOwningThread();
 
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -45,18 +45,18 @@ class RequestParams;
 
 class IDBTransaction final
   : public IDBWrapperCache
   , public nsIRunnable
 {
   friend class indexedDB::BackgroundCursorChild;
   friend class indexedDB::BackgroundRequestChild;
 
-  class WorkerFeature;
-  friend class WorkerFeature;
+  class WorkerHolder;
+  friend class WorkerHolder;
 
 public:
   enum Mode
   {
     READ_ONLY = 0,
     READ_WRITE,
     READ_WRITE_FLUSH,
     CLEANUP,
@@ -75,17 +75,17 @@ public:
   };
 
 private:
   RefPtr<IDBDatabase> mDatabase;
   RefPtr<DOMError> mError;
   nsTArray<nsString> mObjectStoreNames;
   nsTArray<RefPtr<IDBObjectStore>> mObjectStores;
   nsTArray<RefPtr<IDBObjectStore>> mDeletedObjectStores;
-  nsAutoPtr<WorkerFeature> mWorkerFeature;
+  nsAutoPtr<WorkerHolder> mWorkerHolder;
 
   // Tagged with mMode. If mMode is VERSION_CHANGE then mBackgroundActor will be
   // a BackgroundVersionChangeTransactionChild. Otherwise it will be a
   // BackgroundTransactionChild.
   union {
     indexedDB::BackgroundTransactionChild* mNormalBackgroundActor;
     indexedDB::BackgroundVersionChangeTransactionChild* mVersionChangeBackgroundActor;
   } mBackgroundActor;
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -190,43 +190,43 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
 
 namespace {
 
-class MessagePortFeature final : public workers::WorkerFeature
+class MessagePortWorkerHolder final : public workers::WorkerHolder
 {
   MessagePort* mPort;
 
 public:
-  explicit MessagePortFeature(MessagePort* aPort)
+  explicit MessagePortWorkerHolder(MessagePort* aPort)
     : mPort(aPort)
   {
     MOZ_ASSERT(aPort);
-    MOZ_COUNT_CTOR(MessagePortFeature);
+    MOZ_COUNT_CTOR(MessagePortWorkerHolder);
   }
 
   virtual bool Notify(workers::Status aStatus) override
   {
     if (aStatus > Running) {
       // We cannot process messages anymore because we cannot dispatch new
       // runnables. Let's force a Close().
       mPort->CloseForced();
     }
 
     return true;
   }
 
 private:
-  ~MessagePortFeature()
+  ~MessagePortWorkerHolder()
   {
-    MOZ_COUNT_DTOR(MessagePortFeature);
+    MOZ_COUNT_DTOR(MessagePortWorkerHolder);
   }
 };
 
 class ForceCloseHelper final : public nsIIPCBackgroundChildCreateCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
@@ -282,17 +282,17 @@ MessagePort::MessagePort(nsIGlobalObject
   mIdentifier = new MessagePortIdentifier();
   mIdentifier->neutered() = true;
   mIdentifier->sequenceId() = 0;
 }
 
 MessagePort::~MessagePort()
 {
   CloseForced();
-  MOZ_ASSERT(!mWorkerFeature);
+  MOZ_ASSERT(!mWorkerHolder);
 }
 
 /* static */ already_AddRefed<MessagePort>
 MessagePort::Create(nsIGlobalObject* aGlobal, const nsID& aUUID,
                     const nsID& aDestinationUUID, ErrorResult& aRv)
 {
   MOZ_ASSERT(aGlobal);
 
@@ -335,42 +335,42 @@ MessagePort::Initialize(const nsID& aUUI
   mIdentifier->uuid() = aUUID;
   mIdentifier->destinationUuid() = aDestinationUUID;
   mIdentifier->sequenceId() = aSequenceID;
 
   mState = aState;
 
   if (mNeutered) {
     // If this port is neutered we don't want to keep it alive artificially nor
-    // we want to add listeners or workerFeatures.
+    // we want to add listeners or workerWorkerHolders.
     mState = eStateDisentangled;
     return;
   }
 
   if (mState == eStateEntangling) {
     ConnectToPBackground();
   } else {
     MOZ_ASSERT(mState == eStateUnshippedEntangled);
   }
 
   // The port has to keep itself alive until it's entangled.
   UpdateMustKeepAlive();
 
   if (!NS_IsMainThread()) {
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(workerPrivate);
-    MOZ_ASSERT(!mWorkerFeature);
+    MOZ_ASSERT(!mWorkerHolder);
 
-    nsAutoPtr<WorkerFeature> feature(new MessagePortFeature(this));
-    if (NS_WARN_IF(!workerPrivate->AddFeature(feature))) {
+    nsAutoPtr<WorkerHolder> workerHolder(new MessagePortWorkerHolder(this));
+    if (NS_WARN_IF(!workerHolder->HoldWorker(workerPrivate))) {
       aRv.Throw(NS_ERROR_FAILURE);
       return;
     }
 
-    mWorkerFeature = Move(feature);
+    mWorkerHolder = Move(workerHolder);
   } else if (GetOwner()) {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(GetOwner()->IsInnerWindow());
     mInnerID = GetOwner()->WindowID();
 
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(this, "inner-window-destroyed", false);
@@ -907,23 +907,18 @@ MessagePort::ActorCreated(mozilla::ipc::
 void
 MessagePort::UpdateMustKeepAlive()
 {
   if (mState >= eStateDisentangled &&
       mMessages.IsEmpty() &&
       mIsKeptAlive) {
     mIsKeptAlive = false;
 
-    if (mWorkerFeature) {
-      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-      MOZ_ASSERT(workerPrivate);
-
-      workerPrivate->RemoveFeature(mWorkerFeature);
-      mWorkerFeature = nullptr;
-    }
+    // The DTOR of this WorkerHolder will release the worker for us.
+    mWorkerHolder = nullptr;
 
     if (NS_IsMainThread()) {
       nsCOMPtr<nsIObserverService> obs =
         do_GetService("@mozilla.org/observer-service;1");
       if (obs) {
         obs->RemoveObserver(this, "inner-window-destroyed");
       }
     }
--- a/dom/messagechannel/MessagePort.h
+++ b/dom/messagechannel/MessagePort.h
@@ -24,17 +24,17 @@ namespace dom {
 
 class MessagePortChild;
 class MessagePortIdentifier;
 class MessagePortMessage;
 class PostMessageRunnable;
 class SharedMessagePortMessage;
 
 namespace workers {
-class WorkerFeature;
+class WorkerHolder;
 } // namespace workers
 
 class MessagePort final : public DOMEventTargetHelper
                         , public nsIIPCBackgroundChildCreateCallback
                         , public nsIObserver
 {
   friend class PostMessageRunnable;
 
@@ -159,17 +159,17 @@ private:
   // We release the object when the port is closed or disentangled.
   void UpdateMustKeepAlive();
 
   bool IsCertainlyAliveForCC() const override
   {
     return mIsKeptAlive;
   }
 
-  nsAutoPtr<workers::WorkerFeature> mWorkerFeature;
+  nsAutoPtr<workers::WorkerHolder> mWorkerHolder;
 
   RefPtr<PostMessageRunnable> mPostMessageRunnable;
 
   RefPtr<MessagePortChild> mActor;
 
   RefPtr<MessagePort> mUnshippedEntangledPort;
 
   nsTArray<RefPtr<SharedMessagePortMessage>> mMessages;
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -453,19 +453,20 @@ public:
     } else {
       AssertIsOnMainThread();
     }
 
     mInited = mNotification->AddRefObject();
   }
 
   // This is only required because Gecko runs script in a worker's onclose
-  // handler (non-standard, Bug 790919) where calls to AddFeature() will fail.
-  // Due to non-standardness and added complications if we decide to support
-  // this, attempts to create a Notification in onclose just throw exceptions.
+  // handler (non-standard, Bug 790919) where calls to HoldWorker() will
+  // fail. Due to non-standardness and added complications if we decide to
+  // support this, attempts to create a Notification in onclose just throw
+  // exceptions.
   bool
   Initialized()
   {
     return mInited;
   }
 
   ~NotificationRef()
   {
@@ -1194,17 +1195,17 @@ Notification::CreateInternal(nsIGlobalOb
   return notification.forget();
 }
 
 Notification::~Notification()
 {
   mData.setUndefined();
   mozilla::DropJSObjects(this);
   AssertIsOnTargetThread();
-  MOZ_ASSERT(!mFeature);
+  MOZ_ASSERT(!mWorkerHolder);
   MOZ_ASSERT(!mTempRef);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Notification)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
   tmp->mData.setUndefined();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
@@ -2435,43 +2436,43 @@ void Notification::InitFromBase64(const 
 
   container->GetDataAsBase64(mDataAsBase64);
 }
 
 bool
 Notification::AddRefObject()
 {
   AssertIsOnTargetThread();
-  MOZ_ASSERT_IF(mWorkerPrivate && !mFeature, mTaskCount == 0);
-  MOZ_ASSERT_IF(mWorkerPrivate && mFeature, mTaskCount > 0);
-  if (mWorkerPrivate && !mFeature) {
-    if (!RegisterFeature()) {
+  MOZ_ASSERT_IF(mWorkerPrivate && !mWorkerHolder, mTaskCount == 0);
+  MOZ_ASSERT_IF(mWorkerPrivate && mWorkerHolder, mTaskCount > 0);
+  if (mWorkerPrivate && !mWorkerHolder) {
+    if (!RegisterWorkerHolder()) {
       return false;
     }
   }
   AddRef();
   ++mTaskCount;
   return true;
 }
 
 void
 Notification::ReleaseObject()
 {
   AssertIsOnTargetThread();
   MOZ_ASSERT(mTaskCount > 0);
-  MOZ_ASSERT_IF(mWorkerPrivate, mFeature);
+  MOZ_ASSERT_IF(mWorkerPrivate, mWorkerHolder);
 
   --mTaskCount;
   if (mWorkerPrivate && mTaskCount == 0) {
-    UnregisterFeature();
+    UnregisterWorkerHolder();
   }
   Release();
 }
 
-NotificationFeature::NotificationFeature(Notification* aNotification)
+NotificationWorkerHolder::NotificationWorkerHolder(Notification* aNotification)
   : mNotification(aNotification)
 {
   MOZ_ASSERT(mNotification->mWorkerPrivate);
   mNotification->mWorkerPrivate->AssertIsOnWorkerThread();
 }
 
 /*
  * Called from the worker, runs on main thread, blocks worker.
@@ -2509,17 +2510,17 @@ class CloseNotificationRunnable final
   bool
   HadObserver()
   {
     return mHadObserver;
   }
 };
 
 bool
-NotificationFeature::Notify(Status aStatus)
+NotificationWorkerHolder::Notify(Status aStatus)
 {
   if (aStatus >= Canceling) {
     // CloseNotificationRunnable blocks the worker by pushing a sync event loop
     // on the stack. Meanwhile, WorkerControlRunnables dispatched to the worker
     // can still continue running. One of these is
     // ReleaseNotificationControlRunnable that releases the notification,
     // invalidating the notification and this feature. We hold this reference to
     // keep the notification valid until we are done with it.
@@ -2552,38 +2553,36 @@ NotificationFeature::Notify(Status aStat
     // From this point we cannot touch properties of this feature because
     // ReleaseObject() may have led to the notification going away and the
     // notification owns this feature!
   }
   return true;
 }
 
 bool
-Notification::RegisterFeature()
+Notification::RegisterWorkerHolder()
 {
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(!mFeature);
-  mFeature = MakeUnique<NotificationFeature>(this);
-  bool added = mWorkerPrivate->AddFeature(mFeature.get());
-  if (!added) {
-    mFeature = nullptr;
+  MOZ_ASSERT(!mWorkerHolder);
+  mWorkerHolder = MakeUnique<NotificationWorkerHolder>(this);
+  if (NS_WARN_IF(!mWorkerHolder->HoldWorker(mWorkerPrivate))) {
+    return false;
   }
 
-  return added;
+  return true;
 }
 
 void
-Notification::UnregisterFeature()
+Notification::UnregisterWorkerHolder()
 {
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(mFeature);
-  mWorkerPrivate->RemoveFeature(mFeature.get());
-  mFeature = nullptr;
+  MOZ_ASSERT(mWorkerHolder);
+  mWorkerHolder = nullptr;
 }
 
 /*
  * Checks:
  * 1) Is aWorker allowed to show a notification for scope?
  * 2) Is aWorker an active worker?
  *
  * If it is not an active worker, Result() will be NS_ERROR_NOT_AVAILABLE.
--- a/dom/notification/Notification.h
+++ b/dom/notification/Notification.h
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_notification_h__
 #define mozilla_dom_notification_h__
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/dom/NotificationBinding.h"
-#include "mozilla/dom/workers/bindings/WorkerFeature.h"
+#include "mozilla/dom/workers/bindings/WorkerHolder.h"
 
 #include "nsIObserver.h"
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
 #include "nsWeakReference.h"
 
@@ -31,24 +31,24 @@ class NotificationRef;
 class WorkerNotificationObserver;
 class Promise;
 
 namespace workers {
   class WorkerPrivate;
 } // namespace workers
 
 class Notification;
-class NotificationFeature final : public workers::WorkerFeature
+class NotificationWorkerHolder final : public workers::WorkerHolder
 {
   // Since the feature is strongly held by a Notification, it is ok to hold
   // a raw pointer here.
   Notification* mNotification;
 
 public:
-  explicit NotificationFeature(Notification* aNotification);
+  explicit NotificationWorkerHolder(Notification* aNotification);
 
   bool
   Notify(workers::Status aStatus) override;
 };
 
 // Records telemetry probes at application startup, when a notification is
 // shown, and when the notification permission is revoked for a site.
 class NotificationTelemetryService final : public nsIObserver
@@ -443,23 +443,23 @@ private:
   void
   SetAlertName();
 
   bool IsTargetThread() const
   {
     return NS_IsMainThread() == !mWorkerPrivate;
   }
 
-  bool RegisterFeature();
-  void UnregisterFeature();
+  bool RegisterWorkerHolder();
+  void UnregisterWorkerHolder();
 
   nsresult ResolveIconAndSoundURL(nsString&, nsString&);
 
   // Only used for Notifications on Workers, worker thread only.
-  UniquePtr<NotificationFeature> mFeature;
+  UniquePtr<NotificationWorkerHolder> mWorkerHolder;
   // Target thread only.
   uint32_t mTaskCount;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_notification_h__
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -2522,17 +2522,17 @@ Promise::AppendCallbacks(PromiseCallback
     // RunResolveTask() yet, so we may never have called
     // `PromiseDebugging:AddUncaughtRejection`.
   }
   mIsLastInChain = false;
 
 #if defined(DOM_PROMISE_DEPRECATED_REPORTING)
   // Now that there is a callback, we don't need to report anymore.
   mHadRejectCallback = true;
-  RemoveFeature();
+  RemoveWorkerHolder();
 #endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
 
   mResolveCallbacks.AppendElement(aResolveCallback);
   mRejectCallbacks.AppendElement(aRejectCallback);
 
   // If promise's state is fulfilled, queue a task to process our fulfill
   // callbacks with promise's result. If promise's state is rejected, queue a
   // task to process our reject callbacks with promise's result.
@@ -2762,20 +2762,19 @@ Promise::Settle(JS::Handle<JS::Value> aV
   // watch for thread shutdown.
   if (aState == PromiseState::Rejected &&
       !mHadRejectCallback &&
       !NS_IsMainThread()) {
     workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(worker);
     worker->AssertIsOnWorkerThread();
 
-    mFeature = new PromiseReportRejectFeature(this);
-    if (NS_WARN_IF(!worker->AddFeature(mFeature))) {
-      // To avoid a false RemoveFeature().
-      mFeature = nullptr;
+    mWorkerHolder = new PromiseReportRejectWorkerHolder(this);
+    if (NS_WARN_IF(!mWorkerHolder->HoldWorker(worker))) {
+      mWorkerHolder = nullptr;
       // Worker is shutting down, report rejection immediately since it is
       // unlikely that reject callbacks will be added after this point.
       MaybeReportRejectedOnce();
     }
   }
 #endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
 
   TriggerPromiseReactions();
@@ -2814,34 +2813,30 @@ Promise::TriggerPromiseReactions()
     RefPtr<PromiseReactionJob> task =
       new PromiseReactionJob(this, callbacks[i], mResult);
     runtime->DispatchToMicroTask(task);
   }
 }
 
 #if defined(DOM_PROMISE_DEPRECATED_REPORTING)
 void
-Promise::RemoveFeature()
+Promise::RemoveWorkerHolder()
 {
   NS_ASSERT_OWNINGTHREAD(Promise);
 
-  if (mFeature) {
-    workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(worker);
-    worker->RemoveFeature(mFeature);
-    mFeature = nullptr;
-  }
+  // The DTOR of this WorkerHolder will release the worker for us.
+  mWorkerHolder = nullptr;
 }
 
 bool
-PromiseReportRejectFeature::Notify(workers::Status aStatus)
+PromiseReportRejectWorkerHolder::Notify(workers::Status aStatus)
 {
   MOZ_ASSERT(aStatus > workers::Running);
   mPromise->MaybeReportRejectedOnce();
-  // After this point, `this` has been deleted by RemoveFeature!
+  // After this point, `this` has been deleted by RemoveWorkerHolder!
   return true;
 }
 #endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
 
 bool
 Promise::CaptureStack(JSContext* aCx, JS::Heap<JSObject*>& aTarget)
 {
   NS_ASSERT_OWNINGTHREAD(Promise);
@@ -2966,25 +2961,25 @@ PromiseWorkerProxy::PromiseWorkerProxy(w
                                        Promise* aWorkerPromise,
                                        const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks)
   : mWorkerPrivate(aWorkerPrivate)
   , mWorkerPromise(aWorkerPromise)
   , mCleanedUp(false)
   , mCallbacks(aCallbacks)
   , mCleanUpLock("cleanUpLock")
 #ifdef DEBUG
-  , mFeatureAdded(false)
+  , mWorkerHolderAdded(false)
 #endif
 {
 }
 
 PromiseWorkerProxy::~PromiseWorkerProxy()
 {
   MOZ_ASSERT(mCleanedUp);
-  MOZ_ASSERT(!mFeatureAdded);
+  MOZ_ASSERT(!mWorkerHolderAdded);
   MOZ_ASSERT(!mWorkerPromise);
   MOZ_ASSERT(!mWorkerPrivate);
 }
 
 void
 PromiseWorkerProxy::CleanProperties()
 {
 #ifdef DEBUG
@@ -3002,23 +2997,23 @@ PromiseWorkerProxy::CleanProperties()
   Clear();
 }
 
 bool
 PromiseWorkerProxy::AddRefObject()
 {
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(!mFeatureAdded);
-  if (!mWorkerPrivate->AddFeature(this)) {
+  MOZ_ASSERT(!mWorkerHolderAdded);
+  if (NS_WARN_IF(!HoldWorker(mWorkerPrivate))) {
     return false;
   }
 
 #ifdef DEBUG
-  mFeatureAdded = true;
+  mWorkerHolderAdded = true;
 #endif
   // Maintain a reference so that we have a valid object to clean up when
   // removing the feature.
   AddRef();
   return true;
 }
 
 workers::WorkerPrivate*
@@ -3027,17 +3022,17 @@ PromiseWorkerProxy::GetWorkerPrivate() c
 #ifdef DEBUG
   if (NS_IsMainThread()) {
     mCleanUpLock.AssertCurrentThreadOwns();
   }
 #endif
   // Safe to check this without a lock since we assert lock ownership on the
   // main thread above.
   MOZ_ASSERT(!mCleanedUp);
-  MOZ_ASSERT(mFeatureAdded);
+  MOZ_ASSERT(mWorkerHolderAdded);
 
   return mWorkerPrivate;
 }
 
 Promise*
 PromiseWorkerProxy::WorkerPromise() const
 {
 #ifdef DEBUG
@@ -3111,31 +3106,32 @@ PromiseWorkerProxy::Notify(Status aStatu
 void
 PromiseWorkerProxy::CleanUp()
 {
   // Can't release Mutex while it is still locked, so scope the lock.
   {
     MutexAutoLock lock(Lock());
 
     // |mWorkerPrivate| is not safe to use anymore if we have already
-    // cleaned up and RemoveFeature(), so we need to check |mCleanedUp| first.
+    // cleaned up and RemoveWorkerHolder(), so we need to check |mCleanedUp|
+    // first.
     if (CleanedUp()) {
       return;
     }
 
     MOZ_ASSERT(mWorkerPrivate);
     mWorkerPrivate->AssertIsOnWorkerThread();
 
     // Release the Promise and remove the PromiseWorkerProxy from the features of
     // the worker thread since the Promise has been resolved/rejected or the
     // worker thread has been cancelled.
-    MOZ_ASSERT(mFeatureAdded);
-    mWorkerPrivate->RemoveFeature(this);
+    MOZ_ASSERT(mWorkerHolderAdded);
+    ReleaseWorker();
 #ifdef DEBUG
-    mFeatureAdded = false;
+    mWorkerHolderAdded = false;
 #endif
     CleanProperties();
   }
   Release();
 }
 
 JSObject*
 PromiseWorkerProxy::CustomReadHandler(JSContext* aCx,
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -24,17 +24,17 @@
 // Bug 1083361 introduces a new mechanism for tracking uncaught
 // rejections. This #define serves to track down the parts of code
 // that need to be removed once clients have been put together
 // to take advantage of the new mechanism. New code should not
 // depend on code #ifdefed to this #define.
 #define DOM_PROMISE_DEPRECATED_REPORTING !SPIDERMONKEY_PROMISE
 
 #if defined(DOM_PROMISE_DEPRECATED_REPORTING)
-#include "mozilla/dom/workers/bindings/WorkerFeature.h"
+#include "mozilla/dom/workers/bindings/WorkerHolder.h"
 #endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
 
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class AnyCallback;
@@ -43,24 +43,25 @@ class MediaStreamError;
 class PromiseCallback;
 class PromiseInit;
 class PromiseNativeHandler;
 class PromiseDebugging;
 
 class Promise;
 
 #if defined(DOM_PROMISE_DEPRECATED_REPORTING)
-class PromiseReportRejectFeature : public workers::WorkerFeature
+class PromiseReportRejectWorkerHolder : public workers::WorkerHolder
 {
-  // PromiseReportRejectFeature is held by an nsAutoPtr on the Promise which
-  // means that this object will be destroyed before the Promise is destroyed.
+  // PromiseReportRejectWorkerHolder is held by an nsAutoPtr on the Promise
+  // which means that this object will be destroyed before the Promise is
+  // destroyed.
   Promise* MOZ_NON_OWNING_REF mPromise;
 
 public:
-  explicit PromiseReportRejectFeature(Promise* aPromise)
+  explicit PromiseReportRejectWorkerHolder(Promise* aPromise)
     : mPromise(aPromise)
   {
     MOZ_ASSERT(mPromise);
   }
 
   virtual bool
   Notify(workers::Status aStatus) override;
 };
@@ -79,17 +80,17 @@ class Promise : public nsISupports,
 #endif // SPIDERMONKEY_PROMISE
                 public SupportsWeakPtr<Promise>
 {
   friend class NativePromiseCallback;
   friend class PromiseReactionJob;
   friend class PromiseResolverTask;
   friend class PromiseTask;
 #if defined(DOM_PROMISE_DEPRECATED_REPORTING)
-  friend class PromiseReportRejectFeature;
+  friend class PromiseReportRejectWorkerHolder;
 #endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
   friend class PromiseWorkerProxy;
   friend class PromiseWorkerProxyRunnable;
   friend class RejectPromiseCallback;
   friend class ResolvePromiseCallback;
   friend class PromiseResolveThenableJob;
   friend class FastPromiseResolveThenableJob;
   friend class WrapperPromiseCallback;
@@ -404,17 +405,17 @@ private:
 #if defined(DOM_PROMISE_DEPRECATED_REPORTING)
   // If we have been rejected and our mResult is a JS exception,
   // report it to the error console.
   // Use MaybeReportRejectedOnce() for actual calls.
   void MaybeReportRejected();
 
   void MaybeReportRejectedOnce() {
     MaybeReportRejected();
-    RemoveFeature();
+    RemoveWorkerHolder();
     mResult.setUndefined();
   }
 #endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
 
   void MaybeResolveInternal(JSContext* aCx,
                             JS::Handle<JS::Value> aValue);
   void MaybeRejectInternal(JSContext* aCx,
                            JS::Handle<JS::Value> aValue);
@@ -459,17 +460,17 @@ private:
 
   static JSObject*
   CreateFunction(JSContext* aCx, Promise* aPromise, int32_t aTask);
 
   static JSObject*
   CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTask);
 
 #if defined(DOM_PROMISE_DEPRECATED_REPORTING)
-  void RemoveFeature();
+  void RemoveWorkerHolder();
 #endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
 
   // Capture the current stack and store it in aTarget.  If false is
   // returned, an exception is presumably pending on aCx.
   bool CaptureStack(JSContext* aCx, JS::Heap<JSObject*>& aTarget);
 #endif // SPIDERMONKEY_PROMISE
 
   void HandleException(JSContext* aCx);
@@ -498,17 +499,17 @@ private:
 
 #if defined(DOM_PROMISE_DEPRECATED_REPORTING)
   bool mHadRejectCallback;
 
   // If a rejected promise on a worker has no reject callbacks attached, it
   // needs to know when the worker is shutting down, to report the error on the
   // console before the worker's context is deleted. This feature is used for
   // that purpose.
-  nsAutoPtr<PromiseReportRejectFeature> mFeature;
+  nsAutoPtr<PromiseReportRejectWorkerHolder> mWorkerHolder;
 #endif // defined(DOM_PROMISE_DEPRECATED_REPORTING)
 
   bool mTaskPending;
   bool mResolvePending;
 
   // `true` if this Promise is the last in the chain, or `false` if
   // another Promise has been created from this one by a call to
   // `then`, `all`, `race`, etc.
--- a/dom/promise/PromiseWorkerProxy.h
+++ b/dom/promise/PromiseWorkerProxy.h
@@ -6,17 +6,17 @@
 
 #ifndef mozilla_dom_PromiseWorkerProxy_h
 #define mozilla_dom_PromiseWorkerProxy_h
 
 // Required for Promise::PromiseTaskSync.
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/StructuredCloneHolder.h"
-#include "mozilla/dom/workers/bindings/WorkerFeature.h"
+#include "mozilla/dom/workers/bindings/WorkerHolder.h"
 #include "nsProxyRelease.h"
 
 #include "WorkerRunnable.h"
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
@@ -105,17 +105,17 @@ class WorkerPrivate;
 //        }
 //
 // Note: If a PromiseWorkerProxy is not cleaned up by a WorkerRunnable - this
 // can happen if the main thread Promise is never fulfilled - it will
 // stay alive till the worker reaches a Canceling state, even if all external
 // references to it are dropped.
 
 class PromiseWorkerProxy : public PromiseNativeHandler
-                         , public workers::WorkerFeature
+                         , public workers::WorkerHolder
                          , public StructuredCloneHolderBase
 {
   friend class PromiseWorkerProxyRunnable;
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
 public:
   typedef JSObject* (*ReadCallbackOp)(JSContext* aCx,
@@ -224,15 +224,15 @@ private:
   // which can be added by calling StoreISupports() on the main thread.
   nsTArray<nsMainThreadPtrHandle<nsISupports>> mSupportsArray;
 
   // Ensure the worker and the main thread won't race to access |mCleanedUp|.
   Mutex mCleanUpLock;
 
 #ifdef DEBUG
   // Maybe get rid of this entirely and rely on mCleanedUp
-  bool mFeatureAdded;
+  bool mWorkerHolderAdded;
 #endif
 };
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PromiseWorkerProxy_h
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -53,17 +53,17 @@
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/InternalResponse.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/UniquePtr.h"
 #include "Principal.h"
-#include "WorkerFeature.h"
+#include "WorkerHolder.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
 #define MAX_CONCURRENT_SCRIPTS 1000
 
 USING_WORKERS_NAMESPACE
 
@@ -533,17 +533,17 @@ private:
   ~LoaderListener() {}
 
   RefPtr<ScriptLoaderRunnable> mRunnable;
   uint32_t mIndex;
 };
 
 NS_IMPL_ISUPPORTS(LoaderListener, nsIStreamLoaderObserver, nsIRequestObserver)
 
-class ScriptLoaderRunnable final : public WorkerFeature
+class ScriptLoaderRunnable final : public WorkerHolder
                                  , public nsIRunnable
 {
   friend class ScriptExecutorRunnable;
   friend class CachePromiseHandler;
   friend class CacheScriptLoader;
   friend class LoaderListener;
 
   WorkerPrivate* mWorkerPrivate;
@@ -1941,17 +1941,17 @@ ScriptExecutorRunnable::ShutdownScriptLo
         LogExceptionToConsole(aCx, aWorkerPrivate);
         mScriptLoader.mRv.Throw(NS_ERROR_DOM_NETWORK_ERR);
       }
     } else {
       mScriptLoader.mRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     }
   }
 
-  aWorkerPrivate->RemoveFeature(&mScriptLoader);
+  mScriptLoader.ReleaseWorker();
   aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, aResult);
 }
 
 void
 ScriptExecutorRunnable::LogExceptionToConsole(JSContext* aCx,
                                               WorkerPrivate* aWorkerPrivate)
 {
   aWorkerPrivate->AssertIsOnWorkerThread();
@@ -1993,25 +1993,25 @@ LoadAllScripts(WorkerPrivate* aWorkerPri
 
   RefPtr<ScriptLoaderRunnable> loader =
     new ScriptLoaderRunnable(aWorkerPrivate, syncLoop.EventTarget(),
                              aLoadInfos, aIsMainScript, aWorkerScriptType,
                              aRv);
 
   NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!");
 
-  if (!aWorkerPrivate->AddFeature(loader)) {
+  if (NS_WARN_IF(!loader->HoldWorker(aWorkerPrivate))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   if (NS_FAILED(NS_DispatchToMainThread(loader))) {
     NS_ERROR("Failed to dispatch!");
 
-    aWorkerPrivate->RemoveFeature(loader);
+    loader->ReleaseWorker();
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   syncLoop.Run();
 }
 
 } /* anonymous namespace */
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -207,50 +207,50 @@ namespace {
 // rejected.
 class KeepAliveHandler final
 {
   // Use an internal class to listen for the promise resolve/reject
   // callbacks.  This class also registers a feature so that it can
   // preemptively cleanup if the service worker is timed out and
   // terminated.
   class InternalHandler final : public PromiseNativeHandler
-                              , public WorkerFeature
+                              , public WorkerHolder
   {
     nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
 
     // Worker thread only
     WorkerPrivate* mWorkerPrivate;
     RefPtr<Promise> mPromise;
-    bool mFeatureAdded;
+    bool mWorkerHolderAdded;
 
     ~InternalHandler()
     {
       MaybeCleanup();
     }
 
     bool
-    AddFeature()
+    UseWorkerHolder()
     {
       MOZ_ASSERT(mWorkerPrivate);
       mWorkerPrivate->AssertIsOnWorkerThread();
-      MOZ_ASSERT(!mFeatureAdded);
-      mFeatureAdded = mWorkerPrivate->AddFeature(this);
-      return mFeatureAdded;
+      MOZ_ASSERT(!mWorkerHolderAdded);
+      mWorkerHolderAdded = HoldWorker(mWorkerPrivate);
+      return mWorkerHolderAdded;
     }
 
     void
     MaybeCleanup()
     {
       MOZ_ASSERT(mWorkerPrivate);
       mWorkerPrivate->AssertIsOnWorkerThread();
       if (!mPromise) {
         return;
       }
-      if (mFeatureAdded) {
-        mWorkerPrivate->RemoveFeature(this);
+      if (mWorkerHolderAdded) {
+        ReleaseWorker();
       }
       mPromise = nullptr;
       mKeepAliveToken = nullptr;
     }
 
     void
     ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
     {
@@ -280,34 +280,34 @@ class KeepAliveHandler final
     }
 
     InternalHandler(const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken,
                     WorkerPrivate* aWorkerPrivate,
                     Promise* aPromise)
       : mKeepAliveToken(aKeepAliveToken)
       , mWorkerPrivate(aWorkerPrivate)
       , mPromise(aPromise)
-      , mFeatureAdded(false)
+      , mWorkerHolderAdded(false)
     {
       MOZ_ASSERT(mKeepAliveToken);
       MOZ_ASSERT(mWorkerPrivate);
       MOZ_ASSERT(mPromise);
     }
 
   public:
     static already_AddRefed<InternalHandler>
     Create(const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken,
            WorkerPrivate* aWorkerPrivate,
            Promise* aPromise)
     {
       RefPtr<InternalHandler> ref = new InternalHandler(aKeepAliveToken,
                                                         aWorkerPrivate,
                                                         aPromise);
 
-      if (NS_WARN_IF(!ref->AddFeature())) {
+      if (NS_WARN_IF(!ref->UseWorkerHolder())) {
         return nullptr;
       }
 
       return ref.forget();
     }
 
     NS_DECL_ISUPPORTS
   };
@@ -513,17 +513,17 @@ private:
 };
 
 /*
  * Used to handle ExtendableEvent::waitUntil() and catch abnormal worker
  * termination during the execution of life cycle events. It is responsible
  * with advancing the job queue for install/activate tasks.
  */
 class LifeCycleEventWatcher final : public PromiseNativeHandler,
-                                    public WorkerFeature
+                                    public WorkerHolder
 {
   WorkerPrivate* mWorkerPrivate;
   RefPtr<LifeCycleEventCallback> mCallback;
   bool mDone;
 
   ~LifeCycleEventWatcher()
   {
     if (mDone) {
@@ -559,17 +559,17 @@ public:
 
     // We need to listen for worker termination in case the event handler
     // never completes or never resolves the waitUntil promise. There are
     // two possible scenarios:
     // 1. The keepAlive token expires and the worker is terminated, in which
     //    case the registration/update promise will be rejected
     // 2. A new service worker is registered which will terminate the current
     //    installing worker.
-    if (NS_WARN_IF(!mWorkerPrivate->AddFeature(this))) {
+    if (NS_WARN_IF(!HoldWorker(mWorkerPrivate))) {
       NS_WARNING("LifeCycleEventWatcher failed to add feature.");
       ReportResult(false);
       return false;
     }
 
     return true;
   }
 
@@ -597,17 +597,17 @@ public:
     mDone = true;
 
     mCallback->SetResult(aResult);
     nsresult rv = NS_DispatchToMainThread(mCallback);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       NS_RUNTIMEABORT("Failed to dispatch life cycle event handler.");
     }
 
-    mWorkerPrivate->RemoveFeature(this);
+    ReleaseWorker();
   }
 
   void
   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
     MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate);
     mWorkerPrivate->AssertIsOnWorkerThread();
 
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -856,17 +856,17 @@ ServiceWorkerRegistrationMainThread::Get
 
 #endif /* ! MOZ_SIMPLEPUSH */
 }
 
 ////////////////////////////////////////////////////
 // Worker Thread implementation
 
 class ServiceWorkerRegistrationWorkerThread final : public ServiceWorkerRegistration
-                                                  , public WorkerFeature
+                                                  , public WorkerHolder
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationWorkerThread,
                                            ServiceWorkerRegistration)
 
   ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate,
                                         const nsAString& aScope);
@@ -1183,17 +1183,17 @@ void
 ServiceWorkerRegistrationWorkerThread::InitListener()
 {
   MOZ_ASSERT(!mListener);
   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
 
   mListener = new WorkerListener(worker, this);
-  if (!worker->AddFeature(this)) {
+  if (!HoldWorker(worker)) {
     mListener = nullptr;
     NS_WARNING("Could not add feature");
     return;
   }
 
   RefPtr<StartListeningRunnable> r =
     new StartListeningRunnable(mListener);
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
@@ -1237,22 +1237,22 @@ public:
 void
 ServiceWorkerRegistrationWorkerThread::ReleaseListener(Reason aReason)
 {
   if (!mListener) {
     return;
   }
 
   // We can assert worker here, because:
-  // 1) We always AddFeature, so if the worker has shutdown already, we'll have
-  //    received Notify and removed it. If AddFeature had failed, mListener will
-  //    be null and we won't reach here.
+  // 1) We always HoldWorker, so if the worker has shutdown already, we'll
+  //    have received Notify and removed it. If HoldWorker had failed,
+  //    mListener will be null and we won't reach here.
   // 2) Otherwise, worker is still around even if we are going away.
   mWorkerPrivate->AssertIsOnWorkerThread();
-  mWorkerPrivate->RemoveFeature(this);
+  ReleaseWorker();
 
   mListener->ClearRegistration();
 
   if (aReason == RegistrationIsGoingAway) {
     RefPtr<AsyncStopListeningRunnable> r =
       new AsyncStopListeningRunnable(mListener);
     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
   } else if (aReason == WorkerIsGoingAway) {
--- a/dom/workers/ServiceWorkerRegistration.h
+++ b/dom/workers/ServiceWorkerRegistration.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ServiceWorkerRegistration_h
 #define mozilla_dom_ServiceWorkerRegistration_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/ServiceWorkerBinding.h"
 #include "mozilla/dom/ServiceWorkerCommon.h"
-#include "mozilla/dom/workers/bindings/WorkerFeature.h"
+#include "mozilla/dom/workers/bindings/WorkerHolder.h"
 #include "nsContentUtils.h" // Required for nsContentUtils::PushEnabled
 
 // Support for Notification API extension.
 #include "mozilla/dom/NotificationBinding.h"
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
new file mode 100644
--- /dev/null
+++ b/dom/workers/WorkerHolder.cpp
@@ -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/. */
+
+#include "WorkerHolder.h"
+#include "WorkerPrivate.h"
+
+BEGIN_WORKERS_NAMESPACE
+
+WorkerHolder::WorkerHolder()
+  : mWorkerPrivate(nullptr)
+{
+}
+
+WorkerHolder::~WorkerHolder()
+{
+  ReleaseWorkerInternal();
+  MOZ_ASSERT(mWorkerPrivate == nullptr);
+}
+
+bool
+WorkerHolder::HoldWorker(WorkerPrivate* aWorkerPrivate)
+{
+  MOZ_ASSERT(aWorkerPrivate);
+  aWorkerPrivate->AssertIsOnWorkerThread();
+
+  if (!aWorkerPrivate->AddHolder(this)) {
+    return false;
+  }
+
+  mWorkerPrivate = aWorkerPrivate;
+  return true;
+}
+
+void
+WorkerHolder::ReleaseWorker()
+{
+  MOZ_ASSERT(mWorkerPrivate);
+  ReleaseWorkerInternal();
+}
+
+void
+WorkerHolder::ReleaseWorkerInternal()
+{
+  if (mWorkerPrivate) {
+    mWorkerPrivate->RemoveHolder(this);
+    mWorkerPrivate = nullptr;
+  }
+}
+
+END_WORKERS_NAMESPACE
rename from dom/workers/WorkerFeature.h
rename to dom/workers/WorkerHolder.h
--- a/dom/workers/WorkerFeature.h
+++ b/dom/workers/WorkerHolder.h
@@ -1,16 +1,16 @@
 /* -*- 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_workerfeature_h__
-#define mozilla_dom_workers_workerfeature_h__
+#ifndef mozilla_dom_workers_WorkerHolder_h
+#define mozilla_dom_workers_WorkerHolder_h
 
 #include "mozilla/dom/workers/Workers.h"
 
 BEGIN_WORKERS_NAMESPACE
 
 /**
  * Use this chart to help figure out behavior during each of the closing
  * statuses. Details below.
@@ -64,19 +64,28 @@ enum Status
   // The application is shutting down. Setting this status causes the worker to
   // abort immediately and the close handler is never scheduled.
   Killing,
 
   // The close handler has run and the worker is effectively dead.
   Dead
 };
 
-class WorkerFeature
+class WorkerHolder
 {
 public:
-  virtual ~WorkerFeature() { }
+  WorkerHolder();
+  virtual ~WorkerHolder();
+
+  bool HoldWorker(WorkerPrivate* aWorkerPrivate);
+  void ReleaseWorker();
 
   virtual bool Notify(Status aStatus) = 0;
+
+protected:
+  void ReleaseWorkerInternal();
+
+  WorkerPrivate* MOZ_NON_OWNING_REF mWorkerPrivate;
 };
 
 END_WORKERS_NAMESPACE
 
-#endif /* mozilla_dom_workers_workerfeature_h__ */
+#endif /* mozilla_dom_workers_WorkerHolder_h */
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -97,17 +97,17 @@
 #include "Principal.h"
 #include "RuntimeService.h"
 #include "ScriptLoader.h"
 #include "ServiceWorkerEvents.h"
 #include "ServiceWorkerManager.h"
 #include "ServiceWorkerWindowClient.h"
 #include "SharedWorker.h"
 #include "WorkerDebuggerManager.h"
-#include "WorkerFeature.h"
+#include "WorkerHolder.h"
 #include "WorkerNavigator.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 #include "WorkerThread.h"
 
 // JS_MaybeGC will run once every second during normal execution.
 #define PERIODIC_GC_TIMER_DELAY_SEC 1
 
@@ -4543,17 +4543,17 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
         // The debugger queue doesn't get cleared, so we can ignore that.
       }
 
       currentStatus = mStatus;
     }
 
     // If the close handler has finished and all features are done then we can
     // kill this thread.
-    if (currentStatus != Running && !HasActiveFeatures()) {
+    if (currentStatus != Running && !HasActiveHolders()) {
       if (mCloseHandlerFinished && currentStatus != Killing) {
         NotifyInternal(aCx, Killing);
         MOZ_ASSERT(!JS_IsExceptionPending(aCx));
 
 #ifdef DEBUG
         {
           MutexAutoLock lock(mMutex);
           currentStatus = mStatus;
@@ -5247,66 +5247,66 @@ WorkerPrivate::RemoveChildWorker(ParentT
   mChildWorkers.RemoveElement(aChildWorker);
 
   if (mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(false)) {
     NS_WARNING("Failed to modify busy count!");
   }
 }
 
 bool
-WorkerPrivate::AddFeature(WorkerFeature* aFeature)
+WorkerPrivate::AddHolder(WorkerHolder* aHolder)
 {
   AssertIsOnWorkerThread();
 
   {
     MutexAutoLock lock(mMutex);
 
     if (mStatus >= Canceling) {
       return false;
     }
   }
 
-  MOZ_ASSERT(!mFeatures.Contains(aFeature), "Already know about this one!");
-
-  if (mFeatures.IsEmpty() && !ModifyBusyCountFromWorker(true)) {
+  MOZ_ASSERT(!mHolders.Contains(aHolder), "Already know about this one!");
+
+  if (mHolders.IsEmpty() && !ModifyBusyCountFromWorker(true)) {
     return false;
   }
 
-  mFeatures.AppendElement(aFeature);
+  mHolders.AppendElement(aHolder);
   return true;
 }
 
 void
-WorkerPrivate::RemoveFeature(WorkerFeature* aFeature)
+WorkerPrivate::RemoveHolder(WorkerHolder* aHolder)
 {
   AssertIsOnWorkerThread();
 
-  MOZ_ASSERT(mFeatures.Contains(aFeature), "Didn't know about this one!");
-  mFeatures.RemoveElement(aFeature);
-
-  if (mFeatures.IsEmpty() && !ModifyBusyCountFromWorker(false)) {
+  MOZ_ASSERT(mHolders.Contains(aHolder), "Didn't know about this one!");
+  mHolders.RemoveElement(aHolder);
+
+  if (mHolders.IsEmpty() && !ModifyBusyCountFromWorker(false)) {
     NS_WARNING("Failed to modify busy count!");
   }
 }
 
 void
-WorkerPrivate::NotifyFeatures(JSContext* aCx, Status aStatus)
+WorkerPrivate::NotifyHolders(JSContext* aCx, Status aStatus)
 {
   AssertIsOnWorkerThread();
   MOZ_ASSERT(!JS_IsExceptionPending(aCx));
 
   NS_ASSERTION(aStatus > Running, "Bad status!");
 
   if (aStatus >= Closing) {
     CancelAllTimeouts();
   }
 
-  nsTObserverArray<WorkerFeature*>::ForwardIterator iter(mFeatures);
+  nsTObserverArray<WorkerHolder*>::ForwardIterator iter(mHolders);
   while (iter.HasMore()) {
-    WorkerFeature* feature = iter.GetNext();
+    WorkerHolder* feature = iter.GetNext();
     if (!feature->Notify(aStatus)) {
       NS_WARNING("Failed to notify feature!");
     }
     MOZ_ASSERT(!JS_IsExceptionPending(aCx));
   }
 
   AutoTArray<ParentType*, 10> children;
   children.AppendElements(mChildWorkers);
@@ -5790,17 +5790,17 @@ WorkerPrivate::NotifyInternal(JSContext*
     mCrossThreadDispatcher = nullptr;
   }
 
   MOZ_ASSERT(previousStatus != Pending);
 
   MOZ_ASSERT(previousStatus >= Canceling || mKillTime.IsNull());
 
   // Let all our features know the new status.
-  NotifyFeatures(aCx, aStatus);
+  NotifyHolders(aCx, aStatus);
   MOZ_ASSERT(!JS_IsExceptionPending(aCx));
 
   // If this is the first time our status has changed then we need to clear the
   // main event queue.
   if (previousStatus == Running) {
     // NB: If we're in a sync loop, we can't clear the queue immediately,
     // because this is the wrong queue. So we have to defer it until later.
     if (mSyncLoopStack.Length()) {
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -25,17 +25,17 @@
 #include "nsHashKeys.h"
 #include "nsRefPtrHashtable.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "nsTObserverArray.h"
 
 #include "Queue.h"
-#include "WorkerFeature.h"
+#include "WorkerHolder.h"
 
 #ifdef XP_WIN
 #undef PostMessage
 #endif
 
 class nsIChannel;
 class nsIDocument;
 class nsIEventTarget;
@@ -857,16 +857,17 @@ private:
   void
   ReportErrorToDebuggerOnMainThread(const nsAString& aFilename,
                                     uint32_t aLineno,
                                     const nsAString& aMessage);
 };
 
 class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
 {
+  friend class WorkerHolder;
   friend class WorkerPrivateParent<WorkerPrivate>;
   typedef WorkerPrivateParent<WorkerPrivate> ParentType;
   friend class AutoSyncLoopHolder;
 
   struct TimeoutInfo;
 
   class MemoryReporter;
   friend class MemoryReporter;
@@ -892,17 +893,17 @@ class WorkerPrivate : public WorkerPriva
   nsTArray<nsCOMPtr<nsIRunnable>> mUndispatchedRunnablesForSyncLoop;
   RefPtr<WorkerThread> mThread;
   PRThread* mPRThread;
 
   // Things touched on worker thread only.
   RefPtr<WorkerGlobalScope> mScope;
   RefPtr<WorkerDebuggerGlobalScope> mDebuggerScope;
   nsTArray<ParentType*> mChildWorkers;
-  nsTObserverArray<WorkerFeature*> mFeatures;
+  nsTObserverArray<WorkerHolder*> mHolders;
   nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
   uint32_t mDebuggerEventLoopLevel;
 
   struct SyncLoopInfo
   {
     explicit SyncLoopInfo(EventTarget* aEventTarget);
 
     RefPtr<EventTarget> mEventTarget;
@@ -1070,32 +1071,16 @@ public:
   ModifyBusyCountFromWorker(bool aIncrease);
 
   bool
   AddChildWorker(ParentType* aChildWorker);
 
   void
   RemoveChildWorker(ParentType* aChildWorker);
 
-  bool
-  AddFeature(WorkerFeature* aFeature);
-
-  void
-  RemoveFeature(WorkerFeature* aFeature);
-
-  void
-  NotifyFeatures(JSContext* aCx, Status aStatus);
-
-  bool
-  HasActiveFeatures()
-  {
-    return !(mChildWorkers.IsEmpty() && mTimeouts.IsEmpty() &&
-             mFeatures.IsEmpty());
-  }
-
   void
   PostMessageToParent(JSContext* aCx,
                       JS::Handle<JS::Value> aMessage,
                       const Optional<Sequence<JS::Value>>& aTransferable,
                       ErrorResult& aRv)
   {
     PostMessageToParentInternal(aCx, aMessage, aTransferable, aRv);
   }
@@ -1437,16 +1422,32 @@ private:
   void
   InitializeGCTimers();
 
   void
   SetGCTimerMode(GCTimerMode aMode);
 
   void
   ShutdownGCTimers();
+
+  bool
+  AddHolder(WorkerHolder* aHolder);
+
+  void
+  RemoveHolder(WorkerHolder* aHolder);
+
+  void
+  NotifyHolders(JSContext* aCx, Status aStatus);
+
+  bool
+  HasActiveHolders()
+  {
+    return !(mChildWorkers.IsEmpty() && mTimeouts.IsEmpty() &&
+             mHolders.IsEmpty());
+  }
 };
 
 // This class is only used to trick the DOM bindings.  We never create
 // instances of it, and static_casting to it is fine since it doesn't add
 // anything to WorkerPrivate.
 class ChromeWorkerPrivate : public WorkerPrivate
 {
 public:
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -1680,17 +1680,17 @@ void
 XMLHttpRequest::MaybePin(ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mRooted) {
     return;
   }
 
-  if (!mWorkerPrivate->AddFeature(this)) {
+  if (!HoldWorker(mWorkerPrivate)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   NS_ADDREF_THIS();
 
   mRooted = true;
 }
@@ -1798,17 +1798,17 @@ XMLHttpRequest::DispatchPrematureAbortEv
 
 void
 XMLHttpRequest::Unpin()
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   MOZ_ASSERT(mRooted, "Mismatched calls to Unpin!");
 
-  mWorkerPrivate->RemoveFeature(this);
+  ReleaseWorker();
 
   mRooted = false;
 
   NS_RELEASE_THIS();
 }
 
 void
 XMLHttpRequest::SendInternal(SendRunnable* aRunnable,
--- a/dom/workers/XMLHttpRequest.h
+++ b/dom/workers/XMLHttpRequest.h
@@ -2,17 +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/. */
 
 #ifndef mozilla_dom_workers_xmlhttprequest_h__
 #define mozilla_dom_workers_xmlhttprequest_h__
 
-#include "mozilla/dom/workers/bindings/WorkerFeature.h"
+#include "mozilla/dom/workers/bindings/WorkerHolder.h"
 
 // Need this for XMLHttpRequestResponseType.
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 
 #include "mozilla/dom/TypedArray.h"
 
 #include "nsXMLHttpRequest.h"
 
@@ -25,17 +25,17 @@ class Blob;
 BEGIN_WORKERS_NAMESPACE
 
 class Proxy;
 class SendRunnable;
 class XMLHttpRequestUpload;
 class WorkerPrivate;
 
 class XMLHttpRequest final: public nsXHREventTarget,
-                            public WorkerFeature
+                            public WorkerHolder
 {
 public:
   struct StateData
   {
     nsString mResponseText;
     nsString mResponseURL;
     uint32_t mStatus;
     nsCString mStatusText;
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -33,17 +33,17 @@ EXPORTS.mozilla.dom.workers += [
 # Stuff needed for the bindings, not really public though.
 EXPORTS.mozilla.dom.workers.bindings += [
     'ServiceWorker.h',
     'ServiceWorkerClient.h',
     'ServiceWorkerClients.h',
     'ServiceWorkerWindowClient.h',
     'SharedWorker.h',
     'URL.h',
-    'WorkerFeature.h',
+    'WorkerHolder.h',
     'XMLHttpRequest.h',
     'XMLHttpRequestUpload.h',
 ]
 
 XPIDL_MODULE = 'dom_workers'
 
 XPIDL_SOURCES += [
     'nsIWorkerDebugger.idl',
@@ -77,16 +77,17 @@ UNIFIED_SOURCES += [
     'ServiceWorkerRegistrationInfo.cpp',
     'ServiceWorkerScriptCache.cpp',
     'ServiceWorkerUnregisterJob.cpp',
     'ServiceWorkerUpdateJob.cpp',
     'ServiceWorkerWindowClient.cpp',
     'SharedWorker.cpp',
     'URL.cpp',
     'WorkerDebuggerManager.cpp',
+    'WorkerHolder.cpp',
     'WorkerLocation.cpp',
     'WorkerNavigator.cpp',
     'WorkerPrivate.cpp',
     'WorkerRunnable.cpp',
     'WorkerScope.cpp',
     'WorkerThread.cpp',
     'XMLHttpRequest.cpp',
     'XMLHttpRequestUpload.cpp',
--- a/dom/workers/test/bug1020226_worker.js
+++ b/dom/workers/test/bug1020226_worker.js
@@ -1,11 +1,12 @@
 var p = new Promise(function(resolve, reject) {
   // This causes a runnable to be queued.
   reject(new Error());
   postMessage("loaded");
 
   // This prevents that runnable from running until the window calls terminate(),
   // at which point the worker goes into the Canceling state and then an
-  // AddFeature() is attempted, which fails, which used to result in multiple
-  // calls to the error reporter, one after the worker's context had been GCed.
+  // HoldWorker() is attempted, which fails, which used to result in
+  // multiple calls to the error reporter, one after the worker's context had
+  // been GCed.
   while (true);
 });
--- a/ipc/glue/SendStreamChild.cpp
+++ b/ipc/glue/SendStreamChild.cpp
@@ -4,59 +4,59 @@
  * 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/ipc/SendStream.h"
 
 #include "mozilla/unused.h"
 #include "mozilla/dom/PContentChild.h"
 #include "mozilla/dom/WorkerPrivate.h"
-#include "mozilla/dom/workers/bindings/WorkerFeature.h"
+#include "mozilla/dom/workers/bindings/WorkerHolder.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "nsIAsyncInputStream.h"
 #include "nsICancelableRunnable.h"
 #include "nsIRunnable.h"
 #include "nsIThread.h"
 #include "nsStreamUtils.h"
 
 namespace mozilla {
 namespace ipc {
 
 using mozilla::dom::PContentChild;
 using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
 using mozilla::dom::workers::Status;
-using mozilla::dom::workers::WorkerFeature;
+using mozilla::dom::workers::WorkerHolder;
 using mozilla::dom::workers::WorkerPrivate;
 
 namespace {
 
 class SendStreamChildImpl final : public SendStreamChild
-                                , public WorkerFeature
+                                , public WorkerHolder
 {
 public:
   explicit SendStreamChildImpl(nsIAsyncInputStream* aStream);
   ~SendStreamChildImpl();
 
   void Start() override;
   void StartDestroy() override;
 
   bool
-  AddAsWorkerFeature(dom::workers::WorkerPrivate* aWorkerPrivate);
+  AddAsWorkerHolder(dom::workers::WorkerPrivate* aWorkerPrivate);
 
 private:
   class Callback;
 
   // PSendStreamChild methods
   virtual void
   ActorDestroy(ActorDestroyReason aReason) override;
 
   virtual bool
   RecvRequestClose(const nsresult& aRv) override;
 
-  // WorkerFeature methods
+  // WorkerHolder methods
   virtual bool
   Notify(Status aStatus) override;
 
   void DoRead();
 
   void Wait();
 
   void OnStreamReady(Callback* aCallback);
@@ -88,17 +88,17 @@ public:
   {
     // any thread
     if (mOwningThread == NS_GetCurrentThread()) {
       return Run();
     }
 
     // If this fails, then it means the owning thread is a Worker that has
     // been shutdown.  Its ok to lose the event in this case because the
-    // SendStreamChild listens for this event through the Feature.
+    // SendStreamChild listens for this event through the WorkerHolder.
     nsresult rv = mOwningThread->Dispatch(this, nsIThread::DISPATCH_NORMAL);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to dispatch stream readable event to owning thread");
     }
 
     return NS_OK;
   }
 
@@ -112,17 +112,17 @@ public:
     return NS_OK;
   }
 
   nsresult
   Cancel() override
   {
     // Cancel() gets called when the Worker thread is being shutdown.  We have
     // nothing to do here because SendStreamChild handles this case via
-    // the Feature.
+    // the WorkerHolder.
     return NS_OK;
   }
 
   void
   ClearActor()
   {
     MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
     MOZ_ASSERT(mActor);
@@ -175,21 +175,21 @@ SendStreamChildImpl::Start()
 void
 SendStreamChildImpl::StartDestroy()
 {
   NS_ASSERT_OWNINGTHREAD(SendStreamChild);
   OnEnd(NS_ERROR_ABORT);
 }
 
 bool
-SendStreamChildImpl::AddAsWorkerFeature(WorkerPrivate* aWorkerPrivate)
+SendStreamChildImpl::AddAsWorkerHolder(WorkerPrivate* aWorkerPrivate)
 {
   NS_ASSERT_OWNINGTHREAD(SendStreamChild);
   MOZ_ASSERT(aWorkerPrivate);
-  bool result = aWorkerPrivate->AddFeature(this);
+  bool result = HoldWorker(aWorkerPrivate);
   if (result) {
     mWorkerPrivate = aWorkerPrivate;
   }
   return result;
 }
 
 void
 SendStreamChildImpl::ActorDestroy(ActorDestroyReason aReason)
@@ -202,17 +202,17 @@ SendStreamChildImpl::ActorDestroy(ActorD
   MOZ_ASSERT(mClosed);
 
   if (mCallback) {
     mCallback->ClearActor();
     mCallback = nullptr;
   }
 
   if (mWorkerPrivate) {
-    mWorkerPrivate->RemoveFeature(this);
+    ReleaseWorker();
     mWorkerPrivate = nullptr;
   }
 }
 
 bool
 SendStreamChildImpl::RecvRequestClose(const nsresult& aRv)
 {
   NS_ASSERT_OWNINGTHREAD(SendStreamChild);
@@ -400,17 +400,17 @@ SendStreamChild::Create(nsIAsyncInputStr
   // SendStreamChild reads in the current thread, so it is only supported
   // on non-blocking, async channels
   if (NS_WARN_IF(IsBlocking(aInputStream))) {
     return nullptr;
   }
 
   SendStreamChildImpl* actor = new SendStreamChildImpl(aInputStream);
 
-  if (workerPrivate && !actor->AddAsWorkerFeature(workerPrivate)) {
+  if (workerPrivate && !actor->AddAsWorkerHolder(workerPrivate)) {
     delete actor;
     return nullptr;
   }
 
   aManager->SendPSendStreamConstructor(actor);
   return actor;
 }