1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/message_loop/message_loop.h" 6 #include "base/message_loop/message_loop_proxy.h" 7 #include "base/synchronization/waitable_event.h" 8 #include "base/threading/thread.h" 9 #include "ipc/ipc_message.h" 10 #include "ppapi/c/pp_errors.h" 11 #include "ppapi/host/host_message_context.h" 12 #include "ppapi/host/resource_host.h" 13 #include "ppapi/host/resource_message_filter.h" 14 #include "testing/gtest/include/gtest/gtest.h" 15 16 namespace ppapi { 17 namespace host { 18 19 typedef testing::Test ResourceMessageFilterTest; 20 21 namespace { 22 23 base::WaitableEvent g_handler_completion(true, false); 24 25 enum TestMessageTypes { 26 MSG1_TYPE = 1, 27 MSG2_TYPE, 28 MSG3_TYPE, 29 REPLY_MSG1_TYPE, 30 REPLY_MSG2_TYPE, 31 REPLY_MSG3_TYPE, 32 }; 33 34 // Dummy resource host which simply stores a copy of messages it handles. 35 // |SendReply| is overridden to store a copy of the outgoing message and the 36 // message loop on which it was sent. 37 class MyResourceHost : public ResourceHost { 38 public: 39 // Messages of type |msg_type| will be handled (simply by replying with a 40 // message of type |reply_msg_type|). 41 MyResourceHost(PpapiHost* host, 42 PP_Instance instance, 43 PP_Resource resource, 44 uint32 msg_type, 45 uint32 reply_msg_type) 46 : ResourceHost(host, instance, resource), 47 msg_type_(msg_type), 48 reply_msg_type_(reply_msg_type), 49 last_reply_message_loop_(NULL) { 50 } 51 52 const IPC::Message& last_handled_msg() const { return last_handled_msg_; } 53 const IPC::Message& last_reply_msg() const { return last_reply_msg_; } 54 base::MessageLoop* last_reply_message_loop() const { 55 return last_reply_message_loop_; 56 } 57 58 void AddMessageFilter(scoped_refptr<ResourceMessageFilter> filter) { 59 AddFilter(filter); 60 } 61 62 virtual int32_t OnResourceMessageReceived( 63 const IPC::Message& msg, 64 HostMessageContext* context) OVERRIDE { 65 last_handled_msg_ = msg; 66 if (msg.type() == msg_type_) { 67 context->reply_msg = IPC::Message(0, reply_msg_type_, 68 IPC::Message::PRIORITY_NORMAL); 69 return PP_OK; 70 } 71 return PP_ERROR_FAILED; 72 } 73 74 virtual void SendReply(const ReplyMessageContext& context, 75 const IPC::Message& msg) OVERRIDE { 76 last_reply_msg_ = msg; 77 last_reply_message_loop_ = base::MessageLoop::current(); 78 g_handler_completion.Signal(); 79 } 80 81 private: 82 uint32 msg_type_; 83 uint32 reply_msg_type_; 84 85 IPC::Message last_handled_msg_; 86 IPC::Message last_reply_msg_; 87 base::MessageLoop* last_reply_message_loop_; 88 }; 89 90 // Dummy message filter which simply stores a copy of messages it handles. 91 // The message loop on which the message is handled is also stored for checking 92 // later. 93 class MyResourceFilter : public ResourceMessageFilter { 94 public: 95 // Messages of type |msg_type| will be handled (simply by replying with a 96 // message of type |reply_msg_type|). |io_thread| is the thread on which 97 // replies should be sent. |bg_thread| is the thread on which the message 98 // should be handled. 99 MyResourceFilter(const base::Thread& io_thread, 100 const base::Thread& bg_thread, 101 uint32 msg_type, 102 uint32 reply_msg_type) 103 : ResourceMessageFilter(io_thread.message_loop_proxy()), 104 message_loop_proxy_(bg_thread.message_loop_proxy()), 105 msg_type_(msg_type), 106 reply_msg_type_(reply_msg_type), 107 last_message_loop_(NULL) { 108 } 109 110 const IPC::Message& last_handled_msg() const { return last_handled_msg_; } 111 base::MessageLoop* last_message_loop() const { return last_message_loop_; } 112 113 virtual scoped_refptr<base::TaskRunner> OverrideTaskRunnerForMessage( 114 const IPC::Message& msg) OVERRIDE { 115 if (msg.type() == msg_type_) 116 return message_loop_proxy_; 117 return NULL; 118 } 119 120 virtual int32_t OnResourceMessageReceived( 121 const IPC::Message& msg, 122 HostMessageContext* context) OVERRIDE { 123 last_handled_msg_ = msg; 124 last_message_loop_ = base::MessageLoop::current(); 125 if (msg.type() == msg_type_) { 126 context->reply_msg = IPC::Message(0, reply_msg_type_, 127 IPC::Message::PRIORITY_NORMAL); 128 return PP_OK; 129 } 130 return PP_ERROR_FAILED; 131 } 132 133 private: 134 scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; 135 uint32 msg_type_; 136 uint32 reply_msg_type_; 137 138 IPC::Message last_handled_msg_; 139 base::MessageLoop* last_message_loop_; 140 }; 141 142 } // namespace 143 144 // Test that messages are filtered correctly and handlers are run on the correct 145 // threads. 146 TEST_F(ResourceMessageFilterTest, TestHandleMessage) { 147 base::Thread io_thread("test_io_thread"); 148 ASSERT_TRUE(io_thread.Start()); 149 150 base::Thread bg_thread1("test_background_thread1"); 151 ASSERT_TRUE(bg_thread1.Start()); 152 scoped_refptr<MyResourceFilter> filter1 = 153 new MyResourceFilter(io_thread, bg_thread1, MSG1_TYPE, REPLY_MSG1_TYPE); 154 155 base::Thread bg_thread2("test_background_thread2"); 156 ASSERT_TRUE(bg_thread2.Start()); 157 scoped_refptr<MyResourceFilter> filter2 = 158 new MyResourceFilter(io_thread, bg_thread2, MSG2_TYPE, REPLY_MSG2_TYPE); 159 160 PP_Instance instance = 12345; 161 PP_Resource resource = 67890; 162 MyResourceHost host(NULL, instance, resource, MSG3_TYPE, REPLY_MSG3_TYPE); 163 host.AddMessageFilter(filter1); 164 host.AddMessageFilter(filter2); 165 166 proxy::ResourceMessageCallParams params(resource, 1); 167 params.set_has_callback(); 168 HostMessageContext context(params); 169 IPC::Message message1(0, MSG1_TYPE, IPC::Message::PRIORITY_NORMAL); 170 IPC::Message message2(0, MSG2_TYPE, IPC::Message::PRIORITY_NORMAL); 171 IPC::Message message3(0, MSG3_TYPE, IPC::Message::PRIORITY_NORMAL); 172 173 // Message 1 handled by the first filter. 174 host.HandleMessage(message1, &context); 175 g_handler_completion.Wait(); 176 EXPECT_EQ(filter1->last_handled_msg().type(), message1.type()); 177 EXPECT_EQ(filter1->last_message_loop(), bg_thread1.message_loop()); 178 EXPECT_EQ(host.last_reply_msg().type(), static_cast<uint32>(REPLY_MSG1_TYPE)); 179 EXPECT_EQ(host.last_reply_message_loop(), io_thread.message_loop()); 180 g_handler_completion.Reset(); 181 182 // Message 2 handled by the second filter. 183 host.HandleMessage(message2, &context); 184 g_handler_completion.Wait(); 185 EXPECT_EQ(filter2->last_handled_msg().type(), message2.type()); 186 EXPECT_EQ(filter2->last_message_loop(), bg_thread2.message_loop()); 187 EXPECT_EQ(host.last_reply_msg().type(), static_cast<uint32>(REPLY_MSG2_TYPE)); 188 EXPECT_EQ(host.last_reply_message_loop(), io_thread.message_loop()); 189 g_handler_completion.Reset(); 190 191 // Message 3 handled by the resource host. 192 host.HandleMessage(message3, &context); 193 EXPECT_EQ(host.last_handled_msg().type(), message3.type()); 194 EXPECT_EQ(host.last_reply_msg().type(), static_cast<uint32>(REPLY_MSG3_TYPE)); 195 196 io_thread.Stop(); 197 bg_thread1.Stop(); 198 bg_thread2.Stop(); 199 } 200 201 } // namespace proxy 202 } // namespace ppapi 203