Home | History | Annotate | Download | only in glue
      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