In_progress_cache : Initial draft

Details:
1- Stores info into proto format and writes to a file.
2- The cache is instantiated & owned by ChromeDownloadManagerDelegate.
3- The entry is created when download starts and will be deleted as
soon as the download completes.

This is continued from this CL by shaktisahu@:
https://chromium-review.googlesource.com/c/chromium/src/+/731403.

Bug: 778425
Change-Id: I6bd3f6df2648ac7b812c464b572492fb670d32bb
Reviewed-on: https://chromium-review.googlesource.com/753537
Commit-Queue: Joy Ming <[email protected]>
Reviewed-by: David Trainor <[email protected]>
Reviewed-by: Min Qin <[email protected]>
Reviewed-by: John Abd-El-Malek <[email protected]>
Cr-Commit-Position: refs/heads/master@{#516024}
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 10b7af4..1204c531 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -40,6 +40,7 @@
     "//cc/paint",
     "//components/discardable_memory/common",
     "//components/discardable_memory/service",
+    "//components/download/downloader/in_progress",
     "//components/filesystem:lib",
     "//components/leveldb:lib",
     "//components/link_header_util",
diff --git a/content/browser/DEPS b/content/browser/DEPS
index f0b0a540..642746e 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -3,6 +3,7 @@
   # See comment in content/DEPS for which components are allowed.
   "+components/discardable_memory/common",
   "+components/discardable_memory/service",
+  "+components/download/downloader/in_progress",
   "+components/filesystem",
   "+components/leveldb",
   "+components/link_header_util",
diff --git a/content/browser/download/download_item_impl.cc b/content/browser/download/download_item_impl.cc
index 3fba819..2e15955 100644
--- a/content/browser/download/download_item_impl.cc
+++ b/content/browser/download/download_item_impl.cc
@@ -37,6 +37,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task_runner_util.h"
+#include "components/download/downloader/in_progress/in_progress_cache.h"
 #include "content/browser/download/download_create_info.h"
 #include "content/browser/download/download_file.h"
 #include "content/browser/download/download_interrupt_reasons_impl.h"
@@ -2282,6 +2283,16 @@
   download_params->set_hash_state(std::move(hash_state_));
   download_params->set_fetch_error_body(fetch_error_body_);
 
+  auto* manager_delegate = GetBrowserContext()->GetDownloadManagerDelegate();
+  if (manager_delegate) {
+    download::InProgressCache* in_progress_cache =
+        manager_delegate->GetInProgressCache();
+    download::DownloadEntry* entry =
+        in_progress_cache->RetrieveEntry(GetGuid());
+    if (entry)
+      download_params->set_request_origin(entry->request_origin);
+  }
+
   // Note that resumed downloads disallow redirects. Hence the referrer URL
   // (which is the contents of the Referer header for the last download request)
   // will only be sent to the URL returned by GetURL().
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 5492b4d..b8d5f6b 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -22,6 +22,8 @@
 #include "base/supports_user_data.h"
 #include "base/synchronization/lock.h"
 #include "build/build_config.h"
+#include "components/download/downloader/in_progress/download_entry.h"
+#include "components/download/downloader/in_progress/in_progress_cache.h"
 #include "content/browser/byte_stream.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/download/download_create_info.h"
@@ -279,6 +281,53 @@
 
 }  // namespace
 
+// Responsible for persisting the in-progress metadata associated with a
+// download.
+class InProgressDownloadObserver : public DownloadItem::Observer {
+ public:
+  explicit InProgressDownloadObserver(
+      download::InProgressCache* in_progress_cache);
+  ~InProgressDownloadObserver() override;
+
+ private:
+  // DownloadItem::Observer
+  void OnDownloadUpdated(DownloadItem* download) override;
+  void OnDownloadRemoved(DownloadItem* download) override;
+
+  // The persistent cache to store in-progress metadata.
+  download::InProgressCache* in_progress_cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(InProgressDownloadObserver);
+};
+
+InProgressDownloadObserver::InProgressDownloadObserver(
+    download::InProgressCache* in_progress_cache)
+    : in_progress_cache_(in_progress_cache) {}
+
+InProgressDownloadObserver::~InProgressDownloadObserver() = default;
+
+void InProgressDownloadObserver::OnDownloadUpdated(DownloadItem* download) {
+  // TODO(crbug.com/778425): Properly handle fail/resume/retry for downloads
+  // that are in the INTERRUPTED state for a long time.
+  switch (download->GetState()) {
+    case DownloadItem::DownloadState::COMPLETE:
+    case DownloadItem::DownloadState::CANCELLED:
+      if (in_progress_cache_)
+        in_progress_cache_->RemoveEntry(download->GetGuid());
+      break;
+    case DownloadItem::DownloadState::IN_PROGRESS:
+      // TODO(crbug.com/778425): After RetrieveEntry has been implemented, do a
+      // check to make sure the entry exists in the cache.
+      break;
+    default:
+      break;
+  }
+}
+
+void InProgressDownloadObserver::OnDownloadRemoved(DownloadItem* download) {
+  in_progress_cache_->RemoveEntry(download->GetGuid());
+}
+
 DownloadManagerImpl::DownloadManagerImpl(BrowserContext* browser_context)
     : item_factory_(new DownloadItemFactoryImpl()),
       file_factory_(new DownloadFileFactory()),
@@ -473,6 +522,16 @@
   }
 #endif
 
