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 "chrome/browser/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 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 reciever, 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 reciever, 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 183void PushMessagingInvalidationHandler::UpdateRegistrations() { 184 syncer::ObjectIdSet ids; 185 for (std::set<std::string>::const_iterator it = 186 registered_extensions_.begin(); it != registered_extensions_.end(); 187 ++it) { 188 const syncer::ObjectIdSet& object_ids = ExtensionIdToObjectIds(*it); 189 ids.insert(object_ids.begin(), object_ids.end()); 190 } 191 service_->UpdateRegisteredInvalidationIds(this, ids); 192} 193 194} // namespace extensions 195