[Background Fetch] Cleanup fetch data on browser startup.

A browser might shutdown, for example, when a request moves to the
active state, but before we delete the pending entry.
This CL modifes the initialization DatabaseTask to delete such
inconsistencies.

Change-Id: Icefcac3789d299c83bbda2b7b1dc117ca4e1a5e0
Reviewed-on: https://chromium-review.googlesource.com/1140164
Commit-Queue: Rayan Kanso <rayankans@chromium.org>
Reviewed-by: Peter Beverloo <peter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#576169}
diff --git a/content/browser/background_fetch/storage/get_initialization_data_task.cc b/content/browser/background_fetch/storage/get_initialization_data_task.cc
index fdba52b..9b47c3d8 100644
--- a/content/browser/background_fetch/storage/get_initialization_data_task.cc
+++ b/content/browser/background_fetch/storage/get_initialization_data_task.cc
@@ -106,23 +106,27 @@
   base::WeakPtrFactory<GetTitleTask> weak_factory_;  // Keep as last.
 };
 
-// Fills the BackgroundFetchInitializationData with the number of completed
-// requests.
-class GetCompletedRequestsTask : public InitializationSubTask {
+// Gets the number of completed fetches, the number of active fetches,
+// and deletes inconsistencies in state transitions.
+// 1. Get all completed requests.
+// 2. Delete matching active requests.
+// 3. Get active requests.
+// 4. Delete matching pending requests.
+class GetRequestsTask : public InitializationSubTask {
  public:
-  GetCompletedRequestsTask(DatabaseTaskHost* host,
-                           const SubTaskInit& sub_task_init,
-                           base::OnceClosure done_closure)
+  GetRequestsTask(DatabaseTaskHost* host,
+                  const SubTaskInit& sub_task_init,
+                  base::OnceClosure done_closure)
       : InitializationSubTask(host, sub_task_init, std::move(done_closure)),
         weak_factory_(this) {}
 
-  ~GetCompletedRequestsTask() override = default;
+  ~GetRequestsTask() override = default;
 
   void Start() override {
     service_worker_context()->GetRegistrationUserDataByKeyPrefix(
         sub_task_init().service_worker_registration_id,
         CompletedRequestKeyPrefix(sub_task_init().unique_id),
-        base::BindOnce(&GetCompletedRequestsTask::DidGetCompletedRequests,
+        base::BindOnce(&GetRequestsTask::DidGetCompletedRequests,
                        weak_factory_.GetWeakPtr()));
   }
 
@@ -139,36 +143,34 @@
     }
 
     sub_task_init().initialization_data->num_completed_requests = data.size();
-    FinishWithError(blink::mojom::BackgroundFetchError::NONE);
-  }
 
-  base::WeakPtrFactory<GetCompletedRequestsTask>
-      weak_factory_;  // Keep as last.
-};
+    std::vector<std::string> active_requests_to_delete;
+    active_requests_to_delete.reserve(data.size());
+    for (const std::string& serialized_completed_request : data) {
+      proto::BackgroundFetchCompletedRequest completed_request;
+      if (!completed_request.ParseFromString(serialized_completed_request) ||
+          sub_task_init().unique_id != completed_request.unique_id()) {
+        *sub_task_init().error =
+            blink::mojom::BackgroundFetchError::STORAGE_ERROR;
+        continue;
+      }
+      active_requests_to_delete.push_back(ActiveRequestKey(
+          completed_request.unique_id(), completed_request.request_index()));
+    }
 
