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