Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / native_client_sdk / src / examples / api / file_io / file_io.cc
blob2f3eaaeff1d38012e90857816aca5c62816c0dbc
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 <stdio.h>
11 #include <sstream>
12 #include <string>
13 #include <vector>
15 #include "ppapi/c/pp_stdint.h"
16 #include "ppapi/c/ppb_file_io.h"
17 #include "ppapi/cpp/directory_entry.h"
18 #include "ppapi/cpp/file_io.h"
19 #include "ppapi/cpp/file_ref.h"
20 #include "ppapi/cpp/file_system.h"
21 #include "ppapi/cpp/instance.h"
22 #include "ppapi/cpp/message_loop.h"
23 #include "ppapi/cpp/module.h"
24 #include "ppapi/cpp/var.h"
25 #include "ppapi/cpp/var_array.h"
26 #include "ppapi/utility/completion_callback_factory.h"
27 #include "ppapi/utility/threading/simple_thread.h"
29 #ifndef INT32_MAX
30 #define INT32_MAX (0x7FFFFFFF)
31 #endif
33 #ifdef WIN32
34 #undef min
35 #undef max
36 #undef PostMessage
38 // Allow 'this' in initializer list
39 #pragma warning(disable : 4355)
40 #endif
42 namespace {
43 typedef std::vector<std::string> StringVector;
46 /// The Instance class. One of these exists for each instance of your NaCl
47 /// module on the web page. The browser will ask the Module object to create
48 /// a new Instance for each occurrence of the <embed> tag that has these
49 /// attributes:
50 /// type="application/x-nacl"
51 /// src="file_io.nmf"
52 class FileIoInstance : public pp::Instance {
53 public:
54 /// The constructor creates the plugin-side instance.
55 /// @param[in] instance the handle to the browser-side plugin instance.
56 explicit FileIoInstance(PP_Instance instance)
57 : pp::Instance(instance),
58 callback_factory_(this),
59 file_system_(this, PP_FILESYSTEMTYPE_LOCALPERSISTENT),
60 file_system_ready_(false),
61 file_thread_(this) {}
63 virtual ~FileIoInstance() { file_thread_.Join(); }
65 virtual bool Init(uint32_t /*argc*/,
66 const char * /*argn*/ [],
67 const char * /*argv*/ []) {
68 file_thread_.Start();
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));
75 return true;
78 private:
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 void PostArrayMessage(const char* command, const StringVector& strings) {
90 pp::VarArray message;
91 message.Set(0, command);
92 for (size_t i = 0; i < strings.size(); ++i) {
93 message.Set(i + 1, strings[i]);
96 PostMessage(message);
99 void PostArrayMessage(const char* command) {
100 PostArrayMessage(command, StringVector());
103 void PostArrayMessage(const char* command, std::string s) {
104 StringVector sv;
105 sv.push_back(s);
106 PostArrayMessage(command, sv);
109 /// Handler for messages coming in from the browser via postMessage(). The
110 /// @a var_message can contain anything: a JSON string; a string that encodes
111 /// method names and arguments; etc.
113 /// Here we use messages to communicate with the user interface
115 /// @param[in] var_message The message posted by the browser.
116 virtual void HandleMessage(const pp::Var& var_message) {
117 if (!var_message.is_array())
118 return;
120 // Message should be an array with the following elements:
121 // [command, path, extra args]
122 pp::VarArray message(var_message);
123 std::string command = message.Get(0).AsString();
124 std::string file_name = message.Get(1).AsString();
126 if (file_name.length() == 0 || file_name[0] != '/') {
127 ShowStatusMessage("File name must begin with /");
128 return;
131 printf("command: %s file_name: %s\n", command.c_str(), file_name.c_str());
133 if (command == "load") {
134 file_thread_.message_loop().PostWork(
135 callback_factory_.NewCallback(&FileIoInstance::Load, file_name));
136 } else if (command == "save") {
137 std::string file_text = message.Get(2).AsString();
138 file_thread_.message_loop().PostWork(callback_factory_.NewCallback(
139 &FileIoInstance::Save, file_name, file_text));
140 } else if (command == "delete") {
141 file_thread_.message_loop().PostWork(
142 callback_factory_.NewCallback(&FileIoInstance::Delete, file_name));
143 } else if (command == "list") {
144 const std::string& dir_name = file_name;
145 file_thread_.message_loop().PostWork(
146 callback_factory_.NewCallback(&FileIoInstance::List, dir_name));
147 } else if (command == "makedir") {
148 const std::string& dir_name = file_name;
149 file_thread_.message_loop().PostWork(
150 callback_factory_.NewCallback(&FileIoInstance::MakeDir, dir_name));
151 } else if (command == "rename") {
152 const std::string new_name = message.Get(2).AsString();
153 file_thread_.message_loop().PostWork(callback_factory_.NewCallback(
154 &FileIoInstance::Rename, file_name, new_name));
158 void OpenFileSystem(int32_t /* result */) {
159 int32_t rv = file_system_.Open(1024 * 1024, pp::BlockUntilComplete());
160 if (rv == PP_OK) {
161 file_system_ready_ = true;
162 // Notify the user interface that we're ready
163 PostArrayMessage("READY");
164 } else {
165 ShowErrorMessage("Failed to open file system", rv);
169 void Save(int32_t /* result */,
170 const std::string& file_name,
171 const std::string& file_contents) {
172 if (!file_system_ready_) {
173 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
174 return;
176 pp::FileRef ref(file_system_, file_name.c_str());
177 pp::FileIO file(this);
179 int32_t open_result =
180 file.Open(ref,
181 PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE |
182 PP_FILEOPENFLAG_TRUNCATE,
183 pp::BlockUntilComplete());
184 if (open_result != PP_OK) {
185 ShowErrorMessage("File open for write failed", open_result);
186 return;
189 // We have truncated the file to 0 bytes. So we need only write if
190 // file_contents is non-empty.
191 if (!file_contents.empty()) {
192 if (file_contents.length() > INT32_MAX) {
193 ShowErrorMessage("File too big", PP_ERROR_FILETOOBIG);
194 return;
196 int64_t offset = 0;
197 int32_t bytes_written = 0;
198 do {
199 bytes_written = file.Write(offset,
200 file_contents.data() + offset,
201 file_contents.length(),
202 pp::BlockUntilComplete());
203 if (bytes_written > 0) {
204 offset += bytes_written;
205 } else {
206 ShowErrorMessage("File write failed", bytes_written);
207 return;
209 } while (bytes_written < static_cast<int64_t>(file_contents.length()));
211 // All bytes have been written, flush the write buffer to complete
212 int32_t flush_result = file.Flush(pp::BlockUntilComplete());
213 if (flush_result != PP_OK) {
214 ShowErrorMessage("File fail to flush", flush_result);
215 return;
217 ShowStatusMessage("Save success");
220 void Load(int32_t /* result */, const std::string& file_name) {
221 if (!file_system_ready_) {
222 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
223 return;
225 pp::FileRef ref(file_system_, file_name.c_str());
226 pp::FileIO file(this);
228 int32_t open_result =
229 file.Open(ref, PP_FILEOPENFLAG_READ, pp::BlockUntilComplete());
230 if (open_result == PP_ERROR_FILENOTFOUND) {
231 ShowErrorMessage("File not found", open_result);
232 return;
233 } else if (open_result != PP_OK) {
234 ShowErrorMessage("File open for read failed", open_result);
235 return;
237 PP_FileInfo info;
238 int32_t query_result = file.Query(&info, pp::BlockUntilComplete());
239 if (query_result != PP_OK) {
240 ShowErrorMessage("File query failed", query_result);
241 return;
243 // FileIO.Read() can only handle int32 sizes
244 if (info.size > INT32_MAX) {
245 ShowErrorMessage("File too big", PP_ERROR_FILETOOBIG);
246 return;
249 std::vector<char> data(info.size);
250 int64_t offset = 0;
251 int32_t bytes_read = 0;
252 int32_t bytes_to_read = info.size;
253 while (bytes_to_read > 0) {
254 bytes_read = file.Read(offset,
255 &data[offset],
256 data.size() - offset,
257 pp::BlockUntilComplete());
258 if (bytes_read > 0) {
259 offset += bytes_read;
260 bytes_to_read -= bytes_read;
261 } else if (bytes_read < 0) {
262 // If bytes_read < PP_OK then it indicates the error code.
263 ShowErrorMessage("File read failed", bytes_read);
264 return;
267 // Done reading, send content to the user interface
268 std::string string_data(data.begin(), data.end());
269 PostArrayMessage("DISP", string_data);
270 ShowStatusMessage("Load success");
273 void Delete(int32_t /* result */, const std::string& file_name) {
274 if (!file_system_ready_) {
275 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
276 return;
278 pp::FileRef ref(file_system_, file_name.c_str());
280 int32_t result = ref.Delete(pp::BlockUntilComplete());
281 if (result == PP_ERROR_FILENOTFOUND) {
282 ShowStatusMessage("File/Directory not found");
283 return;
284 } else if (result != PP_OK) {
285 ShowErrorMessage("Deletion failed", result);
286 return;
288 ShowStatusMessage("Delete success");
291 void List(int32_t /* result */, const std::string& dir_name) {
292 if (!file_system_ready_) {
293 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
294 return;
297 pp::FileRef ref(file_system_, dir_name.c_str());
299 // Pass ref along to keep it alive.
300 ref.ReadDirectoryEntries(callback_factory_.NewCallbackWithOutput(
301 &FileIoInstance::ListCallback, ref));
304 void ListCallback(int32_t result,
305 const std::vector<pp::DirectoryEntry>& entries,
306 pp::FileRef /* unused_ref */) {
307 if (result != PP_OK) {
308 ShowErrorMessage("List failed", result);
309 return;
312 StringVector sv;
313 for (size_t i = 0; i < entries.size(); ++i) {
314 pp::Var name = entries[i].file_ref().GetName();
315 if (name.is_string()) {
316 sv.push_back(name.AsString());
319 PostArrayMessage("LIST", sv);
320 ShowStatusMessage("List success");
323 void MakeDir(int32_t /* result */, const std::string& dir_name) {
324 if (!file_system_ready_) {
325 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
326 return;
328 pp::FileRef ref(file_system_, dir_name.c_str());
330 int32_t result = ref.MakeDirectory(
331 PP_MAKEDIRECTORYFLAG_NONE, pp::BlockUntilComplete());
332 if (result != PP_OK) {
333 ShowErrorMessage("Make directory failed", result);
334 return;
336 ShowStatusMessage("Make directory success");
339 void Rename(int32_t /* result */,
340 const std::string& old_name,
341 const std::string& new_name) {
342 if (!file_system_ready_) {
343 ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
344 return;
347 pp::FileRef ref_old(file_system_, old_name.c_str());
348 pp::FileRef ref_new(file_system_, new_name.c_str());
350 int32_t result = ref_old.Rename(ref_new, pp::BlockUntilComplete());
351 if (result != PP_OK) {
352 ShowErrorMessage("Rename failed", result);
353 return;
355 ShowStatusMessage("Rename success");
358 /// Encapsulates our simple javascript communication protocol
359 void ShowErrorMessage(const std::string& message, int32_t result) {
360 std::stringstream ss;
361 ss << message << " -- Error #: " << result;
362 PostArrayMessage("ERR", ss.str());
365 void ShowStatusMessage(const std::string& message) {
366 PostArrayMessage("STAT", message);
370 /// The Module class. The browser calls the CreateInstance() method to create
371 /// an instance of your NaCl module on the web page. The browser creates a new
372 /// instance for each <embed> tag with type="application/x-nacl".
373 class FileIoModule : public pp::Module {
374 public:
375 FileIoModule() : pp::Module() {}
376 virtual ~FileIoModule() {}
378 /// Create and return a FileIoInstance object.
379 /// @param[in] instance The browser-side instance.
380 /// @return the plugin-side instance.
381 virtual pp::Instance* CreateInstance(PP_Instance instance) {
382 return new FileIoInstance(instance);
386 namespace pp {
387 /// Factory function called by the browser when the module is first loaded.
388 /// The browser keeps a singleton of this module. It calls the
389 /// CreateInstance() method on the object you return to make instances. There
390 /// is one instance per <embed> tag on the page. This is the main binding
391 /// point for your NaCl module with the browser.
392 Module* CreateModule() { return new FileIoModule(); }
393 } // namespace pp