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 |