summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authormattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-10 22:25:16 +0000
committermattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-10 22:25:16 +0000
commit10fb8126df079ccf47a308f2b5a40ed01a8efda0 (patch)
tree1d6c4d7b0fcb33e73024ede12eee97b1b57077a6 /chrome/browser
parent64fe65afcd193bea778490faa2d562bc96430502 (diff)
downloadchromium_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.cc4
-rw-r--r--chrome/browser/extensions/extensions_service.cc16
-rw-r--r--chrome/browser/extensions/extensions_service.h4
-rw-r--r--chrome/browser/gtk/browser_toolbar_gtk.cc3
-rw-r--r--chrome/browser/gtk/location_bar_view_gtk.cc159
-rw-r--r--chrome/browser/gtk/location_bar_view_gtk.h64
-rw-r--r--chrome/browser/image_loading_tracker.cc114
-rw-r--r--chrome/browser/image_loading_tracker.h72
-rw-r--r--chrome/browser/views/location_bar_view.cc164
-rw-r--r--chrome/browser/views/location_bar_view.h19
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();