diff options
author | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-10 22:25:16 +0000 |
---|---|---|
committer | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-10 22:25:16 +0000 |
commit | 10fb8126df079ccf47a308f2b5a40ed01a8efda0 (patch) | |
tree | 1d6c4d7b0fcb33e73024ede12eee97b1b57077a6 /chrome/browser | |
parent | 64fe65afcd193bea778490faa2d562bc96430502 (diff) | |
download | chromium_src-10fb8126df079ccf47a308f2b5a40ed01a8efda0.zip chromium_src-10fb8126df079ccf47a308f2b5a40ed01a8efda0.tar.gz chromium_src-10fb8126df079ccf47a308f2b5a40ed01a8efda0.tar.bz2 |
Linux: implement Page Actions support.
BUG=11973
TEST=load an extension with page actions, it should work like on windows.
Review URL: http://codereview.chromium.org/195050
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@25934 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/extensions/extension_browsertests_misc.cc | 4 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.cc | 16 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.h | 4 | ||||
-rw-r--r-- | chrome/browser/gtk/browser_toolbar_gtk.cc | 3 | ||||
-rw-r--r-- | chrome/browser/gtk/location_bar_view_gtk.cc | 159 | ||||
-rw-r--r-- | chrome/browser/gtk/location_bar_view_gtk.h | 64 | ||||
-rw-r--r-- | chrome/browser/image_loading_tracker.cc | 114 | ||||
-rw-r--r-- | chrome/browser/image_loading_tracker.h | 72 | ||||
-rw-r--r-- | chrome/browser/views/location_bar_view.cc | 164 | ||||
-rw-r--r-- | chrome/browser/views/location_bar_view.h | 19 |
10 files changed, 447 insertions, 172 deletions
diff --git a/chrome/browser/extensions/extension_browsertests_misc.cc b/chrome/browser/extensions/extension_browsertests_misc.cc index 3da3b0c..059016c 100644 --- a/chrome/browser/extensions/extension_browsertests_misc.cc +++ b/chrome/browser/extensions/extension_browsertests_misc.cc @@ -193,7 +193,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TabContents) { EXPECT_TRUE(result); } -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) // Tests that we can load page actions in the Omnibox. IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, PageAction) { ASSERT_TRUE(LoadExtension( @@ -220,7 +220,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, PageAction) { ui_test_utils::NavigateToURL(browser(), net::FilePathToFileURL(no_feed)); ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(0)); } -#endif // defined(OS_WIN) +#endif // defined(OS_WIN) || defined(OS_LINUX) GURL GetFeedUrl(const std::string& feed_page) { FilePath test_dir; diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index d7cb206..7b16360 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -144,6 +144,22 @@ void ExtensionsService::Init() { GarbageCollectExtensions(); } +std::vector<PageAction*> ExtensionsService::GetPageActions() const { + std::vector<PageAction*> result; + + // TODO(finnur): Sort the page icons in some meaningful way. + for (ExtensionList::const_iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + const PageActionMap& page_actions = (*iter)->page_actions(); + for (PageActionMap::const_iterator i(page_actions.begin()); + i != page_actions.end(); ++i) { + result.push_back(i->second); + } + } + + return result; +} + void ExtensionsService::InstallExtension(const FilePath& extension_path) { CrxInstaller::Start(extension_path, install_directory_, Extension::INTERNAL, "", // no expected id diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h index a17ef93..85822a1 100644 --- a/chrome/browser/extensions/extensions_service.h +++ b/chrome/browser/extensions/extensions_service.h @@ -102,6 +102,10 @@ class ExtensionsService return GetExtensionByIdInternal(id, true, false); } + // Retrieves a vector of all page actions, irrespective of which + // extension they belong to. + std::vector<PageAction*> GetPageActions() const; + // Install the extension file at |extension_path|. Will install as an // update if an older version is already installed. // For fresh installs, this method also causes the extension to be diff --git a/chrome/browser/gtk/browser_toolbar_gtk.cc b/chrome/browser/gtk/browser_toolbar_gtk.cc index 8f4c00c..87f2d5a 100644 --- a/chrome/browser/gtk/browser_toolbar_gtk.cc +++ b/chrome/browser/gtk/browser_toolbar_gtk.cc @@ -79,7 +79,8 @@ BrowserToolbarGtk::BrowserToolbarGtk(Browser* browser, BrowserWindowGtk* window) : toolbar_(NULL), location_bar_(new LocationBarViewGtk(browser->command_updater(), browser->toolbar_model(), - this)), + this, + browser)), model_(browser->toolbar_model()), browser_(browser), window_(window), diff --git a/chrome/browser/gtk/location_bar_view_gtk.cc b/chrome/browser/gtk/location_bar_view_gtk.cc index a53b244..3184cd4 100644 --- a/chrome/browser/gtk/location_bar_view_gtk.cc +++ b/chrome/browser/gtk/location_bar_view_gtk.cc @@ -15,8 +15,11 @@ #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/alternate_nav_url_fetcher.h" #include "chrome/browser/autocomplete/autocomplete_edit_view_gtk.h" +#include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/command_updater.h" +#include "chrome/browser/extensions/extension_browser_event_router.h" +#include "chrome/browser/extensions/extension_tabs_module.h" #include "chrome/browser/gtk/first_run_bubble.h" #include "chrome/browser/gtk/gtk_theme_provider.h" #include "chrome/browser/gtk/rounded_window.h" @@ -80,8 +83,18 @@ std::wstring GetKeywordName(Profile* profile, return std::wstring(); } +// If widget is visible, increment the int pointed to by count. +// Suitible for use with gtk_container_foreach. +void CountVisibleWidgets(GtkWidget* widget, gpointer count) { + if (GTK_WIDGET_VISIBLE(widget)) + *static_cast<int*>(count) += 1; +} + } // namespace +//////////////////////////////////////////////////////////////////////////////// +// LocationBarViewGtk + // static const GdkColor LocationBarViewGtk::kBackgroundColorByLevel[3] = { GDK_COLOR_RGB(255, 245, 195), // SecurityLevel SECURE: Yellow. @@ -90,7 +103,8 @@ const GdkColor LocationBarViewGtk::kBackgroundColorByLevel[3] = { }; LocationBarViewGtk::LocationBarViewGtk(CommandUpdater* command_updater, - ToolbarModel* toolbar_model, AutocompletePopupPositioner* popup_positioner) + ToolbarModel* toolbar_model, AutocompletePopupPositioner* popup_positioner, + Browser* browser) : security_icon_event_box_(NULL), security_lock_icon_image_(NULL), security_warning_icon_image_(NULL), @@ -104,6 +118,7 @@ LocationBarViewGtk::LocationBarViewGtk(CommandUpdater* command_updater, profile_(NULL), command_updater_(command_updater), toolbar_model_(toolbar_model), + browser_(browser), popup_positioner_(popup_positioner), disposition_(CURRENT_TAB), transition_(PageTransition::TYPED), @@ -226,6 +241,9 @@ void LocationBarViewGtk::Init(bool popup_window_mode) { gtk_box_pack_end(GTK_BOX(hbox_.get()), security_icon_event_box_, FALSE, FALSE, 0); + page_action_hbox_ = gtk_hbox_new(FALSE, kInnerPadding); + gtk_box_pack_end(GTK_BOX(hbox_.get()), page_action_hbox_, FALSE, FALSE, 0); + registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, NotificationService::AllSources()); @@ -239,6 +257,7 @@ void LocationBarViewGtk::SetProfile(Profile* profile) { void LocationBarViewGtk::Update(const TabContents* contents) { SetSecurityIcon(toolbar_model_->GetIcon()); + UpdatePageActions(); SetInfoText(); location_entry_->Update(contents); // The security level (background color) could have changed, etc. @@ -369,7 +388,37 @@ void LocationBarViewGtk::FocusSearch() { } void LocationBarViewGtk::UpdatePageActions() { - // http://code.google.com/p/chromium/issues/detail?id=11973 + std::vector<PageAction*> page_actions; + if (profile_->GetExtensionsService()) + page_actions = profile_->GetExtensionsService()->GetPageActions(); + + // Initialize on the first call, or re-inialize if more extensions have been + // loaded or added after startup. + if (page_actions.size() != page_action_views_.size()) { + page_action_views_.reset(); // Delete the old views (if any). + + for (size_t i = 0; i < page_actions.size(); ++i) { + page_action_views_.push_back( + new PageActionViewGtk(this, profile_, page_actions[i])); + gtk_box_pack_end(GTK_BOX(page_action_hbox_), + page_action_views_[i]->widget(), FALSE, FALSE, 0); + } + } + + TabContents* contents = browser_->GetSelectedTabContents(); + if (!page_action_views_.empty() && contents) { + GURL url = GURL(WideToUTF8(toolbar_model_->GetText())); + + for (size_t i = 0; i < page_action_views_.size(); i++) + page_action_views_[i]->UpdateVisibility(contents, url); + } + + // If there are no visible page actions, hide the hbox too, so that it does + // not affect the padding in the location bar. + if (PageActionVisibleCount()) + gtk_widget_show(page_action_hbox_); + else + gtk_widget_hide(page_action_hbox_); } void LocationBarViewGtk::SaveStateToContents(TabContents* contents) { @@ -381,8 +430,10 @@ void LocationBarViewGtk::Revert() { } int LocationBarViewGtk::PageActionVisibleCount() { - NOTIMPLEMENTED(); - return -1; + int count = 0; + gtk_container_foreach(GTK_CONTAINER(page_action_hbox_), CountVisibleWidgets, + &count); + return count; } void LocationBarViewGtk::Observe(NotificationType type, @@ -600,3 +651,103 @@ gboolean LocationBarViewGtk::OnSecurityIconPressed( tab->ShowPageInfo(nav_entry->url(), nav_entry->ssl(), true); return true; } + +//////////////////////////////////////////////////////////////////////////////// +// LocationBarViewGtk::PageActionViewGtk + +LocationBarViewGtk::PageActionViewGtk::PageActionViewGtk( + LocationBarViewGtk* owner, Profile* profile, const PageAction* page_action) + : owner_(owner), + profile_(profile), + page_action_(page_action) { + event_box_.Own(gtk_event_box_new()); + // Make the event box not visible so it does not paint a background. + gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE); + g_signal_connect(event_box_.get(), "button-press-event", + G_CALLBACK(&OnButtonPressed), this); + + image_.Own(gtk_image_new()); + gtk_container_add(GTK_CONTAINER(event_box_.get()), image_.get()); + + Extension* extension = profile->GetExtensionsService()->GetExtensionById( + page_action->extension_id()); + DCHECK(extension); + + DCHECK(!page_action->icon_paths().empty()); + const std::vector<std::string>& icon_paths = page_action->icon_paths(); + pixbufs_.resize(icon_paths.size()); + tracker_ = new ImageLoadingTracker(this, icon_paths.size()); + for (std::vector<std::string>::const_iterator iter = icon_paths.begin(); + iter != icon_paths.end(); ++iter) { + tracker_->PostLoadImageTask(extension->GetResourcePath(*iter)); + } +} + +LocationBarViewGtk::PageActionViewGtk::~PageActionViewGtk() { + if (tracker_) + tracker_->StopTrackingImageLoad(); + image_.Destroy(); + event_box_.Destroy(); + for (size_t i=0; i < pixbufs_.size(); ++i) { + if (pixbufs_[i]) + g_object_unref(pixbufs_[i]); + } +} + +void LocationBarViewGtk::PageActionViewGtk::UpdateVisibility( + TabContents* contents, GURL url) { + // Save this off so we can pass it back to the extension when the action gets + // executed. See PageActionImageView::OnMousePressed. + current_tab_id_ = ExtensionTabUtil::GetTabId(contents); + current_url_ = url; + + const PageActionState* state = contents->GetPageActionState(page_action_); + bool visible = state != NULL; + if (visible) { + // Set the tooltip. + if (state->title().empty()) + gtk_widget_set_tooltip_text(event_box_.get(), + page_action_->name().c_str()); + else + gtk_widget_set_tooltip_text(event_box_.get(), state->title().c_str()); + // Set the image. + int index = state->icon_index(); + // The image index (if not within bounds) will be set to the first image. + if (index < 0 || index >= static_cast<int>(pixbufs_.size())) + index = 0; + // The pixbuf might not be loaded yet. + if (pixbufs_[index]) + gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()), pixbufs_[index]); + else + visible = false; + } + + if (visible) { + gtk_widget_show_all(event_box_.get()); + } else { + gtk_widget_hide_all(event_box_.get()); + } +} + +void LocationBarViewGtk::PageActionViewGtk::OnImageLoaded(SkBitmap* image, + size_t index) { + DCHECK(index < pixbufs_.size()); + if (index == pixbufs_.size() - 1) + tracker_ = NULL; // The tracker object will delete itself when we return. + pixbufs_[index] = gfx::GdkPixbufFromSkBitmap(image); + owner_->UpdatePageActions(); +} + +// static +gboolean LocationBarViewGtk::PageActionViewGtk::OnButtonPressed( + GtkWidget* sender, + GdkEventButton* event, + LocationBarViewGtk::PageActionViewGtk* page_action_view) { + ExtensionBrowserEventRouter::GetInstance()->PageActionExecuted( + page_action_view->profile_, + page_action_view->page_action_->extension_id(), + page_action_view->page_action_->id(), + page_action_view->current_tab_id_, + page_action_view->current_url_.spec()); + return true; +} diff --git a/chrome/browser/gtk/location_bar_view_gtk.h b/chrome/browser/gtk/location_bar_view_gtk.h index 1a8a453..22bf7d1 100644 --- a/chrome/browser/gtk/location_bar_view_gtk.h +++ b/chrome/browser/gtk/location_bar_view_gtk.h @@ -8,11 +8,14 @@ #include <gtk/gtk.h> #include <string> +#include <vector> #include "base/basictypes.h" #include "base/scoped_ptr.h" +#include "base/scoped_vector.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" #include "chrome/browser/autocomplete/autocomplete_edit_view_gtk.h" +#include "chrome/browser/image_loading_tracker.h" #include "chrome/browser/location_bar.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" @@ -22,8 +25,10 @@ class AutocompleteEditViewGtk; class AutocompletePopupPositioner; +class Browser; class CommandUpdater; class GtkThemeProvider; +class PageAction; class Profile; class SkBitmap; class TabContents; @@ -36,7 +41,8 @@ class LocationBarViewGtk : public AutocompleteEditController, public: LocationBarViewGtk(CommandUpdater* command_updater, ToolbarModel* toolbar_model, - AutocompletePopupPositioner* popup_positioner); + AutocompletePopupPositioner* popup_positioner, + Browser* browser_); virtual ~LocationBarViewGtk(); void Init(bool popup_window_mode); @@ -92,6 +98,57 @@ class LocationBarViewGtk : public AutocompleteEditController, static const GdkColor kBackgroundColorByLevel[3]; private: + class PageActionViewGtk : public ImageLoadingTracker::Observer { + public: + PageActionViewGtk( + LocationBarViewGtk* owner, Profile* profile, + const PageAction* page_action); + virtual ~PageActionViewGtk(); + + GtkWidget* widget() { return event_box_.get(); } + + // Called to notify the PageAction that it should determine whether to be + // visible or hidden. |contents| is the TabContents that is active, |url| + // is the current page URL. + void UpdateVisibility(TabContents* contents, GURL url); + + // A callback from ImageLoadingTracker for when the image has loaded. + virtual void OnImageLoaded(SkBitmap* image, size_t index); + + private: + static gboolean OnButtonPressed(GtkWidget* sender, GdkEventButton* event, + PageActionViewGtk* page_action_view); + + // The location bar view that owns us. + LocationBarViewGtk* owner_; + + // The current profile (not owned by us). + Profile* profile_; + + // The PageAction that this view represents. The PageAction is not owned by + // us, it resides in the extension of this particular profile. + const PageAction* page_action_; + + // The icons representing different states for the page action. + std::vector<GdkPixbuf*> pixbufs_; + + // The object that is waiting for the image loading to complete + // asynchronously. It will delete itself once it is done. + ImageLoadingTracker* tracker_; + + // The widgets for this page action. + OwnedWidgetGtk event_box_; + OwnedWidgetGtk image_; + + // The tab id we are currently showing the icon for. + int current_tab_id_; + + // The URL we are currently showing the icon for. + GURL current_url_; + + DISALLOW_COPY_AND_ASSIGN(PageActionViewGtk); + }; + static gboolean HandleExposeThunk(GtkWidget* widget, GdkEventExpose* event, gpointer userdata) { return reinterpret_cast<LocationBarViewGtk*>(userdata)-> @@ -129,6 +186,10 @@ class LocationBarViewGtk : public AutocompleteEditController, // Toolbar info text (EV cert info). GtkWidget* info_label_; + // Extension page action icons. + GtkWidget* page_action_hbox_; + ScopedVector<PageActionViewGtk> page_action_views_; + // Area on the left shown when in tab to search mode. GtkWidget* tab_to_search_box_; GtkWidget* tab_to_search_label_; @@ -144,6 +205,7 @@ class LocationBarViewGtk : public AutocompleteEditController, Profile* profile_; CommandUpdater* command_updater_; ToolbarModel* toolbar_model_; + Browser* browser_; // We need to hold on to this just to it pass to the edit. AutocompletePopupPositioner* popup_positioner_; diff --git a/chrome/browser/image_loading_tracker.cc b/chrome/browser/image_loading_tracker.cc new file mode 100644 index 0000000..423e5fd --- /dev/null +++ b/chrome/browser/image_loading_tracker.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2009 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/image_loading_tracker.h" + +#include "app/gfx/favicon_size.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "base/thread.h" +#include "chrome/browser/browser_process.h" +#include "skia/ext/image_operations.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "webkit/glue/image_decoder.h" + +//////////////////////////////////////////////////////////////////////////////// +// ImageLoadingTracker::LoadImageTask + +// The LoadImageTask is for asynchronously loading the image on the file thread. +// If the image is successfully loaded and decoded it will report back on the +// |callback_loop| to let the caller know the image is done loading. +class ImageLoadingTracker::LoadImageTask : public Task { + public: + // Constructor for the LoadImageTask class. |tracker| is the object that + // we use to communicate back to the entity that wants the image after we + // decode it. |path| is the path to load the image from. |index| is an + // identifier for the image that we pass back to the caller. + LoadImageTask(ImageLoadingTracker* tracker, + const FilePath& path, + size_t index) + : callback_loop_(MessageLoop::current()), + tracker_(tracker), + path_(path), + index_(index) {} + + void ReportBack(SkBitmap* image) { + DCHECK(image); + callback_loop_->PostTask(FROM_HERE, NewRunnableMethod(tracker_, + &ImageLoadingTracker::OnImageLoaded, + image, + index_)); + } + + virtual void Run() { + // Read the file from disk. + std::string file_contents; + if (!file_util::PathExists(path_) || + !file_util::ReadFileToString(path_, &file_contents)) { + ReportBack(NULL); + return; + } + + // Decode the image using WebKit's image decoder. + const unsigned char* data = + reinterpret_cast<const unsigned char*>(file_contents.data()); + webkit_glue::ImageDecoder decoder(gfx::Size(kFavIconSize, kFavIconSize)); + scoped_ptr<SkBitmap> decoded(new SkBitmap()); + *decoded = decoder.Decode(data, file_contents.length()); + if (decoded->empty()) { + ReportBack(NULL); + return; // Unable to decode. + } + + if (decoded->width() != kFavIconSize || decoded->height() != kFavIconSize) { + // The bitmap is not the correct size, re-sample. + int new_width = decoded->width(); + int new_height = decoded->height(); + // Calculate what dimensions to use within the constraints (16x16 max). + calc_favicon_target_size(&new_width, &new_height); + *decoded = skia::ImageOperations::Resize( + *decoded, skia::ImageOperations::RESIZE_LANCZOS3, + new_width, new_height); + } + + ReportBack(decoded.release()); + } + + private: + // The message loop that we need to call back on to report that we are done. + MessageLoop* callback_loop_; + + // The object that is waiting for us to respond back. + ImageLoadingTracker* tracker_; + + // The path to the image to load asynchronously. + FilePath path_; + + // The index of the icon being loaded. + size_t index_; +}; + +//////////////////////////////////////////////////////////////////////////////// +// ImageLoadingTracker + +void ImageLoadingTracker::PostLoadImageTask(FilePath path) { + MessageLoop* file_loop = g_browser_process->file_thread()->message_loop(); + file_loop->PostTask(FROM_HERE, new LoadImageTask(this, path, + posted_count_++)); +} + +void ImageLoadingTracker::OnImageLoaded(SkBitmap* image, size_t index) { + if (image == NULL) { + NOTREACHED() << "Image failed to decode."; + image = new SkBitmap(); + } + if (observer_) + observer_->OnImageLoaded(image, index); + delete image; + if (--image_count_ == 0) + Release(); // We are no longer needed. +} diff --git a/chrome/browser/image_loading_tracker.h b/chrome/browser/image_loading_tracker.h new file mode 100644 index 0000000..7265b63 --- /dev/null +++ b/chrome/browser/image_loading_tracker.h @@ -0,0 +1,72 @@ +// Copyright (c) 2009 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_IMAGE_LOADING_TRACKER_H_ +#define CHROME_BROWSER_IMAGE_LOADING_TRACKER_H_ + +#include "base/ref_counted.h" + +class FilePath; +class SkBitmap; + +// The views need to load their icons asynchronously but might be deleted before +// the images have loaded. This class stays alive while the request is in +// progress (manages its own lifetime) and keeps track of whether the view still +// cares about the icon loading. +class ImageLoadingTracker + : public base::RefCountedThreadSafe<ImageLoadingTracker> { + public: + class Observer { + public: + // Will be called when the image with the given index has loaded. + // The |image| is owned by the tracker, so the observer should make a copy + // if they need to access it after this call. + virtual void OnImageLoaded(SkBitmap* image, size_t index) = 0; + }; + + ImageLoadingTracker(Observer* observer, size_t image_count) + : observer_(observer), image_count_(image_count), posted_count_(0) { + AddRef(); // We hold on to a reference to ourself to make sure we don't + // get deleted until we get a response from image loading (see + // ImageLoadingDone). + } + ~ImageLoadingTracker() {} + + // If there are remaining images to be loaded, the observing object should + // call this method on its destruction, so that the tracker will not attempt + // to make any more callbacks to it. + void StopTrackingImageLoad() { + observer_ = NULL; + } + + // Specify path of image to load. This method must be called a number of + // times equal to the |image_count| arugment to the constructor. Calling it + // any more or less than that is an error. + void PostLoadImageTask(FilePath path); + + private: + class LoadImageTask; + + // When an image has finished loaded and scaled on the file thread, it is + // posted back to this method on the original thread. This method then calls + // the observer's OnImageLoaded and deletes the ImageLoadingTracker if it was + // the last image in the list. + // |image| may be null if the file failed to decode. + void OnImageLoaded(SkBitmap* image, size_t index); + + // The view that is waiting for the image to load. + Observer* observer_; + + // The number of images this ImageTracker should keep track of. This is + // decremented as each image finishes loading, and the tracker will delete + // itself when it reaches zero. + size_t image_count_; + + // The number of tasks that have been posted so far. + size_t posted_count_; + + DISALLOW_COPY_AND_ASSIGN(ImageLoadingTracker); +}; + +#endif // CHROME_BROWSER_IMAGE_LOADING_TRACKER_H_ diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc index efe3cef..dcaa2d1 100644 --- a/chrome/browser/views/location_bar_view.cc +++ b/chrome/browser/views/location_bar_view.cc @@ -12,7 +12,6 @@ #include "app/gfx/canvas.h" #include "app/gfx/color_utils.h" -#include "app/gfx/favicon_size.h" #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/file_util.h" @@ -23,7 +22,6 @@ #include "chrome/browser/alternate_nav_url_fetcher.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" -#include "chrome/browser/browser_process.h" #include "chrome/browser/command_updater.h" #include "chrome/browser/extensions/extension_browser_event_router.h" #include "chrome/browser/extensions/extension_tabs_module.h" @@ -39,11 +37,9 @@ #include "chrome/common/page_action.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" -#include "skia/ext/image_operations.h" #include "views/focus/focus_manager.h" #include "views/widget/root_view.h" #include "views/widget/widget.h" -#include "webkit/glue/image_decoder.h" #if defined(OS_WIN) #include "app/win_util.h" @@ -657,29 +653,10 @@ void LocationBarView::DeletePageActionViews() { } } -std::vector<PageAction*> LocationBarView::GetPageActions() { - std::vector<PageAction*> result; - if (!profile_->GetExtensionsService()) - return result; - - // Query the extension system to see how many page actions we have. - // TODO(finnur): Sort the page icons in some meaningful way. - const ExtensionList* extensions = - profile_->GetExtensionsService()->extensions(); - for (ExtensionList::const_iterator iter = extensions->begin(); - iter != extensions->end(); ++iter) { - const PageActionMap& page_actions = (*iter)->page_actions(); - for (PageActionMap::const_iterator i(page_actions.begin()); - i != page_actions.end(); ++i) { - result.push_back(i->second); - } - } - - return result; -} - void LocationBarView::RefreshPageActionViews() { - std::vector<PageAction*> page_actions = GetPageActions(); + std::vector<PageAction*> page_actions; + if (profile_->GetExtensionsService()) + page_actions = profile_->GetExtensionsService()->GetPageActions(); // On startup we sometimes haven't loaded any extensions. This makes sure // we catch up when the extensions (and any page actions) load. @@ -1208,119 +1185,6 @@ void LocationBarView::SecurityImageView::ShowInfoBubble() { // PageActionImageView---------------------------------------------------------- -// The views need to load their icons asynchronously but might be deleted before -// the images have loaded. This class stays alive while the request is in -// progress (manages its own lifetime) and keeps track of whether the view still -// cares about the icon loading. -class LocationBarView::PageActionImageView::ImageLoadingTracker - : public base::RefCountedThreadSafe<ImageLoadingTracker> { - public: - explicit ImageLoadingTracker(PageActionImageView* view, int image_count) - : view_(view), image_count_(image_count) { - AddRef(); // We hold on to a reference to ourself to make sure we don't - // get deleted until we get a response from image loading (see - // ImageLoadingDone). - } - ~ImageLoadingTracker() {} - - void StopTrackingImageLoad() { - view_ = NULL; - } - - void OnImageLoaded(SkBitmap* image, int index) { - if (image == NULL) { - NOTREACHED() << "Image failed to decode."; - image = new SkBitmap(); - } - if (view_) - view_->OnImageLoaded(image, index); - delete image; - if (--image_count_ == 0) - Release(); // We are no longer needed. - } - - private: - - // The view that is waiting for the image to load. - PageActionImageView* view_; - - // The number of images this ImageTracker should keep track of. - int image_count_; -}; - -// The LoadImageTask is for asynchronously loading the image on the file thread. -// If the image is successfully loaded and decoded it will report back on the -// |callback_loop| to let the caller know the image is done loading. -class LocationBarView::PageActionImageView::LoadImageTask : public Task { - public: - // Constructor for the LoadImageTask class. |tracker| is the object that - // we use to communicate back to the entity that wants the image after we - // decode it. |path| is the path to load the image from. |index| is an - // identifier for the image that we pass back to the caller. - LoadImageTask(ImageLoadingTracker* tracker, - const FilePath& path, - int index) - : callback_loop_(MessageLoop::current()), - tracker_(tracker), - path_(path), - index_(index) {} - - void ReportBack(SkBitmap* image) { - DCHECK(image); - callback_loop_->PostTask(FROM_HERE, NewRunnableMethod(tracker_, - &PageActionImageView::ImageLoadingTracker::OnImageLoaded, - image, - index_)); - } - - virtual void Run() { - // Read the file from disk. - std::string file_contents; - if (!file_util::PathExists(path_) || - !file_util::ReadFileToString(path_, &file_contents)) { - ReportBack(NULL); - return; - } - - // Decode the image using WebKit's image decoder. - const unsigned char* data = - reinterpret_cast<const unsigned char*>(file_contents.data()); - webkit_glue::ImageDecoder decoder(gfx::Size(kFavIconSize, kFavIconSize)); - scoped_ptr<SkBitmap> decoded(new SkBitmap()); - *decoded = decoder.Decode(data, file_contents.length()); - if (decoded->empty()) { - ReportBack(NULL); - return; // Unable to decode. - } - - if (decoded->width() != kFavIconSize || decoded->height() != kFavIconSize) { - // The bitmap is not the correct size, re-sample. - int new_width = decoded->width(); - int new_height = decoded->height(); - // Calculate what dimensions to use within the constraints (16x16 max). - calc_favicon_target_size(&new_width, &new_height); - *decoded = skia::ImageOperations::Resize( - *decoded, skia::ImageOperations::RESIZE_LANCZOS3, - new_width, new_height); - } - - ReportBack(decoded.release()); - } - - private: - // The message loop that we need to call back on to report that we are done. - MessageLoop* callback_loop_; - - // The object that is waiting for us to respond back. - ImageLoadingTracker* tracker_; - - // The path to the image to load asynchronously. - FilePath path_; - - // The index of the icon being loaded. - int index_; -}; - LocationBarView::PageActionImageView::PageActionImageView( LocationBarView* owner, Profile* profile, @@ -1341,13 +1205,11 @@ LocationBarView::PageActionImageView::PageActionImageView( DCHECK(!page_action->icon_paths().empty()); const std::vector<std::string>& icon_paths = page_action->icon_paths(); page_action_icons_.resize(icon_paths.size()); - int index = 0; - MessageLoop* file_loop = g_browser_process->file_thread()->message_loop(); tracker_ = new ImageLoadingTracker(this, icon_paths.size()); for (std::vector<std::string>::const_iterator iter = icon_paths.begin(); iter != icon_paths.end(); ++iter) { FilePath path = extension->GetResourcePath(*iter); - file_loop->PostTask(FROM_HERE, new LoadImageTask(tracker_, path, index++)); + tracker_->PostLoadImageTask(path); } } @@ -1369,6 +1231,15 @@ void LocationBarView::PageActionImageView::ShowInfoBubble() { ShowInfoBubbleImpl(ASCIIToWide(tooltip_), GetColor(false, TEXT)); } +void LocationBarView::PageActionImageView::OnImageLoaded(SkBitmap* image, + size_t index) { + DCHECK(index < page_action_icons_.size()); + if (index == page_action_icons_.size() - 1) + tracker_ = NULL; // The tracker object will delete itself when we return. + page_action_icons_[index] = *image; + owner_->UpdatePageActions(); +} + void LocationBarView::PageActionImageView::UpdateVisibility( TabContents* contents, GURL url) { // Save this off so we can pass it back to the extension when the action gets @@ -1394,15 +1265,6 @@ void LocationBarView::PageActionImageView::UpdateVisibility( SetVisible(visible); } -void LocationBarView::PageActionImageView::OnImageLoaded(SkBitmap* image, - size_t index) { - DCHECK(index < page_action_icons_.size()); - if (index == page_action_icons_.size() - 1) - tracker_ = NULL; // The tracker object will delete itself when we return. - page_action_icons_[index] = *image; - owner_->UpdatePageActions(); -} - //////////////////////////////////////////////////////////////////////////////// // LocationBarView, LocationBar implementation: diff --git a/chrome/browser/views/location_bar_view.h b/chrome/browser/views/location_bar_view.h index 919821f..3fdfcdc 100644 --- a/chrome/browser/views/location_bar_view.h +++ b/chrome/browser/views/location_bar_view.h @@ -11,6 +11,7 @@ #include "app/gfx/font.h" #include "base/gfx/rect.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" +#include "chrome/browser/image_loading_tracker.h" #include "chrome/browser/location_bar.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/toolbar_model.h" @@ -333,7 +334,8 @@ class LocationBarView : public LocationBar, // PageActionImageView is used to display the icon for a given PageAction // and notify the extension when the icon is clicked. - class PageActionImageView : public LocationBarImageView { + class PageActionImageView : public LocationBarImageView, + public ImageLoadingTracker::Observer { public: PageActionImageView( LocationBarView* owner, Profile* profile, @@ -346,20 +348,15 @@ class LocationBarView : public LocationBar, // Overridden from LocationBarImageView. virtual void ShowInfoBubble(); + // Overridden from ImageLoadingTracker. + virtual void OnImageLoaded(SkBitmap* image, size_t index); + // Called to notify the PageAction that it should determine whether to be // visible or hidden. |contents| is the TabContents that is active, |url| // is the current page URL. void UpdateVisibility(TabContents* contents, GURL url); - // A callback for when the image has loaded. - void OnImageLoaded(SkBitmap* image, size_t index); - private: - // We load the images for the PageActions on the file thread. These tasks - // help with that. - class LoadImageTask; - class ImageLoadingTracker; - // The location bar view that owns us. LocationBarView* owner_; @@ -431,10 +428,6 @@ class LocationBarView : public LocationBar, // Delete all page action views that we have created. void DeletePageActionViews(); - // Retrieves a vector of all page actions, irrespective of which - // extension they belong to. - std::vector<PageAction*> GetPageActions(); - // Update the views for the Page Actions, to reflect state changes for // PageActions. void RefreshPageActionViews(); |