autofill_data_type_controller.cc revision 731df977c0511bca2206b5f333555b1205ff1f43
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/glue/autofill_data_type_controller.h"
6
7#include "base/logging.h"
8#include "base/metrics/histogram.h"
9#include "base/task.h"
10#include "base/time.h"
11#include "chrome/browser/browser_thread.h"
12#include "chrome/browser/profile.h"
13#include "chrome/browser/sync/glue/autofill_change_processor.h"
14#include "chrome/browser/sync/glue/autofill_model_associator.h"
15#include "chrome/browser/sync/profile_sync_service.h"
16#include "chrome/browser/sync/profile_sync_factory.h"
17#include "chrome/browser/webdata/web_data_service.h"
18#include "chrome/common/notification_service.h"
19
20namespace browser_sync {
21
22AutofillDataTypeController::AutofillDataTypeController(
23    ProfileSyncFactory* profile_sync_factory,
24    Profile* profile,
25    ProfileSyncService* sync_service)
26    : profile_sync_factory_(profile_sync_factory),
27      profile_(profile),
28      sync_service_(sync_service),
29      state_(NOT_RUNNING),
30      personal_data_(NULL),
31      abort_association_(false),
32      abort_association_complete_(false, false) {
33  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
34  DCHECK(profile_sync_factory);
35  DCHECK(profile);
36  DCHECK(sync_service);
37}
38
39AutofillDataTypeController::~AutofillDataTypeController() {
40  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
41}
42
43void AutofillDataTypeController::Start(StartCallback* start_callback) {
44  VLOG(1) << "Starting autofill data controller.";
45  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
46  DCHECK(start_callback);
47  if (state() != NOT_RUNNING) {
48    start_callback->Run(BUSY);
49    delete start_callback;
50    return;
51  }
52
53  start_callback_.reset(start_callback);
54  abort_association_ = false;
55
56  // Waiting for the personal data is subtle:  we do this as the PDM resets
57  // its cache of unique IDs once it gets loaded. If we were to proceed with
58  // association, the local ids in the mappings would wind up colliding.
59  personal_data_ = profile_->GetPersonalDataManager();
60  if (!personal_data_->IsDataLoaded()) {
61    set_state(MODEL_STARTING);
62    personal_data_->SetObserver(this);
63    return;
64  }
65
66  ContinueStartAfterPersonalDataLoaded();
67}
68
69void AutofillDataTypeController::ContinueStartAfterPersonalDataLoaded() {
70  web_data_service_ = profile_->GetWebDataService(Profile::IMPLICIT_ACCESS);
71  if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) {
72    set_state(ASSOCIATING);
73    BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
74                            NewRunnableMethod(
75                                this,
76                                &AutofillDataTypeController::StartImpl));
77  } else {
78    set_state(MODEL_STARTING);
79    notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED,
80                                NotificationService::AllSources());
81  }
82}
83
84void AutofillDataTypeController::OnPersonalDataLoaded() {
85  DCHECK_EQ(state_, MODEL_STARTING);
86  personal_data_->RemoveObserver(this);
87  ContinueStartAfterPersonalDataLoaded();
88}
89
90void AutofillDataTypeController::Observe(NotificationType type,
91                                         const NotificationSource& source,
92                                         const NotificationDetails& details) {
93  VLOG(1) << "Web database loaded observed.";
94  notification_registrar_.RemoveAll();
95  set_state(ASSOCIATING);
96  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
97                          NewRunnableMethod(
98                              this,
99                              &AutofillDataTypeController::StartImpl));
100}
101
102void AutofillDataTypeController::Stop() {
103  VLOG(1) << "Stopping autofill data type controller.";
104  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
105
106  // If Stop() is called while Start() is waiting for association to
107  // complete, we need to abort the association and wait for the DB
108  // thread to finish the StartImpl() task.
109  if (state_ == ASSOCIATING) {
110    {
111      AutoLock lock(abort_association_lock_);
112      abort_association_ = true;
113      if (model_associator_.get())
114        model_associator_->AbortAssociation();
115    }
116    // Wait for the model association to abort.
117    abort_association_complete_.Wait();
118    StartDoneImpl(ABORTED, STOPPING);
119  }
120
121  // If Stop() is called while Start() is waiting for the personal
122  // data manager or web data service to load, abort the start.
123  if (state_ == MODEL_STARTING)
124    StartDoneImpl(ABORTED, STOPPING);
125
126  // Note that we are doing most of the stop work here (deactivate and
127  // disassociate) on the UI thread even though the associate &
128  // activate were done on the DB thread.  This is because Stop() must
129  // be synchronous.
130  notification_registrar_.RemoveAll();
131  personal_data_->RemoveObserver(this);
132  if (change_processor_ != NULL && change_processor_->IsRunning())
133    sync_service_->DeactivateDataType(this, change_processor_.get());
134
135  if (model_associator_ != NULL)
136    model_associator_->DisassociateModels();
137
138  set_state(NOT_RUNNING);
139  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
140                          NewRunnableMethod(
141                              this,
142                              &AutofillDataTypeController::StopImpl));
143}
144
145void AutofillDataTypeController::StartImpl() {
146  VLOG(1) << "Autofill data type controller StartImpl called.";
147  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
148  // No additional services need to be started before we can proceed
149  // with model association.
150  {
151    AutoLock lock(abort_association_lock_);
152    if (abort_association_) {
153      abort_association_complete_.Signal();
154      return;
155    }
156    ProfileSyncFactory::SyncComponents sync_components =
157        profile_sync_factory_->CreateAutofillSyncComponents(
158            sync_service_,
159            web_data_service_->GetDatabase(),
160            profile_->GetPersonalDataManager(),
161            this);
162    model_associator_.reset(sync_components.model_associator);
163    change_processor_.reset(sync_components.change_processor);
164  }
165
166  bool sync_has_nodes = false;
167  if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
168    StartFailed(UNRECOVERABLE_ERROR);
169    return;
170  }
171
172  base::TimeTicks start_time = base::TimeTicks::Now();
173  bool merge_success = model_associator_->AssociateModels();
174  UMA_HISTOGRAM_TIMES("Sync.AutofillAssociationTime",
175                      base::TimeTicks::Now() - start_time);
176  if (!merge_success) {
177    StartFailed(ASSOCIATION_FAILED);
178    return;
179  }
180
181  sync_service_->ActivateDataType(this, change_processor_.get());
182  StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING);
183}
184
185void AutofillDataTypeController::StartDone(
186    DataTypeController::StartResult result,
187    DataTypeController::State new_state) {
188  VLOG(1) << "Autofill data type controller StartDone called.";
189  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
190
191  abort_association_complete_.Signal();
192  AutoLock lock(abort_association_lock_);
193  if (!abort_association_) {
194    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
195                            NewRunnableMethod(
196                                this,
197                                &AutofillDataTypeController::StartDoneImpl,
198                                result,
199                                new_state));
200  }
201}
202
203void AutofillDataTypeController::StartDoneImpl(
204    DataTypeController::StartResult result,
205    DataTypeController::State new_state) {
206  VLOG(1) << "Autofill data type controller StartDoneImpl called.";
207  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
208
209  set_state(new_state);
210  start_callback_->Run(result);
211  start_callback_.reset();
212
213  if (result == UNRECOVERABLE_ERROR || result == ASSOCIATION_FAILED) {
214    UMA_HISTOGRAM_ENUMERATION("Sync.AutofillStartFailures",
215                              result,
216                              MAX_START_RESULT);
217  }
218}
219
220void AutofillDataTypeController::StopImpl() {
221  VLOG(1) << "Autofill data type controller StopImpl called.";
222  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
223
224  change_processor_.reset();
225  model_associator_.reset();
226}
227
228void AutofillDataTypeController::StartFailed(StartResult result) {
229  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
230  change_processor_.reset();
231  model_associator_.reset();
232  StartDone(result, NOT_RUNNING);
233}
234
235void AutofillDataTypeController::OnUnrecoverableError(
236    const tracked_objects::Location& from_here,
237    const std::string& message) {
238  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
239  BrowserThread::PostTask(
240    BrowserThread::UI, FROM_HERE,
241    NewRunnableMethod(this,
242                      &AutofillDataTypeController::OnUnrecoverableErrorImpl,
243                      from_here, message));
244  UMA_HISTOGRAM_COUNTS("Sync.AutofillRunFailures", 1);
245}
246
247void AutofillDataTypeController::OnUnrecoverableErrorImpl(
248    const tracked_objects::Location& from_here,
249    const std::string& message) {
250  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
251  sync_service_->OnUnrecoverableError(from_here, message);
252}
253
254}  // namespace browser_sync
255