Add DumpAccessibilityTree tests for modal dialogs.
[chromium-blink-merge.git] / sync / internal_api / write_node.cc
blob079987af3a469b13837c2af3b2ea37d1a9efec8c
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/strings/string_util.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/values.h"
10 #include "sync/internal_api/public/base_transaction.h"
11 #include "sync/internal_api/public/write_transaction.h"
12 #include "sync/internal_api/syncapi_internal.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/mutable_entry.h"
22 #include "sync/syncable/nigori_util.h"
23 #include "sync/syncable/syncable_util.h"
24 #include "sync/util/cryptographer.h"
26 using std::string;
27 using std::vector;
29 namespace syncer {
31 using syncable::kEncryptedString;
32 using syncable::SPECIFICS;
34 static const char kDefaultNameForNewNodes[] = " ";
36 void WriteNode::SetIsFolder(bool folder) {
37 if (entry_->GetIsDir() == folder)
38 return; // Skip redundant changes.
40 entry_->PutIsDir(folder);
41 MarkForSyncing();
44 void WriteNode::SetTitle(const std::wstring& title) {
45 DCHECK_NE(GetModelType(), UNSPECIFIED);
46 ModelType type = GetModelType();
47 // It's possible the nigori lost the set of encrypted types. If the current
48 // specifics are already encrypted, we want to ensure we continue encrypting.
49 bool needs_encryption = GetTransaction()->GetEncryptedTypes().Has(type) ||
50 entry_->GetSpecifics().has_encrypted();
52 // If this datatype is encrypted and is not a bookmark, we disregard the
53 // specified title in favor of kEncryptedString. For encrypted bookmarks the
54 // NON_UNIQUE_NAME will still be kEncryptedString, but we store the real title
55 // into the specifics. All strings compared are server legal strings.
56 std::string new_legal_title;
57 if (type != BOOKMARKS && needs_encryption) {
58 new_legal_title = kEncryptedString;
59 } else {
60 SyncAPINameToServerName(WideToUTF8(title), &new_legal_title);
61 TruncateUTF8ToByteSize(new_legal_title, 255, &new_legal_title);
64 std::string current_legal_title;
65 if (BOOKMARKS == type &&
66 entry_->GetSpecifics().has_encrypted()) {
67 // Encrypted bookmarks only have their title in the unencrypted specifics.
68 current_legal_title = GetBookmarkSpecifics().title();
69 } else {
70 // Non-bookmarks and legacy bookmarks (those with no title in their
71 // specifics) store their title in NON_UNIQUE_NAME. Non-legacy bookmarks
72 // store their title in specifics as well as NON_UNIQUE_NAME.
73 current_legal_title = entry_->GetNonUniqueName();
76 bool title_matches = (current_legal_title == new_legal_title);
77 bool encrypted_without_overwriting_name = (needs_encryption &&
78 entry_->GetNonUniqueName() != kEncryptedString);
80 // If the title matches and the NON_UNIQUE_NAME is properly overwritten as
81 // necessary, nothing needs to change.
82 if (title_matches && !encrypted_without_overwriting_name) {
83 DVLOG(2) << "Title matches, dropping change.";
84 return;
87 // For bookmarks, we also set the title field in the specifics.
88 // TODO(zea): refactor bookmarks to not need this functionality.
89 if (GetModelType() == BOOKMARKS) {
90 sync_pb::EntitySpecifics specifics = GetEntitySpecifics();
91 specifics.mutable_bookmark()->set_title(new_legal_title);
92 SetEntitySpecifics(specifics); // Does it's own encryption checking.
95 // For bookmarks, this has to happen after we set the title in the specifics,
96 // because the presence of a title in the NON_UNIQUE_NAME is what controls
97 // the logic deciding whether this is an empty node or a legacy bookmark.
98 // See BaseNode::GetUnencryptedSpecific(..).
99 if (needs_encryption)
100 entry_->PutNonUniqueName(kEncryptedString);
101 else
102 entry_->PutNonUniqueName(new_legal_title);
104 DVLOG(1) << "Overwriting title of type "
105 << ModelTypeToString(type)
106 << " and marking for syncing.";
107 MarkForSyncing();
110 void WriteNode::SetAppSpecifics(
111 const sync_pb::AppSpecifics& new_value) {
112 sync_pb::EntitySpecifics entity_specifics;
113 entity_specifics.mutable_app()->CopyFrom(new_value);
114 SetEntitySpecifics(entity_specifics);
117 void WriteNode::SetAutofillSpecifics(
118 const sync_pb::AutofillSpecifics& new_value) {
119 sync_pb::EntitySpecifics entity_specifics;
120 entity_specifics.mutable_autofill()->CopyFrom(new_value);
121 SetEntitySpecifics(entity_specifics);
124 void WriteNode::SetAutofillProfileSpecifics(
125 const sync_pb::AutofillProfileSpecifics& new_value) {
126 sync_pb::EntitySpecifics entity_specifics;
127 entity_specifics.mutable_autofill_profile()->
128 CopyFrom(new_value);
129 SetEntitySpecifics(entity_specifics);
132 void WriteNode::SetBookmarkSpecifics(
133 const sync_pb::BookmarkSpecifics& new_value) {
134 sync_pb::EntitySpecifics entity_specifics;
135 entity_specifics.mutable_bookmark()->CopyFrom(new_value);
136 SetEntitySpecifics(entity_specifics);
139 void WriteNode::SetNigoriSpecifics(
140 const sync_pb::NigoriSpecifics& new_value) {
141 sync_pb::EntitySpecifics entity_specifics;
142 entity_specifics.mutable_nigori()->CopyFrom(new_value);
143 SetEntitySpecifics(entity_specifics);
146 void WriteNode::SetPasswordSpecifics(
147 const sync_pb::PasswordSpecificsData& data) {
148 DCHECK_EQ(GetModelType(), PASSWORDS);
150 Cryptographer* cryptographer = GetTransaction()->GetCryptographer();
152 // We have to do the idempotency check here (vs in UpdateEntryWithEncryption)
153 // because Passwords have their encrypted data within the PasswordSpecifics,
154 // vs within the EntitySpecifics like all the other types.
155 const sync_pb::EntitySpecifics& old_specifics = GetEntry()->GetSpecifics();
156 sync_pb::EntitySpecifics entity_specifics;
157 // Copy over the old specifics if they exist.
158 if (GetModelTypeFromSpecifics(old_specifics) == PASSWORDS) {
159 entity_specifics.CopyFrom(old_specifics);
160 } else {
161 AddDefaultFieldValue(PASSWORDS, &entity_specifics);
163 sync_pb::PasswordSpecifics* password_specifics =
164 entity_specifics.mutable_password();
165 // This will only update password_specifics if the underlying unencrypted blob
166 // was different from |data| or was not encrypted with the proper passphrase.
167 if (!cryptographer->Encrypt(data, password_specifics->mutable_encrypted())) {
168 NOTREACHED() << "Failed to encrypt password, possibly due to sync node "
169 << "corruption";
170 return;
172 SetEntitySpecifics(entity_specifics);
175 void WriteNode::SetThemeSpecifics(
176 const sync_pb::ThemeSpecifics& new_value) {
177 sync_pb::EntitySpecifics entity_specifics;
178 entity_specifics.mutable_theme()->CopyFrom(new_value);
179 SetEntitySpecifics(entity_specifics);
182 void WriteNode::SetSessionSpecifics(
183 const sync_pb::SessionSpecifics& new_value) {
184 sync_pb::EntitySpecifics entity_specifics;
185 entity_specifics.mutable_session()->CopyFrom(new_value);
186 SetEntitySpecifics(entity_specifics);
189 void WriteNode::SetManagedUserSettingSpecifics(
190 const sync_pb::ManagedUserSettingSpecifics& new_value) {
191 sync_pb::EntitySpecifics entity_specifics;
192 entity_specifics.mutable_managed_user_setting()->CopyFrom(new_value);
193 SetEntitySpecifics(entity_specifics);
196 void WriteNode::SetManagedUserSpecifics(
197 const sync_pb::ManagedUserSpecifics& new_value) {
198 sync_pb::EntitySpecifics entity_specifics;
199 entity_specifics.mutable_managed_user()->CopyFrom(new_value);
200 SetEntitySpecifics(entity_specifics);
203 void WriteNode::SetDeviceInfoSpecifics(
204 const sync_pb::DeviceInfoSpecifics& new_value) {
205 sync_pb::EntitySpecifics entity_specifics;
206 entity_specifics.mutable_device_info()->CopyFrom(new_value);
207 SetEntitySpecifics(entity_specifics);
210 void WriteNode::SetExperimentsSpecifics(
211 const sync_pb::ExperimentsSpecifics& new_value) {
212 sync_pb::EntitySpecifics entity_specifics;
213 entity_specifics.mutable_experiments()->CopyFrom(new_value);
214 SetEntitySpecifics(entity_specifics);
217 void WriteNode::SetPriorityPreferenceSpecifics(
218 const sync_pb::PriorityPreferenceSpecifics& new_value) {
219 sync_pb::EntitySpecifics entity_specifics;
220 entity_specifics.mutable_priority_preference()->CopyFrom(new_value);
221 SetEntitySpecifics(entity_specifics);
224 void WriteNode::SetEntitySpecifics(
225 const sync_pb::EntitySpecifics& new_value) {
226 ModelType new_specifics_type =
227 GetModelTypeFromSpecifics(new_value);
228 CHECK(!new_value.password().has_client_only_encrypted_data());
229 DCHECK_NE(new_specifics_type, UNSPECIFIED);
230 DVLOG(1) << "Writing entity specifics of type "
231 << ModelTypeToString(new_specifics_type);
232 DCHECK_EQ(new_specifics_type, GetModelType());
234 // Preserve unknown fields.
235 const sync_pb::EntitySpecifics& old_specifics = entry_->GetSpecifics();
236 sync_pb::EntitySpecifics new_specifics;
237 new_specifics.CopyFrom(new_value);
238 new_specifics.mutable_unknown_fields()->MergeFrom(
239 old_specifics.unknown_fields());
241 // Will update the entry if encryption was necessary.
242 if (!UpdateEntryWithEncryption(GetTransaction()->GetWrappedTrans(),
243 new_specifics,
244 entry_)) {
245 return;
247 if (entry_->GetSpecifics().has_encrypted()) {
248 // EncryptIfNecessary already updated the entry for us and marked for
249 // syncing if it was needed. Now we just make a copy of the unencrypted
250 // specifics so that if this node is updated, we do not have to decrypt the
251 // old data. Note that this only modifies the node's local data, not the
252 // entry itself.
253 SetUnencryptedSpecifics(new_value);
256 DCHECK_EQ(new_specifics_type, GetModelType());
259 void WriteNode::ResetFromSpecifics() {
260 SetEntitySpecifics(GetEntitySpecifics());
263 void WriteNode::SetTypedUrlSpecifics(
264 const sync_pb::TypedUrlSpecifics& new_value) {
265 sync_pb::EntitySpecifics entity_specifics;
266 entity_specifics.mutable_typed_url()->CopyFrom(new_value);
267 SetEntitySpecifics(entity_specifics);
270 void WriteNode::SetExtensionSpecifics(
271 const sync_pb::ExtensionSpecifics& new_value) {
272 sync_pb::EntitySpecifics entity_specifics;
273 entity_specifics.mutable_extension()->CopyFrom(new_value);
274 SetEntitySpecifics(entity_specifics);
277 void WriteNode::SetExternalId(int64 id) {
278 if (GetExternalId() != id)
279 entry_->PutLocalExternalId(id);
282 WriteNode::WriteNode(WriteTransaction* transaction)
283 : entry_(NULL), transaction_(transaction) {
284 DCHECK(transaction);
287 WriteNode::~WriteNode() {
288 delete entry_;
291 // Find an existing node matching the ID |id|, and bind this WriteNode to it.
292 // Return true on success.
293 BaseNode::InitByLookupResult WriteNode::InitByIdLookup(int64 id) {
294 DCHECK(!entry_) << "Init called twice";
295 DCHECK_NE(id, kInvalidId);
296 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
297 syncable::GET_BY_HANDLE, id);
298 if (!entry_->good())
299 return INIT_FAILED_ENTRY_NOT_GOOD;
300 if (entry_->GetIsDel())
301 return INIT_FAILED_ENTRY_IS_DEL;
302 return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
305 // Find a node by client tag, and bind this WriteNode to it.
306 // Return true if the write node was found, and was not deleted.
307 // Undeleting a deleted node is possible by ClientTag.
308 BaseNode::InitByLookupResult WriteNode::InitByClientTagLookup(
309 ModelType model_type,
310 const std::string& tag) {
311 DCHECK(!entry_) << "Init called twice";
312 if (tag.empty())
313 return INIT_FAILED_PRECONDITION;
315 const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
317 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
318 syncable::GET_BY_CLIENT_TAG, hash);
319 if (!entry_->good())
320 return INIT_FAILED_ENTRY_NOT_GOOD;
321 if (entry_->GetIsDel())
322 return INIT_FAILED_ENTRY_IS_DEL;
323 return DecryptIfNecessary() ? INIT_OK : INIT_FAILED_DECRYPT_IF_NECESSARY;
326 BaseNode::InitByLookupResult WriteNode::InitByTagLookup(
327 const std::string& tag) {
328 DCHECK(!entry_) << "Init called twice";
329 if (tag.empty())
330 return INIT_FAILED_PRECONDITION;
331 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
332 syncable::GET_BY_SERVER_TAG, tag);
333 if (!entry_->good())
334 return INIT_FAILED_ENTRY_NOT_GOOD;
335 if (entry_->GetIsDel())
336 return INIT_FAILED_ENTRY_IS_DEL;
337 ModelType model_type = GetModelType();
338 DCHECK_EQ(model_type, NIGORI);
339 return INIT_OK;
342 // Create a new node with default properties, and bind this WriteNode to it.
343 // Return true on success.
344 bool WriteNode::InitBookmarkByCreation(const BaseNode& parent,
345 const BaseNode* predecessor) {
346 DCHECK(!entry_) << "Init called twice";
347 // |predecessor| must be a child of |parent| or NULL.
348 if (predecessor && predecessor->GetParentId() != parent.GetId()) {
349 DCHECK(false);
350 return false;
353 syncable::Id parent_id = parent.GetEntry()->GetId();
355 // Start out with a dummy name. We expect
356 // the caller to set a meaningful name after creation.
357 string dummy(kDefaultNameForNewNodes);
359 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
360 syncable::CREATE, BOOKMARKS,
361 parent_id, dummy);
363 if (!entry_->good())
364 return false;
366 // Entries are untitled folders by default.
367 entry_->PutIsDir(true);
369 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
370 return PutPredecessor(predecessor);
373 // Create a new node with default properties and a client defined unique tag,
374 // and bind this WriteNode to it.
375 // Return true on success. If the tag exists in the database, then
376 // we will attempt to undelete the node.
377 // TODO(chron): Code datatype into hash tag.
378 // TODO(chron): Is model type ever lost?
379 WriteNode::InitUniqueByCreationResult WriteNode::InitUniqueByCreation(
380 ModelType model_type,
381 const BaseNode& parent,
382 const std::string& tag) {
383 // This DCHECK will only fail if init is called twice.
384 DCHECK(!entry_);
385 if (tag.empty()) {
386 LOG(WARNING) << "InitUniqueByCreation failed due to empty tag.";
387 return INIT_FAILED_EMPTY_TAG;
390 const std::string hash = syncable::GenerateSyncableHash(model_type, tag);
392 syncable::Id parent_id = parent.GetEntry()->GetId();
394 // Start out with a dummy name. We expect
395 // the caller to set a meaningful name after creation.
396 string dummy(kDefaultNameForNewNodes);
398 // Check if we have this locally and need to undelete it.
399 scoped_ptr<syncable::MutableEntry> existing_entry(
400 new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
401 syncable::GET_BY_CLIENT_TAG, hash));
403 if (existing_entry->good()) {
404 if (existing_entry->GetIsDel()) {
405 // Rules for undelete:
406 // BASE_VERSION: Must keep the same.
407 // ID: Essential to keep the same.
408 // META_HANDLE: Must be the same, so we can't "split" the entry.
409 // IS_DEL: Must be set to false, will cause reindexing.
410 // This one is weird because IS_DEL is true for "update only"
411 // items. It should be OK to undelete an update only.
412 // MTIME/CTIME: Seems reasonable to just leave them alone.
413 // IS_UNSYNCED: Must set this to true or face database insurrection.
414 // We do this below this block.
415 // IS_UNAPPLIED_UPDATE: Either keep it the same or also set BASE_VERSION
416 // to SERVER_VERSION. We keep it the same here.
417 // IS_DIR: We'll leave it the same.
418 // SPECIFICS: Reset it.
420 existing_entry->PutIsDel(false);
422 // Client tags are immutable and must be paired with the ID.
423 // If a server update comes down with an ID and client tag combo,
424 // and it already exists, always overwrite it and store only one copy.
425 // We have to undelete entries because we can't disassociate IDs from
426 // tags and updates.
428 existing_entry->PutNonUniqueName(dummy);
429 existing_entry->PutParentId(parent_id);
430 entry_ = existing_entry.release();
431 } else {
432 return INIT_FAILED_ENTRY_ALREADY_EXISTS;
434 } else {
435 entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
436 syncable::CREATE,
437 model_type, parent_id, dummy);
438 if (!entry_->good())
439 return INIT_FAILED_COULD_NOT_CREATE_ENTRY;
441 // Only set IS_DIR for new entries. Don't bitflip undeleted ones.
442 entry_->PutUniqueClientTag(hash);
445 // We don't support directory and tag combinations.
446 entry_->PutIsDir(false);
448 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
449 bool success = PutPredecessor(NULL);
450 if (!success)
451 return INIT_FAILED_SET_PREDECESSOR;
453 return INIT_SUCCESS;
456 bool WriteNode::SetPosition(const BaseNode& new_parent,
457 const BaseNode* predecessor) {
458 // |predecessor| must be a child of |new_parent| or NULL.
459 if (predecessor && predecessor->GetParentId() != new_parent.GetId()) {
460 DCHECK(false);
461 return false;
464 syncable::Id new_parent_id = new_parent.GetEntry()->GetId();
466 // Filter out redundant changes if both the parent and the predecessor match.
467 if (new_parent_id == entry_->GetParentId()) {
468 const syncable::Id& old = entry_->GetPredecessorId();
469 if ((!predecessor && old.IsRoot()) ||
470 (predecessor && (old == predecessor->GetEntry()->GetId()))) {
471 return true;
475 entry_->PutParentId(new_parent_id);
477 // Now set the predecessor, which sets IS_UNSYNCED as necessary.
478 return PutPredecessor(predecessor);
481 const syncable::Entry* WriteNode::GetEntry() const {
482 return entry_;
485 const BaseTransaction* WriteNode::GetTransaction() const {
486 return transaction_;
489 syncable::MutableEntry* WriteNode::GetMutableEntryForTest() {
490 return entry_;
493 void WriteNode::Tombstone() {
494 // These lines must be in this order. The call to Put(IS_DEL) might choose to
495 // unset the IS_UNSYNCED bit if the item was not known to the server at the
496 // time of deletion. It's important that the bit not be reset in that case.
497 MarkForSyncing();
498 entry_->PutIsDel(true);
501 void WriteNode::Drop() {
502 if (entry_->GetId().ServerKnows()) {
503 entry_->PutIsDel(true);
507 bool WriteNode::PutPredecessor(const BaseNode* predecessor) {
508 syncable::Id predecessor_id = predecessor ?
509 predecessor->GetEntry()->GetId() : syncable::Id();
510 if (!entry_->PutPredecessor(predecessor_id))
511 return false;
512 // Mark this entry as unsynced, to wake up the syncer.
513 MarkForSyncing();
515 return true;
518 void WriteNode::MarkForSyncing() {
519 syncable::MarkForSyncing(entry_);
522 } // namespace syncer