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 "chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_handler.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/values.h"
12#include "chrome/browser/drive/drive_notification_manager.h"
13#include "chrome/browser/drive/drive_notification_manager_factory.h"
14#include "chrome/browser/extensions/api/sync_file_system/sync_file_system_api_helpers.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/sync_file_system/logger.h"
17#include "chrome/browser/sync_file_system/sync_file_system_service.h"
18#include "chrome/browser/sync_file_system/sync_file_system_service_factory.h"
19#include "chrome/browser/sync_file_system/sync_service_state.h"
20#include "chrome/common/extensions/api/sync_file_system.h"
21#include "content/public/browser/storage_partition.h"
22#include "content/public/browser/web_ui.h"
23#include "google_apis/drive/time_util.h"
24
25using drive::EventLogger;
26using sync_file_system::SyncFileSystemServiceFactory;
27using sync_file_system::SyncServiceState;
28
29namespace syncfs_internals {
30
31SyncFileSystemInternalsHandler::SyncFileSystemInternalsHandler(Profile* profile)
32    : profile_(profile),
33      observing_task_log_(false) {
34  sync_file_system::SyncFileSystemService* sync_service =
35      SyncFileSystemServiceFactory::GetForProfile(profile);
36  if (sync_service)
37    sync_service->AddSyncEventObserver(this);
38}
39
40SyncFileSystemInternalsHandler::~SyncFileSystemInternalsHandler() {
41  sync_file_system::SyncFileSystemService* sync_service =
42      SyncFileSystemServiceFactory::GetForProfile(profile_);
43  if (!sync_service)
44    return;
45  sync_service->RemoveSyncEventObserver(this);
46  if (observing_task_log_)
47    sync_service->task_logger()->RemoveObserver(this);
48}
49
50void SyncFileSystemInternalsHandler::RegisterMessages() {
51  web_ui()->RegisterMessageCallback(
52      "getServiceStatus",
53      base::Bind(&SyncFileSystemInternalsHandler::GetServiceStatus,
54                 base::Unretained(this)));
55  web_ui()->RegisterMessageCallback(
56      "getLog",
57      base::Bind(&SyncFileSystemInternalsHandler::GetLog,
58                 base::Unretained(this)));
59  web_ui()->RegisterMessageCallback(
60      "clearLogs",
61      base::Bind(&SyncFileSystemInternalsHandler::ClearLogs,
62                 base::Unretained(this)));
63  web_ui()->RegisterMessageCallback(
64      "getNotificationSource",
65      base::Bind(&SyncFileSystemInternalsHandler::GetNotificationSource,
66                 base::Unretained(this)));
67  web_ui()->RegisterMessageCallback(
68      "observeTaskLog",
69      base::Bind(&SyncFileSystemInternalsHandler::ObserveTaskLog,
70                 base::Unretained(this)));
71}
72
73void SyncFileSystemInternalsHandler::OnSyncStateUpdated(
74    const GURL& app_origin,
75    sync_file_system::SyncServiceState state,
76    const std::string& description) {
77  std::string state_string = extensions::api::sync_file_system::ToString(
78        extensions::SyncServiceStateToExtensionEnum(state));
79  if (!description.empty())
80    state_string += " (" + description + ")";
81
82  // TODO(calvinlo): OnSyncStateUpdated should be updated to also provide the
83  // notification mechanism (XMPP or Polling).
84  web_ui()->CallJavascriptFunction("SyncService.onGetServiceStatus",
85                                   base::StringValue(state_string));
86}
87
88void SyncFileSystemInternalsHandler::OnFileSynced(
89    const storage::FileSystemURL& url,
90    sync_file_system::SyncFileStatus status,
91    sync_file_system::SyncAction action,
92    sync_file_system::SyncDirection direction) {
93}
94
95void SyncFileSystemInternalsHandler::OnLogRecorded(
96    const sync_file_system::TaskLogger::TaskLog& task_log) {
97  base::DictionaryValue dict;
98  int64 duration = (task_log.end_time - task_log.start_time).InMilliseconds();
99  dict.SetInteger("duration", duration);
100  dict.SetString("task_description", task_log.task_description);
101  dict.SetString("result_description", task_log.result_description);
102
103  scoped_ptr<base::ListValue> details(new base::ListValue);
104  details->AppendStrings(task_log.details);
105  dict.Set("details", details.release());
106  web_ui()->CallJavascriptFunction("TaskLog.onTaskLogRecorded", dict);
107}
108
109void SyncFileSystemInternalsHandler::GetServiceStatus(
110    const base::ListValue* args) {
111  SyncServiceState state_enum = sync_file_system::SYNC_SERVICE_DISABLED;
112  sync_file_system::SyncFileSystemService* sync_service =
113      SyncFileSystemServiceFactory::GetForProfile(profile_);
114  if (sync_service)
115    state_enum = sync_service->GetSyncServiceState();
116  const std::string state_string = extensions::api::sync_file_system::ToString(
117      extensions::SyncServiceStateToExtensionEnum(state_enum));
118  web_ui()->CallJavascriptFunction("SyncService.onGetServiceStatus",
119                                   base::StringValue(state_string));
120}
121
122void SyncFileSystemInternalsHandler::GetNotificationSource(
123    const base::ListValue* args) {
124  drive::DriveNotificationManager* drive_notification_manager =
125      drive::DriveNotificationManagerFactory::FindForBrowserContext(profile_);
126  if (!drive_notification_manager)
127    return;
128  bool xmpp_enabled = drive_notification_manager->push_notification_enabled();
129  std::string notification_source = xmpp_enabled ? "XMPP" : "Polling";
130  web_ui()->CallJavascriptFunction("SyncService.onGetNotificationSource",
131                                   base::StringValue(notification_source));
132}
133
134void SyncFileSystemInternalsHandler::GetLog(
135    const base::ListValue* args) {
136  const std::vector<EventLogger::Event> log =
137      sync_file_system::util::GetLogHistory();
138
139  int last_log_id_sent;
140  if (!args->GetInteger(0, &last_log_id_sent))
141    last_log_id_sent = -1;
142
143  // Collate events which haven't been sent to WebUI yet.
144  base::ListValue list;
145  for (std::vector<EventLogger::Event>::const_iterator log_entry = log.begin();
146       log_entry != log.end();
147       ++log_entry) {
148    if (log_entry->id <= last_log_id_sent)
149      continue;
150
151    base::DictionaryValue* dict = new base::DictionaryValue;
152    dict->SetInteger("id", log_entry->id);
153    dict->SetString("time",
154        google_apis::util::FormatTimeAsStringLocaltime(log_entry->when));
155    dict->SetString("logEvent", log_entry->what);
156    list.Append(dict);
157    last_log_id_sent = log_entry->id;
158  }
159  if (list.empty())
160    return;
161
162  web_ui()->CallJavascriptFunction("SyncService.onGetLog", list);
163}
164
165void SyncFileSystemInternalsHandler::ClearLogs(const base::ListValue* args) {
166  sync_file_system::util::ClearLog();
167}
168
169void SyncFileSystemInternalsHandler::ObserveTaskLog(
170    const base::ListValue* args) {
171  sync_file_system::SyncFileSystemService* sync_service =
172      SyncFileSystemServiceFactory::GetForProfile(profile_);
173  if (!sync_service)
174    return;
175  if (!observing_task_log_) {
176    observing_task_log_ = true;
177    sync_service->task_logger()->AddObserver(this);
178  }
179
180  DCHECK(sync_service->task_logger());
181  const sync_file_system::TaskLogger::LogList& log =
182      sync_service->task_logger()->GetLog();
183
184  for (sync_file_system::TaskLogger::LogList::const_iterator itr = log.begin();
185       itr != log.end(); ++itr)
186    OnLogRecorded(**itr);
187}
188
189}  // namespace syncfs_internals
190