diff options
-rw-r--r-- | chrome/test/ppapi/ppapi_browsertest.cc | 3 | ||||
-rw-r--r-- | ppapi/api/ppb_file_io.idl | 32 | ||||
-rw-r--r-- | ppapi/c/ppb_file_io.h | 68 | ||||
-rw-r--r-- | ppapi/cpp/array_output.h | 2 | ||||
-rw-r--r-- | ppapi/cpp/file_io.cc | 162 | ||||
-rw-r--r-- | ppapi/cpp/file_io.h | 52 | ||||
-rw-r--r-- | ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_file_io.cc | 3 | ||||
-rw-r--r-- | ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c | 92 | ||||
-rw-r--r-- | ppapi/proxy/ppb_file_io_proxy.cc | 14 | ||||
-rw-r--r-- | ppapi/shared_impl/array_writer.h | 21 | ||||
-rw-r--r-- | ppapi/shared_impl/ppb_file_io_shared.cc | 49 | ||||
-rw-r--r-- | ppapi/shared_impl/ppb_file_io_shared.h | 17 | ||||
-rw-r--r-- | ppapi/tests/test_file_io.cc | 168 | ||||
-rw-r--r-- | ppapi/tests/test_file_io.h | 2 | ||||
-rw-r--r-- | ppapi/tests/test_utils.h | 52 | ||||
-rw-r--r-- | ppapi/thunk/interfaces_ppb_public_stable.h | 1 | ||||
-rw-r--r-- | ppapi/thunk/ppb_file_io_api.h | 4 | ||||
-rw-r--r-- | ppapi/thunk/ppb_file_io_thunk.cc | 34 | ||||
-rw-r--r-- | webkit/plugins/ppapi/ppb_file_io_impl.cc | 8 | ||||
-rw-r--r-- | webkit/plugins/ppapi/ppb_file_io_impl.h | 4 |
20 files changed, 701 insertions, 87 deletions
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc index fed7521..01c07ed 100644 --- a/chrome/test/ppapi/ppapi_browsertest.cc +++ b/chrome/test/ppapi/ppapi_browsertest.cc @@ -510,6 +510,7 @@ TEST_PPAPI_IN_PROCESS_VIA_HTTP(FileIO_ParallelReads) TEST_PPAPI_IN_PROCESS_VIA_HTTP(FileIO_ParallelWrites) TEST_PPAPI_IN_PROCESS_VIA_HTTP(FileIO_NotAllowMixedReadWrite) TEST_PPAPI_IN_PROCESS_VIA_HTTP(FileIO_ReadWriteSetLength) +TEST_PPAPI_IN_PROCESS_VIA_HTTP(FileIO_ReadToArrayWriteSetLength) TEST_PPAPI_IN_PROCESS_VIA_HTTP(FileIO_TouchQuery) TEST_PPAPI_IN_PROCESS_VIA_HTTP(FileIO_WillWriteWillSetLength) @@ -519,6 +520,7 @@ TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(FileIO_ParallelReads) TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(FileIO_ParallelWrites) TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(FileIO_NotAllowMixedReadWrite) TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(FileIO_ReadWriteSetLength) +TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(FileIO_ReadToArrayWriteSetLength) TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(FileIO_TouchQuery) TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(FileIO_WillWriteWillSetLength) @@ -552,6 +554,7 @@ TEST_PPAPI_NACL_VIA_HTTP(FileIO_ParallelWrites) TEST_PPAPI_NACL_VIA_HTTP(FileIO_NotAllowMixedReadWrite) TEST_PPAPI_NACL_VIA_HTTP(MAYBE_NACL_FileIO_TouchQuery) TEST_PPAPI_NACL_VIA_HTTP(FileIO_ReadWriteSetLength) +TEST_PPAPI_NACL_VIA_HTTP(FileIO_ReadToArrayWriteSetLength) // The following test requires PPB_FileIO_Trusted, not available in NaCl. TEST_PPAPI_NACL_VIA_HTTP(DISABLED_FileIO_WillWriteWillSetLength) diff --git a/ppapi/api/ppb_file_io.idl b/ppapi/api/ppb_file_io.idl index 58f3f13..23e97ca 100644 --- a/ppapi/api/ppb_file_io.idl +++ b/ppapi/api/ppb_file_io.idl @@ -9,7 +9,8 @@ */ label Chrome { - M14 = 1.0 + M14 = 1.0, + M25 = 1.1 }; /** @@ -145,6 +146,8 @@ interface PPB_FileIO { * large enough to hold the specified number of bytes to read. This function * might perform a partial read. * + * ReadToArray() is preferred to Read() when doing asynchronous operations. + * * @param[in] file_io A <code>PP_Resource</code> corresponding to a file * FileIO. * @param[in] offset The offset into the file. @@ -240,5 +243,32 @@ interface PPB_FileIO { * FileIO. */ void Close([in] PP_Resource file_io); + + /** + * ReadToArray() reads from an offset in the file. A PP_ArrayOutput must be + * provided so that output will be stored in its allocated buffer. This + * function might perform a partial read. + * + * @param[in] file_io A <code>PP_Resource</code> corresponding to a file + * FileIO. + * @param[in] offset The offset into the file. + * @param[in] max_read_length The maximum number of bytes to read from + * <code>offset</code>. + * @param[in] output A <code>PP_ArrayOutput</code> to hold the output data. + * @param[in] callback A <code>PP_CompletionCallback</code> to be called upon + * completion of ReadToArray(). + * + * @return The number of bytes read or an error code from + * <code>pp_errors.h</code>. If the return value is 0, then end-of-file was + * reached. It is valid to call ReadToArray() multiple times with a completion + * callback to queue up parallel reads from the file, but pending reads + * cannot be interleaved with other operations. + */ + [version = 1.1] + int32_t ReadToArray([in] PP_Resource file_io, + [in] int64_t offset, + [in] int32_t max_read_length, + [inout] PP_ArrayOutput output, + [in] PP_CompletionCallback callback); }; diff --git a/ppapi/c/ppb_file_io.h b/ppapi/c/ppb_file_io.h index 4b9db95..43fcdb9 100644 --- a/ppapi/c/ppb_file_io.h +++ b/ppapi/c/ppb_file_io.h @@ -3,11 +3,12 @@ * found in the LICENSE file. */ -/* From ppb_file_io.idl modified Wed Feb 15 15:55:56 2012. */ +/* From ppb_file_io.idl modified Thu Nov 15 08:08:30 2012. */ #ifndef PPAPI_C_PPB_FILE_IO_H_ #define PPAPI_C_PPB_FILE_IO_H_ +#include "ppapi/c/pp_array_output.h" #include "ppapi/c/pp_bool.h" #include "ppapi/c/pp_completion_callback.h" #include "ppapi/c/pp_file_info.h" @@ -18,7 +19,8 @@ #include "ppapi/c/pp_time.h" #define PPB_FILEIO_INTERFACE_1_0 "PPB_FileIO;1.0" -#define PPB_FILEIO_INTERFACE PPB_FILEIO_INTERFACE_1_0 +#define PPB_FILEIO_INTERFACE_1_1 "PPB_FileIO;1.1" +#define PPB_FILEIO_INTERFACE PPB_FILEIO_INTERFACE_1_1 /** * @file @@ -73,7 +75,7 @@ PP_COMPILE_ASSERT_SIZE_IN_BYTES(PP_FileOpenFlags, 4); * The <code>PPB_FileIO</code> struct is used to operate on a regular file * (PP_FileType_Regular). */ -struct PPB_FileIO_1_0 { +struct PPB_FileIO_1_1 { /** * Create() creates a new FileIO object. * @@ -161,6 +163,8 @@ struct PPB_FileIO_1_0 { * large enough to hold the specified number of bytes to read. This function * might perform a partial read. * + * ReadToArray() is preferred to Read() when doing asynchronous operations. + * * @param[in] file_io A <code>PP_Resource</code> corresponding to a file * FileIO. * @param[in] offset The offset into the file. @@ -252,9 +256,65 @@ struct PPB_FileIO_1_0 { * FileIO. */ void (*Close)(PP_Resource file_io); + /** + * ReadToArray() reads from an offset in the file. A PP_ArrayOutput must be + * provided so that output will be stored in its allocated buffer. This + * function might perform a partial read. + * + * @param[in] file_io A <code>PP_Resource</code> corresponding to a file + * FileIO. + * @param[in] offset The offset into the file. + * @param[in] max_read_length The maximum number of bytes to read from + * <code>offset</code>. + * @param[in] output A <code>PP_ArrayOutput</code> to hold the output data. + * @param[in] callback A <code>PP_CompletionCallback</code> to be called upon + * completion of ReadToArray(). + * + * @return The number of bytes read or an error code from + * <code>pp_errors.h</code>. If the return value is 0, then end-of-file was + * reached. It is valid to call ReadToArray() multiple times with a completion + * callback to queue up parallel reads from the file, but pending reads + * cannot be interleaved with other operations. + */ + int32_t (*ReadToArray)(PP_Resource file_io, + int64_t offset, + int32_t max_read_length, + struct PP_ArrayOutput* output, + struct PP_CompletionCallback callback); }; -typedef struct PPB_FileIO_1_0 PPB_FileIO; +typedef struct PPB_FileIO_1_1 PPB_FileIO; + +struct PPB_FileIO_1_0 { + PP_Resource (*Create)(PP_Instance instance); + PP_Bool (*IsFileIO)(PP_Resource resource); + int32_t (*Open)(PP_Resource file_io, + PP_Resource file_ref, + int32_t open_flags, + struct PP_CompletionCallback callback); + int32_t (*Query)(PP_Resource file_io, + struct PP_FileInfo* info, + struct PP_CompletionCallback callback); + int32_t (*Touch)(PP_Resource file_io, + PP_Time last_access_time, + PP_Time last_modified_time, + struct PP_CompletionCallback callback); + int32_t (*Read)(PP_Resource file_io, + int64_t offset, + char* buffer, + int32_t bytes_to_read, + struct PP_CompletionCallback callback); + int32_t (*Write)(PP_Resource file_io, + int64_t offset, + const char* buffer, + int32_t bytes_to_write, + struct PP_CompletionCallback callback); + int32_t (*SetLength)(PP_Resource file_io, + int64_t length, + struct PP_CompletionCallback callback); + int32_t (*Flush)(PP_Resource file_io, struct PP_CompletionCallback callback); + void (*Close)(PP_Resource file_io); +}; /** * @} */ diff --git a/ppapi/cpp/array_output.h b/ppapi/cpp/array_output.h index 57fa790..fe0bbc3 100644 --- a/ppapi/cpp/array_output.h +++ b/ppapi/cpp/array_output.h @@ -176,7 +176,7 @@ template<typename T> class ArrayOutputAdapterWithStorage : public ArrayOutputAdapter<T> { public: ArrayOutputAdapterWithStorage() { - set_output(&output_storage_); + this->set_output(&output_storage_); } std::vector<T>& output() { return output_storage_; } diff --git a/ppapi/cpp/file_io.cc b/ppapi/cpp/file_io.cc index e596577..faaf284 100644 --- a/ppapi/cpp/file_io.cc +++ b/ppapi/cpp/file_io.cc @@ -4,9 +4,12 @@ #include "ppapi/cpp/file_io.h" +#include <string.h> // memcpy + #include "ppapi/c/ppb_file_io.h" #include "ppapi/c/pp_errors.h" #include "ppapi/cpp/completion_callback.h" +#include "ppapi/cpp/dev/resource_array_dev.h" #include "ppapi/cpp/file_ref.h" #include "ppapi/cpp/instance_handle.h" #include "ppapi/cpp/module.h" @@ -20,16 +23,23 @@ template <> const char* interface_name<PPB_FileIO_1_0>() { return PPB_FILEIO_INTERFACE_1_0; } +template <> const char* interface_name<PPB_FileIO_1_1>() { + return PPB_FILEIO_INTERFACE_1_1; +} + } // namespace FileIO::FileIO() { } FileIO::FileIO(const InstanceHandle& instance) { - if (!has_interface<PPB_FileIO_1_0>()) - return; - PassRefFromConstructor(get_interface<PPB_FileIO_1_0>()->Create( - instance.pp_instance())); + if (has_interface<PPB_FileIO_1_1>()) { + PassRefFromConstructor(get_interface<PPB_FileIO_1_1>()->Create( + instance.pp_instance())); + } else if (has_interface<PPB_FileIO_1_0>()) { + PassRefFromConstructor(get_interface<PPB_FileIO_1_0>()->Create( + instance.pp_instance())); + } } FileIO::FileIO(const FileIO& other) @@ -39,71 +49,147 @@ FileIO::FileIO(const FileIO& other) int32_t FileIO::Open(const FileRef& file_ref, int32_t open_flags, const CompletionCallback& cc) { - if (!has_interface<PPB_FileIO_1_0>()) - return cc.MayForce(PP_ERROR_NOINTERFACE); - return get_interface<PPB_FileIO_1_0>()->Open( - pp_resource(), file_ref.pp_resource(), open_flags, - cc.pp_completion_callback()); + if (has_interface<PPB_FileIO_1_1>()) { + return get_interface<PPB_FileIO_1_1>()->Open( + pp_resource(), file_ref.pp_resource(), open_flags, + cc.pp_completion_callback()); + } else if (has_interface<PPB_FileIO_1_0>()) { + return get_interface<PPB_FileIO_1_0>()->Open( + pp_resource(), file_ref.pp_resource(), open_flags, + cc.pp_completion_callback()); + } + return cc.MayForce(PP_ERROR_NOINTERFACE); } int32_t FileIO::Query(PP_FileInfo* result_buf, const CompletionCallback& cc) { - if (!has_interface<PPB_FileIO_1_0>()) - return cc.MayForce(PP_ERROR_NOINTERFACE); - return get_interface<PPB_FileIO_1_0>()->Query( - pp_resource(), result_buf, cc.pp_completion_callback()); + if (has_interface<PPB_FileIO_1_1>()) { + return get_interface<PPB_FileIO_1_1>()->Query( + pp_resource(), result_buf, cc.pp_completion_callback()); + } else if (has_interface<PPB_FileIO_1_0>()) { + return get_interface<PPB_FileIO_1_0>()->Query( + pp_resource(), result_buf, cc.pp_completion_callback()); + } + return cc.MayForce(PP_ERROR_NOINTERFACE); } int32_t FileIO::Touch(PP_Time last_access_time, PP_Time last_modified_time, const CompletionCallback& cc) { - if (!has_interface<PPB_FileIO_1_0>()) - return cc.MayForce(PP_ERROR_NOINTERFACE); - return get_interface<PPB_FileIO_1_0>()->Touch( - pp_resource(), last_access_time, last_modified_time, - cc.pp_completion_callback()); + if (has_interface<PPB_FileIO_1_1>()) { + return get_interface<PPB_FileIO_1_1>()->Touch( + pp_resource(), last_access_time, last_modified_time, + cc.pp_completion_callback()); + } else if (has_interface<PPB_FileIO_1_0>()) { + return get_interface<PPB_FileIO_1_0>()->Touch( + pp_resource(), last_access_time, last_modified_time, + cc.pp_completion_callback()); + } + return cc.MayForce(PP_ERROR_NOINTERFACE); } int32_t FileIO::Read(int64_t offset, char* buffer, int32_t bytes_to_read, const CompletionCallback& cc) { - if (!has_interface<PPB_FileIO_1_0>()) - return cc.MayForce(PP_ERROR_NOINTERFACE); - return get_interface<PPB_FileIO_1_0>()->Read(pp_resource(), - offset, buffer, bytes_to_read, cc.pp_completion_callback()); + if (has_interface<PPB_FileIO_1_1>()) { + return get_interface<PPB_FileIO_1_1>()->Read(pp_resource(), + offset, buffer, bytes_to_read, cc.pp_completion_callback()); + } else if (has_interface<PPB_FileIO_1_0>()) { + return get_interface<PPB_FileIO_1_0>()->Read(pp_resource(), + offset, buffer, bytes_to_read, cc.pp_completion_callback()); + } + return cc.MayForce(PP_ERROR_NOINTERFACE); +} + +int32_t FileIO::Read( + int32_t offset, + int32_t max_read_length, + const CompletionCallbackWithOutput< std::vector<char> >& cc) { + if (has_interface<PPB_FileIO_1_1>()) { + PP_ArrayOutput array_output = cc.output(); + return get_interface<PPB_FileIO_1_1>()->ReadToArray(pp_resource(), + offset, max_read_length, &array_output, + cc.pp_completion_callback()); + } else if (has_interface<PPB_FileIO_1_0>()) { + // Data for our callback wrapper. The callback handler will delete it and + // temp_buffer. + CallbackData1_0* data = new CallbackData1_0; + data->output = cc.output(); + data->temp_buffer = max_read_length >= 0 ? new char[max_read_length] : NULL; + data->original_callback = cc.pp_completion_callback(); + + // Actual returned bytes might not equals to max_read_length. We need to + // read to a temporary buffer first and copy later to make sure the array + // buffer has correct size. + return get_interface<PPB_FileIO_1_0>()->Read( + pp_resource(), offset, data->temp_buffer, max_read_length, + PP_MakeCompletionCallback(&CallbackConverter, data)); + } + return cc.MayForce(PP_ERROR_NOINTERFACE); } int32_t FileIO::Write(int64_t offset, const char* buffer, int32_t bytes_to_write, const CompletionCallback& cc) { - if (!has_interface<PPB_FileIO_1_0>()) - return cc.MayForce(PP_ERROR_NOINTERFACE); - return get_interface<PPB_FileIO_1_0>()->Write( - pp_resource(), offset, buffer, bytes_to_write, - cc.pp_completion_callback()); + if (has_interface<PPB_FileIO_1_1>()) { + return get_interface<PPB_FileIO_1_1>()->Write( + pp_resource(), offset, buffer, bytes_to_write, + cc.pp_completion_callback()); + } else if (has_interface<PPB_FileIO_1_0>()) { + return get_interface<PPB_FileIO_1_0>()->Write( + pp_resource(), offset, buffer, bytes_to_write, + cc.pp_completion_callback()); + } + return cc.MayForce(PP_ERROR_NOINTERFACE); } int32_t FileIO::SetLength(int64_t length, const CompletionCallback& cc) { - if (!has_interface<PPB_FileIO_1_0>()) - return cc.MayForce(PP_ERROR_NOINTERFACE); - return get_interface<PPB_FileIO_1_0>()->SetLength( - pp_resource(), length, cc.pp_completion_callback()); + if (has_interface<PPB_FileIO_1_1>()) { + return get_interface<PPB_FileIO_1_1>()->SetLength( + pp_resource(), length, cc.pp_completion_callback()); + } else if (has_interface<PPB_FileIO_1_0>()) { + return get_interface<PPB_FileIO_1_0>()->SetLength( + pp_resource(), length, cc.pp_completion_callback()); + } + return cc.MayForce(PP_ERROR_NOINTERFACE); } int32_t FileIO::Flush(const CompletionCallback& cc) { - if (!has_interface<PPB_FileIO_1_0>()) - return cc.MayForce(PP_ERROR_NOINTERFACE); - return get_interface<PPB_FileIO_1_0>()->Flush( - pp_resource(), cc.pp_completion_callback()); + if (has_interface<PPB_FileIO_1_1>()) { + return get_interface<PPB_FileIO_1_1>()->Flush( + pp_resource(), cc.pp_completion_callback()); + } else if (has_interface<PPB_FileIO_1_0>()) { + return get_interface<PPB_FileIO_1_0>()->Flush( + pp_resource(), cc.pp_completion_callback()); + } + return cc.MayForce(PP_ERROR_NOINTERFACE); } void FileIO::Close() { - if (!has_interface<PPB_FileIO_1_0>()) - return; - get_interface<PPB_FileIO_1_0>()->Close(pp_resource()); + if (has_interface<PPB_FileIO_1_1>()) + get_interface<PPB_FileIO_1_1>()->Close(pp_resource()); + else if (has_interface<PPB_FileIO_1_0>()) + get_interface<PPB_FileIO_1_0>()->Close(pp_resource()); +} + +// static +void FileIO::CallbackConverter(void* user_data, int32_t result) { + CallbackData1_0* data = static_cast<CallbackData1_0*>(user_data); + + if (result >= 0) { + // Copy to the destination buffer owned by the callback. + char* buffer = static_cast<char*>(data->output.GetDataBuffer( + data->output.user_data, result, sizeof(char))); + memcpy(buffer, data->temp_buffer, result); + delete[] data->temp_buffer; + } + + // Now execute the original callback. + PP_RunCompletionCallback(&data->original_callback, result); + delete data; } } // namespace pp diff --git a/ppapi/cpp/file_io.h b/ppapi/cpp/file_io.h index 0d8dcc9..3b93bfe 100644 --- a/ppapi/cpp/file_io.h +++ b/ppapi/cpp/file_io.h @@ -6,6 +6,7 @@ #define PPAPI_CPP_FILE_IO_H_ #include "ppapi/c/pp_time.h" +#include "ppapi/cpp/completion_callback.h" #include "ppapi/cpp/resource.h" /// @file @@ -15,7 +16,6 @@ struct PP_FileInfo; namespace pp { -class CompletionCallback; class FileRef; class InstanceHandle; @@ -87,6 +87,19 @@ class FileIO : public Resource { /// large enough to hold the specified number of bytes to read. This /// function might perform a partial read. /// + /// <strong>Caveat:</strong> This Read() is potentially unsafe if you're using + /// a CompletionCallbackFactory to scope callbacks to the lifetime of your + /// class. When your class goes out of scope, the callback factory will not + /// actually cancel the callback, but will rather just skip issuing the + /// callback on your class. This means that if the FileIO object outlives + /// your class (if you made a copy saved somewhere else, for example), then + /// the browser will still try to write into your buffer when the + /// asynchronous read completes, potentially causing a crash. + /// + /// See the other version of Read() which avoids this problem by writing into + /// CompletionCallbackWithOutput, where the output buffer is automatically + /// managed by the callback. + /// /// @param[in] offset The offset into the file. /// @param[in] buffer The buffer to hold the specified number of bytes read. /// @param[in] bytes_to_read The number of bytes to read from @@ -103,6 +116,28 @@ class FileIO : public Resource { int32_t bytes_to_read, const CompletionCallback& cc); + /// Read() reads from an offset in the file. A PP_ArrayOutput must be + /// provided so that output will be stored in its allocated buffer. This + /// function might perform a partial read. + /// + /// @param[in] file_io A <code>PP_Resource</code> corresponding to a file + /// FileIO. + /// @param[in] offset The offset into the file. + /// @param[in] max_read_length The maximum number of bytes to read from + /// <code>offset</code>. + /// @param[in] output A <code>PP_ArrayOutput</code> to hold the output data. + /// @param[in] callback A <code>PP_CompletionCallback</code> to be called upon + /// completion of Read(). + /// + /// @return The number of bytes read or an error code from + /// <code>pp_errors.h</code>. If the return value is 0, then end-of-file was + /// reached. It is valid to call Read() multiple times with a completion + /// callback to queue up parallel reads from the file, but pending reads + /// cannot be interleaved with other operations. + int32_t Read(int32_t offset, + int32_t max_read_length, + const CompletionCallbackWithOutput< std::vector<char> >& cc); + /// Write() writes to an offset in the file. This function might perform a /// partial write. The FileIO object must have been opened with write access. /// @@ -153,6 +188,21 @@ class FileIO : public Resource { /// open, then it will be implicitly closed, so you are not required to call /// Close(). void Close(); + + private: + struct CallbackData1_0 { + PP_ArrayOutput output; + char* temp_buffer; + PP_CompletionCallback original_callback; + }; + + // Provide backwards-compatability for older Read versions. Converts the + // old-style "char*" output buffer of 1.0 to the new "PP_ArrayOutput" + // interface in 1.1. + // + // This takes a heap-allocated CallbackData1_0 struct passed as the user data + // and deletes it when the call completes. + static void CallbackConverter(void* user_data, int32_t result); }; } // namespace pp diff --git a/ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_file_io.cc b/ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_file_io.cc index 1cdfe7a..b538532 100644 --- a/ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_file_io.cc +++ b/ppapi/native_client/src/shared/ppapi_proxy/plugin_ppb_file_io.cc @@ -295,7 +295,8 @@ const PPB_FileIO* PluginFileIO::GetInterface() { Write, SetLength, Flush, - Close + Close, + NULL // TODO: support ReadToArray }; return &file_io_interface; } diff --git a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c index a3b055c..1231f031 100644 --- a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c +++ b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c @@ -119,6 +119,7 @@ static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_AudioConfig_1_0; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_AudioConfig_1_1; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Core_1_0; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_FileIO_1_0; +static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_FileIO_1_1; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_FileRef_1_0; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_FileSystem_1_0; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Fullscreen_1_0; @@ -356,6 +357,76 @@ void Pnacl_M14_PPB_FileIO_Close(PP_Resource file_io) { /* End wrapper methods for PPB_FileIO_1_0 */ +/* Begin wrapper methods for PPB_FileIO_1_1 */ + +static __attribute__((pnaclcall)) +PP_Resource Pnacl_M25_PPB_FileIO_Create(PP_Instance instance) { + const struct PPB_FileIO_1_1 *iface = Pnacl_WrapperInfo_PPB_FileIO_1_1.real_iface; + return iface->Create(instance); +} + +static __attribute__((pnaclcall)) +PP_Bool Pnacl_M25_PPB_FileIO_IsFileIO(PP_Resource resource) { + const struct PPB_FileIO_1_1 *iface = Pnacl_WrapperInfo_PPB_FileIO_1_1.real_iface; + return iface->IsFileIO(resource); +} + +static __attribute__((pnaclcall)) +int32_t Pnacl_M25_PPB_FileIO_Open(PP_Resource file_io, PP_Resource file_ref, int32_t open_flags, struct PP_CompletionCallback callback) { + const struct PPB_FileIO_1_1 *iface = Pnacl_WrapperInfo_PPB_FileIO_1_1.real_iface; + return iface->Open(file_io, file_ref, open_flags, callback); +} + +static __attribute__((pnaclcall)) +int32_t Pnacl_M25_PPB_FileIO_Query(PP_Resource file_io, struct PP_FileInfo* info, struct PP_CompletionCallback callback) { + const struct PPB_FileIO_1_1 *iface = Pnacl_WrapperInfo_PPB_FileIO_1_1.real_iface; + return iface->Query(file_io, info, callback); +} + +static __attribute__((pnaclcall)) +int32_t Pnacl_M25_PPB_FileIO_Touch(PP_Resource file_io, PP_Time last_access_time, PP_Time last_modified_time, struct PP_CompletionCallback callback) { + const struct PPB_FileIO_1_1 *iface = Pnacl_WrapperInfo_PPB_FileIO_1_1.real_iface; + return iface->Touch(file_io, last_access_time, last_modified_time, callback); +} + +static __attribute__((pnaclcall)) +int32_t Pnacl_M25_PPB_FileIO_Read(PP_Resource file_io, int64_t offset, char* buffer, int32_t bytes_to_read, struct PP_CompletionCallback callback) { + const struct PPB_FileIO_1_1 *iface = Pnacl_WrapperInfo_PPB_FileIO_1_1.real_iface; + return iface->Read(file_io, offset, buffer, bytes_to_read, callback); +} + +static __attribute__((pnaclcall)) +int32_t Pnacl_M25_PPB_FileIO_Write(PP_Resource file_io, int64_t offset, const char* buffer, int32_t bytes_to_write, struct PP_CompletionCallback callback) { + const struct PPB_FileIO_1_1 *iface = Pnacl_WrapperInfo_PPB_FileIO_1_1.real_iface; + return iface->Write(file_io, offset, buffer, bytes_to_write, callback); +} + +static __attribute__((pnaclcall)) +int32_t Pnacl_M25_PPB_FileIO_SetLength(PP_Resource file_io, int64_t length, struct PP_CompletionCallback callback) { + const struct PPB_FileIO_1_1 *iface = Pnacl_WrapperInfo_PPB_FileIO_1_1.real_iface; + return iface->SetLength(file_io, length, callback); +} + +static __attribute__((pnaclcall)) +int32_t Pnacl_M25_PPB_FileIO_Flush(PP_Resource file_io, struct PP_CompletionCallback callback) { + const struct PPB_FileIO_1_1 *iface = Pnacl_WrapperInfo_PPB_FileIO_1_1.real_iface; + return iface->Flush(file_io, callback); +} + +static __attribute__((pnaclcall)) +void Pnacl_M25_PPB_FileIO_Close(PP_Resource file_io) { + const struct PPB_FileIO_1_1 *iface = Pnacl_WrapperInfo_PPB_FileIO_1_1.real_iface; + iface->Close(file_io); +} + +static __attribute__((pnaclcall)) +int32_t Pnacl_M25_PPB_FileIO_ReadToArray(PP_Resource file_io, int64_t offset, int32_t max_read_length, struct PP_ArrayOutput* output, struct PP_CompletionCallback callback) { + const struct PPB_FileIO_1_1 *iface = Pnacl_WrapperInfo_PPB_FileIO_1_1.real_iface; + return iface->ReadToArray(file_io, offset, max_read_length, output, callback); +} + +/* End wrapper methods for PPB_FileIO_1_1 */ + /* Begin wrapper methods for PPB_FileRef_1_0 */ static __attribute__((pnaclcall)) @@ -3466,6 +3537,20 @@ struct PPB_FileIO_1_0 Pnacl_Wrappers_PPB_FileIO_1_0 = { .Close = (void (*)(PP_Resource file_io))&Pnacl_M14_PPB_FileIO_Close }; +struct PPB_FileIO_1_1 Pnacl_Wrappers_PPB_FileIO_1_1 = { + .Create = (PP_Resource (*)(PP_Instance instance))&Pnacl_M25_PPB_FileIO_Create, + .IsFileIO = (PP_Bool (*)(PP_Resource resource))&Pnacl_M25_PPB_FileIO_IsFileIO, + .Open = (int32_t (*)(PP_Resource file_io, PP_Resource file_ref, int32_t open_flags, struct PP_CompletionCallback callback))&Pnacl_M25_PPB_FileIO_Open, + .Query = (int32_t (*)(PP_Resource file_io, struct PP_FileInfo* info, struct PP_CompletionCallback callback))&Pnacl_M25_PPB_FileIO_Query, + .Touch = (int32_t (*)(PP_Resource file_io, PP_Time last_access_time, PP_Time last_modified_time, struct PP_CompletionCallback callback))&Pnacl_M25_PPB_FileIO_Touch, + .Read = (int32_t (*)(PP_Resource file_io, int64_t offset, char* buffer, int32_t bytes_to_read, struct PP_CompletionCallback callback))&Pnacl_M25_PPB_FileIO_Read, + .Write = (int32_t (*)(PP_Resource file_io, int64_t offset, const char* buffer, int32_t bytes_to_write, struct PP_CompletionCallback callback))&Pnacl_M25_PPB_FileIO_Write, + .SetLength = (int32_t (*)(PP_Resource file_io, int64_t length, struct PP_CompletionCallback callback))&Pnacl_M25_PPB_FileIO_SetLength, + .Flush = (int32_t (*)(PP_Resource file_io, struct PP_CompletionCallback callback))&Pnacl_M25_PPB_FileIO_Flush, + .Close = (void (*)(PP_Resource file_io))&Pnacl_M25_PPB_FileIO_Close, + .ReadToArray = (int32_t (*)(PP_Resource file_io, int64_t offset, int32_t max_read_length, struct PP_ArrayOutput* output, struct PP_CompletionCallback callback))&Pnacl_M25_PPB_FileIO_ReadToArray +}; + struct PPB_FileRef_1_0 Pnacl_Wrappers_PPB_FileRef_1_0 = { .Create = (PP_Resource (*)(PP_Resource file_system, const char* path))&Pnacl_M14_PPB_FileRef_Create, .IsFileRef = (PP_Bool (*)(PP_Resource resource))&Pnacl_M14_PPB_FileRef_IsFileRef, @@ -4266,6 +4351,12 @@ static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_FileIO_1_0 = { .real_iface = NULL }; +static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_FileIO_1_1 = { + .iface_macro = PPB_FILEIO_INTERFACE_1_1, + .wrapped_iface = (void *) &Pnacl_Wrappers_PPB_FileIO_1_1, + .real_iface = NULL +}; + static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_FileRef_1_0 = { .iface_macro = PPB_FILEREF_INTERFACE_1_0, .wrapped_iface = (void *) &Pnacl_Wrappers_PPB_FileRef_1_0, @@ -5022,6 +5113,7 @@ static struct __PnaclWrapperInfo *s_ppb_wrappers[] = { &Pnacl_WrapperInfo_PPB_AudioConfig_1_1, &Pnacl_WrapperInfo_PPB_Core_1_0, &Pnacl_WrapperInfo_PPB_FileIO_1_0, + &Pnacl_WrapperInfo_PPB_FileIO_1_1, &Pnacl_WrapperInfo_PPB_FileRef_1_0, &Pnacl_WrapperInfo_PPB_FileSystem_1_0, &Pnacl_WrapperInfo_PPB_Fullscreen_1_0, diff --git a/ppapi/proxy/ppb_file_io_proxy.cc b/ppapi/proxy/ppb_file_io_proxy.cc index e367da4..002efdf 100644 --- a/ppapi/proxy/ppb_file_io_proxy.cc +++ b/ppapi/proxy/ppb_file_io_proxy.cc @@ -64,9 +64,9 @@ class FileIO : public PPB_FileIO_Shared { scoped_refptr<TrackedCallback> callback) OVERRIDE; virtual int32_t ReadValidated( int64_t offset, - char* buffer, - int32_t bytes_to_read, - scoped_refptr<TrackedCallback> callback) OVERRIDE; + const PP_ArrayOutput& output_array_buffer, + int32_t max_read_length, + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; virtual int32_t WriteValidated( int64_t offset, const char* buffer, @@ -154,12 +154,12 @@ int32_t FileIO::TouchValidated(PP_Time last_access_time, } int32_t FileIO::ReadValidated(int64_t offset, - char* buffer, - int32_t bytes_to_read, + const PP_ArrayOutput& output_array_buffer, + int32_t max_read_length, scoped_refptr<TrackedCallback> callback) { GetDispatcher()->Send(new PpapiHostMsg_PPBFileIO_Read( - kApiID, host_resource(), offset, bytes_to_read)); - RegisterCallback(OPERATION_READ, callback, buffer, NULL); + kApiID, host_resource(), offset, max_read_length)); + RegisterCallback(OPERATION_READ, callback, &output_array_buffer, NULL); return PP_OK_COMPLETIONPENDING; } diff --git a/ppapi/shared_impl/array_writer.h b/ppapi/shared_impl/array_writer.h index cabd0af..b761a07 100644 --- a/ppapi/shared_impl/array_writer.h +++ b/ppapi/shared_impl/array_writer.h @@ -38,7 +38,8 @@ class PPAPI_SHARED_EXPORT ArrayWriter { // Sets the array output back to its is_null() state. void Reset(); - // Copies the given vector of data to the plugin output array. + // StoreArray() and StoreVector() copy the given array/vector of data to the + // plugin output array. // // Returns true on success, false if the plugin reported allocation failure. // In either case, the object will become is_null() immediately after the @@ -48,27 +49,35 @@ class PPAPI_SHARED_EXPORT ArrayWriter { // want to transfer a reference only on success. Likewise, if you have a // structure of PP_Vars or a struct that contains a PP_Resource, we need to // make sure that the right thing happens with the ref on success and failure. - template<typename T> - bool StoreVector(const std::vector<T>& input) { + template <typename T> + bool StoreArray(const T* input, uint32_t count) { // Always call the alloc function, even on 0 array size. void* dest = pp_array_output_.GetDataBuffer( pp_array_output_.user_data, - static_cast<uint32_t>(input.size()), + count, sizeof(T)); // Regardless of success, we clear the output to prevent future calls on // this same output object. Reset(); - if (input.empty()) + if (count == 0) return true; // Allow plugin to return NULL on 0 elements. if (!dest) return false; - memcpy(dest, &input[0], sizeof(T) * input.size()); + if (input) + memcpy(dest, input, sizeof(T) * count); return true; } + // Copies the given array/vector of data to the plugin output array. See + // comment of StoreArray() for detail. + template<typename T> + bool StoreVector(const std::vector<T>& input) { + return StoreArray(input.size() ? &input[0] : NULL, input.size()); + } + // Stores the given vector of resources as PP_Resources to the output vector, // adding one reference to each. // diff --git a/ppapi/shared_impl/ppb_file_io_shared.cc b/ppapi/shared_impl/ppb_file_io_shared.cc index b60e174..42a8d32 100644 --- a/ppapi/shared_impl/ppb_file_io_shared.cc +++ b/ppapi/shared_impl/ppb_file_io_shared.cc @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/message_loop.h" #include "ppapi/c/pp_errors.h" +#include "ppapi/shared_impl/array_writer.h" #include "ppapi/shared_impl/file_type_conversion.h" #include "ppapi/shared_impl/time_conversion.h" #include "ppapi/thunk/enter.h" @@ -21,9 +22,17 @@ using thunk::EnterResourceNoLock; using thunk::PPB_FileIO_API; using thunk::PPB_FileRef_API; +namespace { + +// An adapter to let Read() share the same implementation with ReadToArray(). +void* DummyGetDataBuffer(void* user_data, uint32_t count, uint32_t size) { + return user_data; +} + +} // namespace + PPB_FileIO_Shared::CallbackEntry::CallbackEntry() - : read_buffer(NULL), - info(NULL) { + : info(NULL) { } PPB_FileIO_Shared::CallbackEntry::CallbackEntry(const CallbackEntry& entry) @@ -103,7 +112,21 @@ int32_t PPB_FileIO_Shared::Read(int64_t offset, int32_t rv = CommonCallValidation(true, OPERATION_READ); if (rv != PP_OK) return rv; - return ReadValidated(offset, buffer, bytes_to_read, callback); + PP_ArrayOutput buffer_adapter = { &DummyGetDataBuffer, buffer }; + return ReadValidated(offset, buffer_adapter, bytes_to_read, callback); +} + +int32_t PPB_FileIO_Shared::ReadToArray( + int64_t offset, + int32_t max_read_length, + PP_ArrayOutput* output_array_buffer, + scoped_refptr<TrackedCallback> callback) { + int32_t rv = CommonCallValidation(true, OPERATION_READ); + if (rv != PP_OK) + return rv; + if (!output_array_buffer) + return PP_ERROR_BADARGUMENT; + return ReadValidated(offset, *output_array_buffer, max_read_length, callback); } int32_t PPB_FileIO_Shared::Write(int64_t offset, @@ -152,7 +175,7 @@ void PPB_FileIO_Shared::ExecuteQueryCallback(int32_t pp_error, RunAndRemoveFirstPendingCallback(pp_error); } -void PPB_FileIO_Shared::ExecuteReadCallback(int32_t pp_error, +void PPB_FileIO_Shared::ExecuteReadCallback(int32_t pp_error_or_bytes, const char* data) { if (pending_op_ != OPERATION_READ || callbacks_.empty()) { NOTREACHED(); @@ -160,13 +183,14 @@ void PPB_FileIO_Shared::ExecuteReadCallback(int32_t pp_error, } // The result code contains the number of bytes if it's positive. - if (pp_error > 0) { - char* read_buffer = callbacks_.front().read_buffer; - DCHECK(data); - DCHECK(read_buffer); - memcpy(read_buffer, data, pp_error); + ArrayWriter output; + output.set_pp_array_output(callbacks_.front().read_buffer); + if (output.is_valid()) { + output.StoreArray(data, std::max(0, pp_error_or_bytes)); } - RunAndRemoveFirstPendingCallback(pp_error); + + // Always issue the callback. + RunAndRemoveFirstPendingCallback(pp_error_or_bytes); } int32_t PPB_FileIO_Shared::CommonCallValidation(bool should_be_open, @@ -191,14 +215,15 @@ int32_t PPB_FileIO_Shared::CommonCallValidation(bool should_be_open, void PPB_FileIO_Shared::RegisterCallback( OperationType op, scoped_refptr<TrackedCallback> callback, - char* read_buffer, + const PP_ArrayOutput* read_buffer, PP_FileInfo* info) { DCHECK(pending_op_ == OPERATION_NONE || (pending_op_ != OPERATION_EXCLUSIVE && pending_op_ == op)); CallbackEntry entry; entry.callback = callback; - entry.read_buffer = read_buffer; + if (read_buffer) + entry.read_buffer = *read_buffer; entry.info = info; callbacks_.push_back(entry); diff --git a/ppapi/shared_impl/ppb_file_io_shared.h b/ppapi/shared_impl/ppb_file_io_shared.h index a062497..2c39c8d 100644 --- a/ppapi/shared_impl/ppb_file_io_shared.h +++ b/ppapi/shared_impl/ppb_file_io_shared.h @@ -42,6 +42,10 @@ class PPAPI_SHARED_EXPORT PPB_FileIO_Shared : public Resource, char* buffer, int32_t bytes_to_read, scoped_refptr<TrackedCallback> callback) OVERRIDE; + virtual int32_t ReadToArray(int64_t offset, + int32_t max_read_length, + PP_ArrayOutput* output_array_buffer, + scoped_refptr<TrackedCallback> callback) OVERRIDE; virtual int32_t Write(int64_t offset, const char* buffer, int32_t bytes_to_write, @@ -54,7 +58,7 @@ class PPAPI_SHARED_EXPORT PPB_FileIO_Shared : public Resource, 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); + void ExecuteReadCallback(int32_t pp_error_or_bytes, const char* data); protected: struct CallbackEntry { @@ -64,9 +68,8 @@ class PPAPI_SHARED_EXPORT PPB_FileIO_Shared : public Resource, scoped_refptr<TrackedCallback> callback; - // Pointer back to the caller's read buffer; only used by |Read()|, NULL - // for non-read operations. Not owned. - char* read_buffer; + // Output buffer used only by |Read()|. + PP_ArrayOutput read_buffer; // Pointer back to the caller's PP_FileInfo structure for Query operations. // NULL for non-query operations. Not owned. @@ -102,8 +105,8 @@ class PPAPI_SHARED_EXPORT PPB_FileIO_Shared : public Resource, PP_Time last_modified_time, scoped_refptr<TrackedCallback> callback) = 0; virtual int32_t ReadValidated(int64_t offset, - char* buffer, - int32_t bytes_to_read, + const PP_ArrayOutput& buffer, + int32_t max_read_length, scoped_refptr<TrackedCallback> callback) = 0; virtual int32_t WriteValidated(int64_t offset, const char* buffer, @@ -131,7 +134,7 @@ class PPAPI_SHARED_EXPORT PPB_FileIO_Shared : public Resource, // query operations. void RegisterCallback(OperationType op, scoped_refptr<TrackedCallback> callback, - char* read_buffer, + const PP_ArrayOutput* read_buffer, PP_FileInfo* info); // Pops the oldest callback from the queue and runs it. diff --git a/ppapi/tests/test_file_io.cc b/ppapi/tests/test_file_io.cc index c61b31d..9f88f00 100644 --- a/ppapi/tests/test_file_io.cc +++ b/ppapi/tests/test_file_io.cc @@ -82,6 +82,29 @@ int32_t ReadEntireFile(PP_Instance instance, return PP_OK; } +int32_t ReadToArrayEntireFile(PP_Instance instance, + pp::FileIO* file_io, + int32_t offset, + std::string* data, + bool force_async) { + TestCompletionCallbackWithOutput< std::vector<char> > callback( + instance, force_async); + + for (;;) { + callback.WaitForResult(file_io->Read(offset, 256, callback)); + int32_t rv = callback.result(); + if (rv < 0) + return rv; + if (rv == 0) + break; + assert(rv == static_cast<int32_t>(callback.output().size())); + offset += rv; + data->append(callback.output().begin(), callback.output().end()); + } + + return PP_OK; +} + int32_t WriteEntireBuffer(PP_Instance instance, pp::FileIO* file_io, int32_t offset, @@ -115,6 +138,7 @@ bool TestFileIO::Init() { void TestFileIO::RunTests(const std::string& filter) { RUN_TEST_FORCEASYNC_AND_NOT(Open, filter); RUN_TEST_FORCEASYNC_AND_NOT(ReadWriteSetLength, filter); + RUN_TEST_FORCEASYNC_AND_NOT(ReadToArrayWriteSetLength, filter); RUN_TEST_FORCEASYNC_AND_NOT(TouchQuery, filter); RUN_TEST_FORCEASYNC_AND_NOT(AbortCalls, filter); RUN_TEST_FORCEASYNC_AND_NOT(ParallelReads, filter); @@ -357,6 +381,150 @@ std::string TestFileIO::TestReadWriteSetLength() { PASS(); } +// This is basically a copy of TestReadWriteSetLength, but with the new Read +// API. With this test case, we can make sure the two Read's have the same +// behavior. +std::string TestFileIO::TestReadToArrayWriteSetLength() { + TestCompletionCallback callback(instance_->pp_instance(), force_async_); + + pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); + pp::FileRef file_ref(file_system, "/file_read_write_setlength"); + int32_t rv = file_system.Open(1024, callback); + if (force_async_ && rv != PP_OK_COMPLETIONPENDING) + return ReportError("FileSystem::Open force_async", rv); + if (rv == PP_OK_COMPLETIONPENDING) + rv = callback.WaitForResult(); + if (rv != PP_OK) + return ReportError("FileSystem::Open", rv); + + pp::FileIO file_io(instance_); + rv = file_io.Open(file_ref, + PP_FILEOPENFLAG_CREATE | + PP_FILEOPENFLAG_TRUNCATE | + PP_FILEOPENFLAG_READ | + PP_FILEOPENFLAG_WRITE, + callback); + if (force_async_ && rv != PP_OK_COMPLETIONPENDING) + return ReportError("FileIO::Open force_async", rv); + if (rv == PP_OK_COMPLETIONPENDING) + rv = callback.WaitForResult(); + if (rv != PP_OK) + return ReportError("FileIO::Open", rv); + + // Write something to the file. + rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0, "test_test"); + if (rv != PP_OK) + return ReportError("FileIO::Write", rv); + + TestCompletionCallbackWithOutput< std::vector<char> > callback2( + instance_->pp_instance(), force_async_); + // Check for failing read operation. + callback2.WaitForResult( + file_io.Read(0, -1, // negative number of bytes to read + callback2)); + CHECK_CALLBACK_BEHAVIOR(callback2); + if (callback2.result() != PP_ERROR_FAILED) + return ReportError("FileIO::Read", callback2.result()); + + // Read the entire file. + std::string read_buffer; + read_buffer.reserve(10); + rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0, + &read_buffer, force_async_); + if (rv != PP_OK) + return ReportError("FileIO::Read", rv); + if (read_buffer != "test_test") + return ReportMismatch("FileIO::Read", read_buffer, "test_test"); + + // Truncate the file. + rv = file_io.SetLength(4, callback); + if (force_async_ && rv != PP_OK_COMPLETIONPENDING) + return ReportError("FileIO::SetLength force_async", rv); + if (rv == PP_OK_COMPLETIONPENDING) + rv = callback.WaitForResult(); + if (rv != PP_OK) + return ReportError("FileIO::SetLength", rv); + + // Check the file contents. + read_buffer.clear(); + rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0, + &read_buffer, force_async_); + if (rv != PP_OK) + return ReportError("FileIO::Read", rv); + if (read_buffer != "test") + return ReportMismatch("FileIO::Read", read_buffer, "test"); + + // Try to read past the end of the file. + read_buffer.clear(); + rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 100, + &read_buffer, force_async_); + if (rv != PP_OK) + return ReportError("FileIO::Read", rv); + if (!read_buffer.empty()) + return ReportMismatch("FileIO::Read", read_buffer, "<empty string>"); + + // Write past the end of the file. The file should be zero-padded. + rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 8, "test"); + if (rv != PP_OK) + return ReportError("FileIO::Write", rv); + + // Check the contents of the file. + read_buffer.clear(); + rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0, + &read_buffer, force_async_); + if (rv != PP_OK) + return ReportError("FileIO::Read", rv); + if (read_buffer != std::string("test\0\0\0\0test", 12)) + return ReportMismatch("FileIO::Read", read_buffer, + std::string("test\0\0\0\0test", 12)); + + // Extend the file. + rv = file_io.SetLength(16, callback); + if (force_async_ && rv != PP_OK_COMPLETIONPENDING) + return ReportError("FileIO::SetLength force_async", rv); + if (rv == PP_OK_COMPLETIONPENDING) + rv = callback.WaitForResult(); + if (rv != PP_OK) + return ReportError("FileIO::SetLength", rv); + + // Check the contents of the file. + read_buffer.clear(); + rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0, + &read_buffer, force_async_); + if (rv != PP_OK) + return ReportError("FileIO::Read", rv); + if (read_buffer != std::string("test\0\0\0\0test\0\0\0\0", 16)) + return ReportMismatch("FileIO::Read", read_buffer, + std::string("test\0\0\0\0test\0\0\0\0", 16)); + + // Write in the middle of the file. + rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 4, "test"); + if (rv != PP_OK) + return ReportError("FileIO::Write", rv); + + // Check the contents of the file. + read_buffer.clear(); + rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0, + &read_buffer, force_async_); + if (rv != PP_OK) + return ReportError("FileIO::Read", rv); + if (read_buffer != std::string("testtesttest\0\0\0\0", 16)) + return ReportMismatch("FileIO::Read", read_buffer, + std::string("testtesttest\0\0\0\0", 16)); + + // Read from the middle of the file. + read_buffer.clear(); + rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 4, + &read_buffer, force_async_); + if (rv != PP_OK) + return ReportError("FileIO::Read", rv); + if (read_buffer != std::string("testtest\0\0\0\0", 12)) + return ReportMismatch("FileIO::Read", read_buffer, + std::string("testtest\0\0\0\0", 12)); + + PASS(); +} + std::string TestFileIO::TestTouchQuery() { TestCompletionCallback callback(instance_->pp_instance(), force_async_); diff --git a/ppapi/tests/test_file_io.h b/ppapi/tests/test_file_io.h index b971105..bf1c798 100644 --- a/ppapi/tests/test_file_io.h +++ b/ppapi/tests/test_file_io.h @@ -10,6 +10,7 @@ #include "ppapi/tests/test_case.h" namespace pp { +class FileIO; class FileSystem; } // namespace pp @@ -38,6 +39,7 @@ class TestFileIO : public TestCase { std::string TestOpen(); std::string TestReadWriteSetLength(); + std::string TestReadToArrayWriteSetLength(); std::string TestTouchQuery(); std::string TestAbortCalls(); std::string TestParallelReads(); diff --git a/ppapi/tests/test_utils.h b/ppapi/tests/test_utils.h index 8ef2f4e..09e0b94 100644 --- a/ppapi/tests/test_utils.h +++ b/ppapi/tests/test_utils.h @@ -151,7 +151,7 @@ class TestCompletionCallback { // Reset so that this callback can be used again. void Reset(); - private: + protected: static void Handler(void* user_data, int32_t result); void RunMessageLoop(); void QuitMessageLoop(); @@ -172,6 +172,56 @@ class TestCompletionCallback { pp::MessageLoop target_loop_; }; +template <typename OutputT> +class TestCompletionCallbackWithOutput : public TestCompletionCallback { + public: + explicit TestCompletionCallbackWithOutput(PP_Instance instance) : + TestCompletionCallback(instance) { + } + + TestCompletionCallbackWithOutput(PP_Instance instance, bool force_async) : + TestCompletionCallback(instance, force_async) { + } + + TestCompletionCallbackWithOutput(PP_Instance instance, + CallbackType callback_type) : + TestCompletionCallback(instance, callback_type) { + } + + pp::CompletionCallbackWithOutput<OutputT> GetCallbackWithOutput(); + operator pp::CompletionCallbackWithOutput<OutputT>() { + return GetCallbackWithOutput(); + } + + const OutputT& output() { return output_storage_.output(); } + + typename pp::CompletionCallbackWithOutput<OutputT>::OutputStorageType + output_storage_; +}; + +template <typename OutputT> +pp::CompletionCallbackWithOutput<OutputT> +TestCompletionCallbackWithOutput<OutputT>::GetCallbackWithOutput() { + Reset(); + if (callback_type_ == PP_BLOCKING) { + pp::CompletionCallbackWithOutput<OutputT> cc( + &TestCompletionCallback::Handler, + this, + &output_storage_); + return cc; + } + + target_loop_ = pp::MessageLoop::GetCurrent(); + pp::CompletionCallbackWithOutput<OutputT> cc( + &TestCompletionCallback::Handler, + this, + &output_storage_); + if (callback_type_ == PP_OPTIONAL) + cc.set_flags(PP_COMPLETIONCALLBACK_FLAG_OPTIONAL); + return cc; +} + + // Verifies that the callback didn't record any errors. If the callback is run // in an unexpected way (e.g., if it's invoked asynchronously when the call // should have blocked), this returns an appropriate error string. diff --git a/ppapi/thunk/interfaces_ppb_public_stable.h b/ppapi/thunk/interfaces_ppb_public_stable.h index 19b299c..d3a74a6 100644 --- a/ppapi/thunk/interfaces_ppb_public_stable.h +++ b/ppapi/thunk/interfaces_ppb_public_stable.h @@ -51,6 +51,7 @@ UNPROXIED_API(PPB_AudioConfig) // Note: Core is special and is registered manually. PROXIED_IFACE(PPB_Audio, PPB_AUDIO_INTERFACE_1_0, PPB_Audio_1_0) PROXIED_IFACE(PPB_FileIO, PPB_FILEIO_INTERFACE_1_0, PPB_FileIO_1_0) +PROXIED_IFACE(PPB_FileIO, PPB_FILEIO_INTERFACE_1_1, PPB_FileIO_1_1) PROXIED_IFACE(PPB_FileRef, PPB_FILEREF_INTERFACE_1_0, PPB_FileRef_1_0) PROXIED_IFACE(PPB_FileSystem, PPB_FILESYSTEM_INTERFACE_1_0, PPB_FileSystem_1_0) PROXIED_IFACE(PPB_Graphics2D, PPB_GRAPHICS_2D_INTERFACE_1_0, PPB_Graphics2D_1_0) diff --git a/ppapi/thunk/ppb_file_io_api.h b/ppapi/thunk/ppb_file_io_api.h index 1e6e3ad..b7b3d90 100644 --- a/ppapi/thunk/ppb_file_io_api.h +++ b/ppapi/thunk/ppb_file_io_api.h @@ -31,6 +31,10 @@ class PPAPI_THUNK_EXPORT PPB_FileIO_API { char* buffer, int32_t bytes_to_read, scoped_refptr<TrackedCallback> callback) = 0; + virtual int32_t ReadToArray(int64_t offset, + int32_t max_read_length, + PP_ArrayOutput* buffer, + scoped_refptr<TrackedCallback> callback) = 0; virtual int32_t Write(int64_t offset, const char* buffer, int32_t bytes_to_write, diff --git a/ppapi/thunk/ppb_file_io_thunk.cc b/ppapi/thunk/ppb_file_io_thunk.cc index 8fcfa52..467958f 100644 --- a/ppapi/thunk/ppb_file_io_thunk.cc +++ b/ppapi/thunk/ppb_file_io_thunk.cc @@ -72,6 +72,18 @@ int32_t Read(PP_Resource file_io, enter.callback())); } +int32_t ReadToArray(PP_Resource file_io, + int64_t offset, + int32_t max_read_length, + PP_ArrayOutput* buffer, + PP_CompletionCallback callback) { + EnterFileIO enter(file_io, callback, true); + if (enter.failed()) + return enter.retval(); + return enter.SetResult(enter.object()->ReadToArray( + offset, max_read_length, buffer, enter.callback())); +} + int32_t Write(PP_Resource file_io, int64_t offset, const char* buffer, @@ -107,7 +119,21 @@ void Close(PP_Resource file_io) { enter.object()->Close(); } -const PPB_FileIO g_ppb_file_io_thunk = { +const PPB_FileIO_1_1 g_ppb_file_io_thunk = { + &Create, + &IsFileIO, + &Open, + &Query, + &Touch, + &Read, + &Write, + &SetLength, + &Flush, + &Close, + &ReadToArray +}; + +const PPB_FileIO_1_0 g_ppb_file_io_thunk_1_0 = { &Create, &IsFileIO, &Open, @@ -122,9 +148,13 @@ const PPB_FileIO g_ppb_file_io_thunk = { } // namespace -const PPB_FileIO_1_0* GetPPB_FileIO_1_0_Thunk() { +const PPB_FileIO_1_1* GetPPB_FileIO_1_1_Thunk() { return &g_ppb_file_io_thunk; } +const PPB_FileIO_1_0* GetPPB_FileIO_1_0_Thunk() { + return &g_ppb_file_io_thunk_1_0; +} + } // namespace thunk } // namespace ppapi diff --git a/webkit/plugins/ppapi/ppb_file_io_impl.cc b/webkit/plugins/ppapi/ppb_file_io_impl.cc index 8c59b7e..75e62a5 100644 --- a/webkit/plugins/ppapi/ppb_file_io_impl.cc +++ b/webkit/plugins/ppapi/ppb_file_io_impl.cc @@ -190,8 +190,8 @@ int32_t PPB_FileIO_Impl::TouchValidated( int32_t PPB_FileIO_Impl::ReadValidated( int64_t offset, - char* buffer, - int32_t bytes_to_read, + const PP_ArrayOutput& output_array_buffer, + int32_t max_read_length, scoped_refptr<TrackedCallback> callback) { PluginDelegate* plugin_delegate = GetPluginDelegate(); if (!plugin_delegate) @@ -199,12 +199,12 @@ int32_t PPB_FileIO_Impl::ReadValidated( if (!base::FileUtilProxy::Read( plugin_delegate->GetFileThreadMessageLoopProxy(), file_, offset, - bytes_to_read, + max_read_length, base::Bind(&PPB_FileIO_Impl::ExecutePlatformReadCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; - RegisterCallback(OPERATION_READ, callback, buffer, NULL); + RegisterCallback(OPERATION_READ, callback, &output_array_buffer, NULL); return PP_OK_COMPLETIONPENDING; } diff --git a/webkit/plugins/ppapi/ppb_file_io_impl.h b/webkit/plugins/ppapi/ppb_file_io_impl.h index 52d1105..a6f9e89 100644 --- a/webkit/plugins/ppapi/ppb_file_io_impl.h +++ b/webkit/plugins/ppapi/ppb_file_io_impl.h @@ -51,8 +51,8 @@ class PPB_FileIO_Impl : public ::ppapi::PPB_FileIO_Shared { scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; virtual int32_t ReadValidated( int64_t offset, - char* buffer, - int32_t bytes_to_read, + const PP_ArrayOutput& output_array_buffer, + int32_t max_read_length, scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; virtual int32_t WriteValidated( int64_t offset, |