| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/sync/glue/typed_url_data_type_controller.h" | 5 #include "chrome/browser/sync/glue/typed_url_data_type_controller.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | |
| 8 #include "base/metrics/histogram.h" | 7 #include "base/metrics/histogram.h" |
| 9 #include "base/task.h" | 8 #include "base/task.h" |
| 10 #include "base/time.h" | |
| 11 #include "chrome/browser/history/history.h" | 9 #include "chrome/browser/history/history.h" |
| 12 #include "chrome/browser/profiles/profile.h" | 10 #include "chrome/browser/profiles/profile.h" |
| 13 #include "chrome/browser/sync/glue/typed_url_change_processor.h" | |
| 14 #include "chrome/browser/sync/glue/typed_url_model_associator.h" | |
| 15 #include "chrome/browser/sync/profile_sync_factory.h" | 11 #include "chrome/browser/sync/profile_sync_factory.h" |
| 16 #include "chrome/browser/sync/profile_sync_service.h" | 12 #include "chrome/browser/sync/profile_sync_service.h" |
| 17 #include "content/browser/browser_thread.h" | 13 #include "content/browser/browser_thread.h" |
| 18 #include "content/common/notification_service.h" | 14 #include "content/common/notification_service.h" |
| 19 | 15 |
| 20 namespace browser_sync { | 16 namespace browser_sync { |
| 21 | 17 |
| 22 class ControlTask : public HistoryDBTask { | 18 class ControlTask : public HistoryDBTask { |
| 23 public: | 19 public: |
| 24 ControlTask(TypedUrlDataTypeController* controller, bool start) | 20 ControlTask(TypedUrlDataTypeController* controller, bool start) |
| 25 : controller_(controller), start_(start) {} | 21 : controller_(controller), start_(start) {} |
| 26 | 22 |
| 27 virtual bool RunOnDBThread(history::HistoryBackend* backend, | 23 virtual bool RunOnDBThread(history::HistoryBackend* backend, |
| 28 history::HistoryDatabase* db) { | 24 history::HistoryDatabase* db) { |
| 29 if (start_) { | 25 controller_->RunOnHistoryThread(start_, backend); |
| 30 controller_->StartImpl(backend); | |
| 31 } else { | |
| 32 controller_->StopImpl(); | |
| 33 } | |
| 34 | 26 |
| 35 // Release the reference to the controller. This ensures that | 27 // Release the reference to the controller. This ensures that |
| 36 // the controller isn't held past its lifetime in unit tests. | 28 // the controller isn't held past its lifetime in unit tests. |
| 37 controller_ = NULL; | 29 controller_ = NULL; |
| 38 return true; | 30 return true; |
| 39 } | 31 } |
| 40 | 32 |
| 41 virtual void DoneRunOnMainThread() {} | 33 virtual void DoneRunOnMainThread() {} |
| 42 | 34 |
| 43 protected: | 35 protected: |
| 44 scoped_refptr<TypedUrlDataTypeController> controller_; | 36 scoped_refptr<TypedUrlDataTypeController> controller_; |
| 45 bool start_; | 37 bool start_; |
| 46 }; | 38 }; |
| 47 | 39 |
| 48 TypedUrlDataTypeController::TypedUrlDataTypeController( | 40 TypedUrlDataTypeController::TypedUrlDataTypeController( |
| 49 ProfileSyncFactory* profile_sync_factory, | 41 ProfileSyncFactory* profile_sync_factory, |
| 50 Profile* profile, | 42 Profile* profile, |
| 51 ProfileSyncService* sync_service) | 43 ProfileSyncService* sync_service) |
| 52 : profile_sync_factory_(profile_sync_factory), | 44 : NonFrontendDataTypeController(profile_sync_factory, |
| 53 profile_(profile), | 45 profile, |
| 54 sync_service_(sync_service), | 46 sync_service), |
| 55 state_(NOT_RUNNING), | 47 backend_(NULL) { |
| 56 abort_association_(false), | |
| 57 abort_association_complete_(false, false), | |
| 58 datatype_stopped_(false, false) { | |
| 59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 60 DCHECK(profile_sync_factory); | |
| 61 DCHECK(profile); | |
| 62 DCHECK(sync_service); | |
| 63 } | 48 } |
| 64 | 49 |
| 65 TypedUrlDataTypeController::~TypedUrlDataTypeController() { | 50 TypedUrlDataTypeController::~TypedUrlDataTypeController() { |
| 66 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 67 } | 51 } |
| 68 | 52 |
| 69 void TypedUrlDataTypeController::Start(StartCallback* start_callback) { | 53 void TypedUrlDataTypeController::RunOnHistoryThread(bool start, |
| 70 VLOG(1) << "Starting typed_url data controller."; | 54 history::HistoryBackend* backend) { |
| 55 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 56 // The only variable we can access here is backend_, since it is always |
| 57 // read from the DB thread. Touching anything else could lead to memory |
| 58 // corruption. |
| 59 backend_ = backend; |
| 60 if (start) { |
| 61 Associate(); |
| 62 } else { |
| 63 Destroy(); |
| 64 } |
| 65 backend_ = NULL; |
| 66 } |
| 67 |
| 68 bool TypedUrlDataTypeController::StartModels() { |
| 71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 69 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 72 DCHECK(start_callback); | 70 DCHECK_EQ(state_, MODEL_STARTING); |
| 73 if (state_ != NOT_RUNNING || start_callback_.get()) { | |
| 74 start_callback->Run(BUSY, FROM_HERE); | |
| 75 delete start_callback; | |
| 76 return; | |
| 77 } | |
| 78 | |
| 79 start_callback_.reset(start_callback); | |
| 80 abort_association_ = false; | |
| 81 | |
| 82 HistoryService* history = profile_->GetHistoryServiceWithoutCreating(); | 71 HistoryService* history = profile_->GetHistoryServiceWithoutCreating(); |
| 83 if (history) { | 72 if (history) { |
| 84 set_state(ASSOCIATING); | |
| 85 history_service_ = history; | 73 history_service_ = history; |
| 86 history_service_->ScheduleDBTask(new ControlTask(this, true), this); | 74 return true; |
| 87 } else { | 75 } else { |
| 88 set_state(MODEL_STARTING); | |
| 89 notification_registrar_.Add(this, NotificationType::HISTORY_LOADED, | 76 notification_registrar_.Add(this, NotificationType::HISTORY_LOADED, |
| 90 NotificationService::AllSources()); | 77 NotificationService::AllSources()); |
| 78 return false; |
| 91 } | 79 } |
| 92 } | 80 } |
| 93 | 81 |
| 82 bool TypedUrlDataTypeController::KickOffAssociation() { |
| 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 84 DCHECK_EQ(state_, ASSOCIATING); |
| 85 DCHECK(history_service_.get()); |
| 86 history_service_->ScheduleDBTask(new ControlTask(this, true), this); |
| 87 return true; |
| 88 } |
| 89 |
| 90 void TypedUrlDataTypeController::CreateSyncComponents() { |
| 91 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 92 DCHECK_EQ(state_, ASSOCIATING); |
| 93 DCHECK(backend_); |
| 94 ProfileSyncFactory::SyncComponents sync_components = |
| 95 profile_sync_factory_->CreateTypedUrlSyncComponents( |
| 96 sync_service_, |
| 97 backend_, |
| 98 this); |
| 99 model_associator_.reset(sync_components.model_associator); |
| 100 change_processor_.reset(sync_components.change_processor); |
| 101 } |
| 102 |
| 94 void TypedUrlDataTypeController::Observe(NotificationType type, | 103 void TypedUrlDataTypeController::Observe(NotificationType type, |
| 95 const NotificationSource& source, | 104 const NotificationSource& source, |
| 96 const NotificationDetails& details) { | 105 const NotificationDetails& details) { |
| 97 VLOG(1) << "History loaded observed."; | 106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 107 DCHECK_EQ(state_, MODEL_STARTING); |
| 98 notification_registrar_.Remove(this, | 108 notification_registrar_.Remove(this, |
| 99 NotificationType::HISTORY_LOADED, | 109 NotificationType::HISTORY_LOADED, |
| 100 NotificationService::AllSources()); | 110 NotificationService::AllSources()); |
| 101 | |
| 102 history_service_ = profile_->GetHistoryServiceWithoutCreating(); | 111 history_service_ = profile_->GetHistoryServiceWithoutCreating(); |
| 103 DCHECK(history_service_.get()); | 112 DCHECK(history_service_.get()); |
| 104 history_service_->ScheduleDBTask(new ControlTask(this, true), this); | 113 state_ = ASSOCIATING; |
| 114 KickOffAssociation(); |
| 105 } | 115 } |
| 106 | 116 |
| 107 // TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of | 117 void TypedUrlDataTypeController::CleanUpState() { |
| 108 // distinguishing chrome shutdown from sync shutdown, we should be able to avoid | |
| 109 // this (http://crbug.com/55662). Further, all this functionality should be | |
| 110 // abstracted to a higher layer, where we could ensure all datatypes are doing | |
| 111 // the same thing (http://crbug.com/76232). | |
| 112 void TypedUrlDataTypeController::Stop() { | |
| 113 VLOG(1) << "Stopping typed_url data type controller."; | |
| 114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 118 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 119 DCHECK(state_ == STOPPING || state_ == NOT_RUNNING); |
| 120 notification_registrar_.RemoveAll(); |
| 121 } |
| 115 | 122 |
| 116 // If Stop() is called while Start() is waiting for association to | 123 bool TypedUrlDataTypeController::KickOffDestroy() { |
| 117 // complete, we need to abort the association and wait for the DB | 124 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 118 // thread to finish the StartImpl() task. | 125 DCHECK_EQ(state_, STOPPING); |
| 119 if (state_ == ASSOCIATING) { | |
| 120 { | |
| 121 base::AutoLock lock(abort_association_lock_); | |
| 122 abort_association_ = true; | |
| 123 if (model_associator_.get()) | |
| 124 model_associator_->AbortAssociation(); | |
| 125 } | |
| 126 // Wait for the model association to abort. | |
| 127 abort_association_complete_.Wait(); | |
| 128 StartDoneImpl(ABORTED, STOPPING); | |
| 129 } | |
| 130 | |
| 131 // If Stop() is called while Start() is waiting for the history service to | |
| 132 // load, abort the start. | |
| 133 if (state_ == MODEL_STARTING) | |
| 134 StartDoneImpl(ABORTED, STOPPING); | |
| 135 | |
| 136 DCHECK(!start_callback_.get()); | |
| 137 | |
| 138 if (change_processor_ != NULL) | |
| 139 sync_service_->DeactivateDataType(this, change_processor_.get()); | |
| 140 | |
| 141 set_state(NOT_RUNNING); | |
| 142 DCHECK(history_service_.get()); | 126 DCHECK(history_service_.get()); |
| 143 history_service_->ScheduleDBTask(new ControlTask(this, false), this); | 127 history_service_->ScheduleDBTask(new ControlTask(this, false), this); |
| 144 datatype_stopped_.Wait(); | |
| 145 } | |
| 146 | |
| 147 bool TypedUrlDataTypeController::enabled() { | |
| 148 return true; | 128 return true; |
| 149 } | 129 } |
| 150 | 130 |
| 151 syncable::ModelType TypedUrlDataTypeController::type() const { | 131 syncable::ModelType TypedUrlDataTypeController::type() const { |
| 152 return syncable::TYPED_URLS; | 132 return syncable::TYPED_URLS; |
| 153 } | 133 } |
| 154 | 134 |
| 155 browser_sync::ModelSafeGroup TypedUrlDataTypeController::model_safe_group() | 135 browser_sync::ModelSafeGroup TypedUrlDataTypeController::model_safe_group() |
| 156 const { | 136 const { |
| 157 return browser_sync::GROUP_HISTORY; | 137 return browser_sync::GROUP_HISTORY; |
| 158 } | 138 } |
| 159 | 139 |
| 160 std::string TypedUrlDataTypeController::name() const { | 140 void TypedUrlDataTypeController::RecordUnrecoverableError( |
| 161 // For logging only. | 141 const tracked_objects::Location& from_here, |
| 162 return "typed_url"; | 142 const std::string& message) { |
| 143 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 144 UMA_HISTOGRAM_COUNTS("Sync.TypedUrlRunFailures", 1); |
| 163 } | 145 } |
| 164 | 146 |
| 165 DataTypeController::State TypedUrlDataTypeController::state() const { | 147 void TypedUrlDataTypeController::RecordAssociationTime(base::TimeDelta time) { |
| 166 return state_; | 148 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 149 UMA_HISTOGRAM_TIMES("Sync.TypedUrlAssociationTime", time); |
| 167 } | 150 } |
| 168 | 151 |
| 169 void TypedUrlDataTypeController::StartImpl(history::HistoryBackend* backend) { | 152 void TypedUrlDataTypeController::RecordStartFailure(StartResult result) { |
| 170 VLOG(1) << "TypedUrl data type controller StartImpl called."; | 153 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 171 // No additional services need to be started before we can proceed | 154 UMA_HISTOGRAM_ENUMERATION("Sync.TypedUrlStartFailures", |
| 172 // with model association. | 155 result, |
| 173 { | 156 MAX_START_RESULT); |
| 174 base::AutoLock lock(abort_association_lock_); | |
| 175 if (abort_association_) { | |
| 176 abort_association_complete_.Signal(); | |
| 177 return; | |
| 178 } | |
| 179 ProfileSyncFactory::SyncComponents sync_components = | |
| 180 profile_sync_factory_->CreateTypedUrlSyncComponents( | |
| 181 sync_service_, | |
| 182 backend, | |
| 183 this); | |
| 184 model_associator_.reset(sync_components.model_associator); | |
| 185 change_processor_.reset(sync_components.change_processor); | |
| 186 } | |
| 187 | |
| 188 if (!model_associator_->CryptoReadyIfNecessary()) { | |
| 189 StartFailed(NEEDS_CRYPTO); | |
| 190 return; | |
| 191 } | |
| 192 | |
| 193 bool sync_has_nodes = false; | |
| 194 if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) { | |
| 195 StartFailed(UNRECOVERABLE_ERROR); | |
| 196 return; | |
| 197 } | |
| 198 | |
| 199 base::TimeTicks start_time = base::TimeTicks::Now(); | |
| 200 bool merge_success = model_associator_->AssociateModels(); | |
| 201 UMA_HISTOGRAM_TIMES("Sync.TypedUrlAssociationTime", | |
| 202 base::TimeTicks::Now() - start_time); | |
| 203 if (!merge_success) { | |
| 204 StartFailed(ASSOCIATION_FAILED); | |
| 205 return; | |
| 206 } | |
| 207 | |
| 208 sync_service_->ActivateDataType(this, change_processor_.get()); | |
| 209 StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING); | |
| 210 } | 157 } |
| 211 | |
| 212 void TypedUrlDataTypeController::StartDone( | |
| 213 DataTypeController::StartResult result, | |
| 214 DataTypeController::State new_state) { | |
| 215 VLOG(1) << "TypedUrl data type controller StartDone called."; | |
| 216 | |
| 217 abort_association_complete_.Signal(); | |
| 218 base::AutoLock lock(abort_association_lock_); | |
| 219 if (!abort_association_) { | |
| 220 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 221 NewRunnableMethod( | |
| 222 this, | |
| 223 &TypedUrlDataTypeController::StartDoneImpl, | |
| 224 result, | |
| 225 new_state)); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 void TypedUrlDataTypeController::StartDoneImpl( | |
| 230 DataTypeController::StartResult result, | |
| 231 DataTypeController::State new_state) { | |
| 232 VLOG(1) << "TypedUrl data type controller StartDoneImpl called."; | |
| 233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 234 set_state(new_state); | |
| 235 start_callback_->Run(result, FROM_HERE); | |
| 236 start_callback_.reset(); | |
| 237 | |
| 238 if (result == UNRECOVERABLE_ERROR || result == ASSOCIATION_FAILED) { | |
| 239 UMA_HISTOGRAM_ENUMERATION("Sync.TypedUrlStartFailures", | |
| 240 result, | |
| 241 MAX_START_RESULT); | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 void TypedUrlDataTypeController::StopImpl() { | |
| 246 VLOG(1) << "TypedUrl data type controller StopImpl called."; | |
| 247 | |
| 248 if (model_associator_ != NULL) | |
| 249 model_associator_->DisassociateModels(); | |
| 250 | |
| 251 change_processor_.reset(); | |
| 252 model_associator_.reset(); | |
| 253 | |
| 254 datatype_stopped_.Signal(); | |
| 255 } | |
| 256 | |
| 257 void TypedUrlDataTypeController::StartFailed(StartResult result) { | |
| 258 change_processor_.reset(); | |
| 259 model_associator_.reset(); | |
| 260 StartDone(result, NOT_RUNNING); | |
| 261 } | |
| 262 | |
| 263 void TypedUrlDataTypeController::OnUnrecoverableError( | |
| 264 const tracked_objects::Location& from_here, | |
| 265 const std::string& message) { | |
| 266 BrowserThread::PostTask( | |
| 267 BrowserThread::UI, FROM_HERE, | |
| 268 NewRunnableMethod(this, | |
| 269 &TypedUrlDataTypeController::OnUnrecoverableErrorImpl, | |
| 270 from_here, message)); | |
| 271 } | |
| 272 | |
| 273 void TypedUrlDataTypeController::OnUnrecoverableErrorImpl( | |
| 274 const tracked_objects::Location& from_here, | |
| 275 const std::string& message) { | |
| 276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 277 UMA_HISTOGRAM_COUNTS("Sync.TypedUrlRunFailures", 1); | |
| 278 sync_service_->OnUnrecoverableError(from_here, message); | |
| 279 } | |
| 280 | |
| 281 } // namespace browser_sync | 158 } // namespace browser_sync |
| OLD | NEW |