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