summaryrefslogtreecommitdiffstats
path: root/ppapi
diff options
context:
space:
mode:
authormgiuca@chromium.org <mgiuca@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-31 10:59:03 +0000
committermgiuca@chromium.org <mgiuca@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-31 10:59:03 +0000
commit17d69d8f367c5662b1c0be354d523f17d2a319b4 (patch)
tree3bd424646a210d043a01aa39f35583da3b54833b /ppapi
parent958e785645c18e72c5246684fdf344b8c1a9324a (diff)
downloadchromium_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.cc48
-rw-r--r--ppapi/proxy/plugin_var_tracker.h5
-rw-r--r--ppapi/proxy/ppapi_messages.h10
-rw-r--r--ppapi/proxy/raw_var_data.cc20
-rw-r--r--ppapi/shared_impl/test_globals.h7
-rw-r--r--ppapi/shared_impl/var_tracker.h14
-rw-r--r--ppapi/tests/test_post_message.cc118
-rw-r--r--ppapi/tests/test_post_message.h20
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_