1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "ServiceWorkerJobQueue.h" 8 9 #include "nsThreadUtils.h" 10 #include "ServiceWorkerJob.h" 11 #include "mozilla/dom/WorkerCommon.h" 12 13 namespace mozilla { 14 namespace dom { 15 16 class ServiceWorkerJobQueue::Callback final 17 : public ServiceWorkerJob::Callback { 18 RefPtr<ServiceWorkerJobQueue> mQueue; 19 20 ~Callback() = default; 21 22 public: Callback(ServiceWorkerJobQueue * aQueue)23 explicit Callback(ServiceWorkerJobQueue* aQueue) : mQueue(aQueue) { 24 MOZ_ASSERT(NS_IsMainThread()); 25 MOZ_ASSERT(mQueue); 26 } 27 JobFinished(ServiceWorkerJob * aJob,ErrorResult & aStatus)28 virtual void JobFinished(ServiceWorkerJob* aJob, 29 ErrorResult& aStatus) override { 30 MOZ_ASSERT(NS_IsMainThread()); 31 mQueue->JobFinished(aJob); 32 } 33 JobDiscarded(ErrorResult &)34 virtual void JobDiscarded(ErrorResult&) override { 35 // no-op; nothing to do. 36 } 37 38 NS_INLINE_DECL_REFCOUNTING(ServiceWorkerJobQueue::Callback, override) 39 }; 40 ~ServiceWorkerJobQueue()41ServiceWorkerJobQueue::~ServiceWorkerJobQueue() { 42 MOZ_ASSERT(NS_IsMainThread()); 43 MOZ_ASSERT(mJobList.IsEmpty()); 44 } 45 JobFinished(ServiceWorkerJob * aJob)46void ServiceWorkerJobQueue::JobFinished(ServiceWorkerJob* aJob) { 47 MOZ_ASSERT(NS_IsMainThread()); 48 MOZ_ASSERT(aJob); 49 50 // XXX There are some corner cases where jobs can double-complete. Until 51 // we track all these down we do a non-fatal assert in debug builds and 52 // a runtime check to verify the queue is in the correct state. 53 NS_ASSERTION(!mJobList.IsEmpty(), 54 "Job queue should contain the job that just completed."); 55 NS_ASSERTION(mJobList.SafeElementAt(0, nullptr) == aJob, 56 "Job queue should contain the job that just completed."); 57 if (NS_WARN_IF(mJobList.SafeElementAt(0, nullptr) != aJob)) { 58 return; 59 } 60 61 mJobList.RemoveElementAt(0); 62 63 if (mJobList.IsEmpty()) { 64 return; 65 } 66 67 RunJob(); 68 } 69 RunJob()70void ServiceWorkerJobQueue::RunJob() { 71 MOZ_ASSERT(NS_IsMainThread()); 72 MOZ_ASSERT(!mJobList.IsEmpty()); 73 MOZ_ASSERT(mJobList[0]->GetState() == ServiceWorkerJob::State::Initial); 74 75 RefPtr<Callback> callback = new Callback(this); 76 mJobList[0]->Start(callback); 77 } 78 ServiceWorkerJobQueue()79ServiceWorkerJobQueue::ServiceWorkerJobQueue() { 80 MOZ_ASSERT(NS_IsMainThread()); 81 } 82 ScheduleJob(ServiceWorkerJob * aJob)83void ServiceWorkerJobQueue::ScheduleJob(ServiceWorkerJob* aJob) { 84 MOZ_ASSERT(NS_IsMainThread()); 85 MOZ_ASSERT(aJob); 86 MOZ_ASSERT(!mJobList.Contains(aJob)); 87 88 if (mJobList.IsEmpty()) { 89 mJobList.AppendElement(aJob); 90 RunJob(); 91 return; 92 } 93 94 MOZ_ASSERT(mJobList[0]->GetState() == ServiceWorkerJob::State::Started); 95 96 RefPtr<ServiceWorkerJob>& tailJob = mJobList[mJobList.Length() - 1]; 97 if (!tailJob->ResultCallbacksInvoked() && aJob->IsEquivalentTo(tailJob)) { 98 tailJob->StealResultCallbacksFrom(aJob); 99 return; 100 } 101 102 mJobList.AppendElement(aJob); 103 } 104 CancelAll()105void ServiceWorkerJobQueue::CancelAll() { 106 MOZ_ASSERT(NS_IsMainThread()); 107 108 for (RefPtr<ServiceWorkerJob>& job : mJobList) { 109 job->Cancel(); 110 } 111 112 // Remove jobs that are queued but not started since they should never 113 // run after being canceled. This means throwing away all jobs except 114 // for the job at the front of the list. 115 if (!mJobList.IsEmpty()) { 116 MOZ_ASSERT(mJobList[0]->GetState() == ServiceWorkerJob::State::Started); 117 mJobList.TruncateLength(1); 118 } 119 } 120 121 } // namespace dom 122 } // namespace mozilla 123