1 // Copyright (c) 2009 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/values.h" 6 #include "chrome/browser/extensions/extension_message_service.h" 7 #include "chrome/common/extensions/extension_messages.h" 8 #include "chrome/common/render_messages.h" 9 #include "chrome/renderer/extensions/event_bindings.h" 10 #include "chrome/renderer/extensions/renderer_extension_bindings.h" 11 #include "chrome/test/render_view_test.h" 12 #include "content/common/view_messages.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 15 static void DispatchOnConnect(int source_port_id, const std::string& name, 16 const std::string& tab_json) { 17 ListValue args; 18 args.Set(0, Value::CreateIntegerValue(source_port_id)); 19 args.Set(1, Value::CreateStringValue(name)); 20 args.Set(2, Value::CreateStringValue(tab_json)); 21 // Testing extensionId. Set in EventBindings::HandleContextCreated. 22 // We use the same id for source & target to similute an extension "talking 23 // to itself". 24 args.Set(3, Value::CreateStringValue(EventBindings::kTestingExtensionId)); 25 args.Set(4, Value::CreateStringValue(EventBindings::kTestingExtensionId)); 26 RendererExtensionBindings::Invoke( 27 "", ExtensionMessageService::kDispatchOnConnect, args, NULL, GURL()); 28 } 29 30 static void DispatchOnDisconnect(int source_port_id) { 31 ListValue args; 32 args.Set(0, Value::CreateIntegerValue(source_port_id)); 33 RendererExtensionBindings::Invoke( 34 "", ExtensionMessageService::kDispatchOnDisconnect, args, NULL, GURL()); 35 } 36 37 static void DispatchOnMessage(const std::string& message, int source_port_id) { 38 ListValue args; 39 args.Set(0, Value::CreateStringValue(message)); 40 args.Set(1, Value::CreateIntegerValue(source_port_id)); 41 RendererExtensionBindings::Invoke( 42 "", ExtensionMessageService::kDispatchOnMessage, args, NULL, GURL()); 43 } 44 45 // Tests that the bindings for opening a channel to an extension and sending 46 // and receiving messages through that channel all works. 47 TEST_F(RenderViewTest, ExtensionMessagesOpenChannel) { 48 render_thread_.sink().ClearMessages(); 49 LoadHTML("<body></body>"); 50 ExecuteJavaScript( 51 "var port = chrome.extension.connect({name:'testName'});" 52 "port.onMessage.addListener(doOnMessage);" 53 "port.postMessage({message: 'content ready'});" 54 "function doOnMessage(msg, port) {" 55 " alert('content got: ' + msg.val);" 56 "}"); 57 58 // Verify that we opened a channel and sent a message through it. 59 const IPC::Message* open_channel_msg = 60 render_thread_.sink().GetUniqueMessageMatching( 61 ExtensionHostMsg_OpenChannelToExtension::ID); 62 ASSERT_TRUE(open_channel_msg); 63 void* iter = IPC::SyncMessage::GetDataIterator(open_channel_msg); 64 ExtensionHostMsg_OpenChannelToExtension::SendParam open_params; 65 ASSERT_TRUE(IPC::ReadParam(open_channel_msg, &iter, &open_params)); 66 EXPECT_EQ("testName", open_params.d); 67 68 const IPC::Message* post_msg = 69 render_thread_.sink().GetUniqueMessageMatching( 70 ExtensionHostMsg_PostMessage::ID); 71 ASSERT_TRUE(post_msg); 72 ExtensionHostMsg_PostMessage::Param post_params; 73 ExtensionHostMsg_PostMessage::Read(post_msg, &post_params); 74 EXPECT_EQ("{\"message\":\"content ready\"}", post_params.b); 75 76 // Now simulate getting a message back from the other side. 77 render_thread_.sink().ClearMessages(); 78 const int kPortId = 0; 79 DispatchOnMessage("{\"val\": 42}", kPortId); 80 81 // Verify that we got it. 82 const IPC::Message* alert_msg = 83 render_thread_.sink().GetUniqueMessageMatching( 84 ViewHostMsg_RunJavaScriptMessage::ID); 85 ASSERT_TRUE(alert_msg); 86 iter = IPC::SyncMessage::GetDataIterator(alert_msg); 87 ViewHostMsg_RunJavaScriptMessage::SendParam alert_param; 88 ASSERT_TRUE(IPC::ReadParam(alert_msg, &iter, &alert_param)); 89 EXPECT_EQ(L"content got: 42", alert_param.a); 90 } 91 92 // Tests that the bindings for handling a new channel connection and channel 93 // closing all works. 94 TEST_F(RenderViewTest, ExtensionMessagesOnConnect) { 95 LoadHTML("<body></body>"); 96 ExecuteJavaScript( 97 "chrome.extension.onConnect.addListener(function (port) {" 98 " port.test = 24;" 99 " port.onMessage.addListener(doOnMessage);" 100 " port.onDisconnect.addListener(doOnDisconnect);" 101 " port.postMessage({message: 'onconnect from ' + port.tab.url + " 102 " ' name ' + port.name});" 103 "});" 104 "function doOnMessage(msg, port) {" 105 " alert('got: ' + msg.val);" 106 "}" 107 "function doOnDisconnect(port) {" 108 " alert('disconnected: ' + port.test);" 109 "}"); 110 111 render_thread_.sink().ClearMessages(); 112 113 // Simulate a new connection being opened. 114 const int kPortId = 0; 115 const std::string kPortName = "testName"; 116 DispatchOnConnect(kPortId, kPortName, "{\"url\":\"foo://bar\"}"); 117 118 // Verify that we handled the new connection by posting a message. 119 const IPC::Message* post_msg = 120 render_thread_.sink().GetUniqueMessageMatching( 121 ExtensionHostMsg_PostMessage::ID); 122 ASSERT_TRUE(post_msg); 123 ExtensionHostMsg_PostMessage::Param post_params; 124 ExtensionHostMsg_PostMessage::Read(post_msg, &post_params); 125 std::string expected_msg = 126 "{\"message\":\"onconnect from foo://bar name " + kPortName + "\"}"; 127 EXPECT_EQ(expected_msg, post_params.b); 128 129 // Now simulate getting a message back from the channel opener. 130 render_thread_.sink().ClearMessages(); 131 DispatchOnMessage("{\"val\": 42}", kPortId); 132 133 // Verify that we got it. 134 const IPC::Message* alert_msg = 135 render_thread_.sink().GetUniqueMessageMatching( 136 ViewHostMsg_RunJavaScriptMessage::ID); 137 ASSERT_TRUE(alert_msg); 138 void* iter = IPC::SyncMessage::GetDataIterator(alert_msg); 139 ViewHostMsg_RunJavaScriptMessage::SendParam alert_param; 140 ASSERT_TRUE(IPC::ReadParam(alert_msg, &iter, &alert_param)); 141 EXPECT_EQ(L"got: 42", alert_param.a); 142 143 // Now simulate the channel closing. 144 render_thread_.sink().ClearMessages(); 145 DispatchOnDisconnect(kPortId); 146 147 // Verify that we got it. 148 alert_msg = 149 render_thread_.sink().GetUniqueMessageMatching( 150 ViewHostMsg_RunJavaScriptMessage::ID); 151 ASSERT_TRUE(alert_msg); 152 iter = IPC::SyncMessage::GetDataIterator(alert_msg); 153 ASSERT_TRUE(IPC::ReadParam(alert_msg, &iter, &alert_param)); 154 EXPECT_EQ(L"disconnected: 24", alert_param.a); 155 } 156