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_manager.h"
17#include "content/browser/child_process_security_policy_impl.h"
18#include "content/browser/renderer_host/render_view_host_impl.h"
19#include "content/browser/web_contents/web_contents_impl.h"
20#include "content/common/view_messages.h"
21#include "content/public/browser/notification_service.h"
22#include "content/public/browser/notification_types.h"
23#include "content/public/browser/render_view_host_observer.h"
24#include "content/public/browser/render_widget_host_view.h"
25#include "content/public/common/content_switches.h"
26#include "content/public/common/drop_data.h"
27#include "content/public/test/browser_test_utils.h"
28#include "content/public/test/test_utils.h"
29#include "content/shell/shell.h"
30#include "content/test/content_browser_test.h"
31#include "content/test/content_browser_test_utils.h"
32#include "net/base/net_util.h"
33#include "net/test/embedded_test_server/embedded_test_server.h"
34#include "net/test/embedded_test_server/http_request.h"
35#include "net/test/embedded_test_server/http_response.h"
36#include "net/test/spawned_test_server/spawned_test_server.h"
37#include "third_party/WebKit/public/web/WebInputEvent.h"
38
39using WebKit::WebInputEvent;
40using WebKit::WebMouseEvent;
41using content::BrowserPluginEmbedder;
42using content::BrowserPluginGuest;
43using content::BrowserPluginHostFactory;
44using content::WebContentsImpl;
45
46namespace {
47
48const char kHTMLForGuest[] =
49    "data:text/html,<html><body>hello world</body></html>";
50const char kHTMLForGuestBusyLoop[] =
51    "data:text/html,<html><head><script type=\"text/javascript\">"
52    "function PauseMs(timems) {"
53    "  document.title = \"start\";"
54    "  var date = new Date();"
55    "  var currDate = null;"
56    "  do {"
57    "    currDate = new Date();"
58    "  } while (currDate - date < timems)"
59    "}"
60    "function StartPauseMs(timems) {"
61    "  setTimeout(function() { PauseMs(timems); }, 0);"
62    "}"
63    "</script></head><body></body></html>";
64const char kHTMLForGuestTouchHandler[] =
65    "data:text/html,<html><body><div id=\"touch\">With touch</div></body>"
66    "<script type=\"text/javascript\">"
67    "function handler() {}"
68    "function InstallTouchHandler() { "
69    "  document.getElementById(\"touch\").addEventListener(\"touchstart\", "
70    "     handler);"
71    "}"
72    "function UninstallTouchHandler() { "
73    "  document.getElementById(\"touch\").removeEventListener(\"touchstart\", "
74    "     handler);"
75    "}"
76    "</script></html>";
77const char kHTMLForGuestWithTitle[] =
78    "data:text/html,"
79    "<html><head><title>%s</title></head>"
80    "<body>hello world</body>"
81    "</html>";
82const char kHTMLForGuestAcceptDrag[] =
83    "data:text/html,<html><body>"
84    "<script>"
85    "function dropped() {"
86    "  document.title = \"DROPPED\";"
87    "}"
88    "</script>"
89    "<textarea id=\"text\" style=\"width:100%; height: 100%\""
90    "    ondrop=\"dropped();\">"
91    "</textarea>"
92    "</body></html>";
93const char kHTMLForGuestWithSize[] =
94    "data:text/html,"
95    "<html>"
96    "<body style=\"margin: 0px;\">"
97    "<img style=\"width: 100%; height: 400px;\"/>"
98    "</body>"
99    "</html>";
100
101std::string GetHTMLForGuestWithTitle(const std::string& title) {
102  return base::StringPrintf(kHTMLForGuestWithTitle, title.c_str());
103}
104
105}  // namespace
106
107namespace content {
108
109// Test factory for creating test instances of BrowserPluginEmbedder and
110// BrowserPluginGuest.
111class TestBrowserPluginHostFactory : public BrowserPluginHostFactory {
112 public:
113  virtual BrowserPluginGuestManager*
114      CreateBrowserPluginGuestManager() OVERRIDE {
115    guest_manager_instance_count_++;
116    if (message_loop_runner_.get())
117      message_loop_runner_->Quit();
118    return new TestBrowserPluginGuestManager();
119  }
120
121  virtual BrowserPluginGuest* CreateBrowserPluginGuest(
122      int instance_id,
123      WebContentsImpl* web_contents) OVERRIDE {
124    return new TestBrowserPluginGuest(instance_id, web_contents);
125  }
126
127  // Also keeps track of number of instances created.
128  virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder(
129      WebContentsImpl* web_contents) OVERRIDE {
130
131    return new TestBrowserPluginEmbedder(web_contents);
132  }
133
134  // Singleton getter.
135  static TestBrowserPluginHostFactory* GetInstance() {
136    return Singleton<TestBrowserPluginHostFactory>::get();
137  }
138
139  // Waits for at least one embedder to be created in the test. Returns true if
140  // we have a guest, false if waiting times out.
141  void WaitForGuestManagerCreation() {
142    // Check if already have created an instance.
143    if (guest_manager_instance_count_ > 0)
144      return;
145    // Wait otherwise.
146    message_loop_runner_ = new MessageLoopRunner();
147    message_loop_runner_->Run();
148  }
149
150 protected:
151  TestBrowserPluginHostFactory() : guest_manager_instance_count_(0) {}
152  virtual ~TestBrowserPluginHostFactory() {}
153
154 private:
155  // For Singleton.
156  friend struct DefaultSingletonTraits<TestBrowserPluginHostFactory>;
157
158  scoped_refptr<MessageLoopRunner> message_loop_runner_;
159  int guest_manager_instance_count_;
160
161  DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginHostFactory);
162};
163
164// Test factory class for browser plugin that creates guests with short hang
165// timeout.
166class TestShortHangTimeoutGuestFactory : public TestBrowserPluginHostFactory {
167 public:
168  virtual BrowserPluginGuest* CreateBrowserPluginGuest(
169      int instance_id, WebContentsImpl* web_contents) OVERRIDE {
170    BrowserPluginGuest* guest =
171        new TestBrowserPluginGuest(instance_id, web_contents);
172    guest->set_guest_hang_timeout_for_testing(TestTimeouts::tiny_timeout());
173    return guest;
174  }
175
176  // Singleton getter.
177  static TestShortHangTimeoutGuestFactory* GetInstance() {
178    return Singleton<TestShortHangTimeoutGuestFactory>::get();
179  }
180
181 protected:
182  TestShortHangTimeoutGuestFactory() {}
183  virtual ~TestShortHangTimeoutGuestFactory() {}
184
185 private:
186  // For Singleton.
187  friend struct DefaultSingletonTraits<TestShortHangTimeoutGuestFactory>;
188
189  DISALLOW_COPY_AND_ASSIGN(TestShortHangTimeoutGuestFactory);
190};
191
192// A transparent observer that can be used to verify that a RenderViewHost
193// received a specific message.
194class RenderViewHostMessageObserver : public RenderViewHostObserver {
195 public:
196  RenderViewHostMessageObserver(RenderViewHost* host,
197                                uint32 message_id)
198      : RenderViewHostObserver(host),
199        message_id_(message_id),
200        message_received_(false) {
201  }
202
203  virtual ~RenderViewHostMessageObserver() {}
204
205  void WaitUntilMessageReceived() {
206    if (message_received_)
207      return;
208    message_loop_runner_ = new MessageLoopRunner();
209    message_loop_runner_->Run();
210  }
211
212  void ResetState() {
213    message_received_ = false;
214  }
215
216  // IPC::Listener implementation.
217  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
218    if (message.type() == message_id_) {
219      message_received_ = true;
220      if (message_loop_runner_.get())
221        message_loop_runner_->Quit();
222    }
223    return false;
224  }
225
226 private:
227  scoped_refptr<MessageLoopRunner> message_loop_runner_;
228  uint32 message_id_;
229  bool message_received_;
230
231  DISALLOW_COPY_AND_ASSIGN(RenderViewHostMessageObserver);
232};
233
234class BrowserPluginHostTest : public ContentBrowserTest {
235 public:
236  BrowserPluginHostTest()
237      : test_embedder_(NULL),
238        test_guest_(NULL),
239        test_guest_manager_(NULL) {}
240
241  virtual void SetUp() OVERRIDE {
242    // Override factory to create tests instances of BrowserPlugin*.
243    content::BrowserPluginEmbedder::set_factory_for_testing(
244        TestBrowserPluginHostFactory::GetInstance());
245    content::BrowserPluginGuest::set_factory_for_testing(
246        TestBrowserPluginHostFactory::GetInstance());
247    content::BrowserPluginGuestManager::set_factory_for_testing(
248        TestBrowserPluginHostFactory::GetInstance());
249
250    // On legacy windows, the AcceptDragEvents test needs this to pass.
251#if defined(OS_WIN) && !defined(USE_AURA)
252    UseRealGLBindings();
253#endif
254
255    ContentBrowserTest::SetUp();
256  }
257  virtual void TearDown() OVERRIDE {
258    content::BrowserPluginEmbedder::set_factory_for_testing(NULL);
259    content::BrowserPluginGuest::set_factory_for_testing(NULL);
260
261    ContentBrowserTest::TearDown();
262  }
263
264  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
265    // Enable browser plugin in content_shell for running test.
266    command_line->AppendSwitch(switches::kEnableBrowserPluginForAllViewTypes);
267  }
268
269  static void SimulateSpaceKeyPress(WebContents* web_contents) {
270    SimulateKeyPress(web_contents,
271                     ui::VKEY_SPACE,
272                     false,   // control.
273                     false,   // shift.
274                     false,   // alt.
275                     false);  // command.
276  }
277
278  static void SimulateTabKeyPress(WebContents* web_contents) {
279    SimulateKeyPress(web_contents,
280                     ui::VKEY_TAB,
281                     false,   // control.
282                     false,   // shift.
283                     false,   // alt.
284                     false);  // command.
285  }
286
287  // Executes the javascript synchronously and makes sure the returned value is
288  // freed properly.
289  void ExecuteSyncJSFunction(RenderViewHost* rvh, const std::string& jscript) {
290    scoped_ptr<base::Value> value =
291        content::ExecuteScriptAndGetValue(rvh, jscript);
292  }
293
294  bool IsAttributeNull(RenderViewHost* rvh, const std::string& attribute) {
295    scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh,
296        "document.getElementById('plugin').getAttribute('" + attribute + "');");
297    return value->GetType() == Value::TYPE_NULL;
298  }
299
300  // Removes all attributes in the comma-delimited string |attributes|.
301  void RemoveAttributes(RenderViewHost* rvh, const std::string& attributes) {
302    std::vector<std::string> attributes_list;
303    base::SplitString(attributes, ',', &attributes_list);
304    std::vector<std::string>::const_iterator itr;
305    for (itr = attributes_list.begin(); itr != attributes_list.end(); ++itr) {
306      ExecuteSyncJSFunction(rvh, "document.getElementById('plugin')"
307                                 "." + *itr + " = null;");
308    }
309  }
310
311  // This helper method does the following:
312  // 1. Start the test server and navigate the shell to |embedder_url|.
313  // 2. Execute custom pre-navigation |embedder_code| if provided.
314  // 3. Navigate the guest to the |guest_url|.
315  // 4. Verify that the guest has been created and has completed loading.
316  void StartBrowserPluginTest(const std::string& embedder_url,
317                              const std::string& guest_url,
318                              bool is_guest_data_url,
319                              const std::string& embedder_code) {
320    ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
321    GURL test_url(embedded_test_server()->GetURL(embedder_url));
322    NavigateToURL(shell(), test_url);
323
324    WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>(
325        shell()->web_contents());
326    RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
327        embedder_web_contents->GetRenderViewHost());
328    // Focus the embedder.
329    rvh->Focus();
330    // Activative IME.
331    rvh->SetInputMethodActive(true);
332
333    // Allow the test to do some operations on the embedder before we perform
334    // the first navigation of the guest.
335    if (!embedder_code.empty())
336      ExecuteSyncJSFunction(rvh, embedder_code);
337
338    if (!is_guest_data_url) {
339      test_url = embedded_test_server()->GetURL(guest_url);
340      ExecuteSyncJSFunction(
341          rvh, base::StringPrintf("SetSrc('%s');", test_url.spec().c_str()));
342    } else {
343      ExecuteSyncJSFunction(
344          rvh, base::StringPrintf("SetSrc('%s');", guest_url.c_str()));
345    }
346
347    // Wait to make sure embedder is created/attached to WebContents.
348    TestBrowserPluginHostFactory::GetInstance()->WaitForGuestManagerCreation();
349
350    test_embedder_ = static_cast<TestBrowserPluginEmbedder*>(
351        embedder_web_contents->GetBrowserPluginEmbedder());
352    ASSERT_TRUE(test_embedder_);
353
354    test_guest_manager_ = static_cast<TestBrowserPluginGuestManager*>(
355        embedder_web_contents->GetBrowserPluginGuestManager());
356    ASSERT_TRUE(test_guest_manager_);
357
358    test_guest_manager_->WaitForGuestAdded();
359
360    // Verify that we have exactly one guest.
361    const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map =
362        test_guest_manager_->guest_web_contents_for_testing();
363    EXPECT_EQ(1u, instance_map.size());
364
365    WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
366        instance_map.begin()->second);
367    test_guest_ = static_cast<TestBrowserPluginGuest*>(
368        test_guest_web_contents->GetBrowserPluginGuest());
369    test_guest_->WaitForLoadStop();
370  }
371
372  TestBrowserPluginEmbedder* test_embedder() const { return test_embedder_; }
373  TestBrowserPluginGuest* test_guest() const { return test_guest_; }
374  TestBrowserPluginGuestManager* test_guest_manager() const {
375    return test_guest_manager_;
376  }
377
378 private:
379  TestBrowserPluginEmbedder* test_embedder_;
380  TestBrowserPluginGuest* test_guest_;
381  TestBrowserPluginGuestManager* test_guest_manager_;
382  DISALLOW_COPY_AND_ASSIGN(BrowserPluginHostTest);
383};
384
385// This test ensures that if guest isn't there and we resize the guest (from
386// js), it remembers the size correctly.
387//
388// Initially we load an embedder with a guest without a src attribute (which has
389// dimension 640x480), resize it to 100x200, and then we set the source to a
390// sample guest. In the end we verify that the correct size has been set.
391IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, NavigateAfterResize) {
392  const gfx::Size nxt_size = gfx::Size(100, 200);
393  const std::string embedder_code = base::StringPrintf(
394      "SetSize(%d, %d);", nxt_size.width(), nxt_size.height());
395  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
396  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, embedder_code);
397
398  // Wait for the guest to receive a damage buffer of size 100x200.
399  // This means the guest will be painted properly at that size.
400  test_guest()->WaitForDamageBufferWithSize(nxt_size);
401}
402
403IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AdvanceFocus) {
404  const char kEmbedderURL[] = "/browser_plugin_focus.html";
405  const char* kGuestURL = "/browser_plugin_focus_child.html";
406  StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
407
408  SimulateMouseClick(test_embedder()->web_contents(), 0,
409      WebKit::WebMouseEvent::ButtonLeft);
410  BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
411  // Wait until we focus into the guest.
412  test_guest()->WaitForFocus();
413
414  // TODO(fsamuel): A third Tab key press should not be necessary.
415  // The browser plugin will take keyboard focus but it will not
416  // focus an initial element. The initial element is dependent
417  // upon tab direction which WebKit does not propagate to the plugin.
418  // See http://crbug.com/147644.
419  BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
420  BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
421  BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
422  test_guest()->WaitForAdvanceFocus();
423}
424
425// This test opens a page in http and then opens another page in https, forcing
426// a RenderViewHost swap in the web_contents. We verify that the embedder in the
427// web_contents gets cleared properly.
428IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderChangedAfterSwap) {
429  net::SpawnedTestServer https_server(
430      net::SpawnedTestServer::TYPE_HTTPS,
431      net::SpawnedTestServer::kLocalhost,
432      base::FilePath(FILE_PATH_LITERAL("content/test/data")));
433  ASSERT_TRUE(https_server.Start());
434
435  // 1. Load an embedder page with one guest in it.
436  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
437  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
438
439  // 2. Navigate to a URL in https, so we trigger a RenderViewHost swap.
440  GURL test_https_url(https_server.GetURL(
441      "files/browser_plugin_title_change.html"));
442  content::WindowedNotificationObserver swap_observer(
443      content::NOTIFICATION_WEB_CONTENTS_SWAPPED,
444      content::Source<WebContents>(test_embedder()->web_contents()));
445  NavigateToURL(shell(), test_https_url);
446  swap_observer.Wait();
447
448  TestBrowserPluginEmbedder* test_embedder_after_swap =
449      static_cast<TestBrowserPluginEmbedder*>(
450          static_cast<WebContentsImpl*>(shell()->web_contents())->
451              GetBrowserPluginEmbedder());
452  // Verify we have a no embedder in web_contents (since the new page doesn't
453  // have any browser plugin).
454  ASSERT_TRUE(!test_embedder_after_swap);
455  ASSERT_NE(test_embedder(), test_embedder_after_swap);
456}
457
458// This test opens two pages in http and there is no RenderViewHost swap,
459// therefore the embedder created on first page navigation stays the same in
460// web_contents.
461IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderSameAfterNav) {
462  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
463  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
464  WebContentsImpl* embedder_web_contents = test_embedder()->web_contents();
465
466  // Navigate to another page in same host and port, so RenderViewHost swap
467  // does not happen and existing embedder doesn't change in web_contents.
468  GURL test_url_new(embedded_test_server()->GetURL(
469      "/browser_plugin_title_change.html"));
470  const string16 expected_title = ASCIIToUTF16("done");
471  content::TitleWatcher title_watcher(shell()->web_contents(), expected_title);
472  NavigateToURL(shell(), test_url_new);
473  LOG(INFO) << "Start waiting for title";
474  string16 actual_title = title_watcher.WaitAndGetTitle();
475  EXPECT_EQ(expected_title, actual_title);
476  LOG(INFO) << "Done navigating to second page";
477
478  TestBrowserPluginEmbedder* test_embedder_after_nav =
479      static_cast<TestBrowserPluginEmbedder*>(
480          embedder_web_contents->GetBrowserPluginEmbedder());
481  // Embedder must not change in web_contents.
482  ASSERT_EQ(test_embedder_after_nav, test_embedder());
483}
484
485// This test verifies that hiding the embedder also hides the guest.
486IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, BrowserPluginVisibilityChanged) {
487  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
488  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
489
490  // Hide the Browser Plugin.
491  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
492      test_embedder()->web_contents()->GetRenderViewHost());
493  ExecuteSyncJSFunction(
494      rvh, "document.getElementById('plugin').style.visibility = 'hidden'");
495
496  // Make sure that the guest is hidden.
497  test_guest()->WaitUntilHidden();
498}
499
500IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderVisibilityChanged) {
501  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
502  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
503
504  // Hide the embedder.
505  test_embedder()->web_contents()->WasHidden();
506
507  // Make sure that hiding the embedder also hides the guest.
508  test_guest()->WaitUntilHidden();
509}
510
511// Verifies that installing/uninstalling touch-event handlers in the guest
512// plugin correctly updates the touch-event handling state in the embedder.
513IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AcceptTouchEvents) {
514  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
515  StartBrowserPluginTest(
516      kEmbedderURL, kHTMLForGuestTouchHandler, true, std::string());
517
518  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
519      test_embedder()->web_contents()->GetRenderViewHost());
520  // The embedder should not have any touch event handlers at this point.
521  EXPECT_FALSE(rvh->has_touch_handler());
522
523  // Install the touch handler in the guest. This should cause the embedder to
524  // start listening for touch events too.
525  RenderViewHostMessageObserver observer(rvh,
526      ViewHostMsg_HasTouchEventHandlers::ID);
527  ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(),
528                        "InstallTouchHandler();");
529  observer.WaitUntilMessageReceived();
530  EXPECT_TRUE(rvh->has_touch_handler());
531
532  // Uninstalling the touch-handler in guest should cause the embedder to stop
533  // listening for touch events.
534  observer.ResetState();
535  ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(),
536                        "UninstallTouchHandler();");
537  observer.WaitUntilMessageReceived();
538  EXPECT_FALSE(rvh->has_touch_handler());
539}
540
541// This tests verifies that reloading the embedder does not crash the browser
542// and that the guest is reset.
543IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, ReloadEmbedder) {
544  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
545  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
546  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
547      test_embedder()->web_contents()->GetRenderViewHost());
548
549  // Change the title of the page to 'modified' so that we know that
550  // the page has successfully reloaded when it goes back to 'embedder'
551  // in the next step.
552  {
553    const string16 expected_title = ASCIIToUTF16("modified");
554    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
555                                        expected_title);
556
557    ExecuteSyncJSFunction(rvh,
558                          base::StringPrintf("SetTitle('%s');", "modified"));
559
560    string16 actual_title = title_watcher.WaitAndGetTitle();
561    EXPECT_EQ(expected_title, actual_title);
562  }
563
564  // Reload the embedder page, and verify that the reload was successful.
565  // Then navigate the guest to verify that the browser process does not crash.
566  {
567    const string16 expected_title = ASCIIToUTF16("embedder");
568    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
569                                        expected_title);
570
571    test_embedder()->web_contents()->GetController().Reload(false);
572    string16 actual_title = title_watcher.WaitAndGetTitle();
573    EXPECT_EQ(expected_title, actual_title);
574
575    ExecuteSyncJSFunction(
576        test_embedder()->web_contents()->GetRenderViewHost(),
577        base::StringPrintf("SetSrc('%s');", kHTMLForGuest));
578    test_guest_manager()->WaitForGuestAdded();
579
580    const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map =
581        test_guest_manager()->guest_web_contents_for_testing();
582    WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
583        instance_map.begin()->second);
584    TestBrowserPluginGuest* new_test_guest =
585        static_cast<TestBrowserPluginGuest*>(
586          test_guest_web_contents->GetBrowserPluginGuest());
587    ASSERT_TRUE(new_test_guest != NULL);
588
589    // Wait for the guest to send an UpdateRectMsg, meaning it is ready.
590    new_test_guest->WaitForUpdateRectMsg();
591  }
592}
593
594// Always failing in the win7_aura try bot.  See http://crbug.com/181107.
595#if defined(OS_WIN) && defined(USE_AURA)
596#define MAYBE_AcceptDragEvents DISABLED_AcceptDragEvents
597#else
598#define MAYBE_AcceptDragEvents AcceptDragEvents
599#endif
600
601// Tests that a drag-n-drop over the browser plugin in the embedder happens
602// correctly.
603IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_AcceptDragEvents) {
604  const char kEmbedderURL[] = "/browser_plugin_dragging.html";
605  StartBrowserPluginTest(
606      kEmbedderURL, kHTMLForGuestAcceptDrag, true, std::string());
607
608  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
609      test_embedder()->web_contents()->GetRenderViewHost());
610
611  // Get a location in the embedder outside of the plugin.
612  base::ListValue *start, *end;
613  scoped_ptr<base::Value> value =
614      content::ExecuteScriptAndGetValue(rvh, "dragLocation()");
615  ASSERT_TRUE(value->GetAsList(&start) && start->GetSize() == 2);
616  double start_x, start_y;
617  ASSERT_TRUE(start->GetDouble(0, &start_x) && start->GetDouble(1, &start_y));
618
619  // Get a location in the embedder that falls inside the plugin.
620  value = content::ExecuteScriptAndGetValue(rvh, "dropLocation()");
621  ASSERT_TRUE(value->GetAsList(&end) && end->GetSize() == 2);
622  double end_x, end_y;
623  ASSERT_TRUE(end->GetDouble(0, &end_x) && end->GetDouble(1, &end_y));
624
625  DropData drop_data;
626  GURL url = GURL("https://www.domain.com/index.html");
627  drop_data.url = url;
628
629  // Pretend that the URL is being dragged over the embedder. Start the drag
630  // from outside the plugin, then move the drag inside the plugin and drop.
631  // This should trigger appropriate messages from the embedder to the guest,
632  // and end with a drop on the guest. The guest changes title when a drop
633  // happens.
634  const string16 expected_title = ASCIIToUTF16("DROPPED");
635  content::TitleWatcher title_watcher(test_guest()->web_contents(),
636      expected_title);
637
638  rvh->DragTargetDragEnter(drop_data, gfx::Point(start_x, start_y),
639      gfx::Point(start_x, start_y), WebKit::WebDragOperationEvery, 0);
640  rvh->DragTargetDragOver(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y),
641      WebKit::WebDragOperationEvery, 0);
642  rvh->DragTargetDrop(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y), 0);
643
644  string16 actual_title = title_watcher.WaitAndGetTitle();
645  EXPECT_EQ(expected_title, actual_title);
646}
647
648// This test verifies that round trip postMessage works as expected.
649// 1. The embedder posts a message 'testing123' to the guest.
650// 2. The guest receives and replies to the message using the event object's
651// source object: event.source.postMessage('foobar', '*')
652// 3. The embedder receives the message and uses the event's source
653// object to do one final reply: 'stop'
654// 4. The guest receives the final 'stop' message.
655// 5. The guest acks the 'stop' message with a 'stop_ack' message.
656// 6. The embedder changes its title to 'main guest' when it sees the 'stop_ack'
657// message.
658IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, PostMessage) {
659  const char* kTesting = "testing123";
660  const char* kEmbedderURL = "/browser_plugin_embedder.html";
661  const char* kGuestURL = "/browser_plugin_post_message_guest.html";
662  StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
663  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
664      test_embedder()->web_contents()->GetRenderViewHost());
665  {
666    const string16 expected_title = ASCIIToUTF16("main guest");
667    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
668                                        expected_title);
669
670    // By the time we get here 'contentWindow' should be ready because the
671    // guest has completed loading.
672    ExecuteSyncJSFunction(
673        rvh, base::StringPrintf("PostMessage('%s, false');", kTesting));
674
675    // The title will be updated to "main guest" at the last stage of the
676    // process described above.
677    string16 actual_title = title_watcher.WaitAndGetTitle();
678    EXPECT_EQ(expected_title, actual_title);
679  }
680}
681
682// This is the same as BrowserPluginHostTest.PostMessage but also
683// posts a message to an iframe.
684// TODO(fsamuel): This test should replace the previous test once postMessage
685// iframe targeting is fixed (see http://crbug.com/153701).
686IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DISABLED_PostMessageToIFrame) {
687  const char* kTesting = "testing123";
688  const char* kEmbedderURL = "/browser_plugin_embedder.html";
689  const char* kGuestURL = "/browser_plugin_post_message_guest.html";
690  StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
691  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
692      test_embedder()->web_contents()->GetRenderViewHost());
693  {
694    const string16 expected_title = ASCIIToUTF16("main guest");
695    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
696                                        expected_title);
697
698    ExecuteSyncJSFunction(
699        rvh, base::StringPrintf("PostMessage('%s, false');", kTesting));
700
701    // The title will be updated to "main guest" at the last stage of the
702    // process described above.
703    string16 actual_title = title_watcher.WaitAndGetTitle();
704    EXPECT_EQ(expected_title, actual_title);
705  }
706  {
707    content::TitleWatcher ready_watcher(test_embedder()->web_contents(),
708                                        ASCIIToUTF16("ready"));
709
710    RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
711        test_guest()->web_contents()->GetRenderViewHost());
712    GURL test_url = embedded_test_server()->GetURL(
713        "/browser_plugin_post_message_guest.html");
714    ExecuteSyncJSFunction(
715        guest_rvh,
716        base::StringPrintf(
717            "CreateChildFrame('%s');", test_url.spec().c_str()));
718
719    string16 actual_title = ready_watcher.WaitAndGetTitle();
720    EXPECT_EQ(ASCIIToUTF16("ready"), actual_title);
721
722    content::TitleWatcher iframe_watcher(test_embedder()->web_contents(),
723                                        ASCIIToUTF16("iframe"));
724    ExecuteSyncJSFunction(
725        rvh, base::StringPrintf("PostMessage('%s', true);", kTesting));
726
727    // The title will be updated to "iframe" at the last stage of the
728    // process described above.
729    actual_title = iframe_watcher.WaitAndGetTitle();
730    EXPECT_EQ(ASCIIToUTF16("iframe"), actual_title);
731  }
732}
733
734// This test verifies that if a browser plugin is hidden before navigation,
735// the guest starts off hidden.
736IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, HiddenBeforeNavigation) {
737  const char* kEmbedderURL = "/browser_plugin_embedder.html";
738  const std::string embedder_code =
739      "document.getElementById('plugin').style.visibility = 'hidden'";
740  StartBrowserPluginTest(
741      kEmbedderURL, kHTMLForGuest, true, embedder_code);
742  EXPECT_FALSE(test_guest()->visible());
743}
744
745// This test verifies that if a browser plugin is focused before navigation then
746// the guest starts off focused.
747IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusBeforeNavigation) {
748  const char* kEmbedderURL = "/browser_plugin_embedder.html";
749  const std::string embedder_code =
750      "document.getElementById('plugin').focus();";
751  StartBrowserPluginTest(
752      kEmbedderURL, kHTMLForGuest, true, embedder_code);
753  RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
754      test_guest()->web_contents()->GetRenderViewHost());
755  // Verify that the guest is focused.
756  scoped_ptr<base::Value> value =
757      content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()");
758  bool result = false;
759  ASSERT_TRUE(value->GetAsBoolean(&result));
760  EXPECT_TRUE(result);
761}
762
763IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusTracksEmbedder) {
764  const char* kEmbedderURL = "/browser_plugin_embedder.html";
765  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
766  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
767      test_embedder()->web_contents()->GetRenderViewHost());
768  RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
769      test_guest()->web_contents()->GetRenderViewHost());
770  {
771    // Focus the BrowserPlugin. This will have the effect of also focusing the
772    // current guest.
773    ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').focus();");
774    // Verify that key presses go to the guest.
775    SimulateSpaceKeyPress(test_embedder()->web_contents());
776    test_guest()->WaitForInput();
777    // Verify that the guest is focused.
778    scoped_ptr<base::Value> value =
779        content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()");
780    bool result = false;
781    ASSERT_TRUE(value->GetAsBoolean(&result));
782    EXPECT_TRUE(result);
783  }
784  // Blur the embedder.
785  test_embedder()->web_contents()->GetRenderViewHost()->Blur();
786  test_guest()->WaitForBlur();
787}
788
789// This test verifies that if a browser plugin is in autosize mode before
790// navigation then the guest starts auto-sized.
791IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AutoSizeBeforeNavigation) {
792  const char* kEmbedderURL = "/browser_plugin_embedder.html";
793  const std::string embedder_code =
794      "document.getElementById('plugin').minwidth = 300;"
795      "document.getElementById('plugin').minheight = 200;"
796      "document.getElementById('plugin').maxwidth = 600;"
797      "document.getElementById('plugin').maxheight = 400;"
798      "document.getElementById('plugin').autosize = true;";
799  StartBrowserPluginTest(
800      kEmbedderURL, kHTMLForGuestWithSize, true, embedder_code);
801  // Verify that the guest has been auto-sized.
802  test_guest()->WaitForViewSize(gfx::Size(300, 400));
803}
804
805// This test verifies that enabling autosize resizes the guest and triggers
806// a 'sizechanged' event.
807IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AutoSizeAfterNavigation) {
808  const char* kEmbedderURL = "/browser_plugin_embedder.html";
809  StartBrowserPluginTest(
810      kEmbedderURL, kHTMLForGuestWithSize, true, std::string());
811  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
812      test_embedder()->web_contents()->GetRenderViewHost());
813
814  {
815    const string16 expected_title = ASCIIToUTF16("AutoSize(300, 400)");
816    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
817                                        expected_title);
818    ExecuteSyncJSFunction(
819        rvh,
820        "document.getElementById('plugin').minwidth = 300;"
821        "document.getElementById('plugin').minheight = 200;"
822        "document.getElementById('plugin').maxwidth = 600;"
823        "document.getElementById('plugin').maxheight = 400;"
824        "document.getElementById('plugin').autosize = true;");
825    string16 actual_title = title_watcher.WaitAndGetTitle();
826    EXPECT_EQ(expected_title, actual_title);
827  }
828  {
829    // Change the minwidth and verify that it causes relayout.
830    const string16 expected_title = ASCIIToUTF16("AutoSize(350, 400)");
831    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
832                                        expected_title);
833    ExecuteSyncJSFunction(
834        rvh, "document.getElementById('plugin').minwidth = 350;");
835    string16 actual_title = title_watcher.WaitAndGetTitle();
836    EXPECT_EQ(expected_title, actual_title);
837  }
838  {
839    // Turn off autoSize and verify that the guest resizes to fit the container.
840    ExecuteSyncJSFunction(
841        rvh, "document.getElementById('plugin').autosize = null;");
842    test_guest()->WaitForViewSize(gfx::Size(640, 480));
843  }
844}
845
846// Test for regression http://crbug.com/162961.
847IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, GetRenderViewHostAtPositionTest) {
848  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
849  const std::string embedder_code =
850      base::StringPrintf("SetSize(%d, %d);", 100, 100);
851  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuestWithSize, true,
852                         embedder_code);
853  // Check for render view host at position (150, 150) that is outside the
854  // bounds of our guest, so this would respond with the render view host of the
855  // embedder.
856  test_embedder()->WaitForRenderViewHostAtPosition(150, 150);
857  ASSERT_EQ(test_embedder()->web_contents()->GetRenderViewHost(),
858            test_embedder()->last_rvh_at_position_response());
859}
860
861// This test verifies that all autosize attributes can be removed
862// without crashing the plugin, or throwing errors.
863IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, RemoveAutosizeAttributes) {
864  const char* kEmbedderURL = "/browser_plugin_embedder.html";
865  const std::string embedder_code =
866      "document.getElementById('plugin').minwidth = 300;"
867      "document.getElementById('plugin').minheight = 200;"
868      "document.getElementById('plugin').maxwidth = 600;"
869      "document.getElementById('plugin').maxheight = 400;"
870      "document.getElementById('plugin').name = 'name';"
871      "document.getElementById('plugin').src = 'foo';"
872      "document.getElementById('plugin').autosize = '';";
873  StartBrowserPluginTest(
874      kEmbedderURL, kHTMLForGuestWithSize, true, embedder_code);
875  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
876      test_embedder()->web_contents()->GetRenderViewHost());
877  RemoveAttributes(rvh, "maxheight, maxwidth, minheight, minwidth, autosize");
878
879  // Verify that the guest resizes to fit the container (and hasn't crashed).
880  test_guest()->WaitForViewSize(gfx::Size(640, 480));
881  EXPECT_TRUE(IsAttributeNull(rvh, "maxheight"));
882  EXPECT_TRUE(IsAttributeNull(rvh, "maxwidth"));
883  EXPECT_TRUE(IsAttributeNull(rvh, "minheight"));
884  EXPECT_TRUE(IsAttributeNull(rvh, "minwidth"));
885  EXPECT_TRUE(IsAttributeNull(rvh, "autosize"));
886}
887
888// This test verifies that autosize works when some of the parameters are unset.
889IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, PartialAutosizeAttributes) {
890  const char* kEmbedderURL = "/browser_plugin_embedder.html";
891  const std::string embedder_code =
892      "document.getElementById('plugin').minwidth = 300;"
893      "document.getElementById('plugin').minheight = 200;"
894      "document.getElementById('plugin').maxwidth = 700;"
895      "document.getElementById('plugin').maxheight = 600;"
896      "document.getElementById('plugin').autosize = '';";
897  StartBrowserPluginTest(
898      kEmbedderURL, kHTMLForGuestWithSize, true, embedder_code);
899  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
900      test_embedder()->web_contents()->GetRenderViewHost());
901  {
902    // Remove an autosize attribute and verify that it causes relayout.
903    const string16 expected_title = ASCIIToUTF16("AutoSize(640, 400)");
904    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
905                                        expected_title);
906    RemoveAttributes(rvh, "minwidth");
907    string16 actual_title = title_watcher.WaitAndGetTitle();
908    EXPECT_EQ(expected_title, actual_title);
909  }
910  {
911    // Remove an autosize attribute and verify that it causes relayout.
912    // Also tests that when minwidth > maxwidth, minwidth = maxwidth.
913    const string16 expected_title = ASCIIToUTF16("AutoSize(700, 480)");
914    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
915                                        expected_title);
916    RemoveAttributes(rvh, "maxheight");
917    ExecuteSyncJSFunction(
918        rvh, "document.getElementById('plugin').minwidth = 800;"
919             "document.getElementById('plugin').minheight = 800;");
920    string16 actual_title = title_watcher.WaitAndGetTitle();
921    EXPECT_EQ(expected_title, actual_title);
922  }
923  {
924    // Remove maxwidth and make sure the size returns to plugin size.
925    const string16 expected_title = ASCIIToUTF16("AutoSize(640, 480)");
926    content::TitleWatcher title_watcher(test_embedder()->web_contents(),
927                                        expected_title);
928    RemoveAttributes(rvh, "maxwidth");
929    string16 actual_title = title_watcher.WaitAndGetTitle();
930    EXPECT_EQ(expected_title, actual_title);
931  }
932}
933
934// This test verifies that if IME is enabled in the embedder, it is also enabled
935// in the guest.
936IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, VerifyInputMethodActive) {
937  const char* kEmbedderURL = "/browser_plugin_embedder.html";
938  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
939  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
940      test_guest()->web_contents()->GetRenderViewHost());
941  EXPECT_TRUE(rvh->input_method_active());
942}
943
944}  // namespace content
945