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 "fake_ppapi/fake_pepper_interface_html5_fs.h"
6
7 #include <string.h>
8
9 #include <algorithm>
10
11 #include <ppapi/c/pp_completion_callback.h>
12 #include <ppapi/c/pp_errors.h>
13
14 #include "gtest/gtest.h"
15
16 namespace {
17
18 class FakeInstanceResource : public FakeResource {
19 public:
FakeInstanceResource()20 FakeInstanceResource() : filesystem_template(NULL) {}
classname()21 static const char* classname() { return "FakeInstanceResource"; }
22
23 FakeHtml5FsFilesystem* filesystem_template; // Weak reference.
24 };
25
26 class FakeFileSystemResource : public FakeResource {
27 public:
FakeFileSystemResource()28 FakeFileSystemResource() : filesystem(NULL), opened(false) {}
~FakeFileSystemResource()29 ~FakeFileSystemResource() { delete filesystem; }
classname()30 static const char* classname() { return "FakeFileSystemResource"; }
31
32 FakeHtml5FsFilesystem* filesystem; // Owned.
33 bool opened;
34 };
35
36 class FakeFileRefResource : public FakeResource {
37 public:
FakeFileRefResource()38 FakeFileRefResource() : filesystem(NULL) {}
classname()39 static const char* classname() { return "FakeFileRefResource"; }
40
41 FakeHtml5FsFilesystem* filesystem; // Weak reference.
42 FakeHtml5FsFilesystem::Path path;
43 };
44
45 class FakeFileIoResource : public FakeResource {
46 public:
FakeFileIoResource()47 FakeFileIoResource() : node(NULL), open_flags(0) {}
classname()48 static const char* classname() { return "FakeFileIoResource"; }
49
50 FakeHtml5FsNode* node; // Weak reference.
51 int32_t open_flags;
52 };
53
54 // Helper function to call the completion callback if it is defined (an
55 // asynchronous call), or return the result directly if it isn't (a synchronous
56 // call).
57 //
58 // Use like this:
59 // if (<some error condition>)
60 // return RunCompletionCallback(callback, PP_ERROR_FUBAR);
61 //
62 // /* Everything worked OK */
63 // return RunCompletionCallback(callback, PP_OK);
RunCompletionCallback(PP_CompletionCallback * callback,int32_t result)64 int32_t RunCompletionCallback(PP_CompletionCallback* callback, int32_t result) {
65 if (callback->func) {
66 PP_RunCompletionCallback(callback, result);
67 return PP_OK_COMPLETIONPENDING;
68 }
69 return result;
70 }
71
72 } // namespace
73
FakeHtml5FsNode(const PP_FileInfo & info)74 FakeHtml5FsNode::FakeHtml5FsNode(const PP_FileInfo& info) : info_(info) {}
75
FakeHtml5FsNode(const PP_FileInfo & info,const std::vector<uint8_t> & contents)76 FakeHtml5FsNode::FakeHtml5FsNode(const PP_FileInfo& info,
77 const std::vector<uint8_t>& contents)
78 : info_(info), contents_(contents) {}
79
FakeHtml5FsNode(const PP_FileInfo & info,const std::string & contents)80 FakeHtml5FsNode::FakeHtml5FsNode(const PP_FileInfo& info,
81 const std::string& contents)
82 : info_(info) {
83 std::copy(contents.begin(), contents.end(), std::back_inserter(contents_));
84 }
85
Read(int64_t offset,char * buffer,int32_t bytes_to_read)86 int32_t FakeHtml5FsNode::Read(int64_t offset,
87 char* buffer,
88 int32_t bytes_to_read) {
89 if (offset < 0)
90 return PP_ERROR_FAILED;
91
92 bytes_to_read =
93 std::max(0, std::min<int32_t>(bytes_to_read, contents_.size() - offset));
94 memcpy(buffer, contents_.data() + offset, bytes_to_read);
95 return bytes_to_read;
96 }
97
Write(int64_t offset,const char * buffer,int32_t bytes_to_write)98 int32_t FakeHtml5FsNode::Write(int64_t offset,
99 const char* buffer,
100 int32_t bytes_to_write) {
101 if (offset < 0)
102 return PP_ERROR_FAILED;
103
104 size_t new_size = offset + bytes_to_write;
105 if (new_size > contents_.size())
106 contents_.resize(new_size);
107
108 memcpy(contents_.data() + offset, buffer, bytes_to_write);
109 info_.size = new_size;
110 return bytes_to_write;
111 }
112
Append(const char * buffer,int32_t bytes_to_write)113 int32_t FakeHtml5FsNode::Append(const char* buffer, int32_t bytes_to_write) {
114 return Write(contents_.size(), buffer, bytes_to_write);
115 }
116
SetLength(int64_t length)117 int32_t FakeHtml5FsNode::SetLength(int64_t length) {
118 contents_.resize(length);
119 info_.size = length;
120 return PP_OK;
121 }
122
GetInfo(PP_FileInfo * out_info)123 void FakeHtml5FsNode::GetInfo(PP_FileInfo* out_info) { *out_info = info_; }
124
IsRegular() const125 bool FakeHtml5FsNode::IsRegular() const {
126 return info_.type == PP_FILETYPE_REGULAR;
127 }
128
IsDirectory() const129 bool FakeHtml5FsNode::IsDirectory() const {
130 return info_.type == PP_FILETYPE_DIRECTORY;
131 }
132
FakeHtml5FsFilesystem()133 FakeHtml5FsFilesystem::FakeHtml5FsFilesystem()
134 : filesystem_type_(PP_FILESYSTEMTYPE_INVALID) {
135 Clear();
136 }
137
FakeHtml5FsFilesystem(PP_FileSystemType type)138 FakeHtml5FsFilesystem::FakeHtml5FsFilesystem(PP_FileSystemType type)
139 : filesystem_type_(type) {
140 Clear();
141 }
142
FakeHtml5FsFilesystem(const FakeHtml5FsFilesystem & filesystem,PP_FileSystemType type)143 FakeHtml5FsFilesystem::FakeHtml5FsFilesystem(
144 const FakeHtml5FsFilesystem& filesystem,
145 PP_FileSystemType type)
146 : node_map_(filesystem.node_map_), filesystem_type_(type) {}
147
Clear()148 void FakeHtml5FsFilesystem::Clear() {
149 node_map_.clear();
150 // Always have a root node.
151 AddDirectory("/", NULL);
152 }
153
AddEmptyFile(const Path & path,FakeHtml5FsNode ** out_node)154 bool FakeHtml5FsFilesystem::AddEmptyFile(const Path& path,
155 FakeHtml5FsNode** out_node) {
156 return AddFile(path, std::vector<uint8_t>(), out_node);
157 }
158
AddFile(const Path & path,const std::string & contents,FakeHtml5FsNode ** out_node)159 bool FakeHtml5FsFilesystem::AddFile(const Path& path,
160 const std::string& contents,
161 FakeHtml5FsNode** out_node) {
162 std::vector<uint8_t> data;
163 std::copy(contents.begin(), contents.end(), std::back_inserter(data));
164 return AddFile(path, data, out_node);
165 }
166
AddFile(const Path & path,const std::vector<uint8_t> & contents,FakeHtml5FsNode ** out_node)167 bool FakeHtml5FsFilesystem::AddFile(const Path& path,
168 const std::vector<uint8_t>& contents,
169 FakeHtml5FsNode** out_node) {
170 NodeMap::iterator iter = node_map_.find(path);
171 if (iter != node_map_.end()) {
172 if (out_node)
173 *out_node = NULL;
174 return false;
175 }
176
177 PP_FileInfo info;
178 info.size = contents.size();
179 info.type = PP_FILETYPE_REGULAR;
180 info.system_type = filesystem_type_;
181 info.creation_time = 0;
182 info.last_access_time = 0;
183 info.last_modified_time = 0;
184
185 FakeHtml5FsNode node(info, contents);
186 std::pair<NodeMap::iterator, bool> result =
187 node_map_.insert(NodeMap::value_type(path, node));
188
189 EXPECT_EQ(true, result.second);
190 if (out_node)
191 *out_node = &result.first->second;
192 return true;
193 }
194
AddDirectory(const Path & path,FakeHtml5FsNode ** out_node)195 bool FakeHtml5FsFilesystem::AddDirectory(const Path& path,
196 FakeHtml5FsNode** out_node) {
197 NodeMap::iterator iter = node_map_.find(path);
198 if (iter != node_map_.end()) {
199 if (out_node)
200 *out_node = NULL;
201 return false;
202 }
203
204 PP_FileInfo info;
205 info.size = 0;
206 info.type = PP_FILETYPE_DIRECTORY;
207 info.system_type = filesystem_type_;
208 info.creation_time = 0;
209 info.last_access_time = 0;
210 info.last_modified_time = 0;
211
212 FakeHtml5FsNode node(info);
213 std::pair<NodeMap::iterator, bool> result =
214 node_map_.insert(NodeMap::value_type(path, node));
215
216 EXPECT_EQ(true, result.second);
217 if (out_node)
218 *out_node = &result.first->second;
219 return true;
220 }
221
RemoveNode(const Path & path)222 bool FakeHtml5FsFilesystem::RemoveNode(const Path& path) {
223 return node_map_.erase(path) >= 1;
224 }
225
GetNode(const Path & path)226 FakeHtml5FsNode* FakeHtml5FsFilesystem::GetNode(const Path& path) {
227 NodeMap::iterator iter = node_map_.find(path);
228 if (iter == node_map_.end())
229 return NULL;
230 return &iter->second;
231 }
232
GetDirectoryEntries(const Path & path,DirectoryEntries * out_dir_entries) const233 bool FakeHtml5FsFilesystem::GetDirectoryEntries(
234 const Path& path,
235 DirectoryEntries* out_dir_entries) const {
236 out_dir_entries->clear();
237
238 NodeMap::const_iterator iter = node_map_.find(path);
239 if (iter == node_map_.end())
240 return false;
241
242 const FakeHtml5FsNode& dir_node = iter->second;
243 if (!dir_node.IsDirectory())
244 return false;
245
246 for (NodeMap::const_iterator iter = node_map_.begin();
247 iter != node_map_.end();
248 ++iter) {
249 const Path& node_path = iter->first;
250 if (node_path.find(path) == std::string::npos)
251 continue;
252
253 // A node is not a child of itself.
254 if (&iter->second == &dir_node)
255 continue;
256
257 // Only consider children, not descendants. If we find a forward slash, then
258 // the node must be in a subdirectory.
259 if (node_path.find('/', path.size() + 1) != std::string::npos)
260 continue;
261
262 // The directory entry names do not include the path.
263 Path entry_path = node_path;
264 size_t last_slash = node_path.rfind('/');
265 if (last_slash != std::string::npos)
266 entry_path.erase(0, last_slash + 1);
267
268 DirectoryEntry entry;
269 entry.path = entry_path;
270 entry.node = &iter->second;
271 out_dir_entries->push_back(entry);
272 }
273
274 return true;
275 }
276
277 // static
GetParentPath(const Path & path)278 FakeHtml5FsFilesystem::Path FakeHtml5FsFilesystem::GetParentPath(
279 const Path& path) {
280 size_t last_slash = path.rfind('/');
281 if (last_slash == 0)
282 return "/";
283
284 EXPECT_EQ(std::string::npos, last_slash);
285 return path.substr(0, last_slash);
286 }
287
FakeFileIoInterface(FakeCoreInterface * core_interface)288 FakeFileIoInterface::FakeFileIoInterface(FakeCoreInterface* core_interface)
289 : core_interface_(core_interface) {}
290
Create(PP_Resource)291 PP_Resource FakeFileIoInterface::Create(PP_Resource) {
292 return CREATE_RESOURCE(core_interface_->resource_manager(),
293 FakeFileIoResource,
294 new FakeFileIoResource);
295 }
296
Open(PP_Resource file_io,PP_Resource file_ref,int32_t open_flags,PP_CompletionCallback callback)297 int32_t FakeFileIoInterface::Open(PP_Resource file_io,
298 PP_Resource file_ref,
299 int32_t open_flags,
300 PP_CompletionCallback callback) {
301 FakeFileIoResource* file_io_resource =
302 core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
303 if (file_io_resource == NULL)
304 return PP_ERROR_BADRESOURCE;
305
306 bool flag_write = !!(open_flags & PP_FILEOPENFLAG_WRITE);
307 bool flag_create = !!(open_flags & PP_FILEOPENFLAG_CREATE);
308 bool flag_truncate = !!(open_flags & PP_FILEOPENFLAG_TRUNCATE);
309 bool flag_exclusive = !!(open_flags & PP_FILEOPENFLAG_EXCLUSIVE);
310 bool flag_append = !!(open_flags & PP_FILEOPENFLAG_APPEND);
311
312 if ((flag_append && flag_write) || (flag_truncate && !flag_write))
313 return PP_ERROR_BADARGUMENT;
314
315 FakeFileRefResource* file_ref_resource =
316 core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref);
317 if (file_ref_resource == NULL)
318 return PP_ERROR_BADRESOURCE;
319
320 const FakeHtml5FsFilesystem::Path& path = file_ref_resource->path;
321 FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem;
322 FakeHtml5FsNode* node = filesystem->GetNode(path);
323 bool node_exists = node != NULL;
324
325 if (!node_exists) {
326 if (!flag_create)
327 return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
328
329 bool result = filesystem->AddEmptyFile(path, &node);
330 EXPECT_EQ(true, result);
331 } else {
332 if (flag_create && flag_exclusive)
333 return RunCompletionCallback(&callback, PP_ERROR_FILEEXISTS);
334 }
335
336 file_io_resource->node = node;
337 file_io_resource->open_flags = open_flags;
338
339 if (flag_truncate)
340 return RunCompletionCallback(&callback, node->SetLength(0));
341
342 return RunCompletionCallback(&callback, PP_OK);
343 }
344
Query(PP_Resource file_io,PP_FileInfo * info,PP_CompletionCallback callback)345 int32_t FakeFileIoInterface::Query(PP_Resource file_io,
346 PP_FileInfo* info,
347 PP_CompletionCallback callback) {
348 FakeFileIoResource* file_io_resource =
349 core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
350 if (file_io_resource == NULL)
351 return PP_ERROR_BADRESOURCE;
352
353 if (!file_io_resource->node)
354 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
355
356 file_io_resource->node->GetInfo(info);
357 return RunCompletionCallback(&callback, PP_OK);
358 }
359
Read(PP_Resource file_io,int64_t offset,char * buffer,int32_t bytes_to_read,PP_CompletionCallback callback)360 int32_t FakeFileIoInterface::Read(PP_Resource file_io,
361 int64_t offset,
362 char* buffer,
363 int32_t bytes_to_read,
364 PP_CompletionCallback callback) {
365 FakeFileIoResource* file_io_resource =
366 core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
367 if (file_io_resource == NULL)
368 return PP_ERROR_BADRESOURCE;
369
370 if (bytes_to_read < 0)
371 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
372
373 if ((file_io_resource->open_flags & PP_FILEOPENFLAG_READ) !=
374 PP_FILEOPENFLAG_READ) {
375 return RunCompletionCallback(&callback, PP_ERROR_NOACCESS);
376 }
377
378 if (!file_io_resource->node)
379 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
380
381 int32_t result = file_io_resource->node->Read(offset, buffer, bytes_to_read);
382 return RunCompletionCallback(&callback, result);
383 }
384
Write(PP_Resource file_io,int64_t offset,const char * buffer,int32_t bytes_to_write,PP_CompletionCallback callback)385 int32_t FakeFileIoInterface::Write(PP_Resource file_io,
386 int64_t offset,
387 const char* buffer,
388 int32_t bytes_to_write,
389 PP_CompletionCallback callback) {
390 FakeFileIoResource* file_io_resource =
391 core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
392 if (file_io_resource == NULL)
393 return PP_ERROR_BADRESOURCE;
394
395 if ((file_io_resource->open_flags & PP_FILEOPENFLAG_WRITE) !=
396 PP_FILEOPENFLAG_WRITE) {
397 return RunCompletionCallback(&callback, PP_ERROR_NOACCESS);
398 }
399
400 if (!file_io_resource->node)
401 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
402
403 int32_t result;
404 if ((file_io_resource->open_flags & PP_FILEOPENFLAG_APPEND) ==
405 PP_FILEOPENFLAG_APPEND) {
406 result = file_io_resource->node->Append(buffer, bytes_to_write);
407 } else {
408 result = file_io_resource->node->Write(offset, buffer, bytes_to_write);
409 }
410
411 return RunCompletionCallback(&callback, result);
412 }
413
SetLength(PP_Resource file_io,int64_t length,PP_CompletionCallback callback)414 int32_t FakeFileIoInterface::SetLength(PP_Resource file_io,
415 int64_t length,
416 PP_CompletionCallback callback) {
417 FakeFileIoResource* file_io_resource =
418 core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
419 if (file_io_resource == NULL)
420 return PP_ERROR_BADRESOURCE;
421
422 if ((file_io_resource->open_flags & PP_FILEOPENFLAG_WRITE) !=
423 PP_FILEOPENFLAG_WRITE) {
424 return RunCompletionCallback(&callback, PP_ERROR_NOACCESS);
425 }
426
427 if (!file_io_resource->node)
428 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
429
430 int32_t result = file_io_resource->node->SetLength(length);
431 return RunCompletionCallback(&callback, result);
432 }
433
Flush(PP_Resource file_io,PP_CompletionCallback callback)434 int32_t FakeFileIoInterface::Flush(PP_Resource file_io,
435 PP_CompletionCallback callback) {
436 FakeFileIoResource* file_io_resource =
437 core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
438 if (file_io_resource == NULL)
439 return PP_ERROR_BADRESOURCE;
440
441 if (!file_io_resource->node)
442 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
443
444 return RunCompletionCallback(&callback, PP_OK);
445 }
446
Close(PP_Resource file_io)447 void FakeFileIoInterface::Close(PP_Resource file_io) {
448 FakeFileIoResource* file_io_resource =
449 core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io);
450 if (file_io_resource == NULL)
451 return;
452
453 file_io_resource->node = NULL;
454 file_io_resource->open_flags = 0;
455 }
456
FakeFileRefInterface(FakeCoreInterface * core_interface,FakeVarInterface * var_interface)457 FakeFileRefInterface::FakeFileRefInterface(FakeCoreInterface* core_interface,
458 FakeVarInterface* var_interface)
459 : core_interface_(core_interface), var_interface_(var_interface) {}
460
Create(PP_Resource file_system,const char * path)461 PP_Resource FakeFileRefInterface::Create(PP_Resource file_system,
462 const char* path) {
463 FakeFileSystemResource* file_system_resource =
464 core_interface_->resource_manager()->Get<FakeFileSystemResource>(
465 file_system);
466 if (file_system_resource == NULL)
467 return PP_ERROR_BADRESOURCE;
468
469 if (!file_system_resource->opened)
470 return PP_ERROR_FAILED;
471
472 if (path == NULL)
473 return PP_ERROR_FAILED;
474
475 size_t path_len = strlen(path);
476 if (path_len == 0)
477 return PP_ERROR_FAILED;
478
479 FakeFileRefResource* file_ref_resource = new FakeFileRefResource;
480 file_ref_resource->filesystem = file_system_resource->filesystem;
481 file_ref_resource->path = path;
482
483 // Remove a trailing slash from the path, unless it is the root path.
484 if (path_len > 1 && file_ref_resource->path[path_len - 1] == '/')
485 file_ref_resource->path.erase(path_len - 1);
486
487 return CREATE_RESOURCE(core_interface_->resource_manager(),
488 FakeFileRefResource,
489 file_ref_resource);
490 }
491
GetName(PP_Resource file_ref)492 PP_Var FakeFileRefInterface::GetName(PP_Resource file_ref) {
493 FakeFileRefResource* file_ref_resource =
494 core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref);
495 if (file_ref_resource == NULL)
496 return PP_MakeUndefined();
497
498 return var_interface_->VarFromUtf8(file_ref_resource->path.c_str(),
499 file_ref_resource->path.size());
500 }
501
MakeDirectory(PP_Resource directory_ref,PP_Bool make_parents,PP_CompletionCallback callback)502 int32_t FakeFileRefInterface::MakeDirectory(PP_Resource directory_ref,
503 PP_Bool make_parents,
504 PP_CompletionCallback callback) {
505 FakeFileRefResource* directory_ref_resource =
506 core_interface_->resource_manager()->Get<FakeFileRefResource>(
507 directory_ref);
508 if (directory_ref_resource == NULL)
509 return PP_ERROR_BADRESOURCE;
510
511 // TODO(binji): We don't currently use make_parents in nacl_io, so
512 // I won't bother handling it yet.
513 if (make_parents == PP_TRUE)
514 return PP_ERROR_FAILED;
515
516 FakeHtml5FsFilesystem* filesystem = directory_ref_resource->filesystem;
517 FakeHtml5FsFilesystem::Path path = directory_ref_resource->path;
518
519 // Pepper returns PP_ERROR_NOACCESS when trying to create the root directory,
520 // not PP_ERROR_FILEEXISTS, as you might expect.
521 if (path == "/")
522 return RunCompletionCallback(&callback, PP_ERROR_NOACCESS);
523
524 FakeHtml5FsNode* node = filesystem->GetNode(path);
525 if (node != NULL)
526 return RunCompletionCallback(&callback, PP_ERROR_FILEEXISTS);
527
528 FakeHtml5FsFilesystem::Path parent_path = filesystem->GetParentPath(path);
529 FakeHtml5FsNode* parent_node = filesystem->GetNode(parent_path);
530 if (parent_node == NULL)
531 return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
532
533 if (!parent_node->IsDirectory())
534 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
535
536 bool result = filesystem->AddDirectory(directory_ref_resource->path, NULL);
537 EXPECT_EQ(true, result);
538 return RunCompletionCallback(&callback, PP_OK);
539 }
540
Delete(PP_Resource file_ref,PP_CompletionCallback callback)541 int32_t FakeFileRefInterface::Delete(PP_Resource file_ref,
542 PP_CompletionCallback callback) {
543 FakeFileRefResource* file_ref_resource =
544 core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref);
545 if (file_ref_resource == NULL)
546 return PP_ERROR_BADRESOURCE;
547
548 FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem;
549 FakeHtml5FsFilesystem::Path path = file_ref_resource->path;
550 FakeHtml5FsNode* node = filesystem->GetNode(path);
551 if (node == NULL)
552 return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
553
554 filesystem->RemoveNode(path);
555 return RunCompletionCallback(&callback, PP_OK);
556 }
557
Query(PP_Resource file_ref,PP_FileInfo * info,PP_CompletionCallback callback)558 int32_t FakeFileRefInterface::Query(PP_Resource file_ref,
559 PP_FileInfo* info,
560 PP_CompletionCallback callback) {
561 FakeFileRefResource* file_ref_resource =
562 core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref);
563 if (file_ref_resource == NULL)
564 return PP_ERROR_BADRESOURCE;
565
566 FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem;
567 FakeHtml5FsFilesystem::Path path = file_ref_resource->path;
568 FakeHtml5FsNode* node = filesystem->GetNode(path);
569 if (node == NULL)
570 return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
571
572 node->GetInfo(info);
573 return RunCompletionCallback(&callback, PP_OK);
574 }
575
ReadDirectoryEntries(PP_Resource directory_ref,const PP_ArrayOutput & output,PP_CompletionCallback callback)576 int32_t FakeFileRefInterface::ReadDirectoryEntries(
577 PP_Resource directory_ref,
578 const PP_ArrayOutput& output,
579 PP_CompletionCallback callback) {
580 FakeFileRefResource* directory_ref_resource =
581 core_interface_->resource_manager()->Get<FakeFileRefResource>(
582 directory_ref);
583 if (directory_ref_resource == NULL)
584 return PP_ERROR_BADRESOURCE;
585
586 FakeHtml5FsFilesystem* filesystem = directory_ref_resource->filesystem;
587 FakeHtml5FsFilesystem::Path path = directory_ref_resource->path;
588 FakeHtml5FsNode* node = filesystem->GetNode(path);
589 if (node == NULL)
590 return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
591
592 if (!node->IsDirectory())
593 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
594
595 FakeHtml5FsFilesystem::DirectoryEntries fake_dir_entries;
596 filesystem->GetDirectoryEntries(path, &fake_dir_entries);
597
598 uint32_t element_count = fake_dir_entries.size();
599 uint32_t element_size = sizeof(fake_dir_entries[0]);
600 void* data_buffer =
601 (*output.GetDataBuffer)(output.user_data, element_count, element_size);
602
603 if (data_buffer == NULL)
604 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
605
606 PP_DirectoryEntry* dir_entries = static_cast<PP_DirectoryEntry*>(data_buffer);
607 for (uint32_t i = 0; i < element_count; ++i) {
608 const FakeHtml5FsFilesystem::DirectoryEntry& fake_dir_entry =
609 fake_dir_entries[i];
610
611 FakeFileRefResource* file_ref_resource = new FakeFileRefResource;
612 file_ref_resource->filesystem = directory_ref_resource->filesystem;
613 file_ref_resource->path = fake_dir_entry.path;
614 PP_Resource file_ref = CREATE_RESOURCE(core_interface_->resource_manager(),
615 FakeFileRefResource,
616 file_ref_resource);
617
618 dir_entries[i].file_ref = file_ref;
619 dir_entries[i].file_type = fake_dir_entry.node->file_type();
620 }
621
622 return RunCompletionCallback(&callback, PP_OK);
623 }
624
Rename(PP_Resource file_ref,PP_Resource new_file_ref,PP_CompletionCallback callback)625 int32_t FakeFileRefInterface::Rename(PP_Resource file_ref,
626 PP_Resource new_file_ref,
627 PP_CompletionCallback callback) {
628 FakeFileRefResource* file_ref_resource =
629 core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref);
630 if (file_ref_resource == NULL)
631 return PP_ERROR_BADRESOURCE;
632
633 FakeFileRefResource* new_file_ref_resource =
634 core_interface_->resource_manager()->Get<FakeFileRefResource>(
635 new_file_ref);
636 if (new_file_ref_resource == NULL)
637 return PP_ERROR_BADRESOURCE;
638
639 FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem;
640 FakeHtml5FsFilesystem::Path path = file_ref_resource->path;
641 FakeHtml5FsFilesystem::Path newpath = new_file_ref_resource->path;
642 FakeHtml5FsNode* node = filesystem->GetNode(path);
643 if (node == NULL)
644 return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
645 // FakeFileRefResource does not support directory rename.
646 if (!node->IsRegular())
647 return RunCompletionCallback(&callback, PP_ERROR_NOTAFILE);
648
649 // Remove the destination if it exists.
650 filesystem->RemoveNode(newpath);
651 const std::vector<uint8_t> contents = node->contents();
652 EXPECT_TRUE(filesystem->AddFile(newpath, contents, NULL));
653 EXPECT_TRUE(filesystem->RemoveNode(path));
654 return RunCompletionCallback(&callback, PP_OK);
655 }
656
FakeFileSystemInterface(FakeCoreInterface * core_interface)657 FakeFileSystemInterface::FakeFileSystemInterface(
658 FakeCoreInterface* core_interface)
659 : core_interface_(core_interface) {}
660
IsFileSystem(PP_Resource resource)661 PP_Bool FakeFileSystemInterface::IsFileSystem(PP_Resource resource) {
662 bool not_found_ok = true;
663 FakeFileSystemResource* file_system_resource =
664 core_interface_->resource_manager()->Get<FakeFileSystemResource>(
665 resource, not_found_ok);
666 return file_system_resource != NULL ? PP_TRUE : PP_FALSE;
667 }
668
Create(PP_Instance instance,PP_FileSystemType filesystem_type)669 PP_Resource FakeFileSystemInterface::Create(PP_Instance instance,
670 PP_FileSystemType filesystem_type) {
671 FakeInstanceResource* instance_resource =
672 core_interface_->resource_manager()->Get<FakeInstanceResource>(instance);
673 if (instance_resource == NULL)
674 return PP_ERROR_BADRESOURCE;
675
676 FakeFileSystemResource* file_system_resource = new FakeFileSystemResource;
677 file_system_resource->filesystem = new FakeHtml5FsFilesystem(
678 *instance_resource->filesystem_template, filesystem_type);
679
680 return CREATE_RESOURCE(core_interface_->resource_manager(),
681 FakeFileSystemResource,
682 file_system_resource);
683 }
684
Open(PP_Resource file_system,int64_t expected_size,PP_CompletionCallback callback)685 int32_t FakeFileSystemInterface::Open(PP_Resource file_system,
686 int64_t expected_size,
687 PP_CompletionCallback callback) {
688 FakeFileSystemResource* file_system_resource =
689 core_interface_->resource_manager()->Get<FakeFileSystemResource>(
690 file_system);
691 if (file_system_resource == NULL)
692 return PP_ERROR_BADRESOURCE;
693
694 file_system_resource->opened = true;
695 return RunCompletionCallback(&callback, PP_OK);
696 }
697
FakePepperInterfaceHtml5Fs()698 FakePepperInterfaceHtml5Fs::FakePepperInterfaceHtml5Fs()
699 : core_interface_(&resource_manager_),
700 var_interface_(&var_manager_),
701 file_system_interface_(&core_interface_),
702 file_ref_interface_(&core_interface_, &var_interface_),
703 file_io_interface_(&core_interface_) {
704 Init();
705 }
706
FakePepperInterfaceHtml5Fs(const FakeHtml5FsFilesystem & filesystem)707 FakePepperInterfaceHtml5Fs::FakePepperInterfaceHtml5Fs(
708 const FakeHtml5FsFilesystem& filesystem)
709 : core_interface_(&resource_manager_),
710 var_interface_(&var_manager_),
711 filesystem_template_(filesystem),
712 file_system_interface_(&core_interface_),
713 file_ref_interface_(&core_interface_, &var_interface_),
714 file_io_interface_(&core_interface_),
715 instance_(0) {
716 Init();
717 }
718
Init()719 void FakePepperInterfaceHtml5Fs::Init() {
720 FakeInstanceResource* instance_resource = new FakeInstanceResource;
721 instance_resource->filesystem_template = &filesystem_template_;
722
723 instance_ = CREATE_RESOURCE(core_interface_.resource_manager(),
724 FakeInstanceResource,
725 instance_resource);
726 }
727
~FakePepperInterfaceHtml5Fs()728 FakePepperInterfaceHtml5Fs::~FakePepperInterfaceHtml5Fs() {
729 core_interface_.ReleaseResource(instance_);
730 }
731
GetCoreInterface()732 nacl_io::CoreInterface* FakePepperInterfaceHtml5Fs::GetCoreInterface() {
733 return &core_interface_;
734 }
735
736 nacl_io::FileSystemInterface*
GetFileSystemInterface()737 FakePepperInterfaceHtml5Fs::GetFileSystemInterface() {
738 return &file_system_interface_;
739 }
740
GetFileRefInterface()741 nacl_io::FileRefInterface* FakePepperInterfaceHtml5Fs::GetFileRefInterface() {
742 return &file_ref_interface_;
743 }
744
GetFileIoInterface()745 nacl_io::FileIoInterface* FakePepperInterfaceHtml5Fs::GetFileIoInterface() {
746 return &file_io_interface_;
747 }
748
GetVarInterface()749 nacl_io::VarInterface* FakePepperInterfaceHtml5Fs::GetVarInterface() {
750 return &var_interface_;
751 }
752