Disable flaky test ExtensionActionContextMenuTest.RunInspectPopup
[chromium-blink-merge.git] / sync / internal_api / write_node.cc
blob6ab752cdcf66f975f04ebda1fb2418c0392a989a
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.
5 #include "sync/internal_api/public/write_node.h"
7 #include "base/utf_string_conversions.h"
8 #include "base/values.h"
9 #include "sync/internal_api/public/base_transaction.h"
10 #include "sync/internal_api/public/write_transaction.h"
11 #include "sync/internal_api/syncapi_internal.h"
12 #include "sync/protocol/app_specifics.pb.h"
13 #include "sync/protocol/autofill_specifics.pb.h"
14 #include "sync/protocol/bookmark_specifics.pb.h"
15 #include "sync/protocol/extension_specifics.pb.h"
16 #include "sync/protocol/password_specifics.pb.h"
17 #include "sync/protocol/session_specifics.pb.h"
18 #include "sync/protocol/theme_specifics.pb.h"
19 #include "sync/protocol/typed_url_specifics.pb.h"
20 #include "sync/syncable/mutable_entry.h"
21 #include "sync/syncable/nigori_util.h"
22 #include "sync/syncable/syncable_util.h"
23 #include "sync/util/cryptographer.h"
25 using std::string;
26 using std::vector;
28 namespace syncer {
30 using syncable::kEncryptedString;
31 using syncable::SPECIFICS;
33 static const char kDefaultNameForNewNodes[] = " ";
35 void WriteNode::SetIsFolder(bool folder) {
36 if (entry_->Get(syncable::IS_DIR) == folder)
37 return; // Skip redundant changes.
39 entry_->Put(syncable::IS_DIR, folder);
40 MarkForSyncing();
43 void WriteNode::SetTitle(const std::wstring& title) {
44 DCHECK_NE(GetModelType(), UNSPECIFIED);
45 ModelType type = GetModelType();
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 = GetTransaction()->GetEncryptedTypes().Has(type) ||
49 entry_->Get(SPECIFICS).has_encrypted();
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 != BOOKMARKS && needs_encryption) {
57 new_legal_title = kEncryptedString;
58 } else {
59 SyncAPINameToServerName(WideToUTF8(title), &new_legal_title);
62 std::string current_legal_title;
63 if (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);
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);
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;
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() == 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.
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);
102 DVLOG(1) << "Overwriting title of type "
103 << ModelTypeToString(type)
104 << " and marking for syncing.";
105 MarkForSyncing();
108 void WriteNode::SetAppSpecifics(
109 const sync_pb::AppSpecifics& new_value) {
110 sync_pb::EntitySpecifics entity_specifics;
111 entity_specifics.mutable_app()->CopyFrom(new_value);
112 SetEntitySpecifics(entity_specifics);
115 void WriteNode::SetAutofillSpecifics(
116 const sync_pb::AutofillSpecifics& new_value) {
117 sync_pb::EntitySpecifics entity_specifics;
118 entity_specifics.mutable_autofill()->CopyFrom(new_value);
119 SetEntitySpecifics(entity_specifics);
122 void WriteNode::SetAutofillProfileSpecifics(
123 const sync_pb::AutofillProfileSpecifics& new_value) {
124 sync_pb::EntitySpecifics entity_specifics;
125 entity_specifics.mutable_autofill_profile()->
126 CopyFrom(new_value);
127 SetEntitySpecifics(entity_specifics);
130 void WriteNode::SetBookmarkSpecifics(
131 const sync_pb::BookmarkSpecifics& new_value) {
132 sync_pb::EntitySpecifics entity_specifics;
133 entity_specifics.mutable_bookmark()->CopyFrom(new_value);
134 SetEntitySpecifics(entity_specifics);
137 void WriteNode::SetNigoriSpecifics(
138 const sync_pb::NigoriSpecifics& new_value) {
139 sync_pb::EntitySpecifics entity_specifics;
140 entity_specifics.mutable_nigori()->CopyFrom(new_value);
141 SetEntitySpecifics(entity_specifics);
144 void WriteNode::SetPasswordSpecifics(
145 const sync_pb::PasswordSpecificsData& data) {
146 DCHECK_EQ(GetModelType(), PASSWORDS);
148 Cryptographer* cryptographer = GetTransaction()->GetCryptographer();
150 // We have to do the idempotency check here (vs in UpdateEntryWithEncryption)
151 // because Passwords have their encrypted data within the PasswordSpecifics,
152 // vs within the EntitySpecifics like all the other types.
153 const sync_pb::EntitySpecifics& old_specifics = GetEntry()->Get(SPECIFICS);
154 sync_pb::EntitySpecifics entity_specifics;
155 // Copy over the old specifics if they exist.
156 if (GetModelTypeFromSpecifics(old_specifics) == PASSWORDS) {
157 entity_specifics.CopyFrom(old_specifics);
158 } else {
159 AddDefaultFieldValue(PASSWORDS, &entity_specifics);
161 sync_pb::PasswordSpecifics* password_specifics =
162 entity_specifics.mutable_password();
163 // This will only update password_specifics if the underlying unencrypted blob
164 // was different from |data| or was not encrypted with the proper passphrase.
165 if (!cryptographer->Encrypt(data, password_specifics->mutable_encrypted())) {
166 NOTREACHED() << "Failed to encrypt password, possibly due to sync node "
167 << "corruption";
168 return;
170 SetEntitySpecifics(entity_specifics);
173 void WriteNode::SetThemeSpecifics(
174 const sync_pb::ThemeSpecifics& new_value) {
175 sync_pb::EntitySpecifics entity_specifics;
176 entity_specifics.mutable_theme()->CopyFrom(new_value);
177 SetEntitySpecifics(entity_specifics);
180 void WriteNode::SetSessionSpecifics(
181 const sync_pb::SessionSpecifics& new_value) {
182 sync_pb::EntitySpecifics entity_specifics;
183 entity_specifics.mutable_session()->CopyFrom(new_value);
184 SetEntitySpecifics(entity_specifics);
187 void WriteNode::SetDeviceInfoSpecifics(
188 const sync_pb::DeviceInfoSpecifics& new_value) {
189 sync_pb::EntitySpecifics entity_specifics;
190 entity_specifics.mutable_device_info()->CopyFrom(new_value);
191 SetEntitySpecifics(entity_specifics);
194 void WriteNode::SetExperimentsSpecifics(
195 const sync_pb::ExperimentsSpecifics& new_value) {
196 sync_pb::EntitySpecifics entity_specifics;
197 entity_specifics.mutable_experiments()->CopyFrom(new_value);
198 SetEntitySpecifics(entity_specifics);
201 void WriteNode::SetPriorityPreferenceSpecifics(
202 const sync_pb::PriorityPreferenceSpecifics& new_value) {
203 sync_pb::EntitySpecifics entity_specifics;
204 entity_specifics.mutable_priority_preference()->CopyFrom(new_value);
205 SetEntitySpecifics(entity_specifics);
208 void WriteNode::SetEntitySpecifics(
209 const sync_pb::EntitySpecifics& new_value) {
210 ModelType new_specifics_type =
211 GetModelTypeFromSpecifics(new_value);
212 DCHECK_NE(new_specifics_type, UNSPECIFIED);
213 DVLOG(1) << "Writing entity specifics of type "
214 << ModelTypeToString(new_specifics_type);
215 DCHECK_EQ(new_specifics_type, GetModelType());
217 // Preserve unknown fields.
218 const sync_pb::EntitySpecifics& old_specifics = entry_->Get(SPECIFICS);
219 sync_pb::EntitySpecifics new_specifics;
220 new_specifics.CopyFrom(new_value);
221 new_specifics.mutable_unknown_fields()->MergeFrom(
222 old_specifics.unknown_fields());
224 // Will update the entry if encryption was necessary.
225 if (!UpdateEntryWithEncryption(GetTransaction()->GetWrappedTrans(),
226 new_specifics,
227 entry_)) {
228 return;
230 if (entry_->Get(SPECIFICS).has_encrypted()) {
231 // EncryptIfNecessary already updated the entry for us and marked for
232 // syncing if it was needed. Now we just make a copy of the unencrypted
233 // specifics so that if this node is updated, we do not have to decrypt the
234 // old data. Note that this only modifies the node's local data, not the
235 // entry itself.
236 SetUnencryptedSpecifics(new_value);
239 DCHECK_EQ(new_specifics_type, GetModelType());
242 void WriteNode::ResetFromSpecifics() {
243 SetEntitySpecifics(GetEntitySpecifics());
246 void WriteNode::SetTypedUrlSpecifics(
247 const sync_pb::TypedUrlSpecifics& new_value) {
248 sync_pb::EntitySpecifics entity_specifics;
249 entity_specifics.mutable_typed_url()->CopyFrom(new_value);
250 SetEntitySpecifics(entity_specifics);
253 void WriteNode::SetExtensionSpecifics(
254 const sync_pb::ExtensionSpecifics& new_value) {
255 sync_pb::EntitySpecifics entity_specifics;
256 entity_specifics.mutable_extension()->CopyFrom(new_value);
257 SetEntitySpecifics(entity_specifics);
260 void WriteNode::SetExternalId(int64 id) {
261 if (GetExternalId() != id)
262 entry_->Put(syncable::LOCAL_EXTERNAL_ID, id);
265 WriteNode::WriteNode(WriteTransaction* transaction)
266 : entry_(NULL), transaction_(transaction) {
267 DCHECK(transaction);
270 WriteNode::~WriteNode() {
271 delete entry_;
274 // Find an existing node matching the ID |id|, and bind this WriteNode to it.
275 // Return true on success.
276 BaseNode::InitByLookupResult WriteNode::InitByIdLookup(int64 id) {
277 DCHECK(!entry_) << "Init called twice";
278 DCHECK_NE(id, kInvalidId);
279 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
280 syncable::GET_BY_HANDLE, id);
281 if (!entry_->good())
282 return INIT_FAILED_ENTRY_NOT_GOOD;
283 if (entry_->Get(syncable::IS_DEL))
284 return INIT_FAILED_ENTRY_IS_DEL;
285 return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
288 // Find a node by client tag, and bind this WriteNode to it.
289 // Return true if the write node was found, and was not deleted.
290 // Undeleting a deleted node is possible by ClientTag.
291 BaseNode::InitByLookupResult WriteNode::InitByClientTagLookup(
292 ModelType model_type,
293 const std::string& tag) {
294 DCHECK(!entry_) << "Init called twice";
295 if (tag.empty())
296 return INIT_FAILED_PRECONDITION;
298 const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
300 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
301 syncable::GET_BY_CLIENT_TAG, hash);
302 if (!entry_->good())
303 return INIT_FAILED_ENTRY_NOT_GOOD;
304 if (entry_->Get(syncable::IS_DEL))
305 return INIT_FAILED_ENTRY_IS_DEL;
306 return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
309 BaseNode::InitByLookupResult WriteNode::InitByTagLookup(
310 const std::string& tag) {
311 DCHECK(!entry_) << "Init called twice";
312 if (tag.empty())
313 return INIT_FAILED_PRECONDITION;
314 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
315 syncable::GET_BY_SERVER_TAG, tag);
316 if (!entry_->good())
317 return INIT_FAILED_ENTRY_NOT_GOOD;
318 if (entry_->Get(syncable::IS_DEL))
319 return INIT_FAILED_ENTRY_IS_DEL;
320 ModelType model_type = GetModelType();
321 DCHECK_EQ(model_type, NIGORI);
322 return INIT_OK;
325 // Create a new node with default properties, and bind this WriteNode to it.
326 // Return true on success.
327 bool WriteNode::InitBookmarkByCreation(const BaseNode& parent,
328 const BaseNode* predecessor) {
329 DCHECK(!entry_) << "Init called twice";
330 // |predecessor| must be a child of |parent| or NULL.
331 if (predecessor && predecessor->GetParentId() != parent.GetId()) {
332 DCHECK(false);
333 return false;
336 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID);
338 // Start out with a dummy name. We expect
339 // the caller to set a meaningful name after creation.
340 string dummy(kDefaultNameForNewNodes);
342 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
343 syncable::CREATE, BOOKMARKS,
344 parent_id, dummy);
346 if (!entry_->good())
347 return false;
349 // Entries are untitled folders by default.
350 entry_->Put(syncable::IS_DIR, true);
352 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
353 return PutPredecessor(predecessor);
356 // Create a new node with default properties and a client defined unique tag,
357 // and bind this WriteNode to it.
358 // Return true on success. If the tag exists in the database, then
359 // we will attempt to undelete the node.
360 // TODO(chron): Code datatype into hash tag.
361 // TODO(chron): Is model type ever lost?
362 WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreation(
363 ModelType model_type,
364 const BaseNode& parent,
365 const std::string& tag) {
366 // This DCHECK will only fail if init is called twice.
367 DCHECK(!entry_);
368 if (tag.empty()) {
369 LOG(WARNING) << "InitUniqueByCreation failed due to empty tag.";
370 return INIT_FAILED_EMPTY_TAG;
373 const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
375 syncable::Id parent_id = parent.GetEntry()->Get(syncable::ID);
377 // Start out with a dummy name. We expect
378 // the caller to set a meaningful name after creation.
379 string dummy(kDefaultNameForNewNodes);
381 // Check if we have this locally and need to undelete it.
382 scoped_ptr<syncable::MutableEntry> existing_entry(
383 new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
384 syncable::GET_BY_CLIENT_TAG, hash));
386 if (existing_entry->good()) {
387 if (existing_entry->Get(syncable::IS_DEL)) {
388 // Rules for undelete:
389 // BASE_VERSION: Must keep the same.
390 // ID: Essential to keep the same.
391 // META_HANDLE: Must be the same, so we can't "split" the entry.
392 // IS_DEL: Must be set to false, will cause reindexing.
393 // This one is weird because IS_DEL is true for "update only"
394 // items. It should be OK to undelete an update only.
395 // MTIME/CTIME: Seems reasonable to just leave them alone.
396 // IS_UNSYNCED: Must set this to true or face database insurrection.
397 // We do this below this block.
398 // IS_UNAPPLIED_UPDATE: Either keep it the same or also set BASE_VERSION
399 // to SERVER_VERSION. We keep it the same here.
400 // IS_DIR: We'll leave it the same.
401 // SPECIFICS: Reset it.
403 existing_entry->Put(syncable::IS_DEL, false);
405 // Client tags are immutable and must be paired with the ID.
406 // If a server update comes down with an ID and client tag combo,
407 // and it already exists, always overwrite it and store only one copy.
408 // We have to undelete entries because we can't disassociate IDs from
409 // tags and updates.
411 existing_entry->Put(syncable::NON_UNIQUE_NAME, dummy);
412 existing_entry->Put(syncable::PARENT_ID, parent_id);
413 entry_ = existing_entry.release();
414 } else {
415 return INIT_FAILED_ENTRY_ALREADY_EXISTS;
417 } else {
418 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
419 syncable::CREATE,
420 model_type, parent_id, dummy);
421 if (!entry_->good())
422 return INIT_FAILED_COULD_NOT_CREATE_ENTRY;
424 // Only set IS_DIR for new entries. Don't bitflip undeleted ones.
425 entry_->Put(syncable::UNIQUE_CLIENT_TAG, hash);
428 // We don't support directory and tag combinations.
429 entry_->Put(syncable::IS_DIR, false);
431 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
432 bool success = PutPredecessor(NULL);
433 if (!success)
434 return INIT_FAILED_SET_PREDECESSOR;
436 return INIT_SUCCESS;
439 bool WriteNode::SetPosition(const BaseNode& new_parent,
440 const BaseNode* predecessor) {
441 // |predecessor| must be a child of |new_parent| or NULL.
442 if (predecessor && predecessor->GetParentId() != new_parent.GetId()) {
443 DCHECK(false);
444 return false;
447 syncable::Id new_parent_id = new_parent.GetEntry()->Get(syncable::ID);
449 // Filter out redundant changes if both the parent and the predecessor match.
450 if (new_parent_id == entry_->Get(syncable::PARENT_ID)) {
451 const syncable::Id& old = entry_->GetPredecessorId();
452 if ((!predecessor && old.IsRoot()) ||
453 (predecessor && (old == predecessor->GetEntry()->Get(syncable::ID)))) {
454 return true;
458 // Atomically change the parent. This will fail if it would
459 // introduce a cycle in the hierarchy.
460 if (!entry_->Put(syncable::PARENT_ID, new_parent_id))
461 return false;
463 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
464 return PutPredecessor(predecessor);
467 const syncable::Entry* WriteNode::GetEntry() const {
468 return entry_;
471 const BaseTransaction* WriteNode::GetTransaction() const {
472 return transaction_;
475 syncable::MutableEntry* WriteNode::GetMutableEntryForTest() {
476 return entry_;
479 void WriteNode::Tombstone() {
480 // These lines must be in this order. The call to Put(IS_DEL) might choose to
481 // unset the IS_UNSYNCED bit if the item was not known to the server at the
482 // time of deletion. It's important that the bit not be reset in that case.
483 MarkForSyncing();
484 entry_->Put(syncable::IS_DEL, true);
487 void WriteNode::Drop() {
488 if (entry_->Get(syncable::ID).ServerKnows()) {
489 entry_->Put(syncable::IS_DEL, true);
493 bool WriteNode::PutPredecessor(const BaseNode* predecessor) {
494 syncable::Id predecessor_id = predecessor ?
495 predecessor->GetEntry()->Get(syncable::ID) : syncable::Id();
496 if (!entry_->PutPredecessor(predecessor_id))
497 return false;
498 // Mark this entry as unsynced, to wake up the syncer.
499 MarkForSyncing();
501 return true;
504 void WriteNode::MarkForSyncing() {
505 syncable::MarkForSyncing(entry_);
508 } // namespace syncer