Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / find_bar_host.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/views/find_bar_host.h"
6
7 #include <algorithm>
8
9 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
10 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
11 #include "chrome/browser/ui/view_ids.h"
12 #include "chrome/browser/ui/views/find_bar_view.h"
13 #include "chrome/browser/ui/views/frame/browser_view.h"
14 #include "content/public/browser/render_view_host.h"
15 #include "content/public/browser/web_contents.h"
16 #include "content/public/browser/web_contents_view.h"
17 #include "ui/events/event.h"
18 #include "ui/events/keycodes/keyboard_codes.h"
19 #include "ui/views/focus/external_focus_tracker.h"
20 #include "ui/views/focus/view_storage.h"
21 #include "ui/views/widget/root_view.h"
22 #include "ui/views/widget/widget.h"
23
24 using content::NativeWebKeyboardEvent;
25
26 namespace chrome {
27
28 // Declared in browser_dialogs.h so others don't have to depend on our header.
29 FindBar* CreateFindBar(BrowserView* browser_view) {
30   return new FindBarHost(browser_view);
31 }
32
33 }  // namespace chrome
34
35 ////////////////////////////////////////////////////////////////////////////////
36 // FindBarHost, public:
37
38 FindBarHost::FindBarHost(BrowserView* browser_view)
39     : DropdownBarHost(browser_view),
40       find_bar_controller_(NULL) {
41   FindBarView* find_bar_view = new FindBarView(this);
42   Init(browser_view->find_bar_host_view(), find_bar_view, find_bar_view);
43 }
44
45 FindBarHost::~FindBarHost() {
46 }
47
48 bool FindBarHost::MaybeForwardKeyEventToWebpage(
49     const ui::KeyEvent& key_event) {
50   if (!ShouldForwardKeyEventToWebpageNative(key_event)) {
51     // Native implementation says not to forward these events.
52     return false;
53   }
54
55   switch (key_event.key_code()) {
56     case ui::VKEY_DOWN:
57     case ui::VKEY_UP:
58     case ui::VKEY_PRIOR:
59     case ui::VKEY_NEXT:
60       break;
61     case ui::VKEY_HOME:
62     case ui::VKEY_END:
63       if (key_event.IsControlDown())
64         break;
65     // Fall through.
66     default:
67       return false;
68   }
69
70   content::WebContents* contents = find_bar_controller_->web_contents();
71   if (!contents)
72     return false;
73
74   content::RenderViewHost* render_view_host = contents->GetRenderViewHost();
75
76   // Make sure we don't have a text field element interfering with keyboard
77   // input. Otherwise Up and Down arrow key strokes get eaten. "Nom Nom Nom".
78   render_view_host->ClearFocusedElement();
79   NativeWebKeyboardEvent event = GetKeyboardEvent(contents, key_event);
80   render_view_host->ForwardKeyboardEvent(event);
81   return true;
82 }
83
84 FindBarController* FindBarHost::GetFindBarController() const {
85   return find_bar_controller_;
86 }
87
88 void FindBarHost::SetFindBarController(FindBarController* find_bar_controller) {
89   find_bar_controller_ = find_bar_controller;
90 }
91
92 void FindBarHost::Show(bool animate) {
93   DropdownBarHost::Show(animate);
94 }
95
96 void FindBarHost::Hide(bool animate) {
97   DropdownBarHost::Hide(animate);
98 }
99
100 void FindBarHost::SetFocusAndSelection() {
101   DropdownBarHost::SetFocusAndSelection();
102 }
103
104 void FindBarHost::ClearResults(const FindNotificationDetails& results) {
105   find_bar_view()->UpdateForResult(results, base::string16());
106 }
107
108 void FindBarHost::StopAnimation() {
109   DropdownBarHost::StopAnimation();
110 }
111
112 void FindBarHost::MoveWindowIfNecessary(const gfx::Rect& selection_rect,
113                                         bool no_redraw) {
114   // We only move the window if one is active for the current WebContents. If we
115   // don't check this, then SetWidgetPosition below will end up making the Find
116   // Bar visible.
117   content::WebContents* web_contents = find_bar_controller_->web_contents();
118   if (!web_contents)
119     return;
120
121   FindTabHelper* find_tab_helper = FindTabHelper::FromWebContents(web_contents);
122   if (!find_tab_helper || !find_tab_helper->find_ui_active())
123     return;
124
125   gfx::Rect new_pos = GetDialogPosition(selection_rect);
126   SetDialogPosition(new_pos, no_redraw);
127
128   // May need to redraw our frame to accommodate bookmark bar styles.
129   view()->Layout();  // Bounds may have changed.
130   view()->SchedulePaint();
131 }
132
133 void FindBarHost::SetFindTextAndSelectedRange(
134     const base::string16& find_text,
135     const gfx::Range& selected_range) {
136   find_bar_view()->SetFindTextAndSelectedRange(find_text, selected_range);
137 }
138
139 base::string16 FindBarHost::GetFindText() {
140   return find_bar_view()->GetFindText();
141 }
142
143 gfx::Range FindBarHost::GetSelectedRange() {
144   return find_bar_view()->GetSelectedRange();
145 }
146
147 void FindBarHost::UpdateUIForFindResult(const FindNotificationDetails& result,
148                                         const base::string16& find_text) {
149   // Make sure match count is clear. It may get set again in UpdateForResult
150   // if enough data is available.
151   find_bar_view()->ClearMatchCount();
152
153   if (!find_text.empty())
154     find_bar_view()->UpdateForResult(result, find_text);
155
156   // We now need to check if the window is obscuring the search results.
157   MoveWindowIfNecessary(result.selection_rect(), false);
158
159   // Once we find a match we no longer want to keep track of what had
160   // focus. EndFindSession will then set the focus to the page content.
161   if (result.number_of_matches() > 0)
162     ResetFocusTracker();
163 }
164
165 bool FindBarHost::IsFindBarVisible() {
166   return DropdownBarHost::IsVisible();
167 }
168
169 void FindBarHost::RestoreSavedFocus() {
170   if (focus_tracker() == NULL) {
171     // TODO(brettw): Focus() should be on WebContentsView.
172     find_bar_controller_->web_contents()->GetView()->Focus();
173   } else {
174     focus_tracker()->FocusLastFocusedExternalView();
175   }
176 }
177
178 bool FindBarHost::HasGlobalFindPasteboard() {
179   return false;
180 }
181
182 void FindBarHost::UpdateFindBarForChangedWebContents() {
183 }
184
185 FindBarTesting* FindBarHost::GetFindBarTesting() {
186   return this;
187 }
188
189 ////////////////////////////////////////////////////////////////////////////////
190 // FindBarWin, ui::AcceleratorTarget implementation:
191
192 bool FindBarHost::AcceleratorPressed(const ui::Accelerator& accelerator) {
193   ui::KeyboardCode key = accelerator.key_code();
194   if (key == ui::VKEY_RETURN && accelerator.IsCtrlDown()) {
195     // Ctrl+Enter closes the Find session and navigates any link that is active.
196     find_bar_controller_->EndFindSession(
197         FindBarController::kActivateSelectionOnPage,
198         FindBarController::kClearResultsInFindBox);
199     return true;
200   } else if (key == ui::VKEY_ESCAPE) {
201     // This will end the Find session and hide the window, causing it to loose
202     // focus and in the process unregister us as the handler for the Escape
203     // accelerator through the OnWillChangeFocus event.
204     find_bar_controller_->EndFindSession(
205         FindBarController::kKeepSelectionOnPage,
206         FindBarController::kKeepResultsInFindBox);
207     return true;
208   } else {
209     NOTREACHED() << "Unknown accelerator";
210   }
211
212   return false;
213 }
214
215 bool FindBarHost::CanHandleAccelerators() const {
216   return true;
217 }
218
219 ////////////////////////////////////////////////////////////////////////////////
220 // FindBarTesting implementation:
221
222 bool FindBarHost::GetFindBarWindowInfo(gfx::Point* position,
223                                       bool* fully_visible) {
224   if (!find_bar_controller_ ||
225 #if defined(OS_WIN) && !defined(USE_AURA)
226       !::IsWindow(host()->GetNativeView())) {
227 #else
228       false) {
229       // TODO(sky): figure out linux side.
230       // This is tricky due to asynchronous nature of x11.
231       // See bug http://crbug.com/28629.
232 #endif
233     if (position)
234       *position = gfx::Point();
235     if (fully_visible)
236       *fully_visible = false;
237     return false;
238   }
239
240   gfx::Rect window_rect = host()->GetWindowBoundsInScreen();
241   if (position)
242     *position = window_rect.origin();
243   if (fully_visible)
244     *fully_visible = IsVisible() && !IsAnimating();
245   return true;
246 }
247
248 base::string16 FindBarHost::GetFindSelectedText() {
249   return find_bar_view()->GetFindSelectedText();
250 }
251
252 base::string16 FindBarHost::GetMatchCountText() {
253   return find_bar_view()->GetMatchCountText();
254 }
255
256 int FindBarHost::GetWidth() {
257   return view()->width();
258 }
259
260 ////////////////////////////////////////////////////////////////////////////////
261 // Overridden from DropdownBarHost:
262
263 gfx::Rect FindBarHost::GetDialogPosition(gfx::Rect avoid_overlapping_rect) {
264   // Find the area we have to work with (after accounting for scrollbars, etc).
265   gfx::Rect widget_bounds;
266   GetWidgetBounds(&widget_bounds);
267   if (widget_bounds.IsEmpty())
268     return gfx::Rect();
269
270   // Ask the view how large an area it needs to draw on.
271   gfx::Size prefsize = view()->GetPreferredSize();
272
273   // Limit width to the available area.
274   if (widget_bounds.width() < prefsize.width())
275     prefsize.set_width(widget_bounds.width());
276
277   // Don't show the find bar if |widget_bounds| is not tall enough.
278   if (widget_bounds.height() < prefsize.height())
279     return gfx::Rect();
280
281   // Place the view in the top right corner of the widget boundaries (top left
282   // for RTL languages).
283   gfx::Rect view_location;
284   int x = widget_bounds.x();
285   if (!base::i18n::IsRTL())
286     x += widget_bounds.width() - prefsize.width();
287   int y = widget_bounds.y();
288   view_location.SetRect(x, y, prefsize.width(), prefsize.height());
289
290   // When we get Find results back, we specify a selection rect, which we
291   // should strive to avoid overlapping. But first, we need to offset the
292   // selection rect (if one was provided).
293   if (!avoid_overlapping_rect.IsEmpty()) {
294     // For comparison (with the Intersects function below) we need to account
295     // for the fact that we draw the Find widget relative to the Chrome frame,
296     // whereas the selection rect is relative to the page.
297     GetWidgetPositionNative(&avoid_overlapping_rect);
298   }
299
300   gfx::Rect new_pos = FindBarController::GetLocationForFindbarView(
301       view_location, widget_bounds, avoid_overlapping_rect);
302
303   // While we are animating, the Find window will grow bottoms up so we need to
304   // re-position the widget so that it appears to grow out of the toolbar.
305   if (animation_offset() > 0)
306     new_pos.Offset(0, std::min(0, -animation_offset()));
307
308   return new_pos;
309 }
310
311 void FindBarHost::SetDialogPosition(const gfx::Rect& new_pos, bool no_redraw) {
312   if (new_pos.IsEmpty())
313     return;
314
315   // Make sure the window edges are clipped to just the visible region. We need
316   // to do this before changing position, so that when we animate the closure
317   // of it it doesn't look like the window crumbles into the toolbar.
318   UpdateWindowEdges(new_pos);
319
320   SetWidgetPositionNative(new_pos, no_redraw);
321
322   // Tell the immersive mode controller about the find bar's new bounds. The
323   // immersive mode controller uses the bounds to keep the top-of-window views
324   // revealed when the mouse is hovered over the find bar.
325   browser_view()->immersive_mode_controller()->OnFindBarVisibleBoundsChanged(
326       host()->GetWindowBoundsInScreen());
327 }
328
329 void FindBarHost::GetWidgetBounds(gfx::Rect* bounds) {
330   DCHECK(bounds);
331   // The BrowserView does Layout for the components that we care about
332   // positioning relative to, so we ask it to tell us where we should go.
333   *bounds = browser_view()->GetFindBarBoundingBox();
334 }
335
336 void FindBarHost::RegisterAccelerators() {
337   DropdownBarHost::RegisterAccelerators();
338
339   // Register for Ctrl+Return.
340   ui::Accelerator escape(ui::VKEY_RETURN, ui::EF_CONTROL_DOWN);
341   focus_manager()->RegisterAccelerator(
342       escape, ui::AcceleratorManager::kNormalPriority, this);
343 }
344
345 void FindBarHost::UnregisterAccelerators() {
346   // Unregister Ctrl+Return.
347   ui::Accelerator escape(ui::VKEY_RETURN, ui::EF_CONTROL_DOWN);
348   focus_manager()->UnregisterAccelerator(escape, this);
349
350   DropdownBarHost::UnregisterAccelerators();
351 }
352
353 void FindBarHost::OnVisibilityChanged() {
354   // Tell the immersive mode controller about the find bar's bounds. The
355   // immersive mode controller uses the bounds to keep the top-of-window views
356   // revealed when the mouse is hovered over the find bar.
357   gfx::Rect visible_bounds;
358   if (IsVisible())
359     visible_bounds = host()->GetWindowBoundsInScreen();
360   browser_view()->immersive_mode_controller()->OnFindBarVisibleBoundsChanged(
361       visible_bounds);
362 }
363
364 ////////////////////////////////////////////////////////////////////////////////
365 // private:
366
367 void FindBarHost::GetWidgetPositionNative(gfx::Rect* avoid_overlapping_rect) {
368   gfx::Rect frame_rect = host()->GetTopLevelWidget()->GetWindowBoundsInScreen();
369   content::WebContentsView* tab_view =
370       find_bar_controller_->web_contents()->GetView();
371   gfx::Rect webcontents_rect = tab_view->GetViewBounds();
372   avoid_overlapping_rect->Offset(0, webcontents_rect.y() - frame_rect.y());
373 }