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.
6 /// This example demonstrates the use of persistent file I/O
8 #define __STDC_LIMIT_MACROS
12 #include "ppapi/c/pp_stdint.h"
13 #include "ppapi/c/ppb_file_io.h"
14 #include "ppapi/cpp/file_io.h"
15 #include "ppapi/cpp/file_ref.h"
16 #include "ppapi/cpp/file_system.h"
17 #include "ppapi/cpp/instance.h"
18 #include "ppapi/cpp/message_loop.h"
19 #include "ppapi/cpp/module.h"
20 #include "ppapi/cpp/var.h"
21 #include "ppapi/utility/completion_callback_factory.h"
22 #include "ppapi/utility/threading/simple_thread.h"
25 #define INT32_MAX (0x7FFFFFFF)
33 // Allow 'this' in initializer list
34 #pragma warning(disable : 4355)
38 /// Used for our simple protocol to communicate with Javascript
39 const char* const kLoadPrefix
= "ld";
40 const char* const kSavePrefix
= "sv";
41 const char* const kDeletePrefix
= "de";
44 /// The Instance class. One of these exists for each instance of your NaCl
45 /// module on the web page. The browser will ask the Module object to create
46 /// a new Instance for each occurrence of the <embed> tag that has these
48 /// type="application/x-nacl"
50 class FileIoInstance
: public pp::Instance
{
52 /// The constructor creates the plugin-side instance.
53 /// @param[in] instance the handle to the browser-side plugin instance.
54 explicit FileIoInstance(PP_Instance instance
)
55 : pp::Instance(instance
),
56 callback_factory_(this),
57 file_system_(this, PP_FILESYSTEMTYPE_LOCALPERSISTENT
),
58 file_system_ready_(false),
62 virtual ~FileIoInstance() {
66 virtual bool Init(uint32_t /*argc*/, const char* /*argn*/[],
67 const char* /*argv*/[]) {
69 // Open the file system on the file_thread_. Since this is the first
70 // operation we perform there, and because we do everything on the
71 // file_thread_ synchronously, this ensures that the FileSystem is open
72 // before any FileIO operations execute.
73 file_thread_
.message_loop().PostWork(
74 callback_factory_
.NewCallback(&FileIoInstance::OpenFileSystem
));
79 pp::CompletionCallbackFactory
<FileIoInstance
> callback_factory_
;
80 pp::FileSystem file_system_
;
82 // Indicates whether file_system_ was opened successfully. We only read/write
83 // this on the file_thread_.
84 bool file_system_ready_
;
86 // We do all our file operations on the file_thread_.
87 pp::SimpleThread file_thread_
;
89 /// Handler for messages coming in from the browser via postMessage(). The
90 /// @a var_message can contain anything: a JSON string; a string that encodes
91 /// method names and arguments; etc.
93 /// Here we use messages to communicate with the user interface
95 /// @param[in] var_message The message posted by the browser.
96 virtual void HandleMessage(const pp::Var
& var_message
) {
97 if (!var_message
.is_string())
100 // Parse message into: instruction file_name_length file_name [file_text]
101 std::string message
= var_message
.AsString();
102 std::string instruction
;
103 std::string file_name
;
104 std::stringstream
reader(message
);
105 int file_name_length
;
107 reader
>> instruction
>> file_name_length
;
108 file_name
.resize(file_name_length
);
109 reader
.ignore(1); // Eat the delimiter
110 reader
.read(&file_name
[0], file_name_length
);
112 if (file_name
.length() == 0 || file_name
[0] != '/') {
113 ShowStatusMessage("File name must begin with /");
117 // Dispatch the instruction
118 if (instruction
.compare(kLoadPrefix
) == 0) {
119 file_thread_
.message_loop().PostWork(
120 callback_factory_
.NewCallback(&FileIoInstance::Load
, file_name
));
124 if (instruction
.compare(kSavePrefix
) == 0) {
125 // Read the rest of the message as the file text
126 reader
.ignore(1); // Eat the delimiter
127 std::string file_text
= message
.substr(reader
.tellg());
128 file_thread_
.message_loop().PostWork(
129 callback_factory_
.NewCallback(&FileIoInstance::Save
,
135 if (instruction
.compare(kDeletePrefix
) == 0) {
136 file_thread_
.message_loop().PostWork(
137 callback_factory_
.NewCallback(&FileIoInstance::Delete
, file_name
));
142 void OpenFileSystem(int32_t /* result */) {
143 int32_t rv
= file_system_
.Open(1024*1024, pp::CompletionCallback());
145 file_system_ready_
= true;
146 // Notify the user interface that we're ready
147 PostMessage(pp::Var("READY|"));
149 ShowErrorMessage("Failed to open file system", rv
);
153 void Save(int32_t /* result */, const std::string
& file_name
,
154 const std::string
& file_contents
) {
155 if (!file_system_ready_
) {
156 ShowErrorMessage("File system is not open", PP_ERROR_FAILED
);
159 pp::FileRef
ref(file_system_
, file_name
.c_str());
160 pp::FileIO
file(this);
162 int32_t open_result
= file
.Open(
164 PP_FILEOPENFLAG_WRITE
|PP_FILEOPENFLAG_CREATE
|PP_FILEOPENFLAG_TRUNCATE
,
165 pp::CompletionCallback());
166 if (open_result
!= PP_OK
) {
167 ShowErrorMessage("File open for write failed", open_result
);
171 // We have truncated the file to 0 bytes. So we need only write if
172 // file_contents is non-empty.
173 if (!file_contents
.empty()) {
174 if (file_contents
.length() > INT32_MAX
) {
175 ShowErrorMessage("File too big", PP_ERROR_FILETOOBIG
);
179 int32_t bytes_written
= 0;
181 bytes_written
= file
.Write(offset
,
182 file_contents
.data() + offset
,
183 file_contents
.length(),
184 pp::CompletionCallback());
185 if (bytes_written
> 0) {
186 offset
+= bytes_written
;
188 ShowErrorMessage("File write failed", bytes_written
);
191 } while (bytes_written
< static_cast<int64_t>(file_contents
.length()));
193 // All bytes have been written, flush the write buffer to complete
194 int32_t flush_result
= file
.Flush(pp::CompletionCallback());
195 if (flush_result
!= PP_OK
) {
196 ShowErrorMessage("File fail to flush", flush_result
);
199 ShowStatusMessage("Save successful");
202 void Load(int32_t /* result */, const std::string
& file_name
) {
203 if (!file_system_ready_
) {
204 ShowErrorMessage("File system is not open", PP_ERROR_FAILED
);
207 pp::FileRef
ref(file_system_
, file_name
.c_str());
208 pp::FileIO
file(this);
210 int32_t open_result
= file
.Open(ref
, PP_FILEOPENFLAG_READ
,
211 pp::CompletionCallback());
212 if (open_result
== PP_ERROR_FILENOTFOUND
) {
213 ShowStatusMessage("File not found");
215 } else if (open_result
!= PP_OK
) {
216 ShowErrorMessage("File open for read failed", open_result
);
220 int32_t query_result
= file
.Query(&info
, pp::CompletionCallback());
221 if (query_result
!= PP_OK
) {
222 ShowErrorMessage("File query failed", query_result
);
225 // FileIO.Read() can only handle int32 sizes
226 if (info
.size
> INT32_MAX
) {
227 ShowErrorMessage("File too big", PP_ERROR_FILETOOBIG
);
231 std::vector
<char> data(info
.size
);
233 int32_t bytes_read
= 0;
235 bytes_read
= file
.Read(offset
, &data
[offset
], data
.size() - offset
,
236 pp::CompletionCallback());
238 offset
+= bytes_read
;
239 } while (bytes_read
> 0);
240 // If bytes_read < PP_OK then it indicates the error code.
241 if (bytes_read
< PP_OK
) {
242 ShowErrorMessage("File read failed", bytes_read
);
245 PP_DCHECK(bytes_read
== 0);
246 // Done reading, send content to the user interface
247 std::string
string_data(data
.begin(), data
.end());
248 PostMessage(pp::Var("DISP|" + string_data
));
249 ShowStatusMessage("Load complete");
252 void Delete(int32_t /* result */, const std::string
& file_name
) {
253 if (!file_system_ready_
) {
254 ShowErrorMessage("File system is not open", PP_ERROR_FAILED
);
257 pp::FileRef
ref(file_system_
, file_name
.c_str());
259 int32_t result
= ref
.Delete(pp::CompletionCallback());
260 if (result
== PP_ERROR_FILENOTFOUND
) {
261 ShowStatusMessage("File not found");
263 } else if (result
!= PP_OK
) {
264 ShowErrorMessage("Deletion failed", result
);
267 ShowStatusMessage("File deleted");
270 /// Encapsulates our simple javascript communication protocol
271 void ShowErrorMessage(const std::string
& message
, int32_t result
) {
272 std::stringstream ss
;
273 ss
<< "ERR|" << message
<< " -- Error #: " << result
;
274 PostMessage(pp::Var(ss
.str()));
277 /// Encapsulates our simple javascript communication protocol
278 void ShowStatusMessage(const std::string
& message
) {
279 std::stringstream ss
;
280 ss
<< "STAT|" << message
;
281 PostMessage(pp::Var(ss
.str()));
285 /// The Module class. The browser calls the CreateInstance() method to create
286 /// an instance of your NaCl module on the web page. The browser creates a new
287 /// instance for each <embed> tag with type="application/x-nacl".
288 class FileIoModule
: public pp::Module
{
290 FileIoModule() : pp::Module() {}
291 virtual ~FileIoModule() {}
293 /// Create and return a FileIoInstance object.
294 /// @param[in] instance The browser-side instance.
295 /// @return the plugin-side instance.
296 virtual pp::Instance
* CreateInstance(PP_Instance instance
) {
297 return new FileIoInstance(instance
);
302 /// Factory function called by the browser when the module is first loaded.
303 /// The browser keeps a singleton of this module. It calls the
304 /// CreateInstance() method on the object you return to make instances. There
305 /// is one instance per <embed> tag on the page. This is the main binding
306 /// point for your NaCl module with the browser.
307 Module
* CreateModule() {
308 return new FileIoModule();