+  if (delegate_) {
+    if (!in_progress_download_observer_) {
+      in_progress_download_observer_.reset(
+          new InProgressDownloadObserver(delegate_->GetInProgressCache()));
+    }
+    // May already observe this item, remove observer first.
+    download->RemoveObserver(in_progress_download_observer_.get());
+    download->AddObserver(in_progress_download_observer_.get());
+  }
+
   std::unique_ptr<DownloadFile> download_file;
 
   if (info->result == DOWNLOAD_INTERRUPT_REASON_NONE) {
@@ -623,6 +682,7 @@
     int render_view_route_id,
     int render_frame_route_id,
     bool do_not_prompt_for_login) {
+  LOG(ERROR) << "BeginDownloadRequest";
   if (ResourceDispatcherHostImpl::Get()->is_shutdown())
     return DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN;
 
@@ -860,6 +920,13 @@
 void DownloadManagerImpl::BeginDownloadInternal(
     std::unique_ptr<content::DownloadUrlParameters> params,
     uint32_t id) {
+  download::InProgressCache* in_progress_cache =
+      GetBrowserContext()->GetDownloadManagerDelegate()->GetInProgressCache();
+  if (in_progress_cache) {
+    in_progress_cache->AddOrReplaceEntry(download::DownloadEntry(
+        params.get()->guid(), params.get()->request_origin()));
+  }
+
   if (base::FeatureList::IsEnabled(features::kNetworkService)) {
     std::unique_ptr<ResourceRequest> request = CreateResourceRequest(
         params.get());
diff --git a/content/browser/download/download_manager_impl.h b/content/browser/download/download_manager_impl.h
index 368a104..e73ec221 100644
--- a/content/browser/download/download_manager_impl.h
+++ b/content/browser/download/download_manager_impl.h
@@ -241,6 +241,9 @@
   // Observers that want to be notified of changes to the set of downloads.
   base::ObserverList<Observer> observers_;
 
+  // Stores information about in-progress download items.
+  std::unique_ptr<DownloadItem::Observer> in_progress_download_observer_;
+
   // The current active browser context.
   BrowserContext* browser_context_;
 
diff --git a/content/browser/download/download_manager_impl_unittest.cc b/content/browser/download/download_manager_impl_unittest.cc
index c59f97f..9d6350c 100644
--- a/content/browser/download/download_manager_impl_unittest.cc
+++ b/content/browser/download/download_manager_impl_unittest.cc
@@ -126,6 +126,10 @@
   // we don't keep dangling pointers.
   void RemoveItem(int id);
 
+  // Sets |has_observer_calls_| to reflect whether we expect to add/remove
+  // observers during the CreateActiveItem.
+  void SetHasObserverCalls(bool observer_calls);
+
   // Overridden methods from DownloadItemFactory.
   DownloadItemImpl* CreatePersistedItem(
       DownloadItemImplDelegate* delegate,
@@ -168,11 +172,13 @@
  private:
   std::map<uint32_t, MockDownloadItemImpl*> items_;
   DownloadItemImplDelegate item_delegate_;
+  bool has_observer_calls_;
 
   DISALLOW_COPY_AND_ASSIGN(MockDownloadItemFactory);
 };
 
-MockDownloadItemFactory::MockDownloadItemFactory() {}
+MockDownloadItemFactory::MockDownloadItemFactory()
+    : has_observer_calls_(false) {}
 
 MockDownloadItemFactory::~MockDownloadItemFactory() {}
 
@@ -198,6 +204,10 @@
   items_.erase(id);
 }
 
+void MockDownloadItemFactory::SetHasObserverCalls(bool has_observer_calls) {
+  has_observer_calls_ = has_observer_calls;
+}
+
 DownloadItemImpl* MockDownloadItemFactory::CreatePersistedItem(
     DownloadItemImplDelegate* delegate,
     const std::string& guid,
@@ -253,6 +263,12 @@
   // the download.
   EXPECT_CALL(*result, MockStart(_, _));
 
+  // In the StartDownload case, expect the remove/add observer calls.
+  if (has_observer_calls_) {
+    EXPECT_CALL(*result, RemoveObserver(_));
+    EXPECT_CALL(*result, AddObserver(_));
+  }
+
   return result;
 }
 
@@ -501,6 +517,10 @@
             base::Unretained(this)));
   }
 
+  void SetHasObserverCalls(bool has_observer_calls) {
+    mock_download_item_factory_->SetHasObserverCalls(has_observer_calls);
+  }
+
  protected:
   // Key test variable; we'll keep it available to sub-classes.
   std::unique_ptr<DownloadManagerImpl> download_manager_;
@@ -529,6 +549,8 @@
 
 // Confirm the appropriate invocations occur when you start a download.
 TEST_F(DownloadManagerTest, StartDownload) {
+  SetHasObserverCalls(true);
+
   std::unique_ptr<DownloadCreateInfo> info(new DownloadCreateInfo);
   std::unique_ptr<ByteStreamReader> stream(new MockByteStreamReader);
   uint32_t local_id(5);  // Random value
@@ -558,6 +580,8 @@
   download_manager_->StartDownload(std::move(info), std::move(input_stream),
                                    DownloadUrlParameters::OnStartedCallback());
   EXPECT_TRUE(download_manager_->GetDownload(local_id));
+
+  SetHasObserverCalls(false);
 }
 
 // Confirm that calling DetermineDownloadTarget behaves properly if the delegate