From 4f95dd7ca9883879dc642c2f61cff2edad412b38 Mon Sep 17 00:00:00 2001
From: "brettw@chromium.org"
 <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Thu, 8 Dec 2011 21:02:32 +0000
Subject: Implement a proxy for Pepper FileIO.

This splits apart the old in-process implementation into a new object in shared_impl that does most of the general tracking. This alllows that code to be shared by the proxy.

BUG=http://crbug.com/101154

Review URL: http://codereview.chromium.org/8764003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113656 0039d316-1c4b-4281-b951-d872f2087c98
---
 ppapi/shared_impl/api_id.h                |   1 +
 ppapi/shared_impl/file_type_conversion.cc |  74 ++++++++++
 ppapi/shared_impl/file_type_conversion.h  |  23 +++
 ppapi/shared_impl/ppb_file_io_shared.cc   | 237 ++++++++++++++++++++++++++++++
 ppapi/shared_impl/ppb_file_io_shared.h    | 157 ++++++++++++++++++++
 5 files changed, 492 insertions(+)
 create mode 100644 ppapi/shared_impl/file_type_conversion.cc
 create mode 100644 ppapi/shared_impl/file_type_conversion.h
 create mode 100644 ppapi/shared_impl/ppb_file_io_shared.cc
 create mode 100644 ppapi/shared_impl/ppb_file_io_shared.h

(limited to 'ppapi/shared_impl')

