1// Copyright 2013 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 "apps/app_window.h"
6#include "apps/app_window_registry.h"
7#include "base/strings/stringprintf.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/app/chrome_command_ids.h"
10#include "chrome/browser/apps/app_browsertest_util.h"
11#include "chrome/browser/chrome_content_browser_client.h"
12#include "chrome/browser/extensions/extension_test_message_listener.h"
13#include "chrome/browser/guest_view/guest_view_base.h"
14#include "chrome/browser/guest_view/guest_view_manager.h"
15#include "chrome/browser/guest_view/guest_view_manager_factory.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h"
18#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
19#include "chrome/test/base/interactive_test_utils.h"
20#include "chrome/test/base/test_launcher_utils.h"
21#include "chrome/test/base/ui_test_utils.h"
22#include "content/public/browser/notification_service.h"
23#include "content/public/browser/render_process_host.h"
24#include "content/public/browser/render_view_host.h"
25#include "content/public/browser/render_widget_host_iterator.h"
26#include "content/public/browser/render_widget_host_view.h"
27#include "content/public/browser/web_contents.h"
28#include "content/public/common/content_switches.h"
29#include "content/public/test/browser_test_utils.h"
30#include "net/test/embedded_test_server/embedded_test_server.h"
31#include "ui/base/ime/composition_text.h"
32#include "ui/base/ime/text_input_client.h"
33#include "ui/base/test/ui_controls.h"
34#include "ui/events/keycodes/keyboard_codes.h"
35
36using apps::AppWindow;
37
38class TestGuestViewManager : public GuestViewManager {
39 public:
40  explicit TestGuestViewManager(content::BrowserContext* context) :
41      GuestViewManager(context),
42      web_contents_(NULL) {}
43
44  content::WebContents* WaitForGuestCreated() {
45    if (web_contents_)
46      return web_contents_;
47
48    message_loop_runner_ = new content::MessageLoopRunner;
49    message_loop_runner_->Run();
50    return web_contents_;
51  }
52
53 private:
54  // GuestViewManager override:
55  virtual void AddGuest(int guest_instance_id,
56                        content::WebContents* guest_web_contents) OVERRIDE{
57    GuestViewManager::AddGuest(guest_instance_id, guest_web_contents);
58    web_contents_ = guest_web_contents;
59
60    if (message_loop_runner_)
61      message_loop_runner_->Quit();
62  }
63
64  content::WebContents* web_contents_;
65  scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
66};
67
68// Test factory for creating test instances of GuestViewManager.
69class TestGuestViewManagerFactory : public GuestViewManagerFactory {
70 public:
71  TestGuestViewManagerFactory() :
72      test_guest_view_manager_(NULL) {}
73
74  virtual ~TestGuestViewManagerFactory() {}
75
76  virtual GuestViewManager* CreateGuestViewManager(
77      content::BrowserContext* context) OVERRIDE {
78    return GetManager(context);
79  }
80
81  TestGuestViewManager* GetManager(content::BrowserContext* context) {
82    if (!test_guest_view_manager_) {
83      test_guest_view_manager_ = new TestGuestViewManager(context);
84    }
85    return test_guest_view_manager_;
86  }
87
88 private:
89  TestGuestViewManager* test_guest_view_manager_;
90
91  DISALLOW_COPY_AND_ASSIGN(TestGuestViewManagerFactory);
92};
93
94class WebViewInteractiveTest
95    : public extensions::PlatformAppBrowserTest {
96 public:
97  WebViewInteractiveTest()
98      : guest_web_contents_(NULL),
99        embedder_web_contents_(NULL),
100        corner_(gfx::Point()),
101        mouse_click_result_(false),
102        first_click_(true) {
103    GuestViewManager::set_factory_for_testing(&factory_);
104  }
105
106  TestGuestViewManager* GetGuestViewManager() {
107    return factory_.GetManager(browser()->profile());
108  }
109
110  void MoveMouseInsideWindowWithListener(gfx::Point point,
111                                         const std::string& message) {
112    ExtensionTestMessageListener move_listener(message, false);
113    ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
114        gfx::Point(corner_.x() + point.x(), corner_.y() + point.y())));
115    ASSERT_TRUE(move_listener.WaitUntilSatisfied());
116  }
117
118  void SendMouseClickWithListener(ui_controls::MouseButton button,
119                                  const std::string& message) {
120    ExtensionTestMessageListener listener(message, false);
121    SendMouseClick(button);
122    ASSERT_TRUE(listener.WaitUntilSatisfied());
123  }
124
125  void SendMouseClick(ui_controls::MouseButton button) {
126    SendMouseEvent(button, ui_controls::DOWN);
127    SendMouseEvent(button, ui_controls::UP);
128  }
129
130  void MoveMouseInsideWindow(const gfx::Point& point) {
131    ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
132        gfx::Point(corner_.x() + point.x(), corner_.y() + point.y())));
133  }
134
135  gfx::NativeWindow GetPlatformAppWindow() {
136    const apps::AppWindowRegistry::AppWindowList& app_windows =
137        apps::AppWindowRegistry::Get(browser()->profile())->app_windows();
138    return (*app_windows.begin())->GetNativeWindow();
139  }
140
141  void SendKeyPressToPlatformApp(ui::KeyboardCode key) {
142    ASSERT_EQ(1U, GetAppWindowCount());
143    ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
144        GetPlatformAppWindow(), key, false, false, false, false));
145  }
146
147  void SendCopyKeyPressToPlatformApp() {
148    ASSERT_EQ(1U, GetAppWindowCount());
149#if defined(OS_MACOSX)
150    // Send Cmd+C on MacOSX.
151    ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
152        GetPlatformAppWindow(), ui::VKEY_C, false, false, false, true));
153#else
154    // Send Ctrl+C on Windows and Linux/ChromeOS.
155    ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
156        GetPlatformAppWindow(), ui::VKEY_C, true, false, false, false));
157#endif
158  }
159
160  void SendStartOfLineKeyPressToPlatformApp() {
161#if defined(OS_MACOSX)
162    // Send Cmd+Left on MacOSX.
163    ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
164        GetPlatformAppWindow(), ui::VKEY_LEFT, false, false, false, true));
165#else
166    // Send Ctrl+Left on Windows and Linux/ChromeOS.
167    ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
168        GetPlatformAppWindow(), ui::VKEY_LEFT, true, false, false, false));
169#endif
170  }
171
172  void SendBackShortcutToPlatformApp() {
173#if defined(OS_MACOSX)
174    // Send Cmd+[ on MacOSX.
175    ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
176        GetPlatformAppWindow(), ui::VKEY_OEM_4, false, false, false, true));
177#else
178    // Send browser back key on Linux/Windows.
179    ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
180        GetPlatformAppWindow(), ui::VKEY_BROWSER_BACK,
181        false, false, false, false));
182#endif
183  }
184
185  void SendForwardShortcutToPlatformApp() {
186#if defined(OS_MACOSX)
187    // Send Cmd+] on MacOSX.
188    ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
189        GetPlatformAppWindow(), ui::VKEY_OEM_6, false, false, false, true));
190#else
191    // Send browser back key on Linux/Windows.
192    ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
193        GetPlatformAppWindow(), ui::VKEY_BROWSER_FORWARD,
194        false, false, false, false));
195#endif
196  }
197
198  void SendMouseEvent(ui_controls::MouseButton button,
199                      ui_controls::MouseButtonState state) {
200    if (first_click_) {
201      mouse_click_result_ = ui_test_utils::SendMouseEventsSync(button,
202                                                                state);
203      first_click_ = false;
204    } else {
205      ASSERT_EQ(mouse_click_result_, ui_test_utils::SendMouseEventsSync(
206          button, state));
207    }
208  }
209
210  enum TestServer {
211    NEEDS_TEST_SERVER,
212    NO_TEST_SERVER
213  };
214
215  scoped_ptr<ExtensionTestMessageListener> RunAppHelper(
216      const std::string& test_name,
217      const std::string& app_location,
218      TestServer test_server,
219      content::WebContents** embedder_web_contents) {
220    // For serving guest pages.
221    if ((test_server == NEEDS_TEST_SERVER) && !StartEmbeddedTestServer()) {
222      LOG(ERROR) << "FAILED TO START TEST SERVER.";
223      return scoped_ptr<ExtensionTestMessageListener>();
224    }
225
226    LoadAndLaunchPlatformApp(app_location.c_str(), "Launched");
227    if (!ui_test_utils::ShowAndFocusNativeWindow(GetPlatformAppWindow())) {
228      LOG(ERROR) << "UNABLE TO FOCUS TEST WINDOW.";
229      return scoped_ptr<ExtensionTestMessageListener>();
230    }
231
232    // Flush any pending events to make sure we start with a clean slate.
233    content::RunAllPendingInMessageLoop();
234
235    *embedder_web_contents = GetFirstAppWindowWebContents();
236
237    scoped_ptr<ExtensionTestMessageListener> done_listener(
238        new ExtensionTestMessageListener("TEST_PASSED", false));
239    done_listener->set_failure_message("TEST_FAILED");
240    if (!content::ExecuteScript(
241            *embedder_web_contents,
242            base::StringPrintf("runTest('%s')", test_name.c_str()))) {
243      LOG(ERROR) << "UNABLE TO START TEST";
244      return scoped_ptr<ExtensionTestMessageListener>();
245    }
246
247    return done_listener.Pass();
248  }
249
250  void TestHelper(const std::string& test_name,
251                  const std::string& app_location,
252                  TestServer test_server) {
253    content::WebContents* embedder_web_contents = NULL;
254    scoped_ptr<ExtensionTestMessageListener> done_listener(
255        RunAppHelper(
256            test_name, app_location, test_server, &embedder_web_contents));
257
258    ASSERT_TRUE(done_listener);
259    ASSERT_TRUE(done_listener->WaitUntilSatisfied());
260
261    guest_web_contents_ = GetGuestViewManager()->WaitForGuestCreated();
262  }
263
264  void RunTest(const std::string& app_name) {
265  }
266  void SetupTest(const std::string& app_name,
267                 const std::string& guest_url_spec) {
268    ASSERT_TRUE(StartEmbeddedTestServer());
269    GURL::Replacements replace_host;
270    std::string host_str("localhost");  // Must stay in scope with replace_host.
271    replace_host.SetHostStr(host_str);
272
273    GURL guest_url = embedded_test_server()->GetURL(guest_url_spec);
274    guest_url = guest_url.ReplaceComponents(replace_host);
275
276    ui_test_utils::UrlLoadObserver guest_observer(
277        guest_url, content::NotificationService::AllSources());
278
279    LoadAndLaunchPlatformApp(app_name.c_str(), "connected");
280
281    guest_observer.Wait();
282    content::Source<content::NavigationController> source =
283        guest_observer.source();
284    EXPECT_TRUE(source->GetWebContents()->GetRenderProcessHost()->
285        IsIsolatedGuest());
286
287    guest_web_contents_ = source->GetWebContents();
288    embedder_web_contents_ =
289        GuestViewBase::FromWebContents(guest_web_contents_)->
290            embedder_web_contents();
291
292    gfx::Rect offset = embedder_web_contents_->GetContainerBounds();
293    corner_ = gfx::Point(offset.x(), offset.y());
294
295    const testing::TestInfo* const test_info =
296            testing::UnitTest::GetInstance()->current_test_info();
297    const char* prefix = "DragDropWithinWebView";
298    if (!strncmp(test_info->name(), prefix, strlen(prefix))) {
299      // In the drag drop test we add 20px padding to the page body because on
300      // windows if we get too close to the edge of the window the resize cursor
301      // appears and we start dragging the window edge.
302      corner_.Offset(20, 20);
303    }
304  }
305
306  content::WebContents* guest_web_contents() {
307    return guest_web_contents_;
308  }
309
310  content::WebContents* embedder_web_contents() {
311    return embedder_web_contents_;
312  }
313
314  gfx::Point corner() {
315    return corner_;
316  }
317
318  void SimulateRWHMouseClick(content::RenderWidgetHost* rwh,
319                             blink::WebMouseEvent::Button button,
320                             int x,
321                             int y) {
322    blink::WebMouseEvent mouse_event;
323    mouse_event.button = button;
324    mouse_event.x = mouse_event.windowX = x;
325    mouse_event.y = mouse_event.windowY = y;
326    mouse_event.modifiers = 0;
327
328    mouse_event.type = blink::WebInputEvent::MouseDown;
329    rwh->ForwardMouseEvent(mouse_event);
330    mouse_event.type = blink::WebInputEvent::MouseUp;
331    rwh->ForwardMouseEvent(mouse_event);
332  }
333
334  class PopupCreatedObserver {
335   public:
336    explicit PopupCreatedObserver()
337        : initial_widget_count_(0),
338          last_render_widget_host_(NULL),
339          seen_new_widget_(false) {}
340
341    ~PopupCreatedObserver() {}
342
343    void Wait() {
344      size_t current_widget_count = CountWidgets();
345      if (!seen_new_widget_ &&
346          current_widget_count == initial_widget_count_ + 1) {
347        seen_new_widget_ = true;
348      }
349
350      // If we haven't seen any new widget or we get 0 size widget, we need to
351      // schedule waiting.
352      bool needs_to_schedule_wait = true;
353
354      if (seen_new_widget_) {
355        gfx::Rect popup_bounds =
356            last_render_widget_host_->GetView()->GetViewBounds();
357        if (!popup_bounds.size().IsEmpty())
358          needs_to_schedule_wait = false;
359      }
360
361      if (needs_to_schedule_wait) {
362        ScheduleWait();
363      } else {
364        // We are done.
365        if (message_loop_)
366          message_loop_->Quit();
367      }
368
369      if (!message_loop_) {
370        message_loop_ = new content::MessageLoopRunner;
371        message_loop_->Run();
372      }
373    }
374
375    void Init() { initial_widget_count_ = CountWidgets(); }
376
377    // Returns the last widget created.
378    content::RenderWidgetHost* last_render_widget_host() {
379      return last_render_widget_host_;
380    }
381
382   private:
383    void ScheduleWait() {
384      base::MessageLoop::current()->PostDelayedTask(
385          FROM_HERE,
386          base::Bind(&PopupCreatedObserver::Wait, base::Unretained(this)),
387          base::TimeDelta::FromMilliseconds(200));
388    }
389
390    size_t CountWidgets() {
391      scoped_ptr<content::RenderWidgetHostIterator> widgets(
392          content::RenderWidgetHost::GetRenderWidgetHosts());
393      size_t num_widgets = 0;
394      while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
395        if (widget->IsRenderView())
396          continue;
397        ++num_widgets;
398        last_render_widget_host_ = widget;
399      }
400      return num_widgets;
401    }
402
403    size_t initial_widget_count_;
404    content::RenderWidgetHost* last_render_widget_host_;
405    scoped_refptr<content::MessageLoopRunner> message_loop_;
406    bool seen_new_widget_;
407
408    DISALLOW_COPY_AND_ASSIGN(PopupCreatedObserver);
409  };
410
411  void WaitForTitle(const char* title) {
412    base::string16 expected_title(base::ASCIIToUTF16(title));
413    base::string16 error_title(base::ASCIIToUTF16("FAILED"));
414    content::TitleWatcher title_watcher(guest_web_contents(), expected_title);
415    title_watcher.AlsoWaitForTitle(error_title);
416    ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
417  }
418
419  void PopupTestHelper(const gfx::Point& padding) {
420    PopupCreatedObserver popup_observer;
421    popup_observer.Init();
422    // Press alt+DOWN to open popup.
423    bool alt = true;
424    content::SimulateKeyPress(
425        guest_web_contents(), ui::VKEY_DOWN, false, false, alt, false);
426    popup_observer.Wait();
427
428    content::RenderWidgetHost* popup_rwh =
429        popup_observer.last_render_widget_host();
430    gfx::Rect popup_bounds = popup_rwh->GetView()->GetViewBounds();
431
432    content::RenderViewHost* embedder_rvh =
433        GetFirstAppWindowWebContents()->GetRenderViewHost();
434    gfx::Rect embedder_bounds = embedder_rvh->GetView()->GetViewBounds();
435    gfx::Vector2d diff = popup_bounds.origin() - embedder_bounds.origin();
436    LOG(INFO) << "DIFF: x = " << diff.x() << ", y = " << diff.y();
437
438    const int left_spacing = 40 + padding.x();  // div.style.paddingLeft = 40px.
439    // div.style.paddingTop = 50px + (input box height = 26px).
440    const int top_spacing = 50 + 26 + padding.y();
441
442    // If the popup is placed within |threshold_px| of the expected position,
443    // then we consider the test as a pass.
444    const int threshold_px = 10;
445
446    EXPECT_LE(std::abs(diff.x() - left_spacing), threshold_px);
447    EXPECT_LE(std::abs(diff.y() - top_spacing), threshold_px);
448
449    // Close the popup.
450    content::SimulateKeyPress(
451        guest_web_contents(), ui::VKEY_ESCAPE, false, false, false, false);
452  }
453
454  void DragTestStep1() {
455    // Move mouse to start of text.
456    MoveMouseInsideWindow(gfx::Point(45, 8));
457    MoveMouseInsideWindow(gfx::Point(45, 9));
458    SendMouseEvent(ui_controls::LEFT, ui_controls::DOWN);
459
460    MoveMouseInsideWindow(gfx::Point(74, 12));
461    MoveMouseInsideWindow(gfx::Point(78, 12));
462
463    // Now wait a bit before moving mouse to initiate drag/drop.
464    base::MessageLoop::current()->PostDelayedTask(
465        FROM_HERE,
466        base::Bind(&WebViewInteractiveTest::DragTestStep2,
467                   base::Unretained(this)),
468        base::TimeDelta::FromMilliseconds(200));
469  }
470
471  void DragTestStep2() {
472    // Drag source over target.
473    MoveMouseInsideWindow(gfx::Point(76, 76));
474
475    // Create a second mouse over the source to trigger the drag over event.
476    MoveMouseInsideWindow(gfx::Point(76, 77));
477
478    // Release mouse to drop.
479    SendMouseEvent(ui_controls::LEFT, ui_controls::UP);
480    SendMouseClick(ui_controls::LEFT);
481
482    quit_closure_.Run();
483
484    // Note that following ExtensionTestMessageListener and ExecuteScript*
485    // call must be after we quit |quit_closure_|. Otherwise the class
486    // here won't be able to receive messages sent by chrome.test.sendMessage.
487    // This is because of the nature of drag and drop code (esp. the
488    // MessageLoop) in it.
489
490    // Now check if we got a drop and read the drop data.
491    embedder_web_contents_ = GetFirstAppWindowWebContents();
492    ExtensionTestMessageListener drop_listener("guest-got-drop", false);
493    EXPECT_TRUE(content::ExecuteScript(embedder_web_contents_,
494                                       "window.checkIfGuestGotDrop()"));
495    EXPECT_TRUE(drop_listener.WaitUntilSatisfied());
496
497    std::string last_drop_data;
498    EXPECT_TRUE(content::ExecuteScriptAndExtractString(
499                    embedder_web_contents_,
500                    "window.domAutomationController.send(getLastDropData())",
501                    &last_drop_data));
502
503    last_drop_data_ = last_drop_data;
504  }
505
506 protected:
507  TestGuestViewManagerFactory factory_;
508  content::WebContents* guest_web_contents_;
509  content::WebContents* embedder_web_contents_;
510  gfx::Point corner_;
511  bool mouse_click_result_;
512  bool first_click_;
513  // Only used in drag/drop test.
514  base::Closure quit_closure_;
515  std::string last_drop_data_;
516};
517
518// ui_test_utils::SendMouseMoveSync doesn't seem to work on OS_MACOSX, and
519// likely won't work on many other platforms as well, so for now this test
520// is for Windows and Linux only. As of Sept 17th, 2013 this test is disabled
521// on Windows due to flakines, see http://crbug.com/293445.
522
523// Disabled on Linux Aura because pointer lock does not work on Linux Aura.
524// crbug.com/341876
525
526#if defined(OS_LINUX) && !defined(USE_AURA)
527
528IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, PointerLock) {
529  SetupTest("web_view/pointer_lock",
530            "/extensions/platform_apps/web_view/pointer_lock/guest.html");
531
532  // Move the mouse over the Lock Pointer button.
533  ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
534      gfx::Point(corner().x() + 75, corner().y() + 25)));
535
536  // Click the Lock Pointer button. The first two times the button is clicked
537  // the permission API will deny the request (intentional).
538  ExtensionTestMessageListener exception_listener("request exception", false);
539  SendMouseClickWithListener(ui_controls::LEFT, "lock error");
540  ASSERT_TRUE(exception_listener.WaitUntilSatisfied());
541  SendMouseClickWithListener(ui_controls::LEFT, "lock error");
542
543  // Click the Lock Pointer button, locking the mouse to lockTarget1.
544  SendMouseClickWithListener(ui_controls::LEFT, "locked");
545
546  // Attempt to move the mouse off of the lock target, and onto lockTarget2,
547  // (which would trigger a test failure).
548  ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
549      gfx::Point(corner().x() + 74, corner().y() + 74)));
550  MoveMouseInsideWindowWithListener(gfx::Point(75, 75), "mouse-move");
551
552#if defined(OS_WIN)
553  // When the mouse is unlocked on win aura, sending a test mouse click clicks
554  // where the mouse moved to while locked. I was unable to figure out why, and
555  // since the issue only occurs with the test mouse events, just fix it with
556  // a simple workaround - moving the mouse back to where it should be.
557  // TODO(mthiesse): Fix Win Aura simulated mouse events while mouse locked.
558  MoveMouseInsideWindowWithListener(gfx::Point(75, 25), "mouse-move");
559#endif
560
561  ExtensionTestMessageListener unlocked_listener("unlocked", false);
562  // Send a key press to unlock the mouse.
563  SendKeyPressToPlatformApp(ui::VKEY_ESCAPE);
564
565  // Wait for page to receive (successful) mouse unlock response.
566  ASSERT_TRUE(unlocked_listener.WaitUntilSatisfied());
567
568  // After the second lock, guest.js sends a message to main.js to remove the
569  // webview object. main.js then removes the div containing the webview, which
570  // should unlock, and leave the mouse over the mousemove-capture-container
571  // div. We then move the mouse over that div to ensure the mouse was properly
572  // unlocked and that the div receieves the message.
573  ExtensionTestMessageListener move_captured_listener("move-captured", false);
574  move_captured_listener.set_failure_message("timeout");
575
576  // Mouse should already be over lock button (since we just unlocked), so send
577  // click to re-lock the mouse.
578  SendMouseClickWithListener(ui_controls::LEFT, "deleted");
579
580  // A mousemove event is triggered on the mousemove-capture-container element
581  // when we delete the webview container (since the mouse moves onto the
582  // element), but just in case, send an explicit mouse movement to be safe.
583  ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
584      gfx::Point(corner().x() + 50, corner().y() + 10)));
585
586  // Wait for page to receive second (successful) mouselock response.
587  bool success = move_captured_listener.WaitUntilSatisfied();
588  if (!success) {
589    fprintf(stderr, "TIMEOUT - retrying\n");
590    // About 1 in 40 tests fail to detect mouse moves at this point (why?).
591    // Sending a right click seems to fix this (why?).
592    ExtensionTestMessageListener move_listener2("move-captured", false);
593    SendMouseClick(ui_controls::RIGHT);
594    ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
595        gfx::Point(corner().x() + 51, corner().y() + 11)));
596    ASSERT_TRUE(move_listener2.WaitUntilSatisfied());
597  }
598}
599
600#endif  // defined(OS_LINUX) && !defined(USE_AURA)
601
602// Tests that if a <webview> is focused before navigation then the guest starts
603// off focused.
604IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, Focus_FocusBeforeNavigation) {
605  TestHelper("testFocusBeforeNavigation", "web_view/focus", NO_TEST_SERVER);
606}
607
608// Tests that setting focus on the <webview> sets focus on the guest.
609IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, Focus_FocusEvent) {
610  TestHelper("testFocusEvent", "web_view/focus", NO_TEST_SERVER);
611}
612
613IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, Focus_FocusTracksEmbedder) {
614  content::WebContents* embedder_web_contents = NULL;
615
616  scoped_ptr<ExtensionTestMessageListener> done_listener(
617      RunAppHelper("testFocusTracksEmbedder", "web_view/focus", NO_TEST_SERVER,
618                   &embedder_web_contents));
619  done_listener->WaitUntilSatisfied();
620
621  ExtensionTestMessageListener next_step_listener("TEST_STEP_PASSED", false);
622  next_step_listener.set_failure_message("TEST_STEP_FAILED");
623  EXPECT_TRUE(content::ExecuteScript(
624                  embedder_web_contents,
625                  "window.runCommand('testFocusTracksEmbedderRunNextStep');"));
626
627  // Blur the embedder.
628  embedder_web_contents->GetRenderViewHost()->Blur();
629  // Ensure that the guest is also blurred.
630  ASSERT_TRUE(next_step_listener.WaitUntilSatisfied());
631}
632
633IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, Focus_AdvanceFocus) {
634  content::WebContents* embedder_web_contents = NULL;
635
636  {
637    scoped_ptr<ExtensionTestMessageListener> done_listener(
638        RunAppHelper("testAdvanceFocus", "web_view/focus", NO_TEST_SERVER,
639                     &embedder_web_contents));
640    done_listener->WaitUntilSatisfied();
641  }
642
643  {
644    ExtensionTestMessageListener listener("button1-focused", false);
645    listener.set_failure_message("TEST_FAILED");
646    SimulateRWHMouseClick(embedder_web_contents->GetRenderViewHost(),
647                          blink::WebMouseEvent::ButtonLeft, 200, 20);
648    content::SimulateKeyPress(embedder_web_contents, ui::VKEY_TAB,
649        false, false, false, false);
650    ASSERT_TRUE(listener.WaitUntilSatisfied());
651  }
652
653  {
654    // Wait for button1 to be focused again, this means we were asked to
655    // move the focus to the next focusable element.
656    ExtensionTestMessageListener listener("button1-advance-focus", false);
657    listener.set_failure_message("TEST_FAILED");
658    // TODO(fsamuel): A third Tab key press should not be necessary.
659    // The <webview> will take keyboard focus but it will not focus an initial
660    // element. The initial element is dependent upon tab direction which blink
661    // does not propagate to the plugin.
662    // See http://crbug.com/147644.
663    content::SimulateKeyPress(embedder_web_contents, ui::VKEY_TAB,
664        false, false, false, false);
665    content::SimulateKeyPress(embedder_web_contents, ui::VKEY_TAB,
666        false, false, false, false);
667    content::SimulateKeyPress(embedder_web_contents, ui::VKEY_TAB,
668        false, false, false, false);
669    ASSERT_TRUE(listener.WaitUntilSatisfied());
670  }
671}
672
673// Tests that blurring <webview> also blurs the guest.
674IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, Focus_BlurEvent) {
675  TestHelper("testBlurEvent", "web_view/focus", NO_TEST_SERVER);
676}
677
678// Tests that guests receive edit commands and respond appropriately.
679IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, EditCommands) {
680  LoadAndLaunchPlatformApp("web_view/edit_commands", "connected");
681
682  ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
683      GetPlatformAppWindow()));
684
685  // Flush any pending events to make sure we start with a clean slate.
686  content::RunAllPendingInMessageLoop();
687
688  ExtensionTestMessageListener copy_listener("copy", false);
689  SendCopyKeyPressToPlatformApp();
690
691  // Wait for the guest to receive a 'copy' edit command.
692  ASSERT_TRUE(copy_listener.WaitUntilSatisfied());
693}
694
695// Tests that guests receive edit commands and respond appropriately.
696IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, EditCommandsNoMenu) {
697  SetupTest("web_view/edit_commands_no_menu",
698      "/extensions/platform_apps/web_view/edit_commands_no_menu/"
699      "guest.html");
700
701  ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
702      GetPlatformAppWindow()));
703
704  // Flush any pending events to make sure we start with a clean slate.
705  content::RunAllPendingInMessageLoop();
706
707  ExtensionTestMessageListener start_of_line_listener("StartOfLine", false);
708  SendStartOfLineKeyPressToPlatformApp();
709  // Wait for the guest to receive a 'copy' edit command.
710  ASSERT_TRUE(start_of_line_listener.WaitUntilSatisfied());
711}
712
713IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest,
714                       NewWindow_NewWindowNameTakesPrecedence) {
715  TestHelper("testNewWindowNameTakesPrecedence",
716             "web_view/newwindow",
717             NEEDS_TEST_SERVER);
718}
719
720IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest,
721                       NewWindow_WebViewNameTakesPrecedence) {
722  TestHelper("testWebViewNameTakesPrecedence",
723             "web_view/newwindow",
724             NEEDS_TEST_SERVER);
725}
726
727IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, NewWindow_NoName) {
728  TestHelper("testNoName",
729             "web_view/newwindow",
730             NEEDS_TEST_SERVER);
731}
732
733IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, NewWindow_Redirect) {
734  TestHelper("testNewWindowRedirect",
735             "web_view/newwindow",
736             NEEDS_TEST_SERVER);
737}
738
739IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, NewWindow_Close) {
740  TestHelper("testNewWindowClose",
741             "web_view/newwindow",
742             NEEDS_TEST_SERVER);
743}
744
745IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, NewWindow_ExecuteScript) {
746  TestHelper("testNewWindowExecuteScript",
747             "web_view/newwindow",
748             NEEDS_TEST_SERVER);
749}
750
751IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest,
752                       NewWindow_DeclarativeWebRequest) {
753  TestHelper("testNewWindowDeclarativeWebRequest",
754             "web_view/newwindow",
755             NEEDS_TEST_SERVER);
756}
757
758IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, NewWindow_WebRequest) {
759  TestHelper("testNewWindowWebRequest",
760             "web_view/newwindow",
761             NEEDS_TEST_SERVER);
762}
763
764// A custom elements bug needs to be addressed to enable this test:
765// See http://crbug.com/282477 for more information.
766IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest,
767                       DISABLED_NewWindow_WebRequestCloseWindow) {
768  TestHelper("testNewWindowWebRequestCloseWindow",
769             "web_view/newwindow",
770             NEEDS_TEST_SERVER);
771}
772
773IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest,
774                       NewWindow_WebRequestRemoveElement) {
775  TestHelper("testNewWindowWebRequestRemoveElement",
776             "web_view/newwindow",
777             NEEDS_TEST_SERVER);
778}
779
780// Tests that Ctrl+Click/Cmd+Click on a link fires up the newwindow API.
781IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, NewWindow_OpenInNewTab) {
782  content::WebContents* embedder_web_contents = NULL;
783
784  ExtensionTestMessageListener loaded_listener("Loaded", false);
785  scoped_ptr<ExtensionTestMessageListener> done_listener(
786    RunAppHelper("testNewWindowOpenInNewTab",
787                 "web_view/newwindow",
788                 NEEDS_TEST_SERVER,
789                 &embedder_web_contents));
790
791  loaded_listener.WaitUntilSatisfied();
792#if defined(OS_MACOSX)
793  ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
794      GetPlatformAppWindow(), ui::VKEY_RETURN,
795      false, false, false, true /* cmd */));
796#else
797  ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
798      GetPlatformAppWindow(), ui::VKEY_RETURN,
799      true /* ctrl */, false, false, false));
800#endif
801
802  // Wait for the embedder to receive a 'newwindow' event.
803  ASSERT_TRUE(done_listener->WaitUntilSatisfied());
804}
805
806
807IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, ExecuteCode) {
808  ASSERT_TRUE(RunPlatformAppTestWithArg(
809      "platform_apps/web_view/common", "execute_code")) << message_;
810}
811
812IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, PopupPositioningBasic) {
813  TestHelper("testBasic", "web_view/popup_positioning", NO_TEST_SERVER);
814  ASSERT_TRUE(guest_web_contents());
815  PopupTestHelper(gfx::Point());
816
817  // TODO(lazyboy): Move the embedder window to a random location and
818  // make sure we keep rendering popups correct in webview.
819}
820
821// Tests that moving browser plugin (without resize/UpdateRects) correctly
822// repositions popup.
823IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, PopupPositioningMoved) {
824  TestHelper("testMoved", "web_view/popup_positioning", NO_TEST_SERVER);
825  ASSERT_TRUE(guest_web_contents());
826  PopupTestHelper(gfx::Point(20, 0));
827}
828
829// Drag and drop inside a webview is currently only enabled for linux and mac,
830// but the tests don't work on anything except chromeos for now. This is because
831// of simulating mouse drag code's dependency on platforms.
832#if defined(OS_CHROMEOS)
833IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, DragDropWithinWebView) {
834  LoadAndLaunchPlatformApp("web_view/dnd_within_webview", "connected");
835  ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(GetPlatformAppWindow()));
836
837  embedder_web_contents_ = GetFirstAppWindowWebContents();
838  gfx::Rect offset = embedder_web_contents_->GetContainerBounds();
839  corner_ = gfx::Point(offset.x(), offset.y());
840
841  // In the drag drop test we add 20px padding to the page body because on
842  // windows if we get too close to the edge of the window the resize cursor
843  // appears and we start dragging the window edge.
844  corner_.Offset(20, 20);
845
846  // Flush any pending events to make sure we start with a clean slate.
847  content::RunAllPendingInMessageLoop();
848  for (;;) {
849    base::RunLoop run_loop;
850    quit_closure_ = run_loop.QuitClosure();
851    base::MessageLoop::current()->PostTask(
852        FROM_HERE,
853        base::Bind(&WebViewInteractiveTest::DragTestStep1,
854                   base::Unretained(this)));
855    run_loop.Run();
856
857    if (last_drop_data_ == "Drop me")
858      break;
859
860    LOG(INFO) << "Drag was cancelled in interactive_test, restarting drag";
861
862    // Reset state for next try.
863    ExtensionTestMessageListener reset_listener("resetStateReply", false);
864    EXPECT_TRUE(content::ExecuteScript(embedder_web_contents_,
865                                       "window.resetState()"));
866    ASSERT_TRUE(reset_listener.WaitUntilSatisfied());
867  }
868  ASSERT_EQ("Drop me", last_drop_data_);
869}
870#endif  // (defined(OS_CHROMEOS))
871
872IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, Navigation) {
873  TestHelper("testNavigation", "web_view/navigation", NO_TEST_SERVER);
874}
875
876IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, Navigation_BackForwardKeys) {
877  LoadAndLaunchPlatformApp("web_view/navigation", "Launched");
878  ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
879      GetPlatformAppWindow()));
880  // Flush any pending events to make sure we start with a clean slate.
881  content::RunAllPendingInMessageLoop();
882
883  content::WebContents* embedder_web_contents = GetFirstAppWindowWebContents();
884  ASSERT_TRUE(embedder_web_contents);
885
886  ExtensionTestMessageListener done_listener(
887      "TEST_PASSED", false);
888  done_listener.set_failure_message("TEST_FAILED");
889  ExtensionTestMessageListener ready_back_key_listener(
890      "ReadyForBackKey", false);
891  ExtensionTestMessageListener ready_forward_key_listener(
892      "ReadyForForwardKey", false);
893
894  EXPECT_TRUE(content::ExecuteScript(
895                  embedder_web_contents,
896                  "runTest('testBackForwardKeys')"));
897
898  ASSERT_TRUE(ready_back_key_listener.WaitUntilSatisfied());
899  SendBackShortcutToPlatformApp();
900
901  ASSERT_TRUE(ready_forward_key_listener.WaitUntilSatisfied());
902  SendForwardShortcutToPlatformApp();
903
904  ASSERT_TRUE(done_listener.WaitUntilSatisfied());
905}
906
907IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest,
908                       PointerLock_PointerLockLostWithFocus) {
909  TestHelper("testPointerLockLostWithFocus",
910             "web_view/pointerlock",
911             NO_TEST_SERVER);
912}
913
914// This test exercies the following scenario:
915// 1. An <input> in guest has focus.
916// 2. User takes focus to embedder by clicking e.g. an <input> in embedder.
917// 3. User brings back the focus directly to the <input> in #1.
918//
919// Now we need to make sure TextInputTypeChanged fires properly for the guest's
920// view upon step #3. We simply read the input type's state after #3 to
921// make sure it's not TEXT_INPUT_TYPE_NONE.
922IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, Focus_FocusRestored) {
923  TestHelper("testFocusRestored", "web_view/focus", NO_TEST_SERVER);
924  content::WebContents* embedder_web_contents = GetFirstAppWindowWebContents();
925  ASSERT_TRUE(embedder_web_contents);
926  ASSERT_TRUE(guest_web_contents());
927
928  // 1) We click on the guest so that we get a focus event.
929  ExtensionTestMessageListener next_step_listener("TEST_STEP_PASSED", false);
930  next_step_listener.set_failure_message("TEST_STEP_FAILED");
931  {
932    content::SimulateMouseClickAt(guest_web_contents(),
933                                  0,
934                                  blink::WebMouseEvent::ButtonLeft,
935                                  gfx::Point(10, 10));
936    EXPECT_TRUE(content::ExecuteScript(
937                    embedder_web_contents,
938                    "window.runCommand('testFocusRestoredRunNextStep', 1);"));
939  }
940  // Wait for the next step to complete.
941  ASSERT_TRUE(next_step_listener.WaitUntilSatisfied());
942
943  // 2) We click on the embedder so the guest's focus goes away and it observes
944  // a blur event.
945  next_step_listener.Reset();
946  {
947    content::SimulateMouseClickAt(embedder_web_contents,
948                                  0,
949                                  blink::WebMouseEvent::ButtonLeft,
950                                  gfx::Point(200, 20));
951    EXPECT_TRUE(content::ExecuteScript(
952                    embedder_web_contents,
953                    "window.runCommand('testFocusRestoredRunNextStep', 2);"));
954  }
955  // Wait for the next step to complete.
956  ASSERT_TRUE(next_step_listener.WaitUntilSatisfied());
957
958  // 3) We click on the guest again to bring back focus directly to the previous
959  // input element, then we ensure text_input_type is properly set.
960  next_step_listener.Reset();
961  {
962    content::SimulateMouseClickAt(guest_web_contents(),
963                                  0,
964                                  blink::WebMouseEvent::ButtonLeft,
965                                  gfx::Point(10, 10));
966    EXPECT_TRUE(content::ExecuteScript(
967                    embedder_web_contents,
968                    "window.runCommand('testFocusRestoredRunNextStep', 3)"));
969  }
970  // Wait for the next step to complete.
971  ASSERT_TRUE(next_step_listener.WaitUntilSatisfied());
972
973  // |text_input_client| is not available for mac and android.
974#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
975  ui::TextInputClient* text_input_client =
976      embedder_web_contents->GetRenderViewHost()->GetView()
977          ->GetTextInputClient();
978  ASSERT_TRUE(text_input_client);
979  ASSERT_TRUE(text_input_client->GetTextInputType() !=
980              ui::TEXT_INPUT_TYPE_NONE);
981#endif
982}
983
984// ui::TextInputClient is NULL for mac and android.
985#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
986IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, Focus_InputMethod) {
987  content::WebContents* embedder_web_contents = NULL;
988  scoped_ptr<ExtensionTestMessageListener> done_listener(
989      RunAppHelper("testInputMethod", "web_view/focus", NO_TEST_SERVER,
990                   &embedder_web_contents));
991  ASSERT_TRUE(done_listener->WaitUntilSatisfied());
992
993  ui::TextInputClient* text_input_client =
994      embedder_web_contents->GetRenderViewHost()->GetView()
995          ->GetTextInputClient();
996  ASSERT_TRUE(text_input_client);
997
998  ExtensionTestMessageListener next_step_listener("TEST_STEP_PASSED", false);
999  next_step_listener.set_failure_message("TEST_STEP_FAILED");
1000
1001  // An input element inside the <webview> gets focus and is given some
1002  // user input via IME.
1003  {
1004    ui::CompositionText composition;
1005    composition.text = base::UTF8ToUTF16("InputTest123");
1006    text_input_client->SetCompositionText(composition);
1007    EXPECT_TRUE(content::ExecuteScript(
1008                    embedder_web_contents,
1009                    "window.runCommand('testInputMethodRunNextStep', 1);"));
1010
1011    // Wait for the next step to complete.
1012    ASSERT_TRUE(next_step_listener.WaitUntilSatisfied());
1013  }
1014
1015  // A composition is committed via IME.
1016  {
1017    next_step_listener.Reset();
1018
1019    ui::CompositionText composition;
1020    composition.text = base::UTF8ToUTF16("InputTest456");
1021    text_input_client->SetCompositionText(composition);
1022    text_input_client->ConfirmCompositionText();
1023    EXPECT_TRUE(content::ExecuteScript(
1024                  embedder_web_contents,
1025                  "window.runCommand('testInputMethodRunNextStep', 2);"));
1026
1027    // Wait for the next step to complete.
1028    EXPECT_TRUE(next_step_listener.WaitUntilSatisfied());
1029  }
1030
1031  // Moving focus causes IME cancel, and the composition will be committed
1032  // in first <input> in the <webview>, not in the second <input>.
1033  {
1034    next_step_listener.Reset();
1035    ui::CompositionText composition;
1036    composition.text = base::UTF8ToUTF16("InputTest789");
1037    text_input_client->SetCompositionText(composition);
1038    EXPECT_TRUE(content::ExecuteScript(
1039                    embedder_web_contents,
1040                    "window.runCommand('testInputMethodRunNextStep', 3);"));
1041
1042    // Wait for the next step to complete.
1043    EXPECT_TRUE(next_step_listener.WaitUntilSatisfied());
1044  }
1045
1046  // Tests ExtendSelectionAndDelete message works in <webview>.
1047  {
1048    next_step_listener.Reset();
1049
1050    // At this point we have set focus on first <input> in the <webview>,
1051    // and the value it contains is 'InputTestABC' with caret set after 'T'.
1052    // Now we delete 'Test' in 'InputTestABC', as the caret is after 'T':
1053    // delete before 1 character ('T') and after 3 characters ('est').
1054    text_input_client->ExtendSelectionAndDelete(1, 3);
1055    EXPECT_TRUE(content::ExecuteScript(
1056                    embedder_web_contents,
1057                    "window.runCommand('testInputMethodRunNextStep', 4);"));
1058
1059    // Wait for the next step to complete.
1060    EXPECT_TRUE(next_step_listener.WaitUntilSatisfied());
1061  }
1062}
1063#endif
1064
1065#if defined(OS_MACOSX)
1066IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, TextSelection) {
1067  SetupTest("web_view/text_selection",
1068            "/extensions/platform_apps/web_view/text_selection/guest.html");
1069  ASSERT_TRUE(guest_web_contents());
1070  ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
1071      GetPlatformAppWindow()));
1072
1073  // Wait until guest sees a context menu, select an arbitrary item (copy).
1074  ExtensionTestMessageListener ctx_listener("MSG_CONTEXTMENU", false);
1075  ContextMenuNotificationObserver menu_observer(IDC_CONTENT_CONTEXT_COPY);
1076  SimulateRWHMouseClick(guest_web_contents()->GetRenderViewHost(),
1077                        blink::WebMouseEvent::ButtonRight, 20, 20);
1078  ASSERT_TRUE(ctx_listener.WaitUntilSatisfied());
1079
1080  // Now verify that the selection text propagates properly to RWHV.
1081  content::RenderWidgetHostView* guest_rwhv =
1082      guest_web_contents()->GetRenderWidgetHostView();
1083  ASSERT_TRUE(guest_rwhv);
1084  std::string selected_text = base::UTF16ToUTF8(guest_rwhv->GetSelectedText());
1085  ASSERT_TRUE(selected_text.size() >= 10u);
1086  ASSERT_EQ("AAAAAAAAAA", selected_text.substr(0, 10));
1087}
1088#endif
1089