Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(326)

Side by Side Diff: chrome/browser/sync/glue/extension_sync.cc

Issue 3110008: Massive refactoring of extensions sync code (Closed) Base URL: 76.121.192.83:~/projects/chromium/src
Patch Set: Fixed compile error Created 10 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW
« no previous file with comments | « chrome/browser/sync/glue/extension_sync.h ('k') | chrome/browser/sync/glue/extension_sync_traits.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698