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
23 using syncer::JsEventDetails;
24 using syncer::ModelTypeSet;
25 using syncer::WeakHandle;
26
SyncInternalsMessageHandler()27 SyncInternalsMessageHandler::SyncInternalsMessageHandler()
28 : is_registered_(false),
29 is_registered_for_counters_(false),
30 weak_ptr_factory_(this) {
31 }
32
~SyncInternalsMessageHandler()33 SyncInternalsMessageHandler::~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
RegisterMessages()48 void 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
HandleRegisterForEvents(const base::ListValue * args)77 void 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
HandleRegisterForPerTypeCounters(const base::ListValue * args)94 void SyncInternalsMessageHandler::HandleRegisterForPerTypeCounters(
95 const base::ListValue* args) {
96 DCHECK(args->empty());
97
98 ProfileSyncService* service = GetProfileSyncService();
99 if (service && !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
HandleRequestUpdatedAboutInfo(const base::ListValue * args)109 void SyncInternalsMessageHandler::HandleRequestUpdatedAboutInfo(
110 const base::ListValue* args) {
111 DCHECK(args->empty());
112 SendAboutInfo();
113 }
114
HandleRequestListOfTypes(const base::ListValue * args)115 void SyncInternalsMessageHandler::HandleRequestListOfTypes(
116 const base::ListValue* args) {
117 DCHECK(args->empty());
118 base::DictionaryValue event_details;
119 scoped_ptr<base::ListValue> type_list(new base::ListValue());
120 ModelTypeSet protocol_types = syncer::ProtocolTypes();
121 for (ModelTypeSet::Iterator it = protocol_types.First();
122 it.Good(); it.Inc()) {
123 type_list->Append(new base::StringValue(ModelTypeToString(it.Get())));
124 }
125 event_details.Set("types", type_list.release());
126 web_ui()->CallJavascriptFunction(
127 "chrome.sync.dispatchEvent",
128 base::StringValue("onReceivedListOfTypes"),
129 event_details);
130 }
131
HandleGetAllNodes(const base::ListValue * args)132 void SyncInternalsMessageHandler::HandleGetAllNodes(
133 const base::ListValue* args) {
134 DCHECK_EQ(1U, args->GetSize());
135 int request_id = 0;
136 bool success = args->GetInteger(0, &request_id);
137 DCHECK(success);
138
139 ProfileSyncService* service = GetProfileSyncService();
140 if (service) {
141 service->GetAllNodes(
142 base::Bind(&SyncInternalsMessageHandler::OnReceivedAllNodes,
143 weak_ptr_factory_.GetWeakPtr(), request_id));
144 }
145 }
146
OnReceivedAllNodes(int request_id,scoped_ptr<base::ListValue> nodes)147 void SyncInternalsMessageHandler::OnReceivedAllNodes(
148 int request_id,
149 scoped_ptr<base::ListValue> nodes) {
150 base::FundamentalValue id(request_id);
151 web_ui()->CallJavascriptFunction("chrome.sync.getAllNodesCallback",
152 id, *nodes);
153 }
154
OnStateChanged()155 void SyncInternalsMessageHandler::OnStateChanged() {
156 SendAboutInfo();
157 }
158
OnProtocolEvent(const syncer::ProtocolEvent & event)159 void SyncInternalsMessageHandler::OnProtocolEvent(
160 const syncer::ProtocolEvent& event) {
161 scoped_ptr<base::DictionaryValue> value(
162 syncer::ProtocolEvent::ToValue(event));
163 web_ui()->CallJavascriptFunction(
164 "chrome.sync.dispatchEvent",
165 base::StringValue("onProtocolEvent"),
166 *value);
167 }
168
OnCommitCountersUpdated(syncer::ModelType type,const syncer::CommitCounters & counters)169 void SyncInternalsMessageHandler::OnCommitCountersUpdated(
170 syncer::ModelType type,
171 const syncer::CommitCounters& counters) {
172 EmitCounterUpdate(type, "commit", counters.ToValue());
173 }
174
OnUpdateCountersUpdated(syncer::ModelType type,const syncer::UpdateCounters & counters)175 void SyncInternalsMessageHandler::OnUpdateCountersUpdated(
176 syncer::ModelType type,
177 const syncer::UpdateCounters& counters) {
178 EmitCounterUpdate(type, "update", counters.ToValue());
179 }
180
OnStatusCountersUpdated(syncer::ModelType type,const syncer::StatusCounters & counters)181 void SyncInternalsMessageHandler::OnStatusCountersUpdated(
182 syncer::ModelType type,
183 const syncer::StatusCounters& counters) {
184 EmitCounterUpdate(type, "status", counters.ToValue());
185 }
186
EmitCounterUpdate(syncer::ModelType type,const std::string & counter_type,scoped_ptr<base::DictionaryValue> value)187 void SyncInternalsMessageHandler::EmitCounterUpdate(
188 syncer::ModelType type,
189 const std::string& counter_type,
190 scoped_ptr<base::DictionaryValue> value) {
191 scoped_ptr<base::DictionaryValue> details(new base::DictionaryValue());
192 details->SetString("modelType", ModelTypeToString(type));
193 details->SetString("counterType", counter_type);
194 details->Set("counters", value.release());
195 web_ui()->CallJavascriptFunction("chrome.sync.dispatchEvent",
196 base::StringValue("onCountersUpdated"),
197 *details);
198 }
199
HandleJsEvent(const std::string & name,const JsEventDetails & details)200 void SyncInternalsMessageHandler::HandleJsEvent(
201 const std::string& name,
202 const JsEventDetails& details) {
203 DVLOG(1) << "Handling event: " << name
204 << " with details " << details.ToString();
205 web_ui()->CallJavascriptFunction("chrome.sync.dispatchEvent",
206 base::StringValue(name),
207 details.Get());
208 }
209
SendAboutInfo()210 void SyncInternalsMessageHandler::SendAboutInfo() {
211 scoped_ptr<base::DictionaryValue> value =
212 sync_ui_util::ConstructAboutInformation(GetProfileSyncService());
213 web_ui()->CallJavascriptFunction(
214 "chrome.sync.dispatchEvent",
215 base::StringValue("onAboutInfoUpdated"),
216 *value);
217 }
218
219 // Gets the ProfileSyncService of the underlying original profile.
220 // May return NULL (e.g., if sync is disabled on the command line).
GetProfileSyncService()221 ProfileSyncService* SyncInternalsMessageHandler::GetProfileSyncService() {
222 Profile* profile = Profile::FromWebUI(web_ui());
223 ProfileSyncServiceFactory* factory = ProfileSyncServiceFactory::GetInstance();
224 return factory->GetForProfile(profile->GetOriginalProfile());
225 }
226