native_renderer_messaging_service_unittest.cc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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/native_renderer_messaging_service.h"
  5. #include <memory>
  6. #include "base/strings/stringprintf.h"
  7. #include "components/crx_file/id_util.h"
  8. #include "content/public/common/child_process_host.h"
  9. #include "extensions/common/api/messaging/messaging_endpoint.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/common/extension_messages.h"
  14. #include "extensions/common/value_builder.h"
  15. #include "extensions/renderer/bindings/api_binding_test_util.h"
  16. #include "extensions/renderer/bindings/api_binding_types.h"
  17. #include "extensions/renderer/bindings/api_response_validator.h"
  18. #include "extensions/renderer/message_target.h"
  19. #include "extensions/renderer/messaging_util.h"
  20. #include "extensions/renderer/native_extension_bindings_system.h"
  21. #include "extensions/renderer/native_extension_bindings_system_test_base.h"
  22. #include "extensions/renderer/script_context.h"
  23. #include "extensions/renderer/script_context_set.h"
  24. namespace extensions {
  25. namespace {
  26. // Unfortunately, we have a layering violation in the runtime API. The runtime
  27. // API is defined at the //extensions layer, but the `MessageSender` type has an
  28. // optional `tabs.Tab` property. This causes issues in type validation because
  29. // when we try to look up the `tabs.Tab` property, it fails (since it's only
  30. // defined in //chrome). This is a "real bug" in that it's a layering violation,
  31. // but it doesn't have real-world implications since right now the only consumer
  32. // of //extensions is //chrome (and thus, the tabs API will always be defined).
  33. // Ignore validation for the affected runtime message-related events.
  34. class RuntimeMessageValidationIgnorer {
  35. public:
  36. RuntimeMessageValidationIgnorer()
  37. : test_handler_(base::BindRepeating(
  38. &RuntimeMessageValidationIgnorer::HardValidationFailure)) {
  39. test_handler_.IgnoreSignature("runtime.onMessage");
  40. test_handler_.IgnoreSignature("runtime.onMessageExternal");
  41. test_handler_.IgnoreSignature("runtime.onConnect");
  42. }
  43. ~RuntimeMessageValidationIgnorer() = default;
  44. private:
  45. // Hard-fail on any unexpected validation errors.
  46. static void HardValidationFailure(const std::string& name,
  47. const std::string& failure) {
  48. NOTREACHED() << "Unexpected validation failure: " << name << ", "
  49. << failure;
  50. }
  51. APIResponseValidator::TestHandler test_handler_;
  52. };
  53. } // namespace
  54. class NativeRendererMessagingServiceTest
  55. : public NativeExtensionBindingsSystemUnittest {
  56. public:
  57. NativeRendererMessagingServiceTest() {}
  58. NativeRendererMessagingServiceTest(
  59. const NativeRendererMessagingServiceTest&) = delete;
  60. NativeRendererMessagingServiceTest& operator=(
  61. const NativeRendererMessagingServiceTest&) = delete;
  62. ~NativeRendererMessagingServiceTest() override {}
  63. // NativeExtensionBindingsSystemUnittest:
  64. void SetUp() override {
  65. NativeExtensionBindingsSystemUnittest::SetUp();
  66. messaging_service_ =
  67. std::make_unique<NativeRendererMessagingService>(bindings_system());
  68. extension_ = ExtensionBuilder("foo").Build();
  69. RegisterExtension(extension_);
  70. v8::HandleScope handle_scope(isolate());
  71. v8::Local<v8::Context> context = MainContext();
  72. script_context_ = CreateScriptContext(context, extension_.get(),
  73. Feature::BLESSED_EXTENSION_CONTEXT);
  74. script_context_->set_url(extension_->url());
  75. bindings_system()->UpdateBindingsForContext(script_context_);
  76. }
  77. void TearDown() override {
  78. script_context_ = nullptr;
  79. extension_ = nullptr;
  80. messaging_service_.reset();
  81. NativeExtensionBindingsSystemUnittest::TearDown();
  82. }
  83. bool UseStrictIPCMessageSender() override { return true; }
  84. NativeRendererMessagingService* messaging_service() {
  85. return messaging_service_.get();
  86. }
  87. ScriptContext* script_context() { return script_context_; }
  88. const Extension* extension() { return extension_.get(); }
  89. private:
  90. std::unique_ptr<NativeRendererMessagingService> messaging_service_;
  91. ScriptContext* script_context_ = nullptr;
  92. scoped_refptr<const Extension> extension_;
  93. };
  94. TEST_F(NativeRendererMessagingServiceTest, ValidateMessagePort) {
  95. v8::HandleScope handle_scope(isolate());
  96. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  97. const PortId port_id(other_context_id, 0, false, SerializationFormat::kJson);
  98. EXPECT_FALSE(
  99. messaging_service()->HasPortForTesting(script_context(), port_id));
  100. EXPECT_CALL(*ipc_message_sender(),
  101. SendCloseMessagePort(MSG_ROUTING_NONE, port_id, false));
  102. messaging_service()->ValidateMessagePort(script_context_set(), port_id,
  103. nullptr);
  104. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  105. messaging_service()->CreatePortForTesting(script_context(), "channel",
  106. port_id);
  107. EXPECT_TRUE(
  108. messaging_service()->HasPortForTesting(script_context(), port_id));
  109. // With a valid port, we shouldn't dispatch a message to close it.
  110. messaging_service()->ValidateMessagePort(script_context_set(), port_id,
  111. nullptr);
  112. }
  113. TEST_F(NativeRendererMessagingServiceTest, OpenMessagePort) {
  114. RuntimeMessageValidationIgnorer message_validation_ignorer;
  115. v8::HandleScope handle_scope(isolate());
  116. v8::Local<v8::Context> context = MainContext();
  117. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  118. const PortId port_id(other_context_id, 0, false, SerializationFormat::kJson);
  119. EXPECT_FALSE(
  120. messaging_service()->HasPortForTesting(script_context(), port_id));
  121. const std::string channel_name = "some channel";
  122. ExtensionMsg_TabConnectionInfo tab_connection_info;
  123. tab_connection_info.frame_id = 0;
  124. const int tab_id = 10;
  125. GURL source_url("http://example.com");
  126. tab_connection_info.tab.Swap(
  127. DictionaryBuilder().Set("tabId", tab_id).Build().get());
  128. ExtensionMsg_ExternalConnectionInfo external_connection_info;
  129. external_connection_info.target_id = extension()->id();
  130. external_connection_info.source_endpoint =
  131. MessagingEndpoint::ForExtension(extension()->id());
  132. external_connection_info.source_url = source_url;
  133. external_connection_info.guest_process_id =
  134. content::ChildProcessHost::kInvalidUniqueID;
  135. external_connection_info.guest_render_frame_routing_id = 0;
  136. const char kAddListener[] =
  137. "(function() {\n"
  138. " chrome.runtime.onConnect.addListener(function(port) {\n"
  139. " this.eventFired = true;\n"
  140. " this.sender = port.sender\n"
  141. " });\n"
  142. "})";
  143. v8::Local<v8::Function> add_listener =
  144. FunctionFromString(context, kAddListener);
  145. RunFunctionOnGlobal(add_listener, context, 0, nullptr);
  146. EXPECT_CALL(*ipc_message_sender(),
  147. SendOpenMessagePort(MSG_ROUTING_NONE, port_id));
  148. messaging_service()->DispatchOnConnect(script_context_set(), port_id,
  149. channel_name, tab_connection_info,
  150. external_connection_info, nullptr);
  151. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  152. ASSERT_TRUE(
  153. messaging_service()->HasPortForTesting(script_context(), port_id));
  154. EXPECT_EQ("true", GetStringPropertyFromObject(context->Global(), context,
  155. "eventFired"));
  156. std::unique_ptr<base::DictionaryValue> expected_sender =
  157. DictionaryBuilder()
  158. .Set("frameId", 0)
  159. .Set("tab", DictionaryBuilder().Set("tabId", tab_id).Build())
  160. .Set("url", source_url.spec())
  161. .Set("id", extension()->id())
  162. .Build();
  163. EXPECT_EQ(ValueToString(*expected_sender),
  164. GetStringPropertyFromObject(context->Global(), context, "sender"));
  165. }
  166. TEST_F(NativeRendererMessagingServiceTest, DeliverMessageToPort) {
  167. v8::HandleScope handle_scope(isolate());
  168. v8::Local<v8::Context> context = MainContext();
  169. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  170. const PortId port_id1(other_context_id, 0, false, SerializationFormat::kJson);
  171. const PortId port_id2(other_context_id, 1, false, SerializationFormat::kJson);
  172. gin::Handle<GinPort> port1 = messaging_service()->CreatePortForTesting(
  173. script_context(), "channel1", port_id1);
  174. gin::Handle<GinPort> port2 = messaging_service()->CreatePortForTesting(
  175. script_context(), "channel2", port_id2);
  176. ASSERT_FALSE(port1.IsEmpty());
  177. const char kOnMessageListenerTemplate[] =
  178. "(function(port) {\n"
  179. " port.onMessage.addListener((message) => {\n"
  180. " this.%s = message;\n"
  181. " });\n"
  182. "})";
  183. const char kPort1Message[] = "port1Message";
  184. const char kPort2Message[] = "port2Message";
  185. {
  186. v8::Local<v8::Function> add_on_message_listener = FunctionFromString(
  187. context, base::StringPrintf(kOnMessageListenerTemplate, kPort1Message));
  188. v8::Local<v8::Value> args[] = {port1.ToV8()};
  189. RunFunctionOnGlobal(add_on_message_listener, context, std::size(args),
  190. args);
  191. }
  192. {
  193. v8::Local<v8::Function> add_on_message_listener = FunctionFromString(
  194. context, base::StringPrintf(kOnMessageListenerTemplate, kPort2Message));
  195. v8::Local<v8::Value> args[] = {port2.ToV8()};
  196. RunFunctionOnGlobal(add_on_message_listener, context, std::size(args),
  197. args);
  198. }
  199. // We've only added listeners (not dispatched any messages), so neither
  200. // listener should have been triggered.
  201. v8::Local<v8::Object> global = context->Global();
  202. EXPECT_EQ("undefined",
  203. GetStringPropertyFromObject(global, context, kPort1Message));
  204. EXPECT_EQ("undefined",
  205. GetStringPropertyFromObject(global, context, kPort2Message));
  206. const char kMessageString[] = R"({"data":"hello"})";
  207. messaging_service()->DeliverMessage(
  208. script_context_set(), port_id1,
  209. Message(kMessageString, SerializationFormat::kJson, false), nullptr);
  210. // Only port1 should have been notified of the message (ports only receive
  211. // messages directed to themselves).
  212. EXPECT_EQ(kMessageString,
  213. GetStringPropertyFromObject(global, context, kPort1Message));
  214. EXPECT_EQ("undefined",
  215. GetStringPropertyFromObject(global, context, kPort2Message));
  216. }
  217. TEST_F(NativeRendererMessagingServiceTest, DisconnectMessagePort) {
  218. v8::HandleScope handle_scope(isolate());
  219. v8::Local<v8::Context> context = MainContext();
  220. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  221. const PortId port_id1(other_context_id, 0, false, SerializationFormat::kJson);
  222. const PortId port_id2(other_context_id, 1, false, SerializationFormat::kJson);
  223. gin::Handle<GinPort> port1 = messaging_service()->CreatePortForTesting(
  224. script_context(), "channel1", port_id1);
  225. gin::Handle<GinPort> port2 = messaging_service()->CreatePortForTesting(
  226. script_context(), "channel2", port_id2);
  227. const char kOnDisconnectListenerTemplate[] =
  228. "(function(port) {\n"
  229. " port.onDisconnect.addListener(() => {\n"
  230. " this.%s = true;\n"
  231. " });\n"
  232. "})";
  233. const char kPort1Disconnect[] = "port1Disconnect";
  234. const char kPort2Disconnect[] = "port2Disconnect";
  235. {
  236. v8::Local<v8::Function> add_on_disconnect_listener = FunctionFromString(
  237. context,
  238. base::StringPrintf(kOnDisconnectListenerTemplate, kPort1Disconnect));
  239. v8::Local<v8::Value> args[] = {port1.ToV8()};
  240. RunFunctionOnGlobal(add_on_disconnect_listener, context, std::size(args),
  241. args);
  242. }
  243. {
  244. v8::Local<v8::Function> add_on_disconnect_listener = FunctionFromString(
  245. context,
  246. base::StringPrintf(kOnDisconnectListenerTemplate, kPort2Disconnect));
  247. v8::Local<v8::Value> args[] = {port2.ToV8()};
  248. RunFunctionOnGlobal(add_on_disconnect_listener, context, std::size(args),
  249. args);
  250. }
  251. v8::Local<v8::Object> global = context->Global();
  252. EXPECT_EQ("undefined",
  253. GetStringPropertyFromObject(global, context, kPort1Disconnect));
  254. EXPECT_EQ("undefined",
  255. GetStringPropertyFromObject(global, context, kPort2Disconnect));
  256. messaging_service()->DispatchOnDisconnect(script_context_set(), port_id1,
  257. std::string(), nullptr);
  258. EXPECT_EQ("true",
  259. GetStringPropertyFromObject(global, context, kPort1Disconnect));
  260. EXPECT_EQ("undefined",
  261. GetStringPropertyFromObject(global, context, kPort2Disconnect));
  262. }
  263. TEST_F(NativeRendererMessagingServiceTest, PostMessageFromJS) {
  264. v8::HandleScope handle_scope(isolate());
  265. v8::Local<v8::Context> context = MainContext();
  266. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  267. const PortId port_id(other_context_id, 0, false, SerializationFormat::kJson);
  268. gin::Handle<GinPort> port = messaging_service()->CreatePortForTesting(
  269. script_context(), "channel", port_id);
  270. v8::Local<v8::Object> port_object = port.ToV8().As<v8::Object>();
  271. const char kDispatchMessage[] =
  272. "(function(port) {\n"
  273. " port.postMessage({data: 'hello'});\n"
  274. "})";
  275. v8::Local<v8::Function> post_message =
  276. FunctionFromString(context, kDispatchMessage);
  277. v8::Local<v8::Value> args[] = {port_object};
  278. EXPECT_CALL(*ipc_message_sender(),
  279. SendPostMessageToPort(
  280. port_id, Message(R"({"data":"hello"})",
  281. SerializationFormat::kJson, false)));
  282. RunFunctionOnGlobal(post_message, context, std::size(args), args);
  283. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  284. }
  285. TEST_F(NativeRendererMessagingServiceTest, DisconnectFromJS) {
  286. v8::HandleScope handle_scope(isolate());
  287. v8::Local<v8::Context> context = MainContext();
  288. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  289. const PortId port_id(other_context_id, 0, false, SerializationFormat::kJson);
  290. gin::Handle<GinPort> port = messaging_service()->CreatePortForTesting(
  291. script_context(), "channel", port_id);
  292. v8::Local<v8::Object> port_object = port.ToV8().As<v8::Object>();
  293. const char kDispatchMessage[] =
  294. "(function(port) {\n"
  295. " port.disconnect();\n"
  296. "})";
  297. v8::Local<v8::Function> post_message =
  298. FunctionFromString(context, kDispatchMessage);
  299. v8::Local<v8::Value> args[] = {port_object};
  300. EXPECT_CALL(*ipc_message_sender(),
  301. SendCloseMessagePort(MSG_ROUTING_NONE, port_id, true));
  302. RunFunctionOnGlobal(post_message, context, std::size(args), args);
  303. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  304. }
  305. TEST_F(NativeRendererMessagingServiceTest, Connect) {
  306. v8::HandleScope handle_scope(isolate());
  307. const std::string kChannel = "channel";
  308. PortId expected_port_id(script_context()->context_id(), 0, true,
  309. SerializationFormat::kJson);
  310. MessageTarget target(MessageTarget::ForExtension(extension()->id()));
  311. EXPECT_CALL(*ipc_message_sender(),
  312. SendOpenMessageChannel(script_context(), expected_port_id, target,
  313. kChannel));
  314. gin::Handle<GinPort> new_port = messaging_service()->Connect(
  315. script_context(), target, "channel", SerializationFormat::kJson);
  316. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  317. ASSERT_FALSE(new_port.IsEmpty());
  318. EXPECT_EQ(expected_port_id, new_port->port_id());
  319. EXPECT_EQ(kChannel, new_port->name());
  320. EXPECT_FALSE(new_port->is_closed_for_testing());
  321. }
  322. // Tests sending a one-time message through the messaging service and getting a
  323. // response to a callback. Note that this is more thoroughly tested in the
  324. // OneTimeMessageHandler tests; this is just to ensure
  325. // NativeRendererMessagingService correctly forwards the calls.
  326. TEST_F(NativeRendererMessagingServiceTest, SendOneTimeMessageWithCallback) {
  327. v8::HandleScope handle_scope(isolate());
  328. v8::Local<v8::Context> context = MainContext();
  329. const std::string kChannel = "chrome.runtime.sendMessage";
  330. PortId port_id(script_context()->context_id(), 0, true,
  331. SerializationFormat::kJson);
  332. const char kEchoArgs[] =
  333. "(function() { this.replyArgs = Array.from(arguments); })";
  334. v8::Local<v8::Function> response_callback =
  335. FunctionFromString(context, kEchoArgs);
  336. // Send a message and expect a reply to a passed in callback. A new port
  337. // should be created, and should remain open until the response is sent.
  338. const Message message("\"hi\"", SerializationFormat::kJson, false);
  339. MessageTarget target(MessageTarget::ForExtension(extension()->id()));
  340. EXPECT_CALL(
  341. *ipc_message_sender(),
  342. SendOpenMessageChannel(script_context(), port_id, target, kChannel));
  343. EXPECT_CALL(*ipc_message_sender(), SendPostMessageToPort(port_id, message));
  344. v8::Local<v8::Promise> promise = messaging_service()->SendOneTimeMessage(
  345. script_context(), target, kChannel, message,
  346. binding::AsyncResponseType::kCallback, response_callback);
  347. // Since this is a callback based request, the returned promise should be
  348. // empty.
  349. EXPECT_TRUE(promise.IsEmpty());
  350. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  351. EXPECT_TRUE(
  352. messaging_service()->HasPortForTesting(script_context(), port_id));
  353. // Respond to the message. The response callback should be triggered, and the
  354. // port should be closed.
  355. EXPECT_CALL(*ipc_message_sender(),
  356. SendCloseMessagePort(MSG_ROUTING_NONE, port_id, true));
  357. messaging_service()->DeliverMessage(
  358. script_context_set(), port_id,
  359. Message("\"reply\"", SerializationFormat::kJson, false), nullptr);
  360. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  361. EXPECT_EQ("[\"reply\"]", GetStringPropertyFromObject(context->Global(),
  362. context, "replyArgs"));
  363. EXPECT_FALSE(
  364. messaging_service()->HasPortForTesting(script_context(), port_id));
  365. }
  366. // Similar to the above test, tests sending a one-time message through the
  367. // messaging service, but this time using a Promise for the response.
  368. TEST_F(NativeRendererMessagingServiceTest, SendOneTimeMessageWithPromise) {
  369. v8::HandleScope handle_scope(isolate());
  370. v8::Local<v8::Context> context = MainContext();
  371. const std::string kChannel = "chrome.runtime.sendMessage";
  372. PortId port_id(script_context()->context_id(), 0, true,
  373. SerializationFormat::kJson);
  374. // Send a message and expect a reply fulfilling a promise. A new port should
  375. // be created, and should remain open until the response is sent.
  376. const Message message("\"hi\"", SerializationFormat::kJson, false);
  377. MessageTarget target(MessageTarget::ForExtension(extension()->id()));
  378. EXPECT_CALL(
  379. *ipc_message_sender(),
  380. SendOpenMessageChannel(script_context(), port_id, target, kChannel));
  381. EXPECT_CALL(*ipc_message_sender(), SendPostMessageToPort(port_id, message));
  382. v8::Local<v8::Promise> promise = messaging_service()->SendOneTimeMessage(
  383. script_context(), target, kChannel, message,
  384. binding::AsyncResponseType::kPromise, v8::Local<v8::Function>());
  385. ASSERT_FALSE(promise.IsEmpty());
  386. EXPECT_EQ(v8::Promise::kPending, promise->State());
  387. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  388. EXPECT_TRUE(
  389. messaging_service()->HasPortForTesting(script_context(), port_id));
  390. // Respond to the message. The response callback should be triggered, and the
  391. // port should be closed.
  392. EXPECT_CALL(*ipc_message_sender(),
  393. SendCloseMessagePort(MSG_ROUTING_NONE, port_id, true));
  394. messaging_service()->DeliverMessage(
  395. script_context_set(), port_id,
  396. Message("\"reply\"", SerializationFormat::kJson, false), nullptr);
  397. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  398. EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
  399. EXPECT_EQ("\"reply\"", V8ToString(promise->Result(), context));
  400. EXPECT_FALSE(
  401. messaging_service()->HasPortForTesting(script_context(), port_id));
  402. }
  403. // Tests receiving a one-time message through the messaging service. Note that
  404. // this is more thoroughly tested in the OneTimeMessageHandler tests; this is
  405. // just to ensure NativeRendererMessagingService correctly forwards the calls.
  406. TEST_F(NativeRendererMessagingServiceTest, ReceiveOneTimeMessage) {
  407. RuntimeMessageValidationIgnorer message_validation_ignorer;
  408. v8::HandleScope handle_scope(isolate());
  409. v8::Local<v8::Context> context = MainContext();
  410. constexpr char kRegisterListener[] =
  411. "(function() {\n"
  412. " chrome.runtime.onMessage.addListener(\n"
  413. " function(message, sender, reply) {\n"
  414. " this.eventMessage = message;\n"
  415. " reply({data: 'hi'});\n"
  416. " });\n"
  417. "})";
  418. v8::Local<v8::Function> add_listener =
  419. FunctionFromString(context, kRegisterListener);
  420. RunFunctionOnGlobal(add_listener, context, 0, nullptr);
  421. const std::string kChannel = "chrome.runtime.sendMessage";
  422. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  423. const PortId port_id(other_context_id, 0, false, SerializationFormat::kJson);
  424. ExtensionMsg_TabConnectionInfo tab_connection_info;
  425. tab_connection_info.frame_id = 0;
  426. const int tab_id = 10;
  427. GURL source_url("http://example.com");
  428. tab_connection_info.tab.Swap(
  429. DictionaryBuilder().Set("tabId", tab_id).Build().get());
  430. ExtensionMsg_ExternalConnectionInfo external_connection_info;
  431. external_connection_info.target_id = extension()->id();
  432. external_connection_info.source_endpoint =
  433. MessagingEndpoint::ForExtension(extension()->id());
  434. external_connection_info.source_url = source_url;
  435. external_connection_info.guest_process_id =
  436. content::ChildProcessHost::kInvalidUniqueID;
  437. external_connection_info.guest_render_frame_routing_id = 0;
  438. // Open a receiver for the message.
  439. EXPECT_CALL(*ipc_message_sender(),
  440. SendOpenMessagePort(MSG_ROUTING_NONE, port_id));
  441. messaging_service()->DispatchOnConnect(script_context_set(), port_id,
  442. kChannel, tab_connection_info,
  443. external_connection_info, nullptr);
  444. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  445. EXPECT_TRUE(
  446. messaging_service()->HasPortForTesting(script_context(), port_id));
  447. // Post the message to the receiver. The receiver should respond, and the
  448. // port should close.
  449. EXPECT_CALL(*ipc_message_sender(),
  450. SendPostMessageToPort(
  451. port_id, Message(R"({"data":"hi"})",
  452. SerializationFormat::kJson, false)));
  453. EXPECT_CALL(*ipc_message_sender(),
  454. SendCloseMessagePort(MSG_ROUTING_NONE, port_id, true));
  455. messaging_service()->DeliverMessage(
  456. script_context_set(), port_id,
  457. Message("\"message\"", SerializationFormat::kJson, false), nullptr);
  458. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  459. EXPECT_FALSE(
  460. messaging_service()->HasPortForTesting(script_context(), port_id));
  461. }
  462. // Test sending a one-time message from an external source (e.g., a different
  463. // extension). This shouldn't conflict with messages sent from the same source.
  464. TEST_F(NativeRendererMessagingServiceTest, TestExternalOneTimeMessages) {
  465. RuntimeMessageValidationIgnorer message_validation_ignorer;
  466. v8::HandleScope handle_scope(isolate());
  467. v8::Local<v8::Context> context = MainContext();
  468. constexpr char kListeners[] =
  469. R"((function() {
  470. chrome.runtime.onMessage.addListener((message) => {
  471. this.onMessageReceived = message;
  472. return true; // Keep the channel open.
  473. });
  474. chrome.runtime.onMessageExternal.addListener((message) => {
  475. this.onMessageExternalReceived = message;
  476. return true; // Keep the channel open.
  477. });
  478. }))";
  479. v8::Local<v8::Function> add_listener =
  480. FunctionFromString(context, kListeners);
  481. RunFunctionOnGlobal(add_listener, context, 0, nullptr);
  482. base::UnguessableToken other_context_id = base::UnguessableToken::Create();
  483. int next_port_id = 0;
  484. const PortId on_message_port_id(other_context_id, ++next_port_id, false,
  485. SerializationFormat::kJson);
  486. const PortId on_message_external_port_id(other_context_id, ++next_port_id,
  487. false, SerializationFormat::kJson);
  488. auto open_port = [this](const PortId& port_id, const ExtensionId& source_id) {
  489. ExtensionMsg_TabConnectionInfo tab_connection_info;
  490. tab_connection_info.frame_id = 0;
  491. const int tab_id = 10;
  492. GURL source_url("http://example.com");
  493. tab_connection_info.tab.Swap(
  494. DictionaryBuilder().Set("tabId", tab_id).Build().get());
  495. ExtensionMsg_ExternalConnectionInfo external_connection_info;
  496. external_connection_info.target_id = extension()->id();
  497. external_connection_info.source_endpoint =
  498. MessagingEndpoint::ForExtension(source_id);
  499. external_connection_info.source_url = source_url;
  500. external_connection_info.guest_process_id =
  501. content::ChildProcessHost::kInvalidUniqueID;
  502. external_connection_info.guest_render_frame_routing_id = 0;
  503. // Open a receiver for the message.
  504. EXPECT_CALL(*ipc_message_sender(),
  505. SendOpenMessagePort(MSG_ROUTING_NONE, port_id));
  506. messaging_service()->DispatchOnConnect(
  507. script_context_set(), port_id, messaging_util::kSendMessageChannel,
  508. tab_connection_info, external_connection_info, nullptr);
  509. ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender());
  510. EXPECT_TRUE(
  511. messaging_service()->HasPortForTesting(script_context(), port_id));
  512. };
  513. open_port(on_message_port_id, extension()->id());
  514. const ExtensionId other_extension =
  515. crx_file::id_util::GenerateId("different");
  516. open_port(on_message_external_port_id, other_extension);
  517. EXPECT_CALL(*ipc_message_sender(),
  518. SendMessageResponsePending(MSG_ROUTING_NONE, on_message_port_id));
  519. messaging_service()->DeliverMessage(
  520. script_context_set(), on_message_port_id,
  521. Message("\"onMessage\"", SerializationFormat::kJson, false), nullptr);
  522. EXPECT_EQ("\"onMessage\"",
  523. GetStringPropertyFromObject(context->Global(), context,
  524. "onMessageReceived"));
  525. EXPECT_EQ("undefined",
  526. GetStringPropertyFromObject(context->Global(), context,
  527. "onMessageExternalReceived"));
  528. messaging_service()->DeliverMessage(
  529. script_context_set(), on_message_external_port_id,
  530. Message("\"onMessageExternal\"", SerializationFormat::kJson, false),
  531. nullptr);
  532. EXPECT_EQ("\"onMessage\"",
  533. GetStringPropertyFromObject(context->Global(), context,
  534. "onMessageReceived"));
  535. EXPECT_EQ("\"onMessageExternal\"",
  536. GetStringPropertyFromObject(context->Global(), context,
  537. "onMessageExternalReceived"));
  538. }
  539. } // namespace extensions