[GCM] Move extension specific logic out of GCMProfileService

A new interface GCMAppHandler is introduce for app specific logic.
For extensions, ExtensionGCMAppHandler is created for both event
dispatching and extension notification handling.

BUG=343268
[email protected]

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@258235 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/api/gcm/gcm_api.h b/chrome/browser/extensions/api/gcm/gcm_api.h
index 78bdad3..07009a16 100644
--- a/chrome/browser/extensions/api/gcm/gcm_api.h
+++ b/chrome/browser/extensions/api/gcm/gcm_api.h
@@ -5,7 +5,6 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_API_GCM_GCM_API_H_
 #define CHROME_BROWSER_EXTENSIONS_API_GCM_GCM_API_H_
 
-#include "chrome/browser/services/gcm/gcm_event_router.h"
 #include "chrome/common/extensions/api/gcm.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_function.h"
@@ -92,21 +91,17 @@
   bool ValidateMessageData(const gcm::GCMClient::MessageData& data) const;
 };
 
-class GcmJsEventRouter : public gcm::GCMEventRouter,
-                         public EventRouter::Observer {
+class GcmJsEventRouter : public EventRouter::Observer {
  public:
   explicit GcmJsEventRouter(Profile* profile);
 
   virtual ~GcmJsEventRouter();
 
-  // GCMEventRouter:
-  virtual void OnMessage(
-      const std::string& app_id,
-      const gcm::GCMClient::IncomingMessage& message) OVERRIDE;
-  virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE;
-  virtual void OnSendError(
-      const std::string& app_id,
-      const gcm::GCMClient::SendErrorDetails& send_error_details) OVERRIDE;
+  void OnMessage(const std::string& app_id,
+                 const gcm::GCMClient::IncomingMessage& message);
+  void OnMessagesDeleted(const std::string& app_id);
+  void OnSendError(const std::string& app_id,
+                   const gcm::GCMClient::SendErrorDetails& send_error_details);
 
   // EventRouter::Observer:
   virtual void OnListenerAdded(const EventListenerInfo& details) OVERRIDE;
diff --git a/chrome/browser/extensions/api/gcm/gcm_apitest.cc b/chrome/browser/extensions/api/gcm/gcm_apitest.cc
index faf4f98f..879c1c9 100644
--- a/chrome/browser/extensions/api/gcm/gcm_apitest.cc
+++ b/chrome/browser/extensions/api/gcm/gcm_apitest.cc
@@ -5,6 +5,7 @@
 #include "base/run_loop.h"
 #include "chrome/browser/extensions/api/gcm/gcm_api.h"
 #include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_gcm_app_handler.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/services/gcm/fake_gcm_profile_service.h"
 #include "chrome/browser/services/gcm/gcm_client_factory.h"
@@ -186,8 +187,8 @@
       LoadTestExtension(kEventsExtension, "on_messages_deleted.html");
   ASSERT_TRUE(extension);
 
-  GcmJsEventRouter router(profile());
-  router.OnMessagesDeleted(extension->id());
+  extensions::ExtensionGCMAppHandler app_handler(profile());
+  app_handler.OnMessagesDeleted(extension->id());
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
 
@@ -199,17 +200,17 @@
       LoadTestExtension(kEventsExtension, "on_message.html");
   ASSERT_TRUE(extension);
 
-  GcmJsEventRouter router(profile());
+  extensions::ExtensionGCMAppHandler app_handler(profile());
 
   gcm::GCMClient::IncomingMessage message;
   message.data["property1"] = "value1";
   message.data["property2"] = "value2";
   // First message is sent without a collapse key.
-  router.OnMessage(extension->id(), message);
+  app_handler.OnMessage(extension->id(), message);
 
   // Second message carries the same data and a collapse key.
   message.collapse_key = "collapseKeyValue";
-  router.OnMessage(extension->id(), message);
+  app_handler.OnMessage(extension->id(), message);
 
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
@@ -223,27 +224,32 @@
   ASSERT_TRUE(extension);
 
   std::string total_expected_messages = "5";
-  GcmJsEventRouter router(profile());
-  router.OnSendError(extension->id(),
-                     CreateErrorDetails("error_message_1",
-                                        gcm::GCMClient::ASYNC_OPERATION_PENDING,
-                                        total_expected_messages));
-  router.OnSendError(extension->id(),
-                     CreateErrorDetails("error_message_2",
-                                        gcm::GCMClient::SERVER_ERROR,
-                                        total_expected_messages));
-  router.OnSendError(extension->id(),
-                     CreateErrorDetails("error_message_3",
-                                        gcm::GCMClient::NETWORK_ERROR,
-                                        total_expected_messages));
-  router.OnSendError(extension->id(),
-                     CreateErrorDetails("error_message_4",
-                                        gcm::GCMClient::UNKNOWN_ERROR,
-                                        total_expected_messages));
-  router.OnSendError(extension->id(),
-                     CreateErrorDetails("error_message_5",
-                                        gcm::GCMClient::TTL_EXCEEDED,
-                                        total_expected_messages));
+  extensions::ExtensionGCMAppHandler app_handler(profile());
+  app_handler.OnSendError(
+      extension->id(),
+      CreateErrorDetails("error_message_1",
+                         gcm::GCMClient::ASYNC_OPERATION_PENDING,
+                         total_expected_messages));
+  app_handler.OnSendError(
+      extension->id(),
+      CreateErrorDetails("error_message_2",
+                         gcm::GCMClient::SERVER_ERROR,
+                         total_expected_messages));
+  app_handler.OnSendError(
+      extension->id(),
+      CreateErrorDetails("error_message_3",
+                         gcm::GCMClient::NETWORK_ERROR,
+                         total_expected_messages));
+  app_handler.OnSendError(
+      extension->id(),
+      CreateErrorDetails("error_message_4",
+                         gcm::GCMClient::UNKNOWN_ERROR,
+                         total_expected_messages));
+  app_handler.OnSendError(
+      extension->id(),
+      CreateErrorDetails("error_message_5",
+                         gcm::GCMClient::TTL_EXCEEDED,
+                         total_expected_messages));
 
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
diff --git a/chrome/browser/extensions/extension_gcm_app_handler.cc b/chrome/browser/extensions/extension_gcm_app_handler.cc
new file mode 100644
index 0000000..3ed99a0
--- /dev/null
+++ b/chrome/browser/extensions/extension_gcm_app_handler.cc
@@ -0,0 +1,156 @@
+// 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.
+
+#include "chrome/browser/extensions/extension_gcm_app_handler.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/services/gcm/gcm_profile_service.h"
+#include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_source.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/one_shot_event.h"
+#include "extensions/common/permissions/permissions_data.h"
+
+#if !defined(OS_ANDROID)
+#include "chrome/browser/extensions/api/gcm/gcm_api.h"
+#endif
+
+namespace extensions {
+
+namespace {
+
+bool IsGCMPermissionEnabled(const Extension* extension) {
+  return PermissionsData::HasAPIPermission(extension, APIPermission::kGcm);
+}
+
+}  // namespace
+
+ExtensionGCMAppHandler::ExtensionGCMAppHandler(Profile* profile)
+    : profile_(profile),
+      weak_factory_(this) {
+  // Listen to various extension related notifications.
+  registrar_.Add(this,
+                 chrome::NOTIFICATION_EXTENSION_LOADED,
+                 content::Source<Profile>(profile_));
+  registrar_.Add(this,
+                 chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
+                 content::Source<Profile>(profile_));
+  registrar_.Add(this,
+                 chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
+                 content::Source<Profile>(profile_));
+
+  // Register app handlers when the extension system is ready. It might be ready
+  // now.
+  ExtensionSystem::Get(profile_)->ready().Post(
+      FROM_HERE,
+      base::Bind(&ExtensionGCMAppHandler::OnExtensionsReady,
+                 weak_factory_.GetWeakPtr()));
+
+#if !defined(OS_ANDROID)
+  js_event_router_.reset(new extensions::GcmJsEventRouter(profile_));
+#endif
+}
+
+ExtensionGCMAppHandler::~ExtensionGCMAppHandler() {
+  const ExtensionSet& enabled_extensions =
+      ExtensionRegistry::Get(profile_)->enabled_extensions();
+  for (ExtensionSet::const_iterator extension = enabled_extensions.begin();
+       extension != enabled_extensions.end();
+       ++extension) {
+    if (IsGCMPermissionEnabled(extension->get()))
+      GetGCMProfileService()->RemoveAppHandler((*extension)->id());
+  }
+}
+
+void ExtensionGCMAppHandler::ShutdownHandler() {
+#if !defined(OS_ANDROID)
+  js_event_router_.reset();
+#endif
+}
+
+void ExtensionGCMAppHandler::OnMessage(
+    const std::string& app_id,
+    const gcm::GCMClient::IncomingMessage& message) {
+#if !defined(OS_ANDROID)
+  js_event_router_->OnMessage(app_id, message);
+#endif
+}
+
+void ExtensionGCMAppHandler::OnMessagesDeleted(const std::string& app_id) {
+#if !defined(OS_ANDROID)
+  js_event_router_->OnMessagesDeleted(app_id);
+#endif
+}
+
+void ExtensionGCMAppHandler::OnSendError(
+    const std::string& app_id,
+    const gcm::GCMClient::SendErrorDetails& send_error_details) {
+#if !defined(OS_ANDROID)
+  js_event_router_->OnSendError(app_id, send_error_details);
+#endif
+}
+
+void ExtensionGCMAppHandler::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  switch (type) {
+    case chrome:: NOTIFICATION_EXTENSION_LOADED: {
+      const Extension* extension = content::Details<Extension>(details).ptr();
+      if (IsGCMPermissionEnabled(extension))
+        GetGCMProfileService()->AddAppHandler(extension->id(), this);
+      break;
+    }
+    case chrome:: NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
+      const Extension* extension =
+          content::Details<UnloadedExtensionInfo>(details)->extension;
+      if (IsGCMPermissionEnabled(extension))
+        GetGCMProfileService()->RemoveAppHandler(extension->id());
+      break;
+    }
+    case chrome:: NOTIFICATION_EXTENSION_UNINSTALLED: {
+      const Extension* extension = content::Details<Extension>(details).ptr();
+      if (IsGCMPermissionEnabled(extension)) {
+        GetGCMProfileService()->Unregister(
+            extension->id(),
+            base::Bind(&ExtensionGCMAppHandler::OnUnregisterCompleted,
+                       weak_factory_.GetWeakPtr(),
+                       extension->id()));
+        GetGCMProfileService()->RemoveAppHandler(extension->id());
+      }
+      break;
+    }
+    default:
+      NOTREACHED();
+  }
+}
+
+gcm::GCMProfileService* ExtensionGCMAppHandler::GetGCMProfileService() const {
+  return gcm::GCMProfileServiceFactory::GetForProfile(profile_);
+}
+
+void ExtensionGCMAppHandler::OnExtensionsReady() {
+  // Register app handler for those loaded extensions that use GCM.
+  const ExtensionSet& enabled_extensions =
+      ExtensionRegistry::Get(profile_)->enabled_extensions();
+  for (ExtensionSet::const_iterator extension = enabled_extensions.begin();
+       extension != enabled_extensions.end();
+       ++extension) {
+    if (IsGCMPermissionEnabled(extension->get()))
+      GetGCMProfileService()->AddAppHandler((*extension)->id(), this);
+  }
+}
+
+void ExtensionGCMAppHandler::OnUnregisterCompleted(
+    const std::string& app_id, gcm::GCMClient::Result result) {
+  // Nothing to do.
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_gcm_app_handler.h b/chrome/browser/extensions/extension_gcm_app_handler.h
new file mode 100644
index 0000000..5edf0a9f
--- /dev/null
+++ b/chrome/browser/extensions/extension_gcm_app_handler.h
@@ -0,0 +1,67 @@
+// 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_GCM_APP_HANDLER_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_GCM_APP_HANDLER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/services/gcm/gcm_app_handler.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "extensions/browser/event_router.h"
+#include "google_apis/gcm/gcm_client.h"
+
+class Profile;
+namespace gcm {
+class GCMProfileService;
+}
+
+namespace extensions {
+
+class GcmJsEventRouter;
+
+// Defines the interface to provide handling logic for a given app.
+class ExtensionGCMAppHandler : public gcm::GCMAppHandler,
+                               public content::NotificationObserver {
+ public:
+  explicit ExtensionGCMAppHandler(Profile* profile);
+  virtual ~ExtensionGCMAppHandler();
+
+  // Overridden from gcm::GCMAppHandler:
+  virtual void ShutdownHandler() OVERRIDE;
+  virtual void OnMessage(
+      const std::string& app_id,
+      const gcm::GCMClient::IncomingMessage& message) OVERRIDE;
+  virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE;
+  virtual void OnSendError(
+      const std::string& app_id,
+      const gcm::GCMClient::SendErrorDetails& send_error_details) OVERRIDE;
+
+ private:
+  // Overridden from content::NotificationObserver:
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE;
+
+  gcm::GCMProfileService* GetGCMProfileService() const;
+  void OnExtensionsReady();
+  void OnUnregisterCompleted(const std::string& app_id,
+                             gcm::GCMClient::Result result);
+
+  Profile* profile_;
+  content::NotificationRegistrar registrar_;
+
+#if !defined(OS_ANDROID)
+  scoped_ptr<extensions::GcmJsEventRouter> js_event_router_;
+#endif
+
+  base::WeakPtrFactory<ExtensionGCMAppHandler> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionGCMAppHandler);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_GCM_APP_HANDLER_H_
diff --git a/chrome/browser/extensions/extension_sync_service.cc b/chrome/browser/extensions/extension_sync_service.cc
index 833a1ce..87b6623 100644
--- a/chrome/browser/extensions/extension_sync_service.cc
+++ b/chrome/browser/extensions/extension_sync_service.cc
@@ -11,6 +11,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/extensions/app_sync_data.h"
 #include "chrome/browser/extensions/extension_error_ui.h"
+#include "chrome/browser/extensions/extension_gcm_app_handler.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_sync_data.h"
 #include "chrome/browser/extensions/extension_sync_service_factory.h"
@@ -33,6 +34,7 @@
 using extensions::ExtensionPrefs;
 using extensions::ExtensionRegistry;
 using extensions::FeatureSwitch;
+using extensions::ExtensionGCMAppHandler;
 
 ExtensionSyncService::ExtensionSyncService(Profile* profile,
                                            ExtensionPrefs* extension_prefs,
@@ -51,7 +53,8 @@
           make_scoped_ptr(new browser_sync::SyncPrefs(
               extension_prefs_->pref_service())),
           &extension_sync_bundle_,
-          syncer::EXTENSIONS) {
+          syncer::EXTENSIONS),
+      extesnion_gcm_app_handler_(new ExtensionGCMAppHandler(profile)) {
   SetSyncStartFlare(sync_start_util::GetFlareForSyncableService(
       profile_->GetPath()));
 
diff --git a/chrome/browser/extensions/extension_sync_service.h b/chrome/browser/extensions/extension_sync_service.h
index e5e3af7..e8a8fe5 100644
--- a/chrome/browser/extensions/extension_sync_service.h
+++ b/chrome/browser/extensions/extension_sync_service.h
@@ -29,6 +29,7 @@
 
 namespace extensions {
 class AppSyncData;
+class ExtensionGCMAppHandler;
 class ExtensionPrefs;
 class ExtensionSyncData;
 }  // namespace extensions
@@ -151,6 +152,9 @@
   // asynchronously via MergeDataAndStartSyncing as soon as possible.
   syncer::SyncableService::StartSyncFlare flare_;
 
+  // Used to provide the extension specific logic for GCMProfileService.
+  scoped_ptr<extensions::ExtensionGCMAppHandler> extesnion_gcm_app_handler_;
+
   DISALLOW_COPY_AND_ASSIGN(ExtensionSyncService);
 };