• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/services/gcm/push_messaging_service_impl.h"
6 
7 #include <vector>
8 
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/services/gcm/gcm_profile_service.h"
15 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/common/pref_names.h"
18 #include "components/gcm_driver/gcm_driver.h"
19 #include "components/pref_registry/pref_registry_syncable.h"
20 
21 namespace gcm {
22 
23 namespace {
24 const char kAppIdPrefix[] = "push:";
25 const int kMaxRegistrations = 1000000;
26 }  // namespace
27 
28 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)29 void PushMessagingServiceImpl::RegisterProfilePrefs(
30     user_prefs::PrefRegistrySyncable* registry) {
31   registry->RegisterIntegerPref(
32       prefs::kPushMessagingRegistrationCount,
33       0,
34       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
35 }
36 
37 // static
InitializeForProfile(Profile * profile)38 void PushMessagingServiceImpl::InitializeForProfile(Profile* profile) {
39   // TODO(mvanouwerkerk): Make sure to remove this check at the same time as
40   // push graduates from experimental in Blink.
41   if (!CommandLine::ForCurrentProcess()->HasSwitch(
42           switches::kEnableExperimentalWebPlatformFeatures)) {
43     return;
44   }
45   // TODO(johnme): Consider whether push should be enabled in incognito. If it
46   // does get enabled, then be careful that you're reading the pref from the
47   // right profile, as prefs defined in a regular profile are visible in the
48   // corresponding incognito profile unless overrriden.
49   if (!profile || profile->IsOffTheRecord() ||
50       profile->GetPrefs()->GetInteger(prefs::kPushMessagingRegistrationCount) <=
51           0) {
52     return;
53   }
54   // Create the GCMProfileService, and hence instantiate this class.
55   GCMProfileService* gcm_service =
56       gcm::GCMProfileServiceFactory::GetForProfile(profile);
57   PushMessagingServiceImpl* push_service =
58       static_cast<PushMessagingServiceImpl*>(
59           gcm_service->push_messaging_service());
60   // Register ourselves as an app handler.
61   gcm_service->driver()->AddAppHandler(kAppIdPrefix, push_service);
62 }
63 
PushMessagingServiceImpl(GCMProfileService * gcm_profile_service,Profile * profile)64 PushMessagingServiceImpl::PushMessagingServiceImpl(
65     GCMProfileService* gcm_profile_service,
66     Profile* profile)
67     : gcm_profile_service_(gcm_profile_service),
68       profile_(profile),
69       weak_factory_(this) {
70 }
71 
~PushMessagingServiceImpl()72 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
73   // TODO(johnme): If it's possible for this to be destroyed before GCMDriver,
74   // then we should call RemoveAppHandler.
75 }
76 
CanHandle(const std::string & app_id) const77 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const {
78   // TODO(mvanouwerkerk): Finalize and centralize format of Push API app_id.
79   return StartsWithASCII(app_id, kAppIdPrefix, true);
80 }
81 
ShutdownHandler()82 void PushMessagingServiceImpl::ShutdownHandler() {
83   // TODO(johnme): Do any necessary cleanup.
84 }
85 
OnMessage(const std::string & app_id,const GCMClient::IncomingMessage & message)86 void PushMessagingServiceImpl::OnMessage(
87     const std::string& app_id,
88     const GCMClient::IncomingMessage& message) {
89   // The Push API only exposes a single string of data in the push event fired
90   // on the Service Worker. When developers send messages using GCM to the Push
91   // API, they must pass a single key-value pair, where the key is "data" and
92   // the value is the string they want to be passed to their Service Worker.
93   // For example, they could send the following JSON using the HTTPS GCM API:
94   // {
95   //     "registration_ids": ["FOO", "BAR"],
96   //     "data": {
97   //         "data": "BAZ",
98   //     },
99   //     "delay_while_idle": true,
100   // }
101   // TODO(johnme): Make sure this is clearly documented for developers.
102   GCMClient::MessageData::const_iterator it = message.data.find("data");
103   if (it != message.data.end()) {
104     const std::string& data ALLOW_UNUSED = it->second;
105     // TODO(mvanouwerkerk): Fire push event with data on the Service Worker
106     // corresponding to app_id (and remove ALLOW_UNUSED above).
107   } else {
108     // Drop the message, as it is invalid.
109     // TODO(mvanouwerkerk): Show a warning in the developer console of the
110     // Service Worker corresponding to app_id.
111     // TODO(johnme): Add diagnostic observers (e.g. UMA and an internals page)
112     // to know when bad things happen.
113   }
114 }
115 
OnMessagesDeleted(const std::string & app_id)116 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
117   // TODO(mvanouwerkerk): Fire push error event on the Service Worker
118   // corresponding to app_id.
119 }
120 
OnSendError(const std::string & app_id,const GCMClient::SendErrorDetails & send_error_details)121 void PushMessagingServiceImpl::OnSendError(
122     const std::string& app_id,
123     const GCMClient::SendErrorDetails& send_error_details) {
124   NOTREACHED() << "The Push API shouldn't have sent messages upstream";
125 }
126 
Register(const std::string & app_id,const std::string & sender_id,const content::PushMessagingService::RegisterCallback & callback)127 void PushMessagingServiceImpl::Register(
128     const std::string& app_id,
129     const std::string& sender_id,
130     const content::PushMessagingService::RegisterCallback& callback) {
131   if (!gcm_profile_service_->driver()) {
132     NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?";
133   }
134 
135   if (profile_->GetPrefs()->GetInteger(
136           prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) {
137     DidRegister(app_id, callback, "", GCMClient::UNKNOWN_ERROR);
138     return;
139   }
140 
141   // If this is registering for the first time then the driver does not have
142   // this as an app handler and registration would fail.
143   if (gcm_profile_service_->driver()->GetAppHandler(kAppIdPrefix) != this)
144     gcm_profile_service_->driver()->AddAppHandler(kAppIdPrefix, this);
145 
146   std::vector<std::string> sender_ids(1, sender_id);
147   gcm_profile_service_->driver()->Register(
148       app_id,
149       sender_ids,
150       base::Bind(&PushMessagingServiceImpl::DidRegister,
151                  weak_factory_.GetWeakPtr(),
152                  app_id,
153                  callback));
154 }
155 
DidRegister(const std::string & app_id,const content::PushMessagingService::RegisterCallback & callback,const std::string & registration_id,GCMClient::Result result)156 void PushMessagingServiceImpl::DidRegister(
157     const std::string& app_id,
158     const content::PushMessagingService::RegisterCallback& callback,
159     const std::string& registration_id,
160     GCMClient::Result result) {
161   GURL endpoint = GURL("https://android.googleapis.com/gcm/send");
162   bool success = (result == GCMClient::SUCCESS);
163   callback.Run(endpoint, registration_id, success);
164   if (success) {
165     // TODO(johnme): Make sure the pref doesn't get out of sync after crashes.
166     int registration_count = profile_->GetPrefs()->GetInteger(
167         prefs::kPushMessagingRegistrationCount);
168     profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
169                                      registration_count + 1);
170   }
171 }
172 
173 // TODO(johnme): Unregister should decrement the pref, and call
174 // RemoveAppHandler if the count drops to zero.
175 
176 }  // namespace gcm
177