1// Copyright 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 "base/command_line.h" 6#include "base/memory/singleton.h" 7#include "base/run_loop.h" 8#include "base/strings/string_split.h" 9#include "base/strings/string_util.h" 10#include "base/strings/utf_string_conversions.h" 11#include "base/test/test_timeouts.h" 12#include "content/browser/browser_plugin/browser_plugin_guest.h" 13#include "content/browser/browser_plugin/browser_plugin_host_factory.h" 14#include "content/browser/browser_plugin/test_browser_plugin_embedder.h" 15#include "content/browser/browser_plugin/test_browser_plugin_guest.h" 16#include "content/browser/browser_plugin/test_browser_plugin_guest_delegate.h" 17#include "content/browser/browser_plugin/test_browser_plugin_guest_manager.h" 18#include "content/browser/child_process_security_policy_impl.h" 19#include "content/browser/renderer_host/render_view_host_impl.h" 20#include "content/browser/web_contents/web_contents_impl.h" 21#include "content/common/browser_plugin/browser_plugin_messages.h" 22#include "content/common/view_messages.h" 23#include "content/public/browser/notification_service.h" 24#include "content/public/browser/notification_types.h" 25#include "content/public/browser/render_widget_host_view.h" 26#include "content/public/browser/web_contents_observer.h" 27#include "content/public/common/content_switches.h" 28#include "content/public/common/drop_data.h" 29#include "content/public/common/url_constants.h" 30#include "content/public/test/browser_test_utils.h" 31#include "content/public/test/test_utils.h" 32#include "content/shell/browser/shell.h" 33#include "content/test/content_browser_test.h" 34#include "content/test/content_browser_test_utils.h" 35#include "net/base/net_util.h" 36#include "net/test/embedded_test_server/embedded_test_server.h" 37#include "net/test/embedded_test_server/http_request.h" 38#include "net/test/embedded_test_server/http_response.h" 39#include "net/test/spawned_test_server/spawned_test_server.h" 40#include "third_party/WebKit/public/web/WebInputEvent.h" 41 42using blink::WebInputEvent; 43using blink::WebMouseEvent; 44using content::BrowserPluginEmbedder; 45using content::BrowserPluginGuest; 46using content::BrowserPluginHostFactory; 47using content::WebContentsImpl; 48 49const char kHTMLForGuest[] = 50 "data:text/html,<html><body>hello world</body></html>"; 51 52const char kHTMLForGuestTouchHandler[] = 53 "data:text/html,<html><body><div id=\"touch\">With touch</div></body>" 54 "<script type=\"text/javascript\">" 55 "function handler() {}" 56 "function InstallTouchHandler() { " 57 " document.getElementById(\"touch\").addEventListener(\"touchstart\", " 58 " handler);" 59 "}" 60 "function UninstallTouchHandler() { " 61 " document.getElementById(\"touch\").removeEventListener(\"touchstart\", " 62 " handler);" 63 "}" 64 "</script></html>"; 65 66const char kHTMLForGuestAcceptDrag[] = 67 "data:text/html,<html><body>" 68 "<script>" 69 "function dropped() {" 70 " document.title = \"DROPPED\";" 71 "}" 72 "</script>" 73 "<textarea id=\"text\" style=\"width:100%; height: 100%\"" 74 " ondrop=\"dropped();\">" 75 "</textarea>" 76 "</body></html>"; 77 78const char kHTMLForGuestWithSize[] = 79 "data:text/html," 80 "<html>" 81 "<body style=\"margin: 0px;\">" 82 "<img style=\"width: 100%; height: 400px;\"/>" 83 "</body>" 84 "</html>"; 85 86namespace content { 87 88// Test factory for creating test instances of BrowserPluginEmbedder and 89// BrowserPluginGuest. 90class TestBrowserPluginHostFactory : public BrowserPluginHostFactory { 91 public: 92 virtual BrowserPluginGuestManager* 93 CreateBrowserPluginGuestManager() OVERRIDE { 94 guest_manager_instance_count_++; 95 if (message_loop_runner_.get()) 96 message_loop_runner_->Quit(); 97 return new TestBrowserPluginGuestManager(); 98 } 99 100 virtual BrowserPluginGuest* CreateBrowserPluginGuest( 101 int instance_id, 102 WebContentsImpl* web_contents) OVERRIDE { 103 return new TestBrowserPluginGuest(instance_id, web_contents); 104 } 105 106 // Also keeps track of number of instances created. 107 virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder( 108 WebContentsImpl* web_contents) OVERRIDE { 109 110 return new TestBrowserPluginEmbedder(web_contents); 111 } 112 113 // Singleton getter. 114 static TestBrowserPluginHostFactory* GetInstance() { 115 return Singleton<TestBrowserPluginHostFactory>::get(); 116 } 117 118 // Waits for at least one embedder to be created in the test. Returns true if 119 // we have a guest, false if waiting times out. 120 void WaitForGuestManagerCreation() { 121 // Check if already have created an instance. 122 if (guest_manager_instance_count_ > 0) 123 return; 124 // Wait otherwise. 125 message_loop_runner_ = new MessageLoopRunner(); 126 message_loop_runner_->Run(); 127 } 128 129 protected: 130 TestBrowserPluginHostFactory() : guest_manager_instance_count_(0) {} 131 virtual ~TestBrowserPluginHostFactory() {} 132 133 private: 134 // For Singleton. 135 friend struct DefaultSingletonTraits<TestBrowserPluginHostFactory>; 136 137 scoped_refptr<MessageLoopRunner> message_loop_runner_; 138 int guest_manager_instance_count_; 139 140 DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginHostFactory); 141}; 142 143// Test factory class for browser plugin that creates guests with short hang 144// timeout. 145class TestShortHangTimeoutGuestFactory : public TestBrowserPluginHostFactory { 146 public: 147 virtual BrowserPluginGuest* CreateBrowserPluginGuest( 148 int instance_id, WebContentsImpl* web_contents) OVERRIDE { 149 TestBrowserPluginGuest* guest = 150 new TestBrowserPluginGuest(instance_id, web_contents); 151 guest->set_guest_hang_timeout(TestTimeouts::tiny_timeout()); 152 return guest; 153 } 154 155 // Singleton getter. 156 static TestShortHangTimeoutGuestFactory* GetInstance() { 157 return Singleton<TestShortHangTimeoutGuestFactory>::get(); 158 } 159 160 protected: 161 TestShortHangTimeoutGuestFactory() {} 162 virtual ~TestShortHangTimeoutGuestFactory() {} 163 164 private: 165 // For Singleton. 166 friend struct DefaultSingletonTraits<TestShortHangTimeoutGuestFactory>; 167 168 DISALLOW_COPY_AND_ASSIGN(TestShortHangTimeoutGuestFactory); 169}; 170 171// A transparent observer that can be used to verify that a RenderViewHost 172// received a specific message. 173class MessageObserver : public WebContentsObserver { 174 public: 175 MessageObserver(WebContents* web_contents, uint32 message_id) 176 : WebContentsObserver(web_contents), 177 message_id_(message_id), 178 message_received_(false) { 179 } 180 181 virtual ~MessageObserver() {} 182 183 void WaitUntilMessageReceived() { 184 if (message_received_) 185 return; 186 message_loop_runner_ = new MessageLoopRunner(); 187 message_loop_runner_->Run(); 188 } 189 190 void ResetState() { 191 message_received_ = false; 192 } 193 194 // IPC::Listener implementation. 195 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 196 if (message.type() == message_id_) { 197 message_received_ = true; 198 if (message_loop_runner_.get()) 199 message_loop_runner_->Quit(); 200 } 201 return false; 202 } 203 204 private: 205 scoped_refptr<MessageLoopRunner> message_loop_runner_; 206 uint32 message_id_; 207 bool message_received_; 208 209 DISALLOW_COPY_AND_ASSIGN(MessageObserver); 210}; 211 212class BrowserPluginHostTest : public ContentBrowserTest { 213 public: 214 BrowserPluginHostTest() 215 : test_embedder_(NULL), 216 test_guest_(NULL), 217 test_guest_manager_(NULL) {} 218 219 virtual void SetUp() OVERRIDE { 220 // Override factory to create tests instances of BrowserPlugin*. 221 content::BrowserPluginEmbedder::set_factory_for_testing( 222 TestBrowserPluginHostFactory::GetInstance()); 223 content::BrowserPluginGuest::set_factory_for_testing( 224 TestBrowserPluginHostFactory::GetInstance()); 225 content::BrowserPluginGuestManager::set_factory_for_testing( 226 TestBrowserPluginHostFactory::GetInstance()); 227 228 // On legacy windows, the AcceptDragEvents test needs this to pass. 229#if defined(OS_WIN) && !defined(USE_AURA) 230 UseRealGLBindings(); 231#endif 232 // We need real contexts, otherwise the embedder doesn't composite, but the 233 // guest does, and that isn't an expected configuration. 234 UseRealGLContexts(); 235 236 ContentBrowserTest::SetUp(); 237 } 238 virtual void TearDown() OVERRIDE { 239 content::BrowserPluginEmbedder::set_factory_for_testing(NULL); 240 content::BrowserPluginGuest::set_factory_for_testing(NULL); 241 242 ContentBrowserTest::TearDown(); 243 } 244 245 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 246 // Enable browser plugin in content_shell for running test. 247 command_line->AppendSwitch(switches::kEnableBrowserPluginForAllViewTypes); 248 } 249 250 static void SimulateSpaceKeyPress(WebContents* web_contents) { 251 SimulateKeyPress(web_contents, 252 ui::VKEY_SPACE, 253 false, // control. 254 false, // shift. 255 false, // alt. 256 false); // command. 257 } 258 259 static void SimulateTabKeyPress(WebContents* web_contents) { 260 SimulateKeyPress(web_contents, 261 ui::VKEY_TAB, 262 false, // control. 263 false, // shift. 264 false, // alt. 265 false); // command. 266 } 267 268 // Executes the javascript synchronously and makes sure the returned value is 269 // freed properly. 270 void ExecuteSyncJSFunction(RenderViewHost* rvh, const std::string& jscript) { 271 scoped_ptr<base::Value> value = 272 content::ExecuteScriptAndGetValue(rvh, jscript); 273 } 274 275 bool IsAttributeNull(RenderViewHost* rvh, const std::string& attribute) { 276 scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh, 277 "document.getElementById('plugin').getAttribute('" + attribute + "');"); 278 return value->GetType() == Value::TYPE_NULL; 279 } 280 281 // Removes all attributes in the comma-delimited string |attributes|. 282 void RemoveAttributes(RenderViewHost* rvh, const std::string& attributes) { 283 std::vector<std::string> attributes_list; 284 base::SplitString(attributes, ',', &attributes_list); 285 std::vector<std::string>::const_iterator itr; 286 for (itr = attributes_list.begin(); itr != attributes_list.end(); ++itr) { 287 ExecuteSyncJSFunction(rvh, "document.getElementById('plugin')" 288 "." + *itr + " = null;"); 289 } 290 } 291 292 // This helper method does the following: 293 // 1. Start the test server and navigate the shell to |embedder_url|. 294 // 2. Execute custom pre-navigation |embedder_code| if provided. 295 // 3. Navigate the guest to the |guest_url|. 296 // 4. Verify that the guest has been created and has completed loading. 297 void StartBrowserPluginTest(const std::string& embedder_url, 298 const std::string& guest_url, 299 bool is_guest_data_url, 300 const std::string& embedder_code) { 301 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 302 GURL test_url(embedded_test_server()->GetURL(embedder_url)); 303 NavigateToURL(shell(), test_url); 304 305 WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>( 306 shell()->web_contents()); 307 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 308 embedder_web_contents->GetRenderViewHost()); 309 // Focus the embedder. 310 rvh->Focus(); 311 // Activative IME. 312 rvh->SetInputMethodActive(true); 313 314 // Allow the test to do some operations on the embedder before we perform 315 // the first navigation of the guest. 316 if (!embedder_code.empty()) 317 ExecuteSyncJSFunction(rvh, embedder_code); 318 319 if (!is_guest_data_url) { 320 test_url = embedded_test_server()->GetURL(guest_url); 321 ExecuteSyncJSFunction( 322 rvh, base::StringPrintf("SetSrc('%s');", test_url.spec().c_str())); 323 } else { 324 ExecuteSyncJSFunction( 325 rvh, base::StringPrintf("SetSrc('%s');", guest_url.c_str())); 326 } 327 328 // Wait to make sure embedder is created/attached to WebContents. 329 TestBrowserPluginHostFactory::GetInstance()->WaitForGuestManagerCreation(); 330 331 test_embedder_ = static_cast<TestBrowserPluginEmbedder*>( 332 embedder_web_contents->GetBrowserPluginEmbedder()); 333 ASSERT_TRUE(test_embedder_); 334 335 test_guest_manager_ = static_cast<TestBrowserPluginGuestManager*>( 336 embedder_web_contents->GetBrowserPluginGuestManager()); 337 ASSERT_TRUE(test_guest_manager_); 338 339 test_guest_manager_->WaitForGuestAdded(); 340 341 // Verify that we have exactly one guest. 342 const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map = 343 test_guest_manager_->guest_web_contents_for_testing(); 344 EXPECT_EQ(1u, instance_map.size()); 345 346 WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>( 347 instance_map.begin()->second); 348 test_guest_ = static_cast<TestBrowserPluginGuest*>( 349 test_guest_web_contents->GetBrowserPluginGuest()); 350 test_guest_->WaitForLoadStop(); 351 } 352 353 TestBrowserPluginEmbedder* test_embedder() const { return test_embedder_; } 354 TestBrowserPluginGuest* test_guest() const { return test_guest_; } 355 TestBrowserPluginGuestManager* test_guest_manager() const { 356 return test_guest_manager_; 357 } 358 359 private: 360 TestBrowserPluginEmbedder* test_embedder_; 361 TestBrowserPluginGuest* test_guest_; 362 TestBrowserPluginGuestManager* test_guest_manager_; 363 DISALLOW_COPY_AND_ASSIGN(BrowserPluginHostTest); 364}; 365 366// This test ensures that if guest isn't there and we resize the guest (from 367// js), it remembers the size correctly. 368// 369// Initially we load an embedder with a guest without a src attribute (which has 370// dimension 640x480), resize it to 100x200, and then we set the source to a 371// sample guest. In the end we verify that the correct size has been set. 372IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, NavigateAfterResize) { 373 const gfx::Size nxt_size = gfx::Size(100, 200); 374 const std::string embedder_code = base::StringPrintf( 375 "SetSize(%d, %d);", nxt_size.width(), nxt_size.height()); 376 const char kEmbedderURL[] = "/browser_plugin_embedder.html"; 377 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, embedder_code); 378 379 // Wait for the guest to receive a damage buffer of size 100x200. 380 // This means the guest will be painted properly at that size. 381 test_guest()->WaitForDamageBufferWithSize(nxt_size); 382} 383 384IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AdvanceFocus) { 385 const char kEmbedderURL[] = "/browser_plugin_focus.html"; 386 const char* kGuestURL = "/browser_plugin_focus_child.html"; 387 StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string()); 388 389 SimulateMouseClick(test_embedder()->web_contents(), 0, 390 blink::WebMouseEvent::ButtonLeft); 391 BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents()); 392 // Wait until we focus into the guest. 393 test_guest()->WaitForFocus(); 394 395 // TODO(fsamuel): A third Tab key press should not be necessary. 396 // The browser plugin will take keyboard focus but it will not 397 // focus an initial element. The initial element is dependent 398 // upon tab direction which WebKit does not propagate to the plugin. 399 // See http://crbug.com/147644. 400 BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents()); 401 BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents()); 402 BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents()); 403 test_guest()->WaitForAdvanceFocus(); 404} 405 406// This test opens a page in http and then opens another page in https, forcing 407// a RenderViewHost swap in the web_contents. We verify that the embedder in the 408// web_contents gets cleared properly. 409IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderChangedAfterSwap) { 410 net::SpawnedTestServer https_server( 411 net::SpawnedTestServer::TYPE_HTTPS, 412 net::SpawnedTestServer::kLocalhost, 413 base::FilePath(FILE_PATH_LITERAL("content/test/data"))); 414 ASSERT_TRUE(https_server.Start()); 415 416 // 1. Load an embedder page with one guest in it. 417 const char kEmbedderURL[] = "/browser_plugin_embedder.html"; 418 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); 419 420 // 2. Navigate to a URL in https, so we trigger a RenderViewHost swap. 421 GURL test_https_url(https_server.GetURL( 422 "files/browser_plugin_title_change.html")); 423 content::WindowedNotificationObserver swap_observer( 424 content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, 425 content::Source<WebContents>(test_embedder()->web_contents())); 426 NavigateToURL(shell(), test_https_url); 427 swap_observer.Wait(); 428 429 TestBrowserPluginEmbedder* test_embedder_after_swap = 430 static_cast<TestBrowserPluginEmbedder*>( 431 static_cast<WebContentsImpl*>(shell()->web_contents())-> 432 GetBrowserPluginEmbedder()); 433 // Verify we have a no embedder in web_contents (since the new page doesn't 434 // have any browser plugin). 435 ASSERT_TRUE(!test_embedder_after_swap); 436 ASSERT_NE(test_embedder(), test_embedder_after_swap); 437} 438 439// This test opens two pages in http and there is no RenderViewHost swap, 440// therefore the embedder created on first page navigation stays the same in 441// web_contents. 442// Failing flakily on Windows: crbug.com/308405 443#if defined(OS_WIN) 444#define MAYBE_EmbedderSameAfterNav DISABLED_EmbedderSameAfterNav 445#else 446#define MAYBE_EmbedderSameAfterNav EmbedderSameAfterNav 447#endif 448IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_EmbedderSameAfterNav) { 449 const char kEmbedderURL[] = "/browser_plugin_embedder.html"; 450 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); 451 WebContentsImpl* embedder_web_contents = test_embedder()->web_contents(); 452 453 // Navigate to another page in same host and port, so RenderViewHost swap 454 // does not happen and existing embedder doesn't change in web_contents. 455 GURL test_url_new(embedded_test_server()->GetURL( 456 "/browser_plugin_title_change.html")); 457 const base::string16 expected_title = ASCIIToUTF16("done"); 458 content::TitleWatcher title_watcher(shell()->web_contents(), expected_title); 459 NavigateToURL(shell(), test_url_new); 460 VLOG(0) << "Start waiting for title"; 461 base::string16 actual_title = title_watcher.WaitAndGetTitle(); 462 EXPECT_EQ(expected_title, actual_title); 463 VLOG(0) << "Done navigating to second page"; 464 465 TestBrowserPluginEmbedder* test_embedder_after_nav = 466 static_cast<TestBrowserPluginEmbedder*>( 467 embedder_web_contents->GetBrowserPluginEmbedder()); 468 // Embedder must not change in web_contents. 469 ASSERT_EQ(test_embedder_after_nav, test_embedder()); 470} 471 472// This test verifies that hiding the embedder also hides the guest. 473IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, BrowserPluginVisibilityChanged) { 474 const char kEmbedderURL[] = "/browser_plugin_embedder.html"; 475 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); 476 477 // Hide the Browser Plugin. 478 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 479 test_embedder()->web_contents()->GetRenderViewHost()); 480 ExecuteSyncJSFunction( 481 rvh, "document.getElementById('plugin').style.visibility = 'hidden'"); 482 483 // Make sure that the guest is hidden. 484 test_guest()->WaitUntilHidden(); 485} 486 487IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderVisibilityChanged) { 488 const char kEmbedderURL[] = "/browser_plugin_embedder.html"; 489 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); 490 491 // Hide the embedder. 492 test_embedder()->web_contents()->WasHidden(); 493 494 // Make sure that hiding the embedder also hides the guest. 495 test_guest()->WaitUntilHidden(); 496} 497 498// Verifies that installing/uninstalling touch-event handlers in the guest 499// plugin correctly updates the touch-event handling state in the embedder. 500IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AcceptTouchEvents) { 501 const char kEmbedderURL[] = "/browser_plugin_embedder.html"; 502 StartBrowserPluginTest( 503 kEmbedderURL, kHTMLForGuestTouchHandler, true, std::string()); 504 505 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 506 test_embedder()->web_contents()->GetRenderViewHost()); 507 // The embedder should not have any touch event handlers at this point. 508 EXPECT_FALSE(rvh->has_touch_handler()); 509 510 // Install the touch handler in the guest. This should cause the embedder to 511 // start listening for touch events too. 512 MessageObserver observer(test_embedder()->web_contents(), 513 ViewHostMsg_HasTouchEventHandlers::ID); 514 ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(), 515 "InstallTouchHandler();"); 516 observer.WaitUntilMessageReceived(); 517 EXPECT_TRUE(rvh->has_touch_handler()); 518 519 // Uninstalling the touch-handler in guest should cause the embedder to stop 520 // listening for touch events. 521 observer.ResetState(); 522 ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(), 523 "UninstallTouchHandler();"); 524 observer.WaitUntilMessageReceived(); 525 EXPECT_FALSE(rvh->has_touch_handler()); 526} 527 528// This tests verifies that reloading the embedder does not crash the browser 529// and that the guest is reset. 530IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, ReloadEmbedder) { 531 const char kEmbedderURL[] = "/browser_plugin_embedder.html"; 532 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); 533 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 534 test_embedder()->web_contents()->GetRenderViewHost()); 535 536 // Change the title of the page to 'modified' so that we know that 537 // the page has successfully reloaded when it goes back to 'embedder' 538 // in the next step. 539 { 540 const base::string16 expected_title = ASCIIToUTF16("modified"); 541 content::TitleWatcher title_watcher(test_embedder()->web_contents(), 542 expected_title); 543 544 ExecuteSyncJSFunction(rvh, 545 base::StringPrintf("SetTitle('%s');", "modified")); 546 547 base::string16 actual_title = title_watcher.WaitAndGetTitle(); 548 EXPECT_EQ(expected_title, actual_title); 549 } 550 551 // Reload the embedder page, and verify that the reload was successful. 552 // Then navigate the guest to verify that the browser process does not crash. 553 { 554 const base::string16 expected_title = ASCIIToUTF16("embedder"); 555 content::TitleWatcher title_watcher(test_embedder()->web_contents(), 556 expected_title); 557 558 test_embedder()->web_contents()->GetController().Reload(false); 559 base::string16 actual_title = title_watcher.WaitAndGetTitle(); 560 EXPECT_EQ(expected_title, actual_title); 561 562 ExecuteSyncJSFunction( 563 test_embedder()->web_contents()->GetRenderViewHost(), 564 base::StringPrintf("SetSrc('%s');", kHTMLForGuest)); 565 test_guest_manager()->WaitForGuestAdded(); 566 567 const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map = 568 test_guest_manager()->guest_web_contents_for_testing(); 569 WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>( 570 instance_map.begin()->second); 571 TestBrowserPluginGuest* new_test_guest = 572 static_cast<TestBrowserPluginGuest*>( 573 test_guest_web_contents->GetBrowserPluginGuest()); 574 ASSERT_TRUE(new_test_guest != NULL); 575 576 // Wait for the guest to send an UpdateRectMsg, meaning it is ready. 577 new_test_guest->WaitForUpdateRectMsg(); 578 } 579} 580 581// Always failing in the win7_aura try bot. See http://crbug.com/181107. 582// Times out on the Mac. See http://crbug.com/297576. 583#if (defined(OS_WIN) && defined(USE_AURA)) || defined(OS_MACOSX) 584#define MAYBE_AcceptDragEvents DISABLED_AcceptDragEvents 585#else 586#define MAYBE_AcceptDragEvents AcceptDragEvents 587#endif 588 589// Tests that a drag-n-drop over the browser plugin in the embedder happens 590// correctly. 591IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_AcceptDragEvents) { 592 const char kEmbedderURL[] = "/browser_plugin_dragging.html"; 593 StartBrowserPluginTest( 594 kEmbedderURL, kHTMLForGuestAcceptDrag, true, std::string()); 595 596 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 597 test_embedder()->web_contents()->GetRenderViewHost()); 598 599 // Get a location in the embedder outside of the plugin. 600 base::ListValue *start, *end; 601 scoped_ptr<base::Value> value = 602 content::ExecuteScriptAndGetValue(rvh, "dragLocation()"); 603 ASSERT_TRUE(value->GetAsList(&start) && start->GetSize() == 2); 604 double start_x, start_y; 605 ASSERT_TRUE(start->GetDouble(0, &start_x) && start->GetDouble(1, &start_y)); 606 607 // Get a location in the embedder that falls inside the plugin. 608 value = content::ExecuteScriptAndGetValue(rvh, "dropLocation()"); 609 ASSERT_TRUE(value->GetAsList(&end) && end->GetSize() == 2); 610 double end_x, end_y; 611 ASSERT_TRUE(end->GetDouble(0, &end_x) && end->GetDouble(1, &end_y)); 612 613 DropData drop_data; 614 GURL url = GURL("https://www.domain.com/index.html"); 615 drop_data.url = url; 616 617 // Pretend that the URL is being dragged over the embedder. Start the drag 618 // from outside the plugin, then move the drag inside the plugin and drop. 619 // This should trigger appropriate messages from the embedder to the guest, 620 // and end with a drop on the guest. The guest changes title when a drop 621 // happens. 622 const base::string16 expected_title = ASCIIToUTF16("DROPPED"); 623 content::TitleWatcher title_watcher(test_guest()->web_contents(), 624 expected_title); 625 626 rvh->DragTargetDragEnter(drop_data, gfx::Point(start_x, start_y), 627 gfx::Point(start_x, start_y), blink::WebDragOperationEvery, 0); 628 rvh->DragTargetDragOver(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y), 629 blink::WebDragOperationEvery, 0); 630 rvh->DragTargetDrop(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y), 0); 631 632 base::string16 actual_title = title_watcher.WaitAndGetTitle(); 633 EXPECT_EQ(expected_title, actual_title); 634} 635 636// This test verifies that round trip postMessage works as expected. 637// 1. The embedder posts a message 'testing123' to the guest. 638// 2. The guest receives and replies to the message using the event object's 639// source object: event.source.postMessage('foobar', '*') 640// 3. The embedder receives the message and uses the event's source 641// object to do one final reply: 'stop' 642// 4. The guest receives the final 'stop' message. 643// 5. The guest acks the 'stop' message with a 'stop_ack' message. 644// 6. The embedder changes its title to 'main guest' when it sees the 'stop_ack' 645// message. 646IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, PostMessage) { 647 const char* kTesting = "testing123"; 648 const char* kEmbedderURL = "/browser_plugin_embedder.html"; 649 const char* kGuestURL = "/browser_plugin_post_message_guest.html"; 650 StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string()); 651 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 652 test_embedder()->web_contents()->GetRenderViewHost()); 653 { 654 const base::string16 expected_title = ASCIIToUTF16("main guest"); 655 content::TitleWatcher title_watcher(test_embedder()->web_contents(), 656 expected_title); 657 658 // By the time we get here 'contentWindow' should be ready because the 659 // guest has completed loading. 660 ExecuteSyncJSFunction( 661 rvh, base::StringPrintf("PostMessage('%s, false');", kTesting)); 662 663 // The title will be updated to "main guest" at the last stage of the 664 // process described above. 665 base::string16 actual_title = title_watcher.WaitAndGetTitle(); 666 EXPECT_EQ(expected_title, actual_title); 667 } 668} 669 670// This is the same as BrowserPluginHostTest.PostMessage but also 671// posts a message to an iframe. 672// TODO(fsamuel): This test should replace the previous test once postMessage 673// iframe targeting is fixed (see http://crbug.com/153701). 674IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DISABLED_PostMessageToIFrame) { 675 const char* kTesting = "testing123"; 676 const char* kEmbedderURL = "/browser_plugin_embedder.html"; 677 const char* kGuestURL = "/browser_plugin_post_message_guest.html"; 678 StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string()); 679 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 680 test_embedder()->web_contents()->GetRenderViewHost()); 681 { 682 const base::string16 expected_title = ASCIIToUTF16("main guest"); 683 content::TitleWatcher title_watcher(test_embedder()->web_contents(), 684 expected_title); 685 686 ExecuteSyncJSFunction( 687 rvh, base::StringPrintf("PostMessage('%s, false');", kTesting)); 688 689 // The title will be updated to "main guest" at the last stage of the 690 // process described above. 691 base::string16 actual_title = title_watcher.WaitAndGetTitle(); 692 EXPECT_EQ(expected_title, actual_title); 693 } 694 { 695 content::TitleWatcher ready_watcher(test_embedder()->web_contents(), 696 ASCIIToUTF16("ready")); 697 698 RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( 699 test_guest()->web_contents()->GetRenderViewHost()); 700 GURL test_url = embedded_test_server()->GetURL( 701 "/browser_plugin_post_message_guest.html"); 702 ExecuteSyncJSFunction( 703 guest_rvh, 704 base::StringPrintf( 705 "CreateChildFrame('%s');", test_url.spec().c_str())); 706 707 base::string16 actual_title = ready_watcher.WaitAndGetTitle(); 708 EXPECT_EQ(ASCIIToUTF16("ready"), actual_title); 709 710 content::TitleWatcher iframe_watcher(test_embedder()->web_contents(), 711 ASCIIToUTF16("iframe")); 712 ExecuteSyncJSFunction( 713 rvh, base::StringPrintf("PostMessage('%s', true);", kTesting)); 714 715 // The title will be updated to "iframe" at the last stage of the 716 // process described above. 717 actual_title = iframe_watcher.WaitAndGetTitle(); 718 EXPECT_EQ(ASCIIToUTF16("iframe"), actual_title); 719 } 720} 721 722// This test verifies that if a browser plugin is hidden before navigation, 723// the guest starts off hidden. 724IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, HiddenBeforeNavigation) { 725 const char* kEmbedderURL = "/browser_plugin_embedder.html"; 726 const std::string embedder_code = 727 "document.getElementById('plugin').style.visibility = 'hidden'"; 728 StartBrowserPluginTest( 729 kEmbedderURL, kHTMLForGuest, true, embedder_code); 730 EXPECT_FALSE(test_guest()->visible()); 731} 732 733// This test verifies that if a browser plugin is focused before navigation then 734// the guest starts off focused. 735IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusBeforeNavigation) { 736 const char* kEmbedderURL = "/browser_plugin_embedder.html"; 737 const std::string embedder_code = 738 "document.getElementById('plugin').focus();"; 739 StartBrowserPluginTest( 740 kEmbedderURL, kHTMLForGuest, true, embedder_code); 741 RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( 742 test_guest()->web_contents()->GetRenderViewHost()); 743 // Verify that the guest is focused. 744 scoped_ptr<base::Value> value = 745 content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()"); 746 bool result = false; 747 ASSERT_TRUE(value->GetAsBoolean(&result)); 748 EXPECT_TRUE(result); 749} 750 751IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusTracksEmbedder) { 752 const char* kEmbedderURL = "/browser_plugin_embedder.html"; 753 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); 754 // Blur the embedder. 755 test_embedder()->web_contents()->GetRenderViewHost()->Blur(); 756 // Ensure that the guest is also blurred. 757 test_guest()->WaitForBlur(); 758} 759 760// Test for regression http://crbug.com/162961. 761IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, GetRenderViewHostAtPositionTest) { 762 const char kEmbedderURL[] = "/browser_plugin_embedder.html"; 763 const std::string embedder_code = 764 base::StringPrintf("SetSize(%d, %d);", 100, 100); 765 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuestWithSize, true, 766 embedder_code); 767 // Check for render view host at position (150, 150) that is outside the 768 // bounds of our guest, so this would respond with the render view host of the 769 // embedder. 770 test_embedder()->WaitForRenderViewHostAtPosition(150, 150); 771 ASSERT_EQ(test_embedder()->web_contents()->GetRenderViewHost(), 772 test_embedder()->last_rvh_at_position_response()); 773} 774 775// This test verifies that if IME is enabled in the embedder, it is also enabled 776// in the guest. 777IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, VerifyInputMethodActive) { 778 const char* kEmbedderURL = "/browser_plugin_embedder.html"; 779 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); 780 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 781 test_guest()->web_contents()->GetRenderViewHost()); 782 EXPECT_TRUE(rvh->input_method_active()); 783} 784 785// Verify that navigating to an invalid URL (e.g. 'http:') doesn't cause 786// a crash. 787IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DoNotCrashOnInvalidNavigation) { 788 const char kEmbedderURL[] = "/browser_plugin_embedder.html"; 789 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); 790 TestBrowserPluginGuestDelegate* delegate = 791 new TestBrowserPluginGuestDelegate(); 792 test_guest()->SetDelegate(delegate); 793 794 const char kValidSchemeWithEmptyURL[] = "http:"; 795 ExecuteSyncJSFunction( 796 test_embedder()->web_contents()->GetRenderViewHost(), 797 base::StringPrintf("SetSrc('%s');", kValidSchemeWithEmptyURL)); 798 EXPECT_TRUE(delegate->load_aborted()); 799 EXPECT_FALSE(delegate->load_aborted_url().is_valid()); 800 EXPECT_EQ(kValidSchemeWithEmptyURL, 801 delegate->load_aborted_url().possibly_invalid_spec()); 802 803 delegate->ResetStates(); 804 805 // Attempt a navigation to chrome-guest://abc123, which is a valid URL. But it 806 // should be blocked because the scheme isn't web-safe or a pseudo-scheme. 807 ExecuteSyncJSFunction( 808 test_embedder()->web_contents()->GetRenderViewHost(), 809 base::StringPrintf("SetSrc('%s://abc123');", kGuestScheme)); 810 EXPECT_TRUE(delegate->load_aborted()); 811 EXPECT_TRUE(delegate->load_aborted_url().is_valid()); 812} 813 814// Tests involving the threaded compositor. 815class BrowserPluginThreadedCompositorTest : public BrowserPluginHostTest { 816 public: 817 BrowserPluginThreadedCompositorTest() {} 818 virtual ~BrowserPluginThreadedCompositorTest() {} 819 820 protected: 821 virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE { 822 BrowserPluginHostTest::SetUpCommandLine(cmd); 823 cmd->AppendSwitch(switches::kEnableThreadedCompositing); 824 825 // http://crbug.com/327035 826 cmd->AppendSwitch(switches::kDisableDelegatedRenderer); 827 } 828}; 829 830static void CompareSkBitmaps(const SkBitmap& expected_bitmap, 831 const SkBitmap& bitmap) { 832 EXPECT_EQ(expected_bitmap.width(), bitmap.width()); 833 if (expected_bitmap.width() != bitmap.width()) 834 return; 835 EXPECT_EQ(expected_bitmap.height(), bitmap.height()); 836 if (expected_bitmap.height() != bitmap.height()) 837 return; 838 EXPECT_EQ(expected_bitmap.config(), bitmap.config()); 839 if (expected_bitmap.config() != bitmap.config()) 840 return; 841 842 SkAutoLockPixels expected_bitmap_lock(expected_bitmap); 843 SkAutoLockPixels bitmap_lock(bitmap); 844 int fails = 0; 845 const int kAllowableError = 2; 846 for (int i = 0; i < bitmap.width() && fails < 10; ++i) { 847 for (int j = 0; j < bitmap.height() && fails < 10; ++j) { 848 SkColor expected_color = expected_bitmap.getColor(i, j); 849 SkColor color = bitmap.getColor(i, j); 850 int expected_alpha = SkColorGetA(expected_color); 851 int alpha = SkColorGetA(color); 852 int expected_red = SkColorGetR(expected_color); 853 int red = SkColorGetR(color); 854 int expected_green = SkColorGetG(expected_color); 855 int green = SkColorGetG(color); 856 int expected_blue = SkColorGetB(expected_color); 857 int blue = SkColorGetB(color); 858 EXPECT_NEAR(expected_alpha, alpha, kAllowableError) 859 << "expected_color: " << std::hex << expected_color 860 << " color: " << color 861 << " Failed at " << std::dec << i << ", " << j 862 << " Failure " << ++fails; 863 EXPECT_NEAR(expected_red, red, kAllowableError) 864 << "expected_color: " << std::hex << expected_color 865 << " color: " << color 866 << " Failed at " << std::dec << i << ", " << j 867 << " Failure " << ++fails; 868 EXPECT_NEAR(expected_green, green, kAllowableError) 869 << "expected_color: " << std::hex << expected_color 870 << " color: " << color 871 << " Failed at " << std::dec << i << ", " << j 872 << " Failure " << ++fails; 873 EXPECT_NEAR(expected_blue, blue, kAllowableError) 874 << "expected_color: " << std::hex << expected_color 875 << " color: " << color 876 << " Failed at " << std::dec << i << ", " << j 877 << " Failure " << ++fails; 878 } 879 } 880 EXPECT_LT(fails, 10); 881} 882 883static void CompareSkBitmapAndRun(const base::Closure& callback, 884 const SkBitmap& expected_bitmap, 885 bool *result, 886 bool succeed, 887 const SkBitmap& bitmap) { 888 *result = succeed; 889 if (succeed) 890 CompareSkBitmaps(expected_bitmap, bitmap); 891 callback.Run(); 892} 893 894// http://crbug.com/171744 895#if defined(OS_MACOSX) 896#define MAYBE_GetBackingStore DISABLED_GetBackingStore 897#else 898#define MAYBE_GetBackingStore GetBackingStore 899#endif 900IN_PROC_BROWSER_TEST_F(BrowserPluginThreadedCompositorTest, 901 MAYBE_GetBackingStore) { 902 const char kEmbedderURL[] = "/browser_plugin_embedder.html"; 903 const char kHTMLForGuest[] = 904 "data:text/html,<html><style>body { background-color: red; }</style>" 905 "<body></body></html>"; 906 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, 907 std::string("SetSize(50, 60);")); 908 909 WebContentsImpl* guest_contents = test_guest()->web_contents(); 910 RenderWidgetHostImpl* guest_widget_host = 911 RenderWidgetHostImpl::From(guest_contents->GetRenderViewHost()); 912 913 SkBitmap expected_bitmap; 914 expected_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 50, 60); 915 expected_bitmap.allocPixels(); 916 expected_bitmap.eraseARGB(255, 255, 0, 0); // #f00 917 bool result = false; 918 while (!result) { 919 base::RunLoop loop; 920 guest_widget_host->CopyFromBackingStore(gfx::Rect(), 921 guest_widget_host->GetView()->GetViewBounds().size(), 922 base::Bind(&CompareSkBitmapAndRun, loop.QuitClosure(), expected_bitmap, 923 &result)); 924 loop.Run(); 925 } 926} 927 928// Tests input method. 929IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, InputMethod) { 930 const char kEmbedderURL[] = "/browser_plugin_embedder.html"; 931 const char kGuestHTML[] = "data:text/html," 932 "<html><body><input id=\"input1\">" 933 "<input id=\"input2\"></body>" 934 "<script>" 935 "var i = document.getElementById(\"input1\");" 936 "i.oninput = function() {" 937 " document.title = i.value;" 938 "}" 939 "</script>" 940 "</html>"; 941 StartBrowserPluginTest(kEmbedderURL, kGuestHTML, true, 942 "document.getElementById(\"plugin\").focus();"); 943 944 RenderViewHostImpl* embedder_rvh = static_cast<RenderViewHostImpl*>( 945 test_embedder()->web_contents()->GetRenderViewHost()); 946 RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( 947 test_guest()->web_contents()->GetRenderViewHost()); 948 949 std::vector<blink::WebCompositionUnderline> underlines; 950 951 // An input field in browser plugin guest gets focus and given some user 952 // input via IME. 953 { 954 ExecuteSyncJSFunction(guest_rvh, 955 "document.getElementById('input1').focus();"); 956 string16 expected_title = UTF8ToUTF16("InputTest123"); 957 content::TitleWatcher title_watcher(test_guest()->web_contents(), 958 expected_title); 959 embedder_rvh->Send( 960 new ViewMsg_ImeSetComposition( 961 test_embedder()->web_contents()->GetRoutingID(), 962 expected_title, 963 underlines, 964 12, 12)); 965 base::string16 actual_title = title_watcher.WaitAndGetTitle(); 966 EXPECT_EQ(expected_title, actual_title); 967 } 968 // A composition is committed via IME. 969 { 970 string16 expected_title = UTF8ToUTF16("InputTest456"); 971 content::TitleWatcher title_watcher(test_guest()->web_contents(), 972 expected_title); 973 embedder_rvh->Send( 974 new ViewMsg_ImeConfirmComposition( 975 test_embedder()->web_contents()->GetRoutingID(), 976 expected_title, 977 gfx::Range(), 978 true)); 979 base::string16 actual_title = title_watcher.WaitAndGetTitle(); 980 EXPECT_EQ(expected_title, actual_title); 981 } 982 // IME composition starts, but focus moves out, then the composition will 983 // be committed and get cancel msg. 984 { 985 ExecuteSyncJSFunction(guest_rvh, 986 "document.getElementById('input1').value = '';"); 987 string16 composition = UTF8ToUTF16("InputTest789"); 988 content::TitleWatcher title_watcher(test_guest()->web_contents(), 989 composition); 990 embedder_rvh->Send( 991 new ViewMsg_ImeSetComposition( 992 test_embedder()->web_contents()->GetRoutingID(), 993 composition, 994 underlines, 995 12, 12)); 996 base::string16 actual_title = title_watcher.WaitAndGetTitle(); 997 EXPECT_EQ(composition, actual_title); 998 // Moving focus causes IME cancel, and the composition will be committed 999 // in input1, not in input2. 1000 ExecuteSyncJSFunction(guest_rvh, 1001 "document.getElementById('input2').focus();"); 1002 test_guest()->WaitForImeCancel(); 1003 scoped_ptr<base::Value> value = 1004 content::ExecuteScriptAndGetValue( 1005 guest_rvh, "document.getElementById('input1').value"); 1006 std::string result; 1007 ASSERT_TRUE(value->GetAsString(&result)); 1008 EXPECT_EQ(UTF16ToUTF8(composition), result); 1009 } 1010 // Tests ExtendSelectionAndDelete message works in browser_plugin. 1011 { 1012 // Set 'InputTestABC' in input1 and put caret at 6 (after 'T'). 1013 ExecuteSyncJSFunction(guest_rvh, 1014 "var i = document.getElementById('input1');" 1015 "i.focus();" 1016 "i.value = 'InputTestABC';" 1017 "i.selectionStart=6;" 1018 "i.selectionEnd=6;"); 1019 string16 expected_value = UTF8ToUTF16("InputABC"); 1020 content::TitleWatcher title_watcher(test_guest()->web_contents(), 1021 expected_value); 1022 // Delete 'Test' in 'InputTestABC', as the caret is after 'T': 1023 // delete before 1 character ('T') and after 3 characters ('est'). 1024 embedder_rvh->Send( 1025 new ViewMsg_ExtendSelectionAndDelete( 1026 test_embedder()->web_contents()->GetRoutingID(), 1027 1, 3)); 1028 base::string16 actual_title = title_watcher.WaitAndGetTitle(); 1029 EXPECT_EQ(expected_value, actual_title); 1030 scoped_ptr<base::Value> value = 1031 content::ExecuteScriptAndGetValue( 1032 guest_rvh, "document.getElementById('input1').value"); 1033 std::string actual_value; 1034 ASSERT_TRUE(value->GetAsString(&actual_value)); 1035 EXPECT_EQ(UTF16ToUTF8(expected_value), actual_value); 1036 } 1037} 1038 1039} // namespace content 1040