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..be8e7ebb59bdcb69179c7a4d24e9efbc408dc7cc 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 kMimeTypeForRFC822[] = "message/rfc822"; |
+const char kMimeTypeForMHTML[] = "multipart/related"; |
+ |
+// 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_ == kMimeTypeForRFC822) |
+ mime_type_ = kMimeTypeForMHTML; |
+ |
+ 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,124 @@ 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 || |
+ file_info.size < 0) { |
+ NotifyStartError( |
+ net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED)); |
+ return; |
+ } |
+ |
+ // Compute content size. |
+ if (!byte_range_.ComputeBounds(file_info.size)) { |
+ NotifyStartError(net::URLRequestStatus( |
+ net::URLRequestStatus::FAILED, net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); |
+ return; |
+ } |
+ const int64 offset = byte_range_.first_byte_position(); |
+ const int64 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,12 +332,18 @@ 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_); |
- int result = stream_reader_->Read( |
- buf, buf_size, |
- base::Bind(&DriveURLRequestJob::OnReadCompleted, |
- weak_ptr_factory_.GetWeakPtr())); |
+ if (remaining_bytes_ == 0) { |
+ *bytes_read = 0; |
+ return true; |
+ } |
+ |
+ const int result = |
+ stream_reader_->Read(buf, |
+ std::min<int64>(buf_size, remaining_bytes_), |
+ base::Bind(&DriveURLRequestJob::OnReadCompleted, |
+ weak_ptr_factory_.GetWeakPtr())); |
if (result == net::ERR_IO_PENDING) { |
// The data is not yet available. |
@@ -165,35 +358,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 +374,7 @@ void DriveURLRequestJob::OnReadCompleted(int read_result) { |
read_result)); |
} |
+ remaining_bytes_ -= read_result; |
SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status. |
NotifyReadComplete(read_result); |
} |