Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / native_client_sdk / src / examples / api / file_io / file_io.cc
blobae8df8523b435ecbb9e7d6419c9b77c559d59480
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.
5 /// @file file_io.cc
6 /// This example demonstrates the use of persistent file I/O
8 #define __STDC_LIMIT_MACROS
9 #include <sstream>
10 #include <string>
12 #include "ppapi/c/pp_stdint.h"
13 #include "ppapi/c/ppb_file_io.h"
14 #include "ppapi/cpp/directory_entry.h"
15 #include "ppapi/cpp/file_io.h"
16 #include "ppapi/cpp/file_ref.h"
17 #include "ppapi/cpp/file_system.h"
18 #include "ppapi/cpp/instance.h"
19 #include "ppapi/cpp/message_loop.h"
20 #include "ppapi/cpp/module.h"
21 #include "ppapi/cpp/var.h"
22 #include "ppapi/utility/completion_callback_factory.h"
23 #include "ppapi/utility/threading/simple_thread.h"
25 #ifndef INT32_MAX
26 #define INT32_MAX (0x7FFFFFFF)
27 #endif
29 #ifdef WIN32
30 #undef min
31 #undef max
32 #undef PostMessage
34 // Allow 'this' in initializer list
35 #pragma warning(disable : 4355)
36 #endif
38 namespace {
39 /// Used for our simple protocol to communicate with Javascript
40 const char* const kLoadPrefix = "ld";
41 const char* const kSavePrefix = "sv";
42 const char* const kDeletePrefix = "de";
43 const char* const kListPrefix = "ls";
44 const char* const kMakeDirPrefix = "md";
47 /// The Instance class. One of these exists for each instance of your NaCl
48 /// module on the web page. The browser will ask the Module object to create
49 /// a new Instance for each occurrence of the <embed> tag that has these
50 /// attributes:
51 /// type="application/x-nacl"
52 /// src="file_io.nmf"
53 class FileIoInstance : public pp::Instance {
54 public:
55 /// The constructor creates the plugin-side instance.
56 /// @param[in] instance the handle to the browser-side plugin instance.
57 explicit FileIoInstance(PP_Instance instance)
58 : pp::Instance(instance),
59 callback_factory_(this),
60 file_system_(this, PP_FILESYSTEMTYPE_LOCALPERSISTENT),
61 file_system_ready_(false),
62 file_thread_(this) {}
64 virtual ~FileIoInstance() { file_thread_.Join(); }
66 virtual bool Init(uint32_t /*argc*/,
67 const char * /*argn*/ [],
68 const char * /*argv*/ []) {
69 file_thread_.Start();
70 // Open the file system on the file_thread_. Since this is the first
71 // operation we perform there, and because we do everything on the
72 // file_thread_ synchronously, this ensures that the FileSystem is open
73 // before any FileIO operations execute.
74 file_thread_.message_loop().PostWork(
75 callback_factory_.NewCallback(&FileIoInstance::OpenFileSystem));
76 return true;
79 private:
80 pp::CompletionCallbackFactory<FileIoInstance> callback_factory_;
81 pp::FileSystem file_system_;
83 // Indicates whether file_system_ was opened successfully. We only read/write
84 // this on the file_thread_.
85 bool file_system_ready_;
87 // We do all our file operations on the file_thread_.
88 pp::SimpleThread file_thread_;
90 /// Handler for messages coming in from the browser via postMessage(). The
91 /// @a var_message can contain anything: a JSON string; a string that encodes
92 /// method names and arguments; etc.
93 ///
94 /// Here we use messages to communicate with the user interface
95 ///
96 /// @param[in] var_message The message posted by the browser.
97 virtual void HandleMessage(const pp::Var& var_message) {
98 if (!var_message.is_string())
99 return;
101 // Parse message into: instruction file_name_length file_name [file_text]
102 std::string message = var_message.AsString();
103 std::string instruction;
104 std::string file_name;
105 std::stringstream reader(message);
106 int file_name_length;
108 reader >> instruction >> file_name_length;
109 file_name.resize(file_name_length);
110 reader.ignore(1); // Eat the delimiter
111 reader.read(&file_name[0], file_name_length);
113 if (file_name.length() == 0 || file_name[0] != '/') {
114 ShowStatusMessage("File name must begin with /");
115 return;
118 // Dispatch the instruction
119 if (instruction == kLoadPrefix) {
120 file_thread_.message_loop().PostWork(
121 callback_factory_.NewCallback(&FileIoInstance::Load, file_name));
122 } else if (instruction == kSavePrefix) {
123 // Read the rest of the message as the file text
124 reader.ignore(1); // Eat the delimiter
125 std::string file_text = message.substr(reader.tellg());
126 file_thread_.message_loop().PostWork(callback_factory_.NewCallback(
127 &FileIoInstance::Save, file_name, file_text));
128 } else if (instruction == kDeletePrefix) {
129 file_thread_.message_loop().PostWork(
130 callback_factory_.NewCallback(&FileIoInstance::Delete, file_name));
131 } else if (instruction == kListPrefix) {
132 const std::string& dir_name = file_name;
133 file_thread_.message_loop().PostWork(
134 callback_factory_.NewCallback(&FileIoInstance::List, dir_name));
135 } else if (instruction == kMakeDirPrefix) {
136 const std::string& dir_name = file_name;
137 file_thread_.message_loop().PostWork(
138 callback_factory_.NewCallback(&FileIoInstance::MakeDir, dir_name));
142 void OpenFileSystem(int32_t /* result */) {
143 int32_t rv = file_system_.Open(1024 * 1024, pp::BlockUntilComplete());
144 if (rv == PP_OK) {
145 file_system_ready_ = true;
146 // Notify the user interface that we're ready
147 PostMessage("READY|");
148 } else {
149 ShowErrorMessage("Failed to open file system", rv);
153 void Save(int32_t /* result */,
154 const std::string& file_name,
155 const std::string& file_contents) {
156 if (!file_system_ready_) {
157 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
158 return;
160 pp::FileRef ref(file_system_, file_name.c_str());
161 pp::FileIO file(this);
163 int32_t open_result =
164 file.Open(ref,
165 PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE |
166 PP_FILEOPENFLAG_TRUNCATE,
167 pp::BlockUntilComplete());
168 if (open_result != PP_OK) {
169 ShowErrorMessage("File open for write failed", open_result);
170 return;
173 // We have truncated the file to 0 bytes. So we need only write if
174 // file_contents is non-empty.
175 if (!file_contents.empty()) {
176 if (file_contents.length() > INT32_MAX) {
177 ShowErrorMessage("File too big", PP_ERROR_FILETOOBIG);
178 return;
180 int64_t offset = 0;
181 int32_t bytes_written = 0;
182 do {
183 bytes_written = file.Write(offset,
184 file_contents.data() + offset,
185 file_contents.length(),
186 pp::BlockUntilComplete());
187 if (bytes_written > 0) {
188 offset += bytes_written;
189 } else {
190 ShowErrorMessage("File write failed", bytes_written);
191 return;
193 } while (bytes_written < static_cast<int64_t>(file_contents.length()));
195 // All bytes have been written, flush the write buffer to complete
196 int32_t flush_result = file.Flush(pp::BlockUntilComplete());
197 if (flush_result != PP_OK) {
198 ShowErrorMessage("File fail to flush", flush_result);
199 return;
201 ShowStatusMessage("Save success");
204 void Load(int32_t /* result */, const std::string& file_name) {
205 if (!file_system_ready_) {
206 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
207 return;
209 pp::FileRef ref(file_system_, file_name.c_str());
210 pp::FileIO file(this);
212 int32_t open_result =
213 file.Open(ref, PP_FILEOPENFLAG_READ, pp::BlockUntilComplete());
214 if (open_result == PP_ERROR_FILENOTFOUND) {
215 ShowErrorMessage("File not found", open_result);
216 return;
217 } else if (open_result != PP_OK) {
218 ShowErrorMessage("File open for read failed", open_result);
219 return;
221 PP_FileInfo info;
222 int32_t query_result = file.Query(&info, pp::BlockUntilComplete());
223 if (query_result != PP_OK) {
224 ShowErrorMessage("File query failed", query_result);
225 return;
227 // FileIO.Read() can only handle int32 sizes
228 if (info.size > INT32_MAX) {
229 ShowErrorMessage("File too big", PP_ERROR_FILETOOBIG);
230 return;
233 std::vector<char> data(info.size);
234 int64_t offset = 0;
235 int32_t bytes_read = 0;
236 int32_t bytes_to_read = info.size;
237 while (bytes_to_read > 0) {
238 bytes_read = file.Read(offset,
239 &data[offset],
240 data.size() - offset,
241 pp::BlockUntilComplete());
242 if (bytes_read > 0) {
243 offset += bytes_read;
244 bytes_to_read -= bytes_read;
245 } else if (bytes_read < 0) {
246 // If bytes_read < PP_OK then it indicates the error code.
247 ShowErrorMessage("File read failed", bytes_read);
248 return;
251 // Done reading, send content to the user interface
252 std::string string_data(data.begin(), data.end());
253 PostMessage("DISP|" + string_data);
254 ShowStatusMessage("Load success");
257 void Delete(int32_t /* result */, const std::string& file_name) {
258 if (!file_system_ready_) {
259 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
260 return;
262 pp::FileRef ref(file_system_, file_name.c_str());
264 int32_t result = ref.Delete(pp::BlockUntilComplete());
265 if (result == PP_ERROR_FILENOTFOUND) {
266 ShowStatusMessage("File/Directory not found");
267 return;
268 } else if (result != PP_OK) {
269 ShowErrorMessage("Deletion failed", result);
270 return;
272 ShowStatusMessage("Delete success");
275 void List(int32_t /* result */, const std::string& dir_name) {
276 if (!file_system_ready_) {
277 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
278 return;
281 pp::FileRef ref(file_system_, dir_name.c_str());
283 // Pass ref along to keep it alive.
284 ref.ReadDirectoryEntries(callback_factory_.NewCallbackWithOutput(
285 &FileIoInstance::ListCallback, ref));
288 void ListCallback(int32_t result,
289 const std::vector<pp::DirectoryEntry>& entries,
290 pp::FileRef /* unused_ref */) {
291 if (result != PP_OK) {
292 ShowErrorMessage("List failed", result);
293 return;
296 std::stringstream ss;
297 ss << "LIST";
298 for (size_t i = 0; i < entries.size(); ++i) {
299 pp::Var name = entries[i].file_ref().GetName();
300 if (name.is_string()) {
301 ss << "|" << name.AsString();
304 PostMessage(ss.str());
305 ShowStatusMessage("List success");
308 void MakeDir(int32_t /* result */, const std::string& dir_name) {
309 if (!file_system_ready_) {
310 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
311 return;
313 pp::FileRef ref(file_system_, dir_name.c_str());
315 int32_t result = ref.MakeDirectory(
316 PP_MAKEDIRECTORYFLAG_NONE, pp::BlockUntilComplete());
317 if (result != PP_OK) {
318 ShowErrorMessage("Make directory failed", result);
319 return;
321 ShowStatusMessage("Make directory success");
324 /// Encapsulates our simple javascript communication protocol
325 void ShowErrorMessage(const std::string& message, int32_t result) {
326 std::stringstream ss;
327 ss << "ERR|" << message << " -- Error #: " << result;
328 PostMessage(ss.str());
331 /// Encapsulates our simple javascript communication protocol
332 void ShowStatusMessage(const std::string& message) {
333 std::stringstream ss;
334 ss << "STAT|" << message;
335 PostMessage(ss.str());
339 /// The Module class. The browser calls the CreateInstance() method to create
340 /// an instance of your NaCl module on the web page. The browser creates a new
341 /// instance for each <embed> tag with type="application/x-nacl".
342 class FileIoModule : public pp::Module {
343 public:
344 FileIoModule() : pp::Module() {}
345 virtual ~FileIoModule() {}
347 /// Create and return a FileIoInstance object.
348 /// @param[in] instance The browser-side instance.
349 /// @return the plugin-side instance.
350 virtual pp::Instance* CreateInstance(PP_Instance instance) {
351 return new FileIoInstance(instance);
355 namespace pp {
356 /// Factory function called by the browser when the module is first loaded.
357 /// The browser keeps a singleton of this module. It calls the
358 /// CreateInstance() method on the object you return to make instances. There
359 /// is one instance per <embed> tag on the page. This is the main binding
360 /// point for your NaCl module with the browser.
361 Module* CreateModule() { return new FileIoModule(); }
362 } // namespace pp