OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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/extensions_service.h" |
| 12 #include "chrome/browser/profile.h" |
| 13 #include "chrome/browser/sync/engine/syncapi.h" |
| 14 #include "chrome/browser/sync/glue/extension_data.h" |
| 15 #include "chrome/browser/sync/glue/extension_sync_traits.h" |
| 16 #include "chrome/browser/sync/profile_sync_service.h" |
| 17 |
| 18 namespace browser_sync { |
| 19 |
| 20 bool RootNodeHasChildren(const char* tag, |
| 21 ProfileSyncService* sync_service, |
| 22 bool* has_children) { |
| 23 CHECK(has_children); |
| 24 *has_children = false; |
| 25 sync_api::ReadTransaction trans( |
| 26 sync_service->backend()->GetUserShareHandle()); |
| 27 sync_api::ReadNode node(&trans); |
| 28 if (!node.InitByTagLookup(tag)) { |
| 29 LOG(ERROR) << "Root node with tag " << tag << " does not exist"; |
| 30 return false; |
| 31 } |
| 32 *has_children = node.GetFirstChildId() != sync_api::kInvalidId; |
| 33 return true; |
| 34 } |
| 35 |
| 36 ExtensionsService* GetExtensionsServiceFromProfile( |
| 37 Profile* profile) { |
| 38 CHECK(profile); |
| 39 ExtensionsService* extensions_service = profile->GetExtensionsService(); |
| 40 CHECK(extensions_service); |
| 41 return extensions_service; |
| 42 } |
| 43 |
| 44 namespace { |
| 45 |
| 46 ExtensionsService* GetExtensionsServiceFromProfileSyncService( |
| 47 ProfileSyncService* sync_service) { |
| 48 CHECK(sync_service); |
| 49 return GetExtensionsServiceFromProfile(sync_service->profile()); |
| 50 } |
| 51 |
| 52 // Updates the value in |extension_data_map| from the given data, |
| 53 // creating an entry if necessary. Returns a pointer to the |
| 54 // updated/created ExtensionData object. |
| 55 ExtensionData* SetOrCreateExtensionData( |
| 56 ExtensionDataMap* extension_data_map, |
| 57 ExtensionData::Source source, |
| 58 bool merge_user_properties, |
| 59 const sync_pb::ExtensionSpecifics& data) { |
| 60 DcheckIsExtensionSpecificsValid(data); |
| 61 const std::string& extension_id = data.id(); |
| 62 std::pair<ExtensionDataMap::iterator, bool> result = |
| 63 extension_data_map->insert( |
| 64 std::make_pair(extension_id, |
| 65 ExtensionData::FromData(source, data))); |
| 66 ExtensionData* extension_data = &result.first->second; |
| 67 if (result.second) { |
| 68 // The value was just inserted, so it shouldn't need an update |
| 69 // from source. |
| 70 DCHECK(!extension_data->NeedsUpdate(source)); |
| 71 } else { |
| 72 extension_data->SetData(source, merge_user_properties, data); |
| 73 } |
| 74 return extension_data; |
| 75 } |
| 76 |
| 77 // Reads the client data for each extension in |extensions| to be |
| 78 // synced and updates |extension_data_map|. Puts all unsynced |
| 79 // extensions in |unsynced_extensions|. |
| 80 void ReadClientDataFromExtensionList( |
| 81 const ExtensionList& extensions, |
| 82 const ExtensionTypeSet& allowed_extension_types, |
| 83 ExtensionsService* extensions_service, |
| 84 std::set<std::string>* unsynced_extensions, |
| 85 ExtensionDataMap* extension_data_map) { |
| 86 for (ExtensionList::const_iterator it = extensions.begin(); |
| 87 it != extensions.end(); ++it) { |
| 88 CHECK(*it); |
| 89 const Extension& extension = **it; |
| 90 if (IsExtensionValidAndSyncable(extension, allowed_extension_types)) { |
| 91 sync_pb::ExtensionSpecifics client_specifics; |
| 92 GetExtensionSpecifics(extension, extensions_service, |
| 93 &client_specifics); |
| 94 DcheckIsExtensionSpecificsValid(client_specifics); |
| 95 const ExtensionData& extension_data = |
| 96 *SetOrCreateExtensionData( |
| 97 extension_data_map, ExtensionData::CLIENT, |
| 98 true, client_specifics); |
| 99 DcheckIsExtensionSpecificsValid(extension_data.merged_data()); |
| 100 // Assumes this is called before any server data is read. |
| 101 DCHECK(extension_data.NeedsUpdate(ExtensionData::SERVER)); |
| 102 DCHECK(!extension_data.NeedsUpdate(ExtensionData::CLIENT)); |
| 103 } else { |
| 104 unsynced_extensions->insert(extension.id()); |
| 105 } |
| 106 } |
| 107 } |
| 108 |
| 109 // Simply calls ReadClientDataFromExtensionList() on the list of |
| 110 // enabled and disabled extensions from |extensions_service|. |
| 111 void SlurpClientData( |
| 112 const ExtensionTypeSet& allowed_extension_types, |
| 113 ExtensionsService* extensions_service, |
| 114 std::set<std::string>* unsynced_extensions, |
| 115 ExtensionDataMap* extension_data_map) { |
| 116 const ExtensionList* extensions = extensions_service->extensions(); |
| 117 CHECK(extensions); |
| 118 ReadClientDataFromExtensionList( |
| 119 *extensions, allowed_extension_types, extensions_service, |
| 120 unsynced_extensions, extension_data_map); |
| 121 |
| 122 const ExtensionList* disabled_extensions = |
| 123 extensions_service->disabled_extensions(); |
| 124 CHECK(disabled_extensions); |
| 125 ReadClientDataFromExtensionList( |
| 126 *disabled_extensions, allowed_extension_types, extensions_service, |
| 127 unsynced_extensions, extension_data_map); |
| 128 } |
| 129 |
| 130 // Gets the boilerplate error message for not being able to find a |
| 131 // root node. |
| 132 // |
| 133 // TODO(akalin): Put this somewhere where all data types can use it. |
| 134 std::string GetRootNodeDoesNotExistError(const char* root_node_tag) { |
| 135 return |
| 136 std::string("Server did not create the top-level ") + |
| 137 root_node_tag + |
| 138 " node. We might be running against an out-of-date server."; |
| 139 } |
| 140 |
| 141 // Gets the data from the server for extensions to be synced and |
| 142 // updates |extension_data_map|. Skips all extensions in |
| 143 // |unsynced_extensions|. |
| 144 bool SlurpServerData( |
| 145 const char* root_node_tag, |
| 146 const ExtensionSpecificsGetter extension_specifics_getter, |
| 147 const std::set<std::string>& unsynced_extensions, |
| 148 ProfileSyncService* sync_service, |
| 149 ExtensionDataMap* extension_data_map) { |
| 150 sync_api::WriteTransaction trans( |
| 151 sync_service->backend()->GetUserShareHandle()); |
| 152 sync_api::ReadNode root(&trans); |
| 153 if (!root.InitByTagLookup(root_node_tag)) { |
| 154 LOG(ERROR) << GetRootNodeDoesNotExistError(root_node_tag); |
| 155 return false; |
| 156 } |
| 157 |
| 158 int64 id = root.GetFirstChildId(); |
| 159 while (id != sync_api::kInvalidId) { |
| 160 sync_api::ReadNode sync_node(&trans); |
| 161 if (!sync_node.InitByIdLookup(id)) { |
| 162 LOG(ERROR) << "Failed to fetch sync node for id " << id; |
| 163 return false; |
| 164 } |
| 165 const sync_pb::ExtensionSpecifics& server_data = |
| 166 (*extension_specifics_getter)(sync_node); |
| 167 if (!IsExtensionSpecificsValid(server_data)) { |
| 168 LOG(ERROR) << "Invalid extensions specifics for id " << id; |
| 169 return false; |
| 170 } |
| 171 // Don't process server data for extensions we know are |
| 172 // unsyncable. This doesn't catch everything, as if we don't |
| 173 // have the extension already installed we can't check, but we |
| 174 // also check at extension install time. |
| 175 if (unsynced_extensions.find(server_data.id()) == |
| 176 unsynced_extensions.end()) { |
| 177 // Pass in false for merge_user_properties so client user |
| 178 // settings always take precedence. |
| 179 const ExtensionData& extension_data = |
| 180 *SetOrCreateExtensionData( |
| 181 extension_data_map, ExtensionData::SERVER, false, server_data); |
| 182 DcheckIsExtensionSpecificsValid(extension_data.merged_data()); |
| 183 } |
| 184 id = sync_node.GetSuccessorId(); |
| 185 } |
| 186 return true; |
| 187 } |
| 188 |
| 189 } // namespace |
| 190 |
| 191 bool SlurpExtensionData(const ExtensionSyncTraits& traits, |
| 192 ProfileSyncService* sync_service, |
| 193 ExtensionDataMap* extension_data_map) { |
| 194 ExtensionsService* extensions_service = |
| 195 GetExtensionsServiceFromProfileSyncService(sync_service); |
| 196 std::set<std::string> unsynced_extensions; |
| 197 |
| 198 // Read client-side data first so server data takes precedence, and |
| 199 // also so we have an idea of which extensions are unsyncable. |
| 200 SlurpClientData( |
| 201 traits.allowed_extension_types, extensions_service, |
| 202 &unsynced_extensions, extension_data_map); |
| 203 |
| 204 if (!SlurpServerData( |
| 205 traits.root_node_tag, traits.extension_specifics_getter, |
| 206 unsynced_extensions, sync_service, extension_data_map)) { |
| 207 return false; |
| 208 } |
| 209 return true; |
| 210 } |
| 211 |
| 212 namespace { |
| 213 |
| 214 // Updates the server data from the given extension data. |
| 215 // extension_data->ServerNeedsUpdate() must hold before this function |
| 216 // is called. Returns whether or not the update was successful. If |
| 217 // the update was successful, extension_data->ServerNeedsUpdate() will |
| 218 // be false after this function is called. This function leaves |
| 219 // extension_data->ClientNeedsUpdate() unchanged. |
| 220 bool UpdateServer( |
| 221 const ExtensionSyncTraits& traits, |
| 222 ExtensionData* extension_data, |
| 223 sync_api::WriteTransaction* trans) { |
| 224 DCHECK(extension_data->NeedsUpdate(ExtensionData::SERVER)); |
| 225 const sync_pb::ExtensionSpecifics& specifics = |
| 226 extension_data->merged_data(); |
| 227 const std::string& id = specifics.id(); |
| 228 sync_api::WriteNode write_node(trans); |
| 229 if (write_node.InitByClientTagLookup(traits.model_type, id)) { |
| 230 (*traits.extension_specifics_setter)(specifics, &write_node); |
| 231 } else { |
| 232 sync_api::ReadNode root(trans); |
| 233 if (!root.InitByTagLookup(traits.root_node_tag)) { |
| 234 LOG(ERROR) << GetRootNodeDoesNotExistError(traits.root_node_tag); |
| 235 return false; |
| 236 } |
| 237 sync_api::WriteNode create_node(trans); |
| 238 if (!create_node.InitUniqueByCreation(traits.model_type, root, id)) { |
| 239 LOG(ERROR) << "Could not create node for extension " << id; |
| 240 return false; |
| 241 } |
| 242 (*traits.extension_specifics_setter)(specifics, &create_node); |
| 243 } |
| 244 bool old_client_needs_update = |
| 245 extension_data->NeedsUpdate(ExtensionData::CLIENT); |
| 246 extension_data->ResolveData(ExtensionData::SERVER); |
| 247 DCHECK(!extension_data->NeedsUpdate(ExtensionData::SERVER)); |
| 248 DCHECK_EQ(extension_data->NeedsUpdate(ExtensionData::CLIENT), |
| 249 old_client_needs_update); |
| 250 return true; |
| 251 } |
| 252 |
| 253 // Tries to update the client data from the given extension data. |
| 254 // extension_data->ServerNeedsUpdate() must not hold and |
| 255 // extension_data->ClientNeedsUpdate() must hold before this function |
| 256 // is called. If the update was successful, |
| 257 // extension_data->ClientNeedsUpdate() will be false after this |
| 258 // function is called. Otherwise, the extension needs updating to a |
| 259 // new version. |
| 260 void TryUpdateClient( |
| 261 const ExtensionTypeSet& allowed_extension_types, |
| 262 ExtensionsService* extensions_service, |
| 263 ExtensionData* extension_data) { |
| 264 DCHECK(!extension_data->NeedsUpdate(ExtensionData::SERVER)); |
| 265 DCHECK(extension_data->NeedsUpdate(ExtensionData::CLIENT)); |
| 266 const sync_pb::ExtensionSpecifics& specifics = |
| 267 extension_data->merged_data(); |
| 268 DcheckIsExtensionSpecificsValid(specifics); |
| 269 const std::string& id = specifics.id(); |
| 270 Extension* extension = extensions_service->GetExtensionById(id, true); |
| 271 if (extension) { |
| 272 if (!IsExtensionValidAndSyncable(*extension, allowed_extension_types)) { |
| 273 LOG(DFATAL) << "TryUpdateClient() called for non-syncable extension " |
| 274 << extension->id(); |
| 275 return; |
| 276 } |
| 277 SetExtensionProperties(specifics, extensions_service, extension); |
| 278 { |
| 279 sync_pb::ExtensionSpecifics extension_specifics; |
| 280 GetExtensionSpecifics(*extension, extensions_service, |
| 281 &extension_specifics); |
| 282 DCHECK(AreExtensionSpecificsUserPropertiesEqual( |
| 283 specifics, extension_specifics)) |
| 284 << ExtensionSpecificsToString(specifics) << ", " |
| 285 << ExtensionSpecificsToString(extension_specifics); |
| 286 } |
| 287 if (!IsExtensionOutdated(*extension, specifics)) { |
| 288 extension_data->ResolveData(ExtensionData::CLIENT); |
| 289 DCHECK(!extension_data->NeedsUpdate(ExtensionData::CLIENT)); |
| 290 } |
| 291 } else { |
| 292 GURL update_url(specifics.update_url()); |
| 293 // TODO(akalin): Replace silent update with a list of enabled |
| 294 // permissions. |
| 295 extensions_service->AddPendingExtension( |
| 296 id, update_url, false, true, |
| 297 specifics.enabled(), specifics.incognito_enabled()); |
| 298 } |
| 299 DCHECK(!extension_data->NeedsUpdate(ExtensionData::SERVER)); |
| 300 } |
| 301 |
| 302 // Kick off a run of the extension updater. |
| 303 // |
| 304 // TODO(akalin): Combine this with the similar function in |
| 305 // theme_util.cc. |
| 306 void NudgeExtensionUpdater(ExtensionsService* extensions_service) { |
| 307 ExtensionUpdater* extension_updater = extensions_service->updater(); |
| 308 // Auto-updates should now be on always (see the construction of the |
| 309 // ExtensionsService in ProfileImpl::InitExtensions()). |
| 310 if (extension_updater) { |
| 311 extension_updater->CheckNow(); |
| 312 } else { |
| 313 LOG(DFATAL) << "Extension updater unexpectedly NULL; " |
| 314 << "auto-updates may be turned off"; |
| 315 } |
| 316 } |
| 317 |
| 318 } // namespace |
| 319 |
| 320 bool FlushExtensionData(const ExtensionSyncTraits& traits, |
| 321 const ExtensionDataMap& extension_data_map, |
| 322 ProfileSyncService* sync_service) { |
| 323 sync_api::WriteTransaction trans( |
| 324 sync_service->backend()->GetUserShareHandle()); |
| 325 sync_api::ReadNode root(&trans); |
| 326 if (!root.InitByTagLookup(traits.root_node_tag)) { |
| 327 LOG(ERROR) << GetRootNodeDoesNotExistError(traits.root_node_tag); |
| 328 return false; |
| 329 } |
| 330 |
| 331 ExtensionsService* extensions_service = |
| 332 GetExtensionsServiceFromProfileSyncService(sync_service); |
| 333 |
| 334 // Update server and client as necessary. |
| 335 bool should_nudge_extension_updater = false; |
| 336 for (ExtensionDataMap::const_iterator it = extension_data_map.begin(); |
| 337 it != extension_data_map.end(); ++it) { |
| 338 ExtensionData extension_data = it->second; |
| 339 // Update server first. |
| 340 if (extension_data.NeedsUpdate(ExtensionData::SERVER)) { |
| 341 if (!UpdateServer(traits, &extension_data, &trans)) { |
| 342 LOG(ERROR) << "Could not update server data for extension " |
| 343 << it->first; |
| 344 return false; |
| 345 } |
| 346 } |
| 347 DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER)); |
| 348 if (extension_data.NeedsUpdate(ExtensionData::CLIENT)) { |
| 349 TryUpdateClient(traits.allowed_extension_types, |
| 350 extensions_service, &extension_data); |
| 351 if (extension_data.NeedsUpdate(ExtensionData::CLIENT)) { |
| 352 should_nudge_extension_updater = true; |
| 353 } |
| 354 } |
| 355 DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER)); |
| 356 } |
| 357 |
| 358 if (should_nudge_extension_updater) { |
| 359 NudgeExtensionUpdater(extensions_service); |
| 360 } |
| 361 |
| 362 return true; |
| 363 } |
| 364 |
| 365 bool UpdateServerData(const ExtensionSyncTraits& traits, |
| 366 const Extension& extension, |
| 367 ProfileSyncService* sync_service, |
| 368 std::string* error) { |
| 369 const std::string& id = extension.id(); |
| 370 if (!IsExtensionValidAndSyncable(extension, |
| 371 traits.allowed_extension_types)) { |
| 372 *error = |
| 373 std::string("UpdateServerData() called for invalid or " |
| 374 "unsyncable extension ") + id; |
| 375 LOG(DFATAL) << *error; |
| 376 return false; |
| 377 } |
| 378 |
| 379 ExtensionsService* extensions_service = |
| 380 GetExtensionsServiceFromProfileSyncService(sync_service); |
| 381 sync_pb::ExtensionSpecifics client_data; |
| 382 GetExtensionSpecifics(extension, extensions_service, &client_data); |
| 383 DcheckIsExtensionSpecificsValid(client_data); |
| 384 ExtensionData extension_data = |
| 385 ExtensionData::FromData(ExtensionData::CLIENT, client_data); |
| 386 |
| 387 sync_api::WriteTransaction trans( |
| 388 sync_service->backend()->GetUserShareHandle()); |
| 389 |
| 390 sync_api::ReadNode node(&trans); |
| 391 if (node.InitByClientTagLookup(traits.model_type, id)) { |
| 392 sync_pb::ExtensionSpecifics server_data = |
| 393 (*traits.extension_specifics_getter)(node); |
| 394 if (IsExtensionSpecificsValid(server_data)) { |
| 395 // If server node exists and is valid, update |extension_data| |
| 396 // from it (but with it taking precedence). |
| 397 extension_data = |
| 398 ExtensionData::FromData(ExtensionData::SERVER, server_data); |
| 399 extension_data.SetData(ExtensionData::CLIENT, true, client_data); |
| 400 } else { |
| 401 LOG(ERROR) << "Invalid extensions specifics for id " << id |
| 402 << "; treating as empty"; |
| 403 } |
| 404 } |
| 405 |
| 406 if (extension_data.NeedsUpdate(ExtensionData::SERVER)) { |
| 407 if (!UpdateServer(traits, &extension_data, &trans)) { |
| 408 *error = |
| 409 std::string("Could not update server data for extension ") + id; |
| 410 LOG(ERROR) << *error; |
| 411 return false; |
| 412 } |
| 413 } |
| 414 DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER)); |
| 415 // Client may still need updating, e.g. if we disable an extension |
| 416 // while it's being auto-updated. If so, then we'll be called |
| 417 // again once the auto-update is finished. |
| 418 // |
| 419 // TODO(akalin): Figure out a way to tell when the above happens, |
| 420 // so we know exactly what NeedsUpdate(CLIENT) should return. |
| 421 return true; |
| 422 } |
| 423 |
| 424 void RemoveServerData(const ExtensionSyncTraits& traits, |
| 425 const Extension& extension, |
| 426 ProfileSyncService* sync_service) { |
| 427 const std::string& id = extension.id(); |
| 428 if (!IsExtensionValidAndSyncable(extension, |
| 429 traits.allowed_extension_types)) { |
| 430 LOG(DFATAL) << "RemoveServerData() called for invalid or " |
| 431 << "unsyncable extension " << id; |
| 432 return; |
| 433 } |
| 434 |
| 435 sync_api::WriteTransaction trans( |
| 436 sync_service->backend()->GetUserShareHandle()); |
| 437 sync_api::WriteNode write_node(&trans); |
| 438 if (write_node.InitByClientTagLookup(traits.model_type, id)) { |
| 439 write_node.Remove(); |
| 440 } else { |
| 441 LOG(ERROR) << "Server data does not exist for extension " << id; |
| 442 } |
| 443 } |
| 444 |
| 445 void UpdateClient(const ExtensionSyncTraits& traits, |
| 446 const sync_pb::ExtensionSpecifics& server_data, |
| 447 ExtensionsService* extensions_service) { |
| 448 DcheckIsExtensionSpecificsValid(server_data); |
| 449 ExtensionData extension_data = |
| 450 ExtensionData::FromData(ExtensionData::SERVER, server_data); |
| 451 Extension* extension = |
| 452 extensions_service->GetExtensionById(server_data.id(), true); |
| 453 if (extension) { |
| 454 if (!IsExtensionValidAndSyncable( |
| 455 *extension, traits.allowed_extension_types)) { |
| 456 LOG(WARNING) << "Ignoring server data for invalid or " |
| 457 << "non-syncable extension " << extension->id(); |
| 458 return; |
| 459 } |
| 460 sync_pb::ExtensionSpecifics client_data; |
| 461 GetExtensionSpecifics(*extension, extensions_service, &client_data); |
| 462 DcheckIsExtensionSpecificsValid(client_data); |
| 463 extension_data = |
| 464 ExtensionData::FromData(ExtensionData::CLIENT, client_data); |
| 465 extension_data.SetData(ExtensionData::SERVER, true, server_data); |
| 466 } |
| 467 DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER)); |
| 468 if (extension_data.NeedsUpdate(ExtensionData::CLIENT)) { |
| 469 TryUpdateClient(traits.allowed_extension_types, |
| 470 extensions_service, &extension_data); |
| 471 if (extension_data.NeedsUpdate(ExtensionData::CLIENT)) { |
| 472 NudgeExtensionUpdater(extensions_service); |
| 473 } |
| 474 } |
| 475 DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER)); |
| 476 } |
| 477 |
| 478 void RemoveFromClient(const ExtensionSyncTraits& traits, |
| 479 const std::string& id, |
| 480 ExtensionsService* extensions_service) { |
| 481 Extension* extension = extensions_service->GetExtensionById(id, true); |
| 482 if (extension) { |
| 483 if (IsExtensionValidAndSyncable(*extension, |
| 484 traits.allowed_extension_types)) { |
| 485 extensions_service->UninstallExtension(id, false); |
| 486 } else { |
| 487 LOG(WARNING) << "Ignoring server data for invalid or " |
| 488 << "non-syncable extension " << extension->id(); |
| 489 } |
| 490 } else { |
| 491 LOG(ERROR) << "Trying to uninstall nonexistent extension " << id; |
| 492 } |
| 493 } |
| 494 |
| 495 } // namespace browser_sync |
OLD | NEW |