1// Copyright 2014 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_internals_message_handler.h"
6
7#include <vector>
8
9#include "base/logging.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/sync/about_sync_util.h"
12#include "chrome/browser/sync/profile_sync_service.h"
13#include "chrome/browser/sync/profile_sync_service_factory.h"
14#include "content/public/browser/browser_thread.h"
15#include "content/public/browser/web_ui.h"
16#include "sync/internal_api/public/events/protocol_event.h"
17#include "sync/internal_api/public/sessions/commit_counters.h"
18#include "sync/internal_api/public/sessions/status_counters.h"
19#include "sync/internal_api/public/sessions/update_counters.h"
20#include "sync/internal_api/public/util/weak_handle.h"
21#include "sync/js/js_event_details.h"
22
23using syncer::JsEventDetails;
24using syncer::ModelTypeSet;
25using syncer::WeakHandle;
26
27SyncInternalsMessageHandler::SyncInternalsMessageHandler()
28    : is_registered_(false),
29      is_registered_for_counters_(false),
30      weak_ptr_factory_(this) {
31}
32
33SyncInternalsMessageHandler::~SyncInternalsMessageHandler() {
34  if (js_controller_)
35    js_controller_->RemoveJsEventHandler(this);
36
37  ProfileSyncService* service = GetProfileSyncService();
38  if (service && service->HasObserver(this)) {
39    service->RemoveObserver(this);
40    service->RemoveProtocolEventObserver(this);
41  }
42
43  if (service && is_registered_for_counters_) {
44    service->RemoveTypeDebugInfoObserver(this);
45  }
46}
47
48void SyncInternalsMessageHandler::RegisterMessages() {
49  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
50
51  web_ui()->RegisterMessageCallback(
52      "registerForEvents",
53      base::Bind(&SyncInternalsMessageHandler::HandleRegisterForEvents,
54                 base::Unretained(this)));
55
56  web_ui()->RegisterMessageCallback(
57      "registerForPerTypeCounters",
58      base::Bind(&SyncInternalsMessageHandler::HandleRegisterForPerTypeCounters,
59                 base::Unretained(this)));
60
61  web_ui()->RegisterMessageCallback(
62      "requestUpdatedAboutInfo",
63      base::Bind(&SyncInternalsMessageHandler::HandleRequestUpdatedAboutInfo,
64                 base::Unretained(this)));
65
66  web_ui()->RegisterMessageCallback(
67      "requestListOfTypes",
68      base::Bind(&SyncInternalsMessageHandler::HandleRequestListOfTypes,
69                 base::Unretained(this)));
70
71  web_ui()->RegisterMessageCallback(
72      "getAllNodes",
73      base::Bind(&SyncInternalsMessageHandler::HandleGetAllNodes,
74                 base::Unretained(this)));
75}
76
77void SyncInternalsMessageHandler::HandleRegisterForEvents(
78    const base::ListValue* args) {
79  DCHECK(args->empty());
80
81  // is_registered_ flag protects us from double-registering.  This could
82  // happen on a page refresh, where the JavaScript gets re-run but the
83  // message handler remains unchanged.
84  ProfileSyncService* service = GetProfileSyncService();
85  if (service && !is_registered_) {
86    service->AddObserver(this);
87    service->AddProtocolEventObserver(this);
88    js_controller_ = service->GetJsController();
89    js_controller_->AddJsEventHandler(this);
90    is_registered_ = true;
91  }
92}
93
94void SyncInternalsMessageHandler::HandleRegisterForPerTypeCounters(
95    const base::ListValue* args) {
96  DCHECK(args->empty());
97
98  if (ProfileSyncService* service = GetProfileSyncService()) {
99    if (!is_registered_for_counters_) {
100      service->AddTypeDebugInfoObserver(this);
101      is_registered_for_counters_ = true;
102    } else {
103      // Re-register to ensure counters get re-emitted.
104      service->RemoveTypeDebugInfoObserver(this);
105      service->AddTypeDebugInfoObserver(this);
106    }
107  }
108}
109
110void SyncInternalsMessageHandler::HandleRequestUpdatedAboutInfo(
111    const base::ListValue* args) {
112  DCHECK(args->empty());
113  SendAboutInfo();
114}
115
116void SyncInternalsMessageHandler::HandleRequestListOfTypes(
117    const base::ListValue* args) {
118  DCHECK(args->empty());
119  base::DictionaryValue event_details;
120  scoped_ptr<base::ListValue> type_list(new base::ListValue());
121  ModelTypeSet protocol_types = syncer::ProtocolTypes();
122  for (ModelTypeSet::Iterator it = protocol_types.First();
123       it.Good(); it.Inc()) {
124    type_list->Append(new base::StringValue(ModelTypeToString(it.Get())));
125  }
126  event_details.Set("types", type_list.release());
127  web_ui()->CallJavascriptFunction(
128      "chrome.sync.dispatchEvent",
129      base::StringValue("onReceivedListOfTypes"),
130      event_details);
131}
132
133void SyncInternalsMessageHandler::HandleGetAllNodes(
134    const base::ListValue* args) {
135  DCHECK_EQ(1U, args->GetSize());
136  int request_id = 0;
137  bool success = args->GetInteger(0, &request_id);
138  DCHECK(success);
139
140  ProfileSyncService* service = GetProfileSyncService();
141  if (service) {
142    service->GetAllNodes(
143        base::Bind(&SyncInternalsMessageHandler::OnReceivedAllNodes,
144                   weak_ptr_factory_.GetWeakPtr(), request_id));
145  }
146}
147
148void SyncInternalsMessageHandler::OnReceivedAllNodes(
149    int request_id,
150    scoped_ptr<base::ListValue> nodes) {
151  base::FundamentalValue id(request_id);
152  web_ui()->CallJavascriptFunction("chrome.sync.getAllNodesCallback",
153                                   id, *nodes);
154}
155
156void SyncInternalsMessageHandler::OnStateChanged() {
157  SendAboutInfo();
158}
159
160void SyncInternalsMessageHandler::OnProtocolEvent(
161    const syncer::ProtocolEvent& event) {
162  scoped_ptr<base::DictionaryValue> value(
163      syncer::ProtocolEvent::ToValue(event));
164  web_ui()->CallJavascriptFunction(
165      "chrome.sync.dispatchEvent",
166      base::StringValue("onProtocolEvent"),
167      *value);
168}
169
170void SyncInternalsMessageHandler::OnCommitCountersUpdated(
171    syncer::ModelType type,
172    const syncer::CommitCounters& counters) {
173  EmitCounterUpdate(type, "commit", counters.ToValue());
174}
175
176void SyncInternalsMessageHandler::OnUpdateCountersUpdated(
177    syncer::ModelType type,
178    const syncer::UpdateCounters& counters) {
179  EmitCounterUpdate(type, "update", counters.ToValue());
180}
181
182void SyncInternalsMessageHandler::OnStatusCountersUpdated(
183    syncer::ModelType type,
184    const syncer::StatusCounters& counters) {
185  EmitCounterUpdate(type, "status", counters.ToValue());
186}
187
188void SyncInternalsMessageHandler::EmitCounterUpdate(
189    syncer::ModelType type,
190    const std::string& counter_type,
191    scoped_ptr<base::DictionaryValue> value) {
192  scoped_ptr<base::DictionaryValue> details(new base::DictionaryValue());
193  details->SetString("modelType", ModelTypeToString(type));
194  details->SetString("counterType", counter_type);
195  details->Set("counters", value.release());
196  web_ui()->CallJavascriptFunction("chrome.sync.dispatchEvent",
197                                   base::StringValue("onCountersUpdated"),
198                                   *details);
199}
200
201void SyncInternalsMessageHandler::HandleJsEvent(
202    const std::string& name,
203    const JsEventDetails& details) {
204  DVLOG(1) << "Handling event: " << name
205           << " with details " << details.ToString();
206  web_ui()->CallJavascriptFunction("chrome.sync.dispatchEvent",
207                                   base::StringValue(name),
208                                   details.Get());
209}
210
211void SyncInternalsMessageHandler::SendAboutInfo() {
212  scoped_ptr<base::DictionaryValue> value =
213      sync_ui_util::ConstructAboutInformation(GetProfileSyncService());
214  web_ui()->CallJavascriptFunction(
215      "chrome.sync.dispatchEvent",
216      base::StringValue("onAboutInfoUpdated"),
217      *value);
218}
219
220// Gets the ProfileSyncService of the underlying original profile.
221// May return NULL (e.g., if sync is disabled on the command line).
222ProfileSyncService* SyncInternalsMessageHandler::GetProfileSyncService() {
223  Profile* profile = Profile::FromWebUI(web_ui());
224  ProfileSyncServiceFactory* factory = ProfileSyncServiceFactory::GetInstance();
225  return factory->GetForProfile(profile->GetOriginalProfile());
226}
227