[Sync] Refactor JS event handling to reduce boilerplate

Split event handlers/routers to handle events and message replies
separately.

Reduce JS boilerplate.

Use OVERRIDE where appropriate.

BUG=
TEST=

Review URL: http://codereview.chromium.org/6930030

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@84357 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/resources/sync_internals/chrome_sync.js b/chrome/browser/resources/sync_internals/chrome_sync.js
index 4b452742..8d2dcbf 100644
--- a/chrome/browser/resources/sync_internals/chrome_sync.js
+++ b/chrome/browser/resources/sync_internals/chrome_sync.js
@@ -46,7 +46,7 @@
 
 // Fires the event.  Called by the actual event callback.  Any
 // exceptions thrown by a listener are caught and logged.
-Event.prototype.dispatch_ = function() {
+Event.prototype.fire = function() {
   var args = Array.prototype.slice.call(arguments);
   for (var i = 0; i < this.listeners_.length; i++) {
     try {
@@ -62,182 +62,78 @@
   }
 };
 
-// Service events.
-chrome.sync.onSyncServiceStateChanged = new Event();
+var events = [
+  // Service events.
+  'onSyncServiceStateChanged',
 
-// Notifier events.
-chrome.sync.onSyncNotificationStateChange = new Event();
-chrome.sync.onSyncIncomingNotification = new Event();
+  // Notifier events.
+  'onSyncNotificationStateChange',
+  'onSyncIncomingNotification',
 
-// Manager events.
-chrome.sync.onChangesApplied = new Event();
-chrome.sync.onChangesComplete = new Event();
-chrome.sync.onSyncCycleCompleted = new Event();
-chrome.sync.onAuthError = new Event();
-chrome.sync.onUpdatedToken = new Event();
-chrome.sync.onPassphraseRequired = new Event();
-chrome.sync.onPassphraseAccepted = new Event();
-chrome.sync.onEncryptionComplete = new Event();
-chrome.sync.onMigrationNeededForTypes = new Event();
-chrome.sync.onInitializationComplete = new Event();
-chrome.sync.onPaused = new Event();
-chrome.sync.onResumed = new Event();
-chrome.sync.onStopSyncingPermanently = new Event();
-chrome.sync.onClearServerDataSucceeded = new Event();
-chrome.sync.onClearServerDataFailed = new Event();
+  // Manager events.
+  'onChangesApplied',
+  'onChangesComplete',
+  'onSyncCycleCompleted',
+  'onAuthError',
+  'onUpdatedToken',
+  'onPassphraseRequired',
+  'onPassphraseAccepted',
+  'onEncryptionComplete',
+  'onMigrationNeededForTypes',
+  'onInitializationComplete',
+  'onPaused',
+  'onResumed',
+  'onStopSyncingPermanently',
+  'onClearServerDataSucceeded',
+  'onClearServerDataFailed'
+];
 
-function AsyncFunction(name) {
-  this.name_ = name;
-  this.callbacks_ = [];
+for (var i = 0; i < events.length; ++i) {
+  var event = events[i];
+  chrome.sync[event] = new Event();
 }
 
-// Calls the function, assuming the last argument is a callback to be
-// called with the return value.
-AsyncFunction.prototype.call = function() {
-  var args = Array.prototype.slice.call(arguments);
-  this.callbacks_.push(args.pop());
-  chrome.send(this.name_, args);
+function makeAsyncFunction(name) {
+  var callbacks = [];
+
+  // Calls the function, assuming the last argument is a callback to be
+  // called with the return value.
+  var fn = function() {
+    var args = Array.prototype.slice.call(arguments);
+    callbacks.push(args.pop());
+    chrome.send(name, args);
+  };
+
+  // Handle a reply, assuming that messages are processed in FIFO order.
+  // Called by SyncInternalsUI::HandleJsMessageReply().
+  fn.handleReply = function() {
+    var args = Array.prototype.slice.call(arguments);
+    // Remove the callback before we call it since the callback may
+    // throw.
+    var callback = callbacks.shift();
+    callback.apply(null, args);
+  };
+
+  return fn;
 }
 
-// Handle a reply, assuming that messages are processed in FIFO order.
-AsyncFunction.prototype.handleReply = function() {
-  var args = Array.prototype.slice.call(arguments);
-  // Remove the callback before we call it since the callback may
-  // throw.
-  var callback = this.callbacks_.shift();
-  callback.apply(null, args);
-}
+var syncFunctions = [
+  // Sync service functions.
+  'getAboutInfo',
 
-// Sync service functions.
-chrome.sync.getAboutInfo_ = new AsyncFunction('getAboutInfo');
-chrome.sync.getAboutInfo = function(callback) {
-  chrome.sync.getAboutInfo_.call(callback);
-}
+  // Notification functions.
+  'getNotificationState',
+  'getNotificationInfo',
 
-// Notification functions.
-chrome.sync.getNotificationState_ =
-    new AsyncFunction('getNotificationState');
-chrome.sync.getNotificationState = function(callback) {
-  chrome.sync.getNotificationState_.call(callback);
-}
+  // Node lookup functions.
+  'getRootNode',
+  'getNodeById',
+  'findNodesContainingString'
+];
 
-chrome.sync.getNotificationInfo_ =
-    new AsyncFunction('getNotificationInfo');
-chrome.sync.getNotificationInfo = function(callback) {
-  chrome.sync.getNotificationInfo_.call(callback);
-}
-
-// Node lookup functions.
-chrome.sync.getRootNode_ = new AsyncFunction('getRootNode');
-chrome.sync.getRootNode = function(callback) {
-  chrome.sync.getRootNode_.call(callback);
-}
-
-chrome.sync.getNodeById_ = new AsyncFunction('getNodeById');
-chrome.sync.getNodeById = function(id, callback) {
-  chrome.sync.getNodeById_.call(id, callback);
+for (var i = 0; i < syncFunctions.length; ++i) {
+  var syncFunction = syncFunctions[i];
+  chrome.sync[syncFunction] = makeAsyncFunction(syncFunction);
 }
 
 })();
-
-// TODO(akalin): Rewrite the C++ side to not need the handlers below.
-
-// Sync service event handlers.
-
-function onSyncServiceStateChanged() {
-  chrome.sync.onSyncServiceStateChanged.dispatch_();
-}
-
-// Notification event handlers.
-
-function onSyncNotificationStateChange(notificationsEnabled) {
-  chrome.sync.onSyncNotificationStateChange.dispatch_(notificationsEnabled);
-}
-
-function onSyncIncomingNotification(changedTypes) {
-  chrome.sync.onSyncIncomingNotification.dispatch_(changedTypes);
-}
-
-// Sync manager event handlers.
-
-function onChangesApplied(modelType, changes) {
-  chrome.sync.onChangesApplied.dispatch_(modelType, changes);
-}
-
-function onChangesComplete(modelType) {
-  chrome.sync.onChangesComplete.dispatch_(modelType);
-}
-
-function onSyncCycleCompleted(snapshot) {
-  chrome.sync.onSyncCycleCompleted.dispatch_(snapshot);
-}
-
-function onAuthError(authError) {
-  chrome.sync.onAuthError.dispatch_(authError);
-}
-
-function onUpdatedToken(token) {
-  chrome.sync.onUpdatedToken.dispatch_(token);
-}
-
-function onPassphraseRequired(reason) {
-  chrome.sync.onPassphraseRequired.dispatch_(reason);
-}
-
-function onPassphraseAccepted(bootstrapToken) {
-  chrome.sync.onPassphraseAccepted.dispatch_(bootstrapToken);
-}
-
-function onEncryptionComplete(encrypted_types) {
-  chrome.sync.onEncryptionComplete.dispatch_(encrypted_types);
-}
-
-function onMigrationNeededForTypes(model_types) {
-  chrome.sync.onMigrationNeededForTypes.dispatch_(model_types);
-}
-
-function onInitializationComplete() {
-  chrome.sync.onInitializationComplete.dispatch_();
-}
-
-function onPaused() {
-  chrome.sync.onPaused.dispatch_();
-}
-
-function onResumed() {
-  chrome.sync.onResumed.dispatch_();
-}
-
-function onStopSyncingPermanently() {
-  chrome.sync.onStopSyncingPermanently.dispatch_();
-}
-
-function onClearServerDataSucceeded() {
-  chrome.sync.onClearServerDataSucceeded();
-}
-
-function onClearServerDataFailed() {
-  chrome.sync.onClearServerDataFailed();
-}
-
-// Function reply handlers.
-
-function onGetAboutInfoFinished(aboutInfo) {
-  chrome.sync.getAboutInfo_.handleReply(aboutInfo);
-}
-
-function onGetNotificationStateFinished(notificationState) {
-  chrome.sync.getNotificationState_.handleReply(notificationState);
-}
-
-function onGetRootNodeFinished(rootNode) {
-  chrome.sync.getRootNode_.handleReply(rootNode);
-}
-
-function onGetNodeByIdFinished(node) {
-  chrome.sync.getNodeById_.handleReply(node);
-}
-
-function onGetNotificationInfoFinished(notificationInfo) {
-  chrome.sync.getNotificationInfo_.handleReply(notificationInfo);
-}
diff --git a/chrome/browser/sync/README.js b/chrome/browser/sync/README.js
index db12140..d8c86aa 100644
--- a/chrome/browser/sync/README.js
+++ b/chrome/browser/sync/README.js
@@ -5,9 +5,8 @@
 about:sync) interacts with the sync service/backend.
 
 Basically, chrome://sync-internals sends asynchronous messages to the
-sync backend and the sync backend asynchronously raises events to
-chrome://sync-internals, either when replying to messages or when
-something interesting happens.
+sync backend and the sync backend asynchronously raises events and
+message replies to chrome://sync-internals.
 
 Both messages and events have a name and a list of arguments, the
 latter of which is represented by a JsArgList (js_arg_list.h) object,
@@ -19,7 +18,7 @@
 ------------------
 
 chrome://sync-internals is represented by SyncInternalsUI
-(chrome/browser/web_ui/sync_internals_ui.h).  SyncInternalsUI
+(chrome/browser/ui/webui/sync_internals_ui.h).  SyncInternalsUI
 interacts with the sync service via a JsFrontend (js_frontend.h)
 object, which has a ProcessMessage() method.  The JsFrontend can
 handle some messages itself, but it can also delegate the rest to a
@@ -36,14 +35,14 @@
 so that's okay.
 
 SyncInternalsUI is a JsEventHandler (js_event_handler.h), which means
-that it has a HandleJsEvent() method, but JsBackends cannot easily
-access those objects.  Instead, each JsBackend keeps track of its
-parent router, which is a JsEventRouter object (js_event_router.h).
-Basically, a JsEventRouter is another JsBackend object or a JsFrontend
-object.  So an event travels up through the JsEventRouter until it
-reaches the JsFrontend, which knows about the existing JsEventHandlers
-(via AddHandler()/RemoveHandler()) and so can delegate to the right
-one.
+that it has a HandleJsEvent() method and a HandleJsMessageReply()
+method, but JsBackends cannot easily access those objects.  Instead,
+each JsBackend keeps track of its parent router, which is a
+JsEventRouter object (js_event_router.h).  Basically, a JsEventRouter
+is another JsBackend object or a JsFrontend object.  So an event or
+message reply travels up through the JsEventRouter until it reaches
+the JsFrontend, which knows about the existing JsEventHandlers (via
+AddHandler()/RemoveHandler()) and so can delegate to the right one.
 
 A diagram of the flow of a message and its reply:
 
diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc
index 6cf7408..f7df069c 100644
--- a/chrome/browser/sync/engine/syncapi.cc
+++ b/chrome/browser/sync/engine/syncapi.cc
@@ -1404,12 +1404,15 @@
   virtual void OnServerConnectionEvent(const ServerConnectionEvent& event);
 
   // browser_sync::JsBackend implementation.
-  virtual void SetParentJsEventRouter(browser_sync::JsEventRouter* router);
-  virtual void RemoveParentJsEventRouter();
-  virtual const browser_sync::JsEventRouter* GetParentJsEventRouter() const;
-  virtual void ProcessMessage(const std::string& name,
-                              const browser_sync::JsArgList& args,
-                              const browser_sync::JsEventHandler* sender);
+  virtual void SetParentJsEventRouter(
+      browser_sync::JsEventRouter* router) OVERRIDE;
+  virtual void RemoveParentJsEventRouter() OVERRIDE;
+  virtual const browser_sync::JsEventRouter*
+      GetParentJsEventRouter() const OVERRIDE;
+  virtual void ProcessMessage(
+      const std::string& name,
+      const browser_sync::JsArgList& args,
+      const browser_sync::JsEventHandler* sender) OVERRIDE;
 
   ListValue* FindNodesContainingString(const std::string& query);
 
@@ -2627,9 +2630,8 @@
     bool notifications_enabled = allstatus_.status().notifications_enabled;
     ListValue return_args;
     return_args.Append(Value::CreateBooleanValue(notifications_enabled));
-    parent_router_->RouteJsEvent(
-        "onGetNotificationStateFinished",
-        browser_sync::JsArgList(return_args), sender);
+    parent_router_->RouteJsMessageReply(
+        name, browser_sync::JsArgList(return_args), sender);
   } else if (name == "getNotificationInfo") {
     if (!parent_router_) {
       LogNoRouter(name, args);
@@ -2638,8 +2640,8 @@
 
     ListValue return_args;
     return_args.Append(NotificationInfoToValue(notification_info_map_));
-    parent_router_->RouteJsEvent("onGetNotificationInfoFinished",
-        browser_sync::JsArgList(return_args), sender);
+    parent_router_->RouteJsMessageReply(
+        name, browser_sync::JsArgList(return_args), sender);
   } else if (name == "getRootNode") {
     if (!parent_router_) {
       LogNoRouter(name, args);
@@ -2650,24 +2652,22 @@
     root.InitByRootLookup();
     ListValue return_args;
     return_args.Append(root.ToValue());
-    parent_router_->RouteJsEvent(
-        "onGetRootNodeFinished",
-        browser_sync::JsArgList(return_args), sender);
+    parent_router_->RouteJsMessageReply(
+        name, browser_sync::JsArgList(return_args), sender);
   } else if (name == "getNodeById") {
     if (!parent_router_) {
       LogNoRouter(name, args);
       return;
     }
-    parent_router_->RouteJsEvent(
-        "onGetNodeByIdFinished", ProcessGetNodeByIdMessage(args), sender);
+    parent_router_->RouteJsMessageReply(
+        name, ProcessGetNodeByIdMessage(args), sender);
   } else if (name == "findNodesContainingString") {
     if (!parent_router_) {
       LogNoRouter(name, args);
       return;
     }
-    parent_router_->RouteJsEvent(
-        "onFindNodesContainingStringFinished",
-        ProcessFindNodesContainingString(args), sender);
+    parent_router_->RouteJsMessageReply(
+        name, ProcessFindNodesContainingString(args), sender);
   } else {
     VLOG(1) << "Dropping unknown message " << name
               << " with args " << args.ToString();
@@ -2728,7 +2728,7 @@
     args.Append(Value::CreateBooleanValue(notifications_enabled));
     // TODO(akalin): Tidy up grammar in event names.
     parent_router_->RouteJsEvent("onSyncNotificationStateChange",
-                                 browser_sync::JsArgList(args), NULL);
+                                 browser_sync::JsArgList(args));
   }
 }
 
@@ -2769,7 +2769,7 @@
       changed_types->Append(Value::CreateStringValue(model_type_str));
     }
     parent_router_->RouteJsEvent("onSyncIncomingNotification",
-                                 browser_sync::JsArgList(args), NULL);
+                                 browser_sync::JsArgList(args));
   }
 }
 
