Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / window_sizer / window_sizer.cc
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 "chrome/browser/ui/window_sizer/window_sizer.h"
6
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"
20
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
26
27 namespace {
28
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;
33
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) {
41   }
42
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);
49
50     if (!browser_ || !browser_->profile()->GetPrefs())
51       return false;
52
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));
66
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;
78     }
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));
82
83     return has_prefs;
84   }
85
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;
93
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;
117         }
118       }
119     }
120
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;
126     }
127
128     return false;
129   }
130
131  private:
132   std::string app_name_;
133
134   // If set, is used as the reference browser for GetLastActiveWindowState.
135   const Browser* browser_;
136   DISALLOW_COPY_AND_ASSIGN(DefaultStateProvider);
137 };
138
139 class DefaultTargetDisplayProvider : public WindowSizer::TargetDisplayProvider {
140  public:
141   DefaultTargetDisplayProvider() {}
142   ~DefaultTargetDisplayProvider() override {}
143
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         CommandLine::ForCurrentProcess()->HasSwitch(switches::kViewerConnect);
161 #endif
162     // Use the target display on ash.
163     if (chrome::ShouldOpenAshOnStartup() || force_ash) {
164       aura::Window* target = ash::Shell::GetTargetRootWindow();
165       return screen->GetDisplayNearestWindow(target);
166     }
167 #endif
168     // Find the size of the work area of the monitor that intersects the bounds
169     // of the anchor window.
170     return screen->GetDisplayMatching(bounds);
171   }
172
173  private:
174   DISALLOW_COPY_AND_ASSIGN(DefaultTargetDisplayProvider);
175 };
176
177 }  // namespace
178
179 ///////////////////////////////////////////////////////////////////////////////
180 // WindowSizer, public:
181
182 WindowSizer::WindowSizer(
183     scoped_ptr<StateProvider> state_provider,
184     scoped_ptr<TargetDisplayProvider> target_display_provider,
185     const Browser* browser)
186     : state_provider_(state_provider.Pass()),
187       target_display_provider_(target_display_provider.Pass()),
188       // TODO(scottmg): NativeScreen is wrong. http://crbug.com/133312
189       screen_(gfx::Screen::GetNativeScreen()),
190       browser_(browser) {
191 }
192
193 WindowSizer::WindowSizer(
194     scoped_ptr<StateProvider> state_provider,
195     scoped_ptr<TargetDisplayProvider> target_display_provider,
196     gfx::Screen* screen,
197     const Browser* browser)
198     : state_provider_(state_provider.Pass()),
199       target_display_provider_(target_display_provider.Pass()),
200       screen_(screen),
201       browser_(browser) {
202   DCHECK(screen_);
203 }
204
205 WindowSizer::~WindowSizer() {
206 }
207
208 // static
209 void WindowSizer::GetBrowserWindowBoundsAndShowState(
210     const std::string& app_name,
211     const gfx::Rect& specified_bounds,
212     const Browser* browser,
213     gfx::Rect* window_bounds,
214     ui::WindowShowState* show_state) {
215   scoped_ptr<StateProvider> state_provider(
216       new DefaultStateProvider(app_name, browser));
217   scoped_ptr<TargetDisplayProvider> target_display_provider(
218       new DefaultTargetDisplayProvider);
219   const WindowSizer sizer(state_provider.Pass(),
220                           target_display_provider.Pass(),
221                           browser);
222   sizer.DetermineWindowBoundsAndShowState(specified_bounds,
223                                           window_bounds,
224                                           show_state);
225 }
226
227 ///////////////////////////////////////////////////////////////////////////////
228 // WindowSizer, private:
229
230 void WindowSizer::DetermineWindowBoundsAndShowState(
231     const gfx::Rect& specified_bounds,
232     gfx::Rect* bounds,
233     ui::WindowShowState* show_state) const {
234   DCHECK(bounds);
235   DCHECK(show_state);
236   // Pre-populate the window state with our default.
237   *show_state = GetWindowDefaultShowState();
238   *bounds = specified_bounds;
239
240 #if defined(USE_ASH)
241   // See if ash should decide the window placement.
242   if (GetBrowserBoundsAsh(bounds, show_state))
243     return;
244 #endif
245
246   if (bounds->IsEmpty()) {
247     // See if there's last active window's placement information.
248     if (GetLastActiveWindowBounds(bounds, show_state))
249       return;
250     // See if there's saved placement information.
251     if (GetSavedWindowBounds(bounds, show_state))
252       return;
253
254     // No saved placement, figure out some sensible default size based on
255     // the user's screen size.
256     GetDefaultWindowBounds(GetTargetDisplay(gfx::Rect()), bounds);
257     return;
258   }
259
260   // In case that there was a bound given we need to make sure that it is
261   // visible and fits on the screen.
262   // Find the size of the work area of the monitor that intersects the bounds
263   // of the anchor window. Note: AdjustBoundsToBeVisibleOnMonitorContaining
264   // does not exactly what we want: It makes only sure that "a minimal part"
265   // is visible on the screen.
266   gfx::Rect work_area = screen_->GetDisplayMatching(*bounds).work_area();
267   // Resize so that it fits.
268   bounds->AdjustToFit(work_area);
269 }
270
271 bool WindowSizer::GetLastActiveWindowBounds(
272     gfx::Rect* bounds,
273     ui::WindowShowState* show_state) const {
274   DCHECK(bounds);
275   DCHECK(show_state);
276   if (!state_provider_.get() ||
277       !state_provider_->GetLastActiveWindowState(bounds, show_state))
278     return false;
279   gfx::Rect last_window_bounds = *bounds;
280   bounds->Offset(kWindowTilePixels, kWindowTilePixels);
281   AdjustBoundsToBeVisibleOnDisplay(screen_->GetDisplayMatching(*bounds),
282                                    gfx::Rect(),
283                                    bounds);
284   return true;
285 }
286
287 bool WindowSizer::GetSavedWindowBounds(gfx::Rect* bounds,
288                                        ui::WindowShowState* show_state) const {
289   DCHECK(bounds);
290   DCHECK(show_state);
291   gfx::Rect saved_work_area;
292   if (!state_provider_.get() ||
293       !state_provider_->GetPersistentState(bounds,
294                                            &saved_work_area,
295                                            show_state))
296     return false;
297   AdjustBoundsToBeVisibleOnDisplay(GetTargetDisplay(*bounds),
298                                    saved_work_area,
299                                    bounds);
300   return true;
301 }
302
303 void WindowSizer::GetDefaultWindowBounds(const gfx::Display& display,
304                                          gfx::Rect* default_bounds) const {
305   DCHECK(default_bounds);
306 #if defined(USE_ASH)
307   // TODO(beng): insufficient but currently necessary. http://crbug.com/133312
308   if (chrome::ShouldOpenAshOnStartup()) {
309     *default_bounds = ash::WindowPositioner::GetDefaultWindowBounds(display);
310     return;
311   }
312 #endif
313   gfx::Rect work_area = display.work_area();
314
315   // The default size is either some reasonably wide width, or if the work
316   // area is narrower, then the work area width less some aesthetic padding.
317   int default_width = std::min(work_area.width() - 2 * kWindowTilePixels, 1050);
318   int default_height = work_area.height() - 2 * kWindowTilePixels;
319
320   // For wider aspect ratio displays at higher resolutions, we might size the
321   // window narrower to allow two windows to easily be placed side-by-side.
322   gfx::Rect screen_size = screen_->GetPrimaryDisplay().bounds();
323   double width_to_height =
324     static_cast<double>(screen_size.width()) / screen_size.height();
325
326   // The least wide a screen can be to qualify for the halving described above.
327   static const int kMinScreenWidthForWindowHalving = 1600;
328   // We assume 16:9/10 is a fairly standard indicator of a wide aspect ratio
329   // computer display.
330   if (((width_to_height * 10) >= 16) &&
331       work_area.width() > kMinScreenWidthForWindowHalving) {
332     // Halve the work area, subtracting aesthetic padding on either side.
333     // The padding is set so that two windows, side by side have
334     // kWindowTilePixels between screen edge and each other.
335     default_width = static_cast<int>(work_area.width() / 2. -
336         1.5 * kWindowTilePixels);
337   }
338   default_bounds->SetRect(kWindowTilePixels + work_area.x(),
339                           kWindowTilePixels + work_area.y(),
340                           default_width, default_height);
341 }
342
343 void WindowSizer::AdjustBoundsToBeVisibleOnDisplay(
344     const gfx::Display& display,
345     const gfx::Rect& saved_work_area,
346     gfx::Rect* bounds) const {
347   DCHECK(bounds);
348
349   // If |bounds| is empty, reset to the default size.
350   if (bounds->IsEmpty()) {
351     gfx::Rect default_bounds;
352     GetDefaultWindowBounds(display, &default_bounds);
353     if (bounds->height() <= 0)
354       bounds->set_height(default_bounds.height());
355     if (bounds->width() <= 0)
356       bounds->set_width(default_bounds.width());
357   }
358
359   // Ensure the minimum height and width.
360   bounds->set_height(std::max(kMinVisibleHeight, bounds->height()));
361   bounds->set_width(std::max(kMinVisibleWidth, bounds->width()));
362
363   gfx::Rect work_area = display.work_area();
364   // Ensure that the title bar is not above the work area.
365   if (bounds->y() < work_area.y())
366     bounds->set_y(work_area.y());
367
368   // Reposition and resize the bounds if the saved_work_area is different from
369   // the current work area and the current work area doesn't completely contain
370   // the bounds.
371   if (!saved_work_area.IsEmpty() &&
372       saved_work_area != work_area &&
373       !work_area.Contains(*bounds)) {
374     bounds->set_width(std::min(bounds->width(), work_area.width()));
375     bounds->set_height(std::min(bounds->height(), work_area.height()));
376     bounds->set_x(
377         std::max(work_area.x(),
378                  std::min(bounds->x(), work_area.right() - bounds->width())));
379     bounds->set_y(
380         std::max(work_area.y(),
381                  std::min(bounds->y(), work_area.bottom() - bounds->height())));
382   }
383
384 #if defined(OS_MACOSX)
385   // Limit the maximum height.  On the Mac the sizer is on the
386   // bottom-right of the window, and a window cannot be moved "up"
387   // past the menubar.  If the window is too tall you'll never be able
388   // to shrink it again.  Windows does not have this limitation
389   // (e.g. can be resized from the top).
390   bounds->set_height(std::min(work_area.height(), bounds->height()));
391
392   // On mac, we want to be aggressive about repositioning windows that are
393   // partially offscreen.  If the window is partially offscreen horizontally,
394   // move it to be flush with the left edge of the work area.
395   if (bounds->x() < work_area.x() || bounds->right() > work_area.right())
396     bounds->set_x(work_area.x());
397
398   // If the window is partially offscreen vertically, move it to be flush with
399   // the top of the work area.
400   if (bounds->y() < work_area.y() || bounds->bottom() > work_area.bottom())
401     bounds->set_y(work_area.y());
402 #else
403   // On non-Mac platforms, we are less aggressive about repositioning.  Simply
404   // ensure that at least kMinVisibleWidth * kMinVisibleHeight is visible.
405   const int min_y = work_area.y() + kMinVisibleHeight - bounds->height();
406   const int min_x = work_area.x() + kMinVisibleWidth - bounds->width();
407   const int max_y = work_area.bottom() - kMinVisibleHeight;
408   const int max_x = work_area.right() - kMinVisibleWidth;
409   bounds->set_y(std::max(min_y, std::min(max_y, bounds->y())));
410   bounds->set_x(std::max(min_x, std::min(max_x, bounds->x())));
411 #endif  // defined(OS_MACOSX)
412 }
413
414 gfx::Display WindowSizer::GetTargetDisplay(const gfx::Rect& bounds) const {
415   return target_display_provider_->GetTargetDisplay(screen_, bounds);
416 }
417
418 ui::WindowShowState WindowSizer::GetWindowDefaultShowState() const {
419   if (!browser_)
420     return ui::SHOW_STATE_DEFAULT;
421
422   // Only tabbed browsers use the command line or preference state, with the
423   // exception of devtools.
424   bool show_state = !browser_->is_type_tabbed() && !browser_->is_devtools();
425
426 #if defined(USE_AURA)
427   // We use the apps save state on aura.
428   show_state &= !browser_->is_app();
429 #endif
430
431   if (show_state)
432     return browser_->initial_show_state();
433
434   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kStartMaximized))
435     return ui::SHOW_STATE_MAXIMIZED;
436
437   if (browser_->initial_show_state() != ui::SHOW_STATE_DEFAULT)
438     return browser_->initial_show_state();
439
440   // Otherwise we use the default which can be overridden later on.
441   return ui::SHOW_STATE_DEFAULT;
442 }