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