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/sync_file_system/extension_sync_event_observer.h"
6
7#include "base/lazy_instance.h"
8#include "chrome/browser/extensions/api/sync_file_system/sync_file_system_api_helpers.h"
9#include "chrome/browser/sync_file_system/sync_event_observer.h"
10#include "chrome/browser/sync_file_system/sync_file_system_service.h"
11#include "chrome/browser/sync_file_system/sync_file_system_service_factory.h"
12#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
13#include "chrome/common/extensions/api/sync_file_system.h"
14#include "content/public/browser/browser_context.h"
15#include "extensions/browser/event_router.h"
16#include "extensions/browser/extension_registry.h"
17#include "extensions/browser/extension_system_provider.h"
18#include "extensions/browser/extensions_browser_client.h"
19#include "extensions/common/extension_set.h"
20#include "storage/browser/fileapi/file_system_url.h"
21#include "storage/common/fileapi/file_system_util.h"
22
23using sync_file_system::SyncEventObserver;
24
25namespace extensions {
26
27static base::LazyInstance<
28    BrowserContextKeyedAPIFactory<ExtensionSyncEventObserver> > g_factory =
29    LAZY_INSTANCE_INITIALIZER;
30
31// static
32BrowserContextKeyedAPIFactory<ExtensionSyncEventObserver>*
33ExtensionSyncEventObserver::GetFactoryInstance() {
34  return g_factory.Pointer();
35}
36
37ExtensionSyncEventObserver::ExtensionSyncEventObserver(
38    content::BrowserContext* context)
39    : browser_context_(context), sync_service_(NULL) {}
40
41void ExtensionSyncEventObserver::InitializeForService(
42    sync_file_system::SyncFileSystemService* sync_service) {
43  DCHECK(sync_service);
44  if (sync_service_ != NULL) {
45    DCHECK_EQ(sync_service_, sync_service);
46    return;
47  }
48  sync_service_ = sync_service;
49  sync_service_->AddSyncEventObserver(this);
50}
51
52ExtensionSyncEventObserver::~ExtensionSyncEventObserver() {}
53
54void ExtensionSyncEventObserver::Shutdown() {
55  if (sync_service_ != NULL)
56    sync_service_->RemoveSyncEventObserver(this);
57}
58
59std::string ExtensionSyncEventObserver::GetExtensionId(
60    const GURL& app_origin) {
61  const Extension* app = ExtensionRegistry::Get(browser_context_)
62      ->enabled_extensions().GetAppByURL(app_origin);
63  if (!app) {
64    // The app is uninstalled or disabled.
65    return std::string();
66  }
67  return app->id();
68}
69
70void ExtensionSyncEventObserver::OnSyncStateUpdated(
71    const GURL& app_origin,
72    sync_file_system::SyncServiceState state,
73    const std::string& description) {
74  // Convert state and description into SyncState Object.
75  api::sync_file_system::ServiceInfo service_info;
76  service_info.state = SyncServiceStateToExtensionEnum(state);
77  service_info.description = description;
78  scoped_ptr<base::ListValue> params(
79      api::sync_file_system::OnServiceStatusChanged::Create(service_info));
80
81  BroadcastOrDispatchEvent(
82      app_origin,
83      api::sync_file_system::OnServiceStatusChanged::kEventName,
84      params.Pass());
85}
86
87void ExtensionSyncEventObserver::OnFileSynced(
88    const storage::FileSystemURL& url,
89    sync_file_system::SyncFileStatus status,
90    sync_file_system::SyncAction action,
91    sync_file_system::SyncDirection direction) {
92  scoped_ptr<base::ListValue> params(new base::ListValue());
93
94  // For now we always assume events come only for files (not directories).
95  scoped_ptr<base::DictionaryValue> entry(
96      CreateDictionaryValueForFileSystemEntry(
97          url, sync_file_system::SYNC_FILE_TYPE_FILE));
98  if (!entry)
99    return;
100  params->Append(entry.release());
101
102  // Status, SyncAction and any optional notes to go here.
103  api::sync_file_system::FileStatus status_enum =
104      SyncFileStatusToExtensionEnum(status);
105  api::sync_file_system::SyncAction action_enum =
106      SyncActionToExtensionEnum(action);
107  api::sync_file_system::SyncDirection direction_enum =
108      SyncDirectionToExtensionEnum(direction);
109  params->AppendString(api::sync_file_system::ToString(status_enum));
110  params->AppendString(api::sync_file_system::ToString(action_enum));
111  params->AppendString(api::sync_file_system::ToString(direction_enum));
112
113  BroadcastOrDispatchEvent(
114      url.origin(),
115      api::sync_file_system::OnFileStatusChanged::kEventName,
116      params.Pass());
117}
118
119void ExtensionSyncEventObserver::BroadcastOrDispatchEvent(
120    const GURL& app_origin,
121    const std::string& event_name,
122    scoped_ptr<base::ListValue> values) {
123  // Check to see whether the event should be broadcasted to all listening
124  // extensions or sent to a specific extension ID.
125  bool broadcast_mode = app_origin.is_empty();
126  EventRouter* event_router = EventRouter::Get(browser_context_);
127  DCHECK(event_router);
128
129  scoped_ptr<Event> event(new Event(event_name, values.Pass()));
130  event->restrict_to_browser_context = browser_context_;
131
132  // No app_origin, broadcast to all listening extensions for this event name.
133  if (broadcast_mode) {
134    event_router->BroadcastEvent(event.Pass());
135    return;
136  }
137
138  // Dispatch to single extension ID.
139  const std::string extension_id = GetExtensionId(app_origin);
140  if (extension_id.empty())
141    return;
142  event_router->DispatchEventToExtension(extension_id, event.Pass());
143}
144
145template <>
146void BrowserContextKeyedAPIFactory<
147    ExtensionSyncEventObserver>::DeclareFactoryDependencies() {
148  DependsOn(sync_file_system::SyncFileSystemServiceFactory::GetInstance());
149  DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
150}
151
152}  // namespace extensions
153