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