Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(105)

Unified Diff: chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_snapshots_polling_fetcher.cc

Issue 11038063: Support chrome_to_mobile job receiving Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix format Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_snapshots_polling_fetcher.cc
diff --git a/chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_snapshots_polling_fetcher.cc b/chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_snapshots_polling_fetcher.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b0953c039ed58eae2f4c39ebfebc97a5c4824f4a
--- /dev/null
+++ b/chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_snapshots_polling_fetcher.cc
@@ -0,0 +1,531 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_snapshots_polling_fetcher.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/values.h"
+#include "chrome/common/cloud_print/cloud_print_consts.h"
+#include "chrome/common/cloud_print/cloud_print_helpers.h"
+#include "chrome/browser/chrome_to_mobile/receive/chrome_to_mobile_receive_util.h"
+
+namespace {
+
+// Max times to poll cloud print server.
+const int kMaxPollingNum = 50;
+// Minimal time to wait between polling cloud print server.
+const int kBasePollingIntervalInSecond = 2;
+// Maximal time to wait between polling cloud print server.
+const int kMaxPollingIntervalInSecond = 180;
+
+} // namespace
+
+namespace chrome_to_mobile_receive {
+
+// Class that keeps the status of fetching a snapshot.
+//
+// The following steps are needed to complete the data fetching for a snapshot.
+// Note these steps are asynchronous.
+//
+// (1) Get the url job. This job contains the webpage url that generates the
+// snapshot. It also contains the information on if an offline data job
+// of this snapshot is expected.
+//
+// (2) Delete the url print job.
+//
+// If no offline data job is expected, snapshot fetch completes when (2)
+// completes.
+//
+// (3) Otherwise fetch the offline data job. This is done by polling the cloud
+// print server before the maximum retry number is reached.
+// Note the actual offline data is not included in this print job; instead
+// a download url is given.
+//
+// If it fails to fetch the offline data job before the maximum retry number is
+// reached, snapshot fetch completes when (2) completes.
+//
+// (4) Otherwise download the offline data, pointed by a url provided
+// in the offline data job.
+//
+// (5) When (4) completes, successful or not, delete the offline data job.
+//
+// Note the offline data job should not be deleted before (4) completes, as
+// the deletion of the offline data print job will cause the offline data to
+// also be deleted.
+//
+// Snapshot fetch completes when (5) complets.
+class ChromeToMobileSnapshotsPollingFetcher::SnapshotEntry {
+ public:
+ SnapshotEntry(ChromeToMobileSnapshotsPollingFetcher* owner,
+ const std::string& snapshot_id);
+ ~SnapshotEntry();
+ // Initializes this snapshot entry by a url job.
+ void InitialWithURLJob(const std::string& snapshot_type,
+ const std::string& url_print_job_id,
+ const std::string& original_url,
+ const std::string& title,
+ const std::string& create_time);
+ // Updates this snapshot entry by an offline data job.
+ void UpdateWithOfflineDataJob(const std::string& offline_data_print_job_id,
+ const std::string& offline_data_download_url);
+
+ // Processes request |source| if it is a delete request for this snapshot.
+ // Returns true if it is a delete request for this snapshot.
+ bool HandleDeleteComplete(chrome_to_mobile::CloudPrintRequest* source);
+ // Processes request |source| if it is a download request for this snapshot.
+ // Returns true if it is a download request for this snapshot.
+ bool HandleDownloadComplete(chrome_to_mobile::CloudPrintRequest* source);
+
+ // Gives up polling the cloud print server to fetch the offline data job for
+ // this snapshot.
+ void GiveUpPollingOfflineDataJob();
+
+ // Returns true if it is waiting for offline data job for this snapshot.
+ bool IsWaitingForOfflineDataJob() const;
+ // Returns true if the fetch of this snapshot has completed, as described
+ // in the class document.
+ bool HasCompleted() const;
+
+ private:
+ // Returns if an offline data is expected.
+ bool IsOfflineDataExpected() const;
+
+ // The owner of this snapshot.
+ ChromeToMobileSnapshotsPollingFetcher* const owner_;
+
+ // The id of this snapshot. This information is initialized from the url job,
+ // and it is used to identify if an offline data job is for this snapshot.
+ const std::string snapshot_id_;
+ // The type of this snapshot: url only or url with an offline data. This
+ // information is from the url job.
+ std::string snapshot_type_;
+ // The print job id of the url job. This information is from the url job.
+ std::string url_print_job_id_;
+ // The original url of the web page that generates this snapshot; it should
+ // not be empty. This information is from the url job.
+ std::string original_url_;
+
+ // The print job id of the offlince data job; it is meaningful only if an
+ // offline data is expected. This information is from the offline data job.
+ std::string offline_data_print_job_id_;
+ // The url pointing to an offline data of this snapshot; it is meaningful only
+ // if an offline data is expected. This information is from the offline data
+ // job.
+ std::string offline_data_download_url_;
+
+ // Request sent to delete a url job.
+ scoped_ptr<chrome_to_mobile::CloudPrintRequest> delete_url_job_request_;
+ // Request sent to download the offline data at |offline_data_download_url_|.
+ scoped_ptr<chrome_to_mobile::CloudPrintRequest> download_request_;
+ // Request sent to delete the offline data print job. This should be sent
+ // after |download_request_| completes, as the deletion of the offline
+ // data print job will cause the data at |offline_data_download_url_| to be
+ // deleted.
+ scoped_ptr<chrome_to_mobile::CloudPrintRequest>
+ delete_offline_data_job_request_;
+
+ // True if |owner->consumer_| has been notified that the offline data download
+ // completes.
+ bool has_notified_consumer_download_completed_;
+
+ DISALLOW_COPY_AND_ASSIGN(SnapshotEntry);
+};
+
+ChromeToMobileSnapshotsPollingFetcher::SnapshotEntry::SnapshotEntry(
+ ChromeToMobileSnapshotsPollingFetcher* owner,
+ const std::string& snapshot_id)
+ : owner_(owner),
+ snapshot_id_(snapshot_id),
+ has_notified_consumer_download_completed_(false) {
+}
+
+ChromeToMobileSnapshotsPollingFetcher::SnapshotEntry::~SnapshotEntry() {
+ if (IsOfflineDataExpected() && !has_notified_consumer_download_completed_) {
+ owner_->consumer_->OnSnapshotDataDownloadComplete(
+ snapshot_id_, false, std::string(), std::string());
+ }
+}
+
+void ChromeToMobileSnapshotsPollingFetcher::SnapshotEntry::InitialWithURLJob(
+ const std::string& snapshot_type,
+ const std::string& url_print_job_id,
+ const std::string& original_url,
+ const std::string& title,
+ const std::string& create_time) {
+ DCHECK(snapshot_type.size());
+ DCHECK(url_print_job_id.size());
+ DCHECK(original_url.size());
+
+ snapshot_type_ = snapshot_type;
+ url_print_job_id_ = url_print_job_id;
+ original_url_ = original_url;
+
+ owner_->consumer_->OnSnapshotUrlFetched(snapshot_id_,
+ original_url,
+ title,
+ IsOfflineDataExpected(),
+ create_time);
+
+ delete_url_job_request_.reset(
+ chrome_to_mobile::CloudPrintRequest::CreateAndStartGetRequest(
+ cloud_print::GetUrlForJobDelete(
+ owner_->cloud_print_server_url_, url_print_job_id_),
+ owner_->settings_,
+ owner_));
+}
+
+void ChromeToMobileSnapshotsPollingFetcher::SnapshotEntry::
+ UpdateWithOfflineDataJob(const std::string& offline_data_print_job_id,
+ const std::string& offline_data_download_url) {
+ // This check is needed as it is possible to get such a job for more than once
+ // due to the latency of the delete request.
+ if (!IsWaitingForOfflineDataJob())
+ return;
+
+ offline_data_print_job_id_ = offline_data_print_job_id;
+ offline_data_download_url_ = offline_data_download_url;
+ download_request_.reset(chrome_to_mobile::CloudPrintRequest::CreateAndStart(
+ GURL(offline_data_download_url), std::string("Accept: application/pdf"),
+ net::URLFetcher::GET, std::string(), std::string(), owner_->settings_,
+ owner_));
+}
+
+void ChromeToMobileSnapshotsPollingFetcher::SnapshotEntry::
+ GiveUpPollingOfflineDataJob() {
+ // No affect if it is not waiting for an offline data job.
+ if (!IsWaitingForOfflineDataJob())
+ return;
+ if (!has_notified_consumer_download_completed_) {
+ owner_->consumer_->OnSnapshotDataDownloadComplete(
+ snapshot_id_, false, std::string(), std::string());
+ has_notified_consumer_download_completed_ = true;
+ }
+}
+
+bool ChromeToMobileSnapshotsPollingFetcher::SnapshotEntry::HandleDeleteComplete(
+ chrome_to_mobile::CloudPrintRequest* source) {
+ if (delete_url_job_request_ == source) {
+ delete_url_job_request_.reset();
+ return true;
+ }
+ if (delete_offline_data_job_request_ == source) {
+ delete_offline_data_job_request_.reset();
+ return true;
+ }
+ return false;
+}
+
+bool ChromeToMobileSnapshotsPollingFetcher::SnapshotEntry::
+ HandleDownloadComplete(chrome_to_mobile::CloudPrintRequest* source) {
+ if (source != download_request_)
+ return false;
+
+ scoped_ptr<chrome_to_mobile::CloudPrintRequest> to_be_release(
+ download_request_.release());
+
+ bool success;
+ std::string mime_type = source->GetResponseMimeType();
+ std::string data = source->GetResponseData(&success);
+ owner_->consumer_->OnSnapshotDataDownloadComplete(
+ snapshot_id_, success, mime_type, data);
+ has_notified_consumer_download_completed_ = true;
+
+ delete_offline_data_job_request_.reset(
+ chrome_to_mobile::CloudPrintRequest::CreateAndStartGetRequest(
+ cloud_print::GetUrlForJobDelete(
+ owner_->cloud_print_server_url_, offline_data_print_job_id_),
+ owner_->settings_,
+ owner_));
+
+ return true;
+}
+
+bool ChromeToMobileSnapshotsPollingFetcher::SnapshotEntry::
+ IsWaitingForOfflineDataJob() const {
+ // No more print job is expected if no offline data is expected, or an offline
+ // data job has been received, or the consumer has been notified that the
+ // offline data download has completed (which can happen when it is decided to
+ // give up polling for the offline data.
+ if (!IsOfflineDataExpected() ||
+ !offline_data_download_url_.empty() ||
+ has_notified_consumer_download_completed_) {
+ return false;
+ }
+ return true;
+}
+
+bool ChromeToMobileSnapshotsPollingFetcher::SnapshotEntry::HasCompleted()
+ const {
+ return !IsWaitingForOfflineDataJob() &&
+ !delete_url_job_request_.get() &&
+ !download_request_.get() &&
+ !delete_offline_data_job_request_.get();
+}
+
+bool ChromeToMobileSnapshotsPollingFetcher::SnapshotEntry::
+ IsOfflineDataExpected() const {
+ return snapshot_type_.compare(kSnapshotTypeURLWithDelayedSnapshot) == 0;
+}
+
+ChromeToMobileSnapshotsFetcher* ChromeToMobileSnapshotsFetcher::Create(
+ const GURL& cloud_print_server_url,
+ const std::string& printer_id,
+ const chrome_to_mobile::CloudPrintRequest::Settings& settings,
+ ChromeToMobileSnapshotsFetcher::Consumer* delegate) {
+ return new ChromeToMobileSnapshotsPollingFetcher(cloud_print_server_url,
+ printer_id, settings,
+ delegate,
+ kBasePollingIntervalInSecond,
+ kMaxPollingIntervalInSecond,
+ kMaxPollingNum);
+}
+
+ChromeToMobileSnapshotsPollingFetcher::ChromeToMobileSnapshotsPollingFetcher(
+ const GURL& cloud_print_server_url,
+ const std::string& printer_id,
+ const chrome_to_mobile::CloudPrintRequest::Settings& settings,
+ ChromeToMobileSnapshotsFetcher::Consumer* consumer,
+ int base_polling_interval_seconds,
+ int max_polling_interval_seconds,
+ int max_polling_number)
+ : cloud_print_server_url_(cloud_print_server_url),
+ printer_id_(printer_id),
+ settings_(settings),
+ consumer_(consumer),
+ base_polling_interval_in_seconds_(base_polling_interval_seconds),
+ max_polling_interval_in_seconds_(max_polling_interval_seconds),
+ max_polling_number_(max_polling_number),
+ polling_number_(0),
+ has_oauth2_token_fetch_failure_(false),
+ has_cloud_print_auth_error_(false) {
+ DCHECK(consumer_);
+}
+
+ChromeToMobileSnapshotsPollingFetcher::
+ ~ChromeToMobileSnapshotsPollingFetcher() {
+ Cancel();
+}
+
+bool ChromeToMobileSnapshotsPollingFetcher::DeletePendingSnapshots() {
+ bool fetch_completed_by_being_cancelled = false;
+
+ for (SnapshotMap::const_iterator iter = pending_snapshots_.begin();
+ iter != pending_snapshots_.end();
+ ++iter) {
+ if (!iter->second->HasCompleted())
+ fetch_completed_by_being_cancelled = true;
+ delete iter->second;
+ }
+ pending_snapshots_.clear();
+ return fetch_completed_by_being_cancelled;
+}
+
+void ChromeToMobileSnapshotsPollingFetcher::Cancel() {
+ DCHECK(CalledOnValidThread());
+
+ bool fetch_completed_by_being_cancelled = DeletePendingSnapshots();
+ pending_snapshots_.clear();
+ fetch_request_.reset();
+ if (fetch_completed_by_being_cancelled)
+ consumer_->OnSnapshotsFetchComplete(this);
+}
+
+void ChromeToMobileSnapshotsPollingFetcher::Fetch() {
+ DCHECK(CalledOnValidThread());
+
+ polling_number_ = 0;
+ Poll();
+}
+
+void ChromeToMobileSnapshotsPollingFetcher::Poll() {
+ DCHECK(CalledOnValidThread());
+
+ ++polling_number_;
+ polling_timer_.Stop();
+ fetch_request_.reset(
+ chrome_to_mobile::CloudPrintRequest::CreateAndStartGetRequest(
+ cloud_print::GetUrlForJobFetch(cloud_print_server_url_,
+ printer_id_,
+ std::string()),
+ settings_,
+ this));
+}
+
+bool ChromeToMobileSnapshotsPollingFetcher::HandleFetchComplete(
+ chrome_to_mobile::CloudPrintRequest* source) {
+ DCHECK(CalledOnValidThread());
+
+ if (source != fetch_request_.get())
+ return false;
+
+ scoped_ptr<chrome_to_mobile::CloudPrintRequest> to_be_release(
+ fetch_request_.release());
+
+ bool succeeded;
+ std::string response_data = source->GetResponseData(&succeeded);
+ scoped_ptr<base::DictionaryValue> response_dict_to_be_release;
+ base::DictionaryValue* response_dict = NULL;
+ if (succeeded) {
+ cloud_print::ParseResponseJSON(response_data, &succeeded, &response_dict);
+ response_dict_to_be_release.reset(response_dict);
+ }
+ base::ListValue* job_list = NULL;
+ if (succeeded && response_dict)
+ succeeded = response_dict->GetList(cloud_print::kJobListValue, &job_list);
+
+ if (succeeded && job_list) {
+ AddPendingSnapshotsFromURLJobs(job_list);
+ UpdatePendingSnapshotsWithOfflineDataJobs(job_list);
+ }
+
+ // Checks if it should continue polling.
+ bool should_give_up = polling_number_ >= max_polling_number_;
+ bool is_waiting_for_offline_data_jobs = false;
+ std::map<std::string, SnapshotEntry*>::iterator iter =
+ pending_snapshots_.begin();
+ for (; iter != pending_snapshots_.end(); ++iter) {
+ SnapshotEntry* entry = iter->second;
+ if (entry->IsWaitingForOfflineDataJob())
+ is_waiting_for_offline_data_jobs = true;
+ if (should_give_up)
+ entry->GiveUpPollingOfflineDataJob();
+ }
+ if (!should_give_up && is_waiting_for_offline_data_jobs) {
+ int64 max_backoff =
+ (1 << polling_number_) * base_polling_interval_in_seconds_;
+ int64 backoff =
+ base_polling_interval_in_seconds_ + base::RandDouble() * max_backoff;
+ if (backoff > max_polling_interval_in_seconds_)
+ backoff = max_polling_interval_in_seconds_;
+
+ polling_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromSeconds(backoff),
+ this,
+ &ChromeToMobileSnapshotsPollingFetcher::Poll);
+ }
+ return true;
+}
+
+void ChromeToMobileSnapshotsPollingFetcher::AddPendingSnapshotsFromURLJobs(
+ const ListValue* job_list) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(job_list);
+
+ for (size_t index = 0; index < job_list->GetSize(); ++index) {
+ std::string job_id;
+ std::string snapshot_id;
+ std::string snapshot_type;
+ std::string original_url;
+ std::string title;
+ std::string create_time;
+ if (!ParseSnapshotURLJobInformationFromCloudPrintFetchJobList(
+ job_list, index, &job_id, &snapshot_id, &snapshot_type, &original_url,
+ &title, &create_time)) {
+ continue;
+ }
+ if (pending_snapshots_.find(snapshot_id) != pending_snapshots_.end())
+ continue;
+
+ SnapshotEntry* snapshot_entry = new SnapshotEntry(this, snapshot_id);
+ snapshot_entry->InitialWithURLJob(
+ snapshot_type, job_id, original_url, title, create_time);
+ pending_snapshots_[snapshot_id] = snapshot_entry;
+ }
+}
+
+void ChromeToMobileSnapshotsPollingFetcher::
+ UpdatePendingSnapshotsWithOfflineDataJobs(const ListValue* job_list) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(job_list);
+
+ // Early return if there is no pending snapshot.
+ if (!pending_snapshots_.size())
+ return;
+
+ for (size_t index = 0; index < job_list->GetSize(); ++index) {
+ std::string job_id;
+ std::string snapshot_id;
+ std::string download_url;
+ if (!ParseSnapshotOfflineDataJobInformationFromCloudPrintFetchJobList(
+ job_list, index, &job_id, &snapshot_id, &download_url)) {
+ continue;
+ }
+ if (pending_snapshots_.find(snapshot_id) == pending_snapshots_.end())
+ return;
+ SnapshotEntry* entry = pending_snapshots_[snapshot_id];
+ entry->UpdateWithOfflineDataJob(job_id, download_url);
+ }
+}
+
+bool ChromeToMobileSnapshotsPollingFetcher::HandleDownloadComplete(
+ chrome_to_mobile::CloudPrintRequest* source) {
+ SnapshotMap::iterator iter = pending_snapshots_.begin();
+ for (; iter != pending_snapshots_.end(); ++iter) {
+ if (iter->second->HandleDownloadComplete(source))
+ return true;
+ }
+ return false;
+}
+
+bool ChromeToMobileSnapshotsPollingFetcher::HandleDeleteComplete(
+ chrome_to_mobile::CloudPrintRequest* source) {
+ std::map<std::string, SnapshotEntry*>::iterator iter =
+ pending_snapshots_.begin();
+ for (; iter != pending_snapshots_.end(); ++iter) {
+ SnapshotEntry* snapshot_entry = iter->second;
+ if (snapshot_entry->HandleDeleteComplete(source)) {
+ if (snapshot_entry->HasCompleted()) {
+ pending_snapshots_.erase(iter->first);
+ delete snapshot_entry;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+void ChromeToMobileSnapshotsPollingFetcher::OnRequestComplete(
+ chrome_to_mobile::CloudPrintRequest* source) {
+ has_oauth2_token_fetch_failure_ = source->HasOAuth2AccessTokenFailure();
+ has_cloud_print_auth_error_ = source->HasCloudPrintAuthError();
+ if (has_oauth2_token_fetch_failure_ || has_cloud_print_auth_error_) {
+ Cancel();
+ return;
+ }
+
+ if (!HandleFetchComplete(source)) {
+ if (!HandleDownloadComplete(source))
+ HandleDeleteComplete(source);
+ }
+
+ bool has_completed = true;
+ std::map<std::string, SnapshotEntry*>::const_iterator iter =
+ pending_snapshots_.begin();
+ for (; iter != pending_snapshots_.end(); ++iter) {
+ if (!iter->second->HasCompleted()) {
+ has_completed = false;
+ break;
+ }
+ }
+ // Once completed, no request should come before |Fetch()| is called again.
+ if (has_completed) {
+ consumer_->OnSnapshotsFetchComplete(this);
+ DeletePendingSnapshots();
+ }
+}
+
+
+bool ChromeToMobileSnapshotsPollingFetcher::HasCloudPrintAuthError() const {
+ return has_cloud_print_auth_error_;
+}
+
+bool ChromeToMobileSnapshotsPollingFetcher::HasOAuth2AccessTokenFailure()
+ const {
+ return has_oauth2_token_fetch_failure_;
+}
+
+} // namespace chrome_to_mobile_receive

Powered by Google App Engine
This is Rietveld 408576698