1// Copyright (c) 2012 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 <string>
6
7#include "base/basictypes.h"
8#include "base/logging.h"
9#include "testing/gtest/include/gtest/gtest.h"
10#include "webkit/browser/fileapi/file_system_url.h"
11#include "webkit/browser/fileapi/isolated_context.h"
12
13#define FPL(x) FILE_PATH_LITERAL(x)
14
15#if defined(FILE_PATH_USES_DRIVE_LETTERS)
16#define DRIVE FPL("C:")
17#else
18#define DRIVE
19#endif
20
21namespace fileapi {
22
23typedef IsolatedContext::MountPointInfo FileInfo;
24
25namespace {
26
27const base::FilePath kTestPaths[] = {
28  base::FilePath(DRIVE FPL("/a/b.txt")),
29  base::FilePath(DRIVE FPL("/c/d/e")),
30  base::FilePath(DRIVE FPL("/h/")),
31  base::FilePath(DRIVE FPL("/")),
32#if defined(FILE_PATH_USES_WIN_SEPARATORS)
33  base::FilePath(DRIVE FPL("\\foo\\bar")),
34  base::FilePath(DRIVE FPL("\\")),
35#endif
36  // For duplicated base name test.
37  base::FilePath(DRIVE FPL("/")),
38  base::FilePath(DRIVE FPL("/f/e")),
39  base::FilePath(DRIVE FPL("/f/b.txt")),
40};
41
42}  // namespace
43
44class IsolatedContextTest : public testing::Test {
45 public:
46  IsolatedContextTest() {
47    for (size_t i = 0; i < arraysize(kTestPaths); ++i)
48      fileset_.insert(kTestPaths[i].NormalizePathSeparators());
49  }
50
51  virtual void SetUp() {
52    IsolatedContext::FileInfoSet files;
53    for (size_t i = 0; i < arraysize(kTestPaths); ++i) {
54      std::string name;
55      ASSERT_TRUE(
56          files.AddPath(kTestPaths[i].NormalizePathSeparators(), &name));
57      names_.push_back(name);
58    }
59    id_ = IsolatedContext::GetInstance()->RegisterDraggedFileSystem(files);
60    IsolatedContext::GetInstance()->AddReference(id_);
61    ASSERT_FALSE(id_.empty());
62  }
63
64  virtual void TearDown() {
65    IsolatedContext::GetInstance()->RemoveReference(id_);
66  }
67
68  IsolatedContext* isolated_context() const {
69    return IsolatedContext::GetInstance();
70  }
71
72 protected:
73  std::string id_;
74  std::multiset<base::FilePath> fileset_;
75  std::vector<std::string> names_;
76
77 private:
78  DISALLOW_COPY_AND_ASSIGN(IsolatedContextTest);
79};
80
81TEST_F(IsolatedContextTest, RegisterAndRevokeTest) {
82  // See if the returned top-level entries match with what we registered.
83  std::vector<FileInfo> toplevels;
84  ASSERT_TRUE(isolated_context()->GetDraggedFileInfo(id_, &toplevels));
85  ASSERT_EQ(fileset_.size(), toplevels.size());
86  for (size_t i = 0; i < toplevels.size(); ++i) {
87    ASSERT_TRUE(fileset_.find(toplevels[i].path) != fileset_.end());
88  }
89
90  // See if the name of each registered kTestPaths (that is what we
91  // register in SetUp() by RegisterDraggedFileSystem) is properly cracked as
92  // a valid virtual path in the isolated filesystem.
93  for (size_t i = 0; i < arraysize(kTestPaths); ++i) {
94    base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(id_)
95        .AppendASCII(names_[i]);
96    std::string cracked_id;
97    base::FilePath cracked_path;
98    FileSystemType cracked_type;
99    FileSystemMountOption cracked_option;
100    ASSERT_TRUE(isolated_context()->CrackVirtualPath(
101        virtual_path, &cracked_id, &cracked_type, &cracked_path,
102        &cracked_option));
103    ASSERT_EQ(kTestPaths[i].NormalizePathSeparators().value(),
104              cracked_path.value());
105    ASSERT_EQ(id_, cracked_id);
106    ASSERT_EQ(kFileSystemTypeDragged, cracked_type);
107  }
108
109  // Make sure GetRegisteredPath returns false for id_ since it is
110  // registered for dragged files.
111  base::FilePath path;
112  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id_, &path));
113
114  // Deref the current one and registering a new one.
115  isolated_context()->RemoveReference(id_);
116
117  std::string id2 = isolated_context()->RegisterFileSystemForPath(
118      kFileSystemTypeNativeLocal, base::FilePath(DRIVE FPL("/foo")), NULL);
119
120  // Make sure the GetDraggedFileInfo returns false for both ones.
121  ASSERT_FALSE(isolated_context()->GetDraggedFileInfo(id2, &toplevels));
122  ASSERT_FALSE(isolated_context()->GetDraggedFileInfo(id_, &toplevels));
123
124  // Make sure the GetRegisteredPath returns true only for the new one.
125  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id_, &path));
126  ASSERT_TRUE(isolated_context()->GetRegisteredPath(id2, &path));
127
128  // Try registering three more file systems for the same path as id2.
129  std::string id3 = isolated_context()->RegisterFileSystemForPath(
130      kFileSystemTypeNativeLocal, path, NULL);
131  std::string id4 = isolated_context()->RegisterFileSystemForPath(
132      kFileSystemTypeNativeLocal, path, NULL);
133  std::string id5 = isolated_context()->RegisterFileSystemForPath(
134      kFileSystemTypeNativeLocal, path, NULL);
135
136  // Remove file system for id4.
137  isolated_context()->AddReference(id4);
138  isolated_context()->RemoveReference(id4);
139
140  // Only id4 should become invalid now.
141  ASSERT_TRUE(isolated_context()->GetRegisteredPath(id2, &path));
142  ASSERT_TRUE(isolated_context()->GetRegisteredPath(id3, &path));
143  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id4, &path));
144  ASSERT_TRUE(isolated_context()->GetRegisteredPath(id5, &path));
145
146  // Revoke file system id5, after adding multiple references.
147  isolated_context()->AddReference(id5);
148  isolated_context()->AddReference(id5);
149  isolated_context()->AddReference(id5);
150  isolated_context()->RevokeFileSystem(id5);
151
152  // No matter how many references we add id5 must be invalid now.
153  ASSERT_TRUE(isolated_context()->GetRegisteredPath(id2, &path));
154  ASSERT_TRUE(isolated_context()->GetRegisteredPath(id3, &path));
155  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id4, &path));
156  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id5, &path));
157
158  // Revoke the file systems by path.
159  isolated_context()->RevokeFileSystemByPath(path);
160
161  // Now all the file systems associated to the path must be invalid.
162  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id2, &path));
163  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id3, &path));
164  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id4, &path));
165  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id5, &path));
166}
167
168TEST_F(IsolatedContextTest, CrackWithRelativePaths) {
169  const struct {
170    base::FilePath::StringType path;
171    bool valid;
172  } relatives[] = {
173    { FPL("foo"), true },
174    { FPL("foo/bar"), true },
175    { FPL(".."), false },
176    { FPL("foo/.."), false },
177    { FPL("foo/../bar"), false },
178#if defined(FILE_PATH_USES_WIN_SEPARATORS)
179# define SHOULD_FAIL_WITH_WIN_SEPARATORS false
180#else
181# define SHOULD_FAIL_WITH_WIN_SEPARATORS true
182#endif
183    { FPL("foo\\..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS },
184    { FPL("foo/..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS },
185  };
186
187  for (size_t i = 0; i < arraysize(kTestPaths); ++i) {
188    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(relatives); ++j) {
189      SCOPED_TRACE(testing::Message() << "Testing "
190                   << kTestPaths[i].value() << " " << relatives[j].path);
191      base::FilePath virtual_path =
192          isolated_context()->CreateVirtualRootPath(id_).AppendASCII(
193              names_[i]).Append(relatives[j].path);
194      std::string cracked_id;
195      base::FilePath cracked_path;
196      FileSystemType cracked_type;
197      FileSystemMountOption cracked_option;
198      if (!relatives[j].valid) {
199        ASSERT_FALSE(isolated_context()->CrackVirtualPath(
200            virtual_path, &cracked_id, &cracked_type, &cracked_path,
201            &cracked_option));
202        continue;
203      }
204      ASSERT_TRUE(isolated_context()->CrackVirtualPath(
205          virtual_path, &cracked_id, &cracked_type, &cracked_path,
206          &cracked_option));
207      ASSERT_EQ(kTestPaths[i].Append(relatives[j].path)
208                    .NormalizePathSeparators().value(),
209                cracked_path.value());
210      ASSERT_EQ(id_, cracked_id);
211      ASSERT_EQ(kFileSystemTypeDragged, cracked_type);
212    }
213  }
214}
215
216TEST_F(IsolatedContextTest, CrackURLWithRelativePaths) {
217  const struct {
218    base::FilePath::StringType path;
219    bool valid;
220  } relatives[] = {
221    { FPL("foo"), true },
222    { FPL("foo/bar"), true },
223    { FPL(".."), false },
224    { FPL("foo/.."), false },
225    { FPL("foo/../bar"), false },
226#if defined(FILE_PATH_USES_WIN_SEPARATORS)
227# define SHOULD_FAIL_WITH_WIN_SEPARATORS false
228#else
229# define SHOULD_FAIL_WITH_WIN_SEPARATORS true
230#endif
231    { FPL("foo\\..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS },
232    { FPL("foo/..\\baz"), SHOULD_FAIL_WITH_WIN_SEPARATORS },
233  };
234
235  for (size_t i = 0; i < arraysize(kTestPaths); ++i) {
236    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(relatives); ++j) {
237      SCOPED_TRACE(testing::Message() << "Testing "
238                   << kTestPaths[i].value() << " " << relatives[j].path);
239      base::FilePath virtual_path =
240          isolated_context()->CreateVirtualRootPath(id_).AppendASCII(
241              names_[i]).Append(relatives[j].path);
242
243      FileSystemURL cracked = isolated_context()->CreateCrackedFileSystemURL(
244          GURL("http://chromium.org"), kFileSystemTypeIsolated, virtual_path);
245
246      ASSERT_EQ(relatives[j].valid, cracked.is_valid());
247
248      if (!relatives[j].valid)
249        continue;
250      ASSERT_EQ(GURL("http://chromium.org"), cracked.origin());
251      ASSERT_EQ(kTestPaths[i].Append(relatives[j].path)
252                    .NormalizePathSeparators().value(),
253                cracked.path().value());
254      ASSERT_EQ(virtual_path.NormalizePathSeparators(), cracked.virtual_path());
255      ASSERT_EQ(id_, cracked.filesystem_id());
256      ASSERT_EQ(kFileSystemTypeDragged, cracked.type());
257      ASSERT_EQ(kFileSystemTypeIsolated, cracked.mount_type());
258    }
259  }
260}
261
262TEST_F(IsolatedContextTest, TestWithVirtualRoot) {
263  std::string cracked_id;
264  base::FilePath cracked_path;
265  FileSystemMountOption cracked_option;
266
267  // Trying to crack virtual root "/" returns true but with empty cracked path
268  // as "/" of the isolated filesystem is a pure virtual directory
269  // that has no corresponding platform directory.
270  base::FilePath virtual_path = isolated_context()->CreateVirtualRootPath(id_);
271  ASSERT_TRUE(isolated_context()->CrackVirtualPath(
272      virtual_path, &cracked_id, NULL, &cracked_path, &cracked_option));
273  ASSERT_EQ(FPL(""), cracked_path.value());
274  ASSERT_EQ(id_, cracked_id);
275
276  // Trying to crack "/foo" should fail (because "foo" is not the one
277  // included in the kTestPaths).
278  virtual_path = isolated_context()->CreateVirtualRootPath(
279      id_).AppendASCII("foo");
280  ASSERT_FALSE(isolated_context()->CrackVirtualPath(
281      virtual_path, &cracked_id, NULL, &cracked_path, &cracked_option));
282}
283
284TEST_F(IsolatedContextTest, CanHandleURL) {
285  const GURL test_origin("http://chromium.org");
286  const base::FilePath test_path(FPL("/mount"));
287
288  // Should handle isolated file system.
289  EXPECT_TRUE(isolated_context()->HandlesFileSystemMountType(
290      fileapi::kFileSystemTypeIsolated));
291
292  // Shouldn't handle the rest.
293  EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
294      fileapi::kFileSystemTypeExternal));
295  EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
296      fileapi::kFileSystemTypeTemporary));
297  EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
298      fileapi::kFileSystemTypePersistent));
299  EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
300      fileapi::kFileSystemTypeTest));
301  // Not even if it's isolated subtype.
302  EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
303      fileapi::kFileSystemTypeNativeLocal));
304  EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
305      fileapi::kFileSystemTypeDragged));
306  EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
307      fileapi::kFileSystemTypeNativeMedia));
308  EXPECT_FALSE(isolated_context()->HandlesFileSystemMountType(
309      fileapi::kFileSystemTypeDeviceMedia));
310}
311
312TEST_F(IsolatedContextTest, VirtualFileSystemTests) {
313  // Should be able to register empty and non-absolute paths
314  std::string empty_fsid = isolated_context()->RegisterFileSystemForVirtualPath(
315      fileapi::kFileSystemTypeIsolated, "_", base::FilePath());
316  std::string relative_fsid =
317      isolated_context()->RegisterFileSystemForVirtualPath(
318          fileapi::kFileSystemTypeIsolated, "_",
319          base::FilePath(FPL("relpath")));
320  ASSERT_FALSE(empty_fsid.empty());
321  ASSERT_FALSE(relative_fsid.empty());
322
323  // Make sure that filesystem root is not prepended to cracked virtual paths.
324  base::FilePath database_root = base::FilePath(DRIVE FPL("/database_path"));
325  std::string database_fsid =
326      isolated_context()->RegisterFileSystemForVirtualPath(
327          fileapi::kFileSystemTypeIsolated, "_", database_root);
328
329  base::FilePath test_virtual_path =
330      base::FilePath().AppendASCII("virtualdir").AppendASCII("virtualfile.txt");
331
332  base::FilePath whole_virtual_path =
333      isolated_context()->CreateVirtualRootPath(database_fsid)
334          .AppendASCII("_").Append(test_virtual_path);
335
336  std::string cracked_id;
337  base::FilePath cracked_path;
338  FileSystemMountOption cracked_option;
339  ASSERT_TRUE(isolated_context()->CrackVirtualPath(
340      whole_virtual_path, &cracked_id, NULL, &cracked_path, &cracked_option));
341  ASSERT_EQ(database_fsid, cracked_id);
342  ASSERT_EQ(test_virtual_path, cracked_path);
343}
344
345}  // namespace fileapi
346