1// Copyright (c) 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/bind.h"
6#include "base/bind_helpers.h"
7#include "base/files/file_path.h"
8#include "base/memory/singleton.h"
9#include "base/message_loop/message_loop.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/browser/ui/browser.h"
13#include "chrome/browser/ui/tabs/tab_strip_model.h"
14#include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
15#include "chrome/common/url_constants.h"
16#include "chrome/test/base/in_process_browser_test.h"
17#include "chrome/test/base/ui_test_utils.h"
18#include "content/public/browser/browser_context.h"
19#include "content/public/browser/render_widget_host_view.h"
20#include "content/public/browser/web_contents.h"
21#include "testing/gmock/include/gmock/gmock.h"
22#include "testing/gtest/include/gtest/gtest.h"
23#include "ui/views/controls/webview/web_dialog_view.h"
24#include "ui/views/widget/widget.h"
25#include "ui/web_dialogs/test/test_web_dialog_delegate.h"
26
27using content::BrowserContext;
28using content::WebContents;
29using testing::Eq;
30using ui::WebDialogDelegate;
31
32namespace {
33
34// Initial size of WebDialog for SizeWindow test case.
35const int kInitialWidth = 40;
36const int kInitialHeight = 40;
37
38class TestWebDialogView : public views::WebDialogView {
39 public:
40  TestWebDialogView(content::BrowserContext* context,
41                    WebDialogDelegate* delegate)
42      : views::WebDialogView(context, delegate, new ChromeWebContentsHandler),
43        should_quit_on_size_change_(false) {
44    delegate->GetDialogSize(&last_size_);
45  }
46
47  void set_should_quit_on_size_change(bool should_quit) {
48    should_quit_on_size_change_ = should_quit;
49  }
50
51 private:
52  // TODO(xiyuan): Update this when WidgetDelegate has bounds change hook.
53  virtual void SaveWindowPlacement(const gfx::Rect& bounds,
54                                   ui::WindowShowState show_state) OVERRIDE {
55    if (should_quit_on_size_change_ && last_size_ != bounds.size()) {
56      // Schedule message loop quit because we could be called while
57      // the bounds change call is on the stack and not in the nested message
58      // loop.
59      base::MessageLoop::current()->PostTask(
60          FROM_HERE,
61          base::Bind(&base::MessageLoop::Quit,
62                     base::Unretained(base::MessageLoop::current())));
63    }
64
65    last_size_ = bounds.size();
66  }
67
68  virtual void OnDialogClosed(const std::string& json_retval) OVERRIDE {
69    should_quit_on_size_change_ = false;  // No quit when we are closing.
70    views::WebDialogView::OnDialogClosed(json_retval);
71  }
72
73  // Whether we should quit message loop when size change is detected.
74  bool should_quit_on_size_change_;
75  gfx::Size last_size_;
76
77  DISALLOW_COPY_AND_ASSIGN(TestWebDialogView);
78};
79
80}  // namespace
81
82class WebDialogBrowserTest : public InProcessBrowserTest {
83 public:
84  WebDialogBrowserTest() {}
85};
86
87// Windows has some issues resizing windows. An off by one problem, and a
88// minimum size that seems too big. See http://crbug.com/52602.
89#if defined(OS_WIN)
90#define MAYBE_SizeWindow DISABLED_SizeWindow
91#else
92#define MAYBE_SizeWindow SizeWindow
93#endif
94IN_PROC_BROWSER_TEST_F(WebDialogBrowserTest, MAYBE_SizeWindow) {
95  ui::test::TestWebDialogDelegate delegate(
96      (GURL(chrome::kChromeUIChromeURLsURL)));
97  delegate.set_size(kInitialWidth, kInitialHeight);
98
99  TestWebDialogView* view =
100      new TestWebDialogView(browser()->profile(), &delegate);
101  WebContents* web_contents =
102      browser()->tab_strip_model()->GetActiveWebContents();
103  ASSERT_TRUE(web_contents != NULL);
104  views::Widget::CreateWindowWithParent(view, web_contents->GetNativeView());
105  view->GetWidget()->Show();
106
107  // TestWebDialogView should quit current message loop on size change.
108  view->set_should_quit_on_size_change(true);
109
110  gfx::Rect bounds = view->GetWidget()->GetClientAreaBoundsInScreen();
111
112  gfx::Rect set_bounds = bounds;
113  gfx::Rect actual_bounds, rwhv_bounds;
114
115  // Bigger than the default in both dimensions.
116  set_bounds.set_width(400);
117  set_bounds.set_height(300);
118
119  view->MoveContents(web_contents, set_bounds);
120  content::RunMessageLoop();  // TestWebDialogView will quit.
121  actual_bounds = view->GetWidget()->GetClientAreaBoundsInScreen();
122  EXPECT_EQ(set_bounds, actual_bounds);
123
124  rwhv_bounds =
125      view->web_contents()->GetRenderWidgetHostView()->GetViewBounds();
126  EXPECT_LT(0, rwhv_bounds.width());
127  EXPECT_LT(0, rwhv_bounds.height());
128  EXPECT_GE(set_bounds.width(), rwhv_bounds.width());
129  EXPECT_GE(set_bounds.height(), rwhv_bounds.height());
130
131  // Larger in one dimension and smaller in the other.
132  set_bounds.set_width(550);
133  set_bounds.set_height(250);
134
135  view->MoveContents(web_contents, set_bounds);
136  content::RunMessageLoop();  // TestWebDialogView will quit.
137  actual_bounds = view->GetWidget()->GetClientAreaBoundsInScreen();
138  EXPECT_EQ(set_bounds, actual_bounds);
139
140  rwhv_bounds =
141      view->web_contents()->GetRenderWidgetHostView()->GetViewBounds();
142  EXPECT_LT(0, rwhv_bounds.width());
143  EXPECT_LT(0, rwhv_bounds.height());
144  EXPECT_GE(set_bounds.width(), rwhv_bounds.width());
145  EXPECT_GE(set_bounds.height(), rwhv_bounds.height());
146
147  // Get very small.
148  const gfx::Size min_size = view->GetWidget()->GetMinimumSize();
149  EXPECT_LT(0, min_size.width());
150  EXPECT_LT(0, min_size.height());
151
152  set_bounds.set_size(min_size);
153
154  view->MoveContents(web_contents, set_bounds);
155  content::RunMessageLoop();  // TestWebDialogView will quit.
156  actual_bounds = view->GetWidget()->GetClientAreaBoundsInScreen();
157  EXPECT_EQ(set_bounds, actual_bounds);
158
159  rwhv_bounds =
160      view->web_contents()->GetRenderWidgetHostView()->GetViewBounds();
161  EXPECT_LT(0, rwhv_bounds.width());
162  EXPECT_LT(0, rwhv_bounds.height());
163  EXPECT_GE(set_bounds.width(), rwhv_bounds.width());
164  EXPECT_GE(set_bounds.height(), rwhv_bounds.height());
165
166  // Check to make sure we can't get to 0x0. First expand beyond the minimum
167  // size that was set above so that TestWebDialogView has a change to pick up.
168  set_bounds.set_height(250);
169  view->MoveContents(web_contents, set_bounds);
170  content::RunMessageLoop();  // TestWebDialogView will quit.
171  actual_bounds = view->GetWidget()->GetClientAreaBoundsInScreen();
172  EXPECT_EQ(set_bounds, actual_bounds);
173
174  // Now verify that attempts to re-size to 0x0 enforces the minimum size.
175  set_bounds.set_width(0);
176  set_bounds.set_height(0);
177
178  view->MoveContents(web_contents, set_bounds);
179  content::RunMessageLoop();  // TestWebDialogView will quit.
180  actual_bounds = view->GetWidget()->GetClientAreaBoundsInScreen();
181  EXPECT_EQ(min_size, actual_bounds.size());
182
183  // And that the render view is also non-zero.
184  rwhv_bounds =
185      view->web_contents()->GetRenderWidgetHostView()->GetViewBounds();
186  EXPECT_LT(0, rwhv_bounds.width());
187  EXPECT_LT(0, rwhv_bounds.height());
188
189  view->GetWidget()->CloseNow();
190}
191