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 "chrome/browser/ui/views/frame/browser_view.h"
6
7#include "base/prefs/pref_service.h"
8#include "chrome/browser/devtools/devtools_window_testing.h"
9#include "chrome/browser/ui/browser.h"
10#include "chrome/browser/ui/browser_tabstrip.h"
11#include "chrome/browser/ui/tabs/tab_strip_model.h"
12#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
13#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view_observer.h"
14#include "chrome/common/url_constants.h"
15#include "chrome/test/base/in_process_browser_test.h"
16#include "chrome/test/base/ui_test_utils.h"
17#include "components/bookmarks/common/bookmark_pref_names.h"
18#include "content/public/browser/invalidate_type.h"
19#include "content/public/browser/web_contents.h"
20#include "content/public/browser/web_contents_observer.h"
21
22class BrowserViewTest : public InProcessBrowserTest {
23 public:
24  BrowserViewTest() : InProcessBrowserTest(), devtools_(NULL) {}
25
26 protected:
27  BrowserView* browser_view() {
28    return BrowserView::GetBrowserViewForBrowser(browser());
29  }
30
31  views::WebView* devtools_web_view() {
32    return browser_view()->GetDevToolsWebViewForTest();
33  }
34
35  views::WebView* contents_web_view() {
36    return browser_view()->GetContentsWebViewForTest();
37  }
38
39  void OpenDevToolsWindow(bool docked) {
40    devtools_ =
41        DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), docked);
42  }
43
44  void CloseDevToolsWindow() {
45    DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_);
46  }
47
48  void SetDevToolsBounds(const gfx::Rect& bounds) {
49    DevToolsWindowTesting::Get(devtools_)->SetInspectedPageBounds(bounds);
50  }
51
52  DevToolsWindow* devtools_;
53
54 private:
55  DISALLOW_COPY_AND_ASSIGN(BrowserViewTest);
56};
57
58namespace {
59
60// Used to simulate scenario in a crash. When WebContentsDestroyed() is invoked
61// updates the navigation state of another tab.
62class TestWebContentsObserver : public content::WebContentsObserver {
63 public:
64  TestWebContentsObserver(content::WebContents* source,
65                          content::WebContents* other)
66      : content::WebContentsObserver(source),
67        other_(other) {}
68  virtual ~TestWebContentsObserver() {}
69
70  virtual void WebContentsDestroyed() OVERRIDE {
71    other_->NotifyNavigationStateChanged(static_cast<content::InvalidateTypes>(
72        content::INVALIDATE_TYPE_URL | content::INVALIDATE_TYPE_LOAD));
73  }
74
75 private:
76  content::WebContents* other_;
77
78  DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver);
79};
80
81}  // namespace
82
83// Verifies don't crash when CloseNow() is invoked with two tabs in a browser.
84// Additionally when one of the tabs is destroyed NotifyNavigationStateChanged()
85// is invoked on the other.
86IN_PROC_BROWSER_TEST_F(BrowserViewTest, CloseWithTabs) {
87  Browser* browser2 =
88      new Browser(Browser::CreateParams(browser()->profile(),
89                                        browser()->host_desktop_type()));
90  chrome::AddTabAt(browser2, GURL(), -1, true);
91  chrome::AddTabAt(browser2, GURL(), -1, true);
92  TestWebContentsObserver observer(
93      browser2->tab_strip_model()->GetWebContentsAt(0),
94      browser2->tab_strip_model()->GetWebContentsAt(1));
95  BrowserView::GetBrowserViewForBrowser(browser2)->GetWidget()->CloseNow();
96}
97
98// Same as CloseWithTabs, but activates the first tab, which is the first tab
99// BrowserView will destroy.
100IN_PROC_BROWSER_TEST_F(BrowserViewTest, CloseWithTabsStartWithActive) {
101  Browser* browser2 =
102      new Browser(Browser::CreateParams(browser()->profile(),
103                                        browser()->host_desktop_type()));
104  chrome::AddTabAt(browser2, GURL(), -1, true);
105  chrome::AddTabAt(browser2, GURL(), -1, true);
106  browser2->tab_strip_model()->ActivateTabAt(0, true);
107  TestWebContentsObserver observer(
108      browser2->tab_strip_model()->GetWebContentsAt(0),
109      browser2->tab_strip_model()->GetWebContentsAt(1));
110  BrowserView::GetBrowserViewForBrowser(browser2)->GetWidget()->CloseNow();
111}
112
113// Verifies that page and devtools WebViews are being correctly layed out
114// when DevTools is opened/closed/updated/undocked.
115IN_PROC_BROWSER_TEST_F(BrowserViewTest, DevToolsUpdatesBrowserWindow) {
116  gfx::Rect full_bounds =
117      browser_view()->GetContentsContainerForTest()->GetLocalBounds();
118  gfx::Rect small_bounds(10, 20, 30, 40);
119
120  browser_view()->UpdateDevTools();
121  EXPECT_FALSE(devtools_web_view()->web_contents());
122  EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
123  EXPECT_EQ(full_bounds, contents_web_view()->bounds());
124
125  // Docked.
126  OpenDevToolsWindow(true);
127  EXPECT_TRUE(devtools_web_view()->web_contents());
128  EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
129
130  SetDevToolsBounds(small_bounds);
131  EXPECT_TRUE(devtools_web_view()->web_contents());
132  EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
133  EXPECT_EQ(small_bounds, contents_web_view()->bounds());
134
135  browser_view()->UpdateDevTools();
136  EXPECT_TRUE(devtools_web_view()->web_contents());
137  EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
138  EXPECT_EQ(small_bounds, contents_web_view()->bounds());
139
140  CloseDevToolsWindow();
141  EXPECT_FALSE(devtools_web_view()->web_contents());
142  EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
143  EXPECT_EQ(full_bounds, contents_web_view()->bounds());
144
145  browser_view()->UpdateDevTools();
146  EXPECT_FALSE(devtools_web_view()->web_contents());
147  EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
148  EXPECT_EQ(full_bounds, contents_web_view()->bounds());
149
150  // Undocked.
151  OpenDevToolsWindow(false);
152  EXPECT_TRUE(devtools_web_view()->web_contents());
153  EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
154
155  SetDevToolsBounds(small_bounds);
156  EXPECT_TRUE(devtools_web_view()->web_contents());
157  EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
158  EXPECT_EQ(small_bounds, contents_web_view()->bounds());
159
160  browser_view()->UpdateDevTools();
161  EXPECT_TRUE(devtools_web_view()->web_contents());
162  EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
163  EXPECT_EQ(small_bounds, contents_web_view()->bounds());
164
165  CloseDevToolsWindow();
166  EXPECT_FALSE(devtools_web_view()->web_contents());
167  EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
168  EXPECT_EQ(full_bounds, contents_web_view()->bounds());
169
170  browser_view()->UpdateDevTools();
171  EXPECT_FALSE(devtools_web_view()->web_contents());
172  EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
173  EXPECT_EQ(full_bounds, contents_web_view()->bounds());
174}
175
176class BookmarkBarViewObserverImpl : public BookmarkBarViewObserver {
177 public:
178  BookmarkBarViewObserverImpl() : change_count_(0) {
179  }
180
181  int change_count() const { return change_count_; }
182  void clear_change_count() { change_count_ = 0; }
183
184  // BookmarkBarViewObserver:
185  virtual void OnBookmarkBarVisibilityChanged() OVERRIDE {
186    change_count_++;
187  }
188
189 private:
190  int change_count_;
191
192  DISALLOW_COPY_AND_ASSIGN(BookmarkBarViewObserverImpl);
193};
194
195// Verifies we don't unnecessarily change the visibility of the BookmarkBarView.
196IN_PROC_BROWSER_TEST_F(BrowserViewTest, AvoidUnnecessaryVisibilityChanges) {
197  // Create two tabs, the first empty and the second the ntp. Make it so the
198  // BookmarkBarView isn't shown (meaning it'll only be shown when on the ntp).
199  browser()->profile()->GetPrefs()->SetBoolean(
200      bookmarks::prefs::kShowBookmarkBar, false);
201  GURL new_tab_url(chrome::kChromeUINewTabURL);
202  chrome::AddTabAt(browser(), GURL(), -1, true);
203  ui_test_utils::NavigateToURL(browser(), new_tab_url);
204
205  ASSERT_TRUE(browser_view()->bookmark_bar());
206  BookmarkBarViewObserverImpl observer;
207  BookmarkBarView* bookmark_bar = browser_view()->bookmark_bar();
208  bookmark_bar->AddObserver(&observer);
209  EXPECT_TRUE(bookmark_bar->visible());
210
211  // Go to empty tab. Bookmark bar should hide.
212  browser()->tab_strip_model()->ActivateTabAt(0, true);
213  EXPECT_FALSE(bookmark_bar->visible());
214  EXPECT_EQ(1, observer.change_count());
215  observer.clear_change_count();
216
217  // Go to ntp tab. Bookmark bar should show.
218  browser()->tab_strip_model()->ActivateTabAt(1, true);
219  EXPECT_TRUE(bookmark_bar->visible());
220  EXPECT_EQ(1, observer.change_count());
221  observer.clear_change_count();
222
223  // Repeat with the bookmark bar always visible.
224  browser()->profile()->GetPrefs()->SetBoolean(
225      bookmarks::prefs::kShowBookmarkBar, true);
226  browser()->tab_strip_model()->ActivateTabAt(1, true);
227  EXPECT_TRUE(bookmark_bar->visible());
228  observer.clear_change_count();
229
230  browser()->tab_strip_model()->ActivateTabAt(0, true);
231  EXPECT_TRUE(bookmark_bar->visible());
232  EXPECT_EQ(0, observer.change_count());
233  observer.clear_change_count();
234
235  browser()->tab_strip_model()->ActivateTabAt(1, true);
236  EXPECT_TRUE(bookmark_bar->visible());
237  EXPECT_EQ(0, observer.change_count());
238  observer.clear_change_count();
239
240  browser_view()->bookmark_bar()->RemoveObserver(&observer);
241}
242