| Index: content/browser/download/save_package.cc
|
| ===================================================================
|
| --- content/browser/download/save_package.cc (revision 94123)
|
| +++ content/browser/download/save_package.cc (working copy)
|
| @@ -18,6 +18,7 @@
|
| #include "base/task.h"
|
| #include "base/threading/thread.h"
|
| #include "base/utf_string_conversions.h"
|
| +#include "chrome/browser/download/download_history.h"
|
| #include "chrome/browser/download/download_item.h"
|
| #include "chrome/browser/download/download_item_model.h"
|
| #include "chrome/browser/download/download_manager.h"
|
| @@ -36,8 +37,6 @@
|
| #include "content/browser/renderer_host/render_view_host_delegate.h"
|
| #include "content/browser/renderer_host/resource_dispatcher_host.h"
|
| #include "content/browser/tab_contents/tab_contents.h"
|
| -#include "content/common/content_notification_types.h"
|
| -#include "content/common/notification_service.h"
|
| #include "content/common/url_constants.h"
|
| #include "content/common/view_messages.h"
|
| #include "net/base/io_buffer.h"
|
| @@ -51,6 +50,46 @@
|
|
|
| namespace {
|
|
|
| +/*
|
| +Race analysis:
|
| ++ The actions that can race against each other are:
|
| + * History callback
|
| + * Completion (save page as finishes)
|
| + * Cancel (SavePackage::Stop())
|
| + * Shutdown
|
| + * Remove
|
| ++ Cancel and completion can be considered equivalent for race
|
| + processing, as they have the same effect on the download manager, and
|
| + SavePackage won't produce any more events after either of them. I'll
|
| + refer to the OR of these two events as "Finishing".
|
| ++ Anything that occurs after both History callback and Finishing is
|
| + uninteresting, as there won't be anything in the queues anymore.
|
| ++ Only the History Callback or Remove can occur after Shutdown (the
|
| + SavePackage system won't produce any more events after these two).
|
| ++ Remove does not cancel (probably not a mistake you'd make, but it
|
| + does on downloads, so I'm noting it for myself).
|
| ++ Shutdown can only occur after Finishing(Cancel) (destroying a
|
| + profile happens strictly after destroying all TabContents associated
|
| + with the profile).
|
| ++ Remove can only occur after the History Callback.
|
| +
|
| +This leads to the following possible orders of events:
|
| +-- Finishing, History Callback
|
| + Variations on the basic one. Ok.
|
| +-- Finishing(Cancel), Shutdown, History Callback
|
| + The cancel leaves the DI in the queue, shutdown removes and deletes
|
| + it, and the history callback doesn't find it, leaving it in the DB
|
| + as IN_PROGRESS, which will be turned into Cancelled on next load.
|
| + Ok.
|
| +-- History Callback, Finishing
|
| + The basic order. Ok.
|
| +-- History Callback, Remove, Finishing
|
| + This results in a lack of update of the database to indicate
|
| + that the download has completed (if the finishing action is
|
| + completed), but that's not solvable until we cancel on Remove
|
| + (which would update the DB).
|
| +*/
|
| +
|
| // A counter for uniquely identifying each save package.
|
| int g_save_package_id = 0;
|
|
|
| @@ -120,6 +159,7 @@
|
| const FilePath& directory_full_path)
|
| : TabContentsObserver(tab_contents),
|
| file_manager_(NULL),
|
| + download_manager_(NULL),
|
| download_(NULL),
|
| page_url_(GetUrlToBeSaved()),
|
| saved_main_file_path_(file_full_path),
|
| @@ -147,6 +187,7 @@
|
| SavePackage::SavePackage(TabContents* tab_contents)
|
| : TabContentsObserver(tab_contents),
|
| file_manager_(NULL),
|
| + download_manager_(NULL),
|
| download_(NULL),
|
| page_url_(GetUrlToBeSaved()),
|
| title_(tab_contents->GetTitle()),
|
| @@ -171,6 +212,7 @@
|
| const FilePath& directory_full_path)
|
| : TabContentsObserver(tab_contents),
|
| file_manager_(NULL),
|
| + download_manager_(NULL),
|
| download_(NULL),
|
| saved_main_file_path_(file_full_path),
|
| saved_main_directory_path_(directory_full_path),
|
| @@ -192,6 +234,10 @@
|
| Cancel(true);
|
| }
|
|
|
| + // We should no longer be observing the DownloadManager at this point.
|
| + CHECK(!download_manager_);
|
| + CHECK(!download_);
|
| +
|
| DCHECK(all_save_items_count_ == (waiting_item_queue_.size() +
|
| completed_count() +
|
| in_process_count()));
|
| @@ -207,9 +253,6 @@
|
| STLDeleteValues(&in_progress_items_);
|
| STLDeleteValues(&saved_failed_items_);
|
|
|
| - // The DownloadItem is owned by DownloadManager.
|
| - download_ = NULL;
|
| -
|
| file_manager_ = NULL;
|
| }
|
|
|
| @@ -265,20 +308,23 @@
|
| return false;
|
| }
|
|
|
| - // Create the fake DownloadItem and display the view.
|
| - DownloadManager* download_manager =
|
| - tab_contents()->profile()->GetDownloadManager();
|
| - download_ = new DownloadItem(download_manager,
|
| + // Get the download manager and add ourselves as an observer.
|
| + download_manager_ = tab_contents()->profile()->GetDownloadManager();
|
| + if (!download_manager_) {
|
| + NOTREACHED();
|
| + return false;
|
| + }
|
| +
|
| + // Create the download item.
|
| + download_ = new DownloadItem(download_manager_,
|
| saved_main_file_path_,
|
| page_url_,
|
| profile->IsOffTheRecord());
|
| + download_->AddObserver(this);
|
|
|
| - // Transfer the ownership to the download manager. We need the DownloadItem
|
| - // to be alive as long as the Profile is alive.
|
| - download_manager->SavePageAsDownloadStarted(download_);
|
| + // Transfer ownership to the download manager.
|
| + download_manager_->SavePageDownloadStarted(download_);
|
|
|
| - tab_contents()->OnStartDownload(download_);
|
| -
|
| // Check save type and process the save page job.
|
| if (save_type_ == SAVE_AS_COMPLETE_HTML) {
|
| // Get directory
|
| @@ -639,7 +685,10 @@
|
| wait_state_ = FAILED;
|
|
|
| // Inform the DownloadItem we have canceled whole save page job.
|
| - download_->Cancel(false);
|
| + if (download_) {
|
| + download_->Cancel(false);
|
| + FinalizeDownloadEntry();
|
| + }
|
| }
|
|
|
| void SavePackage::CheckFinish() {
|
| @@ -692,13 +741,11 @@
|
| &SaveFileManager::RemoveSavedFileFromFileMap,
|
| save_ids));
|
|
|
| - download_->OnAllDataSaved(all_save_items_count_);
|
| - download_->MarkAsComplete();
|
| -
|
| - NotificationService::current()->Notify(
|
| - content::NOTIFICATION_SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
|
| - Source<SavePackage>(this),
|
| - Details<GURL>(&page_url_));
|
| + if (download_) {
|
| + download_->OnAllDataSaved(all_save_items_count_);
|
| + download_->MarkAsComplete();
|
| + FinalizeDownloadEntry();
|
| + }
|
| }
|
|
|
| // Called for updating end state.
|
| @@ -718,7 +765,8 @@
|
|
|
| // Inform the DownloadItem to update UI.
|
| // We use the received bytes as number of saved files.
|
| - download_->Update(completed_count());
|
| + if (download_)
|
| + download_->Update(completed_count());
|
|
|
| if (save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM &&
|
| save_item->url() == page_url_ && !save_item->received_bytes()) {
|
| @@ -759,7 +807,8 @@
|
|
|
| // Inform the DownloadItem to update UI.
|
| // We use the received bytes as number of saved files.
|
| - download_->Update(completed_count());
|
| + if (download_)
|
| + download_->Update(completed_count());
|
|
|
| if (save_type_ == SAVE_AS_ONLY_HTML ||
|
| save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM) {
|
| @@ -1023,7 +1072,8 @@
|
| static_cast<int>(frames_list.size());
|
|
|
| // We use total bytes as the total number of files we want to save.
|
| - download_->set_total_bytes(all_save_items_count_);
|
| + if (download_)
|
| + download_->set_total_bytes(all_save_items_count_);
|
|
|
| if (all_save_items_count_) {
|
| // Put all sub-resources to wait list.
|
| @@ -1311,3 +1361,30 @@
|
| contents_mime_type == "text/css" ||
|
| net::IsSupportedJavascriptMimeType(contents_mime_type.c_str());
|
| }
|
| +
|
| +void SavePackage::StopObservation() {
|
| + DCHECK(download_);
|
| + DCHECK(download_manager_);
|
| +
|
| + download_->RemoveObserver(this);
|
| + download_ = NULL;
|
| + download_manager_ = NULL;
|
| +}
|
| +
|
| +void SavePackage::OnDownloadUpdated(DownloadItem* download) {
|
| + DCHECK(download_);
|
| + DCHECK(download_ == download);
|
| + DCHECK(download_manager_);
|
| +
|
| + // Check for removal.
|
| + if (download->state() == DownloadItem::REMOVING)
|
| + StopObservation();
|
| +}
|
| +
|
| +void SavePackage::FinalizeDownloadEntry() {
|
| + DCHECK(download_);
|
| + DCHECK(download_manager_);
|
| +
|
| + download_manager_->SavePageDownloadFinished(download_);
|
| + StopObservation();
|
| +}
|
|
|