diff --git a/chrome/browser/sync/engine/syncapi_unittest.cc b/chrome/browser/sync/engine/syncapi_unittest.cc
index 465a6422..6ccf8f0 100644
--- a/chrome/browser/sync/engine/syncapi_unittest.cc
+++ b/chrome/browser/sync/engine/syncapi_unittest.cc
@@ -807,18 +807,19 @@
     false_args.Append(Value::CreateBooleanValue(false));
 
     EXPECT_CALL(event_router,
-                RouteJsEvent("onGetNotificationStateFinished",
-                             HasArgsAsList(false_args), &event_handler));
+                RouteJsMessageReply("getNotificationState",
+                                    HasArgsAsList(false_args),
+                                    &event_handler));
 
     js_backend->SetParentJsEventRouter(&event_router);
 
     // This message should be dropped.
     js_backend->ProcessMessage("unknownMessage",
-                                 kNoArgs, &event_handler);
+                               kNoArgs, &event_handler);
 
     // This should trigger the reply.
     js_backend->ProcessMessage("getNotificationState",
-                                 kNoArgs, &event_handler);
+                               kNoArgs, &event_handler);
 
     js_backend->RemoveParentJsEventRouter();
   }
@@ -828,9 +829,9 @@
   {
     StrictMock<MockJsEventHandler> event_handler;
     js_backend->ProcessMessage("unknownMessage",
-                                 kNoArgs, &event_handler);
+                               kNoArgs, &event_handler);
     js_backend->ProcessMessage("getNotificationState",
-                                 kNoArgs, &event_handler);
+                               kNoArgs, &event_handler);
   }
 }
 
