| Index: chrome/browser/chromeos/gdata/gdata_uploader.cc
|
| diff --git a/chrome/browser/chromeos/gdata/gdata_uploader.cc b/chrome/browser/chromeos/gdata/gdata_uploader.cc
|
| deleted file mode 100644
|
| index 32212d089d02902480c48c8b26e2c38794ed9cb0..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/chromeos/gdata/gdata_uploader.cc
|
| +++ /dev/null
|
| @@ -1,483 +0,0 @@
|
| -// Copyright (c) 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/chromeos/gdata/gdata_uploader.h"
|
| -
|
| -#include <algorithm>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/callback.h"
|
| -#include "chrome/browser/chromeos/gdata/drive_service_interface.h"
|
| -#include "chrome/browser/chromeos/gdata/gdata_upload_file_info.h"
|
| -#include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h"
|
| -#include "content/public/browser/browser_thread.h"
|
| -#include "content/public/browser/download_item.h"
|
| -#include "net/base/file_stream.h"
|
| -#include "net/base/net_errors.h"
|
| -
|
| -using content::BrowserThread;
|
| -
|
| -namespace {
|
| -
|
| -// Google Documents List API requires uploading in chunks of 512kB.
|
| -const int64 kUploadChunkSize = 512 * 1024;
|
| -
|
| -// Maximum number of times we try to open a file before giving up.
|
| -const int kMaxFileOpenTries = 5;
|
| -
|
| -} // namespace
|
| -
|
| -namespace gdata {
|
| -
|
| -GDataUploader::GDataUploader(DriveServiceInterface* drive_service)
|
| - : drive_service_(drive_service),
|
| - next_upload_id_(0),
|
| - ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
|
| -}
|
| -
|
| -GDataUploader::~GDataUploader() {
|
| -}
|
| -
|
| -int GDataUploader::UploadNewFile(scoped_ptr<UploadFileInfo> upload_file_info) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - DCHECK(upload_file_info.get());
|
| - DCHECK_EQ(upload_file_info->upload_id, -1);
|
| - DCHECK(!upload_file_info->file_path.empty());
|
| - DCHECK(!upload_file_info->gdata_path.empty());
|
| - DCHECK(!upload_file_info->title.empty());
|
| - DCHECK(!upload_file_info->content_type.empty());
|
| - DCHECK(!upload_file_info->initial_upload_location.is_empty());
|
| - DCHECK_EQ(UPLOAD_INVALID, upload_file_info->upload_mode);
|
| -
|
| - upload_file_info->upload_mode = UPLOAD_NEW_FILE;
|
| -
|
| - // When uploading a new file, we should retry file open as the file may
|
| - // not yet be ready. See comments in OpenCompletionCallback.
|
| - // TODO(satorux): The retry should be done only when we are uploading
|
| - // while downloading files from web sites (i.e. saving files to Drive).
|
| - upload_file_info->should_retry_file_open = true;
|
| - return StartUploadFile(upload_file_info.Pass());
|
| -}
|
| -
|
| -int GDataUploader::StreamExistingFile(
|
| - scoped_ptr<UploadFileInfo> upload_file_info) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - DCHECK(upload_file_info.get());
|
| - DCHECK_EQ(upload_file_info->upload_id, -1);
|
| - DCHECK(!upload_file_info->file_path.empty());
|
| - DCHECK(!upload_file_info->gdata_path.empty());
|
| - DCHECK(upload_file_info->title.empty());
|
| - DCHECK(!upload_file_info->content_type.empty());
|
| - DCHECK(!upload_file_info->initial_upload_location.is_empty());
|
| - DCHECK_EQ(UPLOAD_INVALID, upload_file_info->upload_mode);
|
| -
|
| - upload_file_info->upload_mode = UPLOAD_EXISTING_FILE;
|
| -
|
| - // When uploading a new file, we should retry file open as the file may
|
| - // not yet be ready. See comments in OpenCompletionCallback.
|
| - // TODO(satorux): The retry should be done only when we are uploading
|
| - // while downloading files from web sites (i.e. saving files to Drive).
|
| - upload_file_info->should_retry_file_open = true;
|
| - return StartUploadFile(upload_file_info.Pass());
|
| -}
|
| -
|
| -int GDataUploader::StartUploadFile(
|
| - scoped_ptr<UploadFileInfo> upload_file_info) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - DCHECK(upload_file_info.get());
|
| - DCHECK_EQ(upload_file_info->upload_id, -1);
|
| - DCHECK_NE(UPLOAD_INVALID, upload_file_info->upload_mode);
|
| -
|
| - const int upload_id = next_upload_id_++;
|
| - upload_file_info->upload_id = upload_id;
|
| -
|
| - // Add upload_file_info to our internal map and take ownership.
|
| - pending_uploads_[upload_id] = upload_file_info.release();
|
| -
|
| - UploadFileInfo* info = GetUploadFileInfo(upload_id);
|
| - DVLOG(1) << "Uploading file: " << info->DebugString();
|
| -
|
| - // Create a FileStream to make sure the file can be opened successfully.
|
| - info->file_stream = new net::FileStream(NULL);
|
| -
|
| - // Create buffer to hold upload data. The full file size may not be known at
|
| - // this point, so it may not be appropriate to use info->file_size.
|
| - info->buf_len = kUploadChunkSize;
|
| - info->buf = new net::IOBuffer(info->buf_len);
|
| -
|
| - OpenFile(info);
|
| - return upload_id;
|
| -}
|
| -
|
| -int GDataUploader::UploadExistingFile(
|
| - const GURL& upload_location,
|
| - const FilePath& gdata_file_path,
|
| - const FilePath& local_file_path,
|
| - int64 file_size,
|
| - const std::string& content_type,
|
| - const UploadFileInfo::UploadCompletionCallback& callback) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - DCHECK(!upload_location.is_empty());
|
| - DCHECK(!local_file_path.empty());
|
| - DCHECK(!content_type.empty());
|
| -
|
| - scoped_ptr<UploadFileInfo> upload_file_info(new UploadFileInfo);
|
| - upload_file_info->upload_mode = UPLOAD_EXISTING_FILE;
|
| - upload_file_info->initial_upload_location = upload_location;
|
| - upload_file_info->file_path = local_file_path;
|
| - upload_file_info->file_size = file_size;
|
| - upload_file_info->content_type = content_type;
|
| - upload_file_info->completion_callback = callback;
|
| - upload_file_info->gdata_path = gdata_file_path,
|
| - upload_file_info->content_length = file_size;
|
| - upload_file_info->all_bytes_present = true;
|
| -
|
| - // When uploading an updated file, we should not retry file open as the
|
| - // file should already be present by definition.
|
| - upload_file_info->should_retry_file_open = false;
|
| - return StartUploadFile(upload_file_info.Pass());
|
| -}
|
| -
|
| -void GDataUploader::UpdateUpload(int upload_id,
|
| - content::DownloadItem* download) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| -
|
| - UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id);
|
| - if (!upload_file_info)
|
| - return;
|
| -
|
| - const int64 file_size = download->GetReceivedBytes();
|
| -
|
| - // Update file_size and all_bytes_present.
|
| - DVLOG(1) << "Updating file size from " << upload_file_info->file_size
|
| - << " to " << file_size
|
| - << (download->AllDataSaved() ? " (AllDataSaved)" : " (In-progress)");
|
| - upload_file_info->file_size = file_size;
|
| - upload_file_info->all_bytes_present = download->AllDataSaved();
|
| - if (upload_file_info->file_path != download->GetFullPath()) {
|
| - // We shouldn't see a rename if should_retry_file_open is true. The only
|
| - // rename we expect (for now) is the final rename that happens after the
|
| - // download transition from IN_PROGRESS -> COMPLETE. This, in turn, only
|
| - // happens after the upload completes. However, since this isn't enforced by
|
| - // the API contract, we reset the retry count so we can retry all over again
|
| - // with the new path.
|
| - // TODO(asanka): Introduce a synchronization point after the initial rename
|
| - // of the download and get rid of the retry logic.
|
| - upload_file_info->num_file_open_tries = 0;
|
| - upload_file_info->file_path = download->GetFullPath();
|
| - }
|
| -
|
| - // Resume upload if necessary and possible.
|
| - if (upload_file_info->upload_paused &&
|
| - (upload_file_info->all_bytes_present ||
|
| - upload_file_info->SizeRemaining() > kUploadChunkSize)) {
|
| - DVLOG(1) << "Resuming upload " << upload_file_info->title;
|
| - upload_file_info->upload_paused = false;
|
| - UploadNextChunk(upload_file_info);
|
| - }
|
| -
|
| - // Retry opening this file if we failed before. File open can fail because
|
| - // the downloads system sets the full path on the UI thread and schedules a
|
| - // rename on the FILE thread. Thus the new path is visible on the UI thread
|
| - // before the renamed file is available on the file system.
|
| - if (upload_file_info->should_retry_file_open) {
|
| - DCHECK(!download->IsComplete());
|
| - // Disallow further retries.
|
| - upload_file_info->should_retry_file_open = false;
|
| - OpenFile(upload_file_info);
|
| - }
|
| -}
|
| -
|
| -int64 GDataUploader::GetUploadedBytes(int upload_id) const {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - UploadFileInfo* upload_info = GetUploadFileInfo(upload_id);
|
| - // We return the start_range as the count of uploaded bytes since that is the
|
| - // start of the next or currently uploading chunk.
|
| - // TODO(asanka): Use a finer grained progress value than this. We end up
|
| - // reporting progress in kUploadChunkSize increments.
|
| - return upload_info ? upload_info->start_range : 0;
|
| -}
|
| -
|
| -UploadFileInfo* GDataUploader::GetUploadFileInfo(int upload_id) const {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| -
|
| - UploadFileInfoMap::const_iterator it = pending_uploads_.find(upload_id);
|
| - DVLOG_IF(1, it == pending_uploads_.end()) << "No upload found for id "
|
| - << upload_id;
|
| - return it != pending_uploads_.end() ? it->second : NULL;
|
| -}
|
| -
|
| -void GDataUploader::OpenFile(UploadFileInfo* upload_file_info) {
|
| - // Open the file asynchronously.
|
| - const int rv = upload_file_info->file_stream->Open(
|
| - upload_file_info->file_path,
|
| - base::PLATFORM_FILE_OPEN |
|
| - base::PLATFORM_FILE_READ |
|
| - base::PLATFORM_FILE_ASYNC,
|
| - base::Bind(&GDataUploader::OpenCompletionCallback,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - upload_file_info->upload_id));
|
| - DCHECK_EQ(net::ERR_IO_PENDING, rv);
|
| -}
|
| -
|
| -void GDataUploader::OpenCompletionCallback(int upload_id, int result) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| -
|
| - UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id);
|
| - if (!upload_file_info)
|
| - return;
|
| -
|
| - // The file may actually not exist yet, as the downloads system downloads
|
| - // to a temp location and then renames the file. If this is the case, we
|
| - // just retry opening the file later.
|
| - if (result != net::OK) {
|
| - DCHECK_EQ(result, net::ERR_FILE_NOT_FOUND);
|
| -
|
| - if (upload_file_info->should_retry_file_open) {
|
| - // File open failed. Try again later.
|
| - upload_file_info->num_file_open_tries++;
|
| -
|
| - DVLOG(1) << "Error opening \"" << upload_file_info->file_path.value()
|
| - << "\" for reading: " << net::ErrorToString(result)
|
| - << ", tries=" << upload_file_info->num_file_open_tries;
|
| -
|
| - // Stop trying to open this file if we exceed kMaxFileOpenTries.
|
| - const bool exceeded_max_attempts =
|
| - upload_file_info->num_file_open_tries >= kMaxFileOpenTries;
|
| - upload_file_info->should_retry_file_open = !exceeded_max_attempts;
|
| - }
|
| - if (!upload_file_info->should_retry_file_open) {
|
| - UploadFailed(scoped_ptr<UploadFileInfo>(upload_file_info),
|
| - DRIVE_FILE_ERROR_NOT_FOUND);
|
| - }
|
| - return;
|
| - }
|
| -
|
| - // Open succeeded, initiate the upload.
|
| - upload_file_info->should_retry_file_open = false;
|
| - if (upload_file_info->initial_upload_location.is_empty()) {
|
| - UploadFailed(scoped_ptr<UploadFileInfo>(upload_file_info),
|
| - DRIVE_FILE_ERROR_ABORT);
|
| - return;
|
| - }
|
| - drive_service_->InitiateUpload(
|
| - InitiateUploadParams(upload_file_info->upload_mode,
|
| - upload_file_info->title,
|
| - upload_file_info->content_type,
|
| - upload_file_info->content_length,
|
| - upload_file_info->initial_upload_location,
|
| - upload_file_info->gdata_path),
|
| - base::Bind(&GDataUploader::OnUploadLocationReceived,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - upload_file_info->upload_id));
|
| -}
|
| -
|
| -void GDataUploader::OnUploadLocationReceived(
|
| - int upload_id,
|
| - GDataErrorCode code,
|
| - const GURL& upload_location) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| -
|
| - UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id);
|
| - if (!upload_file_info)
|
| - return;
|
| -
|
| - DVLOG(1) << "Got upload location [" << upload_location.spec()
|
| - << "] for [" << upload_file_info->title << "]";
|
| -
|
| - if (code != HTTP_SUCCESS) {
|
| - // TODO(achuith): Handle error codes from Google Docs server.
|
| - UploadFailed(scoped_ptr<UploadFileInfo>(upload_file_info),
|
| - DRIVE_FILE_ERROR_ABORT);
|
| - return;
|
| - }
|
| -
|
| - upload_file_info->upload_location = upload_location;
|
| -
|
| - // Start the upload from the beginning of the file.
|
| - UploadNextChunk(upload_file_info);
|
| -}
|
| -
|
| -void GDataUploader::UploadNextChunk(UploadFileInfo* upload_file_info) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - // Check that |upload_file_info| is in pending_uploads_.
|
| - DCHECK(upload_file_info == GetUploadFileInfo(upload_file_info->upload_id));
|
| - DVLOG(1) << "Number of pending uploads=" << pending_uploads_.size();
|
| -
|
| - // Determine number of bytes to read for this upload iteration, which cannot
|
| - // exceed size of buf i.e. buf_len.
|
| - const int64 bytes_remaining = upload_file_info->SizeRemaining();
|
| - const int bytes_to_read = std::min(upload_file_info->SizeRemaining(),
|
| - upload_file_info->buf_len);
|
| -
|
| - // Update the content length if the file_size is known.
|
| - if (upload_file_info->all_bytes_present)
|
| - upload_file_info->content_length = upload_file_info->file_size;
|
| - else if (bytes_remaining == bytes_to_read) {
|
| - // Wait for more data if this is the last chunk we have and we don't know
|
| - // whether we've reached the end of the file. We won't know how much data to
|
| - // expect until the transfer is complete (the Content-Length might be
|
| - // incorrect or absent). If we've sent the last chunk out already when we
|
| - // find out there's no more data, we won't be able to complete the upload.
|
| - DVLOG(1) << "Paused upload " << upload_file_info->title;
|
| - upload_file_info->upload_paused = true;
|
| - return;
|
| - }
|
| -
|
| - if (bytes_to_read == 0) {
|
| - // This should only happen when the actual file size is 0.
|
| - DCHECK(upload_file_info->all_bytes_present &&
|
| - upload_file_info->content_length == 0);
|
| -
|
| - upload_file_info->start_range = 0;
|
| - upload_file_info->end_range = -1;
|
| - // Skips file_stream->Read and error checks for 0-byte case. Immediately
|
| - // proceeds to ResumeUpload.
|
| - // TODO(kinaba): http://crbug.com/134814
|
| - // Replace the following PostTask() to an direct method call. This is needed
|
| - // because we have to ResumeUpload after the previous InitiateUpload or
|
| - // ResumeUpload is completely finished; at this point, we are inside the
|
| - // callback function from the previous operation, which is not treated as
|
| - // finished yet.
|
| - base::MessageLoopProxy::current()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&GDataUploader::ResumeUpload,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - upload_file_info->upload_id));
|
| - return;
|
| - }
|
| -
|
| - upload_file_info->file_stream->Read(
|
| - upload_file_info->buf,
|
| - bytes_to_read,
|
| - base::Bind(&GDataUploader::ReadCompletionCallback,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - upload_file_info->upload_id,
|
| - bytes_to_read));
|
| -}
|
| -
|
| -void GDataUploader::ReadCompletionCallback(
|
| - int upload_id,
|
| - int bytes_to_read,
|
| - int bytes_read) {
|
| - // The Read is asynchronously executed on BrowserThread::UI, where
|
| - // Read() was called.
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - DVLOG(1) << "ReadCompletionCallback bytes read=" << bytes_read;
|
| -
|
| - UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id);
|
| - if (!upload_file_info)
|
| - return;
|
| -
|
| - // TODO(achuith): Handle this error.
|
| - DCHECK_EQ(bytes_to_read, bytes_read);
|
| - DCHECK_GT(bytes_read, 0) << "Error reading from file "
|
| - << upload_file_info->file_path.value();
|
| -
|
| - upload_file_info->start_range = upload_file_info->end_range + 1;
|
| - upload_file_info->end_range = upload_file_info->start_range +
|
| - bytes_read - 1;
|
| -
|
| - ResumeUpload(upload_id);
|
| -}
|
| -
|
| -void GDataUploader::ResumeUpload(int upload_id) {
|
| - UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id);
|
| - if (!upload_file_info)
|
| - return;
|
| -
|
| - drive_service_->ResumeUpload(
|
| - ResumeUploadParams(upload_file_info->upload_mode,
|
| - upload_file_info->start_range,
|
| - upload_file_info->end_range,
|
| - upload_file_info->content_length,
|
| - upload_file_info->content_type,
|
| - upload_file_info->buf,
|
| - upload_file_info->upload_location,
|
| - upload_file_info->gdata_path),
|
| - base::Bind(&GDataUploader::OnResumeUploadResponseReceived,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - upload_file_info->upload_id));
|
| -}
|
| -
|
| -void GDataUploader::OnResumeUploadResponseReceived(
|
| - int upload_id,
|
| - const ResumeUploadResponse& response,
|
| - scoped_ptr<DocumentEntry> entry) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| -
|
| - UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id);
|
| - if (!upload_file_info)
|
| - return;
|
| -
|
| - const UploadMode upload_mode = upload_file_info->upload_mode;
|
| - if ((upload_mode == UPLOAD_NEW_FILE && response.code == HTTP_CREATED) ||
|
| - (upload_mode == UPLOAD_EXISTING_FILE && response.code == HTTP_SUCCESS)) {
|
| - DVLOG(1) << "Successfully created uploaded file=["
|
| - << upload_file_info->title;
|
| -
|
| - // Remove |upload_id| from the UploadFileInfoMap. The UploadFileInfo object
|
| - // will be deleted upon completion of completion_callback.
|
| - RemoveUpload(upload_id);
|
| -
|
| - // Done uploading.
|
| - upload_file_info->entry = entry.Pass();
|
| - if (!upload_file_info->completion_callback.is_null()) {
|
| - upload_file_info->completion_callback.Run(
|
| - DRIVE_FILE_OK,
|
| - scoped_ptr<UploadFileInfo>(upload_file_info));
|
| - }
|
| - return;
|
| - }
|
| -
|
| - // If code is 308 (RESUME_INCOMPLETE) and range_received is what has been
|
| - // previously uploaded (i.e. = upload_file_info->end_range), proceed to
|
| - // upload the next chunk.
|
| - if (response.code != HTTP_RESUME_INCOMPLETE ||
|
| - response.start_range_received != 0 ||
|
| - response.end_range_received != upload_file_info->end_range) {
|
| - // TODO(achuith): Handle error cases, e.g.
|
| - // - when previously uploaded data wasn't received by Google Docs server,
|
| - // i.e. when end_range_received < upload_file_info->end_range
|
| - LOG(ERROR) << "UploadNextChunk http code=" << response.code
|
| - << ", start_range_received=" << response.start_range_received
|
| - << ", end_range_received=" << response.end_range_received
|
| - << ", expected end range=" << upload_file_info->end_range;
|
| - UploadFailed(
|
| - scoped_ptr<UploadFileInfo>(upload_file_info),
|
| - response.code == HTTP_FORBIDDEN ?
|
| - DRIVE_FILE_ERROR_NO_SPACE :
|
| - DRIVE_FILE_ERROR_ABORT);
|
| - return;
|
| - }
|
| -
|
| - DVLOG(1) << "Received range " << response.start_range_received
|
| - << "-" << response.end_range_received
|
| - << " for [" << upload_file_info->title << "]";
|
| -
|
| - // Continue uploading.
|
| - UploadNextChunk(upload_file_info);
|
| -}
|
| -
|
| -void GDataUploader::UploadFailed(scoped_ptr<UploadFileInfo> upload_file_info,
|
| - DriveFileError error) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| -
|
| - RemoveUpload(upload_file_info->upload_id);
|
| -
|
| - LOG(ERROR) << "Upload failed " << upload_file_info->DebugString();
|
| - // This is subtle but we should take the callback reference before
|
| - // calling upload_file_info.Pass(). Otherwise, it'll crash.
|
| - const UploadFileInfo::UploadCompletionCallback& callback =
|
| - upload_file_info->completion_callback;
|
| - if (!callback.is_null())
|
| - callback.Run(error, upload_file_info.Pass());
|
| -}
|
| -
|
| -void GDataUploader::RemoveUpload(int upload_id) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - pending_uploads_.erase(upload_id);
|
| -}
|
| -
|
| -} // namespace gdata
|
|
|