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(¶ms); 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(¶ms); 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, ¶m); 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(¶ms); 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, ¶m); 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