// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/command_line.h" #include "base/memory/singleton.h" #include "base/run_loop.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/test/test_timeouts.h" #include "content/browser/browser_plugin/browser_plugin_guest.h" #include "content/browser/browser_plugin/browser_plugin_host_factory.h" #include "content/browser/browser_plugin/test_browser_plugin_embedder.h" #include "content/browser/browser_plugin/test_browser_plugin_guest.h" #include "content/browser/browser_plugin/test_browser_plugin_guest_manager.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/common/view_messages.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/render_view_host_observer.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" #include "content/shell/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/base/net_util.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_response.h" #include "net/test/spawned_test_server/spawned_test_server.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #include "webkit/common/webdropdata.h" using WebKit::WebInputEvent; using WebKit::WebMouseEvent; using content::BrowserPluginEmbedder; using content::BrowserPluginGuest; using content::BrowserPluginHostFactory; using content::WebContentsImpl; namespace { const char kHTMLForGuest[] = "data:text/html,<html><body>hello world</body></html>"; const char kHTMLForGuestBusyLoop[] = "data:text/html,<html><head><script type=\"text/javascript\">" "function PauseMs(timems) {" " document.title = \"start\";" " var date = new Date();" " var currDate = null;" " do {" " currDate = new Date();" " } while (currDate - date < timems)" "}" "function StartPauseMs(timems) {" " setTimeout(function() { PauseMs(timems); }, 0);" "}" "</script></head><body></body></html>"; const char kHTMLForGuestTouchHandler[] = "data:text/html,<html><body><div id=\"touch\">With touch</div></body>" "<script type=\"text/javascript\">" "function handler() {}" "function InstallTouchHandler() { " " document.getElementById(\"touch\").addEventListener(\"touchstart\", " " handler);" "}" "function UninstallTouchHandler() { " " document.getElementById(\"touch\").removeEventListener(\"touchstart\", " " handler);" "}" "</script></html>"; const char kHTMLForGuestWithTitle[] = "data:text/html," "<html><head><title>%s</title></head>" "<body>hello world</body>" "</html>"; const char kHTMLForGuestAcceptDrag[] = "data:text/html,<html><body>" "<script>" "function dropped() {" " document.title = \"DROPPED\";" "}" "</script>" "<textarea id=\"text\" style=\"width:100%; height: 100%\"" " ondrop=\"dropped();\">" "</textarea>" "</body></html>"; const char kHTMLForGuestWithSize[] = "data:text/html," "<html>" "<body style=\"margin: 0px;\">" "<img style=\"width: 100%; height: 400px;\"/>" "</body>" "</html>"; std::string GetHTMLForGuestWithTitle(const std::string& title) { return base::StringPrintf(kHTMLForGuestWithTitle, title.c_str()); } } // namespace namespace content { // Test factory for creating test instances of BrowserPluginEmbedder and // BrowserPluginGuest. class TestBrowserPluginHostFactory : public BrowserPluginHostFactory { public: virtual BrowserPluginGuestManager* CreateBrowserPluginGuestManager() OVERRIDE { guest_manager_instance_count_++; if (message_loop_runner_.get()) message_loop_runner_->Quit(); return new TestBrowserPluginGuestManager(); } virtual BrowserPluginGuest* CreateBrowserPluginGuest( int instance_id, WebContentsImpl* web_contents) OVERRIDE { return new TestBrowserPluginGuest(instance_id, web_contents); } // Also keeps track of number of instances created. virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder( WebContentsImpl* web_contents) OVERRIDE { return new TestBrowserPluginEmbedder(web_contents); } // Singleton getter. static TestBrowserPluginHostFactory* GetInstance() { return Singleton<TestBrowserPluginHostFactory>::get(); } // Waits for at least one embedder to be created in the test. Returns true if // we have a guest, false if waiting times out. void WaitForGuestManagerCreation() { // Check if already have created an instance. if (guest_manager_instance_count_ > 0) return; // Wait otherwise. message_loop_runner_ = new MessageLoopRunner(); message_loop_runner_->Run(); } protected: TestBrowserPluginHostFactory() : guest_manager_instance_count_(0) {} virtual ~TestBrowserPluginHostFactory() {} private: // For Singleton. friend struct DefaultSingletonTraits<TestBrowserPluginHostFactory>; scoped_refptr<MessageLoopRunner> message_loop_runner_; int guest_manager_instance_count_; DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginHostFactory); }; // Test factory class for browser plugin that creates guests with short hang // timeout. class TestShortHangTimeoutGuestFactory : public TestBrowserPluginHostFactory { public: virtual BrowserPluginGuest* CreateBrowserPluginGuest( int instance_id, WebContentsImpl* web_contents) OVERRIDE { BrowserPluginGuest* guest = new TestBrowserPluginGuest(instance_id, web_contents); guest->set_guest_hang_timeout_for_testing(TestTimeouts::tiny_timeout()); return guest; } // Singleton getter. static TestShortHangTimeoutGuestFactory* GetInstance() { return Singleton<TestShortHangTimeoutGuestFactory>::get(); } protected: TestShortHangTimeoutGuestFactory() {} virtual ~TestShortHangTimeoutGuestFactory() {} private: // For Singleton. friend struct DefaultSingletonTraits<TestShortHangTimeoutGuestFactory>; DISALLOW_COPY_AND_ASSIGN(TestShortHangTimeoutGuestFactory); }; // A transparent observer that can be used to verify that a RenderViewHost // received a specific message. class RenderViewHostMessageObserver : public RenderViewHostObserver { public: RenderViewHostMessageObserver(RenderViewHost* host, uint32 message_id) : RenderViewHostObserver(host), message_id_(message_id), message_received_(false) { } virtual ~RenderViewHostMessageObserver() {} void WaitUntilMessageReceived() { if (message_received_) return; message_loop_runner_ = new MessageLoopRunner(); message_loop_runner_->Run(); } void ResetState() { message_received_ = false; } // IPC::Listener implementation. virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { if (message.type() == message_id_) { message_received_ = true; if (message_loop_runner_.get()) message_loop_runner_->Quit(); } return false; } private: scoped_refptr<MessageLoopRunner> message_loop_runner_; uint32 message_id_; bool message_received_; DISALLOW_COPY_AND_ASSIGN(RenderViewHostMessageObserver); }; class BrowserPluginHostTest : public ContentBrowserTest { public: BrowserPluginHostTest() : test_embedder_(NULL), test_guest_(NULL), test_guest_manager_(NULL) {} virtual void SetUp() OVERRIDE { // Override factory to create tests instances of BrowserPlugin*. content::BrowserPluginEmbedder::set_factory_for_testing( TestBrowserPluginHostFactory::GetInstance()); content::BrowserPluginGuest::set_factory_for_testing( TestBrowserPluginHostFactory::GetInstance()); content::BrowserPluginGuestManager::set_factory_for_testing( TestBrowserPluginHostFactory::GetInstance()); ContentBrowserTest::SetUp(); } virtual void TearDown() OVERRIDE { content::BrowserPluginEmbedder::set_factory_for_testing(NULL); content::BrowserPluginGuest::set_factory_for_testing(NULL); ContentBrowserTest::TearDown(); } virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { // Enable browser plugin in content_shell for running test. command_line->AppendSwitch(switches::kEnableBrowserPluginForAllViewTypes); } static void SimulateSpaceKeyPress(WebContents* web_contents) { SimulateKeyPress(web_contents, ui::VKEY_SPACE, false, // control. false, // shift. false, // alt. false); // command. } static void SimulateTabKeyPress(WebContents* web_contents) { SimulateKeyPress(web_contents, ui::VKEY_TAB, false, // control. false, // shift. false, // alt. false); // command. } // Executes the javascript synchronously and makes sure the returned value is // freed properly. void ExecuteSyncJSFunction(RenderViewHost* rvh, const std::string& jscript) { scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh, jscript); } bool IsAttributeNull(RenderViewHost* rvh, const std::string& attribute) { scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh, "document.getElementById('plugin').getAttribute('" + attribute + "');"); return value->GetType() == Value::TYPE_NULL; } // Removes all attributes in the comma-delimited string |attributes|. void RemoveAttributes(RenderViewHost* rvh, const std::string& attributes) { std::vector<std::string> attributes_list; base::SplitString(attributes, ',', &attributes_list); std::vector<std::string>::const_iterator itr; for (itr = attributes_list.begin(); itr != attributes_list.end(); ++itr) { ExecuteSyncJSFunction(rvh, "document.getElementById('plugin')" "." + *itr + " = null;"); } } // This helper method does the following: // 1. Start the test server and navigate the shell to |embedder_url|. // 2. Execute custom pre-navigation |embedder_code| if provided. // 3. Navigate the guest to the |guest_url|. // 4. Verify that the guest has been created and has completed loading. void StartBrowserPluginTest(const std::string& embedder_url, const std::string& guest_url, bool is_guest_data_url, const std::string& embedder_code) { ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); GURL test_url(embedded_test_server()->GetURL(embedder_url)); NavigateToURL(shell(), test_url); WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>( shell()->web_contents()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( embedder_web_contents->GetRenderViewHost()); // Focus the embedder. rvh->Focus(); // Allow the test to do some operations on the embedder before we perform // the first navigation of the guest. if (!embedder_code.empty()) ExecuteSyncJSFunction(rvh, embedder_code); if (!is_guest_data_url) { test_url = embedded_test_server()->GetURL(guest_url); ExecuteSyncJSFunction( rvh, base::StringPrintf("SetSrc('%s');", test_url.spec().c_str())); } else { ExecuteSyncJSFunction( rvh, base::StringPrintf("SetSrc('%s');", guest_url.c_str())); } // Wait to make sure embedder is created/attached to WebContents. TestBrowserPluginHostFactory::GetInstance()->WaitForGuestManagerCreation(); test_embedder_ = static_cast<TestBrowserPluginEmbedder*>( embedder_web_contents->GetBrowserPluginEmbedder()); ASSERT_TRUE(test_embedder_); test_guest_manager_ = static_cast<TestBrowserPluginGuestManager*>( embedder_web_contents->GetBrowserPluginGuestManager()); ASSERT_TRUE(test_guest_manager_); test_guest_manager_->WaitForGuestAdded(); // Verify that we have exactly one guest. const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map = test_guest_manager_->guest_web_contents_for_testing(); EXPECT_EQ(1u, instance_map.size()); WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>( instance_map.begin()->second); test_guest_ = static_cast<TestBrowserPluginGuest*>( test_guest_web_contents->GetBrowserPluginGuest()); test_guest_->WaitForLoadStop(); } TestBrowserPluginEmbedder* test_embedder() const { return test_embedder_; } TestBrowserPluginGuest* test_guest() const { return test_guest_; } TestBrowserPluginGuestManager* test_guest_manager() const { return test_guest_manager_; } private: TestBrowserPluginEmbedder* test_embedder_; TestBrowserPluginGuest* test_guest_; TestBrowserPluginGuestManager* test_guest_manager_; DISALLOW_COPY_AND_ASSIGN(BrowserPluginHostTest); }; // This test loads a guest that has a busy loop, and therefore it hangs the // guest. // // Disabled on Windows and Linux since it is flaky. crbug.com/164812 // THIS TEST IS ALWAYS FLAKY. DO NOT ENABLE AGAIN WITHOUT REWRITING. #if defined(OS_WIN) || defined(OS_LINUX) #define MAYBE_GuestUnresponsive DISABLED_GuestUnresponsive #else #define MAYBE_GuestUnresponsive GuestUnresponsive #endif IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_GuestUnresponsive) { // Override the hang timeout for guest to be very small. content::BrowserPluginGuest::set_factory_for_testing( TestShortHangTimeoutGuestFactory::GetInstance()); const char kEmbedderURL[] = "/browser_plugin_embedder_guest_unresponsive.html"; StartBrowserPluginTest( kEmbedderURL, kHTMLForGuestBusyLoop, true, std::string()); // Wait until the busy loop starts. { const string16 expected_title = ASCIIToUTF16("start"); content::TitleWatcher title_watcher(test_guest()->web_contents(), expected_title); // Hang the guest for a length of time. int spin_time = 10 * TestTimeouts::tiny_timeout().InMilliseconds(); ExecuteSyncJSFunction( test_guest()->web_contents()->GetRenderViewHost(), base::StringPrintf("StartPauseMs(%d);", spin_time).c_str()); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } { const string16 expected_title = ASCIIToUTF16("done"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); // Send a mouse event to the guest. SimulateMouseClick(test_embedder()->web_contents(), 0, WebKit::WebMouseEvent::ButtonLeft); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } // Verify that the embedder has received the 'unresponsive' and 'responsive' // events. RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh, "unresponsiveCalled"); bool result = false; ASSERT_TRUE(value->GetAsBoolean(&result)); EXPECT_TRUE(result); value = content::ExecuteScriptAndGetValue(rvh, "responsiveCalled"); result = false; ASSERT_TRUE(value->GetAsBoolean(&result)); EXPECT_TRUE(result); } // This test ensures that if guest isn't there and we resize the guest (from // js), it remembers the size correctly. // // Initially we load an embedder with a guest without a src attribute (which has // dimension 640x480), resize it to 100x200, and then we set the source to a // sample guest. In the end we verify that the correct size has been set. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, NavigateAfterResize) { const gfx::Size nxt_size = gfx::Size(100, 200); const std::string embedder_code = base::StringPrintf( "SetSize(%d, %d);", nxt_size.width(), nxt_size.height()); const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, embedder_code); // Wait for the guest to receive a damage buffer of size 100x200. // This means the guest will be painted properly at that size. test_guest()->WaitForDamageBufferWithSize(nxt_size); } IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AdvanceFocus) { const char kEmbedderURL[] = "/browser_plugin_focus.html"; const char* kGuestURL = "/browser_plugin_focus_child.html"; StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string()); SimulateMouseClick(test_embedder()->web_contents(), 0, WebKit::WebMouseEvent::ButtonLeft); BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents()); // Wait until we focus into the guest. test_guest()->WaitForFocus(); // TODO(fsamuel): A third Tab key press should not be necessary. // The browser plugin will take keyboard focus but it will not // focus an initial element. The initial element is dependent // upon tab direction which WebKit does not propagate to the plugin. // See http://crbug.com/147644. BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents()); BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents()); BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents()); test_guest()->WaitForAdvanceFocus(); } // This test opens a page in http and then opens another page in https, forcing // a RenderViewHost swap in the web_contents. We verify that the embedder in the // web_contents gets cleared properly. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderChangedAfterSwap) { net::SpawnedTestServer https_server( net::SpawnedTestServer::TYPE_HTTPS, net::SpawnedTestServer::kLocalhost, base::FilePath(FILE_PATH_LITERAL("content/test/data"))); ASSERT_TRUE(https_server.Start()); // 1. Load an embedder page with one guest in it. const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); // 2. Navigate to a URL in https, so we trigger a RenderViewHost swap. GURL test_https_url(https_server.GetURL( "files/browser_plugin_title_change.html")); content::WindowedNotificationObserver swap_observer( content::NOTIFICATION_WEB_CONTENTS_SWAPPED, content::Source<WebContents>(test_embedder()->web_contents())); NavigateToURL(shell(), test_https_url); swap_observer.Wait(); TestBrowserPluginEmbedder* test_embedder_after_swap = static_cast<TestBrowserPluginEmbedder*>( static_cast<WebContentsImpl*>(shell()->web_contents())-> GetBrowserPluginEmbedder()); // Verify we have a no embedder in web_contents (since the new page doesn't // have any browser plugin). ASSERT_TRUE(!test_embedder_after_swap); ASSERT_NE(test_embedder(), test_embedder_after_swap); } // This test opens two pages in http and there is no RenderViewHost swap, // therefore the embedder created on first page navigation stays the same in // web_contents. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderSameAfterNav) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); WebContentsImpl* embedder_web_contents = test_embedder()->web_contents(); // Navigate to another page in same host and port, so RenderViewHost swap // does not happen and existing embedder doesn't change in web_contents. GURL test_url_new(embedded_test_server()->GetURL( "/browser_plugin_title_change.html")); const string16 expected_title = ASCIIToUTF16("done"); content::TitleWatcher title_watcher(shell()->web_contents(), expected_title); NavigateToURL(shell(), test_url_new); LOG(INFO) << "Start waiting for title"; string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); LOG(INFO) << "Done navigating to second page"; TestBrowserPluginEmbedder* test_embedder_after_nav = static_cast<TestBrowserPluginEmbedder*>( embedder_web_contents->GetBrowserPluginEmbedder()); // Embedder must not change in web_contents. ASSERT_EQ(test_embedder_after_nav, test_embedder()); } // This test verifies that hiding the embedder also hides the guest. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, BrowserPluginVisibilityChanged) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); // Hide the Browser Plugin. RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); ExecuteSyncJSFunction( rvh, "document.getElementById('plugin').style.visibility = 'hidden'"); // Make sure that the guest is hidden. test_guest()->WaitUntilHidden(); } IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderVisibilityChanged) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); // Hide the embedder. test_embedder()->web_contents()->WasHidden(); // Make sure that hiding the embedder also hides the guest. test_guest()->WaitUntilHidden(); } // This test verifies that calling the reload method reloads the guest. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, ReloadGuest) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); test_guest()->ResetUpdateRectCount(); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').reload()"); test_guest()->WaitForReload(); } // This test verifies that calling the stop method forwards the stop request // to the guest's WebContents. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, StopGuest) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').stop()"); test_guest()->WaitForStop(); } // Verifies that installing/uninstalling touch-event handlers in the guest // plugin correctly updates the touch-event handling state in the embedder. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AcceptTouchEvents) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest( kEmbedderURL, kHTMLForGuestTouchHandler, true, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); // The embedder should not have any touch event handlers at this point. EXPECT_FALSE(rvh->has_touch_handler()); // Install the touch handler in the guest. This should cause the embedder to // start listening for touch events too. RenderViewHostMessageObserver observer(rvh, ViewHostMsg_HasTouchEventHandlers::ID); ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(), "InstallTouchHandler();"); observer.WaitUntilMessageReceived(); EXPECT_TRUE(rvh->has_touch_handler()); // Uninstalling the touch-handler in guest should cause the embedder to stop // listening for touch events. observer.ResetState(); ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(), "UninstallTouchHandler();"); observer.WaitUntilMessageReceived(); EXPECT_FALSE(rvh->has_touch_handler()); } IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, Renavigate) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest( kEmbedderURL, GetHTMLForGuestWithTitle("P1"), true, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); // Navigate to P2 and verify that the navigation occurred. { const string16 expected_title = ASCIIToUTF16("P2"); content::TitleWatcher title_watcher(test_guest()->web_contents(), expected_title); ExecuteSyncJSFunction( rvh, base::StringPrintf( "SetSrc('%s');", GetHTMLForGuestWithTitle("P2").c_str())); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } // Navigate to P3 and verify that the navigation occurred. { const string16 expected_title = ASCIIToUTF16("P3"); content::TitleWatcher title_watcher(test_guest()->web_contents(), expected_title); ExecuteSyncJSFunction( rvh, base::StringPrintf( "SetSrc('%s');", GetHTMLForGuestWithTitle("P3").c_str())); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } // Go back and verify that we're back at P2. { const string16 expected_title = ASCIIToUTF16("P2"); content::TitleWatcher title_watcher(test_guest()->web_contents(), expected_title); ExecuteSyncJSFunction(rvh, "Back();"); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh, "CanGoBack()"); bool result = false; ASSERT_TRUE(value->GetAsBoolean(&result)); EXPECT_TRUE(result); value = content::ExecuteScriptAndGetValue(rvh, "CanGoForward()"); result = false; ASSERT_TRUE(value->GetAsBoolean(&result)); EXPECT_TRUE(result); } // Go forward and verify that we're back at P3. { const string16 expected_title = ASCIIToUTF16("P3"); content::TitleWatcher title_watcher(test_guest()->web_contents(), expected_title); ExecuteSyncJSFunction(rvh, "Forward();"); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh, "CanGoForward()"); bool result = true; ASSERT_TRUE(value->GetAsBoolean(&result)); EXPECT_FALSE(result); } // Go back two entries and verify that we're back at P1. { const string16 expected_title = ASCIIToUTF16("P1"); content::TitleWatcher title_watcher(test_guest()->web_contents(), expected_title); ExecuteSyncJSFunction(rvh, "Go(-2);"); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh, "CanGoBack()"); bool result = true; ASSERT_TRUE(value->GetAsBoolean(&result)); EXPECT_FALSE(result); } } // This tests verifies that reloading the embedder does not crash the browser // and that the guest is reset. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, ReloadEmbedder) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); // Change the title of the page to 'modified' so that we know that // the page has successfully reloaded when it goes back to 'embedder' // in the next step. { const string16 expected_title = ASCIIToUTF16("modified"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); ExecuteSyncJSFunction(rvh, base::StringPrintf("SetTitle('%s');", "modified")); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } // Reload the embedder page, and verify that the reload was successful. // Then navigate the guest to verify that the browser process does not crash. { const string16 expected_title = ASCIIToUTF16("embedder"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); test_embedder()->web_contents()->GetController().Reload(false); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); ExecuteSyncJSFunction( test_embedder()->web_contents()->GetRenderViewHost(), base::StringPrintf("SetSrc('%s');", kHTMLForGuest)); test_guest_manager()->WaitForGuestAdded(); const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map = test_guest_manager()->guest_web_contents_for_testing(); WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>( instance_map.begin()->second); TestBrowserPluginGuest* new_test_guest = static_cast<TestBrowserPluginGuest*>( test_guest_web_contents->GetBrowserPluginGuest()); ASSERT_TRUE(new_test_guest != NULL); // Wait for the guest to send an UpdateRectMsg, meaning it is ready. new_test_guest->WaitForUpdateRectMsg(); } } IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, TerminateGuest) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').terminate()"); // Expect the guest to crash. test_guest()->WaitForExit(); } // This test verifies that the guest is responsive after crashing and going back // to a previous navigation entry. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, BackAfterTerminateGuest) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; StartBrowserPluginTest( kEmbedderURL, GetHTMLForGuestWithTitle("P1"), true, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); // Navigate to P2 and verify that the navigation occurred. { const string16 expected_title = ASCIIToUTF16("P2"); content::TitleWatcher title_watcher(test_guest()->web_contents(), expected_title); ExecuteSyncJSFunction( rvh, base::StringPrintf( "SetSrc('%s');", GetHTMLForGuestWithTitle("P2").c_str())); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } // Kill the guest. ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').terminate()"); // Expect the guest to report that it crashed. test_guest()->WaitForExit(); // Go back and verify that we're back at P1. { const string16 expected_title = ASCIIToUTF16("P1"); content::TitleWatcher title_watcher(test_guest()->web_contents(), expected_title); ExecuteSyncJSFunction(rvh, "Back();"); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } // Send an input event and verify that the guest receives the input. SimulateMouseClick(test_embedder()->web_contents(), 0, WebKit::WebMouseEvent::ButtonLeft); test_guest()->WaitForInput(); } IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, LoadStart) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, "about:blank", true, std::string()); const string16 expected_title = ASCIIToUTF16(kHTMLForGuest); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); // Renavigate the guest to |kHTMLForGuest|. RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); ExecuteSyncJSFunction(rvh, base::StringPrintf("SetSrc('%s');", kHTMLForGuest)); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } namespace { class EmptyHttpResponse : public net::test_server::HttpResponse { public: virtual std::string ToResponseString() const OVERRIDE { return std::string(); } }; // Handles |request| by serving an empty response. scoped_ptr<net::test_server::HttpResponse> EmptyResponseHandler( const std::string& path, const net::test_server::HttpRequest& request) { if (StartsWithASCII(path, request.relative_url, true)) { return scoped_ptr<net::test_server::HttpResponse>( new EmptyHttpResponse); } return scoped_ptr<net::test_server::HttpResponse>(NULL); } // Handles |request| by serving a redirect response. scoped_ptr<net::test_server::HttpResponse> RedirectResponseHandler( const std::string& path, const GURL& redirect_target, const net::test_server::HttpRequest& request) { if (!StartsWithASCII(path, request.relative_url, true)) return scoped_ptr<net::test_server::HttpResponse>(NULL); scoped_ptr<net::test_server::BasicHttpResponse> http_response( new net::test_server::BasicHttpResponse); http_response->set_code(net::test_server::MOVED); http_response->AddCustomHeader("Location", redirect_target.spec()); return http_response.PassAs<net::test_server::HttpResponse>(); } } // namespace IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, LoadAbort) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, "about:blank", true, std::string()); { // Navigate the guest to "close-socket". const char kEmptyResponsePath[] = "/close-socket"; embedded_test_server()->RegisterRequestHandler( base::Bind(&EmptyResponseHandler, kEmptyResponsePath)); const string16 expected_title = ASCIIToUTF16("ERR_EMPTY_RESPONSE"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); GURL test_url = embedded_test_server()->GetURL(kEmptyResponsePath); ExecuteSyncJSFunction( rvh, base::StringPrintf("SetSrc('%s');", test_url.spec().c_str())); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } { // Navigate the guest to an illegal chrome:// URL. GURL test_url("chrome://newtab"); ChildProcessSecurityPolicyImpl* policy = ChildProcessSecurityPolicyImpl::GetInstance(); // Register chrome:// as a safe scheme so as to bypass // ChildProcessSecurityPolicyImpl::CanRequestURL(). if (!policy->IsWebSafeScheme(test_url.scheme())) policy->RegisterWebSafeScheme(test_url.scheme()); const string16 expected_title = ASCIIToUTF16("ERR_INVALID_URL"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); ExecuteSyncJSFunction( rvh, base::StringPrintf("SetSrc('%s');", test_url.spec().c_str())); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } { // Navigate the guest to an illegal file:// URL. const string16 expected_title = ASCIIToUTF16("ERR_ABORTED"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); GURL test_url("file://foo"); ExecuteSyncJSFunction( rvh, base::StringPrintf("SetSrc('%s');", test_url.spec().c_str())); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } } IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, LoadRedirect) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, "about:blank", true, std::string()); const string16 expected_title = ASCIIToUTF16("redirected"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); // Navigate with a redirect and wait until the title changes. const char kRedirectResponsePath[] = "/server-redirect"; embedded_test_server()->RegisterRequestHandler( base::Bind(&RedirectResponseHandler, kRedirectResponsePath, embedded_test_server()->GetURL("/title1.html"))); GURL redirect_url(embedded_test_server()->GetURL(kRedirectResponsePath)); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); ExecuteSyncJSFunction( rvh, base::StringPrintf("SetSrc('%s');", redirect_url.spec().c_str())); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); // Verify that we heard a loadRedirect during the navigation. scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh, "redirectOldUrl"); std::string result; EXPECT_TRUE(value->GetAsString(&result)); EXPECT_EQ(redirect_url.spec().c_str(), result); value = content::ExecuteScriptAndGetValue(rvh, "redirectNewUrl"); EXPECT_TRUE(value->GetAsString(&result)); EXPECT_EQ(embedded_test_server()->GetURL("/title1.html").spec().c_str(), result); } // Always failing in the win7_aura try bot. See http://crbug.com/181107. #if defined(OS_WIN) && defined(USE_AURA) #define MAYBE_AcceptDragEvents DISABLED_AcceptDragEvents #else #define MAYBE_AcceptDragEvents AcceptDragEvents #endif // Tests that a drag-n-drop over the browser plugin in the embedder happens // correctly. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_AcceptDragEvents) { const char kEmbedderURL[] = "/browser_plugin_dragging.html"; StartBrowserPluginTest( kEmbedderURL, kHTMLForGuestAcceptDrag, true, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); // Get a location in the embedder outside of the plugin. base::ListValue *start, *end; scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh, "dragLocation()"); ASSERT_TRUE(value->GetAsList(&start) && start->GetSize() == 2); double start_x, start_y; ASSERT_TRUE(start->GetDouble(0, &start_x) && start->GetDouble(1, &start_y)); // Get a location in the embedder that falls inside the plugin. value = content::ExecuteScriptAndGetValue(rvh, "dropLocation()"); ASSERT_TRUE(value->GetAsList(&end) && end->GetSize() == 2); double end_x, end_y; ASSERT_TRUE(end->GetDouble(0, &end_x) && end->GetDouble(1, &end_y)); WebDropData drop_data; GURL url = GURL("https://www.domain.com/index.html"); drop_data.url = url; // Pretend that the URL is being dragged over the embedder. Start the drag // from outside the plugin, then move the drag inside the plugin and drop. // This should trigger appropriate messages from the embedder to the guest, // and end with a drop on the guest. The guest changes title when a drop // happens. const string16 expected_title = ASCIIToUTF16("DROPPED"); content::TitleWatcher title_watcher(test_guest()->web_contents(), expected_title); rvh->DragTargetDragEnter(drop_data, gfx::Point(start_x, start_y), gfx::Point(start_x, start_y), WebKit::WebDragOperationEvery, 0); rvh->DragTargetDragOver(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y), WebKit::WebDragOperationEvery, 0); rvh->DragTargetDrop(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y), 0); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } // This test verifies that round trip postMessage works as expected. // 1. The embedder posts a message 'testing123' to the guest. // 2. The guest receives and replies to the message using the event object's // source object: event.source.postMessage('foobar', '*') // 3. The embedder receives the message and uses the event's source // object to do one final reply: 'stop' // 4. The guest receives the final 'stop' message. // 5. The guest acks the 'stop' message with a 'stop_ack' message. // 6. The embedder changes its title to 'main guest' when it sees the 'stop_ack' // message. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, PostMessage) { const char* kTesting = "testing123"; const char* kEmbedderURL = "/browser_plugin_embedder.html"; const char* kGuestURL = "/browser_plugin_post_message_guest.html"; StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); { const string16 expected_title = ASCIIToUTF16("main guest"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); // By the time we get here 'contentWindow' should be ready because the // guest has completed loading. ExecuteSyncJSFunction( rvh, base::StringPrintf("PostMessage('%s, false');", kTesting)); // The title will be updated to "main guest" at the last stage of the // process described above. string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } } // This is the same as BrowserPluginHostTest.PostMessage but also // posts a message to an iframe. // TODO(fsamuel): This test should replace the previous test once postMessage // iframe targeting is fixed (see http://crbug.com/153701). IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DISABLED_PostMessageToIFrame) { const char* kTesting = "testing123"; const char* kEmbedderURL = "/browser_plugin_embedder.html"; const char* kGuestURL = "/browser_plugin_post_message_guest.html"; StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); { const string16 expected_title = ASCIIToUTF16("main guest"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); ExecuteSyncJSFunction( rvh, base::StringPrintf("PostMessage('%s, false');", kTesting)); // The title will be updated to "main guest" at the last stage of the // process described above. string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } { content::TitleWatcher ready_watcher(test_embedder()->web_contents(), ASCIIToUTF16("ready")); RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( test_guest()->web_contents()->GetRenderViewHost()); GURL test_url = embedded_test_server()->GetURL( "/browser_plugin_post_message_guest.html"); ExecuteSyncJSFunction( guest_rvh, base::StringPrintf( "CreateChildFrame('%s');", test_url.spec().c_str())); string16 actual_title = ready_watcher.WaitAndGetTitle(); EXPECT_EQ(ASCIIToUTF16("ready"), actual_title); content::TitleWatcher iframe_watcher(test_embedder()->web_contents(), ASCIIToUTF16("iframe")); ExecuteSyncJSFunction( rvh, base::StringPrintf("PostMessage('%s', true);", kTesting)); // The title will be updated to "iframe" at the last stage of the // process described above. actual_title = iframe_watcher.WaitAndGetTitle(); EXPECT_EQ(ASCIIToUTF16("iframe"), actual_title); } } IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, LoadStop) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, "about:blank", true, std::string()); const string16 expected_title = ASCIIToUTF16("loadStop"); content::TitleWatcher title_watcher( test_embedder()->web_contents(), expected_title); // Renavigate the guest to |kHTMLForGuest|. RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); ExecuteSyncJSFunction(rvh, base::StringPrintf("SetSrc('%s');", kHTMLForGuest)); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, LoadCommit) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, "about:blank", true, std::string()); const string16 expected_title = ASCIIToUTF16( base::StringPrintf("loadCommit:%s", kHTMLForGuest)); content::TitleWatcher title_watcher( test_embedder()->web_contents(), expected_title); // Renavigate the guest to |kHTMLForGuest|. RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); ExecuteSyncJSFunction(rvh, base::StringPrintf("SetSrc('%s');", kHTMLForGuest)); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); scoped_ptr<base::Value> is_top_level = content::ExecuteScriptAndGetValue(rvh, "commitIsTopLevel"); bool top_level_bool = false; EXPECT_TRUE(is_top_level->GetAsBoolean(&top_level_bool)); EXPECT_EQ(true, top_level_bool); } // This test verifies that if a browser plugin is hidden before navigation, // the guest starts off hidden. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, HiddenBeforeNavigation) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; const std::string embedder_code = "document.getElementById('plugin').style.visibility = 'hidden'"; StartBrowserPluginTest( kEmbedderURL, kHTMLForGuest, true, embedder_code); EXPECT_FALSE(test_guest()->visible()); } // This test verifies that if we lose the guest, and get a new one, // the new guest will inherit the visibility state of the old guest. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, VisibilityPreservation) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); // Hide the BrowserPlugin. ExecuteSyncJSFunction( rvh, "document.getElementById('plugin').style.visibility = 'hidden';"); test_guest()->WaitUntilHidden(); // Kill the current guest. ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').terminate();"); test_guest()->WaitForExit(); // Get a new guest. ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').reload();"); test_guest()->WaitForLoadStop(); // Verify that the guest is told to hide. test_guest()->WaitUntilHidden(); } // This test verifies that if a browser plugin is focused before navigation then // the guest starts off focused. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusBeforeNavigation) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; const std::string embedder_code = "document.getElementById('plugin').focus();"; StartBrowserPluginTest( kEmbedderURL, kHTMLForGuest, true, embedder_code); RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( test_guest()->web_contents()->GetRenderViewHost()); // Verify that the guest is focused. scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()"); bool result = false; ASSERT_TRUE(value->GetAsBoolean(&result)); EXPECT_TRUE(result); } // This test verifies that if we lose the guest, and get a new one, // the new guest will inherit the focus state of the old guest. // crbug.com/170249 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DISABLED_FocusPreservation) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( test_guest()->web_contents()->GetRenderViewHost()); { // Focus the BrowserPlugin. This will have the effect of also focusing the // current guest. ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').focus();"); // Verify that key presses go to the guest. SimulateSpaceKeyPress(test_embedder()->web_contents()); test_guest()->WaitForInput(); // Verify that the guest is focused. scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()"); bool result = false; ASSERT_TRUE(value->GetAsBoolean(&result)); EXPECT_TRUE(result); } // Kill the current guest. ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').terminate();"); test_guest()->WaitForExit(); { // Get a new guest. ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').reload();"); test_guest()->WaitForLoadStop(); // Verify that the guest is focused. scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()"); bool result = false; ASSERT_TRUE(value->GetAsBoolean(&result)); EXPECT_TRUE(result); } } IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusTracksEmbedder) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( test_guest()->web_contents()->GetRenderViewHost()); { // Focus the BrowserPlugin. This will have the effect of also focusing the // current guest. ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').focus();"); // Verify that key presses go to the guest. SimulateSpaceKeyPress(test_embedder()->web_contents()); test_guest()->WaitForInput(); // Verify that the guest is focused. scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()"); bool result = false; ASSERT_TRUE(value->GetAsBoolean(&result)); EXPECT_TRUE(result); } // Blur the embedder. test_embedder()->web_contents()->GetRenderViewHost()->Blur(); test_guest()->WaitForBlur(); } // This test verifies that if a browser plugin is in autosize mode before // navigation then the guest starts auto-sized. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AutoSizeBeforeNavigation) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; const std::string embedder_code = "document.getElementById('plugin').minwidth = 300;" "document.getElementById('plugin').minheight = 200;" "document.getElementById('plugin').maxwidth = 600;" "document.getElementById('plugin').maxheight = 400;" "document.getElementById('plugin').autosize = true;"; StartBrowserPluginTest( kEmbedderURL, kHTMLForGuestWithSize, true, embedder_code); // Verify that the guest has been auto-sized. test_guest()->WaitForViewSize(gfx::Size(300, 400)); } // This test verifies that enabling autosize resizes the guest and triggers // a 'sizechanged' event. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AutoSizeAfterNavigation) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; StartBrowserPluginTest( kEmbedderURL, kHTMLForGuestWithSize, true, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); { const string16 expected_title = ASCIIToUTF16("AutoSize(300, 400)"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); ExecuteSyncJSFunction( rvh, "document.getElementById('plugin').minwidth = 300;" "document.getElementById('plugin').minheight = 200;" "document.getElementById('plugin').maxwidth = 600;" "document.getElementById('plugin').maxheight = 400;" "document.getElementById('plugin').autosize = true;"); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } { // Change the minwidth and verify that it causes relayout. const string16 expected_title = ASCIIToUTF16("AutoSize(350, 400)"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); ExecuteSyncJSFunction( rvh, "document.getElementById('plugin').minwidth = 350;"); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } { // Turn off autoSize and verify that the guest resizes to fit the container. ExecuteSyncJSFunction( rvh, "document.getElementById('plugin').autosize = null;"); test_guest()->WaitForViewSize(gfx::Size(640, 480)); } } // Test for regression http://crbug.com/162961. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, GetRenderViewHostAtPositionTest) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; const std::string embedder_code = base::StringPrintf("SetSize(%d, %d);", 100, 100); StartBrowserPluginTest(kEmbedderURL, kHTMLForGuestWithSize, true, embedder_code); // Check for render view host at position (150, 150) that is outside the // bounds of our guest, so this would respond with the render view host of the // embedder. test_embedder()->WaitForRenderViewHostAtPosition(150, 150); ASSERT_EQ(test_embedder()->web_contents()->GetRenderViewHost(), test_embedder()->last_rvh_at_position_response()); } // Flaky on Win Aura Tests (1) bot. See http://crbug.com/233087. #if defined(OS_WIN) && defined(USE_AURA) #define MAYBE_ChangeWindowName DISABLED_ChangeWindowName #else #define MAYBE_ChangeWindowName ChangeWindowName #endif IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_ChangeWindowName) { const char kEmbedderURL[] = "/browser_plugin_naming_embedder.html"; const char* kGuestURL = "/browser_plugin_naming_guest.html"; StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string()); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); // Verify that the plugin's name is properly initialized. { scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue( rvh, "document.getElementById('plugin').name"); std::string result; EXPECT_TRUE(value->GetAsString(&result)); EXPECT_EQ("start", result); } { // Open a channel with the guest, wait until it replies, // then verify that the plugin's name has been updated. const string16 expected_title = ASCIIToUTF16("guest"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); ExecuteSyncJSFunction(rvh, "OpenCommChannel();"); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue( rvh, "document.getElementById('plugin').name"); std::string result; EXPECT_TRUE(value->GetAsString(&result)); EXPECT_EQ("guest", result); } { // Set the plugin's name and verify that the window.name of the guest // has been updated. const string16 expected_title = ASCIIToUTF16("foobar"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').name = 'foobar';"); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } } // This test verifies that all autosize attributes can be removed // without crashing the plugin, or throwing errors. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, RemoveAutosizeAttributes) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; const std::string embedder_code = "document.getElementById('plugin').minwidth = 300;" "document.getElementById('plugin').minheight = 200;" "document.getElementById('plugin').maxwidth = 600;" "document.getElementById('plugin').maxheight = 400;" "document.getElementById('plugin').name = 'name';" "document.getElementById('plugin').src = 'foo';" "document.getElementById('plugin').autosize = '';"; StartBrowserPluginTest( kEmbedderURL, kHTMLForGuestWithSize, true, embedder_code); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); RemoveAttributes(rvh, "maxheight, maxwidth, minheight, minwidth, autosize"); // Verify that the guest resizes to fit the container (and hasn't crashed). test_guest()->WaitForViewSize(gfx::Size(640, 480)); EXPECT_TRUE(IsAttributeNull(rvh, "maxheight")); EXPECT_TRUE(IsAttributeNull(rvh, "maxwidth")); EXPECT_TRUE(IsAttributeNull(rvh, "minheight")); EXPECT_TRUE(IsAttributeNull(rvh, "minwidth")); EXPECT_TRUE(IsAttributeNull(rvh, "autosize")); } // This test verifies that autosize works when some of the parameters are unset. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, PartialAutosizeAttributes) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; const std::string embedder_code = "document.getElementById('plugin').minwidth = 300;" "document.getElementById('plugin').minheight = 200;" "document.getElementById('plugin').maxwidth = 700;" "document.getElementById('plugin').maxheight = 600;" "document.getElementById('plugin').autosize = '';"; StartBrowserPluginTest( kEmbedderURL, kHTMLForGuestWithSize, true, embedder_code); RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( test_embedder()->web_contents()->GetRenderViewHost()); { // Remove an autosize attribute and verify that it causes relayout. const string16 expected_title = ASCIIToUTF16("AutoSize(640, 400)"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); RemoveAttributes(rvh, "minwidth"); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } { // Remove an autosize attribute and verify that it causes relayout. // Also tests that when minwidth > maxwidth, minwidth = maxwidth. const string16 expected_title = ASCIIToUTF16("AutoSize(700, 480)"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); RemoveAttributes(rvh, "maxheight"); ExecuteSyncJSFunction( rvh, "document.getElementById('plugin').minwidth = 800;" "document.getElementById('plugin').minheight = 800;"); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } { // Remove maxwidth and make sure the size returns to plugin size. const string16 expected_title = ASCIIToUTF16("AutoSize(640, 480)"); content::TitleWatcher title_watcher(test_embedder()->web_contents(), expected_title); RemoveAttributes(rvh, "maxwidth"); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); } } } // namespace content