summaryrefslogtreecommitdiff
path: root/sync
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2013-05-29 14:40:03 +0100
committerTorne (Richard Coles) <torne@google.com>2013-05-29 14:40:03 +0100
commit90dce4d38c5ff5333bea97d859d4e484e27edf0c (patch)
tree9c51c7dd97d24b15befa97a3482c51851e5383a1 /sync
parent1515035f5917d10d363b0888a3615d581ad8b83f (diff)
downloadchromium_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')
-rw-r--r--sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java93
-rw-r--r--sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java9
-rw-r--r--sync/engine/conflict_resolver.cc45
-rw-r--r--sync/engine/sync_scheduler_impl.cc61
-rw-r--r--sync/engine/sync_scheduler_impl.h7
-rw-r--r--sync/engine/sync_scheduler_unittest.cc87
-rw-r--r--sync/internal_api/base_node.cc9
-rw-r--r--sync/internal_api/internal_components_factory_impl.cc2
-rw-r--r--sync/internal_api/public/base/model_type.h4
-rw-r--r--sync/internal_api/public/base_node.h9
-rw-r--r--sync/internal_api/public/engine/model_safe_worker.cc52
-rw-r--r--sync/internal_api/public/engine/model_safe_worker.h63
-rw-r--r--sync/internal_api/public/engine/passive_model_worker.cc13
-rw-r--r--sync/internal_api/public/engine/passive_model_worker.h10
-rw-r--r--sync/internal_api/public/internal_components_factory.h2
-rw-r--r--sync/internal_api/public/internal_components_factory_impl.h2
-rw-r--r--sync/internal_api/public/test/test_internal_components_factory.h2
-rw-r--r--sync/internal_api/public/write_node.h4
-rw-r--r--sync/internal_api/test/test_internal_components_factory.cc2
-rw-r--r--sync/internal_api/write_node.cc7
-rw-r--r--sync/protocol/managed_user_specifics.proto26
-rw-r--r--sync/protocol/proto_enum_conversions.cc12
-rw-r--r--sync/protocol/proto_enum_conversions.h11
-rw-r--r--sync/protocol/proto_value_conversions.cc12
-rw-r--r--sync/protocol/proto_value_conversions.h4
-rw-r--r--sync/protocol/proto_value_conversions_unittest.cc7
-rw-r--r--sync/protocol/session_specifics.proto8
-rw-r--r--sync/protocol/sync.proto2
-rw-r--r--sync/sessions/nudge_tracker.cc14
-rw-r--r--sync/sessions/nudge_tracker.h5
-rw-r--r--sync/sessions/sync_session_context.cc4
-rw-r--r--sync/sessions/sync_session_context.h4
-rw-r--r--sync/sessions/test_util.cc12
-rw-r--r--sync/sessions/test_util.h8
-rw-r--r--sync/sync_proto.gypi1
-rw-r--r--sync/syncable/entry.cc4
-rw-r--r--sync/syncable/entry.h6
-rw-r--r--sync/syncable/model_type.cc25
-rw-r--r--sync/syncable/mutable_entry.cc10
-rw-r--r--sync/syncable/mutable_entry.h6
-rw-r--r--sync/syncable/nigori_util.cc4
-rw-r--r--sync/syncable/syncable_write_transaction.cc6
-rw-r--r--sync/test/engine/fake_model_worker.cc10
-rw-r--r--sync/test/engine/fake_model_worker.h7
-rw-r--r--sync/tools/sync_client.cc2
-rw-r--r--sync/tools/testserver/chromiumsync.py8
-rw-r--r--sync/util/data_type_histogram.h5
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: \