Home | History | Annotate | Download | only in fileapi
      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