1 // Copyright (c) 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.
5 #include "fake_pepper_interface_html5fs.h"
11 #include <ppapi/c/pp_completion_callback.h>
12 #include <ppapi/c/pp_errors.h>
14 #include "gtest/gtest.h"
18 class FakeInstanceResource : public FakeResource {
20 FakeInstanceResource() : filesystem_template(NULL) {}
21 static const char* classname() { return "FakeInstanceResource"; }
23 FakeHtml5FsFilesystem* filesystem_template; // Weak reference.
26 class FakeFileSystemResource : public FakeResource {
28 FakeFileSystemResource() : filesystem(NULL), opened(false) {}
29 ~FakeFileSystemResource() { delete filesystem; }
30 static const char* classname() { return "FakeFileSystemResource"; }
32 FakeHtml5FsFilesystem* filesystem; // Owned.
36 class FakeFileRefResource : public FakeResource {
38 FakeFileRefResource() : filesystem(NULL) {}
39 static const char* classname() { return "FakeFileRefResource"; }
41 FakeHtml5FsFilesystem* filesystem; // Weak reference.
42 FakeHtml5FsFilesystem::Path path;
45 class FakeFileIoResource : public FakeResource {
47 FakeFileIoResource() : node(NULL), open_flags(0) {}
48 static const char* classname() { return "FakeFileIoResource"; }
50 FakeHtml5FsNode* node; // Weak reference.
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
59 // if (<some error condition>)
60 // return RunCompletionCallback(callback, PP_ERROR_FUBAR);
62 // /* Everything worked OK */
63 // return RunCompletionCallback(callback, PP_OK);
64 int32_t RunCompletionCallback(PP_CompletionCallback* callback, int32_t result) {
66 PP_RunCompletionCallback(callback, result);
67 return PP_OK_COMPLETIONPENDING;
74 FakeHtml5FsNode::FakeHtml5FsNode(const PP_FileInfo& info) : info_(info) {}
76 FakeHtml5FsNode::FakeHtml5FsNode(const PP_FileInfo& info,
77 const std::vector<uint8_t>& contents)
78 : info_(info), contents_(contents) {}
80 FakeHtml5FsNode::FakeHtml5FsNode(const PP_FileInfo& info,
81 const std::string& contents)
83 std::copy(contents.begin(), contents.end(), std::back_inserter(contents_));
86 int32_t FakeHtml5FsNode::Read(int64_t offset,
88 int32_t bytes_to_read) {
90 return PP_ERROR_FAILED;
93 std::max(0, std::min<int32_t>(bytes_to_read, contents_.size() - offset));
94 memcpy(buffer, contents_.data() + offset, bytes_to_read);
98 int32_t FakeHtml5FsNode::Write(int64_t offset,
100 int32_t bytes_to_write) {
102 return PP_ERROR_FAILED;
104 size_t new_size = offset + bytes_to_write;
105 if (new_size > contents_.size())
106 contents_.resize(new_size);
108 memcpy(contents_.data() + offset, buffer, bytes_to_write);
109 info_.size = new_size;
110 return 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);
117 int32_t FakeHtml5FsNode::SetLength(int64_t length) {
118 contents_.resize(length);
123 void FakeHtml5FsNode::GetInfo(PP_FileInfo* out_info) { *out_info = info_; }
125 bool FakeHtml5FsNode::IsRegular() const {
126 return info_.type == PP_FILETYPE_REGULAR;
129 bool FakeHtml5FsNode::IsDirectory() const {
130 return info_.type == PP_FILETYPE_DIRECTORY;
133 FakeHtml5FsFilesystem::FakeHtml5FsFilesystem()
134 : filesystem_type_(PP_FILESYSTEMTYPE_INVALID) {
138 FakeHtml5FsFilesystem::FakeHtml5FsFilesystem(PP_FileSystemType type)
139 : filesystem_type_(type) {
143 FakeHtml5FsFilesystem::FakeHtml5FsFilesystem(
144 const FakeHtml5FsFilesystem& filesystem,
145 PP_FileSystemType type)
146 : node_map_(filesystem.node_map_), filesystem_type_(type) {}
148 void FakeHtml5FsFilesystem::Clear() {
150 // Always have a root node.
151 AddDirectory("/", NULL);
154 bool FakeHtml5FsFilesystem::AddEmptyFile(const Path& path,
155 FakeHtml5FsNode** out_node) {
156 return AddFile(path, std::vector<uint8_t>(), 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);
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()) {
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;
185 FakeHtml5FsNode node(info, contents);
186 std::pair<NodeMap::iterator, bool> result =
187 node_map_.insert(NodeMap::value_type(path, node));
189 EXPECT_EQ(true, result.second);
191 *out_node = &result.first->second;
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()) {
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;
212 FakeHtml5FsNode node(info);
213 std::pair<NodeMap::iterator, bool> result =
214 node_map_.insert(NodeMap::value_type(path, node));
216 EXPECT_EQ(true, result.second);
218 *out_node = &result.first->second;
222 bool FakeHtml5FsFilesystem::RemoveNode(const Path& path) {
223 return node_map_.erase(path) >= 1;
226 FakeHtml5FsNode* FakeHtml5FsFilesystem::GetNode(const Path& path) {
227 NodeMap::iterator iter = node_map_.find(path);
228 if (iter == node_map_.end())
230 return &iter->second;
233 bool FakeHtml5FsFilesystem::GetDirectoryEntries(
235 DirectoryEntries* out_dir_entries) const {
236 out_dir_entries->clear();
238 NodeMap::const_iterator iter = node_map_.find(path);
239 if (iter == node_map_.end())
242 const FakeHtml5FsNode& dir_node = iter->second;
243 if (!dir_node.IsDirectory())
246 for (NodeMap::const_iterator iter = node_map_.begin();
247 iter != node_map_.end();
249 const Path& node_path = iter->first;
250 if (node_path.find(path) == std::string::npos)
253 // A node is not a child of itself.
254 if (&iter->second == &dir_node)
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)
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);
268 DirectoryEntry entry;
269 entry.path = entry_path;
270 entry.node = &iter->second;
271 out_dir_entries->push_back(entry);
278 FakeHtml5FsFilesystem::Path FakeHtml5FsFilesystem::GetParentPath(
280 size_t last_slash = path.rfind('/');
284 EXPECT_EQ(std::string::npos, last_slash);
285 return path.substr(0, last_slash);
288 FakeFileIoInterface::FakeFileIoInterface(FakeCoreInterface* core_interface)
289 : core_interface_(core_interface) {}
291 PP_Resource FakeFileIoInterface::Create(PP_Resource) {
292 return CREATE_RESOURCE(core_interface_->resource_manager(),
294 new FakeFileIoResource);
297 int32_t FakeFileIoInterface::Open(PP_Resource file_io,
298 PP_Resource file_ref,
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;
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);
312 if ((flag_append && flag_write) || (flag_truncate && !flag_write))
313 return PP_ERROR_BADARGUMENT;
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;
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;
327 return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
329 bool result = filesystem->AddEmptyFile(path, &node);
330 EXPECT_EQ(true, result);
332 if (flag_create && flag_exclusive)
333 return RunCompletionCallback(&callback, PP_ERROR_FILEEXISTS);
336 file_io_resource->node = node;
337 file_io_resource->open_flags = open_flags;
340 return RunCompletionCallback(&callback, node->SetLength(0));
342 return RunCompletionCallback(&callback, PP_OK);
345 int32_t FakeFileIoInterface::Query(PP_Resource file_io,
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;
353 if (!file_io_resource->node)
354 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
356 file_io_resource->node->GetInfo(info);
357 return RunCompletionCallback(&callback, PP_OK);
360 int32_t FakeFileIoInterface::Read(PP_Resource file_io,
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;
370 if (bytes_to_read < 0)
371 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
373 if ((file_io_resource->open_flags & PP_FILEOPENFLAG_READ) !=
374 PP_FILEOPENFLAG_READ) {
375 return RunCompletionCallback(&callback, PP_ERROR_NOACCESS);
378 if (!file_io_resource->node)
379 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
381 int32_t result = file_io_resource->node->Read(offset, buffer, bytes_to_read);
382 return RunCompletionCallback(&callback, result);
385 int32_t FakeFileIoInterface::Write(PP_Resource file_io,
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;
395 if ((file_io_resource->open_flags & PP_FILEOPENFLAG_WRITE) !=
396 PP_FILEOPENFLAG_WRITE) {
397 return RunCompletionCallback(&callback, PP_ERROR_NOACCESS);
400 if (!file_io_resource->node)
401 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
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);
408 result = file_io_resource->node->Write(offset, buffer, bytes_to_write);
411 return RunCompletionCallback(&callback, result);
414 int32_t FakeFileIoInterface::SetLength(PP_Resource file_io,
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;
422 if ((file_io_resource->open_flags & PP_FILEOPENFLAG_WRITE) !=
423 PP_FILEOPENFLAG_WRITE) {
424 return RunCompletionCallback(&callback, PP_ERROR_NOACCESS);
427 if (!file_io_resource->node)
428 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
430 int32_t result = file_io_resource->node->SetLength(length);
431 return RunCompletionCallback(&callback, result);
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;
441 if (!file_io_resource->node)
442 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
444 return RunCompletionCallback(&callback, PP_OK);
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)
453 file_io_resource->node = NULL;
454 file_io_resource->open_flags = 0;
457 FakeFileRefInterface::FakeFileRefInterface(FakeCoreInterface* core_interface,
458 FakeVarInterface* var_interface)
459 : core_interface_(core_interface), var_interface_(var_interface) {}
461 PP_Resource FakeFileRefInterface::Create(PP_Resource file_system,
463 FakeFileSystemResource* file_system_resource =
464 core_interface_->resource_manager()->Get<FakeFileSystemResource>(
466 if (file_system_resource == NULL)
467 return PP_ERROR_BADRESOURCE;
469 if (!file_system_resource->opened)
470 return PP_ERROR_FAILED;
473 return PP_ERROR_FAILED;
475 size_t path_len = strlen(path);
477 return PP_ERROR_FAILED;
479 FakeFileRefResource* file_ref_resource = new FakeFileRefResource;
480 file_ref_resource->filesystem = file_system_resource->filesystem;
481 file_ref_resource->path = path;
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);
487 return CREATE_RESOURCE(core_interface_->resource_manager(),
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();
498 return var_interface_->VarFromUtf8(file_ref_resource->path.c_str(),
499 file_ref_resource->path.size());
502 int32_t FakeFileRefInterface::MakeDirectory(PP_Resource directory_ref,
503 PP_Bool make_ancestors,
504 PP_CompletionCallback callback) {
505 FakeFileRefResource* directory_ref_resource =
506 core_interface_->resource_manager()->Get<FakeFileRefResource>(
508 if (directory_ref_resource == NULL)
509 return PP_ERROR_BADRESOURCE;
511 // TODO(binji): We don't currently use make_ancestors==PP_TRUE in nacl_io, so
512 // I won't bother implementing it.
513 if (make_ancestors == PP_TRUE)
514 return PP_ERROR_FAILED;
516 FakeHtml5FsFilesystem* filesystem = directory_ref_resource->filesystem;
517 FakeHtml5FsFilesystem::Path path = directory_ref_resource->path;
519 // Pepper returns PP_ERROR_NOACCESS when trying to create the root directory,
520 // not PP_ERROR_FILEEXISTS, as you might expect.
522 return RunCompletionCallback(&callback, PP_ERROR_NOACCESS);
524 FakeHtml5FsNode* node = filesystem->GetNode(path);
526 return RunCompletionCallback(&callback, PP_ERROR_FILEEXISTS);
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);
533 if (!parent_node->IsDirectory())
534 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
536 bool result = filesystem->AddDirectory(directory_ref_resource->path, NULL);
537 EXPECT_EQ(true, result);
538 return RunCompletionCallback(&callback, PP_OK);
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;
548 FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem;
549 FakeHtml5FsFilesystem::Path path = file_ref_resource->path;
550 FakeHtml5FsNode* node = filesystem->GetNode(path);
552 return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
554 filesystem->RemoveNode(path);
555 return RunCompletionCallback(&callback, PP_OK);
558 int32_t FakeFileRefInterface::Query(PP_Resource file_ref,
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;
566 FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem;
567 FakeHtml5FsFilesystem::Path path = file_ref_resource->path;
568 FakeHtml5FsNode* node = filesystem->GetNode(path);
570 return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
573 return RunCompletionCallback(&callback, PP_OK);
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>(
583 if (directory_ref_resource == NULL)
584 return PP_ERROR_BADRESOURCE;
586 FakeHtml5FsFilesystem* filesystem = directory_ref_resource->filesystem;
587 FakeHtml5FsFilesystem::Path path = directory_ref_resource->path;
588 FakeHtml5FsNode* node = filesystem->GetNode(path);
590 return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND);
592 if (!node->IsDirectory())
593 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
595 FakeHtml5FsFilesystem::DirectoryEntries fake_dir_entries;
596 filesystem->GetDirectoryEntries(path, &fake_dir_entries);
598 uint32_t element_count = fake_dir_entries.size();
599 uint32_t element_size = sizeof(fake_dir_entries[0]);
601 (*output.GetDataBuffer)(output.user_data, element_count, element_size);
603 if (data_buffer == NULL)
604 return RunCompletionCallback(&callback, PP_ERROR_FAILED);
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 =
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(),
618 dir_entries[i].file_ref = file_ref;
619 dir_entries[i].file_type = fake_dir_entry.node->file_type();
622 return RunCompletionCallback(&callback, PP_OK);
625 FakeFileSystemInterface::FakeFileSystemInterface(
626 FakeCoreInterface* core_interface)
627 : core_interface_(core_interface) {}
629 PP_Resource FakeFileSystemInterface::Create(PP_Instance instance,
630 PP_FileSystemType filesystem_type) {
631 FakeInstanceResource* instance_resource =
632 core_interface_->resource_manager()->Get<FakeInstanceResource>(instance);
633 if (instance_resource == NULL)
634 return PP_ERROR_BADRESOURCE;
636 FakeFileSystemResource* file_system_resource = new FakeFileSystemResource;
637 file_system_resource->filesystem = new FakeHtml5FsFilesystem(
638 *instance_resource->filesystem_template, filesystem_type);
640 return CREATE_RESOURCE(core_interface_->resource_manager(),
641 FakeFileSystemResource,
642 file_system_resource);
645 int32_t FakeFileSystemInterface::Open(PP_Resource file_system,
646 int64_t expected_size,
647 PP_CompletionCallback callback) {
648 FakeFileSystemResource* file_system_resource =
649 core_interface_->resource_manager()->Get<FakeFileSystemResource>(
651 if (file_system_resource == NULL)
652 return PP_ERROR_BADRESOURCE;
654 file_system_resource->opened = true;
655 return RunCompletionCallback(&callback, PP_OK);
658 FakePepperInterfaceHtml5Fs::FakePepperInterfaceHtml5Fs()
659 : file_system_interface_(&core_interface_),
660 file_ref_interface_(&core_interface_, &var_interface_),
661 file_io_interface_(&core_interface_) {
665 FakePepperInterfaceHtml5Fs::FakePepperInterfaceHtml5Fs(
666 const FakeHtml5FsFilesystem& filesystem)
667 : filesystem_template_(filesystem),
668 file_system_interface_(&core_interface_),
669 file_ref_interface_(&core_interface_, &var_interface_),
670 file_io_interface_(&core_interface_),
675 void FakePepperInterfaceHtml5Fs::Init() {
676 FakeInstanceResource* instance_resource = new FakeInstanceResource;
677 instance_resource->filesystem_template = &filesystem_template_;
679 instance_ = CREATE_RESOURCE(core_interface_.resource_manager(),
680 FakeInstanceResource,
684 FakePepperInterfaceHtml5Fs::~FakePepperInterfaceHtml5Fs() {
685 core_interface_.ReleaseResource(instance_);
688 nacl_io::CoreInterface* FakePepperInterfaceHtml5Fs::GetCoreInterface() {
689 return &core_interface_;
692 nacl_io::FileSystemInterface*
693 FakePepperInterfaceHtml5Fs::GetFileSystemInterface() {
694 return &file_system_interface_;
697 nacl_io::FileRefInterface* FakePepperInterfaceHtml5Fs::GetFileRefInterface() {
698 return &file_ref_interface_;
701 nacl_io::FileIoInterface* FakePepperInterfaceHtml5Fs::GetFileIoInterface() {
702 return &file_io_interface_;
705 nacl_io::VarInterface* FakePepperInterfaceHtml5Fs::GetVarInterface() {
706 return &var_interface_;