diff options
author | Andras Becsi <andras.becsi@digia.com> | 2013-12-11 21:33:03 +0100 |
---|---|---|
committer | Andras Becsi <andras.becsi@digia.com> | 2013-12-13 12:34:07 +0100 |
commit | f2a33ff9cbc6d19943f1c7fbddd1f23d23975577 (patch) | |
tree | 0586a32aa390ade8557dfd6b4897f43a07449578 /chromium/content/browser/fileapi | |
parent | 5362912cdb5eea702b68ebe23702468d17c3017a (diff) |
Update Chromium to branch 1650 (31.0.1650.63)
Change-Id: I57d8c832eaec1eb2364e0a8e7352a6dd354db99f
Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Diffstat (limited to 'chromium/content/browser/fileapi')
8 files changed, 303 insertions, 200 deletions
diff --git a/chromium/content/browser/fileapi/browser_file_system_helper.cc b/chromium/content/browser/fileapi/browser_file_system_helper.cc index 6df6f1f381c..094f35ac304 100644 --- a/chromium/content/browser/fileapi/browser_file_system_helper.cc +++ b/chromium/content/browser/fileapi/browser_file_system_helper.cc @@ -98,27 +98,6 @@ bool FileSystemURLIsValid( return context->GetFileSystemBackend(url.type()) != NULL; } -bool CheckFileSystemPermissionsForProcess( - fileapi::FileSystemContext* context, int process_id, - const fileapi::FileSystemURL& url, int permissions, - base::PlatformFileError* error) { - DCHECK(error); - - if (!FileSystemURLIsValid(context, url)) { - *error = base::PLATFORM_FILE_ERROR_INVALID_URL; - return false; - } - - if (!ChildProcessSecurityPolicyImpl::GetInstance()-> - HasPermissionsForFileSystemFile(process_id, url, permissions)) { - *error = base::PLATFORM_FILE_ERROR_SECURITY; - return false; - } - - *error = base::PLATFORM_FILE_OK; - return true; -} - void SyncGetPlatformPath(fileapi::FileSystemContext* context, int process_id, const GURL& path, diff --git a/chromium/content/browser/fileapi/browser_file_system_helper.h b/chromium/content/browser/fileapi/browser_file_system_helper.h index ba1e0943628..ad44ced5064 100644 --- a/chromium/content/browser/fileapi/browser_file_system_helper.h +++ b/chromium/content/browser/fileapi/browser_file_system_helper.h @@ -32,14 +32,6 @@ CreateFileSystemContext( CONTENT_EXPORT bool FileSystemURLIsValid(fileapi::FileSystemContext* context, const fileapi::FileSystemURL& url); -// Check whether a process has permission to access the file system URL. -CONTENT_EXPORT bool CheckFileSystemPermissionsForProcess( - fileapi::FileSystemContext* context, - int process_id, - const fileapi::FileSystemURL& url, - int permissions, - base::PlatformFileError* error); - // Get the platform path from a file system URL. This needs to be called // on the FILE thread. CONTENT_EXPORT void SyncGetPlatformPath(fileapi::FileSystemContext* context, diff --git a/chromium/content/browser/fileapi/chrome_blob_storage_context.cc b/chromium/content/browser/fileapi/chrome_blob_storage_context.cc index 728b0d58e25..97151a83f67 100644 --- a/chromium/content/browser/fileapi/chrome_blob_storage_context.cc +++ b/chromium/content/browser/fileapi/chrome_blob_storage_context.cc @@ -7,10 +7,10 @@ #include "base/bind.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" -#include "webkit/browser/blob/blob_storage_controller.h" +#include "webkit/browser/blob/blob_storage_context.h" using base::UserDataAdapter; -using webkit_blob::BlobStorageController; +using webkit_blob::BlobStorageContext; namespace content { @@ -40,7 +40,7 @@ ChromeBlobStorageContext* ChromeBlobStorageContext::GetFor( void ChromeBlobStorageContext::InitializeOnIOThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - controller_.reset(new BlobStorageController()); + context_.reset(new BlobStorageContext()); } ChromeBlobStorageContext::~ChromeBlobStorageContext() {} diff --git a/chromium/content/browser/fileapi/chrome_blob_storage_context.h b/chromium/content/browser/fileapi/chrome_blob_storage_context.h index 3992e0e4198..641e2353006 100644 --- a/chromium/content/browser/fileapi/chrome_blob_storage_context.h +++ b/chromium/content/browser/fileapi/chrome_blob_storage_context.h @@ -11,7 +11,7 @@ #include "content/common/content_export.h" namespace webkit_blob { -class BlobStorageController; +class BlobStorageContext; } namespace content { @@ -36,8 +36,8 @@ class CONTENT_EXPORT ChromeBlobStorageContext void InitializeOnIOThread(); - webkit_blob::BlobStorageController* controller() const { - return controller_.get(); + webkit_blob::BlobStorageContext* context() const { + return context_.get(); } protected: @@ -51,7 +51,7 @@ class CONTENT_EXPORT ChromeBlobStorageContext void DeleteOnCorrectThread() const; - scoped_ptr<webkit_blob::BlobStorageController> controller_; + scoped_ptr<webkit_blob::BlobStorageContext> context_; }; struct ChromeBlobStorageContextDeleter { diff --git a/chromium/content/browser/fileapi/file_system_browsertest.cc b/chromium/content/browser/fileapi/file_system_browsertest.cc index 7c464a36e9f..eafb1b1cfa4 100644 --- a/chromium/content/browser/fileapi/file_system_browsertest.cc +++ b/chromium/content/browser/fileapi/file_system_browsertest.cc @@ -13,7 +13,7 @@ #include "content/public/browser/storage_partition.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "webkit/browser/quota/quota_manager.h" @@ -36,7 +36,8 @@ class FileSystemBrowserTest : public ContentBrowserTest { LOG(INFO) << "Navigating to URL and blocking."; NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2); LOG(INFO) << "Navigation done."; - std::string result = the_browser->web_contents()->GetURL().ref(); + std::string result = + the_browser->web_contents()->GetLastCommittedURL().ref(); if (result != "pass") { std::string js_result; ASSERT_TRUE(ExecuteScriptAndExtractString( diff --git a/chromium/content/browser/fileapi/fileapi_message_filter.cc b/chromium/content/browser/fileapi/fileapi_message_filter.cc index 5641564cff9..1a79ea2d349 100644 --- a/chromium/content/browser/fileapi/fileapi_message_filter.cc +++ b/chromium/content/browser/fileapi/fileapi_message_filter.cc @@ -28,7 +28,8 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "url/gurl.h" -#include "webkit/browser/blob/blob_storage_controller.h" +#include "webkit/browser/blob/blob_storage_context.h" +#include "webkit/browser/blob/blob_storage_host.h" #include "webkit/browser/fileapi/file_observers.h" #include "webkit/browser/fileapi/file_permission_policy.h" #include "webkit/browser/fileapi/file_system_context.h" @@ -37,9 +38,15 @@ #include "webkit/common/blob/blob_data.h" #include "webkit/common/blob/shareable_file_reference.h" #include "webkit/common/fileapi/directory_entry.h" +#include "webkit/common/fileapi/file_system_info.h" #include "webkit/common/fileapi/file_system_types.h" #include "webkit/common/fileapi/file_system_util.h" +#if defined(ENABLE_PLUGINS) +#include "content/browser/renderer_host/pepper/pepper_security_helper.h" +#include "ppapi/shared_impl/file_type_conversion.h" +#endif // defined(ENABLE_PLUGINS) + using fileapi::FileSystemFileUtil; using fileapi::FileSystemBackend; using fileapi::FileSystemOperation; @@ -47,7 +54,8 @@ using fileapi::FileSystemURL; using fileapi::FileUpdateObserver; using fileapi::UpdateObserverList; using webkit_blob::BlobData; -using webkit_blob::BlobStorageController; +using webkit_blob::BlobStorageContext; +using webkit_blob::BlobStorageHost; namespace content { @@ -68,6 +76,7 @@ FileAPIMessageFilter::FileAPIMessageFilter( StreamContext* stream_context) : process_id_(process_id), context_(file_system_context), + security_policy_(ChildProcessSecurityPolicyImpl::GetInstance()), request_context_getter_(request_context_getter), request_context_(NULL), blob_storage_context_(blob_storage_context), @@ -86,6 +95,7 @@ FileAPIMessageFilter::FileAPIMessageFilter( StreamContext* stream_context) : process_id_(process_id), context_(file_system_context), + security_policy_(ChildProcessSecurityPolicyImpl::GetInstance()), request_context_(request_context), blob_storage_context_(blob_storage_context), stream_context_(stream_context) { @@ -106,6 +116,9 @@ void FileAPIMessageFilter::OnChannelConnected(int32 peer_pid) { DCHECK(request_context_); } + blob_storage_host_.reset( + new BlobStorageHost(blob_storage_context_->context())); + operation_runner_ = context_->CreateFileSystemOperationRunner(); } @@ -115,10 +128,7 @@ void FileAPIMessageFilter::OnChannelClosing() { // Unregister all the blob and stream URLs that are previously registered in // this process. - for (base::hash_set<std::string>::const_iterator iter = blob_urls_.begin(); - iter != blob_urls_.end(); ++iter) { - blob_storage_context_->controller()->RemoveBlob(GURL(*iter)); - } + blob_storage_host_.reset(); for (base::hash_set<std::string>::const_iterator iter = stream_urls_.begin(); iter != stream_urls_.end(); ++iter) { stream_context_->registry()->UnregisterStream(GURL(*iter)); @@ -159,6 +169,7 @@ bool FileAPIMessageFilter::OnMessageReceived( bool handled = true; IPC_BEGIN_MESSAGE_MAP_EX(FileAPIMessageFilter, message, *message_was_ok) IPC_MESSAGE_HANDLER(FileSystemHostMsg_Open, OnOpen) + IPC_MESSAGE_HANDLER(FileSystemHostMsg_ResolveURL, OnResolveURL) IPC_MESSAGE_HANDLER(FileSystemHostMsg_DeleteFileSystem, OnDeleteFileSystem) IPC_MESSAGE_HANDLER(FileSystemHostMsg_Move, OnMove) IPC_MESSAGE_HANDLER(FileSystemHostMsg_Copy, OnCopy) @@ -168,10 +179,13 @@ bool FileAPIMessageFilter::OnMessageReceived( IPC_MESSAGE_HANDLER(FileSystemHostMsg_Exists, OnExists) IPC_MESSAGE_HANDLER(FileSystemHostMsg_ReadDirectory, OnReadDirectory) IPC_MESSAGE_HANDLER(FileSystemHostMsg_Write, OnWrite) + IPC_MESSAGE_HANDLER(FileSystemHostMsg_WriteDeprecated, OnWriteDeprecated) IPC_MESSAGE_HANDLER(FileSystemHostMsg_Truncate, OnTruncate) IPC_MESSAGE_HANDLER(FileSystemHostMsg_TouchFile, OnTouchFile) IPC_MESSAGE_HANDLER(FileSystemHostMsg_CancelWrite, OnCancel) - IPC_MESSAGE_HANDLER(FileSystemHostMsg_OpenFile, OnOpenFile) +#if defined(ENABLE_PLUGINS) + IPC_MESSAGE_HANDLER(FileSystemHostMsg_OpenPepperFile, OnOpenPepperFile) +#endif // defined(ENABLE_PLUGINS) IPC_MESSAGE_HANDLER(FileSystemHostMsg_NotifyCloseFile, OnNotifyCloseFile) IPC_MESSAGE_HANDLER(FileSystemHostMsg_CreateSnapshotFile, OnCreateSnapshotFile) @@ -187,14 +201,26 @@ bool FileAPIMessageFilter::OnMessageReceived( IPC_MESSAGE_HANDLER(BlobHostMsg_SyncAppendSharedMemory, OnAppendSharedMemoryToBlob) IPC_MESSAGE_HANDLER(BlobHostMsg_FinishBuilding, OnFinishBuildingBlob) - IPC_MESSAGE_HANDLER(BlobHostMsg_Clone, OnCloneBlob) - IPC_MESSAGE_HANDLER(BlobHostMsg_Remove, OnRemoveBlob) + IPC_MESSAGE_HANDLER(BlobHostMsg_IncrementRefCount, + OnIncrementBlobRefCount) + IPC_MESSAGE_HANDLER(BlobHostMsg_DecrementRefCount, + OnDecrementBlobRefCount) + IPC_MESSAGE_HANDLER(BlobHostMsg_RegisterPublicURL, + OnRegisterPublicBlobURL) + IPC_MESSAGE_HANDLER(BlobHostMsg_RevokePublicURL, OnRevokePublicBlobURL) + IPC_MESSAGE_HANDLER(BlobHostMsg_DeprecatedRegisterBlobURL, + OnDeprecatedRegisterBlobURL) + IPC_MESSAGE_HANDLER(BlobHostMsg_DeprecatedRevokeBlobURL, + OnDeprecatedRevokeBlobURL) + IPC_MESSAGE_HANDLER(BlobHostMsg_DeprecatedCloneBlobURL, + OnDeprecatedCloneBlobURL) IPC_MESSAGE_HANDLER(StreamHostMsg_StartBuilding, OnStartBuildingStream) IPC_MESSAGE_HANDLER(StreamHostMsg_AppendBlobDataItem, OnAppendBlobDataItemToStream) IPC_MESSAGE_HANDLER(StreamHostMsg_SyncAppendSharedMemory, OnAppendSharedMemoryToStream) IPC_MESSAGE_HANDLER(StreamHostMsg_FinishBuilding, OnFinishBuildingStream) + IPC_MESSAGE_HANDLER(StreamHostMsg_AbortBuilding, OnAbortBuildingStream) IPC_MESSAGE_HANDLER(StreamHostMsg_Clone, OnCloneStream) IPC_MESSAGE_HANDLER(StreamHostMsg_Remove, OnRemoveStream) IPC_MESSAGE_UNHANDLED(handled = false) @@ -226,6 +252,23 @@ void FileAPIMessageFilter::OnOpen( &FileAPIMessageFilter::DidOpenFileSystem, this, request_id)); } +void FileAPIMessageFilter::OnResolveURL( + int request_id, + const GURL& filesystem_url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + FileSystemURL url(context_->CrackURL(filesystem_url)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanReadFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); + return; + } + + context_->ResolveURL(url, base::Bind( + &FileAPIMessageFilter::DidResolveURL, this, request_id)); +} + void FileAPIMessageFilter::OnDeleteFileSystem( int request_id, const GURL& origin_url, @@ -238,15 +281,17 @@ void FileAPIMessageFilter::OnDeleteFileSystem( void FileAPIMessageFilter::OnMove( int request_id, const GURL& src_path, const GURL& dest_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL src_url(context_->CrackURL(src_path)); FileSystemURL dest_url(context_->CrackURL(dest_path)); - const int src_permissions = - fileapi::kReadFilePermissions | fileapi::kWriteFilePermissions; - if (!HasPermissionsForFile(src_url, src_permissions, &error) || - !HasPermissionsForFile( - dest_url, fileapi::kCreateFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, src_url) || + !ValidateFileSystemURL(request_id, dest_url)) { + return; + } + if (!security_policy_->CanReadFileSystemFile(process_id_, src_url) || + !security_policy_->CanWriteFileSystemFile(process_id_, src_url) || + !security_policy_->CanCreateFileSystemFile(process_id_, dest_url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -258,28 +303,34 @@ void FileAPIMessageFilter::OnMove( void FileAPIMessageFilter::OnCopy( int request_id, const GURL& src_path, const GURL& dest_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL src_url(context_->CrackURL(src_path)); FileSystemURL dest_url(context_->CrackURL(dest_path)); - if (!HasPermissionsForFile(src_url, fileapi::kReadFilePermissions, &error) || - !HasPermissionsForFile( - dest_url, fileapi::kCreateFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, src_url) || + !ValidateFileSystemURL(request_id, dest_url)) { + return; + } + if (!security_policy_->CanReadFileSystemFile(process_id_, src_url) || + !security_policy_->CanCreateFileSystemFile(process_id_, dest_url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } operations_[request_id] = operation_runner()->Copy( src_url, dest_url, + fileapi::FileSystemOperationRunner::CopyProgressCallback(), base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id)); } void FileAPIMessageFilter::OnRemove( int request_id, const GURL& path, bool recursive) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, fileapi::kWriteFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -291,10 +342,12 @@ void FileAPIMessageFilter::OnRemove( void FileAPIMessageFilter::OnReadMetadata( int request_id, const GURL& path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, fileapi::kReadFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanReadFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -306,10 +359,12 @@ void FileAPIMessageFilter::OnCreate( int request_id, const GURL& path, bool exclusive, bool is_directory, bool recursive) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, fileapi::kCreateFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanCreateFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -327,10 +382,12 @@ void FileAPIMessageFilter::OnCreate( void FileAPIMessageFilter::OnExists( int request_id, const GURL& path, bool is_directory) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, fileapi::kReadFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanReadFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -348,10 +405,12 @@ void FileAPIMessageFilter::OnExists( void FileAPIMessageFilter::OnReadDirectory( int request_id, const GURL& path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, fileapi::kReadFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanReadFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -360,11 +419,21 @@ void FileAPIMessageFilter::OnReadDirectory( this, request_id)); } -void FileAPIMessageFilter::OnWrite( +void FileAPIMessageFilter::OnWriteDeprecated( int request_id, const GURL& path, const GURL& blob_url, int64 offset) { + std::string uuid = + blob_storage_context_->context()->LookupUuidFromDeprecatedURL(blob_url); + OnWrite(request_id, path, uuid, offset); +} + +void FileAPIMessageFilter::OnWrite( + int request_id, + const GURL& path, + const std::string& blob_uuid, + int64 offset) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!request_context_) { // We can't write w/o a request context, trying to do so will crash. @@ -373,14 +442,19 @@ void FileAPIMessageFilter::OnWrite( } FileSystemURL url(context_->CrackURL(path)); - base::PlatformFileError error; - if (!HasPermissionsForFile(url, fileapi::kWriteFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } + scoped_ptr<webkit_blob::BlobDataHandle> blob = + blob_storage_context_->context()->GetBlobDataFromUUID(blob_uuid); + operations_[request_id] = operation_runner()->Write( - request_context_, url, blob_url, offset, + request_context_, url, blob.Pass(), offset, base::Bind(&FileAPIMessageFilter::DidWrite, this, request_id)); } @@ -388,10 +462,12 @@ void FileAPIMessageFilter::OnTruncate( int request_id, const GURL& path, int64 length) { - base::PlatformFileError error; FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, fileapi::kWriteFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -407,9 +483,11 @@ void FileAPIMessageFilter::OnTouchFile( const base::Time& last_modified_time) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); FileSystemURL url(context_->CrackURL(path)); - base::PlatformFileError error; - if (!HasPermissionsForFile(url, fileapi::kCreateFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanCreateFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -437,14 +515,16 @@ void FileAPIMessageFilter::OnCancel( } } -void FileAPIMessageFilter::OnOpenFile( - int request_id, const GURL& path, int file_flags) { +#if defined(ENABLE_PLUGINS) +void FileAPIMessageFilter::OnOpenPepperFile( + int request_id, const GURL& path, int pp_open_flags) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; - const int open_permissions = file_flags & fileapi::kOpenPepperFilePermissions; - FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, open_permissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); +FileSystemURL url(context_->CrackURL(path)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!CanOpenFileSystemURLWithPepperFlags(pp_open_flags, process_id_, url)) { + Send(new FileSystemMsg_DidFail( + request_id, base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -461,11 +541,19 @@ void FileAPIMessageFilter::OnOpenFile( quota_policy = quota::kQuotaLimitTypeLimited; } + int platform_file_flags = 0; + if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(pp_open_flags, + &platform_file_flags)) { + // |pp_open_flags| should have already been checked in PepperFileIOHost. + NOTREACHED() << "Open file request with invalid pp_open_flags ignored."; + } + operations_[request_id] = operation_runner()->OpenFile( - url, open_permissions, PeerHandle(), + url, platform_file_flags, PeerHandle(), base::Bind(&FileAPIMessageFilter::DidOpenFile, this, request_id, quota_policy)); } +#endif // defined(ENABLE_PLUGINS) void FileAPIMessageFilter::OnNotifyCloseFile(int file_open_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); @@ -518,9 +606,11 @@ void FileAPIMessageFilter::OnCreateSnapshotFile( // Make sure if this file can be read by the renderer as this is // called when the renderer is about to create a new File object // (for reading the file). - base::PlatformFileError error; - if (!HasPermissionsForFile(url, fileapi::kReadFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanReadFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -535,39 +625,38 @@ void FileAPIMessageFilter::OnDidReceiveSnapshotFile(int request_id) { in_transit_snapshot_files_.erase(request_id); } -void FileAPIMessageFilter::OnStartBuildingBlob(const GURL& url) { +void FileAPIMessageFilter::OnStartBuildingBlob(const std::string& uuid) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - blob_storage_context_->controller()->StartBuildingBlob(url); - blob_urls_.insert(url.spec()); + ignore_result(blob_storage_host_->StartBuildingBlob(uuid)); } void FileAPIMessageFilter::OnAppendBlobDataItemToBlob( - const GURL& url, const BlobData::Item& item) { + const std::string& uuid, const BlobData::Item& item) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (item.type() == BlobData::Item::TYPE_FILE_FILESYSTEM) { - base::PlatformFileError error; - FileSystemURL filesystem_url(context_->CrackURL(item.url())); - if (!HasPermissionsForFile(filesystem_url, - fileapi::kReadFilePermissions, &error)) { - OnRemoveBlob(url); + FileSystemURL filesystem_url(context_->CrackURL(item.filesystem_url())); + if (!FileSystemURLIsValid(context_, filesystem_url) || + !security_policy_->CanReadFileSystemFile(process_id_, filesystem_url)) { + ignore_result(blob_storage_host_->CancelBuildingBlob(uuid)); return; } } if (item.type() == BlobData::Item::TYPE_FILE && - !ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( - process_id_, item.path())) { - OnRemoveBlob(url); + !security_policy_->CanReadFile(process_id_, item.path())) { + ignore_result(blob_storage_host_->CancelBuildingBlob(uuid)); return; } if (item.length() == 0) { BadMessageReceived(); return; } - blob_storage_context_->controller()->AppendBlobDataItem(url, item); + ignore_result(blob_storage_host_->AppendBlobDataItem(uuid, item)); } void FileAPIMessageFilter::OnAppendSharedMemoryToBlob( - const GURL& url, base::SharedMemoryHandle handle, size_t buffer_size) { + const std::string& uuid, + base::SharedMemoryHandle handle, + size_t buffer_size) { DCHECK(base::SharedMemory::IsHandleValid(handle)); if (!buffer_size) { BadMessageReceived(); @@ -579,33 +668,59 @@ void FileAPIMessageFilter::OnAppendSharedMemoryToBlob( base::SharedMemory shared_memory(handle, true); #endif if (!shared_memory.Map(buffer_size)) { - OnRemoveBlob(url); + ignore_result(blob_storage_host_->CancelBuildingBlob(uuid)); return; } BlobData::Item item; item.SetToSharedBytes(static_cast<char*>(shared_memory.memory()), buffer_size); - blob_storage_context_->controller()->AppendBlobDataItem(url, item); + ignore_result(blob_storage_host_->AppendBlobDataItem(uuid, item)); } void FileAPIMessageFilter::OnFinishBuildingBlob( - const GURL& url, const std::string& content_type) { + const std::string& uuid, const std::string& content_type) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ignore_result(blob_storage_host_->FinishBuildingBlob(uuid, content_type)); + // TODO(michaeln): check return values once blink has migrated, crbug/174200 +} + +void FileAPIMessageFilter::OnIncrementBlobRefCount(const std::string& uuid) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ignore_result(blob_storage_host_->IncrementBlobRefCount(uuid)); +} + +void FileAPIMessageFilter::OnDecrementBlobRefCount(const std::string& uuid) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ignore_result(blob_storage_host_->DecrementBlobRefCount(uuid)); +} + +void FileAPIMessageFilter::OnRegisterPublicBlobURL( + const GURL& public_url, const std::string& uuid) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ignore_result(blob_storage_host_->RegisterPublicBlobURL(public_url, uuid)); +} + +void FileAPIMessageFilter::OnRevokePublicBlobURL(const GURL& public_url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ignore_result(blob_storage_host_->RevokePublicBlobURL(public_url)); +} + +void FileAPIMessageFilter::OnDeprecatedRegisterBlobURL( + const GURL& url, const std::string& uuid) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - blob_storage_context_->controller()->FinishBuildingBlob(url, content_type); + blob_storage_host_->DeprecatedRegisterBlobURL(url, uuid); } -void FileAPIMessageFilter::OnCloneBlob( +void FileAPIMessageFilter::OnDeprecatedCloneBlobURL( const GURL& url, const GURL& src_url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - blob_storage_context_->controller()->CloneBlob(url, src_url); - blob_urls_.insert(url.spec()); + blob_storage_host_->DeprecatedCloneBlobURL(url, src_url); } -void FileAPIMessageFilter::OnRemoveBlob(const GURL& url) { +void FileAPIMessageFilter::OnDeprecatedRevokeBlobURL(const GURL& url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - blob_storage_context_->controller()->RemoveBlob(url); - blob_urls_.erase(url.spec()); + blob_storage_host_->DeprecatedRevokeBlobURL(url); } void FileAPIMessageFilter::OnStartBuildingStream( @@ -631,10 +746,10 @@ void FileAPIMessageFilter::OnAppendBlobDataItemToStream( DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); scoped_refptr<Stream> stream(GetStreamForURL(url)); - if (!stream.get()) { - NOTREACHED(); + // Stream instances may be deleted on error. Just abort if there's no Stream + // instance for |url| in the registry. + if (!stream.get()) return; - } // Data for stream is delivered as TYPE_BYTES item. if (item.type() != BlobData::Item::TYPE_BYTES) { @@ -662,10 +777,8 @@ void FileAPIMessageFilter::OnAppendSharedMemoryToStream( } scoped_refptr<Stream> stream(GetStreamForURL(url)); - if (!stream.get()) { - NOTREACHED(); + if (!stream.get()) return; - } stream->AddData(static_cast<char*>(shared_memory.memory()), buffer_size); } @@ -675,17 +788,22 @@ void FileAPIMessageFilter::OnFinishBuildingStream(const GURL& url) { scoped_refptr<Stream> stream(GetStreamForURL(url)); if (stream.get()) stream->Finalize(); - else - NOTREACHED(); +} + +void FileAPIMessageFilter::OnAbortBuildingStream(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + scoped_refptr<Stream> stream(GetStreamForURL(url)); + if (stream.get()) + stream->Abort(); } void FileAPIMessageFilter::OnCloneStream( const GURL& url, const GURL& src_url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!GetStreamForURL(src_url)) { - NOTREACHED(); + // Abort if there's no Stream instance for |src_url| (source Stream which + // we're going to make |url| point to) in the registry. + if (!GetStreamForURL(src_url)) return; - } stream_context_->registry()->CloneStream(url, src_url); stream_urls_.insert(url.spec()); @@ -694,10 +812,8 @@ void FileAPIMessageFilter::OnCloneStream( void FileAPIMessageFilter::OnRemoveStream(const GURL& url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!GetStreamForURL(url).get()) { - NOTREACHED(); + if (!GetStreamForURL(url).get()) return; - } stream_context_->registry()->UnregisterStream(url); stream_urls_.erase(url.spec()); @@ -776,18 +892,35 @@ void FileAPIMessageFilter::DidWrite(int request_id, void FileAPIMessageFilter::DidOpenFileSystem(int request_id, base::PlatformFileError result, - const std::string& name, + const std::string& filesystem_name, const GURL& root) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (result == base::PLATFORM_FILE_OK) { DCHECK(root.is_valid()); - Send(new FileSystemMsg_DidOpenFileSystem(request_id, name, root)); + Send(new FileSystemMsg_DidOpenFileSystem( + request_id, filesystem_name, root)); } else { Send(new FileSystemMsg_DidFail(request_id, result)); } // For OpenFileSystem we do not create a new operation, so no unregister here. } +void FileAPIMessageFilter::DidResolveURL(int request_id, + base::PlatformFileError result, + const fileapi::FileSystemInfo& info, + const base::FilePath& file_path, + bool is_directory) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (result == base::PLATFORM_FILE_OK) { + DCHECK(info.root_url.is_valid()); + Send(new FileSystemMsg_DidResolveURL( + request_id, info, file_path, is_directory)); + } else { + Send(new FileSystemMsg_DidFail(request_id, result)); + } + // For ResolveURL we do not create a new operation, so no unregister here. +} + void FileAPIMessageFilter::DidDeleteFileSystem( int request_id, base::PlatformFileError result) { @@ -805,7 +938,7 @@ void FileAPIMessageFilter::DidCreateSnapshot( base::PlatformFileError result, const base::PlatformFileInfo& info, const base::FilePath& platform_path, - const scoped_refptr<webkit_blob::ShareableFileReference>& snapshot_file) { + const scoped_refptr<webkit_blob::ShareableFileReference>& /* unused */) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); operations_.erase(request_id); @@ -814,17 +947,16 @@ void FileAPIMessageFilter::DidCreateSnapshot( return; } - scoped_refptr<webkit_blob::ShareableFileReference> file_ref = snapshot_file; - if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( - process_id_, platform_path)) { + scoped_refptr<webkit_blob::ShareableFileReference> file_ref = + webkit_blob::ShareableFileReference::Get(platform_path); + if (!security_policy_->CanReadFile(process_id_, platform_path)) { // Give per-file read permission to the snapshot file if it hasn't it yet. // In order for the renderer to be able to read the file via File object, // it must be granted per-file read permission for the file's platform // path. By now, it has already been verified that the renderer has // sufficient permissions to read the file, so giving per-file permission // here must be safe. - ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile( - process_id_, platform_path); + security_policy_->GrantReadFile(process_id_, platform_path); // Revoke all permissions for the file when the last ref of the file // is dropped. @@ -849,10 +981,13 @@ void FileAPIMessageFilter::DidCreateSnapshot( request_id, info, platform_path)); } -bool FileAPIMessageFilter::HasPermissionsForFile( - const FileSystemURL& url, int permissions, base::PlatformFileError* error) { - return CheckFileSystemPermissionsForProcess(context_, process_id_, url, - permissions, error); +bool FileAPIMessageFilter::ValidateFileSystemURL( + int request_id, const fileapi::FileSystemURL& url) { + if (FileSystemURLIsValid(context_, url)) + return true; + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_INVALID_URL)); + return false; } scoped_refptr<Stream> FileAPIMessageFilter::GetStreamForURL(const GURL& url) { diff --git a/chromium/content/browser/fileapi/fileapi_message_filter.h b/chromium/content/browser/fileapi/fileapi_message_filter.h index 8680f24a998..e9a707f4ac4 100644 --- a/chromium/content/browser/fileapi/fileapi_message_filter.h +++ b/chromium/content/browser/fileapi/fileapi_message_filter.h @@ -37,6 +37,7 @@ class FileSystemURL; class FileSystemContext; class FileSystemOperationRunner; struct DirectoryEntry; +struct FileSystemInfo; } namespace net { @@ -45,10 +46,12 @@ class URLRequestContextGetter; } // namespace net namespace webkit_blob { +class BlobStorageHost; class ShareableFileReference; } namespace content { +class ChildProcessSecurityPolicyImpl; class ChromeBlobStorageContext; // TODO(tyoshino): Factor out code except for IPC gluing from @@ -91,6 +94,8 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { fileapi::FileSystemType type, int64 requested_size, bool create); + void OnResolveURL(int request_id, + const GURL& filesystem_url); void OnDeleteFileSystem(int request_id, const GURL& origin_url, fileapi::FileSystemType type); @@ -111,6 +116,11 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { void OnReadDirectory(int request_id, const GURL& path); void OnWrite(int request_id, const GURL& path, + const std::string& blob_uuid, + int64 offset); + void OnWriteDeprecated( + int request_id, + const GURL& path, const GURL& blob_url, int64 offset); void OnTruncate(int request_id, const GURL& path, int64 length); @@ -119,7 +129,9 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { const base::Time& last_access_time, const base::Time& last_modified_time); void OnCancel(int request_id, int request_to_cancel); - void OnOpenFile(int request_id, const GURL& path, int file_flags); +#if defined(ENABLE_PLUGINS) + void OnOpenPepperFile(int request_id, const GURL& path, int pp_open_flags); +#endif // defined(ENABLE_PLUGINS) void OnNotifyCloseFile(int file_open_id); void OnWillUpdate(const GURL& path); void OnDidUpdate(const GURL& path, int64 delta); @@ -131,14 +143,26 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { // Handlers for BlobHostMsg_ family messages. - void OnStartBuildingBlob(const GURL& url); - void OnAppendBlobDataItemToBlob( - const GURL& url, const webkit_blob::BlobData::Item& item); - void OnAppendSharedMemoryToBlob( - const GURL& url, base::SharedMemoryHandle handle, size_t buffer_size); - void OnFinishBuildingBlob(const GURL& url, const std::string& content_type); - void OnCloneBlob(const GURL& url, const GURL& src_url); - void OnRemoveBlob(const GURL& url); + void OnStartBuildingBlob(const std::string& uuid); + void OnAppendBlobDataItemToBlob(const std::string& uuid, + const webkit_blob::BlobData::Item& item); + void OnAppendSharedMemoryToBlob(const std::string& uuid, + base::SharedMemoryHandle handle, + size_t buffer_size); + void OnFinishBuildingBlob(const std::string& uuid, + const std::string& content_type); + void OnCancelBuildingBlob(const std::string& uuid); + void OnIncrementBlobRefCount(const std::string& uuid); + void OnDecrementBlobRefCount(const std::string& uuid); + void OnRegisterPublicBlobURL(const GURL& public_url, const std::string& uuid); + void OnRevokePublicBlobURL(const GURL& public_url); + + // Extra methods to establish a mapping from old-style blobURLs to uuids, + // and to clone them. These won't be here for long, just during a + // transition period. See crbug/174200 + void OnDeprecatedRegisterBlobURL(const GURL& url, const std::string& uuid); + void OnDeprecatedCloneBlobURL(const GURL& url, const GURL& existing_url); + void OnDeprecatedRevokeBlobURL(const GURL& url); // Handlers for StreamHostMsg_ family messages. // @@ -154,6 +178,7 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { void OnAppendSharedMemoryToStream( const GURL& url, base::SharedMemoryHandle handle, size_t buffer_size); void OnFinishBuildingStream(const GURL& url); + void OnAbortBuildingStream(const GURL& url); void OnCloneStream(const GURL& url, const GURL& src_url); void OnRemoveStream(const GURL& url); @@ -179,8 +204,13 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { bool complete); void DidOpenFileSystem(int request_id, base::PlatformFileError result, - const std::string& name, + const std::string& filesystem_name, const GURL& root); + void DidResolveURL(int request_id, + base::PlatformFileError result, + const fileapi::FileSystemInfo& info, + const base::FilePath& file_path, + bool is_directory); void DidDeleteFileSystem(int request_id, base::PlatformFileError result); void DidCreateSnapshot( @@ -191,12 +221,12 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { const base::FilePath& platform_path, const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref); - // Checks renderer's access permissions for single file. - bool HasPermissionsForFile(const fileapi::FileSystemURL& url, - int permissions, - base::PlatformFileError* error); + // Sends a FileSystemMsg_DidFail and returns false if |url| is invalid. + bool ValidateFileSystemURL(int request_id, const fileapi::FileSystemURL& url); - // Retrieves the Stream object for |url| from |stream_context_|. + // Retrieves the Stream object for |url| from |stream_context_|. Returns unset + // scoped_refptr when there's no Stream instance for the given |url| + // registered with stream_context_->registry(). scoped_refptr<Stream> GetStreamForURL(const GURL& url); fileapi::FileSystemOperationRunner* operation_runner() { @@ -206,6 +236,7 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { int process_id_; fileapi::FileSystemContext* context_; + ChildProcessSecurityPolicyImpl* security_policy_; // Keeps map from request_id to OperationID for ongoing operations. // (Primarily for Cancel operation) @@ -222,9 +253,9 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { scoped_ptr<fileapi::FileSystemOperationRunner> operation_runner_; - // Keep track of blob URLs registered in this process. Need to unregister - // all of them when the renderer process dies. - base::hash_set<std::string> blob_urls_; + // Keeps track of blobs used in this process and cleans up + // when the renderer process dies. + scoped_ptr<webkit_blob::BlobStorageHost> blob_storage_host_; // Keep track of stream URLs registered in this process. Need to unregister // all of them when the renderer process dies. diff --git a/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc b/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc index 06b112ba64d..90e422c7da6 100644 --- a/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc +++ b/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc @@ -23,7 +23,7 @@ #include "content/public/test/test_browser_thread.h" #include "net/base/io_buffer.h" #include "testing/gtest/include/gtest/gtest.h" -#include "webkit/browser/blob/blob_storage_controller.h" +#include "webkit/browser/blob/blob_storage_context.h" #include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/mock_file_system_context.h" #include "webkit/common/blob/blob_data.h" @@ -95,34 +95,6 @@ class FileAPIMessageFilterTest : public testing::Test { scoped_refptr<FileAPIMessageFilter> filter_; }; -TEST_F(FileAPIMessageFilterTest, BuildEmptyBlob) { - webkit_blob::BlobStorageController* controller = - blob_storage_context_->controller(); - - const GURL kUrl("blob:foobar"); - const GURL kDifferentUrl("blob:barfoo"); - - EXPECT_EQ(NULL, controller->GetBlobDataFromUrl(kUrl)); - - BlobHostMsg_StartBuilding start_message(kUrl); - EXPECT_TRUE(InvokeOnMessageReceived(start_message)); - - // Blob is still being built. Nothing should be returned. - EXPECT_EQ(NULL, controller->GetBlobDataFromUrl(kUrl)); - - BlobHostMsg_FinishBuilding finish_message(kUrl, kFakeContentType); - EXPECT_TRUE(InvokeOnMessageReceived(finish_message)); - - // Now, Blob is built. - webkit_blob::BlobData* blob_data = controller->GetBlobDataFromUrl(kUrl); - ASSERT_FALSE(blob_data == NULL); - EXPECT_EQ(0U, blob_data->items().size()); - EXPECT_EQ(kFakeContentType, blob_data->content_type()); - - // Nothing should be returned for a URL we didn't use. - EXPECT_TRUE(controller->GetBlobDataFromUrl(kDifferentUrl) == NULL); -} - TEST_F(FileAPIMessageFilterTest, CloseChannelWithInflightRequest) { scoped_refptr<FileAPIMessageFilter> filter( new FileAPIMessageFilter( @@ -190,14 +162,10 @@ TEST_F(FileAPIMessageFilterTest, MultipleFilters) { TEST_F(FileAPIMessageFilterTest, BuildEmptyStream) { StreamRegistry* stream_registry = stream_context_->registry(); - webkit_blob::BlobStorageController* blob_controller = - blob_storage_context_->controller(); - const GURL kUrl(kFakeBlobInternalUrlSpec); const GURL kDifferentUrl("blob:barfoo"); EXPECT_EQ(NULL, stream_registry->GetStream(kUrl).get()); - EXPECT_EQ(NULL, blob_controller->GetBlobDataFromUrl(kUrl)); StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType); EXPECT_TRUE(InvokeOnMessageReceived(start_message)); @@ -217,9 +185,6 @@ TEST_F(FileAPIMessageFilterTest, BuildEmptyStream) { StreamHostMsg_FinishBuilding finish_message(kUrl); EXPECT_TRUE(InvokeOnMessageReceived(finish_message)); - // Blob controller shouldn't be affected. - EXPECT_EQ(NULL, blob_controller->GetBlobDataFromUrl(kUrl)); - stream = stream_registry->GetStream(kUrl); ASSERT_FALSE(stream.get() == NULL); EXPECT_EQ(Stream::STREAM_EMPTY, |