1 // Copyright (c) 2011 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/sync/glue/extension_sync.h" 6 7 #include <utility> 8 9 #include "base/logging.h" 10 #include "chrome/browser/extensions/extension_updater.h" 11 #include "chrome/browser/extensions/extension_service.h" 12 #include "chrome/browser/extensions/extension_sync_data.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/sync/engine/syncapi.h" 15 #include "chrome/browser/sync/glue/extension_data.h" 16 #include "chrome/browser/sync/glue/extension_sync_traits.h" 17 #include "chrome/browser/sync/glue/extension_util.h" 18 #include "chrome/browser/sync/profile_sync_service.h" 19 20 namespace browser_sync { 21 22 bool RootNodeHasChildren(const char* tag, 23 sync_api::UserShare* user_share, 24 bool* has_children) { 25 CHECK(has_children); 26 *has_children = false; 27 sync_api::ReadTransaction trans(user_share); 28 sync_api::ReadNode node(&trans); 29 if (!node.InitByTagLookup(tag)) { 30 LOG(ERROR) << "Root node with tag " << tag << " does not exist"; 31 return false; 32 } 33 *has_children = node.GetFirstChildId() != sync_api::kInvalidId; 34 return true; 35 } 36 37 namespace { 38 39 // Updates the value in |extension_data_map| from the given data, 40 // creating an entry if necessary. Returns a pointer to the 41 // updated/created ExtensionData object. 42 ExtensionData* SetOrCreateExtensionData( 43 ExtensionDataMap* extension_data_map, 44 ExtensionData::Source source, 45 bool merge_user_properties, 46 const sync_pb::ExtensionSpecifics& data) { 47 DcheckIsExtensionSpecificsValid(data); 48 const std::string& extension_id = data.id(); 49 std::pair<ExtensionDataMap::iterator, bool> result = 50 extension_data_map->insert( 51 std::make_pair(extension_id, 52 ExtensionData::FromData(source, data))); 53 ExtensionData* extension_data = &result.first->second; 54 if (result.second) { 55 // The value was just inserted, so it shouldn't need an update 56 // from source. 57 DCHECK(!extension_data->NeedsUpdate(source)); 58 } else { 59 extension_data->SetData(source, merge_user_properties, data); 60 } 61 return extension_data; 62 } 63 64 // Reads the client data for each extension in |extensions| to be 65 // synced and updates |extension_data_map|. Puts all unsynced 66 // extensions in |unsynced_extensions|. 67 void ReadClientDataFromExtensionList( 68 const ExtensionList& extensions, 69 IsValidAndSyncablePredicate is_valid_and_syncable, 70 const ExtensionServiceInterface& extensions_service, 71 std::set<std::string>* unsynced_extensions, 72 ExtensionDataMap* extension_data_map) { 73 for (ExtensionList::const_iterator it = extensions.begin(); 74 it != extensions.end(); ++it) { 75 CHECK(*it); 76 const Extension& extension = **it; 77 if (is_valid_and_syncable(extension)) { 78 sync_pb::ExtensionSpecifics client_specifics; 79 GetExtensionSpecifics(extension, extensions_service, 80 &client_specifics); 81 DcheckIsExtensionSpecificsValid(client_specifics); 82 const ExtensionData& extension_data = 83 *SetOrCreateExtensionData( 84 extension_data_map, ExtensionData::CLIENT, 85 true, client_specifics); 86 DcheckIsExtensionSpecificsValid(extension_data.merged_data()); 87 // Assumes this is called before any server data is read. 88 DCHECK(extension_data.NeedsUpdate(ExtensionData::SERVER)); 89 DCHECK(!extension_data.NeedsUpdate(ExtensionData::CLIENT)); 90 } else { 91 unsynced_extensions->insert(extension.id()); 92 } 93 } 94 } 95 96 // Simply calls ReadClientDataFromExtensionList() on the list of 97 // enabled and disabled extensions from |extensions_service|. 98 void SlurpClientData( 99 IsValidAndSyncablePredicate is_valid_and_syncable, 100 const ExtensionServiceInterface& extensions_service, 101 std::set<std::string>* unsynced_extensions, 102 ExtensionDataMap* extension_data_map) { 103 const ExtensionList* extensions = extensions_service.extensions(); 104 CHECK(extensions); 105 ReadClientDataFromExtensionList( 106 *extensions, is_valid_and_syncable, extensions_service, 107 unsynced_extensions, extension_data_map); 108 109 const ExtensionList* disabled_extensions = 110 extensions_service.disabled_extensions(); 111 CHECK(disabled_extensions); 112 ReadClientDataFromExtensionList( 113 *disabled_extensions, is_valid_and_syncable, extensions_service, 114 unsynced_extensions, extension_data_map); 115 } 116 117 // Gets the boilerplate error message for not being able to find a 118 // root node. 119 // 120 // TODO(akalin): Put this somewhere where all data types can use it. 121 std::string GetRootNodeDoesNotExistError(const char* root_node_tag) { 122 return 123 std::string("Server did not create the top-level ") + 124 root_node_tag + 125 " node. We might be running against an out-of-date server."; 126 } 127 128 // Gets the data from the server for extensions to be synced and 129 // updates |extension_data_map|. Skips all extensions in 130 // |unsynced_extensions|. 131 bool SlurpServerData( 132 const char* root_node_tag, 133 const ExtensionSpecificsGetter extension_specifics_getter, 134 const std::set<std::string>& unsynced_extensions, 135 sync_api::UserShare* user_share, 136 ExtensionDataMap* extension_data_map) { 137 sync_api::WriteTransaction trans(user_share); 138 sync_api::ReadNode root(&trans); 139 if (!root.InitByTagLookup(root_node_tag)) { 140 LOG(ERROR) << GetRootNodeDoesNotExistError(root_node_tag); 141 return false; 142 } 143 144 int64 id = root.GetFirstChildId(); 145 while (id != sync_api::kInvalidId) { 146 sync_api::ReadNode sync_node(&trans); 147 if (!sync_node.InitByIdLookup(id)) { 148 LOG(ERROR) << "Failed to fetch sync node for id " << id; 149 return false; 150 } 151 const sync_pb::ExtensionSpecifics& server_data = 152 (*extension_specifics_getter)(sync_node); 153 if (!IsExtensionSpecificsValid(server_data)) { 154 LOG(ERROR) << "Invalid extensions specifics for id " << id; 155 return false; 156 } 157 // Don't process server data for extensions we know are 158 // unsyncable. This doesn't catch everything, as if we don't 159 // have the extension already installed we can't check, but we 160 // also check at extension install time. 161 if (unsynced_extensions.find(server_data.id()) == 162 unsynced_extensions.end()) { 163 // Pass in false for merge_user_properties so client user 164 // settings always take precedence. 165 const ExtensionData& extension_data = 166 *SetOrCreateExtensionData( 167 extension_data_map, ExtensionData::SERVER, false, server_data); 168 DcheckIsExtensionSpecificsValid(extension_data.merged_data()); 169 } 170 id = sync_node.GetSuccessorId(); 171 } 172 return true; 173 } 174 175 } // namespace 176 177 bool SlurpExtensionData(const ExtensionSyncTraits& traits, 178 const ExtensionServiceInterface& extensions_service, 179 sync_api::UserShare* user_share, 180 ExtensionDataMap* extension_data_map) { 181 std::set<std::string> unsynced_extensions; 182 183 // Read client-side data first so server data takes precedence, and 184 // also so we have an idea of which extensions are unsyncable. 185 SlurpClientData( 186 traits.is_valid_and_syncable, extensions_service, 187 &unsynced_extensions, extension_data_map); 188 189 if (!SlurpServerData( 190 traits.root_node_tag, traits.extension_specifics_getter, 191 unsynced_extensions, user_share, extension_data_map)) { 192 return false; 193 } 194 return true; 195 } 196 197 namespace { 198 199 // Updates the server data from the given extension data. 200 // extension_data->ServerNeedsUpdate() must hold before this function 201 // is called. Returns whether or not the update was successful. If 202 // the update was successful, extension_data->ServerNeedsUpdate() will 203 // be false after this function is called. This function leaves 204 // extension_data->ClientNeedsUpdate() unchanged. 205 bool UpdateServer( 206 const ExtensionSyncTraits& traits, 207 ExtensionData* extension_data, 208 sync_api::WriteTransaction* trans) { 209 DCHECK(extension_data->NeedsUpdate(ExtensionData::SERVER)); 210 const sync_pb::ExtensionSpecifics& specifics = 211 extension_data->merged_data(); 212 const std::string& id = specifics.id(); 213 sync_api::WriteNode write_node(trans); 214 if (write_node.InitByClientTagLookup(traits.model_type, id)) { 215 (*traits.extension_specifics_setter)(specifics, &write_node); 216 } else { 217 sync_api::ReadNode root(trans); 218 if (!root.InitByTagLookup(traits.root_node_tag)) { 219 LOG(ERROR) << GetRootNodeDoesNotExistError(traits.root_node_tag); 220 return false; 221 } 222 sync_api::WriteNode create_node(trans); 223 if (!create_node.InitUniqueByCreation(traits.model_type, root, id)) { 224 LOG(ERROR) << "Could not create node for extension " << id; 225 return false; 226 } 227 (*traits.extension_specifics_setter)(specifics, &create_node); 228 } 229 bool old_client_needs_update = 230 extension_data->NeedsUpdate(ExtensionData::CLIENT); 231 extension_data->ResolveData(ExtensionData::SERVER); 232 DCHECK(!extension_data->NeedsUpdate(ExtensionData::SERVER)); 233 DCHECK_EQ(extension_data->NeedsUpdate(ExtensionData::CLIENT), 234 old_client_needs_update); 235 return true; 236 } 237 238 } // namespace 239 240 bool FlushExtensionData(const ExtensionSyncTraits& traits, 241 const ExtensionDataMap& extension_data_map, 242 ExtensionServiceInterface* extensions_service, 243 sync_api::UserShare* user_share) { 244 sync_api::WriteTransaction trans(user_share); 245 sync_api::ReadNode root(&trans); 246 if (!root.InitByTagLookup(traits.root_node_tag)) { 247 LOG(ERROR) << GetRootNodeDoesNotExistError(traits.root_node_tag); 248 return false; 249 } 250 251 // Update server and client as necessary. 252 for (ExtensionDataMap::const_iterator it = extension_data_map.begin(); 253 it != extension_data_map.end(); ++it) { 254 ExtensionData extension_data = it->second; 255 // Update server first. 256 if (extension_data.NeedsUpdate(ExtensionData::SERVER)) { 257 if (!UpdateServer(traits, &extension_data, &trans)) { 258 LOG(ERROR) << "Could not update server data for extension " 259 << it->first; 260 return false; 261 } 262 } 263 DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER)); 264 ExtensionSyncData sync_data; 265 if (!GetExtensionSyncData(extension_data.merged_data(), &sync_data)) { 266 // TODO(akalin): Should probably recover or drop. 267 NOTREACHED(); 268 return false; 269 } 270 extensions_service->ProcessSyncData(sync_data, 271 traits.is_valid_and_syncable); 272 } 273 return true; 274 } 275 276 bool UpdateServerData(const ExtensionSyncTraits& traits, 277 const Extension& extension, 278 const ExtensionServiceInterface& extensions_service, 279 sync_api::UserShare* user_share, 280 std::string* error) { 281 const std::string& id = extension.id(); 282 if (!traits.is_valid_and_syncable(extension)) { 283 *error = 284 std::string("UpdateServerData() called for invalid or " 285 "unsyncable extension ") + id; 286 LOG(DFATAL) << *error; 287 return false; 288 } 289 290 sync_pb::ExtensionSpecifics client_data; 291 GetExtensionSpecifics(extension, extensions_service, 292 &client_data); 293 DcheckIsExtensionSpecificsValid(client_data); 294 ExtensionData extension_data = 295 ExtensionData::FromData(ExtensionData::CLIENT, client_data); 296 297 sync_api::WriteTransaction trans(user_share); 298 299 sync_api::ReadNode node(&trans); 300 if (node.InitByClientTagLookup(traits.model_type, id)) { 301 sync_pb::ExtensionSpecifics server_data = 302 (*traits.extension_specifics_getter)(node); 303 if (IsExtensionSpecificsValid(server_data)) { 304 // If server node exists and is valid, update |extension_data| 305 // from it (but with it taking precedence). 306 extension_data = 307 ExtensionData::FromData(ExtensionData::SERVER, server_data); 308 extension_data.SetData(ExtensionData::CLIENT, true, client_data); 309 } else { 310 LOG(ERROR) << "Invalid extensions specifics for id " << id 311 << "; treating as empty"; 312 } 313 } 314 315 if (extension_data.NeedsUpdate(ExtensionData::SERVER)) { 316 if (!UpdateServer(traits, &extension_data, &trans)) { 317 *error = 318 std::string("Could not update server data for extension ") + id; 319 LOG(ERROR) << *error; 320 return false; 321 } 322 } 323 DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER)); 324 // Client may still need updating, e.g. if we disable an extension 325 // while it's being auto-updated. If so, then we'll be called 326 // again once the auto-update is finished. 327 // 328 // TODO(akalin): Figure out a way to tell when the above happens, 329 // so we know exactly what NeedsUpdate(CLIENT) should return. 330 return true; 331 } 332 333 void RemoveServerData(const ExtensionSyncTraits& traits, 334 const std::string& id, 335 sync_api::UserShare* user_share) { 336 sync_api::WriteTransaction trans(user_share); 337 sync_api::WriteNode write_node(&trans); 338 if (write_node.InitByClientTagLookup(traits.model_type, id)) { 339 write_node.Remove(); 340 } else { 341 LOG(ERROR) << "Server data does not exist for extension " << id; 342 } 343 } 344 345 } // namespace browser_sync 346