1// Copyright 2013 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 "extensions/browser/api/system_info/system_info_api.h"
6
7#include <set>
8
9#include "base/bind.h"
10#include "base/lazy_instance.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/memory/singleton.h"
13#include "base/strings/string_util.h"
14#include "base/values.h"
15#include "components/storage_monitor/removable_storage_observer.h"
16#include "components/storage_monitor/storage_info.h"
17#include "components/storage_monitor/storage_monitor.h"
18#include "content/public/browser/browser_thread.h"
19#include "extensions/browser/api/system_display/display_info_provider.h"
20#include "extensions/browser/api/system_storage/storage_info_provider.h"
21#include "extensions/browser/extensions_browser_client.h"
22#include "extensions/common/api/system_display.h"
23#include "extensions/common/api/system_storage.h"
24#include "ui/gfx/display_observer.h"
25#include "ui/gfx/screen.h"
26
27namespace extensions {
28
29using core_api::system_storage::StorageUnitInfo;
30using content::BrowserThread;
31using storage_monitor::StorageMonitor;
32
33namespace system_display = core_api::system_display;
34namespace system_storage = core_api::system_storage;
35
36namespace {
37
38bool IsDisplayChangedEvent(const std::string& event_name) {
39  return event_name == system_display::OnDisplayChanged::kEventName;
40}
41
42bool IsSystemStorageEvent(const std::string& event_name) {
43  return (event_name == system_storage::OnAttached::kEventName ||
44          event_name == system_storage::OnDetached::kEventName);
45}
46
47// Event router for systemInfo API. It is a singleton instance shared by
48// multiple profiles.
49class SystemInfoEventRouter : public gfx::DisplayObserver,
50                              public storage_monitor::RemovableStorageObserver {
51 public:
52  static SystemInfoEventRouter* GetInstance();
53
54  SystemInfoEventRouter();
55  virtual ~SystemInfoEventRouter();
56
57  // Add/remove event listener for the |event_name| event.
58  void AddEventListener(const std::string& event_name);
59  void RemoveEventListener(const std::string& event_name);
60
61 private:
62  // gfx::DisplayObserver:
63  virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE;
64  virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE;
65  virtual void OnDisplayMetricsChanged(const gfx::Display& display,
66                                       uint32_t metrics) OVERRIDE;
67
68  // RemovableStorageObserver implementation.
69  virtual void OnRemovableStorageAttached(
70      const storage_monitor::StorageInfo& info) OVERRIDE;
71  virtual void OnRemovableStorageDetached(
72      const storage_monitor::StorageInfo& info) OVERRIDE;
73
74  // Called from any thread to dispatch the systemInfo event to all extension
75  // processes cross multiple profiles.
76  void DispatchEvent(const std::string& event_name,
77                     scoped_ptr<base::ListValue> args);
78
79  // Called to dispatch the systemInfo.display.onDisplayChanged event.
80  void OnDisplayChanged();
81
82  // Used to record the event names being watched.
83  std::multiset<std::string> watching_event_set_;
84
85  bool has_storage_monitor_observer_;
86
87  DISALLOW_COPY_AND_ASSIGN(SystemInfoEventRouter);
88};
89
90static base::LazyInstance<SystemInfoEventRouter>::Leaky
91    g_system_info_event_router = LAZY_INSTANCE_INITIALIZER;
92
93// static
94SystemInfoEventRouter* SystemInfoEventRouter::GetInstance() {
95  return g_system_info_event_router.Pointer();
96}
97
98SystemInfoEventRouter::SystemInfoEventRouter()
99    : has_storage_monitor_observer_(false) {
100}
101
102SystemInfoEventRouter::~SystemInfoEventRouter() {
103  if (has_storage_monitor_observer_) {
104    StorageMonitor* storage_monitor = StorageMonitor::GetInstance();
105    if (storage_monitor)
106      storage_monitor->RemoveObserver(this);
107  }
108}
109
110void SystemInfoEventRouter::AddEventListener(const std::string& event_name) {
111  DCHECK_CURRENTLY_ON(BrowserThread::UI);
112
113  watching_event_set_.insert(event_name);
114  if (watching_event_set_.count(event_name) > 1)
115    return;
116
117  if (IsDisplayChangedEvent(event_name)) {
118    gfx::Screen* screen = DisplayInfoProvider::Get()->GetActiveScreen();
119    if (screen)
120      screen->AddObserver(this);
121  }
122
123  if (IsSystemStorageEvent(event_name)) {
124    if (!has_storage_monitor_observer_) {
125      has_storage_monitor_observer_ = true;
126      DCHECK(StorageMonitor::GetInstance()->IsInitialized());
127      StorageMonitor::GetInstance()->AddObserver(this);
128    }
129  }
130}
131
132void SystemInfoEventRouter::RemoveEventListener(const std::string& event_name) {
133  DCHECK_CURRENTLY_ON(BrowserThread::UI);
134
135  std::multiset<std::string>::iterator it =
136      watching_event_set_.find(event_name);
137  if (it != watching_event_set_.end()) {
138    watching_event_set_.erase(it);
139    if (watching_event_set_.count(event_name) > 0)
140      return;
141  }
142
143  if (IsDisplayChangedEvent(event_name)) {
144    gfx::Screen* screen = DisplayInfoProvider::Get()->GetActiveScreen();
145    if (screen)
146      screen->RemoveObserver(this);
147  }
148
149  if (IsSystemStorageEvent(event_name)) {
150    const std::string& other_event_name =
151        (event_name == system_storage::OnDetached::kEventName)
152            ? system_storage::OnAttached::kEventName
153            : system_storage::OnDetached::kEventName;
154    if (watching_event_set_.count(other_event_name) == 0) {
155      StorageMonitor::GetInstance()->RemoveObserver(this);
156      has_storage_monitor_observer_ = false;
157    }
158  }
159}
160
161void SystemInfoEventRouter::OnRemovableStorageAttached(
162    const storage_monitor::StorageInfo& info) {
163  StorageUnitInfo unit;
164  systeminfo::BuildStorageUnitInfo(info, &unit);
165  scoped_ptr<base::ListValue> args(new base::ListValue);
166  args->Append(unit.ToValue().release());
167  DispatchEvent(system_storage::OnAttached::kEventName, args.Pass());
168}
169
170void SystemInfoEventRouter::OnRemovableStorageDetached(
171    const storage_monitor::StorageInfo& info) {
172  scoped_ptr<base::ListValue> args(new base::ListValue);
173  std::string transient_id =
174      StorageMonitor::GetInstance()->GetTransientIdForDeviceId(
175          info.device_id());
176  args->AppendString(transient_id);
177
178  DispatchEvent(system_storage::OnDetached::kEventName, args.Pass());
179}
180
181void SystemInfoEventRouter::OnDisplayAdded(const gfx::Display& new_display) {
182  OnDisplayChanged();
183}
184
185void SystemInfoEventRouter::OnDisplayRemoved(const gfx::Display& old_display) {
186  OnDisplayChanged();
187}
188
189void SystemInfoEventRouter::OnDisplayMetricsChanged(const gfx::Display& display,
190                                                    uint32_t metrics) {
191  OnDisplayChanged();
192}
193
194void SystemInfoEventRouter::OnDisplayChanged() {
195  scoped_ptr<base::ListValue> args(new base::ListValue());
196  DispatchEvent(system_display::OnDisplayChanged::kEventName, args.Pass());
197}
198
199void SystemInfoEventRouter::DispatchEvent(const std::string& event_name,
200                                          scoped_ptr<base::ListValue> args) {
201  ExtensionsBrowserClient::Get()->BroadcastEventToRenderers(event_name,
202                                                            args.Pass());
203}
204
205void AddEventListener(const std::string& event_name) {
206  SystemInfoEventRouter::GetInstance()->AddEventListener(event_name);
207}
208
209void RemoveEventListener(const std::string& event_name) {
210  SystemInfoEventRouter::GetInstance()->RemoveEventListener(event_name);
211}
212
213}  // namespace
214
215static base::LazyInstance<BrowserContextKeyedAPIFactory<SystemInfoAPI> >
216    g_factory = LAZY_INSTANCE_INITIALIZER;
217
218// static
219BrowserContextKeyedAPIFactory<SystemInfoAPI>*
220SystemInfoAPI::GetFactoryInstance() {
221  return g_factory.Pointer();
222}
223
224SystemInfoAPI::SystemInfoAPI(content::BrowserContext* context)
225    : browser_context_(context) {
226  EventRouter* router = EventRouter::Get(browser_context_);
227  router->RegisterObserver(this, system_storage::OnAttached::kEventName);
228  router->RegisterObserver(this, system_storage::OnDetached::kEventName);
229  router->RegisterObserver(this, system_display::OnDisplayChanged::kEventName);
230}
231
232SystemInfoAPI::~SystemInfoAPI() {
233}
234
235void SystemInfoAPI::Shutdown() {
236  EventRouter::Get(browser_context_)->UnregisterObserver(this);
237}
238
239void SystemInfoAPI::OnListenerAdded(const EventListenerInfo& details) {
240  if (IsSystemStorageEvent(details.event_name)) {
241    StorageMonitor::GetInstance()->EnsureInitialized(
242        base::Bind(&AddEventListener, details.event_name));
243  } else {
244    AddEventListener(details.event_name);
245  }
246}
247
248void SystemInfoAPI::OnListenerRemoved(const EventListenerInfo& details) {
249  if (IsSystemStorageEvent(details.event_name)) {
250    StorageMonitor::GetInstance()->EnsureInitialized(
251        base::Bind(&RemoveEventListener, details.event_name));
252  } else {
253    RemoveEventListener(details.event_name);
254  }
255}
256
257}  // namespace extensions
258