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

Side by Side Diff: chrome/browser/sync/glue/extension_model_associator.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
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/sync/glue/extension_model_associator.h" 5 #include "chrome/browser/sync/glue/extension_model_associator.h"
6 6
7 #include <map>
8 #include <utility>
9
10 #include "base/logging.h" 7 #include "base/logging.h"
11 #include "base/utf_string_conversions.h" 8 #include "chrome/browser/chrome_thread.h"
12 #include "chrome/browser/extensions/extension_updater.h" 9 #include "chrome/browser/sync/glue/extension_data.h"
13 #include "chrome/browser/extensions/extensions_service.h" 10 #include "chrome/browser/sync/glue/extension_sync_traits.h"
14 #include "chrome/browser/profile.h" 11 #include "chrome/browser/sync/glue/extension_sync.h"
15 #include "chrome/browser/sync/engine/syncapi.h"
16 #include "chrome/browser/sync/glue/extension_util.h"
17 #include "chrome/browser/sync/profile_sync_service.h"
18 #include "chrome/browser/sync/protocol/extension_specifics.pb.h" 12 #include "chrome/browser/sync/protocol/extension_specifics.pb.h"
19 #include "chrome/common/extensions/extension.h"
20 13
21 namespace browser_sync { 14 namespace browser_sync {
22 15
23 namespace {
24
25 static const char kExtensionsTag[] = "google_chrome_extensions";
26
27 static const char kNoExtensionsFolderError[] =
28 "Server did not create the top-level extensions node. We "
29 "might be running against an out-of-date server.";
30
31 typedef std::map<std::string, ExtensionData> ExtensionDataMap;
32
33 ExtensionData* SetOrCreateData(
34 ExtensionDataMap* extension_data_map,
35 ExtensionData::Source source,
36 bool merge_user_properties,
37 const sync_pb::ExtensionSpecifics& data) {
38 DcheckIsExtensionSpecificsValid(data);
39 const std::string& extension_id = data.id();
40 std::pair<ExtensionDataMap::iterator, bool> result =
41 extension_data_map->insert(
42 std::make_pair(extension_id,
43 ExtensionData::FromData(source, data)));
44 ExtensionData* extension_data = &result.first->second;
45 if (result.second) {
46 // The value was just inserted, so it shouldn't need an update
47 // from source.
48 DCHECK(!extension_data->NeedsUpdate(source));
49 } else {
50 extension_data->SetData(source, merge_user_properties, data);
51 }
52 return extension_data;
53 }
54
55 void GetSyncableExtensionsClientData(
56 const ExtensionList& extensions,
57 ExtensionsService* extensions_service,
58 std::set<std::string>* unsyncable_extensions,
59 ExtensionDataMap* extension_data_map) {
60 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
61 for (ExtensionList::const_iterator it = extensions.begin();
62 it != extensions.end(); ++it) {
63 CHECK(*it);
64 const Extension& extension = **it;
65 if (IsExtensionSyncable(extension)) {
66 sync_pb::ExtensionSpecifics client_specifics;
67 GetExtensionSpecifics(extension, extensions_service,
68 &client_specifics);
69 DcheckIsExtensionSpecificsValid(client_specifics);
70 const ExtensionData& extension_data =
71 *SetOrCreateData(extension_data_map,
72 ExtensionData::CLIENT, true, client_specifics);
73 DcheckIsExtensionSpecificsValid(extension_data.merged_data());
74 // Assumes this is called before any server data is read.
75 DCHECK(extension_data.NeedsUpdate(ExtensionData::SERVER));
76 DCHECK(!extension_data.NeedsUpdate(ExtensionData::CLIENT));
77 } else {
78 unsyncable_extensions->insert(extension.id());
79 }
80 }
81 }
82
83 } // namespace
84
85 ExtensionModelAssociator::ExtensionModelAssociator( 16 ExtensionModelAssociator::ExtensionModelAssociator(
86 ProfileSyncService* sync_service) : sync_service_(sync_service) { 17 ProfileSyncService* sync_service) : sync_service_(sync_service) {
87 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); 18 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
88 DCHECK(sync_service_); 19 DCHECK(sync_service_);
89 } 20 }
90 21
91 ExtensionModelAssociator::~ExtensionModelAssociator() { 22 ExtensionModelAssociator::~ExtensionModelAssociator() {
92 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); 23 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
93 } 24 }
94 25
95 bool ExtensionModelAssociator::AssociateModels() { 26 bool ExtensionModelAssociator::AssociateModels() {
96 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); 27 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
97 sync_api::WriteTransaction trans( 28 const ExtensionSyncTraits traits = GetExtensionSyncTraits();
98 sync_service_->backend()->GetUserShareHandle()); 29
99 sync_api::ReadNode root(&trans); 30 ExtensionDataMap extension_data_map;
100 if (!root.InitByTagLookup(kExtensionsTag)) { 31 if (!SlurpExtensionData(traits, sync_service_, &extension_data_map)) {
101 LOG(ERROR) << kNoExtensionsFolderError; 32 return false;
33 }
34 if (!FlushExtensionData(traits, extension_data_map, sync_service_)) {
102 return false; 35 return false;
103 } 36 }
104 37
105 std::set<std::string> unsyncable_extensions;
106 ExtensionDataMap extension_data_map;
107
108 // Read client-side data. Do this first so server data takes
109 // precedence.
110 {
111 ExtensionsService* extensions_service = GetExtensionsService();
112
113 const ExtensionList* extensions = extensions_service->extensions();
114 CHECK(extensions);
115 GetSyncableExtensionsClientData(
116 *extensions, extensions_service,
117 &unsyncable_extensions, &extension_data_map);
118
119 const ExtensionList* disabled_extensions =
120 extensions_service->disabled_extensions();
121 CHECK(disabled_extensions);
122 GetSyncableExtensionsClientData(
123 *disabled_extensions, extensions_service,
124 &unsyncable_extensions, &extension_data_map);
125 }
126
127 // Read server-side data.
128 {
129 int64 id = root.GetFirstChildId();
130 while (id != sync_api::kInvalidId) {
131 sync_api::ReadNode sync_node(&trans);
132 if (!sync_node.InitByIdLookup(id)) {
133 LOG(ERROR) << "Failed to fetch sync node for id " << id;
134 return false;
135 }
136 const sync_pb::ExtensionSpecifics& server_data =
137 sync_node.GetExtensionSpecifics();
138 if (!IsExtensionSpecificsValid(server_data)) {
139 LOG(ERROR) << "Invalid extensions specifics for id " << id;
140 return false;
141 }
142 // Don't process server data for extensions we know are
143 // unsyncable. This doesn't catch everything, as if we don't
144 // have the extension already installed we can't check, but we
145 // also check at extension install time.
146 if (unsyncable_extensions.find(server_data.id()) ==
147 unsyncable_extensions.end()) {
148 // Pass in false for merge_user_properties so client user
149 // settings always take precedence.
150 const ExtensionData& extension_data =
151 *SetOrCreateData(&extension_data_map,
152 ExtensionData::SERVER, false, server_data);
153 DcheckIsExtensionSpecificsValid(extension_data.merged_data());
154 }
155 id = sync_node.GetSuccessorId();
156 }
157 }
158
159 // Update server and client as necessary.
160 bool should_nudge_extension_updater = false;
161 for (ExtensionDataMap::iterator it = extension_data_map.begin();
162 it != extension_data_map.end(); ++it) {
163 ExtensionData* extension_data = &it->second;
164 // Update server first.
165 if (extension_data->NeedsUpdate(ExtensionData::SERVER)) {
166 if (!UpdateServer(extension_data, &trans, root)) {
167 LOG(ERROR) << "Could not update server data for extension "
168 << it->first;
169 return false;
170 }
171 }
172 DCHECK(!extension_data->NeedsUpdate(ExtensionData::SERVER));
173 if (extension_data->NeedsUpdate(ExtensionData::CLIENT)) {
174 TryUpdateClient(extension_data);
175 if (extension_data->NeedsUpdate(ExtensionData::CLIENT)) {
176 should_nudge_extension_updater = true;
177 }
178 }
179 DCHECK(!extension_data->NeedsUpdate(ExtensionData::SERVER));
180 }
181
182 if (should_nudge_extension_updater) {
183 NudgeExtensionUpdater();
184 }
185
186 return true; 38 return true;
187 } 39 }
188 40
189 bool ExtensionModelAssociator::DisassociateModels() { 41 bool ExtensionModelAssociator::DisassociateModels() {
190 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); 42 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
191 // Nothing to do. 43 // Nothing to do.
192 return true; 44 return true;
193 } 45 }
194 46
195 bool ExtensionModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { 47 bool ExtensionModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
196 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); 48 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
197 CHECK(has_nodes); 49 const ExtensionSyncTraits traits = GetExtensionSyncTraits();
198 *has_nodes = false; 50 return RootNodeHasChildren(traits.root_node_tag, sync_service_, has_nodes);
199 sync_api::ReadTransaction trans(
200 sync_service_->backend()->GetUserShareHandle());
201 sync_api::ReadNode root(&trans);
202 if (!root.InitByTagLookup(kExtensionsTag)) {
203 LOG(ERROR) << kNoExtensionsFolderError;
204 return false;
205 }
206 // The sync model has user created nodes iff the extensions folder has
207 // any children.
208 *has_nodes = root.GetFirstChildId() != sync_api::kInvalidId;
209 return true;
210 }
211
212 bool ExtensionModelAssociator::OnClientUpdate(const std::string& id) {
213 sync_api::WriteTransaction trans(
214 sync_service_->backend()->GetUserShareHandle());
215 sync_api::ReadNode root(&trans);
216 if (!root.InitByTagLookup(kExtensionsTag)) {
217 LOG(ERROR) << kNoExtensionsFolderError;
218 return false;
219 }
220 ExtensionsService* extensions_service = GetExtensionsService();
221 Extension* extension = extensions_service->GetExtensionById(id, true);
222 if (extension) {
223 if (!IsExtensionSyncable(*extension)) {
224 LOG(DFATAL) << "OnClientUpdate() called for non-syncable extension "
225 << id;
226 return false;
227 }
228 sync_pb::ExtensionSpecifics client_data;
229 GetExtensionSpecifics(*extension, extensions_service, &client_data);
230 DcheckIsExtensionSpecificsValid(client_data);
231 ExtensionData extension_data =
232 ExtensionData::FromData(ExtensionData::CLIENT, client_data);
233 sync_pb::ExtensionSpecifics server_data;
234 if (GetExtensionDataFromServer(id, &trans, root, &server_data)) {
235 extension_data =
236 ExtensionData::FromData(ExtensionData::SERVER, server_data);
237 extension_data.SetData(ExtensionData::CLIENT, true, client_data);
238 }
239 if (extension_data.NeedsUpdate(ExtensionData::SERVER)) {
240 if (!UpdateServer(&extension_data, &trans, root)) {
241 LOG(ERROR) << "Could not update server data for extension " << id;
242 return false;
243 }
244 }
245 DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER));
246 // Client may still need updating, e.g. if we disable an extension
247 // while it's being auto-updated. If so, then we'll be called
248 // again once the auto-update is finished.
249 //
250 // TODO(akalin): Figure out a way to tell when the above happens,
251 // so we know exactly what NeedsUpdate(CLIENT) should return.
252 } else {
253 sync_api::WriteNode write_node(&trans);
254 if (write_node.InitByClientTagLookup(syncable::EXTENSIONS, id)) {
255 write_node.Remove();
256 } else {
257 LOG(ERROR) << "Trying to remove server data for "
258 << "nonexistent extension " << id;
259 }
260 }
261 return true;
262 }
263
264 void ExtensionModelAssociator::OnServerUpdate(
265 const sync_pb::ExtensionSpecifics& server_data) {
266 DcheckIsExtensionSpecificsValid(server_data);
267 ExtensionData extension_data =
268 ExtensionData::FromData(ExtensionData::SERVER, server_data);
269 ExtensionsService* extensions_service = GetExtensionsService();
270 Extension* extension =
271 extensions_service->GetExtensionById(server_data.id(), true);
272 if (extension) {
273 if (!IsExtensionSyncable(*extension)) {
274 // Ignore updates for non-syncable extensions (we may get those
275 // for extensions that were previously syncable).
276 return;
277 }
278 sync_pb::ExtensionSpecifics client_data;
279 GetExtensionSpecifics(*extension, extensions_service, &client_data);
280 DcheckIsExtensionSpecificsValid(client_data);
281 extension_data =
282 ExtensionData::FromData(ExtensionData::CLIENT, client_data);
283 extension_data.SetData(ExtensionData::SERVER, true, server_data);
284 }
285 DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER));
286 if (extension_data.NeedsUpdate(ExtensionData::CLIENT)) {
287 TryUpdateClient(&extension_data);
288 if (extension_data.NeedsUpdate(ExtensionData::CLIENT)) {
289 NudgeExtensionUpdater();
290 }
291 }
292 DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER));
293 }
294
295 void ExtensionModelAssociator::OnServerRemove(const std::string& id) {
296 ExtensionsService* extensions_service = GetExtensionsService();
297 Extension* extension = extensions_service->GetExtensionById(id, true);
298 if (extension) {
299 if (IsExtensionSyncable(*extension)) {
300 extensions_service->UninstallExtension(id, false);
301 }
302 } else {
303 LOG(ERROR) << "Trying to uninstall nonexistent extension " << id;
304 }
305 }
306
307 ExtensionsService* ExtensionModelAssociator::GetExtensionsService() {
308 CHECK(sync_service_);
309 Profile* profile = sync_service_->profile();
310 CHECK(profile);
311 ExtensionsService* extensions_service = profile->GetExtensionsService();
312 CHECK(extensions_service);
313 return extensions_service;
314 }
315
316 bool ExtensionModelAssociator::GetExtensionDataFromServer(
317 const std::string& id, sync_api::WriteTransaction* trans,
318 const sync_api::ReadNode& root,
319 sync_pb::ExtensionSpecifics* server_data) {
320 sync_api::ReadNode sync_node(trans);
321 if (!sync_node.InitByClientTagLookup(syncable::EXTENSIONS, id)) {
322 LOG(ERROR) << "Failed to fetch sync node for id " << id;
323 return false;
324 }
325 const sync_pb::ExtensionSpecifics& read_server_data =
326 sync_node.GetExtensionSpecifics();
327 if (!IsExtensionSpecificsValid(read_server_data)) {
328 LOG(ERROR) << "Invalid extensions specifics for id " << id;
329 return false;
330 }
331 *server_data = read_server_data;
332 return true;
333 }
334
335 namespace {
336
337 void SetNodeData(const sync_pb::ExtensionSpecifics& specifics,
338 sync_api::WriteNode* node) {
339 node->SetTitle(UTF8ToWide(specifics.name()));
340 node->SetExtensionSpecifics(specifics);
341 }
342
343 } // namespace
344
345 bool ExtensionModelAssociator::UpdateServer(
346 ExtensionData* extension_data,
347 sync_api::WriteTransaction* trans,
348 const sync_api::ReadNode& root) {
349 DCHECK(extension_data->NeedsUpdate(ExtensionData::SERVER));
350 const sync_pb::ExtensionSpecifics& specifics =
351 extension_data->merged_data();
352 const std::string& id = specifics.id();
353 sync_api::WriteNode write_node(trans);
354 if (write_node.InitByClientTagLookup(syncable::EXTENSIONS, id)) {
355 SetNodeData(specifics, &write_node);
356 } else {
357 sync_api::WriteNode create_node(trans);
358 if (!create_node.InitUniqueByCreation(syncable::EXTENSIONS, root, id)) {
359 LOG(ERROR) << "Could not create node for extension " << id;
360 return false;
361 }
362 SetNodeData(specifics, &create_node);
363 }
364 bool old_client_needs_update =
365 extension_data->NeedsUpdate(ExtensionData::CLIENT);
366 extension_data->ResolveData(ExtensionData::SERVER);
367 DCHECK(!extension_data->NeedsUpdate(ExtensionData::SERVER));
368 DCHECK_EQ(extension_data->NeedsUpdate(ExtensionData::CLIENT),
369 old_client_needs_update);
370 return true;
371 }
372
373 void ExtensionModelAssociator::TryUpdateClient(
374 ExtensionData* extension_data) {
375 DCHECK(!extension_data->NeedsUpdate(ExtensionData::SERVER));
376 DCHECK(extension_data->NeedsUpdate(ExtensionData::CLIENT));
377 const sync_pb::ExtensionSpecifics& specifics =
378 extension_data->merged_data();
379 DcheckIsExtensionSpecificsValid(specifics);
380 ExtensionsService* extensions_service = GetExtensionsService();
381 const std::string& id = specifics.id();
382 Extension* extension = extensions_service->GetExtensionById(id, true);
383 if (extension) {
384 if (!IsExtensionSyncable(*extension)) {
385 LOG(DFATAL) << "TryUpdateClient() called for non-syncable extension "
386 << extension->id();
387 return;
388 }
389 SetExtensionProperties(specifics, extensions_service, extension);
390 {
391 sync_pb::ExtensionSpecifics extension_specifics;
392 GetExtensionSpecifics(*extension, extensions_service,
393 &extension_specifics);
394 DCHECK(AreExtensionSpecificsUserPropertiesEqual(
395 specifics, extension_specifics))
396 << ExtensionSpecificsToString(specifics) << ", "
397 << ExtensionSpecificsToString(extension_specifics);
398 }
399 if (!IsExtensionOutdated(*extension, specifics)) {
400 extension_data->ResolveData(ExtensionData::CLIENT);
401 DCHECK(!extension_data->NeedsUpdate(ExtensionData::CLIENT));
402 }
403 } else {
404 GURL update_url(specifics.update_url());
405 // TODO(akalin): Replace silent update with a list of enabled
406 // permissions.
407 extensions_service->AddPendingExtension(
408 id, update_url, false, true,
409 specifics.enabled(), specifics.incognito_enabled());
410 }
411 DCHECK(!extension_data->NeedsUpdate(ExtensionData::SERVER));
412 }
413
414 void ExtensionModelAssociator::NudgeExtensionUpdater() {
415 ExtensionUpdater* extension_updater = GetExtensionsService()->updater();
416 // Auto-updates should now be on always (see the construction of the
417 // ExtensionsService in ProfileImpl::InitExtensions()).
418 if (extension_updater) {
419 extension_updater->CheckNow();
420 } else {
421 LOG(DFATAL) << "Extension updater unexpectedly NULL; "
422 << "auto-updates may be turned off";
423 }
424 } 51 }
425 52
426 } // namespace browser_sync 53 } // namespace browser_sync
OLDNEW
« no previous file with comments | « chrome/browser/sync/glue/extension_model_associator.h ('k') | chrome/browser/sync/glue/extension_sync.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698