@@ -845,8 +846,8 @@
   JsArgList return_args;
 
   EXPECT_CALL(event_router,
-              RouteJsEvent("onGetRootNodeFinished", _, &event_handler)).
-      WillOnce(SaveArg<1>(&return_args));
+              RouteJsMessageReply("getRootNode", _, &event_handler))
+      .WillOnce(SaveArg<1>(&return_args));
 
   js_backend->SetParentJsEventRouter(&event_router);
 
@@ -896,7 +897,7 @@
   JsArgList return_args;
 
   EXPECT_CALL(event_router,
-              RouteJsEvent("onGetNodeByIdFinished", _, &event_handler))
+              RouteJsMessageReply("getNodeById", _, &event_handler))
       .Times(2).WillRepeatedly(SaveArg<1>(&return_args));
 
   js_backend->SetParentJsEventRouter(&event_router);
@@ -932,8 +933,8 @@
   null_args.Append(Value::CreateNullValue());
 
   EXPECT_CALL(event_router,
-              RouteJsEvent("onGetNodeByIdFinished",
-                           HasArgsAsList(null_args), &event_handler))
+              RouteJsMessageReply("getNodeById", HasArgsAsList(null_args),
+                                  &event_handler))
       .Times(5);
 
   js_backend->SetParentJsEventRouter(&event_router);
@@ -982,10 +983,10 @@
 
   EXPECT_CALL(event_router,
               RouteJsEvent("onSyncNotificationStateChange",
-                           HasArgsAsList(true_args), NULL));
+                           HasArgsAsList(true_args)));
   EXPECT_CALL(event_router,
               RouteJsEvent("onSyncNotificationStateChange",
-                           HasArgsAsList(false_args), NULL));
+                           HasArgsAsList(false_args)));
 
   browser_sync::JsBackend* js_backend = sync_manager_.GetJsBackend();
 
@@ -1028,7 +1029,7 @@
 
   EXPECT_CALL(event_router,
               RouteJsEvent("onSyncIncomingNotification",
-                           HasArgsAsList(expected_args), NULL));
+                           HasArgsAsList(expected_args)));
 
   browser_sync::JsBackend* js_backend = sync_manager_.GetJsBackend();
 
diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc
index 782d9222..d039471 100644
--- a/chrome/browser/sync/glue/sync_backend_host.cc
+++ b/chrome/browser/sync/glue/sync_backend_host.cc
@@ -1024,11 +1024,19 @@
 }
 
 void SyncBackendHost::Core::RouteJsEvent(
+    const std::string& name, const JsArgList& args) {
+  host_->frontend_loop_->PostTask(
+      FROM_HERE, NewRunnableMethod(
+          this, &Core::RouteJsEventOnFrontendLoop, name, args));
+}
+
+void SyncBackendHost::Core::RouteJsMessageReply(
     const std::string& name, const JsArgList& args,
     const JsEventHandler* target) {
   host_->frontend_loop_->PostTask(
       FROM_HERE, NewRunnableMethod(
-          this, &Core::RouteJsEventOnFrontendLoop, name, args, target));
+          this, &Core::RouteJsMessageReplyOnFrontendLoop,
+          name, args, target));
 }
 
 void SyncBackendHost::Core::HandleStopSyncingPermanentlyOnFrontendLoop() {
@@ -1061,6 +1069,16 @@
 }
 
 void SyncBackendHost::Core::RouteJsEventOnFrontendLoop(
+    const std::string& name, const JsArgList& args) {
+  if (!host_ || !parent_router_)
+    return;
+
+  DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
+
+  parent_router_->RouteJsEvent(name, args);
+}
+
+void SyncBackendHost::Core::RouteJsMessageReplyOnFrontendLoop(
     const std::string& name, const JsArgList& args,
     const JsEventHandler* target) {
   if (!host_ || !parent_router_)
@@ -1068,7 +1086,7 @@
 
   DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
 
-  parent_router_->RouteJsEvent(name, args, target);
+  parent_router_->RouteJsMessageReply(name, args, target);
 }
 
 void SyncBackendHost::Core::StartSavingChanges() {
diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h
index b36adea..6002dae 100644
--- a/chrome/browser/sync/glue/sync_backend_host.h
+++ b/chrome/browser/sync/glue/sync_backend_host.h
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#include "base/compiler_specific.h"
 #include "base/file_path.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/ref_counted.h"
@@ -292,16 +293,18 @@
         const syncable::ModelTypeSet& encrypted_types);
 
     // JsBackend implementation.
-    virtual void SetParentJsEventRouter(JsEventRouter* router);
-    virtual void RemoveParentJsEventRouter();
-    virtual const JsEventRouter* GetParentJsEventRouter() const;
+    virtual void SetParentJsEventRouter(JsEventRouter* router) OVERRIDE;
+    virtual void RemoveParentJsEventRouter() OVERRIDE;
+    virtual const JsEventRouter* GetParentJsEventRouter() const OVERRIDE;
     virtual void ProcessMessage(const std::string& name, const JsArgList& args,
-                                const JsEventHandler* sender);
+                                const JsEventHandler* sender) OVERRIDE;
 
     // JsEventRouter implementation.
     virtual void RouteJsEvent(const std::string& event_name,
-                              const JsArgList& args,
-                              const JsEventHandler* dst);
+                              const JsArgList& args) OVERRIDE;
+    virtual void RouteJsMessageReply(const std::string& event_name,
+                                     const JsArgList& args,
+                                     const JsEventHandler* target) OVERRIDE;
 
     struct DoInitializeOptions {
       DoInitializeOptions(
@@ -485,8 +488,11 @@
     void HandleInitalizationCompletedOnFrontendLoop();
 
     void RouteJsEventOnFrontendLoop(
+        const std::string& name, const JsArgList& args);
+
+    void RouteJsMessageReplyOnFrontendLoop(
         const std::string& name, const JsArgList& args,
-        const JsEventHandler* dst);
+        const JsEventHandler* target);
 
     void FinishConfigureDataTypesOnFrontendLoop();
 
diff --git a/chrome/browser/sync/js_event_handler.h b/chrome/browser/sync/js_event_handler.h
index 0e8b301..058df9e 100644
--- a/chrome/browser/sync/js_event_handler.h
+++ b/chrome/browser/sync/js_event_handler.h
@@ -21,6 +21,9 @@
   virtual void HandleJsEvent(
       const std::string& name, const JsArgList& args) = 0;
 
+  virtual void HandleJsMessageReply(
+      const std::string& name, const JsArgList& args) = 0;
+
  protected:
   virtual ~JsEventHandler() {}
 };
diff --git a/chrome/browser/sync/js_event_handler_list.cc b/chrome/browser/sync/js_event_handler_list.cc
index abc619385..3629fb2 100644
--- a/chrome/browser/sync/js_event_handler_list.cc
+++ b/chrome/browser/sync/js_event_handler_list.cc
@@ -76,18 +76,19 @@
 }
 
 void JsEventHandlerList::RouteJsEvent(const std::string& name,
-                                      const JsArgList& args,
-                                      const JsEventHandler* target) {
-  if (target) {
-    JsEventHandler* non_const_target(const_cast<JsEventHandler*>(target));
-    if (handlers_.HasObserver(non_const_target)) {
-      non_const_target->HandleJsEvent(name, args);
-    } else {
-      VLOG(1) << "Unknown target; dropping event " << name
-              << " with args " << args.ToString();
-    }
+                                      const JsArgList& args) {
+  FOR_EACH_OBSERVER(JsEventHandler, handlers_, HandleJsEvent(name, args));
+}
+
+void JsEventHandlerList::RouteJsMessageReply(const std::string& name,
+                                             const JsArgList& args,
+                                             const JsEventHandler* target) {
+  JsEventHandler* non_const_target(const_cast<JsEventHandler*>(target));
+  if (handlers_.HasObserver(non_const_target)) {
+    non_const_target->HandleJsMessageReply(name, args);
   } else {
-    FOR_EACH_OBSERVER(JsEventHandler, handlers_, HandleJsEvent(name, args));
+    VLOG(1) << "Unknown target; dropping message reply " << name
+            << " with args " << args.ToString();
   }
 }
 
diff --git a/chrome/browser/sync/js_event_handler_list.h b/chrome/browser/sync/js_event_handler_list.h
index 2d5cf9e0..a1f0946 100644
--- a/chrome/browser/sync/js_event_handler_list.h
+++ b/chrome/browser/sync/js_event_handler_list.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/basictypes.h"
+#include "base/compiler_specific.h"
 #include "base/observer_list.h"
 #include "chrome/browser/sync/js_arg_list.h"
 #include "chrome/browser/sync/js_event_router.h"
@@ -38,17 +39,19 @@
   // JsFrontend implementation.  Routes messages to any attached
   // backend; if there is none, queues up the message for processing
   // when the next backend is attached.
-  virtual void AddHandler(JsEventHandler* handler);
-  virtual void RemoveHandler(JsEventHandler* handler);
+  virtual void AddHandler(JsEventHandler* handler) OVERRIDE;
+  virtual void RemoveHandler(JsEventHandler* handler) OVERRIDE;
   virtual void ProcessMessage(
       const std::string& name, const JsArgList& args,
-      const JsEventHandler* sender);
+      const JsEventHandler* sender) OVERRIDE;
 
   // JsEventRouter implementation.  Routes the event to the
   // appropriate handler(s).
   virtual void RouteJsEvent(const std::string& name,
-                            const JsArgList& args,
-                            const JsEventHandler* target);
+                            const JsArgList& args) OVERRIDE;
+  virtual void RouteJsMessageReply(const std::string& name,
+                                   const JsArgList& args,
+                                   const JsEventHandler* target) OVERRIDE;
 
  private:
   // A struct used to hold the arguments to ProcessMessage() for
