summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views
diff options
context:
space:
mode:
authorbrettw@google.com <brettw@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-21 15:03:01 +0000
committerbrettw@google.com <brettw@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-21 15:03:01 +0000
commit9e0534be9784821a8507f20a873d6410b21680e5 (patch)
treec6dd8baa8bf9986df0168cf54c6a5c8b5c49e0e9 /chrome/browser/views
parent1c023d6ec42e2dd30e8a31edbb2cdd49db86cfc4 (diff)
downloadchromium_src-9e0534be9784821a8507f20a873d6410b21680e5.zip
chromium_src-9e0534be9784821a8507f20a873d6410b21680e5.tar.gz
chromium_src-9e0534be9784821a8507f20a873d6410b21680e5.tar.bz2
Factor find in page out of WebContents and into the view, since it is almost
all view specific. Rename find in page controller to be "FindBarWin", and find in page view to be "findBarView" (since this is not Windows specific, but "Views" specific). I did lots of simplification and moving around of the find in page API. I coalesced several of the show functions into one master function on the view. I removed some other sizing and window updating code from WebContents and moved it to the view. Review URL: http://codereview.chromium.org/7667 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3654 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/views')
-rw-r--r--chrome/browser/views/browser_views.vcproj20
-rw-r--r--chrome/browser/views/constrained_window_impl.cc10
-rw-r--r--chrome/browser/views/find_bar_view.cc493
-rw-r--r--chrome/browser/views/find_bar_view.h138
-rw-r--r--chrome/browser/views/find_bar_win.cc692
-rw-r--r--chrome/browser/views/find_bar_win.h240
-rw-r--r--chrome/browser/views/find_bar_win_interactive_uitest.cc96
-rw-r--r--chrome/browser/views/find_bar_win_uitest.cc211
-rw-r--r--chrome/browser/views/tab_contents_container_view.cc19
9 files changed, 1912 insertions, 7 deletions
diff --git a/chrome/browser/views/browser_views.vcproj b/chrome/browser/views/browser_views.vcproj
index e8a17da..cfce335 100644
--- a/chrome/browser/views/browser_views.vcproj
+++ b/chrome/browser/views/browser_views.vcproj
@@ -433,6 +433,26 @@
>
</File>
</Filter>
+ <Filter
+ Name="Find Bar"
+ >
+ <File
+ RelativePath=".\find_bar_view.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\find_bar_view.h"
+ >
+ </File>
+ <File
+ RelativePath=".\find_bar_win.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\find_bar_win.h"
+ >
+ </File>
+ </Filter>
<File
RelativePath=".\about_chrome_view.cc"
>
diff --git a/chrome/browser/views/constrained_window_impl.cc b/chrome/browser/views/constrained_window_impl.cc
index dd948b7..e315e5a 100644
--- a/chrome/browser/views/constrained_window_impl.cc
+++ b/chrome/browser/views/constrained_window_impl.cc
@@ -15,6 +15,7 @@
#include "chrome/browser/toolbar_model.h"
#include "chrome/browser/web_app.h"
#include "chrome/browser/web_contents.h"
+#include "chrome/browser/web_contents_view.h"
#include "chrome/browser/window_sizer.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/gfx/chrome_canvas.h"
@@ -1132,9 +1133,16 @@ void ConstrainedWindowImpl::InitSizeForContents(
CustomFrameWindow::Init(owner_->GetContainerHWND(), initial_bounds);
contents_container_->Attach(constrained_contents_->GetContainerHWND());
- constrained_contents_->SizeContents(
+ // TODO(brettw) this should be done some other way, see
+ // WebContentsView::SizeContents.
+ if (constrained_contents_->AsWebContents()) {
+ // This should always be true, all constrained windows are WebContents.
+ constrained_contents_->AsWebContents()->view()->SizeContents(
gfx::Size(contents_container_->width(),
contents_container_->height()));
+ } else {
+ NOTREACHED();
+ }
current_bounds_ = initial_bounds;
// Note that this is HWND_TOP, not HWND_TOPMOST... this is important
diff --git a/chrome/browser/views/find_bar_view.cc b/chrome/browser/views/find_bar_view.cc
new file mode 100644
index 0000000..2307895
--- /dev/null
+++ b/chrome/browser/views/find_bar_view.cc
@@ -0,0 +1,493 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/views/find_bar_view.h"
+
+#include <algorithm>
+
+#include "base/string_util.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/views/find_bar_win.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/views/background.h"
+#include "chrome/views/label.h"
+#include "skia/include/SkGradientShader.h"
+
+#include "generated_resources.h"
+
+// The amount of whitespace to have before the find button.
+static const int kWhiteSpaceAfterMatchCountLabel = 3;
+
+// The margins around the search field and the close button.
+static const int kMarginLeftOfCloseButton = 5;
+static const int kMarginRightOfCloseButton = 5;
+static const int kMarginLeftOfFindTextField = 12;
+
+// The margins around the match count label (We add extra space so that the
+// background highlight extends beyond just the text).
+static const int kMatchCountExtraWidth = 9;
+
+// Minimum width for the match count label.
+static const int kMatchCountMinWidth = 30;
+
+// The text color for the match count label.
+static const SkColor kTextColorMatchCount = SkColorSetRGB(178, 178, 178);
+
+// The text color for the match count label when no matches are found.
+static const SkColor kTextColorNoMatch = SK_ColorBLACK;
+
+// The background color of the match count label when results are found.
+static const SkColor kBackgroundColorMatch = SkColorSetRGB(255, 255, 255);
+
+// The background color of the match count label when no results are found.
+static const SkColor kBackgroundColorNoMatch = SkColorSetRGB(255, 102, 102);
+
+// The background images for the dialog. They are split into a left, a middle
+// and a right part. The middle part determines the height of the dialog. The
+// middle part is stretched to fill any remaining part between the left and the
+// right image, after sizing the dialog to kWindowWidth.
+static const SkBitmap* kDlgBackground_left = NULL;
+static const SkBitmap* kDlgBackground_middle = NULL;
+static const SkBitmap* kDlgBackground_right = NULL;
+
+// These are versions of the above images but for use when the bookmarks bar
+// is extended (when toolbar_blend_ = false).
+static const SkBitmap* kDlgBackground_bb_left = NULL;
+static const SkBitmap* kDlgBackground_bb_middle = NULL;
+static const SkBitmap* kDlgBackground_bb_right = NULL;
+
+// When we are animating, we draw only the top part of the left and right
+// edges to give the illusion that the find dialog is attached to the
+// window during this animation; this is the height of the items we draw.
+static const int kAnimatingEdgeHeight = 5;
+
+// The background image for the Find text box, which we draw behind the Find box
+// to provide the Chrome look to the edge of the text box.
+static const SkBitmap* kBackground = NULL;
+
+// The rounded edge on the left side of the Find text box.
+static const SkBitmap* kBackground_left = NULL;
+
+// The default number of average characters that the text box will be. This
+// number brings the width on a "regular fonts" system to about 300px.
+static const int kDefaultCharWidth = 43;
+
+////////////////////////////////////////////////////////////////////////////////
+// FindBarView, public:
+
+FindBarView::FindBarView(FindBarWin* container)
+ : container_(container),
+ find_text_(NULL),
+ match_count_text_(NULL),
+ focus_forwarder_view_(NULL),
+ find_previous_button_(NULL),
+ find_next_button_(NULL),
+ close_button_(NULL),
+ animation_offset_(0),
+ toolbar_blend_(true),
+ match_count_(-1),
+ active_match_ordinal_(-1) {
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+
+ find_text_ = new views::TextField();
+ find_text_->SetFont(rb.GetFont(ResourceBundle::BaseFont));
+ find_text_->set_default_width_in_chars(kDefaultCharWidth);
+ AddChildView(find_text_);
+
+ match_count_text_ = new views::Label();
+ match_count_text_->SetFont(rb.GetFont(ResourceBundle::BaseFont));
+ match_count_text_->SetColor(kTextColorMatchCount);
+ match_count_text_->SetHorizontalAlignment(views::Label::ALIGN_CENTER);
+ AddChildView(match_count_text_);
+
+ // Create a focus forwarder view which sends focus to find_text_.
+ focus_forwarder_view_ = new FocusForwarderView(find_text_);
+ AddChildView(focus_forwarder_view_);
+
+ find_previous_button_ = new views::Button();
+ find_previous_button_->SetEnabled(false);
+ find_previous_button_->SetFocusable(true);
+ find_previous_button_->SetImage(views::Button::BS_NORMAL,
+ rb.GetBitmapNamed(IDR_FINDINPAGE_PREV));
+ find_previous_button_->SetImage(views::Button::BS_HOT,
+ rb.GetBitmapNamed(IDR_FINDINPAGE_PREV_H));
+ find_previous_button_->SetImage(views::Button::BS_DISABLED,
+ rb.GetBitmapNamed(IDR_FINDINPAGE_PREV_P));
+ find_previous_button_->SetTooltipText(
+ l10n_util::GetString(IDS_FIND_IN_PAGE_PREVIOUS_TOOLTIP));
+ AddChildView(find_previous_button_);
+
+ find_next_button_ = new views::Button();
+ find_next_button_->SetEnabled(false);
+ find_next_button_->SetFocusable(true);
+ find_next_button_->SetImage(views::Button::BS_NORMAL,
+ rb.GetBitmapNamed(IDR_FINDINPAGE_NEXT));
+ find_next_button_->SetImage(views::Button::BS_HOT,
+ rb.GetBitmapNamed(IDR_FINDINPAGE_NEXT_H));
+ find_next_button_->SetImage(views::Button::BS_DISABLED,
+ rb.GetBitmapNamed(IDR_FINDINPAGE_NEXT_P));
+ find_next_button_->SetTooltipText(
+ l10n_util::GetString(IDS_FIND_IN_PAGE_NEXT_TOOLTIP));
+ AddChildView(find_next_button_);
+
+ close_button_ = new views::Button();
+ close_button_->SetFocusable(true);
+ close_button_->SetImage(views::Button::BS_NORMAL,
+ rb.GetBitmapNamed(IDR_CLOSE_BAR));
+ close_button_->SetImage(views::Button::BS_HOT,
+ rb.GetBitmapNamed(IDR_CLOSE_BAR_H));
+ close_button_->SetImage(views::Button::BS_PUSHED,
+ rb.GetBitmapNamed(IDR_CLOSE_BAR_P));
+ close_button_->SetTooltipText(
+ l10n_util::GetString(IDS_FIND_IN_PAGE_CLOSE_TOOLTIP));
+ AddChildView(close_button_);
+
+ if (kDlgBackground_left == NULL) {
+ // Background images for the dialog.
+ kDlgBackground_left = rb.GetBitmapNamed(IDR_FIND_DLG_LEFT_BACKGROUND);
+ kDlgBackground_middle = rb.GetBitmapNamed(IDR_FIND_DLG_MIDDLE_BACKGROUND);
+ kDlgBackground_right = rb.GetBitmapNamed(IDR_FIND_DLG_RIGHT_BACKGROUND);
+ kDlgBackground_bb_left =
+ rb.GetBitmapNamed(IDR_FIND_DLG_LEFT_BB_BACKGROUND);
+ kDlgBackground_bb_middle =
+ rb.GetBitmapNamed(IDR_FIND_DLG_MIDDLE_BB_BACKGROUND);
+ kDlgBackground_bb_right =
+ rb.GetBitmapNamed(IDR_FIND_DLG_RIGHT_BB_BACKGROUND);
+
+ // Background images for the Find edit box.
+ kBackground = rb.GetBitmapNamed(IDR_FIND_BOX_BACKGROUND);
+ if (UILayoutIsRightToLeft())
+ kBackground_left = rb.GetBitmapNamed(IDR_FIND_BOX_BACKGROUND_LEFT_RTL);
+ else
+ kBackground_left = rb.GetBitmapNamed(IDR_FIND_BOX_BACKGROUND_LEFT);
+ }
+}
+
+FindBarView::~FindBarView() {
+}
+
+void FindBarView::ResetMatchCount() {
+ match_count_text_->SetText(std::wstring());
+ ResetMatchCountBackground();
+}
+
+void FindBarView::ResetMatchCountBackground() {
+ match_count_text_->SetBackground(
+ views::Background::CreateSolidBackground(kBackgroundColorMatch));
+ match_count_text_->SetColor(kTextColorMatchCount);
+}
+
+void FindBarView::UpdateMatchCount(int number_of_matches,
+ bool final_update) {
+ if (number_of_matches < 0) // We ignore -1 sent during FindNext operations.
+ return;
+
+ // If we have previously recorded a match-count number we don't want to
+ // overwrite it with a preliminary number of 1 (which the renderer sends when
+ // it found one match and is about to start scoping to find more). This way
+ // updates are smoother (as we don't flash '1' briefly after typing each
+ // letter of a query).
+ if (match_count_ > 0 && number_of_matches == 1 && !final_update)
+ return;
+
+ if (number_of_matches == 0)
+ active_match_ordinal_ = 0;
+
+ match_count_ = number_of_matches;
+
+ if (find_text_->GetText().empty() || number_of_matches > 0) {
+ ResetMatchCountBackground();
+ } else {
+ match_count_text_->SetBackground(
+ views::Background::CreateSolidBackground(kBackgroundColorNoMatch));
+ match_count_text_->SetColor(kTextColorNoMatch);
+ MessageBeep(MB_OK);
+ }
+}
+
+void FindBarView::UpdateActiveMatchOrdinal(int ordinal) {
+ if (ordinal >= 0)
+ active_match_ordinal_ = ordinal;
+}
+
+void FindBarView::UpdateResultLabel() {
+ std::wstring search_string = find_text_->GetText();
+
+ if (search_string.length() > 0) {
+ match_count_text_->SetText(
+ l10n_util::GetStringF(IDS_FIND_IN_PAGE_COUNT,
+ IntToWString(active_match_ordinal_),
+ IntToWString(match_count_)));
+ } else {
+ ResetMatchCount();
+ }
+
+ // Make sure Find Next and Find Previous are enabled if we found any matches.
+ find_previous_button_->SetEnabled(match_count_ > 0);
+ find_next_button_->SetEnabled(match_count_ > 0);
+
+ Layout(); // The match_count label may have increased/decreased in size.
+}
+
+void FindBarView::OnShow() {
+ find_text_->RequestFocus();
+ find_text_->SelectAll();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FindBarView, views::View overrides:
+
+void FindBarView::Paint(ChromeCanvas* canvas) {
+ SkPaint paint;
+
+ // Get the local bounds so that we now how much to stretch the background.
+ gfx::Rect lb = GetLocalBounds(true);
+
+ // First, we draw the background image for the whole dialog (3 images: left,
+ // middle and right). Note, that the window region has been set by the
+ // controller, so the whitespace in the left and right background images is
+ // actually outside the window region and is therefore not drawn. See
+ // FindInPageContainerWin::CreateRoundedWindowEdges() for details.
+ const SkBitmap *bg_left =
+ toolbar_blend_ ? kDlgBackground_left : kDlgBackground_bb_left;
+ const SkBitmap *bg_middle =
+ toolbar_blend_ ? kDlgBackground_middle : kDlgBackground_bb_middle;
+ const SkBitmap *bg_right =
+ toolbar_blend_ ? kDlgBackground_right : kDlgBackground_bb_right;
+
+ canvas->TileImageInt(*bg_left,
+ 0,
+ 0,
+ bg_left->width(),
+ bg_left->height());
+
+ // Stretch the middle background to cover all of the area between the two
+ // other images.
+ canvas->TileImageInt(*bg_middle,
+ bg_left->width(),
+ 0,
+ lb.width() -
+ bg_left->width() -
+ bg_right->width(),
+ bg_middle->height());
+
+ canvas->TileImageInt(*bg_right,
+ lb.right() - bg_right->width(),
+ 0,
+ bg_right->width(),
+ bg_right->height());
+
+ // Then we draw the background image for the Find TextField. We start by
+ // calculating the position of background images for the Find text box.
+ gfx::Rect find_text_rect;
+ gfx::Rect back_button_rect;
+ int x = 0; // x coordinate of the curved edge background image.
+ int w = 0; // width of the background image for the text field.
+ if (UILayoutIsRightToLeft()) {
+ find_text_rect = find_text_->GetBounds(APPLY_MIRRORING_TRANSFORMATION);
+ back_button_rect =
+ find_previous_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION);
+ x = find_text_rect.right();
+ w = find_text_rect.right() - back_button_rect.right();
+ } else {
+ find_text_rect = find_text_->bounds();
+ back_button_rect = find_previous_button_->bounds();
+ x = find_text_rect.x() - kBackground_left->width();
+ w = back_button_rect.x() - find_text_rect.x();
+ }
+
+ // Draw the image to the left that creates a curved left edge for the box
+ // (drawn on the right for RTL languages).
+ canvas->TileImageInt(*kBackground_left,
+ x,
+ back_button_rect.y(),
+ kBackground_left->width(),
+ kBackground_left->height());
+
+ // Draw the top and bottom border for whole text box (encompasses both the
+ // find_text_ edit box and the match_count_text_ label).
+ int background_height = kBackground->height();
+ canvas->TileImageInt(*kBackground,
+ UILayoutIsRightToLeft() ?
+ back_button_rect.right() : find_text_rect.x(),
+ back_button_rect.y(),
+ w,
+ background_height);
+
+ if (animation_offset_ > 0) {
+ // While animating we draw the curved edges at the point where the
+ // controller told us the top of the window is: |animation_offset_|.
+ canvas->TileImageInt(*bg_left,
+ lb.x(),
+ animation_offset_,
+ bg_left->width(),
+ kAnimatingEdgeHeight);
+ canvas->TileImageInt(*bg_right,
+ lb.right() - bg_right->width(),
+ animation_offset_,
+ bg_right->width(),
+ kAnimatingEdgeHeight);
+ }
+}
+
+void FindBarView::Layout() {
+ gfx::Size panel_size = GetPreferredSize();
+
+ // First we draw the close button on the far right.
+ gfx::Size sz = close_button_->GetPreferredSize();
+ close_button_->SetBounds(panel_size.width() - sz.width() -
+ kMarginRightOfCloseButton,
+ (height() - sz.height()) / 2,
+ sz.width(),
+ sz.height());
+ close_button_->SetListener(this, CLOSE_TAG);
+
+ // Next, the FindNext button to the left the close button.
+ sz = find_next_button_->GetPreferredSize();
+ find_next_button_->SetBounds(close_button_->x() -
+ find_next_button_->width() -
+ kMarginLeftOfCloseButton,
+ (height() - sz.height()) / 2,
+ sz.width(),
+ sz.height());
+ find_next_button_->SetListener(this, FIND_NEXT_TAG);
+
+ // Then, the FindPrevious button to the left the FindNext button.
+ sz = find_previous_button_->GetPreferredSize();
+ find_previous_button_->SetBounds(find_next_button_->x() -
+ find_previous_button_->width(),
+ (height() - sz.height()) / 2,
+ sz.width(),
+ sz.height());
+ find_previous_button_->SetListener(this, FIND_PREVIOUS_TAG);
+
+ // Then the label showing the match count number.
+ sz = match_count_text_->GetPreferredSize();
+ // We extend the label bounds a bit to give the background highlighting a bit
+ // of breathing room (margins around the text).
+ sz.Enlarge(kMatchCountExtraWidth, 0);
+ sz.set_width(std::max(kMatchCountMinWidth, static_cast<int>(sz.width())));
+ match_count_text_->SetBounds(find_previous_button_->x() -
+ kWhiteSpaceAfterMatchCountLabel -
+ sz.width(),
+ (height() - sz.height()) / 2 + 1,
+ sz.width(),
+ sz.height());
+
+ // And whatever space is left in between, gets filled up by the find edit box.
+ sz = find_text_->GetPreferredSize();
+ sz.set_width(match_count_text_->x() - kMarginLeftOfFindTextField);
+ find_text_->SetBounds(match_count_text_->x() - sz.width(),
+ (height() - sz.height()) / 2 + 1,
+ sz.width(),
+ sz.height());
+ find_text_->SetController(this);
+ find_text_->RequestFocus();
+
+ // The focus forwarder view is a hidden view that should cover the area
+ // between the find text box and the find button so that when the user clicks
+ // in that area we focus on the find text box.
+ int find_text_edge = find_text_->x() + find_text_->width();
+ focus_forwarder_view_->SetBounds(find_text_edge,
+ find_previous_button_->y(),
+ find_previous_button_->x() -
+ find_text_edge,
+ find_previous_button_->height());
+}
+
+void FindBarView::ViewHierarchyChanged(bool is_add,
+ View *parent,
+ View *child) {
+ if (is_add && child == this) {
+ find_text_->SetHorizontalMargins(3, 3); // Left and Right margins.
+ find_text_->RemoveBorder(); // We draw our own border (a background image).
+ }
+}
+
+gfx::Size FindBarView::GetPreferredSize() {
+ gfx::Size prefsize = find_text_->GetPreferredSize();
+ prefsize.set_height(kDlgBackground_middle->height());
+
+ // Add up all the preferred sizes and margins of the rest of the controls.
+ prefsize.Enlarge(kMarginLeftOfCloseButton + kMarginRightOfCloseButton +
+ kMarginLeftOfFindTextField,
+ 0);
+ prefsize.Enlarge(find_previous_button_->GetPreferredSize().width(), 0);
+ prefsize.Enlarge(find_next_button_->GetPreferredSize().width(), 0);
+ prefsize.Enlarge(close_button_->GetPreferredSize().width(), 0);
+ return prefsize;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FindBarView, views::BaseButton::ButtonListener implementation:
+
+void FindBarView::ButtonPressed(views::BaseButton* sender) {
+ switch (sender->GetTag()) {
+ case FIND_PREVIOUS_TAG:
+ case FIND_NEXT_TAG:
+ if (find_text_->GetText().length() > 0) {
+ container_->set_find_string(find_text_->GetText());
+ container_->StartFinding(sender->GetTag() == FIND_NEXT_TAG);
+ }
+ break;
+ case CLOSE_TAG:
+ container_->EndFindSession();
+ break;
+ default:
+ NOTREACHED() << L"Unknown button";
+ break;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FindBarView, views::TextField::Controller implementation:
+
+void FindBarView::ContentsChanged(views::TextField* sender,
+ const std::wstring& new_contents) {
+ // When the user changes something in the text box we check the contents and
+ // if the textbox contains something we set it as the new search string and
+ // initiate search (even though old searches might be in progress).
+ if (new_contents.length() > 0) {
+ container_->set_find_string(new_contents);
+ container_->StartFinding(true);
+ } else {
+ // The textbox is empty so we reset.
+ UpdateMatchCount(0, true); // true = final update.
+ UpdateResultLabel();
+ container_->StopFinding(true); // true = clear selection on page.
+ container_->set_find_string(std::wstring());
+ }
+}
+
+void FindBarView::HandleKeystroke(views::TextField* sender, UINT message,
+ TCHAR key, UINT repeat_count, UINT flags) {
+ // If the dialog is not visible, there is no reason to process keyboard input.
+ if (!container_->IsVisible())
+ return;
+
+ switch (key) {
+ case VK_RETURN: {
+ // Pressing Return/Enter starts the search (unless text box is empty).
+ std::wstring find_string = find_text_->GetText();
+ if (find_string.length() > 0) {
+ container_->set_find_string(find_string);
+ // Search forwards for enter, backwards for shift-enter.
+ container_->StartFinding(GetKeyState(VK_SHIFT) >= 0);
+ }
+ break;
+ }
+ }
+}
+
+bool FindBarView::FocusForwarderView::OnMousePressed(
+ const views::MouseEvent& event) {
+ if (view_to_focus_on_mousedown_) {
+ view_to_focus_on_mousedown_->ClearSelection();
+ view_to_focus_on_mousedown_->RequestFocus();
+ }
+ return true;
+}
+
diff --git a/chrome/browser/views/find_bar_view.h b/chrome/browser/views/find_bar_view.h
new file mode 100644
index 0000000..146f96b
--- /dev/null
+++ b/chrome/browser/views/find_bar_view.h
@@ -0,0 +1,138 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_VIEWS_FIND_BAR_VIEW_H_
+#define CHROME_BROWSER_VIEWS_FIND_BAR_VIEW_H_
+
+#include "base/gfx/size.h"
+#include "chrome/views/button.h"
+#include "chrome/views/text_field.h"
+
+class FindBarWin;
+
+namespace views {
+class Label;
+class MouseEvent;
+class View;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// The FindInPageView is responsible for drawing the UI controls of the
+// FindInPage window, the find text box, the 'Find' button and the 'Close'
+// button. It communicates the user search words to the FindBarWin.
+//
+////////////////////////////////////////////////////////////////////////////////
+class FindBarView : public views::View,
+ public views::BaseButton::ButtonListener,
+ public views::TextField::Controller {
+ public:
+ // A tag denoting which button the user pressed.
+ enum ButtonTag {
+ FIND_PREVIOUS_TAG = 0, // The Find Previous button.
+ FIND_NEXT_TAG, // The Find Next button.
+ CLOSE_TAG, // The Close button (the 'X').
+ };
+
+ FindBarView(FindBarWin* container);
+ virtual ~FindBarView();
+
+ // Updates the UI to show how many matches were found on the page/frames.
+ // This function does nothing if |number_of_matches| is below 0, which can
+ // happen during a FindNext operation when a scoping effort is already in
+ // progress. |final_update| specifies whether this is the last update message
+ // this Find operation will produce or if this is just a preliminary report.
+ void UpdateMatchCount(int number_of_matches, bool final_update);
+
+ // Notifies the view of the ordinal for the currently active item on the page.
+ void UpdateActiveMatchOrdinal(int ordinal);
+
+ // Updates the label inside the Find text box that shows the ordinal of the
+ // active item and how many matches were found.
+ void UpdateResultLabel();
+
+ // Resets the UI element that shows how many matches were found.
+ void ResetMatchCount();
+
+ // Resets the background for the match count label.
+ void ResetMatchCountBackground();
+
+ // View needs to respond to Show to set focus to the find text box
+ void OnShow();
+
+ // Updates the view to let it know where the controller is clipping the
+ // Find window (while animating the opening or closing of the window).
+ void animation_offset(int offset) { animation_offset_ = offset; }
+
+ // Overridden from views::View:
+ virtual void Paint(ChromeCanvas* canvas);
+ virtual void Layout();
+ virtual gfx::Size GetPreferredSize();
+ virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child);
+
+ // Overridden from views::ButtonListener::ButtonPressed:
+ virtual void ButtonPressed(views::BaseButton* sender);
+
+ // Overridden from views::TextField::Controller:
+ virtual void ContentsChanged(views::TextField* sender,
+ const std::wstring& new_contents);
+ virtual void HandleKeystroke(views::TextField* sender, UINT message,
+ TCHAR key, UINT repeat_count, UINT flags);
+
+ // Set whether or not we're attempting to blend with the toolbar.
+ void SetToolbarBlend(bool toolbar_blend) {toolbar_blend_ = toolbar_blend;}
+
+ private:
+ // We use a hidden view to grab mouse clicks and bring focus to the find
+ // text box. This is because although the find text box may look like it
+ // extends all the way to the find button, it only goes as far as to the
+ // match_count label. The user, however, expects being able to click anywhere
+ // inside what looks like the find text box (including on or around the
+ // match_count label) and have focus brought to the find box.
+ class FocusForwarderView : public views::View {
+ public:
+ explicit FocusForwarderView(
+ views::TextField* view_to_focus_on_mousedown)
+ : view_to_focus_on_mousedown_(view_to_focus_on_mousedown) {}
+
+ private:
+ virtual bool OnMousePressed(const views::MouseEvent& event);
+
+ views::TextField* view_to_focus_on_mousedown_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusForwarderView);
+ };
+
+ // Manages the OS-specific view for the find bar and acts as an intermediary
+ // between us and the WebContentsView.
+ FindBarWin* container_;
+
+ // The controls in the window.
+ views::TextField* find_text_;
+ views::Label* match_count_text_;
+ FocusForwarderView* focus_forwarder_view_;
+ views::Button* find_previous_button_;
+ views::Button* find_next_button_;
+ views::Button* close_button_;
+
+ // Whether or not we're attempting to blend with the toolbar (this is
+ // false if the bookmarks bar is visible).
+ bool toolbar_blend_;
+
+ // While animating, the controller clips the window and draws only the bottom
+ // part of it. The view needs to know the pixel offset at which we are drawing
+ // the window so that we can draw the curved edges that attach to the toolbar
+ // in the right location.
+ int animation_offset_;
+
+ // How many matches were found on the page.
+ int match_count_;
+
+ // The ordinal of the currently active match.
+ int active_match_ordinal_;
+
+ DISALLOW_COPY_AND_ASSIGN(FindBarView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_FIND_BAR_VIEW_H_
diff --git a/chrome/browser/views/find_bar_win.cc b/chrome/browser/views/find_bar_win.cc
new file mode 100644
index 0000000..1633981
--- /dev/null
+++ b/chrome/browser/views/find_bar_win.cc
@@ -0,0 +1,692 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/views/find_bar_win.h"
+
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/find_notification_details.h"
+#include "chrome/browser/render_view_host.h"
+#include "chrome/browser/view_ids.h"
+#include "chrome/browser/views/bookmark_bar_view.h"
+#include "chrome/browser/views/find_bar_view.h"
+#include "chrome/browser/web_contents.h"
+#include "chrome/browser/web_contents_view.h"
+#include "chrome/views/container_win.h"
+#include "chrome/views/external_focus_tracker.h"
+#include "chrome/views/native_scroll_bar.h"
+#include "chrome/views/root_view.h"
+#include "chrome/views/view_storage.h"
+
+int FindBarWin::request_id_counter_ = 0;
+
+// The minimum space between the FindInPage window and the search result.
+static const int kMinFindWndDistanceFromSelection = 5;
+
+// The amount of space we expect the window border to take up.
+static const int kWindowBorderWidth = 3;
+
+////////////////////////////////////////////////////////////////////////////////
+// FindBarWin, public:
+
+FindBarWin::FindBarWin(WebContentsView* parent_tab, HWND parent_hwnd)
+ : parent_tab_(parent_tab),
+ current_request_id_(request_id_counter_++),
+ parent_hwnd_(parent_hwnd),
+ find_dialog_animation_offset_(0),
+ show_on_tab_selection_(false),
+ focus_manager_(NULL),
+ old_accel_target_for_esc_(NULL) {
+ // Start listening to focus changes, so we can register and unregister our
+ // own handler for Escape.
+ SetFocusChangeListener(parent_hwnd);
+
+ // Don't let ContainerWin manage our lifetime. We want our lifetime to
+ // coincide with WebContents.
+ ContainerWin::set_delete_on_destroy(false);
+
+ view_ = new FindBarView(this);
+
+ views::FocusManager* focus_manager;
+ focus_manager = views::FocusManager::GetFocusManager(parent_hwnd_);
+ DCHECK(focus_manager);
+
+ // Stores the currently focused view, and tracks focus changes so that we can
+ // restore focus when the find box is closed.
+ focus_tracker_.reset(new views::ExternalFocusTracker(view_, focus_manager));
+
+ // Figure out where to place the dialog, initialize and set the position.
+ gfx::Rect find_dlg_rect = GetDialogPosition(gfx::Rect());
+ set_window_style(WS_CHILD | WS_CLIPCHILDREN);
+ set_window_ex_style(WS_EX_TOPMOST);
+ ContainerWin::Init(parent_hwnd, find_dlg_rect, false);
+ SetContentsView(view_);
+
+ // Start the process of animating the opening of the window.
+ animation_.reset(new SlideAnimation(this));
+ animation_->Show();
+}
+
+FindBarWin::~FindBarWin() {
+ Close();
+}
+
+// TODO(brettw) this should not be so complicated. The view should really be in
+// charge of these regions. CustomFrameWindow will do this for us. It will also
+// let us set a path for the window region which will avoid some logic here.
+void FindBarWin::UpdateWindowEdges(const gfx::Rect& new_pos) {
+ int w = new_pos.width();
+ int h = new_pos.height();
+
+ // This polygon array represents the outline of the background image for the
+ // dialog. Basically, it encompasses only the visible pixels of the
+ // concatenated find_dlg_LMR_bg images (where LMR = [left | middle | right]).
+ static const POINT polygon[] = {
+ {0, 0}, {0, 1}, {2, 3}, {2, 29}, {4, 31},
+ {4, 32}, {w+0, 32},
+ {w+0, 31}, {w+1, 31}, {w+3, 29}, {w+3, 3}, {w+6, 0}
+ };
+
+ // Find the largest x and y value in the polygon.
+ int max_x = 0, max_y = 0;
+ for (int i = 0; i < arraysize(polygon); i++) {
+ max_x = std::max(max_x, static_cast<int>(polygon[i].x));
+ max_y = std::max(max_y, static_cast<int>(polygon[i].y));
+ }
+
+ // We then create the polygon and use SetWindowRgn to force the window to draw
+ // only within that area. This region may get reduced in size below.
+ HRGN region = CreatePolygonRgn(polygon, arraysize(polygon), ALTERNATE);
+
+ // Are we animating?
+ if (find_dialog_animation_offset_ > 0) {
+ // The animation happens in two steps: First, we clip the window and then in
+ // GetDialogPosition we offset the window position so that it still looks
+ // attached to the toolbar as it grows. We clip the window by creating a
+ // rectangle region (that gradually increases as the animation progresses)
+ // and find the intersection between the two regions using CombineRgn.
+
+ // |y| shrinks as the animation progresses from the height of the view down
+ // to 0 (and reverses when closing).
+ int y = find_dialog_animation_offset_;
+ // |y| shrinking means the animation (visible) region gets larger. In other
+ // words: the rectangle grows upward (when the dialog is opening).
+ HRGN animation_region = CreateRectRgn(0, y, max_x, max_y);
+ // |region| will contain the intersected parts after calling this function:
+ CombineRgn(region, animation_region, region, RGN_AND);
+ DeleteObject(animation_region);
+
+ // Next, we need to increase the region a little bit to account for the
+ // curved edges that the view will draw to make it look like grows out of
+ // the toolbar.
+ POINT left_curve[] = {
+ {0, y+0}, {0, y+1}, {2, y+3}, {2, y+0}, {0, y+0}
+ };
+ POINT right_curve[] = {
+ {w+3, y+3}, {w+6, y+0}, {w+3, y+0}, {w+3, y+3}
+ };
+
+ // Combine the region for the curve on the left with our main region.
+ HRGN r = CreatePolygonRgn(left_curve, arraysize(left_curve), ALTERNATE);
+ CombineRgn(region, r, region, RGN_OR);
+ DeleteObject(r);
+
+ // Combine the region for the curve on the right with our main region.
+ r = CreatePolygonRgn(right_curve, arraysize(right_curve), ALTERNATE);
+ CombineRgn(region, r, region, RGN_OR);
+ DeleteObject(r);
+ }
+
+ // Now see if we need to truncate the region because parts of it obscures
+ // the main window border.
+ gfx::Rect dialog_bounds;
+ GetDialogBounds(&dialog_bounds);
+
+ // Calculate how much our current position overlaps our boundaries. If we
+ // overlap, it means we have too little space to draw the whole dialog and
+ // we allow overwriting the scrollbar before we start truncating our dialog.
+ //
+ // TODO(brettw) this constant is evil. This is the amount of room we've added
+ // to the window size, when we set the region, it can change the size.
+ static const int kAddedWidth = 14;
+ int difference = (curr_pos_relative_.right() - kAddedWidth) -
+ dialog_bounds.width() -
+ views::NativeScrollBar::GetVerticalScrollBarWidth() +
+ 1;
+ if (difference > 0) {
+ POINT exclude[4];
+ exclude[0].x = max_x - difference; // Top left corner.
+ exclude[0].y = 0;
+
+ exclude[1].x = max_x; // Top right corner.
+ exclude[1].y = 0;
+
+ exclude[2].x = max_x; // Bottom right corner.
+ exclude[2].y = max_y;
+
+ exclude[3].x = max_x - difference; // Bottom left corner.
+ exclude[3].y = max_y;
+
+ // Subtract this region from the original region.
+ HRGN exclude_rgn = CreatePolygonRgn(exclude, arraysize(exclude), ALTERNATE);
+ int result = CombineRgn(region, region, exclude_rgn, RGN_DIFF);
+ DeleteObject(exclude_rgn);
+ }
+
+ // The system now owns the region, so we do not delete it.
+ SetWindowRgn(region, TRUE); // TRUE = Redraw.
+}
+
+void FindBarWin::Show() {
+ // Note: This function is called when the user presses Ctrl+F or switches back
+ // to the parent tab of the Find window (assuming the Find window has been
+ // opened at least once). If the Find window is already visible, we should
+ // just forward the command to the view so that it will select all text and
+ // grab focus. If the window is not visible, however, there are two scenarios:
+ if (!IsVisible() && !animation_->IsAnimating()) {
+ if (show_on_tab_selection_) {
+ // The tab just got re-selected and we need to show the window again
+ // (without animation). We also want to reset the window location so that
+ // we don't surprise the user by popping up to the left for no apparent
+ // reason.
+ gfx::Rect new_pos = GetDialogPosition(gfx::Rect());
+ SetDialogPosition(new_pos);
+ } else {
+ // The Find window was dismissed and we need to start the animation again.
+ animation_->Show();
+ }
+ }
+
+ view_->OnShow();
+}
+
+bool FindBarWin::IsAnimating() {
+ return animation_->IsAnimating();
+}
+
+void FindBarWin::EndFindSession() {
+ if (IsVisible()) {
+ show_on_tab_selection_ = false;
+ animation_->Hide();
+
+ // We reset the match count here so that we don't show old results when the
+ // user has navigated to another page. We could alternatively achieve the
+ // same effect by nulling the search string, but then the user looses the
+ // last search that was entered, which can be frustrating if searching for
+ // the same string on multiple pages.
+ view_->ResetMatchCount();
+
+ // When we hide the window, we need to notify the renderer that we are done
+ // for now, so that we can abort the scoping effort and clear all the
+ // tick-marks and highlighting.
+ StopFinding(false); // false = don't clear selection on page.
+
+ // When we get dismissed we restore the focus to where it belongs.
+ RestoreSavedFocus();
+ }
+}
+
+void FindBarWin::Close() {
+ // We may already have been destroyed if the selection resulted in a tab
+ // switch which will have reactivated the browser window and closed us, so
+ // we need to check to see if we're still a window before trying to destroy
+ // ourself.
+ if (IsWindow())
+ DestroyWindow();
+}
+
+void FindBarWin::DidBecomeSelected() {
+ if (!IsVisible() && show_on_tab_selection_) {
+ Show();
+ show_on_tab_selection_ = false;
+ }
+}
+
+void FindBarWin::DidBecomeUnselected() {
+ if (::IsWindow(GetHWND()) && IsVisible()) {
+ // Finish any existing animations.
+ if (animation_->IsAnimating()) {
+ show_on_tab_selection_ = animation_->IsShowing();
+ animation_->End();
+ } else {
+ show_on_tab_selection_ = true;
+ }
+
+ ShowWindow(SW_HIDE);
+ }
+}
+
+void FindBarWin::StartFinding(bool forward_direction) {
+ if (find_string_.empty())
+ return;
+
+ bool find_next = last_find_string_ == find_string_;
+ if (!find_next)
+ current_request_id_ = request_id_counter_++;
+
+ last_find_string_ = find_string_;
+
+ GetRenderViewHost()->StartFinding(current_request_id_,
+ find_string_,
+ forward_direction,
+ false, // case sensitive
+ find_next);
+}
+
+void FindBarWin::StopFinding(bool clear_selection) {
+ last_find_string_.clear();
+ GetRenderViewHost()->StopFinding(clear_selection);
+}
+
+void FindBarWin::MoveWindowIfNecessary(
+ const gfx::Rect& selection_rect) {
+ gfx::Rect new_pos = GetDialogPosition(selection_rect);
+ SetDialogPosition(new_pos);
+
+ // May need to redraw our frame to accomodate bookmark bar
+ // styles.
+ view_->SchedulePaint();
+}
+
+void FindBarWin::RespondToResize(const gfx::Size& new_size) {
+ if (!IsVisible())
+ return;
+
+ // We are only interested in changes to width.
+ if (window_size_.width() == new_size.width())
+ return;
+
+ // Save the new size so we can compare later and ignore future invocations
+ // of RespondToResize.
+ window_size_ = new_size;
+
+ gfx::Rect new_pos = GetDialogPosition(gfx::Rect());
+ SetDialogPosition(new_pos);
+}
+
+void FindBarWin::SetParent(HWND new_parent) {
+ DCHECK(new_parent);
+ if (parent_hwnd_ != new_parent) {
+ // Sync up the focus listener with the new focus manager.
+ SetFocusChangeListener(new_parent);
+
+ parent_hwnd_ = new_parent;
+ ::SetParent(GetHWND(), new_parent);
+
+ // The MSDN documentation specifies that you need to manually update the
+ // UI state after changing the parent.
+ ::SendMessage(new_parent,
+ WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0);
+
+ // We have a new focus manager now, so start tracking with that.
+ focus_tracker_.reset(new views::ExternalFocusTracker(view_,
+ focus_manager_));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FindBarWin, views::ContainerWin implementation:
+
+void FindBarWin::OnFinalMessage(HWND window) {
+ // We are exiting, so we no longer need to monitor focus changes.
+ focus_manager_->RemoveFocusChangeListener(this);
+
+ // Destroy the focus tracker now, otherwise by the time we're destroyed the
+ // focus manager the focus tracker is referencing may have already been
+ // destroyed resulting in the focus tracker trying to reference a deleted
+ // focus manager.
+ focus_tracker_.reset(NULL);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// FindBarWin, views::FocusChangeListener implementation:
+
+void FindBarWin::FocusWillChange(views::View* focused_before,
+ views::View* focused_now) {
+ // First we need to determine if one or both of the views passed in are child
+ // views of our view.
+ bool our_view_before = focused_before && view_->IsParentOf(focused_before);
+ bool our_view_now = focused_now && view_->IsParentOf(focused_now);
+
+ // When both our_view_before and our_view_now are false, it means focus is
+ // changing hands elsewhere in the application (and we shouldn't do anything).
+ // Similarly, when both are true, focus is changing hands within the Find
+ // window (and again, we should not do anything). We therefore only need to
+ // look at when we gain initial focus and when we loose it.
+ if (!our_view_before && our_view_now) {
+ // We are gaining focus from outside the Find window so we must register
+ // a handler for Escape.
+ RegisterEscAccelerator();
+ } else if (our_view_before && !our_view_now) {
+ // We are losing focus to something outside our window so we restore the
+ // original handler for Escape.
+ UnregisterEscAccelerator();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FindBarWin, views::AcceleratorTarget implementation:
+
+bool FindBarWin::AcceleratorPressed(const views::Accelerator& accelerator) {
+ DCHECK(accelerator.GetKeyCode() == VK_ESCAPE); // We only expect Escape key.
+ // This will end the Find session and hide the window, causing it to loose
+ // focus and in the process unregister us as the handler for the Escape
+ // accelerator through the FocusWillChange event.
+ EndFindSession();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FindBarWin, AnimationDelegate implementation:
+
+void FindBarWin::AnimationProgressed(const Animation* animation) {
+ // First, we calculate how many pixels to slide the window.
+ find_dialog_animation_offset_ =
+ static_cast<int>((1.0 - animation_->GetCurrentValue()) *
+ view_->height());
+
+ // This call makes sure it appears in the right location, the size and shape
+ // is correct and that it slides in the right direction.
+ gfx::Rect find_dlg_rect = GetDialogPosition(gfx::Rect());
+ SetDialogPosition(find_dlg_rect);
+
+ // Let the view know if we are animating, and at which offset to draw the
+ // edges.
+ view_->animation_offset(find_dialog_animation_offset_);
+ view_->SchedulePaint();
+}
+
+void FindBarWin::AnimationEnded(const Animation* animation) {
+ if (!animation_->IsShowing()) {
+ // Animation has finished closing.
+ find_dialog_animation_offset_ = 0;
+ ShowWindow(SW_HIDE);
+ } else {
+ // Animation has finished opening.
+ }
+}
+
+void FindBarWin::OnFindReply(int request_id,
+ int number_of_matches,
+ const gfx::Rect& selection_rect,
+ int active_match_ordinal,
+ bool final_update) {
+ // Ignore responses for requests other than the one we have most recently
+ // issued. That way we won't act on stale results when the user has
+ // already typed in another query.
+ if (view_ && request_id == current_request_id_) {
+ view_->UpdateMatchCount(number_of_matches, final_update);
+ view_->UpdateActiveMatchOrdinal(active_match_ordinal);
+ view_->UpdateResultLabel();
+
+ // We now need to check if the window is obscuring the search results.
+ if (!selection_rect.IsEmpty())
+ MoveWindowIfNecessary(selection_rect);
+
+ // Once we find a match we no longer want to keep track of what had
+ // focus. EndFindSession will then set the focus to the page content.
+ if (number_of_matches > 0)
+ focus_tracker_.reset(NULL);
+ }
+
+ // Notify all observers of this notification, such as the automation
+ // providers which do UI tests for find in page.
+ FindNotificationDetails detail(request_id,
+ number_of_matches,
+ selection_rect,
+ active_match_ordinal,
+ final_update);
+ NotificationService::current()->Notify(
+ NOTIFY_FIND_RESULT_AVAILABLE,
+ Source<TabContents>(parent_tab_->GetWebContents()),
+ Details<FindNotificationDetails>(&detail));
+}
+
+RenderViewHost* FindBarWin::GetRenderViewHost() const {
+ return parent_tab_->GetWebContents()->render_view_host();
+}
+
+void FindBarWin::GetDialogBounds(gfx::Rect* bounds) {
+ DCHECK(bounds);
+
+ // We need to find the View for the toolbar because we want to visually
+ // extend it (draw our dialog slightly overlapping its border).
+ views::View* root_view = views::GetRootViewForHWND(parent_hwnd_);
+ views::View* toolbar = NULL;
+ BookmarkBarView* bookmark_bar = NULL;
+ if (root_view) {
+ toolbar = root_view->GetViewByID(VIEW_ID_TOOLBAR);
+ bookmark_bar = static_cast<BookmarkBarView*>(
+ root_view->GetViewByID(VIEW_ID_BOOKMARK_BAR));
+ }
+
+ // To figure out what area we have to work with we need to know the rect for
+ // the browser window and the page content area starts and get a pointer to
+ // the toolbar to see where to draw the FindInPage dialog. If any of this
+ // fails, we return an empty rect.
+ CRect browser_client_rect, browser_window_rect, content_window_rect;
+ if (!::IsWindow(parent_hwnd_) ||
+ !::GetWindowRect(parent_tab_->GetContentHWND(), &content_window_rect) ||
+ !::GetWindowRect(parent_hwnd_, &browser_window_rect) ||
+ !::GetClientRect(parent_hwnd_, &browser_client_rect) ||
+ !toolbar) {
+ *bounds = gfx::Rect();
+ return;
+ }
+
+ // Start with browser's client rect, then change it below.
+ *bounds = gfx::Rect(browser_client_rect);
+
+ // Find the dimensions of the toolbar and the BookmarkBar.
+ gfx::Rect toolbar_bounds, bookmark_bar_bounds;
+ if (toolbar) {
+ if (!g_browser_process->IsUsingNewFrames()) {
+ toolbar_bounds = toolbar->bounds();
+ } else {
+ toolbar_bounds = toolbar->GetLocalBounds(false);
+ }
+ // Need to convert toolbar bounds into Container coords because the toolbar
+ // is the child of another view that isn't the top level view. This is
+ // required to ensure correct positioning relative to the top,left of the
+ // window.
+ gfx::Point topleft;
+ views::View::ConvertPointToContainer(toolbar, &topleft);
+ toolbar_bounds.Offset(topleft.x(), topleft.y());
+ }
+
+ // If the bookmarks bar is available, we need to update our
+ // position and paint accordingly
+ if (bookmark_bar) {
+ if (bookmark_bar->IsAlwaysShown()) {
+ // If it's always on, don't try to blend with the toolbar.
+ view_->SetToolbarBlend(false);
+ } else {
+ // Else it's on, but hidden (in which case we should try
+ // to blend with the toolbar.
+ view_->SetToolbarBlend(true);
+ }
+
+ // If we're not in the New Tab page style, align ourselves with
+ // the bookmarks bar (this works even if the bar is hidden).
+ if (!bookmark_bar->IsNewTabPage() ||
+ bookmark_bar->IsAlwaysShown()) {
+ bookmark_bar_bounds = bookmark_bar->bounds();
+ }
+ } else {
+ view_->SetToolbarBlend(true);
+ }
+
+ // Figure out at which y coordinate to draw the FindInPage window. If we have
+ // a toolbar (chrome) we want to overlap it by one pixel so that we look like
+ // we are part of the chrome (which will also draw our window on top of any
+ // info-bars, if present). If there is no chrome, then we have a constrained
+ // window or a Chrome application so we want to draw at the top of the page
+ // content (right beneath the title bar).
+ int y_pos_offset = 0;
+ if (!toolbar_bounds.IsEmpty()) {
+ // We have a toolbar (chrome), so overlap it by one pixel.
+ y_pos_offset = toolbar_bounds.bottom() - 1;
+ // If there is a bookmark bar attached to the toolbar we should appear
+ // attached to it instead of the toolbar.
+ if (!bookmark_bar_bounds.IsEmpty())
+ y_pos_offset += bookmark_bar_bounds.height() - 1;
+ } else {
+ // There is no toolbar, so this is probably a constrained window or a Chrome
+ // Application. This means we draw the Find window at the top of the page
+ // content window. We subtract 1 to overlap the light-blue line that is part
+ // of the title bar (so that we don't look detached by 1 pixel).
+ WINDOWINFO wi;
+ wi.cbSize = sizeof(WINDOWINFO);
+ GetWindowInfo(parent_hwnd_, &wi);
+ y_pos_offset = content_window_rect.TopLeft().y - wi.rcClient.top - 1;
+ }
+
+ bounds->Offset(0, y_pos_offset);
+
+ // We also want to stay well within limits of the vertical scrollbar and not
+ // draw on the window border (frame) itself either.
+ int width = views::NativeScrollBar::GetVerticalScrollBarWidth();
+ width += kWindowBorderWidth;
+ bounds->set_x(bounds->x() + width);
+ bounds->set_width(bounds->width() - (2 * width));
+}
+
+gfx::Rect FindBarWin::GetDialogPosition(gfx::Rect avoid_overlapping_rect) {
+ // Find the area we have to work with (after accounting for scrollbars, etc).
+ gfx::Rect dialog_bounds;
+ GetDialogBounds(&dialog_bounds);
+ if (dialog_bounds.IsEmpty())
+ return gfx::Rect();
+
+ // Ask the view how large an area it needs to draw on.
+ gfx::Size prefsize = view_->GetPreferredSize();
+
+ // Place the view in the top right corner of the dialog boundaries (top left
+ // for RTL languages).
+ gfx::Rect view_location;
+ int x = view_->UILayoutIsRightToLeft() ?
+ dialog_bounds.x() : dialog_bounds.width() - prefsize.width();
+ int y = dialog_bounds.y();
+ view_location.SetRect(x, y, prefsize.width(), prefsize.height());
+
+ // Make sure we don't go out of bounds to the left (right in RTL) if the
+ // window is too small to fit our dialog.
+ if (view_->UILayoutIsRightToLeft()) {
+ int boundary = dialog_bounds.width() - prefsize.width();
+ view_location.set_x(std::min(view_location.x(), boundary));
+ } else {
+ view_location.set_x(std::max(view_location.x(), dialog_bounds.x()));
+ }
+
+ gfx::Rect new_pos = view_location;
+
+ // When we get Find results back, we specify a selection rect, which we
+ // should strive to avoid overlapping. But first, we need to offset the
+ // selection rect (if one was provided).
+ if (!avoid_overlapping_rect.IsEmpty()) {
+ // For comparison (with the Intersects function below) we need to account
+ // for the fact that we draw the Find dialog relative to the window,
+ // whereas the selection rect is relative to the page.
+ RECT frame_rect = {0}, webcontents_rect = {0};
+ ::GetWindowRect(parent_hwnd_, &frame_rect);
+ ::GetWindowRect(parent_tab_->GetContainerHWND(), &webcontents_rect);
+ avoid_overlapping_rect.Offset(0, webcontents_rect.top - frame_rect.top);
+ }
+
+ // If the selection rectangle intersects the current position on screen then
+ // we try to move our dialog to the left (right for RTL) of the selection
+ // rectangle.
+ if (!avoid_overlapping_rect.IsEmpty() &&
+ avoid_overlapping_rect.Intersects(new_pos)) {
+ if (view_->UILayoutIsRightToLeft()) {
+ new_pos.set_x(avoid_overlapping_rect.x() +
+ avoid_overlapping_rect.width() +
+ (2 * kMinFindWndDistanceFromSelection));
+
+ // If we moved it off-screen to the right, we won't move it at all.
+ if (new_pos.x() + new_pos.width() > dialog_bounds.width())
+ new_pos = view_location; // Reset.
+ } else {
+ new_pos.set_x(avoid_overlapping_rect.x() - new_pos.width() -
+ kMinFindWndDistanceFromSelection);
+
+ // If we moved it off-screen to the left, we won't move it at all.
+ if (new_pos.x() < 0)
+ new_pos = view_location; // Reset.
+ }
+ }
+
+ // While we are animating, the Find window will grow bottoms up so we need to
+ // re-position the dialog so that it appears to grow out of the toolbar.
+ if (find_dialog_animation_offset_ > 0)
+ new_pos.Offset(0, std::min(0, -find_dialog_animation_offset_));
+
+ return new_pos;
+}
+
+void FindBarWin::SetDialogPosition(const gfx::Rect& new_pos) {
+ if (new_pos.IsEmpty())
+ return;
+
+ // Make sure the window edges are clipped to just the visible region. We need
+ // to do this before changing position, so that when we animate the closure
+ // of it it doesn't look like the window crumbles into the toolbar.
+ UpdateWindowEdges(new_pos);
+
+ ::SetWindowPos(GetHWND(), HWND_TOP,
+ new_pos.x(), new_pos.y(), new_pos.width(), new_pos.height(),
+ SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_SHOWWINDOW);
+
+ curr_pos_relative_ = new_pos;
+}
+
+void FindBarWin::SetFocusChangeListener(HWND parent_hwnd) {
+ // When tabs get torn off the tab-strip they get a new window with a new
+ // FocusManager, which means we need to clean up old listener and start a new
+ // one with the new FocusManager.
+ if (focus_manager_) {
+ if (old_accel_target_for_esc_)
+ UnregisterEscAccelerator();
+ focus_manager_->RemoveFocusChangeListener(this);
+ }
+
+ // Register as a listener with the new focus manager.
+ focus_manager_ = views::FocusManager::GetFocusManager(parent_hwnd);
+ DCHECK(focus_manager_);
+ focus_manager_->AddFocusChangeListener(this);
+}
+
+void FindBarWin::RestoreSavedFocus() {
+ if (focus_tracker_.get() == NULL) {
+ // TODO(brettw) Focus() should be on WebContentsView.
+ parent_tab_->GetWebContents()->Focus();
+ } else {
+ focus_tracker_->FocusLastFocusedExternalView();
+ }
+}
+
+void FindBarWin::RegisterEscAccelerator() {
+ views::Accelerator escape(VK_ESCAPE, false, false, false);
+
+ // TODO(finnur): Once we fix issue 1307173 we should not remember any old
+ // accelerator targets and just Register and Unregister when needed.
+ views::AcceleratorTarget* old_target =
+ focus_manager_->RegisterAccelerator(escape, this);
+
+ if (!old_accel_target_for_esc_)
+ old_accel_target_for_esc_ = old_target;
+}
+
+void FindBarWin::UnregisterEscAccelerator() {
+ // TODO(finnur): Once we fix issue 1307173 we should not remember any old
+ // accelerator targets and just Register and Unregister when needed.
+ DCHECK(old_accel_target_for_esc_ != NULL);
+ views::Accelerator escape(VK_ESCAPE, false, false, false);
+ views::AcceleratorTarget* current_target =
+ focus_manager_->GetTargetForAccelerator(escape);
+ if (current_target == this)
+ focus_manager_->RegisterAccelerator(escape, old_accel_target_for_esc_);
+}
+
diff --git a/chrome/browser/views/find_bar_win.h b/chrome/browser/views/find_bar_win.h
new file mode 100644
index 0000000..bd3b06c
--- /dev/null
+++ b/chrome/browser/views/find_bar_win.h
@@ -0,0 +1,240 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_VIEWS_FIND_BAR_WIN_H_
+#define CHROME_BROWSER_VIEWS_FIND_BAR_WIN_H_
+
+#include "base/gfx/rect.h"
+#include "chrome/browser/render_view_host_delegate.h"
+#include "chrome/common/animation.h"
+#include "chrome/views/container_win.h"
+
+class FindBarView;
+class RenderViewHost;
+class SlideAnimation;
+class WebContentsView;
+
+namespace views {
+class ExternalFocusTracker;
+class View;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// The FindBarWin implements the container window for the Windows find-in-page
+// functionality. It uses the FindBarWin implementation to draw its content and
+// is responsible for showing, hiding, closing, and moving the window if needed,
+// for example if the window is obscuring the selection results. It also
+// communicates with the parent_tab to instruct it to start searching for what
+// the user selected and receives notifications about the search results and
+// communicates that to the view.
+//
+// We create one container per tab and remember each search query per tab.
+//
+////////////////////////////////////////////////////////////////////////////////
+class FindBarWin : public views::FocusChangeListener,
+ public views::ContainerWin,
+ public AnimationDelegate {
+ public:
+ FindBarWin(WebContentsView* parent_tab, HWND parent_hwnd);
+ virtual ~FindBarWin();
+
+ // Shows the find bar. Any previous search string will again be visible.
+ void Show();
+
+ // Ends the current session.
+ void EndFindSession();
+
+ // Closes the find bar window (calls Close on the Window Container).
+ void Close();
+
+ // This is triggered when the parent tab of the Find dialog becomes
+ // unselected, at which point the find bar should become hidden. Otherwise,
+ // we leave artifacts on the chrome when other tabs are visible. However, we
+ // need to show the Find dialog again when the parent tab becomes selected
+ // again, so we set a flag for that and show the window if we get a
+ // DidBecomeSelected call.
+ void DidBecomeUnselected();
+
+ // If |show_on_tab_selection_| is true, then we show the dialog and clear the
+ // flag, otherwise we do nothing.
+ void DidBecomeSelected();
+
+ // Starts the Find operation by calling StartFinding on the Tab. This function
+ // can be called from the outside as a result of hot-keys, so it uses the
+ // last remembered search string as specified with set_find_string(). This
+ // function does not block while a search is in progress. The controller will
+ // receive the results through the notification mechanism. See Observe(...)
+ // for details.
+ void StartFinding(bool forward_direction);
+
+ // Stops the current Find operation. If |clear_selection| is true, it will
+ // also clear the selection on the focused frame.
+ void StopFinding(bool clear_selection);
+
+ // If the find bar obscures the search results we need to move the window. To
+ // do that we need to know what is selected on the page. We simply calculate
+ // where it would be if we place it on the left of the selection and if it
+ // doesn't fit on the screen we try the right side. The parameter
+ // |selection_rect| is expected to have coordinates relative to the top of
+ // the web page area.
+ void MoveWindowIfNecessary(const gfx::Rect& selection_rect);
+
+ // Moves the window according to the new window size.
+ void RespondToResize(const gfx::Size& new_size);
+
+ // Whether we are animating the position of the Find window.
+ bool IsAnimating();
+
+ // Changes the parent window for the find bar. If |new_parent| is already
+ // the parent of this window then no action is taken.
+ // |new_parent| can not be NULL.
+ void SetParent(HWND new_parent);
+
+ // We need to monitor focus changes so that we can register a handler for
+ // Escape when we get focus and unregister it when we looses focus. This
+ // function unregisters our old Escape accelerator (if registered) and
+ // registers a new one with the FocusManager associated with the
+ // new |parent_hwnd|.
+ void SetFocusChangeListener(HWND parent_hwnd);
+
+ // Accessors for specifying and retrieving the current search string.
+ std::wstring find_string() { return find_string_; }
+ void set_find_string(const std::wstring& find_string) {
+ find_string_ = find_string;
+ }
+
+ // Updates the find bar with the latest results. This is called when the
+ // renderer (through the RenderViewHostDelegate::View) finds more stuff.
+ void OnFindReply(int request_id,
+ int number_of_matches,
+ const gfx::Rect& selection_rect,
+ int active_match_ordinal,
+ bool final_update);
+
+ // Overridden from views::ContainerWin:
+ virtual void OnFinalMessage(HWND window);
+
+ // Overridden from views::FocusChangeListener:
+ virtual void FocusWillChange(views::View* focused_before,
+ views::View* focused_now);
+
+ // Overridden from views::AcceleratorTarget:
+ virtual bool AcceleratorPressed(const views::Accelerator& accelerator);
+
+ // AnimationDelegate implementation:
+ virtual void AnimationProgressed(const Animation* animation);
+ virtual void AnimationEnded(const Animation* animation);
+
+ private:
+ // Returns the RenderViewHost associated with the current tab.
+ RenderViewHost* GetRenderViewHost() const;
+
+ // Retrieves the boundaries that the find bar has to work with within the
+ // Chrome frame window. The resulting rectangle will be a rectangle that
+ // overlaps the bottom of the Chrome toolbar by one pixel (so we can create
+ // the illusion that the find bar is part of the toolbar) and covers the page
+ // area, except that we deflate the rect width by subtracting (from both
+ // sides) the width of the toolbar and some extra pixels to account for the
+ // width of the Chrome window borders. |bounds| is relative to the browser
+ // window. If the function fails to determine the browser window/client area
+ // rectangle or the rectangle for the page area then |bounds| will
+ // be an empty rectangle.
+ void GetDialogBounds(gfx::Rect* bounds);
+
+ // Returns the rectangle representing where to position the find bar. It uses
+ // GetDialogBounds and positions itself within that, either to the left (if an
+ // InfoBar is present) or to the right (no InfoBar). If
+ // |avoid_overlapping_rect| is specified, the return value will be a rectangle
+ // located immediately to the left of |avoid_overlapping_rect|, as long as
+ // there is enough room for the dialog to draw within the bounds. If not, the
+ // dialog position returned will overlap |avoid_overlapping_rect|.
+ // Note: |avoid_overlapping_rect| is expected to use coordinates relative to
+ // the top of the page area, (it will be converted to coordinates relative to
+ // the top of the browser window, when comparing against the dialog
+ // coordinates). The returned value is relative to the browser window.
+ gfx::Rect GetDialogPosition(gfx::Rect avoid_overlapping_rect);
+
+ // Moves the dialog window to the provided location, moves it to top in the
+ // z-order (HWND_TOP, not HWND_TOPMOST) and shows the window (if hidden).
+ // It then calls UpdateWindowEdges to make sure we don't overwrite the Chrome
+ // window border.
+ void SetDialogPosition(const gfx::Rect& new_pos);
+
+ // The dialog needs rounded edges, so we create a polygon that corresponds to
+ // the background images for this window (and make the polygon only contain
+ // the pixels that we want to draw). The polygon is then given to SetWindowRgn
+ // which changes the window from being a rectangle in shape, to being a rect
+ // with curved edges. We also check to see if the region should be truncated
+ // to prevent from drawing onto Chrome's window border.
+ void UpdateWindowEdges(const gfx::Rect& new_pos);
+
+ // Upon dismissing the window, restore focus to the last focused view which is
+ // not FindBarView or any of its children.
+ void RestoreSavedFocus();
+
+ // Registers this class as the handler for when Escape is pressed. We will
+ // unregister once we loose focus. See also: SetFocusChangeListener().
+ void RegisterEscAccelerator();
+
+ // When we loose focus, we unregister the handler for Escape. See
+ // also: SetFocusChangeListener().
+ void UnregisterEscAccelerator();
+
+ // The tab we are associated with.
+ WebContentsView* parent_tab_;
+
+ // The window handle of our parent window (the main Chrome window).
+ HWND parent_hwnd_;
+
+ // Our view, which is responsible for drawing the UI.
+ FindBarView* view_;
+
+ // Each time a search request comes in we assign it an id before passing it
+ // over the IPC so that when the results come in we can evaluate whether we
+ // still care about the results of the search (in some cases we don't because
+ // the user has issued a new search).
+ static int request_id_counter_;
+
+ // This variable keeps track of what the most recent request id is.
+ int current_request_id_;
+
+ // The current string we are searching for.
+ std::wstring find_string_;
+
+ // The last string we searched for. This is used to figure out if this is a
+ // Find or a FindNext operation (FindNext should not increase the request id).
+ std::wstring last_find_string_;
+
+ // The current window position (relative to parent).
+ gfx::Rect curr_pos_relative_;
+
+ // The current window size.
+ gfx::Size window_size_;
+
+ // The y position pixel offset of the window while animating the Find dialog.
+ int find_dialog_animation_offset_;
+
+ // The animation class to use when opening the Find window.
+ scoped_ptr<SlideAnimation> animation_;
+
+ // Whether to show the Find dialog when its tab becomes selected again.
+ bool show_on_tab_selection_;
+
+ // The focus manager we register with to keep track of focus changes.
+ views::FocusManager* focus_manager_;
+
+ // Stores the previous accelerator target for the Escape key, so that we can
+ // restore the state once we loose focus.
+ views::AcceleratorTarget* old_accel_target_for_esc_;
+
+ // Tracks and stores the last focused view which is not the FindBarView
+ // or any of its children. Used to restore focus once the FindBarView is
+ // closed.
+ scoped_ptr<views::ExternalFocusTracker> focus_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(FindBarWin);
+};
+
+#endif // CHROME_BROWSER_VIEWS_FIND_BAR_WIN_H_
diff --git a/chrome/browser/views/find_bar_win_interactive_uitest.cc b/chrome/browser/views/find_bar_win_interactive_uitest.cc
new file mode 100644
index 0000000..383a64a
--- /dev/null
+++ b/chrome/browser/views/find_bar_win_interactive_uitest.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include "chrome/browser/view_ids.h"
+#include "chrome/views/view.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/window_proxy.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "chrome/test/ui/ui_test.h"
+#include "net/url_request/url_request_unittest.h"
+
+namespace {
+
+// The delay waited after sending an OS simulated event.
+static const int kActionDelayMs = 500;
+static const wchar_t kDocRoot[] = L"chrome/test/data";
+static const wchar_t kSimplePage[] = L"404_is_enough_for_us.html";
+
+class FindInPageTest : public UITest {
+ public:
+ FindInPageTest() {
+ show_window_ = true;
+ dom_automation_enabled_ = true;
+ }
+};
+
+// Activate a tab by clicking on it. Returns true if the call was successful
+// (meaning the messages were correctly sent, but does not guarantee the tab
+// has been changed).
+bool ActivateTabByClick(AutomationProxy* automation,
+ WindowProxy* browser_window,
+ int tab_index) {
+ // Click on the tab.
+ gfx::Rect bounds;
+
+ if (!browser_window->GetViewBounds(VIEW_ID_TAB_0 + tab_index, &bounds, true))
+ return false;
+
+ POINT click(bounds.CenterPoint().ToPOINT());
+ if (!browser_window->SimulateOSClick(click,
+ views::Event::EF_LEFT_BUTTON_DOWN))
+ return false;
+
+ // Wait a bit to let the click be processed.
+ ::Sleep(kActionDelayMs);
+
+ return true;
+}
+
+} // namespace
+
+TEST_F(FindInPageTest, CrashEscHandlers) {
+ TestServer server(kDocRoot);
+
+ scoped_ptr<BrowserProxy> browser(automation()->GetLastActiveBrowserWindow());
+ ASSERT_TRUE(browser.get() != NULL);
+ scoped_ptr<WindowProxy> window(
+ automation()->GetWindowForBrowser(browser.get()));
+ ASSERT_TRUE(window.get() != NULL);
+
+ // First we navigate to our test page (tab A).
+ GURL url = server.TestServerPageW(kSimplePage);
+ scoped_ptr<TabProxy> tabA(GetActiveTab());
+ EXPECT_NE(AUTOMATION_MSG_NAVIGATION_ERROR, tabA->NavigateToURL(url));
+
+ EXPECT_TRUE(tabA->OpenFindInPage());
+
+ // Open another tab (tab B).
+ EXPECT_TRUE(browser->AppendTab(url));
+ scoped_ptr<TabProxy> tabB(GetActiveTab());
+
+ EXPECT_TRUE(tabB->OpenFindInPage());
+
+ // Select tab A.
+ EXPECT_TRUE(ActivateTabByClick(automation(), window.get(), 0));
+
+ // Close tab B.
+ EXPECT_TRUE(tabB->Close(true));
+
+ // Click on the location bar so that Find box loses focus.
+ gfx::Rect bounds;
+ EXPECT_TRUE(window->GetViewBounds(VIEW_ID_LOCATION_BAR, &bounds, false));
+ POINT click(bounds.CenterPoint().ToPOINT());
+ EXPECT_TRUE(window->SimulateOSClick(click,
+ views::Event::EF_LEFT_BUTTON_DOWN));
+ ::Sleep(kActionDelayMs);
+ int focused_view_id;
+ EXPECT_TRUE(window->GetFocusedViewID(&focused_view_id));
+ EXPECT_EQ(VIEW_ID_LOCATION_BAR, focused_view_id);
+
+ // This used to crash until bug 1303709 was fixed.
+ EXPECT_TRUE(window->SimulateOSKeyPress(VK_ESCAPE, 0));
+ ::Sleep(kActionDelayMs);
+}
diff --git a/chrome/browser/views/find_bar_win_uitest.cc b/chrome/browser/views/find_bar_win_uitest.cc
new file mode 100644
index 0000000..d1e036d2
--- /dev/null
+++ b/chrome/browser/views/find_bar_win_uitest.cc
@@ -0,0 +1,211 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/browser/find_bar_win.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "chrome/test/automation/window_proxy.h"
+#include "chrome/test/ui/ui_test.h"
+#include "net/url_request/url_request_unittest.h"
+
+class FindInPageControllerTest : public UITest {
+ public:
+ FindInPageControllerTest() {
+ show_window_ = true;
+ }
+};
+
+const std::wstring kFramePage = L"files/find_in_page/frames.html";
+const std::wstring kUserSelectPage = L"files/find_in_page/user-select.html";
+const std::wstring kCrashPage = L"files/find_in_page/crash_1341577.html";
+const std::wstring kTooFewMatchesPage = L"files/find_in_page/bug_1155639.html";
+
+// This test loads a page with frames and starts FindInPage requests
+TEST_F(FindInPageControllerTest, FindInPageFrames) {
+ TestServer server(L"chrome/test/data");
+
+ // First we navigate to our frames page.
+ GURL url = server.TestServerPageW(kFramePage);
+ scoped_ptr<TabProxy> tab(GetActiveTab());
+ ASSERT_TRUE(tab->NavigateToURL(url));
+ WaitUntilTabCount(1);
+
+ // Try incremental search (mimicking user typing in).
+ EXPECT_EQ(18, tab->FindInPage(L"g", FWD, IGNORE_CASE, false));
+ EXPECT_EQ(11, tab->FindInPage(L"go", FWD, IGNORE_CASE, false));
+ EXPECT_EQ(04, tab->FindInPage(L"goo", FWD, IGNORE_CASE, false));
+ EXPECT_EQ(03, tab->FindInPage(L"goog", FWD, IGNORE_CASE, false));
+ EXPECT_EQ(02, tab->FindInPage(L"googl", FWD, IGNORE_CASE, false));
+ EXPECT_EQ(01, tab->FindInPage(L"google", FWD, IGNORE_CASE, false));
+ EXPECT_EQ(00, tab->FindInPage(L"google!", FWD, IGNORE_CASE, false));
+
+ // Negative test (no matches should be found).
+ EXPECT_EQ(0, tab->FindInPage(L"Non-existing string", FWD, IGNORE_CASE,
+ false));
+
+ // 'horse' only exists in the three right frames.
+ EXPECT_EQ(3, tab->FindInPage(L"horse", FWD, IGNORE_CASE, false));
+
+ // 'cat' only exists in the first frame.
+ EXPECT_EQ(1, tab->FindInPage(L"cat", FWD, IGNORE_CASE, false));
+
+ // Try searching again, should still come up with 1 match.
+ EXPECT_EQ(1, tab->FindInPage(L"cat", FWD, IGNORE_CASE, false));
+
+ // Try searching backwards, ignoring case, should still come up with 1 match.
+ EXPECT_EQ(1, tab->FindInPage(L"CAT", BACK, IGNORE_CASE, false));
+
+ // Try case sensitive, should NOT find it.
+ EXPECT_EQ(0, tab->FindInPage(L"CAT", FWD, CASE_SENSITIVE, false));
+
+ // Try again case sensitive, but this time with right case.
+ EXPECT_EQ(1, tab->FindInPage(L"dog", FWD, CASE_SENSITIVE, false));
+
+ // Try non-Latin characters ('Hreggvidur' with 'eth' for 'd' in left frame).
+ EXPECT_EQ(1, tab->FindInPage(L"Hreggvi\u00F0ur", FWD, IGNORE_CASE, false));
+ EXPECT_EQ(1, tab->FindInPage(L"Hreggvi\u00F0ur", FWD, CASE_SENSITIVE, false));
+ EXPECT_EQ(0, tab->FindInPage(L"hreggvi\u00F0ur", FWD, CASE_SENSITIVE, false));
+}
+
+// Load a page with no selectable text and make sure we don't crash.
+TEST_F(FindInPageControllerTest, FindUnSelectableText) {
+ TestServer server(L"chrome/test/data");
+
+ GURL url = server.TestServerPageW(kUserSelectPage);
+ scoped_ptr<TabProxy> tab(GetActiveTab());
+ ASSERT_TRUE(tab->NavigateToURL(url));
+ WaitUntilTabCount(1);
+
+ EXPECT_EQ(0, tab->FindInPage(L"text", FWD, IGNORE_CASE, false));
+ EXPECT_EQ(0, tab->FindInPage(L"Non-existing string", FWD, IGNORE_CASE,
+ false));
+}
+
+// Try to reproduce the crash seen in issue 1341577.
+TEST_F(FindInPageControllerTest, FindCrash_Issue1341577) {
+ TestServer server(L"chrome/test/data");
+
+ GURL url = server.TestServerPageW(kCrashPage);
+ scoped_ptr<TabProxy> tab(GetActiveTab());
+ ASSERT_TRUE(tab->NavigateToURL(url));
+ WaitUntilTabCount(1);
+
+ // This would crash the tab. These must be the first two find requests issued
+ // against the frame, otherwise an active frame pointer is set and it wont
+ // produce the crash.
+ EXPECT_EQ(1, tab->FindInPage(L"\u0D4C", FWD, IGNORE_CASE, false));
+ // FindNext returns -1 for match count because it doesn't bother with
+ // recounting the number of matches. We don't care about the match count
+ // anyway in this case, we just want to make sure it doesn't crash.
+ EXPECT_EQ(-1, tab->FindInPage(L"\u0D4C", FWD, IGNORE_CASE, true));
+
+ // This should work fine.
+ EXPECT_EQ(1, tab->FindInPage(L"\u0D24\u0D46", FWD, IGNORE_CASE, false));
+ EXPECT_EQ(0, tab->FindInPage(L"nostring", FWD, IGNORE_CASE, false));
+}
+
+// Test to make sure Find does the right thing when restarting from a timeout.
+// We used to have a problem where we'd stop finding matches when all of the
+// following conditions were true:
+// 1) The page has a lot of text to search.
+// 2) The page contains more than one match.
+// 3) It takes longer than the time-slice given to each Find operation (100
+// ms) to find one or more of those matches (so Find times out and has to try
+// again from where it left off).
+TEST_F(FindInPageControllerTest, FindEnoughMatches_Issue1155639) {
+ TestServer server(L"chrome/test/data");
+
+ GURL url = server.TestServerPageW(kTooFewMatchesPage);
+ scoped_ptr<TabProxy> tab(GetActiveTab());
+ ASSERT_TRUE(tab->NavigateToURL(url));
+ WaitUntilTabCount(1);
+
+ // This string appears 5 times at the bottom of a long page. If Find restarts
+ // properly after a timeout, it will find 5 matches, not just 1.
+ EXPECT_EQ(5, tab->FindInPage(L"008.xml", FWD, IGNORE_CASE, false));
+}
+
+// The find window should not change its location just because we open and close
+// a new tab.
+TEST_F(FindInPageControllerTest, FindMovesOnTabClose_Issue1343052) {
+ fprintf(stderr, "Starting FindMovesOnTabClose_Issue1343052\n");
+ TestServer server(L"chrome/test/data");
+
+ fprintf(stderr, "TestServerPageW\n");
+ GURL url = server.TestServerPageW(kFramePage);
+ fprintf(stderr, "GetActiveTab A\n");
+ scoped_ptr<TabProxy> tabA(GetActiveTab());
+ fprintf(stderr, "Navigate A\n");
+ ASSERT_TRUE(tabA->NavigateToURL(url));
+ fprintf(stderr, "WaitUntilTabCount(1) for A\n");
+ WaitUntilTabCount(1);
+
+ fprintf(stderr, "GetBrowserWindow(0)\n");
+ scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser.get() != NULL);
+
+ // Toggle the bookmark bar state.
+ fprintf(stderr, "ApplyAccelerator bookmark bar\n");
+ browser->ApplyAccelerator(IDC_SHOW_BOOKMARKS_BAR);
+ fprintf(stderr, "WaitForBookmarkVisibility\n");
+ EXPECT_TRUE(WaitForBookmarkBarVisibilityChange(browser.get(), true));
+
+ // Open the Find window and wait for it to animate.
+ fprintf(stderr, "OpenFindInPage in A\n");
+ EXPECT_TRUE(tabA->OpenFindInPage());
+ fprintf(stderr, "WaitForWindowFullyVisible in A\n");
+ EXPECT_TRUE(WaitForFindWindowFullyVisible(tabA.get()));
+
+ // Find its location.
+ int x = -1, y = -1;
+ fprintf(stderr, "GetFindWindowLocation in A\n");
+ EXPECT_TRUE(tabA->GetFindWindowLocation(&x, &y));
+
+ // Open another tab (tab B).
+ fprintf(stderr, "AppendTab B\n");
+ EXPECT_TRUE(browser->AppendTab(url));
+ fprintf(stderr, "GetActiveTab B\n");
+ scoped_ptr<TabProxy> tabB(GetActiveTab());
+
+ // Close tab B.
+ fprintf(stderr, "Tab Close B\n");
+ EXPECT_TRUE(tabB->Close(true));
+
+ // See if the Find window has moved.
+ int new_x = -1, new_y = -1;
+ fprintf(stderr, "GetFindWindowLocation in A\n");
+ EXPECT_TRUE(tabA->GetFindWindowLocation(&new_x, &new_y));
+
+ EXPECT_EQ(x, new_x);
+ EXPECT_EQ(y, new_y);
+
+ // Now reset the bookmarks bar state and try the same again.
+ fprintf(stderr, "ApplyAccelerator BookmarksBar\n");
+ browser->ApplyAccelerator(IDC_SHOW_BOOKMARKS_BAR);
+ fprintf(stderr, "WaitForBookmarkBarVisibilityChange\n");
+ EXPECT_TRUE(WaitForBookmarkBarVisibilityChange(browser.get(), false));
+
+ // Bookmark bar has moved, reset our coordinates.
+ fprintf(stderr, "GetFindWindowLocation again\n");
+ EXPECT_TRUE(tabA->GetFindWindowLocation(&x, &y));
+
+ // Open another tab (tab C).
+ fprintf(stderr, "Append tab C\n");
+ EXPECT_TRUE(browser->AppendTab(url));
+ fprintf(stderr, "GetActiveTab C\n");
+ scoped_ptr<TabProxy> tabC(GetActiveTab());
+
+ // Close it.
+ fprintf(stderr, "Close tab C\n");
+ EXPECT_TRUE(tabC->Close(true));
+
+ // See if the Find window has moved.
+ fprintf(stderr, "GetFindWindowLocation yet again\n");
+ EXPECT_TRUE(tabA->GetFindWindowLocation(&new_x, &new_y));
+
+ EXPECT_EQ(x, new_x);
+ EXPECT_EQ(y, new_y);
+ fprintf(stderr, "Done!\n");
+}
diff --git a/chrome/browser/views/tab_contents_container_view.cc b/chrome/browser/views/tab_contents_container_view.cc
index 7a506fc..c0c013b6 100644
--- a/chrome/browser/views/tab_contents_container_view.cc
+++ b/chrome/browser/views/tab_contents_container_view.cc
@@ -31,14 +31,21 @@ TabContentsContainerView::~TabContentsContainerView() {
void TabContentsContainerView::SetTabContents(TabContents* tab_contents) {
if (tab_contents_) {
- // TODO(beng): (Cleanup) We want to call the _base_ class' version here.
- // WebContents' WM_WINDOWPOSCHANGED handler will ensure its
- // version is called. The correct thing to do here is to
- // rationalize all TabContents Hide/Show/Size etc into a single
- // API, but that's too complex for this first phase.
- tab_contents_->TabContents::HideContents();
+ // TODO(brettw) should this move to HWNDView::Detach which is called below?
+ // It needs cleanup regardless.
+ HWND container_hwnd = tab_contents_->GetContainerHWND();
+
+ // Hide the contents before adjusting its parent to avoid a full desktop
+ // flicker.
+ ::ShowWindow(container_hwnd, SW_HIDE);
+
+ // Reset the parent to NULL to ensure hidden tabs don't receive messages.
+ ::SetParent(container_hwnd, NULL);
+
+ tab_contents_->WasHidden();
// Unregister the tab contents window from the FocusManager.
+ views::FocusManager::UninstallFocusSubclass(container_hwnd);
HWND hwnd = tab_contents_->GetContentHWND();
if (hwnd) {
// We may not have an HWND anymore, if the renderer crashed and we are