Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / browser / browser_plugin / browser_plugin_host_browsertest.cc
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/command_line.h"
6 #include "base/memory/singleton.h"
7 #include "base/run_loop.h"
8 #include "base/strings/string_split.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/test/test_timeouts.h"
12 #include "content/browser/browser_plugin/browser_plugin_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"
46
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;
54
55 const char kHTMLForGuest[] =
56     "data:text/html,<html><body>hello world</body></html>";
57
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\", "
64     "     handler);"
65     "}"
66     "function UninstallTouchHandler() { "
67     "  document.getElementById(\"touch\").removeEventListener(\"touchstart\", "
68     "     handler);"
69     "}"
70     "</script></html>";
71
72 const char kHTMLForGuestAcceptDrag[] =
73     "data:text/html,<html><body>"
74     "<script>"
75     "function dropped() {"
76     "  document.title = \"DROPPED\";"
77     "}"
78     "</script>"
79     "<textarea id=\"text\" style=\"width:100%; height: 100%\""
80     "    ondrop=\"dropped();\">"
81     "</textarea>"
82     "</body></html>";
83
84 namespace content {
85
86 class TestBrowserPluginEmbedder : public BrowserPluginEmbedder {
87  public:
88   TestBrowserPluginEmbedder(WebContentsImpl* web_contents)
89       : BrowserPluginEmbedder(web_contents) {
90   }
91
92   virtual ~TestBrowserPluginEmbedder() {}
93
94   WebContentsImpl* web_contents() const {
95     return static_cast<WebContentsImpl*>(BrowserPluginEmbedder::web_contents());
96   }
97
98  private:
99   DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginEmbedder);
100 };
101
102 // Test factory for creating test instances of BrowserPluginEmbedder and
103 // BrowserPluginGuest.
104 class TestBrowserPluginHostFactory : public BrowserPluginHostFactory {
105  public:
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);
112   }
113
114   virtual BrowserPluginGuest* CreateBrowserPluginGuest(
115       int instance_id,
116       WebContentsImpl* web_contents) OVERRIDE {
117     return new TestBrowserPluginGuest(instance_id, web_contents);
118   }
119
120   // Also keeps track of number of instances created.
121   virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder(
122       WebContentsImpl* web_contents) OVERRIDE {
123
124     return new TestBrowserPluginEmbedder(web_contents);
125   }
126
127   // Singleton getter.
128   static TestBrowserPluginHostFactory* GetInstance() {
129     return Singleton<TestBrowserPluginHostFactory>::get();
130   }
131
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)
137       return;
138     // Wait otherwise.
139     message_loop_runner_ = new MessageLoopRunner();
140     message_loop_runner_->Run();
141   }
142
143  protected:
144   TestBrowserPluginHostFactory() : guest_manager_instance_count_(0) {}
145   virtual ~TestBrowserPluginHostFactory() {}
146
147  private:
148   // For Singleton.
149   friend struct DefaultSingletonTraits<TestBrowserPluginHostFactory>;
150
151   scoped_refptr<MessageLoopRunner> message_loop_runner_;
152   int guest_manager_instance_count_;
153
154   DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginHostFactory);
155 };
156
157 // Test factory class for browser plugin that creates guests with short hang
158 // timeout.
159 class TestShortHangTimeoutGuestFactory : public TestBrowserPluginHostFactory {
160  public:
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());
166     return guest;
167   }
168
169   // Singleton getter.
170   static TestShortHangTimeoutGuestFactory* GetInstance() {
171     return Singleton<TestShortHangTimeoutGuestFactory>::get();
172   }
173
174  protected:
175   TestShortHangTimeoutGuestFactory() {}
176   virtual ~TestShortHangTimeoutGuestFactory() {}
177
178  private:
179   // For Singleton.
180   friend struct DefaultSingletonTraits<TestShortHangTimeoutGuestFactory>;
181
182   DISALLOW_COPY_AND_ASSIGN(TestShortHangTimeoutGuestFactory);
183 };
184
185 // A transparent observer that can be used to verify that a RenderViewHost
186 // received a specific message.
187 class MessageObserver : public WebContentsObserver {
188  public:
189   MessageObserver(WebContents* web_contents, uint32 message_id)
190       : WebContentsObserver(web_contents),
191         message_id_(message_id),
192         message_received_(false) {
193   }
194
195   virtual ~MessageObserver() {}
196
197   void WaitUntilMessageReceived() {
198     if (message_received_)
199       return;
200     message_loop_runner_ = new MessageLoopRunner();
201     message_loop_runner_->Run();
202   }
203
204   void ResetState() {
205     message_received_ = false;
206   }
207
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();
214     }
215     return false;
216   }
217
218  private:
219   scoped_refptr<MessageLoopRunner> message_loop_runner_;
220   uint32 message_id_;
221   bool message_received_;
222
223   DISALLOW_COPY_AND_ASSIGN(MessageObserver);
224 };
225
226 class BrowserPluginHostTest : public ContentBrowserTest {
227  public:
228   BrowserPluginHostTest()
229       : test_embedder_(NULL),
230         test_guest_(NULL),
231         test_guest_manager_(NULL) {}
232
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();
242   }
243   virtual void TearDown() OVERRIDE {
244     BrowserPluginEmbedder::set_factory_for_testing(NULL);
245     BrowserPluginGuest::set_factory_for_testing(NULL);
246
247     ContentBrowserTest::TearDown();
248   }
249
250   static void SimulateSpaceKeyPress(WebContents* web_contents) {
251     SimulateKeyPress(web_contents,
252                      ui::VKEY_SPACE,
253                      false,   // control.
254                      false,   // shift.
255                      false,   // alt.
256                      false);  // command.
257   }
258
259   static void SimulateTabKeyPress(WebContents* web_contents) {
260     SimulateKeyPress(web_contents,
261                      ui::VKEY_TAB,
262                      false,   // control.
263                      false,   // shift.
264                      false,   // alt.
265                      false);  // command.
266   }
267
268   // Executes the JavaScript synchronously and makes sure the returned value is
269   // freed properly.
270   void ExecuteSyncJSFunction(RenderFrameHost* rfh, const std::string& jscript) {
271     scoped_ptr<base::Value> value =
272         content::ExecuteScriptAndGetValue(rfh, jscript);
273   }
274
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);
287
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.
298     rvh->Focus();
299     // Activative IME.
300     rvh->SetInputMethodActive(true);
301
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);
306
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()));
311     } else {
312       ExecuteSyncJSFunction(
313           rfh, base::StringPrintf("SetSrc('%s');", guest_url.c_str()));
314     }
315
316     // Wait to make sure embedder is created/attached to WebContents.
317     TestBrowserPluginHostFactory::GetInstance()->WaitForGuestManagerCreation();
318
319     test_embedder_ = static_cast<TestBrowserPluginEmbedder*>(
320         embedder_web_contents->GetBrowserPluginEmbedder());
321     ASSERT_TRUE(test_embedder_);
322
323     test_guest_manager_ = static_cast<TestBrowserPluginGuestManager*>(
324         BrowserPluginGuestManager::FromBrowserContext(
325             test_embedder_->GetWebContents()->GetBrowserContext()));
326     ASSERT_TRUE(test_guest_manager_);
327
328     WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
329         test_guest_manager_->WaitForGuestAdded());
330
331     test_guest_ = static_cast<TestBrowserPluginGuest*>(
332         test_guest_web_contents->GetBrowserPluginGuest());
333     test_guest_->WaitForLoadStop();
334   }
335
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_;
340   }
341
342  private:
343   TestBrowserPluginEmbedder* test_embedder_;
344   TestBrowserPluginGuest* test_guest_;
345   TestBrowserPluginGuestManager* test_guest_manager_;
346   DISALLOW_COPY_AND_ASSIGN(BrowserPluginHostTest);
347 };
348
349 // This test ensures that if guest isn't there and we resize the guest (from
350 // js), it remembers the size correctly.
351 //
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);
361
362   // Wait for the guest to be resized to 100x200.
363   test_guest()->WaitForResizeGuest(nxt_size);
364 }
365
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());
370
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();
376
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();
386 }
387
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());
397
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());
401
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();
410
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);
419 }
420
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
423 // web_contents.
424 // Failing flakily on Windows: crbug.com/308405
425 #if defined(OS_WIN)
426 #define MAYBE_EmbedderSameAfterNav DISABLED_EmbedderSameAfterNav
427 #else
428 #define MAYBE_EmbedderSameAfterNav EmbedderSameAfterNav
429 #endif
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();
434
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";
446
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());
452 }
453
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());
460
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());
465
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());
474
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());
482 }
483
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();
490
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'
493   // in the next step.
494   {
495     const base::string16 expected_title = ASCIIToUTF16("modified");
496     content::TitleWatcher title_watcher(test_embedder()->web_contents(),
497                                         expected_title);
498
499     ExecuteSyncJSFunction(rfh,
500                           base::StringPrintf("SetTitle('%s');", "modified"));
501
502     base::string16 actual_title = title_watcher.WaitAndGetTitle();
503     EXPECT_EQ(expected_title, actual_title);
504   }
505
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.
508   {
509     const base::string16 expected_title = ASCIIToUTF16("embedder");
510     content::TitleWatcher title_watcher(test_embedder()->web_contents(),
511                                         expected_title);
512
513     test_embedder()->web_contents()->GetController().Reload(false);
514     base::string16 actual_title = title_watcher.WaitAndGetTitle();
515     EXPECT_EQ(expected_title, actual_title);
516
517     ExecuteSyncJSFunction(
518         test_embedder()->web_contents()->GetMainFrame(),
519         base::StringPrintf("SetSrc('%s');", kHTMLForGuest));
520
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);
527
528     // Wait for the guest to send an UpdateRectMsg, meaning it is ready.
529     new_test_guest->WaitForUpdateRectMsg();
530   }
531 }
532
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
537 #else
538 #define MAYBE_AcceptDragEvents AcceptDragEvents
539 #endif
540
541 // Tests that a drag-n-drop over the browser plugin in the embedder happens
542 // correctly.
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());
547
548   RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
549       test_embedder()->web_contents()->GetRenderViewHost());
550   RenderFrameHost* rfh = test_embedder()->web_contents()->GetMainFrame();
551
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));
559
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);
563   double end_x, end_y;
564   ASSERT_TRUE(end->GetDouble(0, &end_x) && end->GetDouble(1, &end_y));
565
566   DropData drop_data;
567   GURL url = GURL("https://www.domain.com/index.html");
568   drop_data.url = url;
569
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
574   // happens.
575   const base::string16 expected_title = ASCIIToUTF16("DROPPED");
576   content::TitleWatcher title_watcher(test_guest()->web_contents(),
577       expected_title);
578
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);
584
585   base::string16 actual_title = title_watcher.WaitAndGetTitle();
586   EXPECT_EQ(expected_title, actual_title);
587 }
588
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'
598 // message.
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();
605   {
606     const base::string16 expected_title = ASCIIToUTF16("main guest");
607     content::TitleWatcher title_watcher(test_embedder()->web_contents(),
608                                         expected_title);
609
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));
614
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);
619   }
620 }
621
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();
632   {
633     const base::string16 expected_title = ASCIIToUTF16("main guest");
634     content::TitleWatcher title_watcher(test_embedder()->web_contents(),
635                                         expected_title);
636
637     ExecuteSyncJSFunction(
638         rfh, base::StringPrintf("PostMessage('%s, false');", kTesting));
639
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);
644   }
645   {
646     content::TitleWatcher ready_watcher(test_embedder()->web_contents(),
647                                         ASCIIToUTF16("ready"));
648
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(
654         guest_rfh,
655         base::StringPrintf(
656             "CreateChildFrame('%s');", test_url.spec().c_str()));
657
658     base::string16 actual_title = ready_watcher.WaitAndGetTitle();
659     EXPECT_EQ(ASCIIToUTF16("ready"), actual_title);
660
661     content::TitleWatcher iframe_watcher(test_embedder()->web_contents(),
662                                         ASCIIToUTF16("iframe"));
663     ExecuteSyncJSFunction(
664         rfh, base::StringPrintf("PostMessage('%s', true);", kTesting));
665
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);
670   }
671 }
672
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());
682 }
683
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()");
696   bool result = false;
697   ASSERT_TRUE(value->GetAsBoolean(&result));
698   EXPECT_TRUE(result);
699 }
700
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();
708 }
709
710 // This test verifies that if IME is enabled in the embedder, it is also enabled
711 // in the guest.
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());
718 }
719
720 // Verify that navigating to an invalid URL (e.g. 'http:') doesn't cause
721 // a crash.
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);
728
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());
737
738   delegate->ResetStates();
739
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());
747 }
748
749 // Tests involving the threaded compositor.
750 class BrowserPluginThreadedCompositorTest : public BrowserPluginHostTest {
751  public:
752   BrowserPluginThreadedCompositorTest() {}
753   virtual ~BrowserPluginThreadedCompositorTest() {}
754
755  protected:
756   virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE {
757     BrowserPluginHostTest::SetUpCommandLine(cmd);
758     cmd->AppendSwitch(switches::kEnableThreadedCompositing);
759   }
760 };
761
762 class BrowserPluginThreadedCompositorPixelTest
763     : public BrowserPluginThreadedCompositorTest {
764  protected:
765   virtual void SetUp() OVERRIDE {
766     EnablePixelOutput();
767     BrowserPluginThreadedCompositorTest::SetUp();
768   }
769 };
770
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())
775     return;
776   EXPECT_EQ(expected_bitmap.height(), bitmap.height());
777   if (expected_bitmap.height() != bitmap.height())
778     return;
779   EXPECT_EQ(expected_bitmap.config(), bitmap.config());
780   if (expected_bitmap.config() != bitmap.config())
781     return;
782
783   SkAutoLockPixels expected_bitmap_lock(expected_bitmap);
784   SkAutoLockPixels bitmap_lock(bitmap);
785   int fails = 0;
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;
819     }
820   }
821   EXPECT_LT(fails, 10);
822 }
823
824 static void CompareSkBitmapAndRun(const base::Closure& callback,
825                                   const SkBitmap& expected_bitmap,
826                                   bool *result,
827                                   bool succeed,
828                                   const SkBitmap& bitmap) {
829   *result = succeed;
830   if (succeed)
831     CompareSkBitmaps(expected_bitmap, bitmap);
832   callback.Run();
833 }
834
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
839 #else
840 #define MAYBE_GetBackingStore GetBackingStore
841 #endif
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);"));
850
851   WebContentsImpl* guest_contents = test_guest()->web_contents();
852   RenderWidgetHostImpl* guest_widget_host =
853       RenderWidgetHostImpl::From(guest_contents->GetRenderViewHost());
854
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
859   bool result = false;
860   while (!result) {
861     base::RunLoop loop;
862     guest_widget_host->CopyFromBackingStore(
863         gfx::Rect(),
864         guest_widget_host->GetView()->GetViewBounds().size(),
865         base::Bind(&CompareSkBitmapAndRun,
866                    loop.QuitClosure(),
867                    expected_bitmap,
868                    &result),
869         SkBitmap::kARGB_8888_Config);
870     loop.Run();
871   }
872 }
873
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.
878 //
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>"
886       "<script>"
887       "var i = document.getElementById(\"input1\");"
888       "document.body.addEventListener(\"click\", function(e) {"
889       "  i.focus();"
890       "});"
891       "i.addEventListener(\"focus\", function(e) {"
892       "  document.title = \"FOCUS\";"
893       "});"
894       "i.addEventListener(\"blur\", function(e) {"
895       "  document.title = \"BLUR\";"
896       "});"
897       "</script>"
898       "</html>";
899   StartBrowserPluginTest(kEmbedderURL, kGuestHTML, true,
900                          "document.getElementById(\"plugin\").focus();");
901
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)
906   };
907
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(),
911                                         expected_title);
912     SimulateMouseClickAt(test_embedder()->web_contents(), 0,
913         blink::WebMouseEvent::ButtonLeft,
914         kClickPoints[i]);
915     base::string16 actual_title = title_watcher.WaitAndGetTitle();
916     EXPECT_EQ(expected_title, actual_title);
917   }
918   TestBrowserPluginGuest* guest = test_guest();
919   ASSERT_TRUE(guest);
920   ui::TextInputType text_input_type = guest->last_text_input_type();
921   ASSERT_TRUE(text_input_type != ui::TEXT_INPUT_TYPE_NONE);
922 }
923
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>"
930       "<script>"
931       "var i = document.getElementById(\"input1\");"
932       "i.oninput = function() {"
933       "  document.title = i.value;"
934       "}"
935       "</script>"
936       "</html>";
937   StartBrowserPluginTest(kEmbedderURL, kGuestHTML, true,
938                          "document.getElementById(\"plugin\").focus();");
939
940   RenderViewHostImpl* embedder_rvh = static_cast<RenderViewHostImpl*>(
941       test_embedder()->web_contents()->GetRenderViewHost());
942   RenderFrameHost* guest_rfh = test_guest()->web_contents()->GetMainFrame();
943
944   std::vector<blink::WebCompositionUnderline> underlines;
945
946   // An input field in browser plugin guest gets focus and given some user
947   // input via IME.
948   {
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(),
953                                         expected_title);
954     embedder_rvh->Send(
955         new ViewMsg_ImeSetComposition(
956             test_embedder()->web_contents()->GetRoutingID(),
957             expected_title,
958             underlines,
959             12, 12));
960     base::string16 actual_title = title_watcher.WaitAndGetTitle();
961     EXPECT_EQ(expected_title, actual_title);
962   }
963   // A composition is committed via IME.
964   {
965     base::string16 expected_title = base::UTF8ToUTF16("InputTest456");
966     content::TitleWatcher title_watcher(test_guest()->web_contents(),
967                                         expected_title);
968     embedder_rvh->Send(
969         new ViewMsg_ImeConfirmComposition(
970             test_embedder()->web_contents()->GetRoutingID(),
971             expected_title,
972             gfx::Range(),
973             true));
974     base::string16 actual_title = title_watcher.WaitAndGetTitle();
975     EXPECT_EQ(expected_title, actual_title);
976   }
977   // IME composition starts, but focus moves out, then the composition will
978   // be committed and get cancel msg.
979   {
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(),
984                                         composition);
985     embedder_rvh->Send(
986         new ViewMsg_ImeSetComposition(
987             test_embedder()->web_contents()->GetRoutingID(),
988             composition,
989             underlines,
990             12, 12));
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");
1001     std::string result;
1002     ASSERT_TRUE(value->GetAsString(&result));
1003     EXPECT_EQ(base::UTF16ToUTF8(composition), result);
1004   }
1005   // Tests ExtendSelectionAndDelete message works in browser_plugin.
1006   {
1007     // Set 'InputTestABC' in input1 and put caret at 6 (after 'T').
1008     ExecuteSyncJSFunction(guest_rfh,
1009                           "var i = document.getElementById('input1');"
1010                           "i.focus();"
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(),
1016                                         expected_value);
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);
1030   }
1031 }
1032
1033 }  // namespace content