diff options
26 files changed, 370 insertions, 135 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_popup_model.cc b/chrome/browser/autocomplete/autocomplete_popup_model.cc index 009cc0b..f4f7a82 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_model.cc +++ b/chrome/browser/autocomplete/autocomplete_popup_model.cc @@ -8,6 +8,7 @@ #include "chrome/browser/autocomplete/autocomplete_edit.h" #include "chrome/browser/autocomplete/autocomplete_popup_view.h" #include "chrome/browser/profile.h" +#include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/common/notification_service.h" @@ -279,3 +280,12 @@ void AutocompletePopupModel::Observe(NotificationType type, view_->UpdatePopupAppearance(); } + +const SkBitmap* AutocompletePopupModel::GetSpecialIconForMatch( + const AutocompleteMatch& match) const { + if (!match.template_url || !match.template_url->IsExtensionKeyword()) + return NULL; + + return &profile_->GetExtensionsService()->GetOmniboxIcon( + match.template_url->GetExtensionId()); +} diff --git a/chrome/browser/autocomplete/autocomplete_popup_model.h b/chrome/browser/autocomplete/autocomplete_popup_model.h index a986419..8686bde 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_model.h +++ b/chrome/browser/autocomplete/autocomplete_popup_model.h @@ -12,6 +12,7 @@ class AutocompleteEditModel; class AutocompleteEditView; class Profile; +class SkBitmap; class AutocompletePopupView; @@ -110,6 +111,10 @@ class AutocompletePopupModel : public NotificationObserver { // can be removed from history, and if so, remove it and update the popup. void TryDeletingCurrentItem(); + // Returns the special icon to use for a given match, or NULL if we should + // use a standard style icon. + const SkBitmap* GetSpecialIconForMatch(const AutocompleteMatch& match) const; + // The token value for selected_line_, hover_line_ and functions dealing with // a "line number" that indicates "no line". static const size_t kNoMatch = -1; diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_gtk.cc b/chrome/browser/autocomplete/autocomplete_popup_view_gtk.cc index a6605ba..8a6c24b 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_view_gtk.cc +++ b/chrome/browser/autocomplete/autocomplete_popup_view_gtk.cc @@ -13,6 +13,7 @@ #include "base/basictypes.h" #include "base/i18n/rtl.h" #include "base/logging.h" +#include "base/stl_util-inl.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete/autocomplete.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" @@ -196,26 +197,6 @@ void SetupLayoutForMatch(PangoLayout* layout, pango_attr_list_unref(attrs); } -GdkPixbuf* IconForMatch(BrowserThemeProvider* theme, - const AutocompleteMatch& match, - bool selected) { - int icon = match.starred ? - IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match.type); - if (selected) { - switch (icon) { - case IDR_OMNIBOX_HTTP: icon = IDR_OMNIBOX_HTTP_DARK; break; - case IDR_OMNIBOX_HISTORY: icon = IDR_OMNIBOX_HISTORY_DARK; break; - case IDR_OMNIBOX_SEARCH: icon = IDR_OMNIBOX_SEARCH_DARK; break; - case IDR_OMNIBOX_MORE: icon = IDR_OMNIBOX_MORE_DARK; break; - case IDR_OMNIBOX_STAR: icon = IDR_OMNIBOX_STAR_DARK; break; - default: NOTREACHED(); break; - } - } - - // TODO(estade): Do we want to flip these for RTL? (Windows doesn't). - return theme->GetPixbufNamed(icon); -} - // Generates the normal URL color, a green color used in unhighlighted URL // text. It is a mix of |kURLTextColor| and the current text color. Unlike the // selected text color, It is more important to match the qualities of the @@ -352,6 +333,9 @@ AutocompletePopupViewGtk::~AutocompletePopupViewGtk() { model_.reset(); g_object_unref(layout_); gtk_widget_destroy(window_); + + for (PixbufMap::iterator it = pixbufs_.begin(); it != pixbufs_.end(); ++it) + g_object_unref(it->second); } void AutocompletePopupViewGtk::InvalidateLine(size_t line) { @@ -498,6 +482,32 @@ void AutocompletePopupViewGtk::AcceptLine(size_t line, is_keyword_hint ? std::wstring() : keyword); } +GdkPixbuf* AutocompletePopupViewGtk::IconForMatch( + const AutocompleteMatch& match, bool selected) { + const SkBitmap* bitmap = model_->GetSpecialIconForMatch(match); + if (bitmap) { + if (!ContainsKey(pixbufs_, bitmap)) + pixbufs_[bitmap] = gfx::GdkPixbufFromSkBitmap(bitmap); + return pixbufs_[bitmap]; + } + + int icon = match.starred ? + IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match.type); + if (selected) { + switch (icon) { + case IDR_OMNIBOX_HTTP: icon = IDR_OMNIBOX_HTTP_DARK; break; + case IDR_OMNIBOX_HISTORY: icon = IDR_OMNIBOX_HISTORY_DARK; break; + case IDR_OMNIBOX_SEARCH: icon = IDR_OMNIBOX_SEARCH_DARK; break; + case IDR_OMNIBOX_MORE: icon = IDR_OMNIBOX_MORE_DARK; break; + case IDR_OMNIBOX_STAR: icon = IDR_OMNIBOX_STAR_DARK; break; + default: NOTREACHED(); break; + } + } + + // TODO(estade): Do we want to flip these for RTL? (Windows doesn't). + return theme_provider_->GetPixbufNamed(icon); +} + gboolean AutocompletePopupViewGtk::HandleMotion(GtkWidget* widget, GdkEventMotion* event) { // TODO(deanm): Windows has a bunch of complicated logic here. @@ -610,7 +620,7 @@ gboolean AutocompletePopupViewGtk::HandleExpose(GtkWidget* widget, (line_rect.width() - kIconLeftPadding - kIconWidth + gtk_offset); // Draw the icon for this result. DrawFullPixbuf(drawable, gc, - IconForMatch(theme_provider_, match, is_selected), + IconForMatch(match, is_selected), icon_start_x, line_rect.y() + kIconTopPadding); // Draw the results text vertically centered in the results space. diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_gtk.h b/chrome/browser/autocomplete/autocomplete_popup_view_gtk.h index 4759417..2942489 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_view_gtk.h +++ b/chrome/browser/autocomplete/autocomplete_popup_view_gtk.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_POPUP_VIEW_GTK_H_ #include <gtk/gtk.h> +#include <map> #include "base/basictypes.h" #include "base/scoped_ptr.h" @@ -16,6 +17,7 @@ class AutocompleteEditModel; class AutocompleteEditView; +class AutocompleteMatch; class AutocompletePopupModel; class GtkThemeProvider; class Profile; @@ -56,6 +58,8 @@ class AutocompletePopupViewGtk : public AutocompletePopupView, // Accept a line of the results, for example, when the user clicks a line. void AcceptLine(size_t line, WindowOpenDisposition disposition); + GdkPixbuf* IconForMatch(const AutocompleteMatch& match, bool selected); + static gboolean HandleExposeThunk(GtkWidget* widget, GdkEventExpose* event, gpointer userdata) { return reinterpret_cast<AutocompletePopupViewGtk*>(userdata)-> @@ -99,6 +103,11 @@ class AutocompletePopupViewGtk : public AutocompletePopupView, GtkThemeProvider* theme_provider_; NotificationRegistrar registrar_; + // Used to cache GdkPixbufs and map them from the SkBitmaps they were created + // from. + typedef std::map<const SkBitmap*, GdkPixbuf*> PixbufMap; + PixbufMap pixbufs_; + // A list of colors which we should use for drawing the popup. These change // between gtk and normal mode. GdkColor border_color_; diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_mac.h b/chrome/browser/autocomplete/autocomplete_popup_view_mac.h index d19576f..7d1ec26 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_view_mac.h +++ b/chrome/browser/autocomplete/autocomplete_popup_view_mac.h @@ -8,6 +8,7 @@ #import <Cocoa/Cocoa.h> #include <string> +#include <map> #include "base/basictypes.h" #include "base/scoped_ptr.h" @@ -20,7 +21,9 @@ class AutocompletePopupModel; class AutocompleteEditModel; class AutocompleteEditViewMac; +@class NSImage; class Profile; +class SkBitmap; // Implements AutocompletePopupView using a raw NSWindow containing an // NSTableView. @@ -114,6 +117,9 @@ class AutocompletePopupViewMac : public AutocompletePopupView { // change. void PositionPopup(const CGFloat matrixHeight); + // Returns the NSImage that should be used as an icon for the given match. + NSImage* ImageForMatch(const AutocompleteMatch& match); + scoped_ptr<AutocompletePopupModel> model_; AutocompleteEditViewMac* edit_view_; NSTextField* field_; // owned by tab controller diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm b/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm index 8319f00..16105ad 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm +++ b/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm @@ -6,6 +6,7 @@ #include "app/resource_bundle.h" #include "app/text_elider.h" +#include "base/stl_util-inl.h" #include "base/sys_string_conversions.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" #include "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" @@ -14,6 +15,7 @@ #include "chrome/browser/cocoa/image_utils.h" #include "gfx/rect.h" #include "grit/theme_resources.h" +#include "skia/ext/skia_utils_mac.h" #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" namespace { @@ -356,6 +358,17 @@ void AutocompletePopupViewMac::PositionPopup(const CGFloat matrixHeight) { [[field_ window] addChildWindow:popup_ ordered:NSWindowAbove]; } +NSImage* AutocompletePopupViewMac::ImageForMatch( + const AutocompleteMatch& match) { + const SkBitmap* bitmap = model_->GetSpecialIconForMatch(match); + if (bitmap) + return gfx::SkBitmapToNSImage(*bitmap); + + const int resource_id = match.starred ? + IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match.type); + return AutocompleteEditViewMac::ImageForResource(resource_id); +} + void AutocompletePopupViewMac::UpdatePopupAppearance() { DCHECK([NSThread isMainThread]); const AutocompleteResult& result = model_->result(); @@ -400,9 +413,7 @@ void AutocompletePopupViewMac::UpdatePopupAppearance() { for (size_t ii = 0; ii < rows; ++ii) { AutocompleteButtonCell* cell = [matrix cellAtRow:ii column:0]; const AutocompleteMatch& match = model_->result().match_at(ii); - const int resource_id = match.starred ? IDR_OMNIBOX_STAR - : AutocompleteMatch::TypeToIcon(match.type); - [cell setImage:AutocompleteEditViewMac::ImageForResource(resource_id)]; + [cell setImage:ImageForMatch(match)]; [cell setAttributedTitle:MatchText(match, resultFont, matrixWidth)]; } diff --git a/chrome/browser/cocoa/location_bar/autocomplete_text_field_cell_unittest.mm b/chrome/browser/cocoa/location_bar/autocomplete_text_field_cell_unittest.mm index f22de95..69c3f47 100644 --- a/chrome/browser/cocoa/location_bar/autocomplete_text_field_cell_unittest.mm +++ b/chrome/browser/cocoa/location_bar/autocomplete_text_field_cell_unittest.mm @@ -230,6 +230,7 @@ TEST_F(AutocompleteTextFieldCellTest, TextFrame) { EXPECT_LT(NSMaxX(textFrame), searchHintMaxX); EXPECT_TRUE(NSContainsRect(cursorFrame, textFrame)); + // Text frame should take everything over again on reset. [cell clearHint]; textFrame = [cell textFrameForFrame:bounds]; @@ -288,6 +289,7 @@ TEST_F(AutocompleteTextFieldCellTest, DrawingRectForBounds) { EXPECT_TRUE(NSContainsRect(NSInsetRect(textFrame, 1, 1), drawingRect)); [cell clearHint]; + textFrame = [cell textFrameForFrame:bounds]; drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); @@ -414,6 +416,8 @@ TEST_F(AutocompleteTextFieldCellTest, PageActionImageFrame) { EXPECT_LE(NSMaxX(labelRect), NSMinX(iconRect0)); } + NSImage* image = [NSImage imageNamed:@"NSApplicationIcon"]; + // Test that the cell drops the search hint if there is no room for // it. TEST_F(AutocompleteTextFieldCellTest, OmitsSearchHintIfNarrow) { diff --git a/chrome/browser/cocoa/location_bar/autocomplete_text_field_unittest.mm b/chrome/browser/cocoa/location_bar/autocomplete_text_field_unittest.mm index 44e223b..162ccf9 100644 --- a/chrome/browser/cocoa/location_bar/autocomplete_text_field_unittest.mm +++ b/chrome/browser/cocoa/location_bar/autocomplete_text_field_unittest.mm @@ -266,6 +266,7 @@ TEST_F(AutocompleteTextFieldTest, Display) { [field_ display]; [cell clearHint]; + [field_ display]; } @@ -382,6 +383,7 @@ TEST_F(AutocompleteTextFieldTest, ResetFieldEditorKeywordHint) { // Capture the editor frame resulting from the standard focus // machinery. + [test_window() makePretendKeyWindowAndSetFirstResponder:field_]; const NSRect baseEditorFrame(EditorFrame()); @@ -398,6 +400,7 @@ TEST_F(AutocompleteTextFieldTest, ResetFieldEditorKeywordHint) { mock_left_decoration_.SetVisible(true); EXPECT_FALSE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_ inFrame:[field_ bounds]])); + [field_ resetFieldEditorFrameIfNeeded]; EXPECT_TRUE(NSEqualRects(baseEditorFrame, EditorFrame())); } diff --git a/chrome/browser/cocoa/location_bar/location_bar_view_mac.h b/chrome/browser/cocoa/location_bar/location_bar_view_mac.h index 8dd1a2e..24e7344 100644 --- a/chrome/browser/cocoa/location_bar/location_bar_view_mac.h +++ b/chrome/browser/cocoa/location_bar/location_bar_view_mac.h @@ -130,8 +130,10 @@ class LocationBarViewMac : public AutocompleteEditController, virtual std::wstring GetTitle() const; NSImage* GetTabButtonImage(); + NSImage* GetKeywordImage(const std::wstring& keyword); AutocompleteTextField* GetAutocompleteTextField() { return field_; } + // Overridden from NotificationObserver. virtual void Observe(NotificationType type, const NotificationSource& source, diff --git a/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm b/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm index ebd1e60..4fb40d6 100644 --- a/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm +++ b/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm @@ -391,6 +391,18 @@ NSImage* LocationBarViewMac::GetTabButtonImage() { return tab_button_image_; } +NSImage* LocationBarViewMac::GetKeywordImage(const std::wstring& keyword) { + const TemplateURL* template_url = + profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword); + if (template_url && template_url->IsExtensionKeyword()) { + const SkBitmap& bitmap = profile_->GetExtensionsService()-> + GetOmniboxIcon(template_url->GetExtensionId()); + return gfx::SkBitmapToNSImage(bitmap); + } + + return AutocompleteEditViewMac::ImageForResource(IDR_OMNIBOX_SEARCH); +} + void LocationBarViewMac::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { @@ -851,8 +863,8 @@ void LocationBarViewMac::Layout() { // Switch from location icon to keyword mode. location_icon_decoration_->SetVisible(false); selected_keyword_decoration_->SetVisible(true); - selected_keyword_decoration_->SetKeyword(short_name, is_extension_keyword); + selected_keyword_decoration_->SetImage(GetKeywordImage(keyword)); // TODO(shess): This goes away once the hints are decorations. [cell clearHint]; diff --git a/chrome/browser/cocoa/location_bar/selected_keyword_decoration.h b/chrome/browser/cocoa/location_bar/selected_keyword_decoration.h index 8df3ed7..683a8ed 100644 --- a/chrome/browser/cocoa/location_bar/selected_keyword_decoration.h +++ b/chrome/browser/cocoa/location_bar/selected_keyword_decoration.h @@ -24,6 +24,8 @@ class SelectedKeywordDecoration : public BubbleDecoration { // return the resulting width. virtual CGFloat GetWidthForSpace(CGFloat width); + void SetImage(NSImage* image); + private: friend class SelectedKeywordDecorationTest; FRIEND_TEST_ALL_PREFIXES(SelectedKeywordDecorationTest, diff --git a/chrome/browser/cocoa/location_bar/selected_keyword_decoration.mm b/chrome/browser/cocoa/location_bar/selected_keyword_decoration.mm index ebb62bf..3c593ac 100644 --- a/chrome/browser/cocoa/location_bar/selected_keyword_decoration.mm +++ b/chrome/browser/cocoa/location_bar/selected_keyword_decoration.mm @@ -30,12 +30,12 @@ CGFloat SelectedKeywordDecoration::GetWidthForSpace(CGFloat width) { const CGFloat full_width = GetWidthForImageAndLabel(search_image_, full_string_); if (full_width <= width) { - SetImage(search_image_); + BubbleDecoration::SetImage(search_image_); SetLabel(full_string_); return full_width; } - SetImage(nil); + BubbleDecoration::SetImage(nil); const CGFloat no_image_width = GetWidthForImageAndLabel(nil, full_string_); if (no_image_width <= width || !partial_string_) { SetLabel(full_string_); @@ -65,3 +65,9 @@ void SelectedKeywordDecoration::SetKeyword(const std::wstring& short_name, [l10n_util::GetNSStringF(message_id, WideToUTF16(min_name)) copy]); } } + +void SelectedKeywordDecoration::SetImage(NSImage* image) { + if (image != search_image_) + search_image_.reset([image retain]); + BubbleDecoration::SetImage(image); +} diff --git a/chrome/browser/extensions/extension_icon_manager.cc b/chrome/browser/extensions/extension_icon_manager.cc new file mode 100755 index 0000000..1e3befc --- /dev/null +++ b/chrome/browser/extensions/extension_icon_manager.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2010 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/extensions/extension_icon_manager.h" + +#include "app/resource_bundle.h" +#include "base/logging.h" +#include "base/stl_util-inl.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_resource.h" +#include "gfx/color_utils.h" +#include "gfx/favicon_size.h" +#include "gfx/skbitmap_operations.h" +#include "gfx/size.h" +#include "grit/theme_resources.h" +#include "skia/ext/image_operations.h" + +ExtensionIconManager::ExtensionIconManager() + : ALLOW_THIS_IN_INITIALIZER_LIST(image_tracker_(this)), + monochrome_(false) { +} + +void ExtensionIconManager::LoadIcon(Extension* extension) { + ExtensionResource icon_resource; + extension->GetIconPathAllowLargerSize(&icon_resource, + Extension::EXTENSION_ICON_BITTY); + if (!icon_resource.extension_root().empty()) { + image_tracker_.LoadImage(extension, + icon_resource, + gfx::Size(kFavIconSize, kFavIconSize), + ImageLoadingTracker::CACHE); + pending_icons_.insert(extension->id()); + } +} + +const SkBitmap& ExtensionIconManager::GetIcon(const std::string& extension_id) { + const SkBitmap* result = NULL; + if (ContainsKey(icons_, extension_id)) { + result = &icons_[extension_id]; + } else { + EnsureDefaultIcon(); + result = &default_icon_; + } + DCHECK(result); + DCHECK(result->width() == kFavIconSize); + DCHECK(result->height() == kFavIconSize); + return *result; +} + +void ExtensionIconManager::RemoveIcon(const std::string& extension_id) { + icons_.erase(extension_id); + pending_icons_.erase(extension_id); +} + +void ExtensionIconManager::OnImageLoaded(SkBitmap* image, + ExtensionResource resource, + int index) { + if (!image) + return; + + const std::string extension_id = resource.extension_id(); + + // We may have removed the icon while waiting for it to load. In that case, + // do nothing. + if (!ContainsKey(pending_icons_, extension_id)) + return; + + pending_icons_.erase(extension_id); + icons_[extension_id] = ApplyTransforms(*image); +} + +void ExtensionIconManager::EnsureDefaultIcon() { + if (default_icon_.empty()) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + SkBitmap* src = rb.GetBitmapNamed(IDR_EXTENSIONS_SECTION); + default_icon_ = ApplyTransforms(*src); + } +} + +SkBitmap ExtensionIconManager::ApplyTransforms(const SkBitmap& source) { + SkBitmap result = source; + + if (result.width() != kFavIconSize || result.height() != kFavIconSize) { + result = skia::ImageOperations::Resize( + result, skia::ImageOperations::RESIZE_LANCZOS3, + kFavIconSize, kFavIconSize); + } + + if (monochrome_) { + color_utils::HSL shift = {-1, 0, 0.6}; + result = SkBitmapOperations::CreateHSLShiftedBitmap(result, shift); + } + + return result; +} diff --git a/chrome/browser/extensions/extension_icon_manager.h b/chrome/browser/extensions/extension_icon_manager.h new file mode 100755 index 0000000..abf7192 --- /dev/null +++ b/chrome/browser/extensions/extension_icon_manager.h @@ -0,0 +1,66 @@ +// Copyright (c) 2010 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_EXTENSIONS_EXTENSION_ICON_MANAGER_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_ICON_MANAGER_H_ + +#include <map> +#include <set> +#include <string> + +#include "base/basictypes.h" +#include "chrome/browser/extensions/image_loading_tracker.h" +#include "third_party/skia/include/core/SkBitmap.h" + +class Extension; + +class ExtensionIconManager : public ImageLoadingTracker::Observer { + public: + ExtensionIconManager(); + + // Start loading the icon for the given extension. + void LoadIcon(Extension* extension); + + // This returns a bitmap of width/height kFavIconSize, loaded either from an + // entry specified in the extension's 'icon' section of the manifest, or a + // default extension icon. + const SkBitmap& GetIcon(const std::string& extension_id); + + // Removes the extension's icon from memory. + void RemoveIcon(const std::string& extension_id); + + // Implements the ImageLoadingTracker::Observer interface. + virtual void OnImageLoaded(SkBitmap* image, ExtensionResource resource, + int index); + + void set_monochrome(bool value) { monochrome_ = value; } + + private: + // Makes sure we've done one-time initialization of the default extension icon + // default_icon_. + void EnsureDefaultIcon(); + + // Helper function to return a copy of |src| with the proper scaling and + // coloring. + SkBitmap ApplyTransforms(const SkBitmap& src); + + // Used for loading extension icons. + ImageLoadingTracker image_tracker_; + + // Maps extension id to an SkBitmap with the icon for that extension. + std::map<std::string, SkBitmap> icons_; + + // Set of extension IDs waiting for icons to load. + std::set<std::string> pending_icons_; + + // The default icon we'll use if an extension doesn't have one. + SkBitmap default_icon_; + + // If true, we will desaturate the icons to make them monochromatic. + bool monochrome_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionIconManager); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_ICON_MANAGER_H_ diff --git a/chrome/browser/extensions/extension_menu_manager.cc b/chrome/browser/extensions/extension_menu_manager.cc index 1bc55c7..8cce43c 100644 --- a/chrome/browser/extensions/extension_menu_manager.cc +++ b/chrome/browser/extensions/extension_menu_manager.cc @@ -6,23 +6,17 @@ #include <algorithm> -#include "app/resource_bundle.h" #include "base/logging.h" #include "base/stl_util-inl.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "base/json/json_writer.h" -#include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/extensions/extension_tabs_module.h" #include "chrome/browser/profile.h" #include "chrome/common/extensions/extension.h" -#include "chrome/common/extensions/extension_resource.h" #include "gfx/favicon_size.h" -#include "gfx/size.h" -#include "grit/theme_resources.h" -#include "skia/ext/image_operations.h" #include "webkit/glue/context_menu.h" ExtensionMenuItem::ExtensionMenuItem(const Id& id, @@ -35,7 +29,8 @@ ExtensionMenuItem::ExtensionMenuItem(const Id& id, type_(type), checked_(checked), contexts_(contexts), - parent_id_(0) {} + parent_id_(0) { +} ExtensionMenuItem::~ExtensionMenuItem() { STLDeleteElements(&children_); @@ -101,8 +96,7 @@ void ExtensionMenuItem::AddChild(ExtensionMenuItem* item) { children_.push_back(item); } -ExtensionMenuManager::ExtensionMenuManager() - : ALLOW_THIS_IN_INITIALIZER_LIST(image_tracker_(this)) { +ExtensionMenuManager::ExtensionMenuManager() { registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, NotificationService::AllSources()); } @@ -150,17 +144,8 @@ bool ExtensionMenuManager::AddContextItem(Extension* extension, RadioItemSelected(item); // If this is the first item for this extension, start loading its icon. - if (first_item) { - ExtensionResource icon_resource; - extension->GetIconPathAllowLargerSize(&icon_resource, - Extension::EXTENSION_ICON_BITTY); - if (!icon_resource.extension_root().empty()) { - image_tracker_.LoadImage(extension, - icon_resource, - gfx::Size(kFavIconSize, kFavIconSize), - ImageLoadingTracker::CACHE); - } - } + if (first_item) + icon_manager_.LoadIcon(extension); return true; } @@ -275,8 +260,8 @@ bool ExtensionMenuManager::RemoveContextMenuItem( } DCHECK(result); // The check at the very top should have prevented this. - if (list.empty() && ContainsKey(extension_icons_, extension_id)) - extension_icons_.erase(extension_id); + if (list.empty()) + icon_manager_.RemoveIcon(extension_id); return result; } @@ -297,9 +282,7 @@ void ExtensionMenuManager::RemoveAllContextItems(std::string extension_id) { } STLDeleteElements(&context_items_[extension_id]); context_items_.erase(extension_id); - - if (ContainsKey(extension_icons_, extension_id)) - extension_icons_.erase(extension_id); + icon_manager_.RemoveIcon(extension_id); } ExtensionMenuItem* ExtensionMenuManager::GetItemById( @@ -461,54 +444,5 @@ void ExtensionMenuManager::Observe(NotificationType type, const SkBitmap& ExtensionMenuManager::GetIconForExtension( const std::string& extension_id) { - const SkBitmap* result = NULL; - if (ContainsKey(extension_icons_, extension_id)) { - result = &(extension_icons_[extension_id]); - } else { - EnsureDefaultIcon(); - result = &default_icon_; - } - DCHECK(result); - DCHECK(result->width() == kFavIconSize); - DCHECK(result->height() == kFavIconSize); - return *result; -} - -void ExtensionMenuManager::OnImageLoaded(SkBitmap* image, - ExtensionResource resource, - int index) { - if (!image) - return; - - const std::string extension_id = resource.extension_id(); - - // Make sure we still have menu items for this extension (since image loading - // is asynchronous, there's a slight chance they may have all been removed - // while the icon was loading). - if (!ContainsKey(context_items_, extension_id)) - return; - - if (image->width() == kFavIconSize && image->height() == kFavIconSize) - extension_icons_[extension_id] = *image; - else - extension_icons_[extension_id] = ScaleToFavIconSize(*image); -} - -void ExtensionMenuManager::EnsureDefaultIcon() { - if (default_icon_.empty()) { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - SkBitmap* src = rb.GetBitmapNamed(IDR_EXTENSIONS_SECTION); - if (src->width() == kFavIconSize && src->height() == kFavIconSize) { - default_icon_ = *src; - } else { - default_icon_ = SkBitmap(ScaleToFavIconSize(*src)); - } - } -} - -SkBitmap ExtensionMenuManager::ScaleToFavIconSize(const SkBitmap& source) { - return skia::ImageOperations::Resize(source, - skia::ImageOperations::RESIZE_LANCZOS3, - kFavIconSize, - kFavIconSize); + return icon_manager_.GetIcon(extension_id); } diff --git a/chrome/browser/extensions/extension_menu_manager.h b/chrome/browser/extensions/extension_menu_manager.h index 9e407d0..882c972 100644 --- a/chrome/browser/extensions/extension_menu_manager.h +++ b/chrome/browser/extensions/extension_menu_manager.h @@ -13,16 +13,16 @@ #include "base/basictypes.h" #include "base/scoped_ptr.h" #include "base/string16.h" -#include "chrome/browser/extensions/image_loading_tracker.h" +#include "chrome/browser/extensions/extension_icon_manager.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" -#include "third_party/skia/include/core/SkBitmap.h" struct ContextMenuParams; class Extension; class ExtensionMessageService; class Profile; +class SkBitmap; class TabContents; // Represents a menu item added by an extension. @@ -156,9 +156,7 @@ class ExtensionMenuItem { }; // This class keeps track of menu items added by extensions. -class ExtensionMenuManager - : public ImageLoadingTracker::Observer, - public NotificationObserver { +class ExtensionMenuManager : public NotificationObserver { public: ExtensionMenuManager(); virtual ~ExtensionMenuManager(); @@ -207,18 +205,14 @@ class ExtensionMenuManager const ContextMenuParams& params, const ExtensionMenuItem::Id& menuItemId); - // Implements the NotificationObserver interface. - virtual void Observe(NotificationType type, const NotificationSource& source, - const NotificationDetails& details); - // This returns a bitmap of width/height kFavIconSize, loaded either from an // entry specified in the extension's 'icon' section of the manifest, or a // default extension icon. const SkBitmap& GetIconForExtension(const std::string& extension_id); - // Implements the ImageLoadingTracker::Observer interface. - virtual void OnImageLoaded(SkBitmap* image, ExtensionResource resource, - int index); + // Implements the NotificationObserver interface. + virtual void Observe(NotificationType type, const NotificationSource& source, + const NotificationDetails& details); private: // This is a helper function which takes care of de-selecting any other radio @@ -229,13 +223,6 @@ class ExtensionMenuManager bool DescendantOf(ExtensionMenuItem* item, const ExtensionMenuItem::Id& ancestor_id); - // Makes sure we've done one-time initialization of the default extension icon - // default_icon_. - void EnsureDefaultIcon(); - - // Helper function to return a copy of |src| scaled to kFavIconSize. - SkBitmap ScaleToFavIconSize(const SkBitmap& src); - // We keep items organized by mapping an extension id to a list of items. typedef std::map<std::string, ExtensionMenuItem::List> MenuItemMap; MenuItemMap context_items_; @@ -247,14 +234,7 @@ class ExtensionMenuManager NotificationRegistrar registrar_; - // Used for loading extension icons. - ImageLoadingTracker image_tracker_; - - // Maps extension id to an SkBitmap with the icon for that extension. - std::map<std::string, SkBitmap> extension_icons_; - - // The default icon we'll use if an extension doesn't have one. - SkBitmap default_icon_; + ExtensionIconManager icon_manager_; DISALLOW_COPY_AND_ASSIGN(ExtensionMenuManager); }; diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index 9a91533..3259c11 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -186,6 +186,9 @@ ExtensionsService::ExtensionsService(Profile* profile, } backend_ = new ExtensionsServiceBackend(install_directory_); + + // Use monochrome icons for omnibox icons. + omnibox_icon_manager_.set_monochrome(true); } ExtensionsService::~ExtensionsService() { @@ -906,6 +909,11 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, if (profile_->GetTemplateURLModel()) profile_->GetTemplateURLModel()->RegisterExtensionKeyword(extension); + + // Load the icon for omnibox-enabled extensions so it will be ready to display + // in the URL bar. + if (!extension->omnibox_keyword().empty()) + omnibox_icon_manager_.LoadIcon(extension); } void ExtensionsService::UpdateActiveExtensionsInCrashReporter() { @@ -1029,6 +1037,11 @@ Extension* ExtensionsService::GetExtensionByOverlappingWebExtent( return NULL; } +const SkBitmap& ExtensionsService::GetOmniboxIcon( + const std::string& extension_id) { + return omnibox_icon_manager_.GetIcon(extension_id); +} + void ExtensionsService::ClearProvidersForTesting() { ChromeThread::PostTask( ChromeThread::FILE, FROM_HERE, diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h index 5385da5..fa4e260 100644 --- a/chrome/browser/extensions/extensions_service.h +++ b/chrome/browser/extensions/extensions_service.h @@ -20,6 +20,7 @@ #include "base/tuple.h" #include "base/values.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/extensions/extension_icon_manager.h" #include "chrome/browser/extensions/extension_menu_manager.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_process_manager.h" @@ -275,6 +276,9 @@ class ExtensionsService // extent, if one exists. Extension* GetExtensionByOverlappingWebExtent(const ExtensionExtent& extent); + // Returns the icon to display in the omnibox for the given extension. + const SkBitmap& GetOmniboxIcon(const std::string& extension_id); + // Clear all ExternalExtensionProviders. void ClearProvidersForTesting(); @@ -333,7 +337,6 @@ class ExtensionsService ExtensionsQuotaService* quota_service() { return "a_service_; } - // Access to menu items added by extensions. ExtensionMenuManager* menu_manager() { return &menu_manager_; } // Notify the frontend that there was an error loading an extension. @@ -436,6 +439,9 @@ class ExtensionsService // Keeps track of menu items added by extensions. ExtensionMenuManager menu_manager_; + // Keeps track of favicon-sized omnibox icons for extensions. + ExtensionIconManager omnibox_icon_manager_; + // List of registered component extensions (see Extension::Location). typedef std::vector<ComponentExtensionInfo> RegisteredComponentExtensions; RegisteredComponentExtensions component_extension_manifests_; diff --git a/chrome/browser/gtk/location_bar_view_gtk.cc b/chrome/browser/gtk/location_bar_view_gtk.cc index 601586b..24c7151 100644 --- a/chrome/browser/gtk/location_bar_view_gtk.cc +++ b/chrome/browser/gtk/location_bar_view_gtk.cc @@ -208,9 +208,9 @@ void LocationBarViewGtk::Init(bool popup_window_mode) { tab_to_search_partial_label_, FALSE, FALSE, 0); GtkWidget* tab_to_search_hbox = gtk_hbox_new(FALSE, 0); ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - GtkWidget* tab_to_search_lens = gtk_image_new_from_pixbuf( + tab_to_search_magnifier_ = gtk_image_new_from_pixbuf( rb.GetPixbufNamed(IDR_OMNIBOX_SEARCH)); - gtk_box_pack_start(GTK_BOX(tab_to_search_hbox), tab_to_search_lens, + gtk_box_pack_start(GTK_BOX(tab_to_search_hbox), tab_to_search_magnifier_, FALSE, FALSE, 0); gtk_util::CenterWidgetInHBox(tab_to_search_hbox, tab_to_search_label_hbox, false, 0); @@ -875,6 +875,24 @@ void LocationBarViewGtk::SetKeywordLabel(const std::wstring& keyword) { WideToUTF8(full_name).c_str()); gtk_label_set_text(GTK_LABEL(tab_to_search_partial_label_), WideToUTF8(partial_name).c_str()); + + if (last_keyword_ != keyword) { + last_keyword_ = keyword; + + if (is_extension_keyword) { + const TemplateURL* template_url = + profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword); + const SkBitmap& bitmap = profile_->GetExtensionsService()-> + GetOmniboxIcon(template_url->GetExtensionId()); + GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&bitmap); + gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_), pixbuf); + g_object_unref(pixbuf); + } else { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_), + rb.GetPixbufNamed(IDR_OMNIBOX_SEARCH)); + } + } } void LocationBarViewGtk::SetKeywordHintLabel(const std::wstring& keyword) { diff --git a/chrome/browser/gtk/location_bar_view_gtk.h b/chrome/browser/gtk/location_bar_view_gtk.h index da87cdc..870e415 100644 --- a/chrome/browser/gtk/location_bar_view_gtk.h +++ b/chrome/browser/gtk/location_bar_view_gtk.h @@ -362,6 +362,7 @@ class LocationBarViewGtk : public AutocompleteEditController, // Area on the left shown when in tab to search mode. GtkWidget* tab_to_search_box_; + GtkWidget* tab_to_search_magnifier_; GtkWidget* tab_to_search_full_label_; GtkWidget* tab_to_search_partial_label_; @@ -417,6 +418,9 @@ class LocationBarViewGtk : public AutocompleteEditController, // Indicate if |tab_to_search_hint_| should be shown. bool show_keyword_hint_; + // The last search keyword that was shown via the |tab_to_search_box_|. + std::wstring last_keyword_; + DISALLOW_COPY_AND_ASSIGN(LocationBarViewGtk); }; diff --git a/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.cc b/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.cc index 17d243c..45a10bf 100644 --- a/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.cc +++ b/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.cc @@ -153,7 +153,7 @@ class AutocompleteResultView : public views::View { ResultViewState GetState() const; - SkBitmap* GetIcon() const; + const SkBitmap* GetIcon() const; // Draws the specified |text| into the canvas, using highlighting provided by // |classifications|. If |force_dim| is true, ACMatchClassification::DIM is @@ -339,7 +339,11 @@ ResultViewState AutocompleteResultView::GetState() const { return model_->IsHoveredIndex(model_index_) ? HOVERED : NORMAL; } -SkBitmap* AutocompleteResultView::GetIcon() const { +const SkBitmap* AutocompleteResultView::GetIcon() const { + const SkBitmap* bitmap = model_->GetSpecialIcon(model_index_); + if (bitmap) + return bitmap; + int icon = match_.starred ? IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match_.type); if (model_->IsSelectedIndex(model_index_)) { @@ -740,6 +744,13 @@ bool AutocompletePopupContentsView::IsHoveredIndex(size_t index) const { return HasMatchAt(index) ? index == model_->hovered_line() : false; } +const SkBitmap* AutocompletePopupContentsView::GetSpecialIcon( + size_t index) const { + if (!HasMatchAt(index)) + return NULL; + return model_->GetSpecialIconForMatch(GetMatchAtIndex(index)); +} + //////////////////////////////////////////////////////////////////////////////// // AutocompletePopupContentsView, AnimationDelegate implementation: diff --git a/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.h b/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.h index d814206..80704cd 100644 --- a/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.h +++ b/chrome/browser/views/autocomplete/autocomplete_popup_contents_view.h @@ -33,6 +33,10 @@ class AutocompleteResultViewModel { // Returns true if the index is hovered. virtual bool IsHoveredIndex(size_t index) const = 0; + + // Returns the special-case icon we should use for the given index, or NULL + // if we should use the default icon. + virtual const SkBitmap* GetSpecialIcon(size_t index) const = 0; }; // A view representing the contents of the autocomplete popup. @@ -63,6 +67,7 @@ class AutocompletePopupContentsView : public views::View, // Overridden from AutocompleteResultViewModel: virtual bool IsSelectedIndex(size_t index) const; virtual bool IsHoveredIndex(size_t index) const; + virtual const SkBitmap* GetSpecialIcon(size_t index) const; // Overridden from AnimationDelegate: virtual void AnimationProgressed(const Animation* animation); diff --git a/chrome/browser/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/views/location_bar/icon_label_bubble_view.cc index 729c9a1..4e92ac0 100644 --- a/chrome/browser/views/location_bar/icon_label_bubble_view.cc +++ b/chrome/browser/views/location_bar/icon_label_bubble_view.cc @@ -43,6 +43,10 @@ void IconLabelBubbleView::SetLabel(const std::wstring& label) { label_->SetText(label); } +void IconLabelBubbleView::SetImage(const SkBitmap& bitmap) { + image_->SetImage(bitmap); +} + void IconLabelBubbleView::Paint(gfx::Canvas* canvas) { int y_offset = (GetParent()->height() - height()) / 2; canvas->TranslateInt(0, y_offset); diff --git a/chrome/browser/views/location_bar/icon_label_bubble_view.h b/chrome/browser/views/location_bar/icon_label_bubble_view.h index 53b0b33..dd5b196 100644 --- a/chrome/browser/views/location_bar/icon_label_bubble_view.h +++ b/chrome/browser/views/location_bar/icon_label_bubble_view.h @@ -20,6 +20,8 @@ class ImageView; class Label; } +class SkBitmap; + // View used to draw a bubble to the left of the address, containing an icon and // a label. We use this as a base for the classes that handle the EV bubble and // tab-to-search UI. @@ -32,6 +34,7 @@ class IconLabelBubbleView : public views::View { void SetFont(const gfx::Font& font); void SetLabel(const std::wstring& label); + void SetImage(const SkBitmap& bitmap); virtual void Paint(gfx::Canvas* canvas); virtual gfx::Size GetPreferredSize(); diff --git a/chrome/browser/views/location_bar/location_bar_view.cc b/chrome/browser/views/location_bar/location_bar_view.cc index 8ed874d..4dffa4e 100644 --- a/chrome/browser/views/location_bar/location_bar_view.cc +++ b/chrome/browser/views/location_bar/location_bar_view.cc @@ -17,6 +17,7 @@ #include "chrome/browser/extensions/extension_browser_event_router.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/profile.h" +#include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/view_ids.h" #include "chrome/browser/views/browser_dialogs.h" #include "chrome/browser/views/location_bar/content_setting_image_view.h" @@ -457,8 +458,20 @@ void LocationBarView::Layout() { selected_keyword_view_->SetVisible(show_selected_keyword); keyword_hint_view_->SetVisible(show_keyword_hint); if (show_selected_keyword) { - if (selected_keyword_view_->keyword() != keyword) + if (selected_keyword_view_->keyword() != keyword) { selected_keyword_view_->SetKeyword(keyword); + + const TemplateURL* template_url = + profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword); + if (template_url && template_url->IsExtensionKeyword()) { + const SkBitmap& bitmap = profile_->GetExtensionsService()-> + GetOmniboxIcon(template_url->GetExtensionId()); + selected_keyword_view_->SetImage(bitmap); + } else { + selected_keyword_view_->SetImage(*ResourceBundle::GetSharedInstance(). + GetBitmapNamed(IDR_OMNIBOX_SEARCH)); + } + } } else if (show_keyword_hint) { if (keyword_hint_view_->keyword() != keyword) keyword_hint_view_->SetKeyword(keyword); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index e5438f3..ce10a59 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1216,6 +1216,8 @@ 'browser/extensions/extension_host.h', 'browser/extensions/extension_host_mac.h', 'browser/extensions/extension_host_mac.mm', + 'browser/extensions/extension_icon_manager.cc', + 'browser/extensions/extension_icon_manager.h', 'browser/extensions/extension_idle_api.cc', 'browser/extensions/extension_idle_api.h', 'browser/extensions/extension_idle_api_constants.cc', |