1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/browser/fileapi/fileapi_message_filter.h" 6 7#include <string> 8#include <vector> 9 10#include "base/memory/ref_counted.h" 11#include "base/memory/shared_memory.h" 12#include "base/message_loop/message_loop.h" 13#include "base/process/process.h" 14#include "content/browser/child_process_security_policy_impl.h" 15#include "content/browser/fileapi/chrome_blob_storage_context.h" 16#include "content/browser/streams/stream_registry.h" 17#include "content/common/fileapi/file_system_messages.h" 18#include "content/common/fileapi/webblob_messages.h" 19#include "content/public/browser/browser_thread.h" 20#include "content/public/common/common_param_traits.h" 21#include "content/public/test/mock_render_process_host.h" 22#include "content/public/test/test_browser_context.h" 23#include "content/public/test/test_browser_thread.h" 24#include "content/public/test/test_file_system_context.h" 25#include "net/base/io_buffer.h" 26#include "storage/browser/blob/blob_storage_context.h" 27#include "storage/browser/fileapi/file_system_context.h" 28#include "storage/common/blob/blob_data.h" 29#include "testing/gtest/include/gtest/gtest.h" 30 31namespace content { 32 33namespace { 34 35const char kFakeBlobInternalUrlSpec[] = 36 "blob:blobinternal%3A///dc83ede4-9bbd-453b-be2e-60fd623fcc93"; 37const char kFakeBlobInternalUrlSpec2[] = 38 "blob:blobinternal%3A///d28ae2e7-d233-4dda-9598-d135fe5d403e"; 39 40const char kFakeContentType[] = "fake/type"; 41 42} // namespace 43 44class FileAPIMessageFilterTest : public testing::Test { 45 public: 46 FileAPIMessageFilterTest() 47 : io_browser_thread_(BrowserThread::IO, &message_loop_) { 48 } 49 50 protected: 51 virtual void SetUp() OVERRIDE { 52 file_system_context_ = 53 CreateFileSystemContextForTesting(NULL, base::FilePath()); 54 55 std::vector<storage::FileSystemType> types; 56 file_system_context_->GetFileSystemTypes(&types); 57 for (size_t i = 0; i < types.size(); ++i) { 58 ChildProcessSecurityPolicyImpl::GetInstance() 59 ->RegisterFileSystemPermissionPolicy( 60 types[i], 61 storage::FileSystemContext::GetPermissionPolicy(types[i])); 62 } 63 64 stream_context_ = StreamContext::GetFor(&browser_context_); 65 blob_storage_context_ = ChromeBlobStorageContext::GetFor(&browser_context_); 66 67 filter_ = new FileAPIMessageFilter( 68 0 /* process_id */, 69 browser_context_.GetRequestContext(), 70 file_system_context_.get(), 71 blob_storage_context_, 72 stream_context_); 73 74 // Complete initialization. 75 message_loop_.RunUntilIdle(); 76 } 77 78 base::MessageLoop message_loop_; 79 TestBrowserThread io_browser_thread_; 80 81 TestBrowserContext browser_context_; 82 scoped_refptr<storage::FileSystemContext> file_system_context_; 83 StreamContext* stream_context_; 84 ChromeBlobStorageContext* blob_storage_context_; 85 86 scoped_refptr<FileAPIMessageFilter> filter_; 87}; 88 89TEST_F(FileAPIMessageFilterTest, CloseChannelWithInflightRequest) { 90 scoped_refptr<FileAPIMessageFilter> filter( 91 new FileAPIMessageFilter( 92 0 /* process_id */, 93 browser_context_.GetRequestContext(), 94 file_system_context_.get(), 95 ChromeBlobStorageContext::GetFor(&browser_context_), 96 StreamContext::GetFor(&browser_context_))); 97 filter->OnChannelConnected(0); 98 99 // Complete initialization. 100 message_loop_.RunUntilIdle(); 101 102 int request_id = 0; 103 const GURL kUrl("filesystem:http://example.com/temporary/foo"); 104 FileSystemHostMsg_ReadMetadata read_metadata(request_id++, kUrl); 105 EXPECT_TRUE(filter->OnMessageReceived(read_metadata)); 106 107 // Close the filter while it has inflight request. 108 filter->OnChannelClosing(); 109 110 // This shouldn't cause DCHECK failure. 111 message_loop_.RunUntilIdle(); 112} 113 114TEST_F(FileAPIMessageFilterTest, MultipleFilters) { 115 scoped_refptr<FileAPIMessageFilter> filter1( 116 new FileAPIMessageFilter( 117 0 /* process_id */, 118 browser_context_.GetRequestContext(), 119 file_system_context_.get(), 120 ChromeBlobStorageContext::GetFor(&browser_context_), 121 StreamContext::GetFor(&browser_context_))); 122 scoped_refptr<FileAPIMessageFilter> filter2( 123 new FileAPIMessageFilter( 124 1 /* process_id */, 125 browser_context_.GetRequestContext(), 126 file_system_context_.get(), 127 ChromeBlobStorageContext::GetFor(&browser_context_), 128 StreamContext::GetFor(&browser_context_))); 129 filter1->OnChannelConnected(0); 130 filter2->OnChannelConnected(1); 131 132 // Complete initialization. 133 message_loop_.RunUntilIdle(); 134 135 int request_id = 0; 136 const GURL kUrl("filesystem:http://example.com/temporary/foo"); 137 FileSystemHostMsg_ReadMetadata read_metadata(request_id++, kUrl); 138 EXPECT_TRUE(filter1->OnMessageReceived(read_metadata)); 139 140 // Close the other filter before the request for filter1 is processed. 141 filter2->OnChannelClosing(); 142 143 // This shouldn't cause DCHECK failure. 144 message_loop_.RunUntilIdle(); 145} 146 147TEST_F(FileAPIMessageFilterTest, BuildEmptyStream) { 148 StreamRegistry* stream_registry = stream_context_->registry(); 149 150 const GURL kUrl(kFakeBlobInternalUrlSpec); 151 const GURL kDifferentUrl("blob:barfoo"); 152 153 EXPECT_EQ(NULL, stream_registry->GetStream(kUrl).get()); 154 155 StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType); 156 EXPECT_TRUE(filter_->OnMessageReceived(start_message)); 157 158 const int kBufferSize = 10; 159 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize)); 160 int bytes_read = 0; 161 162 scoped_refptr<Stream> stream = stream_registry->GetStream(kUrl); 163 // Stream becomes available for read right after registration. 164 ASSERT_FALSE(stream.get() == NULL); 165 EXPECT_EQ(Stream::STREAM_EMPTY, 166 stream->ReadRawData(buffer.get(), kBufferSize, &bytes_read)); 167 EXPECT_EQ(0, bytes_read); 168 stream = NULL; 169 170 StreamHostMsg_FinishBuilding finish_message(kUrl); 171 EXPECT_TRUE(filter_->OnMessageReceived(finish_message)); 172 173 stream = stream_registry->GetStream(kUrl); 174 ASSERT_FALSE(stream.get() == NULL); 175 EXPECT_EQ(Stream::STREAM_EMPTY, 176 stream->ReadRawData(buffer.get(), kBufferSize, &bytes_read)); 177 EXPECT_EQ(0, bytes_read); 178 179 // Run loop to finish transfer. 180 message_loop_.RunUntilIdle(); 181 182 EXPECT_EQ(Stream::STREAM_COMPLETE, 183 stream->ReadRawData(buffer.get(), kBufferSize, &bytes_read)); 184 EXPECT_EQ(0, bytes_read); 185 186 // Nothing should be returned for a URL we didn't use. 187 EXPECT_TRUE(stream_registry->GetStream(kDifferentUrl).get() == NULL); 188} 189 190TEST_F(FileAPIMessageFilterTest, BuildNonEmptyStream) { 191 StreamRegistry* stream_registry = stream_context_->registry(); 192 193 const GURL kUrl(kFakeBlobInternalUrlSpec); 194 195 EXPECT_EQ(NULL, stream_registry->GetStream(kUrl).get()); 196 197 StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType); 198 EXPECT_TRUE(filter_->OnMessageReceived(start_message)); 199 200 storage::BlobData::Item item; 201 const std::string kFakeData = "foobarbaz"; 202 item.SetToBytes(kFakeData.data(), kFakeData.size()); 203 StreamHostMsg_AppendBlobDataItem append_message(kUrl, item); 204 EXPECT_TRUE(filter_->OnMessageReceived(append_message)); 205 206 StreamHostMsg_FinishBuilding finish_message(kUrl); 207 EXPECT_TRUE(filter_->OnMessageReceived(finish_message)); 208 209 // Run loop to finish transfer and commit finalize command. 210 message_loop_.RunUntilIdle(); 211 212 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kFakeData.size())); 213 int bytes_read = 0; 214 215 scoped_refptr<Stream> stream = stream_registry->GetStream(kUrl); 216 ASSERT_FALSE(stream.get() == NULL); 217 218 EXPECT_EQ(Stream::STREAM_HAS_DATA, 219 stream->ReadRawData(buffer.get(), kFakeData.size(), &bytes_read)); 220 EXPECT_EQ(kFakeData.size(), static_cast<size_t>(bytes_read)); 221 EXPECT_EQ(kFakeData, std::string(buffer->data(), bytes_read)); 222 223 EXPECT_EQ(Stream::STREAM_COMPLETE, 224 stream->ReadRawData(buffer.get(), kFakeData.size(), &bytes_read)); 225 EXPECT_EQ(0, bytes_read); 226} 227 228TEST_F(FileAPIMessageFilterTest, BuildStreamWithSharedMemory) { 229 StreamRegistry* stream_registry = stream_context_->registry(); 230 231 const GURL kUrl(kFakeBlobInternalUrlSpec); 232 233 EXPECT_EQ(NULL, stream_registry->GetStream(kUrl).get()); 234 235 // For win, we need to set valid PID to the filter. 236 // OnAppendSharedMemoryToStream passes the peer process's handle to 237 // SharedMemory's constructor. If it's incorrect, DuplicateHandle won't work 238 // correctly. 239 filter_->set_peer_pid_for_testing(base::Process::Current().pid()); 240 241 StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType); 242 EXPECT_TRUE(filter_->OnMessageReceived(start_message)); 243 244 const std::string kFakeData = "foobarbaz"; 245 246 scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory); 247 ASSERT_TRUE(shared_memory->CreateAndMapAnonymous(kFakeData.size())); 248 memcpy(shared_memory->memory(), kFakeData.data(), kFakeData.size()); 249 StreamHostMsg_SyncAppendSharedMemory append_message( 250 kUrl, shared_memory->handle(), kFakeData.size()); 251 EXPECT_TRUE(filter_->OnMessageReceived(append_message)); 252 253 StreamHostMsg_FinishBuilding finish_message(kUrl); 254 EXPECT_TRUE(filter_->OnMessageReceived(finish_message)); 255 256 // Run loop to finish transfer and commit finalize command. 257 message_loop_.RunUntilIdle(); 258 259 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kFakeData.size())); 260 int bytes_read = 0; 261 262 scoped_refptr<Stream> stream = stream_registry->GetStream(kUrl); 263 ASSERT_FALSE(stream.get() == NULL); 264 265 EXPECT_EQ(Stream::STREAM_HAS_DATA, 266 stream->ReadRawData(buffer.get(), kFakeData.size(), &bytes_read)); 267 EXPECT_EQ(kFakeData.size(), static_cast<size_t>(bytes_read)); 268 EXPECT_EQ(kFakeData, std::string(buffer->data(), bytes_read)); 269 270 EXPECT_EQ(Stream::STREAM_COMPLETE, 271 stream->ReadRawData(buffer.get(), kFakeData.size(), &bytes_read)); 272 EXPECT_EQ(0, bytes_read); 273} 274 275TEST_F(FileAPIMessageFilterTest, BuildStreamAndCallOnChannelClosing) { 276 StreamRegistry* stream_registry = stream_context_->registry(); 277 278 const GURL kUrl(kFakeBlobInternalUrlSpec); 279 280 StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType); 281 EXPECT_TRUE(filter_->OnMessageReceived(start_message)); 282 283 ASSERT_FALSE(stream_registry->GetStream(kUrl).get() == NULL); 284 285 filter_->OnChannelClosing(); 286 287 ASSERT_EQ(NULL, stream_registry->GetStream(kUrl).get()); 288} 289 290TEST_F(FileAPIMessageFilterTest, CloneStream) { 291 StreamRegistry* stream_registry = stream_context_->registry(); 292 293 const GURL kUrl(kFakeBlobInternalUrlSpec); 294 const GURL kDestUrl(kFakeBlobInternalUrlSpec2); 295 296 StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType); 297 EXPECT_TRUE(filter_->OnMessageReceived(start_message)); 298 299 StreamHostMsg_Clone clone_message(kDestUrl, kUrl); 300 EXPECT_TRUE(filter_->OnMessageReceived(clone_message)); 301 302 ASSERT_FALSE(stream_registry->GetStream(kUrl).get() == NULL); 303 ASSERT_FALSE(stream_registry->GetStream(kDestUrl).get() == NULL); 304} 305 306} // namespace content 307