diff --git a/chrome/browser/sync/js_event_handler_list_unittest.cc b/chrome/browser/sync/js_event_handler_list_unittest.cc
index 449e70f..fee0dc3a 100644
--- a/chrome/browser/sync/js_event_handler_list_unittest.cc
+++ b/chrome/browser/sync/js_event_handler_list_unittest.cc
@@ -37,13 +37,13 @@
   EXPECT_CALL(backend, ProcessMessage("test1", HasArgs(args2), &handler1));
   EXPECT_CALL(backend, ProcessMessage("test2", HasArgs(args1), &handler2));
 
-  EXPECT_CALL(handler1, HandleJsEvent("reply1", HasArgs(args2)));
-  EXPECT_CALL(handler1, HandleJsEvent("allreply", HasArgs(args1)));
+  EXPECT_CALL(handler1, HandleJsMessageReply("reply1", HasArgs(args2)));
+  EXPECT_CALL(handler1, HandleJsEvent("event", HasArgs(args1)));
 
-  EXPECT_CALL(handler2, HandleJsEvent("reply2", HasArgs(args1)));
-  EXPECT_CALL(handler2, HandleJsEvent("allreply", HasArgs(args1)));
-  EXPECT_CALL(handler2, HandleJsEvent("anotherreply2", HasArgs(args2)));
-  EXPECT_CALL(handler2, HandleJsEvent("anotherallreply", HasArgs(args2)));
+  EXPECT_CALL(handler2, HandleJsMessageReply("reply2", HasArgs(args1)));
+  EXPECT_CALL(handler2, HandleJsEvent("event", HasArgs(args1)));
+  EXPECT_CALL(handler2, HandleJsMessageReply("anotherreply2", HasArgs(args2)));
+  EXPECT_CALL(handler2, HandleJsEvent("anotherevent", HasArgs(args2)));
 
   event_handlers.SetBackend(&backend);
 
@@ -52,23 +52,25 @@
 
   event_handlers.ProcessMessage("test1", args2, &handler1);
 
-  event_handlers.RouteJsEvent("reply2", args1, &handler2);
-  event_handlers.RouteJsEvent("reply1", args2, &handler1);
-  event_handlers.RouteJsEvent("allreply", args1, NULL);
+  event_handlers.RouteJsMessageReply("reply2", args1, &handler2);
+  event_handlers.RouteJsMessageReply("reply1", args2, &handler1);
+  event_handlers.RouteJsEvent("event", args1);
 
   event_handlers.RemoveHandler(&handler1);
 
   event_handlers.ProcessMessage("test2", args1, &handler2);
 
-  event_handlers.RouteJsEvent("droppedreply1", args1, &handler1);
-  event_handlers.RouteJsEvent("anotherreply2", args2, &handler2);
-  event_handlers.RouteJsEvent("anotherallreply", args2, NULL);
+  event_handlers.RouteJsMessageReply("droppedreply1", args1, &handler1);
+  event_handlers.RouteJsMessageReply("anotherreply2", args2, &handler2);
+  event_handlers.RouteJsEvent("anotherevent", args2);
 
   event_handlers.RemoveHandler(&handler2);
 
-  event_handlers.RouteJsEvent("anotherdroppedreply1", args1, &handler1);
-  event_handlers.RouteJsEvent("anotheranotherreply2", args2, &handler2);
-  event_handlers.RouteJsEvent("droppedallreply", args2, NULL);
+  event_handlers.RouteJsMessageReply("anotherdroppedreply1",
+                                     args1, &handler1);
+  event_handlers.RouteJsMessageReply("anotheranotherreply2",
+                                     args2, &handler2);
+  event_handlers.RouteJsEvent("droppedevent", args2);
 
   // Let destructor of |event_handlers| call RemoveBackend().
 }
