| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/gtk/browser_actions_toolbar_gtk.h" | 5 #include "chrome/browser/gtk/browser_actions_toolbar_gtk.h" |
| 6 | 6 |
| 7 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "app/gfx/canvas_paint.h" | 10 #include "app/gfx/canvas_paint.h" |
| 11 #include "app/gfx/gtk_util.h" | 11 #include "app/gfx/gtk_util.h" |
| 12 #include "chrome/browser/browser.h" | 12 #include "chrome/browser/browser.h" |
| 13 #include "chrome/browser/extensions/extension_browser_event_router.h" | 13 #include "chrome/browser/extensions/extension_browser_event_router.h" |
| 14 #include "chrome/browser/extensions/extensions_service.h" | 14 #include "chrome/browser/extensions/extensions_service.h" |
| 15 #include "chrome/browser/extensions/image_loading_tracker.h" | 15 #include "chrome/browser/extensions/image_loading_tracker.h" |
| 16 #include "chrome/browser/gtk/extension_popup_gtk.h" | 16 #include "chrome/browser/gtk/extension_popup_gtk.h" |
| 17 #include "chrome/browser/gtk/gtk_chrome_button.h" | 17 #include "chrome/browser/gtk/gtk_chrome_button.h" |
| 18 #include "chrome/browser/gtk/gtk_theme_provider.h" | 18 #include "chrome/browser/gtk/gtk_theme_provider.h" |
| 19 #include "chrome/browser/profile.h" | 19 #include "chrome/browser/profile.h" |
| 20 #include "chrome/browser/tab_contents/tab_contents.h" |
| 20 #include "chrome/common/extensions/extension.h" | 21 #include "chrome/common/extensions/extension.h" |
| 22 #include "chrome/common/extensions/extension_action2.h" |
| 21 #include "chrome/common/notification_details.h" | 23 #include "chrome/common/notification_details.h" |
| 22 #include "chrome/common/notification_service.h" | 24 #include "chrome/common/notification_service.h" |
| 23 #include "chrome/common/notification_source.h" | 25 #include "chrome/common/notification_source.h" |
| 24 #include "chrome/common/notification_type.h" | 26 #include "chrome/common/notification_type.h" |
| 25 | 27 |
| 26 // The size of each button on the toolbar. | 28 // The size of each button on the toolbar. |
| 27 static const int kButtonSize = 29; | 29 static const int kButtonSize = 29; |
| 28 | 30 |
| 29 class BrowserActionButton : public NotificationObserver, | 31 class BrowserActionButton : public NotificationObserver, |
| 30 public ImageLoadingTracker::Observer { | 32 public ImageLoadingTracker::Observer { |
| 31 public: | 33 public: |
| 32 BrowserActionButton(Browser* browser, Extension* extension) | 34 BrowserActionButton(BrowserActionsToolbarGtk* toolbar, |
| 33 : browser_(browser), | 35 Extension* extension) |
| 36 : toolbar_(toolbar), |
| 34 extension_(extension), | 37 extension_(extension), |
| 35 button_(gtk_chrome_button_new()), | 38 button_(gtk_chrome_button_new()), |
| 36 gdk_icon_(NULL) { | 39 tracker_(NULL), |
| 40 tab_specific_icon_(NULL), |
| 41 default_icon_(NULL) { |
| 37 DCHECK(extension_->browser_action()); | 42 DCHECK(extension_->browser_action()); |
| 38 | 43 |
| 39 gtk_widget_set_size_request(button_.get(), kButtonSize, kButtonSize); | 44 gtk_widget_set_size_request(button_.get(), kButtonSize, kButtonSize); |
| 40 | 45 |
| 41 browser_action_icons_.resize( | 46 UpdateState(); |
| 42 extension->browser_action()->icon_paths().size()); | 47 |
| 43 tracker_ = new ImageLoadingTracker(this, browser_action_icons_.size()); | 48 // The Browser Action API does not allow the default icon path to be |
| 44 for (size_t i = 0; i < extension->browser_action()->icon_paths().size(); | 49 // changed at runtime, so we can load this now and cache it. |
| 45 ++i) { | 50 std::string path = extension_->browser_action()->GetDefaultIconPath(); |
| 46 tracker_->PostLoadImageTask( | 51 if (!path.empty()) { |
| 47 extension->GetResource(extension->browser_action()->icon_paths()[i]), | 52 tracker_ = new ImageLoadingTracker(this, 1); |
| 53 tracker_->PostLoadImageTask(extension_->GetResource(path), |
| 48 gfx::Size(Extension::kBrowserActionIconMaxSize, | 54 gfx::Size(Extension::kBrowserActionIconMaxSize, |
| 49 Extension::kBrowserActionIconMaxSize)); | 55 Extension::kBrowserActionIconMaxSize)); |
| 50 } | 56 } |
| 51 | 57 |
| 52 OnStateUpdated(); | |
| 53 | |
| 54 // We need to hook up extension popups here. http://crbug.com/23897 | 58 // We need to hook up extension popups here. http://crbug.com/23897 |
| 55 g_signal_connect(button_.get(), "clicked", | 59 g_signal_connect(button_.get(), "clicked", |
| 56 G_CALLBACK(OnButtonClicked), this); | 60 G_CALLBACK(OnButtonClicked), this); |
| 57 g_signal_connect_after(button_.get(), "expose-event", | 61 g_signal_connect_after(button_.get(), "expose-event", |
| 58 G_CALLBACK(OnExposeEvent), this); | 62 G_CALLBACK(OnExposeEvent), this); |
| 59 | 63 |
| 60 registrar_.Add(this, NotificationType::EXTENSION_BROWSER_ACTION_UPDATED, | 64 registrar_.Add(this, NotificationType::EXTENSION_BROWSER_ACTION_UPDATED, |
| 61 Source<ExtensionAction>(extension->browser_action())); | 65 Source<ExtensionAction2>(extension->browser_action())); |
| 62 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, | 66 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, |
| 63 NotificationService::AllSources()); | 67 NotificationService::AllSources()); |
| 64 | 68 |
| 65 OnThemeChanged(); | 69 OnThemeChanged(); |
| 66 } | 70 } |
| 67 | 71 |
| 68 ~BrowserActionButton() { | 72 ~BrowserActionButton() { |
| 69 if (gdk_icon_) | 73 if (tab_specific_icon_) |
| 70 g_object_unref(gdk_icon_); | 74 g_object_unref(tab_specific_icon_); |
| 75 |
| 76 if (default_icon_) |
| 77 g_object_unref(default_icon_); |
| 71 | 78 |
| 72 button_.Destroy(); | 79 button_.Destroy(); |
| 73 tracker_->StopTrackingImageLoad(); | 80 |
| 81 if (tracker_) |
| 82 tracker_->StopTrackingImageLoad(); |
| 74 } | 83 } |
| 75 | 84 |
| 76 GtkWidget* widget() { return button_.get(); } | 85 GtkWidget* widget() { return button_.get(); } |
| 77 | 86 |
| 78 void Observe(NotificationType type, | 87 void Observe(NotificationType type, |
| 79 const NotificationSource& source, | 88 const NotificationSource& source, |
| 80 const NotificationDetails& details) { | 89 const NotificationDetails& details) { |
| 81 if (type == NotificationType::EXTENSION_BROWSER_ACTION_UPDATED) | 90 if (type == NotificationType::EXTENSION_BROWSER_ACTION_UPDATED) |
| 82 OnStateUpdated(); | 91 UpdateState(); |
| 83 else if (type == NotificationType::BROWSER_THEME_CHANGED) | 92 else if (type == NotificationType::BROWSER_THEME_CHANGED) |
| 84 OnThemeChanged(); | 93 OnThemeChanged(); |
| 85 else | 94 else |
| 86 NOTREACHED(); | 95 NOTREACHED(); |
| 87 } | 96 } |
| 88 | 97 |
| 89 // ImageLoadingTracker::Observer implementation. | 98 // ImageLoadingTracker::Observer implementation. |
| 90 void OnImageLoaded(SkBitmap* image, size_t index) { | 99 void OnImageLoaded(SkBitmap* image, size_t index) { |
| 91 DCHECK(index < browser_action_icons_.size()); | 100 default_icon_ = gfx::GdkPixbufFromSkBitmap(image); |
| 92 browser_action_icons_[index] = image ? *image : SkBitmap(); | 101 UpdateState(); |
| 102 } |
| 93 | 103 |
| 94 OnStateUpdated(); | 104 // Updates the button based on the latest state from the associated |
| 105 // browser action. |
| 106 void UpdateState() { |
| 107 int tab_id = toolbar_->GetCurrentTabId(); |
| 108 if (tab_id < 0) |
| 109 return; |
| 110 |
| 111 gtk_widget_set_tooltip_text(button_.get(), |
| 112 extension_->browser_action()->GetTitle(tab_id).c_str()); |
| 113 |
| 114 SkBitmap image = extension_->browser_action()->GetIcon(tab_id); |
| 115 if (!image.isNull()) { |
| 116 GdkPixbuf* previous_gdk_icon = tab_specific_icon_; |
| 117 tab_specific_icon_ = gfx::GdkPixbufFromSkBitmap(&image); |
| 118 SetImage(tab_specific_icon_); |
| 119 if (previous_gdk_icon) |
| 120 g_object_unref(previous_gdk_icon); |
| 121 } else if (default_icon_) { |
| 122 SetImage(default_icon_); |
| 123 } |
| 95 } | 124 } |
| 96 | 125 |
| 97 private: | 126 private: |
| 98 // Called when the tooltip has changed or an image has loaded. | 127 void SetImage(GdkPixbuf* image) { |
| 99 void OnStateUpdated() { | 128 gtk_button_set_image(GTK_BUTTON(button_.get()), |
| 100 gtk_widget_set_tooltip_text(button_.get(), | 129 gtk_image_new_from_pixbuf(image)); |
| 101 extension_->browser_action_state()->title().c_str()); | |
| 102 | |
| 103 SkBitmap* image = extension_->browser_action_state()->icon(); | |
| 104 if (!image) { | |
| 105 if (static_cast<size_t>( | |
| 106 extension_->browser_action_state()->icon_index()) < | |
| 107 browser_action_icons_.size()) { | |
| 108 image = &browser_action_icons_[ | |
| 109 extension_->browser_action_state()->icon_index()]; | |
| 110 } | |
| 111 } | |
| 112 | |
| 113 if (image && !image->empty()) { | |
| 114 GdkPixbuf* current_gdk_icon = gdk_icon_; | |
| 115 gdk_icon_ = gfx::GdkPixbufFromSkBitmap(image); | |
| 116 gtk_button_set_image(GTK_BUTTON(button_.get()), | |
| 117 gtk_image_new_from_pixbuf(gdk_icon_)); | |
| 118 if (current_gdk_icon) | |
| 119 g_object_unref(current_gdk_icon); | |
| 120 } | |
| 121 } | 130 } |
| 122 | 131 |
| 123 void OnThemeChanged() { | 132 void OnThemeChanged() { |
| 124 gtk_chrome_button_set_use_gtk_rendering(GTK_CHROME_BUTTON(button_.get()), | 133 gtk_chrome_button_set_use_gtk_rendering(GTK_CHROME_BUTTON(button_.get()), |
| 125 GtkThemeProvider::GetFrom(browser_->profile())->UseGtkTheme()); | 134 GtkThemeProvider::GetFrom( |
| 135 toolbar_->browser()->profile())->UseGtkTheme()); |
| 126 } | 136 } |
| 127 | 137 |
| 128 static void OnButtonClicked(GtkWidget* widget, BrowserActionButton* action) { | 138 static void OnButtonClicked(GtkWidget* widget, BrowserActionButton* action) { |
| 129 if (action->extension_->browser_action()->is_popup()) { | 139 if (action->extension_->browser_action()->has_popup()) { |
| 130 ExtensionPopupGtk::Show(action->extension_->browser_action()->popup_url(), | 140 ExtensionPopupGtk::Show(action->extension_->browser_action()->popup_url(), |
| 131 action->browser_, gfx::Rect(widget->allocation)); | 141 action->toolbar_->browser(), |
| 132 | 142 gfx::Rect(widget->allocation)); |
| 133 } else { | 143 } else { |
| 134 ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted( | 144 ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted( |
| 135 action->browser_->profile(), action->extension_->id(), | 145 action->toolbar_->browser()->profile(), action->extension_->id(), |
| 136 action->browser_); | 146 action->toolbar_->browser()); |
| 137 } | 147 } |
| 138 } | 148 } |
| 139 | 149 |
| 140 static gboolean OnExposeEvent(GtkWidget* widget, | 150 static gboolean OnExposeEvent(GtkWidget* widget, |
| 141 GdkEventExpose* event, | 151 GdkEventExpose* event, |
| 142 BrowserActionButton* action) { | 152 BrowserActionButton* button) { |
| 143 if (action->extension_->browser_action_state()->badge_text().empty()) | 153 int tab_id = button->toolbar_->GetCurrentTabId(); |
| 154 if (tab_id < 0) |
| 155 return FALSE; |
| 156 |
| 157 ExtensionAction2* action = button->extension_->browser_action(); |
| 158 if (action->GetBadgeText(tab_id).empty()) |
| 144 return FALSE; | 159 return FALSE; |
| 145 | 160 |
| 146 gfx::CanvasPaint canvas(event, false); | 161 gfx::CanvasPaint canvas(event, false); |
| 147 gfx::Rect bounding_rect(widget->allocation); | 162 gfx::Rect bounding_rect(widget->allocation); |
| 148 action->extension_->browser_action_state()->PaintBadge(&canvas, | 163 ExtensionActionState::PaintBadge(&canvas, bounding_rect, |
| 149 bounding_rect); | 164 action->GetBadgeText(tab_id), |
| 165 action->GetBadgeTextColor(tab_id), |
| 166 action->GetBadgeBackgroundColor(tab_id)); |
| 150 return FALSE; | 167 return FALSE; |
| 151 } | 168 } |
| 152 | 169 |
| 153 // The Browser that executes a command when the button is pressed. | 170 // The toolbar containing this button. |
| 154 Browser* browser_; | 171 BrowserActionsToolbarGtk* toolbar_; |
| 155 | 172 |
| 156 // The extension that contains this browser action. | 173 // The extension that contains this browser action. |
| 157 Extension* extension_; | 174 Extension* extension_; |
| 158 | 175 |
| 159 // The gtk widget for this browser action. | 176 // The gtk widget for this browser action. |
| 160 OwnedWidgetGtk button_; | 177 OwnedWidgetGtk button_; |
| 161 | 178 |
| 162 // Loads the button's icons for us on the file thread. | 179 // Loads the button's icons for us on the file thread. |
| 163 ImageLoadingTracker* tracker_; | 180 ImageLoadingTracker* tracker_; |
| 164 | 181 |
| 165 // Icons for all the different states the button can be in. These will be | 182 // If we are displaying a tab-specific icon, it will be here. |
| 166 // empty while they are loading. | 183 GdkPixbuf* tab_specific_icon_; |
| 167 std::vector<SkBitmap> browser_action_icons_; | |
| 168 | 184 |
| 169 // SkBitmap must be converted to GdkPixbuf before assignment to the button. | 185 // If the browser action has a default icon, it will be here. |
| 170 // This stores the current icon while it is in use. | 186 GdkPixbuf* default_icon_; |
| 171 GdkPixbuf* gdk_icon_; | |
| 172 | 187 |
| 173 NotificationRegistrar registrar_; | 188 NotificationRegistrar registrar_; |
| 174 }; | 189 }; |
| 175 | 190 |
| 176 BrowserActionsToolbarGtk::BrowserActionsToolbarGtk(Browser* browser) | 191 BrowserActionsToolbarGtk::BrowserActionsToolbarGtk(Browser* browser) |
| 177 : browser_(browser), | 192 : browser_(browser), |
| 178 profile_(browser->profile()), | 193 profile_(browser->profile()), |
| 179 hbox_(gtk_hbox_new(0, FALSE)) { | 194 hbox_(gtk_hbox_new(0, FALSE)) { |
| 180 ExtensionsService* extension_service = profile_->GetExtensionsService(); | 195 ExtensionsService* extension_service = profile_->GetExtensionsService(); |
| 181 registrar_.Add(this, NotificationType::EXTENSION_LOADED, | 196 registrar_.Add(this, NotificationType::EXTENSION_LOADED, |
| 182 Source<ExtensionsService>(extension_service)); | 197 Source<ExtensionsService>(extension_service)); |
| 183 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, | 198 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, |
| 184 Source<ExtensionsService>(extension_service)); | 199 Source<ExtensionsService>(extension_service)); |
| 185 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED, | 200 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED, |
| 186 Source<ExtensionsService>(extension_service)); | 201 Source<ExtensionsService>(extension_service)); |
| 187 | 202 |
| 188 CreateAllButtons(); | 203 CreateAllButtons(); |
| 189 } | 204 } |
| 190 | 205 |
| 191 BrowserActionsToolbarGtk::~BrowserActionsToolbarGtk() { | 206 BrowserActionsToolbarGtk::~BrowserActionsToolbarGtk() { |
| 192 hbox_.Destroy(); | 207 hbox_.Destroy(); |
| 193 } | 208 } |
| 194 | 209 |
| 210 int BrowserActionsToolbarGtk::GetCurrentTabId() { |
| 211 TabContents* selected_tab = browser_->GetSelectedTabContents(); |
| 212 if (!selected_tab) |
| 213 return -1; |
| 214 |
| 215 return selected_tab->controller().session_id().id(); |
| 216 } |
| 217 |
| 218 void BrowserActionsToolbarGtk::Update() { |
| 219 for (ExtensionButtonMap::iterator iter = extension_button_map_.begin(); |
| 220 iter != extension_button_map_.end(); ++iter) { |
| 221 iter->second->UpdateState(); |
| 222 } |
| 223 } |
| 224 |
| 195 void BrowserActionsToolbarGtk::Observe(NotificationType type, | 225 void BrowserActionsToolbarGtk::Observe(NotificationType type, |
| 196 const NotificationSource& source, | 226 const NotificationSource& source, |
| 197 const NotificationDetails& details) { | 227 const NotificationDetails& details) { |
| 198 Extension* extension = Details<Extension>(details).ptr(); | 228 Extension* extension = Details<Extension>(details).ptr(); |
| 199 | 229 |
| 200 if (type == NotificationType::EXTENSION_LOADED) { | 230 if (type == NotificationType::EXTENSION_LOADED) { |
| 201 CreateButtonForExtension(extension); | 231 CreateButtonForExtension(extension); |
| 202 } else if (type == NotificationType::EXTENSION_UNLOADED || | 232 } else if (type == NotificationType::EXTENSION_UNLOADED || |
| 203 type == NotificationType::EXTENSION_UNLOADED_DISABLED) { | 233 type == NotificationType::EXTENSION_UNLOADED_DISABLED) { |
| 204 RemoveButtonForExtension(extension); | 234 RemoveButtonForExtension(extension); |
| 205 } else { | 235 } else { |
| 206 NOTREACHED() << "Received unexpected notification"; | 236 NOTREACHED() << "Received unexpected notification"; |
| 207 } | 237 } |
| 208 } | 238 } |
| 209 | 239 |
| 210 void BrowserActionsToolbarGtk::CreateAllButtons() { | 240 void BrowserActionsToolbarGtk::CreateAllButtons() { |
| 211 ExtensionsService* extension_service = profile_->GetExtensionsService(); | 241 ExtensionsService* extension_service = profile_->GetExtensionsService(); |
| 212 if (!extension_service) // The |extension_service| can be NULL in Incognito. | 242 if (!extension_service) // The |extension_service| can be NULL in Incognito. |
| 213 return; | 243 return; |
| 214 | 244 |
| 215 // Get all browser actions, including those with popups. | 245 for (size_t i = 0; i < extension_service->extensions()->size(); ++i) { |
| 216 std::vector<ExtensionAction*> browser_actions = | |
| 217 extension_service->GetBrowserActions(true); | |
| 218 | |
| 219 for (size_t i = 0; i < browser_actions.size(); ++i) { | |
| 220 Extension* extension = extension_service->GetExtensionById( | 246 Extension* extension = extension_service->GetExtensionById( |
| 221 browser_actions[i]->extension_id()); | 247 extension_service->extensions()->at(i)->id()); |
| 222 CreateButtonForExtension(extension); | 248 CreateButtonForExtension(extension); |
| 223 } | 249 } |
| 224 } | 250 } |
| 225 | 251 |
| 226 void BrowserActionsToolbarGtk::CreateButtonForExtension(Extension* extension) { | 252 void BrowserActionsToolbarGtk::CreateButtonForExtension(Extension* extension) { |
| 227 // Only show extensions with browser actions and that have an icon. | 253 // Only show extensions with browser actions. |
| 228 if (!extension->browser_action() || | 254 if (!extension->browser_action()) |
| 229 extension->browser_action()->icon_paths().empty()) { | |
| 230 return; | 255 return; |
| 231 } | |
| 232 | 256 |
| 233 RemoveButtonForExtension(extension); | 257 RemoveButtonForExtension(extension); |
| 234 linked_ptr<BrowserActionButton> button( | 258 linked_ptr<BrowserActionButton> button( |
| 235 new BrowserActionButton(browser_, extension)); | 259 new BrowserActionButton(this, extension)); |
| 236 gtk_box_pack_end(GTK_BOX(hbox_.get()), button->widget(), FALSE, FALSE, 0); | 260 gtk_box_pack_end(GTK_BOX(hbox_.get()), button->widget(), FALSE, FALSE, 0); |
| 237 gtk_widget_show(button->widget()); | 261 gtk_widget_show(button->widget()); |
| 238 extension_button_map_[extension->id()] = button; | 262 extension_button_map_[extension->id()] = button; |
| 239 | 263 |
| 240 UpdateVisibility(); | 264 UpdateVisibility(); |
| 241 } | 265 } |
| 242 | 266 |
| 243 void BrowserActionsToolbarGtk::RemoveButtonForExtension(Extension* extension) { | 267 void BrowserActionsToolbarGtk::RemoveButtonForExtension(Extension* extension) { |
| 244 if (extension_button_map_.erase(extension->id())) | 268 if (extension_button_map_.erase(extension->id())) |
| 245 UpdateVisibility(); | 269 UpdateVisibility(); |
| 246 } | 270 } |
| 247 | 271 |
| 248 void BrowserActionsToolbarGtk::UpdateVisibility() { | 272 void BrowserActionsToolbarGtk::UpdateVisibility() { |
| 249 if (button_count() == 0) | 273 if (button_count() == 0) |
| 250 gtk_widget_hide(widget()); | 274 gtk_widget_hide(widget()); |
| 251 else | 275 else |
| 252 gtk_widget_show(widget()); | 276 gtk_widget_show(widget()); |
| 253 } | 277 } |
| OLD | NEW |