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