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