diff --git a/ppapi/shared_impl/api_id.h b/ppapi/shared_impl/api_id.h
index c8c9362..73654b0 100644
--- a/ppapi/shared_impl/api_id.h
+++ b/ppapi/shared_impl/api_id.h
@@ -21,6 +21,7 @@ enum ApiID {
   API_ID_PPB_CORE,
   API_ID_PPB_CURSORCONTROL,
   API_ID_PPB_FILE_CHOOSER,
+  API_ID_PPB_FILE_IO,
   API_ID_PPB_FILE_REF,
   API_ID_PPB_FILE_SYSTEM,
   API_ID_PPB_FLASH,
diff --git a/ppapi/shared_impl/file_type_conversion.cc b/ppapi/shared_impl/file_type_conversion.cc
new file mode 100644
index 0000000..a9bd435
--- /dev/null
+++ b/ppapi/shared_impl/file_type_conversion.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2011 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.
+
+#include "ppapi/shared_impl/file_type_conversion.h"
+
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/c/ppb_file_io.h"
+
+namespace ppapi {
+
+int PlatformFileErrorToPepperError(base::PlatformFileError error_code) {
+  switch (error_code) {
+    case base::PLATFORM_FILE_OK:
+      return PP_OK;
+    case base::PLATFORM_FILE_ERROR_EXISTS:
+      return PP_ERROR_FILEEXISTS;
+    case base::PLATFORM_FILE_ERROR_NOT_FOUND:
+      return PP_ERROR_FILENOTFOUND;
+    case base::PLATFORM_FILE_ERROR_ACCESS_DENIED:
+    case base::PLATFORM_FILE_ERROR_SECURITY:
+      return PP_ERROR_NOACCESS;
+    case base::PLATFORM_FILE_ERROR_NO_MEMORY:
+      return PP_ERROR_NOMEMORY;
+    case base::PLATFORM_FILE_ERROR_NO_SPACE:
+      return PP_ERROR_NOSPACE;
+    case base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY:
+      return PP_ERROR_FAILED;
+    default:
+      return PP_ERROR_FAILED;
+  }
+}
+
+bool PepperFileOpenFlagsToPlatformFileFlags(int32_t pp_open_flags,
+                                            int* flags_out) {
+  if (!flags_out)
+    return false;
+
+  bool pp_read = !!(pp_open_flags & PP_FILEOPENFLAG_READ);
+  bool pp_write = !!(pp_open_flags & PP_FILEOPENFLAG_WRITE);
+  bool pp_create = !!(pp_open_flags & PP_FILEOPENFLAG_CREATE);
+  bool pp_truncate = !!(pp_open_flags & PP_FILEOPENFLAG_TRUNCATE);
+  bool pp_exclusive = !!(pp_open_flags & PP_FILEOPENFLAG_EXCLUSIVE);
+
+  int flags = 0;
+  if (pp_read)
+    flags |= base::PLATFORM_FILE_READ;
+  if (pp_write) {
+    flags |= base::PLATFORM_FILE_WRITE;
+    flags |= base::PLATFORM_FILE_WRITE_ATTRIBUTES;
+  }
+
+  if (pp_truncate && !pp_write)
+    return false;
+
+  if (pp_create) {
+    if (pp_exclusive) {
+      flags |= base::PLATFORM_FILE_CREATE;
+    } else if (pp_truncate) {
+      flags |= base::PLATFORM_FILE_CREATE_ALWAYS;
+    } else {
+      flags |= base::PLATFORM_FILE_OPEN_ALWAYS;
+    }
+  } else if (pp_truncate) {
+    flags |= base::PLATFORM_FILE_OPEN_TRUNCATED;
+  } else {
+    flags |= base::PLATFORM_FILE_OPEN;
+  }
+
+  *flags_out = flags;
+  return true;
+}
+
+}  // namespace ppapi
diff --git a/ppapi/shared_impl/file_type_conversion.h b/ppapi/shared_impl/file_type_conversion.h
new file mode 100644
index 0000000..3ecd404
--- /dev/null
+++ b/ppapi/shared_impl/file_type_conversion.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2011 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.
+
+#ifndef PPAPI_SHARED_IMPL_FILE_TYPE_CONVERSION_H_
+#define PPAPI_SHARED_IMPL_FILE_TYPE_CONVERSION_H_
+
+#include "base/platform_file.h"
+#include "ppapi/c/pp_stdint.h"
+
+namespace ppapi {
+
+int PlatformFileErrorToPepperError(base::PlatformFileError error_code);
+
+// Converts a PP_FileOpenFlags_Dev flag combination into a corresponding
+// PlatformFileFlags flag combination.
+// Returns |true| if okay.
+bool PepperFileOpenFlagsToPlatformFileFlags(int32_t pp_open_flags,
+                                            int* flags_out);
+
+}  // namespace ppapi
+
+#endif  // PPAPI_SHARED_IMPL_FILE_TYPE_CONVERSION_H_
diff --git a/ppapi/shared_impl/ppb_file_io_shared.cc b/ppapi/shared_impl/ppb_file_io_shared.cc
new file mode 100644
index 0000000..a51c33f
--- /dev/null
+++ b/ppapi/shared_impl/ppb_file_io_shared.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2011 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.
+
+#include "ppapi/shared_impl/ppb_file_io_shared.h"
+
+#include <string.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/shared_impl/file_type_conversion.h"
+#include "ppapi/shared_impl/time_conversion.h"
+#include "ppapi/thunk/enter.h"
+#include "ppapi/thunk/ppb_file_ref_api.h"
+
+namespace ppapi {
+
+using thunk::EnterResourceNoLock;
+using thunk::PPB_FileIO_API;
+using thunk::PPB_FileRef_API;
+
+PPB_FileIO_Shared::CallbackEntry::CallbackEntry()
+    : read_buffer(NULL),
+      info(NULL) {
+}
+
+PPB_FileIO_Shared::CallbackEntry::CallbackEntry(const CallbackEntry& entry)
+    : callback(entry.callback),
+      read_buffer(entry.read_buffer),
+      info(entry.info) {
+}
+
+PPB_FileIO_Shared::CallbackEntry::~CallbackEntry() {
+}
+
+PPB_FileIO_Shared::PPB_FileIO_Shared(PP_Instance instance)
+    : Resource(instance),
+      file_system_type_(PP_FILESYSTEMTYPE_INVALID),
+      file_open_(false),
+      pending_op_(OPERATION_NONE) {
+}
+
+PPB_FileIO_Shared::PPB_FileIO_Shared(const HostResource& host_resource)
+    : Resource(host_resource),
+      file_system_type_(PP_FILESYSTEMTYPE_INVALID),
+      file_open_(false),
+      pending_op_(OPERATION_NONE) {
+}
+
+PPB_FileIO_Shared::~PPB_FileIO_Shared() {
+  // The callbacks list should have been cleared by LastPluginRefWasDeleted.
+  DCHECK(callbacks_.empty());
+}
+
+void PPB_FileIO_Shared::LastPluginRefWasDeleted() {
+  // Abort all pending callbacks. Do this by posting a task to avoid reentering
+  // the plugin's Release() call that probably deleted this object.
+  for (size_t i = 0; i < callbacks_.size(); i++) {
+    MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+        callbacks_[i].callback.func, callbacks_[i].callback.user_data,
+        static_cast<int32_t>(PP_ERROR_ABORTED)));
+  }
+  callbacks_.erase(callbacks_.begin(), callbacks_.end());
+}
+
+thunk::PPB_FileIO_API* PPB_FileIO_Shared::AsPPB_FileIO_API() {
+  return this;
+}
+
+int32_t PPB_FileIO_Shared::Open(PP_Resource file_ref,
+                                int32_t open_flags,
+                                PP_CompletionCallback callback) {
+  EnterResourceNoLock<PPB_FileRef_API> enter(file_ref, true);
+  if (enter.failed())
+    return PP_ERROR_BADRESOURCE;
+
+  int32_t rv = CommonCallValidation(false, OPERATION_EXCLUSIVE, callback);
+  if (rv != PP_OK)
+    return rv;
+
+  PP_FileSystemType type = enter.object()->GetFileSystemType();
+  if (type != PP_FILESYSTEMTYPE_LOCALPERSISTENT &&
+      type != PP_FILESYSTEMTYPE_LOCALTEMPORARY &&
+      type != PP_FILESYSTEMTYPE_EXTERNAL)
+    return PP_ERROR_FAILED;
+  file_system_type_ = type;
+
+  return OpenValidated(file_ref, enter.object(), open_flags, callback);
+}
+
+int32_t PPB_FileIO_Shared::Query(PP_FileInfo* info,
+                                 PP_CompletionCallback callback) {
+  int32_t rv = CommonCallValidation(true, OPERATION_EXCLUSIVE, callback);
+  if (rv != PP_OK)
+    return rv;
+  if (!info)
+    return PP_ERROR_BADARGUMENT;
+  return QueryValidated(info, callback);
+}
+
+int32_t PPB_FileIO_Shared::Touch(PP_Time last_access_time,
+                                 PP_Time last_modified_time,
+                                 PP_CompletionCallback callback) {
+  int32_t rv = CommonCallValidation(true, OPERATION_EXCLUSIVE, callback);
+  if (rv != PP_OK)
+    return rv;
+  return TouchValidated(last_access_time, last_modified_time, callback);
+}
+
+int32_t PPB_FileIO_Shared::Read(int64_t offset,
+                                char* buffer,
+                                int32_t bytes_to_read,
+                                PP_CompletionCallback callback) {
+  int32_t rv = CommonCallValidation(true, OPERATION_READ, callback);
+  if (rv != PP_OK)
+    return rv;
+  return ReadValidated(offset, buffer, bytes_to_read, callback);
+}
+
+int32_t PPB_FileIO_Shared::Write(int64_t offset,
+                                 const char* buffer,
+                                 int32_t bytes_to_write,
+                                 PP_CompletionCallback callback) {
+  int32_t rv = CommonCallValidation(true, OPERATION_WRITE, callback);
+  if (rv != PP_OK)
+    return rv;
+  return WriteValidated(offset, buffer, bytes_to_write, callback);
+}
+
+int32_t PPB_FileIO_Shared::SetLength(int64_t length,
+                                     PP_CompletionCallback callback) {
+  int32_t rv = CommonCallValidation(true, OPERATION_EXCLUSIVE, callback);
+  if (rv != PP_OK)
+    return rv;
+  return SetLengthValidated(length, callback);
+}
+
+int32_t PPB_FileIO_Shared::Flush(PP_CompletionCallback callback) {
+  int32_t rv = CommonCallValidation(true, OPERATION_EXCLUSIVE, callback);
+  if (rv != PP_OK)
+    return rv;
+  return FlushValidated(callback);
+}
+
+void PPB_FileIO_Shared::ExecuteGeneralCallback(int32_t pp_error) {
+  RunAndRemoveFirstPendingCallback(pp_error);
+}
+
+void PPB_FileIO_Shared::ExecuteOpenFileCallback(int32_t pp_error) {
+  if (pp_error == PP_OK)
+    file_open_ = true;
+  ExecuteGeneralCallback(pp_error);
+}
+
+void PPB_FileIO_Shared::ExecuteQueryCallback(int32_t pp_error,
+                                             const PP_FileInfo& info) {
+  if (pending_op_ != OPERATION_EXCLUSIVE || callbacks_.empty() ||
+      !callbacks_.front().info) {
+    NOTREACHED();
+    return;
+  }
+  *callbacks_.front().info = info;
+  RunAndRemoveFirstPendingCallback(pp_error);
+}
+
+void PPB_FileIO_Shared::ExecuteReadCallback(int32_t pp_error,
+                                            const char* data) {
+  if (pending_op_ != OPERATION_READ || callbacks_.empty()) {
+    NOTREACHED();
+    return;
+  }
+
+  char* read_buffer = callbacks_.front().read_buffer;
+  DCHECK(data);
+  DCHECK(read_buffer);
+
+  // The result code contains the number of bytes if it's positive.
+  if (pp_error > 0)
+    memcpy(read_buffer, data, pp_error);
+  RunAndRemoveFirstPendingCallback(pp_error);
+}
+
+int32_t PPB_FileIO_Shared::CommonCallValidation(
+    bool should_be_open,
+    OperationType new_op,
+    PP_CompletionCallback callback) {
+  // Only asynchronous operation is supported.
+  if (!callback.func)
+    return PP_ERROR_BLOCKS_MAIN_THREAD;
+
+  if (should_be_open) {
+    if (!file_open_)
+      return PP_ERROR_FAILED;
+  } else {
+    if (file_open_)
+      return PP_ERROR_FAILED;
+  }
+
+  if (pending_op_ != OPERATION_NONE &&
+      (pending_op_ != new_op || pending_op_ == OPERATION_EXCLUSIVE)) {
+    return PP_ERROR_INPROGRESS;
+  }
+
+  return PP_OK;
+}
+
+void PPB_FileIO_Shared::RegisterCallback(OperationType op,
+                                         PP_CompletionCallback callback,
+                                         char* read_buffer,
+                                         PP_FileInfo* info) {
+  DCHECK(callback.func);
+  DCHECK(pending_op_ == OPERATION_NONE ||
+         (pending_op_ != OPERATION_EXCLUSIVE && pending_op_ == op));
+
+  CallbackEntry entry;
+  entry.callback = callback;
+  entry.read_buffer = read_buffer;
+  entry.info = info;
+  callbacks_.push_back(entry);
+
+  pending_op_ = op;
+}
+
+void PPB_FileIO_Shared::RunAndRemoveFirstPendingCallback(int32_t result) {
+  DCHECK(!callbacks_.empty());
+
+  CallbackEntry front = callbacks_.front();
+  callbacks_.pop_front();
+  if (callbacks_.empty())
+    pending_op_ = OPERATION_NONE;
+
+  PP_RunCompletionCallback(&front.callback, result);
+}
+
+}  // namespace ppapi
diff --git a/ppapi/shared_impl/ppb_file_io_shared.h b/ppapi/shared_impl/ppb_file_io_shared.h
new file mode 100644
index 0000000..341db46
--- /dev/null
+++ b/ppapi/shared_impl/ppb_file_io_shared.h
@@ -0,0 +1,157 @@
+// Copyright (c) 2011 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.
+
+#ifndef PPAPI_SHARED_IMPL_PPB_FILE_IO_SHARED_H_
+#define PPAPI_SHARED_IMPL_PPB_FILE_IO_SHARED_H_
+
+#include <deque>
+
+#include "base/compiler_specific.h"
+#include "ppapi/shared_impl/ppapi_shared_export.h"
+#include "ppapi/shared_impl/resource.h"
+#include "ppapi/thunk/ppb_file_io_api.h"
+
+namespace ppapi {
+
+namespace thunk {
+class PPB_FileRef_API;
+}
+
+class PPAPI_SHARED_EXPORT PPB_FileIO_Shared : public Resource,
+                                              public thunk::PPB_FileIO_API {
+ public:
+  PPB_FileIO_Shared(PP_Instance instance);
+  PPB_FileIO_Shared(const HostResource& host_resource);
+  ~PPB_FileIO_Shared();
+
+  // Resource overrides.
+  virtual void LastPluginRefWasDeleted() OVERRIDE;
+  virtual thunk::PPB_FileIO_API* AsPPB_FileIO_API() OVERRIDE;
+
+  // PPB_FileIO_API implementation.
+  virtual int32_t Open(PP_Resource file_ref,
+                       int32_t open_flags,
+                       PP_CompletionCallback callback) OVERRIDE;
+  virtual int32_t Query(PP_FileInfo* info,
+                        PP_CompletionCallback callback) OVERRIDE;
+  virtual int32_t Touch(PP_Time last_access_time,
+                        PP_Time last_modified_time,
+                        PP_CompletionCallback callback) OVERRIDE;
+  virtual int32_t Read(int64_t offset,
+                       char* buffer,
+                       int32_t bytes_to_read,
+                       PP_CompletionCallback callback) OVERRIDE;
+  virtual int32_t Write(int64_t offset,
+                        const char* buffer,
+                        int32_t bytes_to_write,
+                        PP_CompletionCallback callback) OVERRIDE;
+  virtual int32_t SetLength(int64_t length,
+                            PP_CompletionCallback callback) OVERRIDE;
+  virtual int32_t Flush(PP_CompletionCallback callback) OVERRIDE;
+
+  // Callback handler for different types of operations.
+  void ExecuteGeneralCallback(int32_t pp_error);
+  void ExecuteOpenFileCallback(int32_t pp_error);
+  void ExecuteQueryCallback(int32_t pp_error, const PP_FileInfo& info);
+  void ExecuteReadCallback(int32_t pp_error, const char* data);
+
+ protected:
+  struct CallbackEntry {
+    CallbackEntry();
+    CallbackEntry(const CallbackEntry& entry);
+    ~CallbackEntry();
+
+    PP_CompletionCallback callback;
+
+    // Pointer back to the caller's read buffer; only used by |Read()|, NULL
+    // for non-read operations. Not owned.
+    char* read_buffer;
+
+    // Pointer back to the caller's PP_FileInfo structure for Query operations.
+    // NULL for non-query operations. Not owned.
+    PP_FileInfo* info;
+  };
+
+  enum OperationType {
+    // There is no pending operation right now.
+    OPERATION_NONE,
+
+    // If there are pending reads, any other kind of async operation is not
+    // allowed.
+    OPERATION_READ,
+
+    // If there are pending writes, any other kind of async operation is not
+    // allowed.
+    OPERATION_WRITE,
+
+    // If there is a pending operation that is neither read nor write, no
+    // further async operation is allowed.
+    OPERATION_EXCLUSIVE
+  };
+
+  // Validated versions of the FileIO API. Subclasses in the proxy and impl
+  // implement these so the common error checking stays here.
+  virtual int32_t OpenValidated(PP_Resource file_ref_resource,
+                                thunk::PPB_FileRef_API* file_ref_api,
+                                int32_t open_flags,
+                                PP_CompletionCallback callback) = 0;
+  virtual int32_t QueryValidated(PP_FileInfo* info,
+                                 PP_CompletionCallback callback) = 0;
+  virtual int32_t TouchValidated(PP_Time last_access_time,
+                                 PP_Time last_modified_time,
+                                 PP_CompletionCallback callback) = 0;
+  virtual int32_t ReadValidated(int64_t offset,
+                                char* buffer,
+                                int32_t bytes_to_read,
+                                PP_CompletionCallback callback) = 0;
+  virtual int32_t WriteValidated(int64_t offset,
+                                 const char* buffer,
+                                 int32_t bytes_to_write,
+                                 PP_CompletionCallback callback) = 0;
+  virtual int32_t SetLengthValidated(int64_t length,
+                                     PP_CompletionCallback callback) = 0;
+  virtual int32_t FlushValidated(PP_CompletionCallback callback) = 0;
+
+  // Called for every "Validated" function.
+  //
+  // This verifies that the callback is valid and that no callback is already
+  // pending, or it is a read(write) request and currently the pending
+  // operations are reads(writes).
+  //
+  // Returns |PP_OK| to indicate that everything is valid or |PP_ERROR_...| if
+  // the call should be aborted and that code returned to the plugin.
+  int32_t CommonCallValidation(bool should_be_open,
+                               OperationType new_op,
+                               PP_CompletionCallback callback);
+
+  // Sets up a pending callback. This should only be called once it is certain
+  // that |PP_OK_COMPLETIONPENDING| will be returned.
+  //
+  // |read_buffer| is only used by read operations, |info| is used only by
+  // query operations.
+  void RegisterCallback(OperationType op,
+                        PP_CompletionCallback callback,
+                        char* read_buffer,
+                        PP_FileInfo* info);
+
+  // Pops the oldest callback from the queue and runs it.
+  void RunAndRemoveFirstPendingCallback(int32_t result);
+
+  // The file system type specified in the Open() call. This will be
+  // PP_FILESYSTEMTYPE_INVALID before open was called. This value does not
+  // indicate that the open command actually succeeded.
+  PP_FileSystemType file_system_type_;
+
+  // Set to true when the file has been successfully opened.
+  bool file_open_;
+
+  std::deque<CallbackEntry> callbacks_;
+  OperationType pending_op_;
+
+  DISALLOW_COPY_AND_ASSIGN(PPB_FileIO_Shared);
+};
+
+}  // namespace ppapi
+
+#endif  // PPAPI_SHARED_IMPL_PPB_FILE_IO_SHARED_H_
-- 
cgit v1.1