diff options
author | mgiuca@chromium.org <mgiuca@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-31 10:59:03 +0000 |
---|---|---|
committer | mgiuca@chromium.org <mgiuca@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-31 10:59:03 +0000 |
commit | 17d69d8f367c5662b1c0be354d523f17d2a319b4 (patch) | |
tree | 3bd424646a210d043a01aa39f35583da3b54833b /ppapi | |
parent | 958e785645c18e72c5246684fdf344b8c1a9324a (diff) | |
download | chromium_src-17d69d8f367c5662b1c0be354d523f17d2a319b4.zip chromium_src-17d69d8f367c5662b1c0be354d523f17d2a319b4.tar.gz chromium_src-17d69d8f367c5662b1c0be354d523f17d2a319b4.tar.bz2 |
[PPAPI] It is now possible to pass filesystems from JavaScript to NaCl modules.
If a DOMFileSystem is passed as a message to the NaCl module, it will be
converted into a resource var which is available to the plugin via the dev
interface PPB_VarResource_Dev.
BUG=177017
Review URL: https://codereview.chromium.org/26564009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@232080 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ppapi')
-rw-r--r-- | ppapi/proxy/plugin_var_tracker.cc | 48 | ||||
-rw-r--r-- | ppapi/proxy/plugin_var_tracker.h | 5 | ||||
-rw-r--r-- | ppapi/proxy/ppapi_messages.h | 10 | ||||
-rw-r--r-- | ppapi/proxy/raw_var_data.cc | 20 | ||||
-rw-r--r-- | ppapi/shared_impl/test_globals.h | 7 | ||||
-rw-r--r-- | ppapi/shared_impl/var_tracker.h | 14 | ||||
-rw-r--r-- | ppapi/tests/test_post_message.cc | 118 | ||||
-rw-r--r-- | ppapi/tests/test_post_message.h | 20 |
8 files changed, 234 insertions, 8 deletions
diff --git a/ppapi/proxy/plugin_var_tracker.cc b/ppapi/proxy/plugin_var_tracker.cc index 0788b99..a9cbd9c 100644 --- a/ppapi/proxy/plugin_var_tracker.cc +++ b/ppapi/proxy/plugin_var_tracker.cc @@ -6,10 +6,13 @@ #include "base/memory/ref_counted.h" #include "base/memory/singleton.h" +#include "ipc/ipc_message.h" #include "ppapi/c/dev/ppp_class_deprecated.h" #include "ppapi/c/ppb_var.h" +#include "ppapi/proxy/file_system_resource.h" #include "ppapi/proxy/plugin_array_buffer_var.h" #include "ppapi/proxy/plugin_dispatcher.h" +#include "ppapi/proxy/plugin_globals.h" #include "ppapi/proxy/plugin_resource_var.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/proxy_object_var.h" @@ -22,6 +25,16 @@ namespace ppapi { namespace proxy { +namespace { + +Connection GetConnectionForInstance(PP_Instance instance) { + PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); + DCHECK(dispatcher); + return Connection(PluginGlobals::Get()->GetBrowserSender(), dispatcher); +} + +} // namespace + PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i) : dispatcher(d), host_object_id(i) { @@ -154,6 +167,41 @@ void PluginVarTracker::ReleaseHostObject(PluginDispatcher* dispatcher, ReleaseVar(found->second); } +PP_Var PluginVarTracker::MakeResourcePPVarFromMessage( + PP_Instance instance, + const IPC::Message& creation_message, + int pending_renderer_id, + int pending_browser_id) { + DCHECK(pending_renderer_id); + DCHECK(pending_browser_id); + switch (creation_message.type()) { + case PpapiPluginMsg_FileSystem_CreateFromPendingHost::ID: { + PP_FileSystemType file_system_type; + if (!UnpackMessage<PpapiPluginMsg_FileSystem_CreateFromPendingHost>( + creation_message, &file_system_type)) { + NOTREACHED() << "Invalid message of type " + "PpapiPluginMsg_FileSystem_CreateFromPendingHost"; + return PP_MakeNull(); + } + // Create a plugin-side resource and attach it to the host resource. + // Note: This only makes sense when the plugin is out of process (which + // should always be true when passing resource vars). + PP_Resource pp_resource = + (new FileSystemResource(GetConnectionForInstance(instance), + instance, + pending_renderer_id, + pending_browser_id, + file_system_type))->GetReference(); + return MakeResourcePPVar(pp_resource); + } + default: { + NOTREACHED() << "Creation message has unexpected type " + << creation_message.type(); + return PP_MakeNull(); + } + } +} + ResourceVar* PluginVarTracker::MakeResourceVar(PP_Resource pp_resource) { // The resource 0 returns a null resource var. if (!pp_resource) diff --git a/ppapi/proxy/plugin_var_tracker.h b/ppapi/proxy/plugin_var_tracker.h index 126c8fc..03a9e5e 100644 --- a/ppapi/proxy/plugin_var_tracker.h +++ b/ppapi/proxy/plugin_var_tracker.h @@ -59,6 +59,11 @@ class PPAPI_PROXY_EXPORT PluginVarTracker : public VarTracker { const PP_Var& host_object); // VarTracker public overrides. + virtual PP_Var MakeResourcePPVarFromMessage( + PP_Instance instance, + const IPC::Message& creation_message, + int pending_renderer_id, + int pending_browser_id) OVERRIDE; virtual ResourceVar* MakeResourceVar(PP_Resource pp_resource) OVERRIDE; virtual void DidDeleteInstance(PP_Instance instance) OVERRIDE; virtual int TrackSharedMemoryHandle(PP_Instance instance, diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h index 02c4d84..2313cf7 100644 --- a/ppapi/proxy/ppapi_messages.h +++ b/ppapi/proxy/ppapi_messages.h @@ -1291,6 +1291,16 @@ IPC_MESSAGE_CONTROL0(PpapiPluginMsg_FileSystem_OpenReply) IPC_MESSAGE_CONTROL1(PpapiHostMsg_FileSystem_InitIsolatedFileSystem, std::string /* fsid */) IPC_MESSAGE_CONTROL0(PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply) +// Passed from renderer to browser. Creates an already-open file system with a +// given |root_url| and |file_system_type|. +IPC_MESSAGE_CONTROL2(PpapiHostMsg_FileSystem_CreateFromRenderer, + std::string /* root_url */, + PP_FileSystemType /* file_system_type */) +// Nested within a ResourceVar for file systems being passed from the renderer +// to the plugin. Creates an already-open file system resource on the plugin, +// linked to the existing resource host given in the ResourceVar. +IPC_MESSAGE_CONTROL1(PpapiPluginMsg_FileSystem_CreateFromPendingHost, + PP_FileSystemType /* file_system_type */) // Flash DRM ------------------------------------------------------------------ IPC_MESSAGE_CONTROL0(PpapiHostMsg_FlashDRM_Create) diff --git a/ppapi/proxy/raw_var_data.cc b/ppapi/proxy/raw_var_data.cc index 91bcfbfd..49ee8c2 100644 --- a/ppapi/proxy/raw_var_data.cc +++ b/ppapi/proxy/raw_var_data.cc @@ -695,14 +695,18 @@ bool ResourceRawVarData::Init(const PP_Var& var, PP_Instance /*instance*/) { } PP_Var ResourceRawVarData::CreatePPVar(PP_Instance instance) { - // If pp_resource_ is NULL, it could be because we are on the plugin side and - // there is a pending resource host on the renderer. - // TODO(mgiuca): Create a plugin-side resource in this case. - // Currently, this should never occur. This will be needed when passing a - // resource from the renderer to the plugin (http://crbug.com/177017). - DCHECK(pp_resource_); - - return PpapiGlobals::Get()->GetVarTracker()->MakeResourcePPVar(pp_resource_); + // If this is not a pending resource host, just create the var. + if (pp_resource_ || !creation_message_) { + return PpapiGlobals::Get()->GetVarTracker()->MakeResourcePPVar( + pp_resource_); + } + + // This is a pending resource host, so create the resource and var. + return PpapiGlobals::Get()->GetVarTracker()->MakeResourcePPVarFromMessage( + instance, + *creation_message_, + pending_renderer_host_id_, + pending_browser_host_id_); } void ResourceRawVarData::PopulatePPVar(const PP_Var& var, diff --git a/ppapi/shared_impl/test_globals.h b/ppapi/shared_impl/test_globals.h index 36ed829..2b4b5cb 100644 --- a/ppapi/shared_impl/test_globals.h +++ b/ppapi/shared_impl/test_globals.h @@ -18,6 +18,13 @@ class TestVarTracker : public VarTracker { public: TestVarTracker() : VarTracker(THREAD_SAFE) {} virtual ~TestVarTracker() {} + virtual PP_Var MakeResourcePPVarFromMessage( + PP_Instance instance, + const IPC::Message& creation_message, + int pending_renderer_id, + int pending_browser_id) OVERRIDE { + return PP_MakeNull(); + } virtual ResourceVar* MakeResourceVar(PP_Resource pp_resource) OVERRIDE { return NULL; } diff --git a/ppapi/shared_impl/var_tracker.h b/ppapi/shared_impl/var_tracker.h index 168fcb9..4856766 100644 --- a/ppapi/shared_impl/var_tracker.h +++ b/ppapi/shared_impl/var_tracker.h @@ -21,6 +21,10 @@ #include "ppapi/shared_impl/ppapi_shared_export.h" #include "ppapi/shared_impl/var.h" +namespace IPC { +class Message; +} // namespace IPC + namespace ppapi { class ArrayBufferVar; @@ -85,6 +89,16 @@ class PPAPI_SHARED_EXPORT VarTracker { // usually immediately put this in a scoped_refptr). ArrayBufferVar* MakeArrayBufferVar(uint32 size_in_bytes, const void* data); + // Creates a new resource var from a resource creation message. Returns a + // PP_Var that references a new PP_Resource, both with an initial reference + // count of 1. On the host side, |creation_message| is ignored, and an empty + // resource var is always returned. + virtual PP_Var MakeResourcePPVarFromMessage( + PP_Instance instance, + const IPC::Message& creation_message, + int pending_renderer_id, + int pending_browser_id) = 0; + // Creates a new resource var that points to a given resource ID. Returns a // PP_Var that references it and has an initial reference count of 1. // If |pp_resource| is 0, returns a valid, empty resource var. On the plugin diff --git a/ppapi/tests/test_post_message.cc b/ppapi/tests/test_post_message.cc index b1e30b2..2932b0e 100644 --- a/ppapi/tests/test_post_message.cc +++ b/ppapi/tests/test_post_message.cc @@ -4,12 +4,17 @@ #include "ppapi/tests/test_post_message.h" +#include <string.h> #include <algorithm> #include <map> #include <sstream> #include "ppapi/c/dev/ppb_testing_dev.h" #include "ppapi/c/pp_var.h" +#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/var.h" #include "ppapi/cpp/var_array.h" @@ -28,6 +33,7 @@ REGISTER_TEST_CASE(PostMessage); namespace { +const char kTestFilename[] = "testfile.txt"; const char kTestString[] = "Hello world!"; const bool kTestBool = true; const int32_t kTestInt = 42; @@ -161,6 +167,18 @@ TestPostMessage::~TestPostMessage() { bool TestPostMessage::Init() { bool success = CheckTestingInterface(); + core_interface_ = static_cast<const PPB_Core*>( + pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE)); + file_system_interface_ = static_cast<const PPB_FileSystem*>( + pp::Module::Get()->GetBrowserInterface(PPB_FILESYSTEM_INTERFACE)); + var_interface_ = static_cast<const PPB_Var*>( + pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE)); + var_resource_interface_ = static_cast<const PPB_VarResource_Dev*>( + pp::Module::Get()->GetBrowserInterface(PPB_VAR_RESOURCE_DEV_INTERFACE)); + if (!core_interface_ || !file_system_interface_ || !var_interface_ || + !var_resource_interface_) + return false; + // Set up a special listener that only responds to a FINISHED_WAITING string. // This is for use by WaitForMessages. std::string js_code; @@ -201,6 +219,7 @@ void TestPostMessage::RunTests(const std::string& filter) { RUN_TEST(SendingArrayBuffer, filter); RUN_TEST(SendingArray, filter); RUN_TEST(SendingDictionary, filter); + RUN_TEST(SendingResource, filter); RUN_TEST(SendingComplexVar, filter); RUN_TEST(MessageEvent, filter); RUN_TEST(NoHandler, filter); @@ -276,6 +295,31 @@ int TestPostMessage::WaitForMessages() { return message_data_.size() - message_size_before; } +int TestPostMessage::PostAsyncMessageFromJavaScriptAndWait( + const std::string& func) { + // After the |func| calls callback, post both the given |message|, as well as + // the special message FINISHED_WAITING_MESSAGE. This ensures that + // RunMessageLoop correctly waits until the callback is called. + std::string js_code; + js_code += "var plugin = document.getElementById('plugin');" + "var callback = function(message) {" + " plugin.postMessage(message);" + " plugin.postMessage('" FINISHED_WAITING_MESSAGE "');" + "};"; + js_code += "(" + func + ")(callback);"; + instance_->EvalScript(js_code); + + size_t message_size_before = message_data_.size(); + // Unlike WaitForMessages, we do not post FINISHED_WAITING_MESSAGE. This is + // because the above JavaScript code will post it for us, when the + // asynchronous operation completes. + testing_interface_->RunMessageLoop(instance_->pp_instance()); + // Now that the FINISHED_WAITING_MESSAGE has been echoed back to us, we know + // that all pending messages have been slurped up. Return the number we + // received (which may be zero). + return message_data_.size() - message_size_before; +} + std::string TestPostMessage::CheckMessageProperties( const pp::Var& test_data, const std::vector<std::string>& properties_to_check) { @@ -533,6 +577,80 @@ std::string TestPostMessage::TestSendingDictionary() { PASS(); } +std::string TestPostMessage::TestSendingResource() { + // Clean up after previous tests. This also swallows the message sent by Init + // if we didn't run the 'SendInInit' test. All tests other than 'SendInInit' + // should start with these. + WaitForMessages(); + message_data_.clear(); + ASSERT_TRUE(ClearListeners()); + + // Test sending a DOMFileSystem from JavaScript to the plugin. + // This opens a real (temporary) file using the HTML5 FileSystem API and + // writes to it. + ASSERT_TRUE(AddEchoingListener("message_event.data")); + ASSERT_EQ(message_data_.size(), 0); + std::string js_code = + "function(callback) {" + " window.webkitRequestFileSystem(window.TEMPORARY, 1024," + " function(fileSystem) {" + " fileSystem.root.getFile('"; + js_code += kTestFilename; + js_code += "', {create: true}, function(tempFile) {" + " tempFile.createWriter(function(writer) {" + " writer.onerror = function() { callback(null); };" + " writer.onwriteend = function() { callback(fileSystem); };" + " var blob = new Blob(['"; + js_code += kTestString; + js_code += "'], {'type': 'text/plain'});" + " writer.write(blob);" + " });" + " }, function() { callback(null); });" + " }, function() { callback(null); });" + "}"; + ASSERT_EQ(PostAsyncMessageFromJavaScriptAndWait(js_code), 1); + // TODO(mgiuca): Use the C++ API instead of the C API, when it is available. + PP_Var var = message_data_.back().Detach(); + PP_Resource result = var_resource_interface_->VarToResource(var); + ASSERT_TRUE(file_system_interface_->IsFileSystem(result)); + { + pp::FileSystem file_system(pp::PASS_REF, result); + std::string file_path("/"); + file_path += kTestFilename; + pp::FileRef file_ref(file_system, file_path.c_str()); + ASSERT_NE(0, file_ref.pp_resource()); + + // Read the file and test that its contents match. + pp::FileIO file_io(instance_); + ASSERT_NE(0, file_io.pp_resource()); + TestCompletionCallback callback(instance_->pp_instance(), + callback_type()); + callback.WaitForResult( + file_io.Open(file_ref, PP_FILEOPENFLAG_READ, callback.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(callback); + ASSERT_EQ(PP_OK, callback.result()); + + int length = strlen(kTestString); + std::vector<char> buffer_vector(length); + char* buffer = &buffer_vector[0]; // Note: Not null-terminated! + callback.WaitForResult( + file_io.Read(0, buffer, length, callback.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(callback); + ASSERT_EQ(length, callback.result()); + ASSERT_EQ(0, memcmp(buffer, kTestString, length)); + } + var_interface_->Release(var); + + WaitForMessages(); + message_data_.clear(); + ASSERT_TRUE(ClearListeners()); + + // TODO(mgiuca): Test roundtrip from plugin to JS and back, when the plugin to + // JS support is available. + + PASS(); +} + std::string TestPostMessage::TestSendingComplexVar() { // Clean up after previous tests. This also swallows the message sent by Init // if we didn't run the 'SendInInit' test. All tests other than 'SendInInit' diff --git a/ppapi/tests/test_post_message.h b/ppapi/tests/test_post_message.h index 2f2f628..0416496 100644 --- a/ppapi/tests/test_post_message.h +++ b/ppapi/tests/test_post_message.h @@ -8,6 +8,9 @@ #include <string> #include <vector> +#include "ppapi/c/dev/ppb_var_resource_dev.h" +#include "ppapi/c/ppb_file_system.h" +#include "ppapi/c/ppb_var.h" #include "ppapi/tests/test_case.h" class TestPostMessage : public TestCase { @@ -47,6 +50,13 @@ class TestPostMessage : public TestCase { // at the time of invocation. int WaitForMessages(); + // Posts a message from JavaScript to the plugin and wait for it to arrive. + // |func| should be a JavaScript function(callback) which calls |callback| + // with the variable to post. This function will block until the message + // arrives on the plugin side (there is no need to use WaitForMessages()). + // Returns the number of messages that were pending at the time of invocation. + int PostAsyncMessageFromJavaScriptAndWait(const std::string& func); + // Verifies that the given javascript assertions are true of the message // (|test_data|) passed via PostMessage(). std::string CheckMessageProperties( @@ -71,6 +81,10 @@ class TestPostMessage : public TestCase { // Test sending Dictionary vars in both directions. std::string TestSendingDictionary(); + // Test sending Resource vars from JavaScript to the plugin. + // TODO(mgiuca): Test sending Resource vars in both directions. + std::string TestSendingResource(); + // Test sending a complex var with references and cycles in both directions. std::string TestSendingComplexVar(); @@ -93,6 +107,12 @@ class TestPostMessage : public TestCase { // This is used to store pp::Var objects we receive via a call to // HandleMessage. VarVector message_data_; + + // Interfaces for C APIs. + const PPB_Core* core_interface_; + const PPB_FileSystem* file_system_interface_; + const PPB_Var* var_interface_; + const PPB_VarResource_Dev* var_resource_interface_; }; #endif // PPAPI_TESTS_TEST_POST_MESSAGE_H_ |