Chromium Code Reviews| Index: chrome/browser/chromeos/drive/drive_url_request_job.cc |
| diff --git a/chrome/browser/chromeos/drive/drive_url_request_job.cc b/chrome/browser/chromeos/drive/drive_url_request_job.cc |
| index 2e1ebb123a0b238ae43517717b4f88577450556b..aeb89f9d6d4105900fb10b2bf0ad7c41fe1a7f1b 100644 |
| --- a/chrome/browser/chromeos/drive/drive_url_request_job.cc |
| +++ b/chrome/browser/chromeos/drive/drive_url_request_job.cc |
| @@ -4,16 +4,19 @@ |
| #include "chrome/browser/chromeos/drive/drive_url_request_job.h" |
| -#include <string> |
| +#include <algorithm> |
| +#include <vector> |
| #include "base/bind.h" |
| #include "base/logging.h" |
| -#include "base/memory/scoped_ptr.h" |
| -#include "chrome/browser/chromeos/drive/drive.pb.h" |
| -#include "chrome/browser/chromeos/drive/drive_file_stream_reader.h" |
| -#include "chrome/browser/chromeos/drive/file_system_interface.h" |
| +#include "base/memory/ref_counted.h" |
| +#include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chromeos/drive/file_system_util.h" |
| +#include "chrome/browser/extensions/api/file_handlers/mime_util.h" |
| +#include "chrome/browser/profiles/profile_manager.h" |
| +#include "chrome/common/url_constants.h" |
| #include "content/public/browser/browser_thread.h" |
| +#include "content/public/browser/storage_partition.h" |
| #include "net/base/net_errors.h" |
| #include "net/http/http_byte_range.h" |
| #include "net/http/http_request_headers.h" |
| @@ -21,39 +24,153 @@ |
| #include "net/http/http_util.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_status.h" |
| +#include "storage/browser/fileapi/file_system_backend.h" |
| +#include "storage/browser/fileapi/file_system_context.h" |
| +#include "storage/browser/fileapi/file_system_operation_runner.h" |
| using content::BrowserThread; |
| namespace drive { |
| namespace { |
| -struct MimeTypeReplacement { |
| - const char* original_type; |
| - const char* new_type; |
| -}; |
| +const char kWrongMimeTypeForMHTML[] = "message/rfc822"; |
| +const char kCorrectMimeTypeForMHTML[] = "multipart/related"; |
|
mtomasz
2014/09/19 06:33:45
I think it's not wrong. It's just another mime typ
hirono
2014/09/19 07:00:43
Done.
|
| + |
| +// Check if the |url| points a valid location or not. |
| +bool IsValidURL(const storage::FileSystemURL& url) { |
| + switch (url.type()) { |
| + case storage::kFileSystemTypeDrive: { |
| + const base::FilePath my_drive_path = util::GetDriveMyDriveRootPath(); |
| + const base::FilePath drive_other_path = |
| + util::GetDriveGrandRootPath().Append(util::kDriveOtherDirName); |
| + const base::FilePath url_drive_path = |
| + util::ExtractDrivePathFromFileSystemUrl(url); |
| + return my_drive_path == url_drive_path || |
| + my_drive_path.IsParent(url_drive_path) || |
| + drive_other_path.IsParent(url_drive_path); |
| + } |
| + default: |
| + return false; |
| + } |
| +} |
| -const MimeTypeReplacement kMimeTypeReplacements[] = { |
| - {"message/rfc822", "multipart/related"} // Fixes MHTML |
| -}; |
| +// Helper for obtaining FileSystemContext, FileSystemURL, and mime type on the |
| +// UI thread. |
| +class URLHelper { |
| + public: |
| + // The scoped pointer to control lifetime of the instance itself. The pointer |
| + // is passed to callback functions and binds the lifetime of the instance to |
| + // the callback's lifetime. |
| + typedef scoped_ptr<URLHelper> Lifetime; |
| + |
| + URLHelper(void* profile_id, |
| + const GURL& url, |
| + const DriveURLRequestJob::HelperCallback& callback) |
| + : profile_id_(profile_id), url_(url), callback_(callback) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + Lifetime lifetime(this); |
| + BrowserThread::PostTask(BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(&URLHelper::RunOnUIThread, |
| + base::Unretained(this), |
| + base::Passed(&lifetime))); |
| + } |
| + |
| + private: |
| + void RunOnUIThread(Lifetime lifetime) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + Profile* const profile = reinterpret_cast<Profile*>(profile_id_); |
| + if (!g_browser_process->profile_manager()->IsValidProfile(profile)) { |
| + ReplyResult(net::ERR_FAILED); |
| + return; |
| + } |
| + content::StoragePartition* const storage = |
| + content::BrowserContext::GetStoragePartitionForSite(profile, url_); |
| + DCHECK(storage); |
| + |
| + scoped_refptr<storage::FileSystemContext> context = |
| + storage->GetFileSystemContext(); |
| + DCHECK(context.get()); |
| + |
| + // Obtain the absolute path in the file system. |
| + base::FilePath path = drive::util::GetDriveMountPointPath(profile); |
| + drive::util::GetDriveGrandRootPath().AppendRelativePath( |
| + util::DriveURLToFilePath(url_), &path); |
| + |
| + storage::ExternalFileSystemBackend* const backend = |
| + context->external_backend(); |
| + DCHECK(backend); |
| + |
| + // Obtain the virtual path. |
| + base::FilePath virtual_path; |
| + if (!backend->GetVirtualPath(path, &virtual_path)) { |
| + ReplyResult(net::ERR_FILE_NOT_FOUND); |
| + return; |
| + } |
| + |
| + // Obtain the file system URL. |
| + // TODO(hirono): After removing MHTML support, stop to use the special |
| + // drive: scheme and use filesystem: URL directly. crbug.com/415455 |
| + file_system_url_ = context->CreateCrackedFileSystemURL( |
| + GURL(std::string(chrome::kDriveScheme) + ":"), |
| + storage::kFileSystemTypeExternal, |
| + virtual_path); |
| + if (!IsValidURL(file_system_url_)) { |
| + ReplyResult(net::ERR_INVALID_URL); |
| + return; |
| + } |
| -std::string FixupMimeType(const std::string& type) { |
| - for (size_t i = 0; i < arraysize(kMimeTypeReplacements); i++) { |
| - if (type == kMimeTypeReplacements[i].original_type) |
| - return kMimeTypeReplacements[i].new_type; |
| + file_system_context_ = context; |
| + |
| + extensions::app_file_handler_util::GetMimeTypeForLocalPath( |
| + profile, |
| + file_system_url_.path(), |
| + base::Bind(&URLHelper::OnGotMimeTypeOnUIThread, |
| + base::Unretained(this), |
| + base::Passed(&lifetime))); |
| } |
| - return type; |
| -} |
| + |
| + void OnGotMimeTypeOnUIThread(Lifetime lifetime, |
| + const std::string& mime_type) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + mime_type_ = mime_type; |
| + |
| + if (mime_type_ == kWrongMimeTypeForMHTML) |
| + mime_type_ = kCorrectMimeTypeForMHTML; |
| + |
| + ReplyResult(net::OK); |
| + } |
| + |
| + void ReplyResult(net::Error error) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + |
| + BrowserThread::PostTask(BrowserThread::IO, |
| + FROM_HERE, |
| + base::Bind(callback_, |
| + error, |
| + file_system_context_, |
| + file_system_url_, |
| + mime_type_)); |
| + } |
| + |
| + void* const profile_id_; |
| + const GURL url_; |
| + const DriveURLRequestJob::HelperCallback callback_; |
| + scoped_refptr<storage::FileSystemContext> file_system_context_; |
| + storage::FileSystemURL file_system_url_; |
| + std::string mime_type_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(URLHelper); |
| +}; |
| } // namespace |
| -DriveURLRequestJob::DriveURLRequestJob( |
| - const FileSystemGetter& file_system_getter, |
| - base::SequencedTaskRunner* file_task_runner, |
| - net::URLRequest* request, |
| - net::NetworkDelegate* network_delegate) |
| +DriveURLRequestJob::DriveURLRequestJob(void* profile_id, |
| + net::URLRequest* request, |
| + net::NetworkDelegate* network_delegate) |
| : net::URLRequestJob(request, network_delegate), |
| - file_system_getter_(file_system_getter), |
| - file_task_runner_(file_task_runner), |
| + profile_id_(profile_id), |
| + remaining_bytes_(0), |
| weak_ptr_factory_(this) { |
| } |
| @@ -77,7 +194,7 @@ void DriveURLRequestJob::SetExtraRequestHeaders( |
| void DriveURLRequestJob::Start() { |
| DVLOG(1) << "Starting request"; |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(!stream_reader_); |
| // We only support GET request. |
| @@ -89,54 +206,122 @@ void DriveURLRequestJob::Start() { |
| return; |
| } |
| - base::FilePath drive_file_path(util::DriveURLToFilePath(request_->url())); |
| - if (drive_file_path.empty()) { |
| - // Not a valid url. |
| + // Check if the scheme is correct. |
| + if (!request()->url().SchemeIs(chrome::kDriveScheme)) { |
| NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED, |
| net::ERR_INVALID_URL)); |
| return; |
| } |
| - // Initialize the stream reader. |
| - stream_reader_.reset( |
| - new DriveFileStreamReader(file_system_getter_, file_task_runner_.get())); |
| - stream_reader_->Initialize( |
| - drive_file_path, |
| - byte_range_, |
| - base::Bind(&DriveURLRequestJob::OnDriveFileStreamReaderInitialized, |
| + // Owned by itself. |
| + new URLHelper(profile_id_, |
| + request()->url(), |
| + base::Bind(&DriveURLRequestJob::OnHelperResultObtained, |
| + weak_ptr_factory_.GetWeakPtr())); |
| +} |
| + |
| +void DriveURLRequestJob::OnHelperResultObtained( |
| + net::Error error, |
| + const scoped_refptr<storage::FileSystemContext>& file_system_context, |
| + const storage::FileSystemURL& file_system_url, |
| + const std::string& mime_type) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + |
| + if (error != net::OK) { |
| + NotifyStartError( |
| + net::URLRequestStatus(net::URLRequestStatus::FAILED, error)); |
| + return; |
| + } |
| + |
| + DCHECK(file_system_context.get()); |
| + file_system_context_ = file_system_context; |
| + file_system_url_ = file_system_url; |
| + mime_type_ = mime_type; |
| + |
| + // Check if the entry has a redirect URL. |
| + file_system_context_->external_backend()->GetRedirectURLForContents( |
| + file_system_url_, |
| + base::Bind(&DriveURLRequestJob::OnRedirectURLObtained, |
| + weak_ptr_factory_.GetWeakPtr())); |
| +} |
| + |
| +void DriveURLRequestJob::OnRedirectURLObtained(const GURL& redirect_url) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + redirect_url_ = redirect_url; |
| + if (!redirect_url_.is_empty()) { |
| + NotifyHeadersComplete(); |
| + return; |
| + } |
| + |
| + // Obtain file system context. |
| + file_system_context_->operation_runner()->GetMetadata( |
| + file_system_url_, |
| + base::Bind(&DriveURLRequestJob::OnFileInfoObtained, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| +void DriveURLRequestJob::OnFileInfoObtained(base::File::Error result, |
| + const base::File::Info& file_info) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + |
| + if (result == base::File::FILE_ERROR_NOT_FOUND) { |
| + NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED, |
| + net::ERR_FILE_NOT_FOUND)); |
| + return; |
| + } |
| + |
| + if (result != base::File::FILE_OK || file_info.is_directory) { |
| + NotifyStartError( |
| + net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED)); |
| + return; |
| + } |
| + |
| + // Compute content size. |
| + int64 offset = 0; |
| + int64 size = storage::kMaximumLength; |
| + if (byte_range_.ComputeBounds(file_info.size)) { |
| + offset = byte_range_.first_byte_position(); |
| + size = byte_range_.last_byte_position() + 1 - |
| + byte_range_.first_byte_position(); |
| + set_expected_content_size(size); |
| + } |
| + remaining_bytes_ = size; |
| + |
| + // Create file stream reader. |
| + stream_reader_ = file_system_context_->CreateFileStreamReader( |
| + file_system_url_, offset, size, base::Time()); |
| + if (!stream_reader_) { |
| + NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED, |
| + net::ERR_FILE_NOT_FOUND)); |
| + return; |
| + } |
| + |
| + NotifyHeadersComplete(); |
| +} |
| + |
| void DriveURLRequestJob::Kill() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| stream_reader_.reset(); |
| + file_system_context_ = NULL; |
| net::URLRequestJob::Kill(); |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| } |
| bool DriveURLRequestJob::GetMimeType(std::string* mime_type) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| - |
| - if (!entry_) { |
| - return false; |
| - } |
| - |
| - mime_type->assign( |
| - FixupMimeType(entry_->file_specific_info().content_mime_type())); |
| + mime_type->assign(mime_type_); |
| return !mime_type->empty(); |
| } |
| bool DriveURLRequestJob::IsRedirectResponse( |
| GURL* location, int* http_status_code) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| - |
| - if (!entry_ || !entry_->file_specific_info().is_hosted_document()) { |
| + if (redirect_url_.is_empty()) |
| return false; |
| - } |
| // Redirect a hosted document. |
| - *location = GURL(entry_->file_specific_info().alternate_url()); |
| + *location = redirect_url_; |
| const int kHttpFound = 302; |
| *http_status_code = kHttpFound; |
| return true; |
| @@ -145,10 +330,16 @@ bool DriveURLRequestJob::IsRedirectResponse( |
| bool DriveURLRequestJob::ReadRawData( |
| net::IOBuffer* buf, int buf_size, int* bytes_read) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| - DCHECK(stream_reader_ && stream_reader_->IsInitialized()); |
| + DCHECK(stream_reader_); |
| + |
| + if (remaining_bytes_ == 0) { |
| + *bytes_read = 0; |
| + return true; |
| + } |
| - int result = stream_reader_->Read( |
| - buf, buf_size, |
| + const int result = stream_reader_->Read( |
| + buf, |
| + std::min(static_cast<int64>(buf_size), remaining_bytes_), |
| base::Bind(&DriveURLRequestJob::OnReadCompleted, |
| weak_ptr_factory_.GetWeakPtr())); |
| @@ -165,35 +356,13 @@ bool DriveURLRequestJob::ReadRawData( |
| // Reading has been finished immediately. |
| *bytes_read = result; |
| + remaining_bytes_ -= result; |
| return true; |
| } |
| DriveURLRequestJob::~DriveURLRequestJob() { |
| } |
| -void DriveURLRequestJob::OnDriveFileStreamReaderInitialized( |
| - int error, scoped_ptr<ResourceEntry> entry) { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| - DCHECK(stream_reader_); |
| - |
| - if (error != FILE_ERROR_OK) { |
| - NotifyStartError( |
| - net::URLRequestStatus(net::URLRequestStatus::FAILED, error)); |
| - return; |
| - } |
| - |
| - DCHECK(entry && entry->has_file_specific_info()); |
| - entry_ = entry.Pass(); |
| - |
| - if (!entry_->file_specific_info().is_hosted_document()) { |
| - // We don't need to set content size for hosted documents, |
| - // because it will be redirected. |
| - set_expected_content_size(entry_->file_info().size()); |
| - } |
| - |
| - NotifyHeadersComplete(); |
| -} |
| - |
| void DriveURLRequestJob::OnReadCompleted(int read_result) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| @@ -203,6 +372,7 @@ void DriveURLRequestJob::OnReadCompleted(int read_result) { |
| read_result)); |
| } |
| + remaining_bytes_ -= read_result; |
| SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status. |
| NotifyReadComplete(read_result); |
| } |