diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-05-29 14:40:03 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-05-29 14:40:03 +0100 |
commit | 90dce4d38c5ff5333bea97d859d4e484e27edf0c (patch) | |
tree | 9c51c7dd97d24b15befa97a3482c51851e5383a1 /sync | |
parent | 1515035f5917d10d363b0888a3615d581ad8b83f (diff) | |
download | chromium_org-90dce4d38c5ff5333bea97d859d4e484e27edf0c.tar.gz |
Merge from Chromium at DEPS revision r202854
This commit was generated by merge_to_master.py.
Change-Id: Idca323f71ef844a9e04f454d4f070b1e398f2deb
Diffstat (limited to 'sync')
47 files changed, 501 insertions, 205 deletions
diff --git a/sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java b/sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java index aa6b3d3838..b912599eab 100644 --- a/sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java +++ b/sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java @@ -7,10 +7,6 @@ package org.chromium.sync.notifier; import android.accounts.Account; import android.content.Context; import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.util.Log; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; @@ -19,11 +15,8 @@ import com.google.common.collect.Lists; import org.chromium.base.ActivityStatus; import org.chromium.sync.internal_api.pub.base.ModelType; -import java.util.HashSet; import java.util.Set; -import javax.annotation.Nullable; - /** * Controller used to send start, stop, and registration-change commands to the invalidation * client library used by Sync. @@ -93,18 +86,6 @@ public class InvalidationController implements ActivityStatus.StateListener { } } - /** - * Name of the manifest application metadata property specifying the name of the class - * implementing the invalidation client. - */ - private static final String IMPLEMENTING_CLASS_MANIFEST_PROPERTY = - "org.chromium.sync.notifier.IMPLEMENTING_CLASS_NAME"; - - /** - * Logging tag. - */ - private static final String TAG = InvalidationController.class.getSimpleName(); - private static final Object LOCK = new Object(); private static InvalidationController sInstance; @@ -124,31 +105,13 @@ public class InvalidationController implements ActivityStatus.StateListener { typesToRegister.remove(ModelType.PROXY_TABS); Intent registerIntent = IntentProtocol.createRegisterIntent(account, allTypes, typesToRegister); - setDestinationClassName(registerIntent); + registerIntent.setClass(mContext, InvalidationService.class); mContext.startService(registerIntent); } /** * Reads all stored preferences and calls * {@link #setRegisteredTypes(android.accounts.Account, boolean, java.util.Set)} with the stored - * values. It can be used on startup of Chrome to ensure we always have a consistent set of - * registrations. - */ - @Deprecated - public void refreshRegisteredTypes() { - InvalidationPreferences invalidationPreferences = new InvalidationPreferences(mContext); - Set<String> savedSyncedTypes = invalidationPreferences.getSavedSyncedTypes(); - Account account = invalidationPreferences.getSavedSyncedAccount(); - boolean allTypes = savedSyncedTypes != null && - savedSyncedTypes.contains(ModelType.ALL_TYPES_TYPE); - Set<ModelType> modelTypes = savedSyncedTypes == null ? - new HashSet<ModelType>() : ModelType.syncTypesToModelTypes(savedSyncedTypes); - setRegisteredTypes(account, allTypes, modelTypes); - } - - /** - * Reads all stored preferences and calls - * {@link #setRegisteredTypes(android.accounts.Account, boolean, java.util.Set)} with the stored * values, refreshing the set of types with {@code types}. It can be used on startup of Chrome * to ensure we always have a set of registrations consistent with the native code. * @param types Set of types for which to register. @@ -166,7 +129,7 @@ public class InvalidationController implements ActivityStatus.StateListener { * Starts the invalidation client. */ public void start() { - Intent intent = setDestinationClassName(new Intent()); + Intent intent = new Intent(mContext, InvalidationService.class); mContext.startService(intent); } @@ -174,7 +137,7 @@ public class InvalidationController implements ActivityStatus.StateListener { * Stops the invalidation client. */ public void stop() { - Intent intent = setDestinationClassName(new Intent()); + Intent intent = new Intent(mContext, InvalidationService.class); intent.putExtra(IntentProtocol.EXTRA_STOP, true); mContext.startService(intent); } @@ -201,19 +164,6 @@ public class InvalidationController implements ActivityStatus.StateListener { } /** - * Returns the singleton instance that will use {@code context} to issue intents. - * - * This method is only kept until the downstream callers of this method have been changed to use - * {@link InvalidationController#get(android.content.Context)}. - * - * TODO(nyquist) Remove this method. - */ - @Deprecated - public static InvalidationController newInstance(Context context) { - return get(context); - } - - /** * Creates an instance using {@code context} to send intents. */ @VisibleForTesting @@ -222,43 +172,6 @@ public class InvalidationController implements ActivityStatus.StateListener { ActivityStatus.registerStateListener(this); } - /** - * Sets the destination class name of {@code intent} to the value given by the manifest - * property named {@link #IMPLEMENTING_CLASS_MANIFEST_PROPERTY}. If no such property exists or - * its value is null, takes no action. - * - * @return {@code intent} - */ - private Intent setDestinationClassName(Intent intent) { - String className = getDestinationClassName(mContext); - if (className != null) { - intent.setClassName(mContext, className); - } - return intent; - } - - @VisibleForTesting - @Nullable static String getDestinationClassName(Context context) { - ApplicationInfo appInfo; - try { - // Fetch application info and read the appropriate metadata element. - appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), - PackageManager.GET_META_DATA); - String className = null; - if (appInfo.metaData != null) { - className = appInfo.metaData.getString(IMPLEMENTING_CLASS_MANIFEST_PROPERTY); - } - if (className == null) { - Log.wtf(TAG, "No value for " + IMPLEMENTING_CLASS_MANIFEST_PROPERTY - + " in manifest; sync notifications will not work"); - } - return className; - } catch (NameNotFoundException exception) { - Log.wtf(TAG, "Cannot read own application info", exception); - } - return null; - } - @Override public void onActivityStateChange(int newState) { if (SyncStatusHelper.get(mContext).isSyncEnabled()) { diff --git a/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java b/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java index 90d4db9560..76ddd49d8a 100644 --- a/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java +++ b/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java @@ -285,19 +285,12 @@ public class InvalidationControllerTest extends InstrumentationTestCase { assertEquals(mContext.getPackageName(), mController.getContractAuthority()); } - @SmallTest - @Feature({"Sync"}) - public void testGetIntentDestination() { - assertEquals("org.chromium.sync.notifier.TEST_VALUE", - InvalidationController.getDestinationClassName(mContext)); - } - /** * Asserts that {@code intent} is destined for the correct component. */ private static void validateIntentComponent(Intent intent) { assertNotNull(intent.getComponent()); - assertEquals("org.chromium.sync.notifier.TEST_VALUE", + assertEquals(InvalidationService.class.getName(), intent.getComponent().getClassName()); } diff --git a/sync/engine/conflict_resolver.cc b/sync/engine/conflict_resolver.cc index 074eb37460..fafd237c95 100644 --- a/sync/engine/conflict_resolver.cc +++ b/sync/engine/conflict_resolver.cc @@ -222,52 +222,19 @@ void ConflictResolver::ResolveConflicts( const std::set<syncable::Id>& simple_conflict_ids, sessions::StatusController* status) { // Iterate over simple conflict items. - set<Id>::const_iterator conflicting_item_it; - set<Id> processed_items; - for (conflicting_item_it = simple_conflict_ids.begin(); - conflicting_item_it != simple_conflict_ids.end(); - ++conflicting_item_it) { - Id id = *conflicting_item_it; - if (processed_items.count(id) > 0) - continue; - + set<Id>::const_iterator it; + for (it = simple_conflict_ids.begin(); + it != simple_conflict_ids.end(); + ++it) { // We don't resolve conflicts for control types here. - Entry conflicting_node(trans, syncable::GET_BY_ID, id); + Entry conflicting_node(trans, syncable::GET_BY_ID, *it); CHECK(conflicting_node.good()); if (IsControlType( GetModelTypeFromSpecifics(conflicting_node.Get(syncable::SPECIFICS)))) { continue; } - // We have a simple conflict. In order check if positions have changed, - // we need to process conflicting predecessors before successors. Traverse - // backwards through all continuous conflicting predecessors, building a - // stack of items to resolve in predecessor->successor order, then process - // each item individually. - list<Id> predecessors; - Id prev_id = id; - do { - predecessors.push_back(prev_id); - Entry entry(trans, syncable::GET_BY_ID, prev_id); - // Any entry in conflict must be valid. - CHECK(entry.good()); - - // We can't traverse over a delete item. - if (entry.Get(syncable::IS_DEL)) - break; - - Id new_prev_id = entry.GetPredecessorId(); - if (new_prev_id == prev_id) - break; - prev_id = new_prev_id; - } while (processed_items.count(prev_id) == 0 && - simple_conflict_ids.count(prev_id) > 0); // Excludes root. - while (!predecessors.empty()) { - id = predecessors.back(); - predecessors.pop_back(); - ProcessSimpleConflict(trans, id, cryptographer, status); - processed_items.insert(id); - } + ProcessSimpleConflict(trans, *it, cryptographer, status); } return; } diff --git a/sync/engine/sync_scheduler_impl.cc b/sync/engine/sync_scheduler_impl.cc index 626a695cd3..55adcc52b6 100644 --- a/sync/engine/sync_scheduler_impl.cc +++ b/sync/engine/sync_scheduler_impl.cc @@ -334,13 +334,25 @@ bool SyncSchedulerImpl::CanRunNudgeJobNow(JobPriority priority) { return false; } - // If all types are throttled, do not continue. Today, we don't treat a - // per-datatype "unthrottle" event as something that should force a canary - // job. For this reason, there's no good time to reschedule this job to run - // -- we'll lazily wait for an independent event to trigger a sync. + // Per-datatype throttling will prevent a sync cycle if the only reason to + // perform this cycle is to commit a local change. If there is any other + // reason to perform the sync cycle (such as an invalidation for the throttled + // type or a local change to non-throttled type) then the cycle will be + // allowed to proceed. + // + // If the cycle is prevented, we will drop it without scheduling another. + // Unlike global throttling, we have no timer to tell us when to wake us up + // when a type is unthrottled. + // + // If the sync cycle is not prevented, the commit message will be filtered to + // prevent the commit of any items of the throttled data type. However, if + // the sync cycle succeeds, the nudge tracker will be told to assume that all + // outstanding commits (including the ones for throttled types) were completed + // successfully. It does not yet have the ability to selectively clear the + // uncommitted status of only some types. ModelTypeSet throttled_types = session_context_->throttled_data_type_tracker()->GetThrottledTypes(); - if (!nudge_tracker_.GetLocallyModifiedTypes().Empty() && + if (!nudge_tracker_.IsGetUpdatesRequired() && throttled_types.HasAll(nudge_tracker_.GetLocallyModifiedTypes())) { // TODO(sync): Throttled types should be pruned from the sources list. SDVLOG(1) << "Not running a nudge because we're fully datatype throttled."; @@ -378,7 +390,7 @@ void SyncSchedulerImpl::ScheduleLocalRefreshRequest( DCHECK(!types.Empty()); SDVLOG_LOC(nudge_location, 2) - << "Scheduling sync because of local refresch request for " + << "Scheduling sync because of local refresh request for " << ModelTypeSetToString(types); nudge_tracker_.RecordLocalRefreshRequest(types); ScheduleNudgeImpl(desired_delay, nudge_location); @@ -543,8 +555,12 @@ void SyncSchedulerImpl::HandleFailure( if (IsSyncingCurrentlySilenced()) { SDVLOG(2) << "Was throttled during previous sync cycle."; RestartWaiting(); - } else { - UpdateExponentialBackoff(model_neutral_state); + } else if (!IsBackingOff()) { + // Setup our backoff if this is our first such failure. + TimeDelta length = delay_provider_->GetDelay( + delay_provider_->GetInitialDelay(model_neutral_state)); + wait_interval_.reset( + new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, length)); SDVLOG(2) << "Sync cycle failed. Will back off for " << wait_interval_->length.InMilliseconds() << "ms."; RestartWaiting(); @@ -638,22 +654,11 @@ void SyncSchedulerImpl::RestartWaiting() { pending_wakeup_timer_.Start( FROM_HERE, wait_interval_->length, - base::Bind(&SyncSchedulerImpl::TryCanaryJob, + base::Bind(&SyncSchedulerImpl::ExponentialBackoffRetry, weak_ptr_factory_.GetWeakPtr())); } } -void SyncSchedulerImpl::UpdateExponentialBackoff( - const sessions::ModelNeutralState& model_neutral_state) { - DCHECK(CalledOnValidThread()); - - TimeDelta length = delay_provider_->GetDelay( - IsBackingOff() ? wait_interval_->length : - delay_provider_->GetInitialDelay(model_neutral_state)); - wait_interval_.reset(new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, - length)); -} - void SyncSchedulerImpl::RequestStop(const base::Closure& callback) { syncer_->RequestEarlyExit(); // Safe to call from any thread. DCHECK(weak_handle_this_.IsInitialized()); @@ -728,6 +733,22 @@ void SyncSchedulerImpl::Unthrottle() { TryCanaryJob(); } +void SyncSchedulerImpl::ExponentialBackoffRetry() { + TryCanaryJob(); + + if (IsBackingOff()) { + // If we succeeded, our wait interval would have been cleared. If it hasn't + // been cleared, then we should increase our backoff interval and schedule + // another retry. + TimeDelta length = delay_provider_->GetDelay(wait_interval_->length); + wait_interval_.reset( + new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, length)); + SDVLOG(2) << "Sync cycle failed. Will back off for " + << wait_interval_->length.InMilliseconds() << "ms."; + RestartWaiting(); + } +} + void SyncSchedulerImpl::Notify(SyncEngineEvent::EventCause cause) { DCHECK(CalledOnValidThread()); session_context_->NotifyListeners(SyncEngineEvent(cause)); diff --git a/sync/engine/sync_scheduler_impl.h b/sync/engine/sync_scheduler_impl.h index d5ee56c27a..46f5c35e32 100644 --- a/sync/engine/sync_scheduler_impl.h +++ b/sync/engine/sync_scheduler_impl.h @@ -172,10 +172,6 @@ class SYNC_EXPORT_PRIVATE SyncSchedulerImpl // Helper to restart waiting with |wait_interval_|'s timer. void RestartWaiting(); - // Helper to adjust our wait interval when we expereince a transient failure. - void UpdateExponentialBackoff( - const sessions::ModelNeutralState& model_neutral_state); - // Determines if we're allowed to contact the server right now. bool CanRunJobNow(JobPriority priority); @@ -209,6 +205,9 @@ class SYNC_EXPORT_PRIVATE SyncSchedulerImpl // Transitions out of the THROTTLED WaitInterval then calls TryCanaryJob(). void Unthrottle(); + // Attempts to exit EXPONENTIAL_BACKOFF by calling TryCanaryJob(). + void ExponentialBackoffRetry(); + // Called when the root cause of the current connection error is fixed. void OnServerConnectionErrorFixed(); diff --git a/sync/engine/sync_scheduler_unittest.cc b/sync/engine/sync_scheduler_unittest.cc index 21e14306df..3820102b8c 100644 --- a/sync/engine/sync_scheduler_unittest.cc +++ b/sync/engine/sync_scheduler_unittest.cc @@ -225,6 +225,10 @@ class SyncSchedulerTest : public testing::Test { SyncSessionContext* context() { return context_.get(); } + ThrottledDataTypeTracker* throttled_data_type_tracker() { + return throttled_data_type_tracker_.get(); + } + private: syncable::Directory* directory() { return dir_maker_.directory(); @@ -778,6 +782,89 @@ TEST_F(SyncSchedulerTest, ThrottlingExpiresFromConfigure) { StopSyncScheduler(); } +TEST_F(SyncSchedulerTest, TypeThrottlingBlocksNudge) { + UseMockDelayProvider(); + EXPECT_CALL(*delay(), GetDelay(_)) + .WillRepeatedly(Return(zero())); + + TimeDelta poll(TimeDelta::FromDays(1)); + TimeDelta throttle1(TimeDelta::FromSeconds(60)); + scheduler()->OnReceivedLongPollIntervalUpdate(poll); + + const ModelTypeSet types(BOOKMARKS); + + ::testing::InSequence seq; + EXPECT_CALL(*syncer(), SyncShare(_,_,_)) + .WillOnce(DoAll( + WithArg<0>( + sessions::test_util::SimulateTypesThrottled(types, throttle1)), + Return(true))) + .RetiresOnSaturation(); + + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + scheduler()->ScheduleLocalNudge(zero(), types, FROM_HERE); + PumpLoop(); + EXPECT_TRUE(throttled_data_type_tracker()->GetThrottledTypes().HasAll(types)); + + // This won't cause a sync cycle because the types are throttled. + scheduler()->ScheduleLocalNudge(zero(), types, FROM_HERE); + PumpLoop(); + + StopSyncScheduler(); +} + +TEST_F(SyncSchedulerTest, TypeThrottlingDoesntBlockOtherSources) { + UseMockDelayProvider(); + EXPECT_CALL(*delay(), GetDelay(_)) + .WillRepeatedly(Return(zero())); + + SyncShareRecords records; + TimeDelta poll(TimeDelta::FromDays(1)); + TimeDelta throttle1(TimeDelta::FromSeconds(60)); + scheduler()->OnReceivedLongPollIntervalUpdate(poll); + + const ModelTypeSet throttled_types(BOOKMARKS); + const ModelTypeSet unthrottled_types(PREFERENCES); + + ::testing::InSequence seq; + EXPECT_CALL(*syncer(), SyncShare(_,_,_)) + .WillOnce(DoAll( + WithArg<0>( + sessions::test_util::SimulateTypesThrottled( + throttled_types, throttle1)), + Return(true))) + .RetiresOnSaturation(); + EXPECT_CALL(*syncer(), SyncShare(_,_,_)) + .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), + WithArg<0>(RecordSyncShare(&records)))); + + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + scheduler()->ScheduleLocalNudge(zero(), throttled_types, FROM_HERE); + PumpLoop(); + EXPECT_EQ(0U, records.snapshots.size()); + EXPECT_TRUE(throttled_data_type_tracker()->GetThrottledTypes().HasAll( + throttled_types)); + + // This invalidaiton will cause a sync even though the types are throttled. + ModelTypeInvalidationMap invalidation_map = + ModelTypeSetToInvalidationMap(throttled_types, "test"); + scheduler()->ScheduleInvalidationNudge(zero(), invalidation_map, FROM_HERE); + RunLoop(); + EXPECT_EQ(1U, records.snapshots.size()); + + // Refresh requests will cause a sync, too. + scheduler()->ScheduleLocalRefreshRequest(zero(), throttled_types, FROM_HERE); + RunLoop(); + EXPECT_EQ(2U, records.snapshots.size()); + + // Even local nudges for other data types will trigger a sync. + scheduler()->ScheduleLocalNudge(zero(), unthrottled_types, FROM_HERE); + RunLoop(); + EXPECT_EQ(3U, records.snapshots.size()); + + StopSyncScheduler(); +} + // Test nudges / polls don't run in config mode and config tasks do. TEST_F(SyncSchedulerTest, ConfigurationMode) { TimeDelta poll(TimeDelta::FromMilliseconds(15)); diff --git a/sync/internal_api/base_node.cc b/sync/internal_api/base_node.cc index 1e1513b1b7..f9bf79b67a 100644 --- a/sync/internal_api/base_node.cc +++ b/sync/internal_api/base_node.cc @@ -215,6 +215,10 @@ int64 BaseNode::GetFirstChildId() const { return IdToMetahandle(GetTransaction()->GetWrappedTrans(), id_string); } +void BaseNode::GetChildIds(std::vector<int64>* result) const { + GetEntry()->GetChildHandles(result); +} + int BaseNode::GetTotalNodeCount() const { syncable::BaseTransaction* trans = GetTransaction()->GetWrappedTrans(); @@ -335,6 +339,11 @@ const sync_pb::ManagedUserSettingSpecifics& return GetEntitySpecifics().managed_user_setting(); } +const sync_pb::ManagedUserSpecifics& BaseNode::GetManagedUserSpecifics() const { + DCHECK_EQ(GetModelType(), MANAGED_USERS); + return GetEntitySpecifics().managed_user(); +} + const sync_pb::DeviceInfoSpecifics& BaseNode::GetDeviceInfoSpecifics() const { DCHECK_EQ(GetModelType(), DEVICE_INFO); return GetEntitySpecifics().device_info(); diff --git a/sync/internal_api/internal_components_factory_impl.cc b/sync/internal_api/internal_components_factory_impl.cc index ccc195112b..ea60250f42 100644 --- a/sync/internal_api/internal_components_factory_impl.cc +++ b/sync/internal_api/internal_components_factory_impl.cc @@ -36,7 +36,7 @@ scoped_ptr<sessions::SyncSessionContext> InternalComponentsFactoryImpl::BuildContext( ServerConnectionManager* connection_manager, syncable::Directory* directory, - const std::vector<ModelSafeWorker*> workers, + const std::vector<ModelSafeWorker*>& workers, ExtensionsActivityMonitor* monitor, ThrottledDataTypeTracker* throttled_data_type_tracker, const std::vector<SyncEngineEventListener*>& listeners, diff --git a/sync/internal_api/public/base/model_type.h b/sync/internal_api/public/base/model_type.h index 268715a9b9..59f970fd86 100644 --- a/sync/internal_api/public/base/model_type.h +++ b/sync/internal_api/public/base/model_type.h @@ -95,6 +95,10 @@ enum ModelType { PRIORITY_PREFERENCES, // Managed user settings. MANAGED_USER_SETTINGS, + // Managed users. Every managed user is a profile that is configured remotely + // by this user and can have restrictions applied. MANAGED_USERS and + // MANAGED_USER_SETTINGS can not be encrypted. + MANAGED_USERS, // ---- Proxy types ---- // Proxy types are excluded from the sync protocol, but are still considered diff --git a/sync/internal_api/public/base_node.h b/sync/internal_api/public/base_node.h index 7cf2edb2af..6c54ce2ad1 100644 --- a/sync/internal_api/public/base_node.h +++ b/sync/internal_api/public/base_node.h @@ -158,6 +158,10 @@ class SYNC_EXPORT BaseNode { const sync_pb::ManagedUserSettingSpecifics& GetManagedUserSettingSpecifics() const; + // Getter specific to the MANAGED_USERS datatype. Returns protobuf data. + // Can only be called if GetModelType() == MANAGED_USERS. + const sync_pb::ManagedUserSpecifics& GetManagedUserSpecifics() const; + // Getter specific to the DEVICE_INFO datatype. Returns protobuf // data. Can only be called if GetModelType() == DEVICE_INFO. const sync_pb::DeviceInfoSpecifics& GetDeviceInfoSpecifics() const; @@ -191,6 +195,11 @@ class SYNC_EXPORT BaseNode { // children, return 0. int64 GetFirstChildId() const; + // Returns the IDs of the children of this node. + // If this type supports user-defined positions the returned IDs will be in + // the correct order. + void GetChildIds(std::vector<int64>* result) const; + // Returns the total number of nodes including and beneath this node. // Recursively iterates through all children. int GetTotalNodeCount() const; diff --git a/sync/internal_api/public/engine/model_safe_worker.cc b/sync/internal_api/public/engine/model_safe_worker.cc index d7bf906ef9..3e2096a942 100644 --- a/sync/internal_api/public/engine/model_safe_worker.cc +++ b/sync/internal_api/public/engine/model_safe_worker.cc @@ -80,6 +80,58 @@ std::string ModelSafeGroupToString(ModelSafeGroup group) { } } +ModelSafeWorker::ModelSafeWorker(WorkerLoopDestructionObserver* observer) + : stopped_(false), + work_done_or_stopped_(false, false), + observer_(observer) {} + ModelSafeWorker::~ModelSafeWorker() {} +void ModelSafeWorker::RequestStop() { + base::AutoLock al(stopped_lock_); + + // Set stop flag but don't signal work_done_or_stopped_ to unblock sync loop + // because the worker may be working and depending on sync command object + // living on sync thread. his prevents any *further* tasks from being posted + // to worker threads (see DoWorkAndWaitUntilDone below), but note that one + // may already be posted. + stopped_ = true; +} + +SyncerError ModelSafeWorker::DoWorkAndWaitUntilDone(const WorkCallback& work) { + { + base::AutoLock al(stopped_lock_); + if (stopped_) + return CANNOT_DO_WORK; + + CHECK(!work_done_or_stopped_.IsSignaled()); + } + + return DoWorkAndWaitUntilDoneImpl(work); +} + +bool ModelSafeWorker::IsStopped() { + base::AutoLock al(stopped_lock_); + return stopped_; +} + +void ModelSafeWorker::WillDestroyCurrentMessageLoop() { + { + base::AutoLock al(stopped_lock_); + stopped_ = true; + + // Must signal to unblock syncer if it's waiting for a posted task to + // finish. At this point, all pending tasks posted to the loop have been + // destroyed (see MessageLoop::~MessageLoop). So syncer will be blocked + // indefinitely without signaling here. + work_done_or_stopped_.Signal(); + + DVLOG(1) << ModelSafeGroupToString(GetModelSafeGroup()) + << " worker stops on destruction of its working thread."; + } + + if (observer_) + observer_->OnWorkerLoopDestroyed(GetModelSafeGroup()); +} + } // namespace syncer diff --git a/sync/internal_api/public/engine/model_safe_worker.h b/sync/internal_api/public/engine/model_safe_worker.h index fbec6b17a0..94925dfa54 100644 --- a/sync/internal_api/public/engine/model_safe_worker.h +++ b/sync/internal_api/public/engine/model_safe_worker.h @@ -11,6 +11,9 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" +#include "base/message_loop.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" #include "sync/base/sync_export.h" #include "sync/internal_api/public/base/model_type.h" #include "sync/internal_api/public/base/model_type_invalidation_map.h" @@ -44,28 +47,74 @@ enum ModelSafeGroup { SYNC_EXPORT std::string ModelSafeGroupToString(ModelSafeGroup group); +// WorkerLoopDestructionObserver is notified when the thread where it works +// is going to be destroyed. +class WorkerLoopDestructionObserver { + public: + virtual void OnWorkerLoopDestroyed(ModelSafeGroup group) = 0; +}; + // The Syncer uses a ModelSafeWorker for all tasks that could potentially // modify syncable entries (e.g under a WriteTransaction). The ModelSafeWorker // only knows how to do one thing, and that is take some work (in a fully // pre-bound callback) and have it performed (as in Run()) from a thread which // is guaranteed to be "model-safe", where "safe" refers to not allowing us to // cause an embedding application model to fall out of sync with the -// syncable::Directory due to a race. +// syncable::Directory due to a race. Each ModelSafeWorker is affiliated with +// a thread and does actual work on that thread. On the destruction of that +// thread, the affiliated worker is effectively disabled to do more +// work and will notify its observer. class SYNC_EXPORT ModelSafeWorker - : public base::RefCountedThreadSafe<ModelSafeWorker> { + : public base::RefCountedThreadSafe<ModelSafeWorker>, + public MessageLoop::DestructionObserver { public: - // Any time the Syncer performs model modifications (e.g employing a - // WriteTransaction), it should be done by this method to ensure it is done - // from a model-safe thread. - virtual SyncerError DoWorkAndWaitUntilDone(const WorkCallback& work) = 0; + // Subclass should implement to observe destruction of the loop where + // it actually does work. + virtual void RegisterForLoopDestruction() = 0; + + // If not stopped, call DoWorkAndWaitUntilDoneImpl() to do work. Otherwise + // return CANNOT_DO_WORK. + SyncerError DoWorkAndWaitUntilDone(const WorkCallback& work); + + // Soft stop worker by setting stopped_ flag. Called when sync is disabled + // or browser is shutting down. + void RequestStop(); virtual ModelSafeGroup GetModelSafeGroup() = 0; + // MessageLoop::DestructionObserver implementation. + virtual void WillDestroyCurrentMessageLoop() OVERRIDE; + protected: + friend class base::RefCountedThreadSafe<ModelSafeWorker>; + + explicit ModelSafeWorker(WorkerLoopDestructionObserver* observer); virtual ~ModelSafeWorker(); + // Any time the Syncer performs model modifications (e.g employing a + // WriteTransaction), it should be done by this method to ensure it is done + // from a model-safe thread. + virtual SyncerError DoWorkAndWaitUntilDoneImpl(const WorkCallback& work) = 0; + + base::WaitableEvent* work_done_or_stopped() { + return &work_done_or_stopped_; + } + + // Return true if the worker was stopped. Thread safe. + bool IsStopped(); + private: - friend class base::RefCountedThreadSafe<ModelSafeWorker>; + // Whether the worker should/can do more work. Set when sync is disabled or + // when the worker's working thread is to be destroyed. + base::Lock stopped_lock_; + bool stopped_; + + // Signal set when work on native thread is finished or when native thread + // is to be destroyed so no more work can be done. + base::WaitableEvent work_done_or_stopped_; + + // Notified when working thread of the worker is to be destroyed. + WorkerLoopDestructionObserver* observer_; }; // A map that details which ModelSafeGroup each ModelType diff --git a/sync/internal_api/public/engine/passive_model_worker.cc b/sync/internal_api/public/engine/passive_model_worker.cc index 50d00cf081..8b1a4e3c93 100644 --- a/sync/internal_api/public/engine/passive_model_worker.cc +++ b/sync/internal_api/public/engine/passive_model_worker.cc @@ -8,13 +8,20 @@ namespace syncer { -PassiveModelWorker::PassiveModelWorker(const base::MessageLoop* sync_loop) - : sync_loop_(sync_loop) {} +PassiveModelWorker::PassiveModelWorker(const base::MessageLoop* sync_loop, + WorkerLoopDestructionObserver* observer) + : ModelSafeWorker(observer), + sync_loop_(sync_loop) { +} PassiveModelWorker::~PassiveModelWorker() { } -SyncerError PassiveModelWorker::DoWorkAndWaitUntilDone( +void PassiveModelWorker::RegisterForLoopDestruction() { + NOTREACHED(); +} + +SyncerError PassiveModelWorker::DoWorkAndWaitUntilDoneImpl( const WorkCallback& work) { DCHECK_EQ(base::MessageLoop::current(), sync_loop_); // Simply do the work on the current thread. diff --git a/sync/internal_api/public/engine/passive_model_worker.h b/sync/internal_api/public/engine/passive_model_worker.h index a6ea011dd7..f834c833c7 100644 --- a/sync/internal_api/public/engine/passive_model_worker.h +++ b/sync/internal_api/public/engine/passive_model_worker.h @@ -22,13 +22,17 @@ namespace syncer { // thread). class SYNC_EXPORT PassiveModelWorker : public ModelSafeWorker { public: - explicit PassiveModelWorker(const base::MessageLoop* sync_loop); + explicit PassiveModelWorker(const base::MessageLoop* sync_loop, + WorkerLoopDestructionObserver* observer); // ModelSafeWorker implementation. Called on the sync thread. - virtual SyncerError DoWorkAndWaitUntilDone( - const WorkCallback& work) OVERRIDE; + virtual void RegisterForLoopDestruction() OVERRIDE; virtual ModelSafeGroup GetModelSafeGroup() OVERRIDE; + protected: + virtual SyncerError DoWorkAndWaitUntilDoneImpl( + const WorkCallback& work) OVERRIDE; + private: virtual ~PassiveModelWorker(); diff --git a/sync/internal_api/public/internal_components_factory.h b/sync/internal_api/public/internal_components_factory.h index f49f88a056..1d5b122a4e 100644 --- a/sync/internal_api/public/internal_components_factory.h +++ b/sync/internal_api/public/internal_components_factory.h @@ -71,7 +71,7 @@ class SYNC_EXPORT InternalComponentsFactory { virtual scoped_ptr<sessions::SyncSessionContext> BuildContext( ServerConnectionManager* connection_manager, syncable::Directory* directory, - const std::vector<ModelSafeWorker*> workers, + const std::vector<ModelSafeWorker*>& workers, ExtensionsActivityMonitor* monitor, ThrottledDataTypeTracker* throttled_data_type_tracker, const std::vector<SyncEngineEventListener*>& listeners, diff --git a/sync/internal_api/public/internal_components_factory_impl.h b/sync/internal_api/public/internal_components_factory_impl.h index 197ce46a44..646da5ff07 100644 --- a/sync/internal_api/public/internal_components_factory_impl.h +++ b/sync/internal_api/public/internal_components_factory_impl.h @@ -26,7 +26,7 @@ class SYNC_EXPORT InternalComponentsFactoryImpl virtual scoped_ptr<sessions::SyncSessionContext> BuildContext( ServerConnectionManager* connection_manager, syncable::Directory* directory, - const std::vector<ModelSafeWorker*> workers, + const std::vector<ModelSafeWorker*>& workers, ExtensionsActivityMonitor* monitor, ThrottledDataTypeTracker* throttled_data_type_tracker, const std::vector<SyncEngineEventListener*>& listeners, diff --git a/sync/internal_api/public/test/test_internal_components_factory.h b/sync/internal_api/public/test/test_internal_components_factory.h index 9db26e21b9..665999d30b 100644 --- a/sync/internal_api/public/test/test_internal_components_factory.h +++ b/sync/internal_api/public/test/test_internal_components_factory.h @@ -32,7 +32,7 @@ class TestInternalComponentsFactory : public InternalComponentsFactory { virtual scoped_ptr<sessions::SyncSessionContext> BuildContext( ServerConnectionManager* connection_manager, syncable::Directory* directory, - const std::vector<ModelSafeWorker*> workers, + const std::vector<ModelSafeWorker*>& workers, ExtensionsActivityMonitor* monitor, ThrottledDataTypeTracker* throttled_data_type_tracker, const std::vector<SyncEngineEventListener*>& listeners, diff --git a/sync/internal_api/public/write_node.h b/sync/internal_api/public/write_node.h index 3cb4039a20..d3781e8b9e 100644 --- a/sync/internal_api/public/write_node.h +++ b/sync/internal_api/public/write_node.h @@ -165,6 +165,10 @@ class SYNC_EXPORT WriteNode : public BaseNode { void SetManagedUserSettingSpecifics( const sync_pb::ManagedUserSettingSpecifics& specifics); + // Set the managed user setting specifics (name and value). + // Should only be called if GetModelType() == MANAGED_USERS. + void SetManagedUserSpecifics(const sync_pb::ManagedUserSpecifics& specifics); + // Set the device info specifics. // Should only be called if GetModelType() == DEVICE_INFO. void SetDeviceInfoSpecifics(const sync_pb::DeviceInfoSpecifics& specifics); diff --git a/sync/internal_api/test/test_internal_components_factory.cc b/sync/internal_api/test/test_internal_components_factory.cc index dee381f932..18962bc9fc 100644 --- a/sync/internal_api/test/test_internal_components_factory.cc +++ b/sync/internal_api/test/test_internal_components_factory.cc @@ -30,7 +30,7 @@ scoped_ptr<sessions::SyncSessionContext> TestInternalComponentsFactory::BuildContext( ServerConnectionManager* connection_manager, syncable::Directory* directory, - const std::vector<ModelSafeWorker*> workers, + const std::vector<ModelSafeWorker*>& workers, ExtensionsActivityMonitor* monitor, ThrottledDataTypeTracker* throttled_data_type_tracker, const std::vector<SyncEngineEventListener*>& listeners, diff --git a/sync/internal_api/write_node.cc b/sync/internal_api/write_node.cc index db461d6cdb..22496c1fdd 100644 --- a/sync/internal_api/write_node.cc +++ b/sync/internal_api/write_node.cc @@ -193,6 +193,13 @@ void WriteNode::SetManagedUserSettingSpecifics( SetEntitySpecifics(entity_specifics); } +void WriteNode::SetManagedUserSpecifics( + const sync_pb::ManagedUserSpecifics& new_value) { + sync_pb::EntitySpecifics entity_specifics; + entity_specifics.mutable_managed_user()->CopyFrom(new_value); + SetEntitySpecifics(entity_specifics); +} + void WriteNode::SetDeviceInfoSpecifics( const sync_pb::DeviceInfoSpecifics& new_value) { sync_pb::EntitySpecifics entity_specifics; diff --git a/sync/protocol/managed_user_specifics.proto b/sync/protocol/managed_user_specifics.proto new file mode 100644 index 0000000000..c71bb29ee6 --- /dev/null +++ b/sync/protocol/managed_user_specifics.proto @@ -0,0 +1,26 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Sync protocol datatype extension for managed user settings. + +// Update proto_value_conversions{.h,.cc,_unittest.cc} if you change +// any fields in this file. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; +option retain_unknown_fields = true; + +package sync_pb; + +// Properties of managed user sync objects. +message ManagedUserSpecifics { + // A randomly-generated identifier for the managed user. + optional string id = 1; + // The human-visible name of the managed user + optional string name = 2; + // This flag is set by the server to acknowledge that it has committed a + // newly created managed user. + optional bool acknowledged = 3 [default = false]; +} diff --git a/sync/protocol/proto_enum_conversions.cc b/sync/protocol/proto_enum_conversions.cc index f0095a760b..ad07c5f463 100644 --- a/sync/protocol/proto_enum_conversions.cc +++ b/sync/protocol/proto_enum_conversions.cc @@ -219,6 +219,18 @@ const char* SingletonEventTypeString( return ""; } +const char* GetBlockedStateString( + sync_pb::TabNavigation::BlockedState state) { + ASSERT_ENUM_BOUNDS(sync_pb::TabNavigation, BlockedState, + STATE_ALLOWED, STATE_BLOCKED); + switch (state) { + ENUM_CASE(sync_pb::TabNavigation, STATE_ALLOWED); + ENUM_CASE(sync_pb::TabNavigation, STATE_BLOCKED); + } + NOTREACHED(); + return ""; +} + #undef ASSERT_ENUM_BOUNDS #undef ENUM_CASE diff --git a/sync/protocol/proto_enum_conversions.h b/sync/protocol/proto_enum_conversions.h index 2f29ed5ed5..b6d84facf9 100644 --- a/sync/protocol/proto_enum_conversions.h +++ b/sync/protocol/proto_enum_conversions.h @@ -45,18 +45,17 @@ SYNC_EXPORT_PRIVATE const char* GetErrorTypeString( SYNC_EXPORT_PRIVATE const char* GetActionString( sync_pb::SyncEnums::Action action); -const char* GetDeviceTypeString( - sync_pb::SyncEnums::DeviceType device_type); +const char* GetDeviceTypeString(sync_pb::SyncEnums::DeviceType device_type); -const char* GetFaviconTypeString( - sync_pb::SessionTab::FaviconType favicon_type); +const char* GetFaviconTypeString(sync_pb::SessionTab::FaviconType favicon_type); -const char* PassphraseTypeString( - sync_pb::NigoriSpecifics::PassphraseType type); +const char* PassphraseTypeString(sync_pb::NigoriSpecifics::PassphraseType type); const char* SingletonEventTypeString( sync_pb::DebugEventInfo::SingletonEventType type); +const char* GetBlockedStateString(sync_pb::TabNavigation::BlockedState state); + } // namespace syncer #endif // SYNC_PROTOCOL_PROTO_ENUM_CONVERSIONS_H_ diff --git a/sync/protocol/proto_value_conversions.cc b/sync/protocol/proto_value_conversions.cc index 3f5ef8ef39..c25bbebd0a 100644 --- a/sync/protocol/proto_value_conversions.cc +++ b/sync/protocol/proto_value_conversions.cc @@ -198,6 +198,8 @@ base::DictionaryValue* TabNavigationToValue( SET_INT64(global_id); SET_STR(search_terms); SET_STR(favicon_url); + SET_ENUM(blocked_state, GetBlockedStateString); + SET_STR_REP(content_pack_categories); return value; } @@ -423,6 +425,15 @@ base::DictionaryValue* ManagedUserSettingSpecificsToValue( return value; } +base::DictionaryValue* ManagedUserSpecificsToValue( + const sync_pb::ManagedUserSpecifics& proto) { + base::DictionaryValue* value = new base::DictionaryValue(); + SET_STR(id); + SET_STR(name); + SET_BOOL(acknowledged); + return value; +} + base::DictionaryValue* NigoriSpecificsToValue( const sync_pb::NigoriSpecifics& proto) { base::DictionaryValue* value = new base::DictionaryValue(); @@ -552,6 +563,7 @@ base::DictionaryValue* EntitySpecificsToValue( SET_FIELD(favicon_tracking, FaviconTrackingSpecificsToValue); SET_FIELD(history_delete_directive, HistoryDeleteDirectiveSpecificsToValue); SET_FIELD(managed_user_setting, ManagedUserSettingSpecificsToValue); + SET_FIELD(managed_user, ManagedUserSpecificsToValue); SET_FIELD(nigori, NigoriSpecificsToValue); SET_FIELD(password, PasswordSpecificsToValue); SET_FIELD(preference, PreferenceSpecificsToValue); diff --git a/sync/protocol/proto_value_conversions.h b/sync/protocol/proto_value_conversions.h index 5a98758e02..8bcc26d136 100644 --- a/sync/protocol/proto_value_conversions.h +++ b/sync/protocol/proto_value_conversions.h @@ -42,6 +42,7 @@ class GlobalIdDirective; class HistoryDeleteDirectiveSpecifics; class KeystoreEncryptionFlagsSpecifics; class ManagedUserSettingSpecifics; +class ManagedUserSpecifics; class NigoriSpecifics; class PasswordSpecifics; class PasswordSpecificsData; @@ -170,6 +171,9 @@ SYNC_EXPORT base::DictionaryValue* HistoryDeleteDirectiveSpecificsToValue( SYNC_EXPORT_PRIVATE base::DictionaryValue* ManagedUserSettingSpecificsToValue( const sync_pb::ManagedUserSettingSpecifics& managed_user_setting_specifics); +SYNC_EXPORT_PRIVATE base::DictionaryValue* ManagedUserSpecificsToValue( + const sync_pb::ManagedUserSpecifics& managed_user_specifics); + SYNC_EXPORT_PRIVATE base::DictionaryValue* NigoriSpecificsToValue( const sync_pb::NigoriSpecifics& nigori_specifics); diff --git a/sync/protocol/proto_value_conversions_unittest.cc b/sync/protocol/proto_value_conversions_unittest.cc index fe2750cb7a..792065c76f 100644 --- a/sync/protocol/proto_value_conversions_unittest.cc +++ b/sync/protocol/proto_value_conversions_unittest.cc @@ -53,7 +53,7 @@ TEST_F(ProtoValueConversionsTest, ProtoChangeCheck) { // If this number changes, that means we added or removed a data // type. Don't forget to add a unit test for {New // type}SpecificsToValue below. - EXPECT_EQ(27, MODEL_TYPE_COUNT); + EXPECT_EQ(28, MODEL_TYPE_COUNT); // We'd also like to check if we changed any field in our messages. // However, that's hard to do: sizeof could work, but it's @@ -178,6 +178,10 @@ TEST_F(ProtoValueConversionsTest, ManagedUserSettingSpecificsToValue) { TestSpecificsToValue(ManagedUserSettingSpecificsToValue); } +TEST_F(ProtoValueConversionsTest, ManagedUserSpecificsToValue) { + TestSpecificsToValue(ManagedUserSpecificsToValue); +} + TEST_F(ProtoValueConversionsTest, NigoriSpecificsToValue) { TestSpecificsToValue(NigoriSpecificsToValue); } @@ -237,6 +241,7 @@ TEST_F(ProtoValueConversionsTest, EntitySpecificsToValue) { SET_FIELD(favicon_tracking); SET_FIELD(history_delete_directive); SET_FIELD(managed_user_setting); + SET_FIELD(managed_user); SET_FIELD(nigori); SET_FIELD(password); SET_FIELD(preference); diff --git a/sync/protocol/session_specifics.proto b/sync/protocol/session_specifics.proto index 707e24c69e..f7b7d113ff 100644 --- a/sync/protocol/session_specifics.proto +++ b/sync/protocol/session_specifics.proto @@ -124,4 +124,12 @@ message TabNavigation { optional string search_terms = 16; // The favicon url associated with this page. optional string favicon_url = 17; + enum BlockedState { + STATE_ALLOWED = 1; + STATE_BLOCKED = 2; + } + // Whether access to the URL was allowed or blocked. + optional BlockedState blocked_state = 18 [default=STATE_ALLOWED]; + // A list of category identifiers for the URL. + repeated string content_pack_categories = 19; } diff --git a/sync/protocol/sync.proto b/sync/protocol/sync.proto index 005275b0d6..b89b9b74bf 100644 --- a/sync/protocol/sync.proto +++ b/sync/protocol/sync.proto @@ -33,6 +33,7 @@ import "get_updates_caller_info.proto"; import "history_delete_directive_specifics.proto"; import "nigori_specifics.proto"; import "managed_user_setting_specifics.proto"; +import "managed_user_specifics.proto"; import "password_specifics.proto"; import "preference_specifics.proto"; import "priority_preference_specifics.proto"; @@ -116,6 +117,7 @@ message EntitySpecifics { optional FaviconTrackingSpecifics favicon_tracking = 181534; optional FaviconImageSpecifics favicon_image = 182019; optional ManagedUserSettingSpecifics managed_user_setting = 186662; + optional ManagedUserSpecifics managed_user = 194582; } message SyncEntity { diff --git a/sync/sessions/nudge_tracker.cc b/sync/sessions/nudge_tracker.cc index 7eae5eccb4..b481a391f2 100644 --- a/sync/sessions/nudge_tracker.cc +++ b/sync/sessions/nudge_tracker.cc @@ -21,12 +21,20 @@ NudgeTracker::NudgeTracker() NudgeTracker::~NudgeTracker() { } +bool NudgeTracker::IsGetUpdatesRequired() { + if (!refresh_requested_counts_.empty()) { + return true; + } else if (!payload_list_map_.empty()) { + return true; + } else { + return false; + } +} + bool NudgeTracker::IsSyncRequired() { if (!local_nudge_counts_.empty()) { return true; - } else if (!refresh_requested_counts_.empty()) { - return true; - } else if (!payload_list_map_.empty()) { + } else if (IsGetUpdatesRequired()) { return true; } else { return false; diff --git a/sync/sessions/nudge_tracker.h b/sync/sessions/nudge_tracker.h index c0932506e8..0e557f3ef8 100644 --- a/sync/sessions/nudge_tracker.h +++ b/sync/sessions/nudge_tracker.h @@ -28,6 +28,11 @@ class SYNC_EXPORT_PRIVATE NudgeTracker { NudgeTracker(); ~NudgeTracker(); + // Returns true if one of the main reasons for performing the sync cycle is to + // fetch updates. This is true when we have pending invalidations or refresh + // requests. + bool IsGetUpdatesRequired(); + // Returns true if there is a good reason for performing a sync cycle. // This does not take into account whether or not this is a good *time* to // perform a sync cycle; that's the scheduler's job. diff --git a/sync/sessions/sync_session_context.cc b/sync/sessions/sync_session_context.cc index f931f4cb6b..41ed246744 100644 --- a/sync/sessions/sync_session_context.cc +++ b/sync/sessions/sync_session_context.cc @@ -27,7 +27,6 @@ SyncSessionContext::SyncSessionContext( const std::string& invalidator_client_id) : connection_manager_(connection_manager), directory_(directory), - workers_(workers), extensions_activity_monitor_(extensions_activity_monitor), notifications_enabled_(false), max_commit_batch_size_(kDefaultMaxCommitBatchSize), @@ -36,6 +35,9 @@ SyncSessionContext::SyncSessionContext( traffic_recorder_(traffic_recorder), keystore_encryption_enabled_(keystore_encryption_enabled), invalidator_client_id_(invalidator_client_id) { + for (size_t i = 0u; i < workers.size(); ++i) + workers_.push_back(workers[i]); + std::vector<SyncEngineEventListener*>::const_iterator it; for (it = listeners.begin(); it != listeners.end(); ++it) listeners_.AddObserver(*it); diff --git a/sync/sessions/sync_session_context.h b/sync/sessions/sync_session_context.h index 755e574fcc..a6f693a8bb 100644 --- a/sync/sessions/sync_session_context.h +++ b/sync/sessions/sync_session_context.h @@ -76,7 +76,7 @@ class SYNC_EXPORT_PRIVATE SyncSessionContext { routing_info_ = routing_info; } - const std::vector<ModelSafeWorker*> workers() const { + const std::vector<scoped_refptr<ModelSafeWorker> >& workers() const { return workers_; } @@ -151,7 +151,7 @@ class SYNC_EXPORT_PRIVATE SyncSessionContext { ModelSafeRoutingInfo routing_info_; // The set of ModelSafeWorkers. Used to execute tasks of various threads. - const std::vector<ModelSafeWorker*> workers_; + std::vector<scoped_refptr<ModelSafeWorker> > workers_; // We use this to stuff extensions activity into CommitMessages so the server // can correlate commit traffic with extension-related bookmark mutations. diff --git a/sync/sessions/test_util.cc b/sync/sessions/test_util.cc index 3b4b6c9563..03f6de2983 100644 --- a/sync/sessions/test_util.cc +++ b/sync/sessions/test_util.cc @@ -4,6 +4,8 @@ #include "sync/sessions/test_util.h" +#include "sync/engine/throttled_data_type_tracker.h" + namespace syncer { namespace sessions { namespace test_util { @@ -64,6 +66,16 @@ void SimulateThrottledImpl(sessions::SyncSession* session, session->delegate()->OnSilencedUntil(base::TimeTicks::Now() + delta); } +void SimulateTypesThrottledImpl( + sessions::SyncSession* session, + ModelTypeSet types, + const base::TimeDelta& delta) { + session->mutable_status_controller()->set_last_download_updates_result( + SERVER_RETURN_THROTTLED); + session->context()->throttled_data_type_tracker()-> + SetUnthrottleTime(types, base::TimeTicks::Now() + delta); +} + void SimulatePollIntervalUpdateImpl(sessions::SyncSession* session, const base::TimeDelta& new_poll) { SimulateSuccess(session, SYNCER_BEGIN, SYNCER_END); diff --git a/sync/sessions/test_util.h b/sync/sessions/test_util.h index 38638e17bd..6ab32b194c 100644 --- a/sync/sessions/test_util.h +++ b/sync/sessions/test_util.h @@ -27,6 +27,10 @@ void SimulateSuccess(sessions::SyncSession* session, SyncerStep begin, SyncerStep end); void SimulateThrottledImpl(sessions::SyncSession* session, const base::TimeDelta& delta); +void SimulateTypesThrottledImpl( + sessions::SyncSession* session, + ModelTypeSet types, + const base::TimeDelta& delta); void SimulatePollIntervalUpdateImpl(sessions::SyncSession* session, const base::TimeDelta& new_poll); void SimulateSessionsCommitDelayUpdateImpl(sessions::SyncSession* session, @@ -36,6 +40,10 @@ ACTION_P(SimulateThrottled, throttle) { SimulateThrottledImpl(arg0, throttle); } +ACTION_P2(SimulateTypesThrottled, types, throttle) { + SimulateTypesThrottledImpl(arg0, types, throttle); +} + ACTION_P(SimulatePollIntervalUpdate, poll) { SimulatePollIntervalUpdateImpl(arg0, poll); } diff --git a/sync/sync_proto.gypi b/sync/sync_proto.gypi index 04f5e35490..bb79b4b4cc 100644 --- a/sync/sync_proto.gypi +++ b/sync/sync_proto.gypi @@ -29,6 +29,7 @@ 'protocol/history_delete_directive_specifics.proto', 'protocol/nigori_specifics.proto', 'protocol/managed_user_setting_specifics.proto', + 'protocol/managed_user_specifics.proto', 'protocol/password_specifics.proto', 'protocol/preference_specifics.proto', 'protocol/priority_preference_specifics.proto', diff --git a/sync/syncable/entry.cc b/sync/syncable/entry.cc index 2d98c6fdff..ac6244852c 100644 --- a/sync/syncable/entry.cc +++ b/sync/syncable/entry.cc @@ -104,6 +104,10 @@ Id Entry::GetFirstChildId() const { return dir()->GetFirstChildId(basetrans_, kernel_); } +void Entry::GetChildHandles(std::vector<int64>* result) const { + dir()->GetChildHandlesById(basetrans_, Get(ID), result); +} + bool Entry::ShouldMaintainPosition() const { return kernel_->ShouldMaintainPosition(); } diff --git a/sync/syncable/entry.h b/sync/syncable/entry.h index f9d3f90cc9..26ccc0a7b8 100644 --- a/sync/syncable/entry.h +++ b/sync/syncable/entry.h @@ -109,6 +109,12 @@ class SYNC_EXPORT Entry { Id GetSuccessorId() const; Id GetFirstChildId() const; + // Returns a vector of this node's children's handles. + // Clears |result| if there are no children. If this node is of a type that + // supports user-defined ordering then the resulting vector will be in the + // proper order. + void GetChildHandles(std::vector<int64>* result) const; + inline bool ExistsOnClientBecauseNameIsNonEmpty() const { DCHECK(kernel_); return !kernel_->ref(NON_UNIQUE_NAME).empty(); diff --git a/sync/syncable/model_type.cc b/sync/syncable/model_type.cc index 140d7ab523..22ea71ec83 100644 --- a/sync/syncable/model_type.cc +++ b/sync/syncable/model_type.cc @@ -104,6 +104,9 @@ void AddDefaultFieldValue(ModelType datatype, case MANAGED_USER_SETTINGS: specifics->mutable_managed_user_setting(); break; + case MANAGED_USERS: + specifics->mutable_managed_user(); + break; default: NOTREACHED() << "No known extension for model type."; } @@ -192,6 +195,8 @@ int GetSpecificsFieldNumberFromModelType(ModelType model_type) { return sync_pb::EntitySpecifics::kFaviconTrackingFieldNumber; case MANAGED_USER_SETTINGS: return sync_pb::EntitySpecifics::kManagedUserSettingFieldNumber; + case MANAGED_USERS: + return sync_pb::EntitySpecifics::kManagedUserFieldNumber; default: NOTREACHED() << "No known extension for model type."; return 0; @@ -311,6 +316,9 @@ ModelType GetModelTypeFromSpecifics(const sync_pb::EntitySpecifics& specifics) { if (specifics.has_managed_user_setting()) return MANAGED_USER_SETTINGS; + if (specifics.has_managed_user()) + return MANAGED_USERS; + return UNSPECIFIED; } @@ -362,6 +370,8 @@ ModelTypeSet EncryptableUserTypes() { encryptable_user_types.RemoveAll(PriorityUserTypes()); // Managed user settings are not encrypted since they are set server-side. encryptable_user_types.Remove(MANAGED_USER_SETTINGS); + // Managed users are not encrypted since they are managed server-side. + encryptable_user_types.Remove(MANAGED_USERS); // Proxy types have no sync representation and are therefore not encrypted. // Note however that proxy types map to one or more protocol types, which // may or may not be encrypted themselves. @@ -452,6 +462,8 @@ const char* ModelTypeToString(ModelType model_type) { return "Favicon Tracking"; case MANAGED_USER_SETTINGS: return "Managed User Settings"; + case MANAGED_USERS: + return "Managed Users"; case PROXY_TABS: return "Tabs"; default: @@ -521,6 +533,8 @@ int ModelTypeToHistogramInt(ModelType model_type) { return 25; case MANAGED_USER_SETTINGS: return 26; + case MANAGED_USERS: + return 27; // Silence a compiler warning. case MODEL_TYPE_COUNT: return 0; @@ -604,6 +618,8 @@ ModelType ModelTypeFromString(const std::string& model_type_string) { return FAVICON_TRACKING; else if (model_type_string == "Managed User Settings") return MANAGED_USER_SETTINGS; + else if (model_type_string == "Managed Users") + return MANAGED_USERS; else if (model_type_string == "Tabs") return PROXY_TABS; else @@ -694,6 +710,8 @@ std::string ModelTypeToRootTag(ModelType type) { return "google_chrome_favicon_tracking"; case MANAGED_USER_SETTINGS: return "google_chrome_managed_user_settings"; + case MANAGED_USERS: + return "google_chrome_managed_users"; case PROXY_TABS: return std::string(); default: @@ -732,6 +750,7 @@ const char kDictionaryNotificationType[] = "DICTIONARY"; const char kFaviconImageNotificationType[] = "FAVICON_IMAGE"; const char kFaviconTrackingNotificationType[] = "FAVICON_TRACKING"; const char kManagedUserSettingNotificationType[] = "MANAGED_USER_SETTING"; +const char kManagedUserNotificationType[] = "MANAGED_USER"; } // namespace bool RealModelTypeToNotificationType(ModelType model_type, @@ -809,6 +828,9 @@ bool RealModelTypeToNotificationType(ModelType model_type, case MANAGED_USER_SETTINGS: *notification_type = kManagedUserSettingNotificationType; return true; + case MANAGED_USERS: + *notification_type = kManagedUserNotificationType; + return true; default: break; } @@ -890,6 +912,9 @@ bool NotificationTypeToRealModelType(const std::string& notification_type, } else if (notification_type == kManagedUserSettingNotificationType) { *model_type = MANAGED_USER_SETTINGS; return true; + } else if (notification_type == kManagedUserNotificationType) { + *model_type = MANAGED_USERS; + return true; } *model_type = UNSPECIFIED; return false; diff --git a/sync/syncable/mutable_entry.cc b/sync/syncable/mutable_entry.cc index 7c91135ffe..9698ab7ecc 100644 --- a/sync/syncable/mutable_entry.cc +++ b/sync/syncable/mutable_entry.cc @@ -157,6 +157,10 @@ bool MutableEntry::PutIsDel(bool is_del) { bool MutableEntry::Put(Int64Field field, const int64& value) { DCHECK(kernel_); + + // We shouldn't set TRANSACTION_VERSION here. See UpdateTransactionVersion. + DCHECK_NE(TRANSACTION_VERSION, field); + write_transaction_->SaveOriginal(kernel_); if (kernel_->ref(field) != value) { ScopedKernelLock lock(dir()); @@ -413,6 +417,12 @@ bool MutableEntry::Put(BitTemp field, bool value) { return true; } +void MutableEntry::UpdateTransactionVersion(int64 value) { + ScopedKernelLock lock(dir()); + kernel_->put(TRANSACTION_VERSION, value); + kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); +} + // This function sets only the flags needed to get this entry to sync. bool MarkForSyncing(MutableEntry* e) { DCHECK_NE(static_cast<MutableEntry*>(NULL), e); diff --git a/sync/syncable/mutable_entry.h b/sync/syncable/mutable_entry.h index 134e4a9b8f..60c2715e3c 100644 --- a/sync/syncable/mutable_entry.h +++ b/sync/syncable/mutable_entry.h @@ -84,6 +84,12 @@ class SYNC_EXPORT_PRIVATE MutableEntry : public Entry { bool Put(BitTemp field, bool value); + // This is similar to what one would expect from Put(TRANSACTION_VERSION), + // except that it doesn't bother to invoke 'SaveOriginals'. Calling that + // function is at best unnecessary, since the transaction will have already + // used its list of mutations by the time this function is called. + void UpdateTransactionVersion(int64 version); + protected: syncable::MetahandleSet* GetDirtyIndexHelper(); diff --git a/sync/syncable/nigori_util.cc b/sync/syncable/nigori_util.cc index 5c88316c99..3e456dd1f5 100644 --- a/sync/syncable/nigori_util.cc +++ b/sync/syncable/nigori_util.cc @@ -242,7 +242,7 @@ void UpdateNigoriFromEncryptedTypes(ModelTypeSet encrypted_types, bool encrypt_everything, sync_pb::NigoriSpecifics* nigori) { nigori->set_encrypt_everything(encrypt_everything); - COMPILE_ASSERT(27 == MODEL_TYPE_COUNT, UpdateEncryptedTypes); + COMPILE_ASSERT(28 == MODEL_TYPE_COUNT, UpdateEncryptedTypes); nigori->set_encrypt_bookmarks( encrypted_types.Has(BOOKMARKS)); nigori->set_encrypt_preferences( @@ -276,7 +276,7 @@ ModelTypeSet GetEncryptedTypesFromNigori( return ModelTypeSet::All(); ModelTypeSet encrypted_types; - COMPILE_ASSERT(27 == MODEL_TYPE_COUNT, UpdateEncryptedTypes); + COMPILE_ASSERT(28 == MODEL_TYPE_COUNT, UpdateEncryptedTypes); if (nigori.encrypt_bookmarks()) encrypted_types.Put(BOOKMARKS); if (nigori.encrypt_preferences()) diff --git a/sync/syncable/syncable_write_transaction.cc b/sync/syncable/syncable_write_transaction.cc index 5b884e7f35..04113a7411 100644 --- a/sync/syncable/syncable_write_transaction.cc +++ b/sync/syncable/syncable_write_transaction.cc @@ -40,9 +40,7 @@ void WriteTransaction::SaveOriginal(const EntryKernel* entry) { const int64 handle = entry->ref(META_HANDLE); EntryKernelMutationMap::iterator it = mutations_.lower_bound(handle); if (it == mutations_.end() || it->first != handle) { - EntryKernelMutation mutation; - mutation.original = *entry; - ignore_result(mutations_.insert(it, std::make_pair(handle, mutation))); + mutations_[handle].original = *entry; } } @@ -136,7 +134,7 @@ void WriteTransaction::UpdateTransactionVersion( directory_->IncrementTransactionVersion(type); type_seen.Put(type); } - entry.Put(TRANSACTION_VERSION, directory_->GetTransactionVersion(type)); + entry.UpdateTransactionVersion(directory_->GetTransactionVersion(type)); } } diff --git a/sync/test/engine/fake_model_worker.cc b/sync/test/engine/fake_model_worker.cc index dba6fddf83..80a58d4e09 100644 --- a/sync/test/engine/fake_model_worker.cc +++ b/sync/test/engine/fake_model_worker.cc @@ -6,7 +6,9 @@ namespace syncer { -FakeModelWorker::FakeModelWorker(ModelSafeGroup group) : group_(group) {} +FakeModelWorker::FakeModelWorker(ModelSafeGroup group) + : ModelSafeWorker(NULL), + group_(group) {} FakeModelWorker::~FakeModelWorker() { // We may need to relax this is FakeModelWorker is used in a @@ -16,7 +18,11 @@ FakeModelWorker::~FakeModelWorker() { DCHECK(CalledOnValidThread()); } -SyncerError FakeModelWorker::DoWorkAndWaitUntilDone( +void FakeModelWorker::RegisterForLoopDestruction() { + NOTREACHED(); +} + +SyncerError FakeModelWorker::DoWorkAndWaitUntilDoneImpl( const WorkCallback& work) { DCHECK(CalledOnValidThread()); // Simply do the work on the current thread. diff --git a/sync/test/engine/fake_model_worker.h b/sync/test/engine/fake_model_worker.h index 7b7bd4b1a0..9c621be7ba 100644 --- a/sync/test/engine/fake_model_worker.h +++ b/sync/test/engine/fake_model_worker.h @@ -22,10 +22,13 @@ class FakeModelWorker : public ModelSafeWorker, public base::NonThreadSafe { explicit FakeModelWorker(ModelSafeGroup group); // ModelSafeWorker implementation. - virtual SyncerError DoWorkAndWaitUntilDone( - const WorkCallback& work) OVERRIDE; + virtual void RegisterForLoopDestruction() OVERRIDE; virtual ModelSafeGroup GetModelSafeGroup() OVERRIDE; + protected: + virtual SyncerError DoWorkAndWaitUntilDoneImpl( + const WorkCallback& work) OVERRIDE; + private: virtual ~FakeModelWorker(); diff --git a/sync/tools/sync_client.cc b/sync/tools/sync_client.cc index 7623db1e87..9d3c51d1c4 100644 --- a/sync/tools/sync_client.cc +++ b/sync/tools/sync_client.cc @@ -293,7 +293,7 @@ int SyncClientMain(int argc, char* argv[]) { routing_info[it.Get()] = GROUP_PASSIVE; } scoped_refptr<PassiveModelWorker> passive_model_safe_worker = - new PassiveModelWorker(&sync_loop); + new PassiveModelWorker(&sync_loop, NULL); std::vector<ModelSafeWorker*> workers; workers.push_back(passive_model_safe_worker.get()); diff --git a/sync/tools/testserver/chromiumsync.py b/sync/tools/testserver/chromiumsync.py index fbd3c5b6c2..af43415162 100644 --- a/sync/tools/testserver/chromiumsync.py +++ b/sync/tools/testserver/chromiumsync.py @@ -32,6 +32,7 @@ import favicon_image_specifics_pb2 import favicon_tracking_specifics_pb2 import history_delete_directive_specifics_pb2 import managed_user_setting_specifics_pb2 +import managed_user_specifics_pb2 import nigori_specifics_pb2 import password_specifics_pb2 import preference_specifics_pb2 @@ -62,6 +63,7 @@ ALL_TYPES = ( EXTENSIONS, HISTORY_DELETE_DIRECTIVE, MANAGED_USER_SETTING, + MANAGED_USER, NIGORI, PASSWORD, PREFERENCE, @@ -73,7 +75,7 @@ ALL_TYPES = ( TYPED_URL, EXTENSION_SETTINGS, FAVICON_IMAGES, - FAVICON_TRACKING) = range(25) + FAVICON_TRACKING) = range(26) # An enumeration on the frequency at which the server should send errors # to the client. This would be specified by the url that triggers the error. @@ -105,6 +107,7 @@ SYNC_TYPE_TO_DESCRIPTOR = { FAVICON_TRACKING: SYNC_TYPE_FIELDS['favicon_tracking'], HISTORY_DELETE_DIRECTIVE: SYNC_TYPE_FIELDS['history_delete_directive'], MANAGED_USER_SETTING: SYNC_TYPE_FIELDS['managed_user_setting'], + MANAGED_USER: SYNC_TYPE_FIELDS['managed_user'], NIGORI: SYNC_TYPE_FIELDS['nigori'], PASSWORD: SYNC_TYPE_FIELDS['password'], PREFERENCE: SYNC_TYPE_FIELDS['preference'], @@ -497,6 +500,9 @@ class SyncDataModel(object): PermanentItem('google_chrome_managed_user_settings', name='Managed User Settings', parent_tag=ROOT_ID, sync_type=MANAGED_USER_SETTING), + PermanentItem('google_chrome_managed_users', + name='Managed Users', + parent_tag=ROOT_ID, sync_type=MANAGED_USER), PermanentItem('google_chrome_nigori', name='Nigori', parent_tag=ROOT_ID, sync_type=NIGORI), PermanentItem('google_chrome_passwords', name='Passwords', diff --git a/sync/util/data_type_histogram.h b/sync/util/data_type_histogram.h index c2d8542e74..c1498398c6 100644 --- a/sync/util/data_type_histogram.h +++ b/sync/util/data_type_histogram.h @@ -108,7 +108,10 @@ case ::syncer::MANAGED_USER_SETTINGS: \ PER_DATA_TYPE_MACRO("ManagedUserSetting"); \ break; \ - case ::syncer::PROXY_TABS :\ + case ::syncer::MANAGED_USERS: \ + PER_DATA_TYPE_MACRO("ManagedUser"); \ + break; \ + case ::syncer::PROXY_TABS: \ PER_DATA_TYPE_MACRO("Tabs"); \ break; \ default: \ |