one_time_message_handler_unittest.cc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. // Copyright 2017 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. #include "extensions/renderer/one_time_message_handler.h"
  5. #include <memory>
  6. #include "base/run_loop.h"
  7. #include "base/strings/stringprintf.h"
  8. #include "extensions/common/api/messaging/message.h"
  9. #include "extensions/common/api/messaging/port_id.h"
  10. #include "extensions/common/api/messaging/serialization_format.h"
  11. #include "extensions/common/extension.h"
  12. #include "extensions/common/extension_builder.h"
  13. #include "extensions/renderer/bindings/api_binding_test_util.h"
  14. #include "extensions/renderer/bindings/api_binding_types.h"
  15. #include "extensions/renderer/bindings/api_bindings_system.h"
  16. #include "extensions/renderer/bindings/api_request_handler.h"
  17. #include "extensions/renderer/message_target.h"
  18. #include "extensions/renderer/messaging_util.h"
  19. #include "extensions/renderer/native_extension_bindings_system.h"
  20. #include "extensions/renderer/native_extension_bindings_system_test_base.h"
  21. #include "extensions/renderer/script_context.h"
  22. #include "gin/data_object_builder.h"
  23. #include "ipc/ipc_message.h"
  24. namespace extensions {
  25. namespace {
  26. constexpr char kEchoArgsAndError[] =
  27. "(function() {\n"
  28. " this.replyArgs = Array.from(arguments);\n"
  29. " this.lastError =\n"
  30. " chrome.runtime.lastError ?\n"
  31. " chrome.runtime.lastError.message : undefined;\n"
  32. "})";
  33. } // namespace
  34. class OneTimeMessageHandlerTest : public NativeExtensionBindingsSystemUnittest {
  35. public:
  36. OneTimeMessageHandlerTest() {}
  37. OneTimeMessageHandlerTest(const OneTimeMessageHandlerTest&) = delete;
  38. OneTimeMessageHandlerTest& operator=(const OneTimeMessageHandlerTest&) =
  39. delete;
  40. ~OneTimeMessageHandlerTest() override {}
  41. void SetUp() override {
  42. NativeExtensionBindingsSystemUnittest::SetUp();
  43. message_handler_ =
  44. std::make_unique<OneTimeMessageHandler>(bindings_system());
  45. extension_ = ExtensionBuilder("foo").Build();
  46. RegisterExtension(extension_);
  47. v8::HandleScope handle_scope(isolate());
  48. v8::Local<v8::Context> context = MainContext();
  49. script_context_ = CreateScriptContext(context, extension_.get(),
  50. Feature::BLESSED_EXTENSION_CONTEXT);
  51. script_context_->set_url(extension_->url());
  52. bindings_system()->UpdateBindingsForContext(script_context_);
  53. }
  54. void TearDown() override {
  55. script_context_ = nullptr;
  56. extension_ = nullptr;
  57. message_handler_.reset();
  58. NativeExtensionBindingsSystemUnittest::TearDown();
  59. }
  60. bool UseStrictIPCMessageSender() override { return true; }
  61. std::string GetGlobalProperty(v8::Local<v8::Context> context,
  62. base::StringPiece property) {
  63. return GetStringPropertyFromObject(context->Global(), context, property);
  64. }
  65. OneTimeMessageHandler* message_handler() { return message_handler_.get(); }
  66. ScriptContext* script_context() { return script_context_; }
  67. const Extension* extension() { return extension_.get(); }
  68. private:
  69. std::unique_ptr<OneTimeMessageHandler> message_handler_;
  70. ScriptContext* script_context_ = nullptr;
  71. scoped_refptr<const Extension> extension_;
  72. };
  73. // Tests sending a message without expecting a reply, as in
  74. // chrome.runtime.sendMessage({foo: 'bar'});
  75. TEST_F(OneTimeMessageHandlerTest, SendMessageAndDontExpectReply) {
  76. const PortId port_id(script_context()->context_id(), 0, true,
  77. SerializationFormat::kJson);
  78. const Message message("\"Hello\"", SerializationFormat::kJson, false);
  79. v8::HandleScope handle_scope(isolate());
  80. // We should open a message port, send a message, and then close it
  81. // immediately.
  82. MessageTarget target(MessageTarget::ForExtension(extension()->id()));
  83. EXPECT_CALL(*ipc_message_sender(),
  84. SendOpenMessageChannel(script_context(), port_id, target,
  85. messaging_util::kSendMessageChannel));
  86. EXPECT_CALL(*ipc_message_sender(), SendPostMessageToPort(port_id, message));
  87. EXPECT_CALL(*ipc_message_sender(),
  88. SendCloseMessagePort(MSG_ROUTING_NONE, port_id, true));
  89. message_handler()->SendMessage(
  90. script_context(), port_id, target, messaging_util::kSendMessageChannel,
  91. message, binding::AsyncResponseType::kNone, v8::Local<v8::Function>());
  92. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  93. EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id));
  94. }
  95. // Tests sending a message and expecting a callback reply, as in
  96. // chrome.runtime.sendMessage({foo: 'bar'}, function(reply) { ... });
  97. TEST_F(OneTimeMessageHandlerTest, SendMessageAndExpectCallbackReply) {
  98. const PortId port_id(script_context()->context_id(), 0, true,
  99. SerializationFormat::kJson);
  100. const Message message("\"Hello\"", SerializationFormat::kJson, false);
  101. v8::HandleScope handle_scope(isolate());
  102. v8::Local<v8::Context> context = MainContext();
  103. v8::Local<v8::Function> callback =
  104. FunctionFromString(context, kEchoArgsAndError);
  105. APIRequestHandler* request_handler =
  106. bindings_system()->api_system()->request_handler();
  107. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  108. // We should open a message port and send a message, and the message port
  109. // should remain open (to allow for a reply).
  110. MessageTarget target(MessageTarget::ForExtension(extension()->id()));
  111. EXPECT_CALL(*ipc_message_sender(),
  112. SendOpenMessageChannel(script_context(), port_id, target,
  113. messaging_util::kSendMessageChannel));
  114. EXPECT_CALL(*ipc_message_sender(), SendPostMessageToPort(port_id, message));
  115. message_handler()->SendMessage(
  116. script_context(), port_id, target, messaging_util::kSendMessageChannel,
  117. message, binding::AsyncResponseType::kCallback, callback);
  118. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  119. // We should have added a pending request to the APIRequestHandler, but
  120. // shouldn't yet have triggered the reply callback.
  121. EXPECT_FALSE(request_handler->GetPendingRequestIdsForTesting().empty());
  122. EXPECT_TRUE(message_handler()->HasPort(script_context(), port_id));
  123. EXPECT_EQ("undefined", GetGlobalProperty(context, "replyArgs"));
  124. EXPECT_EQ("undefined", GetGlobalProperty(context, "lastError"));
  125. // Deliver the reply; the message port should close.
  126. EXPECT_CALL(*ipc_message_sender(),
  127. SendCloseMessagePort(MSG_ROUTING_NONE, port_id, true));
  128. const Message reply("\"Hi\"", SerializationFormat::kJson, false);
  129. message_handler()->DeliverMessage(script_context(), reply, port_id);
  130. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  131. EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id));
  132. // And the callback should have been triggered, completing the request.
  133. EXPECT_EQ("[\"Hi\"]", GetGlobalProperty(context, "replyArgs"));
  134. EXPECT_EQ("undefined", GetGlobalProperty(context, "lastError"));
  135. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  136. }
  137. // Tests sending a message and expecting a promise reply, as in
  138. // promise = chrome.runtime.sendMessage({foo: 'bar'});
  139. TEST_F(OneTimeMessageHandlerTest, SendMessageAndExpectPromiseReply) {
  140. const PortId port_id(script_context()->context_id(), 0, true,
  141. SerializationFormat::kJson);
  142. const Message message("\"Hello\"", SerializationFormat::kJson, false);
  143. v8::HandleScope handle_scope(isolate());
  144. v8::Local<v8::Context> context = MainContext();
  145. APIRequestHandler* request_handler =
  146. bindings_system()->api_system()->request_handler();
  147. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  148. // We should open a message port and send a message, and the message port
  149. // should remain open (to allow for a reply).
  150. MessageTarget target(MessageTarget::ForExtension(extension()->id()));
  151. EXPECT_CALL(*ipc_message_sender(),
  152. SendOpenMessageChannel(script_context(), port_id, target,
  153. messaging_util::kSendMessageChannel));
  154. EXPECT_CALL(*ipc_message_sender(), SendPostMessageToPort(port_id, message));
  155. v8::Local<v8::Promise> promise = message_handler()->SendMessage(
  156. script_context(), port_id, target, messaging_util::kSendMessageChannel,
  157. message, binding::AsyncResponseType::kPromise, v8::Local<v8::Function>());
  158. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  159. ASSERT_FALSE(promise.IsEmpty());
  160. // We should have added a pending request to the APIRequestHandler, but
  161. // shouldn't yet have fulfilled the related promise.
  162. EXPECT_FALSE(request_handler->GetPendingRequestIdsForTesting().empty());
  163. EXPECT_TRUE(message_handler()->HasPort(script_context(), port_id));
  164. EXPECT_EQ(v8::Promise::kPending, promise->State());
  165. // Deliver the reply; the message port should close.
  166. EXPECT_CALL(*ipc_message_sender(),
  167. SendCloseMessagePort(MSG_ROUTING_NONE, port_id, true));
  168. const Message reply("\"Hi\"", SerializationFormat::kJson, false);
  169. message_handler()->DeliverMessage(script_context(), reply, port_id);
  170. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  171. EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id));
  172. // And the callback should have been triggered, completing the request.
  173. EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
  174. EXPECT_EQ("\"Hi\"", V8ToString(promise->Result(), context));
  175. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  176. }
  177. // Tests disconnecting an opener (initiator of a sendMessage() call) when using
  178. // callbacks. This can happen when no receiving end exists (i.e., no listener to
  179. // runtime.onMessage).
  180. TEST_F(OneTimeMessageHandlerTest, DisconnectOpenerCallback) {
  181. const PortId port_id(script_context()->context_id(), 0, true,
  182. SerializationFormat::kJson);
  183. const Message message("\"Hello\"", SerializationFormat::kJson, false);
  184. v8::HandleScope handle_scope(isolate());
  185. v8::Local<v8::Context> context = MainContext();
  186. v8::Local<v8::Function> callback =
  187. FunctionFromString(context, kEchoArgsAndError);
  188. MessageTarget target(MessageTarget::ForExtension(extension()->id()));
  189. EXPECT_CALL(*ipc_message_sender(),
  190. SendOpenMessageChannel(script_context(), port_id, target,
  191. messaging_util::kSendMessageChannel));
  192. EXPECT_CALL(*ipc_message_sender(), SendPostMessageToPort(port_id, message));
  193. message_handler()->SendMessage(
  194. script_context(), port_id, target, messaging_util::kSendMessageChannel,
  195. message, binding::AsyncResponseType::kCallback, callback);
  196. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  197. EXPECT_EQ("undefined", GetGlobalProperty(context, "replyArgs"));
  198. EXPECT_EQ("undefined", GetGlobalProperty(context, "lastError"));
  199. // Disconnect the opener with an error. The callback should be triggered, and
  200. // the port should be removed. chrome.runtime.lastError should have been
  201. // populated.
  202. message_handler()->Disconnect(script_context(), port_id, "No receiving end");
  203. EXPECT_EQ("[]", GetGlobalProperty(context, "replyArgs"));
  204. EXPECT_EQ("\"No receiving end\"", GetGlobalProperty(context, "lastError"));
  205. EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id));
  206. }
  207. // Tests disconnecting an opener (initiator of a sendMessage() call) when using
  208. // promises. This can happen when no receiving end exists (i.e., no listener to
  209. // runtime.onMessage).
  210. TEST_F(OneTimeMessageHandlerTest, DisconnectOpenerPromise) {
  211. const PortId port_id(script_context()->context_id(), 0, true,
  212. SerializationFormat::kJson);
  213. const Message message("\"Hello\"", SerializationFormat::kJson, false);
  214. v8::HandleScope handle_scope(isolate());
  215. v8::Local<v8::Context> context = MainContext();
  216. MessageTarget target(MessageTarget::ForExtension(extension()->id()));
  217. EXPECT_CALL(*ipc_message_sender(),
  218. SendOpenMessageChannel(script_context(), port_id, target,
  219. messaging_util::kSendMessageChannel));
  220. EXPECT_CALL(*ipc_message_sender(), SendPostMessageToPort(port_id, message));
  221. v8::Local<v8::Promise> promise = message_handler()->SendMessage(
  222. script_context(), port_id, target, messaging_util::kSendMessageChannel,
  223. message, binding::AsyncResponseType::kPromise, v8::Local<v8::Function>());
  224. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  225. EXPECT_EQ(v8::Promise::kPending, promise->State());
  226. // Disconnect the opener with an error. The promise should be rejected with
  227. // the error and the port should be removed.
  228. message_handler()->Disconnect(script_context(), port_id, "No receiving end");
  229. EXPECT_EQ(v8::Promise::kRejected, promise->State());
  230. ASSERT_TRUE(promise->Result()->IsNativeError());
  231. EXPECT_EQ("\"No receiving end\"",
  232. GetStringPropertyFromObject(promise->Result().As<v8::Object>(),
  233. context, "message"));
  234. EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id));
  235. }
  236. // Tests delivering a message to a receiver and not replying, as in
  237. // chrome.runtime.onMessage.addListener(function(message, sender, reply) {
  238. // ...
  239. // });
  240. TEST_F(OneTimeMessageHandlerTest, DeliverMessageToReceiverWithNoReply) {
  241. v8::HandleScope handle_scope(isolate());
  242. v8::Local<v8::Context> context = MainContext();
  243. constexpr char kRegisterListener[] =
  244. "(function() {\n"
  245. " chrome.runtime.onMessage.addListener(\n"
  246. " function(message, sender, reply) {\n"
  247. " this.eventMessage = message;\n"
  248. " this.eventSender = sender;\n"
  249. " return true; // Reply later\n"
  250. " });\n"
  251. "})";
  252. v8::Local<v8::Function> add_listener =
  253. FunctionFromString(context, kRegisterListener);
  254. RunFunctionOnGlobal(add_listener, context, 0, nullptr);
  255. EXPECT_EQ("undefined", GetGlobalProperty(context, "eventMessage"));
  256. EXPECT_EQ("undefined", GetGlobalProperty(context, "eventSender"));
  257. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  258. const PortId port_id(other_context_id, 0, false, SerializationFormat::kJson);
  259. EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id));
  260. v8::Local<v8::Object> sender =
  261. gin::DataObjectBuilder(isolate())
  262. .Set("origin", std::string("https://example.com"))
  263. .Build();
  264. message_handler()->AddReceiver(script_context(), port_id, sender,
  265. messaging_util::kOnMessageEvent);
  266. EXPECT_TRUE(message_handler()->HasPort(script_context(), port_id));
  267. EXPECT_EQ("undefined", GetGlobalProperty(context, "eventMessage"));
  268. EXPECT_EQ("undefined", GetGlobalProperty(context, "eventSender"));
  269. EXPECT_CALL(*ipc_message_sender(),
  270. SendMessageResponsePending(MSG_ROUTING_NONE, port_id));
  271. const Message message("\"Hi\"", SerializationFormat::kJson, false);
  272. message_handler()->DeliverMessage(script_context(), message, port_id);
  273. EXPECT_EQ("\"Hi\"", GetGlobalProperty(context, "eventMessage"));
  274. EXPECT_EQ(R"({"origin":"https://example.com"})",
  275. GetGlobalProperty(context, "eventSender"));
  276. // TODO(devlin): Right now, the port lives eternally. In JS bindings, we have
  277. // two ways of dealing with this:
  278. // - monitoring the lifetime of the reply object
  279. // - requiring the extension to return true from an onMessage handler
  280. // We should implement these and test lifetime.
  281. }
  282. // Tests delivering a message to a receiver and replying, as in
  283. // chrome.runtime.onMessage.addListener(function(message, sender, reply) {
  284. // reply('foo');
  285. // });
  286. TEST_F(OneTimeMessageHandlerTest, DeliverMessageToReceiverAndReply) {
  287. v8::HandleScope handle_scope(isolate());
  288. v8::Local<v8::Context> context = MainContext();
  289. constexpr char kRegisterListener[] =
  290. "(function() {\n"
  291. " chrome.runtime.onMessage.addListener(\n"
  292. " function(message, sender, reply) {\n"
  293. " reply({data: 'hey'});\n"
  294. " });\n"
  295. "})";
  296. v8::Local<v8::Function> add_listener =
  297. FunctionFromString(context, kRegisterListener);
  298. RunFunctionOnGlobal(add_listener, context, 0, nullptr);
  299. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  300. const PortId port_id(other_context_id, 0, false, SerializationFormat::kJson);
  301. EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id));
  302. v8::Local<v8::Object> sender = v8::Object::New(isolate());
  303. message_handler()->AddReceiver(script_context(), port_id, sender,
  304. messaging_util::kOnMessageEvent);
  305. EXPECT_TRUE(message_handler()->HasPort(script_context(), port_id));
  306. const Message message("\"Hi\"", SerializationFormat::kJson, false);
  307. // When the listener replies, we should post the reply to the message port and
  308. // close the channel.
  309. EXPECT_CALL(*ipc_message_sender(),
  310. SendPostMessageToPort(
  311. port_id, Message(R"({"data":"hey"})",
  312. SerializationFormat::kJson, false)));
  313. EXPECT_CALL(*ipc_message_sender(),
  314. SendCloseMessagePort(MSG_ROUTING_NONE, port_id, true));
  315. message_handler()->DeliverMessage(script_context(), message, port_id);
  316. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  317. EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id));
  318. }
  319. // Tests that nothing breaks when trying to call the reply callback multiple
  320. // times.
  321. TEST_F(OneTimeMessageHandlerTest, TryReplyingMultipleTimes) {
  322. v8::HandleScope handle_scope(isolate());
  323. v8::Local<v8::Context> context = MainContext();
  324. constexpr char kRegisterListener[] =
  325. "(function() {\n"
  326. " chrome.runtime.onMessage.addListener(\n"
  327. " function(message, sender, reply) {\n"
  328. " this.sendReply = reply;\n"
  329. " return true; // Reply later\n"
  330. " });\n"
  331. "})";
  332. v8::Local<v8::Function> add_listener =
  333. FunctionFromString(context, kRegisterListener);
  334. RunFunctionOnGlobal(add_listener, context, 0, nullptr);
  335. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  336. const PortId port_id(other_context_id, 0, false, SerializationFormat::kJson);
  337. v8::Local<v8::Object> sender = v8::Object::New(isolate());
  338. message_handler()->AddReceiver(script_context(), port_id, sender,
  339. messaging_util::kOnMessageEvent);
  340. const Message message("\"Hi\"", SerializationFormat::kJson, false);
  341. EXPECT_CALL(*ipc_message_sender(),
  342. SendMessageResponsePending(MSG_ROUTING_NONE, port_id));
  343. message_handler()->DeliverMessage(script_context(), message, port_id);
  344. v8::Local<v8::Value> reply =
  345. GetPropertyFromObject(context->Global(), context, "sendReply");
  346. ASSERT_FALSE(reply.IsEmpty());
  347. ASSERT_TRUE(reply->IsFunction());
  348. v8::Local<v8::Value> reply_arg = V8ValueFromScriptSource(context, "'hi'");
  349. v8::Local<v8::Value> args[] = {reply_arg};
  350. EXPECT_CALL(
  351. *ipc_message_sender(),
  352. SendPostMessageToPort(
  353. port_id, Message("\"hi\"", SerializationFormat::kJson, false)));
  354. EXPECT_CALL(*ipc_message_sender(),
  355. SendCloseMessagePort(MSG_ROUTING_NONE, port_id, true));
  356. RunFunction(reply.As<v8::Function>(), context, std::size(args), args);
  357. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  358. EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id));
  359. // Running the reply function a second time shouldn't do anything.
  360. // TODO(devlin): Add an error message.
  361. RunFunction(reply.As<v8::Function>(), context, std::size(args), args);
  362. EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id));
  363. }
  364. // Test starting a new sendMessage call from a sendMessage listener.
  365. TEST_F(OneTimeMessageHandlerTest, SendMessageInListener) {
  366. v8::HandleScope handle_scope(isolate());
  367. v8::Local<v8::Context> context = MainContext();
  368. constexpr char kRegisterListener[] =
  369. "(function() {\n"
  370. " chrome.runtime.onMessage.addListener(\n"
  371. " function(message, sender, reply) {\n"
  372. " chrome.runtime.sendMessage('foo', function() {});\n"
  373. " });\n"
  374. "})";
  375. v8::Local<v8::Function> add_listener =
  376. FunctionFromString(context, kRegisterListener);
  377. RunFunctionOnGlobal(add_listener, context, 0, nullptr);
  378. base::UnguessableToken sender_context_id = base::UnguessableToken::Create();
  379. const PortId original_port_id(sender_context_id, 0, false,
  380. SerializationFormat::kJson);
  381. v8::Local<v8::Object> sender = v8::Object::New(isolate());
  382. message_handler()->AddReceiver(script_context(), original_port_id, sender,
  383. messaging_util::kOnMessageEvent);
  384. // On delivering the message, we expect the listener to open a new message
  385. // channel by using sendMessage(). The original message channel will be
  386. // closed.
  387. const PortId listener_created_port_id(script_context()->context_id(), 0, true,
  388. SerializationFormat::kJson);
  389. const Message listener_sent_message("\"foo\"", SerializationFormat::kJson,
  390. false);
  391. MessageTarget target(MessageTarget::ForExtension(extension()->id()));
  392. EXPECT_CALL(
  393. *ipc_message_sender(),
  394. SendOpenMessageChannel(script_context(), listener_created_port_id, target,
  395. messaging_util::kSendMessageChannel));
  396. EXPECT_CALL(
  397. *ipc_message_sender(),
  398. SendPostMessageToPort(listener_created_port_id, listener_sent_message));
  399. EXPECT_CALL(*ipc_message_sender(),
  400. SendCloseMessagePort(MSG_ROUTING_NONE, original_port_id, false));
  401. const Message message("\"Hi\"", SerializationFormat::kJson, false);
  402. message_handler()->DeliverMessage(script_context(), message,
  403. original_port_id);
  404. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  405. }
  406. // Test using sendMessage from the reply to a sendMessage call.
  407. TEST_F(OneTimeMessageHandlerTest, SendMessageInCallback) {
  408. v8::HandleScope handle_scope(isolate());
  409. v8::Local<v8::Context> context = MainContext();
  410. constexpr char kSendMessage[] =
  411. "(function() {\n"
  412. " chrome.runtime.sendMessage(\n"
  413. " 'foo',\n"
  414. " function(reply) {\n"
  415. " chrome.runtime.sendMessage('bar', function() {});\n"
  416. " });\n"
  417. "})";
  418. v8::Local<v8::Function> send_message =
  419. FunctionFromString(context, kSendMessage);
  420. // Running the function should send one message ('foo'), which will wait for
  421. // a reply.
  422. const PortId original_port_id(script_context()->context_id(), 0, true,
  423. SerializationFormat::kJson);
  424. const Message original_message("\"foo\"", SerializationFormat::kJson, false);
  425. MessageTarget target(MessageTarget::ForExtension(extension()->id()));
  426. EXPECT_CALL(*ipc_message_sender(),
  427. SendOpenMessageChannel(script_context(), original_port_id, target,
  428. messaging_util::kSendMessageChannel));
  429. EXPECT_CALL(*ipc_message_sender(),
  430. SendPostMessageToPort(original_port_id, original_message));
  431. RunFunctionOnGlobal(send_message, context, 0, nullptr);
  432. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  433. // Upon delivering the reply to the sender, it should send a second message
  434. // ('bar'). The original message channel should be closed.
  435. const PortId new_port_id(script_context()->context_id(), 1, true,
  436. SerializationFormat::kJson);
  437. EXPECT_CALL(*ipc_message_sender(),
  438. SendOpenMessageChannel(script_context(), new_port_id, target,
  439. messaging_util::kSendMessageChannel));
  440. EXPECT_CALL(
  441. *ipc_message_sender(),
  442. SendPostMessageToPort(
  443. new_port_id, Message("\"bar\"", SerializationFormat::kJson, false)));
  444. EXPECT_CALL(*ipc_message_sender(),
  445. SendCloseMessagePort(MSG_ROUTING_NONE, original_port_id, true));
  446. const Message reply("\"reply\"", SerializationFormat::kJson, false);
  447. message_handler()->DeliverMessage(script_context(), reply, original_port_id);
  448. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  449. }
  450. TEST_F(OneTimeMessageHandlerTest, ResponseCallbackGarbageCollected) {
  451. v8::HandleScope handle_scope(isolate());
  452. v8::Local<v8::Context> context = MainContext();
  453. constexpr char kRegisterListener[] =
  454. "(function() {\n"
  455. " chrome.runtime.onMessage.addListener(\n"
  456. " function(message, sender, reply) {\n"
  457. " return true; // Reply later\n"
  458. " });\n"
  459. "})";
  460. v8::Local<v8::Function> add_listener =
  461. FunctionFromString(context, kRegisterListener);
  462. RunFunctionOnGlobal(add_listener, context, 0, nullptr);
  463. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  464. const PortId port_id(other_context_id, 0, false, SerializationFormat::kJson);
  465. v8::Local<v8::Object> sender = v8::Object::New(isolate());
  466. message_handler()->AddReceiver(script_context(), port_id, sender,
  467. messaging_util::kOnMessageEvent);
  468. const Message message("\"Hi\"", SerializationFormat::kJson, false);
  469. EXPECT_CALL(*ipc_message_sender(),
  470. SendMessageResponsePending(MSG_ROUTING_NONE, port_id));
  471. EXPECT_CALL(*ipc_message_sender(),
  472. SendCloseMessagePort(MSG_ROUTING_NONE, port_id, false));
  473. message_handler()->DeliverMessage(script_context(), message, port_id);
  474. EXPECT_TRUE(message_handler()->HasPort(script_context(), port_id));
  475. EXPECT_EQ(
  476. 1, message_handler()->GetPendingCallbackCountForTest(script_context()));
  477. // The listener didn't retain the reply callback, so it should be garbage
  478. // collected and the related pending callback should have been cleared.
  479. RunGarbageCollection();
  480. base::RunLoop().RunUntilIdle();
  481. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  482. EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id));
  483. EXPECT_EQ(
  484. 0, message_handler()->GetPendingCallbackCountForTest(script_context()));
  485. }
  486. // runtime.onMessage requires that a listener return `true` if they intend to
  487. // respond to the message asynchronously. Verify that we close the port if no
  488. // listener does so.
  489. TEST_F(OneTimeMessageHandlerTest, ChannelClosedIfTrueNotReturned) {
  490. v8::HandleScope handle_scope(isolate());
  491. v8::Local<v8::Context> context = MainContext();
  492. auto register_listener = [context](const char* listener) {
  493. constexpr char kRegisterListenerTemplate[] =
  494. "(function() { chrome.runtime.onMessage.addListener(%s); })";
  495. v8::Local<v8::Function> add_listener = FunctionFromString(
  496. context, base::StringPrintf(kRegisterListenerTemplate, listener));
  497. RunFunctionOnGlobal(add_listener, context, 0, nullptr);
  498. };
  499. register_listener("function(message, reply, sender) { }");
  500. // Add a listener that returns a truthy value, but not `true`.
  501. register_listener("function(message, reply, sender) { return {}; }");
  502. // Add a listener that throws an error.
  503. register_listener(
  504. "function(message, reply, sender) { throw new Error('hi!'); }");
  505. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  506. const PortId port_id(other_context_id, 0, false, SerializationFormat::kJson);
  507. v8::Local<v8::Object> sender = v8::Object::New(isolate());
  508. message_handler()->AddReceiver(script_context(), port_id, sender,
  509. messaging_util::kOnMessageEvent);
  510. EXPECT_TRUE(message_handler()->HasPort(script_context(), port_id));
  511. TestJSRunner::AllowErrors allow_errors;
  512. // Dispatch the message. Since none of these listeners return `true`, the port
  513. // should close.
  514. const Message message("\"Hi\"", SerializationFormat::kJson, false);
  515. EXPECT_CALL(*ipc_message_sender(),
  516. SendCloseMessagePort(MSG_ROUTING_NONE, port_id, false));
  517. message_handler()->DeliverMessage(script_context(), message, port_id);
  518. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  519. EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id));
  520. // If any of the listeners return `true`, the channel should be left open.
  521. register_listener("function(message, reply, sender) { return true; }");
  522. message_handler()->AddReceiver(script_context(), port_id, sender,
  523. messaging_util::kOnMessageEvent);
  524. EXPECT_TRUE(message_handler()->HasPort(script_context(), port_id));
  525. EXPECT_CALL(*ipc_message_sender(),
  526. SendMessageResponsePending(MSG_ROUTING_NONE, port_id));
  527. message_handler()->DeliverMessage(script_context(), message, port_id);
  528. EXPECT_TRUE(message_handler()->HasPort(script_context(), port_id));
  529. }
  530. } // namespace extensions