push_messaging_invalidation_handler.cc revision 116680a4aac90f2aa7413d9095a592090648e557
1// Copyright (c) 2012 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/extensions/api/push_messaging/push_messaging_invalidation_handler.h"
6
7#include <algorithm>
8#include <vector>
9
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/string_split.h"
12#include "chrome/browser/extensions/api/push_messaging/push_messaging_invalidation_handler_delegate.h"
13#include "components/invalidation/invalidation_service.h"
14#include "components/invalidation/object_id_invalidation_map.h"
15#include "extensions/common/extension.h"
16#include "google/cacheinvalidation/types.pb.h"
17
18namespace extensions {
19
20namespace {
21
22const int kNumberOfSubchannels = 4;
23
24// Chrome push messaging object IDs currently have the following format:
25// <format type>/<GAIA ID>/<extension ID>/<subchannel>
26// <format type> must be 'U', and <GAIA ID> is handled server-side so the client
27// never sees it.
28syncer::ObjectIdSet ExtensionIdToObjectIds(const std::string& extension_id) {
29  syncer::ObjectIdSet object_ids;
30  for (int i = 0; i < kNumberOfSubchannels; ++i) {
31    std::string name("U/");
32    name += extension_id;
33    name += "/";
34    name += base::IntToString(i);
35    object_ids.insert(invalidation::ObjectId(
36        ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
37        name));
38  }
39  return object_ids;
40}
41
42// Returns true iff the conversion was successful.
43bool ObjectIdToExtensionAndSubchannel(const invalidation::ObjectId& object_id,
44                                      std::string* extension_id,
45                                      int* subchannel) {
46  if (object_id.source() !=
47      ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING) {
48    DLOG(WARNING) << "Invalid source: " << object_id.source();
49    return false;
50  }
51
52  const std::string& name = object_id.name();
53  std::vector<std::string> components;
54  base::SplitStringDontTrim(name, '/', &components);
55  if (components.size() < 3) {
56    DLOG(WARNING) << "Invalid format type from object name " << name;
57    return false;
58  }
59  if (components[0] != "U") {
60    DLOG(WARNING) << "Invalid format type from object name " << name;
61    return false;
62  }
63  if (!Extension::IdIsValid(components[1])) {
64    DLOG(WARNING) << "Invalid extension ID from object name " << name;
65    return false;
66  }
67  *extension_id = components[1];
68  if (!base::StringToInt(components[2], subchannel)) {
69    DLOG(WARNING) << "Subchannel not a number from object name " << name;
70    return false;
71  }
72  if (*subchannel < 0 || *subchannel >= kNumberOfSubchannels) {
73    DLOG(WARNING) << "Subchannel out of range from object name " << name;
74    return false;
75  }
76  return true;
77}
78
79}  // namespace
80
81PushMessagingInvalidationHandler::PushMessagingInvalidationHandler(
82    invalidation::InvalidationService* service,
83    PushMessagingInvalidationHandlerDelegate* delegate)
84    : service_(service),
85      delegate_(delegate) {
86  DCHECK(service_);
87  service_->RegisterInvalidationHandler(this);
88}
89
90PushMessagingInvalidationHandler::~PushMessagingInvalidationHandler() {
91  DCHECK(thread_checker_.CalledOnValidThread());
92  service_->UnregisterInvalidationHandler(this);
93}
94
95void PushMessagingInvalidationHandler::SuppressInitialInvalidationsForExtension(
96    const std::string& extension_id) {
97  DCHECK(thread_checker_.CalledOnValidThread());
98  const syncer::ObjectIdSet& suppressed_ids =
99      ExtensionIdToObjectIds(extension_id);
100  suppressed_ids_.insert(suppressed_ids.begin(), suppressed_ids.end());
101}
102
103void PushMessagingInvalidationHandler::RegisterExtension(
104    const std::string& extension_id) {
105  DCHECK(thread_checker_.CalledOnValidThread());
106  DCHECK(Extension::IdIsValid(extension_id));
107  registered_extensions_.insert(extension_id);
108  UpdateRegistrations();
109}
110
111void PushMessagingInvalidationHandler::UnregisterExtension(
112    const std::string& extension_id) {
113  DCHECK(thread_checker_.CalledOnValidThread());
114  DCHECK(Extension::IdIsValid(extension_id));
115  registered_extensions_.erase(extension_id);
116  UpdateRegistrations();
117}
118
119void PushMessagingInvalidationHandler::OnInvalidatorStateChange(
120    syncer::InvalidatorState state) {
121  DCHECK(thread_checker_.CalledOnValidThread());
122  // Nothing to do.
123}
124
125void PushMessagingInvalidationHandler::OnIncomingInvalidation(
126    const syncer::ObjectIdInvalidationMap& invalidation_map) {
127  DCHECK(thread_checker_.CalledOnValidThread());
128  invalidation_map.AcknowledgeAll();
129
130  syncer::ObjectIdSet ids = invalidation_map.GetObjectIds();
131  for (syncer::ObjectIdSet::const_iterator it = ids.begin();
132       it != ids.end(); ++it) {
133    const syncer::SingleObjectInvalidationSet& list =
134        invalidation_map.ForObject(*it);
135    const syncer::Invalidation& invalidation = list.back();
136
137    std::string payload;
138    if (invalidation.is_unknown_version()) {
139      payload = std::string();
140    } else {
141      payload = list.back().payload();
142    }
143
144    syncer::ObjectIdSet::iterator suppressed_id = suppressed_ids_.find(*it);
145    if (suppressed_id != suppressed_ids_.end()) {
146      suppressed_ids_.erase(suppressed_id);
147      continue;
148    }
149    DVLOG(2) << "Incoming push message, id is: "
150             << syncer::ObjectIdToString(*it)
151             << " and payload is:" << payload;
152
153    std::string extension_id;
154    int subchannel;
155    if (ObjectIdToExtensionAndSubchannel(*it, &extension_id, &subchannel)) {
156      const syncer::SingleObjectInvalidationSet& invalidation_list =
157          invalidation_map.ForObject(*it);
158
159      // We always forward unknown version invalidation when we receive one.
160      if (invalidation_list.StartsWithUnknownVersion()) {
161        DVLOG(2) << "Sending push message to receiver, extension is "
162            << extension_id << ", subchannel is " << subchannel
163            << "and payload was lost";
164        delegate_->OnMessage(extension_id, subchannel, std::string());
165      }
166
167      // If we receive a new max version for this object, forward its payload.
168      const syncer::Invalidation& max_invalidation = invalidation_list.back();
169      if (!max_invalidation.is_unknown_version() &&
170          max_invalidation.version() > max_object_version_map_[*it]) {
171        max_object_version_map_[*it] = max_invalidation.version();
172        DVLOG(2) << "Sending push message to receiver, extension is "
173            << extension_id << ", subchannel is " << subchannel
174            << ", and payload is " << max_invalidation.payload();
175        delegate_->OnMessage(extension_id,
176                             subchannel,
177                             max_invalidation.payload());
178      }
179    }
180  }
181}
182
183std::string PushMessagingInvalidationHandler::GetOwnerName() const {
184  return "PushMessagingApi";
185}
186
187void PushMessagingInvalidationHandler::UpdateRegistrations() {
188  syncer::ObjectIdSet ids;
189  for (std::set<std::string>::const_iterator it =
190       registered_extensions_.begin(); it != registered_extensions_.end();
191       ++it) {
192    const syncer::ObjectIdSet& object_ids = ExtensionIdToObjectIds(*it);
193    ids.insert(object_ids.begin(), object_ids.end());
194  }
195  service_->UpdateRegisteredInvalidationIds(this, ids);
196}
197
198}  // namespace extensions
199