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/gcm_internals_ui.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/format_macros.h"
12#include "base/memory/weak_ptr.h"
13#include "base/strings/string_util.h"
14#include "base/strings/stringprintf.h"
15#include "base/values.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/browser/services/gcm/gcm_profile_service.h"
18#include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
19#include "chrome/common/url_constants.h"
20#include "components/gcm_driver/gcm_client.h"
21#include "components/gcm_driver/gcm_driver.h"
22#include "content/public/browser/web_ui.h"
23#include "content/public/browser/web_ui_controller.h"
24#include "content/public/browser/web_ui_data_source.h"
25#include "content/public/browser/web_ui_message_handler.h"
26#include "grit/browser_resources.h"
27
28namespace {
29
30void SetCheckinInfo(
31    const std::vector<gcm::CheckinActivity>& checkins,
32    base::ListValue* checkin_info) {
33  std::vector<gcm::CheckinActivity>::const_iterator it = checkins.begin();
34  for (; it < checkins.end(); ++it) {
35    base::ListValue* row = new base::ListValue();
36    checkin_info->Append(row);
37
38    row->AppendDouble(it->time.ToJsTime());
39    row->AppendString(it->event);
40    row->AppendString(it->details);
41  }
42}
43
44void SetConnectionInfo(
45    const std::vector<gcm::ConnectionActivity>& connections,
46    base::ListValue* connection_info) {
47  std::vector<gcm::ConnectionActivity>::const_iterator it = connections.begin();
48  for (; it < connections.end(); ++it) {
49    base::ListValue* row = new base::ListValue();
50    connection_info->Append(row);
51
52    row->AppendDouble(it->time.ToJsTime());
53    row->AppendString(it->event);
54    row->AppendString(it->details);
55  }
56}
57
58void SetRegistrationInfo(
59    const std::vector<gcm::RegistrationActivity>& registrations,
60    base::ListValue* registration_info) {
61  std::vector<gcm::RegistrationActivity>::const_iterator it =
62      registrations.begin();
63  for (; it < registrations.end(); ++it) {
64    base::ListValue* row = new base::ListValue();
65    registration_info->Append(row);
66
67    row->AppendDouble(it->time.ToJsTime());
68    row->AppendString(it->app_id);
69    row->AppendString(it->sender_ids);
70    row->AppendString(it->event);
71    row->AppendString(it->details);
72  }
73}
74
75void SetReceivingInfo(
76    const std::vector<gcm::ReceivingActivity>& receives,
77    base::ListValue* receive_info) {
78  std::vector<gcm::ReceivingActivity>::const_iterator it = receives.begin();
79  for (; it < receives.end(); ++it) {
80    base::ListValue* row = new base::ListValue();
81    receive_info->Append(row);
82
83    row->AppendDouble(it->time.ToJsTime());
84    row->AppendString(it->app_id);
85    row->AppendString(it->from);
86    row->AppendString(base::StringPrintf("%d", it->message_byte_size));
87    row->AppendString(it->event);
88    row->AppendString(it->details);
89  }
90}
91
92void SetSendingInfo(
93    const std::vector<gcm::SendingActivity>& sends,
94    base::ListValue* send_info) {
95  std::vector<gcm::SendingActivity>::const_iterator it = sends.begin();
96  for (; it < sends.end(); ++it) {
97    base::ListValue* row = new base::ListValue();
98    send_info->Append(row);
99
100    row->AppendDouble(it->time.ToJsTime());
101    row->AppendString(it->app_id);
102    row->AppendString(it->receiver_id);
103    row->AppendString(it->message_id);
104    row->AppendString(it->event);
105    row->AppendString(it->details);
106  }
107}
108
109// Class acting as a controller of the chrome://gcm-internals WebUI.
110class GcmInternalsUIMessageHandler : public content::WebUIMessageHandler {
111 public:
112  GcmInternalsUIMessageHandler();
113  virtual ~GcmInternalsUIMessageHandler();
114
115  // WebUIMessageHandler implementation.
116  virtual void RegisterMessages() OVERRIDE;
117
118 private:
119  // Return all of the GCM related infos to the gcm-internals page by calling
120  // Javascript callback function
121  // |gcm-internals.returnInfo()|.
122  void ReturnResults(Profile* profile, gcm::GCMProfileService* profile_service,
123                     const gcm::GCMClient::GCMStatistics* stats) const;
124
125  // Request all of the GCM related infos through gcm profile service.
126  void RequestAllInfo(const base::ListValue* args);
127
128  // Enables/disables GCM activity recording through gcm profile service.
129  void SetRecording(const base::ListValue* args);
130
131  // Callback function of the request for all gcm related infos.
132  void RequestGCMStatisticsFinished(
133      const gcm::GCMClient::GCMStatistics& args) const;
134
135  // Factory for creating references in callbacks.
136  base::WeakPtrFactory<GcmInternalsUIMessageHandler> weak_ptr_factory_;
137
138  DISALLOW_COPY_AND_ASSIGN(GcmInternalsUIMessageHandler);
139};
140
141GcmInternalsUIMessageHandler::GcmInternalsUIMessageHandler()
142    : weak_ptr_factory_(this) {}
143
144GcmInternalsUIMessageHandler::~GcmInternalsUIMessageHandler() {}
145
146void GcmInternalsUIMessageHandler::ReturnResults(
147    Profile* profile,
148    gcm::GCMProfileService* profile_service,
149    const gcm::GCMClient::GCMStatistics* stats) const {
150  base::DictionaryValue results;
151  base::DictionaryValue* device_info = new base::DictionaryValue();
152  results.Set("deviceInfo", device_info);
153
154  device_info->SetBoolean("profileServiceCreated", profile_service != NULL);
155  device_info->SetBoolean("gcmEnabled",
156                          gcm::GCMProfileService::IsGCMEnabled(profile));
157  if (profile_service) {
158    device_info->SetString("signedInUserName",
159                           profile_service->SignedInUserName());
160  }
161  if (stats) {
162    results.SetBoolean("isRecording", stats->is_recording);
163    device_info->SetBoolean("gcmClientCreated", stats->gcm_client_created);
164    device_info->SetString("gcmClientState", stats->gcm_client_state);
165    device_info->SetBoolean("connectionClientCreated",
166                            stats->connection_client_created);
167    device_info->SetString("registeredAppIds",
168                           JoinString(stats->registered_app_ids, ","));
169    if (stats->connection_client_created)
170      device_info->SetString("connectionState", stats->connection_state);
171    if (stats->android_id > 0) {
172      device_info->SetString("androidId",
173          base::StringPrintf("0x%" PRIx64, stats->android_id));
174    }
175    device_info->SetInteger("sendQueueSize", stats->send_queue_size);
176    device_info->SetInteger("resendQueueSize", stats->resend_queue_size);
177
178    if (stats->recorded_activities.checkin_activities.size() > 0) {
179      base::ListValue* checkin_info = new base::ListValue();
180      results.Set("checkinInfo", checkin_info);
181      SetCheckinInfo(stats->recorded_activities.checkin_activities,
182                     checkin_info);
183    }
184    if (stats->recorded_activities.connection_activities.size() > 0) {
185      base::ListValue* connection_info = new base::ListValue();
186      results.Set("connectionInfo", connection_info);
187      SetConnectionInfo(stats->recorded_activities.connection_activities,
188                        connection_info);
189    }
190    if (stats->recorded_activities.registration_activities.size() > 0) {
191      base::ListValue* registration_info = new base::ListValue();
192      results.Set("registrationInfo", registration_info);
193      SetRegistrationInfo(stats->recorded_activities.registration_activities,
194                          registration_info);
195    }
196    if (stats->recorded_activities.receiving_activities.size() > 0) {
197      base::ListValue* receive_info = new base::ListValue();
198      results.Set("receiveInfo", receive_info);
199      SetReceivingInfo(stats->recorded_activities.receiving_activities,
200                       receive_info);
201    }
202    if (stats->recorded_activities.sending_activities.size() > 0) {
203      base::ListValue* send_info = new base::ListValue();
204      results.Set("sendInfo", send_info);
205      SetSendingInfo(stats->recorded_activities.sending_activities, send_info);
206    }
207  }
208  web_ui()->CallJavascriptFunction("gcmInternals.setGcmInternalsInfo",
209                                   results);
210}
211
212void GcmInternalsUIMessageHandler::RequestAllInfo(
213    const base::ListValue* args) {
214  if (args->GetSize() != 1) {
215    NOTREACHED();
216    return;
217  }
218  bool clear_logs = false;
219  if (!args->GetBoolean(0, &clear_logs)) {
220    NOTREACHED();
221    return;
222  }
223
224  Profile* profile = Profile::FromWebUI(web_ui());
225  gcm::GCMProfileService* profile_service =
226    gcm::GCMProfileServiceFactory::GetForProfile(profile);
227
228  if (!profile_service || !profile_service->driver()) {
229    ReturnResults(profile, NULL, NULL);
230  } else {
231    profile_service->driver()->GetGCMStatistics(
232        base::Bind(&GcmInternalsUIMessageHandler::RequestGCMStatisticsFinished,
233                   weak_ptr_factory_.GetWeakPtr()),
234        clear_logs);
235  }
236}
237
238void GcmInternalsUIMessageHandler::SetRecording(const base::ListValue* args) {
239  if (args->GetSize() != 1) {
240    NOTREACHED();
241    return;
242  }
243  bool recording = false;
244  if (!args->GetBoolean(0, &recording)) {
245    NOTREACHED();
246    return;
247  }
248
249  Profile* profile = Profile::FromWebUI(web_ui());
250  gcm::GCMProfileService* profile_service =
251      gcm::GCMProfileServiceFactory::GetForProfile(profile);
252
253  if (!profile_service) {
254    ReturnResults(profile, NULL, NULL);
255    return;
256  }
257  if (profile_service->SignedInUserName().empty()) {
258    ReturnResults(profile, profile_service, NULL);
259    return;
260  }
261  // Get fresh stats after changing recording setting.
262  profile_service->driver()->SetGCMRecording(
263      base::Bind(
264          &GcmInternalsUIMessageHandler::RequestGCMStatisticsFinished,
265          weak_ptr_factory_.GetWeakPtr()),
266      recording);
267}
268
269void GcmInternalsUIMessageHandler::RequestGCMStatisticsFinished(
270    const gcm::GCMClient::GCMStatistics& stats) const {
271  Profile* profile = Profile::FromWebUI(web_ui());
272  DCHECK(profile);
273  gcm::GCMProfileService* profile_service =
274      gcm::GCMProfileServiceFactory::GetForProfile(profile);
275  DCHECK(profile_service);
276  ReturnResults(profile, profile_service, &stats);
277}
278
279void GcmInternalsUIMessageHandler::RegisterMessages() {
280  web_ui()->RegisterMessageCallback(
281      "getGcmInternalsInfo",
282      base::Bind(&GcmInternalsUIMessageHandler::RequestAllInfo,
283                 weak_ptr_factory_.GetWeakPtr()));
284  web_ui()->RegisterMessageCallback(
285      "setGcmInternalsRecording",
286      base::Bind(&GcmInternalsUIMessageHandler::SetRecording,
287                 weak_ptr_factory_.GetWeakPtr()));
288}
289
290}  // namespace
291
292GCMInternalsUI::GCMInternalsUI(content::WebUI* web_ui)
293    : content::WebUIController(web_ui) {
294  // Set up the chrome://gcm-internals source.
295  content::WebUIDataSource* html_source =
296      content::WebUIDataSource::Create(chrome::kChromeUIGCMInternalsHost);
297  html_source->SetUseJsonJSFormatV2();
298
299  html_source->SetJsonPath("strings.js");
300
301  // Add required resources.
302  html_source->AddResourcePath("gcm_internals.css", IDR_GCM_INTERNALS_CSS);
303  html_source->AddResourcePath("gcm_internals.js", IDR_GCM_INTERNALS_JS);
304  html_source->SetDefaultResource(IDR_GCM_INTERNALS_HTML);
305
306  Profile* profile = Profile::FromWebUI(web_ui);
307  content::WebUIDataSource::Add(profile, html_source);
308
309  web_ui->AddMessageHandler(new GcmInternalsUIMessageHandler());
310}
311
312GCMInternalsUI::~GCMInternalsUI() {}
313