[Password manager] Temporarily add some CHECKs to investigate a crash
[chromium-blink-merge.git] / chrome / browser / ui / window_sizer / window_sizer.cc
blob825176b552248c4d98ff61dd4483945899eb3656
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.
5 #include "chrome/browser/ui/window_sizer/window_sizer.h"
7 #include "base/command_line.h"
8 #include "base/compiler_specific.h"
9 #include "base/prefs/pref_service.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_list.h"
14 #include "chrome/browser/ui/browser_window.h"
15 #include "chrome/browser/ui/browser_window_state.h"
16 #include "chrome/browser/ui/host_desktop.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "ui/base/ui_base_switches.h"
19 #include "ui/gfx/screen.h"
21 #if defined(USE_ASH)
22 #include "ash/shell.h"
23 #include "ash/wm/window_positioner.h"
24 #include "chrome/browser/ui/ash/ash_util.h"
25 #endif
27 namespace {
29 // Minimum height of the visible part of a window.
30 const int kMinVisibleHeight = 30;
31 // Minimum width of the visible part of a window.
32 const int kMinVisibleWidth = 30;
34 ///////////////////////////////////////////////////////////////////////////////
35 // An implementation of WindowSizer::StateProvider that gets the last active
36 // and persistent state from the browser window and the user's profile.
37 class DefaultStateProvider : public WindowSizer::StateProvider {
38 public:
39 DefaultStateProvider(const std::string& app_name, const Browser* browser)
40 : app_name_(app_name), browser_(browser) {
43 // Overridden from WindowSizer::StateProvider:
44 bool GetPersistentState(gfx::Rect* bounds,
45 gfx::Rect* work_area,
46 ui::WindowShowState* show_state) const override {
47 DCHECK(bounds);
48 DCHECK(show_state);
50 if (!browser_ || !browser_->profile()->GetPrefs())
51 return false;
53 const base::DictionaryValue* wp_pref =
54 chrome::GetWindowPlacementDictionaryReadOnly(
55 chrome::GetWindowName(browser_), browser_->profile()->GetPrefs());
56 int top = 0, left = 0, bottom = 0, right = 0;
57 bool maximized = false;
58 bool has_prefs = wp_pref &&
59 wp_pref->GetInteger("top", &top) &&
60 wp_pref->GetInteger("left", &left) &&
61 wp_pref->GetInteger("bottom", &bottom) &&
62 wp_pref->GetInteger("right", &right) &&
63 wp_pref->GetBoolean("maximized", &maximized);
64 bounds->SetRect(left, top, std::max(0, right - left),
65 std::max(0, bottom - top));
67 int work_area_top = 0;
68 int work_area_left = 0;
69 int work_area_bottom = 0;
70 int work_area_right = 0;
71 if (wp_pref) {
72 wp_pref->GetInteger("work_area_top", &work_area_top);
73 wp_pref->GetInteger("work_area_left", &work_area_left);
74 wp_pref->GetInteger("work_area_bottom", &work_area_bottom);
75 wp_pref->GetInteger("work_area_right", &work_area_right);
76 if (*show_state == ui::SHOW_STATE_DEFAULT && maximized)
77 *show_state = ui::SHOW_STATE_MAXIMIZED;
79 work_area->SetRect(work_area_left, work_area_top,
80 std::max(0, work_area_right - work_area_left),
81 std::max(0, work_area_bottom - work_area_top));
83 return has_prefs;
86 bool GetLastActiveWindowState(
87 gfx::Rect* bounds,
88 ui::WindowShowState* show_state) const override {
89 DCHECK(show_state);
90 // Applications are always restored with the same position.
91 if (!app_name_.empty())
92 return false;
94 // If a reference browser is set, use its window. Otherwise find last
95 // active. Panels are never used as reference browsers as panels are
96 // specially positioned.
97 BrowserWindow* window = NULL;
98 // Window may be null if browser is just starting up.
99 if (browser_ && browser_->window()) {
100 window = browser_->window();
101 } else {
102 // This code is only ran on the native desktop (on the ash
103 // desktop, GetTabbedBrowserBoundsAsh should take over below
104 // before this is reached). TODO(gab): This code should go in a
105 // native desktop specific window sizer as part of fixing
106 // crbug.com/175812.
107 const BrowserList* native_browser_list =
108 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE);
109 for (BrowserList::const_reverse_iterator it =
110 native_browser_list->begin_last_active();
111 it != native_browser_list->end_last_active(); ++it) {
112 Browser* last_active = *it;
113 if (last_active && last_active->is_type_tabbed()) {
114 window = last_active->window();
115 DCHECK(window);
116 break;
121 if (window) {
122 *bounds = window->GetRestoredBounds();
123 if (*show_state == ui::SHOW_STATE_DEFAULT && window->IsMaximized())
124 *show_state = ui::SHOW_STATE_MAXIMIZED;
125 return true;
128 return false;
131 private:
132 std::string app_name_;
134 // If set, is used as the reference browser for GetLastActiveWindowState.
135 const Browser* browser_;
136 DISALLOW_COPY_AND_ASSIGN(DefaultStateProvider);
139 class DefaultTargetDisplayProvider : public WindowSizer::TargetDisplayProvider {
140 public:
141 DefaultTargetDisplayProvider() {}
142 ~DefaultTargetDisplayProvider() override {}
144 gfx::Display GetTargetDisplay(const gfx::Screen* screen,
145 const gfx::Rect& bounds) const override {
146 #if defined(USE_ASH)
147 bool force_ash = false;
148 // On Windows check if the browser is launched to serve ASH. If yes then
149 // we should get the display for the corresponding root window created for
150 // ASH. This ensures that the display gets the correct workarea, etc.
151 // If the ASH shell does not exist then the current behavior is to open
152 // browser windows if any on the desktop. Preserve that for now.
153 // TODO(ananta).
154 // This effectively means that the running browser process is in a split
155 // personality mode, part of it running in ASH and the other running in
156 // desktop. This may cause apps and other widgets to not work correctly.
157 // Revisit and address.
158 #if defined(OS_WIN)
159 force_ash = ash::Shell::HasInstance() &&
160 base::CommandLine::ForCurrentProcess()->HasSwitch(
161 switches::kViewerConnect);
162 #endif
163 // Use the target display on ash.
164 if (chrome::ShouldOpenAshOnStartup() || force_ash) {
165 aura::Window* target = ash::Shell::GetTargetRootWindow();
166 return screen->GetDisplayNearestWindow(target);
168 #endif
169 // Find the size of the work area of the monitor that intersects the bounds
170 // of the anchor window.
171 return screen->GetDisplayMatching(bounds);
174 private:
175 DISALLOW_COPY_AND_ASSIGN(DefaultTargetDisplayProvider);
178 } // namespace
180 ///////////////////////////////////////////////////////////////////////////////
181 // WindowSizer, public:
183 WindowSizer::WindowSizer(
184 scoped_ptr<StateProvider> state_provider,
185 scoped_ptr<TargetDisplayProvider> target_display_provider,
186 const Browser* browser)
187 : state_provider_(state_provider.Pass()),
188 target_display_provider_(target_display_provider.Pass()),
189 // TODO(scottmg): NativeScreen is wrong. http://crbug.com/133312
190 screen_(gfx::Screen::GetNativeScreen()),
191 browser_(browser) {
194 WindowSizer::WindowSizer(
195 scoped_ptr<StateProvider> state_provider,
196 scoped_ptr<TargetDisplayProvider> target_display_provider,
197 gfx::Screen* screen,
198 const Browser* browser)
199 : state_provider_(state_provider.Pass()),
200 target_display_provider_(target_display_provider.Pass()),
201 screen_(screen),
202 browser_(browser) {
203 DCHECK(screen_);
206 WindowSizer::~WindowSizer() {
209 // static
210 void WindowSizer::GetBrowserWindowBoundsAndShowState(
211 const std::string& app_name,
212 const gfx::Rect& specified_bounds,
213 const Browser* browser,
214 gfx::Rect* window_bounds,
215 ui::WindowShowState* show_state) {
216 scoped_ptr<StateProvider> state_provider(
217 new DefaultStateProvider(app_name, browser));
218 scoped_ptr<TargetDisplayProvider> target_display_provider(
219 new DefaultTargetDisplayProvider);
220 const WindowSizer sizer(state_provider.Pass(),
221 target_display_provider.Pass(),
222 browser);
223 sizer.DetermineWindowBoundsAndShowState(specified_bounds,
224 window_bounds,
225 show_state);
228 ///////////////////////////////////////////////////////////////////////////////
229 // WindowSizer, private:
231 void WindowSizer::DetermineWindowBoundsAndShowState(
232 const gfx::Rect& specified_bounds,
233 gfx::Rect* bounds,
234 ui::WindowShowState* show_state) const {
235 DCHECK(bounds);
236 DCHECK(show_state);
237 // Pre-populate the window state with our default.
238 *show_state = GetWindowDefaultShowState();
239 *bounds = specified_bounds;
241 #if defined(USE_ASH)
242 // See if ash should decide the window placement.
243 if (GetBrowserBoundsAsh(bounds, show_state))
244 return;
245 #endif
247 if (bounds->IsEmpty()) {
248 // See if there's last active window's placement information.
249 if (GetLastActiveWindowBounds(bounds, show_state))
250 return;
251 // See if there's saved placement information.
252 if (GetSavedWindowBounds(bounds, show_state))
253 return;
255 // No saved placement, figure out some sensible default size based on
256 // the user's screen size.
257 GetDefaultWindowBounds(GetTargetDisplay(gfx::Rect()), bounds);
258 return;
261 // In case that there was a bound given we need to make sure that it is
262 // visible and fits on the screen.
263 // Find the size of the work area of the monitor that intersects the bounds
264 // of the anchor window. Note: AdjustBoundsToBeVisibleOnMonitorContaining
265 // does not exactly what we want: It makes only sure that "a minimal part"
266 // is visible on the screen.
267 gfx::Rect work_area = screen_->GetDisplayMatching(*bounds).work_area();
268 // Resize so that it fits.
269 bounds->AdjustToFit(work_area);
272 bool WindowSizer::GetLastActiveWindowBounds(
273 gfx::Rect* bounds,
274 ui::WindowShowState* show_state) const {
275 DCHECK(bounds);
276 DCHECK(show_state);
277 if (!state_provider_.get() ||
278 !state_provider_->GetLastActiveWindowState(bounds, show_state))
279 return false;
280 gfx::Rect last_window_bounds = *bounds;
281 bounds->Offset(kWindowTilePixels, kWindowTilePixels);
282 AdjustBoundsToBeVisibleOnDisplay(screen_->GetDisplayMatching(*bounds),
283 gfx::Rect(),
284 bounds);
285 return true;
288 bool WindowSizer::GetSavedWindowBounds(gfx::Rect* bounds,
289 ui::WindowShowState* show_state) const {
290 DCHECK(bounds);
291 DCHECK(show_state);
292 gfx::Rect saved_work_area;
293 if (!state_provider_.get() ||
294 !state_provider_->GetPersistentState(bounds,
295 &saved_work_area,
296 show_state))
297 return false;
298 AdjustBoundsToBeVisibleOnDisplay(GetTargetDisplay(*bounds),
299 saved_work_area,
300 bounds);
301 return true;
304 void WindowSizer::GetDefaultWindowBounds(const gfx::Display& display,
305 gfx::Rect* default_bounds) const {
306 DCHECK(default_bounds);
307 #if defined(USE_ASH)
308 // TODO(beng): insufficient but currently necessary. http://crbug.com/133312
309 if (chrome::ShouldOpenAshOnStartup()) {
310 *default_bounds = ash::WindowPositioner::GetDefaultWindowBounds(display);
311 return;
313 #endif
314 gfx::Rect work_area = display.work_area();
316 // The default size is either some reasonably wide width, or if the work
317 // area is narrower, then the work area width less some aesthetic padding.
318 int default_width = std::min(work_area.width() - 2 * kWindowTilePixels, 1050);
319 int default_height = work_area.height() - 2 * kWindowTilePixels;
321 // For wider aspect ratio displays at higher resolutions, we might size the
322 // window narrower to allow two windows to easily be placed side-by-side.
323 gfx::Rect screen_size = screen_->GetPrimaryDisplay().bounds();
324 double width_to_height =
325 static_cast<double>(screen_size.width()) / screen_size.height();
327 // The least wide a screen can be to qualify for the halving described above.
328 static const int kMinScreenWidthForWindowHalving = 1600;
329 // We assume 16:9/10 is a fairly standard indicator of a wide aspect ratio
330 // computer display.
331 if (((width_to_height * 10) >= 16) &&
332 work_area.width() > kMinScreenWidthForWindowHalving) {
333 // Halve the work area, subtracting aesthetic padding on either side.
334 // The padding is set so that two windows, side by side have
335 // kWindowTilePixels between screen edge and each other.
336 default_width = static_cast<int>(work_area.width() / 2. -
337 1.5 * kWindowTilePixels);
339 default_bounds->SetRect(kWindowTilePixels + work_area.x(),
340 kWindowTilePixels + work_area.y(),
341 default_width, default_height);
344 void WindowSizer::AdjustBoundsToBeVisibleOnDisplay(
345 const gfx::Display& display,
346 const gfx::Rect& saved_work_area,
347 gfx::Rect* bounds) const {
348 DCHECK(bounds);
350 // If |bounds| is empty, reset to the default size.
351 if (bounds->IsEmpty()) {
352 gfx::Rect default_bounds;
353 GetDefaultWindowBounds(display, &default_bounds);
354 if (bounds->height() <= 0)
355 bounds->set_height(default_bounds.height());
356 if (bounds->width() <= 0)
357 bounds->set_width(default_bounds.width());
360 // Ensure the minimum height and width.
361 bounds->set_height(std::max(kMinVisibleHeight, bounds->height()));
362 bounds->set_width(std::max(kMinVisibleWidth, bounds->width()));
364 gfx::Rect work_area = display.work_area();
365 // Ensure that the title bar is not above the work area.
366 if (bounds->y() < work_area.y())
367 bounds->set_y(work_area.y());
369 // Reposition and resize the bounds if the saved_work_area is different from
370 // the current work area and the current work area doesn't completely contain
371 // the bounds.
372 if (!saved_work_area.IsEmpty() &&
373 saved_work_area != work_area &&
374 !work_area.Contains(*bounds)) {
375 bounds->set_width(std::min(bounds->width(), work_area.width()));
376 bounds->set_height(std::min(bounds->height(), work_area.height()));
377 bounds->set_x(
378 std::max(work_area.x(),
379 std::min(bounds->x(), work_area.right() - bounds->width())));
380 bounds->set_y(
381 std::max(work_area.y(),
382 std::min(bounds->y(), work_area.bottom() - bounds->height())));
385 #if defined(OS_MACOSX)
386 // Limit the maximum height. On the Mac the sizer is on the
387 // bottom-right of the window, and a window cannot be moved "up"
388 // past the menubar. If the window is too tall you'll never be able
389 // to shrink it again. Windows does not have this limitation
390 // (e.g. can be resized from the top).
391 bounds->set_height(std::min(work_area.height(), bounds->height()));
393 // On mac, we want to be aggressive about repositioning windows that are
394 // partially offscreen. If the window is partially offscreen horizontally,
395 // move it to be flush with the left edge of the work area.
396 if (bounds->x() < work_area.x() || bounds->right() > work_area.right())
397 bounds->set_x(work_area.x());
399 // If the window is partially offscreen vertically, move it to be flush with
400 // the top of the work area.
401 if (bounds->y() < work_area.y() || bounds->bottom() > work_area.bottom())
402 bounds->set_y(work_area.y());
403 #else
404 // On non-Mac platforms, we are less aggressive about repositioning. Simply
405 // ensure that at least kMinVisibleWidth * kMinVisibleHeight is visible.
406 const int min_y = work_area.y() + kMinVisibleHeight - bounds->height();
407 const int min_x = work_area.x() + kMinVisibleWidth - bounds->width();
408 const int max_y = work_area.bottom() - kMinVisibleHeight;
409 const int max_x = work_area.right() - kMinVisibleWidth;
410 bounds->set_y(std::max(min_y, std::min(max_y, bounds->y())));
411 bounds->set_x(std::max(min_x, std::min(max_x, bounds->x())));
412 #endif // defined(OS_MACOSX)
415 gfx::Display WindowSizer::GetTargetDisplay(const gfx::Rect& bounds) const {
416 return target_display_provider_->GetTargetDisplay(screen_, bounds);
419 ui::WindowShowState WindowSizer::GetWindowDefaultShowState() const {
420 if (!browser_)
421 return ui::SHOW_STATE_DEFAULT;
423 // Only tabbed browsers use the command line or preference state, with the
424 // exception of devtools.
425 bool show_state = !browser_->is_type_tabbed() && !browser_->is_devtools();
427 #if defined(USE_AURA)
428 // We use the apps save state on aura.
429 show_state &= !browser_->is_app();
430 #endif
432 if (show_state)
433 return browser_->initial_show_state();
435 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
436 switches::kStartMaximized))
437 return ui::SHOW_STATE_MAXIMIZED;
439 if (browser_->initial_show_state() != ui::SHOW_STATE_DEFAULT)
440 return browser_->initial_show_state();
442 // Otherwise we use the default which can be overridden later on.
443 return ui::SHOW_STATE_DEFAULT;