зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1131352 - Part 2: Add ServiceWorkerGlobalScope skipWaiting(). r=nsm, r=baku
This commit is contained in:
Родитель
1fde4d00db
Коммит
2d3a0be044
|
@ -16,6 +16,9 @@ interface ServiceWorkerGlobalScope : WorkerGlobalScope {
|
|||
readonly attribute Clients clients;
|
||||
readonly attribute ServiceWorkerRegistration registration;
|
||||
|
||||
[Throws]
|
||||
Promise<boolean> skipWaiting();
|
||||
|
||||
attribute EventHandler oninstall;
|
||||
attribute EventHandler onactivate;
|
||||
attribute EventHandler onfetch;
|
||||
|
|
|
@ -1135,8 +1135,11 @@ private:
|
|||
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
|
||||
WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
|
||||
|
||||
// FIXME(nsm): Bug 982711 Deal with activateImmediately.
|
||||
NS_WARN_IF_FALSE(!aActivateImmediately, "Immediate activation using replace() is not supported yet");
|
||||
// "If registration's waiting worker's skip waiting flag is set"
|
||||
if (mRegistration->mWaitingWorker->SkipWaitingFlag()) {
|
||||
mRegistration->PurgeActiveWorker();
|
||||
}
|
||||
|
||||
Done(NS_OK);
|
||||
// Activate() is invoked out of band of atomic.
|
||||
mRegistration->TryToActivate();
|
||||
|
@ -1466,7 +1469,7 @@ LifecycleEventWorkerRunnable::DispatchLifecycleEvent(JSContext* aCx, WorkerPriva
|
|||
void
|
||||
ServiceWorkerRegistrationInfo::TryToActivate()
|
||||
{
|
||||
if (!IsControllingDocuments()) {
|
||||
if (!IsControllingDocuments() || mWaitingWorker->SkipWaitingFlag()) {
|
||||
Activate();
|
||||
}
|
||||
}
|
||||
|
@ -1477,28 +1480,36 @@ ContinueActivateTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImme
|
|||
mRegistration->FinishActivate(aSuccess);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::PurgeActiveWorker()
|
||||
{
|
||||
nsRefPtr<ServiceWorkerInfo> exitingWorker = mActiveWorker.forget();
|
||||
if (!exitingWorker)
|
||||
return;
|
||||
|
||||
// FIXME(jaoo): Bug 1170543 - Wait for exitingWorker to finish and terminate it.
|
||||
exitingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
nsresult rv = serviceWorkerScriptCache::PurgeCache(mPrincipal,
|
||||
exitingWorker->CacheName());
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to purge the activating cache.");
|
||||
}
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->InvalidateServiceWorkerRegistrationWorker(this, WhichServiceWorker::ACTIVE_WORKER);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::Activate()
|
||||
{
|
||||
nsRefPtr<ServiceWorkerInfo> activatingWorker = mWaitingWorker;
|
||||
nsRefPtr<ServiceWorkerInfo> exitingWorker = mActiveWorker;
|
||||
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->InvalidateServiceWorkerRegistrationWorker(this, WhichServiceWorker::WAITING_WORKER | WhichServiceWorker::ACTIVE_WORKER);
|
||||
if (!activatingWorker) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (exitingWorker) {
|
||||
// FIXME(nsm): Wait for worker.
|
||||
// Terminate worker
|
||||
exitingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
nsresult rv = serviceWorkerScriptCache::PurgeCache(mPrincipal,
|
||||
exitingWorker->CacheName());
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to purge the activating cache.");
|
||||
}
|
||||
}
|
||||
PurgeActiveWorker();
|
||||
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->InvalidateServiceWorkerRegistrationWorker(this, WhichServiceWorker::WAITING_WORKER);
|
||||
|
||||
mActiveWorker = activatingWorker.forget();
|
||||
mWaitingWorker = nullptr;
|
||||
|
@ -3678,6 +3689,31 @@ ServiceWorkerManager::ClaimClients(nsIPrincipal* aPrincipal,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ServiceWorkerManager::SetSkipWaitingFlag(const nsCString& aScope,
|
||||
uint64_t aServiceWorkerID)
|
||||
{
|
||||
nsRefPtr<ServiceWorkerRegistrationInfo> registration = GetRegistration(aScope);
|
||||
if (!registration) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (registration->mInstallingWorker &&
|
||||
(registration->mInstallingWorker->ID() == aServiceWorkerID)) {
|
||||
registration->mInstallingWorker->SetSkipWaitingFlag();
|
||||
} else if (registration->mWaitingWorker &&
|
||||
(registration->mWaitingWorker->ID() == aServiceWorkerID)) {
|
||||
registration->mWaitingWorker->SetSkipWaitingFlag();
|
||||
if (registration->mWaitingWorker->State() == ServiceWorkerState::Installed) {
|
||||
registration->TryToActivate();
|
||||
}
|
||||
} else {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
|
||||
{
|
||||
|
|
|
@ -117,6 +117,9 @@ public:
|
|||
void
|
||||
Clear();
|
||||
|
||||
void
|
||||
PurgeActiveWorker();
|
||||
|
||||
void
|
||||
TryToActivate();
|
||||
|
||||
|
@ -151,6 +154,7 @@ private:
|
|||
// There is a high chance of there being at least one ServiceWorker
|
||||
// associated with this all the time.
|
||||
nsAutoTArray<ServiceWorker*, 1> mInstances;
|
||||
bool mSkipWaitingFlag;
|
||||
|
||||
~ServiceWorkerInfo()
|
||||
{ }
|
||||
|
@ -181,14 +185,27 @@ public:
|
|||
mScriptSpec = aSpec;
|
||||
}
|
||||
|
||||
explicit ServiceWorkerInfo(ServiceWorkerRegistrationInfo* aReg,
|
||||
const nsACString& aScriptSpec,
|
||||
const nsAString& aCacheName)
|
||||
bool SkipWaitingFlag() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mSkipWaitingFlag;
|
||||
}
|
||||
|
||||
void SetSkipWaitingFlag()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mSkipWaitingFlag = true;
|
||||
}
|
||||
|
||||
ServiceWorkerInfo(ServiceWorkerRegistrationInfo* aReg,
|
||||
const nsACString& aScriptSpec,
|
||||
const nsAString& aCacheName)
|
||||
: mRegistration(aReg)
|
||||
, mScriptSpec(aScriptSpec)
|
||||
, mCacheName(aCacheName)
|
||||
, mState(ServiceWorkerState::EndGuard_)
|
||||
, mServiceWorkerID(GetNextID())
|
||||
, mSkipWaitingFlag(false)
|
||||
{
|
||||
MOZ_ASSERT(mRegistration);
|
||||
MOZ_ASSERT(!aCacheName.IsEmpty());
|
||||
|
@ -338,11 +355,14 @@ public:
|
|||
nsresult
|
||||
ClaimClients(nsIPrincipal* aPrincipal, const nsCString& aScope, uint64_t aId);
|
||||
|
||||
nsresult
|
||||
SetSkipWaitingFlag(const nsCString& aScope, uint64_t aServiceWorkerID);
|
||||
|
||||
static already_AddRefed<ServiceWorkerManager>
|
||||
GetInstance();
|
||||
|
||||
void LoadRegistrations(
|
||||
const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
|
||||
void
|
||||
LoadRegistrations(const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
|
||||
|
||||
// Used by remove() and removeAll() when clearing history.
|
||||
// MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
|
||||
|
|
|
@ -487,6 +487,125 @@ ServiceWorkerGlobalScope::Registration()
|
|||
return mRegistration;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class SkipWaitingResultRunnable final : public WorkerRunnable
|
||||
{
|
||||
nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
|
||||
public:
|
||||
SkipWaitingResultRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
PromiseWorkerProxy* aPromiseProxy)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
|
||||
, mPromiseProxy(aPromiseProxy)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
Promise* promise = mPromiseProxy->GetWorkerPromise();
|
||||
MOZ_ASSERT(promise);
|
||||
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
|
||||
// Release the reference on the worker thread.
|
||||
mPromiseProxy->CleanUp(aCx);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class WorkerScopeSkipWaitingRunnable final : public nsRunnable
|
||||
{
|
||||
nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
nsCString mScope;
|
||||
|
||||
public:
|
||||
WorkerScopeSkipWaitingRunnable(PromiseWorkerProxy* aPromiseProxy,
|
||||
const nsCString& aScope)
|
||||
: mPromiseProxy(aPromiseProxy)
|
||||
, mScope(aScope)
|
||||
{
|
||||
MOZ_ASSERT(aPromiseProxy);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
MOZ_ASSERT(swm);
|
||||
|
||||
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
|
||||
if (mPromiseProxy->IsClean()) {
|
||||
return NS_OK;
|
||||
}
|
||||
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
swm->SetSkipWaitingFlag(mScope, workerPrivate->ServiceWorkerID());
|
||||
|
||||
nsRefPtr<SkipWaitingResultRunnable> runnable =
|
||||
new SkipWaitingResultRunnable(workerPrivate, mPromiseProxy);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSContext* cx = jsapi.cx();
|
||||
if (runnable->Dispatch(cx)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Dispatch to worker thread failed because the worker is shutting down.
|
||||
// Use a control runnable to release the runnable on the worker thread.
|
||||
nsRefPtr<PromiseWorkerProxyControlRunnable> releaseRunnable =
|
||||
new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy);
|
||||
|
||||
if (!releaseRunnable->Dispatch(cx)) {
|
||||
NS_RUNTIMEABORT("Failed to dispatch Claim control runnable.");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(this, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<PromiseWorkerProxy> promiseProxy =
|
||||
PromiseWorkerProxy::Create(mWorkerPrivate, promise);
|
||||
if (!promiseProxy->GetWorkerPromise()) {
|
||||
// Don't dispatch if adding the worker feature failed.
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsRefPtr<WorkerScopeSkipWaitingRunnable> runnable =
|
||||
new WorkerScopeSkipWaitingRunnable(promiseProxy,
|
||||
NS_ConvertUTF16toUTF8(mScope));
|
||||
|
||||
aRv = NS_DispatchToMainThread(runnable);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
WorkerDebuggerGlobalScope::WorkerDebuggerGlobalScope(
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
|
|
|
@ -227,6 +227,9 @@ public:
|
|||
ServiceWorkerRegistrationWorkerThread*
|
||||
Registration();
|
||||
|
||||
already_AddRefed<Promise>
|
||||
SkipWaiting(ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(activate)
|
||||
IMPL_EVENT_HANDLER(beforeevicted)
|
||||
IMPL_EVENT_HANDLER(evicted)
|
||||
|
|
|
@ -107,6 +107,8 @@ support-files =
|
|||
sw_clients/refresher_cached_compressed.html
|
||||
sw_clients/refresher_cached_compressed.html^headers^
|
||||
strict_mode_error.js
|
||||
skip_waiting_installed_worker.js
|
||||
skip_waiting_scope/index.html
|
||||
|
||||
[test_unregister.html]
|
||||
[test_installation_simple.html]
|
||||
|
@ -147,4 +149,5 @@ support-files =
|
|||
[test_app_protocol.html]
|
||||
[test_claim_fetch.html]
|
||||
[test_force_refresh.html]
|
||||
[test_skip_waiting.html]
|
||||
[test_strict_mode_error.html]
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
self.addEventListener('install', evt => {
|
||||
evt.waitUntil(self.skipWaiting());
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1131352 - Add ServiceWorkerGlobalScope skipWaiting()</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
if (!parent) {
|
||||
info("skip_waiting_scope/index.html shouldn't be launched directly!");
|
||||
}
|
||||
|
||||
navigator.serviceWorker.ready.then(function() {
|
||||
parent.postMessage("READY", "*");
|
||||
});
|
||||
|
||||
navigator.serviceWorker.oncontrollerchange = function() {
|
||||
parent.postMessage({
|
||||
event: "controllerchange",
|
||||
controllerScriptURL: navigator.serviceWorker.controller &&
|
||||
navigator.serviceWorker.controller.scriptURL
|
||||
}, "*");
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,95 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1131352 - Add ServiceWorkerGlobalScope skipWaiting()</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
var registration, iframe, content;
|
||||
|
||||
function start() {
|
||||
return navigator.serviceWorker.register("worker.js",
|
||||
{scope: "./skip_waiting_scope/"});
|
||||
}
|
||||
|
||||
function waitForActivated(swr) {
|
||||
registration = swr;
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
window.onmessage = function(e) {
|
||||
if (e.data === "READY") {
|
||||
ok(true, "Active worker is activated now");
|
||||
resolve();
|
||||
} else {
|
||||
ok(false, "Wrong value. Somenting went wrong");
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
iframe = document.createElement("iframe");
|
||||
iframe.setAttribute("src", "skip_waiting_scope/index.html");
|
||||
|
||||
content = document.getElementById("content");
|
||||
content.appendChild(iframe);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function checkWhetherItSkippedWaiting() {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
window.onmessage = function (evt) {
|
||||
if (evt.data.event === "controllerchange") {
|
||||
ok(evt.data.controllerScriptURL.match("skip_waiting_installed_worker"),
|
||||
"The controller changed after skiping the waiting step");
|
||||
resolve();
|
||||
} else {
|
||||
ok(false, "Wrong value. Somenting went wrong");
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
navigator.serviceWorker.register("skip_waiting_installed_worker.js",
|
||||
{scope: "./skip_waiting_scope/"})
|
||||
.then(swr => {
|
||||
registration = swr;
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function clean() {
|
||||
content.removeChild(iframe);
|
||||
|
||||
return registration.unregister();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
start()
|
||||
.then(waitForActivated)
|
||||
.then(checkWhetherItSkippedWaiting)
|
||||
.then(clean)
|
||||
.catch(function(e) {
|
||||
ok(false, "Some test failed with error " + e);
|
||||
}).then(SimpleTest.finish);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true]
|
||||
]}, runTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче