| Index: chrome/browser/sync/syncable/syncable.cc
|
| diff --git a/chrome/browser/sync/syncable/syncable.cc b/chrome/browser/sync/syncable/syncable.cc
|
| index 9d9517472188a6d2b649f623f6b0d913455d83a8..274eafd36f626770c1c1e2064e1d050da4e3aecc 100644
|
| --- a/chrome/browser/sync/syncable/syncable.cc
|
| +++ b/chrome/browser/sync/syncable/syncable.cc
|
| @@ -384,6 +384,33 @@ void Directory::Kernel::Release() {
|
| delete this;
|
| }
|
|
|
| +void Directory::Kernel::AddChangeListener(
|
| + DirectoryChangeListener* listener) {
|
| + base::AutoLock lock(change_listeners_lock_);
|
| + change_listeners_.AddObserver(listener);
|
| +}
|
| +
|
| +void Directory::Kernel::RemoveChangeListener(
|
| + DirectoryChangeListener* listener) {
|
| + base::AutoLock lock(change_listeners_lock_);
|
| + change_listeners_.RemoveObserver(listener);
|
| +}
|
| +
|
| +// Note: it is possible that a listener will remove itself after we
|
| +// have made a copy, but before the copy is consumed. This could
|
| +// theoretically result in accessing a garbage pointer, but can only
|
| +// occur when an about:sync window is closed in the middle of a
|
| +// notification. See crbug.com/85481.
|
| +void Directory::Kernel::CopyChangeListeners(
|
| + ObserverList<DirectoryChangeListener>* change_listeners) {
|
| + DCHECK_EQ(0U, change_listeners->size());
|
| + base::AutoLock lock(change_listeners_lock_);
|
| + ObserverListBase<DirectoryChangeListener>::Iterator it(change_listeners_);
|
| + DirectoryChangeListener* obs;
|
| + while ((obs = it.GetNext()) != NULL)
|
| + change_listeners->AddObserver(obs);
|
| +}
|
| +
|
| Directory::Kernel::~Kernel() {
|
| CHECK_EQ(0, refcount);
|
| delete channel;
|
| @@ -1160,11 +1187,11 @@ void Directory::CheckTreeInvariants(syncable::BaseTransaction* trans,
|
| }
|
|
|
| void Directory::AddChangeListener(DirectoryChangeListener* listener) {
|
| - kernel_->change_listeners_.AddObserver(listener);
|
| + kernel_->AddChangeListener(listener);
|
| }
|
|
|
| void Directory::RemoveChangeListener(DirectoryChangeListener* listener) {
|
| - kernel_->change_listeners_.RemoveObserver(listener);
|
| + kernel_->RemoveChangeListener(listener);
|
| }
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
| @@ -1238,22 +1265,25 @@ bool BaseTransaction::NotifyTransactionChangingAndEnding(
|
| << " seconds.";
|
| }
|
|
|
| + ObserverList<DirectoryChangeListener> change_listeners;
|
| + dirkernel_->CopyChangeListeners(&change_listeners);
|
| +
|
| if (NULL == originals.get() || originals->empty() ||
|
| - (dirkernel_->change_listeners_.size() == 0)) {
|
| + (change_listeners.size() == 0)) {
|
| dirkernel_->transaction_mutex.Release();
|
| return false;
|
| }
|
|
|
| if (writer_ == syncable::SYNCAPI) {
|
| FOR_EACH_OBSERVER(DirectoryChangeListener,
|
| - dirkernel_->change_listeners_,
|
| + change_listeners,
|
| HandleCalculateChangesChangeEventFromSyncApi(
|
| *originals.get(),
|
| writer_,
|
| this));
|
| } else {
|
| FOR_EACH_OBSERVER(DirectoryChangeListener,
|
| - dirkernel_->change_listeners_,
|
| + change_listeners,
|
| HandleCalculateChangesChangeEventFromSyncer(
|
| *originals.get(),
|
| writer_,
|
| @@ -1264,8 +1294,7 @@ bool BaseTransaction::NotifyTransactionChangingAndEnding(
|
| // the HandleTransactionEndingChangeEvent call to each
|
| // DirectoryChangeListener.
|
| {
|
| - ObserverList<DirectoryChangeListener>::Iterator it(
|
| - dirkernel_->change_listeners_);
|
| + ObserverList<DirectoryChangeListener>::Iterator it(change_listeners);
|
| DirectoryChangeListener* obs = NULL;
|
| while ((obs = it.GetNext()) != NULL) {
|
| *models_with_changes |= obs->HandleTransactionEndingChangeEvent(this);
|
| @@ -1283,10 +1312,12 @@ bool BaseTransaction::NotifyTransactionChangingAndEnding(
|
|
|
| void BaseTransaction::NotifyTransactionComplete(
|
| ModelTypeBitSet models_with_changes) {
|
| - FOR_EACH_OBSERVER(DirectoryChangeListener,
|
| - dirkernel_->change_listeners_,
|
| - HandleTransactionCompleteChangeEvent(
|
| - models_with_changes));
|
| + ObserverList<DirectoryChangeListener> change_listeners;
|
| + dirkernel_->CopyChangeListeners(&change_listeners);
|
| + FOR_EACH_OBSERVER(DirectoryChangeListener,
|
| + change_listeners,
|
| + HandleTransactionCompleteChangeEvent(
|
| + models_with_changes));
|
| }
|
|
|
| ReadTransaction::ReadTransaction(Directory* directory, const char* file,
|
|
|