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

Side by Side Diff: chrome/browser/sync/profile_sync_service.cc

Issue 160598: Add files to browser/sync. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 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 | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2006-2008 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 #ifdef CHROME_PERSONALIZATION
6 #include "chrome/browser/sync/profile_sync_service.h"
7
8 #include <stack>
9 #include <vector>
10
11 #include "base/basictypes.h"
12 #include "base/command_line.h"
13 #include "base/file_path.h"
14 #include "base/file_util.h"
15 #include "base/gfx/png_encoder.h"
16 #include "base/stl_util-inl.h"
17 #include "base/string_util.h"
18 #include "base/time.h"
19 #include "chrome/browser/bookmarks/bookmark_utils.h"
20 #include "chrome/browser/history/history_notifications.h"
21 #include "chrome/browser/history/history_types.h"
22 #include "chrome/browser/profile.h"
23 #include "chrome/browser/sync/engine/syncapi.h"
24 #include "chrome/browser/sync/personalization.h"
25 #include "chrome/browser/sync/personalization_strings.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/common/pref_service.h"
28 #include "chrome/common/time_format.h"
29 #include "views/window/window.h"
30
31 using browser_sync::ModelAssociator;
32 using browser_sync::SyncBackendHost;
33
34 ProfileSyncService::ProfileSyncService(Profile* profile)
35 : last_auth_error_(AUTH_ERROR_NONE),
36 profile_(profile),
37 backend_initialized_(false),
38 expecting_first_run_auth_needed_event_(false),
39 is_auth_in_progress_(false),
40 ready_to_process_changes_(false),
41 unrecoverable_error_detected_(false),
42 ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)) {
43 }
44
45 ProfileSyncService::~ProfileSyncService() {
46 Shutdown(false);
47 }
48
49 void ProfileSyncService::Initialize() {
50 InitSettings();
51 RegisterPreferences();
52 if (!profile()->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted))
53 DisableForUser(); // Clean up in case of previous crash / setup abort.
54 else
55 StartUp();
56 }
57
58 void ProfileSyncService::InitSettings() {
59 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
60
61 // Override the sync server URL from the command-line, if sync server and sync
62 // port command-line arguments exist.
63 if (command_line.HasSwitch(switches::kSyncServiceURL)) {
64 std::wstring value(command_line.GetSwitchValue(switches::kSyncServiceURL));
65 if (!value.empty()) {
66 GURL custom_sync_url(WideToUTF8(value));
67 if (custom_sync_url.is_valid()) {
68 sync_service_url_ = custom_sync_url;
69 } else {
70 LOG(WARNING) << "The following sync URL specified at the command-line "
71 << "is invalid: " << value;
72 }
73 }
74 } else {
75 NOTREACHED() << "--sync-url is required when sync is enabled.";
76 }
77
78 if (command_line.HasSwitch(switches::kSyncServicePort)) {
79 std::string port_str = WideToUTF8(command_line.GetSwitchValue(
80 switches::kSyncServicePort));
81 if (!port_str.empty()) {
82 GURL::Replacements replacements;
83 replacements.SetPortStr(port_str);
84 sync_service_url_ = sync_service_url_.ReplaceComponents(replacements);
85 }
86 }
87 }
88
89 void ProfileSyncService::RegisterPreferences() {
90 PrefService* pref_service = profile_->GetPrefs();
91 if (pref_service->IsPrefRegistered(prefs::kSyncUserName))
92 return;
93 pref_service->RegisterStringPref(prefs::kSyncUserName, std::wstring());
94 pref_service->RegisterStringPref(prefs::kSyncLastSyncedTime, std::wstring());
95 pref_service->RegisterBooleanPref(prefs::kSyncHasSetupCompleted, false);
96 }
97
98 void ProfileSyncService::LoadPreferences() {
99 PrefService* pref_service = profile_->GetPrefs();
100 std::wstring last_synced_time_string =
101 pref_service->GetString(prefs::kSyncLastSyncedTime);
102 if (!last_synced_time_string.empty()) {
103 int64 last_synced_time;
104 bool success = StringToInt64(WideToUTF16(last_synced_time_string),
105 &last_synced_time);
106 if (success) {
107 last_synced_time_ = base::Time::FromInternalValue(last_synced_time);
108 } else {
109 NOTREACHED();
110 }
111 }
112 }
113
114 void ProfileSyncService::ClearPreferences() {
115 PrefService* pref_service = profile_->GetPrefs();
116 pref_service->ClearPref(prefs::kSyncUserName);
117 pref_service->ClearPref(prefs::kSyncLastSyncedTime);
118 pref_service->ClearPref(prefs::kSyncHasSetupCompleted);
119
120 pref_service->ScheduleSavePersistentPrefs();
121 }
122
123 void ProfileSyncService::InitializeBackend() {
124 backend_->Initialize(sync_service_url_);
125 }
126
127 void ProfileSyncService::StartUp() {
128 // Don't start up multiple times.
129 if (backend_.get())
130 return;
131
132 LoadPreferences();
133
134 backend_.reset(new SyncBackendHost(this, profile_->GetPath()));
135
136 // We add ourselves as an observer, and we remain one forever. Note we don't
137 // keep any pointer to the model, we just receive notifications from it.
138 BookmarkModel* model = profile_->GetBookmarkModel();
139 model->AddObserver(this);
140
141 // Create new model assocation manager.
142 model_associator_ = new ModelAssociator(this);
143
144 // TODO(timsteele): HttpBridgeFactory should take a const* to the profile's
145 // URLRequestContext, because it needs it to create HttpBridge objects, and
146 // it may need to do that before the default request context has been set
147 // up. For now, call GetRequestContext lazy-init to force creation.
148 profile_->GetRequestContext();
149 InitializeBackend();
150 }
151
152 void ProfileSyncService::Shutdown(bool sync_disabled) {
153 if (backend_.get()) {
154 backend_->Shutdown(sync_disabled);
155 backend_.reset();
156 }
157
158 BookmarkModel* model = profile_->GetBookmarkModel();
159 if (model)
160 model->RemoveObserver(this);
161
162 // Clear all assocations and throw away the assocation manager instance.
163 if (model_associator_.get()) {
164 model_associator_->ClearAll();
165 model_associator_ = NULL;
166 }
167
168 // Clear various flags.
169 is_auth_in_progress_ = false;
170 backend_initialized_ = false;
171 expecting_first_run_auth_needed_event_ = false;
172 ready_to_process_changes_ = false;
173 last_attempted_user_email_.clear();
174 }
175
176 void ProfileSyncService::EnableForUser() {
177 if (wizard_.IsVisible()) {
178 // TODO(timsteele): Focus wizard.
179 return;
180 }
181 expecting_first_run_auth_needed_event_ = true;
182
183 StartUp();
184 FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
185 }
186
187 void ProfileSyncService::DisableForUser() {
188 if (wizard_.IsVisible()) {
189 // TODO(timsteele): Focus wizard.
190 return;
191 }
192 Shutdown(true);
193 ClearPreferences();
194
195 FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
196 }
197
198 void ProfileSyncService::Loaded(BookmarkModel* model) {
199 StartProcessingChangesIfReady();
200 }
201
202 void ProfileSyncService::UpdateSyncNodeProperties(const BookmarkNode* src,
203 sync_api::WriteNode* dst) {
204 // Set the properties of the item.
205 dst->SetIsFolder(src->is_folder());
206 dst->SetTitle(WideToUTF16(src->GetTitle()).c_str());
207 // URL is passed as a C string here because this interface avoids
208 // string16. SetURL copies the data into its own memory.
209 string16 url = UTF8ToUTF16(src->GetURL().spec());
210 dst->SetURL(url.c_str());
211 SetSyncNodeFavicon(src, dst);
212 }
213
214 void ProfileSyncService::EncodeFavicon(const BookmarkNode* src,
215 std::vector<unsigned char>* dst) const {
216 const SkBitmap& favicon = profile_->GetBookmarkModel()->GetFavIcon(src);
217
218 dst->clear();
219
220 // Check for zero-dimension images. This can happen if the favicon is
221 // still being loaded.
222 if (favicon.empty())
223 return;
224
225 // Re-encode the BookmarkNode's favicon as a PNG, and pass the data to the
226 // sync subsystem.
227 if (!PNGEncoder::EncodeBGRASkBitmap(favicon, false, dst))
228 return;
229 }
230
231 void ProfileSyncService::RemoveOneSyncNode(sync_api::WriteTransaction* trans,
232 const BookmarkNode* node) {
233 sync_api::WriteNode sync_node(trans);
234 if (!model_associator_->InitSyncNodeFromBookmarkId(node->id(), &sync_node)) {
235 SetUnrecoverableError();
236 return;
237 }
238 // This node should have no children.
239 DCHECK(sync_node.GetFirstChildId() == sync_api::kInvalidId);
240 // Remove association and delete the sync node.
241 model_associator_->DisassociateIds(sync_node.GetId());
242 sync_node.Remove();
243 }
244
245 void ProfileSyncService::RemoveSyncNodeHierarchy(const BookmarkNode* topmost) {
246 sync_api::WriteTransaction trans(backend_->GetUserShareHandle());
247
248 // Later logic assumes that |topmost| has been unlinked.
249 DCHECK(!topmost->GetParent());
250
251 // A BookmarkModel deletion event means that |node| and all its children were
252 // deleted. Sync backend expects children to be deleted individually, so we do
253 // a depth-first-search here. At each step, we consider the |index|-th child
254 // of |node|. |index_stack| stores index values for the parent levels.
255 std::stack<int> index_stack;
256 index_stack.push(0); // For the final pop. It's never used.
257 const BookmarkNode* node = topmost;
258 int index = 0;
259 while (node) {
260 // The top of |index_stack| should always be |node|'s index.
261 DCHECK(!node->GetParent() || (node->GetParent()->IndexOfChild(node) ==
262 index_stack.top()));
263 if (index == node->GetChildCount()) {
264 // If we've processed all of |node|'s children, delete |node| and move
265 // on to its successor.
266 RemoveOneSyncNode(&trans, node);
267 node = node->GetParent();
268 index = index_stack.top() + 1; // (top() + 0) was what we removed.
269 index_stack.pop();
270 } else {
271 // If |node| has an unprocessed child, process it next after pushing the
272 // current state onto the stack.
273 DCHECK_LT(index, node->GetChildCount());
274 index_stack.push(index);
275 node = node->GetChild(index);
276 index = 0;
277 }
278 }
279 DCHECK(index_stack.empty()); // Nothing should be left on the stack.
280 }
281
282 bool ProfileSyncService::MergeAndSyncAcceptanceNeeded() const {
283 // If we've shown the dialog before, don't show it again.
284 if (profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted))
285 return false;
286
287 return model_associator_->BookmarkModelHasUserCreatedNodes() &&
288 model_associator_->SyncModelHasUserCreatedNodes();
289 }
290
291 bool ProfileSyncService::IsSyncEnabledByUser() const {
292 return profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted);
293 }
294
295 void ProfileSyncService::UpdateLastSyncedTime() {
296 last_synced_time_ = base::Time::Now();
297 profile_->GetPrefs()->SetString(prefs::kSyncLastSyncedTime,
298 Int64ToWString(last_synced_time_.ToInternalValue()));
299 }
300
301 void ProfileSyncService::BookmarkNodeAdded(BookmarkModel* model,
302 const BookmarkNode* parent,
303 int index) {
304 if (!ShouldPushChanges())
305 return;
306
307 DCHECK(backend_->GetUserShareHandle());
308
309 // Acquire a scoped write lock via a transaction.
310 sync_api::WriteTransaction trans(backend_->GetUserShareHandle());
311
312 CreateSyncNode(parent, index, &trans);
313 }
314
315 int64 ProfileSyncService::CreateSyncNode(const BookmarkNode* parent,
316 int index,
317 sync_api::WriteTransaction* trans) {
318 const BookmarkNode* child = parent->GetChild(index);
319 DCHECK(child);
320
321 // Create a WriteNode container to hold the new node.
322 sync_api::WriteNode sync_child(trans);
323
324 // Actually create the node with the appropriate initial position.
325 if (!PlaceSyncNode(CREATE, parent, index, trans, &sync_child)) {
326 LOG(WARNING) << "Sync node creation failed; recovery unlikely";
327 SetUnrecoverableError();
328 return sync_api::kInvalidId;
329 }
330
331 UpdateSyncNodeProperties(child, &sync_child);
332
333 // Associate the ID from the sync domain with the bookmark node, so that we
334 // can refer back to this item later.
335 model_associator_->AssociateIds(child->id(), sync_child.GetId());
336
337 return sync_child.GetId();
338 }
339
340 void ProfileSyncService::BookmarkNodeRemoved(BookmarkModel* model,
341 const BookmarkNode* parent,
342 int index,
343 const BookmarkNode* node) {
344 if (!ShouldPushChanges())
345 return;
346
347 RemoveSyncNodeHierarchy(node);
348 }
349
350 void ProfileSyncService::BookmarkNodeChanged(BookmarkModel* model,
351 const BookmarkNode* node) {
352 if (!ShouldPushChanges())
353 return;
354
355 // We shouldn't see changes to the top-level nodes.
356 DCHECK_NE(node, model->GetBookmarkBarNode());
357 DCHECK_NE(node, model->other_node());
358
359 // Acquire a scoped write lock via a transaction.
360 sync_api::WriteTransaction trans(backend_->GetUserShareHandle());
361
362 // Lookup the sync node that's associated with |node|.
363 sync_api::WriteNode sync_node(&trans);
364 if (!model_associator_->InitSyncNodeFromBookmarkId(node->id(), &sync_node)) {
365 SetUnrecoverableError();
366 return;
367 }
368
369 UpdateSyncNodeProperties(node, &sync_node);
370
371 DCHECK_EQ(sync_node.GetIsFolder(), node->is_folder());
372 DCHECK_EQ(model_associator_->GetBookmarkNodeFromSyncId(
373 sync_node.GetParentId()),
374 node->GetParent());
375 // This node's index should be one more than the predecessor's index.
376 DCHECK_EQ(node->GetParent()->IndexOfChild(node),
377 CalculateBookmarkModelInsertionIndex(node->GetParent(),
378 &sync_node));
379 }
380
381 void ProfileSyncService::BookmarkNodeMoved(BookmarkModel* model,
382 const BookmarkNode* old_parent,
383 int old_index,
384 const BookmarkNode* new_parent,
385 int new_index) {
386 if (!ShouldPushChanges())
387 return;
388
389 const BookmarkNode* child = new_parent->GetChild(new_index);
390 // We shouldn't see changes to the top-level nodes.
391 DCHECK_NE(child, model->GetBookmarkBarNode());
392 DCHECK_NE(child, model->other_node());
393
394 // Acquire a scoped write lock via a transaction.
395 sync_api::WriteTransaction trans(backend_->GetUserShareHandle());
396
397 // Lookup the sync node that's associated with |child|.
398 sync_api::WriteNode sync_node(&trans);
399 if (!model_associator_->InitSyncNodeFromBookmarkId(child->id(),
400 &sync_node)) {
401 SetUnrecoverableError();
402 return;
403 }
404
405 if (!PlaceSyncNode(MOVE, new_parent, new_index, &trans, &sync_node)) {
406 SetUnrecoverableError();
407 return;
408 }
409 }
410
411 void ProfileSyncService::BookmarkNodeFavIconLoaded(BookmarkModel* model,
412 const BookmarkNode* node) {
413 BookmarkNodeChanged(model, node);
414 }
415
416 void ProfileSyncService::BookmarkNodeChildrenReordered(
417 BookmarkModel* model, const BookmarkNode* node) {
418 if (!ShouldPushChanges())
419 return;
420
421 // Acquire a scoped write lock via a transaction.
422 sync_api::WriteTransaction trans(backend_->GetUserShareHandle());
423
424 // The given node's children got reordered. We need to reorder all the
425 // children of the corresponding sync node.
426 for (int i = 0; i < node->GetChildCount(); ++i) {
427 sync_api::WriteNode sync_child(&trans);
428 if (!model_associator_->InitSyncNodeFromBookmarkId(node->GetChild(i)->id(),
429 &sync_child)) {
430 SetUnrecoverableError();
431 return;
432 }
433 DCHECK_EQ(sync_child.GetParentId(),
434 model_associator_->GetSyncIdFromBookmarkId(node->id()));
435
436 if (!PlaceSyncNode(MOVE, node, i, &trans, &sync_child)) {
437 SetUnrecoverableError();
438 return;
439 }
440 }
441 }
442
443 bool ProfileSyncService::PlaceSyncNode(MoveOrCreate operation,
444 const BookmarkNode* parent,
445 int index,
446 sync_api::WriteTransaction* trans,
447 sync_api::WriteNode* dst) {
448 sync_api::ReadNode sync_parent(trans);
449 if (!model_associator_->InitSyncNodeFromBookmarkId(parent->id(),
450 &sync_parent)) {
451 LOG(WARNING) << "Parent lookup failed";
452 SetUnrecoverableError();
453 return false;
454 }
455
456 bool success = false;
457 if (index == 0) {
458 // Insert into first position.
459 success = (operation == CREATE) ? dst->InitByCreation(sync_parent, NULL) :
460 dst->SetPosition(sync_parent, NULL);
461 if (success) {
462 DCHECK_EQ(dst->GetParentId(), sync_parent.GetId());
463 DCHECK_EQ(dst->GetId(), sync_parent.GetFirstChildId());
464 DCHECK_EQ(dst->GetPredecessorId(), sync_api::kInvalidId);
465 }
466 } else {
467 // Find the bookmark model predecessor, and insert after it.
468 const BookmarkNode* prev = parent->GetChild(index - 1);
469 sync_api::ReadNode sync_prev(trans);
470 if (!model_associator_->InitSyncNodeFromBookmarkId(prev->id(),
471 &sync_prev)) {
472 LOG(WARNING) << "Predecessor lookup failed";
473 return false;
474 }
475 success = (operation == CREATE) ?
476 dst->InitByCreation(sync_parent, &sync_prev) :
477 dst->SetPosition(sync_parent, &sync_prev);
478 if (success) {
479 DCHECK_EQ(dst->GetParentId(), sync_parent.GetId());
480 DCHECK_EQ(dst->GetPredecessorId(), sync_prev.GetId());
481 DCHECK_EQ(dst->GetId(), sync_prev.GetSuccessorId());
482 }
483 }
484 return success;
485 }
486
487 // An invariant has been violated. Transition to an error state where we try
488 // to do as little work as possible, to avoid further corruption or crashes.
489 void ProfileSyncService::SetUnrecoverableError() {
490 unrecoverable_error_detected_ = true;
491 LOG(ERROR) << "Unrecoverable error detected -- ProfileSyncService unusable.";
492 }
493
494 // Determine the bookmark model index to which a node must be moved so that
495 // predecessor of the node (in the bookmark model) matches the predecessor of
496 // |source| (in the sync model).
497 // As a precondition, this assumes that the predecessor of |source| has been
498 // updated and is already in the correct position in the bookmark model.
499 int ProfileSyncService::CalculateBookmarkModelInsertionIndex(
500 const BookmarkNode* parent,
501 const sync_api::BaseNode* child_info) const {
502 DCHECK(parent);
503 DCHECK(child_info);
504 int64 predecessor_id = child_info->GetPredecessorId();
505 // A return ID of kInvalidId indicates no predecessor.
506 if (predecessor_id == sync_api::kInvalidId)
507 return 0;
508
509 // Otherwise, insert after the predecessor bookmark node.
510 const BookmarkNode* predecessor =
511 model_associator_->GetBookmarkNodeFromSyncId(predecessor_id);
512 DCHECK(predecessor);
513 DCHECK_EQ(predecessor->GetParent(), parent);
514 return parent->IndexOfChild(predecessor) + 1;
515 }
516
517 void ProfileSyncService::OnBackendInitialized() {
518 backend_initialized_ = true;
519
520 PrefService* pref_service = profile_->GetPrefs();
521 DCHECK(pref_service->IsPrefRegistered(prefs::kSyncUserName));
522 pref_service->SetString(prefs::kSyncUserName,
523 UTF16ToWide(backend_->GetAuthenticatedUsername()));
524 StartProcessingChangesIfReady();
525
526 // The very first time the backend initializes is effectively the first time
527 // we can say we successfully "synced". last_synced_time_ will only be null
528 // in this case, because the pref wasn't restored on StartUp.
529 if (last_synced_time_.is_null())
530 UpdateLastSyncedTime();
531 FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
532 }
533
534 void ProfileSyncService::OnSyncCycleCompleted() {
535 UpdateLastSyncedTime();
536 FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
537 }
538
539 void ProfileSyncService::OnAuthError() {
540 last_auth_error_ = backend_->GetAuthErrorState();
541 // Protect against the in-your-face dialogs that pop out of nowhere.
542 // Require the user to click somewhere to run the setup wizard in the case
543 // of a steady-state auth failure.
544 if (wizard_.IsVisible() || expecting_first_run_auth_needed_event_) {
545 wizard_.Step(AUTH_ERROR_NONE == backend_->GetAuthErrorState() ?
546 SyncSetupWizard::GAIA_SUCCESS : SyncSetupWizard::GAIA_LOGIN);
547 }
548
549 if (expecting_first_run_auth_needed_event_) {
550 last_auth_error_ = AUTH_ERROR_NONE;
551 expecting_first_run_auth_needed_event_ = false;
552 }
553
554 is_auth_in_progress_ = false;
555 // Fan the notification out to interested UI-thread components.
556 FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
557 }
558
559 void ProfileSyncService::ShowLoginDialog() {
560 if (wizard_.IsVisible())
561 return;
562 if (last_auth_error_ != AUTH_ERROR_NONE)
563 wizard_.Step(SyncSetupWizard::GAIA_LOGIN);
564 }
565
566 // ApplyModelChanges is called by the sync backend after changes have been made
567 // to the sync engine's model. Apply these changes to the browser bookmark
568 // model.
569 void ProfileSyncService::ApplyModelChanges(
570 const sync_api::BaseTransaction* trans,
571 const sync_api::SyncManager::ChangeRecord* changes,
572 int change_count) {
573 if (!ShouldPushChanges())
574 return;
575
576 // A note about ordering. Sync backend is responsible for ordering the change
577 // records in the following order:
578 //
579 // 1. Deletions, from leaves up to parents.
580 // 2. Existing items with synced parents & predecessors.
581 // 3. New items with synced parents & predecessors.
582 // 4. Items with parents & predecessors in the list.
583 // 5. Repeat #4 until all items are in the list.
584 //
585 // "Predecessor" here means the previous item within a given folder; an item
586 // in the first position is always said to have a synced predecessor.
587 // For the most part, applying these changes in the order given will yield
588 // the correct result. There is one exception, however: for items that are
589 // moved away from a folder that is being deleted, we will process the delete
590 // before the move. Since deletions in the bookmark model propagate from
591 // parent to child, we must move them to a temporary location.
592 BookmarkModel* model = profile_->GetBookmarkModel();
593
594 // We are going to make changes to the bookmarks model, but don't want to end
595 // up in a feedback loop, so remove ourselves as an observer while applying
596 // changes.
597 model->RemoveObserver(this);
598
599 // A parent to hold nodes temporarily orphaned by parent deletion. It is
600 // lazily created inside the loop.
601 const BookmarkNode* foster_parent = NULL;
602 for (int i = 0; i < change_count; ++i) {
603 const BookmarkNode* dst =
604 model_associator_->GetBookmarkNodeFromSyncId(changes[i].id);
605 // Ignore changes to the permanent top-level nodes. We only care about
606 // their children.
607 if ((dst == model->GetBookmarkBarNode()) || (dst == model->other_node()))
608 continue;
609 if (changes[i].action ==
610 sync_api::SyncManager::ChangeRecord::ACTION_DELETE) {
611 // Deletions should always be at the front of the list.
612 DCHECK(i == 0 || changes[i-1].action == changes[i].action);
613 // Children of a deleted node should not be deleted; they may be
614 // reparented by a later change record. Move them to a temporary place.
615 DCHECK(dst) << "Could not find node to be deleted";
616 const BookmarkNode* parent = dst->GetParent();
617 if (dst->GetChildCount()) {
618 if (!foster_parent) {
619 foster_parent = model->AddGroup(model->other_node(),
620 model->other_node()->GetChildCount(),
621 std::wstring());
622 }
623 for (int i = dst->GetChildCount() - 1; i >= 0; --i) {
624 model->Move(dst->GetChild(i), foster_parent,
625 foster_parent->GetChildCount());
626 }
627 }
628 DCHECK_EQ(dst->GetChildCount(), 0) << "Node being deleted has children";
629 model->Remove(parent, parent->IndexOfChild(dst));
630 dst = NULL;
631 model_associator_->DisassociateIds(changes[i].id);
632 } else {
633 DCHECK_EQ((changes[i].action ==
634 sync_api::SyncManager::ChangeRecord::ACTION_ADD), (dst == NULL))
635 << "ACTION_ADD should be seen if and only if the node is unknown.";
636
637 sync_api::ReadNode src(trans);
638 if (!src.InitByIdLookup(changes[i].id)) {
639 LOG(ERROR) << "ApplyModelChanges was passed a bad ID";
640 SetUnrecoverableError();
641 return;
642 }
643
644 CreateOrUpdateBookmarkNode(&src, model);
645 }
646 }
647 // Clean up the temporary node.
648 if (foster_parent) {
649 // There should be no nodes left under the foster parent.
650 DCHECK_EQ(foster_parent->GetChildCount(), 0);
651 model->Remove(foster_parent->GetParent(),
652 foster_parent->GetParent()->IndexOfChild(foster_parent));
653 foster_parent = NULL;
654 }
655
656 // We are now ready to hear about bookmarks changes again.
657 model->AddObserver(this);
658 }
659
660 // Create a bookmark node corresponding to |src| if one is not already
661 // associated with |src|.
662 const BookmarkNode* ProfileSyncService::CreateOrUpdateBookmarkNode(
663 sync_api::BaseNode* src,
664 BookmarkModel* model) {
665 const BookmarkNode* parent =
666 model_associator_->GetBookmarkNodeFromSyncId(src->GetParentId());
667 if (!parent) {
668 DLOG(WARNING) << "Could not find parent of node being added/updated."
669 << " Node title: " << src->GetTitle()
670 << ", parent id = " << src->GetParentId();
671 return NULL;
672 }
673 int index = CalculateBookmarkModelInsertionIndex(parent, src);
674 const BookmarkNode* dst = model_associator_->GetBookmarkNodeFromSyncId(
675 src->GetId());
676 if (!dst) {
677 dst = CreateBookmarkNode(src, parent, index);
678 model_associator_->AssociateIds(dst->id(), src->GetId());
679 } else {
680 // URL and is_folder are not expected to change.
681 // TODO(ncarter): Determine if such changes should be legal or not.
682 DCHECK_EQ(src->GetIsFolder(), dst->is_folder());
683
684 // Handle reparenting and/or repositioning.
685 model->Move(dst, parent, index);
686
687 // Handle title update and URL changes due to possible conflict resolution
688 // that can happen if both a local user change and server change occur
689 // within a sufficiently small time interval.
690 const BookmarkNode* old_dst = dst;
691 dst = bookmark_utils::ApplyEditsWithNoGroupChange(model, parent, dst,
692 UTF16ToWide(src->GetTitle()),
693 src->GetIsFolder() ? GURL() : GURL(src->GetURL()),
694 NULL); // NULL because we don't need a BookmarkEditor::Handler.
695 if (dst != old_dst) { // dst was replaced with a new node with new URL.
696 model_associator_->DisassociateIds(src->GetId());
697 model_associator_->AssociateIds(dst->id(), src->GetId());
698 }
699 SetBookmarkFavicon(src, dst);
700 }
701
702 return dst;
703 }
704
705 // Creates a bookmark node under the given parent node from the given sync
706 // node. Returns the newly created node.
707 const BookmarkNode* ProfileSyncService::CreateBookmarkNode(
708 sync_api::BaseNode* sync_node,
709 const BookmarkNode* parent,
710 int index) const {
711 DCHECK(parent);
712 DCHECK(index >= 0 && index <= parent->GetChildCount());
713 BookmarkModel* model = profile_->GetBookmarkModel();
714
715 const BookmarkNode* node;
716 if (sync_node->GetIsFolder()) {
717 node = model->AddGroup(parent, index, UTF16ToWide(sync_node->GetTitle()));
718 } else {
719 GURL url(sync_node->GetURL());
720 node = model->AddURL(parent, index,
721 UTF16ToWide(sync_node->GetTitle()), url);
722 SetBookmarkFavicon(sync_node, node);
723 }
724 return node;
725 }
726
727 // Sets the favicon of the given bookmark node from the given sync node.
728 bool ProfileSyncService::SetBookmarkFavicon(
729 sync_api::BaseNode* sync_node,
730 const BookmarkNode* bookmark_node) const {
731 size_t icon_size = 0;
732 const unsigned char* icon_bytes = sync_node->GetFaviconBytes(&icon_size);
733 if (!icon_size || !icon_bytes)
734 return false;
735
736 // Registering a favicon requires that we provide a source URL, but we
737 // don't know where these came from. Currently we just use the
738 // destination URL, which is not correct, but since the favicon URL
739 // is used as a key in the history's thumbnail DB, this gives us a value
740 // which does not collide with others.
741 GURL fake_icon_url = bookmark_node->GetURL();
742
743 std::vector<unsigned char> icon_bytes_vector(icon_bytes,
744 icon_bytes + icon_size);
745
746 HistoryService* history =
747 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
748
749 history->AddPage(bookmark_node->GetURL());
750 history->SetFavIcon(bookmark_node->GetURL(),
751 fake_icon_url,
752 icon_bytes_vector);
753
754 return true;
755 }
756
757 void ProfileSyncService::SetSyncNodeFavicon(
758 const BookmarkNode* bookmark_node,
759 sync_api::WriteNode* sync_node) const {
760 std::vector<unsigned char> favicon_bytes;
761 EncodeFavicon(bookmark_node, &favicon_bytes);
762 if (!favicon_bytes.empty())
763 sync_node->SetFaviconBytes(&favicon_bytes[0], favicon_bytes.size());
764 }
765
766 SyncBackendHost::StatusSummary ProfileSyncService::QuerySyncStatusSummary() {
767 return backend_->GetStatusSummary();
768 }
769
770 SyncBackendHost::Status ProfileSyncService::QueryDetailedSyncStatus() {
771 return backend_->GetDetailedStatus();
772 }
773
774 std::wstring ProfileSyncService::BuildSyncStatusSummaryText(
775 const sync_api::SyncManager::Status::Summary& summary) {
776 switch (summary) {
777 case sync_api::SyncManager::Status::OFFLINE:
778 return L"OFFLINE";
779 case sync_api::SyncManager::Status::OFFLINE_UNSYNCED:
780 return L"OFFLINE_UNSYNCED";
781 case sync_api::SyncManager::Status::SYNCING:
782 return L"SYNCING";
783 case sync_api::SyncManager::Status::READY:
784 return L"READY";
785 case sync_api::SyncManager::Status::PAUSED:
786 return L"PAUSED";
787 case sync_api::SyncManager::Status::CONFLICT:
788 return L"CONFLICT";
789 case sync_api::SyncManager::Status::OFFLINE_UNUSABLE:
790 return L"OFFLINE_UNUSABLE";
791 case sync_api::SyncManager::Status::INVALID: // fall through
792 default:
793 return L"UNKNOWN";
794 }
795 }
796
797 std::wstring ProfileSyncService::GetLastSyncedTimeString() const {
798 if (last_synced_time_.is_null())
799 return kLastSyncedTimeNever;
800
801 base::TimeDelta last_synced = base::Time::Now() - last_synced_time_;
802
803 if (last_synced < base::TimeDelta::FromMinutes(1))
804 return kLastSyncedTimeWithinLastMinute;
805
806 return TimeFormat::TimeElapsed(last_synced);
807 }
808
809 string16 ProfileSyncService::GetAuthenticatedUsername() const {
810 return backend_->GetAuthenticatedUsername();
811 }
812
813 void ProfileSyncService::OnUserSubmittedAuth(
814 const std::string& username, const std::string& password) {
815 last_attempted_user_email_ = username;
816 is_auth_in_progress_ = true;
817 FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
818 backend_->Authenticate(username, password);
819 }
820
821 void ProfileSyncService::OnUserAcceptedMergeAndSync() {
822 bool merge_success = model_associator_->AssociateModels();
823 wizard_.Step(SyncSetupWizard::DONE); // TODO(timsteele): error state?
824 if (!merge_success) {
825 LOG(ERROR) << "Model assocation failed.";
826 SetUnrecoverableError();
827 return;
828 }
829
830 ready_to_process_changes_ = true;
831 FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
832 }
833
834 void ProfileSyncService::OnUserCancelledDialog() {
835 if (!profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted)) {
836 // A sync dialog was aborted before authentication or merge acceptance.
837 // Rollback.
838 DisableForUser();
839 }
840 FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
841 }
842
843 void ProfileSyncService::StartProcessingChangesIfReady() {
844 BookmarkModel* model = profile_->GetBookmarkModel();
845
846 DCHECK(!ready_to_process_changes_);
847
848 // First check if the subsystems are ready. We can't proceed until they
849 // both have finished loading.
850 if (!model->IsLoaded())
851 return;
852 if (!backend_initialized_)
853 return;
854
855 // Show the sync merge warning dialog if needed.
856 if (MergeAndSyncAcceptanceNeeded()) {
857 wizard_.Step(SyncSetupWizard::MERGE_AND_SYNC);
858 return;
859 }
860
861 // We're ready to merge the models.
862 bool merge_success = model_associator_->AssociateModels();
863 wizard_.Step(SyncSetupWizard::DONE); // TODO(timsteele): error state?
864 if (!merge_success) {
865 LOG(ERROR) << "Model assocation failed.";
866 SetUnrecoverableError();
867 return;
868 }
869
870 ready_to_process_changes_ = true;
871 FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
872 }
873
874 void ProfileSyncService::AddObserver(Observer* observer) {
875 observers_.AddObserver(observer);
876 }
877
878 void ProfileSyncService::RemoveObserver(Observer* observer) {
879 observers_.RemoveObserver(observer);
880 }
881
882 bool ProfileSyncService::ShouldPushChanges() {
883 return ready_to_process_changes_ && // Wait for model load and merge.
884 !unrecoverable_error_detected_; // Halt after any terrible events.
885 }
886
887 #endif // CHROME_PERSONALIZATION
OLDNEW
« no previous file with comments | « chrome/browser/sync/profile_sync_service.h ('k') | chrome/browser/sync/profile_sync_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698