1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/command_line.h"
6#include "base/memory/singleton.h"
7#include "base/run_loop.h"
8#include "base/strings/string_split.h"
9#include "base/strings/string_util.h"
10#include "base/strings/utf_string_conversions.h"
11#include "base/test/test_timeouts.h"
12#include "content/browser/browser_plugin/browser_plugin_guest.h"
13#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
14#include "content/browser/browser_plugin/test_browser_plugin_embedder.h"
15#include "content/browser/browser_plugin/test_browser_plugin_guest.h"
16#include "content/browser/browser_plugin/test_browser_plugin_guest_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/renderer_host/render_view_host_impl.h"
20#include "content/browser/web_contents/web_contents_impl.h"
21#include "content/common/browser_plugin/browser_plugin_messages.h"
22#include "content/common/view_messages.h"
23#include "content/public/browser/notification_service.h"
24#include "content/public/browser/notification_types.h"
25#include "content/public/browser/render_widget_host_view.h"
26#include "content/public/browser/web_contents_observer.h"
27#include "content/public/common/content_switches.h"
28#include "content/public/common/drop_data.h"
29#include "content/public/common/url_constants.h"
30#include "content/public/test/browser_test_utils.h"
31#include "content/public/test/test_utils.h"
32#include "content/shell/browser/shell.h"
33#include "content/test/content_browser_test.h"
34#include "content/test/content_browser_test_utils.h"
35#include "net/base/net_util.h"
36#include "net/test/embedded_test_server/embedded_test_server.h"
37#include "net/test/embedded_test_server/http_request.h"
38#include "net/test/embedded_test_server/http_response.h"
39#include "net/test/spawned_test_server/spawned_test_server.h"
40#include "third_party/WebKit/public/web/WebInputEvent.h"
41
42using blink::WebInputEvent;
43using blink::WebMouseEvent;
44using content::BrowserPluginEmbedder;
45using content::BrowserPluginGuest;
46using content::BrowserPluginHostFactory;
47using content::WebContentsImpl;
48
49const char kHTMLForGuest[] =
50    "data:text/html,<html><body>hello world</body></html>";
51
52const char kHTMLForGuestTouchHandler[] =
53    "data:text/html,<html><body><div id=\"touch\">With touch</div></body>"
54    "<script type=\"text/javascript\">"
55    "function handler() {}"
56    "function InstallTouchHandler() { "
57    "  document.getElementById(\"touch\").addEventListener(\"touchstart\", "
58    "     handler);"
59    "}"
60    "function UninstallTouchHandler() { "
61    "  document.getElementById(\"touch\").removeEventListener(\"touchstart\", "
62    "     handler);"
63    "}"
64    "</script></html>";
65
66const char kHTMLForGuestAcceptDrag[] =
67    "data:text/html,<html><body>"
68    "<script>"
69    "function dropped() {"
70    "  document.title = \"DROPPED\";"
71    "}"
72    "</script>"
73    "<textarea id=\"text\" style=\"width:100%; height: 100%\""
74    "    ondrop=\"dropped();\">"
75    "</textarea>"
76    "</body></html>";
77
78const char kHTMLForGuestWithSize[] =
79    "data:text/html,"
80    "<html>"
81    "<body style=\"margin: 0px;\">"
82    "<img style=\"width: 100%; height: 400px;\"/>"
83    "</body>"
84    "</html>";
85
86namespace content {
87
88// Test factory for creating test instances of BrowserPluginEmbedder and
89// BrowserPluginGuest.
90class TestBrowserPluginHostFactory : public BrowserPluginHostFactory {
91 public:
92  virtual BrowserPluginGuestManager*
93      CreateBrowserPluginGuestManager() OVERRIDE {
94    guest_manager_instance_count_++;
95    if (message_loop_runner_.get())
96      message_loop_runner_->Quit();
97    return new TestBrowserPluginGuestManager();
98  }
99
100  virtual BrowserPluginGuest* CreateBrowserPluginGuest(
101      int instance_id,
102      WebContentsImpl* web_contents) OVERRIDE {
103    return new TestBrowserPluginGuest(instance_id, web_contents);
104  }
105
106  // Also keeps track of number of instances created.
107  virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder(
108      WebContentsImpl* web_contents) OVERRIDE {
109
110    return new TestBrowserPluginEmbedder(web_contents);
111  }
112
113  // Singleton getter.
114  static TestBrowserPluginHostFactory* GetInstance() {
115    return Singleton<TestBrowserPluginHostFactory>::get();
116  }
117
118  // Waits for at least one embedder to be created in the test. Returns true if
119  // we have a guest, false if waiting times out.
120  void WaitForGuestManagerCreation() {
121    // Check if already have created an instance.
122    if (guest_manager_instance_count_ > 0)
123      return;
124    // Wait otherwise.
125    message_loop_runner_ = new MessageLoopRunner();
126    message_loop_runner_->Run();
127  }
128
129 protected:
130  TestBrowserPluginHostFactory() : guest_manager_instance_count_(0) {}
131  virtual ~TestBrowserPluginHostFactory() {}
132
133 private:
134  // For Singleton.
135  friend struct DefaultSingletonTraits<TestBrowserPluginHostFactory>;
136
137  scoped_refptr<MessageLoopRunner> message_loop_runner_;
138  int guest_manager_instance_count_;
139
140  DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginHostFactory);
141};
142
143// Test factory class for browser plugin that creates guests with short hang
144// timeout.
145class TestShortHangTimeoutGuestFactory : public TestBrowserPluginHostFactory {
146 public:
147  virtual BrowserPluginGuest* CreateBrowserPluginGuest(
148      int instance_id, WebContentsImpl* web_contents) OVERRIDE {
149    TestBrowserPluginGuest* guest =
150        new TestBrowserPluginGuest(instance_id, web_contents);
151    guest->set_guest_hang_timeout(TestTimeouts::tiny_timeout());
152    return guest;
153  }
154
155  // Singleton getter.
156  static TestShortHangTimeoutGuestFactory* GetInstance() {
157    return Singleton<TestShortHangTimeoutGuestFactory>::get();
158  }
159
160 protected:
161  TestShortHangTimeoutGuestFactory() {}
162  virtual ~TestShortHangTimeoutGuestFactory() {}
163
164 private:
165  // For Singleton.
166  friend struct DefaultSingletonTraits<TestShortHangTimeoutGuestFactory>;
167
168  DISALLOW_COPY_AND_ASSIGN(TestShortHangTimeoutGuestFactory);
169};
170
171// A transparent observer that can be used to verify that a RenderViewHost
172// received a specific message.
173class MessageObserver : public WebContentsObserver {
174 public:
175  MessageObserver(WebContents* web_contents, uint32 message_id)
176      : WebContentsObserver(web_contents),
177        message_id_(message_id),
178        message_received_(false) {
179  }
180
181  virtual ~MessageObserver() {}
182
183  void WaitUntilMessageReceived() {
184    if (message_received_)
185      return;
186    message_loop_runner_ = new MessageLoopRunner();
187    message_loop_runner_->Run();
188  }
189
190  void ResetState() {
191    message_received_ = false;
192  }
193
194  // IPC::Listener implementation.
195  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
196    if (message.type() == message_id_) {
197      message_received_ = true;
198      if (message_loop_runner_.get())
199        message_loop_runner_->Quit();
200    }
201    return false;
202  }
203
204 private:
205  scoped_refptr<MessageLoopRunner> message_loop_runner_;
206  uint32 message_id_;
207  bool message_received_;
208
209  DISALLOW_COPY_AND_ASSIGN(MessageObserver);
210};
211
212class BrowserPluginHostTest : public ContentBrowserTest {
213 public:
214  BrowserPluginHostTest()
215      : test_embedder_(NULL),
216        test_guest_(NULL),
217        test_guest_manager_(NULL) {}
218
219  virtual void SetUp() OVERRIDE {
220    // Override factory to create tests instances of BrowserPlugin*.
221    content::BrowserPluginEmbedder::set_factory_for_testing(
222        TestBrowserPluginHostFactory::GetInstance());
223    content::BrowserPluginGuest::set_factory_for_testing(
224        TestBrowserPluginHostFactory::GetInstance());
225    content::BrowserPluginGuestManager::set_factory_for_testing(
226        TestBrowserPluginHostFactory::GetInstance());
227
228    // On legacy windows, the AcceptDragEvents test needs this to pass.
229#if defined(OS_WIN) && !defined(USE_AURA)
230    UseRealGLBindings();
231#endif
232    // We need real contexts, otherwise the embedder doesn't composite, but the
233    // guest does, and that isn't an expected configuration.
234    UseRealGLContexts();
235
236    ContentBrowserTest::SetUp();
237  }
238  virtual void TearDown() OVERRIDE {
239    content::BrowserPluginEmbedder::set_factory_for_testing(NULL);
240    content::BrowserPluginGuest::set_factory_for_testing(NULL);
241
242    ContentBrowserTest::TearDown();
243  }
244
245  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
246    // Enable browser plugin in content_shell for running test.
247    command_line->AppendSwitch(switches::kEnableBrowserPluginForAllViewTypes);
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(RenderViewHost* rvh, const std::string& jscript) {
271    scoped_ptr<base::Value> value =
272        content::ExecuteScriptAndGetValue(rvh, jscript);
273  }
274
275  bool IsAttributeNull(RenderViewHost* rvh, const std::string& attribute) {
276    scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh,
277        "document.getElementById('plugin').getAttribute('" + attribute + "');");
278    return value->GetType() == Value::TYPE_NULL;
279  }
280
281  // Removes all attributes in the comma-delimited string |attributes|.
282  void RemoveAttributes(RenderViewHost* rvh, const std::string& attributes) {
283    std::vector<std::string> attributes_list;
284    base::SplitString(attributes, ',', &attributes_list);
285    std::vector<std::string>::const_iterator itr;
286    for (itr = attributes_list.begin(); itr != attributes_list.end(); ++itr) {
287      ExecuteSyncJSFunction(rvh, "document.getElementById('plugin')"
288                                 "." + *itr + " = null;");
289    }
290  }
291
292  // This helper method does the following:
293  // 1. Start the test server and navigate the shell to |embedder_url|.
294  // 2. Execute custom pre-navigation |embedder_code| if provided.
295  // 3. Navigate the guest to the |guest_url|.
296  // 4. Verify that the guest has been created and has completed loading.
297  void StartBrowserPluginTest(const std::string& embedder_url,
298                              const std::string& guest_url,
299                              bool is_guest_data_url,
300                              const std::string& embedder_code) {
301    ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
302    GURL test_url(embedded_test_server()->GetURL(embedder_url));
303    NavigateToURL(shell(), test_url);
304
305    WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>(
306        shell()->web_contents());
307    RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
308        embedder_web_contents->GetRenderViewHost());
309    // Focus the embedder.
310    rvh->Focus();
311    // Activative IME.
312    rvh->SetInputMethodActive(true);
313
314    // Allow the test to do some operations on the embedder before we perform
315    // the first navigation of the guest.
316    if (!embedder_code.empty())
317      ExecuteSyncJSFunction(rvh, embedder_code);
318
319    if (!is_guest_data_url) {
320      test_url = embedded_test_server()->GetURL(guest_url);
321      ExecuteSyncJSFunction(
322          rvh, base::StringPrintf("SetSrc('%s');", test_url.spec().c_str()));
323    } else {
324      ExecuteSyncJSFunction(
325          rvh, base::StringPrintf("SetSrc('%s');", guest_url.c_str()));
326    }
327
328    // Wait to make sure embedder is created/attached to WebContents.
329    TestBrowserPluginHostFactory::GetInstance()->WaitForGuestManagerCreation();
330
331    test_embedder_ = static_cast<TestBrowserPluginEmbedder*>(
332        embedder_web_contents->GetBrowserPluginEmbedder());
333    ASSERT_TRUE(test_embedder_);
334
335    test_guest_manager_ = static_cast<TestBrowserPluginGuestManager*>(
336        embedder_web_contents->GetBrowserPluginGuestManager());
337    ASSERT_TRUE(test_guest_manager_);
338
339    test_guest_manager_->WaitForGuestAdded();
340
341    // Verify that we have exactly one guest.
342    const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map =
343        test_guest_manager_->guest_web_contents_for_testing();
344    EXPECT_EQ(1u, instance_map.size());
345
346    WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
347        instance_map.begin()->second);
348    test_guest_ = static_cast<TestBrowserPluginGuest*>(
349        test_guest_web_contents->GetBrowserPluginGuest());
350    test_guest_->WaitForLoadStop();
351  }
352
353  TestBrowserPluginEmbedder* test_embedder() const { return test_embedder_; }
354  TestBrowserPluginGuest* test_guest() const { return test_guest_; }
355  TestBrowserPluginGuestManager* test_guest_manager() const {
356    return test_guest_manager_;
357  }
358
359 private:
360  TestBrowserPluginEmbedder* test_embedder_;
361  TestBrowserPluginGuest* test_guest_;
362  TestBrowserPluginGuestManager* test_guest_manager_;
363  DISALLOW_COPY_AND_ASSIGN(BrowserPluginHostTest);
364};
365
366// This test ensures that if guest isn't there and we resize the guest (from
367// js), it remembers the size correctly.
368//
369// Initially we load an embedder with a guest without a src attribute (which has
370// dimension 640x480), resize it to 100x200, and then we set the source to a
371// sample guest. In the end we verify that the correct size has been set.
372IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, NavigateAfterResize) {
373  const gfx::Size nxt_size = gfx::Size(100, 200);
374  const std::string embedder_code = base::StringPrintf(
375      "SetSize(%d, %d);", nxt_size.width(), nxt_size.height());
376  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
377  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, embedder_code);
378
379  // Wait for the guest to receive a damage buffer of size 100x200.
380  // This means the guest will be painted properly at that size.
381  test_guest()->WaitForDamageBufferWithSize(nxt_size);
382}
383
384IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AdvanceFocus) {
385  const char kEmbedderURL[] = "/browser_plugin_focus.html";
386  const char* kGuestURL = "/browser_plugin_focus_child.html";
387  StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
388
389  SimulateMouseClick(test_embedder()->web_contents(), 0,
390      blink::WebMouseEvent::ButtonLeft);
391  BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
392  // Wait until we focus into the guest.
393  test_guest()->WaitForFocus();
394
395  // TODO(fsamuel): A third Tab key press should not be necessary.
396  // The browser plugin will take keyboard focus but it will not
397  // focus an initial element. The initial element is dependent
398  // upon tab direction which WebKit does not propagate to the plugin.
399  // See http://crbug.com/147644.
400  BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
401  BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
402  BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
403  test_guest()->WaitForAdvanceFocus();
404}
405
406// This test opens a page in http and then opens another page in https, forcing
407// a RenderViewHost swap in the web_contents. We verify that the embedder in the
408// web_contents gets cleared properly.
409IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderChangedAfterSwap) {
410  net::SpawnedTestServer https_server(
411      net::SpawnedTestServer::TYPE_HTTPS,
412      net::SpawnedTestServer::kLocalhost,
413      base::FilePath(FILE_PATH_LITERAL("content/test/data")));
414  ASSERT_TRUE(https_server.Start());
415
416  // 1. Load an embedder page with one guest in it.
417  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
418  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
419
420  // 2. Navigate to a URL in https, so we trigger a RenderViewHost swap.
421  GURL test_https_url(https_server.GetURL(
422      "files/browser_plugin_title_change.html"));
423  content::WindowedNotificationObserver swap_observer(
424      content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
425      content::Source<WebContents>(test_embedder()->web_contents()));
426  NavigateToURL(shell(), test_https_url);
427  swap_observer.Wait();
428
429  TestBrowserPluginEmbedder* test_embedder_after_swap =
430      static_cast<TestBrowserPluginEmbedder*>(
431          static_cast<WebContentsImpl*>(shell()->web_contents())->
432              GetBrowserPluginEmbedder());
433  // Verify we have a no embedder in web_contents (since the new page doesn't
434  // have any browser plugin).
435  ASSERT_TRUE(!test_embedder_after_swap);
436  ASSERT_NE(test_embedder(), test_embedder_after_swap);
437}
438
439// This test opens two pages in http and there is no RenderViewHost swap,
440// therefore the embedder created on first page navigation stays the same in
441// web_contents.
442// Failing flakily on Windows: crbug.com/308405
443#if defined(OS_WIN)
444#define MAYBE_EmbedderSameAfterNav DISABLED_EmbedderSameAfterNav
445#else
446#define MAYBE_EmbedderSameAfterNav EmbedderSameAfterNav
447#endif
448IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_EmbedderSameAfterNav) {
449  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
450  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
451  WebContentsImpl* embedder_web_contents = test_embedder()->web_contents();
452
453  // Navigate to another page in same host and port, so RenderViewHost swap
454  // does not happen and existing embedder doesn't change in web_contents.
455  GURL test_url_new(embedded_test_server()->GetURL(
456      "/browser_plugin_title_change.html"));
457  const base::string16 expected_title = ASCIIToUTF16("done");
458  content::TitleWatcher title_watcher(shell()->web_contents(), expected_title);
459  NavigateToURL(shell(), test_url_new);
460  VLOG(0) << "Start waiting for title";
461  base::string16 actual_title = title_watcher.WaitAndGetTitle();
462  EXPECT_EQ(expected_title, actual_title);
463  VLOG(0) << "Done navigating to second page";
464
465  TestBrowserPluginEmbedder* test_embedder_after_nav =
466      static_cast<TestBrowserPluginEmbedder*>(
467          embedder_web_contents->GetBrowserPluginEmbedder());
468  // Embedder must not change in web_contents.
469  ASSERT_EQ(test_embedder_after_nav, test_embedder());
470}
471
472// This test verifies that hiding the embedder also hides the guest.
473IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, BrowserPluginVisibilityChanged) {
474  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
475  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
476
477  // Hide the Browser Plugin.
478  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
479      test_embedder()->web_contents()->GetRenderViewHost());
480  ExecuteSyncJSFunction(
481      rvh, "document.getElementById('plugin').style.visibility = 'hidden'");
482
483  // Make sure that the guest is hidden.
484  test_guest()->WaitUntilHidden();
485}
486
487IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderVisibilityChanged) {
488  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
489  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
490
491  // Hide the embedder.
492  test_embedder()->web_contents()->WasHidden();
493
494  // Make sure that hiding the embedder also hides the guest.
495  test_guest()->WaitUntilHidden();
496}
497
498// Verifies that installing/uninstalling touch-event handlers in the guest
499// plugin correctly updates the touch-event handling state in the embedder.
500IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AcceptTouchEvents) {
501  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
502  StartBrowserPluginTest(
503      kEmbedderURL, kHTMLForGuestTouchHandler, true, std::string());
504
505  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
506      test_embedder()->web_contents()->GetRenderViewHost());
507  // The embedder should not have any touch event handlers at this point.
508  EXPECT_FALSE(rvh->has_touch_handler());
509
510  // Install the touch handler in the guest. This should cause the embedder to
511  // start listening for touch events too.
512  MessageObserver observer(test_embedder()->web_contents(),
513                           ViewHostMsg_HasTouchEventHandlers::ID);
514  ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(),
515                        "InstallTouchHandler();");
516  observer.WaitUntilMessageReceived();
517  EXPECT_TRUE(rvh->has_touch_handler());
518
519  // Uninstalling the touch-handler in guest should cause the embedder to stop
520  // listening for touch events.
521  observer.ResetState();
522  ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(),
523                        "UninstallTouchHandler();");
524  observer.WaitUntilMessageReceived();
525  EXPECT_FALSE(rvh->has_touch_handler());
526}
527
528// This tests verifies that reloading the embedder does not crash the browser
529// and that the guest is reset.
530IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, ReloadEmbedder) {
531  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
532  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
533  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
534      test_embedder()->web_contents()->GetRenderViewHost());
535
536  // Change the title of the page to 'modified' so that we know that
537  // the page has successfully reloaded when it goes back to 'embedder'
538  // in the next step.
539  {
540    const base::string16 expected_title = ASCIIToUTF16("modified");
541    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
542                                        expected_title);
543
544    ExecuteSyncJSFunction(rvh,
545                          base::StringPrintf("SetTitle('%s');", "modified"));
546
547    base::string16 actual_title = title_watcher.WaitAndGetTitle();
548    EXPECT_EQ(expected_title, actual_title);
549  }
550
551  // Reload the embedder page, and verify that the reload was successful.
552  // Then navigate the guest to verify that the browser process does not crash.
553  {
554    const base::string16 expected_title = ASCIIToUTF16("embedder");
555    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
556                                        expected_title);
557
558    test_embedder()->web_contents()->GetController().Reload(false);
559    base::string16 actual_title = title_watcher.WaitAndGetTitle();
560    EXPECT_EQ(expected_title, actual_title);
561
562    ExecuteSyncJSFunction(
563        test_embedder()->web_contents()->GetRenderViewHost(),
564        base::StringPrintf("SetSrc('%s');", kHTMLForGuest));
565    test_guest_manager()->WaitForGuestAdded();
566
567    const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map =
568        test_guest_manager()->guest_web_contents_for_testing();
569    WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
570        instance_map.begin()->second);
571    TestBrowserPluginGuest* new_test_guest =
572        static_cast<TestBrowserPluginGuest*>(
573          test_guest_web_contents->GetBrowserPluginGuest());
574    ASSERT_TRUE(new_test_guest != NULL);
575
576    // Wait for the guest to send an UpdateRectMsg, meaning it is ready.
577    new_test_guest->WaitForUpdateRectMsg();
578  }
579}
580
581// Always failing in the win7_aura try bot. See http://crbug.com/181107.
582// Times out on the Mac. See http://crbug.com/297576.
583#if (defined(OS_WIN) && defined(USE_AURA)) || defined(OS_MACOSX)
584#define MAYBE_AcceptDragEvents DISABLED_AcceptDragEvents
585#else
586#define MAYBE_AcceptDragEvents AcceptDragEvents
587#endif
588
589// Tests that a drag-n-drop over the browser plugin in the embedder happens
590// correctly.
591IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_AcceptDragEvents) {
592  const char kEmbedderURL[] = "/browser_plugin_dragging.html";
593  StartBrowserPluginTest(
594      kEmbedderURL, kHTMLForGuestAcceptDrag, true, std::string());
595
596  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
597      test_embedder()->web_contents()->GetRenderViewHost());
598
599  // Get a location in the embedder outside of the plugin.
600  base::ListValue *start, *end;
601  scoped_ptr<base::Value> value =
602      content::ExecuteScriptAndGetValue(rvh, "dragLocation()");
603  ASSERT_TRUE(value->GetAsList(&start) && start->GetSize() == 2);
604  double start_x, start_y;
605  ASSERT_TRUE(start->GetDouble(0, &start_x) && start->GetDouble(1, &start_y));
606
607  // Get a location in the embedder that falls inside the plugin.
608  value = content::ExecuteScriptAndGetValue(rvh, "dropLocation()");
609  ASSERT_TRUE(value->GetAsList(&end) && end->GetSize() == 2);
610  double end_x, end_y;
611  ASSERT_TRUE(end->GetDouble(0, &end_x) && end->GetDouble(1, &end_y));
612
613  DropData drop_data;
614  GURL url = GURL("https://www.domain.com/index.html");
615  drop_data.url = url;
616
617  // Pretend that the URL is being dragged over the embedder. Start the drag
618  // from outside the plugin, then move the drag inside the plugin and drop.
619  // This should trigger appropriate messages from the embedder to the guest,
620  // and end with a drop on the guest. The guest changes title when a drop
621  // happens.
622  const base::string16 expected_title = ASCIIToUTF16("DROPPED");
623  content::TitleWatcher title_watcher(test_guest()->web_contents(),
624      expected_title);
625
626  rvh->DragTargetDragEnter(drop_data, gfx::Point(start_x, start_y),
627      gfx::Point(start_x, start_y), blink::WebDragOperationEvery, 0);
628  rvh->DragTargetDragOver(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y),
629      blink::WebDragOperationEvery, 0);
630  rvh->DragTargetDrop(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y), 0);
631
632  base::string16 actual_title = title_watcher.WaitAndGetTitle();
633  EXPECT_EQ(expected_title, actual_title);
634}
635
636// This test verifies that round trip postMessage works as expected.
637// 1. The embedder posts a message 'testing123' to the guest.
638// 2. The guest receives and replies to the message using the event object's
639// source object: event.source.postMessage('foobar', '*')
640// 3. The embedder receives the message and uses the event's source
641// object to do one final reply: 'stop'
642// 4. The guest receives the final 'stop' message.
643// 5. The guest acks the 'stop' message with a 'stop_ack' message.
644// 6. The embedder changes its title to 'main guest' when it sees the 'stop_ack'
645// message.
646IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, PostMessage) {
647  const char* kTesting = "testing123";
648  const char* kEmbedderURL = "/browser_plugin_embedder.html";
649  const char* kGuestURL = "/browser_plugin_post_message_guest.html";
650  StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
651  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
652      test_embedder()->web_contents()->GetRenderViewHost());
653  {
654    const base::string16 expected_title = ASCIIToUTF16("main guest");
655    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
656                                        expected_title);
657
658    // By the time we get here 'contentWindow' should be ready because the
659    // guest has completed loading.
660    ExecuteSyncJSFunction(
661        rvh, base::StringPrintf("PostMessage('%s, false');", kTesting));
662
663    // The title will be updated to "main guest" at the last stage of the
664    // process described above.
665    base::string16 actual_title = title_watcher.WaitAndGetTitle();
666    EXPECT_EQ(expected_title, actual_title);
667  }
668}
669
670// This is the same as BrowserPluginHostTest.PostMessage but also
671// posts a message to an iframe.
672// TODO(fsamuel): This test should replace the previous test once postMessage
673// iframe targeting is fixed (see http://crbug.com/153701).
674IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DISABLED_PostMessageToIFrame) {
675  const char* kTesting = "testing123";
676  const char* kEmbedderURL = "/browser_plugin_embedder.html";
677  const char* kGuestURL = "/browser_plugin_post_message_guest.html";
678  StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
679  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
680      test_embedder()->web_contents()->GetRenderViewHost());
681  {
682    const base::string16 expected_title = ASCIIToUTF16("main guest");
683    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
684                                        expected_title);
685
686    ExecuteSyncJSFunction(
687        rvh, base::StringPrintf("PostMessage('%s, false');", kTesting));
688
689    // The title will be updated to "main guest" at the last stage of the
690    // process described above.
691    base::string16 actual_title = title_watcher.WaitAndGetTitle();
692    EXPECT_EQ(expected_title, actual_title);
693  }
694  {
695    content::TitleWatcher ready_watcher(test_embedder()->web_contents(),
696                                        ASCIIToUTF16("ready"));
697
698    RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
699        test_guest()->web_contents()->GetRenderViewHost());
700    GURL test_url = embedded_test_server()->GetURL(
701        "/browser_plugin_post_message_guest.html");
702    ExecuteSyncJSFunction(
703        guest_rvh,
704        base::StringPrintf(
705            "CreateChildFrame('%s');", test_url.spec().c_str()));
706
707    base::string16 actual_title = ready_watcher.WaitAndGetTitle();
708    EXPECT_EQ(ASCIIToUTF16("ready"), actual_title);
709
710    content::TitleWatcher iframe_watcher(test_embedder()->web_contents(),
711                                        ASCIIToUTF16("iframe"));
712    ExecuteSyncJSFunction(
713        rvh, base::StringPrintf("PostMessage('%s', true);", kTesting));
714
715    // The title will be updated to "iframe" at the last stage of the
716    // process described above.
717    actual_title = iframe_watcher.WaitAndGetTitle();
718    EXPECT_EQ(ASCIIToUTF16("iframe"), actual_title);
719  }
720}
721
722// This test verifies that if a browser plugin is hidden before navigation,
723// the guest starts off hidden.
724IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, HiddenBeforeNavigation) {
725  const char* kEmbedderURL = "/browser_plugin_embedder.html";
726  const std::string embedder_code =
727      "document.getElementById('plugin').style.visibility = 'hidden'";
728  StartBrowserPluginTest(
729      kEmbedderURL, kHTMLForGuest, true, embedder_code);
730  EXPECT_FALSE(test_guest()->visible());
731}
732
733// This test verifies that if a browser plugin is focused before navigation then
734// the guest starts off focused.
735IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusBeforeNavigation) {
736  const char* kEmbedderURL = "/browser_plugin_embedder.html";
737  const std::string embedder_code =
738      "document.getElementById('plugin').focus();";
739  StartBrowserPluginTest(
740      kEmbedderURL, kHTMLForGuest, true, embedder_code);
741  RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
742      test_guest()->web_contents()->GetRenderViewHost());
743  // Verify that the guest is focused.
744  scoped_ptr<base::Value> value =
745      content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()");
746  bool result = false;
747  ASSERT_TRUE(value->GetAsBoolean(&result));
748  EXPECT_TRUE(result);
749}
750
751IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusTracksEmbedder) {
752  const char* kEmbedderURL = "/browser_plugin_embedder.html";
753  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
754  // Blur the embedder.
755  test_embedder()->web_contents()->GetRenderViewHost()->Blur();
756  // Ensure that the guest is also blurred.
757  test_guest()->WaitForBlur();
758}
759
760// Test for regression http://crbug.com/162961.
761IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, GetRenderViewHostAtPositionTest) {
762  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
763  const std::string embedder_code =
764      base::StringPrintf("SetSize(%d, %d);", 100, 100);
765  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuestWithSize, true,
766                         embedder_code);
767  // Check for render view host at position (150, 150) that is outside the
768  // bounds of our guest, so this would respond with the render view host of the
769  // embedder.
770  test_embedder()->WaitForRenderViewHostAtPosition(150, 150);
771  ASSERT_EQ(test_embedder()->web_contents()->GetRenderViewHost(),
772            test_embedder()->last_rvh_at_position_response());
773}
774
775// This test verifies that if IME is enabled in the embedder, it is also enabled
776// in the guest.
777IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, VerifyInputMethodActive) {
778  const char* kEmbedderURL = "/browser_plugin_embedder.html";
779  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
780  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
781      test_guest()->web_contents()->GetRenderViewHost());
782  EXPECT_TRUE(rvh->input_method_active());
783}
784
785// Verify that navigating to an invalid URL (e.g. 'http:') doesn't cause
786// a crash.
787IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DoNotCrashOnInvalidNavigation) {
788  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
789  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
790  TestBrowserPluginGuestDelegate* delegate =
791      new TestBrowserPluginGuestDelegate();
792  test_guest()->SetDelegate(delegate);
793
794  const char kValidSchemeWithEmptyURL[] = "http:";
795  ExecuteSyncJSFunction(
796      test_embedder()->web_contents()->GetRenderViewHost(),
797      base::StringPrintf("SetSrc('%s');", kValidSchemeWithEmptyURL));
798  EXPECT_TRUE(delegate->load_aborted());
799  EXPECT_FALSE(delegate->load_aborted_url().is_valid());
800  EXPECT_EQ(kValidSchemeWithEmptyURL,
801            delegate->load_aborted_url().possibly_invalid_spec());
802
803  delegate->ResetStates();
804
805  // Attempt a navigation to chrome-guest://abc123, which is a valid URL. But it
806  // should be blocked because the scheme isn't web-safe or a pseudo-scheme.
807  ExecuteSyncJSFunction(
808      test_embedder()->web_contents()->GetRenderViewHost(),
809      base::StringPrintf("SetSrc('%s://abc123');", kGuestScheme));
810  EXPECT_TRUE(delegate->load_aborted());
811  EXPECT_TRUE(delegate->load_aborted_url().is_valid());
812}
813
814// Tests involving the threaded compositor.
815class BrowserPluginThreadedCompositorTest : public BrowserPluginHostTest {
816 public:
817  BrowserPluginThreadedCompositorTest() {}
818  virtual ~BrowserPluginThreadedCompositorTest() {}
819
820 protected:
821  virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE {
822    BrowserPluginHostTest::SetUpCommandLine(cmd);
823    cmd->AppendSwitch(switches::kEnableThreadedCompositing);
824
825    // http://crbug.com/327035
826    cmd->AppendSwitch(switches::kDisableDelegatedRenderer);
827  }
828};
829
830static void CompareSkBitmaps(const SkBitmap& expected_bitmap,
831                             const SkBitmap& bitmap) {
832  EXPECT_EQ(expected_bitmap.width(), bitmap.width());
833  if (expected_bitmap.width() != bitmap.width())
834    return;
835  EXPECT_EQ(expected_bitmap.height(), bitmap.height());
836  if (expected_bitmap.height() != bitmap.height())
837    return;
838  EXPECT_EQ(expected_bitmap.config(), bitmap.config());
839  if (expected_bitmap.config() != bitmap.config())
840    return;
841
842  SkAutoLockPixels expected_bitmap_lock(expected_bitmap);
843  SkAutoLockPixels bitmap_lock(bitmap);
844  int fails = 0;
845  const int kAllowableError = 2;
846  for (int i = 0; i < bitmap.width() && fails < 10; ++i) {
847    for (int j = 0; j < bitmap.height() && fails < 10; ++j) {
848      SkColor expected_color = expected_bitmap.getColor(i, j);
849      SkColor color = bitmap.getColor(i, j);
850      int expected_alpha = SkColorGetA(expected_color);
851      int alpha = SkColorGetA(color);
852      int expected_red = SkColorGetR(expected_color);
853      int red = SkColorGetR(color);
854      int expected_green = SkColorGetG(expected_color);
855      int green = SkColorGetG(color);
856      int expected_blue = SkColorGetB(expected_color);
857      int blue = SkColorGetB(color);
858      EXPECT_NEAR(expected_alpha, alpha, kAllowableError)
859          << "expected_color: " << std::hex << expected_color
860          << " color: " <<  color
861          << " Failed at " << std::dec << i << ", " << j
862          << " Failure " << ++fails;
863      EXPECT_NEAR(expected_red, red, 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_green, green, 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_blue, blue, kAllowableError)
874          << "expected_color: " << std::hex << expected_color
875          << " color: " <<  color
876          << " Failed at " << std::dec << i << ", " << j
877          << " Failure " << ++fails;
878    }
879  }
880  EXPECT_LT(fails, 10);
881}
882
883static void CompareSkBitmapAndRun(const base::Closure& callback,
884                                  const SkBitmap& expected_bitmap,
885                                  bool *result,
886                                  bool succeed,
887                                  const SkBitmap& bitmap) {
888  *result = succeed;
889  if (succeed)
890    CompareSkBitmaps(expected_bitmap, bitmap);
891  callback.Run();
892}
893
894// http://crbug.com/171744
895#if defined(OS_MACOSX)
896#define MAYBE_GetBackingStore DISABLED_GetBackingStore
897#else
898#define MAYBE_GetBackingStore GetBackingStore
899#endif
900IN_PROC_BROWSER_TEST_F(BrowserPluginThreadedCompositorTest,
901                       MAYBE_GetBackingStore) {
902  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
903  const char kHTMLForGuest[] =
904      "data:text/html,<html><style>body { background-color: red; }</style>"
905      "<body></body></html>";
906  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true,
907                         std::string("SetSize(50, 60);"));
908
909  WebContentsImpl* guest_contents = test_guest()->web_contents();
910  RenderWidgetHostImpl* guest_widget_host =
911      RenderWidgetHostImpl::From(guest_contents->GetRenderViewHost());
912
913  SkBitmap expected_bitmap;
914  expected_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 50, 60);
915  expected_bitmap.allocPixels();
916  expected_bitmap.eraseARGB(255, 255, 0, 0);  // #f00
917  bool result = false;
918  while (!result) {
919    base::RunLoop loop;
920    guest_widget_host->CopyFromBackingStore(gfx::Rect(),
921        guest_widget_host->GetView()->GetViewBounds().size(),
922        base::Bind(&CompareSkBitmapAndRun, loop.QuitClosure(), expected_bitmap,
923                   &result));
924    loop.Run();
925  }
926}
927
928// Tests input method.
929IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, InputMethod) {
930  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
931  const char kGuestHTML[] = "data:text/html,"
932      "<html><body><input id=\"input1\">"
933      "<input id=\"input2\"></body>"
934      "<script>"
935      "var i = document.getElementById(\"input1\");"
936      "i.oninput = function() {"
937      "  document.title = i.value;"
938      "}"
939      "</script>"
940      "</html>";
941  StartBrowserPluginTest(kEmbedderURL, kGuestHTML, true,
942                         "document.getElementById(\"plugin\").focus();");
943
944  RenderViewHostImpl* embedder_rvh = static_cast<RenderViewHostImpl*>(
945      test_embedder()->web_contents()->GetRenderViewHost());
946  RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
947      test_guest()->web_contents()->GetRenderViewHost());
948
949  std::vector<blink::WebCompositionUnderline> underlines;
950
951  // An input field in browser plugin guest gets focus and given some user
952  // input via IME.
953  {
954    ExecuteSyncJSFunction(guest_rvh,
955                          "document.getElementById('input1').focus();");
956    string16 expected_title = UTF8ToUTF16("InputTest123");
957    content::TitleWatcher title_watcher(test_guest()->web_contents(),
958                                        expected_title);
959    embedder_rvh->Send(
960        new ViewMsg_ImeSetComposition(
961            test_embedder()->web_contents()->GetRoutingID(),
962            expected_title,
963            underlines,
964            12, 12));
965    base::string16 actual_title = title_watcher.WaitAndGetTitle();
966    EXPECT_EQ(expected_title, actual_title);
967  }
968  // A composition is committed via IME.
969  {
970    string16 expected_title = UTF8ToUTF16("InputTest456");
971    content::TitleWatcher title_watcher(test_guest()->web_contents(),
972                                        expected_title);
973    embedder_rvh->Send(
974        new ViewMsg_ImeConfirmComposition(
975            test_embedder()->web_contents()->GetRoutingID(),
976            expected_title,
977            gfx::Range(),
978            true));
979    base::string16 actual_title = title_watcher.WaitAndGetTitle();
980    EXPECT_EQ(expected_title, actual_title);
981  }
982  // IME composition starts, but focus moves out, then the composition will
983  // be committed and get cancel msg.
984  {
985    ExecuteSyncJSFunction(guest_rvh,
986                          "document.getElementById('input1').value = '';");
987    string16 composition = UTF8ToUTF16("InputTest789");
988    content::TitleWatcher title_watcher(test_guest()->web_contents(),
989                                        composition);
990    embedder_rvh->Send(
991        new ViewMsg_ImeSetComposition(
992            test_embedder()->web_contents()->GetRoutingID(),
993            composition,
994            underlines,
995            12, 12));
996    base::string16 actual_title = title_watcher.WaitAndGetTitle();
997    EXPECT_EQ(composition, actual_title);
998    // Moving focus causes IME cancel, and the composition will be committed
999    // in input1, not in input2.
1000    ExecuteSyncJSFunction(guest_rvh,
1001                          "document.getElementById('input2').focus();");
1002    test_guest()->WaitForImeCancel();
1003    scoped_ptr<base::Value> value =
1004        content::ExecuteScriptAndGetValue(
1005            guest_rvh, "document.getElementById('input1').value");
1006    std::string result;
1007    ASSERT_TRUE(value->GetAsString(&result));
1008    EXPECT_EQ(UTF16ToUTF8(composition), result);
1009  }
1010  // Tests ExtendSelectionAndDelete message works in browser_plugin.
1011  {
1012    // Set 'InputTestABC' in input1 and put caret at 6 (after 'T').
1013    ExecuteSyncJSFunction(guest_rvh,
1014                          "var i = document.getElementById('input1');"
1015                          "i.focus();"
1016                          "i.value = 'InputTestABC';"
1017                          "i.selectionStart=6;"
1018                          "i.selectionEnd=6;");
1019    string16 expected_value = UTF8ToUTF16("InputABC");
1020    content::TitleWatcher title_watcher(test_guest()->web_contents(),
1021                                        expected_value);
1022    // Delete 'Test' in 'InputTestABC', as the caret is after 'T':
1023    // delete before 1 character ('T') and after 3 characters ('est').
1024    embedder_rvh->Send(
1025        new ViewMsg_ExtendSelectionAndDelete(
1026            test_embedder()->web_contents()->GetRoutingID(),
1027            1, 3));
1028    base::string16 actual_title = title_watcher.WaitAndGetTitle();
1029    EXPECT_EQ(expected_value, actual_title);
1030    scoped_ptr<base::Value> value =
1031        content::ExecuteScriptAndGetValue(
1032            guest_rvh, "document.getElementById('input1').value");
1033    std::string actual_value;
1034    ASSERT_TRUE(value->GetAsString(&actual_value));
1035    EXPECT_EQ(UTF16ToUTF8(expected_value), actual_value);
1036  }
1037}
1038
1039}  // namespace content
1040