mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 23:05:42 +00:00
Bug 1432846 - Delay update runnables from service workers that don't control any clients. r=bkelly
This commit is contained in:
parent
549e302e25
commit
d116fe517c
@ -1947,7 +1947,7 @@ void
|
|||||||
ServiceWorkerPrivate::StoreISupports(nsISupports* aSupports)
|
ServiceWorkerPrivate::StoreISupports(nsISupports* aSupports)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
MOZ_ASSERT(mWorkerPrivate);
|
MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate);
|
||||||
MOZ_ASSERT(!mSupportsArray.Contains(aSupports));
|
MOZ_ASSERT(!mSupportsArray.Contains(aSupports));
|
||||||
|
|
||||||
mSupportsArray.AppendElement(aSupports);
|
mSupportsArray.AppendElement(aSupports);
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "nsServiceManagerUtils.h"
|
#include "nsServiceManagerUtils.h"
|
||||||
#include "ServiceWorker.h"
|
#include "ServiceWorker.h"
|
||||||
#include "ServiceWorkerManager.h"
|
#include "ServiceWorkerManager.h"
|
||||||
|
#include "ServiceWorkerPrivate.h"
|
||||||
#include "ServiceWorkerRegistration.h"
|
#include "ServiceWorkerRegistration.h"
|
||||||
|
|
||||||
#include "nsIDocument.h"
|
#include "nsIDocument.h"
|
||||||
@ -133,7 +134,7 @@ namespace {
|
|||||||
|
|
||||||
void
|
void
|
||||||
UpdateInternal(nsIPrincipal* aPrincipal,
|
UpdateInternal(nsIPrincipal* aPrincipal,
|
||||||
const nsAString& aScope,
|
const nsACString& aScope,
|
||||||
ServiceWorkerUpdateFinishCallback* aCallback)
|
ServiceWorkerUpdateFinishCallback* aCallback)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
@ -146,7 +147,7 @@ UpdateInternal(nsIPrincipal* aPrincipal,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
swm->Update(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback);
|
swm->Update(aPrincipal, aScope, aCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
|
class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
|
||||||
@ -281,12 +282,52 @@ public:
|
|||||||
|
|
||||||
class SWRUpdateRunnable final : public Runnable
|
class SWRUpdateRunnable final : public Runnable
|
||||||
{
|
{
|
||||||
|
class TimerCallback final : public nsITimerCallback
|
||||||
|
{
|
||||||
|
RefPtr<ServiceWorkerPrivate> mPrivate;
|
||||||
|
RefPtr<Runnable> mRunnable;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TimerCallback(ServiceWorkerPrivate* aPrivate,
|
||||||
|
Runnable* aRunnable)
|
||||||
|
: mPrivate(aPrivate)
|
||||||
|
, mRunnable(aRunnable)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mPrivate);
|
||||||
|
MOZ_ASSERT(aRunnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHOD
|
||||||
|
Notify(nsITimer *aTimer) override
|
||||||
|
{
|
||||||
|
mRunnable->Run();
|
||||||
|
mPrivate->RemoveISupports(aTimer);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
|
|
||||||
|
private:
|
||||||
|
~TimerCallback()
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SWRUpdateRunnable(PromiseWorkerProxy* aPromiseProxy, const nsAString& aScope)
|
explicit SWRUpdateRunnable(PromiseWorkerProxy* aPromiseProxy)
|
||||||
: Runnable("dom::SWRUpdateRunnable")
|
: Runnable("dom::SWRUpdateRunnable")
|
||||||
, mPromiseProxy(aPromiseProxy)
|
, mPromiseProxy(aPromiseProxy)
|
||||||
, mScope(aScope)
|
, mDescriptor(aPromiseProxy->GetWorkerPrivate()->GetServiceWorkerDescriptor())
|
||||||
{}
|
, mDelayed(false)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mPromiseProxy);
|
||||||
|
|
||||||
|
// This runnable is used for update calls originating from a worker thread,
|
||||||
|
// which may be delayed in some cases.
|
||||||
|
MOZ_ASSERT(mPromiseProxy->GetWorkerPrivate()->IsServiceWorker());
|
||||||
|
MOZ_ASSERT(mPromiseProxy->GetWorkerPrivate());
|
||||||
|
mPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHOD
|
NS_IMETHOD
|
||||||
Run() override
|
Run() override
|
||||||
@ -307,20 +348,65 @@ public:
|
|||||||
}
|
}
|
||||||
MOZ_ASSERT(principal);
|
MOZ_ASSERT(principal);
|
||||||
|
|
||||||
|
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||||
|
if (NS_WARN_IF(!swm)) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will delay update jobs originating from a service worker thread.
|
||||||
|
// We don't currently handle ServiceWorkerRegistration.update() from other
|
||||||
|
// worker types. Also, we assume this registration matches self.registration
|
||||||
|
// on the service worker global. This is ok for now because service worker globals
|
||||||
|
// are the only worker contexts where we expose ServiceWorkerRegistration.
|
||||||
|
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||||
|
swm->GetRegistration(principal, mDescriptor.Scope());
|
||||||
|
if (NS_WARN_IF(!registration)) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<ServiceWorkerInfo> worker = registration->GetByDescriptor(mDescriptor);
|
||||||
|
uint32_t delay = registration->GetUpdateDelay();
|
||||||
|
|
||||||
|
// if we have a timer object, it means we've already been delayed once.
|
||||||
|
if (delay && !mDelayed) {
|
||||||
|
nsCOMPtr<nsITimerCallback> cb = new TimerCallback(worker->WorkerPrivate(), this);
|
||||||
|
Result<nsCOMPtr<nsITimer>, nsresult> result =
|
||||||
|
NS_NewTimerWithCallback(cb, delay, nsITimer::TYPE_ONE_SHOT,
|
||||||
|
SystemGroup::EventTargetFor(TaskCategory::Other));
|
||||||
|
|
||||||
|
nsCOMPtr<nsITimer> timer = result.unwrapOr(nullptr);
|
||||||
|
if (NS_WARN_IF(!timer)) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDelayed = true;
|
||||||
|
// We're storing the timer object on the calling service worker's private.
|
||||||
|
// ServiceWorkerPrivate will drop the reference if the worker terminates,
|
||||||
|
// which will cancel the timer.
|
||||||
|
worker->WorkerPrivate()->StoreISupports(timer);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
RefPtr<WorkerThreadUpdateCallback> cb =
|
RefPtr<WorkerThreadUpdateCallback> cb =
|
||||||
new WorkerThreadUpdateCallback(mPromiseProxy);
|
new WorkerThreadUpdateCallback(mPromiseProxy);
|
||||||
UpdateInternal(principal, mScope, cb);
|
UpdateInternal(principal, mDescriptor.Scope(), cb);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~SWRUpdateRunnable()
|
~SWRUpdateRunnable()
|
||||||
{}
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
}
|
||||||
|
|
||||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||||
const nsString mScope;
|
const ServiceWorkerDescriptor mDescriptor;
|
||||||
|
bool mDelayed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS(SWRUpdateRunnable::TimerCallback, nsITimerCallback)
|
||||||
|
|
||||||
class UnregisterCallback final : public nsIServiceWorkerUnregisterCallback
|
class UnregisterCallback final : public nsIServiceWorkerUnregisterCallback
|
||||||
{
|
{
|
||||||
PromiseWindowProxy mPromise;
|
PromiseWindowProxy mPromise;
|
||||||
@ -533,7 +619,7 @@ ServiceWorkerRegistrationMainThread::Update(ErrorResult& aRv)
|
|||||||
|
|
||||||
RefPtr<MainThreadUpdateCallback> cb =
|
RefPtr<MainThreadUpdateCallback> cb =
|
||||||
new MainThreadUpdateCallback(mOuter->GetOwner(), promise);
|
new MainThreadUpdateCallback(mOuter->GetOwner(), promise);
|
||||||
UpdateInternal(doc->NodePrincipal(), mScope, cb);
|
UpdateInternal(doc->NodePrincipal(), NS_ConvertUTF16toUTF8(mScope), cb);
|
||||||
|
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
}
|
}
|
||||||
@ -848,7 +934,7 @@ ServiceWorkerRegistrationWorkerThread::Update(ErrorResult& aRv)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<SWRUpdateRunnable> r = new SWRUpdateRunnable(proxy, mScope);
|
RefPtr<SWRUpdateRunnable> r = new SWRUpdateRunnable(proxy);
|
||||||
MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
|
MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
|
||||||
|
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
|
@ -87,6 +87,7 @@ ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
|
|||||||
: mPrincipal(aPrincipal)
|
: mPrincipal(aPrincipal)
|
||||||
, mDescriptor(aPrincipal, aScope, aUpdateViaCache)
|
, mDescriptor(aPrincipal, aScope, aUpdateViaCache)
|
||||||
, mControlledClientsCounter(0)
|
, mControlledClientsCounter(0)
|
||||||
|
, mDelayMultiplier(0)
|
||||||
, mUpdateState(NoUpdate)
|
, mUpdateState(NoUpdate)
|
||||||
, mCreationTime(PR_Now())
|
, mCreationTime(PR_Now())
|
||||||
, mCreationTimeStamp(TimeStamp::Now())
|
, mCreationTimeStamp(TimeStamp::Now())
|
||||||
@ -709,5 +710,25 @@ ServiceWorkerRegistrationInfo::Descriptor() const
|
|||||||
return mDescriptor;
|
return mDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
ServiceWorkerRegistrationInfo::GetUpdateDelay()
|
||||||
|
{
|
||||||
|
uint32_t delay = Preferences::GetInt("dom.serviceWorkers.update_delay",
|
||||||
|
1000);
|
||||||
|
// This can potentially happen if you spam registration->Update(). We don't
|
||||||
|
// want to wrap to a lower value.
|
||||||
|
if (mDelayMultiplier >= INT_MAX / (delay ? delay : 1)) {
|
||||||
|
return INT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay *= mDelayMultiplier;
|
||||||
|
|
||||||
|
if (!mControlledClientsCounter && mDelayMultiplier < (INT_MAX / 30)) {
|
||||||
|
mDelayMultiplier = (mDelayMultiplier ? mDelayMultiplier : 1) * 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
return delay;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -23,6 +23,7 @@ class ServiceWorkerRegistrationInfo final
|
|||||||
nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> mListeners;
|
nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> mListeners;
|
||||||
|
|
||||||
uint32_t mControlledClientsCounter;
|
uint32_t mControlledClientsCounter;
|
||||||
|
uint32_t mDelayMultiplier;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
@ -94,6 +95,7 @@ public:
|
|||||||
StartControllingClient()
|
StartControllingClient()
|
||||||
{
|
{
|
||||||
++mControlledClientsCounter;
|
++mControlledClientsCounter;
|
||||||
|
mDelayMultiplier = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -211,6 +213,9 @@ public:
|
|||||||
const ServiceWorkerRegistrationDescriptor&
|
const ServiceWorkerRegistrationDescriptor&
|
||||||
Descriptor() const;
|
Descriptor() const;
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
GetUpdateDelay();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Roughly equivalent to [[Update Registration State algorithm]]. Make sure
|
// Roughly equivalent to [[Update Registration State algorithm]]. Make sure
|
||||||
// this is called *before* updating SW instances' state, otherwise they
|
// this is called *before* updating SW instances' state, otherwise they
|
||||||
|
@ -174,6 +174,10 @@ pref("dom.serviceWorkers.idle_timeout", 30000);
|
|||||||
// The amount of time (milliseconds) service workers can be kept running using waitUntil promises.
|
// The amount of time (milliseconds) service workers can be kept running using waitUntil promises.
|
||||||
pref("dom.serviceWorkers.idle_extended_timeout", 300000);
|
pref("dom.serviceWorkers.idle_extended_timeout", 300000);
|
||||||
|
|
||||||
|
// The amount of time (milliseconds) an update request is delayed when triggered
|
||||||
|
// by a service worker that doesn't control any clients.
|
||||||
|
pref("dom.serviceWorkers.update_delay", 1000);
|
||||||
|
|
||||||
// Enable test for 24 hours update, service workers will always treat last update check time is over 24 hours
|
// Enable test for 24 hours update, service workers will always treat last update check time is over 24 hours
|
||||||
pref("dom.serviceWorkers.testUpdateOverOneDay", false);
|
pref("dom.serviceWorkers.testUpdateOverOneDay", false);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user