| 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 : NonFrontendDataTypeController(profile_sync_factory, |
| 52 : profile_sync_factory_(profile_sync_factory), | 44 profile), |
| 53 profile_(profile), | 45 backend_(NULL) { |
| 54 sync_service_(sync_service), | |
| 55 state_(NOT_RUNNING), | |
| 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 } | 46 } |
| 64 | 47 |
| 65 TypedUrlDataTypeController::~TypedUrlDataTypeController() { | 48 TypedUrlDataTypeController::~TypedUrlDataTypeController() { |
| 66 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 67 } | 49 } |
| 68 | 50 |
| 69 void TypedUrlDataTypeController::Start(StartCallback* start_callback) { | 51 void TypedUrlDataTypeController::RunOnHistoryThread(bool start, |
| 70 VLOG(1) << "Starting typed_url data controller."; | 52 history::HistoryBackend* backend) { |
| 53 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 54 // The only variable we can access here is backend_, since it is always |
| 55 // read from the DB thread. Touching anything else could lead to memory |
| 56 // corruption. |
| 57 backend_ = backend; |
| 58 if (start) { |
| 59 StartAssociation(); |
| 60 } else { |
| 61 StopAssociation(); |
| 62 } |
| 63 backend_ = NULL; |
| 64 } |
| 65 |
| 66 bool TypedUrlDataTypeController::StartModels() { |
| 71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 72 DCHECK(start_callback); | 68 DCHECK_EQ(state(), MODEL_STARTING); |
| 73 if (state_ != NOT_RUNNING || start_callback_.get()) { | 69 HistoryService* history = profile()->GetHistoryServiceWithoutCreating(); |
| 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(); | |
| 83 if (history) { | 70 if (history) { |
| 84 set_state(ASSOCIATING); | |
| 85 history_service_ = history; | 71 history_service_ = history; |
| 86 history_service_->ScheduleDBTask(new ControlTask(this, true), this); | 72 return true; |
| 87 } else { | 73 } else { |
| 88 set_state(MODEL_STARTING); | |
| 89 notification_registrar_.Add(this, NotificationType::HISTORY_LOADED, | 74 notification_registrar_.Add(this, NotificationType::HISTORY_LOADED, |
| 90 NotificationService::AllSources()); | 75 NotificationService::AllSources()); |
| 76 return false; |
| 91 } | 77 } |
| 92 } | 78 } |
| 93 | 79 |
| 80 bool TypedUrlDataTypeController::StartAssociationAsync() { |
| 81 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 82 DCHECK_EQ(state(), ASSOCIATING); |
| 83 DCHECK(history_service_.get()); |
| 84 history_service_->ScheduleDBTask(new ControlTask(this, true), this); |
| 85 return true; |
| 86 } |
| 87 |
| 88 void TypedUrlDataTypeController::CreateSyncComponents() { |
| 89 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 90 DCHECK_EQ(state(), ASSOCIATING); |
| 91 DCHECK(backend_); |
| 92 ProfileSyncFactory::SyncComponents sync_components = |
| 93 profile_sync_factory()->CreateTypedUrlSyncComponents( |
| 94 profile_sync_service(), |
| 95 backend_, |
| 96 this); |
| 97 set_model_associator(sync_components.model_associator); |
| 98 set_change_processor(sync_components.change_processor); |
| 99 } |
| 100 |
| 94 void TypedUrlDataTypeController::Observe(NotificationType type, | 101 void TypedUrlDataTypeController::Observe(NotificationType type, |
| 95 const NotificationSource& source, | 102 const NotificationSource& source, |
| 96 const NotificationDetails& details) { | 103 const NotificationDetails& details) { |
| 97 VLOG(1) << "History loaded observed."; | 104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 105 DCHECK_EQ(state(), MODEL_STARTING); |
| 98 notification_registrar_.Remove(this, | 106 notification_registrar_.Remove(this, |
| 99 NotificationType::HISTORY_LOADED, | 107 NotificationType::HISTORY_LOADED, |
| 100 NotificationService::AllSources()); | 108 NotificationService::AllSources()); |
| 101 | 109 history_service_ = profile()->GetHistoryServiceWithoutCreating(); |
| 102 history_service_ = profile_->GetHistoryServiceWithoutCreating(); | |
| 103 DCHECK(history_service_.get()); | 110 DCHECK(history_service_.get()); |
| 104 history_service_->ScheduleDBTask(new ControlTask(this, true), this); | 111 set_state(ASSOCIATING); |
| 112 StopAssociationAsync(); |
| 105 } | 113 } |
| 106 | 114 |
| 107 // TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of | 115 void TypedUrlDataTypeController::StopModels() { |
| 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)); | 116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 117 DCHECK(state() == STOPPING || state() == NOT_RUNNING); |
| 118 notification_registrar_.RemoveAll(); |
| 119 } |
| 115 | 120 |
| 116 // If Stop() is called while Start() is waiting for association to | 121 bool TypedUrlDataTypeController::StopAssociationAsync() { |
| 117 // complete, we need to abort the association and wait for the DB | 122 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 118 // thread to finish the StartImpl() task. | 123 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()); | 124 DCHECK(history_service_.get()); |
| 143 history_service_->ScheduleDBTask(new ControlTask(this, false), this); | 125 history_service_->ScheduleDBTask(new ControlTask(this, false), this); |
| 144 datatype_stopped_.Wait(); | |
| 145 } | |
| 146 | |
| 147 bool TypedUrlDataTypeController::enabled() { | |
| 148 return true; | 126 return true; |
| 149 } | 127 } |
| 150 | 128 |
| 151 syncable::ModelType TypedUrlDataTypeController::type() const { | 129 syncable::ModelType TypedUrlDataTypeController::type() const { |
| 152 return syncable::TYPED_URLS; | 130 return syncable::TYPED_URLS; |
| 153 } | 131 } |
| 154 | 132 |
| 155 browser_sync::ModelSafeGroup TypedUrlDataTypeController::model_safe_group() | 133 browser_sync::ModelSafeGroup TypedUrlDataTypeController::model_safe_group() |
| 156 const { | 134 const { |
| 157 return browser_sync::GROUP_HISTORY; | 135 return browser_sync::GROUP_HISTORY; |
| 158 } | 136 } |
| 159 | 137 |
| 160 std::string TypedUrlDataTypeController::name() const { | 138 void TypedUrlDataTypeController::RecordUnrecoverableError( |
| 161 // For logging only. | 139 const tracked_objects::Location& from_here, |
| 162 return "typed_url"; | 140 const std::string& message) { |
| 141 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 142 UMA_HISTOGRAM_COUNTS("Sync.TypedUrlRunFailures", 1); |
| 163 } | 143 } |
| 164 | 144 |
| 165 DataTypeController::State TypedUrlDataTypeController::state() const { | 145 void TypedUrlDataTypeController::RecordAssociationTime(base::TimeDelta time) { |
| 166 return state_; | 146 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 147 UMA_HISTOGRAM_TIMES("Sync.TypedUrlAssociationTime", time); |
| 167 } | 148 } |
| 168 | 149 |
| 169 void TypedUrlDataTypeController::StartImpl(history::HistoryBackend* backend) { | 150 void TypedUrlDataTypeController::RecordStartFailure(StartResult result) { |
| 170 VLOG(1) << "TypedUrl data type controller StartImpl called."; | 151 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 171 // No additional services need to be started before we can proceed | 152 UMA_HISTOGRAM_ENUMERATION("Sync.TypedUrlStartFailures", |
| 172 // with model association. | 153 result, |
| 173 { | 154 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 } | 155 } |
| 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 | 156 } // namespace browser_sync |
| OLD | NEW |