Home | History | Annotate | Download | only in browser_plugin
      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 "content/renderer/browser_plugin/browser_plugin_browsertest.h"
      6 
      7 #include "base/debug/leak_annotations.h"
      8 #include "base/files/file_path.h"
      9 #include "base/memory/singleton.h"
     10 #include "base/path_service.h"
     11 #include "base/pickle.h"
     12 #include "content/public/common/content_constants.h"
     13 #include "content/public/renderer/content_renderer_client.h"
     14 #include "content/renderer/browser_plugin/browser_plugin.h"
     15 #include "content/renderer/browser_plugin/browser_plugin_manager_factory.h"
     16 #include "content/renderer/browser_plugin/mock_browser_plugin.h"
     17 #include "content/renderer/browser_plugin/mock_browser_plugin_manager.h"
     18 #include "content/renderer/render_thread_impl.h"
     19 #include "content/renderer/renderer_webkitplatformsupport_impl.h"
     20 #include "skia/ext/platform_canvas.h"
     21 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
     22 #include "third_party/WebKit/public/web/WebInputEvent.h"
     23 #include "third_party/WebKit/public/web/WebLocalFrame.h"
     24 #include "third_party/WebKit/public/web/WebScriptSource.h"
     25 
     26 namespace content {
     27 
     28 namespace {
     29 const char kHTMLForBrowserPluginObject[] =
     30     "<object id='browserplugin' width='640px' height='480px'"
     31     " src='foo' type='%s'></object>"
     32     "<script>document.querySelector('object').nonExistentAttribute;</script>";
     33 
     34 const char kHTMLForBrowserPluginWithAllAttributes[] =
     35     "<object id='browserplugin' width='640' height='480' type='%s'"
     36     " autosize maxheight='600' maxwidth='800' minheight='240'"
     37     " minwidth='320' name='Jim' partition='someid' src='foo'>";
     38 
     39 const char kHTMLForSourcelessPluginObject[] =
     40     "<object id='browserplugin' width='640px' height='480px' type='%s'>";
     41 
     42 std::string GetHTMLForBrowserPluginObject() {
     43   return base::StringPrintf(kHTMLForBrowserPluginObject,
     44                             kBrowserPluginMimeType);
     45 }
     46 
     47 }  // namespace
     48 
     49 // Test factory for creating test instances of BrowserPluginManager.
     50 class TestBrowserPluginManagerFactory : public BrowserPluginManagerFactory {
     51  public:
     52   virtual MockBrowserPluginManager* CreateBrowserPluginManager(
     53       RenderViewImpl* render_view) OVERRIDE {
     54     return new MockBrowserPluginManager(render_view);
     55   }
     56 
     57   // Singleton getter.
     58   static TestBrowserPluginManagerFactory* GetInstance() {
     59     return Singleton<TestBrowserPluginManagerFactory>::get();
     60   }
     61 
     62  protected:
     63   TestBrowserPluginManagerFactory() {}
     64   virtual ~TestBrowserPluginManagerFactory() {}
     65 
     66  private:
     67   // For Singleton.
     68   friend struct DefaultSingletonTraits<TestBrowserPluginManagerFactory>;
     69 
     70   DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginManagerFactory);
     71 };
     72 
     73 BrowserPluginTest::BrowserPluginTest() {}
     74 
     75 BrowserPluginTest::~BrowserPluginTest() {}
     76 
     77 void BrowserPluginTest::SetUp() {
     78   BrowserPluginManager::set_factory_for_testing(
     79       TestBrowserPluginManagerFactory::GetInstance());
     80   content::RenderViewTest::SetUp();
     81 }
     82 
     83 void BrowserPluginTest::TearDown() {
     84   BrowserPluginManager::set_factory_for_testing(
     85       TestBrowserPluginManagerFactory::GetInstance());
     86 #if defined(LEAK_SANITIZER)
     87   // Do this before shutting down V8 in RenderViewTest::TearDown().
     88   // http://crbug.com/328552
     89   __lsan_do_leak_check();
     90 #endif
     91   RenderViewTest::TearDown();
     92 }
     93 
     94 std::string BrowserPluginTest::ExecuteScriptAndReturnString(
     95     const std::string& script) {
     96   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
     97   v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
     98       blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
     99   if (value.IsEmpty() || !value->IsString())
    100     return std::string();
    101 
    102   v8::Local<v8::String> v8_str = value->ToString();
    103   int length = v8_str->Utf8Length() + 1;
    104   scoped_ptr<char[]> str(new char[length]);
    105   v8_str->WriteUtf8(str.get(), length);
    106   return str.get();
    107 }
    108 
    109 int BrowserPluginTest::ExecuteScriptAndReturnInt(
    110     const std::string& script) {
    111   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
    112   v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
    113       blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
    114   if (value.IsEmpty() || !value->IsInt32())
    115     return 0;
    116 
    117   return value->Int32Value();
    118 }
    119 
    120 // A return value of false means that a value was not present. The return value
    121 // of the script is stored in |result|
    122 bool BrowserPluginTest::ExecuteScriptAndReturnBool(
    123     const std::string& script, bool* result) {
    124   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
    125   v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
    126       blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
    127   if (value.IsEmpty() || !value->IsBoolean())
    128     return false;
    129 
    130   *result = value->BooleanValue();
    131   return true;
    132 }
    133 
    134 MockBrowserPlugin* BrowserPluginTest::GetCurrentPlugin() {
    135   BrowserPluginHostMsg_Attach_Params params;
    136   return GetCurrentPluginWithAttachParams(&params);
    137 }
    138 
    139 MockBrowserPlugin* BrowserPluginTest::GetCurrentPluginWithAttachParams(
    140     BrowserPluginHostMsg_Attach_Params* params) {
    141   MockBrowserPlugin* browser_plugin = static_cast<MockBrowserPluginManager*>(
    142       browser_plugin_manager())->last_plugin();
    143   if (!browser_plugin)
    144     return NULL;
    145   browser_plugin_manager()->AllocateInstanceID(browser_plugin);
    146 
    147   int instance_id = 0;
    148   const IPC::Message* msg =
    149       browser_plugin_manager()->sink().GetUniqueMessageMatching(
    150           BrowserPluginHostMsg_Attach::ID);
    151   if (!msg)
    152     return NULL;
    153 
    154   PickleIterator iter(*msg);
    155   if (!iter.ReadInt(&instance_id))
    156     return NULL;
    157 
    158   if (!IPC::ParamTraits<BrowserPluginHostMsg_Attach_Params>::Read(
    159       msg, &iter, params)) {
    160     return NULL;
    161   }
    162 
    163   browser_plugin->OnAttachACK(instance_id);
    164   return browser_plugin;
    165 }
    166 
    167 // This test verifies that an initial resize occurs when we instantiate the
    168 // browser plugin.
    169 TEST_F(BrowserPluginTest, InitialResize) {
    170   LoadHTML(GetHTMLForBrowserPluginObject().c_str());
    171   // Verify that the information in Attach is correct.
    172   BrowserPluginHostMsg_Attach_Params params;
    173   MockBrowserPlugin* browser_plugin = GetCurrentPluginWithAttachParams(&params);
    174 
    175   EXPECT_EQ(640, params.resize_guest_params.view_size.width());
    176   EXPECT_EQ(480, params.resize_guest_params.view_size.height());
    177   ASSERT_TRUE(browser_plugin);
    178 }
    179 
    180 // This test verifies that all attributes (present at the time of writing) are
    181 // parsed on initialization. However, this test does minimal checking of
    182 // correct behavior.
    183 TEST_F(BrowserPluginTest, ParseAllAttributes) {
    184   std::string html = base::StringPrintf(kHTMLForBrowserPluginWithAllAttributes,
    185                                         kBrowserPluginMimeType);
    186   LoadHTML(html.c_str());
    187   bool result;
    188   bool has_value = ExecuteScriptAndReturnBool(
    189       "document.getElementById('browserplugin').autosize", &result);
    190   EXPECT_TRUE(has_value);
    191   EXPECT_TRUE(result);
    192   int maxHeight = ExecuteScriptAndReturnInt(
    193       "document.getElementById('browserplugin').maxheight");
    194   EXPECT_EQ(600, maxHeight);
    195   int maxWidth = ExecuteScriptAndReturnInt(
    196       "document.getElementById('browserplugin').maxwidth");
    197   EXPECT_EQ(800, maxWidth);
    198   int minHeight = ExecuteScriptAndReturnInt(
    199       "document.getElementById('browserplugin').minheight");
    200   EXPECT_EQ(240, minHeight);
    201   int minWidth = ExecuteScriptAndReturnInt(
    202       "document.getElementById('browserplugin').minwidth");
    203   EXPECT_EQ(320, minWidth);
    204 }
    205 
    206 TEST_F(BrowserPluginTest, ResizeFlowControl) {
    207   LoadHTML(GetHTMLForBrowserPluginObject().c_str());
    208   MockBrowserPlugin* browser_plugin = GetCurrentPlugin();
    209   ASSERT_TRUE(browser_plugin);
    210   int instance_id = browser_plugin->guest_instance_id();
    211   // Send an UpdateRect to the BrowserPlugin to make sure the browser sees a
    212   // resize related (SetAutoSize) message.
    213   {
    214     // We send a stale UpdateRect to the BrowserPlugin.
    215     BrowserPluginMsg_UpdateRect_Params update_rect_params;
    216     update_rect_params.view_size = gfx::Size(640, 480);
    217     update_rect_params.scale_factor = 1.0f;
    218     update_rect_params.is_resize_ack = true;
    219     BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
    220     browser_plugin->OnMessageReceived(msg);
    221   }
    222 
    223   browser_plugin_manager()->sink().ClearMessages();
    224 
    225   // Resize the browser plugin three times.
    226 
    227   ExecuteJavaScript("document.getElementById('browserplugin').width = '641px'");
    228   GetMainFrame()->view()->layout();
    229   ProcessPendingMessages();
    230 
    231   ExecuteJavaScript("document.getElementById('browserplugin').width = '642px'");
    232   GetMainFrame()->view()->layout();
    233   ProcessPendingMessages();
    234 
    235   ExecuteJavaScript("document.getElementById('browserplugin').width = '643px'");
    236   GetMainFrame()->view()->layout();
    237   ProcessPendingMessages();
    238 
    239   // Expect to see one resize messsage in the sink. BrowserPlugin will not issue
    240   // subsequent resize requests until the first request is satisfied by the
    241   // guest. The rest of the messages could be
    242   // BrowserPluginHostMsg_UpdateGeometry msgs.
    243   EXPECT_LE(1u, browser_plugin_manager()->sink().message_count());
    244   for (size_t i = 0; i < browser_plugin_manager()->sink().message_count();
    245        ++i) {
    246     const IPC::Message* msg = browser_plugin_manager()->sink().GetMessageAt(i);
    247     if (msg->type() != BrowserPluginHostMsg_ResizeGuest::ID)
    248       EXPECT_EQ(msg->type(), BrowserPluginHostMsg_UpdateGeometry::ID);
    249   }
    250   const IPC::Message* msg =
    251       browser_plugin_manager()->sink().GetUniqueMessageMatching(
    252           BrowserPluginHostMsg_ResizeGuest::ID);
    253   ASSERT_TRUE(msg);
    254   BrowserPluginHostMsg_ResizeGuest::Param param;
    255   BrowserPluginHostMsg_ResizeGuest::Read(msg, &param);
    256   instance_id = param.a;
    257   BrowserPluginHostMsg_ResizeGuest_Params params = param.b;
    258   EXPECT_EQ(641, params.view_size.width());
    259   EXPECT_EQ(480, params.view_size.height());
    260 
    261   {
    262     // We send a stale UpdateRect to the BrowserPlugin.
    263     BrowserPluginMsg_UpdateRect_Params update_rect_params;
    264     update_rect_params.view_size = gfx::Size(641, 480);
    265     update_rect_params.scale_factor = 1.0f;
    266     update_rect_params.is_resize_ack = true;
    267     BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
    268     browser_plugin->OnMessageReceived(msg);
    269   }
    270   // Send the BrowserPlugin another UpdateRect, but this time with a size
    271   // that matches the size of the container.
    272   {
    273     BrowserPluginMsg_UpdateRect_Params update_rect_params;
    274     update_rect_params.view_size = gfx::Size(643, 480);
    275     update_rect_params.scale_factor = 1.0f;
    276     update_rect_params.is_resize_ack = true;
    277     BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
    278     browser_plugin->OnMessageReceived(msg);
    279   }
    280 }
    281 
    282 TEST_F(BrowserPluginTest, RemovePlugin) {
    283   LoadHTML(GetHTMLForBrowserPluginObject().c_str());
    284   MockBrowserPlugin* browser_plugin = GetCurrentPlugin();
    285   ASSERT_TRUE(browser_plugin);
    286 
    287   EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
    288       BrowserPluginHostMsg_PluginDestroyed::ID));
    289   ExecuteJavaScript("x = document.getElementById('browserplugin'); "
    290                     "x.parentNode.removeChild(x);");
    291   ProcessPendingMessages();
    292   EXPECT_TRUE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
    293       BrowserPluginHostMsg_PluginDestroyed::ID));
    294 }
    295 
    296 // This test verifies that PluginDestroyed messages do not get sent from a
    297 // BrowserPlugin that has never navigated.
    298 TEST_F(BrowserPluginTest, RemovePluginBeforeNavigation) {
    299   std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject,
    300                                         kBrowserPluginMimeType);
    301   LoadHTML(html.c_str());
    302   EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
    303       BrowserPluginHostMsg_PluginDestroyed::ID));
    304   ExecuteJavaScript("x = document.getElementById('browserplugin'); "
    305                     "x.parentNode.removeChild(x);");
    306   ProcessPendingMessages();
    307   EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
    308       BrowserPluginHostMsg_PluginDestroyed::ID));
    309 }
    310 
    311 // Verify that the 'partition' attribute on the browser plugin is parsed
    312 // correctly.
    313 TEST_F(BrowserPluginTest, AutoSizeAttributes) {
    314   std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject,
    315                                         kBrowserPluginMimeType);
    316   LoadHTML(html.c_str());
    317   const char* kSetAutoSizeParametersAndNavigate =
    318     "var browserplugin = document.getElementById('browserplugin');"
    319     "browserplugin.autosize = true;"
    320     "browserplugin.minwidth = 42;"
    321     "browserplugin.minheight = 43;"
    322     "browserplugin.maxwidth = 1337;"
    323     "browserplugin.maxheight = 1338;"
    324     "browserplugin.src = 'foobar';";
    325   const char* kDisableAutoSize =
    326     "document.getElementById('browserplugin').removeAttribute('autosize');";
    327 
    328   int instance_id = 0;
    329   // Set some autosize parameters before navigating then navigate.
    330   // Verify that the BrowserPluginHostMsg_Attach message contains
    331   // the correct autosize parameters.
    332   ExecuteJavaScript(kSetAutoSizeParametersAndNavigate);
    333   ProcessPendingMessages();
    334 
    335   BrowserPluginHostMsg_Attach_Params params;
    336   MockBrowserPlugin* browser_plugin = GetCurrentPluginWithAttachParams(&params);
    337   ASSERT_TRUE(browser_plugin);
    338 
    339   EXPECT_TRUE(params.auto_size_params.enable);
    340   EXPECT_EQ(42, params.auto_size_params.min_size.width());
    341   EXPECT_EQ(43, params.auto_size_params.min_size.height());
    342   EXPECT_EQ(1337, params.auto_size_params.max_size.width());
    343   EXPECT_EQ(1338, params.auto_size_params.max_size.height());
    344 
    345   // Disable autosize. AutoSize state will not be sent to the guest until
    346   // the guest has responded to the last resize request.
    347   ExecuteJavaScript(kDisableAutoSize);
    348   ProcessPendingMessages();
    349 
    350   const IPC::Message* auto_size_msg =
    351   browser_plugin_manager()->sink().GetUniqueMessageMatching(
    352       BrowserPluginHostMsg_SetAutoSize::ID);
    353   EXPECT_FALSE(auto_size_msg);
    354 
    355   // Send the BrowserPlugin an UpdateRect equal to its |max_size|.
    356   BrowserPluginMsg_UpdateRect_Params update_rect_params;
    357   update_rect_params.view_size = gfx::Size(1337, 1338);
    358   update_rect_params.scale_factor = 1.0f;
    359   update_rect_params.is_resize_ack = true;
    360   BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
    361   browser_plugin->OnMessageReceived(msg);
    362 
    363   // Verify that the autosize state has been updated.
    364   {
    365     const IPC::Message* auto_size_msg =
    366         browser_plugin_manager()->sink().GetUniqueMessageMatching(
    367             BrowserPluginHostMsg_SetAutoSize::ID);
    368     ASSERT_TRUE(auto_size_msg);
    369 
    370     BrowserPluginHostMsg_SetAutoSize::Param param;
    371     BrowserPluginHostMsg_SetAutoSize::Read(auto_size_msg, &param);
    372     BrowserPluginHostMsg_AutoSize_Params auto_size_params = param.b;
    373     BrowserPluginHostMsg_ResizeGuest_Params resize_params = param.c;
    374     EXPECT_FALSE(auto_size_params.enable);
    375     // These value are not populated (as an optimization) if autosize is
    376     // disabled.
    377     EXPECT_EQ(0, auto_size_params.min_size.width());
    378     EXPECT_EQ(0, auto_size_params.min_size.height());
    379     EXPECT_EQ(0, auto_size_params.max_size.width());
    380     EXPECT_EQ(0, auto_size_params.max_size.height());
    381   }
    382 }
    383 
    384 }  // namespace content
    385