Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / tabs / window_finder_win.cc
1 // Copyright 2014 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/tabs/window_finder.h"
6
7 #include "base/win/scoped_gdi_object.h"
8 #include "base/win/windows_version.h"
9 #include "ui/aura/window.h"
10 #include "ui/gfx/screen.h"
11 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
12 #include "ui/views/win/hwnd_util.h"
13
14 #if defined(USE_ASH)
15 aura::Window* GetLocalProcessWindowAtPointAsh(
16     const gfx::Point& screen_point,
17     const std::set<aura::Window*>& ignore);
18 #endif
19
20 namespace {
21
22 // BaseWindowFinder -----------------------------------------------------------
23
24 // Base class used to locate a window. This is intended to be used with the
25 // various win32 functions that iterate over windows.
26 //
27 // A subclass need only override ShouldStopIterating to determine when
28 // iteration should stop.
29 class BaseWindowFinder {
30  public:
31   // Creates a BaseWindowFinder with the specified set of HWNDs to ignore.
32   explicit BaseWindowFinder(const std::set<HWND>& ignore) : ignore_(ignore) {}
33   virtual ~BaseWindowFinder() {}
34
35  protected:
36   static BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) {
37     // Cast must match that in as_lparam().
38     BaseWindowFinder* finder = reinterpret_cast<BaseWindowFinder*>(lParam);
39     if (finder->ignore_.find(hwnd) != finder->ignore_.end())
40       return TRUE;
41
42     return finder->ShouldStopIterating(hwnd) ? FALSE : TRUE;
43   }
44
45   LPARAM as_lparam() {
46     // Cast must match that in WindowCallbackProc().
47     return reinterpret_cast<LPARAM>(static_cast<BaseWindowFinder*>(this));
48   }
49
50   // Returns true if iteration should stop, false if iteration should continue.
51   virtual bool ShouldStopIterating(HWND window) = 0;
52
53  private:
54   const std::set<HWND>& ignore_;
55
56   DISALLOW_COPY_AND_ASSIGN(BaseWindowFinder);
57 };
58
59 // TopMostFinder --------------------------------------------------------------
60
61 // Helper class to determine if a particular point of a window is not obscured
62 // by another window.
63 class TopMostFinder : public BaseWindowFinder {
64  public:
65   // Returns true if |window| is the topmost window at the location
66   // |screen_loc|, not including the windows in |ignore|.
67   static bool IsTopMostWindowAtPoint(HWND window,
68                                      const gfx::Point& screen_loc,
69                                      const std::set<HWND>& ignore) {
70     TopMostFinder finder(window, screen_loc, ignore);
71     return finder.is_top_most_;
72   }
73
74   virtual bool ShouldStopIterating(HWND hwnd) {
75     if (hwnd == target_) {
76       // Window is topmost, stop iterating.
77       is_top_most_ = true;
78       return true;
79     }
80
81     if (!IsWindowVisible(hwnd)) {
82       // The window isn't visible, keep iterating.
83       return false;
84     }
85
86     RECT r;
87     if (!GetWindowRect(hwnd, &r) || !PtInRect(&r, screen_loc_.ToPOINT())) {
88       // The window doesn't contain the point, keep iterating.
89       return false;
90     }
91
92     LONG ex_styles = GetWindowLong(hwnd, GWL_EXSTYLE);
93     if (ex_styles & WS_EX_TRANSPARENT || ex_styles & WS_EX_LAYERED) {
94       // Mouse events fall through WS_EX_TRANSPARENT windows, so we ignore them.
95       //
96       // WS_EX_LAYERED is trickier. Apps like Switcher create a totally
97       // transparent WS_EX_LAYERED window that is always on top. If we don't
98       // ignore WS_EX_LAYERED windows and there are totally transparent
99       // WS_EX_LAYERED windows then there are effectively holes on the screen
100       // that the user can't reattach tabs to. So we ignore them. This is a bit
101       // problematic in so far as WS_EX_LAYERED windows need not be totally
102       // transparent in which case we treat chrome windows as not being obscured
103       // when they really are, but this is better than not being able to
104       // reattach tabs.
105       return false;
106     }
107
108     // hwnd is at the point. Make sure the point is within the windows region.
109     if (GetWindowRgn(hwnd, tmp_region_.Get()) == ERROR) {
110       // There's no region on the window and the window contains the point. Stop
111       // iterating.
112       return true;
113     }
114
115     // The region is relative to the window's rect.
116     BOOL is_point_in_region = PtInRegion(tmp_region_.Get(),
117         screen_loc_.x() - r.left, screen_loc_.y() - r.top);
118     tmp_region_ = CreateRectRgn(0, 0, 0, 0);
119     // Stop iterating if the region contains the point.
120     return !!is_point_in_region;
121   }
122
123  private:
124   TopMostFinder(HWND window,
125                 const gfx::Point& screen_loc,
126                 const std::set<HWND>& ignore)
127       : BaseWindowFinder(ignore),
128         target_(window),
129         screen_loc_(screen_loc),
130         is_top_most_(false),
131         tmp_region_(CreateRectRgn(0, 0, 0, 0)) {
132     EnumWindows(WindowCallbackProc, as_lparam());
133   }
134
135   // The window we're looking for.
136   HWND target_;
137
138   // Location of window to find.
139   gfx::Point screen_loc_;
140
141   // Is target_ the top most window? This is initially false but set to true
142   // in ShouldStopIterating if target_ is passed in.
143   bool is_top_most_;
144
145   base::win::ScopedRegion tmp_region_;
146
147   DISALLOW_COPY_AND_ASSIGN(TopMostFinder);
148 };
149
150 // WindowFinder ---------------------------------------------------------------
151
152 // Helper class to determine if a particular point contains a window from our
153 // process.
154 class LocalProcessWindowFinder : public BaseWindowFinder {
155  public:
156   // Returns the hwnd from our process at screen_loc that is not obscured by
157   // another window. Returns NULL otherwise.
158   static gfx::NativeWindow GetProcessWindowAtPoint(
159       const gfx::Point& screen_loc,
160       const std::set<HWND>& ignore) {
161     LocalProcessWindowFinder finder(screen_loc, ignore);
162     // Windows 8 has a window that appears first in the list of iterated
163     // windows, yet is not visually on top of everything.
164     // TODO(sky): figure out a better way to ignore this window.
165     if (finder.result_ &&
166         ((base::win::OSInfo::GetInstance()->version() >=
167           base::win::VERSION_WIN8) ||
168          TopMostFinder::IsTopMostWindowAtPoint(finder.result_, screen_loc,
169                                                ignore))) {
170       return views::DesktopWindowTreeHostWin::GetContentWindowForHWND(
171           finder.result_);
172     }
173     return NULL;
174   }
175
176  protected:
177   virtual bool ShouldStopIterating(HWND hwnd) {
178     RECT r;
179     if (IsWindowVisible(hwnd) && GetWindowRect(hwnd, &r) &&
180         PtInRect(&r, screen_loc_.ToPOINT())) {
181       result_ = hwnd;
182       return true;
183     }
184     return false;
185   }
186
187  private:
188   LocalProcessWindowFinder(const gfx::Point& screen_loc,
189                            const std::set<HWND>& ignore)
190       : BaseWindowFinder(ignore),
191         screen_loc_(screen_loc),
192         result_(NULL) {
193     EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, as_lparam());
194   }
195
196   // Position of the mouse.
197   gfx::Point screen_loc_;
198
199   // The resulting window. This is initially null but set to true in
200   // ShouldStopIterating if an appropriate window is found.
201   HWND result_;
202
203   DISALLOW_COPY_AND_ASSIGN(LocalProcessWindowFinder);
204 };
205
206 std::set<HWND> RemapIgnoreSet(const std::set<gfx::NativeView>& ignore) {
207   std::set<HWND> hwnd_set;
208   std::set<gfx::NativeView>::const_iterator it = ignore.begin();
209   for (; it != ignore.end(); ++it) {
210     HWND w = (*it)->GetDispatcher()->host()->GetAcceleratedWidget();
211     if (w)
212       hwnd_set.insert(w);
213   }
214   return hwnd_set;
215 }
216
217 }  // namespace
218
219 aura::Window* GetLocalProcessWindowAtPoint(
220     chrome::HostDesktopType host_desktop_type,
221     const gfx::Point& screen_point,
222     const std::set<aura::Window*>& ignore) {
223 #if defined(USE_ASH)
224   if (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH)
225     return GetLocalProcessWindowAtPointAsh(screen_point, ignore);
226 #endif
227   return LocalProcessWindowFinder::GetProcessWindowAtPoint(
228           screen_point, RemapIgnoreSet(ignore));
229 }