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