• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/engine/build_commit_command.h"
6 
7 #include <set>
8 #include <string>
9 #include <vector>
10 
11 #include "base/string_util.h"
12 #include "chrome/browser/sync/engine/syncer_proto_util.h"
13 #include "chrome/browser/sync/engine/syncer_util.h"
14 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
15 #include "chrome/browser/sync/sessions/sync_session.h"
16 #include "chrome/browser/sync/syncable/syncable.h"
17 #include "chrome/browser/sync/syncable/syncable_changes_version.h"
18 
19 using std::set;
20 using std::string;
21 using std::vector;
22 using syncable::IS_DEL;
23 using syncable::Id;
24 using syncable::MutableEntry;
25 using syncable::SPECIFICS;
26 using syncable::UNSPECIFIED;
27 
28 namespace browser_sync {
29 
30 using sessions::SyncSession;
31 
BuildCommitCommand()32 BuildCommitCommand::BuildCommitCommand() {}
~BuildCommitCommand()33 BuildCommitCommand::~BuildCommitCommand() {}
34 
AddExtensionsActivityToMessage(SyncSession * session,CommitMessage * message)35 void BuildCommitCommand::AddExtensionsActivityToMessage(
36     SyncSession* session, CommitMessage* message) {
37   // We only send ExtensionsActivity to the server if bookmarks are being
38   // committed.
39   ExtensionsActivityMonitor* monitor = session->context()->extensions_monitor();
40   if (!session->status_controller()->HasBookmarkCommitActivity()) {
41     // Return the records to the activity monitor.
42     monitor->PutRecords(session->extensions_activity());
43     session->mutable_extensions_activity()->clear();
44     return;
45   }
46   const ExtensionsActivityMonitor::Records& records =
47       session->extensions_activity();
48   for (ExtensionsActivityMonitor::Records::const_iterator it = records.begin();
49        it != records.end(); ++it) {
50     sync_pb::ChromiumExtensionsActivity* activity_message =
51         message->add_extensions_activity();
52     activity_message->set_extension_id(it->second.extension_id);
53     activity_message->set_bookmark_writes_since_last_commit(
54         it->second.bookmark_write_count);
55   }
56 }
57 
58 namespace {
SetEntrySpecifics(MutableEntry * meta_entry,SyncEntity * sync_entry)59 void SetEntrySpecifics(MutableEntry* meta_entry, SyncEntity* sync_entry) {
60   // Add the new style extension and the folder bit.
61   sync_entry->mutable_specifics()->CopyFrom(meta_entry->Get(SPECIFICS));
62   sync_entry->set_folder(meta_entry->Get(syncable::IS_DIR));
63 
64   DCHECK(meta_entry->GetModelType() == sync_entry->GetModelType());
65 }
66 
SetOldStyleBookmarkData(MutableEntry * meta_entry,SyncEntity * sync_entry)67 void SetOldStyleBookmarkData(MutableEntry* meta_entry, SyncEntity* sync_entry) {
68   DCHECK(meta_entry->Get(SPECIFICS).HasExtension(sync_pb::bookmark));
69 
70   // Old-style inlined bookmark data.
71   sync_pb::SyncEntity_BookmarkData* bookmark =
72       sync_entry->mutable_bookmarkdata();
73 
74   if (!meta_entry->Get(syncable::IS_DIR)) {
75     const sync_pb::BookmarkSpecifics& bookmark_specifics =
76         meta_entry->Get(SPECIFICS).GetExtension(sync_pb::bookmark);
77     bookmark->set_bookmark_url(bookmark_specifics.url());
78     bookmark->set_bookmark_favicon(bookmark_specifics.favicon());
79     bookmark->set_bookmark_folder(false);
80   } else {
81     bookmark->set_bookmark_folder(true);
82   }
83 }
84 }  // namespace
85 
ExecuteImpl(SyncSession * session)86 void BuildCommitCommand::ExecuteImpl(SyncSession* session) {
87   ClientToServerMessage message;
88   message.set_share(session->context()->account_name());
89   message.set_message_contents(ClientToServerMessage::COMMIT);
90 
91   CommitMessage* commit_message = message.mutable_commit();
92   commit_message->set_cache_guid(
93       session->write_transaction()->directory()->cache_guid());
94   AddExtensionsActivityToMessage(session, commit_message);
95   SyncerProtoUtil::AddRequestBirthday(
96       session->write_transaction()->directory(), &message);
97 
98   const vector<Id>& commit_ids = session->status_controller()->commit_ids();
99   for (size_t i = 0; i < commit_ids.size(); i++) {
100     Id id = commit_ids[i];
101     SyncEntity* sync_entry =
102         static_cast<SyncEntity*>(commit_message->add_entries());
103     sync_entry->set_id(id);
104     MutableEntry meta_entry(session->write_transaction(),
105                             syncable::GET_BY_ID,
106                             id);
107     CHECK(meta_entry.good());
108     // This is the only change we make to the entry in this function.
109     meta_entry.Put(syncable::SYNCING, true);
110 
111     DCHECK(0 != session->routing_info().count(meta_entry.GetModelType()))
112         << "Committing change to datatype that's not actively enabled.";
113 
114     string name = meta_entry.Get(syncable::NON_UNIQUE_NAME);
115     CHECK(!name.empty());  // Make sure this isn't an update.
116     TruncateUTF8ToByteSize(name, 255, &name);
117     sync_entry->set_name(name);
118 
119     // Set the non_unique_name.  If we do, the server ignores
120     // the |name| value (using |non_unique_name| instead), and will return
121     // in the CommitResponse a unique name if one is generated.
122     // We send both because it may aid in logging.
123     sync_entry->set_non_unique_name(name);
124 
125     if (!meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) {
126       sync_entry->set_client_defined_unique_tag(
127           meta_entry.Get(syncable::UNIQUE_CLIENT_TAG));
128     }
129 
130     // Deleted items with server-unknown parent ids can be a problem so we set
131     // the parent to 0. (TODO(sync): Still true in protocol?).
132     Id new_parent_id;
133     if (meta_entry.Get(syncable::IS_DEL) &&
134         !meta_entry.Get(syncable::PARENT_ID).ServerKnows()) {
135       new_parent_id = session->write_transaction()->root_id();
136     } else {
137       new_parent_id = meta_entry.Get(syncable::PARENT_ID);
138     }
139     sync_entry->set_parent_id(new_parent_id);
140 
141     // If our parent has changed, send up the old one so the server
142     // can correctly deal with multiple parents.
143     // TODO(nick): With the server keeping track of the primary sync parent,
144     // it should not be necessary to provide the old_parent_id: the version
145     // number should suffice.
146     if (new_parent_id != meta_entry.Get(syncable::SERVER_PARENT_ID) &&
147         0 != meta_entry.Get(syncable::BASE_VERSION) &&
148         syncable::CHANGES_VERSION != meta_entry.Get(syncable::BASE_VERSION)) {
149       sync_entry->set_old_parent_id(meta_entry.Get(syncable::SERVER_PARENT_ID));
150     }
151 
152     int64 version = meta_entry.Get(syncable::BASE_VERSION);
153     if (syncable::CHANGES_VERSION == version || 0 == version) {
154       // Undeletions are only supported for items that have a client tag.
155       DCHECK(!id.ServerKnows() ||
156              !meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty())
157           << meta_entry;
158 
159       // Version 0 means to create or undelete an object.
160       sync_entry->set_version(0);
161     } else {
162       DCHECK(id.ServerKnows()) << meta_entry;
163       sync_entry->set_version(meta_entry.Get(syncable::BASE_VERSION));
164     }
165     sync_entry->set_ctime(ClientTimeToServerTime(
166         meta_entry.Get(syncable::CTIME)));
167     sync_entry->set_mtime(ClientTimeToServerTime(
168         meta_entry.Get(syncable::MTIME)));
169 
170     // Deletion is final on the server, let's move things and then delete them.
171     if (meta_entry.Get(IS_DEL)) {
172       sync_entry->set_deleted(true);
173     } else {
174       if (meta_entry.Get(SPECIFICS).HasExtension(sync_pb::bookmark)) {
175         // Common data in both new and old protocol.
176         const Id& prev_id = meta_entry.Get(syncable::PREV_ID);
177         string prev_id_string =
178             prev_id.IsRoot() ? string() : prev_id.GetServerId();
179         sync_entry->set_insert_after_item_id(prev_id_string);
180 
181         // TODO(ncarter): In practice we won't want to send this data twice
182         // over the wire; instead, when deployed servers are able to accept
183         // the new-style scheme, we should abandon the old way.
184         SetOldStyleBookmarkData(&meta_entry, sync_entry);
185       }
186       SetEntrySpecifics(&meta_entry, sync_entry);
187     }
188   }
189   session->status_controller()->mutable_commit_message()->CopyFrom(message);
190 }
191 
192 }  // namespace browser_sync
193