• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RootNodeHasChildren(const char * tag,sync_api::UserShare * user_share,bool * has_children)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.
SetOrCreateExtensionData(ExtensionDataMap * extension_data_map,ExtensionData::Source source,bool merge_user_properties,const sync_pb::ExtensionSpecifics & data)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|.
ReadClientDataFromExtensionList(const ExtensionList & extensions,IsValidAndSyncablePredicate is_valid_and_syncable,const ExtensionServiceInterface & extensions_service,std::set<std::string> * unsynced_extensions,ExtensionDataMap * extension_data_map)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|.
SlurpClientData(IsValidAndSyncablePredicate is_valid_and_syncable,const ExtensionServiceInterface & extensions_service,std::set<std::string> * unsynced_extensions,ExtensionDataMap * extension_data_map)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.
GetRootNodeDoesNotExistError(const char * root_node_tag)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|.
SlurpServerData(const char * root_node_tag,const ExtensionSpecificsGetter extension_specifics_getter,const std::set<std::string> & unsynced_extensions,sync_api::UserShare * user_share,ExtensionDataMap * extension_data_map)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 
SlurpExtensionData(const ExtensionSyncTraits & traits,const ExtensionServiceInterface & extensions_service,sync_api::UserShare * user_share,ExtensionDataMap * extension_data_map)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.
UpdateServer(const ExtensionSyncTraits & traits,ExtensionData * extension_data,sync_api::WriteTransaction * trans)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 
FlushExtensionData(const ExtensionSyncTraits & traits,const ExtensionDataMap & extension_data_map,ExtensionServiceInterface * extensions_service,sync_api::UserShare * user_share)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 
UpdateServerData(const ExtensionSyncTraits & traits,const Extension & extension,const ExtensionServiceInterface & extensions_service,sync_api::UserShare * user_share,std::string * error)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 
RemoveServerData(const ExtensionSyncTraits & traits,const std::string & id,sync_api::UserShare * user_share)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