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