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