summaryrefslogtreecommitdiffstats
path: root/chrome/browser/services/gcm/push_messaging_service_impl.cc
blob: 0306d111fe66e95f69e299af9d68056c0989f568 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// 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/services/gcm/push_messaging_service_impl.h"

#include <vector>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_util.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 "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "components/gcm_driver/gcm_driver.h"
#include "components/pref_registry/pref_registry_syncable.h"

namespace gcm {

namespace {
const char kAppIdPrefix[] = "push:";
const int kMaxRegistrations = 1000000;
}  // namespace

// static
void PushMessagingServiceImpl::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  registry->RegisterIntegerPref(
      prefs::kPushMessagingRegistrationCount,
      0,
      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
}

// static
void PushMessagingServiceImpl::InitializeForProfile(Profile* profile) {
  // TODO(mvanouwerkerk): Make sure to remove this check at the same time as
  // push graduates from experimental in Blink.
  if (!CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableExperimentalWebPlatformFeatures)) {
    return;
  }
  // TODO(johnme): Consider whether push should be enabled in incognito. If it
  // does get enabled, then be careful that you're reading the pref from the
  // right profile, as prefs defined in a regular profile are visible in the
  // corresponding incognito profile unless overrriden.
  if (!profile || profile->IsOffTheRecord() ||
      profile->GetPrefs()->GetInteger(prefs::kPushMessagingRegistrationCount) <=
          0) {
    return;
  }
  // Create the GCMProfileService, and hence instantiate this class.
  GCMProfileService* gcm_service =
      gcm::GCMProfileServiceFactory::GetForProfile(profile);
  PushMessagingServiceImpl* push_service =
      static_cast<PushMessagingServiceImpl*>(
          gcm_service->push_messaging_service());
  // Register ourselves as an app handler.
  gcm_service->driver()->AddAppHandler(kAppIdPrefix, push_service);
}

PushMessagingServiceImpl::PushMessagingServiceImpl(
    GCMProfileService* gcm_profile_service,
    Profile* profile)
    : gcm_profile_service_(gcm_profile_service),
      profile_(profile),
      weak_factory_(this) {
}

PushMessagingServiceImpl::~PushMessagingServiceImpl() {
  // TODO(johnme): If it's possible for this to be destroyed before GCMDriver,
  // then we should call RemoveAppHandler.
}

bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const {
  // TODO(mvanouwerkerk): Finalize and centralize format of Push API app_id.
  return StartsWithASCII(app_id, kAppIdPrefix, true);
}

void PushMessagingServiceImpl::ShutdownHandler() {
  // TODO(johnme): Do any necessary cleanup.
}

void PushMessagingServiceImpl::OnMessage(
    const std::string& app_id,
    const GCMClient::IncomingMessage& message) {
  // The Push API only exposes a single string of data in the push event fired
  // on the Service Worker. When developers send messages using GCM to the Push
  // API, they must pass a single key-value pair, where the key is "data" and
  // the value is the string they want to be passed to their Service Worker.
  // For example, they could send the following JSON using the HTTPS GCM API:
  // {
  //     "registration_ids": ["FOO", "BAR"],
  //     "data": {
  //         "data": "BAZ",
  //     },
  //     "delay_while_idle": true,
  // }
  // TODO(johnme): Make sure this is clearly documented for developers.
  GCMClient::MessageData::const_iterator it = message.data.find("data");
  if (it != message.data.end()) {
    const std::string& data ALLOW_UNUSED = it->second;
    // TODO(mvanouwerkerk): Fire push event with data on the Service Worker
    // corresponding to app_id (and remove ALLOW_UNUSED above).
  } else {
    // Drop the message, as it is invalid.
    // TODO(mvanouwerkerk): Show a warning in the developer console of the
    // Service Worker corresponding to app_id.
    // TODO(johnme): Add diagnostic observers (e.g. UMA and an internals page)
    // to know when bad things happen.
  }
}

void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
  // TODO(mvanouwerkerk): Fire push error event on the Service Worker
  // corresponding to app_id.
}

void PushMessagingServiceImpl::OnSendError(
    const std::string& app_id,
    const GCMClient::SendErrorDetails& send_error_details) {
  NOTREACHED() << "The Push API shouldn't have sent messages upstream";
}

void PushMessagingServiceImpl::Register(
    const std::string& app_id,
    const std::string& sender_id,
    const content::PushMessagingService::RegisterCallback& callback) {
  if (!gcm_profile_service_->driver()) {
    NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?";
  }

  if (profile_->GetPrefs()->GetInteger(
          prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) {
    DidRegister(app_id, callback, "", GCMClient::UNKNOWN_ERROR);
    return;
  }

  // If this is registering for the first time then the driver does not have
  // this as an app handler and registration would fail.
  if (gcm_profile_service_->driver()->GetAppHandler(kAppIdPrefix) != this)
    gcm_profile_service_->driver()->AddAppHandler(kAppIdPrefix, this);

  std::vector<std::string> sender_ids(1, sender_id);
  gcm_profile_service_->driver()->Register(
      app_id,
      sender_ids,
      base::Bind(&PushMessagingServiceImpl::DidRegister,
                 weak_factory_.GetWeakPtr(),
                 app_id,
                 callback));
}

void PushMessagingServiceImpl::DidRegister(
    const std::string& app_id,
    const content::PushMessagingService::RegisterCallback& callback,
    const std::string& registration_id,
    GCMClient::Result result) {
  GURL endpoint = GURL("https://android.googleapis.com/gcm/send");
  bool success = (result == GCMClient::SUCCESS);
  callback.Run(endpoint, registration_id, success);
  if (success) {
    // TODO(johnme): Make sure the pref doesn't get out of sync after crashes.
    int registration_count = profile_->GetPrefs()->GetInteger(
        prefs::kPushMessagingRegistrationCount);
    profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
                                     registration_count + 1);
  }
}

// TODO(johnme): Unregister should decrement the pref, and call
// RemoveAppHandler if the count drops to zero.

}  // namespace gcm