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 "net/base/io_buffer.h" 25 #include "testing/gtest/include/gtest/gtest.h" 26 #include "webkit/browser/blob/blob_storage_controller.h" 27 #include "webkit/browser/fileapi/file_system_context.h" 28 #include "webkit/browser/fileapi/mock_file_system_context.h" 29 #include "webkit/common/blob/blob_data.h" 30 31 namespace content { 32 33 namespace { 34 35 const char kFakeBlobInternalUrlSpec[] = 36 "blob:blobinternal%3A///dc83ede4-9bbd-453b-be2e-60fd623fcc93"; 37 const char kFakeBlobInternalUrlSpec2[] = 38 "blob:blobinternal%3A///d28ae2e7-d233-4dda-9598-d135fe5d403e"; 39 40 const char kFakeContentType[] = "fake/type"; 41 42 } // namespace 43 44 class 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 fileapi::CreateFileSystemContextForTesting(NULL, base::FilePath()); 54 55 std::vector<fileapi::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 fileapi::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 // Tests via OnMessageReceived(const IPC::Message&). The channel proxy calls 79 // this method. Since OnMessageReceived is hidden on FileAPIMessageFilter, 80 // we need to cast it. 81 bool InvokeOnMessageReceived(const IPC::Message& message) { 82 IPC::ChannelProxy::MessageFilter* casted_filter = 83 static_cast<IPC::ChannelProxy::MessageFilter*>(filter_.get()); 84 return casted_filter->OnMessageReceived(message); 85 } 86 87 base::MessageLoop message_loop_; 88 TestBrowserThread io_browser_thread_; 89 90 TestBrowserContext browser_context_; 91 scoped_refptr<fileapi::FileSystemContext> file_system_context_; 92 StreamContext* stream_context_; 93 ChromeBlobStorageContext* blob_storage_context_; 94 95 scoped_refptr<FileAPIMessageFilter> filter_; 96 }; 97 98 TEST_F(FileAPIMessageFilterTest, BuildEmptyBlob) { 99 webkit_blob::BlobStorageController* controller = 100 blob_storage_context_->controller(); 101 102 const GURL kUrl("blob:foobar"); 103 const GURL kDifferentUrl("blob:barfoo"); 104 105 EXPECT_EQ(NULL, controller->GetBlobDataFromUrl(kUrl)); 106 107 BlobHostMsg_StartBuilding start_message(kUrl); 108 EXPECT_TRUE(InvokeOnMessageReceived(start_message)); 109 110 // Blob is still being built. Nothing should be returned. 111 EXPECT_EQ(NULL, controller->GetBlobDataFromUrl(kUrl)); 112 113 BlobHostMsg_FinishBuilding finish_message(kUrl, kFakeContentType); 114 EXPECT_TRUE(InvokeOnMessageReceived(finish_message)); 115 116 // Now, Blob is built. 117 webkit_blob::BlobData* blob_data = controller->GetBlobDataFromUrl(kUrl); 118 ASSERT_FALSE(blob_data == NULL); 119 EXPECT_EQ(0U, blob_data->items().size()); 120 EXPECT_EQ(kFakeContentType, blob_data->content_type()); 121 122 // Nothing should be returned for a URL we didn't use. 123 EXPECT_TRUE(controller->GetBlobDataFromUrl(kDifferentUrl) == NULL); 124 } 125 126 TEST_F(FileAPIMessageFilterTest, CloseChannelWithInflightRequest) { 127 scoped_refptr<FileAPIMessageFilter> filter( 128 new FileAPIMessageFilter( 129 0 /* process_id */, 130 browser_context_.GetRequestContext(), 131 file_system_context_.get(), 132 ChromeBlobStorageContext::GetFor(&browser_context_), 133 StreamContext::GetFor(&browser_context_))); 134 filter->OnChannelConnected(0); 135 136 // Complete initialization. 137 message_loop_.RunUntilIdle(); 138 139 IPC::ChannelProxy::MessageFilter* casted_filter = 140 static_cast<IPC::ChannelProxy::MessageFilter*>(filter.get()); 141 142 int request_id = 0; 143 const GURL kUrl("filesystem:http://example.com/temporary/foo"); 144 FileSystemHostMsg_ReadMetadata read_metadata(request_id++, kUrl); 145 EXPECT_TRUE(casted_filter->OnMessageReceived(read_metadata)); 146 147 // Close the filter while it has inflight request. 148 filter->OnChannelClosing(); 149 150 // This shouldn't cause DCHECK failure. 151 message_loop_.RunUntilIdle(); 152 } 153 154 TEST_F(FileAPIMessageFilterTest, MultipleFilters) { 155 scoped_refptr<FileAPIMessageFilter> filter1( 156 new FileAPIMessageFilter( 157 0 /* process_id */, 158 browser_context_.GetRequestContext(), 159 file_system_context_.get(), 160 ChromeBlobStorageContext::GetFor(&browser_context_), 161 StreamContext::GetFor(&browser_context_))); 162 scoped_refptr<FileAPIMessageFilter> filter2( 163 new FileAPIMessageFilter( 164 1 /* process_id */, 165 browser_context_.GetRequestContext(), 166 file_system_context_.get(), 167 ChromeBlobStorageContext::GetFor(&browser_context_), 168 StreamContext::GetFor(&browser_context_))); 169 filter1->OnChannelConnected(0); 170 filter2->OnChannelConnected(1); 171 172 // Complete initialization. 173 message_loop_.RunUntilIdle(); 174 175 IPC::ChannelProxy::MessageFilter* casted_filter = 176 static_cast<IPC::ChannelProxy::MessageFilter*>(filter1.get()); 177 178 int request_id = 0; 179 const GURL kUrl("filesystem:http://example.com/temporary/foo"); 180 FileSystemHostMsg_ReadMetadata read_metadata(request_id++, kUrl); 181 EXPECT_TRUE(casted_filter->OnMessageReceived(read_metadata)); 182 183 // Close the other filter before the request for filter1 is processed. 184 filter2->OnChannelClosing(); 185 186 // This shouldn't cause DCHECK failure. 187 message_loop_.RunUntilIdle(); 188 } 189 190 TEST_F(FileAPIMessageFilterTest, BuildEmptyStream) { 191 StreamRegistry* stream_registry = stream_context_->registry(); 192 193 webkit_blob::BlobStorageController* blob_controller = 194 blob_storage_context_->controller(); 195 196 const GURL kUrl(kFakeBlobInternalUrlSpec); 197 const GURL kDifferentUrl("blob:barfoo"); 198 199 EXPECT_EQ(NULL, stream_registry->GetStream(kUrl).get()); 200 EXPECT_EQ(NULL, blob_controller->GetBlobDataFromUrl(kUrl)); 201 202 StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType); 203 EXPECT_TRUE(InvokeOnMessageReceived(start_message)); 204 205 const int kBufferSize = 10; 206 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize)); 207 int bytes_read = 0; 208 209 scoped_refptr<Stream> stream = stream_registry->GetStream(kUrl); 210 // Stream becomes available for read right after registration. 211 ASSERT_FALSE(stream.get() == NULL); 212 EXPECT_EQ(Stream::STREAM_EMPTY, 213 stream->ReadRawData(buffer.get(), kBufferSize, &bytes_read)); 214 EXPECT_EQ(0, bytes_read); 215 stream = NULL; 216 217 StreamHostMsg_FinishBuilding finish_message(kUrl); 218 EXPECT_TRUE(InvokeOnMessageReceived(finish_message)); 219 220 // Blob controller shouldn't be affected. 221 EXPECT_EQ(NULL, blob_controller->GetBlobDataFromUrl(kUrl)); 222 223 stream = stream_registry->GetStream(kUrl); 224 ASSERT_FALSE(stream.get() == NULL); 225 EXPECT_EQ(Stream::STREAM_EMPTY, 226 stream->ReadRawData(buffer.get(), kBufferSize, &bytes_read)); 227 EXPECT_EQ(0, bytes_read); 228 229 // Run loop to finish transfer. 230 message_loop_.RunUntilIdle(); 231 232 EXPECT_EQ(Stream::STREAM_COMPLETE, 233 stream->ReadRawData(buffer.get(), kBufferSize, &bytes_read)); 234 EXPECT_EQ(0, bytes_read); 235 236 // Nothing should be returned for a URL we didn't use. 237 EXPECT_TRUE(stream_registry->GetStream(kDifferentUrl).get() == NULL); 238 } 239 240 TEST_F(FileAPIMessageFilterTest, BuildNonEmptyStream) { 241 StreamRegistry* stream_registry = stream_context_->registry(); 242 243 const GURL kUrl(kFakeBlobInternalUrlSpec); 244 245 EXPECT_EQ(NULL, stream_registry->GetStream(kUrl).get()); 246 247 StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType); 248 EXPECT_TRUE(InvokeOnMessageReceived(start_message)); 249 250 webkit_blob::BlobData::Item item; 251 const std::string kFakeData = "foobarbaz"; 252 item.SetToBytes(kFakeData.data(), kFakeData.size()); 253 StreamHostMsg_AppendBlobDataItem append_message(kUrl, item); 254 EXPECT_TRUE(InvokeOnMessageReceived(append_message)); 255 256 StreamHostMsg_FinishBuilding finish_message(kUrl); 257 EXPECT_TRUE(InvokeOnMessageReceived(finish_message)); 258 259 // Run loop to finish transfer and commit finalize command. 260 message_loop_.RunUntilIdle(); 261 262 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kFakeData.size())); 263 int bytes_read = 0; 264 265 scoped_refptr<Stream> stream = stream_registry->GetStream(kUrl); 266 ASSERT_FALSE(stream.get() == NULL); 267 268 EXPECT_EQ(Stream::STREAM_HAS_DATA, 269 stream->ReadRawData(buffer.get(), kFakeData.size(), &bytes_read)); 270 EXPECT_EQ(kFakeData.size(), static_cast<size_t>(bytes_read)); 271 EXPECT_EQ(kFakeData, std::string(buffer->data(), bytes_read)); 272 273 EXPECT_EQ(Stream::STREAM_COMPLETE, 274 stream->ReadRawData(buffer.get(), kFakeData.size(), &bytes_read)); 275 EXPECT_EQ(0, bytes_read); 276 } 277 278 TEST_F(FileAPIMessageFilterTest, BuildStreamWithSharedMemory) { 279 StreamRegistry* stream_registry = stream_context_->registry(); 280 281 const GURL kUrl(kFakeBlobInternalUrlSpec); 282 283 EXPECT_EQ(NULL, stream_registry->GetStream(kUrl).get()); 284 285 // For win, we need to set valid PID to the filter. 286 // OnAppendSharedMemoryToStream passes the peer process's handle to 287 // SharedMemory's constructor. If it's incorrect, DuplicateHandle won't work 288 // correctly. 289 static_cast<IPC::ChannelProxy::MessageFilter*>( 290 filter_.get())->OnChannelConnected(base::Process::Current().pid()); 291 292 StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType); 293 EXPECT_TRUE(InvokeOnMessageReceived(start_message)); 294 295 const std::string kFakeData = "foobarbaz"; 296 297 scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory); 298 ASSERT_TRUE(shared_memory->CreateAndMapAnonymous(kFakeData.size())); 299 memcpy(shared_memory->memory(), kFakeData.data(), kFakeData.size()); 300 StreamHostMsg_SyncAppendSharedMemory append_message( 301 kUrl, shared_memory->handle(), kFakeData.size()); 302 EXPECT_TRUE(InvokeOnMessageReceived(append_message)); 303 304 StreamHostMsg_FinishBuilding finish_message(kUrl); 305 EXPECT_TRUE(InvokeOnMessageReceived(finish_message)); 306 307 // Run loop to finish transfer and commit finalize command. 308 message_loop_.RunUntilIdle(); 309 310 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kFakeData.size())); 311 int bytes_read = 0; 312 313 scoped_refptr<Stream> stream = stream_registry->GetStream(kUrl); 314 ASSERT_FALSE(stream.get() == NULL); 315 316 EXPECT_EQ(Stream::STREAM_HAS_DATA, 317 stream->ReadRawData(buffer.get(), kFakeData.size(), &bytes_read)); 318 EXPECT_EQ(kFakeData.size(), static_cast<size_t>(bytes_read)); 319 EXPECT_EQ(kFakeData, std::string(buffer->data(), bytes_read)); 320 321 EXPECT_EQ(Stream::STREAM_COMPLETE, 322 stream->ReadRawData(buffer.get(), kFakeData.size(), &bytes_read)); 323 EXPECT_EQ(0, bytes_read); 324 } 325 326 TEST_F(FileAPIMessageFilterTest, BuildStreamAndCallOnChannelClosing) { 327 StreamRegistry* stream_registry = stream_context_->registry(); 328 329 const GURL kUrl(kFakeBlobInternalUrlSpec); 330 331 StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType); 332 EXPECT_TRUE(InvokeOnMessageReceived(start_message)); 333 334 ASSERT_FALSE(stream_registry->GetStream(kUrl).get() == NULL); 335 336 filter_->OnChannelClosing(); 337 338 ASSERT_EQ(NULL, stream_registry->GetStream(kUrl).get()); 339 } 340 341 TEST_F(FileAPIMessageFilterTest, CloneStream) { 342 StreamRegistry* stream_registry = stream_context_->registry(); 343 344 const GURL kUrl(kFakeBlobInternalUrlSpec); 345 const GURL kDestUrl(kFakeBlobInternalUrlSpec2); 346 347 StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType); 348 EXPECT_TRUE(InvokeOnMessageReceived(start_message)); 349 350 StreamHostMsg_Clone clone_message(kDestUrl, kUrl); 351 EXPECT_TRUE(InvokeOnMessageReceived(clone_message)); 352 353 ASSERT_FALSE(stream_registry->GetStream(kUrl).get() == NULL); 354 ASSERT_FALSE(stream_registry->GetStream(kDestUrl).get() == NULL); 355 } 356 357 } // namespace content 358