diff --git a/chrome/browser/sync/js_event_router.h b/chrome/browser/sync/js_event_router.h
index be783f4c..9fad052 100644
--- a/chrome/browser/sync/js_event_router.h
+++ b/chrome/browser/sync/js_event_router.h
@@ -20,13 +20,15 @@
 // JsEventRouters.
 class JsEventRouter {
  public:
-  // If |target| is NULL that means the event is intended for every
-  // handler.  Otherwise the event is meant for the given target only.
+  virtual void RouteJsEvent(
+      const std::string& name, const JsArgList& args) = 0;
+
   // |target| is const because it shouldn't be used except by the
   // router that directly knows about it (which can then safely cast
   // away the constness).
-  virtual void RouteJsEvent(const std::string& name, const JsArgList& args,
-                            const JsEventHandler* target) = 0;
+  virtual void RouteJsMessageReply(
+      const std::string& name, const JsArgList& args,
+      const JsEventHandler* target) = 0;
 
  protected:
   virtual ~JsEventRouter() {}
diff --git a/chrome/browser/sync/js_sync_manager_observer.cc b/chrome/browser/sync/js_sync_manager_observer.cc
index a86178a..a58c586e 100644
--- a/chrome/browser/sync/js_sync_manager_observer.cc
+++ b/chrome/browser/sync/js_sync_manager_observer.cc
@@ -35,8 +35,7 @@
   for (int i = 0; i < change_count; ++i) {
     change_values->Append(changes[i].ToValue(trans));
   }
-  parent_router_->RouteJsEvent("onChangesApplied",
-                               JsArgList(return_args), NULL);
+  parent_router_->RouteJsEvent("onChangesApplied", JsArgList(return_args));
 }
 
 void JsSyncManagerObserver::OnChangesComplete(
@@ -44,31 +43,27 @@
   ListValue return_args;
   return_args.Append(Value::CreateStringValue(
       syncable::ModelTypeToString(model_type)));
-  parent_router_->RouteJsEvent("onChangesComplete",
-                               JsArgList(return_args), NULL);
+  parent_router_->RouteJsEvent("onChangesComplete", JsArgList(return_args));
 }
 
 void JsSyncManagerObserver::OnSyncCycleCompleted(
     const sessions::SyncSessionSnapshot* snapshot) {
   ListValue return_args;
   return_args.Append(snapshot->ToValue());
-  parent_router_->RouteJsEvent("onSyncCycleCompleted",
-                               JsArgList(return_args), NULL);
+  parent_router_->RouteJsEvent("onSyncCycleCompleted", JsArgList(return_args));
 }
 
 void JsSyncManagerObserver::OnAuthError(
     const GoogleServiceAuthError& auth_error) {
   ListValue return_args;
   return_args.Append(auth_error.ToValue());
-  parent_router_->RouteJsEvent("onAuthError",
-                               JsArgList(return_args), NULL);
+  parent_router_->RouteJsEvent("onAuthError", JsArgList(return_args));
 }
 
 void JsSyncManagerObserver::OnUpdatedToken(const std::string& token) {
   ListValue return_args;
   return_args.Append(Value::CreateStringValue("<redacted>"));
-  parent_router_->RouteJsEvent("onUpdatedToken",
-                               JsArgList(return_args), NULL);
+  parent_router_->RouteJsEvent("onUpdatedToken", JsArgList(return_args));
 }
 
 void JsSyncManagerObserver::OnPassphraseRequired(
@@ -78,7 +73,7 @@
   return_args.Append(Value::CreateStringValue(
       sync_api::PassphraseRequiredReasonToString(reason)));
   parent_router_->RouteJsEvent("onPassphraseRequired",
-                               JsArgList(return_args), NULL);
+                               JsArgList(return_args));
 }
 
 void JsSyncManagerObserver::OnPassphraseAccepted(
@@ -86,7 +81,7 @@
   ListValue return_args;
   return_args.Append(Value::CreateStringValue("<redacted>"));
   parent_router_->RouteJsEvent("onPassphraseAccepted",
-                               JsArgList(return_args), NULL);
+                               JsArgList(return_args));
 }
 
 void JsSyncManagerObserver::OnEncryptionComplete(
@@ -94,7 +89,7 @@
   ListValue return_args;
   return_args.Append(syncable::ModelTypeSetToValue(encrypted_types));
   parent_router_->RouteJsEvent("onEncryptionComplete",
-                               JsArgList(return_args), NULL);
+                               JsArgList(return_args));
 }
 
 void JsSyncManagerObserver::OnMigrationNeededForTypes(
@@ -102,27 +97,23 @@
   ListValue return_args;
   return_args.Append(syncable::ModelTypeSetToValue(types));
   parent_router_->RouteJsEvent("onMigrationNeededForTypes",
-                               JsArgList(return_args), NULL);
-  // TODO(akalin): Bug 79247. Hook up JS boiler plate!
+                               JsArgList(return_args));
 }
 
 void JsSyncManagerObserver::OnInitializationComplete() {
-  parent_router_->RouteJsEvent("onInitializationComplete",
-                               JsArgList(), NULL);
+  parent_router_->RouteJsEvent("onInitializationComplete", JsArgList());
 }
 
 void JsSyncManagerObserver::OnStopSyncingPermanently() {
-  parent_router_->RouteJsEvent("onStopSyncingPermanently",
-                               JsArgList(), NULL);
+  parent_router_->RouteJsEvent("onStopSyncingPermanently", JsArgList());
 }
 
 void JsSyncManagerObserver::OnClearServerDataSucceeded() {
-  parent_router_->RouteJsEvent("onClearServerDataSucceeded",
-                               JsArgList(), NULL);
+  parent_router_->RouteJsEvent("onClearServerDataSucceeded", JsArgList());
 }
 
 void JsSyncManagerObserver::OnClearServerDataFailed() {
-  parent_router_->RouteJsEvent("onClearServerDataFailed", JsArgList(), NULL);
+  parent_router_->RouteJsEvent("onClearServerDataFailed", JsArgList());
 }
 
 }  // namespace browser_sync
diff --git a/chrome/browser/sync/js_sync_manager_observer_unittest.cc b/chrome/browser/sync/js_sync_manager_observer_unittest.cc
index 8a15cca..000c433 100644
--- a/chrome/browser/sync/js_sync_manager_observer_unittest.cc
+++ b/chrome/browser/sync/js_sync_manager_observer_unittest.cc
@@ -35,16 +35,16 @@
 
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onInitializationComplete",
-                           HasArgs(JsArgList()), NULL));
+                           HasArgs(JsArgList())));
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onStopSyncingPermanently",
-                           HasArgs(JsArgList()), NULL));
+                           HasArgs(JsArgList())));
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onClearServerDataSucceeded",
-                           HasArgs(JsArgList()), NULL));
+                           HasArgs(JsArgList())));
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onClearServerDataFailed",
-                           HasArgs(JsArgList()), NULL));
+                           HasArgs(JsArgList())));
 
   sync_manager_observer_.OnInitializationComplete();
   sync_manager_observer_.OnStopSyncingPermanently();
@@ -63,7 +63,7 @@
     expected_args.Append(Value::CreateStringValue(model_type_str));
     EXPECT_CALL(mock_router_,
                 RouteJsEvent("onChangesComplete",
-                             HasArgsAsList(expected_args), NULL));
+                             HasArgsAsList(expected_args)));
   }
 
   for (int i = syncable::FIRST_REAL_MODEL_TYPE;
@@ -92,7 +92,7 @@
 
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onSyncCycleCompleted",
-                           HasArgsAsList(expected_args), NULL));
+                           HasArgsAsList(expected_args)));
 
   sync_manager_observer_.OnSyncCycleCompleted(&snapshot);
 }
@@ -104,7 +104,7 @@
 
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onAuthError",
-                           HasArgsAsList(expected_args), NULL));
+                           HasArgsAsList(expected_args)));
 
   sync_manager_observer_.OnAuthError(error);
 }
@@ -129,19 +129,18 @@
           sync_api::REASON_SET_PASSPHRASE_FAILED)));
 
   EXPECT_CALL(mock_router_,
-              RouteJsEvent("onPassphraseRequired",
-                           HasArgsAsList(reason_passphrase_not_required_args),
-                           NULL));
+              RouteJsEvent(
+                  "onPassphraseRequired",
+                  HasArgsAsList(reason_passphrase_not_required_args)));
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onPassphraseRequired",
-                           HasArgsAsList(reason_encryption_args), NULL));
+                           HasArgsAsList(reason_encryption_args)));
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onPassphraseRequired",
-                           HasArgsAsList(reason_decryption_args), NULL));
+                           HasArgsAsList(reason_decryption_args)));
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onPassphraseRequired",
-                           HasArgsAsList(reason_set_passphrase_failed_args),
-                           NULL));
+                           HasArgsAsList(reason_set_passphrase_failed_args)));
 
   sync_manager_observer_.OnPassphraseRequired(
       sync_api::REASON_PASSPHRASE_NOT_REQUIRED);
