sync: Add UI code for per type debug counters

Adds a new tab to the about:sync page to display the latest per-type
counters.  These counters will eventually replace the StatusControllers
that contain much of the same information.

This includes some changes to the C++ SyncInternalsMessageHandler to
register to observe these counters as well as some new WebUI to display
them.

There is a known bug in this code.  The StausCounter update logic has
not been implemented yet, so the item counts will always be zero.  This
will be fixed in a future CL.

BUG=349301

Review URL: https://codereview.chromium.org/290023003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@274348 0039d316-1c4b-4281-b951-d872f2087c98
parent daeffe6e
......@@ -55,6 +55,7 @@ IDR_SYNC_INTERNALS_SEARCH_JS
IDR_SYNC_INTERNALS_SYNC_LOG_JS
IDR_SYNC_INTERNALS_SYNC_NODE_BROWSER_JS
IDR_SYNC_INTERNALS_SYNC_SEARCH_JS
IDR_SYNC_INTERNALS_TYPES_JS
IDR_THROBBER
IDR_TRANSLATE_JS
IDR_WEBUI_I18N_PROCESS_JS
......
......@@ -53,6 +53,14 @@ cr.define('chrome.sync', function() {
chrome.send('registerForEvents');
};
/**
* Registers to receive a stream of status counter update events
* chrome.sync.dispatchEvent().
*/
var registerForPerTypeCounters = function() {
chrome.send('registerForPerTypeCounters');
}
/**
* Asks the browser to refresh our snapshot of sync state. Should result
* in an onAboutInfoUpdated event being emitted.
......@@ -111,6 +119,7 @@ cr.define('chrome.sync', function() {
getAllNodes: getAllNodes,
getAllNodesCallback: getAllNodesCallback,
registerForEvents: registerForEvents,
registerForPerTypeCounters: registerForPerTypeCounters,
requestUpdatedAboutInfo: requestUpdatedAboutInfo,
requestListOfTypes: requestListOfTypes,
};
......
......@@ -9,6 +9,7 @@ chrome/test/functional/special_tabs.py. -->
<link rel="stylesheet" href="chrome://resources/css/tabs.css">
<link rel="stylesheet" href="chrome://resources/css/tree.css">
<link rel="stylesheet" href="about.css">
<link rel="stylesheet" href="types.css">
<link rel="stylesheet" href="sync_search.css">
<link rel="stylesheet" href="sync_node_browser.css">
......@@ -34,6 +35,7 @@ chrome/test/functional/special_tabs.py. -->
<script src="chrome://resources/js/util.js"></script>
<script src="chrome://sync-internals/chrome_sync.js"></script>
<script src="chrome://sync-internals/about.js"></script>
<script src="chrome://sync-internals/types.js"></script>
<script src="chrome://sync-internals/sync_log.js"></script>
<script src="chrome://sync-internals/sync_node_browser.js"></script>
<script src="chrome://sync-internals/sync_search.js"></script>
......@@ -52,6 +54,7 @@ chrome/test/functional/special_tabs.py. -->
<tabbox id="sync-page">
<tabs>
<tab id="sync-about-tab">About</tab>
<tab id="sync-types-tab">Types</tab>
<tab id="sync-data-tab">Data</tab>
<tab id="sync-events-tab">Events</tab>
<tab id="sync-browser-tab">Sync Node Browser</tab>
......@@ -61,6 +64,9 @@ chrome/test/functional/special_tabs.py. -->
<tabpanel>
<include src="about.html" />
</tabpanel>
<tabpanel>
<include src="types.html" />
</tabpanel>
<tabpanel>
<include src="data.html" />
</tabpanel>
......
/* Copyright 2014 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#type-counters-table {
table-layout: fixed;
}
#type-counters-table th {
max-width: 80px;
width: 80px;
}
#type-counters-table th.type {
max-width: 200px;
width: 200px;
}
#type-counters-table tr:nth-child(odd) {
background: rgb(239, 243, 255);
}
<div id="type-counters-container-wrapper" jsskip="true">
<div class="section">
<h2>Type Counters</h2>
<table id="type-counters-table">
<thead>
<tr>
<th class='type'>Type</th>
<th>Total Entries</th>
<th>Updates Received</th>
<th>Reflected Updates Received</th>
<th>Tombstone Updates Received</th>
<th>Updates Applied</th>
<th>Hierarchy Conflict Application Failures</th>
<th>Encryption Conflict Application Failures</th>
<th>Server Overwrite Conflicts</th>
<th>Local Overwrite Conflicts</th>
<th>Commit Attempts</th>
<th>Commit Successes</th>
<th>Commit Conflicts</th>
<th>Commit Errors</th>
</tr>
</thead>
<tbody>
<tr jsselect="rows">
<td jscontent="type"></td>
<td jscontent="counters.numEntries || 0">0</td>
<td jscontent="counters.numUpdatesReceived || 0">0</td>
<td jscontent="counters.numReflectedUpdatesReceived || 0">0</td>
<td jscontent="counters.numTombstoneUpdatesReceived || 0">0</td>
<td jscontent="counters.numUpdatesApplied || 0">0</td>
<td jscontent="counters.numHierarchyConflictApplicationFailures || 0">0</td>
<td jscontent="counters.numEncryptionConflictApplicationFailures || 0">0</td>
<td jscontent="counters.numServerOverwrites || 0">0</td>
<td jscontent="counters.numLocalOverwrites || 0">0</td>
<td jscontent="counters.numCommitsAttempted || 0">0</td>
<td jscontent="counters.numCommitsSuccess || 0">0</td>
<td jscontent="counters.numCommitsConflict || 0">0</td>
<td jscontent="counters.numCommitsError || 0">0</td>
</tr>
</tbody>
</table>
</div>
</div>
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
cr.define('chrome.sync.types', function() {
var typeCountersMap = {};
/**
* Redraws the counters table taking advantage of the most recent
* available information.
*
* Makes use of typeCountersMap, which is defined in the containing scope.
*/
var refreshTypeCountersDisplay = function() {
var typeCountersArray = [];
// Transform our map into an array to make jstemplate happy.
Object.keys(typeCountersMap).sort().forEach(function(t) {
typeCountersArray.push({
type: t,
counters: typeCountersMap[t],
});
});
jstProcess(
new JsEvalContext({ rows: typeCountersArray }),
$('type-counters-table'));
};
/**
* Helps to initialize the table by picking up where initTypeCounters() left
* off. That function registers this listener and requests that this event
* be emitted.
*
* @param {!Object} e An event containing the list of known sync types.
*/
var onReceivedListOfTypes = function(e) {
var types = e.details.types;
types.map(function(type) {
if (!typeCountersMap.hasOwnProperty(type)) {
typeCountersMap[type] = {};
}
});
chrome.sync.events.removeEventListener(
'onReceivedListOfTypes',
onReceivedListOfTypes);
refreshTypeCountersDisplay();
};
/**
* Callback for receipt of updated per-type counters.
*
* @param {!Object} e An event containing an updated counter.
*/
var onCountersUpdated = function(e) {
var details = e.details;
var modelType = details.modelType;
var counters = details.counters;
if (typeCountersMap.hasOwnProperty(modelType))
for (k in counters) {
typeCountersMap[modelType][k] = counters[k];
}
refreshTypeCountersDisplay();
};
/**
* Initializes state and callbacks for the per-type counters and status UI.
*/
var initTypeCounters = function() {
chrome.sync.events.addEventListener(
'onCountersUpdated',
onCountersUpdated);
chrome.sync.events.addEventListener(
'onReceivedListOfTypes',
onReceivedListOfTypes);
chrome.sync.requestListOfTypes();
chrome.sync.registerForPerTypeCounters();
};
var onLoad = function() {
initTypeCounters();
};
return {
onLoad: onLoad
};
});
document.addEventListener('DOMContentLoaded', chrome.sync.types.onLoad, false);
......@@ -14,6 +14,7 @@
<include name="IDR_SYNC_INTERNALS_INDEX_HTML" file="sync_internals/sync_index.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_SYNC_INTERNALS_INDEX_JS" file="sync_internals/sync_index.js" type="BINDATA" />
<include name="IDR_SYNC_INTERNALS_CHROME_SYNC_JS" file="sync_internals/chrome_sync.js" type="BINDATA" />
<include name="IDR_SYNC_INTERNALS_TYPES_JS" file="sync_internals/types.js" type="BINDATA" />
<include name="IDR_SYNC_INTERNALS_SYNC_LOG_JS" file="sync_internals/sync_log.js" type="BINDATA" />
<include name="IDR_SYNC_INTERNALS_SYNC_NODE_BROWSER_JS" file="sync_internals/sync_node_browser.js" type="BINDATA" />
<include name="IDR_SYNC_INTERNALS_SYNC_SEARCH_JS" file="sync_internals/sync_search.js" type="BINDATA" />
......
......@@ -3,8 +3,9 @@ include_rules = [
"+js2webui/chrome/test/data",
"+sync/js",
"+sync/internal_api/public/util/weak_handle.h",
"+sync/internal_api/public/sessions",
"+sync/internal_api/public/events",
"+sync/internal_api/public/util/weak_handle.h",
# Other libraries.
"+components/onc",
......
......@@ -14,6 +14,9 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_ui.h"
#include "sync/internal_api/public/events/protocol_event.h"
#include "sync/internal_api/public/sessions/commit_counters.h"
#include "sync/internal_api/public/sessions/status_counters.h"
#include "sync/internal_api/public/sessions/update_counters.h"
#include "sync/internal_api/public/util/weak_handle.h"
#include "sync/js/js_event_details.h"
......@@ -23,7 +26,9 @@ using syncer::WeakHandle;
SyncInternalsMessageHandler::SyncInternalsMessageHandler()
: is_registered_(false),
weak_ptr_factory_(this) {}
is_registered_for_counters_(false),
weak_ptr_factory_(this) {
}
SyncInternalsMessageHandler::~SyncInternalsMessageHandler() {
if (js_controller_)
......@@ -34,6 +39,10 @@ SyncInternalsMessageHandler::~SyncInternalsMessageHandler() {
service->RemoveObserver(this);
service->RemoveProtocolEventObserver(this);
}
if (service && is_registered_for_counters_) {
service->RemoveTypeDebugInfoObserver(this);
}
}
void SyncInternalsMessageHandler::RegisterMessages() {
......@@ -44,6 +53,11 @@ void SyncInternalsMessageHandler::RegisterMessages() {
base::Bind(&SyncInternalsMessageHandler::HandleRegisterForEvents,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"registerForPerTypeCounters",
base::Bind(&SyncInternalsMessageHandler::HandleRegisterForPerTypeCounters,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"requestUpdatedAboutInfo",
base::Bind(&SyncInternalsMessageHandler::HandleRequestUpdatedAboutInfo,
......@@ -77,6 +91,21 @@ void SyncInternalsMessageHandler::HandleRegisterForEvents(
}
}
void SyncInternalsMessageHandler::HandleRegisterForPerTypeCounters(
const base::ListValue* args) {
DCHECK(args->empty());
ProfileSyncService* service = GetProfileSyncService();
if (service && !is_registered_for_counters_) {
service->AddTypeDebugInfoObserver(this);
is_registered_for_counters_ = true;
} else {
// Re-register to ensure counters get re-emitted.
service->RemoveTypeDebugInfoObserver(this);
service->AddTypeDebugInfoObserver(this);
}
}
void SyncInternalsMessageHandler::HandleRequestUpdatedAboutInfo(
const base::ListValue* args) {
DCHECK(args->empty());
......@@ -137,6 +166,37 @@ void SyncInternalsMessageHandler::OnProtocolEvent(
*value);
}
void SyncInternalsMessageHandler::OnCommitCountersUpdated(
syncer::ModelType type,
const syncer::CommitCounters& counters) {
EmitCounterUpdate(type, "commit", counters.ToValue());
}
void SyncInternalsMessageHandler::OnUpdateCountersUpdated(
syncer::ModelType type,
const syncer::UpdateCounters& counters) {
EmitCounterUpdate(type, "update", counters.ToValue());
}
void SyncInternalsMessageHandler::OnStatusCountersUpdated(
syncer::ModelType type,
const syncer::StatusCounters& counters) {
EmitCounterUpdate(type, "status", counters.ToValue());
}
void SyncInternalsMessageHandler::EmitCounterUpdate(
syncer::ModelType type,
const std::string& counter_type,
scoped_ptr<base::DictionaryValue> value) {
scoped_ptr<base::DictionaryValue> details(new base::DictionaryValue());
details->SetString("modelType", ModelTypeToString(type));
details->SetString("counterType", counter_type);
details->Set("counters", value.release());
web_ui()->CallJavascriptFunction("chrome.sync.dispatchEvent",
base::StringValue("onCountersUpdated"),
*details);
}
void SyncInternalsMessageHandler::HandleJsEvent(
const std::string& name,
const JsEventDetails& details) {
......@@ -163,4 +223,3 @@ ProfileSyncService* SyncInternalsMessageHandler::GetProfileSyncService() {
ProfileSyncServiceFactory* factory = ProfileSyncServiceFactory::GetInstance();
return factory->GetForProfile(profile->GetOriginalProfile());
}
......@@ -15,17 +15,18 @@
#include "chrome/browser/sync/profile_sync_service_observer.h"
#include "chrome/browser/sync/protocol_event_observer.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "sync/internal_api/public/sessions/type_debug_info_observer.h"
#include "sync/js/js_controller.h"
#include "sync/js/js_event_handler.h"
class ProfileSyncService;
// The implementation for the chrome://sync-internals page.
class SyncInternalsMessageHandler
: public content::WebUIMessageHandler,
public syncer::JsEventHandler,
public ProfileSyncServiceObserver,
public browser_sync::ProtocolEventObserver {
class SyncInternalsMessageHandler : public content::WebUIMessageHandler,
public syncer::JsEventHandler,
public ProfileSyncServiceObserver,
public browser_sync::ProtocolEventObserver,
public syncer::TypeDebugInfoObserver {
public:
SyncInternalsMessageHandler();
virtual ~SyncInternalsMessageHandler();
......@@ -35,6 +36,9 @@ class SyncInternalsMessageHandler
// Sets up observers to receive events and forward them to the UI.
void HandleRegisterForEvents(const base::ListValue* args);
// Sets up observers to receive per-type counters and forward them to the UI.
void HandleRegisterForPerTypeCounters(const base::ListValue* args);
// Fires an event to send updated info back to the page.
void HandleRequestUpdatedAboutInfo(const base::ListValue* args);
......@@ -58,6 +62,26 @@ class SyncInternalsMessageHandler
// ProtocolEventObserver implementation.
virtual void OnProtocolEvent(const syncer::ProtocolEvent& e) OVERRIDE;
// TypeDebugInfoObserver implementation.
virtual void OnCommitCountersUpdated(
syncer::ModelType type,
const syncer::CommitCounters& counters) OVERRIDE;
virtual void OnUpdateCountersUpdated(
syncer::ModelType type,
const syncer::UpdateCounters& counters) OVERRIDE;
virtual void OnStatusCountersUpdated(
syncer::ModelType type,
const syncer::StatusCounters& counters) OVERRIDE;
// Helper to emit counter updates.
//
// Used in implementation of On*CounterUpdated methods. Emits the given
// dictionary value with additional data to specify the model type and
// counter type.
void EmitCounterUpdate(syncer::ModelType type,
const std::string& counter_type,
scoped_ptr<base::DictionaryValue> value);
private:
// Fetches updated aboutInfo and sends it to the page in the form of an
// onAboutInfoUpdated event.
......@@ -70,6 +94,10 @@ class SyncInternalsMessageHandler
// A flag used to prevent double-registration with ProfileSyncService.
bool is_registered_;
// A flag used to prevent double-registration as TypeDebugInfoObserver with
// ProfileSyncService.
bool is_registered_for_counters_;
base::WeakPtrFactory<SyncInternalsMessageHandler> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(SyncInternalsMessageHandler);
......
......@@ -24,6 +24,7 @@ content::WebUIDataSource* CreateSyncInternalsHTMLSource() {
source->AddResourcePath("sync_index.js", IDR_SYNC_INTERNALS_INDEX_JS);
source->AddResourcePath("chrome_sync.js",
IDR_SYNC_INTERNALS_CHROME_SYNC_JS);
source->AddResourcePath("types.js", IDR_SYNC_INTERNALS_TYPES_JS);
source->AddResourcePath("sync_log.js", IDR_SYNC_INTERNALS_SYNC_LOG_JS);
source->AddResourcePath("sync_node_browser.js",
IDR_SYNC_INTERNALS_SYNC_NODE_BROWSER_JS);
......
......@@ -10,13 +10,13 @@
namespace syncer {
StatusCounters::StatusCounters()
: num_total_entries(0) {}
: num_entries(0) {}
StatusCounters::~StatusCounters() {}
scoped_ptr<base::DictionaryValue> StatusCounters::ToValue() const {
scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
value->SetInteger("numTotalEntries", num_total_entries);
value->SetInteger("numEntries", num_entries);
return value.Pass();
}
......
......@@ -19,7 +19,7 @@ struct SYNC_EXPORT_PRIVATE StatusCounters {
scoped_ptr<base::DictionaryValue> ToValue() const;
std::string ToString() const;
size_t num_total_entries;
size_t num_entries;
};
} // namespace syncer
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment