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.
5 #include "base/command_line.h"
6 #include "base/memory/singleton.h"
7 #include "base/run_loop.h"
8 #include "base/strings/string_split.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/test/test_timeouts.h"
12 #include "content/browser/browser_plugin/browser_plugin_guest.h"
13 #include "content/browser/browser_plugin/browser_plugin_host_factory.h"
14 #include "content/browser/browser_plugin/test_browser_plugin_embedder.h"
15 #include "content/browser/browser_plugin/test_browser_plugin_guest.h"
16 #include "content/browser/browser_plugin/test_browser_plugin_guest_delegate.h"
17 #include "content/browser/browser_plugin/test_browser_plugin_guest_manager.h"
18 #include "content/browser/child_process_security_policy_impl.h"
19 #include "content/browser/frame_host/render_widget_host_view_guest.h"
20 #include "content/browser/renderer_host/render_view_host_impl.h"
21 #include "content/browser/web_contents/web_contents_impl.h"
22 #include "content/common/browser_plugin/browser_plugin_messages.h"
23 #include "content/common/view_messages.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/notification_types.h"
26 #include "content/public/browser/render_widget_host_view.h"
27 #include "content/public/browser/web_contents_observer.h"
28 #include "content/public/common/content_switches.h"
29 #include "content/public/common/drop_data.h"
30 #include "content/public/common/url_constants.h"
31 #include "content/public/test/browser_test_utils.h"
32 #include "content/public/test/test_utils.h"
33 #include "content/shell/browser/shell.h"
34 #include "content/test/content_browser_test.h"
35 #include "content/test/content_browser_test_utils.h"
36 #include "net/base/net_util.h"
37 #include "net/test/embedded_test_server/embedded_test_server.h"
38 #include "net/test/embedded_test_server/http_request.h"
39 #include "net/test/embedded_test_server/http_response.h"
40 #include "net/test/spawned_test_server/spawned_test_server.h"
41 #include "third_party/WebKit/public/web/WebInputEvent.h"
43 using base::ASCIIToUTF16;
44 using blink::WebInputEvent;
45 using blink::WebMouseEvent;
46 using content::BrowserPluginEmbedder;
47 using content::BrowserPluginGuest;
48 using content::BrowserPluginHostFactory;
49 using content::WebContentsImpl;
51 const char kHTMLForGuest[] =
52 "data:text/html,<html><body>hello world</body></html>";
54 const char kHTMLForGuestTouchHandler[] =
55 "data:text/html,<html><body><div id=\"touch\">With touch</div></body>"
56 "<script type=\"text/javascript\">"
57 "function handler() {}"
58 "function InstallTouchHandler() { "
59 " document.getElementById(\"touch\").addEventListener(\"touchstart\", "
62 "function UninstallTouchHandler() { "
63 " document.getElementById(\"touch\").removeEventListener(\"touchstart\", "
68 const char kHTMLForGuestAcceptDrag[] =
69 "data:text/html,<html><body>"
71 "function dropped() {"
72 " document.title = \"DROPPED\";"
75 "<textarea id=\"text\" style=\"width:100%; height: 100%\""
76 " ondrop=\"dropped();\">"
80 const char kHTMLForGuestWithSize[] =
83 "<body style=\"margin: 0px;\">"
84 "<img style=\"width: 100%; height: 400px;\"/>"
90 // Test factory for creating test instances of BrowserPluginEmbedder and
91 // BrowserPluginGuest.
92 class TestBrowserPluginHostFactory : public BrowserPluginHostFactory {
94 virtual BrowserPluginGuestManager*
95 CreateBrowserPluginGuestManager() OVERRIDE {
96 guest_manager_instance_count_++;
97 if (message_loop_runner_.get())
98 message_loop_runner_->Quit();
99 return new TestBrowserPluginGuestManager();
102 virtual BrowserPluginGuest* CreateBrowserPluginGuest(
104 WebContentsImpl* web_contents) OVERRIDE {
105 return new TestBrowserPluginGuest(instance_id, web_contents);
108 // Also keeps track of number of instances created.
109 virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder(
110 WebContentsImpl* web_contents) OVERRIDE {
112 return new TestBrowserPluginEmbedder(web_contents);
116 static TestBrowserPluginHostFactory* GetInstance() {
117 return Singleton<TestBrowserPluginHostFactory>::get();
120 // Waits for at least one embedder to be created in the test. Returns true if
121 // we have a guest, false if waiting times out.
122 void WaitForGuestManagerCreation() {
123 // Check if already have created an instance.
124 if (guest_manager_instance_count_ > 0)
127 message_loop_runner_ = new MessageLoopRunner();
128 message_loop_runner_->Run();
132 TestBrowserPluginHostFactory() : guest_manager_instance_count_(0) {}
133 virtual ~TestBrowserPluginHostFactory() {}
137 friend struct DefaultSingletonTraits<TestBrowserPluginHostFactory>;
139 scoped_refptr<MessageLoopRunner> message_loop_runner_;
140 int guest_manager_instance_count_;
142 DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginHostFactory);
145 // Test factory class for browser plugin that creates guests with short hang
147 class TestShortHangTimeoutGuestFactory : public TestBrowserPluginHostFactory {
149 virtual BrowserPluginGuest* CreateBrowserPluginGuest(
150 int instance_id, WebContentsImpl* web_contents) OVERRIDE {
151 TestBrowserPluginGuest* guest =
152 new TestBrowserPluginGuest(instance_id, web_contents);
153 guest->set_guest_hang_timeout(TestTimeouts::tiny_timeout());
158 static TestShortHangTimeoutGuestFactory* GetInstance() {
159 return Singleton<TestShortHangTimeoutGuestFactory>::get();
163 TestShortHangTimeoutGuestFactory() {}
164 virtual ~TestShortHangTimeoutGuestFactory() {}
168 friend struct DefaultSingletonTraits<TestShortHangTimeoutGuestFactory>;
170 DISALLOW_COPY_AND_ASSIGN(TestShortHangTimeoutGuestFactory);
173 // A transparent observer that can be used to verify that a RenderViewHost
174 // received a specific message.
175 class MessageObserver : public WebContentsObserver {
177 MessageObserver(WebContents* web_contents, uint32 message_id)
178 : WebContentsObserver(web_contents),
179 message_id_(message_id),
180 message_received_(false) {
183 virtual ~MessageObserver() {}
185 void WaitUntilMessageReceived() {
186 if (message_received_)
188 message_loop_runner_ = new MessageLoopRunner();
189 message_loop_runner_->Run();
193 message_received_ = false;
196 // IPC::Listener implementation.
197 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
198 if (message.type() == message_id_) {
199 message_received_ = true;
200 if (message_loop_runner_.get())
201 message_loop_runner_->Quit();
207 scoped_refptr<MessageLoopRunner> message_loop_runner_;
209 bool message_received_;
211 DISALLOW_COPY_AND_ASSIGN(MessageObserver);
214 class BrowserPluginHostTest : public ContentBrowserTest {
216 BrowserPluginHostTest()
217 : test_embedder_(NULL),
219 test_guest_manager_(NULL) {}
221 virtual void SetUp() OVERRIDE {
222 // Override factory to create tests instances of BrowserPlugin*.
223 content::BrowserPluginEmbedder::set_factory_for_testing(
224 TestBrowserPluginHostFactory::GetInstance());
225 content::BrowserPluginGuest::set_factory_for_testing(
226 TestBrowserPluginHostFactory::GetInstance());
227 content::BrowserPluginGuestManager::set_factory_for_testing(
228 TestBrowserPluginHostFactory::GetInstance());
229 ContentBrowserTest::SetUp();
231 virtual void TearDown() OVERRIDE {
232 content::BrowserPluginEmbedder::set_factory_for_testing(NULL);
233 content::BrowserPluginGuest::set_factory_for_testing(NULL);
235 ContentBrowserTest::TearDown();
238 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
239 // Enable browser plugin in content_shell for running test.
240 command_line->AppendSwitch(switches::kEnableBrowserPluginForAllViewTypes);
243 static void SimulateSpaceKeyPress(WebContents* web_contents) {
244 SimulateKeyPress(web_contents,
252 static void SimulateTabKeyPress(WebContents* web_contents) {
253 SimulateKeyPress(web_contents,
261 // Executes the javascript synchronously and makes sure the returned value is
263 void ExecuteSyncJSFunction(RenderViewHost* rvh, const std::string& jscript) {
264 scoped_ptr<base::Value> value =
265 content::ExecuteScriptAndGetValue(rvh, jscript);
268 bool IsAttributeNull(RenderViewHost* rvh, const std::string& attribute) {
269 scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh,
270 "document.getElementById('plugin').getAttribute('" + attribute + "');");
271 return value->GetType() == base::Value::TYPE_NULL;
274 // Removes all attributes in the comma-delimited string |attributes|.
275 void RemoveAttributes(RenderViewHost* rvh, const std::string& attributes) {
276 std::vector<std::string> attributes_list;
277 base::SplitString(attributes, ',', &attributes_list);
278 std::vector<std::string>::const_iterator itr;
279 for (itr = attributes_list.begin(); itr != attributes_list.end(); ++itr) {
280 ExecuteSyncJSFunction(rvh, "document.getElementById('plugin')"
281 "." + *itr + " = null;");
285 // This helper method does the following:
286 // 1. Start the test server and navigate the shell to |embedder_url|.
287 // 2. Execute custom pre-navigation |embedder_code| if provided.
288 // 3. Navigate the guest to the |guest_url|.
289 // 4. Verify that the guest has been created and has completed loading.
290 void StartBrowserPluginTest(const std::string& embedder_url,
291 const std::string& guest_url,
292 bool is_guest_data_url,
293 const std::string& embedder_code) {
294 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
295 GURL test_url(embedded_test_server()->GetURL(embedder_url));
296 NavigateToURL(shell(), test_url);
298 WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>(
299 shell()->web_contents());
300 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
301 embedder_web_contents->GetRenderViewHost());
302 // Focus the embedder.
305 rvh->SetInputMethodActive(true);
307 // Allow the test to do some operations on the embedder before we perform
308 // the first navigation of the guest.
309 if (!embedder_code.empty())
310 ExecuteSyncJSFunction(rvh, embedder_code);
312 if (!is_guest_data_url) {
313 test_url = embedded_test_server()->GetURL(guest_url);
314 ExecuteSyncJSFunction(
315 rvh, base::StringPrintf("SetSrc('%s');", test_url.spec().c_str()));
317 ExecuteSyncJSFunction(
318 rvh, base::StringPrintf("SetSrc('%s');", guest_url.c_str()));
321 // Wait to make sure embedder is created/attached to WebContents.
322 TestBrowserPluginHostFactory::GetInstance()->WaitForGuestManagerCreation();
324 test_embedder_ = static_cast<TestBrowserPluginEmbedder*>(
325 embedder_web_contents->GetBrowserPluginEmbedder());
326 ASSERT_TRUE(test_embedder_);
328 test_guest_manager_ = static_cast<TestBrowserPluginGuestManager*>(
329 embedder_web_contents->GetBrowserPluginGuestManager());
330 ASSERT_TRUE(test_guest_manager_);
332 test_guest_manager_->WaitForGuestAdded();
334 // Verify that we have exactly one guest.
335 const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map =
336 test_guest_manager_->guest_web_contents_for_testing();
337 EXPECT_EQ(1u, instance_map.size());
339 WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
340 instance_map.begin()->second);
341 test_guest_ = static_cast<TestBrowserPluginGuest*>(
342 test_guest_web_contents->GetBrowserPluginGuest());
343 test_guest_->WaitForLoadStop();
346 TestBrowserPluginEmbedder* test_embedder() const { return test_embedder_; }
347 TestBrowserPluginGuest* test_guest() const { return test_guest_; }
348 TestBrowserPluginGuestManager* test_guest_manager() const {
349 return test_guest_manager_;
353 TestBrowserPluginEmbedder* test_embedder_;
354 TestBrowserPluginGuest* test_guest_;
355 TestBrowserPluginGuestManager* test_guest_manager_;
356 DISALLOW_COPY_AND_ASSIGN(BrowserPluginHostTest);
359 // This test ensures that if guest isn't there and we resize the guest (from
360 // js), it remembers the size correctly.
362 // Initially we load an embedder with a guest without a src attribute (which has
363 // dimension 640x480), resize it to 100x200, and then we set the source to a
364 // sample guest. In the end we verify that the correct size has been set.
365 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, NavigateAfterResize) {
366 const gfx::Size nxt_size = gfx::Size(100, 200);
367 const std::string embedder_code = base::StringPrintf(
368 "SetSize(%d, %d);", nxt_size.width(), nxt_size.height());
369 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
370 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, embedder_code);
372 // Wait for the guest to receive a damage buffer of size 100x200.
373 // This means the guest will be painted properly at that size.
374 test_guest()->WaitForDamageBufferWithSize(nxt_size);
377 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AdvanceFocus) {
378 const char kEmbedderURL[] = "/browser_plugin_focus.html";
379 const char* kGuestURL = "/browser_plugin_focus_child.html";
380 StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
382 SimulateMouseClick(test_embedder()->web_contents(), 0,
383 blink::WebMouseEvent::ButtonLeft);
384 BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
385 // Wait until we focus into the guest.
386 test_guest()->WaitForFocus();
388 // TODO(fsamuel): A third Tab key press should not be necessary.
389 // The browser plugin will take keyboard focus but it will not
390 // focus an initial element. The initial element is dependent
391 // upon tab direction which WebKit does not propagate to the plugin.
392 // See http://crbug.com/147644.
393 BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
394 BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
395 BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
396 test_guest()->WaitForAdvanceFocus();
399 // This test opens a page in http and then opens another page in https, forcing
400 // a RenderViewHost swap in the web_contents. We verify that the embedder in the
401 // web_contents gets cleared properly.
402 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderChangedAfterSwap) {
403 net::SpawnedTestServer https_server(
404 net::SpawnedTestServer::TYPE_HTTPS,
405 net::SpawnedTestServer::kLocalhost,
406 base::FilePath(FILE_PATH_LITERAL("content/test/data")));
407 ASSERT_TRUE(https_server.Start());
409 // 1. Load an embedder page with one guest in it.
410 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
411 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
413 // 2. Navigate to a URL in https, so we trigger a RenderViewHost swap.
414 GURL test_https_url(https_server.GetURL(
415 "files/browser_plugin_title_change.html"));
416 content::WindowedNotificationObserver swap_observer(
417 content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
418 content::Source<WebContents>(test_embedder()->web_contents()));
419 NavigateToURL(shell(), test_https_url);
420 swap_observer.Wait();
422 TestBrowserPluginEmbedder* test_embedder_after_swap =
423 static_cast<TestBrowserPluginEmbedder*>(
424 static_cast<WebContentsImpl*>(shell()->web_contents())->
425 GetBrowserPluginEmbedder());
426 // Verify we have a no embedder in web_contents (since the new page doesn't
427 // have any browser plugin).
428 ASSERT_TRUE(!test_embedder_after_swap);
429 ASSERT_NE(test_embedder(), test_embedder_after_swap);
432 // This test opens two pages in http and there is no RenderViewHost swap,
433 // therefore the embedder created on first page navigation stays the same in
435 // Failing flakily on Windows: crbug.com/308405
437 #define MAYBE_EmbedderSameAfterNav DISABLED_EmbedderSameAfterNav
439 #define MAYBE_EmbedderSameAfterNav EmbedderSameAfterNav
441 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_EmbedderSameAfterNav) {
442 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
443 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
444 WebContentsImpl* embedder_web_contents = test_embedder()->web_contents();
446 // Navigate to another page in same host and port, so RenderViewHost swap
447 // does not happen and existing embedder doesn't change in web_contents.
448 GURL test_url_new(embedded_test_server()->GetURL(
449 "/browser_plugin_title_change.html"));
450 const base::string16 expected_title = ASCIIToUTF16("done");
451 content::TitleWatcher title_watcher(shell()->web_contents(), expected_title);
452 NavigateToURL(shell(), test_url_new);
453 VLOG(0) << "Start waiting for title";
454 base::string16 actual_title = title_watcher.WaitAndGetTitle();
455 EXPECT_EQ(expected_title, actual_title);
456 VLOG(0) << "Done navigating to second page";
458 TestBrowserPluginEmbedder* test_embedder_after_nav =
459 static_cast<TestBrowserPluginEmbedder*>(
460 embedder_web_contents->GetBrowserPluginEmbedder());
461 // Embedder must not change in web_contents.
462 ASSERT_EQ(test_embedder_after_nav, test_embedder());
465 // This test verifies that hiding the embedder also hides the guest.
466 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, BrowserPluginVisibilityChanged) {
467 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
468 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
470 // Hide the Browser Plugin.
471 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
472 test_embedder()->web_contents()->GetRenderViewHost());
473 ExecuteSyncJSFunction(
474 rvh, "document.getElementById('plugin').style.visibility = 'hidden'");
476 // Make sure that the guest is hidden.
477 test_guest()->WaitUntilHidden();
480 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderVisibilityChanged) {
481 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
482 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
484 // Hide the embedder.
485 test_embedder()->web_contents()->WasHidden();
487 // Make sure that hiding the embedder also hides the guest.
488 test_guest()->WaitUntilHidden();
491 // Verifies that installing/uninstalling touch-event handlers in the guest
492 // plugin correctly updates the touch-event handling state in the embedder.
493 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AcceptTouchEvents) {
494 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
495 StartBrowserPluginTest(
496 kEmbedderURL, kHTMLForGuestTouchHandler, true, std::string());
498 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
499 test_embedder()->web_contents()->GetRenderViewHost());
500 // The embedder should not have any touch event handlers at this point.
501 EXPECT_FALSE(rvh->has_touch_handler());
503 // Install the touch handler in the guest. This should cause the embedder to
504 // start listening for touch events too.
505 MessageObserver observer(test_embedder()->web_contents(),
506 ViewHostMsg_HasTouchEventHandlers::ID);
507 ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(),
508 "InstallTouchHandler();");
509 observer.WaitUntilMessageReceived();
510 EXPECT_TRUE(rvh->has_touch_handler());
512 // Uninstalling the touch-handler in guest should cause the embedder to stop
513 // listening for touch events.
514 observer.ResetState();
515 ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(),
516 "UninstallTouchHandler();");
517 observer.WaitUntilMessageReceived();
518 EXPECT_FALSE(rvh->has_touch_handler());
521 // This tests verifies that reloading the embedder does not crash the browser
522 // and that the guest is reset.
523 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DISABLED_ReloadEmbedder) {
524 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
525 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
526 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
527 test_embedder()->web_contents()->GetRenderViewHost());
529 // Change the title of the page to 'modified' so that we know that
530 // the page has successfully reloaded when it goes back to 'embedder'
533 const base::string16 expected_title = ASCIIToUTF16("modified");
534 content::TitleWatcher title_watcher(test_embedder()->web_contents(),
537 ExecuteSyncJSFunction(rvh,
538 base::StringPrintf("SetTitle('%s');", "modified"));
540 base::string16 actual_title = title_watcher.WaitAndGetTitle();
541 EXPECT_EQ(expected_title, actual_title);
544 // Reload the embedder page, and verify that the reload was successful.
545 // Then navigate the guest to verify that the browser process does not crash.
547 const base::string16 expected_title = ASCIIToUTF16("embedder");
548 content::TitleWatcher title_watcher(test_embedder()->web_contents(),
551 test_embedder()->web_contents()->GetController().Reload(false);
552 base::string16 actual_title = title_watcher.WaitAndGetTitle();
553 EXPECT_EQ(expected_title, actual_title);
555 ExecuteSyncJSFunction(
556 test_embedder()->web_contents()->GetRenderViewHost(),
557 base::StringPrintf("SetSrc('%s');", kHTMLForGuest));
558 test_guest_manager()->WaitForGuestAdded();
560 const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map =
561 test_guest_manager()->guest_web_contents_for_testing();
562 WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
563 instance_map.begin()->second);
564 TestBrowserPluginGuest* new_test_guest =
565 static_cast<TestBrowserPluginGuest*>(
566 test_guest_web_contents->GetBrowserPluginGuest());
567 ASSERT_TRUE(new_test_guest != NULL);
569 // Wait for the guest to send an UpdateRectMsg, meaning it is ready.
570 new_test_guest->WaitForUpdateRectMsg();
574 // Always failing in the win7 try bot. See http://crbug.com/181107.
575 // Times out on the Mac. See http://crbug.com/297576.
576 #if defined(OS_WIN) || defined(OS_MACOSX)
577 #define MAYBE_AcceptDragEvents DISABLED_AcceptDragEvents
579 #define MAYBE_AcceptDragEvents AcceptDragEvents
582 // Tests that a drag-n-drop over the browser plugin in the embedder happens
584 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_AcceptDragEvents) {
585 const char kEmbedderURL[] = "/browser_plugin_dragging.html";
586 StartBrowserPluginTest(
587 kEmbedderURL, kHTMLForGuestAcceptDrag, true, std::string());
589 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
590 test_embedder()->web_contents()->GetRenderViewHost());
592 // Get a location in the embedder outside of the plugin.
593 base::ListValue *start, *end;
594 scoped_ptr<base::Value> value =
595 content::ExecuteScriptAndGetValue(rvh, "dragLocation()");
596 ASSERT_TRUE(value->GetAsList(&start) && start->GetSize() == 2);
597 double start_x, start_y;
598 ASSERT_TRUE(start->GetDouble(0, &start_x) && start->GetDouble(1, &start_y));
600 // Get a location in the embedder that falls inside the plugin.
601 value = content::ExecuteScriptAndGetValue(rvh, "dropLocation()");
602 ASSERT_TRUE(value->GetAsList(&end) && end->GetSize() == 2);
604 ASSERT_TRUE(end->GetDouble(0, &end_x) && end->GetDouble(1, &end_y));
607 GURL url = GURL("https://www.domain.com/index.html");
610 // Pretend that the URL is being dragged over the embedder. Start the drag
611 // from outside the plugin, then move the drag inside the plugin and drop.
612 // This should trigger appropriate messages from the embedder to the guest,
613 // and end with a drop on the guest. The guest changes title when a drop
615 const base::string16 expected_title = ASCIIToUTF16("DROPPED");
616 content::TitleWatcher title_watcher(test_guest()->web_contents(),
619 rvh->DragTargetDragEnter(drop_data, gfx::Point(start_x, start_y),
620 gfx::Point(start_x, start_y), blink::WebDragOperationEvery, 0);
621 rvh->DragTargetDragOver(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y),
622 blink::WebDragOperationEvery, 0);
623 rvh->DragTargetDrop(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y), 0);
625 base::string16 actual_title = title_watcher.WaitAndGetTitle();
626 EXPECT_EQ(expected_title, actual_title);
629 // This test verifies that round trip postMessage works as expected.
630 // 1. The embedder posts a message 'testing123' to the guest.
631 // 2. The guest receives and replies to the message using the event object's
632 // source object: event.source.postMessage('foobar', '*')
633 // 3. The embedder receives the message and uses the event's source
634 // object to do one final reply: 'stop'
635 // 4. The guest receives the final 'stop' message.
636 // 5. The guest acks the 'stop' message with a 'stop_ack' message.
637 // 6. The embedder changes its title to 'main guest' when it sees the 'stop_ack'
639 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, PostMessage) {
640 const char* kTesting = "testing123";
641 const char* kEmbedderURL = "/browser_plugin_embedder.html";
642 const char* kGuestURL = "/browser_plugin_post_message_guest.html";
643 StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
644 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
645 test_embedder()->web_contents()->GetRenderViewHost());
647 const base::string16 expected_title = ASCIIToUTF16("main guest");
648 content::TitleWatcher title_watcher(test_embedder()->web_contents(),
651 // By the time we get here 'contentWindow' should be ready because the
652 // guest has completed loading.
653 ExecuteSyncJSFunction(
654 rvh, base::StringPrintf("PostMessage('%s, false');", kTesting));
656 // The title will be updated to "main guest" at the last stage of the
657 // process described above.
658 base::string16 actual_title = title_watcher.WaitAndGetTitle();
659 EXPECT_EQ(expected_title, actual_title);
663 // This is the same as BrowserPluginHostTest.PostMessage but also
664 // posts a message to an iframe.
665 // TODO(fsamuel): This test should replace the previous test once postMessage
666 // iframe targeting is fixed (see http://crbug.com/153701).
667 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DISABLED_PostMessageToIFrame) {
668 const char* kTesting = "testing123";
669 const char* kEmbedderURL = "/browser_plugin_embedder.html";
670 const char* kGuestURL = "/browser_plugin_post_message_guest.html";
671 StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
672 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
673 test_embedder()->web_contents()->GetRenderViewHost());
675 const base::string16 expected_title = ASCIIToUTF16("main guest");
676 content::TitleWatcher title_watcher(test_embedder()->web_contents(),
679 ExecuteSyncJSFunction(
680 rvh, base::StringPrintf("PostMessage('%s, false');", kTesting));
682 // The title will be updated to "main guest" at the last stage of the
683 // process described above.
684 base::string16 actual_title = title_watcher.WaitAndGetTitle();
685 EXPECT_EQ(expected_title, actual_title);
688 content::TitleWatcher ready_watcher(test_embedder()->web_contents(),
689 ASCIIToUTF16("ready"));
691 RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
692 test_guest()->web_contents()->GetRenderViewHost());
693 GURL test_url = embedded_test_server()->GetURL(
694 "/browser_plugin_post_message_guest.html");
695 ExecuteSyncJSFunction(
698 "CreateChildFrame('%s');", test_url.spec().c_str()));
700 base::string16 actual_title = ready_watcher.WaitAndGetTitle();
701 EXPECT_EQ(ASCIIToUTF16("ready"), actual_title);
703 content::TitleWatcher iframe_watcher(test_embedder()->web_contents(),
704 ASCIIToUTF16("iframe"));
705 ExecuteSyncJSFunction(
706 rvh, base::StringPrintf("PostMessage('%s', true);", kTesting));
708 // The title will be updated to "iframe" at the last stage of the
709 // process described above.
710 actual_title = iframe_watcher.WaitAndGetTitle();
711 EXPECT_EQ(ASCIIToUTF16("iframe"), actual_title);
715 // This test verifies that if a browser plugin is hidden before navigation,
716 // the guest starts off hidden.
717 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, HiddenBeforeNavigation) {
718 const char* kEmbedderURL = "/browser_plugin_embedder.html";
719 const std::string embedder_code =
720 "document.getElementById('plugin').style.visibility = 'hidden'";
721 StartBrowserPluginTest(
722 kEmbedderURL, kHTMLForGuest, true, embedder_code);
723 EXPECT_FALSE(test_guest()->visible());
726 // This test verifies that if a browser plugin is focused before navigation then
727 // the guest starts off focused.
728 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusBeforeNavigation) {
729 const char* kEmbedderURL = "/browser_plugin_embedder.html";
730 const std::string embedder_code =
731 "document.getElementById('plugin').focus();";
732 StartBrowserPluginTest(
733 kEmbedderURL, kHTMLForGuest, true, embedder_code);
734 RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
735 test_guest()->web_contents()->GetRenderViewHost());
736 // Verify that the guest is focused.
737 scoped_ptr<base::Value> value =
738 content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()");
740 ASSERT_TRUE(value->GetAsBoolean(&result));
744 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusTracksEmbedder) {
745 const char* kEmbedderURL = "/browser_plugin_embedder.html";
746 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
747 // Blur the embedder.
748 test_embedder()->web_contents()->GetRenderViewHost()->Blur();
749 // Ensure that the guest is also blurred.
750 test_guest()->WaitForBlur();
753 // Test for regression http://crbug.com/162961.
754 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, GetRenderViewHostAtPositionTest) {
755 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
756 const std::string embedder_code =
757 base::StringPrintf("SetSize(%d, %d);", 100, 100);
758 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuestWithSize, true,
760 // Check for render view host at position (150, 150) that is outside the
761 // bounds of our guest, so this would respond with the render view host of the
763 test_embedder()->WaitForRenderViewHostAtPosition(150, 150);
764 ASSERT_EQ(test_embedder()->web_contents()->GetRenderViewHost(),
765 test_embedder()->last_rvh_at_position_response());
768 // This test verifies that if IME is enabled in the embedder, it is also enabled
770 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, VerifyInputMethodActive) {
771 const char* kEmbedderURL = "/browser_plugin_embedder.html";
772 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
773 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
774 test_guest()->web_contents()->GetRenderViewHost());
775 EXPECT_TRUE(rvh->input_method_active());
778 // Verify that navigating to an invalid URL (e.g. 'http:') doesn't cause
780 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DoNotCrashOnInvalidNavigation) {
781 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
782 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
783 TestBrowserPluginGuestDelegate* delegate =
784 new TestBrowserPluginGuestDelegate();
785 test_guest()->SetDelegate(delegate);
787 const char kValidSchemeWithEmptyURL[] = "http:";
788 ExecuteSyncJSFunction(
789 test_embedder()->web_contents()->GetRenderViewHost(),
790 base::StringPrintf("SetSrc('%s');", kValidSchemeWithEmptyURL));
791 EXPECT_TRUE(delegate->load_aborted());
792 EXPECT_FALSE(delegate->load_aborted_url().is_valid());
793 EXPECT_EQ(kValidSchemeWithEmptyURL,
794 delegate->load_aborted_url().possibly_invalid_spec());
796 delegate->ResetStates();
798 // Attempt a navigation to chrome-guest://abc123, which is a valid URL. But it
799 // should be blocked because the scheme isn't web-safe or a pseudo-scheme.
800 ExecuteSyncJSFunction(
801 test_embedder()->web_contents()->GetRenderViewHost(),
802 base::StringPrintf("SetSrc('%s://abc123');", kGuestScheme));
803 EXPECT_TRUE(delegate->load_aborted());
804 EXPECT_TRUE(delegate->load_aborted_url().is_valid());
807 // Tests involving the threaded compositor.
808 class BrowserPluginThreadedCompositorTest : public BrowserPluginHostTest {
810 BrowserPluginThreadedCompositorTest() {}
811 virtual ~BrowserPluginThreadedCompositorTest() {}
814 virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE {
815 BrowserPluginHostTest::SetUpCommandLine(cmd);
816 cmd->AppendSwitch(switches::kEnableThreadedCompositing);
820 class BrowserPluginThreadedCompositorPixelTest
821 : public BrowserPluginThreadedCompositorTest {
823 virtual void SetUp() OVERRIDE {
825 BrowserPluginThreadedCompositorTest::SetUp();
828 virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE {
829 BrowserPluginThreadedCompositorTest::SetUpCommandLine(cmd);
830 // http://crbug.com/327035
831 cmd->AppendSwitch(switches::kDisableDelegatedRenderer);
835 static void CompareSkBitmaps(const SkBitmap& expected_bitmap,
836 const SkBitmap& bitmap) {
837 EXPECT_EQ(expected_bitmap.width(), bitmap.width());
838 if (expected_bitmap.width() != bitmap.width())
840 EXPECT_EQ(expected_bitmap.height(), bitmap.height());
841 if (expected_bitmap.height() != bitmap.height())
843 EXPECT_EQ(expected_bitmap.config(), bitmap.config());
844 if (expected_bitmap.config() != bitmap.config())
847 SkAutoLockPixels expected_bitmap_lock(expected_bitmap);
848 SkAutoLockPixels bitmap_lock(bitmap);
850 const int kAllowableError = 2;
851 for (int i = 0; i < bitmap.width() && fails < 10; ++i) {
852 for (int j = 0; j < bitmap.height() && fails < 10; ++j) {
853 SkColor expected_color = expected_bitmap.getColor(i, j);
854 SkColor color = bitmap.getColor(i, j);
855 int expected_alpha = SkColorGetA(expected_color);
856 int alpha = SkColorGetA(color);
857 int expected_red = SkColorGetR(expected_color);
858 int red = SkColorGetR(color);
859 int expected_green = SkColorGetG(expected_color);
860 int green = SkColorGetG(color);
861 int expected_blue = SkColorGetB(expected_color);
862 int blue = SkColorGetB(color);
863 EXPECT_NEAR(expected_alpha, alpha, kAllowableError)
864 << "expected_color: " << std::hex << expected_color
865 << " color: " << color
866 << " Failed at " << std::dec << i << ", " << j
867 << " Failure " << ++fails;
868 EXPECT_NEAR(expected_red, red, kAllowableError)
869 << "expected_color: " << std::hex << expected_color
870 << " color: " << color
871 << " Failed at " << std::dec << i << ", " << j
872 << " Failure " << ++fails;
873 EXPECT_NEAR(expected_green, green, kAllowableError)
874 << "expected_color: " << std::hex << expected_color
875 << " color: " << color
876 << " Failed at " << std::dec << i << ", " << j
877 << " Failure " << ++fails;
878 EXPECT_NEAR(expected_blue, blue, kAllowableError)
879 << "expected_color: " << std::hex << expected_color
880 << " color: " << color
881 << " Failed at " << std::dec << i << ", " << j
882 << " Failure " << ++fails;
885 EXPECT_LT(fails, 10);
888 static void CompareSkBitmapAndRun(const base::Closure& callback,
889 const SkBitmap& expected_bitmap,
892 const SkBitmap& bitmap) {
895 CompareSkBitmaps(expected_bitmap, bitmap);
899 // http://crbug.com/171744
900 #if defined(OS_MACOSX)
901 #define MAYBE_GetBackingStore DISABLED_GetBackingStore
903 #define MAYBE_GetBackingStore GetBackingStore
905 IN_PROC_BROWSER_TEST_F(BrowserPluginThreadedCompositorPixelTest,
906 MAYBE_GetBackingStore) {
907 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
908 const char kHTMLForGuest[] =
909 "data:text/html,<html><style>body { background-color: red; }</style>"
910 "<body></body></html>";
911 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true,
912 std::string("SetSize(50, 60);"));
914 WebContentsImpl* guest_contents = test_guest()->web_contents();
915 RenderWidgetHostImpl* guest_widget_host =
916 RenderWidgetHostImpl::From(guest_contents->GetRenderViewHost());
918 SkBitmap expected_bitmap;
919 expected_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 50, 60);
920 expected_bitmap.allocPixels();
921 expected_bitmap.eraseARGB(255, 255, 0, 0); // #f00
925 guest_widget_host->CopyFromBackingStore(gfx::Rect(),
926 guest_widget_host->GetView()->GetViewBounds().size(),
927 base::Bind(&CompareSkBitmapAndRun, loop.QuitClosure(), expected_bitmap,
933 // This test exercises the following scenario:
934 // 1. An <input> in guest has focus.
935 // 2. User takes focus to embedder by clicking e.g. an <input> in embedder.
936 // 3. User brings back the focus directly to the <input> in #1.
938 // Now we need to make sure TextInputTypeChange fires properly for the guest's
939 // view (RenderWidgetHostViewGuest) upon step #3. This test ensures that,
940 // otherwise IME doesn't work after step #3.
941 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusRestored) {
942 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
943 const char kGuestHTML[] = "data:text/html,"
944 "<html><body><input id=\"input1\"></body>"
946 "var i = document.getElementById(\"input1\");"
947 "document.body.addEventListener(\"click\", function(e) {"
950 "i.addEventListener(\"focus\", function(e) {"
951 " document.title = \"FOCUS\";"
953 "i.addEventListener(\"blur\", function(e) {"
954 " document.title = \"BLUR\";"
958 StartBrowserPluginTest(kEmbedderURL, kGuestHTML, true,
959 "document.getElementById(\"plugin\").focus();");
961 ASSERT_TRUE(test_embedder());
962 const char *kTitles[3] = {"FOCUS", "BLUR", "FOCUS"};
963 gfx::Point kClickPoints[3] = {
964 gfx::Point(20, 20), gfx::Point(700, 20), gfx::Point(20, 20)
967 for (int i = 0; i < 3; ++i) {
968 base::string16 expected_title = base::UTF8ToUTF16(kTitles[i]);
969 content::TitleWatcher title_watcher(test_guest()->web_contents(),
971 SimulateMouseClickAt(test_embedder()->web_contents(), 0,
972 blink::WebMouseEvent::ButtonLeft,
974 base::string16 actual_title = title_watcher.WaitAndGetTitle();
975 EXPECT_EQ(expected_title, actual_title);
977 TestBrowserPluginGuest* guest = test_guest();
979 ui::TextInputType text_input_type = guest->last_text_input_type();
980 ASSERT_TRUE(text_input_type != ui::TEXT_INPUT_TYPE_NONE);
983 // Tests input method.
984 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, InputMethod) {
985 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
986 const char kGuestHTML[] = "data:text/html,"
987 "<html><body><input id=\"input1\">"
988 "<input id=\"input2\"></body>"
990 "var i = document.getElementById(\"input1\");"
991 "i.oninput = function() {"
992 " document.title = i.value;"
996 StartBrowserPluginTest(kEmbedderURL, kGuestHTML, true,
997 "document.getElementById(\"plugin\").focus();");
999 RenderViewHostImpl* embedder_rvh = static_cast<RenderViewHostImpl*>(
1000 test_embedder()->web_contents()->GetRenderViewHost());
1001 RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
1002 test_guest()->web_contents()->GetRenderViewHost());
1004 std::vector<blink::WebCompositionUnderline> underlines;
1006 // An input field in browser plugin guest gets focus and given some user
1009 ExecuteSyncJSFunction(guest_rvh,
1010 "document.getElementById('input1').focus();");
1011 base::string16 expected_title = base::UTF8ToUTF16("InputTest123");
1012 content::TitleWatcher title_watcher(test_guest()->web_contents(),
1015 new ViewMsg_ImeSetComposition(
1016 test_embedder()->web_contents()->GetRoutingID(),
1020 base::string16 actual_title = title_watcher.WaitAndGetTitle();
1021 EXPECT_EQ(expected_title, actual_title);
1023 // A composition is committed via IME.
1025 base::string16 expected_title = base::UTF8ToUTF16("InputTest456");
1026 content::TitleWatcher title_watcher(test_guest()->web_contents(),
1029 new ViewMsg_ImeConfirmComposition(
1030 test_embedder()->web_contents()->GetRoutingID(),
1034 base::string16 actual_title = title_watcher.WaitAndGetTitle();
1035 EXPECT_EQ(expected_title, actual_title);
1037 // IME composition starts, but focus moves out, then the composition will
1038 // be committed and get cancel msg.
1040 ExecuteSyncJSFunction(guest_rvh,
1041 "document.getElementById('input1').value = '';");
1042 base::string16 composition = base::UTF8ToUTF16("InputTest789");
1043 content::TitleWatcher title_watcher(test_guest()->web_contents(),
1046 new ViewMsg_ImeSetComposition(
1047 test_embedder()->web_contents()->GetRoutingID(),
1051 base::string16 actual_title = title_watcher.WaitAndGetTitle();
1052 EXPECT_EQ(composition, actual_title);
1053 // Moving focus causes IME cancel, and the composition will be committed
1054 // in input1, not in input2.
1055 ExecuteSyncJSFunction(guest_rvh,
1056 "document.getElementById('input2').focus();");
1057 test_guest()->WaitForImeCancel();
1058 scoped_ptr<base::Value> value =
1059 content::ExecuteScriptAndGetValue(
1060 guest_rvh, "document.getElementById('input1').value");
1062 ASSERT_TRUE(value->GetAsString(&result));
1063 EXPECT_EQ(base::UTF16ToUTF8(composition), result);
1065 // Tests ExtendSelectionAndDelete message works in browser_plugin.
1067 // Set 'InputTestABC' in input1 and put caret at 6 (after 'T').
1068 ExecuteSyncJSFunction(guest_rvh,
1069 "var i = document.getElementById('input1');"
1071 "i.value = 'InputTestABC';"
1072 "i.selectionStart=6;"
1073 "i.selectionEnd=6;");
1074 base::string16 expected_value = base::UTF8ToUTF16("InputABC");
1075 content::TitleWatcher title_watcher(test_guest()->web_contents(),
1077 // Delete 'Test' in 'InputTestABC', as the caret is after 'T':
1078 // delete before 1 character ('T') and after 3 characters ('est').
1080 new ViewMsg_ExtendSelectionAndDelete(
1081 test_embedder()->web_contents()->GetRoutingID(),
1083 base::string16 actual_title = title_watcher.WaitAndGetTitle();
1084 EXPECT_EQ(expected_value, actual_title);
1085 scoped_ptr<base::Value> value =
1086 content::ExecuteScriptAndGetValue(
1087 guest_rvh, "document.getElementById('input1').value");
1088 std::string actual_value;
1089 ASSERT_TRUE(value->GetAsString(&actual_value));
1090 EXPECT_EQ(base::UTF16ToUTF8(expected_value), actual_value);
1094 } // namespace content