Home | History | Annotate | Download | only in extensions
      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