Add Media.AudioRendererEvents histogram to measure how often OnRenderError() is called.
[chromium-blink-merge.git] / native_client_sdk / src / examples / file_io / file_io.cc
blob680398258b60454170c713f6cb93a61e7d135625
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/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"
24 #ifndef INT32_MAX
25 #define INT32_MAX (0x7FFFFFFF)
26 #endif
28 #ifdef WIN32
29 #undef min
30 #undef max
31 #undef PostMessage
33 // Allow 'this' in initializer list
34 #pragma warning(disable : 4355)
35 #endif
37 namespace {
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
47 /// attributes:
48 /// type="application/x-nacl"
49 /// src="file_io.nmf"
50 class FileIoInstance : public pp::Instance {
51 public:
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),
59 file_thread_(this) {
62 virtual ~FileIoInstance() {
63 file_thread_.Join();
66 virtual bool Init(uint32_t /*argc*/, 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 /// 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.
92 ///
93 /// Here we use messages to communicate with the user interface
94 ///
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())
98 return;
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 /");
114 return;
117 // Dispatch the instruction
118 if (instruction.compare(kLoadPrefix) == 0) {
119 file_thread_.message_loop().PostWork(
120 callback_factory_.NewCallback(&FileIoInstance::Load, file_name));
121 return;
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,
130 file_name,
131 file_text));
132 return;
135 if (instruction.compare(kDeletePrefix) == 0) {
136 file_thread_.message_loop().PostWork(
137 callback_factory_.NewCallback(&FileIoInstance::Delete, file_name));
138 return;
142 void OpenFileSystem(int32_t /* result */) {
143 int32_t rv = file_system_.Open(1024*1024, pp::CompletionCallback());
144 if (rv == PP_OK) {
145 file_system_ready_ = true;
146 // Notify the user interface that we're ready
147 PostMessage(pp::Var("READY|"));
148 } else {
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);
157 return;
159 pp::FileRef ref(file_system_, file_name.c_str());
160 pp::FileIO file(this);
162 int32_t open_result = file.Open(
163 ref,
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);
168 return;
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);
176 return;
178 int64_t offset = 0;
179 int32_t bytes_written = 0;
180 do {
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;
187 } else {
188 ShowErrorMessage("File write failed", bytes_written);
189 return;
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);
197 return;
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);
205 return;
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");
214 return;
215 } else if (open_result != PP_OK) {
216 ShowErrorMessage("File open for read failed", open_result);
217 return;
219 PP_FileInfo info;
220 int32_t query_result = file.Query(&info, pp::CompletionCallback());
221 if (query_result != PP_OK) {
222 ShowErrorMessage("File query failed", query_result);
223 return;
225 // FileIO.Read() can only handle int32 sizes
226 if (info.size > INT32_MAX) {
227 ShowErrorMessage("File too big", PP_ERROR_FILETOOBIG);
228 return;
231 std::vector<char> data(info.size);
232 int64_t offset = 0;
233 int32_t bytes_read = 0;
234 do {
235 bytes_read = file.Read(offset, &data[offset], data.size() - offset,
236 pp::CompletionCallback());
237 if (bytes_read > 0)
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);
243 return;
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);
255 return;
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");
262 return;
263 } else if (result != PP_OK) {
264 ShowErrorMessage("Deletion failed", result);
265 return;
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 {
289 public:
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);
301 namespace pp {
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();
310 } // namespace pp