@@ -157,10 +156,10 @@
 
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onUpdatedToken",
-                           HasArgsAsList(redacted_args), NULL));
+                           HasArgsAsList(redacted_args)));
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onPassphraseAccepted",
-                           HasArgsAsList(redacted_args), NULL));
+                           HasArgsAsList(redacted_args)));
 
   sync_manager_observer_.OnUpdatedToken("sensitive_token");
   sync_manager_observer_.OnPassphraseAccepted("sensitive_token");
@@ -182,7 +181,7 @@
 
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onEncryptionComplete",
-                           HasArgsAsList(expected_args), NULL));
+                           HasArgsAsList(expected_args)));
 
   sync_manager_observer_.OnEncryptionComplete(encrypted_types);
 }
@@ -203,7 +202,7 @@
 
   EXPECT_CALL(mock_router_,
               RouteJsEvent("onMigrationNeededForTypes",
-                           HasArgsAsList(expected_args), NULL));
+                           HasArgsAsList(expected_args)));
 
   sync_manager_observer_.OnMigrationNeededForTypes(types);
 }
@@ -281,7 +280,7 @@
     }
     EXPECT_CALL(mock_router_,
                 RouteJsEvent("onChangesApplied",
-                             HasArgsAsList(expected_args), NULL));
+                             HasArgsAsList(expected_args)));
   }
 
   // Fire OnChangesApplied() for each data type.
diff --git a/chrome/browser/sync/js_test_util.h b/chrome/browser/sync/js_test_util.h
index 23a8dad..85b4fc7 100644
--- a/chrome/browser/sync/js_test_util.h
+++ b/chrome/browser/sync/js_test_util.h
@@ -65,6 +65,8 @@
   ~MockJsEventHandler();
 
   MOCK_METHOD2(HandleJsEvent, void(const ::std::string&, const JsArgList&));
+  MOCK_METHOD2(HandleJsMessageReply,
+               void(const ::std::string&, const JsArgList&));
 };
 
 class MockJsEventRouter : public JsEventRouter {
@@ -72,7 +74,9 @@
   MockJsEventRouter();
   ~MockJsEventRouter();
 
-  MOCK_METHOD3(RouteJsEvent,
+  MOCK_METHOD2(RouteJsEvent,
+               void(const ::std::string&, const JsArgList&));
+  MOCK_METHOD3(RouteJsMessageReply,
                void(const ::std::string&, const JsArgList&,
                     const JsEventHandler*));
 };
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index 9d898cd..5a31625 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -429,7 +429,7 @@
   // TODO(akalin): Make an Observer subclass that listens and does the
   // event routing.
   js_event_handlers_.RouteJsEvent(
-      "onSyncServiceStateChanged", browser_sync::JsArgList(), NULL);
+      "onSyncServiceStateChanged", browser_sync::JsArgList());
 }
 
 // static
diff --git a/chrome/browser/sync/profile_sync_service_unittest.cc b/chrome/browser/sync/profile_sync_service_unittest.cc
index e170e1d..69502a7 100644
--- a/chrome/browser/sync/profile_sync_service_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_unittest.cc
@@ -222,14 +222,15 @@
   arg_list1.Append(Value::CreateBooleanValue(true));
   arg_list1.Append(Value::CreateIntegerValue(5));
   JsArgList args1(arg_list1);
-  EXPECT_CALL(event_handler, HandleJsEvent("testMessage1", HasArgs(args1)));
+  EXPECT_CALL(event_handler,
+              HandleJsMessageReply("testMessage1", HasArgs(args1)));
 
   ListValue arg_list2;
   arg_list2.Append(Value::CreateStringValue("test"));
   arg_list2.Append(arg_list1.DeepCopy());
   JsArgList args2(arg_list2);
   EXPECT_CALL(event_handler,
-              HandleJsEvent("delayTestMessage2", HasArgs(args2)));
+              HandleJsMessageReply("delayTestMessage2", HasArgs(args2)));
 
   ListValue arg_list3;
   arg_list3.Append(arg_list1.DeepCopy());
@@ -280,20 +281,22 @@
   arg_list1.Append(Value::CreateBooleanValue(true));
   arg_list1.Append(Value::CreateIntegerValue(5));
   JsArgList args1(arg_list1);
-  EXPECT_CALL(event_handler, HandleJsEvent("testMessage1", HasArgs(args1)));
+  EXPECT_CALL(event_handler,
+              HandleJsMessageReply("testMessage1", HasArgs(args1)));
 
   ListValue arg_list2;
   arg_list2.Append(Value::CreateStringValue("test"));
   arg_list2.Append(arg_list1.DeepCopy());
   JsArgList args2(arg_list2);
-  EXPECT_CALL(event_handler, HandleJsEvent("testMessage2", HasArgs(args2)));
+  EXPECT_CALL(event_handler,
+              HandleJsMessageReply("testMessage2", HasArgs(args2)));
 
   ListValue arg_list3;
   arg_list3.Append(arg_list1.DeepCopy());
   arg_list3.Append(arg_list2.DeepCopy());
   JsArgList args3(arg_list3);
   EXPECT_CALL(event_handler,
-              HandleJsEvent("delayTestMessage3", HasArgs(args3)));
+              HandleJsMessageReply("delayTestMessage3", HasArgs(args3)));
 
   const JsArgList kNoArgs;
 
diff --git a/chrome/browser/sync/test_profile_sync_service.cc b/chrome/browser/sync/test_profile_sync_service.cc
index 0a2b7bc..337b555 100644
--- a/chrome/browser/sync/test_profile_sync_service.cc
+++ b/chrome/browser/sync/test_profile_sync_service.cc
@@ -122,9 +122,9 @@
     const std::string& name, const JsArgList& args,
     const JsEventHandler* sender) {
   if (name.find("delay") != name.npos) {
-    core_->RouteJsEvent(name, args, sender);
+    core_->RouteJsMessageReply(name, args, sender);
   } else {
-    core_->RouteJsEventOnFrontendLoop(name, args, sender);
+    core_->RouteJsMessageReplyOnFrontendLoop(name, args, sender);
   }
 }
 
diff --git a/chrome/browser/sync/test_profile_sync_service.h b/chrome/browser/sync/test_profile_sync_service.h
index d07e777..4c3e19d3 100644
--- a/chrome/browser/sync/test_profile_sync_service.h
+++ b/chrome/browser/sync/test_profile_sync_service.h
@@ -8,6 +8,7 @@
 
 #include <string>
 
+#include "base/compiler_specific.h"
 #include "chrome/browser/sync/glue/data_type_manager_impl.h"
 #include "chrome/browser/sync/js_backend.h"
 #include "chrome/browser/sync/profile_sync_service.h"
@@ -60,14 +61,14 @@
   virtual JsBackend* GetJsBackend();
 
   // JsBackend implementation.
