summaryrefslogtreecommitdiffstats
path: root/native_client_sdk/src
diff options
context:
space:
mode:
authortysand@chromium.org <tysand@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-11 22:00:21 +0000
committertysand@chromium.org <tysand@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-11 22:00:21 +0000
commit50512e3ea26652015d57078133e6df150299a2ab (patch)
tree2f7eed4d47fcebd3722be8694aa24276a35bb55e /native_client_sdk/src
parentb1af5ccae55c21956ba9e93a0aba21972caf3fa4 (diff)
downloadchromium_src-50512e3ea26652015d57078133e6df150299a2ab.zip
chromium_src-50512e3ea26652015d57078133e6df150299a2ab.tar.gz
chromium_src-50512e3ea26652015d57078133e6df150299a2ab.tar.bz2
New SDK example making use of the persistent file storage
Adding file_io SDK example R=noelallen@chromium.org BUG=132123 TEST= Review URL: https://chromiumcodereview.appspot.com/10513018 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@141519 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk/src')
-rwxr-xr-xnative_client_sdk/src/build_tools/build_sdk.py1
-rw-r--r--native_client_sdk/src/examples/file_io/Makefile76
-rw-r--r--native_client_sdk/src/examples/file_io/file_io.cc444
-rw-r--r--native_client_sdk/src/examples/file_io/file_io.html171
-rw-r--r--native_client_sdk/src/examples/file_io/file_io.nmf6
-rw-r--r--native_client_sdk/src/examples/index.html9
6 files changed, 706 insertions, 1 deletions
diff --git a/native_client_sdk/src/build_tools/build_sdk.py b/native_client_sdk/src/build_tools/build_sdk.py
index dbcfcc2..fe55b92 100755
--- a/native_client_sdk/src/build_tools/build_sdk.py
+++ b/native_client_sdk/src/build_tools/build_sdk.py
@@ -352,6 +352,7 @@ EXAMPLE_MAP = {
'newlib': [
'debugging',
'file_histogram',
+ 'file_io',
'fullscreen_tumbler',
'gamepad',
'geturl',
diff --git a/native_client_sdk/src/examples/file_io/Makefile b/native_client_sdk/src/examples/file_io/Makefile
new file mode 100644
index 0000000..4c665c7
--- /dev/null
+++ b/native_client_sdk/src/examples/file_io/Makefile
@@ -0,0 +1,76 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+#
+# GNU Make based build file.  For details on GNU Make see:
+#   http://www.gnu.org/software/make/manual/make.html
+#
+
+#
+# Project information
+#
+# These variables store project specific settings for the project name
+# build flags, files to copy or install.  In the examples it is typically
+# only the list of sources and project name that will actually change and
+# the rest of the makefile is boilerplate for defining build rules.
+#
+PROJECT:=file_io
+LDFLAGS:=-lppapi_cpp -lppapi
+CXX_SOURCES:=$(PROJECT).cc
+
+
+#
+# Get pepper directory for toolchain and includes.
+#
+# If PEPPER_ROOT is not set, then assume it can be found a two directories up,
+# from the default example directory location.
+#
+THIS_MAKEFILE:=$(abspath $(lastword $(MAKEFILE_LIST)))
+NACL_SDK_ROOT?=$(abspath $(dir $(THIS_MAKEFILE))../..)
+
+# Project Build flags
+WARNINGS:=-Wno-long-long -Wall -Wswitch-enum -pedantic -Werror
+CXXFLAGS:=-pthread -std=gnu++98 $(WARNINGS)
+
+#
+# Compute tool paths
+#
+#
+OSNAME:=$(shell python $(NACL_SDK_ROOT)/tools/getos.py)
+TC_PATH:=$(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_x86_newlib)
+CXX:=$(TC_PATH)/bin/i686-nacl-g++
+
+#
+# Disable DOS PATH warning when using Cygwin based tools Windows
+#
+CYGWIN ?= nodosfilewarning
+export CYGWIN
+
+
+# Declare the ALL target first, to make the 'all' target the default build
+all: $(PROJECT)_x86_32.nexe $(PROJECT)_x86_64.nexe
+
+# Define 32 bit compile and link rules for C++ sources
+x86_32_OBJS:=$(patsubst %.cc,%_32.o,$(CXX_SOURCES))
+$(x86_32_OBJS) : %_32.o : %.cc $(THIS_MAKE)
+ $(CXX) -o $@ -c $< -m32 -O0 -g $(CXXFLAGS)
+
+$(PROJECT)_x86_32.nexe : $(x86_32_OBJS)
+ $(CXX) -o $@ $^ -m32 -O0 -g $(CXXFLAGS) $(LDFLAGS)
+
+# Define 64 bit compile and link rules for C++ sources
+x86_64_OBJS:=$(patsubst %.cc,%_64.o,$(CXX_SOURCES))
+$(x86_64_OBJS) : %_64.o : %.cc $(THIS_MAKE)
+ $(CXX) -o $@ -c $< -m64 -O0 -g $(CXXFLAGS)
+
+$(PROJECT)_x86_64.nexe : $(x86_64_OBJS)
+ $(CXX) -o $@ $^ -m64 -O0 -g $(CXXFLAGS) $(LDFLAGS)
+
+
+# Define a phony rule so it always runs, to build nexe and start up server.
+.PHONY: RUN
+RUN: all
+ python ../httpd.py
+
+
diff --git a/native_client_sdk/src/examples/file_io/file_io.cc b/native_client_sdk/src/examples/file_io/file_io.cc
new file mode 100644
index 0000000..4df8209
--- /dev/null
+++ b/native_client_sdk/src/examples/file_io/file_io.cc
@@ -0,0 +1,444 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// @file file_io.cc
+/// This example demonstrates the use of persistent file I/O
+
+#include <stdio.h>
+
+#include <sstream>
+#include <string>
+
+#include "ppapi/c/ppb_file_io.h"
+#include "ppapi/cpp/file_io.h"
+#include "ppapi/cpp/file_ref.h"
+#include "ppapi/cpp/file_system.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/var.h"
+#include "ppapi/utility/completion_callback_factory.h"
+
+namespace {
+/// Used for our simple protocol to communicate with Javascript
+const char* const kLoadPrefix = "ld";
+const char* const kSavePrefix = "sv";
+const char* const kDeletePrefix = "de";
+}
+
+/// The Instance class. One of these exists for each instance of your NaCl
+/// module on the web page. The browser will ask the Module object to create
+/// a new Instance for each occurence of the <embed> tag that has these
+/// attributes:
+/// type="application/x-nacl"
+/// src="file_io.nmf"
+class FileIoInstance : public pp::Instance {
+ public:
+ /// The constructor creates the plugin-side instance.
+ /// @param[in] instance the handle to the browser-side plugin instance.
+ explicit FileIoInstance(PP_Instance instance)
+ : pp::Instance(instance),
+ callback_factory_(this),
+ file_system_(this, PP_FILESYSTEMTYPE_LOCALPERSISTENT),
+ file_system_ready_(false) {
+ }
+
+ virtual ~FileIoInstance() {
+ }
+
+ virtual bool Init(uint32_t /*argc*/, const char* /*argn*/[],
+ const char* /*argv*/[]) {
+ pp::CompletionCallback callback = callback_factory_.NewCallback(
+ &FileIoInstance::FileSystemOpenCallback);
+ int32_t rv = file_system_.Open(1024*1024, callback);
+ if (rv != PP_OK_COMPLETIONPENDING) {
+ callback.Run(rv);
+ return false;
+ }
+ return true;
+ }
+
+ protected:
+ pp::CompletionCallbackFactory<FileIoInstance> callback_factory_;
+ pp::FileSystem file_system_;
+ bool file_system_ready_;
+
+ private:
+ /// Struct to hold various info about a file operation. Our scheme in this
+ /// example is to allocate this information on the heap so that is persists
+ /// after the asynchronous call until the callback is called, and is
+ /// therefore available to the main thread to complete the requested operation
+ struct Request {
+ pp::FileRef ref;
+ pp::FileIO file;
+ std::string file_contents;
+ int64_t offset;
+ PP_FileInfo info;
+ };
+
+ /// Handler for messages coming in from the browser via postMessage(). The
+ /// @a var_message can contain anything: a JSON string; a string that encodes
+ /// method names and arguments; etc.
+ ///
+ /// Here we use messages to communicate with the user interface
+ ///
+ /// @param[in] var_message The message posted by the browser.
+ virtual void HandleMessage(const pp::Var& var_message) {
+ if (!var_message.is_string())
+ return;
+
+ // Parse message into: instruction file_name_length file_name [file_text]
+ std::string message = var_message.AsString();
+ std::string instruction;
+ std::string file_name;
+ std::stringstream reader(message);
+ int file_name_length;
+
+ reader >> instruction >> file_name_length;
+ file_name.resize(file_name_length);
+ reader.ignore(1); // Eat the delimiter
+ reader.read(&file_name[0], file_name_length);
+
+ if (file_name.length() == 0 || file_name[0] != '/') {
+ ShowStatusMessage("File name must begin with /");
+ return;
+ }
+
+ // Dispatch the instruction
+ if (instruction.compare(kLoadPrefix) == 0) {
+ Load(file_name);
+ return;
+ }
+
+ if (instruction.compare(kSavePrefix) == 0) {
+ // Read the rest of the message as the file text
+ reader.ignore(1); // Eat the delimiter
+ std::string file_text = message.substr(reader.tellg());
+ Save(file_name, file_text);
+ return;
+ }
+
+ if (instruction.compare(kDeletePrefix) == 0) {
+ Delete(file_name);
+ return;
+ }
+ }
+
+ bool Save(const std::string& file_name, const std::string& file_contents) {
+ if (!file_system_ready_) {
+ ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
+ return false;
+ }
+
+ FileIoInstance::Request* request = new FileIoInstance::Request;
+ request->ref = pp::FileRef(file_system_, file_name.c_str());
+ request->file = pp::FileIO(this);
+ request->file_contents = file_contents;
+
+ pp::CompletionCallback callback = callback_factory_.NewCallback(
+ &FileIoInstance::SaveOpenCallback, request);
+ int32_t rv = request->file.Open(request->ref,
+ PP_FILEOPENFLAG_WRITE|PP_FILEOPENFLAG_CREATE|PP_FILEOPENFLAG_TRUNCATE,
+ callback);
+
+ // Handle cleanup in the callback if error
+ if (rv != PP_OK_COMPLETIONPENDING) {
+ callback.Run(rv);
+ return false;
+ }
+ return true;
+ }
+
+ void SaveOpenCallback(int32_t result, FileIoInstance::Request* request) {
+ if (result != PP_OK) {
+ ShowErrorMessage("File open for write failed", result);
+ delete request;
+ return;
+ }
+
+ // It is an error to write 0 bytes to the file, however,
+ // upon opening we have truncated the file to length 0
+ if (request->file_contents.length() == 0) {
+ pp::CompletionCallback callback = callback_factory_.NewCallback(
+ &FileIoInstance::SaveFlushCallback, request);
+ int32_t rv = request->file.Flush(callback);
+
+ // Handle cleanup in the callback if error
+ if (rv != PP_OK_COMPLETIONPENDING) {
+ callback.Run(rv);
+ return;
+ }
+ } else if (request->file_contents.length() <= INT32_MAX) {
+ request->offset = 0;
+ pp::CompletionCallback callback = callback_factory_.NewCallback(
+ &FileIoInstance::SaveWriteCallback, request);
+
+ int32_t rv = request->file.Write(request->offset,
+ request->file_contents.c_str(),
+ request->file_contents.length(), callback);
+
+ // Handle cleanup in the callback if error
+ if (rv != PP_OK_COMPLETIONPENDING) {
+ callback.Run(rv);
+ return;
+ }
+ } else {
+ ShowErrorMessage("File too big", PP_ERROR_FILETOOBIG);
+ delete request;
+ return;
+ }
+ }
+
+ void SaveWriteCallback(int32_t bytes_written,
+ FileIoInstance::Request* request) {
+ // bytes_written is the error code if < 0
+ if (bytes_written < 0) {
+ ShowErrorMessage("File write failed", bytes_written);
+ delete request;
+ return;
+ }
+
+ // Ensure the content length is something write() can handle
+ assert(request->file_contents.length() <= INT32_MAX);
+
+ request->offset += bytes_written;
+
+ if (request->offset == request->file_contents.length() ||
+ bytes_written == 0) {
+ // All bytes have been written, flush the write buffer to complete
+ pp::CompletionCallback callback = callback_factory_.NewCallback(
+ &FileIoInstance::SaveFlushCallback, request);
+ int32_t rv = request->file.Flush(callback);
+
+ // Handle cleanup in the callback if error
+ if (rv != PP_OK_COMPLETIONPENDING) {
+ callback.Run(rv);
+ return;
+ }
+ } else {
+ // If all the bytes haven't been written call write again with remainder
+ pp::CompletionCallback callback = callback_factory_.NewCallback(
+ &FileIoInstance::SaveWriteCallback, request);
+ int32_t rv = request->file.Write(request->offset,
+ request->file_contents.c_str() + request->offset,
+ request->file_contents.length() - request->offset, callback);
+
+ // Handle cleanup in the callback if error
+ if (rv != PP_OK_COMPLETIONPENDING) {
+ callback.Run(rv);
+ return;
+ }
+ }
+ }
+
+ void SaveFlushCallback(int32_t result, FileIoInstance::Request* request) {
+ if (result != PP_OK) {
+ ShowErrorMessage("File fail to flush", result);
+ delete request;
+ return;
+ }
+ ShowStatusMessage("Save successful");
+ delete request;
+ }
+
+ bool Load(const std::string& file_name) {
+ if (!file_system_ready_) {
+ ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
+ return false;
+ }
+
+ FileIoInstance::Request* request = new FileIoInstance::Request;
+ request->ref = pp::FileRef(file_system_, file_name.c_str());
+ request->file = pp::FileIO(this);
+
+ pp::CompletionCallback callback = callback_factory_.NewCallback(
+ &FileIoInstance::LoadOpenCallback, request);
+ int32_t rv = request->file.Open(request->ref, PP_FILEOPENFLAG_READ,
+ callback);
+
+ // Handle cleanup in the callback if error
+ if (rv != PP_OK_COMPLETIONPENDING) {
+ callback.Run(rv);
+ return false;
+ }
+ return true;
+ }
+
+ void LoadOpenCallback(int32_t result, FileIoInstance::Request* request) {
+ if (result == PP_ERROR_FILENOTFOUND) {
+ ShowStatusMessage("File not found");
+ delete request;
+ return;
+ } else if (result != PP_OK) {
+ ShowErrorMessage("File open for read failed", result);
+ delete request;
+ return;
+ }
+
+ pp::CompletionCallback callback = callback_factory_.NewCallback(
+ &FileIoInstance::LoadQueryCallback, request);
+ int32_t rv = request->file.Query(&request->info, callback);
+
+ // Handle cleanup in the callback if error
+ if (rv != PP_OK_COMPLETIONPENDING) {
+ callback.Run(rv);
+ return;
+ }
+ }
+
+ void LoadQueryCallback(int32_t result, FileIoInstance::Request* request) {
+ if (result != PP_OK) {
+ ShowErrorMessage("File query failed", result);
+ delete request;
+ return;
+ }
+
+ // FileIO.Read() can only handle int32 sizes
+ if (request->info.size > INT32_MAX) {
+ ShowErrorMessage("File too big", PP_ERROR_FILETOOBIG);
+ delete request;
+ return;
+ }
+
+ // Allocate a buffer to read the file into
+ // Here we must allocate on the heap so FileIO::Read will write to this
+ // one and only copy of file_contents
+ request->file_contents.resize(request->info.size, '\0');
+ request->offset = 0;
+
+ pp::CompletionCallback callback = callback_factory_.NewCallback(
+ &FileIoInstance::LoadReadCallback, request);
+ int32_t rv = request->file.Read(request->offset,
+ &request->file_contents[request->offset],
+ request->file_contents.length(), callback);
+
+ // Handle cleanup in the callback if error
+ if (rv != PP_OK_COMPLETIONPENDING) {
+ callback.Run(rv);
+ return;
+ }
+ }
+
+ void LoadReadCallback(int32_t bytes_read, FileIoInstance::Request* request) {
+ // If bytes_read < 0 then it indicates the error code
+ if (bytes_read < 0) {
+ ShowErrorMessage("File read failed", bytes_read);
+ delete request;
+ return;
+ }
+
+ // Ensure the content length is something read() can handle
+ assert(request->file_contents.length() <= INT32_MAX);
+
+ request->offset += bytes_read;
+
+ if (request->offset == request->file_contents.length() || bytes_read == 0) {
+ // Done reading, send content to the user interface
+ PostMessage(pp::Var("DISP|" + request->file_contents));
+ ShowStatusMessage("Load complete");
+ delete request;
+ } else {
+ // Some bytes remain to be read, call read again with remainder
+ pp::CompletionCallback callback = callback_factory_.NewCallback(
+ &FileIoInstance::LoadReadCallback, request);
+ int32_t rv = request->file.Read(request->offset,
+ &request->file_contents[request->offset],
+ request->file_contents.length() - request->offset, callback);
+
+ // Handle cleanup in the callback if error
+ if (rv != PP_OK_COMPLETIONPENDING) {
+ callback.Run(rv);
+ return;
+ }
+ }
+ }
+
+ bool Delete(const std::string& file_name) {
+ if (!file_system_ready_) {
+ ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
+ return false;
+ }
+
+ FileIoInstance::Request* request = new FileIoInstance::Request;
+ request->ref = pp::FileRef(file_system_, file_name.c_str());
+
+ pp::CompletionCallback callback = callback_factory_.NewCallback(
+ &FileIoInstance::DeleteCallback, request);
+ int32_t rv = request->ref.Delete(callback);
+
+ // Handle cleanup in the callback if error
+ if (rv != PP_OK_COMPLETIONPENDING) {
+ callback.Run(rv);
+ return false;
+ }
+ return true;
+ }
+
+ void DeleteCallback(int32_t result, FileIoInstance::Request* request) {
+ if (result == PP_ERROR_FILENOTFOUND) {
+ ShowStatusMessage("File not found");
+ delete request;
+ return;
+ } else if (result != PP_OK) {
+ ShowErrorMessage("Deletion failed", result);
+ delete request;
+ return;
+ }
+
+ ShowStatusMessage("File deleted");
+ delete request;
+ }
+
+ void FileSystemOpenCallback(int32_t result) {
+ if (result != PP_OK) {
+ ShowErrorMessage("File system open call failed", result);
+ return;
+ }
+
+ file_system_ready_ = true;
+ // Notify the user interface that we're ready
+ PostMessage(pp::Var("READY|"));
+ }
+
+ /// Encapsulates our simple javascript communication protocol
+ void ShowErrorMessage(const std::string& message, int32_t result) {
+ std::stringstream ss;
+ ss << "ERR|" << message << " -- Error #: " << result;
+ PostMessage(pp::Var(ss.str()));
+ }
+
+ /// Encapsulates our simple javascript communication protocol
+ void ShowStatusMessage(const std::string& message) {
+ std::stringstream ss;
+ ss << "STAT|" << message;
+ PostMessage(pp::Var(ss.str()));
+ }
+};
+
+/// The Module class. The browser calls the CreateInstance() method to create
+/// an instance of your NaCl module on the web page. The browser creates a new
+/// instance for each <embed> tag with type="application/x-nacl".
+class FileIoModule : public pp::Module {
+ public:
+ FileIoModule() : pp::Module() {}
+ virtual ~FileIoModule() {}
+
+ /// Create and return a FileIoInstance object.
+ /// @param[in] instance The browser-side instance.
+ /// @return the plugin-side instance.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new FileIoInstance(instance);
+ }
+};
+
+namespace pp {
+/// Factory function called by the browser when the module is first loaded.
+/// The browser keeps a singleton of this module. It calls the
+/// CreateInstance() method on the object you return to make instances. There
+/// is one instance per <embed> tag on the page. This is the main binding
+/// point for your NaCl module with the browser.
+Module* CreateModule() {
+ return new FileIoModule();
+}
+} // namespace pp
+
diff --git a/native_client_sdk/src/examples/file_io/file_io.html b/native_client_sdk/src/examples/file_io/file_io.html
new file mode 100644
index 0000000..009c2e8
--- /dev/null
+++ b/native_client_sdk/src/examples/file_io/file_io.html
@@ -0,0 +1,171 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <!--
+ Copyright (c) 2012 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+ <title>File I/O Example</title>
+
+ <script type="text/javascript">
+ FileIoModule = null; // Global application object.
+ statusText = 'NO-STATUS';
+
+ // Request file system space and indicate successful load
+ function moduleDidLoad() {
+ FileIoModule = document.getElementById('file_io');
+ }
+
+ function createNaClModule() {
+ // Dynamically generate this HTML:
+ // <embed name="nacl_module"
+ // id="file_io"
+ // width=0 height=0
+ // src="file_io.nmf"
+ // type="application/x-nacl" />
+ var listenerDiv = document.getElementById('listener');
+ var naclModule = document.createElement('embed');
+ naclModule.setAttribute('name', 'nacl_module');
+ naclModule.setAttribute('id', 'file_io');
+ naclModule.setAttribute('width', 0);
+ naclModule.setAttribute('height', 0);
+ naclModule.setAttribute('src', 'file_io.nmf');
+ naclModule.setAttribute('type', 'application/x-nacl');
+ listenerDiv.appendChild(naclModule);
+ }
+
+ // If the page loads before the Native Client module loads, then set the
+ // status message indicating that the module is still loading. Otherwise,
+ // do not change the status message.
+ function pageDidLoad() {
+ // Request file system space
+ updateStatus('Allocating storage...');
+ window.webkitStorageInfo.requestQuota(window.PERSISTENT, 1024*1024,
+ function(bytes) {
+ updateStatus('Allocated '+bytes+' bytes of persistant storage.');
+ createNaClModule();
+ },
+ function(e) { alert('Failed to allocate space') });
+ }
+
+ // Set the global status message. If the element with id 'statusField'
+ // exists, then set its HTML to the status message as well.
+ // opt_message The message test. If this is null or undefined, then
+ // attempt to set the element with id 'statusField' to the value of
+ // |statusText|.
+ function updateStatus(opt_message) {
+ if (opt_message)
+ statusText = opt_message;
+ var statusField = document.getElementById('status_field');
+ if (statusField) {
+ statusField.innerHTML = statusText;
+ statusField.style.color = "black";
+ }
+ }
+
+ function loadFile() {
+ if (FileIoModule) {
+ var fileName = document.getElementById('file_name').value;
+
+ // Package a message using a simple protocol containing:
+ // instruction file_name_length file_name
+ var msg = "ld " + fileName.length + " " + fileName;
+ FileIoModule.postMessage(msg);
+ }
+ }
+
+ function saveFile() {
+ if (FileIoModule) {
+ var fileName = document.getElementById('file_name').value;
+ var fileText = document.getElementById('file_editor').value;
+
+ // Package a message using a simple protocol containing:
+ // instruction file_name_length file_name file_contents
+ var msg = "sv " + fileName.length + " " + fileName + " " + fileText;
+ FileIoModule.postMessage(msg);
+ }
+ }
+
+ function deleteFile() {
+ if (FileIoModule) {
+ var fileName = document.getElementById('file_name').value;
+
+ // Package a message using a simple protocol containing:
+ // instruction file_name_length file_name
+ var msg = "de " + fileName.length + " " + fileName;
+ FileIoModule.postMessage(msg);
+ }
+ }
+
+ function handleMessage(message_event) {
+ var messageParts = message_event.data.split("|", 3);
+
+ if (messageParts[0] == "ERR") {
+ updateStatus(messageParts[1]);
+ document.getElementById('status_field').style.color = "red";
+ }
+ else if(messageParts[0] == "STAT") {
+ updateStatus(messageParts[1]);
+ }
+ else if (messageParts[0] == "DISP") {
+ // Display the message in the file edit box
+ document.getElementById('file_editor').value = messageParts[1];
+ }
+ else if (messageParts[0] == "READY") {
+ var statusField = document.getElementById('status_field');
+ updateStatus(statusField.innerHTML + ' Ready!');
+ }
+ }
+
+
+ </script>
+</head>
+<body onload="pageDidLoad()">
+
+<h1>File I/O example</h1>
+This example demonstrates the use of the persistent file system
+<p>
+The window below allows creating and editing files in the file store.
+<p>
+ <!-- Load the published .nexe. This includes the 'nacl' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the
+ instruction set architecture ('x86-32', 'x86-64', etc.); the value is a URL
+ for the desired NaCl module.
+ To load the debug versions of your .nexes, set the 'nacl' attribute to the
+ _dbg.nmf version of the manifest file.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element
+ to ensure that the listeners are active before the NaCl module 'load'
+ event fires.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener');
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <textarea id="file_editor"
+ cols="40"
+ rows="10"
+ wrap="hard"
+ placeholder="Enter some text to save in a file..."></textarea>
+ <br>File Name
+ <input type="text" id="file_name" action=""/>
+ <button id="save_but" onclick="saveFile()" action="">Save</button>
+ <button id="load_but" onclick="loadFile()" action="">Load</button>
+ <button id="delete_but" onclick="deleteFile()" action="">Delete</button>
+ </div>
+</p>
+
+
+
+<h2>Status</h2>
+<div id="status_field">NO-STATUS</div>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/file_io/file_io.nmf b/native_client_sdk/src/examples/file_io/file_io.nmf
new file mode 100644
index 0000000..4864849
--- /dev/null
+++ b/native_client_sdk/src/examples/file_io/file_io.nmf
@@ -0,0 +1,6 @@
+{
+ "program": {
+ "x86-64": {"url": "file_io_x86_64.nexe"},
+ "x86-32": {"url": "file_io_x86_32.nexe"}
+ }
+}
diff --git a/native_client_sdk/src/examples/index.html b/native_client_sdk/src/examples/index.html
index 34aa471..c400f42 100644
--- a/native_client_sdk/src/examples/index.html
+++ b/native_client_sdk/src/examples/index.html
@@ -76,7 +76,7 @@ See the link for further information.
<h3>Common APIs</h3>
<dd><p>The following set of examples illustrate various Pepper APIs including
-audio, 2D, 3D, input and urls.</p></dd>
+audio, 2D, 3D, file I/O, input and urls.</p></dd>
<dt><a href="sine_synth/sine_synth.html">Sine Wave Synthesizer</a></dt>
<dd> The Sine Wave Synthesizer example demonstrates playing sound (a sine
wave). Enter the desired frequency and hit play to start, stop to end. The
@@ -113,6 +113,13 @@ audio, 2D, 3D, input and urls.</p></dd>
<p>Teaching focus: URL loading.</p>
</dd>
+
+ <dt><a href="file_io/file_io.html">File I/O</a></dt>
+ <dd> The File IO example demonstrates saving, loading, and deleting files
+ from the persistent file store.
+
+ <p>Teaching focus: File input and output.</p>
+ </dd>
<h3>Common Concepts</h3>
<dd><p>The following set of examples illustrate various common concepts such as