Fix first search result highlight

Background:
The first search result should be highlighted after the search results
are updated.

Changes:
1. Create SearchResultBaseView as the parent class for SearchResultView,
SearchResultTileItemView and SearchAnswerContainerView so that we could
easily highlight different search results.
2. Rename SetFirstResultSelected to SetFirstResultHighlighted since we
will remove selection completely in crbug.com/766807.

BUG=804023

TBR=khmel@chromium.org

(cherry picked from commit 61930607aefd9ef4658d2fce8f5a16bd4479cb75)

Change-Id: I731657350ed76b3a8b92adce2c7e70e86b9a013c
Reviewed-on: https://chromium-review.googlesource.com/891939
Commit-Queue: Weidong Guo <weidongg@chromium.org>
Reviewed-by: Yury Khmel <khmel@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#533387}
Reviewed-on: https://chromium-review.googlesource.com/926904
Reviewed-by: Weidong Guo <weidongg@chromium.org>
Cr-Commit-Position: refs/branch-heads/3325@{#558}
Cr-Branched-From: bc084a8b5afa3744a74927344e304c02ae54189f-refs/heads/master@{#530369}
diff --git a/ui/app_list/BUILD.gn b/ui/app_list/BUILD.gn
index a626d5d..d5a40a5 100644
--- a/ui/app_list/BUILD.gn
+++ b/ui/app_list/BUILD.gn
@@ -78,6 +78,8 @@
     "views/search_result_actions_view_delegate.h",
     "views/search_result_answer_card_view.cc",
     "views/search_result_answer_card_view.h",
+    "views/search_result_base_view.cc",
+    "views/search_result_base_view.h",
     "views/search_result_container_view.cc",
     "views/search_result_container_view.h",
     "views/search_result_list_view.cc",
diff --git a/ui/app_list/views/app_list_view_unittest.cc b/ui/app_list/views/app_list_view_unittest.cc
index 1cead10..c4ffa1c 100644
--- a/ui/app_list/views/app_list_view_unittest.cc
+++ b/ui/app_list/views/app_list_view_unittest.cc
@@ -552,11 +552,10 @@
   forward_view_list.push_back(contents_view()
                                   ->search_result_answer_card_view_for_test()
                                   ->GetSearchAnswerContainerViewForTest());
-  views::View* results_container = contents_view()
-                                       ->search_result_list_view_for_test()
-                                       ->results_container_for_test();
+  SearchResultListView* list_view =
+      contents_view()->search_result_list_view_for_test();
   for (int i = 0; i < kListResults; ++i)
-    forward_view_list.push_back(results_container->child_at(i));
+    forward_view_list.push_back(list_view->GetResultViewAt(i));
   forward_view_list.push_back(search_box_view()->search_box());
   std::vector<views::View*> backward_view_list = forward_view_list;
   std::reverse(backward_view_list.begin(), backward_view_list.end());
@@ -707,11 +706,10 @@
   forward_view_list.push_back(contents_view()
                                   ->search_result_answer_card_view_for_test()
                                   ->GetSearchAnswerContainerViewForTest());
-  views::View* results_container = contents_view()
-                                       ->search_result_list_view_for_test()
-                                       ->results_container_for_test();
+  SearchResultListView* list_view =
+      contents_view()->search_result_list_view_for_test();
   for (int i = 0; i < kListResults; ++i)
-    forward_view_list.push_back(results_container->child_at(i));
+    forward_view_list.push_back(list_view->GetResultViewAt(i));
   forward_view_list.push_back(search_box_view()->search_box());
 
   // Test traversal triggered by down.
@@ -720,7 +718,7 @@
   std::vector<views::View*> backward_view_list;
   backward_view_list.push_back(search_box_view()->search_box());
   for (int i = kListResults - 1; i >= 0; --i)
-    backward_view_list.push_back(results_container->child_at(i));
+    backward_view_list.push_back(list_view->GetResultViewAt(i));
   backward_view_list.push_back(contents_view()
                                    ->search_result_answer_card_view_for_test()
                                    ->GetSearchAnswerContainerViewForTest());
@@ -950,12 +948,10 @@
   search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test"));
   const int kListResults = 2;
   SetUpSearchResults(0, kListResults, false);
-  const views::View* results_container =
-      contents_view()
-          ->search_result_list_view_for_test()
-          ->results_container_for_test();
+  SearchResultListView* list_view =
+      contents_view()->search_result_list_view_for_test();
   EXPECT_EQ(search_box_view()->search_box(), focused_view());
-  EXPECT_EQ(results_container->child_at(0),
+  EXPECT_EQ(list_view->GetResultViewAt(0),
             contents_view()->search_results_page_view()->first_result_view());
 
   // Populate both fake list results and tile results.
@@ -996,15 +992,13 @@
   search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test1"));
   const int kListResults = 2;
   SetUpSearchResults(0, kListResults, false);
-  const views::View* results_container =
-      contents_view()
-          ->search_result_list_view_for_test()
-          ->results_container_for_test();
-  views::View* first_result_view =
+  SearchResultListView* list_view =
+      contents_view()->search_result_list_view_for_test();
+  SearchResultBaseView* first_result_view =
       contents_view()->search_results_page_view()->first_result_view();
   EXPECT_EQ(search_box_view()->search_box(), focused_view());
-  EXPECT_EQ(results_container->child_at(0), first_result_view);
-  EXPECT_TRUE(static_cast<SearchResultView*>(first_result_view)->selected());
+  EXPECT_EQ(list_view->GetResultViewAt(0), first_result_view);
+  EXPECT_TRUE(first_result_view->background_highlighted());
 
   // Type something else.
   search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test2"));
@@ -1015,8 +1009,8 @@
   SimulateKeyPress(ui::VKEY_TAB, false);
   EXPECT_EQ(search_box_view()->close_button(), focused_view());
   SimulateKeyPress(ui::VKEY_TAB, false);
-  EXPECT_EQ(results_container->child_at(0), focused_view());
-  EXPECT_TRUE(static_cast<SearchResultView*>(first_result_view)->selected());
+  EXPECT_EQ(list_view->GetResultViewAt(0), focused_view());
+  EXPECT_TRUE(first_result_view->background_highlighted());
 
   // Update search results, both list and tile results are populated.
   const int kTileResults = 3;
@@ -1027,11 +1021,10 @@
           ->tile_views_for_test();
   first_result_view =
       contents_view()->search_results_page_view()->first_result_view();
-  EXPECT_EQ(results_container->child_at(0), focused_view());
+  EXPECT_EQ(list_view->GetResultViewAt(0), focused_view());
   EXPECT_EQ(tile_views[0], first_result_view);
-  EXPECT_FALSE(
-      static_cast<SearchResultTileItemView*>(first_result_view)->HasFocus());
-  EXPECT_TRUE(static_cast<SearchResultView*>(focused_view())->selected());
+  EXPECT_FALSE(first_result_view->HasFocus());
+  EXPECT_TRUE(list_view->GetResultViewAt(0)->background_highlighted());
 }
 
 // Tests hitting Enter key when focus is on search box.
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index 39b5fcf..78c468b 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -24,6 +24,7 @@
 #include "ui/app_list/views/app_list_view.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/search_box_view_delegate.h"
+#include "ui/app_list/views/search_result_base_view.h"
 #include "ui/app_list/views/search_result_page_view.h"
 #include "ui/base/ime/text_input_flags.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/ui/app_list/views/search_result_answer_card_view.cc b/ui/app_list/views/search_result_answer_card_view.cc
index 584d788..429ede1 100644
--- a/ui/app_list/views/search_result_answer_card_view.cc
+++ b/ui/app_list/views/search_result_answer_card_view.cc
@@ -6,13 +6,13 @@
 
 #include <vector>
 
-#include "ash/app_list/model/search/search_result_observer.h"
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_view_delegate.h"
+#include "ui/app_list/views/search_result_base_view.h"
+#include "ui/gfx/canvas.h"
 #include "ui/views/background.h"
-#include "ui/views/controls/button/button.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
 
@@ -20,12 +20,10 @@
 
 // Container of the search answer view.
 class SearchResultAnswerCardView::SearchAnswerContainerView
-    : public views::Button,
-      public views::ButtonListener,
-      public SearchResultObserver {
+    : public SearchResultBaseView {
  public:
   explicit SearchAnswerContainerView(AppListViewDelegate* view_delegate)
-      : Button(this), view_delegate_(view_delegate) {
+      : view_delegate_(view_delegate) {
     SetFocusBehavior(FocusBehavior::ALWAYS);
     // Center the card horizontally in the container. Padding is set on the
     // server.
@@ -41,17 +39,6 @@
       search_result_->RemoveObserver(this);
   }
 
-  bool selected() const { return selected_; }
-
-  void SetSelected(bool selected) {
-    if (selected == selected_)
-      return;
-    selected_ = selected;
-    UpdateBackgroundColor();
-    if (selected)
-      ScrollRectToVisible(GetLocalBounds());
-  }
-
   bool SetSearchResult(SearchResult* search_result) {
     views::View* const old_result_view = child_count() ? child_at(0) : nullptr;
     views::View* const new_result_view =
@@ -84,15 +71,12 @@
     return "SearchAnswerContainerView";
   }
 
-  void OnBlur() override {
-    SetSelected(false);
-    Button::OnBlur();
-  }
+  void OnBlur() override { SetBackgroundHighlighted(false); }
 
   void OnFocus() override {
-    SetSelected(true);
+    ScrollRectToVisible(GetLocalBounds());
     NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
-    Button::OnFocus();
+    SetBackgroundHighlighted(true);
   }
 
   bool OnKeyPressed(const ui::KeyEvent& event) override {
@@ -111,6 +95,11 @@
     node_data->SetName(accessible_name());
   }
 
+  void PaintButtonContents(gfx::Canvas* canvas) override {
+    if (background_highlighted())
+      canvas->FillRect(GetContentsBounds(), kAnswerCardSelectedColor);
+  }
+
   // views::ButtonListener overrides:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override {
     DCHECK(sender == this);
@@ -122,18 +111,7 @@
   void OnResultDestroying() override { search_result_ = nullptr; }
 
  private:
-  void UpdateBackgroundColor() {
-    if (selected_) {
-      SetBackground(views::CreateSolidBackground(kAnswerCardSelectedColor));
-    } else {
-      SetBackground(nullptr);
-    }
-
-    SchedulePaint();
-  }
-
   AppListViewDelegate* const view_delegate_;  // Not owned.
-  bool selected_ = false;
   SearchResult* search_result_ = nullptr;  // Not owned.
 
   DISALLOW_COPY_AND_ASSIGN(SearchAnswerContainerView);
@@ -178,22 +156,17 @@
   parent()->SetVisible(have_result);
 
   set_container_score(have_result ? display_results.front()->relevance() : 0);
-  if (title_changed && search_answer_container_view_->selected())
+  if (title_changed && search_answer_container_view_->HasFocus()) {
     search_answer_container_view_->NotifyAccessibilityEvent(
         ui::AX_EVENT_SELECTION, true);
+  }
   return have_result ? 1 : 0;
 }
 
 void SearchResultAnswerCardView::UpdateSelectedIndex(int old_selected,
                                                      int new_selected) {
-  if (new_selected == old_selected)
-    return;
-
-  const bool is_selected = new_selected == 0;
-  search_answer_container_view_->SetSelected(is_selected);
-  if (is_selected)
-    search_answer_container_view_->NotifyAccessibilityEvent(
-        ui::AX_EVENT_SELECTION, true);
+  // TODO(weidongg): This implementation is deprecated and should be removed as
+  // part of removing "pseudo-focus" logic work (https://crbug.com/766807).
 }
 
 bool SearchResultAnswerCardView::OnKeyPressed(const ui::KeyEvent& event) {
@@ -205,22 +178,16 @@
   return SearchResultContainerView::OnKeyPressed(event);
 }
 
-views::View* SearchResultAnswerCardView::GetSelectedView() const {
-  return search_answer_container_view_->selected()
-             ? search_answer_container_view_
-             : nullptr;
+views::View* SearchResultAnswerCardView::GetSelectedView() {
+  // TODO(weidongg): This implementation is deprecated and should be removed as
+  // part of removing "pseudo-focus" logic work (https://crbug.com/766807).
+  return nullptr;
 }
 
-views::View* SearchResultAnswerCardView::GetFirstResultView() {
+SearchResultBaseView* SearchResultAnswerCardView::GetFirstResultView() {
   return num_results() <= 0 ? nullptr : search_answer_container_view_;
 }
 
-void SearchResultAnswerCardView::SetFirstResultSelected(bool selected) {
-  if (num_results() <= 0)
-    return;
-  search_answer_container_view_->SetSelected(selected);
-}
-
 views::View* SearchResultAnswerCardView::GetSearchAnswerContainerViewForTest()
     const {
   return search_answer_container_view_;
diff --git a/ui/app_list/views/search_result_answer_card_view.h b/ui/app_list/views/search_result_answer_card_view.h
index cf6b883..e2c8c26 100644
--- a/ui/app_list/views/search_result_answer_card_view.h
+++ b/ui/app_list/views/search_result_answer_card_view.h
@@ -29,9 +29,8 @@
   int DoUpdate() override;
   void UpdateSelectedIndex(int old_selected, int new_selected) override;
   bool OnKeyPressed(const ui::KeyEvent& event) override;
-  views::View* GetSelectedView() const override;
-  views::View* GetFirstResultView() override;
-  void SetFirstResultSelected(bool selected) override;
+  views::View* GetSelectedView() override;
+  SearchResultBaseView* GetFirstResultView() override;
 
   views::View* GetSearchAnswerContainerViewForTest() const;
 
diff --git a/ui/app_list/views/search_result_answer_card_view_unittest.cc b/ui/app_list/views/search_result_answer_card_view_unittest.cc
index 5b10d55..0855578 100644
--- a/ui/app_list/views/search_result_answer_card_view_unittest.cc
+++ b/ui/app_list/views/search_result_answer_card_view_unittest.cc
@@ -133,14 +133,6 @@
   EXPECT_EQ(1, GetYSize());
 }
 
-TEST_F(SearchResultAnswerCardViewTest, ButtonBackground) {
-  views::View* button = result_view()->parent();
-  EXPECT_EQ(kAnswerCardSelectedColor, button->background()->get_color());
-
-  ClearSelectedIndex();
-  EXPECT_EQ(nullptr, button->background());
-}
-
 TEST_F(SearchResultAnswerCardViewTest, KeyboardEvents) {
   EXPECT_TRUE(KeyPress(ui::VKEY_RETURN));
   EXPECT_EQ(1, GetOpenResultCountAndReset(0));
diff --git a/ui/app_list/views/search_result_base_view.cc b/ui/app_list/views/search_result_base_view.cc
new file mode 100644
index 0000000..5d04a4c
--- /dev/null
+++ b/ui/app_list/views/search_result_base_view.cc
@@ -0,0 +1,18 @@
+// Copyright 2018 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 "ui/app_list/views/search_result_base_view.h"
+
+namespace app_list {
+
+SearchResultBaseView::SearchResultBaseView() : Button(this) {}
+
+SearchResultBaseView::~SearchResultBaseView() = default;
+
+void SearchResultBaseView::SetBackgroundHighlighted(bool enabled) {
+  background_highlighted_ = enabled;
+  SchedulePaint();
+}
+
+}  // namespace app_list
diff --git a/ui/app_list/views/search_result_base_view.h b/ui/app_list/views/search_result_base_view.h
new file mode 100644
index 0000000..afa058f
--- /dev/null
+++ b/ui/app_list/views/search_result_base_view.h
@@ -0,0 +1,36 @@
+// Copyright 2018 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 UI_APP_LIST_VIEWS_SEARCH_RESULT_BASE_VIEW_H_
+#define UI_APP_LIST_VIEWS_SEARCH_RESULT_BASE_VIEW_H_
+
+#include "ash/app_list/model/search/search_result_observer.h"
+#include "ui/app_list/app_list_export.h"
+#include "ui/views/controls/button/button.h"
+
+namespace app_list {
+
+class APP_LIST_EXPORT SearchResultBaseView : public views::Button,
+                                             public views::ButtonListener,
+                                             public SearchResultObserver {
+ public:
+  SearchResultBaseView();
+
+  // Set or remove the background highlight.
+  void SetBackgroundHighlighted(bool enabled);
+
+  bool background_highlighted() const { return background_highlighted_; }
+
+ protected:
+  ~SearchResultBaseView() override;
+
+ private:
+  bool background_highlighted_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(SearchResultBaseView);
+};
+
+}  // namespace app_list
+
+#endif  // UI_APP_LIST_VIEWS_SEARCH_RESULT_BASE_VIEW_H_
diff --git a/ui/app_list/views/search_result_container_view.h b/ui/app_list/views/search_result_container_view.h
index e5bb4258..b597a93 100644
--- a/ui/app_list/views/search_result_container_view.h
+++ b/ui/app_list/views/search_result_container_view.h
@@ -16,6 +16,8 @@
 
 namespace app_list {
 
+class SearchResultBaseView;
+
 // SearchResultContainerView is a base class for views that contain multiple
 // search results. SearchPageView holds these in a list and manages which one is
 // selected. There can be one result within one SearchResultContainerView
@@ -87,14 +89,11 @@
                                    bool directional_movement) = 0;
 
   // Returns selected view in this container view.
-  virtual views::View* GetSelectedView() const = 0;
+  virtual views::View* GetSelectedView() = 0;
 
   // Returns the first result in the container view. Returns NULL if it does not
   // exist.
-  virtual views::View* GetFirstResultView() = 0;
-
-  // Sets the first result in this container view selected/unselected.
-  virtual void SetFirstResultSelected(bool selected) = 0;
+  virtual SearchResultBaseView* GetFirstResultView() = 0;
 
  private:
   // Schedules an Update call using |update_factory_|. Do nothing if there is a
diff --git a/ui/app_list/views/search_result_list_view.cc b/ui/app_list/views/search_result_list_view.cc
index 49a2087..937420b 100644
--- a/ui/app_list/views/search_result_list_view.cc
+++ b/ui/app_list/views/search_result_list_view.cc
@@ -36,8 +36,10 @@
   results_container_->SetLayoutManager(
       std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
 
-  for (int i = 0; i < kMaxResults; ++i)
-    results_container_->AddChildView(new SearchResultView(this));
+  for (int i = 0; i < kMaxResults; ++i) {
+    search_result_views_.emplace_back(new SearchResultView(this));
+    results_container_->AddChildView(search_result_views_.back());
+  }
   AddChildView(results_container_);
 }
 
@@ -52,14 +54,13 @@
              results_container_->child_at(selected_index())) == result_view;
 }
 
-SearchResultView* SearchResultListView::GetResultViewAt(int index) const {
-  DCHECK(index >= 0 && index < results_container_->child_count());
-  return static_cast<SearchResultView*>(results_container_->child_at(index));
+SearchResultView* SearchResultListView::GetResultViewAt(size_t index) {
+  DCHECK(index >= 0 && index < search_result_views_.size());
+  return search_result_views_[index];
 }
 
 void SearchResultListView::ListItemsRemoved(size_t start, size_t count) {
-  size_t last = std::min(
-      start + count, static_cast<size_t>(results_container_->child_count()));
+  size_t last = std::min(start + count, search_result_views_.size());
   for (size_t i = start; i < last; ++i)
     GetResultViewAt(i)->ClearResultNoRepaint();
 
@@ -83,24 +84,15 @@
   return num_results();
 }
 
-views::View* SearchResultListView::GetSelectedView() const {
+views::View* SearchResultListView::GetSelectedView() {
   return IsValidSelectionIndex(selected_index())
              ? GetResultViewAt(selected_index())
              : nullptr;
 }
 
-views::View* SearchResultListView::GetFirstResultView() {
+SearchResultBaseView* SearchResultListView::GetFirstResultView() {
   DCHECK(results_container_->has_children());
-  return num_results() <= 0 ? nullptr : results_container_->child_at(0);
-}
-
-void SearchResultListView::SetFirstResultSelected(bool selected) {
-  DCHECK(results_container_->has_children());
-  if (num_results() <= 0)
-    return;
-  SearchResultView* search_result_view =
-      static_cast<SearchResultView*>(results_container_->child_at(0));
-  search_result_view->SetSelected(selected);
+  return num_results() <= 0 ? nullptr : search_result_views_[0];
 }
 
 int SearchResultListView::DoUpdate() {
diff --git a/ui/app_list/views/search_result_list_view.h b/ui/app_list/views/search_result_list_view.h
index eb101aa..eb8d76c 100644
--- a/ui/app_list/views/search_result_list_view.h
+++ b/ui/app_list/views/search_result_list_view.h
@@ -31,6 +31,9 @@
 
   bool IsResultViewSelected(const SearchResultView* result_view) const;
 
+  // Helper function to get SearchResultView at given |index|.
+  SearchResultView* GetResultViewAt(size_t index);
+
   void SearchResultActivated(SearchResultView* view, int event_flags);
 
   void SearchResultActionActivated(SearchResultView* view,
@@ -51,11 +54,8 @@
                            bool directional_movement) override;
   void NotifyFirstResultYIndex(int y_index) override;
   int GetYSize() override;
-  views::View* GetSelectedView() const override;
-  views::View* GetFirstResultView() override;
-  void SetFirstResultSelected(bool selected) override;
-
-  views::View* results_container_for_test() const { return results_container_; }
+  views::View* GetSelectedView() override;
+  SearchResultBaseView* GetFirstResultView() override;
 
  private:
   friend class test::SearchResultListViewTest;
@@ -64,9 +64,6 @@
   int DoUpdate() override;
   void UpdateSelectedIndex(int old_selected, int new_selected) override;
 
-  // Helper function to get SearchResultView at given |index|.
-  SearchResultView* GetResultViewAt(int index) const;
-
   // Overridden from views::View:
   void Layout() override;
   int GetHeightForWidth(int w) const override;
@@ -76,6 +73,8 @@
 
   views::View* results_container_;
 
+  std::vector<SearchResultView*> search_result_views_;  // Not owned.
+
   DISALLOW_COPY_AND_ASSIGN(SearchResultListView);
 };
 
diff --git a/ui/app_list/views/search_result_page_view.cc b/ui/app_list/views/search_result_page_view.cc
index d1b50d2..56b0588 100644
--- a/ui/app_list/views/search_result_page_view.cc
+++ b/ui/app_list/views/search_result_page_view.cc
@@ -16,6 +16,7 @@
 #include "ui/app_list/views/app_list_main_view.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/search_box_view.h"
+#include "ui/app_list/views/search_result_base_view.h"
 #include "ui/app_list/views/search_result_list_view.h"
 #include "ui/app_list/views/search_result_tile_item_list_view.h"
 #include "ui/gfx/canvas.h"
@@ -263,35 +264,32 @@
 
   // Only sort and layout the containers when they have all updated.
   for (SearchResultContainerView* view : result_container_views_) {
-    if (view->UpdateScheduled()) {
+    if (view->UpdateScheduled())
       return;
-    }
   }
 
-  if (result_container_views_.empty())
-    return;
-  // Set the first result (if it exists) selected when search results are
-  // updated. Note that the focus is not set on the first result to prevent
-  // frequent focus switch between search box and first result during typing
-  // query.
-  SearchResultContainerView* old_first_container_view =
-      result_container_views_[0];
   ReorderSearchResultContainers();
 
   views::View* focused_view = GetFocusManager()->GetFocusedView();
-  if (first_result_view_ != focused_view) {
-    // If the old first result is focused, do not clear the selection. (This
-    // happens when the user moved the focus before search results are
-    // updated.)
-    old_first_container_view->SetFirstResultSelected(false);
-  }
+
+  // Clear the first search result view's background highlight.
+  if (first_result_view_ && first_result_view_ != focused_view)
+    first_result_view_->SetBackgroundHighlighted(false);
+
   first_result_view_ = result_container_views_[0]->GetFirstResultView();
-  if (!Contains(focused_view)) {
-    // If one of the search result is focused, do not set the first result
-    // selected.
-    result_container_views_[0]->SetFirstResultSelected(true);
-  }
-  return;
+
+  // If one of the search result is focused, do not highlight the first search
+  // result.
+  if (Contains(focused_view))
+    return;
+
+  if (!first_result_view_)
+    return;
+
+  // Highlight the first result after search results are updated. Note that the
+  // focus is not set on the first result to prevent frequent focus switch
+  // between the search box and the first result when the user is typing query.
+  first_result_view_->SetBackgroundHighlighted(true);
 }
 
 gfx::Rect SearchResultPageView::GetPageBoundsForState(
diff --git a/ui/app_list/views/search_result_page_view.h b/ui/app_list/views/search_result_page_view.h
index 1a66385..e3cf677 100644
--- a/ui/app_list/views/search_result_page_view.h
+++ b/ui/app_list/views/search_result_page_view.h
@@ -16,6 +16,8 @@
 
 namespace app_list {
 
+class SearchResultBaseView;
+
 // The search results page for the app list.
 class APP_LIST_EXPORT SearchResultPageView
     : public AppListPage,
@@ -49,7 +51,7 @@
 
   views::View* contents_view() { return contents_view_; }
 
-  views::View* first_result_view() const { return first_result_view_; }
+  SearchResultBaseView* first_result_view() const { return first_result_view_; }
 
  private:
   // Separator between SearchResultContainerView.
@@ -68,7 +70,7 @@
   views::View* const contents_view_;
 
   // The first search result's view or nullptr if there's no search result.
-  views::View* first_result_view_ = nullptr;
+  SearchResultBaseView* first_result_view_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(SearchResultPageView);
 };
diff --git a/ui/app_list/views/search_result_tile_item_list_view.cc b/ui/app_list/views/search_result_tile_item_list_view.cc
index ac437c0..9626fb7 100644
--- a/ui/app_list/views/search_result_tile_item_list_view.cc
+++ b/ui/app_list/views/search_result_tile_item_list_view.cc
@@ -96,20 +96,16 @@
   return num_results() ? 1 : 0;
 }
 
-views::View* SearchResultTileItemListView::GetSelectedView() const {
+views::View* SearchResultTileItemListView::GetSelectedView() {
   return IsValidSelectionIndex(selected_index()) ? tile_views_[selected_index()]
                                                  : nullptr;
 }
 
-views::View* SearchResultTileItemListView::GetFirstResultView() {
+SearchResultBaseView* SearchResultTileItemListView::GetFirstResultView() {
   DCHECK(!tile_views_.empty());
   return num_results() <= 0 ? nullptr : tile_views_[0];
 }
 
-// TODO(warx): This implementation is deprecated and should be removed as part
-// of removing "pseudo-focus" logic work (https://crbug.com/766807).
-void SearchResultTileItemListView::SetFirstResultSelected(bool selected) {}
-
 int SearchResultTileItemListView::DoUpdate() {
   std::vector<SearchResult*> display_results =
       SearchModel::FilterSearchResultsByDisplayType(
diff --git a/ui/app_list/views/search_result_tile_item_list_view.h b/ui/app_list/views/search_result_tile_item_list_view.h
index d4ce5a4..50eff7f 100644
--- a/ui/app_list/views/search_result_tile_item_list_view.h
+++ b/ui/app_list/views/search_result_tile_item_list_view.h
@@ -35,9 +35,8 @@
                            bool directional_movement) override;
   void NotifyFirstResultYIndex(int y_index) override;
   int GetYSize() override;
-  views::View* GetSelectedView() const override;
-  views::View* GetFirstResultView() override;
-  void SetFirstResultSelected(bool selected) override;
+  views::View* GetSelectedView() override;
+  SearchResultBaseView* GetFirstResultView() override;
 
   // Overridden from views::View:
   bool OnKeyPressed(const ui::KeyEvent& event) override;
diff --git a/ui/app_list/views/search_result_tile_item_view.cc b/ui/app_list/views/search_result_tile_item_view.cc
index b607b87..bb6da67 100644
--- a/ui/app_list/views/search_result_tile_item_view.cc
+++ b/ui/app_list/views/search_result_tile_item_view.cc
@@ -85,8 +85,7 @@
     SearchResultContainerView* result_container,
     AppListViewDelegate* view_delegate,
     PaginationModel* pagination_model)
-    : views::Button(this),
-      result_container_(result_container),
+    : result_container_(result_container),
       view_delegate_(view_delegate),
       pagination_model_(pagination_model),
       is_play_store_app_search_enabled_(
@@ -304,10 +303,12 @@
   } else if (!IsSuggestedAppTile()) {
     ScrollRectToVisible(GetLocalBounds());
   }
+  SetBackgroundHighlighted(true);
   UpdateBackgroundColor();
 }
 
 void SearchResultTileItemView::OnBlur() {
+  SetBackgroundHighlighted(false);
   UpdateBackgroundColor();
 }
 
@@ -316,7 +317,7 @@
 }
 
 void SearchResultTileItemView::PaintButtonContents(gfx::Canvas* canvas) {
-  if (!item_ || !HasFocus())
+  if (!item_ || !background_highlighted())
     return;
 
   gfx::Rect rect(GetContentsBounds());
diff --git a/ui/app_list/views/search_result_tile_item_view.h b/ui/app_list/views/search_result_tile_item_view.h
index 5511153..626edfd 100644
--- a/ui/app_list/views/search_result_tile_item_view.h
+++ b/ui/app_list/views/search_result_tile_item_view.h
@@ -7,11 +7,10 @@
 
 #include <memory>
 
-#include "ash/app_list/model/search/search_result_observer.h"
 #include "base/macros.h"
 #include "ui/app_list/app_list_export.h"
+#include "ui/app_list/views/search_result_base_view.h"
 #include "ui/views/context_menu_controller.h"
-#include "ui/views/controls/button/button.h"
 
 namespace views {
 class ImageView;
@@ -29,10 +28,8 @@
 // A tile view that displays a search result. It hosts view for search result
 // that has SearchResult::DisplayType DISPLAY_TILE or DISPLAY_RECOMMENDATION.
 class APP_LIST_EXPORT SearchResultTileItemView
-    : public views::Button,
-      public views::ButtonListener,
-      public views::ContextMenuController,
-      public SearchResultObserver {
+    : public SearchResultBaseView,
+      public views::ContextMenuController {
  public:
   SearchResultTileItemView(SearchResultContainerView* result_container,
                            AppListViewDelegate* view_delegate,
diff --git a/ui/app_list/views/search_result_view.cc b/ui/app_list/views/search_result_view.cc
index 6cbbf78..a9964f3 100644
--- a/ui/app_list/views/search_result_view.cc
+++ b/ui/app_list/views/search_result_view.cc
@@ -42,7 +42,7 @@
 // URL color.
 constexpr SkColor kUrlColor = SkColorSetARGBMacro(0xFF, 0x33, 0x67, 0xD6);
 // Row selected color, #000 8%.
-constexpr SkColor kRowSelectedColor =
+constexpr SkColor kRowHighlightedColor =
     SkColorSetARGBMacro(0x14, 0x00, 0x00, 0x00);
 
 int GetIconViewWidth() {
@@ -55,8 +55,7 @@
 const char SearchResultView::kViewClassName[] = "ui/app_list/SearchResultView";
 
 SearchResultView::SearchResultView(SearchResultListView* list_view)
-    : views::Button(this),
-      list_view_(list_view),
+    : list_view_(list_view),
       icon_(new views::ImageView),
       badge_icon_(new views::ImageView),
       actions_view_(new SearchResultActionsView(this)),
@@ -134,18 +133,6 @@
   return accessible_name;
 }
 
-void SearchResultView::SetSelected(bool selected) {
-  if (selected_ == selected)
-    return;
-  selected_ = selected;
-
-  if (selected_) {
-    ScrollRectToVisible(GetLocalBounds());
-    NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
-  }
-  SchedulePaint();
-}
-
 void SearchResultView::UpdateAccessibleName() {
   SetAccessibleName(ComputeAccessibleName());
 }
@@ -295,8 +282,8 @@
 
   // Possibly call FillRect a second time (these colours are partially
   // transparent, so the previous FillRect is not redundant).
-  if (selected())
-    canvas->FillRect(content_rect, kRowSelectedColor);
+  if (background_highlighted())
+    canvas->FillRect(content_rect, kRowHighlightedColor);
 
   gfx::Rect border_bottom = gfx::SubtractRects(rect, content_rect);
   canvas->FillRect(border_bottom, kResultBorderColor);
@@ -328,13 +315,13 @@
 }
 
 void SearchResultView::OnFocus() {
-  SetSelected(true);
-  Button::OnFocus();
+  ScrollRectToVisible(GetLocalBounds());
+  NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
+  SetBackgroundHighlighted(true);
 }
 
 void SearchResultView::OnBlur() {
-  SetSelected(false);
-  Button::OnBlur();
+  SetBackgroundHighlighted(false);
 }
 
 void SearchResultView::ButtonPressed(views::Button* sender,
diff --git a/ui/app_list/views/search_result_view.h b/ui/app_list/views/search_result_view.h
index a09708d..3e0a334 100644
--- a/ui/app_list/views/search_result_view.h
+++ b/ui/app_list/views/search_result_view.h
@@ -11,14 +11,13 @@
 #include <string>
 #include <vector>
 
-#include "ash/app_list/model/search/search_result_observer.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "ui/app_list/app_list_export.h"
 #include "ui/app_list/views/search_result_actions_view_delegate.h"
+#include "ui/app_list/views/search_result_base_view.h"
 #include "ui/views/context_menu_controller.h"
-#include "ui/views/controls/button/button.h"
 
 namespace gfx {
 class RenderText;
@@ -41,10 +40,8 @@
 
 // SearchResultView displays a SearchResult.
 class APP_LIST_EXPORT SearchResultView
-    : public views::Button,
-      public views::ButtonListener,
+    : public SearchResultBaseView,
       public views::ContextMenuController,
-      public SearchResultObserver,
       public SearchResultActionsViewDelegate {
  public:
   // Internal class name.
@@ -68,9 +65,6 @@
 
   void set_is_last_result(bool is_last) { is_last_result_ = is_last; }
 
-  void SetSelected(bool selected);
-  bool selected() const { return selected_; }
-
  private:
   friend class app_list::test::SearchResultListViewTest;
 
diff --git a/ui/app_list/views/suggestions_container_view.cc b/ui/app_list/views/suggestions_container_view.cc
index a909ccb..50d0f0d 100644
--- a/ui/app_list/views/suggestions_container_view.cc
+++ b/ui/app_list/views/suggestions_container_view.cc
@@ -87,18 +87,16 @@
   return 0;
 }
 
-views::View* SuggestionsContainerView::GetSelectedView() const {
+views::View* SuggestionsContainerView::GetSelectedView() {
   return IsValidSelectionIndex(selected_index())
              ? search_result_tile_views_[selected_index()]
              : nullptr;
 }
 
-views::View* SuggestionsContainerView::GetFirstResultView() {
+SearchResultBaseView* SuggestionsContainerView::GetFirstResultView() {
   return nullptr;
 }
 
-void SuggestionsContainerView::SetFirstResultSelected(bool selected) {}
-
 const char* SuggestionsContainerView::GetClassName() const {
   return "SuggestionsContainerView";
 }
diff --git a/ui/app_list/views/suggestions_container_view.h b/ui/app_list/views/suggestions_container_view.h
index 6ffd066..bcd43d4 100644
--- a/ui/app_list/views/suggestions_container_view.h
+++ b/ui/app_list/views/suggestions_container_view.h
@@ -36,9 +36,8 @@
                            bool directional_movement) override;
   void NotifyFirstResultYIndex(int y_index) override;
   int GetYSize() override;
-  views::View* GetSelectedView() const override;
-  views::View* GetFirstResultView() override;
-  void SetFirstResultSelected(bool selected) override;
+  views::View* GetSelectedView() override;
+  SearchResultBaseView* GetFirstResultView() override;
   const char* GetClassName() const override;
 
  private: