1 // Copyright 2019 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/native_file_system/native_file_system_manager_impl.h"
6 
7 #include <string>
8 #include <utility>
9 #include <vector>
10 
11 #include "base/bind_helpers.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/run_loop.h"
15 #include "base/test/bind_test_util.h"
16 #include "base/test/gmock_callback_support.h"
17 #include "base/test/scoped_feature_list.h"
18 #include "base/test/task_environment.h"
19 #include "content/browser/native_file_system/fixed_native_file_system_permission_grant.h"
20 #include "content/browser/native_file_system/mock_native_file_system_permission_context.h"
21 #include "content/browser/native_file_system/native_file_system_directory_handle_impl.h"
22 #include "content/browser/native_file_system/native_file_system_file_handle_impl.h"
23 #include "content/browser/native_file_system/native_file_system_transfer_token_impl.h"
24 #include "content/public/test/browser_task_environment.h"
25 #include "mojo/public/cpp/bindings/pending_remote.h"
26 #include "mojo/public/cpp/bindings/remote.h"
27 #include "storage/browser/blob/blob_storage_context.h"
28 #include "storage/browser/test/async_file_test_helper.h"
29 #include "storage/browser/test/test_file_system_context.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "third_party/blink/public/common/features.h"
32 
33 namespace content {
34 
35 using base::test::RunOnceCallback;
36 using blink::mojom::PermissionStatus;
37 
38 class NativeFileSystemManagerImplTest : public testing::Test {
39  public:
NativeFileSystemManagerImplTest()40   NativeFileSystemManagerImplTest()
41       : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {
42     scoped_feature_list_.InitAndEnableFeature(
43         blink::features::kNativeFileSystemAPI);
44   }
45 
SetUp()46   void SetUp() override {
47     ASSERT_TRUE(dir_.CreateUniqueTempDir());
48     ASSERT_TRUE(dir_.GetPath().IsAbsolute());
49     file_system_context_ = storage::CreateFileSystemContextForTesting(
50         /*quota_manager_proxy=*/nullptr, dir_.GetPath());
51 
52     chrome_blob_context_ = base::MakeRefCounted<ChromeBlobStorageContext>();
53     chrome_blob_context_->InitializeOnIOThread(base::FilePath(),
54                                                base::FilePath(), nullptr);
55 
56     manager_ = base::MakeRefCounted<NativeFileSystemManagerImpl>(
57         file_system_context_, chrome_blob_context_, &permission_context_,
58         /*off_the_record=*/false);
59 
60     manager_->BindReceiver(kBindingContext,
61                            manager_remote_.BindNewPipeAndPassReceiver());
62   }
63 
64   template <typename HandleType>
GetPermissionStatusSync(bool writable,HandleType * handle)65   PermissionStatus GetPermissionStatusSync(bool writable, HandleType* handle) {
66     PermissionStatus result;
67     base::RunLoop loop;
68     handle->GetPermissionStatus(
69         writable, base::BindLambdaForTesting([&](PermissionStatus status) {
70           result = status;
71           loop.Quit();
72         }));
73     loop.Run();
74     return result;
75   }
76 
77   mojo::Remote<blink::mojom::NativeFileSystemDirectoryHandle>
GetHandleForDirectory(const base::FilePath & path)78   GetHandleForDirectory(const base::FilePath& path) {
79     EXPECT_CALL(
80         permission_context_,
81         GetReadPermissionGrant(
82             kTestOrigin, path, /*is_directory=*/true, kProcessId, kFrameId,
83             NativeFileSystemPermissionContext::UserAction::kOpen))
84         .WillOnce(testing::Return(allow_grant_));
85     EXPECT_CALL(
86         permission_context_,
87         GetWritePermissionGrant(
88             kTestOrigin, path, /*is_directory=*/true, kProcessId, kFrameId,
89             NativeFileSystemPermissionContext::UserAction::kOpen))
90         .WillOnce(testing::Return(allow_grant_));
91 
92     blink::mojom::NativeFileSystemEntryPtr entry =
93         manager_->CreateDirectoryEntryFromPath(kBindingContext, path);
94     return mojo::Remote<blink::mojom::NativeFileSystemDirectoryHandle>(
95         std::move(entry->entry_handle->get_directory()));
96   }
97 
SerializeAndDeserializeToken(mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token_remote)98   NativeFileSystemTransferTokenImpl* SerializeAndDeserializeToken(
99       mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken>
100           token_remote) {
101     std::vector<uint8_t> serialized;
102     base::RunLoop serialize_loop;
103     manager_->SerializeHandle(
104         std::move(token_remote),
105         base::BindLambdaForTesting([&](const std::vector<uint8_t>& bits) {
106           EXPECT_FALSE(bits.empty());
107           serialized = bits;
108           serialize_loop.Quit();
109         }));
110     serialize_loop.Run();
111 
112     manager_->DeserializeHandle(kTestOrigin, serialized,
113                                 token_remote.InitWithNewPipeAndPassReceiver());
114     base::RunLoop resolve_loop;
115     NativeFileSystemTransferTokenImpl* result;
116     manager_->ResolveTransferToken(
117         std::move(token_remote),
118         base::BindLambdaForTesting(
119             [&](NativeFileSystemTransferTokenImpl* token) {
120               result = token;
121               resolve_loop.Quit();
122             }));
123     resolve_loop.Run();
124     return result;
125   }
126 
127  protected:
128   const GURL kTestURL = GURL("https://example.com/test");
129   const url::Origin kTestOrigin = url::Origin::Create(kTestURL);
130   const int kProcessId = 1;
131   const int kFrameId = 2;
132   const NativeFileSystemManagerImpl::BindingContext kBindingContext = {
133       kTestOrigin, kTestURL, kProcessId, kFrameId};
134 
135   base::test::ScopedFeatureList scoped_feature_list_;
136   BrowserTaskEnvironment task_environment_;
137 
138   base::ScopedTempDir dir_;
139   scoped_refptr<storage::FileSystemContext> file_system_context_;
140   scoped_refptr<ChromeBlobStorageContext> chrome_blob_context_;
141 
142   testing::StrictMock<MockNativeFileSystemPermissionContext>
143       permission_context_;
144   scoped_refptr<NativeFileSystemManagerImpl> manager_;
145   mojo::Remote<blink::mojom::NativeFileSystemManager> manager_remote_;
146 
147   scoped_refptr<FixedNativeFileSystemPermissionGrant> ask_grant_ =
148       base::MakeRefCounted<FixedNativeFileSystemPermissionGrant>(
149           FixedNativeFileSystemPermissionGrant::PermissionStatus::ASK);
150   scoped_refptr<FixedNativeFileSystemPermissionGrant> ask_grant2_ =
151       base::MakeRefCounted<FixedNativeFileSystemPermissionGrant>(
152           FixedNativeFileSystemPermissionGrant::PermissionStatus::ASK);
153   scoped_refptr<FixedNativeFileSystemPermissionGrant> allow_grant_ =
154       base::MakeRefCounted<FixedNativeFileSystemPermissionGrant>(
155           FixedNativeFileSystemPermissionGrant::PermissionStatus::GRANTED);
156 };
157 
TEST_F(NativeFileSystemManagerImplTest,GetSandboxedFileSystem_Permissions)158 TEST_F(NativeFileSystemManagerImplTest, GetSandboxedFileSystem_Permissions) {
159   mojo::PendingRemote<blink::mojom::NativeFileSystemDirectoryHandle>
160       directory_remote;
161   base::RunLoop loop;
162   manager_remote_->GetSandboxedFileSystem(base::BindLambdaForTesting(
163       [&](blink::mojom::NativeFileSystemErrorPtr result,
164           mojo::PendingRemote<blink::mojom::NativeFileSystemDirectoryHandle>
165               handle) {
166         EXPECT_EQ(blink::mojom::NativeFileSystemStatus::kOk, result->status);
167         directory_remote = std::move(handle);
168         loop.Quit();
169       }));
170   loop.Run();
171   mojo::Remote<blink::mojom::NativeFileSystemDirectoryHandle> root(
172       std::move(directory_remote));
173   ASSERT_TRUE(root);
174   EXPECT_EQ(PermissionStatus::GRANTED,
175             GetPermissionStatusSync(/*writable=*/false, root.get()));
176   EXPECT_EQ(PermissionStatus::GRANTED,
177             GetPermissionStatusSync(/*writable=*/true, root.get()));
178 }
179 
TEST_F(NativeFileSystemManagerImplTest,CreateFileEntryFromPath_Permissions)180 TEST_F(NativeFileSystemManagerImplTest, CreateFileEntryFromPath_Permissions) {
181   const base::FilePath kTestPath(dir_.GetPath().AppendASCII("foo"));
182 
183   EXPECT_CALL(
184       permission_context_,
185       GetReadPermissionGrant(
186           kTestOrigin, kTestPath, /*is_directory=*/false, kProcessId, kFrameId,
187           NativeFileSystemPermissionContext::UserAction::kOpen))
188       .WillOnce(testing::Return(allow_grant_));
189   EXPECT_CALL(
190       permission_context_,
191       GetWritePermissionGrant(
192           kTestOrigin, kTestPath, /*is_directory=*/false, kProcessId, kFrameId,
193           NativeFileSystemPermissionContext::UserAction::kOpen))
194       .WillOnce(testing::Return(ask_grant_));
195 
196   blink::mojom::NativeFileSystemEntryPtr entry =
197       manager_->CreateFileEntryFromPath(kBindingContext, kTestPath);
198   mojo::Remote<blink::mojom::NativeFileSystemFileHandle> handle(
199       std::move(entry->entry_handle->get_file()));
200 
201   EXPECT_EQ(PermissionStatus::GRANTED,
202             GetPermissionStatusSync(/*writable=*/false, handle.get()));
203   EXPECT_EQ(PermissionStatus::ASK,
204             GetPermissionStatusSync(/*writable=*/true, handle.get()));
205 }
206 
TEST_F(NativeFileSystemManagerImplTest,CreateWritableFileEntryFromPath_Permissions)207 TEST_F(NativeFileSystemManagerImplTest,
208        CreateWritableFileEntryFromPath_Permissions) {
209   const base::FilePath kTestPath(dir_.GetPath().AppendASCII("foo"));
210 
211   EXPECT_CALL(
212       permission_context_,
213       GetReadPermissionGrant(
214           kTestOrigin, kTestPath, /*is_directory=*/false, kProcessId, kFrameId,
215           NativeFileSystemPermissionContext::UserAction::kSave))
216       .WillOnce(testing::Return(allow_grant_));
217   EXPECT_CALL(
218       permission_context_,
219       GetWritePermissionGrant(
220           kTestOrigin, kTestPath, /*is_directory=*/false, kProcessId, kFrameId,
221           NativeFileSystemPermissionContext::UserAction::kSave))
222       .WillOnce(testing::Return(allow_grant_));
223 
224   blink::mojom::NativeFileSystemEntryPtr entry =
225       manager_->CreateWritableFileEntryFromPath(kBindingContext, kTestPath);
226   mojo::Remote<blink::mojom::NativeFileSystemFileHandle> handle(
227       std::move(entry->entry_handle->get_file()));
228 
229   EXPECT_EQ(PermissionStatus::GRANTED,
230             GetPermissionStatusSync(/*writable=*/false, handle.get()));
231   EXPECT_EQ(PermissionStatus::GRANTED,
232             GetPermissionStatusSync(/*writable=*/true, handle.get()));
233 }
234 
TEST_F(NativeFileSystemManagerImplTest,CreateDirectoryEntryFromPath_Permissions)235 TEST_F(NativeFileSystemManagerImplTest,
236        CreateDirectoryEntryFromPath_Permissions) {
237   const base::FilePath kTestPath(dir_.GetPath().AppendASCII("foo"));
238 
239   EXPECT_CALL(
240       permission_context_,
241       GetReadPermissionGrant(
242           kTestOrigin, kTestPath, /*is_directory=*/true, kProcessId, kFrameId,
243           NativeFileSystemPermissionContext::UserAction::kOpen))
244       .WillOnce(testing::Return(allow_grant_));
245   EXPECT_CALL(
246       permission_context_,
247       GetWritePermissionGrant(
248           kTestOrigin, kTestPath, /*is_directory=*/true, kProcessId, kFrameId,
249           NativeFileSystemPermissionContext::UserAction::kOpen))
250       .WillOnce(testing::Return(ask_grant_));
251 
252   blink::mojom::NativeFileSystemEntryPtr entry =
253       manager_->CreateDirectoryEntryFromPath(kBindingContext, kTestPath);
254   mojo::Remote<blink::mojom::NativeFileSystemDirectoryHandle> handle(
255       std::move(entry->entry_handle->get_directory()));
256   EXPECT_EQ(PermissionStatus::GRANTED,
257             GetPermissionStatusSync(/*writable=*/false, handle.get()));
258   EXPECT_EQ(PermissionStatus::ASK,
259             GetPermissionStatusSync(/*writable=*/true, handle.get()));
260 }
261 
TEST_F(NativeFileSystemManagerImplTest,FileWriterSwapDeletedOnConnectionClose)262 TEST_F(NativeFileSystemManagerImplTest,
263        FileWriterSwapDeletedOnConnectionClose) {
264   auto test_file_url = file_system_context_->CreateCrackedFileSystemURL(
265       kTestOrigin, storage::kFileSystemTypeTest,
266       base::FilePath::FromUTF8Unsafe("test"));
267 
268   auto test_swap_url = file_system_context_->CreateCrackedFileSystemURL(
269       kTestOrigin, storage::kFileSystemTypeTest,
270       base::FilePath::FromUTF8Unsafe("test.crswap"));
271 
272   ASSERT_EQ(base::File::FILE_OK,
273             storage::AsyncFileTestHelper::CreateFile(file_system_context_.get(),
274                                                      test_file_url));
275 
276   ASSERT_EQ(base::File::FILE_OK,
277             storage::AsyncFileTestHelper::CreateFile(file_system_context_.get(),
278                                                      test_swap_url));
279 
280   mojo::Remote<blink::mojom::NativeFileSystemFileWriter> writer_remote(
281       manager_->CreateFileWriter(kBindingContext, test_file_url, test_swap_url,
282                                  NativeFileSystemManagerImpl::SharedHandleState(
283                                      allow_grant_, allow_grant_, {})));
284 
285   ASSERT_TRUE(writer_remote.is_bound());
286   ASSERT_TRUE(storage::AsyncFileTestHelper::FileExists(
287       file_system_context_.get(), test_swap_url,
288       storage::AsyncFileTestHelper::kDontCheckSize));
289 
290   // Severs the mojo pipe, causing the writer to be destroyed.
291   writer_remote.reset();
292   base::RunLoop().RunUntilIdle();
293 
294   ASSERT_FALSE(storage::AsyncFileTestHelper::FileExists(
295       file_system_context_.get(), test_swap_url,
296       storage::AsyncFileTestHelper::kDontCheckSize));
297 }
298 
TEST_F(NativeFileSystemManagerImplTest,FileWriterCloseAllowedToCompleteOnDestruct)299 TEST_F(NativeFileSystemManagerImplTest,
300        FileWriterCloseAllowedToCompleteOnDestruct) {
301   auto test_file_url = file_system_context_->CreateCrackedFileSystemURL(
302       kTestOrigin, storage::kFileSystemTypeTest,
303       base::FilePath::FromUTF8Unsafe("test"));
304 
305   auto test_swap_url = file_system_context_->CreateCrackedFileSystemURL(
306       kTestOrigin, storage::kFileSystemTypeTest,
307       base::FilePath::FromUTF8Unsafe("test.crswap"));
308 
309   ASSERT_EQ(base::File::FILE_OK,
310             storage::AsyncFileTestHelper::CreateFileWithData(
311                 file_system_context_.get(), test_swap_url, "foo", 3));
312 
313   mojo::Remote<blink::mojom::NativeFileSystemFileWriter> writer_remote(
314       manager_->CreateFileWriter(kBindingContext, test_file_url, test_swap_url,
315                                  NativeFileSystemManagerImpl::SharedHandleState(
316                                      allow_grant_, allow_grant_, {})));
317 
318   ASSERT_TRUE(writer_remote.is_bound());
319   ASSERT_FALSE(storage::AsyncFileTestHelper::FileExists(
320       file_system_context_.get(), test_file_url,
321       storage::AsyncFileTestHelper::kDontCheckSize));
322   writer_remote->Close(base::DoNothing());
323 
324   // Severs the mojo pipe, causing the writer to be destroyed.
325   writer_remote.reset();
326   base::RunLoop().RunUntilIdle();
327 
328   ASSERT_FALSE(storage::AsyncFileTestHelper::FileExists(
329       file_system_context_.get(), test_swap_url,
330       storage::AsyncFileTestHelper::kDontCheckSize));
331   ASSERT_TRUE(storage::AsyncFileTestHelper::FileExists(
332       file_system_context_.get(), test_file_url, 3));
333 }
334 
TEST_F(NativeFileSystemManagerImplTest,SerializeHandle_SandboxedFile)335 TEST_F(NativeFileSystemManagerImplTest, SerializeHandle_SandboxedFile) {
336   auto test_file_url = file_system_context_->CreateCrackedFileSystemURL(
337       kTestOrigin, storage::kFileSystemTypeTemporary,
338       base::FilePath::FromUTF8Unsafe("test/foo/bar"));
339   NativeFileSystemFileHandleImpl file(manager_.get(), kBindingContext,
340                                       test_file_url,
341                                       {ask_grant_, ask_grant_, {}});
342   mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token_remote;
343   manager_->CreateTransferToken(file,
344                                 token_remote.InitWithNewPipeAndPassReceiver());
345 
346   NativeFileSystemTransferTokenImpl* token =
347       SerializeAndDeserializeToken(std::move(token_remote));
348   ASSERT_TRUE(token);
349   EXPECT_EQ(test_file_url, token->url());
350   EXPECT_EQ(NativeFileSystemTransferTokenImpl::HandleType::kFile,
351             token->type());
352 
353   // Deserialized sandboxed filesystem handles should always be readable and
354   // writable.
355   EXPECT_EQ(PermissionStatus::GRANTED,
356             token->shared_handle_state().read_grant->GetStatus());
357   EXPECT_EQ(PermissionStatus::GRANTED,
358             token->shared_handle_state().write_grant->GetStatus());
359 }
360 
TEST_F(NativeFileSystemManagerImplTest,SerializeHandle_SandboxedDirectory)361 TEST_F(NativeFileSystemManagerImplTest, SerializeHandle_SandboxedDirectory) {
362   auto test_file_url = file_system_context_->CreateCrackedFileSystemURL(
363       kTestOrigin, storage::kFileSystemTypeTemporary,
364       base::FilePath::FromUTF8Unsafe("hello/world/"));
365   NativeFileSystemDirectoryHandleImpl directory(manager_.get(), kBindingContext,
366                                                 test_file_url,
367                                                 {ask_grant_, ask_grant_, {}});
368   mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token_remote;
369   manager_->CreateTransferToken(directory,
370                                 token_remote.InitWithNewPipeAndPassReceiver());
371 
372   NativeFileSystemTransferTokenImpl* token =
373       SerializeAndDeserializeToken(std::move(token_remote));
374   ASSERT_TRUE(token);
375   EXPECT_EQ(test_file_url, token->url());
376   EXPECT_EQ(NativeFileSystemTransferTokenImpl::HandleType::kDirectory,
377             token->type());
378 
379   // Deserialized sandboxed filesystem handles should always be readable and
380   // writable.
381   EXPECT_EQ(PermissionStatus::GRANTED,
382             token->shared_handle_state().read_grant->GetStatus());
383   EXPECT_EQ(PermissionStatus::GRANTED,
384             token->shared_handle_state().write_grant->GetStatus());
385 }
386 
TEST_F(NativeFileSystemManagerImplTest,SerializeHandle_Native_SingleFile)387 TEST_F(NativeFileSystemManagerImplTest, SerializeHandle_Native_SingleFile) {
388   const base::FilePath kTestPath(dir_.GetPath().AppendASCII("foo"));
389 
390   // Expect calls to get grants when creating the initial handle.
391   EXPECT_CALL(
392       permission_context_,
393       GetReadPermissionGrant(
394           kTestOrigin, kTestPath, /*is_directory=*/false, kProcessId, kFrameId,
395           NativeFileSystemPermissionContext::UserAction::kOpen))
396       .WillOnce(testing::Return(allow_grant_));
397   EXPECT_CALL(
398       permission_context_,
399       GetWritePermissionGrant(
400           kTestOrigin, kTestPath, /*is_directory=*/false, kProcessId, kFrameId,
401           NativeFileSystemPermissionContext::UserAction::kOpen))
402       .WillOnce(testing::Return(allow_grant_));
403 
404   blink::mojom::NativeFileSystemEntryPtr entry =
405       manager_->CreateFileEntryFromPath(kBindingContext, kTestPath);
406   mojo::Remote<blink::mojom::NativeFileSystemFileHandle> handle(
407       std::move(entry->entry_handle->get_file()));
408 
409   mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token_remote;
410   handle->Transfer(token_remote.InitWithNewPipeAndPassReceiver());
411 
412   // Deserializing tokens should re-request grants, with correct user action.
413   EXPECT_CALL(
414       permission_context_,
415       GetReadPermissionGrant(
416           kTestOrigin, kTestPath, /*is_directory=*/false,
417           /*process_id=*/ChildProcessHost::kInvalidUniqueID,
418           /*frame_id=*/MSG_ROUTING_NONE,
419           NativeFileSystemPermissionContext::UserAction::kLoadFromStorage))
420       .WillOnce(testing::Return(ask_grant_));
421   EXPECT_CALL(
422       permission_context_,
423       GetWritePermissionGrant(
424           kTestOrigin, kTestPath, /*is_directory=*/false,
425           /*process_id=*/ChildProcessHost::kInvalidUniqueID,
426           /*frame_id=*/MSG_ROUTING_NONE,
427           NativeFileSystemPermissionContext::UserAction::kLoadFromStorage))
428       .WillOnce(testing::Return(ask_grant2_));
429 
430   NativeFileSystemTransferTokenImpl* token =
431       SerializeAndDeserializeToken(std::move(token_remote));
432   ASSERT_TRUE(token);
433   EXPECT_EQ(kTestOrigin, token->url().origin());
434   EXPECT_EQ(kTestPath, token->url().path());
435   EXPECT_EQ(storage::kFileSystemTypeNativeLocal, token->url().type());
436   EXPECT_EQ(storage::kFileSystemTypeIsolated, token->url().mount_type());
437   EXPECT_EQ(NativeFileSystemTransferTokenImpl::HandleType::kFile,
438             token->type());
439   EXPECT_EQ(ask_grant_, token->shared_handle_state().read_grant);
440   EXPECT_EQ(ask_grant2_, token->shared_handle_state().write_grant);
441 }
442 
TEST_F(NativeFileSystemManagerImplTest,SerializeHandle_Native_SingleDirectory)443 TEST_F(NativeFileSystemManagerImplTest,
444        SerializeHandle_Native_SingleDirectory) {
445   const base::FilePath kTestPath(dir_.GetPath().AppendASCII("foobar"));
446   mojo::Remote<blink::mojom::NativeFileSystemDirectoryHandle> handle =
447       GetHandleForDirectory(kTestPath);
448 
449   mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token_remote;
450   handle->Transfer(token_remote.InitWithNewPipeAndPassReceiver());
451 
452   // Deserializing tokens should re-request grants, with correct user action.
453   EXPECT_CALL(
454       permission_context_,
455       GetReadPermissionGrant(
456           kTestOrigin, kTestPath, /*is_directory=*/true,
457           /*process_id=*/ChildProcessHost::kInvalidUniqueID,
458           /*frame_id=*/MSG_ROUTING_NONE,
459           NativeFileSystemPermissionContext::UserAction::kLoadFromStorage))
460       .WillOnce(testing::Return(ask_grant_));
461   EXPECT_CALL(
462       permission_context_,
463       GetWritePermissionGrant(
464           kTestOrigin, kTestPath, /*is_directory=*/true,
465           /*process_id=*/ChildProcessHost::kInvalidUniqueID,
466           /*frame_id=*/MSG_ROUTING_NONE,
467           NativeFileSystemPermissionContext::UserAction::kLoadFromStorage))
468       .WillOnce(testing::Return(ask_grant2_));
469 
470   NativeFileSystemTransferTokenImpl* token =
471       SerializeAndDeserializeToken(std::move(token_remote));
472   ASSERT_TRUE(token);
473   EXPECT_EQ(kTestOrigin, token->url().origin());
474   EXPECT_EQ(kTestPath, token->url().path());
475   EXPECT_EQ(storage::kFileSystemTypeNativeLocal, token->url().type());
476   EXPECT_EQ(storage::kFileSystemTypeIsolated, token->url().mount_type());
477   EXPECT_EQ(NativeFileSystemTransferTokenImpl::HandleType::kDirectory,
478             token->type());
479   EXPECT_EQ(ask_grant_, token->shared_handle_state().read_grant);
480   EXPECT_EQ(ask_grant2_, token->shared_handle_state().write_grant);
481 }
482 
TEST_F(NativeFileSystemManagerImplTest,SerializeHandle_Native_FileInsideDirectory)483 TEST_F(NativeFileSystemManagerImplTest,
484        SerializeHandle_Native_FileInsideDirectory) {
485   const base::FilePath kDirectoryPath(dir_.GetPath().AppendASCII("foo"));
486   const std::string kTestName = "test file name ☺";
487   base::CreateDirectory(kDirectoryPath);
488 
489   mojo::Remote<blink::mojom::NativeFileSystemDirectoryHandle> directory_handle =
490       GetHandleForDirectory(kDirectoryPath);
491 
492   mojo::Remote<blink::mojom::NativeFileSystemFileHandle> file_handle;
493   base::RunLoop get_file_loop;
494   directory_handle->GetFile(
495       kTestName, /*create=*/true,
496       base::BindLambdaForTesting(
497           [&](blink::mojom::NativeFileSystemErrorPtr result,
498               mojo::PendingRemote<blink::mojom::NativeFileSystemFileHandle>
499                   handle) {
500             get_file_loop.Quit();
501             ASSERT_EQ(blink::mojom::NativeFileSystemStatus::kOk,
502                       result->status);
503             file_handle.Bind(std::move(handle));
504           }));
505   get_file_loop.Run();
506   ASSERT_TRUE(file_handle.is_bound());
507 
508   mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token_remote;
509   file_handle->Transfer(token_remote.InitWithNewPipeAndPassReceiver());
510 
511   // Deserializing tokens should re-request grants, with correct user action.
512   EXPECT_CALL(
513       permission_context_,
514       GetReadPermissionGrant(
515           kTestOrigin, kDirectoryPath, /*is_directory=*/true,
516           /*process_id=*/ChildProcessHost::kInvalidUniqueID,
517           /*frame_id=*/MSG_ROUTING_NONE,
518           NativeFileSystemPermissionContext::UserAction::kLoadFromStorage))
519       .WillOnce(testing::Return(ask_grant_));
520   EXPECT_CALL(
521       permission_context_,
522       GetWritePermissionGrant(
523           kTestOrigin, kDirectoryPath, /*is_directory=*/true,
524           /*process_id=*/ChildProcessHost::kInvalidUniqueID,
525           /*frame_id=*/MSG_ROUTING_NONE,
526           NativeFileSystemPermissionContext::UserAction::kLoadFromStorage))
527       .WillOnce(testing::Return(ask_grant2_));
528 
529   NativeFileSystemTransferTokenImpl* token =
530       SerializeAndDeserializeToken(std::move(token_remote));
531   ASSERT_TRUE(token);
532   EXPECT_EQ(kTestOrigin, token->url().origin());
533   EXPECT_EQ(kDirectoryPath.Append(base::FilePath::FromUTF8Unsafe(kTestName)),
534             token->url().path());
535   EXPECT_EQ(storage::kFileSystemTypeNativeLocal, token->url().type());
536   EXPECT_EQ(storage::kFileSystemTypeIsolated, token->url().mount_type());
537   EXPECT_EQ(NativeFileSystemTransferTokenImpl::HandleType::kFile,
538             token->type());
539   EXPECT_EQ(ask_grant_, token->shared_handle_state().read_grant);
540   EXPECT_EQ(ask_grant2_, token->shared_handle_state().write_grant);
541 }
542 
TEST_F(NativeFileSystemManagerImplTest,SerializeHandle_Native_DirectoryInsideDirectory)543 TEST_F(NativeFileSystemManagerImplTest,
544        SerializeHandle_Native_DirectoryInsideDirectory) {
545   const base::FilePath kDirectoryPath(dir_.GetPath().AppendASCII("foo"));
546   const std::string kTestName = "test dir name";
547   base::CreateDirectory(kDirectoryPath);
548 
549   mojo::Remote<blink::mojom::NativeFileSystemDirectoryHandle> directory_handle =
550       GetHandleForDirectory(kDirectoryPath);
551 
552   mojo::Remote<blink::mojom::NativeFileSystemDirectoryHandle> child_handle;
553   base::RunLoop get_directory_loop;
554   directory_handle->GetDirectory(
555       kTestName, /*create=*/true,
556       base::BindLambdaForTesting(
557           [&](blink::mojom::NativeFileSystemErrorPtr result,
558               mojo::PendingRemote<blink::mojom::NativeFileSystemDirectoryHandle>
559                   handle) {
560             get_directory_loop.Quit();
561             ASSERT_EQ(blink::mojom::NativeFileSystemStatus::kOk,
562                       result->status);
563             child_handle.Bind(std::move(handle));
564           }));
565   get_directory_loop.Run();
566   ASSERT_TRUE(child_handle.is_bound());
567 
568   mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token_remote;
569   child_handle->Transfer(token_remote.InitWithNewPipeAndPassReceiver());
570 
571   // Deserializing tokens should re-request grants, with correct user action.
572   EXPECT_CALL(
573       permission_context_,
574       GetReadPermissionGrant(
575           kTestOrigin, kDirectoryPath, /*is_directory=*/true,
576           /*process_id=*/ChildProcessHost::kInvalidUniqueID,
577           /*frame_id=*/MSG_ROUTING_NONE,
578           NativeFileSystemPermissionContext::UserAction::kLoadFromStorage))
579       .WillOnce(testing::Return(ask_grant_));
580   EXPECT_CALL(
581       permission_context_,
582       GetWritePermissionGrant(
583           kTestOrigin, kDirectoryPath, /*is_directory=*/true,
584           /*process_id=*/ChildProcessHost::kInvalidUniqueID,
585           /*frame_id=*/MSG_ROUTING_NONE,
586           NativeFileSystemPermissionContext::UserAction::kLoadFromStorage))
587       .WillOnce(testing::Return(ask_grant2_));
588 
589   NativeFileSystemTransferTokenImpl* token =
590       SerializeAndDeserializeToken(std::move(token_remote));
591   ASSERT_TRUE(token);
592   EXPECT_EQ(kTestOrigin, token->url().origin());
593   EXPECT_EQ(kDirectoryPath.AppendASCII(kTestName), token->url().path());
594   EXPECT_EQ(storage::kFileSystemTypeNativeLocal, token->url().type());
595   EXPECT_EQ(storage::kFileSystemTypeIsolated, token->url().mount_type());
596   EXPECT_EQ(NativeFileSystemTransferTokenImpl::HandleType::kDirectory,
597             token->type());
598   EXPECT_EQ(ask_grant_, token->shared_handle_state().read_grant);
599   EXPECT_EQ(ask_grant2_, token->shared_handle_state().write_grant);
600 }
601 
602 }  // namespace content
603