-  virtual void SetParentJsEventRouter(JsEventRouter* router);
-  virtual void RemoveParentJsEventRouter();
-  virtual const JsEventRouter* GetParentJsEventRouter() const;
+  virtual void SetParentJsEventRouter(JsEventRouter* router) OVERRIDE;
+  virtual void RemoveParentJsEventRouter() OVERRIDE;
+  virtual const JsEventRouter* GetParentJsEventRouter() const OVERRIDE;
   // Fires an event identical to the message unless the message has
   // "delay" as a prefix, in which case a task to fire the identical
   // event is posted instead.
   virtual void ProcessMessage(const std::string& name, const JsArgList& args,
-                              const JsEventHandler* sender);
+                              const JsEventHandler* sender) OVERRIDE;
 
   virtual void StartConfiguration(Callback0::Type* callback);
 
diff --git a/chrome/browser/ui/webui/sync_internals_ui.cc b/chrome/browser/ui/webui/sync_internals_ui.cc
index e11eb236..e1f88af3 100644
--- a/chrome/browser/ui/webui/sync_internals_ui.cc
+++ b/chrome/browser/ui/webui/sync_internals_ui.cc
@@ -56,8 +56,7 @@
     args.Append(about_info);
     ProfileSyncService* service = GetProfile()->GetProfileSyncService();
     sync_ui_util::ConstructAboutInformation(service, about_info);
-    HandleJsEvent("onGetAboutInfoFinished",
-                  browser_sync::JsArgList(args));
+    HandleJsMessageReply(name, browser_sync::JsArgList(args));
   } else {
     browser_sync::JsFrontend* backend = GetJsFrontend();
     if (backend) {
@@ -72,8 +71,19 @@
 void SyncInternalsUI::HandleJsEvent(const std::string& name,
                                     const browser_sync::JsArgList& args) {
   VLOG(1) << "Handling event: " << name << " with args " << args.ToString();
+  const std::string& event_handler = "chrome.sync." + name + ".fire";
   std::vector<const Value*> arg_list(args.Get().begin(), args.Get().end());
-  CallJavascriptFunction(name, arg_list);
+  CallJavascriptFunction(event_handler, arg_list);
+}
+
+void SyncInternalsUI::HandleJsMessageReply(
+    const std::string& name,
+    const browser_sync::JsArgList& args) {
+  VLOG(1) << "Handling reply for " << name << " message with args "
+          << args.ToString();
+  const std::string& reply_handler = "chrome.sync." + name + ".handleReply";
+  std::vector<const Value*> arg_list(args.Get().begin(), args.Get().end());
+  CallJavascriptFunction(reply_handler, arg_list);
 }
 
 browser_sync::JsFrontend* SyncInternalsUI::GetJsFrontend() {
diff --git a/chrome/browser/ui/webui/sync_internals_ui.h b/chrome/browser/ui/webui/sync_internals_ui.h
index e5a8118..6512fca 100644
--- a/chrome/browser/ui/webui/sync_internals_ui.h
+++ b/chrome/browser/ui/webui/sync_internals_ui.h
@@ -43,6 +43,9 @@
   // browser_sync::JsEventHandler implementation.
   virtual void HandleJsEvent(const std::string& name,
                              const browser_sync::JsArgList& args) OVERRIDE;
+  virtual void HandleJsMessageReply(
+      const std::string& name,
+      const browser_sync::JsArgList& args) OVERRIDE;
 
  private:
   // Returns the sync service's JsFrontend object, or NULL if the sync
diff --git a/chrome/browser/ui/webui/sync_internals_ui_unittest.cc b/chrome/browser/ui/webui/sync_internals_ui_unittest.cc
index 1c0068c..0ae2436 100644
--- a/chrome/browser/ui/webui/sync_internals_ui_unittest.cc
+++ b/chrome/browser/ui/webui/sync_internals_ui_unittest.cc
@@ -138,7 +138,8 @@
   ConstructTestSyncInternalsUI();
 
   EXPECT_CALL(*GetTestSyncInternalsUI(),
-              ExecuteJavascript(ASCIIToUTF16("testMessage(5,true);")));
+              ExecuteJavascript(
+                  ASCIIToUTF16("chrome.sync.testMessage.fire(5,true);")));
 
   ListValue args;
   args.Append(Value::CreateIntegerValue(5));
@@ -152,7 +153,8 @@
   ConstructTestSyncInternalsUI();
 
   EXPECT_CALL(*GetTestSyncInternalsUI(),
-              ExecuteJavascript(ASCIIToUTF16("testMessage(5,true);")));
+              ExecuteJavascript(
+                  ASCIIToUTF16("chrome.sync.testMessage.fire(5,true);")));
 
   ListValue args;
   args.Append(Value::CreateIntegerValue(5));
@@ -160,6 +162,40 @@
   GetTestSyncInternalsUI()->HandleJsEvent("testMessage", JsArgList(args));
 }
 
+TEST_F(SyncInternalsUITest, HandleJsMessageReply) {
+  ExpectSetupTeardownCalls();
+
+  ConstructTestSyncInternalsUI();
+
+  EXPECT_CALL(
+      *GetTestSyncInternalsUI(),
+      ExecuteJavascript(
+          ASCIIToUTF16("chrome.sync.testMessage.handleReply(5,true);")));
+
+  ListValue args;
+  args.Append(Value::CreateIntegerValue(5));
+  args.Append(Value::CreateBooleanValue(true));
+  GetTestSyncInternalsUI()->HandleJsMessageReply(
+      "testMessage", JsArgList(args));
+}
+
+TEST_F(SyncInternalsUITest, HandleJsMessageReplyNullService) {
+  ExpectSetupTeardownCallsNullService();
+
+  ConstructTestSyncInternalsUI();
+
+  EXPECT_CALL(
+      *GetTestSyncInternalsUI(),
+      ExecuteJavascript(
+          ASCIIToUTF16("chrome.sync.testMessage.handleReply(5,true);")));
+
+  ListValue args;
+  args.Append(Value::CreateIntegerValue(5));
+  args.Append(Value::CreateBooleanValue(true));
+  GetTestSyncInternalsUI()->HandleJsMessageReply(
+      "testMessage", JsArgList(args));
+}
+
 TEST_F(SyncInternalsUITest, OnWebUISendBasic) {
   ExpectSetupTeardownCalls();
 
@@ -191,7 +227,7 @@
 
 namespace {
 const char kAboutInfoCall[] =
-    "onGetAboutInfoFinished({\"summary\":\"SYNC DISABLED\"});";
+    "chrome.sync.getAboutInfo.handleReply({\"summary\":\"SYNC DISABLED\"});";
 }  // namespace
 
 // TODO(lipalani) - add a test case to test about:sync with a non null service.