1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/api/system_indicator/system_indicator_manager.h"
6
7 #include "base/memory/linked_ptr.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/extensions/extension_action.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/status_icons/status_icon.h"
14 #include "chrome/browser/status_icons/status_icon_observer.h"
15 #include "chrome/browser/status_icons/status_tray.h"
16 #include "chrome/common/extensions/api/system_indicator.h"
17 #include "content/public/browser/notification_details.h"
18 #include "content/public/browser/notification_source.h"
19 #include "extensions/browser/event_router.h"
20 #include "extensions/browser/extension_registry.h"
21 #include "extensions/browser/extension_system.h"
22 #include "extensions/common/extension.h"
23 #include "ui/gfx/image/image.h"
24
25 namespace extensions {
26
27 namespace system_indicator = api::system_indicator;
28
29 // Observes clicks on a given status icon and forwards the event to the
30 // appropriate extension. Handles icon updates, and responsible for creating
31 // and removing the icon from the notification area during construction and
32 // destruction.
33 class ExtensionIndicatorIcon : public StatusIconObserver,
34 public ExtensionActionIconFactory::Observer {
35 public:
36 static ExtensionIndicatorIcon* Create(const Extension* extension,
37 const ExtensionAction* action,
38 Profile* profile,
39 StatusTray* status_tray);
40 virtual ~ExtensionIndicatorIcon();
41
42 // StatusIconObserver implementation.
43 virtual void OnStatusIconClicked() OVERRIDE;
44
45 // ExtensionActionIconFactory::Observer implementation.
46 virtual void OnIconUpdated() OVERRIDE;
47
48 private:
49 ExtensionIndicatorIcon(const Extension* extension,
50 const ExtensionAction* action,
51 Profile* profile,
52 StatusTray* status_tray);
53
54 const extensions::Extension* extension_;
55 StatusTray* status_tray_;
56 StatusIcon* icon_;
57 Profile* profile_;
58 ExtensionActionIconFactory icon_factory_;
59 };
60
Create(const Extension * extension,const ExtensionAction * action,Profile * profile,StatusTray * status_tray)61 ExtensionIndicatorIcon* ExtensionIndicatorIcon::Create(
62 const Extension* extension,
63 const ExtensionAction* action,
64 Profile* profile,
65 StatusTray* status_tray) {
66 scoped_ptr<ExtensionIndicatorIcon> extension_icon(
67 new ExtensionIndicatorIcon(extension, action, profile, status_tray));
68
69 // Check if a status icon was successfully created.
70 if (extension_icon->icon_)
71 return extension_icon.release();
72
73 // We could not create a status icon.
74 return NULL;
75 }
76
~ExtensionIndicatorIcon()77 ExtensionIndicatorIcon::~ExtensionIndicatorIcon() {
78 if (icon_) {
79 icon_->RemoveObserver(this);
80 status_tray_->RemoveStatusIcon(icon_);
81 }
82 }
83
OnStatusIconClicked()84 void ExtensionIndicatorIcon::OnStatusIconClicked() {
85 scoped_ptr<base::ListValue> params(
86 api::system_indicator::OnClicked::Create());
87
88 EventRouter* event_router = EventRouter::Get(profile_);
89 scoped_ptr<Event> event(new Event(
90 system_indicator::OnClicked::kEventName,
91 params.Pass(),
92 profile_));
93 event_router->DispatchEventToExtension(
94 extension_->id(), event.Pass());
95 }
96
OnIconUpdated()97 void ExtensionIndicatorIcon::OnIconUpdated() {
98 icon_->SetImage(
99 icon_factory_.GetIcon(ExtensionAction::kDefaultTabId).AsImageSkia());
100 }
101
ExtensionIndicatorIcon(const Extension * extension,const ExtensionAction * action,Profile * profile,StatusTray * status_tray)102 ExtensionIndicatorIcon::ExtensionIndicatorIcon(const Extension* extension,
103 const ExtensionAction* action,
104 Profile* profile,
105 StatusTray* status_tray)
106 : extension_(extension),
107 status_tray_(status_tray),
108 icon_(NULL),
109 profile_(profile),
110 icon_factory_(profile, extension, action, this) {
111 // Get the icon image and tool tip for the status icon. The extension name is
112 // used as the tool tip.
113 gfx::ImageSkia icon_image =
114 icon_factory_.GetIcon(ExtensionAction::kDefaultTabId).AsImageSkia();
115 base::string16 tool_tip = base::UTF8ToUTF16(extension_->name());
116
117 icon_ = status_tray_->CreateStatusIcon(
118 StatusTray::OTHER_ICON, icon_image, tool_tip);
119 if (icon_)
120 icon_->AddObserver(this);
121 }
122
SystemIndicatorManager(Profile * profile,StatusTray * status_tray)123 SystemIndicatorManager::SystemIndicatorManager(Profile* profile,
124 StatusTray* status_tray)
125 : profile_(profile),
126 status_tray_(status_tray),
127 extension_registry_observer_(this) {
128 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
129
130 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_SYSTEM_INDICATOR_UPDATED,
131 content::Source<Profile>(profile_->GetOriginalProfile()));
132 }
133
~SystemIndicatorManager()134 SystemIndicatorManager::~SystemIndicatorManager() {
135 DCHECK(thread_checker_.CalledOnValidThread());
136 }
137
Shutdown()138 void SystemIndicatorManager::Shutdown() {
139 DCHECK(thread_checker_.CalledOnValidThread());
140 }
141
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionInfo::Reason reason)142 void SystemIndicatorManager::OnExtensionUnloaded(
143 content::BrowserContext* browser_context,
144 const Extension* extension,
145 UnloadedExtensionInfo::Reason reason) {
146 RemoveIndicator(extension->id());
147 }
148
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)149 void SystemIndicatorManager::Observe(
150 int type,
151 const content::NotificationSource& source,
152 const content::NotificationDetails& details) {
153 DCHECK(thread_checker_.CalledOnValidThread());
154 DCHECK_EQ(type, chrome::NOTIFICATION_EXTENSION_SYSTEM_INDICATOR_UPDATED);
155
156 OnSystemIndicatorChanged(content::Details<ExtensionAction>(details).ptr());
157 }
158
OnSystemIndicatorChanged(const ExtensionAction * extension_action)159 void SystemIndicatorManager::OnSystemIndicatorChanged(
160 const ExtensionAction* extension_action) {
161 DCHECK(thread_checker_.CalledOnValidThread());
162 std::string extension_id = extension_action->extension_id();
163 ExtensionService* service =
164 ExtensionSystem::Get(profile_)->extension_service();
165
166 if (extension_action->GetIsVisible(ExtensionAction::kDefaultTabId)) {
167 const Extension* extension =
168 service->GetExtensionById(extension_id, false);
169 CreateOrUpdateIndicator(extension, extension_action);
170 } else {
171 RemoveIndicator(extension_id);
172 }
173 }
174
SendClickEventToExtensionForTest(const std::string extension_id)175 bool SystemIndicatorManager::SendClickEventToExtensionForTest(
176 const std::string extension_id) {
177
178 extensions::SystemIndicatorManager::SystemIndicatorMap::iterator it =
179 system_indicators_.find(extension_id);
180
181 if (it == system_indicators_.end())
182 return false;
183
184 it->second->OnStatusIconClicked();
185 return true;
186 }
187
CreateOrUpdateIndicator(const Extension * extension,const ExtensionAction * extension_action)188 void SystemIndicatorManager::CreateOrUpdateIndicator(
189 const Extension* extension,
190 const ExtensionAction* extension_action) {
191 DCHECK(thread_checker_.CalledOnValidThread());
192 SystemIndicatorMap::iterator it = system_indicators_.find(extension->id());
193 if (it != system_indicators_.end()) {
194 it->second->OnIconUpdated();
195 return;
196 }
197
198 ExtensionIndicatorIcon* extension_icon = ExtensionIndicatorIcon::Create(
199 extension, extension_action, profile_, status_tray_);
200 if (extension_icon)
201 system_indicators_[extension->id()] = make_linked_ptr(extension_icon);
202 }
203
RemoveIndicator(const std::string & extension_id)204 void SystemIndicatorManager::RemoveIndicator(const std::string& extension_id) {
205 DCHECK(thread_checker_.CalledOnValidThread());
206 system_indicators_.erase(extension_id);
207 }
208
209 } // namespace extensions
210