Bug 1728326 - Notify WebExtensions internals when a WebExtensions background service worker is destroyed. r=asuth

This patch introduces the following changes:
- In WorkerPrivate::ExtensionAPIAllowed: removed assertion on StaticPrefs::extensions_backgroundServiceWorker_enabled_AtStartup,
  replaced with just ignoring mExtensionAPIAllowed if the feature is turned out by prefs
- Added a new CreateAndDispatchWorkerDestroyedRunnable helper function to create and dispatch
  a runnable (as a low priority one) to call a new mozIExtensionAPIRequestHandler.onExtensionWorkerDestroyed
  method.

The service worker that has been destroyed is identified by its descriptor ID (the descriptor ID is an integer,
assigned on the main process to each ServiceWorkerPrivate instance, and still available when the worker is
already being destroyed).

Differential Revision: https://phabricator.services.mozilla.com/D124697
This commit is contained in:
Luca Greco 2021-11-05 20:26:59 +00:00
Родитель b3adb549de
Коммит fe6d66f09d
7 изменённых файлов: 89 добавлений и 5 удалений

Просмотреть файл

@ -52,6 +52,7 @@
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/dom/JSExecutionManager.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/extensions/ExtensionBrowser.h" // extensions::CreateWorkerDestroyedRunnable
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "mozilla/StorageAccess.h"
#include "mozilla/StoragePrincipalHelper.h"
@ -3528,6 +3529,20 @@ void WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot) {
NS_WARNING("Failed to dispatch runnable!");
}
} else {
if (ExtensionAPIAllowed()) {
MOZ_ASSERT(IsServiceWorker());
RefPtr<Runnable> extWorkerRunnable =
extensions::CreateWorkerDestroyedRunnable(ServiceWorkerID(),
GetBaseURI());
// Dispatch as a low priority runnable.
if (NS_FAILED(
DispatchToMainThreadForMessaging(extWorkerRunnable.forget()))) {
NS_WARNING(
"Failed to dispatch runnable to notify extensions worker "
"destroyed");
}
}
// Note, this uses the lower priority DispatchToMainThreadForMessaging for
// dispatching TopLevelWorkerFinishedRunnable to the main thread so that
// other relevant runnables are guaranteed to run before it.
@ -3536,6 +3551,12 @@ void WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot) {
if (NS_FAILED(DispatchToMainThreadForMessaging(runnable.forget()))) {
NS_WARNING("Failed to dispatch runnable!");
}
// NOTE: Calling any WorkerPrivate methods (or accessing member data) after
// this point is unsafe (the TopLevelWorkerFinishedRunnable just dispatched
// may be able to call ClearSelfAndParentEventTargetRef on this
// WorkerPrivate instance and by the time we get here the WorkerPrivate
// instance destructor may have been already called).
}
}

Просмотреть файл

@ -170,11 +170,9 @@ class WorkerPrivate final : public RelativeTimeline {
}
bool ExtensionAPIAllowed() {
// This method should never be actually called if the extension background
// service worker is disabled by pref.
MOZ_ASSERT(
StaticPrefs::extensions_backgroundServiceWorker_enabled_AtStartup());
return mExtensionAPIAllowed;
return (
StaticPrefs::extensions_backgroundServiceWorker_enabled_AtStartup() &&
mExtensionAPIAllowed);
}
void SetIsDebuggerRegistered(bool aDebuggerRegistered) {

Просмотреть файл

@ -159,4 +159,13 @@ interface mozIExtensionAPIRequestHandler : nsISupports
void handleAPIRequest(in nsISupports extension,
in mozIExtensionAPIRequest apiRequest,
[optional, retval] out mozIExtensionAPIRequestResult apiRequestResult);
/**
* A method called when an extension background service worker is destroyed.
*
* @param extension An instance of the WebExtensionPolicy webidl interface.
* @param serviceWorkerDescriptorId
*/
void onExtensionWorkerDestroyed(in nsISupports extension,
in unsigned long long serviceWorkerDescriptorId);
};

Просмотреть файл

@ -625,5 +625,25 @@ void RequestWorkerRunnable::ReadResult(JSContext* aCx,
aRv.Throw(NS_ERROR_UNEXPECTED);
}
// NotifyWorkerDestroyedRunnable
nsresult NotifyWorkerDestroyedRunnable::Run() {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<WebExtensionPolicy> policy =
ExtensionPolicyService::GetSingleton().GetByURL(mSWBaseURI.get());
nsCOMPtr<mozIExtensionAPIRequestHandler> handler =
&ExtensionAPIRequestForwarder::APIRequestHandler();
MOZ_ASSERT(handler);
if (NS_FAILED(handler->OnExtensionWorkerDestroyed(policy, mSWDescriptorId))) {
NS_WARNING(
"nsIExtensionAPIRequestHandler.onExtensionWorkerDestroyed call failed");
}
return NS_OK;
}
} // namespace extensions
} // namespace mozilla

Просмотреть файл

@ -195,6 +195,29 @@ class RequestWorkerRunnable : public dom::WorkerMainThreadRunnable {
ExtensionAPIRequestForwarder* mOuterRequest;
};
class NotifyWorkerDestroyedRunnable : public Runnable {
uint64_t mSWDescriptorId;
nsCOMPtr<nsIURI> mSWBaseURI;
public:
explicit NotifyWorkerDestroyedRunnable(
const uint64_t aServiceWorkerDescriptorId,
const nsCOMPtr<nsIURI>& aWorkerBaseURI)
: Runnable("extensions::NotifyWorkerDestroyedRunnable"),
mSWDescriptorId(aServiceWorkerDescriptorId),
mSWBaseURI(aWorkerBaseURI) {
MOZ_ASSERT(mSWDescriptorId > 0);
MOZ_ASSERT(mSWBaseURI);
}
NS_IMETHOD Run() override;
NS_INLINE_DECL_REFCOUNTING_INHERITED(NotifyWorkerDestroyedRunnable, Runnable)
private:
~NotifyWorkerDestroyedRunnable() = default;
};
} // namespace extensions
} // namespace mozilla

Просмотреть файл

@ -97,6 +97,15 @@ bool ExtensionAPIAllowed(JSContext* aCx, JSObject* aGlobal) {
#endif
}
already_AddRefed<Runnable> CreateWorkerDestroyedRunnable(
const uint64_t aServiceWorkerDescriptorId,
const nsCOMPtr<nsIURI>& aWorkerBaseURI) {
RefPtr<NotifyWorkerDestroyedRunnable> runnable =
new NotifyWorkerDestroyedRunnable(aServiceWorkerDescriptorId,
aWorkerBaseURI);
return runnable.forget();
}
void ExtensionBrowser::SetLastError(JS::Handle<JS::Value> aLastError) {
mLastError.set(aLastError);
mCheckedLastError = false;

Просмотреть файл

@ -27,6 +27,10 @@ class ExtensionTest;
bool ExtensionAPIAllowed(JSContext* aCx, JSObject* aGlobal);
already_AddRefed<Runnable> CreateWorkerDestroyedRunnable(
const uint64_t aServiceWorkerDescriptorId,
const nsCOMPtr<nsIURI>& aWorkerBaseURI);
class ExtensionBrowser final : public nsISupports, public nsWrapperCache {
nsCOMPtr<nsIGlobalObject> mGlobal;
JS::Heap<JS::Value> mLastError;