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