1// Copyright (c) 2011 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/profiles/profile.h"
12#include "chrome/browser/sync/glue/autofill_change_processor.h"
13#include "chrome/browser/sync/glue/autofill_model_associator.h"
14#include "chrome/browser/sync/profile_sync_factory.h"
15#include "chrome/browser/sync/profile_sync_service.h"
16#include "chrome/browser/webdata/web_data_service.h"
17#include "content/browser/browser_thread.h"
18#include "content/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      datatype_stopped_(false, false) {
34  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
35  DCHECK(profile_sync_factory);
36  DCHECK(profile);
37  DCHECK(sync_service);
38}
39
40AutofillDataTypeController::~AutofillDataTypeController() {
41  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
42
43  // TODO(zea): remove once crbug.com/61804 is resolved.
44  CHECK_EQ(state_, NOT_RUNNING) << "AutofillDataTypeController destroyed "
45                                << "without being stopped.";
46  CHECK(!change_processor_.get()) << "AutofillDataTypeController destroyed "
47                                  << "while holding a change processor.";
48}
49
50void AutofillDataTypeController::Start(StartCallback* start_callback) {
51  VLOG(1) << "Starting autofill data controller.";
52  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
53  DCHECK(start_callback);
54  if (state() != NOT_RUNNING) {
55    start_callback->Run(BUSY, FROM_HERE);
56    delete start_callback;
57    return;
58  }
59
60  start_callback_.reset(start_callback);
61  abort_association_ = false;
62
63  // Waiting for the personal data is subtle:  we do this as the PDM resets
64  // its cache of unique IDs once it gets loaded. If we were to proceed with
65  // association, the local ids in the mappings would wind up colliding.
66  personal_data_ = profile_->GetPersonalDataManager();
67  if (!personal_data_->IsDataLoaded()) {
68    set_state(MODEL_STARTING);
69    personal_data_->SetObserver(this);
70    return;
71  }
72
73  ContinueStartAfterPersonalDataLoaded();
74}
75
76void AutofillDataTypeController::ContinueStartAfterPersonalDataLoaded() {
77  web_data_service_ = profile_->GetWebDataService(Profile::IMPLICIT_ACCESS);
78  if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) {
79    set_state(ASSOCIATING);
80    BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
81                            NewRunnableMethod(
82                                this,
83                                &AutofillDataTypeController::StartImpl));
84  } else {
85    set_state(MODEL_STARTING);
86    notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED,
87                                NotificationService::AllSources());
88  }
89}
90
91void AutofillDataTypeController::OnPersonalDataLoaded() {
92  DCHECK_EQ(state_, MODEL_STARTING);
93  personal_data_->RemoveObserver(this);
94  ContinueStartAfterPersonalDataLoaded();
95}
96
97void AutofillDataTypeController::Observe(NotificationType type,
98                                         const NotificationSource& source,
99                                         const NotificationDetails& details) {
100  VLOG(1) << "Web database loaded observed.";
101  notification_registrar_.RemoveAll();
102  set_state(ASSOCIATING);
103  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
104                          NewRunnableMethod(
105                              this,
106                              &AutofillDataTypeController::StartImpl));
107}
108
109// TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of
110// distinguishing chrome shutdown from sync shutdown, we should be able to avoid
111// this (http://crbug.com/55662). Further, all this functionality should be
112// abstracted to a higher layer, where we could ensure all datatypes are doing
113// the same thing (http://crbug.com/76232).
114void AutofillDataTypeController::Stop() {
115  VLOG(1) << "Stopping autofill data type controller.";
116  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
117
118  // If Stop() is called while Start() is waiting for association to
119  // complete, we need to abort the association and wait for the DB
120  // thread to finish the StartImpl() task.
121  if (state_ == ASSOCIATING) {
122    {
123      base::AutoLock lock(abort_association_lock_);
124      abort_association_ = true;
125      if (model_associator_.get())
126        model_associator_->AbortAssociation();
127    }
128    // Wait for the model association to abort.
129    abort_association_complete_.Wait();
130    StartDoneImpl(ABORTED, STOPPING, FROM_HERE);
131  }
132
133  // If Stop() is called while Start() is waiting for the personal
134  // data manager or web data service to load, abort the start.
135  if (state_ == MODEL_STARTING)
136    StartDoneImpl(ABORTED, STOPPING, FROM_HERE);
137
138  DCHECK(!start_callback_.get());
139
140  // Deactivate the change processor on the UI thread. We dont want to listen
141  // for any more changes or process them from server.
142  notification_registrar_.RemoveAll();
143  personal_data_->RemoveObserver(this);
144  if (change_processor_ != NULL && change_processor_->IsRunning())
145    sync_service_->DeactivateDataType(this, change_processor_.get());
146
147  set_state(NOT_RUNNING);
148  if (BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
149                          NewRunnableMethod(
150                              this,
151                              &AutofillDataTypeController::StopImpl))) {
152    // We need to ensure the data type has fully stoppped before continuing. In
153    // particular, during shutdown we may attempt to destroy the
154    // profile_sync_service before we've removed its observers (BUG 61804).
155    datatype_stopped_.Wait();
156  } else if (change_processor_.get()) {
157    // TODO(zea): remove once crbug.com/61804 is resolved.
158    LOG(FATAL) << "AutofillDataTypeController::Stop() called after DB thread"
159               << " killed.";
160  }
161  CHECK(!change_processor_.get()) << "AutofillChangeProcessor not released.";
162}
163
164bool AutofillDataTypeController::enabled() {
165  return true;
166}
167
168syncable::ModelType AutofillDataTypeController::type() const {
169  return syncable::AUTOFILL;
170}
171
172browser_sync::ModelSafeGroup AutofillDataTypeController::model_safe_group()
173    const {
174  return browser_sync::GROUP_DB;
175}
176
177std::string AutofillDataTypeController::name() const {
178  // For logging only.
179  return "autofill";
180}
181
182DataTypeController::State AutofillDataTypeController::state() const {
183  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
184  return state_;
185}
186
187ProfileSyncFactory::SyncComponents
188  AutofillDataTypeController::CreateSyncComponents(
189      ProfileSyncService* profile_sync_service,
190      WebDatabase* web_database,
191      PersonalDataManager* personal_data,
192      browser_sync::UnrecoverableErrorHandler* error_handler) {
193  return profile_sync_factory_->CreateAutofillSyncComponents(
194      profile_sync_service,
195      web_database,
196      personal_data,
197      this);
198}
199
200void AutofillDataTypeController::StartImpl() {
201  VLOG(1) << "Autofill data type controller StartImpl called.";
202  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
203  // No additional services need to be started before we can proceed
204  // with model association.
205  {
206    base::AutoLock lock(abort_association_lock_);
207    if (abort_association_) {
208      abort_association_complete_.Signal();
209      return;
210    }
211    ProfileSyncFactory::SyncComponents sync_components =
212        CreateSyncComponents(
213            sync_service_,
214            web_data_service_->GetDatabase(),
215            profile_->GetPersonalDataManager(),
216            this);
217    model_associator_.reset(sync_components.model_associator);
218    change_processor_.reset(sync_components.change_processor);
219  }
220
221  if (!model_associator_->CryptoReadyIfNecessary()) {
222    StartFailed(NEEDS_CRYPTO);
223    return;
224  }
225
226  bool sync_has_nodes = false;
227  if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
228    StartFailed(UNRECOVERABLE_ERROR);
229    return;
230  }
231
232  base::TimeTicks start_time = base::TimeTicks::Now();
233  bool merge_success = model_associator_->AssociateModels();
234  UMA_HISTOGRAM_TIMES("Sync.AutofillAssociationTime",
235                      base::TimeTicks::Now() - start_time);
236  VLOG(1) << "Autofill association time: " <<
237      (base::TimeTicks::Now() - start_time).InSeconds();
238  if (!merge_success) {
239    StartFailed(ASSOCIATION_FAILED);
240    return;
241  }
242
243  sync_service_->ActivateDataType(this, change_processor_.get());
244  StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING);
245}
246
247void AutofillDataTypeController::StartDone(
248    DataTypeController::StartResult result,
249    DataTypeController::State new_state) {
250  VLOG(1) << "Autofill data type controller StartDone called.";
251  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
252
253  abort_association_complete_.Signal();
254  base::AutoLock lock(abort_association_lock_);
255  if (!abort_association_) {
256    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
257                            NewRunnableMethod(
258                                this,
259                                &AutofillDataTypeController::StartDoneImpl,
260                                result,
261                                new_state,
262                                FROM_HERE));
263  }
264}
265
266void AutofillDataTypeController::StartDoneImpl(
267    DataTypeController::StartResult result,
268    DataTypeController::State new_state,
269    const tracked_objects::Location& location) {
270  VLOG(1) << "Autofill data type controller StartDoneImpl called.";
271  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
272
273  set_state(new_state);
274  start_callback_->Run(result, location);
275  start_callback_.reset();
276
277  if (result == UNRECOVERABLE_ERROR || result == ASSOCIATION_FAILED) {
278    UMA_HISTOGRAM_ENUMERATION("Sync.AutofillStartFailures",
279                              result,
280                              MAX_START_RESULT);
281  }
282}
283
284void AutofillDataTypeController::StopImpl() {
285  VLOG(1) << "Autofill data type controller StopImpl called.";
286  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
287
288  if (model_associator_ != NULL)
289    model_associator_->DisassociateModels();
290
291  change_processor_.reset();
292  model_associator_.reset();
293
294  datatype_stopped_.Signal();
295}
296
297void AutofillDataTypeController::StartFailed(StartResult result) {
298  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
299  change_processor_.reset();
300  model_associator_.reset();
301  StartDone(result, NOT_RUNNING);
302}
303
304void AutofillDataTypeController::OnUnrecoverableError(
305    const tracked_objects::Location& from_here,
306    const std::string& message) {
307  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
308  BrowserThread::PostTask(
309    BrowserThread::UI, FROM_HERE,
310    NewRunnableMethod(this,
311                      &AutofillDataTypeController::OnUnrecoverableErrorImpl,
312                      from_here, message));
313  UMA_HISTOGRAM_COUNTS("Sync.AutofillRunFailures", 1);
314}
315
316void AutofillDataTypeController::OnUnrecoverableErrorImpl(
317    const tracked_objects::Location& from_here,
318    const std::string& message) {
319  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
320  sync_service_->OnUnrecoverableError(from_here, message);
321}
322
323}  // namespace browser_sync
324