-// Fills the BackgroundFetchInitializationData with the guids of active
-// (previously started) requests.
-class GetActiveRequestsTask : public InitializationSubTask {
- public:
-  GetActiveRequestsTask(DatabaseTaskHost* host,
-                        const SubTaskInit& sub_task_init,
-                        base::OnceClosure done_closure)
-      : InitializationSubTask(host, sub_task_init, std::move(done_closure)),
-        weak_factory_(this) {}
+    if (active_requests_to_delete.empty()) {
+      DidClearActiveRequests(blink::ServiceWorkerStatusCode::kOk);
+      return;
+    }
 
-  ~GetActiveRequestsTask() override = default;
-
-  void Start() override {
-    service_worker_context()->GetRegistrationUserDataByKeyPrefix(
+    service_worker_context()->ClearRegistrationUserData(
         sub_task_init().service_worker_registration_id,
-        ActiveRequestKeyPrefix(sub_task_init().unique_id),
-        base::BindOnce(&GetActiveRequestsTask::DidGetActiveRequests,
+        std::move(active_requests_to_delete),
+        base::BindOnce(&GetRequestsTask::DidClearActiveRequests,
                        weak_factory_.GetWeakPtr()));
   }
 
- private:
-  void DidGetActiveRequests(const std::vector<std::string>& data,
-                            blink::ServiceWorkerStatusCode status) {
+  void DidClearActiveRequests(blink::ServiceWorkerStatusCode status) {
     switch (ToDatabaseStatus(status)) {
       case DatabaseStatus::kFailed:
         FinishWithError(blink::mojom::BackgroundFetchError::STORAGE_ERROR);
@@ -178,6 +180,26 @@
         break;
     }
 
+    service_worker_context()->GetRegistrationUserDataByKeyPrefix(
+        sub_task_init().service_worker_registration_id,
+        ActiveRequestKeyPrefix(sub_task_init().unique_id),
+        base::BindOnce(&GetRequestsTask::DidGetRemainingActiveRequests,
+                       weak_factory_.GetWeakPtr()));
+  }
+
+  void DidGetRemainingActiveRequests(const std::vector<std::string>& data,
+                                     blink::ServiceWorkerStatusCode status) {
+    switch (ToDatabaseStatus(status)) {
+      case DatabaseStatus::kFailed:
+        FinishWithError(blink::mojom::BackgroundFetchError::STORAGE_ERROR);
+        return;
+      case DatabaseStatus::kNotFound:
+      case DatabaseStatus::kOk:
+        break;
+    }
+
+    std::vector<std::string> pending_requests_to_delete;
+    pending_requests_to_delete.reserve(data.size());
     for (const std::string& serialized_active_request : data) {
       proto::BackgroundFetchActiveRequest active_request;
       if (!active_request.ParseFromString(serialized_active_request)) {
@@ -188,14 +210,38 @@
       DCHECK_EQ(sub_task_init().unique_id, active_request.unique_id());
       sub_task_init().initialization_data->active_fetch_guids.push_back(
           active_request.download_guid());
+      pending_requests_to_delete.push_back(PendingRequestKey(
+          active_request.unique_id(), active_request.request_index()));
+    }
+
+    if (pending_requests_to_delete.empty()) {
+      DidClearPendingRequests(blink::ServiceWorkerStatusCode::kOk);
+      return;
+    }
+
+    service_worker_context()->ClearRegistrationUserData(
+        sub_task_init().service_worker_registration_id,
+        std::move(pending_requests_to_delete),
+        base::BindOnce(&GetRequestsTask::DidClearPendingRequests,
+                       weak_factory_.GetWeakPtr()));
+  }
+
+  void DidClearPendingRequests(blink::ServiceWorkerStatusCode status) {
+    switch (ToDatabaseStatus(status)) {
+      case DatabaseStatus::kFailed:
+        FinishWithError(blink::mojom::BackgroundFetchError::STORAGE_ERROR);
+        return;
+      case DatabaseStatus::kNotFound:
+      case DatabaseStatus::kOk:
+        break;
     }
 
     FinishWithError(blink::mojom::BackgroundFetchError::NONE);
   }
 
-  base::WeakPtrFactory<GetActiveRequestsTask> weak_factory_;  // Keep as last.
+  base::WeakPtrFactory<GetRequestsTask> weak_factory_;  // Keep as last.
 
-  DISALLOW_COPY_AND_ASSIGN(GetActiveRequestsTask);
+  DISALLOW_COPY_AND_ASSIGN(GetRequestsTask);
 };
 
 // Deserializes the icon and creates an SkBitmap from it.
@@ -371,25 +417,21 @@
   void Start() override {
     // We need 3 queries to get the initialization data. These are wrapped
     // in a BarrierClosure to avoid querying them serially.
-    // 1. Metadata
-    // 2. Active Requests
-    // 3. Completed Requests
-    // 4. UI Title
+    // 1. Metadata (+ icon deserialization)
+    // 2. Request statuses and state sanitization
+    // 3. UI Title
     base::RepeatingClosure barrier_closure = base::BarrierClosure(
-        4u,
+        3u,
         base::BindOnce(
             [](base::WeakPtr<FillBackgroundFetchInitializationDataTask> task) {
               if (task)
                 task->FinishWithError(*task->sub_task_init().error);
             },
             weak_factory_.GetWeakPtr()));
-
     AddSubTask(std::make_unique<FillFromMetadataTask>(this, sub_task_init(),
                                                       barrier_closure));
-    AddSubTask(std::make_unique<GetCompletedRequestsTask>(this, sub_task_init(),
-                                                          barrier_closure));
-    AddSubTask(std::make_unique<GetActiveRequestsTask>(this, sub_task_init(),
-                                                       barrier_closure));
+    AddSubTask(std::make_unique<GetRequestsTask>(this, sub_task_init(),
+                                                 barrier_closure));
     AddSubTask(
         std::make_unique<GetTitleTask>(this, sub_task_init(), barrier_closure));
   }