Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / tabs / dock_info_win.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/tabs/dock_info.h"
6
7 #include "base/win/scoped_gdi_object.h"
8 #include "base/win/windows_version.h"
9 #include "chrome/browser/ui/ash/tabs/dock_info_ash.h"
10 #include "chrome/browser/ui/browser_list.h"
11 #include "chrome/browser/ui/browser_window.h"
12 #include "chrome/browser/ui/views/frame/browser_view.h"
13 #include "chrome/browser/ui/views/tabs/tab.h"
14 #include "ui/aura/root_window.h"
15 #include "ui/aura/window.h"
16 #include "ui/gfx/screen.h"
17 #include "ui/views/win/hwnd_util.h"
18
19 #if defined(USE_AURA)
20 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
21 #endif
22
23 namespace {
24
25 // BaseWindowFinder -----------------------------------------------------------
26
27 // Base class used to locate a window. This is intended to be used with the
28 // various win32 functions that iterate over windows.
29 //
30 // A subclass need only override ShouldStopIterating to determine when
31 // iteration should stop.
32 class BaseWindowFinder {
33  public:
34   // Creates a BaseWindowFinder with the specified set of HWNDs to ignore.
35   explicit BaseWindowFinder(const std::set<HWND>& ignore) : ignore_(ignore) {}
36   virtual ~BaseWindowFinder() {}
37
38  protected:
39   static BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) {
40     // Cast must match that in as_lparam().
41     BaseWindowFinder* finder = reinterpret_cast<BaseWindowFinder*>(lParam);
42     if (finder->ignore_.find(hwnd) != finder->ignore_.end())
43       return TRUE;
44
45     return finder->ShouldStopIterating(hwnd) ? FALSE : TRUE;
46   }
47
48   LPARAM as_lparam() {
49     // Cast must match that in WindowCallbackProc().
50     return reinterpret_cast<LPARAM>(static_cast<BaseWindowFinder*>(this));
51   }
52
53   // Returns true if iteration should stop, false if iteration should continue.
54   virtual bool ShouldStopIterating(HWND window) = 0;
55
56  private:
57   const std::set<HWND>& ignore_;
58
59   DISALLOW_COPY_AND_ASSIGN(BaseWindowFinder);
60 };
61
62 // TopMostFinder --------------------------------------------------------------
63
64 // Helper class to determine if a particular point of a window is not obscured
65 // by another window.
66 class TopMostFinder : public BaseWindowFinder {
67  public:
68   // Returns true if |window| is the topmost window at the location
69   // |screen_loc|, not including the windows in |ignore|.
70   static bool IsTopMostWindowAtPoint(HWND window,
71                                      const gfx::Point& screen_loc,
72                                      const std::set<HWND>& ignore) {
73     TopMostFinder finder(window, screen_loc, ignore);
74     return finder.is_top_most_;
75   }
76
77   virtual bool ShouldStopIterating(HWND hwnd) {
78     if (hwnd == target_) {
79       // Window is topmost, stop iterating.
80       is_top_most_ = true;
81       return true;
82     }
83
84     if (!IsWindowVisible(hwnd)) {
85       // The window isn't visible, keep iterating.
86       return false;
87     }
88
89     RECT r;
90     if (!GetWindowRect(hwnd, &r) || !PtInRect(&r, screen_loc_.ToPOINT())) {
91       // The window doesn't contain the point, keep iterating.
92       return false;
93     }
94
95     LONG ex_styles = GetWindowLong(hwnd, GWL_EXSTYLE);
96     if (ex_styles & WS_EX_TRANSPARENT || ex_styles & WS_EX_LAYERED) {
97       // Mouse events fall through WS_EX_TRANSPARENT windows, so we ignore them.
98       //
99       // WS_EX_LAYERED is trickier. Apps like Switcher create a totally
100       // transparent WS_EX_LAYERED window that is always on top. If we don't
101       // ignore WS_EX_LAYERED windows and there are totally transparent
102       // WS_EX_LAYERED windows then there are effectively holes on the screen
103       // that the user can't reattach tabs to. So we ignore them. This is a bit
104       // problematic in so far as WS_EX_LAYERED windows need not be totally
105       // transparent in which case we treat chrome windows as not being obscured
106       // when they really are, but this is better than not being able to
107       // reattach tabs.
108       return false;
109     }
110
111     // hwnd is at the point. Make sure the point is within the windows region.
112     if (GetWindowRgn(hwnd, tmp_region_.Get()) == ERROR) {
113       // There's no region on the window and the window contains the point. Stop
114       // iterating.
115       return true;
116     }
117
118     // The region is relative to the window's rect.
119     BOOL is_point_in_region = PtInRegion(tmp_region_.Get(),
120         screen_loc_.x() - r.left, screen_loc_.y() - r.top);
121     tmp_region_ = CreateRectRgn(0, 0, 0, 0);
122     // Stop iterating if the region contains the point.
123     return !!is_point_in_region;
124   }
125
126  private:
127   TopMostFinder(HWND window,
128                 const gfx::Point& screen_loc,
129                 const std::set<HWND>& ignore)
130       : BaseWindowFinder(ignore),
131         target_(window),
132         screen_loc_(screen_loc),
133         is_top_most_(false),
134         tmp_region_(CreateRectRgn(0, 0, 0, 0)) {
135     EnumWindows(WindowCallbackProc, as_lparam());
136   }
137
138   // The window we're looking for.
139   HWND target_;
140
141   // Location of window to find.
142   gfx::Point screen_loc_;
143
144   // Is target_ the top most window? This is initially false but set to true
145   // in ShouldStopIterating if target_ is passed in.
146   bool is_top_most_;
147
148   base::win::ScopedRegion tmp_region_;
149
150   DISALLOW_COPY_AND_ASSIGN(TopMostFinder);
151 };
152
153 // WindowFinder ---------------------------------------------------------------
154
155 // Helper class to determine if a particular point contains a window from our
156 // process.
157 class LocalProcessWindowFinder : public BaseWindowFinder {
158  public:
159   // Returns the hwnd from our process at screen_loc that is not obscured by
160   // another window. Returns NULL otherwise.
161   static gfx::NativeWindow GetProcessWindowAtPoint(
162       const gfx::Point& screen_loc,
163       const std::set<HWND>& ignore) {
164     LocalProcessWindowFinder finder(screen_loc, ignore);
165     // Windows 8 has a window that appears first in the list of iterated
166     // windows, yet is not visually on top of everything.
167     // TODO(sky): figure out a better way to ignore this window.
168     if (finder.result_ &&
169         ((base::win::OSInfo::GetInstance()->version() >=
170           base::win::VERSION_WIN8) ||
171          TopMostFinder::IsTopMostWindowAtPoint(finder.result_, screen_loc,
172                                                ignore))) {
173 #if defined(USE_AURA)
174       return views::DesktopWindowTreeHostWin::GetContentWindowForHWND(
175           finder.result_);
176 #else
177       return finder.result_;
178 #endif
179     }
180     return NULL;
181   }
182
183  protected:
184   virtual bool ShouldStopIterating(HWND hwnd) {
185     RECT r;
186     if (IsWindowVisible(hwnd) && GetWindowRect(hwnd, &r) &&
187         PtInRect(&r, screen_loc_.ToPOINT())) {
188       result_ = hwnd;
189       return true;
190     }
191     return false;
192   }
193
194  private:
195   LocalProcessWindowFinder(const gfx::Point& screen_loc,
196                            const std::set<HWND>& ignore)
197       : BaseWindowFinder(ignore),
198         screen_loc_(screen_loc),
199         result_(NULL) {
200     EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, as_lparam());
201   }
202
203   // Position of the mouse.
204   gfx::Point screen_loc_;
205
206   // The resulting window. This is initially null but set to true in
207   // ShouldStopIterating if an appropriate window is found.
208   HWND result_;
209
210   DISALLOW_COPY_AND_ASSIGN(LocalProcessWindowFinder);
211 };
212
213 // DockToWindowFinder ---------------------------------------------------------
214
215 // Helper class for creating a DockInfo from a specified point.
216 class DockToWindowFinder : public BaseWindowFinder {
217  public:
218   // Returns the DockInfo for the specified point. If there is no docking
219   // position for the specified point the returned DockInfo has a type of NONE.
220   static DockInfo GetDockInfoAtPoint(const gfx::Point& screen_loc,
221                                      const std::set<HWND>& ignore) {
222     DockToWindowFinder finder(screen_loc, ignore);
223     HWND hwnd = views::HWNDForNativeWindow(finder.result_.window());
224     if (!finder.result_.window() ||
225         !TopMostFinder::IsTopMostWindowAtPoint(hwnd,
226                                                finder.result_.hot_spot(),
227                                                ignore)) {
228       finder.result_.set_type(DockInfo::NONE);
229     }
230     return finder.result_;
231   }
232
233  protected:
234   virtual bool ShouldStopIterating(HWND hwnd) {
235 #if defined(USE_AURA)
236     BrowserView* window = BrowserView::GetBrowserViewForNativeWindow(
237         views::DesktopWindowTreeHostWin::GetContentWindowForHWND(hwnd));
238 #else
239     BrowserView* window = BrowserView::GetBrowserViewForNativeWindow(hwnd);
240 #endif
241     RECT bounds;
242     if (!window || !IsWindowVisible(hwnd) ||
243         !GetWindowRect(hwnd, &bounds)) {
244       return false;
245     }
246
247     // Check the three corners we allow docking to. We don't currently allow
248     // docking to top of window as it conflicts with docking to the tab strip.
249     if (CheckPoint(hwnd, bounds.left, (bounds.top + bounds.bottom) / 2,
250                    DockInfo::LEFT_OF_WINDOW) ||
251         CheckPoint(hwnd, bounds.right - 1, (bounds.top + bounds.bottom) / 2,
252                    DockInfo::RIGHT_OF_WINDOW) ||
253         CheckPoint(hwnd, (bounds.left + bounds.right) / 2, bounds.bottom - 1,
254                    DockInfo::BOTTOM_OF_WINDOW)) {
255       return true;
256     }
257     return false;
258   }
259
260  private:
261   DockToWindowFinder(const gfx::Point& screen_loc,
262                      const std::set<HWND>& ignore)
263       : BaseWindowFinder(ignore),
264         screen_loc_(screen_loc) {
265     gfx::Rect work_area = gfx::Screen::GetNativeScreen()->
266         GetDisplayNearestPoint(screen_loc).bounds();
267     if (!work_area.IsEmpty()) {
268       result_.set_monitor_bounds(work_area);
269       EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, as_lparam());
270     }
271   }
272
273   bool CheckPoint(HWND hwnd, int x, int y, DockInfo::Type type) {
274     bool in_enable_area;
275     if (DockInfo::IsCloseToPoint(screen_loc_, x, y, &in_enable_area)) {
276       result_.set_in_enable_area(in_enable_area);
277 #if defined(USE_AURA)
278       result_.set_window(
279           views::DesktopWindowTreeHostWin::GetContentWindowForHWND(hwnd));
280 #else
281       result_.set_window(hwnd);
282 #endif
283       result_.set_type(type);
284       result_.set_hot_spot(gfx::Point(x, y));
285       // Only show the hotspot if the monitor contains the bounds of the popup
286       // window. Otherwise we end with a weird situation where the popup window
287       // isn't completely visible.
288       return result_.monitor_bounds().Contains(result_.GetPopupRect());
289     }
290     return false;
291   }
292
293   // The location to look for.
294   gfx::Point screen_loc_;
295
296   // The resulting DockInfo.
297   DockInfo result_;
298
299   DISALLOW_COPY_AND_ASSIGN(DockToWindowFinder);
300 };
301
302 std::set<HWND> RemapIgnoreSet(const std::set<gfx::NativeView>& ignore) {
303 #if defined(USE_AURA)
304   std::set<HWND> hwnd_set;
305   std::set<gfx::NativeView>::const_iterator it = ignore.begin();
306   for (; it != ignore.end(); ++it) {
307     HWND w = (*it)->GetDispatcher()->host()->GetAcceleratedWidget();
308     if (w)
309       hwnd_set.insert(w);
310   }
311   return hwnd_set;
312 #else
313   // NativeViews are already HWNDs on non-Aura Windows.
314   return ignore;
315 #endif
316 }
317
318 }  // namespace
319
320 // DockInfo -------------------------------------------------------------------
321
322 // static
323 DockInfo DockInfo::GetDockInfoAtPoint(chrome::HostDesktopType host_desktop_type,
324                                       const gfx::Point& screen_point,
325                                       const std::set<gfx::NativeView>& ignore) {
326 #if defined(USE_AURA)
327   if (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH)
328     return chrome::ash::GetDockInfoAtPointAsh(screen_point, ignore);
329 #endif
330   // Try docking to a window first.
331   DockInfo info = DockToWindowFinder::GetDockInfoAtPoint(
332       screen_point, RemapIgnoreSet(ignore));
333   if (info.type() != DockInfo::NONE)
334     return info;
335
336   // No window relative positions. Try monitor relative positions.
337   const gfx::Rect& m_bounds = info.monitor_bounds();
338   int mid_x = m_bounds.x() + m_bounds.width() / 2;
339   int mid_y = m_bounds.y() + m_bounds.height() / 2;
340
341   bool result =
342       info.CheckMonitorPoint(screen_point, mid_x, m_bounds.y(),
343                              DockInfo::MAXIMIZE) ||
344       info.CheckMonitorPoint(screen_point, mid_x, m_bounds.bottom(),
345                              DockInfo::BOTTOM_HALF) ||
346       info.CheckMonitorPoint(screen_point, m_bounds.x(), mid_y,
347                              DockInfo::LEFT_HALF) ||
348       info.CheckMonitorPoint(screen_point, m_bounds.right(), mid_y,
349                              DockInfo::RIGHT_HALF);
350
351   return info;
352 }
353
354 gfx::NativeView DockInfo::GetLocalProcessWindowAtPoint(
355     chrome::HostDesktopType host_desktop_type,
356     const gfx::Point& screen_point,
357     const std::set<gfx::NativeView>& ignore) {
358 #if defined(USE_AURA)
359   if (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH)
360     return chrome::ash::GetLocalProcessWindowAtPointAsh(screen_point, ignore);
361 #endif
362   return
363       LocalProcessWindowFinder::GetProcessWindowAtPoint(
364           screen_point, RemapIgnoreSet(ignore));
365 }
366
367 #if defined(USE_AURA)
368
369 bool DockInfo::GetWindowBounds(gfx::Rect* bounds) const {
370   if (!window())
371     return false;
372   *bounds = window_->bounds();
373   return true;
374 }
375
376 void DockInfo::SizeOtherWindowTo(const gfx::Rect& bounds) const {
377   window_->SetBounds(bounds);
378 }
379
380 #else  // USE_AURA
381
382 bool DockInfo::GetWindowBounds(gfx::Rect* bounds) const {
383   RECT window_rect;
384   if (!window() || !GetWindowRect(window(), &window_rect))
385     return false;
386   *bounds = gfx::Rect(window_rect);
387   return true;
388 }
389
390 void DockInfo::SizeOtherWindowTo(const gfx::Rect& bounds) const {
391   if (IsZoomed(window())) {
392     // We're docking relative to another window, we need to make sure the
393     // window we're docking to isn't maximized.
394     ShowWindow(window(), SW_RESTORE | SW_SHOWNA);
395   }
396   SetWindowPos(window(), HWND_TOP, bounds.x(), bounds.y(), bounds.width(),
397                bounds.height(), SWP_NOACTIVATE | SWP_NOOWNERZORDER);
398 }
399
400 // static
401 int DockInfo::GetHotSpotDeltaY() {
402   return Tab::GetMinimumUnselectedSize().height() - 1;
403 }
404
405 #endif  // !USE_AURA