diff options
Diffstat (limited to 'ppapi')
-rw-r--r-- | ppapi/api/ppb_messaging.idl | 4 | ||||
-rw-r--r-- | ppapi/api/ppp_messaging.idl | 7 | ||||
-rw-r--r-- | ppapi/c/ppb_messaging.h | 6 | ||||
-rw-r--r-- | ppapi/c/ppp_messaging.h | 9 | ||||
-rw-r--r-- | ppapi/cpp/instance.h | 24 | ||||
-rw-r--r-- | ppapi/proxy/handle_converter.cc | 8 | ||||
-rw-r--r-- | ppapi/proxy/ppb_instance_proxy.cc | 10 | ||||
-rw-r--r-- | ppapi/proxy/raw_var_data.cc | 77 | ||||
-rw-r--r-- | ppapi/proxy/raw_var_data.h | 2 | ||||
-rw-r--r-- | ppapi/proxy/raw_var_data_unittest.cc | 32 | ||||
-rw-r--r-- | ppapi/proxy/serialized_var.cc | 39 | ||||
-rw-r--r-- | ppapi/proxy/serialized_var.h | 31 | ||||
-rw-r--r-- | ppapi/tests/test_post_message.cc | 48 | ||||
-rw-r--r-- | ppapi/tests/test_post_message.h | 4 |
14 files changed, 215 insertions, 86 deletions
diff --git a/ppapi/api/ppb_messaging.idl b/ppapi/api/ppb_messaging.idl index 647f7f6..0f9a3aa 100644 --- a/ppapi/api/ppb_messaging.idl +++ b/ppapi/api/ppb_messaging.idl @@ -36,8 +36,8 @@ interface PPB_Messaging { * JavaScript. * * When passing array or dictionary <code>PP_Var</code>s, the entire reference - * graph will be converted and transferred, including reference cycles if they - * exist. + * graph will be converted and transferred. If the reference graph has cycles, + * the message will not be sent and an error will be logged to the console. * * Listeners for message events in JavaScript code will receive an object * conforming to the HTML 5 <code>MessageEvent</code> interface. diff --git a/ppapi/api/ppp_messaging.idl b/ppapi/api/ppp_messaging.idl index 150f771..83c2010 100644 --- a/ppapi/api/ppp_messaging.idl +++ b/ppapi/api/ppp_messaging.idl @@ -36,10 +36,9 @@ interface PPP_Messaging { * * When converting JavaScript arrays, any object properties whose name * is not an array index are ignored. When passing arrays and objects, the - * entire reference graph will be converted and transferred, including - * reference cycles if they exist. Since <code>PP_Var</code>s are ref-counted, - * the author of the plugin must take care if they expect to receive vars with - * cycles. Cycles must be manually broken to correctly release the vars. + * entire reference graph will be converted and transferred. If the reference + * graph has cycles, the message will not be sent and an error will be logged + * to the console. * * The following JavaScript code invokes <code>HandleMessage</code>, passing * the module instance on which it was invoked, with <code>message</code> diff --git a/ppapi/c/ppb_messaging.h b/ppapi/c/ppb_messaging.h index 404a11c..06e31205 100644 --- a/ppapi/c/ppb_messaging.h +++ b/ppapi/c/ppb_messaging.h @@ -3,7 +3,7 @@ * found in the LICENSE file. */ -/* From ppb_messaging.idl modified Mon May 20 15:31:07 2013. */ +/* From ppb_messaging.idl modified Wed Jun 5 10:32:59 2013. */ #ifndef PPAPI_C_PPB_MESSAGING_H_ #define PPAPI_C_PPB_MESSAGING_H_ @@ -50,8 +50,8 @@ struct PPB_Messaging_1_0 { * JavaScript. * * When passing array or dictionary <code>PP_Var</code>s, the entire reference - * graph will be converted and transferred, including reference cycles if they - * exist. + * graph will be converted and transferred. If the reference graph has cycles, + * the message will not be sent and an error will be logged to the console. * * Listeners for message events in JavaScript code will receive an object * conforming to the HTML 5 <code>MessageEvent</code> interface. diff --git a/ppapi/c/ppp_messaging.h b/ppapi/c/ppp_messaging.h index b2e1f3c..28d40d6 100644 --- a/ppapi/c/ppp_messaging.h +++ b/ppapi/c/ppp_messaging.h @@ -3,7 +3,7 @@ * found in the LICENSE file. */ -/* From ppp_messaging.idl modified Tue May 21 09:01:17 2013. */ +/* From ppp_messaging.idl modified Wed Jun 5 10:32:43 2013. */ #ifndef PPAPI_C_PPP_MESSAGING_H_ #define PPAPI_C_PPP_MESSAGING_H_ @@ -52,10 +52,9 @@ struct PPP_Messaging_1_0 { * * When converting JavaScript arrays, any object properties whose name * is not an array index are ignored. When passing arrays and objects, the - * entire reference graph will be converted and transferred, including - * reference cycles if they exist. Since <code>PP_Var</code>s are ref-counted, - * the author of the plugin must take care if they expect to receive vars with - * cycles. Cycles must be manually broken to correctly release the vars. + * entire reference graph will be converted and transferred. If the reference + * graph has cycles, the message will not be sent and an error will be logged + * to the console. * * The following JavaScript code invokes <code>HandleMessage</code>, passing * the module instance on which it was invoked, with <code>message</code> diff --git a/ppapi/cpp/instance.h b/ppapi/cpp/instance.h index 48bca90..6bf4e83 100644 --- a/ppapi/cpp/instance.h +++ b/ppapi/cpp/instance.h @@ -244,6 +244,12 @@ class Instance { /// JavaScript execution will not be blocked while HandleMessage() is /// processing the message. /// + /// When converting JavaScript arrays, any object properties whose name + /// is not an array index are ignored. When passing arrays and objects, the + /// entire reference graph will be converted and transferred. If the reference + /// graph has cycles, the message will not be sent and an error will be logged + /// to the console. + /// /// <strong>Example:</strong> /// /// The following JavaScript code invokes <code>HandleMessage</code>, passing @@ -264,9 +270,10 @@ class Instance { /// /// Refer to PostMessage() for sending messages to JavaScript. /// - /// @param[in] message A <code>Var</code> containing the data sent from - /// JavaScript. Message can have an int32_t, double, bool, or string value - /// (objects are not supported). + /// @param[in] message A <code>Var</code> which has been converted from a + /// JavaScript value. JavaScript array/object types are supported from Chrome + /// M29 onward. All JavaScript values are copied when passing them to the + /// plugin. virtual void HandleMessage(const Var& message); /// @} @@ -453,6 +460,11 @@ class Instance { /// /// The browser will pop-up an alert saying "Hello world!" /// + /// When passing array or dictionary <code>PP_Var</code>s, the entire + /// reference graph will be converted and transferred. If the reference graph + /// has cycles, the message will not be sent and an error will be logged to + /// the console. + /// /// Listeners for message events in JavaScript code will receive an object /// conforming to the HTML 5 <code>MessageEvent</code> interface. /// Specifically, the value of message will be contained as a property called @@ -466,9 +478,9 @@ class Instance { /// Refer to HandleMessage() for receiving events from JavaScript. /// /// @param[in] message A <code>Var</code> containing the data to be sent to - /// JavaScript. Message can have a numeric, boolean, or string value; arrays - /// and dictionaries are not yet supported. Ref-counted var types are copied, - /// and are therefore not shared between the instance and the browser. + /// JavaScript. Message can have a numeric, boolean, or string value. + /// Array/Dictionary types are supported from Chrome M29 onward. + /// All var types are copied when passing them to JavaScript. void PostMessage(const Var& message); /// @} diff --git a/ppapi/proxy/handle_converter.cc b/ppapi/proxy/handle_converter.cc index b77ee49..d2fc4a9 100644 --- a/ppapi/proxy/handle_converter.cc +++ b/ppapi/proxy/handle_converter.cc @@ -54,18 +54,14 @@ void ConvertHandlesInParam(const ppapi::proxy::SerializedVar& var, Handles* handles, IPC::Message* msg, int* handle_index) { - if (!var.raw_var_data()) - return; - - std::vector<ppapi::proxy::SerializedHandle*> var_handles = - var.raw_var_data()->GetHandles(); + std::vector<ppapi::proxy::SerializedHandle*> var_handles = var.GetHandles(); if (var_handles.empty()) return; for (size_t i = 0; i < var_handles.size(); ++i) handles->push_back(*var_handles[i]); if (msg) - var.raw_var_data()->Write(msg, base::Bind(&HandleWriter, handle_index)); + var.WriteDataToMessage(msg, base::Bind(&HandleWriter, handle_index)); } // For PpapiMsg_ResourceReply and the reply to PpapiHostMsg_ResourceSyncCall, diff --git a/ppapi/proxy/ppb_instance_proxy.cc b/ppapi/proxy/ppb_instance_proxy.cc index 4009a19..e3948f06a 100644 --- a/ppapi/proxy/ppb_instance_proxy.cc +++ b/ppapi/proxy/ppb_instance_proxy.cc @@ -56,6 +56,10 @@ namespace proxy { namespace { +const char kSerializationError[] = "Failed to convert a PostMessage " + "argument from a PP_Var to a Javascript value. It may have cycles or be of " + "an unsupported type."; + InterfaceProxy* CreateInstanceProxy(Dispatcher* dispatcher) { return new PPB_Instance_Proxy(dispatcher); } @@ -933,6 +937,12 @@ void PPB_Instance_Proxy::OnHostMsgPostMessage( PP_Instance instance, SerializedVarReceiveInput message) { EnterInstanceNoLock enter(instance); + if (!message.is_valid_var()) { + PpapiGlobals::Get()->LogWithSource( + instance, PP_LOGLEVEL_ERROR, std::string(), kSerializationError); + return; + } + if (enter.succeeded()) enter.functions()->PostMessage(instance, message.GetForInstance(dispatcher(), diff --git a/ppapi/proxy/raw_var_data.cc b/ppapi/proxy/raw_var_data.cc index 4de51ae..a550c3f 100644 --- a/ppapi/proxy/raw_var_data.cc +++ b/ppapi/proxy/raw_var_data.cc @@ -31,25 +31,27 @@ static const uint32 kMinimumArrayBufferSizeForShmem = 256 * 1024; static uint32 g_minimum_array_buffer_size_for_shmem = kMinimumArrayBufferSizeForShmem; -void DefaultHandleWriter(IPC::Message* m, const SerializedHandle& handle) { - IPC::ParamTraits<SerializedHandle>::Write(m, handle); -} +struct StackEntry { + StackEntry(PP_Var v, size_t i) : var(v), data_index(i) {} + PP_Var var; + size_t data_index; +}; // For a given PP_Var, returns the RawVarData associated with it, or creates a // new one if there is no existing one. The data is appended to |data| if it // is newly created. The index into |data| pointing to the result is returned. -// |id_map| keeps track of RawVarDatas that have already been created. +// |visited_map| keeps track of RawVarDatas that have already been created. size_t GetOrCreateRawVarData(const PP_Var& var, - base::hash_map<int64_t, size_t>* id_map, + base::hash_map<int64_t, size_t>* visited_map, ScopedVector<RawVarData>* data) { if (VarTracker::IsVarTypeRefcounted(var.type)) { - base::hash_map<int64_t, size_t>::iterator it = id_map->find( + base::hash_map<int64_t, size_t>::iterator it = visited_map->find( var.value.as_id); - if (it != id_map->end()) { + if (it != visited_map->end()) { return it->second; } else { data->push_back(RawVarData::Create(var.type)); - (*id_map)[var.value.as_id] = data->size() - 1; + (*visited_map)[var.value.as_id] = data->size() - 1; } } else { data->push_back(RawVarData::Create(var.type)); @@ -57,6 +59,10 @@ size_t GetOrCreateRawVarData(const PP_Var& var, return data->size() - 1; } +bool CanHaveChildren(PP_Var var) { + return var.type == PP_VARTYPE_ARRAY || var.type == PP_VARTYPE_DICTIONARY; +} + } // namespace // RawVarDataGraph ------------------------------------------------------------ @@ -66,27 +72,40 @@ RawVarDataGraph::RawVarDataGraph() { RawVarDataGraph::~RawVarDataGraph() { } +// This function uses a stack-based DFS search to traverse the var graph. Each +// iteration, the top node on the stack examined. If the node has not been +// visited yet (i.e. !initialized()) then it is added to the list of +// |parent_ids| which contains all of the nodes on the path from the start node +// to the current node. Each of that nodes children are examined. If they appear +// in the list of |parent_ids| it means we have a cycle and we return NULL. +// Otherwise, if they haven't been visited yet we add them to the stack, If the +// node at the top of the stack has already been visited, then we pop it off the +// stack and erase it from |parent_ids|. // static scoped_ptr<RawVarDataGraph> RawVarDataGraph::Create(const PP_Var& var, PP_Instance instance) { scoped_ptr<RawVarDataGraph> graph(new RawVarDataGraph); // Map of |var.value.as_id| to a RawVarData index in RawVarDataGraph. - base::hash_map<int64_t, size_t> id_map; + base::hash_map<int64_t, size_t> visited_map; + base::hash_set<int64_t> parent_ids; - std::stack<std::pair<PP_Var, size_t> > stack; - stack.push(make_pair(var, GetOrCreateRawVarData(var, &id_map, - &graph->data_))); + std::stack<StackEntry> stack; + stack.push(StackEntry(var, GetOrCreateRawVarData(var, &visited_map, + &graph->data_))); - // Traverse the PP_Var graph with DFS. while (!stack.empty()) { - PP_Var current_var = stack.top().first; - RawVarData* current_var_data = graph->data_[stack.top().second]; - stack.pop(); + PP_Var current_var = stack.top().var; + RawVarData* current_var_data = graph->data_[stack.top().data_index]; - // If the node is initialized, it means we have visited it. - if (current_var_data->initialized()) + if (current_var_data->initialized()) { + stack.pop(); + if (CanHaveChildren(current_var)) + parent_ids.erase(current_var.value.as_id); continue; + } + if (CanHaveChildren(current_var)) + parent_ids.insert(current_var.value.as_id); if (!current_var_data->Init(current_var, instance)) { NOTREACHED(); return scoped_ptr<RawVarDataGraph>(); @@ -103,10 +122,16 @@ scoped_ptr<RawVarDataGraph> RawVarDataGraph::Create(const PP_Var& var, array_var->elements().begin(); iter != array_var->elements().end(); ++iter) { - size_t child_id = GetOrCreateRawVarData(iter->get(), &id_map, + const PP_Var& child = iter->get(); + // If a child of this node is already in parent_ids, we have a cycle so + // we just return null. + if (CanHaveChildren(child) && parent_ids.count(child.value.as_id) != 0) + return scoped_ptr<RawVarDataGraph>(); + size_t child_id = GetOrCreateRawVarData(child, &visited_map, &graph->data_); static_cast<ArrayRawVarData*>(current_var_data)->AddChild(child_id); - stack.push(make_pair(iter->get(), child_id)); + if (!graph->data_[child_id]->initialized()) + stack.push(StackEntry(child, child_id)); } } else if (current_var.type == PP_VARTYPE_DICTIONARY) { DictionaryVar* dict_var = DictionaryVar::FromPPVar(current_var); @@ -118,11 +143,15 @@ scoped_ptr<RawVarDataGraph> RawVarDataGraph::Create(const PP_Var& var, dict_var->key_value_map().begin(); iter != dict_var->key_value_map().end(); ++iter) { - size_t child_id = GetOrCreateRawVarData(iter->second.get(), &id_map, + const PP_Var& child = iter->second.get(); + if (CanHaveChildren(child) && parent_ids.count(child.value.as_id) != 0) + return scoped_ptr<RawVarDataGraph>(); + size_t child_id = GetOrCreateRawVarData(child, &visited_map, &graph->data_); static_cast<DictionaryRawVarData*>( current_var_data)->AddChild(iter->first, child_id); - stack.push(make_pair(iter->second.get(), child_id)); + if (!graph->data_[child_id]->initialized()) + stack.push(StackEntry(child, child_id)); } } } @@ -153,10 +182,6 @@ void RawVarDataGraph::Write(IPC::Message* m, } } -void RawVarDataGraph::Write(IPC::Message* m) { - Write(m, base::Bind(&DefaultHandleWriter)); -} - // static scoped_ptr<RawVarDataGraph> RawVarDataGraph::Read(const IPC::Message* m, PickleIterator* iter) { diff --git a/ppapi/proxy/raw_var_data.h b/ppapi/proxy/raw_var_data.h index 1e8746f..c7981d3 100644 --- a/ppapi/proxy/raw_var_data.h +++ b/ppapi/proxy/raw_var_data.h @@ -65,8 +65,6 @@ class PPAPI_PROXY_EXPORT RawVarDataGraph { // Write the graph to a message using the given HandleWriter. void Write(IPC::Message* m, const HandleWriter& handle_writer); - // Write the graph to a message using the default handle writer. - void Write(IPC::Message* m); // Create a RawVarDataGraph from the given message. static scoped_ptr<RawVarDataGraph> Read(const IPC::Message* m, diff --git a/ppapi/proxy/raw_var_data_unittest.cc b/ppapi/proxy/raw_var_data_unittest.cc index 2b134af..2ee6914 100644 --- a/ppapi/proxy/raw_var_data_unittest.cc +++ b/ppapi/proxy/raw_var_data_unittest.cc @@ -26,6 +26,10 @@ namespace proxy { namespace { +void DefaultHandleWriter(IPC::Message* m, const SerializedHandle& handle) { + IPC::ParamTraits<SerializedHandle>::Write(m, handle); +} + class RawVarDataTest : public testing::Test { public: RawVarDataTest() {} @@ -44,21 +48,28 @@ class RawVarDataTest : public testing::Test { TestGlobals globals_; }; -PP_Var WriteAndRead(const PP_Var& var) { +bool WriteAndRead(const PP_Var& var, PP_Var* result) { PP_Instance dummy_instance = 1234; scoped_ptr<RawVarDataGraph> expected_data(RawVarDataGraph::Create( var, dummy_instance)); + if (!expected_data) + return false; IPC::Message m; - expected_data->Write(&m); + expected_data->Write(&m, base::Bind(&DefaultHandleWriter)); PickleIterator iter(m); scoped_ptr<RawVarDataGraph> actual_data(RawVarDataGraph::Read(&m, &iter)); - return actual_data->CreatePPVar(dummy_instance); + *result = actual_data->CreatePPVar(dummy_instance); + return true; } // Assumes a ref for var. bool WriteReadAndCompare(const PP_Var& var) { ScopedPPVar expected(ScopedPPVar::PassRef(), var); - ScopedPPVar actual(ScopedPPVar::PassRef(), WriteAndRead(expected.get())); + PP_Var result; + bool success = WriteAndRead(expected.get(), &result); + if (!success) + return false; + ScopedPPVar actual(ScopedPPVar::PassRef(), result); return TestEqual(expected.get(), actual.get()); } @@ -160,25 +171,18 @@ TEST_F(RawVarDataTest, DictionaryArrayTest) { // Array <-> dictionary cycle. dictionary->SetWithStringKey("10", release_array.get()); - ScopedPPVar result = ScopedPPVar(ScopedPPVar::PassRef(), - WriteAndRead(release_dictionary.get())); - EXPECT_TRUE(TestEqual(release_dictionary.get(), result.get())); + PP_Var result; + ASSERT_FALSE(WriteAndRead(release_dictionary.get(), &result)); // Break the cycle. // TODO(raymes): We need some better machinery for releasing vars with // cycles. Remove the code below once we have that. dictionary->DeleteWithStringKey("10"); - DictionaryVar* result_dictionary = DictionaryVar::FromPPVar(result.get()); - result_dictionary->DeleteWithStringKey("10"); // Array with self references. array->Set(index, release_array.get()); - result = ScopedPPVar(ScopedPPVar::PassRef(), - WriteAndRead(release_array.get())); - EXPECT_TRUE(TestEqual(release_array.get(), result.get())); + ASSERT_FALSE(WriteAndRead(release_array.get(), &result)); // Break the self reference. array->Set(index, PP_MakeUndefined()); - ArrayVar* result_array = ArrayVar::FromPPVar(result.get()); - result_array->Set(index, PP_MakeUndefined()); } } // namespace proxy diff --git a/ppapi/proxy/serialized_var.cc b/ppapi/proxy/serialized_var.cc index 1217a83..72e7cc8 100644 --- a/ppapi/proxy/serialized_var.cc +++ b/ppapi/proxy/serialized_var.cc @@ -18,13 +18,20 @@ namespace ppapi { namespace proxy { +namespace { +void DefaultHandleWriter(IPC::Message* m, const SerializedHandle& handle) { + IPC::ParamTraits<SerializedHandle>::Write(m, handle); +} +} // namespace + // SerializedVar::Inner -------------------------------------------------------- SerializedVar::Inner::Inner() : serialization_rules_(NULL), var_(PP_MakeUndefined()), instance_(0), - cleanup_mode_(CLEANUP_NONE) { + cleanup_mode_(CLEANUP_NONE), + is_valid_var_(true) { #ifndef NDEBUG has_been_serialized_ = false; has_been_deserialized_ = false; @@ -107,7 +114,24 @@ void SerializedVar::Inner::WriteToMessage(IPC::Message* m) const { DCHECK(!has_been_serialized_); has_been_serialized_ = true; #endif - RawVarDataGraph::Create(var_, instance_)->Write(m); + scoped_ptr<RawVarDataGraph> data = RawVarDataGraph::Create(var_, instance_); + if (data) { + m->WriteBool(true); // Success. + data->Write(m, base::Bind(&DefaultHandleWriter)); + } else { + m->WriteBool(false); // Failure. + } +} + +void SerializedVar::Inner::WriteDataToMessage( + IPC::Message* m, + const HandleWriter& handle_writer) const { + if (raw_var_data_) { + m->WriteBool(true); // Success. + raw_var_data_->Write(m, handle_writer); + } else { + m->WriteBool(false); // Failure. + } } bool SerializedVar::Inner::ReadFromMessage(const IPC::Message* m, @@ -125,8 +149,15 @@ bool SerializedVar::Inner::ReadFromMessage(const IPC::Message* m, #endif // When reading, the dispatcher should be set when we get a Deserialize // call (which will supply a dispatcher). - raw_var_data_ = RawVarDataGraph::Read(m, iter); - return raw_var_data_.get() != NULL; + if (!m->ReadBool(iter, &is_valid_var_)) + return false; + if (is_valid_var_) { + raw_var_data_ = RawVarDataGraph::Read(m, iter); + if (!raw_var_data_) + return false; + } + + return true; } void SerializedVar::Inner::SetCleanupModeToEndSendPassRef() { diff --git a/ppapi/proxy/serialized_var.h b/ppapi/proxy/serialized_var.h index 98cafa0..0b8e796 100644 --- a/ppapi/proxy/serialized_var.h +++ b/ppapi/proxy/serialized_var.h @@ -80,12 +80,24 @@ class PPAPI_PROXY_EXPORT SerializedVar { void WriteToMessage(IPC::Message* m) const { inner_->WriteToMessage(m); } + // If ReadFromMessage has been called, WriteDataToMessage will write the var + // that has been read from ReadFromMessage back to a message. This is used + // when converting handles for use in NaCl. + void WriteDataToMessage(IPC::Message* m, + const HandleWriter& handle_writer) const { + inner_->WriteDataToMessage(m, handle_writer); + } bool ReadFromMessage(const IPC::Message* m, PickleIterator* iter) { return inner_->ReadFromMessage(m, iter); } - RawVarDataGraph* raw_var_data() const { - return inner_->raw_var_data(); + bool is_valid_var() const { + return inner_->is_valid_var(); + } + + // Returns the shared memory handles associated with this SerializedVar. + std::vector<SerializedHandle*> GetHandles() const { + return inner_->GetHandles(); } protected: @@ -110,8 +122,13 @@ class PPAPI_PROXY_EXPORT SerializedVar { serialization_rules_ = serialization_rules; } - RawVarDataGraph* raw_var_data() { - return raw_var_data_.get(); + bool is_valid_var() const { + return is_valid_var_; + } + + std::vector<SerializedHandle*> GetHandles() { + return (raw_var_data_ ? raw_var_data_->GetHandles() : + std::vector<SerializedHandle*>()); } // See outer class's declarations above. @@ -124,6 +141,8 @@ class PPAPI_PROXY_EXPORT SerializedVar { void ForceSetVarValueForTest(PP_Var value); void WriteToMessage(IPC::Message* m) const; + void WriteDataToMessage(IPC::Message* m, + const HandleWriter& handle_writer) const; bool ReadFromMessage(const IPC::Message* m, PickleIterator* iter); // Sets the cleanup mode. See the CleanupMode enum below. @@ -162,6 +181,9 @@ class PPAPI_PROXY_EXPORT SerializedVar { CleanupMode cleanup_mode_; + // If the var is not properly serialized, this will be false. + bool is_valid_var_; + #ifndef NDEBUG // When being sent or received over IPC, we should only be serialized or // deserialized once. These flags help us assert this is true. @@ -342,6 +364,7 @@ class PPAPI_PROXY_EXPORT SerializedVarReceiveInput { PP_Var Get(Dispatcher* dispatcher); PP_Var GetForInstance(Dispatcher* dispatcher, PP_Instance instance); + bool is_valid_var() { return serialized_.is_valid_var(); } private: const SerializedVar& serialized_; diff --git a/ppapi/tests/test_post_message.cc b/ppapi/tests/test_post_message.cc index 8c67ee5..944b5614 100644 --- a/ppapi/tests/test_post_message.cc +++ b/ppapi/tests/test_post_message.cc @@ -238,6 +238,16 @@ bool TestPostMessage::AddEchoingListener(const std::string& expression) { return true; } +bool TestPostMessage::PostMessageFromJavaScript(const std::string& func) { + std::string js_code; + js_code += "var plugin = document.getElementById('plugin');" + "plugin.postMessage("; + js_code += func + "()"; + js_code += " );"; + instance_->EvalScript(js_code); + return true; +} + bool TestPostMessage::ClearListeners() { std::string js_code; js_code += "var plugin = document.getElementById('plugin');" @@ -523,6 +533,7 @@ std::string TestPostMessage::TestSendingComplexVar() { // if we didn't run the 'SendInInit' test. All tests other than 'SendInInit' // should start with these. WaitForMessages(); + message_data_.clear(); ASSERT_TRUE(ClearListeners()); pp::Var string(kTestString); @@ -531,7 +542,6 @@ std::string TestPostMessage::TestSendingComplexVar() { dictionary.Set(pp::Var("bar"), string); dictionary.Set(pp::Var("abc"), pp::Var(kTestInt)); dictionary.Set(pp::Var("def"), pp::Var()); - dictionary.Set(pp::Var("dictionary"), dictionary); // Self-reference. // Reference to array. pp::VarArray_Dev array; @@ -544,15 +554,9 @@ std::string TestPostMessage::TestSendingComplexVar() { dictionary.Set(pp::Var("array-ref1"), array); dictionary.Set(pp::Var("array-ref2"), array); - // Set up a (dictionary -> array -> dictionary) cycle. - pp::VarArray_Dev array2; - array2.Set(0, dictionary); - dictionary.Set(pp::Var("array2"), array2); - // Set up the JavaScript message event listener to echo the data part of the // message event back to us. ASSERT_TRUE(AddEchoingListener("message_event.data")); - message_data_.clear(); instance_->PostMessage(dictionary); // PostMessage is asynchronous, so we should not receive a response yet. ASSERT_EQ(message_data_.size(), 0); @@ -561,12 +565,36 @@ std::string TestPostMessage::TestSendingComplexVar() { pp::VarDictionary_Dev result(message_data_.back()); ASSERT_TRUE(VarsEqual(dictionary, message_data_.back())); + WaitForMessages(); + message_data_.clear(); + ASSERT_TRUE(ClearListeners()); + + // Set up a (dictionary -> array -> dictionary) cycle. Cycles shouldn't be + // transmitted. + pp::VarArray_Dev array2; + array2.Set(0, dictionary); + dictionary.Set(pp::Var("array2"), array2); + + ASSERT_TRUE(AddEchoingListener("message_event.data")); + instance_->PostMessage(dictionary); + // PostMessage is asynchronous, so we should not receive a response yet. + ASSERT_EQ(message_data_.size(), 0); + ASSERT_EQ(WaitForMessages(), 0); + // Break the cycles. - dictionary.Delete(pp::Var("dictionary")); dictionary.Delete(pp::Var("array2")); - result.Delete(pp::Var("dictionary")); - result.Delete(pp::Var("array2")); + WaitForMessages(); + message_data_.clear(); + ASSERT_TRUE(ClearListeners()); + + // Test sending a cycle from JavaScript to the plugin. + ASSERT_TRUE(AddEchoingListener("message_event.data")); + PostMessageFromJavaScript("function() { var x = []; x[0] = x; return x; }"); + ASSERT_EQ(message_data_.size(), 0); + ASSERT_EQ(WaitForMessages(), 0); + + WaitForMessages(); message_data_.clear(); ASSERT_TRUE(ClearListeners()); diff --git a/ppapi/tests/test_post_message.h b/ppapi/tests/test_post_message.h index 3ea69e8..2f2f628 100644 --- a/ppapi/tests/test_post_message.h +++ b/ppapi/tests/test_post_message.h @@ -34,6 +34,10 @@ class TestPostMessage : public TestCase { // Returns true on success, false on failure. bool AddEchoingListener(const std::string& expression); + // Posts a message from JavaScript to the plugin. |func| should be a + // JavaScript function which returns the variable to post. + bool PostMessageFromJavaScript(const std::string& func); + // Clear any listeners that have been added using AddEchoingListener by // calling removeEventListener for each. // Returns true on success, false on failure. |