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

Side by Side Diff: chrome/browser/sync/internal_api/write_node.cc

Issue 10147003: [Sync] Move 'syncapi_core' and 'sync_unit_tests' targets to sync/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix Win update errors Created 8 years, 8 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/internal_api/write_node.h"
6
7 #include "base/utf_string_conversions.h"
8 #include "base/values.h"
9 #include "chrome/browser/sync/internal_api/syncapi_internal.h"
10 #include "chrome/browser/sync/internal_api/base_transaction.h"
11 #include "chrome/browser/sync/internal_api/write_transaction.h"
12 #include "sync/engine/nigori_util.h"
13 #include "sync/protocol/app_specifics.pb.h"
14 #include "sync/protocol/autofill_specifics.pb.h"
15 #include "sync/protocol/bookmark_specifics.pb.h"
16 #include "sync/protocol/extension_specifics.pb.h"
17 #include "sync/protocol/password_specifics.pb.h"
18 #include "sync/protocol/session_specifics.pb.h"
19 #include "sync/protocol/theme_specifics.pb.h"
20 #include "sync/protocol/typed_url_specifics.pb.h"
21 #include "sync/syncable/syncable.h"
22 #include "sync/util/cryptographer.h"
23
24 using browser_sync::Cryptographer;
25 using std::string;
26 using std::vector;
27 using syncable::kEncryptedString;
28 using syncable::SPECIFICS;
29
30 namespace sync_api {
31
32 static const char kDefaultNameForNewNodes[] = " ";
33
34 void WriteNode::SetIsFolder(bool folder) {
35 if (entry_->Get(syncable::IS_DIR) == folder)
36 return; // Skip redundant changes.
37
38 entry_->Put(syncable::IS_DIR, folder);
39 MarkForSyncing();
40 }
41
42 void WriteNode::SetTitle(const std::wstring& title) {
43 DCHECK_NE(GetModelType(), syncable::UNSPECIFIED);
44 syncable::ModelType type = GetModelType();
45 Cryptographer* cryptographer = GetTransaction()->GetCryptographer();
46 // It's possible the nigori lost the set of encrypted types. If the current
47 // specifics are already encrypted, we want to ensure we continue encrypting.
48 bool needs_encryption = cryptographer->GetEncryptedTypes().Has(type) ||
49 entry_->Get(SPECIFICS).has_encrypted();
50
51 // If this datatype is encrypted and is not a bookmark, we disregard the
52 // specified title in favor of kEncryptedString. For encrypted bookmarks the
53 // NON_UNIQUE_NAME will still be kEncryptedString, but we store the real title
54 // into the specifics. All strings compared are server legal strings.
55 std::string new_legal_title;
56 if (type != syncable::BOOKMARKS && needs_encryption) {
57 new_legal_title = kEncryptedString;
58 } else {
59 SyncAPINameToServerName(WideToUTF8(title), &new_legal_title);
60 }
61
62 std::string current_legal_title;
63 if (syncable::BOOKMARKS == type &&
64 entry_->Get(syncable::SPECIFICS).has_encrypted()) {
65 // Encrypted bookmarks only have their title in the unencrypted specifics.
66 current_legal_title = GetBookmarkSpecifics().title();
67 } else {
68 // Non-bookmarks and legacy bookmarks (those with no title in their
69 // specifics) store their title in NON_UNIQUE_NAME. Non-legacy bookmarks
70 // store their title in specifics as well as NON_UNIQUE_NAME.
71 current_legal_title = entry_->Get(syncable::NON_UNIQUE_NAME);
72 }
73
74 bool title_matches = (current_legal_title == new_legal_title);
75 bool encrypted_without_overwriting_name = (needs_encryption &&
76 entry_->Get(syncable::NON_UNIQUE_NAME) != kEncryptedString);
77
78 // If the title matches and the NON_UNIQUE_NAME is properly overwritten as
79 // necessary, nothing needs to change.
80 if (title_matches && !encrypted_without_overwriting_name) {
81 DVLOG(2) << "Title matches, dropping change.";
82 return;
83 }
84
85 // For bookmarks, we also set the title field in the specifics.
86 // TODO(zea): refactor bookmarks to not need this functionality.
87 if (GetModelType() == syncable::BOOKMARKS) {
88 sync_pb::EntitySpecifics specifics = GetEntitySpecifics();
89 specifics.mutable_bookmark()->set_title(new_legal_title);
90 SetEntitySpecifics(specifics); // Does it's own encryption checking.
91 }
92
93 // For bookmarks, this has to happen after we set the title in the specifics,
94 // because the presence of a title in the NON_UNIQUE_NAME is what controls
95 // the logic deciding whether this is an empty node or a legacy bookmark.
96 // See BaseNode::GetUnencryptedSpecific(..).
97 if (needs_encryption)
98 entry_->Put(syncable::NON_UNIQUE_NAME, kEncryptedString);
99 else
100 entry_->Put(syncable::NON_UNIQUE_NAME, new_legal_title);
101
102 DVLOG(1) << "Overwriting title of type "
103 << syncable::ModelTypeToString(type)
104 << " and marking for syncing.";
105 MarkForSyncing();
106 }
107
108 void WriteNode::SetURL(const GURL& url) {
109 sync_pb::BookmarkSpecifics new_value = GetBookmarkSpecifics();
110 new_value.set_url(url.spec());
111 SetBookmarkSpecifics(new_value);
112 }
113
114 void WriteNode::SetAppSpecifics(
115 const sync_pb::AppSpecifics& new_value) {
116 sync_pb::EntitySpecifics entity_specifics;
117 entity_specifics.mutable_app()->CopyFrom(new_value);
118 SetEntitySpecifics(entity_specifics);
119 }
120
121 void WriteNode::SetAutofillSpecifics(
122 const sync_pb::AutofillSpecifics& new_value) {
123 sync_pb::EntitySpecifics entity_specifics;
124 entity_specifics.mutable_autofill()->CopyFrom(new_value);
125 SetEntitySpecifics(entity_specifics);
126 }
127
128 void WriteNode::SetAutofillProfileSpecifics(
129 const sync_pb::AutofillProfileSpecifics& new_value) {
130 sync_pb::EntitySpecifics entity_specifics;
131 entity_specifics.mutable_autofill_profile()->
132 CopyFrom(new_value);
133 SetEntitySpecifics(entity_specifics);
134 }
135
136 void WriteNode::SetBookmarkSpecifics(
137 const sync_pb::BookmarkSpecifics& new_value) {
138 sync_pb::EntitySpecifics entity_specifics;
139 entity_specifics.mutable_bookmark()->CopyFrom(new_value);
140 SetEntitySpecifics(entity_specifics);
141 }
142
143 void WriteNode::SetNigoriSpecifics(
144 const sync_pb::NigoriSpecifics& new_value) {
145 sync_pb::EntitySpecifics entity_specifics;
146 entity_specifics.mutable_nigori()->CopyFrom(new_value);
147 SetEntitySpecifics(entity_specifics);
148 }
149
150 void WriteNode::SetPasswordSpecifics(
151 const sync_pb::PasswordSpecificsData& data) {
152 DCHECK_EQ(syncable::PASSWORDS, GetModelType());
153
154 Cryptographer* cryptographer = GetTransaction()->GetCryptographer();
155
156 // We have to do the idempotency check here (vs in UpdateEntryWithEncryption)
157 // because Passwords have their encrypted data within the PasswordSpecifics,
158 // vs within the EntitySpecifics like all the other types.
159 const sync_pb::EntitySpecifics& old_specifics = GetEntry()->Get(SPECIFICS);
160 sync_pb::EntitySpecifics entity_specifics;
161 // Copy over the old specifics if they exist.
162 if (syncable::GetModelTypeFromSpecifics(old_specifics) ==
163 syncable::PASSWORDS) {
164 entity_specifics.CopyFrom(old_specifics);
165 } else {
166 syncable::AddDefaultFieldValue(syncable::PASSWORDS,
167 &entity_specifics);
168 }
169 sync_pb::PasswordSpecifics* password_specifics =
170 entity_specifics.mutable_password();
171 // This will only update password_specifics if the underlying unencrypted blob
172 // was different from |data| or was not encrypted with the proper passphrase.
173 if (!cryptographer->Encrypt(data, password_specifics->mutable_encrypted())) {
174 NOTREACHED() << "Failed to encrypt password, possibly due to sync node "
175 << "corruption";
176 return;
177 }
178 SetEntitySpecifics(entity_specifics);
179 }
180
181 void WriteNode::SetThemeSpecifics(
182 const sync_pb::ThemeSpecifics& new_value) {
183 sync_pb::EntitySpecifics entity_specifics;
184 entity_specifics.mutable_theme()->CopyFrom(new_value);
185 SetEntitySpecifics(entity_specifics);
186 }
187
188 void WriteNode::SetSessionSpecifics(
189 const sync_pb::SessionSpecifics& new_value) {
190 sync_pb::EntitySpecifics entity_specifics;
191 entity_specifics.mutable_session()->CopyFrom(new_value);
192 SetEntitySpecifics(entity_specifics);
193 }
194
195 void WriteNode::SetEntitySpecifics(
196 const sync_pb::EntitySpecifics& new_value) {
197 syncable::ModelType new_specifics_type =
198 syncable::GetModelTypeFromSpecifics(new_value);
199 DCHECK_NE(new_specifics_type, syncable::UNSPECIFIED);
200 DVLOG(1) << "Writing entity specifics of type "
201 << syncable::ModelTypeToString(new_specifics_type);
202 // GetModelType() can be unspecified if this is the first time this
203 // node is being initialized (see PutModelType()). Otherwise, it
204 // should match |new_specifics_type|.
205 if (GetModelType() != syncable::UNSPECIFIED) {
206 DCHECK_EQ(new_specifics_type, GetModelType());
207 }
208 browser_sync::Cryptographer* cryptographer =
209 GetTransaction()->GetCryptographer();
210
211 // Preserve unknown fields.
212 const sync_pb::EntitySpecifics& old_specifics = entry_->Get(SPECIFICS);
213 sync_pb::EntitySpecifics new_specifics;
214 new_specifics.CopyFrom(new_value);
215 new_specifics.mutable_unknown_fields()->MergeFrom(
216 old_specifics.unknown_fields());
217
218 // Will update the entry if encryption was necessary.
219 if (!UpdateEntryWithEncryption(cryptographer, new_specifics, entry_)) {
220 return;
221 }
222 if (entry_->Get(SPECIFICS).has_encrypted()) {
223 // EncryptIfNecessary already updated the entry for us and marked for
224 // syncing if it was needed. Now we just make a copy of the unencrypted
225 // specifics so that if this node is updated, we do not have to decrypt the
226 // old data. Note that this only modifies the node's local data, not the
227 // entry itself.
228 SetUnencryptedSpecifics(new_value);
229 }
230
231 DCHECK_EQ(new_specifics_type, GetModelType());
232 }
233
234 void WriteNode::ResetFromSpecifics() {
235 SetEntitySpecifics(GetEntitySpecifics());
236 }
237
238 void WriteNode::SetTypedUrlSpecifics(
239 const sync_pb::TypedUrlSpecifics& new_value) {
240 sync_pb::EntitySpecifics entity_specifics;
241 entity_specifics.mutable_typed_url()->CopyFrom(new_value);
242 SetEntitySpecifics(entity_specifics);
243 }
244
245 void WriteNode::SetExtensionSpecifics(
246 const sync_pb::ExtensionSpecifics& new_value) {
247 sync_pb::EntitySpecifics entity_specifics;
248 entity_specifics.mutable_extension()->CopyFrom(new_value);
249 SetEntitySpecifics(entity_specifics);
250 }
251
252 void WriteNode::SetExternalId(int64 id) {
253 if (GetExternalId() != id)
254 entry_->Put(syncable::LOCAL_EXTERNAL_ID, id);
255 }
256
257 WriteNode::WriteNode(WriteTransaction* transaction)
258 : entry_(NULL), transaction_(transaction) {
259 DCHECK(transaction);
260 }
261
262 WriteNode::~WriteNode() {
263 delete entry_;
264 }
265
266 // Find an existing node matching the ID |id|, and bind this WriteNode to it.
267 // Return true on success.
268 BaseNode::InitByLookupResult WriteNode::InitByIdLookup(int64 id) {
269 DCHECK(!entry_) << "Init called twice";
270 DCHECK_NE(id, kInvalidId);
271 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
272 syncable::GET_BY_HANDLE, id);
273 if (!entry_->good())
274 return INIT_FAILED_ENTRY_NOT_GOOD;
275 if (entry_->Get(syncable::IS_DEL))
276 return INIT_FAILED_ENTRY_IS_DEL;
277 return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
278 }
279
280 // Find a node by client tag, and bind this WriteNode to it.
281 // Return true if the write node was found, and was not deleted.
282 // Undeleting a deleted node is possible by ClientTag.
283 BaseNode::InitByLookupResult WriteNode::InitByClientTagLookup(
284 syncable::ModelType model_type,
285 const std::string& tag) {
286 DCHECK(!entry_) << "Init called twice";
287 if (tag.empty())
288 return INIT_FAILED_PRECONDITION;
289
290 const std::string hash = GenerateSyncableHash(model_type, tag);
291
292 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
293 syncable::GET_BY_CLIENT_TAG, hash);
294 if (!entry_->good())
295 return INIT_FAILED_ENTRY_NOT_GOOD;
296 if (entry_->Get(syncable::IS_DEL))
297 return INIT_FAILED_ENTRY_IS_DEL;
298 return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
299 }
300
301 BaseNode::InitByLookupResult WriteNode::InitByTagLookup(
302 const std::string& tag) {
303 DCHECK(!entry_) << "Init called twice";
304 if (tag.empty())
305 return INIT_FAILED_PRECONDITION;
306 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
307 syncable::GET_BY_SERVER_TAG, tag);
308 if (!entry_->good())
309 return INIT_FAILED_ENTRY_NOT_GOOD;
310 if (entry_->Get(syncable::IS_DEL))
311 return INIT_FAILED_ENTRY_IS_DEL;
312 syncable::ModelType model_type = GetModelType();
313 DCHECK_EQ(syncable::NIGORI, model_type);
314 return INIT_OK;
315 }
316
317 void WriteNode::PutModelType(syncable::ModelType model_type) {
318 // Set an empty specifics of the appropriate datatype. The presence
319 // of the specific field will identify the model type.
320 DCHECK(GetModelType() == model_type ||
321 GetModelType() == syncable::UNSPECIFIED); // Immutable once set.
322
323 sync_pb::EntitySpecifics specifics;
324 syncable::AddDefaultFieldValue(model_type, &specifics);
325 SetEntitySpecifics(specifics);
326 }
327
328 // Create a new node with default properties, and bind this WriteNode to it.
329 // Return true on success.
330 bool WriteNode::InitByCreation(syncable::ModelType model_type,
331 const BaseNode& parent,
332 const BaseNode* predecessor) {
333 DCHECK(!entry_) << "Init called twice";
334 // |predecessor| must be a child of |parent| or NULL.
335 if (predecessor && predecessor->GetParentId() != parent.GetId()) {
336 DCHECK(false);
337 return false;
338 }
339
340 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID);
341
342 // Start out with a dummy name. We expect
343 // the caller to set a meaningful name after creation.
344 string dummy(kDefaultNameForNewNodes);
345
346 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
347 syncable::CREATE, parent_id, dummy);
348
349 if (!entry_->good())
350 return false;
351
352 // Entries are untitled folders by default.
353 entry_->Put(syncable::IS_DIR, true);
354
355 PutModelType(model_type);
356
357 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
358 return PutPredecessor(predecessor);
359 }
360
361 // Create a new node with default properties and a client defined unique tag,
362 // and bind this WriteNode to it.
363 // Return true on success. If the tag exists in the database, then
364 // we will attempt to undelete the node.
365 // TODO(chron): Code datatype into hash tag.
366 // TODO(chron): Is model type ever lost?
367 bool WriteNode::InitUniqueByCreation(syncable::ModelType model_type,
368 const BaseNode& parent,
369 const std::string& tag) {
370 DCHECK(!entry_) << "Init called twice";
371 if (tag.empty()) {
372 LOG(WARNING) << "InitUniqueByCreation failed due to empty tag.";
373 return false;
374 }
375
376 const std::string hash = GenerateSyncableHash(model_type, tag);
377
378 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID);
379
380 // Start out with a dummy name. We expect
381 // the caller to set a meaningful name after creation.
382 string dummy(kDefaultNameForNewNodes);
383
384 // Check if we have this locally and need to undelete it.
385 scoped_ptr<syncable::MutableEntry> existing_entry(
386 new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
387 syncable::GET_BY_CLIENT_TAG, hash));
388
389 if (existing_entry->good()) {
390 if (existing_entry->Get(syncable::IS_DEL)) {
391 // Rules for undelete:
392 // BASE_VERSION: Must keep the same.
393 // ID: Essential to keep the same.
394 // META_HANDLE: Must be the same, so we can't "split" the entry.
395 // IS_DEL: Must be set to false, will cause reindexing.
396 // This one is weird because IS_DEL is true for "update only"
397 // items. It should be OK to undelete an update only.
398 // MTIME/CTIME: Seems reasonable to just leave them alone.
399 // IS_UNSYNCED: Must set this to true or face database insurrection.
400 // We do this below this block.
401 // IS_UNAPPLIED_UPDATE: Either keep it the same or also set BASE_VERSION
402 // to SERVER_VERSION. We keep it the same here.
403 // IS_DIR: We'll leave it the same.
404 // SPECIFICS: Reset it.
405
406 existing_entry->Put(syncable::IS_DEL, false);
407
408 // Client tags are immutable and must be paired with the ID.
409 // If a server update comes down with an ID and client tag combo,
410 // and it already exists, always overwrite it and store only one copy.
411 // We have to undelete entries because we can't disassociate IDs from
412 // tags and updates.
413
414 existing_entry->Put(syncable::NON_UNIQUE_NAME, dummy);
415 existing_entry->Put(syncable::PARENT_ID, parent_id);
416 entry_ = existing_entry.release();
417 } else {
418 return false;
419 }
420 } else {
421 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
422 syncable::CREATE, parent_id, dummy);
423 if (!entry_->good()) {
424 return false;
425 }
426
427 // Only set IS_DIR for new entries. Don't bitflip undeleted ones.
428 entry_->Put(syncable::UNIQUE_CLIENT_TAG, hash);
429 }
430
431 // We don't support directory and tag combinations.
432 entry_->Put(syncable::IS_DIR, false);
433
434 // Will clear specifics data.
435 PutModelType(model_type);
436
437 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
438 return PutPredecessor(NULL);
439 }
440
441 bool WriteNode::SetPosition(const BaseNode& new_parent,
442 const BaseNode* predecessor) {
443 // |predecessor| must be a child of |new_parent| or NULL.
444 if (predecessor && predecessor->GetParentId() != new_parent.GetId()) {
445 DCHECK(false);
446 return false;
447 }
448
449 syncable::Id new_parent_id = new_parent.GetEntry()->Get(syncable::ID);
450
451 // Filter out redundant changes if both the parent and the predecessor match.
452 if (new_parent_id == entry_->Get(syncable::PARENT_ID)) {
453 const syncable::Id& old = entry_->Get(syncable::PREV_ID);
454 if ((!predecessor && old.IsRoot()) ||
455 (predecessor && (old == predecessor->GetEntry()->Get(syncable::ID)))) {
456 return true;
457 }
458 }
459
460 // Atomically change the parent. This will fail if it would
461 // introduce a cycle in the hierarchy.
462 if (!entry_->Put(syncable::PARENT_ID, new_parent_id))
463 return false;
464
465 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
466 return PutPredecessor(predecessor);
467 }
468
469 const syncable::Entry* WriteNode::GetEntry() const {
470 return entry_;
471 }
472
473 const BaseTransaction* WriteNode::GetTransaction() const {
474 return transaction_;
475 }
476
477 void WriteNode::Remove() {
478 entry_->Put(syncable::IS_DEL, true);
479 MarkForSyncing();
480 }
481
482 bool WriteNode::PutPredecessor(const BaseNode* predecessor) {
483 syncable::Id predecessor_id = predecessor ?
484 predecessor->GetEntry()->Get(syncable::ID) : syncable::Id();
485 if (!entry_->PutPredecessor(predecessor_id))
486 return false;
487 // Mark this entry as unsynced, to wake up the syncer.
488 MarkForSyncing();
489
490 return true;
491 }
492
493 void WriteNode::SetFaviconBytes(const vector<unsigned char>& bytes) {
494 sync_pb::BookmarkSpecifics new_value = GetBookmarkSpecifics();
495 new_value.set_favicon(bytes.empty() ? NULL : &bytes[0], bytes.size());
496 SetBookmarkSpecifics(new_value);
497 }
498
499 void WriteNode::MarkForSyncing() {
500 syncable::MarkForSyncing(entry_);
501 }
502
503 } // namespace sync_api
OLDNEW
« no previous file with comments | « chrome/browser/sync/internal_api/write_node.h ('k') | chrome/browser/sync/internal_api/write_transaction.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698