Home | History | Annotate | Download | only in push_messaging
      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 "extensions/common/extension.h"
     15 #include "google/cacheinvalidation/types.pb.h"
     16 #include "sync/notifier/object_id_invalidation_map.h"
     17 
     18 namespace extensions {
     19 
     20 namespace {
     21 
     22 const 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.
     28 syncer::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.
     43 bool 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 
     81 PushMessagingInvalidationHandler::PushMessagingInvalidationHandler(
     82     invalidation::InvalidationService* service,
     83     PushMessagingInvalidationHandlerDelegate* delegate)
     84     : service_(service),
     85       delegate_(delegate) {
     86   DCHECK(service_);
     87   service_->RegisterInvalidationHandler(this);
     88 }
     89 
     90 PushMessagingInvalidationHandler::~PushMessagingInvalidationHandler() {
     91   DCHECK(thread_checker_.CalledOnValidThread());
     92   service_->UnregisterInvalidationHandler(this);
     93 }
     94 
     95 void 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 
    103 void 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 
    111 void 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 
    119 void PushMessagingInvalidationHandler::OnInvalidatorStateChange(
    120     syncer::InvalidatorState state) {
    121   DCHECK(thread_checker_.CalledOnValidThread());
    122   // Nothing to do.
    123 }
    124 
    125 void 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 
    183 std::string PushMessagingInvalidationHandler::GetOwnerName() const {
    184   return "PushMessagingApi";
    185 }
    186 
    187 void 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