Index: chrome/browser/ui/views/omnibox/omnibox_result_view.cc |
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc |
index b241434426da794fbf5458f05edcb91fc737eb1d..3702dfa0dea075f032e492a8cf1fd715cf81e889 100644 |
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc |
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc |
@@ -17,6 +17,8 @@ |
#include "base/memory/scoped_vector.h" |
#include "base/strings/string_number_conversions.h" |
#include "base/strings/string_util.h" |
+#include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service_factory.h" |
+#include "chrome/browser/profiles/profile.h" |
#include "chrome/browser/ui/omnibox/omnibox_popup_model.h" |
#include "chrome/browser/ui/views/location_bar/location_bar_view.h" |
#include "chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h" |
@@ -38,6 +40,34 @@ using ui::NativeTheme; |
namespace { |
+// Calls back to the OmniboxResultView when the requested image is downloaded. |
+// This is a separate class instead of being implemented on OmniboxResultView |
+// because BitmapFetcherService currently takes ownership of this object. |
+// TODO(dschuyler): Make BitmapFetcherService use the more typical non-owning |
+// ObserverList pattern and have OmniboxResultView implement the Observer call |
+// directly. |
+class AnswerImageObserver : public BitmapFetcherService::Observer { |
+ public: |
+ explicit AnswerImageObserver( |
+ const base::WeakPtr<OmniboxResultView>& view) |
+ : view_(view) {} |
+ |
+ void OnImageChanged(BitmapFetcherService::RequestId request_id, |
+ const SkBitmap& image) override; |
+ |
+ private: |
+ const base::WeakPtr<OmniboxResultView> view_; |
+ DISALLOW_COPY_AND_ASSIGN(AnswerImageObserver); |
+}; |
+ |
+void AnswerImageObserver::OnImageChanged( |
+ BitmapFetcherService::RequestId request_id, |
+ const SkBitmap& image) { |
+ DCHECK(!image.empty()); |
+ if (view_) |
+ view_->SetAnswerImage(gfx::ImageSkia::CreateFrom1xBitmap(image)); |
+} |
+ |
// The minimum distance between the top and bottom of the {icon|text} and the |
// top or bottom of the row. |
const int kMinimumIconVerticalPadding = 2; |
@@ -133,13 +163,17 @@ OmniboxResultView::OmniboxResultView(OmniboxPopupContentsView* model, |
model_(model), |
model_index_(model_index), |
location_bar_view_(location_bar_view), |
+ image_service_(BitmapFetcherServiceFactory::GetForBrowserContext( |
+ location_bar_view_->profile())), |
font_list_(font_list), |
font_height_( |
std::max(font_list.GetHeight(), |
font_list.DeriveWithStyle(gfx::Font::BOLD).GetHeight())), |
mirroring_context_(new MirroringContext()), |
keyword_icon_(new views::ImageView()), |
- animation_(new gfx::SlideAnimation(this)) { |
+ animation_(new gfx::SlideAnimation(this)), |
+ request_id_(BitmapFetcherService::REQUEST_ID_INVALID), |
+ weak_ptr_factory_(this) { |
CHECK_GE(model_index, 0); |
if (default_icon_size_ == 0) { |
default_icon_size_ = |
@@ -154,6 +188,8 @@ OmniboxResultView::OmniboxResultView(OmniboxPopupContentsView* model, |
} |
OmniboxResultView::~OmniboxResultView() { |
+ if (image_service_) |
+ image_service_->CancelRequest(request_id_); |
} |
SkColor OmniboxResultView::GetColor( |
@@ -175,6 +211,16 @@ void OmniboxResultView::SetMatch(const AutocompleteMatch& match) { |
ResetRenderTexts(); |
animation_->Reset(); |
+ answer_image_ = gfx::ImageSkia(); |
+ if (image_service_) { |
+ image_service_->CancelRequest(request_id_); |
+ if (match_.answer) { |
+ request_id_ = image_service_->RequestImage( |
+ match_.answer->second_line().image_url(), |
+ new AnswerImageObserver(weak_ptr_factory_.GetWeakPtr())); |
+ } |
+} |
+ |
AutocompleteMatch* associated_keyword_match = match_.associated_keyword.get(); |
if (associated_keyword_match) { |
keyword_icon_->SetImage(GetKeywordIcon()); |
@@ -222,12 +268,11 @@ int OmniboxResultView::GetTextHeight() const { |
return font_height_; |
} |
-void OmniboxResultView::PaintMatch( |
- const AutocompleteMatch& match, |
- gfx::RenderText* contents, |
- gfx::RenderText* description, |
- gfx::Canvas* canvas, |
- int x) const { |
+void OmniboxResultView::PaintMatch(const AutocompleteMatch& match, |
+ gfx::RenderText* contents, |
+ gfx::RenderText* description, |
+ gfx::Canvas* canvas, |
+ int x) const { |
int y = text_bounds_.y(); |
if (!separator_rendertext_) { |
@@ -253,6 +298,18 @@ void OmniboxResultView::PaintMatch( |
if (description_max_width != 0) { |
x = DrawRenderText(match, separator_rendertext_.get(), false, canvas, x, y, |
separator_width_); |
+ |
+ if (!answer_image_.isNull()) { |
+ canvas->DrawImageInt(answer_image_, |
+ // Source x, y, w, h. |
+ 0, 0, answer_image_.width(), answer_image_.height(), |
+ // Destination x, y, w, h. |
+ GetMirroredXInView(x), |
+ y + kMinimumIconVerticalPadding, default_icon_size_, |
+ default_icon_size_, true); |
+ x += default_icon_size_ + LocationBarView::kIconInternalPadding; |
+ } |
+ |
DrawRenderText(match, description, false, canvas, x, y, |
description_max_width); |
} |
@@ -396,6 +453,11 @@ int OmniboxResultView::GetMatchContentsWidth() const { |
return contents_rendertext_ ? contents_rendertext_->GetContentWidth() : 0; |
} |
+void OmniboxResultView::SetAnswerImage(const gfx::ImageSkia& image) { |
+ answer_image_ = image; |
+ SchedulePaint(); |
+} |
+ |
// TODO(skanuj): This is probably identical across all OmniboxResultView rows in |
// the omnibox dropdown. Consider sharing the result. |
int OmniboxResultView::GetDisplayOffset( |