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_embedder.h"
13 #include "content/browser/browser_plugin/browser_plugin_guest.h"
14 #include "content/browser/browser_plugin/browser_plugin_host_factory.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/browser_plugin/test_guest_manager_delegate.h"
19 #include "content/browser/child_process_security_policy_impl.h"
20 #include "content/browser/frame_host/render_frame_host_impl.h"
21 #include "content/browser/frame_host/render_widget_host_view_guest.h"
22 #include "content/browser/renderer_host/render_view_host_impl.h"
23 #include "content/browser/web_contents/web_contents_impl.h"
24 #include "content/common/browser_plugin/browser_plugin_messages.h"
25 #include "content/common/view_messages.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/browser/notification_types.h"
28 #include "content/public/browser/render_frame_host.h"
29 #include "content/public/browser/render_widget_host_view.h"
30 #include "content/public/browser/web_contents_observer.h"
31 #include "content/public/common/content_switches.h"
32 #include "content/public/common/drop_data.h"
33 #include "content/public/common/url_constants.h"
34 #include "content/public/test/browser_test_utils.h"
35 #include "content/public/test/content_browser_test.h"
36 #include "content/public/test/content_browser_test_utils.h"
37 #include "content/public/test/test_utils.h"
38 #include "content/shell/browser/shell.h"
39 #include "content/shell/browser/shell_browser_context.h"
40 #include "net/base/net_util.h"
41 #include "net/test/embedded_test_server/embedded_test_server.h"
42 #include "net/test/embedded_test_server/http_request.h"
43 #include "net/test/embedded_test_server/http_response.h"
44 #include "net/test/spawned_test_server/spawned_test_server.h"
45 #include "third_party/WebKit/public/web/WebInputEvent.h"
47 using base::ASCIIToUTF16;
48 using blink::WebInputEvent;
49 using blink::WebMouseEvent;
50 using content::BrowserPluginEmbedder;
51 using content::BrowserPluginGuest;
52 using content::BrowserPluginHostFactory;
53 using content::WebContentsImpl;
55 const char kHTMLForGuest[] =
56 "data:text/html,<html><body>hello world</body></html>";
58 const char kHTMLForGuestTouchHandler[] =
59 "data:text/html,<html><body><div id=\"touch\">With touch</div></body>"
60 "<script type=\"text/javascript\">"
61 "function handler() {}"
62 "function InstallTouchHandler() { "
63 " document.getElementById(\"touch\").addEventListener(\"touchstart\", "
66 "function UninstallTouchHandler() { "
67 " document.getElementById(\"touch\").removeEventListener(\"touchstart\", "
72 const char kHTMLForGuestAcceptDrag[] =
73 "data:text/html,<html><body>"
75 "function dropped() {"
76 " document.title = \"DROPPED\";"
79 "<textarea id=\"text\" style=\"width:100%; height: 100%\""
80 " ondrop=\"dropped();\">"
86 class TestBrowserPluginEmbedder : public BrowserPluginEmbedder {
88 TestBrowserPluginEmbedder(WebContentsImpl* web_contents)
89 : BrowserPluginEmbedder(web_contents) {
92 virtual ~TestBrowserPluginEmbedder() {}
94 WebContentsImpl* web_contents() const {
95 return static_cast<WebContentsImpl*>(BrowserPluginEmbedder::web_contents());
99 DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginEmbedder);
102 // Test factory for creating test instances of BrowserPluginEmbedder and
103 // BrowserPluginGuest.
104 class TestBrowserPluginHostFactory : public BrowserPluginHostFactory {
106 virtual BrowserPluginGuestManager*
107 CreateBrowserPluginGuestManager(BrowserContext* context) OVERRIDE {
108 guest_manager_instance_count_++;
109 if (message_loop_runner_)
110 message_loop_runner_->Quit();
111 return new TestBrowserPluginGuestManager(context);
114 virtual BrowserPluginGuest* CreateBrowserPluginGuest(
116 WebContentsImpl* web_contents) OVERRIDE {
117 return new TestBrowserPluginGuest(instance_id, web_contents);
120 // Also keeps track of number of instances created.
121 virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder(
122 WebContentsImpl* web_contents) OVERRIDE {
124 return new TestBrowserPluginEmbedder(web_contents);
128 static TestBrowserPluginHostFactory* GetInstance() {
129 return Singleton<TestBrowserPluginHostFactory>::get();
132 // Waits for at least one embedder to be created in the test. Returns true if
133 // we have a guest, false if waiting times out.
134 void WaitForGuestManagerCreation() {
135 // Check if already have created an instance.
136 if (guest_manager_instance_count_ > 0)
139 message_loop_runner_ = new MessageLoopRunner();
140 message_loop_runner_->Run();
144 TestBrowserPluginHostFactory() : guest_manager_instance_count_(0) {}
145 virtual ~TestBrowserPluginHostFactory() {}
149 friend struct DefaultSingletonTraits<TestBrowserPluginHostFactory>;
151 scoped_refptr<MessageLoopRunner> message_loop_runner_;
152 int guest_manager_instance_count_;
154 DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginHostFactory);
157 // Test factory class for browser plugin that creates guests with short hang
159 class TestShortHangTimeoutGuestFactory : public TestBrowserPluginHostFactory {
161 virtual BrowserPluginGuest* CreateBrowserPluginGuest(
162 int instance_id, WebContentsImpl* web_contents) OVERRIDE {
163 TestBrowserPluginGuest* guest =
164 new TestBrowserPluginGuest(instance_id, web_contents);
165 guest->set_guest_hang_timeout(TestTimeouts::tiny_timeout());
170 static TestShortHangTimeoutGuestFactory* GetInstance() {
171 return Singleton<TestShortHangTimeoutGuestFactory>::get();
175 TestShortHangTimeoutGuestFactory() {}
176 virtual ~TestShortHangTimeoutGuestFactory() {}
180 friend struct DefaultSingletonTraits<TestShortHangTimeoutGuestFactory>;
182 DISALLOW_COPY_AND_ASSIGN(TestShortHangTimeoutGuestFactory);
185 // A transparent observer that can be used to verify that a RenderViewHost
186 // received a specific message.
187 class MessageObserver : public WebContentsObserver {
189 MessageObserver(WebContents* web_contents, uint32 message_id)
190 : WebContentsObserver(web_contents),
191 message_id_(message_id),
192 message_received_(false) {
195 virtual ~MessageObserver() {}
197 void WaitUntilMessageReceived() {
198 if (message_received_)
200 message_loop_runner_ = new MessageLoopRunner();
201 message_loop_runner_->Run();
205 message_received_ = false;
208 // IPC::Listener implementation.
209 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
210 if (message.type() == message_id_) {
211 message_received_ = true;
212 if (message_loop_runner_)
213 message_loop_runner_->Quit();
219 scoped_refptr<MessageLoopRunner> message_loop_runner_;
221 bool message_received_;
223 DISALLOW_COPY_AND_ASSIGN(MessageObserver);
226 class BrowserPluginHostTest : public ContentBrowserTest {
228 BrowserPluginHostTest()
229 : test_embedder_(NULL),
231 test_guest_manager_(NULL) {}
233 virtual void SetUp() OVERRIDE {
234 // Override factory to create tests instances of BrowserPlugin*.
235 BrowserPluginEmbedder::set_factory_for_testing(
236 TestBrowserPluginHostFactory::GetInstance());
237 BrowserPluginGuest::set_factory_for_testing(
238 TestBrowserPluginHostFactory::GetInstance());
239 BrowserPluginGuestManager::set_factory_for_testing(
240 TestBrowserPluginHostFactory::GetInstance());
241 ContentBrowserTest::SetUp();
243 virtual void TearDown() OVERRIDE {
244 BrowserPluginEmbedder::set_factory_for_testing(NULL);
245 BrowserPluginGuest::set_factory_for_testing(NULL);
247 ContentBrowserTest::TearDown();
250 static void SimulateSpaceKeyPress(WebContents* web_contents) {
251 SimulateKeyPress(web_contents,
259 static void SimulateTabKeyPress(WebContents* web_contents) {
260 SimulateKeyPress(web_contents,
268 // Executes the JavaScript synchronously and makes sure the returned value is
270 void ExecuteSyncJSFunction(RenderFrameHost* rfh, const std::string& jscript) {
271 scoped_ptr<base::Value> value =
272 content::ExecuteScriptAndGetValue(rfh, jscript);
275 // This helper method does the following:
276 // 1. Start the test server and navigate the shell to |embedder_url|.
277 // 2. Execute custom pre-navigation |embedder_code| if provided.
278 // 3. Navigate the guest to the |guest_url|.
279 // 4. Verify that the guest has been created and has completed loading.
280 void StartBrowserPluginTest(const std::string& embedder_url,
281 const std::string& guest_url,
282 bool is_guest_data_url,
283 const std::string& embedder_code) {
284 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
285 GURL test_url(embedded_test_server()->GetURL(embedder_url));
286 NavigateToURL(shell(), test_url);
288 WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>(
289 shell()->web_contents());
290 static_cast<ShellBrowserContext*>(
291 embedder_web_contents->GetBrowserContext())->
292 set_guest_manager_delegate_for_testing(
293 TestGuestManagerDelegate::GetInstance());
294 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
295 embedder_web_contents->GetRenderViewHost());
296 RenderFrameHost* rfh = embedder_web_contents->GetMainFrame();
297 // Focus the embedder.
300 rvh->SetInputMethodActive(true);
302 // Allow the test to do some operations on the embedder before we perform
303 // the first navigation of the guest.
304 if (!embedder_code.empty())
305 ExecuteSyncJSFunction(rfh, embedder_code);
307 if (!is_guest_data_url) {
308 test_url = embedded_test_server()->GetURL(guest_url);
309 ExecuteSyncJSFunction(
310 rfh, base::StringPrintf("SetSrc('%s');", test_url.spec().c_str()));
312 ExecuteSyncJSFunction(
313 rfh, base::StringPrintf("SetSrc('%s');", guest_url.c_str()));
316 // Wait to make sure embedder is created/attached to WebContents.
317 TestBrowserPluginHostFactory::GetInstance()->WaitForGuestManagerCreation();
319 test_embedder_ = static_cast<TestBrowserPluginEmbedder*>(
320 embedder_web_contents->GetBrowserPluginEmbedder());
321 ASSERT_TRUE(test_embedder_);
323 test_guest_manager_ = static_cast<TestBrowserPluginGuestManager*>(
324 BrowserPluginGuestManager::FromBrowserContext(
325 test_embedder_->GetWebContents()->GetBrowserContext()));
326 ASSERT_TRUE(test_guest_manager_);
328 WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
329 test_guest_manager_->WaitForGuestAdded());
331 test_guest_ = static_cast<TestBrowserPluginGuest*>(
332 test_guest_web_contents->GetBrowserPluginGuest());
333 test_guest_->WaitForLoadStop();
336 TestBrowserPluginEmbedder* test_embedder() const { return test_embedder_; }
337 TestBrowserPluginGuest* test_guest() const { return test_guest_; }
338 TestBrowserPluginGuestManager* test_guest_manager() const {
339 return test_guest_manager_;
343 TestBrowserPluginEmbedder* test_embedder_;
344 TestBrowserPluginGuest* test_guest_;
345 TestBrowserPluginGuestManager* test_guest_manager_;
346 DISALLOW_COPY_AND_ASSIGN(BrowserPluginHostTest);
349 // This test ensures that if guest isn't there and we resize the guest (from
350 // js), it remembers the size correctly.
352 // Initially we load an embedder with a guest without a src attribute (which has
353 // dimension 640x480), resize it to 100x200, and then we set the source to a
354 // sample guest. In the end we verify that the correct size has been set.
355 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, NavigateAfterResize) {
356 const gfx::Size nxt_size = gfx::Size(100, 200);
357 const std::string embedder_code = base::StringPrintf(
358 "SetSize(%d, %d);", nxt_size.width(), nxt_size.height());
359 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
360 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, embedder_code);
362 // Wait for the guest to be resized to 100x200.
363 test_guest()->WaitForResizeGuest(nxt_size);
366 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AdvanceFocus) {
367 const char kEmbedderURL[] = "/browser_plugin_focus.html";
368 const char* kGuestURL = "/browser_plugin_focus_child.html";
369 StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
371 SimulateMouseClick(test_embedder()->web_contents(), 0,
372 blink::WebMouseEvent::ButtonLeft);
373 BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
374 // Wait until we focus into the guest.
375 test_guest()->WaitForFocus();
377 // TODO(fsamuel): A third Tab key press should not be necessary.
378 // The browser plugin will take keyboard focus but it will not
379 // focus an initial element. The initial element is dependent
380 // upon tab direction which WebKit does not propagate to the plugin.
381 // See http://crbug.com/147644.
382 BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
383 BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
384 BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
385 test_guest()->WaitForAdvanceFocus();
388 // This test opens a page in http and then opens another page in https, forcing
389 // a RenderViewHost swap in the web_contents. We verify that the embedder in the
390 // web_contents gets cleared properly.
391 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderChangedAfterSwap) {
392 net::SpawnedTestServer https_server(
393 net::SpawnedTestServer::TYPE_HTTPS,
394 net::SpawnedTestServer::kLocalhost,
395 base::FilePath(FILE_PATH_LITERAL("content/test/data")));
396 ASSERT_TRUE(https_server.Start());
398 // 1. Load an embedder page with one guest in it.
399 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
400 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
402 // 2. Navigate to a URL in https, so we trigger a RenderViewHost swap.
403 GURL test_https_url(https_server.GetURL(
404 "files/browser_plugin_title_change.html"));
405 content::WindowedNotificationObserver swap_observer(
406 content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
407 content::Source<WebContents>(test_embedder()->web_contents()));
408 NavigateToURL(shell(), test_https_url);
409 swap_observer.Wait();
411 TestBrowserPluginEmbedder* test_embedder_after_swap =
412 static_cast<TestBrowserPluginEmbedder*>(
413 static_cast<WebContentsImpl*>(shell()->web_contents())->
414 GetBrowserPluginEmbedder());
415 // Verify we have a no embedder in web_contents (since the new page doesn't
416 // have any browser plugin).
417 ASSERT_TRUE(!test_embedder_after_swap);
418 ASSERT_NE(test_embedder(), test_embedder_after_swap);
421 // This test opens two pages in http and there is no RenderViewHost swap,
422 // therefore the embedder created on first page navigation stays the same in
424 // Failing flakily on Windows: crbug.com/308405
426 #define MAYBE_EmbedderSameAfterNav DISABLED_EmbedderSameAfterNav
428 #define MAYBE_EmbedderSameAfterNav EmbedderSameAfterNav
430 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_EmbedderSameAfterNav) {
431 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
432 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
433 WebContentsImpl* embedder_web_contents = test_embedder()->web_contents();
435 // Navigate to another page in same host and port, so RenderViewHost swap
436 // does not happen and existing embedder doesn't change in web_contents.
437 GURL test_url_new(embedded_test_server()->GetURL(
438 "/browser_plugin_title_change.html"));
439 const base::string16 expected_title = ASCIIToUTF16("done");
440 content::TitleWatcher title_watcher(shell()->web_contents(), expected_title);
441 NavigateToURL(shell(), test_url_new);
442 VLOG(0) << "Start waiting for title";
443 base::string16 actual_title = title_watcher.WaitAndGetTitle();
444 EXPECT_EQ(expected_title, actual_title);
445 VLOG(0) << "Done navigating to second page";
447 TestBrowserPluginEmbedder* test_embedder_after_nav =
448 static_cast<TestBrowserPluginEmbedder*>(
449 embedder_web_contents->GetBrowserPluginEmbedder());
450 // Embedder must not change in web_contents.
451 ASSERT_EQ(test_embedder_after_nav, test_embedder());
454 // Verifies that installing/uninstalling touch-event handlers in the guest
455 // plugin correctly updates the touch-event handling state in the embedder.
456 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AcceptTouchEvents) {
457 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
458 StartBrowserPluginTest(
459 kEmbedderURL, kHTMLForGuestTouchHandler, true, std::string());
461 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
462 test_embedder()->web_contents()->GetRenderViewHost());
463 // The embedder should not have any touch event handlers at this point.
464 EXPECT_FALSE(rvh->has_touch_handler());
466 // Install the touch handler in the guest. This should cause the embedder to
467 // start listening for touch events too.
468 MessageObserver observer(test_embedder()->web_contents(),
469 ViewHostMsg_HasTouchEventHandlers::ID);
470 ExecuteSyncJSFunction(test_guest()->web_contents()->GetMainFrame(),
471 "InstallTouchHandler();");
472 observer.WaitUntilMessageReceived();
473 EXPECT_TRUE(rvh->has_touch_handler());
475 // Uninstalling the touch-handler in guest should cause the embedder to stop
476 // listening for touch events.
477 observer.ResetState();
478 ExecuteSyncJSFunction(test_guest()->web_contents()->GetMainFrame(),
479 "UninstallTouchHandler();");
480 observer.WaitUntilMessageReceived();
481 EXPECT_FALSE(rvh->has_touch_handler());
484 // This tests verifies that reloading the embedder does not crash the browser
485 // and that the guest is reset.
486 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DISABLED_ReloadEmbedder) {
487 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
488 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
489 RenderFrameHost* rfh = test_embedder()->web_contents()->GetMainFrame();
491 // Change the title of the page to 'modified' so that we know that
492 // the page has successfully reloaded when it goes back to 'embedder'
495 const base::string16 expected_title = ASCIIToUTF16("modified");
496 content::TitleWatcher title_watcher(test_embedder()->web_contents(),
499 ExecuteSyncJSFunction(rfh,
500 base::StringPrintf("SetTitle('%s');", "modified"));
502 base::string16 actual_title = title_watcher.WaitAndGetTitle();
503 EXPECT_EQ(expected_title, actual_title);
506 // Reload the embedder page, and verify that the reload was successful.
507 // Then navigate the guest to verify that the browser process does not crash.
509 const base::string16 expected_title = ASCIIToUTF16("embedder");
510 content::TitleWatcher title_watcher(test_embedder()->web_contents(),
513 test_embedder()->web_contents()->GetController().Reload(false);
514 base::string16 actual_title = title_watcher.WaitAndGetTitle();
515 EXPECT_EQ(expected_title, actual_title);
517 ExecuteSyncJSFunction(
518 test_embedder()->web_contents()->GetMainFrame(),
519 base::StringPrintf("SetSrc('%s');", kHTMLForGuest));
521 WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
522 test_guest_manager()->WaitForGuestAdded());
523 TestBrowserPluginGuest* new_test_guest =
524 static_cast<TestBrowserPluginGuest*>(
525 test_guest_web_contents->GetBrowserPluginGuest());
526 ASSERT_TRUE(new_test_guest != NULL);
528 // Wait for the guest to send an UpdateRectMsg, meaning it is ready.
529 new_test_guest->WaitForUpdateRectMsg();
533 // Always failing in the win7 try bot. See http://crbug.com/181107.
534 // Times out on the Mac. See http://crbug.com/297576.
535 #if defined(OS_WIN) || defined(OS_MACOSX)
536 #define MAYBE_AcceptDragEvents DISABLED_AcceptDragEvents
538 #define MAYBE_AcceptDragEvents AcceptDragEvents
541 // Tests that a drag-n-drop over the browser plugin in the embedder happens
543 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_AcceptDragEvents) {
544 const char kEmbedderURL[] = "/browser_plugin_dragging.html";
545 StartBrowserPluginTest(
546 kEmbedderURL, kHTMLForGuestAcceptDrag, true, std::string());
548 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
549 test_embedder()->web_contents()->GetRenderViewHost());
550 RenderFrameHost* rfh = test_embedder()->web_contents()->GetMainFrame();
552 // Get a location in the embedder outside of the plugin.
553 base::ListValue *start, *end;
554 scoped_ptr<base::Value> value =
555 content::ExecuteScriptAndGetValue(rfh, "dragLocation()");
556 ASSERT_TRUE(value->GetAsList(&start) && start->GetSize() == 2);
557 double start_x, start_y;
558 ASSERT_TRUE(start->GetDouble(0, &start_x) && start->GetDouble(1, &start_y));
560 // Get a location in the embedder that falls inside the plugin.
561 value = content::ExecuteScriptAndGetValue(rfh, "dropLocation()");
562 ASSERT_TRUE(value->GetAsList(&end) && end->GetSize() == 2);
564 ASSERT_TRUE(end->GetDouble(0, &end_x) && end->GetDouble(1, &end_y));
567 GURL url = GURL("https://www.domain.com/index.html");
570 // Pretend that the URL is being dragged over the embedder. Start the drag
571 // from outside the plugin, then move the drag inside the plugin and drop.
572 // This should trigger appropriate messages from the embedder to the guest,
573 // and end with a drop on the guest. The guest changes title when a drop
575 const base::string16 expected_title = ASCIIToUTF16("DROPPED");
576 content::TitleWatcher title_watcher(test_guest()->web_contents(),
579 rvh->DragTargetDragEnter(drop_data, gfx::Point(start_x, start_y),
580 gfx::Point(start_x, start_y), blink::WebDragOperationEvery, 0);
581 rvh->DragTargetDragOver(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y),
582 blink::WebDragOperationEvery, 0);
583 rvh->DragTargetDrop(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y), 0);
585 base::string16 actual_title = title_watcher.WaitAndGetTitle();
586 EXPECT_EQ(expected_title, actual_title);
589 // This test verifies that round trip postMessage works as expected.
590 // 1. The embedder posts a message 'testing123' to the guest.
591 // 2. The guest receives and replies to the message using the event object's
592 // source object: event.source.postMessage('foobar', '*')
593 // 3. The embedder receives the message and uses the event's source
594 // object to do one final reply: 'stop'
595 // 4. The guest receives the final 'stop' message.
596 // 5. The guest acks the 'stop' message with a 'stop_ack' message.
597 // 6. The embedder changes its title to 'main guest' when it sees the 'stop_ack'
599 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, PostMessage) {
600 const char* kTesting = "testing123";
601 const char* kEmbedderURL = "/browser_plugin_embedder.html";
602 const char* kGuestURL = "/browser_plugin_post_message_guest.html";
603 StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
604 RenderFrameHost* rfh = test_embedder()->web_contents()->GetMainFrame();
606 const base::string16 expected_title = ASCIIToUTF16("main guest");
607 content::TitleWatcher title_watcher(test_embedder()->web_contents(),
610 // By the time we get here 'contentWindow' should be ready because the
611 // guest has completed loading.
612 ExecuteSyncJSFunction(
613 rfh, base::StringPrintf("PostMessage('%s, false');", kTesting));
615 // The title will be updated to "main guest" at the last stage of the
616 // process described above.
617 base::string16 actual_title = title_watcher.WaitAndGetTitle();
618 EXPECT_EQ(expected_title, actual_title);
622 // This is the same as BrowserPluginHostTest.PostMessage but also
623 // posts a message to an iframe.
624 // TODO(fsamuel): This test should replace the previous test once postMessage
625 // iframe targeting is fixed (see http://crbug.com/153701).
626 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DISABLED_PostMessageToIFrame) {
627 const char* kTesting = "testing123";
628 const char* kEmbedderURL = "/browser_plugin_embedder.html";
629 const char* kGuestURL = "/browser_plugin_post_message_guest.html";
630 StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
631 RenderFrameHost* rfh = test_embedder()->web_contents()->GetMainFrame();
633 const base::string16 expected_title = ASCIIToUTF16("main guest");
634 content::TitleWatcher title_watcher(test_embedder()->web_contents(),
637 ExecuteSyncJSFunction(
638 rfh, base::StringPrintf("PostMessage('%s, false');", kTesting));
640 // The title will be updated to "main guest" at the last stage of the
641 // process described above.
642 base::string16 actual_title = title_watcher.WaitAndGetTitle();
643 EXPECT_EQ(expected_title, actual_title);
646 content::TitleWatcher ready_watcher(test_embedder()->web_contents(),
647 ASCIIToUTF16("ready"));
649 RenderFrameHost* guest_rfh =
650 test_guest()->web_contents()->GetMainFrame();
651 GURL test_url = embedded_test_server()->GetURL(
652 "/browser_plugin_post_message_guest.html");
653 ExecuteSyncJSFunction(
656 "CreateChildFrame('%s');", test_url.spec().c_str()));
658 base::string16 actual_title = ready_watcher.WaitAndGetTitle();
659 EXPECT_EQ(ASCIIToUTF16("ready"), actual_title);
661 content::TitleWatcher iframe_watcher(test_embedder()->web_contents(),
662 ASCIIToUTF16("iframe"));
663 ExecuteSyncJSFunction(
664 rfh, base::StringPrintf("PostMessage('%s', true);", kTesting));
666 // The title will be updated to "iframe" at the last stage of the
667 // process described above.
668 actual_title = iframe_watcher.WaitAndGetTitle();
669 EXPECT_EQ(ASCIIToUTF16("iframe"), actual_title);
673 // This test verifies that if a browser plugin is hidden before navigation,
674 // the guest starts off hidden.
675 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, HiddenBeforeNavigation) {
676 const char* kEmbedderURL = "/browser_plugin_embedder.html";
677 const std::string embedder_code =
678 "document.getElementById('plugin').style.visibility = 'hidden'";
679 StartBrowserPluginTest(
680 kEmbedderURL, kHTMLForGuest, true, embedder_code);
681 EXPECT_FALSE(test_guest()->visible());
684 // This test verifies that if a browser plugin is focused before navigation then
685 // the guest starts off focused.
686 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusBeforeNavigation) {
687 const char* kEmbedderURL = "/browser_plugin_embedder.html";
688 const std::string embedder_code =
689 "document.getElementById('plugin').focus();";
690 StartBrowserPluginTest(
691 kEmbedderURL, kHTMLForGuest, true, embedder_code);
692 RenderFrameHost* guest_rfh = test_guest()->web_contents()->GetMainFrame();
693 // Verify that the guest is focused.
694 scoped_ptr<base::Value> value =
695 content::ExecuteScriptAndGetValue(guest_rfh, "document.hasFocus()");
697 ASSERT_TRUE(value->GetAsBoolean(&result));
701 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusTracksEmbedder) {
702 const char* kEmbedderURL = "/browser_plugin_embedder.html";
703 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
704 // Blur the embedder.
705 test_embedder()->web_contents()->GetRenderViewHost()->Blur();
706 // Ensure that the guest is also blurred.
707 test_guest()->WaitForBlur();
710 // This test verifies that if IME is enabled in the embedder, it is also enabled
712 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, VerifyInputMethodActive) {
713 const char* kEmbedderURL = "/browser_plugin_embedder.html";
714 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
715 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
716 test_guest()->web_contents()->GetRenderViewHost());
717 EXPECT_TRUE(rvh->input_method_active());
720 // Verify that navigating to an invalid URL (e.g. 'http:') doesn't cause
722 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DoNotCrashOnInvalidNavigation) {
723 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
724 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
725 TestBrowserPluginGuestDelegate* delegate =
726 new TestBrowserPluginGuestDelegate();
727 test_guest()->SetDelegate(delegate);
729 const char kValidSchemeWithEmptyURL[] = "http:";
730 ExecuteSyncJSFunction(
731 test_embedder()->web_contents()->GetMainFrame(),
732 base::StringPrintf("SetSrc('%s');", kValidSchemeWithEmptyURL));
733 EXPECT_TRUE(delegate->load_aborted());
734 EXPECT_FALSE(delegate->load_aborted_url().is_valid());
735 EXPECT_EQ(kValidSchemeWithEmptyURL,
736 delegate->load_aborted_url().possibly_invalid_spec());
738 delegate->ResetStates();
740 // Attempt a navigation to chrome-guest://abc123, which is a valid URL. But it
741 // should be blocked because the scheme isn't web-safe or a pseudo-scheme.
742 ExecuteSyncJSFunction(
743 test_embedder()->web_contents()->GetMainFrame(),
744 base::StringPrintf("SetSrc('%s://abc123');", kGuestScheme));
745 EXPECT_TRUE(delegate->load_aborted());
746 EXPECT_TRUE(delegate->load_aborted_url().is_valid());
749 // Tests involving the threaded compositor.
750 class BrowserPluginThreadedCompositorTest : public BrowserPluginHostTest {
752 BrowserPluginThreadedCompositorTest() {}
753 virtual ~BrowserPluginThreadedCompositorTest() {}
756 virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE {
757 BrowserPluginHostTest::SetUpCommandLine(cmd);
758 cmd->AppendSwitch(switches::kEnableThreadedCompositing);
762 class BrowserPluginThreadedCompositorPixelTest
763 : public BrowserPluginThreadedCompositorTest {
765 virtual void SetUp() OVERRIDE {
767 BrowserPluginThreadedCompositorTest::SetUp();
771 static void CompareSkBitmaps(const SkBitmap& expected_bitmap,
772 const SkBitmap& bitmap) {
773 EXPECT_EQ(expected_bitmap.width(), bitmap.width());
774 if (expected_bitmap.width() != bitmap.width())
776 EXPECT_EQ(expected_bitmap.height(), bitmap.height());
777 if (expected_bitmap.height() != bitmap.height())
779 EXPECT_EQ(expected_bitmap.config(), bitmap.config());
780 if (expected_bitmap.config() != bitmap.config())
783 SkAutoLockPixels expected_bitmap_lock(expected_bitmap);
784 SkAutoLockPixels bitmap_lock(bitmap);
786 const int kAllowableError = 2;
787 for (int i = 0; i < bitmap.width() && fails < 10; ++i) {
788 for (int j = 0; j < bitmap.height() && fails < 10; ++j) {
789 SkColor expected_color = expected_bitmap.getColor(i, j);
790 SkColor color = bitmap.getColor(i, j);
791 int expected_alpha = SkColorGetA(expected_color);
792 int alpha = SkColorGetA(color);
793 int expected_red = SkColorGetR(expected_color);
794 int red = SkColorGetR(color);
795 int expected_green = SkColorGetG(expected_color);
796 int green = SkColorGetG(color);
797 int expected_blue = SkColorGetB(expected_color);
798 int blue = SkColorGetB(color);
799 EXPECT_NEAR(expected_alpha, alpha, kAllowableError)
800 << "expected_color: " << std::hex << expected_color
801 << " color: " << color
802 << " Failed at " << std::dec << i << ", " << j
803 << " Failure " << ++fails;
804 EXPECT_NEAR(expected_red, red, kAllowableError)
805 << "expected_color: " << std::hex << expected_color
806 << " color: " << color
807 << " Failed at " << std::dec << i << ", " << j
808 << " Failure " << ++fails;
809 EXPECT_NEAR(expected_green, green, kAllowableError)
810 << "expected_color: " << std::hex << expected_color
811 << " color: " << color
812 << " Failed at " << std::dec << i << ", " << j
813 << " Failure " << ++fails;
814 EXPECT_NEAR(expected_blue, blue, kAllowableError)
815 << "expected_color: " << std::hex << expected_color
816 << " color: " << color
817 << " Failed at " << std::dec << i << ", " << j
818 << " Failure " << ++fails;
821 EXPECT_LT(fails, 10);
824 static void CompareSkBitmapAndRun(const base::Closure& callback,
825 const SkBitmap& expected_bitmap,
828 const SkBitmap& bitmap) {
831 CompareSkBitmaps(expected_bitmap, bitmap);
835 // Mac: http://crbug.com/171744
836 // Aura/Ubercomp: http://crbug.com//327035
837 #if defined(OS_MACOSX) || defined(USE_AURA)
838 #define MAYBE_GetBackingStore DISABLED_GetBackingStore
840 #define MAYBE_GetBackingStore GetBackingStore
842 IN_PROC_BROWSER_TEST_F(BrowserPluginThreadedCompositorPixelTest,
843 MAYBE_GetBackingStore) {
844 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
845 const char kHTMLForGuest[] =
846 "data:text/html,<html><style>body { background-color: red; }</style>"
847 "<body></body></html>";
848 StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true,
849 std::string("SetSize(50, 60);"));
851 WebContentsImpl* guest_contents = test_guest()->web_contents();
852 RenderWidgetHostImpl* guest_widget_host =
853 RenderWidgetHostImpl::From(guest_contents->GetRenderViewHost());
855 SkBitmap expected_bitmap;
856 expected_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 50, 60);
857 expected_bitmap.allocPixels();
858 expected_bitmap.eraseARGB(255, 255, 0, 0); // #f00
862 guest_widget_host->CopyFromBackingStore(
864 guest_widget_host->GetView()->GetViewBounds().size(),
865 base::Bind(&CompareSkBitmapAndRun,
869 SkBitmap::kARGB_8888_Config);
874 // This test exercises the following scenario:
875 // 1. An <input> in guest has focus.
876 // 2. User takes focus to embedder by clicking e.g. an <input> in embedder.
877 // 3. User brings back the focus directly to the <input> in #1.
879 // Now we need to make sure TextInputTypeChange fires properly for the guest's
880 // view (RenderWidgetHostViewGuest) upon step #3. This test ensures that,
881 // otherwise IME doesn't work after step #3.
882 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusRestored) {
883 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
884 const char kGuestHTML[] = "data:text/html,"
885 "<html><body><input id=\"input1\"></body>"
887 "var i = document.getElementById(\"input1\");"
888 "document.body.addEventListener(\"click\", function(e) {"
891 "i.addEventListener(\"focus\", function(e) {"
892 " document.title = \"FOCUS\";"
894 "i.addEventListener(\"blur\", function(e) {"
895 " document.title = \"BLUR\";"
899 StartBrowserPluginTest(kEmbedderURL, kGuestHTML, true,
900 "document.getElementById(\"plugin\").focus();");
902 ASSERT_TRUE(test_embedder());
903 const char *kTitles[3] = {"FOCUS", "BLUR", "FOCUS"};
904 gfx::Point kClickPoints[3] = {
905 gfx::Point(20, 20), gfx::Point(700, 20), gfx::Point(20, 20)
908 for (int i = 0; i < 3; ++i) {
909 base::string16 expected_title = base::UTF8ToUTF16(kTitles[i]);
910 content::TitleWatcher title_watcher(test_guest()->web_contents(),
912 SimulateMouseClickAt(test_embedder()->web_contents(), 0,
913 blink::WebMouseEvent::ButtonLeft,
915 base::string16 actual_title = title_watcher.WaitAndGetTitle();
916 EXPECT_EQ(expected_title, actual_title);
918 TestBrowserPluginGuest* guest = test_guest();
920 ui::TextInputType text_input_type = guest->last_text_input_type();
921 ASSERT_TRUE(text_input_type != ui::TEXT_INPUT_TYPE_NONE);
924 // Tests input method.
925 IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, InputMethod) {
926 const char kEmbedderURL[] = "/browser_plugin_embedder.html";
927 const char kGuestHTML[] = "data:text/html,"
928 "<html><body><input id=\"input1\">"
929 "<input id=\"input2\"></body>"
931 "var i = document.getElementById(\"input1\");"
932 "i.oninput = function() {"
933 " document.title = i.value;"
937 StartBrowserPluginTest(kEmbedderURL, kGuestHTML, true,
938 "document.getElementById(\"plugin\").focus();");
940 RenderViewHostImpl* embedder_rvh = static_cast<RenderViewHostImpl*>(
941 test_embedder()->web_contents()->GetRenderViewHost());
942 RenderFrameHost* guest_rfh = test_guest()->web_contents()->GetMainFrame();
944 std::vector<blink::WebCompositionUnderline> underlines;
946 // An input field in browser plugin guest gets focus and given some user
949 ExecuteSyncJSFunction(guest_rfh,
950 "document.getElementById('input1').focus();");
951 base::string16 expected_title = base::UTF8ToUTF16("InputTest123");
952 content::TitleWatcher title_watcher(test_guest()->web_contents(),
955 new ViewMsg_ImeSetComposition(
956 test_embedder()->web_contents()->GetRoutingID(),
960 base::string16 actual_title = title_watcher.WaitAndGetTitle();
961 EXPECT_EQ(expected_title, actual_title);
963 // A composition is committed via IME.
965 base::string16 expected_title = base::UTF8ToUTF16("InputTest456");
966 content::TitleWatcher title_watcher(test_guest()->web_contents(),
969 new ViewMsg_ImeConfirmComposition(
970 test_embedder()->web_contents()->GetRoutingID(),
974 base::string16 actual_title = title_watcher.WaitAndGetTitle();
975 EXPECT_EQ(expected_title, actual_title);
977 // IME composition starts, but focus moves out, then the composition will
978 // be committed and get cancel msg.
980 ExecuteSyncJSFunction(guest_rfh,
981 "document.getElementById('input1').value = '';");
982 base::string16 composition = base::UTF8ToUTF16("InputTest789");
983 content::TitleWatcher title_watcher(test_guest()->web_contents(),
986 new ViewMsg_ImeSetComposition(
987 test_embedder()->web_contents()->GetRoutingID(),
991 base::string16 actual_title = title_watcher.WaitAndGetTitle();
992 EXPECT_EQ(composition, actual_title);
993 // Moving focus causes IME cancel, and the composition will be committed
994 // in input1, not in input2.
995 ExecuteSyncJSFunction(guest_rfh,
996 "document.getElementById('input2').focus();");
997 test_guest()->WaitForImeCancel();
998 scoped_ptr<base::Value> value =
999 content::ExecuteScriptAndGetValue(
1000 guest_rfh, "document.getElementById('input1').value");
1002 ASSERT_TRUE(value->GetAsString(&result));
1003 EXPECT_EQ(base::UTF16ToUTF8(composition), result);
1005 // Tests ExtendSelectionAndDelete message works in browser_plugin.
1007 // Set 'InputTestABC' in input1 and put caret at 6 (after 'T').
1008 ExecuteSyncJSFunction(guest_rfh,
1009 "var i = document.getElementById('input1');"
1011 "i.value = 'InputTestABC';"
1012 "i.selectionStart=6;"
1013 "i.selectionEnd=6;");
1014 base::string16 expected_value = base::UTF8ToUTF16("InputABC");
1015 content::TitleWatcher title_watcher(test_guest()->web_contents(),
1017 // Delete 'Test' in 'InputTestABC', as the caret is after 'T':
1018 // delete before 1 character ('T') and after 3 characters ('est').
1019 RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
1020 test_embedder()->web_contents()->GetFocusedFrame());
1021 rfh->ExtendSelectionAndDelete(1, 3);
1022 base::string16 actual_title = title_watcher.WaitAndGetTitle();
1023 EXPECT_EQ(expected_value, actual_title);
1024 scoped_ptr<base::Value> value =
1025 content::ExecuteScriptAndGetValue(
1026 guest_rfh, "document.getElementById('input1').value");
1027 std::string actual_value;
1028 ASSERT_TRUE(value->GetAsString(&actual_value));
1029 EXPECT_EQ(base::UTF16ToUTF8(expected_value), actual_value);
1033 } // namespace content